From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org,
torvalds@linux-foundation.org, stable@vger.kernel.org
Cc: lwn@lwn.net, jslaby@suse.cz,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: Re: Linux 6.6.145
Date: Fri, 24 Jul 2026 17:05:47 +0200 [thread overview]
Message-ID: <2026072447-idiom-slighted-3071@gregkh> (raw)
In-Reply-To: <2026072446-unsolved-treble-c609@gregkh>
diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst
index 6adb7988e0eb..5247eabb9206 100644
--- a/Documentation/admin-guide/pm/cpufreq.rst
+++ b/Documentation/admin-guide/pm/cpufreq.rst
@@ -499,7 +499,7 @@ This governor exposes the following tunables:
of those tasks above 0 and set this attribute to 1.
``sampling_down_factor``
- Temporary multiplier, between 1 (default) and 100 inclusive, to apply to
+ Temporary multiplier, between 1 (default) and 100000 inclusive, to apply to
the ``sampling_rate`` value if the CPU load goes above ``up_threshold``.
This causes the next execution of the governor's worker routine (after
@@ -569,8 +569,8 @@ This governor exposes the following tunables:
100 (5 by default).
This is how much the frequency is allowed to change in one go. Setting
- it to 0 will cause the default frequency step (5 percent) to be used
- and setting it to 100 effectively causes the governor to periodically
+ it to 0 disables frequency changes by the governor entirely and setting
+ it to 100 effectively causes the governor to periodically
switch the frequency between the ``scaling_min_freq`` and
``scaling_max_freq`` policy limits.
diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst
index f80f243e227e..4630d3d74913 100644
--- a/Documentation/crypto/userspace-if.rst
+++ b/Documentation/crypto/userspace-if.rst
@@ -328,33 +328,10 @@ CRYPTO_USER_API_RNG_CAVP option:
Zero-Copy Interface
-------------------
-In addition to the send/write/read/recv system call family, the AF_ALG
-interface can be accessed with the zero-copy interface of
-splice/vmsplice. As the name indicates, the kernel tries to avoid a copy
-operation into kernel space.
-
-The zero-copy operation requires data to be aligned at the page
-boundary. Non-aligned data can be used as well, but may require more
-operations of the kernel which would defeat the speed gains obtained
-from the zero-copy interface.
-
-The system-inherent limit for the size of one zero-copy operation is 16
-pages. If more data is to be sent to AF_ALG, user space must slice the
-input into segments with a maximum size of 16 pages.
-
-Zero-copy can be used with the following code example (a complete
-working example is provided with libkcapi):
-
-::
-
- int pipes[2];
-
- pipe(pipes);
- /* input data in iov */
- vmsplice(pipes[1], iov, iovlen, SPLICE_F_GIFT);
- /* opfd is the file descriptor returned from accept() system call */
- splice(pipes[0], NULL, opfd, NULL, ret, 0);
- read(opfd, out, outlen);
+AF_ALG used to have zero-copy support, but it was removed due to it being a
+frequent source of vulnerabilities. For backwards compatibility the splice()
+and sendfile() system calls are still supported, but the kernel will make an
+internal copy of the data before passing it to the crypto code.
Setsockopt Interface
diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
index 541325f900a1..01f2afa023f0 100644
--- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
+++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
@@ -63,6 +63,16 @@ properties:
CMA pool to use for buffers allocation instead of the default
CMA pool.
+ # FIXME: This should be made required eventually once every SoC will
+ # have the MBUS declared.
+ interconnects:
+ maxItems: 1
+
+ # FIXME: This should be made required eventually once every SoC will
+ # have the MBUS declared.
+ interconnect-names:
+ const: dma-mem
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/net/renesas,ether.yaml b/Documentation/devicetree/bindings/net/renesas,ether.yaml
index 06b38c9bc6ec..f1b0b03df838 100644
--- a/Documentation/devicetree/bindings/net/renesas,ether.yaml
+++ b/Documentation/devicetree/bindings/net/renesas,ether.yaml
@@ -119,8 +119,7 @@ examples:
#size-cells = <0>;
phy1: ethernet-phy@1 {
- compatible = "ethernet-phy-id0022.1537",
- "ethernet-phy-ieee802.3-c22";
+ compatible = "ethernet-phy-id0022.1537";
reg = <1>;
interrupt-parent = <&irqc0>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml
index f3deda9f7127..3c19147af91e 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux-aon.yaml
@@ -55,6 +55,10 @@ patternProperties:
drive_soc_gpio27_pee6, drive_ao_retention_n_pee2,
drive_vcomp_alert_pee1, drive_hdmi_cec_pgg0 ]
+required:
+ - compatible
+ - reg
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml
index 17b865ecfcda..4f79dbc1bde6 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra234-pinmux.yaml
@@ -112,6 +112,10 @@ patternProperties:
drive_sdmmc1_dat2_pj4, drive_sdmmc1_dat1_pj3,
drive_sdmmc1_dat0_pj2 ]
+required:
+ - compatible
+ - reg
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml
index d6b3b5a5c0b3..5e4b1e741795 100644
--- a/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/mt6359-regulator.yaml
@@ -18,84 +18,41 @@ patternProperties:
"^buck_v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^v(s1|gpu11|modem|pu|core|s2|pa|proc2|proc1|core_sshub)$"
-
unevaluatedProperties: false
"^ldo_v(ibr|rf12|usb|camio|efuse|xo22)$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^v(ibr|rf12|usb|camio|efuse|xo22)$"
-
unevaluatedProperties: false
"^ldo_v(rfck|emc|a12|a09|ufs|bbck)$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^v(rfck|emc|a12|a09|ufs|bbck)$"
-
unevaluatedProperties: false
"^ldo_vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^vcn(18|13|33_1_bt|13_1_wifi|33_2_bt|33_2_wifi)$"
-
unevaluatedProperties: false
"^ldo_vsram_(proc2|others|md|proc1|others_sshub)$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^vsram_(proc2|others|md|proc1|others_sshub)$"
-
unevaluatedProperties: false
"^ldo_v(fe|bif|io)28$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^v(fe|bif|io)28$"
-
unevaluatedProperties: false
"^ldo_v(aud|io|aux|rf|m)18$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^v(aud|io|aux|rf|m)18$"
-
unevaluatedProperties: false
"^ldo_vsim[12]$":
type: object
$ref: regulator.yaml#
-
- properties:
- regulator-name:
- pattern: "^vsim[12]$"
-
- required:
- - regulator-name
-
unevaluatedProperties: false
additionalProperties: false
diff --git a/Documentation/driver-api/nvdimm/btt.rst b/Documentation/driver-api/nvdimm/btt.rst
index 107395c042ae..cb1afe9dc3cb 100644
--- a/Documentation/driver-api/nvdimm/btt.rst
+++ b/Documentation/driver-api/nvdimm/btt.rst
@@ -161,9 +161,8 @@ process::
nlanes = min(nfree, num_cpus)
A lane number is obtained at the start of any IO, and is used for indexing into
-all the on-disk and in-memory data structures for the duration of the IO. If
-there are more CPUs than the max number of available lanes, than lanes are
-protected by spinlocks.
+all the on-disk and in-memory data structures for the duration of the IO. Lanes
+are protected by mutexes.
d. In-memory data structure: Read Tracking Table (RTT)
diff --git a/Documentation/driver-api/pci/pci.rst b/Documentation/driver-api/pci/pci.rst
index 4843cfad4f60..a0f2903dce4e 100644
--- a/Documentation/driver-api/pci/pci.rst
+++ b/Documentation/driver-api/pci/pci.rst
@@ -31,6 +31,9 @@ PCI Support Library
.. kernel-doc:: drivers/pci/slot.c
:export:
+.. kernel-doc:: drivers/pci/rebar.c
+ :export:
+
.. kernel-doc:: drivers/pci/rom.c
:export:
diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst
index 2b59cff8be17..0a186efa0cec 100644
--- a/Documentation/filesystems/proc.rst
+++ b/Documentation/filesystems/proc.rst
@@ -23,13 +23,13 @@ fixes/update part 1.1 Stefani Seibold <stefani@seibold.net> June 9 2009
1 Collecting System Information
1.1 Process-Specific Subdirectories
1.2 Kernel data
- 1.3 IDE devices in /proc/ide
- 1.4 Networking info in /proc/net
- 1.5 SCSI info
- 1.6 Parallel port info in /proc/parport
- 1.7 TTY info in /proc/tty
- 1.8 Miscellaneous kernel statistics in /proc/stat
- 1.9 Ext4 file system parameters
+ 1.3 Networking info in /proc/net
+ 1.4 SCSI info
+ 1.5 Parallel port info in /proc/parport
+ 1.6 TTY info in /proc/tty
+ 1.7 Miscellaneous kernel statistics in /proc/stat
+ 1.8 Ext4 file system parameters
+ 1.9 /proc/consoles - Shows registered system consoles
2 Modifying System Parameters
diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml
index 6d89e30f5fd5..0888c39ec2bc 100644
--- a/Documentation/netlink/specs/handshake.yaml
+++ b/Documentation/netlink/specs/handshake.yaml
@@ -113,6 +113,7 @@ operations:
name: done
doc: Handler reports handshake completion
attribute-set: done
+ flags: [admin-perm]
do:
request:
attributes:
diff --git a/Makefile b/Makefile
index db02323ee4ca..935db0304062 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 6
-SUBLEVEL = 144
+SUBLEVEL = 145
EXTRAVERSION =
NAME = Pinguïn Aangedreven
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 5808a66e2a81..22187531abb9 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -11,6 +11,7 @@
*/
#include <linux/sched.h>
+#include <linux/security.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/pci.h>
@@ -36,12 +37,16 @@ static int hose_mmap_page_range(struct pci_controller *hose,
static int __pci_mmap_fits(struct pci_dev *pdev, int num,
struct vm_area_struct *vma, int sparse)
{
+ resource_size_t len = pci_resource_len(pdev, num);
unsigned long nr, start, size;
int shift = sparse ? 5 : 0;
+ if (!len)
+ return 0;
+
nr = vma_pages(vma);
start = vma->vm_pgoff;
- size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
+ size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1;
if (start < size && size - start >= nr)
return 1;
@@ -71,7 +76,11 @@ static int pci_mmap_resource(struct kobject *kobj,
struct resource *res = attr->private;
enum pci_mmap_state mmap_type;
struct pci_bus_region bar;
- int i;
+ int i, ret;
+
+ ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+ if (ret)
+ return ret;
for (i = 0; i < PCI_STD_NUM_BARS; i++)
if (res == &pdev->resource[i])
diff --git a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts
index 1115c812f6c8..b22e5c645331 100644
--- a/arch/arm/boot/dts/ti/omap/am335x-sl50.dts
+++ b/arch/arm/boot/dts/ti/omap/am335x-sl50.dts
@@ -557,8 +557,8 @@ cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
dai-format = "dsp_b";
- bitclock-master = <&codec_port>;
- frame-master = <&codec_port>;
+ bitclock-master = <&codec_endpoint>;
+ frame-master = <&codec_endpoint>;
bitclock-inversion;
clocks = <&audio_mclk>;
};
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index a83d29fed175..f4b8d8f7dbef 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -170,7 +170,6 @@ CONFIG_ROOT_NFS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_CRYPTO_DEV_SUN4I_SS=y
-CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG=y
CONFIG_CRYPTO_DEV_SUN8I_CE=y
CONFIG_CRYPTO_DEV_SUN8I_SS=y
CONFIG_DMA_CMA=y
diff --git a/arch/arm/mach-imx/cpu-imx31.c b/arch/arm/mach-imx/cpu-imx31.c
index 35c544924e50..e81ef9e36a1f 100644
--- a/arch/arm/mach-imx/cpu-imx31.c
+++ b/arch/arm/mach-imx/cpu-imx31.c
@@ -36,6 +36,7 @@ static int mx31_read_cpu_rev(void)
void __iomem *iim_base;
struct device_node *np;
u32 i, srev;
+ int rev = IMX_CHIP_REVISION_UNKNOWN;
np = of_find_compatible_node(NULL, NULL, "fsl,imx31-iim");
iim_base = of_iomap(np, 0);
@@ -48,13 +49,17 @@ static int mx31_read_cpu_rev(void)
for (i = 0; i < ARRAY_SIZE(mx31_cpu_type); i++)
if (srev == mx31_cpu_type[i].srev) {
+ rev = mx31_cpu_type[i].rev;
imx_print_silicon_rev(mx31_cpu_type[i].name,
mx31_cpu_type[i].rev);
- return mx31_cpu_type[i].rev;
+ goto out;
}
imx_print_silicon_rev("i.MX31", IMX_CHIP_REVISION_UNKNOWN);
- return IMX_CHIP_REVISION_UNKNOWN;
+
+out:
+ iounmap(iim_base);
+ return rev;
}
int mx31_revision(void)
diff --git a/arch/arm/mach-imx/mm-imx3.c b/arch/arm/mach-imx/mm-imx3.c
index 0788c5cc7f9e..9b0b014d7fe2 100644
--- a/arch/arm/mach-imx/mm-imx3.c
+++ b/arch/arm/mach-imx/mm-imx3.c
@@ -106,6 +106,7 @@ void __init imx31_init_early(void)
arm_pm_idle = imx31_idle;
np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm");
mx3_ccm_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!mx3_ccm_base);
}
#endif /* ifdef CONFIG_SOC_IMX31 */
@@ -143,6 +144,7 @@ void __init imx35_init_early(void)
arch_ioremap_caller = imx3_ioremap_caller;
np = of_find_compatible_node(NULL, NULL, "fsl,imx35-ccm");
mx3_ccm_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!mx3_ccm_base);
}
#endif /* ifdef CONFIG_SOC_IMX35 */
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts
index 5430b62a3b28..8dc8d2e12a7a 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mpxl.dts
@@ -473,6 +473,9 @@ tlv320aic3x04: audio-codec@18 {
reset-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>;
iov-supply = <®_vcc_1v8>;
ldoin-supply = <®_vcc_3v3>;
+ assigned-clocks = <&clk IMX8MP_CLK_SAI3>;
+ assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>;
+ assigned-clock-rates = <12288000>;
};
se97_1c: temperature-sensor@1c {
diff --git a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi
index 49d105eb4769..b7986605119e 100644
--- a/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8x-colibri.dtsi
@@ -383,12 +383,12 @@ pinctrl_adc0: adc0grp {
*/
pinctrl_atmel_adap: atmeladaptergrp {
fsl,pins = <IMX8QXP_UART1_RX_LSIO_GPIO0_IO22 0x21>, /* SODIMM 30 */
- <IMX8QXP_UART1_TX_LSIO_GPIO0_IO21 0x4000021>; /* SODIMM 28 */
+ <IMX8QXP_UART1_TX_LSIO_GPIO0_IO21 0x40000021>; /* SODIMM 28 */
};
/* Atmel MXT touchsceen + boards with built-in Capacitive Touch Connector */
pinctrl_atmel_conn: atmelconnectorgrp {
- fsl,pins = <IMX8QXP_QSPI0B_DATA2_LSIO_GPIO3_IO20 0x4000021>, /* SODIMM 107 */
+ fsl,pins = <IMX8QXP_QSPI0B_DATA2_LSIO_GPIO3_IO20 0x40000021>, /* SODIMM 107 */
<IMX8QXP_QSPI0B_SS1_B_LSIO_GPIO3_IO24 0x21>; /* SODIMM 106 */
};
diff --git a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi
index 54e3ec168fa9..0788db329b78 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi
@@ -494,6 +494,7 @@ flash@0 {
&pcie {
pinctrl-names = "default";
pinctrl-0 = <&pcie_pins>;
+ memory-region = <&wifi_restricted_dma_region>;
pcie0: pcie@0,0 {
device_type = "pci";
@@ -508,7 +509,6 @@ pcie0: pcie@0,0 {
wifi: wifi@0,0 {
reg = <0x10000 0 0 0 0x100000>,
<0x10000 0 0x100000 0 0x100000>;
- memory-region = <&wifi_restricted_dma_region>;
};
};
};
diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index bcc36055ef97..d1d7f6a50e11 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -1447,7 +1447,7 @@ ethernet@6800000 {
<&bpmp TEGRA234_CLK_MGBE0_RX_PCS_M>,
<&bpmp TEGRA234_CLK_MGBE0_RX_PCS>,
<&bpmp TEGRA234_CLK_MGBE0_TX_PCS>;
- clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m",
+ clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m",
"rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m",
"rx-pcs", "tx-pcs";
resets = <&bpmp TEGRA234_RESET_MGBE0_MAC>,
@@ -1481,7 +1481,7 @@ ethernet@6900000 {
<&bpmp TEGRA234_CLK_MGBE1_RX_PCS_M>,
<&bpmp TEGRA234_CLK_MGBE1_RX_PCS>,
<&bpmp TEGRA234_CLK_MGBE1_TX_PCS>;
- clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m",
+ clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m",
"rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m",
"rx-pcs", "tx-pcs";
resets = <&bpmp TEGRA234_RESET_MGBE1_MAC>,
@@ -1515,7 +1515,7 @@ ethernet@6a00000 {
<&bpmp TEGRA234_CLK_MGBE2_RX_PCS_M>,
<&bpmp TEGRA234_CLK_MGBE2_RX_PCS>,
<&bpmp TEGRA234_CLK_MGBE2_TX_PCS>;
- clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m",
+ clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m",
"rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m",
"rx-pcs", "tx-pcs";
resets = <&bpmp TEGRA234_RESET_MGBE2_MAC>,
@@ -1549,7 +1549,7 @@ ethernet@6b00000 {
<&bpmp TEGRA234_CLK_MGBE3_RX_PCS_M>,
<&bpmp TEGRA234_CLK_MGBE3_RX_PCS>,
<&bpmp TEGRA234_CLK_MGBE3_TX_PCS>;
- clock-names = "mgbe", "mac", "mac-divider", "ptp-ref", "rx-input-m",
+ clock-names = "mgbe", "mac", "mac-divider", "ptp_ref", "rx-input-m",
"rx-input", "tx", "eee-pcs", "rx-pcs-input", "rx-pcs-m",
"rx-pcs", "tx-pcs";
resets = <&bpmp TEGRA234_RESET_MGBE3_MAC>,
diff --git a/arch/arm64/boot/dts/qcom/sdm630.dtsi b/arch/arm64/boot/dts/qcom/sdm630.dtsi
index 74b36cb8bffa..36ae911fcfc2 100644
--- a/arch/arm64/boot/dts/qcom/sdm630.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm630.dtsi
@@ -491,9 +491,12 @@ venus_region: venus@9f800000 {
no-map;
};
- adsp_mem: adsp-region@f6000000 {
- reg = <0x0 0xf6000000 0x0 0x800000>;
- no-map;
+ adsp_mem: adsp-region {
+ compatible = "shared-dma-pool";
+ alloc-ranges = <0x0 0x80000000 0x0 0x80000000>;
+ alignment = <0x0 0x400000>;
+ size = <0x0 0x800000>;
+ reusable;
};
qseecom_mem: qseecom-region@f6800000 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso
index 51f1a4883ab8..df1907560c52 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso
+++ b/arch/arm64/boot/dts/qcom/sdm845-db845c-navigation-mezzanine.dtso
@@ -16,7 +16,12 @@ &camss {
status = "okay";
ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
port@0 {
+ reg = <0>;
+
csiphy0_ep: endpoint {
data-lanes = <0 1 2 3>;
remote-endpoint = <&ov8856_ep>;
diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi
index c1c62c6d34bd..0dad7b0866ea 100644
--- a/arch/arm64/boot/dts/qcom/sm8450.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi
@@ -4223,8 +4223,12 @@ ufs_mem_phy_lanes: phy@1d87400 {
ice: crypto@1d88000 {
compatible = "qcom,sm8450-inline-crypto-engine",
"qcom,inline-crypto-engine";
- reg = <0 0x01d88000 0 0x8000>;
- clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
+ reg = <0 0x01d88000 0 0x18000>;
+ clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
+ <&gcc GCC_UFS_PHY_AHB_CLK>;
+ clock-names = "core",
+ "iface";
+ power-domains = <&gcc UFS_PHY_GDSC>;
};
cryptobam: dma-controller@1dc4000 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts
index 68a72ac24cd4..5d0bcabc5196 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts
@@ -244,7 +244,7 @@ rk809: pmic@20 {
compatible = "rockchip,rk809";
reg = <0x20>;
interrupt-parent = <&gpio0>;
- interrupts = <RK_PA7 IRQ_TYPE_LEVEL_LOW>;
+ interrupts = <RK_PA3 IRQ_TYPE_LEVEL_LOW>;
clock-output-names = "rk808-clkout1", "rk808-clkout2";
assigned-clocks = <&cru I2S1_MCLKOUT_TX>;
assigned-clock-parents = <&cru CLK_I2S1_8CH_TX>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
index b6ad8328c7eb..b9f4b6e63d47 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-nanopi-r5s.dts
@@ -77,7 +77,7 @@ &mdio0 {
rgmii_phy0: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
- pinctrl-0 = <ð_phy0_reset_pin>;
+ pinctrl-0 = <&gmac0_rstn_gpio0_c5_pin>;
pinctrl-names = "default";
};
};
@@ -111,8 +111,8 @@ &pcie3x2 {
&pinctrl {
gmac0 {
- eth_phy0_reset_pin: eth-phy0-reset-pin {
- rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_up>;
+ gmac0_rstn_gpio0_c5_pin: gmac0-rstn-gpio0-c5-pin {
+ rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index b7afaa026842..4c7467b382b6 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -16,6 +16,58 @@
#include <asm/sysreg.h>
#include <linux/irqchip/arm-gic-v3.h>
+.macro init_el2_hcr val
+ mov_q x0, \val
+
+ /*
+ * Compliant CPUs advertise their VHE-onlyness with
+ * ID_AA64MMFR4_EL1.E2H0 < 0. On such CPUs HCR_EL2.E2H is RES1, but it
+ * can reset into an UNKNOWN state and might not read as 1 until it has
+ * been initialized explicitly.
+ * Initalize HCR_EL2.E2H so that later code can rely upon HCR_EL2.E2H
+ * indicating whether the CPU is running in E2H mode.
+ */
+ mrs_s x1, SYS_ID_AA64MMFR4_EL1
+ sbfx x1, x1, #ID_AA64MMFR4_EL1_E2H0_SHIFT, #ID_AA64MMFR4_EL1_E2H0_WIDTH
+ cmp x1, #0
+ b.lt .LnE2H0_\@
+
+ /*
+ * Unfortunately, HCR_EL2.E2H can be RES1 even if not advertised
+ * as such via ID_AA64MMFR4_EL1.E2H0:
+ *
+ * - Fruity CPUs predate the !FEAT_E2H0 relaxation, and seem to
+ * have HCR_EL2.E2H implemented as RAO/WI.
+ *
+ * - On CPUs that lack FEAT_FGT, a hypervisor can't trap guest
+ * reads of ID_AA64MMFR4_EL1 to advertise !FEAT_E2H0. NV
+ * guests on these hosts can write to HCR_EL2.E2H without
+ * trapping to the hypervisor, but these writes have no
+ * functional effect.
+ *
+ * Handle both cases by checking for an essential VHE property
+ * (system register remapping) to decide whether we're
+ * effectively VHE-only or not.
+ */
+ msr hcr_el2, x0 // Setup HCR_EL2 as nVHE
+ isb
+ mov x1, #1 // Write something to FAR_EL1
+ msr far_el1, x1
+ isb
+ mov x1, #2 // Try to overwrite it via FAR_EL2
+ msr far_el2, x1
+ isb
+ mrs x1, far_el1 // If we see the latest write in FAR_EL1,
+ cmp x1, #2 // we can safely assume we are VHE only.
+ b.ne .LnVHE_\@ // Otherwise, we know that nVHE works.
+
+.LnE2H0_\@:
+ orr x0, x0, #HCR_E2H
+ msr hcr_el2, x0
+ isb
+.LnVHE_\@:
+.endm
+
.macro __init_el2_sctlr
mov_q x0, INIT_SCTLR_EL2_MMU_OFF
msr sctlr_el2, x0
@@ -203,10 +255,6 @@
.Lskip_fgt_\@:
.endm
-.macro __init_el2_nvhe_prepare_eret
- mov x0, #INIT_PSTATE_EL1
- msr spsr_el2, x0
-.endm
/**
* Initialize EL2 registers to sane values. This should be called early on all
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 51cd106cd840..1b209efac972 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -146,5 +146,6 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val);
extern unsigned long kvm_nvhe_sym(__icache_flags);
extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits);
extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl);
+extern unsigned int kvm_nvhe_sym(hyp_gicv3_nr_lr);
#endif /* __ARM64_KVM_HYP_H__ */
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 7350243a6a28..3ae4239c88d1 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -281,6 +281,12 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
}
}
+#define ptep_get ptep_get
+static inline pte_t ptep_get(pte_t *ptep)
+{
+ return READ_ONCE(*ptep);
+}
+
extern void __sync_icache_dcache(pte_t pteval);
bool pgattr_change_is_safe(u64 old, u64 new);
@@ -308,7 +314,7 @@ static inline void __check_safe_pte_update(struct mm_struct *mm, pte_t *ptep,
if (!IS_ENABLED(CONFIG_DEBUG_VM))
return;
- old_pte = READ_ONCE(*ptep);
+ old_pte = ptep_get(ptep);
if (!pte_valid(old_pte) || !pte_valid(pte))
return;
@@ -897,7 +903,7 @@ static inline int __ptep_test_and_clear_young(pte_t *ptep)
{
pte_t old_pte, pte;
- pte = READ_ONCE(*ptep);
+ pte = ptep_get(ptep);
do {
old_pte = pte;
pte = pte_mkold(pte);
@@ -979,7 +985,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
{
pte_t old_pte, pte;
- pte = READ_ONCE(*ptep);
+ pte = ptep_get(ptep);
do {
old_pte = pte;
pte = pte_wrprotect(pte);
@@ -1071,7 +1077,7 @@ static inline void arch_swap_restore(swp_entry_t entry, struct folio *folio)
#endif /* CONFIG_ARM64_MTE */
/*
- * On AArch64, the cache coherency is handled via the set_pte_at() function.
+ * On AArch64, the cache coherency is handled via the set_ptes() function.
*/
static inline void update_mmu_cache_range(struct vm_fault *vmf,
struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 2b478ca356b0..e72d62416b1a 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -107,7 +107,7 @@ static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data)
{
struct set_perm_data *spd = data;
const efi_memory_desc_t *md = spd->md;
- pte_t pte = READ_ONCE(*ptep);
+ pte_t pte = ptep_get(ptep);
if (md->attribute & EFI_MEMORY_RO)
pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S
index 6325db1a2179..28af4bcd97a5 100644
--- a/arch/arm64/kernel/entry-fpsimd.S
+++ b/arch/arm64/kernel/entry-fpsimd.S
@@ -38,10 +38,10 @@ SYM_FUNC_END(fpsimd_load_state)
*
* x0 - pointer to buffer for state
* x1 - pointer to storage for FPSR
- * x2 - Save FFR if non-zero
+ * w2 - Save FFR if non-zero
*/
SYM_FUNC_START(sve_save_state)
- sve_save 0, x1, x2, 3
+ sve_save 0, x1, w2, 3
ret
SYM_FUNC_END(sve_save_state)
@@ -50,10 +50,10 @@ SYM_FUNC_END(sve_save_state)
*
* x0 - pointer to buffer for state
* x1 - pointer to storage for FPSR
- * x2 - Restore FFR if non-zero
+ * w2 - Restore FFR if non-zero
*/
SYM_FUNC_START(sve_load_state)
- sve_load 0, x1, x2, 4
+ sve_load 0, x1, w2, 4
ret
SYM_FUNC_END(sve_load_state)
@@ -103,13 +103,13 @@ SYM_FUNC_END(sme_set_vq)
* Save the ZA and ZT state
*
* x0 - pointer to buffer for state
- * x1 - number of ZT registers to save
+ * w1 - number of ZT registers to save
*/
SYM_FUNC_START(sme_save_state)
_sme_rdsvl 2, 1 // x2 = VL/8
sme_save_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
- cbz x1, 1f
+ cbz w1, 1f
_str_zt 0
1:
ret
@@ -119,13 +119,13 @@ SYM_FUNC_END(sme_save_state)
* Load the ZA and ZT state
*
* x0 - pointer to buffer for state
- * x1 - number of ZT registers to save
+ * w1 - number of ZT registers to save
*/
SYM_FUNC_START(sme_load_state)
_sme_rdsvl 2, 1 // x2 = VL/8
sme_load_za 0, x2, 12 // Leaves x0 pointing to the end of ZA
- cbz x1, 1f
+ cbz w1, 1f
_ldr_zt 0
1:
ret
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 6517bf2644a0..9996029853d2 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -575,10 +575,8 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
msr sctlr_el2, x0
isb
0:
- mov_q x0, HCR_HOST_NVHE_FLAGS
- msr hcr_el2, x0
- isb
+ init_el2_hcr HCR_HOST_NVHE_FLAGS
init_el2_state
/* Hypervisor stub */
@@ -588,27 +586,23 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)
mov_q x1, INIT_SCTLR_EL1_MMU_OFF
- /*
- * Fruity CPUs seem to have HCR_EL2.E2H set to RES1,
- * making it impossible to start in nVHE mode. Is that
- * compliant with the architecture? Absolutely not!
- */
mrs x0, hcr_el2
and x0, x0, #HCR_E2H
- cbz x0, 1f
+ cbz x0, 2f
/* Set a sane SCTLR_EL1, the VHE way */
pre_disable_mmu_workaround
msr_s SYS_SCTLR_EL12, x1
mov x2, #BOOT_CPU_FLAG_E2H
- b 2f
+ b 3f
-1:
+2:
pre_disable_mmu_workaround
msr sctlr_el1, x1
mov x2, xzr
-2:
- __init_el2_nvhe_prepare_eret
+3:
+ mov x0, #INIT_PSTATE_EL1
+ msr spsr_el2, x0
mov w0, #BOOT_CPU_MODE_EL2
orr x0, x0, x2
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 35225632d70a..c94dcffb7e83 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -557,6 +557,15 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,
else
alignment_mask = 0x7;
offset = hw->address & alignment_mask;
+
+ /*
+ * BAS is an 8-bit field in WCR/BCR; the shift below would
+ * silently drop the high bits of ctrl.len when offset + len
+ * exceeds 8, programming hardware to watch fewer bytes than
+ * the user requested.
+ */
+ if (((u32)hw->ctrl.len << offset) > ARM_BREAKPOINT_LEN_8)
+ return -EINVAL;
}
hw->address &= ~alignment_mask;
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index 84ea9c50c076..92e0ab252d62 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -67,7 +67,7 @@ int memcmp_pages(struct page *page1, struct page *page2)
/*
* If the page content is identical but at least one of the pages is
* tagged, return non-zero to avoid KSM merging. If only one of the
- * pages is tagged, set_pte_at() may zero or change the tags of the
+ * pages is tagged, set_ptes() may zero or change the tags of the
* other page via mte_sync_tags().
*/
if (page_mte_tagged(page1) || page_mte_tagged(page2))
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 3ae529e967c7..e49ead6d005c 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -2026,6 +2026,8 @@ static int __init init_subsystems(void)
switch (err) {
case 0:
vgic_present = true;
+ if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
+ kvm_nvhe_sym(hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr;
break;
case -ENODEV:
case -ENXIO:
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index efe82cc86bd1..ce238ef9e113 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -1073,7 +1073,7 @@ int kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
} else {
/*
* Only locking to serialise with a concurrent
- * set_pte_at() in the VMM but still overriding the
+ * set_ptes() in the VMM but still overriding the
* tags, hence ignoring the return value.
*/
try_page_mte_tagging(page);
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
index 1cc06e6797bd..9b2ada54be53 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
@@ -202,10 +202,11 @@ SYM_CODE_START_LOCAL(__kvm_hyp_init_cpu)
2: msr SPsel, #1 // We want to use SP_EL{1,2}
+ init_el2_hcr 0
+
/* Initialize EL2 CPU state to sane values. */
init_el2_state // Clobbers x0..x2
finalise_el2_state
- __init_el2_nvhe_prepare_eret
/* Enable MMU, set vectors and stack. */
mov x0, x28
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 350d1775a5ce..ac0d5a0de014 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -22,6 +22,9 @@
DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
+/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */
+unsigned int hyp_gicv3_nr_lr;
+
void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
@@ -30,6 +33,9 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
+ /* __hyp_running_vcpu must be NULL in a guest context. */
+ hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu = NULL;
+
hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state);
hyp_vcpu->vcpu.arch.sve_max_vl = host_vcpu->arch.sve_max_vl;
@@ -46,6 +52,12 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
+
+ /* Bound used_lrs by the number of implemented list registers. */
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs =
+ min_t(unsigned int,
+ hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs,
+ hyp_gicv3_nr_lr);
}
static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
index d57bcb6ab94d..5688a16e2ea7 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -218,6 +218,9 @@ asmlinkage void __noreturn __kvm_host_psci_cpu_entry(bool is_cpu_on)
if (is_cpu_on)
release_boot_args(boot_args);
+ write_sysreg_el1(INIT_SCTLR_EL1_MMU_OFF, SYS_SCTLR);
+ write_sysreg(INIT_PSTATE_EL1, SPSR_EL2);
+
__host_enter(host_ctxt);
}
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index c53186d84869..c8d180b80abd 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -713,6 +713,8 @@ struct vgic_its *vgic_msi_to_its(struct kvm *kvm, struct kvm_msi *msi)
address = (u64)msi->address_hi << 32 | msi->address_lo;
+ guard(srcu)(&kvm->srcu);
+
kvm_io_dev = kvm_io_bus_get_dev(kvm, KVM_MMIO_BUS, address);
if (!kvm_io_dev)
return ERR_PTR(-EINVAL);
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 8be4c1ebdec2..6f78baa72fb8 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -701,15 +701,16 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
raw_spin_lock(&irq->irq_lock);
/*
- * If the affinity has been preserved, move the
- * interrupt around. Otherwise, it means things have
- * changed while the interrupt was unlocked, and we
- * need to replay this.
+ * If the interrupt is still ours and its affinity has
+ * been preserved, move it around. Otherwise, it means
+ * things have changed while the interrupt was unlocked
+ * (it may even have been taken off the list with its
+ * affinity left untouched), and we need to replay this.
*
* In all cases, we cannot trust the list not to have
* changed, so we restart from the beginning.
*/
- if (target_vcpu == vgic_target_oracle(irq)) {
+ if (irq->vcpu == vcpu && target_vcpu == vgic_target_oracle(irq)) {
struct vgic_cpu *new_cpu = &target_vcpu->arch.vgic_cpu;
list_del(&irq->ap_list);
diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c
index 7232b1e70a12..cb845b3649a1 100644
--- a/arch/arm64/lib/insn.c
+++ b/arch/arm64/lib/insn.c
@@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
long offset;
offset = label_imm_common(pc, addr, SZ_1M);
+ if (offset >= SZ_1M)
+ return AARCH64_BREAK_FAULT;
insn = aarch64_insn_get_bcond_value();
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 893b9485b840..586bcf2a3db1 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -191,7 +191,7 @@ static void show_pte(unsigned long addr)
if (!ptep)
break;
- pte = READ_ONCE(*ptep);
+ pte = ptep_get(ptep);
pr_cont(", pte=%016llx", pte_val(pte));
pte_unmap(ptep);
} while(0);
@@ -205,7 +205,7 @@ static void show_pte(unsigned long addr)
*
* It needs to cope with hardware update of the accessed/dirty state by other
* agents in the system and can safely skip the __sync_icache_dcache() call as,
- * like set_pte_at(), the PTE is never changed from no-exec to exec here.
+ * like set_ptes(), the PTE is never changed from no-exec to exec here.
*
* Returns whether or not the PTE actually changed.
*/
@@ -214,7 +214,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
pte_t entry, int dirty)
{
pteval_t old_pteval, pteval;
- pte_t pte = READ_ONCE(*ptep);
+ pte_t pte = ptep_get(ptep);
if (pte_same(pte, entry))
return 0;
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 06efc3a1652e..688d11b1cabd 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -228,7 +228,7 @@ static void clear_flush(struct mm_struct *mm,
unsigned long i, saddr = addr;
for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
- ptep_clear(mm, addr, ptep);
+ ptep_get_and_clear(mm, addr, ptep);
flush_tlb_range(&vma, saddr, addr);
}
@@ -246,12 +246,12 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
if (!pte_present(pte)) {
for (i = 0; i < ncontig; i++, ptep++, addr += pgsize)
- set_pte_at(mm, addr, ptep, pte);
+ set_ptes(mm, addr, ptep, pte, 1);
return;
}
if (!pte_cont(pte)) {
- set_pte_at(mm, addr, ptep, pte);
+ set_ptes(mm, addr, ptep, pte, 1);
return;
}
@@ -262,7 +262,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
clear_flush(mm, addr, ptep, pgsize, ncontig);
for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
- set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
+ set_ptes(mm, addr, ptep, pfn_pte(pfn, hugeprot), 1);
}
pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
@@ -465,7 +465,7 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma,
hugeprot = pte_pgprot(pte);
for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
- set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
+ set_ptes(mm, addr, ptep, pfn_pte(pfn, hugeprot), 1);
return 1;
}
@@ -479,7 +479,7 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm,
size_t pgsize;
pte_t pte;
- if (!pte_cont(READ_ONCE(*ptep))) {
+ if (!pte_cont(ptep_get(ptep))) {
ptep_set_wrprotect(mm, addr, ptep);
return;
}
@@ -494,7 +494,7 @@ void huge_ptep_set_wrprotect(struct mm_struct *mm,
pfn = pte_pfn(pte);
for (i = 0; i < ncontig; i++, ptep++, addr += pgsize, pfn += dpfn)
- set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
+ set_ptes(mm, addr, ptep, pfn_pte(pfn, hugeprot), 1);
}
pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
@@ -504,7 +504,7 @@ pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
size_t pgsize;
int ncontig;
- if (!pte_cont(READ_ONCE(*ptep)))
+ if (!pte_cont(ptep_get(ptep)))
return ptep_clear_flush(vma, addr, ptep);
ncontig = find_num_contig(mm, addr, ptep, &pgsize);
@@ -552,7 +552,7 @@ pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr
* when the permission changes from executable to non-executable
* in cases where cpu is affected with errata #2645198.
*/
- if (pte_user_exec(READ_ONCE(*ptep)))
+ if (pte_user_exec(ptep_get(ptep)))
return huge_ptep_clear_flush(vma, addr, ptep);
}
return huge_ptep_get_and_clear(vma->vm_mm, addr, ptep, psize);
diff --git a/arch/arm64/mm/kasan_init.c b/arch/arm64/mm/kasan_init.c
index f17d066e85eb..1b96e0ad6661 100644
--- a/arch/arm64/mm/kasan_init.c
+++ b/arch/arm64/mm/kasan_init.c
@@ -113,7 +113,7 @@ static void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr,
memset(__va(page_phys), KASAN_SHADOW_INIT, PAGE_SIZE);
next = addr + PAGE_SIZE;
set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL));
- } while (ptep++, addr = next, addr != end && pte_none(READ_ONCE(*ptep)));
+ } while (ptep++, addr = next, addr != end && pte_none(ptep_get(ptep)));
}
static void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr,
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 8c5cbf4c858d..e075792d7225 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -165,7 +165,7 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
phys_addr_t phys, pgprot_t prot)
{
do {
- pte_t old_pte = READ_ONCE(*ptep);
+ pte_t old_pte = ptep_get(ptep);
/*
* Required barriers to make this visible to the table walker
@@ -178,7 +178,7 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
* only allow updates to the permission attributes.
*/
BUG_ON(!pgattr_change_is_safe(pte_val(old_pte),
- READ_ONCE(pte_val(*ptep))));
+ pte_val(ptep_get(ptep))));
phys += PAGE_SIZE;
} while (ptep++, addr += PAGE_SIZE, addr != end);
@@ -864,7 +864,7 @@ static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr,
do {
ptep = pte_offset_kernel(pmdp, addr);
- pte = READ_ONCE(*ptep);
+ pte = ptep_get(ptep);
if (pte_none(pte))
continue;
@@ -901,7 +901,13 @@ static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr,
if (free_mapped) {
/* CONT blocks are not supported in the vmemmap */
WARN_ON(pmd_cont(pmd));
- flush_tlb_kernel_range(addr, addr + PMD_SIZE);
+ /*
+ * Invalidating a block entry requires just
+ * a single overlapping TLB invalidation,
+ * so limit the range of the flush to a single
+ * page.
+ */
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
free_hotplug_page_range(pmd_page(pmd),
PMD_SIZE, altmap);
}
@@ -931,7 +937,8 @@ static void unmap_hotplug_pud_range(p4d_t *p4dp, unsigned long addr,
if (pud_sect(pud)) {
pud_clear(pudp);
if (free_mapped) {
- flush_tlb_kernel_range(addr, addr + PUD_SIZE);
+ /* See comment in unmap_hotplug_pmd_range(). */
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
free_hotplug_page_range(pud_page(pud),
PUD_SIZE, altmap);
}
@@ -1001,7 +1008,7 @@ static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,
do {
ptep = pte_offset_kernel(pmdp, addr);
- pte = READ_ONCE(*ptep);
+ pte = ptep_get(ptep);
/*
* This is just a sanity check here which verifies that
@@ -1020,7 +1027,7 @@ static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,
*/
ptep = pte_offset_kernel(pmdp, 0UL);
for (i = 0; i < PTRS_PER_PTE; i++) {
- if (!pte_none(READ_ONCE(ptep[i])))
+ if (!pte_none(ptep_get(&ptep[i])))
return;
}
@@ -1495,7 +1502,7 @@ pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte
* when the permission changes from executable to non-executable
* in cases where cpu is affected with errata #2645198.
*/
- if (pte_user_exec(READ_ONCE(*ptep)))
+ if (pte_user_exec(ptep_get(ptep)))
return ptep_clear_flush(vma, addr, ptep);
}
return ptep_get_and_clear(vma->vm_mm, addr, ptep);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 0a62f458c5cb..e0e35bd94222 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -36,7 +36,7 @@ bool can_set_direct_map(void)
static int change_page_range(pte_t *ptep, unsigned long addr, void *data)
{
struct page_change_data *cdata = data;
- pte_t pte = READ_ONCE(*ptep);
+ pte_t pte = ptep_get(ptep);
pte = clear_pte_bit(pte, cdata->clear_mask);
pte = set_pte_bit(pte, cdata->set_mask);
@@ -242,5 +242,5 @@ bool kernel_page_present(struct page *page)
return true;
ptep = pte_offset_kernel(pmdp, addr);
- return pte_valid(READ_ONCE(*ptep));
+ return pte_valid(ptep_get(ptep));
}
diff --git a/arch/arm64/mm/trans_pgd.c b/arch/arm64/mm/trans_pgd.c
index 7b14df3c6477..f71ab4704cce 100644
--- a/arch/arm64/mm/trans_pgd.c
+++ b/arch/arm64/mm/trans_pgd.c
@@ -33,7 +33,7 @@ static void *trans_alloc(struct trans_pgd_info *info)
static void _copy_pte(pte_t *dst_ptep, pte_t *src_ptep, unsigned long addr)
{
- pte_t pte = READ_ONCE(*src_ptep);
+ pte_t pte = ptep_get(src_ptep);
if (pte_valid(pte)) {
/*
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 76ce150e7347..43089787196b 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -1130,7 +1130,7 @@ UnsignedEnum 43:40 TraceFilt
0b0000 NI
0b0001 IMP
EndEnum
-UnsignedEnum 39:36 DoubleLock
+SignedEnum 39:36 DoubleLock
0b0000 IMP
0b1111 NI
EndEnum
@@ -1669,6 +1669,43 @@ UnsignedEnum 3:0 TCRX
EndEnum
EndSysreg
+Sysreg ID_AA64MMFR4_EL1 3 0 0 7 4
+Res0 63:40
+UnsignedEnum 39:36 E3DSE
+ 0b0000 NI
+ 0b0001 IMP
+EndEnum
+Res0 35:28
+SignedEnum 27:24 E2H0
+ 0b0000 IMP
+ 0b1110 NI_NV1
+ 0b1111 NI
+EndEnum
+UnsignedEnum 23:20 NV_frac
+ 0b0000 NV_NV2
+ 0b0001 NV2_ONLY
+EndEnum
+UnsignedEnum 19:16 FGWTE3
+ 0b0000 NI
+ 0b0001 IMP
+EndEnum
+UnsignedEnum 15:12 HACDBS
+ 0b0000 NI
+ 0b0001 IMP
+EndEnum
+UnsignedEnum 11:8 ASID2
+ 0b0000 NI
+ 0b0001 IMP
+EndEnum
+UnsignedEnum 7:4 EIESB
+ 0b0000 NI
+ 0b0001 ToEL3
+ 0b0010 ToELx
+ 0b1111 ANY
+EndEnum
+Res0 3:0
+EndSysreg
+
Sysreg SCTLR_EL1 3 0 1 0 0
Field 63 TIDCP
Field 62 SPINTMASK
diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h
index 49e29b29996f..4f31109714c0 100644
--- a/arch/loongarch/include/asm/acpi.h
+++ b/arch/loongarch/include/asm/acpi.h
@@ -37,6 +37,8 @@ static inline bool acpi_has_cpu_in_madt(void)
extern struct list_head acpi_wakeup_device_list;
extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC];
+extern void acpi_add_early_pio(void);
+extern void acpi_remove_early_pio(void);
extern int __init parse_acpi_topology(void);
static inline u32 get_acpi_id_for_cpu(unsigned int cpu)
diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h
index e7c09e33e109..0e725e09a95d 100644
--- a/arch/loongarch/include/asm/pgtable.h
+++ b/arch/loongarch/include/asm/pgtable.h
@@ -410,6 +410,8 @@ static inline pte_t pte_mkwrite_novma(pte_t pte)
static inline pte_t pte_wrprotect(pte_t pte)
{
+ if (pte_val(pte) & _PAGE_DIRTY)
+ pte_val(pte) |= _PAGE_MODIFIED;
pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY);
return pte;
}
@@ -522,6 +524,8 @@ static inline pmd_t pmd_mkwrite_novma(pmd_t pmd)
static inline pmd_t pmd_wrprotect(pmd_t pmd)
{
+ if (pmd_val(pmd) & _PAGE_DIRTY)
+ pmd_val(pmd) |= _PAGE_MODIFIED;
pmd_val(pmd) &= ~(_PAGE_WRITE | _PAGE_DIRTY);
return pmd;
}
diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
index 1f529b13490b..34101b753eaa 100644
--- a/arch/loongarch/kernel/acpi.c
+++ b/arch/loongarch/kernel/acpi.c
@@ -14,6 +14,7 @@
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/serial_core.h>
+#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/numa.h>
#include <asm/loongson.h>
@@ -57,6 +58,33 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
return ioremap_cache(phys, size);
}
+#define PIO_BASE (unsigned long)PCI_IOBASE
+#define PIO_SIZE ALIGN(ISA_IOSIZE, PAGE_SIZE)
+
+static bool acpi_pio;
+
+/* Add PIO for early access */
+void acpi_add_early_pio(void)
+{
+ if (!acpi_disabled) {
+ acpi_pio = true;
+ ioremap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE,
+ LOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL));
+ }
+}
+
+/* Remove PIO for PCI register */
+void acpi_remove_early_pio(void)
+{
+ if (!acpi_pio)
+ return;
+
+ if (!acpi_disabled) {
+ acpi_pio = false;
+ vunmap_range(PIO_BASE, PIO_BASE + PIO_SIZE);
+ }
+}
+
#ifdef CONFIG_SMP
static int set_processor_mask(u32 id, u32 flags)
{
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index 2e34ece474ee..729b0660b61c 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -530,6 +530,8 @@ static __init int arch_reserve_pio_range(void)
{
struct device_node *np;
+ acpi_add_early_pio();
+
for_each_node_by_name(np, "isa") {
struct of_range range;
struct of_range_parser parser;
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
index ed6762945437..d3fafa5cdf6b 100644
--- a/arch/loongarch/kernel/smp.c
+++ b/arch/loongarch/kernel/smp.c
@@ -613,6 +613,7 @@ static void stop_this_cpu(void *dummy)
set_cpu_online(smp_processor_id(), false);
calculate_cpu_foreign_map();
local_irq_disable();
+ rcu_report_dead(smp_processor_id());
while (true);
}
diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c
index 2d584a59a2a0..b53dae9d57c8 100644
--- a/arch/loongarch/pci/acpi.c
+++ b/arch/loongarch/pci/acpi.c
@@ -65,6 +65,8 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
struct resource_entry *entry, *tmp;
struct acpi_device *device = ci->bridge;
+ acpi_remove_early_pio();
+
status = acpi_pci_probe_root_resources(ci);
if (status > 0) {
acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h);
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
index 011d1d678840..a0b439c90488 100644
--- a/arch/mips/dec/int-handler.S
+++ b/arch/mips/dec/int-handler.S
@@ -277,7 +277,7 @@
srlv t3,t1,t2
handle_it:
- j dec_irq_dispatch
+ j do_IRQ
nop
#if defined(CONFIG_32BIT) && defined(CONFIG_MIPS_FP_SUPPORT)
diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c
index 723ce16cbfc0..a005246a0ac5 100644
--- a/arch/mips/dec/platform.c
+++ b/arch/mips/dec/platform.c
@@ -38,6 +38,10 @@ static struct platform_device dec_rtc_device = {
.num_resources = ARRAY_SIZE(dec_rtc_resources),
};
+static struct platform_device *dec_rtc_devices[] __initdata = {
+ &dec_rtc_device,
+};
+
static struct resource dec_dz_resources[] = {
{ .name = "dz", .flags = IORESOURCE_MEM, },
{ .name = "dz", .flags = IORESOURCE_IRQ, },
@@ -137,7 +141,7 @@ static int __init dec_add_devices(void)
}
num_zs = i;
- ret1 = platform_device_register(&dec_rtc_device);
+ ret1 = platform_add_devices(dec_rtc_devices, 1);
ret2 = IS_ENABLED(CONFIG_32BIT) ?
platform_add_devices(dec_dz_devices, num_dz) : 0;
ret3 = platform_add_devices(dec_zs_devices, num_zs);
diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c
index 8d74d7d6c05b..e46c7accefff 100644
--- a/arch/mips/dec/prom/init.c
+++ b/arch/mips/dec/prom/init.c
@@ -3,7 +3,7 @@
* init.c: PROM library initialisation code.
*
* Copyright (C) 1998 Harald Koerfgen
- * Copyright (C) 2002, 2004 Maciej W. Rozycki
+ * Copyright (C) 2002, 2004, 2026 Maciej W. Rozycki
*/
#include <linux/init.h>
#include <linux/kernel.h>
@@ -20,6 +20,10 @@
#include <asm/dec/prom.h>
+#ifdef CONFIG_64BIT
+unsigned long o32_stk[O32_STK_SIZE] __initdata = { 0 };
+#endif
+
int (*__rex_bootinit)(void);
int (*__rex_bootread)(void);
int (*__rex_getbitmap)(memmap *);
diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c
index 6c3704f51d0d..ca6cb6b7293b 100644
--- a/arch/mips/dec/setup.c
+++ b/arch/mips/dec/setup.c
@@ -776,9 +776,3 @@ void __init arch_init_irq(void)
pr_err("Failed to register halt interrupt\n");
}
}
-
-asmlinkage unsigned int dec_irq_dispatch(unsigned int irq)
-{
- do_IRQ(irq);
- return 0;
-}
diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S
index ee856709e0b6..77533cfbdfc1 100644
--- a/arch/mips/fw/lib/call_o32.S
+++ b/arch/mips/fw/lib/call_o32.S
@@ -74,7 +74,7 @@ NESTED(call_o32, O32_FRAMESZ, ra)
PTR_LA t1,6*O32_SZREG(fp)
li t2,O32_ARGC-6
1:
- lw t3,(t0)
+ ld t3,(t0)
REG_ADDU t0,SZREG
sw t3,(t1)
REG_SUBU t2,1
diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h
index 908e96e3a311..1f83e94e3b82 100644
--- a/arch/mips/include/asm/dec/prom.h
+++ b/arch/mips/include/asm/dec/prom.h
@@ -4,7 +4,7 @@
*
* DECstation PROM interface.
*
- * Copyright (C) 2002 Maciej W. Rozycki
+ * Copyright (C) 2002, 2026 Maciej W. Rozycki
*
* Based on arch/mips/dec/prom/prom.h by the Anonymous.
*/
@@ -97,6 +97,17 @@ extern int (*__pmax_close)(int);
#ifdef CONFIG_64BIT
+#define O32_STK_SIZE 512
+extern unsigned long o32_stk[];
+
+/* Switch the stack if outside the 32-bit address space. */
+static inline unsigned long *o32_get_stk(void)
+{
+ long fp = (long)__builtin_frame_address(0);
+
+ return fp != (int)fp ? o32_stk + O32_STK_SIZE : NULL;
+}
+
/*
* On MIPS64 we have to call PROM functions via a helper
* dispatcher to accommodate ABI incompatibilities.
@@ -128,7 +139,7 @@ int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...));
#define prom_getchar() _prom_getchar(__prom_getchar, NULL)
#define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x)
-#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x)
+#define prom_printf(x...) _prom_printf(__prom_printf, o32_get_stk(), x)
#else /* !CONFIG_64BIT */
diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c
index 67e130d3f038..519d14553536 100644
--- a/arch/mips/kernel/mips-mt-fpaff.c
+++ b/arch/mips/kernel/mips-mt-fpaff.c
@@ -70,11 +70,16 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
struct task_struct *p;
int retval;
- if (len < sizeof(new_mask))
- return -EINVAL;
-
- if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
- return -EFAULT;
+ if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
+ return -ENOMEM;
+ if (len < cpumask_size())
+ cpumask_clear(new_mask);
+ else if (len > cpumask_size())
+ len = cpumask_size();
+ if (copy_from_user(new_mask, user_mask_ptr, len)) {
+ retval = -EFAULT;
+ goto out_free_new_mask;
+ }
cpus_read_lock();
rcu_read_lock();
@@ -83,7 +88,8 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
if (!p) {
rcu_read_unlock();
cpus_read_unlock();
- return -ESRCH;
+ retval = -ESRCH;
+ goto out_free_new_mask;
}
/* Prevent p going away */
@@ -94,13 +100,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
retval = -ENOMEM;
goto out_put_task;
}
- if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) {
- retval = -ENOMEM;
- goto out_free_cpus_allowed;
- }
if (!alloc_cpumask_var(&effective_mask, GFP_KERNEL)) {
retval = -ENOMEM;
- goto out_free_new_mask;
+ goto out_free_cpus_allowed;
}
if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) {
retval = -EPERM;
@@ -141,13 +143,13 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
}
out_unlock:
free_cpumask_var(effective_mask);
-out_free_new_mask:
- free_cpumask_var(new_mask);
out_free_cpus_allowed:
free_cpumask_var(cpus_allowed);
out_put_task:
put_task_struct(p);
cpus_read_unlock();
+out_free_new_mask:
+ free_cpumask_var(new_mask);
return retval;
}
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 81f6c4f8fbc1..1a3eb4820eb7 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -19,6 +19,7 @@
#include <linux/sched/mm.h>
#include <linux/cpumask.h>
#include <linux/cpu.h>
+#include <linux/rcupdate.h>
#include <linux/err.h>
#include <linux/ftrace.h>
#include <linux/irqdomain.h>
@@ -410,6 +411,7 @@ static void stop_this_cpu(void *dummy)
set_cpu_online(smp_processor_id(), false);
calculate_cpu_foreign_map();
local_irq_disable();
+ rcu_report_dead(smp_processor_id());
while (1);
}
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c
index 6e368a4658b5..de3713b42cbc 100644
--- a/arch/mips/mm/init.c
+++ b/arch/mips/mm/init.c
@@ -281,9 +281,15 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages,
void *data)
{
struct maar_walk_info *wi = data;
- struct maar_config *cfg = &wi->cfg[wi->num_cfg];
+ struct maar_config *cfg;
unsigned int maar_align;
+ /* Ensure we don't overflow the cfg array */
+ if (WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg)))
+ return -1;
+
+ cfg = &wi->cfg[wi->num_cfg];
+
/* MAAR registers hold physical addresses right shifted by 4 bits */
maar_align = BIT(MIPS_MAAR_ADDR_SHIFT + 4);
@@ -292,9 +298,7 @@ static int maar_res_walk(unsigned long start_pfn, unsigned long nr_pages,
cfg->upper = ALIGN_DOWN(PFN_PHYS(start_pfn + nr_pages), maar_align) - 1;
cfg->attrs = MIPS_MAAR_S;
- /* Ensure we don't overflow the cfg array */
- if (!WARN_ON(wi->num_cfg >= ARRAY_SIZE(wi->cfg)))
- wi->num_cfg++;
+ wi->num_cfg++;
return 0;
}
diff --git a/arch/mips/n64/init.c b/arch/mips/n64/init.c
index dfbd864f4667..66ec28ab41f3 100644
--- a/arch/mips/n64/init.c
+++ b/arch/mips/n64/init.c
@@ -50,7 +50,7 @@ void __init prom_init(void)
#define W 320
#define H 240
-#define REG_BASE ((u32 *) CKSEG1ADDR(0x4400000))
+#define REG_BASE ((u32 __iomem *) CKSEG1ADDR(0x4400000))
static void __init n64rdp_write_reg(const u8 reg, const u32 value)
{
diff --git a/arch/mips/ralink/mt7621.c b/arch/mips/ralink/mt7621.c
index 137781d0bd0a..01a621921408 100644
--- a/arch/mips/ralink/mt7621.c
+++ b/arch/mips/ralink/mt7621.c
@@ -63,7 +63,7 @@ phys_addr_t mips_cpc_default_phys_base(void)
static bool __init mt7621_addr_wraparound_test(phys_addr_t size)
{
- void *dm = (void *)KSEG1ADDR(&detect_magic);
+ void __iomem *dm = (void __iomem *)KSEG1ADDR(&detect_magic);
if (CPHYSADDR(dm + size) >= MT7621_LOWMEM_MAX_SIZE)
return true;
diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c
index 7026b464b02e..f39017d6bd84 100644
--- a/arch/mips/sgi-ip22/ip22-gio.c
+++ b/arch/mips/sgi-ip22/ip22-gio.c
@@ -30,7 +30,6 @@ static struct {
static void gio_bus_release(struct device *dev)
{
- kfree(dev);
}
static struct device gio_bus = {
@@ -101,6 +100,8 @@ int gio_device_register(struct gio_device *giodev)
{
giodev->dev.bus = &gio_bus_type;
giodev->dev.parent = &gio_bus;
+ giodev->dev.release = gio_release_dev;
+
return device_register(&giodev->dev);
}
EXPORT_SYMBOL_GPL(gio_device_register);
@@ -132,13 +133,9 @@ static int gio_device_probe(struct device *dev)
if (!drv->probe)
return error;
- gio_dev_get(gio_dev);
-
match = gio_match_device(drv->id_table, gio_dev);
if (match)
error = drv->probe(gio_dev, match);
- if (error)
- gio_dev_put(gio_dev);
return error;
}
diff --git a/arch/powerpc/kexec/core_64.c b/arch/powerpc/kexec/core_64.c
index ec7e909d7dcb..f8ae6c035aa1 100644
--- a/arch/powerpc/kexec/core_64.c
+++ b/arch/powerpc/kexec/core_64.c
@@ -167,7 +167,7 @@ static void kexec_prepare_cpus_wait(int wait_state)
int my_cpu, i, notified=-1;
hw_breakpoint_disable();
- my_cpu = get_cpu();
+ my_cpu = raw_smp_processor_id();
/* Make sure each CPU has at least made it to the state we need.
*
* FIXME: There is a (slim) chance of a problem if not all of the CPUs
@@ -265,8 +265,6 @@ static void kexec_prepare_cpus(void)
/* after we tell the others to go down */
if (ppc_md.kexec_cpu_down)
ppc_md.kexec_cpu_down(0, 0);
-
- put_cpu();
}
#else /* ! SMP */
diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c
index 1a53ab08447c..31037df4178c 100644
--- a/arch/powerpc/perf/core-fsl-emb.c
+++ b/arch/powerpc/perf/core-fsl-emb.c
@@ -366,9 +366,10 @@ static void fsl_emb_pmu_del(struct perf_event *event, int flags)
cpuhw->n_events--;
+ put_cpu_var(cpu_hw_events);
+
out:
perf_pmu_enable(event->pmu);
- put_cpu_var(cpu_hw_events);
}
static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags)
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 02a8158c469d..c35485c43b04 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -268,10 +268,12 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma,
if (write && !(vma->vm_flags & VM_WRITE))
return -EACCES;
+ if (offset >= LS_SIZE)
+ return -EFAULT;
if (spu_acquire(ctx))
return -EINTR;
- if ((offset + len) > vma->vm_end)
- len = vma->vm_end - offset;
+ if ((offset + len) > LS_SIZE)
+ len = LS_SIZE - offset;
local_store = ctx->ops->get_ls(ctx);
if (write)
memcpy_toio(local_store + offset, buf, len);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 4dbb47ddbdcc..06ed5e2aa265 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -396,7 +396,8 @@ static void pnv_kexec_wait_secondaries_down(void)
{
int my_cpu, i, notified = -1;
- my_cpu = get_cpu();
+ /* Called with interrupts disabled, so the CPU is pinned. */
+ my_cpu = raw_smp_processor_id();
for_each_online_cpu(i) {
uint8_t status;
diff --git a/arch/powerpc/platforms/pseries/papr_platform_attributes.c b/arch/powerpc/platforms/pseries/papr_platform_attributes.c
index eea2041b270b..f63e2de9ccca 100644
--- a/arch/powerpc/platforms/pseries/papr_platform_attributes.c
+++ b/arch/powerpc/platforms/pseries/papr_platform_attributes.c
@@ -271,11 +271,9 @@ static int __init papr_init(void)
esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs);
temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL);
- if (temp_esi_buf)
- esi_buf = temp_esi_buf;
- else
- return -ENOMEM;
-
+ if (!temp_esi_buf)
+ goto out_free_esi_buf;
+ esi_buf = temp_esi_buf;
goto retry;
}
diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c
index 9fb97c9f9b0b..54f65c8ef4d2 100644
--- a/arch/riscv/kernel/cacheinfo.c
+++ b/arch/riscv/kernel/cacheinfo.c
@@ -128,7 +128,7 @@ int populate_cache_leaves(unsigned int cpu)
ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
levels = level;
}
- of_node_put(np);
+ of_node_put(prev);
return 0;
}
diff --git a/arch/riscv/kernel/machine_kexec.c b/arch/riscv/kernel/machine_kexec.c
index ccb0c5d5c63c..353d2b39f1d3 100644
--- a/arch/riscv/kernel/machine_kexec.c
+++ b/arch/riscv/kernel/machine_kexec.c
@@ -67,6 +67,9 @@ machine_kexec_prepare(struct kimage *image)
if (image->segment[i].memsz <= sizeof(fdt))
continue;
+ if (!image->segment[i].buf)
+ continue;
+
if (image->file_mode)
memcpy(&fdt, image->segment[i].buf, sizeof(fdt));
else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt)))
diff --git a/arch/riscv/kernel/probes/rethook_trampoline.S b/arch/riscv/kernel/probes/rethook_trampoline.S
index 21bac92a170a..e4a8408cd2ef 100644
--- a/arch/riscv/kernel/probes/rethook_trampoline.S
+++ b/arch/riscv/kernel/probes/rethook_trampoline.S
@@ -41,6 +41,9 @@
REG_S x29, PT_T4(sp)
REG_S x30, PT_T5(sp)
REG_S x31, PT_T6(sp)
+ /* save original sp */
+ addi a0, sp, PT_SIZE_ON_STACK
+ REG_S a0, PT_SP(sp)
.endm
.macro restore_all_base_regs
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index 124ad8fe626f..aa0b2f284dca 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task,
while (!kstack_end(ksp)) {
if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
break;
- pc = READ_ONCE_NOCHECK(*ksp++) - 0x4;
+ pc = READ_ONCE_NOCHECK(*ksp++);
}
}
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index 7b70ccb7fec3..8d8c5eb2e8da 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -123,7 +123,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
bpf_jit_binary_pack_alloc(prog_size + extable_size,
&jit_data->ro_image, sizeof(u32),
&jit_data->header, &jit_data->image,
- bpf_fill_ill_insns);
+ bpf_fill_ill_insns,
+ bpf_prog_was_classic(prog));
if (!jit_data->ro_header) {
prog = orig_prog;
goto out_offset;
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index e590b4c09625..fbf4eb85cb0a 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -17,6 +17,7 @@
#include <linux/export.h>
#include <linux/miscdevice.h>
#include <linux/perf_event.h>
+#include <linux/nospec.h>
#include <asm/cpu_mf.h>
#include <asm/hwctrset.h>
@@ -766,6 +767,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
if (!is_userspace_event(ev)) {
if (ev >= ARRAY_SIZE(cpumf_generic_events_user))
return -EOPNOTSUPP;
+ ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_user));
ev = cpumf_generic_events_user[ev];
}
} else if (!attr->exclude_kernel && attr->exclude_user) {
@@ -776,6 +778,7 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
if (!is_userspace_event(ev)) {
if (ev >= ARRAY_SIZE(cpumf_generic_events_basic))
return -EOPNOTSUPP;
+ ev = array_index_nospec(ev, ARRAY_SIZE(cpumf_generic_events_basic));
ev = cpumf_generic_events_basic[ev];
}
}
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 258000417724..4055359fd54f 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -48,7 +48,7 @@ void ret_from_fork(void) asm("ret_from_fork");
void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs)
{
- void (*func)(void *arg);
+ int (*func)(void *arg);
schedule_tail(prev);
diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c
index e5d3c0793f61..6e6726b2a441 100644
--- a/arch/s390/kvm/pci.c
+++ b/arch/s390/kvm/pci.c
@@ -302,9 +302,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
gaite->gisc = fib->fmt0.isc;
gaite->count++;
- gaite->aisbo = fib->fmt0.aisbo;
- gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb &
- ~PAGE_MASK));
+ if (fib->fmt0.sum == 1) {
+ gaite->aisbo = fib->fmt0.aisbo;
+ gaite->aisb = virt_to_phys(page_address(aisb_page) +
+ (fib->fmt0.aisb & ~PAGE_MASK));
+ } else {
+ gaite->aisbo = 0;
+ gaite->aisb = 0;
+ }
aift->kzdev[zdev->aisb] = zdev->kzdev;
spin_unlock_irq(&aift->gait_lock);
@@ -330,6 +335,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
unpin1:
unpin_user_page(aibv_page);
out:
+ kvm_s390_gisc_unregister(kvm, fib->fmt0.isc);
return rc;
}
diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c
index ab657b359789..36d851c7454a 100644
--- a/arch/sparc/kernel/led.c
+++ b/arch/sparc/kernel/led.c
@@ -78,7 +78,7 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer,
return PTR_ERR(buf);
/* work around \n when echo'ing into proc */
- if (buf[count - 1] == '\n')
+ if (count > 0 && buf[count - 1] == '\n')
buf[count - 1] = '\0';
/* before we change anything we want to stop any running timers,
diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c
index 55c98fdd67d2..45c533c67e94 100644
--- a/arch/x86/boot/compressed/acpi.c
+++ b/arch/x86/boot/compressed/acpi.c
@@ -182,10 +182,15 @@ static unsigned long get_cmdline_acpi_rsdp(void)
char val[MAX_ADDR_LEN] = { };
int ret;
- ret = cmdline_find_option("acpi_rsdp", val, MAX_ADDR_LEN);
+ ret = cmdline_find_option("acpi_rsdp", val, sizeof(val));
if (ret < 0)
return 0;
+ if (ret >= sizeof(val)) {
+ warn("acpi_rsdp= value too long; ignoring");
+ return 0;
+ }
+
if (boot_kstrtoul(val, 16, &addr))
return 0;
#endif
diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c
index 023bf1c3de8b..5b83beab89e1 100644
--- a/arch/x86/boot/early_serial_console.c
+++ b/arch/x86/boot/early_serial_console.c
@@ -117,7 +117,7 @@ static unsigned int probe_baud(int port)
static void parse_console_uart8250(void)
{
char optstr[64], *options;
- int baud = DEFAULT_BAUD;
+ int baud;
int port = 0;
/*
@@ -136,10 +136,13 @@ static void parse_console_uart8250(void)
else
return;
- if (options && (options[0] == ','))
- baud = simple_strtoull(options + 1, &options, 0);
- else
+ if (options && (options[0] == ',')) {
+ baud = simple_strtoull(options + 1, NULL, 0);
+ if (!baud)
+ baud = DEFAULT_BAUD;
+ } else {
baud = probe_baud(port);
+ }
if (port)
early_serial_init(port, baud);
diff --git a/arch/x86/events/amd/brs.c b/arch/x86/events/amd/brs.c
index ed308719236c..01fcd43522bc 100644
--- a/arch/x86/events/amd/brs.c
+++ b/arch/x86/events/amd/brs.c
@@ -259,13 +259,13 @@ void amd_brs_disable_all(void)
amd_brs_disable();
}
-static bool amd_brs_match_plm(struct perf_event *event, u64 to)
+static bool amd_brs_match_plm(struct perf_event *event, u64 from, u64 to)
{
int type = event->attr.branch_sample_type;
int plm_k = PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_HV;
int plm_u = PERF_SAMPLE_BRANCH_USER;
- if (!(type & plm_k) && kernel_ip(to))
+ if (!(type & plm_k) && (kernel_ip(to) || kernel_ip(from)))
return 0;
if (!(type & plm_u) && !kernel_ip(to))
@@ -338,11 +338,11 @@ void amd_brs_drain(void)
*/
to = (u64)(((s64)to << shift) >> shift);
- if (!amd_brs_match_plm(event, to))
- continue;
-
rdmsrl(brs_from(brs_idx), from);
+ if (!amd_brs_match_plm(event, from, to))
+ continue;
+
perf_clear_branch_entry_bitfields(br+nr);
br[nr].from = from;
diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c
index 211f429750f5..24f356e561e2 100644
--- a/arch/x86/events/amd/core.c
+++ b/arch/x86/events/amd/core.c
@@ -1364,12 +1364,12 @@ static int __init amd_core_pmu_init(void)
u64 even_ctr_mask = 0ULL;
int i;
- if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
- return 0;
-
/* Avoid calculating the value each time in the NMI handler */
perf_nmi_window = msecs_to_jiffies(100);
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
+ return 0;
+
/*
* If core performance counter extensions exists, we must use
* MSR_F15H_PERF_CTL/MSR_F15H_PERF_CTR msrs. See also
diff --git a/arch/x86/events/amd/lbr.c b/arch/x86/events/amd/lbr.c
index 5149830c7c4f..0788c11febf6 100644
--- a/arch/x86/events/amd/lbr.c
+++ b/arch/x86/events/amd/lbr.c
@@ -124,7 +124,8 @@ static void amd_pmu_lbr_filter(void)
}
/* If type does not correspond, then discard */
- if (type == X86_BR_NONE || (br_sel & type) != type) {
+ if (type == X86_BR_NONE || (br_sel & type) != type ||
+ (!(br_sel & X86_BR_KERNEL) && kernel_ip(cpuc->lbr_entries[i].from))) {
cpuc->lbr_entries[i].from = 0; /* mark invalid */
compress = true;
}
diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c
index 7fd4334e12a1..21735c40b2b0 100644
--- a/arch/x86/events/intel/uncore_snb.c
+++ b/arch/x86/events/intel/uncore_snb.c
@@ -537,12 +537,6 @@ void tgl_uncore_cpu_init(void)
skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box;
}
-static void adl_uncore_msr_init_box(struct intel_uncore_box *box)
-{
- if (box->pmu->pmu_idx == 0)
- wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
-}
-
static void adl_uncore_msr_enable_box(struct intel_uncore_box *box)
{
wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
@@ -561,7 +555,6 @@ static void adl_uncore_msr_exit_box(struct intel_uncore_box *box)
}
static struct intel_uncore_ops adl_uncore_msr_ops = {
- .init_box = adl_uncore_msr_init_box,
.enable_box = adl_uncore_msr_enable_box,
.disable_box = adl_uncore_msr_disable_box,
.exit_box = adl_uncore_msr_exit_box,
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index fb469ace3839..c7d019b0ef4a 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -418,6 +418,10 @@ extern void srso_alias_untrain_ret(void);
extern void entry_untrain_ret(void);
extern void entry_ibpb(void);
+#ifdef CONFIG_BPF_JIT
+extern void bpf_arch_ibpb(void);
+#endif
+
#ifdef CONFIG_X86_64
extern void clear_bhb_loop(void);
#endif
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index ef1d3a5024ed..32a27c19acde 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -16,6 +16,7 @@
#include <linux/sched/smt.h>
#include <linux/pgtable.h>
#include <linux/bpf.h>
+#include <linux/filter.h>
#include <asm/spec-ctrl.h>
#include <asm/cmdline.h>
@@ -1360,8 +1361,21 @@ static inline const char *spectre_v2_module_string(void)
{
return spectre_v2_bad_module ? " - vulnerable module loaded" : "";
}
+
+/*
+ * The "retpoline sequence" is the "call;mov;ret" sequence that
+ * replaces normal indirect branch instructions. Differentiate
+ * *the* retpoline sequence from the LFENCE-prefixed indirect
+ * branches that simply use the retpoline infrastructure.
+ */
+static inline bool retpoline_seq_enabled(void)
+{
+ return boot_cpu_has(X86_FEATURE_RETPOLINE) && !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE);
+}
+
#else
static inline const char *spectre_v2_module_string(void) { return ""; }
+static inline bool retpoline_seq_enabled(void) { return false; }
#endif
#define SPECTRE_V2_LFENCE_MSG "WARNING: LFENCE mitigation is not recommended for this CPU, data leaks possible!\n"
@@ -1835,8 +1849,7 @@ static void __init bhi_select_mitigation(void)
return;
/* Retpoline mitigates against BHI unless the CPU has RRSBA behavior */
- if (boot_cpu_has(X86_FEATURE_RETPOLINE) &&
- !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) {
+ if (retpoline_seq_enabled()) {
spec_ctrl_disable_kernel_rrsba();
if (rrsba_disabled)
return;
@@ -1858,6 +1871,27 @@ static void __init bhi_select_mitigation(void)
pr_info("Spectre BHI mitigation: SW BHB clearing on syscall\n");
}
+#ifdef CONFIG_BPF_JIT
+static void __bpf_arch_ibpb(void *unused)
+{
+ entry_ibpb();
+}
+
+void bpf_arch_ibpb(void)
+{
+ on_each_cpu(__bpf_arch_ibpb, NULL, 1);
+}
+
+static bool __init cpu_wants_ibpb_bpf(void)
+{
+ /* A genuine retpoline already neutralizes ring0 indirect predictions */
+ if (retpoline_seq_enabled())
+ return false;
+
+ return boot_cpu_has(X86_FEATURE_IBPB);
+}
+#endif
+
static void __init spectre_v2_select_mitigation(void)
{
enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
@@ -2041,6 +2075,14 @@ static void __init spectre_v2_select_mitigation(void)
pr_info("Enabling Restricted Speculation for firmware calls\n");
}
+#ifdef CONFIG_BPF_JIT
+ if (cpu_wants_ibpb_bpf()) {
+ static_call_update(bpf_arch_pred_flush, bpf_arch_ibpb);
+ static_branch_enable(&bpf_pred_flush_enabled);
+ pr_info("Enabling IBPB for BPF\n");
+ }
+#endif
+
/* Set up IBPB and STIBP depending on the general spectre V2 command */
spectre_v2_cmd = cmd;
}
@@ -3210,9 +3252,7 @@ static const char *spectre_bhi_state(void)
return "; BHI: BHI_DIS_S";
else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP))
return "; BHI: SW loop, KVM: SW loop";
- else if (boot_cpu_has(X86_FEATURE_RETPOLINE) &&
- !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE) &&
- rrsba_disabled)
+ else if (retpoline_seq_enabled() && rrsba_disabled)
return "; BHI: Retpoline";
else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT))
return "; BHI: Vulnerable, KVM: SW loop";
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 01c71e67a093..f5d0fd131b27 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -3138,6 +3138,12 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
struct kvm_lapic *apic)
{
int vector;
+
+ if (unlikely(!pv_eoi_enabled(vcpu))) {
+ __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
+ return;
+ }
+
/*
* PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host
* and KVM_PV_EOI_ENABLED in guest memory as follows:
@@ -3149,8 +3155,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
* KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset:
* -> host enabled PV EOI, guest executed EOI.
*/
- BUG_ON(!pv_eoi_enabled(vcpu));
-
if (pv_eoi_test_and_clr_pending(vcpu))
return;
vector = apic_set_eoi(apic);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 85d301a03b20..ebbb9f63beae 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1810,6 +1810,24 @@ static void vmx_inject_exception(struct kvm_vcpu *vcpu)
u32 intr_info = ex->vector | INTR_INFO_VALID_MASK;
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ /*
+ * When injecting a #DB, single-stepping is enabled in RFLAGS, and STI
+ * or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to
+ * prevent a false positive from VM-Entry consistency check. VM-Entry
+ * asserts that a single-step #DB _must_ be pending in this scenario,
+ * as the previous instruction cannot have toggled RFLAGS.TF 0=>1
+ * (because STI and POP/MOV don't modify RFLAGS), therefore the one
+ * instruction delay when activating single-step breakpoints must have
+ * already expired. However, the CPU isn't smart enough to peek at
+ * vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there
+ * is indeed a #DB pending/imminent.
+ */
+ if (ex->vector == DB_VECTOR &&
+ (vmx_get_rflags(vcpu) & X86_EFLAGS_TF) &&
+ vmx_get_interrupt_shadow(vcpu))
+ vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS,
+ vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS);
+
kvm_deliver_exception_payload(vcpu, ex);
if (ex->has_error_code) {
@@ -5281,26 +5299,9 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
* avoid single-step #DB and MTF updates, as ICEBP is
* higher priority. Note, skipping ICEBP still clears
* STI and MOVSS blocking.
- *
- * For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS
- * if single-step is enabled in RFLAGS and STI or MOVSS
- * blocking is active, as the CPU doesn't set the bit
- * on VM-Exit due to #DB interception. VM-Entry has a
- * consistency check that a single-step #DB is pending
- * in this scenario as the previous instruction cannot
- * have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV
- * don't modify RFLAGS), therefore the one instruction
- * delay when activating single-step breakpoints must
- * have already expired. Note, the CPU sets/clears BS
- * as appropriate for all other VM-Exits types.
*/
if (is_icebp(intr_info))
WARN_ON(!skip_emulated_instruction(vcpu));
- else if ((vmx_get_rflags(vcpu) & X86_EFLAGS_TF) &&
- (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
- (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS)))
- vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS,
- vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS) | DR6_BS);
kvm_queue_exception_p(vcpu, DB_VECTOR, dr6);
return 1;
@@ -6700,11 +6701,10 @@ static noinstr void vmx_l1d_flush(struct kvm_vcpu *vcpu)
static void vmx_update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
{
- struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
int tpr_threshold;
if (is_guest_mode(vcpu) &&
- nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW))
+ nested_cpu_has(get_vmcs12(vcpu), CPU_BASED_TPR_SHADOW))
return;
tpr_threshold = (irr == -1 || tpr < irr) ? 0 : irr;
diff --git a/arch/x86/mm/numa_emulation.c b/arch/x86/mm/numa_emulation.c
index 9a9305367fdd..b8ae60553714 100644
--- a/arch/x86/mm/numa_emulation.c
+++ b/arch/x86/mm/numa_emulation.c
@@ -211,7 +211,7 @@ static u64 uniform_size(u64 max_addr, u64 base, u64 hole, int nr_nodes)
* Sets up fake nodes of `size' interleaved over physical nodes ranging from
* `addr' to `max_addr'.
*
- * Returns zero on success or negative on error.
+ * Returns node ID of the next node on success or negative error code.
*/
static int __init split_nodes_size_interleave_uniform(struct numa_meminfo *ei,
struct numa_meminfo *pi,
@@ -395,7 +395,7 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt)
if (strchr(emu_cmdline, 'U')) {
nodemask_t physnode_mask = numa_nodes_parsed;
unsigned long n;
- int nid = 0;
+ int nid = 0, nr_created;
n = simple_strtoul(emu_cmdline, &emu_cmdline, 0);
ret = -1;
@@ -413,9 +413,18 @@ void __init numa_emulation(struct numa_meminfo *numa_meminfo, int numa_dist_cnt)
n, &pi.blk[0], nid);
if (ret < 0)
break;
- if (ret < n) {
+
+ /*
+ * If no memory was found for this physical node,
+ * skip the under-allocation check.
+ */
+ if (ret == nid)
+ continue;
+
+ nr_created = ret - nid;
+ if (nr_created < n) {
pr_info("%s: phys: %d only got %d of %ld nodes, failing\n",
- __func__, i, ret, n);
+ __func__, i, nr_created, n);
ret = -1;
break;
}
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 20ce6a1bd6c5..19e995f58d63 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -612,6 +612,14 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
*/
cond_mitigation(tsk);
+ /*
+ * Indicate that CR3 is about to change. nmi_uaccess_okay()
+ * and others are sensitive to the window where mm_cpumask(),
+ * CR3 and cpu_tlbstate.loaded_mm are not all in sync.
+ */
+ this_cpu_write(cpu_tlbstate.loaded_mm, LOADED_MM_SWITCHING);
+ barrier();
+
/*
* Stop remote flushes for the previous mm.
* Skip kernel threads; we never send init_mm TLB flushing IPIs,
@@ -629,14 +637,6 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
next_tlb_gen = atomic64_read(&next->context.tlb_gen);
choose_new_asid(next, next_tlb_gen, &new_asid, &need_flush);
-
- /*
- * Indicate that CR3 is about to change. nmi_uaccess_okay()
- * and others are sensitive to the window where mm_cpumask(),
- * CR3 and cpu_tlbstate.loaded_mm are not all in sync.
- */
- this_cpu_write(cpu_tlbstate.loaded_mm, LOADED_MM_SWITCHING);
- barrier();
}
new_lam = mm_lam_cr3_mask(next);
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 0be138fbd0a0..8028a5a4ab64 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -2927,7 +2927,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
/* allocate module memory for x86 insns and extable */
header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size,
&image, align, &rw_header, &rw_image,
- jit_fill_hole);
+ jit_fill_hole,
+ bpf_prog_was_classic(prog));
if (!header) {
prog = orig_prog;
goto out_addrs;
diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c
index 68244a3422d1..683935da493d 100644
--- a/arch/x86/platform/olpc/olpc-xo15-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo15-sci.c
@@ -185,6 +185,7 @@ static int xo15_sci_add(struct acpi_device *device)
static void xo15_sci_remove(struct acpi_device *device)
{
+ device_init_wakeup(&device->dev, false);
acpi_disable_gpe(NULL, xo15_sci_gpe);
acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler);
cancel_work_sync(&sci_work);
diff --git a/block/bdev.c b/block/bdev.c
index a8357b72a27b..a255492ba826 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -383,16 +383,12 @@ EXPORT_SYMBOL_GPL(blockdev_superblock);
void __init bdev_cache_init(void)
{
- int err;
static struct vfsmount *bd_mnt;
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD|SLAB_ACCOUNT|SLAB_PANIC),
init_once);
- err = register_filesystem(&bd_type);
- if (err)
- panic("Cannot register bdev pseudo-fs");
bd_mnt = kern_mount(&bd_type);
if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs");
@@ -1028,7 +1024,12 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise)
mutex_lock(&bdev->bd_holder_lock);
if (bdev->bd_holder_ops && bdev->bd_holder_ops->mark_dead)
bdev->bd_holder_ops->mark_dead(bdev, surprise);
- else
+ /*
+ * On surprise removal the device is already gone; syncing is
+ * futile and can hang forever waiting on I/O that will never
+ * complete. Match fs_bdev_mark_dead(), which also skips it.
+ */
+ else if (!surprise)
sync_blockdev(bdev);
mutex_unlock(&bdev->bd_holder_lock);
diff --git a/block/partitions/aix.c b/block/partitions/aix.c
index 85f4b967565e..cdcd60686e0b 100644
--- a/block/partitions/aix.c
+++ b/block/partitions/aix.c
@@ -228,6 +228,15 @@ int aix_partition(struct parsed_partitions *state)
int next_lp_ix = 1;
int lp_ix;
+ /*
+ * pvd was read into a fixed-size struct pvd whose ppe[] array
+ * holds ARRAY_SIZE(pvd->ppe) entries. pp_count is an
+ * unvalidated on-disk __be16, so clamp the scan to the array
+ * size to avoid walking past the allocation.
+ */
+ if (numpps > ARRAY_SIZE(pvd->ppe))
+ numpps = ARRAY_SIZE(pvd->ppe);
+
for (i = 0; i < numpps; i += 1) {
struct ppe *p = pvd->ppe + i;
unsigned int lv_ix;
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index 8785a2dba9ab..e7fea668473d 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -976,7 +976,7 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
ssize_t plen;
/* use the existing memory in an allocated page */
- if (ctx->merge && !(msg->msg_flags & MSG_SPLICE_PAGES)) {
+ if (ctx->merge) {
sgl = list_entry(ctx->tsgl_list.prev,
struct af_alg_tsgl, list);
sg = sgl->sg + sgl->cur - 1;
@@ -1020,60 +1020,37 @@ int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
if (sgl->cur)
sg_unmark_end(sg + sgl->cur - 1);
- if (msg->msg_flags & MSG_SPLICE_PAGES) {
- struct sg_table sgtable = {
- .sgl = sg,
- .nents = sgl->cur,
- .orig_nents = sgl->cur,
- };
-
- plen = extract_iter_to_sg(&msg->msg_iter, len, &sgtable,
- MAX_SGL_ENTS - sgl->cur, 0);
- if (plen < 0) {
- err = plen;
+ do {
+ struct page *pg;
+ unsigned int i = sgl->cur;
+
+ plen = min_t(size_t, len, PAGE_SIZE);
+
+ pg = alloc_page(GFP_KERNEL);
+ if (!pg) {
+ err = -ENOMEM;
goto unlock;
}
- for (; sgl->cur < sgtable.nents; sgl->cur++)
- get_page(sg_page(&sg[sgl->cur]));
+ sg_assign_page(sg + i, pg);
+
+ err = memcpy_from_msg(page_address(sg_page(sg + i)),
+ msg, plen);
+ if (err) {
+ __free_page(sg_page(sg + i));
+ sg_assign_page(sg + i, NULL);
+ goto unlock;
+ }
+
+ sg[i].length = plen;
len -= plen;
ctx->used += plen;
copied += plen;
size -= plen;
- } else {
- do {
- struct page *pg;
- unsigned int i = sgl->cur;
-
- plen = min_t(size_t, len, PAGE_SIZE);
-
- pg = alloc_page(GFP_KERNEL);
- if (!pg) {
- err = -ENOMEM;
- goto unlock;
- }
-
- sg_assign_page(sg + i, pg);
-
- err = memcpy_from_msg(
- page_address(sg_page(sg + i)),
- msg, plen);
- if (err) {
- __free_page(sg_page(sg + i));
- sg_assign_page(sg + i, NULL);
- goto unlock;
- }
-
- sg[i].length = plen;
- len -= plen;
- ctx->used += plen;
- copied += plen;
- size -= plen;
- sgl->cur++;
- } while (len && sgl->cur < MAX_SGL_ENTS);
-
- ctx->merge = plen & (PAGE_SIZE - 1);
- }
+ sgl->cur++;
+ } while (len && sgl->cur < MAX_SGL_ENTS);
+
+ ctx->merge = plen & (PAGE_SIZE - 1);
if (!size)
sg_mark_end(sg + sgl->cur - 1);
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index cb651ab58d62..c6c2ce21895d 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -9,10 +9,10 @@
* The following concept of the memory management is used:
*
* The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is
- * filled by user space with the data submitted via sendmsg (maybe with
- * MSG_SPLICE_PAGES). Filling up the TX SGL does not cause a crypto operation
- * -- the data will only be tracked by the kernel. Upon receipt of one recvmsg
- * call, the caller must provide a buffer which is tracked with the RX SGL.
+ * filled by user space with the data submitted via sendmsg. Filling up the TX
+ * SGL does not cause a crypto operation -- the data will only be tracked by the
+ * kernel. Upon receipt of one recvmsg call, the caller must provide a buffer
+ * which is tracked with the RX SGL.
*
* During the processing of the recvmsg operation, the cipher request is
* allocated and prepared. As part of the recvmsg operation, the processed
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index e31b1da58dba..b12df4544d0b 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -107,37 +107,24 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
skcipher_request_set_crypt(&areq->cra_u.skcipher_req, areq->tsgl,
areq->first_rsgl.sgl.sgt.sgl, len, ctx->iv);
- if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) {
- /* AIO operation */
- sock_hold(sk);
- areq->iocb = msg->msg_iocb;
-
- /* Remember output size that will be generated. */
- areq->outlen = len;
-
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- CRYPTO_TFM_REQ_MAY_SLEEP,
- af_alg_async_cb, areq);
- err = ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req);
-
- /* AIO operation in progress */
- if (err == -EINPROGRESS)
- return -EIOCBQUEUED;
-
- sock_put(sk);
- } else {
- /* Synchronous operation */
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- CRYPTO_TFM_REQ_MAY_SLEEP |
- CRYPTO_TFM_REQ_MAY_BACKLOG,
- crypto_req_done, &ctx->wait);
- err = crypto_wait_req(ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
- &ctx->wait);
- }
+ /*
+ * Force synchronous processing. The async (AIO) path passed the
+ * socket-wide ctx->iv into the request, which the worker
+ * dereferenced after the socket lock was dropped, letting a
+ * concurrent sendmsg(ALG_SET_IV) inject an attacker IV. Mainline
+ * removed the AIO socket path in commit fcc77d33a34c ("net: Remove
+ * support for AIO on sockets"); these stable trees lack the
+ * per-request ctx->state used by newer kernels, so the minimal safe
+ * fix is to always complete synchronously.
+ */
+ skcipher_request_set_callback(&areq->cra_u.skcipher_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &ctx->wait);
+ err = crypto_wait_req(ctx->enc ?
+ crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
+ crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
+ &ctx->wait);
free:
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index f440767bd727..e20a106e7443 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -305,6 +305,8 @@ static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen,
if (pelen > hashed_bytes) {
tmp = hashed_bytes + ctx->certs_size;
+ if (tmp <= hashed_bytes || pelen < tmp)
+ return -ELIBBAD;
ret = crypto_shash_update(desc,
pebuf + hashed_bytes,
pelen - tmp);
diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c
index a41741326998..0d516c77cc26 100644
--- a/crypto/asymmetric_keys/x509_loader.c
+++ b/crypto/asymmetric_keys/x509_loader.c
@@ -20,7 +20,7 @@ int x509_load_certificate_list(const u8 cert_list[],
*/
if (end - p < 4)
goto dodgy_cert;
- if (p[0] != 0x30 &&
+ if (p[0] != 0x30 ||
p[1] != 0x82)
goto dodgy_cert;
plen = (p[2] << 8) | p[3];
diff --git a/crypto/drbg.c b/crypto/drbg.c
index ff4ebbc68efa..211757f3f972 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -603,7 +603,7 @@ static int drbg_ctr_generate(struct drbg_state *drbg,
if (addtl && !list_empty(addtl)) {
ret = drbg_ctr_update(drbg, addtl, 2);
if (ret)
- return 0;
+ return ret;
}
/* 10.2.1.5.2 step 4.1 */
@@ -2095,7 +2095,7 @@ static inline void __init drbg_fill_array(struct rng_alg *alg,
* it is selected.
*/
if (fips_enabled)
- alg->base.cra_priority += 200;
+ alg->base.cra_priority += 2000;
alg->base.cra_ctxsize = sizeof(struct drbg_state);
alg->base.cra_module = THIS_MODULE;
diff --git a/crypto/ecc.c b/crypto/ecc.c
index 27fcecf3a61f..086fc3791920 100644
--- a/crypto/ecc.c
+++ b/crypto/ecc.c
@@ -400,14 +400,26 @@ static uint128_t mul_64_64(u64 left, u64 right)
return result;
}
-static uint128_t add_128_128(uint128_t a, uint128_t b)
+/* Calculate addition with overflow checking. Returns true on wrap-around,
+ * false otherwise.
+ */
+static bool check_add_128_128_overflow(uint128_t *result, uint128_t a,
+ uint128_t b)
{
- uint128_t result;
+ bool carry;
- result.m_low = a.m_low + b.m_low;
- result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
+ result->m_low = a.m_low + b.m_low;
+ carry = (result->m_low < a.m_low);
- return result;
+ result->m_high = a.m_high + b.m_high + carry;
+
+ /* Using constant-time bitwise arithmetic to prevent timing
+ * side-channels.
+ */
+ carry = (result->m_high < a.m_high) |
+ ((result->m_high == a.m_high) & carry);
+
+ return carry;
}
static void vli_mult(u64 *result, const u64 *left, const u64 *right,
@@ -432,9 +444,7 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right,
uint128_t product;
product = mul_64_64(left[i], right[k - i]);
-
- r01 = add_128_128(r01, product);
- r2 += (r01.m_high < product.m_high);
+ r2 += check_add_128_128_overflow(&r01, r01, product);
}
result[k] = r01.m_low;
@@ -457,7 +467,7 @@ static void vli_umult(u64 *result, const u64 *left, u32 right,
uint128_t product;
product = mul_64_64(left[k], right);
- r01 = add_128_128(r01, product);
+ check_add_128_128_overflow(&r01, r01, product);
/* no carry */
result[k] = r01.m_low;
r01.m_low = r01.m_high;
@@ -494,8 +504,7 @@ static void vli_square(u64 *result, const u64 *left, unsigned int ndigits)
product.m_low <<= 1;
}
- r01 = add_128_128(r01, product);
- r2 += (r01.m_high < product.m_high);
+ r2 += check_add_128_128_overflow(&r01, r01, product);
}
result[k] = r01.m_low;
diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c
index 3811f3805b5d..38271526d1f8 100644
--- a/crypto/ecrdsa.c
+++ b/crypto/ecrdsa.c
@@ -153,7 +153,7 @@ int ecrdsa_param_curve(void *context, size_t hdrlen, unsigned char tag,
struct ecrdsa_ctx *ctx = context;
ctx->curve_oid = look_up_OID(value, vlen);
- if (!ctx->curve_oid)
+ if (ctx->curve_oid == OID__NR)
return -EINVAL;
ctx->curve = get_curve_by_oid(ctx->curve_oid);
return 0;
diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index 71a0c74eb634..8e3b0e82473d 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -122,6 +122,8 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
return -EINPROGRESS;
if (err == -EBUSY) {
/* try non-parallel mode */
+ aead_request_set_callback(creq, flags, req->base.complete,
+ req->base.data);
return crypto_aead_encrypt(creq);
}
@@ -173,6 +175,8 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
return -EINPROGRESS;
if (err == -EBUSY) {
/* try non-parallel mode */
+ aead_request_set_callback(creq, flags, req->base.complete,
+ req->base.data);
return crypto_aead_decrypt(creq);
}
diff --git a/crypto/rng.c b/crypto/rng.c
index e462031fef68..c49115a5b266 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -259,5 +259,16 @@ void crypto_unregister_rngs(struct rng_alg *algs, int count)
}
EXPORT_SYMBOL_GPL(crypto_unregister_rngs);
+static void __exit rng_exit(void)
+{
+ int err;
+
+ err = crypto_del_default_rng();
+ if (err)
+ pr_err("Failed delete default RNG: %d\n", err);
+}
+
+module_exit(rng_exit);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Random Number Generator");
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
index 0555f68c2dfd..acecb7f86fd9 100644
--- a/drivers/acpi/acpi_ipmi.c
+++ b/drivers/acpi/acpi_ipmi.c
@@ -485,7 +485,7 @@ static void ipmi_bmc_gone(int iface)
mutex_lock(&driver_data.ipmi_lock);
list_for_each_entry_safe(iter, temp,
&driver_data.ipmi_devices, head) {
- if (iter->ipmi_ifnum != iface) {
+ if (iter->ipmi_ifnum == iface) {
ipmi_device = iter;
__ipmi_dev_kill(iter);
break;
@@ -545,7 +545,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
return AE_TYPE;
}
- acpi_ipmi_msg_get(tx_msg);
mutex_lock(&driver_data.ipmi_lock);
/* Do not add a tx_msg that can not be flushed. */
if (ipmi_device->dead) {
@@ -553,6 +552,7 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
ipmi_msg_release(tx_msg);
return AE_NOT_EXIST;
}
+ acpi_ipmi_msg_get(tx_msg);
spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags);
list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags);
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index ecba82ac7cd5..542e16c3ab9f 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -588,12 +588,16 @@ static int acpi_tad_remove(struct platform_device *pdev)
static int acpi_tad_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_tad_driver_data *dd;
+ acpi_handle handle;
acpi_status status;
unsigned long long caps;
int ret;
+ handle = ACPI_HANDLE(dev);
+ if (!handle)
+ return -ENODEV;
+
ret = acpi_install_cmos_rtc_space_handler(handle);
if (ret < 0) {
dev_info(dev, "Unable to install space handler\n");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 645464f02363..9d2301b75898 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -578,6 +578,76 @@ void acpi_dev_remove_notify_handler(struct acpi_device *adev,
}
EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler);
+struct acpi_notify_handler_devres {
+ acpi_notify_handler handler;
+ u32 handler_type;
+ struct acpi_device *adev;
+};
+
+static void devm_acpi_notify_handler_release(struct device *dev, void *res)
+{
+ struct acpi_notify_handler_devres *dr = res;
+
+ acpi_dev_remove_notify_handler(dr->adev, dr->handler_type,
+ dr->handler);
+}
+
+/**
+ * devm_acpi_install_notify_handler - Install an ACPI notify handler for a
+ * managed device
+ * @dev: Device to install a notify handler for
+ * @handler_type: Type of the notify handler
+ * @handler: Handler function to install
+ *
+ * This function performs the same function as acpi_dev_install_notify_handler()
+ * called for the ACPI companion of @dev with the same @handler_type and
+ * @handler arguments, but the ACPI notify handler installed by it will be
+ * automatically removed on driver detach.
+ *
+ * Callers should ensure that all resources used by @handler have been allocated
+ * prior to invoking this function, in which case those resources should be
+ * devres-managed so that they won't be released before the notify handler
+ * removal. Otherwise, special synchronization between @handler and the
+ * management of those resources is required.
+ *
+ * When the request fails, an error message is printed. Don't add extra error
+ * messages at the call sites.
+ *
+ * Return: 0 on success or a negative error number.
+ */
+int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type,
+ acpi_notify_handler handler)
+{
+ struct acpi_notify_handler_devres *dr;
+ struct acpi_device *adev;
+ int ret;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev && dev->bus == &acpi_bus_type)
+ adev = to_acpi_device(dev);
+
+ if (!adev)
+ return dev_err_probe(dev, -ENODEV, "No ACPI companion in %s()\n", __func__);
+
+ dr = devres_alloc(devm_acpi_notify_handler_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ ret = acpi_dev_install_notify_handler(adev, handler_type, handler);
+ if (ret) {
+ devres_free(dr);
+ return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n");
+ }
+
+ dr->handler = handler;
+ dr->handler_type = handler_type;
+ dr->adev = adev;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_acpi_install_notify_handler);
+
/* Handle events targeting \_SB device (at present only graceful shutdown) */
#define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 018a98d87bcb..33b3e6dd17c5 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -171,8 +171,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
-/* Check for valid access_width, otherwise, fallback to using bit_width */
-#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
+/*
+ * PCC reuses the access_width field as the subspace id, so only decode access
+ * size for non-PCC registers. Otherwise, use the bit_width.
+ */
+#define GET_BIT_WIDTH(reg) (((reg)->access_width && \
+ (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \
+ (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
/* Shift and apply the mask for CPC reads/writes */
#define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \
@@ -1033,7 +1038,6 @@ static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
* by the bit width field; the access size is used to indicate
* the PCC subspace id.
*/
- size = reg->bit_width;
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
}
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
@@ -1105,7 +1109,6 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
* by the bit width field; the access size is used to indicate
* the PCC subspace id.
*/
- size = reg->bit_width;
vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id);
}
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 5a1ced5bf7f6..f27571da91da 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -55,6 +55,8 @@ MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods");
LIST_HEAD(acpi_descs);
DEFINE_MUTEX(acpi_desc_lock);
+DEFINE_MUTEX(acpi_notify_lock);
+
static struct workqueue_struct *nfit_wq;
struct nfit_table_prev {
@@ -1702,9 +1704,15 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
struct acpi_device *adev = data;
struct device *dev = &adev->dev;
- device_lock(dev->parent);
- __acpi_nvdimm_notify(dev, event);
- device_unlock(dev->parent);
+ /*
+ * Locking is needed here for synchronization with driver probe and
+ * removal and the parent NFIT device's ACPI driver data pointer is
+ * NULL when teardown is in progress.
+ */
+ guard(mutex)(&acpi_notify_lock);
+
+ if (acpi_driver_data(to_acpi_device(dev->parent)))
+ __acpi_nvdimm_notify(dev, event);
}
static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
@@ -3067,6 +3075,8 @@ static void acpi_nfit_unregister(void *data)
struct acpi_nfit_desc *acpi_desc = data;
nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
+ /* The nvdimm_bus object may have been freed, so clear the pointer. */
+ acpi_desc->nvdimm_bus = NULL;
}
int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
@@ -3154,11 +3164,10 @@ EXPORT_SYMBOL_GPL(acpi_nfit_init);
static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
{
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- struct device *dev = acpi_desc->dev;
- /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
- device_lock(dev);
- device_unlock(dev);
+ /* Bounce the notify lock to flush acpi_nfit_add / acpi_nfit_notify */
+ mutex_lock(&acpi_notify_lock);
+ mutex_unlock(&acpi_notify_lock);
/* Bounce the init_mutex to complete initial registration */
mutex_lock(&acpi_desc->init_mutex);
@@ -3291,23 +3300,24 @@ static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data)
{
struct acpi_device *adev = data;
- device_lock(&adev->dev);
- __acpi_nfit_notify(&adev->dev, handle, event);
- device_unlock(&adev->dev);
-}
-
-static void acpi_nfit_remove_notify_handler(void *data)
-{
- struct acpi_device *adev = data;
+ /*
+ * Locking is needed here for synchronization with driver probe and
+ * removal and the ACPI driver data pointer is NULL when teardown
+ * is in progress.
+ */
+ guard(mutex)(&acpi_notify_lock);
- acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY,
- acpi_nfit_notify);
+ if (acpi_driver_data(adev))
+ __acpi_nfit_notify(&adev->dev, handle, event);
}
void acpi_nfit_shutdown(void *data)
{
struct acpi_nfit_desc *acpi_desc = data;
- struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
+ struct device *bus_dev;
+
+ if (!acpi_desc || !acpi_desc->nvdimm_bus)
+ return;
/*
* Destruct under acpi_desc_lock so that nfit_handle_mce does not
@@ -3322,6 +3332,7 @@ void acpi_nfit_shutdown(void *data)
mutex_unlock(&acpi_desc->init_mutex);
cancel_delayed_work_sync(&acpi_desc->dwork);
+ bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
/*
* Bounce the nvdimm bus lock to make sure any in-flight
* acpi_nfit_ars_rescan() submissions have had a chance to
@@ -3344,13 +3355,14 @@ static int acpi_nfit_add(struct acpi_device *adev)
acpi_size sz;
int rc = 0;
- rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
- acpi_nfit_notify);
- if (rc)
- return rc;
+ /*
+ * Prevent acpi_nfit_notify() from progressing until the probe is
+ * complete in case there is a concurrent event to process.
+ */
+ guard(mutex)(&acpi_notify_lock);
- rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler,
- adev);
+ rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY,
+ acpi_nfit_notify);
if (rc)
return rc;
@@ -3364,6 +3376,11 @@ static int acpi_nfit_add(struct acpi_device *adev)
* data in the format of a series of NFIT Structures.
*/
dev_dbg(dev, "failed to find NFIT at startup\n");
+ /*
+ * Let acpi_nfit_update_notify() run in case it will need to
+ * allocate the acpi_desc object.
+ */
+ adev->driver_data = dev;
return 0;
}
@@ -3398,10 +3415,26 @@ static int acpi_nfit_add(struct acpi_device *adev)
+ sizeof(struct acpi_table_nfit),
sz - sizeof(struct acpi_table_nfit));
- if (rc)
+ if (rc) {
+ acpi_nfit_shutdown(acpi_desc);
return rc;
+ }
- return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
+ /*
+ * Let notify handlers operate (the actual value of the ACPI driver
+ * data pointer does not matter here so long as it is not NULL).
+ */
+ adev->driver_data = dev;
+ return 0;
+}
+
+static void acpi_nfit_remove(struct acpi_device *adev)
+{
+ guard(mutex)(&acpi_notify_lock);
+
+ /* Make notify handlers bail out early going forward. */
+ adev->driver_data = NULL;
+ acpi_nfit_shutdown(dev_get_drvdata(&adev->dev));
}
static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
@@ -3453,6 +3486,9 @@ static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle)
{
struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
+ if (!acpi_desc)
+ return;
+
if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON)
acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
else
@@ -3485,6 +3521,7 @@ static struct acpi_driver acpi_nfit_driver = {
.ids = acpi_nfit_ids,
.ops = {
.add = acpi_nfit_add,
+ .remove = acpi_nfit_remove,
},
};
diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c
index 843f678ade0c..d423c6608f7d 100644
--- a/drivers/acpi/pfr_telemetry.c
+++ b/drivers/acpi/pfr_telemetry.c
@@ -365,10 +365,14 @@ static void pfrt_log_put_idx(void *data)
static int acpi_pfrt_log_probe(struct platform_device *pdev)
{
- acpi_handle handle = ACPI_HANDLE(&pdev->dev);
struct pfrt_log_device *pfrt_log_dev;
+ acpi_handle handle;
int ret;
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle)
+ return -ENODEV;
+
if (!acpi_has_method(handle, "_DSM")) {
dev_dbg(&pdev->dev, "Missing _DSM\n");
return -ENODEV;
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index aedf7e40145e..fb93d5401d89 100644
--- a/drivers/acpi/pfr_update.c
+++ b/drivers/acpi/pfr_update.c
@@ -507,10 +507,14 @@ static void pfru_put_idx(void *data)
static int acpi_pfru_probe(struct platform_device *pdev)
{
- acpi_handle handle = ACPI_HANDLE(&pdev->dev);
struct pfru_device *pfru_dev;
+ acpi_handle handle;
int ret;
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle)
+ return -ENODEV;
+
if (!acpi_has_method(handle, "_DSM")) {
dev_dbg(&pdev->dev, "Missing _DSM\n");
return -ENODEV;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 531684a69c64..672ccad447cc 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -839,7 +839,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt);
/**
- * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources().
+ * acpi_dev_free_resource_list - Free resource from acpi_dev_get_resources().
* @list: The head of the resource list to free.
*/
void acpi_dev_free_resource_list(struct list_head *list)
@@ -959,7 +959,7 @@ static int __acpi_dev_get_resources(struct acpi_device *adev,
*
* The resultant struct resource objects are put on the list pointed to by
* @list, that must be empty initially, as members of struct resource_entry
- * objects. Callers of this routine should use %acpi_dev_free_resource_list() to
+ * objects. Callers of this routine should use acpi_dev_free_resource_list() to
* free that list.
*
* The number of resources in the output list is returned on success, an error
@@ -1000,7 +1000,7 @@ static int is_memory(struct acpi_resource *ares, void *not_used)
* The resultant struct resource objects are put on the list pointed to
* by @list, that must be empty initially, as members of struct
* resource_entry objects. Callers of this routine should use
- * %acpi_dev_free_resource_list() to free that list.
+ * acpi_dev_free_resource_list() to free that list.
*
* The number of resources in the output list is returned on success,
* an error code reflecting the error condition is returned otherwise.
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 8263508415a8..2c656699a83c 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -926,7 +926,7 @@ static int acpi_thermal_add(struct acpi_device *device)
int result;
if (!device)
- return -EINVAL;
+ return -ENODEV;
tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
if (!tz)
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 73408a5b45b3..20014ed53604 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1567,7 +1567,20 @@ static void binder_txn_latency_free(struct binder_transaction *t)
static void binder_free_transaction(struct binder_transaction *t)
{
- struct binder_proc *target_proc = t->to_proc;
+ struct binder_thread *target_thread;
+ struct binder_proc *target_proc;
+
+ spin_lock(&t->lock);
+ target_proc = t->to_proc;
+ target_thread = t->to_thread;
+ /*
+ * Pin target_thread to keep target_proc alive. Undelivered
+ * transactions with !target_thread are safe, as target_proc
+ * can only be the current context there.
+ */
+ if (target_thread)
+ atomic_inc(&target_thread->tmp_ref);
+ spin_unlock(&t->lock);
if (target_proc) {
binder_inner_proc_lock(target_proc);
@@ -1581,6 +1594,10 @@ static void binder_free_transaction(struct binder_transaction *t)
t->buffer->transaction = NULL;
binder_inner_proc_unlock(target_proc);
}
+
+ if (target_thread)
+ binder_thread_dec_tmpref(target_thread);
+
if (trace_binder_txn_latency_free_enabled())
binder_txn_latency_free(t);
/*
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 91c6291b01f4..07dd04b8d01d 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1508,6 +1508,7 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
{
struct ata_link *link = dev->link;
struct ata_port *ap = link->ap;
+ const bool owns_eh_mutex = ap->host->eh_owner == current;
u8 command = tf->command;
int auto_timeout = 0;
struct ata_queued_cmd *qc;
@@ -1586,11 +1587,25 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
}
}
- ata_eh_release(ap);
+ if (owns_eh_mutex) {
+ /*
+ * To prevent that the compiler complains about the
+ * ata_eh_release() call below.
+ */
+ __acquire(&ap->host->eh_mutex);
+ ata_eh_release(ap);
+ }
rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout));
- ata_eh_acquire(ap);
+ if (owns_eh_mutex) {
+ ata_eh_acquire(ap);
+ /*
+ * To prevent that the compiler complains about the above
+ * ata_eh_acquire() call.
+ */
+ __release(&ap->host->eh_mutex);
+ }
ata_sff_flush_pio_task(ap);
diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c
index 821bcf20741e..de9ac851f6a5 100644
--- a/drivers/ata/pata_pxa.c
+++ b/drivers/ata/pata_pxa.c
@@ -287,6 +287,7 @@ static int pxa_ata_probe(struct platform_device *pdev)
ret = dmaengine_slave_config(data->dma_chan, &config);
if (ret < 0) {
dev_err(&pdev->dev, "dma configuration failed: %d\n", ret);
+ dma_release_channel(data->dma_chan);
return ret;
}
diff --git a/drivers/ata/sata_gemini.c b/drivers/ata/sata_gemini.c
index 4c270999ba3c..22a30c1d8886 100644
--- a/drivers/ata/sata_gemini.c
+++ b/drivers/ata/sata_gemini.c
@@ -385,7 +385,7 @@ static int gemini_sata_probe(struct platform_device *pdev)
if (sg->ide_pins) {
ret = gemini_setup_ide_pins(dev);
if (ret)
- return ret;
+ goto out_unprep_clk;
}
dev_info(dev, "set up the Gemini IDE/SATA nexus\n");
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 44f9d0a06d5b..fa27c8f1af95 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -326,12 +326,11 @@ void deferred_probe_extend_timeout(void)
* If the work hasn't been queued yet or if the work expired, don't
* start a new one.
*/
- if (cancel_delayed_work(&deferred_probe_timeout_work)) {
- schedule_delayed_work(&deferred_probe_timeout_work,
- driver_deferred_probe_timeout * HZ);
+ if (delayed_work_pending(&deferred_probe_timeout_work) &&
+ mod_delayed_work(system_wq, &deferred_probe_timeout_work,
+ secs_to_jiffies(driver_deferred_probe_timeout)))
pr_debug("Extended deferred probe timeout by %d secs\n",
driver_deferred_probe_timeout);
- }
}
/**
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index f3133ba831c5..4f4614811d09 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -1506,9 +1506,10 @@ static void device_cache_fw_images(void)
mutex_lock(&fw_lock);
fwc->state = FW_LOADER_START_CACHE;
- dpm_for_each_dev(NULL, dev_cache_fw_image);
mutex_unlock(&fw_lock);
+ dpm_for_each_dev(NULL, dev_cache_fw_image);
+
/* wait for completion of caching firmware for all devices */
async_synchronize_full_domain(&fw_cache_domain);
diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c
index a0af8f5f13d8..54a1abae3948 100644
--- a/drivers/base/firmware_loader/sysfs_upload.c
+++ b/drivers/base/firmware_loader/sysfs_upload.c
@@ -340,7 +340,6 @@ firmware_upload_register(struct module *module, struct device *parent,
goto free_fw_upload_priv;
}
fw_upload->priv = fw_sysfs;
- fw_sysfs->fw_upload_priv = fw_upload_priv;
fw_dev = &fw_sysfs->dev;
ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, NULL, 0, 0,
@@ -348,10 +347,12 @@ firmware_upload_register(struct module *module, struct device *parent,
if (ret != 0) {
if (ret > 0)
ret = -EINVAL;
- goto free_fw_sysfs;
+ put_device(fw_dev);
+ goto free_fw_upload_priv;
}
fw_priv->is_paged_buf = true;
fw_sysfs->fw_priv = fw_priv;
+ fw_sysfs->fw_upload_priv = fw_upload_priv;
ret = device_add(fw_dev);
if (ret) {
@@ -362,9 +363,6 @@ firmware_upload_register(struct module *module, struct device *parent,
return fw_upload;
-free_fw_sysfs:
- kfree(fw_sysfs);
-
free_fw_upload_priv:
kfree(fw_upload_priv);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index ef5157fc8dcc..dc3d30860ef3 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -101,7 +101,7 @@ void device_pm_sleep_init(struct device *dev)
dev->power.is_noirq_suspended = false;
dev->power.is_late_suspended = false;
init_completion(&dev->power.completion);
- complete_all(&dev->power.completion);
+ complete(&dev->power.completion);
dev->power.wakeup = NULL;
INIT_LIST_HEAD(&dev->power.entry);
}
@@ -238,6 +238,10 @@ static void dpm_wait(struct device *dev, bool async)
if (!dev)
return;
+ /* Devices with no PM support don't use the completion. */
+ if (dev->power.no_pm)
+ return;
+
if (async || (pm_async_enabled && dev->power.async_suspend))
wait_for_completion(&dev->power.completion);
}
diff --git a/drivers/base/property.c b/drivers/base/property.c
index ea93fc8ba592..f0dd0d46c04b 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -1207,8 +1207,10 @@ fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode,
if (fwnode_ep.port != port)
continue;
- if (fwnode_ep.id == endpoint)
+ if (fwnode_ep.id == endpoint) {
+ fwnode_handle_put(best_ep);
return ep;
+ }
if (!endpoint_next)
continue;
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index ac18d36b0ea8..f85ebdc3cff6 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -1998,6 +1998,11 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req
data_size -= digest_size;
}
+ if (data_size < 0) {
+ drbd_err(peer_device, "Invalid data reply size\n");
+ return -EIO;
+ }
+
/* optimistically update recv_cnt. if receiving fails below,
* we disconnect anyways, and counters will be reset. */
peer_device->device->recv_cnt += data_size>>9;
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index bc211c324206..59d3534da2c4 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -412,6 +412,7 @@ config BT_ATH3K
config BT_MTKSDIO
tristate "MediaTek HCI SDIO driver"
depends on MMC
+ depends on USB || !BT_HCIBTUSB_MTK
select BT_MTK
help
MediaTek Bluetooth HCI SDIO driver.
@@ -424,6 +425,7 @@ config BT_MTKSDIO
config BT_MTKUART
tristate "MediaTek HCI UART driver"
depends on SERIAL_DEV_BUS
+ depends on USB || !BT_HCIBTUSB_MTK
select BT_MTK
help
MediaTek Bluetooth HCI UART driver.
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 1fa58c059cbf..8942ebd0d0b9 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -254,9 +254,13 @@ static int bpa10x_setup(struct hci_dev *hdev)
if (IS_ERR(skb))
return PTR_ERR(skb);
- bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
+ /* Bounded print: the device controls skb->len. */
+ if (skb->len > 1) {
+ int len = skb->len - 1;
- hci_set_fw_info(hdev, "%s", skb->data + 1);
+ bt_dev_info(hdev, "%.*s", len, (char *)(skb->data + 1));
+ hci_set_fw_info(hdev, "%.*s", len, skb->data + 1);
+ }
kfree_skb(skb);
return 0;
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 582915f9a8d7..ab68c2cbd79e 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/usb.h>
+#include <linux/iopoll.h>
+#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -436,6 +438,7 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(btmtk_process_coredump);
+#if IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK)
static void btmtk_usb_wmt_recv(struct urb *urb)
{
struct hci_dev *hdev = urb->context;
@@ -573,8 +576,8 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev)
return err;
}
-int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
- struct btmtk_hci_wmt_params *wmt_params)
+static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
+ struct btmtk_hci_wmt_params *wmt_params)
{
struct btmtk_data *data = hci_get_priv(hdev);
struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
@@ -638,12 +641,7 @@ int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
* WMT command.
*/
err = wait_on_bit_timeout(&data->flags, BTMTK_TX_WAIT_VND_EVT,
- TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
- if (err == -EINTR) {
- bt_dev_err(hdev, "Execution of wmt command interrupted");
- clear_bit(BTMTK_TX_WAIT_VND_EVT, &data->flags);
- goto err_free_wc;
- }
+ TASK_UNINTERRUPTIBLE, HCI_INIT_TIMEOUT);
if (err) {
bt_dev_err(hdev, "Execution of wmt command timed out");
@@ -711,7 +709,461 @@ int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
kfree(wc);
return err;
}
-EXPORT_SYMBOL_GPL(btmtk_usb_hci_wmt_sync);
+
+static int btmtk_usb_func_query(struct hci_dev *hdev)
+{
+ struct btmtk_hci_wmt_params wmt_params;
+ int status, err;
+ u8 param = 0;
+
+ /* Query whether the function is enabled */
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 4;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = ¶m;
+ wmt_params.status = &status;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query function status (%d)", err);
+ return err;
+ }
+
+ return status;
+}
+
+static int btmtk_usb_uhw_reg_write(struct hci_dev *hdev, u32 reg, u32 val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ put_unaligned_le32(val, buf);
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x02,
+ 0x5E,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_SET_TIMEOUT);
+ if (err < 0)
+ bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
+
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_uhw_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err;
+ void *buf;
+
+ buf = kzalloc(4, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x01,
+ 0xDE,
+ reg >> 16, reg & 0xffff,
+ buf, 4, USB_CTRL_GET_TIMEOUT);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
+ goto err_free_buf;
+ }
+
+ *val = get_unaligned_le32(buf);
+ bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ int pipe, err, size = sizeof(u32);
+ void *buf;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ pipe = usb_rcvctrlpipe(data->udev, 0);
+ err = usb_control_msg(data->udev, pipe, 0x63,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ reg >> 16, reg & 0xffff,
+ buf, size, USB_CTRL_GET_TIMEOUT);
+ if (err < 0)
+ goto err_free_buf;
+
+ *val = get_unaligned_le32(buf);
+
+err_free_buf:
+ kfree(buf);
+
+ return err;
+}
+
+static int btmtk_usb_id_get(struct hci_dev *hdev, u32 reg, u32 *id)
+{
+ return btmtk_usb_reg_read(hdev, reg, id);
+}
+
+static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
+{
+ u32 val = 0;
+
+ btmtk_usb_uhw_reg_read(hdev, MTK_BT_MISC, &val);
+
+ return val & MTK_BT_RST_DONE;
+}
+
+int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
+{
+ u32 val;
+ int err;
+
+ if (dev_id == 0x7922) {
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ val |= 0x00002020;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, 0x00010001);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ val |= BIT(0);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, val);
+ if (err < 0)
+ return err;
+ msleep(100);
+ } else if (dev_id == 0x7925) {
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val |= (1 << 5);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val &= 0xFFFF00FF;
+ val |= (1 << 13);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, 0x00010001);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
+ if (err < 0)
+ return err;
+ val |= (1 << 0);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_RESET_REG_CONNV3, val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
+ if (err < 0)
+ return err;
+ msleep(100);
+ } else {
+ /* It's Device EndPoint Reset Option Register */
+ bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_WDT_STATUS, &val);
+ if (err < 0)
+ return err;
+ /* Reset the bluetooth chip via USB interface. */
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 1);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT, &val);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT1, 0x000000FF);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_UDMA_INT_STA_BT1, &val);
+ if (err < 0)
+ return err;
+ /* MT7921 need to delay 20ms between toggle reset bit */
+ msleep(20);
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_BT_SUBSYS_RST, 0);
+ if (err < 0)
+ return err;
+ err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_SUBSYS_RST, &val);
+ if (err < 0)
+ return err;
+ }
+
+ err = readx_poll_timeout(btmtk_usb_reset_done, hdev, val,
+ val & MTK_BT_RST_DONE, 20000, 1000000);
+ if (err < 0)
+ bt_dev_err(hdev, "Reset timeout");
+
+ if (dev_id == 0x7922) {
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_UDMA_INT_STA_BT, 0x000000FF);
+ if (err < 0)
+ return err;
+ }
+
+ err = btmtk_usb_id_get(hdev, 0x70010200, &val);
+ if (err < 0 || !val)
+ bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_subsys_reset);
+
+int btmtk_usb_setup(struct hci_dev *hdev)
+{
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ ktime_t calltime, delta, rettime;
+ struct btmtk_tci_sleep tci_sleep;
+ unsigned long long duration;
+ struct sk_buff *skb;
+ const char *fwname;
+ int err, status;
+ u32 dev_id = 0;
+ char fw_bin_name[64];
+ u32 fw_version = 0, fw_flavor = 0;
+ u8 param;
+
+ calltime = ktime_get();
+
+ err = btmtk_usb_id_get(hdev, 0x80000008, &dev_id);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get device id (%d)", err);
+ return err;
+ }
+
+ if (!dev_id || dev_id != 0x7663) {
+ err = btmtk_usb_id_get(hdev, 0x70010200, &dev_id);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get device id (%d)", err);
+ return err;
+ }
+ err = btmtk_usb_id_get(hdev, 0x80021004, &fw_version);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get fw version (%d)", err);
+ return err;
+ }
+ err = btmtk_usb_id_get(hdev, 0x70010020, &fw_flavor);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
+ return err;
+ }
+ fw_flavor = (fw_flavor & 0x00000080) >> 7;
+ }
+
+ btmtk_data->dev_id = dev_id;
+
+ err = btmtk_register_coredump(hdev, btmtk_data->drv_name, fw_version);
+ if (err < 0)
+ bt_dev_err(hdev, "Failed to register coredump (%d)", err);
+
+ switch (dev_id) {
+ case 0x7663:
+ fwname = FIRMWARE_MT7663;
+ break;
+ case 0x7668:
+ fwname = FIRMWARE_MT7668;
+ break;
+ case 0x7922:
+ case 0x7961:
+ case 0x7925:
+ /* Reset the device to ensure it's in the initial state before
+ * downloading the firmware to ensure.
+ */
+
+ if (!test_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags))
+ btmtk_usb_subsys_reset(hdev, dev_id);
+
+ btmtk_fw_get_filename(fw_bin_name, sizeof(fw_bin_name), dev_id,
+ fw_version, fw_flavor);
+
+ err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
+ btmtk_usb_hci_wmt_sync);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
+ clear_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags);
+ return err;
+ }
+
+ set_bit(BTMTK_FIRMWARE_LOADED, &btmtk_data->flags);
+
+ /* It's Device EndPoint Reset Option Register */
+ err = btmtk_usb_uhw_reg_write(hdev, MTK_EP_RST_OPT,
+ MTK_EP_RST_IN_OUT_OPT);
+ if (err < 0)
+ return err;
+
+ /* Enable Bluetooth protocol */
+ param = 1;
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = ¶m;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ return err;
+ }
+
+ hci_set_msft_opcode(hdev, 0xFD30);
+ hci_set_aosp_capable(hdev);
+
+ goto done;
+ default:
+ bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
+ dev_id);
+ return -ENODEV;
+ }
+
+ /* Query whether the firmware is already download */
+ wmt_params.op = BTMTK_WMT_SEMAPHORE;
+ wmt_params.flag = 1;
+ wmt_params.dlen = 0;
+ wmt_params.data = NULL;
+ wmt_params.status = &status;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
+ return err;
+ }
+
+ if (status == BTMTK_WMT_PATCH_DONE) {
+ bt_dev_info(hdev, "firmware already downloaded");
+ goto ignore_setup_fw;
+ }
+
+ /* Setup a firmware which the device definitely requires */
+ err = btmtk_setup_firmware(hdev, fwname,
+ btmtk_usb_hci_wmt_sync);
+ if (err < 0)
+ return err;
+
+ignore_setup_fw:
+ err = readx_poll_timeout(btmtk_usb_func_query, hdev, status,
+ status < 0 || status != BTMTK_WMT_ON_PROGRESS,
+ 2000, 5000000);
+ /* -ETIMEDOUT happens */
+ if (err < 0)
+ return err;
+
+ /* The other errors happen in btmtk_usb_func_query */
+ if (status < 0)
+ return status;
+
+ if (status == BTMTK_WMT_ON_DONE) {
+ bt_dev_info(hdev, "function already on");
+ goto ignore_func_on;
+ }
+
+ /* Enable Bluetooth protocol */
+ param = 1;
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = ¶m;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ return err;
+ }
+
+ignore_func_on:
+ /* Apply the low power environment setup */
+ tci_sleep.mode = 0x5;
+ tci_sleep.duration = cpu_to_le16(0x640);
+ tci_sleep.host_duration = cpu_to_le16(0x640);
+ tci_sleep.host_wakeup_pin = 0;
+ tci_sleep.time_compensation = 0;
+
+ skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
+ return err;
+ }
+ kfree_skb(skb);
+
+done:
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+
+ bt_dev_info(hdev, "Device setup in %llu usecs", duration);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_setup);
+
+int btmtk_usb_shutdown(struct hci_dev *hdev)
+{
+ struct btmtk_data *data = hci_get_priv(hdev);
+ struct btmtk_hci_wmt_params wmt_params;
+ u8 param = 0;
+ int err;
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return err;
+
+ /* Disable the device */
+ wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+ wmt_params.flag = 0;
+ wmt_params.dlen = sizeof(param);
+ wmt_params.data = ¶m;
+ wmt_params.status = NULL;
+
+ err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
+ if (err < 0) {
+ bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+ usb_autopm_put_interface(data->intf);
+ return err;
+ }
+
+ usb_autopm_put_interface(data->intf);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_usb_shutdown);
+#endif
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index 3055b9728ae2..fe41528ae509 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -140,6 +140,8 @@ struct btmtk_hci_wmt_params {
enum {
BTMTK_TX_WAIT_VND_EVT,
+ BTMTK_FIRMWARE_LOADED,
+ BTMTK_HW_RESET_ACTIVE,
};
typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
@@ -152,6 +154,7 @@ struct btmtk_coredump_info {
};
struct btmtk_data {
+ const char *drv_name;
unsigned long flags;
u32 dev_id;
btmtk_reset_sync_func_t reset_sync;
@@ -186,8 +189,11 @@ int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
u32 fw_flavor);
-int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
- struct btmtk_hci_wmt_params *wmt_params);
+int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id);
+
+int btmtk_usb_setup(struct hci_dev *hdev);
+
+int btmtk_usb_shutdown(struct hci_dev *hdev);
#else
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
@@ -228,8 +234,17 @@ static void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id,
{
}
-static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
- struct btmtk_hci_wmt_params *wmt_params)
+static int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
+{
+ return -EOPNOTSUPP;
+}
+
+static int btmtk_usb_setup(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static int btmtk_usb_shutdown(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index e249aa758783..019cf85d3c07 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -606,7 +606,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
bdev->hdev->stat.err_rx++;
}
- } while (int_status || time_is_before_jiffies(txrx_timeout));
+ } while (int_status && time_is_after_jiffies(txrx_timeout));
/* Enable interrupt */
sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, 0);
@@ -1111,6 +1111,9 @@ static int btmtksdio_setup(struct hci_dev *hdev)
return err;
}
+ btmtk_fw_get_filename(fwname, sizeof(fwname), dev_id,
+ fw_version, 0);
+
snprintf(fwname, sizeof(fwname),
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
dev_id & 0xffff, (fw_version & 0xff) + 1);
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index f0d0c5a6d512..2d3aabf5f308 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -1030,6 +1030,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
}
nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
+ if (nxpdev->fw_dnld_v3_offset >= nxpdev->fw->size ||
+ len > nxpdev->fw->size - nxpdev->fw_dnld_v3_offset) {
+ bt_dev_err(hdev, "FW download out of bounds, ignoring request");
+ len = 0;
+ goto free_skb;
+ }
serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
nxpdev->fw_dnld_v3_offset, len);
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 3d995790a507..49fb6c0b2209 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -763,8 +763,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
- min_size = patch_offset + patch_length;
- if (btrtl_dev->fw_len < min_size)
+ if (patch_length < sizeof(epatch_info->fw_version) ||
+ patch_offset > btrtl_dev->fw_len ||
+ patch_length > btrtl_dev->fw_len - patch_offset)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 9b2857a8e7ac..c3eb668c3994 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -518,6 +518,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x7392, 0xe611), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2c4e, 0x0128), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek 8852AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x2852), .driver_info = BTUSB_REALTEK |
@@ -2686,178 +2688,6 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
-static int btusb_mtk_func_query(struct hci_dev *hdev)
-{
- struct btmtk_hci_wmt_params wmt_params;
- int status, err;
- u8 param = 0;
-
- /* Query whether the function is enabled */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 4;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = ¶m;
- wmt_params.status = &status;
-
- err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to query function status (%d)", err);
- return err;
- }
-
- return status;
-}
-
-static int btusb_mtk_uhw_reg_write(struct btusb_data *data, u32 reg, u32 val)
-{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
-
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- put_unaligned_le32(val, buf);
-
- pipe = usb_sndctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x02,
- 0x5E,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_SET_TIMEOUT);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
- goto err_free_buf;
- }
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
-
-static int btusb_mtk_uhw_reg_read(struct btusb_data *data, u32 reg, u32 *val)
-{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
-
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x01,
- 0xDE,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_SET_TIMEOUT);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
- goto err_free_buf;
- }
-
- *val = get_unaligned_le32(buf);
- bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
-
-static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
-{
- int pipe, err, size = sizeof(u32);
- void *buf;
-
- buf = kzalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x63,
- USB_TYPE_VENDOR | USB_DIR_IN,
- reg >> 16, reg & 0xffff,
- buf, size, USB_CTRL_SET_TIMEOUT);
- if (err < 0)
- goto err_free_buf;
-
- *val = get_unaligned_le32(buf);
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
-
-static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
-{
- return btusb_mtk_reg_read(data, reg, id);
-}
-
-static u32 btusb_mtk_reset_done(struct hci_dev *hdev)
-{
- struct btusb_data *data = hci_get_drvdata(hdev);
- u32 val = 0;
-
- btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
-
- return val & MTK_BT_RST_DONE;
-}
-
-static int btusb_mtk_subsys_reset(struct hci_dev *hdev, u32 dev_id)
-{
- struct btusb_data *data = hci_get_drvdata(hdev);
- u32 val;
- int err;
-
- if (dev_id == 0x7925) {
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val |= (1 << 5);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val &= 0xFFFF00FF;
- val |= (1 << 13);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, 0x00010001);
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val |= (1 << 0);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
- msleep(100);
- } else {
- /* It's Device EndPoint Reset Option Register */
- bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
- btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
-
- /* Reset the bluetooth chip via USB interface. */
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
- /* MT7921 need to delay 20ms between toggle reset bit */
- msleep(20);
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
- btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
- }
-
- err = readx_poll_timeout(btusb_mtk_reset_done, hdev, val,
- val & MTK_BT_RST_DONE, 20000, 1000000);
- if (err < 0)
- bt_dev_err(hdev, "Reset timeout");
-
- btusb_mtk_id_get(data, 0x70010200, &val);
- if (!val)
- bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
-
- return err;
-}
-
static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
{
struct btusb_data *data = hci_get_drvdata(hdev);
@@ -2865,7 +2695,7 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
int err;
/* It's MediaTek specific bluetooth reset mechanism via USB */
- if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
+ if (test_and_set_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags)) {
bt_dev_err(hdev, "last reset failed? Not resetting again");
return -EBUSY;
}
@@ -2877,10 +2707,10 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
- err = btusb_mtk_subsys_reset(hdev, btmtk_data->dev_id);
+ err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
usb_queue_reset_device(data->intf);
- clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
+ clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
return err;
}
@@ -2888,202 +2718,23 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
static int btusb_mtk_setup(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmtk_hci_wmt_params wmt_params;
- ktime_t calltime, delta, rettime;
- struct btmtk_tci_sleep tci_sleep;
- unsigned long long duration;
- struct sk_buff *skb;
- const char *fwname;
- int err, status;
- u32 dev_id = 0;
- char fw_bin_name[64];
- u32 fw_version = 0;
- u8 param;
- struct btmtk_data *mediatek;
-
- calltime = ktime_get();
-
- err = btusb_mtk_id_get(data, 0x80000008, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
-
- if (!dev_id || dev_id != 0x7663) {
- err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
- err = btusb_mtk_id_get(data, 0x80021004, &fw_version);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get fw version (%d)", err);
- return err;
- }
- }
-
- mediatek = hci_get_priv(hdev);
- mediatek->dev_id = dev_id;
- mediatek->reset_sync = btusb_mtk_reset;
-
- err = btmtk_register_coredump(hdev, btusb_driver.name, fw_version);
- if (err < 0)
- bt_dev_err(hdev, "Failed to register coredump (%d)", err);
-
- switch (dev_id) {
- case 0x7663:
- fwname = FIRMWARE_MT7663;
- break;
- case 0x7668:
- fwname = FIRMWARE_MT7668;
- break;
- case 0x7922:
- case 0x7961:
- case 0x7925:
- if (dev_id == 0x7925)
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
- dev_id & 0xffff, dev_id & 0xffff, (fw_version & 0xff) + 1);
- else
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
- dev_id & 0xffff, (fw_version & 0xff) + 1);
-
- err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
- btmtk_usb_hci_wmt_sync);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
- return err;
- }
-
- /* It's Device EndPoint Reset Option Register */
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = ¶m;
- wmt_params.status = NULL;
-
- err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
-
- hci_set_msft_opcode(hdev, 0xFD30);
- hci_set_aosp_capable(hdev);
- goto done;
- default:
- bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
- dev_id);
- return -ENODEV;
- }
-
- /* Query whether the firmware is already download */
- wmt_params.op = BTMTK_WMT_SEMAPHORE;
- wmt_params.flag = 1;
- wmt_params.dlen = 0;
- wmt_params.data = NULL;
- wmt_params.status = &status;
-
- err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
- return err;
- }
-
- if (status == BTMTK_WMT_PATCH_DONE) {
- bt_dev_info(hdev, "firmware already downloaded");
- goto ignore_setup_fw;
- }
-
- /* Setup a firmware which the device definitely requires */
- err = btmtk_setup_firmware(hdev, fwname,
- btmtk_usb_hci_wmt_sync);
- if (err < 0)
- return err;
-
-ignore_setup_fw:
- err = readx_poll_timeout(btusb_mtk_func_query, hdev, status,
- status < 0 || status != BTMTK_WMT_ON_PROGRESS,
- 2000, 5000000);
- /* -ETIMEDOUT happens */
- if (err < 0)
- return err;
-
- /* The other errors happen in btusb_mtk_func_query */
- if (status < 0)
- return status;
-
- if (status == BTMTK_WMT_ON_DONE) {
- bt_dev_info(hdev, "function already on");
- goto ignore_func_on;
- }
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = ¶m;
- wmt_params.status = NULL;
-
- err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
-
-ignore_func_on:
- /* Apply the low power environment setup */
- tci_sleep.mode = 0x5;
- tci_sleep.duration = cpu_to_le16(0x640);
- tci_sleep.host_duration = cpu_to_le16(0x640);
- tci_sleep.host_wakeup_pin = 0;
- tci_sleep.time_compensation = 0;
-
- skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
- return err;
- }
- kfree_skb(skb);
-
-done:
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
- bt_dev_info(hdev, "Device setup in %llu usecs", duration);
+ /* MediaTek WMT vendor cmd requiring below USB resources to
+ * complete the handshake.
+ */
+ btmtk_data->drv_name = btusb_driver.name;
+ btmtk_data->intf = data->intf;
+ btmtk_data->udev = data->udev;
+ btmtk_data->ctrl_anchor = &data->ctrl_anchor;
+ btmtk_data->reset_sync = btusb_mtk_reset;
- return 0;
+ return btmtk_usb_setup(hdev);
}
static int btusb_mtk_shutdown(struct hci_dev *hdev)
{
- struct btmtk_hci_wmt_params wmt_params;
- u8 param = 0;
- int err;
-
- /* Disable the device */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = ¶m;
- wmt_params.status = NULL;
-
- err = btmtk_usb_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
-
- return 0;
+ return btmtk_usb_shutdown(hdev);
}
static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
@@ -3160,6 +2811,11 @@ static int marvell_config_oob_wake(struct hci_dev *hdev)
return 0;
}
+#else
+static inline int marvell_config_oob_wake(struct hci_dev *hdev)
+{
+ return 0;
+}
#endif
static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
@@ -3974,6 +3630,11 @@ static int btusb_config_oob_wake(struct hci_dev *hdev)
bt_dev_info(hdev, "OOB Wake-on-BT configured at IRQ %u", irq);
return 0;
}
+#else
+static inline int btusb_config_oob_wake(struct hci_dev *hdev)
+{
+ return 0;
+}
#endif
static void btusb_check_needs_reset_resume(struct usb_interface *intf)
@@ -4197,7 +3858,6 @@ static int btusb_probe(struct usb_interface *intf,
hdev->notify = btusb_notify;
hdev->wakeup = btusb_wakeup;
-#ifdef CONFIG_PM
err = btusb_config_oob_wake(hdev);
if (err)
goto out_free_dev;
@@ -4206,9 +3866,9 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_MARVELL && data->oob_wake_irq) {
err = marvell_config_oob_wake(hdev);
if (err)
- goto out_free_dev;
+ goto err_disable_wakeup;
}
-#endif
+
if (id->driver_info & BTUSB_CW6622)
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
@@ -4243,7 +3903,7 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_INTEL_COMBINED) {
err = btintel_configure_setup(hdev, btusb_driver.name);
if (err)
- goto out_free_dev;
+ goto err_kill_tx_urbs;
/* Transport specific configuration */
hdev->send = btusb_send_frame_intel;
@@ -4401,7 +4061,7 @@ static int btusb_probe(struct usb_interface *intf,
err = usb_set_interface(data->udev, 0, 0);
if (err < 0) {
BT_ERR("failed to set interface 0, alt 0 %d", err);
- goto out_free_dev;
+ goto err_kill_tx_urbs;
}
}
@@ -4409,7 +4069,7 @@ static int btusb_probe(struct usb_interface *intf,
err = usb_driver_claim_interface(&btusb_driver,
data->isoc, data);
if (err < 0)
- goto out_free_dev;
+ goto err_kill_tx_urbs;
}
if (IS_ENABLED(CONFIG_BT_HCIBTUSB_BCM) && data->diag) {
@@ -4427,7 +4087,7 @@ static int btusb_probe(struct usb_interface *intf,
err = hci_register_dev(hdev);
if (err < 0)
- goto out_free_dev;
+ goto err_release_siblings;
usb_set_intfdata(intf, data);
@@ -4436,6 +4096,20 @@ static int btusb_probe(struct usb_interface *intf,
return 0;
+err_release_siblings:
+ if (data->diag) {
+ usb_set_intfdata(data->diag, NULL);
+ usb_driver_release_interface(&btusb_driver, data->diag);
+ }
+ if (data->isoc) {
+ usb_set_intfdata(data->isoc, NULL);
+ usb_driver_release_interface(&btusb_driver, data->isoc);
+ }
+err_kill_tx_urbs:
+ usb_kill_anchored_urbs(&data->tx_anchor);
+err_disable_wakeup:
+ if (data->oob_wake_irq)
+ device_init_wakeup(&data->udev->dev, false);
out_free_dev:
if (data->reset_gpio)
gpiod_put(data->reset_gpio);
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index ac737ee14361..2986b18655a1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1008,7 +1008,7 @@ static void qca_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
skb_put_data(skb, buf, strlen(buf));
snprintf(buf, sizeof(buf), "Driver: %s\n",
- hu->serdev->dev.driver->name);
+ hu->serdev ? hu->serdev->dev.driver->name : "hci_ldisc_qca");
skb_put_data(skb, buf, strlen(buf));
}
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 093504505169..c0588fe110a2 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -337,7 +337,17 @@ static ssize_t force_devcd_write(struct file *file, const char __user *user_buf,
if (copy_from_user(&dump_data, user_buf, count))
return -EFAULT;
+ switch (dump_data.state) {
+ case HCI_DEVCOREDUMP_DONE:
+ case HCI_DEVCOREDUMP_ABORT:
+ case HCI_DEVCOREDUMP_TIMEOUT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
data_size = count - offsetof(struct devcoredump_test_data, data);
+
skb = alloc_skb(data_size, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 5244013d328f..378734724a4f 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -330,12 +330,6 @@ static int of_weim_notify(struct notifier_block *nb, unsigned long action,
"Failed to setup timing for '%pOF'\n", rd->dn);
if (!of_node_check_flag(rd->dn, OF_POPULATED)) {
- /*
- * Clear the flag before adding the device so that
- * fw_devlink doesn't skip adding consumers to this
- * device.
- */
- fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE);
if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) {
dev_err(&pdev->dev,
"Failed to create child device '%pOF'\n",
diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 558cfb3a06c5..03a4be194e90 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -216,7 +216,9 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele
ret = mhi_ep_create_device(mhi_cntrl, ch_id);
if (ret) {
dev_err(dev, "Error creating device for channel (%u)\n", ch_id);
+ mutex_lock(&mhi_cntrl->state_lock);
mhi_ep_handle_syserr(mhi_cntrl);
+ mutex_unlock(&mhi_cntrl->state_lock);
return ret;
}
}
@@ -1071,11 +1073,12 @@ static void mhi_ep_reset_worker(struct work_struct *work)
mhi_ep_power_down(mhi_cntrl);
- mutex_lock(&mhi_cntrl->state_lock);
-
/* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */
mhi_ep_mmio_reset(mhi_cntrl);
+
+ mutex_lock(&mhi_cntrl->state_lock);
cur_state = mhi_cntrl->mhi_state;
+ mutex_unlock(&mhi_cntrl->state_lock);
/*
* Only proceed further if the reset is due to SYS_ERR. The host will
@@ -1084,8 +1087,6 @@ static void mhi_ep_reset_worker(struct work_struct *work)
*/
if (cur_state == MHI_STATE_SYS_ERR)
mhi_ep_power_up(mhi_cntrl);
-
- mutex_unlock(&mhi_cntrl->state_lock);
}
/*
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index db0ed4e5d315..c2b2e56331d1 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -447,6 +447,9 @@ static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg,
struct sunxi_rsb_ctx *ctx = context;
struct sunxi_rsb_device *rdev = ctx->rdev;
+ if (reg > 0xff)
+ return -EINVAL;
+
return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size);
}
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index b1f94e3c0c6a..eb4a28392c37 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -256,19 +256,22 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
if (wait) {
ret = starfive_trng_wait_idle(trng);
- if (ret)
- return -ETIMEDOUT;
+ if (ret) {
+ ret = -ETIMEDOUT;
+ goto out_put;
+ }
}
ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);
if (ret)
- return ret;
+ goto out_put;
memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);
+ ret = max;
+out_put:
pm_runtime_put_sync_autosuspend(trng->dev);
-
- return max;
+ return ret;
}
static int starfive_trng_probe(struct platform_device *pdev)
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index e41a84e6b4b5..43e84983c428 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -7,6 +7,7 @@
#include <asm/barrier.h>
#include <linux/err.h>
#include <linux/hw_random.h>
+#include <linux/nospec.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/virtio.h>
@@ -66,8 +67,26 @@ static void request_entropy(struct virtrng_info *vi)
static unsigned int copy_data(struct virtrng_info *vi, void *buf,
unsigned int size)
{
- size = min_t(unsigned int, size, vi->data_avail);
- memcpy(buf, vi->data + vi->data_idx, size);
+ unsigned int idx, avail;
+
+ /*
+ * vi->data_avail was set from the device-reported used.len and
+ * vi->data_idx was advanced by previous copy_data() calls. A
+ * malicious or buggy virtio-rng backend can drive either past
+ * sizeof(vi->data). Clamp at point of use and harden the index
+ * with array_index_nospec() so the memcpy() below cannot be
+ * steered into adjacent slab memory, including under
+ * speculation.
+ */
+ avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data));
+ if (vi->data_idx >= avail) {
+ vi->data_avail = 0;
+ request_entropy(vi);
+ return 0;
+ }
+ size = min_t(unsigned int, size, avail - vi->data_idx);
+ idx = array_index_nospec(vi->data_idx, sizeof(vi->data));
+ memcpy(buf, vi->data + idx, size);
vi->data_idx += size;
vi->data_avail -= size;
if (vi->data_avail == 0)
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index 896a3550fba9..6b993744f2d7 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -263,6 +263,7 @@ static ssize_t tlclk_read(struct file *filp, char __user *buf, size_t count,
}
static const struct file_operations tlclk_fops = {
+ .owner = THIS_MODULE,
.read = tlclk_read,
.open = tlclk_open,
.release = tlclk_release,
@@ -852,6 +853,9 @@ static void __exit tlclk_cleanup(void)
misc_deregister(&tlclk_miscdev);
unregister_chrdev(tlclk_major, "telco_clock");
+ got_event = 1;
+ wake_up_all(&wq);
+
release_region(TLCLK_BASE, 8);
del_timer_sync(&switchover_timer);
kfree(alarm_events);
diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
index 12ee42a31c71..697b89af386b 100644
--- a/drivers/char/tpm/eventlog/tpm1.c
+++ b/drivers/char/tpm/eventlog/tpm1.c
@@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v)
temp_ptr = (char *) &temp_event;
- for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++)
+ for (i = 0; i < sizeof(struct tcpa_event); i++)
seq_putc(m, temp_ptr[i]);
temp_ptr = (char *) v;
- for (i = (sizeof(struct tcpa_event) - 1);
+ for (i = sizeof(struct tcpa_event);
i < (sizeof(struct tcpa_event) + temp_event.event_size); i++)
seq_putc(m, temp_ptr[i]);
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index e2c0baa69fef..7d2d07535881 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -36,7 +36,7 @@ static int tpm_open(struct inode *inode, struct file *file)
tpm_common_open(file, chip, priv, NULL);
- return 0;
+ return nonseekable_open(inode, file);
out:
clear_bit(0, &chip->is_open);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index aaeace9f4a4d..1000051f53bb 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -66,8 +66,8 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
bool check_cancel)
{
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
unsigned long stop;
- long rc;
u8 status;
bool canceled = false;
u8 sts_mask;
@@ -87,23 +87,30 @@ static int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
/* process status changes with irq support */
if (sts_mask) {
ret = -ETIME;
+ add_wait_queue(queue, &wait);
again:
+ if (wait_for_tpm_stat_cond(chip, sts_mask, check_cancel,
+ &canceled)) {
+ ret = canceled ? -ECANCELED : 0;
+ goto out;
+ }
+
timeout = stop - jiffies;
if ((long)timeout <= 0)
- return -ETIME;
- rc = wait_event_interruptible_timeout(*queue,
- wait_for_tpm_stat_cond(chip, sts_mask, check_cancel,
- &canceled),
- timeout);
- if (rc > 0) {
- if (canceled)
- return -ECANCELED;
- ret = 0;
- }
- if (rc == -ERESTARTSYS && freezing(current)) {
- clear_thread_flag(TIF_SIGPENDING);
- goto again;
+ goto out;
+
+ if (signal_pending(current)) {
+ if (freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
+ goto out;
}
+
+ wait_woken(&wait, TASK_INTERRUPTIBLE, timeout);
+ goto again;
+out:
+ remove_wait_queue(queue, &wait);
}
if (ret)
diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
index eef0fb06ea83..63f48ec1e481 100644
--- a/drivers/char/tpm/tpmrm-dev.c
+++ b/drivers/char/tpm/tpmrm-dev.c
@@ -29,7 +29,7 @@ static int tpmrm_open(struct inode *inode, struct file *file)
tpm_common_open(file, chip, &priv->priv, &priv->space);
- return 0;
+ return nonseekable_open(inode, file);
}
static int tpmrm_release(struct inode *inode, struct file *file)
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 5aa9c1f1c886..40bdc1c80ab0 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -176,9 +176,9 @@ static int __init pmc_register_ops(void)
of_node_put(np);
return -ENODEV;
}
- of_node_put(np);
at91_pmc_backup_suspend = of_iomap(np, 0);
+ of_node_put(np);
if (!at91_pmc_backup_suspend) {
pr_warn("%s(): unable to map securam\n", __func__);
return -ENOMEM;
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index fdec715c9ba9..096b06916619 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -9,9 +9,9 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
-#include <asm/div64.h>
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
@@ -61,7 +61,7 @@ static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
ftmp = rate - fmin;
ftmp += clk->info->range.step_size - 1; /* to round up */
- do_div(ftmp, clk->info->range.step_size);
+ ftmp = div64_ul(ftmp, clk->info->range.step_size);
return ftmp * clk->info->range.step_size + fmin;
}
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index 108b697bd317..5ab46a537b5d 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -256,7 +256,7 @@ static void scpi_clocks_remove(struct platform_device *pdev)
}
for_each_available_child_of_node(np, child)
- of_clk_del_provider(np);
+ of_clk_del_provider(child);
}
static int scpi_clocks_probe(struct platform_device *pdev)
diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c
index f9c5e296dba2..c878d4001da5 100644
--- a/drivers/clk/qcom/a53-pll.c
+++ b/drivers/clk/qcom/a53-pll.c
@@ -20,7 +20,7 @@
static const struct pll_freq_tbl a53pll_freq[] = {
{ 998400000, 52, 0x0, 0x1, 0 },
{ 1094400000, 57, 0x0, 0x1, 0 },
- { 1152000000, 62, 0x0, 0x1, 0 },
+ { 1152000000, 60, 0x0, 0x1, 0 },
{ 1209600000, 63, 0x0, 0x1, 0 },
{ 1248000000, 65, 0x0, 0x1, 0 },
{ 1363200000, 71, 0x0, 0x1, 0 },
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index 69fee3540d37..e9efe03dc126 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -286,6 +286,9 @@ static int sun5i_timer_probe(struct platform_device *pdev)
}
rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "failed to get reset\n");
if (rstc)
reset_control_deassert(rstc);
diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c
index 304537dadf2c..a4bff4b6d975 100644
--- a/drivers/clocksource/timer-tegra186.c
+++ b/drivers/clocksource/timer-tegra186.c
@@ -144,7 +144,8 @@ static void tegra186_wdt_enable(struct tegra186_wdt *wdt)
u32 value;
/* unmask hardware IRQ, this may have been lost across powergate */
- value = TKEIE_WDT_MASK(wdt->index, 1);
+ value = readl(tegra->regs + TKEIE(wdt->tmr->hwirq));
+ value |= TKEIE_WDT_MASK(wdt->index, 1);
writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq));
/* clear interrupt */
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 76b0b9e6309b..fdeaf6f54c33 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1970,6 +1970,7 @@ void cpufreq_suspend(void)
if (!cpufreq_driver)
return;
+ cpus_read_lock();
if (!has_target() && !cpufreq_driver->suspend)
goto suspend;
@@ -1989,6 +1990,7 @@ void cpufreq_suspend(void)
suspend:
cpufreq_suspended = true;
+ cpus_read_unlock();
}
/**
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index efdcc4ab4334..24fbb7313c95 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -103,10 +103,6 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy)
if (load > dbs_data->up_threshold) {
dbs_info->down_skip = 0;
- /* if we are already at full speed then break out early */
- if (requested_freq == policy->max)
- goto out;
-
requested_freq += freq_step;
if (requested_freq > policy->max)
requested_freq = policy->max;
@@ -124,13 +120,7 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy)
/* Check for frequency decrease */
if (load < cs_tuners->down_threshold) {
- /*
- * if we cannot reduce the frequency anymore, break out early
- */
- if (requested_freq == policy->min)
- goto out;
-
- if (requested_freq > freq_step)
+ if (requested_freq > policy->min + freq_step)
requested_freq -= freq_step;
else
requested_freq = policy->min;
diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c
index c6bdc455517f..f692729068f5 100644
--- a/drivers/cpufreq/ia64-acpi-cpufreq.c
+++ b/drivers/cpufreq/ia64-acpi-cpufreq.c
@@ -302,7 +302,7 @@ acpi_cpufreq_cpu_init (
}
-static int
+static void
acpi_cpufreq_cpu_exit (
struct cpufreq_policy *policy)
{
@@ -316,8 +316,6 @@ acpi_cpufreq_cpu_exit (
kfree(policy->freq_table);
kfree(data);
}
-
- return (0);
}
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 60b4e3b608c0..048e2579e4fc 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -2666,10 +2666,12 @@ static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy)
* from getting to lower performance levels, so force the minimum
* performance on CPU offline to prevent that from happening.
*/
- if (hwp_active)
+ if (hwp_active) {
intel_pstate_hwp_offline(cpu);
- else
+ } else {
intel_pstate_set_min_pstate(cpu);
+ policy->cur = cpu->pstate.min_freq;
+ }
intel_pstate_exit_perf_limits(policy);
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index 6f8b5ea7aeae..335429d0683b 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acpi_handle *handle)
}
kfree(output.pointer);
+ output.pointer = NULL;
+ output.length = ACPI_ALLOCATE_BUFFER;
capabilities[0] = 0x0;
capabilities[1] = 0x1;
diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig
index b8e75210a0e3..06ea0e9fe6f2 100644
--- a/drivers/crypto/allwinner/Kconfig
+++ b/drivers/crypto/allwinner/Kconfig
@@ -24,14 +24,6 @@ config CRYPTO_DEV_SUN4I_SS
To compile this driver as a module, choose M here: the module
will be called sun4i-ss.
-config CRYPTO_DEV_SUN4I_SS_PRNG
- bool "Support for Allwinner Security System PRNG"
- depends on CRYPTO_DEV_SUN4I_SS
- select CRYPTO_RNG
- help
- Select this option if you want to provide kernel-side support for
- the Pseudo-Random Number Generator found in the Security System.
-
config CRYPTO_DEV_SUN4I_SS_DEBUG
bool "Enable sun4i-ss stats"
depends on CRYPTO_DEV_SUN4I_SS
diff --git a/drivers/crypto/allwinner/sun4i-ss/Makefile b/drivers/crypto/allwinner/sun4i-ss/Makefile
index c0a2797d3168..06a9ae81f9f8 100644
--- a/drivers/crypto/allwinner/sun4i-ss/Makefile
+++ b/drivers/crypto/allwinner/sun4i-ss/Makefile
@@ -1,4 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sun4i-ss.o
sun4i-ss-y += sun4i-ss-core.o sun4i-ss-hash.o sun4i-ss-cipher.o
-sun4i-ss-$(CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG) += sun4i-ss-prng.o
diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c
index 3bcfcfc37084..6678fab422c3 100644
--- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c
+++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-core.c
@@ -215,23 +215,6 @@ static struct sun4i_ss_alg_template ss_algs[] = {
}
}
},
-#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG
-{
- .type = CRYPTO_ALG_TYPE_RNG,
- .alg.rng = {
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "sun4i_ss_rng",
- .cra_priority = 300,
- .cra_ctxsize = 0,
- .cra_module = THIS_MODULE,
- },
- .generate = sun4i_ss_prng_generate,
- .seed = sun4i_ss_prng_seed,
- .seedsize = SS_SEED_LEN / BITS_PER_BYTE,
- }
-},
-#endif
};
static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v)
@@ -249,12 +232,6 @@ static int sun4i_ss_debugfs_show(struct seq_file *seq, void *v)
ss_algs[i].stat_req, ss_algs[i].stat_opti, ss_algs[i].stat_fb,
ss_algs[i].stat_bytes);
break;
- case CRYPTO_ALG_TYPE_RNG:
- seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n",
- ss_algs[i].alg.rng.base.cra_driver_name,
- ss_algs[i].alg.rng.base.cra_name,
- ss_algs[i].stat_req, ss_algs[i].stat_bytes);
- break;
case CRYPTO_ALG_TYPE_AHASH:
seq_printf(seq, "%s %s reqs=%lu\n",
ss_algs[i].alg.hash.halg.base.cra_driver_name,
@@ -473,13 +450,6 @@ static int sun4i_ss_probe(struct platform_device *pdev)
goto error_alg;
}
break;
- case CRYPTO_ALG_TYPE_RNG:
- err = crypto_register_rng(&ss_algs[i].alg.rng);
- if (err) {
- dev_err(ss->dev, "Fail to register %s\n",
- ss_algs[i].alg.rng.base.cra_name);
- }
- break;
}
}
@@ -499,9 +469,6 @@ static int sun4i_ss_probe(struct platform_device *pdev)
case CRYPTO_ALG_TYPE_AHASH:
crypto_unregister_ahash(&ss_algs[i].alg.hash);
break;
- case CRYPTO_ALG_TYPE_RNG:
- crypto_unregister_rng(&ss_algs[i].alg.rng);
- break;
}
}
error_pm:
@@ -522,9 +489,6 @@ static int sun4i_ss_remove(struct platform_device *pdev)
case CRYPTO_ALG_TYPE_AHASH:
crypto_unregister_ahash(&ss_algs[i].alg.hash);
break;
- case CRYPTO_ALG_TYPE_RNG:
- crypto_unregister_rng(&ss_algs[i].alg.rng);
- break;
}
}
diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c
deleted file mode 100644
index 491fcb7b81b4..000000000000
--- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss-prng.c
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-#include "sun4i-ss.h"
-
-int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- struct sun4i_ss_alg_template *algt;
- struct rng_alg *alg = crypto_rng_alg(tfm);
-
- algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng);
- memcpy(algt->ss->seed, seed, slen);
-
- return 0;
-}
-
-int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen)
-{
- struct sun4i_ss_alg_template *algt;
- struct rng_alg *alg = crypto_rng_alg(tfm);
- int i, err;
- u32 v;
- u32 *data = (u32 *)dst;
- const u32 mode = SS_OP_PRNG | SS_PRNG_CONTINUE | SS_ENABLED;
- size_t len;
- struct sun4i_ss_ctx *ss;
- unsigned int todo = (dlen / 4) * 4;
-
- algt = container_of(alg, struct sun4i_ss_alg_template, alg.rng);
- ss = algt->ss;
-
- err = pm_runtime_resume_and_get(ss->dev);
- if (err < 0)
- return err;
-
- if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG)) {
- algt->stat_req++;
- algt->stat_bytes += todo;
- }
-
- spin_lock_bh(&ss->slock);
-
- writel(mode, ss->base + SS_CTL);
-
- while (todo > 0) {
- /* write the seed */
- for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++)
- writel(ss->seed[i], ss->base + SS_KEY0 + i * 4);
-
- /* Read the random data */
- len = min_t(size_t, SS_DATA_LEN / BITS_PER_BYTE, todo);
- readsl(ss->base + SS_TXFIFO, data, len / 4);
- data += len / 4;
- todo -= len;
-
- /* Update the seed */
- for (i = 0; i < SS_SEED_LEN / BITS_PER_LONG; i++) {
- v = readl(ss->base + SS_KEY0 + i * 4);
- ss->seed[i] = v;
- }
- }
-
- writel(0, ss->base + SS_CTL);
- spin_unlock_bh(&ss->slock);
-
- pm_runtime_put(ss->dev);
-
- return 0;
-}
diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h
index 6c5d4aa6453c..f7d1c79ac677 100644
--- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h
+++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h
@@ -31,8 +31,6 @@
#include <crypto/internal/skcipher.h>
#include <crypto/aes.h>
#include <crypto/internal/des.h>
-#include <crypto/internal/rng.h>
-#include <crypto/rng.h>
#define SS_CTL 0x00
#define SS_KEY0 0x04
@@ -62,10 +60,6 @@
/* SS_CTL configuration values */
-/* PRNG generator mode - bit 15 */
-#define SS_PRNG_ONESHOT (0 << 15)
-#define SS_PRNG_CONTINUE (1 << 15)
-
/* IV mode for hash */
#define SS_IV_ARBITRARY (1 << 14)
@@ -94,14 +88,10 @@
#define SS_OP_3DES (2 << 4)
#define SS_OP_SHA1 (3 << 4)
#define SS_OP_MD5 (4 << 4)
-#define SS_OP_PRNG (5 << 4)
/* Data end bit - bit 2 */
#define SS_DATA_END (1 << 2)
-/* PRNG start bit - bit 1 */
-#define SS_PRNG_START (1 << 1)
-
/* SS Enable bit - bit 0 */
#define SS_DISABLED (0 << 0)
#define SS_ENABLED (1 << 0)
@@ -128,9 +118,6 @@
#define SS_RXFIFO_EMP_INT_ENABLE (1 << 2)
#define SS_TXFIFO_AVA_INT_ENABLE (1 << 0)
-#define SS_SEED_LEN 192
-#define SS_DATA_LEN 160
-
/*
* struct ss_variant - Describe SS hardware variant
* @sha1_in_be: The SHA1 digest is given by SS in BE, and so need to be inverted.
@@ -151,9 +138,6 @@ struct sun4i_ss_ctx {
char buf[4 * SS_RX_MAX];/* buffer for linearize SG src */
char bufo[4 * SS_TX_MAX]; /* buffer for linearize SG dst */
spinlock_t slock; /* control the use of the device */
-#ifdef CONFIG_CRYPTO_DEV_SUN4I_SS_PRNG
- u32 seed[SS_SEED_LEN / BITS_PER_LONG];
-#endif
struct dentry *dbgfs_dir;
struct dentry *dbgfs_stats;
};
@@ -164,7 +148,6 @@ struct sun4i_ss_alg_template {
union {
struct skcipher_alg crypto;
struct ahash_alg hash;
- struct rng_alg rng;
} alg;
struct sun4i_ss_ctx *ss;
unsigned long stat_req;
@@ -231,6 +214,3 @@ int sun4i_ss_des_setkey(struct crypto_skcipher *tfm, const u8 *key,
unsigned int keylen);
int sun4i_ss_des3_setkey(struct crypto_skcipher *tfm, const u8 *key,
unsigned int keylen);
-int sun4i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen);
-int sun4i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c
index ded732242732..91cecec729f2 100644
--- a/drivers/crypto/amcc/crypto4xx_alg.c
+++ b/drivers/crypto/amcc/crypto4xx_alg.c
@@ -12,9 +12,6 @@
#include <linux/interrupt.h>
#include <linux/spinlock_types.h>
#include <linux/scatterlist.h>
-#include <linux/crypto.h>
-#include <linux/hash.h>
-#include <crypto/internal/hash.h>
#include <linux/dma-mapping.h>
#include <crypto/algapi.h>
#include <crypto/aead.h>
@@ -616,106 +613,3 @@ int crypto4xx_decrypt_aes_gcm(struct aead_request *req)
{
return crypto4xx_crypt_aes_gcm(req, true);
}
-
-/*
- * HASH SHA1 Functions
- */
-static int crypto4xx_hash_alg_init(struct crypto_tfm *tfm,
- unsigned int sa_len,
- unsigned char ha,
- unsigned char hm)
-{
- struct crypto_alg *alg = tfm->__crt_alg;
- struct crypto4xx_alg *my_alg;
- struct crypto4xx_ctx *ctx = crypto_tfm_ctx(tfm);
- struct dynamic_sa_hash160 *sa;
- int rc;
-
- my_alg = container_of(__crypto_ahash_alg(alg), struct crypto4xx_alg,
- alg.u.hash);
- ctx->dev = my_alg->dev;
-
- /* Create SA */
- if (ctx->sa_in || ctx->sa_out)
- crypto4xx_free_sa(ctx);
-
- rc = crypto4xx_alloc_sa(ctx, sa_len);
- if (rc)
- return rc;
-
- crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
- sizeof(struct crypto4xx_ctx));
- sa = (struct dynamic_sa_hash160 *)ctx->sa_in;
- set_dynamic_sa_command_0(&sa->ctrl, SA_SAVE_HASH, SA_NOT_SAVE_IV,
- SA_NOT_LOAD_HASH, SA_LOAD_IV_FROM_SA,
- SA_NO_HEADER_PROC, ha, SA_CIPHER_ALG_NULL,
- SA_PAD_TYPE_ZERO, SA_OP_GROUP_BASIC,
- SA_OPCODE_HASH, DIR_INBOUND);
- set_dynamic_sa_command_1(&sa->ctrl, 0, SA_HASH_MODE_HASH,
- CRYPTO_FEEDBACK_MODE_NO_FB, SA_EXTENDED_SN_OFF,
- SA_SEQ_MASK_OFF, SA_MC_ENABLE,
- SA_NOT_COPY_PAD, SA_NOT_COPY_PAYLOAD,
- SA_NOT_COPY_HDR);
- /* Need to zero hash digest in SA */
- memset(sa->inner_digest, 0, sizeof(sa->inner_digest));
- memset(sa->outer_digest, 0, sizeof(sa->outer_digest));
-
- return 0;
-}
-
-int crypto4xx_hash_init(struct ahash_request *req)
-{
- struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
- int ds;
- struct dynamic_sa_ctl *sa;
-
- sa = ctx->sa_in;
- ds = crypto_ahash_digestsize(
- __crypto_ahash_cast(req->base.tfm));
- sa->sa_command_0.bf.digest_len = ds >> 2;
- sa->sa_command_0.bf.load_hash_state = SA_LOAD_HASH_FROM_SA;
-
- return 0;
-}
-
-int crypto4xx_hash_update(struct ahash_request *req)
-{
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
- struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
- struct scatterlist dst;
- unsigned int ds = crypto_ahash_digestsize(ahash);
-
- sg_init_one(&dst, req->result, ds);
-
- return crypto4xx_build_pd(&req->base, ctx, req->src, &dst,
- req->nbytes, NULL, 0, ctx->sa_in,
- ctx->sa_len, 0, NULL);
-}
-
-int crypto4xx_hash_final(struct ahash_request *req)
-{
- return 0;
-}
-
-int crypto4xx_hash_digest(struct ahash_request *req)
-{
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
- struct crypto4xx_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
- struct scatterlist dst;
- unsigned int ds = crypto_ahash_digestsize(ahash);
-
- sg_init_one(&dst, req->result, ds);
-
- return crypto4xx_build_pd(&req->base, ctx, req->src, &dst,
- req->nbytes, NULL, 0, ctx->sa_in,
- ctx->sa_len, 0, NULL);
-}
-
-/*
- * SHA1 Algorithm
- */
-int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm)
-{
- return crypto4xx_hash_alg_init(tfm, SA_HASH160_LEN, SA_HASH_ALG_SHA1,
- SA_HASH_MODE_HASH);
-}
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index d553f3f1efbe..822c1ac2d5dd 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -31,11 +31,9 @@
#include <crypto/ctr.h>
#include <crypto/gcm.h>
#include <crypto/sha1.h>
-#include <crypto/rng.h>
#include <crypto/scatterwalk.h>
#include <crypto/skcipher.h>
#include <crypto/internal/aead.h>
-#include <crypto/internal/rng.h>
#include <crypto/internal/skcipher.h>
#include "crypto4xx_reg_def.h"
#include "crypto4xx_core.h"
@@ -485,18 +483,6 @@ static void crypto4xx_copy_pkt_to_dst(struct crypto4xx_device *dev,
}
}
-static void crypto4xx_copy_digest_to_dst(void *dst,
- struct pd_uinfo *pd_uinfo,
- struct crypto4xx_ctx *ctx)
-{
- struct dynamic_sa_ctl *sa = (struct dynamic_sa_ctl *) ctx->sa_in;
-
- if (sa->sa_command_0.bf.hash_alg == SA_HASH_ALG_SHA1) {
- memcpy(dst, pd_uinfo->sr_va->save_digest,
- SA_HASH_ALG_SHA1_DIGEST_SIZE);
- }
-}
-
static void crypto4xx_ret_sg_desc(struct crypto4xx_device *dev,
struct pd_uinfo *pd_uinfo)
{
@@ -549,23 +535,6 @@ static void crypto4xx_cipher_done(struct crypto4xx_device *dev,
skcipher_request_complete(req, 0);
}
-static void crypto4xx_ahash_done(struct crypto4xx_device *dev,
- struct pd_uinfo *pd_uinfo)
-{
- struct crypto4xx_ctx *ctx;
- struct ahash_request *ahash_req;
-
- ahash_req = ahash_request_cast(pd_uinfo->async_req);
- ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(ahash_req));
-
- crypto4xx_copy_digest_to_dst(ahash_req->result, pd_uinfo, ctx);
- crypto4xx_ret_sg_desc(dev, pd_uinfo);
-
- if (pd_uinfo->state & PD_ENTRY_BUSY)
- ahash_request_complete(ahash_req, -EINPROGRESS);
- ahash_request_complete(ahash_req, 0);
-}
-
static void crypto4xx_aead_done(struct crypto4xx_device *dev,
struct pd_uinfo *pd_uinfo,
struct ce_pd *pd)
@@ -642,9 +611,6 @@ static void crypto4xx_pd_done(struct crypto4xx_device *dev, u32 idx)
case CRYPTO_ALG_TYPE_AEAD:
crypto4xx_aead_done(dev, pd_uinfo, pd);
break;
- case CRYPTO_ALG_TYPE_AHASH:
- crypto4xx_ahash_done(dev, pd_uinfo);
- break;
}
}
@@ -915,8 +881,7 @@ int crypto4xx_build_pd(struct crypto_async_request *req,
}
pd->pd_ctl.w = PD_CTL_HOST_READY |
- ((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AHASH) ||
- (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
+ ((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ?
PD_CTL_HASH_FINAL : 0);
pd->pd_ctl_len.w = 0x00400000 | (assoclen + datalen);
pd_uinfo->state = PD_ENTRY_INUSE | (is_busy ? PD_ENTRY_BUSY : 0);
@@ -1022,14 +987,6 @@ static int crypto4xx_register_alg(struct crypto4xx_device *sec_dev,
rc = crypto_register_aead(&alg->alg.u.aead);
break;
- case CRYPTO_ALG_TYPE_AHASH:
- rc = crypto_register_ahash(&alg->alg.u.hash);
- break;
-
- case CRYPTO_ALG_TYPE_RNG:
- rc = crypto_register_rng(&alg->alg.u.rng);
- break;
-
default:
rc = crypto_register_skcipher(&alg->alg.u.cipher);
break;
@@ -1051,18 +1008,10 @@ static void crypto4xx_unregister_alg(struct crypto4xx_device *sec_dev)
list_for_each_entry_safe(alg, tmp, &sec_dev->alg_list, entry) {
list_del(&alg->entry);
switch (alg->alg.type) {
- case CRYPTO_ALG_TYPE_AHASH:
- crypto_unregister_ahash(&alg->alg.u.hash);
- break;
-
case CRYPTO_ALG_TYPE_AEAD:
crypto_unregister_aead(&alg->alg.u.aead);
break;
- case CRYPTO_ALG_TYPE_RNG:
- crypto_unregister_rng(&alg->alg.u.rng);
- break;
-
default:
crypto_unregister_skcipher(&alg->alg.u.cipher);
}
@@ -1121,69 +1070,6 @@ static irqreturn_t crypto4xx_ce_interrupt_handler_revb(int irq, void *data)
PPC4XX_TMO_ERR_INT);
}
-static int ppc4xx_prng_data_read(struct crypto4xx_device *dev,
- u8 *data, unsigned int max)
-{
- unsigned int i, curr = 0;
- u32 val[2];
-
- do {
- /* trigger PRN generation */
- writel(PPC4XX_PRNG_CTRL_AUTO_EN,
- dev->ce_base + CRYPTO4XX_PRNG_CTRL);
-
- for (i = 0; i < 1024; i++) {
- /* usually 19 iterations are enough */
- if ((readl(dev->ce_base + CRYPTO4XX_PRNG_STAT) &
- CRYPTO4XX_PRNG_STAT_BUSY))
- continue;
-
- val[0] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_0);
- val[1] = readl_be(dev->ce_base + CRYPTO4XX_PRNG_RES_1);
- break;
- }
- if (i == 1024)
- return -ETIMEDOUT;
-
- if ((max - curr) >= 8) {
- memcpy(data, &val, 8);
- data += 8;
- curr += 8;
- } else {
- /* copy only remaining bytes */
- memcpy(data, &val, max - curr);
- break;
- }
- } while (curr < max);
-
- return curr;
-}
-
-static int crypto4xx_prng_generate(struct crypto_rng *tfm,
- const u8 *src, unsigned int slen,
- u8 *dstn, unsigned int dlen)
-{
- struct rng_alg *alg = crypto_rng_alg(tfm);
- struct crypto4xx_alg *amcc_alg;
- struct crypto4xx_device *dev;
- int ret;
-
- amcc_alg = container_of(alg, struct crypto4xx_alg, alg.u.rng);
- dev = amcc_alg->dev;
-
- mutex_lock(&dev->core_dev->rng_lock);
- ret = ppc4xx_prng_data_read(dev, dstn, dlen);
- mutex_unlock(&dev->core_dev->rng_lock);
- return ret;
-}
-
-
-static int crypto4xx_prng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- return 0;
-}
-
/*
* Supported Crypto Algorithms
*/
@@ -1353,18 +1239,6 @@ static struct crypto4xx_alg_common crypto4xx_alg[] = {
.cra_module = THIS_MODULE,
},
} },
- { .type = CRYPTO_ALG_TYPE_RNG, .u.rng = {
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "crypto4xx_rng",
- .cra_priority = 300,
- .cra_ctxsize = 0,
- .cra_module = THIS_MODULE,
- },
- .generate = crypto4xx_prng_generate,
- .seed = crypto4xx_prng_seed,
- .seedsize = 0,
- } },
};
/*
@@ -1442,7 +1316,6 @@ static int crypto4xx_probe(struct platform_device *ofdev)
core_dev->dev->core_dev = core_dev;
core_dev->dev->is_revb = is_revb;
core_dev->device = dev;
- mutex_init(&core_dev->rng_lock);
spin_lock_init(&core_dev->lock);
INIT_LIST_HEAD(&core_dev->dev->alg_list);
ratelimit_default_init(&core_dev->dev->aead_ratelimit);
@@ -1520,7 +1393,6 @@ static int crypto4xx_remove(struct platform_device *ofdev)
tasklet_kill(&core_dev->tasklet);
/* Un-register with Linux CryptoAPI */
crypto4xx_unregister_alg(core_dev->dev);
- mutex_destroy(&core_dev->rng_lock);
/* Free all allocated memory */
crypto4xx_stop_all(core_dev);
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
index 56c10668c0ab..d8224bbe89ca 100644
--- a/drivers/crypto/amcc/crypto4xx_core.h
+++ b/drivers/crypto/amcc/crypto4xx_core.h
@@ -14,11 +14,8 @@
#define __CRYPTO4XX_CORE_H__
#include <linux/ratelimit.h>
-#include <linux/mutex.h>
#include <linux/scatterlist.h>
-#include <crypto/internal/hash.h>
#include <crypto/internal/aead.h>
-#include <crypto/internal/rng.h>
#include <crypto/internal/skcipher.h>
#include "crypto4xx_reg_def.h"
#include "crypto4xx_sa.h"
@@ -112,7 +109,6 @@ struct crypto4xx_core_device {
u32 irq;
struct tasklet_struct tasklet;
spinlock_t lock;
- struct mutex rng_lock;
};
struct crypto4xx_ctx {
@@ -135,9 +131,7 @@ struct crypto4xx_alg_common {
u32 type;
union {
struct skcipher_alg cipher;
- struct ahash_alg hash;
struct aead_alg aead;
- struct rng_alg rng;
} u;
};
@@ -182,11 +176,6 @@ int crypto4xx_encrypt_noiv_block(struct skcipher_request *req);
int crypto4xx_decrypt_noiv_block(struct skcipher_request *req);
int crypto4xx_rfc3686_encrypt(struct skcipher_request *req);
int crypto4xx_rfc3686_decrypt(struct skcipher_request *req);
-int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm);
-int crypto4xx_hash_digest(struct ahash_request *req);
-int crypto4xx_hash_final(struct ahash_request *req);
-int crypto4xx_hash_update(struct ahash_request *req);
-int crypto4xx_hash_init(struct ahash_request *req);
/*
* Note: Only use this function to copy items that is word aligned.
diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h
index 1038061224da..73d626308a84 100644
--- a/drivers/crypto/amcc/crypto4xx_reg_def.h
+++ b/drivers/crypto/amcc/crypto4xx_reg_def.h
@@ -90,20 +90,9 @@
#define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8
#define CRYPTO4XX_ENDIAN_CFG 0x000600d8
-#define CRYPTO4XX_PRNG_STAT 0x00070000
-#define CRYPTO4XX_PRNG_STAT_BUSY 0x1
#define CRYPTO4XX_PRNG_CTRL 0x00070004
#define CRYPTO4XX_PRNG_SEED_L 0x00070008
#define CRYPTO4XX_PRNG_SEED_H 0x0007000c
-
-#define CRYPTO4XX_PRNG_RES_0 0x00070020
-#define CRYPTO4XX_PRNG_RES_1 0x00070024
-#define CRYPTO4XX_PRNG_RES_2 0x00070028
-#define CRYPTO4XX_PRNG_RES_3 0x0007002C
-
-#define CRYPTO4XX_PRNG_LFSR_L 0x00070030
-#define CRYPTO4XX_PRNG_LFSR_H 0x00070034
-
/*
* Initialize CRYPTO ENGINE registers, and memory bases.
*/
diff --git a/drivers/crypto/amlogic/amlogic-gxl-core.c b/drivers/crypto/amlogic/amlogic-gxl-core.c
index da6dfe0f9ac3..fde4f7bf7b1b 100644
--- a/drivers/crypto/amlogic/amlogic-gxl-core.c
+++ b/drivers/crypto/amlogic/amlogic-gxl-core.c
@@ -293,8 +293,8 @@ static int meson_crypto_probe(struct platform_device *pdev)
return 0;
error_alg:
meson_unregister_algs(mc);
-error_flow:
meson_free_chanlist(mc, MAXFLOW - 1);
+error_flow:
clk_disable_unprepare(mc->busclk);
return err;
}
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 813d8517e1c3..9da5a0388080 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -380,7 +380,7 @@ MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids);
#endif
static const struct i2c_device_id atmel_ecc_id[] = {
- { "atecc508a", 0 },
+ { "atecc508a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, atmel_ecc_id);
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 2cf5407818f1..a3f9887b928f 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -18,6 +18,12 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
+/*
+ * According to review by Bill Cox [1], the ATSHA204 has very low entropy.
+ * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
+ */
+static const unsigned short atsha204_quality = 1;
+
static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
void *areq, int status)
{
@@ -47,8 +53,8 @@ static int atmel_sha204a_rng_read_nonblocking(struct hwrng *rng, void *data,
if (rng->priv) {
work_data = (struct atmel_i2c_work_data *)rng->priv;
- max = min(sizeof(work_data->cmd.data), max);
- memcpy(data, &work_data->cmd.data, max);
+ max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
+ memcpy(data, &work_data->cmd.data[RSP_DATA_IDX], max);
rng->priv = 0;
} else {
work_data = kmalloc(sizeof(*work_data), GFP_ATOMIC);
@@ -86,8 +92,8 @@ static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max,
if (ret)
return ret;
- max = min(sizeof(cmd.data), max);
- memcpy(data, cmd.data, max);
+ max = min(RANDOM_RSP_SIZE - CMD_OVERHEAD_SIZE, max);
+ memcpy(data, &cmd.data[RSP_DATA_IDX], max);
return max;
}
@@ -95,6 +101,7 @@ static int atmel_sha204a_rng_read(struct hwrng *rng, void *data, size_t max,
static int atmel_sha204a_probe(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv;
+ const unsigned short *quality;
int ret;
ret = atmel_i2c_probe(client);
@@ -108,11 +115,9 @@ static int atmel_sha204a_probe(struct i2c_client *client)
i2c_priv->hwrng.name = dev_name(&client->dev);
i2c_priv->hwrng.read = atmel_sha204a_rng_read;
- /*
- * According to review by Bill Cox [1], this HWRNG has very low entropy.
- * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html
- */
- i2c_priv->hwrng.quality = 1;
+ quality = i2c_get_match_data(client);
+ if (quality)
+ i2c_priv->hwrng.quality = *quality;
ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
if (ret)
@@ -132,15 +137,15 @@ static void atmel_sha204a_remove(struct i2c_client *client)
}
static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = {
- { .compatible = "atmel,atsha204", },
+ { .compatible = "atmel,atsha204", .data = &atsha204_quality },
{ .compatible = "atmel,atsha204a", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids);
static const struct i2c_device_id atmel_sha204a_id[] = {
- { "atsha204", 0 },
- { "atsha204a", 0 },
+ { "atsha204", (kernel_ulong_t)&atsha204_quality },
+ { "atsha204a" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id);
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 066f08a3a040..7cdbcca4d46d 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -597,7 +597,7 @@ static int aead_setkey(struct crypto_aead *aead,
dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n",
keys.authkeylen + keys.enckeylen, keys.enckeylen,
keys.authkeylen);
- print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
/*
@@ -633,7 +633,7 @@ static int aead_setkey(struct crypto_aead *aead,
dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad +
keys.enckeylen, ctx->dir);
- print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ",
+ print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
ctx->adata.keylen_pad + keys.enckeylen, 1);
@@ -674,7 +674,7 @@ static int gcm_setkey(struct crypto_aead *aead,
if (err)
return err;
- print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -695,7 +695,7 @@ static int rfc4106_setkey(struct crypto_aead *aead,
if (err)
return err;
- print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -721,7 +721,7 @@ static int rfc4543_setkey(struct crypto_aead *aead,
if (err)
return err;
- print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -748,7 +748,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
u32 *desc;
const bool is_rfc3686 = alg->caam.rfc3686;
- print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
ctx->cdata.keylen = keylen;
diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c
index 743ce50c14f2..9d61235b9aae 100644
--- a/drivers/crypto/caam/caamalg_qi.c
+++ b/drivers/crypto/caam/caamalg_qi.c
@@ -212,7 +212,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key,
dev_dbg(jrdev, "keylen %d enckeylen %d authkeylen %d\n",
keys.authkeylen + keys.enckeylen, keys.enckeylen,
keys.authkeylen);
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
/*
@@ -248,7 +248,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key,
ctx->adata.keylen_pad + keys.enckeylen,
ctx->dir);
- print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ",
+ print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
ctx->adata.keylen_pad + keys.enckeylen, 1);
@@ -371,7 +371,7 @@ static int gcm_setkey(struct crypto_aead *aead,
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -475,7 +475,7 @@ static int rfc4106_setkey(struct crypto_aead *aead,
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -581,7 +581,7 @@ static int rfc4543_setkey(struct crypto_aead *aead,
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -631,7 +631,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
const bool is_rfc3686 = alg->caam.rfc3686;
int ret = 0;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
ctx->cdata.keylen = keylen;
diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c
index ac97a15ac78b..80240e8009cd 100644
--- a/drivers/crypto/caam/caamalg_qi2.c
+++ b/drivers/crypto/caam/caamalg_qi2.c
@@ -299,7 +299,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key,
dev_dbg(dev, "keylen %d enckeylen %d authkeylen %d\n",
keys.authkeylen + keys.enckeylen, keys.enckeylen,
keys.authkeylen);
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
ctx->adata.keylen = keys.authkeylen;
@@ -313,7 +313,7 @@ static int aead_setkey(struct crypto_aead *aead, const u8 *key,
memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen);
dma_sync_single_for_device(dev, ctx->key_dma, ctx->adata.keylen_pad +
keys.enckeylen, ctx->dir);
- print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ",
+ print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
ctx->adata.keylen_pad + keys.enckeylen, 1);
@@ -730,7 +730,7 @@ static int gcm_setkey(struct crypto_aead *aead,
ret = aes_check_keylen(keylen);
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -826,7 +826,7 @@ static int rfc4106_setkey(struct crypto_aead *aead,
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -925,7 +925,7 @@ static int rfc4543_setkey(struct crypto_aead *aead,
if (ret)
return ret;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
memcpy(ctx->key, key, keylen);
@@ -953,7 +953,7 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
u32 *desc;
const bool is_rfc3686 = alg->caam.rfc3686;
- print_hex_dump_debug("key in @" __stringify(__LINE__)": ",
+ print_hex_dump_devel("key in @" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
ctx->cdata.keylen = keylen;
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index c1a06f033b11..9243e0843162 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -505,7 +505,7 @@ static int axcbc_setkey(struct crypto_ahash *ahash, const u8 *key,
DMA_TO_DEVICE);
ctx->adata.keylen = keylen;
- print_hex_dump_debug("axcbc ctx.key@" __stringify(__LINE__)" : ",
+ print_hex_dump_devel("axcbc ctx.key@" __stringify(__LINE__)" : ",
DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, keylen, 1);
return axcbc_set_sh_desc(ahash);
@@ -525,7 +525,7 @@ static int acmac_setkey(struct crypto_ahash *ahash, const u8 *key,
ctx->adata.key_virt = key;
ctx->adata.keylen = keylen;
- print_hex_dump_debug("acmac ctx.key@" __stringify(__LINE__)" : ",
+ print_hex_dump_devel("acmac ctx.key@" __stringify(__LINE__)" : ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
return acmac_set_sh_desc(ahash);
diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c
index 88cc4fe2a585..de2fcc387477 100644
--- a/drivers/crypto/caam/key_gen.c
+++ b/drivers/crypto/caam/key_gen.c
@@ -58,7 +58,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out,
dev_dbg(jrdev, "split keylen %d split keylen padded %d\n",
adata->keylen, adata->keylen_pad);
- print_hex_dump_debug("ctx.key@" __stringify(__LINE__)": ",
+ print_hex_dump_devel("ctx.key@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key_in, keylen, 1);
if (local_max > max_keylen)
@@ -113,7 +113,7 @@ int gen_split_key(struct device *jrdev, u8 *key_out,
wait_for_completion(&result.completion);
ret = result.err;
- print_hex_dump_debug("ctx.key@"__stringify(__LINE__)": ",
+ print_hex_dump_devel("ctx.key@"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key_out,
adata->keylen_pad, 1);
}
diff --git a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c
index 153004bdfb5c..753bdb2ae33f 100644
--- a/drivers/crypto/cavium/cpt/cptvf_reqmanager.c
+++ b/drivers/crypto/cavium/cpt/cptvf_reqmanager.c
@@ -108,8 +108,8 @@ static int setup_sgio_components(struct cpt_vf *cptvf, struct buf_ptr *list,
sg_cleanup:
for (j = 0; j < i; j++) {
if (list[j].dma_addr) {
- dma_unmap_single(&pdev->dev, list[i].dma_addr,
- list[i].size, DMA_BIDIRECTIONAL);
+ dma_unmap_single(&pdev->dev, list[j].dma_addr,
+ list[j].size, DMA_BIDIRECTIONAL);
}
list[j].dma_addr = 0;
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index 9844ac4f9b86..173572912339 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -1016,7 +1016,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)
/* Userspace wants to query the certificate length. */
if (!input.pdh_cert_address ||
!input.pdh_cert_len ||
- !input.cert_chain_address)
+ !input.cert_chain_address ||
+ !input.cert_chain_len)
goto cmd;
input_pdh_cert_address = (void __user *)input.pdh_cert_address;
diff --git a/drivers/crypto/hisilicon/Kconfig b/drivers/crypto/hisilicon/Kconfig
index 4137a8bf131f..107bfffaead2 100644
--- a/drivers/crypto/hisilicon/Kconfig
+++ b/drivers/crypto/hisilicon/Kconfig
@@ -79,6 +79,5 @@ config CRYPTO_DEV_HISI_TRNG
tristate "Support for HISI TRNG Driver"
depends on ARM64 && ACPI
select HW_RANDOM
- select CRYPTO_RNG
help
Support for HiSilicon TRNG Driver.
diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c
index e09357fea057..c7f874570383 100644
--- a/drivers/crypto/hisilicon/qm.c
+++ b/drivers/crypto/hisilicon/qm.c
@@ -4568,8 +4568,6 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev)
u32 delay = 0;
int ret;
- hisi_qm_dev_err_uninit(pf_qm);
-
/*
* Check whether there is an ECC mbit error, If it occurs, need to
* wait for soft reset to fix it.
@@ -4586,6 +4584,8 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev)
return;
}
+ hisi_qm_dev_err_uninit(pf_qm);
+
/* PF obtains the information of VF by querying the register. */
if (qm->fun_type == QM_HW_PF)
qm_cmd_uninit(qm);
diff --git a/drivers/crypto/hisilicon/trng/trng.c b/drivers/crypto/hisilicon/trng/trng.c
index b2d9b5310b78..6584ed051e09 100644
--- a/drivers/crypto/hisilicon/trng/trng.c
+++ b/drivers/crypto/hisilicon/trng/trng.c
@@ -1,234 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 HiSilicon Limited. */
-#include <crypto/internal/rng.h>
#include <linux/acpi.h>
-#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#define HISI_TRNG_REG 0x00F0
#define HISI_TRNG_BYTES 4
#define HISI_TRNG_QUALITY 512
-#define HISI_TRNG_VERSION 0x01B8
-#define HISI_TRNG_VER_V1 GENMASK(31, 0)
#define SLEEP_US 10
#define TIMEOUT_US 10000
-#define SW_DRBG_NUM_SHIFT 2
-#define SW_DRBG_KEY_BASE 0x082C
-#define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT))
-#define SW_DRBG_SEED_REGS_NUM 12
-#define SW_DRBG_SEED_SIZE 48
-#define SW_DRBG_BLOCKS 0x0830
-#define SW_DRBG_INIT 0x0834
-#define SW_DRBG_GEN 0x083c
-#define SW_DRBG_STATUS 0x0840
-#define SW_DRBG_BLOCKS_NUM 4095
-#define SW_DRBG_DATA_BASE 0x0850
-#define SW_DRBG_DATA_NUM 4
-#define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT))
-#define SW_DRBG_BYTES 16
-#define SW_DRBG_ENABLE_SHIFT 12
-#define SEED_SHIFT_24 24
-#define SEED_SHIFT_16 16
-#define SEED_SHIFT_8 8
-#define SW_MAX_RANDOM_BYTES 65520
-
-struct hisi_trng_list {
- struct mutex lock;
- struct list_head list;
- bool is_init;
-};
struct hisi_trng {
void __iomem *base;
- struct hisi_trng_list *trng_list;
- struct list_head list;
struct hwrng rng;
- u32 ver;
- u32 ctx_num;
- /* The bytes of the random number generated since the last seeding. */
- u32 random_bytes;
- struct mutex lock;
-};
-
-struct hisi_trng_ctx {
- struct hisi_trng *trng;
};
-static atomic_t trng_active_devs;
-static struct hisi_trng_list trng_devices;
-static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait);
-
-static int hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed)
-{
- u32 val, seed_reg, i;
- int ret;
-
- writel(0x0, trng->base + SW_DRBG_BLOCKS);
-
- for (i = 0; i < SW_DRBG_SEED_SIZE;
- i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) {
- val = seed[i] << SEED_SHIFT_24;
- val |= seed[i + 1UL] << SEED_SHIFT_16;
- val |= seed[i + 2UL] << SEED_SHIFT_8;
- val |= seed[i + 3UL];
-
- seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM;
- writel(val, trng->base + SW_DRBG_SEED(seed_reg));
- }
-
- writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT),
- trng->base + SW_DRBG_BLOCKS);
- writel(0x1, trng->base + SW_DRBG_INIT);
- ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS,
- val, val & BIT(0), SLEEP_US, TIMEOUT_US);
- if (ret) {
- pr_err("failed to init trng(%d)\n", ret);
- return -EIO;
- }
-
- trng->random_bytes = 0;
-
- return 0;
-}
-
-static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
- struct hisi_trng *trng = ctx->trng;
- int ret;
-
- if (slen < SW_DRBG_SEED_SIZE) {
- pr_err("slen(%u) is not matched with trng(%d)\n", slen,
- SW_DRBG_SEED_SIZE);
- return -EINVAL;
- }
-
- mutex_lock(&trng->lock);
- ret = hisi_trng_set_seed(trng, seed);
- mutex_unlock(&trng->lock);
-
- return ret;
-}
-
-static int hisi_trng_reseed(struct hisi_trng *trng)
-{
- u8 seed[SW_DRBG_SEED_SIZE];
- int size;
-
- if (!trng->random_bytes)
- return 0;
-
- size = hisi_trng_read(&trng->rng, seed, SW_DRBG_SEED_SIZE, false);
- if (size != SW_DRBG_SEED_SIZE)
- return -EIO;
-
- return hisi_trng_set_seed(trng, seed);
-}
-
-static int hisi_trng_get_bytes(struct hisi_trng *trng, u8 *dstn, unsigned int dlen)
-{
- u32 data[SW_DRBG_DATA_NUM];
- u32 currsize = 0;
- u32 val = 0;
- int ret;
- u32 i;
-
- ret = hisi_trng_reseed(trng);
- if (ret)
- return ret;
-
- do {
- ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS,
- val, val & BIT(1), SLEEP_US, TIMEOUT_US);
- if (ret) {
- pr_err("failed to generate random number(%d)!\n", ret);
- break;
- }
-
- for (i = 0; i < SW_DRBG_DATA_NUM; i++)
- data[i] = readl(trng->base + SW_DRBG_DATA(i));
-
- if (dlen - currsize >= SW_DRBG_BYTES) {
- memcpy(dstn + currsize, data, SW_DRBG_BYTES);
- currsize += SW_DRBG_BYTES;
- } else {
- memcpy(dstn + currsize, data, dlen - currsize);
- currsize = dlen;
- }
-
- trng->random_bytes += SW_DRBG_BYTES;
- writel(0x1, trng->base + SW_DRBG_GEN);
- } while (currsize < dlen);
-
- return ret;
-}
-
-static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dstn, unsigned int dlen)
-{
- struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm);
- struct hisi_trng *trng = ctx->trng;
- unsigned int currsize = 0;
- unsigned int block_size;
- int ret;
-
- if (!dstn || !dlen) {
- pr_err("output is error, dlen %u!\n", dlen);
- return -EINVAL;
- }
-
- do {
- block_size = min_t(unsigned int, dlen - currsize, SW_MAX_RANDOM_BYTES);
- mutex_lock(&trng->lock);
- ret = hisi_trng_get_bytes(trng, dstn + currsize, block_size);
- mutex_unlock(&trng->lock);
- if (ret)
- return ret;
- currsize += block_size;
- } while (currsize < dlen);
-
- return 0;
-}
-
-static int hisi_trng_init(struct crypto_tfm *tfm)
-{
- struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);
- struct hisi_trng *trng;
- u32 ctx_num = ~0;
-
- mutex_lock(&trng_devices.lock);
- list_for_each_entry(trng, &trng_devices.list, list) {
- if (trng->ctx_num < ctx_num) {
- ctx_num = trng->ctx_num;
- ctx->trng = trng;
- }
- }
- ctx->trng->ctx_num++;
- mutex_unlock(&trng_devices.lock);
-
- return 0;
-}
-
-static void hisi_trng_exit(struct crypto_tfm *tfm)
-{
- struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm);
-
- mutex_lock(&trng_devices.lock);
- ctx->trng->ctx_num--;
- mutex_unlock(&trng_devices.lock);
-}
-
static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
struct hisi_trng *trng;
@@ -260,42 +53,6 @@ static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
return currsize;
}
-static struct rng_alg hisi_trng_alg = {
- .generate = hisi_trng_generate,
- .seed = hisi_trng_seed,
- .seedsize = SW_DRBG_SEED_SIZE,
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "hisi_stdrng",
- .cra_priority = 300,
- .cra_ctxsize = sizeof(struct hisi_trng_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = hisi_trng_init,
- .cra_exit = hisi_trng_exit,
- },
-};
-
-static void hisi_trng_add_to_list(struct hisi_trng *trng)
-{
- mutex_lock(&trng_devices.lock);
- list_add_tail(&trng->list, &trng_devices.list);
- mutex_unlock(&trng_devices.lock);
-}
-
-static int hisi_trng_del_from_list(struct hisi_trng *trng)
-{
- int ret = -EBUSY;
-
- mutex_lock(&trng_devices.lock);
- if (!trng->ctx_num) {
- list_del(&trng->list);
- ret = 0;
- }
- mutex_unlock(&trng_devices.lock);
-
- return ret;
-}
-
static int hisi_trng_probe(struct platform_device *pdev)
{
struct hisi_trng *trng;
@@ -305,70 +62,20 @@ static int hisi_trng_probe(struct platform_device *pdev)
if (!trng)
return -ENOMEM;
- platform_set_drvdata(pdev, trng);
-
trng->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(trng->base))
return PTR_ERR(trng->base);
- trng->ctx_num = 0;
- trng->random_bytes = SW_MAX_RANDOM_BYTES;
- mutex_init(&trng->lock);
- trng->ver = readl(trng->base + HISI_TRNG_VERSION);
- if (!trng_devices.is_init) {
- INIT_LIST_HEAD(&trng_devices.list);
- mutex_init(&trng_devices.lock);
- trng_devices.is_init = true;
- }
-
- hisi_trng_add_to_list(trng);
- if (trng->ver != HISI_TRNG_VER_V1 &&
- atomic_inc_return(&trng_active_devs) == 1) {
- ret = crypto_register_rng(&hisi_trng_alg);
- if (ret) {
- dev_err(&pdev->dev,
- "failed to register crypto(%d)\n", ret);
- atomic_dec_return(&trng_active_devs);
- goto err_remove_from_list;
- }
- }
-
trng->rng.name = pdev->name;
trng->rng.read = hisi_trng_read;
trng->rng.quality = HISI_TRNG_QUALITY;
+
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret);
- goto err_crypto_unregister;
- }
-
- return ret;
-
-err_crypto_unregister:
- if (trng->ver != HISI_TRNG_VER_V1 &&
- atomic_dec_return(&trng_active_devs) == 0)
- crypto_unregister_rng(&hisi_trng_alg);
-
-err_remove_from_list:
- hisi_trng_del_from_list(trng);
return ret;
}
-static int hisi_trng_remove(struct platform_device *pdev)
-{
- struct hisi_trng *trng = platform_get_drvdata(pdev);
-
- /* Wait until the task is finished */
- while (hisi_trng_del_from_list(trng))
- ;
-
- if (trng->ver != HISI_TRNG_VER_V1 &&
- atomic_dec_return(&trng_active_devs) == 0)
- crypto_unregister_rng(&hisi_trng_alg);
-
- return 0;
-}
-
static const struct acpi_device_id hisi_trng_acpi_match[] = {
{ "HISI02B3", 0 },
{ }
@@ -377,7 +84,6 @@ MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match);
static struct platform_driver hisi_trng_driver = {
.probe = hisi_trng_probe,
- .remove = hisi_trng_remove,
.driver = {
.name = "hisi-trng-v2",
.acpi_match_table = ACPI_PTR(hisi_trng_acpi_match),
diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
index 79d5a1535eda..658337b6aad6 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
+++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
@@ -314,6 +314,8 @@ struct adf_accel_dev {
struct {
/* protects VF2PF interrupts access */
spinlock_t vf2pf_ints_lock;
+ /* prevents VF2PF handling from racing with VF state teardown */
+ bool vf2pf_disabled;
/* vf_info is non-zero when SR-IOV is init'ed */
struct adf_accel_vf_info *vf_info;
} pf;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c
index af495a6f039f..b47b566689c3 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_aer.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c
@@ -113,13 +113,14 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev,
struct adf_reset_dev_data *reset_data;
if (!adf_dev_started(accel_dev) ||
- test_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
+ test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
return 0;
- set_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
- if (!reset_data)
+ if (!reset_data) {
+ clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
return -ENOMEM;
+ }
reset_data->accel_dev = accel_dev;
init_completion(&reset_data->compl);
reset_data->mode = mode;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h
index d9255b307c4b..2cf21f59a21c 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h
+++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h
@@ -123,6 +123,7 @@ void qat_comp_alg_callback(void *resp);
int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
+void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev);
int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
@@ -195,6 +196,7 @@ void adf_misc_wq_flush(void);
int adf_sriov_configure(struct pci_dev *pdev, int numvfs);
void adf_disable_sriov(struct adf_accel_dev *accel_dev);
void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask);
+void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs);
void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev);
bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev);
bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr);
diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c
index ce7c9ef6346b..0d34f346a381 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_isr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c
@@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask)
unsigned long flags;
spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
+ if (!READ_ONCE(accel_dev->pf.vf2pf_disabled))
+ GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask);
+ spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
+}
+
+void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs)
+{
+ void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
+ unsigned long flags;
+ u32 vf_mask;
+
+ vf_mask = BIT_ULL(num_vfs) - 1;
+ if (!vf_mask)
+ return;
+
+ spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
+ WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false);
GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask);
spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
}
@@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev)
unsigned long flags;
spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
+ WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true);
GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr);
spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
}
@@ -151,6 +169,27 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr)
return IRQ_NONE;
}
+void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev)
+{
+ struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
+ struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
+ u32 num_entries = pci_dev_info->msix_entries.num_entries;
+ struct adf_irq *irqs = pci_dev_info->msix_entries.irqs;
+ u32 irq_idx;
+ int irq;
+
+ if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs)
+ return;
+
+ irq_idx = num_entries > 1 ? hw_data->num_banks : 0;
+ if (irq_idx >= num_entries || !irqs[irq_idx].enabled)
+ return;
+
+ irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks);
+ if (irq > 0)
+ synchronize_irq(irq);
+}
+
static void adf_free_irqs(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c
index f44025bb6f99..202c08c9d9f0 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c
@@ -26,18 +26,26 @@ static void adf_iov_send_resp(struct work_struct *work)
u32 vf_nr = vf_info->vf_nr;
bool ret;
+ if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
+ goto out;
+
ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr);
if (ret)
/* re-enable interrupt on PF from this VF */
adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr);
+out:
kfree(pf2vf_resp);
}
void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info)
{
+ struct adf_accel_dev *accel_dev = vf_info->accel_dev;
struct adf_pf2vf_resp *pf2vf_resp;
+ if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
+ return;
+
pf2vf_resp = kzalloc(sizeof(*pf2vf_resp), GFP_ATOMIC);
if (!pf2vf_resp)
return;
@@ -47,6 +55,12 @@ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info)
queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work);
}
+static void adf_flush_pf2vf_resp_wq(void)
+{
+ if (pf2vf_resp_wq)
+ flush_workqueue(pf2vf_resp_wq);
+}
+
static int adf_enable_sriov(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
@@ -73,7 +87,7 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev)
hw_data->configure_iov_threads(accel_dev, true);
/* Enable VF to PF interrupts for all VFs */
- adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1);
+ adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs);
/*
* Due to the hardware design, when SR-IOV and the ring arbiter
@@ -105,8 +119,10 @@ void adf_disable_sriov(struct adf_accel_dev *accel_dev)
adf_pf2vf_notify_restarting(accel_dev);
pci_disable_sriov(accel_to_pci_dev(accel_dev));
- /* Disable VF to PF interrupts */
+ /* Block VF2PF work and disable VF to PF interrupts */
adf_disable_all_vf2pf_interrupts(accel_dev);
+ adf_isr_sync_ae_cluster(accel_dev);
+ adf_flush_pf2vf_resp_wq();
/* Clear Valid bits in AE Thread to PCIe Function Mapping */
if (hw_data->configure_iov_threads)
diff --git a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c
index 4128200a9032..4437c3a5c6dd 100644
--- a/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c
+++ b/drivers/crypto/intel/qat/qat_common/qat_asym_algs.c
@@ -1032,7 +1032,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key)
ptr = rsa_key->p;
len = rsa_key->p_sz;
qat_rsa_drop_leading_zeros(&ptr, &len);
- if (!len)
+ if (!len || len > half_key_sz)
goto err;
ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL);
if (!ctx->p)
@@ -1043,7 +1043,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key)
ptr = rsa_key->q;
len = rsa_key->q_sz;
qat_rsa_drop_leading_zeros(&ptr, &len);
- if (!len)
+ if (!len || len > half_key_sz)
goto free_p;
ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL);
if (!ctx->q)
@@ -1054,7 +1054,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key)
ptr = rsa_key->dp;
len = rsa_key->dp_sz;
qat_rsa_drop_leading_zeros(&ptr, &len);
- if (!len)
+ if (!len || len > half_key_sz)
goto free_q;
ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp,
GFP_KERNEL);
@@ -1066,7 +1066,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key)
ptr = rsa_key->dq;
len = rsa_key->dq_sz;
qat_rsa_drop_leading_zeros(&ptr, &len);
- if (!len)
+ if (!len || len > half_key_sz)
goto free_dp;
ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq,
GFP_KERNEL);
@@ -1078,7 +1078,7 @@ static void qat_rsa_setkey_crt(struct qat_rsa_ctx *ctx, struct rsa_key *rsa_key)
ptr = rsa_key->qinv;
len = rsa_key->qinv_sz;
qat_rsa_drop_leading_zeros(&ptr, &len);
- if (!len)
+ if (!len || len > half_key_sz)
goto free_dq;
ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv,
GFP_KERNEL);
diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c
index c80baf1ad90b..89030e2711ce 100644
--- a/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c
+++ b/drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c
@@ -157,8 +157,8 @@ static inline int setup_sgio_components(struct pci_dev *pdev,
sg_cleanup:
for (j = 0; j < i; j++) {
if (list[j].dma_addr) {
- dma_unmap_single(&pdev->dev, list[i].dma_addr,
- list[i].size, DMA_BIDIRECTIONAL);
+ dma_unmap_single(&pdev->dev, list[j].dma_addr,
+ list[j].size, DMA_BIDIRECTIONAL);
}
list[j].dma_addr = 0;
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 347483f6fc5d..fc414306d7b6 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -12,7 +12,6 @@
* All rights reserved.
*/
-#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -255,6 +254,46 @@ static int init_device(struct device *dev)
return 0;
}
+static void dma_map_request(struct device *dev, struct talitos_request *request,
+ struct talitos_desc *desc, bool is_sec1)
+{
+ struct talitos_edesc *edesc =
+ container_of(desc, struct talitos_edesc, desc);
+ dma_addr_t dma_desc, prev_dma_desc;
+ struct talitos_edesc *prev_edesc = NULL;
+
+ if (is_sec1) {
+ while (edesc) {
+ edesc->desc.hdr1 = edesc->desc.hdr;
+
+ dma_desc = dma_map_single(dev, &edesc->desc.hdr1,
+ TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (!prev_edesc) {
+ request->dma_desc = dma_desc;
+ goto next;
+ }
+
+ /* Chain in any previous descriptors. */
+
+ prev_edesc->desc.next_desc = cpu_to_be32(dma_desc);
+
+ dma_sync_single_for_device(dev, prev_dma_desc,
+ TALITOS_DESC_SIZE,
+ DMA_TO_DEVICE);
+
+next:
+ prev_edesc = edesc;
+ prev_dma_desc = dma_desc;
+ edesc = edesc->next_desc;
+ }
+ } else {
+ request->dma_desc = dma_map_single(dev, desc, TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+ }
+}
+
/**
* talitos_submit - submits a descriptor to the device for processing
* @dev: the SEC device to be used
@@ -291,16 +330,7 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
request = &priv->chan[ch].fifo[head];
/* map descriptor and save caller data */
- if (is_sec1) {
- desc->hdr1 = desc->hdr;
- request->dma_desc = dma_map_single(dev, &desc->hdr1,
- TALITOS_DESC_SIZE,
- DMA_BIDIRECTIONAL);
- } else {
- request->dma_desc = dma_map_single(dev, desc,
- TALITOS_DESC_SIZE,
- DMA_BIDIRECTIONAL);
- }
+ dma_map_request(dev, request, desc, is_sec1);
request->callback = callback;
request->context = context;
@@ -322,19 +352,51 @@ static int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
return -EINPROGRESS;
}
-static __be32 get_request_hdr(struct talitos_request *request, bool is_sec1)
+static __be32 get_request_hdr(struct device *dev,
+ struct talitos_request *request, bool is_sec1)
{
struct talitos_edesc *edesc;
+ dma_addr_t dma_desc;
- if (!is_sec1)
- return request->desc->hdr;
+ if (!is_sec1) {
+ dma_sync_single_for_cpu(dev, request->dma_desc,
+ TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL);
- if (!request->desc->next_desc)
- return request->desc->hdr1;
+ return request->desc->hdr;
+ }
edesc = container_of(request->desc, struct talitos_edesc, desc);
+ dma_desc = request->dma_desc;
+ while (edesc->next_desc) {
+ dma_desc = be32_to_cpu(edesc->desc.next_desc);
+ edesc = edesc->next_desc;
+ }
+
+ dma_sync_single_for_cpu(dev, dma_desc, TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
- return ((struct talitos_desc *)(edesc->buf + edesc->dma_len))->hdr1;
+ return edesc->desc.hdr1;
+}
+
+static void dma_unmap_request(struct device *dev,
+ struct talitos_request *request, bool is_sec1)
+{
+ struct talitos_edesc *edesc;
+
+ if (is_sec1) {
+ dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+ edesc = container_of(request->desc, struct talitos_edesc, desc);
+ while (edesc->next_desc) {
+ dma_unmap_single(dev,
+ be32_to_cpu(edesc->desc.next_desc),
+ TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL);
+ edesc = edesc->next_desc;
+ }
+ } else {
+ dma_unmap_single(dev, request->dma_desc, TALITOS_DESC_SIZE,
+ DMA_BIDIRECTIONAL);
+ }
}
/*
@@ -358,7 +420,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
/* descriptors with their done bits set don't get the error */
rmb();
- hdr = get_request_hdr(request, is_sec1);
+ hdr = get_request_hdr(dev, request, is_sec1);
if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
status = 0;
@@ -368,9 +430,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
else
status = error;
- dma_unmap_single(dev, request->dma_desc,
- TALITOS_DESC_SIZE,
- DMA_BIDIRECTIONAL);
+ dma_unmap_request(dev, request, is_sec1);
/* copy entries so we can call callback outside lock */
saved_req.desc = request->desc;
@@ -459,14 +519,35 @@ DEF_TALITOS2_DONE(ch0, TALITOS2_ISR_CH_0_DONE)
DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE)
DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE)
+static __be32 search_desc_hdr_in_request(struct talitos_request *request,
+ dma_addr_t cur_desc, bool is_sec1)
+{
+ struct talitos_edesc *edesc;
+
+ if (request->dma_desc == cur_desc) {
+ return request->desc->hdr;
+ } else if (is_sec1) {
+ edesc = container_of(request->desc, struct talitos_edesc, desc);
+ while (edesc->next_desc) {
+ if (edesc->desc.next_desc == cpu_to_be32(cur_desc))
+ return edesc->next_desc->desc.hdr1;
+ edesc = edesc->next_desc;
+ }
+ }
+ return 0;
+}
+
/*
* locate current (offending) descriptor
*/
static __be32 current_desc_hdr(struct device *dev, int ch)
{
struct talitos_private *priv = dev_get_drvdata(dev);
+ bool is_sec1 = has_ftr_sec1(priv);
+ struct talitos_request *request;
int tail, iter;
dma_addr_t cur_desc;
+ __be32 hdr = 0;
cur_desc = ((u64)in_be32(priv->chan[ch].reg + TALITOS_CDPR)) << 32;
cur_desc |= in_be32(priv->chan[ch].reg + TALITOS_CDPR_LO);
@@ -477,27 +558,21 @@ static __be32 current_desc_hdr(struct device *dev, int ch)
}
tail = priv->chan[ch].tail;
-
iter = tail;
- while (priv->chan[ch].fifo[iter].dma_desc != cur_desc &&
- priv->chan[ch].fifo[iter].desc->next_desc != cpu_to_be32(cur_desc)) {
- iter = (iter + 1) & (priv->fifo_len - 1);
- if (iter == tail) {
- dev_err(dev, "couldn't locate current descriptor\n");
- return 0;
- }
- }
+ do {
+ request = &priv->chan[ch].fifo[iter];
- if (priv->chan[ch].fifo[iter].desc->next_desc == cpu_to_be32(cur_desc)) {
- struct talitos_edesc *edesc;
+ hdr = search_desc_hdr_in_request(request, cur_desc, is_sec1);
+ if (hdr)
+ break;
- edesc = container_of(priv->chan[ch].fifo[iter].desc,
- struct talitos_edesc, desc);
- return ((struct talitos_desc *)
- (edesc->buf + edesc->dma_len))->hdr;
- }
+ iter = (iter + 1) & (priv->fifo_len - 1);
+ } while (iter != tail);
+
+ if (!hdr)
+ dev_err(dev, "couldn't locate current descriptor\n");
- return priv->chan[ch].fifo[iter].desc->hdr;
+ return hdr;
}
/*
@@ -869,28 +944,20 @@ struct talitos_ahash_req_ctx {
u8 buf[2][HASH_MAX_BLOCK_SIZE];
int buf_idx;
unsigned int swinit;
- unsigned int first_desc;
- unsigned int last_desc;
+ unsigned int first_request;
unsigned int last_request;
unsigned int to_hash_later;
unsigned int nbuf;
struct scatterlist bufsl[2];
struct scatterlist *psrc;
-
- struct scatterlist request_bufsl[2];
- struct ahash_request *areq;
- struct scatterlist *request_sl;
- unsigned int remaining_ahash_request_bytes;
- unsigned int current_ahash_request_bytes;
- struct work_struct sec1_ahash_process_remaining;
};
struct talitos_export_state {
u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)];
u8 buf[HASH_MAX_BLOCK_SIZE];
unsigned int swinit;
- unsigned int first_desc;
- unsigned int last_desc;
+ unsigned int first_request;
+ unsigned int last_request;
unsigned int to_hash_later;
unsigned int nbuf;
};
@@ -1396,10 +1463,6 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
dma_len = 0;
}
alloc_len += icv_stashing ? authsize : 0;
-
- /* if its a ahash, add space for a second desc next to the first one */
- if (is_sec1 && !dst)
- alloc_len += sizeof(struct talitos_desc);
alloc_len += ivsize;
edesc = kmalloc(ALIGN(alloc_len, dma_get_cache_alignment()), flags);
@@ -1415,6 +1478,7 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
edesc->dst_nents = dst_nents;
edesc->iv_dma = iv_dma;
edesc->dma_len = dma_len;
+ edesc->next_desc = NULL;
if (dma_len)
edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0],
edesc->dma_len,
@@ -1715,39 +1779,37 @@ static void common_nonsnoop_hash_unmap(struct device *dev,
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
struct talitos_desc *desc = &edesc->desc;
- struct talitos_desc *desc2 = (struct talitos_desc *)
- (edesc->buf + edesc->dma_len);
unmap_single_talitos_ptr(dev, &desc->ptr[5], DMA_FROM_DEVICE);
- if (desc->next_desc &&
- desc->ptr[5].ptr != desc2->ptr[5].ptr)
- unmap_single_talitos_ptr(dev, &desc2->ptr[5], DMA_FROM_DEVICE);
- if (req_ctx->last_desc)
+
+ if (edesc->last && req_ctx->last_request)
memcpy(areq->result, req_ctx->hw_context,
crypto_ahash_digestsize(tfm));
- if (req_ctx->psrc)
- talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL, 0, 0);
+ if (edesc->src)
+ talitos_sg_unmap(dev, edesc, edesc->src, NULL, 0, 0);
/* When using hashctx-in, must unmap it. */
if (from_talitos_ptr_len(&desc->ptr[1], is_sec1))
unmap_single_talitos_ptr(dev, &desc->ptr[1],
DMA_TO_DEVICE);
- else if (desc->next_desc)
- unmap_single_talitos_ptr(dev, &desc2->ptr[1],
- DMA_TO_DEVICE);
-
- if (is_sec1 && req_ctx->nbuf)
- unmap_single_talitos_ptr(dev, &desc->ptr[3],
- DMA_TO_DEVICE);
if (edesc->dma_len)
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
DMA_BIDIRECTIONAL);
+}
+
+static void free_edesc_list_from(struct ahash_request *areq, struct talitos_edesc *edesc)
+{
+ struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq));
+ struct talitos_edesc *next;
- if (desc->next_desc)
- dma_unmap_single(dev, be32_to_cpu(desc->next_desc),
- TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL);
+ while (edesc) {
+ next = edesc->next_desc;
+ common_nonsnoop_hash_unmap(ctx->dev, edesc, areq);
+ kfree(edesc);
+ edesc = next;
+ }
}
static void ahash_done(struct device *dev,
@@ -1758,30 +1820,46 @@ static void ahash_done(struct device *dev,
struct talitos_edesc *edesc =
container_of(desc, struct talitos_edesc, desc);
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
+ bool is_sec1 = has_ftr_sec1(dev_get_drvdata(dev));
+ struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct talitos_edesc *next;
- if (!req_ctx->last_desc && req_ctx->to_hash_later) {
- /* Position any partial block for next update/final/finup */
- req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1;
- req_ctx->nbuf = req_ctx->to_hash_later;
- }
- common_nonsnoop_hash_unmap(dev, edesc, areq);
-
- kfree(edesc);
+ if (is_sec1) {
+ if (!req_ctx->last_request && req_ctx->to_hash_later) {
+ /* Position any partial block for next update/final/finup */
+ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1;
+ req_ctx->nbuf = req_ctx->to_hash_later;
+ }
- if (err) {
+ free_edesc_list_from(areq, edesc);
ahash_request_complete(areq, err);
- return;
- }
+ } else {
+ next = edesc->next_desc;
- req_ctx->remaining_ahash_request_bytes -=
- req_ctx->current_ahash_request_bytes;
+ common_nonsnoop_hash_unmap(dev, edesc, areq);
+ kfree(edesc);
- if (!req_ctx->remaining_ahash_request_bytes) {
- ahash_request_complete(areq, 0);
- return;
- }
+ if (err)
+ goto out;
- schedule_work(&req_ctx->sec1_ahash_process_remaining);
+ if (next) {
+ err = talitos_submit(dev, ctx->ch, &next->desc,
+ ahash_done, areq);
+ if (err != -EINPROGRESS)
+ goto out;
+ return;
+ }
+out:
+ if (!req_ctx->last_request && req_ctx->to_hash_later) {
+ /* Position any partial block for next update/final/finup */
+ req_ctx->buf_idx = (req_ctx->buf_idx + 1) & 1;
+ req_ctx->nbuf = req_ctx->to_hash_later;
+ }
+ if (err && next)
+ free_edesc_list_from(areq, next);
+ ahash_request_complete(areq, err);
+ }
}
/*
@@ -1805,18 +1883,15 @@ static void talitos_handle_buggy_hash(struct talitos_ctx *ctx,
(char *)padded_hash, DMA_TO_DEVICE);
}
-static int common_nonsnoop_hash(struct talitos_edesc *edesc,
- struct ahash_request *areq, unsigned int length,
- void (*callback) (struct device *dev,
- struct talitos_desc *desc,
- void *context, int error))
+static void common_nonsnoop_hash(struct talitos_edesc *edesc,
+ struct ahash_request *areq,
+ unsigned int length)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
struct device *dev = ctx->dev;
struct talitos_desc *desc = &edesc->desc;
- int ret;
bool sync_needed = false;
struct talitos_private *priv = dev_get_drvdata(dev);
bool is_sec1 = has_ftr_sec1(priv);
@@ -1825,7 +1900,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
/* first DWORD empty */
/* hash context in */
- if (!req_ctx->first_desc || req_ctx->swinit) {
+ if (!edesc->first || !req_ctx->first_request || req_ctx->swinit) {
map_single_talitos_ptr_nosync(dev, &desc->ptr[1],
req_ctx->hw_context_size,
req_ctx->hw_context,
@@ -1833,40 +1908,31 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
req_ctx->swinit = 0;
}
/* Indicate next op is not the first. */
- req_ctx->first_desc = 0;
+ req_ctx->first_request = 0;
/* HMAC key */
if (ctx->keylen)
to_talitos_ptr(&desc->ptr[2], ctx->dma_key, ctx->keylen,
is_sec1);
- if (is_sec1 && req_ctx->nbuf)
- length -= req_ctx->nbuf;
-
sg_count = edesc->src_nents ?: 1;
if (is_sec1 && sg_count > 1)
- sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length);
+ sg_copy_to_buffer(edesc->src, sg_count, edesc->buf, length);
else if (length)
- sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count,
- DMA_TO_DEVICE);
+ sg_count = dma_map_sg(dev, edesc->src, sg_count, DMA_TO_DEVICE);
+
/*
* data in
*/
- if (is_sec1 && req_ctx->nbuf) {
- map_single_talitos_ptr(dev, &desc->ptr[3], req_ctx->nbuf,
- req_ctx->buf[req_ctx->buf_idx],
- DMA_TO_DEVICE);
- } else {
- sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
- &desc->ptr[3], sg_count, 0, 0);
- if (sg_count > 1)
- sync_needed = true;
- }
+ sg_count = talitos_sg_map(dev, edesc->src, length, edesc, &desc->ptr[3],
+ sg_count, 0, 0);
+ if (sg_count > 1)
+ sync_needed = true;
/* fifth DWORD empty */
/* hash/HMAC out -or- hash context out */
- if (req_ctx->last_desc)
+ if (edesc->last && req_ctx->last_request)
map_single_talitos_ptr(dev, &desc->ptr[5],
crypto_ahash_digestsize(tfm),
req_ctx->hw_context, DMA_FROM_DEVICE);
@@ -1881,73 +1947,94 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0)
talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]);
- if (is_sec1 && req_ctx->nbuf && length) {
- struct talitos_desc *desc2 = (struct talitos_desc *)
- (edesc->buf + edesc->dma_len);
- dma_addr_t next_desc;
-
- memset(desc2, 0, sizeof(*desc2));
- desc2->hdr = desc->hdr;
- desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT;
- desc2->hdr1 = desc2->hdr;
- desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD;
- desc->hdr |= DESC_HDR_MODE0_MDEU_CONT;
- desc->hdr &= ~DESC_HDR_DONE_NOTIFY;
-
- if (desc->ptr[1].ptr)
- copy_talitos_ptr(&desc2->ptr[1], &desc->ptr[1],
- is_sec1);
- else
- map_single_talitos_ptr_nosync(dev, &desc2->ptr[1],
- req_ctx->hw_context_size,
- req_ctx->hw_context,
- DMA_TO_DEVICE);
- copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1);
- sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
- &desc2->ptr[3], sg_count, 0, 0);
- if (sg_count > 1)
- sync_needed = true;
- copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1);
- if (req_ctx->last_desc)
- map_single_talitos_ptr_nosync(dev, &desc->ptr[5],
- req_ctx->hw_context_size,
- req_ctx->hw_context,
- DMA_FROM_DEVICE);
-
- next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE,
- DMA_BIDIRECTIONAL);
- desc->next_desc = cpu_to_be32(next_desc);
- }
-
if (sync_needed)
dma_sync_single_for_device(dev, edesc->dma_link_tbl,
edesc->dma_len, DMA_BIDIRECTIONAL);
-
- ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
- if (ret != -EINPROGRESS) {
- common_nonsnoop_hash_unmap(dev, edesc, areq);
- kfree(edesc);
- }
- return ret;
}
static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq,
+ struct scatterlist *src,
unsigned int nbytes)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
+
+ return talitos_edesc_alloc(ctx->dev, src, NULL, NULL, 0,
+ nbytes, 0, 0, 0, areq->base.flags, false);
+}
+
+static struct talitos_edesc *
+ahash_process_req_prepare(struct ahash_request *areq, unsigned int nbytes,
+ unsigned int blocksize, bool is_sec1)
+{
+ struct talitos_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(areq));
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
- struct talitos_private *priv = dev_get_drvdata(ctx->dev);
- bool is_sec1 = has_ftr_sec1(priv);
+ struct talitos_edesc *first = NULL, *prev_edesc = NULL, *edesc;
+ size_t desc_max = is_sec1 ? TALITOS1_MAX_DATA_LEN :
+ TALITOS2_MAX_DATA_LEN;
+ struct scatterlist tmp[2];
+ size_t to_hash_this_desc;
+ struct scatterlist *src;
+ size_t offset = 0;
+
+ do {
+ src = scatterwalk_ffwd(tmp, req_ctx->psrc, offset);
+
+ to_hash_this_desc =
+ min(nbytes, ALIGN_DOWN(desc_max, blocksize));
+
+ /* Allocate extended descriptor */
+ edesc = ahash_edesc_alloc(areq, src, to_hash_this_desc);
+ if (IS_ERR(edesc)) {
+ if (first)
+ free_edesc_list_from(areq, first);
+ return edesc;
+ }
- if (is_sec1)
- nbytes -= req_ctx->nbuf;
+ edesc->src =
+ scatterwalk_ffwd(edesc->bufsl, req_ctx->psrc, offset);
+ edesc->desc.hdr = ctx->desc_hdr_template;
+ edesc->first = offset == 0;
+ edesc->last = nbytes - to_hash_this_desc == 0;
- return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0,
- nbytes, 0, 0, 0, areq->base.flags, false);
+ /* On last one, request SEC to pad; otherwise continue */
+ if (req_ctx->last_request && edesc->last)
+ edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD;
+ else
+ edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT;
+
+ /* request SEC to INIT hash. */
+ if (req_ctx->first_request && edesc->first && !req_ctx->swinit)
+ edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT;
+
+ /*
+ * When the tfm context has a keylen, it's an HMAC.
+ * A first or last (ie. not middle) descriptor must request HMAC.
+ */
+ if (ctx->keylen && ((req_ctx->first_request && edesc->first) ||
+ (req_ctx->last_request && edesc->last)))
+ edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC;
+
+ /* clear the DN bit */
+ if (is_sec1 && !edesc->last)
+ edesc->desc.hdr &= ~DESC_HDR_DONE_NOTIFY;
+
+ common_nonsnoop_hash(edesc, areq, to_hash_this_desc);
+
+ offset += to_hash_this_desc;
+ nbytes -= to_hash_this_desc;
+
+ if (!prev_edesc)
+ first = edesc;
+ else
+ prev_edesc->next_desc = edesc;
+ prev_edesc = edesc;
+ } while (nbytes);
+
+ return first;
}
-static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes)
+static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
@@ -1955,23 +2042,23 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes
struct talitos_edesc *edesc;
unsigned int blocksize =
crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+ bool is_sec1 = has_ftr_sec1(dev_get_drvdata(ctx->dev));
unsigned int nbytes_to_hash;
unsigned int to_hash_later;
unsigned int nsg;
int nents;
struct device *dev = ctx->dev;
- struct talitos_private *priv = dev_get_drvdata(dev);
- bool is_sec1 = has_ftr_sec1(priv);
u8 *ctx_buf = req_ctx->buf[req_ctx->buf_idx];
+ int ret;
- if (!req_ctx->last_desc && (nbytes + req_ctx->nbuf <= blocksize)) {
+ if (!req_ctx->last_request && (nbytes + req_ctx->nbuf <= blocksize)) {
/* Buffer up to one whole block */
- nents = sg_nents_for_len(req_ctx->request_sl, nbytes);
+ nents = sg_nents_for_len(areq->src, nbytes);
if (nents < 0) {
dev_err(dev, "Invalid number of src SG.\n");
return nents;
}
- sg_copy_to_buffer(req_ctx->request_sl, nents,
+ sg_copy_to_buffer(areq->src, nents,
ctx_buf + req_ctx->nbuf, nbytes);
req_ctx->nbuf += nbytes;
return 0;
@@ -1981,7 +2068,7 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes
nbytes_to_hash = nbytes + req_ctx->nbuf;
to_hash_later = nbytes_to_hash & (blocksize - 1);
- if (req_ctx->last_desc)
+ if (req_ctx->last_request)
to_hash_later = 0;
else if (to_hash_later)
/* There is a partial block. Hash the full block(s) now */
@@ -1993,123 +2080,39 @@ static int ahash_process_req_one(struct ahash_request *areq, unsigned int nbytes
}
/* Chain in any previously buffered data */
- if (!is_sec1 && req_ctx->nbuf) {
+ if (req_ctx->nbuf) {
nsg = (req_ctx->nbuf < nbytes_to_hash) ? 2 : 1;
sg_init_table(req_ctx->bufsl, nsg);
sg_set_buf(req_ctx->bufsl, ctx_buf, req_ctx->nbuf);
if (nsg > 1)
- sg_chain(req_ctx->bufsl, 2, req_ctx->request_sl);
+ sg_chain(req_ctx->bufsl, 2, areq->src);
req_ctx->psrc = req_ctx->bufsl;
- } else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) {
- int offset;
-
- if (nbytes_to_hash > blocksize)
- offset = blocksize - req_ctx->nbuf;
- else
- offset = nbytes_to_hash - req_ctx->nbuf;
- nents = sg_nents_for_len(req_ctx->request_sl, offset);
- if (nents < 0) {
- dev_err(dev, "Invalid number of src SG.\n");
- return nents;
- }
- sg_copy_to_buffer(req_ctx->request_sl, nents,
- ctx_buf + req_ctx->nbuf, offset);
- req_ctx->nbuf += offset;
- req_ctx->psrc = scatterwalk_ffwd(req_ctx->bufsl, req_ctx->request_sl,
- offset);
} else
- req_ctx->psrc = req_ctx->request_sl;
+ req_ctx->psrc = areq->src;
if (to_hash_later) {
- nents = sg_nents_for_len(req_ctx->request_sl, nbytes);
+ nents = sg_nents_for_len(areq->src, nbytes);
if (nents < 0) {
dev_err(dev, "Invalid number of src SG.\n");
return nents;
}
- sg_pcopy_to_buffer(req_ctx->request_sl, nents,
+ sg_pcopy_to_buffer(areq->src, nents,
req_ctx->buf[(req_ctx->buf_idx + 1) & 1],
to_hash_later,
nbytes - to_hash_later);
}
req_ctx->to_hash_later = to_hash_later;
- /* Allocate extended descriptor */
- edesc = ahash_edesc_alloc(req_ctx->areq, nbytes_to_hash);
+ edesc = ahash_process_req_prepare(areq, nbytes_to_hash, blocksize,
+ is_sec1);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
- edesc->desc.hdr = ctx->desc_hdr_template;
-
- /* On last one, request SEC to pad; otherwise continue */
- if (req_ctx->last_desc)
- edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_PAD;
- else
- edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_CONT;
-
- /* request SEC to INIT hash. */
- if (req_ctx->first_desc && !req_ctx->swinit)
- edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT;
-
- /* When the tfm context has a keylen, it's an HMAC.
- * A first or last (ie. not middle) descriptor must request HMAC.
- */
- if (ctx->keylen && (req_ctx->first_desc || req_ctx->last_desc))
- edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC;
+ ret = talitos_submit(dev, ctx->ch, &edesc->desc, ahash_done, areq);
+ if (ret != -EINPROGRESS)
+ free_edesc_list_from(areq, edesc);
- return common_nonsnoop_hash(edesc, req_ctx->areq, nbytes_to_hash, ahash_done);
-}
-
-static void sec1_ahash_process_remaining(struct work_struct *work)
-{
- struct talitos_ahash_req_ctx *req_ctx =
- container_of(work, struct talitos_ahash_req_ctx,
- sec1_ahash_process_remaining);
- int err = 0;
-
- req_ctx->request_sl = scatterwalk_ffwd(req_ctx->request_bufsl,
- req_ctx->request_sl, TALITOS1_MAX_DATA_LEN);
-
- if (req_ctx->remaining_ahash_request_bytes > TALITOS1_MAX_DATA_LEN)
- req_ctx->current_ahash_request_bytes = TALITOS1_MAX_DATA_LEN;
- else {
- req_ctx->current_ahash_request_bytes =
- req_ctx->remaining_ahash_request_bytes;
-
- if (req_ctx->last_request)
- req_ctx->last_desc = 1;
- }
-
- err = ahash_process_req_one(req_ctx->areq,
- req_ctx->current_ahash_request_bytes);
-
- if (err != -EINPROGRESS)
- ahash_request_complete(req_ctx->areq, err);
-}
-
-static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
-{
- struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
- struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
- struct device *dev = ctx->dev;
- struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
- struct talitos_private *priv = dev_get_drvdata(dev);
- bool is_sec1 = has_ftr_sec1(priv);
-
- req_ctx->areq = areq;
- req_ctx->request_sl = areq->src;
- req_ctx->remaining_ahash_request_bytes = nbytes;
-
- if (is_sec1) {
- if (nbytes > TALITOS1_MAX_DATA_LEN)
- nbytes = TALITOS1_MAX_DATA_LEN;
- else if (req_ctx->last_request)
- req_ctx->last_desc = 1;
- }
-
- req_ctx->current_ahash_request_bytes = nbytes;
-
- return ahash_process_req_one(req_ctx->areq,
- req_ctx->current_ahash_request_bytes);
+ return ret;
}
static int ahash_init(struct ahash_request *areq)
@@ -2124,15 +2127,13 @@ static int ahash_init(struct ahash_request *areq)
/* Initialize the context */
req_ctx->buf_idx = 0;
req_ctx->nbuf = 0;
- req_ctx->first_desc = 1; /* first_desc indicates h/w must init its context */
+ req_ctx->first_request = 1;
req_ctx->swinit = 0; /* assume h/w init of context */
size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE)
? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256
: TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512;
req_ctx->hw_context_size = size;
req_ctx->last_request = 0;
- req_ctx->last_desc = 0;
- INIT_WORK(&req_ctx->sec1_ahash_process_remaining, sec1_ahash_process_remaining);
dma = dma_map_single(dev, req_ctx->hw_context, req_ctx->hw_context_size,
DMA_TO_DEVICE);
@@ -2197,13 +2198,14 @@ static int ahash_finup(struct ahash_request *areq)
static int ahash_digest(struct ahash_request *areq)
{
- struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
- struct crypto_ahash *ahash = crypto_ahash_reqtfm(areq);
-
- ahash->init(areq);
- req_ctx->last = 1;
+ ahash_init(areq);
+ return ahash_finup(areq);
+}
- return ahash_process_req(areq, areq->nbytes);
+static int ahash_digest_sha224_swinit(struct ahash_request *areq)
+{
+ ahash_init_sha224_swinit(areq);
+ return ahash_finup(areq);
}
static int ahash_export(struct ahash_request *areq, void *out)
@@ -2223,8 +2225,8 @@ static int ahash_export(struct ahash_request *areq, void *out)
req_ctx->hw_context_size);
memcpy(export->buf, req_ctx->buf[req_ctx->buf_idx], req_ctx->nbuf);
export->swinit = req_ctx->swinit;
- export->first_desc = req_ctx->first_desc;
- export->last_desc = req_ctx->last_desc;
+ export->first_request = req_ctx->first_request;
+ export->last_request = req_ctx->last_request;
export->to_hash_later = req_ctx->to_hash_later;
export->nbuf = req_ctx->nbuf;
@@ -2249,8 +2251,8 @@ static int ahash_import(struct ahash_request *areq, const void *in)
memcpy(req_ctx->hw_context, export->hw_context, size);
memcpy(req_ctx->buf[0], export->buf, export->nbuf);
req_ctx->swinit = export->swinit;
- req_ctx->first_desc = export->first_desc;
- req_ctx->last_desc = export->last_desc;
+ req_ctx->first_request = export->first_request;
+ req_ctx->last_request = export->last_request;
req_ctx->to_hash_later = export->to_hash_later;
req_ctx->nbuf = export->nbuf;
@@ -3320,6 +3322,8 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
(!strcmp(alg->cra_name, "sha224") ||
!strcmp(alg->cra_name, "hmac(sha224)"))) {
t_alg->algt.alg.hash.init = ahash_init_sha224_swinit;
+ t_alg->algt.alg.hash.digest =
+ ahash_digest_sha224_swinit;
t_alg->algt.desc_hdr_template =
DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
DESC_HDR_SEL0_MDEUA |
diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h
index 1a93ee355929..11f0eb6a41db 100644
--- a/drivers/crypto/talitos.h
+++ b/drivers/crypto/talitos.h
@@ -44,11 +44,17 @@ struct talitos_desc {
/*
* talitos_edesc - s/w-extended descriptor
+ * @bufsl: scatterlist buffer
+ * @src: pointer to input scatterlist
+ * @first: first descriptor of a chain
+ * @last: last descriptor of a chain
+ *
* @src_nents: number of segments in input scatterlist
* @dst_nents: number of segments in output scatterlist
* @iv_dma: dma address of iv for checking continuity and link table
* @dma_len: length of dma mapped link_tbl space
* @dma_link_tbl: bus physical address of link_tbl/buf
+ * @next_desc: next descriptor
* @desc: h/w descriptor
* @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2)
* @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1)
@@ -58,11 +64,17 @@ struct talitos_desc {
* of link_tbl data
*/
struct talitos_edesc {
+ struct scatterlist bufsl[2];
+ struct scatterlist *src;
+ int first;
+ int last;
+
int src_nents;
int dst_nents;
dma_addr_t iv_dma;
int dma_len;
dma_addr_t dma_link_tbl;
+ struct talitos_edesc *next_desc;
struct talitos_desc desc;
union {
DECLARE_FLEX_ARRAY(struct talitos_ptr, link_tbl);
diff --git a/drivers/dax/kmem.c b/drivers/dax/kmem.c
index c57acb73e3db..60d8bb7acf51 100644
--- a/drivers/dax/kmem.c
+++ b/drivers/dax/kmem.c
@@ -193,6 +193,12 @@ static void dev_dax_kmem_remove(struct dev_dax *dev_dax)
if (rc)
continue;
+ /* range was never added during probe */
+ if (!data->res[i]) {
+ success++;
+ continue;
+ }
+
rc = remove_memory(range.start, range_len(&range));
if (rc == 0) {
remove_resource(data->res[i]);
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index 980975ffc55d..1f05c486ac32 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -27,6 +27,7 @@ struct udmabuf {
pgoff_t pagecount;
struct page **pages;
struct sg_table *sg;
+ enum dma_data_direction sg_dir;
struct miscdevice *device;
};
@@ -139,7 +140,7 @@ static void release_udmabuf(struct dma_buf *buf)
pgoff_t pg;
if (ubuf->sg)
- put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL);
+ put_sg_table(dev, ubuf->sg, ubuf->sg_dir);
for (pg = 0; pg < ubuf->pagecount; pg++)
put_page(ubuf->pages[pg]);
@@ -159,6 +160,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
if (IS_ERR(ubuf->sg)) {
ret = PTR_ERR(ubuf->sg);
ubuf->sg = NULL;
+ } else {
+ ubuf->sg_dir = direction;
}
} else {
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 491b22240221..b1c9187602d5 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -905,11 +905,12 @@ void dma_release_channel(struct dma_chan *chan)
mutex_lock(&dma_list_mutex);
WARN_ONCE(chan->client_count != 1,
"chan reference count %d != 1\n", chan->client_count);
- dma_chan_put(chan);
/* drop PRIVATE cap enabled by __dma_request_channel() */
if (--chan->device->privatecnt == 0)
dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask);
+ dma_chan_put(chan);
+
if (chan->slave) {
sysfs_remove_link(&chan->dev->device.kobj, DMA_SLAVE_NAME);
sysfs_remove_link(&chan->slave->kobj, chan->name);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b15..cc783e076671 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -108,7 +108,7 @@ struct dw_edma {
struct dw_edma_chan *chan;
- raw_spinlock_t lock; /* Only for legacy */
+ raw_spinlock_t lock; /* Protect v0 shared registers */
struct dw_edma_chip *chip;
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index b75fdaffad9a..2850a9df80f5 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
+ unsigned long flags;
u32 tmp;
dw_edma_v0_core_write_chunk(chunk);
@@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
}
}
/* Interrupt unmask - done, abort */
+ raw_spin_lock_irqsave(&dw->lock, flags);
+
tmp = GET_RW_32(dw, chan->dir, int_mask);
tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
@@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
+
+ raw_spin_unlock_irqrestore(&dw->lock, flags);
+
/* Channel control */
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
(DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 51012bd39900..2d1600b797f8 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -2321,7 +2321,9 @@ static int sdma_probe(struct platform_device *pdev)
goto err_register;
}
- spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus");
+ struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np);
+
+ spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus");
ret = of_address_to_resource(spba_bus, 0, &spba_res);
if (!ret) {
sdma->spba_start_addr = spba_res.start;
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index 36830efd13f7..a6fc151f8cc1 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -2252,6 +2252,7 @@ static int gpi_probe(struct platform_device *pdev)
/* clear and Set capabilities */
dma_cap_zero(gpi_dev->dma_device.cap_mask);
dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask);
+ dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask);
/* configure dmaengine apis */
gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 029f45f7e37f..e5ce733b5a6e 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -825,6 +825,13 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
* len to calculate the optimum burst size
*/
burst_byte = burst_size ? burst_size * slave_bw : len;
+
+ /*
+ * Find the largest burst size that evenly divides the transfer length.
+ * The hardware requires the transfer length to be a multiple of the
+ * burst size - partial bursts are not supported.
+ */
+ burst_byte = min(burst_byte, 1U << __ffs(len));
burst_mmio_width = burst_byte / 4;
if (burst_mmio_width < TEGRA_GPCDMA_MMIOSEQ_BURST_MIN)
diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index 3c70d8607497..ac345aae4942 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -88,6 +88,7 @@ static struct res_config *res_cfg;
static int retry_rd_err_log;
static int decoding_via_mca;
static bool mem_cfg_2lm;
+static bool no_adxl;
static u32 offsets_scrub_icx[] = {0x22c60, 0x22c54, 0x22c5c, 0x22c58, 0x22c28, 0x20ed8};
static u32 offsets_scrub_spr[] = {0x22c60, 0x22c54, 0x22f08, 0x22c58, 0x22c28, 0x20ed8};
@@ -1173,8 +1174,14 @@ static int __init i10nm_init(void)
}
rc = skx_adxl_get();
- if (rc)
- goto fail;
+ if (rc) {
+ /* Decoding errors via MCA banks for 2LM isn't supported yet */
+ if (rc != -ENODEV || mem_cfg_2lm)
+ goto fail;
+ i10nm_printk(KERN_INFO, "ADXL not found, falling back to MCA-based decoding.\n");
+ no_adxl = true;
+ decoding_via_mca = true;
+ }
opstate_init();
mce_register_decode_chain(&i10nm_mce_dec);
@@ -1208,7 +1215,8 @@ static void __exit i10nm_exit(void)
teardown_i10nm_debug();
mce_unregister_decode_chain(&i10nm_mce_dec);
- skx_adxl_put();
+ if (!no_adxl)
+ skx_adxl_put();
skx_remove();
}
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index 9e43aed72bd9..6b492ac8ba48 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -417,6 +417,9 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
rows = numrow(mtr);
cols = imc->hbm_mc ? 6 : numcol(mtr);
+ if (ranks < 0 || rows < 0 || cols < 0)
+ return 0;
+
if (imc->hbm_mc) {
banks = 32;
mtype = MEM_HBM2;
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 7a4d1a478e33..21f3a9dae072 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -257,9 +257,10 @@ static void fwnet_header_cache_update(struct hh_cache *hh,
memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);
}
-static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int fwnet_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr)
{
- memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN);
+ memcpy(haddr, dev->dev_addr, FWNET_ALEN);
return FWNET_ALEN;
}
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 356e83631664..6e01f70c3a9e 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -194,8 +194,12 @@ scmi_power_name_get(const struct scmi_protocol_handle *ph,
u32 domain)
{
struct scmi_power_info *pi = ph->get_priv(ph);
- struct power_dom_info *dom = pi->dom_info + domain;
+ struct power_dom_info *dom;
+ if (domain >= pi->num_domains)
+ return "unknown";
+
+ dom = pi->dom_info + domain;
return dom->name;
}
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 0b5853fa9d87..4e3e937cb92c 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -777,7 +777,7 @@ static int scmi_sensor_config_get(const struct scmi_protocol_handle *ph,
if (!ret) {
struct scmi_sensor_info *s = si->sensors + sensor_id;
- *sensor_config = get_unaligned_le64(t->rx.buf);
+ *sensor_config = get_unaligned_le32(t->rx.buf);
s->sensor_config = *sensor_config;
}
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index f1ced28793d2..af618609d369 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -1117,6 +1117,8 @@ static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max)
return -EINVAL;
size += next * sizeof(u64);
+ if (size > max)
+ return -EINVAL;
if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v))
return size;
diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c
index 2a82c726d6e5..a44cec54ba99 100644
--- a/drivers/fpga/microchip-spi.c
+++ b/drivers/fpga/microchip-spi.c
@@ -116,6 +116,9 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr,
}
header_size = *(buf + MPF_HEADER_SIZE_OFFSET);
+ if (!header_size)
+ return -EINVAL;
+
if (header_size > count) {
info->header_size = header_size;
return -EAGAIN;
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index b24e349deed5..4cd4ac17fff6 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -93,7 +93,7 @@ struct sprd_eic {
struct gpio_chip chip;
void __iomem *base[SPRD_EIC_MAX_BANK];
enum sprd_eic_type type;
- spinlock_t lock;
+ raw_spinlock_t lock;
int irq;
};
@@ -145,7 +145,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset,
unsigned long flags;
u32 tmp;
- spin_lock_irqsave(&sprd_eic->lock, flags);
+ raw_spin_lock_irqsave(&sprd_eic->lock, flags);
tmp = readl_relaxed(base + reg);
if (val)
@@ -154,7 +154,7 @@ static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset,
tmp &= ~BIT(SPRD_EIC_BIT(offset));
writel_relaxed(tmp, base + reg);
- spin_unlock_irqrestore(&sprd_eic->lock, flags);
+ raw_spin_unlock_irqrestore(&sprd_eic->lock, flags);
}
static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
@@ -618,7 +618,7 @@ static int sprd_eic_probe(struct platform_device *pdev)
if (!sprd_eic)
return -ENOMEM;
- spin_lock_init(&sprd_eic->lock);
+ raw_spin_lock_init(&sprd_eic->lock);
sprd_eic->type = pdata->type;
sprd_eic->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
index f54ca5a1775e..66ae11001704 100644
--- a/drivers/gpio/gpio-f7188x.c
+++ b/drivers/gpio/gpio-f7188x.c
@@ -48,7 +48,8 @@
/*
* Nuvoton devices.
*/
-#define SIO_NCT6126D_ID 0xD283 /* NCT6126D chipset ID */
+#define SIO_NCT6126D_VER_A_ID 0xD283 /* NCT6126D version A chipset ID */
+#define SIO_NCT6126D_VER_B_ID 0xD284 /* NCT6126D version B chipset ID */
#define SIO_LD_GPIO_NUVOTON 0x07 /* GPIO logical device */
@@ -559,7 +560,8 @@ static int __init f7188x_find(int addr, struct f7188x_sio *sio)
case SIO_F81865_ID:
sio->type = f81865;
break;
- case SIO_NCT6126D_ID:
+ case SIO_NCT6126D_VER_A_ID:
+ case SIO_NCT6126D_VER_B_ID:
sio->device = SIO_LD_GPIO_NUVOTON;
sio->type = nct6126d;
break;
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index a40bd56673fe..5f12ce9ac28c 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -268,6 +268,7 @@ static int __init egpio_probe(struct platform_device *pdev)
struct gpio_chip *chip;
unsigned int irq, irq_end;
int i;
+ int ret;
/* Initialize ei data structure. */
ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
@@ -331,7 +332,10 @@ static int __init egpio_probe(struct platform_device *pdev)
chip->base = pdata->chip[i].gpio_base;
chip->ngpio = pdata->chip[i].num_gpios;
- gpiochip_add_data(chip, &ei->chip[i]);
+ ret = devm_gpiochip_add_data(&pdev->dev, chip, &ei->chip[i]);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register gpiochip %d\n", i);
}
/* Set initial pin values */
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c
index ed29b07d16c1..a662989ff21e 100644
--- a/drivers/gpio/gpio-mlxbf3.c
+++ b/drivers/gpio/gpio-mlxbf3.c
@@ -252,7 +252,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
if (ret)
- dev_err_probe(dev, ret, "Failed adding memory mapped gpiochip\n");
+ return dev_err_probe(dev, ret,
+ "Failed adding memory mapped gpiochip\n");
return 0;
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index ee184720de57..9ca659cff438 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -1232,7 +1232,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
BUG();
}
- devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
+ err = devm_gpiochip_add_data(&pdev->dev, &mvchip->chip, mvchip);
+ if (err)
+ return dev_err_probe(&pdev->dev, err,
+ "failed to register gpiochip\n");
/* Some MVEBU SoCs have simple PWM support for GPIO lines */
if (IS_REACHABLE(CONFIG_PWM)) {
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index 28dba7048509..41a62353822d 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -117,6 +117,24 @@ static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset)
return ret;
}
+static int palmas_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct palmas_gpio *pg = gpiochip_get_data(gc);
+ struct palmas *palmas = pg->palmas;
+ unsigned int val;
+ unsigned int reg;
+ int ret;
+ int gpio16 = (offset/8);
+
+ offset %= 8;
+ reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR;
+ ret = palmas_read(palmas, PALMAS_GPIO_BASE, reg, &val);
+ if (ret)
+ return ret;
+
+ return (val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct palmas_gpio *pg = gpiochip_get_data(gc);
@@ -165,6 +183,7 @@ static int palmas_gpio_probe(struct platform_device *pdev)
palmas_gpio->gpio_chip.can_sleep = true;
palmas_gpio->gpio_chip.direction_input = palmas_gpio_input;
palmas_gpio->gpio_chip.direction_output = palmas_gpio_output;
+ palmas_gpio->gpio_chip.get_direction = palmas_gpio_get_direction;
palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq;
palmas_gpio->gpio_chip.set = palmas_gpio_set;
palmas_gpio->gpio_chip.get = palmas_gpio_get;
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index e48392074e4b..a53946859c8c 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -38,7 +38,7 @@
struct sch_gpio {
struct gpio_chip chip;
- spinlock_t lock;
+ raw_spinlock_t lock;
unsigned short iobase;
unsigned short resume_base;
@@ -102,9 +102,9 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned int gpio_num)
struct sch_gpio *sch = gpiochip_get_data(gc);
unsigned long flags;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GIO, 1);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
return 0;
}
@@ -120,9 +120,9 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val)
struct sch_gpio *sch = gpiochip_get_data(gc);
unsigned long flags;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GLV, val);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
}
static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
@@ -131,9 +131,9 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
struct sch_gpio *sch = gpiochip_get_data(gc);
unsigned long flags;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GIO, 0);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
/*
* according to the datasheet, writing to the level register has no
@@ -193,14 +193,14 @@ static int sch_irq_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GTPE, rising);
sch_gpio_reg_set(sch, gpio_num, GTNE, falling);
irq_set_handler_locked(d, handle_edge_irq);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
return 0;
}
@@ -212,9 +212,9 @@ static void sch_irq_ack(struct irq_data *d)
irq_hw_number_t gpio_num = irqd_to_hwirq(d);
unsigned long flags;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GTS, 1);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
}
static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num, int val)
@@ -222,9 +222,9 @@ static void sch_irq_mask_unmask(struct gpio_chip *gc, irq_hw_number_t gpio_num,
struct sch_gpio *sch = gpiochip_get_data(gc);
unsigned long flags;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
sch_gpio_reg_set(sch, gpio_num, GGPE, val);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
}
static void sch_irq_mask(struct irq_data *d)
@@ -265,12 +265,12 @@ static u32 sch_gpio_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context)
int offset;
u32 ret;
- spin_lock_irqsave(&sch->lock, flags);
+ raw_spin_lock_irqsave(&sch->lock, flags);
core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS);
resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS);
- spin_unlock_irqrestore(&sch->lock, flags);
+ raw_spin_unlock_irqrestore(&sch->lock, flags);
pending = (resume_status << sch->resume_base) | core_status;
for_each_set_bit(offset, &pending, sch->chip.ngpio)
@@ -336,7 +336,7 @@ static int sch_gpio_probe(struct platform_device *pdev)
pdev->name))
return -EBUSY;
- spin_lock_init(&sch->lock);
+ raw_spin_lock_init(&sch->lock);
sch->iobase = res->start;
sch->chip = sch_gpio_chip;
sch->chip.label = dev_name(&pdev->dev);
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c
index fad979797486..c40fb5ec93e6 100644
--- a/drivers/gpio/gpio-timberdale.c
+++ b/drivers/gpio/gpio-timberdale.c
@@ -228,7 +228,7 @@ static int timbgpio_probe(struct platform_device *pdev)
tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
if (!tgpio)
- return -EINVAL;
+ return -ENOMEM;
tgpio->irq_base = pdata->irq_base;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 9c0e4e4bf3f6..fdfed86c8432 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -1823,13 +1823,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu(
mutex_lock(&mem->lock);
- /* Unpin MMIO/DOORBELL BO's that were pinned during allocation */
- if (mem->alloc_flags &
- (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL |
- KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) {
- amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo);
- }
-
mapped_to_gpu_memory = mem->mapped_to_gpu_memory;
is_imported = mem->is_imported;
mutex_unlock(&mem->lock);
@@ -1843,6 +1836,15 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu(
return -EBUSY;
}
+ /* At this point the BO is guaranteed to be freed, so unpin the
+ * MMIO/DOORBELL BOs that were pinned during allocation.
+ */
+ if (mem->alloc_flags &
+ (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL |
+ KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) {
+ amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo);
+ }
+
/* Make sure restore workers don't access the BO any more */
mutex_lock(&process_info->lock);
list_del(&mem->validate_list);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index ad2723206a18..8672838fb67c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1168,7 +1168,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev)
pci_release_resource(adev->pdev, 0);
- r = pci_resize_resource(adev->pdev, 0, rbar_size);
+ r = pci_resize_resource(adev->pdev, 0, rbar_size,
+ (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5
+ : 1 << 2);
if (r == -ENOSPC)
DRM_INFO("Not enough PCI address space for a large BAR.");
else if (r && r != -ENOTSUPP)
@@ -3590,6 +3592,8 @@ int amdgpu_device_init(struct amdgpu_device *adev,
mutex_init(&adev->pm.stable_pstate_ctx_lock);
mutex_init(&adev->benchmark_mutex);
+ spin_lock_init(&adev->irq.lock);
+
amdgpu_device_init_apu_flags(adev);
r = amdgpu_device_check_arguments(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
index ea75c2b2bbb1..055aa0418a73 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
@@ -299,7 +299,6 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
int pages)
{
unsigned t;
- unsigned p;
int i, j;
u64 page_base;
/* Starting from VEGA10, system bit must be 0 to mean invalid. */
@@ -313,8 +312,7 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
return;
t = offset / AMDGPU_GPU_PAGE_SIZE;
- p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
- for (i = 0; i < pages; i++, p++) {
+ for (i = 0; i < pages; i++) {
page_base = adev->dummy_page_addr;
if (!adev->gart.ptr)
continue;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index a1b15d0d6c48..73a00f30da9c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -27,6 +27,7 @@
*/
#include <linux/ktime.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/pagemap.h>
#include <linux/pci.h>
#include <linux/dma-buf.h>
@@ -878,13 +879,14 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data,
return r;
}
-static int amdgpu_gem_align_pitch(struct amdgpu_device *adev,
- int width,
- int cpp,
- bool tiled)
+static unsigned int amdgpu_gem_align_pitch(struct amdgpu_device *adev,
+ unsigned int width,
+ unsigned int cpp,
+ bool tiled)
{
- int aligned = width;
- int pitch_mask = 0;
+ unsigned int aligned = width;
+ unsigned int pitch_mask = 0;
+ unsigned int pitch;
switch (cpp) {
case 1:
@@ -899,9 +901,12 @@ static int amdgpu_gem_align_pitch(struct amdgpu_device *adev,
break;
}
- aligned += pitch_mask;
+ if (check_add_overflow(aligned, pitch_mask, &aligned))
+ return 0;
aligned &= ~pitch_mask;
- return aligned * cpp;
+ if (check_mul_overflow(aligned, cpp, &pitch))
+ return 0;
+ return pitch;
}
int amdgpu_mode_dumb_create(struct drm_file *file_priv,
@@ -928,8 +933,12 @@ int amdgpu_mode_dumb_create(struct drm_file *file_priv,
args->pitch = amdgpu_gem_align_pitch(adev, args->width,
DIV_ROUND_UP(args->bpp, 8), 0);
+ if (!args->pitch)
+ return -EINVAL;
args->size = (u64)args->pitch * args->height;
args->size = ALIGN(args->size, PAGE_SIZE);
+ if (!args->size)
+ return -EINVAL;
domain = amdgpu_bo_get_preferred_domain(adev,
amdgpu_display_supported_domains(adev, flags));
r = amdgpu_gem_object_create(adev, args->size, 0, domain, flags,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
index 5978edf7ea71..79b053dc1733 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
@@ -273,8 +273,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev)
int r = 0;
unsigned int irq;
- spin_lock_init(&adev->irq.lock);
-
/* Enable MSI if not disabled by module parameter */
adev->irq.msi_enabled = false;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
index 8d9ff9e151de..9a7f7d2b2767 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
@@ -89,6 +89,7 @@ int amdgpu_mca_mp0_ras_sw_init(struct amdgpu_device *adev)
strcpy(ras->ras_block.ras_comm.name, "mca.mp0");
ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA;
+ ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP0;
ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE;
adev->mca.mp0.ras_if = &ras->ras_block.ras_comm;
@@ -113,6 +114,7 @@ int amdgpu_mca_mp1_ras_sw_init(struct amdgpu_device *adev)
strcpy(ras->ras_block.ras_comm.name, "mca.mp1");
ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA;
+ ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MP1;
ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE;
adev->mca.mp1.ras_if = &ras->ras_block.ras_comm;
@@ -137,6 +139,7 @@ int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev)
strcpy(ras->ras_block.ras_comm.name, "mca.mpio");
ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__MCA;
+ ras->ras_block.ras_comm.sub_block_index = AMDGPU_RAS_MCA_BLOCK__MPIO;
ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE;
adev->mca.mpio.ras_if = &ras->ras_block.ras_comm;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
index 3dc2cffdae4f..ba45e85b23f1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
@@ -101,7 +101,8 @@
#define SOC15_DPG_MODE_OFFSET(ip, inst_idx, reg) \
({ \
- uint32_t internal_reg_offset, addr; \
+ /* To avoid a -Wunused-but-set-variable warning. */ \
+ uint32_t internal_reg_offset __maybe_unused, addr; \
bool video_range, video1_range, aon_range, aon1_range; \
\
addr = (adev->reg_offset[ip##_HWIP][inst_idx][reg##_BASE_IDX] + reg); \
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 1b39ecc544f9..de6495c2b2f4 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -2327,6 +2327,9 @@ static int criu_restore_memory_of_gpu(struct kfd_process_device *pdd,
const bool criu_resume = true;
u64 offset;
+ if (bo_priv->idr_handle > INT_MAX)
+ return -EINVAL;
+
if (bo_bucket->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL) {
if (bo_bucket->size !=
kfd_doorbell_process_slice(pdd->dev->kfd))
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index c98a08c269ae..5ffd17ac8e1c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -479,6 +479,11 @@ int kfd_criu_restore_event(struct file *devkfd,
}
*priv_data_offset += sizeof(*ev_priv);
+ if (ev_priv->event_id > INT_MAX) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
if (ev_priv->user_handle) {
ret = kfd_kmap_event_page(p, ev_priv->user_handle);
if (ret)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index b1fcc40b7308..dc4d6bd4a7d0 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -3935,6 +3935,7 @@ int kfd_criu_resume_svm(struct kfd_process *p)
list_for_each_entry_safe(criu_svm_md, next, &svms->criu_svm_metadata_list, list) {
pr_debug("freeing criu_svm_md[]\n\tstart: 0x%llx\n",
criu_svm_md->data.start_addr);
+ list_del(&criu_svm_md->list);
kfree(criu_svm_md);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
index 4c5ef3ef8dbd..30a79dffbd37 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
@@ -322,7 +322,6 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
int i = 0, k = 0;
int ramp_up_num_steps = 1; // TODO: Ramp is currently disabled. Reenable it.
uint8_t visual_confirm_enabled;
- int pipe_idx = 0;
if (dc == NULL)
return false;
@@ -336,7 +335,7 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
cmd.fw_assisted_mclk_switch.config_data.visual_confirm_enabled = visual_confirm_enabled;
if (should_manage_pstate) {
- for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->stream)
@@ -350,7 +349,6 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
cmd.fw_assisted_mclk_switch.config_data.vactive_stretch_margin_us = dc->debug.fpo_vactive_margin_us;
break;
}
- pipe_idx++;
}
}
@@ -714,7 +712,7 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
bool enable)
{
uint8_t cmd_pipe_index = 0;
- uint32_t i, pipe_idx;
+ uint32_t i;
uint8_t subvp_count = 0;
union dmub_rb_cmd cmd;
struct pipe_ctx *subvp_pipes[2];
@@ -740,7 +738,7 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
if (enable) {
// For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd
- for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->stream)
@@ -763,7 +761,6 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
}
- pipe_idx++;
}
if (subvp_count == 2) {
update_subvp_prefetch_end_to_mall_start(dc, context, &cmd, subvp_pipes);
@@ -1054,4 +1051,4 @@ void dc_dmub_srv_enable_dpia_trace(const struct dc *dc)
}
DC_LOG_DEBUG("Enabled DPIA trace\n");
-}
\ No newline at end of file
+}
diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
index 84f9b412a4f1..dbb62b911c75 100644
--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
@@ -447,6 +447,8 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
*
* @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
* @info_packet: output structure where to store VSIF
+ * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
+ * @ALLMValue: ALLM bit value to advertise in HF-VSIF
*/
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index c6e6e4766c8b..dbeae02c76d6 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -7279,6 +7279,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector,
u8 num_v_tile, num_h_tile;
struct drm_tile_group *tg;
+ /* tiled block payload per spec: cap 1 + topo 3 + size 4 + bezel 5 + id 9 = 22 */
+ if (block->num_bytes < 22) {
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] Unexpected tiled block size %u\n",
+ connector->base.id, connector->name, block->num_bytes);
+ return;
+ }
+
w = tile->tile_size[0] | tile->tile_size[1] << 8;
h = tile->tile_size[2] | tile->tile_size[3] << 8;
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
index 89bed78f1466..db7fce4e8cc3 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c
@@ -32,26 +32,43 @@ struct hibmc_display_panel_pll {
struct hibmc_dislay_pll_config {
u64 hdisplay;
u64 vdisplay;
+ int clock;
u32 pll1_config_value;
u32 pll2_config_value;
};
static const struct hibmc_dislay_pll_config hibmc_pll_table[] = {
- {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ},
- {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ},
- {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ},
- {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ},
- {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ},
- {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ},
- {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
- {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
- {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ},
- {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
- {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ},
- {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ},
- {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ},
+ {640, 480, 25000, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ},
+ {800, 600, 40000, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ},
+ {1024, 768, 65000, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ},
+ {1152, 864, 78750, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ},
+ {1280, 768, 80000, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ},
+ {1280, 720, 74375, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ},
+ {1280, 960, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
+ {1280, 1024, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
+ {1440, 900, 105952, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ},
+ {1600, 900, 108000, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ},
+ {1600, 1200, 162500, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ},
+ {1920, 1080, 148750, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ},
+ {1920, 1200, 193750, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ},
};
+static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode)
+{
+ int i, diff;
+
+ for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) {
+ if (hibmc_pll_table[i].hdisplay == mode->hdisplay &&
+ hibmc_pll_table[i].vdisplay == mode->vdisplay) {
+ diff = abs(mode->clock - hibmc_pll_table[i].clock);
+ if (diff < mode->clock / 100) /* tolerance 1/100 */
+ return i;
+ }
+ }
+
+ return -MODE_CLOCK_RANGE;
+}
+
static int hibmc_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
@@ -214,19 +231,15 @@ static enum drm_mode_status
hibmc_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
- size_t i = 0;
int vrefresh = drm_mode_vrefresh(mode);
if (vrefresh < 59 || vrefresh > 61)
return MODE_NOCLOCK;
- for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) {
- if (hibmc_pll_table[i].hdisplay == mode->hdisplay &&
- hibmc_pll_table[i].vdisplay == mode->vdisplay)
- return MODE_OK;
- }
+ if (hibmc_get_best_clock_idx(mode) >= 0)
+ return MODE_OK;
- return MODE_BAD;
+ return MODE_CLOCK_RANGE;
}
static u32 format_pll_reg(void)
@@ -281,23 +294,20 @@ static void set_vclock_hisilicon(struct drm_device *dev, u64 pll)
writel(val, priv->mmio + CRT_PLL1_HS);
}
-static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2)
+static void get_pll_config(struct drm_display_mode *mode, u32 *pll1, u32 *pll2)
{
- size_t i;
- size_t count = ARRAY_SIZE(hibmc_pll_table);
-
- for (i = 0; i < count; i++) {
- if (hibmc_pll_table[i].hdisplay == x &&
- hibmc_pll_table[i].vdisplay == y) {
- *pll1 = hibmc_pll_table[i].pll1_config_value;
- *pll2 = hibmc_pll_table[i].pll2_config_value;
- return;
- }
+ int idx;
+
+ idx = hibmc_get_best_clock_idx(mode);
+ if (idx < 0) {
+ /* if found none, we use default value */
+ *pll1 = CRT_PLL1_HS_25MHZ;
+ *pll2 = CRT_PLL2_HS_25MHZ;
+ return;
}
- /* if found none, we use default value */
- *pll1 = CRT_PLL1_HS_25MHZ;
- *pll2 = CRT_PLL2_HS_25MHZ;
+ *pll1 = hibmc_pll_table[idx].pll1_config_value;
+ *pll2 = hibmc_pll_table[idx].pll2_config_value;
}
/*
@@ -319,7 +329,7 @@ static u32 display_ctrl_adjust(struct drm_device *dev,
x = mode->hdisplay;
y = mode->vdisplay;
- get_pll_config(x, y, &pll1, &pll2);
+ get_pll_config(mode, &pll1, &pll2);
writel(pll2, priv->mmio + CRT_PLL2_HS);
set_vclock_hisilicon(dev, pll1);
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
index 96f960bcfd82..e36cc8593df8 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
@@ -174,6 +174,15 @@ void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate)
writel(gate, mmio + gate_reg);
}
+static void hibmc_display_ctrl(struct hibmc_drm_private *priv)
+{
+ u32 reg;
+
+ reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
+ reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1);
+ writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
+}
+
static void hibmc_hw_config(struct hibmc_drm_private *priv)
{
u32 reg;
@@ -205,6 +214,8 @@ static void hibmc_hw_config(struct hibmc_drm_private *priv)
reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1);
writel(reg, priv->mmio + HIBMC_MISC_CTRL);
+
+ hibmc_display_ctrl(priv);
}
static int hibmc_hw_map(struct hibmc_drm_private *priv)
diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
index 8c6d2ea2a472..6dc5a9fc429f 100644
--- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
+++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
@@ -66,26 +66,6 @@ static const struct drm_connector_funcs hibmc_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
-static void hibmc_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- u32 reg;
- struct drm_device *dev = encoder->dev;
- struct hibmc_drm_private *priv = to_hibmc_drm_private(dev);
-
- reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
- reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1);
- reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1);
- reg |= HIBMC_DISPLAY_CONTROL_FPEN(1);
- reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1);
- writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE);
-}
-
-static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = {
- .mode_set = hibmc_encoder_mode_set,
-};
-
int hibmc_vdac_init(struct hibmc_drm_private *priv)
{
struct drm_device *dev = &priv->dev;
@@ -108,8 +88,6 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv)
return ret;
}
- drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs);
-
ret = drm_connector_init_with_ddc(dev, connector,
&hibmc_connector_funcs,
DRM_MODE_CONNECTOR_VGA,
diff --git a/drivers/gpu/drm/i915/gt/intel_region_lmem.c b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
index f8512aee58a8..472576feaa9a 100644
--- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c
+++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
@@ -37,7 +37,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
_release_bars(pdev);
- ret = pci_resize_resource(pdev, resno, bar_size);
+ ret = pci_resize_resource(pdev, resno, bar_size, 0);
if (ret) {
drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
resno, 1 << bar_size, ERR_PTR(ret));
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index ed77c957eceb..9c8f6a0458c8 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -37,9 +37,9 @@ enum {
ISR_DISCONNECTED,
ISR_CONNECT_PENDING,
ISR_CONNECTED,
- ISR_HPD_REPLUG_COUNT,
+ ISR_HPD_IO_GLITCH_COUNT,
ISR_IRQ_HPD_PULSE_COUNT,
- ISR_HPD_LO_GLITH_COUNT,
+ ISR_HPD_REPLUG_COUNT,
};
/* event thread connection state */
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 78785ed4b40c..df9df1250a7e 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -46,8 +46,8 @@
#define DP_DP_IRQ_HPD_INT_ACK (0x00000002)
#define DP_DP_HPD_REPLUG_INT_ACK (0x00000004)
#define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008)
-#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F)
-#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C)
+#define DP_DP_HPD_STATE_STATUS_BITS_MASK (0x00000007)
+#define DP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1D)
#define REG_DP_DP_HPD_INT_MASK (0x0000000C)
#define DP_DP_HPD_PLUG_INT_MASK (0x00000001)
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
index 023ddc7c5399..a295e94e4cb8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
@@ -65,13 +65,14 @@ pramin_init(struct nvkm_bios *bios, const char *name)
/* we can't get the bios image pointer without PDISP */
if (device->card_type >= GA100)
- addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */
+ addr = nvkm_rd32(device, 0x820c04);
else
if (device->card_type >= GM100)
addr = nvkm_rd32(device, 0x021c04);
else
if (device->card_type >= NV_C0)
addr = nvkm_rd32(device, 0x022500);
+
if (addr & 0x00000001) {
nvkm_debug(subdev, "... display disabled\n");
return ERR_PTR(-ENODEV);
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 96934fee7e94..03600671370c 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -27,6 +27,7 @@
*/
#include <linux/iosys-map.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <drm/drm_device.h>
@@ -809,6 +810,7 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile
int aligned = width;
int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
int pitch_mask = 0;
+ int pitch;
switch (cpp) {
case 1:
@@ -823,9 +825,12 @@ int radeon_align_pitch(struct radeon_device *rdev, int width, int cpp, bool tile
break;
}
- aligned += pitch_mask;
+ if (check_add_overflow(aligned, pitch_mask, &aligned))
+ return 0;
aligned &= ~pitch_mask;
- return aligned * cpp;
+ if (check_mul_overflow(aligned, cpp, &pitch))
+ return 0;
+ return pitch;
}
int radeon_mode_dumb_create(struct drm_file *file_priv,
@@ -839,8 +844,12 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
args->pitch = radeon_align_pitch(rdev, args->width,
DIV_ROUND_UP(args->bpp, 8), 0);
+ if (!args->pitch)
+ return -EINVAL;
args->size = (u64)args->pitch * args->height;
args->size = ALIGN(args->size, PAGE_SIZE);
+ if (!args->size)
+ return -EINVAL;
r = radeon_gem_object_create(rdev, args->size, 0,
RADEON_GEM_DOMAIN_VRAM, 0,
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 8626171e9a6d..537d9e21dc52 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -354,8 +354,10 @@ int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring,
/* restore the saved ring content */
r = radeon_ring_lock(rdev, ring, size);
- if (r)
+ if (r) {
+ kvfree(data);
return r;
+ }
for (i = 0; i < size; ++i) {
radeon_ring_write(ring, data[i]);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 93ed841f5dce..04879e23797f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -332,7 +332,7 @@ void rockchip_gem_free_object(struct drm_gem_object *obj)
struct rockchip_drm_private *private = drm->dev_private;
struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
- if (obj->import_attach) {
+ if (drm_gem_is_imported(obj)) {
if (private->domain) {
rockchip_gem_iommu_unmap(rk_obj);
} else {
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index caa1fe6cc386..9c46caa6192f 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -100,8 +100,10 @@ bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
int err;
of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
- if (it.node == dev->of_node)
+ if (it.node == dev->of_node) {
+ of_node_put(it.node);
return true;
+ }
return false;
}
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index a825fbbc01af..c4cc8353574e 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -69,7 +69,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_
return ERR_PTR(-ENOMEM);
kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;
@@ -170,7 +170,6 @@ static void tegra_bo_unpin(struct host1x_bo_mapping *map)
kfree(map->sgt);
}
- host1x_bo_put(map->bo);
kfree(map);
}
@@ -225,6 +224,7 @@ static const struct host1x_bo_ops tegra_bo_ops = {
static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
{
int prot = IOMMU_READ | IOMMU_WRITE;
+ ssize_t size;
int err;
if (bo->mm)
@@ -246,13 +246,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
bo->iova = bo->mm->start;
- bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
- if (!bo->size) {
+ size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
+ if (size < 0) {
dev_err(tegra->drm->dev, "failed to map buffer\n");
- err = -ENOMEM;
+ err = size;
goto remove;
}
+ bo->size = size;
+
mutex_unlock(&tegra->mm_lock);
return 0;
@@ -494,17 +496,9 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
void tegra_bo_free_object(struct drm_gem_object *gem)
{
struct tegra_drm *tegra = gem->dev->dev_private;
- struct host1x_bo_mapping *mapping, *tmp;
struct tegra_bo *bo = to_tegra_bo(gem);
- /* remove all mappings of this buffer object from any caches */
- list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) {
- if (mapping->cache)
- host1x_bo_unpin(mapping);
- else
- dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping,
- dev_name(mapping->dev));
- }
+ host1x_bo_clear_cached_mappings(&bo->base);
if (tegra->domain)
tegra_bo_iommu_unmap(tegra, bo);
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index a160d01f26e1..e68847fb43d1 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -100,9 +100,6 @@ static int gr2d_exit(struct host1x_client *client)
if (err < 0)
return err;
- pm_runtime_dont_use_autosuspend(client->dev);
- pm_runtime_force_suspend(client->dev);
-
host1x_client_iommu_detach(client);
host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr2d->channel);
@@ -276,15 +273,21 @@ static int gr2d_probe(struct platform_device *pdev)
if (err)
return err;
+ /* initialize address register map */
+ for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
+ set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
+
+ pm_runtime_enable(dev);
+
err = host1x_client_register(&gr2d->client.base);
if (err < 0) {
+ pm_runtime_disable(dev);
dev_err(dev, "failed to register host1x client: %d\n", err);
return err;
}
- /* initialize address register map */
- for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
- set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
return 0;
}
@@ -367,10 +370,6 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev)
goto disable_clk;
}
- pm_runtime_enable(dev);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, 500);
-
return 0;
disable_clk:
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 00c8564520e7..3cde55b2ff28 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -108,9 +108,6 @@ static int gr3d_exit(struct host1x_client *client)
if (err < 0)
return err;
- pm_runtime_dont_use_autosuspend(client->dev);
- pm_runtime_force_suspend(client->dev);
-
host1x_client_iommu_detach(client);
host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr3d->channel);
@@ -529,16 +526,22 @@ static int gr3d_probe(struct platform_device *pdev)
if (err)
return err;
+ /* initialize address register map */
+ for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
+ set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
+
+ pm_runtime_enable(&pdev->dev);
+
err = host1x_client_register(&gr3d->client.base);
if (err < 0) {
+ pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
return err;
}
- /* initialize address register map */
- for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
- set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
return 0;
}
@@ -601,10 +604,6 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev)
goto disable_clk;
}
- pm_runtime_enable(dev);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, 500);
-
return 0;
disable_clk:
diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c
index 2430fcc97448..705382035f5f 100644
--- a/drivers/gpu/drm/tegra/submit.c
+++ b/drivers/gpu/drm/tegra/submit.c
@@ -76,7 +76,7 @@ gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction
return ERR_PTR(-ENOMEM);
kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;
@@ -117,7 +117,6 @@ static void gather_bo_unpin(struct host1x_bo_mapping *map)
dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
sg_free_table(map->sgt);
kfree(map->sgt);
- host1x_bo_put(map->bo);
kfree(map);
}
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index d096d8d2bc8f..3b8133be544e 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -268,8 +268,6 @@ int tidss_modeset_init(struct tidss_device *tidss)
if (ret)
return ret;
- drm_mode_config_reset(ddev);
-
dev_dbg(tidss->dev, "%s done\n", __func__);
return 0;
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index 3937889fa912..6b8b7395a418 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -457,7 +457,7 @@ static int host1x_device_add(struct host1x *host1x,
err = host1x_device_parse_dt(device, driver);
if (err < 0) {
- kfree(device);
+ put_device(&device->dev);
return err;
}
@@ -881,6 +881,20 @@ int host1x_client_resume(struct host1x_client *client)
}
EXPORT_SYMBOL(host1x_client_resume);
+/**
+ * host1x_bo_pin() - Create a DMA mapping for the buffer object
+ * @dev: Device onto which DMA map to
+ * @bo: Buffer object to map
+ * @dir: DMA direction
+ * @cache: Cache in which to store mapping, or NULL
+ *
+ * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented
+ * until host1x_bo_unpin is called.
+ *
+ * If @cache is specified, the mapping is also stored in the cache and not released
+ * until @bo is freed (refcount drops to zero). This improves performance when a buffer
+ * is pinned and unpinned frequently as in the case of display use.
+ */
struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo,
enum dma_data_direction dir,
struct host1x_bo_cache *cache)
@@ -893,6 +907,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
list_for_each_entry(mapping, &cache->mappings, entry) {
if (mapping->bo == bo && mapping->direction == dir) {
kref_get(&mapping->ref);
+ host1x_bo_get(bo);
goto unlock;
}
}
@@ -902,6 +917,8 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
if (IS_ERR(mapping))
goto unlock;
+ host1x_bo_get(bo);
+
spin_lock(&mapping->bo->lock);
list_add_tail(&mapping->list, &bo->mappings);
spin_unlock(&mapping->bo->lock);
@@ -912,7 +929,12 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
list_add_tail(&mapping->entry, &cache->mappings);
- /* bump reference count to track the copy in the cache */
+ /*
+ * Bump the mapping reference count to track the mapping in the cache,
+ * but do not bump the BO's refcount. This allows the BO to still get freed,
+ * triggering the release of the cache mapping through
+ * host1x_bo_clear_cached_mappings.
+ */
kref_get(&mapping->ref);
}
@@ -942,9 +964,17 @@ static void __host1x_bo_unpin(struct kref *ref)
mapping->bo->ops->unpin(mapping);
}
+/**
+ * host1x_bo_unpin() - Release an established DMA mapping of a buffer object
+ * @mapping: Mapping to release
+ *
+ * Unmaps the given @mapping, unless it is cached. Decreases the refcount on
+ * the underlying buffer object.
+ */
void host1x_bo_unpin(struct host1x_bo_mapping *mapping)
{
struct host1x_bo_cache *cache = mapping->cache;
+ struct host1x_bo *bo = mapping->bo;
if (cache)
mutex_lock(&cache->lock);
@@ -953,5 +983,33 @@ void host1x_bo_unpin(struct host1x_bo_mapping *mapping)
if (cache)
mutex_unlock(&cache->lock);
+
+ host1x_bo_put(bo);
}
EXPORT_SYMBOL(host1x_bo_unpin);
+
+/**
+ * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo
+ * @bo: Buffer object to release mappings of
+ *
+ * Drops references to any mappings pointing to @bo left in any caches. This must
+ * be called by any host1x_bo implementers that may be pinned with caching enabled
+ * before freeing the bo.
+ */
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+{
+ struct host1x_bo_mapping *mapping, *tmp;
+ struct host1x_bo_cache *cache;
+
+ list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) {
+ cache = mapping->cache;
+ if (WARN_ON(!cache))
+ continue;
+
+ mutex_lock(&mapping->cache->lock);
+ WARN_ON(kref_read(&mapping->ref) != 1);
+ __host1x_bo_unpin(&mapping->ref);
+ mutex_unlock(&mapping->cache->lock);
+ }
+}
+EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 3ed49e1fd933..70bda32f1ff4 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
}
if (host->domain) {
+ ssize_t map_err;
+
for_each_sgtable_sg(map->sgt, sg, j)
gather_size += sg->length;
@@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto put;
}
- err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
- map->sgt, IOMMU_READ);
- if (err == 0) {
+ map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
+ map->sgt, IOMMU_READ);
+ if (map_err < 0) {
__free_iova(&host->iova, alloc);
- err = -EINVAL;
+ err = map_err;
goto put;
}
diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c
index c45e5aa569d2..7a3fcdf7cd14 100644
--- a/drivers/hid/hid-appleir.c
+++ b/drivers/hid/hid-appleir.c
@@ -109,9 +109,10 @@ struct appleir {
struct hid_device *hid;
unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
struct timer_list key_up_timer; /* timer for key up */
- spinlock_t lock; /* protects .current_key */
+ spinlock_t lock; /* protects .current_key, .removing */
int current_key; /* the currently pressed key */
int prev_key_idx; /* key index in a 2 packets message */
+ bool removing; /* set during teardown; gates input_dev access */
};
static int get_key(int data)
@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_list *t)
unsigned long flags;
spin_lock_irqsave(&appleir->lock, flags);
- if (appleir->current_key) {
+ if (!appleir->removing && appleir->current_key) {
key_up(hid, appleir, appleir->current_key);
appleir->current_key = 0;
}
@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report,
int index;
spin_lock_irqsave(&appleir->lock, flags);
+ if (appleir->removing) {
+ spin_unlock_irqrestore(&appleir->lock, flags);
+ goto out;
+ }
/*
* If we already have a key down, take it up before marking
* this one down
@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_device *hid, struct hid_report *report,
appleir->prev_key_idx = 0;
if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
- key_down(hid, appleir, appleir->current_key);
- /*
- * Remote doesn't do key up, either pull them up, in the test
- * above, or here set a timer which pulls them up after 1/8 s
- */
- mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ spin_lock_irqsave(&appleir->lock, flags);
+ if (!appleir->removing) {
+ key_down(hid, appleir, appleir->current_key);
+ /*
+ * Remote doesn't do key up, either pull them up, in
+ * the test above, or here set a timer which pulls them
+ * up after 1/8 s
+ */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ }
+ spin_unlock_irqrestore(&appleir->lock, flags);
goto out;
}
if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
- battery_flat(appleir);
+ spin_lock_irqsave(&appleir->lock, flags);
+ if (!appleir->removing)
+ battery_flat(appleir);
+ spin_unlock_irqrestore(&appleir->lock, flags);
/* Fall through */
}
@@ -318,8 +331,20 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id)
static void appleir_remove(struct hid_device *hid)
{
struct appleir *appleir = hid_get_drvdata(hid);
+ unsigned long flags;
+
+ /*
+ * Mark the driver as tearing down so that any concurrent raw_event
+ * (e.g. from a USB URB completion that hid_hw_stop() has not yet
+ * killed) and the key_up_timer softirq stop touching input_dev
+ * before hid_hw_stop() frees it via hidinput_disconnect().
+ */
+ spin_lock_irqsave(&appleir->lock, flags);
+ appleir->removing = true;
+ spin_unlock_irqrestore(&appleir->lock, flags);
+
+ timer_shutdown_sync(&appleir->key_up_timer);
hid_hw_stop(hid);
- del_timer_sync(&appleir->key_up_timer);
}
static const struct hid_device_id appleir_devices[] = {
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 2de4d831c526..b3bcc0fdede7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1998,6 +1998,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
u8 *cdata = data;
int ret = 0;
+ if (report_enum->numbered && (size < 1 || bufsize < 1)) {
+ hid_warn_ratelimited(hid,
+ "Event data for numbered report is too short (%d vs %zu)\n",
+ size, bufsize);
+ return -EINVAL;
+ }
+
report = hid_get_report(report_enum, data);
if (!report)
return 0;
diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c
index 97f047f18136..cf8f47134fb6 100644
--- a/drivers/hid/hid-letsketch.c
+++ b/drivers/hid/hid-letsketch.c
@@ -295,13 +295,42 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id *
ret = letsketch_setup_input_tablet(data);
if (ret)
- return ret;
+ goto err_shutdown_timer;
ret = letsketch_setup_input_tablet_pad(data);
if (ret)
- return ret;
+ goto err_shutdown_timer;
+
+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ if (ret)
+ goto err_shutdown_timer;
- return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+ return 0;
+
+err_shutdown_timer:
+ /*
+ * Drain any pending callback and permanently disable the timer
+ * before devm releases data: if hid_hw_start() enabled I/O on an
+ * always-poll-quirk device and then failed, raw_event may have
+ * armed the timer already.
+ */
+ timer_shutdown_sync(&data->inrange_timer);
+ return ret;
+}
+
+static void letsketch_remove(struct hid_device *hdev)
+{
+ struct letsketch_data *data = hid_get_drvdata(hdev);
+
+ /*
+ * hid_hw_stop() synchronously kills the URBs that deliver
+ * raw_event(), so once it returns no path can re-arm
+ * inrange_timer. timer_shutdown_sync() then drains any
+ * in-flight callback and permanently disables further
+ * mod_timer() calls before devm releases data.
+ */
+ hid_hw_stop(hdev);
+ timer_shutdown_sync(&data->inrange_timer);
}
static const struct hid_device_id letsketch_devices[] = {
@@ -314,6 +343,7 @@ static struct hid_driver letsketch_driver = {
.name = "letsketch",
.id_table = letsketch_devices,
.probe = letsketch_probe,
+ .remove = letsketch_remove,
.raw_event = letsketch_raw_event,
};
module_hid_driver(letsketch_driver);
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index acbec1dcf196..7551144a72cb 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -945,11 +945,27 @@ static const struct hid_device_id lg_g15_devices[] = {
};
MODULE_DEVICE_TABLE(hid, lg_g15_devices);
+static void lg_g15_remove(struct hid_device *hdev)
+{
+ struct lg_g15_data *g15 = hid_get_drvdata(hdev);
+
+ /*
+ * g15->work is only initialized for the models that schedule it
+ * (G15, G15 v2, G510). The G13 and Z-10 leave it zeroed, so only
+ * cancel it when it was set up.
+ */
+ if (g15 && g15->work.func)
+ cancel_work_sync(&g15->work);
+
+ hid_hw_stop(hdev);
+}
+
static struct hid_driver lg_g15_driver = {
.name = "lg-g15",
.id_table = lg_g15_devices,
.raw_event = lg_g15_raw_event,
.probe = lg_g15_probe,
+ .remove = lg_g15_remove,
};
module_hid_driver(lg_g15_driver);
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 113307157a27..eba52b95ad1b 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -163,7 +163,6 @@ struct hidpp_battery {
/**
* struct hidpp_scroll_counter - Utility class for processing high-resolution
* scroll events.
- * @dev: the input device for which events should be reported.
* @wheel_multiplier: the scalar multiplier to be applied to each wheel event
* @remainder: counts the number of high-resolution units moved since the last
* low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 6c04eed0a464..4481bf5c9133 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -31,6 +31,7 @@
* [1] https://gitlab.freedesktop.org/libevdev/hid-tools
*/
+#include <linux/bitmap.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
@@ -83,8 +84,7 @@ enum latency_mode {
HID_LATENCY_HIGH = 1,
};
-#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */
-#define MT_IO_FLAGS_RUNNING 32
+#define MT_IO_FLAGS_RUNNING 0
static const bool mtrue = true; /* default for true */
static const bool mfalse; /* default for false */
@@ -160,10 +160,9 @@ struct mt_device {
struct mt_class mtclass; /* our mt device class */
struct timer_list release_timer; /* to release sticky fingers */
struct hid_device *hdev; /* hid_device we're attached to */
- unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING)
- * first 8 bits are reserved for keeping the slot
- * states, this is fine because we only support up
- * to 250 slots (MT_MAX_MAXCONTACT)
+ unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */
+ unsigned long *active_slots; /* bitmap of slots with an active
+ * contact, sized for maxcontacts
*/
__u8 inputmode_value; /* InputMode HID feature value */
__u8 maxcontacts;
@@ -947,7 +946,7 @@ static void mt_release_pending_palms(struct mt_device *td,
for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) {
clear_bit(slotnum, app->pending_palm_slots);
- clear_bit(slotnum, &td->mt_io_flags);
+ clear_bit(slotnum, td->active_slots);
input_mt_slot(input, slotnum);
input_mt_report_slot_inactive(input);
@@ -1153,9 +1152,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
- set_bit(slotnum, &td->mt_io_flags);
+ set_bit(slotnum, td->active_slots);
} else {
- clear_bit(slotnum, &td->mt_io_flags);
+ clear_bit(slotnum, td->active_slots);
}
return 0;
@@ -1290,7 +1289,7 @@ static void mt_touch_report(struct hid_device *hid,
* defect.
*/
if (app->quirks & MT_QUIRK_STICKY_FINGERS) {
- if (td->mt_io_flags & MT_IO_SLOTS_MASK)
+ if (!bitmap_empty(td->active_slots, td->maxcontacts))
mod_timer(&td->release_timer,
jiffies + msecs_to_jiffies(100));
else
@@ -1330,6 +1329,15 @@ static int mt_touch_input_configured(struct hid_device *hdev,
if (td->is_buttonpad)
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+ if (!td->active_slots) {
+ td->active_slots = devm_kcalloc(&td->hdev->dev,
+ BITS_TO_LONGS(td->maxcontacts),
+ sizeof(long),
+ GFP_KERNEL);
+ if (!td->active_slots)
+ return -ENOMEM;
+ }
+
app->pending_palm_slots = devm_kcalloc(&hi->input->dev,
BITS_TO_LONGS(td->maxcontacts),
sizeof(long),
@@ -1738,7 +1746,7 @@ static void mt_release_contacts(struct hid_device *hid)
for (i = 0; i < mt->num_slots; i++) {
input_mt_slot(input_dev, i);
input_mt_report_slot_inactive(input_dev);
- clear_bit(i, &td->mt_io_flags);
+ clear_bit(i, td->active_slots);
}
input_mt_sync_frame(input_dev);
input_sync(input_dev);
@@ -1761,7 +1769,7 @@ static void mt_expired_timeout(struct timer_list *t)
*/
if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
return;
- if (td->mt_io_flags & MT_IO_SLOTS_MASK)
+ if (!bitmap_empty(td->active_slots, td->maxcontacts))
mt_release_contacts(hdev);
clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
}
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index bbda231a7ce3..ee0977a8e42e 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -72,7 +72,8 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
struct picolcd_pending *work;
struct hid_report *report = picolcd_out_report(report_id, hdev);
unsigned long flags;
- int i, j, k;
+ int i, j;
+ unsigned int k;
if (!report || !data)
return NULL;
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 4db50cacbf9a..b197f16f2546 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -2199,6 +2199,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
size == DS4_INPUT_REPORT_USB_SIZE) {
struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data;
+ if (usb->num_touch_reports > ARRAY_SIZE(usb->touch_reports)) {
+ hid_err(hdev, "DualShock4 USB input report has invalid num_touch_reports=%d\n",
+ usb->num_touch_reports);
+ return -EINVAL;
+ }
+
ds4_report = &usb->common;
num_touch_reports = min_t(u8, usb->num_touch_reports,
ARRAY_SIZE(usb->touch_reports));
@@ -2214,6 +2220,12 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
return -EILSEQ;
}
+ if (bt->num_touch_reports > ARRAY_SIZE(bt->touch_reports)) {
+ hid_err(hdev, "DualShock4 BT input report has invalid num_touch_reports=%d\n",
+ bt->num_touch_reports);
+ return -EINVAL;
+ }
+
ds4_report = &bt->common;
num_touch_reports = min_t(u8, bt->num_touch_reports,
ARRAY_SIZE(bt->touch_reports));
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 3cd00afa453a..3fc2967bc817 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -286,6 +286,54 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
}
EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id, u32 attr_usage_id,
+ u32 report_id,
+ enum sensor_hub_read_flags flag,
+ u32 buffer_size, u8 *buffer)
+{
+ struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+ struct hid_report *report;
+ unsigned long flags;
+ long cycles;
+ int ret;
+
+ report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
+ if (!report)
+ return -EINVAL;
+
+ mutex_lock(hsdev->mutex_ptr);
+ if (flag == SENSOR_HUB_SYNC) {
+ memset(&hsdev->pending, 0, sizeof(hsdev->pending));
+ init_completion(&hsdev->pending.ready);
+ hsdev->pending.usage_id = usage_id;
+ hsdev->pending.attr_usage_id = attr_usage_id;
+ hsdev->pending.max_raw_size = buffer_size;
+ hsdev->pending.raw_data = buffer;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hsdev->pending.status = true;
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+ mutex_lock(&data->mutex);
+ hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
+ mutex_unlock(&data->mutex);
+ ret = 0;
+ if (flag == SENSOR_HUB_SYNC) {
+ cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready,
+ HZ * 5);
+ if (cycles == 0)
+ ret = -ETIMEDOUT;
+ else if (cycles < 0)
+ ret = cycles;
+
+ hsdev->pending.status = false;
+ }
+ mutex_unlock(hsdev->mutex_ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values);
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
u32 usage_id,
@@ -480,6 +528,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
struct hid_collection *collection = NULL;
void *priv = NULL;
struct hid_sensor_hub_device *hsdev = NULL;
+ u32 copy_size;
+ u32 avail;
hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
report->id, size, report->type);
@@ -520,12 +570,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
hsdev->pending.attr_usage_id ==
report->field[i]->logical)) {
hid_dbg(hdev, "data was pending ...\n");
- hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
- if (hsdev->pending.raw_data)
- hsdev->pending.raw_size = sz;
- else
- hsdev->pending.raw_size = 0;
- complete(&hsdev->pending.ready);
+ if (hsdev->pending.max_raw_size) {
+ if (hsdev->pending.index < hsdev->pending.max_raw_size) {
+ avail = hsdev->pending.max_raw_size - hsdev->pending.index;
+ copy_size = clamp(sz, 0U, avail);
+
+ memcpy(hsdev->pending.raw_data + hsdev->pending.index,
+ ptr, copy_size);
+ hsdev->pending.index += copy_size;
+ if (hsdev->pending.index >= hsdev->pending.max_raw_size) {
+ hsdev->pending.raw_size = hsdev->pending.index;
+ complete(&hsdev->pending.ready);
+ }
+ }
+ } else {
+ hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
+ if (hsdev->pending.raw_data)
+ hsdev->pending.raw_size = sz;
+ else
+ hsdev->pending.raw_size = 0;
+ complete(&hsdev->pending.ready);
+ }
}
if (callback->capture_sample) {
if (report->field[i]->logical)
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index dbccdfa63916..dccb78bb3afd 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -2403,7 +2403,7 @@ static const struct wiimod_ops wiimod_guitar = {
.in_ext = wiimod_guitar_in_ext,
};
-/*
+/*
* Turntable
* DJ Hero came with a Turntable Controller that was plugged in
* as an extension.
@@ -2439,15 +2439,15 @@ static const __u16 wiimod_turntable_map[] = {
static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext)
{
__u8 be, cs, sx, sy, ed, rtt, rbg, rbr, rbb, ltt, lbg, lbr, lbb, bp, bm;
- /*
+ /*
* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
*------+------+-----+-----+-----+-----+------+------+--------+
- * 0 | RTT<4:3> | SX <5:0> |
- * 1 | RTT<2:1> | SY <5:0> |
+ * 0 | RTT<4:3> | SX <5:0> |
+ * 1 | RTT<2:1> | SY <5:0> |
*------+------+-----+-----+-----+-----+------+------+--------+
* 2 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> |
*------+------+-----+-----+-----+-----+------+------+--------+
- * 3 | ED<2:0> | LTT<4:0> |
+ * 3 | ED<2:0> | LTT<4:0> |
*------+------+-----+-----+-----+-----+------+------+--------+
* 4 | 0 | 0 | LBR | B- | 0 | B+ | RBR | LTT<5> |
*------+------+-----+-----+-----+-----+------+------+--------+
@@ -2458,20 +2458,20 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext)
* With Motion+ enabled, it will look like this:
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
*------+------+-----+-----+-----+-----+------+------+--------+
- * 1 | RTT<4:3> | SX <5:1> | 0 |
- * 2 | RTT<2:1> | SY <5:1> | 0 |
+ * 1 | RTT<4:3> | SX <5:1> | 0 |
+ * 2 | RTT<2:1> | SY <5:1> | 0 |
*------+------+-----+-----+-----+-----+------+------+--------+
* 3 |RTT<0>| ED<4:3> | CS<3:0> | RTT<5> |
*------+------+-----+-----+-----+-----+------+------+--------+
- * 4 | ED<2:0> | LTT<4:0> |
+ * 4 | ED<2:0> | LTT<4:0> |
*------+------+-----+-----+-----+-----+------+------+--------+
* 5 | 0 | 0 | LBR | B- | 0 | B+ | RBR | XXXX |
*------+------+-----+-----+-----+-----+------+------+--------+
* 6 | LBB | 0 | RBG | BE | LBG | RBB | XXXX | XXXX |
*------+------+-----+-----+-----+-----+------+------+--------+
*/
-
- be = !(ext[5] & 0x10);
+
+ be = !(ext[5] & 0x10);
cs = ((ext[2] & 0x1e));
sx = ext[0] & 0x3f;
sy = ext[1] & 0x3f;
@@ -2499,32 +2499,32 @@ static void wiimod_turntable_in_ext(struct wiimote_data *wdata, const __u8 *ext)
input_report_abs(wdata->extension.input, ABS_HAT1X, ltt);
input_report_abs(wdata->extension.input, ABS_HAT2X, cs);
input_report_abs(wdata->extension.input, ABS_HAT3X, ed);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_RIGHT],
rbg);
input_report_key(wdata->extension.input,
wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_RIGHT],
rbr);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_RIGHT],
rbb);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_G_LEFT],
lbg);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_R_LEFT],
lbr);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_B_LEFT],
lbb);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_EUPHORIA],
be);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_PLUS],
bp);
- input_report_key(wdata->extension.input,
- wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS],
+ input_report_key(wdata->extension.input,
+ wiimod_turntable_map[WIIMOD_TURNTABLE_KEY_MINUS],
bm);
input_sync(wdata->extension.input);
@@ -2557,7 +2557,7 @@ static void wiimod_turntable_close(struct input_dev *dev)
static int wiimod_turntable_probe(const struct wiimod_ops *ops,
struct wiimote_data *wdata)
{
- int ret, i;
+ int ret, i;
wdata->extension.input = input_allocate_device();
if (!wdata->extension.input)
@@ -2594,9 +2594,9 @@ static int wiimod_turntable_probe(const struct wiimod_ops *ops,
input_set_abs_params(wdata->extension.input,
ABS_HAT1X, -8, 8, 0, 0);
input_set_abs_params(wdata->extension.input,
- ABS_HAT2X, 0, 31, 1, 1);
+ ABS_HAT2X, 0, 31, 1, 1);
input_set_abs_params(wdata->extension.input,
- ABS_HAT3X, 0, 7, 0, 0);
+ ABS_HAT3X, 0, 7, 0, 0);
ret = input_register_device(wdata->extension.input);
if (ret)
goto err_free;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 507719a388ef..8b4cda6ca844 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -2422,16 +2422,16 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
error = wacom_register_inputs(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) {
error = wacom_initialize_leds(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
error = wacom_initialize_remotes(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
}
if (!wireless) {
@@ -2445,14 +2445,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
cancel_delayed_work_sync(&wacom->init_work);
_wacom_query_tablet_data(wacom);
error = -ENODEV;
- goto fail_quirks;
+ goto fail_hw_stop;
}
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) {
error = hid_hw_open(hdev);
if (error) {
hid_err(hdev, "hw open failed\n");
- goto fail_quirks;
+ goto fail_hw_stop;
}
}
@@ -2461,7 +2461,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
return 0;
-fail_quirks:
+fail_hw_stop:
hid_hw_stop(hdev);
fail:
wacom_release_resources(wacom);
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2b090dbd836c..0049b04102e9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -938,6 +938,7 @@ config SENSORS_LTC2992
tristate "Linear Technology LTC2992"
depends on I2C
depends on GPIOLIB
+ select REGMAP_I2C
help
If you say yes here you get support for Linear Technology LTC2992
I2C System Monitor. The LTC2992 measures current, voltage, and
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index d778a2aaefec..c6e4711cb085 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -1037,6 +1037,9 @@ static int atk_ec_present(struct atk_data *data)
if (obj->type != ACPI_TYPE_PACKAGE)
continue;
+ if (!obj->package.count)
+ continue;
+
id = &obj->package.elements[0];
if (id->type != ACPI_TYPE_INTEGER)
continue;
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 51882f7386cc..716afea0db24 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -1400,6 +1400,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
+ if (val < 0)
+ val = 0;
+
err = it87_lock(data);
if (err)
return err;
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 58a103073fbc..c92d08e9827a 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -214,6 +214,11 @@ int occ_update_response(struct occ *occ)
if (rc)
return rc;
+ if (!occ->active) {
+ rc = -ENODEV;
+ goto unlock;
+ }
+
/* limit the maximum rate of polling the OCC */
if (time_after(jiffies, occ->next_update)) {
rc = occ_poll(occ);
@@ -222,6 +227,7 @@ int occ_update_response(struct occ *occ)
rc = occ->last_error;
}
+unlock:
mutex_unlock(&occ->lock);
return rc;
}
@@ -1105,11 +1111,16 @@ static void occ_parse_poll_response(struct occ *occ)
int occ_active(struct occ *occ, bool active)
{
- int rc = mutex_lock_interruptible(&occ->lock);
+ struct device *hwmon = NULL;
+ int rc = mutex_lock_interruptible(&occ->hwmon_lock);
if (rc)
return rc;
+ rc = mutex_lock_interruptible(&occ->lock);
+ if (rc)
+ goto unlock_hwmon;
+
if (active) {
if (occ->active) {
rc = -EALREADY;
@@ -1154,14 +1165,17 @@ int occ_active(struct occ *occ, bool active)
goto unlock;
}
- if (occ->hwmon)
- hwmon_device_unregister(occ->hwmon);
+ hwmon = occ->hwmon;
occ->active = false;
occ->hwmon = NULL;
}
unlock:
mutex_unlock(&occ->lock);
+ if (hwmon)
+ hwmon_device_unregister(hwmon);
+unlock_hwmon:
+ mutex_unlock(&occ->hwmon_lock);
return rc;
}
@@ -1170,6 +1184,7 @@ int occ_setup(struct occ *occ)
int rc;
mutex_init(&occ->lock);
+ mutex_init(&occ->hwmon_lock);
occ->groups[0] = &occ->group;
rc = occ_setup_sysfs(occ);
@@ -1190,15 +1205,22 @@ EXPORT_SYMBOL_GPL(occ_setup);
void occ_shutdown(struct occ *occ)
{
- mutex_lock(&occ->lock);
+ struct device *hwmon;
occ_shutdown_sysfs(occ);
- if (occ->hwmon)
- hwmon_device_unregister(occ->hwmon);
+ mutex_lock(&occ->hwmon_lock);
+ mutex_lock(&occ->lock);
+
+ hwmon = occ->hwmon;
+ occ->active = false;
occ->hwmon = NULL;
mutex_unlock(&occ->lock);
+
+ if (hwmon)
+ hwmon_device_unregister(hwmon);
+ mutex_unlock(&occ->hwmon_lock);
}
EXPORT_SYMBOL_GPL(occ_shutdown);
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 7ac4b2febce6..82f600093c7f 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -101,6 +101,7 @@ struct occ {
unsigned long next_update;
struct mutex lock; /* lock OCC access */
+ struct mutex hwmon_lock; /* serialize hwmon registration/removal */
struct device *hwmon;
struct occ_attribute *attrs;
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index e2c61d6fa521..0fd1cec7b6ca 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -508,7 +508,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data,
static int adm1275_probe(struct i2c_client *client)
{
s32 (*config_read_fn)(const struct i2c_client *client, u8 reg);
- u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
+ u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0};
int config, device_config;
int ret;
struct pmbus_driver_info *info;
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index a61e2fb176da..eafeabfd93d7 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -3190,18 +3190,23 @@ static void pmbus_regulator_notify_worker(struct work_struct *work)
int i, j;
for (i = 0; i < data->info->pages; i++) {
- int event;
+ unsigned int event;
event = atomic_xchg(&data->regulator_events[i], 0);
if (!event)
continue;
for (j = 0; j < data->info->num_regulators; j++) {
- if (i == rdev_get_id(data->rdevs[j])) {
+ if (i != rdev_get_id(data->rdevs[j]))
+ continue;
+ while (event) {
+ unsigned int _event = BIT(__ffs(event));
+
regulator_notifier_call_chain(data->rdevs[j],
- event, NULL);
- break;
+ _event, NULL);
+ event &= ~_event;
}
+ break;
}
}
}
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index b638d672ac45..c9eae0132ad2 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -1823,6 +1823,8 @@ static int w83627hf_probe(struct platform_device *pdev)
return 0;
error:
+ device_remove_file(dev, &dev_attr_vrm);
+ device_remove_file(dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&dev->kobj, &w83627hf_group);
sysfs_remove_group(&dev->kobj, &w83627hf_group_opt);
return err;
@@ -1834,6 +1836,8 @@ static int w83627hf_remove(struct platform_device *pdev)
hwmon_device_unregister(data->hwmon_dev);
+ device_remove_file(&pdev->dev, &dev_attr_vrm);
+ device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group);
sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt);
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 96bab94ba899..52387b0675f7 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -1918,6 +1918,7 @@ static int w83793_probe(struct i2c_client *client)
for (i = 0; i < ARRAY_SIZE(w83793_vid); i++)
device_remove_file(dev, &w83793_vid[i].dev_attr);
+ device_remove_file(dev, &dev_attr_vrm);
for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++)
device_remove_file(dev, &w83793_left_fan[i].dev_attr);
diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c
index a0fd67fd2934..1daba80c6d6b 100644
--- a/drivers/hwspinlock/qcom_hwspinlock.c
+++ b/drivers/hwspinlock/qcom_hwspinlock.c
@@ -178,7 +178,6 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev,
static int qcom_hwspinlock_probe(struct platform_device *pdev)
{
struct hwspinlock_device *bank;
- struct reg_field field;
struct regmap *regmap;
size_t array_size;
u32 stride;
@@ -200,9 +199,7 @@ static int qcom_hwspinlock_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bank);
for (i = 0; i < QCOM_MUTEX_NUM_LOCKS; i++) {
- field.reg = base + i * stride;
- field.lsb = 0;
- field.msb = 31;
+ struct reg_field field = REG_FIELD(base + i * stride, 0, 31);
bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev,
regmap, field);
diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/drivers/hwtracing/coresight/coresight-cti-platform.c
index ccef04f27f12..438f9c7ddccd 100644
--- a/drivers/hwtracing/coresight/coresight-cti-platform.c
+++ b/drivers/hwtracing/coresight/coresight-cti-platform.c
@@ -329,6 +329,7 @@ static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
if (!tg)
return -ENOMEM;
+ tg->nr_sigs = nr_filter_sigs;
err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
if (!err)
drvdata->config.trig_out_filter |= tg->used_mask;
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index fa80039e0821..01ea3244cbdc 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -86,7 +86,7 @@ struct etb_drvdata {
struct coresight_device *csdev;
struct miscdevice miscdev;
spinlock_t spinlock;
- local_t reading;
+ atomic_t reading;
pid_t pid;
u8 *buf;
u32 mode;
@@ -604,7 +604,7 @@ static int etb_open(struct inode *inode, struct file *file)
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
- if (local_cmpxchg(&drvdata->reading, 0, 1))
+ if (atomic_cmpxchg(&drvdata->reading, 0, 1))
return -EBUSY;
dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__);
@@ -642,7 +642,7 @@ static int etb_release(struct inode *inode, struct file *file)
{
struct etb_drvdata *drvdata = container_of(file->private_data,
struct etb_drvdata, miscdev);
- local_set(&drvdata->reading, 0);
+ atomic_set(&drvdata->reading, 0);
dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__);
return 0;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index c5928f63475a..331df260d074 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -1837,7 +1837,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR0);
if (drvdata->numvmidc > 4)
- state->trcvmidcctlr0 = etm4x_read32(csa, TRCVMIDCCTLR1);
+ state->trcvmidcctlr1 = etm4x_read32(csa, TRCVMIDCCTLR1);
state->trcclaimset = etm4x_read32(csa, TRCCLAIMCLR);
@@ -1963,7 +1963,7 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR0);
if (drvdata->numvmidc > 4)
- etm4x_relaxed_write32(csa, state->trcvmidcctlr0, TRCVMIDCCTLR1);
+ etm4x_relaxed_write32(csa, state->trcvmidcctlr1, TRCVMIDCCTLR1);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c
index 6e32d31a95fe..62af5953d371 100644
--- a/drivers/hwtracing/coresight/ultrasoc-smb.c
+++ b/drivers/hwtracing/coresight/ultrasoc-smb.c
@@ -361,6 +361,7 @@ static void smb_sync_perf_buffer(struct smb_drv_data *drvdata,
unsigned long to_copy;
long pg_idx, pg_offset;
+ head %= (unsigned long)buf->nr_pages << PAGE_SHIFT;
pg_idx = head >> PAGE_SHIFT;
pg_offset = head & (PAGE_SIZE - 1);
diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c
index b3a73921ab69..d3f4885c49f6 100644
--- a/drivers/i2c/busses/i2c-mlxbf.c
+++ b/drivers/i2c/busses/i2c-mlxbf.c
@@ -1082,8 +1082,10 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev,
tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params);
if (IS_ERR(tmp_res->io)) {
+ int ret = PTR_ERR(tmp_res->io);
+
devm_kfree(dev, tmp_res);
- return PTR_ERR(tmp_res->io);
+ return ret;
}
tmp_res->type = type;
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index e4e4995ab224..4baf6a5ed4e2 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -857,7 +857,7 @@ static int fsl_i2c_probe(struct platform_device *op)
"fsl,timeout", &mpc_ops.timeout);
if (!result) {
- mpc_ops.timeout *= HZ / 1000000;
+ mpc_ops.timeout = mpc_ops.timeout * HZ / 1000000;
if (mpc_ops.timeout < 5)
mpc_ops.timeout = 5;
} else {
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 6b788bca64d5..f7c54730f6be 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -1253,7 +1253,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
i2c->auto_restart = i2c->dev_comp->auto_restart;
/* checking if we can skip restart and optimize using WRRD mode */
- if (i2c->auto_restart && num == 2) {
+ if (num == 2) {
if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
msgs[0].addr == msgs[1].addr) {
i2c->auto_restart = 0;
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index e43801065eb2..7f5b33234909 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -450,8 +450,13 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
{
struct stm32f7_i2c_spec *specs;
u32 p_prev = STM32F7_PRESC_MAX;
- u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
- setup->clock_src);
+ /*
+ * Truncate instead of rounding to closest: if the clock period is
+ * overestimated, the computed SCL timings will come out shorter on
+ * the wire, which can push the bus above the target rate and below
+ * the spec's tLOW/tHIGH minimums.
+ */
+ u32 i2cclk = NSEC_PER_SEC / setup->clock_src;
u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
setup->speed_freq);
u32 clk_error_prev = i2cbus;
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 943f0021d6a2..f64bcdaae62d 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -63,6 +63,7 @@
static DEFINE_MUTEX(core_lock);
static DEFINE_IDR(i2c_adapter_idr);
+static void i2c_deregister_clients(struct i2c_adapter *adap);
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
@@ -1545,17 +1546,26 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}
- dev_set_name(&adap->dev, "i2c-%d", adap->nr);
+ res = dev_set_name(&adap->dev, "i2c-%d", adap->nr);
+ if (res)
+ goto err_remove_irq_domain;
+
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
- res = device_register(&adap->dev);
+ device_initialize(&adap->dev);
+
+ adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root);
+
+ mutex_lock(&core_lock);
+ idr_replace(&i2c_adapter_idr, adap, adap->nr);
+ mutex_unlock(&core_lock);
+
+ res = device_add(&adap->dev);
if (res) {
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
- goto out_list;
+ goto err_replace_id;
}
- adap->debugfs = debugfs_create_dir(dev_name(&adap->dev), i2c_debugfs_root);
-
res = i2c_setup_smbus_alert(adap);
if (res)
goto out_reg;
@@ -1595,10 +1605,18 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
return 0;
out_reg:
+ i2c_deregister_clients(adap);
+ device_del(&adap->dev);
+err_replace_id:
+ mutex_lock(&core_lock);
+ idr_replace(&i2c_adapter_idr, NULL, adap->nr);
+ mutex_unlock(&core_lock);
debugfs_remove_recursive(adap->debugfs);
init_completion(&adap->dev_released);
- device_unregister(&adap->dev);
+ put_device(&adap->dev);
wait_for_completion(&adap->dev_released);
+err_remove_irq_domain:
+ i2c_host_notify_irq_teardown(adap);
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
@@ -1618,7 +1636,7 @@ static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
int id;
mutex_lock(&core_lock);
- id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
+ id = idr_alloc(&i2c_adapter_idr, NULL, adap->nr, adap->nr + 1, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
return id == -ENOSPC ? -EBUSY : id;
@@ -1654,7 +1672,7 @@ int i2c_add_adapter(struct i2c_adapter *adapter)
}
mutex_lock(&core_lock);
- id = idr_alloc(&i2c_adapter_idr, adapter,
+ id = idr_alloc(&i2c_adapter_idr, NULL,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (WARN(id < 0, "couldn't get idr"))
@@ -1736,29 +1754,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data)
return 0;
}
-/**
- * i2c_del_adapter - unregister I2C adapter
- * @adap: the adapter being unregistered
- * Context: can sleep
- *
- * This unregisters an I2C adapter which was previously registered
- * by @i2c_add_adapter or @i2c_add_numbered_adapter.
- */
-void i2c_del_adapter(struct i2c_adapter *adap)
+static void i2c_deregister_clients(struct i2c_adapter *adap)
{
- struct i2c_adapter *found;
struct i2c_client *client, *next;
- /* First make sure that this adapter was ever added */
- mutex_lock(&core_lock);
- found = idr_find(&i2c_adapter_idr, adap->nr);
- mutex_unlock(&core_lock);
- if (found != adap) {
- pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
- return;
- }
-
- i2c_acpi_remove_space_handler(adap);
/* Tell drivers about this removal */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap,
@@ -1784,6 +1783,34 @@ void i2c_del_adapter(struct i2c_adapter *adap)
* them up properly, so we give them a chance to do that first. */
device_for_each_child(&adap->dev, NULL, __unregister_client);
device_for_each_child(&adap->dev, NULL, __unregister_dummy);
+}
+
+/**
+ * i2c_del_adapter - unregister I2C adapter
+ * @adap: the adapter being unregistered
+ * Context: can sleep
+ *
+ * This unregisters an I2C adapter which was previously registered
+ * by @i2c_add_adapter or @i2c_add_numbered_adapter.
+ */
+void i2c_del_adapter(struct i2c_adapter *adap)
+{
+ struct i2c_adapter *found;
+
+ /* First make sure that this adapter was ever added */
+ mutex_lock(&core_lock);
+ found = idr_find(&i2c_adapter_idr, adap->nr);
+ if (found == adap)
+ idr_replace(&i2c_adapter_idr, NULL, adap->nr);
+ mutex_unlock(&core_lock);
+ if (found != adap) {
+ pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
+ return;
+ }
+
+ i2c_acpi_remove_space_handler(adap);
+
+ i2c_deregister_clients(adap);
#ifdef CONFIG_I2C_COMPAT
class_compat_remove_link(i2c_adapter_compat_class, &adap->dev,
diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c
index 50e97e2ed2cf..a250921bbce0 100644
--- a/drivers/i2c/i2c-core-of.c
+++ b/drivers/i2c/i2c-core-of.c
@@ -178,11 +178,6 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
- /*
- * Clear the flag before adding the device so that fw_devlink
- * doesn't skip adding consumers to this device.
- */
- fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE);
client = of_i2c_register_device(adap, rd->dn);
if (IS_ERR(client)) {
dev_err(&adap->dev, "failed to create client for '%pOF'\n",
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index f74ef65d257d..c062e94ce221 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2014,12 +2014,11 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
bool enable_ibi = false;
int ret;
- if (!master)
- return -EINVAL;
-
newdev = i3c_master_alloc_i3c_dev(master, &info);
- if (IS_ERR(newdev))
- return PTR_ERR(newdev);
+ if (IS_ERR(newdev)) {
+ ret = PTR_ERR(newdev);
+ goto err_prevent_addr_reuse;
+ }
ret = i3c_master_attach_i3c_dev(master, newdev);
if (ret)
@@ -2141,6 +2140,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
err_free_dev:
i3c_master_free_i3c_dev(newdev);
+err_prevent_addr_reuse:
+ /*
+ * Although the device has not been added, the address has been
+ * assigned. Prevent the address from being used again.
+ */
+ if (i3c_bus_get_addr_slot_status(&master->bus, addr) == I3C_ADDR_SLOT_FREE)
+ i3c_bus_set_addr_slot_status(&master->bus, addr, I3C_ADDR_SLOT_I3C_DEV);
+
+ dev_err(&master->dev, "Failed to add I3C device at address %u, error %d\n", addr, ret);
+
return ret;
}
EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked);
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 9bc5957123c5..bafa75182c1a 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -1037,6 +1037,8 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
if (samples && count > samples)
count = samples;
+ count = min_t(u8, count, BMC150_ACCEL_FIFO_LENGTH);
+
ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count);
if (ret)
return ret;
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index ba99649fe195..78d44e6011eb 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -146,8 +146,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
if (mask == IIO_CHAN_INFO_SCALE) {
/* Check no integer component */
if (val)
- return -EINVAL;
- ret = kxsd9_write_scale(indio_dev, val2);
+ ret = -EINVAL;
+ else
+ ret = kxsd9_write_scale(indio_dev, val2);
}
pm_runtime_mark_last_busy(st->dev);
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 16a0de6002be..b7ffdbfe2f09 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -254,6 +254,8 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
3 * sizeof(__be16), (u8 *)buf);
+ if (ret < 0)
+ return ret;
ret = mma8452_set_runtime_pm_state(data->client, false);
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
index e34ed7dacd89..9c3777232415 100644
--- a/drivers/iio/adc/lpc32xx_adc.c
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -179,6 +179,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ init_completion(&st->completion);
+
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
LPC32XXAD_NAME, st);
if (retval < 0) {
@@ -197,8 +199,6 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, iodev);
- init_completion(&st->completion);
-
iodev->name = LPC32XXAD_NAME;
iodev->info = &lpc32xx_adc_iio_info;
iodev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
index 602ed05552bf..5223fde389ef 100644
--- a/drivers/iio/adc/spear_adc.c
+++ b/drivers/iio/adc/spear_adc.c
@@ -282,6 +282,7 @@ static int spear_adc_probe(struct platform_device *pdev)
st = iio_priv(indio_dev);
+ init_completion(&st->completion);
mutex_init(&st->lock);
st->np = np;
@@ -346,8 +347,6 @@ static int spear_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
- init_completion(&st->completion);
-
indio_dev->name = SPEAR_ADC_MOD_NAME;
indio_dev->info = &spear_adc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
index c6004f2b5d3d..3618d56680f9 100644
--- a/drivers/iio/adc/ti-ads124s08.c
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -322,7 +322,8 @@ static int ads124s_probe(struct spi_device *spi)
ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(ads124s_priv->reset_gpio))
- dev_info(&spi->dev, "Reset GPIO not defined\n");
+ return dev_err_probe(&spi->dev, PTR_ERR(ads124s_priv->reset_gpio),
+ "Failed to get reset GPIO\n");
ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index 5de86a953d41..b704f76592ea 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -861,6 +861,9 @@ static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev,
if (dev->channels[i].scan_index == scan_index)
break;
+ if (i == dev->num_channels)
+ return NULL;
+
return &dev->channels[i];
}
@@ -1002,6 +1005,8 @@ static void ams_handle_event(struct iio_dev *indio_dev, u32 event)
const struct iio_chan_spec *chan;
chan = ams_event_to_channel(indio_dev, event);
+ if (!chan)
+ return;
if (chan->type == IIO_TEMP) {
/*
diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
index 6ecdc7a07199..c8fe8f76f178 100644
--- a/drivers/iio/chemical/scd30_core.c
+++ b/drivers/iio/chemical/scd30_core.c
@@ -4,6 +4,8 @@
*
* Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
*/
+
+#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
@@ -43,6 +45,11 @@
#define SCD30_TEMP_OFFSET_MAX 655360
#define SCD30_EXTRA_TIMEOUT_PER_S 250
+/* Floating point arithmetic macros */
+#define SCD30_FLOAT_MANTISSA_MSK GENMASK(22, 0)
+#define SCD30_FLOAT_EXP_MSK GENMASK(30, 23)
+#define SCD30_FLOAT_SIGN_MSK BIT(31)
+
enum {
SCD30_CONC,
SCD30_TEMP,
@@ -89,10 +96,14 @@ static int scd30_reset(struct scd30_state *state)
/* simplified float to fixed point conversion with a scaling factor of 0.01 */
static int scd30_float_to_fp(int float32)
{
- int fraction, shift,
- mantissa = float32 & GENMASK(22, 0),
- sign = (float32 & BIT(31)) ? -1 : 1,
- exp = (float32 & ~BIT(31)) >> 23;
+ int fraction, shift, sign;
+ int mantissa = FIELD_GET(SCD30_FLOAT_MANTISSA_MSK, float32);
+ int exp = FIELD_GET(SCD30_FLOAT_EXP_MSK, float32);
+
+ if (float32 & SCD30_FLOAT_SIGN_MSK)
+ sign = -1;
+ else
+ sign = 1;
/* special case 0 */
if (!exp && !mantissa)
diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
index e85be46e48d3..37d0bdaa8d82 100644
--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
@@ -78,13 +78,13 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
-static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
+static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period)
{
uint32_t period_min, period_max;
/* check that period is acceptable */
- period_min = ts->min_period * mult;
- period_max = ts->max_period * mult;
+ period_min = ts->min_period * ts->mult;
+ period_max = ts->max_period * ts->mult;
if (period > period_min && period < period_max)
return true;
else
@@ -92,15 +92,15 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio
}
static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
- uint32_t mult, uint32_t period)
+ uint32_t period)
{
uint32_t new_chip_period;
- if (!inv_validate_period(ts, period, mult))
+ if (!inv_validate_period(ts, period))
return false;
/* update chip internal period estimation */
- new_chip_period = period / mult;
+ new_chip_period = period / ts->mult;
inv_update_acc(&ts->chip_period, new_chip_period);
ts->period = ts->mult * ts->chip_period.val;
@@ -133,16 +133,14 @@ static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
}
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
- uint32_t fifo_period, size_t fifo_nb,
- size_t sensor_nb, int64_t timestamp)
+ size_t sample_nb, int64_t timestamp)
{
struct inv_sensors_timestamp_interval *it;
int64_t delta, interval;
- const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
- uint32_t period = ts->period;
+ uint32_t period;
bool valid = false;
- if (fifo_nb == 0)
+ if (sample_nb == 0)
return;
/* update interrupt timestamp and compute chip and sensor periods */
@@ -152,14 +150,14 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
delta = it->up - it->lo;
if (it->lo != 0) {
/* compute period: delta time divided by number of samples */
- period = div_s64(delta, fifo_nb);
- valid = inv_update_chip_period(ts, fifo_mult, period);
+ period = div_s64(delta, sample_nb);
+ valid = inv_update_chip_period(ts, period);
}
/* no previous data, compute theoritical value from interrupt */
if (ts->timestamp == 0) {
/* elapsed time: sensor period * sensor samples number */
- interval = (int64_t)ts->period * (int64_t)sensor_nb;
+ interval = (int64_t)ts->period * (int64_t)sample_nb;
ts->timestamp = it->up - interval;
return;
}
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 78f5728417d5..6fd725b3c068 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -498,6 +498,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
u8 *outdata;
struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned int byte_for_channel;
+ u32 tmp;
byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
ch->scan_type.shift, 8);
@@ -510,12 +511,22 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
if (err < 0)
goto st_sensors_free_memory;
- if (byte_for_channel == 1)
- *data = (s8)*outdata;
- else if (byte_for_channel == 2)
- *data = (s16)get_unaligned_le16(outdata);
- else if (byte_for_channel == 3)
- *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23);
+ if (byte_for_channel == 1) {
+ tmp = *outdata;
+ } else if (byte_for_channel == 2) {
+ if (ch->scan_type.endianness == IIO_BE)
+ tmp = get_unaligned_be16(outdata);
+ else
+ tmp = get_unaligned_le16(outdata);
+ } else if (byte_for_channel == 3) {
+ if (ch->scan_type.endianness == IIO_BE)
+ tmp = get_unaligned_be24(outdata);
+ else
+ tmp = get_unaligned_le24(outdata);
+ } else {
+ return -EINVAL;
+ }
+ *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1);
st_sensors_free_memory:
kfree(outdata);
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index 0e2eb0e98235..dcdb7661b662 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -204,6 +204,9 @@ static int bmg160_get_filter(struct bmg160_data *data, int *val)
break;
}
+ if (i == ARRAY_SIZE(bmg160_samp_freq_table))
+ return -EINVAL;
+
*val = bmg160_samp_freq_table[i].filter;
return ret ? ret : IIO_VAL_INT;
@@ -221,6 +224,9 @@ static int bmg160_set_filter(struct bmg160_data *data, int val)
break;
}
+ if (i == ARRAY_SIZE(bmg160_samp_freq_table))
+ return -EINVAL;
+
ret = regmap_write(data->regmap, BMG160_REG_PMU_BW,
bmg160_samp_freq_table[i].bw_bits);
if (ret < 0) {
@@ -261,8 +267,14 @@ static int bmg160_chip_init(struct bmg160_data *data)
if (ret < 0)
return ret;
- /* Wait upto 500 ms to be ready after changing mode */
- usleep_range(500, 1000);
+ /*
+ * Wait for the chip to be ready after switching to normal mode.
+ * The BMG160 datasheet (BST-BMG160-DS000-07 Rev. 1.0, May 2013)
+ * specifies a start-up / wake-up time (tsu, twusm) of 30 ms; use
+ * BMG160_MAX_STARTUP_TIME_MS (80 ms) as a safety margin, matching
+ * what bmg160_runtime_resume() already does.
+ */
+ msleep(BMG160_MAX_STARTUP_TIME_MS);
/* Set Bandwidth */
ret = bmg160_set_bw(data, BMG160_DEF_BW);
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index f890bf842db8..7e18b31f9d77 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -79,7 +79,7 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
ret = devm_request_irq(&adis->spi->dev, adis->spi->irq,
&iio_trigger_generic_data_rdy_poll,
- adis->irq_flag,
+ adis->irq_flag | IRQF_NO_THREAD,
indio_dev->name,
adis->trig);
if (ret)
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index 3e98a7110cbe..d9d67d81c6fc 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -784,7 +784,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
ret = devm_request_irq(&indio_dev->dev, irq,
&iio_trigger_generic_data_rdy_poll,
- irq_type, "bmi160", data->trig);
+ irq_type | IRQF_NO_THREAD,
+ "bmi160", data->trig);
if (ret)
return ret;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index f8c5d87f92c5..f260a4d07b29 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -718,10 +718,10 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
return ERR_PTR(-ENOMEM);
/*
- * clock period is 32kHz (31250ns)
+ * clock period is 8kHz (125000ns)
* jitter is +/- 2% (20 per mille)
*/
- ts_chip.clock_period = 31250;
+ ts_chip.clock_period = 125000;
ts_chip.jitter = 20;
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
ts = iio_priv(indio_dev);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
index 743580ea5845..bead30514297 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
@@ -222,10 +222,15 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
latency_accel = period_accel * wm_accel;
/* 0 value for watermark means that the sensor is turned off */
+ if (wm_gyro == 0 && wm_accel == 0)
+ return 0;
+
if (latency_gyro == 0) {
watermark = wm_accel;
+ st->fifo.watermark.eff_accel = wm_accel;
} else if (latency_accel == 0) {
watermark = wm_gyro;
+ st->fifo.watermark.eff_gyro = wm_gyro;
} else {
/* compute the smallest latency that is a multiple of both */
if (latency_gyro <= latency_accel)
@@ -241,10 +246,18 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
watermark = latency / period;
if (watermark < 1)
watermark = 1;
+ /* update effective watermark */
+ st->fifo.watermark.eff_gyro = latency / period_gyro;
+ if (st->fifo.watermark.eff_gyro < 1)
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = latency / period_accel;
+ if (st->fifo.watermark.eff_accel < 1)
+ st->fifo.watermark.eff_accel = 1;
}
/* compute watermark value in bytes */
wm_size = watermark * packet_size;
+ st->fifo.watermark.value = watermark;
/* changing FIFO watermark requires to turn off watermark interrupt */
ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,
@@ -464,11 +477,10 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
st->fifo.nb.accel = 0;
st->fifo.nb.total = 0;
- /* compute maximum FIFO read size */
+ /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */
if (max == 0)
- max_count = sizeof(st->fifo.data);
- else
- max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
+ max = st->fifo.watermark.value;
+ max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
/* read FIFO count value */
raw_fifo_count = (__be16 *)st->buffer;
@@ -515,20 +527,20 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
return 0;
/* handle gyroscope timestamp and FIFO data parsing */
- ts = iio_priv(st->indio_gyro);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
- st->fifo.nb.gyro, st->timestamp.gyro);
if (st->fifo.nb.gyro > 0) {
+ ts = iio_priv(st->indio_gyro);
+ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,
+ st->timestamp.gyro);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
}
/* handle accelerometer timestamp and FIFO data parsing */
- ts = iio_priv(st->indio_accel);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
- st->fifo.nb.accel, st->timestamp.accel);
if (st->fifo.nb.accel > 0) {
+ ts = iio_priv(st->indio_accel);
+ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,
+ st->timestamp.accel);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;
@@ -556,9 +568,7 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
if (st->fifo.nb.gyro > 0) {
ts = iio_priv(st->indio_gyro);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period,
- st->fifo.nb.total, st->fifo.nb.gyro,
- gyro_ts);
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
@@ -566,9 +576,7 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
if (st->fifo.nb.accel > 0) {
ts = iio_priv(st->indio_accel);
- inv_sensors_timestamp_interrupt(ts, st->fifo.period,
- st->fifo.nb.total, st->fifo.nb.accel,
- accel_ts);
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;
@@ -582,6 +590,10 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st)
unsigned int val;
int ret;
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = 1;
+ st->fifo.watermark.value = 1;
+
/*
* Default FIFO configuration (bits 7 to 5)
* - use invalid value
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
index eed6a3152acf..88b8b9f780af 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h
@@ -32,6 +32,9 @@ struct inv_icm42600_fifo {
struct {
unsigned int gyro;
unsigned int accel;
+ unsigned int eff_gyro;
+ unsigned int eff_accel;
+ unsigned int value;
} watermark;
size_t count;
struct {
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index 99eb651743f8..2f9a0e7fada4 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -523,6 +523,7 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
if (ret)
return ret;
+ irq_type |= IRQF_ONESHOT;
return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp,
inv_icm42600_irq_handler, irq_type,
"inv_icm42600", st);
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index 4af1fc9395b8..49d2e18c3a8e 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -729,10 +729,10 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
return ERR_PTR(-ENOMEM);
/*
- * clock period is 32kHz (31250ns)
+ * clock period is 8kHz (125000ns)
* jitter is +/- 2% (20 per mille)
*/
- ts_chip.clock_period = 31250;
+ ts_chip.clock_period = 125000;
ts_chip.jitter = 20;
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
ts = iio_priv(indio_dev);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index ace3ce4faea7..26621f181e82 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -113,7 +113,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
goto end_session;
/* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
- inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
+ inv_sensors_timestamp_interrupt(&st->timestamp, nb, pf->timestamp);
inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
/* clear internal data buffer for avoiding kernel data leak */
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index ef664c7c661b..e6b3edb64e7b 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -1576,6 +1576,26 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
return -ENODEV;
}
+ hw->settings = &st_lsm6dsx_sensor_settings[i];
+
+ if (hw->settings->shub_settings.page_mux.addr) {
+ /*
+ * If the IMU has the shub page selected on init, for example
+ * after a CPU watchdog reset while the page is selected, the
+ * regular register space is shadowed. While the regular
+ * register space is shadowed, the registers needed for
+ * initializing the IMU are not available.
+ *
+ * Unconditionally clear the shub page selection to ensure
+ * normal register access.
+ */
+ err = st_lsm6dsx_set_page(hw, false);
+ if (err < 0) {
+ dev_err(hw->dev, "failed to clear shub page\n");
+ return err;
+ }
+ }
+
err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
@@ -1588,7 +1608,6 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
}
*name = st_lsm6dsx_sensor_settings[i].id[j].name;
- hw->settings = &st_lsm6dsx_sensor_settings[i];
return 0;
}
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index f67e4afa5f94..30cf5edc5863 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -207,6 +207,8 @@ static int iio_event_getfd(struct iio_dev *indio_dev)
goto unlock;
}
+ kfifo_reset_out(&ev_int->det_events);
+
iio_device_get(indio_dev);
fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
@@ -214,10 +216,7 @@ static int iio_event_getfd(struct iio_dev *indio_dev)
if (fd < 0) {
clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
iio_device_put(indio_dev);
- } else {
- kfifo_reset_out(&ev_int->det_events);
}
-
unlock:
mutex_unlock(&iio_dev_opaque->mlock);
return fd;
diff --git a/drivers/iio/light/al3010.c b/drivers/iio/light/al3010.c
index 7d4053bfceea..7d7d0c12e63b 100644
--- a/drivers/iio/light/al3010.c
+++ b/drivers/iio/light/al3010.c
@@ -43,7 +43,7 @@ enum al3xxxx_range {
};
static const int al3010_scales[][2] = {
- {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
+ { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 },
};
struct al3010_data {
diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c
index fec10d5e037e..7e73fa2f9a81 100644
--- a/drivers/iio/light/gp2ap002.c
+++ b/drivers/iio/light/gp2ap002.c
@@ -258,7 +258,7 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev,
case IIO_LIGHT:
ret = gp2ap002_get_lux(gp2ap002);
if (ret < 0)
- return ret;
+ goto out;
*val = ret;
ret = IIO_VAL_INT;
goto out;
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index 25a45c4251fb..92f92af84960 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -280,8 +280,10 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
ret = wait_event_timeout(opt->result_ready_queue,
opt->result_ready,
msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
- if (ret == 0)
- return -ETIMEDOUT;
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
} else {
/* Sleep for result ready time */
timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c
index ea2c437199c0..598d9d907506 100644
--- a/drivers/iio/light/si1133.c
+++ b/drivers/iio/light/si1133.c
@@ -395,8 +395,14 @@ static int si1133_command(struct si1133_data *data, u8 cmd)
expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;
- if (cmd == SI1133_CMD_FORCE)
+ if (cmd == SI1133_CMD_FORCE) {
+ /* Flush pending IRQs from a previous timeout. */
+ regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &resp);
+ regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,
+ SI1133_IRQ_CHANNEL_ENABLE);
+
reinit_completion(&data->completion);
+ }
err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);
if (err) {
@@ -409,6 +415,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd)
/* wait for irq */
if (!wait_for_completion_timeout(&data->completion,
msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {
+ regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE, 0);
err = -ETIMEDOUT;
goto out;
}
@@ -427,6 +434,11 @@ static int si1133_command(struct si1133_data *data, u8 cmd)
dev_warn(dev,
"Failed to read command 0x%02x, ret=%d\n",
cmd, err);
+ /*
+ * Reset counter on err to prevent software and hardware
+ * counters being out of sync.
+ */
+ si1133_cmd_reset_counter(data);
goto out;
}
}
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index 75fcf2c93717..ab8478c9b045 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -442,6 +442,23 @@ static const struct iio_info tcs3472_info = {
.attrs = &tcs3472_attribute_group,
};
+static int tcs3472_powerdown(struct tcs3472_data *data)
+{
+ int ret;
+ u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON;
+
+ mutex_lock(&data->lock);
+
+ ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
+ data->enable & ~enable_mask);
+ if (!ret)
+ data->enable &= ~enable_mask;
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
static int tcs3472_probe(struct i2c_client *client)
{
struct tcs3472_data *data;
@@ -515,7 +532,7 @@ static int tcs3472_probe(struct i2c_client *client)
ret = iio_triggered_buffer_setup(indio_dev, NULL,
tcs3472_trigger_handler, NULL);
if (ret < 0)
- return ret;
+ goto error_powerdown;
if (client->irq) {
ret = request_threaded_irq(client->irq, NULL,
@@ -538,23 +555,8 @@ static int tcs3472_probe(struct i2c_client *client)
free_irq(client->irq, indio_dev);
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
- return ret;
-}
-
-static int tcs3472_powerdown(struct tcs3472_data *data)
-{
- int ret;
- u8 enable_mask = TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON;
-
- mutex_lock(&data->lock);
-
- ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
- data->enable & ~enable_mask);
- if (!ret)
- data->enable &= ~enable_mask;
-
- mutex_unlock(&data->lock);
-
+error_powerdown:
+ tcs3472_powerdown(data);
return ret;
}
diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c
index 7bdbfe72f0f0..00bef57d95cf 100644
--- a/drivers/iio/light/tsl2591.c
+++ b/drivers/iio/light/tsl2591.c
@@ -1139,10 +1139,8 @@ static int tsl2591_probe(struct i2c_client *client)
NULL, tsl2591_event_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"tsl2591_irq", indio_dev);
- if (ret) {
- dev_err_probe(&client->dev, ret, "IRQ request error\n");
- return -EINVAL;
- }
+ if (ret)
+ return ret;
indio_dev->info = &tsl2591_info;
} else {
indio_dev->info = &tsl2591_info_no_irq;
diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c
index 278735a1f260..695792631910 100644
--- a/drivers/iio/light/veml6030.c
+++ b/drivers/iio/light/veml6030.c
@@ -710,9 +710,11 @@ static irqreturn_t veml6030_event_handler(int irq, void *private)
else
evtdir = IIO_EV_DIR_FALLING;
- iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
- 0, IIO_EV_TYPE_THRESH, evtdir),
- iio_get_time_ns(indio_dev));
+ iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ evtdir),
+ iio_get_time_ns(indio_dev));
return IRQ_HANDLED;
}
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index c1894a26ffd6..547a4738cf1d 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -464,6 +464,10 @@ static int ak8975_who_i_am(struct i2c_client *client,
dev_err(&client->dev, "Error reading WIA\n");
return ret;
}
+ if (ret != sizeof(wia_val)) {
+ dev_err(&client->dev, "Error reading WIA\n");
+ return -EIO;
+ }
if (wia_val[0] != AK8975_DEVICE_ID)
return -ENODEV;
@@ -580,6 +584,10 @@ static int ak8975_setup(struct i2c_client *client)
dev_err(&client->dev, "Not able to read asa data\n");
return ret;
}
+ if (ret != sizeof(data->asa)) {
+ dev_err(&client->dev, "Error reading asa data\n");
+ return -EIO;
+ }
/* After reading fuse ROM data set power-down mode */
ret = ak8975_set_mode(data, POWER_DOWN);
@@ -719,6 +727,10 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
sizeof(rval), (u8*)&rval);
if (ret < 0)
goto exit;
+ if (ret != sizeof(rval)) {
+ ret = -EIO;
+ goto exit;
+ }
/* Read out ST2 for release lock on measurment data. */
ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST2]);
@@ -746,6 +758,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
exit:
mutex_unlock(&data->lock);
+ pm_runtime_put_autosuspend(&data->client->dev);
dev_err(&client->dev, "Error in reading axis\n");
return ret;
}
@@ -847,6 +860,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
(u8 *)fval);
if (ret < 0)
goto unlock;
+ if (ret != sizeof(fval))
+ goto unlock;
mutex_unlock(&data->lock);
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index a033699910e8..f124062af383 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -69,6 +69,13 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct dev_rot_state *rot_state = iio_priv(indio_dev);
+ struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev;
+ struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion;
+ u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION;
+ union {
+ s16 val16[4];
+ s32 val32[4];
+ } raw_buf;
int ret_type;
int i;
@@ -78,8 +85,37 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (size >= 4) {
- for (i = 0; i < 4; ++i)
- vals[i] = rot_state->scan.sampled_vals[i];
+ if (info->size <= 0 || info->size > sizeof(raw_buf))
+ return -EINVAL;
+
+ hid_sensor_power_state(&rot_state->common_attributes, true);
+
+ ret_type = sensor_hub_input_attr_read_values(hsdev,
+ hsdev->usage,
+ usage_id,
+ info->report_id,
+ SENSOR_HUB_SYNC,
+ info->size,
+ (u8 *)&raw_buf);
+
+ hid_sensor_power_state(&rot_state->common_attributes, false);
+
+ if (ret_type < 0)
+ return ret_type;
+
+ switch (info->size) {
+ case sizeof(raw_buf.val16):
+ for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++)
+ vals[i] = raw_buf.val16[i];
+ break;
+ case sizeof(raw_buf.val32):
+ for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++)
+ vals[i] = raw_buf.val32[i];
+ break;
+ default:
+ return -EINVAL;
+ }
+
ret_type = IIO_VAL_INT_MULTIPLE;
*val_len = 4;
} else
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index 02ea38c8a3e4..e3eb154feb5f 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -106,20 +106,20 @@ static int mpl115_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_PROCESSED:
pm_runtime_get_sync(data->dev);
ret = mpl115_comp_pressure(data, val, val2);
- if (ret < 0)
- return ret;
pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
+ if (ret < 0)
+ return ret;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_RAW:
pm_runtime_get_sync(data->dev);
/* temperature -5.35 C / LSB, 472 LSB is 25 C */
ret = mpl115_read_temp(data);
- if (ret < 0)
- return ret;
pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
+ if (ret < 0)
+ return ret;
*val = ret >> 6;
return IIO_VAL_INT;
diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c
index fcb96c44d954..a2e4ddf095d8 100644
--- a/drivers/iio/temperature/ltc2983.c
+++ b/drivers/iio/temperature/ltc2983.c
@@ -751,7 +751,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
int ret = 0;
struct device *dev = &st->spi->dev;
struct fwnode_handle *ref;
- u32 excitation_current = 0, n_wires = 0;
+ u32 excitation_current = 0, n_wires = 2;
rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
if (!rtd)
@@ -1231,12 +1231,11 @@ static int ltc2983_chan_read(struct ltc2983_data *st,
start_conversion |= LTC2983_STATUS_CHAN_SEL(sensor->chan);
dev_dbg(&st->spi->dev, "Start conversion on chan:%d, status:%02X\n",
sensor->chan, start_conversion);
+ reinit_completion(&st->completion);
/* start conversion */
ret = regmap_write(st->regmap, LTC2983_STATUS_REG, start_conversion);
if (ret)
return ret;
-
- reinit_completion(&st->completion);
/*
* wait for conversion to complete.
* 300 ms should be more than enough to complete the conversion.
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 50bb3c43f40b..8d0d77628d9c 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -514,6 +514,7 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
struct rdma_ah_attr new_ah_attr;
struct cm_device *cm_dev;
struct cm_port *port;
+ u16 pkey_index;
int ret;
port = get_cm_port_from_path(path, sgid_attr);
@@ -522,12 +523,10 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
cm_dev = port->cm_dev;
ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num,
- be16_to_cpu(path->pkey), &av->pkey_index);
+ be16_to_cpu(path->pkey), &pkey_index);
if (ret)
return ret;
- cm_set_av_port(av, port);
-
/*
* av->ah_attr might be initialized based on wc or during
* request processing time which might have reference to sgid_attr.
@@ -542,6 +541,8 @@ static int cm_init_av_by_path(struct sa_path_rec *path,
if (ret)
return ret;
+ av->pkey_index = pkey_index;
+ cm_set_av_port(av, port);
av->timeout = path->packet_life_time + 1;
rdma_move_ah_attr(&av->ah_attr, &new_ah_attr);
return 0;
@@ -2169,8 +2170,10 @@ static int cm_req_handler(struct cm_work *work)
cm_id_priv->av.ah_attr.roce.dmac);
work->path[0].hop_limit = grh->hop_limit;
- /* This destroy call is needed to pair with cm_init_av_for_response */
- cm_destroy_av(&cm_id_priv->av);
+ /*
+ * cm_init_av_by_path() will internally pair with the above
+ * cm_init_av_for_response() if it succeeds.
+ */
ret = cm_init_av_by_path(&work->path[0], gid_attr, &cm_id_priv->av);
if (ret) {
int err;
diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c
index af59486fe418..d41af5ab6403 100644
--- a/drivers/infiniband/core/counters.c
+++ b/drivers/infiniband/core/counters.c
@@ -649,7 +649,7 @@ void rdma_counter_init(struct ib_device *dev)
fail:
for (i = port; i >= rdma_start_port(dev); i--) {
- port_counter = &dev->port_data[port].port_counter;
+ port_counter = &dev->port_data[i].port_counter;
rdma_free_hw_stats_struct(port_counter->hstats);
port_counter->hstats = NULL;
mutex_destroy(&port_counter->lock);
diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c
index f13016dc8016..d6fcb1a4bd4f 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.c
@@ -355,14 +355,14 @@ static int calc_hem_config(struct hns_roce_dev *hr_dev,
bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num);
switch (bt_num) {
case 3:
- index->l1 = l0_idx * chunk_ba_num + l1_idx;
+ index->l1 = (u64)l0_idx * chunk_ba_num + l1_idx;
index->l0 = l0_idx;
- index->buf = l0_idx * chunk_ba_num * chunk_ba_num +
- l1_idx * chunk_ba_num + l2_idx;
+ index->buf = (u64)l0_idx * chunk_ba_num * chunk_ba_num +
+ (u64)l1_idx * chunk_ba_num + l2_idx;
break;
case 2:
index->l0 = l0_idx;
- index->buf = l0_idx * chunk_ba_num + l1_idx;
+ index->buf = (u64)l0_idx * chunk_ba_num + l1_idx;
break;
case 1:
index->buf = l0_idx;
diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c
index 5f39a25064d1..a67b3291e68f 100644
--- a/drivers/infiniband/hw/hns/hns_roce_main.c
+++ b/drivers/infiniband/hw/hns/hns_roce_main.c
@@ -969,7 +969,7 @@ static void check_and_get_armed_cq(struct list_head *cq_list, struct ib_cq *cq)
unsigned long flags;
spin_lock_irqsave(&hr_cq->lock, flags);
- if (cq->comp_handler) {
+ if (cq->comp_handler && hr_cq->ib_cq.poll_ctx != IB_POLL_DIRECT) {
if (!hr_cq->is_armed) {
hr_cq->is_armed = 1;
list_add_tail(&hr_cq->node, cq_list);
diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index 2358fab95784..8ffbc790c168 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -2133,7 +2133,6 @@ static int irdma_create_cq(struct ib_cq *ibcq,
}
cqmr_shadow = &iwpbl_shadow->cq_mr;
info.shadow_area_pa = cqmr_shadow->cq_pbl.addr;
- cqmr->split = true;
} else {
info.shadow_area_pa = cqmr->shadow;
}
@@ -2326,10 +2325,11 @@ static inline u64 *irdma_next_pbl_addr(u64 *pbl, struct irdma_pble_info **pinfo,
* irdma_copy_user_pgaddrs - copy user page address to pble's os locally
* @iwmr: iwmr for IB's user page addresses
* @pbl: ple pointer to save 1 level or 0 level pble
+ * @pbl_len: Max number of PBL entries to populate
* @level: indicated level 0, 1 or 2
*/
static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl,
- enum irdma_pble_level level)
+ u32 pbl_len, enum irdma_pble_level level)
{
struct ib_umem *region = iwmr->region;
struct irdma_pbl *iwpbl = &iwmr->iwpbl;
@@ -2337,7 +2337,9 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl,
struct irdma_pble_info *pinfo;
struct ib_block_iter biter;
u32 idx = 0;
- u32 pbl_cnt = 0;
+
+ if (!pbl_len)
+ return;
pinfo = (level == PBLE_LEVEL_1) ? NULL : palloc->level2.leaf;
@@ -2346,7 +2348,7 @@ static void irdma_copy_user_pgaddrs(struct irdma_mr *iwmr, u64 *pbl,
rdma_umem_for_each_dma_block(region, &biter, iwmr->page_size) {
*pbl = rdma_block_iter_dma_address(&biter);
- if (++pbl_cnt == palloc->total_cnt)
+ if (!--pbl_len)
break;
pbl = irdma_next_pbl_addr(pbl, &pinfo, &idx);
}
@@ -2422,6 +2424,7 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr,
u64 *pbl;
int status;
enum irdma_pble_level level = PBLE_LEVEL_1;
+ u32 pbl_len;
if (lvl) {
status = irdma_get_pble(rf->pble_rsrc, palloc, iwmr->page_cnt,
@@ -2429,16 +2432,18 @@ static int irdma_setup_pbles(struct irdma_pci_f *rf, struct irdma_mr *iwmr,
if (status)
return status;
+ pbl_len = palloc->total_cnt;
iwpbl->pbl_allocated = true;
level = palloc->level;
pinfo = (level == PBLE_LEVEL_1) ? &palloc->level1 :
palloc->level2.leaf;
pbl = pinfo->addr;
} else {
+ pbl_len = IRDMA_MAX_SAVED_PHY_PGADDR;
pbl = iwmr->pgaddrmem;
}
- irdma_copy_user_pgaddrs(iwmr, pbl, level);
+ irdma_copy_user_pgaddrs(iwmr, pbl, pbl_len, level);
if (lvl)
iwmr->pgaddrmem[0] = *pbl;
@@ -2503,7 +2508,8 @@ static int irdma_handle_q_mem(struct irdma_device *iwdev,
case IRDMA_MEMREG_TYPE_CQ:
hmc_p = &cqmr->cq_pbl;
- if (!cqmr->split)
+ if (!(iwdev->rf->sc_dev.hw_attrs.uk_attrs.feature_flags &
+ IRDMA_FEATURE_CQ_RESIZE))
cqmr->shadow = (dma_addr_t)arr[req->cq_pages];
if (lvl)
@@ -2847,6 +2853,7 @@ static int irdma_reg_user_mr_type_mem(struct irdma_mr *iwmr, int access,
int err;
lvl = iwmr->page_cnt != 1 ? PBLE_LEVEL_1 | PBLE_LEVEL_2 : PBLE_LEVEL_0;
+ iwmr->access = access;
err = irdma_setup_pbles(iwdev->rf, iwmr, lvl);
if (err)
diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h
index 97abd45a5cee..346c8adebf1c 100644
--- a/drivers/infiniband/hw/irdma/verbs.h
+++ b/drivers/infiniband/hw/irdma/verbs.h
@@ -62,7 +62,6 @@ struct irdma_hmc_pble {
struct irdma_cq_mr {
struct irdma_hmc_pble cq_pbl;
dma_addr_t shadow;
- bool split;
};
struct irdma_qp_mr {
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 41ca1114a995..f9eb4d32ad75 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -137,6 +137,7 @@ struct mlx4_ib_mr {
dma_addr_t page_map;
u32 npages;
u32 max_pages;
+ int access_flags;
struct mlx4_mr mmr;
struct ib_umem *umem;
size_t page_map_size;
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index e331c04967df..1f2f924b5c30 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -439,6 +439,7 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
if (err)
goto err_mr;
+ mr->access_flags = access_flags;
mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key;
mr->ibmr.page_size = 1U << shift;
@@ -499,6 +500,8 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start,
if (err)
goto release_mpt_entry;
+ } else {
+ mr_access_flags = mmr->access_flags;
}
if (flags & IB_MR_REREG_TRANS) {
@@ -540,8 +543,10 @@ struct ib_mr *mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, u64 start,
* return a failure. But dereg_mr will free the resources.
*/
err = mlx4_mr_hw_write_mpt(dev->dev, &mmr->mmr, pmpt_entry);
- if (!err && flags & IB_MR_REREG_ACCESS)
- mmr->mmr.access = mr_access_flags;
+ if (!err && flags & IB_MR_REREG_ACCESS) {
+ mmr->access_flags = mr_access_flags;
+ mmr->mmr.access = convert_access(mr_access_flags);
+ }
release_mpt_entry:
mlx4_mr_hw_put_mpt(dev->dev, pmpt_entry);
diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c
index 88f534cf690e..1676f819c2f7 100644
--- a/drivers/infiniband/hw/mlx4/sysfs.c
+++ b/drivers/infiniband/hw/mlx4/sysfs.c
@@ -638,12 +638,6 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
p->port_num = port_num;
p->slave = slave;
- ret = kobject_init_and_add(&p->kobj, &port_type,
- kobject_get(dev->dev_ports_parent[slave]),
- "%d", port_num);
- if (ret)
- goto err_alloc;
-
p->pkey_group.name = "pkey_idx";
p->pkey_group.attrs =
alloc_group_attrs(show_port_pkey,
@@ -651,13 +645,9 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
dev->dev->caps.pkey_table_len[port_num]);
if (!p->pkey_group.attrs) {
ret = -ENOMEM;
- goto err_alloc;
+ goto err_free_port;
}
- ret = sysfs_create_group(&p->kobj, &p->pkey_group);
- if (ret)
- goto err_free_pkey;
-
p->gid_group.name = "gid_idx";
p->gid_group.attrs = alloc_group_attrs(show_port_gid_idx, NULL, 1);
if (!p->gid_group.attrs) {
@@ -665,28 +655,47 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
goto err_free_pkey;
}
+ ret = kobject_init_and_add(&p->kobj, &port_type,
+ kobject_get(dev->dev_ports_parent[slave]),
+ "%d", port_num);
+ if (ret)
+ goto err_put;
+
+ ret = sysfs_create_group(&p->kobj, &p->pkey_group);
+ if (ret)
+ goto err_del;
+
ret = sysfs_create_group(&p->kobj, &p->gid_group);
if (ret)
- goto err_free_gid;
+ goto err_remove_pkey;
ret = add_vf_smi_entries(p);
if (ret)
- goto err_free_gid;
+ goto err_remove_gid;
list_add_tail(&p->kobj.entry, &dev->pkeys.pkey_port_list[slave]);
return 0;
-err_free_gid:
- kfree(p->gid_group.attrs[0]);
- kfree(p->gid_group.attrs);
+err_remove_gid:
+ sysfs_remove_group(&p->kobj, &p->gid_group);
+
+err_remove_pkey:
+ sysfs_remove_group(&p->kobj, &p->pkey_group);
+
+err_del:
+ kobject_del(&p->kobj);
+
+err_put:
+ kobject_put(dev->dev_ports_parent[slave]);
+ kobject_put(&p->kobj);
+ return ret;
err_free_pkey:
for (i = 0; i < dev->dev->caps.pkey_table_len[port_num]; ++i)
kfree(p->pkey_group.attrs[i]);
kfree(p->pkey_group.attrs);
-err_alloc:
- kobject_put(dev->dev_ports_parent[slave]);
+err_free_port:
kfree(p);
return ret;
}
diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c
index 388c95562a92..0aee5738e298 100644
--- a/drivers/infiniband/hw/mlx5/devx.c
+++ b/drivers/infiniband/hw/mlx5/devx.c
@@ -1851,6 +1851,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_ASYNC_QUERY)(
return err;
}
+static bool devx_key_in_sub_list(struct list_head *list, u32 key_level1)
+{
+ struct devx_event_subscription *s;
+
+ list_for_each_entry(s, list, event_list)
+ if (s->xa_key_level1 == key_level1)
+ return true;
+
+ return false;
+}
+
static void
subscribe_event_xa_dealloc(struct mlx5_devx_event_table *devx_event_table,
u32 key_level1,
@@ -2098,10 +2109,17 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)(
event_sub = kzalloc(sizeof(*event_sub), GFP_KERNEL);
if (!event_sub) {
+ if (!devx_key_in_sub_list(&sub_list, key_level1))
+ subscribe_event_xa_dealloc(devx_event_table,
+ key_level1,
+ obj,
+ obj_id);
err = -ENOMEM;
goto err;
}
+ event_sub->ev_file = ev_file;
+ event_sub->xa_key_level1 = key_level1;
list_add_tail(&event_sub->event_list, &sub_list);
uverbs_uobject_get(&ev_file->uobj);
if (use_eventfd) {
@@ -2116,9 +2134,6 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)(
}
event_sub->cookie = cookie;
- event_sub->ev_file = ev_file;
- /* May be needed upon cleanup the devx object/subscription */
- event_sub->xa_key_level1 = key_level1;
event_sub->xa_key_level2 = obj_id;
INIT_LIST_HEAD(&event_sub->obj_list);
}
@@ -2163,10 +2178,11 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)(
list_for_each_entry_safe(event_sub, tmp_sub, &sub_list, event_list) {
list_del(&event_sub->event_list);
- subscribe_event_xa_dealloc(devx_event_table,
- event_sub->xa_key_level1,
- obj,
- obj_id);
+ if (!devx_key_in_sub_list(&sub_list, event_sub->xa_key_level1))
+ subscribe_event_xa_dealloc(devx_event_table,
+ event_sub->xa_key_level1,
+ obj,
+ obj_id);
if (event_sub->eventfd)
eventfd_ctx_put(event_sub->eventfd);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 5408ee2b33ec..10422dee6db8 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -1785,9 +1785,13 @@ static int mlx5_ib_alloc_transport_domain(struct mlx5_ib_dev *dev, u32 *tdn,
if ((MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_ETH) ||
(!MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) &&
!MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc)))
- return err;
+ return 0;
+
+ err = mlx5_ib_enable_lb(dev, true, false);
+ if (err)
+ mlx5_cmd_dealloc_transport_domain(dev->mdev, *tdn, uid);
- return mlx5_ib_enable_lb(dev, true, false);
+ return err;
}
static void mlx5_ib_dealloc_transport_domain(struct mlx5_ib_dev *dev, u32 tdn,
@@ -2278,7 +2282,7 @@ static int uar_mmap(struct mlx5_ib_dev *dev, enum mlx5_ib_mmap_cmd cmd,
if (!dyn_uar)
return err;
- mlx5_cmd_uar_dealloc(dev->mdev, idx, context->devx_uid);
+ mlx5_cmd_uar_dealloc(dev->mdev, uar_index, context->devx_uid);
free_bfreg:
mlx5_ib_free_bfreg(dev, bfregi, bfreg_dyn_idx);
@@ -3791,6 +3795,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
dev->port[i].roce.last_port_state = IB_PORT_DOWN;
}
+ mutex_init(&dev->lb.mutex);
+
err = mlx5r_cmd_query_special_mkeys(dev);
if (err)
return err;
@@ -4006,11 +4012,6 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev)
if (err)
return err;
- if ((MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) &&
- (MLX5_CAP_GEN(dev->mdev, disable_local_lb_uc) ||
- MLX5_CAP_GEN(dev->mdev, disable_local_lb_mc)))
- mutex_init(&dev->lb.mutex);
-
if (MLX5_CAP_GEN_64(dev->mdev, general_obj_types) &
MLX5_GENERAL_OBJ_TYPES_CAP_VIRTIO_NET_Q) {
err = mlx5_ib_init_var_table(dev);
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 6968092b6a41..25348f732ac5 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -1707,6 +1707,21 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
if (!(flags & IB_MR_REREG_PD))
new_pd = ib_mr->pd;
+ if (mr->is_odp_implicit && !(flags & IB_MR_REREG_TRANS)) {
+ if (!(new_access_flags & IB_ACCESS_ON_DEMAND))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ /*
+ * Due to all the child mkeys we cannot actually change an
+ * implicit MR in place. If the user did not specify a new
+ * translation then force the fixed implicit MR values.
+ */
+ start = 0;
+ iova = 0;
+ length = U64_MAX;
+ flags |= IB_MR_REREG_TRANS;
+ }
+
if (!(flags & IB_MR_REREG_TRANS)) {
struct ib_umem *umem;
@@ -1721,7 +1736,7 @@ struct ib_mr *mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start,
}
/* DM or ODP MR's don't have a normal umem so we can't re-use it */
if (!mr->umem || is_odp_mr(mr) || is_dmabuf_mr(mr))
- goto recreate;
+ return ERR_PTR(-EOPNOTSUPP);
/*
* Only one active MR can refer to a umem at one time, revoke
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 0a9ae84600b2..6bbf12732051 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -451,16 +451,13 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap,
if (ucmd) {
qp->rq.wqe_cnt = ucmd->rq_wqe_count;
- if (ucmd->rq_wqe_shift > BITS_PER_BYTE * sizeof(ucmd->rq_wqe_shift))
- return -EINVAL;
qp->rq.wqe_shift = ucmd->rq_wqe_shift;
- if ((1 << qp->rq.wqe_shift) /
- sizeof(struct mlx5_wqe_data_seg) <
- wq_sig)
+ if (check_shl_overflow(1, qp->rq.wqe_shift, &wqe_size))
+ return -EINVAL;
+ if (wqe_size / sizeof(struct mlx5_wqe_data_seg) < wq_sig)
return -EINVAL;
qp->rq.max_gs =
- (1 << qp->rq.wqe_shift) /
- sizeof(struct mlx5_wqe_data_seg) -
+ wqe_size / sizeof(struct mlx5_wqe_data_seg) -
wq_sig;
qp->rq.max_post = qp->rq.wqe_cnt;
} else {
@@ -3090,6 +3087,7 @@ static int create_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
int err;
if (params->is_rss_raw) {
+ rdma_restrack_no_track(&qp->ibqp.res);
err = create_rss_raw_qp_tir(dev, pd, qp, params);
goto out;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_mmap.c b/drivers/infiniband/sw/rxe/rxe_mmap.c
index 6b7f2bd69879..466ab6dbe9af 100644
--- a/drivers/infiniband/sw/rxe/rxe_mmap.c
+++ b/drivers/infiniband/sw/rxe/rxe_mmap.c
@@ -93,18 +93,31 @@ int rxe_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
goto done;
found_it:
+ /*
+ * Increment refcount and check whether it is being freed atm while
+ * holding lock to prevent UAF
+ */
+ if (!kref_get_unless_zero(&ip->ref)) {
+ spin_unlock_bh(&rxe->pending_lock);
+ ret = -ENXIO;
+ goto done;
+ }
+
list_del_init(&ip->pending_mmaps);
spin_unlock_bh(&rxe->pending_lock);
+ vma->vm_ops = &rxe_vm_ops;
+ vma->vm_private_data = ip;
+
ret = remap_vmalloc_range(vma, ip->obj, 0);
if (ret) {
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+ kref_put(&ip->ref, rxe_mmap_release);
rxe_dbg_dev(rxe, "err %d from remap_vmalloc_range\n", ret);
goto done;
}
- vma->vm_ops = &rxe_vm_ops;
- vma->vm_private_data = ip;
- rxe_vma_open(vma);
done:
return ret;
}
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 70e9bf3a7042..539a30ec9821 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -273,6 +273,7 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp)
struct rxe_recv_wqe *wqe;
struct ib_event ev;
unsigned int count;
+ unsigned int num_sge;
size_t size;
unsigned long flags;
@@ -288,12 +289,13 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp)
}
/* don't trust user space data */
- if (unlikely(wqe->dma.num_sge > srq->rq.max_sge)) {
+ num_sge = wqe->dma.num_sge;
+ if (unlikely(num_sge > srq->rq.max_sge)) {
spin_unlock_irqrestore(&srq->rq.consumer_lock, flags);
rxe_dbg_qp(qp, "invalid num_sge in SRQ entry\n");
return RESPST_ERR_MALFORMED_WQE;
}
- size = sizeof(*wqe) + wqe->dma.num_sge*sizeof(struct rxe_sge);
+ size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge);
memcpy(&qp->resp.srq_wqe, wqe, size);
qp->resp.wqe = &qp->resp.srq_wqe.wqe;
@@ -317,6 +319,29 @@ static enum resp_states get_srq_wqe(struct rxe_qp *qp)
return RESPST_CHK_LENGTH;
}
+static enum resp_states rxe_get_recv_wqe(struct rxe_qp *qp)
+{
+ struct rxe_queue *q = qp->rq.queue;
+ struct rxe_recv_wqe *wqe;
+ unsigned int num_sge;
+ size_t size;
+
+ wqe = queue_head(q, QUEUE_TYPE_FROM_CLIENT);
+ if (!wqe)
+ return RESPST_ERR_RNR;
+
+ num_sge = wqe->dma.num_sge;
+ if (unlikely(num_sge > qp->rq.max_sge)) {
+ rxe_dbg_qp(qp, "invalid num_sge in recv WQE\n");
+ return RESPST_ERR_MALFORMED_WQE;
+ }
+ size = sizeof(*wqe) + num_sge * sizeof(struct rxe_sge);
+ memcpy(&qp->resp.srq_wqe, wqe, size);
+
+ qp->resp.wqe = &qp->resp.srq_wqe.wqe;
+ return RESPST_CHK_LENGTH;
+}
+
static enum resp_states check_resource(struct rxe_qp *qp,
struct rxe_pkt_info *pkt)
{
@@ -337,9 +362,7 @@ static enum resp_states check_resource(struct rxe_qp *qp,
if (srq)
return get_srq_wqe(qp);
- qp->resp.wqe = queue_head(qp->rq.queue,
- QUEUE_TYPE_FROM_CLIENT);
- return (qp->resp.wqe) ? RESPST_CHK_LENGTH : RESPST_ERR_RNR;
+ return rxe_get_recv_wqe(qp);
}
return RESPST_CHK_LENGTH;
diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c
index e4fa61a0c6ca..96a8e23a8ce2 100644
--- a/drivers/infiniband/sw/siw/siw_qp_rx.c
+++ b/drivers/infiniband/sw/siw/siw_qp_rx.c
@@ -848,6 +848,15 @@ int siw_proc_rresp(struct siw_qp *qp)
}
mem_p = *mem;
+ if (unlikely(wqe->processed + srx->fpdu_part_rem > wqe->bytes)) {
+ siw_dbg_qp(qp, "rresp len: %d + %d > %d\n",
+ wqe->processed, srx->fpdu_part_rem, wqe->bytes);
+ wqe->wc_status = SIW_WC_LOC_LEN_ERR;
+ siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
+ DDP_ETYPE_TAGGED_BUF,
+ DDP_ECODE_T_BASE_BOUNDS, 0);
+ return -EINVAL;
+ }
bytes = min(srx->fpdu_part_rem, srx->skb_new);
if (mem_p->mem_obj == NULL)
diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c
index 2c3c8b32190f..10061b218758 100644
--- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c
+++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c
@@ -224,8 +224,9 @@ static int rdma_write_sg(struct rtrs_srv_op *id)
/* WR will fail with length error
* if this is 0
*/
- if (plist->length == 0) {
- rtrs_err(s, "Invalid RDMA-Write sg list length 0\n");
+ if (plist->length == 0 || plist->length > max_chunk_size) {
+ rtrs_err(s, "Invalid RDMA-Write sg list length %u\n",
+ plist->length);
return -EINVAL;
}
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 4bebc34a2929..13e1fc5e2759 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1129,9 +1129,10 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
struct srp_imm_buf *imm_buf = srpt_get_desc_buf(srp_cmd);
void *data = (void *)srp_cmd + imm_data_offset;
uint32_t len = be32_to_cpu(imm_buf->len);
- uint32_t req_size = imm_data_offset + len;
+ uint32_t req_size;
- if (req_size > srp_max_req_size) {
+ if (check_add_overflow((uint32_t)imm_data_offset, len, &req_size) ||
+ req_size > srp_max_req_size) {
pr_err("Immediate data (length %d + %d) exceeds request size %d\n",
imm_data_offset, len, srp_max_req_size);
return -EINVAL;
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
index 763642c8cee9..9f7ba41a947b 100644
--- a/drivers/input/joystick/iforce/iforce-packets.c
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -192,14 +192,18 @@ void iforce_process_packet(struct iforce *iforce,
/* Check if an effect was just started or stopped */
i = data[1] & 0x7f;
- if (data[1] & 0x80) {
- if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
- /* Report play event */
- input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+ if (i < IFORCE_EFFECTS_MAX) {
+ if (data[1] & 0x80) {
+ if (!test_and_set_bit(FF_CORE_IS_PLAYED,
+ iforce->core_effects[i].flags)) {
+ /* Report play event */
+ input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+ }
+ } else if (test_and_clear_bit(FF_CORE_IS_PLAYED,
+ iforce->core_effects[i].flags)) {
+ /* Report stop event */
+ input_report_ff_status(dev, i, FF_STATUS_STOPPED);
}
- } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
- /* Report stop event */
- input_report_ff_status(dev, i, FF_STATUS_STOPPED);
}
for (j = 3; j < len; j += 2)
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
index 3833ac47b2b8..ecccf1c45772 100644
--- a/drivers/input/joystick/maplecontrol.c
+++ b/drivers/input/joystick/maplecontrol.c
@@ -112,6 +112,8 @@ static int probe_maple_controller(struct device *dev)
pad->dev = idev;
pad->mdev = mdev;
+ maple_set_drvdata(mdev, pad);
+
idev->open = dc_pad_open;
idev->close = dc_pad_close;
@@ -146,7 +148,6 @@ static int probe_maple_controller(struct device *dev)
goto fail;
mdev->driver = mdrv;
- maple_set_drvdata(mdev, pad);
return 0;
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
index d08b565be24c..30bbdfb8b642 100644
--- a/drivers/input/keyboard/maple_keyb.c
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -169,6 +169,8 @@ static int probe_maple_kbd(struct device *dev)
kbd->dev = idev;
memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
+ maple_set_drvdata(mdev, kbd);
+
idev->name = mdev->product_name;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
idev->keycode = kbd->keycode;
@@ -193,8 +195,6 @@ static int probe_maple_kbd(struct device *dev)
mdev->driver = mdrv;
- maple_set_drvdata(mdev, kbd);
-
return error;
fail_register:
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index ca4155814bf6..2bac9d9c7b0c 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -407,7 +407,16 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
static void ims_pcu_report_events(struct ims_pcu *pcu)
{
- u32 data = get_unaligned_be32(&pcu->read_buf[3]);
+ u32 data;
+
+ /* 6-axis setting (1 byte) + button data + checksum */
+ if (pcu->read_pos < IMS_PCU_DATA_OFFSET + 1 + sizeof(data) + 1) {
+ dev_warn(pcu->dev, "Short buttons report: %d bytes\n",
+ pcu->read_pos);
+ return;
+ }
+
+ data = get_unaligned_be32(&pcu->read_buf[IMS_PCU_DATA_OFFSET + 1]);
ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK);
if (pcu->gamepad)
@@ -667,11 +676,19 @@ static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
return error;
}
- if (expected_response && pcu->cmd_buf[2] != expected_response) {
- dev_err(pcu->dev,
- "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n",
- pcu->cmd_buf[2], expected_response);
- return -EINVAL;
+ if (expected_response) {
+ if (pcu->cmd_buf_len < 3) {
+ dev_err(pcu->dev, "Short response from bootloader: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
+ if (pcu->cmd_buf[2] != expected_response) {
+ dev_err(pcu->dev,
+ "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n",
+ pcu->cmd_buf[2], expected_response);
+ return -EINVAL;
+ }
}
return 0;
@@ -699,6 +716,12 @@ static int ims_pcu_get_info(struct ims_pcu *pcu)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + IMS_PCU_SET_INFO_SIZE + 1) {
+ dev_err(pcu->dev, "Short GET_INFO response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
memcpy(pcu->part_number,
&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
sizeof(pcu->part_number));
@@ -796,6 +819,12 @@ static int ims_pcu_verify_block(struct ims_pcu *pcu,
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_BL_DATA_OFFSET + sizeof(*fragment) + len + 1) {
+ dev_err(pcu->dev, "Short READ_APP response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET];
if (get_unaligned_le32(&fragment->addr) != addr ||
fragment->len != len) {
@@ -993,6 +1022,10 @@ ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
error);
/* Assume the LED is OFF */
brightness = LED_OFF;
+ } else if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 2 + 1) {
+ dev_err(pcu->dev, "Short GET_BRIGHTNESS response: %d bytes\n",
+ pcu->cmd_buf_len);
+ brightness = LED_OFF;
} else {
brightness =
get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
@@ -1140,6 +1173,8 @@ static ssize_t ims_pcu_reset_device(struct device *dev,
dev_info(pcu->dev, "Attempting to reset device\n");
+ guard(mutex)(&pcu->cmd_mutex);
+
error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1);
if (error) {
dev_info(pcu->dev,
@@ -1272,6 +1307,12 @@ static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
if (error)
return error;
+ if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) {
+ dev_err(pcu->dev, "Short OFN_GET_CONFIG response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
if (result < 0)
return -EIO;
@@ -1292,6 +1333,12 @@ static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
if (error)
return error;
+ if (pcu->cmd_buf_len < OFN_REG_RESULT_OFFSET + 2 + 1) {
+ dev_err(pcu->dev, "Short OFN_SET_CONFIG response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
if (result < 0)
return -EIO;
@@ -1502,7 +1549,7 @@ static void ims_pcu_irq(struct urb *urb)
}
dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__,
- urb->actual_length, urb->actual_length, pcu->urb_in_buf);
+ urb->actual_length, urb->actual_length, urb->transfer_buffer);
if (urb == pcu->urb_in)
ims_pcu_process_data(pcu, urb);
@@ -1630,8 +1677,9 @@ ims_pcu_get_cdc_union_desc(struct usb_interface *intf)
while (buflen >= sizeof(*union_desc)) {
union_desc = (struct usb_cdc_union_desc *)buf;
- if (union_desc->bLength > buflen) {
- dev_err(&intf->dev, "Too large descriptor\n");
+ if (union_desc->bLength < 2 || union_desc->bLength > buflen) {
+ dev_err(&intf->dev, "Invalid descriptor length: %d\n",
+ union_desc->bLength);
return NULL;
}
@@ -1667,7 +1715,7 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc
pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev,
union_desc->bMasterInterface0);
- if (!pcu->ctrl_intf)
+ if (pcu->ctrl_intf != intf)
return -EINVAL;
alt = pcu->ctrl_intf->cur_altsetting;
@@ -1676,6 +1724,12 @@ static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pc
return -ENODEV;
pcu->ep_ctrl = &alt->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(pcu->ep_ctrl)) {
+ dev_err(pcu->dev,
+ "Control endpoint is not INTERRUPT IN\n");
+ return -EINVAL;
+ }
+
pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
pcu->data_intf = usb_ifnum_to_if(pcu->udev,
@@ -1757,11 +1811,16 @@ static void ims_pcu_stop_io(struct ims_pcu *pcu)
static int ims_pcu_line_setup(struct ims_pcu *pcu)
{
struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting;
- struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf;
+ struct usb_cdc_line_coding *line __free(kfree) =
+ kmalloc(sizeof(*line), GFP_KERNEL);
int error;
- memset(line, 0, sizeof(*line));
+ if (!line)
+ return -ENOMEM;
+
line->dwDTERate = cpu_to_le32(57600);
+ line->bCharFormat = USB_CDC_1_STOP_BITS;
+ line->bParityType = USB_CDC_NO_PARITY;
line->bDataBits = 8;
error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
@@ -1805,6 +1864,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) {
+ dev_err(pcu->dev, "Short GET_FW_VERSION response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
snprintf(pcu->fw_version, sizeof(pcu->fw_version),
"%02d%02d%02d%02d.%c%c",
pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
@@ -1817,6 +1882,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 6 + 1) {
+ dev_err(pcu->dev, "Short GET_BL_VERSION response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
snprintf(pcu->bl_version, sizeof(pcu->bl_version),
"%02d%02d%02d%02d.%c%c",
pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
@@ -1829,6 +1900,12 @@ static int ims_pcu_get_device_info(struct ims_pcu *pcu)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) {
+ dev_err(pcu->dev, "Short RESET_REASON response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
snprintf(pcu->reset_reason, sizeof(pcu->reset_reason),
"%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
@@ -1855,6 +1932,12 @@ static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 1 + 1) {
+ dev_err(pcu->dev, "Short GET_DEVICE_ID response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
*device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET];
dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id);
@@ -1960,6 +2043,12 @@ static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu)
return error;
}
+ if (pcu->cmd_buf_len < IMS_PCU_DATA_OFFSET + 15 + 4 + 1) {
+ dev_err(pcu->dev, "Short QUERY_DEVICE response: %d bytes\n",
+ pcu->cmd_buf_len);
+ return -EIO;
+ }
+
pcu->fw_start_addr =
get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]);
pcu->fw_end_addr =
@@ -2065,7 +2154,6 @@ static int ims_pcu_probe(struct usb_interface *intf,
static void ims_pcu_disconnect(struct usb_interface *intf)
{
struct ims_pcu *pcu = usb_get_intfdata(intf);
- struct usb_host_interface *alt = intf->cur_altsetting;
usb_set_intfdata(intf, NULL);
@@ -2073,7 +2161,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf)
* See if we are dealing with control or data interface. The cleanup
* happens when we unbind primary (control) interface.
*/
- if (alt->desc.bInterfaceClass != USB_CLASS_COMM)
+ if (intf != pcu->ctrl_intf)
return;
sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
@@ -2086,6 +2174,7 @@ static void ims_pcu_disconnect(struct usb_interface *intf)
ims_pcu_destroy_application_mode(pcu);
ims_pcu_buffers_free(pcu);
+ usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf);
kfree(pcu);
}
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index c0baf3729dc2..fd0dfa9f0d89 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -424,8 +424,17 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
if (error)
return error;
}
- data->width_x = data->max_x / x_traces;
- data->width_y = data->max_y / y_traces;
+
+ if (!x_traces || !y_traces) {
+ dev_warn(&client->dev,
+ "invalid trace numbers: x=%u, y=%u\n",
+ x_traces, y_traces);
+ data->width_x = 1;
+ data->width_y = 1;
+ } else {
+ data->width_x = data->max_x / x_traces;
+ data->width_y = data->max_y / y_traces;
+ }
if (device_property_read_u32(&client->dev,
"touchscreen-x-mm", &x_mm) ||
@@ -439,8 +448,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
data->x_res = elan_convert_resolution(hw_x_res, data->pattern);
data->y_res = elan_convert_resolution(hw_y_res, data->pattern);
} else {
- data->x_res = (data->max_x + 1) / x_mm;
- data->y_res = (data->max_y + 1) / y_mm;
+ if (unlikely(x_mm == 0 || y_mm == 0)) {
+ dev_warn(&client->dev,
+ "invalid physical dimensions: x_mm=%u, y_mm=%u\n",
+ x_mm, y_mm);
+ data->x_res = 1;
+ data->y_res = 1;
+ } else {
+ data->x_res = (data->max_x + 1) / x_mm;
+ data->y_res = (data->max_y + 1) / y_mm;
+ }
}
if (device_property_read_bool(&client->dev, "elan,clickpad"))
@@ -959,6 +976,7 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num,
if (data->report_features & ETP_FEATURE_REPORT_MK) {
unsigned int mk_x, mk_y, area_x, area_y;
+ int adj_width_x, adj_width_y;
u8 mk_data = high_precision ?
packet[ETP_MK_DATA_OFFSET + contact_num] :
finger_data[3];
@@ -970,8 +988,14 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num,
* To avoid treating large finger as palm, let's reduce
* the width x and y per trace.
*/
- area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
- area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+ adj_width_x = data->width_x > ETP_FWIDTH_REDUCE ?
+ data->width_x - ETP_FWIDTH_REDUCE : 0;
+ adj_width_y = data->width_y > ETP_FWIDTH_REDUCE ?
+ data->width_y - ETP_FWIDTH_REDUCE : 0;
+
+ area_x = mk_x * adj_width_x;
+ area_y = mk_y * adj_width_y;
input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
index 2de64d6a04d1..47ea8cc5f61b 100644
--- a/drivers/input/mouse/maplemouse.c
+++ b/drivers/input/mouse/maplemouse.c
@@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapleq *mq)
static int dc_mouse_open(struct input_dev *dev)
{
- struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+ struct dc_mouse *mse = input_get_drvdata(dev);
maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
MAPLE_FUNC_MOUSE);
@@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_dev *dev)
static void dc_mouse_close(struct input_dev *dev)
{
- struct dc_mouse *mse = maple_get_drvdata(to_maple_dev(&dev->dev));
+ struct dc_mouse *mse = input_get_drvdata(dev);
maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
MAPLE_FUNC_MOUSE);
@@ -88,6 +88,9 @@ static int probe_maple_mouse(struct device *dev)
mse->dev = input_dev;
mse->mdev = mdev;
+ maple_set_drvdata(mdev, mse);
+
+ input_set_drvdata(input_dev, mse);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
@@ -102,12 +105,12 @@ static int probe_maple_mouse(struct device *dev)
goto fail_register;
mdev->driver = mdrv;
- maple_set_drvdata(mdev, mse);
return error;
fail_register:
input_free_device(input_dev);
+ maple_set_drvdata(mdev, NULL);
fail_nomem:
kfree(mse);
fail:
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 1b45b1d3077d..17dbfdf26460 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -448,11 +448,13 @@ static int __init rmi_bus_init(void)
if (error) {
pr_err("%s: error registering the RMI physical driver: %d\n",
__func__, error);
- goto err_unregister_bus;
+ goto err_unregister_function_handlers;
}
return 0;
+err_unregister_function_handlers:
+ rmi_unregister_function_handlers();
err_unregister_bus:
bus_unregister(&rmi_bus_type);
return error;
diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
index 35045f161dc2..b2155c8e20e7 100644
--- a/drivers/input/rmi4/rmi_f30.c
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn,
int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END);
f30->gpioled_key_map = devm_kcalloc(&fn->dev,
- button_count,
+ f30->gpioled_count,
sizeof(f30->gpioled_key_map[0]),
GFP_KERNEL);
if (!f30->gpioled_key_map) {
diff --git a/drivers/input/rmi4/rmi_f3a.c b/drivers/input/rmi4/rmi_f3a.c
index 0e8baed84dbb..a0777644eef0 100644
--- a/drivers/input/rmi4/rmi_f3a.c
+++ b/drivers/input/rmi4/rmi_f3a.c
@@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a,
int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END);
f3a->gpio_key_map = devm_kcalloc(&fn->dev,
- button_count,
+ f3a->gpio_count,
sizeof(f3a->gpio_key_map[0]),
GFP_KERNEL);
if (!f3a->gpio_key_map) {
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 294bd942ee37..7c4447c271d4 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -1056,7 +1056,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
}
ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03;
- ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f;
+ ts->max_touch_num = min(ts->config[MAX_CONTACTS_LOC] & 0x0f,
+ GOODIX_MAX_CONTACTS);
x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]);
y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
index af233b6a16d9..cf0f454976c4 100644
--- a/drivers/input/touchscreen/mms114.c
+++ b/drivers/input/touchscreen/mms114.c
@@ -165,7 +165,7 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou
unsigned int x;
unsigned int y;
- if (touch->id > MMS114_MAX_TOUCH) {
+ if (touch->id == 0 || touch->id > MMS114_MAX_TOUCH) {
dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
return;
}
@@ -218,7 +218,9 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id)
struct i2c_client *client = data->client;
struct input_dev *input_dev = data->input_dev;
struct mms114_touch touch[MMS114_MAX_TOUCH];
+ struct mms114_touch *t;
int packet_size;
+ int event_size;
int touch_size;
int index;
int error;
@@ -234,11 +236,19 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id)
if (packet_size <= 0)
goto out;
+ if (packet_size > sizeof(touch)) {
+ dev_err(&client->dev, "Invalid packet size %d (max %zu)\n",
+ packet_size, sizeof(touch));
+ goto out;
+ }
+
/* MMS136 has slightly different event size */
if (data->type == TYPE_MMS134S || data->type == TYPE_MMS136)
- touch_size = packet_size / MMS136_EVENT_SIZE;
+ event_size = MMS136_EVENT_SIZE;
else
- touch_size = packet_size / MMS114_EVENT_SIZE;
+ event_size = MMS114_EVENT_SIZE;
+
+ touch_size = packet_size / event_size;
error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size,
(u8 *)touch);
@@ -246,18 +256,20 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id)
goto out;
for (index = 0; index < touch_size; index++) {
- switch (touch[index].type) {
+ t = (struct mms114_touch *)((u8 *)touch + index * event_size);
+
+ switch (t->type) {
case MMS114_TYPE_TOUCHSCREEN:
- mms114_process_mt(data, touch + index);
+ mms114_process_mt(data, t);
break;
case MMS114_TYPE_TOUCHKEY:
- mms114_process_touchkey(data, touch + index);
+ mms114_process_touchkey(data, t);
break;
default:
dev_err(&client->dev, "Wrong touch type (%d)\n",
- touch[index].type);
+ t->type);
break;
}
}
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
index bde3c6ee3c60..ff1a0d5b2385 100644
--- a/drivers/input/touchscreen/touchwin.c
+++ b/drivers/input/touchscreen/touchwin.c
@@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct serio *serio,
if (data) { /* touch */
tw->touched = 1;
tw->data[tw->idx++] = data;
- /* verify length and that the two Y's are the same */
- if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
- input_report_abs(dev, ABS_X, tw->data[0]);
- input_report_abs(dev, ABS_Y, tw->data[1]);
- input_report_key(dev, BTN_TOUCH, 1);
- input_sync(dev);
+ /* a full packet ends the accumulation, valid or not */
+ if (tw->idx == TW_LENGTH) {
+ /* report only if the two Y's are the same */
+ if (tw->data[1] == tw->data[2]) {
+ input_report_abs(dev, ABS_X, tw->data[0]);
+ input_report_abs(dev, ABS_Y, tw->data[1]);
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ }
tw->idx = 0;
}
} else if (tw->touched) { /* untouch */
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 2571a782b7b6..d81defd49949 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -926,12 +926,13 @@ static inline int get_hpet_devid(int id)
}
enum amd_iommu_intr_mode_type {
- AMD_IOMMU_GUEST_IR_LEGACY,
-
- /* This mode is not visible to users. It is used when
- * we cannot fully enable vAPIC and fallback to only support
- * legacy interrupt remapping via 128-bit IRTE.
+ /*
+ * The legacy format mode is not visible to users to prevent the user
+ * from crashing x2APIC systems, which for all intents and purposes
+ * require 128-bit IRTEs. The legacy format will be forced as needed
+ * when hardware doesn't support 128-bit IRTEs.
*/
+ AMD_IOMMU_GUEST_IR_LEGACY,
AMD_IOMMU_GUEST_IR_LEGACY_GA,
AMD_IOMMU_GUEST_IR_VAPIC,
};
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index a05a7501e107..23b1960024b6 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -158,11 +158,17 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
for (i = 0; i < nr_irqs; i++) {
struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
+ /*
+ * irq_map[] is indexed by GIC SPI number. The parent domain's
+ * hwirq contains the GIC interrupt number (GIC SPI +
+ * GIC_IRQ_START).
+ */
+ cb->irq_map[d->parent_data->hwirq - GIC_IRQ_START] = IRQ_FREE;
+ cb->write(d->parent_data->hwirq - GIC_IRQ_START, cb->safe_map);
irq_domain_reset_irq_data(d);
- cb->irq_map[d->hwirq] = IRQ_FREE;
- cb->write(d->hwirq, cb->safe_map);
}
raw_spin_unlock(&cb->lock);
+ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
}
static int crossbar_domain_translate(struct irq_domain *d,
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 840108727b13..d3ff3ab57d4d 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -3218,11 +3218,9 @@ static void its_cpu_init_collection(struct its_node *its)
/* avoid cross node collections and its mapping */
if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) {
- struct device_node *cpu_node;
+ struct device_node *cpu_node __free(device_node) = of_get_cpu_node(cpu, NULL);
- cpu_node = of_get_cpu_node(cpu, NULL);
- if (its->numa_node != NUMA_NO_NODE &&
- its->numa_node != of_node_to_nid(cpu_node))
+ if (its->numa_node != NUMA_NO_NODE && its->numa_node != of_node_to_nid(cpu_node))
return;
}
diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c
index b2d61d4f6fe6..c218c588f37a 100644
--- a/drivers/irqchip/irq-ts4800.c
+++ b/drivers/irqchip/irq-ts4800.c
@@ -28,6 +28,7 @@ struct ts4800_irq_data {
void __iomem *base;
struct platform_device *pdev;
struct irq_domain *domain;
+ unsigned int parent_irq;
};
static void ts4800_irq_mask(struct irq_data *d)
@@ -134,6 +135,7 @@ static int ts4800_ic_probe(struct platform_device *pdev)
irq_set_chained_handler_and_data(parent_irq,
ts4800_ic_chained_handle_irq, data);
+ data->parent_irq = parent_irq;
platform_set_drvdata(pdev, data);
return 0;
@@ -142,6 +144,14 @@ static int ts4800_ic_probe(struct platform_device *pdev)
static int ts4800_ic_remove(struct platform_device *pdev)
{
struct ts4800_irq_data *data = platform_get_drvdata(pdev);
+ unsigned int hwirq;
+
+ irq_set_chained_handler_and_data(data->parent_irq, NULL, NULL);
+
+ for (hwirq = 0; hwirq < 8; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(data->domain, hwirq));
+
+ irq_dispose_mapping(data->parent_irq);
irq_domain_remove(data->domain);
diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c
index 3d361c920030..d69e1febc79f 100644
--- a/drivers/leds/uleds.c
+++ b/drivers/leds/uleds.c
@@ -102,7 +102,8 @@ static ssize_t uleds_write(struct file *file, const char __user *buffer,
name = udev->user_dev.name;
if (!name[0] || !strcmp(name, ".") || !strcmp(name, "..") ||
- strchr(name, '/')) {
+ strnchr(name, sizeof(udev->user_dev.name), '/') ||
+ !strnchr(name, sizeof(udev->user_dev.name), '\0')) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c
index 91487aa4d7da..8bcecddee0eb 100644
--- a/drivers/mailbox/mtk-adsp-mailbox.c
+++ b/drivers/mailbox/mtk-adsp-mailbox.c
@@ -19,6 +19,7 @@ struct mtk_adsp_mbox_priv {
struct mbox_controller mbox;
void __iomem *va_mboxreg;
const struct mtk_adsp_mbox_cfg *cfg;
+ int irq;
};
struct mtk_adsp_mbox_cfg {
@@ -67,6 +68,8 @@ static int mtk_adsp_mbox_startup(struct mbox_chan *chan)
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in);
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out);
+ enable_irq(priv->irq);
+
return 0;
}
@@ -74,6 +77,8 @@ static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan)
{
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
+ disable_irq(priv->irq);
+
/* Clear ADSP mbox command */
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in);
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out);
@@ -139,8 +144,10 @@ static int mtk_adsp_mbox_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ priv->irq = irq;
ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq,
- mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE,
+ mtk_adsp_mbox_isr,
+ IRQF_TRIGGER_NONE | IRQF_NO_AUTOEN,
dev_name(dev), mbox->chans);
if (ret < 0)
return ret;
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 24d5ade3462d..4177d23eb7d6 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -2189,7 +2189,9 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c
struct dm_io_region io_reg = {
.bdev = c->bdev,
.sector = block_to_sector(c, block),
- .count = block_to_sector(c, count),
+ .count = likely(c->sectors_per_block_bits >= 0) ?
+ count << c->sectors_per_block_bits :
+ count * (c->block_size >> SECTOR_SHIFT),
};
if (WARN_ON_ONCE(dm_bufio_in_request()))
diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c
index 6acfa5bf97a4..b7770e2aa77b 100644
--- a/drivers/md/dm-era-target.c
+++ b/drivers/md/dm-era-target.c
@@ -810,8 +810,10 @@ static struct era_metadata *metadata_open(struct block_device *bdev,
int r;
struct era_metadata *md = kzalloc(sizeof(*md), GFP_KERNEL);
- if (!md)
- return NULL;
+ if (!md) {
+ DMERR("could not allocate metadata struct");
+ return ERR_PTR(-ENOMEM);
+ }
md->bdev = bdev;
md->block_size = block_size;
@@ -1229,6 +1231,7 @@ static dm_block_t get_block(struct era *era, struct bio *bio)
static void remap_to_origin(struct era *era, struct bio *bio)
{
bio_set_dev(bio, era->origin_dev->bdev);
+ bio->bi_iter.bi_sector = dm_target_offset(era->ti, bio->bi_iter.bi_sector);
}
/*
@@ -1561,7 +1564,7 @@ static void era_dtr(struct dm_target *ti)
static int era_map(struct dm_target *ti, struct bio *bio)
{
struct era *era = ti->private;
- dm_block_t block = get_block(era, bio);
+ dm_block_t block;
/*
* All bios get remapped to the origin device. We do this now, but
@@ -1569,6 +1572,7 @@ static int era_map(struct dm_target *ti, struct bio *bio)
* block is marked in this era.
*/
remap_to_origin(era, bio);
+ block = get_block(era, bio);
/*
* REQ_PREFLUSH bios carry no data, so we're not interested in them.
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index be1bf3865415..1b562d2189c5 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1483,9 +1483,6 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se
*metadata_offset = 0;
}
- if (unlikely(!is_power_of_2(ic->tag_size)))
- hash_offset = (hash_offset + to_copy) % ic->tag_size;
-
total_size -= to_copy;
} while (unlikely(total_size));
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a9cde9fe8fa3..874778c74196 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -683,7 +683,7 @@ static void list_version_get_info(struct target_type *tt, void *param)
struct vers_iter *info = param;
/* Check space - it might have changed since the first iteration */
- if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) {
+ if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) {
info->flags = DM_BUFFER_FULL_FLAG;
return;
}
@@ -1806,8 +1806,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
goto out_argv;
table = dm_get_live_table(md, &srcu_idx);
- if (!table)
+ if (!table) {
+ DMERR("The device has no table.");
+ r = -EINVAL;
goto out_table;
+ }
if (dm_deleting_md(md)) {
r = -ENXIO;
@@ -2337,7 +2340,7 @@ int __init dm_early_create(struct dm_ioctl *dmi,
/* resume device */
r = dm_resume(md);
if (r)
- goto err_destroy_table;
+ goto err_hash_remove;
DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
dm_put(md);
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index 94d3f13e673e..5b30b33001c4 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -425,6 +425,9 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
*/
bitset_size = dm_round_up(region_count, BITS_PER_LONG);
bitset_size >>= BYTE_SHIFT;
+ /* Handle dm_round_up rollover on 32-bit systems */
+ if (!bitset_size)
+ bitset_size = 1UL << (BITS_PER_LONG - BYTE_SHIFT);
lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits);
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index db2d997a6c18..6b1a554eb7d4 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -683,10 +683,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
*/
last = raw_cpu_ptr(stats->last);
stats_aux->merged =
- (bi_sector == (READ_ONCE(last->last_sector) &&
- ((bi_rw == WRITE) ==
- (READ_ONCE(last->last_rw) == WRITE))
- ));
+ bi_sector == READ_ONCE(last->last_sector) &&
+ (bi_rw == WRITE) == (READ_ONCE(last->last_rw) == WRITE);
WRITE_ONCE(last->last_sector, end_sector);
WRITE_ONCE(last->last_rw, bi_rw);
} else
@@ -833,10 +831,10 @@ static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long
result = jiffies_to_msecs(j & 0x3fffff);
if (j >= 1 << 22) {
mult = jiffies_to_msecs(1 << 22);
- result += (unsigned long long)mult * (unsigned long long)jiffies_to_msecs((j >> 22) & 0x3fffff);
+ result += (unsigned long long)mult * ((j >> 22) & 0x3fffff);
}
if (j >= 1ULL << 44)
- result += (unsigned long long)mult * (unsigned long long)mult * (unsigned long long)jiffies_to_msecs(j >> 44);
+ result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44);
return result;
}
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 6022189c1388..23cc0335b41f 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -186,6 +186,7 @@ struct dm_pool_metadata {
uint32_t time;
dm_block_t root;
dm_block_t details_root;
+ dm_block_t held_root;
struct list_head thin_devices;
uint64_t trans_id;
unsigned long flags;
@@ -748,6 +749,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd)
*/
pmd->root = le64_to_cpu(disk_super->data_mapping_root);
pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+ pmd->held_root = le64_to_cpu(disk_super->held_root);
__setup_btree_details(pmd);
dm_bm_unlock(sblock);
@@ -838,6 +840,7 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
pmd->time = le32_to_cpu(disk_super->time);
pmd->root = le64_to_cpu(disk_super->data_mapping_root);
pmd->details_root = le64_to_cpu(disk_super->device_details_root);
+ pmd->held_root = le64_to_cpu(disk_super->held_root);
pmd->trans_id = le64_to_cpu(disk_super->trans_id);
pmd->flags = le32_to_cpu(disk_super->flags);
pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
@@ -928,6 +931,7 @@ static int __commit_transaction(struct dm_pool_metadata *pmd)
disk_super->time = cpu_to_le32(pmd->time);
disk_super->data_mapping_root = cpu_to_le64(pmd->root);
disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+ disk_super->held_root = cpu_to_le64(pmd->held_root);
disk_super->trans_id = cpu_to_le64(pmd->trans_id);
disk_super->flags = cpu_to_le32(pmd->flags);
@@ -1333,9 +1337,14 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
{
int r, inc;
struct thin_disk_superblock *disk_super;
- struct dm_block *copy, *sblock;
+ struct dm_block *copy;
dm_block_t held_root;
+ if (pmd->held_root) {
+ DMWARN("Pool metadata snapshot already exists: release this before taking another.");
+ return -EBUSY;
+ }
+
/*
* We commit to ensure the btree roots which we increment in a
* moment are up to date.
@@ -1353,22 +1362,16 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION,
&sb_validator, ©, &inc);
- if (r)
+ if (r) {
+ dm_sm_dec_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
return r;
+ }
BUG_ON(!inc);
held_root = dm_block_location(copy);
disk_super = dm_block_data(copy);
- if (le64_to_cpu(disk_super->held_root)) {
- DMWARN("Pool metadata snapshot already exists: release this before taking another.");
-
- dm_tm_dec(pmd->tm, held_root);
- dm_tm_unlock(pmd->tm, copy);
- return -EBUSY;
- }
-
/*
* Wipe the spacemap since we're not publishing this.
*/
@@ -1384,18 +1387,8 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root));
dm_tm_unlock(pmd->tm, copy);
- /*
- * Write the held root into the superblock.
- */
- r = superblock_lock(pmd, &sblock);
- if (r) {
- dm_tm_dec(pmd->tm, held_root);
- return r;
- }
+ pmd->held_root = held_root;
- disk_super = dm_block_data(sblock);
- disk_super->held_root = cpu_to_le64(held_root);
- dm_bm_unlock(sblock);
return 0;
}
@@ -1415,18 +1408,10 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
{
int r;
struct thin_disk_superblock *disk_super;
- struct dm_block *sblock, *copy;
+ struct dm_block *copy;
dm_block_t held_root;
- r = superblock_lock(pmd, &sblock);
- if (r)
- return r;
-
- disk_super = dm_block_data(sblock);
- held_root = le64_to_cpu(disk_super->held_root);
- disk_super->held_root = cpu_to_le64(0);
-
- dm_bm_unlock(sblock);
+ held_root = pmd->held_root;
if (!held_root) {
DMWARN("No pool metadata snapshot found: nothing to release.");
@@ -1437,13 +1422,15 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
if (r)
return r;
+ pmd->held_root = 0;
+
disk_super = dm_block_data(copy);
dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root));
dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root));
- dm_sm_dec_block(pmd->metadata_sm, held_root);
-
dm_tm_unlock(pmd->tm, copy);
+ dm_sm_dec_block(pmd->metadata_sm, held_root);
+
return 0;
}
@@ -1462,19 +1449,7 @@ int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
static int __get_metadata_snap(struct dm_pool_metadata *pmd,
dm_block_t *result)
{
- int r;
- struct thin_disk_superblock *disk_super;
- struct dm_block *sblock;
-
- r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
- &sb_validator, &sblock);
- if (r)
- return r;
-
- disk_super = dm_block_data(sblock);
- *result = le64_to_cpu(disk_super->held_root);
-
- dm_bm_unlock(sblock);
+ *result = pmd->held_root;
return 0;
}
diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c
index 0666699b6858..9a64f575ae5f 100644
--- a/drivers/md/dm-verity-loadpin.c
+++ b/drivers/md/dm-verity-loadpin.c
@@ -70,7 +70,7 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev)
table = dm_get_live_table(md, &srcu_idx);
- if (table->num_targets != 1)
+ if (!table || table->num_targets != 1)
goto out;
ti = dm_table_get_target(table, 0);
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 60e74c0e02f1..6ae5886566d9 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -25,7 +25,7 @@
#define DM_MSG_PREFIX "verity"
-#define DM_VERITY_ENV_LENGTH 42
+#define DM_VERITY_ENV_LENGTH 46
#define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 259cc8a97348..836123f2929a 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1245,6 +1245,12 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
if (!wait_read_barrier(conf, bio->bi_iter.bi_sector,
bio->bi_opf & REQ_NOWAIT)) {
bio_wouldblock_error(bio);
+
+ if (r1bio_existed) {
+ set_bit(R1BIO_Returned, &r1_bio->state);
+ raid_end_bio_io(r1_bio);
+ }
+
return;
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index fd03c01bcf25..ca9ae89c6d1d 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1744,6 +1744,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
r10_bio->mddev = mddev;
r10_bio->state = 0;
r10_bio->sectors = 0;
+ r10_bio->read_slot = -1;
memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
wait_blocked_dev(mddev, r10_bio);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 5c513916cf72..077089e13450 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1214,7 +1214,8 @@ static int mtk_jpeg_release(struct file *file)
struct mtk_jpeg_dev *jpeg = video_drvdata(file);
struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(file->private_data);
- cancel_work_sync(&ctx->jpeg_work);
+ if (jpeg->variant->jpeg_worker)
+ cancel_work_sync(&ctx->jpeg_work);
mutex_lock(&jpeg->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index 81be744e9f1b..a1354d40ad8e 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -474,13 +474,14 @@ static int mxc_isi_probe(struct platform_device *pdev)
return ret;
}
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = mxc_isi_crossbar_init(isi);
- if (ret) {
- dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
- goto err_pm;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to initialize crossbar\n");
for (i = 0; i < isi->pdata->num_channels; ++i) {
ret = mxc_isi_pipe_init(isi, i);
@@ -503,12 +504,11 @@ static int mxc_isi_probe(struct platform_device *pdev)
err_xbar:
mxc_isi_crossbar_cleanup(&isi->crossbar);
-err_pm:
- pm_runtime_disable(isi->dev);
+
return ret;
}
-static int mxc_isi_remove(struct platform_device *pdev)
+static void mxc_isi_remove(struct platform_device *pdev)
{
struct mxc_isi_dev *isi = platform_get_drvdata(pdev);
unsigned int i;
@@ -521,12 +521,8 @@ static int mxc_isi_remove(struct platform_device *pdev)
mxc_isi_pipe_cleanup(pipe);
}
- mxc_isi_crossbar_cleanup(&isi->crossbar);
mxc_isi_v4l2_cleanup(isi);
-
- pm_runtime_disable(isi->dev);
-
- return 0;
+ mxc_isi_crossbar_cleanup(&isi->crossbar);
}
static const struct of_device_id mxc_isi_of_match[] = {
@@ -539,7 +535,7 @@ MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
static struct platform_driver mxc_isi_driver = {
.probe = mxc_isi_probe,
- .remove = mxc_isi_remove,
+ .remove_new = mxc_isi_remove,
.driver = {
.of_match_table = mxc_isi_of_match,
.name = MXC_ISI_DRIVER_NAME,
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 8295542e1a7c..d6984129c2f3 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height)
uv_sclines = ALIGN(((height + 1) >> 1), 16);
y_plane = y_stride * y_sclines;
- uv_plane = uv_stride * uv_sclines + SZ_4K;
- size = y_plane + uv_plane + SZ_8K;
+ uv_plane = uv_stride * uv_sclines;
+ size = y_plane + uv_plane;
return ALIGN(size, SZ_4K);
}
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
index e3f0a90a567b..277322d1fa27 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
@@ -135,8 +135,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_H264,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -172,8 +172,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -194,8 +194,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
.caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
- .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.num_caps = 24,
.pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
.pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
@@ -209,8 +209,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -228,8 +228,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
.caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
.caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
- .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
.num_caps = 23,
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
index 4e8af645f8b9..b5b769d735c1 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
@@ -172,8 +172,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -194,8 +194,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
.caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
- .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.num_caps = 24,
.pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
.pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
@@ -209,8 +209,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -228,8 +228,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
.caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
.caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
- .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
.num_caps = 23,
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index aaa96f256356..bdfb9bc3eb3f 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -379,7 +379,7 @@ static void rga_cmd_set(struct rga_ctx *ctx)
{
struct rockchip_rga *rga = ctx->rga;
- memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4);
+ memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
rga_cmd_set_src_addr(ctx, rga->src_mmu_pages);
/*
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index e8917e5630a4..03990fd30f15 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -6,7 +6,7 @@
#ifndef __RGA_HW_H__
#define __RGA_HW_H__
-#define RGA_CMDBUF_SIZE 0x20
+#define RGA_CMDBUF_SIZE 0x80
/* Hardware limits */
#define MAX_WIDTH 8192
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index ea7d91b19079..abac50d55462 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1122,7 +1122,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream)
* uvc_video_decode_end will never be called with a NULL buffer.
*/
static int uvc_video_decode_start(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const u8 *data, int len)
+ struct uvc_buffer *buf,
+ struct uvc_buffer *meta_buf,
+ const u8 *data, int len)
{
u8 header_len;
u8 fid;
@@ -1141,6 +1143,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
header_len = data[0];
fid = data[1] & UVC_STREAM_FID;
+ /*
+ * Mark the buffer as done if we're at the beginning of a new frame.
+ * End of frame detection is better implemented by checking the EOF
+ * bit (FID bit toggling is delayed by one frame compared to the EOF
+ * bit), but some devices don't set the bit at end of frame (and the
+ * last payload can be lost anyway). We thus must check if the FID has
+ * been toggled.
+ *
+ * stream->last_fid is initialized to -1, and buf->bytesused to 0,
+ * so the first isochronous frame will never trigger an end of frame
+ * detection.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer. This also
+ * avoids detecting end of frame conditions at FID toggling if the
+ * previous payload had the EOF bit set.
+ */
+ if (fid != stream->last_fid && buf && buf->bytesused != 0) {
+ uvc_dbg(stream->dev, FRAME,
+ "Frame complete (FID bit toggled)\n");
+ buf->state = UVC_BUF_STATE_READY;
+
+ return -EAGAIN;
+ }
+
+ /*
+ * Some cameras, when running two parallel streams (one MJPEG alongside
+ * another non-MJPEG stream), are known to lose the EOF packet for a frame.
+ * We can detect the end of a frame by checking for a new SOI marker, as
+ * the SOI always lies on the packet boundary between two frames for
+ * these devices.
+ */
+ if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF &&
+ (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG ||
+ stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) &&
+ buf && buf->bytesused != 0) {
+ const u8 *packet = data + header_len;
+
+ if (len >= header_len + 2 &&
+ packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) {
+ buf->state = UVC_BUF_STATE_READY;
+ buf->error = 1;
+ stream->last_fid ^= UVC_STREAM_FID;
+ return -EAGAIN;
+ }
+ }
+
/*
* Increase the sequence number regardless of any buffer states, so
* that discontinuous sequence numbers always indicate lost frames.
@@ -1149,6 +1198,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
stream->sequence++;
if (stream->sequence)
uvc_video_stats_update(stream);
+
+ /*
+ * On a FID flip initialize sequence number and timestamp.
+ *
+ * The driver already takes care of injecting FID flips for
+ * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF.
+ */
+ if (buf) {
+ buf->buf.field = V4L2_FIELD_NONE;
+ buf->buf.sequence = stream->sequence;
+ buf->buf.vb2_buf.timestamp =
+ ktime_to_ns(uvc_video_get_time());
+ }
}
uvc_video_clock_decode(stream, buf, data, len);
@@ -1189,57 +1251,10 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
return -ENODATA;
}
- buf->buf.field = V4L2_FIELD_NONE;
- buf->buf.sequence = stream->sequence;
- buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time());
-
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
- }
-
- /*
- * Mark the buffer as done if we're at the beginning of a new frame.
- * End of frame detection is better implemented by checking the EOF
- * bit (FID bit toggling is delayed by one frame compared to the EOF
- * bit), but some devices don't set the bit at end of frame (and the
- * last payload can be lost anyway). We thus must check if the FID has
- * been toggled.
- *
- * stream->last_fid is initialized to -1, so the first isochronous
- * frame will never trigger an end of frame detection.
- *
- * Empty buffers (bytesused == 0) don't trigger end of frame detection
- * as it doesn't make sense to return an empty buffer. This also
- * avoids detecting end of frame conditions at FID toggling if the
- * previous payload had the EOF bit set.
- */
- if (fid != stream->last_fid && buf->bytesused != 0) {
- uvc_dbg(stream->dev, FRAME,
- "Frame complete (FID bit toggled)\n");
- buf->state = UVC_BUF_STATE_READY;
- return -EAGAIN;
- }
-
- /*
- * Some cameras, when running two parallel streams (one MJPEG alongside
- * another non-MJPEG stream), are known to lose the EOF packet for a frame.
- * We can detect the end of a frame by checking for a new SOI marker, as
- * the SOI always lies on the packet boundary between two frames for
- * these devices.
- */
- if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF &&
- (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG ||
- stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) {
- const u8 *packet = data + header_len;
-
- if (len >= header_len + 2 &&
- packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI &&
- buf->bytesused != 0) {
- buf->state = UVC_BUF_STATE_READY;
- buf->error = 1;
- stream->last_fid ^= UVC_STREAM_FID;
- return -EAGAIN;
- }
+ if (meta_buf)
+ meta_buf->state = UVC_BUF_STATE_ACTIVE;
}
stream->last_fid = fid;
@@ -1411,7 +1426,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
ktime_t time;
const u8 *scr;
- if (!meta_buf || length == 2)
+ if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE)
return;
has_pts = mem[1] & UVC_STREAM_PTS;
@@ -1528,7 +1543,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
/* Decode the payload header. */
mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
do {
- ret = uvc_video_decode_start(stream, buf, mem,
+ ret = uvc_video_decode_start(stream, buf, meta_buf, mem,
urb->iso_frame_desc[i].actual_length);
if (ret == -EAGAIN)
uvc_video_next_buffers(stream, &buf, &meta_buf);
@@ -1577,7 +1592,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
*/
if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
do {
- ret = uvc_video_decode_start(stream, buf, mem, len);
+ ret = uvc_video_decode_start(stream, buf, meta_buf, mem,
+ len);
if (ret == -EAGAIN)
uvc_video_next_buffers(stream, &buf, &meta_buf);
} while (ret == -EAGAIN);
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index b37507e09339..da6dc7e737ef 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -262,6 +262,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_VYUY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
.block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
{ .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c
index 04115cd92433..8c841a9e382b 100644
--- a/drivers/memstick/core/ms_block.c
+++ b/drivers/memstick/core/ms_block.c
@@ -1339,6 +1339,10 @@ static int msb_ftl_initialize(struct msb_data *msb)
return 0;
msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE;
+ if (msb->block_count > MS_MAX_ZONES * MS_BLOCKS_IN_ZONE) {
+ pr_err("Too many blocks: %d\n", msb->block_count);
+ return -EINVAL;
+ }
msb->logical_block_count = msb->zone_count * 496 - 2;
msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c
index 79d393b602bf..1aa0e1241a66 100644
--- a/drivers/mfd/cros_ec_dev.c
+++ b/drivers/mfd/cros_ec_dev.c
@@ -145,7 +145,6 @@ static int ec_device_probe(struct platform_device *pdev)
if (!ec)
return retval;
- dev_set_drvdata(dev, ec);
ec->ec_dev = dev_get_drvdata(dev->parent);
ec->dev = dev;
ec->cmd_offset = ec_platform->cmd_offset;
@@ -187,6 +186,8 @@ static int ec_device_probe(struct platform_device *pdev)
if (retval)
goto failed;
+ dev_set_drvdata(dev, ec);
+
/* check whether this EC is a sensor hub. */
if (cros_ec_get_sensor_count(ec) > 0) {
retval = mfd_add_hotplug_devices(ec->dev,
diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c
index 1cea3f8f467d..c1f7b2cdb45b 100644
--- a/drivers/mfd/cs42l43.c
+++ b/drivers/mfd/cs42l43.c
@@ -666,7 +666,7 @@ static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *con
unsigned int loadaddr, val;
int ret;
- if (!firmware) {
+ if (!firmware || firmware->size < sizeof(*hdr)) {
dev_err(cs42l43->dev, "Failed to load firmware\n");
cs42l43->firmware_error = -ENODEV;
goto err;
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index 509dcb226cbf..1865621d62f2 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -747,9 +747,11 @@ static int sm501_register_device(struct sm501_devdata *sm,
if (ret >= 0) {
dev_dbg(sm->dev, "registered %s\n", pdev->name);
list_add_tail(&smdev->list, &sm->devices);
- } else
+ } else {
dev_err(sm->dev, "error registering %s (%d)\n",
pdev->name, ret);
+ platform_device_put(pdev);
+ }
return ret;
}
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 55675ceedcd3..08836772b820 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -395,7 +395,7 @@ static int tps6586x_add_subdevs(struct tps6586x *tps6586x,
pdev->dev.parent = tps6586x->dev;
pdev->dev.platform_data = subdev->platform_data;
- pdev->dev.of_node = subdev->of_node;
+ pdev->dev.of_node = of_node_get(subdev->of_node);
ret = platform_device_add(pdev);
if (ret) {
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 4e0a645b45b9..b420e4d82cf4 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -1585,7 +1585,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300,
return linear_length;
}
-static void __vub300_command_response(struct vub300_mmc_host *vub300,
+static bool __vub300_command_response(struct vub300_mmc_host *vub300,
struct mmc_command *cmd,
struct mmc_data *data, int data_length)
{
@@ -1597,17 +1597,11 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
msecs_to_jiffies(msec_timeout));
if (respretval == 0) { /* TIMED OUT */
/* we don't know which of "out" and "res" if any failed */
- int result;
vub300->usb_timed_out = 1;
usb_kill_urb(vub300->command_out_urb);
usb_kill_urb(vub300->command_res_urb);
cmd->error = -ETIMEDOUT;
- result = usb_lock_device_for_reset(vub300->udev,
- vub300->interface);
- if (result == 0) {
- result = usb_reset_device(vub300->udev);
- usb_unlock_device(vub300->udev);
- }
+ return true;
} else if (respretval < 0) {
/* we don't know which of "out" and "res" if any failed */
usb_kill_urb(vub300->command_out_urb);
@@ -1703,6 +1697,8 @@ static void __vub300_command_response(struct vub300_mmc_host *vub300,
} else {
cmd->error = -EINVAL;
}
+
+ return false;
}
static void construct_request_response(struct vub300_mmc_host *vub300,
@@ -1748,6 +1744,7 @@ static void vub300_cmndwork_thread(struct work_struct *work)
struct mmc_request *req = vub300->req;
struct mmc_command *cmd = vub300->cmd;
struct mmc_data *data = vub300->data;
+ bool reset_device;
int data_length;
mutex_lock(&vub300->cmd_mutex);
init_completion(&vub300->command_complete);
@@ -1770,7 +1767,8 @@ static void vub300_cmndwork_thread(struct work_struct *work)
data_length = __command_read_data(vub300, cmd, data);
else
data_length = __command_write_data(vub300, cmd, data);
- __vub300_command_response(vub300, cmd, data, data_length);
+ reset_device = __vub300_command_response(vub300, cmd,
+ data, data_length);
vub300->req = NULL;
vub300->cmd = NULL;
vub300->data = NULL;
@@ -1778,6 +1776,16 @@ static void vub300_cmndwork_thread(struct work_struct *work)
if (cmd->error == -ENOMEDIUM)
check_vub300_port_status(vub300);
mutex_unlock(&vub300->cmd_mutex);
+ if (reset_device) {
+ int result;
+
+ result = usb_lock_device_for_reset(vub300->udev,
+ vub300->interface);
+ if (result == 0) {
+ result = usb_reset_device(vub300->udev);
+ usb_unlock_device(vub300->udev);
+ }
+ }
mmc_request_done(vub300->mmc, req);
kref_put(&vub300->kref, vub300_delete);
return;
diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c
index d533475fda15..f429b536d468 100644
--- a/drivers/mtd/devices/mchp23k256.c
+++ b/drivers/mtd/devices/mchp23k256.c
@@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi)
data = dev_get_platdata(&spi->dev);
- flash->caps = of_device_get_match_data(&spi->dev);
+ flash->caps = spi_get_device_match_data(spi);
if (!flash->caps)
flash->caps = &mchp23k256_caps;
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index 8297b366a066..a476e53562a4 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -129,6 +129,7 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
static int register_device(char *name, unsigned long start, unsigned long length)
{
slram_mtd_list_t **curmtd;
+ int ret = -ENOMEM;
curmtd = &slram_mtdlist;
while (*curmtd) {
@@ -155,14 +156,15 @@ static int register_device(char *name, unsigned long start, unsigned long length
if (!(*curmtd)->mtdinfo) {
E("slram: Cannot allocate new MTD device.\n");
- return(-ENOMEM);
+ goto err_free_list;
}
if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start =
memremap(start, length,
MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC))) {
E("slram: memremap failed\n");
- return -EIO;
+ ret = -EIO;
+ goto err_free_priv;
}
((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end =
((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length;
@@ -183,10 +185,8 @@ static int register_device(char *name, unsigned long start, unsigned long length
if (mtd_device_register((*curmtd)->mtdinfo, NULL, 0)) {
E("slram: Failed to register new device\n");
- memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);
- kfree((*curmtd)->mtdinfo->priv);
- kfree((*curmtd)->mtdinfo);
- return(-EAGAIN);
+ ret = -EAGAIN;
+ goto err_unmap;
}
T("slram: Registered device %s from %luKiB to %luKiB\n", name,
(start / 1024), ((start + length) / 1024));
@@ -194,6 +194,16 @@ static int register_device(char *name, unsigned long start, unsigned long length
((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start,
((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end);
return(0);
+
+err_unmap:
+ memunmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start);
+err_free_priv:
+ kfree((*curmtd)->mtdinfo->priv);
+err_free_list:
+ kfree((*curmtd)->mtdinfo);
+ kfree(*curmtd);
+ *curmtd = NULL;
+ return ret;
}
static void unregister_devices(void)
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c
index 53019d313db7..3667faefe6c5 100644
--- a/drivers/mtd/maps/vmu-flash.c
+++ b/drivers/mtd/maps/vmu-flash.c
@@ -547,6 +547,7 @@ static void vmu_queryblocks(struct mapleq *mq)
mpart->partition = card->partition;
mtd_cur->priv = mpart;
mtd_cur->owner = THIS_MODULE;
+ mtd_cur->dev.parent = &mdev->dev;
pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
if (!pcache)
diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c
index 0e21d443078e..5a7b8508e066 100644
--- a/drivers/mtd/nand/onenand/onenand_samsung.c
+++ b/drivers/mtd/nand/onenand/onenand_samsung.c
@@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
time_before(jiffies, timeout));
+ if (!(status & S5PC110_DMA_TRANS_STATUS_TD))
+ return -ETIMEDOUT;
+
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
return 0;
@@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
- wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
+ if (!wait_for_completion_timeout(&onenand->complete,
+ msecs_to_jiffies(20)))
+ return -ETIMEDOUT;
return 0;
}
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index f0e2318ce088..d3c58c3aab1a 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -683,8 +683,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf,
return check_erased_page(chip, buf);
}
- if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
+ if (!ctrl->nand_stat) {
mtd->ecc_stats.failed++;
+ return -ETIMEDOUT;
+ }
+
+ if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) {
+ mtd->ecc_stats.failed++;
+ return -EIO;
+ }
return nctrl->max_bitflips;
}
diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index 677fcb03f9be..064ffc13fb27 100644
--- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
@@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ unsigned long time_left;
int res;
sg_init_one(&host->sgl, mem, len);
@@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
flags);
if (!desc) {
dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
+ res = -ENXIO;
goto out1;
}
@@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
- wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
+ time_left = wait_for_completion_timeout(&host->comp_dma,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dmaengine_terminate_sync(host->dma_chan);
+ res = -ETIMEDOUT;
+ goto out1;
+ }
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
@@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
- return -ENXIO;
+ return res;
}
static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 8abad092b058..e6e6417fb41f 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
+ unsigned long time_left;
host->dma_slave_config.direction = dir;
host->dma_slave_config.src_addr = dma;
@@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
- wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000));
+ time_left = wait_for_completion_timeout(&host->comp,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dmaengine_terminate_sync(host->dma_chan);
+ res = -ETIMEDOUT;
+ } else {
+ res = 0;
+ }
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
- return 0;
+ return res;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 896a7d819e3c..e9e4ea87116c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -175,7 +175,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs)
* cs should always lie between 0 and nanddev_ntargets(), when that's
* not the case it's a bug and the caller should be fixed.
*/
- if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
+ if (WARN_ON(cs >= nanddev_ntargets(&chip->base)))
return;
chip->cur_cs = cs;
diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c
index 88a2d7a639d0..198bffa25db8 100644
--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
+++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
@@ -1153,7 +1153,7 @@ static int pl35x_nand_probe(struct platform_device *pdev)
nfc->controller.ops = &pl35x_nandc_ops;
INIT_LIST_HEAD(&nfc->chips);
- nfc->conf_regs = devm_ioremap_resource(&smc_amba->dev, &smc_amba->res);
+ nfc->conf_regs = devm_ioremap_resource(nfc->dev, &smc_amba->res);
if (IS_ERR(nfc->conf_regs))
return PTR_ERR(nfc->conf_regs);
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 24cd25de2b8b..fd05a24d64a9 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig MTD_SPI_NOR
tristate "SPI NOR device support"
- depends on MTD
depends on MTD && SPI_MASTER
select SPI_MEM
help
diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c
index 4f3428360358..b52dbfad2ce6 100644
--- a/drivers/mtd/spi-nor/swp.c
+++ b/drivers/mtd/spi-nor/swp.c
@@ -277,8 +277,15 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
/* Prefer top, if both are valid */
use_top = can_be_top;
- /* lock_len: length of region that should remain locked */
- if (use_top)
+ /*
+ * lock_len: length of region that should remain locked.
+ *
+ * When can_be_top and can_be_bottom booleans are true, both adjacent
+ * regions are unlocked, thus the entire flash can be unlocked.
+ */
+ if (can_be_top && can_be_bottom)
+ lock_len = 0;
+ else if (use_top)
lock_len = nor->params->size - (ofs + len);
else
lock_len = ofs;
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index ddd087c2c3ed..f2da0c49171f 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -3303,9 +3303,9 @@ static size_t amt_get_size(const struct net_device *dev)
nla_total_size(sizeof(__u16)) + /* IFLA_AMT_GATEWAY_PORT */
nla_total_size(sizeof(__u32)) + /* IFLA_AMT_LINK */
nla_total_size(sizeof(__u32)) + /* IFLA_MAX_TUNNELS */
- nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_DISCOVERY_IP */
- nla_total_size(sizeof(struct iphdr)) + /* IFLA_AMT_REMOTE_IP */
- nla_total_size(sizeof(struct iphdr)); /* IFLA_AMT_LOCAL_IP */
+ nla_total_size(sizeof(__be32)) + /* IFLA_AMT_DISCOVERY_IP */
+ nla_total_size(sizeof(__be32)) + /* IFLA_AMT_REMOTE_IP */
+ nla_total_size(sizeof(__be32)); /* IFLA_AMT_LOCAL_IP */
}
static int amt_fill_info(struct sk_buff *skb, const struct net_device *dev)
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index d1da96f3efd4..447a9e832d85 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1284,6 +1284,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
fallthrough;
case AD_RX_PORT_DISABLED:
port->sm_vars &= ~AD_PORT_MATCHED;
+ port->partner_oper.port_state &= ~LACP_STATE_SYNCHRONIZATION;
break;
case AD_RX_LACP_DISABLED:
port->sm_vars &= ~AD_PORT_SELECTED;
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e57e1296da37..6b558aa98c6d 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1456,9 +1456,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
struct slave *slave;
mask = features;
-
- features &= ~NETIF_F_ONE_FOR_ALL;
- features |= NETIF_F_ALL_FOR_ALL;
+ features = netdev_base_features(features);
bond_for_each_slave(bond, slave, iter) {
features = netdev_increment_features(features,
@@ -1470,87 +1468,52 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
return features;
}
-#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_GSO_ENCAP_ALL | \
- NETIF_F_HIGHDMA | NETIF_F_LRO)
-
-#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
-
-#define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_GSO_SOFTWARE)
-
-
-static void bond_compute_features(struct bonding *bond)
+static int bond_header_create(struct sk_buff *skb, struct net_device *bond_dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
{
- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM;
- netdev_features_t vlan_features = BOND_VLAN_FEATURES;
- netdev_features_t enc_features = BOND_ENC_FEATURES;
-#ifdef CONFIG_XFRM_OFFLOAD
- netdev_features_t xfrm_features = BOND_XFRM_FEATURES;
-#endif /* CONFIG_XFRM_OFFLOAD */
- netdev_features_t mpls_features = BOND_MPLS_FEATURES;
- struct net_device *bond_dev = bond->dev;
- struct list_head *iter;
+ struct bonding *bond = netdev_priv(bond_dev);
+ const struct header_ops *slave_ops;
struct slave *slave;
- unsigned short max_hard_header_len = ETH_HLEN;
- unsigned int tso_max_size = TSO_MAX_SIZE;
- u16 tso_max_segs = TSO_MAX_SEGS;
-
- if (!bond_has_slaves(bond))
- goto done;
- vlan_features &= NETIF_F_ALL_FOR_ALL;
- mpls_features &= NETIF_F_ALL_FOR_ALL;
-
- bond_for_each_slave(bond, slave, iter) {
- vlan_features = netdev_increment_features(vlan_features,
- slave->dev->vlan_features, BOND_VLAN_FEATURES);
-
- enc_features = netdev_increment_features(enc_features,
- slave->dev->hw_enc_features,
- BOND_ENC_FEATURES);
-
-#ifdef CONFIG_XFRM_OFFLOAD
- xfrm_features = netdev_increment_features(xfrm_features,
- slave->dev->hw_enc_features,
- BOND_XFRM_FEATURES);
-#endif /* CONFIG_XFRM_OFFLOAD */
-
- mpls_features = netdev_increment_features(mpls_features,
- slave->dev->mpls_features,
- BOND_MPLS_FEATURES);
-
- dst_release_flag &= slave->dev->priv_flags;
- if (slave->dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = slave->dev->hard_header_len;
+ int ret = 0;
- tso_max_size = min(tso_max_size, slave->dev->tso_max_size);
- tso_max_segs = min(tso_max_segs, slave->dev->tso_max_segs);
+ rcu_read_lock();
+ slave = rcu_dereference(bond->curr_active_slave);
+ if (slave) {
+ slave_ops = READ_ONCE(slave->dev->header_ops);
+ if (slave_ops && slave_ops->create)
+ ret = slave_ops->create(skb, slave->dev,
+ type, daddr, saddr, len);
}
- bond_dev->hard_header_len = max_hard_header_len;
-
-done:
- bond_dev->vlan_features = vlan_features;
- bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
- NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_STAG_TX;
-#ifdef CONFIG_XFRM_OFFLOAD
- bond_dev->hw_enc_features |= xfrm_features;
-#endif /* CONFIG_XFRM_OFFLOAD */
- bond_dev->mpls_features = mpls_features;
- netif_set_tso_max_segs(bond_dev, tso_max_segs);
- netif_set_tso_max_size(bond_dev, tso_max_size);
+ rcu_read_unlock();
+ return ret;
+}
- bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
- if ((bond_dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) &&
- dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
- bond_dev->priv_flags |= IFF_XMIT_DST_RELEASE;
+static int bond_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
+{
+ struct bonding *bond = netdev_priv(dev);
+ const struct header_ops *slave_ops;
+ struct slave *slave;
+ int ret = 0;
- netdev_change_features(bond_dev);
+ rcu_read_lock();
+ slave = rcu_dereference(bond->curr_active_slave);
+ if (slave) {
+ slave_ops = READ_ONCE(slave->dev->header_ops);
+ if (slave_ops && slave_ops->parse)
+ ret = slave_ops->parse(skb, slave->dev, haddr);
+ }
+ rcu_read_unlock();
+ return ret;
}
+static const struct header_ops bond_header_ops = {
+ .create = bond_header_create,
+ .parse = bond_header_parse,
+};
+
static void bond_setup_by_slave(struct net_device *bond_dev,
struct net_device *slave_dev)
{
@@ -1558,7 +1521,8 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
dev_close(bond_dev);
- bond_dev->header_ops = slave_dev->header_ops;
+ bond_dev->header_ops = slave_dev->header_ops ?
+ &bond_header_ops : NULL;
bond_dev->type = slave_dev->type;
bond_dev->hard_header_len = slave_dev->hard_header_len;
@@ -2303,7 +2267,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
}
bond->slave_cnt++;
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
bond_set_carrier(bond);
/* Needs to be called before bond_select_active_slave(), which will
@@ -2555,7 +2519,7 @@ static int __bond_release_one(struct net_device *bond_dev,
call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
}
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
(old_features & NETIF_F_VLAN_CHALLENGED))
slave_info(bond_dev, slave_dev, "last VLAN challenged slave left bond - VLAN blocking is removed\n");
@@ -4057,7 +4021,7 @@ static int bond_slave_netdev_event(unsigned long event,
case NETDEV_FEAT_CHANGE:
if (!bond->notifier_ctx) {
bond->notifier_ctx = true;
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
bond->notifier_ctx = false;
}
break;
@@ -4343,7 +4307,7 @@ void bond_work_init_all(struct bonding *bond)
INIT_DELAYED_WORK(&bond->slave_arr_work, bond_slave_arr_handler);
}
-static void bond_work_cancel_all(struct bonding *bond)
+void bond_work_cancel_all(struct bonding *bond)
{
cancel_delayed_work_sync(&bond->mii_work);
cancel_delayed_work_sync(&bond->arp_work);
@@ -6073,7 +6037,7 @@ void bond_setup(struct net_device *bond_dev)
/* Don't allow bond devices to change network namespaces. */
bond_dev->features |= NETIF_F_NETNS_LOCAL;
- bond_dev->hw_features = BOND_VLAN_FEATURES |
+ bond_dev->hw_features = MASTER_UPPER_DEV_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_VLAN_STAG_RX |
@@ -6082,6 +6046,7 @@ void bond_setup(struct net_device *bond_dev)
bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
bond_dev->features |= bond_dev->hw_features;
bond_dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
+ bond_dev->features |= NETIF_F_GSO_PARTIAL;
#ifdef CONFIG_XFRM_OFFLOAD
bond_dev->hw_features |= BOND_XFRM_FEATURES;
/* Only enable XFRM features if this is an active-backup config */
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 0eaf4b0e06ff..f771d569524f 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -596,18 +596,20 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
+ struct bonding *bond = netdev_priv(bond_dev);
int err;
- err = bond_changelink(bond_dev, tb, data, extack);
- if (err < 0)
+ err = register_netdevice(bond_dev);
+ if (err)
return err;
- err = register_netdevice(bond_dev);
- if (!err) {
- struct bonding *bond = netdev_priv(bond_dev);
+ netif_carrier_off(bond_dev);
+ bond_work_init_all(bond);
- netif_carrier_off(bond_dev);
- bond_work_init_all(bond);
+ err = bond_changelink(bond_dev, tb, data, extack);
+ if (err) {
+ bond_work_cancel_all(bond);
+ unregister_netdevice(bond_dev);
}
return err;
diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index fdf2136824b3..75494260dd01 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -1365,10 +1365,13 @@ static void esd_usb_disconnect(struct usb_interface *intf)
if (dev->nets[i]) {
netdev = dev->nets[i]->netdev;
unregister_netdev(netdev);
- free_candev(netdev);
}
}
unlink_all_urbs(dev);
+ for (i = 0; i < dev->net_count; i++) {
+ if (dev->nets[i])
+ free_candev(dev->nets[i]->netdev);
+ }
kfree(dev);
}
}
diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c
index a7d41e781398..afb11690c217 100644
--- a/drivers/net/dsa/sja1105/sja1105_ptp.c
+++ b/drivers/net/dsa/sja1105/sja1105_ptp.c
@@ -755,7 +755,7 @@ static int sja1105_per_out_enable(struct sja1105_private *priv,
* 2 edges on PTP_CLK. So check for truncation which happens
* at periods larger than around 68.7 seconds.
*/
- pin_duration = ns_to_sja1105_ticks(pin_duration / 2);
+ pin_duration = max_t(u64, ns_to_sja1105_ticks(pin_duration / 2), 1);
if (pin_duration > U32_MAX) {
rc = -ERANGE;
goto out;
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index dd9c50d3ec0f..4d7446bb66b7 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -749,6 +749,18 @@ static void ena_destroy_all_tx_queues(struct ena_adapter *adapter)
}
}
+static void ena_destroy_xdp_tx_queues(struct ena_adapter *adapter)
+{
+ u16 ena_qid;
+ int i;
+
+ for (i = adapter->xdp_first_ring;
+ i < adapter->xdp_first_ring + adapter->xdp_num_queues; i++) {
+ ena_qid = ENA_IO_TXQ_IDX(i);
+ ena_com_destroy_io_queue(adapter->ena_dev, ena_qid);
+ }
+}
+
static void ena_destroy_all_rx_queues(struct ena_adapter *adapter)
{
u16 ena_qid;
@@ -2035,14 +2047,21 @@ static int create_queues_with_size_backoff(struct ena_adapter *adapter)
rc = ena_setup_tx_resources_in_range(adapter,
0,
adapter->num_io_queues);
- if (rc)
+ if (rc) {
+ ena_destroy_xdp_tx_queues(adapter);
+ ena_free_all_io_tx_resources_in_range(adapter,
+ adapter->xdp_first_ring,
+ adapter->xdp_num_queues);
goto err_setup_tx;
+ }
rc = ena_create_io_tx_queues_in_range(adapter,
0,
adapter->num_io_queues);
- if (rc)
+ if (rc) {
+ ena_destroy_xdp_tx_queues(adapter);
goto err_create_tx_queues;
+ }
rc = ena_setup_all_rx_resources(adapter);
if (rc)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index 528441b28c4e..7c3f62783de3 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -4752,6 +4752,7 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp)
fp = kcalloc(bp->fp_array_size, sizeof(*fp), GFP_KERNEL);
if (!fp)
goto alloc_err;
+ bp->fp = fp;
for (i = 0; i < bp->fp_array_size; i++) {
fp[i].tpa_info =
kcalloc(ETH_MAX_AGGREGATION_QUEUES_E1H_E2,
@@ -4760,8 +4761,6 @@ int bnx2x_alloc_mem_bp(struct bnx2x *bp)
goto alloc_err;
}
- bp->fp = fp;
-
/* allocate sp objs */
bp->sp_objs = kcalloc(bp->fp_array_size, sizeof(struct bnx2x_sp_objs),
GFP_KERNEL);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index d8e4456d111e..482f56aed980 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -41,9 +41,8 @@
#include "bcmgenet.h"
-/* Default highest priority queue for multi queue support */
-#define GENET_Q1_PRIORITY 0
-#define GENET_Q0_PRIORITY 1
+#define GENET_Q0_WEIGHT 1
+#define GENET_Q1_WEIGHT 4
#define GENET_Q0_RX_BD_CNT \
(TOTAL_DESC - priv->hw_params->rx_queues * priv->hw_params->rx_bds_per_q)
@@ -2086,13 +2085,6 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev)
int i;
index = skb_get_queue_mapping(skb);
- /* Mapping strategy:
- * queue_mapping = 0, unclassified, packet xmited through ring 0
- * queue_mapping = 1, goes to ring 1. (highest priority queue)
- * queue_mapping = 2, goes to ring 2.
- * queue_mapping = 3, goes to ring 3.
- * queue_mapping = 4, goes to ring 4.
- */
ring = &priv->tx_rings[index];
txq = netdev_get_tx_queue(dev, index);
@@ -2792,8 +2784,9 @@ static void bcmgenet_fini_tx_napi(struct bcmgenet_priv *priv)
/* Initialize Tx queues
*
- * Queues 1-4 are priority-based, each one has 32 descriptors,
- * with queue 1 being the highest priority queue.
+ * Queues 1-4 are the priority queues, each one has 32 descriptors.
+ * The weighted round-robin arbiter gives them a larger share of TX
+ * bandwidth than the default queue 0.
*
* Queue 0 is the default Tx queue with
* GENET_Q0_TX_BD_CNT = 256 - 4 * 32 = 128 descriptors.
@@ -2821,8 +2814,8 @@ static void bcmgenet_init_tx_queues(struct net_device *dev)
dma_ctrl = 0;
ring_cfg = 0;
- /* Enable strict priority arbiter mode */
- bcmgenet_tdma_writel(priv, DMA_ARBITER_SP, DMA_ARB_CTRL);
+ /* Enable Weighted Round-Robin arbiter mode */
+ bcmgenet_tdma_writel(priv, DMA_ARBITER_WRR, DMA_ARB_CTRL);
/* Initialize Tx priority queues */
for (i = 0; i <= priv->hw_params->tx_queues; i++) {
@@ -2832,7 +2825,7 @@ static void bcmgenet_init_tx_queues(struct net_device *dev)
ring_cfg |= (1 << i);
dma_ctrl |= (1 << (i + DMA_RING_BUF_EN_SHIFT));
dma_priority[DMA_PRIO_REG_INDEX(i)] |=
- (i ? GENET_Q1_PRIORITY : GENET_Q0_PRIORITY)
+ (i ? GENET_Q1_WEIGHT : GENET_Q0_WEIGHT)
<< DMA_PRIO_REG_SHIFT(i);
}
diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
index 068ed52b66c9..51110ba4fa10 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
@@ -1332,18 +1332,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct)
if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) {
dev_err(&oct->pci_dev->dev, "%s CN23XX BAR1 map failed\n",
__func__);
- octeon_unmap_pci_barx(oct, 0);
- return 1;
+ goto err_unmap_bar0;
}
if (cn23xx_get_pf_num(oct) != 0)
- return 1;
+ goto err_unmap_bar1;
- if (cn23xx_sriov_config(oct)) {
- octeon_unmap_pci_barx(oct, 0);
- octeon_unmap_pci_barx(oct, 1);
- return 1;
- }
+ if (cn23xx_sriov_config(oct))
+ goto err_unmap_bar1;
octeon_write_csr64(oct, CN23XX_SLI_MAC_CREDIT_CNT, 0x3F802080802080ULL);
@@ -1374,6 +1370,12 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct)
oct->coproc_clock_rate = 1000000ULL * cn23xx_coprocessor_clock(oct);
return 0;
+
+err_unmap_bar1:
+ octeon_unmap_pci_barx(oct, 1);
+err_unmap_bar0:
+ octeon_unmap_pci_barx(oct, 0);
+ return 1;
}
EXPORT_SYMBOL_GPL(setup_cn23xx_octeon_pf_device);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 175bf9b13058..1b8247bd9e9b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -6737,14 +6737,6 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state)
return;
}
- if (is_t4(adapter->params.chip)) {
- sge_idma_decode = (const char **)t4_decode;
- sge_idma_decode_nstates = ARRAY_SIZE(t4_decode);
- } else {
- sge_idma_decode = (const char **)t5_decode;
- sge_idma_decode_nstates = ARRAY_SIZE(t5_decode);
- }
-
if (state < sge_idma_decode_nstates)
CH_WARN(adapter, "idma state %s\n", sge_idma_decode[state]);
else
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index 176f7072338b..0f8b46dccc09 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -2180,7 +2180,7 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Cannot join a bridge while VLAN uppers are present");
- return 0;
+ return err;
}
return 0;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index e3ab6f4f8dbb..3ee6995bc7d2 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -1425,6 +1425,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
{
struct enetc_tx_swbd xdp_redirect_arr[ENETC_MAX_SKB_FRAGS] = {0};
struct enetc_ndev_priv *priv = netdev_priv(ndev);
+ struct skb_shared_info *shinfo;
struct enetc_bdr *tx_ring;
int xdp_tx_bd_cnt, i, k;
int xdp_tx_frm_cnt = 0;
@@ -1440,6 +1441,12 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use));
for (k = 0; k < num_frames; k++) {
+ if (xdp_frame_has_frags(frames[k])) {
+ shinfo = xdp_get_shared_info_from_frame(frames[k]);
+ if (unlikely((shinfo->nr_frags + 1) > ENETC_MAX_SKB_FRAGS))
+ break;
+ }
+
xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring,
xdp_redirect_arr,
frames[k]);
diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c
index fb416d60dcd7..bfc784b7f130 100644
--- a/drivers/net/ethernet/freescale/fman/fman.c
+++ b/drivers/net/ethernet/freescale/fman/fman.c
@@ -1995,8 +1995,10 @@ static int fman_init(struct fman *fman)
/* Init KeyGen */
fman->keygen = keygen_init(fman->kg_regs);
- if (!fman->keygen)
+ if (!fman->keygen) {
+ free_init_resources(fman);
return -EINVAL;
+ }
err = enable(fman, cfg);
if (err != 0)
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index e3dfbd7a4236..4d7fdfe55224 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -470,10 +470,13 @@ static void free_gfar_dev(struct gfar_private *priv)
{
int i, j;
- for (i = 0; i < priv->num_grps; i++)
+ for (i = 0; i < MAXGROUPS; i++)
for (j = 0; j < GFAR_NUM_IRQS; j++) {
- kfree(priv->gfargrp[i].irqinfo[j]);
- priv->gfargrp[i].irqinfo[j] = NULL;
+ if (priv->gfargrp[i].irqinfo[j]) {
+ irq_dispose_mapping(priv->gfargrp[i].irqinfo[j]->irq);
+ kfree(priv->gfargrp[i].irqinfo[j]);
+ priv->gfargrp[i].irqinfo[j] = NULL;
+ }
}
free_netdev(priv->ndev);
@@ -630,7 +633,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
{
const char *model;
- int err = 0, i;
+ int err = 0, i, j;
phy_interface_t interface;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
@@ -714,8 +717,11 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
priv->rx_list.count = 0;
mutex_init(&priv->rx_queue_access);
- for (i = 0; i < MAXGROUPS; i++)
+ for (i = 0; i < MAXGROUPS; i++) {
priv->gfargrp[i].regs = NULL;
+ for (j = 0; j < GFAR_NUM_IRQS; j++)
+ priv->gfargrp[i].irqinfo[j] = NULL;
+ }
/* Parse and initialize group specific information */
if (priv->mode == MQ_MG_MODE) {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debug.h b/drivers/net/ethernet/intel/i40e/i40e_debug.h
index 27ebc72d8bfe..8e650ae035d1 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debug.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_debug.h
@@ -41,7 +41,7 @@ struct device *i40e_hw_to_dev(struct i40e_hw *hw);
#define i40e_debug(h, m, s, ...) \
do { \
if (((m) & (h)->debug_mask)) \
- dev_info(i40e_hw_to_dev(hw), s, ##__VA_ARGS__); \
+ dev_info(i40e_hw_to_dev(h), s, ##__VA_ARGS__); \
} while (0)
#endif /* _I40E_DEBUG_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index eaf1ed6c9df4..fbc448fa6e9d 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -3454,7 +3454,6 @@ ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool ena_auto_link_update)
if (!pi || !aq_failures)
return -EINVAL;
- *aq_failures = 0;
hw = pi->hw;
pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index c254484e9b6b..988788d21dd5 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3015,7 +3015,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
struct ice_vsi *vsi = np->vsi;
struct ice_hw *hw = &pf->hw;
struct ice_port_info *pi;
- u8 aq_failures;
+ u8 aq_failures = 0;
bool link_up;
u32 is_an;
int err;
@@ -3086,18 +3086,22 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
/* Set the FC mode and only restart AN if link is up */
err = ice_set_fc(pi, &aq_failures, link_up);
- if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) {
+ switch (aq_failures) {
+ case ICE_SET_FC_AQ_FAIL_GET:
netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %s\n",
err, ice_aq_str(hw->adminq.sq_last_status));
err = -EAGAIN;
- } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) {
+ break;
+ case ICE_SET_FC_AQ_FAIL_SET:
netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %s\n",
err, ice_aq_str(hw->adminq.sq_last_status));
err = -EAGAIN;
- } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) {
+ break;
+ case ICE_SET_FC_AQ_FAIL_UPDATE:
netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %s\n",
err, ice_aq_str(hw->adminq.sq_last_status));
err = -EAGAIN;
+ break;
}
return err;
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 957b6f1b61d6..6e0e64cc58e1 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4763,16 +4763,14 @@ static void ice_init_wakeup(struct ice_pf *pf)
device_set_wakeup_enable(ice_pf_to_dev(pf), false);
}
-static int ice_init_link(struct ice_pf *pf)
+static void ice_init_link(struct ice_pf *pf)
{
struct device *dev = ice_pf_to_dev(pf);
int err;
err = ice_init_link_events(pf->hw.port_info);
- if (err) {
+ if (err)
dev_err(dev, "ice_init_link_events failed: %d\n", err);
- return err;
- }
/* not a fatal error if this fails */
err = ice_init_nvm_phy_type(pf->hw.port_info);
@@ -4806,8 +4804,6 @@ static int ice_init_link(struct ice_pf *pf)
} else {
set_bit(ICE_FLAG_NO_MEDIA, pf->flags);
}
-
- return err;
}
static int ice_init_pf_sw(struct ice_pf *pf)
@@ -4939,13 +4935,11 @@ static int ice_init(struct ice_pf *pf)
ice_init_wakeup(pf);
- err = ice_init_link(pf);
- if (err)
- goto err_init_link;
+ ice_init_link(pf);
err = ice_send_version(pf);
if (err)
- goto err_init_link;
+ goto err_deinit_pf_sw;
ice_verify_cacheline_size(pf);
@@ -4964,7 +4958,7 @@ static int ice_init(struct ice_pf *pf)
return 0;
-err_init_link:
+err_deinit_pf_sw:
ice_deinit_pf_sw(pf);
err_init_pf_sw:
ice_dealloc_vsis(pf);
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
index 1463fee451ef..fe4a641ce70d 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
@@ -784,7 +784,7 @@ void ice_reset_all_vfs(struct ice_pf *pf)
* setup only when VF creates its first FDIR rule.
*/
if (vf->ctrl_vsi_idx != ICE_NO_VSI)
- ice_vf_ctrl_invalidate_vsi(vf);
+ ice_vf_ctrl_vsi_release(vf);
ice_vf_pre_vsi_rebuild(vf);
if (ice_vf_rebuild_vsi(vf)) {
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 2941721b6515..eb7b13b66ea3 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -5866,6 +5866,9 @@ static int mvneta_resume(struct device *device)
rtnl_unlock();
mvneta_set_rx_mode(dev);
+ if (!pp->neta_armada3700)
+ on_each_cpu(mvneta_percpu_enable, pp, true);
+
return 0;
}
#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
index c1775bd01c2b..a07e0b3d8d00 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
@@ -120,13 +120,13 @@ void mcs_get_rx_secy_stats(struct mcs *mcs, struct mcs_secy_stats *stats, int id
reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYUNTAGGEDX(id);
stats->pkt_untaged_cnt = mcs_reg_read(mcs, reg);
- reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id);
- stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg);
-
if (mcs->hw->mcs_blks > 1) {
reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYNOTAGX(id);
stats->pkt_notag_cnt = mcs_reg_read(mcs, reg);
+ return;
}
+ reg = MCSX_CSE_RX_MEM_SLAVE_INPKTSSECYCTLX(id);
+ stats->pkt_ctl_cnt = mcs_reg_read(mcs, reg);
}
void mcs_get_flowid_stats(struct mcs *mcs, struct mcs_flowid_stats *stats,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
index 56dab11833b5..285ae2939634 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
@@ -505,10 +505,11 @@ static int rvu_dbg_mcs_rx_secy_stats_display(struct seq_file *filp, void *unused
seq_printf(filp, "secy%d: Tagged ctrl pkts: %lld\n", secy_id,
stats.pkt_tagged_ctl_cnt);
seq_printf(filp, "secy%d: Untaged pkts: %lld\n", secy_id, stats.pkt_untaged_cnt);
- seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt);
if (mcs->hw->mcs_blks > 1)
seq_printf(filp, "secy%d: pkts notag: %lld\n", secy_id,
stats.pkt_notag_cnt);
+ else
+ seq_printf(filp, "secy%d: Ctrl pkts: %lld\n", secy_id, stats.pkt_ctl_cnt);
}
mutex_unlock(&mcs->stats_lock);
return 0;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 0703b0d8df78..095c339166b5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -4121,7 +4121,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
rvu_npc_install_allmulti_entry(rvu, pcifunc, nixlf,
pfvf->rx_chan_base);
} else {
- if (!nix_rx_multicast)
+ if (!nix_rx_multicast && !is_vf(pcifunc))
rvu_npc_enable_allmulti_entry(rvu, pcifunc, nixlf, false);
}
@@ -4131,7 +4131,7 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req,
pfvf->rx_chan_base,
pfvf->rx_chan_cnt);
else
- if (!nix_rx_multicast)
+ if (!nix_rx_multicast && !is_vf(pcifunc))
rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false);
return 0;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
index 3af58bc9f533..8f07d19819ce 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_macsec.c
@@ -213,6 +213,7 @@ static void cn10k_mcs_free_rsrc(struct otx2_nic *pfvf, enum mcs_direction dir,
clear_req->id = hw_rsrc_id;
clear_req->type = type;
clear_req->dir = dir;
+ clear_req->all = all;
req = otx2_mbox_alloc_msg_mcs_free_resources(mbox);
if (!req)
@@ -1807,11 +1808,16 @@ int cn10k_mcs_init(struct otx2_nic *pfvf)
void cn10k_mcs_free(struct otx2_nic *pfvf)
{
+ struct cn10k_mcs_cfg *cfg = pfvf->macsec_cfg;
+
if (!test_bit(CN10K_HW_MACSEC, &pfvf->hw.cap_flag))
return;
- cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true);
- cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true);
+ if (!list_empty(&cfg->txsc_list)) {
+ cn10k_mcs_free_rsrc(pfvf, MCS_TX, MCS_RSRC_TYPE_SECY, 0, true);
+ cn10k_mcs_free_rsrc(pfvf, MCS_RX, MCS_RSRC_TYPE_SECY, 0, true);
+ }
+
kfree(pfvf->macsec_cfg);
pfvf->macsec_cfg = NULL;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index 784130b4b086..c4f5635284ef 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -1448,6 +1448,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf)
continue;
qmem_free(pf->dev, sq->sqe);
qmem_free(pf->dev, sq->tso_hdrs);
+ qmem_free(pf->dev, sq->timestamps);
kfree(sq->sg);
kfree(sq->sqb_ptrs);
}
@@ -2360,10 +2361,42 @@ EXPORT_SYMBOL(otx2_ioctl);
static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac)
{
+ struct npc_get_field_status_req *freq;
+ struct npc_get_field_status_rsp *frsp;
struct npc_install_flow_req *req;
int err;
mutex_lock(&pf->mbox.lock);
+
+ /* Skip installing the DMAC filter if the hardware parser profile
+ * does not support DMAC extraction.
+ */
+ freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox);
+ if (!freq) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ freq->field = NPC_DMAC;
+ err = otx2_sync_mbox_msg(&pf->mbox);
+ if (err)
+ goto out;
+
+ frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp
+ (&pf->mbox.mbox, 0, &freq->hdr);
+ if (IS_ERR(frsp)) {
+ err = PTR_ERR(frsp);
+ goto out;
+ }
+
+ if (!frsp->enable) {
+ netdev_warn(pf->netdev,
+ "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n",
+ vf);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox);
if (!req) {
err = -ENOMEM;
@@ -2402,13 +2435,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
if (!is_valid_ether_addr(mac))
return -EINVAL;
- config = &pf->vf_configs[vf];
- ether_addr_copy(config->mac, mac);
-
ret = otx2_do_set_vf_mac(pf, vf, mac);
- if (ret == 0)
- dev_info(&pdev->dev,
- "Load/Reload VF driver\n");
+ if (ret == 0) {
+ config = &pf->vf_configs[vf];
+ ether_addr_copy(config->mac, mac);
+ dev_info(&pdev->dev, "Load/Reload VF driver\n");
+ }
return ret;
}
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c
index 4fb886c57cd7..fa35372ded43 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
@@ -372,7 +372,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port)
struct device_node *ports, *node;
struct fwnode_handle *fwnode;
struct phylink *phy_link;
- int err;
+ int err = 0;
if (!sw->np)
return 0;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
index 1e033d63b451..eb4df95fe9d0 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -916,7 +916,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
mib = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*mib),
&ppe->mib_phys, GFP_KERNEL);
if (!mib)
- return NULL;
+ goto err_free_l2_flows;
ppe->mib_table = mib;
@@ -924,7 +924,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
GFP_KERNEL);
if (!acct)
- return NULL;
+ goto err_free_l2_flows;
ppe->acct_table = acct;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
index b4f3bd7d346e..1856d211339f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c
@@ -54,6 +54,12 @@ static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
priv->stats_nch);
}
+static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
+{
+ return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
+ max(priv->max_nch, priv->stats_nch));
+}
+
static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
{
struct mlx5e_hv_vhca_stats_agent *sagent;
@@ -67,7 +73,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
priv = container_of(sagent, struct mlx5e_priv, stats_agent);
buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
- agent = sagent->agent;
+ agent = READ_ONCE(sagent->agent);
buf = sagent->buf;
memset(buf, 0, buf_len);
@@ -122,18 +128,21 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
{
- int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
+ int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv);
struct mlx5_hv_vhca_agent *agent;
priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
if (!priv->stats_agent.buf)
return;
+ INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
+
agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
MLX5_HV_VHCA_AGENT_STATS,
mlx5e_hv_vhca_stats_control, NULL,
mlx5e_hv_vhca_stats_cleanup,
- priv);
+ priv,
+ &priv->stats_agent.agent);
if (IS_ERR_OR_NULL(agent)) {
if (IS_ERR(agent))
@@ -142,18 +151,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
PTR_ERR(agent));
kvfree(priv->stats_agent.buf);
- return;
+ priv->stats_agent.buf = NULL;
}
-
- priv->stats_agent.agent = agent;
- INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
}
void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
{
- if (IS_ERR_OR_NULL(priv->stats_agent.agent))
+ struct mlx5_hv_vhca_agent *agent;
+
+ agent = READ_ONCE(priv->stats_agent.agent);
+ if (IS_ERR_OR_NULL(agent))
return;
- mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
+ mlx5_hv_vhca_agent_destroy(agent);
+ WRITE_ONCE(priv->stats_agent.agent, NULL);
kvfree(priv->stats_agent.buf);
+ priv->stats_agent.buf = NULL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
index 6ab02f3fc291..70d8cce7c173 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
@@ -713,34 +713,43 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx)
}
sc_xarray_element->rx_sc = rx_sc;
- err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element,
- XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL);
- if (err) {
- if (err == -EBUSY)
- netdev_err(ctx->netdev,
- "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n",
- MLX5_MACEC_RX_FS_ID_MAX);
- goto destroy_sc_xarray_elemenet;
- }
rx_sc->md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL);
if (!rx_sc->md_dst) {
err = -ENOMEM;
- goto erase_xa_alloc;
+ goto destroy_sc_xarray_elemenet;
}
rx_sc->sci = ctx_rx_sc->sci;
rx_sc->active = ctx_rx_sc->active;
- list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list);
-
rx_sc->sc_xarray_element = sc_xarray_element;
rx_sc->md_dst->u.macsec_info.sci = rx_sc->sci;
+
+ /*
+ * Publish the fully-initialised SC last: xa_alloc() makes
+ * sc_xarray_element->rx_sc (and rx_sc->md_dst) reachable from the RX
+ * datapath via xa_load(). Doing it only after md_dst is allocated and
+ * initialised pairs with the rcu_read_lock()/xa_load() in
+ * mlx5e_macsec_offload_handle_rx_skb(), so a reader can never observe
+ * a non-NULL md_dst with uninitialised contents.
+ */
+ err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element,
+ XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL);
+ if (err) {
+ if (err == -EBUSY)
+ netdev_err(ctx->netdev,
+ "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n",
+ MLX5_MACEC_RX_FS_ID_MAX);
+ goto destroy_md_dst;
+ }
+
+ list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list);
mutex_unlock(&macsec->lock);
return 0;
-erase_xa_alloc:
- xa_erase(&macsec->sc_xarray, sc_xarray_element->fs_id);
+destroy_md_dst:
+ dst_release(&rx_sc->md_dst->dst);
destroy_sc_xarray_elemenet:
kfree(sc_xarray_element);
destroy_rx_sc:
@@ -828,7 +837,7 @@ static void macsec_del_rxsc_ctx(struct mlx5e_macsec *macsec, struct mlx5e_macsec
*/
list_del_rcu(&rx_sc->rx_sc_list_element);
xa_erase(&macsec->sc_xarray, rx_sc->sc_xarray_element->fs_id);
- metadata_dst_free(rx_sc->md_dst);
+ dst_release(&rx_sc->md_dst->dst);
kfree(rx_sc->sc_xarray_element);
kfree_rcu_mightsleep(rx_sc);
}
@@ -1698,10 +1707,10 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev,
rcu_read_lock();
sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id);
- rx_sc = sc_xarray_element->rx_sc;
- if (rx_sc) {
- dst_hold(&rx_sc->md_dst->dst);
- skb_dst_set(skb, &rx_sc->md_dst->dst);
+ rx_sc = sc_xarray_element ? sc_xarray_element->rx_sc : NULL;
+ if (rx_sc && rx_sc->md_dst) {
+ if (dst_hold_safe(&rx_sc->md_dst->dst))
+ skb_dst_set(skb, &rx_sc->md_dst->dst);
}
rcu_read_unlock();
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
index 30564d9b00e9..779342ebc843 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.c
@@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
NULL,
mlx5_hv_vhca_control_agent_invalidate,
- NULL, NULL);
+ NULL, NULL, NULL);
}
static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
@@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
- void *priv)
+ void *priv,
+ struct mlx5_hv_vhca_agent **ctx_update)
{
struct mlx5_hv_vhca_agent *agent;
@@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
agent->invalidate = invalidate;
agent->cleanup = cleaup;
+ if (ctx_update)
+ WRITE_ONCE(*ctx_update, agent);
+
mutex_lock(&hv_vhca->agents_lock);
hv_vhca->agents[type] = agent;
mutex_unlock(&hv_vhca->agents_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
index f240ffe5116c..8b3974cf0ee4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/hv_vhca.h
@@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
- void *context);
+ void *context,
+ struct mlx5_hv_vhca_agent **ctx_update);
void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent);
int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
@@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
void (*invalidate)(struct mlx5_hv_vhca_agent*,
u64 block_mask),
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
- void *context)
+ void *context,
+ struct mlx5_hv_vhca_agent **ctx_update)
{
return NULL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c
index 4571c56ec3c9..97f6097d4c70 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c
@@ -176,7 +176,8 @@ void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy,
int reformat_type)
{
mutex_lock(&tun_entropy->lock);
- if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN)
+ if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN ||
+ reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL)
tun_entropy->num_enabling_entries--;
else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE &&
--tun_entropy->num_disabling_entries == 0)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 80f3493fc544..9b8663227093 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -570,7 +570,6 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
{
struct mlx5_profile *prof = &dev->profile;
void *set_hca_cap;
- int max_uc_list;
int err;
err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL);
@@ -653,10 +652,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
MLX5_SET(cmd_hca_cap, set_hca_cap, roce,
mlx5_is_roce_on(dev));
- max_uc_list = max_uc_list_get_devlink_param(dev);
- if (max_uc_list > 0)
- MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_current_uc_list,
- ilog2(max_uc_list));
+ if (MLX5_CAP_GEN_MAX(dev, log_max_current_uc_list)) {
+ int max_uc_list = max_uc_list_get_devlink_param(dev);
+
+ if (max_uc_list > 0)
+ MLX5_SET(cmd_hca_cap, set_hca_cap,
+ log_max_current_uc_list, ilog2(max_uc_list));
+ }
return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 51f49510826a..9aa303464e97 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -4427,7 +4427,7 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
if (err)
- return err;
+ goto err_lag_uppers_bridge_join;
err = mlxsw_sp_lag_uppers_bridge_join(mlxsw_sp_port, lag_dev,
extack);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 4cd79473ace5..58357822754b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -1018,6 +1018,7 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp,
err_tree_replace:
for (i--; i >= 0; i--) {
+ vr = &mlxsw_sp->router->vrs[i];
if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id))
continue;
mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp,
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index 50abc227f6f1..01c58c64e9b7 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -3299,8 +3299,8 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter,
adapter->max_tx_channels = PCI11X1X_MAX_TX_CHANNELS;
adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS;
adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT;
- pci11x1x_strap_get_status(adapter);
spin_lock_init(&adapter->eth_syslock_spinlock);
+ pci11x1x_strap_get_status(adapter);
mutex_init(&adapter->sgmii_rw_lock);
pci11x1x_set_rfe_rd_fifo_threshold(adapter);
sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL);
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
index c266f903ea8a..ea17afdf9a9a 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
@@ -601,7 +601,6 @@ static void lan966x_vcap_admin_free(struct vcap_admin *admin)
kfree(admin->cache.keystream);
kfree(admin->cache.maskstream);
kfree(admin->cache.actionstream);
- mutex_destroy(&admin->lock);
kfree(admin);
}
@@ -615,7 +614,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl,
if (!admin)
return ERR_PTR(-ENOMEM);
- mutex_init(&admin->lock);
+ admin->vctrl = ctrl;
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
@@ -721,6 +720,7 @@ int lan966x_vcap_init(struct lan966x *lan966x)
ctrl->ops = &lan966x_vcap_ops;
INIT_LIST_HEAD(&ctrl->list);
+ mutex_init(&ctrl->lock);
for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) {
cfg = &lan966x_vcap_inst_cfg[i];
@@ -780,5 +780,6 @@ void lan966x_vcap_deinit(struct lan966x *lan966x)
lan966x_vcap_admin_free(admin);
}
+ mutex_destroy(&ctrl->lock);
kfree(ctrl);
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
index 4af85d108a06..3419454dcfee 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
@@ -740,11 +740,13 @@ int sparx5_register_notifier_blocks(struct sparx5 *s5)
sparx5_owq = alloc_ordered_workqueue("sparx5_order", 0);
if (!sparx5_owq) {
err = -ENOMEM;
- goto err_switchdev_blocking_nb;
+ goto err_alloc_workqueue;
}
return 0;
+err_alloc_workqueue:
+ unregister_switchdev_blocking_notifier(&s5->switchdev_blocking_nb);
err_switchdev_blocking_nb:
unregister_switchdev_notifier(&s5->switchdev_nb);
err_switchdev_nb:
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
index 187efa1fc904..1403cb343f21 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
@@ -1941,7 +1941,6 @@ static void sparx5_vcap_admin_free(struct vcap_admin *admin)
{
if (!admin)
return;
- mutex_destroy(&admin->lock);
kfree(admin->cache.keystream);
kfree(admin->cache.maskstream);
kfree(admin->cache.actionstream);
@@ -1961,7 +1960,7 @@ sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl,
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
- mutex_init(&admin->lock);
+ admin->vctrl = ctrl;
admin->vtype = cfg->vtype;
admin->vinst = cfg->vinst;
admin->ingress = cfg->ingress;
@@ -2069,6 +2068,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5)
ctrl->ops = &sparx5_vcap_ops;
INIT_LIST_HEAD(&ctrl->list);
+ mutex_init(&ctrl->lock);
for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) {
cfg = &sparx5_vcap_inst_cfg[idx];
admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg);
@@ -2107,5 +2107,6 @@ void sparx5_vcap_destroy(struct sparx5 *sparx5)
list_del(&admin->list);
sparx5_vcap_admin_free(admin);
}
+ mutex_destroy(&ctrl->lock);
kfree(ctrl);
}
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index ef980e4e5bc2..07437c94d474 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -934,6 +934,16 @@ static bool vcap_rule_exists(struct vcap_control *vctrl, u32 id)
return false;
}
+void vcap_lock(struct vcap_admin *admin)
+{
+ mutex_lock(&admin->vctrl->lock);
+}
+
+void vcap_unlock(struct vcap_admin *admin)
+{
+ mutex_unlock(&admin->vctrl->lock);
+}
+
/* Find a rule with a provided rule id return a locked vcap */
static struct vcap_rule_internal *
vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
@@ -943,11 +953,11 @@ vcap_get_locked_rule(struct vcap_control *vctrl, u32 id)
/* Look for the rule id in all vcaps */
list_for_each_entry(admin, &vctrl->list, list) {
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry(ri, &admin->rules, list)
if (ri->data.id == id)
return ri;
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
}
return NULL;
}
@@ -961,14 +971,14 @@ int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie)
/* Look for the rule id in all vcaps */
list_for_each_entry(admin, &vctrl->list, list) {
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry(ri, &admin->rules, list) {
if (ri->data.cookie == cookie) {
id = ri->data.id;
break;
}
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
if (id)
return id;
}
@@ -985,11 +995,11 @@ int vcap_admin_rule_count(struct vcap_admin *admin, int cid)
int count = 0;
list_for_each_entry(elem, &admin->rules, list) {
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
if (elem->data.vcap_chain_id >= min_cid &&
elem->data.vcap_chain_id < max_cid)
++count;
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
}
return count;
}
@@ -2266,7 +2276,7 @@ int vcap_add_rule(struct vcap_rule *rule)
if (ret)
return ret;
/* Insert the new rule in the list of vcap rules */
- mutex_lock(&ri->admin->lock);
+ vcap_lock(ri->admin);
vcap_rule_set_state(ri);
ret = vcap_insert_rule(ri, &move);
@@ -2302,7 +2312,7 @@ int vcap_add_rule(struct vcap_rule *rule)
goto out;
}
out:
- mutex_unlock(&ri->admin->lock);
+ vcap_unlock(ri->admin);
return ret;
}
EXPORT_SYMBOL_GPL(vcap_add_rule);
@@ -2330,7 +2340,7 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
if (vctrl->vcaps[admin->vtype].rows == 0)
return ERR_PTR(-EINVAL);
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
/* Check if a rule with this id already exists */
if (vcap_rule_exists(vctrl, id)) {
err = -EINVAL;
@@ -2369,13 +2379,13 @@ struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl,
goto out_free;
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
return (struct vcap_rule *)ri;
out_free:
kfree(ri);
out_unlock:
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
return ERR_PTR(err);
}
@@ -2446,7 +2456,7 @@ struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id)
return ERR_PTR(-ENOENT);
rule = vcap_decode_rule(elem);
- mutex_unlock(&elem->admin->lock);
+ vcap_unlock(elem->admin);
return rule;
}
EXPORT_SYMBOL_GPL(vcap_get_rule);
@@ -2483,7 +2493,7 @@ int vcap_mod_rule(struct vcap_rule *rule)
err = vcap_write_counter(ri, &ctr);
out:
- mutex_unlock(&ri->admin->lock);
+ vcap_unlock(ri->admin);
return err;
}
EXPORT_SYMBOL_GPL(vcap_mod_rule);
@@ -2570,7 +2580,7 @@ int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id)
admin->last_used_addr = elem->addr;
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
return err;
}
EXPORT_SYMBOL_GPL(vcap_del_rule);
@@ -2585,7 +2595,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
if (ret)
return ret;
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry_safe(ri, next_ri, &admin->rules, list) {
vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size);
list_del(&ri->list);
@@ -2598,7 +2608,7 @@ int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin)
list_del(&eport->list);
kfree(eport);
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
return 0;
}
@@ -3004,7 +3014,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
continue;
/* Found the admin, now find the offloadable rules */
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry(ri, &admin->rules, list) {
/* Is the rule in the lookup defined by the chain */
if (!(ri->data.vcap_chain_id >= chain &&
@@ -3022,7 +3032,7 @@ static int vcap_enable_rules(struct vcap_control *vctrl,
if (err)
break;
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
if (err)
break;
}
@@ -3062,7 +3072,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
continue;
/* Found the admin, now find the rules on the chain */
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry(ri, &admin->rules, list) {
if (ri->data.vcap_chain_id != chain)
continue;
@@ -3077,7 +3087,7 @@ static int vcap_disable_rules(struct vcap_control *vctrl,
if (err)
break;
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
if (err)
break;
}
@@ -3121,9 +3131,9 @@ static int vcap_enable(struct vcap_control *vctrl, struct net_device *ndev,
eport->cookie = cookie;
eport->src_cid = src_cid;
eport->dst_cid = dst_cid;
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_add_tail(&eport->list, &admin->enabled);
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
if (vcap_path_exist(vctrl, ndev, src_cid)) {
/* Enable chained lookups */
@@ -3173,9 +3183,9 @@ static int vcap_disable(struct vcap_control *vctrl, struct net_device *ndev,
dst_cid = vcap_get_next_chain(vctrl, ndev, dst_cid);
}
- mutex_lock(&found->lock);
+ vcap_lock(found);
list_del(&eport->list);
- mutex_unlock(&found->lock);
+ vcap_unlock(found);
kfree(eport);
return 0;
}
@@ -3258,9 +3268,9 @@ int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
return -EINVAL;
}
- mutex_lock(&ri->admin->lock);
+ vcap_lock(ri->admin);
err = vcap_write_counter(ri, ctr);
- mutex_unlock(&ri->admin->lock);
+ vcap_unlock(ri->admin);
return err;
}
@@ -3279,9 +3289,9 @@ int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr)
return -EINVAL;
}
- mutex_lock(&ri->admin->lock);
+ vcap_lock(ri->admin);
err = vcap_read_counter(ri, ctr);
- mutex_unlock(&ri->admin->lock);
+ vcap_unlock(ri->admin);
return err;
}
@@ -3383,7 +3393,7 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
/* Iterate all rules in each VCAP instance */
list_for_each_entry(admin, &vctrl->list, list) {
- mutex_lock(&admin->lock);
+ vcap_lock(admin);
list_for_each_entry(ri, &admin->rules, list) {
if (ri->data.cookie != cookie)
continue;
@@ -3400,12 +3410,12 @@ int vcap_get_rule_count_by_cookie(struct vcap_control *vctrl,
if (err)
goto unlock;
}
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
}
return err;
unlock:
- mutex_unlock(&admin->lock);
+ vcap_unlock(admin);
return err;
}
EXPORT_SYMBOL_GPL(vcap_get_rule_count_by_cookie);
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h
index 9eccfa633c1a..944da1997a55 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h
@@ -164,7 +164,7 @@ struct vcap_admin {
struct list_head list; /* for insertion in vcap_control */
struct list_head rules; /* list of rules */
struct list_head enabled; /* list of enabled ports */
- struct mutex lock; /* control access to rules */
+ struct vcap_control *vctrl; /* the control instance owning this vcap */
enum vcap_type vtype; /* type of vcap */
int vinst; /* instance number within the same type */
int first_cid; /* first chain id in this vcap */
@@ -275,6 +275,7 @@ struct vcap_control {
const struct vcap_info *vcaps; /* client supplied vcap models */
const struct vcap_statistics *stats; /* client supplied vcap stats */
struct list_head list; /* list of vcap instances */
+ struct mutex lock; /* serialize access to all vcap instances */
};
#endif /* __VCAP_API__ */
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
index c2c3397c5898..78e2d85bb503 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
@@ -410,9 +410,9 @@ static int vcap_debugfs_show(struct seq_file *m, void *unused)
};
int ret;
- mutex_lock(&info->admin->lock);
+ vcap_lock(info->admin);
ret = vcap_show_admin(info->vctrl, info->admin, &out);
- mutex_unlock(&info->admin->lock);
+ vcap_unlock(info->admin);
return ret;
}
DEFINE_SHOW_ATTRIBUTE(vcap_debugfs);
@@ -427,9 +427,9 @@ static int vcap_raw_debugfs_show(struct seq_file *m, void *unused)
};
int ret;
- mutex_lock(&info->admin->lock);
+ vcap_lock(info->admin);
ret = vcap_show_admin_raw(info->vctrl, info->admin, &out);
- mutex_unlock(&info->admin->lock);
+ vcap_unlock(info->admin);
return ret;
}
DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs);
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c
index b23c11b0647c..5511933ef063 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c
@@ -243,10 +243,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
{
/* Initialize the shared objects */
INIT_LIST_HEAD(&test_vctrl.list);
+ mutex_init(&test_vctrl.lock);
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
- mutex_init(&admin->lock);
+ admin->vctrl = &test_vctrl;
list_add_tail(&admin->list, &test_vctrl.list);
memset(test_updateaddr, 0, sizeof(test_updateaddr));
test_updateaddridx = 0;
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
index 88744ae65293..d0569307de13 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c
@@ -233,10 +233,11 @@ static void vcap_test_api_init(struct vcap_admin *admin)
{
/* Initialize the shared objects */
INIT_LIST_HEAD(&test_vctrl.list);
+ mutex_init(&test_vctrl.lock);
INIT_LIST_HEAD(&admin->list);
INIT_LIST_HEAD(&admin->rules);
INIT_LIST_HEAD(&admin->enabled);
- mutex_init(&admin->lock);
+ admin->vctrl = &test_vctrl;
list_add_tail(&admin->list, &test_vctrl.list);
memset(test_updateaddr, 0, sizeof(test_updateaddr));
test_updateaddridx = 0;
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
index df81d9ff502b..80663f911bd2 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h
@@ -50,6 +50,9 @@ struct vcap_stream_iter {
/* Check that the control has a valid set of callbacks */
int vcap_api_check(struct vcap_control *ctrl);
+/* Serialize access to the vcap instances of a control */
+void vcap_lock(struct vcap_admin *admin);
+void vcap_unlock(struct vcap_admin *admin);
/* Erase the VCAP cache area used or encoding and decoding */
void vcap_erase_cache(struct vcap_rule_internal *ri);
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index d3c9a3020fbf..8a91b056931f 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -840,6 +840,8 @@ int mana_gd_create_mana_wq_cq(struct gdma_dev *gd,
if (!queue)
return -ENOMEM;
+ queue->id = INVALID_QUEUE_ID;
+
gmi = &queue->mem_info;
err = mana_gd_alloc_memory(gc, spec->queue_size, gmi);
if (err)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 3cf4ad1d91f6..0249ba7e3b92 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1880,7 +1880,8 @@ static void mana_destroy_txq(struct mana_port_context *apc)
netif_napi_del(napi);
apc->tx_qp[i].txq.napi_initialized = false;
}
- mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object);
+ if (apc->tx_qp[i].tx_object != INVALID_MANA_HANDLE)
+ mana_destroy_wq_obj(apc, GDMA_SQ, apc->tx_qp[i].tx_object);
mana_deinit_cq(apc, &apc->tx_qp[i].tx_cq);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index b07dd56b0c76..38c2695bc529 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -114,8 +114,15 @@ static void ionic_get_link_ext_stats(struct net_device *netdev,
{
struct ionic_lif *lif = netdev_priv(netdev);
- if (lif->ionic->pdev->is_physfn)
- stats->link_down_events = lif->link_down_count;
+ if (lif->ionic->pdev->is_virtfn)
+ return;
+
+ if (!lif->ionic->idev.port_info) {
+ netdev_err_once(netdev, "port_info not initialized\n");
+ return;
+ }
+
+ stats->link_down_events = lif->link_down_count;
}
static int ionic_get_link_ksettings(struct net_device *netdev,
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 273dae622c41..51affa97aab4 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -765,6 +765,9 @@ qede_tpa_rx_build_skb(struct qede_dev *edev,
struct sk_buff *skb;
skb = qede_build_skb(rxq, bd, len, pad);
+ if (unlikely(!skb))
+ return NULL;
+
bd->page_offset += rxq->rx_buf_seg_size;
if (bd->page_offset == PAGE_SIZE) {
@@ -812,6 +815,8 @@ qede_rx_build_skb(struct qede_dev *edev,
}
skb = qede_build_skb(rxq, bd, len, pad);
+ if (unlikely(!skb))
+ return NULL;
if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
/* Incr page ref count to reuse on allocation failure so
@@ -961,7 +966,7 @@ static inline void qede_tpa_cont(struct qede_dev *edev,
{
int i;
- for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++)
+ for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++)
qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
le16_to_cpu(cqe->len_list[i]));
@@ -986,7 +991,7 @@ static int qede_tpa_end(struct qede_dev *edev,
dma_unmap_page(rxq->dev, tpa_info->buffer.mapping,
PAGE_SIZE, rxq->data_direction);
- for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++)
+ for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++)
qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
le16_to_cpu(cqe->len_list[i]));
if (unlikely(i > 1))
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
index 9f3479500f85..d055a2628d8c 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c
@@ -126,7 +126,10 @@ rmnet_map_ingress_handler(struct sk_buff *skb,
consume_skb(skb);
} else {
- __rmnet_map_ingress_handler(skb, port);
+ if (rmnet_map_validate_packet_len(skb, port))
+ __rmnet_map_ingress_handler(skb, port);
+ else
+ kfree_skb(skb);
}
}
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
index b70284095568..60ca8b780c88 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h
@@ -59,5 +59,6 @@ void rmnet_map_tx_aggregate_init(struct rmnet_port *port);
void rmnet_map_tx_aggregate_exit(struct rmnet_port *port);
void rmnet_map_update_ul_agg_config(struct rmnet_port *port, u32 size,
u32 count, u32 time);
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port);
#endif /* _RMNET_MAP_H_ */
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index a5e3d1a88305..8471157a431b 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -333,54 +333,62 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,
return map_header;
}
-/* Deaggregates a single packet
- * A whole new buffer is allocated for each portion of an aggregated frame.
- * Caller should keep calling deaggregate() on the source skb until 0 is
- * returned, indicating that there are no more packets to deaggregate. Caller
- * is responsible for freeing the original skb.
- */
-struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
- struct rmnet_port *port)
+u32 rmnet_map_validate_packet_len(struct sk_buff *skb, struct rmnet_port *port)
{
struct rmnet_map_v5_csum_header *next_hdr = NULL;
struct rmnet_map_header *maph;
void *data = skb->data;
- struct sk_buff *skbn;
- u8 nexthdr_type;
u32 packet_len;
- if (skb->len == 0)
- return NULL;
+ if (skb->len < sizeof(*maph))
+ return 0;
maph = (struct rmnet_map_header *)skb->data;
+
+ /* Some hardware can send us empty frames. Catch them */
+ if (!maph->pkt_len)
+ return 0;
+
packet_len = ntohs(maph->pkt_len) + sizeof(*maph);
if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV4) {
packet_len += sizeof(struct rmnet_map_dl_csum_trailer);
- } else if (port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) {
- if (!(maph->flags & MAP_CMD_FLAG)) {
- packet_len += sizeof(*next_hdr);
- if (maph->flags & MAP_NEXT_HEADER_FLAG)
- next_hdr = data + sizeof(*maph);
- else
- /* Mapv5 data pkt without csum hdr is invalid */
- return NULL;
- }
+ } else if ((port->data_format & RMNET_FLAGS_INGRESS_MAP_CKSUMV5) &&
+ !(maph->flags & MAP_CMD_FLAG)) {
+ /* Mapv5 data pkt without csum hdr is invalid */
+ if (!(maph->flags & MAP_NEXT_HEADER_FLAG))
+ return 0;
+
+ packet_len += sizeof(*next_hdr);
+ next_hdr = data + sizeof(*maph);
}
- if (((int)skb->len - (int)packet_len) < 0)
- return NULL;
+ if (skb->len < packet_len)
+ return 0;
- /* Some hardware can send us empty frames. Catch them */
- if (!maph->pkt_len)
- return NULL;
+ if (next_hdr &&
+ u8_get_bits(next_hdr->header_info, MAPV5_HDRINFO_HDR_TYPE_FMASK) !=
+ RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
+ return 0;
- if (next_hdr) {
- nexthdr_type = u8_get_bits(next_hdr->header_info,
- MAPV5_HDRINFO_HDR_TYPE_FMASK);
- if (nexthdr_type != RMNET_MAP_HEADER_TYPE_CSUM_OFFLOAD)
- return NULL;
- }
+ return packet_len;
+}
+
+/* Deaggregates a single packet
+ * A whole new buffer is allocated for each portion of an aggregated frame.
+ * Caller should keep calling deaggregate() on the source skb until 0 is
+ * returned, indicating that there are no more packets to deaggregate. Caller
+ * is responsible for freeing the original skb.
+ */
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+ struct rmnet_port *port)
+{
+ struct sk_buff *skbn;
+ u32 packet_len;
+
+ packet_len = rmnet_map_validate_packet_len(skb, port);
+ if (!packet_len)
+ return NULL;
skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
if (!skbn)
diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c
index d3a2fbb14140..7fc18a465e64 100644
--- a/drivers/net/ethernet/sun/sungem.c
+++ b/drivers/net/ethernet/sun/sungem.c
@@ -2986,10 +2986,10 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->max_mtu = GEM_MAX_MTU;
/* Register with kernel */
- if (register_netdev(dev)) {
+ err = register_netdev(dev);
+ if (err) {
pr_err("Cannot register net device, aborting\n");
- err = -ENOMEM;
- goto err_out_free_consistent;
+ goto err_out_clear_drvdata;
}
/* Undo the get_cell with appropriate locking (we could use
@@ -3003,8 +3003,13 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->dev_addr);
return 0;
+err_out_clear_drvdata:
+ pci_set_drvdata(pdev, NULL);
+ netif_napi_del(&gp->napi);
+
err_out_free_consistent:
- gem_remove_one(pdev);
+ dma_free_coherent(&pdev->dev, sizeof(struct gem_init_block),
+ gp->init_block, gp->gblock_dvma);
err_out_iounmap:
gem_put_cell(gp);
iounmap(gp->regs);
diff --git a/drivers/net/ethernet/sunplus/spl2sw_phy.c b/drivers/net/ethernet/sunplus/spl2sw_phy.c
index 6f899e48f51d..a4889c52e00e 100644
--- a/drivers/net/ethernet/sunplus/spl2sw_phy.c
+++ b/drivers/net/ethernet/sunplus/spl2sw_phy.c
@@ -79,12 +79,14 @@ int spl2sw_phy_connect(struct spl2sw_common *comm)
void spl2sw_phy_remove(struct spl2sw_common *comm)
{
struct net_device *ndev;
+ struct spl2sw_mac *mac;
int i;
for (i = 0; i < MAX_NETDEV_NUM; i++)
if (comm->ndev[i]) {
ndev = comm->ndev[i];
- if (ndev)
- phy_disconnect(ndev->phydev);
+ mac = netdev_priv(ndev);
+ phy_disconnect(ndev->phydev);
+ of_node_put(mac->phy_node);
}
}
diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c
index 587643a371de..50fdbe70cae8 100644
--- a/drivers/net/ieee802154/ca8210.c
+++ b/drivers/net/ieee802154/ca8210.c
@@ -597,7 +597,7 @@ static int ca8210_test_int_driver_write(
fifo_buffer = kmemdup(buf, len, GFP_KERNEL);
if (!fifo_buffer)
return -ENOMEM;
- kfifo_in(&test->up_fifo, &fifo_buffer, 4);
+ kfifo_in(&test->up_fifo, &fifo_buffer, sizeof(fifo_buffer));
wake_up_interruptible(&priv->test.readq);
return 0;
@@ -921,9 +921,10 @@ static int ca8210_spi_transfer(
if (status < 0) {
dev_crit(
&spi->dev,
- "status %d from spi_sync in write\n",
+ "status %d from spi_async in write\n",
status
);
+ kfree(cas_ctl);
}
return status;
@@ -2527,6 +2528,7 @@ static ssize_t ca8210_test_int_user_read(
struct ca8210_priv *priv = filp->private_data;
unsigned char *fifo_buffer;
unsigned long bytes_not_copied;
+ unsigned int copied;
if (filp->f_flags & O_NONBLOCK) {
/* Non-blocking mode */
@@ -2540,7 +2542,8 @@ static ssize_t ca8210_test_int_user_read(
);
}
- if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) {
+ copied = kfifo_out(&priv->test.up_fifo, &fifo_buffer, sizeof(fifo_buffer));
+ if (copied != sizeof(fifo_buffer)) {
dev_err(
&priv->spi->dev,
"test_interface: Wrong number of elements popped from upstream fifo\n"
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index d9789b296906..c15b38ece607 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -638,7 +638,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
}
unprotected_len = skb->len;
- eth = eth_hdr(skb);
+ eth = skb_eth_hdr(skb);
sci_present = macsec_send_sci(secy);
hh = skb_push(skb, macsec_extra_len(sci_present));
memmove(hh, eth, 2 * ETH_ALEN);
@@ -3558,19 +3558,22 @@ static int macsec_dev_open(struct net_device *dev)
ops = macsec_get_ops(netdev_priv(dev), &ctx);
if (!ops) {
err = -EOPNOTSUPP;
- goto clear_allmulti;
+ goto clear_promisc;
}
ctx.secy = &macsec->secy;
err = macsec_offload(ops->mdo_dev_open, &ctx);
if (err)
- goto clear_allmulti;
+ goto clear_promisc;
}
if (netif_carrier_ok(real_dev))
netif_carrier_on(dev);
return 0;
+clear_promisc:
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(real_dev, -1);
clear_allmulti:
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(real_dev, -1);
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 165c624c985f..e352ceb2f7cb 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -747,6 +747,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp)
static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
{
mdiobus_unregister(sfp->i2c_mii);
+ mdiobus_free(sfp->i2c_mii);
sfp->i2c_mii = NULL;
}
diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
index f582d81a5091..244db32c1060 100644
--- a/drivers/net/team/Makefile
+++ b/drivers/net/team/Makefile
@@ -3,6 +3,7 @@
# Makefile for the network team driver
#
+team-y:= team_core.o
obj-$(CONFIG_NET_TEAM) += team.o
obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o
obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
deleted file mode 100644
index deb6eb3a240a..000000000000
--- a/drivers/net/team/team.c
+++ /dev/null
@@ -1,3120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * drivers/net/team/team.c - Network team device driver
- * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
- */
-
-#include <linux/ethtool.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/rcupdate.h>
-#include <linux/errno.h>
-#include <linux/ctype.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/netpoll.h>
-#include <linux/if_vlan.h>
-#include <linux/if_arp.h>
-#include <linux/socket.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <net/rtnetlink.h>
-#include <net/genetlink.h>
-#include <net/netlink.h>
-#include <net/sch_generic.h>
-#include <generated/utsrelease.h>
-#include <linux/if_team.h>
-
-#define DRV_NAME "team"
-
-
-/**********
- * Helpers
- **********/
-
-static struct team_port *team_port_get_rtnl(const struct net_device *dev)
-{
- struct team_port *port = rtnl_dereference(dev->rx_handler_data);
-
- return netif_is_team_port(dev) ? port : NULL;
-}
-
-/*
- * Since the ability to change device address for open port device is tested in
- * team_port_add, this function can be called without control of return value
- */
-static int __set_port_dev_addr(struct net_device *port_dev,
- const unsigned char *dev_addr)
-{
- struct sockaddr_storage addr;
-
- memcpy(addr.__data, dev_addr, port_dev->addr_len);
- addr.ss_family = port_dev->type;
- return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
-}
-
-static int team_port_set_orig_dev_addr(struct team_port *port)
-{
- return __set_port_dev_addr(port->dev, port->orig.dev_addr);
-}
-
-static int team_port_set_team_dev_addr(struct team *team,
- struct team_port *port)
-{
- return __set_port_dev_addr(port->dev, team->dev->dev_addr);
-}
-
-int team_modeop_port_enter(struct team *team, struct team_port *port)
-{
- return team_port_set_team_dev_addr(team, port);
-}
-EXPORT_SYMBOL(team_modeop_port_enter);
-
-void team_modeop_port_change_dev_addr(struct team *team,
- struct team_port *port)
-{
- team_port_set_team_dev_addr(team, port);
-}
-EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
-
-static void team_lower_state_changed(struct team_port *port)
-{
- struct netdev_lag_lower_state_info info;
-
- info.link_up = port->linkup;
- info.tx_enabled = team_port_enabled(port);
- netdev_lower_state_changed(port->dev, &info);
-}
-
-static void team_refresh_port_linkup(struct team_port *port)
-{
- bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
- port->state.linkup;
-
- if (port->linkup != new_linkup) {
- port->linkup = new_linkup;
- team_lower_state_changed(port);
- }
-}
-
-
-/*******************
- * Options handling
- *******************/
-
-struct team_option_inst { /* One for each option instance */
- struct list_head list;
- struct list_head tmp_list;
- struct team_option *option;
- struct team_option_inst_info info;
- bool changed;
- bool removed;
-};
-
-static struct team_option *__team_find_option(struct team *team,
- const char *opt_name)
-{
- struct team_option *option;
-
- list_for_each_entry(option, &team->option_list, list) {
- if (strcmp(option->name, opt_name) == 0)
- return option;
- }
- return NULL;
-}
-
-static void __team_option_inst_del(struct team_option_inst *opt_inst)
-{
- list_del(&opt_inst->list);
- kfree(opt_inst);
-}
-
-static void __team_option_inst_del_option(struct team *team,
- struct team_option *option)
-{
- struct team_option_inst *opt_inst, *tmp;
-
- list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
- if (opt_inst->option == option)
- __team_option_inst_del(opt_inst);
- }
-}
-
-static int __team_option_inst_add(struct team *team, struct team_option *option,
- struct team_port *port)
-{
- struct team_option_inst *opt_inst;
- unsigned int array_size;
- unsigned int i;
-
- array_size = option->array_size;
- if (!array_size)
- array_size = 1; /* No array but still need one instance */
-
- for (i = 0; i < array_size; i++) {
- opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
- if (!opt_inst)
- return -ENOMEM;
- opt_inst->option = option;
- opt_inst->info.port = port;
- opt_inst->info.array_index = i;
- opt_inst->changed = true;
- opt_inst->removed = false;
- list_add_tail(&opt_inst->list, &team->option_inst_list);
- if (option->init)
- option->init(team, &opt_inst->info);
-
- }
- return 0;
-}
-
-static int __team_option_inst_add_option(struct team *team,
- struct team_option *option)
-{
- int err;
-
- if (!option->per_port) {
- err = __team_option_inst_add(team, option, NULL);
- if (err)
- goto inst_del_option;
- }
- return 0;
-
-inst_del_option:
- __team_option_inst_del_option(team, option);
- return err;
-}
-
-static void __team_option_inst_mark_removed_option(struct team *team,
- struct team_option *option)
-{
- struct team_option_inst *opt_inst;
-
- list_for_each_entry(opt_inst, &team->option_inst_list, list) {
- if (opt_inst->option == option) {
- opt_inst->changed = true;
- opt_inst->removed = true;
- }
- }
-}
-
-static void __team_option_inst_del_port(struct team *team,
- struct team_port *port)
-{
- struct team_option_inst *opt_inst, *tmp;
-
- list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
- if (opt_inst->option->per_port &&
- opt_inst->info.port == port)
- __team_option_inst_del(opt_inst);
- }
-}
-
-static int __team_option_inst_add_port(struct team *team,
- struct team_port *port)
-{
- struct team_option *option;
- int err;
-
- list_for_each_entry(option, &team->option_list, list) {
- if (!option->per_port)
- continue;
- err = __team_option_inst_add(team, option, port);
- if (err)
- goto inst_del_port;
- }
- return 0;
-
-inst_del_port:
- __team_option_inst_del_port(team, port);
- return err;
-}
-
-static void __team_option_inst_mark_removed_port(struct team *team,
- struct team_port *port)
-{
- struct team_option_inst *opt_inst;
-
- list_for_each_entry(opt_inst, &team->option_inst_list, list) {
- if (opt_inst->info.port == port) {
- opt_inst->changed = true;
- opt_inst->removed = true;
- }
- }
-}
-
-static int __team_options_register(struct team *team,
- const struct team_option *option,
- size_t option_count)
-{
- int i;
- struct team_option **dst_opts;
- int err;
-
- dst_opts = kcalloc(option_count, sizeof(struct team_option *),
- GFP_KERNEL);
- if (!dst_opts)
- return -ENOMEM;
- for (i = 0; i < option_count; i++, option++) {
- if (__team_find_option(team, option->name)) {
- err = -EEXIST;
- goto alloc_rollback;
- }
- dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
- if (!dst_opts[i]) {
- err = -ENOMEM;
- goto alloc_rollback;
- }
- }
-
- for (i = 0; i < option_count; i++) {
- err = __team_option_inst_add_option(team, dst_opts[i]);
- if (err)
- goto inst_rollback;
- list_add_tail(&dst_opts[i]->list, &team->option_list);
- }
-
- kfree(dst_opts);
- return 0;
-
-inst_rollback:
- for (i--; i >= 0; i--) {
- __team_option_inst_del_option(team, dst_opts[i]);
- list_del(&dst_opts[i]->list);
- }
-
- i = option_count;
-alloc_rollback:
- for (i--; i >= 0; i--)
- kfree(dst_opts[i]);
-
- kfree(dst_opts);
- return err;
-}
-
-static void __team_options_mark_removed(struct team *team,
- const struct team_option *option,
- size_t option_count)
-{
- int i;
-
- for (i = 0; i < option_count; i++, option++) {
- struct team_option *del_opt;
-
- del_opt = __team_find_option(team, option->name);
- if (del_opt)
- __team_option_inst_mark_removed_option(team, del_opt);
- }
-}
-
-static void __team_options_unregister(struct team *team,
- const struct team_option *option,
- size_t option_count)
-{
- int i;
-
- for (i = 0; i < option_count; i++, option++) {
- struct team_option *del_opt;
-
- del_opt = __team_find_option(team, option->name);
- if (del_opt) {
- __team_option_inst_del_option(team, del_opt);
- list_del(&del_opt->list);
- kfree(del_opt);
- }
- }
-}
-
-static void __team_options_change_check(struct team *team);
-
-int team_options_register(struct team *team,
- const struct team_option *option,
- size_t option_count)
-{
- int err;
-
- err = __team_options_register(team, option, option_count);
- if (err)
- return err;
- __team_options_change_check(team);
- return 0;
-}
-EXPORT_SYMBOL(team_options_register);
-
-void team_options_unregister(struct team *team,
- const struct team_option *option,
- size_t option_count)
-{
- __team_options_mark_removed(team, option, option_count);
- __team_options_change_check(team);
- __team_options_unregister(team, option, option_count);
-}
-EXPORT_SYMBOL(team_options_unregister);
-
-static int team_option_get(struct team *team,
- struct team_option_inst *opt_inst,
- struct team_gsetter_ctx *ctx)
-{
- if (!opt_inst->option->getter)
- return -EOPNOTSUPP;
-
- opt_inst->option->getter(team, ctx);
- return 0;
-}
-
-static int team_option_set(struct team *team,
- struct team_option_inst *opt_inst,
- struct team_gsetter_ctx *ctx)
-{
- if (!opt_inst->option->setter)
- return -EOPNOTSUPP;
- return opt_inst->option->setter(team, ctx);
-}
-
-void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
-{
- struct team_option_inst *opt_inst;
-
- opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
- opt_inst->changed = true;
-}
-EXPORT_SYMBOL(team_option_inst_set_change);
-
-void team_options_change_check(struct team *team)
-{
- __team_options_change_check(team);
-}
-EXPORT_SYMBOL(team_options_change_check);
-
-
-/****************
- * Mode handling
- ****************/
-
-static LIST_HEAD(mode_list);
-static DEFINE_SPINLOCK(mode_list_lock);
-
-struct team_mode_item {
- struct list_head list;
- const struct team_mode *mode;
-};
-
-static struct team_mode_item *__find_mode(const char *kind)
-{
- struct team_mode_item *mitem;
-
- list_for_each_entry(mitem, &mode_list, list) {
- if (strcmp(mitem->mode->kind, kind) == 0)
- return mitem;
- }
- return NULL;
-}
-
-static bool is_good_mode_name(const char *name)
-{
- while (*name != '\0') {
- if (!isalpha(*name) && !isdigit(*name) && *name != '_')
- return false;
- name++;
- }
- return true;
-}
-
-int team_mode_register(const struct team_mode *mode)
-{
- int err = 0;
- struct team_mode_item *mitem;
-
- if (!is_good_mode_name(mode->kind) ||
- mode->priv_size > TEAM_MODE_PRIV_SIZE)
- return -EINVAL;
-
- mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
- if (!mitem)
- return -ENOMEM;
-
- spin_lock(&mode_list_lock);
- if (__find_mode(mode->kind)) {
- err = -EEXIST;
- kfree(mitem);
- goto unlock;
- }
- mitem->mode = mode;
- list_add_tail(&mitem->list, &mode_list);
-unlock:
- spin_unlock(&mode_list_lock);
- return err;
-}
-EXPORT_SYMBOL(team_mode_register);
-
-void team_mode_unregister(const struct team_mode *mode)
-{
- struct team_mode_item *mitem;
-
- spin_lock(&mode_list_lock);
- mitem = __find_mode(mode->kind);
- if (mitem) {
- list_del_init(&mitem->list);
- kfree(mitem);
- }
- spin_unlock(&mode_list_lock);
-}
-EXPORT_SYMBOL(team_mode_unregister);
-
-static const struct team_mode *team_mode_get(const char *kind)
-{
- struct team_mode_item *mitem;
- const struct team_mode *mode = NULL;
-
- if (!try_module_get(THIS_MODULE))
- return NULL;
-
- spin_lock(&mode_list_lock);
- mitem = __find_mode(kind);
- if (!mitem) {
- spin_unlock(&mode_list_lock);
- request_module("team-mode-%s", kind);
- spin_lock(&mode_list_lock);
- mitem = __find_mode(kind);
- }
- if (mitem) {
- mode = mitem->mode;
- if (!try_module_get(mode->owner))
- mode = NULL;
- }
-
- spin_unlock(&mode_list_lock);
- module_put(THIS_MODULE);
- return mode;
-}
-
-static void team_mode_put(const struct team_mode *mode)
-{
- module_put(mode->owner);
-}
-
-static bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
-{
- dev_kfree_skb_any(skb);
- return false;
-}
-
-static rx_handler_result_t team_dummy_receive(struct team *team,
- struct team_port *port,
- struct sk_buff *skb)
-{
- return RX_HANDLER_ANOTHER;
-}
-
-static const struct team_mode __team_no_mode = {
- .kind = "*NOMODE*",
-};
-
-static bool team_is_mode_set(struct team *team)
-{
- return team->mode != &__team_no_mode;
-}
-
-static void team_set_no_mode(struct team *team)
-{
- team->user_carrier_enabled = false;
- team->mode = &__team_no_mode;
-}
-
-static void team_adjust_ops(struct team *team)
-{
- /*
- * To avoid checks in rx/tx skb paths, ensure here that non-null and
- * correct ops are always set.
- */
-
- if (!team->en_port_count || !team_is_mode_set(team) ||
- !team->mode->ops->transmit)
- team->ops.transmit = team_dummy_transmit;
- else
- team->ops.transmit = team->mode->ops->transmit;
-
- if (!team->en_port_count || !team_is_mode_set(team) ||
- !team->mode->ops->receive)
- team->ops.receive = team_dummy_receive;
- else
- team->ops.receive = team->mode->ops->receive;
-}
-
-/*
- * We can benefit from the fact that it's ensured no port is present
- * at the time of mode change. Therefore no packets are in fly so there's no
- * need to set mode operations in any special way.
- */
-static int __team_change_mode(struct team *team,
- const struct team_mode *new_mode)
-{
- /* Check if mode was previously set and do cleanup if so */
- if (team_is_mode_set(team)) {
- void (*exit_op)(struct team *team) = team->ops.exit;
-
- /* Clear ops area so no callback is called any longer */
- memset(&team->ops, 0, sizeof(struct team_mode_ops));
- team_adjust_ops(team);
-
- if (exit_op)
- exit_op(team);
- team_mode_put(team->mode);
- team_set_no_mode(team);
- /* zero private data area */
- memset(&team->mode_priv, 0,
- sizeof(struct team) - offsetof(struct team, mode_priv));
- }
-
- if (!new_mode)
- return 0;
-
- if (new_mode->ops->init) {
- int err;
-
- err = new_mode->ops->init(team);
- if (err)
- return err;
- }
-
- team->mode = new_mode;
- memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
- team_adjust_ops(team);
-
- return 0;
-}
-
-static int team_change_mode(struct team *team, const char *kind)
-{
- const struct team_mode *new_mode;
- struct net_device *dev = team->dev;
- int err;
-
- if (!list_empty(&team->port_list)) {
- netdev_err(dev, "No ports can be present during mode change\n");
- return -EBUSY;
- }
-
- if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
- netdev_err(dev, "Unable to change to the same mode the team is in\n");
- return -EINVAL;
- }
-
- new_mode = team_mode_get(kind);
- if (!new_mode) {
- netdev_err(dev, "Mode \"%s\" not found\n", kind);
- return -EINVAL;
- }
-
- err = __team_change_mode(team, new_mode);
- if (err) {
- netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
- team_mode_put(new_mode);
- return err;
- }
-
- netdev_info(dev, "Mode changed to \"%s\"\n", kind);
- return 0;
-}
-
-
-/*********************
- * Peers notification
- *********************/
-
-static void team_notify_peers_work(struct work_struct *work)
-{
- struct team *team;
- int val;
-
- team = container_of(work, struct team, notify_peers.dw.work);
-
- if (!rtnl_trylock()) {
- schedule_delayed_work(&team->notify_peers.dw, 0);
- return;
- }
- val = atomic_dec_if_positive(&team->notify_peers.count_pending);
- if (val < 0) {
- rtnl_unlock();
- return;
- }
- call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
- rtnl_unlock();
- if (val)
- schedule_delayed_work(&team->notify_peers.dw,
- msecs_to_jiffies(team->notify_peers.interval));
-}
-
-static void team_notify_peers(struct team *team)
-{
- if (!team->notify_peers.count || !netif_running(team->dev))
- return;
- atomic_add(team->notify_peers.count, &team->notify_peers.count_pending);
- schedule_delayed_work(&team->notify_peers.dw, 0);
-}
-
-static void team_notify_peers_init(struct team *team)
-{
- INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work);
-}
-
-static void team_notify_peers_fini(struct team *team)
-{
- cancel_delayed_work_sync(&team->notify_peers.dw);
-}
-
-
-/*******************************
- * Send multicast group rejoins
- *******************************/
-
-static void team_mcast_rejoin_work(struct work_struct *work)
-{
- struct team *team;
- int val;
-
- team = container_of(work, struct team, mcast_rejoin.dw.work);
-
- if (!rtnl_trylock()) {
- schedule_delayed_work(&team->mcast_rejoin.dw, 0);
- return;
- }
- val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
- if (val < 0) {
- rtnl_unlock();
- return;
- }
- call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
- rtnl_unlock();
- if (val)
- schedule_delayed_work(&team->mcast_rejoin.dw,
- msecs_to_jiffies(team->mcast_rejoin.interval));
-}
-
-static void team_mcast_rejoin(struct team *team)
-{
- if (!team->mcast_rejoin.count || !netif_running(team->dev))
- return;
- atomic_add(team->mcast_rejoin.count, &team->mcast_rejoin.count_pending);
- schedule_delayed_work(&team->mcast_rejoin.dw, 0);
-}
-
-static void team_mcast_rejoin_init(struct team *team)
-{
- INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work);
-}
-
-static void team_mcast_rejoin_fini(struct team *team)
-{
- cancel_delayed_work_sync(&team->mcast_rejoin.dw);
-}
-
-
-/************************
- * Rx path frame handler
- ************************/
-
-/* note: already called with rcu_read_lock */
-static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
-{
- struct sk_buff *skb = *pskb;
- struct team_port *port;
- struct team *team;
- rx_handler_result_t res;
-
- skb = skb_share_check(skb, GFP_ATOMIC);
- if (!skb)
- return RX_HANDLER_CONSUMED;
-
- *pskb = skb;
-
- port = team_port_get_rcu(skb->dev);
- team = port->team;
- if (!team_port_enabled(port)) {
- if (is_link_local_ether_addr(eth_hdr(skb)->h_dest))
- /* link-local packets are mostly useful when stack receives them
- * with the link they arrive on.
- */
- return RX_HANDLER_PASS;
- /* allow exact match delivery for disabled ports */
- res = RX_HANDLER_EXACT;
- } else {
- res = team->ops.receive(team, port, skb);
- }
- if (res == RX_HANDLER_ANOTHER) {
- struct team_pcpu_stats *pcpu_stats;
-
- pcpu_stats = this_cpu_ptr(team->pcpu_stats);
- u64_stats_update_begin(&pcpu_stats->syncp);
- u64_stats_inc(&pcpu_stats->rx_packets);
- u64_stats_add(&pcpu_stats->rx_bytes, skb->len);
- if (skb->pkt_type == PACKET_MULTICAST)
- u64_stats_inc(&pcpu_stats->rx_multicast);
- u64_stats_update_end(&pcpu_stats->syncp);
-
- skb->dev = team->dev;
- } else if (res == RX_HANDLER_EXACT) {
- this_cpu_inc(team->pcpu_stats->rx_nohandler);
- } else {
- this_cpu_inc(team->pcpu_stats->rx_dropped);
- }
-
- return res;
-}
-
-
-/*************************************
- * Multiqueue Tx port select override
- *************************************/
-
-static int team_queue_override_init(struct team *team)
-{
- struct list_head *listarr;
- unsigned int queue_cnt = team->dev->num_tx_queues - 1;
- unsigned int i;
-
- if (!queue_cnt)
- return 0;
- listarr = kmalloc_array(queue_cnt, sizeof(struct list_head),
- GFP_KERNEL);
- if (!listarr)
- return -ENOMEM;
- team->qom_lists = listarr;
- for (i = 0; i < queue_cnt; i++)
- INIT_LIST_HEAD(listarr++);
- return 0;
-}
-
-static void team_queue_override_fini(struct team *team)
-{
- kfree(team->qom_lists);
-}
-
-static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
-{
- return &team->qom_lists[queue_id - 1];
-}
-
-/*
- * note: already called with rcu_read_lock
- */
-static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
-{
- struct list_head *qom_list;
- struct team_port *port;
-
- if (!team->queue_override_enabled || !skb->queue_mapping)
- return false;
- qom_list = __team_get_qom_list(team, skb->queue_mapping);
- list_for_each_entry_rcu(port, qom_list, qom_list) {
- if (!team_dev_queue_xmit(team, port, skb))
- return true;
- }
- return false;
-}
-
-static void __team_queue_override_port_del(struct team *team,
- struct team_port *port)
-{
- if (!port->queue_id)
- return;
- list_del_rcu(&port->qom_list);
-}
-
-static bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
- struct team_port *cur)
-{
- if (port->priority < cur->priority)
- return true;
- if (port->priority > cur->priority)
- return false;
- if (port->index < cur->index)
- return true;
- return false;
-}
-
-static void __team_queue_override_port_add(struct team *team,
- struct team_port *port)
-{
- struct team_port *cur;
- struct list_head *qom_list;
- struct list_head *node;
-
- if (!port->queue_id)
- return;
- qom_list = __team_get_qom_list(team, port->queue_id);
- node = qom_list;
- list_for_each_entry(cur, qom_list, qom_list) {
- if (team_queue_override_port_has_gt_prio_than(port, cur))
- break;
- node = &cur->qom_list;
- }
- list_add_tail_rcu(&port->qom_list, node);
-}
-
-static void __team_queue_override_enabled_check(struct team *team)
-{
- struct team_port *port;
- bool enabled = false;
-
- list_for_each_entry(port, &team->port_list, list) {
- if (port->queue_id) {
- enabled = true;
- break;
- }
- }
- if (enabled == team->queue_override_enabled)
- return;
- netdev_dbg(team->dev, "%s queue override\n",
- enabled ? "Enabling" : "Disabling");
- team->queue_override_enabled = enabled;
-}
-
-static void team_queue_override_port_prio_changed(struct team *team,
- struct team_port *port)
-{
- if (!port->queue_id || !team_port_enabled(port))
- return;
- __team_queue_override_port_del(team, port);
- __team_queue_override_port_add(team, port);
- __team_queue_override_enabled_check(team);
-}
-
-static void team_queue_override_port_change_queue_id(struct team *team,
- struct team_port *port,
- u16 new_queue_id)
-{
- if (team_port_enabled(port)) {
- __team_queue_override_port_del(team, port);
- port->queue_id = new_queue_id;
- __team_queue_override_port_add(team, port);
- __team_queue_override_enabled_check(team);
- } else {
- port->queue_id = new_queue_id;
- }
-}
-
-static void team_queue_override_port_add(struct team *team,
- struct team_port *port)
-{
- __team_queue_override_port_add(team, port);
- __team_queue_override_enabled_check(team);
-}
-
-static void team_queue_override_port_del(struct team *team,
- struct team_port *port)
-{
- __team_queue_override_port_del(team, port);
- __team_queue_override_enabled_check(team);
-}
-
-
-/****************
- * Port handling
- ****************/
-
-static bool team_port_find(const struct team *team,
- const struct team_port *port)
-{
- struct team_port *cur;
-
- list_for_each_entry(cur, &team->port_list, list)
- if (cur == port)
- return true;
- return false;
-}
-
-/*
- * Enable/disable port by adding to enabled port hashlist and setting
- * port->index (Might be racy so reader could see incorrect ifindex when
- * processing a flying packet, but that is not a problem). Write guarded
- * by team->lock.
- */
-static void team_port_enable(struct team *team,
- struct team_port *port)
-{
- if (team_port_enabled(port))
- return;
- port->index = team->en_port_count++;
- hlist_add_head_rcu(&port->hlist,
- team_port_index_hash(team, port->index));
- team_adjust_ops(team);
- team_queue_override_port_add(team, port);
- if (team->ops.port_enabled)
- team->ops.port_enabled(team, port);
- team_notify_peers(team);
- team_mcast_rejoin(team);
- team_lower_state_changed(port);
-}
-
-static void __reconstruct_port_hlist(struct team *team, int rm_index)
-{
- int i;
- struct team_port *port;
-
- for (i = rm_index + 1; i < team->en_port_count; i++) {
- port = team_get_port_by_index(team, i);
- hlist_del_rcu(&port->hlist);
- port->index--;
- hlist_add_head_rcu(&port->hlist,
- team_port_index_hash(team, port->index));
- }
-}
-
-static void team_port_disable(struct team *team,
- struct team_port *port)
-{
- if (!team_port_enabled(port))
- return;
- if (team->ops.port_disabled)
- team->ops.port_disabled(team, port);
- hlist_del_rcu(&port->hlist);
- __reconstruct_port_hlist(team, port->index);
- port->index = -1;
- team->en_port_count--;
- team_queue_override_port_del(team, port);
- team_adjust_ops(team);
- team_lower_state_changed(port);
-}
-
-#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_HIGHDMA | NETIF_F_LRO | \
- NETIF_F_GSO_ENCAP_ALL)
-
-#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
-
-static void __team_compute_features(struct team *team)
-{
- struct team_port *port;
- netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
- NETIF_F_ALL_FOR_ALL;
- netdev_features_t enc_features = TEAM_ENC_FEATURES;
- unsigned short max_hard_header_len = ETH_HLEN;
- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM;
-
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
- vlan_features = netdev_increment_features(vlan_features,
- port->dev->vlan_features,
- TEAM_VLAN_FEATURES);
- enc_features =
- netdev_increment_features(enc_features,
- port->dev->hw_enc_features,
- TEAM_ENC_FEATURES);
-
-
- dst_release_flag &= port->dev->priv_flags;
- if (port->dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = port->dev->hard_header_len;
- }
- rcu_read_unlock();
-
- team->dev->vlan_features = vlan_features;
- team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
- NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_STAG_TX;
- team->dev->hard_header_len = max_hard_header_len;
-
- team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
- if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
- team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
-}
-
-static void team_compute_features(struct team *team)
-{
- __team_compute_features(team);
- netdev_change_features(team->dev);
-}
-
-static int team_port_enter(struct team *team, struct team_port *port)
-{
- int err = 0;
-
- dev_hold(team->dev);
- if (team->ops.port_enter) {
- err = team->ops.port_enter(team, port);
- if (err) {
- netdev_err(team->dev, "Device %s failed to enter team mode\n",
- port->dev->name);
- goto err_port_enter;
- }
- }
-
- return 0;
-
-err_port_enter:
- dev_put(team->dev);
-
- return err;
-}
-
-static void team_port_leave(struct team *team, struct team_port *port)
-{
- if (team->ops.port_leave)
- team->ops.port_leave(team, port);
- dev_put(team->dev);
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static int __team_port_enable_netpoll(struct team_port *port)
-{
- struct netpoll *np;
- int err;
-
- np = kzalloc(sizeof(*np), GFP_KERNEL);
- if (!np)
- return -ENOMEM;
-
- err = __netpoll_setup(np, port->dev);
- if (err) {
- kfree(np);
- return err;
- }
- port->np = np;
- return err;
-}
-
-static int team_port_enable_netpoll(struct team_port *port)
-{
- if (!port->team->dev->npinfo)
- return 0;
-
- return __team_port_enable_netpoll(port);
-}
-
-static void team_port_disable_netpoll(struct team_port *port)
-{
- struct netpoll *np = port->np;
-
- if (!np)
- return;
- port->np = NULL;
-
- __netpoll_free(np);
-}
-#else
-static int team_port_enable_netpoll(struct team_port *port)
-{
- return 0;
-}
-static void team_port_disable_netpoll(struct team_port *port)
-{
-}
-#endif
-
-static int team_upper_dev_link(struct team *team, struct team_port *port,
- struct netlink_ext_ack *extack)
-{
- struct netdev_lag_upper_info lag_upper_info;
- int err;
-
- lag_upper_info.tx_type = team->mode->lag_tx_type;
- lag_upper_info.hash_type = NETDEV_LAG_HASH_UNKNOWN;
- err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
- &lag_upper_info, extack);
- if (err)
- return err;
- port->dev->priv_flags |= IFF_TEAM_PORT;
- return 0;
-}
-
-static void team_upper_dev_unlink(struct team *team, struct team_port *port)
-{
- netdev_upper_dev_unlink(port->dev, team->dev);
- port->dev->priv_flags &= ~IFF_TEAM_PORT;
-}
-
-static void __team_port_change_port_added(struct team_port *port, bool linkup);
-static int team_dev_type_check_change(struct net_device *dev,
- struct net_device *port_dev);
-
-static int team_port_add(struct team *team, struct net_device *port_dev,
- struct netlink_ext_ack *extack)
-{
- struct net_device *dev = team->dev;
- struct team_port *port;
- char *portname = port_dev->name;
- int err;
-
- if (port_dev->flags & IFF_LOOPBACK) {
- NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
- netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
- portname);
- return -EINVAL;
- }
-
- if (netif_is_team_port(port_dev)) {
- NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
- netdev_err(dev, "Device %s is already a port "
- "of a team device\n", portname);
- return -EBUSY;
- }
-
- if (dev == port_dev) {
- NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
- netdev_err(dev, "Cannot enslave team device to itself\n");
- return -EINVAL;
- }
-
- if (netdev_has_upper_dev(dev, port_dev)) {
- NL_SET_ERR_MSG(extack, "Device is already an upper device of the team interface");
- netdev_err(dev, "Device %s is already an upper device of the team interface\n",
- portname);
- return -EBUSY;
- }
-
- if (netdev_has_upper_dev(port_dev, dev)) {
- NL_SET_ERR_MSG(extack, "Device is already a lower device of the team interface");
- netdev_err(dev, "Device %s is already a lower device of the team interface\n",
- portname);
- return -EBUSY;
- }
-
- if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
- vlan_uses_dev(dev)) {
- NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
- netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
- portname);
- return -EPERM;
- }
-
- if (port_dev->flags & IFF_UP) {
- NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
- netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
- portname);
- return -EBUSY;
- }
-
- port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
- GFP_KERNEL);
- if (!port)
- return -ENOMEM;
-
- port->dev = port_dev;
- port->team = team;
- INIT_LIST_HEAD(&port->qom_list);
-
- port->orig.mtu = port_dev->mtu;
- /*
- * MTU assignment will be handled in team_dev_type_check_change
- * if dev and port_dev are of different types
- */
- if (dev->type == port_dev->type) {
- err = dev_set_mtu(port_dev, dev->mtu);
- if (err) {
- netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
- goto err_set_mtu;
- }
- }
-
- memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
-
- err = team_port_enter(team, port);
- if (err) {
- netdev_err(dev, "Device %s failed to enter team mode\n",
- portname);
- goto err_port_enter;
- }
-
- err = dev_open(port_dev, extack);
- if (err) {
- netdev_dbg(dev, "Device %s opening failed\n",
- portname);
- goto err_dev_open;
- }
-
- err = vlan_vids_add_by_dev(port_dev, dev);
- if (err) {
- netdev_err(dev, "Failed to add vlan ids to device %s\n",
- portname);
- goto err_vids_add;
- }
-
- err = team_port_enable_netpoll(port);
- if (err) {
- netdev_err(dev, "Failed to enable netpoll on device %s\n",
- portname);
- goto err_enable_netpoll;
- }
-
- if (!(dev->features & NETIF_F_LRO))
- dev_disable_lro(port_dev);
-
- err = netdev_rx_handler_register(port_dev, team_handle_frame,
- port);
- if (err) {
- netdev_err(dev, "Device %s failed to register rx_handler\n",
- portname);
- goto err_handler_register;
- }
-
- err = team_upper_dev_link(team, port, extack);
- if (err) {
- netdev_err(dev, "Device %s failed to set upper link\n",
- portname);
- goto err_set_upper_link;
- }
-
- err = __team_option_inst_add_port(team, port);
- if (err) {
- netdev_err(dev, "Device %s failed to add per-port options\n",
- portname);
- goto err_option_port_add;
- }
-
- /* set promiscuity level to new slave */
- if (dev->flags & IFF_PROMISC) {
- err = dev_set_promiscuity(port_dev, 1);
- if (err)
- goto err_set_slave_promisc;
- }
-
- /* set allmulti level to new slave */
- if (dev->flags & IFF_ALLMULTI) {
- err = dev_set_allmulti(port_dev, 1);
- if (err) {
- if (dev->flags & IFF_PROMISC)
- dev_set_promiscuity(port_dev, -1);
- goto err_set_slave_promisc;
- }
- }
-
- err = team_dev_type_check_change(dev, port_dev);
- if (err)
- goto err_set_dev_type;
-
- if (dev->flags & IFF_UP) {
- netif_addr_lock_bh(dev);
- dev_uc_sync_multiple(port_dev, dev);
- dev_mc_sync_multiple(port_dev, dev);
- netif_addr_unlock_bh(dev);
- }
-
- port->index = -1;
- list_add_tail_rcu(&port->list, &team->port_list);
- team_port_enable(team, port);
- __team_compute_features(team);
- __team_port_change_port_added(port, !!netif_oper_up(port_dev));
- __team_options_change_check(team);
-
- netdev_info(dev, "Port device %s added\n", portname);
-
- return 0;
-
-err_set_dev_type:
-err_set_slave_promisc:
- __team_option_inst_del_port(team, port);
-
-err_option_port_add:
- team_upper_dev_unlink(team, port);
-
-err_set_upper_link:
- netdev_rx_handler_unregister(port_dev);
-
-err_handler_register:
- team_port_disable_netpoll(port);
-
-err_enable_netpoll:
- vlan_vids_del_by_dev(port_dev, dev);
-
-err_vids_add:
- dev_close(port_dev);
-
-err_dev_open:
- team_port_leave(team, port);
- team_port_set_orig_dev_addr(port);
-
-err_port_enter:
- dev_set_mtu(port_dev, port->orig.mtu);
-
-err_set_mtu:
- kfree(port);
-
- return err;
-}
-
-static void __team_port_change_port_removed(struct team_port *port);
-
-static int team_port_del(struct team *team, struct net_device *port_dev)
-{
- struct net_device *dev = team->dev;
- struct team_port *port;
- char *portname = port_dev->name;
-
- port = team_port_get_rtnl(port_dev);
- if (!port || !team_port_find(team, port)) {
- netdev_err(dev, "Device %s does not act as a port of this team\n",
- portname);
- return -ENOENT;
- }
-
- team_port_disable(team, port);
- list_del_rcu(&port->list);
-
- if (dev->flags & IFF_PROMISC)
- dev_set_promiscuity(port_dev, -1);
- if (dev->flags & IFF_ALLMULTI)
- dev_set_allmulti(port_dev, -1);
-
- team_upper_dev_unlink(team, port);
- netdev_rx_handler_unregister(port_dev);
- team_port_disable_netpoll(port);
- vlan_vids_del_by_dev(port_dev, dev);
- if (dev->flags & IFF_UP) {
- dev_uc_unsync(port_dev, dev);
- dev_mc_unsync(port_dev, dev);
- }
- dev_close(port_dev);
- team_port_leave(team, port);
-
- __team_option_inst_mark_removed_port(team, port);
- __team_options_change_check(team);
- __team_option_inst_del_port(team, port);
- __team_port_change_port_removed(port);
-
- team_port_set_orig_dev_addr(port);
- dev_set_mtu(port_dev, port->orig.mtu);
- kfree_rcu(port, rcu);
- netdev_info(dev, "Port device %s removed\n", portname);
- __team_compute_features(team);
-
- return 0;
-}
-
-
-/*****************
- * Net device ops
- *****************/
-
-static void team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
-{
- ctx->data.str_val = team->mode->kind;
-}
-
-static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
-{
- return team_change_mode(team, ctx->data.str_val);
-}
-
-static void team_notify_peers_count_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- ctx->data.u32_val = team->notify_peers.count;
-}
-
-static int team_notify_peers_count_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- team->notify_peers.count = ctx->data.u32_val;
- return 0;
-}
-
-static void team_notify_peers_interval_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- ctx->data.u32_val = team->notify_peers.interval;
-}
-
-static int team_notify_peers_interval_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- team->notify_peers.interval = ctx->data.u32_val;
- return 0;
-}
-
-static void team_mcast_rejoin_count_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- ctx->data.u32_val = team->mcast_rejoin.count;
-}
-
-static int team_mcast_rejoin_count_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- team->mcast_rejoin.count = ctx->data.u32_val;
- return 0;
-}
-
-static void team_mcast_rejoin_interval_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- ctx->data.u32_val = team->mcast_rejoin.interval;
-}
-
-static int team_mcast_rejoin_interval_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- team->mcast_rejoin.interval = ctx->data.u32_val;
- return 0;
-}
-
-static void team_port_en_option_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- ctx->data.bool_val = team_port_enabled(port);
-}
-
-static int team_port_en_option_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- if (ctx->data.bool_val)
- team_port_enable(team, port);
- else
- team_port_disable(team, port);
- return 0;
-}
-
-static void team_user_linkup_option_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- ctx->data.bool_val = port->user.linkup;
-}
-
-static void __team_carrier_check(struct team *team);
-
-static int team_user_linkup_option_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- port->user.linkup = ctx->data.bool_val;
- team_refresh_port_linkup(port);
- __team_carrier_check(port->team);
- return 0;
-}
-
-static void team_user_linkup_en_option_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- ctx->data.bool_val = port->user.linkup_enabled;
-}
-
-static int team_user_linkup_en_option_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- port->user.linkup_enabled = ctx->data.bool_val;
- team_refresh_port_linkup(port);
- __team_carrier_check(port->team);
- return 0;
-}
-
-static void team_priority_option_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- ctx->data.s32_val = port->priority;
-}
-
-static int team_priority_option_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
- s32 priority = ctx->data.s32_val;
-
- if (port->priority == priority)
- return 0;
- port->priority = priority;
- team_queue_override_port_prio_changed(team, port);
- return 0;
-}
-
-static void team_queue_id_option_get(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
-
- ctx->data.u32_val = port->queue_id;
-}
-
-static int team_queue_id_option_set(struct team *team,
- struct team_gsetter_ctx *ctx)
-{
- struct team_port *port = ctx->info->port;
- u16 new_queue_id = ctx->data.u32_val;
-
- if (port->queue_id == new_queue_id)
- return 0;
- if (new_queue_id >= team->dev->real_num_tx_queues)
- return -EINVAL;
- team_queue_override_port_change_queue_id(team, port, new_queue_id);
- return 0;
-}
-
-static const struct team_option team_options[] = {
- {
- .name = "mode",
- .type = TEAM_OPTION_TYPE_STRING,
- .getter = team_mode_option_get,
- .setter = team_mode_option_set,
- },
- {
- .name = "notify_peers_count",
- .type = TEAM_OPTION_TYPE_U32,
- .getter = team_notify_peers_count_get,
- .setter = team_notify_peers_count_set,
- },
- {
- .name = "notify_peers_interval",
- .type = TEAM_OPTION_TYPE_U32,
- .getter = team_notify_peers_interval_get,
- .setter = team_notify_peers_interval_set,
- },
- {
- .name = "mcast_rejoin_count",
- .type = TEAM_OPTION_TYPE_U32,
- .getter = team_mcast_rejoin_count_get,
- .setter = team_mcast_rejoin_count_set,
- },
- {
- .name = "mcast_rejoin_interval",
- .type = TEAM_OPTION_TYPE_U32,
- .getter = team_mcast_rejoin_interval_get,
- .setter = team_mcast_rejoin_interval_set,
- },
- {
- .name = "enabled",
- .type = TEAM_OPTION_TYPE_BOOL,
- .per_port = true,
- .getter = team_port_en_option_get,
- .setter = team_port_en_option_set,
- },
- {
- .name = "user_linkup",
- .type = TEAM_OPTION_TYPE_BOOL,
- .per_port = true,
- .getter = team_user_linkup_option_get,
- .setter = team_user_linkup_option_set,
- },
- {
- .name = "user_linkup_enabled",
- .type = TEAM_OPTION_TYPE_BOOL,
- .per_port = true,
- .getter = team_user_linkup_en_option_get,
- .setter = team_user_linkup_en_option_set,
- },
- {
- .name = "priority",
- .type = TEAM_OPTION_TYPE_S32,
- .per_port = true,
- .getter = team_priority_option_get,
- .setter = team_priority_option_set,
- },
- {
- .name = "queue_id",
- .type = TEAM_OPTION_TYPE_U32,
- .per_port = true,
- .getter = team_queue_id_option_get,
- .setter = team_queue_id_option_set,
- },
-};
-
-
-static int team_init(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
- int i;
- int err;
-
- team->dev = dev;
- team_set_no_mode(team);
- team->notifier_ctx = false;
-
- team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
- if (!team->pcpu_stats)
- return -ENOMEM;
-
- for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
- INIT_HLIST_HEAD(&team->en_port_hlist[i]);
- INIT_LIST_HEAD(&team->port_list);
- err = team_queue_override_init(team);
- if (err)
- goto err_team_queue_override_init;
-
- team_adjust_ops(team);
-
- INIT_LIST_HEAD(&team->option_list);
- INIT_LIST_HEAD(&team->option_inst_list);
-
- team_notify_peers_init(team);
- team_mcast_rejoin_init(team);
-
- err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
- if (err)
- goto err_options_register;
- netif_carrier_off(dev);
-
- lockdep_register_key(&team->team_lock_key);
- __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
- netdev_lockdep_set_classes(dev);
-
- return 0;
-
-err_options_register:
- team_mcast_rejoin_fini(team);
- team_notify_peers_fini(team);
- team_queue_override_fini(team);
-err_team_queue_override_init:
- free_percpu(team->pcpu_stats);
-
- return err;
-}
-
-static void team_uninit(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
- struct team_port *tmp;
-
- mutex_lock(&team->lock);
- list_for_each_entry_safe(port, tmp, &team->port_list, list)
- team_port_del(team, port->dev);
-
- __team_change_mode(team, NULL); /* cleanup */
- __team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
- team_mcast_rejoin_fini(team);
- team_notify_peers_fini(team);
- team_queue_override_fini(team);
- mutex_unlock(&team->lock);
- netdev_change_features(dev);
- lockdep_unregister_key(&team->team_lock_key);
-}
-
-static void team_destructor(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
-
- free_percpu(team->pcpu_stats);
-}
-
-static int team_open(struct net_device *dev)
-{
- return 0;
-}
-
-static int team_close(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
-
- list_for_each_entry(port, &team->port_list, list) {
- dev_uc_unsync(port->dev, dev);
- dev_mc_unsync(port->dev, dev);
- }
-
- return 0;
-}
-
-/*
- * note: already called with rcu_read_lock
- */
-static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
- bool tx_success;
- unsigned int len = skb->len;
-
- tx_success = team_queue_override_transmit(team, skb);
- if (!tx_success)
- tx_success = team->ops.transmit(team, skb);
- if (tx_success) {
- struct team_pcpu_stats *pcpu_stats;
-
- pcpu_stats = this_cpu_ptr(team->pcpu_stats);
- u64_stats_update_begin(&pcpu_stats->syncp);
- u64_stats_inc(&pcpu_stats->tx_packets);
- u64_stats_add(&pcpu_stats->tx_bytes, len);
- u64_stats_update_end(&pcpu_stats->syncp);
- } else {
- this_cpu_inc(team->pcpu_stats->tx_dropped);
- }
-
- return NETDEV_TX_OK;
-}
-
-static u16 team_select_queue(struct net_device *dev, struct sk_buff *skb,
- struct net_device *sb_dev)
-{
- /*
- * This helper function exists to help dev_pick_tx get the correct
- * destination queue. Using a helper function skips a call to
- * skb_tx_hash and will put the skbs in the queue we expect on their
- * way down to the team driver.
- */
- u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
-
- /*
- * Save the original txq to restore before passing to the driver
- */
- qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
-
- if (unlikely(txq >= dev->real_num_tx_queues)) {
- do {
- txq -= dev->real_num_tx_queues;
- } while (txq >= dev->real_num_tx_queues);
- }
- return txq;
-}
-
-static void team_change_rx_flags(struct net_device *dev, int change)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
- int inc;
-
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
- if (change & IFF_PROMISC) {
- inc = dev->flags & IFF_PROMISC ? 1 : -1;
- dev_set_promiscuity(port->dev, inc);
- }
- if (change & IFF_ALLMULTI) {
- inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
- dev_set_allmulti(port->dev, inc);
- }
- }
- rcu_read_unlock();
-}
-
-static void team_set_rx_mode(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
-
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
- dev_uc_sync_multiple(port->dev, dev);
- dev_mc_sync_multiple(port->dev, dev);
- }
- rcu_read_unlock();
-}
-
-static int team_set_mac_address(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
- struct team *team = netdev_priv(dev);
- struct team_port *port;
-
- if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
- dev_addr_set(dev, addr->sa_data);
- mutex_lock(&team->lock);
- list_for_each_entry(port, &team->port_list, list)
- if (team->ops.port_change_dev_addr)
- team->ops.port_change_dev_addr(team, port);
- mutex_unlock(&team->lock);
- return 0;
-}
-
-static int team_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
- int err;
-
- /*
- * Alhough this is reader, it's guarded by team lock. It's not possible
- * to traverse list in reverse under rcu_read_lock
- */
- mutex_lock(&team->lock);
- team->port_mtu_change_allowed = true;
- list_for_each_entry(port, &team->port_list, list) {
- err = dev_set_mtu(port->dev, new_mtu);
- if (err) {
- netdev_err(dev, "Device %s failed to change mtu",
- port->dev->name);
- goto unwind;
- }
- }
- team->port_mtu_change_allowed = false;
- mutex_unlock(&team->lock);
-
- dev->mtu = new_mtu;
-
- return 0;
-
-unwind:
- list_for_each_entry_continue_reverse(port, &team->port_list, list)
- dev_set_mtu(port->dev, dev->mtu);
- team->port_mtu_change_allowed = false;
- mutex_unlock(&team->lock);
-
- return err;
-}
-
-static void
-team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
-{
- struct team *team = netdev_priv(dev);
- struct team_pcpu_stats *p;
- u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
- u32 rx_dropped = 0, tx_dropped = 0, rx_nohandler = 0;
- unsigned int start;
- int i;
-
- for_each_possible_cpu(i) {
- p = per_cpu_ptr(team->pcpu_stats, i);
- do {
- start = u64_stats_fetch_begin(&p->syncp);
- rx_packets = u64_stats_read(&p->rx_packets);
- rx_bytes = u64_stats_read(&p->rx_bytes);
- rx_multicast = u64_stats_read(&p->rx_multicast);
- tx_packets = u64_stats_read(&p->tx_packets);
- tx_bytes = u64_stats_read(&p->tx_bytes);
- } while (u64_stats_fetch_retry(&p->syncp, start));
-
- stats->rx_packets += rx_packets;
- stats->rx_bytes += rx_bytes;
- stats->multicast += rx_multicast;
- stats->tx_packets += tx_packets;
- stats->tx_bytes += tx_bytes;
- /*
- * rx_dropped, tx_dropped & rx_nohandler are u32,
- * updated without syncp protection.
- */
- rx_dropped += READ_ONCE(p->rx_dropped);
- tx_dropped += READ_ONCE(p->tx_dropped);
- rx_nohandler += READ_ONCE(p->rx_nohandler);
- }
- stats->rx_dropped = rx_dropped;
- stats->tx_dropped = tx_dropped;
- stats->rx_nohandler = rx_nohandler;
-}
-
-static int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
- int err;
-
- /*
- * Alhough this is reader, it's guarded by team lock. It's not possible
- * to traverse list in reverse under rcu_read_lock
- */
- mutex_lock(&team->lock);
- list_for_each_entry(port, &team->port_list, list) {
- err = vlan_vid_add(port->dev, proto, vid);
- if (err)
- goto unwind;
- }
- mutex_unlock(&team->lock);
-
- return 0;
-
-unwind:
- list_for_each_entry_continue_reverse(port, &team->port_list, list)
- vlan_vid_del(port->dev, proto, vid);
- mutex_unlock(&team->lock);
-
- return err;
-}
-
-static int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
-
- mutex_lock(&team->lock);
- list_for_each_entry(port, &team->port_list, list)
- vlan_vid_del(port->dev, proto, vid);
- mutex_unlock(&team->lock);
-
- return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void team_poll_controller(struct net_device *dev)
-{
-}
-
-static void __team_netpoll_cleanup(struct team *team)
-{
- struct team_port *port;
-
- list_for_each_entry(port, &team->port_list, list)
- team_port_disable_netpoll(port);
-}
-
-static void team_netpoll_cleanup(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
-
- mutex_lock(&team->lock);
- __team_netpoll_cleanup(team);
- mutex_unlock(&team->lock);
-}
-
-static int team_netpoll_setup(struct net_device *dev,
- struct netpoll_info *npifo)
-{
- struct team *team = netdev_priv(dev);
- struct team_port *port;
- int err = 0;
-
- mutex_lock(&team->lock);
- list_for_each_entry(port, &team->port_list, list) {
- err = __team_port_enable_netpoll(port);
- if (err) {
- __team_netpoll_cleanup(team);
- break;
- }
- }
- mutex_unlock(&team->lock);
- return err;
-}
-#endif
-
-static int team_add_slave(struct net_device *dev, struct net_device *port_dev,
- struct netlink_ext_ack *extack)
-{
- struct team *team = netdev_priv(dev);
- int err;
-
- mutex_lock(&team->lock);
- err = team_port_add(team, port_dev, extack);
- mutex_unlock(&team->lock);
-
- if (!err)
- netdev_change_features(dev);
-
- return err;
-}
-
-static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
-{
- struct team *team = netdev_priv(dev);
- int err;
-
- mutex_lock(&team->lock);
- err = team_port_del(team, port_dev);
- mutex_unlock(&team->lock);
-
- if (err)
- return err;
-
- if (netif_is_team_master(port_dev)) {
- lockdep_unregister_key(&team->team_lock_key);
- lockdep_register_key(&team->team_lock_key);
- lockdep_set_class(&team->lock, &team->team_lock_key);
- }
- netdev_change_features(dev);
-
- return err;
-}
-
-static netdev_features_t team_fix_features(struct net_device *dev,
- netdev_features_t features)
-{
- struct team_port *port;
- struct team *team = netdev_priv(dev);
- netdev_features_t mask;
-
- mask = features;
- features &= ~NETIF_F_ONE_FOR_ALL;
- features |= NETIF_F_ALL_FOR_ALL;
-
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
- features = netdev_increment_features(features,
- port->dev->features,
- mask);
- }
- rcu_read_unlock();
-
- features = netdev_add_tso_features(features, mask);
-
- return features;
-}
-
-static int team_change_carrier(struct net_device *dev, bool new_carrier)
-{
- struct team *team = netdev_priv(dev);
-
- team->user_carrier_enabled = true;
-
- if (new_carrier)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
- return 0;
-}
-
-static const struct net_device_ops team_netdev_ops = {
- .ndo_init = team_init,
- .ndo_uninit = team_uninit,
- .ndo_open = team_open,
- .ndo_stop = team_close,
- .ndo_start_xmit = team_xmit,
- .ndo_select_queue = team_select_queue,
- .ndo_change_rx_flags = team_change_rx_flags,
- .ndo_set_rx_mode = team_set_rx_mode,
- .ndo_set_mac_address = team_set_mac_address,
- .ndo_change_mtu = team_change_mtu,
- .ndo_get_stats64 = team_get_stats64,
- .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = team_poll_controller,
- .ndo_netpoll_setup = team_netpoll_setup,
- .ndo_netpoll_cleanup = team_netpoll_cleanup,
-#endif
- .ndo_add_slave = team_add_slave,
- .ndo_del_slave = team_del_slave,
- .ndo_fix_features = team_fix_features,
- .ndo_change_carrier = team_change_carrier,
- .ndo_features_check = passthru_features_check,
-};
-
-/***********************
- * ethtool interface
- ***********************/
-
-static void team_ethtool_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *drvinfo)
-{
- strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
- strscpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
-}
-
-static int team_ethtool_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct team *team= netdev_priv(dev);
- unsigned long speed = 0;
- struct team_port *port;
-
- cmd->base.duplex = DUPLEX_UNKNOWN;
- cmd->base.port = PORT_OTHER;
-
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list) {
- if (team_port_txable(port)) {
- if (port->state.speed != SPEED_UNKNOWN)
- speed += port->state.speed;
- if (cmd->base.duplex == DUPLEX_UNKNOWN &&
- port->state.duplex != DUPLEX_UNKNOWN)
- cmd->base.duplex = port->state.duplex;
- }
- }
- rcu_read_unlock();
-
- cmd->base.speed = speed ? : SPEED_UNKNOWN;
-
- return 0;
-}
-
-static const struct ethtool_ops team_ethtool_ops = {
- .get_drvinfo = team_ethtool_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_link_ksettings = team_ethtool_get_link_ksettings,
-};
-
-/***********************
- * rt netlink interface
- ***********************/
-
-static void team_setup_by_port(struct net_device *dev,
- struct net_device *port_dev)
-{
- struct team *team = netdev_priv(dev);
-
- if (port_dev->type == ARPHRD_ETHER)
- dev->header_ops = team->header_ops_cache;
- else
- dev->header_ops = port_dev->header_ops;
- dev->type = port_dev->type;
- dev->hard_header_len = port_dev->hard_header_len;
- dev->needed_headroom = port_dev->needed_headroom;
- dev->addr_len = port_dev->addr_len;
- dev->mtu = port_dev->mtu;
- memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len);
- eth_hw_addr_inherit(dev, port_dev);
-
- if (port_dev->flags & IFF_POINTOPOINT) {
- dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
- dev->flags |= (IFF_POINTOPOINT | IFF_NOARP);
- } else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) ==
- (IFF_BROADCAST | IFF_MULTICAST)) {
- dev->flags |= (IFF_BROADCAST | IFF_MULTICAST);
- dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP);
- }
-}
-
-static int team_dev_type_check_change(struct net_device *dev,
- struct net_device *port_dev)
-{
- struct team *team = netdev_priv(dev);
- char *portname = port_dev->name;
- int err;
-
- if (dev->type == port_dev->type)
- return 0;
- if (!list_empty(&team->port_list)) {
- netdev_err(dev, "Device %s is of different type\n", portname);
- return -EBUSY;
- }
- err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
- err = notifier_to_errno(err);
- if (err) {
- netdev_err(dev, "Refused to change device type\n");
- return err;
- }
- dev_uc_flush(dev);
- dev_mc_flush(dev);
- team_setup_by_port(dev, port_dev);
- call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
- return 0;
-}
-
-static void team_setup(struct net_device *dev)
-{
- struct team *team = netdev_priv(dev);
-
- ether_setup(dev);
- dev->max_mtu = ETH_MAX_MTU;
- team->header_ops_cache = dev->header_ops;
-
- dev->netdev_ops = &team_netdev_ops;
- dev->ethtool_ops = &team_ethtool_ops;
- dev->needs_free_netdev = true;
- dev->priv_destructor = team_destructor;
- dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
- dev->priv_flags |= IFF_NO_QUEUE;
- dev->priv_flags |= IFF_TEAM;
-
- /*
- * Indicate we support unicast address filtering. That way core won't
- * bring us to promisc mode in case a unicast addr is added.
- * Let this up to underlay drivers.
- */
- dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
-
- dev->features |= NETIF_F_LLTX;
- dev->features |= NETIF_F_GRO;
-
- /* Don't allow team devices to change network namespaces. */
- dev->features |= NETIF_F_NETNS_LOCAL;
-
- dev->hw_features = TEAM_VLAN_FEATURES |
- NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER |
- NETIF_F_HW_VLAN_STAG_RX |
- NETIF_F_HW_VLAN_STAG_FILTER;
-
- dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
- dev->features |= dev->hw_features;
- dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
-}
-
-static int team_newlink(struct net *src_net, struct net_device *dev,
- struct nlattr *tb[], struct nlattr *data[],
- struct netlink_ext_ack *extack)
-{
- if (tb[IFLA_ADDRESS] == NULL)
- eth_hw_addr_random(dev);
-
- return register_netdevice(dev);
-}
-
-static int team_validate(struct nlattr *tb[], struct nlattr *data[],
- struct netlink_ext_ack *extack)
-{
- if (tb[IFLA_ADDRESS]) {
- if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
- return -EINVAL;
- if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
- return -EADDRNOTAVAIL;
- }
- return 0;
-}
-
-static unsigned int team_get_num_tx_queues(void)
-{
- return TEAM_DEFAULT_NUM_TX_QUEUES;
-}
-
-static unsigned int team_get_num_rx_queues(void)
-{
- return TEAM_DEFAULT_NUM_RX_QUEUES;
-}
-
-static struct rtnl_link_ops team_link_ops __read_mostly = {
- .kind = DRV_NAME,
- .priv_size = sizeof(struct team),
- .setup = team_setup,
- .newlink = team_newlink,
- .validate = team_validate,
- .get_num_tx_queues = team_get_num_tx_queues,
- .get_num_rx_queues = team_get_num_rx_queues,
-};
-
-
-/***********************************
- * Generic netlink custom interface
- ***********************************/
-
-static struct genl_family team_nl_family;
-
-static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
- [TEAM_ATTR_UNSPEC] = { .type = NLA_UNSPEC, },
- [TEAM_ATTR_TEAM_IFINDEX] = { .type = NLA_U32 },
- [TEAM_ATTR_LIST_OPTION] = { .type = NLA_NESTED },
- [TEAM_ATTR_LIST_PORT] = { .type = NLA_NESTED },
-};
-
-static const struct nla_policy
-team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
- [TEAM_ATTR_OPTION_UNSPEC] = { .type = NLA_UNSPEC, },
- [TEAM_ATTR_OPTION_NAME] = {
- .type = NLA_STRING,
- .len = TEAM_STRING_MAX_LEN,
- },
- [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG },
- [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
- [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY },
- [TEAM_ATTR_OPTION_PORT_IFINDEX] = { .type = NLA_U32 },
- [TEAM_ATTR_OPTION_ARRAY_INDEX] = { .type = NLA_U32 },
-};
-
-static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
-{
- struct sk_buff *msg;
- void *hdr;
- int err;
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
- &team_nl_family, 0, TEAM_CMD_NOOP);
- if (!hdr) {
- err = -EMSGSIZE;
- goto err_msg_put;
- }
-
- genlmsg_end(msg, hdr);
-
- return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
-
-err_msg_put:
- nlmsg_free(msg);
-
- return err;
-}
-
-/*
- * Netlink cmd functions should be locked by following two functions.
- * Since dev gets held here, that ensures dev won't disappear in between.
- */
-static struct team *team_nl_team_get(struct genl_info *info)
-{
- struct net *net = genl_info_net(info);
- int ifindex;
- struct net_device *dev;
- struct team *team;
-
- if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
- return NULL;
-
- ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
- dev = dev_get_by_index(net, ifindex);
- if (!dev || dev->netdev_ops != &team_netdev_ops) {
- dev_put(dev);
- return NULL;
- }
-
- team = netdev_priv(dev);
- mutex_lock(&team->lock);
- return team;
-}
-
-static void team_nl_team_put(struct team *team)
-{
- mutex_unlock(&team->lock);
- dev_put(team->dev);
-}
-
-typedef int team_nl_send_func_t(struct sk_buff *skb,
- struct team *team, u32 portid);
-
-static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid)
-{
- return genlmsg_unicast(dev_net(team->dev), skb, portid);
-}
-
-static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
- struct team_option_inst *opt_inst)
-{
- struct nlattr *option_item;
- struct team_option *option = opt_inst->option;
- struct team_option_inst_info *opt_inst_info = &opt_inst->info;
- struct team_gsetter_ctx ctx;
- int err;
-
- ctx.info = opt_inst_info;
- err = team_option_get(team, opt_inst, &ctx);
- if (err)
- return err;
-
- option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION);
- if (!option_item)
- return -EMSGSIZE;
-
- if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
- goto nest_cancel;
- if (opt_inst_info->port &&
- nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
- opt_inst_info->port->dev->ifindex))
- goto nest_cancel;
- if (opt_inst->option->array_size &&
- nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
- opt_inst_info->array_index))
- goto nest_cancel;
-
- switch (option->type) {
- case TEAM_OPTION_TYPE_U32:
- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
- goto nest_cancel;
- if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
- goto nest_cancel;
- break;
- case TEAM_OPTION_TYPE_STRING:
- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
- goto nest_cancel;
- if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
- ctx.data.str_val))
- goto nest_cancel;
- break;
- case TEAM_OPTION_TYPE_BINARY:
- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
- goto nest_cancel;
- if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
- ctx.data.bin_val.ptr))
- goto nest_cancel;
- break;
- case TEAM_OPTION_TYPE_BOOL:
- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
- goto nest_cancel;
- if (ctx.data.bool_val &&
- nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
- goto nest_cancel;
- break;
- case TEAM_OPTION_TYPE_S32:
- if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
- goto nest_cancel;
- if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
- goto nest_cancel;
- break;
- default:
- BUG();
- }
- if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
- goto nest_cancel;
- if (opt_inst->changed) {
- if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
- goto nest_cancel;
- opt_inst->changed = false;
- }
- nla_nest_end(skb, option_item);
- return 0;
-
-nest_cancel:
- nla_nest_cancel(skb, option_item);
- return -EMSGSIZE;
-}
-
-static int __send_and_alloc_skb(struct sk_buff **pskb,
- struct team *team, u32 portid,
- team_nl_send_func_t *send_func)
-{
- int err;
-
- if (*pskb) {
- err = send_func(*pskb, team, portid);
- if (err)
- return err;
- }
- *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!*pskb)
- return -ENOMEM;
- return 0;
-}
-
-static int team_nl_send_options_get(struct team *team, u32 portid, u32 seq,
- int flags, team_nl_send_func_t *send_func,
- struct list_head *sel_opt_inst_list)
-{
- struct nlattr *option_list;
- struct nlmsghdr *nlh;
- void *hdr;
- struct team_option_inst *opt_inst;
- int err;
- struct sk_buff *skb = NULL;
- bool incomplete;
- int i;
-
- opt_inst = list_first_entry(sel_opt_inst_list,
- struct team_option_inst, tmp_list);
-
-start_again:
- err = __send_and_alloc_skb(&skb, team, portid, send_func);
- if (err)
- return err;
-
- hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
- TEAM_CMD_OPTIONS_GET);
- if (!hdr) {
- nlmsg_free(skb);
- return -EMSGSIZE;
- }
-
- if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
- goto nla_put_failure;
- option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION);
- if (!option_list)
- goto nla_put_failure;
-
- i = 0;
- incomplete = false;
- list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
- err = team_nl_fill_one_option_get(skb, team, opt_inst);
- if (err) {
- if (err == -EMSGSIZE) {
- if (!i)
- goto errout;
- incomplete = true;
- break;
- }
- goto errout;
- }
- i++;
- }
-
- nla_nest_end(skb, option_list);
- genlmsg_end(skb, hdr);
- if (incomplete)
- goto start_again;
-
-send_done:
- nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
- if (!nlh) {
- err = __send_and_alloc_skb(&skb, team, portid, send_func);
- if (err)
- return err;
- goto send_done;
- }
-
- return send_func(skb, team, portid);
-
-nla_put_failure:
- err = -EMSGSIZE;
-errout:
- nlmsg_free(skb);
- return err;
-}
-
-static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
-{
- struct team *team;
- struct team_option_inst *opt_inst;
- int err;
- LIST_HEAD(sel_opt_inst_list);
-
- team = team_nl_team_get(info);
- if (!team)
- return -EINVAL;
-
- list_for_each_entry(opt_inst, &team->option_inst_list, list)
- list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
- err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq,
- NLM_F_ACK, team_nl_send_unicast,
- &sel_opt_inst_list);
-
- team_nl_team_put(team);
-
- return err;
-}
-
-static int team_nl_send_event_options_get(struct team *team,
- struct list_head *sel_opt_inst_list);
-
-static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
-{
- struct team *team;
- int err = 0;
- int i;
- struct nlattr *nl_option;
-
- rtnl_lock();
-
- team = team_nl_team_get(info);
- if (!team) {
- err = -EINVAL;
- goto rtnl_unlock;
- }
-
- err = -EINVAL;
- if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
- err = -EINVAL;
- goto team_put;
- }
-
- nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
- struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
- struct nlattr *attr;
- struct nlattr *attr_data;
- LIST_HEAD(opt_inst_list);
- enum team_option_type opt_type;
- int opt_port_ifindex = 0; /* != 0 for per-port options */
- u32 opt_array_index = 0;
- bool opt_is_array = false;
- struct team_option_inst *opt_inst;
- char *opt_name;
- bool opt_found = false;
-
- if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
- err = -EINVAL;
- goto team_put;
- }
- err = nla_parse_nested_deprecated(opt_attrs,
- TEAM_ATTR_OPTION_MAX,
- nl_option,
- team_nl_option_policy,
- info->extack);
- if (err)
- goto team_put;
- if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
- !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
- err = -EINVAL;
- goto team_put;
- }
- switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
- case NLA_U32:
- opt_type = TEAM_OPTION_TYPE_U32;
- break;
- case NLA_STRING:
- opt_type = TEAM_OPTION_TYPE_STRING;
- break;
- case NLA_BINARY:
- opt_type = TEAM_OPTION_TYPE_BINARY;
- break;
- case NLA_FLAG:
- opt_type = TEAM_OPTION_TYPE_BOOL;
- break;
- case NLA_S32:
- opt_type = TEAM_OPTION_TYPE_S32;
- break;
- default:
- goto team_put;
- }
-
- attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
- if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
- err = -EINVAL;
- goto team_put;
- }
-
- opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
- attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
- if (attr)
- opt_port_ifindex = nla_get_u32(attr);
-
- attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
- if (attr) {
- opt_is_array = true;
- opt_array_index = nla_get_u32(attr);
- }
-
- list_for_each_entry(opt_inst, &team->option_inst_list, list) {
- struct team_option *option = opt_inst->option;
- struct team_gsetter_ctx ctx;
- struct team_option_inst_info *opt_inst_info;
- int tmp_ifindex;
-
- opt_inst_info = &opt_inst->info;
- tmp_ifindex = opt_inst_info->port ?
- opt_inst_info->port->dev->ifindex : 0;
- if (option->type != opt_type ||
- strcmp(option->name, opt_name) ||
- tmp_ifindex != opt_port_ifindex ||
- (option->array_size && !opt_is_array) ||
- opt_inst_info->array_index != opt_array_index)
- continue;
- opt_found = true;
- ctx.info = opt_inst_info;
- switch (opt_type) {
- case TEAM_OPTION_TYPE_U32:
- ctx.data.u32_val = nla_get_u32(attr_data);
- break;
- case TEAM_OPTION_TYPE_STRING:
- if (nla_len(attr_data) > TEAM_STRING_MAX_LEN ||
- !memchr(nla_data(attr_data), '\0',
- nla_len(attr_data))) {
- err = -EINVAL;
- goto team_put;
- }
- ctx.data.str_val = nla_data(attr_data);
- break;
- case TEAM_OPTION_TYPE_BINARY:
- ctx.data.bin_val.len = nla_len(attr_data);
- ctx.data.bin_val.ptr = nla_data(attr_data);
- break;
- case TEAM_OPTION_TYPE_BOOL:
- ctx.data.bool_val = attr_data ? true : false;
- break;
- case TEAM_OPTION_TYPE_S32:
- ctx.data.s32_val = nla_get_s32(attr_data);
- break;
- default:
- BUG();
- }
- err = team_option_set(team, opt_inst, &ctx);
- if (err)
- goto team_put;
- opt_inst->changed = true;
- list_add(&opt_inst->tmp_list, &opt_inst_list);
- }
- if (!opt_found) {
- err = -ENOENT;
- goto team_put;
- }
-
- err = team_nl_send_event_options_get(team, &opt_inst_list);
- if (err)
- break;
- }
-
-team_put:
- team_nl_team_put(team);
-rtnl_unlock:
- rtnl_unlock();
- return err;
-}
-
-static int team_nl_fill_one_port_get(struct sk_buff *skb,
- struct team_port *port)
-{
- struct nlattr *port_item;
-
- port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT);
- if (!port_item)
- goto nest_cancel;
- if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
- goto nest_cancel;
- if (port->changed) {
- if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
- goto nest_cancel;
- port->changed = false;
- }
- if ((port->removed &&
- nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
- (port->state.linkup &&
- nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
- nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
- nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
- goto nest_cancel;
- nla_nest_end(skb, port_item);
- return 0;
-
-nest_cancel:
- nla_nest_cancel(skb, port_item);
- return -EMSGSIZE;
-}
-
-static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
- int flags, team_nl_send_func_t *send_func,
- struct team_port *one_port)
-{
- struct nlattr *port_list;
- struct nlmsghdr *nlh;
- void *hdr;
- struct team_port *port;
- int err;
- struct sk_buff *skb = NULL;
- bool incomplete;
- int i;
-
- port = list_first_entry_or_null(&team->port_list,
- struct team_port, list);
-
-start_again:
- err = __send_and_alloc_skb(&skb, team, portid, send_func);
- if (err)
- return err;
-
- hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
- TEAM_CMD_PORT_LIST_GET);
- if (!hdr) {
- nlmsg_free(skb);
- return -EMSGSIZE;
- }
-
- if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
- goto nla_put_failure;
- port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT);
- if (!port_list)
- goto nla_put_failure;
-
- i = 0;
- incomplete = false;
-
- /* If one port is selected, called wants to send port list containing
- * only this port. Otherwise go through all listed ports and send all
- */
- if (one_port) {
- err = team_nl_fill_one_port_get(skb, one_port);
- if (err)
- goto errout;
- } else if (port) {
- list_for_each_entry_from(port, &team->port_list, list) {
- err = team_nl_fill_one_port_get(skb, port);
- if (err) {
- if (err == -EMSGSIZE) {
- if (!i)
- goto errout;
- incomplete = true;
- break;
- }
- goto errout;
- }
- i++;
- }
- }
-
- nla_nest_end(skb, port_list);
- genlmsg_end(skb, hdr);
- if (incomplete)
- goto start_again;
-
-send_done:
- nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
- if (!nlh) {
- err = __send_and_alloc_skb(&skb, team, portid, send_func);
- if (err)
- return err;
- goto send_done;
- }
-
- return send_func(skb, team, portid);
-
-nla_put_failure:
- err = -EMSGSIZE;
-errout:
- nlmsg_free(skb);
- return err;
-}
-
-static int team_nl_cmd_port_list_get(struct sk_buff *skb,
- struct genl_info *info)
-{
- struct team *team;
- int err;
-
- team = team_nl_team_get(info);
- if (!team)
- return -EINVAL;
-
- err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
- NLM_F_ACK, team_nl_send_unicast, NULL);
-
- team_nl_team_put(team);
-
- return err;
-}
-
-static const struct genl_small_ops team_nl_ops[] = {
- {
- .cmd = TEAM_CMD_NOOP,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_noop,
- },
- {
- .cmd = TEAM_CMD_OPTIONS_SET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_options_set,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = TEAM_CMD_OPTIONS_GET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_options_get,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = TEAM_CMD_PORT_LIST_GET,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = team_nl_cmd_port_list_get,
- .flags = GENL_ADMIN_PERM,
- },
-};
-
-static const struct genl_multicast_group team_nl_mcgrps[] = {
- { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
-};
-
-static struct genl_family team_nl_family __ro_after_init = {
- .name = TEAM_GENL_NAME,
- .version = TEAM_GENL_VERSION,
- .maxattr = TEAM_ATTR_MAX,
- .policy = team_nl_policy,
- .netnsok = true,
- .module = THIS_MODULE,
- .small_ops = team_nl_ops,
- .n_small_ops = ARRAY_SIZE(team_nl_ops),
- .resv_start_op = TEAM_CMD_PORT_LIST_GET + 1,
- .mcgrps = team_nl_mcgrps,
- .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps),
-};
-
-static int team_nl_send_multicast(struct sk_buff *skb,
- struct team *team, u32 portid)
-{
- return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
- skb, 0, 0, GFP_KERNEL);
-}
-
-static int team_nl_send_event_options_get(struct team *team,
- struct list_head *sel_opt_inst_list)
-{
- return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
- sel_opt_inst_list);
-}
-
-static int team_nl_send_event_port_get(struct team *team,
- struct team_port *port)
-{
- return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
- port);
-}
-
-static int __init team_nl_init(void)
-{
- return genl_register_family(&team_nl_family);
-}
-
-static void __exit team_nl_fini(void)
-{
- genl_unregister_family(&team_nl_family);
-}
-
-
-/******************
- * Change checkers
- ******************/
-
-static void __team_options_change_check(struct team *team)
-{
- int err;
- struct team_option_inst *opt_inst;
- LIST_HEAD(sel_opt_inst_list);
-
- list_for_each_entry(opt_inst, &team->option_inst_list, list) {
- if (opt_inst->changed)
- list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
- }
- err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
- if (err && err != -ESRCH)
- netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
- err);
-}
-
-/* rtnl lock is held */
-
-static void __team_port_change_send(struct team_port *port, bool linkup)
-{
- int err;
-
- port->changed = true;
- port->state.linkup = linkup;
- team_refresh_port_linkup(port);
- if (linkup) {
- struct ethtool_link_ksettings ecmd;
-
- err = __ethtool_get_link_ksettings(port->dev, &ecmd);
- if (!err) {
- port->state.speed = ecmd.base.speed;
- port->state.duplex = ecmd.base.duplex;
- goto send_event;
- }
- }
- port->state.speed = 0;
- port->state.duplex = 0;
-
-send_event:
- err = team_nl_send_event_port_get(port->team, port);
- if (err && err != -ESRCH)
- netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
- port->dev->name, err);
-
-}
-
-static void __team_carrier_check(struct team *team)
-{
- struct team_port *port;
- bool team_linkup;
-
- if (team->user_carrier_enabled)
- return;
-
- team_linkup = false;
- list_for_each_entry(port, &team->port_list, list) {
- if (port->linkup) {
- team_linkup = true;
- break;
- }
- }
-
- if (team_linkup)
- netif_carrier_on(team->dev);
- else
- netif_carrier_off(team->dev);
-}
-
-static void __team_port_change_check(struct team_port *port, bool linkup)
-{
- if (port->state.linkup != linkup)
- __team_port_change_send(port, linkup);
- __team_carrier_check(port->team);
-}
-
-static void __team_port_change_port_added(struct team_port *port, bool linkup)
-{
- __team_port_change_send(port, linkup);
- __team_carrier_check(port->team);
-}
-
-static void __team_port_change_port_removed(struct team_port *port)
-{
- port->removed = true;
- __team_port_change_send(port, false);
- __team_carrier_check(port->team);
-}
-
-static void team_port_change_check(struct team_port *port, bool linkup)
-{
- struct team *team = port->team;
-
- mutex_lock(&team->lock);
- __team_port_change_check(port, linkup);
- mutex_unlock(&team->lock);
-}
-
-
-/************************************
- * Net device notifier event handler
- ************************************/
-
-static int team_device_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct team_port *port;
-
- port = team_port_get_rtnl(dev);
- if (!port)
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UP:
- if (netif_oper_up(dev))
- team_port_change_check(port, true);
- break;
- case NETDEV_DOWN:
- team_port_change_check(port, false);
- break;
- case NETDEV_CHANGE:
- if (netif_running(port->dev))
- team_port_change_check(port,
- !!netif_oper_up(port->dev));
- break;
- case NETDEV_UNREGISTER:
- team_del_slave(port->team->dev, dev);
- break;
- case NETDEV_FEAT_CHANGE:
- if (!port->team->notifier_ctx) {
- port->team->notifier_ctx = true;
- team_compute_features(port->team);
- port->team->notifier_ctx = false;
- }
- break;
- case NETDEV_PRECHANGEMTU:
- /* Forbid to change mtu of underlaying device */
- if (!port->team->port_mtu_change_allowed)
- return NOTIFY_BAD;
- break;
- case NETDEV_PRE_TYPE_CHANGE:
- /* Forbid to change type of underlaying device */
- return NOTIFY_BAD;
- case NETDEV_RESEND_IGMP:
- /* Propagate to master device */
- call_netdevice_notifiers(event, port->team->dev);
- break;
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block team_notifier_block __read_mostly = {
- .notifier_call = team_device_event,
-};
-
-
-/***********************
- * Module init and exit
- ***********************/
-
-static int __init team_module_init(void)
-{
- int err;
-
- register_netdevice_notifier(&team_notifier_block);
-
- err = rtnl_link_register(&team_link_ops);
- if (err)
- goto err_rtnl_reg;
-
- err = team_nl_init();
- if (err)
- goto err_nl_init;
-
- return 0;
-
-err_nl_init:
- rtnl_link_unregister(&team_link_ops);
-
-err_rtnl_reg:
- unregister_netdevice_notifier(&team_notifier_block);
-
- return err;
-}
-
-static void __exit team_module_exit(void)
-{
- team_nl_fini();
- rtnl_link_unregister(&team_link_ops);
- unregister_netdevice_notifier(&team_notifier_block);
-}
-
-module_init(team_module_init);
-module_exit(team_module_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
-MODULE_DESCRIPTION("Ethernet team device driver");
-MODULE_ALIAS_RTNL_LINK(DRV_NAME);
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
new file mode 100644
index 000000000000..a4bf020cf8e7
--- /dev/null
+++ b/drivers/net/team/team_core.c
@@ -0,0 +1,3182 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * drivers/net/team/team.c - Network team device driver
+ * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
+ */
+
+#include <linux/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/netpoll.h>
+#include <linux/if_vlan.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/rtnetlink.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
+#include <net/sch_generic.h>
+#include <generated/utsrelease.h>
+#include <linux/if_team.h>
+
+#define DRV_NAME "team"
+
+
+/**********
+ * Helpers
+ **********/
+
+static struct team_port *team_port_get_rtnl(const struct net_device *dev)
+{
+ struct team_port *port = rtnl_dereference(dev->rx_handler_data);
+
+ return netif_is_team_port(dev) ? port : NULL;
+}
+
+/*
+ * Since the ability to change device address for open port device is tested in
+ * team_port_add, this function can be called without control of return value
+ */
+static int __set_port_dev_addr(struct net_device *port_dev,
+ const unsigned char *dev_addr)
+{
+ struct sockaddr_storage addr;
+
+ memcpy(addr.__data, dev_addr, port_dev->addr_len);
+ addr.ss_family = port_dev->type;
+ return dev_set_mac_address(port_dev, (struct sockaddr *)&addr, NULL);
+}
+
+static int team_port_set_orig_dev_addr(struct team_port *port)
+{
+ return __set_port_dev_addr(port->dev, port->orig.dev_addr);
+}
+
+static int team_port_set_team_dev_addr(struct team *team,
+ struct team_port *port)
+{
+ return __set_port_dev_addr(port->dev, team->dev->dev_addr);
+}
+
+int team_modeop_port_enter(struct team *team, struct team_port *port)
+{
+ return team_port_set_team_dev_addr(team, port);
+}
+EXPORT_SYMBOL(team_modeop_port_enter);
+
+void team_modeop_port_change_dev_addr(struct team *team,
+ struct team_port *port)
+{
+ team_port_set_team_dev_addr(team, port);
+}
+EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
+
+static void team_lower_state_changed(struct team_port *port)
+{
+ struct netdev_lag_lower_state_info info;
+
+ info.link_up = port->linkup;
+ info.tx_enabled = team_port_enabled(port);
+ netdev_lower_state_changed(port->dev, &info);
+}
+
+static void team_refresh_port_linkup(struct team_port *port)
+{
+ bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
+ port->state.linkup;
+
+ if (port->linkup != new_linkup) {
+ port->linkup = new_linkup;
+ team_lower_state_changed(port);
+ }
+}
+
+
+/*******************
+ * Options handling
+ *******************/
+
+struct team_option_inst { /* One for each option instance */
+ struct list_head list;
+ struct list_head tmp_list;
+ struct team_option *option;
+ struct team_option_inst_info info;
+ bool changed;
+ bool removed;
+};
+
+static struct team_option *__team_find_option(struct team *team,
+ const char *opt_name)
+{
+ struct team_option *option;
+
+ list_for_each_entry(option, &team->option_list, list) {
+ if (strcmp(option->name, opt_name) == 0)
+ return option;
+ }
+ return NULL;
+}
+
+static void __team_option_inst_del(struct team_option_inst *opt_inst)
+{
+ list_del(&opt_inst->list);
+ kfree(opt_inst);
+}
+
+static void __team_option_inst_del_option(struct team *team,
+ struct team_option *option)
+{
+ struct team_option_inst *opt_inst, *tmp;
+
+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
+ if (opt_inst->option == option)
+ __team_option_inst_del(opt_inst);
+ }
+}
+
+static int __team_option_inst_add(struct team *team, struct team_option *option,
+ struct team_port *port)
+{
+ struct team_option_inst *opt_inst;
+ unsigned int array_size;
+ unsigned int i;
+
+ array_size = option->array_size;
+ if (!array_size)
+ array_size = 1; /* No array but still need one instance */
+
+ for (i = 0; i < array_size; i++) {
+ opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL);
+ if (!opt_inst)
+ return -ENOMEM;
+ opt_inst->option = option;
+ opt_inst->info.port = port;
+ opt_inst->info.array_index = i;
+ opt_inst->changed = true;
+ opt_inst->removed = false;
+ list_add_tail(&opt_inst->list, &team->option_inst_list);
+ if (option->init)
+ option->init(team, &opt_inst->info);
+
+ }
+ return 0;
+}
+
+static int __team_option_inst_add_option(struct team *team,
+ struct team_option *option)
+{
+ int err;
+
+ if (!option->per_port) {
+ err = __team_option_inst_add(team, option, NULL);
+ if (err)
+ goto inst_del_option;
+ }
+ return 0;
+
+inst_del_option:
+ __team_option_inst_del_option(team, option);
+ return err;
+}
+
+static void __team_option_inst_mark_removed_option(struct team *team,
+ struct team_option *option)
+{
+ struct team_option_inst *opt_inst;
+
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ if (opt_inst->option == option) {
+ opt_inst->changed = true;
+ opt_inst->removed = true;
+ }
+ }
+}
+
+static void __team_option_inst_del_port(struct team *team,
+ struct team_port *port)
+{
+ struct team_option_inst *opt_inst, *tmp;
+
+ list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) {
+ if (opt_inst->option->per_port &&
+ opt_inst->info.port == port)
+ __team_option_inst_del(opt_inst);
+ }
+}
+
+static int __team_option_inst_add_port(struct team *team,
+ struct team_port *port)
+{
+ struct team_option *option;
+ int err;
+
+ list_for_each_entry(option, &team->option_list, list) {
+ if (!option->per_port)
+ continue;
+ err = __team_option_inst_add(team, option, port);
+ if (err)
+ goto inst_del_port;
+ }
+ return 0;
+
+inst_del_port:
+ __team_option_inst_del_port(team, port);
+ return err;
+}
+
+static void __team_option_inst_mark_removed_port(struct team *team,
+ struct team_port *port)
+{
+ struct team_option_inst *opt_inst;
+
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ if (opt_inst->info.port == port) {
+ opt_inst->changed = true;
+ opt_inst->removed = true;
+ }
+ }
+}
+
+static int __team_options_register(struct team *team,
+ const struct team_option *option,
+ size_t option_count)
+{
+ int i;
+ struct team_option **dst_opts;
+ int err;
+
+ dst_opts = kcalloc(option_count, sizeof(struct team_option *),
+ GFP_KERNEL);
+ if (!dst_opts)
+ return -ENOMEM;
+ for (i = 0; i < option_count; i++, option++) {
+ if (__team_find_option(team, option->name)) {
+ err = -EEXIST;
+ goto alloc_rollback;
+ }
+ dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL);
+ if (!dst_opts[i]) {
+ err = -ENOMEM;
+ goto alloc_rollback;
+ }
+ }
+
+ for (i = 0; i < option_count; i++) {
+ err = __team_option_inst_add_option(team, dst_opts[i]);
+ if (err)
+ goto inst_rollback;
+ list_add_tail(&dst_opts[i]->list, &team->option_list);
+ }
+
+ kfree(dst_opts);
+ return 0;
+
+inst_rollback:
+ for (i--; i >= 0; i--) {
+ __team_option_inst_del_option(team, dst_opts[i]);
+ list_del(&dst_opts[i]->list);
+ }
+
+ i = option_count;
+alloc_rollback:
+ for (i--; i >= 0; i--)
+ kfree(dst_opts[i]);
+
+ kfree(dst_opts);
+ return err;
+}
+
+static void __team_options_mark_removed(struct team *team,
+ const struct team_option *option,
+ size_t option_count)
+{
+ int i;
+
+ for (i = 0; i < option_count; i++, option++) {
+ struct team_option *del_opt;
+
+ del_opt = __team_find_option(team, option->name);
+ if (del_opt)
+ __team_option_inst_mark_removed_option(team, del_opt);
+ }
+}
+
+static void __team_options_unregister(struct team *team,
+ const struct team_option *option,
+ size_t option_count)
+{
+ int i;
+
+ for (i = 0; i < option_count; i++, option++) {
+ struct team_option *del_opt;
+
+ del_opt = __team_find_option(team, option->name);
+ if (del_opt) {
+ __team_option_inst_del_option(team, del_opt);
+ list_del(&del_opt->list);
+ kfree(del_opt);
+ }
+ }
+}
+
+static void __team_options_change_check(struct team *team);
+
+int team_options_register(struct team *team,
+ const struct team_option *option,
+ size_t option_count)
+{
+ int err;
+
+ err = __team_options_register(team, option, option_count);
+ if (err)
+ return err;
+ __team_options_change_check(team);
+ return 0;
+}
+EXPORT_SYMBOL(team_options_register);
+
+void team_options_unregister(struct team *team,
+ const struct team_option *option,
+ size_t option_count)
+{
+ __team_options_mark_removed(team, option, option_count);
+ __team_options_change_check(team);
+ __team_options_unregister(team, option, option_count);
+}
+EXPORT_SYMBOL(team_options_unregister);
+
+static int team_option_get(struct team *team,
+ struct team_option_inst *opt_inst,
+ struct team_gsetter_ctx *ctx)
+{
+ if (!opt_inst->option->getter)
+ return -EOPNOTSUPP;
+
+ opt_inst->option->getter(team, ctx);
+ return 0;
+}
+
+static int team_option_set(struct team *team,
+ struct team_option_inst *opt_inst,
+ struct team_gsetter_ctx *ctx)
+{
+ if (!opt_inst->option->setter)
+ return -EOPNOTSUPP;
+ return opt_inst->option->setter(team, ctx);
+}
+
+void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info)
+{
+ struct team_option_inst *opt_inst;
+
+ opt_inst = container_of(opt_inst_info, struct team_option_inst, info);
+ opt_inst->changed = true;
+}
+EXPORT_SYMBOL(team_option_inst_set_change);
+
+void team_options_change_check(struct team *team)
+{
+ __team_options_change_check(team);
+}
+EXPORT_SYMBOL(team_options_change_check);
+
+
+/****************
+ * Mode handling
+ ****************/
+
+static LIST_HEAD(mode_list);
+static DEFINE_SPINLOCK(mode_list_lock);
+
+struct team_mode_item {
+ struct list_head list;
+ const struct team_mode *mode;
+};
+
+static struct team_mode_item *__find_mode(const char *kind)
+{
+ struct team_mode_item *mitem;
+
+ list_for_each_entry(mitem, &mode_list, list) {
+ if (strcmp(mitem->mode->kind, kind) == 0)
+ return mitem;
+ }
+ return NULL;
+}
+
+static bool is_good_mode_name(const char *name)
+{
+ while (*name != '\0') {
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ name++;
+ }
+ return true;
+}
+
+int team_mode_register(const struct team_mode *mode)
+{
+ int err = 0;
+ struct team_mode_item *mitem;
+
+ if (!is_good_mode_name(mode->kind) ||
+ mode->priv_size > TEAM_MODE_PRIV_SIZE)
+ return -EINVAL;
+
+ mitem = kmalloc(sizeof(*mitem), GFP_KERNEL);
+ if (!mitem)
+ return -ENOMEM;
+
+ spin_lock(&mode_list_lock);
+ if (__find_mode(mode->kind)) {
+ err = -EEXIST;
+ kfree(mitem);
+ goto unlock;
+ }
+ mitem->mode = mode;
+ list_add_tail(&mitem->list, &mode_list);
+unlock:
+ spin_unlock(&mode_list_lock);
+ return err;
+}
+EXPORT_SYMBOL(team_mode_register);
+
+void team_mode_unregister(const struct team_mode *mode)
+{
+ struct team_mode_item *mitem;
+
+ spin_lock(&mode_list_lock);
+ mitem = __find_mode(mode->kind);
+ if (mitem) {
+ list_del_init(&mitem->list);
+ kfree(mitem);
+ }
+ spin_unlock(&mode_list_lock);
+}
+EXPORT_SYMBOL(team_mode_unregister);
+
+static const struct team_mode *team_mode_get(const char *kind)
+{
+ struct team_mode_item *mitem;
+ const struct team_mode *mode = NULL;
+
+ if (!try_module_get(THIS_MODULE))
+ return NULL;
+
+ spin_lock(&mode_list_lock);
+ mitem = __find_mode(kind);
+ if (!mitem) {
+ spin_unlock(&mode_list_lock);
+ request_module("team-mode-%s", kind);
+ spin_lock(&mode_list_lock);
+ mitem = __find_mode(kind);
+ }
+ if (mitem) {
+ mode = mitem->mode;
+ if (!try_module_get(mode->owner))
+ mode = NULL;
+ }
+
+ spin_unlock(&mode_list_lock);
+ module_put(THIS_MODULE);
+ return mode;
+}
+
+static void team_mode_put(const struct team_mode *mode)
+{
+ module_put(mode->owner);
+}
+
+static bool team_dummy_transmit(struct team *team, struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return false;
+}
+
+static rx_handler_result_t team_dummy_receive(struct team *team,
+ struct team_port *port,
+ struct sk_buff *skb)
+{
+ return RX_HANDLER_ANOTHER;
+}
+
+static const struct team_mode __team_no_mode = {
+ .kind = "*NOMODE*",
+};
+
+static bool team_is_mode_set(struct team *team)
+{
+ return team->mode != &__team_no_mode;
+}
+
+static void team_set_no_mode(struct team *team)
+{
+ team->user_carrier_enabled = false;
+ team->mode = &__team_no_mode;
+}
+
+static void team_adjust_ops(struct team *team)
+{
+ /*
+ * To avoid checks in rx/tx skb paths, ensure here that non-null and
+ * correct ops are always set.
+ */
+
+ if (!team->en_port_count || !team_is_mode_set(team) ||
+ !team->mode->ops->transmit)
+ team->ops.transmit = team_dummy_transmit;
+ else
+ team->ops.transmit = team->mode->ops->transmit;
+
+ if (!team->en_port_count || !team_is_mode_set(team) ||
+ !team->mode->ops->receive)
+ team->ops.receive = team_dummy_receive;
+ else
+ team->ops.receive = team->mode->ops->receive;
+}
+
+/*
+ * We can benefit from the fact that it's ensured no port is present
+ * at the time of mode change. Therefore no packets are in fly so there's no
+ * need to set mode operations in any special way.
+ */
+static int __team_change_mode(struct team *team,
+ const struct team_mode *new_mode)
+{
+ /* Check if mode was previously set and do cleanup if so */
+ if (team_is_mode_set(team)) {
+ void (*exit_op)(struct team *team) = team->ops.exit;
+
+ /* Clear ops area so no callback is called any longer */
+ memset(&team->ops, 0, sizeof(struct team_mode_ops));
+ team_adjust_ops(team);
+
+ if (exit_op)
+ exit_op(team);
+ team_mode_put(team->mode);
+ team_set_no_mode(team);
+ /* zero private data area */
+ memset(&team->mode_priv, 0,
+ sizeof(struct team) - offsetof(struct team, mode_priv));
+ }
+
+ if (!new_mode)
+ return 0;
+
+ if (new_mode->ops->init) {
+ int err;
+
+ err = new_mode->ops->init(team);
+ if (err)
+ return err;
+ }
+
+ team->mode = new_mode;
+ memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
+ team_adjust_ops(team);
+
+ return 0;
+}
+
+static int team_change_mode(struct team *team, const char *kind)
+{
+ const struct team_mode *new_mode;
+ struct net_device *dev = team->dev;
+ int err;
+
+ if (!list_empty(&team->port_list)) {
+ netdev_err(dev, "No ports can be present during mode change\n");
+ return -EBUSY;
+ }
+
+ if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) {
+ netdev_err(dev, "Unable to change to the same mode the team is in\n");
+ return -EINVAL;
+ }
+
+ new_mode = team_mode_get(kind);
+ if (!new_mode) {
+ netdev_err(dev, "Mode \"%s\" not found\n", kind);
+ return -EINVAL;
+ }
+
+ err = __team_change_mode(team, new_mode);
+ if (err) {
+ netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
+ team_mode_put(new_mode);
+ return err;
+ }
+
+ netdev_info(dev, "Mode changed to \"%s\"\n", kind);
+ return 0;
+}
+
+
+/*********************
+ * Peers notification
+ *********************/
+
+static void team_notify_peers_work(struct work_struct *work)
+{
+ struct team *team;
+ int val;
+
+ team = container_of(work, struct team, notify_peers.dw.work);
+
+ if (!rtnl_trylock()) {
+ schedule_delayed_work(&team->notify_peers.dw, 0);
+ return;
+ }
+ val = atomic_dec_if_positive(&team->notify_peers.count_pending);
+ if (val < 0) {
+ rtnl_unlock();
+ return;
+ }
+ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev);
+ rtnl_unlock();
+ if (val)
+ schedule_delayed_work(&team->notify_peers.dw,
+ msecs_to_jiffies(team->notify_peers.interval));
+}
+
+static void team_notify_peers(struct team *team)
+{
+ if (!team->notify_peers.count || !netif_running(team->dev))
+ return;
+ atomic_add(team->notify_peers.count, &team->notify_peers.count_pending);
+ schedule_delayed_work(&team->notify_peers.dw, 0);
+}
+
+static void team_notify_peers_init(struct team *team)
+{
+ INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work);
+}
+
+static void team_notify_peers_fini(struct team *team)
+{
+ cancel_delayed_work_sync(&team->notify_peers.dw);
+}
+
+
+/*******************************
+ * Send multicast group rejoins
+ *******************************/
+
+static void team_mcast_rejoin_work(struct work_struct *work)
+{
+ struct team *team;
+ int val;
+
+ team = container_of(work, struct team, mcast_rejoin.dw.work);
+
+ if (!rtnl_trylock()) {
+ schedule_delayed_work(&team->mcast_rejoin.dw, 0);
+ return;
+ }
+ val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending);
+ if (val < 0) {
+ rtnl_unlock();
+ return;
+ }
+ call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev);
+ rtnl_unlock();
+ if (val)
+ schedule_delayed_work(&team->mcast_rejoin.dw,
+ msecs_to_jiffies(team->mcast_rejoin.interval));
+}
+
+static void team_mcast_rejoin(struct team *team)
+{
+ if (!team->mcast_rejoin.count || !netif_running(team->dev))
+ return;
+ atomic_add(team->mcast_rejoin.count, &team->mcast_rejoin.count_pending);
+ schedule_delayed_work(&team->mcast_rejoin.dw, 0);
+}
+
+static void team_mcast_rejoin_init(struct team *team)
+{
+ INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work);
+}
+
+static void team_mcast_rejoin_fini(struct team *team)
+{
+ cancel_delayed_work_sync(&team->mcast_rejoin.dw);
+}
+
+
+/************************
+ * Rx path frame handler
+ ************************/
+
+/* note: already called with rcu_read_lock */
+static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct team_port *port;
+ struct team *team;
+ rx_handler_result_t res;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return RX_HANDLER_CONSUMED;
+
+ *pskb = skb;
+
+ port = team_port_get_rcu(skb->dev);
+ team = port->team;
+ if (!team_port_enabled(port)) {
+ if (is_link_local_ether_addr(eth_hdr(skb)->h_dest))
+ /* link-local packets are mostly useful when stack receives them
+ * with the link they arrive on.
+ */
+ return RX_HANDLER_PASS;
+ /* allow exact match delivery for disabled ports */
+ res = RX_HANDLER_EXACT;
+ } else {
+ res = team->ops.receive(team, port, skb);
+ }
+ if (res == RX_HANDLER_ANOTHER) {
+ struct team_pcpu_stats *pcpu_stats;
+
+ pcpu_stats = this_cpu_ptr(team->pcpu_stats);
+ u64_stats_update_begin(&pcpu_stats->syncp);
+ u64_stats_inc(&pcpu_stats->rx_packets);
+ u64_stats_add(&pcpu_stats->rx_bytes, skb->len);
+ if (skb->pkt_type == PACKET_MULTICAST)
+ u64_stats_inc(&pcpu_stats->rx_multicast);
+ u64_stats_update_end(&pcpu_stats->syncp);
+
+ skb->dev = team->dev;
+ } else if (res == RX_HANDLER_EXACT) {
+ this_cpu_inc(team->pcpu_stats->rx_nohandler);
+ } else {
+ this_cpu_inc(team->pcpu_stats->rx_dropped);
+ }
+
+ return res;
+}
+
+
+/*************************************
+ * Multiqueue Tx port select override
+ *************************************/
+
+static int team_queue_override_init(struct team *team)
+{
+ struct list_head *listarr;
+ unsigned int queue_cnt = team->dev->num_tx_queues - 1;
+ unsigned int i;
+
+ if (!queue_cnt)
+ return 0;
+ listarr = kmalloc_array(queue_cnt, sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!listarr)
+ return -ENOMEM;
+ team->qom_lists = listarr;
+ for (i = 0; i < queue_cnt; i++)
+ INIT_LIST_HEAD(listarr++);
+ return 0;
+}
+
+static void team_queue_override_fini(struct team *team)
+{
+ kfree(team->qom_lists);
+}
+
+static struct list_head *__team_get_qom_list(struct team *team, u16 queue_id)
+{
+ return &team->qom_lists[queue_id - 1];
+}
+
+/*
+ * note: already called with rcu_read_lock
+ */
+static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb)
+{
+ struct list_head *qom_list;
+ struct team_port *port;
+
+ if (!team->queue_override_enabled || !skb->queue_mapping)
+ return false;
+ qom_list = __team_get_qom_list(team, skb->queue_mapping);
+ list_for_each_entry_rcu(port, qom_list, qom_list) {
+ if (!team_dev_queue_xmit(team, port, skb))
+ return true;
+ }
+ return false;
+}
+
+static void __team_queue_override_port_del(struct team *team,
+ struct team_port *port)
+{
+ if (!port->queue_id)
+ return;
+ list_del_rcu(&port->qom_list);
+}
+
+static bool team_queue_override_port_has_gt_prio_than(struct team_port *port,
+ struct team_port *cur)
+{
+ if (port->priority < cur->priority)
+ return true;
+ if (port->priority > cur->priority)
+ return false;
+ if (port->index < cur->index)
+ return true;
+ return false;
+}
+
+static void __team_queue_override_port_add(struct team *team,
+ struct team_port *port)
+{
+ struct team_port *cur;
+ struct list_head *qom_list;
+ struct list_head *node;
+
+ if (!port->queue_id)
+ return;
+ qom_list = __team_get_qom_list(team, port->queue_id);
+ node = qom_list;
+ list_for_each_entry(cur, qom_list, qom_list) {
+ if (team_queue_override_port_has_gt_prio_than(port, cur))
+ break;
+ node = &cur->qom_list;
+ }
+ list_add_tail_rcu(&port->qom_list, node);
+}
+
+static void __team_queue_override_enabled_check(struct team *team)
+{
+ struct team_port *port;
+ bool enabled = false;
+
+ list_for_each_entry(port, &team->port_list, list) {
+ if (port->queue_id) {
+ enabled = true;
+ break;
+ }
+ }
+ if (enabled == team->queue_override_enabled)
+ return;
+ netdev_dbg(team->dev, "%s queue override\n",
+ enabled ? "Enabling" : "Disabling");
+ team->queue_override_enabled = enabled;
+}
+
+static void team_queue_override_port_prio_changed(struct team *team,
+ struct team_port *port)
+{
+ if (!port->queue_id || !team_port_enabled(port))
+ return;
+ __team_queue_override_port_del(team, port);
+ __team_queue_override_port_add(team, port);
+ __team_queue_override_enabled_check(team);
+}
+
+static void team_queue_override_port_change_queue_id(struct team *team,
+ struct team_port *port,
+ u16 new_queue_id)
+{
+ if (team_port_enabled(port)) {
+ __team_queue_override_port_del(team, port);
+ port->queue_id = new_queue_id;
+ __team_queue_override_port_add(team, port);
+ __team_queue_override_enabled_check(team);
+ } else {
+ port->queue_id = new_queue_id;
+ }
+}
+
+static void team_queue_override_port_add(struct team *team,
+ struct team_port *port)
+{
+ __team_queue_override_port_add(team, port);
+ __team_queue_override_enabled_check(team);
+}
+
+static void team_queue_override_port_del(struct team *team,
+ struct team_port *port)
+{
+ __team_queue_override_port_del(team, port);
+ __team_queue_override_enabled_check(team);
+}
+
+
+/****************
+ * Port handling
+ ****************/
+
+static bool team_port_find(const struct team *team,
+ const struct team_port *port)
+{
+ struct team_port *cur;
+
+ list_for_each_entry(cur, &team->port_list, list)
+ if (cur == port)
+ return true;
+ return false;
+}
+
+/*
+ * Enable/disable port by adding to enabled port hashlist and setting
+ * port->index (Might be racy so reader could see incorrect ifindex when
+ * processing a flying packet, but that is not a problem). Write guarded
+ * by team->lock.
+ */
+static void team_port_enable(struct team *team,
+ struct team_port *port)
+{
+ if (team_port_enabled(port))
+ return;
+ port->index = team->en_port_count++;
+ hlist_add_head_rcu(&port->hlist,
+ team_port_index_hash(team, port->index));
+ team_adjust_ops(team);
+ team_queue_override_port_add(team, port);
+ if (team->ops.port_enabled)
+ team->ops.port_enabled(team, port);
+ team_notify_peers(team);
+ team_mcast_rejoin(team);
+ team_lower_state_changed(port);
+}
+
+static void __reconstruct_port_hlist(struct team *team, int rm_index)
+{
+ int i;
+ struct team_port *port;
+
+ for (i = rm_index + 1; i < team->en_port_count; i++) {
+ port = team_get_port_by_index(team, i);
+ hlist_del_rcu(&port->hlist);
+ port->index--;
+ hlist_add_head_rcu(&port->hlist,
+ team_port_index_hash(team, port->index));
+ }
+}
+
+static void team_port_disable(struct team *team,
+ struct team_port *port)
+{
+ if (!team_port_enabled(port))
+ return;
+ if (team->ops.port_disabled)
+ team->ops.port_disabled(team, port);
+ hlist_del_rcu(&port->hlist);
+ __reconstruct_port_hlist(team, port->index);
+ port->index = -1;
+ team->en_port_count--;
+ team_queue_override_port_del(team, port);
+ team_adjust_ops(team);
+ team_lower_state_changed(port);
+}
+
+#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
+ NETIF_F_HIGHDMA | NETIF_F_LRO | \
+ NETIF_F_GSO_ENCAP_ALL)
+
+#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
+
+static void __team_compute_features(struct team *team)
+{
+ struct team_port *port;
+ netdev_features_t vlan_features = TEAM_VLAN_FEATURES &
+ NETIF_F_ALL_FOR_ALL;
+ netdev_features_t enc_features = TEAM_ENC_FEATURES;
+ unsigned short max_hard_header_len = ETH_HLEN;
+ unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
+ IFF_XMIT_DST_RELEASE_PERM;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ vlan_features = netdev_increment_features(vlan_features,
+ port->dev->vlan_features,
+ TEAM_VLAN_FEATURES);
+ enc_features =
+ netdev_increment_features(enc_features,
+ port->dev->hw_enc_features,
+ TEAM_ENC_FEATURES);
+
+
+ dst_release_flag &= port->dev->priv_flags;
+ if (port->dev->hard_header_len > max_hard_header_len)
+ max_hard_header_len = port->dev->hard_header_len;
+ }
+ rcu_read_unlock();
+
+ team->dev->vlan_features = vlan_features;
+ team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
+ NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_STAG_TX;
+ team->dev->hard_header_len = max_hard_header_len;
+
+ team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
+ team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
+}
+
+static void team_compute_features(struct team *team)
+{
+ __team_compute_features(team);
+ netdev_change_features(team->dev);
+}
+
+static int team_port_enter(struct team *team, struct team_port *port)
+{
+ int err = 0;
+
+ dev_hold(team->dev);
+ if (team->ops.port_enter) {
+ err = team->ops.port_enter(team, port);
+ if (err) {
+ netdev_err(team->dev, "Device %s failed to enter team mode\n",
+ port->dev->name);
+ goto err_port_enter;
+ }
+ }
+
+ return 0;
+
+err_port_enter:
+ dev_put(team->dev);
+
+ return err;
+}
+
+static void team_port_leave(struct team *team, struct team_port *port)
+{
+ if (team->ops.port_leave)
+ team->ops.port_leave(team, port);
+ dev_put(team->dev);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static int __team_port_enable_netpoll(struct team_port *port)
+{
+ struct netpoll *np;
+ int err;
+
+ np = kzalloc(sizeof(*np), GFP_KERNEL);
+ if (!np)
+ return -ENOMEM;
+
+ err = __netpoll_setup(np, port->dev);
+ if (err) {
+ kfree(np);
+ return err;
+ }
+ port->np = np;
+ return err;
+}
+
+static int team_port_enable_netpoll(struct team_port *port)
+{
+ if (!port->team->dev->npinfo)
+ return 0;
+
+ return __team_port_enable_netpoll(port);
+}
+
+static void team_port_disable_netpoll(struct team_port *port)
+{
+ struct netpoll *np = port->np;
+
+ if (!np)
+ return;
+ port->np = NULL;
+
+ __netpoll_free(np);
+}
+#else
+static int team_port_enable_netpoll(struct team_port *port)
+{
+ return 0;
+}
+static void team_port_disable_netpoll(struct team_port *port)
+{
+}
+#endif
+
+static int team_upper_dev_link(struct team *team, struct team_port *port,
+ struct netlink_ext_ack *extack)
+{
+ struct netdev_lag_upper_info lag_upper_info;
+ int err;
+
+ lag_upper_info.tx_type = team->mode->lag_tx_type;
+ lag_upper_info.hash_type = NETDEV_LAG_HASH_UNKNOWN;
+ err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
+ &lag_upper_info, extack);
+ if (err)
+ return err;
+ port->dev->priv_flags |= IFF_TEAM_PORT;
+ return 0;
+}
+
+static void team_upper_dev_unlink(struct team *team, struct team_port *port)
+{
+ netdev_upper_dev_unlink(port->dev, team->dev);
+ port->dev->priv_flags &= ~IFF_TEAM_PORT;
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup);
+static int team_dev_type_check_change(struct net_device *dev,
+ struct net_device *port_dev);
+
+static int team_port_add(struct team *team, struct net_device *port_dev,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *dev = team->dev;
+ struct team_port *port;
+ char *portname = port_dev->name;
+ int err;
+
+ if (port_dev->flags & IFF_LOOPBACK) {
+ NL_SET_ERR_MSG(extack, "Loopback device can't be added as a team port");
+ netdev_err(dev, "Device %s is loopback device. Loopback devices can't be added as a team port\n",
+ portname);
+ return -EINVAL;
+ }
+
+ if (netif_is_team_port(port_dev)) {
+ NL_SET_ERR_MSG(extack, "Device is already a port of a team device");
+ netdev_err(dev, "Device %s is already a port "
+ "of a team device\n", portname);
+ return -EBUSY;
+ }
+
+ if (dev == port_dev) {
+ NL_SET_ERR_MSG(extack, "Cannot enslave team device to itself");
+ netdev_err(dev, "Cannot enslave team device to itself\n");
+ return -EINVAL;
+ }
+
+ if (netdev_has_upper_dev(dev, port_dev)) {
+ NL_SET_ERR_MSG(extack, "Device is already an upper device of the team interface");
+ netdev_err(dev, "Device %s is already an upper device of the team interface\n",
+ portname);
+ return -EBUSY;
+ }
+
+ if (netdev_has_upper_dev(port_dev, dev)) {
+ NL_SET_ERR_MSG(extack, "Device is already a lower device of the team interface");
+ netdev_err(dev, "Device %s is already a lower device of the team interface\n",
+ portname);
+ return -EBUSY;
+ }
+
+ if (port_dev->features & NETIF_F_VLAN_CHALLENGED &&
+ vlan_uses_dev(dev)) {
+ NL_SET_ERR_MSG(extack, "Device is VLAN challenged and team device has VLAN set up");
+ netdev_err(dev, "Device %s is VLAN challenged and team device has VLAN set up\n",
+ portname);
+ return -EPERM;
+ }
+
+ if (port_dev->flags & IFF_UP) {
+ NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
+ netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
+ portname);
+ return -EBUSY;
+ }
+
+ port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size,
+ GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->dev = port_dev;
+ port->team = team;
+ INIT_LIST_HEAD(&port->qom_list);
+
+ port->orig.mtu = port_dev->mtu;
+ /*
+ * MTU assignment will be handled in team_dev_type_check_change
+ * if dev and port_dev are of different types
+ */
+ if (dev->type == port_dev->type) {
+ err = dev_set_mtu(port_dev, dev->mtu);
+ if (err) {
+ netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
+ goto err_set_mtu;
+ }
+ }
+
+ memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
+
+ err = team_port_enter(team, port);
+ if (err) {
+ netdev_err(dev, "Device %s failed to enter team mode\n",
+ portname);
+ goto err_port_enter;
+ }
+
+ err = dev_open(port_dev, extack);
+ if (err) {
+ netdev_dbg(dev, "Device %s opening failed\n",
+ portname);
+ goto err_dev_open;
+ }
+
+ err = vlan_vids_add_by_dev(port_dev, dev);
+ if (err) {
+ netdev_err(dev, "Failed to add vlan ids to device %s\n",
+ portname);
+ goto err_vids_add;
+ }
+
+ err = team_port_enable_netpoll(port);
+ if (err) {
+ netdev_err(dev, "Failed to enable netpoll on device %s\n",
+ portname);
+ goto err_enable_netpoll;
+ }
+
+ if (!(dev->features & NETIF_F_LRO))
+ dev_disable_lro(port_dev);
+
+ err = netdev_rx_handler_register(port_dev, team_handle_frame,
+ port);
+ if (err) {
+ netdev_err(dev, "Device %s failed to register rx_handler\n",
+ portname);
+ goto err_handler_register;
+ }
+
+ err = team_upper_dev_link(team, port, extack);
+ if (err) {
+ netdev_err(dev, "Device %s failed to set upper link\n",
+ portname);
+ goto err_set_upper_link;
+ }
+
+ err = __team_option_inst_add_port(team, port);
+ if (err) {
+ netdev_err(dev, "Device %s failed to add per-port options\n",
+ portname);
+ goto err_option_port_add;
+ }
+
+ /* set promiscuity level to new slave */
+ if (dev->flags & IFF_PROMISC) {
+ err = dev_set_promiscuity(port_dev, 1);
+ if (err)
+ goto err_set_slave_promisc;
+ }
+
+ /* set allmulti level to new slave */
+ if (dev->flags & IFF_ALLMULTI) {
+ err = dev_set_allmulti(port_dev, 1);
+ if (err) {
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(port_dev, -1);
+ goto err_set_slave_promisc;
+ }
+ }
+
+ err = team_dev_type_check_change(dev, port_dev);
+ if (err)
+ goto err_set_dev_type;
+
+ if (dev->flags & IFF_UP) {
+ netif_addr_lock_bh(dev);
+ dev_uc_sync_multiple(port_dev, dev);
+ dev_mc_sync_multiple(port_dev, dev);
+ netif_addr_unlock_bh(dev);
+ }
+
+ port->index = -1;
+ list_add_tail_rcu(&port->list, &team->port_list);
+ team_port_enable(team, port);
+ __team_compute_features(team);
+ __team_port_change_port_added(port, !!netif_oper_up(port_dev));
+ __team_options_change_check(team);
+
+ netdev_info(dev, "Port device %s added\n", portname);
+
+ return 0;
+
+err_set_dev_type:
+err_set_slave_promisc:
+ __team_option_inst_del_port(team, port);
+
+err_option_port_add:
+ team_upper_dev_unlink(team, port);
+
+err_set_upper_link:
+ netdev_rx_handler_unregister(port_dev);
+
+err_handler_register:
+ team_port_disable_netpoll(port);
+
+err_enable_netpoll:
+ vlan_vids_del_by_dev(port_dev, dev);
+
+err_vids_add:
+ dev_close(port_dev);
+
+err_dev_open:
+ team_port_leave(team, port);
+ team_port_set_orig_dev_addr(port);
+
+err_port_enter:
+ dev_set_mtu(port_dev, port->orig.mtu);
+
+err_set_mtu:
+ kfree(port);
+
+ return err;
+}
+
+static void __team_port_change_port_removed(struct team_port *port);
+
+static int team_port_del(struct team *team, struct net_device *port_dev)
+{
+ struct net_device *dev = team->dev;
+ struct team_port *port;
+ char *portname = port_dev->name;
+
+ port = team_port_get_rtnl(port_dev);
+ if (!port || !team_port_find(team, port)) {
+ netdev_err(dev, "Device %s does not act as a port of this team\n",
+ portname);
+ return -ENOENT;
+ }
+
+ team_port_disable(team, port);
+ list_del_rcu(&port->list);
+
+ if (dev->flags & IFF_PROMISC)
+ dev_set_promiscuity(port_dev, -1);
+ if (dev->flags & IFF_ALLMULTI)
+ dev_set_allmulti(port_dev, -1);
+
+ team_upper_dev_unlink(team, port);
+ netdev_rx_handler_unregister(port_dev);
+ team_port_disable_netpoll(port);
+ vlan_vids_del_by_dev(port_dev, dev);
+ if (dev->flags & IFF_UP) {
+ dev_uc_unsync(port_dev, dev);
+ dev_mc_unsync(port_dev, dev);
+ }
+ dev_close(port_dev);
+ team_port_leave(team, port);
+
+ __team_option_inst_mark_removed_port(team, port);
+ __team_options_change_check(team);
+ __team_option_inst_del_port(team, port);
+ __team_port_change_port_removed(port);
+
+ team_port_set_orig_dev_addr(port);
+ dev_set_mtu(port_dev, port->orig.mtu);
+ kfree_rcu(port, rcu);
+ netdev_info(dev, "Port device %s removed\n", portname);
+ __team_compute_features(team);
+
+ return 0;
+}
+
+
+/*****************
+ * Net device ops
+ *****************/
+
+static void team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx)
+{
+ ctx->data.str_val = team->mode->kind;
+}
+
+static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx)
+{
+ return team_change_mode(team, ctx->data.str_val);
+}
+
+static void team_notify_peers_count_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ ctx->data.u32_val = team->notify_peers.count;
+}
+
+static int team_notify_peers_count_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ team->notify_peers.count = ctx->data.u32_val;
+ return 0;
+}
+
+static void team_notify_peers_interval_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ ctx->data.u32_val = team->notify_peers.interval;
+}
+
+static int team_notify_peers_interval_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ team->notify_peers.interval = ctx->data.u32_val;
+ return 0;
+}
+
+static void team_mcast_rejoin_count_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ ctx->data.u32_val = team->mcast_rejoin.count;
+}
+
+static int team_mcast_rejoin_count_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ team->mcast_rejoin.count = ctx->data.u32_val;
+ return 0;
+}
+
+static void team_mcast_rejoin_interval_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ ctx->data.u32_val = team->mcast_rejoin.interval;
+}
+
+static int team_mcast_rejoin_interval_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ team->mcast_rejoin.interval = ctx->data.u32_val;
+ return 0;
+}
+
+static void team_port_en_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.bool_val = team_port_enabled(port);
+}
+
+static int team_port_en_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ if (ctx->data.bool_val)
+ team_port_enable(team, port);
+ else
+ team_port_disable(team, port);
+ return 0;
+}
+
+static void team_user_linkup_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.bool_val = port->user.linkup;
+}
+
+static void __team_carrier_check(struct team *team);
+
+static int team_user_linkup_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ port->user.linkup = ctx->data.bool_val;
+ team_refresh_port_linkup(port);
+ __team_carrier_check(port->team);
+ return 0;
+}
+
+static void team_user_linkup_en_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.bool_val = port->user.linkup_enabled;
+}
+
+static int team_user_linkup_en_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ port->user.linkup_enabled = ctx->data.bool_val;
+ team_refresh_port_linkup(port);
+ __team_carrier_check(port->team);
+ return 0;
+}
+
+static void team_priority_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.s32_val = port->priority;
+}
+
+static int team_priority_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+ s32 priority = ctx->data.s32_val;
+
+ if (port->priority == priority)
+ return 0;
+ port->priority = priority;
+ team_queue_override_port_prio_changed(team, port);
+ return 0;
+}
+
+static void team_queue_id_option_get(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+
+ ctx->data.u32_val = port->queue_id;
+}
+
+static int team_queue_id_option_set(struct team *team,
+ struct team_gsetter_ctx *ctx)
+{
+ struct team_port *port = ctx->info->port;
+ u16 new_queue_id = ctx->data.u32_val;
+
+ if (port->queue_id == new_queue_id)
+ return 0;
+ if (new_queue_id >= team->dev->real_num_tx_queues)
+ return -EINVAL;
+ team_queue_override_port_change_queue_id(team, port, new_queue_id);
+ return 0;
+}
+
+static const struct team_option team_options[] = {
+ {
+ .name = "mode",
+ .type = TEAM_OPTION_TYPE_STRING,
+ .getter = team_mode_option_get,
+ .setter = team_mode_option_set,
+ },
+ {
+ .name = "notify_peers_count",
+ .type = TEAM_OPTION_TYPE_U32,
+ .getter = team_notify_peers_count_get,
+ .setter = team_notify_peers_count_set,
+ },
+ {
+ .name = "notify_peers_interval",
+ .type = TEAM_OPTION_TYPE_U32,
+ .getter = team_notify_peers_interval_get,
+ .setter = team_notify_peers_interval_set,
+ },
+ {
+ .name = "mcast_rejoin_count",
+ .type = TEAM_OPTION_TYPE_U32,
+ .getter = team_mcast_rejoin_count_get,
+ .setter = team_mcast_rejoin_count_set,
+ },
+ {
+ .name = "mcast_rejoin_interval",
+ .type = TEAM_OPTION_TYPE_U32,
+ .getter = team_mcast_rejoin_interval_get,
+ .setter = team_mcast_rejoin_interval_set,
+ },
+ {
+ .name = "enabled",
+ .type = TEAM_OPTION_TYPE_BOOL,
+ .per_port = true,
+ .getter = team_port_en_option_get,
+ .setter = team_port_en_option_set,
+ },
+ {
+ .name = "user_linkup",
+ .type = TEAM_OPTION_TYPE_BOOL,
+ .per_port = true,
+ .getter = team_user_linkup_option_get,
+ .setter = team_user_linkup_option_set,
+ },
+ {
+ .name = "user_linkup_enabled",
+ .type = TEAM_OPTION_TYPE_BOOL,
+ .per_port = true,
+ .getter = team_user_linkup_en_option_get,
+ .setter = team_user_linkup_en_option_set,
+ },
+ {
+ .name = "priority",
+ .type = TEAM_OPTION_TYPE_S32,
+ .per_port = true,
+ .getter = team_priority_option_get,
+ .setter = team_priority_option_set,
+ },
+ {
+ .name = "queue_id",
+ .type = TEAM_OPTION_TYPE_U32,
+ .per_port = true,
+ .getter = team_queue_id_option_get,
+ .setter = team_queue_id_option_set,
+ },
+};
+
+
+static int team_init(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+ int i;
+ int err;
+
+ team->dev = dev;
+ team_set_no_mode(team);
+ team->notifier_ctx = false;
+
+ team->pcpu_stats = netdev_alloc_pcpu_stats(struct team_pcpu_stats);
+ if (!team->pcpu_stats)
+ return -ENOMEM;
+
+ for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
+ INIT_HLIST_HEAD(&team->en_port_hlist[i]);
+ INIT_LIST_HEAD(&team->port_list);
+ err = team_queue_override_init(team);
+ if (err)
+ goto err_team_queue_override_init;
+
+ team_adjust_ops(team);
+
+ INIT_LIST_HEAD(&team->option_list);
+ INIT_LIST_HEAD(&team->option_inst_list);
+
+ team_notify_peers_init(team);
+ team_mcast_rejoin_init(team);
+
+ err = team_options_register(team, team_options, ARRAY_SIZE(team_options));
+ if (err)
+ goto err_options_register;
+ netif_carrier_off(dev);
+
+ lockdep_register_key(&team->team_lock_key);
+ __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
+ netdev_lockdep_set_classes(dev);
+
+ return 0;
+
+err_options_register:
+ team_mcast_rejoin_fini(team);
+ team_notify_peers_fini(team);
+ team_queue_override_fini(team);
+err_team_queue_override_init:
+ free_percpu(team->pcpu_stats);
+
+ return err;
+}
+
+static void team_uninit(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+ struct team_port *tmp;
+
+ mutex_lock(&team->lock);
+ list_for_each_entry_safe(port, tmp, &team->port_list, list)
+ team_port_del(team, port->dev);
+
+ __team_change_mode(team, NULL); /* cleanup */
+ __team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
+ team_mcast_rejoin_fini(team);
+ team_notify_peers_fini(team);
+ team_queue_override_fini(team);
+ mutex_unlock(&team->lock);
+ netdev_change_features(dev);
+ lockdep_unregister_key(&team->team_lock_key);
+}
+
+static void team_destructor(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+
+ free_percpu(team->pcpu_stats);
+}
+
+static int team_open(struct net_device *dev)
+{
+ return 0;
+}
+
+static int team_close(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+
+ list_for_each_entry(port, &team->port_list, list) {
+ dev_uc_unsync(port->dev, dev);
+ dev_mc_unsync(port->dev, dev);
+ }
+
+ return 0;
+}
+
+/*
+ * note: already called with rcu_read_lock
+ */
+static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+ bool tx_success;
+ unsigned int len = skb->len;
+
+ tx_success = team_queue_override_transmit(team, skb);
+ if (!tx_success)
+ tx_success = team->ops.transmit(team, skb);
+ if (tx_success) {
+ struct team_pcpu_stats *pcpu_stats;
+
+ pcpu_stats = this_cpu_ptr(team->pcpu_stats);
+ u64_stats_update_begin(&pcpu_stats->syncp);
+ u64_stats_inc(&pcpu_stats->tx_packets);
+ u64_stats_add(&pcpu_stats->tx_bytes, len);
+ u64_stats_update_end(&pcpu_stats->syncp);
+ } else {
+ this_cpu_inc(team->pcpu_stats->tx_dropped);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static u16 team_select_queue(struct net_device *dev, struct sk_buff *skb,
+ struct net_device *sb_dev)
+{
+ /*
+ * This helper function exists to help dev_pick_tx get the correct
+ * destination queue. Using a helper function skips a call to
+ * skb_tx_hash and will put the skbs in the queue we expect on their
+ * way down to the team driver.
+ */
+ u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0;
+
+ /*
+ * Save the original txq to restore before passing to the driver
+ */
+ qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping;
+
+ if (unlikely(txq >= dev->real_num_tx_queues)) {
+ do {
+ txq -= dev->real_num_tx_queues;
+ } while (txq >= dev->real_num_tx_queues);
+ }
+ return txq;
+}
+
+static void team_change_rx_flags(struct net_device *dev, int change)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+ int inc;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ if (change & IFF_PROMISC) {
+ inc = dev->flags & IFF_PROMISC ? 1 : -1;
+ dev_set_promiscuity(port->dev, inc);
+ }
+ if (change & IFF_ALLMULTI) {
+ inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
+ dev_set_allmulti(port->dev, inc);
+ }
+ }
+ rcu_read_unlock();
+}
+
+static void team_set_rx_mode(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ dev_uc_sync_multiple(port->dev, dev);
+ dev_mc_sync_multiple(port->dev, dev);
+ }
+ rcu_read_unlock();
+}
+
+static int team_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+
+ if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ dev_addr_set(dev, addr->sa_data);
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list)
+ if (team->ops.port_change_dev_addr)
+ team->ops.port_change_dev_addr(team, port);
+ mutex_unlock(&team->lock);
+ return 0;
+}
+
+static int team_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+ int err;
+
+ /*
+ * Alhough this is reader, it's guarded by team lock. It's not possible
+ * to traverse list in reverse under rcu_read_lock
+ */
+ mutex_lock(&team->lock);
+ team->port_mtu_change_allowed = true;
+ list_for_each_entry(port, &team->port_list, list) {
+ err = dev_set_mtu(port->dev, new_mtu);
+ if (err) {
+ netdev_err(dev, "Device %s failed to change mtu",
+ port->dev->name);
+ goto unwind;
+ }
+ }
+ team->port_mtu_change_allowed = false;
+ mutex_unlock(&team->lock);
+
+ dev->mtu = new_mtu;
+
+ return 0;
+
+unwind:
+ list_for_each_entry_continue_reverse(port, &team->port_list, list)
+ dev_set_mtu(port->dev, dev->mtu);
+ team->port_mtu_change_allowed = false;
+ mutex_unlock(&team->lock);
+
+ return err;
+}
+
+static void
+team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_pcpu_stats *p;
+ u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
+ u32 rx_dropped = 0, tx_dropped = 0, rx_nohandler = 0;
+ unsigned int start;
+ int i;
+
+ for_each_possible_cpu(i) {
+ p = per_cpu_ptr(team->pcpu_stats, i);
+ do {
+ start = u64_stats_fetch_begin(&p->syncp);
+ rx_packets = u64_stats_read(&p->rx_packets);
+ rx_bytes = u64_stats_read(&p->rx_bytes);
+ rx_multicast = u64_stats_read(&p->rx_multicast);
+ tx_packets = u64_stats_read(&p->tx_packets);
+ tx_bytes = u64_stats_read(&p->tx_bytes);
+ } while (u64_stats_fetch_retry(&p->syncp, start));
+
+ stats->rx_packets += rx_packets;
+ stats->rx_bytes += rx_bytes;
+ stats->multicast += rx_multicast;
+ stats->tx_packets += tx_packets;
+ stats->tx_bytes += tx_bytes;
+ /*
+ * rx_dropped, tx_dropped & rx_nohandler are u32,
+ * updated without syncp protection.
+ */
+ rx_dropped += READ_ONCE(p->rx_dropped);
+ tx_dropped += READ_ONCE(p->tx_dropped);
+ rx_nohandler += READ_ONCE(p->rx_nohandler);
+ }
+ stats->rx_dropped = rx_dropped;
+ stats->tx_dropped = tx_dropped;
+ stats->rx_nohandler = rx_nohandler;
+}
+
+static int team_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+ int err;
+
+ /*
+ * Alhough this is reader, it's guarded by team lock. It's not possible
+ * to traverse list in reverse under rcu_read_lock
+ */
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list) {
+ err = vlan_vid_add(port->dev, proto, vid);
+ if (err)
+ goto unwind;
+ }
+ mutex_unlock(&team->lock);
+
+ return 0;
+
+unwind:
+ list_for_each_entry_continue_reverse(port, &team->port_list, list)
+ vlan_vid_del(port->dev, proto, vid);
+ mutex_unlock(&team->lock);
+
+ return err;
+}
+
+static int team_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list)
+ vlan_vid_del(port->dev, proto, vid);
+ mutex_unlock(&team->lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void team_poll_controller(struct net_device *dev)
+{
+}
+
+static void __team_netpoll_cleanup(struct team *team)
+{
+ struct team_port *port;
+
+ list_for_each_entry(port, &team->port_list, list)
+ team_port_disable_netpoll(port);
+}
+
+static void team_netpoll_cleanup(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+
+ mutex_lock(&team->lock);
+ __team_netpoll_cleanup(team);
+ mutex_unlock(&team->lock);
+}
+
+static int team_netpoll_setup(struct net_device *dev,
+ struct netpoll_info *npifo)
+{
+ struct team *team = netdev_priv(dev);
+ struct team_port *port;
+ int err = 0;
+
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list) {
+ err = __team_port_enable_netpoll(port);
+ if (err) {
+ __team_netpoll_cleanup(team);
+ break;
+ }
+ }
+ mutex_unlock(&team->lock);
+ return err;
+}
+#endif
+
+static int team_add_slave(struct net_device *dev, struct net_device *port_dev,
+ struct netlink_ext_ack *extack)
+{
+ struct team *team = netdev_priv(dev);
+ int err;
+
+ mutex_lock(&team->lock);
+ err = team_port_add(team, port_dev, extack);
+ mutex_unlock(&team->lock);
+
+ if (!err)
+ netdev_change_features(dev);
+
+ return err;
+}
+
+static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
+{
+ struct team *team = netdev_priv(dev);
+ int err;
+
+ mutex_lock(&team->lock);
+ err = team_port_del(team, port_dev);
+ mutex_unlock(&team->lock);
+
+ if (err)
+ return err;
+
+ if (netif_is_team_master(port_dev)) {
+ lockdep_unregister_key(&team->team_lock_key);
+ lockdep_register_key(&team->team_lock_key);
+ lockdep_set_class(&team->lock, &team->team_lock_key);
+ }
+ netdev_change_features(dev);
+
+ return err;
+}
+
+static netdev_features_t team_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct team_port *port;
+ struct team *team = netdev_priv(dev);
+ netdev_features_t mask;
+
+ mask = features;
+ features = netdev_base_features(features);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ features = netdev_increment_features(features,
+ port->dev->features,
+ mask);
+ }
+ rcu_read_unlock();
+
+ features = netdev_add_tso_features(features, mask);
+
+ return features;
+}
+
+static int team_change_carrier(struct net_device *dev, bool new_carrier)
+{
+ struct team *team = netdev_priv(dev);
+
+ team->user_carrier_enabled = true;
+
+ if (new_carrier)
+ netif_carrier_on(dev);
+ else
+ netif_carrier_off(dev);
+ return 0;
+}
+
+static const struct net_device_ops team_netdev_ops = {
+ .ndo_init = team_init,
+ .ndo_uninit = team_uninit,
+ .ndo_open = team_open,
+ .ndo_stop = team_close,
+ .ndo_start_xmit = team_xmit,
+ .ndo_select_queue = team_select_queue,
+ .ndo_change_rx_flags = team_change_rx_flags,
+ .ndo_set_rx_mode = team_set_rx_mode,
+ .ndo_set_mac_address = team_set_mac_address,
+ .ndo_change_mtu = team_change_mtu,
+ .ndo_get_stats64 = team_get_stats64,
+ .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = team_poll_controller,
+ .ndo_netpoll_setup = team_netpoll_setup,
+ .ndo_netpoll_cleanup = team_netpoll_cleanup,
+#endif
+ .ndo_add_slave = team_add_slave,
+ .ndo_del_slave = team_del_slave,
+ .ndo_fix_features = team_fix_features,
+ .ndo_change_carrier = team_change_carrier,
+ .ndo_features_check = passthru_features_check,
+};
+
+/***********************
+ * ethtool interface
+ ***********************/
+
+static void team_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
+ strscpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
+}
+
+static int team_ethtool_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct team *team= netdev_priv(dev);
+ unsigned long speed = 0;
+ struct team_port *port;
+
+ cmd->base.duplex = DUPLEX_UNKNOWN;
+ cmd->base.port = PORT_OTHER;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ if (team_port_txable(port)) {
+ if (port->state.speed != SPEED_UNKNOWN)
+ speed += port->state.speed;
+ if (cmd->base.duplex == DUPLEX_UNKNOWN &&
+ port->state.duplex != DUPLEX_UNKNOWN)
+ cmd->base.duplex = port->state.duplex;
+ }
+ }
+ rcu_read_unlock();
+
+ cmd->base.speed = speed ? : SPEED_UNKNOWN;
+
+ return 0;
+}
+
+static const struct ethtool_ops team_ethtool_ops = {
+ .get_drvinfo = team_ethtool_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = team_ethtool_get_link_ksettings,
+};
+
+/***********************
+ * rt netlink interface
+ ***********************/
+
+/* For tx path we need a linkup && enabled port and for parse any port
+ * suffices.
+ */
+static struct team_port *team_header_port_get_rcu(struct team *team,
+ bool txable)
+{
+ struct team_port *port;
+
+ list_for_each_entry_rcu(port, &team->port_list, list) {
+ if (!txable || team_port_txable(port))
+ return port;
+ }
+
+ return NULL;
+}
+
+static int team_header_create(struct sk_buff *skb, struct net_device *team_dev,
+ unsigned short type, const void *daddr,
+ const void *saddr, unsigned int len)
+{
+ struct team *team = netdev_priv(team_dev);
+ const struct header_ops *port_ops;
+ struct team_port *port;
+ int ret = 0;
+
+ rcu_read_lock();
+ port = team_header_port_get_rcu(team, true);
+ if (port) {
+ port_ops = READ_ONCE(port->dev->header_ops);
+ if (port_ops && port_ops->create)
+ ret = port_ops->create(skb, port->dev,
+ type, daddr, saddr, len);
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+static int team_header_parse(const struct sk_buff *skb,
+ const struct net_device *team_dev,
+ unsigned char *haddr)
+{
+ struct team *team = netdev_priv(team_dev);
+ const struct header_ops *port_ops;
+ struct team_port *port;
+ int ret = 0;
+
+ rcu_read_lock();
+ port = team_header_port_get_rcu(team, false);
+ if (port) {
+ port_ops = READ_ONCE(port->dev->header_ops);
+ if (port_ops && port_ops->parse)
+ ret = port_ops->parse(skb, port->dev, haddr);
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+static const struct header_ops team_header_ops = {
+ .create = team_header_create,
+ .parse = team_header_parse,
+};
+
+static void team_setup_by_port(struct net_device *dev,
+ struct net_device *port_dev)
+{
+ struct team *team = netdev_priv(dev);
+
+ if (port_dev->type == ARPHRD_ETHER)
+ dev->header_ops = team->header_ops_cache;
+ else
+ dev->header_ops = port_dev->header_ops ?
+ &team_header_ops : NULL;
+ dev->type = port_dev->type;
+ dev->hard_header_len = port_dev->hard_header_len;
+ dev->needed_headroom = port_dev->needed_headroom;
+ dev->addr_len = port_dev->addr_len;
+ dev->mtu = port_dev->mtu;
+ memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len);
+ eth_hw_addr_inherit(dev, port_dev);
+
+ if (port_dev->flags & IFF_POINTOPOINT) {
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ dev->flags |= (IFF_POINTOPOINT | IFF_NOARP);
+ } else if ((port_dev->flags & (IFF_BROADCAST | IFF_MULTICAST)) ==
+ (IFF_BROADCAST | IFF_MULTICAST)) {
+ dev->flags |= (IFF_BROADCAST | IFF_MULTICAST);
+ dev->flags &= ~(IFF_POINTOPOINT | IFF_NOARP);
+ }
+}
+
+static int team_dev_type_check_change(struct net_device *dev,
+ struct net_device *port_dev)
+{
+ struct team *team = netdev_priv(dev);
+ char *portname = port_dev->name;
+ int err;
+
+ if (dev->type == port_dev->type)
+ return 0;
+ if (!list_empty(&team->port_list)) {
+ netdev_err(dev, "Device %s is of different type\n", portname);
+ return -EBUSY;
+ }
+ err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
+ err = notifier_to_errno(err);
+ if (err) {
+ netdev_err(dev, "Refused to change device type\n");
+ return err;
+ }
+ dev_uc_flush(dev);
+ dev_mc_flush(dev);
+ team_setup_by_port(dev, port_dev);
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
+ return 0;
+}
+
+static void team_setup(struct net_device *dev)
+{
+ struct team *team = netdev_priv(dev);
+
+ ether_setup(dev);
+ dev->max_mtu = ETH_MAX_MTU;
+ team->header_ops_cache = dev->header_ops;
+
+ dev->netdev_ops = &team_netdev_ops;
+ dev->ethtool_ops = &team_ethtool_ops;
+ dev->needs_free_netdev = true;
+ dev->priv_destructor = team_destructor;
+ dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
+ dev->priv_flags |= IFF_NO_QUEUE;
+ dev->priv_flags |= IFF_TEAM;
+
+ /*
+ * Indicate we support unicast address filtering. That way core won't
+ * bring us to promisc mode in case a unicast addr is added.
+ * Let this up to underlay drivers.
+ */
+ dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE;
+
+ dev->features |= NETIF_F_LLTX;
+ dev->features |= NETIF_F_GRO;
+
+ /* Don't allow team devices to change network namespaces. */
+ dev->features |= NETIF_F_NETNS_LOCAL;
+
+ dev->hw_features = TEAM_VLAN_FEATURES |
+ NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_RX |
+ NETIF_F_HW_VLAN_STAG_FILTER;
+
+ dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
+ dev->features |= dev->hw_features;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
+}
+
+static int team_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ if (tb[IFLA_ADDRESS] == NULL)
+ eth_hw_addr_random(dev);
+
+ return register_netdevice(dev);
+}
+
+static int team_validate(struct nlattr *tb[], struct nlattr *data[],
+ struct netlink_ext_ack *extack)
+{
+ if (tb[IFLA_ADDRESS]) {
+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+ return -EINVAL;
+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+ return -EADDRNOTAVAIL;
+ }
+ return 0;
+}
+
+static unsigned int team_get_num_tx_queues(void)
+{
+ return TEAM_DEFAULT_NUM_TX_QUEUES;
+}
+
+static unsigned int team_get_num_rx_queues(void)
+{
+ return TEAM_DEFAULT_NUM_RX_QUEUES;
+}
+
+static struct rtnl_link_ops team_link_ops __read_mostly = {
+ .kind = DRV_NAME,
+ .priv_size = sizeof(struct team),
+ .setup = team_setup,
+ .newlink = team_newlink,
+ .validate = team_validate,
+ .get_num_tx_queues = team_get_num_tx_queues,
+ .get_num_rx_queues = team_get_num_rx_queues,
+};
+
+
+/***********************************
+ * Generic netlink custom interface
+ ***********************************/
+
+static struct genl_family team_nl_family;
+
+static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
+ [TEAM_ATTR_UNSPEC] = { .type = NLA_UNSPEC, },
+ [TEAM_ATTR_TEAM_IFINDEX] = { .type = NLA_U32 },
+ [TEAM_ATTR_LIST_OPTION] = { .type = NLA_NESTED },
+ [TEAM_ATTR_LIST_PORT] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
+ [TEAM_ATTR_OPTION_UNSPEC] = { .type = NLA_UNSPEC, },
+ [TEAM_ATTR_OPTION_NAME] = {
+ .type = NLA_STRING,
+ .len = TEAM_STRING_MAX_LEN,
+ },
+ [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG },
+ [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
+ [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY },
+ [TEAM_ATTR_OPTION_PORT_IFINDEX] = { .type = NLA_U32 },
+ [TEAM_ATTR_OPTION_ARRAY_INDEX] = { .type = NLA_U32 },
+};
+
+static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+ &team_nl_family, 0, TEAM_CMD_NOOP);
+ if (!hdr) {
+ err = -EMSGSIZE;
+ goto err_msg_put;
+ }
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
+
+err_msg_put:
+ nlmsg_free(msg);
+
+ return err;
+}
+
+/*
+ * Netlink cmd functions should be locked by following two functions.
+ * Since dev gets held here, that ensures dev won't disappear in between.
+ */
+static struct team *team_nl_team_get(struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ int ifindex;
+ struct net_device *dev;
+ struct team *team;
+
+ if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
+ return NULL;
+
+ ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
+ dev = dev_get_by_index(net, ifindex);
+ if (!dev || dev->netdev_ops != &team_netdev_ops) {
+ dev_put(dev);
+ return NULL;
+ }
+
+ team = netdev_priv(dev);
+ mutex_lock(&team->lock);
+ return team;
+}
+
+static void team_nl_team_put(struct team *team)
+{
+ mutex_unlock(&team->lock);
+ dev_put(team->dev);
+}
+
+typedef int team_nl_send_func_t(struct sk_buff *skb,
+ struct team *team, u32 portid);
+
+static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 portid)
+{
+ return genlmsg_unicast(dev_net(team->dev), skb, portid);
+}
+
+static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team,
+ struct team_option_inst *opt_inst)
+{
+ struct nlattr *option_item;
+ struct team_option *option = opt_inst->option;
+ struct team_option_inst_info *opt_inst_info = &opt_inst->info;
+ struct team_gsetter_ctx ctx;
+ int err;
+
+ ctx.info = opt_inst_info;
+ err = team_option_get(team, opt_inst, &ctx);
+ if (err)
+ return err;
+
+ option_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_OPTION);
+ if (!option_item)
+ return -EMSGSIZE;
+
+ if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name))
+ goto nest_cancel;
+ if (opt_inst_info->port &&
+ nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX,
+ opt_inst_info->port->dev->ifindex))
+ goto nest_cancel;
+ if (opt_inst->option->array_size &&
+ nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX,
+ opt_inst_info->array_index))
+ goto nest_cancel;
+
+ switch (option->type) {
+ case TEAM_OPTION_TYPE_U32:
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32))
+ goto nest_cancel;
+ if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val))
+ goto nest_cancel;
+ break;
+ case TEAM_OPTION_TYPE_STRING:
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING))
+ goto nest_cancel;
+ if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA,
+ ctx.data.str_val))
+ goto nest_cancel;
+ break;
+ case TEAM_OPTION_TYPE_BINARY:
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY))
+ goto nest_cancel;
+ if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len,
+ ctx.data.bin_val.ptr))
+ goto nest_cancel;
+ break;
+ case TEAM_OPTION_TYPE_BOOL:
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG))
+ goto nest_cancel;
+ if (ctx.data.bool_val &&
+ nla_put_flag(skb, TEAM_ATTR_OPTION_DATA))
+ goto nest_cancel;
+ break;
+ case TEAM_OPTION_TYPE_S32:
+ if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_S32))
+ goto nest_cancel;
+ if (nla_put_s32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.s32_val))
+ goto nest_cancel;
+ break;
+ default:
+ BUG();
+ }
+ if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED))
+ goto nest_cancel;
+ if (opt_inst->changed) {
+ if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED))
+ goto nest_cancel;
+ opt_inst->changed = false;
+ }
+ nla_nest_end(skb, option_item);
+ return 0;
+
+nest_cancel:
+ nla_nest_cancel(skb, option_item);
+ return -EMSGSIZE;
+}
+
+static int __send_and_alloc_skb(struct sk_buff **pskb,
+ struct team *team, u32 portid,
+ team_nl_send_func_t *send_func)
+{
+ int err;
+
+ if (*pskb) {
+ err = send_func(*pskb, team, portid);
+ if (err)
+ return err;
+ }
+ *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!*pskb)
+ return -ENOMEM;
+ return 0;
+}
+
+static int team_nl_send_options_get(struct team *team, u32 portid, u32 seq,
+ int flags, team_nl_send_func_t *send_func,
+ struct list_head *sel_opt_inst_list)
+{
+ struct nlattr *option_list;
+ struct nlmsghdr *nlh;
+ void *hdr;
+ struct team_option_inst *opt_inst;
+ int err;
+ struct sk_buff *skb = NULL;
+ bool incomplete;
+ int i;
+
+ opt_inst = list_first_entry(sel_opt_inst_list,
+ struct team_option_inst, tmp_list);
+
+start_again:
+ err = __send_and_alloc_skb(&skb, team, portid, send_func);
+ if (err)
+ return err;
+
+ hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
+ TEAM_CMD_OPTIONS_GET);
+ if (!hdr) {
+ nlmsg_free(skb);
+ return -EMSGSIZE;
+ }
+
+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
+ goto nla_put_failure;
+ option_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_OPTION);
+ if (!option_list)
+ goto nla_put_failure;
+
+ i = 0;
+ incomplete = false;
+ list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) {
+ err = team_nl_fill_one_option_get(skb, team, opt_inst);
+ if (err) {
+ if (err == -EMSGSIZE) {
+ if (!i)
+ goto errout;
+ incomplete = true;
+ break;
+ }
+ goto errout;
+ }
+ i++;
+ }
+
+ nla_nest_end(skb, option_list);
+ genlmsg_end(skb, hdr);
+ if (incomplete)
+ goto start_again;
+
+send_done:
+ nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
+ if (!nlh) {
+ err = __send_and_alloc_skb(&skb, team, portid, send_func);
+ if (err)
+ return err;
+ goto send_done;
+ }
+
+ return send_func(skb, team, portid);
+
+nla_put_failure:
+ err = -EMSGSIZE;
+errout:
+ nlmsg_free(skb);
+ return err;
+}
+
+static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
+{
+ struct team *team;
+ struct team_option_inst *opt_inst;
+ int err;
+ LIST_HEAD(sel_opt_inst_list);
+
+ team = team_nl_team_get(info);
+ if (!team)
+ return -EINVAL;
+
+ list_for_each_entry(opt_inst, &team->option_inst_list, list)
+ list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
+ err = team_nl_send_options_get(team, info->snd_portid, info->snd_seq,
+ NLM_F_ACK, team_nl_send_unicast,
+ &sel_opt_inst_list);
+
+ team_nl_team_put(team);
+
+ return err;
+}
+
+static int team_nl_send_event_options_get(struct team *team,
+ struct list_head *sel_opt_inst_list);
+
+static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
+{
+ struct team *team;
+ int err = 0;
+ int i;
+ struct nlattr *nl_option;
+
+ rtnl_lock();
+
+ team = team_nl_team_get(info);
+ if (!team) {
+ err = -EINVAL;
+ goto rtnl_unlock;
+ }
+
+ err = -EINVAL;
+ if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
+ err = -EINVAL;
+ goto team_put;
+ }
+
+ nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
+ struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1];
+ struct nlattr *attr;
+ struct nlattr *attr_data;
+ LIST_HEAD(opt_inst_list);
+ enum team_option_type opt_type;
+ int opt_port_ifindex = 0; /* != 0 for per-port options */
+ u32 opt_array_index = 0;
+ bool opt_is_array = false;
+ struct team_option_inst *opt_inst;
+ char *opt_name;
+ bool opt_found = false;
+
+ if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
+ err = -EINVAL;
+ goto team_put;
+ }
+ err = nla_parse_nested_deprecated(opt_attrs,
+ TEAM_ATTR_OPTION_MAX,
+ nl_option,
+ team_nl_option_policy,
+ info->extack);
+ if (err)
+ goto team_put;
+ if (!opt_attrs[TEAM_ATTR_OPTION_NAME] ||
+ !opt_attrs[TEAM_ATTR_OPTION_TYPE]) {
+ err = -EINVAL;
+ goto team_put;
+ }
+ switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) {
+ case NLA_U32:
+ opt_type = TEAM_OPTION_TYPE_U32;
+ break;
+ case NLA_STRING:
+ opt_type = TEAM_OPTION_TYPE_STRING;
+ break;
+ case NLA_BINARY:
+ opt_type = TEAM_OPTION_TYPE_BINARY;
+ break;
+ case NLA_FLAG:
+ opt_type = TEAM_OPTION_TYPE_BOOL;
+ break;
+ case NLA_S32:
+ opt_type = TEAM_OPTION_TYPE_S32;
+ break;
+ default:
+ goto team_put;
+ }
+
+ attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA];
+ if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) {
+ err = -EINVAL;
+ goto team_put;
+ }
+
+ opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]);
+ attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX];
+ if (attr)
+ opt_port_ifindex = nla_get_u32(attr);
+
+ attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX];
+ if (attr) {
+ opt_is_array = true;
+ opt_array_index = nla_get_u32(attr);
+ }
+
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ struct team_option *option = opt_inst->option;
+ struct team_gsetter_ctx ctx;
+ struct team_option_inst_info *opt_inst_info;
+ int tmp_ifindex;
+
+ opt_inst_info = &opt_inst->info;
+ tmp_ifindex = opt_inst_info->port ?
+ opt_inst_info->port->dev->ifindex : 0;
+ if (option->type != opt_type ||
+ strcmp(option->name, opt_name) ||
+ tmp_ifindex != opt_port_ifindex ||
+ (option->array_size && !opt_is_array) ||
+ opt_inst_info->array_index != opt_array_index)
+ continue;
+ opt_found = true;
+ ctx.info = opt_inst_info;
+ switch (opt_type) {
+ case TEAM_OPTION_TYPE_U32:
+ ctx.data.u32_val = nla_get_u32(attr_data);
+ break;
+ case TEAM_OPTION_TYPE_STRING:
+ if (nla_len(attr_data) > TEAM_STRING_MAX_LEN ||
+ !memchr(nla_data(attr_data), '\0',
+ nla_len(attr_data))) {
+ err = -EINVAL;
+ goto team_put;
+ }
+ ctx.data.str_val = nla_data(attr_data);
+ break;
+ case TEAM_OPTION_TYPE_BINARY:
+ ctx.data.bin_val.len = nla_len(attr_data);
+ ctx.data.bin_val.ptr = nla_data(attr_data);
+ break;
+ case TEAM_OPTION_TYPE_BOOL:
+ ctx.data.bool_val = attr_data ? true : false;
+ break;
+ case TEAM_OPTION_TYPE_S32:
+ ctx.data.s32_val = nla_get_s32(attr_data);
+ break;
+ default:
+ BUG();
+ }
+ err = team_option_set(team, opt_inst, &ctx);
+ if (err)
+ goto team_put;
+ opt_inst->changed = true;
+ list_add(&opt_inst->tmp_list, &opt_inst_list);
+ }
+ if (!opt_found) {
+ err = -ENOENT;
+ goto team_put;
+ }
+
+ err = team_nl_send_event_options_get(team, &opt_inst_list);
+ if (err)
+ break;
+ }
+
+team_put:
+ team_nl_team_put(team);
+rtnl_unlock:
+ rtnl_unlock();
+ return err;
+}
+
+static int team_nl_fill_one_port_get(struct sk_buff *skb,
+ struct team_port *port)
+{
+ struct nlattr *port_item;
+
+ port_item = nla_nest_start_noflag(skb, TEAM_ATTR_ITEM_PORT);
+ if (!port_item)
+ goto nest_cancel;
+ if (nla_put_u32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex))
+ goto nest_cancel;
+ if (port->changed) {
+ if (nla_put_flag(skb, TEAM_ATTR_PORT_CHANGED))
+ goto nest_cancel;
+ port->changed = false;
+ }
+ if ((port->removed &&
+ nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) ||
+ (port->state.linkup &&
+ nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) ||
+ nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) ||
+ nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex))
+ goto nest_cancel;
+ nla_nest_end(skb, port_item);
+ return 0;
+
+nest_cancel:
+ nla_nest_cancel(skb, port_item);
+ return -EMSGSIZE;
+}
+
+static int team_nl_send_port_list_get(struct team *team, u32 portid, u32 seq,
+ int flags, team_nl_send_func_t *send_func,
+ struct team_port *one_port)
+{
+ struct nlattr *port_list;
+ struct nlmsghdr *nlh;
+ void *hdr;
+ struct team_port *port;
+ int err;
+ struct sk_buff *skb = NULL;
+ bool incomplete;
+ int i;
+
+ port = list_first_entry_or_null(&team->port_list,
+ struct team_port, list);
+
+start_again:
+ err = __send_and_alloc_skb(&skb, team, portid, send_func);
+ if (err)
+ return err;
+
+ hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
+ TEAM_CMD_PORT_LIST_GET);
+ if (!hdr) {
+ nlmsg_free(skb);
+ return -EMSGSIZE;
+ }
+
+ if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
+ goto nla_put_failure;
+ port_list = nla_nest_start_noflag(skb, TEAM_ATTR_LIST_PORT);
+ if (!port_list)
+ goto nla_put_failure;
+
+ i = 0;
+ incomplete = false;
+
+ /* If one port is selected, called wants to send port list containing
+ * only this port. Otherwise go through all listed ports and send all
+ */
+ if (one_port) {
+ err = team_nl_fill_one_port_get(skb, one_port);
+ if (err)
+ goto errout;
+ } else if (port) {
+ list_for_each_entry_from(port, &team->port_list, list) {
+ err = team_nl_fill_one_port_get(skb, port);
+ if (err) {
+ if (err == -EMSGSIZE) {
+ if (!i)
+ goto errout;
+ incomplete = true;
+ break;
+ }
+ goto errout;
+ }
+ i++;
+ }
+ }
+
+ nla_nest_end(skb, port_list);
+ genlmsg_end(skb, hdr);
+ if (incomplete)
+ goto start_again;
+
+send_done:
+ nlh = nlmsg_put(skb, portid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI);
+ if (!nlh) {
+ err = __send_and_alloc_skb(&skb, team, portid, send_func);
+ if (err)
+ return err;
+ goto send_done;
+ }
+
+ return send_func(skb, team, portid);
+
+nla_put_failure:
+ err = -EMSGSIZE;
+errout:
+ nlmsg_free(skb);
+ return err;
+}
+
+static int team_nl_cmd_port_list_get(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct team *team;
+ int err;
+
+ team = team_nl_team_get(info);
+ if (!team)
+ return -EINVAL;
+
+ err = team_nl_send_port_list_get(team, info->snd_portid, info->snd_seq,
+ NLM_F_ACK, team_nl_send_unicast, NULL);
+
+ team_nl_team_put(team);
+
+ return err;
+}
+
+static const struct genl_small_ops team_nl_ops[] = {
+ {
+ .cmd = TEAM_CMD_NOOP,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = team_nl_cmd_noop,
+ },
+ {
+ .cmd = TEAM_CMD_OPTIONS_SET,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = team_nl_cmd_options_set,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = TEAM_CMD_OPTIONS_GET,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = team_nl_cmd_options_get,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = TEAM_CMD_PORT_LIST_GET,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = team_nl_cmd_port_list_get,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+static const struct genl_multicast_group team_nl_mcgrps[] = {
+ { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, },
+};
+
+static struct genl_family team_nl_family __ro_after_init = {
+ .name = TEAM_GENL_NAME,
+ .version = TEAM_GENL_VERSION,
+ .maxattr = TEAM_ATTR_MAX,
+ .policy = team_nl_policy,
+ .netnsok = true,
+ .module = THIS_MODULE,
+ .small_ops = team_nl_ops,
+ .n_small_ops = ARRAY_SIZE(team_nl_ops),
+ .resv_start_op = TEAM_CMD_PORT_LIST_GET + 1,
+ .mcgrps = team_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(team_nl_mcgrps),
+};
+
+static int team_nl_send_multicast(struct sk_buff *skb,
+ struct team *team, u32 portid)
+{
+ return genlmsg_multicast_netns(&team_nl_family, dev_net(team->dev),
+ skb, 0, 0, GFP_KERNEL);
+}
+
+static int team_nl_send_event_options_get(struct team *team,
+ struct list_head *sel_opt_inst_list)
+{
+ return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast,
+ sel_opt_inst_list);
+}
+
+static int team_nl_send_event_port_get(struct team *team,
+ struct team_port *port)
+{
+ return team_nl_send_port_list_get(team, 0, 0, 0, team_nl_send_multicast,
+ port);
+}
+
+static int __init team_nl_init(void)
+{
+ return genl_register_family(&team_nl_family);
+}
+
+static void __exit team_nl_fini(void)
+{
+ genl_unregister_family(&team_nl_family);
+}
+
+
+/******************
+ * Change checkers
+ ******************/
+
+static void __team_options_change_check(struct team *team)
+{
+ int err;
+ struct team_option_inst *opt_inst;
+ LIST_HEAD(sel_opt_inst_list);
+
+ list_for_each_entry(opt_inst, &team->option_inst_list, list) {
+ if (opt_inst->changed)
+ list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list);
+ }
+ err = team_nl_send_event_options_get(team, &sel_opt_inst_list);
+ if (err && err != -ESRCH)
+ netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n",
+ err);
+}
+
+/* rtnl lock is held */
+
+static void __team_port_change_send(struct team_port *port, bool linkup)
+{
+ int err;
+
+ port->changed = true;
+ port->state.linkup = linkup;
+ team_refresh_port_linkup(port);
+ if (linkup) {
+ struct ethtool_link_ksettings ecmd;
+
+ err = __ethtool_get_link_ksettings(port->dev, &ecmd);
+ if (!err) {
+ port->state.speed = ecmd.base.speed;
+ port->state.duplex = ecmd.base.duplex;
+ goto send_event;
+ }
+ }
+ port->state.speed = 0;
+ port->state.duplex = 0;
+
+send_event:
+ err = team_nl_send_event_port_get(port->team, port);
+ if (err && err != -ESRCH)
+ netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink (err %d)\n",
+ port->dev->name, err);
+
+}
+
+static void __team_carrier_check(struct team *team)
+{
+ struct team_port *port;
+ bool team_linkup;
+
+ if (team->user_carrier_enabled)
+ return;
+
+ team_linkup = false;
+ list_for_each_entry(port, &team->port_list, list) {
+ if (port->linkup) {
+ team_linkup = true;
+ break;
+ }
+ }
+
+ if (team_linkup)
+ netif_carrier_on(team->dev);
+ else
+ netif_carrier_off(team->dev);
+}
+
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+ if (port->state.linkup != linkup)
+ __team_port_change_send(port, linkup);
+ __team_carrier_check(port->team);
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup)
+{
+ __team_port_change_send(port, linkup);
+ __team_carrier_check(port->team);
+}
+
+static void __team_port_change_port_removed(struct team_port *port)
+{
+ port->removed = true;
+ __team_port_change_send(port, false);
+ __team_carrier_check(port->team);
+}
+
+static void team_port_change_check(struct team_port *port, bool linkup)
+{
+ struct team *team = port->team;
+
+ mutex_lock(&team->lock);
+ __team_port_change_check(port, linkup);
+ mutex_unlock(&team->lock);
+}
+
+
+/************************************
+ * Net device notifier event handler
+ ************************************/
+
+static int team_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct team_port *port;
+
+ port = team_port_get_rtnl(dev);
+ if (!port)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (netif_oper_up(dev))
+ team_port_change_check(port, true);
+ break;
+ case NETDEV_DOWN:
+ team_port_change_check(port, false);
+ break;
+ case NETDEV_CHANGE:
+ if (netif_running(port->dev))
+ team_port_change_check(port,
+ !!netif_oper_up(port->dev));
+ break;
+ case NETDEV_UNREGISTER:
+ team_del_slave(port->team->dev, dev);
+ break;
+ case NETDEV_FEAT_CHANGE:
+ if (!port->team->notifier_ctx) {
+ port->team->notifier_ctx = true;
+ team_compute_features(port->team);
+ port->team->notifier_ctx = false;
+ }
+ break;
+ case NETDEV_PRECHANGEMTU:
+ /* Forbid to change mtu of underlaying device */
+ if (!port->team->port_mtu_change_allowed)
+ return NOTIFY_BAD;
+ break;
+ case NETDEV_PRE_TYPE_CHANGE:
+ /* Forbid to change type of underlaying device */
+ return NOTIFY_BAD;
+ case NETDEV_RESEND_IGMP:
+ /* Propagate to master device */
+ call_netdevice_notifiers(event, port->team->dev);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block team_notifier_block __read_mostly = {
+ .notifier_call = team_device_event,
+};
+
+
+/***********************
+ * Module init and exit
+ ***********************/
+
+static int __init team_module_init(void)
+{
+ int err;
+
+ register_netdevice_notifier(&team_notifier_block);
+
+ err = rtnl_link_register(&team_link_ops);
+ if (err)
+ goto err_rtnl_reg;
+
+ err = team_nl_init();
+ if (err)
+ goto err_nl_init;
+
+ return 0;
+
+err_nl_init:
+ rtnl_link_unregister(&team_link_ops);
+
+err_rtnl_reg:
+ unregister_netdevice_notifier(&team_notifier_block);
+
+ return err;
+}
+
+static void __exit team_module_exit(void)
+{
+ team_nl_fini();
+ rtnl_link_unregister(&team_link_ops);
+ unregister_netdevice_notifier(&team_notifier_block);
+}
+
+module_init(team_module_init);
+module_exit(team_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
+MODULE_DESCRIPTION("Ethernet team device driver");
+MODULE_ALIAS_RTNL_LINK(DRV_NAME);
diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
index dcaa62377808..5d7d11d75b0b 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -786,8 +786,12 @@ static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf,
return true;
}
- /* Start of packet, validate the frame header */
- if (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) {
+ /* Start of packet, validate the frame header. tbnet_poll() puts the
+ * first frame in the skb linear area and every further frame in a page
+ * fragment, so a packet may not span more than MAX_SKB_FRAGS + 1 frames
+ * without overflowing skb_shinfo()->frags[].
+ */
+ if (frame_count == 0 || frame_count > MAX_SKB_FRAGS + 1) {
net->stats.rx_length_errors++;
return false;
}
diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c
index 0bfa37c14059..09afd137b64e 100644
--- a/drivers/net/usb/gl620a.c
+++ b/drivers/net/usb/gl620a.c
@@ -104,6 +104,9 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
return 0;
}
+ if (!skb_pull(skb, size + 4))
+ return 0;
+
// allocate the skb for the individual packet
gl_skb = alloc_skb(size, GFP_ATOMIC);
if (gl_skb) {
@@ -116,9 +119,6 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
// advance to the next packet
packet = (struct gl_packet *)&packet->packet_data[size];
count--;
-
- // shift the data pointer to the next gl_packet
- skb_pull(skb, size + 4);
}
// skip the packet length field 4 bytes
diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c
index ee9c48f7f68f..0dd0a30c3db4 100644
--- a/drivers/net/usb/kalmia.c
+++ b/drivers/net/usb/kalmia.c
@@ -276,6 +276,14 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
"Received header: %6phC. Package length: %i\n",
header_start, skb->len - KALMIA_HEADER_LENGTH);
+ /* both framing headers must be present before we subtract
+ * them, otherwise usb_packet_length underflows and the
+ * device-supplied ether_packet_length drives an out of bounds
+ * access below
+ */
+ if (skb->len < 2 * KALMIA_HEADER_LENGTH)
+ return 0;
+
/* subtract start header and end header */
usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH);
ether_packet_length = get_unaligned_le16(&header_start[2]);
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index f5bcf150fd9e..cc9fa1ae7c1c 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -817,6 +817,156 @@ static void lan78xx_update_stats(struct lan78xx_net *dev)
usb_autopm_put_interface(dev->intf);
}
+static int lan78xx_start_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enable)
+{
+ return lan78xx_update_reg(dev, reg, hw_enable, hw_enable);
+}
+
+static int lan78xx_stop_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enabled,
+ u32 hw_disabled)
+{
+ unsigned long timeout;
+ bool stopped = true;
+ int ret;
+ u32 buf;
+
+ /* Stop the h/w block (if not already stopped) */
+
+ ret = lan78xx_read_reg(dev, reg, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf & hw_enabled) {
+ buf &= ~hw_enabled;
+
+ ret = lan78xx_write_reg(dev, reg, buf);
+ if (ret < 0)
+ return ret;
+
+ stopped = false;
+ timeout = jiffies + HW_DISABLE_TIMEOUT;
+ do {
+ ret = lan78xx_read_reg(dev, reg, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf & hw_disabled)
+ stopped = true;
+ else
+ msleep(HW_DISABLE_DELAY_MS);
+ } while (!stopped && !time_after(jiffies, timeout));
+ }
+
+ ret = stopped ? 0 : -ETIME;
+
+ return ret;
+}
+
+static int lan78xx_flush_fifo(struct lan78xx_net *dev, u32 reg, u32 fifo_flush)
+{
+ return lan78xx_update_reg(dev, reg, fifo_flush, fifo_flush);
+}
+
+static int lan78xx_start_tx_path(struct lan78xx_net *dev)
+{
+ int ret;
+
+ netif_dbg(dev, drv, dev->net, "start tx path");
+
+ /* Start the MAC transmitter */
+
+ ret = lan78xx_start_hw(dev, MAC_TX, MAC_TX_TXEN_);
+ if (ret < 0)
+ return ret;
+
+ /* Start the Tx FIFO */
+
+ ret = lan78xx_start_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int lan78xx_stop_tx_path(struct lan78xx_net *dev)
+{
+ int ret;
+
+ netif_dbg(dev, drv, dev->net, "stop tx path");
+
+ /* Stop the Tx FIFO */
+
+ ret = lan78xx_stop_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_, FCT_TX_CTL_DIS_);
+ if (ret < 0)
+ return ret;
+
+ /* Stop the MAC transmitter */
+
+ ret = lan78xx_stop_hw(dev, MAC_TX, MAC_TX_TXEN_, MAC_TX_TXD_);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* The caller must ensure the Tx path is stopped before calling
+ * lan78xx_flush_tx_fifo().
+ */
+static int lan78xx_flush_tx_fifo(struct lan78xx_net *dev)
+{
+ return lan78xx_flush_fifo(dev, FCT_TX_CTL, FCT_TX_CTL_RST_);
+}
+
+static int lan78xx_start_rx_path(struct lan78xx_net *dev)
+{
+ int ret;
+
+ netif_dbg(dev, drv, dev->net, "start rx path");
+
+ /* Start the Rx FIFO */
+
+ ret = lan78xx_start_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_);
+ if (ret < 0)
+ return ret;
+
+ /* Start the MAC receiver*/
+
+ ret = lan78xx_start_hw(dev, MAC_RX, MAC_RX_RXEN_);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int lan78xx_stop_rx_path(struct lan78xx_net *dev)
+{
+ int ret;
+
+ netif_dbg(dev, drv, dev->net, "stop rx path");
+
+ /* Stop the MAC receiver */
+
+ ret = lan78xx_stop_hw(dev, MAC_RX, MAC_RX_RXEN_, MAC_RX_RXD_);
+ if (ret < 0)
+ return ret;
+
+ /* Stop the Rx FIFO */
+
+ ret = lan78xx_stop_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_, FCT_RX_CTL_DIS_);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* The caller must ensure the Rx path is stopped before calling
+ * lan78xx_flush_rx_fifo().
+ */
+static int lan78xx_flush_rx_fifo(struct lan78xx_net *dev)
+{
+ return lan78xx_flush_fifo(dev, FCT_RX_CTL, FCT_RX_CTL_RST_);
+}
+
/* Loop until the read is completed with timeout called with phy_mutex held */
static int lan78xx_phy_wait_not_busy(struct lan78xx_net *dev)
{
@@ -1268,6 +1418,17 @@ static void lan78xx_deferred_multicast_write(struct work_struct *param)
lan78xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
}
+static void lan78xx_update_vlan_filter(struct lan78xx_priv *pdata,
+ struct net_device *netdev,
+ netdev_features_t features)
+{
+ if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) &&
+ !(netdev->flags & IFF_PROMISC))
+ pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
+ else
+ pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
+}
+
static void lan78xx_set_multicast(struct net_device *netdev)
{
struct lan78xx_net *dev = netdev_priv(netdev);
@@ -1302,6 +1463,8 @@ static void lan78xx_set_multicast(struct net_device *netdev)
}
}
+ lan78xx_update_vlan_filter(pdata, dev->net, dev->net->features);
+
if (netdev_mc_count(dev->net)) {
struct netdev_hw_addr *ha;
int i;
@@ -2606,10 +2769,7 @@ static int lan78xx_set_features(struct net_device *netdev,
else
pdata->rfe_ctl &= ~RFE_CTL_VLAN_STRIP_;
- if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
- pdata->rfe_ctl |= RFE_CTL_VLAN_FILTER_;
- else
- pdata->rfe_ctl &= ~RFE_CTL_VLAN_FILTER_;
+ lan78xx_update_vlan_filter(pdata, netdev, features);
spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
@@ -2747,156 +2907,6 @@ static int lan78xx_urb_config_init(struct lan78xx_net *dev)
return result;
}
-static int lan78xx_start_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enable)
-{
- return lan78xx_update_reg(dev, reg, hw_enable, hw_enable);
-}
-
-static int lan78xx_stop_hw(struct lan78xx_net *dev, u32 reg, u32 hw_enabled,
- u32 hw_disabled)
-{
- unsigned long timeout;
- bool stopped = true;
- int ret;
- u32 buf;
-
- /* Stop the h/w block (if not already stopped) */
-
- ret = lan78xx_read_reg(dev, reg, &buf);
- if (ret < 0)
- return ret;
-
- if (buf & hw_enabled) {
- buf &= ~hw_enabled;
-
- ret = lan78xx_write_reg(dev, reg, buf);
- if (ret < 0)
- return ret;
-
- stopped = false;
- timeout = jiffies + HW_DISABLE_TIMEOUT;
- do {
- ret = lan78xx_read_reg(dev, reg, &buf);
- if (ret < 0)
- return ret;
-
- if (buf & hw_disabled)
- stopped = true;
- else
- msleep(HW_DISABLE_DELAY_MS);
- } while (!stopped && !time_after(jiffies, timeout));
- }
-
- ret = stopped ? 0 : -ETIME;
-
- return ret;
-}
-
-static int lan78xx_flush_fifo(struct lan78xx_net *dev, u32 reg, u32 fifo_flush)
-{
- return lan78xx_update_reg(dev, reg, fifo_flush, fifo_flush);
-}
-
-static int lan78xx_start_tx_path(struct lan78xx_net *dev)
-{
- int ret;
-
- netif_dbg(dev, drv, dev->net, "start tx path");
-
- /* Start the MAC transmitter */
-
- ret = lan78xx_start_hw(dev, MAC_TX, MAC_TX_TXEN_);
- if (ret < 0)
- return ret;
-
- /* Start the Tx FIFO */
-
- ret = lan78xx_start_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int lan78xx_stop_tx_path(struct lan78xx_net *dev)
-{
- int ret;
-
- netif_dbg(dev, drv, dev->net, "stop tx path");
-
- /* Stop the Tx FIFO */
-
- ret = lan78xx_stop_hw(dev, FCT_TX_CTL, FCT_TX_CTL_EN_, FCT_TX_CTL_DIS_);
- if (ret < 0)
- return ret;
-
- /* Stop the MAC transmitter */
-
- ret = lan78xx_stop_hw(dev, MAC_TX, MAC_TX_TXEN_, MAC_TX_TXD_);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-/* The caller must ensure the Tx path is stopped before calling
- * lan78xx_flush_tx_fifo().
- */
-static int lan78xx_flush_tx_fifo(struct lan78xx_net *dev)
-{
- return lan78xx_flush_fifo(dev, FCT_TX_CTL, FCT_TX_CTL_RST_);
-}
-
-static int lan78xx_start_rx_path(struct lan78xx_net *dev)
-{
- int ret;
-
- netif_dbg(dev, drv, dev->net, "start rx path");
-
- /* Start the Rx FIFO */
-
- ret = lan78xx_start_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_);
- if (ret < 0)
- return ret;
-
- /* Start the MAC receiver*/
-
- ret = lan78xx_start_hw(dev, MAC_RX, MAC_RX_RXEN_);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int lan78xx_stop_rx_path(struct lan78xx_net *dev)
-{
- int ret;
-
- netif_dbg(dev, drv, dev->net, "stop rx path");
-
- /* Stop the MAC receiver */
-
- ret = lan78xx_stop_hw(dev, MAC_RX, MAC_RX_RXEN_, MAC_RX_RXD_);
- if (ret < 0)
- return ret;
-
- /* Stop the Rx FIFO */
-
- ret = lan78xx_stop_hw(dev, FCT_RX_CTL, FCT_RX_CTL_EN_, FCT_RX_CTL_DIS_);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-/* The caller must ensure the Rx path is stopped before calling
- * lan78xx_flush_rx_fifo().
- */
-static int lan78xx_flush_rx_fifo(struct lan78xx_net *dev)
-{
- return lan78xx_flush_fifo(dev, FCT_RX_CTL, FCT_RX_CTL_RST_);
-}
-
static int lan78xx_reset(struct lan78xx_net *dev)
{
struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]);
diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c
index b0c0c9dd6a02..8a66549cefa7 100644
--- a/drivers/net/usb/net1080.c
+++ b/drivers/net/usb/net1080.c
@@ -381,7 +381,7 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
skb_trim(skb, skb->len - sizeof *trailer);
if ((packet_len & 0x01) == 0) {
- if (skb->data [packet_len] != PAD_BYTE) {
+ if (packet_len >= skb->len || skb->data[packet_len] != PAD_BYTE) {
dev->net->stats.rx_frame_errors++;
netdev_dbg(dev->net, "bad pad\n");
return 0;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 2b3b0beb55c8..d85a11e9477f 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -1159,6 +1159,8 @@ static int veth_enable_xdp_range(struct net_device *dev, int start, int end,
err_reg_mem:
xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq);
err_rxq_reg:
+ if (!napi_already_on)
+ netif_napi_del(&priv->rq[i].xdp_napi);
for (i--; i >= start; i--) {
struct veth_rq *rq = &priv->rq[i];
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 33f61922c139..fe0e647808cd 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -208,15 +208,16 @@ struct receive_queue {
* because table sizes may be differ according to the device configuration.
*/
#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
-#define VIRTIO_NET_RSS_MAX_TABLE_LEN 128
struct virtio_net_ctrl_rss {
u32 hash_types;
u16 indirection_table_mask;
u16 unclassified_queue;
- u16 indirection_table[VIRTIO_NET_RSS_MAX_TABLE_LEN];
+ u16 hash_cfg_reserved; /* for HASH_CONFIG (see virtio_net_hash_config for details) */
u16 max_tx_vq;
u8 hash_key_length;
u8 key[VIRTIO_NET_RSS_MAX_KEY_SIZE];
+
+ u16 *indirection_table;
};
/* Control VQ buffers: protected by the rtnl lock */
@@ -1333,15 +1334,18 @@ static struct sk_buff *receive_big(struct net_device *dev,
struct virtnet_rq_stats *stats)
{
struct page *page = buf;
+ unsigned long max_len;
struct sk_buff *skb;
+ max_len = (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE -
+ sizeof(struct padded_vnet_hdr) + vi->hdr_len;
+
/* Make sure that len does not exceed the size allocated in
* add_recvbuf_big.
*/
- if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) {
+ if (unlikely(len > max_len)) {
pr_debug("%s: rx error: len %u exceeds allocated size %lu\n",
- dev->name, len,
- (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE);
+ dev->name, len, max_len);
goto err;
}
@@ -2224,6 +2228,9 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
unsigned int received;
unsigned int xdp_xmit = 0;
+ if (budget)
+ virtqueue_disable_cb(rq->vq);
+
virtnet_poll_cleantx(rq, budget);
received = virtnet_receive(rq, budget, &xdp_xmit);
@@ -3011,6 +3018,25 @@ static int virtnet_set_ringparam(struct net_device *dev,
return 0;
}
+static int rss_indirection_table_alloc(struct virtio_net_ctrl_rss *rss, u16 indir_table_size)
+{
+ if (!indir_table_size) {
+ rss->indirection_table = NULL;
+ return 0;
+ }
+
+ rss->indirection_table = kcalloc(indir_table_size, sizeof(u16), GFP_KERNEL);
+ if (!rss->indirection_table)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void rss_indirection_table_free(struct virtio_net_ctrl_rss *rss)
+{
+ kfree(rss->indirection_table);
+}
+
static bool virtnet_commit_rss_command(struct virtnet_info *vi)
{
struct net_device *dev = vi->dev;
@@ -3020,11 +3046,15 @@ static bool virtnet_commit_rss_command(struct virtnet_info *vi)
/* prepare sgs */
sg_init_table(sgs, 4);
- sg_buf_size = offsetof(struct virtio_net_ctrl_rss, indirection_table);
+ sg_buf_size = offsetof(struct virtio_net_ctrl_rss, hash_cfg_reserved);
sg_set_buf(&sgs[0], &vi->ctrl->rss, sg_buf_size);
- sg_buf_size = sizeof(uint16_t) * (vi->ctrl->rss.indirection_table_mask + 1);
- sg_set_buf(&sgs[1], vi->ctrl->rss.indirection_table, sg_buf_size);
+ if (vi->has_rss) {
+ sg_buf_size = sizeof(uint16_t) * vi->rss_indir_table_size;
+ sg_set_buf(&sgs[1], vi->ctrl->rss.indirection_table, sg_buf_size);
+ } else {
+ sg_set_buf(&sgs[1], &vi->ctrl->rss.hash_cfg_reserved, sizeof(uint16_t));
+ }
sg_buf_size = offsetof(struct virtio_net_ctrl_rss, key)
- offsetof(struct virtio_net_ctrl_rss, max_tx_vq);
@@ -4080,7 +4110,10 @@ static void virtnet_free_queues(struct virtnet_info *vi)
kfree(vi->rq);
kfree(vi->sq);
+ if (vi->ctrl)
+ rss_indirection_table_free(&vi->ctrl->rss);
kfree(vi->ctrl);
+ vi->ctrl = NULL;
}
static void _free_receive_bufs(struct virtnet_info *vi)
@@ -4266,6 +4299,9 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
vi->ctrl = kzalloc(sizeof(*vi->ctrl), GFP_KERNEL);
if (!vi->ctrl)
goto err_ctrl;
+ if ((vi->has_rss || vi->has_rss_hash_report) &&
+ rss_indirection_table_alloc(&vi->ctrl->rss, vi->rss_indir_table_size))
+ goto err_sq;
} else {
vi->ctrl = NULL;
}
@@ -4298,7 +4334,10 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
err_rq:
kfree(vi->sq);
err_sq:
+ if (vi->ctrl)
+ rss_indirection_table_free(&vi->ctrl->rss);
kfree(vi->ctrl);
+ vi->ctrl = NULL;
err_ctrl:
return -ENOMEM;
}
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 958d3be4f990..1a3a00476a93 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -680,14 +680,18 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
struct sk_buff *skb,
struct gro_remcsum *grc)
{
- struct sk_buff *p;
struct vxlanhdr *vh, *vh2;
unsigned int hlen, off_vx;
- struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
+ struct vxlan_sock *vs;
+ struct sk_buff *p;
__be32 flags;
skb_gro_remcsum_init(grc);
+ vs = rcu_dereference_sk_user_data(sk);
+ if (!vs)
+ return NULL;
+
off_vx = skb_gro_offset(skb);
hlen = off_vx + sizeof(*vh);
vh = skb_gro_header(skb, hlen, off_vx);
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index e46b7f5ee49e..9dbd04956d62 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -1487,11 +1487,11 @@ static int ixp4xx_hss_probe(struct platform_device *pdev)
"unable to get CLK internal GPIO\n");
ndev = alloc_hdlcdev(port);
- port->netdev = alloc_hdlcdev(port);
- if (!port->netdev) {
+ if (!ndev) {
err = -ENOMEM;
goto err_plat;
}
+ port->netdev = ndev;
SET_NETDEV_DEV(ndev, &pdev->dev);
hdlc = dev_to_hdlc(ndev);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 4e939dcac1c9..c1807356bea7 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2754,6 +2754,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
continue;
}
+ if (ts.qid >= ATH9K_NUM_TX_QUEUES) {
+ ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid);
+ continue;
+ }
+
txq = &sc->tx.txq[ts.qid];
ath_txq_lock(sc, txq);
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 17e1919d1cd8..57de3747cbcf 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -2659,6 +2659,9 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba
if (rsp->candidate_cnt < 1)
return rsp->status ? rsp->status : -EINVAL;
+ if (len < sizeof(*rsp) + sizeof(*candidate))
+ return -EINVAL;
+
candidate = (struct wcn36xx_hal_trigger_ba_rsp_candidate *)(buf + sizeof(*rsp));
for (i = 0; i < STACFG_MAX_TC; i++) {
@@ -2865,6 +2868,12 @@ static int wcn36xx_smd_print_reg_info_ind(struct wcn36xx *wcn,
return -EIO;
}
+ if (rsp->count > (len - sizeof(*rsp)) / sizeof(rsp->regs[0])) {
+ wcn36xx_warn("Truncated print reg info indication: count %u, len %zu\n",
+ rsp->count, len);
+ return -EIO;
+ }
+
wcn36xx_dbg(WCN36XX_DBG_HAL,
"reginfo indication, scenario: 0x%x reason: 0x%x\n",
rsp->scenario, rsp->reason);
@@ -3353,6 +3362,10 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_EXIT_IMPS_RSP:
case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP:
case WCN36XX_HAL_ADD_BCN_FILTER_RSP:
+ if (len > WCN36XX_HAL_BUF_SIZE) {
+ wcn36xx_warn("HAL response too large: %d\n", len);
+ break;
+ }
memcpy(wcn->hal_buf, buf, len);
wcn->hal_rsp_len = len;
complete(&wcn->hal_rsp_compl);
diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c
index dbd13f7aa3e6..bd269fdaa1d0 100644
--- a/drivers/net/wireless/cisco/airo.c
+++ b/drivers/net/wireless/cisco/airo.c
@@ -2437,7 +2437,9 @@ void stop_airo_card(struct net_device *dev, int freeres)
EXPORT_SYMBOL(stop_airo_card);
-static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int wll_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
{
memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN);
return ETH_ALEN;
diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c
index 787f685e70b4..8ba1a709fe47 100644
--- a/drivers/net/wireless/intersil/hostap/hostap_main.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_main.c
@@ -575,6 +575,7 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
static int hostap_80211_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
unsigned char *haddr)
{
memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c
index a6e254a1185c..c2fa654e7f61 100644
--- a/drivers/net/wireless/marvell/mwifiex/join.c
+++ b/drivers/net/wireless/marvell/mwifiex/join.c
@@ -690,7 +690,6 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
/* Send a Media Connected event, according to the Spec */
priv->media_connected = true;
- priv->adapter->ps_state = PS_STATE_AWAKE;
priv->adapter->pps_uapsd_mode = false;
priv->adapter->tx_lock_flag = false;
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index bf4541e76ba2..61543ab7c256 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1137,7 +1137,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
* All further fragments will be validated by mac80211 only.
*/
if (ieee80211_is_frag(hdr) &&
- !ieee80211_is_first_frag(hdr->frame_control))
+ !ieee80211_is_first_frag(hdr->seq_ctrl))
return;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index e2983dbf05a9..ae91a9815fd2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -916,16 +916,16 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
}
if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) {
- u32 tx_retries = 0, tx_failed = 0;
+ u32 tx_retries = 0, tx_failed = 0, count;
if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) {
- tx_retries =
- FIELD_GET(MT_TX_FREE_COUNT_V3, info) - 1;
+ count = FIELD_GET(MT_TX_FREE_COUNT_V3, info);
+ tx_retries = count ? count - 1 : 0;
tx_failed = tx_retries +
!!FIELD_GET(MT_TX_FREE_STAT_V3, info);
} else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) {
- tx_retries =
- FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+ count = FIELD_GET(MT_TX_FREE_COUNT, info);
+ tx_retries = count ? count - 1 : 0;
tx_failed = tx_retries +
!!FIELD_GET(MT_TX_FREE_STAT, info);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index cd4eee374922..5099e6f79448 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -536,8 +536,9 @@ static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len)
stat = FIELD_GET(MT_TX_FREE_STATUS, info);
if (wcid) {
- wcid->stats.tx_retries +=
- FIELD_GET(MT_TX_FREE_COUNT, info) - 1;
+ u32 count = FIELD_GET(MT_TX_FREE_COUNT, info);
+
+ wcid->stats.tx_retries += count ? count - 1 : 0;
wcid->stats.tx_failed += !!stat;
}
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 00b945053e19..21c6df8c7fe8 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -1382,7 +1382,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
GFP_KERNEL);
if (!rt2x00dev->drv_data) {
retval = -ENOMEM;
- goto exit;
+ return retval;
}
}
@@ -1416,7 +1416,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy));
if (!rt2x00dev->workqueue) {
retval = -ENOMEM;
- goto exit;
+ goto exit_free_drv_data;
}
INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
@@ -1488,6 +1488,14 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
exit:
rt2x00lib_remove_dev(rt2x00dev);
+ return retval;
+
+exit_free_drv_data:
+ clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+
+ kfree(rt2x00dev->drv_data);
+ rt2x00dev->drv_data = NULL;
+
return retval;
}
EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev);
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index bd6d8b2af242..1da0bc436d8f 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1074,6 +1074,11 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
* discard the frame if none available
*/
new_len = pkt_stat.pkt_len + pkt_offset;
+ if (unlikely(new_len > RTK_PCI_RX_BUF_SIZE)) {
+ rtw_dbg(rtwdev, RTW_DBG_RX,
+ "oversized RX packet: %u\n", new_len);
+ goto next_rp;
+ }
new = dev_alloc_skb(new_len);
if (WARN_ONCE(!new, "rx routine starvation\n"))
goto next_rp;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 539537360914..48ed0e5dbed8 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -3641,7 +3641,7 @@ static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev,
struct sk_buff *skb;
bool found;
int ret = 0;
- u8 i;
+ u32 i;
if (!req->n_6ghz_params)
return 0;
diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
index bff46f7ca59f..0bbd41263cc2 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
@@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh,
u32 packet_offset, i, rc, dg_len;
for (i = 0; i < nr_of_dg; i++, dg++) {
- if (le32_to_cpu(dg->datagram_index)
- < sizeof(struct mux_adbh))
+ u32 dg_index = le32_to_cpu(dg->datagram_index);
+
+ dg_len = le16_to_cpu(dg->datagram_length);
+
+ if (dg_index < sizeof(struct mux_adbh))
goto dg_error;
- /* Is the packet inside of the ADB */
- if (le32_to_cpu(dg->datagram_index) >=
- le32_to_cpu(adbh->block_length)) {
+ /* Is the packet inside of the ADB and the received skb ? */
+ if (dg_index >= le32_to_cpu(adbh->block_length) ||
+ dg_index >= skb->len ||
+ dg_len > skb->len - dg_index ||
+ dl_head_pad_len >= dg_len) {
goto dg_error;
} else {
- packet_offset =
- le32_to_cpu(dg->datagram_index) +
- dl_head_pad_len;
- dg_len = le16_to_cpu(dg->datagram_length);
+ packet_offset = dg_index + dl_head_pad_len;
/* Pass the packet to the netif layer. */
rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan,
packet_offset,
@@ -589,12 +591,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
struct mux_adbh *adbh;
struct mux_adth *adth;
int nr_of_dg, if_id;
- u32 adth_index;
+ u32 adth_index, prev_index = 0;
u8 *block;
block = skb->data;
adbh = (struct mux_adbh *)block;
+ /* The block header itself must fit in the received skb. */
+ if (skb->len < sizeof(struct mux_adbh))
+ goto adb_decode_err;
+
/* Process the aggregated datagram tables. */
adth_index = le32_to_cpu(adbh->first_table_index);
@@ -606,6 +612,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
/* Loop through mixed session tables. */
while (adth_index) {
+ /* The table header must lie within the received skb, and the
+ * chain must move forward so a modem cannot make the loop
+ * cycle between two tables.
+ */
+ if (adth_index <= prev_index ||
+ adth_index < sizeof(struct mux_adbh) ||
+ adth_index > skb->len - sizeof(struct mux_adth))
+ goto adb_decode_err;
+ prev_index = adth_index;
+
/* Get the reference to the table header. */
adth = (struct mux_adth *)(block + adth_index);
@@ -629,6 +645,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux,
if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth))
goto adb_decode_err;
+ /* The whole datagram table must fit in the received skb. */
+ if (le16_to_cpu(adth->table_length) > skb->len - adth_index)
+ goto adb_decode_err;
+
/* Calculate the number of datagrams. */
nr_of_dg = (le16_to_cpu(adth->table_length) -
sizeof(struct mux_adth)) /
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
index 554ba4669cc8..09df3ee7b319 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
@@ -1053,6 +1053,9 @@ static int t7xx_cldma_late_init(struct cldma_ctrl *md_ctrl)
while (i--)
t7xx_cldma_ring_free(md_ctrl, &md_ctrl->tx_ring[i], DMA_TO_DEVICE);
+ dma_pool_destroy(md_ctrl->gpd_dmapool);
+ md_ctrl->gpd_dmapool = NULL;
+
return ret;
}
diff --git a/drivers/net/wwan/t7xx/t7xx_port_wwan.c b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
index 17389c8f6600..597a06e86f6e 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
@@ -74,6 +74,8 @@ static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
while (cur) {
cloned = skb_clone(cur, GFP_KERNEL);
+ if (!cloned)
+ return cnt ? cnt : -ENOMEM;
cloned->len = skb_headlen(cur);
ret = t7xx_port_send_skb(port_private, cloned, 0, 0);
if (ret) {
diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c
index 17bf7c8a1a55..25c3f30de651 100644
--- a/drivers/ntb/hw/epf/ntb_hw_epf.c
+++ b/drivers/ntb/hw/epf/ntb_hw_epf.c
@@ -6,6 +6,7 @@
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
+#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -69,8 +70,21 @@ enum epf_ntb_bar {
NTB_BAR_NUM,
};
+enum epf_irq_slot {
+ EPF_IRQ_LINK = 0,
+ EPF_IRQ_RESERVED_DB, /* Historically skipped slot */
+ EPF_IRQ_DB_START,
+};
+
#define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1)
+struct ntb_epf_dev;
+
+struct ntb_epf_irq_ctx {
+ struct ntb_epf_dev *ndev;
+ unsigned int irq_no;
+};
+
struct ntb_epf_dev {
struct ntb_dev ntb;
struct device *dev;
@@ -90,8 +104,9 @@ struct ntb_epf_dev {
unsigned int self_spad;
unsigned int peer_spad;
- int db_val;
+ atomic64_t db_val;
u64 db_valid_mask;
+ struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1];
};
#define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb)
@@ -315,16 +330,29 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb)
static irqreturn_t ntb_epf_vec_isr(int irq, void *dev)
{
- struct ntb_epf_dev *ndev = dev;
- int irq_no;
-
- irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0);
- ndev->db_val = irq_no + 1;
+ struct ntb_epf_irq_ctx *ctx = dev;
+ struct ntb_epf_dev *ndev = ctx->ndev;
+ unsigned int db_vector;
+ unsigned int irq_no = ctx->irq_no;
- if (irq_no == 0)
+ if (irq_no == EPF_IRQ_LINK) {
ntb_link_event(&ndev->ntb);
- else
- ntb_db_event(&ndev->ntb, irq_no);
+ } else if (irq_no == EPF_IRQ_RESERVED_DB) {
+ dev_warn_ratelimited(ndev->dev,
+ "Unexpected reserved doorbell slot IRQ received\n");
+ } else {
+ db_vector = irq_no - EPF_IRQ_DB_START;
+ if (ndev->db_count < NTB_EPF_MIN_DB_COUNT ||
+ db_vector >= ndev->db_count - 1) {
+ dev_warn_ratelimited(ndev->dev,
+ "Unexpected doorbell vector %u (db_count %u)\n",
+ db_vector, ndev->db_count);
+ return IRQ_HANDLED;
+ }
+
+ atomic64_or(BIT_ULL(db_vector), &ndev->db_val);
+ ntb_db_event(&ndev->ntb, db_vector);
+ }
return IRQ_HANDLED;
}
@@ -350,31 +378,30 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
argument &= ~MSIX_ENABLE;
}
+ ndev->db_count = irq - 1;
for (i = 0; i < irq; i++) {
+ ndev->irq_ctx[i].ndev = ndev;
+ ndev->irq_ctx[i].irq_no = i;
ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
- 0, "ntb_epf", ndev);
+ 0, "ntb_epf", &ndev->irq_ctx[i]);
if (ret) {
dev_err(dev, "Failed to request irq\n");
- goto err_request_irq;
+ goto err_free_irq;
}
}
- ndev->db_count = irq - 1;
-
ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL,
argument | irq);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
- goto err_configure_db;
+ goto err_free_irq;
}
return 0;
-err_configure_db:
- for (i = 0; i < ndev->db_count + 1; i++)
- free_irq(pci_irq_vector(pdev, i), ndev);
-
-err_request_irq:
+err_free_irq:
+ while (i--)
+ free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
return ret;
@@ -499,7 +526,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
- return ndev->db_val;
+ return atomic64_read(&ndev->db_val);
}
static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
@@ -511,7 +538,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
- ndev->db_val = 0;
+ atomic64_and(~db_bits, &ndev->db_val);
return 0;
}
@@ -552,6 +579,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
struct device *dev = ndev->dev;
int ret;
+ ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
+ if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
+ dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
+ return -EINVAL;
+ }
+
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
NTB_EPF_MAX_DB_COUNT + 1);
@@ -560,15 +593,13 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
return ret;
}
- ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
- ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
+ /*
+ * ndev->db_count includes an extra skipped slot due to the legacy
+ * doorbell layout, hence -1.
+ */
+ ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1;
ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
- if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
- dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
- return -EINVAL;
- }
-
return 0;
}
@@ -663,7 +694,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1);
for (i = 0; i < ndev->db_count + 1; i++)
- free_irq(pci_irq_vector(pdev, i), ndev);
+ free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
}
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index d5593b0dc700..9e54083505f3 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -923,6 +923,9 @@ static int discover_arenas(struct btt *btt)
return ret;
out:
+ kfree(arena->freelist);
+ kfree(arena->rtt);
+ kfree(arena->map_locks);
kfree(arena);
free_arenas(btt);
out_super:
@@ -1593,7 +1596,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
if (btt->init_state != INIT_READY && nd_region->ro) {
dev_warn(dev, "%s is read-only, unable to init btt metadata\n",
dev_name(&nd_region->dev));
- return NULL;
+ goto err;
} else if (btt->init_state != INIT_READY) {
btt->num_arenas = (rawsize / ARENA_MAX_SIZE) +
((rawsize % ARENA_MAX_SIZE) ? 1 : 0);
@@ -1603,25 +1606,28 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
ret = create_arenas(btt);
if (ret) {
dev_info(dev, "init: create_arenas: %d\n", ret);
- return NULL;
+ goto err;
}
ret = btt_meta_init(btt);
if (ret) {
dev_err(dev, "init: error in meta_init: %d\n", ret);
- return NULL;
+ goto err;
}
}
ret = btt_blk_init(btt);
if (ret) {
dev_err(dev, "init: error in blk_init: %d\n", ret);
- return NULL;
+ goto err;
}
btt_debugfs_init(btt);
return btt;
+err:
+ free_arenas(btt);
+ return NULL;
}
/**
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 12774334273c..9b0bd8751715 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -365,11 +365,6 @@ unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd);
for (res = (ndd)->dpa.child, next = res ? res->sibling : NULL; \
res; res = next, next = next ? next->sibling : NULL)
-struct nd_percpu_lane {
- int count;
- spinlock_t lock;
-};
-
enum nd_label_flags {
ND_LABEL_REAP,
};
@@ -400,6 +395,10 @@ struct nd_mapping {
struct nvdimm_drvdata *ndd;
};
+struct nd_lane {
+ struct mutex lock; /* serialize lane access */
+} ____cacheline_aligned_in_smp;
+
struct nd_region {
struct device dev;
struct ida ns_ida;
@@ -420,7 +419,7 @@ struct nd_region {
struct kernfs_node *bb_state;
struct badblocks bb;
struct nd_interleave_set *nd_set;
- struct nd_percpu_lane __percpu *lane;
+ struct nd_lane *lane;
int (*flush)(struct nd_region *nd_region, struct bio *bio);
struct nd_mapping mapping[];
};
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index e2f1fb99707f..c13264c7b0de 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -179,7 +179,9 @@ static void nd_region_release(struct device *dev)
put_device(&nvdimm->dev);
}
- free_percpu(nd_region->lane);
+ for (i = 0; i < nd_region->num_lanes; i++)
+ mutex_destroy(&nd_region->lane[i].lock);
+ kfree(nd_region->lane);
if (!test_bit(ND_REGION_CXL, &nd_region->flags))
memregion_free(nd_region->id);
kfree(nd_region);
@@ -922,52 +924,30 @@ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev)
* nd_region_acquire_lane - allocate and lock a lane
* @nd_region: region id and number of lanes possible
*
- * A lane correlates to a BLK-data-window and/or a log slot in the BTT.
- * We optimize for the common case where there are 256 lanes, one
- * per-cpu. For larger systems we need to lock to share lanes. For now
- * this implementation assumes the cost of maintaining an allocator for
- * free lanes is on the order of the lock hold time, so it implements a
- * static lane = cpu % num_lanes mapping.
+ * A lane correlates to a log slot in the BTT. Lanes are shared across
+ * CPUs using a static lane = cpu % num_lanes mapping, with a per-lane
+ * mutex to serialize access.
*
- * In the case of a BTT instance on top of a BLK namespace a lane may be
- * acquired recursively. We lock on the first instance.
- *
- * In the case of a BTT instance on top of PMEM, we only acquire a lane
- * for the BTT metadata updates.
+ * Callers must be in sleepable context. The only in-tree caller is
+ * BTT's ->submit_bio handler (btt_read_pg / btt_write_pg).
*/
unsigned int nd_region_acquire_lane(struct nd_region *nd_region)
+ __acquires(&nd_region->lane[lane].lock)
{
- unsigned int cpu, lane;
-
- migrate_disable();
- cpu = smp_processor_id();
- if (nd_region->num_lanes < nr_cpu_ids) {
- struct nd_percpu_lane *ndl_lock, *ndl_count;
+ unsigned int lane;
- lane = cpu % nd_region->num_lanes;
- ndl_count = per_cpu_ptr(nd_region->lane, cpu);
- ndl_lock = per_cpu_ptr(nd_region->lane, lane);
- if (ndl_count->count++ == 0)
- spin_lock(&ndl_lock->lock);
- } else
- lane = cpu;
+ might_sleep();
+ lane = raw_smp_processor_id() % nd_region->num_lanes;
+ mutex_lock(&nd_region->lane[lane].lock);
return lane;
}
EXPORT_SYMBOL(nd_region_acquire_lane);
void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane)
+ __releases(&nd_region->lane[lane].lock)
{
- if (nd_region->num_lanes < nr_cpu_ids) {
- unsigned int cpu = smp_processor_id();
- struct nd_percpu_lane *ndl_lock, *ndl_count;
-
- ndl_count = per_cpu_ptr(nd_region->lane, cpu);
- ndl_lock = per_cpu_ptr(nd_region->lane, lane);
- if (--ndl_count->count == 0)
- spin_unlock(&ndl_lock->lock);
- }
- migrate_enable();
+ mutex_unlock(&nd_region->lane[lane].lock);
}
EXPORT_SYMBOL(nd_region_release_lane);
@@ -1037,17 +1017,16 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
goto err_id;
}
- nd_region->lane = alloc_percpu(struct nd_percpu_lane);
+ nd_region->num_lanes = ndr_desc->num_lanes;
+ if (!nd_region->num_lanes)
+ goto err_percpu;
+ nd_region->lane = kcalloc(nd_region->num_lanes,
+ sizeof(*nd_region->lane), GFP_KERNEL);
if (!nd_region->lane)
goto err_percpu;
- for (i = 0; i < nr_cpu_ids; i++) {
- struct nd_percpu_lane *ndl;
-
- ndl = per_cpu_ptr(nd_region->lane, i);
- spin_lock_init(&ndl->lock);
- ndl->count = 0;
- }
+ for (i = 0; i < nd_region->num_lanes; i++)
+ mutex_init(&nd_region->lane[i].lock);
for (i = 0; i < ndr_desc->num_mappings; i++) {
struct nd_mapping_desc *mapping = &ndr_desc->mapping[i];
@@ -1065,7 +1044,6 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
nd_region->ndr_mappings = ndr_desc->num_mappings;
nd_region->provider_data = ndr_desc->provider_data;
nd_region->nd_set = ndr_desc->nd_set;
- nd_region->num_lanes = ndr_desc->num_lanes;
nd_region->flags = ndr_desc->flags;
nd_region->ro = ro;
nd_region->numa_node = ndr_desc->numa_node;
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 48c46c942253..13ff4ac8f3e3 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -3396,7 +3396,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
int ret = -ENOMEM;
#ifdef CONFIG_NVME_MULTIPATH
- size += num_possible_nodes() * sizeof(struct nvme_ns *);
+ size += nr_node_ids * sizeof(struct nvme_ns *);
#endif
head = kzalloc(size, GFP_KERNEL);
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 578f4f29eacf..2aa3340eab06 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -467,6 +467,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio)
ns = nvme_find_path(head);
if (likely(ns)) {
bio_set_dev(bio, ns->disk->part0);
+ /*
+ * Use BIO_REMAPPED to skip bio_check_eod() when this bio
+ * enters submit_bio_noacct() for the per-path device. The EOD
+ * check already passed on the multipath head.
+ */
+ bio_set_flag(bio, BIO_REMAPPED);
bio->bi_opf |= REQ_NVME_MPATH;
trace_block_bio_remap(bio, disk_devt(ns->head->disk),
bio->bi_iter.bi_sector);
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index 74791078fdeb..9fe6ad89bf83 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -125,12 +125,11 @@ int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id)
return ret;
}
-int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
+u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl)
{
int ret = 0;
struct nvmet_host_link *p;
struct nvmet_host *host = NULL;
- const char *hash_name;
down_read(&nvmet_config_sem);
if (nvmet_is_disc_subsys(ctrl->subsys))
@@ -148,13 +147,16 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
}
if (!host) {
pr_debug("host %s not found\n", ctrl->hostnqn);
- ret = -EPERM;
+ ret = NVME_AUTH_DHCHAP_FAILURE_FAILED;
goto out_unlock;
}
ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
- if (ret < 0)
+ if (ret < 0) {
pr_warn("Failed to setup DH group");
+ ret = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ goto out_unlock;
+ }
if (!host->dhchap_secret) {
pr_debug("No authentication provided\n");
@@ -165,12 +167,6 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
pr_debug("Re-use existing hash ID %d\n",
ctrl->shash_id);
} else {
- hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
- if (!hash_name) {
- pr_warn("Hash ID %d invalid\n", host->dhchap_hash_id);
- ret = -EINVAL;
- goto out_unlock;
- }
ctrl->shash_id = host->dhchap_hash_id;
}
@@ -179,7 +175,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10,
host->dhchap_key_hash);
if (IS_ERR(ctrl->host_key)) {
- ret = PTR_ERR(ctrl->host_key);
+ ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
ctrl->host_key = NULL;
goto out_free_hash;
}
@@ -197,7 +193,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10,
host->dhchap_ctrl_key_hash);
if (IS_ERR(ctrl->ctrl_key)) {
- ret = PTR_ERR(ctrl->ctrl_key);
+ ret = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
ctrl->ctrl_key = NULL;
goto out_free_hash;
}
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index c103eba96350..ccd5dd3dac85 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -31,7 +31,7 @@ void nvmet_auth_sq_init(struct nvmet_sq *sq)
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
}
-static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
+static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_negotiate_data *data = d;
@@ -109,13 +109,22 @@ static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
return 0;
}
-static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
+static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_reply_data *data = d;
- u16 dhvlen = le16_to_cpu(data->dhvlen);
+ u16 dhvlen;
u8 *response;
+ if (tl < sizeof(*data))
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
+ dhvlen = le16_to_cpu(data->dhvlen);
+
+ /* Validate that hl and dhvlen fit within the transfer length */
+ if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl)
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n",
__func__, ctrl->cntlid, req->sq->qid,
data->hl, data->cvalid, dhvlen);
@@ -172,7 +181,7 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
return 0;
}
-static u16 nvmet_auth_failure2(void *d)
+static u8 nvmet_auth_failure2(void *d)
{
struct nvmf_auth_dhchap_failure_data *data = d;
@@ -186,6 +195,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
void *d;
u32 tl;
u16 status = 0;
+ u8 dhchap_status;
if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
@@ -237,30 +247,32 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
/* Restart negotiation */
- pr_debug("%s: ctrl %d qid %d reset negotiation\n", __func__,
- ctrl->cntlid, req->sq->qid);
+ pr_debug("%s: ctrl %d qid %d reset negotiation\n",
+ __func__, ctrl->cntlid, req->sq->qid);
if (!req->sq->qid) {
- if (nvmet_setup_auth(ctrl) < 0) {
- status = NVME_SC_INTERNAL;
- pr_err("ctrl %d qid 0 failed to setup"
- "re-authentication",
+ dhchap_status = nvmet_setup_auth(ctrl);
+ if (dhchap_status) {
+ pr_err("ctrl %d qid 0 failed to setup re-authentication\n",
ctrl->cntlid);
- goto done_failure1;
+ req->sq->dhchap_status = dhchap_status;
+ req->sq->dhchap_step =
+ NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ goto done_kfree;
}
}
- req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+ req->sq->dhchap_step =
+ NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
} else if (data->auth_id != req->sq->dhchap_step)
goto done_failure1;
/* Validate negotiation parameters */
- status = nvmet_auth_negotiate(req, d);
- if (status == 0)
+ dhchap_status = nvmet_auth_negotiate(req, d);
+ if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
else {
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
- req->sq->dhchap_status = status;
- status = 0;
+ req->sq->dhchap_status = dhchap_status;
}
goto done_kfree;
}
@@ -284,15 +296,14 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
switch (data->auth_id) {
case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
- status = nvmet_auth_reply(req, d);
- if (status == 0)
+ dhchap_status = nvmet_auth_reply(req, d, tl);
+ if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
else {
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
- req->sq->dhchap_status = status;
- status = 0;
+ req->sq->dhchap_status = dhchap_status;
}
goto done_kfree;
case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
@@ -301,13 +312,12 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
__func__, ctrl->cntlid, req->sq->qid);
goto done_kfree;
case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
- status = nvmet_auth_failure2(d);
- if (status) {
+ dhchap_status = nvmet_auth_failure2(d);
+ if (dhchap_status) {
pr_warn("ctrl %d qid %d: authentication failed (%d)\n",
- ctrl->cntlid, req->sq->qid, status);
- req->sq->dhchap_status = status;
+ ctrl->cntlid, req->sq->qid, dhchap_status);
+ req->sq->dhchap_status = dhchap_status;
req->sq->authenticated = false;
- status = 0;
}
goto done_kfree;
default:
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index fa9e8dc92153..e8dd276dfc62 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -209,8 +209,8 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
struct nvmf_connect_command *c = &req->cmd->connect;
struct nvmf_connect_data *d;
struct nvmet_ctrl *ctrl = NULL;
- u16 status = 0;
- int ret;
+ u16 status;
+ u8 dhchap_status;
if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
return;
@@ -252,11 +252,12 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
uuid_copy(&ctrl->hostid, &d->hostid);
- ret = nvmet_setup_auth(ctrl);
- if (ret < 0) {
- pr_err("Failed to setup authentication, error %d\n", ret);
+ dhchap_status = nvmet_setup_auth(ctrl);
+ if (dhchap_status) {
+ pr_err("Failed to setup authentication, dhchap status %u\n",
+ dhchap_status);
nvmet_ctrl_put(ctrl);
- if (ret == -EPERM)
+ if (dhchap_status == NVME_AUTH_DHCHAP_FAILURE_FAILED)
status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
else
status = NVME_SC_INTERNAL;
@@ -287,7 +288,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
struct nvmf_connect_data *d;
struct nvmet_ctrl *ctrl;
u16 qid = le16_to_cpu(c->qid);
- u16 status = 0;
+ u16 status;
if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
return;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 15b00ed7be16..c0648216b9e4 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -112,8 +112,8 @@ struct nvmet_sq {
bool authenticated;
struct delayed_work auth_expired_work;
u16 dhchap_tid;
- u16 dhchap_status;
- int dhchap_step;
+ u8 dhchap_status;
+ u8 dhchap_step;
u8 *dhchap_c1;
u8 *dhchap_c2;
u32 dhchap_s1;
@@ -700,7 +700,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req);
int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
bool set_ctrl);
int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
-int nvmet_setup_auth(struct nvmet_ctrl *ctrl);
+u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_init(struct nvmet_sq *sq);
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_free(struct nvmet_sq *sq);
@@ -719,7 +719,7 @@ int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
u8 *buf, int buf_size);
#else
-static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl)
+static inline u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl)
{
return 0;
}
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 298c46834a53..537e9fcd458d 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -666,7 +666,8 @@ static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
if (rsp->n_rdma)
nvmet_rdma_rw_ctx_destroy(rsp);
- if (rsp->req.sg != rsp->cmd->inline_sg)
+ if (rsp->req.sg < rsp->cmd->inline_sg ||
+ rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count)
nvmet_req_free_sgls(&rsp->req);
if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list)))
@@ -821,24 +822,25 @@ static void nvmet_rdma_write_data_done(struct ib_cq *cq, struct ib_wc *wc)
static void nvmet_rdma_use_inline_sg(struct nvmet_rdma_rsp *rsp, u32 len,
u64 off)
{
- int sg_count = num_pages(len);
+ u64 page_off = off % PAGE_SIZE;
+ u64 page_idx = off / PAGE_SIZE;
+ int sg_count = num_pages(page_off + len);
struct scatterlist *sg;
int i;
- sg = rsp->cmd->inline_sg;
+ sg = &rsp->cmd->inline_sg[page_idx];
for (i = 0; i < sg_count; i++, sg++) {
if (i < sg_count - 1)
sg_unmark_end(sg);
else
sg_mark_end(sg);
- sg->offset = off;
- sg->length = min_t(int, len, PAGE_SIZE - off);
+ sg->offset = page_off;
+ sg->length = min_t(u64, len, PAGE_SIZE - page_off);
len -= sg->length;
- if (!i)
- off = 0;
+ page_off = 0;
}
- rsp->req.sg = rsp->cmd->inline_sg;
+ rsp->req.sg = &rsp->cmd->inline_sg[page_idx];
rsp->req.sg_cnt = sg_count;
}
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 4174fef03eac..0b6c3241a255 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -1297,7 +1297,10 @@ static int nvmet_tcp_try_recv_ddgst(struct nvmet_tcp_queue *queue)
queue->idx, cmd->req.cmd->common.command_id,
queue->pdu.cmd.hdr.type, le32_to_cpu(cmd->recv_ddgst),
le32_to_cpu(cmd->exp_ddgst));
- nvmet_req_uninit(&cmd->req);
+ if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED)) {
+ cmd->req.cqe->status = NVME_SC_CMD_SEQ_ERROR;
+ nvmet_req_uninit(&cmd->req);
+ }
nvmet_tcp_free_cmd_buffers(cmd);
nvmet_tcp_fatal_error(queue);
ret = -EPROTO;
diff --git a/drivers/of/cpu.c b/drivers/of/cpu.c
index d17b2f851082..8b618e8c5ddf 100644
--- a/drivers/of/cpu.c
+++ b/drivers/of/cpu.c
@@ -60,7 +60,7 @@ static bool __of_find_n_match_cpu_property(struct device_node *cpun,
cell = of_get_property(cpun, prop_name, &prop_len);
if (!cell && !ac && arch_match_cpu_phys_id(cpu, 0))
return true;
- if (!cell || !ac)
+ if (!cell || !ac || ac > 2)
return false;
prop_len /= sizeof(*cell) * ac;
for (tid = 0; tid < prop_len; tid++) {
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index ab109bb92cf4..9cc0f4ffb814 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -224,7 +224,6 @@ static void __of_attach_node(struct device_node *np)
np->sibling = np->parent->child;
np->parent->child = np;
of_node_clear_flag(np, OF_DETACHED);
- fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 18b72f891c5b..d1dfd857f2cd 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -770,11 +770,6 @@ static int of_platform_notify(struct notifier_block *nb,
if (of_node_check_flag(rd->dn, OF_POPULATED))
return NOTIFY_OK;
- /*
- * Clear the flag before adding the device so that fw_devlink
- * doesn't skip adding consumers to this device.
- */
- fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE);
/* pdev_parent may be NULL when no bus platform device */
pdev_parent = of_find_device_by_node(rd->dn->parent);
pdev = of_platform_device_create(rd->dn, NULL,
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 657c08d0ad97..782802806fc9 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -710,7 +710,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
*/
if (unlikely(opp_table->regulator_count == -1)) {
opp_table->regulator_count = 0;
- return 0;
+ goto free_microwatt;
}
for (i = 0, j = 0; i < opp_table->regulator_count; i++) {
@@ -733,6 +733,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
opp->supplies[i].u_watt = microwatt[i];
}
+free_microwatt:
kfree(microwatt);
free_microamp:
kfree(microamp);
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index cc8b4e01e29d..b4554b9f55b9 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
remove.o pci.o pci-driver.o search.o \
- pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
+ pci-sysfs.o rebar.o rom.o setup-res.o irq.o vpd.o \
setup-bus.o vc.o mmap.o setup-irq.o
obj-$(CONFIG_PCI) += msi/
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 23b8dda70cb2..c438cf888ad5 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -560,8 +560,6 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
switch (imx6_pcie->drvdata->variant) {
case IMX6SX:
- regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
break;
case IMX6QP:
case IMX6Q:
@@ -733,6 +731,8 @@ static int imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
break;
case IMX6SX:
+ regmap_clear_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+ IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
break;
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 407558f5d74a..282f218e8b68 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -194,7 +194,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev,
return ERR_PTR(ret);
}
- devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk);
+ ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk);
+ if (ret)
+ return ERR_PTR(ret);
return clk;
}
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index a7170fd0e847..84ed77a44d6d 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -233,7 +233,7 @@ static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node);
+ struct fwnode_handle *fwnode = dev_fwnode(pci->dev);
pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors,
&dw_pcie_msi_domain_ops, pp);
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
index 45b97a4b14db..ac0881659e76 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c
@@ -442,7 +442,7 @@ static const struct irq_domain_ops msi_domain_ops = {
static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct mobiveil_msi *msi = &pcie->rp.msi;
mutex_init(&msi->lock);
@@ -468,13 +468,11 @@ static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
- struct device_node *node = dev->of_node;
struct mobiveil_root_port *rp = &pcie->rp;
/* setup INTx */
- rp->intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
- &intx_domain_ops, pcie);
-
+ rp->intx_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX, &intx_domain_ops,
+ pcie);
if (!rp->intx_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
return -ENOMEM;
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 6be3266cd7b5..86fcdce424ec 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -77,6 +77,10 @@ int pci_host_common_probe(struct platform_device *pdev)
if (!pci_has_flag(PCI_PROBE_ONLY))
pci_add_flags(PCI_REASSIGN_ALL_BUS);
+ /* Do not reassign bus numbers if probe only */
+ if (!pci_has_flag(PCI_PROBE_ONLY))
+ pci_add_flags(PCI_REASSIGN_ALL_BUS);
+
bridge->sysdata = cfg;
bridge->ops = (struct pci_ops *)&ops->pci_ops;
bridge->msi_domain = true;
diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c
index bc630ab8a283..de5e809a537d 100644
--- a/drivers/pci/controller/pci-loongson.c
+++ b/drivers/pci/controller/pci-loongson.c
@@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_LPC, system_bus_quirk);
+static const struct pci_device_id loongson_internal_bridge_devids[] = {
+ { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) },
+ { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) },
+ { 0, },
+};
+
/*
* Some Loongson PCIe ports have hardware limitations on their Maximum Read
* Request Size. They can't handle anything larger than this. Sane
@@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev)
{
struct pci_bus *bus = pdev->bus;
struct pci_dev *bridge;
- static const struct pci_device_id bridge_devids[] = {
- { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) },
- { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) },
- { 0, },
- };
/* look for the matching bridge */
while (!pci_is_root_bus(bus)) {
bridge = bus->self;
bus = bus->parent;
- if (pci_match_id(bridge_devids, bridge)) {
+ if (pci_match_id(loongson_internal_bridge_devids, bridge)) {
if (pcie_get_readrq(pdev) > 256) {
pci_info(pdev, "limiting MRRS to 256\n");
pcie_set_readrq(pdev, 256);
@@ -230,11 +231,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus,
struct loongson_pci *priv = pci_bus_to_loongson_pci(bus);
/*
- * Do not read more than one device on the bus other than
- * the host bus.
+ * Do not read more than one device on the internal bridges.
*/
if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) {
- if (!pci_is_root_bus(bus) && (device > 0))
+ if (!pci_is_root_bus(bus) && (device > 0) &&
+ pci_match_id(loongson_internal_bridge_devids, bus->self))
return NULL;
}
diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c
index 6ad5427490b5..7f2115f35e99 100644
--- a/drivers/pci/controller/pcie-altera-msi.c
+++ b/drivers/pci/controller/pcie-altera-msi.c
@@ -171,7 +171,7 @@ static const struct irq_domain_ops msi_domain_ops = {
static int altera_allocate_domains(struct altera_msi *msi)
{
- struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
+ struct fwnode_handle *fwnode = dev_fwnode(&msi->pdev->dev);
msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
&msi_domain_ops, msi);
diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c
index a9536dc4bf96..3ce6e1efbba5 100644
--- a/drivers/pci/controller/pcie-altera.c
+++ b/drivers/pci/controller/pcie-altera.c
@@ -667,10 +667,9 @@ static void altera_pcie_isr(struct irq_desc *desc)
static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
{
struct device *dev = &pcie->pdev->dev;
- struct device_node *node = dev->of_node;
/* Setup INTx */
- pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
+ pcie->irq_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX,
&intx_domain_ops, pcie);
if (!pcie->irq_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
@@ -680,11 +679,20 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
return 0;
}
+static void altera_pcie_disable_irq(struct altera_pcie *pcie)
+{
+ if (pcie->pcie_data->version == ALTERA_PCIE_V1 ||
+ pcie->pcie_data->version == ALTERA_PCIE_V2) {
+ /* Disable all P2A interrupts */
+ cra_writel(pcie, 0, P2A_INT_ENABLE);
+ }
+}
+
static void altera_pcie_irq_teardown(struct altera_pcie *pcie)
{
+ altera_pcie_disable_irq(pcie);
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
irq_domain_remove(pcie->irq_domain);
- irq_dispose_mapping(pcie->irq);
}
static int altera_pcie_parse_dt(struct altera_pcie *pcie)
@@ -707,7 +715,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
if (pcie->irq < 0)
return pcie->irq;
- irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
return 0;
}
@@ -792,6 +799,12 @@ static int altera_pcie_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * The chained handler uses pcie->irq_domain, so set it only after the
+ * INTx domain has been created.
+ */
+ irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
+
/* clear all interrupts */
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
/* enable all interrupts */
@@ -802,7 +815,16 @@ static int altera_pcie_probe(struct platform_device *pdev)
bridge->busnr = pcie->root_bus_nr;
bridge->ops = &altera_pcie_ops;
- return pci_host_probe(bridge);
+ ret = pci_host_probe(bridge);
+ if (ret)
+ goto err_teardown_irq;
+
+ return 0;
+
+err_teardown_irq:
+ altera_pcie_irq_teardown(pcie);
+
+ return ret;
}
static void altera_pcie_remove(struct platform_device *pdev)
diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c
index 99a99900444d..593418c2bc3a 100644
--- a/drivers/pci/controller/pcie-iproc-bcma.c
+++ b/drivers/pci/controller/pcie-iproc-bcma.c
@@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
if (ret)
return ret;
- pcie->map_irq = iproc_bcma_pcie_map_irq;
+ bridge->map_irq = iproc_bcma_pcie_map_irq;
bcma_set_drvdata(bdev, pcie);
diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c
index acdc583d2980..42fd1b0ded51 100644
--- a/drivers/pci/controller/pcie-iproc-platform.c
+++ b/drivers/pci/controller/pcie-iproc-platform.c
@@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev)
switch (pcie->type) {
case IPROC_PCIE_PAXC:
case IPROC_PCIE_PAXC_V2:
- pcie->map_irq = NULL;
+ bridge->map_irq = NULL;
break;
default:
break;
diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c
index bd1c98b68851..49f127a2e123 100644
--- a/drivers/pci/controller/pcie-iproc.c
+++ b/drivers/pci/controller/pcie-iproc.c
@@ -1514,7 +1514,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
host->ops = &iproc_pcie_ops;
host->sysdata = pcie;
- host->map_irq = pcie->map_irq;
ret = pci_host_probe(host);
if (ret < 0) {
diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h
index 969ded03b8c2..c4443f236ca3 100644
--- a/drivers/pci/controller/pcie-iproc.h
+++ b/drivers/pci/controller/pcie-iproc.h
@@ -61,7 +61,6 @@ struct iproc_msi;
* @base_addr: PCIe host controller register base physical address
* @mem: host bridge memory window resource
* @phy: optional PHY device that controls the Serdes
- * @map_irq: function callback to map interrupts
* @ep_is_internal: indicates an internal emulated endpoint device is connected
* @iproc_cfg_read: indicates the iProc config read function should be used
* @rej_unconfig_pf: indicates the root complex needs to detect and reject
@@ -91,7 +90,6 @@ struct iproc_pcie {
phys_addr_t base_addr;
struct resource mem;
struct phy *phy;
- int (*map_irq)(const struct pci_dev *, u8, u8);
bool ep_is_internal;
bool iproc_cfg_read;
bool rej_unconfig_pf;
diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c
index 822d9b8e09f6..e0733a721925 100644
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -661,8 +661,8 @@ static int mtk_pcie_init_irq_domains(struct mtk_gen3_pcie *pcie)
/* Setup MSI */
mutex_init(&pcie->lock);
- pcie->msi_bottom_domain = irq_domain_add_linear(node, PCIE_MSI_IRQS_NUM,
- &mtk_msi_bottom_domain_ops, pcie);
+ pcie->msi_bottom_domain = irq_domain_create_linear(dev_fwnode(dev), PCIE_MSI_IRQS_NUM,
+ &mtk_msi_bottom_domain_ops, pcie);
if (!pcie->msi_bottom_domain) {
dev_err(dev, "failed to create MSI bottom domain\n");
ret = -ENODEV;
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 82e575e4d387..67c9f55c2f7e 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -7,6 +7,7 @@
* Honghui Zhang <honghui.zhang@mediatek.com>
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
@@ -60,7 +61,7 @@
/* MediaTek specific configuration registers */
#define PCIE_FTS_NUM 0x70c
#define PCIE_FTS_NUM_MASK GENMASK(15, 8)
-#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
+#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x)
#define PCIE_FC_CREDIT 0x73c
#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
@@ -141,29 +142,38 @@
struct mtk_pcie_port;
+/**
+ * enum mtk_pcie_quirks - MTK PCIe quirks
+ * @MTK_PCIE_FIX_CLASS_ID: host's class ID needed to be fixed
+ * @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
+ * @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
+ */
+enum mtk_pcie_quirks {
+ MTK_PCIE_FIX_CLASS_ID = BIT(0),
+ MTK_PCIE_FIX_DEVICE_ID = BIT(1),
+ MTK_PCIE_NO_MSI = BIT(2),
+};
+
/**
* struct mtk_pcie_soc - differentiate between host generations
- * @need_fix_class_id: whether this host's class ID needed to be fixed or not
- * @need_fix_device_id: whether this host's device ID needed to be fixed or not
- * @no_msi: Bridge has no MSI support, and relies on an external block
* @device_id: device ID which this host need to be fixed
* @ops: pointer to configuration access functions
* @startup: pointer to controller setting functions
* @setup_irq: pointer to initialize IRQ functions
+ * @quirks: PCIe device quirks.
*/
struct mtk_pcie_soc {
- bool need_fix_class_id;
- bool need_fix_device_id;
- bool no_msi;
unsigned int device_id;
struct pci_ops *ops;
int (*startup)(struct mtk_pcie_port *port);
int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node);
+ enum mtk_pcie_quirks quirks;
};
/**
* struct mtk_pcie_port - PCIe port information
* @base: IO mapped register base
+ * @phys_base: Physical address of the I/O register base region
* @list: port list
* @pcie: pointer to PCIe host info
* @reset: pointer to port reset control
@@ -186,6 +196,7 @@ struct mtk_pcie_soc {
*/
struct mtk_pcie_port {
void __iomem *base;
+ phys_addr_t phys_base;
struct list_head list;
struct mtk_pcie *pcie;
struct reset_control *reset;
@@ -397,7 +408,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
phys_addr_t addr;
/* MT2712/MT7622 only support 32-bit MSI addresses */
- addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+ addr = port->phys_base + PCIE_MSI_VECTOR;
msg->address_hi = 0;
msg->address_lo = lower_32_bits(addr);
@@ -493,7 +504,7 @@ static struct msi_domain_info mtk_msi_domain_info = {
static int mtk_pcie_allocate_msi_domains(struct mtk_pcie_port *port)
{
- struct fwnode_handle *fwnode = of_node_to_fwnode(port->pcie->dev->of_node);
+ struct fwnode_handle *fwnode = dev_fwnode(port->pcie->dev);
mutex_init(&port->lock);
@@ -520,7 +531,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
u32 val;
phys_addr_t msg_addr;
- msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+ msg_addr = port->phys_base + PCIE_MSI_VECTOR;
val = lower_32_bits(msg_addr);
writel(val, port->base + PCIE_IMSI_ADDR);
@@ -529,25 +540,29 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
writel(val, port->base + PCIE_INT_MASK);
}
-static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
+static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port)
{
- struct mtk_pcie_port *port, *tmp;
+ irq_set_chained_handler_and_data(port->irq, NULL, NULL);
- list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
- irq_set_chained_handler_and_data(port->irq, NULL, NULL);
+ if (port->irq_domain)
+ irq_domain_remove(port->irq_domain);
- if (port->irq_domain)
- irq_domain_remove(port->irq_domain);
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ if (port->msi_domain)
+ irq_domain_remove(port->msi_domain);
+ if (port->inner_domain)
+ irq_domain_remove(port->inner_domain);
+ }
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- if (port->msi_domain)
- irq_domain_remove(port->msi_domain);
- if (port->inner_domain)
- irq_domain_remove(port->inner_domain);
- }
+ irq_dispose_mapping(port->irq);
+}
- irq_dispose_mapping(port->irq);
- }
+static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
+{
+ struct mtk_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list)
+ mtk_pcie_irq_teardown_port(port);
}
static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@@ -703,12 +718,7 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
*/
writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
- /*
- * Described in PCIe CEM specification sections 2.2 (PERST# Signal) and
- * 2.2.1 (Initial Power-Up (G3 to S0)). The deassertion of PERST# should
- * be delayed 100ms (TPVPERL) for the power and clock to become stable.
- */
- msleep(100);
+ msleep(PCIE_T_PVPERL_MS);
/* De-assert PHY, PE, PIPE, MAC and configuration reset */
val = readl(port->base + PCIE_RST_CTRL);
@@ -717,7 +727,7 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
writel(val, port->base + PCIE_RST_CTRL);
/* Set up vendor ID and class code */
- if (soc->need_fix_class_id) {
+ if (soc->quirks & MTK_PCIE_FIX_CLASS_ID) {
val = PCI_VENDOR_ID_MEDIATEK;
writew(val, port->base + PCIE_CONF_VEND_ID);
@@ -725,7 +735,7 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
writew(val, port->base + PCIE_CONF_CLASS_ID);
}
- if (soc->need_fix_device_id)
+ if (soc->quirks & MTK_PCIE_FIX_DEVICE_ID)
writew(soc->device_id, port->base + PCIE_CONF_DEVICE_ID);
/* 100ms timeout value should be enough for Gen1/2 training */
@@ -835,7 +845,7 @@ static int mtk_pcie_startup_port(struct mtk_pcie_port *port)
return 0;
}
-static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
+static int mtk_pcie_enable_port(struct mtk_pcie_port *port)
{
struct mtk_pcie *pcie = port->pcie;
struct device *dev = pcie->dev;
@@ -844,7 +854,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
err = clk_prepare_enable(port->sys_ck);
if (err) {
dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot);
- goto err_sys_clk;
+ return err;
}
err = clk_prepare_enable(port->ahb_ck);
@@ -892,11 +902,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
goto err_phy_on;
}
- if (!pcie->soc->startup(port))
- return;
+ err = pcie->soc->startup(port);
+ if (err) {
+ dev_info(dev, "Port%d link down\n", port->slot);
+ goto err_soc_startup;
+ }
- dev_info(dev, "Port%d link down\n", port->slot);
+ return 0;
+err_soc_startup:
phy_power_off(port->phy);
err_phy_on:
phy_exit(port->phy);
@@ -912,8 +926,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
clk_disable_unprepare(port->ahb_ck);
err_ahb_clk:
clk_disable_unprepare(port->sys_ck);
-err_sys_clk:
- mtk_pcie_port_free(port);
+
+ return err;
}
static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
@@ -923,7 +937,8 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
struct mtk_pcie_port *port;
struct device *dev = pcie->dev;
struct platform_device *pdev = to_platform_device(dev);
- char name[10];
+ struct resource *res;
+ char name[20];
int err;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
@@ -931,7 +946,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
return -ENOMEM;
snprintf(name, sizeof(name), "port%d", slot);
- port->base = devm_platform_ioremap_resource_byname(pdev, name);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res) {
+ dev_err(dev, "failed to get port%d base\n", slot);
+ return -EINVAL;
+ }
+
+ port->phys_base = res->start;
+ port->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(port->base)) {
dev_err(dev, "failed to map port%d base\n", slot);
return PTR_ERR(port->base);
@@ -1081,8 +1103,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
return err;
/* enable each port, and then check link status */
- list_for_each_entry_safe(port, tmp, &pcie->ports, list)
- mtk_pcie_enable_port(port);
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ err = mtk_pcie_enable_port(port);
+ if (err) {
+ mtk_pcie_irq_teardown_port(port);
+ mtk_pcie_port_free(port);
+ }
+ }
/* power down PCIe subsys if slots are all empty (link down) */
if (list_empty(&pcie->ports))
@@ -1118,7 +1145,7 @@ static int mtk_pcie_probe(struct platform_device *pdev)
host->ops = pcie->soc->ops;
host->sysdata = pcie;
- host->msi_domain = pcie->soc->no_msi;
+ host->msi_domain = !!(pcie->soc->quirks & MTK_PCIE_NO_MSI);
err = pci_host_probe(host);
if (err)
@@ -1184,14 +1211,18 @@ static int mtk_pcie_resume_noirq(struct device *dev)
{
struct mtk_pcie *pcie = dev_get_drvdata(dev);
struct mtk_pcie_port *port, *tmp;
+ int err;
if (list_empty(&pcie->ports))
return 0;
clk_prepare_enable(pcie->free_ck);
- list_for_each_entry_safe(port, tmp, &pcie->ports, list)
- mtk_pcie_enable_port(port);
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ err = mtk_pcie_enable_port(port);
+ if (err)
+ mtk_pcie_port_free(port);
+ }
/* In case of EP was removed while system suspend. */
if (list_empty(&pcie->ports))
@@ -1206,9 +1237,9 @@ static const struct dev_pm_ops mtk_pcie_pm_ops = {
};
static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
- .no_msi = true,
.ops = &mtk_pcie_ops,
.startup = mtk_pcie_startup_port,
+ .quirks = MTK_PCIE_NO_MSI,
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
@@ -1218,19 +1249,18 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = {
- .need_fix_class_id = true,
.ops = &mtk_pcie_ops_v2,
.startup = mtk_pcie_startup_port_v2,
.setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_FIX_CLASS_ID,
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
- .need_fix_class_id = true,
- .need_fix_device_id = true,
.device_id = PCI_DEVICE_ID_MEDIATEK_7629,
.ops = &mtk_pcie_ops_v2,
.startup = mtk_pcie_startup_port_v2,
.setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
};
static const struct of_device_id mtk_pcie_ids[] = {
diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c
index 77f6366e7db1..2054492e1097 100644
--- a/drivers/pci/controller/pcie-rcar-host.c
+++ b/drivers/pci/controller/pcie-rcar-host.c
@@ -344,7 +344,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host)
struct rcar_pcie *pcie = &host->pcie;
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
struct resource_entry *win;
- LIST_HEAD(res);
int i = 0;
/* Try setting 5 GT/s link speed */
diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
index 5b82098f32b7..61bbd169bcb2 100644
--- a/drivers/pci/controller/pcie-xilinx-nwl.c
+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
@@ -502,7 +502,7 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
{
#ifdef CONFIG_PCI_MSI
struct device *dev = pcie->dev;
- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct nwl_msi *msi = &pcie->msi;
msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c
index b759b4e0bfb5..cba6bab067b6 100644
--- a/drivers/pci/endpoint/functions/pci-epf-ntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c
@@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb,
struct pci_epc *epc;
int ret;
- if (db_count > MAX_DB_COUNT)
- return -EINVAL;
-
ntb_epc = ntb->epc[type];
epc = ntb_epc->epc;
+ if (!db_count || db_count > MAX_DB_COUNT) {
+ dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n",
+ db_count, MAX_DB_COUNT);
+ return -EINVAL;
+ }
+
if (msix)
ret = epf_ntb_configure_msix(ntb, type, db_count);
else
@@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
u8 func_no, vfunc_no;
struct pci_epc *epc;
struct device *dev;
- u32 db_count;
int ret;
ntb_epc = ntb->epc[type];
@@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
func_no = ntb_epc->func_no;
vfunc_no = ntb_epc->vfunc_no;
- db_count = ntb->db_count;
- if (db_count > MAX_DB_COUNT) {
- dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT);
+ if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) {
+ dev_err(dev, "DB count %d out of range (1 - %d)\n",
+ ntb->db_count, MAX_DB_COUNT);
return -EINVAL;
}
- ntb->db_count = db_count;
epc = ntb_epc->epc;
if (msi_capable) {
- ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count);
+ ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count);
if (ret) {
dev_err(dev, "%s intf: MSI configuration failed\n",
pci_epc_interface_string(type));
@@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
}
if (msix_capable) {
- ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count,
+ ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count,
ntb_epc->msix_bar,
ntb_epc->msix_table_offset);
if (ret) {
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 6590f77c1a0d..0ebd37e7ecd0 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -484,7 +484,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
{
const struct pci_epc_features *epc_features;
struct device *dev;
- u32 db_count;
int ret;
dev = &ntb->epf->dev;
@@ -496,14 +495,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
return -EINVAL;
}
- db_count = ntb->db_count;
- if (db_count > MAX_DB_COUNT) {
- dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT);
+ if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) {
+ dev_err(dev, "DB count %d out of range (1 - %d)\n",
+ ntb->db_count, MAX_DB_COUNT);
return -EINVAL;
}
- ntb->db_count = db_count;
-
if (epc_features->msi_capable) {
ret = pci_epc_set_msi(ntb->epf->epc,
ntb->epf->func_no,
diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c
index f6f4a778a986..b638731aa5ff 100644
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -737,7 +737,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
ret = msix_setup_interrupts(dev, entries, nvec, affd);
if (ret)
- goto out_unmap;
+ goto out_disable;
/* Disable INTX */
pci_intx_for_msi(dev, 0);
@@ -756,8 +756,6 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
pcibios_free_irq(dev);
return 0;
-out_unmap:
- iounmap(dev->msix_base);
out_disable:
dev->msix_enabled = 0;
pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 300caafcfa10..3cd05547853a 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1459,8 +1459,8 @@ static ssize_t resource##n##_resize_store(struct device *dev, \
const char *buf, size_t count)\
{ \
struct pci_dev *pdev = to_pci_dev(dev); \
- unsigned long size, flags; \
- int ret, i; \
+ unsigned long size; \
+ int ret; \
u16 cmd; \
\
if (kstrtoul(buf, 0, &size) < 0) \
@@ -1485,17 +1485,9 @@ static ssize_t resource##n##_resize_store(struct device *dev, \
pci_write_config_word(pdev, PCI_COMMAND, \
cmd & ~PCI_COMMAND_MEMORY); \
\
- flags = pci_resource_flags(pdev, n); \
- \
pci_remove_resource_files(pdev); \
\
- for (i = 0; i < PCI_STD_NUM_BARS; i++) { \
- if (pci_resource_len(pdev, i) && \
- pci_resource_flags(pdev, i) == flags) \
- pci_release_resource(pdev, i); \
- } \
- \
- ret = pci_resize_resource(pdev, n, size); \
+ ret = pci_resize_resource(pdev, n, size, 0); \
\
pci_assign_unassigned_bus_resources(pdev->bus); \
\
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e7648a1abfdb..4b12b286f490 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1518,9 +1518,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool
pci_power_name(dev->current_state),
pci_power_name(state));
- if (dev->bus->self)
- pcie_aspm_pm_state_change(dev->bus->self, locked);
-
return 0;
}
@@ -1873,33 +1870,6 @@ static void pci_restore_config_space(struct pci_dev *pdev)
}
}
-static void pci_restore_rebar_state(struct pci_dev *pdev)
-{
- unsigned int pos, nbars, i;
- u32 ctrl;
-
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
- if (!pos)
- return;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
- PCI_REBAR_CTRL_NBAR_SHIFT;
-
- for (i = 0; i < nbars; i++, pos += 8) {
- struct resource *res;
- int bar_idx, size;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
- res = pdev->resource + bar_idx;
- size = pci_rebar_bytes_to_size(resource_size(res));
- ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
- ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
- pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
- }
-}
-
/**
* pci_restore_state - Restore the saved state of a PCI device
* @dev: PCI device that we're dealing with
@@ -3832,115 +3802,6 @@ void pci_acs_init(struct pci_dev *dev)
pci_enable_acs(dev);
}
-/**
- * pci_rebar_find_pos - find position of resize ctrl reg for BAR
- * @pdev: PCI device
- * @bar: BAR to find
- *
- * Helper to find the position of the ctrl register for a BAR.
- * Returns -ENOTSUPP if resizable BARs are not supported at all.
- * Returns -ENOENT if no ctrl register for the BAR could be found.
- */
-static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
-{
- unsigned int pos, nbars, i;
- u32 ctrl;
-
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
- if (!pos)
- return -ENOTSUPP;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
- PCI_REBAR_CTRL_NBAR_SHIFT;
-
- for (i = 0; i < nbars; i++, pos += 8) {
- int bar_idx;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
- if (bar_idx == bar)
- return pos;
- }
-
- return -ENOENT;
-}
-
-/**
- * pci_rebar_get_possible_sizes - get possible sizes for BAR
- * @pdev: PCI device
- * @bar: BAR to query
- *
- * Get the possible sizes of a resizable BAR as bitmask defined in the spec
- * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
- */
-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
-{
- int pos;
- u32 cap;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return 0;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
- cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
-
- /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
- if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
- bar == 0 && cap == 0x700)
- return 0x3f00;
-
- return cap;
-}
-EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
-
-/**
- * pci_rebar_get_current_size - get the current size of a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- *
- * Read the size of a BAR from the resizable BAR config.
- * Returns size if found or negative error code.
- */
-int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
-{
- int pos;
- u32 ctrl;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return pos;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
-}
-
-/**
- * pci_rebar_set_size - set a new size for a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- * @size: new size as defined in the spec (0=1MB, 19=512GB)
- *
- * Set the new size of a BAR as defined in the spec.
- * Returns zero if resizing was successful, error code otherwise.
- */
-int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
-{
- int pos;
- u32 ctrl;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return pos;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
- ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
- pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
- return 0;
-}
-
/**
* pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
* @dev: the PCI device
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 3c35aae7431f..278af5b263bf 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -263,6 +263,9 @@ enum pci_bar_type {
struct device *pci_get_host_bridge_device(struct pci_dev *dev);
void pci_put_host_bridge_device(struct device *dev);
+int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size,
+ int exclude_bars);
+
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int crs_timeout);
@@ -640,6 +643,7 @@ static inline int acpi_get_rc_resources(struct device *dev, const char *hid,
}
#endif
+void pci_restore_rebar_state(struct pci_dev *pdev);
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
static inline u64 pci_rebar_size_to_bytes(int size)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 5df3a6ea6601..28fa0a9b18c8 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -68,11 +68,10 @@
* Restrict the speed to 2.5GT/s then with the Target Link Speed field,
* request a retrain and check the result.
*
- * If this turns out successful and we know by the Vendor:Device ID it is
- * safe to do so, then lift the restriction, letting the devices negotiate
- * a higher speed. Also check for a similar 2.5GT/s speed restriction the
- * firmware may have already arranged and lift it with ports that already
- * report their data link being up.
+ * If this turns out successful, or where a 2.5GT/s speed restriction has
+ * been previously arranged by the firmware and the port reports its link
+ * already being up, lift the restriction, in a hope it is safe to do so,
+ * letting the devices negotiate a higher speed.
*
* Otherwise revert the speed to the original setting and request a retrain
* again to remove any residual state, ignoring the result as it's supposed
@@ -83,12 +82,9 @@
*/
int pcie_failed_link_retrain(struct pci_dev *dev)
{
- static const struct pci_device_id ids[] = {
- { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */
- {}
- };
- u16 lnksta, lnkctl2;
+ u16 lnksta, lnkctl2, oldlnkctl2;
int ret = -ENOTTY;
+ u32 lnkcap;
if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) ||
!pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting)
@@ -96,10 +92,9 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2);
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+ oldlnkctl2 = lnkctl2;
if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) ==
PCI_EXP_LNKSTA_LBMS) {
- u16 oldlnkctl2 = lnkctl2;
-
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
@@ -107,35 +102,29 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
ret = pcie_retrain_link(dev, false);
- if (ret) {
- pci_info(dev, "retraining failed\n");
- pcie_capability_write_word(dev, PCI_EXP_LNKCTL2,
- oldlnkctl2);
- pcie_retrain_link(dev, true);
- return ret;
- }
-
- pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+ if (ret)
+ goto err;
}
- if ((lnksta & PCI_EXP_LNKSTA_DLLLA) &&
- (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT &&
- pci_match_id(ids, dev)) {
- u32 lnkcap;
-
+ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+ if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT &&
+ (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n");
- pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS;
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
ret = pcie_retrain_link(dev, false);
- if (ret) {
- pci_info(dev, "retraining failed\n");
- return ret;
- }
+ if (ret)
+ goto err;
}
+ return ret;
+err:
+ pci_info(dev, "retraining failed\n");
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, oldlnkctl2);
+
+ pcie_retrain_link(dev, true);
return ret;
}
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
new file mode 100644
index 000000000000..920ef3ab4bda
--- /dev/null
+++ b/drivers/pci/rebar.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Resizable BAR Extended Capability handling.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "pci.h"
+
+/**
+ * pci_rebar_find_pos - find position of resize ctrl reg for BAR
+ * @pdev: PCI device
+ * @bar: BAR to find
+ *
+ * Helper to find the position of the ctrl register for a BAR.
+ * Returns -ENOTSUPP if resizable BARs are not supported at all.
+ * Returns -ENOENT if no ctrl register for the BAR could be found.
+ */
+static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+{
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos)
+ return -ENOTSUPP;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+ PCI_REBAR_CTRL_NBAR_SHIFT;
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ int bar_idx;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+ if (bar_idx == bar)
+ return pos;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * pci_rebar_get_possible_sizes - get possible sizes for BAR
+ * @pdev: PCI device
+ * @bar: BAR to query
+ *
+ * Get the possible sizes of a resizable BAR as bitmask defined in the spec
+ * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
+ */
+u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+{
+ int pos;
+ u32 cap;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return 0;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+ cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
+
+ /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+ if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+ bar == 0 && cap == 0x700)
+ return 0x3f00;
+
+ return cap;
+}
+EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
+
+/**
+ * pci_rebar_get_current_size - get the current size of a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ *
+ * Read the size of a BAR from the resizable BAR config.
+ * Returns size if found or negative error code.
+ */
+int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+{
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
+}
+
+/**
+ * pci_rebar_set_size - set a new size for a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ * @size: new size as defined in the spec (0=1MB, 19=512GB)
+ *
+ * Set the new size of a BAR as defined in the spec.
+ * Returns zero if resizing was successful, error code otherwise.
+ */
+int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+{
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+ return 0;
+}
+
+void pci_restore_rebar_state(struct pci_dev *pdev)
+{
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos)
+ return;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
+ nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ struct resource *res;
+ int bar_idx, size;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
+ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+ res = pci_resource_n(pdev, bar_idx);
+ size = pci_rebar_bytes_to_size(resource_size(res));
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+ }
+}
+
+/**
+ * pci_resize_resource - reconfigure a Resizable BAR and resources
+ * @dev: the PCI device
+ * @resno: index of the BAR to be resized
+ * @size: new size as defined in the spec (0=1MB, 31=128TB)
+ * @exclude_bars: a mask of BARs that should not be released
+ *
+ * Reconfigure @resno to @size and re-run resource assignment algorithm
+ * with the new size.
+ *
+ * Prior to resize, release @dev resources that share a bridge window with
+ * @resno. This unpins the bridge window resource to allow changing it.
+ *
+ * The caller may prevent releasing a particular BAR by providing
+ * @exclude_bars mask, but this may result in the resize operation failing
+ * due to insufficient space.
+ *
+ * Return: 0 on success, or negative on error. In case of an error, the
+ * resources are restored to their original places.
+ */
+int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+ int exclude_bars)
+{
+ struct pci_host_bridge *host;
+ int old, ret;
+ u32 sizes;
+ u16 cmd;
+
+ /* Check if we must preserve the firmware's resource assignment */
+ host = pci_find_host_bridge(dev->bus);
+ if (host->preserve_config)
+ return -ENOTSUPP;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (cmd & PCI_COMMAND_MEMORY)
+ return -EBUSY;
+
+ sizes = pci_rebar_get_possible_sizes(dev, resno);
+ if (!sizes)
+ return -ENOTSUPP;
+
+ if (!(sizes & BIT(size)))
+ return -EINVAL;
+
+ old = pci_rebar_get_current_size(dev, resno);
+ if (old < 0)
+ return old;
+
+ ret = pci_rebar_set_size(dev, resno, size);
+ if (ret)
+ return ret;
+
+ ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
+ if (ret)
+ goto error_resize;
+
+ return 0;
+
+error_resize:
+ pci_rebar_set_size(dev, resno, old);
+ return ret;
+}
+EXPORT_SYMBOL(pci_resize_resource);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index e18d3a4383ba..f2105c6ceef5 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -5,13 +5,40 @@
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
*/
+
+#include <linux/align.h>
+#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include "pci.h"
+#define PCI_ROM_HEADER_SIZE 0x1A
+#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18
+#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15
+#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7)
+#define PCI_ROM_IMAGE_LEN 0x10
+#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512
+#define PCI_ROM_IMAGE_SIGNATURE 0xAA55
+
+/* Data structure signature is "PCIR" in ASCII representation */
+#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350
+#define PCI_ROM_DATA_STRUCT_LEN 0x0A
+
+/*
+ * Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at
+ * least 24 bytes (0x18), large enough to cover every fixed field this
+ * driver reads (up to the Indicator byte at offset 0x15). Reject smaller
+ * device-claimed lengths so the follow-up readers in pci_get_rom_size()
+ * cannot escape the mapped ROM window.
+ */
+#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18
+
/**
* pci_enable_rom - enable ROM decoding for a PCI device
* @pdev: PCI device to enable
@@ -69,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev)
}
EXPORT_SYMBOL_GPL(pci_disable_rom);
+static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image,
+ void __iomem *rom, size_t size,
+ bool expect_valid)
+{
+ unsigned long rom_end = (unsigned long)rom + size - 1;
+ unsigned long header_end;
+ u16 signature;
+
+ /*
+ * Per PCI Firmware r3.3, sec 5.1, each image must start on a
+ * 512-byte boundary and must contain the PCI Expansion ROM header.
+ * Because @rom is page-aligned (returned by ioremap()), checking
+ * 512-byte alignment of @image is equivalent to enforcing the
+ * spec's sector-aligned layout within the ROM. This also
+ * satisfies the natural-alignment requirement of readw() on archs
+ * such as arm64 that disallow unaligned IOMEM access.
+ */
+ if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE))
+ return false;
+
+ if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1,
+ &header_end))
+ return false;
+
+ if (image < rom || header_end > rom_end)
+ return false;
+
+ /* Standard PCI ROMs start out with these bytes 55 AA */
+ signature = readw(image);
+ if (signature != PCI_ROM_IMAGE_SIGNATURE) {
+ if (expect_valid) {
+ pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n",
+ PCI_ROM_IMAGE_SIGNATURE, signature);
+ } else {
+ pci_info(pdev, "No more images in PCI ROM\n");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds,
+ void __iomem *rom, size_t size)
+{
+ unsigned long rom_end = (unsigned long)rom + size - 1;
+ unsigned long end;
+ u32 signature;
+ u16 data_len;
+
+ /*
+ * Some CPU architectures require IOMEM access addresses to be
+ * aligned, for example arm64, so since we're about to call
+ * readl(), check here for 4-byte alignment.
+ */
+ if (!IS_ALIGNED((unsigned long)pds, 4))
+ return false;
+
+ if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1,
+ &end))
+ return false;
+
+ if (pds < rom || end > rom_end)
+ return false;
+
+ signature = readl(pds);
+ if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) {
+ pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n",
+ PCI_ROM_DATA_STRUCT_SIGNATURE, signature);
+ return false;
+ }
+
+ data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN);
+ if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX)
+ return false;
+
+ if (check_add_overflow((unsigned long)pds, data_len - 1, &end))
+ return false;
+
+ if (end > rom_end)
+ return false;
+
+ return true;
+}
+
/**
* pci_get_rom_size - obtain the actual size of the ROM image
* @pdev: target PCI device
@@ -84,37 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom,
size_t size)
{
void __iomem *image;
- int last_image;
unsigned int length;
+ bool last_image;
image = rom;
do {
void __iomem *pds;
- /* Standard PCI ROMs start out with these bytes 55 AA */
- if (readw(image) != 0xAA55) {
- pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n",
- readw(image));
+ if (!pci_rom_header_valid(pdev, image, rom, size, true))
break;
- }
- /* get the PCI data structure and check its "PCIR" signature */
- pds = image + readw(image + 24);
- if (readl(pds) != 0x52494350) {
- pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n",
- readl(pds));
+
+ /* Get the PCI data structure and check its "PCIR" signature */
+ pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT);
+ if (!pci_rom_data_struct_valid(pdev, pds, rom, size))
break;
- }
- last_image = readb(pds + 21) & 0x80;
- length = readw(pds + 16);
- image += length * 512;
- /* Avoid iterating through memory outside the resource window */
- if (image >= rom + size)
+
+ last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) &
+ PCI_ROM_LAST_IMAGE_INDICATOR_BIT;
+ length = readw(pds + PCI_ROM_IMAGE_LEN);
+ image += length * PCI_ROM_IMAGE_SECTOR_SIZE;
+
+ if (!last_image &&
+ !pci_rom_header_valid(pdev, image, rom, size, false))
break;
- if (!last_image) {
- if (readw(image) != 0xAA55) {
- pci_info(pdev, "No more image in the PCI ROM\n");
- break;
- }
- }
} while (length && !last_image);
/* never return a size larger than the PCI resource window */
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index d07c1d9ed062..98d0038e32ef 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2221,18 +2221,16 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
+static int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type,
+ struct list_head *saved)
{
struct pci_dev_resource *dev_res;
struct pci_dev *next;
- LIST_HEAD(saved);
LIST_HEAD(added);
LIST_HEAD(failed);
unsigned int i;
int ret;
- down_read(&pci_bus_sem);
-
/* Walk to the root hub, releasing bridge BARs when possible */
next = bridge;
do {
@@ -2249,9 +2247,9 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
if (res->child)
continue;
- ret = add_to_list(&saved, bridge, res, 0, 0);
+ ret = add_to_list(saved, bridge, res, 0, 0);
if (ret)
- goto cleanup;
+ return ret;
pci_info(bridge, "%s %pR: releasing\n", res_name, res);
@@ -2267,62 +2265,108 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
next = bridge->bus ? bridge->bus->self : NULL;
} while (next);
- if (list_empty(&saved)) {
- up_read(&pci_bus_sem);
+ if (list_empty(saved))
return -ENOENT;
- }
__pci_bus_size_bridges(bridge->subordinate, &added);
__pci_bridge_assign_resources(bridge, &added, &failed);
BUG_ON(!list_empty(&added));
if (!list_empty(&failed)) {
- ret = -ENOSPC;
- goto cleanup;
+ free_list(&failed);
+ return -ENOSPC;
}
- list_for_each_entry(dev_res, &saved, list) {
+ list_for_each_entry(dev_res, saved, list) {
/* Skip the bridge we just assigned resources for */
if (bridge == dev_res->dev)
continue;
+ if (!dev_res->dev->subordinate)
+ continue;
+
bridge = dev_res->dev;
pci_setup_bridge(bridge->subordinate);
}
- free_list(&saved);
- up_read(&pci_bus_sem);
return 0;
+}
-cleanup:
- /* Restore size and flags */
- list_for_each_entry(dev_res, &failed, list) {
- struct resource *res = dev_res->res;
+int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size,
+ int exclude_bars)
+{
+ struct resource *res = pdev->resource + resno;
+ unsigned long flags = res->flags;
+ struct pci_dev_resource *dev_res;
+ struct pci_bus *bus = pdev->bus;
+ struct resource *r;
+ LIST_HEAD(saved);
+ unsigned int i;
+ int ret = 0;
- res->start = dev_res->start;
- res->end = dev_res->end;
- res->flags = dev_res->flags;
+ down_read(&pci_bus_sem);
+
+ pci_dev_for_each_resource(pdev, r, i) {
+ if (i >= PCI_BRIDGE_RESOURCES)
+ break;
+
+ if (exclude_bars & BIT(i))
+ continue;
+
+ if (!pci_resource_len(pdev, i) || r->flags != flags)
+ continue;
+
+ ret = add_to_list(&saved, pdev, r, 0, 0);
+ if (ret)
+ goto restore;
+ pci_release_resource(pdev, i);
}
- free_list(&failed);
+ res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
+
+ if (!bus->self)
+ goto out;
+
+ ret = pci_reassign_bridge_resources(bus->self, res->flags, &saved);
+ if (ret)
+ goto restore;
+
+out:
+ up_read(&pci_bus_sem);
+ free_list(&saved);
+ return ret;
+
+restore:
/* Revert to the old configuration */
list_for_each_entry(dev_res, &saved, list) {
struct resource *res = dev_res->res;
+ struct pci_dev *dev = dev_res->dev;
- bridge = dev_res->dev;
- i = res - bridge->resource;
+ i = res - dev->resource;
+
+ if (res->parent) {
+ release_child_resources(res);
+ pci_release_resource(dev, i);
+ }
res->start = dev_res->start;
res->end = dev_res->end;
res->flags = dev_res->flags;
- pci_claim_resource(bridge, i);
- pci_setup_bridge(bridge->subordinate);
- }
- free_list(&saved);
- up_read(&pci_bus_sem);
+ if (pci_claim_resource(dev, i))
+ continue;
- return ret;
+ if (i < PCI_BRIDGE_RESOURCES) {
+ const char *res_name = pci_resource_name(dev, i);
+
+ pci_update_resource(dev, i);
+ pci_info(dev, "%s %pR: old value restored\n",
+ res_name, res);
+ }
+ if (dev->subordinate)
+ pci_setup_bridge(dev->subordinate);
+ }
+ goto out;
}
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index c6d933ddfd46..3f7f350e9e46 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -427,59 +427,6 @@ void pci_release_resource(struct pci_dev *dev, int resno)
}
EXPORT_SYMBOL(pci_release_resource);
-int pci_resize_resource(struct pci_dev *dev, int resno, int size)
-{
- struct resource *res = dev->resource + resno;
- struct pci_host_bridge *host;
- int old, ret;
- u32 sizes;
- u16 cmd;
-
- /* Check if we must preserve the firmware's resource assignment */
- host = pci_find_host_bridge(dev->bus);
- if (host->preserve_config)
- return -ENOTSUPP;
-
- /* Make sure the resource isn't assigned before resizing it. */
- if (!(res->flags & IORESOURCE_UNSET))
- return -EBUSY;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (cmd & PCI_COMMAND_MEMORY)
- return -EBUSY;
-
- sizes = pci_rebar_get_possible_sizes(dev, resno);
- if (!sizes)
- return -ENOTSUPP;
-
- if (!(sizes & BIT(size)))
- return -EINVAL;
-
- old = pci_rebar_get_current_size(dev, resno);
- if (old < 0)
- return old;
-
- ret = pci_rebar_set_size(dev, resno, size);
- if (ret)
- return ret;
-
- res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
-
- /* Check if the new config works by trying to assign everything. */
- if (dev->bus->self) {
- ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
- if (ret)
- goto error_resize;
- }
- return 0;
-
-error_resize:
- pci_rebar_set_size(dev, resno, old);
- res->end = res->start + pci_rebar_size_to_bytes(old) - 1;
- return ret;
-}
-EXPORT_SYMBOL(pci_resize_resource);
-
int pci_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 840b7f8a31c5..72dc65c72467 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -111,6 +111,9 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
return -ENOMEM;
match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node);
+ if (!match || !match->data)
+ return -ENODEV;
+
drvdata = match->data;
if (of_property_read_bool(dev->of_node, "mux-states")) {
diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
index 1640c5522f0e..53229faa5cc5 100644
--- a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
+++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c
@@ -343,7 +343,7 @@ static inline int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin,
return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2,
CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin,
- !!us << pin);
+ !us << pin);
}
static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev,
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8167.c b/drivers/pinctrl/mediatek/pinctrl-mt8167.c
index ba7f30c3296f..07148f4a8c00 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8167.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8167.c
@@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8167_smt_set[] = {
MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12),
MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0),
MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1),
- MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2),
+ MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2),
MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11),
MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10),
MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11),
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8516.c b/drivers/pinctrl/mediatek/pinctrl-mt8516.c
index 950275c47122..43b434ed7d46 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8516.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8516.c
@@ -244,7 +244,7 @@ static const struct mtk_pin_ies_smt_set mt8516_smt_set[] = {
MTK_PIN_IES_SMT_SPEC(24, 25, 0xA00, 12),
MTK_PIN_IES_SMT_SPEC(26, 30, 0xA00, 0),
MTK_PIN_IES_SMT_SPEC(31, 33, 0xA00, 1),
- MTK_PIN_IES_SMT_SPEC(34, 39, 0xA900, 2),
+ MTK_PIN_IES_SMT_SPEC(34, 39, 0xA00, 2),
MTK_PIN_IES_SMT_SPEC(40, 40, 0xA10, 11),
MTK_PIN_IES_SMT_SPEC(41, 43, 0xA00, 10),
MTK_PIN_IES_SMT_SPEC(44, 47, 0xA00, 11),
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 4f613f908208..5cc00fdc48d8 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -618,7 +618,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
pc->chip.set = meson_gpio_set;
pc->chip.base = -1;
pc->chip.ngpio = pc->data->num_pins;
- pc->chip.can_sleep = true;
+ pc->chip.can_sleep = false;
ret = gpiochip_add_data(&pc->chip, pc);
if (ret) {
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index fc11c3d55fa8..c5b954c9537d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -880,7 +880,7 @@ static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset)
return 0;
out:
- regulator_put(s_reg->regulator);
+ regulator_put(reg);
return ret;
}
diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c
index a3921f8106c1..a3e5f65f9d72 100644
--- a/drivers/platform/x86/amd/pmc/pmc-quirks.c
+++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c
@@ -17,6 +17,7 @@
struct quirk_entry {
u32 s2idle_bug_mmio;
bool spurious_8042;
+ bool need_suspend_delay;
};
static struct quirk_entry quirk_s2idle_bug = {
@@ -27,6 +28,10 @@ static struct quirk_entry quirk_spurious_8042 = {
.spurious_8042 = true,
};
+static struct quirk_entry quirk_s2idle_need_suspend_delay = {
+ .need_suspend_delay = true,
+};
+
static const struct dmi_system_id fwbug_list[] = {
{
.ident = "L14 Gen2 AMD",
@@ -197,6 +202,35 @@ static const struct dmi_system_id fwbug_list[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"),
}
},
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */
+ {
+ .ident = "Zen3-based IdeaPad Slim and similar",
+ .driver_data = &quirk_s2idle_need_suspend_delay,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ /*
+ * Note: there are also some Zen2-based 82X* devices that
+ * need different quirks, they're already handled above
+ */
+ DMI_MATCH(DMI_PRODUCT_NAME, "82X"),
+ }
+ },
+ {
+ .ident = "Zen3+-based IdeaPad Slim and similar",
+ .driver_data = &quirk_s2idle_need_suspend_delay,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83K"),
+ }
+ },
+ {
+ .ident = "IdeaPad Slim 3 15ARP10 (83MM)",
+ .driver_data = &quirk_s2idle_need_suspend_delay,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83MM"),
+ }
+ },
/* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */
{
.ident = "Thinkpad L14 Gen3",
@@ -350,6 +384,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev)
amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio);
}
+bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev)
+{
+ return dev->quirks && dev->quirks->need_suspend_delay;
+}
+
void amd_pmc_quirks_init(struct amd_pmc_dev *dev)
{
const struct dmi_system_id *dmi_id;
diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c
index 259bac3744d4..d5b543f74a22 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -16,6 +16,7 @@
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/limits.h>
@@ -147,6 +148,11 @@ static bool disable_workarounds;
module_param(disable_workarounds, bool, 0644);
MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
+static int delay_suspend = -1;
+module_param(delay_suspend, int, 0644);
+MODULE_PARM_DESC(delay_suspend,
+ "Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org");
+
static struct amd_pmc_dev pmc;
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
@@ -790,6 +796,55 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
return rc;
}
+static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev)
+{
+ /*
+ * Starting a new HW sleep cycle right after waking from one
+ * can cause electrical problems triggering the over voltage protection.
+ * That is avoided by delaying the next suspend a bit, see also
+ * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/
+ */
+ struct smu_metrics table;
+
+ return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status;
+}
+
+static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev)
+{
+ /*
+ * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some
+ * me-time before sleeping or they get uncooperative after waking
+ * up and don't send events for keyboard and lid switch anymore.
+ *
+ * Unfortunately this doesn't entirely fix the problem: It can still
+ * happen when resuming with a timer (wakealarm), but at least the
+ * more common usecases (wakeup by opening lid or pressing a key)
+ * work fine with this workaround.
+ *
+ * See https://bugzilla.kernel.org/show_bug.cgi?id=221383
+ */
+ if (amd_pmc_quirk_need_suspend_delay(pdev)) {
+ /*
+ * delay_suspend=1 force-enables this, otherwise it can be
+ * disabled with disable_workarounds or delay_suspend=0
+ */
+ if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) {
+ dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n");
+ return true;
+ }
+ dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n");
+ } else if (delay_suspend == 1) {
+ dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n",
+ dmi_get_system_info(DMI_SYS_VENDOR),
+ dmi_get_system_info(DMI_PRODUCT_NAME),
+ dmi_get_system_info(DMI_PRODUCT_FAMILY),
+ dmi_get_system_info(DMI_BOARD_VENDOR),
+ dmi_get_system_info(DMI_BOARD_NAME));
+ return true;
+ }
+ return false;
+}
+
static void amd_pmc_s2idle_prepare(void)
{
struct amd_pmc_dev *pdev = &pmc;
@@ -824,11 +879,10 @@ static void amd_pmc_s2idle_prepare(void)
static void amd_pmc_s2idle_check(void)
{
struct amd_pmc_dev *pdev = &pmc;
- struct smu_metrics table;
int rc;
- /* Avoid triggering OVP */
- if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status)
+ if (amd_pmc_intermediate_wakeup_need_delay(pdev) ||
+ amd_pmc_want_suspend_delay(pdev))
msleep(2500);
/* Dump the IdleMask before we add to the STB */
diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h
index 88e0232592dc..5e7b8d5dc5d6 100644
--- a/drivers/platform/x86/amd/pmc/pmc.h
+++ b/drivers/platform/x86/amd/pmc/pmc.h
@@ -40,6 +40,7 @@ struct amd_pmc_dev {
};
void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);
+bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev);
void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
/* List of supported CPU ids */
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c
index 391f7ea4431e..d62c74e9d346 100644
--- a/drivers/platform/x86/xo15-ebook.c
+++ b/drivers/platform/x86/xo15-ebook.c
@@ -38,6 +38,7 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids);
struct ebook_switch {
struct input_dev *input;
char phys[32]; /* for input device */
+ bool gpe_enabled;
};
static int ebook_send_state(struct acpi_device *device)
@@ -131,7 +132,7 @@ static int ebook_switch_add(struct acpi_device *device)
/* Button's GPE is run-wake GPE */
acpi_enable_gpe(device->wakeup.gpe_device,
device->wakeup.gpe_number);
- device_set_wakeup_enable(&device->dev, true);
+ button->gpe_enabled = true;
}
return 0;
@@ -147,6 +148,10 @@ static void ebook_switch_remove(struct acpi_device *device)
{
struct ebook_switch *button = acpi_driver_data(device);
+ if (button->gpe_enabled)
+ acpi_disable_gpe(device->wakeup.gpe_device,
+ device->wakeup.gpe_number);
+
input_unregister_device(button->input);
kfree(button);
}
diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
index e52253694747..a2e9a6bdc2b8 100644
--- a/drivers/pmdomain/imx/imx8m-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c
@@ -54,6 +54,15 @@ struct imx8m_blk_ctrl_domain_data {
* register.
*/
u32 mipi_phy_rst_mask;
+
+ /*
+ * VC8000E reset de-assertion edge and AXI clock may have a timing issue.
+ * Workaround: Set bit2 (vc8000e_clk_en) of BLK_CLK_EN_CSR to 0 to gate off
+ * both AXI clock and VC8000E clock sent to VC8000E and AXI clock sent to
+ * VPU_NOC m_v_2 interface during VC8000E power up(VC8000E reset is
+ * de-asserted by HW)
+ */
+ bool is_errata_err050531;
};
#define DOMAIN_MAX_CLKS 4
@@ -108,7 +117,11 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
dev_err(bc->dev, "failed to enable clocks\n");
goto bus_put;
}
- regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
+ if (data->is_errata_err050531)
+ regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+ else
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
/* power up upstream GPC domain */
ret = pm_runtime_get_sync(domain->power_dev);
@@ -117,6 +130,9 @@ static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd)
goto clk_disable;
}
+ if (data->is_errata_err050531)
+ regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask);
+
/* wait for reset to propagate */
udelay(5);
@@ -510,12 +526,38 @@ static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[]
.clk_mask = BIT(2),
.path_names = (const char *[]){"vc8000e"},
.num_paths = 1,
+ .is_errata_err050531 = true,
},
};
+static int imx8mp_vpu_power_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
+ power_nb);
+
+ if (action == GENPD_NOTIFY_ON) {
+ /*
+ * On power up we have no software backchannel to the GPC to
+ * wait for the ADB handshake to happen, so we just delay for a
+ * bit. On power down the GPC driver waits for the handshake.
+ */
+
+ udelay(5);
+
+ /* set "fuse" bits to enable the VPUs */
+ regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
+ regmap_set_bits(bc->regmap, 0x14, 0xffffffff);
+ }
+
+ return NOTIFY_OK;
+}
+
static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = {
.max_reg = 0x18,
- .power_notifier_fn = imx8mm_vpu_power_notifier,
+ .power_notifier_fn = imx8mp_vpu_power_notifier,
.domains = imx8mp_vpu_blk_ctl_domain_data,
.num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data),
};
diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c
index 5fa6ba7f41e1..b874947b84f2 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -302,8 +302,10 @@ static bool is_full_charged(struct charger_manager *cm)
if (cm->battery_status == POWER_SUPPLY_STATUS_FULL
&& desc->fullbatt_vchkdrop_uV)
uV += desc->fullbatt_vchkdrop_uV;
- if (uV >= desc->fullbatt_uV)
- return true;
+ if (uV >= desc->fullbatt_uV) {
+ is_full = true;
+ goto out;
+ }
}
}
diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c
index d84b81e77362..eb01c67e5d1e 100644
--- a/drivers/power/supply/cpcap-battery.c
+++ b/drivers/power/supply/cpcap-battery.c
@@ -415,10 +415,13 @@ static void cpcap_battery_detect_battery_type(struct cpcap_battery_ddata *ddata)
if (IS_ERR_OR_NULL(nvmem)) {
ddata->check_nvmem = true;
dev_info_once(ddata->dev, "Can not find battery nvmem device. Assuming generic lipo battery\n");
- } else if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
- battery_id = 0;
- ddata->check_nvmem = true;
- dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
+ } else {
+ if (nvmem_device_read(nvmem, 2, 1, &battery_id) < 0) {
+ battery_id = 0;
+ ddata->check_nvmem = true;
+ dev_warn(ddata->dev, "Can not read battery nvmem device. Assuming generic lipo battery\n");
+ }
+ nvmem_device_put(nvmem);
}
switch (battery_id) {
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 1893d37dd575..4f350db05efa 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -262,18 +262,13 @@ static int power_supply_check_supplies(struct power_supply *psy)
if (cnt == 1)
return 0;
- /* All supplies found, allocate char ** array for filling */
- psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from),
+ /* All supplies found, allocate char * array for filling */
+ psy->supplied_from = devm_kcalloc(&psy->dev,
+ cnt - 1, sizeof(*psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from)
return -ENOMEM;
- *psy->supplied_from = devm_kcalloc(&psy->dev,
- cnt - 1, sizeof(**psy->supplied_from),
- GFP_KERNEL);
- if (!*psy->supplied_from)
- return -ENOMEM;
-
return power_supply_populate_supplied_from(psy);
}
#else
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
index 660a71b7263c..8a4187a67f7d 100644
--- a/drivers/pwm/pwm-imx27.c
+++ b/drivers/pwm/pwm-imx27.c
@@ -218,7 +218,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
- unsigned long period_cycles, duty_cycles, prescale, period_us, tmp;
+ unsigned long period_cycles, duty_cycles, prescale, period_us;
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct pwm_state cstate;
unsigned long long c;
@@ -227,6 +227,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
int val;
int ret;
u32 cr;
+ u64 tmp;
pwm_get_state(pwm, &cstate);
@@ -270,6 +271,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
val = readl(imx->mmio_base + MX3_PWMPR);
val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
cr = readl(imx->mmio_base + MX3_PWMCR);
+
+ /*
+ * tmp stores period in nanoseconds. Result fits in u64 since
+ * val <= 0xfffe and prescaler in [1, 0x1000].
+ */
tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr);
tmp = DIV_ROUND_UP_ULL(tmp, clkrate);
period_us = DIV_ROUND_UP_ULL(tmp, 1000);
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 83323c3d10af..61ac7c8a1c7f 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -384,7 +384,6 @@ static void tsi721_db_dpc(struct work_struct *work)
idb_work);
struct rio_mport *mport;
struct rio_dbell *dbell;
- int found = 0;
u32 wr_ptr, rd_ptr;
u64 *idb_entry;
u32 regval;
@@ -402,6 +401,8 @@ static void tsi721_db_dpc(struct work_struct *work)
rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE;
while (wr_ptr != rd_ptr) {
+ int found = 0;
+
idb_entry = (u64 *)(priv->idb_base +
(TSI721_IDB_ENTRY_SIZE * rd_ptr));
rd_ptr++;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 0eadf6dc30e2..3f352d1c64b7 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -228,7 +228,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1,
ret = regulator_lock_nested(rdev1, ww_ctx);
WARN_ON(ret);
ret = regulator_lock_nested(rdev2, ww_ctx);
- if (ret != -EDEADLOCK) {
+ if (ret != -EDEADLK) {
WARN_ON(ret);
goto exit;
}
@@ -244,7 +244,7 @@ static void regulator_lock_two(struct regulator_dev *rdev1,
swap(held, contended);
ret = regulator_lock_nested(contended, ww_ctx);
- if (ret != -EDEADLOCK) {
+ if (ret != -EDEADLK) {
WARN_ON(ret);
break;
}
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
index 73d511eb1c1d..768cdce0d4ec 100644
--- a/drivers/regulator/ltc3676.c
+++ b/drivers/regulator/ltc3676.c
@@ -45,11 +45,11 @@
#define LTC3676_DVBxA_REF_SELECT BIT(5)
#define LTC3676_DVBxB_PGOOD_MASK BIT(5)
-#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3)
-#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4)
-#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5)
-#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6)
-#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7)
+#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(2)
+#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(3)
+#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(4)
+#define LTC3676_IRQSTAT_THERMAL_WARN BIT(5)
+#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(6)
enum ltc3676_reg {
LTC3676_SW1,
diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c
index 432654fbd7a1..c005e65ba0ec 100644
--- a/drivers/regulator/scmi-regulator.c
+++ b/drivers/regulator/scmi-regulator.c
@@ -298,7 +298,7 @@ static int process_scmi_regulator_of_node(struct scmi_device *sdev,
static int scmi_regulator_probe(struct scmi_device *sdev)
{
int d, ret, num_doms;
- struct device_node *np, *child;
+ struct device_node *np;
const struct scmi_handle *handle = sdev->handle;
struct scmi_regulator_info *rinfo;
struct scmi_protocol_handle *ph;
@@ -342,11 +342,11 @@ static int scmi_regulator_probe(struct scmi_device *sdev)
*/
of_node_get(handle->dev->of_node);
np = of_find_node_by_name(handle->dev->of_node, "regulators");
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo);
/* abort on any mem issue */
if (ret == -ENOMEM) {
- of_node_put(child);
+ of_node_put(np);
return ret;
}
}
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index 03e5f5d533eb..e5b1f0e66019 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -108,6 +108,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy
struct minidump_region __iomem *ptr;
struct minidump_region region;
int seg_cnt, i;
+ int ret = 0;
dma_addr_t da;
size_t size;
char *name;
@@ -128,17 +129,22 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy
if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) {
name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL);
if (!name) {
- iounmap(ptr);
- return -ENOMEM;
+ ret = -ENOMEM;
+ break;
}
da = le64_to_cpu(region.address);
size = le64_to_cpu(region.size);
- rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name);
+ ret = rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t,
+ name);
+ if (ret) {
+ kfree(name);
+ break;
+ }
}
}
iounmap(ptr);
- return 0;
+ return ret;
}
void qcom_minidump(struct rproc *rproc, unsigned int minidump_id,
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
index e752594b6971..8e01589ab7af 100644
--- a/drivers/reset/reset-sunxi.c
+++ b/drivers/reset/reset-sunxi.c
@@ -44,7 +44,7 @@ static int sunxi_reset_init(struct device_node *np)
data->membase = ioremap(res.start, size);
if (!data->membase) {
ret = -ENOMEM;
- goto err_alloc;
+ goto err_mem_region;
}
spin_lock_init(&data->lock);
@@ -57,6 +57,8 @@ static int sunxi_reset_init(struct device_node *np)
return reset_controller_register(&data->rcdev);
+err_mem_region:
+ release_mem_region(res.start, size);
err_alloc:
kfree(data);
return ret;
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index da2fe69ba204..f5909e454893 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -1526,7 +1526,6 @@ static void qcom_glink_rpdev_release(struct device *dev)
{
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
- kfree(rpdev->driver_override);
kfree(rpdev);
}
@@ -1770,7 +1769,6 @@ static void qcom_glink_device_release(struct device *dev)
/* Release qcom_glink_alloc_channel() reference */
kref_put(&channel->refcount, qcom_glink_channel_release);
- kfree(rpdev->driver_override);
kfree(rpdev);
}
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 662a67431404..615d06af9929 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -419,33 +419,6 @@ rpmsg_show_attr(src, src, "0x%x\n");
rpmsg_show_attr(dst, dst, "0x%x\n");
rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
-static ssize_t driver_override_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(dev);
- int ret;
-
- ret = driver_set_override(dev, &rpdev->driver_override, buf, count);
- if (ret)
- return ret;
-
- return count;
-}
-
-static ssize_t driver_override_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct rpmsg_device *rpdev = to_rpmsg_device(dev);
- ssize_t len;
-
- device_lock(dev);
- len = sysfs_emit(buf, "%s\n", rpdev->driver_override);
- device_unlock(dev);
- return len;
-}
-static DEVICE_ATTR_RW(driver_override);
-
static ssize_t modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -466,7 +439,6 @@ static struct attribute *rpmsg_dev_attrs[] = {
&dev_attr_dst.attr,
&dev_attr_src.attr,
&dev_attr_announce.attr,
- &dev_attr_driver_override.attr,
NULL,
};
ATTRIBUTE_GROUPS(rpmsg_dev);
@@ -485,9 +457,11 @@ static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
const struct rpmsg_device_id *ids = rpdrv->id_table;
unsigned int i;
+ int ret;
- if (rpdev->driver_override)
- return !strcmp(rpdev->driver_override, drv->name);
+ ret = device_match_driver_override(dev, drv);
+ if (ret >= 0)
+ return ret;
if (ids)
for (i = 0; ids[i].name[0]; i++)
@@ -597,6 +571,7 @@ static struct bus_type rpmsg_bus = {
.name = "rpmsg",
.match = rpmsg_dev_match,
.dev_groups = rpmsg_dev_groups,
+ .driver_override = true,
.uevent = rpmsg_uevent,
.probe = rpmsg_dev_probe,
.remove = rpmsg_dev_remove,
@@ -622,11 +597,9 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev,
device_initialize(dev);
if (driver_override) {
- ret = driver_set_override(dev, &rpdev->driver_override,
- driver_override,
- strlen(driver_override));
+ ret = device_set_driver_override(dev, driver_override);
if (ret) {
- dev_err(dev, "device_set_override failed: %d\n", ret);
+ dev_err(dev, "device_set_driver_override() failed: %d\n", ret);
put_device(dev);
return ret;
}
@@ -635,8 +608,6 @@ int rpmsg_register_device_override(struct rpmsg_device *rpdev,
ret = device_add(dev);
if (ret) {
dev_err(dev, "device_add failed: %d\n", ret);
- kfree(rpdev->driver_override);
- rpdev->driver_override = NULL;
put_device(dev);
}
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index f1af0f674615..905ac7910c98 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -378,7 +378,6 @@ static void virtio_rpmsg_release_device(struct device *dev)
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev);
- kfree(rpdev->driver_override);
kfree(vch);
}
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
index 5f3a3e60a19d..862833519d7f 100644
--- a/drivers/rtc/rtc-abx80x.c
+++ b/drivers/rtc/rtc-abx80x.c
@@ -545,7 +545,8 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
status &= ~ABX8XX_STATUS_BLF;
- tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0);
+ tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS,
+ status);
if (tmp < 0)
return tmp;
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 095de4e0e4f3..e06f3b9cab2c 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -932,6 +932,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
unsigned char rtc_control;
unsigned address_space;
u32 flags = 0;
+ bool hpet_registered = false;
struct nvmem_config nvmem_cfg = {
.name = "cmos_nvram",
.word_size = 1,
@@ -1089,6 +1090,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
" failed in rtc_init().");
goto cleanup1;
}
+ hpet_registered = true;
} else
rtc_cmos_int_handler = cmos_interrupt;
@@ -1138,6 +1140,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
if (is_valid_irq(rtc_irq))
free_irq(rtc_irq, cmos_rtc.rtc);
cleanup1:
+ if (hpet_registered) {
+ hpet_mask_rtc_irq_bit(RTC_IRQMASK);
+ hpet_unregister_irq_handler(cmos_interrupt);
+ }
cmos_rtc.dev = NULL;
cleanup0:
if (RTC_IOMAPPED)
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 74aad2b12460..e7ba52cb5412 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -264,6 +264,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1338_BIT_OSF)
return -EINVAL;
break;
+ case ds_1337:
+ case ds_1339:
+ case ds_1341:
+ case ds_3231:
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
+ if (ret)
+ return ret;
+ if (tmp & DS1337_BIT_OSF)
+ return -EINVAL;
+ break;
case ds_1340:
if (tmp & DS1340_BIT_nEOSC)
return -EINVAL;
@@ -274,13 +284,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
if (tmp & DS1340_BIT_OSF)
return -EINVAL;
break;
- case ds_1341:
- ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp);
- if (ret)
- return ret;
- if (tmp & DS1337_BIT_OSF)
- return -EINVAL;
- break;
case ds_1388:
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
if (ret)
@@ -303,7 +306,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_hour = bcd2bin(tmp);
/* rx8130 is bit position, not BCD */
if (ds1307->type == rx_8130)
- t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f);
+ t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1;
else
t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
@@ -375,14 +378,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
DS1338_BIT_OSF, 0);
break;
- case ds_1340:
- regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
- DS1340_BIT_OSF, 0);
- break;
+ case ds_1337:
+ case ds_1339:
case ds_1341:
+ case ds_3231:
regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
DS1337_BIT_OSF, 0);
break;
+ case ds_1340:
+ regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
+ DS1340_BIT_OSF, 0);
+ break;
case ds_1388:
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
DS1388_BIT_OSF, 0);
diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c
index 5b96a6d39210..aa724a945c08 100644
--- a/drivers/rtc/rtc-mpfs.c
+++ b/drivers/rtc/rtc-mpfs.c
@@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm)
ctrl |= CONTROL_UPLOAD_BIT;
writel(ctrl, rtcdev->base + CONTROL_REG);
- ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US,
+ ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US,
false, rtcdev->base + CONTROL_REG);
if (ret) {
dev_err(dev, "timed out uploading time to rtc");
diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c
index 8d7737e0e2e0..6ef9c4efd7c9 100644
--- a/drivers/rtc/rtc-msc313.c
+++ b/drivers/rtc/rtc-msc313.c
@@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = {
static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id)
{
- struct msc313_rtc *priv = dev_get_drvdata(dev_id);
+ struct msc313_rtc *priv = dev_id;
u16 reg;
reg = readw(priv->rtc_base + REG_RTC_STATUS_INT);
@@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev)
priv->rtc_dev->range_max = U32_MAX;
ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED,
- dev_name(&pdev->dev), &pdev->dev);
+ dev_name(&pdev->dev), priv);
if (ret) {
dev_err(dev, "Could not request IRQ\n");
return ret;
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index bc5193d81f9c..54debecd3cb7 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -122,6 +122,9 @@ static int monwrite_new_hdr(struct mon_private *monpriv)
kfree(monbuf->data);
kfree(monbuf);
monbuf = NULL;
+ } else if (monbuf->hdr.datalen != monhdr->datalen) {
+ /* Data with buffer reuse must not change its length */
+ return -EINVAL;
}
} else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) {
if (mon_buf_count >= mon_max_bufs)
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/drivers/s390/crypto/zcrypt_cex2c.c b/drivers/s390/crypto/zcrypt_cex2c.c
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/drivers/s390/crypto/zcrypt_cex2c.h b/drivers/s390/crypto/zcrypt_cex2c.h
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c
index 5b079b8b7a08..f41b7f1e4416 100644
--- a/drivers/scsi/elx/efct/efct_hw.c
+++ b/drivers/scsi/elx/efct/efct_hw.c
@@ -1998,6 +1998,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort,
wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort);
if (!wqcb) {
efc_log_err(hw->os, "can't allocate request tag\n");
+ io_to_abort->abort_in_progress = false;
+ kref_put(&io_to_abort->ref, io_to_abort->release);
return -ENOSPC;
}
diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c
index e6addab66a60..6a871a59c909 100644
--- a/drivers/scsi/elx/efct/efct_unsol.c
+++ b/drivers/scsi/elx/efct/efct_unsol.c
@@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
efc_log_err(efct, "Additional CDB not supported\n");
+ efct_scsi_io_free(io);
return -EIO;
}
/*
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 42a89ab33e1c..f8a30f33fe7d 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -5031,6 +5031,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
if (phys_disk->in_reset) {
cmd->result = DID_RESET << 16;
+ atomic_dec(&phys_disk->ioaccel_cmds_out);
+ scsi_dma_unmap(cmd);
+ if (use_sg > h->ioaccel_maxsg)
+ hpsa_unmap_ioaccel2_sg_chain_block(h, cp);
return -1;
}
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index dc18d84c54c3..f78e087f2aad 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -8201,6 +8201,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_bsmbx;
}
}
+ mempool_free(mboxq, phba->mbox_mem_pool);
/*
* 1 for cmd, 1 for rsp, NVME adds an extra one
@@ -8326,8 +8327,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
goto out_free_sg_dma_buf;
}
- mempool_free(mboxq, phba->mbox_mem_pool);
-
/* Verify OAS is supported */
lpfc_sli4_oas_verify(phba);
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index d3ff212d28b2..49e60976a61a 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -588,10 +588,7 @@ static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL);
static ssize_t non_fatal_log_show(struct device *cdev,
struct device_attribute *attr, char *buf)
{
- u32 count;
-
- count = pm80xx_get_non_fatal_dump(cdev, attr, buf);
- return count;
+ return pm80xx_get_non_fatal_dump(cdev, attr, buf);
}
static DEVICE_ATTR_RO(non_fatal_log);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 083bf3386c6b..d3795db86b32 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1848,7 +1848,7 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
return 0;
}
-EXPORT_SYMBOL(scsi_scan_host_selected);
+
static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index a9eb548c0de9..ce69240accb2 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/err.h>
+#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/blkdev.h>
@@ -40,8 +41,6 @@
#include <scsi/scsi_transport_sas.h>
#include "scsi_sas_internal.h"
-#include "scsi_priv.h"
-
struct sas_host_attrs {
struct list_head rphy_list;
struct mutex lock;
@@ -222,12 +221,45 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
* SAS host attributes
*/
+/*
+ * Set shost->opt_sectors from the DMA optimal mapping size, but only
+ * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(),
+ * indicating a genuine optimization hint from an IOMMU or DMA backend.
+ * When the two are equal (e.g. IOMMU disabled / passthrough), no real
+ * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size
+ * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment).
+ */
+static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost)
+{
+ struct device *dma_dev = shost->dma_dev;
+ size_t opt = dma_opt_mapping_size(dma_dev);
+ size_t max = dma_max_mapping_size(dma_dev);
+ unsigned int opt_sectors;
+
+ /* opt >= max means no real hint was provided by the DMA layer */
+ if (opt >= max)
+ return;
+
+ /* Clamp to max_sectors to avoid overflow in sector arithmetic */
+ opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT,
+ shost->max_sectors);
+
+ /* Guard against zero before rounddown_pow_of_two() */
+ if (!opt_sectors)
+ return;
+
+ /*
+ * Round down to power-of-two so filesystem geometry calculations
+ * (e.g. XFS stripe width/unit) always produce clean divisors.
+ */
+ shost->opt_sectors = rounddown_pow_of_two(opt_sectors);
+}
+
static int sas_host_setup(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
- struct device *dma_dev = shost->dma_dev;
INIT_LIST_HEAD(&sas_host->rphy_list);
mutex_init(&sas_host->lock);
@@ -239,10 +271,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev,
dev_printk(KERN_ERR, dev, "fail to a bsg device %d\n",
shost->host_no);
- if (dma_dev->dma_mask) {
- shost->opt_sectors = min_t(unsigned int, shost->max_sectors,
- dma_opt_mapping_size(dma_dev) >> SECTOR_SHIFT);
- }
+ sas_dma_setup_opt_sectors(shost);
return 0;
}
@@ -1683,22 +1712,6 @@ int scsi_is_sas_rphy(const struct device *dev)
}
EXPORT_SYMBOL(scsi_is_sas_rphy);
-static void scan_channel_zero(struct Scsi_Host *shost, uint id, u64 lun)
-{
- struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
- struct sas_rphy *rphy;
-
- list_for_each_entry(rphy, &sas_host->rphy_list, list) {
- if (rphy->identify.device_type != SAS_END_DEVICE ||
- rphy->scsi_target_id == -1)
- continue;
-
- if (id == SCAN_WILD_CARD || id == rphy->scsi_target_id) {
- scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id,
- lun, SCSI_SCAN_MANUAL);
- }
- }
-}
/*
* SCSI scan helper
@@ -1708,41 +1721,23 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel,
uint id, u64 lun)
{
struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
- int res = 0;
- int i;
-
- switch (channel) {
- case 0:
- mutex_lock(&sas_host->lock);
- scan_channel_zero(shost, id, lun);
- mutex_unlock(&sas_host->lock);
- break;
-
- case SCAN_WILD_CARD:
- mutex_lock(&sas_host->lock);
- scan_channel_zero(shost, id, lun);
- mutex_unlock(&sas_host->lock);
+ struct sas_rphy *rphy;
- for (i = 1; i <= shost->max_channel; i++) {
- res = scsi_scan_host_selected(shost, i, id, lun,
- SCSI_SCAN_MANUAL);
- if (res)
- goto exit_scan;
- }
- break;
+ mutex_lock(&sas_host->lock);
+ list_for_each_entry(rphy, &sas_host->rphy_list, list) {
+ if (rphy->identify.device_type != SAS_END_DEVICE ||
+ rphy->scsi_target_id == -1)
+ continue;
- default:
- if (channel <= shost->max_channel) {
- res = scsi_scan_host_selected(shost, channel, id, lun,
- SCSI_SCAN_MANUAL);
- } else {
- res = -EINVAL;
+ if ((channel == SCAN_WILD_CARD || channel == 0) &&
+ (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) {
+ scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id,
+ lun, SCSI_SCAN_MANUAL);
}
- break;
}
+ mutex_unlock(&sas_host->lock);
-exit_scan:
- return res;
+ return 0;
}
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 96921a661f64..238eccaa5567 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -862,10 +862,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
if (val >= SG_MAX_QUEUE)
break;
rinfo[val].req_state = srp->done + 1;
- rinfo[val].problem =
- srp->header.masked_status &
- srp->header.host_status &
- srp->header.driver_status;
+ rinfo[val].problem = srp->header.masked_status ||
+ srp->header.host_status ||
+ srp->header.driver_status;
if (srp->done)
rinfo[val].duration =
srp->header.duration;
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index dc194c76f38b..78d2d9b9ebb4 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -2613,7 +2613,7 @@ static int pqi_scan_finished(struct Scsi_Host *shost,
{
struct pqi_ctrl_info *ctrl_info;
- ctrl_info = shost_priv(shost);
+ ctrl_info = shost_to_hba(shost);
return !mutex_is_locked(&ctrl_info->scan_mutex);
}
diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c
index 400b7b385a44..7d632fad1300 100644
--- a/drivers/slimbus/qcom-ctrl.c
+++ b/drivers/slimbus/qcom-ctrl.c
@@ -626,7 +626,7 @@ static int qcom_slim_probe(struct platform_device *pdev)
return ret;
}
-static int qcom_slim_remove(struct platform_device *pdev)
+static void qcom_slim_remove(struct platform_device *pdev)
{
struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev);
@@ -635,7 +635,6 @@ static int qcom_slim_remove(struct platform_device *pdev)
clk_disable_unprepare(ctrl->rclk);
clk_disable_unprepare(ctrl->hclk);
destroy_workqueue(ctrl->rxwq);
- return 0;
}
/*
@@ -721,7 +720,7 @@ static const struct of_device_id qcom_slim_dt_match[] = {
static struct platform_driver qcom_slim_driver = {
.probe = qcom_slim_probe,
- .remove = qcom_slim_remove,
+ .remove_new = qcom_slim_remove,
.driver = {
.name = "qcom_slim_ctrl",
.of_match_table = qcom_slim_dt_match,
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index de1834d52cb8..3a09f244ee8b 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -1612,6 +1612,7 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct qcom_slim_ngd_ctrl *ctrl;
+ int irq;
int ret;
struct pdr_service *pds;
@@ -1625,20 +1626,16 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
if (IS_ERR(ctrl->base))
return PTR_ERR(ctrl->base);
- ret = platform_get_irq(pdev, 0);
- if (ret < 0)
- return ret;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
- ret = devm_request_irq(dev, ret, qcom_slim_ngd_interrupt,
- IRQF_TRIGGER_HIGH, "slim-ngd", ctrl);
+ ret = devm_request_irq(dev, irq, qcom_slim_ngd_interrupt,
+ IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+ "slim-ngd", ctrl);
if (ret)
return dev_err_probe(&pdev->dev, ret, "request IRQ failed\n");
- ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
- ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
- if (IS_ERR(ctrl->notifier))
- return PTR_ERR(ctrl->notifier);
-
ctrl->dev = dev;
ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
ctrl->framer.superfreq =
@@ -1660,46 +1657,54 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
init_completion(&ctrl->qmi_up);
ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl);
- if (IS_ERR(ctrl->pdr)) {
- ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr),
- "Failed to init PDR handle\n");
- goto err_pdr_alloc;
- }
+ if (IS_ERR(ctrl->pdr))
+ return dev_err_probe(dev, PTR_ERR(ctrl->pdr), "Failed to init PDR handle\n");
+
+ ret = of_qcom_slim_ngd_register(dev, ctrl);
+ if (ret)
+ goto err_pdr_release;
pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd");
if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
ret = dev_err_probe(dev, PTR_ERR(pds), "pdr add lookup failed\n");
- goto err_pdr_lookup;
+ goto err_unregister_ngd;
}
- return of_qcom_slim_ngd_register(dev, ctrl);
+ ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
+ ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
+ if (IS_ERR(ctrl->notifier)) {
+ ret = PTR_ERR(ctrl->notifier);
+ goto err_unregister_ngd;
+ }
-err_pdr_alloc:
- qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
+ enable_irq(irq);
-err_pdr_lookup:
+ return 0;
+
+err_unregister_ngd:
+ qcom_slim_ngd_unregister(ctrl);
+err_pdr_release:
pdr_handle_release(ctrl->pdr);
return ret;
}
-static int qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
+static void qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
{
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
- qcom_slim_ngd_unregister(ctrl);
+ pdr_handle_release(ctrl->pdr);
+ qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
- return 0;
+ qcom_slim_ngd_unregister(ctrl);
}
-static int qcom_slim_ngd_remove(struct platform_device *pdev)
+static void qcom_slim_ngd_remove(struct platform_device *pdev)
{
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- pdr_handle_release(ctrl->pdr);
- qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
qcom_slim_ngd_enable(ctrl, false);
qcom_slim_ngd_exit_dma(ctrl);
qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
@@ -1708,7 +1713,6 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev)
kfree(ctrl->ngd);
ctrl->ngd = NULL;
- return 0;
}
static int __maybe_unused qcom_slim_ngd_runtime_idle(struct device *dev)
@@ -1751,7 +1755,7 @@ static const struct dev_pm_ops qcom_slim_ngd_dev_pm_ops = {
static struct platform_driver qcom_slim_ngd_ctrl_driver = {
.probe = qcom_slim_ngd_ctrl_probe,
- .remove = qcom_slim_ngd_ctrl_remove,
+ .remove_new = qcom_slim_ngd_ctrl_remove,
.driver = {
.name = "qcom,slim-ngd-ctrl",
.of_match_table = qcom_slim_ngd_dt_match,
@@ -1760,7 +1764,7 @@ static struct platform_driver qcom_slim_ngd_ctrl_driver = {
static struct platform_driver qcom_slim_ngd_driver = {
.probe = qcom_slim_ngd_probe,
- .remove = qcom_slim_ngd_remove,
+ .remove_new = qcom_slim_ngd_remove,
.driver = {
.name = QCOM_SLIM_NGD_DRV_NAME,
.pm = &qcom_slim_ngd_dev_pm_ops,
diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c
index 3ee0c7c1e9a4..75044b06502a 100644
--- a/drivers/soc/fsl/qe/qe.c
+++ b/drivers/soc/fsl/qe/qe.c
@@ -89,6 +89,9 @@ void qe_reset(void)
if (qe_immr == NULL)
qe_immr = ioremap(get_qe_base(), QE_IMMAP_SIZE);
+ if (!qe_immr)
+ panic("QE:ioremap failed!");
+
qe_snums_init();
qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID,
diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c
index 148f54d9691d..824a21576b88 100644
--- a/drivers/soc/ti/k3-ringacc.c
+++ b/drivers/soc/ti/k3-ringacc.c
@@ -1012,7 +1012,7 @@ static int k3_ringacc_ring_pop_head_proxy(struct k3_ring *ring, void *elem)
static int k3_ringacc_ring_pop_tail_proxy(struct k3_ring *ring, void *elem)
{
return k3_ringacc_ring_access_proxy(ring, elem,
- K3_RINGACC_ACCESS_MODE_POP_HEAD);
+ K3_RINGACC_ACCESS_MODE_POP_TAIL);
}
static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem,
@@ -1083,7 +1083,7 @@ static int k3_ringacc_ring_pop_io(struct k3_ring *ring, void *elem)
static int k3_ringacc_ring_pop_tail_io(struct k3_ring *ring, void *elem)
{
return k3_ringacc_ring_access_io(ring, elem,
- K3_RINGACC_ACCESS_MODE_POP_HEAD);
+ K3_RINGACC_ACCESS_MODE_POP_TAIL);
}
/*
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 6cafeee8ee2a..e25e5554ce89 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -282,6 +282,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
{
dw_spi_enable_chip(dws, 0);
dw_spi_set_clk(dws, 0);
+ dws->current_freq = 0;
}
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index a1d60e51c053..22677e37f47e 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -625,6 +625,7 @@ static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi)
espi->dma_rx = NULL;
fail_free_page:
free_page((unsigned long)espi->zeropage);
+ espi->zeropage = NULL;
return ret;
}
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 6b66b0e62db4..607159769e52 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -603,7 +603,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
tx->sgl, tx->nents, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
- dmaengine_terminate_all(controller->dma_tx);
+ dmaengine_terminate_sync(controller->dma_rx);
return -EINVAL;
}
@@ -624,8 +624,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
transfer_timeout);
if (!timeout) {
dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n");
- dmaengine_terminate_all(controller->dma_tx);
- dmaengine_terminate_all(controller->dma_rx);
+ dmaengine_terminate_sync(controller->dma_tx);
+ dmaengine_terminate_sync(controller->dma_rx);
fsl_lpspi_reset(fsl_lpspi);
return -ETIMEDOUT;
}
@@ -634,8 +634,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
transfer_timeout);
if (!timeout) {
dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n");
- dmaengine_terminate_all(controller->dma_tx);
- dmaengine_terminate_all(controller->dma_rx);
+ dmaengine_terminate_sync(controller->dma_tx);
+ dmaengine_terminate_sync(controller->dma_rx);
fsl_lpspi_reset(fsl_lpspi);
return -ETIMEDOUT;
}
@@ -644,8 +644,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
fsl_lpspi->target_aborted) {
dev_dbg(fsl_lpspi->dev,
"I/O Error in DMA TX interrupted\n");
- dmaengine_terminate_all(controller->dma_tx);
- dmaengine_terminate_all(controller->dma_rx);
+ dmaengine_terminate_sync(controller->dma_tx);
+ dmaengine_terminate_sync(controller->dma_rx);
fsl_lpspi_reset(fsl_lpspi);
return -EINTR;
}
@@ -654,8 +654,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
fsl_lpspi->target_aborted) {
dev_dbg(fsl_lpspi->dev,
"I/O Error in DMA RX interrupted\n");
- dmaengine_terminate_all(controller->dma_tx);
- dmaengine_terminate_all(controller->dma_rx);
+ dmaengine_terminate_sync(controller->dma_tx);
+ dmaengine_terminate_sync(controller->dma_rx);
fsl_lpspi_reset(fsl_lpspi);
return -EINTR;
}
diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c
index 77526f794068..6afa37d10c7c 100644
--- a/drivers/spi/spi-hisi-kunpeng.c
+++ b/drivers/spi/spi-hisi-kunpeng.c
@@ -518,10 +518,8 @@ static int hisi_spi_probe(struct platform_device *pdev)
}
ret = spi_register_controller(host);
- if (ret) {
- dev_err(dev, "failed to register spi host, ret=%d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register spi host\n");
if (hisi_spi_debugfs_init(hs))
dev_info(dev, "failed to create debugfs dir\n");
diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c
index 06626f406f68..fd1f294af02e 100644
--- a/drivers/spi/spi-meson-spifc.c
+++ b/drivers/spi/spi-meson-spifc.c
@@ -363,6 +363,7 @@ static void meson_spifc_remove(struct platform_device *pdev)
pm_runtime_get_sync(&pdev->dev);
clk_disable_unprepare(spifc->clk);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c
index a1696672d12f..2de7ae058c29 100644
--- a/drivers/spi/spi-rpc-if.c
+++ b/drivers/spi/spi-rpc-if.c
@@ -192,8 +192,12 @@ static int __maybe_unused rpcif_spi_suspend(struct device *dev)
static int __maybe_unused rpcif_spi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct rpcif *rpc = spi_controller_get_devdata(ctlr);
+ int ret;
- rpcif_hw_init(dev, false);
+ ret = rpcif_hw_init(rpc->dev, false);
+ if (ret)
+ return ret;
return spi_controller_resume(ctlr);
}
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 65c11909659c..6d0376b6e532 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -242,7 +242,7 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p)
+static int sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p)
{
u32 mask = SICTR_TXRST | SICTR_RXRST;
u32 data;
@@ -251,8 +251,8 @@ static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p)
data |= mask;
sh_msiof_write(p, SICTR, data);
- readl_poll_timeout_atomic(p->mapbase + SICTR, data, !(data & mask), 1,
- 100);
+ return readl_poll_timeout_atomic(p->mapbase + SICTR, data,
+ !(data & mask), 1, 100);
}
static const u32 sh_msiof_spi_div_array[] = {
@@ -926,7 +926,9 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr,
int ret;
/* reset registers */
- sh_msiof_spi_reset_regs(p);
+ ret = sh_msiof_spi_reset_regs(p);
+ if (ret)
+ return ret;
/* setup clocks (clock already enabled in chipselect()) */
if (!spi_controller_is_target(p->ctlr))
diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c
index bf6fcfda3c65..5dac90d63f7f 100644
--- a/drivers/spi/spi-uniphier.c
+++ b/drivers/spi/spi-uniphier.c
@@ -659,6 +659,8 @@ static int uniphier_spi_probe(struct platform_device *pdev)
priv->host = host;
priv->is_save_param = false;
+ init_completion(&priv->xfer_done);
+
priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(priv->base)) {
ret = PTR_ERR(priv->base);
@@ -686,8 +688,6 @@ static int uniphier_spi_probe(struct platform_device *pdev)
goto out_host_put;
}
- init_completion(&priv->xfer_done);
-
clk_rate = clk_get_rate(priv->clk);
host->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER);
diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
index b5405d9974ba..36362bee7fff 100644
--- a/drivers/spi/spi-xilinx.c
+++ b/drivers/spi/spi-xilinx.c
@@ -370,11 +370,18 @@ static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi)
xspi->regs + XIPIF_V123B_RESETR_OFFSET);
/* Fill the Tx FIFO with as many words as possible */
- do {
+ while (1) {
xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
+ if (sr & XSPI_SR_TX_FULL_MASK)
+ break;
+
n_words++;
- } while (!(sr & XSPI_SR_TX_FULL_MASK));
+ }
+
+ /* Handle the NO FIFO case separately */
+ if (!n_words)
+ return 1;
return n_words;
}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index bb7e5e56f6dc..da2ef6b1f7c8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -4541,11 +4541,6 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
- /*
- * Clear the flag before adding the device so that fw_devlink
- * doesn't skip adding consumers to this device.
- */
- fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE);
spi = of_register_spi_device(ctlr, rd->dn);
put_device(&ctlr->dev);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 0803b296e9ac..96b56f87c422 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -3486,10 +3486,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd,
if (ret)
return ret;
- if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
- raw_black_frame) != 0)
- return -ENOMEM;
-
+ ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
+ raw_black_frame);
ia_css_frame_free(raw_black_frame);
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c
index d8a1d4a58db6..4a6363e98f7d 100644
--- a/drivers/staging/media/atomisp/pci/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/sh_css.c
@@ -5224,7 +5224,6 @@ static int load_primary_binaries(
struct ia_css_capture_settings *mycs;
unsigned int i;
bool need_extra_yuv_scaler = false;
- struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES];
IA_CSS_ENTER_PRIVATE("");
assert(pipe);
@@ -5394,15 +5393,16 @@ static int load_primary_binaries(
/* Primary */
for (i = 0; i < mycs->num_primary_stage; i++) {
+ struct ia_css_binary_descr prim_descr;
struct ia_css_frame_info *local_vf_info = NULL;
if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] &&
(i == mycs->num_primary_stage - 1))
local_vf_info = &vf_info;
- ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i],
+ ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr,
&prim_in_info, &prim_out_info,
local_vf_info, i);
- err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]);
+ err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]);
if (err) {
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c
index 76ad802d694e..91134bd8e72f 100644
--- a/drivers/staging/media/ipu3/ipu3-css-params.c
+++ b/drivers/staging/media/ipu3/ipu3-css-params.c
@@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe,
acc->stripe.bds_out_stripes[0].width =
ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f);
} else {
+ u32 offset;
+
/* Image processing is divided into two stripes */
acc->stripe.bds_out_stripes[0].width =
acc->stripe.bds_out_stripes[1].width =
@@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe,
acc->stripe.bds_out_stripes[1].width += f;
}
/* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */
- acc->stripe.bds_out_stripes[1].offset =
- acc->stripe.bds_out_stripes[0].width - 2 * f;
+ offset = acc->stripe.bds_out_stripes[0].width - 2 * f;
+ if (offset > 65535)
+ return -EINVAL;
+ acc->stripe.bds_out_stripes[1].offset = offset;
}
acc->stripe.effective_stripes[0].height =
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 8e248d4a0aec..a0f9f4a5b03a 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -478,7 +478,7 @@ static int cedrus_probe(struct platform_device *pdev)
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to register V4L2 device\n");
- return ret;
+ goto err_hw;
}
vfd = &dev->vfd;
@@ -539,6 +539,8 @@ static int cedrus_probe(struct platform_device *pdev)
v4l2_m2m_release(dev->m2m_dev);
err_v4l2:
v4l2_device_unregister(&dev->v4l2_dev);
+err_hw:
+ cedrus_hw_remove(dev);
return ret;
}
diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c
index 6254a5df2502..a2fd7681c261 100644
--- a/drivers/staging/most/video/video.c
+++ b/drivers/staging/most/video/video.c
@@ -415,6 +415,7 @@ static int comp_register_videodev(struct most_video_dev *mdev)
/* Fill the video capture device struct */
*mdev->vdev = comp_videodev_template;
+ mdev->vdev->release = video_device_release_empty;
mdev->vdev->v4l2_dev = &mdev->v4l2_dev;
mdev->vdev->lock = &mdev->lock;
snprintf(mdev->vdev->name, sizeof(mdev->vdev->name), "MOST: %s",
@@ -427,9 +428,13 @@ static int comp_register_videodev(struct most_video_dev *mdev)
v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n",
ret);
video_device_release(mdev->vdev);
+ return ret;
}
- return ret;
+ mdev->vdev->release = video_device_release;
+
+ return 0;
+
}
static void comp_unregister_videodev(struct most_video_dev *mdev)
diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
index 2823cacde130..0ed7266b033e 100644
--- a/drivers/staging/nvec/nvec.c
+++ b/drivers/staging/nvec/nvec.c
@@ -489,6 +489,8 @@ static void nvec_tx_completed(struct nvec_chip *nvec)
static void nvec_rx_completed(struct nvec_chip *nvec)
{
if (nvec->rx->pos != nvec_msg_size(nvec->rx)) {
+ unsigned char msg_type = nvec->rx->data[0];
+
dev_err(nvec->dev, "RX incomplete: Expected %u bytes, got %u\n",
(uint)nvec_msg_size(nvec->rx),
(uint)nvec->rx->pos);
@@ -497,7 +499,7 @@ static void nvec_rx_completed(struct nvec_chip *nvec)
nvec->state = 0;
/* Battery quirk - Often incomplete, and likes to crash */
- if (nvec->rx->data[0] == NVEC_BAT)
+ if (msg_type == NVEC_BAT)
complete(&nvec->ec_transfer);
return;
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index 7cd44e8c6966..05765a3bc200 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -455,10 +455,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwis
return _FAIL;
}
- if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) ||
- (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) {
+ if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) ||
+ (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) ||
+ (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN)))
return _FAIL;
- }
pos = wpa_ie;
@@ -518,7 +518,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwi
return _FAIL;
}
- if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
+ if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2)))
return _FAIL;
pos = rsn_ie;
@@ -584,20 +584,25 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len)
cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
while (cnt < in_len) {
+ if (cnt + 2 > in_len)
+ break;
+ if (cnt + 2 + in_ie[cnt + 1] > in_len)
+ break;
authmode = in_ie[cnt];
- /* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */
- if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp(&in_ie[cnt+6], wapi_oui1, 4) ||
- !memcmp(&in_ie[cnt+6], wapi_oui2, 4))) {
+ if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
+ in_ie[cnt + 1] >= 8 &&
+ (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
+ !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
if (wapi_ie)
- memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+ memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
if (wapi_len)
- *wapi_len = in_ie[cnt+1]+2;
+ *wapi_len = in_ie[cnt + 1] + 2;
- cnt += in_ie[cnt+1]+2; /* get next */
+ cnt += in_ie[cnt + 1] + 2; /* get next */
} else {
- cnt += in_ie[cnt+1]+2; /* get next */
+ cnt += in_ie[cnt + 1] + 2; /* get next */
}
}
@@ -619,11 +624,17 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie
cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
while (cnt < in_len) {
+ if (cnt + 2 > in_len)
+ break;
+ if (cnt + 2 + in_ie[cnt + 1] > in_len)
+ break;
authmode = in_ie[cnt];
- if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) {
+ if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
+ in_ie[cnt + 1] >= 4 &&
+ (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
if (wpa_ie)
- memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+ memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
*wpa_len = in_ie[cnt + 1] + 2;
cnt += in_ie[cnt + 1] + 2; /* get next */
@@ -632,10 +643,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie
if (rsn_ie)
memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
- *rsn_len = in_ie[cnt+1]+2;
- cnt += in_ie[cnt+1]+2; /* get next */
+ *rsn_len = in_ie[cnt + 1] + 2;
+ cnt += in_ie[cnt + 1] + 2; /* get next */
} else {
- cnt += in_ie[cnt+1]+2; /* get next */
+ cnt += in_ie[cnt + 1] + 2; /* get next */
}
}
}
@@ -667,20 +678,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen)
while (cnt < in_len) {
eid = in_ie[cnt];
- if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) {
+ if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
wpsie_ptr = &in_ie[cnt];
if (wps_ie)
- memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+ memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
if (wps_ielen)
- *wps_ielen = in_ie[cnt+1]+2;
+ *wps_ielen = in_ie[cnt + 1] + 2;
- cnt += in_ie[cnt+1]+2;
+ cnt += in_ie[cnt + 1] + 2;
break;
}
- cnt += in_ie[cnt+1]+2; /* goto next */
+ cnt += in_ie[cnt + 1] + 2; /* goto next */
}
return wpsie_ptr;
@@ -705,6 +716,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att
if (len_attr)
*len_attr = 0;
+ if (wps_ielen < 6)
+ return attr_ptr;
+
if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) ||
(memcmp(wps_ie + 2, wps_oui, 4))) {
return attr_ptr;
@@ -715,6 +729,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att
while (attr_ptr - wps_ie < wps_ielen) {
/* 4 = 2(Attribute ID) + 2(Length) */
+ if (attr_ptr + 4 > wps_ie + wps_ielen)
+ break;
u16 attr_id = get_unaligned_be16(attr_ptr);
u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
u16 attr_len = attr_data_len + 4;
@@ -758,12 +774,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8
if (attr_ptr && attr_len) {
if (buf_content)
- memcpy(buf_content, attr_ptr+4, attr_len-4);
+ memcpy(buf_content, attr_ptr + 4, attr_len - 4);
if (len_content)
- *len_content = attr_len-4;
+ *len_content = attr_len - 4;
- return attr_ptr+4;
+ return attr_ptr + 4;
}
return NULL;
@@ -1009,20 +1025,25 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork)
int group_cipher = 0, pairwise_cipher = 0, is8021x = 0;
int ret = _FAIL;
- pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+ pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12],
+ &wpa_ielen,
+ pnetwork->network.ie_length - 12);
if (pbuf && (wpa_ielen > 0)) {
- if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
+ if (rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher,
+ &pairwise_cipher, &is8021x) == _SUCCESS) {
pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
pnetwork->bcn_info.group_cipher = group_cipher;
pnetwork->bcn_info.is_8021x = is8021x;
ret = _SUCCESS;
}
} else {
- pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+ pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen,
+ pnetwork->network.ie_length - 12);
if (pbuf && (wpa_ielen > 0)) {
- if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
+ if (rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher,
+ &pairwise_cipher, &is8021x) == _SUCCESS) {
pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
pnetwork->bcn_info.group_cipher = group_cipher;
pnetwork->bcn_info.is_8021x = is8021x;
@@ -1091,21 +1112,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_GI, unsigned char *MCS_rate)
u16 max_rate = 0;
if (MCS_rate[0] & BIT(7))
- max_rate = (bw_40MHz) ? ((short_GI)?1500:1350):((short_GI)?722:650);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 1500 : 1350) : ((short_GI) ? 722 : 650);
else if (MCS_rate[0] & BIT(6))
- max_rate = (bw_40MHz) ? ((short_GI)?1350:1215):((short_GI)?650:585);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 1350 : 1215) : ((short_GI) ? 650 : 585);
else if (MCS_rate[0] & BIT(5))
- max_rate = (bw_40MHz) ? ((short_GI)?1200:1080):((short_GI)?578:520);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 1200 : 1080) : ((short_GI) ? 578 : 520);
else if (MCS_rate[0] & BIT(4))
- max_rate = (bw_40MHz) ? ((short_GI)?900:810):((short_GI)?433:390);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 900 : 810) : ((short_GI) ? 433 : 390);
else if (MCS_rate[0] & BIT(3))
- max_rate = (bw_40MHz) ? ((short_GI)?600:540):((short_GI)?289:260);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 600 : 540) : ((short_GI) ? 289 : 260);
else if (MCS_rate[0] & BIT(2))
- max_rate = (bw_40MHz) ? ((short_GI)?450:405):((short_GI)?217:195);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 450 : 405) : ((short_GI) ? 217 : 195);
else if (MCS_rate[0] & BIT(1))
- max_rate = (bw_40MHz) ? ((short_GI)?300:270):((short_GI)?144:130);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
else if (MCS_rate[0] & BIT(0))
- max_rate = (bw_40MHz) ? ((short_GI)?150:135):((short_GI)?72:65);
+ max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
return max_rate;
}
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index cde0a9b0498d..f4c0fd703c5a 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -695,6 +695,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
return _FAIL;
+ if (len < WLAN_HDR_A3_LEN)
+ return _FAIL;
+
sa = GetAddr2Ptr(pframe);
auth_mode = psecuritypriv->dot11AuthAlgrthm;
@@ -706,6 +709,9 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
prxattrib->hdrlen = WLAN_HDR_A3_LEN;
prxattrib->encrypt = _WEP40_;
+ if (len < WLAN_HDR_A3_LEN + 8)
+ return _FAIL;
+
iv = pframe+prxattrib->hdrlen;
prxattrib->key_index = ((iv[3]>>6)&0x3);
@@ -810,7 +816,7 @@ unsigned int OnAuth(struct adapter *padapter, union recv_frame *precv_frame)
p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_, WLAN_EID_CHALLENGE, (int *)&ie_len,
len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4);
- if (!p || ie_len <= 0) {
+ if (!p || ie_len != 128) {
status = WLAN_STATUS_CHALLENGE_FAIL;
goto auth_fail;
}
@@ -1419,7 +1425,11 @@ unsigned int OnAssocRsp(struct adapter *padapter, union recv_frame *precv_frame)
/* to handle HT, WMM, rate adaptive, update MAC reg */
/* for not to handle the synchronous IO in the tasklet */
for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) {
+ if (i + sizeof(*pIE) > pkt_len)
+ break;
pIE = (struct ndis_80211_var_ie *)(pframe + i);
+ if (i + sizeof(*pIE) + pIE->length > pkt_len)
+ break;
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:
@@ -2956,7 +2966,11 @@ void issue_assocreq(struct adapter *padapter)
/* vendor specific IE, such as WPA, WMM, WPS */
for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) {
+ if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length)
+ break;
pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i);
+ if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length)
+ break;
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:
@@ -5361,7 +5375,11 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
/* sizeof(struct ndis_802_11_fix_ie) */
for (i = _FIXED_IE_LENGTH_; i < pnetwork->ie_length;) {
+ if (i + sizeof(*pIE) > pnetwork->ie_length)
+ break;
pIE = (struct ndis_80211_var_ie *)(pnetwork->ies + i);
+ if (i + sizeof(*pIE) + pIE->length > pnetwork->ie_length)
+ break;
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */
diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c
index cfc0db7d99e7..2f44a24d136b 100644
--- a/drivers/staging/rtl8723bs/core/rtw_security.c
+++ b/drivers/staging/rtl8723bs/core/rtw_security.c
@@ -1538,7 +1538,7 @@ void rtw_sec_restore_wep_key(struct adapter *adapter)
struct security_priv *securitypriv = &(adapter->securitypriv);
signed int keyid;
- if ((_WEP40_ == securitypriv->dot11PrivacyAlgrthm) || (_WEP104_ == securitypriv->dot11PrivacyAlgrthm)) {
+ if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) {
for (keyid = 0; keyid < 4; keyid++) {
if (securitypriv->key_mask & BIT(keyid)) {
if (keyid == securitypriv->dot11PrivacyKeyIndex)
diff --git a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
index 7fac9ca3e9a0..aba340530378 100644
--- a/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
+++ b/drivers/staging/rtl8723bs/core/rtw_wlan_util.c
@@ -979,7 +979,8 @@ void HT_caps_handler(struct adapter *padapter, struct ndis_80211_var_ie *pIE)
pmlmeinfo->HT_caps_enable = 1;
- for (i = 0; i < (pIE->length); i++) {
+ for (i = 0; i < umin(pIE->length,
+ sizeof(pmlmeinfo->HT_caps.u.HT_cap)); i++) {
if (i != 2) {
/* Commented by Albert 2010/07/12 */
/* Got the endian issue here. */
@@ -1332,7 +1333,11 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru
len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN);
for (i = 0; i < len;) {
+ if (i + sizeof(*pIE) > len)
+ break;
pIE = (struct ndis_80211_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i);
+ if (i + sizeof(*pIE) + pIE->length > len)
+ break;
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:
@@ -1357,7 +1362,7 @@ void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, stru
break;
}
- i += (pIE->length + 2);
+ i += sizeof(*pIE) + pIE->length;
}
}
@@ -1373,15 +1378,23 @@ unsigned int is_ap_in_tkip(struct adapter *padapter)
for (i = sizeof(struct ndis_802_11_fix_ie); i < pmlmeinfo->network.ie_length;) {
pIE = (struct ndis_80211_var_ie *)(pmlmeinfo->network.ies + i);
+ if (i + sizeof(*pIE) > pmlmeinfo->network.ie_length)
+ break;
+ if (i + sizeof(*pIE) + pIE->length > pmlmeinfo->network.ie_length)
+ break;
+
switch (pIE->element_id) {
case WLAN_EID_VENDOR_SPECIFIC:
- if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)))
+ if (pIE->length >= 16 &&
+ !memcmp(pIE->data, RTW_WPA_OUI, 4) &&
+ !memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4))
return true;
break;
case WLAN_EID_RSN:
- if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4))
+ if (pIE->length >= 12 &&
+ !memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4))
return true;
break;
@@ -1389,7 +1402,7 @@ unsigned int is_ap_in_tkip(struct adapter *padapter)
break;
}
- i += (pIE->length + 2);
+ i += sizeof(*pIE) + pIE->length;
}
return false;
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index f23aeb58d041..62a55d0ebd69 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -1457,6 +1457,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel
pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen);
if (pwpa && wpa_ielen > 0) {
+ if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
+ ret = -EINVAL;
+ goto exit;
+ }
if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
@@ -1466,6 +1470,10 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel
pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen);
if (pwpa2 && wpa2_ielen > 0) {
+ if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) {
+ ret = -EINVAL;
+ goto exit;
+ }
if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
diff --git a/drivers/staging/vme_user/vme_fake.c b/drivers/staging/vme_user/vme_fake.c
index c70aec959f4d..e19582895ee9 100644
--- a/drivers/staging/vme_user/vme_fake.c
+++ b/drivers/staging/vme_user/vme_fake.c
@@ -1244,6 +1244,7 @@ static void __exit fake_exit(void)
{
struct list_head *pos = NULL;
struct list_head *tmplist;
+ struct vme_lm_resource *lm;
struct vme_master_resource *master_image;
struct vme_slave_resource *slave_image;
int i;
@@ -1273,6 +1274,13 @@ static void __exit fake_exit(void)
vme_unregister_bridge(fake_bridge);
fake_crcsr_exit(fake_bridge);
+ /* resources are stored in link list */
+ list_for_each_safe(pos, tmplist, &fake_bridge->lm_resources) {
+ lm = list_entry(pos, struct vme_lm_resource, list);
+ list_del(pos);
+ kfree(lm);
+ }
+
/* resources are stored in link list */
list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
diff --git a/drivers/staging/vme_user/vme_tsi148.c b/drivers/staging/vme_user/vme_tsi148.c
index 4566e391d913..7b2a6698adf6 100644
--- a/drivers/staging/vme_user/vme_tsi148.c
+++ b/drivers/staging/vme_user/vme_tsi148.c
@@ -2538,6 +2538,7 @@ static void tsi148_remove(struct pci_dev *pdev)
{
struct list_head *pos = NULL;
struct list_head *tmplist;
+ struct vme_lm_resource *lm;
struct vme_master_resource *master_image;
struct vme_slave_resource *slave_image;
struct vme_dma_resource *dma_ctrlr;
@@ -2594,6 +2595,13 @@ static void tsi148_remove(struct pci_dev *pdev)
tsi148_crcsr_exit(tsi148_bridge, pdev);
+ /* resources are stored in link list */
+ list_for_each_safe(pos, tmplist, &tsi148_bridge->lm_resources) {
+ lm = list_entry(pos, struct vme_lm_resource, list);
+ list_del(pos);
+ kfree(lm);
+ }
+
/* resources are stored in link list */
list_for_each_safe(pos, tmplist, &tsi148_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c
index b9367b575d00..5147ef048a6a 100644
--- a/drivers/staging/vme_user/vme_user.c
+++ b/drivers/staging/vme_user/vme_user.c
@@ -153,6 +153,17 @@ static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
{
void *image_ptr;
+ /*
+ * The slave window (image_size) can exceed the fixed kern_buf
+ * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf.
+ * *ppos is >= 0 here (checked by the caller), so the
+ * subtraction below cannot wrap.
+ */
+ if (*ppos >= image[minor].size_buf)
+ return 0;
+ if (count > image[minor].size_buf - *ppos)
+ count = image[minor].size_buf - *ppos;
+
image_ptr = image[minor].kern_buf + *ppos;
if (copy_to_user(buf, image_ptr, (unsigned long)count))
return -EFAULT;
@@ -165,6 +176,17 @@ static ssize_t buffer_from_user(unsigned int minor, const char __user *buf,
{
void *image_ptr;
+ /*
+ * The slave window (image_size) can exceed the fixed kern_buf
+ * (size_buf == PCI_BUF_SIZE), so bound the copy to kern_buf.
+ * *ppos is >= 0 here (checked by the caller), so the
+ * subtraction below cannot wrap.
+ */
+ if (*ppos >= image[minor].size_buf)
+ return 0;
+ if (count > image[minor].size_buf - *ppos)
+ count = image[minor].size_buf - *ppos;
+
image_ptr = image[minor].kern_buf + *ppos;
if (copy_from_user(image_ptr, buf, (unsigned long)count))
return -EFAULT;
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c
index 02a4c9aff98d..41e4def46ede 100644
--- a/drivers/target/iscsi/iscsi_target_auth.c
+++ b/drivers/target/iscsi/iscsi_target_auth.c
@@ -437,9 +437,11 @@ static int chap_server_compute_hash(
}
if (type == HEX)
- ret = kstrtoul(&identifier[2], 0, &id);
+ ret = kstrtoul(identifier, 16, &id);
+ else if (type == DECIMAL)
+ ret = kstrtoul(identifier, 10, &id);
else
- ret = kstrtoul(identifier, 0, &id);
+ ret = -EINVAL;
if (ret < 0) {
pr_err("kstrtoul() failed for CHAP identifier: %d\n", ret);
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index 72d374c94ce2..b6a97a842a7d 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -268,69 +268,6 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
}
-static bool tcm_loop_flush_work_iter(struct request *rq, void *data)
-{
- struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq);
- struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc);
- struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd;
-
- flush_work(&se_cmd->work);
- return true;
-}
-
-static int tcm_loop_target_reset(struct scsi_cmnd *sc)
-{
- struct tcm_loop_hba *tl_hba;
- struct tcm_loop_tpg *tl_tpg;
- struct Scsi_Host *sh = sc->device->host;
- int ret;
-
- /*
- * Locate the tcm_loop_hba_t pointer
- */
- tl_hba = *(struct tcm_loop_hba **)shost_priv(sh);
- if (!tl_hba) {
- pr_err("Unable to perform device reset without active I_T Nexus\n");
- return FAILED;
- }
- /*
- * Locate the tl_tpg pointer from TargetID in sc->device->id
- */
- tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
- if (!tl_tpg)
- return FAILED;
-
- /*
- * Issue a LUN_RESET to drain all commands that the target core
- * knows about. This handles commands not yet marked CMD_T_COMPLETE.
- */
- ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET);
- if (ret != TMR_FUNCTION_COMPLETE)
- return FAILED;
-
- /*
- * Flush any deferred target core completion work that may still be
- * queued. Commands that already had CMD_T_COMPLETE set before the TMR
- * are skipped by the TMR drain, but their async completion work
- * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done)
- * may still be pending in target_completion_wq.
- *
- * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery
- * commands (e.g. TUR) immediately after this handler returns SUCCESS —
- * if deferred work is still pending, the memset in queuecommand would
- * zero the se_cmd while the work accesses it, leaking the LUN
- * percpu_ref and hanging configfs unlink forever.
- *
- * Use blk_mq_tagset_busy_iter() to find all started requests and
- * flush_work() on each — the same pattern used by mpi3mr, scsi_debug,
- * and other SCSI drivers to drain outstanding commands during reset.
- */
- blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL);
-
- tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
- return SUCCESS;
-}
-
static const struct scsi_host_template tcm_loop_driver_template = {
.show_info = tcm_loop_show_info,
.proc_name = "tcm_loopback",
@@ -339,7 +276,6 @@ static const struct scsi_host_template tcm_loop_driver_template = {
.change_queue_depth = scsi_change_queue_depth,
.eh_abort_handler = tcm_loop_abort_task,
.eh_device_reset_handler = tcm_loop_device_reset,
- .eh_target_reset_handler = tcm_loop_target_reset,
.this_id = -1,
.sg_tablesize = 256,
.max_sectors = 0xFFFF,
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c
index d3ab251ba049..8deadabca888 100644
--- a/drivers/target/target_core_fabric_lib.c
+++ b/drivers/target/target_core_fabric_lib.c
@@ -289,13 +289,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str)
static bool iscsi_parse_pr_out_transport_id(
struct se_portal_group *se_tpg,
char *buf,
+ u32 buf_len,
u32 *out_tid_len,
char **port_nexus_ptr,
char *i_str)
{
char *p;
+ u32 tid_len;
int i;
- u8 format_code = (buf[0] & 0xc0);
+ u8 format_code;
+
+ /*
+ * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL
+ * LENGTH) must be present before any of it can be parsed.
+ */
+ if (buf_len < 4)
+ return false;
+
+ format_code = buf[0] & 0xc0;
/*
* Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:
*
@@ -315,15 +326,17 @@ static bool iscsi_parse_pr_out_transport_id(
return false;
}
/*
- * If the caller wants the TransportID Length, we set that value for the
- * entire iSCSI Tarnsport ID now.
+ * Reconstruct the self-described TransportID length from the ADDITIONAL
+ * LENGTH field plus the 4-byte header. Reject it if it is below the
+ * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least
+ * 20) or if it runs past the bytes actually received, so that every
+ * access below stays inside the TransportID.
*/
- if (out_tid_len) {
- /* The shift works thanks to integer promotion rules */
- *out_tid_len = get_unaligned_be16(&buf[2]);
- /* Add four bytes for iSCSI Transport ID header */
- *out_tid_len += 4;
- }
+ tid_len = get_unaligned_be16(&buf[2]) + 4;
+ if (tid_len < 24 || tid_len > buf_len)
+ return false;
+ if (out_tid_len)
+ *out_tid_len = tid_len;
/*
* Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator
@@ -331,16 +344,32 @@ static bool iscsi_parse_pr_out_transport_id(
* format.
*/
if (format_code == 0x40) {
- p = strstr(&buf[4], ",i,0x");
+ p = strnstr(&buf[4], ",i,0x", tid_len - 4);
if (!p) {
- pr_err("Unable to locate \",i,0x\" separator"
- " for Initiator port identifier: %s\n",
- &buf[4]);
+ pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n");
+ return false;
+ }
+ /*
+ * The iSCSI name runs from &buf[4] up to the separator; reject it
+ * if it cannot fit in i_str[TRANSPORT_IQN_LEN].
+ */
+ if (p - &buf[4] >= TRANSPORT_IQN_LEN) {
+ pr_err("iSCSI Initiator port name too long in TransportID\n");
return false;
}
*p = '\0'; /* Terminate iSCSI Name */
p += 5; /* Skip over ",i,0x" separator */
+ /*
+ * The ISID must follow the separator. A ",i,0x" sitting at the
+ * very end of the TransportID leaves no ISID and would point the
+ * port nexus at buf + tid_len, i.e. past the descriptor, which
+ * the registration code then reads as the ISID string.
+ */
+ if (p >= buf + tid_len) {
+ pr_err("Missing ISID in iSCSI Initiator port TransportID\n");
+ return false;
+ }
*port_nexus_ptr = p;
/*
* Go ahead and do the lower case conversion of the received
@@ -348,7 +377,7 @@ static bool iscsi_parse_pr_out_transport_id(
* for comparison against the running iSCSI session's ISID from
* iscsi_target.c:lio_sess_get_initiator_sid()
*/
- for (i = 0; i < 12; i++) {
+ for (i = 0; i < 12 && p < buf + tid_len; i++) {
/*
* The first ISCSI INITIATOR SESSION ID field byte
* containing an ASCII null character terminates the
@@ -366,10 +395,22 @@ static bool iscsi_parse_pr_out_transport_id(
*p = tolower(*p);
p++;
}
- } else
+ strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
+ } else {
*port_nexus_ptr = NULL;
-
- strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN);
+ /*
+ * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The
+ * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN].
+ * (For 01b the same tid_len bound would be over-restrictive: the
+ * descriptor also carries the separator and ISID, so a legal
+ * <=223-byte name gives tid_len up to 244.)
+ */
+ if (tid_len - 4 >= TRANSPORT_IQN_LEN) {
+ pr_err("iSCSI Initiator port name too long in TransportID\n");
+ return false;
+ }
+ strscpy(i_str, &buf[4], tid_len - 4);
+ }
return true;
}
@@ -419,8 +460,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
}
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
- char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str)
+ char *buf, u32 buf_len, u32 *out_tid_len,
+ char **port_nexus_ptr, char *i_str)
{
+ /*
+ * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI
+ * format is variable and bounds itself against buf_len below.
+ */
+ if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24)
+ return false;
+
switch (tpg->proto_id) {
case SCSI_PROTOCOL_SAS:
/*
@@ -439,8 +488,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
sbp_parse_pr_out_transport_id(buf, i_str);
break;
case SCSI_PROTOCOL_ISCSI:
- return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len,
- port_nexus_ptr, i_str);
+ return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len,
+ out_tid_len, port_nexus_ptr, i_str);
default:
pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id);
return false;
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 20aab1f50565..32d0da505baf 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg, int *format_code,
unsigned char *buf);
bool target_parse_pr_out_transport_id(struct se_portal_group *tpg,
- char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str);
+ char *buf, u32 buf_len, u32 *out_tid_len,
+ char **port_nexus_ptr, char *i_str);
/* target_core_hba.c */
struct se_hba *core_alloc_hba(const char *, u32, u32);
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 624d2f68bf38..1e2dea10f5bb 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -1572,7 +1572,7 @@ core_scsi3_decode_spec_i_port(
iport_ptr = NULL;
tid_found = target_parse_pr_out_transport_id(tmp_tpg,
- ptr, &tid_len, &iport_ptr, i_str);
+ ptr, tpdl, &tid_len, &iport_ptr, i_str);
if (!tid_found)
continue;
/*
@@ -3280,7 +3280,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
tid_found = target_parse_pr_out_transport_id(dest_se_tpg,
- &buf[24], &tmp_tid_len, &iport_ptr, initiator_str);
+ &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str);
if (!tid_found) {
pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate"
" initiator_str from Transport ID\n");
@@ -3288,9 +3288,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
goto out;
}
- transport_kunmap_data_sg(cmd);
- buf = NULL;
-
pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s"
" %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ?
"port" : "device", initiator_str, (iport_ptr != NULL) ?
@@ -3527,6 +3524,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key,
core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
core_scsi3_put_pr_reg(dest_pr_reg);
+ /*
+ * iport_ptr aliases the PR-OUT parameter list mapped above, so the
+ * buffer is unmapped only here on success (and at out: on error).
+ */
+ transport_kunmap_data_sg(cmd);
return 0;
out:
if (buf)
diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c
index e69868e868eb..dfdc8c1b0eb5 100644
--- a/drivers/thermal/intel/therm_throt.c
+++ b/drivers/thermal/intel/therm_throt.c
@@ -528,8 +528,13 @@ static int thermal_throttle_online(unsigned int cpu)
{
struct thermal_state *state = &per_cpu(thermal_state, cpu);
struct device *dev = get_cpu_device(cpu);
+ int err;
u32 l;
+ err = thermal_throttle_add_dev(dev, cpu);
+ if (err)
+ return err;
+
state->package_throttle.level = PACKAGE_LEVEL;
state->core_throttle.level = CORE_LEVEL;
@@ -547,7 +552,7 @@ static int thermal_throttle_online(unsigned int cpu)
l = apic_read(APIC_LVTTHMR);
apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
- return thermal_throttle_add_dev(dev, cpu);
+ return err;
}
static int thermal_throttle_offline(unsigned int cpu)
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index c3ae44659b81..0e0c4061792c 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -40,6 +40,7 @@ struct thermal_hwmon_temp {
struct thermal_zone_device *tz;
struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
+ bool temp_crit_present;
};
static LIST_HEAD(thermal_hwmon_list);
@@ -197,6 +198,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
&temp->temp_crit.attr);
if (result)
goto unregister_input;
+
+ temp->temp_crit_present = true;
}
mutex_lock(&thermal_hwmon_list_lock);
@@ -241,7 +244,7 @@ void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
}
device_remove_file(hwmon->device, &temp->temp_input.attr);
- if (thermal_zone_crit_temp_valid(tz))
+ if (temp->temp_crit_present)
device_remove_file(hwmon->device, &temp->temp_crit.attr);
mutex_lock(&thermal_hwmon_list_lock);
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index 2cc78a4bf7a1..f88809ff370b 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -10,9 +10,9 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/rational.h>
+#include <linux/util_macros.h>
#include <linux/dma/hsu.h>
-#include <linux/8250_pci.h>
#include "8250.h"
@@ -32,9 +32,9 @@
struct mid8250;
struct mid8250_board {
- unsigned int flags;
unsigned long freq;
unsigned int base_baud;
+ unsigned int bar;
int (*setup)(struct mid8250 *, struct uart_port *p);
void (*exit)(struct mid8250 *);
};
@@ -169,7 +169,6 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p)
{
struct hsu_dma_chip *chip = &mid->dma_chip;
struct pci_dev *pdev = to_pci_dev(p->dev);
- unsigned int bar = FL_GET_BASE(mid->board->flags);
int ret;
pci_set_master(pdev);
@@ -183,7 +182,7 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p)
chip->dev = &pdev->dev;
chip->irq = pci_irq_vector(pdev, 0);
chip->regs = p->membase;
- chip->length = pci_resource_len(pdev, bar);
+ chip->length = pci_resource_len(pdev, mid->board->bar);
chip->offset = DNV_DMA_CHAN_OFFSET;
/* Falling back to PIO mode if DMA probing fails */
@@ -291,7 +290,6 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uart_8250_port uart;
struct mid8250 *mid;
- unsigned int bar;
int ret;
ret = pcim_enable_device(pdev);
@@ -303,7 +301,6 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENOMEM;
mid->board = (struct mid8250_board *)id->driver_data;
- bar = FL_GET_BASE(mid->board->flags);
memset(&uart, 0, sizeof(struct uart_8250_port));
@@ -316,8 +313,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
uart.port.set_termios = mid8250_set_termios;
- uart.port.mapbase = pci_resource_start(pdev, bar);
- uart.port.membase = pcim_iomap(pdev, bar, 0);
+ uart.port.mapbase = pci_resource_start(pdev, mid->board->bar);
+ uart.port.membase = pcim_iomap(pdev, mid->board->bar, 0);
if (!uart.port.membase)
return -ENOMEM;
@@ -353,27 +350,35 @@ static void mid8250_remove(struct pci_dev *pdev)
}
static const struct mid8250_board pnw_board = {
- .flags = FL_BASE0,
.freq = 50000000,
.base_baud = 115200,
+ .bar = 0,
.setup = pnw_setup,
.exit = pnw_exit,
};
static const struct mid8250_board tng_board = {
- .flags = FL_BASE0,
.freq = 38400000,
.base_baud = 1843200,
+ .bar = 0,
.setup = tng_setup,
.exit = tng_exit,
};
static const struct mid8250_board dnv_board = {
- .flags = FL_BASE1,
.freq = 133333333,
.base_baud = 115200,
- .setup = dnv_setup,
- .exit = dnv_exit,
+ .bar = 1,
+ /*
+ * Errata:
+ * HSUART May Stop Functioning when DMA is Active.
+ *
+ * - Denverton document #572409, rev 3.4, DNV60
+ * - Ice Lake Xeon D document #714070, ICXD65
+ * - Snowridge document #731931, SNR44
+ */
+ .setup = PTR_IF(false, dnv_setup),
+ .exit = PTR_IF(false, dnv_exit),
};
static const struct pci_device_id pci_ids[] = {
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 776373423b2a..8796d0525f35 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -953,11 +953,12 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
dev_err(p->port.dev, "teardown incomplete\n");
}
}
+
+ dma->rx_running = 0;
if (!count)
goto out;
ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
- dma->rx_running = 0;
p->port.icount.rx += ret;
p->port.icount.buf_overrun += count - ret;
out:
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 76b6429fb9e9..9018a1bd51fa 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1221,7 +1221,8 @@ static int msm_startup(struct uart_port *port)
data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
msm_write(port, data, MSM_UART_MR1);
- if (msm_port->is_uartdm) {
+ /* Disable DMA for console to prevent PIO/DMA collisions */
+ if (msm_port->is_uartdm && !uart_console(port)) {
msm_request_tx_dma(msm_port, msm_port->uart.mapbase);
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index f6aada5150f9..142abff30122 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -5407,7 +5407,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp,
default:
dev_err(hba->dev,
"Unexpected request response code = %x\n",
- result);
+ ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr));
result = DID_ERROR << 16;
break;
}
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 5812f7ea7f90..b41f352769f0 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -599,7 +599,9 @@ static int uea_send_modem_cmd(struct usb_device *usb,
static void uea_upload_pre_firmware(const struct firmware *fw_entry,
void *context)
{
- struct usb_device *usb = context;
+ struct usb_interface *intf = context;
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct completion *fw_done = usb_get_intfdata(intf);
const u8 *pfw;
u8 value;
u32 crc = 0;
@@ -670,15 +672,17 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry,
err:
release_firmware(fw_entry);
uea_leaves(usb);
+ complete(fw_done);
}
/*
* uea_load_firmware - Load usb firmware for pre-firmware devices.
*/
-static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
+static int uea_load_firmware(struct usb_interface *intf, unsigned int ver)
{
int ret;
char *fw_name = EAGLE_FIRMWARE;
+ struct usb_device *usb = interface_to_usbdev(intf);
uea_enters(usb);
uea_info(usb, "pre-firmware device, uploading firmware\n");
@@ -702,7 +706,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
}
ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
- GFP_KERNEL, usb,
+ GFP_KERNEL, intf,
uea_upload_pre_firmware);
if (ret)
uea_err(usb, "firmware %s is not available\n", fw_name);
@@ -2598,8 +2602,23 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_reset_device(usb);
- if (UEA_IS_PREFIRM(id))
- return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
+ if (UEA_IS_PREFIRM(id)) {
+ struct completion *fw_done;
+
+ /* Wait for the firmware load to be done, in .disconnect() */
+ fw_done = kzalloc(sizeof(*fw_done), GFP_KERNEL);
+ if (!fw_done)
+ return -ENOMEM;
+
+ init_completion(fw_done);
+ usb_set_intfdata(intf, fw_done);
+
+ ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id));
+ if (ret)
+ kfree(fw_done);
+
+ return ret;
+ }
ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
if (ret == 0) {
@@ -2630,6 +2649,13 @@ static void uea_disconnect(struct usb_interface *intf)
usbatm_usb_disconnect(intf);
mutex_unlock(&uea_mutex);
uea_info(usb, "ADSL device removed\n");
+ } else if (usb->config->desc.bNumInterfaces == 1) {
+ struct completion *fw_done = usb_get_intfdata(intf);
+
+ uea_dbg(usb, "pre-firmware device, waiting firmware upload\n");
+ wait_for_completion(fw_done);
+ uea_dbg(usb, "pre-firmware device, finished waiting\n");
+ kfree(fw_done);
}
uea_leaves(usb);
diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c
index 97866bfb2da9..3f35c8af271e 100644
--- a/drivers/usb/cdns3/cdnsp-mem.c
+++ b/drivers/usb/cdns3/cdnsp-mem.c
@@ -632,6 +632,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev,
}
}
+ cdnsp_free_stream_ctx(pdev, pep);
+
cleanup_stream_rings:
kfree(pep->stream_info.stream_rings);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index d64751c42c2b..cb7f0b7a6f3a 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1810,6 +1810,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
.driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
},
+ { USB_DEVICE(0x1965, 0x0017), /* Uniden BC125AT */
+ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+ },
{ USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index b1957dc687a8..c6053771b403 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -281,28 +281,24 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type;
+
+ device_initialize(&ulpi->dev);
+
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
ret = ulpi_of_register(ulpi);
- if (ret) {
- kfree(ulpi);
+ if (ret)
return ret;
- }
ret = ulpi_read_id(ulpi);
- if (ret) {
- of_node_put(ulpi->dev.of_node);
- kfree(ulpi);
+ if (ret)
return ret;
- }
- ret = device_register(&ulpi->dev);
- if (ret) {
- put_device(&ulpi->dev);
+ ret = device_add(&ulpi->dev);
+ if (ret)
return ret;
- }
root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
@@ -334,9 +330,10 @@ struct ulpi *ulpi_register_interface(struct device *dev,
ulpi->ops = ops;
ret = ulpi_register(dev, ulpi);
- if (ret)
+ if (ret) {
+ put_device(&ulpi->dev);
return ERR_PTR(ret);
-
+ }
return ulpi;
}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index a273cdcef0c5..6ed380599405 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -297,6 +297,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CarrolTouch 4500U */
{ USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Samsung T5 EVO Portable SSD */
+ { USB_DEVICE(0x04e8, 0x6200), .driver_info = USB_QUIRK_NO_LPM },
+
/* Samsung Android phone modem - ID conflict with SPH-I500 */
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -574,6 +577,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* VLI disk */
{ USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
+ /* VIA Labs, Inc. USB2.0 Hub */
+ { USB_DEVICE(0x2109, 0x2817), .driver_info = USB_QUIRK_NO_LPM },
+
/* Raydium Touchscreen */
{ USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index 6ea1a876203d..3906b940e2ad 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -907,35 +907,39 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
ret = priv->drvdata->usb_init(priv);
if (ret)
- return ret;
+ goto err_rearm;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_init(priv->phys[i]);
if (ret)
- return ret;
+ goto err_rearm;
}
/* Set PHY Power */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_power_on(priv->phys[i]);
if (ret)
- return ret;
+ goto err_rearm;
}
if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
ret = regulator_enable(priv->vbus);
if (ret)
- return ret;
+ goto err_rearm;
}
if (priv->drvdata->usb_post_init) {
ret = priv->drvdata->usb_post_init(priv);
if (ret)
- return ret;
+ goto err_rearm;
}
return 0;
+
+err_rearm:
+ reset_control_rearm(priv->reset);
+ return ret;
}
static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 7e6661781aff..1d40e5136ef0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -3930,15 +3930,48 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
}
}
+static bool dwc3_prepare_disconnect_gadget(struct dwc3 *dwc,
+ struct usb_gadget_driver **driver,
+ struct usb_gadget **gadget)
+{
+ if (!dwc->async_callbacks || !dwc->gadget_driver ||
+ !dwc->gadget_driver->disconnect)
+ return false;
+
+ *driver = dwc->gadget_driver;
+ *gadget = dwc->gadget;
+
+ return true;
+}
+
static void dwc3_disconnect_gadget(struct dwc3 *dwc)
{
- if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
+ struct usb_gadget_driver *driver;
+ struct usb_gadget *gadget;
+
+ if (dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(dwc->gadget);
+ driver->disconnect(gadget);
spin_lock(&dwc->lock);
}
}
+static void dwc3_disconnect_gadget_sleepable(struct dwc3 *dwc)
+{
+ struct usb_gadget_driver *driver;
+ struct usb_gadget *gadget;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (!dwc3_prepare_disconnect_gadget(dwc, &driver, &gadget)) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ driver->disconnect(gadget);
+}
+
static void dwc3_suspend_gadget(struct dwc3 *dwc)
{
if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
@@ -4818,7 +4851,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
- unsigned long flags;
int ret;
ret = dwc3_gadget_soft_disconnect(dwc);
@@ -4832,10 +4864,7 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
return -EAGAIN;
}
- spin_lock_irqsave(&dwc->lock, flags);
- if (dwc->gadget_driver)
- dwc3_disconnect_gadget(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_disconnect_gadget_sleepable(dwc);
return 0;
}
diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
index 7bf810a0c98a..bee7ef76598e 100644
--- a/drivers/usb/fotg210/fotg210-hcd.c
+++ b/drivers/usb/fotg210/fotg210-hcd.c
@@ -4271,8 +4271,6 @@ static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb,
return 0;
fail:
- iso_sched_free(stream, sched);
- urb->hcpriv = NULL;
return status;
}
@@ -4566,6 +4564,10 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb,
else
usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb);
done_not_linked:
+ if (status < 0) {
+ iso_sched_free(stream, urb->hcpriv);
+ urb->hcpriv = NULL;
+ }
spin_unlock_irqrestore(&fotg210->lock, flags);
done:
return status;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 8ec5cf4d3468..1caea2292b69 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1887,9 +1887,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (cdev->config)
config = cdev->config;
else
- config = list_first_entry(
+ config = list_first_entry_or_null(
&cdev->configs,
- struct usb_configuration, list);
+ struct usb_configuration,
+ list);
if (!config)
goto done;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 17724b12b880..f4658e898673 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -255,6 +255,7 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
static void ffs_release_dev(struct ffs_dev *ffs_dev);
static int ffs_ready(struct ffs_data *ffs);
static void ffs_closed(struct ffs_data *ffs);
+static void ffs_reset_work(struct work_struct *work);
/* Misc helper functions ****************************************************/
@@ -1268,7 +1269,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)
{
struct ffs_epfile *epfile = inode->i_private;
- __ffs_epfile_read_buffer_free(epfile);
ffs_data_closed(epfile->ffs);
return 0;
@@ -1734,6 +1734,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
init_waitqueue_head(&ffs->ev.waitq);
init_waitqueue_head(&ffs->wait);
init_completion(&ffs->ep0req_completion);
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
/* XXX REVISIT need to update it in some places, or do we? */
ffs->ev.can_stall = 1;
@@ -1891,6 +1892,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex));
+ __ffs_epfile_read_buffer_free(epfile);
if (epfile->dentry) {
d_delete(epfile->dentry);
dput(epfile->dentry);
@@ -3261,7 +3263,6 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
- INIT_WORK(&ffs->reset_work, ffs_reset_work);
schedule_work(&ffs->reset_work);
return -ENODEV;
}
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 44e20c6c36d3..23bdeee737a3 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -363,12 +363,11 @@ printer_open(struct inode *inode, struct file *fd)
ret = 0;
/* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
+ kref_get(&dev->kref);
}
spin_unlock_irqrestore(&dev->lock, flags);
- kref_get(&dev->kref);
-
return ret;
}
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 12c5d9cf450c..5dc745b31bb8 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -591,6 +591,7 @@ static int rndis_init_response(struct rndis_params *params,
static int rndis_query_response(struct rndis_params *params,
rndis_query_msg_type *buf)
{
+ u32 BufLength, BufOffset;
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
@@ -598,6 +599,13 @@ static int rndis_query_response(struct rndis_params *params,
if (!params->dev)
return -ENOTSUPP;
+ BufLength = le32_to_cpu(buf->InformationBufferLength);
+ BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+ if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
+ (BufOffset > RNDIS_MAX_TOTAL_SIZE) ||
+ (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
+ return -EINVAL;
+
/*
* we need more memory:
* gen_ndis_query_resp expects enough space for
@@ -614,10 +622,8 @@ static int rndis_query_response(struct rndis_params *params,
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
- le32_to_cpu(buf->InformationBufferOffset)
- + 8 + (u8 *)buf,
- le32_to_cpu(buf->InformationBufferLength),
- r)) {
+ BufOffset + 8 + (u8 *)buf,
+ BufLength, r)) {
/* OID not supported */
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
resp->MessageLength = cpu_to_le32(sizeof *resp);
@@ -1074,6 +1080,12 @@ int rndis_rm_hdr(struct gether *port,
/* tmp points to a struct rndis_packet_msg_type */
__le32 *tmp = (void *)skb->data;
+ /* Need at least MessageType, MessageLength, DataOffset, DataLength */
+ if (skb->len < 16) {
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
/* MessageType, MessageLength */
if (cpu_to_le32(RNDIS_MSG_PACKET)
!= get_unaligned(tmp++)) {
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 0cad5cf19543..a13fbef19c0a 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -31,8 +31,9 @@ static const struct bus_type gadget_bus_type;
/**
* struct usb_udc - describes one usb device controller
* @driver: the gadget driver pointer. For use by the class code
- * @dev: the child device to the actual controller
* @gadget: the gadget. For use by the class code
+ * @gadget_release: the gadget's release routine
+ * @dev: the child device to the actual controller
* @list: for use by the udc class driver
* @vbus: for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
@@ -53,6 +54,7 @@ static const struct bus_type gadget_bus_type;
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
+ void (*gadget_release)(struct device *dev);
struct device dev;
struct list_head list;
bool vbus;
@@ -1351,6 +1353,17 @@ static void usb_udc_nop_release(struct device *dev)
dev_vdbg(dev, "%s\n", __func__);
}
+static void usb_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_udc *udc = gadget->udc;
+ /* Cache the gadget's release routine to prevent UAF */
+ void (*release)(struct device *dev) = udc->gadget_release;
+
+ put_device(&udc->dev);
+ release(dev);
+}
+
/**
* usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
@@ -1407,6 +1420,14 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_init(&udc->connect_lock);
udc->started = false;
+ /*
+ * Align decoupled lifecycles: take a UDC reference to ensure it
+ * remains allocated until the gadget is released, requiring an
+ * override of the gadget's release routine to drop it.
+ */
+ udc->gadget_release = gadget->dev.release;
+ gadget->dev.release = usb_gadget_release;
+ get_device(&udc->dev);
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
@@ -1451,6 +1472,12 @@ int usb_add_gadget(struct usb_gadget *gadget)
mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
+ /*
+ * Revert the override and drop the UDC reference to prevent
+ * leaking the UDC if the gadget was statically allocated.
+ */
+ gadget->dev.release = udc->gadget_release;
+ put_device(&udc->dev);
err_put_udc:
put_device(&udc->dev);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7e834587e7de..e6742838f9a6 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1624,6 +1624,7 @@ iso_stream_schedule(
status = 1; /* and give it back immediately */
iso_sched_free(stream, sched);
sched = NULL;
+ urb->hcpriv = NULL;
}
}
urb->error_count = skip / period;
@@ -1654,8 +1655,6 @@ iso_stream_schedule(
return status;
fail:
- iso_sched_free(stream, sched);
- urb->hcpriv = NULL;
return status;
}
@@ -1967,6 +1966,10 @@ static int itd_submit(struct ehci_hcd *ehci, struct urb *urb,
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
}
done_not_linked:
+ if (status < 0) {
+ iso_sched_free(stream, urb->hcpriv);
+ urb->hcpriv = NULL;
+ }
spin_unlock_irqrestore(&ehci->lock, flags);
done:
return status;
@@ -2344,6 +2347,10 @@ static int sitd_submit(struct ehci_hcd *ehci, struct urb *urb,
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
}
done_not_linked:
+ if (status < 0) {
+ iso_sched_free(stream, urb->hcpriv);
+ urb->hcpriv = NULL;
+ }
spin_unlock_irqrestore(&ehci->lock, flags);
done:
return status;
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 2d792ab271e2..d410e3206e2d 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1685,6 +1685,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
case ClearHubFeature:
break;
case ClearPortFeature:
+ if (index != 1)
+ goto error;
switch (value) {
case USB_PORT_FEAT_SUSPEND:
break;
@@ -1694,6 +1696,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
!pdata->vbus_active_level);
fallthrough;
default:
+ if (value >= 32)
+ goto error;
max3421_hcd->port_status &= ~(1 << value);
}
break;
@@ -1726,6 +1730,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
break;
case SetPortFeature:
+ if (index != 1)
+ goto error;
switch (value) {
case USB_PORT_FEAT_LINK_STATE:
case USB_PORT_FEAT_U1_TIMEOUT:
@@ -1747,6 +1753,8 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
max3421_reset_port(hcd);
fallthrough;
default:
+ if (value >= 32)
+ goto error;
if ((max3421_hcd->port_status & USB_PORT_STAT_POWER)
!= 0)
max3421_hcd->port_status |= (1 << value);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 2b871540bb50..188c5339587f 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1590,6 +1590,7 @@ sl811h_remove(struct platform_device *dev)
remove_debug_file(sl811);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
/* some platforms may use IORESOURCE_IO */
res = platform_get_resource(dev, IORESOURCE_MEM, 1);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 9243eff82817..31509845594e 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3577,6 +3577,7 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
struct xhci_virt_device *vdev;
struct xhci_command *command;
struct xhci_input_control_ctx *ctrl_ctx;
+ struct xhci_stream_info *stream_info[EP_CTX_PER_DEV];
unsigned int ep_index;
unsigned long flags;
u32 changed_ep_bitmask;
@@ -3637,10 +3638,15 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
if (ret < 0)
return ret;
+ /*
+ * dma_free_coherent() called by xhci_free_stream_info() may sleep,
+ * so save stream_info pointers and clear references under lock,
+ * then free the memory outside lock.
+ */
spin_lock_irqsave(&xhci->lock, flags);
for (i = 0; i < num_eps; i++) {
ep_index = xhci_get_endpoint_index(&eps[i]->desc);
- xhci_free_stream_info(xhci, vdev->eps[ep_index].stream_info);
+ stream_info[i] = vdev->eps[ep_index].stream_info;
vdev->eps[ep_index].stream_info = NULL;
/* FIXME Unset maxPstreams in endpoint context and
* update deq ptr to point to normal string ring.
@@ -3650,6 +3656,9 @@ static int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
}
spin_unlock_irqrestore(&xhci->lock, flags);
+ for (i = 0; i < num_eps; i++)
+ xhci_free_stream_info(xhci, stream_info[i]);
+
return 0;
}
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
index 45cff32656c6..1524e4770a46 100644
--- a/drivers/usb/misc/chaoskey.c
+++ b/drivers/usb/misc/chaoskey.c
@@ -320,7 +320,6 @@ static int chaoskey_release(struct inode *inode, struct file *file)
mutex_unlock(&dev->lock);
destruction:
mutex_unlock(&chaoskey_list_lock);
- usb_dbg(interface, "release success");
return rv;
}
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index ea39243efee3..0ce12717c14d 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table);
/* structure to hold all of our device specific stuff */
struct usb_idmouse {
+ struct kref kref;
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
@@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf)
return 0;
}
-static inline void idmouse_delete(struct usb_idmouse *dev)
+static inline void idmouse_delete(struct kref *kref)
{
+ struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref);
+
kfree(dev->bulk_in_buffer);
kfree(dev);
}
@@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file)
/* increment our usage count for the driver */
++dev->open;
+ kref_get(&dev->kref);
+
/* save our object in the file's private structure */
file->private_data = dev;
@@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file)
/* lock our device */
mutex_lock(&dev->lock);
-
--dev->open;
+ mutex_unlock(&dev->lock);
+
+ kref_put(&dev->kref, idmouse_delete);
- if (!dev->present) {
- /* the device was unplugged before the file was released */
- mutex_unlock(&dev->lock);
- idmouse_delete(dev);
- } else {
- mutex_unlock(&dev->lock);
- }
return 0;
}
@@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface,
if (dev == NULL)
return -ENOMEM;
+ kref_init(&dev->kref);
mutex_init(&dev->lock);
dev->udev = udev;
dev->interface = interface;
@@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface,
result = usb_find_bulk_in_endpoint(iface_desc, &endpoint);
if (result) {
dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n");
- idmouse_delete(dev);
- return result;
+ goto err_put_kref;
}
dev->orig_bi_size = usb_endpoint_maxp(endpoint);
@@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface,
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
- idmouse_delete(dev);
- return -ENOMEM;
+ result = -ENOMEM;
+ goto err_put_kref;
}
/* allow device read, write and ioctl */
@@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface,
if (result) {
/* something prevented us from registering this device */
dev_err(&interface->dev, "Unable to allocate minor number.\n");
- idmouse_delete(dev);
- return result;
+ goto err_put_kref;
}
/* be noisy */
dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
return 0;
+
+err_put_kref:
+ kref_put(&dev->kref, idmouse_delete);
+
+ return result;
}
static void idmouse_disconnect(struct usb_interface *interface)
@@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface)
/* prevent device read, write and ioctl */
dev->present = 0;
- /* if the device is opened, idmouse_release will clean this up */
- if (!dev->open) {
- mutex_unlock(&dev->lock);
- idmouse_delete(dev);
- } else {
- /* unlock */
- mutex_unlock(&dev->lock);
- }
+ mutex_unlock(&dev->lock);
+
+ kref_put(&dev->kref, idmouse_delete);
dev_info(&interface->dev, "disconnected\n");
}
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 4fae04094021..5ff51af16a60 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -74,7 +74,6 @@ struct iowarrior {
struct mutex mutex; /* locks this structure */
struct usb_device *udev; /* save off the usb device pointer */
struct usb_interface *interface; /* the interface for this device */
- unsigned char minor; /* the starting minor number for this device */
struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
struct urb *int_in_urb; /* the urb for reading data */
@@ -246,7 +245,6 @@ static void iowarrior_write_callback(struct urb *urb)
*/
static inline void iowarrior_delete(struct iowarrior *dev)
{
- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
kfree(dev->int_in_buffer);
usb_free_urb(dev->int_in_urb);
kfree(dev->read_queue);
@@ -297,9 +295,6 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer,
goto exit;
}
- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
- dev->minor, count);
-
/* read count must be packet size (+ time stamp) */
if ((count != dev->report_size)
&& (count != (dev->report_size + 1))) {
@@ -379,8 +374,6 @@ static ssize_t iowarrior_write(struct file *file,
retval = -ENODEV;
goto exit;
}
- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
- dev->minor, count);
/* if count is 0 we're already done */
if (count == 0) {
retval = 0;
@@ -523,9 +516,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
goto error_out;
}
- dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
- dev->minor, cmd, arg);
-
retval = 0;
io_res = 0;
switch (cmd) {
@@ -672,8 +662,6 @@ static int iowarrior_release(struct inode *inode, struct file *file)
if (!dev)
return -ENODEV;
- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
-
/* lock our device */
mutex_lock(&dev->mutex);
@@ -776,6 +764,7 @@ static int iowarrior_probe(struct usb_interface *interface,
struct usb_host_interface *iface_desc;
int retval = -ENOMEM;
int res;
+ int minor;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
@@ -891,12 +880,12 @@ static int iowarrior_probe(struct usb_interface *interface,
goto error;
}
- dev->minor = interface->minor;
+ minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
"now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
- iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
+ iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE);
return retval;
error:
@@ -920,13 +909,15 @@ static void iowarrior_disconnect(struct usb_interface *interface)
/* prevent device read, write and ioctl */
dev->present = 0;
+ /* write urbs are not stopped on close() so kill unconditionally */
+ usb_kill_anchored_urbs(&dev->submitted);
+
if (dev->opened) {
/* There is a process that holds a filedescriptor to the device ,
so we only shutdown read-/write-ops going on.
Deleting the device is postponed until close() was called.
*/
usb_kill_urb(dev->int_in_urb);
- usb_kill_anchored_urbs(&dev->submitted);
wake_up_interruptible(&dev->read_wait);
wake_up_interruptible(&dev->write_wait);
mutex_unlock(&dev->mutex);
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 7cbef74dfc9a..1fbb9a96e989 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -150,6 +150,7 @@ MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in
/* Structure to hold all of our device specific stuff */
struct ld_usb {
+ struct kref kref;
struct mutex mutex; /* locks this structure */
struct usb_interface *intf; /* save off the usb interface pointer */
unsigned long disconnected:1;
@@ -201,8 +202,10 @@ static void ld_usb_abort_transfers(struct ld_usb *dev)
/*
* ld_usb_delete
*/
-static void ld_usb_delete(struct ld_usb *dev)
+static void ld_usb_delete(struct kref *kref)
{
+ struct ld_usb *dev = container_of(kref, struct ld_usb, kref);
+
/* free data structures */
usb_free_urb(dev->interrupt_in_urb);
usb_free_urb(dev->interrupt_out_urb);
@@ -355,6 +358,8 @@ static int ld_usb_open(struct inode *inode, struct file *file)
goto unlock_exit;
}
+ kref_get(&dev->kref);
+
/* save device in the file's private structure */
file->private_data = dev;
@@ -381,17 +386,8 @@ static int ld_usb_release(struct inode *inode, struct file *file)
mutex_lock(&dev->mutex);
- if (dev->open_count != 1) {
- retval = -ENODEV;
+ if (dev->disconnected)
goto unlock_exit;
- }
- if (dev->disconnected) {
- /* the device was unplugged before the file was released */
- mutex_unlock(&dev->mutex);
- /* unlock here as ld_usb_delete frees dev */
- ld_usb_delete(dev);
- goto exit;
- }
/* wait until write transfer is finished */
if (dev->interrupt_out_busy)
@@ -401,7 +397,7 @@ static int ld_usb_release(struct inode *inode, struct file *file)
unlock_exit:
mutex_unlock(&dev->mutex);
-
+ kref_put(&dev->kref, ld_usb_delete);
exit:
return retval;
}
@@ -660,6 +656,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
goto exit;
+
+ kref_init(&dev->kref);
mutex_init(&dev->mutex);
spin_lock_init(&dev->rbsl);
dev->intf = intf;
@@ -741,7 +739,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
return retval;
error:
- ld_usb_delete(dev);
+ kref_put(&dev->kref, ld_usb_delete);
return retval;
}
@@ -769,18 +767,18 @@ static void ld_usb_disconnect(struct usb_interface *intf)
mutex_lock(&dev->mutex);
- /* if the device is not opened, then we clean up right now */
- if (!dev->open_count) {
- mutex_unlock(&dev->mutex);
- ld_usb_delete(dev);
- } else {
- dev->disconnected = 1;
+ dev->disconnected = 1;
+
+ if (dev->open_count) {
/* wake up pollers */
wake_up_interruptible_all(&dev->read_wait);
wake_up_interruptible_all(&dev->write_wait);
- mutex_unlock(&dev->mutex);
}
+ mutex_unlock(&dev->mutex);
+
+ kref_put(&dev->kref, ld_usb_delete);
+
dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
(minor - USB_LD_MINOR_BASE));
}
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 379cf01a6e96..22e82648b3c2 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -185,6 +185,7 @@ MODULE_DEVICE_TABLE(usb, tower_table);
/* Structure to hold all of our device specific stuff */
struct lego_usb_tower {
+ struct kref kref;
struct mutex lock; /* locks this structure */
struct usb_device *udev; /* save off the usb device pointer */
unsigned char minor; /* the starting minor number for this device */
@@ -220,7 +221,6 @@ struct lego_usb_tower {
/* local function prototypes */
static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos);
static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
-static inline void tower_delete(struct lego_usb_tower *dev);
static int tower_open(struct inode *inode, struct file *file);
static int tower_release(struct inode *inode, struct file *file);
static __poll_t tower_poll(struct file *file, poll_table *wait);
@@ -286,8 +286,10 @@ static inline void lego_usb_tower_debug_data(struct device *dev,
/*
* tower_delete
*/
-static inline void tower_delete(struct lego_usb_tower *dev)
+static inline void tower_delete(struct kref *kref)
{
+ struct lego_usb_tower *dev = container_of(kref, struct lego_usb_tower, kref);
+
/* free data structures */
usb_free_urb(dev->interrupt_in_urb);
usb_free_urb(dev->interrupt_out_urb);
@@ -381,6 +383,8 @@ static int tower_open(struct inode *inode, struct file *file)
dev->open_count = 1;
+ kref_get(&dev->kref);
+
unlock_exit:
mutex_unlock(&dev->lock);
@@ -404,14 +408,8 @@ static int tower_release(struct inode *inode, struct file *file)
mutex_lock(&dev->lock);
- if (dev->disconnected) {
- /* the device was unplugged before the file was released */
-
- /* unlock here as tower_delete frees dev */
- mutex_unlock(&dev->lock);
- tower_delete(dev);
- goto exit;
- }
+ if (dev->disconnected)
+ goto out_unlock;
/* wait until write transfer is finished */
if (dev->interrupt_out_busy) {
@@ -425,7 +423,9 @@ static int tower_release(struct inode *inode, struct file *file)
dev->open_count = 0;
+out_unlock:
mutex_unlock(&dev->lock);
+ kref_put(&dev->kref, tower_delete);
exit:
return retval;
}
@@ -752,6 +752,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_
if (!dev)
goto exit;
+ kref_init(&dev->kref);
mutex_init(&dev->lock);
dev->udev = usb_get_dev(udev);
spin_lock_init(&dev->read_buffer_lock);
@@ -828,7 +829,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_
return retval;
error:
- tower_delete(dev);
+ kref_put(&dev->kref, tower_delete);
return retval;
}
@@ -856,18 +857,18 @@ static void tower_disconnect(struct usb_interface *interface)
mutex_lock(&dev->lock);
- /* if the device is not opened, then we clean up right now */
- if (!dev->open_count) {
- mutex_unlock(&dev->lock);
- tower_delete(dev);
- } else {
- dev->disconnected = 1;
+ dev->disconnected = 1;
+
+ if (dev->open_count) {
/* wake up pollers */
wake_up_interruptible_all(&dev->read_wait);
wake_up_interruptible_all(&dev->write_wait);
- mutex_unlock(&dev->lock);
}
+ mutex_unlock(&dev->lock);
+
+ kref_put(&dev->kref, tower_delete);
+
dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n",
(minor - LEGO_USB_TOWER_MINOR_BASE));
}
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index e93852460fed..3bb0405d85c0 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -732,8 +732,11 @@ static int uss720_probe(struct usb_interface *intf,
* here. */
ret = get_1284_register(pp, 0, ®, GFP_KERNEL);
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
- if (ret < 0)
+ if (ret < 0) {
+ priv->pp = NULL;
+ parport_del_port(pp);
goto probe_abort;
+ }
ret = usb_find_last_int_in_endpoint(interface, &epd);
if (!ret) {
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index ad0eeac4332d..e35b28b01ea6 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -304,6 +304,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep,
if (mtu3_prepare_transfer(mep)) {
ret = -EAGAIN;
+ usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
goto error;
}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 5eac99407c16..62a75be78dfc 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -396,12 +396,14 @@ static int digi_write_oob_command(struct usb_serial_port *port,
len &= ~3;
memcpy(oob_port->write_urb->transfer_buffer, buf, len);
oob_port->write_urb->transfer_buffer_length = len;
+
ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
- if (ret == 0) {
- oob_priv->dp_write_urb_in_use = 1;
- count -= len;
- buf += len;
- }
+ if (ret)
+ break;
+
+ oob_priv->dp_write_urb_in_use = 1;
+ count -= len;
+ buf += len;
}
spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
if (ret)
@@ -431,20 +433,22 @@ static int digi_write_inb_command(struct usb_serial_port *port,
int len;
struct digi_port *priv = usb_get_serial_port_data(port);
unsigned char *data = port->write_urb->transfer_buffer;
+ unsigned long expire;
unsigned long flags;
dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
priv->dp_port_num, count);
if (timeout)
- timeout += jiffies;
- else
- timeout = ULONG_MAX;
+ expire = jiffies + timeout;
spin_lock_irqsave(&priv->dp_port_lock, flags);
while (count > 0 && ret == 0) {
- while (priv->dp_write_urb_in_use &&
- time_before(jiffies, timeout)) {
+ while (priv->dp_write_urb_in_use) {
+ if (timeout && time_after(jiffies, expire)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
cond_wait_interruptible_timeout_irqrestore(
&priv->write_wait, DIGI_RETRY_TIMEOUT,
&priv->dp_port_lock, flags);
@@ -453,6 +457,9 @@ static int digi_write_inb_command(struct usb_serial_port *port,
spin_lock_irqsave(&priv->dp_port_lock, flags);
}
+ if (ret)
+ break;
+
/* len must be a multiple of 4 and small enough to */
/* guarantee the write will send buffered data first, */
/* so commands are in order with data and not split */
@@ -1073,6 +1080,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
unsigned char buf[32];
struct digi_port *priv = usb_get_serial_port_data(port);
struct ktermios not_termios;
+ int throttled;
/* be sure the device is started up */
if (digi_startup_device(port->serial) != 0)
@@ -1100,6 +1108,21 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
not_termios.c_iflag = ~tty->termios.c_iflag;
digi_set_termios(tty, port, ¬_termios);
}
+
+ spin_lock_irq(&priv->dp_port_lock);
+ throttled = priv->dp_throttle_restart;
+ priv->dp_throttled = 0;
+ priv->dp_throttle_restart = 0;
+ spin_unlock_irq(&priv->dp_port_lock);
+
+ if (throttled) {
+ ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit read urb: %d\n", ret);
+ return ret;
+ }
+ }
+
return 0;
}
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 0eef358b314a..40e5448ffcb5 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -518,7 +518,7 @@ static int keyspan_pda_write_start(struct usb_serial_port *port)
if (count == room)
schedule_work(&priv->unthrottle_work);
- return count;
+ return 0;
}
static void keyspan_pda_write_bulk_callback(struct urb *urb)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 6acf48743d4d..974fc6d2c0fb 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1326,6 +1326,22 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x30), /* Telit FE990D50 (RNDIS) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0990, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x30), /* Telit FE990D50 (rmnet) */
+ .driver_info = NCTRL(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0991, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x30), /* Telit FE990D50 (MBIM) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0992, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x30), /* Telit FE990D50 (ECM) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x0993, 0xff, 0xff, 0x60) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff), /* Telit LE910C1-EUX */
.driver_info = NCTRL(0) | RSVD(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 97c66c0d91f4..2cffc559ace5 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -2306,7 +2306,8 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us)
/*US_DEBUG(usb_stor_show_command(us, srb)); */
scsi_set_resid(srb, 0);
- if (unlikely(!(info->SD_Status & SD_Ready) || (info->MS_Status & MS_Ready)))
+ if (unlikely(!(info->SD_Status & SD_Ready) &&
+ !(info->MS_Status & MS_Ready)))
result = ene_init(us);
if (result == USB_STOR_XFER_GOOD) {
result = USB_STOR_TRANSPORT_ERROR;
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 7b36a3334fb3..777f47052499 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -488,7 +488,7 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS |
- US_FL_ALWAYS_SYNC);
+ US_FL_ALWAYS_SYNC | US_FL_NO_SAME);
p = quirks;
while (*p) {
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 7997937a31b0..978d0cae1c7d 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1542,7 +1542,9 @@ static int anx7411_i2c_probe(struct i2c_client *client)
if (anx7411_typec_check_connection(plat))
dev_err(dev, "check status\n");
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto free_wq;
return 0;
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index afb7192adc8e..ee372884bc28 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1277,6 +1277,7 @@ static ssize_t select_usb_power_delivery_store(struct device *dev,
return -EINVAL;
ret = port->ops->pd_set(port, pd);
+ put_device(&pd->dev);
if (ret)
return ret;
diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
index 66b29d09372e..c26823b46b5b 100644
--- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
@@ -327,6 +327,8 @@ static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *c
return ret;
}
+static void rt1711h_unregister_tcpci_port(void *tcpci);
+
static int rt1711h_probe(struct i2c_client *client)
{
int ret;
@@ -378,6 +380,10 @@ static int rt1711h_probe(struct i2c_client *client)
if (IS_ERR_OR_NULL(chip->tcpci))
return PTR_ERR(chip->tcpci);
+ ret = devm_add_action_or_reset(chip->dev, rt1711h_unregister_tcpci_port, chip->tcpci);
+ if (ret)
+ return ret;
+
ret = devm_request_threaded_irq(chip->dev, client->irq, NULL,
rt1711h_irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
@@ -395,11 +401,9 @@ static int rt1711h_probe(struct i2c_client *client)
return 0;
}
-static void rt1711h_remove(struct i2c_client *client)
+static void rt1711h_unregister_tcpci_port(void *tcpci)
{
- struct rt1711h_chip *chip = i2c_get_clientdata(client);
-
- tcpci_unregister_port(chip->tcpci);
+ tcpci_unregister_port(tcpci);
}
static const struct i2c_device_id rt1711h_id[] = {
@@ -424,7 +428,6 @@ static struct i2c_driver rt1711h_i2c_driver = {
.of_match_table = of_match_ptr(rt1711h_of_match),
},
.probe = rt1711h_probe,
- .remove = rt1711h_remove,
.id_table = rt1711h_id,
};
module_i2c_driver(rt1711h_i2c_driver);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index c6330084d4f6..2eabd89c36cb 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1555,6 +1555,11 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
return;
}
+ if (pmdata->svid_index < 0 || pmdata->svid_index >= pmdata->nsvids) {
+ tcpm_log(port, "Invalid SVID index %d", pmdata->svid_index);
+ return;
+ }
+
for (i = 1; i < cnt; i++) {
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
memset(paltmode, 0, sizeof(*paltmode));
@@ -2232,7 +2237,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
if (svdm_version < 0)
return svdm_version;
- header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
+ header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0);
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 11c7da45a562..09693934d6c5 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -166,12 +166,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp)
* that Multi-function is preferred.
*/
if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) {
- dp->data.status |= DP_STATUS_CON_UFP_D;
+ dp->data.status |= DP_STATUS_CON_DFP_D;
if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D))
dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC;
} else {
- dp->data.status |= DP_STATUS_CON_DFP_D;
+ dp->data.status |= DP_STATUS_CON_UFP_D;
if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D))
dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC;
@@ -185,13 +185,12 @@ static int ucsi_displayport_status_update(struct ucsi_dp *dp)
static int ucsi_displayport_configure(struct ucsi_dp *dp)
{
- u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
u64 command;
if (!dp->override)
return 0;
- command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
+ command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, dp->data.conf);
return ucsi_send_command(dp->con->ucsi, command, NULL, 0);
}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index b424ac599d41..a70e0118680c 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1533,6 +1533,26 @@ static void ucsi_resume_work(struct work_struct *work)
}
}
+int ucsi_suspend(struct ucsi *ucsi)
+{
+ int i;
+
+ /*
+ * Cancel pending work so it cannot access the firmware after the ACPI
+ * EC is stopped for suspend; state is re-read on resume.
+ */
+ cancel_delayed_work_sync(&ucsi->work);
+
+ if (!ucsi->connector)
+ return 0;
+
+ for (i = 0; i < ucsi->cap.num_connectors; i++)
+ cancel_work_sync(&ucsi->connector[i].work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ucsi_suspend);
+
int ucsi_resume(struct ucsi *ucsi)
{
if (ucsi->connector)
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 51e745117dcb..2cb34913358d 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -386,6 +386,7 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *retval, size_t size);
void ucsi_altmode_update_active(struct ucsi_connector *con);
+int ucsi_suspend(struct ucsi *ucsi);
int ucsi_resume(struct ucsi *ucsi);
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 7b3ac133ef86..e40f3bf650ad 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -288,6 +288,13 @@ static void ucsi_acpi_remove(struct platform_device *pdev)
ucsi_acpi_notify);
}
+static int ucsi_acpi_suspend(struct device *dev)
+{
+ struct ucsi_acpi *ua = dev_get_drvdata(dev);
+
+ return ucsi_suspend(ua->ucsi);
+}
+
static int ucsi_acpi_resume(struct device *dev)
{
struct ucsi_acpi *ua = dev_get_drvdata(dev);
@@ -295,7 +302,8 @@ static int ucsi_acpi_resume(struct device *dev)
return ucsi_resume(ua->ucsi);
}
-static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, ucsi_acpi_suspend,
+ ucsi_acpi_resume);
static const struct acpi_device_id ucsi_acpi_match[] = {
{ "PNP0CA0", 0 },
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index f86aaaedf4fc..7d436dfdc028 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -1440,8 +1440,8 @@ static void ucsi_ccg_remove(struct i2c_client *client)
cancel_work_sync(&uc->work);
pm_runtime_disable(uc->dev);
ucsi_unregister(uc->ucsi);
- ucsi_destroy(uc->ucsi);
free_irq(uc->irq, uc);
+ ucsi_destroy(uc->ucsi);
}
static const struct of_device_id ucsi_ccg_of_match_table[] = {
diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h
index 1bd4bc005829..5fc71c73dacd 100644
--- a/drivers/usb/usbip/vudc.h
+++ b/drivers/usb/usbip/vudc.h
@@ -38,7 +38,6 @@ struct vep {
struct vrequest {
struct usb_request req;
- struct vudc *udc;
struct list_head req_entry; /* Request queue */
};
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index d2efd982b528..43a0ad39c4b3 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -333,7 +333,6 @@ static int vep_queue(struct usb_ep *_ep, struct usb_request *_req,
static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct vep *ep;
- struct vrequest *req;
struct vudc *udc;
struct vrequest *lst;
unsigned long flags;
@@ -343,8 +342,7 @@ static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
return ret;
ep = to_vep(_ep);
- req = to_vrequest(_req);
- udc = req->udc;
+ udc = ep_to_vudc(ep);
if (!udc->driver)
return -ESHUTDOWN;
diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c
index d7bda179ef79..d6a3a58dbda4 100644
--- a/drivers/vdpa/vdpa_user/vduse_dev.c
+++ b/drivers/vdpa/vdpa_user/vduse_dev.c
@@ -193,6 +193,12 @@ static void vduse_enqueue_msg(struct list_head *head,
list_add_tail(&msg->list, head);
}
+static void vduse_enqueue_msg_head(struct list_head *head,
+ struct vduse_dev_msg *msg)
+{
+ list_add(&msg->list, head);
+}
+
static void vduse_dev_broken(struct vduse_dev *dev)
{
struct vduse_dev_msg *msg, *tmp;
@@ -324,6 +330,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
struct file *file = iocb->ki_filp;
struct vduse_dev *dev = file->private_data;
struct vduse_dev_msg *msg;
+ struct vduse_dev_request req;
int size = sizeof(struct vduse_dev_request);
ssize_t ret;
@@ -335,12 +342,11 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
msg = vduse_dequeue_msg(&dev->send_list);
if (msg)
break;
+ spin_unlock(&dev->msg_lock);
- ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
- goto unlock;
+ return -EAGAIN;
- spin_unlock(&dev->msg_lock);
ret = wait_event_interruptible_exclusive(dev->waitq,
!list_empty(&dev->send_list));
if (ret)
@@ -348,17 +354,34 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
spin_lock(&dev->msg_lock);
}
+
+ memcpy(&req, &msg->req, sizeof(req));
+ /*
+ * We must ensure vduse_msg is on send_list or recv_list before unlock
+ * dev->msg_lock. Because vduse_dev_msg_sync() may be timeout when we
+ * copy data to userspace, and will call list_del() for this msg.
+ */
+ vduse_enqueue_msg(&dev->recv_list, msg);
spin_unlock(&dev->msg_lock);
- ret = copy_to_iter(&msg->req, size, to);
- spin_lock(&dev->msg_lock);
+
+ ret = copy_to_iter(&req, size, to);
if (ret != size) {
+ /*
+ * Roll back: move msg back to send_list if still pending.
+ *
+ * NOTE:
+ * vduse_find_msg() must use req.request_id instead of `msg`.
+ * A malicious userspace may reply to this request, and wake up
+ * the caller, after which `msg` will have already been freed.
+ * And here vduse_find_msg() will return NULL then do nothing.
+ */
+ spin_lock(&dev->msg_lock);
+ msg = vduse_find_msg(&dev->recv_list, req.request_id);
+ if (msg)
+ vduse_enqueue_msg_head(&dev->send_list, msg);
+ spin_unlock(&dev->msg_lock);
ret = -EFAULT;
- vduse_enqueue_msg(&dev->send_list, msg);
- goto unlock;
}
- vduse_enqueue_msg(&dev->recv_list, msg);
-unlock:
- spin_unlock(&dev->msg_lock);
return ret;
}
@@ -1395,26 +1418,18 @@ static int vduse_dev_release(struct inode *inode, struct file *file)
return 0;
}
-static struct vduse_dev *vduse_dev_get_from_minor(int minor)
+static int vduse_dev_open(struct inode *inode, struct file *file)
{
+ int ret = -EBUSY;
struct vduse_dev *dev;
mutex_lock(&vduse_lock);
- dev = idr_find(&vduse_idr, minor);
- mutex_unlock(&vduse_lock);
-
- return dev;
-}
-
-static int vduse_dev_open(struct inode *inode, struct file *file)
-{
- int ret;
- struct vduse_dev *dev = vduse_dev_get_from_minor(iminor(inode));
-
- if (!dev)
+ dev = idr_find(&vduse_idr, iminor(inode));
+ if (!dev) {
+ mutex_unlock(&vduse_lock);
return -ENODEV;
+ }
- ret = -EBUSY;
mutex_lock(&dev->lock);
if (dev->connected)
goto unlock;
@@ -1424,6 +1439,7 @@ static int vduse_dev_open(struct inode *inode, struct file *file)
file->private_data = dev;
unlock:
mutex_unlock(&dev->lock);
+ mutex_unlock(&vduse_lock);
return ret;
}
diff --git a/drivers/vfio/pci/mlx5/cmd.h b/drivers/vfio/pci/mlx5/cmd.h
index aec4c69dd6c1..f002e5eda04f 100644
--- a/drivers/vfio/pci/mlx5/cmd.h
+++ b/drivers/vfio/pci/mlx5/cmd.h
@@ -147,23 +147,26 @@ struct mlx5_vhca_qp {
struct mlx5_vhca_page_tracker {
u32 id;
u32 pdn;
- u8 is_err:1;
+ /* Flags modified at runtime - dedicated storage unit */
+ u8 is_err;
+ int status;
struct mlx5_uars_page *uar;
struct mlx5_vhca_cq cq;
struct mlx5_vhca_qp *host_qp;
struct mlx5_vhca_qp *fw_qp;
struct mlx5_nb nb;
- int status;
};
struct mlx5vf_pci_core_device {
struct vfio_pci_core_device core_device;
int vf_id;
u16 vhca_id;
+ /* Flags only modified on setup/release - bitfield ok */
u8 migrate_cap:1;
- u8 deferred_reset:1;
- u8 mdev_detach:1;
- u8 log_active:1;
+ /* Flags modified at runtime - dedicated storage unit */
+ u8 mdev_detach;
+ u8 log_active;
+ u8 deferred_reset;
struct completion tracker_comp;
/* protect migration state */
struct mutex state_mutex;
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 55e28feba475..db31aeb9df70 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -242,8 +242,11 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat
int ret;
/* Prevent changing power state for PFs with VFs enabled */
- if (pci_num_vf(pdev) && state > PCI_D0)
- return -EBUSY;
+ if (state > PCI_D0) {
+ lockdep_assert_held_write(&vdev->memory_lock);
+ if (vdev->sriov_active)
+ return -EBUSY;
+ }
if (vdev->needs_pm_restore) {
if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) {
@@ -474,7 +477,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
u16 cmd;
u8 msix_pos;
- if (!disable_idle_d3) {
+ if (!vdev->disable_idle_d3) {
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
@@ -552,7 +555,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
out_disable_device:
pci_disable_device(pdev);
out_power:
- if (!disable_idle_d3)
+ if (!vdev->disable_idle_d3)
pm_runtime_put(&pdev->dev);
return ret;
}
@@ -679,7 +682,7 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
vfio_pci_dev_set_try_reset(vdev->vdev.dev_set);
/* Put the pm-runtime usage counter acquired during enable */
- if (!disable_idle_d3)
+ if (!vdev->disable_idle_d3)
pm_runtime_put(&pdev->dev);
}
EXPORT_SYMBOL_GPL(vfio_pci_core_disable);
@@ -2055,6 +2058,8 @@ int vfio_pci_core_init_dev(struct vfio_device *core_vdev)
init_rwsem(&vdev->memory_lock);
xa_init(&vdev->ctx);
+ vdev->disable_idle_d3 = disable_idle_d3;
+
return 0;
}
EXPORT_SYMBOL_GPL(vfio_pci_core_init_dev);
@@ -2146,7 +2151,7 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
dev->driver->pm = &vfio_pci_core_pm_ops;
pm_runtime_allow(dev);
- if (!disable_idle_d3)
+ if (!vdev->disable_idle_d3)
pm_runtime_put(dev);
ret = vfio_register_group_dev(&vdev->vdev);
@@ -2155,10 +2160,11 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
return 0;
out_power:
- if (!disable_idle_d3)
+ if (!vdev->disable_idle_d3)
pm_runtime_get_noresume(dev);
pm_runtime_forbid(dev);
+ vfio_pci_vga_uninit(vdev);
out_vf:
vfio_pci_vf_uninit(vdev);
return ret;
@@ -2174,7 +2180,7 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev)
vfio_pci_vf_uninit(vdev);
vfio_pci_vga_uninit(vdev);
- if (!disable_idle_d3)
+ if (!vdev->disable_idle_d3)
pm_runtime_get_noresume(&vdev->pdev->dev);
pm_runtime_forbid(&vdev->pdev->dev);
@@ -2236,8 +2242,9 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev,
down_write(&vdev->memory_lock);
vfio_pci_set_power_state(vdev, PCI_D0);
- ret = pci_enable_sriov(pdev, nr_virtfn);
+ vdev->sriov_active = true;
up_write(&vdev->memory_lock);
+ ret = pci_enable_sriov(pdev, nr_virtfn);
if (ret) {
pm_runtime_put(&pdev->dev);
goto out_del;
@@ -2251,6 +2258,13 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev,
}
out_del:
+ /*
+ * Avoid taking the memory_lock intentionally. A race with a power
+ * state transition would at most result in an -EBUSY, leaving the
+ * device in PCI_D0.
+ */
+ vdev->sriov_active = false;
+
mutex_lock(&vfio_pci_sriov_pfs_mutex);
list_del_init(&vdev->sriov_pfs_item);
out_unlock:
@@ -2494,7 +2508,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
* state. Increment the usage count for all the devices in the dev_set
* before reset and decrement the same after reset.
*/
- if (!disable_idle_d3 && vfio_pci_dev_set_pm_runtime_get(dev_set))
+ if (vfio_pci_dev_set_pm_runtime_get(dev_set))
return;
if (!pci_reset_bus(pdev))
@@ -2504,8 +2518,7 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
if (reset_done)
cur->needs_reset = false;
- if (!disable_idle_d3)
- pm_runtime_put(&cur->pdev->dev);
+ pm_runtime_put(&cur->pdev->dev);
}
}
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index ec4fbd993bf0..2d0e28db8053 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -843,7 +843,8 @@ int vfio_mig_get_next_state(struct vfio_device *device,
* logical state, as per the above comment.
*/
*next_fsm = vfio_from_fsm_table[cur_fsm][new_fsm];
- while ((state_flags_table[*next_fsm] & device->migration_flags) !=
+ while (*next_fsm != VFIO_DEVICE_STATE_ERROR &&
+ (state_flags_table[*next_fsm] & device->migration_flags) !=
state_flags_table[*next_fsm])
*next_fsm = vfio_from_fsm_table[*next_fsm][new_fsm];
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index b26d9a8e4e05..2953ab6e0c1d 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -390,13 +390,20 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net,
static void vhost_zerocopy_callback(struct sk_buff *skb,
struct ubuf_info *ubuf_base, bool success)
{
- struct ubuf_info_msgzc *ubuf = uarg_to_msgzc(ubuf_base);
- struct vhost_net_ubuf_ref *ubufs = ubuf->ctx;
- struct vhost_virtqueue *vq = ubufs->vq;
+ struct ubuf_info_msgzc *ubuf;
+ struct vhost_net_ubuf_ref *ubufs;
+ struct vhost_virtqueue *vq;
int cnt;
- rcu_read_lock_bh();
+ /* Only the final cloned skb reference completes the vhost descriptor. */
+ if (!refcount_dec_and_test(&ubuf_base->refcnt))
+ return;
+
+ ubuf = uarg_to_msgzc(ubuf_base);
+ ubufs = ubuf->ctx;
+ vq = ubufs->vq;
+ rcu_read_lock_bh();
/* set len to mark this desc buffers done DMA */
vq->heads[ubuf->desc].len = success ?
VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN;
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index f4da76fa235e..3645d83f240d 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1377,16 +1377,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
}
#ifdef CONFIG_MMU
-static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
+static int
+vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index,
+ struct vdpa_notification_area *notify)
{
- struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
+
+ if (index > 65535 || index >= v->nvqs)
+ return -EINVAL;
+
+ index = array_index_nospec(index, v->nvqs);
+
+ *notify = ops->get_vq_notification(vdpa, index);
+
+ return 0;
+}
+
+static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
+{
+ struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
struct vdpa_notification_area notify;
struct vm_area_struct *vma = vmf->vma;
- u16 index = vma->vm_pgoff;
+ unsigned long index = vma->vm_pgoff;
- notify = ops->get_vq_notification(vdpa, index);
+ if (vhost_vdpa_get_vq_notification(v, index, ¬ify))
+ return VM_FAULT_SIGBUS;
return vmf_insert_pfn(vma, vmf->address & PAGE_MASK, PFN_DOWN(notify.addr));
}
@@ -1409,8 +1425,6 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
if (vma->vm_flags & VM_READ)
return -EINVAL;
- if (index > 65535)
- return -EINVAL;
if (!ops->get_vq_notification)
return -ENOTSUPP;
@@ -1418,7 +1432,8 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
* support the doorbell which sits on the page boundary and
* does not share the page with other registers.
*/
- notify = ops->get_vq_notification(vdpa, index);
+ if (vhost_vdpa_get_vq_notification(v, index, ¬ify))
+ return -EINVAL;
if (notify.addr & (PAGE_SIZE - 1))
return -EINVAL;
if (vma->vm_end - vma->vm_start != notify.size)
diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c
index 93fd1773402c..1147cbbdc9bb 100644
--- a/drivers/video/fbdev/aty/radeon_base.c
+++ b/drivers/video/fbdev/aty/radeon_base.c
@@ -2474,6 +2474,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
return 0;
err_unmap_fb:
iounmap(rinfo->fb_base);
+ fb_destroy_modelist(&info->modelist);
err_unmap_rom:
kfree(rinfo->mon1_EDID);
kfree(rinfo->mon2_EDID);
diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
index e857b15e9f5d..e18989c87d03 100644
--- a/drivers/video/fbdev/broadsheetfb.c
+++ b/drivers/video/fbdev/broadsheetfb.c
@@ -1072,12 +1072,14 @@ static int broadsheetfb_probe(struct platform_device *dev)
info->flags = FBINFO_VIRTFB;
info->fbdefio = &broadsheetfb_defio;
- fb_deferred_io_init(info);
+ retval = fb_deferred_io_init(info);
+ if (retval)
+ goto err_vfree;
retval = fb_alloc_cmap(&info->cmap, 16, 0);
if (retval < 0) {
dev_err(&dev->dev, "Failed to allocate colormap\n");
- goto err_vfree;
+ goto err_fbdefio;
}
/* set cmap */
@@ -1121,6 +1123,8 @@ static int broadsheetfb_probe(struct platform_device *dev)
board->cleanup(par);
err_cmap:
fb_dealloc_cmap(&info->cmap);
+err_fbdefio:
+ fb_deferred_io_cleanup(info);
err_vfree:
vfree(videomemory);
err_fb_rel:
diff --git a/drivers/video/fbdev/carminefb.c b/drivers/video/fbdev/carminefb.c
index e56065cdba97..49b6f1f977b6 100644
--- a/drivers/video/fbdev/carminefb.c
+++ b/drivers/video/fbdev/carminefb.c
@@ -589,6 +589,7 @@ static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,
return 0;
err_dealloc_cmap:
+ fb_destroy_modelist(&info->modelist);
fb_dealloc_cmap(&info->cmap);
err_free_fb:
framebuffer_release(info);
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index d1ac4e45eea6..6ceb3c8d7327 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -1183,6 +1183,7 @@ static void fbcon_deinit(struct vc_data *vc)
int idx;
fbcon_free_font(p);
+ p->mode = NULL;
idx = con2fb_map[vc->vc_num];
if (idx == -1)
@@ -1361,14 +1362,14 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
p = &fb_display[unit];
- if (var_to_display(p, var, info))
- return;
-
vc = vc_cons[unit].d;
if (!vc)
return;
+ if (var_to_display(p, var, info))
+ return;
+
default_mode = vc->vc_display_fg;
svc = *default_mode;
t = &fb_display[svc->vc_num];
@@ -2418,6 +2419,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
struct fbcon_display *p = &fb_display[vc->vc_num];
int resize, ret, old_userfont, old_width, old_height, old_charcount;
u8 *old_data = vc->vc_font.data;
+ unsigned short old_hi_font_mask = vc->vc_hi_font_mask;
resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
vc->vc_font.data = (void *)(p->fontdata = data);
@@ -2471,6 +2473,12 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
vc->vc_font.height = old_height;
vc->vc_font.charcount = old_charcount;
+ /* Restore the hi_font state and screen buffer */
+ if (old_hi_font_mask && !vc->vc_hi_font_mask)
+ set_vc_hi_font(vc, true);
+ else if (!old_hi_font_mask && vc->vc_hi_font_mask)
+ set_vc_hi_font(vc, false);
+
return ret;
}
diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c
index ef526ed4a2d9..c0d153ddf9fc 100644
--- a/drivers/video/fbdev/hecubafb.c
+++ b/drivers/video/fbdev/hecubafb.c
@@ -192,7 +192,9 @@ static int hecubafb_probe(struct platform_device *dev)
info->flags = FBINFO_VIRTFB;
info->fbdefio = &hecubafb_defio;
- fb_deferred_io_init(info);
+ retval = fb_deferred_io_init(info);
+ if (retval)
+ goto err_fbdefio;
retval = register_framebuffer(info);
if (retval < 0)
@@ -209,6 +211,8 @@ static int hecubafb_probe(struct platform_device *dev)
return 0;
err_fbreg:
+ fb_deferred_io_cleanup(info);
+err_fbdefio:
framebuffer_release(info);
err_fballoc:
vfree(videomemory);
diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c
index 1897e65ab703..490c26dded97 100644
--- a/drivers/video/fbdev/i740fb.c
+++ b/drivers/video/fbdev/i740fb.c
@@ -1153,6 +1153,7 @@ static int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
fb_dealloc_cmap(&info->cmap);
err_alloc_cmap:
err_find_mode:
+ fb_destroy_modelist(&info->modelist);
if (par->ddc_registered)
i2c_del_adapter(&par->ddc_adapter);
pci_iounmap(dev, par->regs);
diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
index 130394616a7c..c14387354760 100644
--- a/drivers/video/fbdev/metronomefb.c
+++ b/drivers/video/fbdev/metronomefb.c
@@ -645,12 +645,14 @@ static int metronomefb_probe(struct platform_device *dev)
info->flags = FBINFO_VIRTFB;
info->fbdefio = &metronomefb_defio;
- fb_deferred_io_init(info);
+ retval = fb_deferred_io_init(info);
+ if (retval)
+ goto err_free_irq;
retval = fb_alloc_cmap(&info->cmap, 8, 0);
if (retval < 0) {
dev_err(&dev->dev, "Failed to allocate colormap\n");
- goto err_free_irq;
+ goto err_fbdefio;
}
/* set cmap */
@@ -673,6 +675,8 @@ static int metronomefb_probe(struct platform_device *dev)
err_cmap:
fb_dealloc_cmap(&info->cmap);
+err_fbdefio:
+ fb_deferred_io_cleanup(info);
err_free_irq:
board->cleanup(par);
err_csum_table:
diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c
index 907c22408652..674baba74cf0 100644
--- a/drivers/video/fbdev/nvidia/nvidia.c
+++ b/drivers/video/fbdev/nvidia/nvidia.c
@@ -1418,6 +1418,7 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
err_out_iounmap_fb:
iounmap(info->screen_base);
+ fb_destroy_modelist(&info->modelist);
err_out_free_base1:
fb_destroy_modedb(info->monspecs.modedb);
nvidia_delete_i2c_busses(par);
diff --git a/drivers/video/fbdev/s3fb.c b/drivers/video/fbdev/s3fb.c
index 7d257489edcc..c2904df71be5 100644
--- a/drivers/video/fbdev/s3fb.c
+++ b/drivers/video/fbdev/s3fb.c
@@ -1374,6 +1374,7 @@ static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
err_alloc_cmap:
err_find_mode:
#ifdef CONFIG_FB_S3_DDC
+ fb_destroy_modelist(&info->modelist);
if (par->ddc_registered)
i2c_del_adapter(&par->ddc_adapter);
if (par->mmio)
diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c
index b9d72f368c6c..6223f966ea92 100644
--- a/drivers/video/fbdev/sm501fb.c
+++ b/drivers/video/fbdev/sm501fb.c
@@ -95,6 +95,7 @@ struct sm501fb_info {
void __iomem *fbmem; /* remapped framebuffer */
size_t fbmem_len; /* length of remapped region */
u8 *edid_data;
+ char *fb_mode;
};
/* per-framebuffer private data */
@@ -1788,12 +1789,11 @@ static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head,
fb->var.yres_virtual = fb->var.yres;
} else {
if (info->edid_data) {
- ret = fb_find_mode(&fb->var, fb, fb_mode,
+ ret = fb_find_mode(&fb->var, fb,
+ info->fb_mode ?: fb_mode,
fb->monspecs.modedb,
fb->monspecs.modedb_len,
&sm501_default_mode, default_bpp);
- /* edid_data is no longer needed, free it */
- kfree(info->edid_data);
} else {
ret = fb_find_mode(&fb->var, fb,
NULL, NULL, 0, NULL, 8);
@@ -1969,7 +1969,7 @@ static int sm501fb_probe(struct platform_device *pdev)
/* Get EDID */
cp = of_get_property(np, "mode", &len);
if (cp)
- strcpy(fb_mode, cp);
+ info->fb_mode = kstrdup(cp, GFP_KERNEL);
prop = of_get_property(np, "edid", &len);
if (prop && len == EDID_LENGTH) {
info->edid_data = kmemdup(prop, EDID_LENGTH,
@@ -2026,6 +2026,12 @@ static int sm501fb_probe(struct platform_device *pdev)
goto err_started_crt;
}
+ /* These aren't needed any more */
+ kfree(info->edid_data);
+ kfree(info->fb_mode);
+ info->edid_data = NULL;
+ info->fb_mode = NULL;
+
/* we registered, return ok */
return 0;
@@ -2043,6 +2049,8 @@ static int sm501fb_probe(struct platform_device *pdev)
framebuffer_release(info->fb[HEAD_CRT]);
err_alloc:
+ kfree(info->edid_data);
+ kfree(info->fb_mode);
kfree(info);
return ret;
diff --git a/drivers/video/fbdev/sm712.h b/drivers/video/fbdev/sm712.h
index c7ebf03b8d53..83fe25fc61f2 100644
--- a/drivers/video/fbdev/sm712.h
+++ b/drivers/video/fbdev/sm712.h
@@ -101,7 +101,7 @@ struct modeinit {
#define mmio_addr 0x00800000
#define seqw17() smtc_seqw(0x17, 0x30)
#define big_pixel_depth(p, d) {if (p == 24) {p = 32; d = 32; } }
-#define big_swap(p) ((p & 0xff00ff00 >> 8) | (p & 0x00ff00ff << 8))
+#define big_swap(p) (((p & 0xff00ff00) >> 8) | ((p & 0x00ff00ff) << 8))
#else
#define pal_rgb(r, g, b, val) val
#define big_addr 0
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index aed05aeb89dc..08721da00385 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -1551,6 +1551,7 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_err_iobase:
#ifdef CONFIG_FB_3DFX_I2C
+ fb_destroy_modelist(&info->modelist);
tdfxfb_delete_i2c_busses(default_par);
#endif
arch_phys_wc_del(default_par->wc_cookie);
diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c
index 1ba157530af2..5546098d4f2c 100644
--- a/drivers/video/fbdev/tridentfb.c
+++ b/drivers/video/fbdev/tridentfb.c
@@ -1705,6 +1705,7 @@ static int trident_pci_probe(struct pci_dev *dev,
return 0;
out_unmap2:
+ fb_destroy_modelist(&info->modelist);
if (default_par->ddc_registered)
i2c_del_adapter(&default_par->ddc_adapter);
kfree(info->pixmap.addr);
diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c
index e1f421e91b4f..bc40beacfba6 100644
--- a/drivers/video/fbdev/uvesafb.c
+++ b/drivers/video/fbdev/uvesafb.c
@@ -1697,14 +1697,14 @@ static int uvesafb_probe(struct platform_device *dev)
i = uvesafb_vbe_init_mode(info);
if (i < 0) {
err = -EINVAL;
- goto out;
+ goto out_mode;
} else {
mode = &par->vbe_modes[i];
}
if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
err = -ENXIO;
- goto out;
+ goto out_mode;
}
uvesafb_init_info(info, mode);
diff --git a/drivers/video/fbdev/vesafb.c b/drivers/video/fbdev/vesafb.c
index a21581b40256..939927ac2e74 100644
--- a/drivers/video/fbdev/vesafb.c
+++ b/drivers/video/fbdev/vesafb.c
@@ -255,6 +255,7 @@ static int vesafb_probe(struct platform_device *dev)
/* ignore error return of fb_get_options */
fb_get_options("vesafb", &option);
vesafb_setup(option);
+ kfree(option);
if (si->orig_video_isVGA != VIDEO_TYPE_VLFB)
return -ENODEV;
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 59892a31cf76..049c4f4ee56f 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -710,9 +710,7 @@ static int virtio_mmio_remove(struct platform_device *pdev)
#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES)
-static struct device vm_cmdline_parent = {
- .init_name = "virtio-mmio-cmdline",
-};
+static struct device *vm_cmdline_parent;
static int vm_cmdline_parent_registered;
static int vm_cmdline_id;
@@ -720,7 +718,6 @@ static int vm_cmdline_id;
static int vm_cmdline_set(const char *device,
const struct kernel_param *kp)
{
- int err;
struct resource resources[2] = {};
char *str;
long long base, size;
@@ -752,11 +749,10 @@ static int vm_cmdline_set(const char *device,
resources[1].start = resources[1].end = irq;
if (!vm_cmdline_parent_registered) {
- err = device_register(&vm_cmdline_parent);
- if (err) {
- put_device(&vm_cmdline_parent);
+ vm_cmdline_parent = __root_device_register("virtio-mmio-cmdline", NULL);
+ if (IS_ERR(vm_cmdline_parent)) {
pr_err("Failed to register parent device!\n");
- return err;
+ return PTR_ERR(vm_cmdline_parent);
}
vm_cmdline_parent_registered = 1;
}
@@ -767,7 +763,7 @@ static int vm_cmdline_set(const char *device,
(unsigned long long)resources[0].end,
(int)resources[1].start);
- pdev = platform_device_register_resndata(&vm_cmdline_parent,
+ pdev = platform_device_register_resndata(vm_cmdline_parent,
"virtio-mmio", vm_cmdline_id++,
resources, ARRAY_SIZE(resources), NULL, 0);
@@ -791,8 +787,12 @@ static int vm_cmdline_get_device(struct device *dev, void *data)
static int vm_cmdline_get(char *buffer, const struct kernel_param *kp)
{
buffer[0] = '\0';
- device_for_each_child(&vm_cmdline_parent, buffer,
- vm_cmdline_get_device);
+
+ if (vm_cmdline_parent_registered) {
+ device_for_each_child(vm_cmdline_parent, buffer,
+ vm_cmdline_get_device);
+ }
+
return strlen(buffer) + 1;
}
@@ -814,9 +814,9 @@ static int vm_unregister_cmdline_device(struct device *dev,
static void vm_unregister_cmdline_devices(void)
{
if (vm_cmdline_parent_registered) {
- device_for_each_child(&vm_cmdline_parent, NULL,
+ device_for_each_child(vm_cmdline_parent, NULL,
vm_unregister_cmdline_device);
- device_unregister(&vm_cmdline_parent);
+ root_device_unregister(vm_cmdline_parent);
vm_cmdline_parent_registered = 0;
}
}
diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
index 24e457b69566..920c2addaf90 100644
--- a/drivers/watchdog/apple_wdt.c
+++ b/drivers/watchdog/apple_wdt.c
@@ -191,6 +191,7 @@ static int apple_wdt_probe(struct platform_device *pdev)
}
static const struct of_device_id apple_wdt_of_match[] = {
+ { .compatible = "apple,t8103-wdt" },
{ .compatible = "apple,wdt" },
{},
};
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
index 13e72918338a..704b786cc2ec 100644
--- a/drivers/watchdog/sama5d4_wdt.c
+++ b/drivers/watchdog/sama5d4_wdt.c
@@ -30,6 +30,7 @@ struct sama5d4_wdt {
void __iomem *reg_base;
u32 mr;
u32 ir;
+ u32 wddis_mask;
unsigned long last_ping;
bool need_irq;
bool sam9x60_support;
@@ -48,7 +49,10 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
+static inline bool wdt_enabled(struct sama5d4_wdt *wdt)
+{
+ return !(wdt->mr & wdt->wddis_mask);
+}
#define wdt_read(wdt, field) \
readl_relaxed((wdt)->reg_base + (field))
@@ -81,12 +85,9 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
- if (wdt->sam9x60_support) {
+ if (wdt->sam9x60_support)
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
- wdt->mr &= ~AT91_SAM9X60_WDDIS;
- } else {
- wdt->mr &= ~AT91_WDT_WDDIS;
- }
+ wdt->mr &= ~wdt->wddis_mask;
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
@@ -96,12 +97,9 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
- if (wdt->sam9x60_support) {
+ if (wdt->sam9x60_support)
writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
- wdt->mr |= AT91_SAM9X60_WDDIS;
- } else {
- wdt->mr |= AT91_WDT_WDDIS;
- }
+ wdt->mr |= wdt->wddis_mask;
wdt_write(wdt, AT91_WDT_MR, wdt->mr);
return 0;
@@ -117,7 +115,7 @@ static int sama5d4_wdt_ping(struct watchdog_device *wdd)
}
static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
- unsigned int timeout)
+ unsigned int timeout)
{
struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
u32 value = WDT_SEC2TICKS(timeout);
@@ -140,8 +138,8 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
* If the watchdog is enabled, then the timeout can be updated. Else,
* wait that the user enables it.
*/
- if (wdt_enabled)
- wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
+ if (wdt_enabled(wdt))
+ wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~wdt->wddis_mask);
wdd->timeout = timeout;
@@ -184,10 +182,7 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
{
const char *tmp;
- if (wdt->sam9x60_support)
- wdt->mr = AT91_SAM9X60_WDDIS;
- else
- wdt->mr = AT91_WDT_WDDIS;
+ wdt->mr = wdt->wddis_mask;
if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
!strcmp(tmp, "software"))
@@ -213,15 +208,11 @@ static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
* If the watchdog is already running, we can safely update it.
* Else, we have to disable it properly.
*/
- if (!wdt_enabled) {
+ if (!wdt_enabled(wdt)) {
reg = wdt_read(wdt, AT91_WDT_MR);
- if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
- wdt_write_nosleep(wdt, AT91_WDT_MR,
- reg | AT91_SAM9X60_WDDIS);
- else if (!wdt->sam9x60_support &&
- (!(reg & AT91_WDT_WDDIS)))
+ if (!(reg & wdt->wddis_mask))
wdt_write_nosleep(wdt, AT91_WDT_MR,
- reg | AT91_WDT_WDDIS);
+ reg | wdt->wddis_mask);
}
if (wdt->sam9x60_support) {
@@ -273,6 +264,9 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt"))
wdt->sam9x60_support = true;
+ wdt->wddis_mask = wdt->sam9x60_support ? AT91_SAM9X60_WDDIS
+ : AT91_WDT_WDDIS;
+
watchdog_set_drvdata(wdd, wdt);
regs = devm_platform_ioremap_resource(pdev, 0);
@@ -306,8 +300,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(wdd, wdt_timeout, dev);
reg = wdt_read(wdt, AT91_WDT_MR);
- if (!(reg & AT91_WDT_WDDIS)) {
- wdt->mr &= ~AT91_WDT_WDDIS;
+ if (!(reg & wdt->wddis_mask)) {
+ wdt->mr &= ~wdt->wddis_mask;
set_bit(WDOG_HW_RUNNING, &wdd->status);
}
diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
index 2bd3dc25cb03..7e99c3b1f367 100644
--- a/drivers/watchdog/sp5100_tco.c
+++ b/drivers/watchdog/sp5100_tco.c
@@ -92,7 +92,8 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
dev->revision < 0x40) {
return sp5100;
- } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
+ } else if ((dev->vendor == PCI_VENDOR_ID_AMD ||
+ dev->vendor == PCI_VENDOR_ID_HYGON) &&
sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) {
return efch_mmio;
diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c
index 4e689b6ff141..aacf04616fef 100644
--- a/drivers/watchdog/sprd_wdt.c
+++ b/drivers/watchdog/sprd_wdt.c
@@ -320,10 +320,9 @@ static int sprd_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(&wdt->wdd, 0, dev);
ret = devm_watchdog_register_device(dev, &wdt->wdd);
- if (ret) {
- sprd_wdt_disable(wdt);
+ if (ret)
return ret;
- }
+
platform_set_drvdata(pdev, wdt);
return 0;
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 5b55ccae06d4..2d0bb5e4c9c2 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -381,6 +381,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd)
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
unregister_reboot_notifier(&wdd->reboot_nb);
+ if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status))
+ unregister_pm_notifier(&wdd->pm_nb);
+
watchdog_dev_unregister(wdd);
ida_simple_remove(&watchdog_ida, wdd->id);
}
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1f2160765618..16af955c5ea5 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -678,11 +678,15 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv,
mutex_lock(&priv->lock);
gntdev_add_map(priv, map);
op.index = map->index << PAGE_SHIFT;
- mutex_unlock(&priv->lock);
- if (copy_to_user(u, &op, sizeof(op)) != 0)
+ if (copy_to_user(u, &op, sizeof(op)) != 0) {
+ list_del(&map->next);
+ mutex_unlock(&priv->lock);
+ gntdev_put_map(priv, map);
return -EFAULT;
+ }
+ mutex_unlock(&priv->lock);
return 0;
}
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index 3fbea676b377..18c0df0b1f5a 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -612,6 +612,25 @@ static void scsiback_disconnect(struct vscsibk_info *info)
xenbus_unmap_ring_vfree(info->dev, info->ring.sring);
}
+/*
+ * Send the error response for a request that did not reach the target core
+ * and return its tag. Free the tag before the response drops the v2p
+ * reference that keeps the session alive, and snapshot what the response
+ * needs since returning the tag can let the slot be reused.
+ */
+static void scsiback_resp_and_free(struct vscsibk_pend *pending_req,
+ int32_t result)
+{
+ struct vscsibk_info *info = pending_req->info;
+ struct v2p_entry *v2p = pending_req->v2p;
+ struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess;
+ u16 rqid = pending_req->rqid;
+
+ target_free_tag(se_sess, &pending_req->se_cmd);
+ scsiback_send_response(info, NULL, result, 0, rqid);
+ kref_put(&v2p->kref, scsiback_free_translation_entry);
+}
+
static void scsiback_device_action(struct vscsibk_pend *pending_req,
enum tcm_tmreq_table act, int tag)
{
@@ -640,7 +659,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req,
return;
err:
- scsiback_do_resp_with_sense(NULL, err, 0, pending_req);
+ scsiback_resp_and_free(pending_req, err);
}
/*
@@ -793,9 +812,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
case VSCSIIF_ACT_SCSI_CDB:
if (scsiback_gnttab_data_map(&ring_req, pending_req)) {
scsiback_fast_flush_area(pending_req);
- scsiback_do_resp_with_sense(NULL,
- DID_ERROR << 16, 0, pending_req);
- transport_generic_free_cmd(&pending_req->se_cmd, 0);
+ scsiback_resp_and_free(pending_req,
+ DID_ERROR << 16);
} else {
scsiback_cmd_exec(pending_req);
}
@@ -809,9 +827,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info,
break;
default:
pr_err_ratelimited("invalid request\n");
- scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0,
- pending_req);
- transport_generic_free_cmd(&pending_req->se_cmd, 0);
+ scsiback_resp_and_free(pending_req, DID_ERROR << 16);
break;
}
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index e084caf467d1..3fbfd6aa1a36 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -521,10 +521,19 @@ static int v9fs_at_to_dotl_flags(int flags)
* - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more
* than EXT4_LINK_MAX (65000) links.
*
+ * In cacheless mode the server is the source of truth for nlink and the
+ * inode is going away immediately, so locally adjusting i_nlink buys
+ * nothing and races with concurrent metadata fetches that may already
+ * have observed the post-unlink value (nlink == 0).
+ *
* @inode: inode whose nlink is being dropped
*/
static void v9fs_dec_count(struct inode *inode)
{
+ struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
+
+ if (!(v9ses->cache & (CACHE_META | CACHE_LOOSE)))
+ return;
if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2)
drop_nlink(inode);
}
diff --git a/fs/afs/cell.c b/fs/afs/cell.c
index 7c0dce8eecad..93f695936fef 100644
--- a/fs/afs/cell.c
+++ b/fs/afs/cell.c
@@ -474,6 +474,8 @@ static int afs_update_cell(struct afs_cell *cell)
rcu_assign_pointer(cell->vl_servers, vllist);
cell->dns_source = vllist->source;
old = p;
+ } else {
+ old = vllist;
}
write_unlock(&cell->vl_servers_lock);
afs_put_vlserverlist(cell->net, old);
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
index d4ddb20d6732..69877bf9a42e 100644
--- a/fs/afs/cmservice.c
+++ b/fs/afs/cmservice.c
@@ -390,7 +390,6 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
ret = afs_extract_data(call, false);
switch (ret) {
case 0: break;
- case -EAGAIN: return 0;
default: return ret;
}
@@ -503,7 +502,6 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
ret = afs_extract_data(call, false);
switch (ret) {
case 0: break;
- case -EAGAIN: return 0;
default: return ret;
}
diff --git a/fs/afs/fs_operation.c b/fs/afs/fs_operation.c
index 7a3803ce3a22..4316cce855cd 100644
--- a/fs/afs/fs_operation.c
+++ b/fs/afs/fs_operation.c
@@ -240,7 +240,7 @@ int afs_put_operation(struct afs_operation *op)
for (i = 0; i < op->nr_files - 2; i++)
if (op->more_files[i].put_vnode)
iput(&op->more_files[i].vnode->netfs.inode);
- kfree(op->more_files);
+ kvfree(op->more_files);
}
afs_end_cursor(&op->ac);
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 43154c70366a..3493d82a10ff 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -109,8 +109,14 @@ void afs_close_socket(struct afs_net *net)
{
_enter("");
+ cancel_work_sync(&net->charge_preallocation_work);
+ /* Future work items should now see ->live is false. */
+
kernel_listen(net->socket, 0);
+
+ /* Make sure work items are no longer running. */
flush_workqueue(afs_async_calls);
+ cancel_work_sync(&net->charge_preallocation_work);
if (net->spare_incoming_call) {
afs_put_call(net->spare_incoming_call);
@@ -727,7 +733,7 @@ void afs_charge_preallocation(struct work_struct *work)
container_of(work, struct afs_net, charge_preallocation_work);
struct afs_call *call = net->spare_incoming_call;
- for (;;) {
+ while (READ_ONCE(net->live)) {
if (!call) {
call = afs_alloc_call(net, &afs_RXCMxxxx, GFP_KERNEL);
if (!call)
@@ -772,7 +778,8 @@ static void afs_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall,
{
struct afs_net *net = afs_sock2net(sk);
- queue_work(afs_wq, &net->charge_preallocation_work);
+ if (net->live)
+ queue_work(afs_wq, &net->charge_preallocation_work);
}
/*
diff --git a/fs/afs/super.c b/fs/afs/super.c
index e95fb4cb4fcd..1dd6c7c9713b 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -597,7 +597,8 @@ static int afs_get_tree(struct fs_context *fc)
}
fc->root = dget(sb->s_root);
- trace_afs_get_tree(as->cell, as->volume);
+ if (!ctx->dyn_root)
+ trace_afs_get_tree(as->cell, as->volume);
_leave(" = 0 [%p]", sb);
return 0;
diff --git a/fs/afs/vl_list.c b/fs/afs/vl_list.c
index acc48216136a..2761ac3ab007 100644
--- a/fs/afs/vl_list.c
+++ b/fs/afs/vl_list.c
@@ -88,7 +88,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end,
{
struct afs_addr_list *alist;
const u8 *b = *_b;
- int ret = -EINVAL;
+ int ret;
alist = afs_alloc_addrlist(nr_addrs, VL_SERVICE, port);
if (!alist)
@@ -106,6 +106,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end,
case DNS_ADDRESS_IS_IPV4:
if (end - b < 4) {
_leave(" = -EINVAL [short inet]");
+ ret = -EINVAL;
goto error;
}
memcpy(x, b, 4);
@@ -116,6 +117,7 @@ static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end,
case DNS_ADDRESS_IS_IPV6:
if (end - b < 16) {
_leave(" = -EINVAL [short inet6]");
+ ret = -EINVAL;
goto error;
}
memcpy(x, b, 16);
@@ -190,6 +192,8 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell,
b += sizeof(*hdr);
while (end - b >= sizeof(bs)) {
+ int nlen;
+
bs.name_len = afs_extract_le16(&b);
bs.priority = afs_extract_le16(&b);
bs.weight = afs_extract_le16(&b);
@@ -199,10 +203,12 @@ struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell,
bs.protocol = *b++;
bs.nr_addrs = *b++;
+ nlen = min3(bs.name_len, end - b, 255);
+
_debug("extract %u %u %u %u %u %u %*.*s",
bs.name_len, bs.priority, bs.weight,
bs.port, bs.protocol, bs.nr_addrs,
- bs.name_len, bs.name_len, b);
+ bs.name_len, nlen, b);
if (end - b < bs.name_len)
break;
diff --git a/fs/afs/volume.c b/fs/afs/volume.c
index 0f64b9758127..3f2e088fb27d 100644
--- a/fs/afs/volume.c
+++ b/fs/afs/volume.c
@@ -37,7 +37,7 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
goto found;
}
- set_bit(AFS_VOLUME_RM_TREE, &volume->flags);
+ set_bit(AFS_VOLUME_RM_TREE, &p->flags);
rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes);
}
}
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index a2e471d51a8f..479023e9d74a 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -441,6 +441,8 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, long nr,
int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
unsigned int extra_bits,
struct extent_state **cached_state);
+int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
+ unsigned int extra_bits, struct extent_state **cached_state);
struct btrfs_new_inode_args {
/* Input */
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 04ea2b2a9383..72d32a254cdb 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2061,7 +2061,8 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
locked_ref = btrfs_obtain_ref_head(trans);
if (IS_ERR_OR_NULL(locked_ref)) {
if (PTR_ERR(locked_ref) == -EAGAIN) {
- continue;
+ count++;
+ goto again;
} else {
break;
}
@@ -2109,7 +2110,7 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
* Either success case or btrfs_run_delayed_refs_for_head
* returned -EAGAIN, meaning we need to select another head
*/
-
+again:
locked_ref = NULL;
cond_resched();
} while ((nr != -1 && count < nr) || locked_ref);
@@ -6053,12 +6054,16 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed)
*trimmed = 0;
- /* Discard not supported = nothing to do. */
- if (!bdev_max_discard_sectors(device->bdev))
+ /*
+ * The caller only filters out MISSING devices, but a device that was
+ * missing at mount and later rescanned has MISSING cleared while bdev
+ * is still NULL and WRITEABLE is still unset. Skip those here.
+ */
+ if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) || !device->bdev)
return 0;
- /* Not writable = nothing to do. */
- if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
+ /* Discard not supported = nothing to do. */
+ if (!bdev_max_discard_sectors(device->bdev))
return 0;
/* No free space = nothing to do. */
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 8e7108c14de2..a188467a9fb3 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -151,16 +151,8 @@ int btrfs_dirty_pages(struct btrfs_inode *inode, struct page **pages,
end_of_last_block = start_pos + num_bytes - 1;
- /*
- * The pages may have already been dirty, clear out old accounting so
- * we can set things up properly
- */
- clear_extent_bit(&inode->io_tree, start_pos, end_of_last_block,
- EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
- cached);
-
- err = btrfs_set_extent_delalloc(inode, start_pos, end_of_last_block,
- extra_bits, cached);
+ err = btrfs_reset_extent_delalloc(inode, start_pos, end_of_last_block,
+ extra_bits, cached);
if (err)
return err;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 7add1a019d85..b1e2b3f732cc 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -80,10 +80,12 @@ struct btrfs_iget_args {
struct btrfs_dio_data {
ssize_t submitted;
+ loff_t old_isize;
struct extent_changeset *data_reserved;
struct btrfs_ordered_extent *ordered;
bool data_space_reserved;
bool nocow_done;
+ bool updated_isize;
};
struct btrfs_dio_private {
@@ -2666,7 +2668,11 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
unsigned int extra_bits,
struct extent_state **cached_state)
{
- WARN_ON(PAGE_ALIGNED(end));
+ const u32 blocksize = inode->root->fs_info->sectorsize;
+
+ /* Basic alignment check. */
+ ASSERT(IS_ALIGNED(start, blocksize));
+ ASSERT(IS_ALIGNED(end + 1, blocksize));
if (start >= i_size_read(&inode->vfs_inode) &&
!(inode->flags & BTRFS_INODE_PREALLOC)) {
@@ -2872,6 +2878,50 @@ int btrfs_writepage_cow_fixup(struct page *page)
return -EAGAIN;
}
+/*
+ * Clear the old accounting flags and set EXTENT_DELALLOC for the range.
+ *
+ * Return <0 for error, in that case no range has EXTENT_DELALLOC bit cleared or set.
+ */
+int btrfs_reset_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
+ unsigned int extra_bits, struct extent_state **cached_state)
+{
+ const u32 blocksize = inode->root->fs_info->sectorsize;
+
+ /* The @extra_bits can only be EXTENT_NORESERVE for now. */
+ ASSERT(!(extra_bits & ~EXTENT_NORESERVE));
+
+ /* Basic alignment check. */
+ ASSERT(IS_ALIGNED(start, blocksize));
+ ASSERT(IS_ALIGNED(end + 1, blocksize));
+
+ /*
+ * Check and set DELALLOC_NEW flag, this needs to search tree thus can
+ * fail early. Thus we want to do this before clearing EXTENT_DELALLOC.
+ */
+ if (start >= i_size_read(&inode->vfs_inode) &&
+ !(inode->flags & BTRFS_INODE_PREALLOC)) {
+ /*
+ * There can't be any extents following EOF in this case so just
+ * set the delalloc new bit for the range directly.
+ */
+ extra_bits |= EXTENT_DELALLOC_NEW;
+ } else {
+ int ret;
+
+ ret = btrfs_find_new_delalloc_bytes(inode, start, end + 1 - start,
+ NULL);
+ if (unlikely(ret))
+ return ret;
+ }
+ /* Clear the old accounting as the range may already be dirty. */
+ clear_extent_bit(&inode->io_tree, start, end,
+ EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
+ EXTENT_DEFRAG, cached_state);
+ return set_extent_bit(&inode->io_tree, start, end,
+ EXTENT_DELALLOC | extra_bits, cached_state);
+}
+
static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode, u64 file_pos,
struct btrfs_file_extent_item *stack_fi,
@@ -4799,12 +4849,7 @@ int btrfs_truncate_block(struct btrfs_inode *inode, loff_t from, loff_t len,
goto again;
}
- clear_extent_bit(&inode->io_tree, block_start, block_end,
- EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
- &cached_state);
-
- ret = btrfs_set_extent_delalloc(inode, block_start, block_end, 0,
- &cached_state);
+ ret = btrfs_reset_extent_delalloc(inode, block_start, block_end, 0, &cached_state);
if (ret) {
unlock_extent(io_tree, block_start, block_end, &cached_state);
goto out_unlock;
@@ -7362,6 +7407,7 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
bool space_reserved = false;
u64 len = *lenp;
u64 prev_len;
+ loff_t old_isize;
int ret = 0;
/*
@@ -7477,8 +7523,14 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map,
* Need to update the i_size under the extent lock so buffered
* readers will get the updated i_size when we unlock.
*/
- if (start + len > i_size_read(inode))
+ old_isize = i_size_read(inode);
+ if (start + len > old_isize) {
+ if (!dio_data->updated_isize) {
+ dio_data->old_isize = old_isize;
+ dio_data->updated_isize = true;
+ }
i_size_write(inode, start + len);
+ }
out:
if (ret && space_reserved) {
btrfs_delalloc_release_extents(BTRFS_I(inode), len);
@@ -7754,12 +7806,55 @@ static int btrfs_dio_iomap_end(struct inode *inode, loff_t pos, loff_t length,
if (submitted < length) {
pos += submitted;
length -= submitted;
- if (write)
+ if (write) {
+ /*
+ * Got a short write and have updated the isize, need to
+ * revert the isize change.
+ *
+ * Normally we need to update isize with extent lock hold,
+ * but we're safe due to the following factors:
+ *
+ * - Only a single writer can be enlarging isize
+ * Enlarging isize will take the exclusive inode lock.
+ *
+ * - Buffered readers need to wait for the OE we're holding
+ * Buffered readers will lock extent and wait for OE
+ * of the folio range, and since page cache is invalidated
+ * the OE wait can not be skipped.
+ *
+ * So here we are safe to revert the isize before
+ * finishing the OE, and no reader of the remaining range
+ * can see the enlarged size.
+ *
+ * TODO: Extend the DIO_LOCKED lifespan for direct writes,
+ * and only enlarge isize after a successful write.
+ */
+ if (dio_data->updated_isize) {
+ u64 new_isize;
+
+ if (submitted == 0)
+ new_isize = dio_data->old_isize;
+ else
+ new_isize = max(dio_data->old_isize, pos);
+ i_size_write(inode, new_isize);
+ dio_data->updated_isize = false;
+ }
+ /*
+ * We have a short write, if there is any range
+ * that is submitted properly, that part will have
+ * its own OE split from the original one.
+ *
+ * So for the OE at dio_data->ordered, it's the part
+ * that is not submitted, and should be marked
+ * as fully truncated.
+ */
+ btrfs_mark_ordered_extent_truncated(dio_data->ordered, 0);
btrfs_finish_ordered_extent(dio_data->ordered, NULL,
- pos, length, false);
- else
+ pos, length, true);
+ } else {
unlock_extent(&BTRFS_I(inode)->io_tree, pos,
pos + length - 1, NULL);
+ }
ret = -ENOTBLK;
}
if (write) {
@@ -8109,11 +8204,7 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
EXTENT_DEFRAG, &cached_state);
- spin_lock_irq(&inode->ordered_tree.lock);
- set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
- ordered->truncated_len = min(ordered->truncated_len,
- cur - ordered->file_offset);
- spin_unlock_irq(&inode->ordered_tree.lock);
+ btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset);
/*
* If the ordered extent has finished, we're safe to delete all
@@ -8278,19 +8369,8 @@ vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
}
}
- /*
- * page_mkwrite gets called when the page is firstly dirtied after it's
- * faulted in, but write(2) could also dirty a page and set delalloc
- * bits, thus in this case for space account reason, we still need to
- * clear any delalloc bits within this page range since we have to
- * reserve data&meta space before lock_page() (see above comments).
- */
- clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start, end,
- EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING |
- EXTENT_DEFRAG, &cached_state);
-
- ret2 = btrfs_set_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
- &cached_state);
+ ret2 = btrfs_reset_extent_delalloc(BTRFS_I(inode), page_start, end, 0,
+ &cached_state);
if (ret2) {
unlock_extent(io_tree, page_start, page_end, &cached_state);
ret = VM_FAULT_SIGBUS;
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index e0a2d0cfd5eb..ff37971ef897 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -303,6 +303,18 @@ void btrfs_add_ordered_sum(struct btrfs_ordered_extent *entry,
spin_unlock_irq(&tree->lock);
}
+void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
+ u64 truncate_len)
+{
+ struct btrfs_inode *inode = BTRFS_I(ordered->inode);
+
+ ASSERT(truncate_len <= ordered->num_bytes);
+ spin_lock_irq(&inode->ordered_tree.lock);
+ set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
+ ordered->truncated_len = min(ordered->truncated_len, truncate_len);
+ spin_unlock_irq(&inode->ordered_tree.lock);
+}
+
static void finish_ordered_fn(struct btrfs_work *work)
{
struct btrfs_ordered_extent *ordered_extent;
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 1c51ac57e5df..ef06ba6117c2 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -210,6 +210,8 @@ bool btrfs_try_lock_ordered_range(struct btrfs_inode *inode, u64 start, u64 end,
struct extent_state **cached_state);
struct btrfs_ordered_extent *btrfs_split_ordered_extent(
struct btrfs_ordered_extent *ordered, u64 len);
+void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
+ u64 truncate_len);
int __init ordered_data_init(void);
void __cold ordered_data_exit(void);
diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c
index 9f60aa79a8c5..35ef65b7c25d 100644
--- a/fs/btrfs/reflink.c
+++ b/fs/btrfs/reflink.c
@@ -95,10 +95,7 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
if (ret < 0)
goto out_unlock;
- clear_extent_bit(&inode->io_tree, file_offset, range_end,
- EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG,
- NULL);
- ret = btrfs_set_extent_delalloc(inode, file_offset, range_end, 0, NULL);
+ ret = btrfs_reset_extent_delalloc(inode, file_offset, range_end, 0, NULL);
if (ret)
goto out_unlock;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 2045ac3d94c4..671f1eb26ec6 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2048,7 +2048,8 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
int mixed = 0;
list_for_each_entry(found, &fs_info->space_info, list) {
- if (found->flags & BTRFS_BLOCK_GROUP_DATA) {
+ if (found->flags & BTRFS_BLOCK_GROUP_DATA &&
+ found->subgroup_id != BTRFS_SUB_GROUP_DATA_RELOC) {
int i;
total_free_data += found->disk_total - found->disk_used;
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 2df99cd1034d..972fb89ad221 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -225,15 +225,16 @@ static int configfs_dirent_exists(struct configfs_dirent *parent_sd,
{
struct configfs_dirent * sd;
+ spin_lock(&configfs_dirent_lock);
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
if (sd->s_element) {
- const unsigned char *existing = configfs_get_name(sd);
- if (strcmp(existing, new))
- continue;
- else
+ if (strcmp(configfs_get_name(sd), new) == 0) {
+ spin_unlock(&configfs_dirent_lock);
return -EEXIST;
+ }
}
}
+ spin_unlock(&configfs_dirent_lock);
return 0;
}
@@ -462,6 +463,9 @@ static struct dentry * configfs_lookup(struct inode *dir,
inode = configfs_create(dentry, mode);
if (IS_ERR(inode)) {
+ spin_lock(&configfs_dirent_lock);
+ sd->s_dentry = NULL;
+ spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
return ERR_CAST(inode);
}
@@ -548,11 +552,28 @@ static void configfs_detach_rollback(struct dentry *dentry)
configfs_detach_rollback(sd->s_dentry);
}
+/*
+ * Find the next non-cursor. configfs_dirent_lock held by caller.
+ */
+static struct configfs_dirent *next_dirent(struct configfs_dirent *parent,
+ struct configfs_dirent *last)
+{
+ struct configfs_dirent *s;
+
+ s = list_prepare_entry(last, &parent->s_children, s_sibling);
+
+ list_for_each_entry_continue(s, &parent->s_children, s_sibling) {
+ if (s->s_element)
+ return s;
+ }
+ return NULL;
+}
+
static void detach_attrs(struct config_item * item)
{
struct dentry * dentry = dget(item->ci_dentry);
- struct configfs_dirent * parent_sd;
- struct configfs_dirent * sd, * tmp;
+ struct configfs_dirent *parent_sd;
+ struct configfs_dirent *sd, *next;
if (!dentry)
return;
@@ -561,15 +582,19 @@ static void detach_attrs(struct config_item * item)
dentry->d_name.name);
parent_sd = dentry->d_fsdata;
- list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
- if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED))
+
+ spin_lock(&configfs_dirent_lock);
+ for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
+ next = next_dirent(parent_sd, sd);
+ if (!(sd->s_type & CONFIGFS_NOT_PINNED))
continue;
- spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
configfs_drop_dentry(sd, dentry);
configfs_put(sd);
+ spin_lock(&configfs_dirent_lock);
}
+ spin_unlock(&configfs_dirent_lock);
/**
* Drop reference from dget() on entrance.
@@ -618,18 +643,20 @@ static void detach_groups(struct config_group *group)
struct dentry * dentry = dget(group->cg_item.ci_dentry);
struct dentry *child;
struct configfs_dirent *parent_sd;
- struct configfs_dirent *sd, *tmp;
+ struct configfs_dirent *sd, *next;
if (!dentry)
return;
parent_sd = dentry->d_fsdata;
- list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
- if (!sd->s_element ||
- !(sd->s_type & CONFIGFS_USET_DEFAULT))
+ spin_lock(&configfs_dirent_lock);
+ for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
+ next = next_dirent(parent_sd, sd);
+ if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
continue;
child = sd->s_dentry;
+ spin_unlock(&configfs_dirent_lock);
inode_lock(d_inode(child));
@@ -641,7 +668,9 @@ static void detach_groups(struct config_group *group)
d_delete(child);
dput(child);
+ spin_lock(&configfs_dirent_lock);
}
+ spin_unlock(&configfs_dirent_lock);
/**
* Drop reference from dget() on entrance.
@@ -1093,6 +1122,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
struct configfs_dirent *p;
struct configfs_dirent *ret = NULL;
+ spin_lock(&configfs_dirent_lock);
list_for_each_entry(p, &root_sd->s_children, s_sibling) {
if (p->s_type & CONFIGFS_DIR &&
p->s_element == subsys_item) {
@@ -1100,6 +1130,7 @@ configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
break;
}
}
+ spin_unlock(&configfs_dirent_lock);
return ret;
}
diff --git a/fs/dlm/midcomms.c b/fs/dlm/midcomms.c
index 2247ebb61be1..6a6ccf2f8db5 100644
--- a/fs/dlm/midcomms.c
+++ b/fs/dlm/midcomms.c
@@ -970,10 +970,10 @@ static void midcomms_new_msg_cb(void *data)
atomic_inc(&mh->node->send_queue_cnt);
spin_lock_bh(&mh->node->send_queue_lock);
+ /* need to be locked with list_add_tail_rcu() because list is ordered */
+ mh->seq = atomic_fetch_inc(&mh->node->seq_send);
list_add_tail_rcu(&mh->list, &mh->node->send_queue);
spin_unlock_bh(&mh->node->send_queue_lock);
-
- mh->seq = atomic_fetch_inc(&mh->node->seq_send);
}
static struct dlm_msg *dlm_midcomms_get_msg_3_2(struct dlm_mhandle *mh, int nodeid,
diff --git a/fs/efs/file.c b/fs/efs/file.c
index 9e641da6fab2..9153dfe79bbc 100644
--- a/fs/efs/file.c
+++ b/fs/efs/file.c
@@ -18,16 +18,9 @@ int efs_get_block(struct inode *inode, sector_t iblock,
if (create)
return error;
- if (iblock >= inode->i_blocks) {
-#ifdef DEBUG
- /*
- * i have no idea why this happens as often as it does
- */
- pr_warn("%s(): block %d >= %ld (filesize %ld)\n",
- __func__, block, inode->i_blocks, inode->i_size);
-#endif
+ if (iblock >= inode->i_blocks)
return 0;
- }
+
phys = efs_map_block(inode, iblock);
if (phys)
map_bh(bh_result, inode->i_sb, phys);
@@ -42,16 +35,8 @@ int efs_bmap(struct inode *inode, efs_block_t block) {
}
/* are we about to read past the end of a file ? */
- if (!(block < inode->i_blocks)) {
-#ifdef DEBUG
- /*
- * i have no idea why this happens as often as it does
- */
- pr_warn("%s(): block %d >= %ld (filesize %ld)\n",
- __func__, block, inode->i_blocks, inode->i_size);
-#endif
+ if (!(block < inode->i_blocks))
return 0;
- }
return efs_map_block(inode, block);
}
diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index 99625a2ad33b..d39324b00ecb 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -552,21 +552,43 @@ static void exfat_free_benign_secondary_clusters(struct inode *inode,
exfat_free_cluster(inode, &dir);
}
+/*
+ * exfat_init_ext_entry - initialize extension entries of a dentry set
+ * @inode: parent directory inode
+ * @p_dir: directory the dentry set is in
+ * @entry: index of the first entry of the dentry set
+ * @num_entries: number of entries excluding benign secondary entries
+ * @p_uniname: filename to store
+ * @old_es: optional source entry set with benign secondary entries, or NULL
+ * @num_extra: number of benign secondary entries to copy from @old_es
+ *
+ * Set up the file, stream extension, and filename entries, optionally
+ * preserving @num_extra benign secondary entries from @old_es. @old_es
+ * may refer to the same dentry set that is being initialized.
+ */
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int num_entries, struct exfat_uni_name *p_uniname)
+ int entry, int num_entries, struct exfat_uni_name *p_uniname,
+ struct exfat_entry_set_cache *old_es, int num_extra)
{
struct super_block *sb = inode->i_sb;
- int i;
+ int i, src_start = 0;
unsigned short *uniname = p_uniname->name;
struct exfat_dentry *ep;
struct buffer_head *bh;
int sync = IS_DIRSYNC(inode);
+ if (WARN_ON(num_extra < 0 || (num_extra && (!old_es ||
+ old_es->num_entries < ES_IDX_FIRST_FILENAME + num_extra))))
+ num_extra = 0;
+
+ if (old_es && num_extra > 0)
+ src_start = old_es->num_entries - num_extra;
+
ep = exfat_get_dentry(sb, p_dir, entry, &bh);
if (!ep)
return -EIO;
- ep->dentry.file.num_ext = (unsigned char)(num_entries - 1);
+ ep->dentry.file.num_ext = (unsigned char)(num_entries - 1 + num_extra);
exfat_update_bh(bh, sync);
brelse(bh);
@@ -579,14 +601,24 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
exfat_update_bh(bh, sync);
brelse(bh);
+ if (old_es && num_extra > 0) {
+ for (i = 0; i < num_extra; i++) {
+ ep = exfat_get_dentry(sb, p_dir,
+ entry + num_entries + i, &bh);
+ if (!ep)
+ return -EIO;
+
+ *ep = *exfat_get_dentry_cached(old_es, src_start + i);
+ exfat_update_bh(bh, sync);
+ brelse(bh);
+ }
+ }
+
for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) {
ep = exfat_get_dentry(sb, p_dir, entry + i, &bh);
if (!ep)
return -EIO;
- if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
- exfat_free_benign_secondary_clusters(inode, ep);
-
exfat_init_name_entry(ep, uniname);
exfat_update_bh(bh, sync);
brelse(bh);
@@ -598,7 +630,7 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
}
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int order, int num_entries)
+ int entry, int order, int num_entries, bool free_benign)
{
struct super_block *sb = inode->i_sb;
int i;
@@ -610,7 +642,7 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
if (!ep)
return -EIO;
- if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
+ if (free_benign && (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC))
exfat_free_benign_secondary_clusters(inode, ep);
exfat_set_entry_type(ep, TYPE_DELETED);
@@ -1081,6 +1113,7 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
if (entry_type == TYPE_EXTEND) {
unsigned short entry_uniname[16], unichar;
+ unsigned int offset;
if (step != DIRENT_STEP_NAME ||
name_len >= MAX_NAME_LENGTH) {
@@ -1089,13 +1122,15 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
continue;
}
- if (++order == 2)
- uniname = p_uniname->name;
- else
- uniname += EXFAT_FILE_NAME_LEN;
-
+ offset = (++order - 2) * EXFAT_FILE_NAME_LEN;
len = exfat_extract_uni_name(ep, entry_uniname);
brelse(bh);
+ if (offset > MAX_NAME_LENGTH ||
+ len > MAX_NAME_LENGTH - offset) {
+ step = DIRENT_STEP_FILE;
+ continue;
+ }
+ uniname = p_uniname->name + offset;
name_len += len;
unichar = *(uniname+len);
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index 19245ddd885f..96b1faa25aaa 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -480,9 +480,10 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
int entry, unsigned int type, unsigned int start_clu,
unsigned long long size);
int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int num_entries, struct exfat_uni_name *p_uniname);
+ int entry, int num_entries, struct exfat_uni_name *p_uniname,
+ struct exfat_entry_set_cache *old_es, int num_extra);
int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
- int entry, int order, int num_entries);
+ int entry, int order, int num_entries, bool free_benign);
int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
int entry);
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
@@ -498,6 +499,8 @@ struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
int exfat_get_dentry_set(struct exfat_entry_set_cache *es,
struct super_block *sb, struct exfat_chain *p_dir, int entry,
unsigned int type);
+#define exfat_get_dentry_set_by_ei(es, sb, ei) \
+ exfat_get_dentry_set(es, sb, &(ei)->dir, (ei)->entry, ES_ALL_ENTRIES)
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 13329baeafbc..81019484296d 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -42,7 +42,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
exfat_set_volume_dirty(sb);
/* get the directory entry of given file or directory */
- if (exfat_get_dentry_set(&es, sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES))
+ if (exfat_get_dentry_set_by_ei(&es, sb, ei))
return -EIO;
ep = exfat_get_dentry_cached(&es, ES_IDX_FILE);
ep2 = exfat_get_dentry_cached(&es, ES_IDX_STREAM);
diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index 4657f893dea7..83a146d3c684 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -343,6 +343,9 @@ static int exfat_find_empty_entry(struct inode *inode,
ei->hint_femp.eidx = EXFAT_HINT_NONE;
}
+ exfat_chain_set(p_dir, ei->start_clu,
+ EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
+
while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir,
num_entries)) < 0) {
if (dentry == -EIO)
@@ -419,14 +422,11 @@ static int exfat_find_empty_entry(struct inode *inode,
* Zero if it was successful; otherwise nonzero.
*/
static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
- struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
- int lookup)
+ struct exfat_uni_name *p_uniname, int lookup)
{
int namelen;
int lossy = NLS_NAME_NO_LOSSY;
struct super_block *sb = inode->i_sb;
- struct exfat_sb_info *sbi = EXFAT_SB(sb);
- struct exfat_inode_info *ei = EXFAT_I(inode);
int pathlen = strlen(path);
/*
@@ -465,24 +465,19 @@ static int __exfat_resolve_path(struct inode *inode, const unsigned char *path,
if ((lossy && !lookup) || !namelen)
return (lossy & NLS_NAME_OVERLEN) ? -ENAMETOOLONG : -EINVAL;
- exfat_chain_set(p_dir, ei->start_clu,
- EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags);
-
return 0;
}
static inline int exfat_resolve_path(struct inode *inode,
- const unsigned char *path, struct exfat_chain *dir,
- struct exfat_uni_name *uni)
+ const unsigned char *path, struct exfat_uni_name *uni)
{
- return __exfat_resolve_path(inode, path, dir, uni, 0);
+ return __exfat_resolve_path(inode, path, uni, 0);
}
static inline int exfat_resolve_path_for_lookup(struct inode *inode,
- const unsigned char *path, struct exfat_chain *dir,
- struct exfat_uni_name *uni)
+ const unsigned char *path, struct exfat_uni_name *uni)
{
- return __exfat_resolve_path(inode, path, dir, uni, 1);
+ return __exfat_resolve_path(inode, path, uni, 1);
}
static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info)
@@ -502,7 +497,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
int clu_size = 0;
unsigned int start_clu = EXFAT_FREE_CLUSTER;
- ret = exfat_resolve_path(inode, path, p_dir, &uniname);
+ ret = exfat_resolve_path(inode, path, &uniname);
if (ret)
goto out;
@@ -536,7 +531,8 @@ static int exfat_add_entry(struct inode *inode, const char *path,
if (ret)
goto out;
- ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname);
+ ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname,
+ NULL, 0);
if (ret)
goto out;
@@ -623,10 +619,13 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
return -ENOENT;
/* check the validity of directory name in the given pathname */
- ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name);
+ ret = exfat_resolve_path_for_lookup(dir, qname->name, &uni_name);
if (ret)
return ret;
+ exfat_chain_set(&cdir, ei->start_clu,
+ EXFAT_B_TO_CLU(i_size_read(dir), sbi), ei->flags);
+
/* check the validation of hint_stat and initialize it if required */
if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) {
ei->hint_stat.clu = cdir.dir;
@@ -786,29 +785,26 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
/* remove an entry, BUT don't truncate */
static int exfat_unlink(struct inode *dir, struct dentry *dentry)
{
- struct exfat_chain cdir;
struct exfat_dentry *ep;
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
- int num_entries, entry, err = 0;
+ int num_entries, err = 0;
mutex_lock(&EXFAT_SB(sb)->s_lock);
- exfat_chain_dup(&cdir, &ei->dir);
- entry = ei->entry;
if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
goto unlock;
}
- ep = exfat_get_dentry(sb, &cdir, entry, &bh);
+ ep = exfat_get_dentry(sb, &ei->dir, ei->entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
}
- num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
@@ -819,7 +815,8 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
exfat_set_volume_dirty(sb);
/* update the directory entry */
- if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
+ if (exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries,
+ true)) {
err = -EIO;
goto unlock;
}
@@ -944,18 +941,15 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct exfat_dentry *ep;
- struct exfat_chain cdir, clu_to_free;
+ struct exfat_chain clu_to_free;
struct super_block *sb = inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
struct exfat_inode_info *ei = EXFAT_I(inode);
struct buffer_head *bh;
- int num_entries, entry, err;
+ int num_entries, err;
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
- exfat_chain_dup(&cdir, &ei->dir);
- entry = ei->entry;
-
if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
@@ -973,13 +967,13 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
goto unlock;
}
- ep = exfat_get_dentry(sb, &cdir, entry, &bh);
+ ep = exfat_get_dentry(sb, &ei->dir, ei->entry, &bh);
if (!ep) {
err = -EIO;
goto unlock;
}
- num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &ei->dir, ei->entry, ep);
if (num_entries < 0) {
err = -EIO;
brelse(bh);
@@ -989,7 +983,8 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
brelse(bh);
exfat_set_volume_dirty(sb);
- err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
+ err = exfat_remove_entries(dir, &ei->dir, ei->entry, 0, num_entries,
+ true);
if (err) {
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
goto unlock;
@@ -1015,40 +1010,67 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
return err;
}
-static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
- int oldentry, struct exfat_uni_name *p_uniname,
- struct exfat_inode_info *ei)
+/*
+ * Count benign secondary entries beyond the filename entries.
+ * Returns the count, or -EIO if the entry set is inconsistent.
+ */
+static int exfat_count_extra_entries(struct exfat_entry_set_cache *es)
+{
+ struct exfat_dentry *stream;
+ unsigned int name_entries;
+ int extra;
+
+ stream = exfat_get_dentry_cached(es, ES_IDX_STREAM);
+ name_entries = EXFAT_FILENAME_ENTRY_NUM(stream->dentry.stream.name_len);
+ extra = es->num_entries - (ES_IDX_FIRST_FILENAME + name_entries);
+
+ return extra >= 0 ? extra : -EIO;
+}
+
+static int exfat_rename_file(struct inode *parent_inode,
+ struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, num_old_entries, num_new_entries;
struct exfat_dentry *epold, *epnew;
- struct super_block *sb = inode->i_sb;
+ struct super_block *sb = parent_inode->i_sb;
+ struct exfat_entry_set_cache old_es;
struct buffer_head *new_bh, *old_bh;
- int sync = IS_DIRSYNC(inode);
-
- epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh);
- if (!epold)
- return -EIO;
-
- num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold);
- if (num_old_entries < 0)
- return -EIO;
- num_old_entries++;
+ int sync = IS_DIRSYNC(parent_inode);
+ unsigned int num_extra_entries, num_total_entries;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
- if (num_old_entries < num_new_entries) {
+ ret = exfat_get_dentry_set_by_ei(&old_es, sb, ei);
+ if (ret)
+ return -EIO;
+
+ num_old_entries = old_es.num_entries;
+
+ ret = exfat_count_extra_entries(&old_es);
+ if (ret < 0)
+ goto put_old_es;
+ num_extra_entries = ret;
+ num_total_entries = num_new_entries + num_extra_entries;
+
+ if (num_old_entries < num_total_entries) {
int newentry;
+ struct exfat_chain dir;
- newentry =
- exfat_find_empty_entry(inode, p_dir, num_new_entries);
- if (newentry < 0)
- return newentry; /* -EIO or -ENOSPC */
+ newentry = exfat_find_empty_entry(parent_inode, &dir,
+ num_total_entries);
+ if (newentry < 0) {
+ ret = newentry; /* -EIO or -ENOSPC */
+ goto put_old_es;
+ }
- epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh);
- if (!epnew)
- return -EIO;
+ epold = exfat_get_dentry_cached(&old_es, ES_IDX_FILE);
+ epnew = exfat_get_dentry(sb, &dir, newentry, &new_bh);
+ if (!epnew) {
+ ret = -EIO;
+ goto put_old_es;
+ }
*epnew = *epold;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
@@ -1056,116 +1078,149 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(new_bh, sync);
- brelse(old_bh);
brelse(new_bh);
- epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh);
- if (!epold)
- return -EIO;
- epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh);
+ epold = exfat_get_dentry_cached(&old_es, ES_IDX_STREAM);
+ epnew = exfat_get_dentry(sb, &dir, newentry + 1, &new_bh);
if (!epnew) {
- brelse(old_bh);
- return -EIO;
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &dir, newentry, 0,
+ num_total_entries, false);
+ ret = -EIO;
+ goto put_old_es;
}
*epnew = *epold;
exfat_update_bh(new_bh, sync);
- brelse(old_bh);
brelse(new_bh);
- ret = exfat_init_ext_entry(inode, p_dir, newentry,
- num_new_entries, p_uniname);
- if (ret)
- return ret;
+ ret = exfat_init_ext_entry(parent_inode, &dir, newentry,
+ num_new_entries, p_uniname, &old_es,
+ num_extra_entries);
+ if (ret) {
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &dir, newentry, 0,
+ num_total_entries, false);
+ goto put_old_es;
+ }
- exfat_remove_entries(inode, p_dir, oldentry, 0,
- num_old_entries);
- ei->dir = *p_dir;
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
+ num_old_entries, false);
+ ei->dir = dir;
ei->entry = newentry;
} else {
+ epold = exfat_get_dentry(sb, &ei->dir, ei->entry, &old_bh);
+ if (!epold) {
+ ret = -EIO;
+ goto put_old_es;
+ }
+
if (exfat_get_entry_type(epold) == TYPE_FILE) {
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
}
exfat_update_bh(old_bh, sync);
brelse(old_bh);
- ret = exfat_init_ext_entry(inode, p_dir, oldentry,
- num_new_entries, p_uniname);
+
+ ret = exfat_init_ext_entry(parent_inode, &ei->dir, ei->entry,
+ num_new_entries, p_uniname, &old_es,
+ num_extra_entries);
if (ret)
- return ret;
+ goto put_old_es;
- exfat_remove_entries(inode, p_dir, oldentry, num_new_entries,
- num_old_entries);
+ /* Mark excess old entries as deleted (in-place shrink) */
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry,
+ num_total_entries, num_old_entries, false);
}
- return 0;
+ ret = 0;
+
+put_old_es:
+ exfat_put_dentry_set(&old_es, false);
+ return ret;
}
-static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
- int oldentry, struct exfat_chain *p_newdir,
+static int exfat_move_file(struct inode *parent_inode,
struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei)
{
int ret, newentry, num_new_entries, num_old_entries;
struct exfat_dentry *epmov, *epnew;
- struct super_block *sb = inode->i_sb;
- struct buffer_head *mov_bh, *new_bh;
-
- epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh);
- if (!epmov)
- return -EIO;
-
- num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry,
- epmov);
- if (num_old_entries < 0)
- return -EIO;
- num_old_entries++;
+ struct super_block *sb = parent_inode->i_sb;
+ struct exfat_entry_set_cache mov_es;
+ struct buffer_head *new_bh;
+ struct exfat_chain newdir;
+ unsigned int num_extra_entries, num_total_entries;
num_new_entries = exfat_calc_num_entries(p_uniname);
if (num_new_entries < 0)
return num_new_entries;
- newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries);
- if (newentry < 0)
- return newentry; /* -EIO or -ENOSPC */
-
- epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh);
- if (!epnew)
+ ret = exfat_get_dentry_set_by_ei(&mov_es, sb, ei);
+ if (ret)
return -EIO;
+ num_old_entries = mov_es.num_entries;
+
+ ret = exfat_count_extra_entries(&mov_es);
+ if (ret < 0)
+ goto put_mov_es;
+ num_extra_entries = ret;
+ num_total_entries = num_new_entries + num_extra_entries;
+
+ newentry = exfat_find_empty_entry(parent_inode, &newdir,
+ num_total_entries);
+ if (newentry < 0) {
+ ret = newentry; /* -EIO or -ENOSPC */
+ goto put_mov_es;
+ }
+
+ epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_FILE);
+ epnew = exfat_get_dentry(sb, &newdir, newentry, &new_bh);
+ if (!epnew) {
+ ret = -EIO;
+ goto put_mov_es;
+ }
+
*epnew = *epmov;
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
ei->attr |= ATTR_ARCHIVE;
}
- exfat_update_bh(new_bh, IS_DIRSYNC(inode));
- brelse(mov_bh);
+ exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
brelse(new_bh);
- epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh);
- if (!epmov)
- return -EIO;
- epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh);
+ epmov = exfat_get_dentry_cached(&mov_es, ES_IDX_STREAM);
+ epnew = exfat_get_dentry(sb, &newdir, newentry + 1, &new_bh);
if (!epnew) {
- brelse(mov_bh);
- return -EIO;
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &newdir, newentry, 0,
+ num_total_entries, false);
+ ret = -EIO;
+ goto put_mov_es;
}
*epnew = *epmov;
- exfat_update_bh(new_bh, IS_DIRSYNC(inode));
- brelse(mov_bh);
+ exfat_update_bh(new_bh, IS_DIRSYNC(parent_inode));
brelse(new_bh);
- ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries,
- p_uniname);
- if (ret)
- return ret;
-
- exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries);
+ ret = exfat_init_ext_entry(parent_inode, &newdir, newentry,
+ num_new_entries, p_uniname, &mov_es, num_extra_entries);
+ if (ret) {
+ /* Best-effort delete to avoid duplicate entries */
+ exfat_remove_entries(parent_inode, &newdir, newentry, 0,
+ num_total_entries, false);
+ goto put_mov_es;
+ }
- exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size,
- p_newdir->flags);
+ exfat_remove_entries(parent_inode, &ei->dir, ei->entry, 0,
+ num_old_entries, false);
+ ei->dir = newdir;
ei->entry = newentry;
- return 0;
+ ret = 0;
+
+put_mov_es:
+ exfat_put_dentry_set(&mov_es, false);
+ return ret;
}
/* rename or move a old file into a new file */
@@ -1174,20 +1229,13 @@ static int __exfat_rename(struct inode *old_parent_inode,
struct dentry *new_dentry)
{
int ret;
- int dentry;
- struct exfat_chain olddir, newdir;
- struct exfat_chain *p_dir = NULL;
struct exfat_uni_name uni_name;
- struct exfat_dentry *ep;
struct super_block *sb = old_parent_inode->i_sb;
struct exfat_sb_info *sbi = EXFAT_SB(sb);
const unsigned char *new_path = new_dentry->d_name.name;
struct inode *new_inode = new_dentry->d_inode;
int num_entries;
struct exfat_inode_info *new_ei = NULL;
- unsigned int new_entry_type = TYPE_UNUSED;
- int new_entry = 0;
- struct buffer_head *new_bh = NULL;
/* check the validity of pointer parameters */
if (new_path == NULL || strlen(new_path) == 0)
@@ -1198,11 +1246,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
return -ENOENT;
}
- exfat_chain_set(&olddir, EXFAT_I(old_parent_inode)->start_clu,
- EXFAT_B_TO_CLU_ROUND_UP(i_size_read(old_parent_inode), sbi),
- EXFAT_I(old_parent_inode)->flags);
- dentry = ei->entry;
-
/* check whether new dir is existing directory and empty */
if (new_inode) {
ret = -EIO;
@@ -1213,17 +1256,8 @@ static int __exfat_rename(struct inode *old_parent_inode,
goto out;
}
- p_dir = &(new_ei->dir);
- new_entry = new_ei->entry;
- ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
- if (!ep)
- goto out;
-
- new_entry_type = exfat_get_entry_type(ep);
- brelse(new_bh);
-
/* if new_inode exists, update ei */
- if (new_entry_type == TYPE_DIR) {
+ if (S_ISDIR(new_inode->i_mode)) {
struct exfat_chain new_clu;
new_clu.dir = new_ei->start_clu;
@@ -1239,43 +1273,44 @@ static int __exfat_rename(struct inode *old_parent_inode,
}
/* check the validity of directory name in the given new pathname */
- ret = exfat_resolve_path(new_parent_inode, new_path, &newdir,
- &uni_name);
+ ret = exfat_resolve_path(new_parent_inode, new_path, &uni_name);
if (ret)
goto out;
exfat_set_volume_dirty(sb);
- if (olddir.dir == newdir.dir)
- ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
- &uni_name, ei);
+ if (new_parent_inode == old_parent_inode)
+ ret = exfat_rename_file(new_parent_inode, &uni_name, ei);
else
- ret = exfat_move_file(new_parent_inode, &olddir, dentry,
- &newdir, &uni_name, ei);
+ ret = exfat_move_file(new_parent_inode, &uni_name, ei);
if (!ret && new_inode) {
+ struct exfat_dentry *ep;
+ struct buffer_head *new_bh = NULL;
+
/* delete entries of new_dir */
- ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
+ ep = exfat_get_dentry(sb, &new_ei->dir, new_ei->entry, &new_bh);
if (!ep) {
ret = -EIO;
goto del_out;
}
- num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep);
+ num_entries = exfat_count_ext_entries(sb, &new_ei->dir,
+ new_ei->entry, ep);
if (num_entries < 0) {
ret = -EIO;
goto del_out;
}
brelse(new_bh);
- if (exfat_remove_entries(new_inode, p_dir, new_entry, 0,
- num_entries + 1)) {
+ if (exfat_remove_entries(new_inode, &new_ei->dir, new_ei->entry,
+ 0, num_entries + 1, true)) {
ret = -EIO;
goto del_out;
}
/* Free the clusters if new_inode is a dir(as if exfat_rmdir) */
- if (new_entry_type == TYPE_DIR &&
+ if (S_ISDIR(new_inode->i_mode) &&
new_ei->start_clu != EXFAT_EOF_CLUSTER) {
/* new_ei, new_clu_to_free */
struct exfat_chain new_clu_to_free;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 4ddc36f4dbd4..a6913e4eb523 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -262,12 +262,15 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
endbyte = pos + status - 1;
ret2 = filemap_write_and_wait_range(inode->i_mapping, pos,
endbyte);
- if (!ret2)
+ if (!ret2) {
invalidate_mapping_pages(inode->i_mapping,
pos >> PAGE_SHIFT,
endbyte >> PAGE_SHIFT);
- if (ret > 0)
- generic_write_sync(iocb, ret);
+ if (ret > 0)
+ ret = generic_write_sync(iocb, ret);
+ } else {
+ ret = ret2;
+ }
}
out_unlock:
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index a94798e23c1a..9c2d23958146 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -114,7 +114,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
{
int depth, i;
- if (!path)
+ if (IS_ERR_OR_NULL(path))
return;
depth = path->p_depth;
for (i = 0; i <= depth; i++, path++) {
@@ -125,6 +125,8 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
void ext4_free_ext_path(struct ext4_ext_path *path)
{
+ if (IS_ERR_OR_NULL(path))
+ return;
ext4_ext_drop_refs(path);
kfree(path);
}
@@ -4242,7 +4244,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
path = ext4_find_extent(inode, map->m_lblk, NULL, 0);
if (IS_ERR(path)) {
err = PTR_ERR(path);
- path = NULL;
goto out;
}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d72e7f3263ea..6a0afec3542b 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1431,7 +1431,8 @@ static int ext4_journalled_write_end(struct file *file,
BUG_ON(!ext4_handle_valid(handle));
- if (ext4_has_inline_data(inode))
+ if (ext4_has_inline_data(inode) &&
+ ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
return ext4_write_inline_data_end(inode, pos, len, copied,
folio);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index d34affd2075e..e27eae7f163d 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -826,11 +826,17 @@ int ext4_force_shutdown(struct super_block *sb, u32 flags)
thaw_bdev(sb->s_bdev);
break;
case EXT4_GOING_FLAGS_LOGFLUSH:
+ /*
+ * Call ext4_force_commit() before setting EXT4_FLAGS_SHUTDOWN.
+ * This is because in data=ordered mode, journal commit
+ * triggers data writeback which fails if shutdown is already
+ * set, causing the journal to be aborted prematurely before
+ * the commit succeeds.
+ */
+ (void) ext4_force_commit(sb);
set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
- if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) {
- (void) ext4_force_commit(sb);
+ if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
jbd2_journal_abort(sbi->s_journal, -ESHUTDOWN);
- }
break;
case EXT4_GOING_FLAGS_NOLOGFLUSH:
set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index d5636a2a718a..96a84de32169 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -17,27 +17,23 @@
* get_ext_path() - Find an extent path for designated logical block number.
* @inode: inode to be searched
* @lblock: logical block number to find an extent path
- * @ppath: pointer to an extent path pointer (for output)
+ * @path: pointer to an extent path
*
- * ext4_find_extent wrapper. Return 0 on success, or a negative error value
- * on failure.
+ * ext4_find_extent wrapper. Return an extent path pointer on success,
+ * or an error pointer on failure.
*/
-static inline int
+static inline struct ext4_ext_path *
get_ext_path(struct inode *inode, ext4_lblk_t lblock,
- struct ext4_ext_path **ppath)
+ struct ext4_ext_path *path)
{
- struct ext4_ext_path *path = *ppath;
-
- *ppath = NULL;
path = ext4_find_extent(inode, lblock, path, EXT4_EX_NOCACHE);
if (IS_ERR(path))
- return PTR_ERR(path);
+ return path;
if (path[ext_depth(inode)].p_ext == NULL) {
ext4_free_ext_path(path);
- return -ENODATA;
+ return ERR_PTR(-ENODATA);
}
- *ppath = path;
- return 0;
+ return path;
}
/**
@@ -95,9 +91,11 @@ mext_check_coverage(struct inode *inode, ext4_lblk_t from, ext4_lblk_t count,
int ret = 0;
ext4_lblk_t last = from + count;
while (from < last) {
- *err = get_ext_path(inode, from, &path);
- if (*err)
- goto out;
+ path = get_ext_path(inode, from, path);
+ if (IS_ERR(path)) {
+ *err = PTR_ERR(path);
+ return ret;
+ }
ext = path[ext_depth(inode)].p_ext;
if (unwritten != ext4_ext_is_unwritten(ext))
goto out;
@@ -634,9 +632,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
int offset_in_page;
int unwritten, cur_len;
- ret = get_ext_path(orig_inode, o_start, &path);
- if (ret)
+ path = get_ext_path(orig_inode, o_start, path);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
goto out;
+ }
ex = path[path->p_depth].p_ext;
cur_blk = le32_to_cpu(ex->ee_block);
cur_len = ext4_ext_get_actual_len(ex);
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 3ec815e615e7..c629b5580b72 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -742,6 +742,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
for (i = 0; i < orphan_blocks; i++) {
struct page *page;
struct f2fs_orphan_block *orphan_blk;
+ unsigned int entry_count;
page = f2fs_get_meta_page(sbi, start_blk + i);
if (IS_ERR(page)) {
@@ -750,7 +751,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
}
orphan_blk = (struct f2fs_orphan_block *)page_address(page);
- for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
+ entry_count = le32_to_cpu(orphan_blk->entry_count);
+ if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
+ f2fs_err(sbi, "invalid orphan inode entry count %u",
+ entry_count);
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN);
+ err = -EFSCORRUPTED;
+ f2fs_put_page(page, 1);
+ goto out;
+ }
+
+ for (j = 0; j < entry_count; j++) {
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
err = recover_orphan_inode(sbi, ino);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 435791876561..af1438277575 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -887,6 +887,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
}
}
+void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi)
+{
+ struct bio_entry *be, *tmp;
+ struct f2fs_bio_info *io;
+ enum temp_type temp;
+
+ for (temp = HOT; temp < NR_TEMP_TYPE; temp++) {
+ LIST_HEAD(list);
+
+ io = sbi->write_io[DATA] + temp;
+
+ /* A lockless list_empty() check is safe here: any bios from
+ * other kworkers that we miss will be submitted by those
+ * kworkers accordingly.
+ */
+ if (list_empty(&io->bio_list))
+ continue;
+
+ f2fs_down_write(&io->bio_list_lock);
+ list_splice_init(&io->bio_list, &list);
+ f2fs_up_write(&io->bio_list_lock);
+
+ list_for_each_entry_safe(be, tmp, &list, list) {
+ f2fs_submit_write_bio(sbi, be->bio, DATA);
+ del_bio_entry(be);
+ }
+ }
+}
+
int f2fs_merge_page_bio(struct f2fs_io_info *fio)
{
struct bio *bio = *fio->bio;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 50847fd7e550..63d3450f895e 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -3866,6 +3866,7 @@ void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi,
nid_t ino, enum page_type type);
void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
struct bio **bio, struct page *page);
+void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi);
void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi);
int f2fs_submit_page_bio(struct f2fs_io_info *fio);
int f2fs_merge_page_bio(struct f2fs_io_info *fio);
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 5654608a1192..ac21950f53cd 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1173,8 +1173,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static int ra_data_block(struct inode *inode, pgoff_t index)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct dnode_of_data dn;
struct page *page;
struct f2fs_io_info fio = {
@@ -1189,9 +1189,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
};
int err;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
page = f2fs_grab_cache_page(mapping, index, true);
- if (!page)
- return -ENOMEM;
+ if (!page) {
+ err = -ENOMEM;
+ goto out_iput;
+ }
if (f2fs_lookup_read_extent_cache_block(inode, index,
&dn.data_blkaddr)) {
@@ -1250,11 +1263,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
+ if (atomic_inode)
+ iput(atomic_inode);
return 0;
put_encrypted_page:
f2fs_put_page(fio.encrypted_page, 1);
put_page:
f2fs_put_page(page, 1);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
@@ -1265,8 +1283,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
static int move_data_block(struct inode *inode, block_t bidx,
int gc_type, unsigned int segno, int off)
{
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode),
.ino = inode->i_ino,
@@ -1288,10 +1306,23 @@ static int move_data_block(struct inode *inode, block_t bidx,
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
/* do not read out */
page = f2fs_grab_cache_page(mapping, bidx, false);
- if (!page)
- return -ENOMEM;
+ if (!page) {
+ err = -ENOMEM;
+ goto out_iput;
+ }
if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
err = -ENOENT;
@@ -1413,6 +1444,9 @@ static int move_data_block(struct inode *inode, block_t bidx,
f2fs_put_dnode(&dn);
out:
f2fs_put_page(page, 1);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 3546bdb04d15..3dda15cf3c6b 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -307,15 +307,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
F2FS_TOTAL_EXTRA_ATTR_SIZE);
return false;
}
- if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
- f2fs_has_inline_xattr(inode) &&
- (!fi->i_inline_xattr_size ||
- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
- f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu",
- __func__, inode->i_ino, fi->i_inline_xattr_size,
- MAX_INLINE_XATTR_SIZE);
- return false;
- }
if (f2fs_sb_has_compression(sbi) &&
fi->i_flags & F2FS_COMPR_FL &&
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
@@ -329,6 +320,16 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
return false;
}
+ if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
+ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
+ (f2fs_has_inline_xattr(inode) &&
+ fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
+ f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %zu",
+ __func__, inode->i_ino, fi->i_inline_xattr_size,
+ MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
+ return false;
+ }
+
if (!f2fs_sb_has_extra_attr(sbi)) {
if (f2fs_sb_has_project_quota(sbi)) {
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
@@ -842,10 +843,15 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_abort_atomic_write(inode, true);
if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
- clear_inode_flag(fi->cow_inode, FI_COW_FILE);
- F2FS_I(fi->cow_inode)->atomic_inode = NULL;
- iput(fi->cow_inode);
+ struct inode *cow_inode = fi->cow_inode;
+
+ f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
+ clear_inode_flag(cow_inode, FI_COW_FILE);
+ F2FS_I(cow_inode)->atomic_inode = NULL;
fi->cow_inode = NULL;
+ f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
+
+ iput(cow_inode);
}
trace_f2fs_evict_inode(inode);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 8791aba087cd..9c27353f384b 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -433,6 +433,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
if (has_enough_free_secs(sbi, 0, 0))
return;
+ /*
+ * Submit all cached OPU/IPU DATA bios before triggering
+ * foreground GC to avoid potential deadlocks.
+ */
+ f2fs_submit_merged_write(sbi, DATA);
+ f2fs_submit_all_merged_ipu_writes(sbi);
+
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
sbi->gc_thread->f2fs_gc_task) {
DEFINE_WAIT(wait);
@@ -450,6 +457,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
.should_migrate_blocks = false,
.err_gc_skipped = false,
.nr_free_secs = 1 };
+
f2fs_down_write(&sbi->gc_lock);
stat_inc_gc_call_count(sbi, FOREGROUND);
f2fs_gc(sbi, &gc_control);
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index c2cb24be4e79..cb9f58db5253 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
size_t prefix_len;
size_t size;
- prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
-
if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr",
@@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
f2fs_handle_error(F2FS_I_SB(inode),
ERROR_CORRUPTED_XATTR);
- break;
+ error = -EFSCORRUPTED;
+ goto cleanup;
}
+ prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
if (!prefix)
continue;
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index a1236a178a7b..ca424efe498d 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -345,7 +345,8 @@ static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode)
}
struct inode_switch_wbs_context {
- struct rcu_work work;
+ /* List of queued switching contexts for the wb */
+ struct llist_node list;
/*
* Multiple inodes can be switched at once. The switching procedure
@@ -355,7 +356,6 @@ struct inode_switch_wbs_context {
* array embedded into struct inode_switch_wbs_context. Otherwise
* an inode could be left in a non-consistent state.
*/
- struct bdi_writeback *new_wb;
struct inode *inodes[];
};
@@ -464,13 +464,11 @@ static bool inode_do_switch_wbs(struct inode *inode,
return switched;
}
-static void inode_switch_wbs_work_fn(struct work_struct *work)
+static void process_inode_switch_wbs(struct bdi_writeback *new_wb,
+ struct inode_switch_wbs_context *isw)
{
- struct inode_switch_wbs_context *isw =
- container_of(to_rcu_work(work), struct inode_switch_wbs_context, work);
struct backing_dev_info *bdi = inode_to_bdi(isw->inodes[0]);
struct bdi_writeback *old_wb = isw->inodes[0]->i_wb;
- struct bdi_writeback *new_wb = isw->new_wb;
unsigned long nr_switched = 0;
struct inode **inodep;
@@ -530,6 +528,40 @@ static void inode_switch_wbs_work_fn(struct work_struct *work)
atomic_dec(&isw_nr_in_flight);
}
+void inode_switch_wbs_work_fn(struct work_struct *work)
+{
+ struct bdi_writeback *new_wb = container_of(work, struct bdi_writeback,
+ switch_work);
+ struct inode_switch_wbs_context *isw, *next_isw;
+ struct llist_node *list;
+
+ list = llist_del_all(&new_wb->switch_wbs_ctxs);
+ /*
+ * Nothing to do? That would be a problem as references held by isw
+ * items protect wb from freeing...
+ */
+ if (WARN_ON_ONCE(!list))
+ return;
+
+ /*
+ * Grab our reference to wb so that it cannot get freed under us
+ * after we process all the isw items.
+ */
+ wb_get(new_wb);
+ /*
+ * In addition to synchronizing among switchers, I_WB_SWITCH
+ * tells the RCU protected stat update paths to grab the i_page
+ * lock so that stat transfer can synchronize against them.
+ * Let's continue after I_WB_SWITCH is guaranteed to be
+ * visible.
+ */
+ synchronize_rcu();
+
+ llist_for_each_entry_safe(isw, next_isw, list, list)
+ process_inode_switch_wbs(new_wb, isw);
+ wb_put(new_wb);
+}
+
static bool inode_prepare_wbs_switch(struct inode *inode,
struct bdi_writeback *new_wb)
{
@@ -559,6 +591,13 @@ static bool inode_prepare_wbs_switch(struct inode *inode,
return true;
}
+static void wb_queue_isw(struct bdi_writeback *wb,
+ struct inode_switch_wbs_context *isw)
+{
+ if (llist_add(&isw->list, &wb->switch_wbs_ctxs))
+ queue_work(isw_wq, &wb->switch_work);
+}
+
/**
* inode_switch_wbs - change the wb association of an inode
* @inode: target inode
@@ -572,6 +611,7 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
struct backing_dev_info *bdi = inode_to_bdi(inode);
struct cgroup_subsys_state *memcg_css;
struct inode_switch_wbs_context *isw;
+ struct bdi_writeback *new_wb = NULL;
/* noop if seems to be already in progress */
if (inode->i_state & I_WB_SWITCH)
@@ -587,49 +627,52 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
atomic_inc(&isw_nr_in_flight);
- /* find and pin the new wb */
+ /*
+ * Paired with synchronize_rcu() in cgroup_writeback_umount():
+ * holding rcu_read_lock across inode_prepare_wbs_switch()
+ * (covering the SB_ACTIVE check and the inode grab) and
+ * wb_queue_isw() ensures synchronize_rcu() cannot return until
+ * the work is queued, so the subsequent flush_workqueue() will
+ * wait for the switch.
+ */
rcu_read_lock();
+ /* find and pin the new wb */
memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys);
if (memcg_css && !css_tryget(memcg_css))
memcg_css = NULL;
- rcu_read_unlock();
if (!memcg_css)
goto out_free;
- isw->new_wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC);
+ new_wb = wb_get_create(bdi, memcg_css, GFP_ATOMIC);
css_put(memcg_css);
- if (!isw->new_wb)
+ if (!new_wb)
goto out_free;
- if (!inode_prepare_wbs_switch(inode, isw->new_wb))
+ if (!inode_prepare_wbs_switch(inode, new_wb))
goto out_free;
isw->inodes[0] = inode;
- /*
- * In addition to synchronizing among switchers, I_WB_SWITCH tells
- * the RCU protected stat update paths to grab the i_page
- * lock so that stat transfer can synchronize against them.
- * Let's continue after I_WB_SWITCH is guaranteed to be visible.
- */
- INIT_RCU_WORK(&isw->work, inode_switch_wbs_work_fn);
- queue_rcu_work(isw_wq, &isw->work);
+ wb_queue_isw(new_wb, isw);
+ rcu_read_unlock();
return;
out_free:
+ rcu_read_unlock();
atomic_dec(&isw_nr_in_flight);
- if (isw->new_wb)
- wb_put(isw->new_wb);
+ if (new_wb)
+ wb_put(new_wb);
kfree(isw);
}
-static bool isw_prepare_wbs_switch(struct inode_switch_wbs_context *isw,
+static bool isw_prepare_wbs_switch(struct bdi_writeback *new_wb,
+ struct inode_switch_wbs_context *isw,
struct list_head *list, int *nr)
{
struct inode *inode;
list_for_each_entry(inode, list, i_io_list) {
- if (!inode_prepare_wbs_switch(inode, isw->new_wb))
+ if (!inode_prepare_wbs_switch(inode, new_wb))
continue;
isw->inodes[*nr] = inode;
@@ -653,6 +696,7 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb)
{
struct cgroup_subsys_state *memcg_css;
struct inode_switch_wbs_context *isw;
+ struct bdi_writeback *new_wb;
int nr;
bool restart = false;
@@ -665,14 +709,22 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb)
for (memcg_css = wb->memcg_css->parent; memcg_css;
memcg_css = memcg_css->parent) {
- isw->new_wb = wb_get_create(wb->bdi, memcg_css, GFP_KERNEL);
- if (isw->new_wb)
+ new_wb = wb_get_create(wb->bdi, memcg_css, GFP_KERNEL);
+ if (new_wb)
break;
}
- if (unlikely(!isw->new_wb))
- isw->new_wb = &wb->bdi->wb; /* wb_get() is noop for bdi's wb */
+ if (unlikely(!new_wb))
+ new_wb = &wb->bdi->wb; /* wb_get() is noop for bdi's wb */
nr = 0;
+ /*
+ * Paired with synchronize_rcu() in cgroup_writeback_umount().
+ * Holding rcu_read_lock across the SB_ACTIVE check, the inode grab
+ * and wb_queue_isw() ensures synchronize_rcu() cannot return until
+ * the work is queued, so the subsequent flush_workqueue() will wait
+ * for the switch.
+ */
+ rcu_read_lock();
spin_lock(&wb->list_lock);
/*
* In addition to the inodes that have completed writeback, also switch
@@ -682,27 +734,23 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb)
* bandwidth restrictions, as writeback of inode metadata is not
* accounted for.
*/
- restart = isw_prepare_wbs_switch(isw, &wb->b_attached, &nr);
+ restart = isw_prepare_wbs_switch(new_wb, isw, &wb->b_attached, &nr);
if (!restart)
- restart = isw_prepare_wbs_switch(isw, &wb->b_dirty_time, &nr);
+ restart = isw_prepare_wbs_switch(new_wb, isw, &wb->b_dirty_time,
+ &nr);
spin_unlock(&wb->list_lock);
/* no attached inodes? bail out */
if (nr == 0) {
+ rcu_read_unlock();
atomic_dec(&isw_nr_in_flight);
- wb_put(isw->new_wb);
+ wb_put(new_wb);
kfree(isw);
return restart;
}
- /*
- * In addition to synchronizing among switchers, I_WB_SWITCH tells
- * the RCU protected stat update paths to grab the i_page
- * lock so that stat transfer can synchronize against them.
- * Let's continue after I_WB_SWITCH is guaranteed to be visible.
- */
- INIT_RCU_WORK(&isw->work, inode_switch_wbs_work_fn);
- queue_rcu_work(isw_wq, &isw->work);
+ wb_queue_isw(new_wb, isw);
+ rcu_read_unlock();
return restart;
}
@@ -1136,10 +1184,13 @@ void cgroup_writeback_umount(void)
if (atomic_read(&isw_nr_in_flight)) {
/*
- * Use rcu_barrier() to wait for all pending callbacks to
- * ensure that all in-flight wb switches are in the workqueue.
+ * Paired with rcu_read_lock() in inode_switch_wbs() and
+ * cleanup_offline_cgwb(). synchronize_rcu() waits for any
+ * in-flight switcher that already passed the SB_ACTIVE check
+ * to finish queueing its work, so flush_workqueue() below
+ * will then drain it.
*/
- rcu_barrier();
+ synchronize_rcu();
flush_workqueue(isw_wq);
}
}
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index b6cad106c37e..e4d809e2d68d 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -386,7 +386,7 @@ static void cuse_process_init_reply(struct fuse_mount *fm,
rc = -ENOMEM;
cdev = cdev_alloc();
if (!cdev)
- goto err_unlock;
+ goto err_dev;
cdev->owner = THIS_MODULE;
cdev->ops = &cuse_frontend_fops;
@@ -412,6 +412,8 @@ static void cuse_process_init_reply(struct fuse_mount *fm,
err_cdev:
cdev_del(cdev);
+err_dev:
+ device_del(dev);
err_unlock:
mutex_unlock(&cuse_lock);
put_device(dev);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b15ecd74a1bd..252b6697960f 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -912,7 +912,7 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
cs->nr_segs++;
cs->len = 0;
- return 0;
+ return lock_request(cs->req);
}
/*
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
index e46f650b5e9c..245c2b967189 100644
--- a/fs/hfs/bfind.c
+++ b/fs/hfs/bfind.c
@@ -167,7 +167,7 @@ int hfs_brec_find(struct hfs_find_data *fd)
return res;
}
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
index e8cd1a31f247..f9db5b31acd1 100644
--- a/fs/hfs/bnode.c
+++ b/fs/hfs/bnode.c
@@ -16,14 +16,14 @@
#include "btree.h"
static inline
-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
+bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d\n",
+ "node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
+u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@@ -41,13 +41,13 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
- if ((off + len) > node_size) {
- int new_len = (int)node_size - off;
+ if ((u64)off + len > node_size) {
+ u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, "
- "requested_len %d, corrected_len %d\n",
+ "node_size %u, offset %u, "
+ "requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);
@@ -57,12 +57,14 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
return len;
}
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
- int pagenum;
- int bytes_read;
- int bytes_to_read;
+ u32 pagenum;
+ u32 bytes_read;
+ u32 bytes_to_read;
+
+ memset(buf, 0, len);
if (!is_bnode_offset_valid(node, off))
return;
@@ -70,7 +72,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -86,7 +88,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (pagenum >= node->tree->pages_per_bnode)
break;
page = node->page[pagenum];
- bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
+ bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off);
memcpy_from_page(buf + bytes_read, page, off, bytes_to_read);
@@ -95,7 +97,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
}
}
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
// optimize later...
@@ -103,7 +105,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
// optimize later...
@@ -111,10 +113,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
- int key_len;
+ u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@@ -125,14 +127,14 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfs_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfs_btree_key));
- pr_err("hfs: Invalid key length: %d\n", key_len);
+ pr_err("hfs: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page *page;
@@ -142,7 +144,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -157,20 +159,20 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
set_page_dirty(page);
}
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
// optimize later...
hfs_bnode_write(node, &v, off, 2);
}
-void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
+void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data)
{
// optimize later...
hfs_bnode_write(node, &data, off, 1);
}
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page *page;
@@ -180,7 +182,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -195,8 +197,8 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
set_page_dirty(page);
}
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len)
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page *src_page, *dst_page;
@@ -216,7 +218,7 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
set_page_dirty(dst_page);
}
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page *page;
void *ptr;
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
index b01db1fae147..a463315316bf 100644
--- a/fs/hfs/brec.c
+++ b/fs/hfs/brec.c
@@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index e86e1e235658..e748d92d8410 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -259,7 +259,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
u32 count;
diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h
index 0e6baee93245..97f88035b224 100644
--- a/fs/hfs/btree.h
+++ b/fs/hfs/btree.h
@@ -86,48 +86,49 @@ struct hfs_find_data {
/* btree.c */
-extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
-extern void hfs_btree_close(struct hfs_btree *);
-extern void hfs_btree_write(struct hfs_btree *);
-extern int hfs_bmap_reserve(struct hfs_btree *, int);
-extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
+extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
+ btree_keycmp keycmp);
+extern void hfs_btree_close(struct hfs_btree *tree);
+extern void hfs_btree_write(struct hfs_btree *tree);
+extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
+extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
extern void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
-extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
-extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
-extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
-extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
-extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
-extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
-extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8);
-extern void hfs_bnode_clear(struct hfs_bnode *, int, int);
-extern void hfs_bnode_copy(struct hfs_bnode *, int,
- struct hfs_bnode *, int, int);
-extern void hfs_bnode_move(struct hfs_bnode *, int, int, int);
-extern void hfs_bnode_dump(struct hfs_bnode *);
-extern void hfs_bnode_unlink(struct hfs_bnode *);
-extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
-extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
-extern void hfs_bnode_unhash(struct hfs_bnode *);
-extern void hfs_bnode_free(struct hfs_bnode *);
-extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
-extern void hfs_bnode_get(struct hfs_bnode *);
-extern void hfs_bnode_put(struct hfs_bnode *);
+extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
+extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
+extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
+extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
+extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data);
+extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
+extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len);
+extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
+extern void hfs_bnode_dump(struct hfs_bnode *node);
+extern void hfs_bnode_unlink(struct hfs_bnode *node);
+extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
+extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num);
+extern void hfs_bnode_unhash(struct hfs_bnode *node);
+extern void hfs_bnode_free(struct hfs_bnode *node);
+extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num);
+extern void hfs_bnode_get(struct hfs_bnode *node);
+extern void hfs_bnode_put(struct hfs_bnode *node);
/* brec.c */
-extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
-extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
-extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
-extern int hfs_brec_remove(struct hfs_find_data *);
+extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
+extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
+extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
+extern int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
-extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
-extern void hfs_find_exit(struct hfs_find_data *);
-extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
-extern int hfs_brec_find(struct hfs_find_data *);
-extern int hfs_brec_read(struct hfs_find_data *, void *, int);
-extern int hfs_brec_goto(struct hfs_find_data *, int);
+extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd);
+extern void hfs_find_exit(struct hfs_find_data *fd);
+extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd);
+extern int hfs_brec_find(struct hfs_find_data *fd);
+extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
+extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
struct hfs_bnode_desc {
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index f1402d71b092..acd69a3db885 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -171,74 +171,90 @@ struct hfs_sb_info {
#define HFS_FLG_ALT_MDB_DIRTY 2
/* bitmap.c */
-extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *);
-extern int hfs_clear_vbm_bits(struct super_block *, u16, u16);
+extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits);
+extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count);
/* catalog.c */
-extern int hfs_cat_keycmp(const btree_key *, const btree_key *);
+extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2);
struct hfs_find_data;
-extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *);
-extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *);
-extern int hfs_cat_delete(u32, struct inode *, const struct qstr *);
-extern int hfs_cat_move(u32, struct inode *, const struct qstr *,
- struct inode *, const struct qstr *);
-extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *);
+extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
+ struct hfs_find_data *fd);
+extern int hfs_cat_create(u32 cnid, struct inode *dir,
+ const struct qstr *str, struct inode *inode);
+extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str);
+extern int hfs_cat_move(u32 cnid, struct inode *src_dir,
+ const struct qstr *src_name,
+ struct inode *dst_dir,
+ const struct qstr *dst_name);
+extern void hfs_cat_build_key(struct super_block *sb, btree_key *key,
+ u32 parent, const struct qstr *name);
/* dir.c */
extern const struct file_operations hfs_dir_operations;
extern const struct inode_operations hfs_dir_inode_operations;
/* extent.c */
-extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
+extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2);
extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off);
-extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
-extern int hfs_ext_write_extent(struct inode *);
-extern int hfs_extend_file(struct inode *);
-extern void hfs_file_truncate(struct inode *);
+extern int hfs_free_fork(struct super_block *sb,
+ struct hfs_cat_file *file, int type);
+extern int hfs_ext_write_extent(struct inode *inode);
+extern int hfs_extend_file(struct inode *inode);
+extern void hfs_file_truncate(struct inode *inode);
-extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
+extern int hfs_get_block(struct inode *inode, sector_t block,
+ struct buffer_head *bh_result, int create);
/* inode.c */
extern const struct address_space_operations hfs_aops;
extern const struct address_space_operations hfs_btree_aops;
int hfs_write_begin(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, struct page **pagep, void **fsdata);
-extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
-extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
-extern int hfs_write_inode(struct inode *, struct writeback_control *);
-extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *,
- struct iattr *);
+ loff_t pos, unsigned int len, struct page **pagep,
+ void **fsdata);
+extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name,
+ umode_t mode);
+extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
+ __be32 *log_size, __be32 *phys_size);
+extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc);
+extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
- __be32 log_size, __be32 phys_size, u32 clump_size);
-extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *);
-extern void hfs_evict_inode(struct inode *);
-extern void hfs_delete_inode(struct inode *);
+ __be32 __log_size, __be32 phys_size,
+ u32 clump_size);
+extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key,
+ hfs_cat_rec *rec);
+extern void hfs_evict_inode(struct inode *inode);
+extern void hfs_delete_inode(struct inode *inode);
/* attr.c */
extern const struct xattr_handler *hfs_xattr_handlers[];
/* mdb.c */
-extern int hfs_mdb_get(struct super_block *);
-extern void hfs_mdb_commit(struct super_block *);
-extern void hfs_mdb_close(struct super_block *);
-extern void hfs_mdb_put(struct super_block *);
+extern int hfs_mdb_get(struct super_block *sb);
+extern void hfs_mdb_commit(struct super_block *sb);
+extern void hfs_mdb_close(struct super_block *sb);
+extern void hfs_mdb_put(struct super_block *sb);
/* part_tbl.c */
-extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
+extern int hfs_part_find(struct super_block *sb,
+ sector_t *part_start, sector_t *part_size);
/* string.c */
extern const struct dentry_operations hfs_dentry_operations;
-extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
-extern int hfs_strcmp(const unsigned char *, unsigned int,
- const unsigned char *, unsigned int);
+extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this);
+extern int hfs_strcmp(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2);
extern int hfs_compare_dentry(const struct dentry *dentry,
- unsigned int len, const char *str, const struct qstr *name);
+ unsigned int len, const char *str,
+ const struct qstr *name);
/* trans.c */
-extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *);
-extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *);
+extern void hfs_asc2mac(struct super_block *sb,
+ struct hfs_name *out, const struct qstr *in);
+extern int hfs_mac2asc(struct super_block *sb,
+ char *out, const struct hfs_name *in);
/* super.c */
extern void hfs_mark_mdb_dirty(struct super_block *sb);
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 6a89f22d8967..3a3994616fa5 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -50,7 +50,8 @@ static void hfs_write_failed(struct address_space *mapping, loff_t to)
}
int hfs_write_begin(struct file *file, struct address_space *mapping,
- loff_t pos, unsigned len, struct page **pagep, void **fsdata)
+ loff_t pos, unsigned int len, struct page **pagep,
+ void **fsdata)
{
int ret;
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 41f4f56f90fa..094ce196a4d4 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -210,7 +210,7 @@ int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare)
return res;
}
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
{
int res;
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
index fb437598e262..5bd5d3ae40b0 100644
--- a/fs/hfsplus/bnode.c
+++ b/fs/hfsplus/bnode.c
@@ -20,10 +20,12 @@
/* Copy a specified range of bytes from the raw data of a node */
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
+
+ memset(buf, 0, len);
if (!is_bnode_offset_valid(node, off))
return;
@@ -31,7 +33,7 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -43,17 +45,17 @@ void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memcpy_from_page(buf, *pagep, off, l);
while ((len -= l) != 0) {
buf += l;
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_from_page(buf, *++pagep, 0, l);
}
}
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
{
__be16 data;
/* TODO: optimize later... */
@@ -61,7 +63,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
return be16_to_cpu(data);
}
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
{
u8 data;
/* TODO: optimize later... */
@@ -69,10 +71,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
return data;
}
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
{
struct hfs_btree *tree;
- int key_len;
+ u32 key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
@@ -84,17 +86,17 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) {
memset(key, 0, sizeof(hfsplus_btree_key));
- pr_err("hfsplus: Invalid key length: %d\n", key_len);
+ pr_err("hfsplus: Invalid key length: %u\n", key_len);
return;
}
hfs_bnode_read(node, key, off, key_len);
}
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@@ -102,7 +104,7 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -114,29 +116,29 @@ void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memcpy_to_page(*pagep, off, buf, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
buf += l;
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_to_page(*++pagep, 0, buf, l);
set_page_dirty(*pagep);
}
}
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
{
__be16 v = cpu_to_be16(data);
/* TODO: optimize later... */
hfs_bnode_write(node, &v, off, 2);
}
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
{
struct page **pagep;
- int l;
+ u32 l;
if (!is_bnode_offset_valid(node, off))
return;
@@ -144,7 +146,7 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
if (len == 0) {
pr_err("requested zero length: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, len %d\n",
+ "node_size %u, offset %u, len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len);
return;
@@ -156,22 +158,22 @@ void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
pagep = node->page + (off >> PAGE_SHIFT);
off &= ~PAGE_MASK;
- l = min_t(int, len, PAGE_SIZE - off);
+ l = min_t(u32, len, PAGE_SIZE - off);
memzero_page(*pagep, off, l);
set_page_dirty(*pagep);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memzero_page(*++pagep, 0, l);
set_page_dirty(*pagep);
}
}
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len)
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len)
{
struct page **src_page, **dst_page;
- int l;
+ u32 l;
hfs_dbg(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
if (!len)
@@ -188,12 +190,12 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
dst &= ~PAGE_MASK;
if (src == dst) {
- l = min_t(int, len, PAGE_SIZE - src);
+ l = min_t(u32, len, PAGE_SIZE - src);
memcpy_page(*dst_page, src, *src_page, src, l);
set_page_dirty(*dst_page);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
memcpy_page(*++dst_page, 0, *++src_page, 0, l);
set_page_dirty(*dst_page);
}
@@ -225,11 +227,11 @@ void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
}
}
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
{
struct page **src_page, **dst_page;
void *src_ptr, *dst_ptr;
- int l;
+ u32 l;
hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
if (!len)
@@ -299,7 +301,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
dst &= ~PAGE_MASK;
if (src == dst) {
- l = min_t(int, len, PAGE_SIZE - src);
+ l = min_t(u32, len, PAGE_SIZE - src);
dst_ptr = kmap_local_page(*dst_page) + src;
src_ptr = kmap_local_page(*src_page) + src;
@@ -309,7 +311,7 @@ void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
kunmap_local(dst_ptr);
while ((len -= l) != 0) {
- l = min_t(int, len, PAGE_SIZE);
+ l = min_t(u32, len, PAGE_SIZE);
dst_ptr = kmap_local_page(*++dst_page);
src_ptr = kmap_local_page(*++src_page);
memmove(dst_ptr, src_ptr, l);
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
index 1918544a7871..53e4fa3f608b 100644
--- a/fs/hfsplus/brec.c
+++ b/fs/hfsplus/brec.c
@@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
return retval;
}
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
{
struct hfs_btree *tree;
struct hfs_bnode *node, *new_node;
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index fe6a54c4083c..72c593308bba 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
}
/* Make sure @tree has enough space for the @rsvd_nodes */
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
{
struct inode *inode = tree->inode;
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 52be0d59e532..0e45973be3df 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -388,21 +388,21 @@ u32 hfsplus_calc_btree_clump_size(u32 block_size, u32 node_size, u64 sectors,
struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
void hfs_btree_close(struct hfs_btree *tree);
int hfs_btree_write(struct hfs_btree *tree);
-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
+int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
void hfs_bmap_free(struct hfs_bnode *node);
/* bnode.c */
-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len);
-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off);
-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off);
-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off);
-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len);
-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data);
-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);
-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
- struct hfs_bnode *src_node, int src, int len);
-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len);
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
+void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
+void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
+void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
+ struct hfs_bnode *src_node, u32 src, u32 len);
+void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
void hfs_bnode_dump(struct hfs_bnode *node);
void hfs_bnode_unlink(struct hfs_bnode *node);
struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
@@ -417,7 +417,7 @@ bool hfs_bnode_need_zeroout(struct hfs_btree *tree);
/* brec.c */
u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len);
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
int hfs_brec_remove(struct hfs_find_data *fd);
/* bfind.c */
@@ -430,7 +430,7 @@ int hfs_find_rec_by_key(struct hfs_bnode *bnode, struct hfs_find_data *fd,
int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
search_strategy_t rec_found);
int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare);
-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len);
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
/* catalog.c */
@@ -588,14 +588,14 @@ hfsplus_btree_lock_class(struct hfs_btree *tree)
}
static inline
-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
+bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
{
bool is_valid = off < node->tree->node_size;
if (!is_valid) {
pr_err("requested invalid offset: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d\n",
+ "node_size %u, offset %u\n",
node->this, node->type, node->height,
node->tree->node_size, off);
}
@@ -604,7 +604,7 @@ bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
}
static inline
-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
+u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
{
unsigned int node_size;
@@ -613,13 +613,13 @@ int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
node_size = node->tree->node_size;
- if ((off + len) > node_size) {
- int new_len = (int)node_size - off;
+ if ((u64)off + len > node_size) {
+ u32 new_len = node_size - off;
pr_err("requested length has been corrected: "
"NODE: id %u, type %#x, height %u, "
- "node_size %u, offset %d, "
- "requested_len %d, corrected_len %d\n",
+ "node_size %u, offset %u, "
+ "requested_len %u, corrected_len %u\n",
node->this, node->type, node->height,
node->tree->node_size, off, len, new_len);
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 4a791011cb09..1a88f396f968 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -462,6 +462,9 @@ parse_rock_ridge_inode_internal(struct iso_directory_record *de,
inode->i_size = symlink_len;
while (slen > 1) {
rootflag = 0;
+ /* keep the component within the SL record */
+ if (slp->len + 2 > slen)
+ goto eio;
switch (slp->flags & ~1) {
case 0:
inode->i_size +=
@@ -617,6 +620,14 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
slp = &rr->u.SL.link;
while (slen > 1) {
rootflag = 0;
+ /*
+ * A component is slp->len + 2 bytes (a two-byte header plus
+ * len bytes of text). If it does not fit in the bytes left in
+ * the SL record the record is malformed: fail like the plimit
+ * checks below so readlink() returns -EIO, not a truncated path.
+ */
+ if (slp->len + 2 > slen)
+ return NULL;
switch (slp->flags & ~1) {
case 0:
if (slp->len > plimit - rpnt)
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 88ac2639ad61..e0d82f83abdc 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -2303,6 +2303,8 @@ jbd2_journal_initialize_fast_commit(journal_t *journal)
unsigned long long num_fc_blks;
num_fc_blks = jbd2_journal_get_num_fc_blks(sb);
+ if (num_fc_blks > journal->j_last)
+ return -EFSCORRUPTED;
if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
return -ENOSPC;
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index e3b6229e7ae5..e64e0110f66b 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -123,6 +123,8 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
mutex_lock(&file->f_mutex);
nfserr = nlm_do_fopen(rqstp, file, mode);
mutex_unlock(&file->f_mutex);
+ if (nfserr)
+ goto out_unlock;
goto found;
}
nlm_debug_print_fh("creating file for", &lock->fh);
@@ -139,7 +141,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
nfserr = nlm_do_fopen(rqstp, file, mode);
if (nfserr)
- goto out_unlock;
+ goto out_free;
hlist_add_head(&file->f_list, &nlm_files[hash]);
diff --git a/fs/minix/minix.h b/fs/minix/minix.h
index d493507c064f..aee0ff9e5623 100644
--- a/fs/minix/minix.h
+++ b/fs/minix/minix.h
@@ -91,7 +91,7 @@ static inline struct minix_inode_info *minix_i(struct inode *inode)
static inline unsigned minix_blocks_needed(unsigned bits, unsigned blocksize)
{
- return DIV_ROUND_UP(bits, blocksize * 8);
+ return DIV_ROUND_UP_POW2(bits, blocksize * 8);
}
#if defined(CONFIG_MINIX_FS_NATIVE_ENDIAN) && \
diff --git a/fs/namei.c b/fs/namei.c
index 02b0c2731298..861390e523b2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3676,6 +3676,10 @@ static int vfs_tmpfile(struct mnt_idmap *idmap,
int error;
int open_flag = file->f_flags;
+ /* A tmpfile is I_LINKABLE, so guard its owner like may_o_create(). */
+ if (!fsuidgid_has_mapping(dir->i_sb, idmap))
+ return -EOVERFLOW;
+
/* we want directory to be writable */
error = inode_permission(idmap, dir, MAY_WRITE | MAY_EXEC);
if (error)
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 6bed1394d748..7105e623c294 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -248,6 +248,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
struct pnfs_layout_hdr *lo;
u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
LIST_HEAD(free_me_list);
+ bool return_range = false;
ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid);
if (IS_ERR(ino)) {
@@ -292,13 +293,13 @@ static u32 initiate_file_draining(struct nfs_client *clp,
/* Embrace your forgetfulness! */
rv = NFS4ERR_NOMATCHING_LAYOUT;
- if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
- NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
- &args->cbl_range);
- }
+ return_range = true;
}
unlock:
spin_unlock(&ino->i_lock);
+ if (return_range && NFS_SERVER(ino)->pnfs_curr_ld->return_range)
+ NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
+ &args->cbl_range);
pnfs_free_lseg_list(&free_me_list);
/* Free all lsegs that are attached to commit buckets */
nfs_commit_inode(ino, 0);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 46f53f40b741..5f4c12c4d146 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -3275,6 +3275,8 @@ static int nfs_open_permission_mask(int openflags)
mask |= MAY_READ;
if ((openflags & O_ACCMODE) != O_RDONLY)
mask |= MAY_WRITE;
+ if (openflags & O_TRUNC)
+ mask |= MAY_WRITE;
}
return mask;
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index 569ae4ec6084..4b4dcd6fe782 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -786,6 +786,8 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
static bool
filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg)
{
+ if (flseg->dsaddr)
+ return flseg->dsaddr->stripe_count > 1;
return flseg->num_fh > 1;
}
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 42c73c647a27..3c60edc22b8d 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -460,6 +460,10 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
if (!p)
goto out_err_free;
fh_count = be32_to_cpup(p);
+ if (fh_count == 0) {
+ rc = -EINVAL;
+ goto out_err_free;
+ }
fls->mirror_array[i]->fh_versions =
kcalloc(fh_count, sizeof(struct nfs_fh),
@@ -535,6 +539,9 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
if (!p)
goto out_sort_mirrors;
fls->flags = be32_to_cpup(p);
+ if (fls->flags & FF_FLAGS_NO_IO_THRU_MDS)
+ set_bit(NFS4_FF_HDR_NO_IO_THRU_MDS,
+ &FF_LAYOUT_FROM_HDR(lh)->flags);
p = xdr_inline_decode(&stream, 4);
if (!p)
@@ -988,6 +995,16 @@ ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio,
0, NFS4_MAX_UINT64, IOMODE_RW,
NFS_I(pgio->pg_inode)->layout,
pgio->pg_lseg);
+ if (NFS_I(pgio->pg_inode)->layout &&
+ ff_layout_hdr_no_fallback_to_mds(NFS_I(pgio->pg_inode)->layout)) {
+ /*
+ * FF_FLAGS_NO_IO_THRU_MDS: no current lseg but the server's
+ * policy forbids MDS fallback. Surface -EAGAIN so writeback
+ * retries rather than silently issuing the WRITE via MDS.
+ */
+ pgio->pg_error = -EAGAIN;
+ goto out;
+ }
/* no lseg means that pnfs is not in use, so no mirroring here */
nfs_pageio_reset_write_mds(pgio);
out:
@@ -1898,6 +1915,14 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
out_failed:
if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error)
return PNFS_TRY_AGAIN;
+ if (ff_layout_no_fallback_to_mds(lseg)) {
+ /*
+ * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET,
+ * never fall through to MDS I/O.
+ */
+ pnfs_error_mark_layout_for_return(hdr->inode, lseg);
+ return PNFS_TRY_AGAIN;
+ }
trace_pnfs_mds_fallback_read_pagelist(hdr->inode,
hdr->args.offset, hdr->args.count,
IOMODE_READ, NFS_I(hdr->inode)->layout, lseg);
@@ -1969,6 +1994,14 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
out_failed:
if (ff_layout_avoid_mds_available_ds(lseg) && !ds_fatal_error)
return PNFS_TRY_AGAIN;
+ if (ff_layout_no_fallback_to_mds(lseg)) {
+ /*
+ * FF_FLAGS_NO_IO_THRU_MDS: force fresh LAYOUTGET,
+ * never fall through to MDS I/O.
+ */
+ pnfs_error_mark_layout_for_return(hdr->inode, lseg);
+ return PNFS_TRY_AGAIN;
+ }
trace_pnfs_mds_fallback_write_pagelist(hdr->inode,
hdr->args.offset, hdr->args.count,
IOMODE_RW, NFS_I(hdr->inode)->layout, lseg);
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
index 354a031c69b1..1d4294c6a7ec 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.h
+++ b/fs/nfs/flexfilelayout/flexfilelayout.h
@@ -102,12 +102,16 @@ struct nfs4_ff_layout_segment {
struct nfs4_ff_layout_mirror *mirror_array[];
};
+/* nfs4_flexfile_layout::flags bit indices */
+#define NFS4_FF_HDR_NO_IO_THRU_MDS 0 /* any lseg has had FF_FLAGS_NO_IO_THRU_MDS */
+
struct nfs4_flexfile_layout {
struct pnfs_layout_hdr generic_hdr;
struct pnfs_ds_commit_info commit_info;
struct list_head mirrors;
struct list_head error_list; /* nfs4_ff_layout_ds_err */
ktime_t last_report_time; /* Layoutstat report times */
+ unsigned long flags;
};
struct nfs4_flexfile_layoutreturn_args {
@@ -174,6 +178,18 @@ ff_layout_no_fallback_to_mds(struct pnfs_layout_segment *lseg)
return FF_LAYOUT_LSEG(lseg)->flags & FF_FLAGS_NO_IO_THRU_MDS;
}
+/*
+ * Sticky hdr-level mirror of FF_FLAGS_NO_IO_THRU_MDS so callers that have
+ * no current lseg (e.g. between LAYOUTRETURN and the next LAYOUTGET) can
+ * still honor the no-MDS-fallback policy.
+ */
+static inline bool
+ff_layout_hdr_no_fallback_to_mds(struct pnfs_layout_hdr *lo)
+{
+ return test_bit(NFS4_FF_HDR_NO_IO_THRU_MDS,
+ &FF_LAYOUT_FROM_HDR(lo)->flags);
+}
+
static inline bool
ff_layout_no_read_on_rw(struct pnfs_layout_segment *lseg)
{
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 90bd2afe23da..e4aa1d0ccfa2 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -1375,8 +1375,6 @@ _pnfs_return_layout(struct inode *ino)
pnfs_clear_layoutcommit(ino, &tmp_list);
pnfs_mark_matching_lsegs_return(lo, &tmp_list, &range, 0);
- if (NFS_SERVER(ino)->pnfs_curr_ld->return_range)
- NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range);
/* Don't send a LAYOUTRETURN if list was initially empty */
if (!test_bit(NFS_LAYOUT_RETURN_REQUESTED, &lo->plh_flags) ||
@@ -1388,6 +1386,8 @@ _pnfs_return_layout(struct inode *ino)
send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL);
spin_unlock(&ino->i_lock);
+ if (NFS_SERVER(ino)->pnfs_curr_ld->return_range)
+ NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo, &range);
if (send)
status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, true);
out_wait_layoutreturn:
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index ed411897e33f..bf0b6b91a1ec 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -134,10 +134,32 @@ static void nfs_readpage_release(struct nfs_page *req, int error)
static void nfs_page_group_set_uptodate(struct nfs_page *req)
{
- if (nfs_page_group_sync_on_bit(req, PG_UPTODATE))
+ bool uptodate = false;
+
+ nfs_page_group_lock(req);
+ if (!test_bit(PG_READ_FAILED, &req->wb_head->wb_flags) &&
+ nfs_page_group_sync_on_bit_locked(req, PG_UPTODATE))
+ uptodate = true;
+ nfs_page_group_unlock(req);
+
+ if (uptodate)
folio_mark_uptodate(nfs_page_to_folio(req));
}
+static void nfs_page_group_mark_read_failed(struct nfs_page *req)
+{
+ struct nfs_page *tmp;
+
+ nfs_page_group_lock(req);
+ set_bit(PG_READ_FAILED, &req->wb_head->wb_flags);
+ tmp = req;
+ do {
+ clear_bit(PG_UPTODATE, &tmp->wb_flags);
+ tmp = tmp->wb_this_page;
+ } while (tmp != req);
+ nfs_page_group_unlock(req);
+}
+
static void nfs_read_completion(struct nfs_pgio_header *hdr)
{
unsigned long bytes = 0;
@@ -174,6 +196,7 @@ static void nfs_read_completion(struct nfs_pgio_header *hdr)
if (bytes <= hdr->good_bytes)
nfs_page_group_set_uptodate(req);
else {
+ nfs_page_group_mark_read_failed(req);
error = hdr->error;
xchg(&nfs_req_openctx(req)->error, error);
}
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 308214378fd3..84bb200e24ad 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -242,9 +242,7 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
BUG_ON(!ls->ls_file);
if (nfsd4_layout_setlease(ls)) {
- nfsd_file_put(ls->ls_file);
- put_nfs4_file(fp);
- kmem_cache_free(nfs4_layout_stateid_cache, ls);
+ nfs4_put_stid(stp);
return NULL;
}
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 017044c27d38..f4060d0cec8a 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -167,24 +167,13 @@ legacy_recdir_name_error(struct nfs4_client *clp, int error)
static void
__nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
- const char *dname, int len, struct nfsd_net *nn)
+ char *dname, struct nfsd_net *nn)
{
- struct xdr_netobj name;
+ struct xdr_netobj name = { .len = strlen(dname), .data = dname };
struct xdr_netobj princhash = { .len = 0, .data = NULL };
struct nfs4_client_reclaim *crp;
- name.data = kmemdup(dname, len, GFP_KERNEL);
- if (!name.data) {
- dprintk("%s: failed to allocate memory for name.data!\n",
- __func__);
- return;
- }
- name.len = len;
crp = nfs4_client_to_reclaim(name, princhash, nn);
- if (!crp) {
- kfree(name.data);
- return;
- }
crp->cr_clp = clp;
}
@@ -240,8 +229,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
inode_unlock(d_inode(dir));
if (status == 0) {
if (nn->in_grace)
- __nfsd4_create_reclaim_record_grace(clp, dname,
- HEXDIR_LEN, nn);
+ __nfsd4_create_reclaim_record_grace(clp, dname, nn);
vfs_fsync(nn->rec_file, 0);
} else {
printk(KERN_ERR "NFSD: failed to write recovery record"
@@ -254,7 +242,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
nfs4_reset_creds(original_cred);
}
-typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
+typedef int (recdir_func)(struct dentry *, char *, struct nfsd_net *);
struct name_list {
char name[HEXDIR_LEN];
@@ -308,23 +296,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
}
status = iterate_dir(nn->rec_file, &ctx.ctx);
- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
- if (!status) {
- struct dentry *dentry;
- dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
- if (IS_ERR(dentry)) {
- status = PTR_ERR(dentry);
- break;
- }
- status = f(dir, dentry, nn);
- dput(dentry);
- }
+ if (!status)
+ status = f(dir, entry->name, nn);
+
list_del(&entry->list);
kfree(entry);
}
- inode_unlock(d_inode(dir));
nfs4_reset_creds(original_cred);
list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
@@ -422,18 +401,19 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
}
static int
-purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
+purge_old(struct dentry *parent, char *cname, struct nfsd_net *nn)
{
int status;
+ struct dentry *child;
struct xdr_netobj name;
- if (child->d_name.len != HEXDIR_LEN - 1) {
- printk("%s: illegal name %pd in recovery directory\n",
- __func__, child);
+ if (strlen(cname) != HEXDIR_LEN - 1) {
+ printk("%s: illegal name %s in recovery directory\n",
+ __func__, cname);
/* Keep trying; maybe the others are OK: */
return 0;
}
- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
+ name.data = kstrdup(cname, GFP_KERNEL);
if (!name.data) {
dprintk("%s: failed to allocate memory for name.data!\n",
__func__);
@@ -443,10 +423,17 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
if (nfs4_has_reclaimed_state(name, nn))
goto out_free;
- status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
- if (status)
- printk("failed to remove client recovery directory %pd\n",
- child);
+ inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
+ child = lookup_one_len(cname, parent, HEXDIR_LEN-1);
+ if (!IS_ERR(child)) {
+ status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
+ if (status)
+ printk("failed to remove client recovery directory %pd\n",
+ child);
+ dput(child);
+ }
+ inode_unlock(d_inode(parent));
+
out_free:
kfree(name.data);
out:
@@ -477,27 +464,18 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn)
}
static int
-load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
+load_recdir(struct dentry *parent, char *cname, struct nfsd_net *nn)
{
- struct xdr_netobj name;
+ struct xdr_netobj name = { .len = HEXDIR_LEN, .data = cname };
struct xdr_netobj princhash = { .len = 0, .data = NULL };
- if (child->d_name.len != HEXDIR_LEN - 1) {
- printk("%s: illegal name %pd in recovery directory\n",
- __func__, child);
+ if (strlen(cname) != HEXDIR_LEN - 1) {
+ printk("%s: illegal name %s in recovery directory\n",
+ __func__, cname);
/* Keep trying; maybe the others are OK: */
return 0;
}
- name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
- if (!name.data) {
- dprintk("%s: failed to allocate memory for name.data!\n",
- __func__);
- goto out;
- }
- name.len = HEXDIR_LEN;
- if (!nfs4_client_to_reclaim(name, princhash, nn))
- kfree(name.data);
-out:
+ nfs4_client_to_reclaim(name, princhash, nn);
return 0;
}
@@ -793,6 +771,8 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
{
uint8_t cmd, princhashlen;
struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
+ char *namecopy __free(kfree) = NULL;
+ char *princhashcopy __free(kfree) = NULL;
uint16_t namelen;
struct cld_net *cn = nn->cld_net;
@@ -811,20 +791,20 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
dprintk("%s: namelen should not be zero", __func__);
return -EINVAL;
}
- name.data = memdup_user(&ci->cc_name.cn_id, namelen);
- if (IS_ERR(name.data))
- return PTR_ERR(name.data);
+ namecopy = memdup_user(&ci->cc_name.cn_id, namelen);
+ if (IS_ERR(namecopy))
+ return PTR_ERR(namecopy);
+ name.data = namecopy;
name.len = namelen;
if (get_user(princhashlen, &ci->cc_princhash.cp_len))
return -EFAULT;
if (princhashlen > 0) {
- princhash.data = memdup_user(
- &ci->cc_princhash.cp_data,
- princhashlen);
- if (IS_ERR(princhash.data)) {
- kfree(name.data);
- return PTR_ERR(princhash.data);
- }
+ princhashcopy = memdup_user(
+ &ci->cc_princhash.cp_data,
+ princhashlen);
+ if (IS_ERR(princhashcopy))
+ return PTR_ERR(princhashcopy);
+ princhash.data = princhashcopy;
princhash.len = princhashlen;
} else
princhash.len = 0;
@@ -838,21 +818,21 @@ __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
dprintk("%s: namelen should not be zero", __func__);
return -EINVAL;
}
- name.data = memdup_user(&cnm->cn_id, namelen);
- if (IS_ERR(name.data))
- return PTR_ERR(name.data);
+ namecopy = memdup_user(&cnm->cn_id, namelen);
+ if (IS_ERR(namecopy))
+ return PTR_ERR(namecopy);
+ name.data = namecopy;
name.len = namelen;
}
+#ifdef CONFIG_NFSD_V4
if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
name.len = name.len - 5;
- memmove(name.data, name.data + 5, name.len);
+ name.data = name.data + 5;
cn->cn_has_legacy = true;
}
- if (!nfs4_client_to_reclaim(name, princhash, nn)) {
- kfree(name.data);
- kfree(princhash.data);
+#endif
+ if (!nfs4_client_to_reclaim(name, princhash, nn))
return -EFAULT;
- }
return nn->client_tracking_ops->msglen;
}
return -EFAULT;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c0dd50b59ad1..f21878a7f393 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -8093,9 +8093,6 @@ nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn)
/*
* failure => all reset bets are off, nfserr_no_grace...
- *
- * The caller is responsible for freeing name.data if NULL is returned (it
- * will be freed in nfs4_remove_reclaim_record in the normal case).
*/
struct nfs4_client_reclaim *
nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
@@ -8104,6 +8101,22 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
unsigned int strhashval;
struct nfs4_client_reclaim *crp;
+ name.data = kmemdup(name.data, name.len, GFP_KERNEL);
+ if (!name.data) {
+ dprintk("%s: failed to allocate memory for name.data!\n",
+ __func__);
+ return NULL;
+ }
+ if (princhash.len) {
+ princhash.data = kmemdup(princhash.data, princhash.len, GFP_KERNEL);
+ if (!princhash.data) {
+ dprintk("%s: failed to allocate memory for princhash.data!\n",
+ __func__);
+ kfree(name.data);
+ return NULL;
+ }
+ } else
+ princhash.data = NULL;
crp = alloc_reclaim();
if (crp) {
strhashval = clientstr_hashval(name);
@@ -8115,6 +8128,9 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash,
crp->cr_princhash.len = princhash.len;
crp->cr_clp = NULL;
nn->reclaim_str_hashtbl_size++;
+ } else {
+ kfree(name.data);
+ kfree(princhash.data);
}
return crp;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ae1f43eb515a..5b773fa1c557 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -338,6 +338,24 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
return err;
}
+static void
+commit_reset_write_verifier(struct nfsd_net *nn, struct svc_rqst *rqstp,
+ int err)
+{
+ switch (err) {
+ case -EAGAIN:
+ case -ESTALE:
+ /*
+ * Neither of these are the result of a problem with
+ * durable storage, so avoid a write verifier reset.
+ */
+ break;
+ default:
+ nfsd_reset_write_verifier(nn);
+ trace_nfsd_writeverf_reset(nn, rqstp, err);
+ }
+}
+
/*
* Commit metadata changes to stable storage.
*/
@@ -659,8 +677,7 @@ __be32 nfsd4_clone_file_range(struct svc_rqst *rqstp,
&nfsd4_get_cstate(rqstp)->current_fh,
dst_pos,
count, status);
- nfsd_reset_write_verifier(nn);
- trace_nfsd_writeverf_reset(nn, rqstp, status);
+ commit_reset_write_verifier(nn, rqstp, status);
ret = nfserrno(status);
}
}
@@ -1177,23 +1194,23 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
host_err = vfs_iter_write(file, &iter, &pos, flags);
file_end_write(file);
if (host_err < 0) {
- nfsd_reset_write_verifier(nn);
- trace_nfsd_writeverf_reset(nn, rqstp, host_err);
+ commit_reset_write_verifier(nn, rqstp, host_err);
goto out_nfserr;
}
*cnt = host_err;
nfsd_stats_io_write_add(nn, exp, *cnt);
fsnotify_modify(file);
host_err = filemap_check_wb_err(file->f_mapping, since);
- if (host_err < 0)
+ if (host_err < 0) {
+ nfsd_reset_write_verifier(nn);
+ trace_nfsd_writeverf_reset(nn, rqstp, host_err);
goto out_nfserr;
+ }
if (stable && use_wgather) {
host_err = wait_for_concurrent_writes(file);
- if (host_err < 0) {
- nfsd_reset_write_verifier(nn);
- trace_nfsd_writeverf_reset(nn, rqstp, host_err);
- }
+ if (host_err < 0)
+ commit_reset_write_verifier(nn, rqstp, host_err);
}
out_nfserr:
@@ -1330,14 +1347,17 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
nfsd_copy_write_verifier(verf, nn);
err2 = filemap_check_wb_err(nf->nf_file->f_mapping,
since);
+ if (err2 < 0) {
+ nfsd_reset_write_verifier(nn);
+ trace_nfsd_writeverf_reset(nn, rqstp, err2);
+ }
err = nfserrno(err2);
break;
case -EINVAL:
err = nfserr_notsupp;
break;
default:
- nfsd_reset_write_verifier(nn);
- trace_nfsd_writeverf_reset(nn, rqstp, err2);
+ commit_reset_write_verifier(nn, rqstp, err2);
err = nfserrno(err2);
}
} else
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index d4a98ac72b8e..b1730b1d88ba 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -2504,12 +2504,33 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
struct nilfs_sc_info *sci = nilfs->ns_writer;
struct nilfs_transaction_info ti;
int err;
+ size_t i, nfreesegs = argv[4].v_nmembs;
+ __u64 *segnumv = kbufs[4];
if (unlikely(!sci))
return -EROFS;
nilfs_transaction_lock(sb, &ti, 1);
+ /*
+ * Validate segment numbers under ns_segctor_sem (held for write
+ * by nilfs_transaction_lock above) so the check is serialized
+ * against nilfs_ioctl_resize(), which can modify ns_nsegments.
+ * Rejecting bad input here, before any segment-cleaning work
+ * begins, avoids the per-element diagnostic path inside
+ * nilfs_sufile_updatev() that would otherwise run under this
+ * same lock and stall concurrent readers.
+ */
+ for (i = 0; i < nfreesegs; i++) {
+ if (segnumv[i] >= nilfs->ns_nsegments) {
+ nilfs_err(sb,
+ "Segment number %llu to be freed is out of range",
+ (unsigned long long)segnumv[i]);
+ err = -EINVAL;
+ goto bail_unlock;
+ }
+ }
+
err = nilfs_mdt_save_to_shadow_map(nilfs->ns_dat);
if (unlikely(err))
goto out_unlock;
@@ -2550,6 +2571,7 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
sci->sc_freesegs = NULL;
sci->sc_nfreesegs = 0;
nilfs_mdt_clear_shadow_map(nilfs->ns_dat);
+ bail_unlock:
nilfs_transaction_unlock(sb);
return err;
}
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index a5d1fa4e7552..e14039ac792f 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -1086,8 +1086,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_time_gran = 1;
sb->s_max_links = NILFS_LINK_MAX;
- sb->s_bdi = bdi_get(sb->s_bdev->bd_disk->bdi);
-
err = load_nilfs(nilfs, sb);
if (err)
goto failed_nilfs;
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index 8f033e30e0d7..a590c5418613 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -1045,6 +1045,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
if (err)
goto out;
}
+
+ if (vcn0 < svcn || evcn1 <= vcn0) {
+ struct ATTRIB *attr2;
+
+ attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
+ 0, &vcn0, &mi);
+ if (!attr2) {
+ err = -EINVAL;
+ goto out;
+ }
+ err = attr_load_runs(attr2, ni, run, NULL);
+ if (err)
+ goto out;
+ }
}
if (vcn + to_alloc > asize)
diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
index 894fd44164b4..4bf851273e33 100644
--- a/fs/ntfs3/dir.c
+++ b/fs/ntfs3/dir.c
@@ -623,7 +623,7 @@ const struct file_operations ntfs_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = ntfs_readdir,
- .fsync = generic_file_fsync,
+ .fsync = ntfs_file_fsync,
.open = ntfs_file_open,
};
// clang-format on
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 212737a816d7..3eee810c69e2 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -371,8 +371,8 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
{
struct super_block *sb = inode->i_sb;
struct ntfs_inode *ni = ntfs_i(inode);
- int err, dirty = 0;
u64 new_valid;
+ int err;
if (!S_ISREG(inode->i_mode))
return 0;
@@ -388,7 +388,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
}
new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
-
truncate_setsize(inode, new_size);
ni_lock(ni);
@@ -402,20 +401,19 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
ni->i_valid = new_valid;
ni_unlock(ni);
+ if (unlikely(err))
+ return err;
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
inode->i_mtime = inode_set_ctime_current(inode);
if (!IS_DIRSYNC(inode)) {
- dirty = 1;
+ mark_inode_dirty(inode);
} else {
err = ntfs_sync_inode(inode);
if (err)
return err;
}
- if (dirty)
- mark_inode_dirty(inode);
-
/*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/
return 0;
@@ -1224,6 +1222,42 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
return err;
}
+/*
+ * ntfs_file_fsync - file_operations::fsync
+ */
+int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct inode *inode = file_inode(file);
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+ int err, ret;
+
+ if (unlikely(ntfs3_forced_shutdown(sb)))
+ return -EIO;
+
+ ret = file_write_and_wait_range(file, start, end);
+ if (ret)
+ return ret;
+
+ ret = write_inode_now(inode, !datasync);
+
+ if (!ret) {
+ ret = ni_write_parents(ntfs_i(inode), !datasync);
+ }
+
+ if (!ret) {
+ ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
+ ntfs_update_mftmirr(sbi, false);
+ }
+
+ err = sync_blockdev(sb->s_bdev);
+ if (unlikely(err && !ret))
+ ret = err;
+ if (!ret)
+ ret = blkdev_issue_flush(sb->s_bdev);
+ return ret;
+}
+
// clang-format off
const struct inode_operations ntfs_file_inode_operations = {
.getattr = ntfs_getattr,
@@ -1245,7 +1279,7 @@ const struct file_operations ntfs_file_operations = {
.splice_read = ntfs_file_splice_read,
.mmap = ntfs_file_mmap,
.open = ntfs_file_open,
- .fsync = generic_file_fsync,
+ .fsync = ntfs_file_fsync,
.splice_write = iter_file_splice_write,
.fallocate = ntfs_fallocate,
.release = ntfs_file_release,
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 06b1d82b5997..c265710d25b4 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -3145,6 +3145,57 @@ bool ni_is_dirty(struct inode *inode)
return false;
}
+/*
+ * ni_write_parents
+ *
+ * Helper function for ntfs_file_fsync.
+ */
+int ni_write_parents(struct ntfs_inode *ni, int sync)
+{
+ int err = 0;
+ struct ATTRIB *attr = NULL;
+ struct ATTR_LIST_ENTRY *le = NULL;
+ struct ntfs_sb_info *sbi = ni->mi.sbi;
+ struct super_block *sb = sbi->sb;
+
+ while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
+ NULL))) {
+ struct inode *dir;
+ struct ATTR_FILE_NAME *fname;
+
+ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
+ if (!fname)
+ continue;
+
+ /* Check simple case when parent inode equals current inode. */
+ if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
+ if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
+ ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
+ err = -EINVAL;
+ }
+ continue;
+ }
+
+ dir = ntfs_iget5(sb, &fname->home, NULL);
+ if (IS_ERR(dir)) {
+ ntfs_inode_warn(
+ &ni->vfs_inode,
+ "failed to open parent directory r=%lx to write",
+ (long)ino_get(&fname->home));
+ continue;
+ }
+
+ if (!is_bad_inode(dir)) {
+ int err2 = write_inode_now(dir, sync);
+ if (!err)
+ err = err2;
+ }
+ iput(dir);
+ }
+
+ return err;
+}
+
/*
* ni_update_parent
*
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index 9e4394d58f9b..ecb7f9694d70 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -764,8 +764,19 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes)
/*
* Walk through the list headed by the first entry to make
* sure none of the entries are currently being used.
+ *
+ * Bound traversal by ne (rt->used) to defeat a crafted on-disk
+ * cycle in the free chain. Each entry in a legitimate free
+ * list is unique, so a chain that visits more than ne slots
+ * is malformed. Without this guard, an attacker-controlled
+ * RESTART_TABLE with a self-loop or A->B->A cycle whose
+ * offsets satisfy the existing alignment + in-bounds guards
+ * spins forever at mount time.
*/
- for (off = ff; off;) {
+ for (off = ff, i = 0; off; i++) {
+ if (i > ne)
+ return false;
+
if (off == RESTART_ENTRY_ALLOCATED)
return false;
@@ -1170,7 +1181,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo,
goto out;
if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE)
- ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false);
+ ntfs_fix_post_read(&page_buf->rhdr, log->page_size, false);
if (page_buf != *buffer)
memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes);
@@ -3313,6 +3324,17 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
nsize = ALIGN(nsize, 8);
data_off = le16_to_cpu(attr->res.data_off);
+ /*
+ * aoff comes from the on-disk lrh->attr_off. Forbid
+ * writes that begin below the resident attribute's
+ * data_off (which would overwrite the resident header),
+ * and forbid aoff + dlen < data_off, which would make
+ * the data_size assignment below underflow to ~4 GiB.
+ */
+ if (aoff < data_off || aoff + dlen < data_off ||
+ aoff + dlen > asize)
+ goto dirty_vol;
+
if (nsize < asize) {
memmove(Add2Ptr(attr, aoff), data, dlen);
data = NULL; // To skip below memmove().
@@ -3364,7 +3386,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
memmove(Add2Ptr(attr, aoff), data, dlen);
if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
- attr_run(attr), &t64)) {
+ attr_run(attr),
+ le32_to_cpu(attr->size) -
+ le16_to_cpu(attr->nres.run_off),
+ &t64)) {
goto dirty_vol;
}
@@ -3498,6 +3523,18 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
+ /*
+ * e->view.data_off and dlen come from the on-disk
+ * INDEX_ROOT entry / LRH. The neighbouring read sites
+ * (e.g. fs/ntfs3/index.c) check that
+ * view.data_off + view.data_size <= e->size; mirror that
+ * bound here so the memmove cannot reach past the entry.
+ */
+ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) ||
+ le16_to_cpu(e->view.data_off) + dlen >
+ le16_to_cpu(e->size))
+ goto dirty_vol;
+
memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
mi->dirty = true;
@@ -3571,9 +3608,23 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
}
e1 = Add2Ptr(e, esize);
- nsize = esize;
used = le32_to_cpu(hdr->used);
+ /*
+ * Reject crafted entries whose e->size makes e + esize
+ * point past the INDEX_HDR's used boundary. Without this,
+ * PtrOffset(e1, hdr + used) underflows to a quasi-infinite
+ * size_t when fed to the memmove() below.
+ *
+ * Also reject esize == 0: memmove(e, e, ...) is a no-op and
+ * leaves hdr->used unchanged, masking the crafted entry.
+ */
+ if (!esize || Add2Ptr(e, esize) > Add2Ptr(hdr, used) ||
+ PtrOffset(e1, Add2Ptr(hdr, used)) < esize)
+ goto dirty_vol;
+
+ nsize = esize;
+
memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used)));
hdr->used = cpu_to_le32(used - nsize);
@@ -3690,6 +3741,12 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
goto dirty_vol;
}
+ /* See UpdateRecordDataRoot for the rationale. */
+ if (le16_to_cpu(e->view.data_off) > le16_to_cpu(e->size) ||
+ le16_to_cpu(e->view.data_off) + dlen >
+ le16_to_cpu(e->size))
+ goto dirty_vol;
+
memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen);
a_dirty = true;
@@ -3797,11 +3854,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
log->l_size = log->orig_file_size = ni->vfs_inode.i_size;
/* Get the size of page. NOTE: To replay we can use default page. */
-#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2
log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, true);
-#else
- log->page_size = norm_file_page(PAGE_SIZE, &log->l_size, false);
-#endif
if (!log->page_size) {
err = -EINVAL;
goto out;
@@ -3939,9 +3992,28 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
*/
t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size);
if (log->page_size != t32) {
+ u32 old_page_size = log->page_size;
+
log->l_size = log->orig_file_size;
log->page_size = norm_file_page(t32, &log->l_size,
t32 == DefaultLogPageSize);
+
+ /*
+ * If the adopted on-disk page size is larger than the size used
+ * to allocate one_page_buf above, grow the scratch buffer so a
+ * later read_log_page() cannot overflow it.
+ */
+ if (log->page_size > old_page_size) {
+ void *buf;
+
+ buf = krealloc(log->one_page_buf, log->page_size,
+ GFP_NOFS);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ log->one_page_buf = buf;
+ }
}
if (log->page_size != t32 ||
@@ -4220,13 +4292,26 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
if (rst->major_ver)
goto end_conv_1;
+ t16 = le16_to_cpu(dptbl->size);
+ if (t16 < sizeof(struct DIR_PAGE_ENTRY)) {
+ log->set_dirty = true;
+ goto out;
+ }
+
+ t32 = (t16 - sizeof(struct DIR_PAGE_ENTRY)) / sizeof(u64);
+
dp = NULL;
while ((dp = enum_rstbl(dptbl, dp))) {
struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp;
- // NOTE: Danger. Check for of boundary.
+ u32 lcns = le32_to_cpu(dp->lcns_follow);
+
+ if (lcns > t32) {
+ log->set_dirty = true;
+ goto out;
+ }
+
memmove(&dp->vcn, &dp0->vcn_low,
- 2 * sizeof(u64) +
- le32_to_cpu(dp->lcns_follow) * sizeof(u64));
+ 2 * sizeof(u64) + lcns * sizeof(u64));
}
end_conv_1:
@@ -4549,12 +4634,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
* whole routine a loop, case Lcns do not fit below.
*/
t16 = le16_to_cpu(lrh->lcns_follow);
+ t32 = le32_to_cpu(dp->lcns_follow);
+ if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * find_dp() only validates that target_vcn is the first
+ * cluster covered by dp. The walk through lrh->lcns_follow
+ * further entries must stay within the allocated
+ * dp->page_lcns[] array, which is sized by dp->lcns_follow.
+ */
+ if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 >
+ le32_to_cpu(dp->lcns_follow)) {
+ err = -EINVAL;
+ log->set_dirty = true;
+ goto out;
+ }
+
for (i = 0; i < t16; i++) {
size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
le64_to_cpu(dp->vcn));
+ if (j >= t32 || i >= t32 - j) {
+ err = -EINVAL;
+ goto out;
+ }
dp->page_lcns[j + i] = lrh->page_lcns[i];
}
-
goto next_log_record_analyze;
case DeleteDirtyClusters: {
diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index 4330c2b39e50..1e468cddb337 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -1745,6 +1745,22 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
hdr_used = le32_to_cpu(hdr->used);
hdr_total = le32_to_cpu(hdr->total);
+ /*
+ * The destination INDEX_BUFFER has 'hdr_total' bytes of payload
+ * available after the header, of which 'hdr_used' are already
+ * consumed by the single terminal END entry installed by
+ * indx_new(). A crafted image can present a resident root whose
+ * non-last entries (summing to 'to_move') exceed what fits in
+ * this buffer; copying them unchecked would overrun the
+ * kmalloc(1u << indx->index_bits) allocation backing the new
+ * buffer. Reject the copy in that case.
+ */
+ if (to_move > hdr_total - hdr_used) {
+ err = -EINVAL;
+ ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
+ goto out_put_n;
+ }
+
/* Copy root entries into new buffer. */
hdr_insert_head(hdr, re, to_move);
@@ -1849,6 +1865,20 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
memcpy(up_e, sp, sp_size);
used1 = le32_to_cpu(hdr1->used);
+
+ /*
+ * hdr_find_split does not validate per-entry sizes, so a crafted
+ * NTFS_DE whose le16 size field is out of range can place sp such
+ * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this
+ * guard the u32 'used = used1 - to_copy - sp_size' underflows and
+ * the subsequent memmove count becomes a near-4-GiB value,
+ * triggering an out-of-bounds kernel write.
+ */
+ if (PtrOffset(hdr1, sp) + sp_size > used1) {
+ err = -EINVAL;
+ goto out;
+ }
+
hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS);
if (!hdr1_saved) {
err = -ENOMEM;
@@ -2016,13 +2046,21 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
static struct indx_node *indx_find_buffer(struct ntfs_index *indx,
struct ntfs_inode *ni,
const struct INDEX_ROOT *root,
- __le64 vbn, struct indx_node *n)
+ __le64 vbn, struct indx_node *n,
+ int depth)
{
int err;
const struct NTFS_DE *e;
struct indx_node *r;
const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr;
+ /*
+ * Limit recursion depth to prevent stack overflow from crafted
+ * images. Use the same bound as the fnd->nodes array (20).
+ */
+ if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes))
+ return ERR_PTR(-EINVAL);
+
/* Step 1: Scan one level. */
for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) {
if (!e)
@@ -2043,7 +2081,8 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx,
if (err)
return ERR_PTR(err);
- r = indx_find_buffer(indx, ni, root, vbn, n);
+ r = indx_find_buffer(indx, ni, root, vbn, n,
+ depth + 1);
if (r)
return r;
}
@@ -2448,7 +2487,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
fnd_clear(fnd);
- in = indx_find_buffer(indx, ni, root, sub_vbn, NULL);
+ in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0);
if (IS_ERR(in)) {
err = PTR_ERR(in);
goto out;
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index 787e15ba0ca4..fff604cd9527 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -715,6 +715,14 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
+ loff_t vbo = folio_pos(folio);
+
+ if (ni->i_valid <= vbo) {
+ folio_zero_range(folio, 0, folio_size(folio));
+ folio_mark_uptodate(folio);
+ folio_unlock(folio);
+ return 0;
+ }
if (is_resident(ni)) {
ni_lock(ni);
diff --git a/fs/ntfs3/lznt.c b/fs/ntfs3/lznt.c
index fdc9b2ebf341..f818d9785004 100644
--- a/fs/ntfs3/lznt.c
+++ b/fs/ntfs3/lznt.c
@@ -240,7 +240,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr,
if (up - unc > LZNT_CHUNK_SIZE)
return -EINVAL;
/* Correct index */
- while (unc + s_max_off[index] < up)
+ while (index < ARRAY_SIZE(s_max_off) - 1 && unc + s_max_off[index] < up)
index += 1;
/* Check the current flag for zero. */
diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
index 1b0c075c1485..dc41b80cfc58 100644
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@ -341,7 +341,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
ntfs_sync_inode(dir);
if (IS_DIRSYNC(new_dir))
- ntfs_sync_inode(inode);
+ ntfs_sync_inode(new_dir);
}
if (dir_ni != new_dir_ni)
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 31f57ee9a1a9..68c4fe1bf8a5 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -503,6 +503,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ntfs_file_open(struct inode *inode, struct file *file);
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
+int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
extern const struct inode_operations ntfs_special_inode_operations;
extern const struct inode_operations ntfs_file_inode_operations;
extern const struct file_operations ntfs_file_operations;
@@ -580,6 +581,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
bool ni_is_dirty(struct inode *inode);
+int ni_write_parents(struct ntfs_inode *ni, int sync);
/* Globals from fslog.c */
bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
@@ -814,7 +816,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
#else
#define run_unpack_ex run_unpack
#endif
-int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
+int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
+ u64 *highest_vcn);
int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
/* Globals from super.c */
diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c
index f2451eaf928a..535a094e822e 100644
--- a/fs/ntfs3/run.c
+++ b/fs/ntfs3/run.c
@@ -1155,18 +1155,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
* Return the highest vcn from a mapping pairs array
* it used while replaying log file.
*/
-int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn)
+int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size,
+ u64 *highest_vcn)
{
+ const u8 *run_last = run_buf + run_buf_size;
u64 vcn64 = vcn;
u8 size_size;
- while ((size_size = *run_buf & 0xF)) {
+ while (run_buf < run_last && (size_size = *run_buf & 0xF)) {
u8 offset_size = *run_buf++ >> 4;
u64 len;
if (size_size > 8 || offset_size > 8)
return -EINVAL;
+ if (run_buf + size_size + offset_size > run_last)
+ return -EINVAL;
+
len = run_unpack_s64(run_buf, size_size, 0);
if (!len)
return -EINVAL;
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index af40f0da3a95..bcdd9f25b2f5 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -7546,7 +7546,7 @@ int ocfs2_trim_mainbm(struct super_block *sb, struct fstrim_range *range)
len = range->len >> osb->s_clustersize_bits;
minlen = range->minlen >> osb->s_clustersize_bits;
- if (minlen >= osb->bitmap_cpg || range->len < sb->s_blocksize)
+ if (minlen >= osb->bitmap_cpg || range->len < osb->s_clustersize)
return -EINVAL;
trace_ocfs2_trim_mainbm(start, len, minlen);
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 5365fc7675de..680d7fa7e148 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -2400,6 +2400,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
unlock:
up_write(&oi->ip_alloc_sem);
+ if (data_ac) {
+ ocfs2_free_alloc_context(data_ac);
+ data_ac = NULL;
+ }
+ if (meta_ac) {
+ ocfs2_free_alloc_context(meta_ac);
+ meta_ac = NULL;
+ }
+
/* everything looks good, let's start the cleanup */
if (!ret && dwc->dw_orphaned) {
BUG_ON(dwc->dw_writer_pid != task_pid_nr(current));
@@ -2411,10 +2420,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
ocfs2_inode_unlock(inode, 1);
brelse(di_bh);
out:
- if (data_ac)
- ocfs2_free_alloc_context(data_ac);
- if (meta_ac)
- ocfs2_free_alloc_context(meta_ac);
ocfs2_run_deallocs(osb, &dealloc);
ocfs2_dio_free_write_ctx(inode, dwc);
diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c
index 45ca3cb7c097..71f3787bf82f 100644
--- a/fs/ocfs2/buffer_head_io.c
+++ b/fs/ocfs2/buffer_head_io.c
@@ -350,8 +350,6 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
wait_on_buffer(bh);
put_bh(bh);
bhs[i] = NULL;
- } else if (bh && buffer_uptodate(bh)) {
- clear_buffer_uptodate(bh);
}
continue;
}
@@ -380,8 +378,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
BUG_ON(buffer_jbd(bh));
clear_buffer_needs_validate(bh);
status = validate(sb, bh);
- if (status)
+ if (status) {
+ if (buffer_uptodate(bh))
+ clear_buffer_uptodate(bh);
goto read_failure;
+ }
}
}
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
index be5e9ed7da8d..e62ed98df7de 100644
--- a/fs/ocfs2/dlm/dlmdebug.c
+++ b/fs/ocfs2/dlm/dlmdebug.c
@@ -612,6 +612,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file)
struct dlm_ctxt *dlm = inode->i_private;
struct debug_lockres *dl;
void *buf;
+ int status = -ENOMEM;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
@@ -624,16 +625,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file)
dl->dl_len = PAGE_SIZE;
dl->dl_buf = buf;
- dlm_grab(dlm);
- dl->dl_ctxt = dlm;
+ /* ->release uses dl_ctxt after open, so it needs a real pin. */
+ dl->dl_ctxt = dlm_grab(dlm);
+ if (!dl->dl_ctxt) {
+ status = -ENOENT;
+ goto bailseq;
+ }
return 0;
+bailseq:
+ seq_release_private(inode, file);
bailfree:
kfree(buf);
bail:
- mlog_errno(-ENOMEM);
- return -ENOMEM;
+ if (status != -ENOENT)
+ mlog_errno(status);
+ return status;
}
static int debug_lockres_release(struct inode *inode, struct file *file)
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 9148a98a5fbe..5c9f06090e39 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3135,6 +3135,22 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos)
* - Add last pr/ex unlock times and first lock wait time in usecs
*/
#define OCFS2_DLM_DEBUG_STR_VERSION 4
+
+/*
+ * The debug iterator snapshots lockres by value, so a userspace-stack LVB
+ * pointer copied from the original lockres must be rebased to the copied
+ * lksb before the dump walks the raw bytes.
+ */
+static void ocfs2_dlm_seq_rebase_lvb(struct ocfs2_lock_res *lockres)
+{
+ if (!ocfs2_stack_supports_plocks())
+ return;
+
+ if (lockres->l_lksb.lksb_fsdlm.sb_lvbptr)
+ lockres->l_lksb.lksb_fsdlm.sb_lvbptr =
+ (char *)&lockres->l_lksb + sizeof(struct dlm_lksb);
+}
+
static int ocfs2_dlm_seq_show(struct seq_file *m, void *v)
{
int i;
@@ -3195,6 +3211,7 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v)
lockres->l_blocking);
/* Dump the raw LVB */
+ ocfs2_dlm_seq_rebase_lvb(lockres);
lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
for(i = 0; i < DLM_LVB_LEN; i++)
seq_printf(m, "0x%x\t", lvb[i]);
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 26a0d4ef8829..bbf8dfeeac74 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -66,7 +66,40 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode,
static int ocfs2_filecheck_validate_inode_block(struct super_block *sb,
struct buffer_head *bh);
static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
- struct buffer_head *bh);
+ struct buffer_head *bh);
+
+static bool ocfs2_valid_inode_mode(umode_t mode)
+{
+ return fs_umode_to_ftype(mode) != FT_UNKNOWN;
+}
+
+static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di)
+{
+ umode_t mode = le16_to_cpu(di->i_mode);
+
+ if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
+ return false;
+
+ return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0;
+}
+
+static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb,
+ struct ocfs2_dinode *di)
+{
+ umode_t mode = le16_to_cpu(di->i_mode);
+
+ if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
+ return false;
+ if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL)
+ return false;
+ if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters))
+ return false;
+
+ if (S_ISDIR(mode))
+ return true;
+
+ return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode);
+}
void ocfs2_set_inode_flags(struct inode *inode)
{
@@ -1419,6 +1452,86 @@ int ocfs2_validate_inode_block(struct super_block *sb,
goto bail;
}
+ /*
+ * Reject dinodes whose i_mode does not name one of the seven
+ * canonical POSIX file types. ocfs2_populate_inode() copies
+ * i_mode verbatim into inode->i_mode and then dispatches via
+ * switch (mode & S_IFMT) to file/dir/symlink/special_file iops;
+ * an unrecognised type falls into ocfs2_special_file_iops with
+ * init_special_inode(), which interprets i_rdev. Constrain the
+ * type here so the dispatch only ever sees a value mkfs.ocfs2 /
+ * VFS can produce.
+ */
+ if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: mode 0%o has unknown file type\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode));
+ goto bail;
+ }
+
+ /*
+ * id1.dev1.i_rdev is the device-number arm of the id1 union and
+ * is only meaningful for character and block device inodes. For
+ * any other regular user-visible file type the on-disk value
+ * must be zero. ocfs2_populate_inode() currently runs
+ *
+ * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev));
+ *
+ * unconditionally, before the S_IFMT switch decides whether the
+ * inode is a special file. As a result, an i_rdev value present
+ * on a non-device inode is silently published into the in-core
+ * inode; a subsequent forced re-read or in-core mode mutation
+ * (cluster peer with raw write access to the shared LUN,
+ * on-disk corruption, or a separately forged dinode) can then
+ * expose the attacker-controlled device number to
+ * init_special_inode() without ever showing an unusual i_mode
+ * at validation time.
+ *
+ * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1
+ * and journal1 arms of the same union (allocator i_used /
+ * i_total counters and the journal ij_flags /
+ * ij_recovery_generation pair); those bytes are not an i_rdev
+ * and must not be checked here. Restrict the cross-check to
+ * non-system inodes, which is the full attacker-controllable
+ * surface.
+ */
+ if (ocfs2_dinode_has_unexpected_rdev(di)) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode),
+ (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
+ goto bail;
+ }
+
+ /*
+ * Non-inline directories must not have i_size without allocated
+ * clusters: directory growth adds storage before advancing i_size,
+ * and readdir walks i_size block-by-block. A forged directory
+ * with zero clusters and a huge i_size would repeatedly fault on
+ * holes while advancing through the claimed size.
+ *
+ * Non-inline regular files have the same invariant on non-sparse
+ * volumes. Sparse regular files are different: truncate can
+ * legitimately grow i_size without allocating clusters, so keep
+ * the sparse-alloc carveout for S_IFREG only. System inodes and
+ * inline-data dinodes have their own storage rules.
+ */
+ if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
+ if (S_ISDIR(le16_to_cpu(di->i_mode)))
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ else
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ goto bail;
+ }
+
if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
struct ocfs2_inline_data *data = &di->id2.i_data;
@@ -1515,6 +1628,40 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb,
(unsigned long long)bh->b_blocknr,
le32_to_cpu(di->i_fs_generation));
rc = -OCFS2_FILECHECK_ERR_GENERATION;
+ goto bail;
+ }
+
+ if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+ goto bail;
+ }
+
+ if (ocfs2_dinode_has_unexpected_rdev(di)) {
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode),
+ (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+ goto bail;
+ }
+
+ if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
+ if (S_ISDIR(le16_to_cpu(di->i_mode)))
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ else
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
}
bail:
@@ -1690,4 +1837,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = {
.co_io_lock = ocfs2_inode_cache_io_lock,
.co_io_unlock = ocfs2_inode_cache_io_unlock,
};
-
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index b246e5327111..79fd52d9ba09 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -476,8 +476,12 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks)
*/
int ocfs2_assure_trans_credits(handle_t *handle, int nblocks)
{
- int old_nblks = jbd2_handle_buffer_credits(handle);
+ int old_nblks;
+ if (is_handle_aborted(handle))
+ return -EROFS;
+
+ old_nblks = jbd2_handle_buffer_credits(handle);
trace_ocfs2_assure_trans_credits(old_nblks);
if (old_nblks >= nblocks)
return 0;
@@ -1027,11 +1031,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
struct ocfs2_dinode *fe;
fe = (struct ocfs2_dinode *)bh->b_data;
-
- /* The journal bh on the osb always comes from ocfs2_journal_init()
- * and was validated there inside ocfs2_inode_lock_full(). It's a
- * code bug if we mess it up. */
- BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
+ if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe)))
+ return -EIO;
flags = le32_to_cpu(fe->id1.journal1.ij_flags);
if (dirty)
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index 6397170f302f..f8b3b2a3d630 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode)
if (ocfs2_mount_local(osb))
return;
+ if (!osb->journal)
+ return;
+
if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) {
/* WARNING: This only kicks off a single
* checkpoint. If someone races you and adds more
diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c
index d97ec93ff4c2..fc9aa015e05e 100644
--- a/fs/ocfs2/move_extents.c
+++ b/fs/ocfs2/move_extents.c
@@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh,
u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb,
le64_to_cpu(gd->bg_blkno));
+ *phys_cpos = 0;
+
for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) {
used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap);
@@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh,
last_free_bits++;
if (last_free_bits == move_len) {
- i -= move_len;
+ i = i - move_len + 1;
*goal_bit = i;
*phys_cpos = base_cpos + i;
break;
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 5e3ebbab698a..e8b8262026c8 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -494,8 +494,6 @@ struct ocfs2_super
struct rb_root osb_rf_lock_tree;
struct ocfs2_refcount_tree *osb_ref_tree_lru;
- struct mutex system_file_mutex;
-
/*
* OCFS2 needs to schedule several different types of work which
* require cluster locking, disk I/O, recovery waits, etc. Since these
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index c7bda48b5fb2..d8e288b4bfbf 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb,
if (!rc)
return -ENOMEM;
rc->rc_chunk = chunk;
- rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
+ rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS);
if (!rc->rc_bitmap) {
kfree(rc);
return -ENOMEM;
diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c
index 38c0d6f64fce..eeb1c29b033e 100644
--- a/fs/ocfs2/stack_user.c
+++ b/fs/ocfs2/stack_user.c
@@ -327,18 +327,14 @@ static int ocfs2_control_install_private(struct file *file)
ocfs2_control_this_node = p->op_this_node;
running_proto.pv_major = p->op_proto.pv_major;
running_proto.pv_minor = p->op_proto.pv_minor;
- }
-
-out_unlock:
- mutex_unlock(&ocfs2_control_lock);
-
- if (!rc && set_p) {
- /* We set the global values successfully */
atomic_inc(&ocfs2_control_opened);
ocfs2_control_set_handshake_state(file,
OCFS2_CONTROL_HANDSHAKE_VALID);
}
+out_unlock:
+ mutex_unlock(&ocfs2_control_lock);
+
return rc;
}
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 18dba873a163..cc4bba65f152 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -2053,8 +2053,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
spin_lock_init(&osb->osb_xattr_lock);
ocfs2_init_steal_slots(osb);
- mutex_init(&osb->system_file_mutex);
-
atomic_set(&osb->alloc_stats.moves, 0);
atomic_set(&osb->alloc_stats.local_data, 0);
atomic_set(&osb->alloc_stats.bitmap_data, 0);
diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c
index 53a945da873b..b63af8d64904 100644
--- a/fs/ocfs2/sysfile.c
+++ b/fs/ocfs2/sysfile.c
@@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
} else
arr = get_local_system_inode(osb, type, slot);
- mutex_lock(&osb->system_file_mutex);
if (arr && ((inode = *arr) != NULL)) {
/* get a ref in addition to the array ref */
inode = igrab(inode);
- mutex_unlock(&osb->system_file_mutex);
BUG_ON(!inode);
return inode;
@@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
inode = _ocfs2_get_system_file_inode(osb, type, slot);
/* add one more if putting into array for first time */
- if (arr && inode) {
- *arr = igrab(inode);
- BUG_ON(!*arr);
+ if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) {
+ inode = igrab(inode);
+ BUG_ON(!inode);
}
- mutex_unlock(&osb->system_file_mutex);
return inode;
}
diff --git a/fs/orangefs/dir.c b/fs/orangefs/dir.c
index 9cacce5d55c1..973730006eb8 100644
--- a/fs/orangefs/dir.c
+++ b/fs/orangefs/dir.c
@@ -191,7 +191,8 @@ static int fill_from_part(struct orangefs_dir_part *part,
{
const int offset = sizeof(struct orangefs_readdir_response_s);
struct orangefs_khandle *khandle;
- __u32 *len, padlen;
+ __u32 *len;
+ u64 padlen;
loff_t i;
char *s;
i = ctx->pos & ~PART_MASK;
@@ -215,8 +216,8 @@ static int fill_from_part(struct orangefs_dir_part *part,
* len is the size of the string itself. padlen is the
* total size of the encoded string.
*/
- padlen = (sizeof *len + *len + 1) +
- (8 - (sizeof *len + *len + 1)%8)%8;
+ padlen = (u64)sizeof *len + *len + 1;
+ padlen += (8 - padlen % 8) % 8;
if (part->len < i + padlen + sizeof *khandle)
goto next;
s = (void *)part + offset + i + sizeof *len;
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 363d9331216b..e5001b5fc3f0 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -483,7 +483,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
unsigned long flags;
int exit_code = task->exit_code;
struct signal_struct *sig = task->signal;
- unsigned int seq = 1;
+ int ret;
+
+ ret = down_read_killable(&task->signal->exec_update_lock);
+ if (ret)
+ return ret;
state = *get_task_state(task);
vsize = eip = esp = 0;
@@ -540,10 +544,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
if (permitted && (!whole || num_threads < 2))
wchan = !task_is_running(task);
- do {
- seq++; /* 2 on the 1st/lockless path, otherwise odd */
- flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq);
-
+ scoped_seqlock_read (&sig->stats_lock, ss_lock_irqsave) {
cmin_flt = sig->cmin_flt;
cmaj_flt = sig->cmaj_flt;
cutime = sig->cutime;
@@ -565,8 +566,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
}
rcu_read_unlock();
}
- } while (need_seqretry(&sig->stats_lock, seq));
- done_seqretry_irqrestore(&sig->stats_lock, seq, flags);
+ }
if (whole) {
thread_group_cputime_adjusted(task, &utime, &stime);
@@ -663,6 +663,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
seq_puts(m, " 0");
seq_putc(m, '\n');
+ up_read(&task->signal->exec_update_lock);
if (mm)
mmput(mm);
return 0;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d444155581ca..da5c436ea36f 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -218,33 +218,24 @@ static int get_task_root(struct task_struct *task, struct path *root)
return result;
}
-static int proc_cwd_link(struct dentry *dentry, struct path *path)
+static int proc_cwd_link(struct dentry *dentry, struct path *path,
+ struct task_struct *task)
{
- struct task_struct *task = get_proc_task(d_inode(dentry));
int result = -ENOENT;
- if (task) {
- task_lock(task);
- if (task->fs) {
- get_fs_pwd(task->fs, path);
- result = 0;
- }
- task_unlock(task);
- put_task_struct(task);
+ task_lock(task);
+ if (task->fs) {
+ get_fs_pwd(task->fs, path);
+ result = 0;
}
+ task_unlock(task);
return result;
}
-static int proc_root_link(struct dentry *dentry, struct path *path)
+static int proc_root_link(struct dentry *dentry, struct path *path,
+ struct task_struct *task)
{
- struct task_struct *task = get_proc_task(d_inode(dentry));
- int result = -ENOENT;
-
- if (task) {
- result = get_task_root(task, path);
- put_task_struct(task);
- }
- return result;
+ return get_task_root(task, path);
}
/*
@@ -423,18 +414,24 @@ static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
{
unsigned long wchan;
char symname[KSYM_NAME_LEN];
+ int err;
+ err = down_read_killable(&task->signal->exec_update_lock);
+ if (err)
+ return err;
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
goto print0;
wchan = get_wchan(task);
if (wchan && !lookup_symbol_name(wchan, symname)) {
seq_puts(m, symname);
+ up_read(&task->signal->exec_update_lock);
return 0;
}
print0:
seq_putc(m, '0');
+ up_read(&task->signal->exec_update_lock);
return 0;
}
#endif /* CONFIG_KALLSYMS */
@@ -704,24 +701,7 @@ static int proc_pid_syscall(struct seq_file *m, struct pid_namespace *ns,
/* Here the fs part begins */
/************************************************************************/
-/* permission checks */
-static bool proc_fd_access_allowed(struct inode *inode)
-{
- struct task_struct *task;
- bool allowed = false;
- /* Allow access to a task's file descriptors if it is us or we
- * may use ptrace attach to the process and find out that
- * information.
- */
- task = get_proc_task(inode);
- if (task) {
- allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
- put_task_struct(task);
- }
- return allowed;
-}
-
-int proc_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
{
int error;
@@ -794,7 +774,7 @@ static int proc_pid_permission(struct mnt_idmap *idmap,
static const struct inode_operations proc_def_inode_operations = {
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static int proc_single_show(struct seq_file *m, void *v)
@@ -1778,16 +1758,12 @@ static const struct file_operations proc_pid_set_comm_operations = {
.release = single_release,
};
-static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
+static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
+ struct task_struct *task)
{
- struct task_struct *task;
struct file *exe_file;
- task = get_proc_task(d_inode(dentry));
- if (!task)
- return -ENOENT;
exe_file = get_task_exe_file(task);
- put_task_struct(task);
if (exe_file) {
*exe_path = exe_file->f_path;
path_get(&exe_file->f_path);
@@ -1797,26 +1773,42 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
return -ENOENT;
}
+static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out)
+{
+ struct task_struct *task;
+ int ret;
+
+ task = get_proc_task(inode);
+ if (!task)
+ return -ENOENT;
+ ret = down_read_killable(&task->signal->exec_update_lock);
+ if (ret)
+ goto out_put_task;
+ if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
+ ret = -EACCES;
+ goto out;
+ }
+ ret = PROC_I(inode)->op.proc_get_link(dentry, path_out, task);
+
+out:
+ up_read(&task->signal->exec_update_lock);
+out_put_task:
+ put_task_struct(task);
+ return ret;
+}
+
static const char *proc_pid_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
struct path path;
- int error = -EACCES;
+ int error;
if (!dentry)
return ERR_PTR(-ECHILD);
-
- /* Are we allowed to snoop on the tasks file descriptors? */
- if (!proc_fd_access_allowed(inode))
- goto out;
-
- error = PROC_I(inode)->op.proc_get_link(dentry, &path);
- if (error)
- goto out;
-
- error = nd_jump_link(&path);
-out:
+ error = call_proc_get_link(dentry, inode, &path);
+ if (!error)
+ error = nd_jump_link(&path);
return ERR_PTR(error);
}
@@ -1850,24 +1842,18 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b
struct inode *inode = d_inode(dentry);
struct path path;
- /* Are we allowed to snoop on the tasks file descriptors? */
- if (!proc_fd_access_allowed(inode))
- goto out;
-
- error = PROC_I(inode)->op.proc_get_link(dentry, &path);
- if (error)
- goto out;
-
- error = do_proc_readlink(&path, buffer, buflen);
- path_put(&path);
-out:
+ error = call_proc_get_link(dentry, inode, &path);
+ if (!error) {
+ error = do_proc_readlink(&path, buffer, buflen);
+ path_put(&path);
+ }
return error;
}
const struct inode_operations proc_pid_link_inode_operations = {
.readlink = proc_pid_readlink,
.get_link = proc_pid_get_link,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
@@ -2248,21 +2234,16 @@ static const struct dentry_operations tid_map_files_dentry_operations = {
.d_delete = pid_delete_dentry,
};
-static int map_files_get_link(struct dentry *dentry, struct path *path)
+static int map_files_get_link(struct dentry *dentry, struct path *path,
+ struct task_struct *task)
{
unsigned long vm_start, vm_end;
struct vm_area_struct *vma;
- struct task_struct *task;
struct mm_struct *mm;
int rc;
rc = -ENOENT;
- task = get_proc_task(d_inode(dentry));
- if (!task)
- goto out;
-
mm = get_task_mm(task);
- put_task_struct(task);
if (!mm)
goto out;
@@ -2317,7 +2298,7 @@ proc_map_files_get_link(struct dentry *dentry,
static const struct inode_operations proc_map_files_link_inode_operations = {
.readlink = proc_pid_readlink,
.get_link = proc_map_files_get_link,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static struct dentry *
@@ -2358,17 +2339,15 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
if (!task)
goto out;
- result = ERR_PTR(-EACCES);
- if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
- goto out_put_task;
-
result = ERR_PTR(-ENOENT);
if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
goto out_put_task;
- mm = get_task_mm(task);
- if (!mm)
+ mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+ if (IS_ERR(mm)) {
+ result = ERR_CAST(mm);
goto out_put_task;
+ }
result = ERR_PTR(-EINTR);
if (mmap_read_lock_killable(mm))
@@ -2396,7 +2375,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir,
static const struct inode_operations proc_map_files_inode_operations = {
.lookup = proc_map_files_lookup,
.permission = proc_fd_permission,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static int
@@ -2418,23 +2397,22 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
if (!task)
goto out;
- ret = -EACCES;
- if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
- goto out_put_task;
-
ret = 0;
if (!dir_emit_dots(file, ctx))
goto out_put_task;
- mm = get_task_mm(task);
- if (!mm)
+ mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+ if (IS_ERR(mm)) {
+ ret = PTR_ERR(mm);
+ /* if the task has no mm, the directory should just be empty */
+ if (ret == -ESRCH)
+ ret = 0;
goto out_put_task;
+ }
ret = mmap_read_lock_killable(mm);
- if (ret) {
- mmput(mm);
- goto out_put_task;
- }
+ if (ret)
+ goto out_put_mm;
nr_files = 0;
@@ -2460,8 +2438,7 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
if (!p) {
ret = -ENOMEM;
mmap_read_unlock(mm);
- mmput(mm);
- goto out_put_task;
+ goto out_put_mm;
}
p->start = vma->vm_start;
@@ -2469,7 +2446,6 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
p->mode = vma->vm_file->f_mode;
}
mmap_read_unlock(mm);
- mmput(mm);
for (i = 0; i < nr_files; i++) {
char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
@@ -2486,6 +2462,8 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
ctx->pos++;
}
+out_put_mm:
+ mmput(mm);
out_put_task:
put_task_struct(task);
out:
@@ -2892,7 +2870,7 @@ static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \
static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
.lookup = proc_##LSM##_attr_dir_lookup, \
.getattr = pid_getattr, \
- .setattr = proc_setattr, \
+ .setattr = proc_nochmod_setattr, \
}
#ifdef CONFIG_SECURITY_SMACK
@@ -2951,7 +2929,7 @@ static struct dentry *proc_attr_dir_lookup(struct inode *dir,
static const struct inode_operations proc_attr_dir_inode_operations = {
.lookup = proc_attr_dir_lookup,
.getattr = pid_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
#endif
@@ -3444,7 +3422,7 @@ static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *de
static const struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
.permission = proc_pid_permission,
};
@@ -3644,7 +3622,7 @@ static int proc_tid_comm_permission(struct mnt_idmap *idmap,
}
static const struct inode_operations proc_tid_comm_inode_operations = {
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
.permission = proc_tid_comm_permission,
};
@@ -3775,7 +3753,7 @@ static const struct file_operations proc_tid_base_operations = {
static const struct inode_operations proc_tid_base_inode_operations = {
.lookup = proc_tid_base_lookup,
.getattr = pid_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static struct dentry *proc_task_instantiate(struct dentry *dentry,
@@ -3973,7 +3951,7 @@ static int proc_task_getattr(struct mnt_idmap *idmap,
static const struct inode_operations proc_task_inode_operations = {
.lookup = proc_task_lookup,
.getattr = proc_task_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
.permission = proc_pid_permission,
};
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 4297287f6ca0..ab37819bc875 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -104,7 +104,7 @@ static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode,
static const struct inode_operations proc_fdinfo_file_inode_operations = {
.permission = proc_fdinfo_permission,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static const struct file_operations proc_fdinfo_file_operations = {
@@ -172,24 +172,19 @@ static const struct dentry_operations tid_fd_dentry_operations = {
.d_delete = pid_delete_dentry,
};
-static int proc_fd_link(struct dentry *dentry, struct path *path)
+static int proc_fd_link(struct dentry *dentry, struct path *path,
+ struct task_struct *task)
{
- struct task_struct *task;
int ret = -ENOENT;
-
- task = get_proc_task(d_inode(dentry));
- if (task) {
- unsigned int fd = proc_fd(d_inode(dentry));
- struct file *fd_file;
-
- fd_file = fget_task(task, fd);
- if (fd_file) {
- *path = fd_file->f_path;
- path_get(&fd_file->f_path);
- ret = 0;
- fput(fd_file);
- }
- put_task_struct(task);
+ unsigned int fd = proc_fd(d_inode(dentry));
+ struct file *fd_file;
+
+ fd_file = fget_task(task, fd);
+ if (fd_file) {
+ *path = fd_file->f_path;
+ path_get(&fd_file->f_path);
+ ret = 0;
+ fput(fd_file);
}
return ret;
@@ -374,7 +369,7 @@ const struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
.permission = proc_fd_permission,
.getattr = proc_fd_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
@@ -415,7 +410,7 @@ static int proc_readfdinfo(struct file *file, struct dir_context *ctx)
const struct inode_operations proc_fdinfo_inode_operations = {
.lookup = proc_lookupfdinfo,
.permission = proc_fdinfo_permission,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
const struct file_operations proc_fdinfo_operations = {
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 9eff57c7aef3..91c25a5f770c 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir,
/* Add new node and rebalance tree. */
rb_link_node(&de->subdir_node, parent, new);
rb_insert_color(&de->subdir_node, root);
+ if (S_ISDIR(de->mode))
+ dir->nlink++;
return true;
}
@@ -399,7 +401,6 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
write_unlock(&proc_subdir_lock);
goto out_free_inum;
}
- dir->nlink++;
write_unlock(&proc_subdir_lock);
return dp;
@@ -698,6 +699,8 @@ static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent)
{
rb_erase(&pde->subdir_node, &parent->subdir);
RB_CLEAR_NODE(&pde->subdir_node);
+ if (S_ISDIR(pde->mode))
+ parent->nlink--;
}
/*
@@ -723,8 +726,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
de = NULL;
} else {
pde_erase(de, parent);
- if (S_ISDIR(de->mode))
- parent->nlink--;
}
}
write_unlock(&proc_subdir_lock);
@@ -783,8 +784,6 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
continue;
}
next = de->parent;
- if (S_ISDIR(de->mode))
- next->nlink--;
write_unlock(&proc_subdir_lock);
proc_entry_rundown(de);
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index fe3781360120..b902ba0ebe9c 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -107,7 +107,7 @@ extern struct kmem_cache *proc_dir_entry_cache;
void pde_free(struct proc_dir_entry *pde);
union proc_op {
- int (*proc_get_link)(struct dentry *, struct path *);
+ int (*proc_get_link)(struct dentry *, struct path *, struct task_struct *);
int (*proc_show)(struct seq_file *m,
struct pid_namespace *ns, struct pid *pid,
struct task_struct *task);
@@ -183,8 +183,8 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *,
extern const struct dentry_operations pid_dentry_operations;
extern int pid_getattr(struct mnt_idmap *, const struct path *,
struct kstat *, u32, unsigned int);
-extern int proc_setattr(struct mnt_idmap *, struct dentry *,
- struct iattr *);
+int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr);
extern void proc_pid_evict_inode(struct proc_inode *);
extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t);
extern void pid_update_inode(struct task_struct *, struct inode *);
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 8e159fc78c0a..2a3fc96ca622 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struct dentry *dentry,
if (!task)
return ERR_PTR(-EACCES);
+ error = down_read_killable(&task->signal->exec_update_lock);
+ if (error)
+ goto out_put_task;
+
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
goto out;
@@ -64,6 +68,8 @@ static const char *proc_ns_get_link(struct dentry *dentry,
error = nd_jump_link(&ns_path);
out:
+ up_read(&task->signal->exec_update_lock);
+out_put_task:
put_task_struct(task);
return ERR_PTR(error);
}
@@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
if (!task)
return res;
+ res = down_read_killable(&task->signal->exec_update_lock);
+ if (res)
+ goto out_put_task;
+
if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
res = ns_get_name(name, sizeof(name), task, ns_ops);
if (res >= 0)
res = readlink_copy(buffer, buflen, name);
}
+ up_read(&task->signal->exec_update_lock);
+out_put_task:
put_task_struct(task);
return res;
}
@@ -92,7 +104,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
static const struct inode_operations proc_ns_link_inode_operations = {
.readlink = proc_ns_readlink,
.get_link = proc_ns_get_link,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static struct dentry *proc_ns_instantiate(struct dentry *dentry,
@@ -179,5 +191,5 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
const struct inode_operations proc_ns_dir_inode_operations = {
.lookup = proc_ns_dir_lookup,
.getattr = pid_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index 2ba31b6d68c0..39328b507245 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -321,7 +321,7 @@ static int proc_tgid_net_getattr(struct mnt_idmap *idmap,
const struct inode_operations proc_net_inode_operations = {
.lookup = proc_tgid_net_lookup,
.getattr = proc_tgid_net_getattr,
- .setattr = proc_setattr,
+ .setattr = proc_nochmod_setattr,
};
static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c
index 7233c6a7e6d7..693f609512e6 100644
--- a/fs/smb/client/cifs_swn.c
+++ b/fs/smb/client/cifs_swn.c
@@ -28,10 +28,54 @@ struct cifs_swn_reg {
bool net_name_notify;
bool share_name_notify;
bool ip_notify;
+};
- struct cifs_tcon *tcon;
+struct cifs_swn_reg_info {
+ int id;
+ unsigned int ref_count;
+ const char *net_name;
+ const char *share_name;
+ bool net_name_notify;
+ bool share_name_notify;
+ bool ip_notify;
};
+static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg,
+ struct cifs_swn_reg_info *info)
+{
+ info->id = swnreg->id;
+ info->ref_count = kref_read(&swnreg->ref_count);
+ info->net_name = swnreg->net_name;
+ info->share_name = swnreg->share_name;
+ info->net_name_notify = swnreg->net_name_notify;
+ info->share_name_notify = swnreg->share_name_notify;
+ info->ip_notify = swnreg->ip_notify;
+}
+
+static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg,
+ struct cifs_swn_reg_info *info)
+{
+ cifs_swn_snapshot_reg(swnreg, info);
+
+ info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL);
+ if (!info->net_name)
+ return -ENOMEM;
+
+ info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL);
+ if (!info->share_name) {
+ kfree(info->net_name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info)
+{
+ kfree(info->net_name);
+ kfree(info->share_name);
+}
+
static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
{
int ret;
@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
* The authentication information to connect to the witness service is bundled
* into the message.
*/
-static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
+static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg,
+ struct cifs_tcon *tcon)
{
struct sk_buff *skb;
struct genlmsghdr *hdr;
@@ -111,10 +156,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
* told to switch to it (client move message). In these cases we unregister from the
* server address and register to the new address when we receive the notification.
*/
- if (swnreg->tcon->ses->server->use_swn_dstaddr)
- addr = &swnreg->tcon->ses->server->swn_dstaddr;
+ if (tcon->ses->server->use_swn_dstaddr)
+ addr = &tcon->ses->server->swn_dstaddr;
else
- addr = &swnreg->tcon->ses->server->dstaddr;
+ addr = &tcon->ses->server->dstaddr;
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
if (ret < 0)
@@ -138,10 +183,10 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
goto nlmsg_fail;
}
- authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
+ authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype);
switch (authtype) {
case Kerberos:
- ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
+ ret = cifs_swn_auth_info_krb(tcon, skb);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
goto nlmsg_fail;
@@ -149,7 +194,7 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
break;
case NTLMv2:
case RawNTLMSSP:
- ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
+ ret = cifs_swn_auth_info_ntlm(tcon, skb);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
goto nlmsg_fail;
@@ -179,7 +224,8 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
/*
* Sends an uregister message to the userspace daemon based on the registration
*/
-static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
+static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg,
+ struct cifs_tcon *tcon)
{
struct sk_buff *skb;
struct genlmsghdr *hdr;
@@ -208,7 +254,7 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
goto nlmsg_fail;
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
- &swnreg->tcon->ses->server->dstaddr);
+ &tcon->ses->server->dstaddr);
if (ret < 0)
goto nlmsg_fail;
@@ -244,6 +290,88 @@ static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
return ret;
}
+/*
+ * Allocation-free mirror of extract_hostname() + extract_sharename() from
+ * fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under
+ * cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their
+ * parsing in place against the caller's stable net_name/share_name strings.
+ * Keep in sync with unc.c.
+ */
+static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon,
+ const char *net_name,
+ const char *share_name)
+{
+ const char *unc = tcon->tree_name;
+ const char *host, *share, *delim;
+ size_t host_len, share_len;
+
+ if (!tcon->use_witness)
+ return false;
+
+ /* extract_hostname: require strlen(unc) >= 3 */
+ if (strnlen(unc, 3) < 3)
+ return false;
+ /* extract_hostname: skip all leading '\' characters */
+ for (host = unc; *host == '\\'; host++)
+ ;
+ if (!*host)
+ return false;
+ delim = strchr(host, '\\');
+ if (!delim)
+ return false;
+ host_len = delim - host;
+ if (strlen(net_name) != host_len ||
+ strncasecmp(host, net_name, host_len))
+ return false;
+
+ /* extract_sharename: start at unc + 2, then first '\' onward */
+ share = unc + 2;
+ delim = strchr(share, '\\');
+ if (!delim)
+ return false;
+ share = delim + 1;
+ share_len = strlen(share);
+
+ return strlen(share_name) == share_len &&
+ !strncasecmp(share, share_name, share_len);
+}
+
+/*
+ * One SWN registration id represents one net/share name pair. Multiple
+ * mounted tcons can therefore share the id. Pick a live representative at
+ * use time instead of caching the first tcon pointer in the registration.
+ */
+static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg)
+{
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ spin_lock(&tcon->tc_lock);
+ if (tcon->status == TID_EXITING ||
+ !cifs_swn_tcon_matches(tcon, swnreg->net_name,
+ swnreg->share_name)) {
+ spin_unlock(&tcon->tc_lock);
+ continue;
+ }
+ ++tcon->tc_count;
+ trace_smb3_tcon_ref(tcon->debug_id,
+ tcon->tc_count,
+ netfs_trace_tcon_ref_get_swn_notify);
+ spin_unlock(&tcon->tc_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return tcon;
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+}
+
/*
* Try to find a matching registration for the tcon's server name and share name.
* Calls to this function must be protected by cifs_swnreg_idr_mutex.
@@ -313,17 +441,15 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
reg = cifs_find_swn_reg(tcon);
if (!IS_ERR(reg)) {
kref_get(®->ref_count);
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return reg;
+ goto unlock;
} else if (PTR_ERR(reg) != -EEXIST) {
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return reg;
+ goto unlock;
}
reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
if (reg == NULL) {
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto fail_unlock;
}
kref_init(®->ref_count);
@@ -352,9 +478,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
reg->net_name_notify = true;
reg->share_name_notify = true;
reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
-
- reg->tcon = tcon;
-
+unlock:
mutex_unlock(&cifs_swnreg_idr_mutex);
return reg;
@@ -365,6 +489,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
idr_remove(&cifs_swnreg_idr, reg->id);
fail:
kfree(reg);
+fail_unlock:
mutex_unlock(&cifs_swnreg_idr_mutex);
return ERR_PTR(ret);
}
@@ -372,11 +497,6 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
static void cifs_swn_reg_release(struct kref *ref)
{
struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
- int ret;
-
- ret = cifs_swn_send_unregister_message(swnreg);
- if (ret < 0)
- cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
idr_remove(&cifs_swnreg_idr, swnreg->id);
kfree(swnreg->net_name);
@@ -384,23 +504,33 @@ static void cifs_swn_reg_release(struct kref *ref)
kfree(swnreg);
}
-static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
+static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg,
+ struct cifs_tcon *tcon)
{
- mutex_lock(&cifs_swnreg_idr_mutex);
+ if (kref_read(&swnreg->ref_count) == 1) {
+ struct cifs_swn_reg_info swnreg_info;
+ int ret;
+
+ cifs_swn_snapshot_reg(swnreg, &swnreg_info);
+ ret = cifs_swn_send_unregister_message(&swnreg_info, tcon);
+ if (ret < 0)
+ cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n",
+ __func__, ret);
+ }
+
kref_put(&swnreg->ref_count, cifs_swn_reg_release);
- mutex_unlock(&cifs_swnreg_idr_mutex);
}
-static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
+static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state)
{
switch (state) {
case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
- cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
+ cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
- cifs_signal_cifsd_for_reconnect(swnreg->tcon->ses->server, true);
+ cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
break;
case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
@@ -506,7 +636,7 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a
return ret;
}
-static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
+static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
{
struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
@@ -516,14 +646,17 @@ static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_sto
else if (addr->ss_family == AF_INET6)
cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
- return cifs_swn_reconnect(swnreg->tcon, addr);
+ return cifs_swn_reconnect(tcon, addr);
}
int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
{
struct cifs_swn_reg *swnreg;
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
char name[256];
int type;
+ int ret = 0;
if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
int swnreg_id;
@@ -531,21 +664,34 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
mutex_lock(&cifs_swnreg_idr_mutex);
swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
- mutex_unlock(&cifs_swnreg_idr_mutex);
if (swnreg == NULL) {
+ mutex_unlock(&cifs_swnreg_idr_mutex);
cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
return -EINVAL;
}
+ ret = cifs_swn_dup_reg(swnreg, &swnreg_info);
+ mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret)
+ return ret;
} else {
cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
return -EINVAL;
}
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
+ __func__, swnreg_info.id);
+ ret = -ENODEV;
+ goto free_info;
+ }
+
if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
} else {
cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
switch (type) {
@@ -557,15 +703,18 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
sizeof(name));
} else {
cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
} else {
cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return cifs_swn_resource_state_changed(swnreg, name, state);
+ ret = cifs_swn_resource_state_changed(tcon, name, state);
+ break;
}
case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
struct sockaddr_storage addr;
@@ -574,28 +723,36 @@ int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
} else {
cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- return cifs_swn_client_move(swnreg, &addr);
+ ret = cifs_swn_client_move(tcon, &addr);
+ break;
}
default:
cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
break;
}
- return 0;
+out:
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+free_info:
+ cifs_swn_free_reg_info(&swnreg_info);
+ return ret;
}
int cifs_swn_register(struct cifs_tcon *tcon)
{
struct cifs_swn_reg *swnreg;
+ struct cifs_swn_reg_info swnreg_info;
int ret;
swnreg = cifs_get_swn_reg(tcon);
if (IS_ERR(swnreg))
return PTR_ERR(swnreg);
- ret = cifs_swn_send_register_message(swnreg);
+ cifs_swn_snapshot_reg(swnreg, &swnreg_info);
+ ret = cifs_swn_send_register_message(&swnreg_info, tcon);
if (ret < 0) {
cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
/* Do not put the swnreg or return error, the echo task will retry */
@@ -616,35 +773,68 @@ int cifs_swn_unregister(struct cifs_tcon *tcon)
return PTR_ERR(swnreg);
}
+ cifs_put_swn_reg_locked(swnreg, tcon);
mutex_unlock(&cifs_swnreg_idr_mutex);
- cifs_put_swn_reg(swnreg);
-
return 0;
}
-void cifs_swn_dump(struct seq_file *m)
+/*
+ * Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers
+ * intentionally do the per-registration network/genlmsg work without the
+ * mutex held, both to keep the critical section short and to avoid nesting
+ * cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then
+ * pinned for the send.
+ */
+static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info)
{
struct cifs_swn_reg *swnreg;
+ int ret = 0;
+
+ mutex_lock(&cifs_swnreg_idr_mutex);
+ swnreg = idr_get_next(&cifs_swnreg_idr, id);
+ if (swnreg) {
+ ret = cifs_swn_dup_reg(swnreg, info);
+ if (!ret) {
+ *id = swnreg->id + 1;
+ ret = 1;
+ }
+ }
+ mutex_unlock(&cifs_swnreg_idr_mutex);
+
+ return ret;
+}
+
+void cifs_swn_dump(struct seq_file *m)
+{
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
struct sockaddr_in *sa;
struct sockaddr_in6 *sa6;
- int id;
+ int id = 0;
+ int ret;
seq_puts(m, "Witness registrations:");
- mutex_lock(&cifs_swnreg_idr_mutex);
- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
+ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
- id, kref_read(&swnreg->ref_count),
- swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
- swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
- switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
+ swnreg_info.id, swnreg_info.ref_count,
+ swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)",
+ swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)");
+
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ seq_puts(m, "(no live tcon)");
+ goto next;
+ }
+
+ switch (tcon->ses->server->dstaddr.ss_family) {
case AF_INET:
- sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
+ sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;
seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
break;
case AF_INET6:
- sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
+ sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;
seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
if (sa6->sin6_scope_id)
seq_printf(m, "%%%u", sa6->sin6_scope_id);
@@ -652,23 +842,38 @@ void cifs_swn_dump(struct seq_file *m)
default:
seq_puts(m, "(unknown)");
}
- seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+next:
+ seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)");
+ cifs_swn_free_reg_info(&swnreg_info);
}
- mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret < 0)
+ seq_printf(m, "\nFailed to snapshot witness registration: %d", ret);
seq_puts(m, "\n");
}
void cifs_swn_check(void)
{
- struct cifs_swn_reg *swnreg;
- int id;
+ struct cifs_swn_reg_info swnreg_info;
+ struct cifs_tcon *tcon;
+ int id = 0;
int ret;
- mutex_lock(&cifs_swnreg_idr_mutex);
- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
- ret = cifs_swn_send_register_message(swnreg);
+ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) {
+ tcon = cifs_swn_get_tcon(&swnreg_info);
+ if (!tcon) {
+ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n",
+ __func__, swnreg_info.id);
+ goto free_info;
+ }
+
+ ret = cifs_swn_send_register_message(&swnreg_info, tcon);
if (ret < 0)
cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify);
+free_info:
+ cifs_swn_free_reg_info(&swnreg_info);
}
- mutex_unlock(&cifs_swnreg_idr_mutex);
+ if (ret < 0)
+ cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret);
}
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index 2422ac371262..230b98b3a272 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -890,7 +890,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
*/
fattr->cf_mode &= ~07777;
fattr->cf_mode |=
- le32_to_cpu(ppace[i]->sid.sub_auth[2]);
+ le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777;
break;
} else {
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 8e85332cb7ab..97f70d437a7e 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -25,6 +25,7 @@
#include "cifsacl.h"
#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
+#include "../common/cifsglob.h"
#include "../common/smb2pdu.h"
#include "smb2pdu.h"
#include <linux/filelock.h>
@@ -669,12 +670,6 @@ get_rfc1002_length(void *buf)
return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
}
-static inline void
-inc_rfc1001_len(void *buf, int count)
-{
- be32_add_cpu((__be32 *)buf, count);
-}
-
struct TCP_Server_Info {
struct list_head tcp_ses_list;
struct list_head smb_ses_list;
@@ -982,8 +977,6 @@ compare_mid(__u16 mid, const struct smb_hdr *smb)
#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
-#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
-
/*
* Windows only supports a max of 60kb reads and 65535 byte writes. Default to
* those values when posix extensions aren't in force. In actuality here, we
@@ -2131,30 +2124,20 @@ extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp;
/* Operations for different SMB versions */
-#define SMB1_VERSION_STRING "1.0"
-#define SMB20_VERSION_STRING "2.0"
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
extern struct smb_version_operations smb1_operations;
extern struct smb_version_values smb1_values;
extern struct smb_version_operations smb20_operations;
extern struct smb_version_values smb20_values;
#endif /* CIFS_ALLOW_INSECURE_LEGACY */
-#define SMB21_VERSION_STRING "2.1"
extern struct smb_version_operations smb21_operations;
extern struct smb_version_values smb21_values;
-#define SMBDEFAULT_VERSION_STRING "default"
extern struct smb_version_values smbdefault_values;
-#define SMB3ANY_VERSION_STRING "3"
extern struct smb_version_values smb3any_values;
-#define SMB30_VERSION_STRING "3.0"
extern struct smb_version_operations smb30_operations;
extern struct smb_version_values smb30_values;
-#define SMB302_VERSION_STRING "3.02"
-#define ALT_SMB302_VERSION_STRING "3.0.2"
/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */
extern struct smb_version_values smb302_values;
-#define SMB311_VERSION_STRING "3.1.1"
-#define ALT_SMB311_VERSION_STRING "3.11"
extern struct smb_version_operations smb311_operations;
extern struct smb_version_values smb311_values;
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 78feea9c262e..fe5d21f174c4 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -3968,6 +3968,9 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
}
spin_unlock(&cifs_sb->tlink_tree_lock);
+ flush_workqueue(serverclose_wq);
+ flush_workqueue(fileinfo_put_wq);
+
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free);
}
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index e55852aa49f9..cca480ab7e64 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -119,6 +119,23 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0);
}
+static void cifs_invalidate_cached_dir(struct cifs_tcon *tcon,
+ struct dentry *parent)
+{
+ struct cached_fid *parent_cfid = NULL;
+
+ if (!tcon || !parent)
+ return;
+
+ if (!open_cached_dir_by_dentry(tcon, parent, &parent_cfid)) {
+ mutex_lock(&parent_cfid->dirents.de_mutex);
+ parent_cfid->dirents.is_valid = false;
+ parent_cfid->dirents.is_failed = true;
+ mutex_unlock(&parent_cfid->dirents.de_mutex);
+ close_cached_dir(parent_cfid);
+ }
+}
+
/*
* copy nlink to the inode, unless it wasn't provided. Provide
* sane values if we don't have an existing one and none was provided
@@ -1964,6 +1981,9 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
cifs_set_file_info(inode, attrs, xid, full_path, origattr);
out_reval:
+ if (!rc && dentry->d_parent)
+ cifs_invalidate_cached_dir(tcon, dentry->d_parent);
+
if (inode) {
cifs_inode = CIFS_I(inode);
cifs_inode->time = 0; /* will force revalidate to get info
@@ -2268,13 +2288,14 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
}
rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
- cifs_put_tlink(tlink);
if (!rc) {
spin_lock(&d_inode(direntry)->i_lock);
i_size_write(d_inode(direntry), 0);
clear_nlink(d_inode(direntry));
spin_unlock(&d_inode(direntry)->i_lock);
+ if (direntry->d_parent)
+ cifs_invalidate_cached_dir(tcon, direntry->d_parent);
}
cifsInode = CIFS_I(d_inode(direntry));
@@ -2290,6 +2311,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
inode_set_ctime_current(d_inode(direntry));
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ cifs_put_tlink(tlink);
rmdir_exit:
free_dentry_path(page);
@@ -2512,6 +2534,12 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
}
/* force revalidate to go get info when needed */
+ if (!rc) {
+ cifs_invalidate_cached_dir(tcon, source_dentry->d_parent);
+ if (target_dentry->d_parent != source_dentry->d_parent)
+ cifs_invalidate_cached_dir(tcon, target_dentry->d_parent);
+ }
+
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
cifs_rename_exit:
diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
index 8ea532cdc430..8a668b827570 100644
--- a/fs/smb/client/ioctl.c
+++ b/fs/smb/client/ioctl.c
@@ -376,13 +376,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
}
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
#endif /* CONFIG_CIFS_POSIX */
- rc = 0;
- if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
- /* add in the compressed bit */
- ExtAttrBits = FS_COMPR_FL;
- rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
- (int __user *)arg);
- }
+ if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED)
+ ExtAttrBits |= FS_COMPR_FL;
+
+ rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
+ (int __user *)arg);
break;
case FS_IOC_SETFLAGS:
if (pSMBFile == NULL)
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index a31b4e912719..46ca8f326c98 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -1039,6 +1039,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
/* copy DfsPath */
+ if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
max_len = data_end - temp;
node->path_name = cifs_strndup_from_utf16(temp, max_len,
@@ -1049,6 +1053,10 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
}
/* copy link target UNC */
+ if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
max_len = data_end - temp;
node->node_name = cifs_strndup_from_utf16(temp, max_len,
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index dc0a441371be..6fa23cf7a529 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -18,6 +18,9 @@
#include "nterr.h"
#include "cached_dir.h"
+static unsigned int __smb2_calc_size(void *buf, bool *have_data,
+ bool *data_area_overlap);
+
static int
check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
{
@@ -143,6 +146,8 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
int command;
__u32 calc_len; /* calculated length */
__u64 mid;
+ bool have_data;
+ bool data_area_overlap;
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
@@ -226,7 +231,13 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
}
}
- calc_len = smb2_calc_size(buf);
+ have_data = false;
+ data_area_overlap = false;
+ calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap);
+
+ /* Reject responses whose data area overlaps the fixed area. */
+ if (data_area_overlap)
+ return 1;
/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might
* be 0, and not a real miscalculation */
@@ -245,8 +256,13 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
/* Windows 7 server returns 24 bytes more */
if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)
return 0;
- /* server can return one byte more due to implied bcc[0] */
- if (calc_len == len + 1)
+ /*
+ * Server can return one byte more due to implied bcc[0].
+ * Allow it only when there is no data area; if data_length > 0
+ * the +1 gap indicates an overreported data length rather than
+ * the bcc[0] omission.
+ */
+ if (calc_len == len + 1 && !have_data)
return 0;
/*
@@ -405,19 +421,28 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)
}
/*
- * Calculate the size of the SMB message based on the fixed header
- * portion, the number of word parameters and the data portion of the message.
+ * Calculate the size of the SMB message based on the fixed header, fixed
+ * parameter area, and variable data area.
+ *
+ * If have_data is not NULL, it is set when a non-empty data area is found.
+ * If data_area_overlap is not NULL, it is set when the data area overlaps
+ * the fixed area.
*/
-unsigned int
-smb2_calc_size(void *buf)
+static unsigned int
+__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap)
{
struct smb2_pdu *pdu = buf;
struct smb2_hdr *shdr = &pdu->hdr;
int offset; /* the offset from the beginning of SMB to data area */
- int data_length; /* the length of the variable length data area */
+ int data_length = 0; /* the length of the variable length data area */
/* Structure Size has already been checked to make sure it is 64 */
int len = le16_to_cpu(shdr->StructureSize);
+ if (have_data)
+ *have_data = false;
+ if (data_area_overlap)
+ *data_area_overlap = false;
+
/*
* StructureSize2, ie length of fixed parameter area has already
* been checked to make sure it is the correct length.
@@ -440,16 +465,27 @@ smb2_calc_size(void *buf)
if (offset + 1 < len) {
cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n",
offset + 1, len);
+ if (data_area_overlap)
+ *data_area_overlap = true;
data_length = 0;
+ goto calc_size_exit;
} else {
len = offset + data_length;
}
}
calc_size_exit:
cifs_dbg(FYI, "SMB2 len %d\n", len);
+ if (have_data)
+ *have_data = (data_length > 0);
return len;
}
+unsigned int
+smb2_calc_size(void *buf)
+{
+ return __smb2_calc_size(buf, NULL, NULL);
+}
+
/* Note: caller must free return buffer */
__le16 *
cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index c16ed08d62af..c66b02f67d33 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1728,8 +1728,8 @@ smb2_ioctl_query_info(const unsigned int xid,
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
if (qi.input_buffer_length > 0 &&
- le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
- > rsp_iov[1].iov_len) {
+ size_add(le32_to_cpu(io_rsp->OutputOffset),
+ qi.input_buffer_length) > rsp_iov[1].iov_len) {
rc = -EFAULT;
goto out;
}
@@ -1967,8 +1967,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
}
/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */
-static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse)
+static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, struct inode *inode,
+ __u8 setsparse)
{
struct cifsInodeInfo *cifsi;
int rc;
@@ -1977,31 +1978,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
/* if file already sparse don't bother setting sparse again */
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse)
- return true; /* already sparse */
+ return 0; /* already sparse */
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse)
- return true; /* already not sparse */
+ return 0; /* already not sparse */
/*
* Can't check for sparse support on share the usual way via the
* FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share
* since Samba server doesn't set the flag on the share, yet
* supports the set sparse FSCTL and returns sparse correctly
- * in the file attributes. If we fail setting sparse though we
- * mark that server does not support sparse files for this share
- * to avoid repeatedly sending the unsupported fsctl to server
- * if the file is repeatedly extended.
+ * in the file attributes. If the server returns EOPNOTSUPP, mark
+ * that sparse files are not supported on this share to avoid
+ * repeatedly sending the unsupported FSCTL.
*/
if (tcon->broken_sparse_sup)
- return false;
+ return -EOPNOTSUPP;
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
&setsparse, 1, CIFSMaxBufSize, NULL, NULL);
if (rc) {
- tcon->broken_sparse_sup = true;
+ if (rc == -EOPNOTSUPP)
+ tcon->broken_sparse_sup = true;
cifs_dbg(FYI, "set sparse rc = %d\n", rc);
- return false;
+ return rc;
}
if (setsparse)
@@ -2009,7 +2010,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
else
cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE);
- return true;
+ return 0;
}
static int
@@ -3322,10 +3323,9 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
inode_lock(inode);
/* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */
- if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
- rc = -EOPNOTSUPP;
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse);
+ if (rc)
goto out;
- }
filemap_invalidate_lock(inode->i_mapping);
/*
@@ -3431,7 +3431,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (rc)
goto out;
- buf = kzalloc(1024 * 1024, GFP_KERNEL);
+ buf = kvzalloc(1024 * 1024, GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
@@ -3488,7 +3488,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
out:
kfree(out_data);
- kfree(buf);
+ kvfree(buf);
return rc;
}
@@ -4182,11 +4182,13 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
unsigned int iv_size = crypto_aead_ivsize(tfm);
unsigned int len;
+ int ret;
u8 *p;
- *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig);
- if (IS_ERR_VALUE((long)(int)*num_sgs))
- return ERR_PTR(*num_sgs);
+ ret = cifs_get_num_sgs(rqst, num_rqst, sig);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ *num_sgs = ret;
len = iv_size;
len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
@@ -4940,6 +4942,12 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
one_more:
shdr = (struct smb2_hdr *)buf;
next_cmd = le32_to_cpu(shdr->NextCommand);
+
+ if (*num_mids >= MAX_COMPOUND) {
+ cifs_server_dbg(VFS, "too many PDUs in compound\n");
+ return -1;
+ }
+
if (next_cmd) {
if (WARN_ON_ONCE(next_cmd > pdu_length))
return -1;
@@ -4963,10 +4971,6 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
mid_entry->resp_buf_size = server->pdu_size;
}
- if (*num_mids >= MAX_COMPOUND) {
- cifs_server_dbg(VFS, "too many PDUs in compound\n");
- return -1;
- }
bufs[*num_mids] = buf;
mids[(*num_mids)++] = mid_entry;
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 176ae313f4a6..7f26e6fdd6ab 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2307,9 +2307,9 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
memset(posix, 0, sizeof(*posix));
- posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0));
- posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4));
- posix->mode = le32_to_cpu(*(__le32 *)(beg + 8));
+ posix->nlink = get_unaligned_le32(beg);
+ posix->reparse_tag = get_unaligned_le32(beg + 4);
+ posix->mode = get_unaligned_le32(beg + 8);
sid = beg + 12;
sid_len = posix_info_sid_size(sid, end);
@@ -3208,6 +3208,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
oparms->replay = !!(retries);
@@ -3437,6 +3439,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -3625,6 +3629,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
query_attrs = false;
server = cifs_pick_channel(ses);
@@ -3833,6 +3839,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
allocated = false;
server = cifs_pick_channel(ses);
@@ -4020,6 +4028,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -4344,6 +4354,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
@@ -5118,7 +5130,7 @@ int posix_info_sid_size(const void *beg, const void *end)
size_t subauth;
int total;
- if (beg + 1 > end)
+ if (beg + 2 > end)
return -1;
subauth = *(u8 *)(beg+1);
@@ -5814,6 +5826,8 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
replay_again:
/* reinitialize for possible replay */
+ resp_buftype = CIFS_NO_BUFFER;
+ memset(&rsp_iov, 0, sizeof(rsp_iov));
flags = 0;
server = cifs_pick_channel(ses);
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index bd6e59b70872..036c7966e260 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -35,6 +35,7 @@
EM(netfs_trace_tcon_ref_get_find, "GET Find ") \
EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \
EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \
+ EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \
EM(netfs_trace_tcon_ref_new, "NEW ") \
EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \
EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \
@@ -46,6 +47,7 @@
EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \
EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \
EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \
+ EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \
EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \
EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \
EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \
diff --git a/fs/smb/common/cifsglob.h b/fs/smb/common/cifsglob.h
new file mode 100644
index 000000000000..00fd215e3eb5
--- /dev/null
+++ b/fs/smb/common/cifsglob.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ *
+ */
+#ifndef _COMMON_CIFS_GLOB_H
+#define _COMMON_CIFS_GLOB_H
+
+static inline void inc_rfc1001_len(void *buf, int count)
+{
+ be32_add_cpu((__be32 *)buf, count);
+}
+
+#define SMB1_VERSION_STRING "1.0"
+#define SMB20_VERSION_STRING "2.0"
+#define SMB21_VERSION_STRING "2.1"
+#define SMBDEFAULT_VERSION_STRING "default"
+#define SMB3ANY_VERSION_STRING "3"
+#define SMB30_VERSION_STRING "3.0"
+#define SMB302_VERSION_STRING "3.02"
+#define ALT_SMB302_VERSION_STRING "3.0.2"
+#define SMB311_VERSION_STRING "3.1.1"
+#define ALT_SMB311_VERSION_STRING "3.11"
+
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+#endif /* _COMMON_CIFS_GLOB_H */
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index bbb0be5524db..cdc1edeefeb3 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -22,6 +22,81 @@ static struct ksmbd_conn_ops default_conn_ops;
DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
DECLARE_RWSEM(conn_list_lock);
+static struct workqueue_struct *ksmbd_conn_wq;
+
+int ksmbd_conn_wq_init(void)
+{
+ ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
+ if (!ksmbd_conn_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+void ksmbd_conn_wq_destroy(void)
+{
+ if (ksmbd_conn_wq) {
+ destroy_workqueue(ksmbd_conn_wq);
+ ksmbd_conn_wq = NULL;
+ }
+}
+
+/*
+ * __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup
+ * of a ksmbd_conn whose refcount has just dropped to zero.
+ *
+ * This is the common release path used by ksmbd_conn_put() for the embedded
+ * state that outlives the connection thread: async_ida and the attached
+ * transport (which owns the socket and iov for TCP). Called from a workqueue
+ * so that sleep-allowed teardown (sock_release -> tcp_close ->
+ * lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu)
+ * or any other non-sleeping putter context.
+ */
+static void __ksmbd_conn_release_work(struct work_struct *work)
+{
+ struct ksmbd_conn *conn =
+ container_of(work, struct ksmbd_conn, release_work);
+
+ ida_destroy(&conn->async_ida);
+ conn->transport->ops->free_transport(conn->transport);
+ kfree(conn);
+}
+
+/**
+ * ksmbd_conn_get() - take a reference on @conn and return it.
+ *
+ * Returns @conn unchanged so callers can write
+ * "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL
+ * if @conn is NULL.
+ */
+struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ atomic_inc(&conn->refcnt);
+ return conn;
+}
+
+/**
+ * ksmbd_conn_put() - drop a reference and, if it was the last, queue the
+ * release onto ksmbd_conn_wq so it runs from process context.
+ *
+ * Callable from any context including RCU softirq callbacks and non-sleeping
+ * locks; the actual release is deferred to the workqueue. ksmbd_conn_wq is
+ * created in ksmbd_server_init() before any conn can be allocated and is
+ * destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always
+ * non-NULL while a conn reference is held.
+ */
+void ksmbd_conn_put(struct ksmbd_conn *conn)
+{
+ if (!conn)
+ return;
+
+ if (atomic_dec_and_test(&conn->refcnt))
+ queue_work(ksmbd_conn_wq, &conn->release_work);
+}
+
/**
* ksmbd_conn_free() - free resources of the connection instance
*
@@ -36,23 +111,19 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
hash_del(&conn->hlist);
up_write(&conn_list_lock);
+ /*
+ * request_buf / preauth_info / mechToken are only ever accessed by the
+ * connection handler thread that owns @conn. ksmbd_conn_free() is
+ * called from the transport free_transport() path when that thread is
+ * exiting, so it is safe to release them unconditionally even when
+ * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file
+ * holders only retain the conn pointer, not these per-thread buffers).
+ */
xa_destroy(&conn->sessions);
kvfree(conn->request_buf);
kfree(conn->preauth_info);
kfree(conn->mechToken);
- if (atomic_dec_and_test(&conn->refcnt)) {
- /*
- * async_ida is embedded in struct ksmbd_conn, so pair
- * ida_destroy() with the final kfree() rather than with
- * the unconditional field teardown above. This keeps
- * the IDA valid for the entire lifetime of the struct,
- * even while other refcount holders (oplock / vfs
- * durable handles) still reference the connection.
- */
- ida_destroy(&conn->async_ida);
- conn->transport->ops->free_transport(conn->transport);
- kfree(conn);
- }
+ ksmbd_conn_put(conn);
}
/**
@@ -79,6 +150,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
conn->um = ERR_PTR(-EOPNOTSUPP);
if (IS_ERR(conn->um))
conn->um = NULL;
+ INIT_WORK(&conn->release_work, __ksmbd_conn_release_work);
atomic_set(&conn->req_running, 0);
atomic_set(&conn->r_count, 0);
atomic_set(&conn->refcnt, 1);
@@ -457,8 +529,7 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
wake_up(&conn->r_count_q);
- if (atomic_dec_and_test(&conn->refcnt))
- kfree(conn);
+ ksmbd_conn_put(conn);
}
int ksmbd_conn_transport_init(void)
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index 2a4b0141ab45..e00bfda0a5bd 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -15,6 +15,7 @@
#include <linux/kthread.h>
#include <linux/nls.h>
#include <linux/unicode.h>
+#include <linux/workqueue.h>
#include "smb_common.h"
#include "ksmbd_work.h"
@@ -116,6 +117,7 @@ struct ksmbd_conn {
bool binding;
atomic_t refcnt;
bool is_aapl;
+ struct work_struct release_work;
};
struct ksmbd_conn_ops {
@@ -161,6 +163,10 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
+struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
+void ksmbd_conn_put(struct ksmbd_conn *conn);
+int ksmbd_conn_wq_init(void);
+void ksmbd_conn_wq_destroy(void);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
int ksmbd_conn_write(struct ksmbd_work *work);
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index bba17ea84023..82a7709b3e9a 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock);
static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
u64 id, __u16 Tid)
{
- struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
struct oplock_info *opinfo;
@@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
return NULL;
opinfo->sess = sess;
- opinfo->conn = conn;
+ opinfo->conn = ksmbd_conn_get(work->conn);
opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
opinfo->op_state = OPLOCK_STATE_NONE;
opinfo->pending_break = 0;
@@ -50,7 +49,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
init_waitqueue_head(&opinfo->oplock_brk);
atomic_set(&opinfo->refcount, 1);
atomic_set(&opinfo->breaking_cnt, 0);
- atomic_inc(&opinfo->conn->refcnt);
return opinfo;
}
@@ -132,8 +130,7 @@ static void __free_opinfo(struct oplock_info *opinfo)
{
if (opinfo->is_lease)
free_lease(opinfo);
- if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt))
- kfree(opinfo->conn);
+ ksmbd_conn_put(opinfo->conn);
kfree(opinfo);
}
@@ -531,7 +528,12 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
ret = compare_guid_key(opinfo, client_guid, lctx->lease_key);
if (ret) {
+ if (!atomic_inc_not_zero(&opinfo->refcount))
+ continue;
+ if (m_opinfo)
+ opinfo_put(m_opinfo);
m_opinfo = opinfo;
+
/* skip upgrading lease about breaking lease */
if (atomic_read(&opinfo->breaking_cnt))
continue;
@@ -1249,6 +1251,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
if (atomic_read(&m_opinfo->breaking_cnt))
opinfo->o_lease->flags =
SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE;
+ opinfo_put(m_opinfo);
goto out;
}
}
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 416b14267251..21869bcd7f2c 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -587,8 +587,14 @@ static int __init ksmbd_server_init(void)
if (ret)
goto err_crypto_destroy;
+ ret = ksmbd_conn_wq_init();
+ if (ret)
+ goto err_workqueue_destroy;
+
return 0;
+err_workqueue_destroy:
+ ksmbd_workqueue_destroy();
err_crypto_destroy:
ksmbd_crypto_destroy();
err_release_inode_hash:
@@ -614,6 +620,12 @@ static void __exit ksmbd_server_exit(void)
{
ksmbd_server_shutdown();
rcu_barrier();
+ /*
+ * ksmbd_conn_put() defers the final release onto ksmbd_conn_wq,
+ * so drain it after rcu_barrier() has fired any pending RCU
+ * callbacks that may have queued a release.
+ */
+ ksmbd_conn_wq_destroy();
ksmbd_release_inode_hash();
}
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 04fa2c9bba80..3ddff0d9b8ba 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -4445,6 +4445,8 @@ int smb2_query_dir(struct ksmbd_work *work)
ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr);
}
+ mutex_lock(&dir_fp->readdir_lock);
+
if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) {
ksmbd_debug(SMB, "Restart directory scan\n");
generic_file_llseek(dir_fp->filp, 0, SEEK_SET);
@@ -4549,6 +4551,7 @@ int smb2_query_dir(struct ksmbd_work *work)
goto err_out;
}
+ mutex_unlock(&dir_fp->readdir_lock);
kfree(srch_ptr);
ksmbd_fd_put(work, dir_fp);
ksmbd_revert_fsids(work);
@@ -4556,6 +4559,7 @@ int smb2_query_dir(struct ksmbd_work *work)
err_out:
pr_err("error while processing smb2 query dir rc = %d\n", rc);
+ mutex_unlock(&dir_fp->readdir_lock);
kfree(srch_ptr);
err_out2:
@@ -5286,6 +5290,12 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32;
int ret;
+ if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
+ pr_err("no right to read the attributes : 0x%x\n",
+ fp->daccess);
+ return -EACCES;
+ }
+
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
@@ -6279,6 +6289,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
*/
loff_t alloc_blks;
+ u64 alloc_size;
struct inode *inode;
struct kstat stat;
int rc;
@@ -6291,7 +6302,19 @@ static int set_file_allocation_info(struct ksmbd_work *work,
if (rc)
return rc;
- alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
+ /*
+ * AllocationSize is fully client-controlled (the caller only
+ * validates the fixed 8-byte buffer length). Reject values that
+ * would overflow the "round up to 512-byte blocks" conversion
+ * below instead of silently wrapping it to a tiny block count,
+ * which would truncate the file to a size the client never
+ * asked for.
+ */
+ alloc_size = le64_to_cpu(file_alloc_info->AllocationSize);
+ if (alloc_size > MAX_LFS_FILESIZE - 511)
+ return -EINVAL;
+
+ alloc_blks = (alloc_size + 511) >> 9;
inode = file_inode(fp->filp);
if (alloc_blks > stat.blocks) {
@@ -6494,11 +6517,18 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
}
case FILE_LINK_INFORMATION:
{
+ struct smb2_file_link_info *file_info;
+
if (buf_len < sizeof(struct smb2_file_link_info))
return -EINVAL;
- return smb2_create_link(work, work->tcon->share_conf,
- (struct smb2_file_link_info *)buffer,
+ file_info = (struct smb2_file_link_info *)buffer;
+ if (file_info->ReplaceIfExists && !(fp->daccess & FILE_DELETE_LE)) {
+ pr_err("no right to delete : 0x%x\n", fp->daccess);
+ return -EACCES;
+ }
+
+ return smb2_create_link(work, work->tcon->share_conf, file_info,
buf_len, fp->filp,
work->conn->local_nls);
}
@@ -6551,6 +6581,9 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info,
fp->saccess |= FILE_SHARE_DELETE_LE;
+ if (!(fp->daccess & (FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE)))
+ return -EACCES;
+
return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd,
buf_len, false, true);
}
@@ -6563,6 +6596,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info,
*/
int smb2_set_info(struct ksmbd_work *work)
{
+ const struct cred *saved_cred;
struct smb2_set_info_req *req;
struct smb2_set_info_rsp *rsp;
struct ksmbd_file *fp = NULL;
@@ -6604,6 +6638,7 @@ int smb2_set_info(struct ksmbd_work *work)
goto err_out;
}
+ saved_cred = override_creds(fp->filp->f_cred);
switch (req->InfoType) {
case SMB2_O_INFO_FILE:
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
@@ -6611,19 +6646,15 @@ int smb2_set_info(struct ksmbd_work *work)
break;
case SMB2_O_INFO_SECURITY:
ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n");
- if (ksmbd_override_fsids(work)) {
- rc = -ENOMEM;
- goto err_out;
- }
rc = smb2_set_info_sec(fp,
le32_to_cpu(req->AdditionalInformation),
(char *)req + le16_to_cpu(req->BufferOffset),
le32_to_cpu(req->BufferLength));
- ksmbd_revert_fsids(work);
break;
default:
rc = -EOPNOTSUPP;
}
+ revert_creds(saved_cred);
if (rc < 0)
goto err_out;
@@ -7254,14 +7285,14 @@ int smb2_cancel(struct ksmbd_work *work)
continue;
/*
- * A cancelled deferred byte-range lock frees its
- * file_lock and takes the smb2_lock() early-exit that
- * skips release_async_work(), so the work stays on
- * conn->async_requests with a live cancel_fn pointing
- * at the freed file_lock. Re-firing it on a second
- * SMB2_CANCEL is a use-after-free.
+ * Only an ACTIVE deferred work may have its cancel_fn
+ * fired. A CANCELLED or CLOSED work already took the
+ * smb2_lock() non-ACTIVE early-exit that frees the
+ * file_lock and skips release_async_work(), so it is
+ * still on conn->async_requests with a live cancel_fn
+ * pointing at the freed file_lock.
*/
- if (iter->state == KSMBD_WORK_CANCELLED)
+ if (iter->state != KSMBD_WORK_ACTIVE)
break;
ksmbd_debug(SMB,
@@ -7549,9 +7580,11 @@ int smb2_lock(struct ksmbd_work *work)
nolock = 0;
list_del(&cmp_lock->flist);
list_del(&cmp_lock->clist);
+ cmp_lock->conn = NULL;
spin_unlock(&conn->llist_lock);
up_read(&conn_list_lock);
+ ksmbd_conn_put(conn);
locks_free_lock(cmp_lock->fl);
kfree(cmp_lock);
goto out_check_cl;
@@ -7663,31 +7696,30 @@ int smb2_lock(struct ksmbd_work *work)
list_del(&work->fp_entry);
spin_unlock(&fp->f_lock);
- if (work->state != KSMBD_WORK_ACTIVE) {
- list_del(&smb_lock->llist);
- locks_free_lock(flock);
+ list_del(&smb_lock->llist);
+ release_async_work(work);
- if (work->state == KSMBD_WORK_CANCELLED) {
- rsp->hdr.Status =
- STATUS_CANCELLED;
- kfree(smb_lock);
- smb2_send_interim_resp(work,
- STATUS_CANCELLED);
- work->send_no_response = 1;
- goto out;
- }
+ if (work->state == KSMBD_WORK_ACTIVE)
+ goto retry;
- rsp->hdr.Status =
- STATUS_RANGE_NOT_LOCKED;
+ locks_free_lock(flock);
+
+ if (work->state == KSMBD_WORK_CANCELLED) {
+ rsp->hdr.Status = STATUS_CANCELLED;
kfree(smb_lock);
- goto out2;
+ smb2_send_interim_resp(work,
+ STATUS_CANCELLED);
+ work->send_no_response = 1;
+ goto out;
}
- list_del(&smb_lock->llist);
- release_async_work(work);
- goto retry;
+ rsp->hdr.Status =
+ STATUS_RANGE_NOT_LOCKED;
+ kfree(smb_lock);
+ goto out2;
} else if (!rc) {
list_add(&smb_lock->llist, &rollback_list);
+ smb_lock->conn = ksmbd_conn_get(work->conn);
spin_lock(&work->conn->llist_lock);
list_add_tail(&smb_lock->clist,
&work->conn->lock_list);
@@ -7742,11 +7774,14 @@ int smb2_lock(struct ksmbd_work *work)
}
list_del(&smb_lock->llist);
- spin_lock(&work->conn->llist_lock);
+ conn = smb_lock->conn;
+ spin_lock(&conn->llist_lock);
if (!list_empty(&smb_lock->flist))
list_del(&smb_lock->flist);
list_del(&smb_lock->clist);
- spin_unlock(&work->conn->llist_lock);
+ smb_lock->conn = NULL;
+ spin_unlock(&conn->llist_lock);
+ ksmbd_conn_put(conn);
locks_free_lock(smb_lock->fl);
if (rlock)
@@ -8165,6 +8200,7 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
if (fp->f_ci->m_fattr != old_fattr &&
test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) {
+ const struct cred *saved_cred;
struct xattr_dos_attrib da;
ret = ksmbd_vfs_get_dos_attrib_xattr(idmap,
@@ -8173,9 +8209,11 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id,
goto out;
da.attr = le32_to_cpu(fp->f_ci->m_fattr);
+ saved_cred = override_creds(fp->filp->f_cred);
ret = ksmbd_vfs_set_dos_attrib_xattr(idmap,
&fp->filp->f_path,
&da, true);
+ revert_creds(saved_cred);
if (ret)
fp->f_ci->m_fattr = old_fattr;
}
@@ -8407,6 +8445,12 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out;
}
+ if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
+ ksmbd_fd_put(work, fp);
+ ret = -EACCES;
+ goto out;
+ }
+
ret = ksmbd_vfs_zero_data(work, fp, off, len);
ksmbd_fd_put(work, fp);
if (ret < 0)
@@ -8482,6 +8526,21 @@ int smb2_ioctl(struct ksmbd_work *work)
goto dup_ext_out;
}
+ if (!test_tree_conn_flag(work->tcon,
+ KSMBD_TREE_CONN_FLAG_WRITABLE)) {
+ ret = -EACCES;
+ goto dup_ext_out;
+ }
+
+ if (!(fp_out->daccess & FILE_WRITE_DATA_LE)) {
+ ret = -EACCES;
+ goto dup_ext_out;
+ }
+ if (!(fp_in->daccess & FILE_READ_DATA_LE)) {
+ ret = -EACCES;
+ goto dup_ext_out;
+ }
+
src_off = le64_to_cpu(dup_ext->SourceFileOffset);
dst_off = le64_to_cpu(dup_ext->TargetFileOffset);
length = le64_to_cpu(dup_ext->ByteCount);
@@ -9095,10 +9154,13 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work)
WORK_BUFFERS(work, req, rsp);
- if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE &&
- conn->preauth_info)
- ksmbd_gen_preauth_integrity_hash(conn, work->response_buf,
- conn->preauth_info->Preauth_HashValue);
+ if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) {
+ ksmbd_conn_lock(conn);
+ if (conn->preauth_info)
+ ksmbd_gen_preauth_integrity_hash(conn, work->response_buf,
+ conn->preauth_info->Preauth_HashValue);
+ ksmbd_conn_unlock(conn);
+ }
if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) {
__u8 *hash_value;
diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
index bc832fa9a82c..3f274af060da 100644
--- a/fs/smb/server/smb_common.h
+++ b/fs/smb/server/smb_common.h
@@ -10,6 +10,7 @@
#include "glob.h"
#include "nterr.h"
+#include "../common/cifsglob.h"
#include "../common/smb2pdu.h"
#include "smb2pdu.h"
@@ -26,16 +27,8 @@
#define SMB311_PROT 6
#define BAD_PROT 0xFFFF
-#define SMB1_VERSION_STRING "1.0"
-#define SMB20_VERSION_STRING "2.0"
-#define SMB21_VERSION_STRING "2.1"
-#define SMB30_VERSION_STRING "3.0"
-#define SMB302_VERSION_STRING "3.02"
-#define SMB311_VERSION_STRING "3.1.1"
-
#define SMB_ECHO_INTERVAL (60 * HZ)
-#define CIFS_DEFAULT_IOSIZE (64 * 1024)
#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
#define MAX_STREAM_PROT_LEN 0x00FFFFFF
@@ -462,9 +455,4 @@ static inline unsigned int get_rfc1002_len(void *buf)
{
return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
}
-
-static inline void inc_rfc1001_len(void *buf, int count)
-{
- be32_add_cpu((__be32 *)buf, count);
-}
#endif /* __SMB_COMMON_H__ */
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index cd6c72fa1960..420d4e0733e5 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -374,6 +374,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
{
int i, ret;
u16 num_aces = 0;
+ u16 dacl_size;
unsigned int acl_size;
char *acl_base;
struct smb_ace **ppace;
@@ -403,7 +404,11 @@ static void parse_dacl(struct mnt_idmap *idmap,
if (num_aces <= 0)
return;
- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
+ dacl_size = le16_to_cpu(pdacl->size);
+ if (dacl_size < sizeof(struct smb_acl))
+ return;
+
+ if (num_aces > (dacl_size - sizeof(struct smb_acl)) /
(offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
return;
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 820352463a06..69f807aff769 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -994,15 +994,21 @@ void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option)
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
loff_t off, loff_t len)
{
+ const struct cred *saved_cred;
+ int err;
+
smb_break_all_levII_oplock(work, fp, 1);
+ saved_cred = override_creds(fp->filp->f_cred);
if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE)
- return vfs_fallocate(fp->filp,
- FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
- off, len);
-
- return vfs_fallocate(fp->filp,
- FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
- off, len);
+ err = vfs_fallocate(fp->filp,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ off, len);
+ else
+ err = vfs_fallocate(fp->filp,
+ FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
+ off, len);
+ revert_creds(saved_cred);
+ return err;
}
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
@@ -1084,13 +1090,15 @@ int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
int ksmbd_vfs_unlink(struct file *filp)
{
+ const struct cred *saved_cred;
int err = 0;
struct dentry *dir, *dentry = filp->f_path.dentry;
struct mnt_idmap *idmap = file_mnt_idmap(filp);
+ saved_cred = override_creds(filp->f_cred);
err = mnt_want_write(filp->f_path.mnt);
if (err)
- return err;
+ goto out_revert;
dir = dget_parent(dentry);
err = ksmbd_vfs_lock_parent(dir, dentry);
@@ -1110,7 +1118,8 @@ int ksmbd_vfs_unlink(struct file *filp)
out:
dput(dir);
mnt_drop_write(filp->f_path.mnt);
-
+out_revert:
+ revert_creds(saved_cred);
return err;
}
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 5986c3b3251f..d15641007d4e 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -292,10 +292,14 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
up_write(&ci->m_lock);
if (remove_stream_xattr) {
+ const struct cred *saved_cred;
+
+ saved_cred = override_creds(filp->f_cred);
err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
&filp->f_path,
fp->stream.name,
true);
+ revert_creds(saved_cred);
if (err)
pr_err("remove xattr failed : %s\n",
fp->stream.name);
@@ -387,10 +391,14 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
* there are not accesses to fp->lock_list.
*/
list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
- if (!list_empty(&smb_lock->clist) && fp->conn) {
- spin_lock(&fp->conn->llist_lock);
- list_del(&smb_lock->clist);
- spin_unlock(&fp->conn->llist_lock);
+ struct ksmbd_conn *conn = smb_lock->conn;
+
+ if (conn) {
+ spin_lock(&conn->llist_lock);
+ list_del_init(&smb_lock->clist);
+ smb_lock->conn = NULL;
+ spin_unlock(&conn->llist_lock);
+ ksmbd_conn_put(conn);
}
list_del(&smb_lock->flist);
@@ -398,6 +406,17 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
kfree(smb_lock);
}
+ /*
+ * Drop fp's strong reference on conn (taken in ksmbd_open_fd() /
+ * ksmbd_reopen_durable_fd()). Durable fps that reached the
+ * scavenger have already had fp->conn cleared by session_fd_check(),
+ * in which case there is nothing to drop here.
+ */
+ if (fp->conn) {
+ ksmbd_conn_put(fp->conn);
+ fp->conn = NULL;
+ }
+
if (ksmbd_stream_fd(fp))
kfree(fp->stream.name);
kfree(fp->owner.name);
@@ -686,10 +705,18 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
INIT_LIST_HEAD(&fp->node);
INIT_LIST_HEAD(&fp->lock_list);
spin_lock_init(&fp->f_lock);
+ mutex_init(&fp->readdir_lock);
atomic_set(&fp->refcount, 1);
fp->filp = filp;
- fp->conn = work->conn;
+ /*
+ * fp owns a strong reference on fp->conn for as long as fp->conn is
+ * non-NULL, so session_fd_check() and __ksmbd_close_fd() never
+ * dereference a dangling pointer. Paired with ksmbd_conn_put() in
+ * session_fd_check() (durable preserve), in __ksmbd_close_fd()
+ * (final close), and on the error paths below.
+ */
+ fp->conn = ksmbd_conn_get(work->conn);
fp->tcon = work->tcon;
fp->volatile_id = KSMBD_NO_FID;
fp->persistent_id = KSMBD_NO_FID;
@@ -711,6 +738,8 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
return fp;
err_out:
+ /* fp->conn was set and refcounted before every branch here. */
+ ksmbd_conn_put(fp->conn);
kmem_cache_free(filp_cache, fp);
return ERR_PTR(ret);
}
@@ -981,16 +1010,21 @@ void ksmbd_stop_durable_scavenger(void)
static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
struct ksmbd_user *user)
{
+ char *name;
+
if (!user)
return -EINVAL;
/* Duplicate the user name to ensure identity persistence */
- fp->owner.name = kstrdup(user->name, GFP_KERNEL);
- if (!fp->owner.name)
+ name = kstrdup(user->name, GFP_KERNEL);
+ if (!name)
return -ENOMEM;
+ spin_lock(&fp->f_lock);
fp->owner.uid = user->uid;
fp->owner.gid = user->gid;
+ fp->owner.name = name;
+ spin_unlock(&fp->f_lock);
return 0;
}
@@ -1008,18 +1042,24 @@ static int ksmbd_vfs_copy_durable_owner(struct ksmbd_file *fp,
bool ksmbd_vfs_compare_durable_owner(struct ksmbd_file *fp,
struct ksmbd_user *user)
{
- if (!user || !fp->owner.name)
+ bool ret = false;
+
+ if (!user)
return false;
+ spin_lock(&fp->f_lock);
+ if (!fp->owner.name)
+ goto out;
+
/* Check if the UID and GID match first (fast path) */
if (fp->owner.uid != user->uid || fp->owner.gid != user->gid)
- return false;
+ goto out;
/* Validate the account name to ensure the same SecurityContext */
- if (strcmp(fp->owner.name, user->name))
- return false;
-
- return true;
+ ret = (strcmp(fp->owner.name, user->name) == 0);
+out:
+ spin_unlock(&fp->f_lock);
+ return ret;
}
static bool session_fd_check(struct ksmbd_tree_connect *tcon,
@@ -1033,25 +1073,38 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
if (!is_reconnectable(fp))
return false;
+ if (WARN_ON_ONCE(!fp->conn))
+ return false;
+
if (ksmbd_vfs_copy_durable_owner(fp, user))
return false;
+ /*
+ * fp owns a strong reference on fp->conn (taken in ksmbd_open_fd()
+ * / ksmbd_reopen_durable_fd()), so conn stays valid for the whole
+ * body of this function regardless of any op->conn puts below.
+ */
conn = fp->conn;
ci = fp->f_ci;
down_write(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn != conn)
continue;
- if (op->conn && atomic_dec_and_test(&op->conn->refcnt))
- kfree(op->conn);
+ ksmbd_conn_put(op->conn);
op->conn = NULL;
}
up_write(&ci->m_lock);
list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
- spin_lock(&fp->conn->llist_lock);
+ struct ksmbd_conn *lock_conn = smb_lock->conn;
+
+ if (!lock_conn)
+ continue;
+ spin_lock(&lock_conn->llist_lock);
list_del_init(&smb_lock->clist);
- spin_unlock(&fp->conn->llist_lock);
+ smb_lock->conn = NULL;
+ spin_unlock(&lock_conn->llist_lock);
+ ksmbd_conn_put(lock_conn);
}
fp->conn = NULL;
@@ -1062,6 +1115,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
fp->durable_scavenger_timeout =
jiffies_to_msecs(jiffies) + fp->durable_timeout;
+ /* Drop fp's own reference on conn. */
+ ksmbd_conn_put(conn);
return true;
}
@@ -1136,28 +1191,47 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
struct ksmbd_lock *smb_lock;
unsigned int old_f_state;
+ write_lock(&global_ft.lock);
if (!fp->is_durable || fp->conn || fp->tcon) {
+ write_unlock(&global_ft.lock);
pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon);
return -EBADF;
}
if (has_file_id(fp->volatile_id)) {
+ write_unlock(&global_ft.lock);
pr_err("Still in use durable fd: %llu\n", fp->volatile_id);
return -EBADF;
}
+ /*
+ * Initialize fp's connection binding before publishing fp into the
+ * session's file table. If __open_id() is ordered first, a
+ * concurrent teardown that iterates the table can observe a valid
+ * volatile_id with fp->conn == NULL and preserve a
+ * partially-initialized fp. fp owns a strong reference on the new
+ * conn (see ksmbd_open_fd()); undo it on __open_id() failure.
+ */
+ fp->conn = ksmbd_conn_get(conn);
+ fp->tcon = work->tcon;
+ write_unlock(&global_ft.lock);
+
old_f_state = fp->f_state;
fp->f_state = FP_NEW;
+
__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
if (!has_file_id(fp->volatile_id)) {
+ write_lock(&global_ft.lock);
+ fp->conn = NULL;
+ fp->tcon = NULL;
+ write_unlock(&global_ft.lock);
+ ksmbd_conn_put(conn);
fp->f_state = old_f_state;
return -EBADF;
}
- fp->conn = conn;
- fp->tcon = work->tcon;
-
list_for_each_entry(smb_lock, &fp->lock_list, flist) {
+ smb_lock->conn = ksmbd_conn_get(conn);
spin_lock(&conn->llist_lock);
list_add_tail(&smb_lock->clist, &conn->lock_list);
spin_unlock(&conn->llist_lock);
@@ -1168,14 +1242,15 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn)
continue;
- op->conn = fp->conn;
- atomic_inc(&op->conn->refcnt);
+ op->conn = ksmbd_conn_get(fp->conn);
}
up_write(&ci->m_lock);
+ spin_lock(&fp->f_lock);
fp->owner.uid = fp->owner.gid = 0;
kfree(fp->owner.name);
fp->owner.name = NULL;
+ spin_unlock(&fp->f_lock);
return 0;
}
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 79d85cf4e13b..4d78ebc4ceca 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -8,6 +8,7 @@
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/spinlock.h>
#include <linux/idr.h>
@@ -31,6 +32,7 @@ struct ksmbd_session;
struct ksmbd_lock {
struct file_lock *fl;
+ struct ksmbd_conn *conn;
struct list_head clist;
struct list_head flist;
struct list_head llist;
@@ -112,6 +114,7 @@ struct ksmbd_file {
/* if ls is happening on directory, below is valid*/
struct ksmbd_readdir_data readdir_data;
+ struct mutex readdir_lock;
int dot_dotdot[2];
unsigned int f_state;
bool reserve_lease_break;
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index f21e73d10724..833dd3f5a3f2 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -114,6 +114,10 @@ static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
len = ops->show(kobj, of->kn->priv, buf);
if (len < 0)
return len;
+ if (len >= (ssize_t)PAGE_SIZE) {
+ printk("fill_read_buffer: %pS returned bad count\n", ops->show);
+ len = PAGE_SIZE - 1;
+ }
if (pos) {
if (len <= pos)
return 0;
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index f5de2030e769..e3bf8122b7dc 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -82,8 +82,9 @@ static int __load_block_bitmap(struct super_block *sb,
int nr_groups = bitmap->s_nr_groups;
if (block_group >= nr_groups) {
- udf_debug("block_group (%u) > nr_groups (%d)\n",
+ udf_debug("block_group (%u) >= nr_groups (%d)\n",
block_group, nr_groups);
+ return -EFSCORRUPTED;
}
if (bitmap->s_block_bitmap[block_group]) {
@@ -679,7 +680,7 @@ void udf_free_blocks(struct super_block *sb, struct inode *inode,
if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) ||
check_add_overflow(blk, count, &blk) ||
- bloc->logicalBlockNum + count > map->s_partition_len) {
+ blk > map->s_partition_len) {
udf_debug("Invalid request to free blocks: (%d, %u), off %u, "
"len %u, partition len %u\n",
partition, bloc->logicalBlockNum, offset, count,
diff --git a/fs/udf/super.c b/fs/udf/super.c
index dbf5faf07912..1debc30203b3 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1232,6 +1232,14 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
map->s_type_specific.s_virtual.s_start_offset =
le16_to_cpu(vat20->lengthHeader);
+ if (map->s_type_specific.s_virtual.s_start_offset
+ > sbi->s_vat_inode->i_size) {
+ udf_err(sb, "Corrupted VAT header length %u (VAT inode size %lld)\n",
+ map->s_type_specific.s_virtual.s_start_offset,
+ sbi->s_vat_inode->i_size);
+ brelse(bh);
+ return -EFSCORRUPTED;
+ }
map->s_type_specific.s_virtual.s_num_entries =
(sbi->s_vat_inode->i_size -
map->s_type_specific.s_virtual.
@@ -1387,7 +1395,8 @@ static int udf_load_sparable_map(struct super_block *sb,
if (ident != 0 ||
strncmp(st->sparingIdent.ident, UDF_ID_SPARING,
strlen(UDF_ID_SPARING)) ||
- sizeof(*st) + le16_to_cpu(st->reallocationTableLen) >
+ struct_size(st, mapEntry,
+ le16_to_cpu(st->reallocationTableLen)) >
sb->s_blocksize) {
brelse(bh);
continue;
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 74c468cc432e..49f074262598 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -300,7 +300,12 @@ static inline bool userfaultfd_huge_must_wait(struct userfaultfd_ctx *ctx,
*/
if (huge_pte_none_mostly(pte))
ret = true;
- if (!huge_pte_write(pte) && (reason & VM_UFFD_WP))
+ /*
+ * Gate the writability check on pte_present(): huge_pte_write() on a
+ * non-present migration entry decodes random offset bits. The
+ * migration completion path re-delivers the fault if still needed.
+ */
+ if (pte_present(pte) && !huge_pte_write(pte) && (reason & VM_UFFD_WP))
ret = true;
out:
return ret;
@@ -375,7 +380,12 @@ static inline bool userfaultfd_must_wait(struct userfaultfd_ctx *ctx,
ptent = ptep_get(pte);
if (pte_none_mostly(ptent))
ret = true;
- if (!pte_write(ptent) && (reason & VM_UFFD_WP))
+ /*
+ * Gate the writability check on pte_present(): pte_write() on a
+ * non-present swap/migration entry decodes random offset bits. The
+ * page-in path re-delivers the fault if it still needs userspace.
+ */
+ if (pte_present(ptent) && !pte_write(ptent) && (reason & VM_UFFD_WP))
ret = true;
pte_unmap(pte);
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 363da37a8e7f..dd565e4e3daf 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -484,7 +484,7 @@ xfs_defer_relog(
* Log an intent-done item for the first pending intent, and finish the work
* items.
*/
-static int
+int
xfs_defer_finish_one(
struct xfs_trans *tp,
struct xfs_defer_pending *dfp)
@@ -676,9 +676,8 @@ xfs_defer_add(
list_add_tail(&dfp->dfp_list, &tp->t_dfops);
}
- list_add_tail(li, &dfp->dfp_work);
+ xfs_defer_add_item(dfp, li);
trace_xfs_defer_add_item(tp->t_mountp, dfp, li);
- dfp->dfp_count++;
}
/*
@@ -714,6 +713,23 @@ xfs_defer_cancel_recovery(
xfs_defer_pending_cancel_work(mp, dfp);
}
+/* Replay the deferred work item created from a recovered log intent item. */
+int
+xfs_defer_finish_recovery(
+ struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp,
+ struct list_head *capture_list)
+{
+ const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type];
+ int error;
+
+ error = ops->recover_work(dfp, capture_list);
+ if (error)
+ trace_xlog_intent_recovery_failed(mp, error,
+ ops->recover_work);
+ return error;
+}
+
/*
* Move deferred ops from one transaction to another and reset the source to
* initial state. This is primarily used to carry state forward across
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index 5dce938ba3d5..ef86a7f9b059 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -41,6 +41,7 @@ void xfs_defer_add(struct xfs_trans *tp, enum xfs_defer_ops_type type,
struct list_head *h);
int xfs_defer_finish_noroll(struct xfs_trans **tp);
int xfs_defer_finish(struct xfs_trans **tp);
+int xfs_defer_finish_one(struct xfs_trans *tp, struct xfs_defer_pending *dfp);
void xfs_defer_cancel(struct xfs_trans *);
void xfs_defer_move(struct xfs_trans *dtp, struct xfs_trans *stp);
@@ -56,6 +57,8 @@ struct xfs_defer_op_type {
void (*finish_cleanup)(struct xfs_trans *tp,
struct xfs_btree_cur *state, int error);
void (*cancel_item)(struct list_head *item);
+ int (*recover_work)(struct xfs_defer_pending *dfp,
+ struct list_head *capture_list);
unsigned int max_items;
};
@@ -129,6 +132,17 @@ void xfs_defer_start_recovery(struct xfs_log_item *lip,
enum xfs_defer_ops_type dfp_type, struct list_head *r_dfops);
void xfs_defer_cancel_recovery(struct xfs_mount *mp,
struct xfs_defer_pending *dfp);
+int xfs_defer_finish_recovery(struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp, struct list_head *capture_list);
+
+static inline void
+xfs_defer_add_item(
+ struct xfs_defer_pending *dfp,
+ struct list_head *work)
+{
+ list_add_tail(work, &dfp->dfp_work);
+ dfp->dfp_count++;
+}
int __init xfs_defer_init_item_caches(void);
void xfs_defer_destroy_item_caches(void);
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 13583df9f239..c8e5d912895b 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -153,9 +153,11 @@ xlog_recover_resv(const struct xfs_trans_res *r)
return ret;
}
+struct xfs_defer_pending;
+
void xlog_recover_intent_item(struct xlog *log, struct xfs_log_item *lip,
xfs_lsn_t lsn, unsigned int dfp_type);
-void xlog_recover_transfer_intent(struct xfs_trans *tp,
+int xlog_recover_finish_intent(struct xfs_trans *tp,
struct xfs_defer_pending *dfp);
#endif /* __XFS_LOG_RECOVER_H__ */
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 889fca3f8de1..440e50c3f5fc 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -546,43 +546,22 @@ xfs_attri_validate(
return xfs_verify_ino(mp, attrp->alfi_ino);
}
-/*
- * Process an attr intent item that was recovered from the log. We need to
- * delete the attr that it describes.
- */
-STATIC int
-xfs_attri_item_recover(
+static inline struct xfs_attr_intent *
+xfs_attri_recover_work(
+ struct xfs_mount *mp,
struct xfs_defer_pending *dfp,
- struct list_head *capture_list)
+ struct xfs_attri_log_format *attrp,
+ struct xfs_inode **ipp,
+ struct xfs_attri_log_nameval *nv)
{
- struct xfs_log_item *lip = dfp->dfp_intent;
- struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
struct xfs_attr_intent *attr;
- struct xfs_mount *mp = lip->li_log->l_mp;
- struct xfs_inode *ip;
struct xfs_da_args *args;
- struct xfs_trans *tp;
- struct xfs_trans_res resv;
- struct xfs_attri_log_format *attrp;
- struct xfs_attri_log_nameval *nv = attrip->attri_nameval;
- int error;
- int total;
int local;
- struct xfs_attrd_log_item *done_item = NULL;
-
- /*
- * First check the validity of the attr described by the ATTRI. If any
- * are bad, then assume that all are bad and just toss the ATTRI.
- */
- attrp = &attrip->attri_format;
- if (!xfs_attri_validate(mp, attrp) ||
- !xfs_attr_namecheck(attrp->alfi_attr_filter, nv->name.i_addr,
- nv->name.i_len))
- return -EFSCORRUPTED;
+ int error;
- error = xlog_recover_iget(mp, attrp->alfi_ino, &ip);
+ error = xlog_recover_iget(mp, attrp->alfi_ino, ipp);
if (error)
- return error;
+ return ERR_PTR(error);
attr = kmem_zalloc(sizeof(struct xfs_attr_intent) +
sizeof(struct xfs_da_args), KM_NOFS);
@@ -600,7 +579,7 @@ xfs_attri_item_recover(
attr->xattri_nameval = xfs_attri_log_nameval_get(nv);
ASSERT(attr->xattri_nameval);
- args->dp = ip;
+ args->dp = *ipp;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->name = nv->name.i_addr;
@@ -624,40 +603,64 @@ xfs_attri_item_recover(
case XFS_ATTRI_OP_FLAGS_REMOVE:
attr->xattri_dela_state = xfs_attr_init_remove_state(args);
break;
- default:
- ASSERT(0);
- error = -EFSCORRUPTED;
- goto out;
}
+ xfs_defer_add_item(dfp, &attr->xattri_list);
+ return attr;
+}
+
+/*
+ * Process an attr intent item that was recovered from the log. We need to
+ * delete the attr that it describes.
+ */
+STATIC int
+xfs_attr_recover_work(
+ struct xfs_defer_pending *dfp,
+ struct list_head *capture_list)
+{
+ struct xfs_log_item *lip = dfp->dfp_intent;
+ struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip);
+ struct xfs_attr_intent *attr;
+ struct xfs_mount *mp = lip->li_log->l_mp;
+ struct xfs_inode *ip = NULL;
+ struct xfs_da_args *args;
+ struct xfs_trans *tp;
+ struct xfs_trans_res resv;
+ struct xfs_attri_log_format *attrp;
+ struct xfs_attri_log_nameval *nv = attrip->attri_nameval;
+ int error;
+ int total;
+
+ /*
+ * First check the validity of the attr described by the ATTRI. If any
+ * are bad, then assume that all are bad and just toss the ATTRI.
+ */
+ attrp = &attrip->attri_format;
+ if (!xfs_attri_validate(mp, attrp) ||
+ !xfs_attr_namecheck(attrp->alfi_attr_filter, nv->name.i_addr,
+ nv->name.i_len))
+ return -EFSCORRUPTED;
+
+ attr = xfs_attri_recover_work(mp, dfp, attrp, &ip, nv);
+ if (IS_ERR(attr))
+ return PTR_ERR(attr);
+ args = attr->xattri_da_args;
+
xfs_init_attr_trans(args, &resv, &total);
resv = xlog_recover_resv(&resv);
error = xfs_trans_alloc(mp, &resv, total, 0, XFS_TRANS_RESERVE, &tp);
if (error)
- goto out;
-
+ return error;
args->trans = tp;
- done_item = xfs_trans_get_attrd(tp, attrip);
- xlog_recover_transfer_intent(tp, dfp);
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, 0);
- error = xfs_xattri_finish_update(attr, done_item);
- if (error == -EAGAIN) {
- /*
- * There's more work to do, so add the intent item to this
- * transaction so that we can continue it later.
- */
- xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr->xattri_list);
- error = xfs_defer_ops_capture_and_commit(tp, capture_list);
- if (error)
- goto out_unlock;
-
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- xfs_irele(ip);
- return 0;
- }
+ error = xlog_recover_finish_intent(tp, dfp);
+ if (error == -EFSCORRUPTED)
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ &attrip->attri_format,
+ sizeof(attrip->attri_format));
if (error) {
xfs_trans_cancel(tp);
goto out_unlock;
@@ -667,8 +670,6 @@ xfs_attri_item_recover(
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_irele(ip);
-out:
- xfs_attr_free_item(attr);
return error;
}
@@ -892,6 +893,7 @@ const struct xfs_defer_op_type xfs_attr_defer_type = {
.create_done = xfs_attr_create_done,
.finish_item = xfs_attr_finish_item,
.cancel_item = xfs_attr_cancel_item,
+ .recover_work = xfs_attr_recover_work,
};
/*
@@ -928,7 +930,6 @@ static const struct xfs_item_ops xfs_attri_item_ops = {
.iop_format = xfs_attri_item_format,
.iop_unpin = xfs_attri_item_unpin,
.iop_release = xfs_attri_item_release,
- .iop_recover = xfs_attri_item_recover,
.iop_match = xfs_attri_item_match,
.iop_relog = xfs_attri_item_relog,
};
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index 969cad575c30..59b438ac37a2 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -438,15 +438,6 @@ xfs_bmap_update_cancel_item(
kmem_cache_free(xfs_bmap_intent_cache, bi);
}
-const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
- .max_items = XFS_BUI_MAX_FAST_EXTENTS,
- .create_intent = xfs_bmap_update_create_intent,
- .abort_intent = xfs_bmap_update_abort_intent,
- .create_done = xfs_bmap_update_create_done,
- .finish_item = xfs_bmap_update_finish_item,
- .cancel_item = xfs_bmap_update_cancel_item,
-};
-
/* Is this recovered BUI ok? */
static inline bool
xfs_bui_validate(
@@ -481,16 +472,45 @@ xfs_bui_validate(
return xfs_verify_fsbext(mp, map->me_startblock, map->me_len);
}
+static inline struct xfs_bmap_intent *
+xfs_bui_recover_work(
+ struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp,
+ struct xfs_inode **ipp,
+ struct xfs_map_extent *map)
+{
+ struct xfs_bmap_intent *bi;
+ int error;
+
+ error = xlog_recover_iget(mp, map->me_owner, ipp);
+ if (error)
+ return ERR_PTR(error);
+
+ bi = kmem_cache_zalloc(xfs_bmap_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+ bi->bi_whichfork = (map->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
+ XFS_ATTR_FORK : XFS_DATA_FORK;
+ bi->bi_type = map->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
+ bi->bi_bmap.br_startblock = map->me_startblock;
+ bi->bi_bmap.br_startoff = map->me_startoff;
+ bi->bi_bmap.br_blockcount = map->me_len;
+ bi->bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
+ XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+ bi->bi_owner = *ipp;
+ xfs_bmap_update_get_group(mp, bi);
+
+ xfs_defer_add_item(dfp, &bi->bi_list);
+ return bi;
+}
+
/*
* Process a bmap update intent item that was recovered from the log.
* We need to update some inode's bmbt.
*/
STATIC int
-xfs_bui_item_recover(
+xfs_bmap_recover_work(
struct xfs_defer_pending *dfp,
struct list_head *capture_list)
{
- struct xfs_bmap_intent fake = { };
struct xfs_trans_res resv;
struct xfs_log_item *lip = dfp->dfp_intent;
struct xfs_bui_log_item *buip = BUI_ITEM(lip);
@@ -498,7 +518,7 @@ xfs_bui_item_recover(
struct xfs_inode *ip = NULL;
struct xfs_mount *mp = lip->li_log->l_mp;
struct xfs_map_extent *map;
- struct xfs_bud_log_item *budp;
+ struct xfs_bmap_intent *work;
int iext_delta;
int error = 0;
@@ -509,13 +529,9 @@ xfs_bui_item_recover(
}
map = &buip->bui_format.bui_extents[0];
- fake.bi_whichfork = (map->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
- XFS_ATTR_FORK : XFS_DATA_FORK;
- fake.bi_type = map->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
-
- error = xlog_recover_iget(mp, map->me_owner, &ip);
- if (error)
- return error;
+ work = xfs_bui_recover_work(mp, dfp, &ip, map);
+ if (IS_ERR(work))
+ return PTR_ERR(work);
/* Allocate transaction and do the work. */
resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
@@ -524,44 +540,27 @@ xfs_bui_item_recover(
if (error)
goto err_rele;
- budp = xfs_trans_get_bud(tp, buip);
- xlog_recover_transfer_intent(tp, dfp);
-
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, 0);
- if (fake.bi_type == XFS_BMAP_MAP)
+ if (work->bi_type == XFS_BMAP_MAP)
iext_delta = XFS_IEXT_ADD_NOSPLIT_CNT;
else
iext_delta = XFS_IEXT_PUNCH_HOLE_CNT;
- error = xfs_iext_count_may_overflow(ip, fake.bi_whichfork, iext_delta);
+ error = xfs_iext_count_may_overflow(ip, work->bi_whichfork, iext_delta);
if (error == -EFBIG)
error = xfs_iext_count_upgrade(tp, ip, iext_delta);
if (error)
goto err_cancel;
- fake.bi_owner = ip;
- fake.bi_bmap.br_startblock = map->me_startblock;
- fake.bi_bmap.br_startoff = map->me_startoff;
- fake.bi_bmap.br_blockcount = map->me_len;
- fake.bi_bmap.br_state = (map->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
- XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
-
- xfs_bmap_update_get_group(mp, &fake);
- error = xfs_trans_log_finish_bmap_update(tp, budp, &fake);
+ error = xlog_recover_finish_intent(tp, dfp);
if (error == -EFSCORRUPTED)
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, map,
- sizeof(*map));
- xfs_bmap_update_put_group(&fake);
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ &buip->bui_format, sizeof(buip->bui_format));
if (error)
goto err_cancel;
- if (fake.bi_bmap.br_blockcount > 0) {
- ASSERT(fake.bi_type == XFS_BMAP_UNMAP);
- xfs_bmap_unmap_extent(tp, ip, &fake.bi_bmap);
- }
-
/*
* Commit transaction, which frees the transaction and saves the inode
* for later replay activities.
@@ -583,6 +582,16 @@ xfs_bui_item_recover(
return error;
}
+const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+ .max_items = XFS_BUI_MAX_FAST_EXTENTS,
+ .create_intent = xfs_bmap_update_create_intent,
+ .abort_intent = xfs_bmap_update_abort_intent,
+ .create_done = xfs_bmap_update_create_done,
+ .finish_item = xfs_bmap_update_finish_item,
+ .cancel_item = xfs_bmap_update_cancel_item,
+ .recover_work = xfs_bmap_recover_work,
+};
+
STATIC bool
xfs_bui_item_match(
struct xfs_log_item *lip,
@@ -623,7 +632,6 @@ static const struct xfs_item_ops xfs_bui_item_ops = {
.iop_format = xfs_bui_item_format,
.iop_unpin = xfs_bui_item_unpin,
.iop_release = xfs_bui_item_release,
- .iop_recover = xfs_bui_item_recover,
.iop_match = xfs_bui_item_match,
.iop_relog = xfs_bui_item_relog,
};
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index ce42208d51be..fa9a34790aa9 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1174,6 +1174,14 @@ xfs_qm_dqflush_check(
type != XFS_DQTYPE_PROJ)
return __this_address;
+ /* bigtime flag should never be set on root dquots */
+ if (dqp->q_type & XFS_DQTYPE_BIGTIME) {
+ if (!xfs_has_bigtime(dqp->q_mount))
+ return __this_address;
+ if (dqp->q_id == 0)
+ return __this_address;
+ }
+
if (dqp->q_id == 0)
return NULL;
@@ -1189,14 +1197,6 @@ xfs_qm_dqflush_check(
!dqp->q_rtb.timer)
return __this_address;
- /* bigtime flag should never be set on root dquots */
- if (dqp->q_type & XFS_DQTYPE_BIGTIME) {
- if (!xfs_has_bigtime(dqp->q_mount))
- return __this_address;
- if (dqp->q_id == 0)
- return __this_address;
- }
-
return NULL;
}
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index c9908fb33765..49e96ffd64e0 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -567,15 +567,6 @@ xfs_extent_free_cancel_item(
kmem_cache_free(xfs_extfree_item_cache, xefi);
}
-const struct xfs_defer_op_type xfs_extent_free_defer_type = {
- .max_items = XFS_EFI_MAX_FAST_EXTENTS,
- .create_intent = xfs_extent_free_create_intent,
- .abort_intent = xfs_extent_free_abort_intent,
- .create_done = xfs_extent_free_create_done,
- .finish_item = xfs_extent_free_finish_item,
- .cancel_item = xfs_extent_free_cancel_item,
-};
-
/*
* AGFL blocks are accounted differently in the reserve pools and are not
* inserted into the busy extent list.
@@ -632,16 +623,6 @@ xfs_agfl_free_finish_item(
return error;
}
-/* sub-type with special handling for AGFL deferred frees */
-const struct xfs_defer_op_type xfs_agfl_free_defer_type = {
- .max_items = XFS_EFI_MAX_FAST_EXTENTS,
- .create_intent = xfs_extent_free_create_intent,
- .abort_intent = xfs_extent_free_abort_intent,
- .create_done = xfs_extent_free_create_done,
- .finish_item = xfs_agfl_free_finish_item,
- .cancel_item = xfs_extent_free_cancel_item,
-};
-
/* Is this recovered EFI ok? */
static inline bool
xfs_efi_validate_ext(
@@ -651,12 +632,31 @@ xfs_efi_validate_ext(
return xfs_verify_fsbext(mp, extp->ext_start, extp->ext_len);
}
+static inline void
+xfs_efi_recover_work(
+ struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp,
+ struct xfs_extent *extp)
+{
+ struct xfs_extent_free_item *xefi;
+
+ xefi = kmem_cache_zalloc(xfs_extfree_item_cache,
+ GFP_KERNEL | __GFP_NOFAIL);
+ xefi->xefi_startblock = extp->ext_start;
+ xefi->xefi_blockcount = extp->ext_len;
+ xefi->xefi_agresv = XFS_AG_RESV_NONE;
+ xefi->xefi_owner = XFS_RMAP_OWN_UNKNOWN;
+ xfs_extent_free_get_group(mp, xefi);
+
+ xfs_defer_add_item(dfp, &xefi->xefi_list);
+}
+
/*
* Process an extent free intent item that was recovered from
* the log. We need to free the extents that it describes.
*/
STATIC int
-xfs_efi_item_recover(
+xfs_extent_free_recover_work(
struct xfs_defer_pending *dfp,
struct list_head *capture_list)
{
@@ -664,11 +664,9 @@ xfs_efi_item_recover(
struct xfs_log_item *lip = dfp->dfp_intent;
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
struct xfs_mount *mp = lip->li_log->l_mp;
- struct xfs_efd_log_item *efdp;
struct xfs_trans *tp;
int i;
int error = 0;
- bool requeue_only = false;
/*
* First check the validity of the extents described by the
@@ -683,6 +681,8 @@ xfs_efi_item_recover(
sizeof(efip->efi_format));
return -EFSCORRUPTED;
}
+
+ xfs_efi_recover_work(mp, dfp, &efip->efi_format.efi_extents[i]);
}
resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
@@ -690,50 +690,13 @@ xfs_efi_item_recover(
if (error)
return error;
- efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents);
- xlog_recover_transfer_intent(tp, dfp);
-
- for (i = 0; i < efip->efi_format.efi_nextents; i++) {
- struct xfs_extent_free_item fake = {
- .xefi_owner = XFS_RMAP_OWN_UNKNOWN,
- .xefi_agresv = XFS_AG_RESV_NONE,
- };
- struct xfs_extent *extp;
-
- extp = &efip->efi_format.efi_extents[i];
-
- fake.xefi_startblock = extp->ext_start;
- fake.xefi_blockcount = extp->ext_len;
-
- if (!requeue_only) {
- xfs_extent_free_get_group(mp, &fake);
- error = xfs_trans_free_extent(tp, efdp, &fake);
- xfs_extent_free_put_group(&fake);
- }
-
- /*
- * If we can't free the extent without potentially deadlocking,
- * requeue the rest of the extents to a new so that they get
- * run again later with a new transaction context.
- */
- if (error == -EAGAIN || requeue_only) {
- error = xfs_free_extent_later(tp, fake.xefi_startblock,
- fake.xefi_blockcount,
- &XFS_RMAP_OINFO_ANY_OWNER,
- fake.xefi_agresv);
- if (!error) {
- requeue_only = true;
- continue;
- }
- }
-
- if (error == -EFSCORRUPTED)
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- extp, sizeof(*extp));
- if (error)
- goto abort_error;
-
- }
+ error = xlog_recover_finish_intent(tp, dfp);
+ if (error == -EFSCORRUPTED)
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ &efip->efi_format,
+ sizeof(efip->efi_format));
+ if (error)
+ goto abort_error;
return xfs_defer_ops_capture_and_commit(tp, capture_list);
@@ -742,6 +705,27 @@ xfs_efi_item_recover(
return error;
}
+const struct xfs_defer_op_type xfs_extent_free_defer_type = {
+ .max_items = XFS_EFI_MAX_FAST_EXTENTS,
+ .create_intent = xfs_extent_free_create_intent,
+ .abort_intent = xfs_extent_free_abort_intent,
+ .create_done = xfs_extent_free_create_done,
+ .finish_item = xfs_extent_free_finish_item,
+ .cancel_item = xfs_extent_free_cancel_item,
+ .recover_work = xfs_extent_free_recover_work,
+};
+
+/* sub-type with special handling for AGFL deferred frees */
+const struct xfs_defer_op_type xfs_agfl_free_defer_type = {
+ .max_items = XFS_EFI_MAX_FAST_EXTENTS,
+ .create_intent = xfs_extent_free_create_intent,
+ .abort_intent = xfs_extent_free_abort_intent,
+ .create_done = xfs_extent_free_create_done,
+ .finish_item = xfs_agfl_free_finish_item,
+ .cancel_item = xfs_extent_free_cancel_item,
+ .recover_work = xfs_extent_free_recover_work,
+};
+
STATIC bool
xfs_efi_item_match(
struct xfs_log_item *lip,
@@ -784,7 +768,6 @@ static const struct xfs_item_ops xfs_efi_item_ops = {
.iop_format = xfs_efi_item_format,
.iop_unpin = xfs_efi_item_unpin,
.iop_release = xfs_efi_item_release,
- .iop_recover = xfs_efi_item_recover,
.iop_match = xfs_efi_item_match,
.iop_relog = xfs_efi_item_relog,
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index f708bf4104b1..6989c0253d37 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1028,7 +1028,7 @@ xlog_verify_head(
{
struct xlog_rec_header *tmp_rhead;
char *tmp_buffer;
- xfs_daddr_t first_bad;
+ xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL;
xfs_daddr_t tmp_rhead_blk;
int found;
int error;
@@ -1057,7 +1057,8 @@ xlog_verify_head(
*/
error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
XLOG_RECOVER_CRCPASS, &first_bad);
- if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
+ if ((error == -EFSBADCRC || error == -EFSCORRUPTED) &&
+ first_bad != XFS_BUF_DADDR_NULL) {
/*
* We've hit a potential torn write. Reset the error and warn
* about it.
@@ -1871,6 +1872,15 @@ xlog_recover_reorder_trans(
list_for_each_entry_safe(item, n, &sort_list, ri_list) {
enum xlog_recover_reorder fate = XLOG_REORDER_ITEM_LIST;
+ /* a committed item with no regions has a NULL ri_buf[0] */
+ if (!item->ri_cnt || !item->ri_buf) {
+ xfs_warn(log->l_mp,
+ "%s: committed log item has no regions",
+ __func__);
+ error = -EFSCORRUPTED;
+ break;
+ }
+
item->ri_ops = xlog_find_item_ops(item);
if (!item->ri_ops) {
xfs_warn(log->l_mp,
@@ -2565,17 +2575,14 @@ xlog_recover_process_intents(
#endif
list_for_each_entry_safe(dfp, n, &log->r_dfops, dfp_list) {
- struct xfs_log_item *lip = dfp->dfp_intent;
- const struct xfs_item_ops *ops = lip->li_ops;
-
- ASSERT(xlog_item_is_intent(lip));
+ ASSERT(xlog_item_is_intent(dfp->dfp_intent));
/*
* We should never see a redo item with a LSN higher than
* the last transaction we found in the log at the start
* of recovery.
*/
- ASSERT(XFS_LSN_CMP(last_lsn, lip->li_lsn) >= 0);
+ ASSERT(XFS_LSN_CMP(last_lsn, dfp->dfp_intent->li_lsn) >= 0);
/*
* NOTE: If your intent processing routine can create more
@@ -2584,16 +2591,13 @@ xlog_recover_process_intents(
* replayed in the wrong order!
*
* The recovery function can free the log item, so we must not
- * access lip after it returns.
+ * access dfp->dfp_intent after it returns. It must dispose of
+ * @dfp if it returns 0.
*/
- error = ops->iop_recover(dfp, &capture_list);
- if (error) {
- trace_xlog_intent_recovery_failed(log->l_mp, error,
- ops->iop_recover);
+ error = xfs_defer_finish_recovery(log->l_mp, dfp,
+ &capture_list);
+ if (error)
break;
- }
-
- xfs_defer_cancel_recovery(log->l_mp, dfp);
}
if (error)
goto err;
@@ -2627,15 +2631,22 @@ xlog_recover_cancel_intents(
}
/*
- * Transfer ownership of the recovered log intent item to the recovery
- * transaction.
+ * Transfer ownership of the recovered pending work to the recovery transaction
+ * and try to finish the work. If there is more work to be done, the dfp will
+ * remain attached to the transaction. If not, the dfp is freed.
*/
-void
-xlog_recover_transfer_intent(
+int
+xlog_recover_finish_intent(
struct xfs_trans *tp,
struct xfs_defer_pending *dfp)
{
- dfp->dfp_intent = NULL;
+ int error;
+
+ list_move(&dfp->dfp_list, &tp->t_dfops);
+ error = xfs_defer_finish_one(tp, dfp);
+ if (error == -EAGAIN)
+ return 0;
+ return error;
}
/*
@@ -3554,4 +3565,3 @@ xlog_recover_cancel(
if (xlog_recovery_needed(log))
xlog_recover_cancel_intents(log);
}
-
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
index f1b259223802..48f1a38b272e 100644
--- a/fs/xfs/xfs_refcount_item.c
+++ b/fs/xfs/xfs_refcount_item.c
@@ -433,16 +433,6 @@ xfs_refcount_update_cancel_item(
kmem_cache_free(xfs_refcount_intent_cache, ri);
}
-const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
- .max_items = XFS_CUI_MAX_FAST_EXTENTS,
- .create_intent = xfs_refcount_update_create_intent,
- .abort_intent = xfs_refcount_update_abort_intent,
- .create_done = xfs_refcount_update_create_done,
- .finish_item = xfs_refcount_update_finish_item,
- .finish_cleanup = xfs_refcount_finish_one_cleanup,
- .cancel_item = xfs_refcount_update_cancel_item,
-};
-
/* Is this recovered CUI ok? */
static inline bool
xfs_cui_validate_phys(
@@ -468,24 +458,38 @@ xfs_cui_validate_phys(
return xfs_verify_fsbext(mp, pmap->pe_startblock, pmap->pe_len);
}
+static inline void
+xfs_cui_recover_work(
+ struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp,
+ struct xfs_phys_extent *pmap)
+{
+ struct xfs_refcount_intent *ri;
+
+ ri = kmem_cache_alloc(xfs_refcount_intent_cache,
+ GFP_NOFS | __GFP_NOFAIL);
+ ri->ri_type = pmap->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK;
+ ri->ri_startblock = pmap->pe_startblock;
+ ri->ri_blockcount = pmap->pe_len;
+ xfs_refcount_update_get_group(mp, ri);
+
+ xfs_defer_add_item(dfp, &ri->ri_list);
+}
+
/*
* Process a refcount update intent item that was recovered from the log.
* We need to update the refcountbt.
*/
STATIC int
-xfs_cui_item_recover(
+xfs_refcount_recover_work(
struct xfs_defer_pending *dfp,
struct list_head *capture_list)
{
struct xfs_trans_res resv;
struct xfs_log_item *lip = dfp->dfp_intent;
struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
- struct xfs_cud_log_item *cudp;
struct xfs_trans *tp;
- struct xfs_btree_cur *rcur = NULL;
struct xfs_mount *mp = lip->li_log->l_mp;
- unsigned int refc_type;
- bool requeue_only = false;
int i;
int error = 0;
@@ -502,6 +506,8 @@ xfs_cui_item_recover(
sizeof(cuip->cui_format));
return -EFSCORRUPTED;
}
+
+ xfs_cui_recover_work(mp, dfp, &cuip->cui_format.cui_extents[i]);
}
/*
@@ -522,86 +528,32 @@ xfs_cui_item_recover(
if (error)
return error;
- cudp = xfs_trans_get_cud(tp, cuip);
- xlog_recover_transfer_intent(tp, dfp);
-
- for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
- struct xfs_refcount_intent fake = { };
- struct xfs_phys_extent *pmap;
-
- pmap = &cuip->cui_format.cui_extents[i];
- refc_type = pmap->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK;
- switch (refc_type) {
- case XFS_REFCOUNT_INCREASE:
- case XFS_REFCOUNT_DECREASE:
- case XFS_REFCOUNT_ALLOC_COW:
- case XFS_REFCOUNT_FREE_COW:
- fake.ri_type = refc_type;
- break;
- default:
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- &cuip->cui_format,
- sizeof(cuip->cui_format));
- error = -EFSCORRUPTED;
- goto abort_error;
- }
-
- fake.ri_startblock = pmap->pe_startblock;
- fake.ri_blockcount = pmap->pe_len;
-
- if (!requeue_only) {
- xfs_refcount_update_get_group(mp, &fake);
- error = xfs_trans_log_finish_refcount_update(tp, cudp,
- &fake, &rcur);
- xfs_refcount_update_put_group(&fake);
- }
- if (error == -EFSCORRUPTED)
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- &cuip->cui_format,
- sizeof(cuip->cui_format));
- if (error)
- goto abort_error;
-
- /* Requeue what we didn't finish. */
- if (fake.ri_blockcount > 0) {
- struct xfs_bmbt_irec irec = {
- .br_startblock = fake.ri_startblock,
- .br_blockcount = fake.ri_blockcount,
- };
-
- switch (fake.ri_type) {
- case XFS_REFCOUNT_INCREASE:
- xfs_refcount_increase_extent(tp, &irec);
- break;
- case XFS_REFCOUNT_DECREASE:
- xfs_refcount_decrease_extent(tp, &irec);
- break;
- case XFS_REFCOUNT_ALLOC_COW:
- xfs_refcount_alloc_cow_extent(tp,
- irec.br_startblock,
- irec.br_blockcount);
- break;
- case XFS_REFCOUNT_FREE_COW:
- xfs_refcount_free_cow_extent(tp,
- irec.br_startblock,
- irec.br_blockcount);
- break;
- default:
- ASSERT(0);
- }
- requeue_only = true;
- }
- }
+ error = xlog_recover_finish_intent(tp, dfp);
+ if (error == -EFSCORRUPTED)
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ &cuip->cui_format,
+ sizeof(cuip->cui_format));
+ if (error)
+ goto abort_error;
- xfs_refcount_finish_one_cleanup(tp, rcur, error);
return xfs_defer_ops_capture_and_commit(tp, capture_list);
abort_error:
- xfs_refcount_finish_one_cleanup(tp, rcur, error);
xfs_trans_cancel(tp);
return error;
}
+const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
+ .max_items = XFS_CUI_MAX_FAST_EXTENTS,
+ .create_intent = xfs_refcount_update_create_intent,
+ .abort_intent = xfs_refcount_update_abort_intent,
+ .create_done = xfs_refcount_update_create_done,
+ .finish_item = xfs_refcount_update_finish_item,
+ .finish_cleanup = xfs_refcount_finish_one_cleanup,
+ .cancel_item = xfs_refcount_update_cancel_item,
+ .recover_work = xfs_refcount_recover_work,
+};
+
STATIC bool
xfs_cui_item_match(
struct xfs_log_item *lip,
@@ -642,7 +594,6 @@ static const struct xfs_item_ops xfs_cui_item_ops = {
.iop_format = xfs_cui_item_format,
.iop_unpin = xfs_cui_item_unpin,
.iop_release = xfs_cui_item_release,
- .iop_recover = xfs_cui_item_recover,
.iop_match = xfs_cui_item_match,
.iop_relog = xfs_cui_item_relog,
};
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 4058cf361d21..23d1650aee48 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -389,6 +389,7 @@ xfs_reflink_fill_cow_hole(
struct xfs_trans *tp;
xfs_filblks_t resaligned;
xfs_extlen_t resblks;
+ unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
int nimaps;
int error;
bool found;
@@ -407,6 +408,22 @@ xfs_reflink_fill_cow_hole(
*lockmode = XFS_ILOCK_EXCL;
+ /*
+ * The data fork mapping may have changed while we dropped the ILOCK
+ * (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
+ * CoW cycle including xfs_reflink_end_cow(), which remaps this offset
+ * and drops the refcount of the old shared block). Re-read it so the
+ * shared-status recheck below and the caller's in-place iomap both
+ * operate on the current mapping rather than a stale physical block.
+ */
+ if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
+ nimaps = 1;
+ error = xfs_bmapi_read(ip, imap->br_startoff,
+ imap->br_blockcount, imap, &nimaps, 0);
+ if (error)
+ goto out_trans_cancel;
+ }
+
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found);
if (error || !*shared)
goto out_trans_cancel;
@@ -453,6 +470,8 @@ xfs_reflink_fill_delalloc(
bool found;
do {
+ unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
+
xfs_iunlock(ip, *lockmode);
*lockmode = 0;
@@ -463,6 +482,23 @@ xfs_reflink_fill_delalloc(
*lockmode = XFS_ILOCK_EXCL;
+ /*
+ * The data fork mapping may have changed while we dropped the
+ * ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can
+ * complete a full CoW cycle including xfs_reflink_end_cow(),
+ * which remaps this offset and drops the refcount of the old
+ * shared block). Re-read it so the shared-status recheck
+ * below and the caller's in-place iomap both operate on the
+ * current mapping rather than a stale physical block.
+ */
+ if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
+ nimaps = 1;
+ error = xfs_bmapi_read(ip, imap->br_startoff,
+ imap->br_blockcount, imap, &nimaps, 0);
+ if (error)
+ goto out_trans_cancel;
+ }
+
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared,
&found);
if (error || !*shared)
diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c
index 5e8a02d2b045..23684bc2ab85 100644
--- a/fs/xfs/xfs_rmap_item.c
+++ b/fs/xfs/xfs_rmap_item.c
@@ -452,16 +452,6 @@ xfs_rmap_update_cancel_item(
kmem_cache_free(xfs_rmap_intent_cache, ri);
}
-const struct xfs_defer_op_type xfs_rmap_update_defer_type = {
- .max_items = XFS_RUI_MAX_FAST_EXTENTS,
- .create_intent = xfs_rmap_update_create_intent,
- .abort_intent = xfs_rmap_update_abort_intent,
- .create_done = xfs_rmap_update_create_done,
- .finish_item = xfs_rmap_update_finish_item,
- .finish_cleanup = xfs_rmap_finish_one_cleanup,
- .cancel_item = xfs_rmap_update_cancel_item,
-};
-
/* Is this recovered RUI ok? */
static inline bool
xfs_rui_validate_map(
@@ -498,21 +488,72 @@ xfs_rui_validate_map(
return xfs_verify_fsbext(mp, map->me_startblock, map->me_len);
}
+static inline void
+xfs_rui_recover_work(
+ struct xfs_mount *mp,
+ struct xfs_defer_pending *dfp,
+ const struct xfs_map_extent *map)
+{
+ struct xfs_rmap_intent *ri;
+
+ ri = kmem_cache_alloc(xfs_rmap_intent_cache, GFP_NOFS | __GFP_NOFAIL);
+
+ switch (map->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) {
+ case XFS_RMAP_EXTENT_MAP:
+ ri->ri_type = XFS_RMAP_MAP;
+ break;
+ case XFS_RMAP_EXTENT_MAP_SHARED:
+ ri->ri_type = XFS_RMAP_MAP_SHARED;
+ break;
+ case XFS_RMAP_EXTENT_UNMAP:
+ ri->ri_type = XFS_RMAP_UNMAP;
+ break;
+ case XFS_RMAP_EXTENT_UNMAP_SHARED:
+ ri->ri_type = XFS_RMAP_UNMAP_SHARED;
+ break;
+ case XFS_RMAP_EXTENT_CONVERT:
+ ri->ri_type = XFS_RMAP_CONVERT;
+ break;
+ case XFS_RMAP_EXTENT_CONVERT_SHARED:
+ ri->ri_type = XFS_RMAP_CONVERT_SHARED;
+ break;
+ case XFS_RMAP_EXTENT_ALLOC:
+ ri->ri_type = XFS_RMAP_ALLOC;
+ break;
+ case XFS_RMAP_EXTENT_FREE:
+ ri->ri_type = XFS_RMAP_FREE;
+ break;
+ default:
+ ASSERT(0);
+ return;
+ }
+
+ ri->ri_owner = map->me_owner;
+ ri->ri_whichfork = (map->me_flags & XFS_RMAP_EXTENT_ATTR_FORK) ?
+ XFS_ATTR_FORK : XFS_DATA_FORK;
+ ri->ri_bmap.br_startblock = map->me_startblock;
+ ri->ri_bmap.br_startoff = map->me_startoff;
+ ri->ri_bmap.br_blockcount = map->me_len;
+ ri->ri_bmap.br_state = (map->me_flags & XFS_RMAP_EXTENT_UNWRITTEN) ?
+ XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+ xfs_rmap_update_get_group(mp, ri);
+
+ xfs_defer_add_item(dfp, &ri->ri_list);
+}
+
/*
* Process an rmap update intent item that was recovered from the log.
* We need to update the rmapbt.
*/
STATIC int
-xfs_rui_item_recover(
+xfs_rmap_recover_work(
struct xfs_defer_pending *dfp,
struct list_head *capture_list)
{
struct xfs_trans_res resv;
struct xfs_log_item *lip = dfp->dfp_intent;
struct xfs_rui_log_item *ruip = RUI_ITEM(lip);
- struct xfs_rud_log_item *rudp;
struct xfs_trans *tp;
- struct xfs_btree_cur *rcur = NULL;
struct xfs_mount *mp = lip->li_log->l_mp;
int i;
int error = 0;
@@ -530,6 +571,8 @@ xfs_rui_item_recover(
sizeof(ruip->rui_format));
return -EFSCORRUPTED;
}
+
+ xfs_rui_recover_work(mp, dfp, &ruip->rui_format.rui_extents[i]);
}
resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
@@ -538,77 +581,32 @@ xfs_rui_item_recover(
if (error)
return error;
- rudp = xfs_trans_get_rud(tp, ruip);
- xlog_recover_transfer_intent(tp, dfp);
-
- for (i = 0; i < ruip->rui_format.rui_nextents; i++) {
- struct xfs_rmap_intent fake = { };
- struct xfs_map_extent *map;
-
- map = &ruip->rui_format.rui_extents[i];
- switch (map->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) {
- case XFS_RMAP_EXTENT_MAP:
- fake.ri_type = XFS_RMAP_MAP;
- break;
- case XFS_RMAP_EXTENT_MAP_SHARED:
- fake.ri_type = XFS_RMAP_MAP_SHARED;
- break;
- case XFS_RMAP_EXTENT_UNMAP:
- fake.ri_type = XFS_RMAP_UNMAP;
- break;
- case XFS_RMAP_EXTENT_UNMAP_SHARED:
- fake.ri_type = XFS_RMAP_UNMAP_SHARED;
- break;
- case XFS_RMAP_EXTENT_CONVERT:
- fake.ri_type = XFS_RMAP_CONVERT;
- break;
- case XFS_RMAP_EXTENT_CONVERT_SHARED:
- fake.ri_type = XFS_RMAP_CONVERT_SHARED;
- break;
- case XFS_RMAP_EXTENT_ALLOC:
- fake.ri_type = XFS_RMAP_ALLOC;
- break;
- case XFS_RMAP_EXTENT_FREE:
- fake.ri_type = XFS_RMAP_FREE;
- break;
- default:
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- &ruip->rui_format,
- sizeof(ruip->rui_format));
- error = -EFSCORRUPTED;
- goto abort_error;
- }
-
- fake.ri_owner = map->me_owner;
- fake.ri_whichfork = (map->me_flags & XFS_RMAP_EXTENT_ATTR_FORK) ?
- XFS_ATTR_FORK : XFS_DATA_FORK;
- fake.ri_bmap.br_startblock = map->me_startblock;
- fake.ri_bmap.br_startoff = map->me_startoff;
- fake.ri_bmap.br_blockcount = map->me_len;
- fake.ri_bmap.br_state = (map->me_flags & XFS_RMAP_EXTENT_UNWRITTEN) ?
- XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
-
- xfs_rmap_update_get_group(mp, &fake);
- error = xfs_trans_log_finish_rmap_update(tp, rudp, &fake,
- &rcur);
- if (error == -EFSCORRUPTED)
- XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- map, sizeof(*map));
- xfs_rmap_update_put_group(&fake);
- if (error)
- goto abort_error;
-
- }
+ error = xlog_recover_finish_intent(tp, dfp);
+ if (error == -EFSCORRUPTED)
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ &ruip->rui_format,
+ sizeof(ruip->rui_format));
+ if (error)
+ goto abort_error;
- xfs_rmap_finish_one_cleanup(tp, rcur, error);
return xfs_defer_ops_capture_and_commit(tp, capture_list);
abort_error:
- xfs_rmap_finish_one_cleanup(tp, rcur, error);
xfs_trans_cancel(tp);
return error;
}
+const struct xfs_defer_op_type xfs_rmap_update_defer_type = {
+ .max_items = XFS_RUI_MAX_FAST_EXTENTS,
+ .create_intent = xfs_rmap_update_create_intent,
+ .abort_intent = xfs_rmap_update_abort_intent,
+ .create_done = xfs_rmap_update_create_done,
+ .finish_item = xfs_rmap_update_finish_item,
+ .finish_cleanup = xfs_rmap_finish_one_cleanup,
+ .cancel_item = xfs_rmap_update_cancel_item,
+ .recover_work = xfs_rmap_recover_work,
+};
+
STATIC bool
xfs_rui_item_match(
struct xfs_log_item *lip,
@@ -649,7 +647,6 @@ static const struct xfs_item_ops xfs_rui_item_ops = {
.iop_format = xfs_rui_item_format,
.iop_unpin = xfs_rui_item_unpin,
.iop_release = xfs_rui_item_release,
- .iop_recover = xfs_rui_item_recover,
.iop_match = xfs_rui_item_match,
.iop_relog = xfs_rui_item_relog,
};
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index ead65f5f8dc3..ae32ffe9a1b1 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -66,8 +66,6 @@ struct xfs_log_item {
{ (1u << XFS_LI_DIRTY), "DIRTY" }, \
{ (1u << XFS_LI_WHITEOUT), "WHITEOUT" }
-struct xfs_defer_pending;
-
struct xfs_item_ops {
unsigned flags;
void (*iop_size)(struct xfs_log_item *, int *, int *);
@@ -80,8 +78,6 @@ struct xfs_item_ops {
xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t);
uint (*iop_push)(struct xfs_log_item *, struct list_head *);
void (*iop_release)(struct xfs_log_item *);
- int (*iop_recover)(struct xfs_defer_pending *dfp,
- struct list_head *capture_list);
bool (*iop_match)(struct xfs_log_item *item, uint64_t id);
struct xfs_log_item *(*iop_relog)(struct xfs_log_item *intent,
struct xfs_trans *tp);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index d9c20ae23b63..fea9ff235f07 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -521,6 +521,8 @@ int acpi_dev_install_notify_handler(struct acpi_device *adev,
void acpi_dev_remove_notify_handler(struct acpi_device *adev,
u32 handler_type,
acpi_notify_handler handler);
+int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type,
+ acpi_notify_handler handler);
extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32);
extern int register_acpi_notifier(struct notifier_block *);
extern int unregister_acpi_notifier(struct notifier_block *);
diff --git a/include/asm-generic/bitops/lock.h b/include/asm-generic/bitops/lock.h
index 40913516e654..b78be6c6c73e 100644
--- a/include/asm-generic/bitops/lock.h
+++ b/include/asm-generic/bitops/lock.h
@@ -16,16 +16,16 @@
* It can be used to implement bit locks.
*/
static __always_inline int
-arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p)
+arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *addr)
{
long old;
unsigned long mask = BIT_MASK(nr);
- p += BIT_WORD(nr);
- if (READ_ONCE(*p) & mask)
+ addr += BIT_WORD(nr);
+ if (READ_ONCE(*addr) & mask)
return 1;
- old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)p);
+ old = raw_atomic_long_fetch_or_acquire(mask, (atomic_long_t *)addr);
return !!(old & mask);
}
@@ -38,10 +38,10 @@ arch_test_and_set_bit_lock(unsigned int nr, volatile unsigned long *p)
* This operation is atomic and provides release barrier semantics.
*/
static __always_inline void
-arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p)
+arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *addr)
{
- p += BIT_WORD(nr);
- raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)p);
+ addr += BIT_WORD(nr);
+ raw_atomic_long_fetch_andnot_release(BIT_MASK(nr), (atomic_long_t *)addr);
}
/**
@@ -56,14 +56,14 @@ arch_clear_bit_unlock(unsigned int nr, volatile unsigned long *p)
* See for example x86's implementation.
*/
static inline void
-arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *p)
+arch___clear_bit_unlock(unsigned int nr, volatile unsigned long *addr)
{
unsigned long old;
- p += BIT_WORD(nr);
- old = READ_ONCE(*p);
+ addr += BIT_WORD(nr);
+ old = READ_ONCE(*addr);
old &= ~BIT_MASK(nr);
- raw_atomic_long_set_release((atomic_long_t *)p, old);
+ raw_atomic_long_set_release((atomic_long_t *)addr, old);
}
/**
diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h
index af5ad51d3eef..1e3dd1726674 100644
--- a/include/crypto/drbg.h
+++ b/include/crypto/drbg.h
@@ -171,19 +171,15 @@ static inline size_t drbg_max_request_bytes(struct drbg_state *drbg)
return (1 << 16);
}
+/*
+ * SP800-90A allows implementations to support additional info / personalization
+ * strings of up to 2**35 bits. Implementations can have a smaller maximum. We
+ * use 2**35 - 16 bits == U32_MAX - 1 bytes so that the max + 1 always fits in a
+ * size_t, allowing drbg_healthcheck_sanity() to verify its enforcement.
+ */
static inline size_t drbg_max_addtl(struct drbg_state *drbg)
{
- /* SP800-90A requires 2**35 bytes additional info str / pers str */
-#if (__BITS_PER_LONG == 32)
- /*
- * SP800-90A allows smaller maximum numbers to be returned -- we
- * return SIZE_MAX - 1 to allow the verification of the enforcement
- * of this value in drbg_healthcheck_sanity.
- */
- return (SIZE_MAX - 1);
-#else
- return (1UL<<35);
-#endif
+ return U32_MAX - 1;
}
static inline size_t drbg_max_requests(struct drbg_state *drbg)
diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h
index 17f9f015bf7d..071221fe5c57 100644
--- a/include/dt-bindings/power/fsl,imx93-power.h
+++ b/include/dt-bindings/power/fsl,imx93-power.h
@@ -11,5 +11,6 @@
#define IMX93_MEDIABLK_PD_PXP 2
#define IMX93_MEDIABLK_PD_LCDIF 3
#define IMX93_MEDIABLK_PD_ISI 4
+#define IMX93_MEDIABLK_PD_MIPI_PHY 5
#endif
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 517c665da259..625210306813 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -180,6 +180,8 @@ extern void audit_log_lost(const char *message);
extern int audit_log_task_context(struct audit_buffer *ab);
extern void audit_log_task_info(struct audit_buffer *ab);
+extern int audit_log_nf_skb(struct audit_buffer *ab,
+ const struct sk_buff *skb, u8 nfproto);
extern int audit_update_lsm_rules(void);
@@ -245,6 +247,12 @@ static inline int audit_log_task_context(struct audit_buffer *ab)
static inline void audit_log_task_info(struct audit_buffer *ab)
{ }
+static inline int audit_log_nf_skb(struct audit_buffer *ab,
+ const struct sk_buff *skb, u8 nfproto)
+{
+ return 0;
+}
+
static inline kuid_t audit_get_loginuid(struct task_struct *tsk)
{
return INVALID_UID;
diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h
index 2ad261082bba..c5c9d89c73ed 100644
--- a/include/linux/backing-dev-defs.h
+++ b/include/linux/backing-dev-defs.h
@@ -152,6 +152,10 @@ struct bdi_writeback {
struct list_head blkcg_node; /* anchored at blkcg->cgwb_list */
struct list_head b_attached; /* attached inodes, protected by list_lock */
struct list_head offline_node; /* anchored at offline_cgwbs */
+ struct work_struct switch_work; /* work used to perform inode switching
+ * to this wb */
+ struct llist_head switch_wbs_ctxs; /* queued contexts for
+ * writeback switching */
union {
struct work_struct release_work;
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index b2342eebc8d2..07729a31198e 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -20,6 +20,8 @@
#define BITS_TO_U32(nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))
#define BITS_TO_BYTES(nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(char))
+#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE)
+
extern unsigned int __sw_hweight8(unsigned int w);
extern unsigned int __sw_hweight16(unsigned int w);
extern unsigned int __sw_hweight32(unsigned int w);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 102115688627..398fa28c388d 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2047,6 +2047,9 @@ bpf_prog_run_array_uprobe(const struct bpf_prog_array *array,
return ret;
}
+#define bpf_rcu_lock_held() \
+ (rcu_read_lock_held() || rcu_read_lock_trace_held() || rcu_read_lock_bh_held())
+
#ifdef CONFIG_BPF_SYSCALL
DECLARE_PER_CPU(int, bpf_prog_active);
extern struct mutex bpf_stats_enabled_mutex;
diff --git a/include/linux/bpf_local_storage.h b/include/linux/bpf_local_storage.h
index 173ec7f43ed1..81147011238f 100644
--- a/include/linux/bpf_local_storage.h
+++ b/include/linux/bpf_local_storage.h
@@ -18,9 +18,6 @@
#define BPF_LOCAL_STORAGE_CACHE_SIZE 16
-#define bpf_rcu_lock_held() \
- (rcu_read_lock_held() || rcu_read_lock_trace_held() || \
- rcu_read_lock_bh_held())
struct bpf_local_storage_map_bucket {
struct hlist_head list;
raw_spinlock_t lock;
diff --git a/include/linux/damon.h b/include/linux/damon.h
index 8d88ac52a72d..16d28a190dae 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -669,9 +669,13 @@ static inline bool damon_target_has_pid(const struct damon_ctx *ctx)
static inline unsigned int damon_max_nr_accesses(const struct damon_attrs *attrs)
{
- /* {aggr,sample}_interval are unsigned long, hence could overflow */
- return min(attrs->aggr_interval / attrs->sample_interval,
+ unsigned long sample_interval;
+ unsigned long max_nr_accesses;
+
+ sample_interval = attrs->sample_interval ? : 1;
+ max_nr_accesses = min(attrs->aggr_interval / sample_interval,
(unsigned long)UINT_MAX);
+ return max_nr_accesses ? : 1;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index 2a9017eec15c..f4b321236c66 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1125,9 +1125,12 @@ static inline struct device_node *dev_of_node(struct device *dev)
static inline bool dev_has_sync_state(struct device *dev)
{
+ struct device_driver *drv;
+
if (!dev)
return false;
- if (dev->driver && dev->driver->sync_state)
+ drv = READ_ONCE(dev->driver);
+ if (drv && drv->sync_state)
return true;
if (dev->bus && dev->bus->sync_state)
return true;
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index 297231854ada..6f96d1c6d10f 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -42,7 +42,8 @@ extern const struct header_ops eth_header_ops;
int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
const void *daddr, const void *saddr, unsigned len);
-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
+int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr);
int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh,
__be16 type);
void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev,
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index fed8bec024db..8414b7ff2543 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -102,6 +102,7 @@ enum f2fs_error {
ERROR_CORRUPTED_XATTR,
ERROR_INVALID_NODE_REFERENCE,
ERROR_INCONSISTENT_NAT,
+ ERROR_INCONSISTENT_ORPHAN,
ERROR_MAX,
};
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 30fe140d4888..f2e351cc543d 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -22,6 +22,7 @@
#include <linux/vmalloc.h>
#include <linux/sockptr.h>
#include <crypto/sha1.h>
+#include <linux/static_call.h>
#include <linux/u64_stats_sync.h>
#include <net/sch_generic.h>
@@ -1041,6 +1042,15 @@ extern long bpf_jit_limit_max;
typedef void (*bpf_jit_fill_hole_t)(void *area, unsigned int size);
+/*
+ * Flush the indirect branch predictors before reusing JIT memory, so that
+ * indirect jumps into a newly written program don't reuse predictions left
+ * behind by an old program that occupied the same space.
+ */
+void bpf_arch_pred_flush(void);
+DECLARE_STATIC_CALL(bpf_arch_pred_flush, bpf_arch_pred_flush);
+DECLARE_STATIC_KEY_FALSE(bpf_pred_flush_enabled);
+
void bpf_jit_fill_hole_with_zero(void *area, unsigned int size);
struct bpf_binary_header *
@@ -1055,7 +1065,7 @@ void bpf_jit_free(struct bpf_prog *fp);
struct bpf_binary_header *
bpf_jit_binary_pack_hdr(const struct bpf_prog *fp);
-void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns);
+void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic);
void bpf_prog_pack_free(struct bpf_binary_header *hdr);
static inline bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp)
@@ -1069,7 +1079,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **ro_image,
unsigned int alignment,
struct bpf_binary_header **rw_hdr,
u8 **rw_image,
- bpf_jit_fill_hole_t bpf_fill_ill_insns);
+ bpf_jit_fill_hole_t bpf_fill_ill_insns,
+ bool was_classic);
int bpf_jit_binary_pack_finalize(struct bpf_prog *prog,
struct bpf_binary_header *ro_header,
struct bpf_binary_header *rw_header);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5a897ee50f29..e6b2e2749e37 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2481,6 +2481,11 @@ static inline struct mnt_idmap *file_mnt_idmap(struct file *file)
return mnt_idmap(file->f_path.mnt);
}
+static inline bool file_owner_or_capable(struct file *file)
+{
+ return inode_owner_or_capable(file_mnt_idmap(file), file_inode(file));
+}
+
/**
* is_idmapped_mnt - check whether a mount is mapped
* @mnt: the mount to check
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 735f6f334a98..4b6bf73f2723 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -195,8 +195,10 @@ static inline void fwnode_init(struct fwnode_handle *fwnode,
{
fwnode->secondary = NULL;
fwnode->ops = ops;
+ fwnode->dev = NULL;
INIT_LIST_HEAD(&fwnode->consumers);
INIT_LIST_HEAD(&fwnode->suppliers);
+ fwnode->flags = 0;
}
static inline void fwnode_set_flag(struct fwnode_handle *fwnode,
diff --git a/include/linux/glob.h b/include/linux/glob.h
index 861327b33e41..91595e750936 100644
--- a/include/linux/glob.h
+++ b/include/linux/glob.h
@@ -6,5 +6,6 @@
#include <linux/compiler.h> /* For __pure */
bool __pure glob_match(char const *pat, char const *str);
+bool __pure glob_match_len(char const *pat, char const *str, size_t len);
#endif /* _LINUX_GLOB_H */
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
index c27329e2a5ad..f034d1dbbee9 100644
--- a/include/linux/hid-sensor-hub.h
+++ b/include/linux/hid-sensor-hub.h
@@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info {
* @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro.
* @raw_size: Response size for a read request.
* @raw_data: Place holder for received response.
+ * @index: Current write index into raw_data for multi-byte reads.
+ * @max_raw_size: Total buffer size for multi-byte reads; 0 for single-value reads.
*/
struct sensor_hub_pending {
bool status;
@@ -51,6 +53,8 @@ struct sensor_hub_pending {
u32 attr_usage_id;
int raw_size;
u8 *raw_data;
+ u32 index;
+ u32 max_raw_size;
};
/**
@@ -182,6 +186,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
bool is_signed
);
+/**
+ * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request
+ * @hsdev: Hub device instance.
+ * @usage_id: Attribute usage id of parent physical device as per spec
+ * @attr_usage_id: Attribute usage id as per spec
+ * @report_id: Report id to look for
+ * @flag: Synchronous or asynchronous read
+ * @buffer_size: Size of the buffer in bytes
+ * @buffer: Buffer to store the read data
+ *
+ * Issues a synchronous or asynchronous read request for an input attribute,
+ * accumulating data into the provided buffer until it is full.
+ * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a
+ * negative error code.
+ */
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id, u32 attr_usage_id,
+ u32 report_id,
+ enum sensor_hub_read_flags flag,
+ u32 buffer_size, u8 *buffer);
+
/**
* sensor_hub_set_feature() - Feature set request
* @hsdev: Hub device instance.
diff --git a/include/linux/hid.h b/include/linux/hid.h
index a9857fdfc327..f560ae891d7e 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -154,6 +154,7 @@ struct hid_item {
#define HID_UP_TELEPHONY 0x000b0000
#define HID_UP_CONSUMER 0x000c0000
#define HID_UP_DIGITIZER 0x000d0000
+#define HID_UP_HAPTIC 0x000e0000
#define HID_UP_PID 0x000f0000
#define HID_UP_BATTERY 0x00850000
#define HID_UP_CAMERA 0x00900000
@@ -314,6 +315,28 @@ struct hid_item {
#define HID_DG_TOOLSERIALNUMBER 0x000d005b
#define HID_DG_LATENCYMODE 0x000d0060
+#define HID_HP_SIMPLECONTROLLER 0x000e0001
+#define HID_HP_WAVEFORMLIST 0x000e0010
+#define HID_HP_DURATIONLIST 0x000e0011
+#define HID_HP_AUTOTRIGGER 0x000e0020
+#define HID_HP_MANUALTRIGGER 0x000e0021
+#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022
+#define HID_HP_INTENSITY 0x000e0023
+#define HID_HP_REPEATCOUNT 0x000e0024
+#define HID_HP_RETRIGGERPERIOD 0x000e0025
+#define HID_HP_WAVEFORMVENDORPAGE 0x000e0026
+#define HID_HP_WAVEFORMVENDORID 0x000e0027
+#define HID_HP_WAVEFORMCUTOFFTIME 0x000e0028
+#define HID_HP_WAVEFORMNONE 0x000e1001
+#define HID_HP_WAVEFORMSTOP 0x000e1002
+#define HID_HP_WAVEFORMCLICK 0x000e1003
+#define HID_HP_WAVEFORMBUZZCONTINUOUS 0x000e1004
+#define HID_HP_WAVEFORMRUMBLECONTINUOUS 0x000e1005
+#define HID_HP_WAVEFORMPRESS 0x000e1006
+#define HID_HP_WAVEFORMRELEASE 0x000e1007
+#define HID_HP_VENDORWAVEFORMMIN 0x000e2001
+#define HID_HP_VENDORWAVEFORMMAX 0x000e2fff
+
#define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065
#define HID_BAT_CHARGING 0x00850044
@@ -419,6 +442,12 @@ struct hid_item {
#define HID_REPORT_PROTOCOL 1
#define HID_BOOT_PROTOCOL 0
+/*
+ * HID units
+ */
+#define HID_UNIT_GRAM 0x0101
+#define HID_UNIT_NEWTON 0xe111
+
/*
* This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 9c8119ed13a4..4836a9f3a2b0 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -138,6 +138,12 @@ static inline struct host1x_bo_mapping *to_host1x_bo_mapping(struct kref *ref)
return container_of(ref, struct host1x_bo_mapping, ref);
}
+/**
+ * struct host1x_bo_ops - operations implemented by a host1x_bo provider
+ *
+ * @pin: create a DMA mapping. Implementation must not touch the bo's refcount.
+ * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount.
+ */
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
@@ -176,6 +182,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
enum dma_data_direction dir,
struct host1x_bo_cache *cache);
void host1x_bo_unpin(struct host1x_bo_mapping *map);
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo);
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
{
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 8a9792a6427a..47a0feffc121 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -37,7 +37,8 @@ static inline struct ethhdr *inner_eth_hdr(const struct sk_buff *skb)
return (struct ethhdr *)skb_inner_mac_header(skb);
}
-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
+int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr);
extern ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len);
diff --git a/include/linux/iio/common/inv_sensors_timestamp.h b/include/linux/iio/common/inv_sensors_timestamp.h
index a47d304d1ba7..8d506f1e9df2 100644
--- a/include/linux/iio/common/inv_sensors_timestamp.h
+++ b/include/linux/iio/common/inv_sensors_timestamp.h
@@ -71,8 +71,7 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
uint32_t period, bool fifo);
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
- uint32_t fifo_period, size_t fifo_nb,
- size_t sensor_nb, int64_t timestamp);
+ size_t sample_nb, int64_t timestamp);
static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
{
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h
index b157ff4f727f..566f3c2b3892 100644
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -282,6 +282,11 @@ static inline void in_dev_put(struct in_device *idev)
#define __in_dev_put(idev) refcount_dec(&(idev)->refcnt)
#define in_dev_hold(idev) refcount_inc(&(idev)->refcnt)
+static inline bool in_dev_hold_safe(struct in_device *idev)
+{
+ return refcount_inc_not_zero(&idev->refcnt);
+}
+
#endif /* __KERNEL__ */
static __inline__ __be32 inet_make_mask(int logmask)
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 03f38fe9b9a1..f236ea8f47bc 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -523,6 +523,19 @@ static __always_inline unsigned long msecs_to_jiffies(const unsigned int m)
}
}
+/**
+ * secs_to_jiffies: - convert seconds to jiffies
+ * @_secs: time in seconds
+ *
+ * Conversion is done by simple multiplication with HZ
+ *
+ * secs_to_jiffies() is defined as a macro rather than a static inline
+ * function so it can be used in static initializers.
+ *
+ * Return: jiffies value
+ */
+#define secs_to_jiffies(_secs) (unsigned long)((_secs) * HZ)
+
extern unsigned long __usecs_to_jiffies(const unsigned int u);
#if !(USEC_PER_SEC % HZ)
static inline unsigned long _usecs_to_jiffies(const unsigned int u)
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index 7c2d77d75a88..988e62cafd31 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -261,4 +261,29 @@ static inline int find_next_netdev_feature(u64 feature, unsigned long start)
NETIF_F_GSO_UDP_TUNNEL | \
NETIF_F_GSO_UDP_TUNNEL_CSUM)
+/* virtual device features */
+#define MASTER_UPPER_DEV_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
+ NETIF_F_GSO_ENCAP_ALL | \
+ NETIF_F_HIGHDMA | NETIF_F_LRO)
+
+#define MASTER_UPPER_DEV_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \
+ NETIF_F_GSO_PARTIAL)
+
+#define MASTER_UPPER_DEV_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_GSO_SOFTWARE)
+
+#define MASTER_UPPER_DEV_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \
+ NETIF_F_GSO_ESP)
+
+#define MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP)
+
+static inline netdev_features_t netdev_base_features(netdev_features_t features)
+{
+ features &= ~NETIF_F_ONE_FOR_ALL;
+ features |= NETIF_F_ALL_FOR_ALL;
+ return features;
+}
+
#endif /* _LINUX_NETDEV_FEATURES_H */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 202e557496fb..74474139fb9a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -312,7 +312,9 @@ struct header_ops {
int (*create) (struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len);
- int (*parse)(const struct sk_buff *skb, unsigned char *haddr);
+ int (*parse)(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr);
int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type);
void (*cache_update)(struct hh_cache *hh,
const struct net_device *dev,
@@ -3174,7 +3176,7 @@ static inline int dev_parse_header(const struct sk_buff *skb,
if (!dev->header_ops || !dev->header_ops->parse)
return 0;
- return dev->header_ops->parse(skb, haddr);
+ return dev->header_ops->parse(skb, dev, haddr);
}
static inline __be16 dev_parse_header_protocol(const struct sk_buff *skb)
@@ -5005,6 +5007,7 @@ static inline netdev_features_t netdev_add_tso_features(netdev_features_t featur
int __netdev_update_features(struct net_device *dev);
void netdev_update_features(struct net_device *dev);
void netdev_change_features(struct net_device *dev);
+void netdev_compute_master_upper_features(struct net_device *dev, bool update_header);
void netif_stacked_transfer_operstate(const struct net_device *rootdev,
struct net_device *dev);
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 6a46069c5a36..dbffbd86c7ba 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -33,6 +33,7 @@ enum {
PG_TEARDOWN, /* page group sync for destroy */
PG_UNLOCKPAGE, /* page group sync bit in read path */
PG_UPTODATE, /* page group sync bit in read path */
+ PG_READ_FAILED, /* page group saw a read error */
PG_WB_END, /* page group sync bit in write path */
PG_REMOVE, /* page group sync bit in write path */
PG_CONTENDED1, /* Is someone waiting for a lock? */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 052d956d3ba1..db4d82aae134 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1390,7 +1390,8 @@ static inline int pci_rebar_bytes_to_size(u64 bytes)
}
u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
-int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
+int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size,
+ int exclude_bars);
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
bool pci_device_is_present(struct pci_dev *pdev);
void pci_ignore_hotplug(struct pci_dev *dev);
@@ -1460,7 +1461,6 @@ void pci_assign_unassigned_resources(void);
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge);
void pci_assign_unassigned_bus_resources(struct pci_bus *bus);
void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus);
-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type);
int pci_enable_resources(struct pci_dev *, int mask);
void pci_assign_irq(struct pci_dev *dev);
struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res);
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
index 90d8e4475f80..328192936706 100644
--- a/include/linux/rpmsg.h
+++ b/include/linux/rpmsg.h
@@ -41,9 +41,6 @@ struct rpmsg_channel_info {
* rpmsg_device - device that belong to the rpmsg bus
* @dev: the device struct
* @id: device id (used to match between rpmsg drivers and devices)
- * @driver_override: driver name to force a match; do not set directly,
- * because core frees it; use driver_set_override() to
- * set or clear it.
* @src: local address
* @dst: destination address
* @ept: the rpmsg endpoint of this channel
@@ -53,7 +50,6 @@ struct rpmsg_channel_info {
struct rpmsg_device {
struct device dev;
struct rpmsg_device_id id;
- const char *driver_override;
u32 src;
u32 dst;
struct rpmsg_endpoint *ept;
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index b4b4ce9a4151..995f29dd5455 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -1266,4 +1266,114 @@ done_seqretry_irqrestore(seqlock_t *lock, int seq, unsigned long flags)
if (seq & 1)
read_sequnlock_excl_irqrestore(lock, flags);
}
+
+enum ss_state {
+ ss_done = 0,
+ ss_lock,
+ ss_lock_irqsave,
+ ss_lockless,
+};
+
+struct ss_tmp {
+ enum ss_state state;
+ unsigned long data;
+ spinlock_t *lock;
+ spinlock_t *lock_irqsave;
+};
+
+static inline void __scoped_seqlock_cleanup(struct ss_tmp *sst)
+{
+ if (sst->lock)
+ spin_unlock(sst->lock);
+ if (sst->lock_irqsave)
+ spin_unlock_irqrestore(sst->lock_irqsave, sst->data);
+}
+
+extern void __scoped_seqlock_invalid_target(void);
+
+#if defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000
+/*
+ * For some reason some GCC-8 architectures (nios2, alpha) have trouble
+ * determining that the ss_done state is impossible in __scoped_seqlock_next()
+ * below.
+ */
+static inline void __scoped_seqlock_bug(void) { }
+#else
+/*
+ * Canary for compiler optimization -- if the compiler doesn't realize this is
+ * an impossible state, it very likely generates sub-optimal code here.
+ */
+extern void __scoped_seqlock_bug(void);
+#endif
+
+static inline void
+__scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target)
+{
+ switch (sst->state) {
+ case ss_done:
+ __scoped_seqlock_bug();
+ return;
+
+ case ss_lock:
+ case ss_lock_irqsave:
+ sst->state = ss_done;
+ return;
+
+ case ss_lockless:
+ if (!read_seqretry(lock, sst->data)) {
+ sst->state = ss_done;
+ return;
+ }
+ break;
+ }
+
+ switch (target) {
+ case ss_done:
+ __scoped_seqlock_invalid_target();
+ return;
+
+ case ss_lock:
+ sst->lock = &lock->lock;
+ spin_lock(sst->lock);
+ sst->state = ss_lock;
+ return;
+
+ case ss_lock_irqsave:
+ sst->lock_irqsave = &lock->lock;
+ spin_lock_irqsave(sst->lock_irqsave, sst->data);
+ sst->state = ss_lock_irqsave;
+ return;
+
+ case ss_lockless:
+ sst->data = read_seqbegin(lock);
+ return;
+ }
+}
+
+#define __scoped_seqlock_read(_seqlock, _target, _s) \
+ for (struct ss_tmp _s __cleanup(__scoped_seqlock_cleanup) = \
+ { .state = ss_lockless, .data = read_seqbegin(_seqlock) }; \
+ _s.state != ss_done; \
+ __scoped_seqlock_next(&_s, _seqlock, _target))
+
+/**
+ * scoped_seqlock_read() - execute the read-side critical section
+ * without manual sequence counter handling
+ * or calls to other helpers
+ * @_seqlock: pointer to seqlock_t protecting the data
+ * @_target: an enum ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless}
+ * indicating the type of critical read section
+ *
+ * Example::
+ *
+ * scoped_seqlock_read (&lock, ss_lock) {
+ * // read-side critical section
+ * }
+ *
+ * Starts with a lockess pass first. If it fails, restarts the critical
+ * section with the lock held.
+ */
+#define scoped_seqlock_read(_seqlock, _target) \
+ __scoped_seqlock_read(_seqlock, _target, __UNIQUE_ID(seqlock))
+
#endif /* __LINUX_SEQLOCK_H */
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 17d84b3ee8a0..3c1fedb108bf 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -186,6 +186,7 @@ int rpc_switch_client_transport(struct rpc_clnt *,
const struct rpc_timeout *);
void rpc_shutdown_client(struct rpc_clnt *);
+void rpc_hold_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *);
void rpc_task_release_transport(struct rpc_task *);
void rpc_task_release_client(struct rpc_task *);
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 4ecc89301eb7..840f77ff83bf 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -398,6 +398,8 @@ struct rpc_xprt * xprt_alloc(struct net *net, size_t size,
unsigned int max_req);
void xprt_free(struct rpc_xprt *);
void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task);
+void xprt_add_backlog_noncongested(struct rpc_xprt *xprt,
+ struct rpc_task *task);
bool xprt_wake_up_backlog(struct rpc_xprt *xprt, struct rpc_rqst *req);
void xprt_cleanup_ids(void);
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 4f283514a1ed..d123f28f66a7 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -54,6 +54,9 @@ struct vfio_pci_core_device {
struct pci_dev *pdev;
void __iomem *barmap[PCI_STD_NUM_BARS];
bool bar_mmap_supported[PCI_STD_NUM_BARS];
+ /* Flags modified at runtime - dedicated storage unit */
+ bool virq_disabled;
+ bool bardirty;
u8 *pci_config_map;
u8 *vconfig;
struct perm_bits *msi_perm;
@@ -68,18 +71,20 @@ struct vfio_pci_core_device {
u16 msix_size;
u32 msix_offset;
u32 rbar[7];
+ /* Flags only modified on setup/release - bitfield ok */
bool has_dyn_msix:1;
bool pci_2_3:1;
- bool virq_disabled:1;
bool reset_works:1;
bool extended_caps:1;
- bool bardirty:1;
bool has_vga:1;
- bool needs_reset:1;
bool nointx:1;
bool needs_pm_restore:1;
- bool pm_intx_masked:1;
- bool pm_runtime_engaged:1;
+ bool disable_idle_d3:1;
+ /* Flags modified at runtime - dedicated storage unit */
+ bool needs_reset;
+ bool pm_intx_masked;
+ bool pm_runtime_engaged;
+ bool sriov_active;
struct pci_saved_state *pci_saved_state;
struct pci_saved_state *pm_save;
int ioeventfds_nr;
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index 897f2109f6ad..5a6ef0ae032a 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -209,7 +209,9 @@ static inline bool is_vm_area_hugepages(const void *addr)
* allocated in the vmalloc layer.
*/
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
- return find_vm_area(addr)->page_order > 0;
+ struct vm_struct *area = find_vm_area(addr);
+
+ return area && area->page_order > 0;
#else
return false;
#endif
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 083387c00f0c..a7a061c7a770 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -283,6 +283,8 @@ static inline void wbc_init_bio(struct writeback_control *wbc, struct bio *bio)
bio_associate_blkg_from_css(bio, wbc->wb->blkcg_css);
}
+void inode_switch_wbs_work_fn(struct work_struct *work);
+
#else /* CONFIG_CGROUP_WRITEBACK */
static inline void inode_attach_wb(struct inode *inode, struct folio *folio)
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index facb7a469efa..0a9b055a8ee1 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -334,8 +334,11 @@ static inline struct inet6_dev *__in6_dev_get(const struct net_device *dev)
static inline struct inet6_dev *__in6_dev_stats_get(const struct net_device *dev,
const struct sk_buff *skb)
{
- if (netif_is_l3_master(dev))
+ if (netif_is_l3_master(dev)) {
dev = dev_get_by_index_rcu(dev_net(dev), inet6_iif(skb));
+ if (!dev)
+ return NULL;
+ }
return __in6_dev_get(dev);
}
@@ -408,6 +411,11 @@ static inline void in6_dev_hold(struct inet6_dev *idev)
refcount_inc(&idev->refcnt);
}
+static inline bool in6_dev_hold_safe(struct inet6_dev *idev)
+{
+ return refcount_inc_not_zero(&idev->refcnt);
+}
+
/* called with rcu_read_lock held */
static inline bool ip6_ignore_linkdown(const struct net_device *dev)
{
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index b25746b91986..0011d122458b 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -206,6 +206,13 @@ enum {
*/
HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
+ /* When this quirk is set consider Sync Flow Control as supported by
+ * the driver.
+ *
+ * This quirk must be set before hci_register_dev is called.
+ */
+ HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED,
+
/* When this quirk is set, the controller has validated that
* LE states reported through the HCI_LE_READ_SUPPORTED_STATES are
* valid. This mechanism is necessary as many controllers have
@@ -416,6 +423,7 @@ enum {
HCI_WIDEBAND_SPEECH_ENABLED,
HCI_EVENT_FILTER_CONFIGURED,
HCI_PA_SYNC,
+ HCI_SCO_FLOWCTL,
HCI_DUT_MODE,
HCI_VENDOR_DIAG,
@@ -519,7 +527,8 @@ enum {
#define ESCO_LINK 0x02
/* Low Energy links do not have defined link type. Use invented one */
#define LE_LINK 0x80
-#define ISO_LINK 0x82
+#define CIS_LINK 0x82
+#define BIS_LINK 0x83
#define INVALID_LINK 0xff
/* LMP features */
@@ -1513,6 +1522,11 @@ struct hci_rp_read_tx_power {
__s8 tx_power;
} __packed;
+#define HCI_OP_WRITE_SYNC_FLOWCTL 0x0c2f
+struct hci_cp_write_sync_flowctl {
+ __u8 enable;
+} __packed;
+
#define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46
struct hci_rp_read_page_scan_type {
__u8 status;
@@ -2914,8 +2928,9 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
#define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))
/* ISO data length and flags pack/unpack */
-#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14)))
-#define hci_iso_data_len(h) ((h) & 0x3fff)
+#define hci_iso_data_len_pack(h, f) ((__u16) (((h) & 0x0fff) | \
+ (((f) & 0x3) << 14)))
+#define hci_iso_data_len(h) ((h) & 0x0fff)
#define hci_iso_data_flags(h) ((h) >> 14)
/* codec transport types */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a2a6fb20f496..bc18037e25ac 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -957,6 +957,7 @@ enum {
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE,
HCI_CONN_CREATE_CIS,
HCI_CONN_BIG_SYNC,
HCI_CONN_BIG_SYNC_FAILED,
@@ -995,7 +996,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num++;
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
h->iso_num++;
break;
}
@@ -1021,7 +1023,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
case ESCO_LINK:
h->sco_num--;
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
h->iso_num--;
break;
}
@@ -1038,7 +1041,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
case SCO_LINK:
case ESCO_LINK:
return h->sco_num;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
return h->iso_num;
default:
return 0;
@@ -1099,7 +1103,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
+ if (bacmp(&c->dst, ba) || c->type != BIS_LINK)
continue;
if (c->iso_qos.bcast.bis == bis) {
@@ -1123,8 +1127,8 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (bacmp(&c->dst, ba) || c->type != ISO_LINK ||
- !test_bit(HCI_CONN_PER_ADV, &c->flags))
+ if (bacmp(&c->dst, ba) || c->type != BIS_LINK ||
+ !test_bit(HCI_CONN_PER_ADV, &c->flags))
continue;
if (c->iso_qos.bcast.big == big &&
@@ -1234,7 +1238,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
+ if (c->type != CIS_LINK)
continue;
/* Match CIG ID if set */
@@ -1266,7 +1270,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != ISO_LINK || !bacmp(&c->dst, BDADDR_ANY))
+ if (c->type != CIS_LINK)
continue;
if (handle == c->iso_qos.ucast.cig) {
@@ -1289,7 +1293,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK)
+ if (bacmp(&c->dst, BDADDR_ANY) || c->type != BIS_LINK)
continue;
if (handle == c->iso_qos.bcast.big) {
@@ -1312,7 +1316,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != ISO_LINK)
+ if (c->type != BIS_LINK)
continue;
if (handle != BT_ISO_QOS_BIG_UNSET && handle == c->iso_qos.bcast.big) {
@@ -1335,8 +1339,7 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK ||
- c->state != state)
+ if (c->type != BIS_LINK || c->state != state)
continue;
if (handle == c->iso_qos.bcast.big) {
@@ -1359,8 +1362,8 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != ISO_LINK ||
- !test_bit(HCI_CONN_PA_SYNC, &c->flags))
+ if (c->type != BIS_LINK ||
+ !test_bit(HCI_CONN_PA_SYNC, &c->flags))
continue;
if (c->iso_qos.bcast.big == big) {
@@ -1382,8 +1385,8 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != ISO_LINK ||
- !test_bit(HCI_CONN_PA_SYNC, &c->flags))
+ if (c->type != BIS_LINK ||
+ !test_bit(HCI_CONN_PA_SYNC, &c->flags))
continue;
if (c->sync_handle == sync_handle) {
@@ -1824,6 +1827,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_hold_capable(dev) ((dev)->features[0][0] & LMP_HOLD)
#define lmp_sniff_capable(dev) ((dev)->features[0][0] & LMP_SNIFF)
#define lmp_park_capable(dev) ((dev)->features[0][1] & LMP_PARK)
+#define lmp_sco_capable(dev) ((dev)->features[0][1] & LMP_SCO)
#define lmp_inq_rssi_capable(dev) ((dev)->features[0][3] & LMP_RSSI_INQ)
#define lmp_esco_capable(dev) ((dev)->features[0][3] & LMP_ESCO)
#define lmp_bredr_capable(dev) (!((dev)->features[0][4] & LMP_NO_BREDR))
@@ -1958,7 +1962,8 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
case ESCO_LINK:
return sco_connect_ind(hdev, bdaddr, flags);
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
return iso_connect_ind(hdev, bdaddr, flags);
default:
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index bf23edb02838..136036b48e6d 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -614,7 +614,8 @@ struct l2cap_chan {
struct l2cap_ops {
char *name;
- struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan);
+ int (*new_connection)(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan);
int (*recv) (struct l2cap_chan * chan,
struct sk_buff *skb);
void (*teardown) (struct l2cap_chan *chan, int err);
@@ -742,6 +743,7 @@ enum {
FLAG_ECRED_CONN_REQ_SENT,
FLAG_PENDING_SECURITY,
FLAG_HOLD_HCI_CONN,
+ FLAG_DEL,
};
/* Lock nesting levels for L2CAP channels. We need these because lockdep
@@ -878,9 +880,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
return (seq + 1) % (chan->tx_win_max + 1);
}
-static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
+static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
- return NULL;
+ return -EOPNOTSUPP;
}
static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -956,7 +959,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
-void l2cap_chan_set_defaults(struct l2cap_chan *chan);
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
int l2cap_ertm_init(struct l2cap_chan *chan);
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 0d9c1eb40d12..8f5507ef5c0d 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -713,6 +713,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay);
void bond_work_init_all(struct bonding *bond);
+void bond_work_cancel_all(struct bonding *bond);
#ifdef CONFIG_PROC_FS
void bond_create_proc_entry(struct bonding *bond);
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 82da359bca03..c47f6f6d3cad 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -107,6 +107,11 @@ static inline void fib_rule_get(struct fib_rule *rule)
refcount_inc(&rule->refcnt);
}
+static inline bool fib_rule_get_safe(struct fib_rule *rule)
+{
+ return refcount_inc_not_zero(&rule->refcnt);
+}
+
static inline void fib_rule_put(struct fib_rule *rule)
{
if (refcount_dec_and_test(&rule->refcnt))
diff --git a/include/net/gue.h b/include/net/gue.h
index dfca298bec9c..caefd6da8693 100644
--- a/include/net/gue.h
+++ b/include/net/gue.h
@@ -80,7 +80,7 @@ static inline size_t guehdr_flags_len(__be16 flags)
static inline size_t guehdr_priv_flags_len(__be32 flags)
{
- return 0;
+ return (flags & GUE_PFLAG_REMCSUM) ? GUE_PLEN_REMCSUM : 0;
}
/* Validate standard and private flags. Returns non-zero (meaning invalid)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index ca1700c2a573..b6fc9924ef7a 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -370,7 +370,7 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp,
struct fib_result *res, unsigned int flags)
{
struct fib_table *tb;
- int err = -ENETUNREACH;
+ int err = -EAGAIN;
flags |= FIB_LOOKUP_NOREF;
if (net->ipv4.fib_has_custom_rules)
@@ -384,17 +384,16 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp,
if (tb)
err = fib_table_lookup(tb, flp, res, flags);
- if (!err)
+ if (err != -EAGAIN)
goto out;
tb = rcu_dereference_rtnl(net->ipv4.fib_default);
if (tb)
err = fib_table_lookup(tb, flp, res, flags);
-out:
if (err == -EAGAIN)
err = -ENETUNREACH;
-
+out:
rcu_read_unlock();
return err;
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index d70268cf1af8..08b90d33acdc 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -516,7 +516,8 @@ struct ip_vs_protocol {
void (*state_transition)(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd);
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len);
int (*register_app)(struct netns_ipvs *ipvs, struct ip_vs_app *inc);
diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h
index 0b247248b032..fd5c4dbf72ca 100644
--- a/include/net/netfilter/nf_conntrack_extend.h
+++ b/include/net/netfilter/nf_conntrack_extend.h
@@ -38,7 +38,6 @@ enum nf_ct_ext_id {
struct nf_ct_ext {
u8 offset[NF_CT_EXT_NUM];
u8 len;
- unsigned int gen_id;
char data[] __aligned(8);
};
@@ -52,8 +51,6 @@ static inline bool nf_ct_ext_exist(const struct nf_conn *ct, u8 id)
return (ct->ext && __nf_ct_ext_exist(ct->ext, id));
}
-void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id);
-
static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id)
{
struct nf_ct_ext *ext = ct->ext;
@@ -61,19 +58,10 @@ static inline void *nf_ct_ext_find(const struct nf_conn *ct, u8 id)
if (!ext || !__nf_ct_ext_exist(ext, id))
return NULL;
- if (unlikely(ext->gen_id))
- return __nf_ct_ext_find(ext, id);
-
return (void *)ct->ext + ct->ext->offset[id];
}
/* Add this type, returns pointer to data or NULL. */
void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp);
-/* ext genid. if ext->id != ext_genid, extensions cannot be used
- * anymore unless conntrack has CONFIRMED bit set.
- */
-extern atomic_t nf_conntrack_ext_genid;
-void nf_ct_ext_bump_genid(void);
-
#endif /* _NF_CONNTRACK_EXTEND_H */
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index 2bd5c68e6943..c576479b9aa5 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -16,6 +16,7 @@ struct nf_queue_entry {
unsigned int id;
unsigned int hook_index; /* index in hook_entries->hook[] */
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+ struct net_device *bridge_dev;
struct net_device *physin;
struct net_device *physout;
#endif
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h
index c1fa6fee0acf..17a91f7b69ea 100644
--- a/include/net/rtnetlink.h
+++ b/include/net/rtnetlink.h
@@ -211,6 +211,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer,
struct netlink_ext_ack *exterr);
struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid);
+bool rtnl_dev_link_net_capable(const struct net_device *dev,
+ const struct net *link_net);
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index a2310fa995f6..54d365275764 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -112,7 +112,8 @@ int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net,
const union sctp_addr *paddr, void *p, int dif);
int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done,
struct net *net, int *pos, void *p);
-int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p);
+int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
+ struct net *net, int *pos, void *p);
int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
struct sctp_info *info);
diff --git a/include/net/sock.h b/include/net/sock.h
index a6944844553a..c1f129a532b2 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2155,7 +2155,7 @@ static inline int sk_rx_queue_get(const struct sock *sk)
static inline void sk_set_socket(struct sock *sk, struct socket *sock)
{
- sk->sk_socket = sock;
+ WRITE_ONCE(sk->sk_socket, sock);
}
static inline wait_queue_head_t *sk_sleep(struct sock *sk)
diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
index a26d4cd3b8d6..fa4cd4fc3674 100644
--- a/include/net/tc_act/tc_pedit.h
+++ b/include/net/tc_act/tc_pedit.h
@@ -36,17 +36,15 @@ static inline bool is_tcf_pedit(const struct tc_action *a)
return false;
}
-static inline int tcf_pedit_nkeys(const struct tc_action *a)
+/* Must be called with act->tcfa_lock held to ensure consistency of parallel
+ * reads of the same action's pedit keys (e.g. flow_offload count vs fill).
+ * Note, this is only used for pedit offload.
+ */
+static inline int tcf_pedit_nkeys_locked(const struct tc_action *a)
{
- struct tcf_pedit_parms *parms;
- int nkeys;
-
- rcu_read_lock();
- parms = to_pedit_parms(a);
- nkeys = parms->tcfp_nkeys;
- rcu_read_unlock();
-
- return nkeys;
+ lockdep_assert_held(&a->tcfa_lock);
+ return rcu_dereference_protected(to_pedit(a)->parms,
+ lockdep_is_held(&a->tcfa_lock))->tcfp_nkeys;
}
static inline u32 tcf_pedit_htype(const struct tc_action *a, int index)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index c4aab4701711..3d63e52254ed 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -836,6 +836,9 @@ static inline bool addr_match(const void *token1, const void *token2,
unsigned int pdw;
unsigned int pbi;
+ if (prefixlen > 128)
+ return false;
+
pdw = prefixlen >> 5; /* num of whole u32 in prefix */
pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
@@ -860,6 +863,10 @@ static inline bool addr4_match(__be32 a1, __be32 a2, u8 prefixlen)
/* C99 6.5.7 (3): u32 << 32 is undefined behaviour */
if (sizeof(long) == 4 && prefixlen == 0)
return true;
+
+ if (prefixlen > 32)
+ return false;
+
return !((a1 ^ a2) & htonl(~0UL << (32 - prefixlen)));
}
@@ -1143,8 +1150,8 @@ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb,
static inline bool __xfrm_check_nopolicy(struct net *net, struct sk_buff *skb,
int dir)
{
- if (!net->xfrm.policy_count[dir] && !secpath_exists(skb))
- return net->xfrm.policy_default[dir] == XFRM_USERPOLICY_ACCEPT;
+ if (!READ_ONCE(net->xfrm.policy_count[dir]) && !secpath_exists(skb))
+ return READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_ACCEPT;
return false;
}
@@ -1244,8 +1251,8 @@ static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
struct net *net = dev_net(skb->dev);
- if (!net->xfrm.policy_count[XFRM_POLICY_OUT] &&
- net->xfrm.policy_default[XFRM_POLICY_OUT] == XFRM_USERPOLICY_ACCEPT)
+ if (!READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]) &&
+ READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]) == XFRM_USERPOLICY_ACCEPT)
return true;
return (skb_dst(skb)->flags & DST_NOXFRM) ||
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6950169329ef..a1e17df7c49a 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -6460,6 +6460,7 @@ struct bpf_prog_info {
__u32 verified_insns;
__u32 attach_btf_obj_id;
__u32 attach_btf_id;
+ __u32 :32;
} __attribute__((aligned(8)));
struct bpf_map_info {
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index d8123dc5d742..37725c5031b4 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -556,7 +556,6 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
struct io_wq *wq = worker->wq;
do {
- bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
struct io_wq_work *work;
/*
@@ -592,6 +591,7 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
/* handle a whole dependent link */
do {
+ bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state);
struct io_wq_work *next_hashed, *linked;
unsigned int hash = io_get_work_hash(work);
diff --git a/kernel/audit.c b/kernel/audit.c
index 433672f81e30..20b71a5b4760 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -57,6 +57,9 @@
#include <linux/freezer.h>
#include <linux/pid_namespace.h>
#include <net/netns/generic.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/overflow.h>
#include "audit.h"
@@ -909,7 +912,7 @@ static int kauditd_thread(void *dummy)
* do the multicast send and rotate records from the
* main queue to the retry/hold queues */
wait_event_freezable(kauditd_wait,
- (skb_queue_len(&audit_queue) ? 1 : 0));
+ (skb_queue_len_lockless(&audit_queue) ? 1 : 0));
}
return 0;
@@ -1244,7 +1247,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
s.rate_limit = audit_rate_limit;
s.backlog_limit = audit_backlog_limit;
s.lost = atomic_read(&audit_lost);
- s.backlog = skb_queue_len(&audit_queue);
+ s.backlog = skb_queue_len_lockless(&audit_queue);
s.feature_bitmap = AUDIT_FEATURE_BITMAP_ALL;
s.backlog_wait_time = audit_backlog_wait_time;
s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual);
@@ -1585,7 +1588,7 @@ static void audit_receive(struct sk_buff *skb)
/* can't block with the ctrl lock, so penalize the sender now */
if (audit_backlog_limit &&
- (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+ (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
DECLARE_WAITQUEUE(wait, current);
/* wake kauditd to try and flush the queue */
@@ -1887,7 +1890,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
long stime = audit_backlog_wait_time;
while (audit_backlog_limit &&
- (skb_queue_len(&audit_queue) > audit_backlog_limit)) {
+ (skb_queue_len_lockless(&audit_queue) > audit_backlog_limit)) {
/* wake kauditd to try and flush the queue */
wake_up_interruptible(&kauditd_wait);
@@ -1907,7 +1910,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
} else {
if (audit_rate_check() && printk_ratelimit())
pr_warn("audit_backlog=%d > audit_backlog_limit=%d\n",
- skb_queue_len(&audit_queue),
+ skb_queue_len_lockless(&audit_queue),
audit_backlog_limit);
audit_log_lost("backlog limit exceeded");
return NULL;
@@ -2032,7 +2035,8 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
size_t len)
{
- int i, avail, new_len;
+ int avail;
+ size_t i, new_len;
unsigned char *ptr;
struct sk_buff *skb;
@@ -2042,7 +2046,12 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
BUG_ON(!ab->skb);
skb = ab->skb;
avail = skb_tailroom(skb);
- new_len = len<<1;
+
+ if (check_shl_overflow(len, 1, &new_len)) {
+ audit_log_format(ab, "?");
+ return;
+ }
+
if (new_len >= avail) {
/* Round the buffer request up to the next multiple */
new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
@@ -2304,6 +2313,68 @@ void audit_log_path_denied(int type, const char *operation)
audit_log_end(ab);
}
+int audit_log_nf_skb(struct audit_buffer *ab,
+ const struct sk_buff *skb, u8 nfproto)
+{
+ /* find the IP protocol in the case of NFPROTO_BRIDGE */
+ if (nfproto == NFPROTO_BRIDGE) {
+ switch (eth_hdr(skb)->h_proto) {
+ case htons(ETH_P_IP):
+ nfproto = NFPROTO_IPV4;
+ break;
+ case htons(ETH_P_IPV6):
+ nfproto = NFPROTO_IPV6;
+ break;
+ default:
+ goto unknown_proto;
+ }
+ }
+
+ switch (nfproto) {
+ case NFPROTO_IPV4: {
+ struct iphdr iph;
+ const struct iphdr *ih;
+
+ ih = skb_header_pointer(skb, skb_network_offset(skb),
+ sizeof(iph), &iph);
+ if (!ih)
+ return -ENOMEM;
+
+ audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
+ &ih->saddr, &ih->daddr, ih->protocol);
+ break;
+ }
+ case NFPROTO_IPV6: {
+ struct ipv6hdr iph;
+ const struct ipv6hdr *ih;
+ u8 nexthdr;
+ __be16 frag_off;
+
+ ih = skb_header_pointer(skb, skb_network_offset(skb),
+ sizeof(iph), &iph);
+ if (!ih)
+ return -ENOMEM;
+
+ nexthdr = ih->nexthdr;
+ ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(iph),
+ &nexthdr, &frag_off);
+
+ audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
+ &ih->saddr, &ih->daddr, nexthdr);
+ break;
+ }
+ default:
+ goto unknown_proto;
+ }
+
+ return 0;
+
+unknown_proto:
+ audit_log_format(ab, " saddr=? daddr=? proto=?");
+ return -EPFNOSUPPORT;
+}
+EXPORT_SYMBOL(audit_log_nf_skb);
+
/* global counter which is incremented every time something logs in */
static atomic_t session_id = ATOMIC_INIT(0);
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 92486a7b916e..147c3a8ad4e8 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -36,6 +36,27 @@ BTF_SET_START(bpf_lsm_hooks)
#undef LSM_HOOK
BTF_SET_END(bpf_lsm_hooks)
+BTF_SET_START(bpf_lsm_disabled_hooks)
+BTF_ID(func, bpf_lsm_vm_enough_memory)
+BTF_ID(func, bpf_lsm_inode_need_killpriv)
+BTF_ID(func, bpf_lsm_inode_getsecurity)
+BTF_ID(func, bpf_lsm_inode_listsecurity)
+BTF_ID(func, bpf_lsm_inode_copy_up_xattr)
+BTF_ID(func, bpf_lsm_getselfattr)
+BTF_ID(func, bpf_lsm_getprocattr)
+BTF_ID(func, bpf_lsm_setprocattr)
+#ifdef CONFIG_KEYS
+BTF_ID(func, bpf_lsm_key_getsecurity)
+#endif
+#ifdef CONFIG_AUDIT
+BTF_ID(func, bpf_lsm_audit_rule_match)
+#endif
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+BTF_ID(func, bpf_lsm_xfrm_decode_session)
+#endif
+BTF_ID(func, bpf_lsm_ismaclabel)
+BTF_SET_END(bpf_lsm_disabled_hooks)
+
/* List of LSM hooks that should operate on 'current' cgroup regardless
* of function signature.
*/
@@ -97,15 +118,24 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
{
+ u32 btf_id = prog->aux->attach_btf_id;
+ const char *func_name = prog->aux->attach_func_name;
+
if (!prog->gpl_compatible) {
bpf_log(vlog,
"LSM programs must have a GPL compatible license\n");
return -EINVAL;
}
- if (!btf_id_set_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) {
+ if (btf_id_set_contains(&bpf_lsm_disabled_hooks, btf_id)) {
+ bpf_log(vlog, "attach_btf_id %u points to disabled hook %s\n",
+ btf_id, func_name);
+ return -EINVAL;
+ }
+
+ if (!btf_id_set_contains(&bpf_lsm_hooks, btf_id)) {
bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
- prog->aux->attach_btf_id, prog->aux->attach_func_name);
+ btf_id, func_name);
return -EINVAL;
}
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 3aadbf81d8b1..b75b63e67506 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -769,19 +769,65 @@ static int cgroup_bpf_attach(struct cgroup *cgrp,
return ret;
}
+static int effective_prog_pos(struct cgroup *cgrp,
+ enum cgroup_bpf_attach_type atype,
+ struct bpf_prog_list *target_pl)
+{
+ int cnt = 0, preorder_cnt = 0, fstart, bstart, init_bstart, pos = -1;
+ struct bpf_prog_list *pl;
+ struct cgroup *p = cgrp;
+
+ /* count effective programs to find where the preorder region ends */
+ do {
+ if (cnt == 0 || (p->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
+ cnt += prog_list_length(&p->bpf.progs[atype], &preorder_cnt);
+ p = cgroup_parent(p);
+ } while (p);
+
+ /* replay compute_effective_progs() placement and record target's slot */
+ cnt = 0;
+ p = cgrp;
+ fstart = preorder_cnt;
+ bstart = preorder_cnt - 1;
+ do {
+ if (cnt > 0 && !(p->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
+ continue;
+
+ init_bstart = bstart;
+ hlist_for_each_entry(pl, &p->bpf.progs[atype], node) {
+ if (!prog_list_prog(pl))
+ continue;
+
+ if (pl->flags & BPF_F_PREORDER) {
+ if (pl == target_pl)
+ pos = bstart;
+ bstart--;
+ } else {
+ if (pl == target_pl)
+ pos = fstart;
+ fstart++;
+ }
+ cnt++;
+ }
+
+ /* reverse pre-ordering progs at this cgroup level */
+ if (pos >= bstart + 1 && pos <= init_bstart)
+ pos = bstart + 1 + init_bstart - pos;
+ } while ((p = cgroup_parent(p)));
+
+ return pos;
+}
+
/* Swap updated BPF program for given link in effective program arrays across
* all descendant cgroups. This function is guaranteed to succeed.
*/
static void replace_effective_prog(struct cgroup *cgrp,
enum cgroup_bpf_attach_type atype,
- struct bpf_cgroup_link *link)
+ struct bpf_prog_list *pl)
{
struct bpf_prog_array_item *item;
struct cgroup_subsys_state *css;
struct bpf_prog_array *progs;
- struct bpf_prog_list *pl;
- struct hlist_head *head;
- struct cgroup *cg;
int pos;
css_for_each_descendant_pre(css, &cgrp->self) {
@@ -790,27 +836,15 @@ static void replace_effective_prog(struct cgroup *cgrp,
if (percpu_ref_is_zero(&desc->bpf.refcnt))
continue;
- /* find position of link in effective progs array */
- for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
- if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
- continue;
+ pos = effective_prog_pos(desc, atype, pl);
+ if (WARN_ON_ONCE(pos < 0))
+ continue;
- head = &cg->bpf.progs[atype];
- hlist_for_each_entry(pl, head, node) {
- if (!prog_list_prog(pl))
- continue;
- if (pl->link == link)
- goto found;
- pos++;
- }
- }
-found:
- BUG_ON(!cg);
progs = rcu_dereference_protected(
desc->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
item = &progs->items[pos];
- WRITE_ONCE(item->prog, link->link.prog);
+ WRITE_ONCE(item->prog, pl->link->link.prog);
}
}
@@ -853,7 +887,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp,
return -ENOENT;
old_prog = xchg(&link->link.prog, new_prog);
- replace_effective_prog(cgrp, atype, link);
+ replace_effective_prog(cgrp, atype, pl);
bpf_prog_put(old_prog);
return 0;
}
@@ -920,19 +954,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs,
* recomputing the array in place.
*
* @cgrp: The cgroup which descendants to travers
- * @prog: A program to detach or NULL
- * @link: A link to detach or NULL
+ * @pl: The prog_list entry being detached
* @atype: Type of detach operation
*/
-static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
- struct bpf_cgroup_link *link,
+static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl,
enum cgroup_bpf_attach_type atype)
{
struct cgroup_subsys_state *css;
struct bpf_prog_array *progs;
- struct bpf_prog_list *pl;
- struct hlist_head *head;
- struct cgroup *cg;
int pos;
/* recompute effective prog array in place */
@@ -942,24 +971,11 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
if (percpu_ref_is_zero(&desc->bpf.refcnt))
continue;
- /* find position of link or prog in effective progs array */
- for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
- if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
- continue;
-
- head = &cg->bpf.progs[atype];
- hlist_for_each_entry(pl, head, node) {
- if (!prog_list_prog(pl))
- continue;
- if (pl->prog == prog && pl->link == link)
- goto found;
- pos++;
- }
- }
-
+ pos = effective_prog_pos(desc, atype, pl);
/* no link or prog match, skip the cgroup of this layer */
- continue;
-found:
+ if (pos < 0)
+ continue;
+
progs = rcu_dereference_protected(
desc->bpf.effective[atype],
lockdep_is_held(&cgroup_mutex));
@@ -1020,7 +1036,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
/* if update effective array failed replace the prog with a dummy prog*/
pl->prog = old_prog;
pl->link = link;
- purge_effective_progs(cgrp, old_prog, link, atype);
+ purge_effective_progs(cgrp, pl, atype);
}
/* now can actually delete it from this cgroup list */
@@ -1775,7 +1791,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
kfree(ctx.cur_val);
- if (ret == 1 && ctx.new_updated) {
+ if (!ret && ctx.new_updated) {
kvfree(*buf);
*buf = ctx.new_val;
*pcount = ctx.new_len;
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index d708cf3e6207..9fa984ee076b 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -37,6 +37,7 @@
#include <linux/nospec.h>
#include <linux/bpf_mem_alloc.h>
#include <linux/memcontrol.h>
+#include <linux/static_call.h>
#include <asm/barrier.h>
#include <asm/unaligned.h>
@@ -850,6 +851,7 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog,
struct bpf_prog_pack {
struct list_head list;
void *ptr;
+ bool arch_flush_needed;
unsigned long bitmap[];
};
@@ -858,6 +860,15 @@ void bpf_jit_fill_hole_with_zero(void *area, unsigned int size)
memset(area, 0, size);
}
+DEFINE_STATIC_CALL_NULL(bpf_arch_pred_flush, bpf_arch_pred_flush);
+
+/*
+ * Enabled once bpf_arch_pred_flush points at a real flush routine. Lets the
+ * pack allocator test "is a predictor flush wired up at all" with a cheap
+ * static branch instead of repeatedly querying the static call target.
+ */
+DEFINE_STATIC_KEY_FALSE(bpf_pred_flush_enabled);
+
#define BPF_PROG_SIZE_TO_NBITS(size) (round_up(size, BPF_PROG_CHUNK_SIZE) / BPF_PROG_CHUNK_SIZE)
static DEFINE_MUTEX(pack_mutex);
@@ -896,20 +907,30 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins
bitmap_zero(pack->bitmap, BPF_PROG_PACK_SIZE / BPF_PROG_CHUNK_SIZE);
list_add_tail(&pack->list, &pack_list);
+ if (static_branch_unlikely(&bpf_pred_flush_enabled))
+ pack->arch_flush_needed = true;
set_vm_flush_reset_perms(pack->ptr);
set_memory_rox((unsigned long)pack->ptr, BPF_PROG_PACK_SIZE / PAGE_SIZE);
return pack;
}
-void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns)
+void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns, bool was_classic)
{
unsigned int nbits = BPF_PROG_SIZE_TO_NBITS(size);
- struct bpf_prog_pack *pack;
- unsigned long pos;
+ struct bpf_prog_pack *pack, *fallback_pack = NULL;
+ unsigned long pos, fallback_pos = 0;
void *ptr = NULL;
mutex_lock(&pack_mutex);
if (size > BPF_PROG_PACK_SIZE) {
+ /*
+ * Allocations larger than a pack get their own pages, and
+ * predictors are not flushed for such allocation. This is only
+ * safe because cBPF programs (the unprivileged attack surface)
+ * are bounded well below a pack size.
+ */
+ if (was_classic && static_branch_unlikely(&bpf_pred_flush_enabled))
+ pr_warn_once("BPF: Predictors not flushed for allocations greater than BPF_PROG_PACK_SIZE\n");
size = round_up(size, PAGE_SIZE);
ptr = bpf_jit_alloc_exec(size);
if (ptr) {
@@ -922,8 +943,29 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns)
list_for_each_entry(pack, &pack_list, list) {
pos = bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0,
nbits, 0);
- if (pos < BPF_PROG_CHUNK_COUNT)
+ if (pos >= BPF_PROG_CHUNK_COUNT)
+ continue;
+ /* Flush not enabled, use any pack */
+ if (!static_branch_unlikely(&bpf_pred_flush_enabled))
goto found_free_area;
+ /*
+ * cBPF reuse of a dirty pack triggers a flush, so prefer a
+ * clean pack for cBPF. eBPF never flushes, so steer it to a
+ * dirty pack and keep clean packs free for cBPF.
+ */
+ if (was_classic ^ pack->arch_flush_needed)
+ goto found_free_area;
+ if (!fallback_pack) {
+ fallback_pack = pack;
+ fallback_pos = pos;
+ }
+ }
+
+ /* No preferred pack found */
+ if (fallback_pack) {
+ pack = fallback_pack;
+ pos = fallback_pos;
+ goto found_free_area;
}
pack = alloc_new_pack(bpf_fill_ill_insns);
@@ -933,6 +975,16 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns)
pos = 0;
found_free_area:
+ /* Flush only for cBPF as it may contain a crafted gadget */
+ if (static_branch_unlikely(&bpf_pred_flush_enabled) &&
+ pack->arch_flush_needed &&
+ was_classic) {
+ struct bpf_prog_pack *p;
+
+ static_call_cond(bpf_arch_pred_flush)();
+ list_for_each_entry(p, &pack_list, list)
+ p->arch_flush_needed = false;
+ }
bitmap_set(pack->bitmap, pos, nbits);
ptr = (void *)(pack->ptr) + (pos << BPF_PROG_CHUNK_SHIFT);
@@ -970,6 +1022,9 @@ void bpf_prog_pack_free(struct bpf_binary_header *hdr)
"bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n");
bitmap_clear(pack->bitmap, pos, nbits);
+
+ if (static_branch_unlikely(&bpf_pred_flush_enabled))
+ pack->arch_flush_needed = true;
if (bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0,
BPF_PROG_CHUNK_COUNT, 0) == 0) {
list_del(&pack->list);
@@ -1092,7 +1147,8 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr,
unsigned int alignment,
struct bpf_binary_header **rw_header,
u8 **rw_image,
- bpf_jit_fill_hole_t bpf_fill_ill_insns)
+ bpf_jit_fill_hole_t bpf_fill_ill_insns,
+ bool was_classic)
{
struct bpf_binary_header *ro_header;
u32 size, hole, start;
@@ -1105,7 +1161,7 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr,
if (bpf_jit_charge_modmem(size))
return NULL;
- ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns);
+ ro_header = bpf_prog_pack_alloc(size, bpf_fill_ill_insns, was_classic);
if (!ro_header) {
bpf_jit_uncharge_modmem(size);
return NULL;
@@ -2309,7 +2365,7 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map,
cookie = aux->cgroup_storage[i] ?
aux->cgroup_storage[i]->cookie : 0;
ret = map->owner->storage_cookie[i] == cookie ||
- !cookie;
+ (!cookie && !aux->tail_call_reachable);
}
if (ret &&
map->owner->attach_func_proto != aux->attach_func_proto) {
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index c169f0624e10..8ce3b152dcd5 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -558,6 +558,10 @@ static int dev_map_enqueue_clone(struct bpf_dtab_netdev *obj,
{
struct xdp_frame *nxdpf;
+ /* Frags live outside the linear frame and cannot be cloned safely. */
+ if (unlikely(xdp_frame_has_frags(xdpf)))
+ return -EOPNOTSUPP;
+
nxdpf = xdpf_clone(xdpf);
if (!nxdpf)
return -ENOMEM;
@@ -683,6 +687,18 @@ int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb,
if (unlikely(err))
return err;
+ if (dst->xdp_prog && skb_cloned(skb)) {
+ struct sk_buff *nskb;
+
+ nskb = skb_copy(skb, GFP_ATOMIC);
+ if (!nskb)
+ return -ENOMEM;
+
+ nskb->mac_len = skb->mac_len;
+ consume_skb(skb);
+ skb = nskb;
+ }
+
/* Redirect has already succeeded semantically at this point, so we just
* return 0 even if packet is dropped. Helper below takes care of
* freeing skb.
@@ -703,6 +719,9 @@ static int dev_map_redirect_clone(struct bpf_dtab_netdev *dst,
struct sk_buff *nskb;
int err;
+ if (unlikely(skb_is_nonlinear(skb)))
+ return -EOPNOTSUPP;
+
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
return -ENOMEM;
diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c
index fa3e9225aedc..b3f164e31c6b 100644
--- a/kernel/bpf/dispatcher.c
+++ b/kernel/bpf/dispatcher.c
@@ -145,7 +145,7 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
mutex_lock(&d->mutex);
if (!d->image) {
- d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero);
+ d->image = bpf_prog_pack_alloc(PAGE_SIZE, bpf_jit_fill_hole_with_zero, false);
if (!d->image)
goto out;
d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE);
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 934fdc688d7c..9c04e33d9d22 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -681,8 +681,7 @@ static void *__htab_map_lookup_elem(struct bpf_map *map, void *key)
struct htab_elem *l;
u32 hash, key_size;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1117,8 +1116,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
/* unknown flags */
return -EINVAL;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1235,8 +1233,7 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value
/* unknown flags */
return -EINVAL;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1304,8 +1301,7 @@ static long __htab_percpu_map_update_elem(struct bpf_map *map, void *key,
/* unknown flags */
return -EINVAL;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1359,8 +1355,7 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
/* unknown flags */
return -EINVAL;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1437,8 +1432,7 @@ static long htab_map_delete_elem(struct bpf_map *map, void *key)
u32 hash, key_size;
int ret;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
@@ -1473,8 +1467,7 @@ static long htab_lru_map_delete_elem(struct bpf_map *map, void *key)
u32 hash, key_size;
int ret;
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
key_size = map->key_size;
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 885ff0710a8b..419e49946cbd 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -36,8 +36,7 @@
*/
BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key)
{
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
return (unsigned long) map->ops->map_lookup_elem(map, key);
}
@@ -53,8 +52,7 @@ const struct bpf_func_proto bpf_map_lookup_elem_proto = {
BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key,
void *, value, u64, flags)
{
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
return map->ops->map_update_elem(map, key, value, flags);
}
@@ -71,8 +69,7 @@ const struct bpf_func_proto bpf_map_update_elem_proto = {
BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
{
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
return map->ops->map_delete_elem(map, key);
}
@@ -128,8 +125,7 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = {
BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
{
- WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
- !rcu_read_lock_bh_held());
+ WARN_ON_ONCE(!bpf_rcu_lock_held());
return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu);
}
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 9a9630adcba4..99ec74325175 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -611,10 +611,18 @@ static void bpf_destroy_inode(struct inode *inode)
{
enum bpf_type type;
- if (S_ISLNK(inode->i_mode))
- kfree(inode->i_link);
if (!bpf_inode_type(inode, &type))
bpf_any_put(inode->i_private, type);
+}
+
+/*
+ * Called after RCU grace period - safe to free inode and anything
+ * that might be accessed by RCU pathwalk (inode fields, i_link).
+ */
+static void bpf_free_inode(struct inode *inode)
+{
+ if (S_ISLNK(inode->i_mode))
+ kfree(inode->i_link);
free_inode_nonrcu(inode);
}
@@ -623,6 +631,7 @@ static const struct super_operations bpf_super_ops = {
.drop_inode = generic_delete_inode,
.show_options = bpf_show_options,
.destroy_inode = bpf_destroy_inode,
+ .free_inode = bpf_free_inode,
};
enum {
diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c
index c4f1d465ae35..def44994c479 100644
--- a/kernel/bpf/lpm_trie.c
+++ b/kernel/bpf/lpm_trie.c
@@ -236,7 +236,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key)
/* Start walking the trie from the root node ... */
- for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held());
+ for (node = rcu_dereference_check(trie->root, bpf_rcu_lock_held());
node;) {
unsigned int next_bit;
size_t matchlen;
@@ -270,7 +270,7 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key)
*/
next_bit = extract_bit(key->data, node->prefixlen);
node = rcu_dereference_check(node->child[next_bit],
- rcu_read_lock_bh_held());
+ bpf_rcu_lock_held());
}
if (!found)
@@ -353,8 +353,7 @@ static long trie_update_elem(struct bpf_map *map,
*/
slot = &trie->root;
- while ((node = rcu_dereference_protected(*slot,
- lockdep_is_held(&trie->lock)))) {
+ while ((node = rcu_dereference_protected(*slot, 1))) {
matchlen = longest_prefix_match(trie, node, key);
if (node->prefixlen != matchlen ||
@@ -475,8 +474,7 @@ static long trie_delete_elem(struct bpf_map *map, void *_key)
trim = &trie->root;
trim2 = trim;
parent = NULL;
- while ((node = rcu_dereference_protected(
- *trim, lockdep_is_held(&trie->lock)))) {
+ while ((node = rcu_dereference_protected(*trim, 1))) {
matchlen = longest_prefix_match(trie, node, key);
if (node->prefixlen != matchlen ||
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 2207f9e7a567..5fa86a2ab14a 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -4403,10 +4403,11 @@ static int bpf_prog_get_info_by_fd(struct file *file,
u32 info_len = attr->info.info_len;
struct bpf_prog_kstats stats;
char __user *uinsns;
- u32 ulen;
+ u32 ulen, len;
int err;
- err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(info), info_len);
+ len = offsetofend(struct bpf_prog_info, attach_btf_id);
+ err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), len, info_len);
if (err)
return err;
info_len = min_t(u32, sizeof(info), info_len);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f638b2d3a42f..1da0092122c1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4672,7 +4672,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
bool sanitize = reg && is_spillable_regtype(reg->type);
for (i = 0; i < size; i++) {
- u8 type = state->stack[spi].slot_type[i];
+ u8 type = state->stack[spi].slot_type[(slot - i) %
+ BPF_REG_SIZE];
if (type != STACK_MISC && type != STACK_ZERO) {
sanitize = true;
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 633a91709eb5..f9c7178f51ad 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2097,7 +2097,12 @@ static void update_tasks_nodemask(struct cpuset *cs)
migrate = is_memory_migrate(cs);
- mpol_rebind_mm(mm, &cs->mems_allowed);
+ /*
+ * For v1 we can have empty effective_mems, but we cannot
+ * attach any tasks (see cpuset_can_attach_check()). For v2,
+ * effective_mems is guaranteed to not be empty.
+ */
+ mpol_rebind_mm(mm, &cs->effective_mems);
if (migrate)
cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems);
else
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 7ab11b459768..9b731fb9d986 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -175,7 +175,7 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state,
struct cpuhp_step *step = cpuhp_get_step(state);
int (*cbm)(unsigned int cpu, struct hlist_node *node);
int (*cb)(unsigned int cpu);
- int ret, cnt;
+ int ret, cnt, rollback_ret;
if (st->fail == state) {
st->fail = CPUHP_INVALID;
@@ -239,12 +239,12 @@ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state,
break;
trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node);
- ret = cbm(cpu, node);
- trace_cpuhp_exit(cpu, st->state, state, ret);
+ rollback_ret = cbm(cpu, node);
+ trace_cpuhp_exit(cpu, st->state, state, rollback_ret);
/*
* Rollback must not fail,
*/
- WARN_ON_ONCE(ret);
+ WARN_ON_ONCE(rollback_ret);
}
return ret;
}
@@ -2894,21 +2894,17 @@ static const struct attribute_group cpuhp_cpu_attr_group = {
NULL
};
-static ssize_t states_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t states_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- ssize_t cur, res = 0;
+ ssize_t res = 0;
int i;
mutex_lock(&cpuhp_state_mutex);
for (i = CPUHP_OFFLINE; i <= CPUHP_ONLINE; i++) {
struct cpuhp_step *sp = cpuhp_get_step(i);
- if (sp->name) {
- cur = sprintf(buf, "%3d: %s\n", i, sp->name);
- buf += cur;
- res += cur;
- }
+ if (sp->name)
+ res += sysfs_emit_at(buf, res, "%3d: %s\n", i, sp->name);
}
mutex_unlock(&cpuhp_state_mutex);
return res;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index a4187dea6402..d45bce2acdbc 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2384,10 +2384,9 @@ __perf_remove_from_context(struct perf_event *event,
*/
if (flags & DETACH_EXIT)
state = PERF_EVENT_STATE_EXIT;
- if (flags & DETACH_DEAD) {
- event->pending_disable = 1;
+ if (flags & DETACH_DEAD)
state = PERF_EVENT_STATE_DEAD;
- }
+
event_sched_out(event, ctx);
if (event->state > PERF_EVENT_STATE_OFF)
@@ -4450,7 +4449,8 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx)
static void perf_remove_from_owner(struct perf_event *event);
static void perf_event_exit_event(struct perf_event *event,
- struct perf_event_context *ctx);
+ struct perf_event_context *ctx,
+ unsigned long detach_flags);
/*
* Removes all events from the current task that have been marked
@@ -4477,7 +4477,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx)
modified = true;
- perf_event_exit_event(event, ctx);
+ perf_event_exit_event(event, ctx, DETACH_GROUP);
}
raw_spin_lock_irqsave(&ctx->lock, flags);
@@ -13231,10 +13231,11 @@ static void sync_child_event(struct perf_event *child_event)
}
static void
-perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx)
+perf_event_exit_event(struct perf_event *event,
+ struct perf_event_context *ctx,
+ unsigned long detach_flags)
{
struct perf_event *parent_event = event->parent;
- unsigned long detach_flags = 0;
if (parent_event) {
/*
@@ -13249,7 +13250,7 @@ perf_event_exit_event(struct perf_event *event, struct perf_event_context *ctx)
* Do destroy all inherited groups, we don't care about those
* and being thorough is better.
*/
- detach_flags = DETACH_GROUP | DETACH_CHILD;
+ detach_flags |= DETACH_GROUP | DETACH_CHILD;
mutex_lock(&parent_event->child_mutex);
}
@@ -13329,7 +13330,7 @@ static void perf_event_exit_task_context(struct task_struct *child)
perf_event_task(child, child_ctx, 0);
list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry)
- perf_event_exit_event(child_event, child_ctx);
+ perf_event_exit_event(child_event, child_ctx, 0);
mutex_unlock(&child_ctx->mutex);
diff --git a/kernel/fork.c b/kernel/fork.c
index e280f02b6446..724040ac5895 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1205,6 +1205,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
tsk->mm_cid_active = 0;
tsk->migrate_from_cpu = -1;
#endif
+
+#ifdef CONFIG_BPF_SYSCALL
+ RCU_INIT_POINTER(tsk->bpf_storage, NULL);
+ tsk->bpf_ctx = NULL;
+#endif
return tsk;
free_stack:
@@ -2469,10 +2474,6 @@ __latent_entropy struct task_struct *copy_process(
p->sequential_io = 0;
p->sequential_io_avg = 0;
#endif
-#ifdef CONFIG_BPF_SYSCALL
- RCU_INIT_POINTER(p->bpf_storage, NULL);
- p->bpf_ctx = NULL;
-#endif
/* Perform scheduler related setup. Assign this task to a CPU. */
retval = sched_fork(clone_flags, p);
diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
index 474e68f0f063..ce9d90808d95 100644
--- a/kernel/module/decompress.c
+++ b/kernel/module/decompress.c
@@ -307,6 +307,8 @@ int module_decompress(struct load_info *info, const void *buf, size_t size)
*/
n_pages = DIV_ROUND_UP(size, PAGE_SIZE) * 2;
error = module_extend_max_pages(info, n_pages);
+ if (error)
+ return error;
data_size = MODULE_DECOMPRESS_FN(info, buf, size);
if (data_size < 0) {
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d558e43aedcf..797b81dcf69a 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4751,6 +4751,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p)
p->policy = SCHED_NORMAL;
p->static_prio = NICE_TO_PRIO(0);
p->rt_priority = 0;
+ p->timer_slack_ns = p->default_timer_slack_ns;
} else if (PRIO_TO_NICE(p->static_prio) < 0)
p->static_prio = NICE_TO_PRIO(0);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index cdf49a04fd58..c3a6b66b836c 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7711,25 +7711,32 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
static unsigned long
cpu_util(int cpu, struct task_struct *p, int dst_cpu, int boost)
{
+ bool add_task = p && task_cpu(p) != cpu && dst_cpu == cpu;
+ bool sub_task = p && task_cpu(p) == cpu && dst_cpu != cpu;
struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs;
unsigned long util = READ_ONCE(cfs_rq->avg.util_avg);
unsigned long runnable;
- if (boost) {
- runnable = READ_ONCE(cfs_rq->avg.runnable_avg);
- util = max(util, runnable);
- }
-
/*
* If @dst_cpu is -1 or @p migrates from @cpu to @dst_cpu remove its
* contribution. If @p migrates from another CPU to @cpu add its
* contribution. In all the other cases @cpu is not impacted by the
* migration so its util_avg is already correct.
*/
- if (p && task_cpu(p) == cpu && dst_cpu != cpu)
- lsub_positive(&util, task_util(p));
- else if (p && task_cpu(p) != cpu && dst_cpu == cpu)
+ if (add_task)
util += task_util(p);
+ else if (sub_task)
+ lsub_positive(&util, task_util(p));
+
+ if (boost) {
+ runnable = READ_ONCE(cfs_rq->avg.runnable_avg);
+ if (add_task)
+ runnable += READ_ONCE(p->se.avg.runnable_avg);
+ else if (sub_task)
+ lsub_positive(&runnable,
+ READ_ONCE(p->se.avg.runnable_avg));
+ util = max(util, runnable);
+ }
if (sched_feat(UTIL_EST)) {
unsigned long util_est;
@@ -10266,7 +10273,7 @@ static inline void update_sg_wakeup_stats(struct sched_domain *sd,
if (sd->flags & SD_ASYM_CPUCAPACITY)
sgs->group_misfit_task_load = 1;
- for_each_cpu(i, sched_group_span(group)) {
+ for_each_cpu_and(i, sched_group_span(group), p->cpus_ptr) {
struct rq *rq = cpu_rq(i);
unsigned int local;
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index 48b104ab579c..40b48404a5a6 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -68,8 +68,16 @@ SCHED_FEAT(WARN_DOUBLE_CLOCK, false)
* rq lock and possibly create a large contention, sending an
* IPI to that CPU and let that CPU push the RT task to where
* it should go may be a better scenario.
+ *
+ * This is best for PREEMPT_RT, but for non-RT it can cause issues
+ * when preemption is disabled for long periods of time. Have
+ * it only default enabled for PREEMPT_RT.
*/
+# ifdef CONFIG_PREEMPT_RT
SCHED_FEAT(RT_PUSH_IPI, true)
+# else
+SCHED_FEAT(RT_PUSH_IPI, false)
+# endif
#endif
SCHED_FEAT(RT_RUNTIME_SHARE, false)
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 9af1f2a72a0a..b99c386c50e4 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -41,7 +41,7 @@ void posix_cputimers_group_init(struct posix_cputimers *pct, u64 cpu_limit)
*/
int update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
{
- u64 nsecs = rlim_new * NSEC_PER_SEC;
+ u64 nsecs = (u64)rlim_new * NSEC_PER_SEC;
unsigned long irq_fl;
if (!lock_task_sighand(task, &irq_fl))
@@ -1535,6 +1535,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
spin_lock_irq(&timer.it_lock);
error = posix_cpu_timer_set(&timer, flags, &it, NULL);
if (error) {
+ posix_cpu_timer_del(&timer);
spin_unlock_irq(&timer.it_lock);
return error;
}
diff --git a/kernel/time/time.c b/kernel/time/time.c
index a6261fadb92b..bfb0c1636290 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -251,7 +251,7 @@ COMPAT_SYSCALL_DEFINE2(settimeofday, struct old_timeval32 __user *, tv,
get_user(new_ts.tv_nsec, &tv->tv_usec))
return -EFAULT;
- if (new_ts.tv_nsec > USEC_PER_SEC || new_ts.tv_nsec < 0)
+ if (new_ts.tv_nsec >= USEC_PER_SEC || new_ts.tv_nsec < 0)
return -EINVAL;
new_ts.tv_nsec *= NSEC_PER_USEC;
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 87e7cc2dc5cc..c50852022cca 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2549,9 +2549,12 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32
int err = -ENOMEM;
unsigned int i;
+ if (!access_ok(usyms, cnt * sizeof(*usyms)))
+ return -EFAULT;
+
syms = kvmalloc_array(cnt, sizeof(*syms), GFP_KERNEL);
if (!syms)
- goto error;
+ return -ENOMEM;
buf = kvmalloc_array(cnt, KSYM_NAME_LEN, GFP_KERNEL);
if (!buf)
@@ -2576,10 +2579,8 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32
return 0;
error:
- if (err) {
- kvfree(syms);
- kvfree(buf);
- }
+ kvfree(syms);
+ kvfree(buf);
return err;
}
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 52c7dbccafed..0f761bca5135 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -255,7 +255,8 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
if (event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX)
return length;
length -= RB_EVNT_HDR_SIZE;
- if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]))
+ if (length > RB_MAX_SMALL_DATA + sizeof(event->array[0]) ||
+ RB_FORCE_8BYTE_ALIGNMENT)
length -= sizeof(event->array[0]);
return length;
}
diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c
index f1f12ce5efb0..6330d25ac653 100644
--- a/kernel/trace/trace_eprobe.c
+++ b/kernel/trace/trace_eprobe.c
@@ -314,7 +314,7 @@ get_event_field(struct fetch_insn *code, void *rec)
val = (unsigned long)addr;
break;
case FILTER_PTR_STRING:
- val = (unsigned long)(*(char *)addr);
+ val = *(unsigned long *)addr;
break;
default:
WARN_ON_ONCE(1);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index f50c2ad43f3d..9a26ac07e324 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, struct regex *r, int len)
return 0;
}
-static int regex_match_glob(char *str, struct regex *r, int len __maybe_unused)
+static int regex_match_glob(char *str, struct regex *r, int len)
{
- if (glob_match(r->pattern, str))
- return 1;
- return 0;
+ return glob_match_len(r->pattern, str, len) ? 1 : 0;
}
/**
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 59115079c798..f54e3855516f 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/security.h>
+#include <linux/seq_buf.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/stacktrace.h>
@@ -2943,13 +2944,22 @@ find_synthetic_field_var(struct hist_trigger_data *target_hist_data,
{
struct hist_field *event_var;
char *synthetic_name;
+ struct seq_buf s;
synthetic_name = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
if (!synthetic_name)
return ERR_PTR(-ENOMEM);
- strcpy(synthetic_name, "synthetic_");
- strcat(synthetic_name, field_name);
+ seq_buf_init(&s, synthetic_name, MAX_FILTER_STR_VAL);
+ seq_buf_printf(&s, "synthetic_%s", field_name);
+
+ /* Terminate synthetic_name with a NUL. */
+ seq_buf_str(&s);
+
+ if (seq_buf_has_overflowed(&s)) {
+ kfree(synthetic_name);
+ return ERR_PTR(-E2BIG);
+ }
event_var = find_event_var(target_hist_data, system, event_name, synthetic_name);
@@ -2995,6 +3005,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
struct hist_field *key_field;
struct hist_field *event_var;
char *saved_filter;
+ struct seq_buf s;
char *cmd;
int ret;
@@ -3039,28 +3050,34 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
return ERR_PTR(-ENOMEM);
}
+ seq_buf_init(&s, cmd, MAX_FILTER_STR_VAL);
+
/* Use the same keys as the compatible histogram */
- strcat(cmd, "keys=");
+ seq_buf_puts(&s, "keys=");
for_each_hist_key_field(i, hist_data) {
key_field = hist_data->fields[i];
if (!first)
- strcat(cmd, ",");
- strcat(cmd, key_field->field->name);
+ seq_buf_putc(&s, ',');
+ seq_buf_puts(&s, key_field->field->name);
first = false;
}
/* Create the synthetic field variable specification */
- strcat(cmd, ":synthetic_");
- strcat(cmd, field_name);
- strcat(cmd, "=");
- strcat(cmd, field_name);
+ seq_buf_printf(&s, ":synthetic_%s=%s", field_name, field_name);
/* Use the same filter as the compatible histogram */
saved_filter = find_trigger_filter(hist_data, file);
- if (saved_filter) {
- strcat(cmd, " if ");
- strcat(cmd, saved_filter);
+ if (saved_filter)
+ seq_buf_printf(&s, " if %s", saved_filter);
+
+ /* Terminate cmd with a NUL. */
+ seq_buf_str(&s);
+
+ if (seq_buf_has_overflowed(&s)) {
+ kfree(cmd);
+ kfree(var_hist);
+ return ERR_PTR(-E2BIG);
}
var_hist->cmd = kstrdup(cmd, GFP_KERNEL);
diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 3b039374c993..8f4b59bedea6 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -159,7 +159,9 @@ static void osnoise_unregister_instance(struct trace_array *tr)
if (!found)
return;
- kvfree_rcu_mightsleep(inst);
+ /* Do a full sync to ensure that tr remains valid, not just inst */
+ synchronize_rcu();
+ kvfree(inst);
}
/*
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 8f52ffd3c6bb..c0f9ec31a91c 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1104,6 +1104,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
code->op = FETCH_OP_FOFFS;
code->immediate = (unsigned long)offset; // imm64?
+ offset = 0;
} else {
/* uprobes don't support symbols */
if (!(ctx->flags & TPARG_FL_KERNEL)) {
@@ -1219,8 +1220,6 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
return ret;
}
-#define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long))
-
/* Bitfield type needs to be parsed into a fetch function */
static int __parse_bitfield_probe_arg(const char *bf,
const struct fetch_type *t,
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index ce5a0935cd45..9fc6b3a39435 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -496,7 +496,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(NO_RETVAL, "This function returns 'void' type"), \
C(BAD_STACK_NUM, "Invalid stack number"), \
C(BAD_ARG_NUM, "Invalid argument number"), \
- C(BAD_VAR, "Invalid $-valiable specified"), \
+ C(BAD_VAR, "Invalid $-variable specified"), \
C(BAD_REG_NAME, "Invalid register name"), \
C(BAD_MEM_ADDR, "Invalid memory address"), \
C(BAD_IMM, "Invalid immediate value"), \
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 8692e75aa6d8..b59cc9f86d15 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -5119,7 +5119,7 @@ EXPORT_SYMBOL_GPL(set_worker_desc);
*/
void print_worker_info(const char *log_lvl, struct task_struct *task)
{
- work_func_t *fn = NULL;
+ work_func_t fn = NULL;
char name[WQ_NAME_LEN] = { };
char desc[WORKER_DESC_LEN] = { };
struct pool_workqueue *pwq = NULL;
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index e4ef9d032d17..65e66d7e4386 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -768,6 +768,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
}
raw_spin_unlock_irqrestore(&db->lock, flags);
+
+ /*
+ * lookup_object_or_alloc() might have raced with a concurrent
+ * allocation failure which disabled debug objects.
+ */
+ if (!debug_objects_enabled)
+ return 0;
+
debug_print_object(&o, "activate");
switch (o.state) {
@@ -945,6 +953,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
return;
}
+ /*
+ * lookup_object_or_alloc() might have raced with a concurrent
+ * allocation failure which disabled debug objects. Don't run the fixup
+ * as it might turn a valid object useless. See for example
+ * hrtimer_fixup_assert_init().
+ */
+ if (!debug_objects_enabled)
+ return;
+
/* Object is neither tracked nor static. It's not initialized. */
debug_print_object(&o, "assert_init");
debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE);
diff --git a/lib/glob.c b/lib/glob.c
index 15b73f490720..b2a7ccfd8af8 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -9,6 +9,9 @@
MODULE_DESCRIPTION("glob(7) matching");
MODULE_LICENSE("Dual MIT/GPL");
+static bool __pure glob_match_str(char const *pat, char const *str,
+ char const *str_end);
+
/**
* glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
* @pat: Shell-style pattern to match, e.g. "*.[ch]".
@@ -38,6 +41,29 @@ MODULE_LICENSE("Dual MIT/GPL");
* An opening bracket without a matching close is matched literally.
*/
bool __pure glob_match(char const *pat, char const *str)
+{
+ return glob_match_str(pat, str, NULL);
+}
+EXPORT_SYMBOL(glob_match);
+
+/**
+ * glob_match_len - glob match against a length-bounded string
+ * @pat: Shell-style pattern to match.
+ * @str: String to match. Need not be NUL-terminated.
+ * @len: Number of bytes of @str that may be read.
+ *
+ * Like glob_match(), but @str is only read up to @len bytes, so it can be
+ * used on buffers that are not NUL-terminated (e.g. trace event fields).
+ * A NUL byte within @len still terminates the string.
+ */
+bool __pure glob_match_len(char const *pat, char const *str, size_t len)
+{
+ return glob_match_str(pat, str, str + len);
+}
+EXPORT_SYMBOL(glob_match_len);
+
+static bool __pure glob_match_str(char const *pat, char const *str,
+ char const *str_end)
{
/*
* Backtrack to previous * on mismatch and retry starting one
@@ -53,9 +79,11 @@ bool __pure glob_match(char const *pat, char const *str)
* on mismatch, or true after matching the trailing nul bytes.
*/
for (;;) {
- unsigned char c = *str++;
+ unsigned char c = (str_end && str >= str_end) ? '\0' : *str;
unsigned char d = *pat++;
+ str++;
+
switch (d) {
case '?': /* Wildcard: anything but nul */
if (c == '\0')
@@ -120,4 +148,3 @@ bool __pure glob_match(char const *pat, char const *str)
}
}
}
-EXPORT_SYMBOL(glob_match);
diff --git a/lib/test_meminit.c b/lib/test_meminit.c
index 0dc173849a54..d0aa0acd4e01 100644
--- a/lib/test_meminit.c
+++ b/lib/test_meminit.c
@@ -387,7 +387,7 @@ static int __init test_kmemcache(int *total_failures)
ctor = flags & 1;
rcu = flags & 2;
zero = flags & 4;
- if (ctor & zero)
+ if (ctor && zero)
continue;
num_tests += do_kmem_cache_size(size, ctor, rcu, zero,
&failures);
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 4234caa564b3..dabab8461f0d 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -545,6 +545,7 @@ static void cgwb_release_workfn(struct work_struct *work)
wb_exit(wb);
bdi_put(bdi);
WARN_ON_ONCE(!list_empty(&wb->b_attached));
+ WARN_ON_ONCE(work_pending(&wb->switch_work));
call_rcu(&wb->rcu, cgwb_free_rcu);
}
@@ -621,6 +622,8 @@ static int cgwb_create(struct backing_dev_info *bdi,
wb->memcg_css = memcg_css;
wb->blkcg_css = blkcg_css;
INIT_LIST_HEAD(&wb->b_attached);
+ INIT_WORK(&wb->switch_work, inode_switch_wbs_work_fn);
+ init_llist_head(&wb->switch_wbs_ctxs);
INIT_WORK(&wb->release_work, cgwb_release_workfn);
set_bit(WB_registered, &wb->state);
bdi_get(bdi);
@@ -751,6 +754,8 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi)
if (!ret) {
bdi->wb.memcg_css = &root_mem_cgroup->css;
bdi->wb.blkcg_css = blkcg_root_css;
+ INIT_WORK(&bdi->wb.switch_work, inode_switch_wbs_work_fn);
+ init_llist_head(&bdi->wb.switch_wbs_ctxs);
}
return ret;
}
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 8e5e3bb835d1..46600265d7ff 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -1003,7 +1003,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
quota->charged_sz += sz;
if (quota->esz && quota->charged_sz >= quota->esz) {
quota->charge_target_from = t;
- quota->charge_addr_from = r->ar.end + 1;
+ quota->charge_addr_from = r->ar.end;
}
}
if (s->action != DAMOS_STAT)
diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index aa706b696980..d0cea04605ff 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -107,6 +107,7 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r,
* Transform it to fit in [0, DAMOS_MAX_SCORE]
*/
hotness = hotness * DAMOS_MAX_SCORE / DAMON_MAX_SUBSCORE;
+ hotness = max(min(hotness, DAMOS_MAX_SCORE), 0);
return hotness;
}
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 62df3ff8de17..3cde2137dba8 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -1077,22 +1077,19 @@ static int damon_sysfs_access_pattern_add_dirs(
err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
&access_pattern->sz, "sz");
if (err)
- goto put_sz_out;
+ return err;
err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
&access_pattern->nr_accesses, "nr_accesses");
if (err)
- goto put_nr_accesses_sz_out;
+ goto put_sz_out;
err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
&access_pattern->age, "age");
if (err)
- goto put_age_nr_accesses_sz_out;
+ goto put_nr_accesses_sz_out;
return 0;
-put_age_nr_accesses_sz_out:
- kobject_put(&access_pattern->age->kobj);
- access_pattern->age = NULL;
put_nr_accesses_sz_out:
kobject_put(&access_pattern->nr_accesses->kobj);
access_pattern->nr_accesses = NULL;
@@ -1306,12 +1303,12 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
goto put_filters_watermarks_quotas_access_pattern_out;
err = damon_sysfs_scheme_set_tried_regions(scheme);
if (err)
- goto put_tried_regions_out;
+ goto put_stats_out;
return 0;
-put_tried_regions_out:
- kobject_put(&scheme->tried_regions->kobj);
- scheme->tried_regions = NULL;
+put_stats_out:
+ kobject_put(&scheme->stats->kobj);
+ scheme->stats = NULL;
put_filters_watermarks_quotas_access_pattern_out:
kobject_put(&scheme->filters->kobj);
scheme->filters = NULL;
diff --git a/mm/filemap.c b/mm/filemap.c
index 7416868d0449..5a340cad44ed 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -4314,7 +4314,7 @@ static inline bool can_do_cachestat(struct file *f)
{
if (f->f_mode & FMODE_WRITE)
return true;
- if (inode_owner_or_capable(file_mnt_idmap(f), file_inode(f)))
+ if (file_owner_or_capable(f))
return true;
return file_permission(f, MAY_WRITE) == 0;
}
diff --git a/mm/madvise.c b/mm/madvise.c
index 3d6370d3199f..73ea053c9003 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -334,8 +334,7 @@ static inline bool can_do_file_pageout(struct vm_area_struct *vma)
* otherwise we'd be including shared non-exclusive mappings, which
* opens a side channel.
*/
- return inode_owner_or_capable(&nop_mnt_idmap,
- file_inode(vma->vm_file)) ||
+ return file_owner_or_capable(vma->vm_file) ||
file_permission(vma->vm_file, MAY_WRITE) == 0;
}
diff --git a/mm/mincore.c b/mm/mincore.c
index dad3622cc963..2a55eed792b6 100644
--- a/mm/mincore.c
+++ b/mm/mincore.c
@@ -167,8 +167,7 @@ static inline bool can_do_mincore(struct vm_area_struct *vma)
* for writing; otherwise we'd be including shared non-exclusive
* mappings, which opens a side channel.
*/
- return inode_owner_or_capable(&nop_mnt_idmap,
- file_inode(vma->vm_file)) ||
+ return file_owner_or_capable(vma->vm_file) ||
file_permission(vma->vm_file, MAY_WRITE) == 0;
}
diff --git a/mm/mm_init.c b/mm/mm_init.c
index f5519ca1263e..f9f87e2299b8 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1024,10 +1024,17 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
* of how the sparse_vmemmap internals handle compound pages in the lack
* of an altmap. See vmemmap_populate_compound_pages().
*/
-static inline unsigned long compound_nr_pages(struct vmem_altmap *altmap,
+static inline unsigned long compound_nr_pages(unsigned long pfn,
+ struct vmem_altmap *altmap,
struct dev_pagemap *pgmap)
{
- if (!vmemmap_can_optimize(altmap, pgmap))
+ /*
+ * If DAX memory is hot-plugged into an unoccupied subsection
+ * of an early section, the unoptimized boot memmap is reused.
+ * See section_activate().
+ */
+ if (early_section(__pfn_to_section(pfn)) ||
+ !vmemmap_can_optimize(altmap, pgmap))
return pgmap_vmemmap_nr(pgmap);
return VMEMMAP_RESERVE_NR * (PAGE_SIZE / sizeof(struct page));
@@ -1095,7 +1102,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
continue;
memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
- compound_nr_pages(altmap, pgmap));
+ compound_nr_pages(pfn, altmap, pgmap));
}
pr_debug("%s initialised %lu pages in %ums\n", __func__,
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 3ab53fad8876..0b2cdb48aa22 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -49,19 +49,12 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
struct mem_cgroup *memcg;
unsigned long total;
bool memcg_aware;
- int ret, nid;
+ int ret = 0, nid;
count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL);
if (!count_per_node)
return -ENOMEM;
- ret = down_read_killable(&shrinker_rwsem);
- if (ret) {
- kfree(count_per_node);
- return ret;
- }
- rcu_read_lock();
-
memcg_aware = shrinker->flags & SHRINKER_MEMCG_AWARE;
memcg = mem_cgroup_iter(NULL, NULL, NULL);
@@ -91,9 +84,6 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
}
} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
- rcu_read_unlock();
- up_read(&shrinker_rwsem);
-
kfree(count_per_node);
return ret;
}
@@ -117,7 +107,6 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
struct mem_cgroup *memcg = NULL;
int nid;
char kbuf[72];
- ssize_t ret;
read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
if (copy_from_user(kbuf, buf, read_len))
@@ -146,12 +135,6 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
return -EINVAL;
}
- ret = down_read_killable(&shrinker_rwsem);
- if (ret) {
- mem_cgroup_put(memcg);
- return ret;
- }
-
sc.nid = nid;
sc.memcg = memcg;
sc.nr_to_scan = nr_to_scan;
@@ -159,7 +142,6 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
shrinker->scan_objects(shrinker, &sc);
- up_read(&shrinker_rwsem);
mem_cgroup_put(memcg);
return size;
@@ -198,10 +180,12 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
}
shrinker->debugfs_entry = entry;
- debugfs_create_file("count", 0440, entry, shrinker,
- &shrinker_debugfs_count_fops);
- debugfs_create_file("scan", 0220, entry, shrinker,
- &shrinker_debugfs_scan_fops);
+ if (shrinker->count_objects)
+ debugfs_create_file("count", 0440, entry, shrinker,
+ &shrinker_debugfs_count_fops);
+ if (shrinker->scan_objects)
+ debugfs_create_file("scan", 0220, entry, shrinker,
+ &shrinker_debugfs_scan_fops);
return 0;
}
diff --git a/mm/slab.h b/mm/slab.h
index 62df6eeeb5ea..cd55e4a7678d 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -734,14 +734,17 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s,
flags &= gfp_allowed_mask;
/*
- * For kmalloc object, the allocated memory size(object_size) is likely
- * larger than the requested size(orig_size). If redzone check is
- * enabled for the extra space, don't zero it, as it will be redzoned
- * soon. The redzone operation for this extra space could be seen as a
- * replacement of current poisoning under certain debug option, and
- * won't break other sanity checks.
+ * For kmalloc object, the allocated size (object_size) can be larger
+ * than the requested size (orig_size). We however need to zero the
+ * whole object_size to handle possible later krealloc() with
+ *__GFP_ZERO properly.
+ *
+ * But if we keep track of the requested size, krealloc() uses that
+ * information. Additionally if red zoning is enabled, the extra space
+ * is also red zone, so we should not overwrite it. So limit zeroing to
+ * orig_size if we track it.
*/
- if (kmem_cache_debug_flags(s, SLAB_STORE_USER | SLAB_RED_ZONE) &&
+ if (kmem_cache_debug_flags(s, SLAB_STORE_USER) &&
(s->flags & SLAB_KMALLOC))
zero_size = orig_size;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index aba757e5c597..8eb498351d9b 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2123,10 +2123,12 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
* Is there need to periodically free_folio_list? It would
* appear not as the counts should be low
*/
- if (unlikely(folio_test_large(folio)))
+ if (unlikely(folio_test_large(folio))) {
+ try_to_unmap_flush();
destroy_large_folio(folio);
- else
+ } else {
list_add(&folio->lru, &free_folios);
+ }
continue;
activate_locked_split:
diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
index 7b374595328d..a4dde85664f2 100644
--- a/net/6lowpan/nhc.c
+++ b/net/6lowpan/nhc.c
@@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sk_buff *skb,
return ret;
}
} else {
- spin_unlock_bh(&lowpan_nhc_lock);
netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
nhc->name);
+ spin_unlock_bh(&lowpan_nhc_lock);
return -ENOTSUPP;
}
} else {
diff --git a/net/9p/client.c b/net/9p/client.c
index c21ae8b8d649..6d9480ba9274 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -722,6 +722,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
if (err == -ERESTARTSYS && c->status == Connected &&
type == P9_TFLUSH) {
+ if (fatal_signal_pending(current))
+ goto recalc_sigpending;
sigpending = 1;
clear_thread_flag(TIF_SIGPENDING);
goto again;
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index b84748baf9cb..d9f53f1da2c2 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -240,25 +240,36 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
{
struct p9_client *c = id->context;
struct p9_trans_rdma *rdma = c->trans;
+ unsigned long flags;
+
switch (event->event) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
+ spin_lock_irqsave(&rdma->req_lock, flags);
BUG_ON(rdma->state != P9_RDMA_INIT);
rdma->state = P9_RDMA_ADDR_RESOLVED;
+ spin_unlock_irqrestore(&rdma->req_lock, flags);
break;
case RDMA_CM_EVENT_ROUTE_RESOLVED:
+ spin_lock_irqsave(&rdma->req_lock, flags);
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
rdma->state = P9_RDMA_ROUTE_RESOLVED;
+ spin_unlock_irqrestore(&rdma->req_lock, flags);
break;
case RDMA_CM_EVENT_ESTABLISHED:
+ spin_lock_irqsave(&rdma->req_lock, flags);
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
rdma->state = P9_RDMA_CONNECTED;
+ spin_unlock_irqrestore(&rdma->req_lock, flags);
break;
case RDMA_CM_EVENT_DISCONNECTED:
- if (rdma)
+ if (rdma) {
+ spin_lock_irqsave(&rdma->req_lock, flags);
rdma->state = P9_RDMA_CLOSED;
+ spin_unlock_irqrestore(&rdma->req_lock, flags);
+ }
c->status = Disconnected;
break;
@@ -296,6 +307,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
struct p9_req_t *req;
int err = 0;
int16_t tag;
+ unsigned long flags;
req = NULL;
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
@@ -332,7 +344,10 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
err_out:
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n",
req, err, wc->status);
- rdma->state = P9_RDMA_FLUSHING;
+ spin_lock_irqsave(&rdma->req_lock, flags);
+ if (rdma->state < P9_RDMA_FLUSHING)
+ rdma->state = P9_RDMA_FLUSHING;
+ spin_unlock_irqrestore(&rdma->req_lock, flags);
client->status = Disconnected;
goto out;
}
diff --git a/net/atm/common.c b/net/atm/common.c
index 48bb3f66a3f2..96f680a45e30 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -720,6 +720,8 @@ static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
static int check_tp(const struct atm_trafprm *tp)
{
/* @@@ Should be merged with adjust_tp */
+ if (tp->traffic_class > ATM_ANYCLASS)
+ return -EINVAL;
if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS)
return 0;
if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 92d90e352d1f..1ddce66bb047 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -546,7 +546,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
* the one with the lowest address
*/
if (tmp_max == max && max_orig_node &&
- batadv_compare_eth(candidate->orig, max_orig_node->orig))
+ memcmp(candidate->orig, max_orig_node->orig, ETH_ALEN) >= 0)
goto out;
ret = true;
@@ -1077,6 +1077,9 @@ static u16 batadv_arp_get_type(struct batadv_priv *bat_priv,
* @skb: the buffer containing the packet to extract the VID from
* @hdr_size: the size of the batman-adv header encapsulating the packet
*
+ * The caller must ensure that at least @hdr_size + ETH_HLEN bytes are
+ * accessible after skb->data.
+ *
* Return: If the packet embedded in the skb is vlan tagged this function
* returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS
* is returned.
@@ -1159,6 +1162,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
+ /* first, find out the vid. */
+ if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
+ goto out;
+
vid = batadv_dat_get_vid(skb, &hdr_size);
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
@@ -1254,6 +1261,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
+ /* first, find out the vid. */
+ if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
+ goto out;
+
vid = batadv_dat_get_vid(skb, &hdr_size);
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
@@ -1316,6 +1327,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->distributed_arp_table))
return;
+ /* first, find out the vid. */
+ if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
+ return;
+
vid = batadv_dat_get_vid(skb, &hdr_size);
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
@@ -1364,6 +1379,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
+ /* first, find out the vid. */
+ if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
+ goto out;
+
vid = batadv_dat_get_vid(skb, &hdr_size);
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
@@ -1758,6 +1777,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
struct ethhdr *ethhdr;
__be32 ip_src, yiaddr;
unsigned short vid;
+ int hdr_size_tmp;
__be16 proto;
u8 *hw_src;
@@ -1774,8 +1794,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
return;
+ hdr_size_tmp = hdr_size;
+ vid = batadv_dat_get_vid(skb, &hdr_size_tmp);
+ ethhdr = (struct ethhdr *)(skb->data + hdr_size);
hw_src = ethhdr->h_source;
- vid = batadv_dat_get_vid(skb, &hdr_size);
batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
@@ -1815,6 +1837,10 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
if (batadv_forw_packet_is_rebroadcast(forw_packet))
goto out;
+ /* first, find out the vid. */
+ if (!pskb_may_pull(forw_packet->skb, hdr_size + ETH_HLEN))
+ goto out;
+
vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size);
type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 0138b0953e10..fbb2fd767385 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -522,8 +522,10 @@ int batadv_frag_send_packet(struct sk_buff *skb,
mtu = min_t(unsigned int, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
max_fragment_size = mtu - header_size;
- if (skb->len == 0 || max_fragment_size == 0)
- return -EINVAL;
+ if (skb->len == 0 || max_fragment_size == 0) {
+ ret = -EINVAL;
+ goto free_skb;
+ }
num_fragments = (skb->len - 1) / max_fragment_size + 1;
max_fragment_size = (skb->len - 1) / num_fragments + 1;
@@ -549,7 +551,7 @@ int batadv_frag_send_packet(struct sk_buff *skb,
*/
if (skb_has_frag_list(skb) && __skb_linearize(skb)) {
ret = -ENOMEM;
- goto free_skb;
+ goto put_primary_if;
}
/* Create one header to be copied to all fragments */
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 003b03ca31b4..f62797b441e9 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -693,12 +693,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
struct batadv_gw_node *gw_node = NULL;
struct batadv_gw_node *curr_gw = NULL;
struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo;
- struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+ struct ethhdr *ethhdr;
bool out_of_range = false;
u8 curr_tq_avg;
unsigned short vid;
vid = batadv_get_vid(skb, 0);
+ ethhdr = (struct ethhdr *)skb->data;
if (is_multicast_ether_addr(ethhdr->h_dest))
goto out;
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 18b32c39ed4b..417de98c39a7 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -258,6 +258,7 @@ int batadv_mesh_init(struct net_device *soft_iface)
void batadv_mesh_free(struct net_device *soft_iface)
{
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+ struct batadv_softif_vlan *vlan;
atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
@@ -273,6 +274,13 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_mcast_free(bat_priv);
+ /* destroy the "untagged" VLAN */
+ vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+ if (vlan) {
+ batadv_softif_destroy_vlan(bat_priv, vlan);
+ batadv_softif_vlan_put(vlan);
+ }
+
/* Free the TT and the originator tables only after having terminated
* all the other depending components which may use these structures for
* their purposes.
@@ -375,7 +383,7 @@ void batadv_skb_set_priority(struct sk_buff *skb, int offset)
switch (ethhdr->h_proto) {
case htons(ETH_P_8021Q):
- vhdr = skb_header_pointer(skb, offset + sizeof(*vhdr),
+ vhdr = skb_header_pointer(skb, offset,
sizeof(*vhdr), &vhdr_tmp);
if (!vhdr)
return;
@@ -618,6 +626,9 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
* @skb: the buffer containing the packet
* @header_len: length of the batman header preceding the ethernet header
*
+ * The caller must ensure that at least @header_len + ETH_HLEN bytes are
+ * accessible after skb->data.
+ *
* Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the
* skb is vlan tagged. Otherwise BATADV_NO_FLAGS.
*/
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index ec278f73805e..8932c6c54663 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
if (skb_cow(skb, sizeof(*unicast_packet)) < 0)
return false;
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
vid = batadv_get_vid(skb, hdr_len);
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
ethhdr = (struct ethhdr *)(skb->data + hdr_len);
/* do not reroute multicast frames in a unicast header */
@@ -1035,6 +1035,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
hdr_size);
batadv_orig_node_put(orig_node_gw);
if (is_gw) {
+ orig_addr_gw = eth_hdr(skb)->h_source;
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): Dropped unicast pkt received from another backbone gw %pM.\n",
__func__, orig_addr_gw);
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 680f729b3b02..97578973943b 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -206,6 +206,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
+ if (!pskb_may_pull(skb, ETH_HLEN))
+ goto dropped;
+
/* reset control block to avoid left overs from previous users */
memset(skb->cb, 0, sizeof(struct batadv_skb_cb));
@@ -442,6 +445,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
if (!pskb_may_pull(skb, VLAN_ETH_HLEN))
goto dropped;
+ ethhdr = eth_hdr(skb);
vhdr = skb_vlan_eth_hdr(skb);
/* drop batman-in-batman packets to prevent loops */
@@ -599,8 +603,8 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
* @bat_priv: the bat priv with all the soft interface information
* @vlan: the object to remove
*/
-static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
- struct batadv_softif_vlan *vlan)
+void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan)
{
/* explicitly remove the associated TT local entry because it is marked
* with the NOPURGE flag
@@ -1082,22 +1086,13 @@ static int batadv_softif_newlink(struct net *src_net, struct net_device *dev,
static void batadv_softif_destroy_netlink(struct net_device *soft_iface,
struct list_head *head)
{
- struct batadv_priv *bat_priv = netdev_priv(soft_iface);
struct batadv_hard_iface *hard_iface;
- struct batadv_softif_vlan *vlan;
list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->soft_iface == soft_iface)
batadv_hardif_disable_interface(hard_iface);
}
- /* destroy the "untagged" VLAN */
- vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
- if (vlan) {
- batadv_softif_destroy_vlan(bat_priv, vlan);
- batadv_softif_vlan_put(vlan);
- }
-
unregister_netdevice_queue(soft_iface, head);
}
diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h
index 9f2003f1a497..13709a5d23e3 100644
--- a/net/batman-adv/soft-interface.h
+++ b/net/batman-adv/soft-interface.h
@@ -22,6 +22,8 @@ void batadv_interface_rx(struct net_device *soft_iface,
bool batadv_softif_is_valid(const struct net_device *net_dev);
extern struct rtnl_link_ops batadv_link_ops;
int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
+void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan);
void batadv_softif_vlan_release(struct kref *ref);
struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
unsigned short vid);
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 2a7caab5c197..7df77cca52a0 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -3032,7 +3032,7 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv,
out:
batadv_hardif_put(primary_if);
- if (ret && tt_req_node) {
+ if (!ret && tt_req_node) {
spin_lock_bh(&bat_priv->tt.req_list_lock);
if (!hlist_unhashed(&tt_req_node->list)) {
hlist_del_init(&tt_req_node->list);
@@ -4101,7 +4101,8 @@ static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
u16 tvlv_value_len)
{
struct batadv_tvlv_tt_data *tt_data;
- u16 tt_vlan_len, tt_num_entries;
+ u16 tt_num_entries;
+ size_t tt_vlan_len;
char tt_flag;
bool ret;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index b3132079573d..8625d3db2442 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -631,7 +631,7 @@ static struct l2cap_chan *chan_create(void)
if (!chan)
return NULL;
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
@@ -744,19 +744,24 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
ifup(dev->netdev);
}
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
+static void unregister_dev(struct lowpan_btle_dev *dev)
{
- struct l2cap_chan *chan;
-
- chan = chan_create();
- if (!chan)
- return NULL;
-
- chan->ops = pchan->ops;
-
- BT_DBG("chan %p pchan %p", chan, pchan);
-
- return chan;
+ struct hci_dev *hdev = READ_ONCE(dev->hdev);
+
+ /* If netdev holds last reference to hci_dev (its parent device), this
+ * leads to theoretical cyclic locking on lowpan_unregister_netdev:
+ *
+ * rtnl_lock -> put_device(parent) -> hci_release_dev ->
+ * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata ->
+ * chan_ready_cb -> ifup -> rtnl_lock
+ *
+ * However, hci_rx_work is disabled in hci_unregister_dev, so this
+ * should not occur. Make lockdep happy by postponing hdev release after
+ * netdev put.
+ */
+ hci_dev_hold(hdev);
+ lowpan_unregister_netdev(dev->netdev);
+ hci_dev_put(hdev);
}
static void delete_netdev(struct work_struct *work)
@@ -765,7 +770,7 @@ static void delete_netdev(struct work_struct *work)
struct lowpan_btle_dev,
delete_netdev);
- lowpan_unregister_netdev(entry->netdev);
+ unregister_dev(entry);
/* The entry pointer is deleted by the netdev destructor. */
}
@@ -776,20 +781,10 @@ static void chan_close_cb(struct l2cap_chan *chan)
struct lowpan_btle_dev *dev = NULL;
struct lowpan_peer *peer;
int err = -ENOENT;
- bool last = false, remove = true;
+ bool last = false;
BT_DBG("chan %p conn %p", chan, chan->conn);
- if (chan->conn && chan->conn->hcon) {
- if (!is_bt_6lowpan(chan->conn->hcon))
- return;
-
- /* If conn is set, then the netdev is also there and we should
- * not remove it.
- */
- remove = false;
- }
-
spin_lock(&devices_lock);
list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
@@ -816,10 +811,8 @@ static void chan_close_cb(struct l2cap_chan *chan)
ifdown(dev->netdev);
- if (remove) {
- INIT_WORK(&entry->delete_netdev, delete_netdev);
- schedule_work(&entry->delete_netdev);
- }
+ INIT_WORK(&entry->delete_netdev, delete_netdev);
+ schedule_work(&entry->delete_netdev);
} else {
spin_unlock(&devices_lock);
}
@@ -880,7 +873,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
static const struct l2cap_ops bt_6lowpan_chan_ops = {
.name = "L2CAP 6LoWPAN channel",
- .new_connection = chan_new_conn_cb,
.recv = chan_recv_cb,
.close = chan_close_cb,
.state_change = chan_state_change_cb,
@@ -1006,16 +998,19 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
hci_dev_lock(hdev);
hcon = hci_conn_hash_lookup_le(hdev, addr, le_addr_type);
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
- if (!hcon)
+ if (!hcon) {
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
return -ENOENT;
+ }
- *conn = (struct l2cap_conn *)hcon->l2cap_data;
+ *conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
BT_DBG("conn %p dst %pMR type %u", *conn, &hcon->dst, hcon->dst_type);
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
return 0;
}
@@ -1158,18 +1153,22 @@ static ssize_t lowpan_control_write(struct file *fp,
if (conn) {
struct lowpan_peer *peer;
- if (!is_bt_6lowpan(conn->hcon))
+ if (!is_bt_6lowpan(conn->hcon)) {
+ l2cap_conn_put(conn);
return -EINVAL;
+ }
peer = lookup_peer(conn);
if (peer) {
BT_DBG("6LoWPAN connection already exists");
+ l2cap_conn_put(conn);
return -EALREADY;
}
BT_DBG("conn %p dst %pMR type %d user %u", conn,
&conn->hcon->dst, conn->hcon->dst_type,
addr_type);
+ l2cap_conn_put(conn);
}
ret = bt_6lowpan_connect(&addr, addr_type);
@@ -1185,6 +1184,8 @@ static ssize_t lowpan_control_write(struct file *fp,
return ret;
ret = bt_6lowpan_disconnect(conn, addr_type);
+ if (conn)
+ l2cap_conn_put(conn);
if (ret < 0)
return ret;
@@ -1245,6 +1246,7 @@ static void disconnect_devices(void)
break;
new_dev->netdev = entry->netdev;
+ new_dev->hdev = entry->hdev;
INIT_LIST_HEAD(&new_dev->list);
list_add_rcu(&new_dev->list, &devices);
@@ -1256,7 +1258,7 @@ static void disconnect_devices(void)
ifdown(entry->netdev);
BT_DBG("Unregistering netdev %s %p",
entry->netdev->name, entry->netdev);
- lowpan_unregister_netdev(entry->netdev);
+ unregister_dev(entry);
kfree(entry);
}
}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 53a4792ae75d..3233736cc8a7 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
restart:
for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
- /* Prevent early freeing of sk due to unlink and sock_kill */
+ /* The reference from bt_accept_get() keeps sk alive. */
lock_sock(sk);
/* Check sk has not already been unlinked via
@@ -321,13 +321,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
next = bt_accept_get(parent, sk);
- /* sk is safely in the parent list so reduce reference count */
- sock_put(sk);
-
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
bt_accept_unlink(sk);
release_sock(sk);
+ sock_put(sk);
continue;
}
@@ -337,16 +335,6 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
if (newsock)
sock_graft(sk, newsock);
- /* Hand the caller a reference taken while sk is
- * still locked. bt_accept_unlink() just dropped
- * the accept-queue reference; without this hold a
- * concurrent teardown (e.g. l2cap_conn_del() ->
- * l2cap_sock_kill()) could free sk between
- * release_sock() and the caller using it. Every
- * caller drops this with sock_put() when done.
- */
- sock_hold(sk);
-
release_sock(sk);
if (next)
sock_put(next);
@@ -354,6 +342,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
}
release_sock(sk);
+ sock_put(sk);
}
return NULL;
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 45d0f84652fc..a691d228f66c 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -562,14 +562,18 @@ static int bnep_session(void *arg)
return 0;
}
-static struct device *bnep_get_device(struct bnep_session *session)
+static struct l2cap_conn *bnep_get_conn(struct bnep_session *session)
{
- struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
+ struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan;
+ struct l2cap_conn *conn;
- if (!conn || !conn->hcon)
- return NULL;
+ l2cap_chan_lock(chan);
+ conn = chan->conn;
+ if (conn)
+ l2cap_conn_get(conn);
+ l2cap_chan_unlock(chan);
- return &conn->hcon->dev;
+ return conn;
}
static struct device_type bnep_type = {
@@ -581,6 +585,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
struct net_device *dev;
struct bnep_session *s, *ss;
+ struct l2cap_conn *conn = NULL;
u8 dst[ETH_ALEN], src[ETH_ALEN];
int err;
@@ -640,10 +645,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
bnep_set_default_proto_filter(s);
#endif
- SET_NETDEV_DEV(dev, bnep_get_device(s));
+ conn = bnep_get_conn(s);
+ if (!conn) {
+ err = -ENOTCONN;
+ goto failed;
+ }
+
+ SET_NETDEV_DEV(dev, &conn->hcon->dev);
SET_NETDEV_DEVTYPE(dev, &bnep_type);
err = register_netdev(dev);
+ l2cap_conn_put(conn);
+ conn = NULL;
if (err)
goto failed;
@@ -665,6 +678,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
return 0;
failed:
+ if (conn)
+ l2cap_conn_put(conn);
up_write(&bnep_session_sem);
free_netdev(dev);
return err;
diff --git a/net/bluetooth/hci_codec.c b/net/bluetooth/hci_codec.c
index 3cc135bb1d30..5bc5003c387c 100644
--- a/net/bluetooth/hci_codec.c
+++ b/net/bluetooth/hci_codec.c
@@ -100,7 +100,7 @@ static void hci_read_codec_capabilities(struct hci_dev *hdev, __u8 transport,
caps = (void *)skb->data;
if (skb->len < sizeof(*caps))
goto error;
- if (skb->len < caps->len)
+ if (skb->len < sizeof(caps->len) + caps->len)
goto error;
len += sizeof(caps->len) + caps->len;
skb_pull(skb, sizeof(caps->len) + caps->len);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index ab86cc4a5e3f..19ca5016372a 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -870,9 +870,11 @@ static void cis_cleanup(struct hci_conn *conn)
/* Check if ISO connection is a CIS and remove CIG if there are
* no other connections using it.
*/
- hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
- hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
- hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
+ hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_BOUND, &d);
+ hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECT,
+ &d);
+ hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECTED,
+ &d);
if (d.count)
return;
@@ -895,7 +897,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
if (!hdev->acl_mtu)
return ERR_PTR(-ECONNREFUSED);
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
if (hdev->iso_mtu)
/* Dedicated ISO Buffer exists */
break;
@@ -959,7 +962,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
/* conn->src should reflect the local identity address */
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
@@ -1055,7 +1059,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
if (HCI_CONN_HANDLE_UNSET(conn->handle))
hci_conn_failed(conn, reason);
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
if ((conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
@@ -1130,7 +1135,8 @@ void hci_conn_del(struct hci_conn *conn)
hdev->acl_cnt += conn->sent;
} else {
/* Unacked ISO frames */
- if (conn->type == ISO_LINK) {
+ if (conn->type == CIS_LINK ||
+ conn->type == BIS_LINK) {
if (hdev->iso_pkts)
hdev->iso_cnt += conn->sent;
else if (hdev->le_pkts)
@@ -1501,7 +1507,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
memcmp(conn->le_per_adv_data, base, base_len)))
return ERR_PTR(-EADDRINUSE);
- conn = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
+ conn = hci_conn_add_unset(hdev, BIS_LINK, dst, HCI_ROLE_MASTER);
if (IS_ERR(conn))
return conn;
@@ -1707,7 +1713,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
data.count = 0;
/* Create a BIS for each bound connection */
- hci_conn_hash_list_state(hdev, bis_list, ISO_LINK,
+ hci_conn_hash_list_state(hdev, bis_list, BIS_LINK,
BT_BOUND, &data);
cp.handle = qos->bcast.big;
@@ -1803,12 +1809,12 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
data.count = 0;
- hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
+ hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECT, &data);
if (data.count)
continue;
- hci_conn_hash_list_state(hdev, find_cis, ISO_LINK,
+ hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECTED, &data);
if (!data.count)
break;
@@ -1860,7 +1866,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type, qos->ucast.cig,
qos->ucast.cis);
if (!cis) {
- cis = hci_conn_add_unset(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
+ cis = hci_conn_add_unset(hdev, CIS_LINK, dst,
+ HCI_ROLE_MASTER);
if (IS_ERR(cis))
return cis;
cis->cleanup = cis_cleanup;
@@ -1950,7 +1957,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)
int hci_conn_check_create_cis(struct hci_conn *conn)
{
- if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY))
+ if (conn->type != CIS_LINK)
return -EINVAL;
if (!conn->parent || conn->parent->state != BT_CONNECTED ||
@@ -2248,7 +2255,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
* the start periodic advertising and create BIG commands have
* been queued
*/
- hci_conn_hash_list_state(hdev, bis_mark_per_adv, ISO_LINK,
+ hci_conn_hash_list_state(hdev, bis_mark_per_adv, BIS_LINK,
BT_BOUND, &data);
/* Queue start periodic advertising and create BIG */
@@ -2903,26 +2910,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
conn->abort_reason = reason;
- /* If the connection is pending check the command opcode since that
- * might be blocking on hci_cmd_sync_work while waiting its respective
- * event so we need to hci_cmd_sync_cancel to cancel it.
- *
- * hci_connect_le serializes the connection attempts so only one
- * connection can be in BT_CONNECT at time.
+ /* Cancel the connect attempt. A return of 0 means the create command
+ * was still queued and got dequeued, so there is nothing to disconnect.
*/
- if (conn->state == BT_CONNECT && hdev->req_status == HCI_REQ_PEND) {
- switch (hci_skb_event(hdev->sent_cmd)) {
- case HCI_EV_CONN_COMPLETE:
- case HCI_EV_LE_CONN_COMPLETE:
- case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
- case HCI_EVT_LE_CIS_ESTABLISHED:
- hci_cmd_sync_cancel(hdev, ECANCELED);
- break;
- }
- /* Cancel connect attempt if still queued/pending */
- } else if (!hci_cancel_connect_sync(hdev, conn)) {
+ if (!hci_cancel_connect_sync(hdev, conn))
return 0;
- }
/* Run immediately if on cmd_sync_work since this may be called
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 824208a53c25..d9bb05f81f50 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2907,12 +2907,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_ACLDATA_PKT:
/* Detect if ISO packet has been sent as ACL */
- if (hci_conn_num(hdev, ISO_LINK)) {
+ if (hci_conn_num(hdev, CIS_LINK) ||
+ hci_conn_num(hdev, BIS_LINK)) {
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
__u8 type;
type = hci_conn_lookup_type(hdev, hci_handle(handle));
- if (type == ISO_LINK)
+ if (type == CIS_LINK || type == BIS_LINK)
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
}
break;
@@ -3347,7 +3348,8 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
case LE_LINK:
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
cnt = hdev->iso_mtu ? hdev->iso_cnt :
hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
break;
@@ -3361,7 +3363,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
}
static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
- int *quote)
+ __u8 type2, int *quote)
{
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL, *c;
@@ -3373,7 +3375,8 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
- if (c->type != type || skb_queue_empty(&c->data_q))
+ if ((c->type != type && c->type != type2) ||
+ skb_queue_empty(&c->data_q))
continue;
if (c->state != BT_CONNECTED && c->state != BT_CONFIG)
@@ -3556,42 +3559,27 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type)
}
/* Schedule SCO */
-static void hci_sched_sco(struct hci_dev *hdev)
+static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
{
struct hci_conn *conn;
struct sk_buff *skb;
- int quote;
+ int quote, *cnt;
+ unsigned int pkts = hdev->sco_pkts;
- BT_DBG("%s", hdev->name);
+ bt_dev_dbg(hdev, "type %u", type);
- if (!hci_conn_num(hdev, SCO_LINK))
+ if (!hci_conn_num(hdev, type) || !pkts)
return;
- while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
- BT_DBG("skb %p len %d", skb, skb->len);
- hci_send_frame(hdev, skb);
-
- conn->sent++;
- if (conn->sent == ~0)
- conn->sent = 0;
- }
- }
-}
-
-static void hci_sched_esco(struct hci_dev *hdev)
-{
- struct hci_conn *conn;
- struct sk_buff *skb;
- int quote;
-
- BT_DBG("%s", hdev->name);
-
- if (!hci_conn_num(hdev, ESCO_LINK))
- return;
+ /* Use sco_pkts if flow control has not been enabled which will limit
+ * the amount of buffer sent in a row.
+ */
+ if (!hci_dev_test_flag(hdev, HCI_SCO_FLOWCTL))
+ cnt = &pkts;
+ else
+ cnt = &hdev->sco_cnt;
- while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
- "e))) {
+ while (*cnt && (conn = hci_low_sent(hdev, type, type, "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(hdev, skb);
@@ -3599,8 +3587,17 @@ static void hci_sched_esco(struct hci_dev *hdev)
conn->sent++;
if (conn->sent == ~0)
conn->sent = 0;
+ (*cnt)--;
}
}
+
+ /* Rescheduled if all packets were sent and flow control is not enabled
+ * as there could be more packets queued that could not be sent and
+ * since no HCI_EV_NUM_COMP_PKTS event will be generated the reschedule
+ * needs to be forced.
+ */
+ if (!pkts && !hci_dev_test_flag(hdev, HCI_SCO_FLOWCTL))
+ queue_work(hdev->workqueue, &hdev->tx_work);
}
static void hci_sched_acl_pkt(struct hci_dev *hdev)
@@ -3636,8 +3633,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
- hci_sched_sco(hdev);
- hci_sched_esco(hdev);
+ hci_sched_sco(hdev, SCO_LINK);
+ hci_sched_sco(hdev, ESCO_LINK);
}
}
@@ -3692,8 +3689,8 @@ static void hci_sched_le(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
- hci_sched_sco(hdev);
- hci_sched_esco(hdev);
+ hci_sched_sco(hdev, SCO_LINK);
+ hci_sched_sco(hdev, ESCO_LINK);
}
}
@@ -3710,12 +3707,14 @@ static void hci_sched_iso(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
- if (!hci_conn_num(hdev, ISO_LINK))
+ if (!hci_conn_num(hdev, CIS_LINK) &&
+ !hci_conn_num(hdev, BIS_LINK))
return;
cnt = hdev->iso_pkts ? &hdev->iso_cnt :
hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
- while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, "e))) {
+ while (*cnt && (conn = hci_low_sent(hdev, CIS_LINK, BIS_LINK,
+ "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(hdev, skb);
@@ -3738,8 +3737,8 @@ static void hci_tx_work(struct work_struct *work)
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
/* Schedule queues and send stuff to HCI driver */
- hci_sched_sco(hdev);
- hci_sched_esco(hdev);
+ hci_sched_sco(hdev, SCO_LINK);
+ hci_sched_sco(hdev, ESCO_LINK);
hci_sched_iso(hdev);
hci_sched_acl(hdev);
hci_sched_le(hdev);
@@ -4061,7 +4060,7 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
return;
}
- if (hci_req_status_pend(hdev) &&
+ if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND &&
!hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) {
kfree_skb(hdev->req_skb);
hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 4607db3037bd..14caf945aadd 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -43,8 +43,6 @@
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00"
-#define secs_to_jiffies(_secs) msecs_to_jiffies((_secs) * 1000)
-
/* Handle HCI Event packets */
static void *hci_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
@@ -3776,7 +3774,7 @@ static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)
lockdep_assert_held(&hdev->lock);
list_for_each_entry_safe(conn, tmp, &hdev->conn_hash.list, list) {
- if (conn->type != ISO_LINK || !bacmp(&conn->dst, BDADDR_ANY) ||
+ if (conn->type != CIS_LINK ||
conn->state == BT_OPEN || conn->iso_qos.ucast.cig != cig)
continue;
@@ -4445,12 +4443,15 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
break;
case SCO_LINK:
+ case ESCO_LINK:
hdev->sco_cnt += count;
if (hdev->sco_cnt > hdev->sco_pkts)
hdev->sco_cnt = hdev->sco_pkts;
+
break;
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
if (hdev->iso_pkts) {
hdev->iso_cnt += count;
if (hdev->iso_cnt > hdev->iso_pkts)
@@ -6382,7 +6383,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
- mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags);
+ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, BIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_pa_term_sync(hdev, ev->handle);
goto unlock;
@@ -6393,7 +6394,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
if (ev->status) {
/* Add connection to indicate the failed PA sync event */
- pa_sync = hci_conn_add_unset(hdev, ISO_LINK, BDADDR_ANY,
+ pa_sync = hci_conn_add_unset(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE);
if (!pa_sync)
@@ -6420,7 +6421,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
- mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
+ mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT))
hci_le_pa_term_sync(hdev, ev->sync_handle);
@@ -6691,7 +6692,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
goto unlock;
}
- if (conn->type != ISO_LINK) {
+ if (conn->type != CIS_LINK) {
bt_dev_err(hdev,
"Invalid connection link type handle 0x%4.4x",
handle);
@@ -6809,7 +6810,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
if (!acl)
goto unlock;
- mask = hci_proto_connect_ind(hdev, &acl->dst, ISO_LINK, &flags);
+ mask = hci_proto_connect_ind(hdev, &acl->dst, CIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
@@ -6817,8 +6818,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
if (!cis) {
- cis = hci_conn_add(hdev, ISO_LINK, &acl->dst, HCI_ROLE_SLAVE,
- cis_handle);
+ cis = hci_conn_add(hdev, CIS_LINK, &acl->dst,
+ HCI_ROLE_SLAVE, cis_handle);
if (IS_ERR(cis)) {
hci_le_reject_cis(hdev, ev->cis_handle);
goto unlock;
@@ -6952,7 +6953,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "ignore too large handle %u", handle);
continue;
}
- bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
+ bis = hci_conn_add(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE, handle);
if (IS_ERR(bis))
continue;
@@ -7009,7 +7010,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
- mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
+ mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, BIS_LINK, &flags);
if (!(mask & HCI_LM_ACCEPT)) {
hci_le_pa_term_sync(hdev, ev->sync_handle);
goto unlock;
@@ -7026,7 +7027,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
goto unlock;
/* Add connection to indicate the PA sync event */
- pa_sync = hci_conn_add_unset(hdev, ISO_LINK, BDADDR_ANY,
+ pa_sync = hci_conn_add_unset(hdev, BIS_LINK, BDADDR_ANY,
HCI_ROLE_SLAVE);
if (IS_ERR(pa_sync))
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 9da2acec6f7f..7c4f89fee157 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -25,11 +25,11 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
{
bt_dev_dbg(hdev, "result 0x%2.2x", result);
- if (hdev->req_status != HCI_REQ_PEND)
+ if (READ_ONCE(hdev->req_status) != HCI_REQ_PEND)
return;
hdev->req_result = result;
- hdev->req_status = HCI_REQ_DONE;
+ WRITE_ONCE(hdev->req_status, HCI_REQ_DONE);
/* Free the request command so it is not used as response */
kfree_skb(hdev->req_skb);
@@ -168,20 +168,20 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
hci_cmd_sync_add(&req, opcode, plen, param, event, sk);
- hdev->req_status = HCI_REQ_PEND;
+ WRITE_ONCE(hdev->req_status, HCI_REQ_PEND);
err = hci_req_sync_run(&req);
if (err < 0)
return ERR_PTR(err);
err = wait_event_interruptible_timeout(hdev->req_wait_q,
- hdev->req_status != HCI_REQ_PEND,
+ READ_ONCE(hdev->req_status) != HCI_REQ_PEND,
timeout);
if (err == -ERESTARTSYS)
return ERR_PTR(-EINTR);
- switch (hdev->req_status) {
+ switch (READ_ONCE(hdev->req_status)) {
case HCI_REQ_DONE:
err = -bt_to_errno(hdev->req_result);
break;
@@ -195,7 +195,7 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
break;
}
- hdev->req_status = 0;
+ WRITE_ONCE(hdev->req_status, 0);
hdev->req_result = 0;
skb = hdev->req_rsp;
hdev->req_rsp = NULL;
@@ -689,9 +689,9 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
{
bt_dev_dbg(hdev, "err 0x%2.2x", err);
- if (hdev->req_status == HCI_REQ_PEND) {
+ if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
hdev->req_result = err;
- hdev->req_status = HCI_REQ_CANCELED;
+ WRITE_ONCE(hdev->req_status, HCI_REQ_CANCELED);
queue_work(hdev->workqueue, &hdev->cmd_sync_cancel_work);
}
@@ -707,12 +707,12 @@ void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err)
{
bt_dev_dbg(hdev, "err 0x%2.2x", err);
- if (hdev->req_status == HCI_REQ_PEND) {
+ if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
/* req_result is __u32 so error must be positive to be properly
* propagated.
*/
hdev->req_result = err < 0 ? -err : err;
- hdev->req_status = HCI_REQ_CANCELED;
+ WRITE_ONCE(hdev->req_status, HCI_REQ_CANCELED);
wake_up_interruptible(&hdev->req_wait_q);
}
@@ -2971,7 +2971,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
if (sent) {
struct hci_conn *conn;
- conn = hci_conn_hash_lookup_ba(hdev, ISO_LINK,
+ conn = hci_conn_hash_lookup_ba(hdev, BIS_LINK,
&sent->bdaddr);
if (conn) {
struct bt_iso_qos *qos = &conn->iso_qos;
@@ -3877,6 +3877,28 @@ static int hci_write_ca_timeout_sync(struct hci_dev *hdev)
sizeof(param), ¶m, HCI_CMD_TIMEOUT);
}
+/* Enable SCO flow control if supported */
+static int hci_write_sync_flowctl_sync(struct hci_dev *hdev)
+{
+ struct hci_cp_write_sync_flowctl cp;
+ int err;
+
+ /* Check if the controller supports SCO and HCI_OP_WRITE_SYNC_FLOWCTL */
+ if (!lmp_sco_capable(hdev) || !(hdev->commands[10] & BIT(4)) ||
+ !test_bit(HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED, &hdev->quirks))
+ return 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = 0x01;
+
+ err = __hci_cmd_sync_status(hdev, HCI_OP_WRITE_SYNC_FLOWCTL,
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
+ if (!err)
+ hci_dev_set_flag(hdev, HCI_SCO_FLOWCTL);
+
+ return err;
+}
+
/* BR Controller init stage 2 command sequence */
static const struct hci_init_stage br_init2[] = {
/* HCI_OP_READ_BUFFER_SIZE */
@@ -3895,6 +3917,8 @@ static const struct hci_init_stage br_init2[] = {
HCI_INIT(hci_clear_event_filter_sync),
/* HCI_OP_WRITE_CA_TIMEOUT */
HCI_INIT(hci_write_ca_timeout_sync),
+ /* HCI_OP_WRITE_SYNC_FLOWCTL */
+ HCI_INIT(hci_write_sync_flowctl_sync),
{}
};
@@ -5505,7 +5529,7 @@ static int hci_disconnect_sync(struct hci_dev *hdev, struct hci_conn *conn,
{
struct hci_cp_disconnect cp;
- if (test_bit(HCI_CONN_BIG_CREATED, &conn->flags)) {
+ if (conn->type == BIS_LINK) {
/* This is a BIS connection, hci_conn_del will
* do the necessary cleanup.
*/
@@ -5558,7 +5582,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
if (conn->type == LE_LINK)
return hci_le_connect_cancel_sync(hdev, conn, reason);
- if (conn->type == ISO_LINK) {
+ if (conn->type == CIS_LINK) {
/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
* page 1857:
*
@@ -5571,9 +5595,10 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,
return hci_disconnect_sync(hdev, conn, reason);
/* CIS with no Create CIS sent have nothing to cancel */
- if (bacmp(&conn->dst, BDADDR_ANY))
- return HCI_ERROR_LOCAL_HOST_TERM;
+ return HCI_ERROR_LOCAL_HOST_TERM;
+ }
+ if (conn->type == BIS_LINK) {
/* There is no way to cancel a BIS without terminating the BIG
* which is done later on connection cleanup.
*/
@@ -5635,9 +5660,12 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
{
struct hci_cp_reject_conn_req cp;
- if (conn->type == ISO_LINK)
+ if (conn->type == CIS_LINK)
return hci_le_reject_cis_sync(hdev, conn, reason);
+ if (conn->type == BIS_LINK)
+ return -EINVAL;
+
if (conn->type == SCO_LINK || conn->type == ESCO_LINK)
return hci_reject_sco_sync(hdev, conn, reason);
@@ -6529,6 +6557,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
bt_dev_dbg(hdev, "conn %p", conn);
+ /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+ * clear_bit() at done.
+ */
+ hci_conn_get(conn);
+
clear_bit(HCI_CONN_SCANNING, &conn->flags);
conn->state = BT_CONNECT;
@@ -6541,6 +6574,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
hdev->le_scan_type == LE_SCAN_ACTIVE &&
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
hci_conn_del(conn);
+ hci_conn_put(conn);
return -EBUSY;
}
@@ -6586,6 +6620,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
&own_addr_type);
if (err)
goto done;
+
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
/* Send command LE Extended Create Connection if supported */
if (use_ext_conn(hdev)) {
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6621,11 +6661,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
done:
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn, 0x00);
/* Re-enable advertising after the connection attempt is finished. */
hci_resume_advertising_sync(hdev);
+ hci_conn_put(conn);
return err;
}
@@ -6904,10 +6947,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
else
cp.role_switch = 0x00;
- return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
- sizeof(cp), &cp,
- HCI_EV_CONN_COMPLETE,
- conn->conn_timeout, NULL);
+ /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
+ * clear_bit() below.
+ */
+ hci_conn_get(conn);
+
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
+ err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
+ sizeof(cp), &cp,
+ HCI_EV_CONN_COMPLETE,
+ conn->conn_timeout, NULL);
+
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+ hci_conn_put(conn);
+
+ return err;
}
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -6953,20 +7011,95 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
create_le_conn_complete);
}
-int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
+static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
+ struct hci_conn *conn)
{
- if (conn->state != BT_OPEN)
- return -EINVAL;
+ struct hci_cmd_sync_work_entry *entry;
+ int err = -EBUSY;
+
+ /* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel
+ * atomic against the worker, which takes this lock to dequeue every
+ * entry: while it is held no other command can become pending, so
+ * hci_cmd_sync_cancel() cannot cancel an unrelated command.
+ */
+ mutex_lock(&hdev->cmd_sync_work_lock);
+
+ /* In flight: this connection owns the pending request, cancel it. */
+ if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+ goto unlock;
+ }
+ /* Still queued: a successful dequeue means it never started, so there
+ * is nothing to disconnect.
+ */
+ entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn,
+ NULL);
+ if (entry) {
+ _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
+ err = 0;
+ }
+
+unlock:
+ mutex_unlock(&hdev->cmd_sync_work_lock);
+ return err;
+}
+
+static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
+ struct hci_conn *conn)
+{
+ struct hci_cmd_sync_work_entry *entry;
+ int err = -EBUSY;
+
+ /* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel
+ * atomic against the cmd_sync worker.
+ */
+ mutex_lock(&hdev->cmd_sync_work_lock);
+
+ if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+ goto unlock;
+ }
+
+ entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn,
+ create_le_conn_complete);
+ if (entry) {
+ _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
+ err = 0;
+ }
+
+unlock:
+ mutex_unlock(&hdev->cmd_sync_work_lock);
+ return err;
+}
+
+static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
+ struct hci_conn *conn)
+{
+ /* LE Create CIS is shared by the whole CIG and cannot be dequeued
+ * per-connection, so only an in-flight command can be cancelled.
+ * cmd_sync_work_lock keeps the test and the cancel atomic against the
+ * cmd_sync worker.
+ */
+ mutex_lock(&hdev->cmd_sync_work_lock);
+
+ if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+
+ mutex_unlock(&hdev->cmd_sync_work_lock);
+ return -EBUSY;
+}
+
+int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
+{
switch (conn->type) {
case ACL_LINK:
- return !hci_cmd_sync_dequeue_once(hdev,
- hci_acl_create_conn_sync,
- conn, NULL);
+ return hci_acl_cancel_create_conn_sync(hdev, conn);
case LE_LINK:
- return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
- conn, create_le_conn_complete);
+ return hci_le_cancel_create_conn_sync(hdev, conn);
+ case CIS_LINK:
+ return hci_cis_cancel_create_conn_sync(hdev, conn);
+ default:
+ return -ENOENT;
}
-
- return -ENOENT;
}
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 011b2187bd85..189b71966992 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -14,6 +14,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/iso.h>
+#include "eir.h"
static const struct proto_ops iso_sock_ops;
@@ -47,6 +48,7 @@ static void iso_sock_kill(struct sock *sk);
#define EIR_SERVICE_DATA_LENGTH 4
#define BASE_MAX_LENGTH (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH)
+#define EIR_BAA_SERVICE_UUID 0x1851
/* iso_pinfo flags values */
enum {
@@ -1587,6 +1589,8 @@ static int iso_sock_getsockopt(struct socket *sock, int level, int optname,
len = min_t(unsigned int, len, base_len);
if (copy_to_user(optval, base, len))
err = -EFAULT;
+ if (put_user(len, optlen))
+ err = -EFAULT;
break;
@@ -1928,12 +1932,16 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
ev3 = hci_recv_event_data(hdev, HCI_EV_LE_PER_ADV_REPORT);
if (ev3) {
+ size_t base_len = ev3->length;
+ u8 *base;
+
sk = iso_get_sock_listen(&hdev->bdaddr, bdaddr,
iso_match_sync_handle_pa_report, ev3);
-
- if (sk) {
- memcpy(iso_pi(sk)->base, ev3->data, ev3->length);
- iso_pi(sk)->base_len = ev3->length;
+ base = eir_get_service_data(ev3->data, ev3->length,
+ EIR_BAA_SERVICE_UUID, &base_len);
+ if (base && sk && base_len <= sizeof(iso_pi(sk)->base)) {
+ memcpy(iso_pi(sk)->base, base, base_len);
+ iso_pi(sk)->base_len = base_len;
}
} else {
sk = iso_get_sock_listen(&hdev->bdaddr, BDADDR_ANY, NULL, NULL);
@@ -1955,7 +1963,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
{
- if (hcon->type != ISO_LINK) {
+ if (hcon->type != CIS_LINK && hcon->type != BIS_LINK) {
if (hcon->type != LE_LINK)
return;
@@ -1996,7 +2004,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{
- if (hcon->type != ISO_LINK)
+ if (hcon->type != CIS_LINK && hcon->type != BIS_LINK)
return;
BT_DBG("hcon %p reason %d", hcon, reason);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3426eedf20ad..792974eef47c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -411,7 +411,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
- if (!conn) {
+ if (test_bit(FLAG_DEL, &chan->flags)) {
l2cap_chan_put(chan);
return;
}
@@ -422,6 +422,9 @@ static void l2cap_chan_timeout(struct work_struct *work)
*/
l2cap_chan_lock(chan);
+ if (test_bit(FLAG_DEL, &chan->flags))
+ goto unlock;
+
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
else if (chan->state == BT_CONNECT &&
@@ -434,10 +437,10 @@ static void l2cap_chan_timeout(struct work_struct *work)
chan->ops->close(chan);
+unlock:
l2cap_chan_unlock(chan);
- l2cap_chan_put(chan);
-
mutex_unlock(&conn->lock);
+ l2cap_chan_put(chan);
}
struct l2cap_chan *l2cap_chan_create(void)
@@ -490,6 +493,9 @@ static void l2cap_chan_destroy(struct kref *kref)
list_del(&chan->global_l);
write_unlock(&chan_list_lock);
+ if (chan->conn)
+ l2cap_conn_put(chan->conn);
+
kfree(chan);
}
@@ -519,7 +525,10 @@ void l2cap_chan_put(struct l2cap_chan *c)
}
EXPORT_SYMBOL_GPL(l2cap_chan_put);
-void l2cap_chan_set_defaults(struct l2cap_chan *chan)
+/* Initialise @chan with default values, inheriting from the parent channel
+ * @pchan when it is given.
+ */
+void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
{
chan->fcs = L2CAP_FCS_CRC16;
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
@@ -533,6 +542,31 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+ if (pchan) {
+ BT_DBG("chan %p pchan %p", chan, pchan);
+
+ chan->chan_type = pchan->chan_type;
+ chan->imtu = pchan->imtu;
+ chan->omtu = pchan->omtu;
+ chan->mode = pchan->mode;
+ chan->fcs = pchan->fcs;
+ chan->max_tx = pchan->max_tx;
+ chan->tx_win = pchan->tx_win;
+ chan->tx_win_max = pchan->tx_win_max;
+ chan->sec_level = pchan->sec_level;
+ chan->conf_state = pchan->conf_state;
+ chan->flags = pchan->flags;
+ chan->tx_credits = pchan->tx_credits;
+ chan->rx_credits = pchan->rx_credits;
+
+ if (chan->chan_type == L2CAP_CHAN_FIXED) {
+ chan->scid = pchan->scid;
+ chan->dcid = pchan->scid;
+ }
+
+ return;
+ }
+
chan->conf_state = 0;
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
@@ -593,7 +627,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
- chan->conn = conn;
+ chan->conn = l2cap_conn_get(conn);
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
@@ -648,30 +682,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
- struct l2cap_conn *conn = chan->conn;
-
__clear_chan_timer(chan);
- BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
+ BT_DBG("chan %p, err %d, state %s", chan, err,
state_to_string(chan->state));
chan->ops->teardown(chan, err);
- if (conn) {
+ if (!test_and_set_bit(FLAG_DEL, &chan->flags)) {
/* Delete from channel list */
list_del(&chan->list);
l2cap_chan_put(chan);
- chan->conn = NULL;
-
/* Reference was only held for non-fixed channels or
* fixed channels that explicitly requested it using the
* FLAG_HOLD_HCI_CONN flag.
*/
if (chan->chan_type != L2CAP_CHAN_FIXED ||
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
- hci_conn_drop(conn->hcon);
+ hci_conn_drop(chan->conn->hcon);
}
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
@@ -1758,19 +1788,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+ cancel_work_sync(&conn->pending_rx_work);
+
mutex_lock(&conn->lock);
kfree_skb(conn->rx_skb);
skb_queue_purge(&conn->pending_rx);
-
- /* We can not call flush_work(&conn->pending_rx_work) here since we
- * might block if we are running on a worker from the same workqueue
- * pending_rx_work is waiting on.
- */
- if (work_pending(&conn->pending_rx_work))
- cancel_work_sync(&conn->pending_rx_work);
-
cancel_delayed_work_sync(&conn->id_addr_timer);
l2cap_unregister_all_users(conn);
@@ -1888,7 +1912,7 @@ static void l2cap_monitor_timeout(struct work_struct *work)
l2cap_chan_lock(chan);
- if (!chan->conn) {
+ if (test_bit(FLAG_DEL, &chan->flags)) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
@@ -1909,7 +1933,7 @@ static void l2cap_retrans_timeout(struct work_struct *work)
l2cap_chan_lock(chan);
- if (!chan->conn) {
+ if (test_bit(FLAG_DEL, &chan->flags)) {
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
@@ -2524,7 +2548,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
int err;
struct sk_buff_head seg_queue;
- if (!chan->conn)
+ if (test_bit(FLAG_DEL, &chan->flags))
return -ENOTCONN;
/* Connectionless channel */
@@ -3002,13 +3026,24 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
return NULL;
}
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
- unsigned long *val)
+static inline int l2cap_get_conf_opt(void **ptr, void *end, int *type,
+ int *olen, unsigned long *val)
{
struct l2cap_conf_opt *opt = *ptr;
int len;
+ /* opt->len is attacker-controlled. Validate that the full option
+ * (header + value) actually fits in the buffer before touching
+ * opt->val, otherwise the switch below reads past the end of the
+ * caller's buffer.
+ */
+ if (end - *ptr < L2CAP_CONF_OPT_SIZE)
+ return -EINVAL;
+
len = L2CAP_CONF_OPT_SIZE + opt->len;
+ if (end - *ptr < len)
+ return -EINVAL;
+
*ptr += len;
*type = opt->type;
@@ -3110,12 +3145,16 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_lock(chan);
+ if (test_bit(FLAG_DEL, &chan->flags))
+ goto unlock;
+
frames_to_ack = __seq_offset(chan, chan->buffer_seq,
chan->last_acked_seq);
if (frames_to_ack)
l2cap_send_rr_or_rnr(chan, 0);
+unlock:
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
}
@@ -3376,6 +3415,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
void *ptr = rsp->data;
void *endptr = data + data_size;
void *req = chan->conf_req;
+ void *req_end = req + chan->conf_len;
int len = chan->conf_len;
int type, hint, olen;
unsigned long val;
@@ -3389,9 +3429,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
BT_DBG("chan %p", chan);
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&req, req_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
hint = type & L2CAP_CONF_HINT;
type &= L2CAP_CONF_MASK;
@@ -3619,6 +3661,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
struct l2cap_conf_req *req = data;
void *ptr = req->data;
void *endptr = data + size;
+ void *rsp_end = rsp + len;
int type, olen;
unsigned long val;
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
@@ -3627,9 +3670,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
switch (type) {
case L2CAP_CONF_MTU:
@@ -3878,6 +3923,7 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
{
int type, olen;
unsigned long val;
+ void *rsp_end = rsp + len;
/* Use sane default values in case a misbehaving remote device
* did not send an RFC or extended window size option.
*/
@@ -3896,9 +3942,11 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
return;
while (len >= L2CAP_CONF_OPT_SIZE) {
- len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
- if (len < 0)
+ int ret = l2cap_get_conf_opt(&rsp, rsp_end, &type, &olen, &val);
+
+ if (ret < 0)
break;
+ len -= ret;
switch (type) {
case L2CAP_CONF_RFC:
@@ -3955,6 +4003,38 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn,
return 0;
}
+/* Allocate and initialise a channel for an incoming connection.
+ *
+ * The channel inherits its configuration from @pchan and is linked into @conn
+ * before ->new_connection() runs, so the conn list reference keeps it alive if
+ * the callback exposes it (e.g. via the socket accept queue) before this
+ * returns. The l2cap_chan_create() reference is taken over by the subsystem on
+ * success and dropped here on failure.
+ */
+static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
+ struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ l2cap_chan_set_defaults(chan, pchan);
+ chan->ops = pchan->ops;
+
+ __l2cap_chan_add(conn, chan);
+
+ if (pchan->ops->new_connection &&
+ pchan->ops->new_connection(pchan, chan) < 0) {
+ l2cap_chan_del(chan, 0);
+ l2cap_chan_put(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
u8 *data, u8 rsp_code)
{
@@ -4001,7 +4081,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
goto response;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan)
goto response;
@@ -4019,8 +4099,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
chan->psm = psm;
chan->dcid = scid;
- __l2cap_chan_add(conn, chan);
-
dcid = chan->scid;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -4889,7 +4967,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
goto response_unlock;
@@ -4904,8 +4982,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
dcid = chan->scid;
@@ -5114,7 +5190,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
continue;
}
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (!chan) {
result = L2CAP_CR_LE_NO_MEM;
continue;
@@ -5129,8 +5205,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
chan->omtu = mtu;
chan->remote_mps = mps;
- __l2cap_chan_add(conn, chan);
-
l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
/* Init response */
@@ -7401,14 +7475,12 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
goto next;
l2cap_chan_lock(pchan);
- chan = pchan->ops->new_connection(pchan);
+ chan = l2cap_new_connection(conn, pchan);
if (chan) {
bacpy(&chan->src, &hcon->src);
bacpy(&chan->dst, &hcon->dst);
chan->src_type = bdaddr_src_type(hcon);
chan->dst_type = dst_type;
-
- __l2cap_chan_add(conn, chan);
}
l2cap_chan_unlock(pchan);
@@ -7625,6 +7697,7 @@ struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
return c;
}
+EXPORT_SYMBOL(l2cap_conn_hold_unless_zero);
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
{
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 3e9b27d3a610..89f881be9712 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -45,7 +45,8 @@ static struct bt_sock_list l2cap_sk_list = {
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern);
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan);
static void l2cap_sock_cleanup_listen(struct sock *parent);
bool l2cap_is_socket(struct socket *sock)
@@ -1240,6 +1241,23 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg,
return err;
}
+/* Release the sock's ref on chan and clear the pointer so that the ref is
+ * dropped exactly once even if both l2cap_sock_kill() and
+ * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
+ * task from dereferencing the now-dead sock pointer.
+ */
+static void l2cap_sock_put_chan(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ if (!chan)
+ return;
+
+ chan->data = NULL;
+ l2cap_pi(sk)->chan = NULL;
+ l2cap_chan_put(chan);
+}
+
/* Kill socket (only if zapped and orphan)
* Must be called on unlocked socket, with l2cap channel lock.
*/
@@ -1250,13 +1268,9 @@ static void l2cap_sock_kill(struct sock *sk)
BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
- /* Sock is dead, so set chan data to NULL, avoid other task use invalid
- * sock pointer.
- */
- l2cap_pi(sk)->chan->data = NULL;
- /* Kill poor orphan */
+ l2cap_sock_put_chan(sk);
- l2cap_chan_put(l2cap_pi(sk)->chan);
+ /* Kill poor orphan */
sock_set_flag(sk, SOCK_DEAD);
sock_put(sk);
}
@@ -1448,8 +1462,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
/* Close not yet accepted channels.
*
- * bt_accept_dequeue() now returns sk with an extra reference held
- * (taken while sk was still locked) so a concurrent l2cap_conn_del()
+ * bt_accept_dequeue() returns sk with its temporary queue-walk
+ * reference held, so a concurrent l2cap_conn_del()
* -> l2cap_sock_kill() cannot free sk under us.
*
* cleanup_listen() runs under the parent sk lock, so unlike
@@ -1499,12 +1513,13 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
}
}
-static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
+static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
struct sock *sk, *parent = chan->data;
if (!parent)
- return NULL;
+ return -EINVAL;
lock_sock(parent);
@@ -1512,25 +1527,28 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
release_sock(parent);
- return NULL;
+ return -ENOBUFS;
}
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
- GFP_ATOMIC, 0);
+ GFP_ATOMIC, 0, new_chan);
if (!sk) {
release_sock(parent);
- return NULL;
- }
+ return -ENOMEM;
+ }
bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
l2cap_sock_init(sk, parent);
+ /* The conn list reference taken by l2cap_new_connection() keeps new_chan
+ * alive once release_sock() lets another task free this socket.
+ */
bt_accept_enqueue(parent, sk, false);
release_sock(parent);
- return l2cap_pi(sk)->chan;
+ return 0;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1828,10 +1846,7 @@ static void l2cap_sock_destruct(struct sock *sk)
BT_DBG("sk %p", sk);
- if (l2cap_pi(sk)->chan) {
- l2cap_pi(sk)->chan->data = NULL;
- l2cap_chan_put(l2cap_pi(sk)->chan);
- }
+ l2cap_sock_put_chan(sk);
list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
kfree_skb(rx_busy->skb);
@@ -1863,30 +1878,12 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
BT_DBG("sk %p", sk);
if (parent) {
- struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
-
sk->sk_type = parent->sk_type;
bt_sk(sk)->flags = bt_sk(parent)->flags;
- chan->chan_type = pchan->chan_type;
- chan->imtu = pchan->imtu;
- chan->omtu = pchan->omtu;
- chan->conf_state = pchan->conf_state;
- chan->mode = pchan->mode;
- chan->fcs = pchan->fcs;
- chan->max_tx = pchan->max_tx;
- chan->tx_win = pchan->tx_win;
- chan->tx_win_max = pchan->tx_win_max;
- chan->sec_level = pchan->sec_level;
- chan->flags = pchan->flags;
- chan->tx_credits = pchan->tx_credits;
- chan->rx_credits = pchan->rx_credits;
-
- if (chan->chan_type == L2CAP_CHAN_FIXED) {
- chan->scid = pchan->scid;
- chan->dcid = pchan->scid;
- }
-
+ /* Channel configuration is inherited from the parent by
+ * l2cap_new_connection().
+ */
security_sk_clone(parent, sk);
} else {
switch (sk->sk_type) {
@@ -1912,7 +1909,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->mode = L2CAP_MODE_BASIC;
}
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
}
/* Default config options */
@@ -1931,10 +1928,10 @@ static struct proto l2cap_proto = {
};
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
- int proto, gfp_t prio, int kern)
+ int proto, gfp_t prio, int kern,
+ struct l2cap_chan *chan)
{
struct sock *sk;
- struct l2cap_chan *chan;
sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
if (!sk)
@@ -1945,16 +1942,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
- chan = l2cap_chan_create();
- if (!chan) {
- sk_free(sk);
- if (sock)
- sock->sk = NULL;
- return NULL;
- }
-
- l2cap_chan_hold(chan);
-
+ /* The sock takes ownership of the caller's reference on chan. */
l2cap_pi(sk)->chan = chan;
return sk;
@@ -1964,6 +1952,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
+ struct l2cap_chan *chan;
BT_DBG("sock %p", sock);
@@ -1978,10 +1967,16 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
sock->ops = &l2cap_sock_ops;
- sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
- if (!sk)
+ chan = l2cap_chan_create();
+ if (!chan)
return -ENOMEM;
+ sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
+ if (!sk) {
+ l2cap_chan_put(chan);
+ return -ENOMEM;
+ }
+
l2cap_sock_init(sk, NULL);
bt_sock_link(&l2cap_sk_list, sk);
return 0;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 88f44cb36e24..20fe7f011b75 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3233,7 +3233,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
static u8 link_to_bdaddr(u8 link_type, u8 addr_type)
{
switch (link_type) {
- case ISO_LINK:
+ case CIS_LINK:
+ case BIS_LINK:
case LE_LINK:
switch (addr_type) {
case ADDR_LE_DEV_PUBLIC:
@@ -5347,6 +5348,8 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
if (monitor->state == ADV_MONITOR_STATE_NOT_REGISTERED)
monitor->state = ADV_MONITOR_STATE_REGISTERED;
hci_update_passive_scan(hdev);
+ } else {
+ hci_free_adv_monitor(hdev, monitor);
}
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
@@ -7732,6 +7735,8 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
if (!err) {
struct hci_conn_params *params;
+ hci_dev_lock(hdev);
+
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
@@ -7740,6 +7745,7 @@ static void add_device_complete(struct hci_dev *hdev, void *data, int err)
device_flags_changed(NULL, hdev, &cp->addr.bdaddr,
cp->addr.type, hdev->conn_flags,
params ? params->flags : 0);
+ hci_dev_unlock(hdev);
}
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_ADD_DEVICE,
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
index d039683d3bdd..9aee9bffaa7e 100644
--- a/net/bluetooth/msft.c
+++ b/net/bluetooth/msft.c
@@ -292,7 +292,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
monitor->state = ADV_MONITOR_STATE_OFFLOADED;
unlock:
- if (status)
+ if (status && msft->resuming)
hci_free_adv_monitor(hdev, monitor);
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 912e37cb8310..389d7b47ad61 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -201,14 +201,14 @@ static ssize_t address_show(struct device *tty_dev,
struct device_attribute *attr, char *buf)
{
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
- return sprintf(buf, "%pMR\n", &dev->dst);
+ return sysfs_emit(buf, "%pMR\n", &dev->dst);
}
static ssize_t channel_show(struct device *tty_dev,
struct device_attribute *attr, char *buf)
{
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
- return sprintf(buf, "%d\n", dev->channel);
+ return sysfs_emit(buf, "%d\n", dev->channel);
}
static DEVICE_ATTR_RO(address);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 3a3e1cf3c7c8..75609dbd5b2c 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -409,9 +409,13 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src)
sk1 = sk;
}
+ sk = sk ? sk : sk1;
+ if (sk)
+ sock_hold(sk);
+
read_unlock(&sco_sk_list.lock);
- return sk ? sk : sk1;
+ return sk;
}
static void sco_sock_destruct(struct sock *sk)
@@ -451,11 +455,13 @@ static void sco_sock_kill(struct sock *sk)
BT_DBG("sk %p state %d", sk, sk->sk_state);
/* Sock is dead, so set conn->sk to NULL to avoid possible UAF */
+ lock_sock(sk);
if (sco_pi(sk)->conn) {
sco_conn_lock(sco_pi(sk)->conn);
sco_pi(sk)->conn->sk = NULL;
sco_conn_unlock(sco_pi(sk)->conn);
}
+ release_sock(sk);
/* Kill poor orphan */
bt_sock_unlink(&sco_sk_list, sk);
@@ -1302,40 +1308,51 @@ static int sco_sock_release(struct socket *sock)
static void sco_conn_ready(struct sco_conn *conn)
{
- struct sock *parent;
- struct sock *sk = conn->sk;
+ struct sock *parent, *sk;
+
+ sco_conn_lock(conn);
+ sk = sco_sock_hold(conn);
+ sco_conn_unlock(conn);
BT_DBG("conn %p", conn);
if (sk) {
lock_sock(sk);
- sco_sock_clear_timer(sk);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
+
+ /* conn->sk may have become NULL if racing with sk close, but
+ * due to held hdev->lock, it can't become different sk.
+ */
+ if (conn->sk) {
+ sco_sock_clear_timer(sk);
+ sk->sk_state = BT_CONNECTED;
+ sk->sk_state_change(sk);
+ }
+
release_sock(sk);
+ sock_put(sk);
} else {
- sco_conn_lock(conn);
-
- if (!conn->hcon) {
- sco_conn_unlock(conn);
+ if (!conn->hcon)
return;
- }
+
+ lockdep_assert_held(&conn->hcon->hdev->lock);
parent = sco_get_sock_listen(&conn->hcon->src);
- if (!parent) {
- sco_conn_unlock(conn);
+ if (!parent)
return;
- }
lock_sock(parent);
+ sco_conn_lock(conn);
+
+ /* hdev->lock guarantees conn->sk == NULL still here */
+
+ if (parent->sk_state != BT_LISTEN)
+ goto release;
+
sk = sco_sock_alloc(sock_net(parent), NULL,
BTPROTO_SCO, GFP_ATOMIC, 0);
- if (!sk) {
- release_sock(parent);
- sco_conn_unlock(conn);
- return;
- }
+ if (!sk)
+ goto release;
sco_sock_init(sk, parent);
@@ -1353,9 +1370,10 @@ static void sco_conn_ready(struct sco_conn *conn)
/* Wake up parent */
parent->sk_data_ready(parent);
- release_sock(parent);
-
+release:
sco_conn_unlock(conn);
+ release_sock(parent);
+ sock_put(parent);
}
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 6fabc76aa9d7..49e0e412c6c5 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3235,34 +3235,19 @@ static const struct l2cap_ops smp_chan_ops = {
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
};
-static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+static inline int smp_new_conn_cb(struct l2cap_chan *chan,
+ struct l2cap_chan *new_chan)
{
- struct l2cap_chan *chan;
-
- BT_DBG("pchan %p", pchan);
-
- chan = l2cap_chan_create();
- if (!chan)
- return NULL;
-
- chan->chan_type = pchan->chan_type;
- chan->ops = &smp_chan_ops;
- chan->scid = pchan->scid;
- chan->dcid = chan->scid;
- chan->imtu = pchan->imtu;
- chan->omtu = pchan->omtu;
- chan->mode = pchan->mode;
+ new_chan->ops = &smp_chan_ops;
/* Other L2CAP channels may request SMP routines in order to
* change the security level. This means that the SMP channel
* lock must be considered in its own category to avoid lockdep
* warnings.
*/
- atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
-
- BT_DBG("created chan %p", chan);
+ atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
- return chan;
+ return 0;
}
static const struct l2cap_ops smp_root_chan_ops = {
@@ -3333,7 +3318,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
l2cap_add_scid(chan, cid);
- l2cap_chan_set_defaults(chan);
+ l2cap_chan_set_defaults(chan, NULL);
if (cid == L2CAP_CID_SMP) {
u8 bdaddr_type;
diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c
index a3c755d0a09d..a89aec831416 100644
--- a/net/bridge/br_cfm.c
+++ b/net/bridge/br_cfm.c
@@ -805,6 +805,12 @@ int br_cfm_cc_ccm_tx(struct net_bridge *br, const u32 instance,
goto save;
}
+ if (!interval_to_us(mep->cc_config.exp_interval)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Invalid CCM interval");
+ return -EINVAL;
+ }
+
/* Start delayed work to transmit CCM frames. It is done with zero delay
* to send first frame immediately
*/
diff --git a/net/bridge/br_cfm_netlink.c b/net/bridge/br_cfm_netlink.c
index 2faab44652e7..91b9922dc3f2 100644
--- a/net/bridge/br_cfm_netlink.c
+++ b/net/bridge/br_cfm_netlink.c
@@ -34,7 +34,9 @@ br_cfm_cc_config_policy[IFLA_BRIDGE_CFM_CC_CONFIG_MAX + 1] = {
[IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NLA_U32 },
[IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NLA_U32 },
- [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] =
+ NLA_POLICY_RANGE(NLA_U32, BR_CFM_CCM_INTERVAL_3_3_MS,
+ BR_CFM_CCM_INTERVAL_10_MIN),
[IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = {
.type = NLA_BINARY, .len = CFM_MAID_LENGTH },
};
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 6ffc81eedf07..eee6ceb7cdda 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -391,6 +391,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
br_fdb_delete_by_port(br, NULL, 0, 1);
+ timer_shutdown_sync(&br->hello_timer);
+ timer_shutdown_sync(&br->topology_change_timer);
+ timer_shutdown_sync(&br->tcn_timer);
cancel_delayed_work_sync(&br->gc_work);
br_sysfs_delbr(br->dev);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 7d27b2e6038f..4cfebf5fcdf5 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -371,7 +371,8 @@ void br_topology_change_detection(struct net_bridge *br)
{
int isroot = br_is_root_bridge(br);
- if (br->stp_enabled != BR_KERNEL_STP)
+ if (br->stp_enabled != BR_KERNEL_STP ||
+ !(br->dev->flags & IFF_UP))
return;
br_info(br, "topology change detected, %s\n",
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index bc69406d103d..32e73f04d5b1 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -402,6 +402,9 @@ ebt_check_match(struct ebt_entry_match *m, struct xt_mtchk_param *par,
left - sizeof(struct ebt_entry_match) < m->match_size)
return -EINVAL;
+ if (strnlen(m->u.name, XT_EXTENSION_MAXNAMELEN) == XT_EXTENSION_MAXNAMELEN)
+ return -EINVAL;
+
match = xt_find_match(NFPROTO_BRIDGE, m->u.name, m->u.revision);
if (IS_ERR(match) || match->family != NFPROTO_BRIDGE) {
if (!IS_ERR(match))
@@ -920,8 +923,7 @@ static int translate_table(struct net *net, const char *name,
* if an error occurs
*/
newinfo->chainstack =
- vmalloc(array_size(nr_cpu_ids,
- sizeof(*(newinfo->chainstack))));
+ vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
return -ENOMEM;
for_each_possible_cpu(i) {
@@ -938,7 +940,7 @@ static int translate_table(struct net *net, const char *name,
}
}
- cl_s = vmalloc(array_size(udc_cnt, sizeof(*cl_s)));
+ cl_s = vmalloc_array(udc_cnt, sizeof(*cl_s));
if (!cl_s)
return -ENOMEM;
i = 0; /* the i'th udc */
@@ -1018,8 +1020,8 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
* the check on the size is done later, when we have the lock
*/
if (repl->num_counters) {
- unsigned long size = repl->num_counters * sizeof(*counterstmp);
- counterstmp = vmalloc(size);
+ counterstmp = vmalloc_array(repl->num_counters,
+ sizeof(*counterstmp));
if (!counterstmp)
return -ENOMEM;
}
@@ -1386,7 +1388,7 @@ static int do_update_counters(struct net *net, const char *name,
if (num_counters == 0)
return -EINVAL;
- tmp = vmalloc(array_size(num_counters, sizeof(*tmp)));
+ tmp = vmalloc_array(num_counters, sizeof(*tmp));
if (!tmp)
return -ENOMEM;
@@ -1430,6 +1432,8 @@ static int update_counters(struct net *net, sockptr_t arg, unsigned int len)
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
+ hlp.name[sizeof(hlp.name) - 1] = '\0';
+
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return -EINVAL;
@@ -1526,7 +1530,7 @@ static int copy_counters_to_user(struct ebt_table *t,
if (num_counters != nentries)
return -EINVAL;
- counterstmp = vmalloc(array_size(nentries, sizeof(*counterstmp)));
+ counterstmp = vmalloc_array(nentries, sizeof(*counterstmp));
if (!counterstmp)
return -ENOMEM;
@@ -2269,6 +2273,8 @@ static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl,
memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
+ repl->name[sizeof(repl->name) - 1] = '\0';
+
/* starting with hook_entry, 32 vs. 64 bit structures are different */
for (i = 0; i < NF_BR_NUMHOOKS; i++)
repl->hook_entry[i] = compat_ptr(tmp.hook_entry[i]);
@@ -2391,6 +2397,8 @@ static int compat_update_counters(struct net *net, sockptr_t arg,
if (copy_from_sockptr(&hlp, arg, sizeof(hlp)))
return -EFAULT;
+ hlp.name[sizeof(hlp.name) - 1] = '\0';
+
/* try real handler in case userland supplied needed padding */
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
return update_counters(net, arg, len);
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
index d12a221366d6..475d7d1709b9 100644
--- a/net/bridge/netfilter/nft_meta_bridge.c
+++ b/net/bridge/netfilter/nft_meta_bridge.c
@@ -44,7 +44,9 @@ static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
if (!br_dev || !br_vlan_enabled(br_dev))
goto err;
- br_vlan_get_pvid_rcu(in, &p_pvid);
+ if (br_vlan_get_pvid_rcu(in, &p_pvid))
+ goto err;
+
nft_reg_store16(dest, p_pvid);
return;
}
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 35039645c462..04653df3a173 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -58,6 +58,7 @@
#include <linux/can/skb.h>
#include <linux/can/bcm.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <net/sock.h>
#include <net/net_namespace.h>
@@ -89,6 +90,8 @@ MODULE_ALIAS("can-proto-2");
#define BCM_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_ifindex)
+static struct workqueue_struct *bcm_wq;
+
/*
* easy access to the first 64 bit of can(fd)_frame payload. cp->data is
* 64 bit aligned so the offset has to be multiples of 8 which is ensured
@@ -102,6 +105,7 @@ static inline u64 get_u64(const struct canfd_frame *cp, int offset)
struct bcm_op {
struct list_head list;
struct rcu_head rcu;
+ struct work_struct work;
int ifindex;
canid_t can_id;
u32 flags;
@@ -244,7 +248,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
(reduction == 100) ? "near " : "", reduction);
}
- list_for_each_entry(op, &bo->tx_ops, list) {
+ list_for_each_entry_rcu(op, &bo->tx_ops, list) {
seq_printf(m, "tx_op: %03X %s ", op->can_id,
bcm_proc_getifname(net, ifname, op->ifindex));
@@ -745,9 +749,12 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
return NULL;
}
-static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+static void bcm_free_op_work(struct work_struct *work)
{
- struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
+ struct bcm_op *op = container_of(work, struct bcm_op, work);
+
+ hrtimer_cancel(&op->timer);
+ hrtimer_cancel(&op->thrtimer);
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
@@ -755,9 +762,23 @@ static void bcm_free_op_rcu(struct rcu_head *rcu_head)
if ((op->last_frames) && (op->last_frames != &op->last_sframe))
kfree(op->last_frames);
+ /* the last possible access to op->timer/op->thrtimer has now
+ * happened above via hrtimer_cancel() - op->sk is no longer
+ * needed by any pending timer callback, so drop our reference
+ */
+ sock_put(op->sk);
+
kfree(op);
}
+static void bcm_free_op_rcu(struct rcu_head *rcu_head)
+{
+ struct bcm_op *op = container_of(rcu_head, struct bcm_op, rcu);
+
+ INIT_WORK(&op->work, bcm_free_op_work);
+ queue_work(bcm_wq, &op->work);
+}
+
static void bcm_remove_op(struct bcm_op *op)
{
hrtimer_cancel(&op->timer);
@@ -882,6 +903,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
struct canfd_frame *cf;
+ bool add_op_to_list = false;
unsigned int i;
int err;
@@ -1012,6 +1034,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
+ sock_hold(sk);
op->ifindex = ifindex;
/* initialize uninitialized (kzalloc) structure */
@@ -1023,8 +1046,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
- /* add this bcm_op to the list of the tx_ops */
- list_add(&op->list, &bo->tx_ops);
+ add_op_to_list = true;
} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
@@ -1046,6 +1068,10 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->flags |= TX_ANNOUNCE;
}
+ /* add this bcm_op to the list of the tx_ops? */
+ if (add_op_to_list)
+ list_add_rcu(&op->list, &bo->tx_ops);
+
if (op->flags & TX_ANNOUNCE)
bcm_can_tx(op);
@@ -1174,6 +1200,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* bcm_can_tx / bcm_tx_timeout_handler needs this */
op->sk = sk;
+ sock_hold(sk);
op->ifindex = ifindex;
/* ifindex for timeout events w/o previous frame reception */
@@ -1188,9 +1215,6 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
HRTIMER_MODE_REL_SOFT);
op->thrtimer.function = bcm_rx_thr_handler;
- /* add this bcm_op to the list of the rx_ops */
- list_add(&op->list, &bo->rx_ops);
-
/* call can_rx_register() */
do_rx_register = 1;
@@ -1256,6 +1280,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->rx_reg_dev = dev;
dev_put(dev);
+ } else {
+ /* the requested device is gone - do not
+ * silently succeed without registering
+ */
+ err = -ENODEV;
}
} else
@@ -1264,10 +1293,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
bcm_rx_handler, op, "bcm", sk);
if (err) {
/* this bcm rx op is broken -> remove it */
- list_del_rcu(&op->list);
bcm_remove_op(op);
return err;
}
+
+ /* add this bcm_op to the list of the rx_ops */
+ list_add_rcu(&op->list, &bo->rx_ops);
}
return msg_head->nframes * op->cfsiz + MHSIZ;
@@ -1325,12 +1356,13 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
- int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+ int ifindex;
struct bcm_msg_head msg_head;
int cfsiz;
int ret; /* read bytes or error codes as return value */
- if (!bo->bound)
+ /* Lockless fast-path check for bound socket */
+ if (!READ_ONCE(bo->bound))
return -ENOTCONN;
/* check for valid message length from userspace */
@@ -1346,17 +1378,38 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if ((size - MHSIZ) % cfsiz)
return -EINVAL;
+ lock_sock(sk);
+
+ /* Re-validate under the socket lock: a concurrent bcm_notify()
+ * may have unbound this socket (device removal) after the
+ * lockless fast-path check above. bo->ifindex is only ever
+ * mutated under lock_sock(), so reading it here - instead of
+ * before taking the lock - guarantees it can't be observed
+ * torn against bo->bound.
+ */
+ if (!bo->bound) {
+ ret = -ENOTCONN;
+ goto out_release;
+ }
+
+ /* default ifindex for this bcm_op */
+ ifindex = bo->ifindex;
+
/* check for alternative ifindex for this bcm_op */
if (!ifindex && msg->msg_name) {
/* no bound device as default => check msg_name */
DECLARE_SOCKADDR(struct sockaddr_can *, addr, msg->msg_name);
- if (msg->msg_namelen < BCM_MIN_NAMELEN)
- return -EINVAL;
+ if (msg->msg_namelen < BCM_MIN_NAMELEN) {
+ ret = -EINVAL;
+ goto out_release;
+ }
- if (addr->can_family != AF_CAN)
- return -EINVAL;
+ if (addr->can_family != AF_CAN) {
+ ret = -EINVAL;
+ goto out_release;
+ }
/* ifindex from sendto() */
ifindex = addr->can_ifindex;
@@ -1365,20 +1418,21 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
struct net_device *dev;
dev = dev_get_by_index(sock_net(sk), ifindex);
- if (!dev)
- return -ENODEV;
+ if (!dev) {
+ ret = -ENODEV;
+ goto out_release;
+ }
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
- return -ENODEV;
+ ret = -ENODEV;
+ goto out_release;
}
dev_put(dev);
}
}
- lock_sock(sk);
-
switch (msg_head.opcode) {
case TX_SETUP:
@@ -1428,6 +1482,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
break;
}
+out_release:
release_sock(sk);
return ret;
@@ -1464,7 +1519,12 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
bo->bcm_proc_read = NULL;
}
#endif
- bo->bound = 0;
+ /* Paired with the lockless fast-path check in
+ * bcm_sendmsg(); bo->ifindex itself is only ever
+ * accessed under lock_sock() so it needs no
+ * annotation.
+ */
+ WRITE_ONCE(bo->bound, 0);
bo->ifindex = 0;
notify_enodev = 1;
}
@@ -1568,8 +1628,10 @@ static int bcm_release(struct socket *sock)
remove_proc_entry(bo->procname, net->can.bcmproc_dir);
#endif /* CONFIG_PROC_FS */
- list_for_each_entry_safe(op, next, &bo->tx_ops, list)
+ list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
+ list_del_rcu(&op->list);
bcm_remove_op(op);
+ }
list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
/*
@@ -1600,12 +1662,14 @@ static int bcm_release(struct socket *sock)
synchronize_rcu();
- list_for_each_entry_safe(op, next, &bo->rx_ops, list)
+ list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+ list_del_rcu(&op->list);
bcm_remove_op(op);
+ }
/* remove device reference */
if (bo->bound) {
- bo->bound = 0;
+ WRITE_ONCE(bo->bound, 0);
bo->ifindex = 0;
}
@@ -1674,7 +1738,10 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
}
#endif /* CONFIG_PROC_FS */
- bo->bound = 1;
+ /* bo->ifindex above is fully assigned before this point; pairs
+ * with the lockless fast-path check in bcm_sendmsg()
+ */
+ WRITE_ONCE(bo->bound, 1);
fail:
release_sock(sk);
@@ -1787,11 +1854,15 @@ static int __init bcm_module_init(void)
{
int err;
+ bcm_wq = alloc_workqueue("can-bcm-wq", WQ_UNBOUND, 0);
+ if (!bcm_wq)
+ return -ENOMEM;
+
pr_info("can: broadcast manager protocol\n");
err = register_pernet_subsys(&canbcm_pernet_ops);
if (err)
- return err;
+ goto register_pernet_failed;
err = register_netdevice_notifier(&canbcm_notifier);
if (err)
@@ -1809,6 +1880,8 @@ static int __init bcm_module_init(void)
unregister_netdevice_notifier(&canbcm_notifier);
register_notifier_failed:
unregister_pernet_subsys(&canbcm_pernet_ops);
+register_pernet_failed:
+ destroy_workqueue(bcm_wq);
return err;
}
@@ -1817,6 +1890,8 @@ static void __exit bcm_module_exit(void)
can_proto_unregister(&bcm_can_proto);
unregister_netdevice_notifier(&canbcm_notifier);
unregister_pernet_subsys(&canbcm_pernet_ops);
+ rcu_barrier();
+ destroy_workqueue(bcm_wq);
}
module_init(bcm_module_init);
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 4945e4805442..80adf7366e63 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -1215,11 +1215,18 @@ static int isotp_release(struct socket *sock)
SINGLE_MASK(so->txid),
isotp_rcv_echo, sk);
dev_put(dev);
- synchronize_rcu();
}
}
}
+ /* Always wait for a grace period before touching the timers below.
+ * A concurrent NETDEV_UNREGISTER may have already unregistered our
+ * filters and cleared so->bound in isotp_notify() without waiting
+ * for in-flight isotp_rcv() callers to finish, so this call must not
+ * be skipped just because so->bound is already 0 here.
+ */
+ synchronize_rcu();
+
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->rxtimer);
diff --git a/net/core/dev.c b/net/core/dev.c
index 405b448bd91f..0cc03e6c6fb1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -11450,6 +11450,94 @@ netdev_features_t netdev_increment_features(netdev_features_t all,
}
EXPORT_SYMBOL(netdev_increment_features);
+/**
+ * netdev_compute_master_upper_features - compute feature from lowers
+ * @dev: the upper device
+ * @update_header: whether to update upper device's header_len/headroom/tailroom
+ *
+ * Recompute the upper device's feature based on all lower devices.
+ */
+void netdev_compute_master_upper_features(struct net_device *dev, bool update_header)
+{
+ unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
+ netdev_features_t gso_partial_features = MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES;
+ netdev_features_t xfrm_features = MASTER_UPPER_DEV_XFRM_FEATURES;
+ netdev_features_t mpls_features = MASTER_UPPER_DEV_MPLS_FEATURES;
+ netdev_features_t vlan_features = MASTER_UPPER_DEV_VLAN_FEATURES;
+ netdev_features_t enc_features = MASTER_UPPER_DEV_ENC_FEATURES;
+ unsigned short max_header_len = ETH_HLEN;
+ unsigned int tso_max_size = TSO_MAX_SIZE;
+ unsigned short max_headroom = 0;
+ unsigned short max_tailroom = 0;
+ u16 tso_max_segs = TSO_MAX_SEGS;
+ struct net_device *lower_dev;
+ struct list_head *iter;
+
+ mpls_features = netdev_base_features(mpls_features);
+ vlan_features = netdev_base_features(vlan_features);
+ enc_features = netdev_base_features(enc_features);
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter) {
+ gso_partial_features = netdev_increment_features(gso_partial_features,
+ lower_dev->gso_partial_features,
+ MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES);
+
+ vlan_features = netdev_increment_features(vlan_features,
+ lower_dev->vlan_features,
+ MASTER_UPPER_DEV_VLAN_FEATURES);
+
+ enc_features = netdev_increment_features(enc_features,
+ lower_dev->hw_enc_features,
+ MASTER_UPPER_DEV_ENC_FEATURES);
+
+ if (IS_ENABLED(CONFIG_XFRM_OFFLOAD))
+ xfrm_features = netdev_increment_features(xfrm_features,
+ lower_dev->hw_enc_features,
+ MASTER_UPPER_DEV_XFRM_FEATURES);
+
+ mpls_features = netdev_increment_features(mpls_features,
+ lower_dev->mpls_features,
+ MASTER_UPPER_DEV_MPLS_FEATURES);
+
+ dst_release_flag &= lower_dev->priv_flags;
+
+ if (update_header) {
+ max_header_len = max(max_header_len, lower_dev->hard_header_len);
+ max_headroom = max(max_headroom, lower_dev->needed_headroom);
+ max_tailroom = max(max_tailroom, lower_dev->needed_tailroom);
+ }
+
+ tso_max_size = min(tso_max_size, lower_dev->tso_max_size);
+ tso_max_segs = min(tso_max_segs, lower_dev->tso_max_segs);
+ }
+
+ dev->gso_partial_features = gso_partial_features;
+ dev->vlan_features = vlan_features;
+ dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
+ NETIF_F_HW_VLAN_CTAG_TX |
+ NETIF_F_HW_VLAN_STAG_TX;
+ if (IS_ENABLED(CONFIG_XFRM_OFFLOAD))
+ dev->hw_enc_features |= xfrm_features;
+ dev->mpls_features = mpls_features;
+
+ dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ if ((dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) &&
+ dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
+ dev->priv_flags |= IFF_XMIT_DST_RELEASE;
+
+ if (update_header) {
+ dev->hard_header_len = max_header_len;
+ dev->needed_headroom = max_headroom;
+ dev->needed_tailroom = max_tailroom;
+ }
+
+ netif_set_tso_max_segs(dev, tso_max_segs);
+ netif_set_tso_max_size(dev, tso_max_size);
+
+ netdev_change_features(dev);
+}
+EXPORT_SYMBOL(netdev_compute_master_upper_features);
+
static struct hlist_head * __net_init netdev_create_hash(void)
{
int i;
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 75282222e0b4..e31873c87c0a 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -328,7 +328,7 @@ int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
if (err != -EAGAIN) {
if ((arg->flags & FIB_LOOKUP_NOREF) ||
- likely(refcount_inc_not_zero(&rule->refcnt))) {
+ likely(fib_rule_get_safe(rule))) {
arg->rule = rule;
goto out;
}
@@ -386,8 +386,12 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
if (!ops)
return -EAFNOSUPPORT;
list_for_each_entry_rcu(rule, &ops->rules_list, list) {
+ if (!fib_rule_get_safe(rule))
+ continue;
+
err = call_fib_rule_notifier(nb, FIB_EVENT_RULE_ADD,
rule, family, extack);
+ fib_rule_put(rule);
if (err)
break;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 4a9c1367ea4d..62d21a961e5e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2822,6 +2822,9 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start,
if (!space || (space == 1 && start != offset))
copy = msg->sg.data[i].length;
+ if (unlikely(copy + len < copy))
+ return -EINVAL;
+
page = alloc_pages(__GFP_NOWARN | GFP_ATOMIC | __GFP_COMP,
get_order(copy + len));
if (unlikely(!page))
@@ -2985,8 +2988,8 @@ BPF_CALL_4(bpf_msg_pop_data, struct sk_msg *, msg, u32, start,
u32, len, u64, flags)
{
u32 i = 0, l = 0, space, offset = 0;
- u64 last = start + len;
- int pop;
+ u64 last = (u64)start + len;
+ u32 pop;
if (unlikely(flags))
return -EINVAL;
@@ -4390,7 +4393,7 @@ u32 xdp_master_redirect(struct xdp_buff *xdp)
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
master = netdev_master_upper_dev_get_rcu(xdp->rxq->dev);
- if (unlikely(!(master->flags & IFF_UP)))
+ if (unlikely(!master || !(master->flags & IFF_UP)))
return XDP_ABORTED;
slave = master->netdev_ops->ndo_xdp_get_xmit_slave(master, xdp);
if (slave && slave != xdp->rxq->dev) {
@@ -5455,11 +5458,24 @@ static int sol_tcp_sockopt(struct sock *sk, int optname,
KERNEL_SOCKPTR(optval), *optlen);
}
+static bool sk_allows_sol_ip_sockopt(struct sock *sk)
+{
+ switch (sk->sk_family) {
+ case AF_INET:
+ return true;
+ case AF_INET6:
+ /* Allow getting/setting sockopt for possible ipv4-mapped ipv6 socket. */
+ return sk->sk_type != SOCK_RAW && !ipv6_only_sock(sk);
+ default:
+ return false;
+ }
+}
+
static int sol_ip_sockopt(struct sock *sk, int optname,
char *optval, int *optlen,
bool getopt)
{
- if (sk->sk_family != AF_INET)
+ if (!sk_allows_sol_ip_sockopt(sk))
return -EINVAL;
switch (optname) {
@@ -5939,7 +5955,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
struct neighbour *neigh;
struct net_device *dev;
struct fib_result res;
- struct flowi4 fl4;
+ struct flowi4 fl4 = {};
u32 mtu = 0;
int err;
@@ -6076,7 +6092,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
struct neighbour *neigh;
struct net_device *dev;
struct inet6_dev *idev;
- struct flowi6 fl6;
+ struct flowi6 fl6 = {};
int strict = 0;
int oif, err;
u32 mtu = 0;
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 9432e5362b44..5ac4cd4f3c68 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -1149,13 +1149,21 @@ bool __skb_flow_dissect(const struct net *net,
if (dissector_uses_key(flow_dissector,
FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- struct ethhdr *eth = eth_hdr(skb);
struct flow_dissector_key_eth_addrs *key_eth_addrs;
key_eth_addrs = skb_flow_dissector_target(flow_dissector,
FLOW_DISSECTOR_KEY_ETH_ADDRS,
target_container);
- memcpy(key_eth_addrs, eth, sizeof(*key_eth_addrs));
+ /* TC filter blocks can be shared across devices with
+ * different link types, so we cannot validate this
+ * when the filter is installed -- check at dissect time.
+ */
+ if (skb && skb->dev &&
+ skb->dev->type == ARPHRD_ETHER &&
+ skb_mac_header_was_set(skb))
+ memcpy(key_eth_addrs, eth_hdr(skb), sizeof(*key_eth_addrs));
+ else
+ memset(key_eth_addrs, 0, sizeof(*key_eth_addrs));
}
if (dissector_uses_key(flow_dissector,
diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
index 4a0797f0a154..efbd9eecb00d 100644
--- a/net/core/lwt_bpf.c
+++ b/net/core/lwt_bpf.c
@@ -590,6 +590,7 @@ static int handle_gso_encap(struct sk_buff *skb, bool ipv4, int encap_len)
int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress)
{
+ bool is_udp_tunnel;
struct iphdr *iph;
bool ipv4;
int err;
@@ -603,10 +604,16 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress)
ipv4 = true;
if (unlikely(len < iph->ihl * 4))
return -EINVAL;
+ is_udp_tunnel = iph->protocol == IPPROTO_UDP;
+ if (unlikely(is_udp_tunnel && len < iph->ihl * 4 + sizeof(struct udphdr)))
+ return -EINVAL;
} else if (iph->version == 6) {
ipv4 = false;
if (unlikely(len < sizeof(struct ipv6hdr)))
return -EINVAL;
+ is_udp_tunnel = ((struct ipv6hdr *)iph)->nexthdr == NEXTHDR_UDP;
+ if (unlikely(is_udp_tunnel && len < sizeof(struct ipv6hdr) + sizeof(struct udphdr)))
+ return -EINVAL;
} else {
return -EINVAL;
}
@@ -628,6 +635,11 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len, bool ingress)
if (ingress)
skb_postpush_rcsum(skb, iph, len);
skb_reset_network_header(skb);
+ if (is_udp_tunnel) {
+ size_t iph_sz = ipv4 ? iph->ihl * 4 : sizeof(struct ipv6hdr);
+
+ skb_set_transport_header(skb, skb_network_offset(skb) + iph_sz);
+ }
memcpy(skb_network_header(skb), hdr, len);
bpf_compute_data_pointers(skb);
skb_clear_hash(skb);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 350705730f76..09a3ca7e64c5 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2157,6 +2157,14 @@ struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid)
}
EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable);
+bool rtnl_dev_link_net_capable(const struct net_device *dev,
+ const struct net *link_net)
+{
+ return net_eq(link_net, dev_net(dev)) ||
+ ns_capable(link_net->user_ns, CAP_NET_ADMIN);
+}
+EXPORT_SYMBOL_GPL(rtnl_dev_link_net_capable);
+
static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh,
bool strict_check, struct nlattr **tb,
struct netlink_ext_ack *extack)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c5e2ae6d0406..732204d99822 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -5258,15 +5258,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly)
{
- bool ret;
+ struct socket *sock;
+ struct file *file;
+ bool ret = false;
if (likely(READ_ONCE(sysctl_tstamp_allow_data) || tsonly))
return true;
- read_lock_bh(&sk->sk_callback_lock);
- ret = sk->sk_socket && sk->sk_socket->file &&
- file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW);
- read_unlock_bh(&sk->sk_callback_lock);
+ /* The sk pointer remains valid as long as the skb is. The sk_socket and
+ * file pointer may become NULL if the socket is closed. Both structures
+ * (including file->cred) are RCU freed which means they can be accessed
+ * within a RCU read section.
+ */
+ rcu_read_lock();
+ sock = READ_ONCE(sk->sk_socket);
+ if (!sock)
+ goto out;
+ file = READ_ONCE(sock->file);
+ if (!file)
+ goto out;
+ ret = file_ns_capable(file, &init_user_ns, CAP_NET_RAW);
+out:
+ rcu_read_unlock();
return ret;
}
diff --git a/net/devlink/rate.c b/net/devlink/rate.c
index e7e80af4abaf..b4d13b5ef9b6 100644
--- a/net/devlink/rate.c
+++ b/net/devlink/rate.c
@@ -598,11 +598,6 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
if (!rate_node)
return ERR_PTR(-ENOMEM);
- if (parent) {
- rate_node->parent = parent;
- refcount_inc(&rate_node->parent->refcnt);
- }
-
rate_node->type = DEVLINK_RATE_TYPE_NODE;
rate_node->devlink = devlink;
rate_node->priv = priv;
@@ -613,6 +608,11 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
return ERR_PTR(-ENOMEM);
}
+ if (parent) {
+ rate_node->parent = parent;
+ refcount_inc(&rate_node->parent->refcnt);
+ }
+
refcount_set(&rate_node->refcnt, 1);
list_add(&rate_node->list, &devlink->rate_list);
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index b4a6e26ec287..a90d2470e090 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -195,14 +195,11 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
}
EXPORT_SYMBOL(eth_type_trans);
-/**
- * eth_header_parse - extract hardware address from packet
- * @skb: packet to extract header from
- * @haddr: destination buffer
- */
-int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+int eth_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr)
{
const struct ethhdr *eth = eth_hdr(skb);
+
memcpy(haddr, eth->h_source, ETH_ALEN);
return ETH_ALEN;
}
diff --git a/net/handshake/genl.c b/net/handshake/genl.c
index 233be5cbfec9..8fdec426e580 100644
--- a/net/handshake/genl.c
+++ b/net/handshake/genl.c
@@ -36,7 +36,7 @@ static const struct genl_split_ops handshake_nl_ops[] = {
.doit = handshake_nl_done_doit,
.policy = handshake_done_nl_policy,
.maxattr = HANDSHAKE_A_DONE_REMOTE_AUTH,
- .flags = GENL_CMD_CAP_DO,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
};
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 57546e07e06a..cdf4b41c54b2 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -327,7 +327,7 @@ static void __net_exit cfg802154_pernet_exit(struct net *net)
rtnl_lock();
list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
if (net_eq(wpan_phy_net(&rdev->wpan_phy), net))
- WARN_ON(cfg802154_switch_netns(rdev, &init_net));
+ cfg802154_switch_netns(rdev, &init_net);
}
rtnl_unlock();
}
diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c
index 41a556be1017..a9f0c8df5ae4 100644
--- a/net/ieee802154/header_ops.c
+++ b/net/ieee802154/header_ops.c
@@ -173,10 +173,13 @@ ieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan,
{
int pos = 0;
- addr->mode = mode;
-
- if (mode == IEEE802154_ADDR_NONE)
+ if (mode == IEEE802154_ADDR_NONE) {
+ memset(addr, 0, sizeof(*addr));
+ addr->mode = IEEE802154_ADDR_NONE;
return 0;
+ }
+
+ addr->mode = mode;
if (!omit_pan) {
memcpy(&addr->pan_id, buf + pos, 2);
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index c5d91f78301a..e765adc4b88f 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -16,6 +16,15 @@ void ieee802154_nl_exit(void);
.flags = GENL_ADMIN_PERM, \
}
+#define IEEE802154_OP_RELAXED(_cmd, _func) \
+ { \
+ .cmd = _cmd, \
+ .doit = _func, \
+ .dumpit = NULL, \
+ .flags = GENL_ADMIN_PERM, \
+ .validate = GENL_DONT_VALIDATE_STRICT,\
+ }
+
#define IEEE802154_DUMP(_cmd, _func, _dump) \
{ \
.cmd = _cmd, \
@@ -23,6 +32,14 @@ void ieee802154_nl_exit(void);
.dumpit = _dump, \
}
+#define IEEE802154_DUMP_PRIV(_cmd, _func, _dump) \
+ { \
+ .cmd = _cmd, \
+ .doit = _func, \
+ .dumpit = _dump, \
+ .flags = GENL_ADMIN_PERM, \
+ }
+
struct genl_info;
struct sk_buff *ieee802154_nl_create(int flags, u8 req);
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 7d2de4ee6992..cacad21347ec 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -98,24 +98,24 @@ static const struct genl_small_ops ieee802154_ops[] = {
IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams),
IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL,
- ieee802154_llsec_dump_keys),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL,
- ieee802154_llsec_dump_devs),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
- ieee802154_llsec_dump_devkeys),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
- IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
- ieee802154_llsec_dump_seclevels),
- IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL,
- ieee802154_llsec_add_seclevel),
- IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL,
- ieee802154_llsec_del_seclevel),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_KEY, NULL,
+ ieee802154_llsec_dump_keys),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEV, NULL,
+ ieee802154_llsec_dump_devs),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
+ ieee802154_llsec_dump_devkeys),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
+ IEEE802154_DUMP_PRIV(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
+ ieee802154_llsec_dump_seclevels),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_ADD_SECLEVEL,
+ ieee802154_llsec_add_seclevel),
+ IEEE802154_OP_RELAXED(IEEE802154_LLSEC_DEL_SECLEVEL,
+ ieee802154_llsec_del_seclevel),
};
static const struct genl_multicast_group ieee802154_mcgrps[] = {
diff --git a/net/ife/ife.c b/net/ife/ife.c
index be05b690b9ef..7a75947a31e3 100644
--- a/net/ife/ife.c
+++ b/net/ife/ife.c
@@ -79,7 +79,7 @@ void *ife_decode(struct sk_buff *skb, u16 *metalen)
if (unlikely(ifehdrln < 2))
return NULL;
- if (unlikely(!pskb_may_pull(skb, total_pull)))
+ if (unlikely(!pskb_may_pull(skb, total_pull + ETH_HLEN)))
return NULL;
ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len);
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index 028d2d3477d1..0984f3303627 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -77,7 +77,7 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
/* We don't need to handle errors from xfrm_input, it does all
* the error handling and frees the resources on error. */
- xfrm_input(skb, IPPROTO_ESP, spi, -2);
+ xfrm_input(skb, IPPROTO_ESP, spi, 0);
return ERR_PTR(-EINPROGRESS);
out_reset:
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 2836020bf12d..732804f3a0aa 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -232,16 +232,20 @@ static void igmp_gq_start_timer(struct in_device *in_dev)
return;
in_dev->mr_gq_running = 1;
- if (!mod_timer(&in_dev->mr_gq_timer, exp))
- in_dev_hold(in_dev);
+ if (in_dev_hold_safe(in_dev)) {
+ if (mod_timer(&in_dev->mr_gq_timer, exp))
+ in_dev_put(in_dev);
+ }
}
static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
{
- int tv = get_random_u32_below(delay);
+ if (in_dev_hold_safe(in_dev)) {
+ int tv = get_random_u32_below(delay);
- if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
- in_dev_hold(in_dev);
+ if (mod_timer(&in_dev->mr_ifc_timer, jiffies + tv + 2))
+ in_dev_put(in_dev);
+ }
}
static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
@@ -1816,6 +1820,7 @@ void ip_mc_destroy_dev(struct in_device *in_dev)
#endif
while ((i = rtnl_dereference(in_dev->mc_list)) != NULL) {
+ ip_mc_hash_remove(in_dev, i);
in_dev->mc_list = i->next_rcu;
in_dev->mc_count--;
ip_mc_clear_src(i);
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 75d388dd5ac6..b5ebfee9a8fa 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -888,7 +888,8 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
return -(t->hlen + sizeof(*iph));
}
-static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int ipgre_header_parse(const struct sk_buff *skb, const struct net_device *dev,
+ unsigned char *haddr)
{
const struct iphdr *iph = (const struct iphdr *) skb_mac_header(skb);
memcpy(haddr, &iph->saddr, 4);
@@ -1412,6 +1413,9 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
struct ip_tunnel_parm p;
int err;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;
@@ -1441,6 +1445,9 @@ static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
struct ip_tunnel_parm p;
int err;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index d1e7d0ceb7ed..02744447e435 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -579,6 +579,9 @@ static int vti_changelink(struct net_device *dev, struct nlattr *tb[],
__u32 fwmark = t->fwmark;
struct ip_tunnel_parm p;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
vti_netlink_parms(data, &p, &fwmark);
return ip_tunnel_changelink(dev, tb, &p, fwmark);
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 27b8f83c6ea2..b7e935299153 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -457,6 +457,9 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
bool collect_md;
__u32 fwmark = t->fwmark;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
int err = ip_tunnel_encap_setup(t, &ipencap);
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 2064b4013041..1632cedcd348 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -80,7 +80,7 @@ static bool nf_skb_is_icmp_unreach(const struct sk_buff *skb)
if (iph->protocol != IPPROTO_ICMP)
return false;
- thoff = skb_network_offset(skb) + sizeof(*iph);
+ thoff = skb_network_offset(skb) + ip_hdrlen(skb);
tp = skb_header_pointer(skb,
thoff + offsetof(struct icmphdr, type),
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 9c827751dad6..47f2a00e457d 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -966,7 +966,9 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_tcp_reordering,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
+ .extra2 = &init_net.ipv4.sysctl_tcp_max_reordering,
},
{
.procname = "tcp_retries1",
@@ -1185,7 +1187,8 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_tcp_max_reordering,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
},
{
.procname = "tcp_dsack",
@@ -1533,6 +1536,9 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
*/
table[i].mode &= ~0222;
}
+ if (table[i].extra2 >= (void *)&init_net.ipv4 &&
+ table[i].extra2 < (void *)(&init_net.ipv4 + 1))
+ table[i].extra2 += (void *)net - (void *)&init_net;
}
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 429b94a86b5b..c66fede62fa8 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2412,7 +2412,7 @@ static int tcp_mtu_probe(struct sock *sk)
struct sk_buff *skb, *nskb, *next;
struct net *net = sock_net(sk);
int probe_size;
- int size_needed;
+ u64 size_needed;
int copy, len;
int mss_now;
int interval;
@@ -2436,7 +2436,7 @@ static int tcp_mtu_probe(struct sock *sk)
mss_now = tcp_current_mss(sk);
probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high +
icsk->icsk_mtup.search_low) >> 1);
- size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache;
+ size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache;
interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low;
/* When misfortune happens, we are reprobing actively,
* and then reprobe timer has expired. We stick with current
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d752776da51f..83dc90a8990c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -916,7 +916,7 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
if (newf)
rt6_purge_dflt_routers(net);
- return 1;
+ return 0;
}
static void addrconf_linkdown_change(struct net *net, __s32 newf)
@@ -2138,16 +2138,18 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
struct inet6_dev *idev = ifp->idev;
struct net *net = dev_net(idev->dev);
- if (addrconf_dad_end(ifp)) {
+ spin_lock_bh(&ifp->lock);
+
+ if (ifp->state != INET6_IFADDR_STATE_DAD) {
+ spin_unlock_bh(&ifp->lock);
in6_ifa_put(ifp);
return;
}
+ ifp->state = INET6_IFADDR_STATE_POSTDAD;
net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n",
ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
- spin_lock_bh(&ifp->lock);
-
if (ifp->flags & IFA_F_STABLE_PRIVACY) {
struct in6_addr new_addr;
struct inet6_ifaddr *ifp2;
@@ -2195,6 +2197,11 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
in6_ifa_put(ifp2);
lock_errdad:
spin_lock_bh(&ifp->lock);
+ if (ifp->state != INET6_IFADDR_STATE_POSTDAD) {
+ spin_unlock_bh(&ifp->lock);
+ in6_ifa_put(ifp);
+ return;
+ }
}
errdad:
@@ -6337,6 +6344,8 @@ static int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
lctl.data = &val;
ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
+ if (ret)
+ return ret;
if (write)
ret = addrconf_fixup_forwarding(ctl, valp, val);
@@ -6434,6 +6443,8 @@ static int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
lctl.data = &val;
ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
+ if (ret)
+ return ret;
if (write)
ret = addrconf_disable_ipv6(ctl, valp, val);
@@ -6630,6 +6641,8 @@ int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
lctl.data = &val;
ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
+ if (ret)
+ return ret;
if (write)
ret = addrconf_fixup_linkdown(ctl, valp, val);
@@ -6725,6 +6738,8 @@ static int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write,
lctl = *ctl;
lctl.data = &val;
ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
+ if (ret)
+ return ret;
if (write && (*valp != val))
ret = addrconf_disable_policy(ctl, valp, val);
diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c
index 333d87557bf3..84affcc7f4d7 100644
--- a/net/ipv6/esp6_offload.c
+++ b/net/ipv6/esp6_offload.c
@@ -104,7 +104,7 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
/* We don't need to handle errors from xfrm_input, it does all
* the error handling and frees the resources on error. */
- xfrm_input(skb, IPPROTO_ESP, spi, -2);
+ xfrm_input(skb, IPPROTO_ESP, spi, 0);
return ERR_PTR(-EINPROGRESS);
out_reset:
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index b10d2e59d8c4..74442c9fb4ab 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -2089,6 +2089,9 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
struct __ip6_tnl_parm p;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
t = ip6gre_changelink_common(dev, tb, data, &p, extack);
if (IS_ERR(t))
return PTR_ERR(t);
@@ -2303,6 +2306,9 @@ static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
struct __ip6_tnl_parm p;
struct ip6gre_net *ign;
+ if (!rtnl_dev_link_net_capable(dev, t->net))
+ return -EPERM;
+
ign = net_generic(t->net, ip6gre_net_id);
t = ip6gre_changelink_common(dev, tb, data, &p, extack);
if (IS_ERR(t))
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 95cf8c52953c..1bdd5e50c9b8 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -1051,6 +1051,9 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
struct __ip6_tnl_parm p;
struct vti6_net *ip6n;
+ if (!rtnl_dev_link_net_capable(dev, net))
+ return -EPERM;
+
ip6n = net_generic(net, vti6_net_id);
if (dev == ip6n->fb_tnl_dev)
return -EINVAL;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5bd96298d3bb..dacd9e32e96e 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -105,9 +105,9 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
int sysctl_mld_qrv __read_mostly = MLD_QRV_DEFAULT;
-/*
- * socket join on multicast group
- */
+#define mc_assert_locked(idev) \
+ lockdep_assert_held(&(idev)->mc_lock)
+
#define mc_dereference(e, idev) \
rcu_dereference_protected(e, lockdep_is_held(&(idev)->mc_lock))
@@ -166,6 +166,9 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
return iv > 0 ? iv : 1;
}
+/*
+ * socket join on multicast group
+ */
static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
const struct in6_addr *addr, unsigned int mode)
{
@@ -664,12 +667,13 @@ bool inet6_mc_check(const struct sock *sk, const struct in6_addr *mc_addr,
return rv;
}
-/* called with mc_lock */
static void igmp6_group_added(struct ifmcaddr6 *mc)
{
struct net_device *dev = mc->idev->dev;
char buf[MAX_ADDR_LEN];
+ mc_assert_locked(mc->idev);
+
if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
return;
@@ -699,12 +703,13 @@ static void igmp6_group_added(struct ifmcaddr6 *mc)
mld_ifc_event(mc->idev);
}
-/* called with mc_lock */
static void igmp6_group_dropped(struct ifmcaddr6 *mc)
{
struct net_device *dev = mc->idev->dev;
char buf[MAX_ADDR_LEN];
+ mc_assert_locked(mc->idev);
+
if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) <
IPV6_ADDR_SCOPE_LINKLOCAL)
return;
@@ -725,14 +730,13 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc)
refcount_dec(&mc->mca_refcnt);
}
-/*
- * deleted ifmcaddr6 manipulation
- * called with mc_lock
- */
+/* deleted ifmcaddr6 manipulation */
static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
{
struct ifmcaddr6 *pmc;
+ mc_assert_locked(idev);
+
/* this is an "ifmcaddr6" for convenience; only the fields below
* are actually used. In particular, the refcnt and users are not
* used for management of the delete list. Using the same structure
@@ -766,13 +770,14 @@ static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
rcu_assign_pointer(idev->mc_tomb, pmc);
}
-/* called with mc_lock */
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
{
struct ip6_sf_list *psf, *sources, *tomb;
struct in6_addr *pmca = &im->mca_addr;
struct ifmcaddr6 *pmc, *pmc_prev;
+ mc_assert_locked(idev);
+
pmc_prev = NULL;
for_each_mc_tomb(idev, pmc) {
if (ipv6_addr_equal(&pmc->mca_addr, pmca))
@@ -809,11 +814,12 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
}
}
-/* called with mc_lock */
static void mld_clear_delrec(struct inet6_dev *idev)
{
struct ifmcaddr6 *pmc, *nextpmc;
+ mc_assert_locked(idev);
+
pmc = mc_dereference(idev->mc_tomb, idev);
RCU_INIT_POINTER(idev->mc_tomb, NULL);
@@ -870,13 +876,14 @@ static void ma_put(struct ifmcaddr6 *mc)
}
}
-/* called with mc_lock */
static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
const struct in6_addr *addr,
unsigned int mode)
{
struct ifmcaddr6 *mc;
+ mc_assert_locked(idev);
+
mc = kzalloc(sizeof(*mc), GFP_KERNEL);
if (!mc)
return NULL;
@@ -1046,48 +1053,59 @@ bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
return rv;
}
-/* called with mc_lock */
static void mld_gq_start_work(struct inet6_dev *idev)
{
unsigned long tv = get_random_u32_below(idev->mc_maxdelay);
+ mc_assert_locked(idev);
+
idev->mc_gq_running = 1;
- if (!mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
- in6_dev_hold(idev);
+ if (in6_dev_hold_safe(idev)) {
+ if (mod_delayed_work(mld_wq, &idev->mc_gq_work, tv + 2))
+ in6_dev_put(idev);
+ }
}
-/* called with mc_lock */
static void mld_gq_stop_work(struct inet6_dev *idev)
{
+ mc_assert_locked(idev);
+
idev->mc_gq_running = 0;
if (cancel_delayed_work(&idev->mc_gq_work))
__in6_dev_put(idev);
}
-/* called with mc_lock */
static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)
{
unsigned long tv = get_random_u32_below(delay);
- if (!mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
- in6_dev_hold(idev);
+ mc_assert_locked(idev);
+
+ if (in6_dev_hold_safe(idev)) {
+ if (mod_delayed_work(mld_wq, &idev->mc_ifc_work, tv + 2))
+ in6_dev_put(idev);
+ }
}
-/* called with mc_lock */
static void mld_ifc_stop_work(struct inet6_dev *idev)
{
+ mc_assert_locked(idev);
+
idev->mc_ifc_count = 0;
if (cancel_delayed_work(&idev->mc_ifc_work))
__in6_dev_put(idev);
}
-/* called with mc_lock */
static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)
{
unsigned long tv = get_random_u32_below(delay);
- if (!mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
- in6_dev_hold(idev);
+ mc_assert_locked(idev);
+
+ if (in6_dev_hold_safe(idev)) {
+ if (mod_delayed_work(mld_wq, &idev->mc_dad_work, tv + 2))
+ in6_dev_put(idev);
+ }
}
static void mld_dad_stop_work(struct inet6_dev *idev)
@@ -1110,14 +1128,13 @@ static void mld_report_stop_work(struct inet6_dev *idev)
__in6_dev_put(idev);
}
-/*
- * IGMP handling (alias multicast ICMPv6 messages)
- * called with mc_lock
- */
+/* IGMP handling (alias multicast ICMPv6 messages) */
static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
{
unsigned long delay = resptime;
+ mc_assert_locked(ma->idev);
+
/* Do not start work for these addresses */
if (ipv6_addr_is_ll_all_nodes(&ma->mca_addr) ||
IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)
@@ -1136,15 +1153,15 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
ma->mca_flags |= MAF_TIMER_RUNNING;
}
-/* mark EXCLUDE-mode sources
- * called with mc_lock
- */
+/* mark EXCLUDE-mode sources */
static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,
const struct in6_addr *srcs)
{
struct ip6_sf_list *psf;
int i, scount;
+ mc_assert_locked(pmc->idev);
+
scount = 0;
for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
@@ -1167,13 +1184,14 @@ static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,
return true;
}
-/* called with mc_lock */
static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
const struct in6_addr *srcs)
{
struct ip6_sf_list *psf;
int i, scount;
+ mc_assert_locked(pmc->idev);
+
if (pmc->mca_sfmode == MCAST_EXCLUDE)
return mld_xmarksources(pmc, nsrcs, srcs);
@@ -1373,18 +1391,23 @@ static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
void igmp6_event_query(struct sk_buff *skb)
{
struct inet6_dev *idev = __in6_dev_get(skb->dev);
+ bool put = false;
if (!idev || idev->dead)
goto out;
spin_lock_bh(&idev->mc_query_lock);
- if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS) {
+ if (skb_queue_len(&idev->mc_query_queue) < MLD_MAX_SKBS &&
+ in6_dev_hold_safe(idev)) {
__skb_queue_tail(&idev->mc_query_queue, skb);
- if (!mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
- in6_dev_hold(idev);
+ if (mod_delayed_work(mld_wq, &idev->mc_query_work, 0))
+ put = true;
skb = NULL;
}
spin_unlock_bh(&idev->mc_query_lock);
+
+ if (put)
+ in6_dev_put(idev);
out:
kfree_skb(skb);
}
@@ -1541,18 +1564,23 @@ static void mld_query_work(struct work_struct *work)
void igmp6_event_report(struct sk_buff *skb)
{
struct inet6_dev *idev = __in6_dev_get(skb->dev);
+ bool put = false;
if (!idev || idev->dead)
goto out;
spin_lock_bh(&idev->mc_report_lock);
- if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS) {
+ if (skb_queue_len(&idev->mc_report_queue) < MLD_MAX_SKBS &&
+ in6_dev_hold_safe(idev)) {
__skb_queue_tail(&idev->mc_report_queue, skb);
- if (!mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
- in6_dev_hold(idev);
+ if (mod_delayed_work(mld_wq, &idev->mc_report_work, 0))
+ put = true;
skb = NULL;
}
spin_unlock_bh(&idev->mc_report_lock);
+
+ if (put)
+ in6_dev_put(idev);
out:
kfree_skb(skb);
}
@@ -1868,7 +1896,6 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
-/* called with mc_lock */
static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
int type, int gdeleted, int sdeleted,
int crsend)
@@ -1882,6 +1909,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
struct mld2_report *pmr;
unsigned int mtu;
+ mc_assert_locked(idev);
+
if (pmc->mca_flags & MAF_NOREPORT)
return skb;
@@ -2000,12 +2029,13 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
return skb;
}
-/* called with mc_lock */
static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
{
struct sk_buff *skb = NULL;
int type;
+ mc_assert_locked(idev);
+
if (!pmc) {
for_each_mc_mclock(idev, pmc) {
if (pmc->mca_flags & MAF_NOREPORT)
@@ -2027,10 +2057,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
mld_sendpack(skb);
}
-/*
- * remove zero-count source records from a source filter list
- * called with mc_lock
- */
+/* remove zero-count source records from a source filter list */
static void mld_clear_zeros(struct ip6_sf_list __rcu **ppsf, struct inet6_dev *idev)
{
struct ip6_sf_list *psf_prev, *psf_next, *psf;
@@ -2054,7 +2081,6 @@ static void mld_clear_zeros(struct ip6_sf_list __rcu **ppsf, struct inet6_dev *i
}
}
-/* called with mc_lock */
static void mld_send_cr(struct inet6_dev *idev)
{
struct ifmcaddr6 *pmc, *pmc_prev, *pmc_next;
@@ -2218,13 +2244,14 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
goto out;
}
-/* called with mc_lock */
static void mld_send_initial_cr(struct inet6_dev *idev)
{
- struct sk_buff *skb;
struct ifmcaddr6 *pmc;
+ struct sk_buff *skb;
int type;
+ mc_assert_locked(idev);
+
if (mld_in_v1_mode(idev))
return;
@@ -2271,13 +2298,14 @@ static void mld_dad_work(struct work_struct *work)
in6_dev_put(idev);
}
-/* called with mc_lock */
static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
- const struct in6_addr *psfsrc)
+ const struct in6_addr *psfsrc)
{
struct ip6_sf_list *psf, *psf_prev;
int rv = 0;
+ mc_assert_locked(pmc->idev);
+
psf_prev = NULL;
for_each_psf_mclock(pmc, psf) {
if (ipv6_addr_equal(&psf->sf_addr, psfsrc))
@@ -2314,7 +2342,6 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
return rv;
}
-/* called with mc_lock */
static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
int sfmode, int sfcount, const struct in6_addr *psfsrc,
int delta)
@@ -2326,6 +2353,8 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
if (!idev)
return -ENODEV;
+ mc_assert_locked(idev);
+
for_each_mc_mclock(idev, pmc) {
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
@@ -2367,15 +2396,14 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
return err;
}
-/*
- * Add multicast single-source filter to the interface list
- * called with mc_lock
- */
+/* Add multicast single-source filter to the interface list */
static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode,
- const struct in6_addr *psfsrc)
+ const struct in6_addr *psfsrc)
{
struct ip6_sf_list *psf, *psf_prev;
+ mc_assert_locked(pmc->idev);
+
psf_prev = NULL;
for_each_psf_mclock(pmc, psf) {
if (ipv6_addr_equal(&psf->sf_addr, psfsrc))
@@ -2398,11 +2426,12 @@ static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode,
return 0;
}
-/* called with mc_lock */
static void sf_markstate(struct ifmcaddr6 *pmc)
{
- struct ip6_sf_list *psf;
int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE];
+ struct ip6_sf_list *psf;
+
+ mc_assert_locked(pmc->idev);
for_each_psf_mclock(pmc, psf) {
if (pmc->mca_sfcount[MCAST_EXCLUDE]) {
@@ -2415,14 +2444,15 @@ static void sf_markstate(struct ifmcaddr6 *pmc)
}
}
-/* called with mc_lock */
static int sf_setstate(struct ifmcaddr6 *pmc)
{
- struct ip6_sf_list *psf, *dpsf;
int mca_xcount = pmc->mca_sfcount[MCAST_EXCLUDE];
+ struct ip6_sf_list *psf, *dpsf;
int qrv = pmc->idev->mc_qrv;
int new_in, rv;
+ mc_assert_locked(pmc->idev);
+
rv = 0;
for_each_psf_mclock(pmc, psf) {
if (pmc->mca_sfcount[MCAST_EXCLUDE]) {
@@ -2481,10 +2511,7 @@ static int sf_setstate(struct ifmcaddr6 *pmc)
return rv;
}
-/*
- * Add multicast source filter list to the interface list
- * called with mc_lock
- */
+/* Add multicast source filter list to the interface list */
static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
int sfmode, int sfcount, const struct in6_addr *psfsrc,
int delta)
@@ -2496,6 +2523,8 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
if (!idev)
return -ENODEV;
+ mc_assert_locked(idev);
+
for_each_mc_mclock(idev, pmc) {
if (ipv6_addr_equal(pmca, &pmc->mca_addr))
break;
@@ -2541,11 +2570,12 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
return err;
}
-/* called with mc_lock */
static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
{
struct ip6_sf_list *psf, *nextpsf;
+ mc_assert_locked(pmc->idev);
+
for (psf = mc_dereference(pmc->mca_tomb, pmc->idev);
psf;
psf = nextpsf) {
@@ -2565,11 +2595,12 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
pmc->mca_sfcount[MCAST_EXCLUDE] = 1;
}
-/* called with mc_lock */
static void igmp6_join_group(struct ifmcaddr6 *ma)
{
unsigned long delay;
+ mc_assert_locked(ma->idev);
+
if (ma->mca_flags & MAF_NOREPORT)
return;
@@ -2616,9 +2647,10 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
return err;
}
-/* called with mc_lock */
static void igmp6_leave_group(struct ifmcaddr6 *ma)
{
+ mc_assert_locked(ma->idev);
+
if (mld_in_v1_mode(ma->idev)) {
if (ma->mca_flags & MAF_LAST_REPORTER) {
igmp6_send(&ma->mca_addr, ma->idev->dev,
@@ -2663,9 +2695,10 @@ static void mld_ifc_work(struct work_struct *work)
in6_dev_put(idev);
}
-/* called with mc_lock */
static void mld_ifc_event(struct inet6_dev *idev)
{
+ mc_assert_locked(idev);
+
if (mld_in_v1_mode(idev))
return;
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 606aae4e78a9..72ee74fa27c4 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -130,7 +130,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
ktime_t tstamp = skb->tstamp;
struct ip6_frag_state state;
u8 *prevhdr, nexthdr = 0;
- unsigned int mtu, hlen;
+ unsigned int mtu, hlen, nexthdr_offset;
int hroom, err = 0;
__be32 frag_id;
@@ -139,6 +139,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
goto blackhole;
hlen = err;
nexthdr = *prevhdr;
+ nexthdr_offset = prevhdr - skb_network_header(skb);
mtu = skb->dev->mtu;
if (frag_max_size > mtu ||
@@ -157,6 +158,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
(err = skb_checksum_help(skb)))
goto blackhole;
+ prevhdr = skb_network_header(skb) + nexthdr_offset;
hroom = LL_RESERVED_SPACE(skb->dev);
if (skb_has_frag_list(skb)) {
unsigned int first_len = skb_pagelen(skb);
diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c
index 70da2f2ce064..1258783ed876 100644
--- a/net/ipv6/netfilter/ip6t_ah.c
+++ b/net/ipv6/netfilter/ip6t_ah.c
@@ -56,6 +56,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par)
}
hdrlen = ipv6_authlen(ah);
+ if (skb->len - ptr < hdrlen) {
+ /* Packet smaller than its length field */
+ par->hotdrop = true;
+ return false;
+ }
pr_debug("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
pr_debug("RES %04X ", ah->reserved);
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c
index 450dd53846a2..6d1a5d2026a6 100644
--- a/net/ipv6/netfilter/ip6t_hbh.c
+++ b/net/ipv6/netfilter/ip6t_hbh.c
@@ -75,6 +75,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par)
hdrlen = ipv6_optlen(oh);
if (skb->len - ptr < hdrlen) {
/* Packet smaller than it's length field */
+ par->hotdrop = true;
return false;
}
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c
index 5561bd9cea81..278b52752f36 100644
--- a/net/ipv6/netfilter/ip6t_rt.c
+++ b/net/ipv6/netfilter/ip6t_rt.c
@@ -56,7 +56,8 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par)
hdrlen = ipv6_optlen(rh);
if (skb->len - ptr < hdrlen) {
- /* Pcket smaller than its length field */
+ /* Packet smaller than its length field */
+ par->hotdrop = true;
return false;
}
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 82e51b2ec4f5..18e0e1c76c1e 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -346,7 +346,8 @@ static int nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *skb,
skb_network_header(skb)[fq->nhoffset] = skb_transport_header(skb)[0];
memmove(skb->head + sizeof(struct frag_hdr), skb->head,
(skb->data - skb->head) - sizeof(struct frag_hdr));
- skb->mac_header += sizeof(struct frag_hdr);
+ if (skb_mac_header_was_set(skb))
+ skb->mac_header += sizeof(struct frag_hdr);
skb->network_header += sizeof(struct frag_hdr);
skb_reset_transport_header(skb);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 6d80e17c04c0..e6f1a22bc2be 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3251,11 +3251,11 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
/*
* Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
* corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
- * IPV6_MAXPLEN is also valid and means: "any MSS,
- * rely only on pmtu discovery"
+ * Limit the default MSS to GSO_BY_FRAGS - 1 to avoid
+ * collision with the GSO_BY_FRAGS magic value (0xFFFF).
*/
if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
- mtu = IPV6_MAXPLEN;
+ mtu = min_t(unsigned int, IPV6_MAXPLEN, GSO_BY_FRAGS - 1);
return mtu;
}
@@ -4980,6 +4980,9 @@ static int fib6_nh_mtu_change(struct fib6_nh *nh, void *_arg)
struct inet6_dev *idev = __in6_dev_get(arg->dev);
u32 mtu = f6i->fib6_pmtu;
+ if (!idev)
+ return 0;
+
if (mtu >= arg->mtu ||
(mtu < arg->mtu && mtu == idev->cnf.mtu6))
fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index a31521e270f7..5a50755ec69f 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -31,6 +31,9 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
int max_last_entry;
int trailing;
+ if (len < sizeof(*srh))
+ return false;
+
if (srh->type != IPV6_SRCRT_TYPE_4)
return false;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index aa88a41034d9..ac38d4aa09dd 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1615,6 +1615,9 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
__u32 fwmark = t->fwmark;
int err;
+ if (!rtnl_dev_link_net_capable(dev, net))
+ return -EPERM;
+
if (dev == sitn->fb_tunnel_dev)
return -EINVAL;
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index 829eb67240a9..a47afaaea2a1 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1281,8 +1281,8 @@ static int kcm_attach(struct socket *sock, struct socket *csock,
psock->save_write_space = csk->sk_write_space;
psock->save_state_change = csk->sk_state_change;
csk->sk_user_data = psock;
- csk->sk_data_ready = psock_data_ready;
- csk->sk_write_space = psock_write_space;
+ WRITE_ONCE(csk->sk_data_ready, psock_data_ready);
+ WRITE_ONCE(csk->sk_write_space, psock_write_space);
csk->sk_state_change = psock_state_change;
write_unlock_bh(&csk->sk_callback_lock);
@@ -1358,8 +1358,8 @@ static void kcm_unattach(struct kcm_psock *psock)
*/
write_lock_bh(&csk->sk_callback_lock);
csk->sk_user_data = NULL;
- csk->sk_data_ready = psock->save_data_ready;
- csk->sk_write_space = psock->save_write_space;
+ WRITE_ONCE(csk->sk_data_ready, psock->save_data_ready);
+ WRITE_ONCE(csk->sk_write_space, psock->save_write_space);
csk->sk_state_change = psock->save_state_change;
strp_stop(&psock->strp);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 13779cc97259..2c86312a615d 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1218,6 +1218,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
goto out;
}
strcpy(x->calg->alg_name, a->name);
+ x->calg->alg_key_len = 0;
x->props.calgo = sa->sadb_sa_encrypt;
} else {
int keysize = 0;
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index 19f3de3c24ef..f6b6040e0f69 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -317,6 +317,7 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
/* assign new connection to its SAP */
llc_sap_add_socket(sap, sk);
sock_reset_flag(sk, SOCK_ZAPPED);
+ llc_sap_put(sap);
rc = 0;
out:
dev_put(dev);
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 0a3f5e0bec00..e363b443fae9 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -773,7 +773,6 @@ static struct sock *llc_create_incoming_sock(struct sock *sk,
newllc->dev = dev;
dev_hold(dev);
llc_sap_add_socket(llc->sap, newsk);
- llc_sap_hold(llc->sap);
out:
return newsk;
}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 80b143bde93d..a5674e52d7fe 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1410,7 +1410,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL);
if (!sband) {
result = -ENOMEM;
- goto fail_rate;
+ goto fail_band;
}
wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n",
@@ -1475,6 +1475,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
#endif
wiphy_unregister(local->hw.wiphy);
fail_wiphy_register:
+ fail_band:
rtnl_lock();
rate_control_deinitialize(local);
ieee80211_remove_interfaces(local);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2a708132320c..e3841099bbda 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2568,6 +2568,18 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local,
return info_id;
}
+static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id)
+{
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_remove(&local->ack_status_frames, info_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ kfree_skb(ack_skb);
+}
+
/**
* ieee80211_build_hdr - build 802.11 header in the given frame
* @sdata: virtual interface to build the header for
@@ -2925,7 +2937,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto free;
}
}
@@ -2990,6 +3003,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
return skb;
free:
+ if (info_id)
+ ieee80211_remove_ack_skb(local, info_id);
kfree_skb(skb);
return ERR_PTR(ret);
}
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
index 9e4631fade90..b823720630e7 100644
--- a/net/mac802154/iface.c
+++ b/net/mac802154/iface.c
@@ -469,7 +469,9 @@ static int mac802154_header_create(struct sk_buff *skb,
}
static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+mac802154_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
{
struct ieee802154_hdr hdr;
@@ -701,7 +703,7 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local)
mutex_lock(&local->iflist_mtx);
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
- list_del(&sdata->list);
+ list_del_rcu(&sdata->list);
unregister_netdevice(sdata->dev);
}
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index a4e1d7951b2c..f51a1af31513 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -351,8 +351,8 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
if (unlikely(c)) {
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
- kfree_rcu(c, rcu);
rcu_assign_pointer(comment->c, NULL);
+ kfree_rcu(c, rcu);
}
if (!len)
return;
@@ -393,8 +393,8 @@ ip_set_comment_free(struct ip_set *set, void *ptr)
if (unlikely(!c))
return;
set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
- kfree_rcu(c, rcu);
rcu_assign_pointer(comment->c, NULL);
+ kfree_rcu(c, rcu);
}
typedef void (*destroyer)(struct ip_set *, void *);
@@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb)
struct ip_set_net *inst =
(struct ip_set_net *)cb->args[IPSET_CB_NET];
ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX];
- struct ip_set *set = ip_set_ref_netlink(inst, index);
+ struct ip_set *set;
+
+ rcu_read_lock();
+ set = ip_set_ref_netlink(inst, index);
+ rcu_read_unlock();
if (set->variant->uref)
set->variant->uref(set, cb, false);
@@ -1685,7 +1689,9 @@ ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb)
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[IPSET_CB_ARG0]) {
+ rcu_read_lock();
set = ip_set_ref_netlink(inst, index);
+ rcu_read_unlock();
if (set->variant->uref)
set->variant->uref(set, cb, false);
pr_debug("release set %s\n", set->name);
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index 9da942070321..ad2ff9cc0b58 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -385,8 +385,9 @@ static void
mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
{
int i;
+ u8 pos = smp_load_acquire(&n->pos);
- for (i = 0; i < n->pos; i++)
+ for (i = 0; i < pos; i++)
if (test_bit(i, n->used))
ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
}
@@ -489,7 +490,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
#ifdef IP_SET_HASH_WITH_NETS
u8 k;
#endif
- u8 htable_bits = t->htable_bits;
+ u8 pos, htable_bits = t->htable_bits;
spin_lock_bh(&t->hregion[r].lock);
for (i = ahash_bucket_start(r, htable_bits);
@@ -497,7 +498,8 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
n = __ipset_dereference(hbucket(t, i));
if (!n)
continue;
- for (j = 0, d = 0; j < n->pos; j++) {
+ pos = smp_load_acquire(&n->pos);
+ for (j = 0, d = 0; j < pos; j++) {
if (!test_bit(j, n->used)) {
d++;
continue;
@@ -533,7 +535,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
/* Still try to delete expired elements. */
continue;
tmp->size = n->size - AHASH_INIT_SIZE;
- for (j = 0, d = 0; j < n->pos; j++) {
+ for (j = 0, d = 0; j < pos; j++) {
if (!test_bit(j, n->used))
continue;
data = ahash_data(n, j, dsize);
@@ -622,7 +624,7 @@ mtype_resize(struct ip_set *set, bool retried)
{
struct htype *h = set->data;
struct htable *t, *orig;
- u8 htable_bits;
+ u8 pos, htable_bits;
size_t hsize, dsize = set->dsize;
#ifdef IP_SET_HASH_WITH_NETS
u8 flags;
@@ -684,8 +686,9 @@ mtype_resize(struct ip_set *set, bool retried)
n = __ipset_dereference(hbucket(orig, i));
if (!n)
continue;
- for (j = 0; j < n->pos; j++) {
- if (!test_bit(j, n->used))
+ pos = smp_load_acquire(&n->pos);
+ for (j = 0; j < pos; j++) {
+ if (!test_bit_acquire(j, n->used))
continue;
data = ahash_data(n, j, dsize);
if (SET_ELEM_EXPIRED(set, data))
@@ -808,9 +811,10 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size)
{
struct htype *h = set->data;
const struct htable *t;
- u32 i, j, r;
struct hbucket *n;
struct mtype_elem *data;
+ u32 i, j, r;
+ u8 pos;
t = rcu_dereference_bh(h->table);
for (r = 0; r < ahash_numof_locks(t->htable_bits); r++) {
@@ -819,8 +823,9 @@ mtype_ext_size(struct ip_set *set, u32 *elements, size_t *ext_size)
n = rcu_dereference_bh(hbucket(t, i));
if (!n)
continue;
- for (j = 0; j < n->pos; j++) {
- if (!test_bit(j, n->used))
+ pos = smp_load_acquire(&n->pos);
+ for (j = 0; j < pos; j++) {
+ if (!test_bit_acquire(j, n->used))
continue;
data = ahash_data(n, j, set->dsize);
if (!SET_ELEM_EXPIRED(set, data))
@@ -847,6 +852,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
bool flag_exist = flags & IPSET_FLAG_EXIST;
bool deleted = false, forceadd = false, reuse = false;
u32 r, key, multi = 0, elements, maxelem;
+ u8 npos = 0;
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
@@ -888,7 +894,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
ext_size(AHASH_INIT_SIZE, set->dsize);
goto copy_elem;
}
- for (i = 0; i < n->pos; i++) {
+ npos = smp_load_acquire(&n->pos);
+ for (i = 0; i < npos; i++) {
if (!test_bit(i, n->used)) {
/* Reuse first deleted entry */
if (j == -1) {
@@ -932,7 +939,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (elements >= maxelem)
goto set_full;
/* Create a new slot */
- if (n->pos >= n->size) {
+ if (npos >= n->size) {
#ifdef IP_SET_HASH_WITH_MULTI
if (h->bucketsize >= AHASH_MAX_TUNED)
goto set_full;
@@ -961,7 +968,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
}
copy_elem:
- j = n->pos++;
+ j = npos++;
data = ahash_data(n, j, set->dsize);
copy_data:
t->hregion[r].elements++;
@@ -984,6 +991,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (SET_WITH_TIMEOUT(set))
ip_set_timeout_set(ext_timeout(data, set), ext->timeout);
smp_mb__before_atomic();
+ /* Ensure all data writes are visible before updating position */
+ smp_store_release(&n->pos, npos);
set_bit(j, n->used);
if (old != ERR_PTR(-ENOENT)) {
rcu_assign_pointer(hbucket(t, key), n);
@@ -1042,6 +1051,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
int i, j, k, r, ret = -IPSET_ERR_EXIST;
u32 key, multi = 0;
size_t dsize = set->dsize;
+ u8 pos;
/* Userspace add and resize is excluded by the mutex.
* Kernespace add does not trigger resize.
@@ -1057,7 +1067,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
n = rcu_dereference_bh(hbucket(t, key));
if (!n)
goto out;
- for (i = 0, k = 0; i < n->pos; i++) {
+ pos = smp_load_acquire(&n->pos);
+ for (i = 0, k = 0; i < pos; i++) {
if (!test_bit(i, n->used)) {
k++;
continue;
@@ -1071,8 +1082,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
ret = 0;
clear_bit(i, n->used);
smp_mb__after_atomic();
- if (i + 1 == n->pos)
- n->pos--;
+ if (i + 1 == pos)
+ smp_store_release(&n->pos, --pos);
t->hregion[r].elements--;
#ifdef IP_SET_HASH_WITH_NETS
for (j = 0; j < IPSET_NET_COUNT; j++)
@@ -1094,11 +1105,11 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
x->flags = flags;
}
}
- for (; i < n->pos; i++) {
+ for (; i < pos; i++) {
if (!test_bit(i, n->used))
k++;
}
- if (k == n->pos) {
+ if (k == pos) {
t->hregion[r].ext_size -= ext_size(n->size, dsize);
rcu_assign_pointer(hbucket(t, key), NULL);
kfree_rcu(n, rcu);
@@ -1109,7 +1120,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (!tmp)
goto out;
tmp->size = n->size - AHASH_INIT_SIZE;
- for (j = 0, k = 0; j < n->pos; j++) {
+ for (j = 0, k = 0; j < pos; j++) {
if (!test_bit(j, n->used))
continue;
data = ahash_data(n, j, dsize);
@@ -1170,6 +1181,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
int ret, i, j = 0;
#endif
u32 key, multi = 0;
+ u8 pos;
pr_debug("test by nets\n");
for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) {
@@ -1187,8 +1199,9 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
n = rcu_dereference_bh(hbucket(t, key));
if (!n)
continue;
- for (i = 0; i < n->pos; i++) {
- if (!test_bit(i, n->used))
+ pos = smp_load_acquire(&n->pos);
+ for (i = 0; i < pos; i++) {
+ if (!test_bit_acquire(i, n->used))
continue;
data = ahash_data(n, i, set->dsize);
if (!mtype_data_equal(data, d, &multi))
@@ -1221,6 +1234,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
struct mtype_elem *data;
int i, ret = 0;
u32 key, multi = 0;
+ u8 pos;
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
@@ -1243,8 +1257,9 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
ret = 0;
goto out;
}
- for (i = 0; i < n->pos; i++) {
- if (!test_bit(i, n->used))
+ pos = smp_load_acquire(&n->pos);
+ for (i = 0; i < pos; i++) {
+ if (!test_bit_acquire(i, n->used))
continue;
data = ahash_data(n, i, set->dsize);
if (!mtype_data_equal(data, d, &multi))
@@ -1360,6 +1375,7 @@ mtype_list(const struct ip_set *set,
/* We assume that one hash bucket fills into one page */
void *incomplete;
int i, ret = 0;
+ u8 pos;
atd = nla_nest_start(skb, IPSET_ATTR_ADT);
if (!atd)
@@ -1378,8 +1394,9 @@ mtype_list(const struct ip_set *set,
cb->args[IPSET_CB_ARG0], t, n);
if (!n)
continue;
- for (i = 0; i < n->pos; i++) {
- if (!test_bit(i, n->used))
+ pos = smp_load_acquire(&n->pos);
+ for (i = 0; i < pos; i++) {
+ if (!test_bit_acquire(i, n->used))
continue;
e = ahash_data(n, i, set->dsize);
if (SET_ELEM_EXPIRED(set, e))
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
index fdacbc3c15be..f9132b359f0c 100644
--- a/net/netfilter/ipvs/ip_vs_app.c
+++ b/net/netfilter/ipvs/ip_vs_app.c
@@ -362,14 +362,13 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
struct ip_vs_iphdr *ipvsh)
{
int diff;
- const unsigned int tcp_offset = ip_hdrlen(skb);
struct tcphdr *th;
__u32 seq;
- if (skb_ensure_writable(skb, tcp_offset + sizeof(*th)))
+ if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
+ th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
@@ -439,14 +438,13 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
struct ip_vs_iphdr *ipvsh)
{
int diff;
- const unsigned int tcp_offset = ip_hdrlen(skb);
struct tcphdr *th;
__u32 seq;
- if (skb_ensure_writable(skb, tcp_offset + sizeof(*th)))
+ if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
+ th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index d9c3eefe8cbb..9636478e0284 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -995,8 +995,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af,
cp->app = NULL;
cp->app_data = NULL;
/* reset struct ip_vs_seq */
- cp->in_seq.delta = 0;
- cp->out_seq.delta = 0;
+ memset(&cp->in_seq, 0, sizeof(cp->in_seq));
+ memset(&cp->out_seq, 0, sizeof(cp->out_seq));
atomic_inc(&ipvs->conn_count);
if (flags & IP_VS_CONN_F_NO_CPORT)
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 34c4648ba36e..fcd8e0857de2 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -220,10 +220,10 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
static inline void
ip_vs_set_state(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd, unsigned int iph_len)
{
if (likely(pd->pp->state_transition))
- pd->pp->state_transition(cp, direction, skb, pd);
+ pd->pp->state_transition(cp, direction, skb, pd, iph_len);
}
static inline int
@@ -625,7 +625,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
ip_vs_in_stats(cp, skb);
/* set state */
- ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+ ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph->len);
/* transmit the first SYN packet */
ret = cp->packet_xmit(skb, cp, pd->pp, iph);
@@ -1041,8 +1041,7 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
snet.in6 = ciph.saddr.in6;
offset = ciph.len;
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
- pp, offset, sizeof(struct ipv6hdr),
- hooknum);
+ pp, offset, ipvsh->len, hooknum);
}
#endif
@@ -1306,7 +1305,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
after_nat:
ip_vs_out_stats(cp, skb);
- ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd);
+ ip_vs_set_state(cp, IP_VS_DIR_OUTPUT, skb, pd, iph->len);
skb->ipvs_property = 1;
if (!(cp->flags & IP_VS_CONN_F_NFCT))
ip_vs_notrack(skb);
@@ -1585,6 +1584,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
bool tunnel, new_cp = false;
union nf_inet_addr *raddr;
char *outer_proto = "IPIP";
+ unsigned int hlen_ipip;
+ int ulen = 0;
*related = 1;
@@ -1621,9 +1622,10 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (cih == NULL)
+ if (!(cih && cih->version == 4 && cih->ihl >= 5))
return NF_ACCEPT; /* The packet looks wrong, ignore */
raddr = (union nf_inet_addr *)&cih->daddr;
+ hlen_ipip = cih->ihl * 4;
/* Special case for errors for IPIP/UDP/GRE tunnel packets */
tunnel = false;
@@ -1639,9 +1641,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Only for known tunnel */
if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP)
return NF_ACCEPT;
- offset += cih->ihl * 4;
+ offset += hlen_ipip;
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (cih == NULL)
+ if (!(cih && cih->version == 4 && cih->ihl >= 5))
return NF_ACCEPT; /* The packet looks wrong, ignore */
tunnel = true;
} else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
@@ -1649,12 +1651,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* Error for our tunnel must arrive at LOCAL_IN */
(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) {
__u8 iproto;
- int ulen;
/* Non-first fragment has no UDP/GRE header */
if (unlikely(cih->frag_off & htons(IP_OFFSET)))
return NF_ACCEPT;
- offset2 = offset + cih->ihl * 4;
+ offset2 = offset + hlen_ipip;
if (cih->protocol == IPPROTO_UDP) {
ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
raddr, &iproto);
@@ -1723,6 +1724,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
}
if (tunnel) {
+ unsigned int hlen_orig = cih->ihl * 4;
__be32 info = ic->un.gateway;
__u8 type = ic->type;
__u8 code = ic->code;
@@ -1739,6 +1741,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
goto ignore_tunnel;
offset2 -= ihl + sizeof(_icmph);
skb_reset_network_header(skb);
+ /* Ensure the IP header is present in headroom */
+ if (!pskb_may_pull(skb, hlen_ipip))
+ goto ignore_tunnel;
IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
outer_proto, &ip_hdr(skb)->saddr,
&ip_hdr(skb)->daddr, mtu);
@@ -1754,8 +1759,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (dest_dst)
mtu = dst_mtu(dest_dst->dst_cache);
}
- if (mtu > 68 + sizeof(struct iphdr))
- mtu -= sizeof(struct iphdr);
+ if (mtu > 68 + hlen_ipip + ulen)
+ mtu -= hlen_ipip + ulen;
info = htonl(mtu);
}
/* Strip outer IP, ICMP and IPIP/UDP/GRE, go to IP header of
@@ -1764,6 +1769,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (pskb_pull(skb, offset2) == NULL)
goto ignore_tunnel;
skb_reset_network_header(skb);
+ /* Ensure the IP header is present in headroom */
+ if (!pskb_may_pull(skb, hlen_orig))
+ goto ignore_tunnel;
IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
&ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
type, code, ntohl(info));
@@ -2051,7 +2059,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
IP_VS_DBG_PKT(11, af, pp, skb, iph.off, "Incoming packet");
ip_vs_in_stats(cp, skb);
- ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
+ ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd, iph.len);
if (cp->packet_xmit)
ret = cp->packet_xmit(skb, cp, pp, &iph);
/* do not touch skb anymore */
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 63c78a1f3918..c67317be17df 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -372,20 +372,15 @@ static const char *sctp_state_name(int state)
static inline void
set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
- int direction, const struct sk_buff *skb)
+ int direction, const struct sk_buff *skb,
+ unsigned int iph_len)
{
struct sctp_chunkhdr _sctpch, *sch;
unsigned char chunk_type;
int event, next_state;
- int ihl, cofs;
+ int cofs;
-#ifdef CONFIG_IP_VS_IPV6
- ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
- ihl = ip_hdrlen(skb);
-#endif
-
- cofs = ihl + sizeof(struct sctphdr);
+ cofs = iph_len + sizeof(struct sctphdr);
sch = skb_header_pointer(skb, cofs, sizeof(_sctpch), &_sctpch);
if (sch == NULL)
return;
@@ -468,10 +463,11 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
sctp_state_transition(struct ip_vs_conn *cp, int direction,
- const struct sk_buff *skb, struct ip_vs_proto_data *pd)
+ const struct sk_buff *skb, struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
spin_lock_bh(&cp->lock);
- set_sctp_state(pd, cp, direction, skb);
+ set_sctp_state(pd, cp, direction, skb, iph_len);
spin_unlock_bh(&cp->lock);
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index ede4fa3b63f5..b382810156b2 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -580,17 +580,12 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp,
static void
tcp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
struct tcphdr _tcph, *th;
-#ifdef CONFIG_IP_VS_IPV6
- int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
-#else
- int ihl = ip_hdrlen(skb);
-#endif
-
- th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
+ th = skb_header_pointer(skb, iph_len, sizeof(_tcph), &_tcph);
if (th == NULL)
return;
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index ffbebda547fc..dbd4155bb075 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -445,7 +445,8 @@ static const char * udp_state_name(int state)
static void
udp_state_transition(struct ip_vs_conn *cp, int direction,
const struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd,
+ unsigned int iph_len)
{
if (unlikely(!pd)) {
pr_err("UDP no ns data\n");
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 819538d2e460..02a2a966911e 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -707,13 +707,11 @@ int
ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
- struct iphdr *iph = ip_hdr(skb);
-
- if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr,
+ if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, ip_hdr(skb)->daddr,
IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0)
goto tx_error;
- ip_send_check(iph);
+ ip_send_check(ip_hdr(skb));
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c
index ae9ad439449f..6e0bcc58513b 100644
--- a/net/netfilter/nf_conncount.c
+++ b/net/netfilter/nf_conncount.c
@@ -179,17 +179,16 @@ static int __nf_conncount_add(struct net *net,
return -ENOENT;
if (ct && nf_ct_is_confirmed(ct)) {
- /* local connections are confirmed in postrouting so confirmation
- * might have happened before hitting connlimit
+ /* Connection is confirmed but might still be in the setup phase.
+ * Only skip the tracking if it is fully assured. This guarantees
+ * that setup packets or retransmissions are properly counted and
+ * deduplicated.
*/
- if (skb->skb_iif != LOOPBACK_IFINDEX) {
+ if (test_bit(IPS_ASSURED_BIT, &ct->status)) {
err = -EEXIST;
goto out_put;
}
- /* this is likely a local connection, skip optimization to avoid
- * adding duplicates from a 'packet train'
- */
goto check_connections;
}
@@ -208,8 +207,8 @@ static int __nf_conncount_add(struct net *net,
/* Not found, but might be about to be confirmed */
if (PTR_ERR(found) == -EAGAIN) {
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
- nf_ct_zone_id(zone, zone->dir))
+ nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) ==
+ nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL))
goto out_put; /* already exists */
} else {
collect++;
@@ -220,7 +219,7 @@ static int __nf_conncount_add(struct net *net,
found_ct = nf_ct_tuplehash_to_ctrack(found);
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_equal(found_ct, zone, zone->dir)) {
+ nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) {
/*
* We should not see tuples twice unless someone hooks
* this into a table without "-p tcp --syn".
@@ -499,7 +498,7 @@ count_tree(struct net *net,
hash = jhash2(key, data->keylen, conncount_rnd) % CONNCOUNT_SLOTS;
root = &data->root[hash];
- parent = rcu_dereference_raw(root->rb_node);
+ parent = rcu_dereference(root->rb_node);
while (parent) {
int diff;
@@ -507,9 +506,9 @@ count_tree(struct net *net,
diff = key_diff(key, rbconn->key, data->keylen);
if (diff < 0) {
- parent = rcu_dereference_raw(parent->rb_left);
+ parent = rcu_dereference(parent->rb_left);
} else if (diff > 0) {
- parent = rcu_dereference_raw(parent->rb_right);
+ parent = rcu_dereference(parent->rb_right);
} else {
int ret;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 0081c1a0d5e5..b086670ec90d 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -565,9 +565,23 @@ static void destroy_gre_conntrack(struct nf_conn *ct)
{
#ifdef CONFIG_NF_CT_PROTO_GRE
struct nf_conn *master = ct->master;
+ struct nf_conn_help *help;
+
+ if (!master)
+ return;
+
+ help = nfct_help(master);
+ if (help) {
+ struct nf_conntrack_helper *helper;
+
+ rcu_read_lock();
+ helper = rcu_dereference(help->helper);
+ /* Only pptp helper has a destroy callback. */
+ if (helper && helper->destroy)
+ nf_ct_gre_keymap_destroy(master);
- if (master)
- nf_ct_gre_keymap_destroy(master);
+ rcu_read_unlock();
+ }
#endif
}
@@ -836,33 +850,6 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct,
&nf_conntrack_hash[reply_hash]);
}
-static bool nf_ct_ext_valid_pre(const struct nf_ct_ext *ext)
-{
- /* if ext->gen_id is not equal to nf_conntrack_ext_genid, some extensions
- * may contain stale pointers to e.g. helper that has been removed.
- *
- * The helper can't clear this because the nf_conn object isn't in
- * any hash and synchronize_rcu() isn't enough because associated skb
- * might sit in a queue.
- */
- return !ext || ext->gen_id == atomic_read(&nf_conntrack_ext_genid);
-}
-
-static bool nf_ct_ext_valid_post(struct nf_ct_ext *ext)
-{
- if (!ext)
- return true;
-
- if (ext->gen_id != atomic_read(&nf_conntrack_ext_genid))
- return false;
-
- /* inserted into conntrack table, nf_ct_iterate_cleanup()
- * will find it. Disable nf_ct_ext_find() id check.
- */
- WRITE_ONCE(ext->gen_id, 0);
- return true;
-}
-
int
nf_conntrack_hash_check_insert(struct nf_conn *ct)
{
@@ -878,9 +865,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
zone = nf_ct_zone(ct);
- if (!nf_ct_ext_valid_pre(ct->ext))
- return -EAGAIN;
-
local_bh_disable();
do {
sequence = read_seqcount_begin(&nf_conntrack_generation);
@@ -914,18 +898,6 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)
goto chaintoolong;
}
- /* If genid has changed, we can't insert anymore because ct
- * extensions could have stale pointers and nf_ct_iterate_destroy
- * might have completed its table scan already.
- *
- * Increment of the ext genid right after this check is fine:
- * nf_ct_iterate_destroy blocks until locks are released.
- */
- if (!nf_ct_ext_valid_post(ct->ext)) {
- err = -EAGAIN;
- goto out;
- }
-
smp_wmb();
/* The caller holds a reference to this object */
refcount_set(&ct->ct_general.use, 2);
@@ -1207,11 +1179,6 @@ __nf_conntrack_confirm(struct sk_buff *skb)
return NF_DROP;
}
- if (!nf_ct_ext_valid_pre(ct->ext)) {
- NF_CT_STAT_INC(net, insert_failed);
- goto dying;
- }
-
/* We have to check the DYING flag after unlink to prevent
* a race against nf_ct_get_next_corpse() possibly called from
* user context, else we insert an already 'dead' hash, blocking
@@ -1274,16 +1241,6 @@ __nf_conntrack_confirm(struct sk_buff *skb)
nf_conntrack_double_unlock(hash, reply_hash);
local_bh_enable();
- /* ext area is still valid (rcu read lock is held,
- * but will go out of scope soon, we need to remove
- * this conntrack again.
- */
- if (!nf_ct_ext_valid_post(ct->ext)) {
- nf_ct_kill(ct);
- NF_CT_STAT_INC_ATOMIC(net, drop);
- return NF_DROP;
- }
-
help = nfct_help(ct);
if (help && help->helper)
nf_conntrack_event_cache(IPCT_HELPER, ct);
@@ -2493,13 +2450,11 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data)
*/
synchronize_net();
- nf_ct_ext_bump_genid();
iter_data.data = data;
nf_ct_iterate_cleanup(iter, &iter_data);
/* Another cpu might be in a rcu read section with
- * rcu protected pointer cleared in iter callback
- * or hidden via nf_ct_ext_bump_genid() above.
+ * rcu protected pointer cleared in iter callback.
*
* Wait until those are done.
*/
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 6526bdcca580..efabee2063cc 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -77,7 +77,7 @@ static enum retry_state ecache_work_evict_list(struct nf_conntrack_net *cnet)
hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &evicted_list);
- if (time_after(stop, jiffies)) {
+ if (time_after(jiffies, stop)) {
ret = STATE_RESTART;
break;
}
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index dd62cc12e775..0da105e1ded9 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -27,8 +27,6 @@
#define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */
-atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1);
-
static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = {
[NF_CT_EXT_HELPER] = sizeof(struct nf_conn_help),
#if IS_ENABLED(CONFIG_NF_NAT)
@@ -118,10 +116,8 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
if (!new)
return NULL;
- if (!ct->ext) {
+ if (!ct->ext)
memset(new->offset, 0, sizeof(new->offset));
- new->gen_id = atomic_read(&nf_conntrack_ext_genid);
- }
new->offset[id] = newoff;
new->len = newlen;
@@ -131,29 +127,3 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
return (void *)new + newoff;
}
EXPORT_SYMBOL(nf_ct_ext_add);
-
-/* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */
-void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id)
-{
- unsigned int gen_id = atomic_read(&nf_conntrack_ext_genid);
- unsigned int this_id = READ_ONCE(ext->gen_id);
-
- if (!__nf_ct_ext_exist(ext, id))
- return NULL;
-
- if (this_id == 0 || ext->gen_id == gen_id)
- return (void *)ext + ext->offset[id];
-
- return NULL;
-}
-EXPORT_SYMBOL(__nf_ct_ext_find);
-
-void nf_ct_ext_bump_genid(void)
-{
- unsigned int value = atomic_inc_return(&nf_conntrack_ext_genid);
-
- if (value == UINT_MAX)
- atomic_set(&nf_conntrack_ext_genid, 1);
-
- msleep(HZ);
-}
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 0f50ea92ced9..454532838870 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -64,7 +64,7 @@ static const char *const dccprotos[] = {
/* tries to get the ip_addr and port out of a dcc command
* return value: -1 on failure, 0 on success
* data pointer to first byte of DCC command data
- * data_end pointer to last byte of dcc command data
+ * data_end one past end of data
* ip returns parsed ip of dcc command
* port returns parsed port of dcc command
* ad_beg_p returns pointer to first byte of addr data
@@ -82,10 +82,10 @@ static int parse_dcc(char *data, const char *data_end, __be32 *ip,
/* Make sure we have a newline character within the packet boundaries
* because simple_strtoul parses until the first invalid character. */
- for (tmp = data; tmp <= data_end; tmp++)
+ for (tmp = data; tmp < data_end; tmp++)
if (*tmp == '\n')
break;
- if (tmp > data_end || *tmp != '\n')
+ if (tmp >= data_end || *tmp != '\n')
return -1;
*ad_beg_p = data;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index eff5008f5e9d..1028ed8ac485 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3148,7 +3148,7 @@ ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
if (l3proto && exp->tuple.src.l3num != l3proto)
continue;
- if (!net_eq(nf_ct_net(exp->master), net))
+ if (!net_eq(nf_ct_exp_net(exp), net))
continue;
if (cb->args[1]) {
diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c
index 7ab2b25b57bc..b7e99f34dfce 100644
--- a/net/netfilter/nf_conntrack_seqadj.c
+++ b/net/netfilter/nf_conntrack_seqadj.c
@@ -17,12 +17,14 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
if (off == 0)
return 0;
+ spin_lock_bh(&ct->lock);
set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
seqadj = nfct_seqadj(ct);
this_way = &seqadj->seq[dir];
this_way->offset_before = off;
this_way->offset_after = off;
+ spin_unlock_bh(&ct->lock);
return 0;
}
EXPORT_SYMBOL_GPL(nf_ct_seqadj_init);
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index 2500e409757d..d29767646ffa 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -1204,6 +1204,16 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
struct nf_hook_ops *nat_ops;
int i, ret;
+#ifndef MODULE
+ /* If nf_nat_core is built-in and nf_nat_init() fails, dependent
+ * modules like nft_chain_nat.ko may still call this function.
+ * However, nat_net would be invalid, likely pointing to some other
+ * per-net structure.
+ */
+ if (WARN_ON_ONCE(!nf_nat_hook))
+ return -EOPNOTSUPP;
+#endif
+
if (WARN_ON_ONCE(pf >= ARRAY_SIZE(nat_net->nat_proto_net)))
return -EINVAL;
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index 00838c0cc5bb..f7be30c69b5c 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -283,13 +283,24 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
/* Mangle destination port for Cisco phones, then fix up checksums */
if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
+ int doff = *dptr - (const char *)skb->data;
struct udphdr *uh;
+ if (doff <= 0) {
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return NF_DROP;
+ }
+
+ /* ct_sip_info->forced_dport only expected with UDP */
+ if (nf_ct_protonum(ct) != IPPROTO_UDP)
+ return NF_DROP;
+
if (skb_ensure_writable(skb, skb->len)) {
nf_ct_helper_log(skb, ct, "cannot mangle packet");
return NF_DROP;
}
+ *dptr = skb->data + doff;
uh = (void *)skb->data + protoff;
uh->dest = ct_sip_info->forced_dport;
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index d8b7cfda5926..2579f94e3744 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -67,6 +67,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
nf_queue_sock_put(state->sk);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+ dev_put(entry->bridge_dev);
dev_put(entry->physin);
dev_put(entry->physout);
#endif
@@ -83,6 +84,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry)
{
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
const struct sk_buff *skb = entry->skb;
+ struct dst_entry *dst = skb_dst(skb);
+ struct net_device *dev = NULL;
if (nf_bridge_info_exists(skb)) {
entry->physin = nf_bridge_get_physindev(skb, entry->state.net);
@@ -91,6 +94,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry)
entry->physin = NULL;
entry->physout = NULL;
}
+
+ if (entry->state.pf == NFPROTO_BRIDGE &&
+ dst && (dst->flags & DST_FAKE_RTABLE))
+ dev = dst_dev_rcu(dst);
+
+ /* Must hold a reference on the bridge device: dst_hold() protects
+ * the dst itself, but the fake rtable is embedded in bridge-private
+ * storage that netdevice teardown can free independently.
+ */
+ entry->bridge_dev = dev;
#endif
}
@@ -107,6 +120,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
dev_hold(state->out);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+ dev_hold(entry->bridge_dev);
dev_hold(entry->physin);
dev_hold(entry->physout);
#endif
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 71a248cca746..daf8ccbb6433 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -348,8 +348,8 @@ static int nfnl_cthelper_update_policy_all(struct nlattr *tb[],
for (i = 0; i < helper->expect_class_max + 1; i++) {
policy = (struct nf_conntrack_expect_policy *)
&helper->expect_policy[i];
- policy->max_expected = new_policy->max_expected;
- policy->timeout = new_policy->timeout;
+ policy->max_expected = new_policy[i].max_expected;
+ policy->timeout = new_policy[i].timeout;
}
err:
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
index 6d3dfbeb398c..b61656cb0f3e 100644
--- a/net/netfilter/nfnetlink_osf.c
+++ b/net/netfilter/nfnetlink_osf.c
@@ -95,11 +95,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
switch (*optp) {
case OSFOPT_MSS:
- mss = optp[3];
- mss <<= 8;
- mss |= optp[2];
-
- mss = ntohs((__force __be16)mss);
+ mss = get_unaligned_be16(&optp[2]);
break;
case OSFOPT_TS:
break;
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index d6bfb3cff6fd..cfd12124e827 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -971,6 +971,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
if (physinif == ifindex || physoutif == ifindex)
return 1;
+
+ if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex)
+ return 1;
#endif
if (entry->skb_dev && entry->skb_dev->ifindex == ifindex)
return 1;
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 1e8142e64e80..67a138b9be3a 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -389,6 +389,22 @@ static int nft_target_validate(const struct nft_ctx *ctx,
return 0;
}
+static int nft_target_bridge_validate(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ struct xt_target *target = expr->ops->data;
+
+ /* Do not allow UNSPEC to stand-in for NFPROTO_BRIDGE
+ * targets: they are incompatible. ebtables targets return
+ * EBT_ACCEPT, DROP and so on which are not compatible with
+ * NF_ACCEPT, NF_DROP and so on.
+ */
+ if (target->family != NFPROTO_BRIDGE)
+ return -ENOENT;
+
+ return nft_target_validate(ctx, expr);
+}
+
static void __nft_match_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt,
@@ -916,14 +932,16 @@ nft_target_select_ops(const struct nft_ctx *ctx,
ops->init = nft_target_init;
ops->destroy = nft_target_destroy;
ops->dump = nft_target_dump;
- ops->validate = nft_target_validate;
ops->data = target;
ops->reduce = NFT_REDUCE_READONLY;
- if (family == NFPROTO_BRIDGE)
+ if (family == NFPROTO_BRIDGE) {
ops->eval = nft_target_eval_bridge;
- else
+ ops->validate = nft_target_bridge_validate;
+ } else {
ops->eval = nft_target_eval_xt;
+ ops->validate = nft_target_validate;
+ }
return ops;
err:
diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c
index e35588137995..bf01cf8a8907 100644
--- a/net/netfilter/nft_log.c
+++ b/net/netfilter/nft_log.c
@@ -26,46 +26,10 @@ struct nft_log {
char *prefix;
};
-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb)
-{
- struct iphdr _iph;
- const struct iphdr *ih;
-
- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph);
- if (!ih)
- return false;
-
- audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
- &ih->saddr, &ih->daddr, ih->protocol);
-
- return true;
-}
-
-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
-{
- struct ipv6hdr _ip6h;
- const struct ipv6hdr *ih;
- u8 nexthdr;
- __be16 frag_off;
-
- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h);
- if (!ih)
- return false;
-
- nexthdr = ih->nexthdr;
- ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off);
-
- audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
- &ih->saddr, &ih->daddr, nexthdr);
-
- return true;
-}
-
static void nft_log_eval_audit(const struct nft_pktinfo *pkt)
{
struct sk_buff *skb = pkt->skb;
struct audit_buffer *ab;
- int fam = -1;
if (!audit_enabled)
return;
@@ -76,27 +40,7 @@ static void nft_log_eval_audit(const struct nft_pktinfo *pkt)
audit_log_format(ab, "mark=%#x", skb->mark);
- switch (nft_pf(pkt)) {
- case NFPROTO_BRIDGE:
- switch (eth_hdr(skb)->h_proto) {
- case htons(ETH_P_IP):
- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
- break;
- case htons(ETH_P_IPV6):
- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
- break;
- }
- break;
- case NFPROTO_IPV4:
- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
- break;
- case NFPROTO_IPV6:
- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
- break;
- }
-
- if (fam == -1)
- audit_log_format(ab, " saddr=? daddr=? proto=-1");
+ audit_log_nf_skb(ab, skb, nft_pf(pkt));
audit_log_end(ab);
}
diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c
index 4d3e5a31b412..ad3e7d27f02e 100644
--- a/net/netfilter/nft_synproxy.c
+++ b/net/netfilter/nft_synproxy.c
@@ -23,14 +23,13 @@ static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = {
static void nft_synproxy_tcp_options(struct synproxy_options *opts,
const struct tcphdr *tcp,
struct synproxy_net *snet,
- struct nf_synproxy_info *info,
- const struct nft_synproxy *priv)
+ struct nf_synproxy_info *info)
{
this_cpu_inc(snet->stats->syn_received);
if (tcp->ece && tcp->cwr)
opts->options |= NF_SYNPROXY_OPT_ECN;
- opts->options &= priv->info.options;
+ opts->options &= info->options;
opts->mss_encode = opts->mss_option;
opts->mss_option = info->mss;
if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
@@ -55,7 +54,7 @@ static void nft_synproxy_eval_v4(const struct nft_synproxy *priv,
if (tcp->syn) {
/* Initial SYN from client */
- nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
+ nft_synproxy_tcp_options(opts, tcp, snet, &info);
synproxy_send_client_synack(net, skb, tcp, opts);
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
@@ -86,7 +85,7 @@ static void nft_synproxy_eval_v6(const struct nft_synproxy *priv,
if (tcp->syn) {
/* Initial SYN from client */
- nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
+ nft_synproxy_tcp_options(opts, tcp, snet, &info);
synproxy_send_client_synack_ipv6(net, skb, tcp, opts);
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
diff --git a/net/netfilter/xt_AUDIT.c b/net/netfilter/xt_AUDIT.c
index b6a015aee0ce..4c18606b8654 100644
--- a/net/netfilter/xt_AUDIT.c
+++ b/net/netfilter/xt_AUDIT.c
@@ -28,46 +28,10 @@ MODULE_ALIAS("ip6t_AUDIT");
MODULE_ALIAS("ebt_AUDIT");
MODULE_ALIAS("arpt_AUDIT");
-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb)
-{
- struct iphdr _iph;
- const struct iphdr *ih;
-
- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph);
- if (!ih)
- return false;
-
- audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu",
- &ih->saddr, &ih->daddr, ih->protocol);
-
- return true;
-}
-
-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
-{
- struct ipv6hdr _ip6h;
- const struct ipv6hdr *ih;
- u8 nexthdr;
- __be16 frag_off;
-
- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h);
- if (!ih)
- return false;
-
- nexthdr = ih->nexthdr;
- ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off);
-
- audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
- &ih->saddr, &ih->daddr, nexthdr);
-
- return true;
-}
-
static unsigned int
audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
struct audit_buffer *ab;
- int fam = -1;
if (audit_enabled == AUDIT_OFF)
goto errout;
@@ -77,27 +41,7 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
audit_log_format(ab, "mark=%#x", skb->mark);
- switch (xt_family(par)) {
- case NFPROTO_BRIDGE:
- switch (eth_hdr(skb)->h_proto) {
- case htons(ETH_P_IP):
- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
- break;
- case htons(ETH_P_IPV6):
- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
- break;
- }
- break;
- case NFPROTO_IPV4:
- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1;
- break;
- case NFPROTO_IPV6:
- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1;
- break;
- }
-
- if (fam == -1)
- audit_log_format(ab, " saddr=? daddr=? proto=-1");
+ audit_log_nf_skb(ab, skb, xt_family(par));
audit_log_end(ab);
diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c
index 908fd5f2c3c8..eaf2511d63f0 100644
--- a/net/netfilter/xt_cluster.c
+++ b/net/netfilter/xt_cluster.c
@@ -107,7 +107,7 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par)
}
ct = nf_ct_get(skb, &ctinfo);
- if (ct == NULL)
+ if (!ct || nf_ct_is_template(ct))
return false;
if (ct->master)
diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
index 4277084de2e7..2cf27f7d59b9 100644
--- a/net/netfilter/xt_connmark.c
+++ b/net/netfilter/xt_connmark.c
@@ -112,6 +112,16 @@ static int connmark_tg_check(const struct xt_tgchk_param *par)
return ret;
}
+static int connmark_tg_check_v2(const struct xt_tgchk_param *par)
+{
+ const struct xt_connmark_tginfo2 *info = par->targinfo;
+
+ if (info->shift_dir > D_SHIFT_RIGHT || info->shift_bits >= 32)
+ return -EINVAL;
+
+ return connmark_tg_check(par);
+}
+
static void connmark_tg_destroy(const struct xt_tgdtor_param *par)
{
nf_ct_netns_put(par->net, par->family);
@@ -162,7 +172,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = {
.name = "CONNMARK",
.revision = 2,
.family = NFPROTO_IPV4,
- .checkentry = connmark_tg_check,
+ .checkentry = connmark_tg_check_v2,
.target = connmark_tg_v2,
.targetsize = sizeof(struct xt_connmark_tginfo2),
.destroy = connmark_tg_destroy,
@@ -183,7 +193,7 @@ static struct xt_target connmark_tg_reg[] __read_mostly = {
.name = "CONNMARK",
.revision = 2,
.family = NFPROTO_IPV6,
- .checkentry = connmark_tg_check,
+ .checkentry = connmark_tg_check_v2,
.target = connmark_tg_v2,
.targetsize = sizeof(struct xt_connmark_tginfo2),
.destroy = connmark_tg_destroy,
diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c
index b4f7bbc3f3ca..51c7f7ce88d9 100644
--- a/net/netfilter/xt_nat.c
+++ b/net/netfilter/xt_nat.c
@@ -26,6 +26,15 @@ static int xt_nat_checkentry_v0(const struct xt_tgchk_param *par)
static int xt_nat_checkentry(const struct xt_tgchk_param *par)
{
+ switch (par->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_INET:
+ break;
+ default:
+ return -EINVAL;
+ }
+
return nf_ct_netns_get(par->net, par->family);
}
diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c
index b1d736c15fcb..7c05b6342578 100644
--- a/net/netfilter/xt_rateest.c
+++ b/net/netfilter/xt_rateest.c
@@ -16,7 +16,7 @@ xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_rateest_match_info *info = par->matchinfo;
struct gnet_stats_rate_est64 sample = {0};
- u_int32_t bps1, bps2, pps1, pps2;
+ u64 bps1, bps2, pps1, pps2;
bool ret = true;
gen_estimator_read(&info->est1->rate_est, &sample);
diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c
index 117d4615d668..ec1a21e3b6e2 100644
--- a/net/netfilter/xt_u32.c
+++ b/net/netfilter/xt_u32.c
@@ -100,7 +100,7 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par)
{
const struct xt_u32 *data = par->matchinfo;
const struct xt_u32_test *ct;
- unsigned int i;
+ unsigned int i, j;
if (data->ntests > ARRAY_SIZE(data->tests))
return -EINVAL;
@@ -111,6 +111,16 @@ static int u32_mt_checkentry(const struct xt_mtchk_param *par)
if (ct->nnums > ARRAY_SIZE(ct->location) ||
ct->nvalues > ARRAY_SIZE(ct->value))
return -EINVAL;
+
+ for (j = 1; j < ct->nnums; ++j) {
+ switch (ct->location[j].nextop) {
+ case XT_U32_LEFTSH:
+ case XT_U32_RIGHTSH:
+ if (ct->location[j].number >= 32)
+ return -EINVAL;
+ break;
+ }
+ }
}
return 0;
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 32485b367737..82ffb1ac86b0 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -1774,10 +1774,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net,
} else {
rcu_read_lock();
limit = ct_limit_get(info, zone);
- rcu_read_unlock();
err = __ovs_ct_limit_get_zone_limit(
net, info->data, zone, limit, reply);
+ rcu_read_unlock();
if (err)
return err;
}
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index b31abd5508f5..e0c239fa20d8 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -2483,13 +2483,56 @@ static inline int add_nested_action_start(struct sw_flow_actions **sfa,
return used;
}
-static inline void add_nested_action_end(struct sw_flow_actions *sfa,
- int st_offset)
+static inline int add_nested_action_end(struct sw_flow_actions *sfa,
+ int st_offset)
{
- struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions +
- st_offset);
+ struct nlattr *a;
+ u32 attr_len;
+
+ if (WARN_ON_ONCE(st_offset < 0 ||
+ (u32)st_offset > sfa->actions_len))
+ return -EINVAL;
+
+ attr_len = sfa->actions_len - (u32)st_offset;
+ if (WARN_ON_ONCE(attr_len < NLA_HDRLEN))
+ return -EINVAL;
- a->nla_len = sfa->actions_len - st_offset;
+ if (attr_len > U16_MAX)
+ return -EMSGSIZE;
+
+ a = (struct nlattr *)((u8 *)sfa->actions + st_offset);
+ a->nla_len = attr_len;
+ return 0;
+}
+
+/* Free the generated action-list tail at @start and truncate it.
+ * If @nested, @start points to its containing nlattr header.
+ */
+static void ovs_nla_trim(struct sw_flow_actions *sfa, int start, bool nested)
+{
+ const struct nlattr *actions;
+ u32 len;
+
+ if (start < 0)
+ return;
+
+ if (WARN_ON_ONCE((u32)start > sfa->actions_len))
+ return;
+
+ actions = (const struct nlattr *)((u8 *)sfa->actions + start);
+ len = sfa->actions_len - (u32)start;
+
+ if (nested) {
+ if (len < NLA_HDRLEN)
+ goto out;
+
+ actions = (const struct nlattr *)((u8 *)actions + NLA_HDRLEN);
+ len -= NLA_HDRLEN;
+ }
+
+ ovs_nla_free_nested_actions(actions, len);
+out:
+ sfa->actions_len = start;
}
static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
@@ -2509,6 +2552,7 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
const struct nlattr *probability, *actions;
const struct nlattr *a;
+ int actions_start;
int rem, start, err;
struct sample_arg arg;
@@ -2552,18 +2596,27 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg),
log);
if (err)
- return err;
+ goto err;
+ actions_start = (*sfa)->actions_len;
err = __ovs_nla_copy_actions(net, actions, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, actions_start, false);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
static int validate_and_copy_dec_ttl(struct net *net,
@@ -2611,18 +2664,31 @@ static int validate_and_copy_dec_ttl(struct net *net,
return start;
action_start = add_nested_action_start(sfa, OVS_DEC_TTL_ATTR_ACTION, log);
- if (action_start < 0)
- return action_start;
+ if (action_start < 0) {
+ err = action_start;
+ goto err;
+ }
err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type,
vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
+
+ err = add_nested_action_end(*sfa, action_start);
+ if (err)
+ goto err_free;
- add_nested_action_end(*sfa, action_start);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, action_start, true);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
static int validate_and_copy_clone(struct net *net,
@@ -2633,6 +2699,7 @@ static int validate_and_copy_clone(struct net *net,
u32 mpls_label_count, bool log, bool last,
u32 depth)
{
+ int actions_start;
int start, err;
u32 exec;
@@ -2648,17 +2715,26 @@ static int validate_and_copy_clone(struct net *net,
err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec,
sizeof(exec), log);
if (err)
- return err;
+ goto err;
+ actions_start = (*sfa)->actions_len;
err = __ovs_nla_copy_actions(net, attr, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, actions_start, false);
+err:
+ (*sfa)->actions_len = start;
+ return err;
}
void ovs_match_init(struct sw_flow_match *match,
@@ -2749,20 +2825,20 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
tun_dst = metadata_dst_alloc(key.tun_opts_len, METADATA_IP_TUNNEL,
GFP_KERNEL);
- if (!tun_dst)
- return -ENOMEM;
+ if (!tun_dst) {
+ err = -ENOMEM;
+ goto err;
+ }
err = dst_cache_init(&tun_dst->u.tun_info.dst_cache, GFP_KERNEL);
- if (err) {
- dst_release((struct dst_entry *)tun_dst);
- return err;
- }
+ if (err)
+ goto err_free_tun_dst;
a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
sizeof(*ovs_tun), log);
if (IS_ERR(a)) {
- dst_release((struct dst_entry *)tun_dst);
- return PTR_ERR(a);
+ err = PTR_ERR(a);
+ goto err_free_tun_dst;
}
ovs_tun = nla_data(a);
@@ -2783,8 +2859,16 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
ip_tunnel_info_opts_set(tun_info,
TUN_METADATA_OPTS(&key, key.tun_opts_len),
key.tun_opts_len, dst_opt_type);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (WARN_ON_ONCE(err))
+ goto err_free_tun_dst;
+
+ return 0;
+err_free_tun_dst:
+ dst_release((struct dst_entry *)tun_dst);
+err:
+ (*sfa)->actions_len = start;
return err;
}
@@ -2957,7 +3041,7 @@ static int validate_set(const struct nlattr *a,
/* Convert non-masked non-tunnel set actions to masked set actions. */
if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) {
- int start, len = key_len * 2;
+ int err, start, len = key_len * 2;
struct nlattr *at;
*skip_copy = true;
@@ -2969,8 +3053,11 @@ static int validate_set(const struct nlattr *a,
return start;
at = __add_action(sfa, key_type, NULL, len, log);
- if (IS_ERR(at))
- return PTR_ERR(at);
+ if (IS_ERR(at)) {
+ err = PTR_ERR(at);
+ (*sfa)->actions_len = start;
+ return err;
+ }
memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */
memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */
@@ -2980,7 +3067,11 @@ static int validate_set(const struct nlattr *a,
mask->ipv6_label &= htonl(0x000FFFFF);
}
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (WARN_ON_ONCE(err)) {
+ (*sfa)->actions_len = start;
+ return err;
+ }
}
return 0;
@@ -3025,7 +3116,8 @@ static int validate_and_copy_check_pkt_len(struct net *net,
const struct nlattr *acts_if_greater, *acts_if_lesser_eq;
struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1];
struct check_pkt_len_arg arg;
- int nested_acts_start;
+ int greater_acts_start = -1;
+ int lesser_acts_start = -1;
int start, err;
err = nla_parse_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX,
@@ -3060,37 +3152,58 @@ static int validate_and_copy_check_pkt_len(struct net *net,
err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg,
sizeof(arg), log);
if (err)
- return err;
+ goto err_free;
- nested_acts_start = add_nested_action_start(sfa,
- OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log);
- if (nested_acts_start < 0)
- return nested_acts_start;
+ lesser_acts_start =
+ add_nested_action_start(sfa,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL,
+ log);
+ if (lesser_acts_start < 0) {
+ err = lesser_acts_start;
+ goto err_free;
+ }
err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
- add_nested_action_end(*sfa, nested_acts_start);
+ err = add_nested_action_end(*sfa, lesser_acts_start);
+ if (err)
+ goto err_free;
- nested_acts_start = add_nested_action_start(sfa,
- OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log);
- if (nested_acts_start < 0)
- return nested_acts_start;
+ greater_acts_start =
+ add_nested_action_start(sfa,
+ OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER,
+ log);
+ if (greater_acts_start < 0) {
+ err = greater_acts_start;
+ goto err_free;
+ }
err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa,
eth_type, vlan_tci, mpls_label_count, log,
depth + 1);
if (err)
- return err;
+ goto err_free;
+
+ err = add_nested_action_end(*sfa, greater_acts_start);
+ if (err)
+ goto err_free;
- add_nested_action_end(*sfa, nested_acts_start);
- add_nested_action_end(*sfa, start);
+ err = add_nested_action_end(*sfa, start);
+ if (err)
+ goto err_free;
return 0;
+
+err_free:
+ ovs_nla_trim(*sfa, greater_acts_start, true);
+ ovs_nla_trim(*sfa, lesser_acts_start, true);
+ ovs_nla_trim(*sfa, start, false);
+ return err;
}
static int copy_action(const struct nlattr *from,
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 2b582da1e88c..e38fad4144e4 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -129,9 +129,12 @@ static int pn_header_create(struct sk_buff *skb, struct net_device *dev,
return 1;
}
-static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int pn_header_parse(const struct sk_buff *skb,
+ const struct net_device *dev,
+ unsigned char *haddr)
{
const u8 *media = skb_mac_header(skb);
+
*haddr = *media;
return 1;
}
diff --git a/net/psample/psample.c b/net/psample/psample.c
index ddd211a151d0..a4d094377955 100644
--- a/net/psample/psample.c
+++ b/net/psample/psample.c
@@ -465,15 +465,17 @@ void psample_sample_packet(struct psample_group *group, struct sk_buff *skb,
goto error;
if (data_len) {
- int nla_len = nla_total_size(data_len);
+ int nla_len = nla_attr_size(data_len);
struct nlattr *nla;
nla = skb_put(nl_skb, nla_len);
nla->nla_type = PSAMPLE_ATTR_DATA;
- nla->nla_len = nla_attr_size(data_len);
+ nla->nla_len = nla_len;
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
goto error;
+
+ skb_put_zero(nl_skb, nla_padlen(data_len));
}
#ifdef CONFIG_INET
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index 2c009793f193..305523cebe3b 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -496,7 +496,7 @@ int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len)
if (cb->dst_port == QRTR_PORT_CTRL_LEGACY)
cb->dst_port = QRTR_PORT_CTRL;
- if (!size || len != ALIGN(size, 4) + hdrlen)
+ if (!size || size > len || len != ALIGN(size, 4) + hdrlen)
goto err;
if ((cb->type == QRTR_TYPE_NEW_SERVER ||
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 99efe14eb0c9..4f4094d0c5cc 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -157,7 +157,7 @@ static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb)
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
int ret;
- if (sp->len > call->rx_dec_bsize) {
+ if (sp->len > call->rx_dec_bsize || !call->rx_dec_buffer) {
/* Make sure we can hold a 1412-byte jumbo subpacket and make
* sure that the buffer size is aligned to a crypto blocksize.
*/
@@ -429,8 +429,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
rxrpc_see_call(call, rxrpc_call_see_already_released);
mutex_unlock(&call->user_mutex);
- if (!(flags & MSG_PEEK))
- rxrpc_put_call(call, rxrpc_call_put_recvmsg);
+ rxrpc_put_call(call, rxrpc_call_put_recvmsg);
goto try_again;
}
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index bed04ae3003c..3b7e5fcdd4ff 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -148,10 +148,15 @@ static void offload_action_hw_count_dec(struct tc_action *act,
static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act)
{
- if (is_tcf_pedit(act))
- return tcf_pedit_nkeys(act);
- else
- return 1;
+ unsigned int count;
+
+ if (is_tcf_pedit(act)) {
+ spin_lock_bh(&act->tcfa_lock);
+ count = tcf_pedit_nkeys_locked(act);
+ spin_unlock_bh(&act->tcfa_lock);
+ return count;
+ }
+ return 1;
}
static bool tc_act_skip_hw(u32 flags)
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index a1c0e8a9fc8c..305c3c7d2d5f 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -44,7 +44,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb,
tcf_lastuse_update(&prog->tcf_tm);
bstats_update(this_cpu_ptr(prog->common.cpu_bstats), skb);
- filter = rcu_dereference(prog->filter);
+ filter = rcu_dereference_bh(prog->filter);
if (at_ingress) {
__skb_push(skb, skb->mac_len);
filter_res = bpf_prog_run_data_pointers(filter, skb);
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index 7810c9d64ff3..287e25593412 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -845,11 +845,11 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
u8 family, u16 zone, bool *defrag)
{
enum ip_conntrack_info ctinfo;
+ struct tc_skb_cb cb;
struct nf_conn *ct;
int err = 0;
bool frag;
u8 proto;
- u16 mru;
/* Previously seen (loopback)? Ignore. */
ct = nf_ct_get(skb, &ctinfo);
@@ -863,12 +863,13 @@ static int tcf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
if (err || !frag)
return err;
- err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &mru);
+ cb = *tc_skb_cb(skb);
+ err = nf_ct_handle_fragments(net, skb, zone, family, &proto, &cb.mru);
if (err)
return err;
*defrag = true;
- tc_skb_cb(skb)->mru = mru;
+ *tc_skb_cb(skb) = cb;
return 0;
}
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 80b2a1e9d5ce..f26f05fb10fe 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -563,9 +563,18 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
{
if (bind) {
struct flow_action_entry *entry = entry_data;
+ int nkeys = tcf_pedit_nkeys_locked(act);
int k;
- for (k = 0; k < tcf_pedit_nkeys(act); k++) {
+ /* If the required keys exceed the remaining capacity return
+ * -ENOSPC to abort the offload and fallback to software.
+ */
+ if (nkeys > *index_inc) {
+ NL_SET_ERR_MSG_MOD(extack, "Not enough space to offload all pedit keys");
+ return -ENOSPC;
+ }
+
+ for (k = 0; k < nkeys; k++) {
switch (tcf_pedit_cmd(act, k)) {
case TCA_PEDIT_KEY_EX_CMD_SET:
entry->id = FLOW_ACTION_MANGLE;
@@ -602,7 +611,7 @@ static int tcf_pedit_offload_act_setup(struct tc_action *act, void *entry_data,
return -EOPNOTSUPP;
}
- for (k = 1; k < tcf_pedit_nkeys(act); k++) {
+ for (k = 1; k < tcf_pedit_nkeys_locked(act); k++) {
if (cmd != tcf_pedit_cmd(act, k)) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported pedit command offload");
return -EOPNOTSUPP;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 45124fd64d6b..e259ec6ed145 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -3808,12 +3808,21 @@ int tc_setup_action(struct flow_action *flow_action,
entry = &flow_action->entries[j];
spin_lock_bh(&act->tcfa_lock);
+
+ /* Abort the offload if we have exhausted the allocated capacity */
+ if (j >= flow_action->num_entries) {
+ NL_SET_ERR_MSG_MOD(extack, "Flow action buffer overflow");
+ err = -ENOSPC;
+ goto err_out_locked;
+ }
+
err = tcf_act_get_user_cookie(entry, act);
if (err)
goto err_out_locked;
- index = 0;
- err = tc_setup_offload_act(act, entry, &index, extack);
+ index = flow_action->num_entries - j;
+ err = tc_setup_offload_act(act, entry, &index,
+ extack);
if (err)
goto err_out_locked;
@@ -3867,10 +3876,13 @@ unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
int i;
tcf_exts_for_each_action(i, act, exts) {
- if (is_tcf_pedit(act))
- num_acts += tcf_pedit_nkeys(act);
- else
+ if (is_tcf_pedit(act)) {
+ spin_lock_bh(&act->tcfa_lock);
+ num_acts += tcf_pedit_nkeys_locked(act);
+ spin_unlock_bh(&act->tcfa_lock);
+ } else {
num_acts++;
+ }
}
return num_acts;
}
@@ -3971,6 +3983,9 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
skb_do_redirect(skb);
*ret = __NET_XMIT_STOLEN;
return NULL;
+ case TC_ACT_CONSUMED:
+ *ret = __NET_XMIT_STOLEN;
+ return NULL;
}
return skb;
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 29dfe6767f10..d5a5dffcd6f9 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -142,7 +142,8 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog)
static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
struct cls_bpf_prog *oldprog,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack,
+ bool is_rollback)
{
struct tcf_block *block = tp->chain->block;
struct tc_cls_bpf_offload cls_bpf = {};
@@ -177,7 +178,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
&oldprog->in_hw_count, true);
if (prog && err) {
- cls_bpf_offload_cmd(tp, oldprog, prog, extack);
+ if (!is_rollback)
+ cls_bpf_offload_cmd(tp, oldprog, prog, extack, true);
return err;
}
@@ -208,7 +210,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
if (!prog && !oldprog)
return 0;
- return cls_bpf_offload_cmd(tp, prog, oldprog, extack);
+ return cls_bpf_offload_cmd(tp, prog, oldprog, extack, false);
}
static void cls_bpf_stop_offload(struct tcf_proto *tp,
@@ -217,7 +219,7 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp,
{
int err;
- err = cls_bpf_offload_cmd(tp, NULL, prog, extack);
+ err = cls_bpf_offload_cmd(tp, NULL, prog, extack, false);
if (err)
pr_err("Stopping hardware offload failed: %d\n", err);
}
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index d92ffdaf546c..9ba13953d3a5 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -21,6 +21,7 @@
#include <net/inet_sock.h>
#include <net/pkt_cls.h>
+#include <linux/siphash.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/flow_dissector.h>
@@ -57,11 +58,15 @@ struct flow_filter {
struct rcu_work rwork;
};
+static siphash_aligned_key_t flow_keys_secret __read_mostly;
+
static inline u32 addr_fold(void *addr)
{
- unsigned long a = (unsigned long)addr;
-
- return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0);
+#ifdef CONFIG_64BIT
+ return (u32)siphash_1u64((u64)addr, &flow_keys_secret);
+#else
+ return (u32)siphash_1u32((u32)addr, &flow_keys_secret);
+#endif
}
static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow)
@@ -596,6 +601,7 @@ static int flow_init(struct tcf_proto *tp)
return -ENOBUFS;
INIT_LIST_HEAD(&head->filters);
rcu_assign_pointer(tp->root, head);
+ net_get_random_once(&flow_keys_secret, sizeof(flow_keys_secret));
return 0;
}
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 0b85971165d2..ce9ee43e9ee2 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1382,10 +1382,7 @@ static u32 cake_calc_overhead(struct cake_sched_data *q, u32 len, u32 off)
if (q->min_netlen > len)
q->min_netlen = len;
- len += q->rate_overhead;
-
- if (len < q->rate_mpu)
- len = q->rate_mpu;
+ len = max((s32)len + q->rate_overhead, (s32)q->rate_mpu);
if (q->atm_mode == CAKE_ATM_ATM) {
len += 47;
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index 63c02040b426..0dfdc8b65699 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -86,7 +86,7 @@ static void drop_func(struct sk_buff *skb, void *ctx)
qdisc_qstats_drop(sch);
}
-static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
+static struct sk_buff *__codel_qdisc_dequeue(struct Qdisc *sch)
{
struct codel_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
@@ -95,13 +95,51 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
&q->stats, qdisc_pkt_len, codel_get_enqueue_time,
drop_func, dequeue_func);
+ if (skb)
+ qdisc_bstats_update(sch, skb);
+ return skb;
+}
+
+static void codel_dequeue_drop(struct Qdisc *sch)
+{
+ struct codel_sched_data *q = qdisc_priv(sch);
+
if (q->stats.drop_count) {
- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
+ q->stats.drop_len);
q->stats.drop_count = 0;
q->stats.drop_len = 0;
}
- if (skb)
- qdisc_bstats_update(sch, skb);
+}
+
+static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+
+ skb = __codel_qdisc_dequeue(sch);
+
+ codel_dequeue_drop(sch);
+
+ return skb;
+}
+
+static struct sk_buff *codel_peek(struct Qdisc *sch)
+{
+ struct sk_buff *skb = skb_peek(&sch->gso_skb);
+
+ if (!skb) {
+ skb = __codel_qdisc_dequeue(sch);
+
+ if (skb) {
+ __skb_queue_head(&sch->gso_skb, skb);
+ /* it's still part of the queue */
+ qdisc_qstats_backlog_inc(sch, skb);
+ sch->q.qlen++;
+ }
+
+ codel_dequeue_drop(sch);
+ }
+
return skb;
}
@@ -276,7 +314,7 @@ static struct Qdisc_ops codel_qdisc_ops __read_mostly = {
.enqueue = codel_qdisc_enqueue,
.dequeue = codel_qdisc_dequeue,
- .peek = qdisc_peek_dequeued,
+ .peek = codel_peek,
.init = codel_init,
.reset = codel_reset,
.change = codel_change,
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index 9b36955e32b1..9a7d9b14bc88 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -270,7 +270,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
memset(&xstats, 0, sizeof(xstats));
if (qlen)
- xstats.deficit = cl->deficit;
+ xstats.deficit = READ_ONCE(cl->deficit);
if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 ||
gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
@@ -362,7 +362,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (!cl_is_active(cl)) {
list_add_tail(&cl->alist, &q->active);
- cl->deficit = cl->quantum;
+ WRITE_ONCE(cl->deficit, cl->quantum);
}
sch->qstats.backlog += len;
@@ -389,7 +389,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch)
len = qdisc_pkt_len(skb);
if (len <= cl->deficit) {
- cl->deficit -= len;
+ WRITE_ONCE(cl->deficit, cl->deficit - len);
skb = qdisc_dequeue_peeked(cl->qdisc);
if (unlikely(skb == NULL))
goto out;
@@ -403,7 +403,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch)
return skb;
}
- cl->deficit += cl->quantum;
+ WRITE_ONCE(cl->deficit, cl->deficit + cl->quantum);
list_move_tail(&cl->alist, &q->active);
}
out:
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 056895df1785..e059d6c14574 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -278,7 +278,7 @@ static void drop_func(struct sk_buff *skb, void *ctx)
qdisc_qstats_drop(sch);
}
-static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
+static struct sk_buff *__fq_codel_dequeue(struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
@@ -315,12 +315,49 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
qdisc_bstats_update(sch, skb);
flow->deficit -= qdisc_pkt_len(skb);
+ return skb;
+}
+
+static void fq_codel_dequeue_drop(struct Qdisc *sch)
+{
+ struct fq_codel_sched_data *q = qdisc_priv(sch);
+
if (q->cstats.drop_count) {
qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
q->cstats.drop_len);
q->cstats.drop_count = 0;
q->cstats.drop_len = 0;
}
+}
+
+static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+
+ skb = __fq_codel_dequeue(sch);
+
+ fq_codel_dequeue_drop(sch);
+
+ return skb;
+}
+
+static struct sk_buff *fq_codel_peek(struct Qdisc *sch)
+{
+ struct sk_buff *skb = skb_peek(&sch->gso_skb);
+
+ if (!skb) {
+ skb = __fq_codel_dequeue(sch);
+
+ if (skb) {
+ __skb_queue_head(&sch->gso_skb, skb);
+ /* it's still part of the queue */
+ qdisc_qstats_backlog_inc(sch, skb);
+ sch->q.qlen++;
+ }
+
+ fq_codel_dequeue_drop(sch);
+ }
+
return skb;
}
@@ -707,7 +744,7 @@ static struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct fq_codel_sched_data),
.enqueue = fq_codel_enqueue,
.dequeue = fq_codel_dequeue,
- .peek = qdisc_peek_dequeued,
+ .peek = fq_codel_peek,
.init = fq_codel_init,
.reset = fq_codel_reset,
.destroy = fq_codel_destroy,
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 1ac51d024991..71f96a8980a7 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -715,7 +715,7 @@ init_vf(struct hfsc_class *cl, unsigned int len)
rtsc_min(&cl->cl_virtual, &cl->cl_fsc, cl->cl_vt, cl->cl_total);
cl->cl_vtadj = 0;
- cl->cl_vtperiod++; /* increment vt period */
+ WRITE_ONCE(cl->cl_vtperiod, cl->cl_vtperiod + 1); /* increment vt period */
cl->cl_parentperiod = cl->cl_parent->cl_vtperiod;
if (cl->cl_parent->cl_nactive == 0)
cl->cl_parentperiod++;
@@ -753,11 +753,11 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time)
u64 f; /* , myf_bound, delta; */
int go_passive = 0;
- if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC)
+ if (cl->qdisc->q.qlen == 0 && cl->cl_flags & HFSC_FSC && cl->cl_nactive)
go_passive = 1;
for (; cl->cl_parent != NULL; cl = cl->cl_parent) {
- cl->cl_total += len;
+ WRITE_ONCE(cl->cl_total, cl->cl_total + len);
if (!(cl->cl_flags & HFSC_FSC) || cl->cl_nactive == 0)
continue;
@@ -847,7 +847,7 @@ hfsc_adjust_levels(struct hfsc_class *cl)
if (p->level >= level)
level = p->level + 1;
}
- cl->level = level;
+ WRITE_ONCE(cl->level, level);
} while ((cl = cl->cl_parent) != NULL);
}
@@ -1337,10 +1337,10 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
__u32 qlen;
qdisc_qstats_qlen_backlog(cl->qdisc, &qlen, &cl->qstats.backlog);
- xstats.level = cl->level;
- xstats.period = cl->cl_vtperiod;
- xstats.work = cl->cl_total;
- xstats.rtwork = cl->cl_cumul;
+ xstats.level = READ_ONCE(cl->level);
+ xstats.period = READ_ONCE(cl->cl_vtperiod);
+ xstats.work = READ_ONCE(cl->cl_total);
+ xstats.rtwork = READ_ONCE(cl->cl_cumul);
if (gnet_stats_copy_basic(d, NULL, &cl->bstats, true) < 0 ||
gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
@@ -1453,15 +1453,15 @@ hfsc_change_qdisc(struct Qdisc *sch, struct nlattr *opt,
static void
hfsc_reset_class(struct hfsc_class *cl)
{
- cl->cl_total = 0;
- cl->cl_cumul = 0;
+ WRITE_ONCE(cl->cl_total, 0);
+ WRITE_ONCE(cl->cl_cumul, 0);
cl->cl_d = 0;
cl->cl_e = 0;
cl->cl_vt = 0;
cl->cl_vtadj = 0;
cl->cl_cvtmin = 0;
cl->cl_cvtoff = 0;
- cl->cl_vtperiod = 0;
+ WRITE_ONCE(cl->cl_vtperiod, 0);
cl->cl_parentperiod = 0;
cl->cl_f = 0;
cl->cl_myf = 0;
@@ -1627,7 +1627,7 @@ hfsc_dequeue(struct Qdisc *sch)
bstats_update(&cl->bstats, skb);
update_vf(cl, qdisc_pkt_len(skb), cur_time);
if (realtime)
- cl->cl_cumul += qdisc_pkt_len(skb);
+ WRITE_ONCE(cl->cl_cumul, cl->cl_cumul + qdisc_pkt_len(skb));
if (cl->cl_flags & HFSC_RSC) {
if (cl->qdisc->q.qlen != 0) {
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index 67b555c02f2c..aca4795c4101 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -461,12 +461,39 @@ static struct sk_buff *hhf_dequeue(struct Qdisc *sch)
return skb;
}
+static void hhf_reset_classifier(struct hhf_sched_data *q)
+{
+ int i;
+
+ if (!q->hh_flows)
+ return;
+
+ for (i = 0; i < HH_FLOWS_CNT; i++) {
+ struct hh_flow_state *flow, *next;
+ struct list_head *head = &q->hh_flows[i];
+
+ list_for_each_entry_safe(flow, next, head, flowchain) {
+ list_del(&flow->flowchain);
+ kfree(flow);
+ }
+ }
+ WRITE_ONCE(q->hh_flows_current_cnt, 0);
+
+ for (i = 0; i < HHF_ARRAYS_CNT; i++) {
+ if (q->hhf_valid_bits[i])
+ bitmap_zero(q->hhf_valid_bits[i], HHF_ARRAYS_LEN);
+ }
+ q->hhf_arrays_reset_timestamp = hhf_time_stamp();
+}
+
static void hhf_reset(struct Qdisc *sch)
{
+ struct hhf_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
while ((skb = hhf_dequeue(sch)) != NULL)
rtnl_kfree_skbs(skb, skb);
+ hhf_reset_classifier(q);
}
static void hhf_destroy(struct Qdisc *sch)
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index c8a426062923..f1b903b36717 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -568,7 +568,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff)
if (new_mode == HTB_CANT_SEND) {
cl->overlimits++;
- q->overlimits++;
+ WRITE_ONCE(q->overlimits, q->overlimits + 1);
}
if (cl->prio_activity) { /* not necessary: speed optimization */
@@ -628,7 +628,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
/* enqueue to helper queue */
if (q->direct_queue.qlen < q->direct_qlen) {
__qdisc_enqueue_tail(skb, &q->direct_queue);
- q->direct_pkts++;
+ WRITE_ONCE(q->direct_pkts, q->direct_pkts + 1);
} else {
return qdisc_drop(skb, sch, to_free);
}
@@ -1148,6 +1148,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt,
* parts (especially calling ndo_setup_tc) on errors.
*/
q->offload = true;
+ sch->flags |= TCQ_F_OFFLOADED;
return 0;
}
@@ -1208,17 +1209,12 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
struct nlattr *nest;
struct tc_htb_glob gopt;
- if (q->offload)
- sch->flags |= TCQ_F_OFFLOADED;
- else
- sch->flags &= ~TCQ_F_OFFLOADED;
-
- sch->qstats.overlimits = q->overlimits;
+ sch->qstats.overlimits = READ_ONCE(q->overlimits);
/* Its safe to not acquire qdisc lock. As we hold RTNL,
* no change can happen on the qdisc parameters.
*/
- gopt.direct_pkts = q->direct_pkts;
+ gopt.direct_pkts = READ_ONCE(q->direct_pkts);
gopt.version = HTB_VER;
gopt.rate2quantum = q->rate2quantum;
gopt.defcls = q->defcls;
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 0d6649d937c9..62cc1a21a603 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -103,7 +103,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
if (!netif_xmit_stopped(
netdev_get_tx_queue(qdisc_dev(sch), q->curband))) {
qdisc = q->queues[q->curband];
- skb = qdisc->dequeue(qdisc);
+ skb = qdisc_dequeue_peeked(qdisc);
if (skb) {
qdisc_bstats_update(sch, skb);
sch->q.qlen--;
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index c14f9e2d8dc5..6741dd35e36d 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -759,7 +759,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
return NULL;
skip_peek_checks:
- skb = child->ops->dequeue(child);
+ skb = qdisc_dequeue_peeked(child);
if (unlikely(!skb))
return NULL;
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index efcca2696621..13e69bce987e 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -52,7 +52,8 @@
struct teql_master {
struct Qdisc_ops qops;
struct net_device *dev;
- struct Qdisc *slaves;
+ struct Qdisc __rcu *slaves;
+ spinlock_t slaves_lock; /* serializes writes to ->slaves */
struct list_head master_list;
unsigned long tx_bytes;
unsigned long tx_packets;
@@ -61,7 +62,7 @@ struct teql_master {
};
struct teql_sched_data {
- struct Qdisc *next;
+ struct Qdisc __rcu *next;
struct teql_master *m;
struct sk_buff_head q;
};
@@ -101,7 +102,9 @@ teql_dequeue(struct Qdisc *sch)
if (skb == NULL) {
struct net_device *m = qdisc_dev(q);
if (m) {
- dat->m->slaves = sch;
+ spin_lock_bh(&dat->m->slaves_lock);
+ rcu_assign_pointer(dat->m->slaves, sch);
+ spin_unlock_bh(&dat->m->slaves_lock);
netif_wake_queue(m);
}
} else {
@@ -132,34 +135,49 @@ teql_destroy(struct Qdisc *sch)
struct Qdisc *q, *prev;
struct teql_sched_data *dat = qdisc_priv(sch);
struct teql_master *master = dat->m;
+ struct netdev_queue *txq = NULL;
+ bool reset_master_queue = false;
if (!master)
return;
- prev = master->slaves;
+ spin_lock_bh(&master->slaves_lock);
+ prev = rcu_dereference_protected(master->slaves,
+ lockdep_is_held(&master->slaves_lock));
if (prev) {
do {
- q = NEXT_SLAVE(prev);
- if (q == sch) {
- NEXT_SLAVE(prev) = NEXT_SLAVE(q);
- if (q == master->slaves) {
- master->slaves = NEXT_SLAVE(q);
- if (q == master->slaves) {
- struct netdev_queue *txq;
-
- txq = netdev_get_tx_queue(master->dev, 0);
- master->slaves = NULL;
-
- dev_reset_queue(master->dev,
- txq, NULL);
- }
- }
- skb_queue_purge(&dat->q);
- break;
+ struct Qdisc *head, *next;
+
+ q = rcu_dereference_protected(NEXT_SLAVE(prev),
+ lockdep_is_held(&master->slaves_lock));
+ if (q != sch) {
+ prev = q;
+ continue;
}
- } while ((prev = q) != master->slaves);
+ next = rcu_dereference_protected(NEXT_SLAVE(q),
+ lockdep_is_held(&master->slaves_lock));
+ rcu_assign_pointer(NEXT_SLAVE(prev), next);
+
+ head = rcu_dereference_protected(master->slaves,
+ lockdep_is_held(&master->slaves_lock));
+ if (q == head) {
+ rcu_assign_pointer(master->slaves, next);
+ if (q == next) {
+ txq = netdev_get_tx_queue(master->dev, 0);
+ rcu_assign_pointer(master->slaves, NULL);
+ reset_master_queue = true;
+ }
+ }
+ skb_queue_purge(&dat->q);
+ break;
+ } while (prev != rcu_dereference_protected(master->slaves,
+ lockdep_is_held(&master->slaves_lock)));
}
+ spin_unlock_bh(&master->slaves_lock);
+
+ if (reset_master_queue)
+ dev_reset_queue(master->dev, txq, NULL);
}
static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
@@ -168,6 +186,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
struct net_device *dev = qdisc_dev(sch);
struct teql_master *m = (struct teql_master *)sch->ops;
struct teql_sched_data *q = qdisc_priv(sch);
+ struct Qdisc *first;
if (dev->hard_header_len > m->dev->hard_header_len)
return -EINVAL;
@@ -184,7 +203,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
skb_queue_head_init(&q->q);
- if (m->slaves) {
+ spin_lock_bh(&m->slaves_lock);
+ first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock));
+ if (first) {
if (m->dev->flags & IFF_UP) {
if ((m->dev->flags & IFF_POINTOPOINT &&
!(dev->flags & IFF_POINTOPOINT)) ||
@@ -192,8 +213,10 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
!(dev->flags & IFF_BROADCAST)) ||
(m->dev->flags & IFF_MULTICAST &&
!(dev->flags & IFF_MULTICAST)) ||
- dev->mtu < m->dev->mtu)
+ dev->mtu < m->dev->mtu) {
+ spin_unlock_bh(&m->slaves_lock);
return -EINVAL;
+ }
} else {
if (!(dev->flags&IFF_POINTOPOINT))
m->dev->flags &= ~IFF_POINTOPOINT;
@@ -204,14 +227,17 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt,
if (dev->mtu < m->dev->mtu)
m->dev->mtu = dev->mtu;
}
- q->next = NEXT_SLAVE(m->slaves);
- NEXT_SLAVE(m->slaves) = sch;
+ rcu_assign_pointer(q->next,
+ rcu_dereference_protected(NEXT_SLAVE(first),
+ lockdep_is_held(&m->slaves_lock)));
+ rcu_assign_pointer(NEXT_SLAVE(first), sch);
} else {
- q->next = sch;
- m->slaves = sch;
+ rcu_assign_pointer(q->next, sch);
+ rcu_assign_pointer(m->slaves, sch);
m->dev->mtu = dev->mtu;
m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK);
}
+ spin_unlock_bh(&m->slaves_lock);
return 0;
}
@@ -285,12 +311,14 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
int subq = skb_get_queue_mapping(skb);
struct sk_buff *skb_res = NULL;
- start = master->slaves;
-
restart:
nores = 0;
busy = 0;
+ rcu_read_lock();
+
+ start = rcu_dereference(master->slaves);
+
q = start;
if (!q)
goto drop;
@@ -317,10 +345,17 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
netdev_start_xmit(skb, slave, slave_txq, false) ==
NETDEV_TX_OK) {
__netif_tx_unlock(slave_txq);
- master->slaves = NEXT_SLAVE(q);
+ spin_lock(&master->slaves_lock);
+ if (rcu_dereference_protected(master->slaves,
+ lockdep_is_held(&master->slaves_lock)) == q)
+ rcu_assign_pointer(master->slaves,
+ rcu_dereference_protected(NEXT_SLAVE(q),
+ lockdep_is_held(&master->slaves_lock)));
+ spin_unlock(&master->slaves_lock);
netif_wake_queue(dev);
master->tx_packets++;
master->tx_bytes += length;
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
__netif_tx_unlock(slave_txq);
@@ -329,45 +364,56 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
busy = 1;
break;
case 1:
- master->slaves = NEXT_SLAVE(q);
+ spin_lock(&master->slaves_lock);
+ if (rcu_dereference_protected(master->slaves,
+ lockdep_is_held(&master->slaves_lock)) == q)
+ rcu_assign_pointer(master->slaves,
+ rcu_dereference_protected(NEXT_SLAVE(q),
+ lockdep_is_held(&master->slaves_lock)));
+ spin_unlock(&master->slaves_lock);
+ rcu_read_unlock();
return NETDEV_TX_OK;
default:
nores = 1;
break;
}
__skb_pull(skb, skb_network_offset(skb));
- } while ((q = NEXT_SLAVE(q)) != start);
+ } while ((q = rcu_dereference(NEXT_SLAVE(q))) != start);
if (nores && skb_res == NULL) {
skb_res = skb;
+ rcu_read_unlock();
goto restart;
}
if (busy) {
netif_stop_queue(dev);
+ rcu_read_unlock();
return NETDEV_TX_BUSY;
}
master->tx_errors++;
drop:
master->tx_dropped++;
+ rcu_read_unlock();
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static int teql_master_open(struct net_device *dev)
{
- struct Qdisc *q;
+ struct Qdisc *q, *first;
struct teql_master *m = netdev_priv(dev);
int mtu = 0xFFFE;
unsigned int flags = IFF_NOARP | IFF_MULTICAST;
- if (m->slaves == NULL)
+ first = rtnl_dereference(m->slaves);
+ if (!first)
return -EUNATCH;
flags = FMASK;
- q = m->slaves;
+ q = first;
do {
struct net_device *slave = qdisc_dev(q);
@@ -389,7 +435,7 @@ static int teql_master_open(struct net_device *dev)
flags &= ~IFF_BROADCAST;
if (!(slave->flags&IFF_MULTICAST))
flags &= ~IFF_MULTICAST;
- } while ((q = NEXT_SLAVE(q)) != m->slaves);
+ } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first);
m->dev->mtu = mtu;
m->dev->flags = (m->dev->flags&~FMASK) | flags;
@@ -417,14 +463,15 @@ static void teql_master_stats64(struct net_device *dev,
static int teql_master_mtu(struct net_device *dev, int new_mtu)
{
struct teql_master *m = netdev_priv(dev);
- struct Qdisc *q;
+ struct Qdisc *q, *first;
- q = m->slaves;
+ first = rtnl_dereference(m->slaves);
+ q = first;
if (q) {
do {
if (new_mtu > qdisc_dev(q)->mtu)
return -EINVAL;
- } while ((q = NEXT_SLAVE(q)) != m->slaves);
+ } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first);
}
dev->mtu = new_mtu;
@@ -444,6 +491,7 @@ static __init void teql_master_setup(struct net_device *dev)
struct teql_master *master = netdev_priv(dev);
struct Qdisc_ops *ops = &master->qops;
+ spin_lock_init(&master->slaves_lock);
master->dev = dev;
ops->priv_size = sizeof(struct teql_sched_data);
diff --git a/net/sctp/diag.c b/net/sctp/diag.c
index 67eefb3137e2..bc8235bb19e6 100644
--- a/net/sctp/diag.c
+++ b/net/sctp/diag.c
@@ -92,6 +92,7 @@ static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb,
if (!--addrcnt)
break;
}
+ WARN_ON_ONCE(addrcnt);
rcu_read_unlock();
return 0;
@@ -373,42 +374,39 @@ static int sctp_ep_dump(struct sctp_endpoint *ep, void *p)
struct sk_buff *skb = commp->skb;
struct netlink_callback *cb = commp->cb;
const struct inet_diag_req_v2 *r = commp->r;
- struct net *net = sock_net(skb->sk);
struct inet_sock *inet = inet_sk(sk);
int err = 0;
- if (!net_eq(sock_net(sk), net))
+ lock_sock(sk);
+ if (ep->base.dead)
goto out;
- if (cb->args[4] < cb->args[1])
- goto next;
-
- if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs))
- goto next;
+ /* Skip eps with assocs if non-LISTEN states were requested, since
+ * they'll be dumped by sctp_sock_dump() during assoc traversal.
+ */
+ if ((r->idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)) &&
+ !list_empty(&ep->asocs))
+ goto out;
if (r->sdiag_family != AF_UNSPEC &&
sk->sk_family != r->sdiag_family)
- goto next;
+ goto out;
if (r->id.idiag_sport != inet->inet_sport &&
r->id.idiag_sport)
- goto next;
+ goto out;
if (r->id.idiag_dport != inet->inet_dport &&
r->id.idiag_dport)
- goto next;
-
- if (inet_sctp_diag_fill(sk, NULL, skb, r,
- sk_user_ns(NETLINK_CB(cb->skb).sk),
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI,
- cb->nlh, commp->net_admin) < 0) {
- err = 2;
goto out;
- }
-next:
- cb->args[4]++;
+
+ err = inet_sctp_diag_fill(sk, NULL, skb, r,
+ sk_user_ns(NETLINK_CB(cb->skb).sk),
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ cb->nlh, commp->net_admin);
out:
+ release_sock(sk);
return err;
}
@@ -479,41 +477,40 @@ static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
.r = r,
.net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN),
};
- int pos = cb->args[2];
+ int pos;
/* eps hashtable dumps
* args:
* 0 : if it will traversal listen sock
* 1 : to record the sock pos of this time's traversal
- * 4 : to work as a temporary variable to traversal list
*/
if (cb->args[0] == 0) {
- if (!(idiag_states & TCPF_LISTEN))
- goto skip;
- if (sctp_for_each_endpoint(sctp_ep_dump, &commp))
- goto done;
-skip:
+ if (idiag_states & TCPF_LISTEN) {
+ pos = cb->args[1];
+ if (sctp_for_each_endpoint(sctp_ep_dump, net, &pos,
+ &commp)) {
+ cb->args[1] = pos;
+ return;
+ }
+ }
cb->args[0] = 1;
cb->args[1] = 0;
- cb->args[4] = 0;
}
+ if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)))
+ return;
+
/* asocs by transport hashtable dump
* args:
* 1 : to record the assoc pos of this time's traversal
* 2 : to record the transport pos of this time's traversal
* 3 : to mark if we have dumped the ep info of the current asoc
- * 4 : to work as a temporary variable to traversal list
- * 5 : to save the sk we get from travelsing the tsp list.
+ * 4 : to track position within ep->asocs list in sctp_sock_dump()
*/
- if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE)))
- goto done;
-
+ pos = cb->args[2];
sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump,
net, &pos, &commp);
cb->args[2] = pos;
-
-done:
cb->args[1] = cb->args[4];
cb->args[4] = 0;
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index eb2ed7db3fe1..611bddad36d4 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2662,6 +2662,9 @@ static int sctp_process_param(struct sctp_association *asoc,
goto fall_through;
addr_param = param.v + sizeof(struct sctp_addip_param);
+ if (ntohs(addr_param->p.length) >
+ ntohs(param.p->length) - sizeof(struct sctp_addip_param))
+ break;
af = sctp_get_af_specific(param_type2af(addr_param->p.type));
if (!af)
@@ -3060,13 +3063,16 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
union sctp_addr addr;
struct sctp_af *af;
- addr_param = (void *)asconf_param + sizeof(*asconf_param);
-
if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP &&
asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP &&
asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY)
return SCTP_ERROR_UNKNOWN_PARAM;
+ addr_param = (void *)asconf_param + sizeof(*asconf_param);
+ if (ntohs(addr_param->p.length) >
+ ntohs(asconf_param->param_hdr.length) - sizeof(*asconf_param))
+ return SCTP_ERROR_PROTO_VIOLATION;
+
switch (addr_param->p.type) {
case SCTP_PARAM_IPV6_ADDRESS:
if (!asoc->peer.ipv6_address)
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 583d83dcb842..fc04bea029dc 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -73,7 +73,8 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
const struct sctp_association *asoc,
const union sctp_subtype type,
void *arg,
- struct sctp_cmd_seq *commands);
+ struct sctp_cmd_seq *commands,
+ struct sctp_errhdr *err);
static enum sctp_disposition sctp_sf_shut_8_4_5(
struct net *net,
const struct sctp_endpoint *ep,
@@ -2493,9 +2494,15 @@ enum sctp_disposition sctp_sf_cookie_echoed_err(
* errors.
*/
sctp_walk_errors(err, chunk->chunk_hdr) {
- if (SCTP_ERROR_STALE_COOKIE == err->cause)
- return sctp_sf_do_5_2_6_stale(net, ep, asoc, type,
- arg, commands);
+ if (err->cause != SCTP_ERROR_STALE_COOKIE)
+ continue;
+ /* The staleness is only meaningful if the cause is long
+ * enough to hold it; a shorter one is malformed.
+ */
+ if (ntohs(err->length) < sizeof(*err) + sizeof(__be32))
+ break;
+ return sctp_sf_do_5_2_6_stale(net, ep, asoc, type,
+ arg, commands, err);
}
/* It is possible to have malformed error causes, and that
@@ -2537,13 +2544,13 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
const struct sctp_association *asoc,
const union sctp_subtype type,
void *arg,
- struct sctp_cmd_seq *commands)
+ struct sctp_cmd_seq *commands,
+ struct sctp_errhdr *err)
{
int attempts = asoc->init_err_counter + 1;
- struct sctp_chunk *chunk = arg, *reply;
struct sctp_cookie_preserve_param bht;
struct sctp_bind_addr *bp;
- struct sctp_errhdr *err;
+ struct sctp_chunk *reply;
u32 stale;
if (attempts > asoc->max_init_attempts) {
@@ -2554,8 +2561,6 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale(
return SCTP_DISPOSITION_DELETE_TCB;
}
- err = (struct sctp_errhdr *)(chunk->skb->data);
-
/* When calculating the time extension, an implementation
* SHOULD use the RTT information measured based on the
* previous COOKIE ECHO / ERROR exchange, and should add no
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index a6a53b0c4ea3..4cc73e7b3c2a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4110,8 +4110,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk,
if (optlen < sizeof(*params))
return -EINVAL;
/* srs_number_streams is u16, so optlen can't be bigger than this. */
- optlen = min_t(unsigned int, optlen, USHRT_MAX +
- sizeof(__u16) * sizeof(*params));
+ optlen = min_t(unsigned int, optlen,
+ struct_size_t(struct sctp_reset_streams, srs_stream_list,
+ USHRT_MAX));
if (params->srs_number_streams * sizeof(__u16) >
optlen - sizeof(*params))
@@ -4597,8 +4598,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
if (optlen > 0) {
/* Trim it to the biggest size sctp sockopt may need if necessary */
optlen = min_t(unsigned int, optlen,
- PAGE_ALIGN(USHRT_MAX +
- sizeof(__u16) * sizeof(struct sctp_reset_streams)));
+ PAGE_ALIGN(struct_size_t(struct sctp_reset_streams,
+ srs_stream_list, USHRT_MAX)));
kopt = memdup_sockptr(optval, optlen);
if (IS_ERR(kopt))
return PTR_ERR(kopt);
@@ -5317,24 +5318,39 @@ struct sctp_transport *sctp_transport_get_idx(struct net *net,
}
int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
- void *p) {
- int err = 0;
- int hash = 0;
- struct sctp_endpoint *ep;
+ struct net *net, int *pos, void *p) {
+ int err, hash = 0, idx = 0, start;
struct sctp_hashbucket *head;
+ struct sctp_endpoint *ep;
for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize;
hash++, head++) {
+ start = idx;
+again:
read_lock_bh(&head->lock);
sctp_for_each_hentry(ep, &head->chain) {
- err = cb(ep, p);
- if (err)
+ if (sock_net(ep->base.sk) != net)
+ continue;
+ if (idx++ >= *pos) {
+ sctp_endpoint_hold(ep);
break;
+ }
}
read_unlock_bh(&head->lock);
+
+ if (ep) {
+ err = cb(ep, p);
+ sctp_endpoint_put(ep);
+ if (err)
+ return err;
+ (*pos)++;
+
+ idx = start;
+ goto again;
+ }
}
- return err;
+ return 0;
}
EXPORT_SYMBOL_GPL(sctp_for_each_endpoint);
diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c
index 3c06625ceb20..65d6d388adf8 100644
--- a/net/smc/smc_cdc.c
+++ b/net/smc/smc_cdc.c
@@ -438,9 +438,9 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
{
struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
struct smc_cdc_msg *cdc = buf;
+ struct smc_sock *smc = NULL;
struct smc_connection *conn;
struct smc_link_group *lgr;
- struct smc_sock *smc;
if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved))
return; /* short message */
@@ -451,21 +451,26 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
lgr = smc_get_lgr(link);
read_lock_bh(&lgr->conns_lock);
conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
- read_unlock_bh(&lgr->conns_lock);
- if (!conn || conn->out_of_sync)
+ if (!conn || conn->out_of_sync) {
+ read_unlock_bh(&lgr->conns_lock);
return;
+ }
smc = container_of(conn, struct smc_sock, conn);
+ sock_hold(&smc->sk);
+ read_unlock_bh(&lgr->conns_lock);
if (cdc->prod_flags.failover_validation) {
smc_cdc_msg_validate(smc, cdc, link);
- return;
+ goto out;
}
if (smc_cdc_before(ntohs(cdc->seqno),
conn->local_rx_ctrl.seqno))
/* received seqno is old */
- return;
+ goto out;
smc_cdc_msg_recv(smc, cdc);
+out:
+ sock_put(&smc->sk);
}
static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = {
diff --git a/net/socket.c b/net/socket.c
index fa242d7e51c7..ae31c5466463 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -671,7 +671,7 @@ static void __sock_release(struct socket *sock, struct inode *inode)
iput(SOCK_INODE(sock));
return;
}
- sock->file = NULL;
+ WRITE_ONCE(sock->file, NULL);
}
/**
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 63756607f632..85cfd7b7c212 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -1016,8 +1016,23 @@ rpc_free_auth(struct rpc_clnt *clnt)
return NULL;
}
-/*
- * Release reference to the RPC client
+/**
+ * rpc_hold_client - acquire a reference on an rpc_clnt
+ * @clnt: rpc_clnt to pin
+ *
+ * Pairs with rpc_release_client().
+ */
+void rpc_hold_client(struct rpc_clnt *clnt)
+{
+ refcount_inc(&clnt->cl_count);
+}
+
+/**
+ * rpc_release_client - release a reference on an rpc_clnt
+ * @clnt: rpc_clnt to release
+ *
+ * Pairs with rpc_hold_client(). The rpc_clnt's resources are
+ * freed once its reference count drops to zero.
*/
void
rpc_release_client(struct rpc_clnt *clnt)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 78b139d8c1f3..ec09154bd4b2 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -462,6 +462,7 @@ static void svc_tcp_handshake_done(void *data, int status, key_serial_t peerid)
}
clear_bit(XPT_HANDSHAKE, &xprt->xpt_flags);
complete_all(&svsk->sk_handshake_done);
+ svc_xprt_put(xprt);
}
/**
@@ -485,9 +486,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
clear_bit(XPT_TLS_SESSION, &xprt->xpt_flags);
init_completion(&svsk->sk_handshake_done);
+ /* Pin the transport across the asynchronous handshake callback. */
+ svc_xprt_get(xprt);
+
ret = tls_server_hello_x509(&args, GFP_KERNEL);
if (ret) {
trace_svc_tls_not_started(xprt);
+ svc_xprt_put(xprt);
goto out_failed;
}
@@ -496,8 +501,13 @@ static void svc_tcp_handshake(struct svc_xprt *xprt)
if (ret <= 0) {
if (tls_handshake_cancel(sk)) {
trace_svc_tls_timed_out(xprt);
+ svc_xprt_put(xprt);
goto out_close;
}
+ /* Cancellation lost to handshake_complete(): the
+ * callback is in flight and should finish quickly.
+ */
+ wait_for_completion(&svsk->sk_handshake_done);
}
if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) {
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 62e07c330a66..80250af10777 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -180,6 +180,8 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
unsigned int count = 0;
if (head->iov_len) {
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_virt(bvec++, head->iov_base, head->iov_len);
++count;
}
@@ -193,25 +195,27 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
while (remaining > 0) {
len = min_t(unsigned int, remaining,
PAGE_SIZE - offset);
+ if (unlikely(count >= bvec_size))
+ goto bvec_overflow;
bvec_set_page(bvec++, *pages++, len, offset);
remaining -= len;
offset = 0;
- if (unlikely(++count > bvec_size))
- goto bvec_overflow;
+ ++count;
}
}
if (tail->iov_len) {
- bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
- if (unlikely(++count > bvec_size))
+ if (unlikely(count >= bvec_size))
goto bvec_overflow;
+ bvec_set_virt(bvec, tail->iov_base, tail->iov_len);
+ ++count;
}
return count;
bvec_overflow:
pr_warn_once("%s: bio_vec array overflow\n", __func__);
- return count - 1;
+ return count;
}
/**
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index ab453ede54f0..562aa8beae0e 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -1651,6 +1651,22 @@ void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task)
}
EXPORT_SYMBOL_GPL(xprt_add_backlog);
+/**
+ * xprt_add_backlog_noncongested - queue task on backlog
+ * @xprt: transport whose backlog queue receives the task
+ * @task: task to queue
+ *
+ * Like xprt_add_backlog, but does not set XPRT_CONGESTED.
+ * For transports whose free_slot path does not synchronize
+ * with xprt_throttle_congested via reserve_lock.
+ */
+void xprt_add_backlog_noncongested(struct rpc_xprt *xprt,
+ struct rpc_task *task)
+{
+ rpc_sleep_on(&xprt->backlog, task, xprt_complete_request_init);
+}
+EXPORT_SYMBOL_GPL(xprt_add_backlog_noncongested);
+
static bool __xprt_set_rq(struct rpc_task *task, void *data)
{
struct rpc_rqst *req = data;
diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c
index e4d84a13c566..5f304f2c61b1 100644
--- a/net/sunrpc/xprtrdma/backchannel.c
+++ b/net/sunrpc/xprtrdma/backchannel.c
@@ -158,9 +158,7 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst)
rpcrdma_rep_put(&r_xprt->rx_buf, rep);
req->rl_reply = NULL;
- spin_lock(&xprt->bc_pa_lock);
- list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list);
- spin_unlock(&xprt->bc_pa_lock);
+ rpcrdma_req_put(req);
xprt_put(xprt);
}
@@ -202,6 +200,7 @@ static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt)
rqst->rq_xprt = xprt;
__set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state);
xdr_buf_init(&rqst->rq_snd_buf, rdmab_data(req->rl_sendbuf), size);
+ kref_init(&req->rl_kref);
return rqst;
}
diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c
index 47f33bb7bff8..a0d49580dcb8 100644
--- a/net/sunrpc/xprtrdma/frwr_ops.c
+++ b/net/sunrpc/xprtrdma/frwr_ops.c
@@ -404,7 +404,7 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
++num_wrs;
}
- if ((kref_read(&req->rl_kref) > 1) || num_wrs > ep->re_send_count) {
+ if (req->rl_sendctx->sc_unmap_count || num_wrs > ep->re_send_count) {
send_wr->send_flags |= IB_SEND_SIGNALED;
ep->re_send_count = min_t(unsigned int, ep->re_send_batch,
num_wrs - ep->re_send_count);
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index 190a4de239c8..e201b37578a7 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -516,29 +516,11 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
return 0;
}
-static void rpcrdma_sendctx_done(struct kref *kref)
-{
- struct rpcrdma_req *req =
- container_of(kref, struct rpcrdma_req, rl_kref);
- struct rpcrdma_rep *rep = req->rl_reply;
-
- rpcrdma_complete_rqst(rep);
- rep->rr_rxprt->rx_stats.reply_waits_for_send++;
-}
-
-/**
- * rpcrdma_sendctx_unmap - DMA-unmap Send buffer
- * @sc: sendctx containing SGEs to unmap
- *
- */
-void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+static void rpcrdma_sendctx_dma_unmap(struct rpcrdma_sendctx *sc)
{
struct rpcrdma_regbuf *rb = sc->sc_req->rl_sendbuf;
struct ib_sge *sge;
- if (!sc->sc_unmap_count)
- return;
-
/* The first two SGEs contain the transport header and
* the inline buffer. These are always left mapped so
* they can be cheaply re-used.
@@ -547,8 +529,29 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
++sge, --sc->sc_unmap_count)
ib_dma_unmap_page(rdmab_device(rb), sge->addr, sge->length,
DMA_TO_DEVICE);
+}
- kref_put(&sc->sc_req->rl_kref, rpcrdma_sendctx_done);
+/**
+ * rpcrdma_sendctx_unmap - DMA-unmap Send buffer and release Send owner
+ * @sc: sendctx containing SGEs to unmap
+ *
+ */
+void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
+{
+ struct rpcrdma_req *req = sc->sc_req;
+
+ rpcrdma_sendctx_dma_unmap(sc);
+ sc->sc_req = NULL;
+ rpcrdma_req_put(req);
+}
+
+/* No Send was posted. Release DMA mappings prepared for this
+ * sendctx, but leave the request reference count alone.
+ */
+static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
+{
+ rpcrdma_sendctx_dma_unmap(sc);
+ sc->sc_req = NULL;
}
/* Prepare an SGE for the RPC-over-RDMA transport header.
@@ -740,8 +743,6 @@ static bool rpcrdma_prepare_noch_mapped(struct rpcrdma_xprt *r_xprt,
tail->iov_len))
return false;
- if (req->rl_sendctx->sc_unmap_count)
- kref_get(&req->rl_kref);
return true;
}
@@ -771,7 +772,6 @@ static bool rpcrdma_prepare_readch(struct rpcrdma_xprt *r_xprt,
len -= len & 3;
if (!rpcrdma_prepare_tail_iov(req, xdr, page_base, len))
return false;
- kref_get(&req->rl_kref);
}
return true;
@@ -792,6 +792,7 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
struct xdr_buf *xdr,
enum rpcrdma_chunktype rtype)
{
+ struct rpcrdma_sendctx *sc;
int ret;
ret = -EAGAIN;
@@ -800,7 +801,6 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
goto out_nosc;
req->rl_sendctx->sc_unmap_count = 0;
req->rl_sendctx->sc_req = req;
- kref_init(&req->rl_kref);
req->rl_wr.wr_cqe = &req->rl_sendctx->sc_cqe;
req->rl_wr.sg_list = req->rl_sendctx->sc_sges;
req->rl_wr.num_sge = 0;
@@ -828,10 +828,16 @@ inline int rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
goto out_unmap;
}
+ /* The Send-side owner releases this reference when the
+ * Send has completed.
+ */
+ kref_get(&req->rl_kref);
return 0;
out_unmap:
- rpcrdma_sendctx_unmap(req->rl_sendctx);
+ sc = req->rl_sendctx;
+ rpcrdma_sendctx_cancel(sc);
+ rpcrdma_sendctx_unget_locked(r_xprt, sc);
out_nosc:
trace_xprtrdma_prepsend_failed(&req->rl_slot, ret);
return ret;
@@ -1130,6 +1136,8 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep)
/* Peek at stream contents without advancing. */
p = xdr_inline_decode(xdr, 0);
+ if ((char *)xdr->end - (char *)p < 5 * XDR_UNIT)
+ return false;
/* Chunk lists */
if (xdr_item_is_present(p++))
@@ -1154,7 +1162,7 @@ rpcrdma_is_bcall(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep)
*/
p = xdr_inline_decode(xdr, 3 * sizeof(*p));
if (unlikely(!p))
- return true;
+ return false;
rpcrdma_bc_receive_call(r_xprt, rep);
return true;
@@ -1378,6 +1386,11 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep)
struct rpc_rqst *rqst = rep->rr_rqst;
int status;
+ /* I3: every registered MR has been invalidated and
+ * ib_dma_unmap_sg()'d before complete_rqst runs.
+ */
+ WARN_ON_ONCE(!list_empty(&rpcr_to_rdmar(rqst)->rl_registered));
+
switch (rep->rr_proc) {
case rdma_msg:
status = rpcrdma_decode_msg(r_xprt, rep, rqst);
@@ -1409,13 +1422,69 @@ void rpcrdma_complete_rqst(struct rpcrdma_rep *rep)
goto out;
}
-static void rpcrdma_reply_done(struct kref *kref)
-{
- struct rpcrdma_req *req =
- container_of(kref, struct rpcrdma_req, rl_kref);
-
- rpcrdma_complete_rqst(req->rl_reply);
-}
+/* Reply-side ownership invariants
+ *
+ * I1 (Receive WR ownership). A struct rpcrdma_rep is owned by the
+ * HCA between ib_post_recv() and the matching Receive completion.
+ * After ib_dma_sync_single_for_cpu() in rpcrdma_wc_receive() it is
+ * owned by the CPU until rpcrdma_rep_put() returns it to
+ * rb_free_reps; a rep on rb_free_reps is not re-posted until
+ * rpcrdma_post_recvs() pulls it off. Asserted: rpcrdma_post_recvs()
+ * WARNs that a pulled rep has rr_rqst == NULL.
+ *
+ * I2 (rep attachment). While req->rl_reply == rep, the rep cannot be
+ * re-posted. rpcrdma_reply_put() NULLs req->rl_reply before handing
+ * the rep to rpcrdma_rep_put(). Asserted: rpcrdma_reply_put() WARNs
+ * that rl_reply is NULL after the put.
+ *
+ * I3 (Registered-MR fence). On entry to rpcrdma_complete_rqst() every
+ * MR that was on req->rl_registered has had its rkey invalidated
+ * (remotely via IB_WC_WITH_INVALIDATE or locally via IB_WR_LOCAL_INV)
+ * and its pages ib_dma_unmap_sg()'d. The LocalInv chain is posted
+ * on a single QP; strong send-queue ordering makes the last
+ * completion (frwr_wc_localinv_done) observe the
+ * ib_dma_unmap_sg() that ran from each earlier completion's
+ * frwr_mr_put() before complete_rqst is called. The inline
+ * frwr_reminv() path unmaps its one MR synchronously before
+ * rpcrdma_reply_handler() reaches complete_rqst. Asserted:
+ * rpcrdma_complete_rqst() WARNs that rl_registered is empty.
+ *
+ * I4 (Send-buffer release). req->rl_kref carries two unconditional
+ * owners while a Send is outstanding: the RPC-layer reference (set
+ * at xprt_rdma_alloc_slot / xprt_rdma_bc_rqst_get / rpcrdma_req_release
+ * pool-entry) and the Send-side reference (kref_get() in
+ * rpcrdma_prepare_send_sges()). rpcrdma_req_release() runs only
+ * after both have dropped, so the req does not return to its free
+ * pool until rpcrdma_sendctx_unmap() has fired -- the HCA has
+ * released the send buffer before the req can be reused. Asserted:
+ * rpcrdma_req_release() WARNs that rl_sendctx is NULL.
+ *
+ * I5 (req lifecycle). A req is owned by the RPC layer between slot
+ * acquisition and the matching xprt_rdma_free_slot() (or, for the
+ * backchannel, xprt_rdma_bc_free_rqst()). While owned, rl_kref >= 1.
+ * The pools (rb_send_bufs, bc_pa_list, backlog wake target) never
+ * contain a req with outstanding Send-side or Reply-side work.
+ *
+ * Non-hazards. The following claims have been raised by adversarial
+ * review and are each closed by the invariants above:
+ *
+ * * "Reply completes the RPC while the HCA still holds the send
+ * buffer" -- excluded by I4. The Send-side kref reference is held
+ * until rpcrdma_sendctx_unmap() runs from Send completion.
+ *
+ * * "Signal-driven release races the in-flight Send" -- same
+ * resolution. xprt_rdma_free() does not touch rl_kref; the
+ * Send-side reference keeps the req out of its pool until Send
+ * completion fires.
+ *
+ * * "Receive completion races rep reuse" -- excluded by I1. A rep
+ * is on rb_free_reps only after rpcrdma_rep_put() has been called
+ * and rpcrdma_post_recvs() owns the next transition back to the HCA.
+ *
+ * * "Pages still DMA-mapped when call_decode reads them" -- excluded
+ * by I3. The matching ib_dma_unmap_sg() for every MR has run on
+ * the same CPU thread that calls rpcrdma_complete_rqst().
+ */
/**
* rpcrdma_reply_handler - Process received RPC/RDMA messages
@@ -1451,6 +1520,14 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
credits = be32_to_cpu(*p++);
rep->rr_proc = *p++;
+ /* The credit grant from the wire is not trustworthy;
+ * sanitize it before any code path consumes it.
+ */
+ if (credits == 0)
+ credits = 1; /* don't deadlock */
+ else if (credits > r_xprt->rx_ep->re_max_requests)
+ credits = r_xprt->rx_ep->re_max_requests;
+
if (rep->rr_vers != rpcrdma_version)
goto out_badversion;
@@ -1467,12 +1544,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
xprt_pin_rqst(rqst);
spin_unlock(&xprt->queue_lock);
- if (credits == 0)
- credits = 1; /* don't deadlock */
- else if (credits > r_xprt->rx_ep->re_max_requests)
- credits = r_xprt->rx_ep->re_max_requests;
- rpcrdma_post_recvs(r_xprt, credits + (buf->rb_bc_srv_max_requests << 1),
- false);
if (buf->rb_credits != credits)
rpcrdma_update_cwnd(r_xprt, credits);
@@ -1490,21 +1561,28 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
frwr_unmap_async(r_xprt, req);
/* LocalInv completion will complete the RPC */
else
- kref_put(&req->rl_kref, rpcrdma_reply_done);
- return;
+ rpcrdma_complete_rqst(rep);
-out_badversion:
- trace_xprtrdma_reply_vers_err(rep);
- goto out;
+out_post:
+ rpcrdma_post_recvs(r_xprt,
+ credits + (buf->rb_bc_srv_max_requests << 1));
+ return;
out_norqst:
spin_unlock(&xprt->queue_lock);
trace_xprtrdma_reply_rqst_err(rep);
- goto out;
+ rpcrdma_rep_put(buf, rep);
+ goto out_post;
+
+out_badversion:
+ trace_xprtrdma_reply_vers_err(rep);
+ rpcrdma_rep_put(buf, rep);
+ credits = buf->rb_credits;
+ goto out_post;
out_shortreply:
trace_xprtrdma_reply_short_err(rep);
-
-out:
rpcrdma_rep_put(buf, rep);
+ credits = buf->rb_credits;
+ goto out_post;
}
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 29b0562d62e7..9f46c80dba6e 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -280,6 +280,13 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
cancel_delayed_work_sync(&r_xprt->rx_connect_worker);
rpcrdma_xprt_disconnect(r_xprt);
+
+ /* The disconnect's sendctx drain can return bc_prealloc reqs
+ * to bc_pa_list after xprt_destroy_backchannel() emptied it.
+ */
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
+ xprt_rdma_bc_destroy(xprt, 0);
+#endif
rpcrdma_buffer_destroy(&r_xprt->rx_buf);
xprt_rdma_free_addresses(xprt);
@@ -488,6 +495,50 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
queue_delayed_work(system_long_wq, &r_xprt->rx_connect_worker, delay);
}
+/* rl_kref has two owners while a Send is outstanding: the rpc_rqst
+ * owner and the sendctx. Replies complete the RPC but do not drop
+ * either reference. The req returns to its free pool only after
+ * xprt_rdma_free_slot() or xprt_rdma_bc_free_rqst() has dropped the
+ * RPC-layer reference and rpcrdma_sendctx_unmap() has dropped the
+ * Send-side reference.
+ */
+static void rpcrdma_req_release(struct kref *kref)
+{
+ struct rpcrdma_req *req =
+ container_of(kref, struct rpcrdma_req, rl_kref);
+ struct rpc_rqst *rqst = &req->rl_slot;
+ struct rpc_xprt *xprt = rqst->rq_xprt;
+ struct rpcrdma_xprt *r_xprt;
+
+ /* I4: both the RPC-layer and Send-side owners have dropped,
+ * so rpcrdma_sendctx_unmap() has cleared rl_sendctx.
+ */
+ WARN_ON_ONCE(req->rl_sendctx);
+
+ kref_init(&req->rl_kref);
+
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
+ if (bc_prealloc(rqst)) {
+ spin_lock(&xprt->bc_pa_lock);
+ list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list);
+ spin_unlock(&xprt->bc_pa_lock);
+ return;
+ }
+#endif
+
+ if (xprt_wake_up_backlog(xprt, rqst))
+ return;
+
+ r_xprt = rpcx_to_rdmax(xprt);
+ memset(rqst, 0, sizeof(*rqst));
+ rpcrdma_buffer_put(&r_xprt->rx_buf, req);
+}
+
+void rpcrdma_req_put(struct rpcrdma_req *req)
+{
+ kref_put(&req->rl_kref, rpcrdma_req_release);
+}
+
/**
* xprt_rdma_alloc_slot - allocate an rpc_rqst
* @xprt: controlling RPC transport
@@ -506,13 +557,28 @@ xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
req = rpcrdma_buffer_get(&r_xprt->rx_buf);
if (!req)
goto out_sleep;
+ kref_init(&req->rl_kref);
task->tk_rqstp = &req->rl_slot;
task->tk_status = 0;
return;
out_sleep:
- task->tk_status = -ENOMEM;
- xprt_add_backlog(xprt, task);
+ task->tk_status = -EAGAIN;
+ xprt_add_backlog_noncongested(xprt, task);
+ /* A buffer freed between buffer_get and rpc_sleep_on
+ * goes back to the pool with no waiter to wake.
+ * Re-check after joining the backlog to close that gap.
+ */
+ req = rpcrdma_buffer_get(&r_xprt->rx_buf);
+ if (req) {
+ struct rpc_rqst *rqst = &req->rl_slot;
+
+ kref_init(&req->rl_kref);
+ if (!xprt_wake_up_backlog(xprt, rqst)) {
+ memset(rqst, 0, sizeof(*rqst));
+ rpcrdma_buffer_put(&r_xprt->rx_buf, req);
+ }
+ }
}
/**
@@ -528,10 +594,7 @@ xprt_rdma_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *rqst)
container_of(xprt, struct rpcrdma_xprt, rx_xprt);
rpcrdma_reply_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst));
- if (!xprt_wake_up_backlog(xprt, rqst)) {
- memset(rqst, 0, sizeof(*rqst));
- rpcrdma_buffer_put(&r_xprt->rx_buf, rpcr_to_rdmar(rqst));
- }
+ rpcrdma_req_put(rpcr_to_rdmar(rqst));
}
static bool rpcrdma_check_regbuf(struct rpcrdma_xprt *r_xprt,
@@ -595,10 +658,10 @@ xprt_rdma_free(struct rpc_task *task)
frwr_unmap_sync(rpcx_to_rdmax(rqst->rq_xprt), req);
}
- /* XXX: If the RPC is completing because of a signal and
- * not because a reply was received, we ought to ensure
- * that the Send completion has fired, so that memory
- * involved with the Send is not still visible to the NIC.
+ /* The Send-side rl_kref owner keeps req out of its free pool
+ * until rpcrdma_sendctx_unmap() has fired -- see I4 above
+ * rpcrdma_reply_handler() -- so signal-driven release here
+ * does not let the HCA touch a recycled send buffer.
*/
}
@@ -704,7 +767,7 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
r_xprt->rx_stats.mrs_allocated,
r_xprt->rx_stats.local_inv_needed,
r_xprt->rx_stats.empty_sendctx_q,
- r_xprt->rx_stats.reply_waits_for_send);
+ 0LU); /* was reply_waits_for_send; column preserved */
}
static int
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 4132a505d742..27bb176f082f 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -65,18 +65,24 @@
static int rpcrdma_sendctxs_create(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt);
+static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf,
+ unsigned long item);
static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_sendctx *sc);
static int rpcrdma_reqs_setup(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_reqs_reset(struct rpcrdma_xprt *r_xprt);
-static void rpcrdma_rep_destroy(struct rpcrdma_rep *rep);
static void rpcrdma_reps_unmap(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_mrs_create(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_mrs_destroy(struct rpcrdma_xprt *r_xprt);
static void rpcrdma_ep_get(struct rpcrdma_ep *ep);
static int rpcrdma_ep_put(struct rpcrdma_ep *ep);
static struct rpcrdma_regbuf *
+rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction,
+ int node);
+static struct rpcrdma_regbuf *
rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction);
+static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb,
+ size_t size, gfp_t flags, int node);
static void rpcrdma_regbuf_dma_unmap(struct rpcrdma_regbuf *rb);
static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb);
@@ -505,7 +511,7 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt)
* outstanding Receives.
*/
rpcrdma_ep_get(ep);
- rpcrdma_post_recvs(r_xprt, 1, true);
+ rpcrdma_post_recvs(r_xprt, 1);
rc = rdma_connect(ep->re_id, &ep->re_remote_cma);
if (rc)
@@ -532,7 +538,17 @@ int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt)
goto out;
}
rpcrdma_mrs_create(r_xprt);
- frwr_wp_create(r_xprt);
+
+ /*
+ * rpcrdma_encode_write_list() dereferences the write-pad
+ * MR with no NULL check, so fail the connect rather than
+ * publish a transport whose write-pad MR is NULL.
+ */
+ rc = frwr_wp_create(r_xprt);
+ if (rc) {
+ rc = -ENOTCONN;
+ goto out;
+ }
out:
trace_xprtrdma_connect(r_xprt, rc);
@@ -564,9 +580,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt)
rpcrdma_xprt_drain(r_xprt);
rpcrdma_reps_unmap(r_xprt);
+ rpcrdma_sendctxs_destroy(r_xprt);
rpcrdma_reqs_reset(r_xprt);
rpcrdma_mrs_destroy(r_xprt);
- rpcrdma_sendctxs_destroy(r_xprt);
if (rpcrdma_ep_put(ep))
rdma_destroy_id(id);
@@ -598,6 +614,25 @@ static void rpcrdma_sendctxs_destroy(struct rpcrdma_xprt *r_xprt)
if (!buf->rb_sc_ctxs)
return;
+
+ /* The QP is drained, but the final unsignaled Sends might not
+ * have been walked by a signaled Send completion. Release those
+ * Send owners before request buffers are reset.
+ *
+ * Unlike the completion sweep, this walk can visit slots with
+ * no Send posted: after a partial rpcrdma_sendctxs_create()
+ * failure on reconnect, rb_sc_head and rb_sc_tail are stale,
+ * and slots between them can be NULL or have sc_req clear.
+ */
+ for (i = rpcrdma_sendctx_next(buf, buf->rb_sc_tail);
+ i != rpcrdma_sendctx_next(buf, buf->rb_sc_head);
+ i = rpcrdma_sendctx_next(buf, i)) {
+ struct rpcrdma_sendctx *sc = buf->rb_sc_ctxs[i];
+
+ if (sc && sc->sc_req)
+ rpcrdma_sendctx_unmap(sc);
+ }
+
for (i = 0; i <= buf->rb_sc_last; i++)
kfree(buf->rb_sc_ctxs[i]);
kfree(buf->rb_sc_ctxs);
@@ -660,6 +695,12 @@ static unsigned long rpcrdma_sendctx_next(struct rpcrdma_buffer *buf,
return likely(item < buf->rb_sc_last) ? item + 1 : 0;
}
+static unsigned long rpcrdma_sendctx_prev(struct rpcrdma_buffer *buf,
+ unsigned long item)
+{
+ return item > 0 ? item - 1 : buf->rb_sc_last;
+}
+
/**
* rpcrdma_sendctx_get_locked - Acquire a send context
* @r_xprt: controlling transport instance
@@ -704,6 +745,29 @@ struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt)
return NULL;
}
+/**
+ * rpcrdma_sendctx_unget_locked - Release an unposted send context
+ * @r_xprt: controlling transport instance
+ * @sc: send context to release
+ *
+ * Usage: Called when no Send is posted for the sendctx most
+ * recently returned by rpcrdma_sendctx_get_locked().
+ *
+ * The caller serializes calls to this function and to
+ * rpcrdma_sendctx_get_locked() (per transport).
+ */
+void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt,
+ struct rpcrdma_sendctx *sc)
+{
+ struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
+
+ if (WARN_ON_ONCE(buf->rb_sc_ctxs[buf->rb_sc_head] != sc))
+ return;
+
+ buf->rb_sc_head = rpcrdma_sendctx_prev(buf, buf->rb_sc_head);
+ xprt_write_space(&r_xprt->rx_xprt);
+}
+
/**
* rpcrdma_sendctx_put_locked - Release a send context
* @r_xprt: controlling transport instance
@@ -720,15 +784,18 @@ static void rpcrdma_sendctx_put_locked(struct rpcrdma_xprt *r_xprt,
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
unsigned long next_tail;
- /* Unmap SGEs of previously completed but unsignaled
- * Sends by walking up the queue until @sc is found.
+ /* Release previously completed but unsignaled Sends by walking
+ * up the queue until @sc is found.
*/
next_tail = buf->rb_sc_tail;
do {
+ struct rpcrdma_sendctx *cur;
+
next_tail = rpcrdma_sendctx_next(buf, next_tail);
/* ORDER: item must be accessed _before_ tail is updated */
- rpcrdma_sendctx_unmap(buf->rb_sc_ctxs[next_tail]);
+ cur = buf->rb_sc_ctxs[next_tail];
+ rpcrdma_sendctx_unmap(cur);
} while (buf->rb_sc_ctxs[next_tail] != sc);
@@ -938,18 +1005,20 @@ static void rpcrdma_reqs_reset(struct rpcrdma_xprt *r_xprt)
}
static noinline
-struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
- bool temp)
+struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt)
{
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
+ struct rpcrdma_ep *ep = r_xprt->rx_ep;
+ struct ib_device *device = ep->re_id->device;
struct rpcrdma_rep *rep;
rep = kzalloc(sizeof(*rep), XPRTRDMA_GFP_FLAGS);
if (rep == NULL)
goto out;
- rep->rr_rdmabuf = rpcrdma_regbuf_alloc(r_xprt->rx_ep->re_inline_recv,
- DMA_FROM_DEVICE);
+ rep->rr_rdmabuf = rpcrdma_regbuf_alloc_node(ep->re_inline_recv,
+ DMA_FROM_DEVICE,
+ ibdev_to_node(device));
if (!rep->rr_rdmabuf)
goto out_free;
@@ -964,7 +1033,6 @@ struct rpcrdma_rep *rpcrdma_rep_create(struct rpcrdma_xprt *r_xprt,
rep->rr_recv_wr.wr_cqe = &rep->rr_cqe;
rep->rr_recv_wr.sg_list = &rep->rr_rdmabuf->rg_iov;
rep->rr_recv_wr.num_sge = 1;
- rep->rr_temp = temp;
spin_lock(&buf->rb_lock);
list_add(&rep->rr_all, &buf->rb_all_reps);
@@ -983,17 +1051,6 @@ static void rpcrdma_rep_free(struct rpcrdma_rep *rep)
kfree(rep);
}
-static void rpcrdma_rep_destroy(struct rpcrdma_rep *rep)
-{
- struct rpcrdma_buffer *buf = &rep->rr_rxprt->rx_buf;
-
- spin_lock(&buf->rb_lock);
- list_del(&rep->rr_all);
- spin_unlock(&buf->rb_lock);
-
- rpcrdma_rep_free(rep);
-}
-
static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
{
struct llist_node *node;
@@ -1025,10 +1082,8 @@ static void rpcrdma_reps_unmap(struct rpcrdma_xprt *r_xprt)
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_rep *rep;
- list_for_each_entry(rep, &buf->rb_all_reps, rr_all) {
+ list_for_each_entry(rep, &buf->rb_all_reps, rr_all)
rpcrdma_regbuf_dma_unmap(rep->rr_rdmabuf);
- rep->rr_temp = true; /* Mark this rep for destruction */
- }
}
static void rpcrdma_reps_destroy(struct rpcrdma_buffer *buf)
@@ -1201,6 +1256,10 @@ void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
rpcrdma_rep_put(buffers, req->rl_reply);
req->rl_reply = NULL;
}
+ /* I2: rl_reply NULL after the put closes the
+ * 'rep on rb_free_reps still referenced by req' window.
+ */
+ WARN_ON_ONCE(req->rl_reply);
}
/**
@@ -1245,14 +1304,15 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
* or Replies they may be registered externally via frwr_map.
*/
static struct rpcrdma_regbuf *
-rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction)
+rpcrdma_regbuf_alloc_node(size_t size, enum dma_data_direction direction,
+ int node)
{
struct rpcrdma_regbuf *rb;
- rb = kmalloc(sizeof(*rb), XPRTRDMA_GFP_FLAGS);
+ rb = kmalloc_node(sizeof(*rb), XPRTRDMA_GFP_FLAGS, node);
if (!rb)
return NULL;
- rb->rg_data = kmalloc(size, XPRTRDMA_GFP_FLAGS);
+ rb->rg_data = kmalloc_node(size, XPRTRDMA_GFP_FLAGS, node);
if (!rb->rg_data) {
kfree(rb);
return NULL;
@@ -1264,6 +1324,12 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction)
return rb;
}
+static struct rpcrdma_regbuf *
+rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction)
+{
+ return rpcrdma_regbuf_alloc_node(size, direction, NUMA_NO_NODE);
+}
+
/**
* rpcrdma_regbuf_realloc - re-allocate a SEND/RECV buffer
* @rb: regbuf to reallocate
@@ -1274,10 +1340,16 @@ rpcrdma_regbuf_alloc(size_t size, enum dma_data_direction direction)
* returned, @rb is left untouched.
*/
bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags)
+{
+ return rpcrdma_regbuf_realloc_node(rb, size, flags, NUMA_NO_NODE);
+}
+
+static bool rpcrdma_regbuf_realloc_node(struct rpcrdma_regbuf *rb,
+ size_t size, gfp_t flags, int node)
{
void *buf;
- buf = kmalloc(size, flags);
+ buf = kmalloc_node(size, flags, node);
if (!buf)
return false;
@@ -1289,6 +1361,23 @@ bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size, gfp_t flags)
return true;
}
+static bool rpcrdma_rep_resize(struct rpcrdma_xprt *r_xprt,
+ struct rpcrdma_rep *rep)
+{
+ struct rpcrdma_regbuf *rb = rep->rr_rdmabuf;
+ struct rpcrdma_ep *ep = r_xprt->rx_ep;
+ size_t size = ep->re_inline_recv;
+
+ if (likely(rdmab_length(rb) >= size))
+ return true;
+ if (!rpcrdma_regbuf_realloc_node(rb, size, XPRTRDMA_GFP_FLAGS,
+ ibdev_to_node(ep->re_id->device)))
+ return false;
+
+ xdr_buf_init(&rep->rr_hdrbuf, rdmab_data(rb), rdmab_length(rb));
+ return true;
+}
+
/**
* __rpcrdma_regbuf_dma_map - DMA-map a regbuf
* @r_xprt: controlling transport instance
@@ -1341,10 +1430,9 @@ static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb)
* rpcrdma_post_recvs - Refill the Receive Queue
* @r_xprt: controlling transport instance
* @needed: current credit grant
- * @temp: mark Receive buffers to be deleted after one use
*
*/
-void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp)
+void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed)
{
struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpcrdma_ep *ep = r_xprt->rx_ep;
@@ -1358,8 +1446,7 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp)
if (likely(ep->re_receive_count > needed))
goto out;
needed -= ep->re_receive_count;
- if (!temp)
- needed += RPCRDMA_MAX_RECV_BATCH;
+ needed += RPCRDMA_MAX_RECV_BATCH;
if (atomic_inc_return(&ep->re_receiving) > 1)
goto out_dec;
@@ -1368,14 +1455,16 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp)
wr = NULL;
while (needed) {
rep = rpcrdma_rep_get_locked(buf);
- if (rep && rep->rr_temp) {
- rpcrdma_rep_destroy(rep);
- continue;
- }
if (!rep)
- rep = rpcrdma_rep_create(r_xprt, temp);
+ rep = rpcrdma_rep_create(r_xprt);
if (!rep)
break;
+ /* I1: a rep on rb_free_reps must carry no rqst pointer. */
+ WARN_ON_ONCE(rep->rr_rqst);
+ if (!rpcrdma_rep_resize(r_xprt, rep)) {
+ rpcrdma_rep_put(buf, rep);
+ break;
+ }
if (!rpcrdma_regbuf_dma_map(r_xprt, rep->rr_rdmabuf)) {
rpcrdma_rep_put(buf, rep);
break;
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index da409450dfc0..c78734e93c71 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -198,7 +198,6 @@ struct rpcrdma_rep {
__be32 rr_proc;
int rr_wc_flags;
u32 rr_inv_rkey;
- bool rr_temp;
struct rpcrdma_regbuf *rr_rdmabuf;
struct rpcrdma_xprt *rr_rxprt;
struct rpc_rqst *rr_rqst;
@@ -409,7 +408,6 @@ struct rpcrdma_stats {
/* accessed when receiving a reply */
unsigned long long total_rdma_reply;
unsigned long long fixup_copy_count;
- unsigned long reply_waits_for_send;
unsigned long local_inv_needed;
unsigned long nomsg_call_count;
unsigned long bcall_count;
@@ -466,7 +464,7 @@ void rpcrdma_flush_disconnect(struct rpcrdma_xprt *r_xprt, struct ib_wc *wc);
int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt);
void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt);
-void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp);
+void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed);
/*
* Buffer calls - xprtrdma/verbs.c
@@ -478,6 +476,8 @@ void rpcrdma_req_destroy(struct rpcrdma_req *req);
int rpcrdma_buffer_create(struct rpcrdma_xprt *);
void rpcrdma_buffer_destroy(struct rpcrdma_buffer *);
struct rpcrdma_sendctx *rpcrdma_sendctx_get_locked(struct rpcrdma_xprt *r_xprt);
+void rpcrdma_sendctx_unget_locked(struct rpcrdma_xprt *r_xprt,
+ struct rpcrdma_sendctx *sc);
struct rpcrdma_mr *rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt);
void rpcrdma_mrs_refresh(struct rpcrdma_xprt *r_xprt);
@@ -487,6 +487,7 @@ void rpcrdma_buffer_put(struct rpcrdma_buffer *buffers,
struct rpcrdma_req *req);
void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep);
void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req);
+void rpcrdma_req_put(struct rpcrdma_req *req);
bool rpcrdma_regbuf_realloc(struct rpcrdma_regbuf *rb, size_t size,
gfp_t flags);
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 3660ef264711..6263aa95e2ec 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2713,8 +2713,11 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
lower_xprt = rcu_dereference(lower_clnt->cl_xprt);
rcu_read_unlock();
- if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE))
+ if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) {
+ /* XPRT_LOCKED was never acquired. */
+ rpc_shutdown_client(lower_clnt);
goto out_unlock;
+ }
status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec);
if (status) {
@@ -2748,6 +2751,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work)
out_unlock:
current_restore_flags(pflags, PF_MEMALLOC);
upper_transport->clnt = NULL;
+ rpc_release_client(upper_clnt);
xprt_unlock_connect(upper_xprt, upper_transport);
return;
@@ -2795,7 +2799,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
} else
dprintk("RPC: xs_connect scheduled xprt %p\n", xprt);
- transport->clnt = task->tk_client;
+ /*
+ * Only the TLS connect_worker reads transport->clnt; pinning
+ * the upper rpc_clnt unconditionally would form a cycle with
+ * cl_xprt and prevent xprt destruction.
+ */
+ if (xprt->xprtsec.policy != RPC_XPRTSEC_NONE) {
+ rpc_hold_client(task->tk_client);
+ transport->clnt = task->tk_client;
+ }
queue_delayed_work(xprtiod_workqueue,
&transport->connect_worker,
delay);
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 114fef65f92e..4ae097cfcd2d 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
*/
int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr,
- struct sk_buff_head *retrq)
+ struct sk_buff_head *retrq, bool *valid)
{
struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq;
struct tipc_gap_ack_blks *ga;
struct sk_buff_head xmitq;
int rc = 0;
+ u16 glen;
__skb_queue_head_init(&xmitq);
@@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
if (msg_type(hdr) != STATE_MSG) {
tipc_link_bc_init_rcv(l, hdr);
} else if (!msg_bc_ack_invalid(hdr)) {
- tipc_get_gap_ack_blks(&ga, l, hdr, false);
- if (!sysctl_tipc_bc_retruni)
- retrq = &xmitq;
- rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr),
- msg_bc_gap(hdr), ga, &xmitq,
- retrq);
- rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq);
+ glen = tipc_get_gap_ack_blks(&ga, l, hdr, false);
+ if (glen > msg_data_sz(hdr)) {
+ /* Malformed Gap ACK blocks; caller drops the msg */
+ *valid = false;
+ } else {
+ if (!sysctl_tipc_bc_retruni)
+ retrq = &xmitq;
+ rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr),
+ msg_bc_gap(hdr), ga, &xmitq,
+ retrq);
+ rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq);
+ }
}
tipc_bcast_unlock(net);
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index 2d9352dc7b0e..55d17b5413e1 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr);
int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l,
struct tipc_msg *hdr,
- struct sk_buff_head *retrq);
+ struct sk_buff_head *retrq, bool *valid);
int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg,
struct tipc_link *bcl);
int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]);
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index fec638e494c9..ae57ac1174f8 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -482,6 +482,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b)
dev = (struct net_device *)rtnl_dereference(b->media_ptr);
dev_remove_pack(&b->pt);
RCU_INIT_POINTER(dev->tipc_ptr, NULL);
+ RCU_INIT_POINTER(b->media_ptr, NULL);
synchronize_net();
dev_put(dev);
}
diff --git a/net/tipc/core.c b/net/tipc/core.c
index 434e70eabe08..1ddecea1df6e 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)
unregister_pernet_device(&tipc_net_ops);
tipc_unregister_sysctl();
+ /* TODO: Wait for all timers that called call_rcu() to finish before
+ * calling rcu_barrier().
+ */
+ rcu_barrier();
+
pr_info("Deactivated\n");
}
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index 685389d4b245..da60f572dda0 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -58,6 +58,7 @@
* @skb: request message to be (repeatedly) sent
* @timer: timer governing period between requests
* @timer_intv: current interval between requests (in ms)
+ * @rcu: RCU head for deferred freeing
*/
struct tipc_discoverer {
u32 bearer_id;
@@ -69,6 +70,7 @@ struct tipc_discoverer {
struct sk_buff *skb;
struct timer_list timer;
unsigned long timer_intv;
+ struct rcu_head rcu;
};
/**
@@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
return 0;
}
+static void tipc_disc_free_rcu(struct rcu_head *rp)
+{
+ struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer,
+ rcu);
+
+ kfree_skb(d->skb);
+ kfree(d);
+}
+
/**
* tipc_disc_delete - destroy object sending periodic link setup requests
* @d: ptr to link dest structure
@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,
void tipc_disc_delete(struct tipc_discoverer *d)
{
timer_shutdown_sync(&d->timer);
- kfree_skb(d->skb);
- kfree(d);
+ call_rcu(&d->rcu, tipc_disc_free_rcu);
}
/**
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 190b49c5cbc3..ba4f4906e13b 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -280,12 +280,21 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
u32 node, u32 dtype)
{
struct publication *p = NULL;
+ u32 lower = ntohl(i->lower);
+ u32 upper = ntohl(i->upper);
struct tipc_socket_addr sk;
- struct tipc_uaddr ua;
u32 key = ntohl(i->key);
+ struct tipc_uaddr ua;
+
+ /* A peer-advertised binding with lower > upper can never be matched
+ * or withdrawn and would leak the publication; the local bind path
+ * rejects such ranges, so reject ranges learned from the network too.
+ */
+ if (lower > upper)
+ return false;
tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_CLUSTER_SCOPE,
- ntohl(i->type), ntohl(i->lower), ntohl(i->upper));
+ ntohl(i->type), lower, upper);
sk.ref = ntohl(i->port);
sk.node = node;
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 1a9a5bdaccf4..8336a9664703 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -152,11 +152,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_BEARER_DISABLE,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_bearer_disable,
},
{
.cmd = TIPC_NL_BEARER_ENABLE,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_bearer_enable,
},
{
@@ -168,11 +170,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_BEARER_ADD,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_bearer_add,
},
{
.cmd = TIPC_NL_BEARER_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_bearer_set,
},
{
@@ -197,11 +201,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_LINK_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_node_set_link,
},
{
.cmd = TIPC_NL_LINK_RESET_STATS,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_node_reset_link_stats,
},
{
@@ -213,6 +219,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_MEDIA_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_media_set,
},
{
@@ -228,6 +235,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_NET_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_net_set,
},
{
@@ -238,6 +246,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_MON_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_node_set_monitor,
},
{
@@ -255,6 +264,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_PEER_REMOVE,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_peer_rm,
},
#ifdef CONFIG_TIPC_MEDIA_UDP
@@ -269,11 +279,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
{
.cmd = TIPC_NL_KEY_SET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_node_set_key,
},
{
.cmd = TIPC_NL_KEY_FLUSH,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .flags = GENL_UNS_ADMIN_PERM,
.doit = tipc_nl_node_flush_key,
},
#endif
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 69053c039825..97b5ce4c678f 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1833,12 +1833,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n)
}
static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr,
- int bearer_id, struct sk_buff_head *xmitq)
+ int bearer_id, struct sk_buff_head *xmitq,
+ bool *valid)
{
struct tipc_link *ucl;
int rc;
- rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq);
+ rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid);
+ if (!*valid)
+ return;
if (rc & TIPC_LINK_DOWN_EVT) {
tipc_node_reset_links(n);
@@ -2142,12 +2145,18 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
/* Ensure broadcast reception is in synch with peer's send state */
if (unlikely(usr == LINK_PROTOCOL)) {
+ bool valid = true;
+
if (unlikely(skb_linearize(skb))) {
tipc_node_put(n);
goto discard;
}
hdr = buf_msg(skb);
- tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq);
+ tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid);
+ if (!valid) {
+ tipc_node_put(n);
+ goto discard;
+ }
} else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) {
tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr);
}
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index e5931e56ef09..31c6b61f0b0f 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1367,6 +1367,9 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
__skb_queue_tail(xmitq, skb);
return;
} else if (mtyp == CONN_ACK) {
+ if (tsk->snt_unacked < msg_conn_ack(hdr))
+ goto exit;
+
was_cong = tsk_conn_cong(tsk);
tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr));
tsk->snt_unacked -= msg_conn_ack(hdr);
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 89f01227ca5b..39021fab3c59 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2392,6 +2392,17 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_requeue;
}
+ /* An empty data record (legal in TLS 1.3) gives a zero
+ * read_actor return, indistinguishable from the consumer
+ * stalling; the used <= 0 path would requeue it at the
+ * head of rx_list and block all later records. Consume it
+ * here instead.
+ */
+ if (rxm->full_len == 0) {
+ consume_skb(skb);
+ continue;
+ }
+
used = read_actor(desc, skb, rxm->offset, rxm->full_len);
if (used <= 0) {
if (!copied)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 9f2e84af6f8b..e4a490d12b50 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4456,7 +4456,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info,
if (wdev->valid_links) {
if (link_id == -1) {
GENL_SET_ERR_MSG(info,
- "link ID must for MLO group key");
+ "link ID must be set for MLO group key");
return -EINVAL;
}
if (!(wdev->valid_links & BIT(link_id))) {
diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c
index b83ba12a9b37..6fb984f9a182 100644
--- a/net/xfrm/espintcp.c
+++ b/net/xfrm/espintcp.c
@@ -213,43 +213,23 @@ static int espintcp_sendskmsg_locked(struct sock *sk,
struct sk_msg *skmsg = &emsg->skmsg;
bool more = flags & MSG_MORE;
struct scatterlist *sg;
- int done = 0;
int ret;
- sg = &skmsg->sg.data[skmsg->sg.start];
do {
struct bio_vec bvec;
- size_t size = sg->length - emsg->offset;
- int offset = sg->offset + emsg->offset;
- struct page *p;
-
- emsg->offset = 0;
+ sg = &skmsg->sg.data[skmsg->sg.start];
if (sg_is_last(sg) && !more)
msghdr.msg_flags &= ~MSG_MORE;
- p = sg_page(sg);
-retry:
- bvec_set_page(&bvec, p, size, offset);
- iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, size);
- ret = tcp_sendmsg_locked(sk, &msghdr, size);
- if (ret < 0) {
- emsg->offset = offset - sg->offset;
- skmsg->sg.start += done;
+ bvec_set_page(&bvec, sg_page(sg), sg->length, sg->offset);
+ iov_iter_bvec(&msghdr.msg_iter, ITER_SOURCE, &bvec, 1, sg->length);
+ ret = tcp_sendmsg_locked(sk, &msghdr, sg->length);
+ if (ret < 0)
return ret;
- }
-
- if (ret != size) {
- offset += ret;
- size -= ret;
- goto retry;
- }
- done++;
- put_page(p);
- sk_mem_uncharge(sk, sg->length);
- sg = sg_next(sg);
- } while (sg);
+ sk_msg_free_partial(sk, skmsg, ret);
+ } while (skmsg->sg.size);
memset(emsg, 0, sizeof(*emsg));
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 77bc0ad368d7..1483770f8456 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -470,7 +470,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
struct xfrm_offload *xo = xfrm_offload(skb);
struct sec_path *sp;
- if (encap_type < 0) {
+ if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0))) {
x = xfrm_input_state(skb);
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
@@ -493,9 +493,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
seq = XFRM_SKB_CB(skb)->seq.input.low;
goto resume;
}
-
- /* encap_type < -1 indicates a GRO call. */
- encap_type = 0;
+ /* GRO call */
seq = XFRM_SPI_SKB_CB(skb)->seq;
if (xo && (xo->flags & CRYPTO_DONE)) {
diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c
index 44e48c58aa32..77a58f262693 100644
--- a/net/xfrm/xfrm_interface_core.c
+++ b/net/xfrm/xfrm_interface_core.c
@@ -874,6 +874,9 @@ static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
struct net *net = xi->net;
struct xfrm_if_parms p = {};
+ if (!rtnl_dev_link_net_capable(dev, net))
+ return -EPERM;
+
xfrmi_netlink_parms(data, &p);
if (!p.if_id) {
NL_SET_ERR_MSG(extack, "if_id must be non zero");
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 2110941c8e9a..7eaaaf56e631 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -223,6 +223,9 @@ __xfrm6_selector_match(const struct xfrm_selector *sel, const struct flowi *fl)
bool xfrm_selector_match(const struct xfrm_selector *sel, const struct flowi *fl,
unsigned short family)
{
+ if (family != sel->family && sel->family != AF_UNSPEC)
+ return false;
+
switch (family) {
case AF_INET:
return __xfrm4_selector_match(sel, fl);
@@ -656,7 +659,7 @@ static void xfrm_byidx_resize(struct net *net)
static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total)
{
- unsigned int cnt = net->xfrm.policy_count[dir];
+ unsigned int cnt = READ_ONCE(net->xfrm.policy_count[dir]);
unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
if (total)
@@ -682,12 +685,12 @@ static inline int xfrm_byidx_should_resize(struct net *net, int total)
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si)
{
- si->incnt = net->xfrm.policy_count[XFRM_POLICY_IN];
- si->outcnt = net->xfrm.policy_count[XFRM_POLICY_OUT];
- si->fwdcnt = net->xfrm.policy_count[XFRM_POLICY_FWD];
- si->inscnt = net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
- si->outscnt = net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
- si->fwdscnt = net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+ si->incnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN]);
+ si->outcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT]);
+ si->fwdcnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD]);
+ si->inscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX]);
+ si->outscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX]);
+ si->fwdscnt = READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX]);
si->spdhcnt = net->xfrm.policy_idx_hmask;
si->spdhmcnt = xfrm_policy_hashmax;
}
@@ -2298,7 +2301,7 @@ static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
struct net *net = xp_net(pol);
list_add(&pol->walk.all, &net->xfrm.policy_all);
- net->xfrm.policy_count[dir]++;
+ WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] + 1);
xfrm_pol_hold(pol);
}
@@ -2318,7 +2321,7 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
}
list_del_init(&pol->walk.all);
- net->xfrm.policy_count[dir]--;
+ WRITE_ONCE(net->xfrm.policy_count[dir], net->xfrm.policy_count[dir] - 1);
return pol;
}
@@ -3192,7 +3195,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
/* To accelerate a bit... */
if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
- !net->xfrm.policy_count[XFRM_POLICY_OUT]))
+ !READ_ONCE(net->xfrm.policy_count[XFRM_POLICY_OUT])))
goto nopol;
xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id);
@@ -3264,7 +3267,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
nopol:
if ((!dst_orig->dev || !(dst_orig->dev->flags & IFF_LOOPBACK)) &&
- net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) {
+ READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) {
err = -EPERM;
goto error;
}
@@ -3683,12 +3686,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}
if (!pol) {
- if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) {
+ const bool is_crypto_offload = sp &&
+ (xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO);
+
+ if (READ_ONCE(net->xfrm.policy_default[dir]) == XFRM_USERPOLICY_BLOCK) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0;
}
- if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) {
+ if (sp && secpath_has_nontransport(sp, 0, &xerr_idx) && !is_crypto_offload) {
xfrm_secpath_reject(xerr_idx, skb, &fl);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index dab4d8fb9ad7..aab20bc35674 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2722,7 +2722,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
if (IS_ERR(data))
return PTR_ERR(data);
- if (in_compat_syscall()) {
+ if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) {
struct xfrm_translator *xtr = xfrm_get_translator();
if (!xtr) {
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index ec1bda65c82f..cd6898833ce0 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2217,9 +2217,9 @@ static int xfrm_notify_userpolicy(struct net *net)
}
up = nlmsg_data(nlh);
- up->in = net->xfrm.policy_default[XFRM_POLICY_IN];
- up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD];
- up->out = net->xfrm.policy_default[XFRM_POLICY_OUT];
+ up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]);
+ up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]);
+ up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]);
nlmsg_end(skb, nlh);
@@ -2243,13 +2243,13 @@ static int xfrm_set_default(struct sk_buff *skb, struct nlmsghdr *nlh,
struct xfrm_userpolicy_default *up = nlmsg_data(nlh);
if (xfrm_userpolicy_is_valid(up->in))
- net->xfrm.policy_default[XFRM_POLICY_IN] = up->in;
+ WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN], up->in);
if (xfrm_userpolicy_is_valid(up->fwd))
- net->xfrm.policy_default[XFRM_POLICY_FWD] = up->fwd;
+ WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD], up->fwd);
if (xfrm_userpolicy_is_valid(up->out))
- net->xfrm.policy_default[XFRM_POLICY_OUT] = up->out;
+ WRITE_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT], up->out);
rt_genid_bump_all(net);
@@ -2279,9 +2279,9 @@ static int xfrm_get_default(struct sk_buff *skb, struct nlmsghdr *nlh,
}
r_up = nlmsg_data(r_nlh);
- r_up->in = net->xfrm.policy_default[XFRM_POLICY_IN];
- r_up->fwd = net->xfrm.policy_default[XFRM_POLICY_FWD];
- r_up->out = net->xfrm.policy_default[XFRM_POLICY_OUT];
+ r_up->in = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_IN]);
+ r_up->fwd = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_FWD]);
+ r_up->out = READ_ONCE(net->xfrm.policy_default[XFRM_POLICY_OUT]);
nlmsg_end(r_skb, r_nlh);
return nlmsg_unicast(net->xfrm.nlsk, r_skb, portid);
@@ -3122,7 +3122,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!netlink_net_capable(skb, CAP_NET_ADMIN))
return -EPERM;
- if (in_compat_syscall()) {
+ if (IS_ENABLED(CONFIG_COMPAT_FOR_U64_ALIGNMENT) && in_compat_syscall()) {
struct xfrm_translator *xtr = xfrm_get_translator();
if (!xtr)
diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c
index ecc7db237f2e..0b7a6efdb247 100644
--- a/samples/trace_events/trace-events-sample.c
+++ b/samples/trace_events/trace-events-sample.c
@@ -107,6 +107,10 @@ int foo_bar_reg(void)
* for consistency sake, we still take the thread_mutex.
*/
simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn");
+ if (IS_ERR_OR_NULL(simple_tsk_fn)) {
+ pr_err("Failed to create simple_thread_fn\n");
+ simple_tsk_fn = NULL;
+ }
out:
mutex_unlock(&thread_mutex);
return 0;
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 662a5e7c37c2..6c4ff012a05e 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -323,9 +323,7 @@ static int conf_askvalue(struct symbol *sym, const char *def)
line[1] = 0;
if (!sym_is_changeable(sym)) {
- printf("%s\n", def);
- line[0] = '\n';
- line[1] = 0;
+ printf("%s\n", def ?: "");
return 0;
}
@@ -333,7 +331,7 @@ static int conf_askvalue(struct symbol *sym, const char *def)
case oldconfig:
case syncconfig:
if (sym_has_value(sym)) {
- printf("%s\n", def);
+ printf("%s\n", def ?: "");
return 0;
}
/* fall through */
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 157ebdb100c0..bb183924b7a2 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -71,10 +71,10 @@
struct rawdata_f_data {
struct aa_loaddata *loaddata;
+ DECLARE_FLEX_ARRAY(char, data);
};
#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
-#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1)
static void rawdata_f_data_free(struct rawdata_f_data *private)
{
@@ -174,6 +174,7 @@ static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref)
return NULL;
}
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
{
if (ref)
@@ -181,6 +182,7 @@ static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref)
count));
return NULL;
}
+#endif
static void aa_put_common_ref(struct aa_common_ref *ref)
{
@@ -1434,7 +1436,7 @@ static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
struct rawdata_f_data *private = file->private_data;
return simple_read_from_buffer(buf, size, ppos,
- RAWDATA_F_DATA_BUF(private),
+ private->data,
private->loaddata->size);
}
@@ -1467,8 +1469,7 @@ static int rawdata_open(struct inode *inode, struct file *file)
private->loaddata = loaddata;
error = decompress_zstd(loaddata->data, loaddata->compressed_size,
- RAWDATA_F_DATA_BUF(private),
- loaddata->size);
+ private->data, loaddata->size);
if (error)
goto fail_decompress;
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index d6500ec4f6b6..f14f8a4e8594 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -13,6 +13,7 @@
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
+#include <linux/mutex.h>
#include <linux/syscalls.h>
#include <linux/personality.h>
#include <linux/xattr.h>
@@ -1061,6 +1062,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
int count, int flags)
{
struct aa_profile *profile, *root, *hat = NULL;
+ struct aa_ns *ns, *new_ns;
struct aa_label *new;
struct label_it it;
bool sibling = false;
@@ -1071,6 +1073,32 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
AA_BUG(!hats);
AA_BUG(count < 1);
+ /*
+ * Acquire the newest label and then hold the lock until we choose a
+ * hat, so that profile replacement doesn't atomically truncate the
+ * list of potential hats. Because we are getting the namespaces from
+ * the profiles and label, we can rely on the namespaces being live
+ * and avoid incrementing their refcounts while grabbing the lock.
+ */
+ label = aa_get_label(label);
+ ns = labels_ns(label);
+
+retry:
+ mutex_lock_nested(&ns->lock, ns->level);
+ if (label_is_stale(label)) {
+ new = aa_get_newest_label(label);
+ new_ns = labels_ns(new);
+ if (new_ns != ns) {
+ aa_put_label(new);
+ mutex_unlock(&ns->lock);
+ ns = new_ns;
+ label = new;
+ goto retry;
+ }
+ aa_put_label(label);
+ label = new;
+ }
+
if (PROFILE_IS_HAT(labels_profile(label)))
sibling = true;
@@ -1079,7 +1107,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
name = hats[i];
label_for_each_in_ns(it, labels_ns(label), label, profile) {
if (sibling && PROFILE_IS_HAT(profile)) {
- root = aa_get_profile_rcu(&profile->parent);
+ root = aa_get_profile(profile->parent);
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
root = aa_get_profile(profile);
} else { /* conflicting change type */
@@ -1139,6 +1167,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
GLOBAL_ROOT_UID, info, error);
}
}
+ mutex_unlock(&ns->lock);
return ERR_PTR(error);
build:
@@ -1151,7 +1180,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
error = -ENOMEM;
goto fail;
} /* else if (IS_ERR) build_change_hat has logged error so return new */
-
+ mutex_unlock(&ns->lock);
return new;
}
@@ -1440,6 +1469,8 @@ int aa_change_profile(const char *fqname, int flags)
new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
aa_get_label(target),
aa_get_label(&profile->label));
+ if (IS_ERR_OR_NULL(new))
+ goto build_fail;
/*
* no new privs prevents domain transitions that would
* reduce restrictions.
@@ -1457,16 +1488,8 @@ int aa_change_profile(const char *fqname, int flags)
/* only transition profiles in the current ns */
if (stack)
new = aa_label_merge(label, target, GFP_KERNEL);
- if (IS_ERR_OR_NULL(new)) {
- info = "failed to build target label";
- if (!new)
- error = -ENOMEM;
- else
- error = PTR_ERR(new);
- new = NULL;
- perms.allow = 0;
- goto audit;
- }
+ if (IS_ERR_OR_NULL(new))
+ goto build_fail;
error = aa_replace_current_label(new);
} else {
if (new) {
@@ -1478,6 +1501,17 @@ int aa_change_profile(const char *fqname, int flags)
error = aa_set_current_onexec(target, stack);
}
+ goto audit;
+
+build_fail:
+ info = "failed to build target label";
+ if (!new)
+ error = -ENOMEM;
+ else
+ error = PTR_ERR(new);
+ new = NULL;
+ perms.allow = 0;
+
audit:
error = fn_for_each_in_ns(label, profile,
aa_audit_file(subj_cred,
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 73428072a754..37282a3c68a7 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -452,7 +452,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp)
return new;
fail:
- kfree(new);
+ aa_label_free(new);
return NULL;
}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index 3e632700d06f..390947c93020 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -21,6 +21,7 @@
struct aa_sfs_entry aa_sfs_entry_network[] = {
AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK),
+ AA_SFS_FILE_BOOLEAN("tcp-fast-open", 1),
{ }
};
@@ -216,6 +217,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark)
return PTR_ERR(label);
secmark->secid = label->secid;
+ aa_put_label(label);
return 0;
}
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index c70e68977d61..df76104e3b33 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1325,12 +1325,15 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
out:
aa_put_ns(ns);
+
+ ssize_t udata_sz = udata->size;
+
aa_put_profile_loaddata(udata);
kfree(ns_name);
if (error)
return error;
- return udata->size;
+ return udata_sz;
fail_lock:
mutex_unlock(&ns->lock);
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index 197d41f9c32b..542c3f0255ea 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -53,6 +53,8 @@ int aa_getprocattr(struct aa_label *label, char **string)
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
FLAG_HIDDEN_UNCONFINED);
if (len < 0) {
+ kfree(*string);
+ *string = NULL;
aa_put_ns(current_ns);
return len;
}
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 9b907c2fee60..07c6982e1fac 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -128,8 +128,8 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char *temp;
- int offset = 0;
- ssize_t rc, size = 0;
+ size_t offset = 0, size = 0;
+ ssize_t rc;
struct xattr_list *xattr;
if (*ppos != 0)
@@ -152,16 +152,22 @@ static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
return -ENOMEM;
}
+ temp[size] = '\0';
+
+ /*
+ * No truncation possible: size is computed over the same enabled
+ * xattrs under xattr_list_mutex, so offset never exceeds size.
+ */
list_for_each_entry(xattr, &evm_config_xattrnames, list) {
if (!xattr->enabled)
continue;
- sprintf(temp + offset, "%s\n", xattr->name);
- offset += strlen(xattr->name) + 1;
+ offset += snprintf(temp + offset, size + 1 - offset, "%s\n",
+ xattr->name);
}
mutex_unlock(&xattr_list_mutex);
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+ rc = simple_read_from_buffer(buf, count, ppos, temp, offset);
kfree(temp);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index df2580072cfe..e105349794f2 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
struct keyring_search_context *ctx = iterator_data;
const struct key *key = keyring_ptr_to_key(object);
unsigned long kflags = READ_ONCE(key->flags);
- short state = READ_ONCE(key->state);
+ short state = key_read_state(key);
kenter("{%d}", key->serial);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3f11c5ae8fbf..fbfec33bc43e 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3789,9 +3789,9 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
-static int __file_map_prot_check(const struct cred *cred,
- const struct file *file, unsigned long prot,
- bool shared, bool bf_user_file)
+static int __file_map_prot_check(const struct file *file, unsigned long prot,
+ bool shared, bool mounter_check,
+ bool bf_user_file)
{
struct inode *inode = NULL;
bool prot_exec = prot & PROT_EXEC;
@@ -3804,10 +3804,10 @@ static int __file_map_prot_check(const struct cred *cred,
inode = file_inode(file);
}
- if (default_noexec && prot_exec &&
+ if (!mounter_check && default_noexec && prot_exec &&
(!file || IS_PRIVATE(inode) || (!shared && prot_write))) {
int rc;
- u32 sid = cred_sid(cred);
+ u32 sid = current_sid();
/*
* We are making executable an anonymous mapping or a private
@@ -3820,6 +3820,8 @@ static int __file_map_prot_check(const struct cred *cred,
}
if (file) {
+ const struct cred *cred = mounter_check ?
+ file->f_cred : current_cred();
/* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
if (shared && prot_write)
@@ -3833,11 +3835,11 @@ static int __file_map_prot_check(const struct cred *cred,
return 0;
}
-static inline int file_map_prot_check(const struct cred *cred,
- const struct file *file,
- unsigned long prot, bool shared)
+static inline int file_map_prot_check(const struct file *file,
+ unsigned long prot, bool shared,
+ bool mounter_check)
{
- return __file_map_prot_check(cred, file, prot, shared, false);
+ return __file_map_prot_check(file, prot, shared, mounter_check, false);
}
static int selinux_mmap_addr(unsigned long addr)
@@ -3853,12 +3855,14 @@ static int selinux_mmap_addr(unsigned long addr)
return rc;
}
-static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
- unsigned long prot, bool shared)
+static int selinux_mmap_file_common(struct file *file, unsigned long prot,
+ bool shared, bool mounter_check)
{
if (file) {
int rc;
struct common_audit_data ad;
+ const struct cred *cred = mounter_check ?
+ file->f_cred : current_cred();
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
@@ -3867,15 +3871,16 @@ static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
return rc;
}
- return file_map_prot_check(cred, file, prot, shared);
+ return file_map_prot_check(file, prot, shared, mounter_check);
}
static int selinux_mmap_file(struct file *file,
unsigned long reqprot __always_unused,
unsigned long prot, unsigned long flags)
{
- return selinux_mmap_file_common(current_cred(), file, prot,
- (flags & MAP_TYPE) == MAP_SHARED);
+ return selinux_mmap_file_common(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED,
+ false);
}
/**
@@ -3907,8 +3912,9 @@ static int selinux_mmap_backing_file(struct vm_area_struct *vma,
if (vma->vm_flags & VM_EXEC)
prot |= PROT_EXEC;
- return selinux_mmap_file_common(backing_file->f_cred, backing_file,
- prot, vma->vm_flags & VM_SHARED);
+ return selinux_mmap_file_common(backing_file, prot,
+ vma->vm_flags & VM_SHARED,
+ true);
}
static int selinux_file_mprotect(struct vm_area_struct *vma,
@@ -3969,11 +3975,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
}
}
- rc = __file_map_prot_check(cred, file, prot, shared, backing_file);
+ rc = __file_map_prot_check(file, prot, shared, false, backing_file);
if (rc)
return rc;
if (backing_file) {
- rc = file_map_prot_check(file->f_cred, file, prot, shared);
+ rc = file_map_prot_check(file, prot, shared, true);
if (rc)
return rc;
}
@@ -5047,7 +5053,24 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
- return sock_has_perm(sock->sk, SOCKET__WRITE);
+ int rc;
+ struct sockaddr *const addr = msg->msg_name;
+ const int addrlen = msg->msg_namelen;
+
+ rc = sock_has_perm(sock->sk, SOCKET__WRITE);
+ if (rc)
+ return rc;
+
+ if (addr && (msg->msg_flags & MSG_FASTOPEN) &&
+ (sk_is_tcp(sock->sk) ||
+ (sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM &&
+ sock->sk->sk_protocol == IPPROTO_MPTCP))) {
+ rc = selinux_socket_connect(sock, addr, addrlen);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
}
static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index 850dc8c53e9b..d6da0c3f1740 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -947,6 +947,8 @@ static void layout_attached_codec(struct aoa_codec *codec)
if (lineout == 1)
ldev->gpio.methods->set_lineout(codec->gpio, 1);
ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
+ if (!ctl)
+ return;
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
strscpy(ctl->id.name,
"Headphone Switch", sizeof(ctl->id.name));
@@ -961,6 +963,8 @@ static void layout_attached_codec(struct aoa_codec *codec)
if (ldev->have_lineout_detect) {
ctl = snd_ctl_new1(&lineout_detect_choice,
ldev);
+ if (!ctl)
+ return;
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
strscpy(ctl->id.name,
"Headphone Detect Autoswitch",
@@ -968,6 +972,8 @@ static void layout_attached_codec(struct aoa_codec *codec)
aoa_snd_ctl_add(ctl);
ctl = snd_ctl_new1(&lineout_detected,
ldev);
+ if (!ctl)
+ return;
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
strscpy(ctl->id.name,
"Headphone Detected",
diff --git a/sound/core/init.c b/sound/core/init.c
index 81186af3ac62..0dc1d25be184 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1154,7 +1154,7 @@ EXPORT_SYMBOL(snd_card_file_remove);
* typically around calling control ops.
*
* The caller needs to pull down the refcount via snd_power_unref() later
- * no matter whether the error is returned from this function or not.
+ * when this function returns 0.
*
* Return: Zero if successful, or a negative error code.
*/
@@ -1167,7 +1167,11 @@ int snd_power_ref_and_wait(struct snd_card *card)
card->shutdown ||
snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
snd_power_unref(card), snd_power_ref(card));
- return card->shutdown ? -ENODEV : 0;
+ if (card->shutdown) {
+ snd_power_unref(card);
+ return -ENODEV;
+ }
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
@@ -1184,7 +1188,8 @@ int snd_power_wait(struct snd_card *card)
int ret;
ret = snd_power_ref_and_wait(card);
- snd_power_unref(card);
+ if (!ret)
+ snd_power_unref(card);
return ret;
}
EXPORT_SYMBOL(snd_power_wait);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 9d71decaddd6..1fddeb91c9f4 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -488,6 +488,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
memcpy(&tmpev, &cell->event, aligned_size);
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
+ tmpev.data.ext.ptr = NULL;
if (copy_to_user(buf, &tmpev, aligned_size)) {
err = -EFAULT;
break;
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 692860deec0c..85bcf04c74ea 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -211,7 +211,7 @@ int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
len -= offset;
if (len > count)
len = count;
- err = expand_var_event(event, offset, count, buf, true);
+ err = expand_var_event(event, offset, len, buf, true);
if (err < 0)
return err;
return len;
@@ -366,7 +366,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
size = snd_seq_event_packet_size(event);
memcpy(&cell->ump, event, size);
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
- if (size < sizeof(cell->event))
+ if (size < sizeof(cell->ump))
cell->ump.raw.extra = 0;
#endif
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index 78dcb0ea1558..c8a7c3974711 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -24,6 +24,7 @@ Possible options for midisynth module:
#include <sound/seq_device.h>
#include <sound/seq_midi_event.h>
#include <sound/initval.h>
+#include "seq_lock.h"
MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
@@ -42,6 +43,8 @@ struct seq_midisynth {
int device;
int subdevice;
struct snd_rawmidi_file input_rfile;
+ spinlock_t output_lock; /* protects output_rfile publication */
+ snd_use_lock_t output_use_lock; /* in-flight event_input users */
struct snd_rawmidi_file output_rfile;
int seq_client;
int seq_port;
@@ -125,31 +128,42 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
struct seq_midisynth *msynth = private_data;
unsigned char msg[10]; /* buffer for constructing midi messages */
struct snd_rawmidi_substream *substream;
+ int err = 0;
int len;
if (snd_BUG_ON(!msynth))
return -EINVAL;
- substream = msynth->output_rfile.output;
- if (substream == NULL)
- return -ENODEV;
+
+ scoped_guard(spinlock_irqsave, &msynth->output_lock) {
+ substream = msynth->output_rfile.output;
+ if (!substream)
+ return -ENODEV;
+ snd_use_lock_use(&msynth->output_use_lock);
+ }
+
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
/* invalid event */
pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
- return 0;
+ goto out;
}
snd_seq_dump_var_event(ev, __dump_midi, substream);
snd_midi_event_reset_decode(msynth->parser);
} else {
- if (msynth->parser == NULL)
- return -EIO;
+ if (!msynth->parser) {
+ err = -EIO;
+ goto out;
+ }
len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
if (len < 0)
- return 0;
+ goto out;
if (dump_midi(substream, msg, len) < 0)
snd_midi_event_reset_decode(msynth->parser);
}
- return 0;
+
+out:
+ snd_use_lock_free(&msynth->output_use_lock);
+ return err;
}
@@ -163,6 +177,8 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
msynth->card = card;
msynth->device = device;
msynth->subdevice = subdevice;
+ spin_lock_init(&msynth->output_lock);
+ snd_use_lock_init(&msynth->output_use_lock);
return 0;
}
@@ -215,12 +231,13 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
{
int err;
struct seq_midisynth *msynth = private_data;
+ struct snd_rawmidi_file rfile = {};
struct snd_rawmidi_params params;
/* open midi port */
err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
SNDRV_RAWMIDI_LFLG_OUTPUT,
- &msynth->output_rfile);
+ &rfile);
if (err < 0) {
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
@@ -229,12 +246,14 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
params.avail_min = 1;
params.buffer_size = output_buffer_size;
params.no_active_sensing = 1;
- err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms);
+ err = snd_rawmidi_output_params(rfile.output, ¶ms);
if (err < 0) {
- snd_rawmidi_kernel_release(&msynth->output_rfile);
+ snd_rawmidi_kernel_release(&rfile);
return err;
}
snd_midi_event_reset_decode(msynth->parser);
+ scoped_guard(spinlock_irqsave, &msynth->output_lock)
+ msynth->output_rfile = rfile;
return 0;
}
@@ -242,11 +261,19 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
{
struct seq_midisynth *msynth = private_data;
+ struct snd_rawmidi_file rfile = {};
- if (snd_BUG_ON(!msynth->output_rfile.output))
+ scoped_guard(spinlock_irqsave, &msynth->output_lock) {
+ rfile = msynth->output_rfile;
+ msynth->output_rfile = (struct snd_rawmidi_file){};
+ }
+
+ if (snd_BUG_ON(!rfile.output))
return -EINVAL;
- snd_rawmidi_drain_output(msynth->output_rfile.output);
- return snd_rawmidi_kernel_release(&msynth->output_rfile);
+
+ snd_use_lock_sync(&msynth->output_use_lock);
+ snd_rawmidi_drain_output(rfile.output);
+ return snd_rawmidi_kernel_release(&rfile);
}
/* delete given midi synth port */
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
index 806f82c9ceee..dd006630728c 100644
--- a/sound/firewire/isight.c
+++ b/sound/firewire/isight.c
@@ -179,7 +179,8 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle,
if (likely(length >= 16 &&
payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
count = be32_to_cpu(payload->sample_count);
- if (likely(count <= (length - 16) / 4)) {
+ if (likely(count <= (length - 16) / 4 &&
+ count <= MAX_FRAMES_PER_PACKET)) {
total = be32_to_cpu(payload->sample_total);
if (unlikely(total != isight->total_samples)) {
if (!isight->first_packet)
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index d55c3dc229c0..e3c52a97e017 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -875,6 +875,8 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
else
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.index = control_index;
err = snd_ctl_add(card, kctl);
if (err < 0)
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 08e34b184780..caff7a4dbb3a 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -2688,16 +2688,22 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
}
if (cm->can_ac3_hw) {
kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = pcm_spdif_device;
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = pcm_spdif_device;
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = pcm_spdif_device;
err = snd_ctl_add(card, kctl);
if (err < 0)
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index ec598ba1a883..a2bf768e7406 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1670,6 +1670,8 @@ static int snd_es1938_mixer(struct es1938 *chip)
for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
+ if (!kctl)
+ return -ENOMEM;
switch (idx) {
case 0:
chip->master_volume = kctl;
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 027849329c1b..31b58a86058e 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -1900,6 +1900,8 @@ static int aureon_add_controls(struct snd_ice1712 *ice)
for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&cs8415_controls[i], ice);
+ if (!kctl)
+ return -ENOMEM;
if (i > 1)
kctl->id.device = ice->pcm->device;
err = snd_ctl_add(ice->card, kctl);
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 3b0c3e70987b..ad2180659187 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2372,21 +2372,29 @@ int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
if (snd_BUG_ON(!ice->pcm_pro))
return -EIO;
kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm_pro->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm_pro->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm_pro->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm_pro->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 1dc776acd637..c7716fea2793 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -2393,16 +2393,22 @@ static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
return err;
kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = ice->pcm->device;
err = snd_ctl_add(ice->card, kctl);
if (err < 0)
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 6b8d8690b6b2..2fa5ecfe5f9f 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -1822,16 +1822,22 @@ int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
if (snd_BUG_ON(!chip->pcm_spdif))
return -ENXIO;
kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = chip->pcm_spdif->device;
err = snd_ctl_add(chip->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = chip->pcm_spdif->device;
err = snd_ctl_add(chip->card, kctl);
if (err < 0)
return err;
kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.device = chip->pcm_spdif->device;
err = snd_ctl_add(chip->card, kctl);
if (err < 0)
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index 25f123110b5b..7dc0b5c79e39 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -812,6 +812,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)
if (adau1372->use_pll) {
ret = adau1372_enable_pll(adau1372);
if (ret) {
+ if (!adau1372->pd_gpio)
+ regmap_update_bits(adau1372->regmap,
+ ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_PLL_EN,
+ 0);
regcache_cache_only(adau1372->regmap, true);
if (adau1372->pd_gpio)
gpiod_set_value(adau1372->pd_gpio, 1);
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index f2a9c33ff4a6..eed05b2508dd 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -284,22 +284,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261)
if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
/* set FORCE_PWM */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
- AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+ ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
/* set BOOST_OS_WIDTH */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
- AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+ ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
/* set BURST_LOOPR */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
- AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+ ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
/* set RSQN_DLY */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
- AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+ ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
/* set BURST_SSMODE */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
- AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+ ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
/* set BST_BURST */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
- AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+ ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
} else {
dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
}
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 015269f0db54..832ff2cd3d24 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -1055,6 +1055,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
int cs35l56_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "system_suspend\n");
@@ -1070,7 +1071,11 @@ int cs35l56_system_suspend(struct device *dev)
if (cs35l56->base.irq)
disable_irq(cs35l56->base.irq);
- return pm_runtime_force_suspend(dev);
+ ret = pm_runtime_force_suspend(dev);
+ if ((ret < 0) && cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
@@ -1318,11 +1323,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
- goto err;
+ goto err_remove_wm_adsp;
}
return 0;
+err_remove_wm_adsp:
+ wm_adsp2_remove(&cs35l56->dsp);
+
err:
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
@@ -1420,6 +1428,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56)
flush_workqueue(cs35l56->dsp_wq);
destroy_workqueue(cs35l56->dsp_wq);
+ wm_adsp2_remove(&cs35l56->dsp);
+
pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
pm_runtime_suspend(cs35l56->base.dev);
pm_runtime_disable(cs35l56->base.dev);
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 0ddfb0cb376f..7707f0b5562d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -906,12 +906,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev);
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_pcm *pcm;
- const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+ const char *cvt_name;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
if (ret < 0)
return ret;
+ cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+
if (port == NULL)
return -EINVAL;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 591f6c9f9d3a..05eff1ed4e96 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ static const u8 dual_rate_q[] = {4, 8, 9, 12, 16};
struct snd_soc_component *component = dai->component;
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
+ bool dual_rate;
int clk;
int width = aic3x->slot_width;
@@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
/* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+ dual_rate = params_rate(params) >= 64000;
/* Try to find a value for Q which allows us to bypass the PLL and
* generate CODEC_CLK directly. */
- for (pll_q = 2; pll_q < 18; pll_q++)
- if (aic3x->sysclk / (128 * pll_q) == fsref) {
- bypass_pll = 1;
- break;
+ if (dual_rate) {
+ for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) {
+ pll_q = dual_rate_q[i];
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
}
+ } else {
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+ }
if (bypass_pll) {
pll_q &= 0xf;
@@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
* right DAC to right channel input */
data = (LDAC2LCH | RDAC2RCH);
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
- if (params_rate(params) >= 64000)
+ if (dual_rate)
data |= DUAL_RATE_MODE;
snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
data = (fsref * 20) / params_rate(params);
- if (params_rate(params) < 64000)
+ if (!dual_rate)
data /= 2;
data /= 5;
data -= 2;
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 05a7d1588d20..91dd85153e83 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -275,6 +275,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
config_be.dst_addr_width = buswidth;
config_be.dst_maxburst = dma_params_be->maxburst;
+ /*
+ * For eDMA, the back-end may report a maxburst size that is not evenly
+ * divisible by the channel count. This causes the DMA transfer length
+ * to misalign with the FIFO boundary, resulting in wrong data and
+ * audible noise. Align maxburst to the nearest valid boundary:
+ * - If maxburst >= channel count, override to the channel count so
+ * each transfer equals exactly one audio frame.
+ * - If maxburst < channel count, override to 1 to avoid partial-frame
+ * transfers.
+ */
+ if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) {
+ if (dma_params_be->maxburst >= params_channels(params)) {
+ config_be.src_maxburst = params_channels(params);
+ config_be.dst_maxburst = params_channels(params);
+ } else {
+ config_be.src_maxburst = 1;
+ config_be.dst_maxburst = 1;
+ }
+ }
+
memset(&audio_config, 0, sizeof(audio_config));
config_be.peripheral_config = &audio_config;
config_be.peripheral_size = sizeof(audio_config);
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
index d8e44470f501..8bc02ceda13c 100644
--- a/sound/soc/fsl/fsl_audmix.c
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -117,6 +117,9 @@ static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int reg_val, val, mix_clk;
+ if (item[0] >= e->items)
+ return -EINVAL;
+
/* Get current state */
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
@@ -157,6 +160,9 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
unsigned int reg_val, val, mask = 0, ctr = 0;
int ret;
+ if (item[0] >= e->items)
+ return -EINVAL;
+
/* Get current state */
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index cbee5a8764c3..865c61824613 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -1085,6 +1085,11 @@ static const dai_register_cb dai_register_cbs[] = {
mt8183_dai_memif_register,
};
+static void mt8183_afe_release_reserved_mem(void *data)
+{
+ of_reserved_mem_device_release(data);
+}
+
static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
@@ -1111,6 +1116,12 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
+ } else {
+ ret = devm_add_action_or_reset(dev,
+ mt8183_afe_release_reserved_mem,
+ dev);
+ if (ret)
+ return ret;
}
/* initial audio related clock */
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
index 364e43da0e24..06af6eb0e80c 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -2171,6 +2171,11 @@ static const dai_register_cb dai_register_cbs[] = {
mt8192_dai_memif_register,
};
+static void mt8192_afe_release_reserved_mem(void *data)
+{
+ of_reserved_mem_device_release(data);
+}
+
static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
@@ -2201,6 +2206,10 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
+ } else {
+ ret = devm_add_action_or_reset(dev, mt8192_afe_release_reserved_mem, dev);
+ if (ret)
+ return ret;
}
/* init audio related clock */
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 7b447cb50d50..e2ad6dc2dc85 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -533,6 +533,10 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
token = hdr->token & APM_WRITE_TOKEN_MASK;
done = data->payload;
+ if (!graph->rx_data.buf) {
+ mutex_unlock(&graph->lock);
+ break;
+ }
phys = graph->rx_data.buf[token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
@@ -567,6 +571,10 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
mutex_lock(&graph->lock);
rd_done = data->payload;
+ if (!graph->tx_data.buf) {
+ mutex_unlock(&graph->lock);
+ break;
+ }
phys = graph->tx_data.buf[hdr->token].phys;
mutex_unlock(&graph->lock);
/* token numbering starts at 0 */
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 1214dbba6898..923436ee6f7e 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -637,7 +637,7 @@ struct rsnd_priv {
#define RSND_GEN2 (2 << 0)
#define RSND_GEN3 (3 << 0)
#define RSND_GEN4 (4 << 0)
-#define RSND_SOC_MASK (0xFF << 4)
+#define RSND_SOC_MASK (0xF << 4)
#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */
/*
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index a8deec7dc021..62f4a0ad0c22 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -315,10 +315,13 @@ static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > scontrol->max_size - sizeof(*data)) {
+ if (data->size > scontrol->max_size - sizeof(*cdata) -
+ sizeof(*data)) {
dev_err_ratelimited(scomp->dev,
"%u bytes of control data is invalid, max is %zu\n",
- data->size, scontrol->max_size - sizeof(*data));
+ data->size,
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(*data));
return -EINVAL;
}
@@ -336,6 +339,8 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
struct sof_abi_hdr *data = cdata->data;
+ const struct sof_abi_hdr *new_hdr =
+ (const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
@@ -344,14 +349,18 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
- /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > scontrol->max_size - sizeof(*data)) {
- dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n",
- data->size, scontrol->max_size - sizeof(*data));
+ /* Validate the new data's size, not the old one */
+ if (new_hdr->size > scontrol->max_size - sizeof(*cdata) -
+ sizeof(*new_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "data size too big %u bytes max is %zu\n",
+ new_hdr->size,
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(*new_hdr));
return -EINVAL;
}
- size = data->size + sizeof(*data);
+ size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
@@ -389,9 +398,17 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* be->max is coming from topology */
- if (header.length > scontrol->max_size) {
- dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n",
- header.length, scontrol->max_size);
+ if (header.length > scontrol->max_size - sizeof(*cdata)) {
+ dev_err_ratelimited(scomp->dev, "Bytes data size %u exceeds max %zu\n",
+ header.length, scontrol->max_size - sizeof(*cdata));
+ return -EINVAL;
+ }
+
+ /* Ensure the data is large enough to contain the ABI header */
+ if (header.length < sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "Bytes data size %u less than ABI header %zu\n",
+ header.length, sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -427,7 +444,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
}
/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
+ if (cdata->data->size > scontrol->max_size - sizeof(*cdata) - sizeof(struct sof_abi_hdr)) {
dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
goto err_restore;
}
@@ -443,7 +460,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
err_restore:
/* If we have an issue, we restore the old, valid bytes control data */
if (scontrol->old_ipc_control_data) {
- memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size);
+ memcpy(cdata, scontrol->old_ipc_control_data, scontrol->max_size);
kfree(scontrol->old_ipc_control_data);
scontrol->old_ipc_control_data = NULL;
}
@@ -482,10 +499,13 @@ static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
}
/* check data size doesn't exceed max coming from topology */
- if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
- dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
+ if (cdata->data->size > scontrol->max_size - sizeof(*cdata) -
+ sizeof(struct sof_abi_hdr)) {
+ dev_err_ratelimited(scomp->dev,
+ "User data size %u exceeds max size %zu\n",
cdata->data->size,
- scontrol->max_size - sizeof(struct sof_abi_hdr));
+ scontrol->max_size - sizeof(*cdata) -
+ sizeof(struct sof_abi_hdr));
return -EINVAL;
}
@@ -535,6 +555,15 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol,
return;
}
+ /* Verify the size fits within the allocation */
+ if (cdata->num_elems > scontrol->max_size - sizeof(*local_cdata) -
+ sizeof(*local_cdata->data)) {
+ dev_err(scomp->dev,
+ "cdata binary size %u exceeds buffer\n",
+ cdata->num_elems);
+ return;
+ }
+
/* copy the new binary data */
memcpy(local_cdata->data, cdata->data, cdata->num_elems);
} else if (cdata->num_elems != scontrol->num_channels) {
@@ -626,16 +655,28 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_
return;
}
- expected_size = sizeof(struct sof_ipc_ctrl_data);
switch (cdata->type) {
case SOF_CTRL_TYPE_VALUE_CHAN_GET:
case SOF_CTRL_TYPE_VALUE_CHAN_SET:
- expected_size += cdata->num_elems *
- sizeof(struct sof_ipc_ctrl_value_chan);
+ if (check_mul_overflow((size_t)cdata->num_elems,
+ sizeof(struct sof_ipc_ctrl_value_chan),
+ &expected_size))
+ return;
+ if (check_add_overflow(expected_size,
+ sizeof(struct sof_ipc_ctrl_data),
+ &expected_size))
+ return;
break;
case SOF_CTRL_TYPE_DATA_GET:
case SOF_CTRL_TYPE_DATA_SET:
- expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
+ if (check_add_overflow((size_t)cdata->num_elems,
+ sizeof(struct sof_abi_hdr),
+ &expected_size))
+ return;
+ if (check_add_overflow(expected_size,
+ sizeof(struct sof_ipc_ctrl_data),
+ &expected_size))
+ return;
break;
default:
return;
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 3835179b22b4..c54989814ff8 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -407,6 +407,8 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_abi_hdr *data = cdata->data;
+ const struct sof_abi_hdr *new_hdr =
+ (const struct sof_abi_hdr *)ucontrol->value.bytes.data;
size_t size;
int ret;
@@ -417,15 +419,16 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,
return -EINVAL;
}
- /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
- if (data->size > scontrol->max_size - sizeof(*data)) {
+ /* Validate the new data's size, not the old one */
+ if (new_hdr->size > scontrol->max_size - sizeof(*new_hdr)) {
dev_err_ratelimited(scomp->dev,
"data size too big %u bytes max is %zu\n",
- data->size, scontrol->max_size - sizeof(*data));
+ new_hdr->size,
+ scontrol->max_size - sizeof(*new_hdr));
return -EINVAL;
}
- size = data->size + sizeof(*data);
+ size = new_hdr->size + sizeof(*new_hdr);
/* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 51a29d2de5ed..b2b6b7bb8788 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -719,10 +719,13 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
int ret;
while (array_size > 0 && total < count * token_instance_num) {
+ if (array_size < (int)sizeof(*array))
+ return -EINVAL;
+
asize = le32_to_cpu(array->size);
/* validate asize */
- if (asize < sizeof(*array)) {
+ if (asize < (int)sizeof(*array)) {
dev_err(scomp->dev, "error: invalid array size 0x%x\n",
asize);
return -EINVAL;
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index 140cb27f7328..f7918bcd918d 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -60,13 +60,15 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
struct soc_enum *e = (struct soc_enum *)kctl->private_value;
struct snd_soc_dapm_update update[TEGRA_XBAR_UPDATE_MAX_REG] = { };
unsigned int *item = uctl->value.enumerated.item;
- unsigned int value = e->values[item[0]];
+ unsigned int value;
unsigned int i, bit_pos, reg_idx = 0, reg_val = 0;
int change = 0;
if (item[0] >= e->items)
return -EINVAL;
+ value = e->values[item[0]];
+
if (value) {
/* Get the register index and value to set */
reg_idx = (value - 1) / (8 * cmpnt->val_bytes);
diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c
index 5c70fdf61cc1..2db4d1332df1 100644
--- a/sound/usb/caiaq/input.c
+++ b/sound/usb/caiaq/input.c
@@ -330,7 +330,7 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev,
{
struct device *dev = caiaqdev_to_dev(cdev);
- while (len) {
+ while (len >= TKS4_MSGBLOCK_SIZE) {
unsigned int i, block_id = (buf[0] << 8) | buf[1];
switch (block_id) {
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 42a82aad8a3b..925399e2cbbc 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -478,6 +478,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi,
static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep)
{
list_del(&ep->list);
+ if (!ep->disconnected) {
+ ep->disconnected = 1;
+ kill_midi_urbs(ep, false);
+ drain_urb_queue(ep);
+ }
free_midi_urbs(ep);
kfree(ep);
}
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index b85896a821b2..90de8ae32eb6 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -335,6 +335,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
int index = kcontrol->private_value & 0xff;
unsigned int value = ucontrol->value.integer.value[0];
int old_value = kcontrol->private_value >> 8;
+ unsigned long old_pval = kcontrol->private_value;
int err;
if (value > 1)
@@ -343,7 +344,11 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
return 0;
kcontrol->private_value = (value << 8) | index;
err = snd_audigy2nx_led_update(mixer, value, index);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}
static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list)
@@ -493,6 +498,7 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct usb_mixer_interface *mixer = list->mixer;
unsigned int value = ucontrol->value.enumerated.item[0];
+ unsigned long old_pval = kcontrol->private_value;
int err;
if (value > 1)
@@ -503,7 +509,11 @@ static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value = value;
err = snd_emu0204_ch_switch_update(mixer, value);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}
static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list)
@@ -573,46 +583,30 @@ static bool snd_dualsense_ih_match(struct input_handler *handler,
{
struct dualsense_mixer_elem_info *mei;
struct usb_device *snd_dev;
- char *input_dev_path, *usb_dev_path;
- size_t usb_dev_path_len;
- bool match = false;
+ struct device *parent;
mei = container_of(handler, struct dualsense_mixer_elem_info, ih);
snd_dev = mei->info.head.mixer->chip->dev;
- input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
- if (!input_dev_path) {
- dev_warn(&snd_dev->dev, "Failed to get input dev path\n");
- return false;
- }
-
- usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL);
- if (!usb_dev_path) {
- dev_warn(&snd_dev->dev, "Failed to get USB dev path\n");
- goto free_paths;
- }
-
/*
* Ensure the VID:PID matched input device supposedly owned by the
* hid-playstation driver belongs to the actual hardware handled by
- * the current USB audio device, which implies input_dev_path being
- * a subpath of usb_dev_path.
+ * the current USB audio device.
*
* This verification is necessary when there is more than one identical
* controller attached to the host system.
+ *
+ * The input device is registered below the HID device, USB interface and
+ * USB device, so compare the parent chain directly instead of building
+ * kobject path strings. This avoids dereferencing kobject names while the
+ * USB device hierarchy is being torn down during disconnect.
*/
- usb_dev_path_len = strlen(usb_dev_path);
- if (usb_dev_path_len >= strlen(input_dev_path))
- goto free_paths;
-
- usb_dev_path[usb_dev_path_len] = '/';
- match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1);
-
-free_paths:
- kfree(input_dev_path);
- kfree(usb_dev_path);
+ for (parent = dev->dev.parent; parent; parent = parent->parent) {
+ if (parent == &snd_dev->dev)
+ return true;
+ }
- return match;
+ return false;
}
static int snd_dualsense_ih_connect(struct input_handler *handler,
@@ -830,7 +824,11 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value = new_status;
err = snd_xonar_u1_switch_update(list->mixer, new_status);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_status;
+ return err;
+ }
+ return 1;
}
static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list)
@@ -1179,7 +1177,8 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
- u8 oldval = (kcontrol->private_value >> 24) & 0xff;
+ unsigned long old_pval = kcontrol->private_value;
+ u8 oldval = (old_pval >> 24) & 0xff;
u8 newval = ucontrol->value.integer.value[0];
int err;
@@ -1189,7 +1188,11 @@ static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol,
kcontrol->private_value &= ~(0xff << 24);
kcontrol->private_value |= (unsigned int)newval << 24;
err = snd_ni_update_cur_val(list);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}
static const struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = {
@@ -1347,7 +1350,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
- unsigned int pval = list->kctl->private_value;
+ unsigned long old_pval = list->kctl->private_value;
+ unsigned int pval = old_pval;
int cur_val, err, new_val;
cur_val = pval >> 24;
@@ -1358,7 +1362,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
kctl->private_value &= ~(0xff << 24);
kctl->private_value |= new_val << 24;
err = snd_ftu_eff_switch_update(list);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kctl->private_value = old_pval;
+ return err;
+ }
+ return 1;
}
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@@ -2156,13 +2164,18 @@ static int snd_soundblaster_e1_switch_put(struct snd_kcontrol *kcontrol,
{
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
unsigned char value = !!ucontrol->value.integer.value[0];
+ unsigned long old_pval = kcontrol->private_value;
int err;
if (kcontrol->private_value == value)
return 0;
kcontrol->private_value = value;
err = snd_soundblaster_e1_switch_update(list->mixer, value);
- return err < 0 ? err : 1;
+ if (err < 0) {
+ kcontrol->private_value = old_pval;
+ return err;
+ }
+ return 1;
}
static int snd_soundblaster_e1_switch_resume(struct usb_mixer_elem_list *list)
@@ -2937,12 +2950,14 @@ static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol,
if (val == old_value)
return 0;
+ err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
+ if (err < 0)
+ return err;
+
kcontrol->private_value = reg
| ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT)
| ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT);
-
- err = snd_bbfpro_ctl_update(mixer, reg, idx, val);
- return err < 0 ? err : 1;
+ return 1;
}
static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list)
@@ -3027,11 +3042,13 @@ static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol,
new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK;
+ err = snd_bbfpro_vol_update(mixer, idx, new_val);
+ if (err < 0)
+ return err;
+
kcontrol->private_value = idx
| (new_val << SND_BBFPRO_MIXER_VAL_SHIFT);
-
- err = snd_bbfpro_vol_update(mixer, idx, new_val);
- return err < 0 ? err : 1;
+ return 1;
}
static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list)
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 0d6e4f15bf77..9f94ab993419 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -438,7 +438,9 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl,
val = ucontrol->value.integer.value[0];
val = val + opt->start;
if (val != oval) {
- snd_usb_set_cur_mix_value(elem, 0, 0, val);
+ err = snd_usb_set_cur_mix_value(elem, 0, 0, val);
+ if (err < 0)
+ return err;
return 1;
}
return 0;
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index d05cb54de788..b74e198c365c 100644
--- a/sound/usb/mixer_us16x08.c
+++ b/sound/usb/mixer_us16x08.c
@@ -225,14 +225,14 @@ static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol,
@@ -284,14 +284,14 @@ static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol,
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set master, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
@@ -325,14 +325,14 @@ static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol,
break;
}
- if (err > 0) {
- elem->cached |= 1;
- elem->cache_val[0] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set bus parameter, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1;
+ elem->cache_val[0] = val;
+ return 1;
}
static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol,
@@ -393,14 +393,14 @@ static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err);
+ return err;
}
- return err > 0 ? 1 : 0;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
+ return 1;
}
static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol,
@@ -436,6 +436,7 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
int index = ucontrol->id.index;
char buf[sizeof(comp_msg)];
int val_idx, val;
+ int threshold, ratio, attack, release, gain, switch_on;
int err;
val = ucontrol->value.integer.value[0];
@@ -448,36 +449,61 @@ static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol,
/* new control value incl. bias*/
val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE;
- store->val[val_idx][index] = ucontrol->value.integer.value[0];
+ threshold = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)]
+ [index];
+ ratio = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index];
+ attack = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index];
+ release = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)]
+ [index];
+ gain = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
+ switch_on = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)]
+ [index];
+
+ switch (val_idx) {
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD):
+ threshold = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO):
+ ratio = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK):
+ attack = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE):
+ release = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN):
+ gain = val;
+ break;
+ case COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH):
+ switch_on = val;
+ break;
+ }
/* prepare compressor URB message from template */
memcpy(buf, comp_msg, sizeof(comp_msg));
/* place comp values in message buffer watch bias! */
- buf[8] = store->val[
- COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index]
- - SND_US16X08_COMP_THRESHOLD_BIAS;
- buf[11] = ratio_map[store->val[
- COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]];
- buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index]
- + SND_US16X08_COMP_ATTACK_BIAS;
- buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index]
- + SND_US16X08_COMP_RELEASE_BIAS;
- buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index];
- buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index];
+ buf[8] = threshold - SND_US16X08_COMP_THRESHOLD_BIAS;
+ buf[11] = ratio_map[ratio];
+ buf[14] = attack + SND_US16X08_COMP_ATTACK_BIAS;
+ buf[17] = release + SND_US16X08_COMP_RELEASE_BIAS;
+ buf[20] = gain;
+ buf[26] = switch_on;
/* place channel selector in message buffer */
buf[5] = index + 1;
err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg));
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err);
+ return err;
}
+ store->val[val_idx][index] = val;
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
@@ -530,13 +556,13 @@ static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol,
msleep(15);
}
- if (err > 0) {
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err);
+ return err;
}
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
@@ -579,11 +605,10 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
/* copy URB buffer from EQ template */
memcpy(buf, eqs_msq, sizeof(eqs_msq));
- store->val[b_idx][p_idx][index] = val;
- buf[20] = store->val[b_idx][3][index];
- buf[17] = store->val[b_idx][2][index];
- buf[14] = store->val[b_idx][1][index];
- buf[11] = store->val[b_idx][0][index];
+ buf[20] = p_idx == 3 ? val : store->val[b_idx][3][index];
+ buf[17] = p_idx == 2 ? val : store->val[b_idx][2][index];
+ buf[14] = p_idx == 1 ? val : store->val[b_idx][1][index];
+ buf[11] = p_idx == 0 ? val : store->val[b_idx][0][index];
/* place channel index in URB buffer */
buf[5] = index + 1;
@@ -593,14 +618,15 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol,
err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq));
- if (err > 0) {
- /* store new value in EQ band cache */
- elem->cached |= 1 << index;
- elem->cache_val[index] = val;
- } else {
+ if (err < 0) {
usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err);
+ return err;
}
+ store->val[b_idx][p_idx][index] = val;
+ /* store new value in EQ band cache */
+ elem->cached |= 1 << index;
+ elem->cache_val[index] = val;
return 1;
}
@@ -1419,4 +1445,3 @@ int snd_us16x08_controls_create(struct usb_mixer_interface *mixer)
return 0;
}
-
diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c
index 1ddec1f4f05d..bce83e08fb22 100644
--- a/sound/virtio/virtio_pcm.c
+++ b/sound/virtio/virtio_pcm.c
@@ -77,7 +77,8 @@ static const struct virtsnd_v2a_rate g_v2a_rate_map[] = {
[VIRTIO_SND_PCM_RATE_88200] = { SNDRV_PCM_RATE_88200, 88200 },
[VIRTIO_SND_PCM_RATE_96000] = { SNDRV_PCM_RATE_96000, 96000 },
[VIRTIO_SND_PCM_RATE_176400] = { SNDRV_PCM_RATE_176400, 176400 },
- [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 }
+ [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 },
+ [VIRTIO_SND_PCM_RATE_384000] = { SNDRV_PCM_RATE_384000, 384000 }
};
/**
diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c
index f8bfb87624be..d75b03202127 100644
--- a/sound/virtio/virtio_pcm_ops.c
+++ b/sound/virtio/virtio_pcm_ops.c
@@ -90,7 +90,8 @@ static const struct virtsnd_a2v_rate g_a2v_rate_map[] = {
{ 88200, VIRTIO_SND_PCM_RATE_88200 },
{ 96000, VIRTIO_SND_PCM_RATE_96000 },
{ 176400, VIRTIO_SND_PCM_RATE_176400 },
- { 192000, VIRTIO_SND_PCM_RATE_192000 }
+ { 192000, VIRTIO_SND_PCM_RATE_192000 },
+ { 384000, VIRTIO_SND_PCM_RATE_384000 }
};
static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream);
diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c
index 225d3678b4ed..2d07ffea589e 100644
--- a/tools/bpf/bpftool/net.c
+++ b/tools/bpf/bpftool/net.c
@@ -594,14 +594,14 @@ static int query_flow_dissector(struct bpf_attach_info *attach_info)
&attach_flags, prog_ids, &prog_cnt);
close(fd);
if (err) {
- if (errno == EINVAL) {
+ if (err == -EINVAL) {
/* Older kernel's don't support querying
* flow dissector programs.
*/
errno = 0;
return 0;
}
- p_err("can't query prog: %s", strerror(errno));
+ p_err("can't query prog: %s", strerror(-err));
return -1;
}
diff --git a/tools/include/linux/args.h b/tools/include/linux/args.h
new file mode 100644
index 000000000000..14b268f2389a
--- /dev/null
+++ b/tools/include/linux/args.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_ARGS_H
+#define _LINUX_ARGS_H
+
+/*
+ * How do these macros work?
+ *
+ * In __COUNT_ARGS() _0 to _12 are just placeholders from the start
+ * in order to make sure _n is positioned over the correct number
+ * from 12 to 0 (depending on X, which is a variadic argument list).
+ * They serve no purpose other than occupying a position. Since each
+ * macro parameter must have a distinct identifier, those identifiers
+ * are as good as any.
+ *
+ * In COUNT_ARGS() we use actual integers, so __COUNT_ARGS() returns
+ * that as _n.
+ */
+
+/* This counts to 15. Any more, it will return 16th argument. */
+#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _n, X...) _n
+#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+/* Concatenate two parameters, but allow them to be expanded beforehand. */
+#ifndef __CONCAT
+#define __CONCAT(a, b) a ## b
+#endif
+#ifndef CONCATENATE
+#define CONCATENATE(a, b) __CONCAT(a, b)
+#endif
+
+#endif /* _LINUX_ARGS_H */
diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h
index f18683b95ea6..bc6600466e7b 100644
--- a/tools/include/linux/bitops.h
+++ b/tools/include/linux/bitops.h
@@ -20,6 +20,8 @@
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(char))
+#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE)
+
extern unsigned int __sw_hweight8(unsigned int w);
extern unsigned int __sw_hweight16(unsigned int w);
extern unsigned int __sw_hweight32(unsigned int w);
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 8967952de0c2..d3b14f18c78f 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -6463,6 +6463,7 @@ struct bpf_prog_info {
__u32 verified_insns;
__u32 attach_btf_obj_id;
__u32 attach_btf_id;
+ __u32 :32;
} __attribute__((aligned(8)));
struct bpf_map_info {
diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index 5cb0eeec2c8a..36b901e81670 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -260,8 +260,8 @@ static const char *mount_overload(struct fs *fs)
/* "PERF_" + name + "_ENVIRONMENT" + '\0' */
char upper_name[5 + name_len + 12 + 1];
- snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name);
- mem_toupper(upper_name, name_len);
+ snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name);
+ mem_toupper(upper_name, strlen(upper_name));
return getenv(upper_name) ?: *fs->mounts;
}
@@ -293,11 +293,14 @@ int filename__read_int(const char *filename, int *value)
{
char line[64];
int fd = open(filename, O_RDONLY), err = -1;
+ ssize_t n;
if (fd < 0)
return -1;
- if (read(fd, line, sizeof(line)) > 0) {
+ n = read(fd, line, sizeof(line) - 1);
+ if (n > 0) {
+ line[n] = '\0';
*value = atoi(line);
err = 0;
}
@@ -311,11 +314,14 @@ static int filename__read_ull_base(const char *filename,
{
char line[64];
int fd = open(filename, O_RDONLY), err = -1;
+ ssize_t n;
if (fd < 0)
return -1;
- if (read(fd, line, sizeof(line)) > 0) {
+ n = read(fd, line, sizeof(line) - 1);
+ if (n > 0) {
+ line[n] = '\0';
*value = strtoull(line, NULL, base);
if (*value != ULLONG_MAX)
err = 0;
@@ -398,12 +404,13 @@ int filename__write_int(const char *filename, int value)
{
int fd = open(filename, O_WRONLY), err = -1;
char buf[64];
+ int len;
if (fd < 0)
return err;
- sprintf(buf, "%d", value);
- if (write(fd, buf, sizeof(buf)) == sizeof(buf))
+ len = sprintf(buf, "%d", value);
+ if (write(fd, buf, len) == len)
err = 0;
close(fd);
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 802ae6dbddfa..946ca07968a9 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10926,7 +10926,7 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,
static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
{
DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
- unsigned long offset = 0;
+ long offset = 0;
const char *func_name;
char *func;
int n;
@@ -10948,6 +10948,13 @@ static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf
pr_warn("kprobe name is invalid: %s\n", func_name);
return -EINVAL;
}
+
+ if (offset < 0) {
+ free(func);
+ pr_warn("kprobe offset must be a non-negative integer: %li\n", offset);
+ return -EINVAL;
+ }
+
if (opts.retprobe && offset != 0) {
free(func);
pr_warn("kretprobes do not support offset specification\n");
diff --git a/tools/lib/bpf/strset.c b/tools/lib/bpf/strset.c
index 2464bcbd04e0..ace73c6b3d62 100644
--- a/tools/lib/bpf/strset.c
+++ b/tools/lib/bpf/strset.c
@@ -107,6 +107,41 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz)
set->strs_data_len, set->strs_data_max_len, add_sz);
}
+static long strset_str_append(struct strset *set, const char *s)
+{
+ uintptr_t old_data = (uintptr_t)set->strs_data;
+ size_t old_data_len = set->strs_data_len;
+ uintptr_t old_s = (uintptr_t)s;
+ long len = strlen(s) + 1;
+ void *p;
+
+ /*
+ * Hashmap keys are always offsets within set->strs_data, so to even
+ * look up some string from the "outside", we need to first append it
+ * at the end, so that it can be addressed with an offset. Luckily,
+ * until set->strs_data_len is incremented, that string is just a piece
+ * of garbage for the rest of the code, so no harm, no foul. On the
+ * other hand, if the string is unique, it's already appended and
+ * ready to be used, only a simple set->strs_data_len increment away.
+ */
+ p = strset_add_str_mem(set, len);
+ if (!p)
+ return -ENOMEM;
+
+ /*
+ * The set->strs_data might have reallocated and if 's' pointed
+ * to an internal string within the old buffer, then it became
+ * dangling and needs to be reconstructed before the copy.
+ */
+ if (old_data && old_data != (uintptr_t)set->strs_data &&
+ old_s >= old_data && old_s < old_data + old_data_len)
+ s = set->strs_data + (old_s - old_data);
+
+ memcpy(p, s, len);
+
+ return len;
+}
+
/* Find string offset that corresponds to a given string *s*.
* Returns:
* - >0 offset into string data, if string is found;
@@ -116,16 +151,12 @@ static void *strset_add_str_mem(struct strset *set, size_t add_sz)
int strset__find_str(struct strset *set, const char *s)
{
long old_off, new_off, len;
- void *p;
- /* see strset__add_str() for why we do this */
- len = strlen(s) + 1;
- p = strset_add_str_mem(set, len);
- if (!p)
- return -ENOMEM;
+ len = strset_str_append(set, s);
+ if (len < 0)
+ return len;
new_off = set->strs_data_len;
- memcpy(p, s, len);
if (hashmap__find(set->strs_hash, new_off, &old_off))
return old_off;
@@ -142,24 +173,13 @@ int strset__find_str(struct strset *set, const char *s)
int strset__add_str(struct strset *set, const char *s)
{
long old_off, new_off, len;
- void *p;
int err;
- /* Hashmap keys are always offsets within set->strs_data, so to even
- * look up some string from the "outside", we need to first append it
- * at the end, so that it can be addressed with an offset. Luckily,
- * until set->strs_data_len is incremented, that string is just a piece
- * of garbage for the rest of the code, so no harm, no foul. On the
- * other hand, if the string is unique, it's already appended and
- * ready to be used, only a simple set->strs_data_len increment away.
- */
- len = strlen(s) + 1;
- p = strset_add_str_mem(set, len);
- if (!p)
- return -ENOMEM;
+ len = strset_str_append(set, s);
+ if (len < 0)
+ return len;
new_off = set->strs_data_len;
- memcpy(p, s, len);
/* Now attempt to add the string, but only if the string with the same
* contents doesn't exist already (HASHMAP_ADD strategy). If such
diff --git a/tools/lib/bpf/usdt.c b/tools/lib/bpf/usdt.c
index 6ff28e7bf5e3..f6689f12466f 100644
--- a/tools/lib/bpf/usdt.c
+++ b/tools/lib/bpf/usdt.c
@@ -449,10 +449,10 @@ static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, siz
static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt)
{
- char path[PATH_MAX], line[PATH_MAX], mode[16];
+ char path[PATH_MAX], line[4096], mode[16];
size_t seg_start, seg_end, seg_off;
struct elf_seg *seg;
- int tmp_pid, i, err;
+ int tmp_pid, n, i, err;
FILE *f;
*seg_cnt = 0;
@@ -461,8 +461,13 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs,
* /proc/<pid>/root/<path>. They will be reported as just /<path> in
* /proc/<pid>/maps.
*/
- if (sscanf(lib_path, "/proc/%d/root%s", &tmp_pid, path) == 2 && pid == tmp_pid)
+ /* %n is not counted in sscanf() return value, so initialize it. */
+ n = 0;
+ if (sscanf(lib_path, "/proc/%d/root%n", &tmp_pid, &n) == 1 &&
+ n > 0 && pid == tmp_pid && lib_path[n] == '/') {
+ libbpf_strlcpy(path, lib_path + n, sizeof(path));
goto proceed;
+ }
if (!realpath(lib_path, path)) {
pr_warn("usdt: failed to get absolute path of '%s' (err %d), using path as is...\n",
@@ -485,8 +490,11 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs,
* 7f5c6f5d1000-7f5c6f5d3000 rw-p 001c7000 08:04 21238613 /usr/lib64/libc-2.17.so
* 7f5c6f5d3000-7f5c6f5d8000 rw-p 00000000 00:00 0
* 7f5c6f5d8000-7f5c6f5d9000 r-xp 00000000 103:01 362990598 /data/users/andriin/linux/tools/bpf/usdt/libhello_usdt.so
+ *
+ * Some VMA names can be longer than the local buffer. Bound the
+ * writes, but still consume the rest of the line.
*/
- while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",
+ while (fscanf(f, "%zx-%zx %15s %zx %*s %*d%4095[^\n]%*[^\n]\n",
&seg_start, &seg_end, mode, &seg_off, line) == 5) {
void *tmp;
diff --git a/tools/lib/symbol/kallsyms.c b/tools/lib/symbol/kallsyms.c
index e335ac2b9e19..d64bd9cc82a9 100644
--- a/tools/lib/symbol/kallsyms.c
+++ b/tools/lib/symbol/kallsyms.c
@@ -60,7 +60,7 @@ int kallsyms__parse(const char *filename, void *arg,
read_to_eol(&io);
continue;
}
- for (i = 0; i < sizeof(symbol_name); i++) {
+ for (i = 0; i < KSYM_NAME_LEN; i++) {
ch = io__get_char(&io);
if (ch < 0 || ch == '\n')
break;
@@ -68,6 +68,9 @@ int kallsyms__parse(const char *filename, void *arg,
}
symbol_name[i] = '\0';
+ if (i == KSYM_NAME_LEN)
+ read_to_eol(&io);
+
err = process_symbol(arg, symbol_name, symbol_type, start);
if (err)
break;
diff --git a/tools/mm/slabinfo.c b/tools/mm/slabinfo.c
index 04e9e6ba86ea..b02fe299ca4b 100644
--- a/tools/mm/slabinfo.c
+++ b/tools/mm/slabinfo.c
@@ -33,7 +33,7 @@ struct slabinfo {
unsigned int hwcache_align, object_size, objs_per_slab;
unsigned int sanity_checks, slab_size, store_user, trace;
int order, poison, reclaim_account, red_zone;
- unsigned long partial, objects, slabs, objects_partial, objects_total;
+ unsigned long partial, objects, slabs, objects_partial, total_objects;
unsigned long alloc_fastpath, alloc_slowpath;
unsigned long free_fastpath, free_slowpath;
unsigned long free_frozen, free_add_partial, free_remove_partial;
@@ -795,7 +795,7 @@ static void slab_debug(struct slabinfo *s)
fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name);
}
if (!tracing && s->trace)
- set_obj(s, "trace", 1);
+ set_obj(s, "trace", 0);
}
static void totals(void)
@@ -1256,7 +1256,7 @@ static void read_slab_dir(void)
slab->object_size = get_obj("object_size");
slab->objects = get_obj("objects");
slab->objects_partial = get_obj("objects_partial");
- slab->objects_total = get_obj("objects_total");
+ slab->total_objects = get_obj("total_objects");
slab->objs_per_slab = get_obj("objs_per_slab");
slab->order = get_obj("order");
slab->partial = get_obj("partial");
diff --git a/tools/perf/trace/beauty/fcntl.c b/tools/perf/trace/beauty/fcntl.c
index 56ef83b3d130..bcaf71531d51 100644
--- a/tools/perf/trace/beauty/fcntl.c
+++ b/tools/perf/trace/beauty/fcntl.c
@@ -9,6 +9,22 @@
#include <linux/kernel.h>
#include <uapi/linux/fcntl.h>
+#ifndef F_GET_RW_HINT
+#define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11)
+#endif
+
+#ifndef F_SET_RW_HINT
+#define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12)
+#endif
+
+#ifndef F_GET_FILE_RW_HINT
+#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13)
+#endif
+
+#ifndef F_SET_FILE_RW_HINT
+#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14)
+#endif
+
static size_t fcntl__scnprintf_getfd(unsigned long val, char *bf, size_t size, bool show_prefix)
{
return val ? scnprintf(bf, size, "%s", "0") :
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c0c8d7f9514b..f9c396fe81dc 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -304,8 +304,6 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
return ret2;
}
-#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long))
-
static int convert_variable_type(Dwarf_Die *vr_die,
struct probe_trace_arg *tvar,
const char *cast, bool user_access)
@@ -335,7 +333,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
total = dwarf_bytesize(vr_die);
if (boffs < 0 || total < 0)
return -ENOENT;
- ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs,
+ ret = snprintf(buf, 16, "b%d@%d/%d", bsize, boffs,
BYTES_TO_BITS(total));
goto formatted;
}
diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c
index 12053fa43542..b7a77ebc98c5 100644
--- a/tools/power/x86/intel-speed-select/isst-daemon.c
+++ b/tools/power/x86/intel-speed-select/isst-daemon.c
@@ -147,6 +147,7 @@ static void daemonize(char *rundir, char *pidfile)
{
int pid, sid, i;
char str[10];
+ struct stat st;
struct sigaction sig_actions;
sigset_t sig_set;
int ret;
@@ -199,11 +200,17 @@ static void daemonize(char *rundir, char *pidfile)
if (ret == -1)
exit(EXIT_FAILURE);
- pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600);
+ pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
if (pid_file_handle == -1) {
/* Couldn't open lock file */
exit(1);
}
+
+ if (fstat(pid_file_handle, &st) == -1)
+ exit(1);
+
+ if (!S_ISREG(st.st_mode))
+ exit(1);
/* Try to lock file */
#ifdef LOCKF_SUPPORT
if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index ed1361c8b96c..0bb93bff74be 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -396,12 +396,16 @@ static void depopulate_all_mock_resources(void)
static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align)
{
- struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
struct genpool_data_align data = {
.align = align,
};
unsigned long phys;
+ struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res),
+ GFP_KERNEL);
+ if (!res)
+ return NULL;
+
INIT_LIST_HEAD(&res->list);
phys = gen_pool_alloc_algo(cxl_mock_pool, size,
gen_pool_first_fit_align, &data);
@@ -416,7 +420,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align)
list_add(&res->list, &mock_res);
mutex_unlock(&mock_res_lock);
- return res;
+ return no_free_ptr(res);
}
static int populate_cedt(void)
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 0ed100617d99..718f943ab357 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -940,7 +940,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata,
return -EINVAL;
offset = le32_to_cpu(get_lsa->offset);
length = le32_to_cpu(get_lsa->length);
- if (offset + length > LSA_SIZE)
+ if (offset > LSA_SIZE || length > LSA_SIZE - offset)
return -EINVAL;
if (length > cmd->size_out)
return -EINVAL;
@@ -960,7 +960,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata,
return -EINVAL;
offset = le32_to_cpu(set_lsa->offset);
length = cmd->size_in - sizeof(*set_lsa);
- if (offset + length > LSA_SIZE)
+ if (offset > LSA_SIZE || length > LSA_SIZE - offset)
return -EINVAL;
memcpy(lsa + offset, &set_lsa->data[0], length);
@@ -1396,7 +1396,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
return -ENOMEM;
dev_set_drvdata(dev, mdata);
- mdata->lsa = vmalloc(LSA_SIZE);
+ mdata->lsa = vzalloc(LSA_SIZE);
if (!mdata->lsa)
return -ENOMEM;
mdata->fw = vmalloc(FW_SIZE);
diff --git a/tools/testing/radix-tree/linux/idr.h b/tools/testing/radix-tree/linux/idr.h
index 4e342f2e37cf..676c5564e33f 100644
--- a/tools/testing/radix-tree/linux/idr.h
+++ b/tools/testing/radix-tree/linux/idr.h
@@ -1 +1,5 @@
+/* Avoid duplicate definitions due to system headers. */
+#ifdef __CONCAT
+#undef __CONCAT
+#endif
#include "../../../../include/linux/idr.h"
diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h
index 10587a29b967..1e329f29f696 100644
--- a/tools/testing/selftests/bpf/bpf_util.h
+++ b/tools/testing/selftests/bpf/bpf_util.h
@@ -7,6 +7,7 @@
#include <string.h>
#include <errno.h>
#include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
+#include <linux/args.h>
static inline unsigned int bpf_num_possible_cpus(void)
{
@@ -20,25 +21,43 @@ static inline unsigned int bpf_num_possible_cpus(void)
return possible_cpus;
}
-/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst
- * is zero-terminated string no matter what (unless sz == 0, in which case
- * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs
- * in what is returned. Given this is internal helper, it's trivial to extend
- * this, when necessary. Use this instead of strncpy inside libbpf source code.
+/*
+ * Simplified strscpy() implementation. The kernel one is in lib/string.c
*/
-static inline void bpf_strlcpy(char *dst, const char *src, size_t sz)
+static inline ssize_t sized_strscpy(char *dest, const char *src, size_t count)
{
- size_t i;
+ long res = 0;
- if (sz == 0)
- return;
+ if (count == 0)
+ return -E2BIG;
- sz--;
- for (i = 0; i < sz && src[i]; i++)
- dst[i] = src[i];
- dst[i] = '\0';
+ while (count > 1) {
+ char c;
+
+ c = src[res];
+ dest[res] = c;
+ if (!c)
+ return res;
+ res++;
+ count--;
+ }
+
+ /* Force NUL-termination. */
+ dest[res] = '\0';
+
+ /* Return E2BIG if the source didn't stop */
+ return src[res] ? -E2BIG : res;
}
+#define __strscpy0(dst, src, ...) \
+ sized_strscpy(dst, src, sizeof(dst))
+#define __strscpy1(dst, src, size) \
+ sized_strscpy(dst, src, size)
+
+#undef strscpy /* Redefine the placeholder from tools/include/linux/string.h */
+#define strscpy(dst, src, ...) \
+ CONCATENATE(__strscpy, COUNT_ARGS(__VA_ARGS__))(dst, src, __VA_ARGS__)
+
#define __bpf_percpu_val_align __attribute__((__aligned__(8)))
#define BPF_DECLARE_PERCPU(type, name) \
diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
index f68fbc6c3f52..aaae3452f7dd 100644
--- a/tools/testing/selftests/bpf/cgroup_helpers.c
+++ b/tools/testing/selftests/bpf/cgroup_helpers.c
@@ -78,7 +78,7 @@ static int __enable_controllers(const char *cgroup_path, const char *controllers
enable[len] = 0;
close(fd);
} else {
- bpf_strlcpy(enable, controllers, sizeof(enable));
+ strscpy(enable, controllers);
}
snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier_log.c b/tools/testing/selftests/bpf/prog_tests/verifier_log.c
index 8337c6bc5b95..abc473c3741f 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier_log.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c
@@ -317,6 +317,7 @@ static void verif_btf_log_subtest(bool bad_btf)
res = load_btf(&opts, true);
ASSERT_EQ(res, -ENOSPC, "half_log_fd");
ASSERT_EQ(strlen(logs.buf), 24, "log_fixed_25");
+ strscpy(op_name, "log_fixed", sizeof(op_name));
ASSERT_STRNEQ(logs.buf, logs.reference, 24, op_name);
/* validate rolling verifier log logic: try all variations of log buf
diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c b/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
index dd923dc637d5..97f7e8d5b6c9 100644
--- a/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
+++ b/tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
@@ -52,7 +52,7 @@ SEC("iter/task_vma") int proc_maps(struct bpf_iter__task_vma *ctx)
bpf_d_path(&file->f_path, d_path_buf, D_PATH_BUF_SIZE);
BPF_SEQ_PRINTF(seq, "%08llx ", vma->vm_pgoff << 12);
- BPF_SEQ_PRINTF(seq, "%02x:%02x %u", MAJOR(dev), MINOR(dev),
+ BPF_SEQ_PRINTF(seq, "%02x:%02x %llu", MAJOR(dev), MINOR(dev),
file->f_inode->i_ino);
BPF_SEQ_PRINTF(seq, "\t%s\n", d_path_buf);
} else {
diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 2e75fb30f3a5..a96489f398ea 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -24,7 +24,9 @@ CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
-CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(KHDR_INCLUDES) -I$(OUTPUT)
+CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES)
+CFLAGS += -I$(OUTPUT)/tools/include
+
LDLIBS += -lelf -lz -lrt -lpthread
# Silence some warnings when compiled with clang
@@ -68,7 +70,6 @@ BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
SCRATCH_DIR := $(OUTPUT)/tools
BUILD_DIR := $(SCRATCH_DIR)/build
INCLUDE_DIR := $(SCRATCH_DIR)/include
-KHDR_INCLUDES := $(SCRATCH_DIR)/uapi/include
BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a
ifneq ($(CROSS_COMPILE),)
HOST_BUILD_DIR := $(BUILD_DIR)/host
@@ -154,9 +155,6 @@ else
$(Q)cp "$(VMLINUX_H)" $@
endif
-$(KHDR_INCLUDES)/linux/hid.h: $(top_srcdir)/include/uapi/linux/hid.h
- $(MAKE) -C $(top_srcdir) INSTALL_HDR_PATH=$(SCRATCH_DIR)/uapi headers_install
-
$(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids \
$(TOOLSDIR)/bpf/resolve_btfids/main.c \
$(TOOLSDIR)/lib/rbtree.c \
@@ -234,7 +232,7 @@ $(BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(OUTPUT)
$(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $<
$(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked1.o) name $(notdir $(<:.bpf.o=)) > $@
-$(OUTPUT)/%.o: %.c $(BPF_SKELS) $(KHDR_INCLUDES)/linux/hid.h
+$(OUTPUT)/%.o: %.c $(BPF_SKELS)
$(call msg,CC,,$@)
$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 2cf96f818f25..17c37e91bf09 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -444,7 +444,7 @@ FIXTURE(hid_bpf) {
int hid_id;
pthread_t tid;
struct hid *skel;
- int hid_links[3]; /* max number of programs loaded in a single test */
+ struct bpf_link *hid_links[3]; /* max number of programs loaded in a single test */
};
static void detach_bpf(FIXTURE_DATA(hid_bpf) * self)
{
@@ -456,7 +456,7 @@ static void detach_bpf(FIXTURE_DATA(hid_bpf) * self)
for (i = 0; i < ARRAY_SIZE(self->hid_links); i++) {
if (self->hid_links[i])
- close(self->hid_links[i]);
+ bpf_link__destroy(self->hid_links[i]);
}
hid__destroy(self->skel);
@@ -511,14 +511,7 @@ static void load_programs(const struct test_program programs[],
FIXTURE_DATA(hid_bpf) * self,
const FIXTURE_VARIANT(hid_bpf) * variant)
{
- int attach_fd, err = -EINVAL;
- struct attach_prog_args args = {
- .retval = -1,
- };
- DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
- .ctx_in = &args,
- .ctx_size_in = sizeof(args),
- );
+ int err = -EINVAL;
ASSERT_LE(progs_count, ARRAY_SIZE(self->hid_links))
TH_LOG("too many programs are to be loaded");
@@ -529,35 +522,41 @@ static void load_programs(const struct test_program programs[],
for (int i = 0; i < progs_count; i++) {
struct bpf_program *prog;
+ struct bpf_map *map;
+ int *ops_hid_id;
prog = bpf_object__find_program_by_name(*self->skel->skeleton->obj,
programs[i].name);
ASSERT_OK_PTR(prog) TH_LOG("can not find program by name '%s'", programs[i].name);
bpf_program__set_autoload(prog, true);
+
+ map = bpf_object__find_map_by_name(*self->skel->skeleton->obj,
+ programs[i].name + 4);
+ ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
+ programs[i].name + 4);
+
+ /* hid_id is the first field of struct hid_bpf_ops */
+ ops_hid_id = bpf_map__initial_value(map, NULL);
+ ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
+
+ *ops_hid_id = self->hid_id;
}
err = hid__load(self->skel);
ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
- attach_fd = bpf_program__fd(self->skel->progs.attach_prog);
- ASSERT_GE(attach_fd, 0) TH_LOG("locate attach_prog: %d", attach_fd);
-
for (int i = 0; i < progs_count; i++) {
- struct bpf_program *prog;
+ struct bpf_map *map;
- prog = bpf_object__find_program_by_name(*self->skel->skeleton->obj,
- programs[i].name);
- ASSERT_OK_PTR(prog) TH_LOG("can not find program by name '%s'", programs[i].name);
-
- args.prog_fd = bpf_program__fd(prog);
- args.hid = self->hid_id;
- args.insert_head = programs[i].insert_head;
- err = bpf_prog_test_run_opts(attach_fd, &tattr);
- ASSERT_GE(args.retval, 0)
- TH_LOG("attach_hid(%s): %d", programs[i].name, args.retval);
+ map = bpf_object__find_map_by_name(*self->skel->skeleton->obj,
+ programs[i].name + 4);
+ ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
+ programs[i].name + 4);
- self->hid_links[i] = args.retval;
+ self->hid_links[i] = bpf_map__attach_struct_ops(map);
+ ASSERT_OK_PTR(self->hid_links[i]) TH_LOG("failed to attach struct ops '%s'",
+ programs[i].name + 4);
}
self->hidraw_fd = open_hidraw(self->dev_id);
@@ -632,13 +631,17 @@ TEST_F(hid_bpf, test_attach_detach)
{ .name = "hid_first_event" },
{ .name = "hid_second_event" },
};
+ struct bpf_link *link;
__u8 buf[10] = {0};
- int err, link;
+ int err, link_fd;
LOAD_PROGRAMS(progs);
link = self->hid_links[0];
- ASSERT_GT(link, 0) TH_LOG("HID-BPF link not created");
+ ASSERT_OK_PTR(link) TH_LOG("HID-BPF link not created");
+
+ link_fd = bpf_link__fd(link);
+ ASSERT_GE(link_fd, 0) TH_LOG("HID-BPF link FD not valid");
/* inject one event */
buf[0] = 1;
@@ -657,7 +660,7 @@ TEST_F(hid_bpf, test_attach_detach)
/* pin the first program and immediately unpin it */
#define PIN_PATH "/sys/fs/bpf/hid_first_event"
- err = bpf_obj_pin(link, PIN_PATH);
+ err = bpf_obj_pin(link_fd, PIN_PATH);
ASSERT_OK(err) TH_LOG("error while calling bpf_obj_pin");
remove(PIN_PATH);
#undef PIN_PATH
@@ -839,6 +842,17 @@ TEST_F(hid_bpf, test_rdesc_fixup)
ASSERT_EQ(rpt_desc.value[4], 0x42);
}
+TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_rdesc_fixup_get_data_overflow" },
+ };
+
+ LOAD_PROGRAMS(progs);
+
+ ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
+}
+
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index 88c593f753b5..0fa675c8508a 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -1,8 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Red hat */
-#include "vmlinux.h"
-#include <bpf/bpf_helpers.h>
-#include <bpf/bpf_tracing.h>
#include "hid_bpf_helpers.h"
char _license[] SEC("license") = "GPL";
@@ -16,9 +13,10 @@ struct attach_prog_args {
__u64 callback_check = 52;
__u64 callback2_check = 52;
+__u64 get_data_overflow_check;
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx)
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
@@ -32,8 +30,14 @@ int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx)
return hid_ctx->size;
}
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx)
+SEC(".struct_ops.link")
+struct hid_bpf_ops first_event = {
+ .hid_device_event = (void *)hid_first_event,
+ .hid_id = 2,
+};
+
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
@@ -45,8 +49,13 @@ int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx)
return hid_ctx->size;
}
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx)
+SEC(".struct_ops.link")
+struct hid_bpf_ops second_event = {
+ .hid_device_event = (void *)hid_second_event,
+};
+
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
@@ -58,15 +67,10 @@ int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx)
return 9;
}
-SEC("syscall")
-int attach_prog(struct attach_prog_args *ctx)
-{
- ctx->retval = hid_bpf_attach_prog(ctx->hid,
- ctx->prog_fd,
- ctx->insert_head ? HID_BPF_FLAG_INSERT_HEAD :
- HID_BPF_FLAG_NONE);
- return 0;
-}
+SEC(".struct_ops.link")
+struct hid_bpf_ops change_report_id = {
+ .hid_device_event = (void *)hid_change_report_id,
+};
struct hid_hw_request_syscall_args {
/* data needs to come at offset 0 so we can use it in calls */
@@ -138,7 +142,12 @@ static const __u8 rdesc[] = {
0xc0, /* END_COLLECTION */
};
-SEC("?fmod_ret/hid_bpf_rdesc_fixup")
+/*
+ * the following program is marked as sleepable (struct_ops.s).
+ * This is not strictly mandatory but is a nice test for
+ * sleepable struct_ops
+ */
+SEC("?struct_ops.s/hid_rdesc_fixup")
int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
{
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
@@ -157,8 +166,27 @@ int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
return sizeof(rdesc) + 73;
}
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx)
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup,
+};
+
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx)
+{
+ if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */))
+ get_data_overflow_check = 1;
+
+ return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
+};
+
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
@@ -174,8 +202,14 @@ int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx)
return 0;
}
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx)
+SEC(".struct_ops.link")
+struct hid_bpf_ops test_insert1 = {
+ .hid_device_event = (void *)hid_test_insert1,
+ .flags = BPF_F_BEFORE,
+};
+
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
@@ -191,8 +225,13 @@ int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx)
return 0;
}
-SEC("?fmod_ret/hid_bpf_device_event")
-int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
+SEC(".struct_ops.link")
+struct hid_bpf_ops test_insert2 = {
+ .hid_device_event = (void *)hid_test_insert2,
+};
+
+SEC("?struct_ops/hid_device_event")
+int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
@@ -207,3 +246,8 @@ int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
return 0;
}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops test_insert3 = {
+ .hid_device_event = (void *)hid_test_insert3,
+};
diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
index 4fff31dbe0e7..4a2e622ec03b 100644
--- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
+++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
@@ -5,11 +5,93 @@
#ifndef __HID_BPF_HELPERS_H
#define __HID_BPF_HELPERS_H
+/* "undefine" structs and enums in vmlinux.h, because we "override" them below */
+#define hid_bpf_ctx hid_bpf_ctx___not_used
+#define hid_bpf_ops hid_bpf_ops___not_used
+#define hid_report_type hid_report_type___not_used
+#define hid_class_request hid_class_request___not_used
+#define hid_bpf_attach_flags hid_bpf_attach_flags___not_used
+#define HID_INPUT_REPORT HID_INPUT_REPORT___not_used
+#define HID_OUTPUT_REPORT HID_OUTPUT_REPORT___not_used
+#define HID_FEATURE_REPORT HID_FEATURE_REPORT___not_used
+#define HID_REPORT_TYPES HID_REPORT_TYPES___not_used
+#define HID_REQ_GET_REPORT HID_REQ_GET_REPORT___not_used
+#define HID_REQ_GET_IDLE HID_REQ_GET_IDLE___not_used
+#define HID_REQ_GET_PROTOCOL HID_REQ_GET_PROTOCOL___not_used
+#define HID_REQ_SET_REPORT HID_REQ_SET_REPORT___not_used
+#define HID_REQ_SET_IDLE HID_REQ_SET_IDLE___not_used
+#define HID_REQ_SET_PROTOCOL HID_REQ_SET_PROTOCOL___not_used
+
+#include "vmlinux.h"
+
+#undef hid_bpf_ctx
+#undef hid_bpf_ops
+#undef hid_report_type
+#undef hid_class_request
+#undef hid_bpf_attach_flags
+#undef HID_INPUT_REPORT
+#undef HID_OUTPUT_REPORT
+#undef HID_FEATURE_REPORT
+#undef HID_REPORT_TYPES
+#undef HID_REQ_GET_REPORT
+#undef HID_REQ_GET_IDLE
+#undef HID_REQ_GET_PROTOCOL
+#undef HID_REQ_SET_REPORT
+#undef HID_REQ_SET_IDLE
+#undef HID_REQ_SET_PROTOCOL
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <linux/const.h>
+
+enum hid_report_type {
+ HID_INPUT_REPORT = 0,
+ HID_OUTPUT_REPORT = 1,
+ HID_FEATURE_REPORT = 2,
+
+ HID_REPORT_TYPES,
+};
+
+struct hid_bpf_ctx {
+ struct hid_device *hid;
+ __u32 allocated_size;
+ union {
+ __s32 retval;
+ __s32 size;
+ };
+} __attribute__((preserve_access_index));
+
+enum hid_class_request {
+ HID_REQ_GET_REPORT = 0x01,
+ HID_REQ_GET_IDLE = 0x02,
+ HID_REQ_GET_PROTOCOL = 0x03,
+ HID_REQ_SET_REPORT = 0x09,
+ HID_REQ_SET_IDLE = 0x0A,
+ HID_REQ_SET_PROTOCOL = 0x0B,
+};
+
+struct hid_bpf_ops {
+ int hid_id;
+ u32 flags;
+ struct list_head list;
+ int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type,
+ u64 source);
+ int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx);
+ int (*hid_hw_request)(struct hid_bpf_ctx *ctx, unsigned char reportnum,
+ enum hid_report_type rtype, enum hid_class_request reqtype,
+ u64 source);
+ int (*hid_hw_output_report)(struct hid_bpf_ctx *ctx, u64 source);
+ struct hid_device *hdev;
+};
+
+#ifndef BPF_F_BEFORE
+#define BPF_F_BEFORE (1U << 3)
+#endif
+
/* following are kfuncs exported by HID for HID-BPF */
extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
unsigned int offset,
const size_t __sz) __ksym;
-extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym;
extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym;
extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym;
extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx,
diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
index 38fec412206b..e74622e023ca 100644
--- a/tools/testing/selftests/mm/compaction_test.c
+++ b/tools/testing/selftests/mm/compaction_test.c
@@ -234,6 +234,9 @@ int main(int argc, char **argv)
mem_fragmentable_MB -= MAP_SIZE_MB;
}
+ /* Unmap every other entry in the list to create fragmentation with
+ * locked pages before invoking check_compaction().
+ */
for (entry = list; entry != NULL; entry = entry->next) {
munmap(entry->map, MAP_SIZE);
if (!entry->next)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 69b88e973cee..5286e400ce8b 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -1612,8 +1612,8 @@ TEST_F(hmm2, snapshot)
}
/*
- * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that
- * should be mapped by a large page table entry.
+ * Test the hmm_range_fault() handling of large pages (PMD or PUD)
+ * that should be mapped by a large page table entry.
*/
TEST_F(hmm, compound)
{
@@ -1623,6 +1623,7 @@ TEST_F(hmm, compound)
unsigned long default_hsize;
int *ptr;
unsigned char *m;
+ unsigned char prot;
int ret;
unsigned long i;
@@ -1661,11 +1662,20 @@ TEST_F(hmm, compound)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- /* Check what the device saw. */
+ /*
+ * Check what the device saw. The region is backed by a single huge
+ * page that the device reports either at PMD or at PUD level depending
+ * on the configured default hugepage size. Determine that level from
+ * the first page and require every page in the range to match it
+ * exactly, so that a fragmented mapping mixing levels (or a missing
+ * large-page bit) is still caught and reported with its actual value.
+ */
m = buffer->mirror;
+ prot = HMM_DMIRROR_PROT_WRITE |
+ ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD :
+ HMM_DMIRROR_PROT_PMD);
for (i = 0; i < npages; ++i)
- ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE |
- HMM_DMIRROR_PROT_PMD);
+ ASSERT_EQ(m[i], prot);
/* Make the region read-only. */
ret = mprotect(buffer->ptr, size, PROT_READ);
@@ -1676,11 +1686,17 @@ TEST_F(hmm, compound)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- /* Check what the device saw. */
+ /*
+ * Check what the device saw after mprotect(PROT_READ). Same
+ * approach as above: determine the mapping level from the first
+ * page and require every page to match it exactly.
+ */
m = buffer->mirror;
+ prot = HMM_DMIRROR_PROT_READ |
+ ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD :
+ HMM_DMIRROR_PROT_PMD);
for (i = 0; i < npages; ++i)
- ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ |
- HMM_DMIRROR_PROT_PMD);
+ ASSERT_EQ(m[i], prot);
munmap(buffer->ptr, buffer->size);
buffer->ptr = NULL;
@@ -1880,6 +1896,8 @@ TEST_F(hmm, exclusive_cow)
unsigned long i;
int *ptr;
int ret;
+ pid_t pid;
+ int status;
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
ASSERT_NE(npages, 0);
@@ -1908,14 +1926,37 @@ TEST_F(hmm, exclusive_cow)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- fork();
+ pid = fork();
+ if (pid == -1)
+ ASSERT_EQ(pid, 0);
+
+ if (pid == 0) {
+ /*
+ * Child verifies COW independently, then _exit(0)s so it does
+ * not run the test teardown. A failed ASSERT_* here makes the
+ * harness abort() the child, so the parent sees
+ * !WIFEXITED(status) below and fails in turn.
+ */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i]++, i);
- /* Fault pages back to system memory and check them. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i + 1);
+
+ _exit(0);
+ }
+
+ /* Parent: also increment to verify COW works for both processes. */
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
ASSERT_EQ(ptr[i]++, i);
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
- ASSERT_EQ(ptr[i], i+1);
+ ASSERT_EQ(ptr[i], i + 1);
+
+ /* Parent: wait for child and then free the buffer. */
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 0);
hmm_buffer_free(buffer);
}
diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index 4fcce5150850..2544ae35d07a 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -313,6 +313,8 @@ void *child_thread(void *arg)
tcp_info_get_rcv_mss(fd));
}
error:
+ if (ctx)
+ EVP_MD_CTX_free(ctx);
munmap(buffer, buffer_sz);
close(fd);
if (zflg)
@@ -606,6 +608,8 @@ int main(int argc, char *argv[])
EVP_DigestFinal_ex(ctx, digest, &digest_len);
send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0);
}
+ if (ctx)
+ EVP_MD_CTX_free(ctx);
close(fd);
munmap(buffer, buffer_sz);
return 0;
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
index b8d7d480595a..f4734f552d31 100644
--- a/tools/usb/usbip/libsrc/usbip_common.c
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = {
{ USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
{ USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
{ USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" },
{ 0, NULL, NULL }
};
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index 927a151fa9aa..f8879a580407 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -57,6 +57,10 @@ static struct {
.speed = USB_SPEED_SUPER,
.name = "super-speed",
},
+ {
+ .speed = USB_SPEED_SUPER_PLUS,
+ .name = "super-speed-plus",
+ },
};
static
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index 8159fd98680b..4ca3783ee5b7 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed)
switch (speed) {
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
continue;
break;
diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c
index 98ff808d6f0c..16f3723b4671 100644
--- a/tools/virtio/vringh_test.c
+++ b/tools/virtio/vringh_test.c
@@ -159,7 +159,12 @@ static int parallel_test(u64 features,
/* Parent and child use separate addresses, to check our mapping logic! */
host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (host_map == MAP_FAILED)
+ err(1, "mmap host_map");
+
guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (guest_map == MAP_FAILED)
+ err(1, "mmap guest_map");
pipe(to_guest);
pipe(to_host);
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index 9ca20fc59b52..1ff3da1930a9 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/seqlock.h>
#include <linux/irqbypass.h>
+#include <asm/unaligned.h>
#include <trace/events/kvm.h>
#include <kvm/iodev.h>
@@ -757,21 +758,18 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
return true;
/* otherwise, we have to actually compare the data */
-
- BUG_ON(!IS_ALIGNED((unsigned long)val, len));
-
switch (len) {
case 1:
- _val = *(u8 *)val;
+ _val = get_unaligned((u8 *)val);
break;
case 2:
- _val = *(u16 *)val;
+ _val = get_unaligned((u16 *)val);
break;
case 4:
- _val = *(u32 *)val;
+ _val = get_unaligned((u32 *)val);
break;
case 8:
- _val = *(u64 *)val;
+ _val = get_unaligned((u64 *)val);
break;
default:
return false;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index c2bcd9e6d1d5..b83f7612f250 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -5643,25 +5643,19 @@ struct kvm_io_device *kvm_io_bus_get_dev(struct kvm *kvm, enum kvm_bus bus_idx,
gpa_t addr)
{
struct kvm_io_bus *bus;
- int dev_idx, srcu_idx;
- struct kvm_io_device *iodev = NULL;
+ int dev_idx;
- srcu_idx = srcu_read_lock(&kvm->srcu);
+ lockdep_assert_held(&kvm->srcu);
bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
if (!bus)
- goto out_unlock;
+ return NULL;
dev_idx = kvm_io_bus_get_first_dev(bus, addr, 1);
if (dev_idx < 0)
- goto out_unlock;
-
- iodev = bus->range[dev_idx].dev;
-
-out_unlock:
- srcu_read_unlock(&kvm->srcu, srcu_idx);
+ return NULL;
- return iodev;
+ return bus->range[dev_idx].dev;
}
EXPORT_SYMBOL_GPL(kvm_io_bus_get_dev);
prev parent reply other threads:[~2026-07-24 15:06 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-24 15:05 Linux 6.6.145 Greg Kroah-Hartman
2026-07-24 15:05 ` Greg Kroah-Hartman [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2026072447-idiom-slighted-3071@gregkh \
--to=gregkh@linuxfoundation.org \
--cc=akpm@linux-foundation.org \
--cc=jslaby@suse.cz \
--cc=linux-kernel@vger.kernel.org \
--cc=lwn@lwn.net \
--cc=stable@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.