diff options
1451 files changed, 20212 insertions, 9584 deletions
@@ -19,7 +19,8 @@ Abhinav Kumar <quic_abhinavk@quicinc.com> <abhinavk@codeaurora.org> Ahmad Masri <quic_amasri@quicinc.com> <amasri@codeaurora.org> Adam Oldham <oldhamca@gmail.com> Adam Radford <aradford@gmail.com> -Aditya Garg <gargaditya08@proton.me> <gargaditya08@live.com> +Aditya Garg <aditya.garg@linux.dev> <gargaditya08@live.com> +Aditya Garg <aditya.garg@linux.dev> <gargaditya08@proton.me> Adriana Reus <adi.reus@gmail.com> <adriana.reus@intel.com> Adrian Bunk <bunk@stusta.de> Ajay Kaher <ajay.kaher@broadcom.com> <akaher@vmware.com> @@ -210,7 +211,8 @@ Christophe Leroy <chleroy@kernel.org> <christophe.leroy@c-s.fr> Christophe Leroy <chleroy@kernel.org> <christophe.leroy@csgroup.eu> Christophe Leroy <chleroy@kernel.org> <christophe.leroy2@cs-soprasteria.com> Christophe Ricard <christophe.ricard@gmail.com> -Christopher Obbard <christopher.obbard@linaro.org> <chris.obbard@collabora.com> +Christopher Obbard <chris.obbard@oss.qualcomm.com> <chris.obbard@collabora.com> +Christopher Obbard <chris.obbard@oss.qualcomm.com> <christopher.obbard@linaro.org> Christoph Hellwig <hch@lst.de> Christoph Manszewski <c.manszewski@gmail.com> <christoph.manszewski@intel.com> Christoph Paasch <cpaasch@openai.com> <christoph.paasch@gmail.com> @@ -221,6 +223,7 @@ Chuck Lever <cel@kernel.org> <chuck.lever@oracle.com> Chuck Lever <cel@kernel.org> <cel@netapp.com> Chuck Lever <cel@kernel.org> <cel@citi.umich.edu> Claudiu Beznea <claudiu.beznea@tuxon.dev> <claudiu.beznea@microchip.com> +Coiby Xu <coiby.xu@gmail.com> <coxu@redhat.com> Colin Ian King <colin.i.king@gmail.com> <colin.king@canonical.com> Corey Minyard <minyard@acm.org> Damian Hobson-Garcia <dhobsong@igel.co.jp> @@ -420,6 +423,7 @@ Jens Axboe <axboe@kernel.dk> <axboe@suse.de> Jens Axboe <axboe@kernel.dk> <jens.axboe@oracle.com> Jens Axboe <axboe@kernel.dk> <axboe@fb.com> Jens Axboe <axboe@kernel.dk> <axboe@meta.com> +Jens Axboe <axboe@kernel.dk> <axboe@anthropic.com> Jens Osterkamp <Jens.Osterkamp@de.ibm.com> Jens Wiklander <jenswi@kernel.org> <jens.wiklander@linaro.org> Jernej Skrabec <jernej.skrabec@gmail.com> <jernej.skrabec@siol.net> @@ -550,6 +554,9 @@ Li Yang <leoyang.li@nxp.com> <leo@zh-kernel.org> Lior David <quic_liord@quicinc.com> <liord@codeaurora.org> Loic Poulain <loic.poulain@oss.qualcomm.com> <loic.poulain@linaro.org> Loic Poulain <loic.poulain@oss.qualcomm.com> <loic.poulain@intel.com> +Lorenzo Bianconi <lorenzo@kernel.org> <lorenzo.bianconi@redhat.com> +Lorenzo Bianconi <lorenzo@kernel.org> <lorenzo.bianconi@st.com> +Lorenzo Bianconi <lorenzo@kernel.org> <lorenzo.bianconi83@gmail.com> Lorenzo Pieralisi <lpieralisi@kernel.org> <lorenzo.pieralisi@arm.com> Lorenzo Stoakes <ljs@kernel.org> <lstoakes@gmail.com> Lorenzo Stoakes <ljs@kernel.org> <lorenzo.stoakes@oracle.com> @@ -699,6 +706,7 @@ Oliver Hartkopp <socketcan@hartkopp.net> <oliver.hartkopp@volkswagen.de> Oliver Hartkopp <socketcan@hartkopp.net> <oliver@hartkopp.net> Oliver Upton <oupton@kernel.org> <oupton@google.com> Oliver Upton <oupton@kernel.org> <oliver.upton@linux.dev> +Ondrej Mosnáček <omosnacek@gmail.com> <omosnace@redhat.com> Ondřej Jirman <megi@xff.cz> <megous@megous.com> Oza Pawandeep <quic_poza@quicinc.com> <poza@codeaurora.org> Pali Rohár <pali@kernel.org> <pali.rohar@gmail.com> @@ -898,7 +906,8 @@ Thomas Graf <tgraf@suug.ch> Thomas Gleixner <tglx@kernel.org> <tglx@linutronix.de> Thomas Körper <socketcan@esd.eu> <thomas.koerper@esd.eu> Thomas Pedersen <twp@codeaurora.org> -Thorsten Blum <thorsten.blum@linux.dev> <thorsten.blum@toblux.com> +Thorsten Blum <blum@kernel.org> <thorsten.blum@toblux.com> +Thorsten Blum <blum@kernel.org> <thorsten.blum@linux.dev> Tiezhu Yang <yangtiezhu@loongson.cn> <kernelpatch@126.com> Tingwei Zhang <quic_tingwei@quicinc.com> <tingwei@codeaurora.org> Tirupathi Reddy <quic_tirupath@quicinc.com> <tirupath@codeaurora.org> @@ -1512,6 +1512,10 @@ N: Andy Gross E: agross@kernel.org D: Qualcomm SoC subsystem and drivers +N: Mark Gross +E: markgross@kernel.org +D: x86/mellanox platform maintenance and various x86 specific drivers + N: Grant Grundler E: grantgrundler@gmail.com W: http://obmouse.sourceforge.net/ @@ -4305,6 +4309,10 @@ N: Juergen Weigert E: jnweiger@immd4.informatik.uni-erlangen.de D: The Linux Support Team Erlangen +N: Russ Weight +E: russ.weight@gmail.com +D: Added support for Firmware Upload to the Firmware Loader + N: David Weinehall E: tao@acc.umu.se P: 1024D/DC47CA16 7ACE 0FB0 7A74 F994 9B36 E1D1 D14E 8526 DC47 CA16 diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 19258471b7b2..b8a763e4c9fb 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -108,6 +108,9 @@ Description: # NSFS_MAGIC dont_measure fsmagic=0x6e736673 dont_appraise fsmagic=0x6e736673 + # CONFIGFS_MAGIC + dont_measure fsmagic=0x62656570 + dont_appraise fsmagic=0x62656570 measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC diff --git a/Documentation/ABI/testing/sysfs-class-firmware b/Documentation/ABI/testing/sysfs-class-firmware index fba87a55f3ca..44ca1b78a0e1 100644 --- a/Documentation/ABI/testing/sysfs-class-firmware +++ b/Documentation/ABI/testing/sysfs-class-firmware @@ -1,7 +1,7 @@ What: /sys/class/firmware/.../data Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: The data sysfs file is used for firmware-fallback and for firmware uploads. Cat a firmware image to this sysfs file after you echo 1 to the loading sysfs file. When the firmware @@ -13,7 +13,7 @@ Description: The data sysfs file is used for firmware-fallback and for What: /sys/class/firmware/.../cancel Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: Write-only. For firmware uploads, write a "1" to this file to request that the transfer of firmware data to the lower-level device be canceled. This request will be rejected (EBUSY) if @@ -23,7 +23,7 @@ Description: Write-only. For firmware uploads, write a "1" to this file to What: /sys/class/firmware/.../error Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: Read-only. Returns a string describing a failed firmware upload. This string will be in the form of <STATUS>:<ERROR>, where <STATUS> will be one of the status strings described @@ -37,7 +37,7 @@ Description: Read-only. Returns a string describing a failed firmware What: /sys/class/firmware/.../loading Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: The loading sysfs file is used for both firmware-fallback and for firmware uploads. Echo 1 onto the loading file to indicate you are writing a firmware file to the data sysfs node. Echo @@ -49,7 +49,7 @@ Description: The loading sysfs file is used for both firmware-fallback and What: /sys/class/firmware/.../remaining_size Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: Read-only. For firmware upload, this file contains the size of the firmware data that remains to be transferred to the lower-level device driver. The size value is initialized to @@ -62,7 +62,7 @@ Description: Read-only. For firmware upload, this file contains the size What: /sys/class/firmware/.../status Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: Read-only. Returns a string describing the current status of a firmware upload. The string will be one of the following: idle, "receiving", "preparing", "transferring", "programming". @@ -70,7 +70,7 @@ Description: Read-only. Returns a string describing the current status of What: /sys/class/firmware/.../timeout Date: July 2022 KernelVersion: 5.19 -Contact: Russ Weight <russ.weight@linux.dev> +Contact: driver-core@lists.linux.dev Description: This file supports the timeout mechanism for firmware fallback. This file has no affect on firmware uploads. For more information on timeouts please see the documentation diff --git a/Documentation/ABI/testing/sysfs-fs-erofs b/Documentation/ABI/testing/sysfs-fs-erofs index e4cf6fc6a106..0b8b4354e40b 100644 --- a/Documentation/ABI/testing/sysfs-fs-erofs +++ b/Documentation/ABI/testing/sysfs-fs-erofs @@ -5,7 +5,7 @@ Description: Shows all enabled kernel features. Supported features: compr_cfgs, big_pcluster, chunked_file, device_table, compr_head2, sb_chksum, ztailpacking, dedupe, fragments, - 48bit, metabox. + xattr_prefixes, 48bit, metabox. What: /sys/fs/erofs/<disk>/sync_decompress Date: November 2021 diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst index 7db63c002922..7d2a44af52c9 100644 --- a/Documentation/admin-guide/cgroup-v1/memory.rst +++ b/Documentation/admin-guide/cgroup-v1/memory.rst @@ -47,7 +47,6 @@ Features: - pages are linked to per-memcg LRU exclusively, and there is no global LRU. - optionally, memory+swap usage can be accounted and limited. - hierarchical accounting - - soft limit - moving (recharging) account at moving a task is selectable. - usage threshold notifier - memory pressure notifier @@ -76,10 +75,9 @@ Brief summary of control files. memory.memsw.failcnt show the number of memory+Swap hits limits memory.max_usage_in_bytes show max memory usage recorded memory.memsw.max_usage_in_bytes show max memory+Swap usage recorded - memory.soft_limit_in_bytes set/show soft limit of memory usage - This knob is not available on CONFIG_PREEMPT_RT systems. - This knob is deprecated and shouldn't be - used. + memory.soft_limit_in_bytes This knob is deprecated and has no effect. + Writes are ignored and reads always + return the maximum value. memory.stat show various statistics memory.use_hierarchy set/show hierarchical account enabled This knob is deprecated and shouldn't be @@ -340,9 +338,6 @@ memory.kmem.usage_in_bytes, or in a separate counter when it makes sense. The main "kmem" counter is fed into the main counter, so kmem charges will also be visible from the user counter. -Currently no soft limit is implemented for kernel memory. It is future work -to trigger slab reclaim when those limits are reached. - 2.7.1 Current Kernel Memory resources accounted ----------------------------------------------- @@ -710,42 +705,10 @@ For compatibility reasons writing 1 to memory.use_hierarchy will always pass:: THIS IS DEPRECATED! -Soft limits allow for greater sharing of memory. The idea behind soft limits -is to allow control groups to use as much of the memory as needed, provided - -a. There is no memory contention -b. They do not exceed their hard limit - -When the system detects memory contention or low memory, control groups -are pushed back to their soft limits. If the soft limit of each control -group is very high, they are pushed back as much as possible to make -sure that one control group does not starve the others of memory. - -Please note that soft limits is a best-effort feature; it comes with -no guarantees, but it does its best to make sure that when memory is -heavily contended for, memory is allocated based on the soft limit -hints/setup. Currently soft limit based reclaim is set up such that -it gets invoked from balance_pgdat (kswapd). - -7.1 Interface -------------- - -Soft limits can be setup by using the following commands (in this example we -assume a soft limit of 256 MiB):: - - # echo 256M > memory.soft_limit_in_bytes - -If we want to change this to 1G, we can at any time use:: +Writing to memory.soft_limit_in_bytes has no effect and reading it will +always return the maximum value. - # echo 1G > memory.soft_limit_in_bytes - -.. note:: - Soft limits take effect over a long period of time, since they involve - reclaiming memory for balancing between memory cgroups - -.. note:: - It is recommended to set the soft limit always below the hard limit, - otherwise the hard limit will take precedence. +Use memory.low and memory.min in cgroup v2 instead. .. _cgroup-v1-memory-move-charges: diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 7c2a8ed80071..86a2a0099178 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle processes can be categorized as follows: * Processes under the fair-class scheduler -* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback +* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback * Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler - without the ``cgroup_set_weight`` callback + without the corresponding ``cgroup_set_*`` callback For details on when a process is under the fair-class scheduler or a BPF scheduler, check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`. @@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds. $PERIOD duration. "max" for $MAX indicates no limit. If only one number is written, $MAX is updated. - This file affects only processes under the fair-class scheduler. + This file affects only processes under the fair-class scheduler and a BPF + scheduler with the ``cgroup_set_bandwidth`` callback depending on what + the callback actually does. cpu.max.burst A read-write single value file which exists on non-root @@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds. The burst in the range [0, $MAX]. - This file affects only processes under the fair-class scheduler. + This file affects only processes under the fair-class scheduler and a BPF + scheduler with the ``cgroup_set_bandwidth`` callback depending on what + the callback actually does. cpu.pressure A read-write nested-keyed file. @@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds. own relative priorities, but the cgroup itself will be treated as very low priority relative to its peers. - This file affects only processes under the fair-class scheduler. + This file affects only processes under the fair-class scheduler and a BPF + scheduler with the ``cgroup_set_idle`` callback depending on what the + callback actually does. Memory ------ @@ -3064,7 +3070,7 @@ resources (res_a and res_b) are registered then: change in this file generates a file modified event. All fields in this file are hierarchical. - max + <res>.max The number of times the cgroup's resource usage was about to go over the max boundary. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 68647ff4bdd2..33cd30996e47 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4987,18 +4987,6 @@ Kernel parameters Set to non-zero if a chip is present that snoops speed changes. Disabled by default. - pata_legacy.ht6560a= [HW,LIBATA] - Format: <int> - Set to 1, 2, or 3 for HT 6560A on the primary channel, - the secondary channel, or both channels respectively. - Disabled by default. - - pata_legacy.ht6560b= [HW,LIBATA] - Format: <int> - Set to 1, 2, or 3 for HT 6560B on the primary channel, - the secondary channel, or both channels respectively. - Disabled by default. - pata_legacy.iordy_mask= [HW,LIBATA] Format: <int> IORDY enable mask. Set individual bits to allow IORDY @@ -5011,18 +4999,6 @@ Kernel parameters with the sequence. By default IORDY is allowed across all channels. - pata_legacy.opti82c46x= [HW,LIBATA] - Format: <int> - Set to 1, 2, or 3 for Opti 82c611A on the primary - channel, the secondary channel, or both channels - respectively. Disabled by default. - - pata_legacy.opti82c611a= [HW,LIBATA] - Format: <int> - Set to 1, 2, or 3 for Opti 82c465MV on the primary - channel, the secondary channel, or both channels - respectively. Disabled by default. - pata_legacy.pio_mask= [HW,LIBATA] Format: <int> PIO mode mask for autospeed devices. Set individual @@ -5046,19 +5022,6 @@ Kernel parameters the first port in the list above (0x1f0), and so on. By default all supported ports are probed. - pata_legacy.qdi= [HW,LIBATA] - Format: <int> - Set to non-zero to probe QDI controllers. By default - set to 1 if CONFIG_PATA_QDI_MODULE, 0 otherwise. - - pata_legacy.winbond= [HW,LIBATA] - Format: <int> - Set to non-zero to probe Winbond controllers. Use - the standard I/O port (0x130) if 1, otherwise the - value given is the I/O port to use (typically 0x1b0). - By default set to 1 if CONFIG_PATA_WINBOND_VLB_MODULE, - 0 otherwise. - pata_platform.pio_mask= [HW,LIBATA] Format: <int> Supported PIO mode mask. Set individual bits to allow diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index b6328cd0f43e..ffea61d448eb 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -1416,7 +1416,7 @@ Controls whether userspace may arm Syscall User Dispatch via == =================================================================== Only present when the kernel is built with ``CONFIG_SYSCALL_USER_DISPATCH`` -and ``CONFIG_PROC_SYSCTL``. +and ``CONFIG_SYSCTL``. sysctl_writes_strict diff --git a/Documentation/arch/riscv/hwprobe.rst b/Documentation/arch/riscv/hwprobe.rst index 893e1a1215d2..bb1e0cbab36f 100644 --- a/Documentation/arch/riscv/hwprobe.rst +++ b/Documentation/arch/riscv/hwprobe.rst @@ -155,7 +155,8 @@ The following keys are defined: defined in version 1.0 of the RISC-V Cryptography Extensions Volume II. * :c:macro:`RISCV_HWPROBE_EXT_ZFH`: The Zfh extension version 1.0 is supported - as defined in the RISC-V ISA manual. + as defined in the RISC-V ISA manual. Zfh is a superset of Zfhmin, so + RISCV_HWPROBE_EXT_ZFHMIN is reported whenever RISCV_HWPROBE_EXT_ZFH is. * :c:macro:`RISCV_HWPROBE_EXT_ZFHMIN`: The Zfhmin extension version 1.0 is supported as defined in the RISC-V ISA manual. @@ -164,8 +165,9 @@ The following keys are defined: is supported as defined in the RISC-V ISA manual. * :c:macro:`RISCV_HWPROBE_EXT_ZVFH`: The Zvfh extension is supported as - defined in the RISC-V Vector manual starting from commit e2ccd0548d6c - ("Remove draft warnings from Zvfh[min]"). + defined in the RISC-V Vector manual starting from commit e2ccd0548d6c + ("Remove draft warnings from Zvfh[min]"). Zvfh is a superset of Zvfhmin, + so RISCV_HWPROBE_EXT_ZVFHMIN is reported whenever RISCV_HWPROBE_EXT_ZVFH is. * :c:macro:`RISCV_HWPROBE_EXT_ZVFHMIN`: The Zvfhmin extension is supported as defined in the RISC-V Vector manual starting from commit e2ccd0548d6c diff --git a/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml b/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml index 9a18891f721e..b35b8f365b0f 100644 --- a/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml +++ b/Documentation/devicetree/bindings/regulator/fcs,fan53555.yaml @@ -23,6 +23,7 @@ properties: - silergy,syr827 - silergy,syr828 - tcs,tcs4525 + - tcs,tcs4526 - items: - const: rockchip,rk8601 - const: rockchip,rk8600 diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index 5feeb2203050..0da219ae6769 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -117,8 +117,8 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: VLEN/8, the vector register length in bytes. This property is required on - thead systems where the vector register length is not identical on all harts, or - the vlenb CSR is not available. + thead systems where the vector register length is not identical on all + harts, or the vlenb CSR is not available. # RISC-V has multiple properties for cache op block sizes as the sizes # differ between individual CBO extensions @@ -151,8 +151,8 @@ anyOf: - riscv,isa-base dependencies: - riscv,isa-base: [ "riscv,isa-extensions" ] - riscv,isa-extensions: [ "riscv,isa-base" ] + riscv,isa-base: ["riscv,isa-extensions"] + riscv,isa-extensions: ["riscv,isa-base"] required: - interrupt-controller diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 1e70c6804d5d..e8d6d5d858b6 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -44,6 +44,21 @@ allOf: else: properties: starfive,sfc-filter-syscon: false + - if: + properties: + compatible: + contains: + const: ultrarisc,dp1000-spi + then: + properties: + reg: + maxItems: 1 + clocks: + minItems: 2 + clock-names: + minItems: 2 + required: + - clock-names properties: compatible: @@ -63,6 +78,7 @@ properties: - mscc,jaguar2-spi - sophgo,sg2042-spi - thead,th1520-spi + - ultrarisc,dp1000-spi - const: snps,dw-apb-ssi - description: Vendor controllers compatible with v1.01a items: diff --git a/Documentation/fault-injection/nvme-fault-injection.rst b/Documentation/fault-injection/nvme-fault-injection.rst index 1d4427890d75..09730acf0163 100644 --- a/Documentation/fault-injection/nvme-fault-injection.rst +++ b/Documentation/fault-injection/nvme-fault-injection.rst @@ -176,3 +176,68 @@ Message from dmesg:: secondary_startup_64+0xa4/0xb0 nvme nvme0: Could not set queue count (16385) nvme nvme0: IO queues not created + +Example 4: Inject an error into the first write command +------------------------------------------------------- + +:: + + echo 0x01 > /sys/kernel/debug/nvme0n1/fault_inject/opcode + echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times + echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability + dd if=/dev/zero of=/dev/nvme0n1 oflag=direct bs=512 count=1 + +Expected Result:: + + The first write command sent to nvme0n1 fails + +Message from dmesg:: + + FAULT_INJECTION: forcing a failure. + name fault_inject, interval 1, probability 100, space 0, times 1 + CPU: 4 UID: 0 PID: 0 Comm: swapper/4 Not tainted 7.1.0+ #5 PREEMPT(full) + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-20240910_120124-localhost 04/01/2014 + Call Trace: + <IRQ> + dump_stack_lvl+0x6e/0xa0 + dump_stack+0x10/0x16 + should_fail_ex+0x461/0x510 + should_fail+0xb/0x20 + nvme_should_fail+0x11b/0x240 [nvme_core] + nvme_poll_cq+0x6ad/0xb30 [nvme] + nvme_irq+0x84/0xe0 [nvme] + ? __pfx_nvme_irq+0x10/0x10 [nvme] + ? rcu_core+0xa40/0xa90 + ? __pfx_sched_balance_softirq+0x10/0x10 + ? debug_smp_processor_id+0x17/0x20 + ? rcu_is_watching+0x13/0xa0 + __handle_irq_event_percpu+0x396/0x610 + handle_irq_event_percpu+0xf/0x90 + handle_irq_event+0xab/0x110 + handle_edge_irq+0x1a3/0x210 + __common_interrupt+0xff/0x170 + common_interrupt+0x90/0xc0 + </IRQ> + <TASK> + asm_common_interrupt+0x27/0x40 + RIP: 0010:pv_native_safe_halt+0x13/0x20 + Code: 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 8b 05 0a 2a 58 01 85 c0 7e 07 0f 00 2d ff cc 0d 00 fb f4 <c3> cc 0 + RSP: 0018:ffff888100a67e40 EFLAGS: 00000242 + RAX: 0000000000000001 RBX: ffff888100a49c40 RCX: ffffed102b6c645b + RDX: ffffed102b6c645b RSI: ffffffff82a0d3c0 RDI: ffffffff81428b9b + RBP: ffff888100a67e48 R08: ffffed102b6c645b R09: 0000000000000004 + R10: ffffed102b6c645a R11: 0000000000000001 R12: 0000000000000000 + R13: 0000000000000000 R14: ffffed1020149388 R15: dffffc0000000000 + ? do_idle+0x19b/0x2c0 + ? default_idle+0x9/0x20 + arch_cpu_idle+0x9/0x10 + default_idle_call+0x6b/0xa0 + do_idle+0x19b/0x2c0 + ? __pfx_do_idle+0x10/0x10 + ? complete_with_flags+0x63/0x70 + cpu_startup_entry+0x55/0x60 + start_secondary+0x1df/0x1e0 + common_startup_64+0x13e/0x158 + </TASK> + nvme0n1: Write(0x1) @ LBA 0, 1 blocks, Invalid Command Opcode (sct 0x0 / sc 0x1) DNR + operation not supported error, dev nvme0n1, sector 0 op 0x1:(WRITE) flags 0x8800 phys_seg 1 prio class 2 diff --git a/Documentation/hwmon/gpd-fan.rst b/Documentation/hwmon/gpd-fan.rst index 29527a77fe88..b27657d33056 100644 --- a/Documentation/hwmon/gpd-fan.rst +++ b/Documentation/hwmon/gpd-fan.rst @@ -67,7 +67,7 @@ pwm1_enable at full speed. Write "1" to set to manual, write "2" to let the EC control decide fan speed. Read this attribute to see current status. - NB:In consideration of the safety of the device, when setting to manual mode, + NB: In consideration of the safety of the device, when setting to manual mode, the pwm speed will be set to the maximum value (255) by default. You can set a different value by writing pwm1 later. diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst index 9fcde32a140d..c3eb433a78f6 100644 --- a/Documentation/hwmon/hwmon-kernel-api.rst +++ b/Documentation/hwmon/hwmon-kernel-api.rst @@ -42,6 +42,9 @@ register/unregister functions:: char *devm_hwmon_sanitize_name(struct device *dev, const char *name); + int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel); + void hwmon_lock(struct device *dev); void hwmon_unlock(struct device *dev); @@ -90,6 +93,18 @@ implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock() can be used to ensure that calls to those functions are serialized. Those functions also support guard() and scoped_guard() variants. +Drivers can call hwmon_notify_event() to notify userspace and the thermal +subsystem when a hardware monitoring event (such as an alarm or a fault +condition) occurs or clears. The parameters are the hwmon device, the sensor +type, the attribute identifier associated with the event (such as +hwmon_temp_max_alarm or hwmon_fan_fault), and the sensor channel number. +hwmon_notify_event() generates a sysfs event (calling sysfs_notify()) and a +udev event with the attribute name passed in the NAME environment property +(e.g., "NAME=temp1_max_alarm"). If the event is for a temperature sensor and +the sensor is attached to a thermal zone, it also notifies the thermal +subsystem to update the thermal zone. hwmon_notify_event() returns 0 on +success or a negative error code on failure. + Using devm_hwmon_device_register_with_info() -------------------------------------------- diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml index db7cddcda50a..b1eb102ab843 100644 --- a/Documentation/netlink/specs/conntrack.yaml +++ b/Documentation/netlink/specs/conntrack.yaml @@ -361,6 +361,17 @@ attribute-sets: type: u32 byte-order: big-endian - + name: filter-attrs + attributes: + - + name: orig-flags + type: u32 + doc: bitmask of tuple fields to filter on, original direction + - + name: reply-flags + type: u32 + doc: bitmask of tuple fields to filter on, reply direction + - name: conntrack-attrs attributes: - @@ -466,7 +477,7 @@ attribute-sets: - name: filter type: nest - nested-attributes: tuple-attrs + nested-attributes: filter-attrs - name: status-mask type: u32 @@ -591,10 +602,14 @@ operations: request: value: 0x101 attributes: - - mark - - filter + - tuple-orig + - tuple-reply - status + - mark - zone + - mark-mask + - filter + - status-mask reply: value: 0x100 attributes: diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml index b80c2ac3ac31..99f6fba456cc 100644 --- a/Documentation/netlink/specs/rt-link.yaml +++ b/Documentation/netlink/specs/rt-link.yaml @@ -898,6 +898,8 @@ attribute-sets: - name: txqlen type: u32 + checks: + max: 32767 - name: map type: binary diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst index 0e97fd019994..794ae80b3ba3 100644 --- a/Documentation/scheduler/sched-ext.rst +++ b/Documentation/scheduler/sched-ext.rst @@ -230,7 +230,7 @@ optional. The following modified excerpt is from void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei) { - exit_type = ei->type; + exit_type = ei->kind; } SEC(".struct_ops") @@ -242,6 +242,21 @@ optional. The following modified excerpt is from .name = "simple", }; +Scheduler-Dependent Knobs +------------------------- + +The fair-class scheduler enforces CPU controller settings such as +``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the +scheduler core communicates these settings to the BPF scheduler +through ``ops.cgroup_init()`` and reports subsequent changes through +the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task +nice changes are converted to weights and reported through +``ops.set_weight()``. + +Each BPF scheduler is responsible for implementing the scheduling +semantics of these settings and may choose to ignore them. Consult the +loaded scheduler's documentation before relying on these controls. + Dispatch Queues --------------- diff --git a/Documentation/translations/zh_CN/arch/riscv/patch-acceptance.rst b/Documentation/translations/zh_CN/arch/riscv/patch-acceptance.rst index c8eb230ca8ee..20b91d0433a9 100644 --- a/Documentation/translations/zh_CN/arch/riscv/patch-acceptance.rst +++ b/Documentation/translations/zh_CN/arch/riscv/patch-acceptance.rst @@ -15,19 +15,41 @@ arch/riscv 开发者维护指南 概述 ---- -RISC-V指令集体系结构是公开开发的: +RISC-V 指令集体系结构是公开开发的: 正在进行的草案可供所有人查看和测试实现。新模块或者扩展草案可能会在开发过程中发 -生更改---有时以不兼容的方式对以前的草案进行更改。这种灵活性可能会给RISC-V Linux -维护者带来挑战。Linux开发过程更喜欢经过良好检查和测试的代码,而不是试验代码。我 -们希望推广同样的规则到即将被内核合并的RISC-V相关代码。 +生更改 --- 有时以不兼容的方式对以前的草案进行更改。这种灵活性可能会给 RISC-V +Linux 维护者带来挑战。Linux 维护者不赞成频繁的变更,且 Linux 开发过程更喜欢经过 +良好检查和测试的代码,而不是试验代码。我们希望推广同样的规则到即将被内核合并的 +RISC-V 相关代码。 + +Patchwork +--------- + +RISC-V 有一个 patchwork 实例,可以在那里查看补丁的状态: + + https://patchwork.kernel.org/project/linux-riscv/list/ + +如果你的补丁不在默认视图中出现,那么 RISC-V 维护者很有可能已要求修改,或者希望 +将其应用到另一个代码树上。 + +自动化流程会在该 patchwork 实例上运行,在每个补丁到达时立刻对其进行构建/测试。 +自动化流程会根据补丁是否被识别为修复,选用 RISC-V `for-next` 或 `fixes` 分支 +当前的 HEAD;若上述均应用失败,则使用 RISC-V `master` 分支。补丁系列被应用到的具 +体提交将标注在 patchwork 上。任何检查未通过的补丁通常不会被应用,并且在大多数情 +况下将需要重新提交。 附加的提交检查单 ---------------- -我们仅接受相关标准已经被RISC-V基金会标准为“已批准”或“已冻结”的扩展或模块的补丁。 -(开发者当然可以维护自己的Linux内核树,其中包含所需代码扩展草案的代码。) - -此外,RISC-V规范允许爱好者创建自己的自定义扩展。这些自定义拓展不需要通过RISC-V -基金会的任何审核或批准。为了避免将爱好者一些特别的RISC-V拓展添加进内核代码带来 -的维护复杂性和对性能的潜在影响,我们将只接受RISC-V基金会正式冻结或批准的的扩展 -补丁。(开发者当然可以维护自己的Linux内核树,其中包含他们想要的任何自定义扩展 -的代码。) +我们仅接受针对新模块或扩展的补丁,前提是这些模块或扩展的规范被列为未来不太可能发 +生不兼容的变更。对于来自 RISC-V 基金会的规范,这意味着“已冻结”或“已批准”,对于 +UEFI 论坛的规范,这意味着已发布的 ECR。(开发者当然可以维护自己的 Linux 内核树, +其中包含他们所需的任何扩展草案的代码。) + +此外,RISC-V 规范允许实现者创建自己的自定义扩展。这些自定义扩展不需要通过 RISC-V +基金会的任何审核或批准流程。为了避免因添加实现者特定的 RISC-V 扩展带来的维护复杂 +性和对性能的潜在影响,我们将只考虑符合以下任一条件的扩展补丁: + +- 已由 RISC-V 基金会正式冻结或批准 +- 已按照标准 Linux 惯例,在广泛可用的硬件中实现 + +(实现者当然可以维护自己的 Linux 内核树,其中包含他们所需的任何自定义扩展的代码。) diff --git a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst index aaefeda0e164..7317cf5355c9 100644 --- a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst +++ b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst @@ -90,10 +90,6 @@ irq_domain映射的类型 映射的优点是固定时间查找IRQ号,而且irq_descs只分配给在用的IRQ。 缺点是该表 必须尽可能大的hwirq号。 -irq_domain_add_linear()和irq_domain_create_linear()在功能上是等价的, -除了第一个参数不同--前者接受一个Open Firmware特定的 'struct device_node' 而 -后者接受一个更通用的抽象 'struct fwnode_handle' 。 - 大多数驱动应该使用线性映射 树状映射 diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..3b2eb2a7a89a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4512,7 +4512,7 @@ AYANEO PLATFORM EC DRIVER M: Antheas Kapenekakis <lkml@antheas.dev> L: platform-driver-x86@vger.kernel.org S: Maintained -F: Documentation/ABI/testing/sysfs-platform-ayaneo +F: Documentation/ABI/testing/sysfs-platform-ayaneo-ec F: drivers/platform/x86/ayaneo-ec.c AZ6007 DVB DRIVER @@ -5671,8 +5671,8 @@ W: http://bu3sch.de/btgpio.php F: drivers/gpio/gpio-bt8xx.c BTRFS FILE SYSTEM -M: Chris Mason <clm@fb.com> M: David Sterba <dsterba@suse.com> +R: Chris Mason <mason@kernel.org> L: linux-btrfs@vger.kernel.org S: Maintained W: https://btrfs.readthedocs.io @@ -8039,7 +8039,7 @@ F: drivers/gpu/drm/sun4i/sun8i* DRM DRIVER FOR APPLE TOUCH BARS M: Aun-Ali Zaidi <admin@kodeit.net> -M: Aditya Garg <gargaditya08@proton.me> +M: Aditya Garg <aditya.garg@linux.dev> L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git @@ -9387,9 +9387,8 @@ S: Maintained F: drivers/edac/octeon_edac* EDAC-CAVIUM THUNDERX -M: Robert Richter <rric@kernel.org> L: linux-edac@vger.kernel.org -S: Odd Fixes +S: Orphan F: drivers/edac/thunderx_edac* EDAC-CORE @@ -9416,9 +9415,8 @@ S: Supported F: drivers/edac/dmc520_edac.c EDAC-E752X -M: Mark Gross <markgross@kernel.org> L: linux-edac@vger.kernel.org -S: Maintained +S: Orphan F: drivers/edac/e752x_edac.c EDAC-E7XXX @@ -9493,9 +9491,8 @@ S: Maintained F: drivers/edac/igen6_edac.c EDAC-MPC85XX -M: Johannes Thumshirn <morbidrsa@gmail.com> L: linux-edac@vger.kernel.org -S: Maintained +S: Orphan F: drivers/edac/mpc85xx_edac.[ch] EDAC-NPCM @@ -10193,7 +10190,6 @@ F: include/linux/arm_ffa.h FIRMWARE LOADER (request_firmware) M: Luis Chamberlain <mcgrof@kernel.org> -M: Russ Weight <russ.weight@linux.dev> M: Danilo Krummrich <dakr@kernel.org> L: driver-core@lists.linux.dev S: Maintained @@ -12224,7 +12220,7 @@ F: drivers/tty/hvc/ HUNG TASK DETECTOR M: Andrew Morton <akpm@linux-foundation.org> -R: Lance Yang <lance.yang@linux.dev> +M: Lance Yang <lance.yang@linux.dev> R: Masami Hiramatsu <mhiramat@kernel.org> R: Petr Mladek <pmladek@suse.com> L: linux-kernel@vger.kernel.org @@ -13283,7 +13279,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h INTEL KEEM BAY OCS ECC CRYPTO DRIVER M: Prabhjot Khurana <prabhjot.khurana@intel.com> -M: Mark Gross <mgross@linux.intel.com> S: Maintained F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml F: drivers/crypto/intel/keembay/Kconfig @@ -14240,8 +14235,6 @@ F: tools/testing/selftests/ KERNEL SMB3 SERVER (KSMBD) M: Namjae Jeon <linkinjeon@kernel.org> M: Namjae Jeon <linkinjeon@samba.org> -M: Steve French <smfrench@gmail.com> -M: Steve French <sfrench@samba.org> R: Sergey Senozhatsky <senozhatsky@chromium.org> R: Tom Talpey <tom@talpey.com> R: ChenXiaoSong <chenxiaosong@chenxiaosong.com> @@ -15027,6 +15020,8 @@ M: Madhavan Srinivasan <maddy@linux.ibm.com> R: Michael Ellerman <mpe@ellerman.id.au> R: Nicholas Piggin <npiggin@gmail.com> R: Christophe Leroy (CS GROUP) <chleroy@kernel.org> +R: Ritesh Harjani (IBM) <ritesh.list@gmail.com> +R: Shrikanth Hegde <sshegde@linux.ibm.com> L: linuxppc-dev@lists.ozlabs.org S: Supported W: https://github.com/linuxppc/wiki/wiki @@ -17036,8 +17031,8 @@ MEMBLOCK AND MEMORY MANAGEMENT INITIALIZATION M: Mike Rapoport <rppt@kernel.org> L: linux-mm@kvack.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git for-next -T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git fixes +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/memblock.git for-next +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/memblock.git fixes F: Documentation/core-api/boot-time-mm.rst F: include/linux/kho/abi/memblock.h F: include/linux/memblock.h @@ -17416,7 +17411,6 @@ F: mm/swapfile.c MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) M: Andrew Morton <akpm@linux-foundation.org> M: David Hildenbrand <david@kernel.org> -M: Lorenzo Stoakes <ljs@kernel.org> R: Zi Yan <ziy@nvidia.com> R: Baolin Wang <baolin.wang@linux.alibaba.com> R: Liam R. Howlett <liam@infradead.org> @@ -17426,6 +17420,7 @@ R: Dev Jain <dev.jain@arm.com> R: Barry Song <baohua@kernel.org> R: Lance Yang <lance.yang@linux.dev> R: Usama Arif <usama.arif@linux.dev> +R: Kiryl Shutsemau <kas@kernel.org> L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org @@ -17698,7 +17693,7 @@ F: Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml F: drivers/spi/spi-at91-usart.c MICROCHIP ATSHA204A DRIVER -M: Thorsten Blum <thorsten.blum@linux.dev> +M: Thorsten Blum <blum@kernel.org> L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/atmel-sha204a.c @@ -17722,7 +17717,7 @@ F: Documentation/devicetree/bindings/media/microchip,csi2dc.yaml F: drivers/media/platform/microchip/microchip-csi2dc.c MICROCHIP ECC DRIVER -M: Thorsten Blum <thorsten.blum@linux.dev> +M: Thorsten Blum <blum@kernel.org> L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/atmel-ecc.c @@ -19569,7 +19564,7 @@ F: include/linux/platform_data/x86/nvidia-wmi-ec-backlight.h NVM EXPRESS DRIVER M: Keith Busch <kbusch@kernel.org> -M: Jens Axboe <axboe@fb.com> +M: Jens Axboe <axboe@kernel.dk> M: Christoph Hellwig <hch@lst.de> M: Sagi Grimberg <sagi@grimberg.me> L: linux-nvme@lists.infradead.org @@ -24778,7 +24773,7 @@ K: \bsecurity_[a-z_0-9]\+\b SELINUX SECURITY MODULE M: Paul Moore <paul@paul-moore.com> M: Stephen Smalley <stephen.smalley.work@gmail.com> -R: Ondrej Mosnacek <omosnace@redhat.com> +R: Ondrej Mosnáček <omosnacek@gmail.com> L: selinux@vger.kernel.org S: Supported W: https://github.com/SELinuxProject @@ -25236,10 +25231,9 @@ F: Documentation/admin-guide/LSM/Smack.rst F: security/smack/ SMBDIRECT (RDMA Stream Transport with Read/Write-Offload, MS-SMBD) -M: Steve French <smfrench@gmail.com> -M: Steve French <sfrench@samba.org> M: Namjae Jeon <linkinjeon@kernel.org> M: Namjae Jeon <linkinjeon@samba.org> +M: Paulo Alcantara <pc@manguebit.org> R: Stefan Metzmacher <metze@samba.org> R: Tom Talpey <tom@talpey.com> L: linux-cifs@vger.kernel.org @@ -25342,8 +25336,8 @@ F: Documentation/networking/timestamping.rst F: include/linux/net_tstamp.h F: include/uapi/linux/net_tstamp.h F: tools/testing/selftests/bpf/*/net_timestamping* +F: tools/testing/selftests/drivers/net/so_txtime.* F: tools/testing/selftests/net/*timestamp* -F: tools/testing/selftests/net/so_txtime.c SOEKRIS NET48XX LED SUPPORT M: Chris Boot <bootc@bootc.net> @@ -25410,7 +25404,7 @@ F: drivers/md/md* F: drivers/md/raid* F: include/linux/raid/ F: include/uapi/linux/raid/ -F: lib/raid/raid6/ +F: lib/raid/ SOLIDRUN CLEARFOG SUPPORT M: Russell King <linux@armlinux.org.uk> @@ -26954,8 +26948,7 @@ S: Maintained F: drivers/net/ethernet/tehuti/tn40* TELECOM CLOCK DRIVER FOR MCPL0010 -M: Mark Gross <markgross@kernel.org> -S: Supported +S: Orphan F: drivers/char/tlclk.c TEMPO SEMICONDUCTOR DRIVERS @@ -28616,7 +28609,6 @@ L: iommu@lists.linux.dev S: Maintained F: Documentation/devicetree/bindings/iommu/verisilicon,iommu.yaml F: drivers/iommu/vsi-iommu.c -F: include/linux/vsi-iommu.h VF610 NAND DRIVER M: Stefan Agner <stefan@agner.ch> @@ -28876,8 +28868,8 @@ F: include/uapi/linux/virtio_console.h VIRTIO CORE M: "Michael S. Tsirkin" <mst@redhat.com> M: Jason Wang <jasowangio@gmail.com> +M: Eugenio Pérez <eperezma@redhat.com> R: Xuan Zhuo <xuanzhuo@linux.alibaba.com> -R: Eugenio Pérez <eperezma@redhat.com> L: virtualization@lists.linux.dev S: Maintained F: Documentation/ABI/testing/sysfs-bus-vdpa @@ -28954,7 +28946,7 @@ F: include/uapi/linux/virtio_gpu.h VIRTIO HOST (VHOST) M: "Michael S. Tsirkin" <mst@redhat.com> M: Jason Wang <jasowangio@gmail.com> -R: Eugenio Pérez <eperezma@redhat.com> +M: Eugenio Pérez <eperezma@redhat.com> L: kvm@vger.kernel.org L: virtualization@lists.linux.dev L: netdev@vger.kernel.org @@ -29009,8 +29001,8 @@ F: include/uapi/linux/virtio_mem.h VIRTIO NET DRIVER M: "Michael S. Tsirkin" <mst@redhat.com> M: Jason Wang <jasowangio@gmail.com> +M: Eugenio Pérez <eperezma@redhat.com> R: Xuan Zhuo <xuanzhuo@linux.alibaba.com> -R: Eugenio Pérez <eperezma@redhat.com> L: netdev@vger.kernel.org L: virtualization@lists.linux.dev S: Maintained @@ -29026,6 +29018,13 @@ S: Maintained F: drivers/nvdimm/nd_virtio.c F: drivers/nvdimm/virtio_pmem.c +VIRTIO RNG DRIVER +M: Laurent Vivier <lvivier@redhat.com> +L: virtualization@lists.linux.dev +S: Maintained +F: drivers/char/hw_random/virtio-rng.c +F: include/uapi/linux/virtio_rng.h + VIRTIO RTC DRIVER M: Peter Hilber <peter.hilber@oss.qualcomm.com> L: virtualization@lists.linux.dev @@ -29357,7 +29356,7 @@ M: Guenter Roeck <linux@roeck-us.net> L: linux-watchdog@vger.kernel.org S: Maintained W: http://www.linux-watchdog.org/ -T: git git://www.linux-watchdog.org/linux-watchdog.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git F: Documentation/devicetree/bindings/watchdog/ F: Documentation/watchdog/ F: drivers/watchdog/ @@ -29937,12 +29936,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h XILINX VERSAL EDAC DRIVER M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com> +R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com> S: Maintained F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml F: drivers/edac/versal_edac.c XILINX VERSALNET EDAC DRIVER M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> +R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com> S: Maintained F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml F: drivers/edac/versalnet_edac.c @@ -29988,6 +29989,7 @@ F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h XILINX ZYNQMP OCM EDAC DRIVER M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com> +R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com> S: Maintained F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml F: drivers/edac/zynqmp_edac.c @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 3 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc3 NAME = Baby Opossum Posse # *DOCUMENTATION* diff --git a/arch/arm64/crypto/aes-neonbs-glue.c b/arch/arm64/crypto/aes-neonbs-glue.c index 5bcbac979893..7cb1aede9a48 100644 --- a/arch/arm64/crypto/aes-neonbs-glue.c +++ b/arch/arm64/crypto/aes-neonbs-glue.c @@ -66,7 +66,7 @@ static int aesbs_setkey(struct crypto_skcipher *tfm, const u8 *in_key, struct crypto_aes_ctx *rk; int err; - rk = kmalloc(sizeof(*rk), GFP_KERNEL); + rk = kmalloc_obj(*rk); if (!rk) return -ENOMEM; @@ -128,7 +128,7 @@ static int aesbs_cbc_ctr_setkey(struct crypto_skcipher *tfm, const u8 *in_key, struct crypto_aes_ctx *rk; int err; - rk = kmalloc(sizeof(*rk), GFP_KERNEL); + rk = kmalloc_obj(*rk); if (!rk) return -ENOMEM; diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 71493b760b83..3c008821219c 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -78,5 +78,6 @@ void __init cpuinfo_store_boot_cpu(void); void __init init_cpu_features(struct cpuinfo_arm64 *info); void update_cpu_features(int cpu, struct cpuinfo_arm64 *info, struct cpuinfo_arm64 *boot); +bool gmid_el1_accessible(const struct cpuinfo_arm64 *info); #endif /* __ASM_CPU_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 7404a6e83a93..4f04ad82ea34 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -627,13 +627,6 @@ static inline bool id_aa64pfr1_mpamfrac(u64 pfr1) return val > 0; } -static inline bool id_aa64pfr1_mte(u64 pfr1) -{ - u32 val = cpuid_feature_extract_unsigned_field(pfr1, ID_AA64PFR1_EL1_MTE_SHIFT); - - return val >= ID_AA64PFR1_EL1_MTE_MTE2; -} - void __init setup_boot_cpu_features(void); void __init setup_system_features(void); void __init setup_user_features(void); diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 5db8f0619e4b..b33dccfafaf8 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -82,7 +82,7 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry, int scope) for (i = 0; i < target_impl_cpu_num; i++) { if (__is_affected_midr_range(entry, target_impl_cpus[i].midr, - target_impl_cpus[i].midr)) + target_impl_cpus[i].revidr)) return true; } return false; diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 17b83a2518a8..32102c3912fa 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1178,6 +1178,33 @@ static bool detect_ftr_has_mpam(void) return id_aa64pfr0_mpam(pfr0) || id_aa64pfr1_mpamfrac(pfr1); } +bool gmid_el1_accessible(const struct cpuinfo_arm64 *info) +{ + const struct arm64_ftr_bits *ftrp; + s64 mte, ovr; + u64 ftr_mask; + + /* No ID register reflects CONFIG_ARM64_MTE. */ + if (!IS_ENABLED(CONFIG_ARM64_MTE)) + return false; + + for (ftrp = ftr_id_aa64pfr1; ftrp->width; ftrp++) { + if (ftrp->shift == ID_AA64PFR1_EL1_MTE_SHIFT) + break; + } + + ftr_mask = arm64_ftr_mask(ftrp); + mte = arm64_ftr_value(ftrp, info->reg_id_aa64pfr1); + + /* The boot CPU runs before init_cpu_ftr_reg() strips unsafe overrides. */ + if ((id_aa64pfr1_override.mask & ftr_mask) == ftr_mask) { + ovr = arm64_ftr_value(ftrp, id_aa64pfr1_override.val); + mte = arm64_ftr_safe_value(ftrp, ovr, mte); + } + + return mte >= ID_AA64PFR1_EL1_MTE_MTE2; +} + void __init init_cpu_features(struct cpuinfo_arm64 *info) { /* Before we start using the tables, make sure it is sorted */ @@ -1230,7 +1257,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info) init_cpu_ftr_reg(SYS_MPAMIDR_EL1, info->reg_mpamidr); } - if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) + if (gmid_el1_accessible(info)) init_cpu_ftr_reg(SYS_GMID_EL1, info->reg_gmid); } @@ -1492,11 +1519,9 @@ void update_cpu_features(int cpu, * they read/write depends on the GMID_EL1.BS field. Check that the * value is the same on all CPUs. */ - if (IS_ENABLED(CONFIG_ARM64_MTE) && - id_aa64pfr1_mte(info->reg_id_aa64pfr1)) { + if (gmid_el1_accessible(info)) taint |= check_update_ftr_reg(SYS_GMID_EL1, cpu, info->reg_gmid, boot->reg_gmid); - } /* * If we don't have AArch32 at all then skip the checks entirely diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index d50e2a9b066b..45c63f3d75c5 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -502,7 +502,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64smfr0 = read_cpuid(ID_AA64SMFR0_EL1); info->reg_id_aa64fpfr0 = read_cpuid(ID_AA64FPFR0_EL1); - if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) + if (gmid_el1_accessible(info)) info->reg_gmid = read_cpuid(GMID_EL1); if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c index c5693a32e49b..8f9bc2327dc8 100644 --- a/arch/arm64/kernel/machine_kexec.c +++ b/arch/arm64/kernel/machine_kexec.c @@ -129,7 +129,8 @@ int machine_kexec_post_load(struct kimage *kimage) } /* Create a copy of the linear map */ - rc = trans_pgd_create_copy(&info, &trans_pgd, PAGE_OFFSET, PAGE_END); + rc = trans_pgd_create_copy(&info, &trans_pgd, + _PAGE_OFFSET(vabits_actual), PAGE_END); if (rc) return rc; kimage->arch.ttbr1 = __pa(trans_pgd); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 17123f0b6dab..3c4fc566eafc 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -93,7 +93,7 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU; if (num_mmus > kvm->arch.nested_mmus_size) { - tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT); + tmp = kvzalloc_objs(*tmp, num_mmus, GFP_KERNEL_ACCOUNT); if (!tmp) return -ENOMEM; diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 0b52557652be..75c3e463df2e 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -16,6 +16,7 @@ #include <linux/mm.h> #include <linux/hardirq.h> #include <linux/init.h> +#include <linux/irqflags.h> #include <linux/kasan.h> #include <linux/kprobes.h> #include <linux/uaccess.h> @@ -154,6 +155,9 @@ static void show_pte(unsigned long addr) pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n", mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K, vabits_actual, mm_to_pgd_phys(mm)); + + guard(irqsave)(); + pgdp = pgd_offset(mm, addr); pgd = READ_ONCE(*pgdp); pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd)); @@ -167,25 +171,25 @@ static void show_pte(unsigned long addr) if (pgd_none(pgd) || pgd_bad(pgd)) break; - p4dp = p4d_offset(pgdp, addr); + p4dp = p4d_offset_lockless(pgdp, pgd, addr); p4d = READ_ONCE(*p4dp); pr_cont(", p4d=%016llx", p4d_val(p4d)); if (p4d_none(p4d) || p4d_bad(p4d)) break; - pudp = pud_offset(p4dp, addr); + pudp = pud_offset_lockless(p4dp, p4d, addr); pud = READ_ONCE(*pudp); pr_cont(", pud=%016llx", pud_val(pud)); if (pud_none(pud) || pud_bad(pud)) break; - pmdp = pmd_offset(pudp, addr); + pmdp = pmd_offset_lockless(pudp, pud, addr); pmd = READ_ONCE(*pmdp); pr_cont(", pmd=%016llx", pmd_val(pmd)); if (pmd_none(pmd) || pmd_bad(pmd)) break; - ptep = pte_offset_map(pmdp, addr); + ptep = pte_offset_map(&pmd, addr); if (!ptep) break; diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 94bf065c8ac7..e2d37ee221b8 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -3761,7 +3761,9 @@ Sysreg ICC_CR0_EL1 3 1 12 0 1 Res0 63:39 Field 38 PID Field 37:32 IPPT -Res0 31:1 +Res0 31:3 +Field 2 LINK_IDLE +Field 1 LINK Field 0 EN EndSysreg diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index a21f51e5815e..2067d1f2ad7a 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -175,7 +175,7 @@ config LOONGARCH select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC select HAVE_RETHOOK select HAVE_RSEQ - select HAVE_RUST + select HAVE_RUST if !KASAN select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_SETUP_PER_CPU_AREA if NUMA diff --git a/arch/loongarch/include/asm/kvm_dmsintc.h b/arch/loongarch/include/asm/kvm_dmsintc.h index 5a71b9ccbe78..7c0158764d84 100644 --- a/arch/loongarch/include/asm/kvm_dmsintc.h +++ b/arch/loongarch/include/asm/kvm_dmsintc.h @@ -20,6 +20,7 @@ struct dmsintc_state { }; int kvm_loongarch_register_dmsintc_device(void); +void kvm_loongarch_unregister_dmsintc_device(void); void dmsintc_inject_irq(struct kvm_vcpu *vcpu); int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level); int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 vector, int level); diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h index 8b7a2fa3f7f8..9633fbfc066d 100644 --- a/arch/loongarch/include/asm/kvm_eiointc.h +++ b/arch/loongarch/include/asm/kvm_eiointc.h @@ -79,6 +79,7 @@ struct loongarch_eiointc { }; int kvm_loongarch_register_eiointc_device(void); +void kvm_loongarch_unregister_eiointc_device(void); void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level); #endif /* __ASM_KVM_EIOINTC_H */ diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 23cfbecebbd7..65d91c3ce313 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -125,6 +125,7 @@ struct kvm_arch { unsigned int pte_shifts[MAX_PGTABLE_LEVELS]; unsigned int root_level; spinlock_t phyid_map_lock; + spinlock_t pv_setting_lock; struct kvm_phyid_map *phyid_map; /* Enabled PV features */ unsigned long pv_features; @@ -350,7 +351,6 @@ static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {} static inline void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) {} void kvm_check_vpid(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer); -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, const struct kvm_memory_slot *memslot); void kvm_init_vmcs(struct kvm *kvm); void kvm_exc_entry(void); int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu); diff --git a/arch/loongarch/include/asm/kvm_ipi.h b/arch/loongarch/include/asm/kvm_ipi.h index 060163dfb4a3..d1d72d4bb8d1 100644 --- a/arch/loongarch/include/asm/kvm_ipi.h +++ b/arch/loongarch/include/asm/kvm_ipi.h @@ -41,5 +41,6 @@ struct ipi_state { #define IOCSR_ANY_SEND 0x158 int kvm_loongarch_register_ipi_device(void); +void kvm_loongarch_unregister_ipi_device(void); #endif diff --git a/arch/loongarch/include/asm/kvm_pch_pic.h b/arch/loongarch/include/asm/kvm_pch_pic.h index e74b3b742634..887b0431fd20 100644 --- a/arch/loongarch/include/asm/kvm_pch_pic.h +++ b/arch/loongarch/include/asm/kvm_pch_pic.h @@ -70,6 +70,7 @@ struct loongarch_pch_pic { struct kvm_kernel_irq_routing_entry; int kvm_loongarch_register_pch_pic_device(void); +void kvm_loongarch_unregister_pch_pic_device(void); void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level); int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level); diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c index 1985ed30dd16..ddfefea17472 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -275,6 +275,9 @@ bool kprobe_singlestep_handler(struct pt_regs *regs) struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); unsigned long addr = instruction_pointer(regs); + if (!cur) + return false; + if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) && ((unsigned long)&cur->ainsn.insn[1] == addr)) { restore_local_irqflag(kcb, regs); diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S index 2e009fbea53f..160189444684 100644 --- a/arch/loongarch/kernel/rethook_trampoline.S +++ b/arch/loongarch/kernel/rethook_trampoline.S @@ -24,7 +24,6 @@ cfi_st t6, PT_R18 cfi_st t7, PT_R19 cfi_st t8, PT_R20 - cfi_st u0, PT_R21 cfi_st fp, PT_R22 cfi_st s0, PT_R23 cfi_st s1, PT_R24 @@ -59,7 +58,6 @@ cfi_ld t6, PT_R18 cfi_ld t7, PT_R19 cfi_ld t8, PT_R20 - cfi_ld u0, PT_R21 cfi_ld fp, PT_R22 cfi_ld s0, PT_R23 cfi_ld s1, PT_R24 diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index d4b5d1b6bb01..11f54837f56c 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -14,7 +14,6 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq_work.h> -#include <linux/profile.h> #include <linux/seq_file.h> #include <linux/smp.h> #include <linux/threads.h> @@ -716,13 +715,6 @@ void smp_send_stop(void) smp_call_function(stop_this_cpu, NULL, 0); } -#ifdef CONFIG_PROFILING -int setup_profiling_timer(unsigned int multiplier) -{ - return 0; -} -#endif - static void flush_tlb_all_ipi(void *info) { local_flush_tlb_all(); diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index 840d944c2f73..ce097e25881f 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -23,7 +23,7 @@ #include "image-vars.h" /* - * Max avaliable Page Size is 64K, so we set SectionAlignment + * Max available Page Size is 64K, so we set SectionAlignment * field of EFI application to 64K. */ PECOFF_FILE_ALIGN = 0x200; diff --git a/arch/loongarch/kvm/intc/dmsintc.c b/arch/loongarch/kvm/intc/dmsintc.c index bb7285c49df3..89f980d867be 100644 --- a/arch/loongarch/kvm/intc/dmsintc.c +++ b/arch/loongarch/kvm/intc/dmsintc.c @@ -149,7 +149,7 @@ static int kvm_dmsintc_create(struct kvm_device *dev, u32 type) return -EINVAL; } - s = kzalloc(sizeof(struct loongarch_dmsintc), GFP_KERNEL); + s = kzalloc_obj(struct loongarch_dmsintc); if (!s) return -ENOMEM; @@ -180,3 +180,8 @@ int kvm_loongarch_register_dmsintc_device(void) { return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC); } + +void kvm_loongarch_unregister_dmsintc_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_DMSINTC); +} diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 84d84bd432d7..80f78e07c74a 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -695,3 +695,8 @@ int kvm_loongarch_register_eiointc_device(void) { return kvm_register_device_ops(&kvm_eiointc_dev_ops, KVM_DEV_TYPE_LOONGARCH_EIOINTC); } + +void kvm_loongarch_unregister_eiointc_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_EIOINTC); +} diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index fcfaf1a66790..7b333a4a0430 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -463,3 +463,8 @@ int kvm_loongarch_register_ipi_device(void) { return kvm_register_device_ops(&kvm_ipi_dev_ops, KVM_DEV_TYPE_LOONGARCH_IPI); } + +void kvm_loongarch_unregister_ipi_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_IPI); +} diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index e7b77705c516..2b63b0c2c7ce 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -78,6 +78,9 @@ int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int return dmsintc_set_irq(kvm, msg_addr, e->msi.data, level); } + if (e->msi.data >= EIOINTC_IRQS) + return -EINVAL; + eiointc_set_irq(kvm->arch.eiointc, e->msi.data, level); return 0; @@ -500,3 +503,8 @@ int kvm_loongarch_register_pch_pic_device(void) { return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC); } + +void kvm_loongarch_unregister_pch_pic_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_PCHPIC); +} diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index 3e1005526f4b..236523d2449d 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -385,27 +385,52 @@ static int kvm_loongarch_env_init(void) /* Register LoongArch IPI interrupt controller interface. */ ret = kvm_loongarch_register_ipi_device(); if (ret) - return ret; + goto err_env; /* Register LoongArch EIOINTC interrupt controller interface. */ ret = kvm_loongarch_register_eiointc_device(); if (ret) - return ret; + goto err_ipi; /* Register LoongArch PCH-PIC interrupt controller interface. */ ret = kvm_loongarch_register_pch_pic_device(); if (ret) - return ret; + goto err_eiointc; /* Register LoongArch DMSINTC interrupt contrroller interface */ - if (cpu_has_msgint) + if (cpu_has_msgint) { ret = kvm_loongarch_register_dmsintc_device(); + if (ret) + goto err_pch_pic; + } + + return 0; + +err_pch_pic: + kvm_loongarch_unregister_pch_pic_device(); +err_eiointc: + kvm_loongarch_unregister_eiointc_device(); +err_ipi: + kvm_loongarch_unregister_ipi_device(); +err_env: + kvm_unregister_perf_callbacks(); + kfree(kvm_loongarch_ops); + kvm_loongarch_ops = NULL; + free_percpu(vmcs); + vmcs = NULL; return ret; } static void kvm_loongarch_env_exit(void) { + if (cpu_has_msgint) + kvm_loongarch_unregister_dmsintc_device(); + + kvm_loongarch_unregister_pch_pic_device(); + kvm_loongarch_unregister_eiointc_device(); + kvm_loongarch_unregister_ipi_device(); + if (vmcs) free_percpu(vmcs); @@ -428,7 +453,11 @@ static int kvm_loongarch_init(void) if (r) return r; - return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); + r = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); + if (r) + kvm_loongarch_env_exit(); + + return r; } static void kvm_loongarch_exit(void) diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index e104897aa532..3e9a0b285fd2 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -383,6 +383,16 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, const struct kvm_memory_slot hva_t hva_start; size_t size, gpa_offset, hva_offset; + /* + * The generic code allocates a fresh, zeroed memslot for every change, + * so the arch flags computed below must be carried over when only the + * userspace flags change, e.g. when dirty logging is toggled. + */ + if (change == KVM_MR_FLAGS_ONLY) { + new->arch = old->arch; + return 0; + } + if ((change != KVM_MR_MOVE) && (change != KVM_MR_CREATE)) return 0; /* @@ -939,9 +949,3 @@ int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long gpa, bool write, in void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) { } - -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot) -{ - kvm_flush_remote_tlbs(kvm); -} diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index ed9e092c97ba..8e028be3f0a9 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -1165,10 +1165,14 @@ static int kvm_loongarch_cpucfg_set_attr(struct kvm_vcpu *vcpu, return -EINVAL; /* All vCPUs need set the same PV features */ + spin_lock(&kvm->arch.pv_setting_lock); if ((kvm->arch.pv_features & LOONGARCH_PV_FEAT_UPDATED) - && ((kvm->arch.pv_features & valid) != val)) + && ((kvm->arch.pv_features & valid) != val)) { + spin_unlock(&kvm->arch.pv_setting_lock); return -EINVAL; + } kvm->arch.pv_features = val | LOONGARCH_PV_FEAT_UPDATED; + spin_unlock(&kvm->arch.pv_setting_lock); return 0; default: return -ENXIO; diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c index 0a51931d6f6e..6dabb227a732 100644 --- a/arch/loongarch/kvm/vm.c +++ b/arch/loongarch/kvm/vm.c @@ -76,6 +76,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return -ENOMEM; } spin_lock_init(&kvm->arch.phyid_map_lock); + spin_lock_init(&kvm->arch.pv_setting_lock); kvm_init_vmcs(kvm); kvm_vm_init_features(kvm); diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 1eb588e443c9..4da278900938 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -717,7 +717,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext move_reg(ctx, t1, src); emit_zext_32(ctx, t1, true); move_imm(ctx, dst, (ctx->user_vm_start >> 32) << 32, false); - emit_insn(ctx, beq, t1, LOONGARCH_GPR_ZERO, 1); + emit_insn(ctx, beq, t1, LOONGARCH_GPR_ZERO, 2); emit_insn(ctx, or, t1, dst, t1); move_reg(ctx, dst, t1); break; diff --git a/arch/mips/bcm47xx/buttons.c b/arch/mips/bcm47xx/buttons.c index 151a4ee2803f..7bb338da8e42 100644 --- a/arch/mips/bcm47xx/buttons.c +++ b/arch/mips/bcm47xx/buttons.c @@ -523,24 +523,24 @@ bcm47xx_buttons_add(const struct bcm47xx_gpio_key *buttons, int nbuttons) /* 1 node for gpio-keys device, 1 node for each button, 1 terminator */ const struct software_node **node_group __free(kfree) = - kcalloc(1 + nbuttons + 1, sizeof(*node_group), GFP_KERNEL); + kzalloc_objs(*node_group, 1 + nbuttons + 1); if (!node_group) return -ENOMEM; /* 1 code property, 1 gpio property, 1 terminator */ struct property_entry *props __free(kfree) = - kcalloc(nbuttons * 3, sizeof(*props), GFP_KERNEL); + kzalloc_objs(*props, nbuttons * 3); if (!props) return -ENOMEM; /* 1 node for gpio-keys device, 1 node for each button */ struct software_node *nodes __free(kfree) = - kcalloc(1 + nbuttons, sizeof(*nodes), GFP_KERNEL); + kzalloc_objs(*nodes, 1 + nbuttons); if (!nodes) return -ENOMEM; struct software_node_ref_args *ref_args __free(kfree) = - kcalloc(nbuttons, sizeof(*ref_args), GFP_KERNEL); + kzalloc_objs(*ref_args, nbuttons); if (!ref_args) return -ENOMEM; diff --git a/arch/powerpc/include/asm/entry-common.h b/arch/powerpc/include/asm/entry-common.h index c5adb5006361..8e91489fdf2b 100644 --- a/arch/powerpc/include/asm/entry-common.h +++ b/arch/powerpc/include/asm/entry-common.h @@ -222,8 +222,6 @@ static inline void arch_interrupt_enter_prepare(struct pt_regs *regs) if (user_mode(regs)) { kuap_lock(); - account_cpu_user_entry(); - account_stolen_time(); } else { kuap_save_and_lock(regs); /* @@ -270,7 +268,7 @@ static inline void arch_interrupt_exit_prepare(struct pt_regs *regs) } /* irqentry_exit expects to be called with interrupts disabled */ - local_irq_disable(); + hard_irq_disable(); } static inline void arch_interrupt_async_enter_prepare(struct pt_regs *regs) @@ -515,8 +513,14 @@ static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, #ifdef CONFIG_PPC_TRANSACTIONAL_MEM local_paca->tm_scratch = regs->msr; #endif - /* Restore user access locks last */ - kuap_user_restore(regs); + /* + * Do not restore KUAP here. Generic entry might treat this as the last + * arch step before userspace but PowerPC still has kernel work after + * irqentry_exit()/syscall_exit_to_user_mode() i.e. in + * interrupt_exit_user_prepare() / syscall_exit_prepare() may enable + * IRQs and retry. Those functions restore KUAP immediately before rfi, + * which is where it should belong. + */ } #define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 028f69158532..d64cce17a4e0 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -533,9 +533,7 @@ static void eeh_rmv_device(struct eeh_dev *edev, void *userdata) if (rmv_data) list_add(&edev->rmv_entry, &rmv_data->removed_vf_list); } else { - pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(dev); - pci_unlock_rescan_remove(); } } diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c index 5b88bf72786c..55f9c0c9922a 100644 --- a/arch/powerpc/kernel/interrupt.c +++ b/arch/powerpc/kernel/interrupt.c @@ -175,7 +175,7 @@ again: current_thread_info()->exit_flags &= ~_TIF_RESTOREALL; regs->exit_result |= ret; - return ret; + return regs->exit_result; } #endif diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index ee1b5cb557c9..1ae8384637b5 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -1076,7 +1076,7 @@ int iommu_tce_check_ioba(unsigned long page_shift, if (ioba < offset) return -EINVAL; - if ((ioba + 1) > (offset + size)) + if ((ioba + npages < ioba) || (ioba - offset + npages > size)) return -EINVAL; return 0; diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 9ed9dde7d231..d218c8cc1f73 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -824,7 +824,6 @@ void __init early_init_devtree(void *params) fadump_append_bootargs(); /* Scan memory nodes and rebuild MEMBLOCKs */ - early_init_dt_scan_root(); early_init_dt_scan_memory_ppc(); /* diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index fccf96e897f6..206c825225c2 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -54,6 +54,10 @@ int rtas_pci_dn_read_config(struct pci_dn *pdn, int where, int size, u32 *val) if (!config_access_valid(pdn, where)) return PCIBIOS_BAD_REGISTER_NUMBER; #ifdef CONFIG_EEH + if (pdn->edev && + (pdn->edev->mode & EEH_DEV_REMOVED)) + return PCIBIOS_DEVICE_NOT_FOUND; + if (pdn->edev && pdn->edev->pe && (pdn->edev->pe->state & EEH_PE_CFG_BLOCKED)) return PCIBIOS_SET_FAILED; @@ -105,6 +109,10 @@ int rtas_pci_dn_write_config(struct pci_dn *pdn, int where, int size, u32 val) if (!config_access_valid(pdn, where)) return PCIBIOS_BAD_REGISTER_NUMBER; #ifdef CONFIG_EEH + if (pdn->edev && + (pdn->edev->mode & EEH_DEV_REMOVED)) + return PCIBIOS_DEVICE_NOT_FOUND; + if (pdn->edev && pdn->edev->pe && (pdn->edev->pe->state & EEH_PE_CFG_BLOCKED)) return PCIBIOS_SET_FAILED; diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c index 4916c205c4bb..fbefe1927b10 100644 --- a/arch/powerpc/kernel/syscall.c +++ b/arch/powerpc/kernel/syscall.c @@ -18,8 +18,10 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0) long ret; syscall_fn f; - if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) + if (unlikely(!syscall_enter_from_user_mode_randomize_stack(regs, &r0))) { + clear_thread_flag(TIF_SYSCALL_RET); return syscall_get_error(current, regs); + } if (unlikely(test_and_clear_thread_flag(TIF_SYSCALL_RET))) return syscall_get_error(current, regs); diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c index ea50a072debf..d9a466cd602e 100644 --- a/arch/powerpc/kexec/elf_64.c +++ b/arch/powerpc/kexec/elf_64.c @@ -94,6 +94,8 @@ static void *elf64_load(struct kimage *image, char *kernel_buf, cmdline = modified_cmdline; } + kexec_dprintk("Command line: %s", cmdline ? cmdline : ""); + if (initrd != NULL) { kbuf.buffer = initrd; kbuf.bufsz = kbuf.memsz = initrd_len; diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 6075b1c88511..bd80c5fb1b1f 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -57,7 +57,7 @@ int arch_check_excluded_range(struct kimage *image, unsigned long start, emem = image->arch.exclude_ranges; for (i = 0; i < emem->nr_ranges; i++) - if (start < emem->ranges[i].end && end > emem->ranges[i].start) + if (start <= emem->ranges[i].end && end >= emem->ranges[i].start) return 1; return 0; @@ -113,7 +113,7 @@ static int add_usable_mem(struct umem_info *um_info, u64 base, u64 end) loc_end = um_info->ranges[i].end; if (loc_base >= base && loc_end <= end) add = true; - else if (base < loc_end && end > loc_base) { + else if (base <= loc_end && end >= loc_base) { if (loc_base < base) loc_base = base; if (loc_end > end) @@ -377,16 +377,12 @@ static int load_backup_segment(struct kimage *image, struct kexec_buf *kbuf) static unsigned int kdump_extra_elfcorehdr_size(struct crash_mem *cmem) { #if defined(CONFIG_CRASH_HOTPLUG) && defined(CONFIG_MEMORY_HOTPLUG) - unsigned int extra_sz = 0; - if (CONFIG_CRASH_MAX_MEMORY_RANGES > (unsigned int)PN_XNUM) pr_warn("Number of Phdrs %u exceeds max\n", CONFIG_CRASH_MAX_MEMORY_RANGES); else if (cmem->nr_ranges >= CONFIG_CRASH_MAX_MEMORY_RANGES) pr_warn("Configured crash mem ranges may not be enough\n"); else - extra_sz = (CONFIG_CRASH_MAX_MEMORY_RANGES - cmem->nr_ranges) * sizeof(Elf64_Phdr); - - return extra_sz; + return (CONFIG_CRASH_MAX_MEMORY_RANGES - cmem->nr_ranges) * sizeof(Elf64_Phdr); #endif return 0; } diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 0409ac9e7b31..dbac3573b2c8 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -6140,12 +6140,12 @@ static int kvmppc_irq_bypass_add_producer_hv(struct irq_bypass_consumer *cons, struct kvm_kernel_irqfd *irqfd = container_of(cons, struct kvm_kernel_irqfd, consumer); - irqfd->producer = prod; - ret = kvmppc_set_passthru_irq(irqfd->kvm, prod->irq, irqfd->gsi); if (ret) pr_info("kvmppc_set_passthru_irq (irq %d, gsi %d) fails: %d\n", prod->irq, irqfd->gsi, ret); + else + irqfd->producer = prod; return ret; } diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c index 22e616662255..a6ff42d7666c 100644 --- a/arch/powerpc/kvm/book3s_hv_nested.c +++ b/arch/powerpc/kvm/book3s_hv_nested.c @@ -1204,8 +1204,10 @@ static void kvmhv_emulate_tlbie_all_lpid(struct kvm_vcpu *vcpu, int ric) spin_lock(&kvm->mmu_lock); idr_for_each_entry(&kvm->arch.kvm_nested_guest_idr, gp, lpid) { + ++gp->refcnt; spin_unlock(&kvm->mmu_lock); kvmhv_emulate_tlbie_lpid(vcpu, gp, ric); + kvmhv_put_nested(gp); spin_lock(&kvm->mmu_lock); } spin_unlock(&kvm->mmu_lock); diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c index 5fbb95d90e99..463aef870c4e 100644 --- a/arch/powerpc/kvm/book3s_hv_uvmem.c +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c @@ -779,8 +779,11 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma, if (spage) { ret = uv_page_in(kvm->arch.lpid, pfn << page_shift, gpa, 0, page_shift); - if (ret) + if (ret) { + unlock_page(dpage); + put_page(dpage); goto out_finalize; + } } } diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index d03b41336901..45792ecd5dfd 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -165,6 +165,8 @@ static int __init pas_setup_mce_regs(void) while (dev && reg < MAX_MCE_REGS) { mce_regs[reg].name = kasprintf(GFP_KERNEL, "mc%d_mcdebug_errsta", reg); + if (!mce_regs[reg].name) + return -ENOMEM; mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x730); dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa00a, dev); reg++; diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 32ecbc46e74b..728a5610d167 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1623,15 +1623,13 @@ int64_t pnv_opal_pci_msi_eoi(struct irq_data *d) return opal_pci_msi_eoi(phb->opal_id, d->parent_data->hwirq); } -static struct irq_chip pnv_pci_msi_irq_chip; - /* * Returns true iff chip is something that we could call * pnv_opal_pci_msi_eoi for. */ bool is_pnv_opal_msi(struct irq_chip *chip) { - return chip == &pnv_pci_msi_irq_chip; + return chip && chip->name && str_has_prefix(chip->name, "PNV-"); } EXPORT_SYMBOL_GPL(is_pnv_opal_msi); @@ -1728,7 +1726,7 @@ static const struct msi_parent_ops pnv_msi_parent_ops = { .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI, - .prefix = "PNV-", + .prefix = "PNV-", /* Note: is_pnv_opal_msi() uses this */ .init_dev_msi_info = pnv_init_dev_msi_info, }; diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index b8c030eab138..0cc755ac3e7f 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c @@ -6,6 +6,8 @@ * Copyright 2006 Sony Corp. */ +#include <linux/minmax.h> + #include <asm/lv1call.h> #include "platform.h" @@ -74,8 +76,9 @@ static void _dump_node(unsigned int lpar_id, u64 n1, u64 n2, u64 n3, u64 n4, static u64 make_first_field(const char *text, u64 index) { u64 n = 0; + size_t len = min(strlen(text), sizeof(n)); - memcpy((char *)&n, text, strnlen(text, sizeof(n))); + memcpy(&n, text, len); return PS3_VENDOR_ID_NONE + (n >> 32) + index; } diff --git a/arch/powerpc/platforms/pseries/htmdump.c b/arch/powerpc/platforms/pseries/htmdump.c index 489a80e87082..f33941b80ada 100644 --- a/arch/powerpc/platforms/pseries/htmdump.c +++ b/arch/powerpc/platforms/pseries/htmdump.c @@ -527,28 +527,28 @@ static int htmdump_init_debugfs(void) htm_status_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!htm_status_buf) { pr_err("Failed to allocate htmstatus buf\n"); - return -ENOMEM; + goto htm_status_buf_err; } /* Debugfs interface file to present System Processor Configuration */ htm_info_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!htm_info_buf) { pr_err("Failed to allocate htm info buf\n"); - return -ENOMEM; + goto htm_info_buf_err; } /* Debugfs interface file to present HTM capabilities */ htm_caps_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!htm_caps_buf) { pr_err("Failed to allocate htm caps buf\n"); - return -ENOMEM; + goto htm_caps_buf_err; } /* Memory to present HTM system memory configuration */ htm_mem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!htm_mem_buf) { pr_err("Failed to allocate htm mem buf\n"); - return -ENOMEM; + goto htm_mem_buf_err; } debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops); @@ -557,6 +557,17 @@ static int htmdump_init_debugfs(void) debugfs_create_file("htmsystem_mem", 0400, htmdump_debugfs_dir, htm_mem_buf, &htmsystem_mem_fops); return 0; + +htm_mem_buf_err: + kfree(htm_caps_buf); +htm_caps_buf_err: + kfree(htm_info_buf); +htm_info_buf_err: + kfree(htm_status_buf); +htm_status_buf_err: + debugfs_remove_recursive(htmdump_debugfs_dir); + kfree(htm_buf); + return -ENOMEM; } static int __init htmdump_init(void) diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index d11a64a086c1..6fc13f4a79a3 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -132,11 +132,14 @@ static int pseries_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) /* First integer stores max config */ max_config_vfs = of_read_number(&max_vfs[0], 1); - if (max_config_vfs < num_vfs || num_vfs > MAX_VFS_FOR_MAP_PE) { - dev_err(&pdev->dev, - "Num VFs %x > %x Configurable VFs\n", - num_vfs, (num_vfs > MAX_VFS_FOR_MAP_PE) ? - MAX_VFS_FOR_MAP_PE : max_config_vfs); + if (max_config_vfs < num_vfs) { + dev_err(&pdev->dev, "Num VFs %x > %x Configurable VFs\n", + num_vfs, max_config_vfs); + return -EINVAL; + } + if (num_vfs > MAX_VFS_FOR_MAP_PE) { + dev_err(&pdev->dev, "Num VFs %x > %x PE mapping limit\n", + num_vfs, MAX_VFS_FOR_MAP_PE); return -EINVAL; } diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index 8ae088632337..ed3d3e26c136 100644 --- a/arch/powerpc/sysdev/xive/common.c +++ b/arch/powerpc/sysdev/xive/common.c @@ -1134,7 +1134,7 @@ static int __init xive_init_ipis(void) if (!ipi_domain) goto out_free_fwnode; - xive_ipis = kzalloc_objs(*xive_ipis, nr_node_ids, GFP_KERNEL); + xive_ipis = kzalloc_objs(*xive_ipis, nr_node_ids); if (!xive_ipis) goto out_free_domain; diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index f8e26c4bed2b..d6c2dbf8455c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -326,7 +326,7 @@ config STACKTRACE_SUPPORT config GENERIC_BUG def_bool y depends on BUG - select GENERIC_BUG_RELATIVE_POINTERS if 64BIT + select GENERIC_BUG_RELATIVE_POINTERS config GENERIC_BUG_RELATIVE_POINTERS bool diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h index 6f581b84d8fc..699c0cf3e4ef 100644 --- a/arch/riscv/include/asm/bug.h +++ b/arch/riscv/include/asm/bug.h @@ -29,13 +29,8 @@ typedef u32 bug_insn_t; -#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS #define __BUG_ENTRY_ADDR RISCV_INT " 1b - ." #define __BUG_ENTRY_FILE(file) RISCV_INT " " file " - ." -#else -#define __BUG_ENTRY_ADDR RISCV_PTR " 1b" -#define __BUG_ENTRY_FILE(file) RISCV_PTR " " file -#endif #ifdef CONFIG_DEBUG_BUGVERBOSE #define __BUG_ENTRY(file, line, flags) \ diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 04f10a949066..123c89b694e8 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -61,8 +61,8 @@ static inline void __switch_to_fpu(struct task_struct *prev, static __always_inline bool has_fpu(void) { - return riscv_has_extension_likely(RISCV_ISA_EXT_F) || - riscv_has_extension_likely(RISCV_ISA_EXT_D); + /* D extension depends on F, so checking D alone is sufficient. */ + return riscv_has_extension_likely(RISCV_ISA_EXT_D); } #else static __always_inline bool has_fpu(void) { return false; } diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index d2ec96843456..61d21f714830 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -412,6 +412,19 @@ static const unsigned int riscv_zvbb_exts[] = { RISCV_ISA_EXT_ZVKB }; +/* + * The RISC-V ISA manual specifies that Zfh implies Zfhmin and Zvfh implies + * Zvfhmin. Report the implied subset extensions whenever the supersets are + * detected (see https://github.com/riscv/riscv-isa-manual/pull/3070). + */ +static const unsigned int riscv_zfh_exts[] = { + RISCV_ISA_EXT_ZFHMIN +}; + +static const unsigned int riscv_zvfh_exts[] = { + RISCV_ISA_EXT_ZVFHMIN +}; + #define RISCV_ISA_EXT_ZVE64F_IMPLY_LIST \ RISCV_ISA_EXT_ZVE64X, \ RISCV_ISA_EXT_ZVE32F, \ @@ -550,7 +563,8 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { __RISCV_ISA_EXT_DATA(zawrs, RISCV_ISA_EXT_ZAWRS), __RISCV_ISA_EXT_DATA_VALIDATE(zfa, RISCV_ISA_EXT_ZFA, riscv_ext_f_depends), __RISCV_ISA_EXT_DATA_VALIDATE(zfbfmin, RISCV_ISA_EXT_ZFBFMIN, riscv_ext_f_depends), - __RISCV_ISA_EXT_DATA_VALIDATE(zfh, RISCV_ISA_EXT_ZFH, riscv_ext_f_depends), + __RISCV_ISA_EXT_SUPERSET_VALIDATE(zfh, RISCV_ISA_EXT_ZFH, + riscv_zfh_exts, riscv_ext_f_depends), __RISCV_ISA_EXT_DATA_VALIDATE(zfhmin, RISCV_ISA_EXT_ZFHMIN, riscv_ext_f_depends), __RISCV_ISA_EXT_DATA(zca, RISCV_ISA_EXT_ZCA), __RISCV_ISA_EXT_DATA_VALIDATE(zcb, RISCV_ISA_EXT_ZCB, riscv_ext_zca_depends), @@ -586,7 +600,9 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = { __RISCV_ISA_EXT_SUPERSET_VALIDATE(zve64x, RISCV_ISA_EXT_ZVE64X, riscv_zve64x_exts, riscv_ext_vector_x_validate), __RISCV_ISA_EXT_DATA_VALIDATE(zvfbfmin, RISCV_ISA_EXT_ZVFBFMIN, riscv_vector_f_validate), __RISCV_ISA_EXT_DATA_VALIDATE(zvfbfwma, RISCV_ISA_EXT_ZVFBFWMA, riscv_ext_zvfbfwma_validate), - __RISCV_ISA_EXT_DATA(zvfh, RISCV_ISA_EXT_ZVFH), + __RISCV_ISA_EXT_SUPERSET_VALIDATE(zvfh, RISCV_ISA_EXT_ZVFH, + riscv_zvfh_exts, + riscv_ext_vector_float_validate), __RISCV_ISA_EXT_DATA(zvfhmin, RISCV_ISA_EXT_ZVFHMIN), __RISCV_ISA_EXT_DATA_VALIDATE(zvkb, RISCV_ISA_EXT_ZVKB, riscv_ext_vector_crypto_validate), __RISCV_ISA_EXT_DATA_VALIDATE(zvkg, RISCV_ISA_EXT_ZVKG, riscv_ext_vector_crypto_validate), diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c index 16b243376f36..2239c28981bc 100644 --- a/arch/riscv/kernel/patch.c +++ b/arch/riscv/kernel/patch.c @@ -45,6 +45,8 @@ static __always_inline void *patch_map(void *addr, const unsigned int fixmap) phys_addr_t phys; if (core_kernel_text(uintaddr) || is_kernel_exittext(uintaddr)) { + if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) + return addr; phys = __pa_symbol(addr); } else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) { struct page *page = vmalloc_to_page(addr); diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index b2df7f72241a..7cc5a6a5c020 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -349,10 +349,8 @@ long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen)) return -EINVAL; - if (!(arg & PR_TAGGED_ADDR_ENABLE)) { + if (!(arg & PR_TAGGED_ADDR_ENABLE)) pmlen = PMLEN_0; - pmm = ENVCFG_PMM_PMLEN_0; - } if (mmap_write_lock_killable(mm)) return -EINTR; diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c index bd6ca7d769da..7818e1d32622 100644 --- a/arch/riscv/kernel/sys_hwprobe.c +++ b/arch/riscv/kernel/sys_hwprobe.c @@ -297,6 +297,8 @@ static u64 hwprobe_vec_misaligned(const struct cpumask *cpus) static void hwprobe_one_pair(struct riscv_hwprobe *pair, const struct cpumask *cpus) { + pair->value = 0; + switch (pair->key) { case RISCV_HWPROBE_KEY_MVENDORID: case RISCV_HWPROBE_KEY_MARCHID: @@ -331,17 +333,14 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair, break; case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE: - pair->value = 0; if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOZ)) pair->value = riscv_cboz_block_size; break; case RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE: - pair->value = 0; if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOM)) pair->value = riscv_cbom_block_size; break; case RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE: - pair->value = 0; if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOP)) pair->value = riscv_cbop_block_size; break; diff --git a/arch/riscv/kernel/usercfi.c b/arch/riscv/kernel/usercfi.c index f027e6e05251..dec0ba5eff5e 100644 --- a/arch/riscv/kernel/usercfi.c +++ b/arch/riscv/kernel/usercfi.c @@ -525,9 +525,8 @@ static int __init setup_global_riscv_enable(char *str) if (riscv_nousercfi) pr_info("RISC-V user CFI disabled via cmdline - shadow stack status : %s, landing pad status : %s\n", - (riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_BCFI) ? "disabled" : - "enabled", (riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_FCFI) ? - "disabled" : "enabled"); + str_disabled_enabled(riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_BCFI), + str_disabled_enabled(riscv_nousercfi & CMDLINE_DISABLE_RISCV_USERCFI_FCFI)); return 1; } diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index 752f1014d633..6ff741ee7803 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -503,8 +503,8 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low } } - einfo = kvcalloc(num_events, sizeof(*einfo), - GFP_KERNEL_ACCOUNT | __GFP_NOWARN); + einfo = kvzalloc_objs(*einfo, num_events, + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (!einfo) { ret = SBI_ERR_FAILURE; goto out; diff --git a/arch/riscv/lib/uaccess.S b/arch/riscv/lib/uaccess.S index 4efea1b3326c..cf8586a937de 100644 --- a/arch/riscv/lib/uaccess.S +++ b/arch/riscv/lib/uaccess.S @@ -76,6 +76,7 @@ SYM_FUNC_START(fallback_scalar_usercopy_sum_enabled) li a3, 9*SZREG-1 /* size must >= (word_copy stride + SZREG-1) */ bltu a2, a3, .Lbyte_copy_tail +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) /* * Copy first bytes until dst is aligned to word boundary. * a0 - start of dst @@ -103,7 +104,7 @@ SYM_FUNC_START(fallback_scalar_usercopy_sum_enabled) /* a1 - start of src */ andi a3, a1, SZREG-1 bnez a3, .Lshift_copy - +#endif .Lword_copy: /* * Both src and dst are aligned, unrolled word copy @@ -137,6 +138,7 @@ SYM_FUNC_START(fallback_scalar_usercopy_sum_enabled) addi t0, t0, 8*SZREG /* revert to original value */ j .Lbyte_copy_tail +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) .Lshift_copy: /* @@ -189,6 +191,7 @@ SYM_FUNC_START(fallback_scalar_usercopy_sum_enabled) /* Revert src to original unaligned value */ add a1, a1, a3 +#endif .Lbyte_copy_tail: /* diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index f8994caefc70..fb37b0b67efe 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1465,7 +1465,9 @@ struct execmem_info __init *execmem_arch_setup(void) [EXECMEM_KPROBES] = { .start = VMALLOC_START, .end = VMALLOC_END, - .pgprot = PAGE_KERNEL_READ_EXEC, + .pgprot = IS_ENABLED(CONFIG_STRICT_MODULE_RWX) ? + PAGE_KERNEL_READ_EXEC : + PAGE_KERNEL_EXEC, .alignment = 1, }, [EXECMEM_BPF] = { diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 74efe4b138d2..151031e97a24 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -2128,7 +2128,15 @@ bool bpf_jit_supports_ptr_xchg(void) bool bpf_jit_supports_arena(void) { - return true; + /* + * The arena range tree uses kmalloc_nolock(), which needs + * cmpxchg128, provided by ZACAS on riscv. + */ +#ifdef system_has_cmpxchg128 + return system_has_cmpxchg128(); +#else + return false; +#endif } bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena) diff --git a/arch/s390/boot/alternative.c b/arch/s390/boot/alternative.c index 19ea7934b918..77e8bad560c5 100644 --- a/arch/s390/boot/alternative.c +++ b/arch/s390/boot/alternative.c @@ -45,11 +45,12 @@ static void alt_debug_modify(int type, unsigned int nr, bool clear) static char *alt_debug_parse(int type, char *str) { - unsigned long val, endval; + unsigned long val, endval, limit; char *endp; bool clear; int i; + limit = type == ALT_TYPE_FACILITY ? MAX_FACILITY_BIT : MAX_MFEATURE_BIT; if (*str == ':') { str++; } else { @@ -73,7 +74,7 @@ static char *alt_debug_parse(int type, char *str) if (str == endp) break; str = endp; - while (val <= endval) { + while (val <= endval && val < limit) { alt_debug_modify(type, val, clear); val++; } diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c index 6bc950b92be7..c1b43e5e688a 100644 --- a/arch/s390/boot/ipl_parm.c +++ b/arch/s390/boot/ipl_parm.c @@ -23,6 +23,7 @@ struct parmarea parmarea __section(".parmarea") = { }; char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; +static char command_line_buf[COMMAND_LINE_SIZE]; unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; struct ipl_parameter_block __bootdata_preserved(ipl_block); @@ -135,31 +136,29 @@ out: static void append_ipl_block_parm(void) { - char *parm, *delim; - size_t len, rc = 0; + size_t len, extra = 0; + char *delim; len = strlen(early_command_line); - - delim = early_command_line + len; /* '\0' character position */ - parm = early_command_line + len + 1; /* append right after '\0' */ + delim = early_command_line + len; /* '\0' character position */ switch (ipl_block.pb0_hdr.pbt) { case IPL_PBT_CCW: - rc = ipl_block_get_ascii_vmparm( - parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); + extra = ipl_block_get_ascii_vmparm(command_line_buf, sizeof(command_line_buf), &ipl_block); break; case IPL_PBT_FCP: case IPL_PBT_NVME: case IPL_PBT_ECKD: - rc = ipl_block_get_ascii_scpdata( - parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); + extra = ipl_block_get_ascii_scpdata(command_line_buf, sizeof(command_line_buf), &ipl_block); break; } - if (rc) { - if (*parm == '=') - memmove(early_command_line, parm + 1, rc); - else + if (extra) { + if (command_line_buf[0] == '=') { + memmove(early_command_line, command_line_buf + 1, extra); + } else if (len < COMMAND_LINE_SIZE - 2) { *delim = ' '; /* replace '\0' with space */ + sized_strscpy(delim + 1, command_line_buf, COMMAND_LINE_SIZE - len - 1); + } } } @@ -231,7 +230,7 @@ static void modify_fac_list(char *str) if (str == endp) break; str = endp; - while (val <= endval) { + while (val <= endval && val < MAX_FACILITY_BIT) { modify_facility(val, clear); val++; } @@ -245,7 +244,6 @@ static void modify_fac_list(char *str) check_cleared_facilities(); } -static char command_line_buf[COMMAND_LINE_SIZE]; void parse_boot_command_line(void) { char *param, *val; diff --git a/arch/s390/boot/physmem_info.c b/arch/s390/boot/physmem_info.c index 1f2ca5435838..0ebb2174713f 100644 --- a/arch/s390/boot/physmem_info.c +++ b/arch/s390/boot/physmem_info.c @@ -141,7 +141,7 @@ static int tprot(unsigned long addr) static unsigned long search_mem_end(void) { - unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */ + unsigned long range = 1UL << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */ unsigned long offset = 0; unsigned long pivot; diff --git a/arch/s390/crypto/Makefile b/arch/s390/crypto/Makefile index 48aeb0c0ffbd..1d6420813935 100644 --- a/arch/s390/crypto/Makefile +++ b/arch/s390/crypto/Makefile @@ -3,6 +3,8 @@ # Cryptographic API # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_CRYPTO_AES_S390) += aes_s390.o obj-$(CONFIG_CRYPTO_PAES_S390) += paes_s390.o obj-$(CONFIG_S390_PRNG) += prng.o diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c index 62edc66d5478..0be6fa779d2c 100644 --- a/arch/s390/crypto/aes_s390.c +++ b/arch/s390/crypto/aes_s390.c @@ -26,14 +26,14 @@ #include <linux/module.h> #include <linux/cpufeature.h> #include <linux/init.h> -#include <linux/mutex.h> #include <linux/fips.h> +#include <linux/semaphore.h> #include <linux/string.h> #include <crypto/xts.h> #include <asm/cpacf.h> static u8 *ctrblk; -static DEFINE_MUTEX(ctrblk_lock); +static DEFINE_SEMAPHORE(ctrblk_sem, 1); static cpacf_mask_t km_functions, kmc_functions, kmctr_functions, kma_functions; @@ -129,7 +129,7 @@ static int ecb_aes_crypt(struct skcipher_request *req, unsigned long modifier) return fallback_skcipher_crypt(sctx, req, modifier); ret = skcipher_walk_virt(&walk, req, false); - while ((nbytes = walk.nbytes) != 0) { + while (!ret && ((nbytes = walk.nbytes) != 0)) { /* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE - 1); cpacf_km(sctx->fc | modifier, sctx->key, @@ -233,7 +233,7 @@ static int cbc_aes_crypt(struct skcipher_request *req, unsigned long modifier) return ret; memcpy(param.iv, walk.iv, AES_BLOCK_SIZE); memcpy(param.key, sctx->key, sctx->key_len); - while ((nbytes = walk.nbytes) != 0) { + while (!ret && ((nbytes = walk.nbytes) != 0)) { /* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE - 1); cpacf_kmc(sctx->fc | modifier, ¶m, @@ -359,7 +359,7 @@ static int xts_aes_crypt(struct skcipher_request *req, unsigned long modifier) memcpy(xts_param.key + offset, xts_ctx->key, xts_ctx->key_len); memcpy(xts_param.init, pcc_param.xts, 16); - while ((nbytes = walk.nbytes) != 0) { + while (!ret && ((nbytes = walk.nbytes) != 0)) { /* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE - 1); cpacf_km(xts_ctx->fc | modifier, xts_param.key + offset, @@ -487,7 +487,7 @@ static int fullxts_aes_crypt(struct skcipher_request *req, unsigned long modifi memcpy(fxts_param.tweak, req->iv, AES_BLOCK_SIZE); fxts_param.nap[0] = 0x01; /* initial alpha power (1, little-endian) */ - while ((nbytes = walk.nbytes) != 0) { + while (!ret && ((nbytes = walk.nbytes) != 0)) { /* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE - 1); cpacf_km(xts_ctx->fc | modifier, fxts_param.key + offset, @@ -562,48 +562,64 @@ static unsigned int __ctrblk_init(u8 *ctrptr, u8 *iv, unsigned int nbytes) return n; } +static int __ctr_aes_crypt(struct s390_aes_ctx *sctx, + struct skcipher_walk *walk, bool locked) +{ + unsigned int n, nbytes; + int ret = 0; + u8 *ctrptr; + + while (!ret && ((nbytes = walk->nbytes) >= AES_BLOCK_SIZE)) { + n = AES_BLOCK_SIZE; + if (nbytes >= 2 * AES_BLOCK_SIZE && locked) + n = __ctrblk_init(ctrblk, walk->iv, nbytes); + ctrptr = (n > AES_BLOCK_SIZE) ? ctrblk : walk->iv; + cpacf_kmctr(sctx->fc, sctx->key, walk->dst.virt.addr, + walk->src.virt.addr, n, ctrptr); + if (ctrptr == ctrblk) + memcpy(walk->iv, ctrptr + n - AES_BLOCK_SIZE, + AES_BLOCK_SIZE); + crypto_inc(walk->iv, AES_BLOCK_SIZE); + ret = skcipher_walk_done(walk, nbytes - n); + } + + return ret; +} + static int ctr_aes_crypt(struct skcipher_request *req) { struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); struct s390_aes_ctx *sctx = crypto_skcipher_ctx(tfm); - u8 buf[AES_BLOCK_SIZE], *ctrptr; struct skcipher_walk walk; - unsigned int n, nbytes; - int ret, locked; + u8 buf[AES_BLOCK_SIZE]; + int ret; if (unlikely(!sctx->fc)) return fallback_skcipher_crypt(sctx, req, 0); - locked = mutex_trylock(&ctrblk_lock); - ret = skcipher_walk_virt(&walk, req, false); - while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { - n = AES_BLOCK_SIZE; + if (ret) + return ret; - if (nbytes >= 2*AES_BLOCK_SIZE && locked) - n = __ctrblk_init(ctrblk, walk.iv, nbytes); - ctrptr = (n > AES_BLOCK_SIZE) ? ctrblk : walk.iv; - cpacf_kmctr(sctx->fc, sctx->key, walk.dst.virt.addr, - walk.src.virt.addr, n, ctrptr); - if (ctrptr == ctrblk) - memcpy(walk.iv, ctrptr + n - AES_BLOCK_SIZE, - AES_BLOCK_SIZE); - crypto_inc(walk.iv, AES_BLOCK_SIZE); - ret = skcipher_walk_done(&walk, nbytes - n); + if (down_trylock(&ctrblk_sem) == 0) { + ret = __ctr_aes_crypt(sctx, &walk, true); + up(&ctrblk_sem); + } else { + ret = __ctr_aes_crypt(sctx, &walk, false); } - if (locked) - mutex_unlock(&ctrblk_lock); + /* * final block may be < AES_BLOCK_SIZE, copy only nbytes */ - if (nbytes) { + if (!ret && walk.nbytes > 0) { memset(buf, 0, AES_BLOCK_SIZE); - memcpy(buf, walk.src.virt.addr, nbytes); + memcpy(buf, walk.src.virt.addr, walk.nbytes); cpacf_kmctr(sctx->fc, sctx->key, buf, buf, AES_BLOCK_SIZE, walk.iv); - memcpy(walk.dst.virt.addr, buf, nbytes); + memcpy(walk.dst.virt.addr, buf, walk.nbytes); crypto_inc(walk.iv, AES_BLOCK_SIZE); ret = skcipher_walk_done(&walk, 0); + memzero_explicit(buf, sizeof(buf)); } return ret; @@ -895,10 +911,14 @@ static int gcm_aes_crypt(struct aead_request *req, unsigned int flags) gw_in.ptr, aad_bytes); n = aad_bytes + pc_bytes; - if (gcm_in_walk_done(&gw_in, n) != n) - return -ENOMEM; - if (gcm_out_walk_done(&gw_out, n) != n) - return -ENOMEM; + if (gcm_in_walk_done(&gw_in, n) != n) { + ret = -ENOMEM; + goto out; + } + if (gcm_out_walk_done(&gw_out, n) != n) { + ret = -ENOMEM; + goto out; + } aadlen -= aad_bytes; pclen -= pc_bytes; } while (aadlen + pclen > 0); @@ -910,7 +930,10 @@ static int gcm_aes_crypt(struct aead_request *req, unsigned int flags) } else scatterwalk_map_and_copy(param.t, req->dst, len, taglen, 1); +out: memzero_explicit(¶m, sizeof(param)); + memzero_explicit(gw_in.buf, sizeof(gw_in.buf)); + memzero_explicit(gw_out.buf, sizeof(gw_out.buf)); return ret; } diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c index 973436592318..f987bcbe8f35 100644 --- a/arch/s390/crypto/paes_s390.c +++ b/arch/s390/crypto/paes_s390.c @@ -19,7 +19,7 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/module.h> -#include <linux/mutex.h> +#include <linux/semaphore.h> #include <linux/spinlock.h> #include <crypto/aes.h> #include <crypto/algapi.h> @@ -45,7 +45,7 @@ module_param_named(clrkey, pkey_clrkey_allowed, bool, 0444); MODULE_PARM_DESC(clrkey, "Allow clear key material (default N)"); static u8 *ctrblk; -static DEFINE_MUTEX(ctrblk_lock); +static DEFINE_SEMAPHORE(ctrblk_sem, 1); static cpacf_mask_t km_functions, kmc_functions, kmctr_functions; @@ -220,6 +220,10 @@ static inline int convert_key(const u8 *key, unsigned int keylen, xflags); } + /* But finally map -EBUSY to -EIO to indicate an IO failure */ + if (rc == -EBUSY) + rc = -EIO; + out: pr_debug("rc=%d\n", rc); return rc; @@ -432,8 +436,11 @@ static int ecb_paes_do_crypt(struct s390_paes_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -460,6 +467,7 @@ static int ecb_paes_crypt(struct skcipher_request *req, unsigned long modifier) struct s390_paes_ctx *ctx = crypto_skcipher_ctx(tfm); struct skcipher_walk *walk = &req_ctx->walk; bool tested = crypto_skcipher_tested(tfm); + bool cleanup = true; int rc; /* @@ -491,15 +499,17 @@ static int ecb_paes_crypt(struct skcipher_request *req, unsigned long modifier) if (rc == 0 || rc == -EKEYEXPIRED) { atomic_inc(&ctx->via_engine_ctr); rc = crypto_transfer_skcipher_request_to_engine(paes_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (cleanup && walk->nbytes) skcipher_walk_done(walk, rc); out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(&req_ctx->param, sizeof(req_ctx->param)); pr_debug("rc=%d\n", rc); return rc; @@ -549,7 +559,7 @@ static int ecb_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ecb_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -559,7 +569,7 @@ static int ecb_paes_do_one_request(struct crypto_engine *engine, void *areq) atomic_dec(&ctx->via_engine_ctr); crypto_finalize_skcipher_request(engine, req, rc); local_bh_enable(); - return rc; + return 0; } static struct skcipher_engine_alg ecb_paes_alg = { @@ -567,6 +577,7 @@ static struct skcipher_engine_alg ecb_paes_alg = { .base.cra_name = "ecb(paes)", .base.cra_driver_name = "ecb-paes-s390", .base.cra_priority = 401, /* combo: aes + ecb + 1 */ + .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK, .base.cra_blocksize = AES_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct s390_paes_ctx), .base.cra_module = THIS_MODULE, @@ -690,6 +701,8 @@ static int cbc_paes_do_crypt(struct s390_paes_ctx *ctx, if (k) { memcpy(walk->iv, param->iv, AES_BLOCK_SIZE); rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; } if (k < n) { if (!maysleep) { @@ -717,6 +730,7 @@ static int cbc_paes_crypt(struct skcipher_request *req, unsigned long modifier) struct s390_paes_ctx *ctx = crypto_skcipher_ctx(tfm); struct skcipher_walk *walk = &req_ctx->walk; bool tested = crypto_skcipher_tested(tfm); + bool cleanup = true; int rc; /* @@ -748,15 +762,17 @@ static int cbc_paes_crypt(struct skcipher_request *req, unsigned long modifier) if (rc == 0 || rc == -EKEYEXPIRED) { atomic_inc(&ctx->via_engine_ctr); rc = crypto_transfer_skcipher_request_to_engine(paes_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (cleanup && walk->nbytes) skcipher_walk_done(walk, rc); out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(&req_ctx->param, sizeof(req_ctx->param)); pr_debug("rc=%d\n", rc); return rc; @@ -806,7 +822,7 @@ static int cbc_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = cbc_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -816,7 +832,7 @@ static int cbc_paes_do_one_request(struct crypto_engine *engine, void *areq) atomic_dec(&ctx->via_engine_ctr); crypto_finalize_skcipher_request(engine, req, rc); local_bh_enable(); - return rc; + return 0; } static struct skcipher_engine_alg cbc_paes_alg = { @@ -824,6 +840,7 @@ static struct skcipher_engine_alg cbc_paes_alg = { .base.cra_name = "cbc(paes)", .base.cra_driver_name = "cbc-paes-s390", .base.cra_priority = 402, /* cbc-paes-s390 + 1 */ + .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK, .base.cra_blocksize = AES_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct s390_paes_ctx), .base.cra_module = THIS_MODULE, @@ -914,41 +931,14 @@ static inline unsigned int __ctrblk_init(u8 *ctrptr, u8 *iv, unsigned int nbytes return n; } -static int ctr_paes_do_crypt(struct s390_paes_ctx *ctx, - struct s390_pctr_req_ctx *req_ctx, - bool tested, bool maysleep) +static int __ctr_paes_do_crypt(struct s390_paes_ctx *ctx, + struct ctr_param *param, + struct skcipher_walk *walk, + bool tested, bool maysleep, bool locked) { - struct ctr_param *param = &req_ctx->param; - struct skcipher_walk *walk = &req_ctx->walk; - u8 buf[AES_BLOCK_SIZE], *ctrptr; unsigned int nbytes, n, k; - int pk_state, locked, rc = 0; - - if (!req_ctx->param_init_done) { - /* fetch and check protected key state */ - spin_lock_bh(&ctx->pk_lock); - pk_state = ctx->pk_state; - switch (pk_state) { - case PK_STATE_NO_KEY: - rc = -ENOKEY; - break; - case PK_STATE_CONVERT_IN_PROGRESS: - rc = -EKEYEXPIRED; - break; - case PK_STATE_VALID: - memcpy(param->key, ctx->pk.protkey, sizeof(param->key)); - req_ctx->param_init_done = true; - break; - default: - rc = pk_state < 0 ? pk_state : -EIO; - break; - } - spin_unlock_bh(&ctx->pk_lock); - } - if (rc) - goto out; - - locked = mutex_trylock(&ctrblk_lock); + u8 *ctrptr; + int rc = 0; /* * Note that in case of partial processing or failure the walk @@ -968,32 +958,71 @@ static int ctr_paes_do_crypt(struct s390_paes_ctx *ctx, AES_BLOCK_SIZE); crypto_inc(walk->iv, AES_BLOCK_SIZE); rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; } if (k < n) { if (!maysleep) { - if (locked) - mutex_unlock(&ctrblk_lock); rc = -EKEYEXPIRED; goto out; } rc = paes_convert_key(ctx, tested); - if (rc) { - if (locked) - mutex_unlock(&ctrblk_lock); + if (rc) goto out; - } spin_lock_bh(&ctx->pk_lock); memcpy(param->key, ctx->pk.protkey, sizeof(param->key)); spin_unlock_bh(&ctx->pk_lock); } } - if (locked) - mutex_unlock(&ctrblk_lock); + +out: + return rc; +} + +static int ctr_paes_do_crypt(struct s390_paes_ctx *ctx, + struct s390_pctr_req_ctx *req_ctx, + bool tested, bool maysleep) +{ + struct ctr_param *param = &req_ctx->param; + struct skcipher_walk *walk = &req_ctx->walk; + u8 buf[AES_BLOCK_SIZE]; + int pk_state, rc = 0; + + if (!req_ctx->param_init_done) { + /* fetch and check protected key state */ + spin_lock_bh(&ctx->pk_lock); + pk_state = ctx->pk_state; + switch (pk_state) { + case PK_STATE_NO_KEY: + rc = -ENOKEY; + break; + case PK_STATE_CONVERT_IN_PROGRESS: + rc = -EKEYEXPIRED; + break; + case PK_STATE_VALID: + memcpy(param->key, ctx->pk.protkey, sizeof(param->key)); + req_ctx->param_init_done = true; + break; + default: + rc = pk_state < 0 ? pk_state : -EIO; + break; + } + spin_unlock_bh(&ctx->pk_lock); + } + if (rc) + goto out; + + if (down_trylock(&ctrblk_sem) == 0) { + rc = __ctr_paes_do_crypt(ctx, param, walk, tested, maysleep, true); + up(&ctrblk_sem); + } else { + rc = __ctr_paes_do_crypt(ctx, param, walk, tested, maysleep, false); + } /* final block may be < AES_BLOCK_SIZE, copy only nbytes */ - if (nbytes) { + if (!rc && walk->nbytes > 0) { memset(buf, 0, AES_BLOCK_SIZE); - memcpy(buf, walk->src.virt.addr, nbytes); + memcpy(buf, walk->src.virt.addr, walk->nbytes); while (1) { if (cpacf_kmctr(ctx->fc, param, buf, buf, AES_BLOCK_SIZE, @@ -1010,12 +1039,13 @@ static int ctr_paes_do_crypt(struct s390_paes_ctx *ctx, memcpy(param->key, ctx->pk.protkey, sizeof(param->key)); spin_unlock_bh(&ctx->pk_lock); } - memcpy(walk->dst.virt.addr, buf, nbytes); + memcpy(walk->dst.virt.addr, buf, walk->nbytes); crypto_inc(walk->iv, AES_BLOCK_SIZE); rc = skcipher_walk_done(walk, 0); } out: + memzero_explicit(buf, sizeof(buf)); pr_debug("rc=%d\n", rc); return rc; } @@ -1027,6 +1057,7 @@ static int ctr_paes_crypt(struct skcipher_request *req) struct s390_paes_ctx *ctx = crypto_skcipher_ctx(tfm); struct skcipher_walk *walk = &req_ctx->walk; bool tested = crypto_skcipher_tested(tfm); + bool cleanup = true; int rc; /* @@ -1057,15 +1088,17 @@ static int ctr_paes_crypt(struct skcipher_request *req) if (rc == 0 || rc == -EKEYEXPIRED) { atomic_inc(&ctx->via_engine_ctr); rc = crypto_transfer_skcipher_request_to_engine(paes_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (cleanup && walk->nbytes) skcipher_walk_done(walk, rc); out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(&req_ctx->param, sizeof(req_ctx->param)); pr_debug("rc=%d\n", rc); return rc; @@ -1105,7 +1138,7 @@ static int ctr_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = ctr_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -1115,7 +1148,7 @@ static int ctr_paes_do_one_request(struct crypto_engine *engine, void *areq) atomic_dec(&ctx->via_engine_ctr); crypto_finalize_skcipher_request(engine, req, rc); local_bh_enable(); - return rc; + return 0; } static struct skcipher_engine_alg ctr_paes_alg = { @@ -1123,6 +1156,7 @@ static struct skcipher_engine_alg ctr_paes_alg = { .base.cra_name = "ctr(paes)", .base.cra_driver_name = "ctr-paes-s390", .base.cra_priority = 402, /* ecb-paes-s390 + 1 */ + .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK, .base.cra_blocksize = 1, .base.cra_ctxsize = sizeof(struct s390_paes_ctx), .base.cra_module = THIS_MODULE, @@ -1283,8 +1317,11 @@ static int xts_paes_do_crypt_fullkey(struct s390_pxts_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param->key + offset, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -1337,7 +1374,7 @@ static inline int __xts_2keys_prep_param(struct s390_pxts_ctx *ctx, memcpy(param->init, pcc_param.xts, 16); } - memzero_explicit(pcc_param.key, sizeof(pcc_param.key)); + memzero_explicit(&pcc_param, sizeof(pcc_param)); return rc; } @@ -1377,8 +1414,11 @@ static int xts_paes_do_crypt_2keys(struct s390_pxts_ctx *ctx, n = nbytes & ~(AES_BLOCK_SIZE - 1); k = cpacf_km(ctx->fc | req_ctx->modifier, param->key + offset, walk->dst.virt.addr, walk->src.virt.addr, n); - if (k) + if (k) { rc = skcipher_walk_done(walk, nbytes - k); + if (rc) + goto out; + } if (k < n) { if (!maysleep) { rc = -EKEYEXPIRED; @@ -1450,6 +1490,7 @@ static inline int xts_paes_crypt(struct skcipher_request *req, unsigned long mod struct s390_pxts_ctx *ctx = crypto_skcipher_ctx(tfm); struct skcipher_walk *walk = &req_ctx->walk; bool tested = crypto_skcipher_tested(tfm); + bool cleanup = true; int rc; /* @@ -1481,15 +1522,17 @@ static inline int xts_paes_crypt(struct skcipher_request *req, unsigned long mod if (rc == 0 || rc == -EKEYEXPIRED) { atomic_inc(&ctx->via_engine_ctr); rc = crypto_transfer_skcipher_request_to_engine(paes_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (cleanup && walk->nbytes) skcipher_walk_done(walk, rc); out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(&req_ctx->param, sizeof(req_ctx->param)); pr_debug("rc=%d\n", rc); return rc; @@ -1539,7 +1582,7 @@ static int xts_paes_do_one_request(struct crypto_engine *engine, void *areq) rc = xts_paes_do_crypt(ctx, req_ctx, tested, true); if (rc == -EKEYEXPIRED) { return pkey_handle_expired(); - } else if (rc) { + } else if (rc && walk->nbytes) { skcipher_walk_done(walk, rc); } @@ -1549,7 +1592,7 @@ static int xts_paes_do_one_request(struct crypto_engine *engine, void *areq) atomic_dec(&ctx->via_engine_ctr); crypto_finalize_skcipher_request(engine, req, rc); local_bh_enable(); - return rc; + return 0; } static struct skcipher_engine_alg xts_paes_alg = { @@ -1557,6 +1600,7 @@ static struct skcipher_engine_alg xts_paes_alg = { .base.cra_name = "xts(paes)", .base.cra_driver_name = "xts-paes-s390", .base.cra_priority = 402, /* ecb-paes-s390 + 1 */ + .base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_NO_FALLBACK, .base.cra_blocksize = AES_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct s390_pxts_ctx), .base.cra_module = THIS_MODULE, diff --git a/arch/s390/crypto/phmac_s390.c b/arch/s390/crypto/phmac_s390.c index 020a1beb2e22..bbf8a6809ecb 100644 --- a/arch/s390/crypto/phmac_s390.c +++ b/arch/s390/crypto/phmac_s390.c @@ -62,8 +62,10 @@ static inline int hwh_prepare(struct ahash_request *req, */ static inline int hwh_advance(struct hash_walk_helper *hwh, int n) { - if (n < 0) + if (n < 0) { + hwh->walkbytes = n; return crypto_hash_walk_done(&hwh->walk, n); + } hwh->walkbytes -= n; hwh->walkaddr += n; @@ -339,6 +341,10 @@ static inline int convert_key(const u8 *key, unsigned int keylen, xflags); } + /* But finally map -EBUSY to -EIO to indicate an IO failure */ + if (rc == -EBUSY) + rc = -EIO; + out: pr_debug("rc=%d\n", rc); return rc; @@ -606,6 +612,7 @@ static int phmac_update(struct ahash_request *req) struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; struct hash_walk_helper *hwh = &req_ctx->hwh; + bool cleanup = true; int rc; /* prep the walk in the request context */ @@ -629,12 +636,15 @@ static int phmac_update(struct ahash_request *req) req_ctx->async_op = OP_UPDATE; atomic_inc(&tfm_ctx->via_engine_ctr); rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&tfm_ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) { - hwh_advance(hwh, rc); + if (cleanup) { + if (hwh->walkbytes > 0) + hwh_advance(hwh, rc); memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); } @@ -649,6 +659,7 @@ static int phmac_final(struct ahash_request *req) struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; + bool cleanup = true; int rc = 0; /* Try synchronous operation if no active engine usage */ @@ -667,12 +678,14 @@ static int phmac_final(struct ahash_request *req) req_ctx->async_op = OP_FINAL; atomic_inc(&tfm_ctx->via_engine_ctr); rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&tfm_ctx->via_engine_ctr); } out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); pr_debug("rc=%d\n", rc); return rc; @@ -685,6 +698,7 @@ static int phmac_finup(struct ahash_request *req) struct phmac_tfm_ctx *tfm_ctx = crypto_ahash_ctx(tfm); struct kmac_sha2_ctx *kmac_ctx = &req_ctx->kmac_ctx; struct hash_walk_helper *hwh = &req_ctx->hwh; + bool cleanup = true; int rc; /* prep the walk in the request context */ @@ -716,15 +730,17 @@ static int phmac_finup(struct ahash_request *req) /* req->async_op has been set to either OP_FINUP or OP_FINAL */ atomic_inc(&tfm_ctx->via_engine_ctr); rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req); - if (rc != -EINPROGRESS) + if (rc == -EINPROGRESS || rc == -EBUSY) + cleanup = false; + else atomic_dec(&tfm_ctx->via_engine_ctr); } - if (rc != -EINPROGRESS) + if (cleanup && hwh->walkbytes > 0) hwh_advance(hwh, rc); out: - if (rc != -EINPROGRESS) + if (cleanup) memzero_explicit(kmac_ctx, sizeof(*kmac_ctx)); pr_debug("rc=%d\n", rc); return rc; @@ -914,7 +930,7 @@ out: atomic_dec(&tfm_ctx->via_engine_ctr); crypto_finalize_hash_request(engine, req, rc); local_bh_enable(); - return rc; + return 0; } #define S390_ASYNC_PHMAC_ALG(x) \ diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h index a83683169d98..6174552d856d 100644 --- a/arch/s390/include/asm/cpacf.h +++ b/arch/s390/include/asm/cpacf.h @@ -301,6 +301,7 @@ static __always_inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask) { __cpacf_query_insn(opcode, mask, CPACF_FC_QUERY); + kmsan_unpoison_memory(mask, sizeof(*mask)); } static __always_inline int __cpacf_check_opcode(unsigned int opcode) @@ -370,6 +371,7 @@ static __always_inline int cpacf_query_func(unsigned int opcode, static __always_inline void __cpacf_qai(unsigned int opcode, cpacf_qai_t *qai) { __cpacf_query_insn(opcode, qai, CPACF_FC_QUERY_AUTH_INFO); + kmsan_unpoison_memory(qai, sizeof(*qai)); } /** @@ -422,6 +424,7 @@ static inline int cpacf_km(unsigned long func, void *param, [opc] "i" (CPACF_KM) : "cc", "memory", "0", "1"); + kmsan_unpoison_memory(dest, src_len - s.odd); return src_len - s.odd; } @@ -454,6 +457,7 @@ static inline int cpacf_kmc(unsigned long func, void *param, [opc] "i" (CPACF_KMC) : "cc", "memory", "0", "1"); + kmsan_unpoison_memory(dest, src_len - s.odd); return src_len - s.odd; } @@ -587,6 +591,7 @@ static inline int cpacf_kmctr(unsigned long func, void *param, u8 *dest, [opc] "i" (CPACF_KMCTR) : "cc", "memory", "0", "1"); + kmsan_unpoison_memory(dest, src_len - s.odd); return src_len - s.odd; } @@ -619,6 +624,7 @@ static inline void cpacf_prno(unsigned long func, void *param, : [fc] "d" (func), [pba] "d" ((unsigned long)param), [seed] "d" (s.pair), [opc] "i" (CPACF_PRNO) : "cc", "memory", "0", "1"); + kmsan_unpoison_memory(dest, dest_len); } /** diff --git a/arch/s390/include/asm/pai.h b/arch/s390/include/asm/pai.h index 534d0320e2aa..a3456a36aaa7 100644 --- a/arch/s390/include/asm/pai.h +++ b/arch/s390/include/asm/pai.h @@ -76,7 +76,6 @@ static __always_inline void pai_kernel_exit(struct pt_regs *regs) } #define PAI_SAVE_AREA(x) ((x)->hw.event_base) -#define PAI_CPU_MASK(x) ((x)->hw.addr_filters) #define PAI_PMU_IDX(x) ((x)->hw.last_tag) #define PAI_SWLIST(x) (&(x)->hw.tp_list) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index be8369115f6d..9434c76c25b8 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -46,7 +46,6 @@ struct pcpu { unsigned long ec_mask; /* bit mask for ec_xxx functions */ unsigned long ec_clk; /* sigp timestamp for ec_xxx */ unsigned long flags; /* per CPU flags */ - unsigned long capacity; /* cpu capacity for scheduler */ signed char state; /* physical cpu state */ signed char polarization; /* physical polarization */ u16 address; /* physical cpu address */ diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h index fb2bdbf35da5..a6c621e0491c 100644 --- a/arch/s390/include/asm/smp.h +++ b/arch/s390/include/asm/smp.h @@ -30,7 +30,7 @@ static __always_inline unsigned int raw_smp_processor_id(void) return cpu; } -#define arch_scale_cpu_capacity smp_cpu_get_capacity +#define arch_scale_cpu_capacity topology_get_cpu_scale extern struct mutex smp_cpu_state_mutex; extern unsigned int smp_cpu_mt_shift; @@ -53,9 +53,7 @@ extern void smp_save_dump_secondary_cpus(void); extern void smp_yield_cpu(int cpu); extern void smp_cpu_set_polarization(int cpu, int val); extern int smp_cpu_get_polarization(int cpu); -extern void smp_cpu_set_capacity(int cpu, unsigned long val); extern void smp_set_core_capacity(int cpu, unsigned long val); -extern unsigned long smp_cpu_get_capacity(int cpu); extern int smp_cpu_get_cpu_address(int cpu); extern void smp_fill_possible_mask(void); extern void smp_detect_cpus(void); diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 14d2b58ad093..b5bf8284dbfc 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -182,7 +182,7 @@ static struct debug_param_t { static int debug_param_num; /* functions */ -static void debug_get_param(const char *name, int *level, int *pages) +static void debug_get_param(const char *name, int *level, int *pages, bool quiet) { struct debug_param_t *p; int i; @@ -192,11 +192,13 @@ static void debug_get_param(const char *name, int *level, int *pages) if (!glob_match(p->name, name)) continue; if (level && p->level != PARAM_UNSET) { - pr_info("%s: override level to %d\n", name, p->level); + if (!quiet) + pr_info("%s: override level to %d\n", name, p->level); *level = p->level; } if (pages && p->pages != PARAM_UNSET) { - pr_info("%s: override pages to %d\n", name, p->pages); + if (!quiet) + pr_info("%s: override pages to %d\n", name, p->pages); *pages = p->pages; } } @@ -251,7 +253,7 @@ static int __init s390dbf_parse(char *arg) * regular memory allocations are possible. */ for (i = 0, id = __s390dbf_info; &id[i] < __s390dbf_info_end; i++) - debug_get_param(id[i]->name, &id[i]->level, NULL); + debug_get_param(id[i]->name, &id[i]->level, NULL, false); return rc; } @@ -395,7 +397,7 @@ static debug_info_t *debug_info_create(const char *name, int pages_per_area, int level = DEBUG_DEFAULT_LEVEL; debug_info_t *rc; - debug_get_param(name, &level, &pages_per_area); + debug_get_param(name, &level, &pages_per_area, false); rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size, level, ALL_AREAS); if (!rc) goto out; @@ -960,7 +962,7 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas) return; } - debug_get_param(id->name, &id->level, &pages_per_area); + debug_get_param(id->name, &id->level, &pages_per_area, false); copy = debug_info_alloc("", pages_per_area, nr_areas, id->buf_size, id->level, ALL_AREAS); if (!copy) { @@ -1074,9 +1076,6 @@ static void _debug_set_level(debug_info_t *id, int new_level) { unsigned long flags; - if (!id) - return; - if (new_level == DEBUG_OFF_LEVEL) { pr_info("%s: switched off\n", id->name); } else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) { @@ -1101,8 +1100,14 @@ static void _debug_set_level(debug_info_t *id, int new_level) */ void debug_set_level(debug_info_t *id, int new_level) { - /* Level specified via kernel parameter takes precedence */ - debug_get_param(id->name, &new_level, NULL); + if (!id) + return; + + /* + * Level specified via kernel parameter takes precedence. The override + * was already announced during registration, so stay quiet here. + */ + debug_get_param(id->name, &new_level, NULL, true); _debug_set_level(id, new_level); } @@ -1278,7 +1283,7 @@ void debug_set_critical(void) debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf, int len) { - debug_entry_t *active; + debug_entry_t *active = NULL; unsigned long flags; if (!debug_active || !id->areas) @@ -1289,6 +1294,8 @@ debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf, } else { raw_spin_lock_irqsave(&id->lock, flags); } + if (!id->areas) + goto out; do { active = get_active_entry(id); memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size)); @@ -1298,7 +1305,7 @@ debug_entry_t *debug_event_common(debug_info_t *id, int level, const void *buf, len -= id->buf_size; buf += id->buf_size; } while (len > 0); - +out: raw_spin_unlock_irqrestore(&id->lock, flags); return active; } @@ -1311,7 +1318,7 @@ EXPORT_SYMBOL(debug_event_common); debug_entry_t *debug_exception_common(debug_info_t *id, int level, const void *buf, int len) { - debug_entry_t *active; + debug_entry_t *active = NULL; unsigned long flags; if (!debug_active || !id->areas) @@ -1322,6 +1329,8 @@ debug_entry_t *debug_exception_common(debug_info_t *id, int level, } else { raw_spin_lock_irqsave(&id->lock, flags); } + if (!id->areas) + goto out; do { active = get_active_entry(id); memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size)); @@ -1331,7 +1340,7 @@ debug_entry_t *debug_exception_common(debug_info_t *id, int level, len -= id->buf_size; buf += id->buf_size; } while (len > 0); - +out: raw_spin_unlock_irqrestore(&id->lock, flags); return active; } @@ -1357,7 +1366,7 @@ static inline int debug_count_numargs(char *string) debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string, ...) { debug_sprintf_entry_t *curr_event; - debug_entry_t *active; + debug_entry_t *active = NULL; unsigned long flags; int numargs, idx; va_list ap; @@ -1372,6 +1381,8 @@ debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string, } else { raw_spin_lock_irqsave(&id->lock, flags); } + if (!id->areas) + goto out; active = get_active_entry(id); curr_event = (debug_sprintf_entry_t *) DEBUG_DATA(active); va_start(ap, string); @@ -1380,6 +1391,7 @@ debug_entry_t *__debug_sprintf_event(debug_info_t *id, int level, char *string, curr_event->args[idx] = va_arg(ap, long); va_end(ap); debug_finish_entry(id, active, level, 0); +out: raw_spin_unlock_irqrestore(&id->lock, flags); return active; @@ -1392,7 +1404,7 @@ EXPORT_SYMBOL(__debug_sprintf_event); debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *string, ...) { debug_sprintf_entry_t *curr_event; - debug_entry_t *active; + debug_entry_t *active = NULL; unsigned long flags; int numargs, idx; va_list ap; @@ -1408,6 +1420,8 @@ debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *stri } else { raw_spin_lock_irqsave(&id->lock, flags); } + if (!id->areas) + goto out; active = get_active_entry(id); curr_event = (debug_sprintf_entry_t *)DEBUG_DATA(active); va_start(ap, string); @@ -1416,6 +1430,7 @@ debug_entry_t *__debug_sprintf_exception(debug_info_t *id, int level, char *stri curr_event->args[idx] = va_arg(ap, long); va_end(ap); debug_finish_entry(id, active, level, 1); +out: raw_spin_unlock_irqrestore(&id->lock, flags); return active; @@ -1658,9 +1673,11 @@ static void debug_flush(debug_info_t *id, int area) unsigned long flags; int i, j; - if (!id || !id->areas) + if (!id) return; raw_spin_lock_irqsave(&id->lock, flags); + if (!id->areas) + goto out; if (area == DEBUG_FLUSH_ALL) { id->active_area = 0; memset(id->active_entries, 0, id->nr_areas * sizeof(int)); @@ -1675,6 +1692,7 @@ static void debug_flush(debug_info_t *id, int area) for (i = 0; i < id->pages_per_area; i++) memset(id->areas[area][i], 0, PAGE_SIZE); } +out: raw_spin_unlock_irqrestore(&id->lock, flags); } diff --git a/arch/s390/kernel/diag/diag324.c b/arch/s390/kernel/diag/diag324.c index fe325c2a2d0d..3eec0cc8fb9e 100644 --- a/arch/s390/kernel/diag/diag324.c +++ b/arch/s390/kernel/diag/diag324.c @@ -182,8 +182,7 @@ long diag324_pibbuf(unsigned long arg) goto out; rc = copy_to_user((void __user *)address, data->pib, data->pib->len); rc |= put_user(data->sequence, &udata->sequence); - if (rc) - rc = -EFAULT; + rc = rc ? -EFAULT : data->rc; out: mutex_unlock(&pibmutex); return rc; diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index d74ef30155aa..b1e798f8e1dd 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1157,6 +1157,8 @@ static struct attribute_group reipl_nss_attr_group = { void set_os_info_reipl_block(void) { + if (!reipl_block_actual) + return; os_info_entry_add_data(OS_INFO_REIPL_BLOCK, reipl_block_actual, reipl_block_actual->hdr.len); } @@ -1927,7 +1929,8 @@ static struct shutdown_action __refdata dump_action = { static void dump_reipl_run(struct shutdown_trigger *trigger) { struct lowcore *abs_lc; - unsigned int csum; + unsigned long ipib = 0; + unsigned int csum = 0; /* * Set REIPL_CLEAR flag in os_info flags entry indicating @@ -1943,9 +1946,12 @@ static void dump_reipl_run(struct shutdown_trigger *trigger) reipl_type == IPL_TYPE_UNKNOWN) os_info_flags |= OS_INFO_FLAG_REIPL_CLEAR; os_info_entry_add_data(OS_INFO_FLAGS_ENTRY, &os_info_flags, sizeof(os_info_flags)); - csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0); + if (reipl_block_actual) { + ipib = __pa(reipl_block_actual); + csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0); + } abs_lc = get_abs_lowcore(); - abs_lc->ipib = __pa(reipl_block_actual); + abs_lc->ipib = ipib; abs_lc->ipib_checksum = csum; put_abs_lowcore(abs_lc); dump_run(trigger); diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c index cdb8006220ca..013c3dae21ec 100644 --- a/arch/s390/kernel/perf_pai.c +++ b/arch/s390/kernel/perf_pai.c @@ -67,6 +67,7 @@ struct pai_mapptr { static struct pai_root { /* Anchor to per CPU data */ refcount_t refcnt; /* Overall active events */ + atomic_t tskctx; /* Overall per-task events */ struct pai_mapptr __percpu *mapptr; } pai_root[PAI_PMU_MAX]; @@ -93,14 +94,15 @@ struct pai_pmu { /* Define PAI PMU characteristics */ static struct pai_pmu pai_pmu[]; /* Forward declaration */ /* Free per CPU data when the last event is removed. */ -static void pai_root_free(int idx) +static void pai_root_free(int idx, int tasks) { - if (refcount_dec_and_test(&pai_root[idx].refcnt)) { + if (refcount_sub_and_test(tasks, &pai_root[idx].refcnt)) { free_percpu(pai_root[idx].mapptr); pai_root[idx].mapptr = NULL; } - debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d\n", __func__, - idx, refcount_read(&pai_root[idx].refcnt)); + debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d tskctx %d\n", + __func__, idx, refcount_read(&pai_root[idx].refcnt), + atomic_read(&pai_root[idx].tskctx)); } /* @@ -137,40 +139,54 @@ static void pai_free(struct pai_mapptr *mp) mp->mapptr = NULL; } -/* Adjust usage counters and remove allocated memory when all users are - * gone. - */ -static void pai_event_destroy_cpu(struct perf_event *event, int cpu) +/* Called under mutex_lock */ +static void pai_event_destroy_cpu(int idx, int cpu, bool hotplug) { - int idx = PAI_PMU_IDX(event); - struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu); - struct pai_map *cpump = mp->mapptr; + struct pai_mapptr *mp; + struct pai_map *cpump; + int tasks = 1; - mutex_lock(&pai_reserve_mutex); - debug_sprintf_event(paidbg, 5, "%s event %#llx idx %d cpu %d users %d " - "refcnt %u\n", __func__, event->attr.config, idx, - event->cpu, cpump->active_events, - refcount_read(&cpump->refcnt)); - if (refcount_dec_and_test(&cpump->refcnt)) + /* Check reference count and return when all gone. + * 1. An event is installed on online CPU X. + * 2. CPU x is offlined and the per-CPU data is removed. + * 3. Event is destroyed via close system call. + */ + if (!refcount_read(&pai_root[idx].refcnt)) + return; /* No events at all */ + mp = per_cpu_ptr(pai_root[idx].mapptr, cpu); + if (!mp || !mp->mapptr) /* No events on that CPU */ + return; + + /* When hotplug is true, invocation is from CPU hotplug callback. + * Delete per-CPU resource and adjust refcnt when per-task events + * are currently active. This can be more than one. + * In this case adjust counters. + */ + if (hotplug) + tasks = atomic_read(&pai_root[idx].tskctx); + + cpump = mp->mapptr; + if (refcount_sub_and_test(tasks, &cpump->refcnt)) pai_free(mp); - pai_root_free(idx); - mutex_unlock(&pai_reserve_mutex); + pai_root_free(idx, tasks); } static void pai_event_destroy(struct perf_event *event) { - int cpu; + int cpu = 0, idx = PAI_PMU_IDX(event); free_page(PAI_SAVE_AREA(event)); + cpus_read_lock(); + mutex_lock(&pai_reserve_mutex); if (event->cpu == -1) { - struct cpumask *mask = PAI_CPU_MASK(event); - - for_each_cpu(cpu, mask) - pai_event_destroy_cpu(event, cpu); - kfree(mask); + atomic_dec(&pai_root[idx].tskctx); + for_each_online_cpu(cpu) + pai_event_destroy_cpu(idx, cpu, false); } else { - pai_event_destroy_cpu(event, event->cpu); + pai_event_destroy_cpu(idx, event->cpu, false); } + mutex_unlock(&pai_reserve_mutex); + cpus_read_unlock(); } static void paicrypt_event_destroy(struct perf_event *event) @@ -234,25 +250,30 @@ static u64 paicrypt_getall(struct perf_event *event) return sum; } -/* Check concurrent access of counting and sampling for crypto events. - * This function is called in process context and it is save to block. - * When the event initialization functions fails, no other call back will - * be invoked. - * - * Allocate the memory for the event. - */ -static int pai_alloc_cpu(struct perf_event *event, int cpu) +/* Called under mutex_lock */ +static int pai_alloc_cpu(int idx, int cpu, bool hotplug) { - int rc, idx = PAI_PMU_IDX(event); struct pai_map *cpump = NULL; bool need_paiext_cb = false; struct pai_mapptr *mp; + int tasks = 1, rc = 0; + + /* When hotplug is true, invocation is from CPU hotplug callback. + * Allocate per-CPU resource when per-task events are currently active. + * This can be more than one. In this case adjust all reference + * counters. Otherwise return, this ensures memory is only allocated + * when needed. + */ + if (hotplug) { + tasks = atomic_read(&pai_root[idx].tskctx); + if (!tasks) + goto out; + } - mutex_lock(&pai_reserve_mutex); /* Allocate root node */ rc = pai_root_alloc(idx); if (rc) - goto unlock; + goto out; /* Allocate node for this event */ mp = per_cpu_ptr(pai_root[idx].mapptr, cpu); @@ -296,28 +317,45 @@ static int pai_alloc_cpu(struct perf_event *event, int cpu) goto undo; } INIT_LIST_HEAD(&cpump->syswide_list); - refcount_set(&cpump->refcnt, 1); + refcount_set(&cpump->refcnt, tasks); rc = 0; } else { - refcount_inc(&cpump->refcnt); + refcount_add(tasks, &cpump->refcnt); } + /* If tasks is greater than 1, we are called from CPU hotplug path + * and need to adjust the pai_root[idx].refcnt by the number of + * per-process events. Function pai_root_alloc(idx) already + * incremented by one. Adjust for the rest. + */ + if (tasks > 1) + refcount_add(tasks - 1, &pai_root[idx].refcnt); undo: if (rc) { /* Error in allocation of event, decrement anchor. Since * the event in not created, its destroy() function is never * invoked. Adjust the reference counter for the anchor. + * The failure happened in the case of variable + * cpump == NULL branch above. The pai_root[XXX].refcnt has + * been incremented by one. Then the per-CPU allocation + * failed, so decrement it by one, regardless of tasks. */ - pai_root_free(idx); + pai_root_free(idx, 1); } -unlock: - mutex_unlock(&pai_reserve_mutex); +out: /* If rc is non-zero, no increment of counter/sampler was done. */ return rc; } +/* Check concurrent access of counting and sampling for PAI events. + * This function is called in process context and it is safe to block. + * When the event initialization functions fails, no other call back will + * be invoked. + * Called under mutex_lock. + */ static int pai_alloc(struct perf_event *event) { + int idx = PAI_PMU_IDX(event); struct cpumask *maskptr; int cpu, rc = -ENOMEM; @@ -326,24 +364,20 @@ static int pai_alloc(struct perf_event *event) goto out; for_each_online_cpu(cpu) { - rc = pai_alloc_cpu(event, cpu); + rc = pai_alloc_cpu(idx, cpu, false); if (rc) { for_each_cpu(cpu, maskptr) - pai_event_destroy_cpu(event, cpu); - kfree(maskptr); - goto out; + pai_event_destroy_cpu(idx, cpu, false); + goto undo; } cpumask_set_cpu(cpu, maskptr); } - /* - * On error all cpumask are freed and all events have been destroyed. - * Save of which CPUs data structures have been allocated for. - * Release them in pai_event_destroy call back function - * for this event. - */ - PAI_CPU_MASK(event) = maskptr; rc = 0; + /* Trace per-task events for CPU hotplug. */ + atomic_inc(&pai_root[idx].tskctx); +undo: + kfree(maskptr); out: return rc; } @@ -391,10 +425,14 @@ static int pai_event_init(struct perf_event *event, int idx) } } + cpus_read_lock(); + mutex_lock(&pai_reserve_mutex); if (event->cpu >= 0) - rc = pai_alloc_cpu(event, event->cpu); + rc = pai_alloc_cpu(idx, event->cpu, false); else rc = pai_alloc(event); + mutex_unlock(&pai_reserve_mutex); + cpus_read_unlock(); if (rc) { free_page(PAI_SAVE_AREA(event)); goto out; @@ -464,6 +502,7 @@ static void pai_start(struct perf_event *event, int flags, cpump->event = event; } } + event->hw.state &= ~PERF_HES_STOPPED; } static void paicrypt_start(struct perf_event *event, int flags) @@ -510,6 +549,13 @@ static void pai_stop(struct perf_event *event, int flags) struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr); struct pai_map *cpump = mp->mapptr; + /* Cope with multiple invocations: + * 1. perf_event_throttle() --> PMU->stop() + * 2. task schedules out --> PMU->stop() + * Check for event already stopped. + */ + if (event->hw.state & PERF_HES_STOPPED) + return; if (!event->attr.sample_period) { /* Counting */ pai_pmu[idx].pmu->read(event); } else { /* Sampling */ @@ -672,9 +718,9 @@ static void pai_have_samples(int idx) { struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr); struct pai_map *cpump = mp->mapptr; - struct perf_event *event; + struct perf_event *event, *e2; - list_for_each_entry(event, &cpump->syswide_list, hw.tp_list) + list_for_each_entry_safe(event, e2, &cpump->syswide_list, hw.tp_list) pai_have_sample(event, cpump); } @@ -691,6 +737,17 @@ static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx, pai_have_samples(PAI_PMU_CRYPTO); } +/* Prevent ioctl(fd, PERF_EVENT_IOC_PERIOD, ...) call. + * It sets perf_event::event_limit to a positive value and causes + * perf_event_overflow() to invoke pai_stop() call back function when + * perf_event::event_limit hits zero. This is not supported because the + * sample events CRYPTO_ALL and NNPA_ALL are always taken at schedule out + * of a task. + */ +static int pai_check_period(struct perf_event *event, u64 value) +{ + return -EINVAL; +} /* ============================= paiext ====================================*/ static void paiext_event_destroy(struct perf_event *event) @@ -804,6 +861,7 @@ static struct pmu paicrypt = { .stop = paicrypt_stop, .read = paicrypt_read, .sched_task = paicrypt_sched_task, + .check_period = pai_check_period, .attr_groups = paicrypt_attr_groups }; @@ -1015,6 +1073,7 @@ static struct pmu paiext = { .stop = paiext_stop, .read = paiext_read, .sched_task = paiext_sched_task, + .check_period = pai_check_period, .attr_groups = paiext_attr_groups, }; @@ -1218,23 +1277,61 @@ static int __init paipmu_setup(void) return install_ok; } +static int pai_online_cpu(unsigned int cpu) +{ + int rc; + + mutex_lock(&pai_reserve_mutex); + rc = pai_alloc_cpu(PAI_PMU_CRYPTO, cpu, true); + if (rc) + goto out; + rc = pai_alloc_cpu(PAI_PMU_EXT, cpu, true); + if (rc) + pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true); +out: + mutex_unlock(&pai_reserve_mutex); + return rc; +} + +static int pai_offline_cpu(unsigned int cpu) +{ + mutex_lock(&pai_reserve_mutex); + pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true); + pai_event_destroy_cpu(PAI_PMU_EXT, cpu, true); + mutex_unlock(&pai_reserve_mutex); + return 0; +} + static int __init pai_init(void) { + int state, rc; + /* Setup s390dbf facility */ - paidbg = debug_register("pai", 32, 256, 128); + paidbg = debug_register("pai", 1, 1, 128); if (!paidbg) { pr_err("Registration of s390dbf pai failed\n"); return -ENOMEM; } debug_register_view(paidbg, &debug_sprintf_view); - if (!paipmu_setup()) { - /* No PMU registration, no need for debug buffer */ - debug_unregister_view(paidbg, &debug_sprintf_view); - debug_unregister(paidbg); - return -ENODEV; - } + /* CPUHP_BP_PREPARE_DYN --> before CPU is brought online */ + state = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "perf/pai:prepare", + pai_online_cpu, pai_offline_cpu); + rc = state < 0 ? state : 0; + if (rc < 0) + goto out_debug; + + rc = -ENODEV; + if (!paipmu_setup()) + goto out_cpuhp; return 0; + +out_cpuhp: + cpuhp_remove_state(state); +out_debug: + debug_unregister_view(paidbg, &debug_sprintf_view); + debug_unregister(paidbg); + return rc; } device_initcall(pai_init); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 167c72803ccf..32499cad86f0 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -659,23 +659,13 @@ int smp_cpu_get_polarization(int cpu) return per_cpu(pcpu_devices, cpu).polarization; } -void smp_cpu_set_capacity(int cpu, unsigned long val) -{ - per_cpu(pcpu_devices, cpu).capacity = val; -} - -unsigned long smp_cpu_get_capacity(int cpu) -{ - return per_cpu(pcpu_devices, cpu).capacity; -} - void smp_set_core_capacity(int cpu, unsigned long val) { int i; cpu = smp_get_base_cpu(cpu); for (i = cpu; (i <= cpu + smp_cpu_mtid) && (i < nr_cpu_ids); i++) - smp_cpu_set_capacity(i, val); + topology_set_cpu_scale(i, val); } int smp_cpu_get_cpu_address(int cpu) @@ -727,7 +717,7 @@ static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail, else pcpu->state = CPU_STATE_STANDBY; smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); - smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH); + topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH); set_cpu_present(cpu, true); if (!early && arch_register_cpu(cpu)) set_cpu_present(cpu, false); @@ -967,7 +957,7 @@ void __init smp_prepare_boot_cpu(void) ipl_pcpu->state = CPU_STATE_CONFIGURED; lc->pcpu = (unsigned long)ipl_pcpu; smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN); - smp_cpu_set_capacity(0, CPU_CAPACITY_HIGH); + topology_set_cpu_scale(0, CPU_CAPACITY_HIGH); } void __init smp_setup_processor_id(void) diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 1377c6f3f670..42fc0294f543 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -147,7 +147,7 @@ static void add_cpus_to_mask(struct topology_core *tl_core, cpumask_set_cpu(cpu, &book->mask); cpumask_set_cpu(cpu, &socket->mask); smp_cpu_set_polarization(cpu, tl_core->pp); - smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH); + topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH); } } } diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index d804e1140c2e..efcbf406f03e 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -32,7 +32,7 @@ static atomic64_t virt_timer_elapsed; DEFINE_PER_CPU(u64, mt_cycles[8]); static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; -static DEFINE_PER_CPU(u64, mt_scaling_jiffies); +static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies); static inline void set_vtimer(u64 expires) { @@ -81,7 +81,7 @@ static void update_mt_scaling(void) memcpy(cycles_old, cycles_new, sizeof(u64) * (smp_cpu_mtid + 1)); } - __this_cpu_write(mt_scaling_jiffies, jiffies_64); + __this_cpu_write(mt_scaling_jiffies, jiffies); } static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new) @@ -144,7 +144,7 @@ static int do_account_vtime(struct task_struct *tsk) lc->system_timer += timer; /* Update MT utilization calculation */ - if (smp_cpu_mtid && time_after64(jiffies_64, __this_cpu_read(mt_scaling_jiffies))) + if (smp_cpu_mtid && time_after(jiffies, __this_cpu_read(mt_scaling_jiffies))) update_mt_scaling(); /* Calculate cputime delta */ diff --git a/arch/s390/kvm/s390/s390.c b/arch/s390/kvm/s390/s390.c index b0839e887221..5c73f43782a7 100644 --- a/arch/s390/kvm/s390/s390.c +++ b/arch/s390/kvm/s390/s390.c @@ -2168,7 +2168,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) if (args->count < 1 || args->count > KVM_S390_SKEYS_MAX) return -EINVAL; - keys = kvmalloc_array(args->count, sizeof(*keys), GFP_KERNEL_ACCOUNT); + keys = kvmalloc_objs(*keys, args->count, GFP_KERNEL_ACCOUNT); if (!keys) return -ENOMEM; @@ -2205,7 +2205,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) if (args->count < 1 || args->count > KVM_S390_SKEYS_MAX) return -EINVAL; - keys = kvmalloc_array(args->count, sizeof(*keys), GFP_KERNEL_ACCOUNT); + keys = kvmalloc_objs(*keys, args->count, GFP_KERNEL_ACCOUNT); if (!keys) return -ENOMEM; diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c index 9610770fcf6d..4b160eedc5a0 100644 --- a/arch/s390/mm/pgalloc.c +++ b/arch/s390/mm/pgalloc.c @@ -55,63 +55,46 @@ static void __crst_table_upgrade(void *arg) int crst_table_upgrade(struct mm_struct *mm, unsigned long end) { - unsigned long *pgd = NULL, *p4d = NULL, *__pgd; - unsigned long asce_limit = mm->context.asce_limit; + unsigned long *table, *pgd; + int rc, notify; mmap_assert_write_locked(mm); - /* upgrade should only happen from 3 to 4, 3 to 5, or 4 to 5 levels */ - VM_BUG_ON(asce_limit < _REGION2_SIZE); - - if (end <= asce_limit) - return 0; - - if (asce_limit == _REGION2_SIZE) { - p4d = crst_table_alloc(mm); - if (unlikely(!p4d)) - goto err_p4d; - crst_table_init(p4d, _REGION2_ENTRY_EMPTY); - pagetable_p4d_ctor(virt_to_ptdesc(p4d)); - } - if (end > _REGION1_SIZE) { - pgd = crst_table_alloc(mm); - if (unlikely(!pgd)) - goto err_pgd; - crst_table_init(pgd, _REGION1_ENTRY_EMPTY); - pagetable_pgd_ctor(virt_to_ptdesc(pgd)); - } - - spin_lock_bh(&mm->page_table_lock); - - if (p4d) { - __pgd = (unsigned long *) mm->pgd; - p4d_populate(mm, (p4d_t *) p4d, (pud_t *) __pgd); - mm->pgd = (pgd_t *) p4d; - mm->context.asce_limit = _REGION1_SIZE; - mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | - _ASCE_USER_BITS | _ASCE_TYPE_REGION2; - mm_inc_nr_puds(mm); - } - if (pgd) { - __pgd = (unsigned long *) mm->pgd; - pgd_populate(mm, (pgd_t *) pgd, (p4d_t *) __pgd); - mm->pgd = (pgd_t *) pgd; - mm->context.asce_limit = TASK_SIZE_MAX; - mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | - _ASCE_USER_BITS | _ASCE_TYPE_REGION1; + VM_BUG_ON(mm->context.asce_limit < _REGION2_SIZE); + rc = 0; + notify = 0; + while (mm->context.asce_limit < end) { + table = crst_table_alloc(mm); + if (!table) { + rc = -ENOMEM; + break; + } + spin_lock_bh(&mm->page_table_lock); + pgd = (unsigned long *)mm->pgd; + if (mm->context.asce_limit == _REGION2_SIZE) { + crst_table_init(table, _REGION2_ENTRY_EMPTY); + p4d_populate(mm, (p4d_t *)table, (pud_t *)pgd); + pagetable_p4d_ctor(virt_to_ptdesc(table)); + mm->pgd = (pgd_t *)table; + mm->context.asce_limit = _REGION1_SIZE; + mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | + _ASCE_USER_BITS | _ASCE_TYPE_REGION2; + mm_inc_nr_puds(mm); + } else { + crst_table_init(table, _REGION1_ENTRY_EMPTY); + pgd_populate(mm, (pgd_t *)table, (p4d_t *)pgd); + pagetable_pgd_ctor(virt_to_ptdesc(table)); + mm->pgd = (pgd_t *)table; + mm->context.asce_limit = TASK_SIZE_MAX; + mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH | + _ASCE_USER_BITS | _ASCE_TYPE_REGION1; + } + notify = 1; + spin_unlock_bh(&mm->page_table_lock); } - - spin_unlock_bh(&mm->page_table_lock); - - on_each_cpu(__crst_table_upgrade, mm, 0); - - return 0; - -err_pgd: - pagetable_dtor(virt_to_ptdesc(p4d)); - crst_table_free(mm, p4d); -err_p4d: - return -ENOMEM; + if (notify) + on_each_cpu(__crst_table_upgrade, mm, 0); + return rc; } unsigned long *page_table_alloc_noprof(struct mm_struct *mm) diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index d98d97df792a..bbb76113a4d0 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -153,6 +153,9 @@ static ssize_t report_error_write(struct file *filp, struct kobject *kobj, if (off || (count < sizeof(*report))) return -EINVAL; + if (count < (report->length + sizeof(*report))) + return -EINVAL; + ret = sclp_pci_report(report, zdev->fh, zdev->fid); return ret ? ret : count; diff --git a/arch/x86/crypto/aria-aesni-avx2-asm_64.S b/arch/x86/crypto/aria-aesni-avx2-asm_64.S index ed53d4f46bd7..fda8cb8a99a8 100644 --- a/arch/x86/crypto/aria-aesni-avx2-asm_64.S +++ b/arch/x86/crypto/aria-aesni-avx2-asm_64.S @@ -982,6 +982,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_encrypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_encrypt_32way) @@ -1007,6 +1008,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_decrypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_decrypt_32way) @@ -1209,6 +1211,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_ctr_crypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_ctr_crypt_32way) @@ -1359,6 +1362,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfni_encrypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_encrypt_32way) @@ -1384,6 +1388,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfni_decrypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_decrypt_32way) @@ -1428,6 +1433,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfni_ctr_crypt_32way) %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_ctr_crypt_32way) diff --git a/arch/x86/crypto/aria-gfni-avx512-asm_64.S b/arch/x86/crypto/aria-gfni-avx512-asm_64.S index 860887e5d02e..ca83eb126e06 100644 --- a/arch/x86/crypto/aria-gfni-avx512-asm_64.S +++ b/arch/x86/crypto/aria-gfni-avx512-asm_64.S @@ -800,6 +800,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_encrypt_64way) %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_encrypt_64way) @@ -825,6 +826,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_decrypt_64way) %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_decrypt_64way) @@ -966,6 +968,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_ctr_crypt_64way) %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_ctr_crypt_64way) diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index cc13164d948f..1ac2ca35db53 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -3125,6 +3125,27 @@ static void intel_pmu_del_event(struct perf_event *event) this_cpu_ptr(&cpu_hw_events)->n_late_setup--; } +int __intel_pmu_quiesce(void) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + int pmu_enabled = cpuc->enabled; + + cpuc->enabled = 0; + if (pmu_enabled) + intel_pmu_disable_all(); + + return pmu_enabled; +} + +void __intel_pmu_resume(int pmu_enabled) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + + cpuc->enabled = pmu_enabled; + if (pmu_enabled) + intel_pmu_enable_all(0); +} + static int icl_set_topdown_event_period(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; @@ -3316,16 +3337,13 @@ static void intel_pmu_read_event(struct perf_event *event) if (event->hw.flags & (PERF_X86_EVENT_AUTO_RELOAD | PERF_X86_EVENT_TOPDOWN) || is_pebs_counter_event_group(event)) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - bool pmu_enabled = cpuc->enabled; + int pmu_enabled; /* Only need to call update_topdown_event() once for group read. */ if (is_metric_event(event) && (cpuc->txn_flags & PERF_PMU_TXN_READ)) return; - cpuc->enabled = 0; - if (pmu_enabled) - intel_pmu_disable_all(); - + pmu_enabled = __intel_pmu_quiesce(); /* * If the PEBS counters snapshotting is enabled, * the topdown event is available in PEBS records. @@ -3334,10 +3352,7 @@ static void intel_pmu_read_event(struct perf_event *event) static_call(intel_pmu_update_topdown_event)(event, NULL); else intel_pmu_drain_pebs_buffer(); - - cpuc->enabled = pmu_enabled; - if (pmu_enabled) - intel_pmu_enable_all(0); + __intel_pmu_resume(pmu_enabled); return; } diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c index 8940f0292229..b98029b44052 100644 --- a/arch/x86/events/intel/ds.c +++ b/arch/x86/events/intel/ds.c @@ -1242,8 +1242,11 @@ unlock: void intel_pmu_drain_pebs_buffer(void) { + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_sample_data data; + WARN_ON_ONCE(cpuc->enabled); + static_call(x86_pmu_drain_pebs)(NULL, &data); } @@ -1864,8 +1867,11 @@ static void intel_pmu_pebs_via_pt_enable(struct perf_event *event) static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc) { if (cpuc->n_pebs == cpuc->n_large_pebs && - cpuc->n_pebs != cpuc->n_pebs_via_pt) + cpuc->n_pebs != cpuc->n_pebs_via_pt) { + int enabled = __intel_pmu_quiesce(); intel_pmu_drain_pebs_buffer(); + __intel_pmu_resume(enabled); + } } static void __intel_pmu_pebs_enable(struct perf_event *event) @@ -2432,7 +2438,7 @@ static inline void __setup_pebs_basic_group(struct perf_event *event, { /* The ip in basic is EventingIP */ set_linear_ip(regs, ip); - regs->flags = PERF_EFLAGS_EXACT; + regs->flags |= PERF_EFLAGS_EXACT; setup_pebs_time(event, data, tsc); if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) @@ -2444,9 +2450,17 @@ static inline void __setup_pebs_gpr_group(struct perf_event *event, struct pebs_gprs *gprs, u64 sample_type) { + /* + * Update flags with PEBS data. PERF_EFLAGS_EXACT must be set + * in previous basic group handling. + */ + regs->flags = gprs->flags | PERF_EFLAGS_EXACT; + if (event->attr.precise_ip < 2) { set_linear_ip(regs, gprs->ip); regs->flags &= ~PERF_EFLAGS_EXACT; + } else if (regs->flags & X86_VM_MASK) { + regs->flags ^= (PERF_EFLAGS_VM | X86_VM_MASK); } if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index 71ed5b2acea2..4680cba91340 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h @@ -1638,6 +1638,9 @@ static __always_inline void __intel_pmu_lbr_disable(void) wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctl); } +extern int __intel_pmu_quiesce(void); +extern void __intel_pmu_resume(int pmu_enabled); + int intel_pmu_save_and_restart(struct perf_event *event); struct event_constraint * diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c index 243a769fdd97..85ebde361d6a 100644 --- a/arch/x86/kernel/itmt.c +++ b/arch/x86/kernel/itmt.c @@ -110,18 +110,14 @@ int sched_set_itmt_support(void) arch_debugfs_dir, &sysctl_sched_itmt_enabled, &dfs_sched_itmt_fops); - if (IS_ERR_OR_NULL(dfs_sched_itmt)) { + if (IS_ERR(dfs_sched_itmt)) dfs_sched_itmt = NULL; - return -ENOMEM; - } dfs_sched_core_prio = debugfs_create_file("sched_core_priority", 0644, arch_debugfs_dir, NULL, &sched_core_priority_fops); - if (IS_ERR_OR_NULL(dfs_sched_core_prio)) { + if (IS_ERR(dfs_sched_core_prio)) dfs_sched_core_prio = NULL; - return -ENOMEM; - } sched_itmt_capable = true; diff --git a/arch/x86/mm/mem_encrypt.c b/arch/x86/mm/mem_encrypt.c index 95bae74fdab2..3aefdef5bcb0 100644 --- a/arch/x86/mm/mem_encrypt.c +++ b/arch/x86/mm/mem_encrypt.c @@ -13,6 +13,7 @@ #include <linux/cc_platform.h> #include <linux/mem_encrypt.h> #include <linux/virtio_anchor.h> +#include <linux/iommu-dma.h> #include <asm/sev.h> @@ -30,7 +31,7 @@ bool force_dma_unencrypted(struct device *dev) * device does not support DMA to addresses that include the * encryption mask. */ - if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) { + if (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT) && !use_dma_iommu(dev)) { u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask)); u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit); diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 1a9fb530adc3..2853e87797a7 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -13,6 +13,7 @@ #include <linux/bpf_verifier.h> #include <linux/memory.h> #include <linux/sort.h> +#include <linux/execmem.h> #include <asm/extable.h> #include <asm/ftrace.h> #include <asm/set_memory.h> @@ -3818,15 +3819,16 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, * * We cannot use kvmalloc here, because we need image to be in * module memory range. - * Since it must be writable use bpf_jit_alloc_exec_rw(). + * Since it must be writable use execmem_alloc(EXECMEM_MODULE_DATA) + * that returns writable memory in the module address space. */ - image = bpf_jit_alloc_exec_rw(PAGE_SIZE); + image = execmem_alloc(EXECMEM_MODULE_DATA, PAGE_SIZE); if (!image) return -ENOMEM; ret = __arch_prepare_bpf_trampoline(&im, image, image + PAGE_SIZE, image, m, flags, tnodes, func_addr); - bpf_jit_free_exec(image); + execmem_free(image); return ret; } diff --git a/block/bio.c b/block/bio.c index 898b2f5ef8c8..f95b63c0604a 100644 --- a/block/bio.c +++ b/block/bio.c @@ -859,6 +859,7 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp) bio->bi_ioprio = bio_src->bi_ioprio; bio->bi_write_hint = bio_src->bi_write_hint; bio->bi_write_stream = bio_src->bi_write_stream; + bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit; bio->bi_iter = bio_src->bi_iter; bio->bi_io_vec = bio_src->bi_io_vec; @@ -1972,6 +1973,14 @@ struct bio *bio_split(struct bio *bio, int sectors, bio_advance(bio, split->bi_iter.bi_size); + /* + * The gap bit is set when splitting to limits and only applies to the + * front bio that was split off. The remaining bio will calcualte its + * gap value when it is subsequently split to limits, so it is safe to + * re-initialize the value back to 0. + */ + bio->bi_bvec_gap_bit = 0; + if (bio_flagged(bio, BIO_TRACE_COMPLETION)) bio_set_flag(split, BIO_TRACE_COMPLETION); diff --git a/block/genhd.c b/block/genhd.c index f1990c7cdfb9..10ca8b4d6eea 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -448,6 +448,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk, } /* + * We do not support partitions with zoned block devices, so do not try + * to scan the partitions table. + */ + if (blk_queue_is_zoned(disk->queue)) + disk->flags |= GENHD_FL_NO_PART; + + /* * If the driver provides an explicit major number it also must provide * the number of minors numbers supported, and those will be used to * setup the gendisk. diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index dfe0fbdf066d..b4c49259a1a2 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -994,7 +994,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, } ccnt = payload->command_count; - if (payload_len < struct_size(payload, data, ccnt)) { + if (!ccnt || payload_len < struct_size(payload, data, ccnt)) { XDNA_DBG(xdna, "Invalid command count %d", ccnt); return -EINVAL; } diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 31a414c3f0d9..888e857ec558 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -183,8 +183,10 @@ int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo, if (!abo) return -EINVAL; cmd = amdxdna_gem_vmap(abo); - if (!cmd) + if (!cmd) { + amdxdna_gem_put_obj(abo); return -ENOMEM; + } } memset(cmd->data, 0xff, abo->mem.size - sizeof(*cmd)); diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h index b6bef3af7dab..6e78bab8a02c 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.h +++ b/drivers/accel/amdxdna/amdxdna_ctx.h @@ -55,7 +55,7 @@ struct amdxdna_cmd_chain { u32 submit_index; u32 error_index; u32 reserved[3]; - u64 data[] __counted_by(command_count); + u64 data[]; }; /* diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index b01f5d95fac8..4b0d58d0329b 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1246,6 +1246,9 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size) { u64 end; + if (is_import_bo(abo)) + return -EOPNOTSUPP; + if (offset >= abo->mem.size) return -EINVAL; @@ -1253,9 +1256,10 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size) return -EINVAL; size = min(abo->mem.size, end) - offset; - if (is_import_bo(abo)) - drm_clflush_sg(abo->base.sgt); - else if (amdxdna_gem_vmap(abo)) + if (!size) + return 0; + + if (amdxdna_gem_vmap(abo)) drm_clflush_virt_range(amdxdna_gem_vmap(abo) + offset, size); else if (abo->base.pages) drm_clflush_pages(abo->base.pages, abo->mem.size >> PAGE_SHIFT); diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h index d4458eac8447..1eca8590e68d 100644 --- a/drivers/accel/ethosu/ethosu_device.h +++ b/drivers/accel/ethosu/ethosu_device.h @@ -87,6 +87,7 @@ struct gen_pool; #define PMU_EV_TYPE_IDLE 0x20 enum ethosu_cmds { + NPU_OP_STOP = 0x0, NPU_OP_CONV = 0x2, NPU_OP_DEPTHWISE = 0x3, NPU_OP_POOL = 0x5, diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c index d121fb0d7732..8108622de258 100644 --- a/drivers/accel/ethosu/ethosu_drv.c +++ b/drivers/accel/ethosu/ethosu_drv.c @@ -281,8 +281,6 @@ static int ethosu_device_suspend(struct device *dev) static int ethosu_sram_init(struct ethosu_device *ethosudev) { - ethosudev->npu_info.sram_size = 0; - ethosudev->srampool = of_gen_pool_get(ethosudev->base.dev->of_node, "sram", 0); if (!ethosudev->srampool) return 0; @@ -293,6 +291,7 @@ static int ethosu_sram_init(struct ethosu_device *ethosudev) ethosudev->npu_info.sram_size, ðosudev->sramphys); if (!ethosudev->sram) { + ethosudev->npu_info.sram_size = 0; dev_err(ethosudev->base.dev, "failed to allocate from SRAM pool\n"); return -ENOMEM; } @@ -356,6 +355,8 @@ static int ethosu_probe(struct platform_device *pdev) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ethosudev->regs)) + return PTR_ERR(ethosudev->regs); ethosudev->pmu_regs = ethosudev->regs; ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks); diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c index d50fed64d4d9..9afe2549ec84 100644 --- a/drivers/accel/ethosu/ethosu_gem.c +++ b/drivers/accel/ethosu/ethosu_gem.c @@ -204,7 +204,7 @@ static u64 feat_matrix_length(struct ethosu_device *edev, struct feat_matrix *fm, u32 x, u32 y, u32 c, bool ofm) { - u32 element_size, storage = fm->precision >> 14; + u32 element_size, storage = ethosu_is_u65(edev) ? 0 : fm->precision >> 14; int tile = 0; u64 addr; @@ -390,6 +390,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc_obj(*info); struct ethosu_device *edev = to_ethosu_device(ddev); u32 *bocmds = bo->base.vaddr; + bool ends_with_stop = false; struct cmd_state st; int i, ret; @@ -426,6 +427,11 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, } switch (cmd) { + case NPU_OP_STOP: + if (i != size / 4 - 1) + return -EINVAL; + ends_with_stop = true; + break; case NPU_OP_DMA_START: srclen = dma_length(info, &st.dma, &st.dma.src); dstlen = dma_length(info, &st.dma, &st.dma.dst); @@ -688,6 +694,9 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, } } + if (!ends_with_stop) + return -EINVAL; + for (i = 0; i < NPU_BASEP_REGION_MAX; i++) { if (!info->region_size[i]) continue; diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index 6a038c0384cc..8dce74db0cb4 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -154,6 +154,13 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job) drm_gem_object_put(job->cmd_bo); + if (job->done_fence) { + if (dma_fence_was_initialized(job->done_fence)) + dma_fence_put(job->done_fence); + else + dma_fence_free(job->done_fence); + } + kfree(job); } @@ -164,7 +171,6 @@ static void ethosu_job_cleanup(struct kref *ref) pm_runtime_put_autosuspend(job->dev->base.dev); - dma_fence_put(job->done_fence); dma_fence_put(job->inference_done_fence); ethosu_job_err_cleanup(job); @@ -337,7 +343,7 @@ int ethosu_job_init(struct ethosu_device *edev) ret = devm_request_threaded_irq(dev, edev->irq, ethosu_job_irq_handler, ethosu_job_irq_handler_thread, - IRQF_SHARED, KBUILD_MODNAME, + 0, KBUILD_MODNAME, edev); if (ret) { dev_err(dev, "failed to request irq\n"); @@ -368,12 +374,10 @@ int ethosu_job_open(struct ethosu_file_priv *ethosu_priv) { struct ethosu_device *dev = ethosu_priv->edev; struct drm_gpu_scheduler *sched = &dev->sched; - int ret; - ret = drm_sched_entity_init(ðosu_priv->sched_entity, - DRM_SCHED_PRIORITY_NORMAL, - &sched, 1, NULL); - return WARN_ON(ret); + return drm_sched_entity_init(ðosu_priv->sched_entity, + DRM_SCHED_PRIORITY_NORMAL, + &sched, 1, NULL); } void ethosu_job_close(struct ethosu_file_priv *ethosu_priv) @@ -415,7 +419,7 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file ejob->done_fence = kzalloc_obj(*ejob->done_fence); if (!ejob->done_fence) { ret = -ENOMEM; - goto out_cleanup_job; + goto out_put_job; } ret = drm_sched_job_init(&ejob->base, @@ -443,13 +447,13 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file if (!cmd_info->region_size[i]) continue; if (i == ETHOSU_SRAM_REGION) { - if (cmd_info->region_size[i] <= edev->npu_info.sram_size) + if (cmd_info->region_size[i] <= ejob->sram_size) continue; dev_err(dev->dev, - "cmd stream region %d size greater than SRAM size (%llu > %u)\n", + "cmd stream region %d size greater than job SRAM size (%llu > %u)\n", i, cmd_info->region_size[i], - edev->npu_info.sram_size); + ejob->sram_size); ret = -EINVAL; goto out_cleanup_job; } diff --git a/drivers/accel/ivpu/ivpu_fw_log.c b/drivers/accel/ivpu/ivpu_fw_log.c index 716467aa3156..9eafc42120b6 100644 --- a/drivers/accel/ivpu/ivpu_fw_log.c +++ b/drivers/accel/ivpu/ivpu_fw_log.c @@ -26,10 +26,17 @@ MODULE_PARM_DESC(fw_log_level, " error=" __stringify(IVPU_FW_LOG_ERROR) " fatal=" __stringify(IVPU_FW_LOG_FATAL)); +struct ivpu_fw_log_desc { + struct vpu_tracing_buffer_header *log; + u32 header_size; + u32 size; +}; + static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *offset, - struct vpu_tracing_buffer_header **out_log) + struct ivpu_fw_log_desc *desc) { struct vpu_tracing_buffer_header *log; + u32 header_size, size; if ((*offset + sizeof(*log)) > ivpu_bo_size(bo)) return -EINVAL; @@ -39,26 +46,32 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off if (log->vpu_canary_start != VPU_TRACING_BUFFER_CANARY) return -EINVAL; - if (log->header_size < sizeof(*log) || log->header_size > 1024) { - ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size); + header_size = READ_ONCE(log->header_size); + size = READ_ONCE(log->size); + + if (header_size < sizeof(*log) || header_size > 1024) { + ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", header_size); return -EINVAL; } - if (log->size < log->header_size) { - ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + if ((char *)log + size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", size); return -EINVAL; } - if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) { - ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size); + if (size < header_size) { + ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x < header size 0x%x\n", + size, header_size); return -EINVAL; } - *out_log = log; - *offset += log->size; + desc->log = log; + desc->header_size = header_size; + desc->size = size; + *offset += size; ivpu_dbg(vdev, FW_BOOT, - "FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d", - log->name, log->write_index, log->size, log->wrap_count, log->header_version, - log->header_size, log->format, log->alignment); + "FW log name \"%.*s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d", + (int)ARRAY_SIZE(log->name), log->name, log->write_index, size, log->wrap_count, + log->header_version, header_size, log->format, log->alignment); return 0; } @@ -94,11 +107,12 @@ static void fw_log_print_lines(char *buffer, u32 size, struct drm_printer *p) drm_printf(p, "%s", line); } -static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const char *prefix, +static void fw_log_print_buffer(struct ivpu_fw_log_desc *desc, const char *prefix, bool only_new_msgs, struct drm_printer *p) { - char *log_data = (void *)log + log->header_size; - u32 data_size = log->size - log->header_size; + struct vpu_tracing_buffer_header *log = desc->log; + char *log_data = (void *)log + desc->header_size; + u32 data_size = desc->size - desc->header_size; u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0; u32 log_end = READ_ONCE(log->write_index); @@ -109,7 +123,8 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha if (log->wrap_count == log->read_wrap_count) { if (log_end <= log_start) { - drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name); + drm_printf(p, "==== %s \"%.*s\" log empty ====\n", prefix, + (int)ARRAY_SIZE(log->name), log->name); return; } } else if (log->wrap_count == log->read_wrap_count + 1) { @@ -119,7 +134,8 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha log_start = log_end; } - drm_printf(p, "==== %s \"%s\" log start ====\n", prefix, log->name); + drm_printf(p, "==== %s \"%.*s\" log start ====\n", prefix, (int)ARRAY_SIZE(log->name), + log->name); if (log_end > log_start) { fw_log_print_lines(log_data + log_start, log_end - log_start, p); } else { @@ -127,18 +143,19 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha fw_log_print_lines(log_data, log_end, p); } drm_printf(p, "\n\x1b[0m"); /* add new line and clear formatting */ - drm_printf(p, "==== %s \"%s\" log end ====\n", prefix, log->name); + drm_printf(p, "==== %s \"%.*s\" log end ====\n", prefix, (int)ARRAY_SIZE(log->name), + log->name); } static void fw_log_print_all_in_bo(struct ivpu_device *vdev, const char *name, struct ivpu_bo *bo, bool only_new_msgs, struct drm_printer *p) { - struct vpu_tracing_buffer_header *log; + struct ivpu_fw_log_desc desc; u32 next = 0; - while (fw_log_from_bo(vdev, bo, &next, &log) == 0) - fw_log_print_buffer(log, name, only_new_msgs, p); + while (fw_log_from_bo(vdev, bo, &next, &desc) == 0) + fw_log_print_buffer(&desc, name, only_new_msgs, p); } void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_printer *p) @@ -149,36 +166,36 @@ void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_ void ivpu_fw_log_mark_read(struct ivpu_device *vdev) { - struct vpu_tracing_buffer_header *log; + struct ivpu_fw_log_desc desc; u32 next; next = 0; - while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) { - log->read_index = READ_ONCE(log->write_index); - log->read_wrap_count = READ_ONCE(log->wrap_count); + while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &desc) == 0) { + desc.log->read_index = READ_ONCE(desc.log->write_index); + desc.log->read_wrap_count = READ_ONCE(desc.log->wrap_count); } next = 0; - while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) { - log->read_index = READ_ONCE(log->write_index); - log->read_wrap_count = READ_ONCE(log->wrap_count); + while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &desc) == 0) { + desc.log->read_index = READ_ONCE(desc.log->write_index); + desc.log->read_wrap_count = READ_ONCE(desc.log->wrap_count); } } void ivpu_fw_log_reset(struct ivpu_device *vdev) { - struct vpu_tracing_buffer_header *log; + struct ivpu_fw_log_desc desc; u32 next; next = 0; - while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &log) == 0) { - log->read_index = 0; - log->read_wrap_count = 0; + while (fw_log_from_bo(vdev, vdev->fw->mem_log_crit, &next, &desc) == 0) { + desc.log->read_index = 0; + desc.log->read_wrap_count = 0; } next = 0; - while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &log) == 0) { - log->read_index = 0; - log->read_wrap_count = 0; + while (fw_log_from_bo(vdev, vdev->fw->mem_log_verb, &next, &desc) == 0) { + desc.log->read_index = 0; + desc.log->read_wrap_count = 0; } } diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h index 0c3350f22b55..b1ae020a4fc2 100644 --- a/drivers/accel/ivpu/ivpu_gem.h +++ b/drivers/accel/ivpu/ivpu_gem.h @@ -87,15 +87,23 @@ static inline bool ivpu_bo_is_resident(struct ivpu_bo *bo) return !!bo->base.pages; } -static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr) +static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u64 vpu_addr, u64 size) { + u64 bo_size = ivpu_bo_size(bo); + u64 offset; + if (vpu_addr < bo->vpu_addr) return NULL; - if (vpu_addr >= (bo->vpu_addr + ivpu_bo_size(bo))) + if (size > bo_size) + return NULL; + + offset = vpu_addr - bo->vpu_addr; + + if (offset > bo_size - size) return NULL; - return ivpu_bo_vaddr(bo) + (vpu_addr - bo->vpu_addr); + return ivpu_bo_vaddr(bo) + offset; } static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr) diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index 62607ec8ca8f..8e960293b77a 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -79,7 +79,7 @@ ivpu_ipc_tx_prepare(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, return -ENOMEM; } - tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr); + tx_buf = ivpu_to_cpu_addr(ipc->mem_tx, tx_buf_vpu_addr, sizeof(*tx_buf)); if (drm_WARN_ON(&vdev->drm, !tx_buf)) { gen_pool_free(ipc->mm_tx, tx_buf_vpu_addr, sizeof(*tx_buf)); return -EIO; @@ -420,7 +420,7 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev) return; } - ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr); + ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr, sizeof(*ipc_hdr)); if (!ipc_hdr) { ivpu_warn_ratelimited(vdev, "IPC msg 0x%x out of range\n", vpu_addr); continue; @@ -429,7 +429,8 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev) jsm_msg = NULL; if (ipc_hdr->channel != IVPU_IPC_CHAN_BOOT_MSG) { - jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr); + jsm_msg = ivpu_to_cpu_addr(ipc->mem_rx, ipc_hdr->data_addr, + sizeof(*jsm_msg)); if (!jsm_msg) { ivpu_warn_ratelimited(vdev, "JSM msg 0x%x out of range\n", ipc_hdr->data_addr); diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index 50bf3340e49c..2ccc55486aac 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -963,11 +963,13 @@ static int decode_status(struct qaic_device *qdev, void *trans, struct manage_ms static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg, struct wire_msg *msg, struct ioctl_resources *resources, - struct qaic_user *usr) + struct qaic_user *usr, bool orphaned_deactivate) { + u32 msg_hdr_count = le32_to_cpu(msg->hdr.count); u32 msg_hdr_len = le32_to_cpu(msg->hdr.len); struct wire_trans_hdr *trans_hdr; u32 msg_len = 0; + int trans_type; int ret; int i; @@ -975,10 +977,12 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg, msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH) return -EINVAL; - user_msg->len = 0; - user_msg->count = le32_to_cpu(msg->hdr.count); + if (user_msg) { + user_msg->len = 0; + user_msg->count = msg_hdr_count; + } - for (i = 0; i < user_msg->count; ++i) { + for (i = 0; i < msg_hdr_count; ++i) { u32 hdr_len; if (msg_len > msg_hdr_len - sizeof(*trans_hdr)) @@ -990,7 +994,20 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg, size_add(msg_len, hdr_len) > msg_hdr_len) return -EINVAL; - switch (le32_to_cpu(trans_hdr->type)) { + trans_type = le32_to_cpu(trans_hdr->type); + /* + * orphaned_deactivate is the case where a deactivate response + * is received from the device after the user owning the DBC, + * and the message requesting deactivation, has gone away. + * In this case, only process QAIC_TRANS_DEACTIVATE_FROM_DEV + * transaction and skip the others. + */ + if (orphaned_deactivate && trans_type != QAIC_TRANS_DEACTIVATE_FROM_DEV) { + msg_len += hdr_len; + continue; + } + + switch (trans_type) { case QAIC_TRANS_PASSTHROUGH_FROM_DEV: ret = decode_passthrough(qdev, trans_hdr, user_msg, &msg_len); break; @@ -1281,7 +1298,7 @@ dma_xfer_continue: goto dma_cont_failed; } - ret = decode_message(qdev, user_msg, rsp, &resources, usr); + ret = decode_message(qdev, user_msg, rsp, &resources, usr, false); dma_cont_failed: free_dbc_buf(qdev, &resources); @@ -1446,22 +1463,7 @@ static void resp_worker(struct work_struct *work) * response to the QAIC_TRANS_TERMINATE_TO_DEV transaction, * otherwise, the user can issue an soc_reset to the device. */ - u32 msg_count = le32_to_cpu(msg->hdr.count); - u32 msg_len = le32_to_cpu(msg->hdr.len); - u32 len = 0; - int j; - - for (j = 0; j < msg_count && len < msg_len; ++j) { - struct wire_trans_hdr *trans_hdr; - - trans_hdr = (struct wire_trans_hdr *)(msg->data + len); - if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) { - if (decode_deactivate(qdev, trans_hdr, &len, NULL)) - len += le32_to_cpu(trans_hdr->len); - } else { - len += le32_to_cpu(trans_hdr->len); - } - } + decode_message(qdev, NULL, msg, NULL, NULL, true); /* request must have timed out, drop packet */ kfree(msg); } diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index 3141f210fcd1..f40435505818 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -196,7 +196,7 @@ static int rocket_job_push(struct rocket_job *job) if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count)) return -EINVAL; - bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL); + bos = kvmalloc_objs(*bos, bo_count); if (!bos) return -ENOMEM; memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *)); diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c index e4293458bf61..a670722ddd5a 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c @@ -486,7 +486,8 @@ static u32 acpi_add_prt_dep(acpi_handle handle) if (ACPI_FAILURE(status)) continue; dep_devices.count = 1; - dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + dep_devices.handles = kzalloc_objs(*dep_devices.handles, + 1); if (!dep_devices.handles) { acpi_handle_err(handle, "failed to allocate memory\n"); continue; @@ -499,7 +500,8 @@ static u32 acpi_add_prt_dep(acpi_handle handle) if (!gsi_handle) continue; dep_devices.count = 1; - dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + dep_devices.handles = kzalloc_objs(*dep_devices.handles, + 1); if (!dep_devices.handles) { acpi_handle_err(handle, "failed to allocate memory\n"); continue; @@ -526,7 +528,7 @@ static u32 acpi_add_irq_dep(acpi_handle handle) continue; dep_devices.count = 1; - dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL); + dep_devices.handles = kzalloc_objs(*dep_devices.handles, 1); if (!dep_devices.handles) { acpi_handle_err(handle, "failed to allocate memory\n"); continue; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f48715ed827c..163a3cccf197 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -28,9 +28,7 @@ #include "internal.h" #include "sleep.h" -#define ACPI_BUS_CLASS "system_bus" #define ACPI_BUS_HID "LNXSYBUS" -#define ACPI_BUS_DEVICE_NAME "System Bus" #define INVALID_ACPI_HANDLE ((acpi_handle)ZERO_PAGE(0)) @@ -1450,8 +1448,6 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, acpi_object_is_system_bus(handle)) { /* \_SB, \_TZ, LNXSYBUS */ acpi_add_id(pnp, ACPI_BUS_HID); - strscpy(pnp->device_name, ACPI_BUS_DEVICE_NAME); - strscpy(pnp->device_class, ACPI_BUS_CLASS); } break; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 58f512f8952a..9b8c0935001c 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -2614,6 +2614,51 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance) } #endif +/* + * The Marvell 88SE6111/6121/6145 ("Thor") family stops reporting interrupts + * for a port when HOST_IRQ_STAT is cleared while PxIS still holds bits: PxIS + * keeps its content, HOST_IRQ_STAT reads back as 0, the port is never looked + * at again and the command in flight only ends in a timeout. On a 88SE6121 + * this makes every SATA-2 or SATA-3 disk fail to IDENTIFY, while SATA-1 disks + * happen to win the race often enough to work. + * + * Clearing the host status before servicing the ports avoids it. Marvell's + * own driver for these chips does the same and says so ("clear global before + * channel"), and ahci_xgene handles its broken edge latch the same way. The + * price is at most one spurious interrupt per valid one, which is why this is + * not the generic behaviour - see AHCI 1.1 section 10.6.2. + * + * Link: https://bugzilla.kernel.org/show_bug.cgi?id=216094 + */ +static irqreturn_t ahci_mv_irq_handler(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + unsigned int rc; + u32 irq_stat, irq_masked; + + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + irq_masked = irq_stat & hpriv->port_map; + + spin_lock(&host->lock); + + /* + * Use the unmasked value to clear the interrupt, as a spurious pending + * event on a dummy port might cause a screaming IRQ. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); + + rc = ahci_handle_port_intr(host, irq_masked); + + spin_unlock(&host->lock); + + return IRQ_RETVAL(rc); +} + static void ahci_remap_check(struct pci_dev *pdev, int bar, struct ahci_host_priv *hpriv) { @@ -2917,6 +2962,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENOMEM; hpriv->flags |= (unsigned long)pi.private_data; + /* the Marvell "Thor" family needs HOST_IRQ_STAT cleared first */ + if (board_id == board_ahci_mv) + hpriv->irq_handler = ahci_mv_irq_handler; + /* MCP65 revision A1 and A2 can't do MSI */ if (board_id == board_ahci_mcp65 && (pdev->revision == 0xa1 || pdev->revision == 0xa2)) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a7d667cfcfec..b3666519b648 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4823,28 +4823,6 @@ ata_scsi_get_phys_element_status_xlat(struct ata_queued_cmd *qc) return 0; } -static void ata_scsi_depop_ua_cap_changed_complete(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *scmd = qc->scsicmd; - u8 *cdb = scmd->cmnd; - bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12; - bool is_success = qc->err_mask == 0; - - /* - * For successful non-passthrough commands, raise a UNIT ATTENTION with - * the additional sense code set to CAPACITY DATA HAS CHANGED to be - * raised. Note that this should be done only if the capacity has - * actually changed, which may not be the case if the element that was - * specified for depopulation was already depopulated, or we did not - * restore any removed element. But a capacity change unit attention is - * harmless, so always raise the unit attention. - */ - if (is_success && !is_ata_passthru) - ata_scsi_set_sense(qc->dev, scmd, UNIT_ATTENTION, - UA_CHANGED_ASC, CAPACITY_CHANGED_ASCQ); - ata_scsi_qc_complete(qc); -} - static unsigned int ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc) { @@ -4884,7 +4862,6 @@ ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc) tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; qc->flags |= ATA_QCFLAG_RESULT_TF; - qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete; return 0; } @@ -4937,7 +4914,6 @@ ata_scsi_restore_elements_and_rebuild_xlat(struct ata_queued_cmd *qc) tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48; qc->flags |= ATA_QCFLAG_RESULT_TF; - qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete; return 0; } diff --git a/drivers/base/property.c b/drivers/base/property.c index b136c339ddae..cceaa3240ca4 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -526,7 +526,7 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode, if (nval == 0) return -ENODATA; - const char **values __free(kfree) = kcalloc(nval, sizeof(*values), GFP_KERNEL); + const char **values __free(kfree) = kzalloc_objs(*values, nval); if (!values) return -ENOMEM; diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 542ce07530a1..1ecf0791241a 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -17,7 +17,6 @@ config DM_KUNIT_TEST config DRIVER_PE_KUNIT_TEST tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT - select OF default KUNIT_ALL_TESTS config DRIVER_SWNODE_KUNIT_TEST diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c index 855e73b9b21f..89cdfc2f8498 100644 --- a/drivers/base/test/property-entry-test.c +++ b/drivers/base/test/property-entry-test.c @@ -523,6 +523,9 @@ static void pe_test_child_iteration(struct kunit *test) struct fwnode_handle *child; int error, i, num; + if (!IS_ENABLED(CONFIG_OF)) + kunit_skip(test, "requires CONFIG_OF"); + static const struct software_node node = { .name = "sw" }; static const struct software_node node1 = { .name = "sw-1", .parent = &node}; static const struct software_node node2 = { .name = "sw-2", .parent = &node}; diff --git a/drivers/block/drbd/drbd_nl_gen.c b/drivers/block/drbd/drbd_nl_gen.c index fb44b948cec8..9753dc789bde 100644 --- a/drivers/block/drbd/drbd_nl_gen.c +++ b/drivers/block/drbd/drbd_nl_gen.c @@ -656,7 +656,7 @@ static int __drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_drbd_cfg_context_nl_policy, NULL); @@ -714,7 +714,7 @@ static int __disk_conf_from_attrs(struct disk_conf *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disk_conf_nl_policy, NULL); @@ -871,7 +871,7 @@ static int __res_opts_from_attrs(struct res_opts *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_RES_OPTS_ON_NO_DATA + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_RES_OPTS_ON_NO_DATA + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_res_opts_nl_policy, NULL); @@ -921,7 +921,7 @@ static int __net_conf_from_attrs(struct net_conf *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_net_conf_nl_policy, NULL); @@ -1087,7 +1087,7 @@ static int __set_role_parms_from_attrs(struct set_role_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_set_role_parms_nl_policy, NULL); @@ -1133,7 +1133,7 @@ static int __resize_parms_from_attrs(struct resize_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resize_parms_nl_policy, NULL); @@ -1195,7 +1195,7 @@ static int __start_ov_parms_from_attrs(struct start_ov_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_start_ov_parms_nl_policy, NULL); @@ -1245,7 +1245,7 @@ static int __new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_new_c_uuid_parms_nl_policy, NULL); @@ -1291,7 +1291,7 @@ static int __disconnect_parms_from_attrs(struct disconnect_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disconnect_parms_nl_policy, NULL); @@ -1337,7 +1337,7 @@ static int __detach_parms_from_attrs(struct detach_parms *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DETACH_PARMS_FORCE_DETACH + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DETACH_PARMS_FORCE_DETACH + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_detach_parms_nl_policy, NULL); @@ -1383,7 +1383,7 @@ static int __resource_info_from_attrs(struct resource_info *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_info_nl_policy, NULL); @@ -1441,7 +1441,7 @@ static int __device_info_from_attrs(struct device_info *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_info_nl_policy, NULL); @@ -1487,7 +1487,7 @@ static int __connection_info_from_attrs(struct connection_info *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_CONNECTION_INFO_CONN_ROLE + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_CONNECTION_INFO_CONN_ROLE + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_info_nl_policy, NULL); @@ -1537,7 +1537,8 @@ static int __peer_device_info_from_attrs(struct peer_device_info *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, + DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_info_nl_policy, NULL); @@ -1599,7 +1600,8 @@ static int __resource_statistics_from_attrs(struct resource_statistics *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, + DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_statistics_nl_policy, NULL); @@ -1645,7 +1647,7 @@ static int __device_statistics_from_attrs(struct device_statistics *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_statistics_nl_policy, NULL); @@ -1743,7 +1745,8 @@ static int __connection_statistics_from_attrs(struct connection_statistics *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, + DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_statistics_nl_policy, NULL); @@ -1789,7 +1792,8 @@ static int __peer_device_statistics_from_attrs(struct peer_device_statistics *s, *ret_nested_attribute_table = NULL; if (!tla) return -ENOMSG; - ntb = kcalloc(DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1, sizeof(*ntb), GFP_KERNEL); + ntb = kzalloc_objs(*ntb, + DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1); if (!ntb) return -ENOMEM; err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_statistics_nl_policy, NULL); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 6f12976035b0..758c20678bf6 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -458,12 +458,14 @@ static void loop_update_dio_alignment(struct loop_device *lo) * Use the dio alignment of the file system if provided. The incomoing * request's bio_vec is forwarded to the backing file unchanged, so its * required memory alignment becomes the device's dma_alignment when - * used for direct-io. + * used for direct-io. The file system reports zeroed alignments if the + * file can't be used for direct-io at all, so fall back to the block + * device limits in that case. */ if (!vfs_getattr(&file->f_path, &st, STATX_DIOALIGN, 0) && - (st.result_mask & STATX_DIOALIGN)) { + (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) { lo->lo_min_dio_size = st.dio_offset_align; - lo->lo_dio_mem_align = st.dio_mem_align - 1; + lo->lo_dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1); return; } diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 020bd9f1a7b6..2be8231dcd5b 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -525,6 +525,23 @@ static int __send_request(struct request *req) err = __vdc_tx_trigger(port); if (err < 0) { printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err); + /* + * If the port was reset (-ENOTCONN), the dring and the + * LDC channel including all of its mappings are already + * torn down and reallocated - there is nothing to undo + * and @desc must not be touched. + * + * For any other failure the descriptor was never handed + * to the peer: unmap the cookies and free the descriptor + * again, so that a later retry of the request does not + * leak LDC map table entries. + */ + if (err != -ENOTCONN) { + ldc_unmap(port->vio.lp, desc->cookies, + desc->ncookies); + desc->hdr.state = VIO_DESC_FREE; + rqe->req = NULL; + } } else { port->req_id++; dr->prod = vio_dring_next(dr, dr->prod); @@ -539,6 +556,7 @@ static blk_status_t vdc_queue_rq(struct blk_mq_hw_ctx *hctx, struct vdc_port *port = hctx->queue->queuedata; struct vio_dring_state *dr; unsigned long flags; + int ret; dr = &port->vio.drings[VIO_DRIVER_TX_RING]; @@ -560,7 +578,13 @@ static blk_status_t vdc_queue_rq(struct blk_mq_hw_ctx *hctx, return BLK_STS_DEV_RESOURCE; } - if (__send_request(bd->rq) < 0) { + ret = __send_request(bd->rq); + if (ret == -EAGAIN) { + spin_unlock_irqrestore(&port->vio.lock, flags); + /* already spun for 10msec, defer 10msec and retry */ + blk_mq_delay_kick_requeue_list(hctx->queue, 10); + return BLK_STS_DEV_RESOURCE; + } else if (ret < 0) { spin_unlock_irqrestore(&port->vio.lock, flags); return BLK_STS_IOERR; } diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 6c5bec7da97c..66eb55e7162e 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -2653,6 +2653,12 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma) if (vma->vm_flags & VM_WRITE) return -EPERM; + /* + * The per-queue command buffer is kernel-written ABI; prevent + * the daemon from upgrading to writable via mprotect(). + */ + vm_flags_clear(vma, VM_MAYWRITE); + end = UBLKSRV_CMD_BUF_OFFSET + ub->dev_info.nr_hw_queues * max_sz; if (phys_off < UBLKSRV_CMD_BUF_OFFSET || phys_off >= end) return -EINVAL; @@ -3024,6 +3030,7 @@ static void ublk_queue_reset_io_flags(struct ublk_queue *ubq) ubq->canceling = false; spin_unlock(&ubq->cancel_lock); ubq->fail_io = false; + ubq->force_abort = false; } /* device can only be started after all IOs are ready */ @@ -5390,7 +5397,7 @@ static int __ublk_ctrl_reg_buf(struct ublk_device *ub, page_to_pfn(pages[i + 1]) == pfn + (i - start) + 1) i++; - range = kzalloc(sizeof(*range), GFP_KERNEL); + range = kzalloc_obj(*range); if (!range) { ret = -ENOMEM; goto unwind; @@ -5453,7 +5460,7 @@ static int ublk_ctrl_reg_buf(struct ublk_device *ub, nr_pages = buf_reg.len >> PAGE_SHIFT; /* Pin pages before any locks (may sleep) */ - pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL); + pages = kvmalloc_objs(*pages, nr_pages); if (!pages) return -ENOMEM; diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c index 4323ac108cae..f0ca221524db 100644 --- a/drivers/block/zloop.c +++ b/drivers/block/zloop.c @@ -1042,12 +1042,14 @@ static int zloop_get_block_size(struct zloop_device *zlo, * Use the dio alignment of the file system if provided. The incoming * request's bio_vec is forwarded to the backing file unchanged, so its * required memory alignment becomes the device's dma_alignment when - * used for direct-io. + * used for direct-io. The file system reports zeroed alignments if the + * file can't be used for direct-io at all, so fall back to the block + * device limits in that case. */ if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) && - (st.result_mask & STATX_DIOALIGN)) { + (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) { zlo->block_size = st.dio_offset_align; - zlo->dio_mem_align = st.dio_mem_align - 1; + zlo->dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1); } else if (sb_bdev) { zlo->block_size = bdev_physical_block_size(sb_bdev); zlo->dio_mem_align = bdev_dma_alignment(sb_bdev); diff --git a/drivers/block/zram/backend_lz4.c b/drivers/block/zram/backend_lz4.c index 1e28104ad964..1e4ad31d39a6 100644 --- a/drivers/block/zram/backend_lz4.c +++ b/drivers/block/zram/backend_lz4.c @@ -42,7 +42,7 @@ static int lz4_setup_params(struct zcomp_params *params) if (!params->dict || !params->dict_sz) return 0; - dict_stream = kzalloc_obj(*dict_stream, GFP_KERNEL); + dict_stream = kzalloc_obj(*dict_stream); if (!dict_stream) return -ENOMEM; diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index bcb2514b7bc0..909a265fd906 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev, } EXPORT_SYMBOL_GPL(btintel_version_info_tlv); +static u8 btintel_version_tlv_min_len(u8 type) +{ + switch (type) { + case INTEL_TLV_CNVI_TOP: + case INTEL_TLV_CNVR_TOP: + case INTEL_TLV_CNVI_BT: + case INTEL_TLV_CNVR_BT: + case INTEL_TLV_BUILD_NUM: + case INTEL_TLV_GIT_SHA1: + return sizeof(u32); + case INTEL_TLV_DEV_REV_ID: + case INTEL_TLV_TIME_STAMP: + return sizeof(u16); + case INTEL_TLV_IMAGE_TYPE: + case INTEL_TLV_BUILD_TYPE: + case INTEL_TLV_SECURE_BOOT: + case INTEL_TLV_OTP_LOCK: + case INTEL_TLV_API_LOCK: + case INTEL_TLV_DEBUG_LOCK: + case INTEL_TLV_LIMITED_CCE: + case INTEL_TLV_SBE_TYPE: + return sizeof(u8); + case INTEL_TLV_MIN_FW: + return 3; + case INTEL_TLV_OTP_BDADDR: + return sizeof(bdaddr_t); + default: + return 0; + } +} + int btintel_parse_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version, struct sk_buff *skb) { /* Consume Command Complete Status field */ - skb_pull(skb, 1); + if (!skb_pull(skb, 1)) + return -EINVAL; /* Event parameters contain multiple TLVs. Read each of them * and only keep the required data. Also, it use existing legacy @@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev, if (skb->len < tlv->len + sizeof(*tlv)) return -EINVAL; + if (tlv->len < btintel_version_tlv_min_len(tlv->type)) + return -EINVAL; + switch (tlv->type) { case INTEL_TLV_CNVI_TOP: version->cnvi_top = get_unaligned_le32(tlv->val); @@ -667,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev, break; case INTEL_TLV_FW_ID: snprintf(version->fw_id, sizeof(version->fw_id), - "%s", tlv->val); + "%.*s", tlv->len, tlv->val); break; default: /* Ignore rest of information */ @@ -686,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, { struct sk_buff *skb; const u8 param[1] = { 0xFF }; + int err; if (!version) return -EINVAL; @@ -704,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev, return -EIO; } - btintel_parse_version_tlv(hdev, version, skb); + err = btintel_parse_version_tlv(hdev, version, skb); kfree_skb(skb); - return 0; + return err; } /* ------- REGMAP IBT SUPPORT ------- */ diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 005c77a4f5eb..6d9649776ae7 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1099,7 +1099,7 @@ static void btintel_pcie_msix_tx_handle(struct btintel_pcie_data *data) urbd0 = &txq->urbd0s[cr_tia]; - if (urbd0->tfd_index > txq->count) + if (urbd0->tfd_index >= txq->count) return; cr_tia = (cr_tia + 1) % txq->count; @@ -1599,7 +1599,9 @@ static int btintel_pcie_submit_rx_work(struct btintel_pcie_data *data, u8 status rfh_hdr = buf; len = rfh_hdr->packet_len; - if (len <= 0) { + if (len == 0 || len > BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr)) { + bt_dev_err(data->hdev, "Invalid packet_len %d (max %zu)", len, + BTINTEL_PCIE_BUFFER_SIZE - sizeof(*rfh_hdr)); ret = -EINVAL; goto resubmit; } @@ -1696,6 +1698,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id) if (unlikely(!(intr_fh | intr_hw))) { /* Ignore interrupt, inta == 0 */ + bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n"); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST, + BIT(entry->entry)); return IRQ_NONE; } diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index c0ed51567ed4..26d525acd659 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -1374,16 +1374,6 @@ int btmtk_usb_setup(struct hci_dev *hdev) break; case 0x7922: case 0x7925: - /* - * A remote wakeup could cause the device completely unresponsive, and - * recovering from such a state needs a power cycle. - * - * Since the remote wakeup capability is super broken, just disable it - * to get rid of the troubles. The device can still be autosuspended - * when the bluetooth interface is closed. - */ - device_set_wakeup_capable(&btmtk_data->udev->dev, false); - fallthrough; case 0x7961: case 0x7902: case 0x6639: @@ -1587,5 +1577,6 @@ MODULE_FIRMWARE(FIRMWARE_MT7663); MODULE_FIRMWARE(FIRMWARE_MT7668); MODULE_FIRMWARE(FIRMWARE_MT7922); MODULE_FIRMWARE(FIRMWARE_MT7961); +MODULE_FIRMWARE(FIRMWARE_MT7920); MODULE_FIRMWARE(FIRMWARE_MT7925); MODULE_FIRMWARE(FIRMWARE_MT7927); diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h index c83c24897c95..bc26148ec544 100644 --- a/drivers/bluetooth/btmtk.h +++ b/drivers/bluetooth/btmtk.h @@ -7,6 +7,7 @@ #define FIRMWARE_MT7922 "mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin" #define FIRMWARE_MT7902 "mediatek/BT_RAM_CODE_MT7902_1_1_hdr.bin" #define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin" +#define FIRMWARE_MT7920 "mediatek/BT_RAM_CODE_MT7961_1a_2_hdr.bin" #define FIRMWARE_MT7925 "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin" #define FIRMWARE_MT7927 "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin" diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c index d2e13fcb6bab..d669ea4eb3eb 100644 --- a/drivers/bluetooth/btqcomsmd.c +++ b/drivers/bluetooth/btqcomsmd.c @@ -188,7 +188,10 @@ static int btqcomsmd_probe(struct platform_device *pdev) return 0; hci_free_dev: + rpmsg_destroy_ept(btq->cmd_channel); + rpmsg_destroy_ept(btq->acl_channel); hci_free_dev(hdev); + return ret; destroy_cmd_channel: rpmsg_destroy_ept(btq->cmd_channel); destroy_acl_channel: @@ -202,10 +205,11 @@ static void btqcomsmd_remove(struct platform_device *pdev) struct btqcomsmd *btq = platform_get_drvdata(pdev); hci_unregister_dev(btq->hdev); - hci_free_dev(btq->hdev); rpmsg_destroy_ept(btq->cmd_channel); rpmsg_destroy_ept(btq->acl_channel); + + hci_free_dev(btq->hdev); } static const struct of_device_id btqcomsmd_of_match[] = { diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 03fa9409e3ee..d29813331603 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -591,7 +591,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev, * headers. */ if (!key_id) - break; + continue; rc = btrtl_parse_section(hdev, btrtl_dev, opcode, ptr, section_len); break; @@ -600,8 +600,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev, ptr, section_len); break; default: - rc = 0; - break; + continue; } if (rc < 0) { rtl_dev_err(hdev, "RTL: Parse section (%u) err %d", diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index d70a3e7a13f5..002b9f975710 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -6,6 +6,7 @@ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org> */ +#include <linux/cpufeature.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/usb.h> @@ -980,6 +981,8 @@ struct btqca_data { #define BTUSB_USE_ALT3_FOR_WBS 15 #define BTUSB_ALT6_CONTINUOUS_TX 16 #define BTUSB_HW_SSR_ACTIVE 17 +#define BTUSB_WAKEUP_BROKEN 18 +#define BTUSB_RESET 19 struct btusb_data { struct hci_dev *hdev; @@ -1054,13 +1057,15 @@ static void btusb_reset(struct hci_dev *hdev) int err; data = hci_get_drvdata(hdev); - /* This is not an unbalanced PM reference since the device will reset */ err = usb_autopm_get_interface(data->intf); if (err) { bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err); return; } + if (test_and_set_bit(BTUSB_RESET, &data->flags)) + usb_autopm_put_interface_no_suspend(data->intf); + bt_dev_err(hdev, "Resetting usb device."); usb_queue_reset_device(data->intf); } @@ -2092,18 +2097,24 @@ static int btusb_close(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - cancel_delayed_work(&data->rx_work); cancel_work_sync(&data->work); cancel_work_sync(&data->waker); - skb_queue_purge(&data->acl_q); - clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(BTUSB_DIAG_RUNNING, &data->flags); btusb_stop_traffic(data); + + /* rx_work must only be canceled once the URBs that can rearm it are + * gone, and it must be canceled synchronously since btusb_disconnect() + * frees the btusb_data it dereferences right after hci_unregister_dev(). + */ + cancel_delayed_work_sync(&data->rx_work); + + skb_queue_purge(&data->acl_q); + btusb_free_frags(data); err = usb_autopm_get_interface(data->intf); @@ -2129,7 +2140,7 @@ static int btusb_flush(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - cancel_delayed_work(&data->rx_work); + cancel_delayed_work_sync(&data->rx_work); skb_queue_purge(&data->acl_q); @@ -2923,8 +2934,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) } err = usb_autopm_get_interface(data->intf); - if (err < 0) + if (err < 0) { + bt_dev_err(hdev, "Failed usb_autopm_get_interface: %d", err); + clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags); return err; + } /* Release MediaTek ISO data interface */ btusb_mtk_release_iso_intf(hdev); @@ -2946,6 +2960,11 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id); + if (test_and_set_bit(BTUSB_RESET, &data->flags)) { + bt_dev_err(hdev, "last usb reset failed? Resetting again"); + usb_autopm_put_interface_no_suspend(data->intf); + } + usb_queue_reset_device(data->intf); clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags); @@ -2969,10 +2988,25 @@ static int btusb_send_frame_mtk(struct hci_dev *hdev, struct sk_buff *skb) } } +static inline bool platform_is_ryzen(void) +{ +#ifdef CONFIG_X86 + return boot_cpu_has(X86_FEATURE_ZEN); +#else + return false; +#endif +} + +static inline bool is_direct_child_of_root_hub(struct usb_device *udev) +{ + return udev->parent == udev->bus->root_hub; +} + static int btusb_mtk_setup(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); struct btmtk_data *btmtk_data = hci_get_priv(hdev); + int err; /* MediaTek WMT vendor cmd requiring below USB resources to * complete the handshake. @@ -2989,7 +3023,40 @@ static int btusb_mtk_setup(struct hci_dev *hdev) btusb_mtk_claim_iso_intf(data); } - return btmtk_usb_setup(hdev); + err = btmtk_usb_setup(hdev); + if (err) + return err; + + switch (btmtk_data->dev_id) { + case 0x7922: + case 0x7925: + /* + * All reports seen to be relevant to Ryzen-based laptops. These + * NICs are usually used as OEM components thanks to some sort + * of reference designs. + * + * Their popularity on other platforms is unclear. While there + * is still a chance that the quirk may exist on other + * platforms, be cautious and only apply the quirk to direct + * children of Ryzen platforms's root hubs for the time being. + * + * In most cases the root hub is on the SoC or PCH, which needs + * the quirk. Unfortunately, this can't distinguish root hubs on + * PCIe add-in cards. Such roughness should be acceptable, as + * PCIe USB controller add-in cards are less commonly used + * nowadays. On the other hand, applying the quirk doesn't hurt + * any functionalities either, as the device can still be used + * as a wakeup source if desired. + * + * Theoretically, we could retrieve the root hub's PCI vendor ID + * with some hierarchy magic, but that's too intrusive... + */ + if (platform_is_ryzen() && is_direct_child_of_root_hub(data->udev)) + set_bit(BTUSB_WAKEUP_BROKEN, &data->flags); + break; + } + + return 0; } static int btusb_mtk_shutdown(struct hci_dev *hdev) @@ -4540,6 +4607,9 @@ static void btusb_disconnect(struct usb_interface *intf) if (data->reset_gpio) gpiod_put(data->reset_gpio); + if (test_and_clear_bit(BTUSB_RESET, &data->flags)) + usb_autopm_put_interface_no_suspend(data->intf); + if (intf == data->intf) { if (data->isoc) usb_driver_release_interface(&btusb_driver, data->isoc); @@ -4565,11 +4635,26 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) BT_DBG("intf %p", intf); - /* Don't auto-suspend if there are connections or discovery in - * progress; external suspend calls shall never fail. + /* + * It is reported that remote wakeup events could sometimes cause some + * adapters completely unresponsive. Resetting the xHCI root hub doesn't + * help at all, and recovering from such a state needs a power cycle. + * Since disabling remote wakeup simply causes the USB core to gate + * runtime autosuspend as well due to needs_remote_wakeup == 1, let's do + * this ourselves to make our life easier. The interface can be safely + * autosuspended as long as remote wakeup is disabled, i.e., after + * closing the HCI device. + * + * Don't auto-suspend if there are connections or discovery in progress. + * + * External suspend calls shall never fail. Specifically, a device with + * broken remote wakeup may still take the advantage of remote wakeup in + * order to wake up the system from sleep if userspace has enabled it as + * a wakeup source. */ if (PMSG_IS_AUTO(message) && - (hci_conn_count(data->hdev) || hci_discovery_active(data->hdev))) + ((test_bit(BTUSB_WAKEUP_BROKEN, &data->flags) && data->intf->needs_remote_wakeup) || + hci_conn_count(data->hdev) || hci_discovery_active(data->hdev))) return -EBUSY; if (data->suspend_count++) diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index 516b8f74c434..5798a8db016e 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING, TASK_INTERRUPTIBLE, msecs_to_jiffies(2000)); - if (err == 1) { + if (err == -EINTR) { bt_dev_err(hdev, "Firmware load interrupted"); - err = -EINTR; break; } else if (err) { bt_dev_err(hdev, "Firmware request timeout"); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 62eecfa61646..019bcae81af5 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -426,7 +426,7 @@ static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size * Allocate buffer and the sg list. The sg list array is allocated * directly after the port_buffer struct. */ - buf = kmalloc_flex(*buf, sg, pages); + buf = kmalloc_flex(*buf, sg, pages, gfp); if (!buf) goto fail; @@ -1964,13 +1964,28 @@ static const struct file_operations portdev_fops = { static void remove_vqs(struct ports_device *portdev) { struct virtqueue *vq; + bool multiport = use_multiport(portdev); virtio_device_for_each_vq(portdev->vdev, vq) { struct port_buffer *buf; + unsigned int len; - flush_bufs(vq, true); - while ((buf = virtqueue_detach_unused_buf(vq))) - free_buf(buf, true); + /* + * c_ovq cookies are &portdev->cpkt, not port_buffer. + * Detach them but do not free_buf(). + */ + if (multiport && vq == portdev->c_ovq) { + spin_lock(&portdev->c_ovq_lock); + while (virtqueue_get_buf(vq, &len)) + ; + while (virtqueue_detach_unused_buf(vq)) + ; + spin_unlock(&portdev->c_ovq_lock); + } else { + flush_bufs(vq, true); + while ((buf = virtqueue_detach_unused_buf(vq))) + free_buf(buf, true); + } cond_resched(); } portdev->vdev->config->del_vqs(portdev->vdev); diff --git a/drivers/clk/ti/composite.c b/drivers/clk/ti/composite.c index 01eae8995254..83c3592cd179 100644 --- a/drivers/clk/ti/composite.c +++ b/drivers/clk/ti/composite.c @@ -248,7 +248,7 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, return -EINVAL; } - parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + parent_data = kzalloc_objs(*parent_data, num_parents); if (!parent_data) return -ENOMEM; diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 0fef60e82107..baf90a3fb49e 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -181,7 +181,7 @@ static void of_mux_clk_setup(struct device_node *node) pr_err("mux-clock %pOFn must have parents\n", node); return; } - parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + parent_data = kzalloc_objs(*parent_data, num_parents); if (!parent_data) return; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index d4ff8b228f86..8bfd46d60843 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1624,7 +1624,7 @@ static struct freq_attr **get_freq_attrs(void) /* amd_pstate_{max_freq, lowest_nonlinear_freq, highest_perf} should always be visible */ BUG_ON(!count); - attrs = kcalloc(count + 1, sizeof(struct freq_attr *), GFP_KERNEL); + attrs = kzalloc_objs(struct freq_attr *, count + 1); if (!attrs) return ERR_PTR(-ENOMEM); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0d0df986fa3d..96515880b4ac 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1249,7 +1249,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) if (!policy) return NULL; - if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) + if (!zalloc_cpumask_var(&policy->cpus, GFP_KERNEL)) goto err_free_policy; if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) @@ -1258,6 +1258,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL)) goto err_free_rcpumask; + init_rwsem(&policy->rwsem); + init_completion(&policy->kobj_unregister); ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, cpufreq_global_kobject, "policy%u", cpu); @@ -1272,8 +1274,6 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) goto err_free_real_cpus; } - init_rwsem(&policy->rwsem); - freq_constraints_init(&policy->constraints); policy->nb_min.notifier_call = cpufreq_notifier_min; diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index dcf20ea5ef5e..b250d0dde760 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -16,7 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/device/faux.h> +#include <linux/platform_device.h> #include <linux/psci.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> @@ -428,14 +428,14 @@ deinit: * to register cpuidle driver then rollback to cancel all CPUs * registration. */ -static int psci_cpuidle_probe(struct faux_device *fdev) +static int psci_cpuidle_probe(struct platform_device *pdev) { int cpu, ret; struct cpuidle_driver *drv; struct cpuidle_device *dev; for_each_present_cpu(cpu) { - ret = psci_idle_init_cpu(&fdev->dev, cpu); + ret = psci_idle_init_cpu(&pdev->dev, cpu); if (ret) goto out_fail; } @@ -455,36 +455,26 @@ out_fail: return ret; } -static struct faux_device_ops psci_cpuidle_ops = { +static struct platform_driver psci_cpuidle_driver = { .probe = psci_cpuidle_probe, + .driver = { + .name = "psci-cpuidle", + }, }; -static bool __init dt_idle_state_present(void) -{ - struct device_node *cpu_node __free(device_node) = - of_cpu_device_node_get(cpumask_first(cpu_possible_mask)); - if (!cpu_node) - return false; - - struct device_node *state_node __free(device_node) = - of_get_cpu_state_node(cpu_node, 0); - if (!state_node) - return false; - - return !!of_match_node(psci_idle_state_match, state_node); -} - static int __init psci_idle_init(void) { - struct faux_device *fdev; + struct platform_device *pdev; + int ret; - if (!dt_idle_state_present()) - return 0; + ret = platform_driver_register(&psci_cpuidle_driver); + if (ret) + return ret; - fdev = faux_device_create("psci-cpuidle", NULL, &psci_cpuidle_ops); - if (!fdev) { - pr_err("Failed to create psci-cpuidle device\n"); - return -ENODEV; + pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&psci_cpuidle_driver); + return PTR_ERR(pdev); } return 0; diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c index d292975cc468..ed41a90eeeb7 100644 --- a/drivers/cpuidle/dt_idle_genpd.c +++ b/drivers/cpuidle/dt_idle_genpd.c @@ -99,7 +99,7 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, if (!pd) goto out; - pd->name = kasprintf(GFP_KERNEL, "%pOF", np); + pd->name = kstrdup(kbasename(of_node_full_name(np)), GFP_KERNEL); if (!pd->name) goto free_pd; @@ -112,7 +112,6 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, goto free_name; pd->free_states = pd_free_states; - pd->name = kbasename(pd->name); pd->states = states; pd->state_count = state_count; diff --git a/drivers/crypto/inside-secure/eip93/eip93-common.c b/drivers/crypto/inside-secure/eip93/eip93-common.c index 4c163d7281b3..dacf586b2641 100644 --- a/drivers/crypto/inside-secure/eip93/eip93-common.c +++ b/drivers/crypto/inside-secure/eip93/eip93-common.c @@ -533,7 +533,7 @@ int eip93_send_req(struct crypto_async_request *async, memcpy(iv, reqiv, rctx->ivsize); - rctx->sa_state = kzalloc(sizeof(*rctx->sa_state), GFP_KERNEL); + rctx->sa_state = kzalloc_obj(*rctx->sa_state); if (!rctx->sa_state) return -ENOMEM; @@ -561,8 +561,7 @@ int eip93_send_req(struct crypto_async_request *async, iv[3] = 0xffffffff; crypto_inc((u8 *)iv, AES_BLOCK_SIZE); - rctx->sa_state_ctr = kzalloc(sizeof(*rctx->sa_state_ctr), - GFP_KERNEL); + rctx->sa_state_ctr = kzalloc_obj(*rctx->sa_state_ctr); if (!rctx->sa_state_ctr) { err = -ENOMEM; goto free_sa_state; diff --git a/drivers/crypto/intel/qat/qat_common/qat_comp_algs.c b/drivers/crypto/intel/qat/qat_common/qat_comp_algs.c index e0d003b50358..50e444da97e6 100644 --- a/drivers/crypto/intel/qat/qat_common/qat_comp_algs.c +++ b/drivers/crypto/intel/qat/qat_common/qat_comp_algs.c @@ -59,8 +59,7 @@ static void *qat_zstd_alloc_scratch(void) if (!scratch->literals) goto error; - scratch->out_seqs = kvcalloc(QAT_MAX_SEQUENCES, sizeof(ZSTD_Sequence), - GFP_KERNEL); + scratch->out_seqs = kvzalloc_objs(ZSTD_Sequence, QAT_MAX_SEQUENCES); if (!scratch->out_seqs) goto error; diff --git a/drivers/crypto/ti/dthev2-aes.c b/drivers/crypto/ti/dthev2-aes.c index eb5cd902dfb5..a3c069ca2baf 100644 --- a/drivers/crypto/ti/dthev2-aes.c +++ b/drivers/crypto/ti/dthev2-aes.c @@ -387,7 +387,7 @@ static int dthe_aes_run(struct crypto_engine *engine, void *areq) src_nents++; dst_nents++; - src = kmalloc_array(src_nents, sizeof(*src), GFP_ATOMIC); + src = kmalloc_objs(*src, src_nents, GFP_ATOMIC); if (!src) { ret = -ENOMEM; goto aes_ctr_src_alloc_err; @@ -399,7 +399,7 @@ static int dthe_aes_run(struct crypto_engine *engine, void *areq) sg_set_buf(sg, pad_buf, pad_size); if (diff_dst) { - dst = kmalloc_array(dst_nents, sizeof(*dst), GFP_ATOMIC); + dst = kmalloc_objs(*dst, dst_nents, GFP_ATOMIC); if (!dst) { ret = -ENOMEM; goto aes_ctr_dst_alloc_err; @@ -624,7 +624,7 @@ static struct scatterlist *dthe_aead_prep_aad(struct scatterlist *sg, if (assoclen % AES_BLOCK_SIZE) aad_nents++; - aad_sg = kmalloc_array(aad_nents, sizeof(struct scatterlist), GFP_ATOMIC); + aad_sg = kmalloc_objs(struct scatterlist, aad_nents, GFP_ATOMIC); if (!aad_sg) return ERR_PTR(-ENOMEM); @@ -680,7 +680,7 @@ static struct scatterlist *dthe_aead_prep_crypt(struct scatterlist *sg, if (cryptlen % AES_BLOCK_SIZE) crypt_nents++; - crypt_sg = kmalloc_array(crypt_nents, sizeof(struct scatterlist), GFP_ATOMIC); + crypt_sg = kmalloc_objs(struct scatterlist, crypt_nents, GFP_ATOMIC); if (!crypt_sg) { err = -ENOMEM; goto dthe_aead_prep_crypt_mem_err; diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c index e1f64b723082..ac55641b0259 100644 --- a/drivers/devfreq/hisi_uncore_freq.c +++ b/drivers/devfreq/hisi_uncore_freq.c @@ -474,7 +474,7 @@ static int hisi_uncore_mark_related_cpus(struct hisi_uncore_freq *uncore, return -EINVAL; len = rc; - u32 *num __free(kfree) = kcalloc(len, sizeof(*num), GFP_KERNEL); + u32 *num __free(kfree) = kzalloc_objs(*num, len); if (!num) return -ENOMEM; diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c index 2b53a9d277dc..20c50997a7cf 100644 --- a/drivers/dibs/dibs_main.c +++ b/drivers/dibs/dibs_main.c @@ -251,13 +251,19 @@ static int __init dibs_init(void) rc = class_register(&dibs_class); if (rc) - return rc; + goto err; rc = dibs_loopback_init(); if (rc) - pr_err("%s fails with %d\n", __func__, rc); + goto err_unregister; return rc; + +err_unregister: + class_unregister(&dibs_class); +err: + pr_err("%s fails with %d\n", __func__, rc); + return rc; } static void __exit dibs_exit(void) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d504c636dc29..4c9add51f9ef 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -804,6 +804,26 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); /** + * dma_buf_fd_install - install a reserved fd for a dma-buf + * @dmabuf: [in] pointer to dma_buf + * @fd: [in] fd reserved with get_unused_fd_flags() + * + * Publishes a previously reserved fd into the caller's fd table. + * Must only be called after all fallible work (e.g. copy_to_user) + * has succeeded, as it cannot be undone safely once called. + * + * The caller is responsible for having emitted the trace event + * (via dma_buf_fd() or get_unused_fd_flags() + this function) + * before calling this. + */ +void dma_buf_fd_install(struct dma_buf *dmabuf, int fd) +{ + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + fd_install(fd, dmabuf->file); +} +EXPORT_SYMBOL_NS_GPL(dma_buf_fd_install, "DMA_BUF"); + +/** * dma_buf_get - returns the struct dma_buf related to an fd * @fd: [in] fd associated with the struct dma_buf to be returned * diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index 3937dd41bb0f..8a6c2b6e0cc6 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -56,33 +56,6 @@ MODULE_PARM_DESC(mem_accounting, "Enable cgroup-based memory accounting for dma-buf heap allocations (default=false)."); EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP"); -static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, - u32 fd_flags, - u64 heap_flags) -{ - struct dma_buf *dmabuf; - int fd; - - /* - * Allocations from all heaps have to begin - * and end on page boundaries. - */ - len = PAGE_ALIGN(len); - if (!len) - return -EINVAL; - - dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags); - if (IS_ERR(dmabuf)) - return PTR_ERR(dmabuf); - - fd = dma_buf_fd(dmabuf, fd_flags); - if (fd < 0) { - dma_buf_put(dmabuf); - /* just return, as put will call release and that will free */ - } - return fd; -} - static int dma_heap_open(struct inode *inode, struct file *file) { struct dma_heap *heap; @@ -100,30 +73,42 @@ static int dma_heap_open(struct inode *inode, struct file *file) return 0; } -static long dma_heap_ioctl_allocate(struct file *file, void *data) +static struct dma_buf *dma_heap_ioctl_allocate(struct file *file, void *data) { struct dma_heap_allocation_data *heap_allocation = data; struct dma_heap *heap = file->private_data; + struct dma_buf *dmabuf; int fd; + size_t len; if (heap_allocation->fd) - return -EINVAL; + return ERR_PTR(-EINVAL); if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS) - return -EINVAL; + return ERR_PTR(-EINVAL); if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS) - return -EINVAL; + return ERR_PTR(-EINVAL); + + len = PAGE_ALIGN(heap_allocation->len); + if (!len) + return ERR_PTR(-EINVAL); + + dmabuf = heap->ops->allocate(heap, len, heap_allocation->fd_flags, + heap_allocation->heap_flags); - fd = dma_heap_buffer_alloc(heap, heap_allocation->len, - heap_allocation->fd_flags, - heap_allocation->heap_flags); - if (fd < 0) - return fd; + if (IS_ERR(dmabuf)) + return dmabuf; + + fd = get_unused_fd_flags(heap_allocation->fd_flags); + if (fd < 0) { + dma_buf_put(dmabuf); + return ERR_PTR(fd); + } heap_allocation->fd = fd; - return 0; + return dmabuf; } static unsigned int dma_heap_ioctl_cmds[] = { @@ -139,6 +124,8 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, unsigned int in_size, out_size, drv_size, ksize; int nr = _IOC_NR(ucmd); int ret = 0; + int fd; + struct dma_buf *dmabuf; if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds)) return -EINVAL; @@ -175,15 +162,28 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, switch (kcmd) { case DMA_HEAP_IOCTL_ALLOC: - ret = dma_heap_ioctl_allocate(file, kdata); + dmabuf = dma_heap_ioctl_allocate(file, kdata); + + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + break; + } + + fd = ((struct dma_heap_allocation_data *)kdata)->fd; + if (copy_to_user((void __user *)arg, kdata, out_size) != 0) { + put_unused_fd(fd); + dma_buf_put(dmabuf); + ret = -EFAULT; + } else { + dma_buf_fd_install(dmabuf, fd); + } + break; default: ret = -ENOTTY; goto err; } - if (copy_to_user((void __user *)arg, kdata, out_size) != 0) - ret = -EFAULT; err: if (kdata != stack_kdata) kfree(kdata); diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c index 856d0d302a5d..cb62e606d0a2 100644 --- a/drivers/dma-buf/st-dma-fence.c +++ b/drivers/dma-buf/st-dma-fence.c @@ -27,7 +27,7 @@ static struct dma_fence *mock_fence(void) { struct dma_fence *f; - f = kmalloc(sizeof(*f), GFP_KERNEL); + f = kmalloc_obj(*f); if (!f) return NULL; diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 4a9ab5822ffc..df6dd0046242 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -359,7 +359,7 @@ static long udmabuf_create(struct miscdevice *device, long ret = -EINVAL; u32 i, flags; - ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); + ubuf = kzalloc_obj(*ubuf); if (!ubuf) return -ENOMEM; @@ -387,7 +387,7 @@ static long udmabuf_create(struct miscdevice *device, if (ret) goto err; - folios = kvmalloc_array(max_nr_folios, sizeof(*folios), GFP_KERNEL); + folios = kvmalloc_objs(*folios, max_nr_folios); if (!folios) { ret = -ENOMEM; goto err; diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c index c133535d3765..c8e2169877e9 100644 --- a/drivers/dma/switchtec_dma.c +++ b/drivers/dma/switchtec_dma.c @@ -1056,7 +1056,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, int se_buf_len, irq, rc; struct dma_chan *chan; - swdma_chan = kzalloc_obj(*swdma_chan, GFP_KERNEL); + swdma_chan = kzalloc_obj(*swdma_chan); if (!swdma_chan) return -ENOMEM; @@ -1162,8 +1162,7 @@ static int switchtec_dma_chans_enumerate(struct switchtec_dma_dev *swdma_dev, struct dma_device *dma = &swdma_dev->dma_dev; int base, cnt, rc, i; - swdma_dev->swdma_chans = kcalloc(chan_cnt, sizeof(*swdma_dev->swdma_chans), - GFP_KERNEL); + swdma_dev->swdma_chans = kzalloc_objs(*swdma_dev->swdma_chans, chan_cnt); if (!swdma_dev->swdma_chans) return -ENOMEM; @@ -1222,7 +1221,7 @@ static int switchtec_dma_create(struct pci_dev *pdev) /* * Create the switchtec dma device */ - swdma_dev = kzalloc_obj(*swdma_dev, GFP_KERNEL); + swdma_dev = kzalloc_obj(*swdma_dev); if (!swdma_dev) return -ENOMEM; diff --git a/drivers/edac/al_mc_edac.c b/drivers/edac/al_mc_edac.c index 178b9e581a72..bf6921d8890b 100644 --- a/drivers/edac/al_mc_edac.c +++ b/drivers/edac/al_mc_edac.c @@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev) IRQF_SHARED, pdev->name, pdev); - if (ret != 0) { - dev_err(&pdev->dev, - "failed to request UE IRQ %d (%d)\n", - al_mc->irq_ue, ret); + if (ret != 0) return ret; - } } if (al_mc->irq_ce > 0) { @@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev) IRQF_SHARED, pdev->name, pdev); - if (ret != 0) { - dev_err(&pdev->dev, - "failed to request CE IRQ %d (%d)\n", - al_mc->irq_ce, ret); + if (ret != 0) return ret; - } } return 0; diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 4edd2088c2db..68846f583eee 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) int edac_idx, rc; struct device_node *np; const struct edac_device_prv_data *prv = &a10_sdmmceccb_data; + bool is_s10 = device->edac->is_s10; rc = altr_check_ecc_deps(device); if (rc) @@ -1533,7 +1534,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) altdev = dci->pvt_info; *altdev = *device; - if (!devres_open_group(&altdev->ddev, altr_portb_setup, GFP_KERNEL)) + if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) return -ENOMEM; /* Update PortB specific values */ @@ -1548,21 +1549,20 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) /* * Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly - * - * FIXME: Instead of ifdefs with different architectures the driver - * should properly use compatibles. */ -#ifdef CONFIG_64BIT - altdev->sb_irq = irq_of_parse_and_map(np, 1); -#else - altdev->sb_irq = irq_of_parse_and_map(np, 2); -#endif + + /* Using compatibles to determine the IRQ Index */ + if (is_s10) + altdev->sb_irq = irq_of_parse_and_map(np, 1); + else + altdev->sb_irq = irq_of_parse_and_map(np, 2); + if (!altdev->sb_irq) { edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n"); rc = -ENODEV; goto err_release_group_1; } - rc = devm_request_irq(&altdev->ddev, altdev->sb_irq, + rc = devm_request_irq(device->edac->dev, altdev->sb_irq, prv->ecc_irq_handler, IRQF_TRIGGER_HIGH, ecc_name, altdev); if (rc) { @@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) goto err_release_group_1; } -#ifdef CONFIG_64BIT - /* Use IRQ to determine SError origin instead of assigning IRQ */ - rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq); - if (rc) { - edac_printk(KERN_ERR, EDAC_DEVICE, - "Error PortB DBIRQ alloc\n"); - goto err_release_group_1; - } -#else - altdev->db_irq = irq_of_parse_and_map(np, 3); - if (!altdev->db_irq) { - edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n"); - rc = -ENODEV; - goto err_release_group_1; - } - rc = devm_request_irq(&altdev->ddev, altdev->db_irq, - prv->ecc_irq_handler, IRQF_TRIGGER_HIGH, - ecc_name, altdev); - if (rc) { - edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n"); - goto err_release_group_1; + if (is_s10) { + /* Use IRQ to determine SError origin instead of assigning IRQ */ + rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq); + if (rc) { + edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n"); + goto err_release_group_1; + } + } else { + altdev->db_irq = irq_of_parse_and_map(np, 3); + if (!altdev->db_irq) { + edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n"); + rc = -ENODEV; + goto err_release_group_1; + } + rc = devm_request_irq(device->edac->dev, altdev->db_irq, + prv->ecc_irq_handler, IRQF_TRIGGER_HIGH, + ecc_name, altdev); + if (rc) { + edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n"); + goto err_release_group_1; + } } -#endif rc = edac_device_add_device(dci); if (rc) { @@ -1605,13 +1604,13 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) list_add(&altdev->next, &altdev->edac->a10_ecc_devices); - devres_remove_group(&altdev->ddev, altr_portb_setup); + devres_remove_group(device->edac->dev, altr_portb_setup); return 0; err_release_group_1: edac_device_free_ctl_info(dci); - devres_release_group(&altdev->ddev, altr_portb_setup); + devres_release_group(device->edac->dev, altr_portb_setup); edac_printk(KERN_ERR, EDAC_DEVICE, "%s:Error setting up EDAC device: %d\n", ecc_name, rc); return rc; @@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, goto err_release_group1; } -#ifdef CONFIG_64BIT - /* Use IRQ to determine SError origin instead of assigning IRQ */ - rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq); - if (rc) { - edac_printk(KERN_ERR, EDAC_DEVICE, - "Unable to parse DB IRQ index\n"); - goto err_release_group1; - } -#else - altdev->db_irq = irq_of_parse_and_map(np, 1); - if (!altdev->db_irq) { - edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n"); - rc = -ENODEV; - goto err_release_group1; - } - rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler, - IRQF_TRIGGER_HIGH, - ecc_name, altdev); - if (rc) { - edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); - goto err_release_group1; + if (edac->is_s10) { + /* Use IRQ to determine SError origin instead of assigning IRQ */ + rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq); + if (rc) { + edac_printk(KERN_ERR, EDAC_DEVICE, + "Unable to parse DB IRQ index\n"); + goto err_release_group1; + } + } else { + altdev->db_irq = irq_of_parse_and_map(np, 1); + if (!altdev->db_irq) { + edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n"); + rc = -ENODEV; + goto err_release_group1; + } + rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler, + IRQF_TRIGGER_HIGH, + ecc_name, altdev); + if (rc) { + edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); + goto err_release_group1; + } } -#endif rc = edac_device_add_device(dci); if (rc) { @@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = { /************** Stratix 10 EDAC Double Bit Error Handler ************/ #define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m) -#ifdef CONFIG_64BIT /* panic routine issues reboot on non-zero panic_timeout */ extern int panic_timeout; @@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this, return NOTIFY_DONE; } -#endif /****************** Arria 10 EDAC Probe Function *********************/ static int altr_edac_a10_probe(struct platform_device *pdev) @@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev) platform_set_drvdata(pdev, edac); INIT_LIST_HEAD(&edac->a10_ecc_devices); + edac->is_s10 = !!device_get_match_data(&pdev->dev); + edac->ecc_mgr_map = altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node, "altr,sysmgr-syscon"); @@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev) irq_set_chained_handler_and_data(edac->sb_irq, altr_edac_a10_irq_handler, edac); - -#ifdef CONFIG_64BIT + if (edac->is_s10) { int dberror, err_addr; @@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev) regmap_write(edac->ecc_mgr_map, S10_SYSMGR_UE_ADDR_OFST, 0); } - } -#else - edac->db_irq = platform_get_irq(pdev, 1); - if (edac->db_irq < 0) - return edac->db_irq; + } else { + edac->db_irq = platform_get_irq(pdev, 1); + if (edac->db_irq < 0) + return edac->db_irq; - irq_set_chained_handler_and_data(edac->db_irq, - altr_edac_a10_irq_handler, edac); -#endif + irq_set_chained_handler_and_data(edac->db_irq, + altr_edac_a10_irq_handler, edac); + } for_each_child_of_node(pdev->dev.of_node, child) { if (!of_device_is_available(child)) @@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev) static const struct of_device_id altr_edac_a10_of_match[] = { { .compatible = "altr,socfpga-a10-ecc-manager" }, - { .compatible = "altr,socfpga-s10-ecc-manager" }, + { .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 }, {}, }; MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match); diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index f3e84172caa9..9387056fd65e 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h @@ -394,6 +394,7 @@ struct altr_arria10_edac { struct irq_chip irq_chip; struct list_head a10_ecc_devices; struct notifier_block panic_notifier; + bool is_s10; }; #endif /* #ifndef _ALTERA_EDAC_H */ diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index c6aa69dbd9fb..475235c402e8 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void) goto err_pci; } + request_module_nowait("amd_atl"); + /* register stuff with EDAC MCE */ if (boot_cpu_data.x86 >= 0x17) { amd_register_ecc_decoder(decode_umc_error); diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index dadb8acbee3d..6e069b255595 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev) rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH, DRV_NAME, ctx); - if (rc) { - dev_err(&pdev->dev, "unable to request irq %d\n", irq); + if (rc) return rc; - } /* enable interrupts */ regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c index 8195fc9c9354..447d0c620082 100644 --- a/drivers/edac/debugfs.c +++ b/drivers/edac/debugfs.c @@ -1,50 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <linux/string_choices.h> - #include "edac_module.h" static struct dentry *edac_debugfs; -static ssize_t edac_fake_inject_write(struct file *file, - const char __user *data, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - struct mem_ctl_info *mci = to_mci(dev); - static enum hw_event_mc_err_type type; - u16 errcount = mci->fake_inject_count; - - if (!errcount) - errcount = 1; - - type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED - : HW_EVENT_ERR_CORRECTED; - - printk(KERN_DEBUG - "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", - errcount, - (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", - str_plural(errcount), - mci->fake_inject_layer[0], - mci->fake_inject_layer[1], - mci->fake_inject_layer[2] - ); - edac_mc_handle_error(type, mci, errcount, 0, 0, 0, - mci->fake_inject_layer[0], - mci->fake_inject_layer[1], - mci->fake_inject_layer[2], - "FAKE ERROR", "for EDAC testing only"); - - return count; -} - -static const struct file_operations debug_fake_inject_fops = { - .open = simple_open, - .write = edac_fake_inject_write, - .llseek = generic_file_llseek, -}; - void __init edac_debugfs_init(void) { edac_debugfs = debugfs_create_dir("edac", NULL); @@ -57,29 +16,7 @@ void edac_debugfs_exit(void) void edac_create_debugfs_nodes(struct mem_ctl_info *mci) { - struct dentry *parent; - char name[80]; - int i; - - parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); - - for (i = 0; i < mci->n_layers; i++) { - sprintf(name, "fake_inject_%s", - edac_layer_name[mci->layers[i].type]); - debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_layer[i]); - } - - debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_ue); - - debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, - &mci->fake_inject_count); - - debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev, - &debug_fake_inject_fops); - - mci->debugfs = parent; + mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs); } /* Create a toplevel dir under EDAC's debugfs hierarchy */ diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index cf0d3c2dfc04..638be1f47c59 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev) } /* - * edac_device_reset_delay_period - * - * need to stop any outstanding workq queued up at this time - * because we will be resetting the sleep time. - * Then restart the workq on the new delay + * Stop any outstanding workq queued up at this time because sleep time will + * be reset. Then restart the workq on the new delay. */ -void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, - unsigned long msec) +void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec) { edac_dev->poll_msec = msec; edac_dev->delay = msecs_to_jiffies(msec); diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c index b1c2717cd023..6359007701ba 100644 --- a/drivers/edac/edac_device_sysfs.c +++ b/drivers/edac/edac_device_sysfs.c @@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info return sprintf(data, "%u\n", ctl_info->poll_msec); } -static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info - *ctl_info, const char *data, - size_t count) +static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info, + const char *data, size_t count) { - unsigned long value; + unsigned int value; + int ret; - /* get the value and enforce that it is non-zero, must be at least - * one millisecond for the delay period, between scans - * Then cancel last outstanding delay for the work request - * and set a new one. + /* + * Get the value, make sure it is non-zero, must be at least one millisecond + * for the delay period between scans. */ - value = simple_strtoul(data, NULL, 0); + ret = kstrtouint(data, 0, &value); + if (ret < 0) + return ret; + + if (value < 1) + return -EINVAL; + edac_device_reset_delay_period(ctl_info, value); return count; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index c2ed6c696e54..9b4b5582fa9f 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev, ssize_t count; count = edac_dimm_info_location(dimm, data, PAGE_SIZE); - count += scnprintf(data + count, PAGE_SIZE - count, "\n"); + count += sysfs_emit_at(data, count, "\n"); return count; } diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 47593afdc234..eceef5539186 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay); bool edac_stop_work(struct delayed_work *work); bool edac_mod_work(struct delayed_work *work, unsigned long delay); -extern void edac_device_reset_delay_period(struct edac_device_ctl_info - *edac_dev, unsigned long msec); +extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec); extern void edac_mc_reset_delay_period(unsigned long value); /* diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c index a8879d72d064..68d16cc8298d 100644 --- a/drivers/edac/highbank_mc_edac.c +++ b/drivers/edac/highbank_mc_edac.c @@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler, 0, dev_name(&pdev->dev), mci); - if (res < 0) { - dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + if (res < 0) goto err2; - } devres_close_group(&pdev->dev, NULL); return 0; diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c index 94ab80197c2e..6f5fdf43e773 100644 --- a/drivers/edac/ie31200_edac.c +++ b/drivers/edac/ie31200_edac.c @@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm, { dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity; dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1; - dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8; + + switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) { + case 0: + dd->dtype = DEV_X8; + break; + case 1: + dd->dtype = DEV_X16; + break; + case 2: + dd->dtype = DEV_X32; + break; + case 3: + dd->dtype = DEV_X64; + break; + default: + dd->dtype = DEV_UNKNOWN; + } } static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window, diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c index f1fc20d4ebf6..776c5db2f598 100644 --- a/drivers/edac/igen6_edac.c +++ b/drivers/edac/igen6_edac.c @@ -42,7 +42,8 @@ #define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) -#define NUM_IMC 2 /* Max memory controllers */ +/* Probing upper bound, not a hardware capability limit. */ +#define MAX_IMC_TO_PROBE 8 #define NUM_CHANNELS 2 /* Max channels */ #define NUM_DIMMS 2 /* Max DIMMs per channel */ @@ -122,6 +123,43 @@ #define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) #define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) +/* + * A slice represents a portion of memory space participating in an + * interleave relationship within the memory hierarchy. + * + * It can represent in different levels such as: + * + * - a pair of memory controllers + * - a memory controller + * - a memory channel + * - a memory sub-channel / DIMM + * + * +--------+ + * | | + * | Zone 1 | + * | | + * +--------+ +--------+ + * | | | | + * | | | | + * | Zone 0 | | Zone 0 | + * | | | | + * | | | | + * +--------+ +--------+ + * + * Slice L Slice S + * + * Memory space is divided into: + * + * - Zone 0 : Interleaved region + * - Zone 1 : Non-interleaved region (upper part of the large slice). + */ +struct slice { + /* Slice address. */ + u64 addr; + /* Slice that @addr belongs to. */ + int id; +}; + struct igen6_imc { int mc; struct mem_ctl_info *mci; @@ -175,8 +213,6 @@ static struct res_config { /* Set imc->dimm_{l_size,s_size,l_map}[chan]. */ void (*set_dimm_params)(struct igen6_imc *imc, int chan); bool (*ibecc_available)(struct pci_dev *pdev); - /* Extract error address logged in IBECC */ - u64 (*err_addr)(u64 ecclog); /* Convert error address logged in IBECC to system physical address */ u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc); /* Convert error address logged in IBECC to integrated memory controller address */ @@ -184,11 +220,11 @@ static struct res_config { } *res_cfg; static struct igen6_pvt { - struct igen6_imc imc[NUM_IMC]; void __iomem *memss_pma_cr; u64 ms_hash; u64 ms_s_size; int ms_l_map; + struct igen6_imc imc[]; } *igen6_pvt; /* The top of low usable DRAM */ @@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE]; static struct irq_work ecclog_irq_work; static struct work_struct ecclog_work; -/* Compute die IDs for Elkhart Lake with IBECC */ +/* SoC compute die IDs with IBECC capability. */ +/* Elkhart Lake */ #define DID_EHL_SKU5 0x4514 #define DID_EHL_SKU6 0x4528 #define DID_EHL_SKU7 0x452a @@ -238,22 +275,22 @@ static struct work_struct ecclog_work; #define DID_EHL_SKU14 0x4534 #define DID_EHL_SKU15 0x4536 -/* Compute die IDs for ICL-NNPI with IBECC */ +/* ICL-NNPI */ #define DID_ICL_SKU8 0x4581 #define DID_ICL_SKU10 0x4585 #define DID_ICL_SKU11 0x4589 #define DID_ICL_SKU12 0x458d -/* Compute die IDs for Tiger Lake with IBECC */ +/* Tiger Lake */ #define DID_TGL_SKU 0x9a14 -/* Compute die IDs for Alder Lake with IBECC */ +/* Alder Lake */ #define DID_ADL_SKU1 0x4601 #define DID_ADL_SKU2 0x4602 #define DID_ADL_SKU3 0x4621 #define DID_ADL_SKU4 0x4641 -/* Compute die IDs for Alder Lake-N with IBECC */ +/* Alder Lake-N */ #define DID_ADL_N_SKU1 0x4614 #define DID_ADL_N_SKU2 0x4617 #define DID_ADL_N_SKU3 0x461b @@ -267,38 +304,38 @@ static struct work_struct ecclog_work; #define DID_ADL_N_SKU11 0x467c #define DID_ADL_N_SKU12 0x4632 -/* Compute die IDs for Arizona Beach with IBECC */ +/* Arizona Beach */ #define DID_AZB_SKU1 0x4676 -/* Compute did IDs for Amston Lake with IBECC */ +/* Amston Lake */ #define DID_ASL_SKU1 0x464a #define DID_ASL_SKU2 0x4646 #define DID_ASL_SKU3 0x4652 -/* Compute die IDs for Raptor Lake-P with IBECC */ +/* Raptor Lake-P */ #define DID_RPL_P_SKU1 0xa706 #define DID_RPL_P_SKU2 0xa707 #define DID_RPL_P_SKU3 0xa708 #define DID_RPL_P_SKU4 0xa716 #define DID_RPL_P_SKU5 0xa718 -/* Compute die IDs for Meteor Lake-PS with IBECC */ +/* Meteor Lake-PS */ #define DID_MTL_PS_SKU1 0x7d21 #define DID_MTL_PS_SKU2 0x7d22 #define DID_MTL_PS_SKU3 0x7d23 #define DID_MTL_PS_SKU4 0x7d24 -/* Compute die IDs for Meteor Lake-P with IBECC */ +/* Meteor Lake-P */ #define DID_MTL_P_SKU1 0x7d01 #define DID_MTL_P_SKU2 0x7d02 #define DID_MTL_P_SKU3 0x7d14 -/* Compute die IDs for Arrow Lake-UH with IBECC */ +/* Arrow Lake-UH */ #define DID_ARL_UH_SKU1 0x7d06 #define DID_ARL_UH_SKU2 0x7d20 #define DID_ARL_UH_SKU3 0x7d30 -/* Compute die IDs for Panther Lake-H with IBECC */ +/* Panther Lake-H */ #define DID_PTL_H_SKU1 0xb000 #define DID_PTL_H_SKU2 0xb001 #define DID_PTL_H_SKU3 0xb002 @@ -314,15 +351,114 @@ static struct work_struct ecclog_work; #define DID_PTL_H_SKU13 0xb02a #define DID_PTL_H_SKU14 0xb00a -/* Compute die IDs for Wildcat Lake with IBECC */ +/* Starfire */ +#define DID_STF_SKU1 0xb02b + +/* Wildcat Lake */ #define DID_WCL_SKU1 0xfd00 -/* Compute die IDs for Nova Lake-H/HX with IBECC */ +/* Nova Lake-H/HX */ #define DID_NVL_H_SKU1 0xd701 #define DID_NVL_H_SKU2 0xd702 #define DID_NVL_H_SKU3 0xd704 #define DID_NVL_H_SKU4 0xd705 +/* Remove the interleave bit and shift upper part down to fill gap. */ +static u64 squeeze_addr(u64 addr, int intlv_bit) +{ + u64 slice_addr; + + slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit; + slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1); + + return slice_addr; +} + +/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */ +static u64 inflate_addr(u64 addr, int intlv_bit) +{ + u64 inflated_addr; + + /* Insert a zero at @intlv_bit position. */ + inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1); + inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1); + + return inflated_addr; +} + +static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit) +{ + u64 hash_addr; + int i; + + /* + * In hash mode, @intlv_bit is the lowest selected bit of @addr + * to be XORed. While @mask may or may not include this @intlv_bit, + * we enforce that @mask includes @intlv_bit to ensure @intlv_bit is + * XORed exactly once. + */ + hash_mask |= BIT_ULL(intlv_bit); + hash_addr = addr & hash_mask; + + for (i = 6; i < 20; i++) + hash_base ^= (hash_addr >> i) & 1; + + return hash_base; +} + +/* + * Converts a higher-level address (system / IMC / channel) into a lower-level + * slice address and identifier. + */ +static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base, + int intlv_bit, u64 s_size, int l_map, + struct slice *slice) +{ + /* In non-interleave zone. */ + if (addr >= 2 * s_size) { + slice->addr = addr - s_size; + slice->id = l_map; + return; + } + + /* In interleave zone. */ + slice->addr = squeeze_addr(addr, intlv_bit); + + /* Non-hash mode. */ + if (!hash_mask) { + slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit); + return; + } + + /* Hash mode. */ + slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit); +} + +/* Reconstruct address for upper memory hierarchy level. */ +static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base, + int intlv_bit, u64 s_size) +{ + u64 inflated_addr, hash_val; + + /* In non-interleave zone. */ + if (addr >= s_size) + return addr + s_size; + + /* + * In interleave zone. + * + * Insert a zero at @intlv_bit position. + */ + inflated_addr = inflate_addr(addr, intlv_bit); + + /* + * Reconstruct the removed interleave bit and use it to replace + * the zero at @intlv_bit position. + */ + hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit); + return inflated_addr | (hash_val << intlv_bit); +} + static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) { union { @@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) return 0; } +/* Check whether the memory controller is absent. */ +static bool imc_absent(void __iomem *window) +{ + return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0; +} + +/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */ +static void __iomem *map_imc_window(u64 mchbar, int pmc) +{ + void __iomem *window; + + window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE); + if (!window) + return NULL; + + if (imc_absent(window)) { + iounmap(window); + return NULL; + } + + return window; +} + +/* Return the number of present memory controllers. */ +static int get_imc_num(u64 mchbar) +{ + void __iomem *window; + int lmc, pmc; + + for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) { + window = map_imc_window(mchbar, pmc); + if (window) { + iounmap(window); + lmc++; + } + } + + return lmc; +} + static bool ehl_ibecc_available(struct pci_dev *pdev) { u32 v; @@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr) return maddr; } -static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit) -{ - u64 hash_addr = addr & mask, hash = hash_init; - u64 intlv = (addr >> intlv_bit) & 1; - int i; - - for (i = 6; i < 20; i++) - hash ^= (hash_addr >> i) & 1; - - return hash ^ intlv; -} - static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc) { - u64 maddr, hash, mask, ms_s_size; + u64 mask, ms_s_size; int intlv_bit; u32 ms_hash; @@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc) mask = MEM_SLICE_HASH_MASK(ms_hash); intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6; - maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) | - GET_BITFIELD(eaddr, 0, intlv_bit - 1); - - hash = mem_slice_hash(maddr, mask, mc, intlv_bit); - - return maddr | (hash << intlv_bit); + return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size); } static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc) @@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc) static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc) { - u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size; + u64 ms_s_size = igen6_pvt->ms_s_size; struct igen6_imc *imc = &igen6_pvt->imc[mc]; + struct slice slice; int intlv_bit; u32 mc_hash; @@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc) intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6; - imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit | - GET_BITFIELD(eaddr, 0, intlv_bit - 1); - - return imc_addr; -} - -static u64 rpl_p_err_addr(u64 ecclog) -{ - return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog); + translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice); + return slice.addr; } static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc) @@ -686,6 +839,7 @@ static struct res_config tgl_cfg = { .err_addr_to_imc_addr = tgl_err_addr_to_imc_addr, }; +/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */ static struct res_config adl_cfg = { .machine_check = true, .num_imc = 2, @@ -701,37 +855,6 @@ static struct res_config adl_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static struct res_config adl_n_cfg = { - .machine_check = true, - .num_imc = 1, - .reg_mchbar_mask = GENMASK_ULL(41, 17), - .reg_tom_mask = GENMASK_ULL(41, 20), - .reg_touud_mask = GENMASK_ULL(41, 20), - .reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5), - .imc_base = 0xd800, - .ibecc_base = 0xd400, - .ibecc_error_log_offset = 0x68, - .ibecc_available = tgl_ibecc_available, - .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, - .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, -}; - -static struct res_config rpl_p_cfg = { - .machine_check = true, - .num_imc = 2, - .reg_mchbar_mask = GENMASK_ULL(41, 17), - .reg_tom_mask = GENMASK_ULL(41, 20), - .reg_touud_mask = GENMASK_ULL(41, 20), - .reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5), - .imc_base = 0xd800, - .ibecc_base = 0xd400, - .ibecc_error_log_offset = 0x68, - .ibecc_available = tgl_ibecc_available, - .err_addr = rpl_p_err_addr, - .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, - .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, -}; - static struct res_config mtl_ps_cfg = { .machine_check = true, .num_imc = 2, @@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; +/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */ static struct res_config mtl_p_cfg = { .machine_check = true, .num_imc = 2, @@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; +/* Shared by Panther Lake-H and Starfire */ static struct res_config ptl_h_cfg = { .machine_check = true, .num_imc = 2, @@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = { .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, }; -static struct res_config wcl_cfg = { - .machine_check = true, - .num_imc = 1, - .reg_mchbar_mask = GENMASK_ULL(41, 17), - .reg_tom_mask = GENMASK_ULL(41, 20), - .reg_touud_mask = GENMASK_ULL(41, 20), - .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5), - .imc_base = 0xd800, - .ibecc_base = 0xd400, - .ibecc_error_log_offset = 0x170, - .ibecc_available = mtl_p_ibecc_available, - .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, - .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, -}; - static struct res_config nvl_h_cfg = { .machine_check = true, .num_imc = 2, @@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg }, { PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg }, { PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg }, { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, { PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg }, @@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, @@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = { { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, - { PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg }, + { PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg }, { PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, { PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, { PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg }, @@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan) imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val); } -static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit) -{ - u64 hash_addr = addr & mask, hash = 0; - u64 intlv = (addr >> intlv_bit) & 1; - int i; - - for (i = 6; i < 20; i++) - hash ^= (hash_addr >> i) & 1; - - return (int)hash ^ intlv; -} - -static u64 decode_channel_addr(u64 addr, int intlv_bit) -{ - u64 channel_addr; - - /* Remove the interleave bit and shift upper part down to fill gap */ - channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit; - channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1); - - return channel_addr; -} - -static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map, - int *idx, u64 *sub_addr) -{ - int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6; - - if (addr > 2 * s_size) { - *sub_addr = addr - s_size; - *idx = l_map; - return; - } - - if (CHANNEL_HASH_MODE(hash)) { - *sub_addr = decode_channel_addr(addr, intlv_bit); - *idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit); - } else { - *sub_addr = decode_channel_addr(addr, 6); - *idx = GET_BITFIELD(addr, 6, 6); - } -} - static int igen6_decode(struct decoded_addr *res) { struct igen6_imc *imc = &igen6_pvt->imc[res->mc]; - u64 addr = res->imc_addr, sub_addr, s_size; - int idx, l_map; - u32 hash; + u64 addr = res->imc_addr, s_size; + int intlv_bit, l_map; + u32 hash, hash_mask; + struct slice slice; if (addr >= igen6_tom) { edac_dbg(0, "Address 0x%llx out of range\n", addr); @@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res) hash = readl(imc->window + CHANNEL_HASH_OFFSET); s_size = imc->ch_s_size; l_map = imc->ch_l_map; - decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr); - res->channel_idx = idx; - res->channel_addr = sub_addr; + hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0; + intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6; + + translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice); + + res->channel_idx = slice.id; + res->channel_addr = slice.addr; /* Decode sub-channel/DIMM */ hash = readl(imc->window + CHANNEL_EHASH_OFFSET); - s_size = imc->dimm_s_size[idx]; - l_map = imc->dimm_l_map[idx]; - decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr); - res->sub_channel_idx = idx; - res->sub_channel_addr = sub_addr; + s_size = imc->dimm_s_size[res->channel_idx]; + l_map = imc->dimm_l_map[res->channel_idx]; + hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0; + intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6; + + translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice); + + res->sub_channel_idx = slice.id; + res->sub_channel_addr = slice.addr; return 0; } @@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work) llist_for_each_entry_safe(node, tmp, head, llnode) { memset(&res, 0, sizeof(res)); - if (res_cfg->err_addr) - eaddr = res_cfg->err_addr(node->ecclog); - else - eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask; - + eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask; res.mc = node->mc; res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc); res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc); @@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev) { void __iomem *memss_pma_cr; struct igen6_pvt *pvt; + int imc_num, rc; u64 mchbar; - int rc; - pvt = kzalloc_obj(*igen6_pvt); - if (!pvt) + rc = get_mchbar(pdev, &mchbar); + if (rc) return NULL; - rc = get_mchbar(pdev, &mchbar); - if (rc) { - kfree(pvt); + imc_num = get_imc_num(mchbar); + if (!imc_num) { + igen6_printk(KERN_ERR, "No mc found.\n"); return NULL; } + edac_dbg(2, "%d mcs found.\n", imc_num); + + /* Use the runtime detected IMC count. */ + if (res_cfg->num_imc != imc_num) + res_cfg->num_imc = imc_num; + + pvt = kzalloc_flex(*pvt, imc, imc_num); + if (!pvt) + return NULL; memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2); if (!memss_pma_cr) { @@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci) irq_work_queue(&ecclog_irq_work); } -/* Check whether the memory controller is absent. */ -static bool igen6_imc_absent(void __iomem *window) -{ - return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0; -} - static void imc_release(struct device *dev) { /* Nothing to do, the 'imc' owns the 'dev' and will also release it. */ @@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar) { void __iomem *window; int lmc, pmc, rc; - u64 base; - - for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) { - base = mchbar + pmc * MCHBAR_SIZE; - window = ioremap(base, MCHBAR_SIZE); - if (!window) { - igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc); - rc = -ENOMEM; - goto out_unregister_mcis; - } - if (igen6_imc_absent(window)) { - iounmap(window); - edac_dbg(2, "Skip absent mc%d\n", pmc); + for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) { + window = map_imc_window(mchbar, pmc); + if (!window) continue; - } rc = igen6_register_mci(lmc, window, pdev); if (rc) - goto out_iounmap; + goto err_unregister; /* Done, if all present MCs are detected and registered. */ if (++lmc >= res_cfg->num_imc) @@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar) return 0; -out_iounmap: +err_unregister: iounmap(window); - -out_unregister_mcis: igen6_unregister_mcis(); return rc; diff --git a/drivers/edac/thunderx_edac.c b/drivers/edac/thunderx_edac.c index 75c04dfc3962..9c0a1e48f96f 100644 --- a/drivers/edac/thunderx_edac.c +++ b/drivers/edac/thunderx_edac.c @@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev, thunderx_lmc_err_isr, thunderx_lmc_threaded_isr, 0, "[EDAC] ThunderX LMC", mci); - if (ret) { - dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret); + if (ret) goto err_free; - } lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0)); diff --git a/drivers/edac/versalnet_edac.c b/drivers/edac/versalnet_edac.c index 97ec05d68bbb..9e65c4b1d99d 100644 --- a/drivers/edac/versalnet_edac.c +++ b/drivers/edac/versalnet_edac.c @@ -813,7 +813,7 @@ static int init_one_mc(struct mc_priv *priv, struct platform_device *pdev, int i layers[1].is_virt_csrow = false; rc = -ENOMEM; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return rc; diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c index 9955396c9a52..62b8166dc287 100644 --- a/drivers/edac/xgene_edac.c +++ b/drivers/edac/xgene_edac.c @@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev) rc = devm_request_irq(&pdev->dev, irq, xgene_edac_isr, IRQF_SHARED, dev_name(&pdev->dev), edac); - if (rc) { - dev_err(&pdev->dev, - "Could not request IRQ %d\n", irq); + if (rc) goto out_err; - } } } diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ef29fd223287..922777e86d58 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1792,7 +1792,7 @@ static void *scmi_iterator_init(const struct scmi_protocol_handle *ph, { int ret; - struct scmi_iterator *i __free(kfree) = kzalloc(sizeof(*i), GFP_KERNEL); + struct scmi_iterator *i __free(kfree) = kzalloc_obj(*i); if (!i) return ERR_PTR(-ENOMEM); diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 0fd9581275f1..f926c1f64822 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -169,7 +169,7 @@ static int qcom_tzmem_init_area(struct qcom_tzmem_area *area) { int ret; - u64 *handle __free(kfree) = kzalloc(sizeof(*handle), GFP_KERNEL); + u64 *handle __free(kfree) = kzalloc_obj(*handle); if (!handle) return -ENOMEM; @@ -197,8 +197,7 @@ static int qcom_tzmem_pool_add_memory(struct qcom_tzmem_pool *pool, { int ret; - struct qcom_tzmem_area *area __free(kfree) = kzalloc(sizeof(*area), - gfp); + struct qcom_tzmem_area *area __free(kfree) = kzalloc_obj(*area, gfp); if (!area) return -ENOMEM; @@ -415,8 +414,7 @@ void *qcom_tzmem_alloc(struct qcom_tzmem_pool *pool, size_t size, gfp_t gfp) size = PAGE_ALIGN(size); - struct qcom_tzmem_chunk *chunk __free(kfree) = kzalloc(sizeof(*chunk), - gfp); + struct qcom_tzmem_chunk *chunk __free(kfree) = kzalloc_obj(*chunk, gfp); if (!chunk) return NULL; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index cc747ab0237f..33d7c4a4181e 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2386,7 +2386,7 @@ static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params, if (ret || !(info->fw_caps & MSG_FLAG_CAPS_LPM_IRQ_CONTEXT_LOST)) goto end; - irq = kzalloc_obj(*irq, GFP_KERNEL); + irq = kzalloc_obj(*irq); if (!irq) { ti_sci_manage_irq(handle, valid_params, src_id, src_index, dst_id, dst_host_irq, ia_id, vint, diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c index 87652d58d03f..1b3a1af8658d 100644 --- a/drivers/fpga/dfl-afu-dma-region.c +++ b/drivers/fpga/dfl-afu-dma-region.c @@ -316,7 +316,7 @@ int afu_dma_map_region(struct dfl_feature_dev_data *fdata, if (user_addr + length < user_addr) return -EINVAL; - region = kzalloc(sizeof(*region), GFP_KERNEL); + region = kzalloc_obj(*region); if (!region) return -ENOMEM; diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 5ce89f52b4b5..b0b65416d74d 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -886,8 +886,8 @@ gpio_aggregator_make_device_sw_node(struct gpio_aggregator *aggr) if (num_lines == 0) return NULL; - const char **line_names __free(kfree) = kcalloc( - num_lines, sizeof(*line_names), GFP_KERNEL); + const char **line_names __free(kfree) = kzalloc_objs(*line_names, + num_lines); if (!line_names) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index a859deab2bca..30e8009b5fcc 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -518,7 +518,7 @@ static void gpio_mpsse_irq_enable(struct irq_data *irqd) * Can't be devm because it uses a non-raw spinlock (illegal in * this context, where a raw spinlock is held by our caller) */ - worker = kzalloc(sizeof(*worker), GFP_NOWAIT); + worker = kzalloc_obj(*worker, GFP_NOWAIT); if (!worker) return; diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index ef1b779e8ea6..0c73bec04267 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -790,8 +790,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, line_names_size = gpio_sim_get_line_names_size(bank); if (line_names_size) { - line_names = kcalloc(line_names_size, sizeof(*line_names), - GFP_KERNEL); + line_names = kzalloc_objs(*line_names, line_names_size); if (!line_names) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 7d0d366be37a..70ddccccc1c4 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -692,7 +692,8 @@ static int gpio_virtuser_interrupts_set(void *data, u64 val) atomic_set(&ld->irq, irq); } else { irq = atomic_xchg(&ld->irq, 0); - free_irq(irq, ld); + if (irq) + free_irq(irq, ld); } return 0; @@ -1429,8 +1430,7 @@ gpio_virtuser_make_device_swnode(struct gpio_virtuser_device *dev) memset(properties, 0, sizeof(properties)); num_ids = list_count_nodes(&dev->lookup_list); - char **ids __free(kfree) = kcalloc(num_ids + 1, sizeof(*ids), - GFP_KERNEL); + char **ids __free(kfree) = kzalloc_objs(*ids, num_ids + 1); if (!ids) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 9f3b628d5793..d1105b7ae437 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2653,7 +2653,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + cdev = kzalloc_obj(*cdev); if (!cdev) return -ENOMEM; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 940b566946ce..f36e4b171fa7 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -788,13 +788,13 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action, if (!of_property_read_bool(rd->dn, "gpio-hog")) return NOTIFY_DONE; /* not for us */ - if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) - return NOTIFY_DONE; - gdev = of_find_gpio_device_by_node(rd->dn->parent); if (!gdev) return NOTIFY_DONE; /* not for us */ + if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) + return NOTIFY_DONE; + ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn)); if (ret < 0) { pr_err("%s: failed to add hogs for %pOF\n", __func__, diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c index 495bd3d0ddf0..5f9623e40b0f 100644 --- a/drivers/gpio/gpiolib-shared.c +++ b/drivers/gpio/gpiolib-shared.c @@ -261,10 +261,13 @@ static int gpio_shared_of_traverse(struct device_node *curr) con_id[con_id_len - suffix_len] = '\0'; } - ref = gpio_shared_make_ref(fwnode_handle_get(of_fwnode_handle(curr)), - con_id, args.args[1]); - if (!ref) + struct fwnode_handle *curr_fwnode = + fwnode_handle_get(of_fwnode_handle(curr)); + ref = gpio_shared_make_ref(curr_fwnode, con_id, args.args[1]); + if (!ref) { + fwnode_handle_put(curr_fwnode); return -ENOMEM; + } if (!list_empty(&entry->refs)) pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n", diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index ef8ccaf17c9c..66d2325bfae8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1178,7 +1178,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int base = 0; int ret; - gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); + gdev = kzalloc_obj(*gdev); if (!gdev) return -ENOMEM; gc->gpiodev = gdev; @@ -1218,7 +1218,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, goto err_put_device; gdev->ngpio = gc->ngpio; - gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); + gdev->descs = kzalloc_objs(*gdev->descs, gc->ngpio); if (!gdev->descs) { ret = -ENOMEM; goto err_put_device; diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index a5553fcec28c..26e7a48b55f3 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -411,16 +411,13 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) if (!mm->used_scoreboard) goto out_free_free_scoreboard; - mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, - sizeof(*mm->free_trees), - GFP_KERNEL); + mm->free_trees = kmalloc_objs(*mm->free_trees, GPU_BUDDY_MAX_FREE_TREES); if (!mm->free_trees) goto out_free_used_scoreboard; for_each_free_tree(i) { - mm->free_trees[i] = kmalloc_array(mm->max_order + 1, - sizeof(struct rb_root), - GFP_KERNEL); + mm->free_trees[i] = kmalloc_objs(struct rb_root, + mm->max_order + 1); if (!mm->free_trees[i]) goto out_free_tree; @@ -430,9 +427,7 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) mm->n_roots = hweight64(size); - mm->roots = kmalloc_array(mm->n_roots, - sizeof(struct gpu_buddy_block *), - GFP_KERNEL); + mm->roots = kmalloc_objs(struct gpu_buddy_block *, mm->n_roots); if (!mm->roots) goto out_free_tree; diff --git a/drivers/gpu/drm/adp/Kconfig b/drivers/gpu/drm/adp/Kconfig index 9fcc27eb200d..acfa21ee06d2 100644 --- a/drivers/gpu/drm/adp/Kconfig +++ b/drivers/gpu/drm/adp/Kconfig @@ -6,7 +6,6 @@ config DRM_ADP select DRM_KMS_HELPER select DRM_BRIDGE_CONNECTOR select DRM_DISPLAY_HELPER - select DRM_KMS_DMA_HELPER select DRM_GEM_DMA_HELPER select DRM_PANEL_BRIDGE select VIDEOMODE_HELPERS diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 87e15e39eb30..7fd2a8bc7d08 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -299,10 +299,10 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p, amdgpu_res_first(abo->tbo.resource, offset, coredump->ibs[i].ib_size_dw * 4, &cursor); while (cursor.remaining) { - amdgpu_device_mm_access(adev, cursor.start / 4, - &ib_content[off], cursor.size / 4, + amdgpu_device_mm_access(adev, cursor.start, + &ib_content[off], cursor.size, false); - off += cursor.size; + off += cursor.size / 4; amdgpu_res_next(&cursor, cursor.size); } emit_content = true; @@ -603,9 +603,8 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, ring_count++; } if (ring_count) - coredump->rings = kvcalloc(ring_count, - sizeof(struct amdgpu_coredump_ring), - GFP_NOWAIT); + coredump->rings = kvzalloc_objs(struct amdgpu_coredump_ring, + ring_count, GFP_NOWAIT); if (coredump->rings) { for (i = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) { struct amdgpu_coredump_ring *cdump_ring; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 44bed0ba64a3..104d1d2cbad9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -771,6 +771,9 @@ static int amdgpu_device_read_fb_via_bar0(struct amdgpu_device *adev, if (!buf || !size) return -EINVAL; + if (!amdgpu_sriov_vf(adev)) + return -EINVAL; + flags = pci_resource_flags(adev->pdev, 0); if ((flags & IORESOURCE_UNSET) || !(flags & IORESOURCE_MEM)) return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 164e85b66e2d..62f5c5cbd7f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1608,11 +1608,11 @@ int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev, struct pci_dev discovery_bin = adev->discovery.bin; - early_entry = kzalloc(sizeof(*early_entry), GFP_KERNEL); + early_entry = kzalloc_obj(*early_entry); if (!early_entry) return -ENOMEM; - ip_top = kzalloc(sizeof(*ip_top), GFP_KERNEL); + ip_top = kzalloc_obj(*ip_top); if (!ip_top) { kfree(early_entry); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c index d5787d848d04..4a7f63fb2fad 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c @@ -98,7 +98,7 @@ int amdgpu_lockdep_init(void) struct amdgpu_lockdep_dummy_locks *locks; unsigned long flags; - locks = kzalloc(sizeof(*locks), GFP_KERNEL); + locks = kzalloc_obj(*locks); if (!locks) return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index b96f94e5169f..1a86a47406b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -879,7 +879,7 @@ bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev) return (ip_maj == 11 && mes_sched >= 0x8c) || ((ip_maj == 12 && ip_min == 0) && mes_sched >= 0x8d) || - ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x73); + ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x7b); } /* Fix me -- node_id is used to identify the correct MES instances in the future */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 5d9d137209b6..1b6c32a177fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1701,9 +1701,8 @@ u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m) if (dma_resv_trylock(bo->tbo.base.resv)) { dma_resv_describe(bo->tbo.base.resv, m); dma_resv_unlock(bo->tbo.base.resv); - } else { - seq_puts(m, "\n"); } + seq_puts(m, "\n"); return size; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 04f6ebf31cca..42adc8e738d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -396,6 +396,12 @@ static bool psp_get_runtime_db_entry(struct amdgpu_device *adev, bool ret = false; int i; + /* + * Runtime DB is for dGPUs only. + */ + if (adev->flags & AMD_IS_APU) + return false; + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) || amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) || amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) || diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index 95468b9463fb..78adfc839ef8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -1293,7 +1293,7 @@ amdgpu_ras_debugfs_table_read_uniras(struct amdgpu_device *adev, return -ENOMEM; if (num_recs) { - records = kvcalloc(num_recs, sizeof(*records), GFP_KERNEL); + records = kvzalloc_objs(*records, num_recs); if (!records) { res = -ENOMEM; goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index d5a419776e93..016957cac1f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -191,6 +191,8 @@ amdgpu_ttm_job_submit(struct amdgpu_device *adev, struct amdgpu_ttm_buffer_entit * @tmz: if we should setup a TMZ enabled mapping * @size: in number of bytes to map, out number of bytes mapped * @addr: resulting address inside the MC address space + * @vm_needs_flush: out, set true if a GART window was programmed (VMID 0 flush + * needed) or false for a direct address * * Setup one of the GART windows to access a specific piece of memory or return * the physical address for local memory. @@ -200,7 +202,8 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, struct ttm_resource *mem, struct amdgpu_res_cursor *mm_cur, unsigned int window, - bool tmz, uint64_t *size, uint64_t *addr) + bool tmz, uint64_t *size, uint64_t *addr, + bool *vm_needs_flush) { struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); unsigned int offset, num_pages, num_dw, num_bytes; @@ -221,9 +224,12 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, if (!tmz && mem->start != AMDGPU_BO_INVALID_OFFSET) { *addr = amdgpu_ttm_domain_start(adev, mem->mem_type) + mm_cur->start; + *vm_needs_flush = false; return 0; } + /* A GART window is programmed below, so its VMID 0 TLB needs a flush */ + *vm_needs_flush = true; /* * If start begins at an offset inside the page, then adjust the size @@ -324,6 +330,7 @@ static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev, while (src_mm.remaining) { uint64_t from, to, cur_size, tiling_flags; uint32_t num_type, data_format, max_com, write_compress_disable; + bool src_vm_flush, dst_vm_flush; struct dma_fence *next; /* Never copy more than 256MiB at once to avoid a timeout */ @@ -331,12 +338,12 @@ static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev, /* Map src to window 0 and dst to window 1. */ r = amdgpu_ttm_map_buffer(entity, src->bo, src->mem, &src_mm, - 0, tmz, &cur_size, &from); + 0, tmz, &cur_size, &from, &src_vm_flush); if (r) goto error; r = amdgpu_ttm_map_buffer(entity, dst->bo, dst->mem, &dst_mm, - 1, tmz, &cur_size, &to); + 1, tmz, &cur_size, &to, &dst_vm_flush); if (r) goto error; @@ -364,7 +371,7 @@ static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev, } r = amdgpu_copy_buffer(adev, entity, from, to, cur_size, resv, - &next, true, copy_flags); + &next, src_vm_flush || dst_vm_flush, copy_flags); if (r) goto error; @@ -2361,9 +2368,8 @@ void amdgpu_ttm_enable_buffer_funcs(struct amdgpu_device *adev) num_clear_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES); num_move_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES); - adev->mman.clear_entities = kcalloc(num_clear_entities, - sizeof(struct amdgpu_ttm_buffer_entity), - GFP_KERNEL); + adev->mman.clear_entities = kzalloc_objs(struct amdgpu_ttm_buffer_entity, + num_clear_entities); atomic_set(&adev->mman.next_clear_entity, 0); if (!adev->mman.clear_entities) goto error_free_default_entity; @@ -2625,6 +2631,7 @@ int amdgpu_ttm_clear_buffer(struct amdgpu_ttm_buffer_entity *entity, struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); struct dma_fence *fence = NULL; struct amdgpu_res_cursor dst; + bool vm_needs_flush = false; int r; if (!entity) @@ -2646,13 +2653,13 @@ int amdgpu_ttm_clear_buffer(struct amdgpu_ttm_buffer_entity *entity, cur_size = min(dst.size, 256ULL << 20); r = amdgpu_ttm_map_buffer(entity, &bo->tbo, bo->tbo.resource, &dst, - 0, false, &cur_size, &to); + 0, false, &cur_size, &to, &vm_needs_flush); if (r) goto error; r = amdgpu_ttm_fill_mem(adev, entity, 0, to, cur_size, resv, - &next, true, k_job_id); + &next, vm_needs_flush, k_job_id); if (r) goto error; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c index a98a6cfd4fba..71d34fd09385 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c @@ -159,8 +159,8 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev, adev->umc.ras->ecc_info_query_ras_error_address && adev->umc.max_ras_err_cnt_per_query) { err_data->err_addr = - kcalloc(adev->umc.max_ras_err_cnt_per_query, - sizeof(struct eeprom_table_record), GFP_KERNEL); + kzalloc_objs(struct eeprom_table_record, + adev->umc.max_ras_err_cnt_per_query); /* still call query_ras_error_address to clear error status * even NOMEM error is encountered diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 0a816b3c5ff9..e43bda0cab3f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1544,7 +1544,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost) struct amdgpu_usermode_queue *queue; const struct amdgpu_userq_funcs *userq_funcs; unsigned long queue_id; - int r = 0; + int ret = 0, r; xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) { @@ -1555,6 +1555,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost) r = userq_funcs->map(queue); if (r) { dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id); + ret = r; continue; } trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED); @@ -1562,5 +1563,5 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost) } } - return r; + return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index 4b023e024d9f..743b41db5b7c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -227,7 +227,7 @@ static int amdgpu_userq_fence_alloc(struct amdgpu_usermode_queue *userq, struct amdgpu_userq_fence *userq_fence; void *entry; - userq_fence = kmalloc(sizeof(*userq_fence), GFP_KERNEL); + userq_fence = kmalloc_obj(*userq_fence); if (!userq_fence) return -ENOMEM; @@ -244,9 +244,7 @@ static int amdgpu_userq_fence_alloc(struct amdgpu_usermode_queue *userq, } while (xas_retry(&xas, entry)); rcu_read_unlock(); - userq_fence->fence_drv_array = kvmalloc_array(xas.xa_index, - sizeof(fence_drv), - GFP_KERNEL); + userq_fence->fence_drv_array = kvmalloc_objs(fence_drv, xas.xa_index); if (!userq_fence->fence_drv_array) { mutex_unlock(&userq->fence_drv_lock); kfree(userq_fence); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index b43fc643668d..7a007f4916bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -316,8 +316,8 @@ static int amdgpu_virt_ras_realloc_eh_data_space(struct amdgpu_device *adev, if (align_space > AMDGPU_VIRT_RAS_BAD_PAGE_TABLE_MAX_CAPACITY) return -ENOMEM; - new_bps = kmalloc_array(align_space, sizeof(*data->bps), GFP_KERNEL); - new_bo = kcalloc(align_space, sizeof(*data->bps_bo), GFP_KERNEL); + new_bps = kmalloc_objs(*data->bps, align_space); + new_bo = kzalloc_objs(*data->bps_bo, align_space); if (!new_bps || !new_bo) { kfree(new_bps); kfree(new_bo); @@ -355,7 +355,7 @@ static int amdgpu_virt_init_ras_err_handler_data(struct amdgpu_device *adev) if (!bps) goto bps_failure; - bps_bo = kcalloc(align_space, sizeof(*(*data)->bps_bo), GFP_KERNEL); + bps_bo = kzalloc_objs(*(*data)->bps_bo, align_space); if (!bps_bo) goto bps_bo_failure; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index aedf72c2333e..bb04101b0fb5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2090,7 +2090,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, after->start = eaddr + 1; after->last = tmp->last; after->offset = tmp->offset; - after->offset += (after->start - tmp->start) << PAGE_SHIFT; + after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT; after->flags = tmp->flags; after->bo_va = tmp->bo_va; list_add(&after->list, &tmp->bo_va->invalids); @@ -3122,7 +3122,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tIdle BOs:\n"); - list_for_each_entry(base, &lists->needs_update, vm_status) { + list_for_each_entry(base, &lists->idle, vm_status) { if (!base->bo) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 69776dbe188d..0ff5a80aa918 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -1651,10 +1651,10 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 0, 2): case IP_VERSION(11, 0, 3): if (!adev->gfx.disable_uq && - adev->gfx.me_fw_version >= 2420 && - adev->gfx.pfp_fw_version >= 2580 && - adev->gfx.mec_fw_version >= 2650 && - adev->mes.fw_version[0] >= 120) { + adev->gfx.me_fw_version >= 3090 && + adev->gfx.pfp_fw_version >= 3190 && + adev->gfx.mec_fw_version >= 3450 && + adev->mes.fw_version[0] >= 147) { adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs; adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 1e5fd1ef8f1d..e2a81a55c63b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -1436,10 +1436,10 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(12, 0, 0): case IP_VERSION(12, 0, 1): if (!adev->gfx.disable_uq && - adev->gfx.me_fw_version >= 2780 && - adev->gfx.pfp_fw_version >= 2840 && - adev->gfx.mec_fw_version >= 3050 && - adev->mes.fw_version[0] >= 123) { + adev->gfx.me_fw_version >= 3090 && + adev->gfx.pfp_fw_version >= 3190 && + adev->gfx.mec_fw_version >= 3450 && + adev->mes.fw_version[0] >= 147) { adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs; adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 6cf427995078..7f91186ef1d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -4546,9 +4546,11 @@ static int gfx_v8_0_mqd_init(struct amdgpu_ring *ring) /* set static priority for a queue/ring */ gfx_v8_0_mqd_set_priority(ring, mqd); tmp = RREG32(mmCP_HQD_QUANTUM); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10); + if (ring != &adev->gfx.kiq[0].ring) { + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10); + } mqd->cp_hqd_quantum = tmp; /* map_queues packet doesn't need activate the queue, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 7fcfc150a7fc..504a286368eb 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1921,13 +1921,13 @@ static int criu_checkpoint_devices(struct kfd_process *p, struct kfd_criu_device_bucket *device_buckets = NULL; int ret = 0, i; - device_buckets = kvcalloc(num_devices, sizeof(*device_buckets), GFP_KERNEL); + device_buckets = kvzalloc_objs(*device_buckets, num_devices); if (!device_buckets) { ret = -ENOMEM; goto exit; } - device_priv = kvcalloc(num_devices, sizeof(*device_priv), GFP_KERNEL); + device_priv = kvzalloc_objs(*device_priv, num_devices); if (!device_priv) { ret = -ENOMEM; goto exit; @@ -2047,17 +2047,17 @@ static int criu_checkpoint_bos(struct kfd_process *p, int ret = 0, pdd_index, bo_index = 0, id; void *mem; - bo_buckets = kvcalloc(num_bos, sizeof(*bo_buckets), GFP_KERNEL); + bo_buckets = kvzalloc_objs(*bo_buckets, num_bos); if (!bo_buckets) return -ENOMEM; - bo_privs = kvcalloc(num_bos, sizeof(*bo_privs), GFP_KERNEL); + bo_privs = kvzalloc_objs(*bo_privs, num_bos); if (!bo_privs) { ret = -ENOMEM; goto exit; } - files = kvcalloc(num_bos, sizeof(struct file *), GFP_KERNEL); + files = kvzalloc_objs(struct file *, num_bos); if (!files) { ret = -ENOMEM; goto exit; @@ -2588,7 +2588,7 @@ static int criu_restore_bos(struct kfd_process *p, if (!bo_buckets) return -ENOMEM; - files = kvcalloc(args->num_bos, sizeof(struct file *), GFP_KERNEL); + files = kvzalloc_objs(struct file *, args->num_bos); if (!files) { ret = -ENOMEM; goto exit; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 4bc947c3bd0d..9811e4e10291 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -1455,6 +1455,14 @@ static int evict_process_queues_cpsch(struct device_queue_manager *dqm, dqm_evict_mqd_bo(dqm, q); } + /* + * Heavy-weight TLB flush after MES removes queues to ensure + * in-flight memory accesses complete before memory is freed/migrated. + * HWS does this automatically, MES does not. + */ + if (dqm->dev->kfd->shared_resources.enable_mes) + kfd_flush_tlb(pdd); + if (!dqm->dev->kfd->shared_resources.enable_mes) { pdd->last_evict_timestamp = get_jiffies_64(); retval = execute_queues_cpsch(dqm, @@ -3746,8 +3754,11 @@ int suspend_queues(struct kfd_process *p, if (!per_device_suspended) { dqm_unlock(dqm); mutex_unlock(&p->event_mutex); - if (total_suspended) + if (total_suspended) { amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev); + /* Heavy-weight TLB flush after MES suspends queues */ + kfd_flush_tlb(pdd); + } continue; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index ef1d1cb46152..4fe40e9fcfc8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -1169,13 +1169,13 @@ int pqm_debugfs_mqds(struct seq_file *m, void *data) mqd_mgr = q->device->dqm->mqd_mgrs[mqd_type]; size = mqd_mgr->mqd_stride(mqd_mgr, &q->properties); - } - for (xcc = 0; xcc < num_xccs; xcc++) { - mqd = q->mqd + size * xcc; - r = mqd_mgr->debugfs_show_mqd(m, mqd); - if (r != 0) - break; + for (xcc = 0; xcc < num_xccs; xcc++) { + mqd = q->mqd + size * xcc; + r = mqd_mgr->debugfs_show_mqd(m, mqd); + if (r != 0) + break; + } } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ec483276d753..08b8605029ab 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -747,9 +747,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } if (adev->dm.dc->caps.max_links > 0) { adev->dm.hdmi_frl_status_polling_wq = - create_singlethread_workqueue("hdmi_frl_status_polling_workqueue"); + create_singlethread_workqueue("hdmi_frl_status_polling_wq"); if (!adev->dm.hdmi_frl_status_polling_wq) - drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n"); + drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_wq\n"); } if (dc_is_dmub_outbox_supported(adev->dm.dc)) { init_completion(&adev->dm.dmub_aux_transfer_done); @@ -1972,6 +1972,10 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) /* On resume we need to rewrite the MSTM control bits to enable MST*/ s3_handle_mst(ddev, false); + /* Exit IPS before the detection loop's first AUX/DDC access. */ + scoped_guard(mutex, &dm->dc_lock) + dc_exit_ips_for_hw_access(dm->dc); + /* Do detection*/ drm_connector_list_iter_begin(ddev, &iter); drm_for_each_connector_iter(connector, &iter) { @@ -3879,7 +3883,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, continue; bundle->surface_updates[planes_count].surface = dc_plane; - if (new_pcrtc_state->color_mgmt_changed) { + if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) { bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction; bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func; bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; @@ -5698,6 +5702,10 @@ static bool should_reset_plane(struct drm_atomic_commit *state, if (new_crtc_state->color_mgmt_changed) return true; + /* Plane color pipeline or its colorop changes. */ + if (new_plane_state->color_mgmt_changed) + return true; + /* * On zpos change, planes need to be reordered by removing and re-adding * them one by one to the dc state, in order of descending zpos. diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c index e61bbc310f33..b9e90ea449ca 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c @@ -534,8 +534,12 @@ void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) else if (!IS_ERR_OR_NULL(panel_backlight_quirk) && panel_backlight_quirk->force_pwm) caps->aux_support = false; - if (caps->aux_support) - aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; + if (caps->aux_support) { + if (aconnector->dc_link->dpcd_caps.panel_luminance_control) + aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_VESA_AUX; + else + aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; + } luminance_range = &conn_base->display_info.luminance_range; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index c8a1ab8c3b16..154e1f35dcb1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -756,10 +756,14 @@ amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing, break; case DRM_MODE_COLORIMETRY_BT2020_RGB: case DRM_MODE_COLORIMETRY_BT2020_YCC: - if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) - color_space = COLOR_SPACE_2020_RGB_FULLRANGE; - else + if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) { + if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED) + color_space = COLOR_SPACE_2020_RGB_LIMITEDRANGE; + else + color_space = COLOR_SPACE_2020_RGB_FULLRANGE; + } else { color_space = COLOR_SPACE_2020_YCBCR_LIMITED; + } break; case DRM_MODE_COLORIMETRY_DEFAULT: /* ITU601 */ default: @@ -3937,17 +3941,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, } /* Handle MCCS */ - if (do_mccs) + if (do_mccs) { dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); - if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || - as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && - (!sink->edid_caps.freesync_vcp_code || - (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported))) - freesync_capable = false; + if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported) + freesync_capable = false; - if (do_mccs && sink->mccs_caps.freesync_supported && freesync_capable) - dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + if (sink->mccs_caps.freesync_supported && freesync_capable) + dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + } update: if (dm_con_state) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index c4b2fc690fd7..2a6b48e24869 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -196,7 +196,6 @@ static ssize_t dp_link_settings_read(struct file *f, char __user *buf, char *rd_buf_ptr = NULL; const uint32_t rd_buf_size = 100; uint32_t result = 0; - uint8_t str_len = 0; int r; if (*pos & 3 || size & 3) @@ -208,29 +207,26 @@ static ssize_t dp_link_settings_read(struct file *f, char __user *buf, rd_buf_ptr = rd_buf; - str_len = strlen("Current: %d 0x%x %d "); - snprintf(rd_buf_ptr, str_len, "Current: %d 0x%x %d ", + rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), + "Current: %d 0x%x %d ", link->cur_link_settings.lane_count, link->cur_link_settings.link_rate, link->cur_link_settings.link_spread); - rd_buf_ptr += str_len; - str_len = strlen("Verified: %d 0x%x %d "); - snprintf(rd_buf_ptr, str_len, "Verified: %d 0x%x %d ", + rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), + "Verified: %d 0x%x %d ", link->verified_link_cap.lane_count, link->verified_link_cap.link_rate, link->verified_link_cap.link_spread); - rd_buf_ptr += str_len; - str_len = strlen("Reported: %d 0x%x %d "); - snprintf(rd_buf_ptr, str_len, "Reported: %d 0x%x %d ", + rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), + "Reported: %d 0x%x %d ", link->reported_link_cap.lane_count, link->reported_link_cap.link_rate, link->reported_link_cap.link_spread); - rd_buf_ptr += str_len; - str_len = strlen("Preferred: %d 0x%x %d "); - snprintf(rd_buf_ptr, str_len, "Preferred: %d 0x%x %d\n", + rd_buf_ptr += scnprintf(rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), + "Preferred: %d 0x%x %d\n", link->preferred_link_setting.lane_count, link->preferred_link_setting.link_rate, link->preferred_link_setting.link_spread); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index d451082552e8..298de7b75ca8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -200,6 +200,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps( edid_caps->edid_hdmi = connector->display_info.is_hdmi; if (edid_caps->edid_hdmi) { + edid_caps->qs_bit = connector->display_info.rgb_quant_range_selectable; populate_hdmi_info_from_connector(link->dc->config.enable_frl, &connector->display_info.hdmi, edid_caps); drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] max_frl_rate %d\n", __func__, connector->name, edid_caps->max_frl_rate); if (edid_caps->frl_dsc_support) @@ -1202,9 +1203,10 @@ void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hd edid_caps->max_frl_rate = get_max_frl_rate(hdmi->max_lanes, hdmi->max_frl_rate_per_lane); edid_caps->frl_dsc_support = hdmi->dsc_cap.v_1p2; if (edid_caps->frl_dsc_support) { - if (hdmi->dsc_cap.bpc_supported == 10) + /* HF-VSDB DSC max bpc is cumulative: >=12 implies 10 and 8. */ + if (hdmi->dsc_cap.bpc_supported >= 10) edid_caps->frl_dsc_10bpc = true; - else if (hdmi->dsc_cap.bpc_supported == 12) + if (hdmi->dsc_cap.bpc_supported >= 12) edid_caps->frl_dsc_12bpc = true; edid_caps->frl_dsc_all_bpp = hdmi->dsc_cap.all_bpp; edid_caps->frl_dsc_native_420 = hdmi->dsc_cap.native_420; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 824ef3ce5de0..e13b96358208 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -2208,16 +2208,41 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, if (res) return res; - if (plane->type == DRM_PLANE_TYPE_OVERLAY && - plane_cap && plane_cap->per_pixel_alpha) { + /* Blend mode support varies on DCE generations according to HW caps + * and number of planes per CRTC. However, as current driver + * implementation only creates one primary and one cursor plane per + * CRTC for DCE (overlay is only created if + * DC_PLANE_TYPE_DCN_UNIVERSAL), the primary plane blend mode is + * ignored across DCE versions. Keep PREMULTI to avoid uAPI + * regressions: it was the default/mandatory mode for many years and, + * with no overlay plane, primary composes on top of a black + * background, where PREMULTI and PIXEL_NONE are equivalent. + */ + if (plane_cap && plane_cap->type != DC_PLANE_TYPE_DCN_UNIVERSAL) { + unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI); + + drm_plane_create_blend_mode_property(plane, blend_caps); + } else if ((plane->type == DRM_PLANE_TYPE_OVERLAY || + plane->type == DRM_PLANE_TYPE_PRIMARY) && + plane_cap && plane_cap->per_pixel_alpha) { unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE); - drm_plane_create_alpha_property(plane); drm_plane_create_blend_mode_property(plane, blend_caps); + + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + drm_plane_create_alpha_property(plane); } + /* Cursor color format is set to CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA + * by default, so only advertise DRM_MODE_BLEND_PREMULTI blend mode for + * this type of plane. + */ + if (plane->type == DRM_PLANE_TYPE_CURSOR) + drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI)); + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { /* * Allow OVERLAY planes to be used as underlays by assigning an diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index a54fd9529dc9..212a7536e65b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -568,6 +568,23 @@ static void dm_test_output_color_space_bt2020_rgb(struct kunit *test) } /** + * dm_test_output_color_space_bt2020_rgb_limited - Test limited BT.2020 RGB + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt2020_rgb_limited(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_RGB; + state.colorspace = DRM_MODE_COLORIMETRY_BT2020_RGB; + state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_2020_RGB_LIMITEDRANGE); +} + +/** * dm_test_output_color_space_bt2020_ycc - Test Output color space bt2020 ycc * @test: The KUnit test context */ @@ -639,6 +656,24 @@ static void dm_test_output_color_space_bt2020_ycc_rgb_encoding(struct kunit *tes } /** + * dm_test_output_color_space_bt2020_ycc_rgb_encoding_limited - Test limited + * BT.2020 RGB output selected through the BT.2020 YCC connector colorspace + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt2020_ycc_rgb_encoding_limited(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_RGB; + state.colorspace = DRM_MODE_COLORIMETRY_BT2020_YCC; + state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_2020_RGB_LIMITEDRANGE); +} + +/** * dm_test_output_color_space_bt2020_rgb_ycc_encoding - Test Output color space * bt2020 rgb with non-rgb pixel encoding falls back to limited ycbcr * @test: The KUnit test context @@ -4180,7 +4215,7 @@ dm_test_destroy_connector(struct kunit *test, struct drm_device *drm) { struct amdgpu_dm_connector *aconnector; - aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL); + aconnector = kzalloc_obj(*aconnector); KUNIT_ASSERT_NOT_NULL(test, aconnector); KUNIT_ASSERT_EQ(test, @@ -5422,10 +5457,12 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_output_color_space_bt709_y_only), KUNIT_CASE(dm_test_output_color_space_oprgb), KUNIT_CASE(dm_test_output_color_space_bt2020_rgb), + KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_limited), KUNIT_CASE(dm_test_output_color_space_bt2020_ycc), KUNIT_CASE(dm_test_output_color_space_default_ycbcr709_y_only), KUNIT_CASE(dm_test_output_color_space_default_ycbcr601), KUNIT_CASE(dm_test_output_color_space_bt2020_ycc_rgb_encoding), + KUNIT_CASE(dm_test_output_color_space_bt2020_ycc_rgb_encoding_limited), KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_ycc_encoding), /* Tests for amdgpu_dm_convert_dc_color_depth_into_bpc */ KUNIT_CASE(dm_test_convert_color_depth_bpc_mappings), diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c index 4dacddd23878..0d998f204250 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c @@ -1445,7 +1445,7 @@ static void dm_test_crtc_destroy_state_no_stream(struct kunit *test) struct dm_crtc_state *dm_state; /* destroy_state kfree()s the state, so use a plain (unmanaged) alloc. */ - dm_state = kzalloc_obj(*dm_state, GFP_KERNEL); + dm_state = kzalloc_obj(*dm_state); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); amdgpu_dm_crtc_destroy_state(NULL, &dm_state->base); @@ -1475,7 +1475,7 @@ static void dm_test_crtc_destroy_state_releases_stream(struct kunit *test) kref_get(&stream->refcount); /* destroy_state kfree()s the state, so use a plain (unmanaged) alloc. */ - dm_state = kzalloc_obj(*dm_state, GFP_KERNEL); + dm_state = kzalloc_obj(*dm_state); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); dm_state->stream = stream; @@ -1569,7 +1569,7 @@ static void dm_test_crtc_handle_vblank_completes_cursor_only(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); /* drm_crtc_send_vblank_event() consumes (kfree()s) the event. */ - event = kzalloc_obj(*event, GFP_KERNEL); + event = kzalloc_obj(*event); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, event); acrtc->base.dev = &adev->ddev; @@ -1628,7 +1628,7 @@ dm_test_vblank_control_worker_setup(struct kunit *test, bool enable, kref_get(&stream->refcount); /* Worker kfree()s the work item, so it must be a plain allocation. */ - work = kzalloc_obj(*work, GFP_KERNEL); + work = kzalloc_obj(*work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, work); work->dm = &adev->dm; work->acrtc = acrtc; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c index 058e1ad15dfe..82e0c984693c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c @@ -358,12 +358,14 @@ static void dm_test_parse_edid_caps_hdmi_frl(struct kunit *test) /* Drive the HDMI/FRL branch */ connector->display_info.is_hdmi = true; + connector->display_info.rgb_quant_range_selectable = true; connector->display_info.hdmi.scdc.supported = true; connector->display_info.hdmi.max_lanes = 4; connector->display_info.hdmi.max_frl_rate_per_lane = 12; KUNIT_EXPECT_EQ(test, dm_helpers_parse_edid_caps(link, dc_edid, edid_caps), EDID_OK); KUNIT_EXPECT_TRUE(test, edid_caps->edid_hdmi); + KUNIT_EXPECT_EQ(test, edid_caps->qs_bit, 1); KUNIT_EXPECT_TRUE(test, edid_caps->scdc_present); /* max_lanes 4 + max_frl_rate_per_lane 12 -> rate index 6 */ KUNIT_EXPECT_EQ(test, edid_caps->max_frl_rate, 6); @@ -909,7 +911,7 @@ static void dm_test_populate_hdmi_frl_dsc_12bpc(struct kunit *test) KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 2); KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support); - KUNIT_EXPECT_FALSE(test, caps->frl_dsc_10bpc); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_10bpc); KUNIT_EXPECT_TRUE(test, caps->frl_dsc_12bpc); KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 7); KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 1); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c index 28c36217f6a2..861ee9eaa032 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c @@ -103,7 +103,7 @@ static enum dc_status dm_test_dp_read_hpd_rx_irq_data_ok(struct dc_link *link, */ static struct dc_sink *dm_test_sink_create(struct dc_link *link) { - struct dc_sink *sink = kzalloc(sizeof(*sink), GFP_KERNEL); + struct dc_sink *sink = kzalloc_obj(*sink); if (!sink) return NULL; @@ -2131,7 +2131,7 @@ static void dm_test_hpd_rx_offload_work_no_connector(struct kunit *test) offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq); - offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL); + offload_work = kzalloc_obj(*offload_work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work); offload_work->offload_wq = offload_wq; offload_work->adev = adev; @@ -2177,7 +2177,7 @@ static void dm_test_hpd_rx_offload_work_no_connection(struct kunit *test) link->dc = dc; aconn->dc_link = link; - offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL); + offload_work = kzalloc_obj(*offload_work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work); offload_work->offload_wq = offload_wq; offload_work->adev = adev; @@ -2244,7 +2244,7 @@ static void dm_test_hpd_rx_offload_work_automated_test(struct kunit *test) link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; aconn->dc_link = link; - offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL); + offload_work = kzalloc_obj(*offload_work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work); offload_work->offload_wq = offload_wq; offload_work->adev = adev; @@ -2315,7 +2315,7 @@ static void dm_test_hpd_rx_offload_work_link_loss(struct kunit *test) link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; aconn->dc_link = link; - offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL); + offload_work = kzalloc_obj(*offload_work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work); offload_work->offload_wq = offload_wq; offload_work->adev = adev; @@ -3452,9 +3452,9 @@ static void dm_test_handle_hpd_work_out_of_range(struct kunit *test) struct amdgpu_device *adev; adev = dm_kunit_alloc_adev(test); - hpd_work = kzalloc(sizeof(*hpd_work), GFP_KERNEL); + hpd_work = kzalloc_obj(*hpd_work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hpd_work); - hpd_work->dmub_notify = kzalloc(sizeof(*hpd_work->dmub_notify), GFP_KERNEL); + hpd_work->dmub_notify = kzalloc_obj(*hpd_work->dmub_notify); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hpd_work->dmub_notify); hpd_work->dmub_notify->type = (enum dmub_notification_type)ARRAY_SIZE(adev->dm.dmub_callback); @@ -3899,9 +3899,9 @@ static void dm_test_handle_vmin_vmax_update(struct kunit *test) kref_get(&stream->refcount); /* The worker kfree()s both, so they must come from the slab. */ - work = kzalloc(sizeof(*work), GFP_KERNEL); + work = kzalloc_obj(*work); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, work); - adjust = kzalloc(sizeof(*adjust), GFP_KERNEL); + adjust = kzalloc_obj(*adjust); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adjust); work->adev = adev; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index ba97092c7bb8..23ac8ae41c68 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -2945,7 +2945,7 @@ static void dm_test_plane_reset_initializes_state(struct kunit *test) * destroy-existing-state path. The destroy hook frees this state, so it * must be a plain (non-KUnit-managed) allocation. */ - old_state = kzalloc(sizeof(*old_state), GFP_KERNEL); + old_state = kzalloc_obj(*old_state); KUNIT_ASSERT_NOT_NULL(test, old_state); plane->funcs = &dm_test_plane_reset_funcs; plane->state = &old_state->base; @@ -3018,7 +3018,7 @@ static void dm_test_plane_destroy_state_minimal(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, plane); /* destroy_state frees the state itself, so use a plain allocation. */ - dm_plane_state = kzalloc(sizeof(*dm_plane_state), GFP_KERNEL); + dm_plane_state = kzalloc_obj(*dm_plane_state); KUNIT_ASSERT_NOT_NULL(test, dm_plane_state); amdgpu_dm_plane_drm_plane_destroy_state(plane, &dm_plane_state->base); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c index 3c10eec9b1e0..5645866610fc 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -239,7 +239,7 @@ static void dm_test_atomic_destroy_state_no_context(struct kunit *test) * Use kzalloc(), not kunit_kzalloc(): dm_atomic_destroy_state() frees * the state itself, so KUnit-managed memory would be double-freed. */ - dm_state = kzalloc(sizeof(*dm_state), GFP_KERNEL); + dm_state = kzalloc_obj(*dm_state); KUNIT_ASSERT_NOT_NULL(test, dm_state); /* context == NULL: dc_state_release() is skipped and the state is freed. */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c index e2148cb3b8ab..bf80eaf23e9a 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c @@ -377,7 +377,7 @@ struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *p } break; case AMDGPU_FAMILY_GC_11_5_4: { - struct clk_mgr_dcn42 *clk_mgr = kzalloc(sizeof(*clk_mgr), GFP_KERNEL); + struct clk_mgr_dcn42 *clk_mgr = kzalloc_obj(*clk_mgr); if (clk_mgr == NULL) { BREAK_TO_DEBUGGER(); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c index d71b0aed90c9..7dd88d90eba2 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c @@ -1595,7 +1595,7 @@ struct clk_mgr_internal *dcn60_clk_mgr_construct( struct dccg *dccg) { struct clk_log_info log_info = {0}; - struct dcn60_clk_mgr *clk_mgr60 = kzalloc(sizeof(struct dcn60_clk_mgr), GFP_KERNEL); + struct dcn60_clk_mgr *clk_mgr60 = kzalloc_obj(struct dcn60_clk_mgr); struct clk_mgr_internal *clk_mgr; if (!clk_mgr60) @@ -1644,7 +1644,7 @@ struct clk_mgr_internal *dcn60_clk_mgr_construct( clk_mgr->smu_present = false; - clk_mgr->base.bw_params = kzalloc(sizeof(*clk_mgr->base.bw_params), GFP_KERNEL); + clk_mgr->base.bw_params = kzalloc_obj(*clk_mgr->base.bw_params); if (!clk_mgr->base.bw_params) goto fail; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 68f228014305..519ac878ada1 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -914,8 +914,7 @@ static bool dc_construct_update_scratch_pool(struct dc *dc) unsigned int i; for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) { - dc->update_scratch_pool[i] = kzalloc( - sizeof(struct dc_update_scratch_space), GFP_KERNEL); + dc->update_scratch_pool[i] = kzalloc_obj(struct dc_update_scratch_space); if (!dc->update_scratch_pool[i]) return false; dc->update_scratch_in_use[i] = false; @@ -3199,6 +3198,7 @@ static struct dc_update_descriptor check_update_surfaces_for_stream( } if ((stream_update->hdr_static_metadata && !stream_update->stream->use_dynamic_meta) || + stream_update->output_color_space || stream_update->vrr_infopacket || stream_update->vsc_infopacket || stream_update->vsp_infopacket || @@ -4189,6 +4189,7 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, hwss_add_setup_periodic_interrupt(&seq_state, dc, pipe_ctx); if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) || + stream_update->output_color_space || stream_update->vrr_infopacket || stream_update->vsc_infopacket || stream_update->vsp_infopacket || @@ -4371,6 +4372,7 @@ static void commit_planes_do_stream_update(struct dc *dc, dc->hwss.setup_periodic_interrupt(dc, pipe_ctx); if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) || + stream_update->output_color_space || stream_update->vrr_infopacket || stream_update->vsc_infopacket || stream_update->vsp_infopacket || diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 7eaaf38cd9ab..fc9080f0c093 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -1797,7 +1797,11 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx) * pipe-split, merge together per same height. */ for (split_pipe = pipe_ctx->top_pipe; split_pipe; - split_pipe = split_pipe->top_pipe) + split_pipe = split_pipe->top_pipe) { + + if (split_pipe == test_pipe) + continue; + if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) { struct rect r2_half; @@ -1809,6 +1813,7 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx) r2_bottom = min(r2_bottom, r2_half.y + r2_half.height); break; } + } if (r1.x >= r2.x && r1.y >= r2.y && r1_right <= r2_right && r1_bottom <= r2_bottom) return true; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c index 88e825a6582c..0b135a9e6c4f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c @@ -290,7 +290,7 @@ static void dc_plane_cm_free(struct kref *kref) struct dc_plane_cm *dc_plane_cm_create(void) { - struct dc_plane_cm *cm = kvzalloc(sizeof(*cm), GFP_KERNEL); + struct dc_plane_cm *cm = kvzalloc_obj(*cm); if (cm == NULL) goto alloc_fail; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c index 3b03b152da22..c2fa7fd56acf 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c @@ -358,7 +358,7 @@ struct dccg *dccg42_create( const struct dccg_shift *dccg_shift, const struct dccg_mask *dccg_mask) { - struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL); + struct dcn_dccg *dccg_dcn = kzalloc_obj(*dccg_dcn); struct dccg *base; if (dccg_dcn == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c index 8e2f88913e4c..1d6d193b23c7 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn60/dcn60_dccg.c @@ -167,7 +167,7 @@ struct dccg *dccg60_create( const struct dccg_shift *dccg_shift, const struct dccg_mask *dccg_mask) { - struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL); + struct dcn_dccg *dccg_dcn = kzalloc_obj(*dccg_dcn); struct dccg *base; if (dccg_dcn == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c index 66fe7f313ea3..62ca235cd649 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c @@ -320,6 +320,8 @@ static struct fixed31_32 interp_tf_pts(const struct fixed31_32 *output_tf_channe return value; } +#define NUM_DEGAMMA_REGIONS 9 + bool cm3_helper_translate_curve_to_degamma_hw_format( const struct dc_transfer_func *output_tf, struct pwl_params *lut_params) @@ -343,31 +345,15 @@ bool cm3_helper_translate_curve_to_degamma_hw_format( memset(lut_params, 0, sizeof(struct pwl_params)); memset(seg_distr, 0, sizeof(seg_distr)); - if (output_tf->tf == TRANSFER_FUNCTION_PQ || - output_tf->tf == TRANSFER_FUNCTION_SRGB) { - /* 9 segments - * segments are from 2^-9 to 0 - */ - const uint8_t SEG_COUNT = 9; - seg_distr[0] = 0; // Since we only have one point in darkest region - for (k = 1; k < SEG_COUNT; k++) - seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases - - region_start = -SEG_COUNT; - region_end = 0; - } else { - /* 12 segments - * segments are from 2^-12 to 2^0 - * There are less than 256 points, for optimization - */ - const uint8_t SEG_COUNT = 12; - - for (i = 0; i < SEG_COUNT; i++) - seg_distr[i] = 4; + /* 9 segments + * segments are from 2^-9 to 2^0 + */ + seg_distr[0] = 0; // Since we only have one point in darkest region + for (k = 1; k < NUM_DEGAMMA_REGIONS; k++) + seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases - region_start = -SEG_COUNT; - region_end = 0; - } + region_start = -NUM_DEGAMMA_REGIONS; + region_end = 0; for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) seg_distr[i] = -1; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c index 8497eaea012e..297e21e7c68d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c @@ -1128,8 +1128,8 @@ static bool dcn5_mode_programming(struct dml2_core_calcs_mode_programming_ex *in CalculateWatermarks_params->USRRetrainingSupport = &mode_lib->mp.USRRetrainingSupport; CalculateWatermarks_params->temp_read_or_ppt_support = mode_lib->mp.temp_read_or_ppt_support; CalculateWatermarks_params->global_temp_read_or_ppt_supported = &mode_lib->mp.global_temp_read_or_ppt_supported; - CalculateWatermarks_params->VActiveLatencyHidingMargin = 0; - CalculateWatermarks_params->VActiveLatencyHidingUs = 0; + CalculateWatermarks_params->VActiveLatencyHidingMargin = NULL; + CalculateWatermarks_params->VActiveLatencyHidingUs = NULL; dcn5_calculate_watermarks_and_dram_speed_change_support(&mode_lib->scratch, CalculateWatermarks_params); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index 8a371bd1a7a5..28f4a53d0617 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -1819,8 +1819,8 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param bool UnboundedRequestEnabled; unsigned int CompressedBufferSizeInkByte; bool max_outstanding_when_urgent_expected; - const unsigned int max_outstanding_requests; - const unsigned int max_request_size_bytes; + unsigned int max_outstanding_requests; + unsigned int max_request_size_bytes; const unsigned int *meta_row_height_l; const unsigned int *meta_row_height_c; const enum dml2_pstate_method *uclk_pstate_switch_modes; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c index 0b884a8661c8..6d6611a6b5a6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c @@ -1414,7 +1414,7 @@ static bool find_shift_for_valid_cache_id_assignment(const int *mcache_boundarie success = true; for (pipe_index = 0; pipe_index < pipe_count; pipe_index++) { if (!calculate_first_second_splitting(mcache_boundaries, num_boundaries, *shift, - pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], 0, 0)) { + pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], NULL, NULL)) { success = false; break; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index 166f10b8862f..c82886323a51 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -301,6 +301,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s out->smn_latency_us = 2; out->dispclk_dppclk_vco_speed_mhz = 3600; out->pct_ideal_dram_bw_after_urgent_pixel_only = 65.0; + out->gpuvm_min_page_size_kbytes = 4; break; diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c index b75bfea635fd..cf9c7a2089a1 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c @@ -542,7 +542,7 @@ void dal_hw_ddc_init_i3cpad( *hw_ddc = NULL; } - *hw_ddc = kzalloc(sizeof(struct hw_ddc), GFP_KERNEL); + *hw_ddc = kzalloc_obj(struct hw_ddc); if (!*hw_ddc) { ASSERT_CRITICAL(false); return; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c index f4d1ce9079de..3a87c1a26488 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn42/irq_service_dcn42.c @@ -405,7 +405,7 @@ static void dcn42_irq_construct(struct irq_service *irq_service, struct irq_service *dal_irq_service_dcn42_create(struct irq_service_init_data *init_data) { - struct irq_service *irq_service = kzalloc(sizeof(*irq_service), GFP_KERNEL); + struct irq_service *irq_service = kzalloc_obj(*irq_service); if (!irq_service) return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c index 99163346e7d9..651bae6b5ede 100644 --- a/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c +++ b/drivers/gpu/drm/amd/display/dc/irq/dcn60/irq_service_dcn60.c @@ -406,8 +406,7 @@ static void dcn60_irq_construct( struct irq_service *dal_irq_service_dcn60_create( struct irq_service_init_data *init_data) { - struct irq_service *irq_service = kzalloc(sizeof(*irq_service), - GFP_KERNEL); + struct irq_service *irq_service = kzalloc_obj(*irq_service); if (!irq_service) return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c index dd854d992692..f43fc4b78a8d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -103,6 +103,11 @@ static int get_estimated_bw(struct dc_link *link) { uint8_t bw_estimated_bw = 0; + if (link->dpia_bw_alloc_config.bw_granularity == 0) { + DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__); + return 0; + } + core_link_read_dpcd( link, ESTIMATED_BW, diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c index 78b33b2dbae8..2c87e33e909e 100644 --- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c +++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c @@ -615,7 +615,7 @@ struct pg_cntl *pg_cntl42_create( const struct pg_cntl_shift *pg_cntl_shift, const struct pg_cntl_mask *pg_cntl_mask) { - struct dcn_pg_cntl *pg_cntl_dcn = kzalloc(sizeof(*pg_cntl_dcn), GFP_KERNEL); + struct dcn_pg_cntl *pg_cntl_dcn = kzalloc_obj(*pg_cntl_dcn); struct pg_cntl *base; if (pg_cntl_dcn == NULL) { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index dbb8bb7fc20d..aecbcd28c2d9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -1135,7 +1135,7 @@ static struct hpo_frl_stream_encoder *dcn30_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG sub-block */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn30_vpg_create(ctx, vpg_inst); afmt = dcn30_afmt_create(ctx, afmt_inst); @@ -1166,7 +1166,7 @@ static struct hpo_frl_link_encoder *dcn30_hpo_frl_link_encoder_create(enum engin ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 88dcf8166378..f67b3e00bd86 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -492,7 +492,7 @@ static struct hpo_frl_stream_encoder *dcn302_hpo_frl_stream_encoder_create(enum return NULL; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn302_vpg_create(ctx, vpg_inst); afmt = dcn302_afmt_create(ctx, afmt_inst); @@ -531,7 +531,7 @@ static struct hpo_frl_link_encoder *dcn302_hpo_frl_link_encoder_create(enum engi ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index 50b7c7b85fac..9e1bf34cca2a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -479,7 +479,7 @@ static struct hpo_frl_stream_encoder *dcn303_hpo_frl_stream_encoder_create(enum return NULL; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn303_vpg_create(ctx, vpg_inst); afmt = dcn303_afmt_create(ctx, afmt_inst); @@ -518,7 +518,7 @@ static struct hpo_frl_link_encoder *dcn303_hpo_frl_link_encoder_create(enum engi ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index db56e30cf259..6359fbb574df 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1346,7 +1346,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1377,7 +1377,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engin ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 63f92e9da6d8..0ddfceca69dd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -1404,7 +1404,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1436,7 +1436,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engin ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 15c1615c5f45..a1894be8cbb2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1347,7 +1347,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1379,7 +1379,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engin ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index a5ea35e45791..4a2d56a196b4 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1340,7 +1340,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1372,7 +1372,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engin ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index 7c2a79015f4e..a2e66761b8ec 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -1339,7 +1339,7 @@ static struct hpo_frl_stream_encoder *dcn32_hpo_frl_stream_encoder_create(enum e } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn32_vpg_create(ctx, vpg_inst); afmt = dcn32_afmt_create(ctx, afmt_inst); @@ -1375,7 +1375,7 @@ static struct hpo_frl_link_encoder *dcn32_hpo_frl_link_encoder_create(enum engin hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index 5ee9a5a8ec3c..4097093d9012 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -1314,7 +1314,7 @@ static struct hpo_frl_stream_encoder *dcn321_hpo_frl_stream_encoder_create(enum } /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn321_vpg_create(ctx, vpg_inst); afmt = dcn321_afmt_create(ctx, afmt_inst); @@ -1350,7 +1350,7 @@ static struct hpo_frl_link_encoder *dcn321_hpo_frl_link_encoder_create(enum engi hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 52826e96c184..fd7a22fcca59 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -1398,7 +1398,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1430,7 +1430,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index e3fc71307c91..83248ee01d11 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -1378,7 +1378,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1410,7 +1410,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index c019a657005d..8d1baa76c347 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -1385,7 +1385,7 @@ static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_stream_encoder); vpg = dcn31_vpg_create(ctx, vpg_inst); afmt = dcn31_afmt_create(ctx, afmt_inst); @@ -1417,7 +1417,7 @@ static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_enc3 = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_enc3) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index aced8ff657bf..0cfdca82bef0 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -1322,7 +1322,7 @@ static struct hpo_frl_stream_encoder *dcn401_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ - hpo_enc401 = kzalloc(sizeof(struct dcn401_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc401 = kzalloc_obj(struct dcn401_hpo_frl_stream_encoder); vpg = dcn401_vpg_create(ctx, vpg_inst); afmt = dcn401_afmt_create(ctx, afmt_inst); @@ -1353,7 +1353,7 @@ static struct hpo_frl_link_encoder *dcn401_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_link_enc = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_link_enc) return NULL; /* out of memory */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 6370d3903eb5..28192d6dda7a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -818,7 +818,7 @@ static struct dce_aux *dcn42_aux_engine_create( uint32_t inst) { struct aux_engine_dce110 *aux_engine = - kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + kzalloc_obj(struct aux_engine_dce110); if (!aux_engine) return NULL; @@ -884,7 +884,7 @@ static struct dce_i2c_hw *dcn42_i2c_hw_create( uint32_t inst) { struct dce_i2c_hw *dce_i2c_hw = - kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + kzalloc_obj(struct dce_i2c_hw); if (!dce_i2c_hw) return NULL; @@ -910,7 +910,7 @@ static struct clock_source *dcn42_clock_source_create( bool dp_clk_src) { struct dce110_clk_src *clk_src = - kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + kzalloc_obj(struct dce110_clk_src); if (!clk_src) return NULL; @@ -929,8 +929,7 @@ static struct hubbub *dcn42_hubbub_create(struct dc_context *ctx) { int i; - struct dcn20_hubbub *hubbub3 = kzalloc(sizeof(struct dcn20_hubbub), - GFP_KERNEL); + struct dcn20_hubbub *hubbub3 = kzalloc_obj(struct dcn20_hubbub); if (!hubbub3) return NULL; @@ -983,7 +982,7 @@ static struct hubp *dcn42_hubp_create( uint32_t inst) { struct dcn20_hubp *hubp2 = - kzalloc(sizeof(struct dcn20_hubp), GFP_KERNEL); + kzalloc_obj(struct dcn20_hubp); if (!hubp2) return NULL; @@ -1025,7 +1024,7 @@ static struct dpp *dcn42_dpp_create( uint32_t inst) { struct dcn42_dpp *dpp42 = - kzalloc(sizeof(struct dcn42_dpp), GFP_KERNEL); + kzalloc_obj(struct dcn42_dpp); if (!dpp42) return NULL; @@ -1051,8 +1050,7 @@ static struct mpc *dcn42_mpc_create( int num_mpcc, int num_rmu) { - struct dcn42_mpc *mpc401 = kzalloc(sizeof(struct dcn42_mpc), - GFP_KERNEL); + struct dcn42_mpc *mpc401 = kzalloc_obj(struct dcn42_mpc); if (!mpc401) return NULL; @@ -1075,7 +1073,7 @@ static struct output_pixel_processor *dcn42_opp_create( struct dc_context *ctx, uint32_t inst) { struct dcn20_opp *opp4 = - kzalloc(sizeof(struct dcn20_opp), GFP_KERNEL); + kzalloc_obj(struct dcn20_opp); if (!opp4) { BREAK_TO_DEBUGGER(); @@ -1098,7 +1096,7 @@ static struct timing_generator *dcn42_timing_generator_create( uint32_t instance) { struct optc *tgn10 = - kzalloc(sizeof(struct optc), GFP_KERNEL); + kzalloc_obj(struct optc); if (!tgn10) return NULL; @@ -1136,7 +1134,7 @@ static struct link_encoder *dcn42_link_encoder_create( const struct encoder_init_data *enc_init_data) { struct dcn20_link_encoder *enc20 = - kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + kzalloc_obj(struct dcn20_link_encoder); if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) return NULL; @@ -1202,7 +1200,7 @@ static struct vpg *dcn42_vpg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_vpg *vpg4 = kzalloc(sizeof(struct dcn31_vpg), GFP_KERNEL); + struct dcn31_vpg *vpg4 = kzalloc_obj(struct dcn31_vpg); if (!vpg4) return NULL; @@ -1231,7 +1229,7 @@ static struct apg *dcn42_apg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_apg *apg31 = kzalloc(sizeof(struct dcn31_apg), GFP_KERNEL); + struct dcn31_apg *apg31 = kzalloc_obj(struct dcn31_apg); if (!apg31) return NULL; @@ -1275,7 +1273,7 @@ static struct stream_encoder *dcn42_stream_encoder_create( } else return NULL; - enc1 = kzalloc(sizeof(struct dcn10_stream_encoder), GFP_KERNEL); + enc1 = kzalloc_obj(struct dcn10_stream_encoder); vpg = dcn42_vpg_create(ctx, vpg_inst); apg = dcn42_apg_create(ctx, apg_inst); @@ -1325,7 +1323,7 @@ static struct hpo_frl_stream_encoder *dcn42_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_enc42 = kzalloc(sizeof(struct dcn42_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc42 = kzalloc_obj(struct dcn42_hpo_frl_stream_encoder); vpg = dcn42_vpg_create(ctx, vpg_inst); apg = dcn42_apg_create(ctx, apg_inst); @@ -1357,7 +1355,7 @@ static struct hpo_frl_link_encoder *dcn42_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_link_enc = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_link_enc) return NULL; /* out of memory */ @@ -1399,7 +1397,7 @@ static struct hpo_dp_stream_encoder *dcn42_hpo_dp_stream_encoder_create( apg_inst = hpo_dp_inst + 5; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_dp_enc31 = kzalloc(sizeof(struct dcn31_hpo_dp_stream_encoder), GFP_KERNEL); + hpo_dp_enc31 = kzalloc_obj(struct dcn31_hpo_dp_stream_encoder); vpg = dcn42_vpg_create(ctx, vpg_inst); apg = dcn42_apg_create(ctx, apg_inst); @@ -1432,7 +1430,7 @@ static struct hpo_dp_link_encoder *dcn42_hpo_dp_link_encoder_create( struct dcn31_hpo_dp_link_encoder *hpo_dp_enc31; /* allocate HPO link encoder */ - hpo_dp_enc31 = kzalloc(sizeof(struct dcn31_hpo_dp_link_encoder), GFP_KERNEL); + hpo_dp_enc31 = kzalloc_obj(struct dcn31_hpo_dp_link_encoder); if (!hpo_dp_enc31) return NULL; /* out of memory */ @@ -1453,7 +1451,7 @@ static struct hpo_dp_link_encoder *dcn42_hpo_dp_link_encoder_create( static struct dce_hwseq *dcn42_hwseq_create( struct dc_context *ctx) { - struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + struct dce_hwseq *hws = kzalloc_obj(struct dce_hwseq); #undef REG_STRUCT #define REG_STRUCT hwseq_reg @@ -1727,8 +1725,7 @@ static bool dcn42_dwbc_create(struct dc_context *ctx, struct resource_pool *pool uint32_t dwb_count = pool->res_cap->num_dwb; for (i = 0; i < dwb_count; i++) { - struct dcn30_dwbc *dwbc42 = kzalloc(sizeof(struct dcn30_dwbc), - GFP_KERNEL); + struct dcn30_dwbc *dwbc42 = kzalloc_obj(struct dcn30_dwbc); if (!dwbc42) { dm_error("DC: failed to create dwbc42!\n"); @@ -1764,8 +1761,7 @@ static bool dcn42_mmhubbub_create(struct dc_context *ctx, struct resource_pool * uint32_t pipe_count = pool->res_cap->num_dwb; for (i = 0; i < pipe_count; i++) { - struct dcn30_mmhubbub *mcif_wb30 = kzalloc(sizeof(struct dcn30_mmhubbub), - GFP_KERNEL); + struct dcn30_mmhubbub *mcif_wb30 = kzalloc_obj(struct dcn30_mmhubbub); if (!mcif_wb30) { dm_error("DC: failed to create mcif_wb30!\n"); @@ -1793,7 +1789,7 @@ static struct display_stream_compressor *dcn42_dsc_create( struct dc_context *ctx, uint32_t inst) { struct dcn401_dsc *dsc = - kzalloc(sizeof(struct dcn401_dsc), GFP_KERNEL); + kzalloc_obj(struct dcn401_dsc); if (!dsc) { BREAK_TO_DEBUGGER(); @@ -1890,7 +1886,7 @@ static struct link_encoder *dcn42_link_enc_create_minimal( if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; - enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + enc20 = kzalloc_obj(struct dcn20_link_encoder); if (!enc20) return NULL; @@ -2441,7 +2437,7 @@ struct resource_pool *dcn42_create_resource_pool( struct dc *dc) { struct dcn42_resource_pool *pool = - kzalloc(sizeof(struct dcn401_resource_pool), GFP_KERNEL); + kzalloc_obj(struct dcn42_resource_pool); if (!pool) return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 41f0c67f57ff..f36d0f828166 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -870,7 +870,7 @@ static struct dce_aux *dcn42b_aux_engine_create( uint32_t inst) { struct aux_engine_dce110 *aux_engine = - kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + kzalloc_obj(struct aux_engine_dce110); if (!aux_engine) return NULL; @@ -940,7 +940,7 @@ static struct dce_i2c_hw *dcn42b_i2c_hw_create( uint32_t inst) { struct dce_i2c_hw *dce_i2c_hw = - kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + kzalloc_obj(struct dce_i2c_hw); if (!dce_i2c_hw) return NULL; @@ -968,7 +968,7 @@ static struct clock_source *dcn42b_clock_source_create( bool dp_clk_src) { struct dce110_clk_src *clk_src = - kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + kzalloc_obj(struct dce110_clk_src); if (!clk_src) return NULL; @@ -988,8 +988,7 @@ static struct hubbub *dcn42b_hubbub_create(struct dc_context *ctx) { int i; - struct dcn20_hubbub *hubbub3 = kzalloc(sizeof(struct dcn20_hubbub), - GFP_KERNEL); + struct dcn20_hubbub *hubbub3 = kzalloc_obj(struct dcn20_hubbub); if (!hubbub3) return NULL; @@ -1042,7 +1041,7 @@ static struct hubp *dcn42b_hubp_create( uint32_t inst) { struct dcn20_hubp *hubp2 = - kzalloc(sizeof(struct dcn20_hubp), GFP_KERNEL); + kzalloc_obj(struct dcn20_hubp); if (!hubp2) return NULL; @@ -1084,7 +1083,7 @@ static struct dpp *dcn42b_dpp_create( uint32_t inst) { struct dcn42_dpp *dpp42b = - kzalloc(sizeof(struct dcn42_dpp), GFP_KERNEL); + kzalloc_obj(struct dcn42_dpp); if (!dpp42b) return NULL; @@ -1110,8 +1109,7 @@ static struct mpc *dcn42b_mpc_create( int num_mpcc, int num_rmu) { - struct dcn42_mpc *mpc42b = kzalloc(sizeof(struct dcn42_mpc), - GFP_KERNEL); + struct dcn42_mpc *mpc42b = kzalloc_obj(struct dcn42_mpc); if (!mpc42b) return NULL; @@ -1134,7 +1132,7 @@ static struct output_pixel_processor *dcn42b_opp_create( struct dc_context *ctx, uint32_t inst) { struct dcn20_opp *opp4 = - kzalloc(sizeof(struct dcn20_opp), GFP_KERNEL); + kzalloc_obj(struct dcn20_opp); if (!opp4) { BREAK_TO_DEBUGGER(); @@ -1158,7 +1156,7 @@ static struct timing_generator *dcn42b_timing_generator_create( uint32_t instance) { struct optc *tgn10 = - kzalloc(sizeof(struct optc), GFP_KERNEL); + kzalloc_obj(struct optc); if (!tgn10) return NULL; @@ -1196,7 +1194,7 @@ static struct link_encoder *dcn42b_link_encoder_create( const struct encoder_init_data *enc_init_data) { struct dcn20_link_encoder *enc20 = - kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + kzalloc_obj(struct dcn20_link_encoder); if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) return NULL; @@ -1271,7 +1269,7 @@ static struct vpg *dcn42b_vpg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_vpg *vpg4 = kzalloc(sizeof(struct dcn31_vpg), GFP_KERNEL); + struct dcn31_vpg *vpg4 = kzalloc_obj(struct dcn31_vpg); if (!vpg4) return NULL; @@ -1300,7 +1298,7 @@ static struct apg *dcn42b_apg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_apg *apg31 = kzalloc(sizeof(struct dcn31_apg), GFP_KERNEL); + struct dcn31_apg *apg31 = kzalloc_obj(struct dcn31_apg); if (!apg31) return NULL; @@ -1344,7 +1342,7 @@ static struct stream_encoder *dcn42b_stream_encoder_create( } else return NULL; - enc1 = kzalloc(sizeof(struct dcn10_stream_encoder), GFP_KERNEL); + enc1 = kzalloc_obj(struct dcn10_stream_encoder); vpg = dcn42b_vpg_create(ctx, vpg_inst); apg = dcn42b_apg_create(ctx, apg_inst); @@ -1404,7 +1402,7 @@ static struct hpo_dp_stream_encoder *dcn42b_hpo_dp_stream_encoder_create( apg_inst = hpo_dp_inst + 6; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_dp_enc31 = kzalloc(sizeof(struct dcn31_hpo_dp_stream_encoder), GFP_KERNEL); + hpo_dp_enc31 = kzalloc_obj(struct dcn31_hpo_dp_stream_encoder); vpg = dcn42b_vpg_create(ctx, vpg_inst); apg = dcn42b_apg_create(ctx, apg_inst); @@ -1437,7 +1435,7 @@ static struct hpo_dp_link_encoder *dcn42b_hpo_dp_link_encoder_create( struct dcn31_hpo_dp_link_encoder *hpo_dp_enc31; /* allocate HPO link encoder */ - hpo_dp_enc31 = kzalloc(sizeof(struct dcn31_hpo_dp_link_encoder), GFP_KERNEL); + hpo_dp_enc31 = kzalloc_obj(struct dcn31_hpo_dp_link_encoder); if (!hpo_dp_enc31) return NULL; /* out of memory */ @@ -1457,7 +1455,7 @@ static struct hpo_dp_link_encoder *dcn42b_hpo_dp_link_encoder_create( static struct dce_hwseq *dcn42b_hwseq_create( struct dc_context *ctx) { - struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + struct dce_hwseq *hws = kzalloc_obj(struct dce_hwseq); #undef REG_STRUCT #define REG_STRUCT hwseq_reg @@ -1716,8 +1714,7 @@ static bool dcn42b_dwbc_create(struct dc_context *ctx, struct resource_pool *poo uint32_t dwb_count = pool->res_cap->num_dwb; for (i = 0; i < dwb_count; i++) { - struct dcn30_dwbc *dwbc42 = kzalloc(sizeof(struct dcn30_dwbc), - GFP_KERNEL); + struct dcn30_dwbc *dwbc42 = kzalloc_obj(struct dcn30_dwbc); if (!dwbc42) { dm_error("DC: failed to create dwbc42!\n"); @@ -1753,8 +1750,7 @@ static bool dcn42b_mmhubbub_create(struct dc_context *ctx, struct resource_pool uint32_t pipe_count = pool->res_cap->num_dwb; for (i = 0; i < pipe_count; i++) { - struct dcn30_mmhubbub *mcif_wb30 = kzalloc(sizeof(struct dcn30_mmhubbub), - GFP_KERNEL); + struct dcn30_mmhubbub *mcif_wb30 = kzalloc_obj(struct dcn30_mmhubbub); if (!mcif_wb30) { dm_error("DC: failed to create mcif_wb30!\n"); @@ -1782,7 +1778,7 @@ static struct display_stream_compressor *dcn42b_dsc_create( struct dc_context *ctx, uint32_t inst) { struct dcn401_dsc *dsc = - kzalloc(sizeof(struct dcn401_dsc), GFP_KERNEL); + kzalloc_obj(struct dcn401_dsc); if (!dsc) { BREAK_TO_DEBUGGER(); @@ -1875,7 +1871,7 @@ static struct link_encoder *dcn42b_link_enc_create_minimal( if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; - enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + enc20 = kzalloc_obj(struct dcn20_link_encoder); if (!enc20) return NULL; @@ -2439,7 +2435,7 @@ struct resource_pool *dcn42b_create_resource_pool( struct dc *dc) { struct dcn42b_resource_pool *pool = - kzalloc(sizeof(struct dcn42b_resource_pool), GFP_KERNEL); + kzalloc_obj(struct dcn42b_resource_pool); if (!pool) return NULL; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c index 4b7668abf4dc..d091ea55cb5d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c @@ -998,7 +998,7 @@ static struct dce_aux *dcn60_aux_engine_create( uint32_t inst) { struct aux_engine_dce110 *aux_engine = - kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL); + kzalloc_obj(struct aux_engine_dce110); if (!aux_engine) return NULL; @@ -1037,7 +1037,7 @@ static struct dce_i2c_hw *dcn60_i2c_hw_create( uint32_t inst) { struct dce_i2c_hw *dce_i2c_hw = - kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL); + kzalloc_obj(struct dce_i2c_hw); if (!dce_i2c_hw) return NULL; @@ -1061,7 +1061,7 @@ static struct clock_source *dcn60_clock_source_create( bool dp_clk_src) { struct dce110_clk_src *clk_src = - kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL); + kzalloc_obj(struct dce110_clk_src); if (!clk_src) return NULL; @@ -1081,8 +1081,7 @@ static struct hubbub *dcn60_hubbub_create(struct dc_context *ctx) { int i; - struct dcn20_hubbub *hubbub2 = kzalloc(sizeof(struct dcn20_hubbub), - GFP_KERNEL); + struct dcn20_hubbub *hubbub2 = kzalloc_obj(struct dcn20_hubbub); if (!hubbub2) return NULL; @@ -1136,7 +1135,7 @@ static struct hubp *dcn60_hubp_create( uint32_t inst) { struct dcn20_hubp *hubp2 = - kzalloc(sizeof(struct dcn20_hubp), GFP_KERNEL); + kzalloc_obj(struct dcn20_hubp); if (!hubp2) return NULL; @@ -1168,7 +1167,7 @@ static struct dpp *dcn60_dpp_create( uint32_t inst) { struct dcn60_dpp *dpp60 = - kzalloc(sizeof(struct dcn60_dpp), GFP_KERNEL); + kzalloc_obj(struct dcn60_dpp); if (!dpp60) return NULL; @@ -1194,8 +1193,7 @@ static struct mpc *dcn60_mpc_create( int num_mpcc, int num_rmu) { - struct dcn60_mpc *mpc60 = kzalloc(sizeof(struct dcn60_mpc), - GFP_KERNEL); + struct dcn60_mpc *mpc60 = kzalloc_obj(struct dcn60_mpc); if (!mpc60) return NULL; @@ -1218,7 +1216,7 @@ static struct output_pixel_processor *dcn60_opp_create( struct dc_context *ctx, uint32_t inst) { struct dcn20_opp *opp2 = - kzalloc(sizeof(struct dcn20_opp), GFP_KERNEL); + kzalloc_obj(struct dcn20_opp); if (!opp2) { BREAK_TO_DEBUGGER(); @@ -1242,7 +1240,7 @@ static struct timing_generator *dcn60_timing_generator_create( uint32_t instance) { struct optc *tgn10 = - kzalloc(sizeof(struct optc), GFP_KERNEL); + kzalloc_obj(struct optc); if (!tgn10) return NULL; @@ -1282,7 +1280,7 @@ static struct link_encoder *dcn60_link_encoder_create( const struct encoder_init_data *enc_init_data) { struct dcn20_link_encoder *enc20 = - kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); + kzalloc_obj(struct dcn20_link_encoder); if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs)) { kfree(enc20); @@ -1349,7 +1347,7 @@ static struct vpg *dcn60_vpg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_vpg *vpg6 = kzalloc(sizeof(struct dcn31_vpg), GFP_KERNEL); + struct dcn31_vpg *vpg6 = kzalloc_obj(struct dcn31_vpg); if (!vpg6) return NULL; @@ -1378,7 +1376,7 @@ static struct apg *dcn60_apg_create( struct dc_context *ctx, uint32_t inst) { - struct dcn31_apg *apg60 = kzalloc(sizeof(struct dcn31_apg), GFP_KERNEL); + struct dcn31_apg *apg60 = kzalloc_obj(struct dcn31_apg); if (!apg60) return NULL; @@ -1415,7 +1413,7 @@ static struct stream_encoder *dcn60_stream_encoder_create( } else return NULL; - enc1 = kzalloc(sizeof(struct dcn10_stream_encoder), GFP_KERNEL); + enc1 = kzalloc_obj(struct dcn10_stream_encoder); vpg = dcn60_vpg_create(ctx, vpg_inst); apg = dcn60_apg_create(ctx, apg_inst); @@ -1462,7 +1460,7 @@ static struct hpo_frl_stream_encoder *dcn60_hpo_frl_stream_encoder_create( return NULL; /* allocate HPO stream encoder and create VPG, APG sub-blocks */ - hpo_enc60 = kzalloc(sizeof(struct dcn401_hpo_frl_stream_encoder), GFP_KERNEL); + hpo_enc60 = kzalloc_obj(struct dcn401_hpo_frl_stream_encoder); vpg = dcn60_vpg_create(ctx, vpg_inst); apg = dcn60_apg_create(ctx, apg_inst); @@ -1493,7 +1491,7 @@ static struct hpo_frl_link_encoder *dcn60_hpo_frl_link_encoder_create( hpo_frl_link_encoder_reg_list(0); /* allocate HPO link encoder */ - hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + hpo_link_enc = kzalloc_obj(struct dcn30_hpo_frl_link_encoder); if (!hpo_link_enc) return NULL; /* out of memory */ @@ -1535,7 +1533,7 @@ static struct hpo_dp_stream_encoder *dcn60_hpo_dp_stream_encoder_create( apg_inst = hpo_dp_inst; /* allocate HPO stream encoder and create VPG sub-block */ - hpo_dp_enc60 = kzalloc(sizeof(struct dcn31_hpo_dp_stream_encoder), GFP_KERNEL); + hpo_dp_enc60 = kzalloc_obj(struct dcn31_hpo_dp_stream_encoder); vpg = dcn60_vpg_create(ctx, vpg_inst); apg = dcn60_apg_create(ctx, apg_inst); @@ -1568,7 +1566,7 @@ static struct hpo_dp_link_encoder *dcn60_hpo_dp_link_encoder_create( struct dcn31_hpo_dp_link_encoder *hpo_dp_enc60; /* allocate HPO link encoder */ - hpo_dp_enc60 = kzalloc(sizeof(struct dcn31_hpo_dp_link_encoder), GFP_KERNEL); + hpo_dp_enc60 = kzalloc_obj(struct dcn31_hpo_dp_link_encoder); if (!hpo_dp_enc60) return NULL; /* out of memory */ @@ -1589,7 +1587,7 @@ static struct hpo_dp_link_encoder *dcn60_hpo_dp_link_encoder_create( static struct dce_hwseq *dcn60_hwseq_create( struct dc_context *ctx) { - struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL); + struct dce_hwseq *hws = kzalloc_obj(struct dce_hwseq); #undef REG_STRUCT #define REG_STRUCT hwseq_reg @@ -1784,7 +1782,7 @@ static struct display_stream_compressor *dcn60_dsc_create( struct dc_context *ctx, uint32_t inst) { struct dcn60_dsc *dsc = - kzalloc(sizeof(struct dcn60_dsc), GFP_KERNEL); + kzalloc_obj(struct dcn60_dsc); if (!dsc) { BREAK_TO_DEBUGGER(); @@ -2363,7 +2361,7 @@ struct resource_pool *dcn60_create_resource_pool( struct dc *dc) { struct dcn60_resource_pool *pool = - kzalloc(sizeof(struct dcn60_resource_pool), GFP_KERNEL); + kzalloc_obj(struct dcn60_resource_pool); if (!pool) return NULL; diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c index ee15c14a899e..2f9690e65ca9 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power.c +++ b/drivers/gpu/drm/amd/display/modules/power/power.c @@ -111,7 +111,7 @@ struct mod_power *mod_power_create(struct dc *dc, if (dc == NULL) goto fail_dc_null; - core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL); + core_power = kzalloc_obj(struct core_power); if (core_power == NULL) goto fail_alloc_context; @@ -129,8 +129,7 @@ struct mod_power *mod_power_create(struct dc *dc, for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) { core_power->map[i].psr_context = - kzalloc(sizeof(struct mod_power_psr_context), - GFP_KERNEL); + kzalloc_obj(struct mod_power_psr_context); if (core_power->map[i].psr_context == NULL) goto fail_construct; } diff --git a/drivers/gpu/drm/amd/display/modules/power/power_abm.c b/drivers/gpu/drm/amd/display/modules/power/power_abm.c index 5e86889eaa84..5f27dea4aa0a 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_abm.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_abm.c @@ -705,8 +705,7 @@ void initialize_backlight_caps(struct core_power *core_power, unsigned int inst) * (do not want to use 256 bytes on the stack) */ ext_backlight_caps = (struct dm_acpi_atif_backlight_caps *) - (kzalloc(sizeof(struct dm_acpi_atif_backlight_caps), - GFP_KERNEL)); + (kzalloc_obj(struct dm_acpi_atif_backlight_caps)); if (ext_backlight_caps == NULL) return; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index ce526db4d24a..808be6c425bf 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -348,7 +348,8 @@ int amdgpu_dpm_switch_power_profile(struct amdgpu_device *adev, const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; int ret = 0; - if (amdgpu_sriov_vf(adev)) + if (amdgpu_sriov_vf(adev) || + amdgpu_in_reset(adev)) return 0; if (pp_funcs && pp_funcs->switch_power_profile) { @@ -367,7 +368,8 @@ int amdgpu_dpm_pause_power_profile(struct amdgpu_device *adev, const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; int ret = 0; - if (amdgpu_sriov_vf(adev)) + if (amdgpu_sriov_vf(adev) || + amdgpu_in_reset(adev)) return 0; if (pp_funcs && pp_funcs->pause_power_profile) { diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 2393fa8d4e4b..bdf1e489369e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -2988,7 +2988,7 @@ static int smu7_init_voltage_dependency_on_display_clock_table(struct pp_hwmgr * if (!amdgpu_device_ip_get_ip_block(hwmgr->adev, AMD_IP_BLOCK_TYPE_DCE)) return 0; - table = kzalloc(struct_size(table, entries, 4), GFP_KERNEL); + table = kzalloc_flex(*table, entries, 4); if (!table) return -ENOMEM; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 6e741ec4a71e..a29e76b03476 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2092,11 +2092,11 @@ static ssize_t smu_v13_0_0_get_gpu_metrics(struct smu_context *smu, gpu_metrics->average_socket_power = metrics->AverageSocketPower; - if ((mp1_ver == IP_VERSION(13, 0, 0) && smu->smc_fw_version <= 0x004e1e00) || - (mp1_ver == IP_VERSION(13, 0, 10) && smu->smc_fw_version <= 0x00500800)) + if ((mp1_ver == IP_VERSION(13, 0, 0) && + (smu->smc_fw_version <= 0x004e1e00 || smu->smc_fw_version >= 0x004e8600)) || + (mp1_ver == IP_VERSION(13, 0, 10) && + smu->smc_fw_version <= 0x00500800)) gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; - else - gpu_metrics->energy_accumulator = UINT_MAX; if (metrics->AverageGfxActivity <= SMU_13_0_0_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index b94ae43586df..5fe409a23772 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2097,8 +2097,8 @@ static ssize_t smu_v13_0_7_get_gpu_metrics(struct smu_context *smu, metrics->Vcn1ActivityPercentage); gpu_metrics->average_socket_power = metrics->AverageSocketPower; - gpu_metrics->energy_accumulator = smu->smc_fw_version <= 0x00521400 ? - metrics->EnergyAccumulator : UINT_MAX; + if (smu->smc_fw_version <= 0x00521400) + gpu_metrics->energy_accumulator = metrics->EnergyAccumulator; if (metrics->AverageGfxActivity <= SMU_13_0_7_BUSY_THRESHOLD) gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index aa4daf8f7d6f..4f820db5f8dd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -222,7 +222,7 @@ static int smu_v15_0_8_tables_init(struct smu_context *smu) smu_table->metrics_time = 0; - driver_pptable = kzalloc(sizeof(PPTable_t), GFP_KERNEL); + driver_pptable = kzalloc_obj(PPTable_t); if (!driver_pptable) return -ENOMEM; @@ -1026,7 +1026,7 @@ static int smu_v15_0_8_fru_get_product_info(struct smu_context *smu, struct amdgpu_device *adev = smu->adev; if (!adev->fru_info) { - adev->fru_info = kzalloc(sizeof(*adev->fru_info), GFP_KERNEL); + adev->fru_info = kzalloc_obj(*adev->fru_info); if (!adev->fru_info) return -ENOMEM; } diff --git a/drivers/gpu/drm/amd/ras/core/cmd.c b/drivers/gpu/drm/amd/ras/core/cmd.c index 6c37dc519eed..446b935c1fc9 100644 --- a/drivers/gpu/drm/amd/ras/core/cmd.c +++ b/drivers/gpu/drm/amd/ras/core/cmd.c @@ -222,7 +222,7 @@ static int ras_cmd_get_cper_records(struct ras_core_context *ras_core, if (!buffer) return RAS_CMD__ERROR_GENERIC; - trace = kcalloc(trace_count, sizeof(*trace), GFP_KERNEL); + trace = kzalloc_objs(*trace, trace_count); if (!trace) { ret = RAS_CMD__ERROR_GENERIC; goto out; @@ -316,7 +316,7 @@ static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core, (input_data->start_batch_id >= overview.last_batch_id)) return RAS_CMD__ERROR_INVALID_INPUT_SIZE; - trace_arry = kcalloc(trace_count, sizeof(*trace_arry), GFP_KERNEL); + trace_arry = kzalloc_objs(*trace_arry, trace_count); if (!trace_arry) return RAS_CMD__ERROR_GENERIC; diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c index fd19c45f2abe..690d4cd1db5e 100644 --- a/drivers/gpu/drm/ast/ast_cursor.c +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -25,6 +25,7 @@ #include <linux/sizes.h> #include <drm/drm_atomic.h> +#include <drm/drm_blend.h> #include <drm/drm_damage_helper.h> #include <drm/drm_format_helper.h> #include <drm/drm_gem_atomic_helper.h> @@ -355,6 +356,8 @@ int ast_cursor_plane_init(struct ast_device *ast) } drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs); drm_plane_enable_fb_damage_clips(cursor_plane); + drm_plane_create_blend_mode_property(cursor_plane, + BIT(DRM_MODE_BLEND_COVERAGE)); return 0; } diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index d1fc6af37cc5..19b43efcf93f 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1263,10 +1263,13 @@ tc358768_atomic_get_input_bus_fmts(struct drm_bridge *bridge, return input_fmts; } -static bool tc358768_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static int tc358768_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + /* Default to positive sync */ if (!(adjusted_mode->flags & @@ -1277,13 +1280,15 @@ static bool tc358768_mode_fixup(struct drm_bridge *bridge, (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC; - return true; + bridge_state->input_bus_cfg.flags = bridge->timings->input_bus_flags; + + return 0; } static const struct drm_bridge_funcs tc358768_bridge_funcs = { .attach = tc358768_bridge_attach, .mode_valid = tc358768_bridge_mode_valid, - .mode_fixup = tc358768_mode_fixup, + .atomic_check = tc358768_bridge_atomic_check, .atomic_pre_enable = tc358768_bridge_atomic_pre_enable, .atomic_enable = tc358768_bridge_atomic_enable, .atomic_disable = tc358768_bridge_atomic_disable, diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index f9fdbf48c6b3..526826ba4524 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -403,7 +403,7 @@ retry: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - return 0; + return err; } static void sn65dsi83_reset_work(struct work_struct *ws) @@ -419,11 +419,13 @@ static void sn65dsi83_reset_work(struct work_struct *ws) ret = sn65dsi83_reset_pipe(ctx); if (ret) { dev_err(ctx->dev, "reset pipe failed %pe\n", ERR_PTR(ret)); - return; + goto bridge_exit; } + if (ctx->irq) enable_irq(ctx->irq); +bridge_exit: drm_bridge_exit(idx); } diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index d90d1d7c9cf9..a2ef272e9f27 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -278,7 +278,14 @@ void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state, plane_state->rotation = DRM_MODE_ROTATE_0; plane_state->alpha = DRM_BLEND_ALPHA_OPAQUE; + plane_state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + if (plane->blend_mode_property) { + if (!drm_object_property_get_default_value(&plane->base, + plane->blend_mode_property, + &val)) + plane_state->pixel_blend_mode = val; + } if (plane->color_encoding_property) { if (!drm_object_property_get_default_value(&plane->base, diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 1050dddadb17..5ea593b3a98e 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -1449,9 +1449,6 @@ static int prepare_signaling(struct drm_device *dev, if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { struct drm_pending_vblank_event *e = crtc_state->event; - if (!file_priv) - continue; - ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); if (ret) { @@ -1567,6 +1564,8 @@ static void complete_signaling(struct drm_device *dev, * to prevent a double free in drm_atomic_commit_clear. */ if (event && (event->base.fence || event->base.file_priv)) { + if (crtc_state->commit && crtc_state->commit->abort_completion) + drm_crtc_commit_put(crtc_state->commit); drm_event_cancel_free(dev, &event->base); crtc_state->event = NULL; } diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index fa923852fae4..2453ec41360f 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -79,7 +79,7 @@ void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr) nr = PAGE_SIZE / sizeof(void *); exec->flags = flags; - exec->objects = kvmalloc_objs(*exec->objects, nr, GFP_KERNEL); + exec->objects = kvmalloc_objs(*exec->objects, nr); /* If allocation here fails, just delay that till the first use */ exec->max_objects = exec->objects ? nr : 0; @@ -322,6 +322,19 @@ int drm_exec_prepare_array(struct drm_exec *exec, { int ret; + /* + * Make sure to lock a contended object even when no objects are + * given, otherwise drm_exec_retry_on_contention() would loop + * forever on patterns like: + * + * ret = drm_exec_prepare_array(exec, objs, num_objects, ...); + * drm_exec_retry_on_contention(exec); + * + * with num_objects == 0. + */ + if (!num_objects) + return drm_exec_lock_contended(exec); + for (unsigned int i = 0; i < num_objects; ++i) { ret = drm_exec_prepare_obj(exec, objects[i], num_fences); if (unlikely(ret)) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 892b325fa99b..a0546955d0b9 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -384,6 +384,58 @@ next: } /** + * drm_pagemap_migrate_populate_src_pages() - Populate the source page array + * @pages: Array of source pages to populate + * @src_mpfn: Source array of migrate PFNs + * @dst_mpfn: Destination array of migrate PFNs + * @npages: Number of pages in the arrays + * + * Populate @pages with the device pages the copy callback is to read from. + * + * Entries are normally only populated at the head of each source folio, with + * the copy callback deriving the rest of the folio from the order recorded in + * the corresponding drm_pagemap_addr. That does not work where + * drm_pagemap_migrate_populate_ram_pfn() had to demote a higher-order source + * folio to order-0 destination folios: the drm_pagemap_addr entries are then + * per-page, and the copy callback needs a source page for each of them. + * Populate every entry for those ranges. + * + * Note that the source folio itself is only split later, by + * migrate_vma_pages() / migrate_device_pages(), so its order cannot be used to + * detect the demotion - the destination has to be inspected instead. + */ +static void drm_pagemap_migrate_populate_src_pages(struct page **pages, + unsigned long *src_mpfn, + unsigned long *dst_mpfn, + unsigned long npages) +{ + unsigned long i; + + for (i = 0; i < npages;) { + struct page *page = migrate_pfn_to_page(src_mpfn[i]); + unsigned int order = 0; + unsigned long j, nr; + + if (!page) { + i++; + continue; + } + + order = folio_order(page_folio(page)); + nr = NR_PAGES(order); + + if (order && !(dst_mpfn[i] & MIGRATE_PFN_COMPOUND)) { + for (j = 0; j < nr && i + j < npages; j++) + pages[i + j] = folio_page(page_folio(page), j); + } else { + pages[i] = page; + } + + i += nr; + } +} + +/** * drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration * @dev: The device for which the pages were mapped * @migrate_pfn: Array of migrate pfns set up for the mapped pages. Used to @@ -875,6 +927,7 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, struct page *page = NULL, *src_page; struct folio *folio; unsigned int order = 0; + gfp_t gfp = GFP_HIGHUSER; if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE)) goto next; @@ -891,11 +944,51 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, order = folio_order(page_folio(src_page)); - /* TODO: Support fallback to single pages if THP allocation fails */ + /* + * A large source folio is always collected whole, at its head + * page, PMD aligned and flagged MIGRATE_PFN_COMPOUND: anything + * else is split before it reaches us, either by + * migrate_vma_collect_pmd() or, for the eviction path, by + * migrate_device_pfns(). Both the order-0 fallback below and + * drm_pagemap_migrate_populate_src_pages() rely on that, as + * they index the folio from @i. + */ + WARN_ON_ONCE(order && + (src_page != folio_page(page_folio(src_page), 0) || + !(src_mpfn[i] & MIGRATE_PFN_COMPOUND))); + + if (order) + gfp |= __GFP_NOWARN; + if (vas) - folio = vma_alloc_folio(GFP_HIGHUSER, order, vas, addr); + folio = vma_alloc_folio(gfp, order, vas, addr); else - folio = folio_alloc(GFP_HIGHUSER, order); + folio = folio_alloc(gfp, order); + + if (!folio && order) { + /* + * Higher-order allocation failed, fall back to + * order-0 allocations for the entire range covered + * by the original higher-order allocation, without + * setting MIGRATE_PFN_COMPOUND, until we move past + * that range. + */ + unsigned long nr = NR_PAGES(order); + unsigned long j; + + gfp &= ~__GFP_NOWARN; + for (j = 0; j < nr && i < npages; j++, i++, addr += PAGE_SIZE) { + folio = vas ? + vma_alloc_folio(gfp, 0, vas, addr) : + folio_alloc(gfp, 0); + if (!folio) + goto free_pages; + + page = folio_page(folio, 0); + mpfn[i] = migrate_pfn(page_to_pfn(page)); + } + continue; + } if (!folio) goto free_pages; @@ -940,11 +1033,11 @@ free_pages: if (!page) goto next_put; + order = folio_order(page_folio(page)); + put_page(page); mpfn[i] = 0; - order = folio_order(page_folio(page)); - next_put: i += NR_PAGES(order); } @@ -1103,11 +1196,116 @@ void drm_pagemap_put(struct drm_pagemap *dpagemap) EXPORT_SYMBOL(drm_pagemap_put); /** + * drm_pagemap_page_get_flags() - Read flags from a device-private folio + * @page: Pointer to a page of the device-private folio + * + * Return: The DRM_PAGEMAP_ZDD_FLAG_* bits encoded in zone_device_data. + */ +static unsigned long drm_pagemap_page_get_flags(struct page *page) +{ + struct folio *folio = page_folio(page); + + return (unsigned long)folio_zone_device_data(folio) & + DRM_PAGEMAP_ZDD_FLAG_MASK; +} + +/** + * drm_pagemap_page_set_flags() - Set flags on a device-private folio + * @page: Pointer to a page of the device-private folio + * @flags: DRM_PAGEMAP_ZDD_FLAG_* bits to set + * + * Preserve any flags already encoded alongside the ZDD pointer. + */ +static void drm_pagemap_page_set_flags(struct page *page, + unsigned long flags) +{ + struct folio *folio = page_folio(page); + unsigned long old; + + if (WARN_ON_ONCE(flags & ~DRM_PAGEMAP_ZDD_FLAG_MASK)) + return; + + old = (unsigned long)folio_zone_device_data(folio); + folio_set_zone_device_data(folio, (void *)(old | flags)); +} + +/** + * drm_pagemap_retire_migrated_pages() - Record migrated device-private folios + * @src_pfns: source array after migrate_vma_pages() or migrate_device_pages() + * @npages: number of entries in @src_pfns + * + * Flag device-private folios successfully migrated to RAM before finalize + * unlocks the sources. The migrated state is stored in the physical folio, so + * it survives later folio splits and subsequent migrations can skip it. + */ +static void drm_pagemap_retire_migrated_pages(unsigned long *src_pfns, + unsigned long npages) +{ + unsigned long i = 0; + + while (i < npages) { + struct page *page = migrate_pfn_to_page(src_pfns[i]); + unsigned long nr = 1; + + if (!page) { + i++; + continue; + } + + if (src_pfns[i] & MIGRATE_PFN_COMPOUND) + nr = folio_nr_pages(page_folio(page)); + + if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) && + is_device_private_page(page)) + drm_pagemap_page_set_flags(page, + DRM_PAGEMAP_ZDD_FLAG_MIGRATED); + + i += nr; + } +} + +/** + * drm_pagemap_skip_retired_pages() - Skip retired device-private folios + * @src_pfns: MIGRATE_PFN-encoded source array + * @npages: number of entries in @src_pfns + * + * Skip source folios already migrated to RAM, identified by the migrated flag + * stored in the physical folio's zone_device_data. + */ +static void drm_pagemap_skip_retired_pages(unsigned long *src_pfns, + unsigned long npages) +{ + unsigned long i = 0; + + while (i < npages) { + struct page *page = migrate_pfn_to_page(src_pfns[i]); + unsigned long nr = 1; + + if (!page) { + i++; + continue; + } + + if (src_pfns[i] & MIGRATE_PFN_COMPOUND) + nr = folio_nr_pages(page_folio(page)); + + if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) && + is_device_private_page(page) && + (drm_pagemap_page_get_flags(page) & + DRM_PAGEMAP_ZDD_FLAG_MIGRATED)) + src_pfns[i] &= ~MIGRATE_PFN_MIGRATE; + + i += nr; + } +} + +/** * drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM * @devmem_allocation: Pointer to the device memory allocation * - * Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and - * migration done via migrate_device_* functions. + * Similar to __drm_pagemap_migrate_to_ram(), but uses the + * migrate_device_* helpers and does not require the mmap lock. + * Device-private PFNs already migrated to RAM by either path are skipped. * * Return: 0 on success, negative error code on failure. */ @@ -1115,17 +1313,18 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) { const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops; struct drm_pagemap_iova_state state = {}; - unsigned long npages, mpages = 0; + unsigned long npages, mpages; struct page **pages; unsigned long *src, *dst; struct drm_pagemap_addr *pagemap_addr; void *buf; - int i, err = 0; + int err = 0; unsigned int retry_count = 2; npages = devmem_allocation->size >> PAGE_SHIFT; retry: + mpages = 0; if (!mmget_not_zero(devmem_allocation->mm)) return -EFAULT; @@ -1148,6 +1347,8 @@ retry: if (err) goto err_free; + drm_pagemap_skip_retired_pages(src, npages); + err = drm_pagemap_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages, src, dst, 0); if (err || !mpages) @@ -1160,27 +1361,20 @@ retry: if (err) goto err_finalize; - for (i = 0; i < npages;) { - unsigned int order = 0; - - pages[i] = migrate_pfn_to_page(src[i]); - if (pages[i]) - order = folio_order(page_folio(pages[i])); - - i += NR_PAGES(order); - } + drm_pagemap_migrate_populate_src_pages(pages, src, dst, npages); err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) goto err_finalize; err_finalize: + drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages, + DMA_FROM_DEVICE, &state); if (err) drm_pagemap_migration_unlock_put_pages(npages, dst); migrate_device_pages(src, dst, npages); + drm_pagemap_retire_migrated_pages(src, npages); migrate_device_finalize(src, dst, npages); - drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages, - DMA_FROM_DEVICE, &state); err_free: kvfree(buf); @@ -1235,7 +1429,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, struct drm_pagemap_addr *pagemap_addr; unsigned long start, end; void *buf; - int i, err = 0; + int err = 0; zdd = drm_pagemap_page_zone_device_data(page); if (time_before64(get_jiffies_64(), zdd->devmem_allocation->timeslice_expiration)) @@ -1275,13 +1469,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, if (!migrate.cpages) goto err_free; + drm_pagemap_skip_retired_pages(migrate.src, npages); + ops = zdd->devmem_allocation->ops; dev = zdd->devmem_allocation->dev; err = drm_pagemap_migrate_populate_ram_pfn(vas, page, npages, &mpages, migrate.src, migrate.dst, start); - if (err) + if (err || !mpages) goto err_finalize; err = drm_pagemap_migrate_map_system_pages(dev, pagemap_addr, @@ -1290,29 +1486,23 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, if (err) goto err_finalize; - for (i = 0; i < npages;) { - unsigned int order = 0; - - pages[i] = migrate_pfn_to_page(migrate.src[i]); - if (pages[i]) - order = folio_order(page_folio(pages[i])); - - i += NR_PAGES(order); - } + drm_pagemap_migrate_populate_src_pages(pages, migrate.src, migrate.dst, + npages); err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) goto err_finalize; err_finalize: - if (err) - drm_pagemap_migration_unlock_put_pages(npages, migrate.dst); - migrate_vma_pages(&migrate); - migrate_vma_finalize(&migrate); if (dev) drm_pagemap_migrate_unmap_pages(dev, pagemap_addr, migrate.dst, npages, DMA_FROM_DEVICE, &state); + if (err) + drm_pagemap_migration_unlock_put_pages(npages, migrate.dst); + migrate_vma_pages(&migrate); + drm_pagemap_retire_migrated_pages(migrate.src, npages); + migrate_vma_finalize(&migrate); err_free: kvfree(buf); err_out: @@ -1360,13 +1550,19 @@ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf) static void drm_pagemap_folio_split(struct folio *orig_folio, struct folio *new_folio) { struct drm_pagemap_zdd *zdd; + unsigned long orig_data, new_data; if (!new_folio) return; new_folio->pgmap = orig_folio->pgmap; - zdd = folio_zone_device_data(orig_folio); - folio_set_zone_device_data(new_folio, drm_pagemap_zdd_get(zdd)); + + orig_data = (unsigned long)folio_zone_device_data(orig_folio); + zdd = (struct drm_pagemap_zdd *)(orig_data & ~DRM_PAGEMAP_ZDD_FLAG_MASK); + + new_data = (unsigned long)drm_pagemap_zdd_get(zdd); + new_data |= orig_data & DRM_PAGEMAP_ZDD_FLAG_MASK; + folio_set_zone_device_data(new_folio, (void *)new_data); } static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = { diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_qr.rs index ac27e86c601c..4d7eb75a3afc 100644 --- a/drivers/gpu/drm/drm_panic_qr.rs +++ b/drivers/gpu/drm/drm_panic_qr.rs @@ -407,8 +407,8 @@ impl DecFifo { for i in (0..self.len).rev() { self.decimals[i + len] = self.decimals[i]; } - for i in 0..len { - self.decimals[i] = (chunk % 10) as u8; + for decimal in &mut self.decimals[..len] { + *decimal = (chunk % 10) as u8; chunk = div10(chunk); } self.len += len; diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 9b44c78cd77f..fe3436d1235d 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -524,7 +524,7 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, return PTR_ERR(dmabuf); } - fd_install(fd, dmabuf->file); + dma_buf_fd_install(dmabuf, fd); *prime_fd = fd; return 0; } diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 2fa170a29a62..c23a5de27eff 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -1619,7 +1619,7 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data, goto err_points; } - chains = kmalloc_objs(*chains, args->count_handles, GFP_KERNEL); + chains = kmalloc_objs(*chains, args->count_handles); if (!chains) { ret = -ENOMEM; goto err_points; diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c index ea0cca58b7c8..8141c3a1e30a 100644 --- a/drivers/gpu/drm/gud/gud_connector.c +++ b/drivers/gpu/drm/gud/gud_connector.c @@ -396,8 +396,16 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect } num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN; - for (i = 0; i < num_modes; i++) - modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; + for (i = 0; i < num_modes; i++) { + char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; + + if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) { + ret = -EIO; + goto free; + } + + modes[i] = mode; + } ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes); free: diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c index 89bd6ca36003..3a1b9e2a2eaa 100644 --- a/drivers/gpu/drm/gud/gud_drv.c +++ b/drivers/gpu/drm/gud/gud_drv.c @@ -289,6 +289,8 @@ static int gud_plane_add_properties(struct gud_device *gdrm) * but mask out any additions on future devices. */ val &= GUD_ROTATION_MASK; + if (!(val & GUD_ROTATION_0)) + continue; ret = drm_plane_create_rotation_property(&gdrm->plane, DRM_MODE_ROTATE_0, val); break; diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a53d88727177..a1a5720996b7 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2381,8 +2381,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); - cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + if (DISPLAY_VER(display) < 30) { + cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); + cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + } if (cdctl != expected) { if (DISPLAY_VER(display) < 20) { @@ -2713,8 +2715,8 @@ static void intel_set_cdclk(struct intel_display *display, } } -static bool dg2_power_well_count(struct intel_display *display, - const struct intel_cdclk_state *cdclk_state) +static int dg2_power_well_count(struct intel_display *display, + const struct intel_cdclk_state *cdclk_state) { return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0; } diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 0673f16f6fd0..86bb96ac449b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -530,13 +530,18 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state, } static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The cursor may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0); @@ -586,7 +591,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb, if (crtc_state->enable_psr2_su_region_et) wa_16021440873(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); } } @@ -695,7 +700,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb, if (plane_state) i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); if (plane->cursor.base != base || plane->cursor.size != fbc_ctl || diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 452062417ce9..dbebd7210848 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -3233,7 +3233,8 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder, * 8. Program DDI_CLK_VALFREQ to match intended DDI * clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); /* * 9. Set PORT_CLOCK_CTL register PCLK PLL Request @@ -3406,7 +3407,7 @@ void intel_mtl_tbt_pll_enable_clock(struct intel_encoder *encoder, int port_cloc * clock frequency. */ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), - port_clock); + intel_ddi_link_symbol_clock(encoder, port_clock)); } void intel_mtl_pll_enable(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index b8985e1e0a81..02a53c9848e1 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -1529,6 +1529,17 @@ int intel_ddi_level(struct intel_encoder *encoder, return level; } +int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock) +{ + if (intel_encoder_is_dp(encoder)) + return intel_dp_link_symbol_clock(clock); + + if (intel_hdmi_is_frl(clock)) + return DIV_ROUND_CLOSEST(clock * 10, 18); + + return clock; +} + static void hsw_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index 580ecb09b8b6..239d5a403f91 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -81,6 +81,7 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder); int intel_ddi_level(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, int lane); +int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock); void intel_ddi_update_active_dpll(struct intel_atomic_state *state, struct intel_encoder *encoder, struct intel_crtc *crtc); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b7cc361fd955..43d53a98dae7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1946,6 +1946,8 @@ struct intel_dp { bool colorimetry_support; + bool sst_split_sdp_support; + struct { enum transcoder transcoder; struct mutex lock; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 113d767e62e9..3152122e6aef 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3409,12 +3409,22 @@ intel_dp_audio_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + pipe_config->has_audio = intel_dp_has_audio(encoder, conn_state) && intel_audio_compute_config(encoder, pipe_config, conn_state); pipe_config->sdp_split_enable = pipe_config->has_audio && intel_dp_is_uhbr(pipe_config); + + /* + * SDP splitting for UHBR audio requires explicit sink capability in + * SST mode, whereas in MST mode it is inherently supported. + */ + if (pipe_config->sdp_split_enable && + !intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) + pipe_config->sdp_split_enable = intel_dp->sst_split_sdp_support; } void @@ -4462,16 +4472,25 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, str_enable_disable(tmp)); } -static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp) +static u8 intel_dp_read_dprx_feature_enum(struct intel_dp *intel_dp) { u8 dprx = 0; - if (drm_dp_dpcd_readb(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, - &dprx) != 1) - return false; + drm_dp_dpcd_read_data(&intel_dp->aux, DP_DPRX_FEATURE_ENUMERATION_LIST, + &dprx, sizeof(dprx)); + return dprx; +} + +static bool intel_dp_get_colorimetry_status(u8 dprx) +{ return dprx & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED; } +static bool intel_dp_get_sst_split_sdp_status(u8 dprx) +{ + return dprx & DP_SST_SPLIT_SDP_CAP; +} + static int intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux, u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) { @@ -4771,6 +4790,7 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector { struct intel_display *display = to_intel_display(intel_dp); int ret; + u8 dprx; /* this function is meant to be called only once */ drm_WARN_ON(display->drm, intel_dp->dpcd[DP_DPCD_REV] != 0); @@ -4782,8 +4802,13 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); /* * Read the eDP display control registers. @@ -4874,13 +4899,20 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) * the OUI/ID since we know it won't change. */ if (!intel_dp_is_edp(intel_dp)) { + u8 dprx; + drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd)); intel_init_dpcd_quirks(intel_dp, &intel_dp->desc.ident); + dprx = intel_dp_read_dprx_feature_enum(intel_dp); + intel_dp->colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_colorimetry_status(dprx); + + intel_dp->sst_split_sdp_support = + intel_dp_get_sst_split_sdp_status(dprx); intel_dp_update_sink_caps(intel_dp); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 3be1643f8d03..57daed0b0b36 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -765,10 +765,6 @@ static int mst_stream_compute_config(struct intel_atomic_state *state, if (ret) return ret; - ret = intel_pfit_compute_config(pipe_config, conn_state); - if (ret) - return ret; - for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { if (num_joined_pipes > 1) pipe_config->joiner_pipes = GENMASK(crtc->pipe + num_joined_pipes - 1, diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 8fc6d230493f..86492651b01d 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1976,7 +1976,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, * Change. We handle this step in bxt_set_cdclk(). */ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */ - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port), @@ -2023,7 +2024,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder, lane_phy_pulse_status, lane_phy_pulse_status); } else { - intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock); + intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), + intel_ddi_link_symbol_clock(encoder, port_clock)); } /* diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 07a683293352..5cda1ab90e40 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -879,13 +879,18 @@ skl_plane_disable_arm(struct intel_dsb *dsb, } static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The plane may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); @@ -921,7 +926,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb, skl_write_plane_wm(dsb, plane, crtc_state); - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); if (plane_has_normalizer(plane)) intel_de_write_dsb(display, dsb, @@ -1641,7 +1646,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), SEL_FETCH_PLANE_CTL_ENABLE); else - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); } static void diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 82415af47d54..2f03f95945f1 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *) ent->driver_data; int err; + if (!intel_info) + return -ENODEV; + if (intel_info->require_force_probe && !id_forced(pdev->device)) { dev_info(&pdev->dev, "Your graphics device %04x is not properly supported by i915 in this\n" diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c index 0c55fb6e9727..11157fb14db3 100644 --- a/drivers/gpu/drm/i915/i915_query.c +++ b/drivers/gpu/drm/i915/i915_query.c @@ -403,8 +403,10 @@ static int query_perf_config_list(struct drm_i915_private *i915, ids = krealloc(oa_config_ids, n_configs * sizeof(*oa_config_ids), GFP_KERNEL); - if (!ids) + if (!ids) { + kfree(oa_config_ids); return -ENOMEM; + } alloc = fetch_and_zero(&n_configs); diff --git a/drivers/gpu/drm/logicvc/Kconfig b/drivers/gpu/drm/logicvc/Kconfig index 579a358ed5cf..11aae1626199 100644 --- a/drivers/gpu/drm/logicvc/Kconfig +++ b/drivers/gpu/drm/logicvc/Kconfig @@ -4,7 +4,6 @@ config DRM_LOGICVC depends on OF || COMPILE_TEST select DRM_CLIENT_SELECTION select DRM_KMS_HELPER - select DRM_KMS_DMA_HELPER select DRM_GEM_DMA_HELPER select REGMAP select REGMAP_MMIO diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h index 7903d7470d19..01145db32c53 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h @@ -87,4 +87,5 @@ int gp102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct int gv100_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); int tu102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); int ga102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); +int gb202_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 598513f60449..f142f6310596 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan) { struct nouveau_channel *chan = *pchan; if (chan) { + /* + * Drop the kill-event subscription first. Its handler + * dereferences chan->fence, which the fence context teardown + * below frees, so leaving it armed across the teardown leaves + * a window for a use-after-free. + */ + nvif_event_dtor(&chan->kill); + if (chan->fence) nouveau_fence(chan->cli->drm)->context_del(chan); @@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan) nvif_object_dtor(&chan->nvsw); nvif_object_dtor(&chan->gart); nvif_object_dtor(&chan->vram); - nvif_event_dtor(&chan->kill); nvif_object_dtor(&chan->user); nvif_mem_dtor(&chan->mem_userd); nouveau_vma_del(&chan->sema.vma); diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index 9442ec6e1f6c..ad4570c50be7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -267,7 +267,7 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf) nouveau_fence_new(&fence, dmem->migrate.chan); migrate_vma_pages(&args); nouveau_dmem_fence_done(&fence); - dma_unmap_page(drm->dev->dev, dma_info.dma_addr, PAGE_SIZE, + dma_unmap_page(drm->dev->dev, dma_info.dma_addr, dma_info.size, DMA_BIDIRECTIONAL); done: migrate_vma_finalize(&args); @@ -279,11 +279,25 @@ err: static void nouveau_dmem_folio_split(struct folio *head, struct folio *tail) { + struct nouveau_dmem_chunk *chunk; + struct nouveau_dmem *dmem; + if (tail == NULL) return; tail->pgmap = head->pgmap; tail->mapping = head->mapping; folio_set_zone_device_data(tail, folio_zone_device_data(head)); + + /* + * The split hands out a new independently-freeable folio that will + * later be released via nouveau_dmem_folio_free(); account for it so + * chunk->callocated stays balanced. + */ + chunk = nouveau_page_to_chunk(&head->page); + dmem = chunk->drm->dmem; + spin_lock(&dmem->lock); + chunk->callocated++; + spin_unlock(&dmem->lock); } static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = { @@ -772,7 +786,7 @@ static unsigned long nouveau_dmem_migrate_copy_one(struct nouveau_drm *drm, return mpfn; out_dma_unmap: - dma_unmap_page(dev, dma_info->dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + dma_unmap_page(dev, dma_info->dma_addr, dma_info->size, DMA_BIDIRECTIONAL); out_free_page: nouveau_dmem_page_free_locked(drm, dpage); out: diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index fa3b4ebf38a8..2bd0376193ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags) struct nouveau_sgdma_be *nvbe; enum ttm_caching caching; - if (nvbo->force_coherent) - caching = ttm_uncached; - else if (drm->agp.bridge) + if (nvbo->force_coherent || drm->agp.bridge) caching = ttm_write_combined; else caching = ttm_cached; diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index f5e4756b4de4..fc125fd44a9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -1319,6 +1319,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, op->va.range); if (!op->reg || op->reg->dirty) { ret = -ENOENT; + op->reg = NULL; goto unwind_continue; } @@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, op->va.range); if (IS_ERR(op->ops)) { ret = PTR_ERR(op->ops); + op->reg = NULL; goto unwind_continue; } @@ -1473,6 +1475,7 @@ unwind: op->va.range); break; case OP_UNMAP_SPARSE: + op->reg->dirty = false; __nouveau_uvma_region_insert(uvmm, op->reg); nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new, op->ops); @@ -1489,7 +1492,8 @@ unwind: break; } - drm_gpuva_ops_free(&uvmm->base, op->ops); + if (!IS_ERR_OR_NULL(op->ops)) + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index ea62dc97f118..96c8a5b29999 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2846,7 +2846,7 @@ nv1b2_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2862,7 +2862,7 @@ nv1b3_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2878,7 +2878,7 @@ nv1b5_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2894,7 +2894,7 @@ nv1b6_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2910,7 +2910,7 @@ nv1b7_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index e1aecd3fe96c..98d6ca5ac311 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -27,6 +27,7 @@ nvkm-y += nvkm/engine/disp/gp102.o nvkm-y += nvkm/engine/disp/gv100.o nvkm-y += nvkm/engine/disp/tu102.o nvkm-y += nvkm/engine/disp/ga102.o +nvkm-y += nvkm/engine/disp/gb202.o nvkm-y += nvkm/engine/disp/udisp.o nvkm-y += nvkm/engine/disp/uconn.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c index ab0a85c92430..820834b5ee9b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c @@ -144,12 +144,23 @@ ga102_disp = { }, }; +static const struct nvkm_disp_func +ga102_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + .gsp.intr = tu102_disp_intr, + .gsp.head = &tu102_gsp_head, + .gsp.hdmi_gcp = tu102_sor_hdmi_gcp, + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, +}; + int ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp) { if (nvkm_gsp_rm(device->gsp)) - return r535_disp_new(&ga102_disp, device, type, inst, pdisp); + return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp); return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c new file mode 100644 index 000000000000..d0360610f9fa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Valve Corp. + */ +#include "priv.h" +#include "head.h" +#include "ior.h" + +#include <subdev/timer.h> + +/* GB20x (NVD5.0) reorganised the SF HDMI packet units. The AVI unit is + * unchanged from GV100, but the legacy VSI unit is gone. Vendor infoframes + * are sent through the shared generic infoframe units instead. Register + * layout per NVIDIA's clc971.h/clca71.h, programming sequence per + * nvhdmipkt_C971.c:programAdvancedInfoframeC971(). + */ +static void +gb202_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = head * 0x400; + /* Generic infoframe unit 1, the slot NVIDIA's driver uses for the VSI. */ + const u32 ctrl = 0x6f0138 + hoff; + u8 buf[36] = {}; + int i; + + /* Disable the unit and wait for it to go idle. */ + nvkm_mask(device, ctrl, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, ctrl) & 0x00400000)) + break; + ) < 0) + return; + + if (!size) + return; + + /* Clear SENT status, and point the data port at unit 1's slot. */ + nvkm_mask(device, ctrl, 0x00800000, 0x00800000); + nvkm_wr32(device, 0x6f03f0 + hoff, 0x00000001); + + /* The data port takes the raw packet, except that a zero is inserted + * in HB3 after the three header bytes. A slot is 9 dwords (HB0-3 plus + * up to 32 payload bytes). An HDMI infoframe carries at most PB0-27, + * so the tail stays zero, and we always write the whole slot. + */ + size = min_t(u32, size, 31); + memcpy(buf, data, min_t(u32, size, 3)); + if (size > 3) + memcpy(&buf[4], (u8 *)data + 3, size - 3); + + for (i = 0; i < 36; i += 4) { + nvkm_wr32(device, 0x6f03f4 + hoff, buf[i + 0] | buf[i + 1] << 8 | + buf[i + 2] << 16 | + (u32)buf[i + 3] << 24); + } + + /* No flip ID or scanline matching. */ + nvkm_wr32(device, 0x6f013c + hoff, 0x00000000); + + /* ENABLE | RUN_MODE=ALWAYS | LOC=VBLANK | OFFSET=1 | SIZE=0. */ + nvkm_wr32(device, ctrl, 0x00000041); + + /* Audio priority low (the init value). */ + nvkm_wr32(device, 0x6f03f8 + hoff, 0x00000002); +} + +/* General Control Packet AVMute bracket. The GCP unit moved to slot 1 on + * NVD5.0. Only SB0 (the AVMute bit) is ours to write so we must not do a + * full write here: SB1 carries the deep-color CD/PP fields, and SB1_CTRL + * (bit 24, new with clc871.h) controls where their generation happens (HW + * or driver) on these chips, with the default being HW. + */ +static void +gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hdmi = head * 0x400; + + nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6f004c + hdmi, 0x000000ff, !enable ? 0x00000001 : + 0x00000010); + nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001); +} + +/* Same core-channel state mirror as gv100_head_state() (assembly at 0x680000, + * armed at +0x8000, per-head method offsets unchanged), but NVD5.0 spaces + * heads 0x800 apart (see NVCA7D_HEAD_SET_*(a) in clca7d.h). + */ +static void +gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x800; + u32 data; + + data = nvkm_rd32(device, 0x682064 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682068 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x68206c + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682070 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + /* Bit 31 is ADJ1000DIV1001, not a HERTZ bit. We don't have enough bits + * to add the full clock in hz on Blackwell (35 bits), but state->hz + * is unused and obsolete under GSP so this is fine. + */ + state->hz = nvkm_rd32(device, 0x68200c + hoff) & 0x7fffffff; + + data = nvkm_rd32(device, 0x682004 + hoff); + switch ((data & 0x000000f0) >> 4) { + case 5: state->or.depth = 30; break; + case 4: state->or.depth = 24; break; + case 1: state->or.depth = 18; break; + default: + state->or.depth = 18; + WARN_ON(1); + break; + } +} + +/* NVD5.0 (GB20x and later) moved the RM head-timing interrupt enable to + * the low-latency vector's EN1 block. The event latch is unchanged. + */ +static void +gb202_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000000); +} + +static void +gb202_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); + nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000002); +} + +static irqreturn_t +gb202_disp_intr(struct nvkm_inth *inth) +{ + struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); + irqreturn_t ret = tu102_disp_intr(inth); + + /* The FE interrupt vectors are message-based on NVD5.0. Re-arm the + * low-latency vector so it fires again for any event that latched + * while we were servicing. + */ + nvkm_wr32(disp->engine.subdev.device, 0x611f34, 0x00000001); + return ret; +} + +static const struct nvkm_head_func +gb202_gsp_head = { + .state = gb202_head_state, + .rgpos = gv100_head_rgpos, + .vblank_get = gb202_head_vblank_get, + .vblank_put = gb202_head_vblank_put, +}; + +/* GB20x is GSP-only. This table supplies the register programming the + * GSP-RM display path needs from the chip. + */ +static const struct nvkm_disp_func +gb202_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + /* Head timing arrives on the dedicated low-latency vector. */ + .gsp.intr = gb202_disp_intr, + .gsp.intr_low_latency = true, + .gsp.head = &gb202_gsp_head, + .gsp.hdmi_gcp = gb202_sor_hdmi_gcp, + /* The legacy AVI unit is unchanged on GB20x. */ + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gb202_sor_hdmi_infoframe_vsi, +}; + +int +gb202_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return r535_disp_new(&gb202_gsp_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h index b642729c254f..5976498da909 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -56,6 +56,8 @@ int gv100_head_new(struct nvkm_disp *, int id); void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state); void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline); +extern const struct nvkm_head_func tu102_gsp_head; + #define HEAD_MSG(h,l,f,a...) do { \ struct nvkm_head *_h = (h); \ nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index 3ba04bead2f9..5d682a774f2d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -194,6 +194,7 @@ void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8); extern const struct nvkm_ior_func_hda gv100_sor_hda; +void tu102_sor_hdmi_gcp(struct nvkm_ior *, int, bool); void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); int nv50_pior_cnt(struct nvkm_disp *, unsigned long *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h index a3fd7cb7c488..fde321dbd7c8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h @@ -5,6 +5,8 @@ #include <engine/disp.h> #include <core/enum.h> struct nvkm_head; +struct nvkm_head_func; +struct nvkm_ior; struct nvkm_outp; struct dcb_output; @@ -34,6 +36,20 @@ struct nvkm_disp_func { int (*new)(struct nvkm_disp *, int id); } wndw, head, dac, sor, pior; + /* Register programming that the GSP-RM display path (rm/r535) needs from + * the chip, everything else on that path goes through RM. The hooks are + * called unconditionally and the head table is handed to nvkm_head_new_(). + */ + struct { + irqreturn_t (*intr)(struct nvkm_inth *); + /* Head-timing interrupts arrive on a second DISP vector. */ + bool intr_low_latency; + const struct nvkm_head_func *head; + void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable); + void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size); + void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size); + } gsp; + u16 ramht_size; struct nvkm_sclass root; @@ -72,6 +88,7 @@ int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *); int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); int tu102_disp_init(struct nvkm_disp *); +irqreturn_t tu102_disp_intr(struct nvkm_inth *); void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *); void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c index dcb9f8ba374c..f6c163072ff6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c @@ -30,6 +30,21 @@ #include <nvif/class.h> +/* General Control Packet: bracket an audio enable/disable with AVMute + * through the legacy GCP SF unit. Used by the GSP-RM path, which sends the + * equivalent packet via RM as well but keeps the direct write in sync. + */ +void +tu102_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hdmi = head * 0x400; + + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010); + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001); +} + void tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned) { @@ -104,6 +119,64 @@ tu102_sor_new(struct nvkm_disp *disp, int id) return nvkm_ior_new_(&tu102_sor, disp, SOR, id, hda & BIT(id)); } +/* The GSP-RM display path leaves head-timing (vblank) interrupts and their + * enables to us. These program the RM head-timing line (bit 1 of the + * per-head enable, not the bit nvkm's own gv100 path uses). + */ +static void +tu102_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000); +} + +static void +tu102_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002); +} + +const struct nvkm_head_func +tu102_gsp_head = { + .state = gv100_head_state, + .rgpos = gv100_head_rgpos, + .vblank_get = tu102_head_vblank_get, + .vblank_put = tu102_head_vblank_put, +}; + +static void +tu102_disp_intr_head_timing(struct nvkm_disp *disp, int head) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04)); + + if (stat & 0x00000002) { + nvkm_disp_vblank(disp, head); + + nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002); + } +} + +irqreturn_t +tu102_disp_intr(struct nvkm_inth *inth) +{ + struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff; + int head; + + for_each_set_bit(head, &mask, 8) + tu102_disp_intr_head_timing(disp, head); + + return IRQ_HANDLED; +} + int tu102_disp_init(struct nvkm_disp *disp) { @@ -230,12 +303,23 @@ tu102_disp = { }, }; +static const struct nvkm_disp_func +tu102_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + .gsp.intr = tu102_disp_intr, + .gsp.head = &tu102_gsp_head, + .gsp.hdmi_gcp = tu102_sor_hdmi_gcp, + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, +}; + int tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp) { if (nvkm_gsp_rm(device->gsp)) - return r535_disp_new(&tu102_disp, device, type, inst, pdisp); + return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp); return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c index e77733a5d9c3..f5f22173fc2c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c @@ -400,16 +400,16 @@ r535_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) r535_sor_dp_audio_mute(sor, false); } -static void -r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +static int +r535_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) { struct nvkm_disp *disp = sor->disp; struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); - if (WARN_ON(IS_ERR(ctrl))) - return; + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); ctrl->subDeviceInstance = 0; ctrl->head = head; @@ -429,12 +429,20 @@ r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u ctrl->MST.sendACT = 0; ctrl->MST.singleHeadMSTPipeline = 0; ctrl->MST.bEnableAudioOverRightPanel = 0; - WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl)); + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + +static void +r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +{ + const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api; + + WARN_ON(rmapi->disp->dp.vcpi(sor, head, slot, slot_nr, pbn, aligned_pbn)); } static int -r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, - u32 watermark, u32 hblanksym, u32 vblanksym) +r535_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) { struct nvkm_disp *disp = sor->disp; struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; @@ -461,6 +469,15 @@ r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); } +static int +r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) +{ + const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api; + + return rmapi->disp->dp.sst(sor, head, ef, watermark, hblanksym, vblanksym); +} + static const struct nvkm_ior_func_dp r535_sor_dp = { .sst = r535_sor_dp_sst, @@ -545,16 +562,21 @@ r535_sor_hdmi_ctrl_audio(struct nvkm_outp *outp, bool enable) static void r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable) { - struct nvkm_device *device = sor->disp->engine.subdev.device; - const u32 hdmi = head * 0x400; - r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable); r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable); + sor->disp->func->gsp.hdmi_gcp(sor, head, enable); +} + +static void +r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size) +{ + sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size); +} - /* General Control (GCP). */ - nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010); - nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001); +static void +r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size) +{ + sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size); } static void @@ -582,8 +604,8 @@ r535_sor_hdmi = { .ctrl = r535_sor_hdmi_ctrl, .scdc = r535_sor_hdmi_scdc, /*TODO: SF_USER -> KMS. */ - .infoframe_avi = gv100_sor_hdmi_infoframe_avi, - .infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, + .infoframe_avi = r535_sor_hdmi_infoframe_avi, + .infoframe_vsi = r535_sor_hdmi_infoframe_vsi, .audio = r535_sor_hdmi_audio, }; @@ -608,31 +630,6 @@ r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) return 4; } -static void -r535_head_vblank_put(struct nvkm_head *head) -{ - struct nvkm_device *device = head->disp->engine.subdev.device; - - nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000); -} - -static void -r535_head_vblank_get(struct nvkm_head *head) -{ - struct nvkm_device *device = head->disp->engine.subdev.device; - - nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); - nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002); -} - -static const struct nvkm_head_func -r535_head = { - .state = gv100_head_state, - .rgpos = gv100_head_rgpos, - .vblank_get = r535_head_vblank_get, - .vblank_put = r535_head_vblank_put, -}; - static struct nvkm_conn * r535_conn_new(struct nvkm_disp *disp, u32 id) { @@ -1405,35 +1402,6 @@ r535_disp_event = { }; static void -r535_disp_intr_head_timing(struct nvkm_disp *disp, int head) -{ - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_device *device = subdev->device; - u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04)); - - if (stat & 0x00000002) { - nvkm_disp_vblank(disp, head); - - nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002); - } -} - -static irqreturn_t -r535_disp_intr(struct nvkm_inth *inth) -{ - struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_device *device = subdev->device; - unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff; - int head; - - for_each_set_bit(head, &mask, 8) - r535_disp_intr_head_timing(disp, head); - - return IRQ_HANDLED; -} - -static void r535_disp_fini(struct nvkm_disp *disp, bool suspend) { if (!disp->engine.subdev.use.enabled) @@ -1659,7 +1627,7 @@ r535_disp_oneinit(struct nvkm_disp *disp) nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl); for_each_set_bit(i, &disp->head.mask, disp->head.nr) { - ret = nvkm_head_new_(&r535_head, disp, i); + ret = nvkm_head_new_(disp->func->gsp.head, disp, i); if (ret) return ret; } @@ -1703,12 +1671,20 @@ r535_disp_oneinit(struct nvkm_disp *disp) if (ret) return ret; - ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst); + /* Chips that raise head-timing interrupts on a separate low-latency + * vector report it as a second DISP interrupt table entry, exposed + * as instance 1 by the RM engine-index translation (see + * r570_gsp_xlat_mc_engine_idx()). Their high-latency vector + * (instance 0) is left unhandled as no event nouveau enables is + * routed to it, and without a handler it stays masked. + */ + ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, + disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst); if (ret < 0) return ret; ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev, - r535_disp_intr, &disp->engine.subdev.inth); + disp->func->gsp.intr, &disp->engine.subdev.inth); if (ret) return ret; @@ -1741,6 +1717,7 @@ r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device, rm->uevent = hw->uevent; rm->sor.cnt = r535_sor_cnt; rm->sor.new = r535_sor_new; + rm->gsp = hw->gsp; rm->ramht_size = hw->ramht_size; rm->root.oclass = gpu->disp.class.root; @@ -1782,6 +1759,8 @@ r535_disp = { .dp = { .get_caps = r535_dp_get_caps, .set_indexed_link_rates = r535_dp_set_indexed_link_rates, + .sst = r535_dp_sst, + .vcpi = r535_dp_vcpi, }, .chan = { .set_pushbuf = r535_disp_chan_set_pushbuf, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c index a96e31c2d80b..8a23837f356e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c @@ -5,6 +5,7 @@ #include <rm/rm.h> #include <engine/disp.h> +#include <engine/disp/ior.h> #include <engine/disp/outp.h> #include "nvhw/drf.h" @@ -75,6 +76,67 @@ r570_disp_chan_set_pushbuf(struct nvkm_disp *disp, s32 oclass, int inst, struct } static int +r570_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +{ + struct nvkm_disp *disp = sor->disp; + NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; + + ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, + NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); + + ctrl->subDeviceInstance = 0; + ctrl->head = head; + ctrl->sorIndex = sor->id; + ctrl->dpLink = sor->asy.link == 2; + ctrl->bEnableOverride = 1; + ctrl->bMST = 1; + ctrl->hBlankSym = 0; + ctrl->vBlankSym = 0; + ctrl->colorFormat = 0; + ctrl->bEnableTwoHeadOneOr = 0; + ctrl->singleHeadMultistreamMode = 0; + ctrl->MST.slotStart = slot; + ctrl->MST.slotEnd = slot + slot_nr - 1; + ctrl->MST.PBN = pbn; + ctrl->MST.Timeslice = aligned_pbn; + ctrl->MST.sendACT = 0; + ctrl->MST.singleHeadMSTPipeline = 0; + ctrl->MST.bEnableAudioOverRightPanel = 0; + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + +static int +r570_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) +{ + struct nvkm_disp *disp = sor->disp; + NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; + + ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, + NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); + + ctrl->subDeviceInstance = 0; + ctrl->head = head; + ctrl->sorIndex = sor->id; + ctrl->dpLink = sor->asy.link == 2; + ctrl->bEnableOverride = 1; + ctrl->bMST = 0; + ctrl->hBlankSym = hblanksym; + ctrl->vBlankSym = vblanksym; + ctrl->colorFormat = 0; + ctrl->bEnableTwoHeadOneOr = 0; + ctrl->SST.bEnhancedFraming = ef; + ctrl->SST.tuSize = 64; + ctrl->SST.waterMark = watermark; + ctrl->SST.bEnableAudioOverRightPanel = 0; + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + +static int r570_dp_set_indexed_link_rates(struct nvkm_outp *outp) { NV0073_CTRL_CMD_DP_CONFIG_INDEXED_LINK_RATES_PARAMS *ctrl; @@ -255,6 +317,8 @@ r570_disp = { .dp = { .get_caps = r570_dp_get_caps, .set_indexed_link_rates = r570_dp_set_indexed_link_rates, + .sst = r570_dp_sst, + .vcpi = r570_dp_vcpi, }, .chan = { .set_pushbuf = r570_disp_chan_set_pushbuf, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c index 996941c668ba..1488771c63fc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c @@ -44,6 +44,15 @@ r570_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int *ptype = NVKM_ENGINE_DISP; *pinst = 0; return true; + case MC_ENGINE_IDX_DISP_LOW: + /* GB20x+ report a separate low-latency display vector, used + * for head-timing interrupts. Expose it as a second DISP + * interrupt instance. r535_disp_oneinit() attaches the + * handler to it when the chip's gsp.intr_low_latency is set. + */ + *ptype = NVKM_ENGINE_DISP; + *pinst = 1; + return true; case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE19: *ptype = NVKM_ENGINE_CE; *pinst = mc_engine_idx - MC_ENGINE_IDX_CE0; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h index 06e972835d77..742b25a2a12d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h @@ -256,6 +256,8 @@ typedef struct NV0073_CTRL_DP_CTRL_PARAMS { NvU32 eightLaneDpcdBaseAddr; } NV0073_CTRL_DP_CTRL_PARAMS; +#define NV0073_CTRL_CMD_DP_CONFIG_STREAM (0x731362U) /* finn: Evaluated from "(FINN_NV04_DISPLAY_COMMON_DP_INTERFACE_ID << 8) | NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS_MESSAGE_ID" */ + typedef struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS { NvU32 subDeviceInstance; NvU32 head; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h index a9af94adf9ef..fcd0221dcea1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h @@ -6,6 +6,7 @@ #ifndef __NVKM_RM_H__ #define __NVKM_RM_H__ #include "handles.h" +struct nvkm_ior; struct nvkm_outp; struct r535_gr; @@ -93,6 +94,10 @@ struct nvkm_rm_api { struct { int (*get_caps)(struct nvkm_disp *, int *link_bw, bool *mst, bool *wm); int (*set_indexed_link_rates)(struct nvkm_outp *); + int (*sst)(struct nvkm_ior *, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym); + int (*vcpi)(struct nvkm_ior *, int head, + u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn); } dp; struct { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c index f4489efc94a7..22b0fde6ba34 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c @@ -195,6 +195,9 @@ check_io_mapping(struct nv50_instmem *imem) { struct nvkm_device *device = imem->base.subdev.device; + if (imem->iomap.size) + return true; + return io_mapping_init_wc(&imem->iomap, device->func->resource_addr(device, NVKM_BAR2_INST), device->func->resource_size(device, NVKM_BAR2_INST)) != NULL; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 7f4178800afd..0719fc6a52d5 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -30,6 +30,7 @@ #include <drm/drm_drv.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> #include <drm/drm_edid.h> #include <drm/drm_encoder.h> #include <drm/drm_framebuffer.h> @@ -981,7 +982,7 @@ static struct drm_plane *qxl_create_plane(struct qxl_device *qdev, return ERR_PTR(-EINVAL); } - plane = kzalloc(sizeof(*plane), GFP_KERNEL); + plane = kzalloc_obj(*plane); if (!plane) return ERR_PTR(-ENOMEM); @@ -993,6 +994,9 @@ static struct drm_plane *qxl_create_plane(struct qxl_device *qdev, drm_plane_helper_add(plane, helper_funcs); + drm_plane_create_blend_mode_property(plane, + BIT(DRM_MODE_BLEND_PREMULTI)); + return plane; free_plane: diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index e7f49fe845ea..4e58685f58ff 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -68,6 +68,7 @@ config ROCKCHIP_CDN_DP config ROCKCHIP_DW_DP bool "Rockchip specific extensions for Synopsys DW DP" + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions to enable Synopsys DesignWare Cores based DisplayPort transmit @@ -145,6 +146,8 @@ config ROCKCHIP_RGB config ROCKCHIP_RK3066_HDMI bool "Rockchip specific extensions for RK3066 HDMI" depends on DRM_ROCKCHIP + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions for the RK3066 HDMI driver. If you want to enable diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 587e60232ec7..efd5a98e80bd 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -241,10 +241,11 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, of_graph_get_remote_port(endpoint.local_node); of_property_read_u32(remote_port, "reg", &port_id); - sprintf(name, "%s vp%d", remote_port_parent->full_name, port_id); + snprintf(name, sizeof(name), "%s vp%d", + remote_port_parent->full_name, port_id); } else { - sprintf(name, "%s %s", - remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb"); + snprintf(name, sizeof(name), "%s %s", + remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb"); } DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); diff --git a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c index 8e9ae7d980eb..2dfa3efef210 100644 --- a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c +++ b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2025 Valve Corporation */ +#include <kunit/device.h> + #include "sched_tests.h" /* @@ -288,6 +290,7 @@ static const struct drm_sched_backend_ops drm_mock_scheduler_ops = { */ struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout) { + static unsigned int instance; struct drm_sched_init_args args = { .ops = &drm_mock_scheduler_ops, .num_rqs = DRM_SCHED_PRIORITY_COUNT, @@ -297,11 +300,19 @@ struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout) .name = "drm-mock-scheduler", }; struct drm_mock_scheduler *sched; + struct device *dev; + char name[64]; int ret; sched = kunit_kzalloc(test, sizeof(*sched), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, sched); + snprintf(name, sizeof(name), "%s-%u", args.name, instance++); + dev = kunit_device_register(test, name); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + args.dev = dev; + ret = drm_sched_init(&sched->base, &args); KUNIT_ASSERT_EQ(test, ret, 0); diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c index 819aed466727..9d60db45139c 100644 --- a/drivers/gpu/drm/sysfb/ofdrm.c +++ b/drivers/gpu/drm/sysfb/ofdrm.c @@ -2,6 +2,7 @@ #include <linux/aperture.h> #include <linux/of_address.h> +#include <linux/overflow.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm.h> @@ -238,7 +239,7 @@ static bool is_avivo(u32 vendor, u32 device) /* This will match most R5xx */ return (vendor == PCI_VENDOR_ID_ATI) && ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) || - (PCI_VENDOR_ID_ATI_R600 >= 0x9400)); + (device >= PCI_VENDOR_ID_ATI_R600)); } static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node) @@ -913,7 +914,10 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, return ERR_PTR(-EINVAL); } - fb_size = linebytes * height; + if (check_mul_overflow(linebytes, height, &fb_size)) { + drm_err(dev, "framebuffer size exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } /* * Try to figure out the address of the framebuffer. Unfortunately, Open diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 0b3fcc7011b3..fefc3761a4bc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -904,6 +904,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, struct tegra_dc *dc) { unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); + unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE); enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY; struct tegra_plane *plane; unsigned int num_formats; @@ -939,6 +940,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, } drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + drm_plane_create_blend_mode_property(&plane->base, blend_caps); drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); err = drm_plane_create_rotation_property(&plane->base, @@ -1209,6 +1211,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, struct tegra_dc *dc) { unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); + unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE); struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -1252,6 +1255,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, } drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); + drm_plane_create_blend_mode_property(&plane->base, blend_caps); drm_plane_create_zpos_immutable_property(&plane->base, 255); return &plane->base; @@ -1356,6 +1360,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, bool cursor) { unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm); + unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE); struct tegra_plane *plane; unsigned int num_formats; enum drm_plane_type type; @@ -1394,6 +1399,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, } drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs); + drm_plane_create_blend_mode_property(&plane->base, blend_caps); drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255); err = drm_plane_create_rotation_property(&plane->base, diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index bd442bfd4540..448f49f3a7d7 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -759,6 +759,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, unsigned int index, enum drm_plane_type type) { + unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE); struct tegra_drm *tegra = drm->dev_private; struct tegra_display_hub *hub = tegra->hub; struct tegra_shared_plane *plane; @@ -797,6 +798,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, } drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); + drm_plane_create_blend_mode_property(p, blend_caps); drm_plane_create_zpos_property(p, 0, 0, 255); return p; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c b/drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c index 2e7b3e87fea1..7583b6ebba2a 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c @@ -22,7 +22,7 @@ tilcdc_panel_update_prop(struct of_changeset *ocs, struct device_node *node, { struct property *prop; - prop = kzalloc(sizeof(*prop), GFP_KERNEL); + prop = kzalloc_obj(*prop); if (!prop) return -ENOMEM; diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c index 075221b431d3..3bf23fcf6574 100644 --- a/drivers/gpu/drm/tiny/cirrus-qemu.c +++ b/drivers/gpu/drm/tiny/cirrus-qemu.c @@ -582,6 +582,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev, struct cirrus_device *cirrus; int ret; + if (pci_resource_len(pdev, 0) < CIRRUS_VRAM_SIZE) + return -ENODEV; + ret = aperture_remove_conflicting_pci_devices(pdev, cirrus_driver.name); if (ret) return ret; diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index 8e4e5fc9d3c5..3c41238a8268 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -15,6 +15,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> #include <drm/drm_edid.h> #include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> @@ -540,6 +541,9 @@ static struct drm_plane *vbox_create_plane(struct vbox_private *vbox, drm_plane_helper_add(plane, helper_funcs); + drm_plane_create_blend_mode_property(plane, + BIT(DRM_MODE_BLEND_COVERAGE)); + return plane; free_plane: diff --git a/drivers/gpu/drm/verisilicon/vs_plane.c b/drivers/gpu/drm/verisilicon/vs_plane.c index d81f7b8f4c65..7ddb9d2dcb83 100644 --- a/drivers/gpu/drm/verisilicon/vs_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_plane.c @@ -136,7 +136,7 @@ struct drm_plane_state *vs_plane_duplicate_state(struct drm_plane *plane) vs_state_old = to_vs_plane_state(plane->state); - vs_state = kzalloc_obj(*vs_state, GFP_KERNEL); + vs_state = kzalloc_obj(*vs_state); if (!vs_state) return NULL; @@ -166,7 +166,7 @@ void vs_plane_reset(struct drm_plane *plane) plane->state = NULL; } - vs_state = kzalloc_obj(*vs_state, GFP_KERNEL); + vs_state = kzalloc_obj(*vs_state); if (!vs_state) return; diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 44ffffec550f..a1a875a0c706 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -344,7 +344,7 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev, if (ret) { kfree(virtio_gpu_fb); drm_gem_object_put(obj); - return NULL; + return ERR_PTR(ret); } return &virtio_gpu_fb->base; @@ -378,8 +378,11 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) vgdev->ddev->mode_config.fb_modifiers_not_supported = true; - for (i = 0 ; i < vgdev->num_scanouts; ++i) - vgdev_output_init(vgdev, i); + for (i = 0; i < vgdev->num_scanouts; ++i) { + ret = vgdev_output_init(vgdev, i); + if (ret) + return ret; + } ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts); if (ret) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 17a6a4d26516..9df4c7117341 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -43,6 +43,8 @@ #include <drm/drm_probe_helper.h> #include <drm/virtgpu_drm.h> +#include <xen/xen.h> + #define DRIVER_NAME "virtio_gpu" #define DRIVER_DESC "virtio GPU" @@ -60,6 +62,24 @@ /* See virtio_gpu_ctx_create. One additional character for NULL terminator. */ #define DEBUG_NAME_MAX_LEN 65 +/* + * Whether the host must be told about resource backing pages by DMA address + * rather than guest-physical address. + * + * This mirrors vring_use_map_api() in drivers/virtio/virtio_ring.c, including + * its xen_domain() case. + */ +static inline bool virtio_gpu_use_dma_api(const struct virtio_device *vdev) +{ + if (!virtio_has_dma_quirk(vdev)) + return true; + + if (xen_domain()) + return true; + + return false; +} + struct virtio_gpu_object_params { unsigned long size; bool dumb; @@ -343,6 +363,7 @@ void virtio_gpu_array_put_free_work(struct work_struct *work); /* virtgpu_vq.c */ int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev); void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev); +void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev); void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo, struct virtio_gpu_object_params *params, diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 1ffe1e431f65..1d4d3bf46a20 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -352,6 +352,7 @@ void virtio_gpu_deinit(struct drm_device *dev) flush_work(&vgdev->cursorq.dequeue_work); flush_work(&vgdev->config_changed_work); virtio_reset_device(vgdev->vdev); + virtio_gpu_reclaim_vbufs(vgdev); vgdev->vdev->config->del_vqs(vgdev->vdev); mutex_destroy(&vgdev->obj_restore_lock); } diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 9bc0bd68c314..49899485be6f 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -173,7 +173,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev, struct virtio_gpu_mem_entry **ents, unsigned int *nents) { - bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); + bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev); struct scatterlist *sg; struct sg_table *pages; int si; diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 1d1b27ece62a..640815af4098 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -24,6 +24,7 @@ */ #include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> #include <drm/drm_damage_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_atomic_helper.h> @@ -609,6 +610,9 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, if (type == DRM_PLANE_TYPE_PRIMARY) drm_plane_enable_fb_damage_clips(plane); + else if (type == DRM_PLANE_TYPE_CURSOR) + drm_plane_create_blend_mode_property(plane, + BIT(DRM_MODE_BLEND_PREMULTI)); return plane; } diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c index 216c77cd0d21..149e6bcb5878 100644 --- a/drivers/gpu/drm/virtio/virtgpu_prime.c +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c @@ -293,9 +293,7 @@ int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev, return -ENOMEM; } - ents = kvmalloc_array(bo->sgt->nents, - sizeof(struct virtio_gpu_mem_entry), - GFP_KERNEL); + ents = kvmalloc_objs(struct virtio_gpu_mem_entry, bo->sgt->nents); if (!ents) { DRM_ERROR("failed to allocate ent list\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index f2b0ab0f610e..c02c03c10d92 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -208,6 +208,21 @@ static void free_vbuf(struct virtio_gpu_device *vgdev, kmem_cache_free(vgdev->vbufs, vbuf); } +void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_vbuffer *vbuf; + + while ((vbuf = virtqueue_detach_unused_buf(vgdev->ctrlq.vq))) { + if (vbuf->objs) + virtio_gpu_array_put_free(vbuf->objs); + if (vbuf->resp_cb_data) + virtio_gpu_cleanup_object(vbuf->resp_cb_data); + free_vbuf(vgdev, vbuf); + } + while ((vbuf = virtqueue_detach_unused_buf(vgdev->cursorq.vq))) + free_vbuf(vgdev, vbuf); +} + static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list) { struct virtio_gpu_vbuffer *vbuf; @@ -764,7 +779,7 @@ int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]); struct virtio_gpu_transfer_to_host_2d *cmd_p; struct virtio_gpu_vbuffer *vbuf; - bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); + bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev); if (virtio_gpu_is_shmem(bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, @@ -795,7 +810,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]); struct virtio_gpu_transfer_to_host_2d *cmd_p; struct virtio_gpu_vbuffer *vbuf; - bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); + bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev); if (virtio_gpu_is_shmem(bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, @@ -1228,7 +1243,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]); struct virtio_gpu_transfer_host_3d *cmd_p; struct virtio_gpu_vbuffer *vbuf; - bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); + bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev); if (virtio_gpu_is_shmem(bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c index 7551b8c7766d..601e3f128c9f 100644 --- a/drivers/gpu/drm/vkms/vkms_configfs.c +++ b/drivers/gpu/drm/vkms/vkms_configfs.c @@ -212,7 +212,7 @@ static struct config_group *make_crtc_group(struct config_group *group, if (dev->enabled) return ERR_PTR(-EBUSY); - crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); + crtc = kzalloc_obj(*crtc); if (!crtc) return ERR_PTR(-ENOMEM); @@ -368,7 +368,7 @@ static struct config_group *make_plane_group(struct config_group *group, if (dev->enabled) return ERR_PTR(-EBUSY); - plane = kzalloc(sizeof(*plane), GFP_KERNEL); + plane = kzalloc_obj(*plane); if (!plane) return ERR_PTR(-ENOMEM); @@ -484,7 +484,7 @@ static struct config_group *make_encoder_group(struct config_group *group, if (dev->enabled) return ERR_PTR(-EBUSY); - encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); + encoder = kzalloc_obj(*encoder); if (!encoder) return ERR_PTR(-ENOMEM); @@ -651,7 +651,7 @@ static struct config_group *make_connector_group(struct config_group *group, if (dev->enabled) return ERR_PTR(-EBUSY); - connector = kzalloc(sizeof(*connector), GFP_KERNEL); + connector = kzalloc_obj(*connector); if (!connector) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h index 18d0fde8c98f..faf8d7e2c5c1 100644 --- a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h +++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h @@ -46,6 +46,7 @@ #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2)) #define PIPE_CONTROL0_QUEUE_DRAIN_MODE BIT(12) +#define PIPE_CONTROL0_UNTYPED_DATAPORT_CACHE_FLUSH BIT(11) /* gen12 */ #define PIPE_CONTROL0_L3_READ_ONLY_CACHE_INVALIDATE BIT(10) /* gen12 */ #define PIPE_CONTROL0_HDC_PIPELINE_FLUSH BIT(9) /* gen12 */ diff --git a/drivers/gpu/drm/xe/xe_amc.c b/drivers/gpu/drm/xe/xe_amc.c index 8ecadee6eea3..edd50bf8261e 100644 --- a/drivers/gpu/drm/xe/xe_amc.c +++ b/drivers/gpu/drm/xe/xe_amc.c @@ -177,7 +177,7 @@ int xe_amc_init(struct xe_i2c *i2c) { struct xe_amc *amc; - amc = kzalloc(sizeof(*amc), GFP_KERNEL); + amc = kzalloc_obj(*amc); if (!amc) return -ENOMEM; diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 9c5384b95c63..ab09dcff5860 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -2435,9 +2435,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi oa_config->id = idr_alloc(&oa->metrics_idr, oa_config, 1, 0, GFP_KERNEL); if (oa_config->id < 0) { - drm_dbg(&oa->xe->drm, "Failed to create sysfs entry for OA config\n"); + drm_dbg(&oa->xe->drm, "Failed to allocate id for OA config\n"); err = oa_config->id; - goto sysfs_err; + goto id_alloc_err; } id = oa_config->id; @@ -2448,6 +2448,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi return id; +id_alloc_err: + sysfs_remove_group(oa->metrics_kobj, &oa_config->sysfs_metric); sysfs_err: mutex_unlock(&oa->metrics_lock); reg_err: diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index dbf8f71d3328..a4986df8328d 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -16,6 +16,7 @@ #include "xe_hw_engine.h" #include "xe_pagefault.h" #include "xe_pagefault_types.h" +#include "xe_pm.h" #include "xe_svm.h" #include "xe_trace_bo.h" #include "xe_vm.h" @@ -292,9 +293,17 @@ static void xe_pagefault_queue_work(struct work_struct *w) { struct xe_pagefault_queue *pf_queue = container_of(w, typeof(*pf_queue), worker); + struct xe_device *xe = pf_queue->xe; struct xe_pagefault pf; unsigned long threshold; + /* + * A live VM holds a PM reference, but a torn-down VM does not. + * Guard the entire worker loop to safely drain stale faults and + * prevent autosuspends from desyncing batched CT flushes. + */ + guard(xe_pm_runtime)(xe); + #define USM_QUEUE_MAX_RUNTIME_MS 20 threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS); @@ -365,6 +374,7 @@ static int xe_pagefault_queue_init(struct xe_device *xe, drm_dbg(&xe->drm, "xe_pagefault_entry_size=%d, total_num_eus=%d, pf_queue->size=%u", xe_pagefault_entry_size(), total_num_eus, pf_queue->size); + pf_queue->xe = xe; spin_lock_init(&pf_queue->lock); INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work); diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h index c4ee625b93dd..f63a12aa0d4f 100644 --- a/drivers/gpu/drm/xe/xe_pagefault_types.h +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h @@ -8,6 +8,7 @@ #include <linux/workqueue.h> +struct xe_device; struct xe_gt; struct xe_pagefault; @@ -118,6 +119,8 @@ struct xe_pagefault { * queue to absorb the device’s worst-case number of outstanding faults. */ struct xe_pagefault_queue { + /** @xe: Back-pointer to the Xe device */ + struct xe_device *xe; /** * @data: Data in queue containing struct xe_pagefault, protected by * @lock diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c index 39a670e91ba7..08b4a4283e96 100644 --- a/drivers/gpu/drm/xe/xe_ring_ops.c +++ b/drivers/gpu/drm/xe/xe_ring_ops.c @@ -212,6 +212,7 @@ static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i) { struct xe_exec_queue *q = job->q; struct xe_gt *gt = q->gt; + struct xe_device *xe = gt_to_xe(gt); bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK); u32 flags0, flags1; @@ -220,6 +221,16 @@ static int emit_render_cache_flush(struct xe_sched_job *job, u32 *dw, int i) LRC_PPHWSP_FLUSH_INVAL_SCRATCH_ADDR, 0); flags0 = PIPE_CONTROL0_HDC_PIPELINE_FLUSH; + /* + * Prior to MTL, HDC Pipeline Flush reliably also flushes the LSC + * untyped L1 dataport cache, provided HDC_CHICKEN0 is programmed + * correctly. Starting with MTL that coupling no longer holds + * regardless of how HDC_CHICKEN0 is programmed, but explicitly + * requesting the flush via PIPE_CONTROL is itself only reliable + * from Xe2 onward, so only gate it in on Xe2+. + */ + if (GRAPHICS_VERx100(xe) >= 2000) + flags0 |= PIPE_CONTROL0_UNTYPED_DATAPORT_CACHE_FLUSH; flags1 = (PIPE_CONTROL_TILE_CACHE_FLUSH | PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH | PIPE_CONTROL_DEPTH_CACHE_FLUSH | diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 9e0176861cb6..23952ad8951e 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -4148,7 +4148,7 @@ static int fill_faults(struct xe_vm *vm, entry_size = sizeof(struct xe_vm_fault); count = args->size / entry_size; - fault_list = kcalloc(count, sizeof(struct xe_vm_fault), GFP_KERNEL); + fault_list = kzalloc_objs(struct xe_vm_fault, count); if (!fault_list) return -ENOMEM; diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index 7b4478fb1697..56cff1e44530 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -90,6 +90,9 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset) offset |= offset_lo << 6; /* HW view bits 31:6 */ offset *= num_enabled; /* convert to SW view */ + drm_info(&xe->drm, "FLAT_CCS base:%llx, aligned:%s\n", offset, + str_yes_no(IS_ALIGNED(offset, SZ_128K))); + /* * Everything below this offset is handed to the VRAM * allocator, so it has to be the *first* address the diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index aa7fa11a0197..a81bf51cbcf1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1253,7 +1253,7 @@ config HID_HYPERV_MOUSE config HID_HYPERV_MOUSE_KUNIT_TEST bool "KUnit tests for Hyper-V mouse driver" if !KUNIT_ALL_TESTS - depends on KUNIT && HID_HYPERV_MOUSE + depends on KUNIT && (HID_HYPERV_MOUSE = KUNIT || KUNIT = y) default KUNIT_ALL_TESTS help Builds unit tests for the Hyper-V synthetic HID driver. diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 702c22fae136..c90b68956cb3 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -62,6 +62,10 @@ struct hid_bpf_offset_write_range { u32 end; }; +struct hid_bpf_ctx__safe_trusted { + struct hid_device *hid; +}; + static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, const struct bpf_reg_state *reg, int off, int size) @@ -86,6 +90,8 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, const char *cur = NULL; int i; + BTF_TYPE_EMIT(struct hid_bpf_ctx__safe_trusted); + t = btf_type_by_id(reg->btf, reg->btf_id); for (i = 0; i < ARRAY_SIZE(write_ranges); i++) { @@ -250,6 +256,11 @@ static void hid_bpf_unreg(void *kdata, struct bpf_link *link) mutex_lock(&hdev->bpf.prog_list_lock); + if (!ops->hdev) { + mutex_unlock(&hdev->bpf.prog_list_lock); + return; + } + list_del_rcu(&ops->list); synchronize_srcu(&hdev->bpf.srcu); ops->hdev = NULL; @@ -310,13 +321,17 @@ static struct bpf_struct_ops bpf_hid_bpf_ops = { void __hid_bpf_ops_destroy_device(struct hid_device *hdev) { struct hid_bpf_ops *e; + int count = 0; - rcu_read_lock(); - list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { - hid_put_device(hdev); + mutex_lock(&hdev->bpf.prog_list_lock); + list_for_each_entry(e, &hdev->bpf.prog_list, list) { e->hdev = NULL; + count++; } - rcu_read_unlock(); + mutex_unlock(&hdev->bpf.prog_list_lock); + + while (count--) + hid_put_device(hdev); } static int __init hid_bpf_struct_ops_init(void) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index ec966fc0a411..c38a2b3b33d9 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -406,7 +406,7 @@ static int asus_kbd_fn_lock_set(struct asus_drvdata *drvdata, bool enabled) struct asus_work_action *action; unsigned long flags; - action = kzalloc(sizeof(struct asus_work_action), GFP_ATOMIC); + action = kzalloc_obj(struct asus_work_action, GFP_ATOMIC); if (!action) return -ENOMEM; @@ -433,7 +433,7 @@ static int asus_kbd_wmi_fan_send(struct asus_drvdata *drvdata, u8 *report_data, return -EINVAL; } - action = kzalloc(sizeof(struct asus_work_action), GFP_NOWAIT); + action = kzalloc_obj(struct asus_work_action, GFP_NOWAIT); if (!action) return -ENOMEM; @@ -746,7 +746,7 @@ static void asus_kbd_backlight_set(struct asus_hid_listener *listener, int brigh drvdata->kbd_backlight_brightness = brightness; - action = kzalloc(sizeof(struct asus_work_action), GFP_NOWAIT); + action = kzalloc_obj(struct asus_work_action, GFP_NOWAIT); if (!action) return; diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 6579bd19da13..cfc061dbdd24 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -687,7 +687,7 @@ static void mousevsc_device_info_valid_descriptor(struct kunit *test) info->hid_descriptor.bLength = sizeof(struct hid_descriptor); info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(4); - report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength; + report = (u8 *)(info + 1); memset(report, 0x42, 4); mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 4); @@ -713,7 +713,7 @@ static void mousevsc_device_info_report_desc_oob(struct kunit *test) info->hid_descriptor.bLength = sizeof(struct hid_descriptor); info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(64); - report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength; + report = (u8 *)(info + 1); memset(report, 0x42, 8); mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 8); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 341bf587863b..b3aca5aa9176 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -338,6 +338,7 @@ #define I2C_VENDOR_ID_CIRQUE 0x0488 #define I2C_PRODUCT_ID_CIRQUE_1063 0x1063 +#define I2C_PRODUCT_ID_CIRQUE_D0C1 0xD0C1 #define USB_VENDOR_ID_CJTOUCH 0x24b8 #define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 2c41bacab1ca..451c7324e6a0 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1321,21 +1321,18 @@ static void mt_touch_report(struct hid_device *hid, * Includes multi-packet support where subsequent * packets are sent with zero contactcount. */ - if (contact_count >= 0) { + if (contact_count > 0) + app->num_expected = contact_count; + else if (app->num_received == 0 && app->prev_scantime != scantime) { /* + * New multi-report frame: + * * For Win8 PTPs the first packet (td->num_received == 0) may * have a contactcount of 0 if there only is a button event. - * We double check that this is not a continuation packet - * of a possible multi-packet frame be checking that the - * timestamp has changed. + * + * Some other devices use a sentinel frame with 0 to release all contacts */ - if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) && - app->num_received == 0 && - app->prev_scantime != scantime) - app->num_expected = contact_count; - /* A non 0 contact count always indicates a first packet */ - else if (contact_count) - app->num_expected = contact_count; + app->num_expected = 0; } app->prev_scantime = scantime; diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 2bd781f1e0f5..ecc19387f6b0 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -235,7 +235,23 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr, break; } - read_input_count = data->readReport[1]; + read_input_count = min_t(int, data->readReport[1], + data->input_report_size - 2); + if (!read_input_count) { + /* + * A zero length reply advances neither + * bytes_read nor bytes_needed, and because a + * reply did arrive the wait above does not + * time out either, so a device answering 0 + * forever would spin here indefinitely with + * page_mutex held. + */ + hid_warn(hdev, "%s: zero-length read reply\n", + __func__); + clear_bit(RMI_READ_DATA_PENDING, &data->flags); + ret = -EIO; + break; + } memcpy(buf + bytes_read, &data->readReport[2], min(read_input_count, bytes_needed)); @@ -271,6 +287,11 @@ static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr, goto exit; } + if (len + 4 > data->output_report_size) { + ret = -EINVAL; + goto exit; + } + data->writeReport[0] = RMI_WRITE_REPORT_ID; data->writeReport[1] = len; data->writeReport[2] = addr & 0xFF; @@ -666,8 +687,16 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - if (id->driver_data) - data->device_flags = id->driver_data; + /* + * RMI_DEVICE can only mean "this probe validated the RMI reports and + * allocated writeReport": every bail-out to start below skips that + * allocation, and device_flags left carrying RMI_DEVICE from + * driver_data would send rmi_input_configured() into rmi_set_page() + * with writeReport still NULL. A bind through the new_id sysfs + * attribute can supply driver_data with the bit set, so do not let + * driver_data grant it. + */ + data->device_flags = id->driver_data & ~RMI_DEVICE; /* * Check for the RMI specific report ids. If they are misisng @@ -696,6 +725,17 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) data->output_report_size = hid_report_len(output_report); + /* + * The write reports built by this driver occupy 6 bytes and the read + * handshake looks at the first 3 bytes of an input report, so refuse + * to drive a device whose reports cannot hold them. + */ + if (data->output_report_size < 6 || data->input_report_size < 3) { + hid_err(hdev, "rmi reports too small (out=%u in=%u)\n", + data->output_report_size, data->input_report_size); + goto start; + } + data->device_flags |= RMI_DEVICE; alloc_size = data->output_report_size + data->input_report_size; diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index ac08cb2d0368..0f364d43aa4b 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -759,7 +759,7 @@ static inline int steam_haptic_pulse(struct steam_device *steam, u8 pad, if (steam->quirks & STEAM_QUIRK_IBEX) { struct steam_ibex_output_report *report = - kzalloc(sizeof(struct steam_ibex_output_report), GFP_KERNEL); + kzalloc_obj(struct steam_ibex_output_report); if (!report) return -ENOMEM; @@ -798,7 +798,7 @@ static inline int steam_haptic_rumble(struct steam_device *steam, if (steam->quirks & STEAM_QUIRK_IBEX) { struct steam_ibex_output_report *report = - kzalloc(sizeof(struct steam_ibex_output_report), GFP_KERNEL); + kzalloc_obj(struct steam_ibex_output_report); if (!report) return -ENOMEM; diff --git a/drivers/hid/hid-steelseries-arctis.c b/drivers/hid/hid-steelseries-arctis.c index 23fb0cebd72a..7a855b66730a 100644 --- a/drivers/hid/hid-steelseries-arctis.c +++ b/drivers/hid/hid-steelseries-arctis.c @@ -469,7 +469,7 @@ static int steelseries_arctis_probe(struct hid_device *hdev, return hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (interface_num == info->sync_interface) { - sd = kzalloc_obj(*sd, GFP_KERNEL); + sd = kzalloc_obj(*sd); if (!sd) return -ENOMEM; diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 0e725a0f0abe..0ff07fdab442 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -136,6 +136,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_BAD_INPUT_SIZE }, { I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_1063, I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND }, + { I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_D0C1, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, /* * Without additional power on command, at least some QTEC devices send garbage */ diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index a29bf051ada7..8feb8027be95 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1550,6 +1550,19 @@ static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len) return 0; } + if (wacom->features.type == INTUOSP2_BT || + wacom->features.type == INTUOSP2S_BT) { + if (len < 286) { + dev_warn(wacom->pen_input->dev.parent, + "Pro2 BT report too short: %zu bytes\n", len); + return 0; + } + } else if (len < 46) { + dev_warn(wacom->pen_input->dev.parent, + "Pro2 BT report too short: %zu bytes\n", len); + return 0; + } + wacom_intuos_pro2_bt_pen(wacom); if (wacom->features.type == INTUOSP2_BT || wacom->features.type == INTUOSP2S_BT) { diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f4370617deac..7e4cc6f55237 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -694,12 +694,11 @@ void *vmbus_alloc_buffer(struct vmbus_channel *channel, return vzalloc(nr_pages << PAGE_SHIFT); /* Worst case: every chunk is a single page. */ - chunks = kvmalloc_array(nr_pages, sizeof(*chunks), - GFP_KERNEL | __GFP_ZERO); + chunks = kvmalloc_objs(*chunks, nr_pages, GFP_KERNEL | __GFP_ZERO); if (!chunks) goto err; - pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL); + pages = kvmalloc_objs(*pages, nr_pages); if (!pages) goto err; diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 9cba97e81111..39c1b793a893 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -939,7 +939,7 @@ static unsigned long process_hot_add(unsigned long pg_start, */ if (rg_size != 0) { - ha_region = kzalloc(sizeof(struct hv_hotadd_state), GFP_KERNEL); + ha_region = kzalloc_obj(struct hv_hotadd_state); if (!ha_region) return 0; diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index ca56bd8b170e..d0baa10502f7 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1128,12 +1128,17 @@ static void applesmc_release_light_sensor(void) static int applesmc_create_key_backlight(void) { + int ret; + if (!smcreg.has_key_backlight) return 0; applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); if (!applesmc_led_wq) return -ENOMEM; - return led_classdev_register(&pdev->dev, &applesmc_backlight); + ret = led_classdev_register(&pdev->dev, &applesmc_backlight); + if (ret) + destroy_workqueue(applesmc_led_wq); + return ret; } static void applesmc_release_key_backlight(void) @@ -1476,9 +1481,9 @@ static int __init applesmc_init(void) applesmc_fan_config[smcreg.fan_count] = 0; applesmc_pwm_config[smcreg.fan_count] = 0; - applesmc_info_temp = kzalloc_obj(*applesmc_info_temp, GFP_KERNEL); - applesmc_info_fan = kzalloc_obj(*applesmc_info_fan, GFP_KERNEL); - applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm, GFP_KERNEL); + applesmc_info_temp = kzalloc_obj(*applesmc_info_temp); + applesmc_info_fan = kzalloc_obj(*applesmc_info_fan); + applesmc_info_pwm = kzalloc_obj(*applesmc_info_pwm); if (!applesmc_info_temp || !applesmc_info_fan || !applesmc_info_pwm) { ret = -ENOMEM; goto out_info; @@ -1493,7 +1498,7 @@ static int __init applesmc_init(void) applesmc_info_pwm->type = hwmon_pwm; applesmc_info_pwm->config = applesmc_pwm_config; - applesmc_info_arr = kcalloc(4, sizeof(*applesmc_info_arr), GFP_KERNEL); + applesmc_info_arr = kzalloc_objs(*applesmc_info_arr, 4); if (!applesmc_info_arr) { ret = -ENOMEM; goto out_info; @@ -1504,7 +1509,7 @@ static int __init applesmc_init(void) applesmc_info_arr[2] = applesmc_info_pwm; applesmc_info_arr[3] = NULL; - applesmc_chip = kzalloc_obj(*applesmc_chip, GFP_KERNEL); + applesmc_chip = kzalloc_obj(*applesmc_chip); if (!applesmc_chip) { ret = -ENOMEM; goto out_info; @@ -1514,10 +1519,9 @@ static int __init applesmc_init(void) applesmc_chip->info = applesmc_info_arr; /* Create non-standard fanX_safe attributes group */ - fan_safe_attrs = kcalloc(smcreg.fan_count, - sizeof(*fan_safe_attrs), GFP_KERNEL); - fan_safe_attr_list = kcalloc(smcreg.fan_count + 1, - sizeof(*fan_safe_attr_list), GFP_KERNEL); + fan_safe_attrs = kzalloc_objs(*fan_safe_attrs, smcreg.fan_count); + fan_safe_attr_list = kzalloc_objs(*fan_safe_attr_list, + smcreg.fan_count + 1); if (!fan_safe_attrs || !fan_safe_attr_list) { ret = -ENOMEM; goto out_info; diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 1c5945d4ba37..bfce589c3fb1 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -934,7 +934,9 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) "missing or invalid reset controller device tree entry"); return PTR_ERR(priv->rst); } - reset_control_deassert(priv->rst); + ret = reset_control_deassert(priv->rst); + if (ret) + return ret; ret = devm_add_action_or_reset(dev, aspeed_pwm_tacho_remove, priv); if (ret) diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c index 702edb831394..e297557ca346 100644 --- a/drivers/hwmon/asus_rog_ryujin.c +++ b/drivers/hwmon/asus_rog_ryujin.c @@ -184,6 +184,7 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, struct completion *status_completion) { + unsigned long flags; int ret; /* @@ -191,9 +192,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i * completion. Reinit is done because hidraw could have triggered * the raw event parsing and marked the passed in completion as done. */ - spin_lock_bh(&priv->status_report_request_lock); + spin_lock_irqsave(&priv->status_report_request_lock, flags); reinit_completion(status_completion); - spin_unlock_bh(&priv->status_report_request_lock); + spin_unlock_irqrestore(&priv->status_report_request_lock, flags); /* Send command for getting data */ ret = rog_ryujin_write_expanded(priv, cmd, cmd_length); @@ -421,11 +422,19 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo int size) { struct rog_ryujin_data *priv = hid_get_drvdata(hdev); + unsigned long flags; - if (data[0] != RYUJIN_CMD_PREFIX) + if (size < 2 || data[0] != RYUJIN_CMD_PREFIX) return 0; + spin_lock_irqsave(&priv->status_report_request_lock, flags); + if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) { + if (size <= priv->info->temp_offset + 1 || + size <= priv->info->pump_speed_offset + 1 || + size <= priv->info->fan_speed_offset + 1) + goto unlock; + /* Received coolant temp and speeds of pump and internal fan */ priv->temp_input[0] = data[priv->info->temp_offset] * 1000 + data[priv->info->temp_offset + 1] * 100; @@ -437,6 +446,9 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo if (!completion_done(&priv->cooler_status_received)) complete_all(&priv->cooler_status_received); } else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1) + goto unlock; + /* Received speeds of four fans attached to the controller */ priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1); priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2); @@ -446,6 +458,9 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo if (!completion_done(&priv->controller_status_received)) complete_all(&priv->controller_status_received); } else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_INTERNAL_FAN_DUTY) + goto unlock; + /* Received report for pump and internal fan duties (in %) */ if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) { /* @@ -463,7 +478,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo * We're expecting a report, so parse it. */ goto read_cooler_duty; - return 0; + goto unlock; } read_cooler_duty: priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]); @@ -472,6 +487,9 @@ read_cooler_duty: if (!completion_done(&priv->cooler_duty_received)) complete_all(&priv->cooler_duty_received); } else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) { + if (size <= RYUJIN_CONTROLLER_DUTY) + goto unlock; + /* Received report for controller duty for fans (in PWM) */ if (data[RYUJIN_CONTROLLER_DUTY] == 0) { /* @@ -489,7 +507,7 @@ read_cooler_duty: * We're expecting a report, so parse it. */ goto read_controller_duty; - return 0; + goto unlock; } read_controller_duty: priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY]; @@ -498,6 +516,8 @@ read_controller_duty: complete_all(&priv->controller_duty_received); } +unlock: + spin_unlock_irqrestore(&priv->status_report_request_lock, flags); return 0; } diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 086571d556b7..9bef767b589e 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -92,11 +92,6 @@ struct cc2_data { bool process_irqs; }; -enum cc2_chan_addr { - CC2_CHAN_TEMP = 0, - CC2_CHAN_HUMIDITY, -}; - /* %RH as a per cent mille from a register value */ static long cc2_rh_convert(u16 data) { @@ -499,7 +494,7 @@ static irqreturn_t cc2_low_interrupt(int irq, void *data) if (cc2->process_irqs) { hwmon_notify_event(cc2->hwmon, hwmon_humidity, - hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY); + hwmon_humidity_min_alarm, 0); cc2->rh_alarm.low_alarm = true; } @@ -512,7 +507,7 @@ static irqreturn_t cc2_high_interrupt(int irq, void *data) if (cc2->process_irqs) { hwmon_notify_event(cc2->hwmon, hwmon_humidity, - hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY); + hwmon_humidity_max_alarm, 0); cc2->rh_alarm.high_alarm = true; } diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index 8354a002f4c5..c09645152613 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -566,21 +566,18 @@ static int bootloader_show(struct seq_file *seqf, void *unused) } DEFINE_SHOW_ATTRIBUTE(bootloader); -static void ccp_debugfs_init(struct ccp_device *ccp) +static void ccp_debugfs_init(struct ccp_device *ccp, bool fw_valid, bool bl_valid) { char name[32]; - int ret; scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev)); ccp->debugfs = debugfs_create_dir(name, NULL); - ret = get_fw_version(ccp); - if (!ret) + if (fw_valid) debugfs_create_file("firmware_version", 0444, ccp->debugfs, ccp, &firmware_fops); - ret = get_bl_version(ccp); - if (!ret) + if (bl_valid) debugfs_create_file("bootloader_version", 0444, ccp->debugfs, ccp, &bootloader_fops); } @@ -588,6 +585,7 @@ static void ccp_debugfs_init(struct ccp_device *ccp) static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct ccp_device *ccp; + bool fw_valid, bl_valid; int ret; ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL); @@ -632,17 +630,27 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) goto out_hw_close; - ccp_debugfs_init(ccp); + /* + * Query the versions before registering the hwmon device: they send + * USB commands without holding ccp->mutex, which is only safe while + * nothing else can call send_usb_cmd(). + */ + fw_valid = !get_fw_version(ccp); + bl_valid = !get_bl_version(ccp); ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro", ccp, &ccp_chip_info, NULL); if (IS_ERR(ccp->hwmon_dev)) { ret = PTR_ERR(ccp->hwmon_dev); - goto out_hw_close; + goto out_debugfs_remove; } + ccp_debugfs_init(ccp, fw_valid, bl_valid); + return 0; +out_debugfs_remove: + debugfs_remove_recursive(ccp->debugfs); out_hw_close: hid_hw_close(hdev); hid_device_io_stop(hdev); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 084828e1e281..df8bd9707605 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/devm-helpers.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/kstrtox.h> @@ -84,6 +85,7 @@ static DEVICE_ATTR_RO(fan1_alarm); static int fan_alarm_init(struct gpio_fan_data *fan_data) { int alarm_irq; + int err; struct device *dev = fan_data->dev; /* @@ -94,7 +96,11 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data) if (alarm_irq <= 0) return 0; - INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); + err = devm_work_autocancel(dev, &fan_data->alarm_work, + fan_alarm_notify); + if (err) + return err; + irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, "GPIO fan alarm", fan_data); @@ -606,8 +612,11 @@ static void gpio_fan_shutdown(struct platform_device *pdev) { struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); - if (fan_data->gpios) + if (fan_data->gpios) { + mutex_lock(&fan_data->lock); set_fan_speed(fan_data, 0); + mutex_unlock(&fan_data->lock); + } } static int gpio_fan_runtime_suspend(struct device *dev) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 41755910a25a..10d2df3efdfa 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -318,6 +318,11 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) return 1; } +static bool is_hwmon_device(struct device *dev) +{ + return dev->class == &hwmon_class; +} + #if IS_REACHABLE(CONFIG_I2C) /* @@ -338,7 +343,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) static int hwmon_match_device(struct device *dev, const void *data) { - return dev->class == &hwmon_class; + return is_hwmon_device(dev); } static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy, @@ -371,18 +376,17 @@ static ssize_t pec_store(struct device *dev, const struct device_attribute *deva * handling is not required. */ hwdev = to_hwmon_device(hdev); - guard(mutex)(&hwdev->lock); - if (hwdev->chip->ops->write) { - err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val); - if (err && err != -EOPNOTSUPP) - goto put; + scoped_guard(mutex, &hwdev->lock) { + if (hwdev->chip->ops->write) { + err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val); + if (err && err != -EOPNOTSUPP) + goto put; + } + if (!val) + client->flags &= ~I2C_CLIENT_PEC; + else + client->flags |= I2C_CLIENT_PEC; } - - if (!val) - client->flags &= ~I2C_CLIENT_PEC; - else - client->flags |= I2C_CLIENT_PEC; - err = count; put: put_device(hdev); @@ -782,6 +786,9 @@ int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, const char *template; int base; + if (WARN(!is_hwmon_device(dev), "%s is not a hardware monitoring device\n", + dev_name(dev))) + return -EINVAL; if (type >= ARRAY_SIZE(__templates)) return -EINVAL; if (attr >= __templates_size[type]) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 5c6dc2c370d8..19b35f3bf3a3 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -8,6 +8,7 @@ */ #include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/bits.h> #include <linux/delay.h> #include <linux/device.h> @@ -129,6 +130,17 @@ enum ina2xx_ids { sy24655 }; +enum ina2xx_alert_type { + INA2XX_ALERT_NONE, + INA2XX_ALERT_CURRENT_LOW, + INA2XX_ALERT_CURRENT_HIGH, + INA2XX_ALERT_POWER_HIGH, + INA2XX_ALERT_BUS_VOLTAGE_LOW, + INA2XX_ALERT_BUS_VOLTAGE_HIGH, + INA2XX_ALERT_SHUNT_VOLTAGE_LOW, + INA2XX_ALERT_SHUNT_VOLTAGE_HIGH, +}; + struct ina2xx_config { u16 config_default; bool has_alerts; /* chip supports alerts and limits */ @@ -148,6 +160,7 @@ struct ina2xx_data { const struct ina2xx_config *config; enum ina2xx_ids chip; + enum ina2xx_alert_type active_alert; long rshunt; long current_lsb_uA; long power_lsb_uW; @@ -428,16 +441,78 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) } } -static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg, long *val) +static u32 ina2xx_alert_type_to_mask(enum ina2xx_alert_type alert) +{ + switch (alert) { + case INA2XX_ALERT_CURRENT_LOW: + case INA2XX_ALERT_SHUNT_VOLTAGE_LOW: + return INA226_SHUNT_UNDER_VOLTAGE_MASK; + case INA2XX_ALERT_CURRENT_HIGH: + case INA2XX_ALERT_SHUNT_VOLTAGE_HIGH: + return INA226_SHUNT_OVER_VOLTAGE_MASK; + case INA2XX_ALERT_BUS_VOLTAGE_LOW: + return INA226_BUS_UNDER_VOLTAGE_MASK; + case INA2XX_ALERT_BUS_VOLTAGE_HIGH: + return INA226_BUS_OVER_VOLTAGE_MASK; + case INA2XX_ALERT_POWER_HIGH: + return INA226_POWER_OVER_LIMIT_MASK; + case INA2XX_ALERT_NONE: + return 0; + default: + /* programmer error */ + WARN_ON_ONCE(1); + return 0; + } +} + +static enum ina2xx_alert_type ina2xx_mask_to_alert_type(u32 mask) +{ + int top_bit = fls(mask & INA226_ALERT_CONFIG_MASK); + + if (!top_bit) + return INA2XX_ALERT_NONE; + + /* + * Multiple bits may be set, with the highest-set function taking + * precedence according to the datasheet. Shunt voltage masks are + * assumed to map to voltage monitoring rather than current monitoring, + * since the latter isn't directly implemented in the hardware. + */ + switch (BIT(top_bit - 1)) { + case INA226_SHUNT_OVER_VOLTAGE_MASK: + return INA2XX_ALERT_SHUNT_VOLTAGE_HIGH; + case INA226_SHUNT_UNDER_VOLTAGE_MASK: + return INA2XX_ALERT_SHUNT_VOLTAGE_LOW; + case INA226_BUS_OVER_VOLTAGE_MASK: + return INA2XX_ALERT_BUS_VOLTAGE_HIGH; + case INA226_BUS_UNDER_VOLTAGE_MASK: + return INA2XX_ALERT_BUS_VOLTAGE_LOW; + case INA226_POWER_OVER_LIMIT_MASK: + return INA2XX_ALERT_POWER_HIGH; + default: + return INA2XX_ALERT_NONE; + } +} + +static int ina226_alert_limit_read(struct ina2xx_data *data, enum ina2xx_alert_type alert, + int reg, long *val) { struct regmap *regmap = data->regmap; int regval; + u32 mask; int ret; + /* Avoid nonzero reads from inactive alerts caused by shared limit register */ + if (data->active_alert != alert) { + *val = 0; + return 0; + } + ret = regmap_read(regmap, INA226_MASK_ENABLE, ®val); if (ret) return ret; + mask = ina2xx_alert_type_to_mask(alert); if (regval & mask) { ret = regmap_read(regmap, INA226_ALERT_LIMIT, ®val); if (ret) @@ -449,9 +524,11 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg, return 0; } -static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, long val) +static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_type alert, + int reg, long val) { struct regmap *regmap = data->regmap; + u32 mask; int ret; if (val < 0) @@ -466,15 +543,22 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, INA226_ALERT_CONFIG_MASK, 0); if (ret < 0) return ret; + data->active_alert = INA2XX_ALERT_NONE; ret = regmap_write(regmap, INA226_ALERT_LIMIT, ina226_alert_to_reg(data, reg, val)); if (ret < 0) return ret; - if (val) - return regmap_update_bits(regmap, INA226_MASK_ENABLE, - INA226_ALERT_CONFIG_MASK, mask); + if (val) { + mask = ina2xx_alert_type_to_mask(alert); + ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, mask); + if (ret < 0) + return ret; + data->active_alert = alert; + } + return 0; } @@ -498,15 +582,26 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val) return 0; } -static int ina226_alert_read(struct regmap *regmap, u32 mask, long *val) +static int ina226_alert_read(struct ina2xx_data *data, enum ina2xx_alert_type alert, long *val) { unsigned int regval; + u32 mask; int ret; - ret = regmap_read_bypassed(regmap, INA226_MASK_ENABLE, ®val); + /* + * With alert latching, reading alerts from hardware also clears the + * alert, so return early if the alert is inactive. + */ + if (data->active_alert != alert) { + *val = 0; + return 0; + } + + ret = regmap_read_bypassed(data->regmap, INA226_MASK_ENABLE, ®val); if (ret) return ret; + mask = ina2xx_alert_type_to_mask(alert); *val = (regval & mask) && (regval & INA226_ALERT_FUNCTION_FLAG); return 0; @@ -515,10 +610,10 @@ static int ina226_alert_read(struct regmap *regmap, u32 mask, long *val) static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) { int voltage_reg = channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE; - u32 under_voltage_mask = channel ? INA226_BUS_UNDER_VOLTAGE_MASK - : INA226_SHUNT_UNDER_VOLTAGE_MASK; - u32 over_voltage_mask = channel ? INA226_BUS_OVER_VOLTAGE_MASK - : INA226_SHUNT_OVER_VOLTAGE_MASK; + enum ina2xx_alert_type under_voltage_alert = channel ? INA2XX_ALERT_BUS_VOLTAGE_LOW + : INA2XX_ALERT_SHUNT_VOLTAGE_LOW; + enum ina2xx_alert_type over_voltage_alert = channel ? INA2XX_ALERT_BUS_VOLTAGE_HIGH + : INA2XX_ALERT_SHUNT_VOLTAGE_HIGH; struct ina2xx_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; unsigned int regval; @@ -532,15 +627,15 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) *val = ina2xx_get_value(data, voltage_reg, regval); break; case hwmon_in_lcrit: - return ina226_alert_limit_read(data, under_voltage_mask, + return ina226_alert_limit_read(data, under_voltage_alert, voltage_reg, val); case hwmon_in_crit: - return ina226_alert_limit_read(data, over_voltage_mask, + return ina226_alert_limit_read(data, over_voltage_alert, voltage_reg, val); case hwmon_in_lcrit_alarm: - return ina226_alert_read(regmap, under_voltage_mask, val); + return ina226_alert_read(data, under_voltage_alert, val); case hwmon_in_crit_alarm: - return ina226_alert_read(regmap, over_voltage_mask, val); + return ina226_alert_read(data, over_voltage_alert, val); default: return -EOPNOTSUPP; } @@ -594,10 +689,10 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val) case hwmon_power_average: return sy24655_average_power_read(data, SY24655_EIN, val); case hwmon_power_crit: - return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK, + return ina226_alert_limit_read(data, INA2XX_ALERT_POWER_HIGH, INA2XX_POWER, val); case hwmon_power_crit_alarm: - return ina226_alert_read(data->regmap, INA226_POWER_OVER_LIMIT_MASK, val); + return ina226_alert_read(data, INA2XX_ALERT_POWER_HIGH, val); default: return -EOPNOTSUPP; } @@ -633,15 +728,15 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) *val = ina2xx_get_value(data, INA2XX_CURRENT, regval); return 0; case hwmon_curr_lcrit: - return ina226_alert_limit_read(data, INA226_SHUNT_UNDER_VOLTAGE_MASK, + return ina226_alert_limit_read(data, INA2XX_ALERT_CURRENT_LOW, INA2XX_CURRENT, val); case hwmon_curr_crit: - return ina226_alert_limit_read(data, INA226_SHUNT_OVER_VOLTAGE_MASK, + return ina226_alert_limit_read(data, INA2XX_ALERT_CURRENT_HIGH, INA2XX_CURRENT, val); case hwmon_curr_lcrit_alarm: - return ina226_alert_read(regmap, INA226_SHUNT_UNDER_VOLTAGE_MASK, val); + return ina226_alert_read(data, INA2XX_ALERT_CURRENT_LOW, val); case hwmon_curr_crit_alarm: - return ina226_alert_read(regmap, INA226_SHUNT_OVER_VOLTAGE_MASK, val); + return ina226_alert_read(data, INA2XX_ALERT_CURRENT_HIGH, val); default: return -EOPNOTSUPP; } @@ -685,12 +780,12 @@ static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val) switch (attr) { case hwmon_in_lcrit: return ina226_alert_limit_write(data, - channel ? INA226_BUS_UNDER_VOLTAGE_MASK : INA226_SHUNT_UNDER_VOLTAGE_MASK, + channel ? INA2XX_ALERT_BUS_VOLTAGE_LOW : INA2XX_ALERT_SHUNT_VOLTAGE_LOW, channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE, val); case hwmon_in_crit: return ina226_alert_limit_write(data, - channel ? INA226_BUS_OVER_VOLTAGE_MASK : INA226_SHUNT_OVER_VOLTAGE_MASK, + channel ? INA2XX_ALERT_BUS_VOLTAGE_HIGH : INA2XX_ALERT_SHUNT_VOLTAGE_HIGH, channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE, val); default: @@ -705,7 +800,7 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val) switch (attr) { case hwmon_power_crit: - return ina226_alert_limit_write(data, INA226_POWER_OVER_LIMIT_MASK, + return ina226_alert_limit_write(data, INA2XX_ALERT_POWER_HIGH, INA2XX_POWER, val); default: return -EOPNOTSUPP; @@ -719,10 +814,10 @@ static int ina2xx_curr_write(struct device *dev, u32 attr, long val) switch (attr) { case hwmon_curr_lcrit: - return ina226_alert_limit_write(data, INA226_SHUNT_UNDER_VOLTAGE_MASK, + return ina226_alert_limit_write(data, INA2XX_ALERT_CURRENT_LOW, INA2XX_CURRENT, val); case hwmon_curr_crit: - return ina226_alert_limit_write(data, INA226_SHUNT_OVER_VOLTAGE_MASK, + return ina226_alert_limit_write(data, INA2XX_ALERT_CURRENT_HIGH, INA2XX_CURRENT, val); default: return -EOPNOTSUPP; @@ -883,8 +978,12 @@ static ssize_t shunt_resistor_show(struct device *dev, struct device_attribute *da, char *buf) { struct ina2xx_data *data = dev_get_drvdata(dev); + long rshunt; - return sysfs_emit(buf, "%li\n", data->rshunt); + scoped_guard(hwmon_lock, dev) { + rshunt = data->rshunt; + } + return sysfs_emit(buf, "%li\n", rshunt); } static ssize_t shunt_resistor_store(struct device *dev, @@ -940,6 +1039,16 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) if (data->config->has_alerts) { bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high"); + unsigned int mask_enable; + + /* + * Infer active alert from MASK_ENABLE in case it's already + * configured (e.g., by a past probe or firmware) + */ + ret = regmap_read(regmap, INA226_MASK_ENABLE, &mask_enable); + if (ret < 0) + return ret; + data->active_alert = ina2xx_mask_to_alert_type(mask_enable); regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_LATCH_ENABLE | INA226_ALERT_POLARITY, diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index b1675dc5b3c7..54ba4b8542e9 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -1106,7 +1106,7 @@ static const struct clk_ops ltc4282_ops = { static int ltc428_clk_provider_setup(struct ltc4282_state *st, struct device *dev) { - struct clk_init_data init; + struct clk_init_data init = {}; int ret; if (!IS_ENABLED(CONFIG_COMMON_CLK)) diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c index 9e19e2697e25..3918dc36c946 100644 --- a/drivers/hwmon/mcp9982.c +++ b/drivers/hwmon/mcp9982.c @@ -395,6 +395,8 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at reg_status, !(reg_status & MCP9982_STATUS_BUSY), MCP9982_WAKE_UP_TIME_US, MCP9982_WAKE_UP_TIME_US * 10); + if (ret) + return ret; break; } break; diff --git a/drivers/hwmon/nct6694-hwmon.c b/drivers/hwmon/nct6694-hwmon.c index 6dcf22ca5018..9a9a4db434c4 100644 --- a/drivers/hwmon/nct6694-hwmon.c +++ b/drivers/hwmon/nct6694-hwmon.c @@ -159,6 +159,9 @@ static inline s8 temp_to_reg(long val) #define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \ HWMON_T_MAX | HWMON_T_MAX_HYST | \ HWMON_T_MAX_ALARM) +#define NCT6694_HWMON_DTIN_CONFIG (HWMON_T_INPUT | \ + HWMON_T_MAX | HWMON_T_MAX_HYST | \ + HWMON_T_MAX_ALARM) #define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \ HWMON_F_MIN | HWMON_F_MIN_ALARM) #define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \ @@ -193,22 +196,22 @@ static const struct hwmon_channel_info *nct6694_info[] = { NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */ NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */ NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */ - NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */ - NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN0 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN1 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN2 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN3 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN4 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN5 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN6 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN7 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN8 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN9 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN10 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN11 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN12 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN13 */ + NCT6694_HWMON_DTIN_CONFIG, /* DTIN14 */ + NCT6694_HWMON_DTIN_CONFIG), /* DTIN15 */ HWMON_CHANNEL_INFO(fan, NCT6694_HWMON_FAN_CONFIG, /* FIN0 */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 806c9a4913bb..5f69c1420b4e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1275,7 +1275,9 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, regval = status & mask; if (regval) { - if (data->revision >= PMBUS_REV_12) { + /* Generic STATUS_WORD alarms are not individually clearable. */ + if (data->revision >= PMBUS_REV_12 && + reg != PMBUS_STATUS_WORD) { ret = _pmbus_write_byte_data(client, page, reg, regval); if (ret) return ret; diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 9cace0e8acda..a97dda9e92dc 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -277,6 +277,8 @@ static ssize_t heater_enable_store(struct device *dev, heating_time_bound = 1100; } + guard(hwmon_lock)(dev); + if (time_before(jiffies, data->heating_complete)) return -EBUSY; @@ -286,7 +288,7 @@ static ssize_t heater_enable_store(struct device *dev, data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound); data->data_pending = true; - return 0; + return count; } static ssize_t heater_power_show(struct device *dev, @@ -314,6 +316,8 @@ static ssize_t heater_power_store(struct device *dev, if (power != 20 && power != 110 && power != 200) return -EINVAL; + guard(hwmon_lock)(dev); + data->heater_power = power; return count; @@ -344,6 +348,8 @@ static ssize_t heater_time_store(struct device *dev, if (time != 100 && time != 1000) return -EINVAL; + guard(hwmon_lock)(dev); + data->heater_time = time; return count; diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c index 48fa5148d9e2..278cb089b0fd 100644 --- a/drivers/hwmon/yogafan.c +++ b/drivers/hwmon/yogafan.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * yoga_fan.c - Lenovo Yoga/Legion Fan Hardware Monitoring Driver * * Provides fan speed monitoring for Lenovo Yoga, Legion, and IdeaPad diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6d65c43d574f..928488a216d4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1679,7 +1679,7 @@ coresight_allocate_device_list(const char *prefix) return list; } - list = kzalloc(sizeof(*list), GFP_KERNEL); + list = kzalloc_obj(*list); if (!list) return NULL; diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index b7521b7ece37..6294b0b0cc8f 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -418,7 +418,7 @@ static struct i2c_gpio_scl_data *i2c_gpio_create_scl(struct device *dev) } } - scl = kzalloc(sizeof(*scl), GFP_KERNEL); + scl = kzalloc_obj(*scl); if (!scl) { if (sharable) fwnode_handle_put(args.fwnode); diff --git a/drivers/i3c/master/amd-i3c-master.c b/drivers/i3c/master/amd-i3c-master.c index ef5ad5abb788..e4da9c38eccb 100644 --- a/drivers/i3c/master/amd-i3c-master.c +++ b/drivers/i3c/master/amd-i3c-master.c @@ -376,7 +376,7 @@ static struct xi3c_xfer *xi3c_master_alloc_xfer(unsigned int ncmds) { struct xi3c_xfer *xfer; - xfer = kzalloc_flex(*xfer, cmds, ncmds, GFP_KERNEL); + xfer = kzalloc_flex(*xfer, cmds, ncmds); if (!xfer) return NULL; @@ -735,7 +735,7 @@ static int xi3c_master_send_bdcast_ccc_cmd(struct xi3c_master *master, if (!xfer) return -ENOMEM; - buf = kmalloc_objs(*buf, xfer_len, GFP_KERNEL); + buf = kmalloc_objs(*buf, xfer_len); if (!buf) return -ENOMEM; diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c index 505fa68ee539..5ae0d694d20c 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -796,8 +796,8 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) struct ad7280_state *st = iio_priv(indio_dev); int i, ret; - unsigned int *channels __free(kfree) = kcalloc(st->scan_cnt, sizeof(*channels), - GFP_KERNEL); + unsigned int *channels __free(kfree) = kzalloc_objs(*channels, + st->scan_cnt); if (!channels) return IRQ_HANDLED; diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index ecc02a427b92..1bd82d3db5ea 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -109,7 +109,7 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, if (nents < 0) return nents; - vecs = kmalloc_array(nents, sizeof(*vecs), GFP_ATOMIC); + vecs = kmalloc_objs(*vecs, nents, GFP_ATOMIC); if (!vecs) return -ENOMEM; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index fb08e4f02520..1f22275de55c 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -345,7 +345,7 @@ static struct iio_channel *iio_channel_get_sys(const char *name, return ERR_PTR(-ENODEV); struct iio_channel *channel __free(kfree) = - kzalloc(sizeof(*channel), GFP_KERNEL); + kzalloc_obj(*channel); if (!channel) { err = -ENOMEM; goto error_no_mem; diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 990cf928b32a..51af8c1c49f9 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -314,10 +314,6 @@ struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, if (!nlmsg_request) return NULL; - spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags); - list_add_tail(&nlmsg_request->inprocess_list, &iwpm_nlmsg_req_list); - spin_unlock_irqrestore(&iwpm_nlmsg_req_lock, flags); - kref_init(&nlmsg_request->kref); kref_get(&nlmsg_request->kref); nlmsg_request->nlmsg_seq = nlmsg_seq; @@ -326,6 +322,11 @@ struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, nlmsg_request->err_code = 0; sema_init(&nlmsg_request->sem, 1); down(&nlmsg_request->sem); + + spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags); + list_add_tail(&nlmsg_request->inprocess_list, &iwpm_nlmsg_req_list); + spin_unlock_irqrestore(&iwpm_nlmsg_req_lock, flags); + return nlmsg_request; } diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index e0b3b36b8b14..3c91f00d09c6 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -2059,6 +2059,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, int ret; INIT_LIST_HEAD(&mad_recv_wc->rmpp_list); + list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list); + ret = ib_mad_enforce_security(mad_agent_priv, mad_recv_wc->wc->pkey_index); if (ret) { @@ -2067,7 +2069,6 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, return; } - list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list); if (is_kernel_rmpp_data_response(mad_agent_priv, mad_recv_wc)) { spin_lock_irqsave(&mad_agent_priv->lock, flags); mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc); diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index a4014a230639..4e8fbee34745 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -2943,7 +2943,7 @@ static int nldev_frmr_pools_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, u32 aging_period; int err; - tb = kzalloc_objs(*tb, RDMA_NLDEV_ATTR_MAX, GFP_KERNEL); + tb = kzalloc_objs(*tb, RDMA_NLDEV_ATTR_MAX); if (!tb) return -ENOMEM; diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index fd5651c003ae..a7cbe643e33c 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -69,7 +69,6 @@ void ib_uverbs_release_file(struct kref *ref) if (file->disassociate_page) __free_pages(file->disassociate_page, 0); - mutex_destroy(&file->disassociation_lock); mutex_destroy(&file->umap_lock); mutex_destroy(&file->ucontext_lock); kfree(file); diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 4929636f7c53..a15182f7a7c5 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1556,9 +1556,10 @@ static ssize_t ucma_process_join(struct ucma_file *file, mutex_lock(&ctx->mutex); ret = rdma_join_multicast(ctx->cm_id, (struct sockaddr *)&mc->addr, join_state, mc); - mutex_unlock(&ctx->mutex); - if (ret) + if (ret) { + mutex_unlock(&ctx->mutex); goto err_xa_erase; + } resp.id = mc->id; if (copy_to_user(u64_to_user_ptr(cmd->response), @@ -1566,6 +1567,7 @@ static ssize_t ucma_process_join(struct ucma_file *file, ret = -EFAULT; goto err_leave_multicast; } + mutex_unlock(&ctx->mutex); xa_store(&multicast_table, mc->id, mc, 0); @@ -1573,7 +1575,6 @@ static ssize_t ucma_process_join(struct ucma_file *file, return 0; err_leave_multicast: - mutex_lock(&ctx->mutex); rdma_leave_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr); mutex_unlock(&ctx->mutex); ucma_cleanup_mc_events(mc); diff --git a/drivers/infiniband/core/uverbs_flow.c b/drivers/infiniband/core/uverbs_flow.c index 1528a294f7f8..de5a2769f084 100644 --- a/drivers/infiniband/core/uverbs_flow.c +++ b/drivers/infiniband/core/uverbs_flow.c @@ -26,6 +26,7 @@ out: return resources; err: + kfree(resources->collection); kfree(resources->counters); kfree(resources); diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 0d88b2ee68ff..2a046c888dfa 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -644,12 +644,15 @@ static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma) goto out; } - mutex_lock(&file->disassociation_lock); + if (!down_read_trylock(&file->hw_destroy_rwsem)) { + ret = -EIO; + goto out; + } vma->vm_ops = &rdma_umap_ops; ret = ucontext->device->ops.mmap(ucontext, vma); - mutex_unlock(&file->disassociation_lock); + up_read(&file->hw_destroy_rwsem); out: srcu_read_unlock(&file->device->disassociate_srcu, srcu_key); return ret; @@ -671,7 +674,6 @@ static void rdma_umap_open(struct vm_area_struct *vma) /* We are racing with disassociation */ if (!down_read_trylock(&ufile->hw_destroy_rwsem)) goto out_zap; - mutex_lock(&ufile->disassociation_lock); /* * Disassociation already completed, the VMA should already be zapped. @@ -684,12 +686,10 @@ static void rdma_umap_open(struct vm_area_struct *vma) goto out_unlock; rdma_umap_priv_init(priv, vma, opriv->entry); - mutex_unlock(&ufile->disassociation_lock); up_read(&ufile->hw_destroy_rwsem); return; out_unlock: - mutex_unlock(&ufile->disassociation_lock); up_read(&ufile->hw_destroy_rwsem); out_zap: /* @@ -773,7 +773,7 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile) { struct rdma_umap_priv *priv, *next_priv; - mutex_lock(&ufile->disassociation_lock); + lockdep_assert_held_write(&ufile->hw_destroy_rwsem); while (1) { struct mm_struct *mm = NULL; @@ -799,10 +799,8 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile) break; } mutex_unlock(&ufile->umap_lock); - if (!mm) { - mutex_unlock(&ufile->disassociation_lock); + if (!mm) return; - } /* * The umap_lock is nested under mmap_lock since it used within @@ -832,8 +830,6 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile) mmap_read_unlock(mm); mmput(mm); } - - mutex_unlock(&ufile->disassociation_lock); } /** @@ -851,8 +847,11 @@ void rdma_user_mmap_disassociate(struct ib_device *device) mutex_lock(&uverbs_dev->lists_mutex); list_for_each_entry(ufile, &uverbs_dev->uverbs_file_list, list) { - if (ufile->ucontext) + if (ufile->ucontext) { + down_write(&ufile->hw_destroy_rwsem); uverbs_user_mmap_disassociate(ufile); + up_write(&ufile->hw_destroy_rwsem); + } } mutex_unlock(&uverbs_dev->lists_mutex); } @@ -927,8 +926,6 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp) mutex_init(&file->umap_lock); INIT_LIST_HEAD(&file->umaps); - mutex_init(&file->disassociation_lock); - filp->private_data = file; list_add_tail(&file->list, &dev->uverbs_file_list); mutex_unlock(&dev->lists_mutex); diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 04abc80c1327..c43e25d37266 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -2058,11 +2058,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) return -ENODEV; rtnl_lock(); - rc = __ethtool_get_link_ksettings(netdev, &lksettings); - rtnl_unlock(); - - dev_put(netdev); + if (READ_ONCE(netdev->reg_state) != NETREG_REGISTERED) { + dev_put(netdev); + rtnl_unlock(); + return -ENODEV; + } + rc = __ethtool_get_link_ksettings(netdev, &lksettings); if (!rc && lksettings.base.speed != (u32)SPEED_UNKNOWN) { netdev_speed = lksettings.base.speed; } else { @@ -2071,6 +2073,8 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) pr_warn("%s speed is unknown, defaulting to %u\n", netdev->name, netdev_speed); } + dev_put(netdev); + rtnl_unlock(); ib_get_width_and_speed(netdev_speed, lksettings.lanes, speed, width); diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index ce72db1b4bc3..17654a9e23fe 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -356,9 +356,13 @@ static int bnxt_re_update_qp1_tos_dscp(struct bnxt_re_dev *rdev) return bnxt_qplib_modify_qp(&rdev->qplib_res, &qp->qplib_qp); } -static void bnxt_re_init_dcb_wq(struct bnxt_re_dev *rdev) +static int bnxt_re_init_dcb_wq(struct bnxt_re_dev *rdev) { rdev->dcb_wq = create_singlethread_workqueue("bnxt_re_dcb_wq"); + if (!rdev->dcb_wq) + return -ENOMEM; + + return 0; } static void bnxt_re_uninit_dcb_wq(struct bnxt_re_dev *rdev) @@ -2339,7 +2343,9 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 op_type) } bnxt_re_debugfs_add_pdev(rdev); - bnxt_re_init_dcb_wq(rdev); + rc = bnxt_re_init_dcb_wq(rdev); + if (rc) + goto fail; bnxt_re_net_register_async_event(rdev); if (!rdev->is_virtfn) diff --git a/drivers/infiniband/hw/bnxt_re/uapi.c b/drivers/infiniband/hw/bnxt_re/uapi.c index feaf98631fc5..a407c6bc469b 100644 --- a/drivers/infiniband/hw/bnxt_re/uapi.c +++ b/drivers/infiniband/hw/bnxt_re/uapi.c @@ -462,7 +462,6 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_DBR_ALLOC)(struct uverbs_attr_bundle *a uobj->object = obj; uverbs_finalize_uobj_create(attrs, BNXT_RE_ALLOC_DBR_HANDLE); - dbr.umdbr = dpi->umdbr; dbr.dpi = dpi->dpi; ret = uverbs_copy_to_struct_or_zero(attrs, BNXT_RE_ALLOC_DBR_ATTR, &dbr, sizeof(dbr)); @@ -525,7 +524,6 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_GET_DEFAULT_DBR)(struct uverbs_attr_bun return PTR_ERR(ib_uctx); uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); - dpi.umdbr = uctx->dpi.umdbr; dpi.dpi = uctx->dpi.dpi; ret = uverbs_copy_to_struct_or_zero(attrs, BNXT_RE_DEFAULT_DBR_ATTR, @@ -543,7 +541,7 @@ DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_DBR_ALLOC, UA_MANDATORY), UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_DBR_ATTR, UVERBS_ATTR_STRUCT(struct bnxt_re_db_region, - umdbr), + reserved2), UA_MANDATORY), UVERBS_ATTR_PTR_OUT(BNXT_RE_ALLOC_DBR_OFFSET, UVERBS_ATTR_TYPE(u64), @@ -563,7 +561,7 @@ DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_DBR, DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_GET_DEFAULT_DBR, UVERBS_ATTR_PTR_OUT(BNXT_RE_DEFAULT_DBR_ATTR, UVERBS_ATTR_STRUCT(struct bnxt_re_db_region, - umdbr), + reserved2), UA_MANDATORY)); DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_DEFAULT_DBR, diff --git a/drivers/infiniband/hw/efa/efa_com.c b/drivers/infiniband/hw/efa/efa_com.c index 583b1cf0d721..04c6d63c449e 100644 --- a/drivers/infiniband/hw/efa/efa_com.c +++ b/drivers/infiniband/hw/efa/efa_com.c @@ -850,7 +850,7 @@ int efa_com_admin_init(struct efa_com_dev *edev, aq->dmadev = edev->dmadev; aq->efa_dev = edev->efa_dev; - set_bit(EFA_AQ_STATE_POLLING_BIT, &aq->state); + efa_com_set_admin_polling_mode(edev, true); sema_init(&aq->avail_cmds, aq->depth); @@ -868,8 +868,6 @@ int efa_com_admin_init(struct efa_com_dev *edev, if (err) goto err_destroy_sq; - efa_com_set_admin_polling_mode(edev, false); - err = efa_com_admin_init_aenq(edev, aenq_handlers); if (err) goto err_destroy_cq; @@ -1254,7 +1252,7 @@ static void efa_com_destroy_eq(struct efa_com_dev *edev, err); } -static void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq) +void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq) { u32 val = 0; @@ -1343,7 +1341,6 @@ int efa_com_eq_init(struct efa_com_dev *edev, struct efa_com_eq *eeq, eeq->phase = 1; eeq->depth = params.depth; eeq->cb = cb; - efa_com_arm_eq(edev, eeq); return 0; diff --git a/drivers/infiniband/hw/efa/efa_com.h b/drivers/infiniband/hw/efa/efa_com.h index 0341704d0921..98fb6a42a6cb 100644 --- a/drivers/infiniband/hw/efa/efa_com.h +++ b/drivers/infiniband/hw/efa/efa_com.h @@ -169,6 +169,7 @@ int efa_com_admin_init(struct efa_com_dev *edev, void efa_com_admin_destroy(struct efa_com_dev *edev); int efa_com_eq_init(struct efa_com_dev *edev, struct efa_com_eq *eeq, efa_eqe_handler cb, u16 depth, u8 msix_vec); +void efa_com_arm_eq(struct efa_com_dev *edev, struct efa_com_eq *eeq); void efa_com_eq_destroy(struct efa_com_dev *edev, struct efa_com_eq *eeq); int efa_com_dev_reset(struct efa_com_dev *edev, enum efa_regs_reset_reason_types reset_reason); diff --git a/drivers/infiniband/hw/efa/efa_main.c b/drivers/infiniband/hw/efa/efa_main.c index 4cd80727dcd2..753ed8430b69 100644 --- a/drivers/infiniband/hw/efa/efa_main.c +++ b/drivers/infiniband/hw/efa/efa_main.c @@ -302,28 +302,30 @@ static void efa_set_host_info(struct efa_dev *dev) static void efa_destroy_eq(struct efa_dev *dev, struct efa_eq *eq) { - efa_com_eq_destroy(&dev->edev, &eq->eeq); efa_free_irq(dev, &eq->irq); + efa_com_eq_destroy(&dev->edev, &eq->eeq); } static int efa_create_eq(struct efa_dev *dev, struct efa_eq *eq, u32 msix_vec) { int err; - efa_setup_comp_irq(dev, eq, msix_vec); - err = efa_request_irq(dev, &eq->irq); + err = efa_com_eq_init(&dev->edev, &eq->eeq, efa_process_eqe, + dev->dev_attr.max_eq_depth, msix_vec); if (err) return err; - err = efa_com_eq_init(&dev->edev, &eq->eeq, efa_process_eqe, - dev->dev_attr.max_eq_depth, msix_vec); + efa_setup_comp_irq(dev, eq, msix_vec); + err = efa_request_irq(dev, &eq->irq); if (err) - goto err_free_comp_irq; + goto err_destroy_eq; + + efa_com_arm_eq(&dev->edev, &eq->eeq); return 0; -err_free_comp_irq: - efa_free_irq(dev, &eq->irq); +err_destroy_eq: + efa_com_eq_destroy(&dev->edev, &eq->eeq); return err; } @@ -619,18 +621,21 @@ static struct efa_dev *efa_probe_device(struct pci_dev *pdev) edev->aq.msix_vector_idx = dev->admin_msix_vector_idx; edev->aenq.msix_vector_idx = dev->admin_msix_vector_idx; - err = efa_set_mgmnt_irq(dev); + err = efa_com_admin_init(edev, &aenq_handlers); if (err) goto err_disable_msix; - err = efa_com_admin_init(edev, &aenq_handlers); + err = efa_set_mgmnt_irq(dev); if (err) - goto err_free_mgmnt_irq; + goto err_destroy_admin; + + efa_com_set_admin_polling_mode(edev, false); return dev; -err_free_mgmnt_irq: - efa_free_irq(dev, &dev->admin_irq); +err_destroy_admin: + efa_com_dev_reset(edev, EFA_REGS_RESET_INIT_ERR); + efa_com_admin_destroy(edev); err_disable_msix: efa_disable_msix(dev); err_reg_read_destroy: @@ -654,8 +659,8 @@ static void efa_remove_device(struct pci_dev *pdev, edev = &dev->edev; efa_com_dev_reset(edev, reset_reason); - efa_com_admin_destroy(edev); efa_free_irq(dev, &dev->admin_irq); + efa_com_admin_destroy(edev); efa_disable_msix(dev); efa_com_mmio_reg_read_destroy(edev); devm_iounmap(&pdev->dev, edev->reg_bar); diff --git a/drivers/infiniband/hw/erdma/erdma_main.c b/drivers/infiniband/hw/erdma/erdma_main.c index 7e87a815e853..445182c6bc5d 100644 --- a/drivers/infiniband/hw/erdma/erdma_main.c +++ b/drivers/infiniband/hw/erdma/erdma_main.c @@ -572,8 +572,8 @@ static int erdma_ib_device_add(struct pci_dev *pdev) INIT_LIST_HEAD(&dev->cep_list); spin_lock_init(&dev->lock); - xa_init_flags(&dev->qp_xa, XA_FLAGS_ALLOC1); - xa_init_flags(&dev->cq_xa, XA_FLAGS_ALLOC1); + xa_init_flags(&dev->qp_xa, XA_FLAGS_ALLOC1 | XA_FLAGS_LOCK_IRQ); + xa_init_flags(&dev->cq_xa, XA_FLAGS_ALLOC1 | XA_FLAGS_LOCK_IRQ); dev->next_alloc_cqn = 1; dev->next_alloc_qpn = 1; diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 65b1af1e6623..f18b88bba281 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -1021,15 +1021,15 @@ int erdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, init_completion(&qp->safe_free); if (qp->ibqp.qp_type == IB_QPT_GSI) { - old_entry = xa_store(&dev->qp_xa, 1, qp, GFP_KERNEL); + old_entry = xa_store_irq(&dev->qp_xa, 1, qp, GFP_KERNEL); if (xa_is_err(old_entry)) ret = xa_err(old_entry); else qp->ibqp.qp_num = 1; } else { - ret = xa_alloc_cyclic(&dev->qp_xa, &qp->ibqp.qp_num, qp, - XA_LIMIT(1, dev->attrs.max_qp - 1), - &dev->next_alloc_qpn, GFP_KERNEL); + ret = xa_alloc_cyclic_irq(&dev->qp_xa, &qp->ibqp.qp_num, qp, + XA_LIMIT(1, dev->attrs.max_qp - 1), + &dev->next_alloc_qpn, GFP_KERNEL); } if (ret < 0) { @@ -1089,7 +1089,7 @@ err_out_cmd: else free_kernel_qp(qp); err_out_xa: - xa_erase(&dev->qp_xa, QP_ID(qp)); + xa_erase_irq(&dev->qp_xa, QP_ID(qp)); err_out: return ret; } @@ -1993,9 +1993,9 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, refcount_set(&cq->refcount, 1); init_completion(&cq->free); - ret = xa_alloc_cyclic(&dev->cq_xa, &cq->cqn, cq, - XA_LIMIT(1, dev->attrs.max_cq - 1), - &dev->next_alloc_cqn, GFP_KERNEL); + ret = xa_alloc_cyclic_irq(&dev->cq_xa, &cq->cqn, cq, + XA_LIMIT(1, dev->attrs.max_cq - 1), + &dev->next_alloc_cqn, GFP_KERNEL); if (ret < 0) return ret; @@ -2041,7 +2041,7 @@ err_free_res: } err_out_xa: - xa_erase(&dev->cq_xa, cq->cqn); + xa_erase_irq(&dev->cq_xa, cq->cqn); return ret; } diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index dc548e6802e2..1a36f995c4f6 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -326,6 +326,7 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma) void *memvirt = NULL; dma_addr_t memdma = 0; u8 subctxt, mapio = 0, vmf = 0, type; + size_t memdmalen = 0; ssize_t memlen = 0; int ret = 0; u16 ctxt; @@ -371,7 +372,9 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma) mapio = 1; break; case PIO_CRED: { + struct credit_return_base *cr = &dd->cr_base[uctxt->sc->node]; u64 cr_page_offset; + if (flags & VM_WRITE) { ret = -EPERM; goto done; @@ -381,11 +384,18 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma) * second or third page allocated for credit returns (if number * of enabled contexts > 64 and 128 respectively). */ - cr_page_offset = ((u64)uctxt->sc->hw_free - - (u64)dd->cr_base[uctxt->numa_id].va) & - PAGE_MASK; - memvirt = dd->cr_base[uctxt->numa_id].va + cr_page_offset; - memdma = dd->cr_base[uctxt->numa_id].dma + cr_page_offset; + cr_page_offset = ((u64)uctxt->sc->hw_free - (u64)cr->va) & + PAGE_MASK; + /* + * dma_mmap_coherent() describes the whole coherent buffer and + * selects the page within it with vma->vm_pgoff, so pass the + * base of the allocation and its length and let vm_pgoff pick + * the page. + */ + vma->vm_pgoff = cr_page_offset >> PAGE_SHIFT; + memvirt = cr->va; + memdma = cr->dma; + memdmalen = TXE_NUM_CONTEXTS * sizeof(struct credit_return); memlen = PAGE_SIZE; flags &= ~VM_MAYWRITE; flags |= VM_DONTCOPY | VM_DONTEXPAND; @@ -567,7 +577,8 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma) ret = 0; } else if (memdma) { ret = dma_mmap_coherent(&dd->pcidev->dev, vma, - memvirt, memdma, memlen); + memvirt, memdma, + memdmalen ? memdmalen : memlen); } else if (mapio) { ret = io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(memaddr), diff --git a/drivers/infiniband/hw/hns/hns_roce_debugfs.c b/drivers/infiniband/hw/hns/hns_roce_debugfs.c index 05630f7c9155..103b8c9ca969 100644 --- a/drivers/infiniband/hw/hns/hns_roce_debugfs.c +++ b/drivers/infiniband/hw/hns/hns_roce_debugfs.c @@ -267,8 +267,7 @@ static int hns_roce_alloc_scc_param(struct hns_roce_dev *hr_dev) struct hns_roce_scc_param *scc_param; int i; - scc_param = kvcalloc(HNS_ROCE_SCC_ALGO_TOTAL, sizeof(*scc_param), - GFP_KERNEL); + scc_param = kvzalloc_objs(*scc_param, HNS_ROCE_SCC_ALGO_TOTAL); if (!scc_param) return -ENOMEM; diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 9cfd84dd6869..5d71300da0ff 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -4281,7 +4281,7 @@ static int irdma_post_send(struct ib_qp *ibqp, stag_info.total_len = iwmr->ibmr.length; stag_info.reg_addr_pa = *palloc->level1.addr; stag_info.first_pm_pbl_index = palloc->level1.idx; - stag_info.local_fence = ib_wr->send_flags & IB_SEND_FENCE; + stag_info.local_fence = true; if (iwmr->npages > IRDMA_MIN_PAGES_PER_FMR) stag_info.chunk_size = 1; err = irdma_sc_mr_fast_register(&iwqp->sc_qp, &stag_info, diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index e688ad66a895..5438224bf325 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -751,11 +751,13 @@ err_add: kobject_put(p); } kobject_put(dev->dev_ports_parent[slave]); + dev->dev_ports_parent[slave] = NULL; err_ports: kobject_put(dev->pkeys.device_parent[slave]); /* extra put for the device_parent create_and_add */ kobject_put(dev->pkeys.device_parent[slave]); + dev->pkeys.device_parent[slave] = NULL; fail_dev: kobject_put(dev->iov_parent); @@ -785,6 +787,8 @@ static void unregister_pkey_tree(struct mlx4_ib_dev *device) return; for (slave = device->dev->persist->num_vfs; slave >= 0; --slave) { + if (!device->pkeys.device_parent[slave]) + continue; list_for_each_entry_safe(p, t, &device->pkeys.pkey_port_list[slave], entry) { diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 373ee1f42d4a..a457647edacd 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1683,11 +1683,8 @@ static int mlx5_ib_query_port_speed_rep(struct mlx5_ib_dev *dev, u32 port_num, struct mlx5_core_dev *mdev; u16 op_mod; - if (!dev->port[port_num - 1].rep) { - mlx5_ib_warn(dev, "Representor doesn't exist for port %u\n", - port_num); - return -EINVAL; - } + if (!dev->port[port_num - 1].rep) + return -ENODEV; rep = dev->port[port_num - 1].rep; mdev = mlx5_eswitch_get_core_dev(rep->esw); diff --git a/drivers/infiniband/sw/rxe/rxe_mcast.c b/drivers/infiniband/sw/rxe/rxe_mcast.c index acd03bd87794..5ca9211ab33b 100644 --- a/drivers/infiniband/sw/rxe/rxe_mcast.c +++ b/drivers/infiniband/sw/rxe/rxe_mcast.c @@ -175,7 +175,9 @@ struct rxe_mcg *rxe_lookup_mcg(struct rxe_dev *rxe, union ib_gid *mgid) * @mgid: multicast address as a gid * @mcg: new mcg object * - * Context: caller should hold rxe->mcg lock + * Initializes the mcg fields. The mcg is private and not yet visible in + * mcg_tree, so this may run without rxe->mcg_lock; __rxe_publish_mcg() + * makes it visible under the lock once it is ready. */ static void __rxe_init_mcg(struct rxe_dev *rxe, union ib_gid *mgid, struct rxe_mcg *mcg) @@ -184,13 +186,22 @@ static void __rxe_init_mcg(struct rxe_dev *rxe, union ib_gid *mgid, memcpy(&mcg->mgid, mgid, sizeof(mcg->mgid)); INIT_LIST_HEAD(&mcg->qp_list); mcg->rxe = rxe; +} - /* caller holds a ref on mcg but that will be - * dropped when mcg goes out of scope. We need to take a ref - * on the pointer that will be saved in the red-black tree - * by __rxe_insert_mcg and used to lookup mcg from mgid later. - * Inserting mcg makes it visible to outside so this should - * be done last after the object is ready. +/** + * __rxe_publish_mcg - make a fully initialized mcg visible in mcg_tree + * @mcg: the mcg object + * + * Context: caller must hold rxe->mcg_lock and a reference on mcg + */ +static void __rxe_publish_mcg(struct rxe_mcg *mcg) +{ + /* caller holds a ref on mcg but that will be dropped when mcg goes + * out of scope. We need to take a ref on the pointer that will be + * saved in the red-black tree by __rxe_insert_mcg and used to lookup + * mcg from mgid later. Inserting mcg makes it visible to outside so + * this is done last after the object is ready and the multicast + * address has been programmed. */ kref_get(&mcg->ref_cnt); __rxe_insert_mcg(mcg); @@ -228,26 +239,37 @@ static struct rxe_mcg *rxe_get_mcg(struct rxe_dev *rxe, union ib_gid *mgid) err = -ENOMEM; goto err_dec; } + __rxe_init_mcg(rxe, mgid, mcg); + + /* program the multicast address while mcg is still private, before + * it is inserted into mcg_tree. dev_mc_add() may sleep so this must + * run outside mcg_lock. On failure mcg was never published, so a + * plain free is correct and the tree is untouched. + */ + err = rxe_mcast_add(rxe, mgid); + if (err) { + kfree(mcg); + goto err_dec; + } spin_lock_bh(&rxe->mcg_lock); - /* re-check to see if someone else just added it */ + /* re-check to see if someone else just added it while we were adding + * the multicast address; if so use theirs and drop ours + */ tmp = __rxe_lookup_mcg(rxe, mgid); if (tmp) { spin_unlock_bh(&rxe->mcg_lock); + rxe_mcast_del(rxe, mgid); atomic_dec(&rxe->mcg_num); kfree(mcg); return tmp; } - __rxe_init_mcg(rxe, mgid, mcg); + __rxe_publish_mcg(mcg); spin_unlock_bh(&rxe->mcg_lock); - /* add mcast address outside of lock */ - err = rxe_mcast_add(rxe, mgid); - if (!err) - return mcg; + return mcg; - kfree(mcg); err_dec: atomic_dec(&rxe->mcg_num); return ERR_PTR(err); diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c index 875eceb55fdf..71d9ea477289 100644 --- a/drivers/infiniband/sw/rxe/rxe_mr.c +++ b/drivers/infiniband/sw/rxe/rxe_mr.c @@ -33,7 +33,8 @@ int mr_check_range(struct rxe_mr *mr, u64 iova, size_t length) case IB_MR_TYPE_USER: case IB_MR_TYPE_MEM_REG: if (iova < mr->ibmr.iova || - iova + length > mr->ibmr.iova + mr->ibmr.length) { + length > mr->ibmr.length || + iova - mr->ibmr.iova > mr->ibmr.length - length) { rxe_dbg_mr(mr, "iova/length out of range\n"); return -EINVAL; } diff --git a/drivers/infiniband/sw/rxe/rxe_odp.c b/drivers/infiniband/sw/rxe/rxe_odp.c index e870efa7a0a3..ab21b620e94c 100644 --- a/drivers/infiniband/sw/rxe/rxe_odp.c +++ b/drivers/infiniband/sw/rxe/rxe_odp.c @@ -120,19 +120,23 @@ int rxe_odp_mr_init_user(struct rxe_dev *rxe, u64 start, u64 length, } static inline bool rxe_check_pagefault(struct ib_umem_odp *umem_odp, u64 iova, - int length) + int length, bool write) { bool need_fault = false; + u64 access = HMM_PFN_VALID; u64 addr; int idx; + if (write) + access |= HMM_PFN_WRITE; + addr = iova & (~(BIT(umem_odp->page_shift) - 1)); /* Skim through all pages that are to be accessed. */ while (addr < iova + length) { idx = (addr - ib_umem_start(umem_odp)) >> umem_odp->page_shift; - if (!(umem_odp->map.pfn_list[idx] & HMM_PFN_VALID)) { + if ((umem_odp->map.pfn_list[idx] & access) != access) { need_fault = true; break; } @@ -155,6 +159,7 @@ static unsigned long rxe_odp_iova_to_page_offset(struct ib_umem_odp *umem_odp, u static int rxe_odp_map_range_and_lock(struct rxe_mr *mr, u64 iova, int length, u32 flags) { struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem); + bool write = !(flags & RXE_PAGEFAULT_RDONLY); bool need_fault; int err; @@ -163,7 +168,7 @@ static int rxe_odp_map_range_and_lock(struct rxe_mr *mr, u64 iova, int length, u mutex_lock(&umem_odp->umem_mutex); - need_fault = rxe_check_pagefault(umem_odp, iova, length); + need_fault = rxe_check_pagefault(umem_odp, iova, length, write); if (need_fault) { mutex_unlock(&umem_odp->umem_mutex); @@ -173,7 +178,7 @@ static int rxe_odp_map_range_and_lock(struct rxe_mr *mr, u64 iova, int length, u if (err < 0) return err; - need_fault = rxe_check_pagefault(umem_odp, iova, length); + need_fault = rxe_check_pagefault(umem_odp, iova, length, write); if (need_fault) { mutex_unlock(&umem_odp->umem_mutex); return -EFAULT; @@ -335,8 +340,9 @@ int rxe_odp_flush_pmem_iova(struct rxe_mr *mr, u64 iova, int err; u8 *va; + /* A flush never modifies memory; read-only access suffices. */ err = rxe_odp_map_range_and_lock(mr, iova, length, - RXE_PAGEFAULT_DEFAULT); + RXE_PAGEFAULT_RDONLY); if (err) return err; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 96c7716057fe..3864284522eb 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -1331,19 +1331,20 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags, if (err) return ERR_PTR(err); + if ((flags & IB_MR_REREG_ACCESS) && + (access & ~RXE_ACCESS_SUPPORTED_MR)) { + rxe_err_mr(mr, "access = %#x not supported\n", access); + return ERR_PTR(-EOPNOTSUPP); + } + if (flags & IB_MR_REREG_PD) { rxe_put(old_pd); rxe_get(pd); mr->ibmr.pd = ibpd; } - if (flags & IB_MR_REREG_ACCESS) { - if (access & ~RXE_ACCESS_SUPPORTED_MR) { - rxe_err_mr(mr, "access = %#x not supported\n", access); - return ERR_PTR(-EOPNOTSUPP); - } + if (flags & IB_MR_REREG_ACCESS) mr->access = access; - } return NULL; } diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index 0245b25e7271..ed49818793dd 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -1719,9 +1719,12 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) SIW_QP_ATTR_STATE | SIW_QP_ATTR_LLP_HANDLE | SIW_QP_ATTR_ORD | SIW_QP_ATTR_IRD | SIW_QP_ATTR_MPA); + if (rv) { + qp->cep = NULL; + siw_cep_put(cep); + goto error_unlock; + } up_write(&qp->state_lock); - if (rv) - goto error; siw_dbg_cep(cep, "[QP %u]: send mpa reply, %d byte pdata\n", qp_id(qp), params->private_data_len); diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c index b566d163c5aa..e5b641c8d694 100644 --- a/drivers/infiniband/sw/siw/siw_qp_rx.c +++ b/drivers/infiniband/sw/siw/siw_qp_rx.c @@ -1079,7 +1079,7 @@ static int siw_get_hdr(struct siw_rx_stream *srx) if (iwarp_pktinfo[opcode].hdr_len > sizeof(struct iwarp_ctrl_tagged)) { int hdrlen = iwarp_pktinfo[opcode].hdr_len; - bytes = min_t(int, hdrlen - MIN_DDP_HDR, srx->skb_new); + bytes = min_t(int, hdrlen - srx->fpdu_part_rcvd, srx->skb_new); skb_copy_bits(skb, srx->skb_offset, (char *)c_hdr + srx->fpdu_part_rcvd, bytes); diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 91f866e3fb8b..143e03b64902 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -87,6 +87,7 @@ enum { IPOIB_FLAG_INITIALIZED = 1, IPOIB_FLAG_ADMIN_UP = 2, IPOIB_PKEY_ASSIGNED = 3, + IPOIB_FLAG_MCAST_FLUSH = 4, IPOIB_FLAG_SUBINTERFACE = 5, IPOIB_STOP_REAPER = 7, IPOIB_FLAG_ADMIN_CM = 9, @@ -414,6 +415,12 @@ struct ipoib_dev_priv { const struct net_device_ops *rn_ops; }; +static inline bool ipoib_mcast_allowed(struct ipoib_dev_priv *priv) +{ + return test_bit(IPOIB_FLAG_OPER_UP, &priv->flags) && + !test_bit(IPOIB_FLAG_MCAST_FLUSH, &priv->flags); +} + struct ipoib_ah { struct net_device *dev; struct ib_ah *ah; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 5061d52a7b12..81bbb3f7c113 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -1227,17 +1227,19 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv, } if (level == IPOIB_FLUSH_LIGHT) { - int oper_up; ipoib_mark_paths_invalid(dev); - /* Set IPoIB operation as down to prevent races between: + /* Set MCAST_FLUSH to prevent races between: * the flush flow which leaves MCG and on the fly joins * which can happen during that time. mcast restart task * should deal with join requests we missed. + * + * Do not clear OPER_UP for this; restoring it races with + * ipoib_ib_dev_down() and can leave OPER_UP set after the + * device is down. */ - oper_up = test_and_clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags); + set_bit(IPOIB_FLAG_MCAST_FLUSH, &priv->flags); ipoib_mcast_dev_flush(dev); - if (oper_up) - set_bit(IPOIB_FLAG_OPER_UP, &priv->flags); + clear_bit(IPOIB_FLAG_MCAST_FLUSH, &priv->flags); ipoib_reap_dead_ahs(priv); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 6401af2fd548..379b78374e21 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -74,7 +74,7 @@ static void __ipoib_mcast_schedule_join_thread(struct ipoib_dev_priv *priv, struct ipoib_mcast *mcast, bool delay) { - if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + if (!ipoib_mcast_allowed(priv)) return; /* @@ -469,7 +469,7 @@ static int ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast) int ret = 0; if (!priv->broadcast || - !test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + !ipoib_mcast_allowed(priv)) return -EINVAL; init_completion(&mcast->done); @@ -555,7 +555,7 @@ void ipoib_mcast_join_task(struct work_struct *work) unsigned long delay_until = 0; struct ipoib_mcast *mcast = NULL; - if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + if (!ipoib_mcast_allowed(priv)) return; if (ib_query_port(priv->ca, priv->port, &port_attr)) { @@ -577,7 +577,7 @@ void ipoib_mcast_join_task(struct work_struct *work) netif_addr_unlock_bh(dev); spin_lock_irq(&priv->lock); - if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + if (!ipoib_mcast_allowed(priv)) goto out; if (!priv->broadcast) { @@ -749,7 +749,7 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) spin_lock_irqsave(&priv->lock, flags); - if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags) || + if (!ipoib_mcast_allowed(priv) || !priv->broadcast || !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) { ++dev->stats.tx_dropped; @@ -871,7 +871,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) LIST_HEAD(remove_list); struct ib_sa_mcmember_rec rec; - if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) + if (!ipoib_mcast_allowed(priv)) /* * shortcut...on shutdown flush is called next, just * let it do all the work @@ -965,9 +965,9 @@ void ipoib_mcast_restart_task(struct work_struct *work) ipoib_mcast_remove_list(&remove_list); /* - * Double check that we are still up + * Double check that we are still up and not flushing */ - if (test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) { + if (ipoib_mcast_allowed(priv)) { spin_lock_irq(&priv->lock); __ipoib_mcast_schedule_join_thread(priv, NULL, 0); spin_unlock_irq(&priv->lock); diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 12a2d12fef07..7ea6888b479c 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -598,11 +598,8 @@ static int iser_check_remote_inv(struct iser_conn *iser_conn, struct ib_wc *wc, iser_dbg("conn %p: remote invalidation for rkey %#x\n", iser_conn, rkey); - if (unlikely(!iser_conn->snd_w_inv)) { - iser_err("conn %p: unexpected remote invalidation, terminating connection\n", - iser_conn); - return -EPROTO; - } + if (unlikely(!iser_conn->snd_w_inv)) + goto bad_inv; task = iscsi_itt_to_ctask(iser_conn->iscsi_conn, hdr->itt); if (likely(task)) { @@ -611,12 +608,16 @@ static int iser_check_remote_inv(struct iser_conn *iser_conn, struct ib_wc *wc, if (iser_task->dir[ISER_DIR_IN]) { desc = iser_task->rdma_reg[ISER_DIR_IN].desc; + if (unlikely(!desc)) + goto bad_inv; if (unlikely(iser_inv_desc(desc, rkey))) return -EINVAL; } if (iser_task->dir[ISER_DIR_OUT]) { desc = iser_task->rdma_reg[ISER_DIR_OUT].desc; + if (unlikely(!desc)) + goto bad_inv; if (unlikely(iser_inv_desc(desc, rkey))) return -EINVAL; } @@ -627,6 +628,11 @@ static int iser_check_remote_inv(struct iser_conn *iser_conn, struct ib_wc *wc, } return 0; + +bad_inv: + iser_err("conn %p: unexpected remote invalidation, terminating connection\n", + iser_conn); + return -EPROTO; } diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 5087ea983071..e69db43370ff 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -21,6 +21,7 @@ #include <target/target_core_fabric.h> #include <target/iscsi/iscsi_transport.h> #include <linux/semaphore.h> +#include <linux/wait_bit.h> #include "ib_isert.h" @@ -310,6 +311,7 @@ isert_init_conn(struct isert_conn *isert_conn) init_completion(&isert_conn->login_req_comp); init_waitqueue_head(&isert_conn->rem_wait); kref_init(&isert_conn->kref); + atomic_set(&isert_conn->ctrl_comp_cnt, 0); mutex_init(&isert_conn->mutex); INIT_WORK(&isert_conn->release_work, isert_release_work); } @@ -1694,6 +1696,8 @@ isert_do_control_comp(struct work_struct *work) struct isert_conn *isert_conn = isert_cmd->conn; struct ib_device *ib_dev = isert_conn->cm_id->device; struct iscsit_cmd *cmd = isert_cmd->iscsit_cmd; + /* The switch below may free isert_cmd. */ + bool counted = isert_cmd->ctrl_counted; isert_dbg("Cmd %p i_state %d\n", isert_cmd, cmd->i_state); @@ -1715,6 +1719,14 @@ isert_do_control_comp(struct work_struct *work) dump_stack(); break; } + + /* + * The count is what keeps isert_conn alive, so drop it last. The wait + * queue lives in the global hash table, not in isert_conn, so this is + * safe even if the waiter has already freed the connection. + */ + if (counted && atomic_dec_and_test(&isert_conn->ctrl_comp_cnt)) + wake_up_var(&isert_conn->ctrl_comp_cnt); } static void @@ -1758,6 +1770,12 @@ isert_send_done(struct ib_cq *cq, struct ib_wc *wc) case ISTATE_SEND_TEXTRSP: isert_unmap_tx_desc(tx_desc, ib_dev); + /* Paired with the wait in isert_wait_conn(). */ + isert_cmd->ctrl_counted = + isert_cmd->iscsit_cmd->i_state != ISTATE_SEND_LOGOUTRSP; + if (isert_cmd->ctrl_counted) + atomic_inc(&isert_conn->ctrl_comp_cnt); + INIT_WORK(&isert_cmd->comp_work, isert_do_control_comp); queue_work(isert_comp_wq, &isert_cmd->comp_work); return; @@ -2602,6 +2620,10 @@ static void isert_wait_conn(struct iscsit_conn *conn) isert_wait4cmds(conn); isert_wait4logout(isert_conn); + /* Paired with the count taken in isert_send_done(). */ + wait_var_event(&isert_conn->ctrl_comp_cnt, + !atomic_read(&isert_conn->ctrl_comp_cnt)); + queue_work(isert_release_wq, &isert_conn->release_work); } diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 0bac5aa66c80..519b17e54bd3 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -153,6 +153,7 @@ struct isert_cmd { struct work_struct comp_work; struct scatterlist sg; bool ctx_init_done; + bool ctrl_counted; }; static inline struct isert_cmd *tx_desc_to_cmd(struct iser_tx_desc *desc) @@ -187,6 +188,7 @@ struct isert_conn { struct mutex mutex; struct kref kref; struct work_struct release_work; + atomic_t ctrl_comp_cnt; bool logout_posted; bool snd_w_inv; wait_queue_head_t rem_wait; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt-trace.h b/drivers/infiniband/ulp/rtrs/rtrs-clt-trace.h index 7738e2676855..29e23404bb7b 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt-trace.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt-trace.h @@ -55,7 +55,7 @@ DECLARE_EVENT_CLASS(rtrs_clt_conn_class, __entry->max_reconnect_attempts = clt->max_reconnect_attempts; __entry->fail_cnt = clt_path->stats->reconnects.fail_cnt; __entry->success_cnt = clt_path->stats->reconnects.successful_cnt; - memcpy(__entry->sessname, kobject_name(&clt_path->kobj), NAME_MAX); + strscpy(__entry->sessname, kobject_name(&clt_path->kobj) ?: "", NAME_MAX); ), TP_printk("RTRS-CLT: sess='%s' state=%s attempts='%d' max-attempts='%d' fail='%d' success='%d'", diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 7b2c51ae614f..eac38b57b00d 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -1732,6 +1732,8 @@ static void destroy_con_cq_qp(struct rtrs_clt_con *con) /* * Be careful here: destroy_con_cq_qp() can be called even * create_con_cq_qp() failed, see comments there. + * Caller must set con->destroyed under this lock first so a + * racing ADDR_RESOLVED cannot ib_cq_pool_get() after we PUT/SKIP. */ lockdep_assert_held(&con->con_mutex); rtrs_cq_qp_destroy(&con->c); @@ -1766,6 +1768,10 @@ static int rtrs_rdma_addr_resolved(struct rtrs_clt_con *con) int err; mutex_lock(&con->con_mutex); + if (con->destroyed) { + mutex_unlock(&con->con_mutex); + return -ECONNABORTED; + } err = create_con_cq_qp(con); mutex_unlock(&con->con_mutex); if (err) { @@ -2221,6 +2227,7 @@ static void rtrs_clt_stop_and_destroy_conns(struct rtrs_clt_path *clt_path) break; con = to_clt_con(clt_path->s.con[cid]); mutex_lock(&con->con_mutex); + con->destroyed = true; destroy_con_cq_qp(con); mutex_unlock(&con->con_mutex); destroy_cm(con); @@ -2387,6 +2394,7 @@ destroy: if (con->c.cm_id) { stop_cm(con); mutex_lock(&con->con_mutex); + con->destroyed = true; destroy_con_cq_qp(con); mutex_unlock(&con->con_mutex); destroy_cm(con); diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.h b/drivers/infiniband/ulp/rtrs/rtrs-clt.h index 1305601a6251..ad64f4517c4b 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.h @@ -75,6 +75,8 @@ struct rtrs_clt_con { unsigned int cpu; struct mutex con_mutex; int cm_err; + /* Set under con_mutex before CQ/QP teardown. */ + bool destroyed; }; /** diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-trace.h b/drivers/infiniband/ulp/rtrs/rtrs-srv-trace.h index 587d3e033081..a7d7b971e6c8 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-trace.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-trace.h @@ -61,7 +61,7 @@ TRACE_EVENT(send_io_resp_imm, __entry->msg_id = id->msg_id; __entry->wr_cnt = atomic_read(&con->c.wr_cnt); __entry->signal_interval = s->signal_interval; - memcpy(__entry->sessname, kobject_name(&srv_path->kobj), NAME_MAX); + strscpy(__entry->sessname, kobject_name(&srv_path->kobj) ?: "", NAME_MAX); ), TP_printk("sess='%s' state='%s' dir=%s err='%d' inval='%d' glob-inval='%d' msgid='%u' wrcnt='%d' sig-interval='%u'", diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 6b429ef63f8f..955f36efeebd 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1038,15 +1038,20 @@ static void srp_del_scsi_host_attr(struct Scsi_Host *shost) static void srp_remove_target(struct srp_target_port *target) { + struct scsi_device *sdev; struct srp_rdma_ch *ch; int i; WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED); srp_del_scsi_host_attr(target->scsi_host); - srp_rport_get(target->rport); - srp_remove_host(target->scsi_host); - scsi_remove_host(target->scsi_host); + /* + * Remove all logical units. This must happen before the + * srp_disconnect_target() call because scsi_remove_device() may trigger + * submission of SCSI commands. See also sd_shutdown(). + */ + shost_for_each_device(sdev, target->scsi_host) + scsi_remove_device(sdev); srp_stop_rport_timers(target->rport); srp_disconnect_target(target); kobj_ns_drop(KOBJ_NS_TYPE_NET, to_ns_common(target->net)); @@ -1055,7 +1060,8 @@ static void srp_remove_target(struct srp_target_port *target) srp_free_ch_ib(target, ch); } cancel_work_sync(&target->tl_err_work); - srp_rport_put(target->rport); + srp_remove_host(target->scsi_host); + scsi_remove_host(target->scsi_host); kfree(target->ch); target->ch = NULL; diff --git a/drivers/input/input.c b/drivers/input/input.c index 78c10eea7328..01c91fec9b7e 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -2344,7 +2344,7 @@ static int input_device_tune_vals(struct input_dev *dev) if (dev->max_vals >= max_vals) return 0; - vals = kcalloc(max_vals, sizeof(*vals), GFP_KERNEL); + vals = kzalloc_objs(*vals, max_vals); if (!vals) return -ENOMEM; diff --git a/drivers/input/keyboard/adp5585-keys.c b/drivers/input/keyboard/adp5585-keys.c index 017c95029180..f2c1ba017d20 100644 --- a/drivers/input/keyboard/adp5585-keys.c +++ b/drivers/input/keyboard/adp5585-keys.c @@ -115,8 +115,8 @@ static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585, "Too many keypad pins (%d) defined (max=%d)\n", n_pins, adp5585->n_pins); - unsigned int *keypad_pins __free(kfree) = kcalloc(n_pins, sizeof(*keypad_pins), - GFP_KERNEL); + unsigned int *keypad_pins __free(kfree) = kzalloc_objs(*keypad_pins, + n_pins); if (!keypad_pins) return -ENOMEM; diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 5736f4bc5a50..b9ad2381f885 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1070,7 +1070,7 @@ static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) if (n <= 0 || n > ATKBD_KEYMAP_SIZE) return -ENXIO; - u32 *ptr __free(kfree) = kcalloc(n, sizeof(*ptr), GFP_KERNEL); + u32 *ptr __free(kfree) = kzalloc_objs(*ptr, n); if (!ptr) return -ENOMEM; diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index b1a0edcc49b4..c3244c1a3751 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1844,7 +1844,7 @@ 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 __free(kfree) = - kmalloc(sizeof(*line), GFP_KERNEL); + kmalloc_obj(*line); int error; if (!line) diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c index 7fb4cbb2aca2..98d9c860d2db 100644 --- a/drivers/input/mouse/psmouse-smbus.c +++ b/drivers/input/mouse/psmouse-smbus.c @@ -232,7 +232,7 @@ int psmouse_smbus_init(struct psmouse *psmouse, struct psmouse_smbus_dev *smbdev; int error; - smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL); + smbdev = kzalloc_obj(*smbdev); if (!smbdev) return -ENOMEM; diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index a7ccedfa459c..868dee9a2ed8 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -84,7 +84,7 @@ static int serio_raw_open(struct inode *inode, struct file *file) if (serio_raw->dead) return -ENODEV; - client = kzalloc(sizeof(*client), GFP_KERNEL); + client = kzalloc_obj(*client); if (!client) return -ENOMEM; diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index a2fe804b038b..71113e860859 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -54,9 +54,6 @@ struct protection_domain *protection_domain_alloc(void); struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev, struct mm_struct *mm); void amd_iommu_domain_free(struct iommu_domain *dom); -int iommu_sva_set_dev_pasid(struct iommu_domain *domain, - struct device *dev, ioasid_t pasid, - struct iommu_domain *old); void amd_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid, struct iommu_domain *domain); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 3dbe20023456..8241ef922519 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -39,18 +39,6 @@ #define MMIO_RANGE_OFFSET 0x0c #define MMIO_MISC_OFFSET 0x10 -/* Masks, shifts and macros to parse the device range capability */ -#define MMIO_RANGE_LD_MASK 0xff000000 -#define MMIO_RANGE_FD_MASK 0x00ff0000 -#define MMIO_RANGE_BUS_MASK 0x0000ff00 -#define MMIO_RANGE_LD_SHIFT 24 -#define MMIO_RANGE_FD_SHIFT 16 -#define MMIO_RANGE_BUS_SHIFT 8 -#define MMIO_GET_LD(x) (((x) & MMIO_RANGE_LD_MASK) >> MMIO_RANGE_LD_SHIFT) -#define MMIO_GET_FD(x) (((x) & MMIO_RANGE_FD_MASK) >> MMIO_RANGE_FD_SHIFT) -#define MMIO_GET_BUS(x) (((x) & MMIO_RANGE_BUS_MASK) >> MMIO_RANGE_BUS_SHIFT) -#define MMIO_MSI_NUM(x) ((x) & 0x1f) - /* Used offsets into the MMIO space */ #define MMIO_DEV_TABLE_OFFSET 0x0000 #define MMIO_CMD_BUF_OFFSET 0x0008 @@ -247,7 +235,6 @@ /* constants to configure the command buffer */ #define CMD_BUFFER_SIZE 8192 -#define CMD_BUFFER_UNINITIALIZED 1 #define CMD_BUFFER_ENTRIES 512 #define MMIO_CMD_SIZE_SHIFT 56 #define MMIO_CMD_SIZE_512 (0x9ULL << MMIO_CMD_SIZE_SHIFT) @@ -434,9 +421,6 @@ struct irq_remap_table { u32 *table; }; -/* Interrupt remapping feature used? */ -extern bool amd_iommu_irq_remap; - extern const struct iommu_ops amd_iommu_ops; /* IVRS indicates that pre-boot remapping was enabled */ diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 40726dfef273..8a410d4aa370 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -152,7 +152,7 @@ struct ivmd_header { } __attribute__((packed)); bool amd_iommu_dump; -bool amd_iommu_irq_remap __read_mostly; +static bool amd_iommu_irq_remap __read_mostly; enum protection_domain_mode amd_iommu_pgtable = PD_MODE_V1; /* Virtual address size */ @@ -909,7 +909,9 @@ static void free_ga_log(struct amd_iommu *iommu) { #ifdef CONFIG_IRQ_REMAP iommu_free_pages(iommu->ga_log); + iommu->ga_log = NULL; iommu_free_pages(iommu->ga_log_tail); + iommu->ga_log_tail = NULL; #endif } @@ -956,6 +958,9 @@ static int iommu_init_ga_log(struct amd_iommu *iommu) if (WARN_ON_ONCE(!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir))) return -EINVAL; + if (iommu->ga_log && iommu->ga_log_tail) + return 0; + iommu->ga_log = iommu_alloc_pages_node_sz(nid, GFP_KERNEL, GA_LOG_SIZE); if (!iommu->ga_log) goto err_out; @@ -1917,19 +1922,20 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h, else iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET; + if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) { + pr_warn_once("Host Address Translation is not supported.\n"); + amd_iommu_hatdis = true; + } + /* XT and GAM require GA mode. */ if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) { amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY; + break; } else { if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT)) amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE; } - if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) { - pr_warn_once("Host Address Translation is not supported.\n"); - amd_iommu_hatdis = true; - } - early_iommu_features_init(iommu, h); break; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 4dc306a4b5c6..56262f6b1f70 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -1076,9 +1076,6 @@ static void iommu_poll_ga_log(struct amd_iommu *iommu) { u32 head, tail; - if (iommu->ga_log == NULL) - return; - head = readl(iommu->mmio_base + MMIO_GA_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_GA_TAIL_OFFSET); @@ -3976,9 +3973,6 @@ static int irq_remapping_select(struct irq_domain *d, struct irq_fwspec *fwspec, struct amd_iommu *iommu; int devid = -1; - if (!amd_iommu_irq_remap) - return 0; - if (x86_fwspec_is_ioapic(fwspec)) devid = get_ioapic_devid(fwspec->param[0]); else if (x86_fwspec_is_hpet(fwspec)) diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c index 63b53b29e029..f1c7987fc585 100644 --- a/drivers/iommu/amd/nested.c +++ b/drivers/iommu/amd/nested.c @@ -96,7 +96,7 @@ struct iommu_domain * amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, const struct iommu_user_data *user_data) { - int ret; + int ret, hdom_id; unsigned long irqflags; struct nested_domain *ndom; struct guest_domain_mapping_info *gdom_info; @@ -161,8 +161,8 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, } /* The gDomID does not exist. We allocate new hdom_id */ - gdom_info->hdom_id = amd_iommu_pdom_id_alloc(); - if (gdom_info->hdom_id <= 0) { + hdom_id = amd_iommu_pdom_id_alloc(); + if (hdom_id <= 0) { __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC); xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags); @@ -170,6 +170,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags, goto out_err_gdom_info; } + gdom_info->hdom_id = hdom_id; ndom->gdom_info = gdom_info; refcount_set(&gdom_info->users, 1); diff --git a/drivers/iommu/amd/pasid.c b/drivers/iommu/amd/pasid.c index d708c6532480..40be5902087c 100644 --- a/drivers/iommu/amd/pasid.c +++ b/drivers/iommu/amd/pasid.c @@ -99,9 +99,9 @@ static const struct mmu_notifier_ops sva_mn = { .release = sva_mn_release, }; -int iommu_sva_set_dev_pasid(struct iommu_domain *domain, - struct device *dev, ioasid_t pasid, - struct iommu_domain *old) +static int iommu_sva_set_dev_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid, + struct iommu_domain *old) { struct pdom_dev_data *pdom_dev_data; struct protection_domain *sva_pdom = to_pdomain(domain); diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 76296079bb8b..2039a9dd71ac 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -165,9 +165,6 @@ void amd_iommu_poll_ppr_log(struct amd_iommu *iommu) { u32 head, tail; - if (iommu->ppr_log == NULL) - return; - head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET); tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 50f8321e979c..dd2fee2f560e 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -796,7 +796,7 @@ static inline struct arm_smmu_invs *arm_smmu_invs_alloc(size_t num_invs) { struct arm_smmu_invs *new_invs; - new_invs = kzalloc(struct_size(new_invs, inv, num_invs), GFP_KERNEL); + new_invs = kzalloc_flex(*new_invs, inv, num_invs); if (!new_invs) return NULL; new_invs->max_invs = num_invs; diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 5c4b06eda546..a664c70a6fe7 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -76,7 +76,7 @@ static struct iommufd_group *iommufd_alloc_group(struct iommufd_ctx *ictx, { struct iommufd_group *new_igroup; - new_igroup = kzalloc_obj(*new_igroup, GFP_KERNEL); + new_igroup = kzalloc_obj(*new_igroup); if (!new_igroup) return ERR_PTR(-ENOMEM); diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c index 3b8067976eac..e4d17a748178 100644 --- a/drivers/iommu/iommufd/driver.c +++ b/drivers/iommu/iommufd/driver.c @@ -49,7 +49,7 @@ int _iommufd_alloc_mmap(struct iommufd_ctx *ictx, struct iommufd_object *owner, if (!length || !PAGE_ALIGNED(length)) return -EINVAL; - immap = kzalloc(sizeof(*immap), GFP_KERNEL); + immap = kzalloc_obj(*immap); if (!immap) return -ENOMEM; immap->owner = owner; diff --git a/drivers/iommu/iommufd/hwpt_noiommu.c b/drivers/iommu/iommufd/hwpt_noiommu.c index 9b8b5eb71491..a80209a7429a 100644 --- a/drivers/iommu/iommufd/hwpt_noiommu.c +++ b/drivers/iommu/iommufd/hwpt_noiommu.c @@ -54,7 +54,7 @@ noiommu_alloc_paging_flags(struct device *dev, u32 flags, (BIT(PT_FEAT_DYNAMIC_TOP) | BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | BIT(PT_FEAT_AMDV1_FORCE_COHERENCE)); - dom = kzalloc(sizeof(*dom), GFP_KERNEL); + dom = kzalloc_obj(*dom); if (!dom) return ERR_PTR(-ENOMEM); diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c index cec3ddd7ab10..fe8e6d0f8a23 100644 --- a/drivers/iommu/riscv/iommu.c +++ b/drivers/iommu/riscv/iommu.c @@ -382,78 +382,97 @@ static int riscv_iommu_queue_wait(struct riscv_iommu_queue *queue, (int)(cons - index) > 0, 0, timeout_us); } -/* Enqueue an entry and wait to be processed if timeout_us > 0 - * - * Error handling for IOMMU hardware not responding in reasonable time - * will be added as separate patch series along with other RAS features. - * For now, only report hardware failure and continue. - */ -static unsigned int riscv_iommu_queue_send(struct riscv_iommu_queue *queue, - void *entry, size_t entry_size) +static int riscv_iommu_queue_wait_for_space(struct riscv_iommu_queue *queue, + unsigned int last) { - unsigned int prod; unsigned int head; unsigned int tail; + unsigned int hw_head; unsigned long flags; + int ret; - /* Do not preempt submission flow. */ - local_irq_save(flags); + ret = riscv_iommu_readl_timeout(queue->iommu, Q_HEAD(queue), hw_head, + !(hw_head & ~queue->mask) && hw_head != last, + 0, RISCV_IOMMU_QUEUE_TIMEOUT); + if (ret) + return ret; - /* 1. Allocate some space in the queue */ - prod = atomic_inc_return(&queue->prod) - 1; + raw_spin_lock_irqsave(&queue->lock, flags); head = atomic_read(&queue->head); + tail = atomic_read(&queue->tail); + if ((tail - head) >= queue->mask) { + last = Q_ITEM(queue, head); + /* + * Re-read hw_head under the lock so that it is consistent with + * the freshly computed 'last'. Using the pre-lock snapshot + * could produce a stale value that wraps around relative to the + * new 'last', advancing the shadow head past entries that have + * not yet been consumed by the hardware. + */ + hw_head = riscv_iommu_readl(queue->iommu, Q_HEAD(queue)); + if (!(hw_head & ~queue->mask) && hw_head != last) + atomic_add((hw_head - last) & queue->mask, &queue->head); + } + raw_spin_unlock_irqrestore(&queue->lock, flags); - /* 2. Wait for space availability. */ - if ((prod - head) > queue->mask) { - if (readx_poll_timeout(atomic_read, &queue->head, - head, (prod - head) < queue->mask, - 0, RISCV_IOMMU_QUEUE_TIMEOUT)) - goto err_busy; - } else if ((prod - head) == queue->mask) { - const unsigned int last = Q_ITEM(queue, head); + return 0; +} + +/* Enqueue an entry and publish it to the hardware queue. */ +static int riscv_iommu_queue_send(struct riscv_iommu_queue *queue, + void *entry, size_t entry_size, + unsigned int *out_prod) +{ + unsigned int prod; + unsigned int head; + unsigned long flags; + int ret; + + /* 1. Wait for space availability and reserve the next slot. */ + for (;;) { + raw_spin_lock_irqsave(&queue->lock, flags); + + prod = atomic_read(&queue->tail); + head = atomic_read(&queue->head); + + if ((prod - head) < queue->mask) + break; + + head = Q_ITEM(queue, head); + raw_spin_unlock_irqrestore(&queue->lock, flags); - if (riscv_iommu_readl_timeout(queue->iommu, Q_HEAD(queue), head, - !(head & ~queue->mask) && head != last, - 0, RISCV_IOMMU_QUEUE_TIMEOUT)) + ret = riscv_iommu_queue_wait_for_space(queue, head); + if (ret) goto err_busy; - atomic_add((head - last) & queue->mask, &queue->head); } - /* 3. Store entry in the ring buffer */ + /* 2. Store entry in the ring buffer. */ memcpy(queue->base + Q_ITEM(queue, prod) * entry_size, entry, entry_size); - /* 4. Wait for all previous entries to be ready */ - if (readx_poll_timeout(atomic_read, &queue->tail, tail, prod == tail, - 0, RISCV_IOMMU_QUEUE_TIMEOUT)) - goto err_busy; - - /* - * 5. Make sure the ring buffer update (whether in normal or I/O memory) is - * completed and visible before signaling the tail doorbell to fetch - * the next command. 'fence ow, ow' - */ + /* 3. Make sure the entry is visible before updating the queue tail. */ dma_wmb(); riscv_iommu_writel(queue->iommu, Q_TAIL(queue), Q_ITEM(queue, prod + 1)); /* - * 6. Make sure the doorbell write to the device has finished before updating - * the shadow tail index in normal memory. 'fence o, w' + * 4. Make sure the doorbell write to the device has finished before + * updating the shadow tail index in normal memory. 'fence o, w' */ #ifdef CONFIG_MMIOWB mmiowb(); #endif - atomic_inc(&queue->tail); + atomic_set(&queue->tail, prod + 1); + atomic_set(&queue->prod, prod + 1); - /* 7. Complete submission and restore local interrupts */ - local_irq_restore(flags); + if (out_prod) + *out_prod = prod; - return prod; + raw_spin_unlock_irqrestore(&queue->lock, flags); + return 0; err_busy: - local_irq_restore(flags); + /* Report the failure and continue; full RAS recovery is not implemented. */ dev_err_once(queue->iommu->dev, "Hardware error: command enqueue failed\n"); - - return prod; + return ret; } /* @@ -492,7 +511,7 @@ static irqreturn_t riscv_iommu_cmdq_process(int irq, void *data) static void riscv_iommu_cmd_send(struct riscv_iommu_device *iommu, struct riscv_iommu_command *cmd) { - riscv_iommu_queue_send(&iommu->cmdq, cmd, sizeof(*cmd)); + riscv_iommu_queue_send(&iommu->cmdq, cmd, sizeof(*cmd), NULL); } /* Send IOFENCE.C command and wait for all scheduled commands to complete. */ @@ -501,9 +520,12 @@ static void riscv_iommu_cmd_sync(struct riscv_iommu_device *iommu, { struct riscv_iommu_command cmd; unsigned int prod; + int ret; riscv_iommu_cmd_iofence(&cmd); - prod = riscv_iommu_queue_send(&iommu->cmdq, &cmd, sizeof(cmd)); + ret = riscv_iommu_queue_send(&iommu->cmdq, &cmd, sizeof(cmd), &prod); + if (ret) + return; if (!timeout_us) return; @@ -1560,6 +1582,7 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu) int rc; RISCV_IOMMU_QUEUE_INIT(&iommu->cmdq, CQ); + raw_spin_lock_init(&iommu->cmdq.lock); RISCV_IOMMU_QUEUE_INIT(&iommu->fltq, FQ); rc = riscv_iommu_init_check(iommu); diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h index 46df79dd5495..5676001548cc 100644 --- a/drivers/iommu/riscv/iommu.h +++ b/drivers/iommu/riscv/iommu.h @@ -12,6 +12,7 @@ #define _RISCV_IOMMU_H_ #include <linux/iommu.h> +#include <linux/spinlock.h> #include <linux/types.h> #include <linux/iopoll.h> @@ -23,6 +24,7 @@ struct riscv_iommu_queue { atomic_t prod; /* unbounded producer allocation index */ atomic_t head; /* unbounded shadow ring buffer consumer index */ atomic_t tail; /* unbounded shadow ring buffer producer index */ + raw_spinlock_t lock; /* serialize queue publishing */ unsigned int mask; /* index mask, queue length - 1 */ unsigned int irq; /* allocated interrupt number */ struct riscv_iommu_device *iommu; /* iommu device handling the queue when active */ diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c index f148f559ac56..58ca7727b7f2 100644 --- a/drivers/iommu/s390-iommu.c +++ b/drivers/iommu/s390-iommu.c @@ -974,6 +974,8 @@ static unsigned long *get_rto_from_iova(struct s390_domain *domain, case ZPCI_TABLE_TYPE_RFX: case ZPCI_TABLE_TYPE_RSX: rso = get_rso_from_iova(domain, iova); + if (!rso) + return NULL; rsx = calc_rsx(iova); rse = READ_ONCE(rso[rsx]); if (!reg_entry_isvalid(rse)) diff --git a/drivers/iommu/vsi-iommu.c b/drivers/iommu/vsi-iommu.c index 42c424496d07..5dac20b143a5 100644 --- a/drivers/iommu/vsi-iommu.c +++ b/drivers/iommu/vsi-iommu.c @@ -231,7 +231,7 @@ static struct iommu_domain *vsi_iommu_domain_alloc_paging(struct device *dev) struct vsi_iommu *iommu = dev_iommu_priv_get(dev); struct vsi_iommu_domain *vsi_domain; - vsi_domain = kzalloc(sizeof(*vsi_domain), GFP_KERNEL); + vsi_domain = kzalloc_obj(*vsi_domain); if (!vsi_domain) return NULL; diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c index b3feb6340b59..4486645a4b6f 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -618,7 +618,7 @@ static int __init gicv5_irs_of_init_affinity(struct device_node *node, if (niaffids != ncpus) return -EINVAL; - u16 *iaffids __free(kfree) = kcalloc(niaffids, sizeof(*iaffids), GFP_KERNEL); + u16 *iaffids __free(kfree) = kzalloc_objs(*iaffids, niaffids); if (!iaffids) return -ENOMEM; diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index ac2d423b1723..5f2551cf077d 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -974,7 +974,8 @@ static void gicv5_cpu_disable_interrupts(void) { u64 cr0; - cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 0); + cr0 = read_sysreg_s(SYS_ICC_CR0_EL1); + cr0 &= ~ICC_CR0_EL1_EN_MASK; write_sysreg_s(cr0, SYS_ICC_CR0_EL1); isb(); } @@ -991,7 +992,8 @@ static void gicv5_cpu_enable_interrupts(void) pcr = FIELD_PREP(ICC_PCR_EL1_PRIORITY, GICV5_IRQ_PRI_MI); write_sysreg_s(pcr, SYS_ICC_PCR_EL1); - cr0 = FIELD_PREP(ICC_CR0_EL1_EN, 1); + cr0 = read_sysreg_s(SYS_ICC_CR0_EL1); + cr0 |= ICC_CR0_EL1_EN_MASK; write_sysreg_s(cr0, SYS_ICC_CR0_EL1); } diff --git a/drivers/irqchip/irq-loongarch-ir.c b/drivers/irqchip/irq-loongarch-ir.c index 21c649a89a70..cb4ad34da058 100644 --- a/drivers/irqchip/irq-loongarch-ir.c +++ b/drivers/irqchip/irq-loongarch-ir.c @@ -331,7 +331,7 @@ static int redirect_domain_alloc(struct irq_domain *domain, unsigned int virq, struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq + i); struct redirect_item *item; - item = kzalloc(sizeof(*item), GFP_KERNEL); + item = kzalloc_obj(*item); if (!item) { pr_err("Alloc redirect descriptor failed\n"); goto out_free_resources; diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 6f69f4e5dbac..12919836dadb 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -64,20 +64,6 @@ struct mbigen_device { void __iomem *base; }; -static inline unsigned int get_mbigen_node_offset(unsigned int nid) -{ - unsigned int offset = nid * MBIGEN_NODE_OFFSET; - - /* - * To avoid touched clear register in unexpected way, we need to directly - * skip clear register when access to more than 10 mbigen nodes. - */ - if (nid >= (REG_MBIGEN_CLEAR_OFFSET / MBIGEN_NODE_OFFSET)) - offset += MBIGEN_NODE_OFFSET; - - return offset; -} - static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) { unsigned int nid, pin; @@ -86,7 +72,8 @@ static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; pin = hwirq % IRQS_PER_MBIGEN_NODE; - return pin * 4 + get_mbigen_node_offset(nid) + REG_MBIGEN_VEC_OFFSET; + return pin * 4 + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_VEC_OFFSET; } static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, @@ -101,7 +88,8 @@ static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, *mask = 1 << (irq_ofst % 32); ofst = irq_ofst / 32 * 4; - *addr = ofst + get_mbigen_node_offset(nid) + REG_MBIGEN_TYPE_OFFSET; + *addr = ofst + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_TYPE_OFFSET; } static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq, diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c index 26e52c3f8c68..f1ab5a77dcef 100644 --- a/drivers/irqchip/irq-realtek-rtl.c +++ b/drivers/irqchip/irq-realtek-rtl.c @@ -188,7 +188,7 @@ static int __init realtek_setup_parents(struct device_node *node) struct irq_domain *domain; cnt = max(1, num_parents); - output = kcalloc(cnt, sizeof(*output), GFP_KERNEL); + output = kzalloc_objs(*output, cnt); if (!output) return -ENOMEM; diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c index bf3a2def69ca..a19e91fbd010 100644 --- a/drivers/irqchip/irq-stm32mp-exti.c +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -22,7 +22,7 @@ #define IRQS_PER_BANK 32 -#define HWSPNLCK_TIMEOUT 1000 /* usec */ +#define HWSPNLCK_TIMEOUT_MS 1 #define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) #define EXTI_HWCFGR1 0x3f0 @@ -376,7 +376,7 @@ static int stm32mp_exti_set_type(struct irq_data *d, unsigned int type) raw_spin_lock(&chip_data->rlock); if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); + err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT_MS); if (err) { pr_err("%s can't get hwspinlock (%d)\n", __func__, err); goto unlock; diff --git a/drivers/mailbox/riscv-sbi-mpxy-mbox.c b/drivers/mailbox/riscv-sbi-mpxy-mbox.c index 714f7fb97a2f..ea69c6b6b4f9 100644 --- a/drivers/mailbox/riscv-sbi-mpxy-mbox.c +++ b/drivers/mailbox/riscv-sbi-mpxy-mbox.c @@ -783,7 +783,7 @@ static int mpxy_mbox_populate_channels(struct mpxy_mbox *mbox) return dev_err_probe(mbox->dev, -ENODEV, "no MPXY channels available\n"); /* Allocate and fetch all channel IDs */ - channel_ids = kcalloc(mbox->channel_count, sizeof(*channel_ids), GFP_KERNEL); + channel_ids = kzalloc_objs(*channel_ids, mbox->channel_count); if (!channel_ids) return -ENOMEM; rc = mpxy_get_channel_ids(mbox->channel_count, channel_ids); diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 33dbc71b730f..1a5072425c4a 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -340,8 +340,6 @@ struct cache { struct list_head invalidation_requests; sector_t migration_threshold; - wait_queue_head_t migration_wait; - atomic_t nr_allocated_migrations; /* * The number of in flight migrations that are performing @@ -397,7 +395,11 @@ struct cache { bool loaded_mappings:1; bool loaded_discards:1; - struct rw_semaphore background_work_lock; + /* background work management */ + bool background_work_allowed; + unsigned background_work_nr; + spinlock_t background_work_lock; + wait_queue_head_t background_work_wait; struct batcher committer; struct work_struct commit_ws; @@ -488,19 +490,13 @@ static struct dm_cache_migration *alloc_migration(struct cache *cache) memset(mg, 0, sizeof(*mg)); mg->cache = cache; - atomic_inc(&cache->nr_allocated_migrations); return mg; } static void free_migration(struct dm_cache_migration *mg) { - struct cache *cache = mg->cache; - - if (atomic_dec_and_test(&cache->nr_allocated_migrations)) - wake_up(&cache->migration_wait); - - mempool_free(mg, &cache->migration_pool); + mempool_free(mg, &mg->cache->migration_pool); } /*----------------------------------------------------------------*/ @@ -1030,34 +1026,39 @@ static void calc_discard_block_range(struct cache *cache, struct bio *bio, static void prevent_background_work(struct cache *cache) { - lockdep_off(); - down_write(&cache->background_work_lock); - lockdep_on(); + spin_lock_irq(&cache->background_work_lock); + cache->background_work_allowed = false; + wait_event_lock_irq(cache->background_work_wait, + cache->background_work_nr == 0, + cache->background_work_lock); + spin_unlock_irq(&cache->background_work_lock); } static void allow_background_work(struct cache *cache) { - lockdep_off(); - up_write(&cache->background_work_lock); - lockdep_on(); + spin_lock_irq(&cache->background_work_lock); + cache->background_work_allowed = true; + spin_unlock_irq(&cache->background_work_lock); } static bool background_work_begin(struct cache *cache) { bool r; - lockdep_off(); - r = down_read_trylock(&cache->background_work_lock); - lockdep_on(); - + spin_lock_irq(&cache->background_work_lock); + r = cache->background_work_allowed; + if (r) + cache->background_work_nr++; + spin_unlock_irq(&cache->background_work_lock); return r; } static void background_work_end(struct cache *cache) { - lockdep_off(); - up_read(&cache->background_work_lock); - lockdep_on(); + spin_lock_irq(&cache->background_work_lock); + if (--cache->background_work_nr == 0) + wake_up(&cache->background_work_wait); + spin_unlock_irq(&cache->background_work_lock); } /*----------------------------------------------------------------*/ @@ -1462,6 +1463,9 @@ static void invalidate_complete(struct dm_cache_migration *mg, bool success) struct bio_list bios; struct cache *cache = mg->cache; + if (success) + atomic_inc(&cache->stats.demotion); + bio_list_init(&bios); if (mg->cell) { if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios)) @@ -1733,7 +1737,6 @@ static int map_bio(struct cache *cache, struct bio *bio, dm_oblock_t block, if (passthrough_mode(cache)) { if (bio_data_dir(bio) == WRITE) { bio_drop_shared_lock(cache, bio); - atomic_inc(&cache->stats.demotion); invalidate_start(cache, cblock, block, bio); return DM_MAPIO_SUBMITTED; } else @@ -2507,9 +2510,7 @@ static int cache_create(struct cache_args *ca, struct cache **result) spin_lock_init(&cache->lock); bio_list_init(&cache->deferred_bios); - atomic_set(&cache->nr_allocated_migrations, 0); atomic_set(&cache->nr_io_migrations, 0); - init_waitqueue_head(&cache->migration_wait); r = -ENOMEM; atomic_set(&cache->nr_dirty, 0); @@ -2592,8 +2593,10 @@ static int cache_create(struct cache_args *ca, struct cache **result) issue_op, cache, cache->wq); dm_iot_init(&cache->tracker); - init_rwsem(&cache->background_work_lock); - prevent_background_work(cache); + init_waitqueue_head(&cache->background_work_wait); + spin_lock_init(&cache->background_work_lock); + cache->background_work_allowed = false; + cache->background_work_nr = 0; *result = cache; return 0; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 608b617fb817..9e170de50ad3 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1745,7 +1745,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io) { struct crypt_config *cc = io->cc; struct bio *base_bio = io->base_bio; - blk_status_t error = io->error; if (!atomic_dec_and_test(&io->io_pending)) return; @@ -1767,7 +1766,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io) else kfree(io->integrity_metadata); - base_bio->bi_status = error; + base_bio->bi_status = io->error; bio_endio(base_bio); } diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c index 1e52bde48b91..5d67c6c19d4b 100644 --- a/drivers/md/dm-ebs-target.c +++ b/drivers/md/dm-ebs-target.c @@ -265,8 +265,7 @@ static int ebs_ctr(struct dm_target *ti, unsigned int argc, char **argv) r = -EINVAL; if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 || - tmp != (sector_t)tmp || - (sector_t)tmp >= ti->len) { + tmp != (sector_t)tmp) { ti->error = "Invalid device offset sector"; goto bad; } diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c index 66fa2f9d1fdc..3479dfe5f708 100644 --- a/drivers/md/dm-inlinecrypt.c +++ b/drivers/md/dm-inlinecrypt.c @@ -322,7 +322,7 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) return -EINVAL; } - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = kzalloc_obj(*ctx); if (!ctx) { ti->error = "Out of memory"; return -ENOMEM; diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index c50feaa98bf9..92970e12267a 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1979,8 +1979,8 @@ static void integrity_metadata(struct work_struct *w) if (unlikely(dio->op == REQ_OP_DISCARD)) { unsigned int bi_size = dio->bio_details.bi_iter.bi_size; - unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : HASH_MAX_DIGESTSIZE; - unsigned int max_blocks = max_size / ic->tag_size; + unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : sizeof(checksums_onstack); + unsigned int max_blocks = (max_size - extra_space) / ic->tag_size; sector_t sector = dio->range.logical_sector; if (!ic->discard_keyed) @@ -3875,6 +3875,10 @@ static void dm_integrity_resume(struct dm_target *ti) r = sync_rw_sb(ic, REQ_OP_READ); if (r) dm_integrity_io_error(ic, "reading superblock", r); + + if (ic->mode == 'R') + goto skip_writes; + if ((ic->sb->flags & flags) != flags) { ic->sb->flags |= flags; r = sync_rw_sb(ic, REQ_OP_WRITE | REQ_FUA); @@ -3984,6 +3988,7 @@ static void dm_integrity_resume(struct dm_target *ti) } } +skip_writes: ic->reboot_notifier.notifier_call = dm_integrity_reboot; ic->reboot_notifier.next = NULL; ic->reboot_notifier.priority = INT_MAX - 1; /* be notified after md and before hardware drivers */ @@ -4130,6 +4135,10 @@ static void dm_integrity_io_hints(struct dm_target *ti, struct queue_limits *lim limits->dma_alignment = limits->logical_block_size - 1; limits->discard_granularity = ic->sectors_per_block << SECTOR_SHIFT; + if (ic->internal_hash && + (ic->mode == 'D' || ic->mode == 'B' || ic->mode == 'I')) + limits->features |= BLK_FEAT_STABLE_WRITES; + if (!ic->internal_hash) { struct blk_integrity *bi = &limits->integrity; diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index e1a783ee2032..1c9fcc746ec6 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -810,7 +810,7 @@ static int llbitmap_expand_pages(struct llbitmap *llbitmap, if (nr_pages <= old_nr_pages) return 0; - pctl = kcalloc(nr_pages, sizeof(*pctl), GFP_NOIO); + pctl = kzalloc_objs(*pctl, nr_pages, GFP_NOIO); if (!pctl) return -ENOMEM; @@ -846,7 +846,7 @@ static int llbitmap_alloc_pages(struct llbitmap *llbitmap) unsigned int nr_pages = max(used_pages, llbitmap_reserved_pages(llbitmap)); int i; - llbitmap->pctl = kcalloc(nr_pages, sizeof(*llbitmap->pctl), GFP_NOIO); + llbitmap->pctl = kzalloc_objs(*llbitmap->pctl, nr_pages, GFP_NOIO); if (!llbitmap->pctl) return -ENOMEM; diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 1bb3a3e98d6b..bd64c0400c0d 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -232,6 +232,19 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC10FA" }, /* NVL */ }; +/* + * The subset of ivsc_acpi_ids[] which are IVSC, rather than CVS, devices. The + * CVS IDs are deliberately not listed here: new ones keep being added, whereas + * this list is complete. + */ +static const struct acpi_device_id ivsc_only_acpi_ids[] = { + { "INTC1059" }, + { "INTC1095" }, + { "INTC100A" }, + { "INTC10CF" }, + { } +}; + static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) { unsigned int i; @@ -283,6 +296,17 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev) return csi_dev; } + /* + * The lookups below match on the ACPI companion alone. That is fine for + * CVS, which binds a driver to that very device, but not for IVSC: there + * the ACPI device also has a driverless platform device, which would be + * returned instead of the mei-csi client. Return NULL for IVSC so that + * the caller fails and the probe is retried once the IVSC device shows + * up. + */ + if (!acpi_match_device_ids(adev, ivsc_only_acpi_ids)) + return NULL; + /* Try to locate CVS device on the I2C bus */ csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); if (csi_dev) diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index eac3bc9af990..4f3299a66b37 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -3105,7 +3105,7 @@ static int allegro_open(struct file *file) unsigned int cpb_size_max; unsigned int cpb_size_def; - channel = kzalloc(sizeof(*channel), GFP_KERNEL); + channel = kzalloc_obj(*channel); if (!channel) return -ENOMEM; diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index 4801617f9559..218aad5d7dfb 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -127,7 +127,7 @@ isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size) struct device *dev = ispif->dev; int ret; - mem_info = kmalloc_obj(*mem_info, GFP_KERNEL); + mem_info = kmalloc_obj(*mem_info); if (!mem_info) return NULL; @@ -368,7 +368,7 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, /* Allocate the sync command object early and outside of the lock */ if (sync) { - ele = kmalloc_obj(*ele, GFP_KERNEL); + ele = kmalloc_obj(*ele); if (!ele) return -ENOMEM; @@ -738,7 +738,7 @@ isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info) { struct isp4if_img_buf_node *node; - node = kmalloc_obj(*node, GFP_KERNEL); + node = kmalloc_obj(*node); if (node) node->buf_info = *buf_info; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index a75a27a8afd0..5e6727d58946 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -25,6 +25,7 @@ rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), + RPPX1_PARAMS_BLOCK_INFO(AWBG_POST, awbg), RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor), RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist), RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist), diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c index 01a61db279bf..71c5561457d5 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c @@ -70,7 +70,7 @@ rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4]) /* Swap to pattern used in our path, PRE1 or PRE2. */ struct rpp_module *acq = mod == &mod->rpp->pre1.bls ? - &mod->rpp->pre1.acq : &mod->rpp->pre2.bls; + &mod->rpp->pre1.acq : &mod->rpp->pre2.acq; enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern; for (unsigned int i = 0; i < 4; ++i) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c index 2d622e85f827..49d9b4a72387 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c @@ -1299,11 +1299,12 @@ static void vdec_av1_slice_setup_tile(struct vdec_av1_slice_frame *frame, tile->uniform_tile_spacing_flag = BIT_FLAG(ctrl_tile, V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING); - for (i = 0; i < tile->tile_cols + 1; i++) + /* Bound the copy to the mi_col_starts[]/mi_row_starts[] capacity. */ + for (i = 0; i < tile->tile_cols + 1 && i < V4L2_AV1_MAX_TILE_COLS + 1; i++) tile->mi_col_starts[i] = ALIGN(ctrl_tile->mi_col_starts[i], BIT(mib_size_log2)) >> mib_size_log2; - for (i = 0; i < tile->tile_rows + 1; i++) + for (i = 0; i < tile->tile_rows + 1 && i < V4L2_AV1_MAX_TILE_ROWS + 1; i++) tile->mi_row_starts[i] = ALIGN(ctrl_tile->mi_row_starts[i], BIT(mib_size_log2)) >> mib_size_log2; } diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c index b5861d0cd0e8..586a5adbedfa 100644 --- a/drivers/media/platform/renesas/rcar-isp/core.c +++ b/drivers/media/platform/renesas/rcar-isp/core.c @@ -232,7 +232,7 @@ int risp_core_job_prepare(struct rcar_isp_core *core) } /* Memory is released when the job is consumed. */ - job = kzalloc(sizeof(*job), GFP_KERNEL); + job = kzalloc_obj(*job); if (!job) return -ENOMEM; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index 9c4a6093af32..2b8e04dd1572 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -16,6 +16,7 @@ */ #include <linux/v4l2-common.h> +#include <media/v4l2-hevc.h> #include <media/v4l2-mem2mem.h> #include "rkvdec.h" @@ -37,15 +38,17 @@ void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size, s32 pic_in_cts_height, u16 *column_width, u16 *row_height) { const struct v4l2_ctrl_hevc_pps *pps = run->pps; + unsigned int num_cols = v4l2_hevc_pps_num_tile_columns(pps); + unsigned int num_rows = v4l2_hevc_pps_num_tile_rows(pps); int i; - for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++) + for (i = 0; i < num_cols; i++) column_width[i] = ((i + 1) * pic_in_cts_width) / (pps->num_tile_columns_minus1 + 1) - (i * pic_in_cts_width) / (pps->num_tile_columns_minus1 + 1); - for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++) + for (i = 0; i < num_rows; i++) row_height[i] = ((i + 1) * pic_in_cts_height) / (pps->num_tile_rows_minus1 + 1) - (i * pic_in_cts_height) / @@ -57,17 +60,20 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size s32 pic_in_cts_height, u16 *column_width, u16 *row_height) { const struct v4l2_ctrl_hevc_pps *pps = run->pps; + unsigned int num_cols = v4l2_hevc_pps_num_tile_columns(pps); + unsigned int num_rows = v4l2_hevc_pps_num_tile_rows(pps); s32 sum = 0; int i; - for (i = 0; i < pps->num_tile_columns_minus1; i++) { + /* The last tile entry is written after the loop, so iterate one less. */ + for (i = 0; i < num_cols - 1; i++) { column_width[i] = pps->column_width_minus1[i] + 1; sum += column_width[i]; } column_width[i] = pic_in_cts_width - sum; sum = 0; - for (i = 0; i < pps->num_tile_rows_minus1; i++) { + for (i = 0; i < num_rows - 1; i++) { row_height[i] = pps->row_height_minus1[i] + 1; sum += row_height[i]; } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index ff3942f91c5d..88e90c438eb9 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -12,6 +12,7 @@ * Jeffy Chen <jeffy.chen@rock-chips.com> */ +#include <media/v4l2-hevc.h> #include <media/v4l2-mem2mem.h> #include "rkvdec.h" @@ -135,6 +136,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, * packet unit). so the driver copy SPS/PPS information to the exact PPS * packet unit for HW accessing. */ + if (pps->pic_parameter_set_id >= ARRAY_SIZE(priv_tbl->param_set)) + return; hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); @@ -253,9 +256,9 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) { /* Userspace also provide column width and row height for uniform spacing */ - for (i = 0; i <= pps->num_tile_columns_minus1; i++) + for (i = 0; i < v4l2_hevc_pps_num_tile_columns(pps); i++) WRITE_PPS(pps->column_width_minus1[i], COLUMN_WIDTH(i)); - for (i = 0; i <= pps->num_tile_rows_minus1; i++) + for (i = 0; i < v4l2_hevc_pps_num_tile_rows(pps); i++) WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i)); } else { WRITE_PPS(DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_y) - 1, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c index d07c74679552..e1936e87f45b 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c @@ -145,6 +145,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, * packet unit). so the driver copy SPS/PPS information to the exact PPS * packet unit for HW accessing. */ + if (pps->pic_parameter_set_id >= ARRAY_SIZE(priv_tbl->param_set)) + return; hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; memset(hw_ps, 0, sizeof(*hw_ps)); diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index e8c2e83379de..e7a7c7a42467 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -5,6 +5,8 @@ * Copyright (C) 2020 Safran Passenger Innovations LLC */ +#include <media/v4l2-hevc.h> + #include "hantro_hw.h" #include "hantro_g2_regs.h" @@ -15,8 +17,8 @@ static void prepare_tile_info_buffer(struct hantro_ctx *ctx) const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu); - unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1; - unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; + unsigned int num_tile_rows = v4l2_hevc_pps_num_tile_rows(pps); + unsigned int num_tile_cols = v4l2_hevc_pps_num_tile_columns(pps); unsigned int pic_width_in_ctbs, pic_height_in_ctbs; unsigned int max_log2_ctb_size, ctb_size; bool tiles_enabled, uniform_spacing; diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index e4e21ad37323..00aa566a4ccd 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -431,20 +431,39 @@ static int rockchip_vpu981_av1_dec_prepare_run(struct hantro_ctx *ctx) { struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec; struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls; + const struct v4l2_av1_tile_info *tile_info; + struct v4l2_ctrl *tge; + u32 num_tiles; ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE); if (WARN_ON(!ctrls->sequence)) return -EINVAL; - ctrls->tile_group_entry = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY); - if (WARN_ON(!ctrls->tile_group_entry)) + tge = v4l2_ctrl_find(&ctx->ctrl_handler, + V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY); + if (WARN_ON(!tge)) return -EINVAL; + ctrls->tile_group_entry = tge->p_cur.p; ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME); if (WARN_ON(!ctrls->frame)) return -EINVAL; + /* + * rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group + * entry array by tile1 * tile_cols + tile0, so it reads up to + * tile_cols * tile_rows entries, and lays out one descriptor per tile + * in the AV1_MAX_TILES tile_info buffer while programming the real + * tile geometry into the hardware. Reject a frame that claims more + * tiles than userspace submitted, or more than the hardware tile + * buffer holds, so the read stays in bounds and the programmed + * geometry matches the descriptors written. + */ + tile_info = &ctrls->frame->tile_info; + num_tiles = (u32)tile_info->tile_cols * tile_info->tile_rows; + if (num_tiles > tge->elems || num_tiles > AV1_MAX_TILES) + return -EINVAL; + ctrls->film_grain = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN); @@ -578,16 +597,30 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx) const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info; const struct v4l2_ctrl_av1_tile_group_entry *group_entry = ctrls->tile_group_entry; - int context_update_y = - tile_info->context_update_tile_id / tile_info->tile_cols; - int context_update_x = - tile_info->context_update_tile_id % tile_info->tile_cols; - int context_update_tile_id = - context_update_x * tile_info->tile_rows + context_update_y; + int context_update_y = 0; + int context_update_x = 0; + int context_update_tile_id = 0; u8 *dst = av1_dec->tile_info.cpu; + u8 *dst_end = dst + av1_dec->tile_info.size; struct hantro_dev *vpu = ctx->dev; int tile0, tile1; + /* + * tile_cols and tile_rows are bounded by the V4L2 control validation + * (V4L2_AV1_MAX_TILE_{COLS,ROWS} and V4L2_AV1_MAX_TILE_COUNT). Guard + * the divisor here, and keep the descriptor writes within the + * AV1_MAX_TILES tile_info buffer below; the register values use the + * unmodified tile geometry. + */ + if (tile_info->tile_cols) { + context_update_y = + tile_info->context_update_tile_id / tile_info->tile_cols; + context_update_x = + tile_info->context_update_tile_id % tile_info->tile_cols; + context_update_tile_id = + context_update_x * tile_info->tile_rows + context_update_y; + } + memset(dst, 0, av1_dec->tile_info.size); for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) { @@ -598,6 +631,10 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx) tile_info->height_in_sbs_minus_1[tile1] + 1; u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1; + /* Stop once the tile_info descriptor buffer is full. */ + if (dst + 16 > dst_end) + break; + /* tile size in SB units (width,height) */ *dst++ = x0; *dst++ = 0; @@ -622,6 +659,8 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx) *dst++ = (end >> 16) & 255; *dst++ = (end >> 24) & 255; } + if (dst + 16 > dst_end) + break; } hantro_reg_write(vpu, &av1_multicore_expect_context_update, !!(context_update_x == 0)); diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index b5117ee9f5fa..7758487da3d3 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -164,7 +164,7 @@ static int igorplugusb_probe(struct usb_interface *intf, if (!ir) return -ENOMEM; - ir->request = kzalloc_obj(*ir->request, GFP_KERNEL); + ir->request = kzalloc_obj(*ir->request); if (!ir->request) goto fail; diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 5b8a594fb9e2..648b88c868bc 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -793,10 +793,30 @@ static int validate_av1_film_grain(struct v4l2_ctrl_av1_film_grain *fg) return 0; } +static int validate_av1_tile_info(struct v4l2_av1_tile_info *t) +{ + /* + * tile_cols and tile_rows index the per-tile descriptor arrays and + * bound the tile loops in the stateless AV1 drivers; the product + * bounds the total tile descriptor count. + */ + if (t->tile_cols > V4L2_AV1_MAX_TILE_COLS || + t->tile_rows > V4L2_AV1_MAX_TILE_ROWS) + return -EINVAL; + + if ((u32)t->tile_cols * t->tile_rows > V4L2_AV1_MAX_TILE_COUNT) + return -EINVAL; + + return 0; +} + static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f) { int ret = 0; + ret = validate_av1_tile_info(&f->tile_info); + if (ret) + return ret; ret = validate_av1_quantization(&f->quantization); if (ret) return ret; @@ -1253,6 +1273,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, p_hevc_pps->flags &= ~V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED; + } else { + /* + * These count the entries the stateless HEVC drivers + * read from column_width_minus1[] / row_height_minus1[] + * and use as tile-loop bounds. + */ + if (p_hevc_pps->num_tile_columns_minus1 >= + ARRAY_SIZE(p_hevc_pps->column_width_minus1)) + return -EINVAL; + if (p_hevc_pps->num_tile_rows_minus1 >= + ARRAY_SIZE(p_hevc_pps->row_height_minus1)) + return -EINVAL; } if (p_hevc_pps->flags & diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c index c00197d095e7..2323f559c6a3 100644 --- a/drivers/media/v4l2-core/v4l2-h264.c +++ b/drivers/media/v4l2-core/v4l2-h264.c @@ -440,7 +440,8 @@ v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder, } if (builder->num_valid > 1 && - !memcmp(b1_reflist, b0_reflist, builder->num_valid)) + !memcmp(b1_reflist, b0_reflist, + sizeof(b1_reflist[0]) * builder->num_valid)) swap(b1_reflist[0], b1_reflist[1]); print_ref_list_b(builder, b0_reflist, 0); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 7aa32b90cf1e..16ad4fd26357 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -135,7 +135,7 @@ static int mfd_match_of_node_to_dev(struct platform_device *pdev, return -EAGAIN; allocate_of_node: - of_entry = kzalloc(sizeof(*of_entry), GFP_KERNEL); + of_entry = kzalloc_obj(*of_entry); if (!of_entry) return -ENOMEM; @@ -174,7 +174,7 @@ static int mfd_add_device(struct device *parent, int id, if (!pdev->mfd_cell) goto fail_device; - res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL); + res = kzalloc_objs(*res, cell->num_resources); if (!res) goto fail_device; diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c index ee49ac779d1a..698f60aa9756 100644 --- a/drivers/mfd/ucb1x00-assabet.c +++ b/drivers/mfd/ucb1x00-assabet.c @@ -96,7 +96,7 @@ static int ucb1x00_assabet_add(struct ucb1x00_dev *dev) struct ucb1x00_assabet_priv *priv; - priv = kzalloc_obj(*priv, GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 90fd669636ec..d4fac2caca86 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1822,24 +1822,20 @@ static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp) return err; } - bp.fd = dma_buf_fd(buf->dmabuf, O_ACCMODE); + bp.fd = get_unused_fd_flags(O_ACCMODE); if (bp.fd < 0) { dma_buf_put(buf->dmabuf); - return -EINVAL; + return bp.fd; } if (copy_to_user(argp, &bp, sizeof(bp))) { - /* - * The usercopy failed, but we can't do much about it, as - * dma_buf_fd() already called fd_install() and made the - * file descriptor accessible for the current process. It - * might already be closed and dmabuf no longer valid when - * we reach this point. Therefore "leak" the fd and rely on - * the process exit path to do any required cleanup. - */ + put_unused_fd(bp.fd); + dma_buf_put(buf->dmabuf); return -EFAULT; } + dma_buf_fd_install(buf->dmabuf, bp.fd); + return 0; } diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c index da4277ced4d6..25cf33fe1ec1 100644 --- a/drivers/mtd/mtd_virt_concat.c +++ b/drivers/mtd/mtd_virt_concat.c @@ -166,7 +166,7 @@ static int mtd_virt_concat_create_item(struct device_node *parts, return 0; } - item = kzalloc_flex(*item, nodes, count, GFP_KERNEL); + item = kzalloc_flex(*item, nodes, count); if (!item) return -ENOMEM; @@ -182,7 +182,7 @@ static int mtd_virt_concat_create_item(struct device_node *parts, for (i = 1; i < count; i++) item->nodes[i] = of_parse_phandle(parts, CONCAT_PROP, (i - 1)); - concat = kzalloc_flex(*concat, subdev, count, GFP_KERNEL); + concat = kzalloc_flex(*concat, subdev, count); if (!concat) { kfree(item); return -ENOMEM; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index c97167d51fe2..d54d309c30a0 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -639,7 +639,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c printk(KERN_NOTICE "into device \"%s\"\n", name); /* allocate the device structure */ - concat = kzalloc_flex(*concat, subdev, num_devs, GFP_KERNEL); + concat = kzalloc_flex(*concat, subdev, num_devs); if (!concat) { printk ("memory allocation error while creating concatenated device \"%s\"\n", diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 839f7482dc18..43ac8e28e418 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -678,9 +678,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected rx channel */ tx_slave = rlb_choose_channel(skb, bond, arp); - if (tx_slave) + if (tx_slave && + !ether_addr_equal_64bits(arp->mac_src, + tx_slave->dev->dev_addr)) { + if (unlikely(skb_cow_head(skb, 0))) + return NULL; + arp = (struct arp_pkt *)skb_network_header(skb); bond_hw_addr_copy(arp->mac_src, tx_slave->dev->dev_addr, tx_slave->dev->addr_len); + } netdev_dbg(bond->dev, "(slave %s): Server sent ARP Reply packet\n", tx_slave ? tx_slave->dev->name : "NULL"); } else if (arp->op_code == htons(ARPOP_REQUEST)) { @@ -875,7 +881,7 @@ static int rlb_initialize(struct bonding *bond) spin_unlock_bh(&bond->mode_lock); /* register to receive ARPs */ - bond->recv_probe = rlb_arp_recv; + WRITE_ONCE(bond->recv_probe, rlb_arp_recv); return 0; } @@ -1281,10 +1287,10 @@ unwind: } /* determine if the packet is NA or NS */ -static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond) +static bool alb_determine_nd(struct sk_buff *skb) { - struct ipv6hdr *ip6hdr; - struct icmp6hdr *hdr; + const struct ipv6hdr *ip6hdr; + const struct icmp6hdr *hdr; if (!pskb_network_may_pull(skb, sizeof(*ip6hdr))) return true; @@ -1296,7 +1302,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond) if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr))) return true; - hdr = icmp6_hdr(skb); + ip6hdr = ipv6_hdr(skb); + hdr = (const struct icmp6hdr *)(ip6hdr + 1); return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT || hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION; } @@ -1339,7 +1346,6 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond, struct slave *tx_slave) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); - struct ethhdr *eth_data = eth_hdr(skb); if (!tx_slave) { /* unbalanced or unassigned, send through primary */ @@ -1350,7 +1356,9 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond, if (tx_slave && bond_slave_can_tx(tx_slave)) { if (tx_slave != rcu_access_pointer(bond->curr_active_slave)) { - ether_addr_copy(eth_data->h_source, + if (unlikely(skb_cow_head(skb, 0))) + return bond_tx_drop(bond->dev, skb); + ether_addr_copy(skb_eth_hdr(skb)->h_source, tx_slave->dev->dev_addr); } @@ -1374,14 +1382,13 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond, struct ethhdr *eth_data; u32 hash_index; - skb_reset_mac_header(skb); - eth_data = eth_hdr(skb); + eth_data = skb_eth_hdr(skb); /* Do not TX balance any multicast or broadcast */ if (!is_multicast_ether_addr(eth_data->h_dest)) { switch (skb->protocol) { case htons(ETH_P_IPV6): - if (alb_determine_nd(skb, bond)) + if (alb_determine_nd(skb)) break; fallthrough; case htons(ETH_P_IP): @@ -1427,8 +1434,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond, u32 hash_index = 0; int hash_size = 0; - skb_reset_mac_header(skb); - eth_data = eth_hdr(skb); + eth_data = skb_eth_hdr(skb); switch (ntohs(skb->protocol)) { case ETH_P_IP: { @@ -1467,7 +1473,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond, break; } - if (alb_determine_nd(skb, bond)) { + if (alb_determine_nd(skb)) { do_tx_balance = false; break; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ef9eb0c53c66..a9bff7663eec 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1245,7 +1245,7 @@ static void bond_peer_notify_may_events(struct bonding *bond, bool force) } if (notified || force) - bond->send_peer_notif--; + WRITE_ONCE(bond->send_peer_notif, bond->send_peer_notif - 1); } /** @@ -2284,7 +2284,7 @@ skip_mac_set: } } - bond->slave_cnt++; + WRITE_ONCE(bond->slave_cnt, bond->slave_cnt + 1); netdev_compute_master_upper_features(bond->dev, true); bond_set_carrier(bond); @@ -2517,9 +2517,7 @@ static int __bond_release_one(struct net_device *bond_dev, bond_alb_deinit_slave(bond, slave); } - if (all) { - RCU_INIT_POINTER(bond->curr_active_slave, NULL); - } else if (oldcurrent == slave) { + if (!all && oldcurrent == slave) { /* Note that we hold RTNL over this sequence, so there * is no concern that another slave add/remove event * will interfere. @@ -2533,7 +2531,7 @@ static int __bond_release_one(struct net_device *bond_dev, unblock_netpoll_tx(); synchronize_rcu(); - bond->slave_cnt--; + WRITE_ONCE(bond->slave_cnt, bond->slave_cnt - 1); if (!bond_has_slaves(bond)) { call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); @@ -4385,13 +4383,13 @@ static int bond_open(struct net_device *bond_dev) if (bond->params.arp_interval) { /* arp interval, in milliseconds. */ queue_delayed_work(bond->wq, &bond->arp_work, 0); - bond->recv_probe = bond_rcv_validate; + WRITE_ONCE(bond->recv_probe, bond_rcv_validate); } if (BOND_MODE(bond) == BOND_MODE_8023AD) { queue_delayed_work(bond->wq, &bond->ad_work, 0); /* register to receive LACPDUs */ - bond->recv_probe = bond_3ad_lacpdu_recv; + WRITE_ONCE(bond->recv_probe, bond_3ad_lacpdu_recv); bond_3ad_initiate_agg_selection(bond, 1); bond_for_each_slave(bond, slave, iter) @@ -4413,7 +4411,7 @@ static int bond_close(struct net_device *bond_dev) struct slave *slave; bond_work_cancel_all(bond); - bond->send_peer_notif = 0; + WRITE_ONCE(bond->send_peer_notif, 0); WRITE_ONCE(bond->recv_probe, NULL); /* Wait for any in-flight RX handlers */ @@ -5118,7 +5116,7 @@ static void bond_skip_slave(struct bond_up_slave *slaves, if (skipslave == slaves->arr[idx]) { slaves->arr[idx] = slaves->arr[slaves->count - 1]; - slaves->count--; + WRITE_ONCE(slaves->count, slaves->count - 1); break; } } diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 36b8d89387ee..9efadeff6a22 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1147,11 +1147,11 @@ static int bond_option_arp_interval_set(struct bonding *bond, */ if (!newval->value) { if (bond->params.arp_validate) - bond->recv_probe = NULL; + WRITE_ONCE(bond->recv_probe, NULL); cancel_delayed_work_sync(&bond->arp_work); } else { /* arp_validate can be set only in active-backup mode */ - bond->recv_probe = bond_rcv_validate; + WRITE_ONCE(bond->recv_probe, bond_rcv_validate); cancel_delayed_work_sync(&bond->mii_work); queue_delayed_work(bond->wq, &bond->arp_work, 0); } diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 50d3a818eb1b..84a086c3e99b 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -1088,6 +1088,8 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv, unsigned int index = 1, rules_cnt = 0; for_each_set_bit_from(index, priv->cfp.unique, priv->num_cfp_rules) { + if (rules_cnt == nfc->rule_cnt) + return -EMSGSIZE; rule_locs[rules_cnt] = index; rules_cnt++; } diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.h b/drivers/net/dsa/lantiq/lantiq_gswip.h index bc3686faad0d..0b75be14dc10 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip.h +++ b/drivers/net/dsa/lantiq/lantiq_gswip.h @@ -42,7 +42,7 @@ #define GSWIP_MDIO_PHY_FDUP_DIS 0x0600 #define GSWIP_MDIO_PHY_FCONTX_MASK 0x0180 #define GSWIP_MDIO_PHY_FCONTX_AUTO 0x0000 -#define GSWIP_MDIO_PHY_FCONTX_EN 0x0100 +#define GSWIP_MDIO_PHY_FCONTX_EN 0x0080 #define GSWIP_MDIO_PHY_FCONTX_DIS 0x0180 #define GSWIP_MDIO_PHY_FCONRX_MASK 0x0060 #define GSWIP_MDIO_PHY_FCONRX_AUTO 0x0000 diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 2b7be091c056..3e61eb3c2b1e 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -3172,23 +3172,31 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE; + priv->info->mac_port_get_caps(ds, port, config); + /* The EN7528 GPHYs report EEE capability, but negotiating EEE with * common link partners (e.g. Realtek GbE NICs) results in an unstable * link with dropped frames. Leave the LPI capabilities empty so that * phylink disables EEE on these PHYs and refuses to enable it from * userspace. */ - if (priv->id != ID_EN7528) { + if (priv->id != ID_EN7528 && + config->mac_capabilities & (MAC_100FD | MAC_1000FD)) { u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port)); - config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD; + /* LPI above 1 Gbps is not supported */ + config->lpi_capabilities = config->mac_capabilities & + (MAC_100FD | MAC_1000FD); + phy_interface_copy(config->lpi_interfaces, + config->supported_interfaces); + __clear_bit(PHY_INTERFACE_MODE_2500BASEX, + config->lpi_interfaces); + /* tx_lpi_timer should be in microseconds. The time units for * LPI threshold are unspecified. */ config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr); } - - priv->info->mac_port_get_caps(ds, port, config); } static int mt753x_pcs_validate(struct phylink_pcs *pcs, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 80b877c74513..7f68a0c55802 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2438,6 +2438,7 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port, struct ethtool_rx_flow_spec *fs = &rxnfc->fs; struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_policy *policy; + u32 cnt = 0; int err; int id; @@ -2463,11 +2464,18 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port, break; case ETHTOOL_GRXCLSRLALL: rxnfc->data = 0; - rxnfc->rule_cnt = 0; - idr_for_each_entry(&chip->policies, policy, id) - if (policy->port == port) - rule_locs[rxnfc->rule_cnt++] = id; err = 0; + idr_for_each_entry(&chip->policies, policy, id) { + if (policy->port != port) + continue; + if (cnt == rxnfc->rule_cnt) { + err = -EMSGSIZE; + break; + } + rule_locs[cnt++] = id; + } + if (!err) + rxnfc->rule_cnt = cnt; break; default: err = -EOPNOTSUPP; diff --git a/drivers/net/dsa/mv88e6xxx/tcflower.c b/drivers/net/dsa/mv88e6xxx/tcflower.c index d67604a55b9f..2ddfeaaa0690 100644 --- a/drivers/net/dsa/mv88e6xxx/tcflower.c +++ b/drivers/net/dsa/mv88e6xxx/tcflower.c @@ -97,7 +97,7 @@ int mv88e6xxx_cls_flower_add(struct dsa_switch *ds, int port, goto err_unlock; } - entry = kzalloc(sizeof(*entry), GFP_KERNEL); + entry = kzalloc_obj(*entry); if (!entry) { err = -ENOMEM; goto err_unlock; diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index fa9a8edce22f..8277c1c87bb3 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -538,7 +538,7 @@ struct airoha_wdma_info { /* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ #define RX_IRQ0_BANK_PIN_MASK 0x839f -#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 +#define RX_IRQ1_BANK_PIN_MASK 0xffe00000 #define RX_IRQ2_BANK_PIN_MASK 0x20 #define RX_IRQ3_BANK_PIN_MASK 0x40 #define RX_IRQ_BANK_PIN_MASK(_n) \ diff --git a/drivers/net/ethernet/alibaba/eea/eea_adminq.c b/drivers/net/ethernet/alibaba/eea/eea_adminq.c index dfad1bdbc44d..73d0caf25700 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_adminq.c +++ b/drivers/net/ethernet/alibaba/eea/eea_adminq.c @@ -439,8 +439,7 @@ int eea_adminq_dev_status(struct eea_net *enet, q_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num + 1; io_num = enet->cfg.rx_ring_num + enet->cfg.tx_ring_num; - req = kcalloc(q_num, sizeof(struct eea_aq_queue_drv_status), - GFP_KERNEL); + req = kzalloc_objs(struct eea_aq_queue_drv_status, q_num); if (!req) return -ENOMEM; @@ -486,11 +485,11 @@ void eea_adminq_config_host_info(struct eea_net *enet) struct eea_aq_host_info_rep *rep; int rc = -ENOMEM; - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + cfg = kzalloc_obj(*cfg); if (!cfg) return; - rep = kzalloc(sizeof(*rep), GFP_KERNEL); + rep = kzalloc_obj(*rep); if (!rep) goto err_free_cfg; diff --git a/drivers/net/ethernet/alibaba/eea/eea_net.c b/drivers/net/ethernet/alibaba/eea/eea_net.c index 63e68580de94..0af6c95b5e63 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_net.c +++ b/drivers/net/ethernet/alibaba/eea/eea_net.c @@ -62,7 +62,7 @@ static int eea_alloc_irq_blks(struct eea_net *enet) num = enet->edev->rx_num; - irq_blks = kvcalloc(num, sizeof(*blk), GFP_KERNEL); + irq_blks = kvzalloc_objs(*blk, num); if (!irq_blks) return -ENOMEM; @@ -194,11 +194,11 @@ static int eea_alloc_rxtx_q_mem(struct eea_net_init_ctx *ctx) struct eea_net_tx *tx; int err, i; - ctx->tx = kvcalloc(ctx->cfg.tx_ring_num, sizeof(*ctx->tx), GFP_KERNEL); + ctx->tx = kvzalloc_objs(*ctx->tx, ctx->cfg.tx_ring_num); if (!ctx->tx) return -ENOMEM; - ctx->rx = kvcalloc(ctx->cfg.rx_ring_num, sizeof(*ctx->rx), GFP_KERNEL); + ctx->rx = kvzalloc_objs(*ctx->rx, ctx->cfg.rx_ring_num); if (!ctx->rx) goto err_free_tx; @@ -601,7 +601,7 @@ static int eea_netdev_init_features(struct net_device *netdev, int err; u32 mtu; - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + cfg = kzalloc_obj(*cfg); if (!cfg) return -ENOMEM; diff --git a/drivers/net/ethernet/alibaba/eea/eea_pci.c b/drivers/net/ethernet/alibaba/eea/eea_pci.c index 9872e360dd5d..c625b58b26f0 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_pci.c +++ b/drivers/net/ethernet/alibaba/eea/eea_pci.c @@ -651,7 +651,7 @@ static int eea_pci_probe(struct pci_dev *pci_dev, struct eea_device *edev; int err; - ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); + ep_dev = kzalloc_obj(*ep_dev); if (!ep_dev) return -ENOMEM; diff --git a/drivers/net/ethernet/alibaba/eea/eea_ring.c b/drivers/net/ethernet/alibaba/eea/eea_ring.c index 99dcabd094b8..fac7d4647ecb 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_ring.c +++ b/drivers/net/ethernet/alibaba/eea/eea_ring.c @@ -217,7 +217,7 @@ struct eea_ring *eea_ering_alloc(u32 index, u32 num, struct eea_device *edev, if (!cq_desc_size || !is_power_of_2(cq_desc_size)) return NULL; - ering = kzalloc(sizeof(*ering), GFP_KERNEL); + ering = kzalloc_obj(*ering); if (!ering) return NULL; diff --git a/drivers/net/ethernet/alibaba/eea/eea_rx.c b/drivers/net/ethernet/alibaba/eea/eea_rx.c index a3f0d2a79ad8..5dfb7cb2ba6d 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_rx.c +++ b/drivers/net/ethernet/alibaba/eea/eea_rx.c @@ -764,7 +764,7 @@ struct eea_net_rx *eea_alloc_rx(struct eea_net_init_ctx *ctx, u32 idx) struct eea_net_rx *rx; int err; - rx = kzalloc(sizeof(*rx), GFP_KERNEL); + rx = kzalloc_obj(*rx); if (!rx) return rx; @@ -786,8 +786,7 @@ struct eea_net_rx *eea_alloc_rx(struct eea_net_init_ctx *ctx, u32 idx) rx->dma_dev = ctx->edev->dma_dev; /* meta */ - rx->meta = kvcalloc(ctx->cfg.rx_ring_depth, - sizeof(*rx->meta), GFP_KERNEL); + rx->meta = kvzalloc_objs(*rx->meta, ctx->cfg.rx_ring_depth); if (!rx->meta) goto err_free_rx; diff --git a/drivers/net/ethernet/alibaba/eea/eea_tx.c b/drivers/net/ethernet/alibaba/eea/eea_tx.c index 85fb0e9ca5ba..c9292bca1a8b 100644 --- a/drivers/net/ethernet/alibaba/eea/eea_tx.c +++ b/drivers/net/ethernet/alibaba/eea/eea_tx.c @@ -480,8 +480,7 @@ int eea_alloc_tx(struct eea_net_init_ctx *ctx, struct eea_net_tx *tx, u32 idx) tx->dma_dev = ctx->edev->dma_dev; /* meta */ - tx->meta = kvcalloc(ctx->cfg.tx_ring_depth, - sizeof(*tx->meta), GFP_KERNEL); + tx->meta = kvzalloc_objs(*tx->meta, ctx->cfg.tx_ring_depth); if (!tx->meta) goto err_free_tx; diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 942454e29488..0ba67a55705d 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -1067,6 +1067,7 @@ static int emac_probe(struct platform_device *pdev) return 0; out_release_sram: + of_node_put(db->phy_node); sunxi_sram_release(&pdev->dev); out_clk_disable_unprepare: clk_disable_unprepare(db->clk); @@ -1094,6 +1095,7 @@ static void emac_remove(struct platform_device *pdev) } unregister_netdev(ndev); + of_node_put(db->phy_node); sunxi_sram_release(&pdev->dev); clk_disable_unprepare(db->clk); irq_dispose_mapping(ndev->irq); diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 922e3ec8af1b..419e4b6ac983 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -810,8 +810,7 @@ void pdsc_host_mem_add(struct pdsc *pdsc) if (count == 0) return; - pdsc->host_mem_reqs = kzalloc_objs(*pdsc->host_mem_reqs, count, - GFP_KERNEL); + pdsc->host_mem_reqs = kzalloc_objs(*pdsc->host_mem_reqs, count); if (!pdsc->host_mem_reqs) { dev_err(pdsc->dev, "failed to alloc host_mem_reqs array\n"); return; diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c index 5ccf017f6af4..7fe4594e9f9b 100644 --- a/drivers/net/ethernet/amd/pds_core/fw.c +++ b/drivers/net/ethernet/amd/pds_core/fw.c @@ -171,8 +171,10 @@ pdsc_legacy_firmware_update(struct pdsc *pdsc, dev_info(pdsc->dev, "Installing firmware\n"); - if (!pdsc->cmd_regs) + if (!pdsc->cmd_regs) { + NL_SET_ERR_MSG_MOD(extack, "BARs not mapped"); return -ENXIO; + } dl = priv_to_devlink(pdsc); devlink_flash_update_status_notify(dl, "Preparing to flash", @@ -198,6 +200,12 @@ pdsc_legacy_firmware_update(struct pdsc *pdsc, copy_sz = min_t(unsigned int, buf_sz, fw->size - offset); mutex_lock(&pdsc->devcmd_lock); + if (!pdsc->cmd_regs) { + mutex_unlock(&pdsc->devcmd_lock); + err = -ENXIO; + NL_SET_ERR_MSG_MOD(extack, "Device reset during flash"); + goto err_out; + } memcpy_toio(&pdsc->cmd_regs->data, fw->data + offset, copy_sz); err = pdsc_devcmd_fw_download_locked(pdsc, data_addr, offset, copy_sz); @@ -382,7 +390,7 @@ static int pdsc_send_package_data(struct pldmfw *context, const u8 *data, if (!length) return 0; - deferred = kmalloc_obj(*deferred, GFP_KERNEL); + deferred = kmalloc_obj(*deferred); if (!deferred) return -ENOMEM; @@ -505,7 +513,7 @@ static int pdsc_send_component_table(struct pldmfw *context, component->version_string, component->index, component->component_size, transfer_flag); - component_priv = kzalloc_obj(*component_priv, GFP_KERNEL); + component_priv = kzalloc_obj(*component_priv); if (!component_priv) return -ENOMEM; @@ -710,7 +718,7 @@ static int pdsc_flash_component_chunk(struct pdsc *pdsc, struct device *dev, u8 *component_data; int err; - deferred = kmalloc_obj(*deferred, GFP_KERNEL); + deferred = kmalloc_obj(*deferred); if (!deferred) return -ENOMEM; diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index bb79e7476370..a971c66d36f9 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -513,8 +513,12 @@ static void pdsc_reset_prepare(struct pci_dev *pdev) pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev); } - pdsc_unmap_bars(pdsc); - pci_release_regions(pdev); + if (!pdev->is_virtfn) { + mutex_lock(&pdsc->devcmd_lock); + pdsc_unmap_bars(pdsc); + mutex_unlock(&pdsc->devcmd_lock); + pci_release_regions(pdev); + } if (pci_is_enabled(pdev)) pci_disable_device(pdev); pdsc_deferred_dma_free(pdsc); @@ -543,7 +547,9 @@ static void pdsc_reset_done(struct pci_dev *pdev) return; } + mutex_lock(&pdsc->devcmd_lock); err = pdsc_map_bars(pdsc); + mutex_unlock(&pdsc->devcmd_lock); if (err) return; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 2de974213090..e2e850c1b90b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -3400,7 +3400,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata) XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val); /* Enable MAC Rx */ - XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1); + XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1); @@ -3411,7 +3411,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata) unsigned int i; /* Disable MAC Rx */ - XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0); XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0); diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c index ed0977832ce4..f2176ef3a127 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c @@ -148,8 +148,9 @@ static int tx_spb_ring_full(struct bcmasp_intf *intf, int cnt) int next_index, i; /* Check if we have enough room for cnt descriptors */ + next_index = intf->tx_spb_index; for (i = 0; i < cnt; i++) { - next_index = incr_ring(intf->tx_spb_index, DESC_RING_COUNT); + next_index = incr_ring(next_index, DESC_RING_COUNT); if (next_index == intf->tx_spb_clean_index) return 1; } @@ -301,6 +302,7 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) txcb->bytes_sent = total_bytes; dma_unmap_addr_set(txcb, dma_addr, mapping); dma_unmap_len_set(txcb, dma_len, size); + txcb->last = false; if (!i) { desc->flags |= DESC_SOF; if (csum_hw) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index d59bcca73a2b..d7728d0c5b6e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -462,6 +462,16 @@ u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb) static void bnxt_txr_db_kick(struct bnxt *bp, struct bnxt_tx_ring_info *txr, u16 prod) { + /* If the most recent BD has its completion suppressed, unset the bit + * so that a completion is generated, otherwise nothing is left to + * clean the ring and wake the queue. + */ + if (txr->kick_txbd0) { + txr->kick_txbd0->tx_bd_len_flags_type &= + cpu_to_le32(~TX_BD_FLAGS_NO_CMPL); + txr->kick_txbd0 = NULL; + } + /* Sync BD data before updating doorbell */ wmb(); bnxt_db_write(bp, &txr->tx_db, prod); @@ -485,7 +495,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) struct bnxt_sw_tx_bd *tx_buf; __le32 lflags = 0; skb_frag_t *frag; - netdev_tx_t ret; i = skb_get_queue_mapping(skb); if (unlikely(i >= bp->tx_nr_rings)) { @@ -509,11 +518,22 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) && !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) { - ret = bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); - if (txr->kick_pending) + int rc = bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); + + /* if SW USO queued a packet, the doorbell will be written + * below and there is no reason to track the last BD with + * suppressed completions + */ + if (rc > 0) + txr->kick_txbd0 = NULL; + + /* if a packet was queued by SW USO or a doorbell was pending + * from a previous xmit that was deferred, write the doorbell. + */ + if (rc > 0 || txr->kick_pending) bnxt_txr_db_kick(bp, txr, txr->tx_prod); - return ret; + return rc < 0 ? NETDEV_TX_BUSY : NETDEV_TX_OK; } free_size = bnxt_tx_avail(bp, txr); @@ -751,23 +771,23 @@ normal_tx: prod = NEXT_TX(prod); WRITE_ONCE(txr->tx_prod, prod); + txr->kick_txbd0 = NULL; if (!netdev_xmit_more() || netif_xmit_stopped(txq)) { bnxt_txr_db_kick(bp, txr, prod); } else { - if (free_size >= bp->tx_wake_thresh) + if (free_size >= bp->tx_wake_thresh) { txbd0->tx_bd_len_flags_type |= cpu_to_le32(TX_BD_FLAGS_NO_CMPL); + txr->kick_txbd0 = txbd0; + } txr->kick_pending = 1; } tx_done: if (unlikely(bnxt_tx_avail(bp, txr) <= MAX_SKB_FRAGS + 1)) { - if (netdev_xmit_more() && !tx_buf->is_push) { - txbd0->tx_bd_len_flags_type &= - cpu_to_le32(~TX_BD_FLAGS_NO_CMPL); + if (txr->kick_pending) bnxt_txr_db_kick(bp, txr, prod); - } netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr), bp->tx_wake_thresh); @@ -1514,14 +1534,16 @@ static int bnxt_discard_rx(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, return 0; } -static u16 bnxt_alloc_agg_idx(struct bnxt_rx_ring_info *rxr, u16 agg_id) +static u16 bnxt_alloc_agg_idx(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, + u16 agg_id) { struct bnxt_tpa_idx_map *map = rxr->rx_tpa_idx_map; - u16 idx = agg_id & MAX_TPA_P5_MASK; + u16 idx = agg_id & (bp->max_tpa_roundup_size - 1); if (test_bit(idx, map->agg_idx_bmap)) { - idx = find_first_zero_bit(map->agg_idx_bmap, MAX_TPA_P5); - if (idx >= MAX_TPA_P5) + idx = find_first_zero_bit(map->agg_idx_bmap, + bp->max_tpa_roundup_size); + if (idx >= bp->max_tpa_roundup_size) return INVALID_HW_RING_ID; } __set_bit(idx, map->agg_idx_bmap); @@ -1586,7 +1608,7 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { agg_id = TPA_START_AGG_ID_P5(tpa_start); - agg_id = bnxt_alloc_agg_idx(rxr, agg_id); + agg_id = bnxt_alloc_agg_idx(bp, rxr, agg_id); if (unlikely(agg_id == INVALID_HW_RING_ID)) { netdev_warn(bp->dev, "Unable to allocate agg ID for ring %d, agg 0x%x\n", rxr->bnapi->index, @@ -3584,7 +3606,7 @@ static void bnxt_free_one_tpa_info_data(struct bnxt *bp, { int i; - for (i = 0; i < bp->max_tpa; i++) { + for (i = 0; i < bp->max_tpa_roundup_size; i++) { struct bnxt_tpa_info *tpa_info = &rxr->rx_tpa[i]; u8 *data = tpa_info->data; @@ -3781,7 +3803,7 @@ static void bnxt_free_one_tpa_info(struct bnxt *bp, kfree(rxr->rx_tpa_idx_map); rxr->rx_tpa_idx_map = NULL; if (rxr->rx_tpa) { - for (i = 0; i < bp->max_tpa; i++) { + for (i = 0; i < bp->max_tpa_roundup_size; i++) { kfree(rxr->rx_tpa[i].agg_arr); rxr->rx_tpa[i].agg_arr = NULL; } @@ -3807,13 +3829,14 @@ static int bnxt_alloc_one_tpa_info(struct bnxt *bp, struct rx_agg_cmp *agg; int i; - rxr->rx_tpa = kzalloc_objs(struct bnxt_tpa_info, bp->max_tpa); + rxr->rx_tpa = kzalloc_objs(struct bnxt_tpa_info, + bp->max_tpa_roundup_size); if (!rxr->rx_tpa) return -ENOMEM; if (!(bp->flags & BNXT_FLAG_CHIP_P5_PLUS)) return 0; - for (i = 0; i < bp->max_tpa; i++) { + for (i = 0; i < bp->max_tpa_roundup_size; i++) { agg = kzalloc_objs(*agg, MAX_SKB_FRAGS); if (!agg) return -ENOMEM; @@ -3832,6 +3855,9 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp) bp->max_tpa = MAX_TPA; if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { + /* TPA is not supported at all, so there is nothing to + * allocate. + */ if (!bp->max_tpa_v2) return 0; bp->max_tpa = min_t(u16, bp->max_tpa_v2, MAX_TPA_P5); @@ -3839,6 +3865,7 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp) if (bp->max_tpa <= 32 && BNXT_CHIP_P5(bp) && !BNXT_NPAR(bp)) bp->max_tpa = MAX_TPA_P5; } + bp->max_tpa_roundup_size = roundup_pow_of_two(bp->max_tpa); for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; @@ -4551,7 +4578,7 @@ static int bnxt_alloc_one_tpa_info_data(struct bnxt *bp, u8 *data; int i; - for (i = 0; i < bp->max_tpa; i++) { + for (i = 0; i < bp->max_tpa_roundup_size; i++) { data = __bnxt_alloc_rx_frag(bp, &mapping, rxr, GFP_KERNEL); if (!data) @@ -5006,7 +5033,8 @@ void bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode) bnxt_get_max_rings(bp, &rx, &tx, true); if (rx > 1) { bp->flags &= ~BNXT_FLAG_NO_AGG_RINGS; - bp->dev->hw_features |= NETIF_F_LRO; + if (BNXT_SUPPORTS_TPA(bp)) + bp->dev->hw_features |= NETIF_F_LRO; } } @@ -5427,6 +5455,8 @@ static void bnxt_clear_ring_indices(struct bnxt *bp) txr->tx_prod = 0; txr->tx_cons = 0; txr->tx_hw_cons = 0; + txr->kick_pending = 0; + txr->kick_txbd0 = NULL; } rxr = bnapi->rx_ring; @@ -11340,8 +11370,13 @@ static int bnxt_shutdown_nic(struct bnxt *bp, bool irq_re_init) static int bnxt_init_nic(struct bnxt *bp, bool irq_re_init) { + int rc; + bnxt_init_cp_rings(bp); - bnxt_init_rx_rings(bp); + rc = bnxt_init_rx_rings(bp); + if (rc) + return rc; + bnxt_init_tx_rings(bp); bnxt_init_ring_grps(bp, irq_re_init); bnxt_init_vnics(bp); @@ -11772,6 +11807,8 @@ static int bnxt_tx_queue_start(struct bnxt *bp, int idx) txr->tx_prod = 0; txr->tx_cons = 0; txr->tx_hw_cons = 0; + txr->kick_pending = 0; + txr->kick_txbd0 = NULL; start_tx: WRITE_ONCE(txr->dev_state, 0); synchronize_net(); @@ -14603,7 +14640,14 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) rxr->rx_sw_agg_prod = 0; rxr->rx_next_cons = 0; rxr->bnapi->in_reset = false; - bnxt_alloc_one_rx_ring(bp, i); + rc = bnxt_alloc_one_rx_ring(bp, i); + if (rc) { + netdev_warn(bp->dev, "RX ring reset failed to allocate buffers, rc = %d, falling back to global reset\n", + rc); + bnxt_reset_task(bp, true); + bnxt_rtnl_unlock_sp(bp); + return; + } cpr = &rxr->bnapi->cp_ring; cpr->sw_stats->rx.rx_resets++; if (bp->flags & BNXT_FLAG_AGG_RINGS) @@ -16332,6 +16376,8 @@ static int bnxt_queue_mem_alloc(struct net_device *dev, clone->need_head_pool = false; clone->rx_page_size = qcfg->rx_page_size; clone->rx_agg_bmap = NULL; + clone->rx_tpa = NULL; + clone->rx_tpa_idx_map = NULL; rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid); if (rc) @@ -16375,11 +16421,16 @@ static int bnxt_queue_mem_alloc(struct net_device *dev, bnxt_alloc_one_rx_ring_skb(bp, clone, idx); if (bp->flags & BNXT_FLAG_AGG_RINGS) bnxt_alloc_one_rx_ring_netmem(bp, clone, idx); - if (bp->flags & BNXT_FLAG_TPA) - bnxt_alloc_one_tpa_info_data(bp, clone); + if (bp->flags & BNXT_FLAG_TPA) { + rc = bnxt_alloc_one_tpa_info_data(bp, clone); + if (rc) + goto err_free_rx_ring_skbs; + } return 0; +err_free_rx_ring_skbs: + bnxt_free_one_rx_ring_skbs(bp, clone); err_free_tpa_info: bnxt_free_one_tpa_info(bp, clone); err_free_rx_agg_ring: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index ab894f8addef..c673b2ce4a0d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -789,7 +789,6 @@ struct nqe_cn { #define MAX_TPA 64 #define MAX_TPA_P5 256 -#define MAX_TPA_P5_MASK (MAX_TPA_P5 - 1) #define MAX_TPA_SEGS_P5 0x3f #if (BNXT_PAGE_SHIFT == 16) @@ -993,6 +992,7 @@ struct bnxt_tx_ring_info { u16 txq_index; u8 tx_napi_idx; u8 kick_pending; + struct tx_bd *kick_txbd0; struct bnxt_db_info tx_db; struct tx_bd *tx_desc_ring[MAX_TX_PAGES]; @@ -2380,6 +2380,7 @@ struct bnxt { u16 max_tpa_v2; u16 max_tpa; + u16 max_tpa_roundup_size; u32 rx_buf_size; u32 rx_buf_use_size; /* useable size */ u16 rx_offset; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c index f7e18bea0fb8..6c1060fa2ea5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c @@ -31,10 +31,14 @@ static u32 bnxt_sw_gso_lhint(unsigned int len) return TX_BD_FLAGS_LHINT_2048_AND_LARGER; } -netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, - struct bnxt_tx_ring_info *txr, - struct netdev_queue *txq, - struct sk_buff *skb) +/* Transmit an skb requiring software UDP segmentation. + * + * Returns 1 if the skb was queued and new BDs were produced, 0 if the skb + * was dropped, or -1 if the ring is full and the skb should be retried. + * The caller owns the doorbell for all three cases. + */ +int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + struct netdev_queue *txq, struct sk_buff *skb) { unsigned int last_unmap_len __maybe_unused = 0; dma_addr_t last_unmap_addr __maybe_unused = 0; @@ -69,7 +73,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, if (unlikely(bnxt_tx_avail(bp, txr) < bds_needed)) { netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr), bp->tx_wake_thresh); - return NETDEV_TX_BUSY; + return -1; } /* BD backpressure alone cannot prevent overwriting in-flight @@ -77,7 +81,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, */ if (!netif_txq_maybe_stop(txq, bnxt_inline_avail(txr), num_segs, num_segs)) - return NETDEV_TX_BUSY; + return -1; if (unlikely(tso_dma_map_init(&map, &pdev->dev, skb, hdr_len))) goto drop; @@ -223,16 +227,15 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, netdev_tx_sent_queue(txq, skb->len); WRITE_ONCE(txr->tx_prod, prod); - txr->kick_pending = 1; if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh)) netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr), bp->tx_wake_thresh); - return NETDEV_TX_OK; + return 1; drop: dev_kfree_skb_any(skb); dev_core_stats_tx_dropped_inc(bp->dev); - return NETDEV_TX_OK; + return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h index 47528c20f311..77d9af97cc22 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h @@ -38,9 +38,7 @@ static inline int bnxt_min_tx_desc_cnt(struct bnxt *bp, return BNXT_MIN_TX_DESC_CNT; } -netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, - struct bnxt_tx_ring_info *txr, - struct netdev_queue *txq, - struct sk_buff *skb); +int bnxt_sw_udp_gso_xmit(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + struct netdev_queue *txq, struct sk_buff *skb); #endif diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 1e1f52285a39..d6931c41f39d 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -968,6 +968,8 @@ struct macb_dma_desc_ptp { * of the frame * @mapping: DMA address of the skb's fragment buffer * @size: size of the DMA mapped buffer + * @fcs_len: FCS bytes appended in software, 0 or ETH_FCS_LEN, only + * set for the last buffer of the frame * @mapped_as_page: true when buffer was mapped with skb_frag_dma_map(), * false when buffer was mapped with dma_map_single() */ @@ -975,6 +977,7 @@ struct macb_tx_skb { struct sk_buff *skb; dma_addr_t mapping; size_t size; + u8 fcs_len; bool mapped_as_page; }; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 76ee4f506033..b8234ac4b602 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1164,6 +1164,8 @@ static int macb_mii_init(struct macb *bp) if (err) goto err_out_unregister_bus; + of_node_put(mdio_np); + return 0; err_out_unregister_bus: @@ -1322,8 +1324,8 @@ static void macb_tx_error_task(struct work_struct *work) bp->netdev->stats.tx_packets++; queue->stats.tx_packets++; packets++; - bp->netdev->stats.tx_bytes += skb->len; - queue->stats.tx_bytes += skb->len; + bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len; + queue->stats.tx_bytes += skb->len - tx_skb->fcs_len; bytes += skb->len; } } else { @@ -1450,8 +1452,8 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) skb->data); bp->netdev->stats.tx_packets++; queue->stats.tx_packets++; - bp->netdev->stats.tx_bytes += skb->len; - queue->stats.tx_bytes += skb->len; + bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len; + queue->stats.tx_bytes += skb->len - tx_skb->fcs_len; packets++; bytes += skb->len; } @@ -2199,7 +2201,8 @@ static void macb_poll_controller(struct net_device *netdev) static unsigned int macb_tx_map(struct macb *bp, struct macb_queue *queue, struct sk_buff *skb, - unsigned int hdrlen) + unsigned int hdrlen, + u8 fcs_len) { unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; unsigned int len, i, tx_head = queue->tx_head; @@ -2284,6 +2287,7 @@ static unsigned int macb_tx_map(struct macb *bp, /* This is the last buffer of the frame: save socket buffer */ tx_skb->skb = skb; + tx_skb->fcs_len = fcs_len; /* Update TX ring: update buffer descriptors in reverse order * to avoid race condition @@ -2417,6 +2421,7 @@ static inline int macb_clear_csum(struct sk_buff *skb) return 0; } +/* Returns a negative errno, or the FCS bytes appended (0 or ETH_FCS_LEN). */ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev) { bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || @@ -2465,7 +2470,7 @@ add_fcs: skb_put_u8(*skb, (fcs >> 16) & 0xff); skb_put_u8(*skb, (fcs >> 24) & 0xff); - return 0; + return ETH_FCS_LEN; } static netdev_tx_t macb_start_xmit(struct sk_buff *skb, @@ -2478,6 +2483,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, netdev_tx_t ret = NETDEV_TX_OK; unsigned int hdrlen; unsigned long flags; + int fcs_len; bool is_lso; if (macb_clear_csum(skb)) { @@ -2485,7 +2491,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, return ret; } - if (macb_pad_and_fcs(&skb, netdev)) { + fcs_len = macb_pad_and_fcs(&skb, netdev); + if (fcs_len < 0) { dev_kfree_skb_any(skb); return ret; } @@ -2548,7 +2555,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, } /* Map socket buffer for DMA transfer */ - if (macb_tx_map(bp, queue, skb, hdrlen)) { + if (macb_tx_map(bp, queue, skb, hdrlen, fcs_len)) { dev_kfree_skb_any(skb); goto unlock; } @@ -4295,9 +4302,9 @@ static int macb_taprio_setup_replace(struct net_device *netdev, u64 total_on_time = 0, start_time_sec = 0, start_time = conf->base_time; u32 configured_queues = 0, speed = 0, start_time_nsec; struct macb_queue_enst_config *enst_queue; - struct tc_taprio_sched_entry *entry; + struct ethtool_link_ksettings kset = {}; struct macb *bp = netdev_priv(netdev); - struct ethtool_link_ksettings kset; + struct tc_taprio_sched_entry *entry; struct macb_queue *queue; u32 queue_mask; u8 queue_id; @@ -4324,12 +4331,12 @@ static int macb_taprio_setup_replace(struct net_device *netdev, } speed = kset.base.speed; - if (unlikely(speed <= 0)) { - netdev_err(netdev, "Invalid speed: %d\n", speed); + if (unlikely(speed == SPEED_UNKNOWN || !speed)) { + netdev_err(netdev, "Invalid speed %d, link-down?\n", speed); return -EINVAL; } - enst_queue = kcalloc(conf->num_entries, sizeof(*enst_queue), GFP_KERNEL); + enst_queue = kzalloc_objs(*enst_queue, conf->num_entries); if (unlikely(!enst_queue)) return -ENOMEM; @@ -5876,6 +5883,7 @@ static int macb_probe(struct platform_device *pdev) } spin_lock_init(&bp->lock); spin_lock_init(&bp->stats_lock); + spin_lock_init(&bp->tsu_clk_lock); /* setup capabilities */ macb_configure_caps(bp, macb_config); @@ -5971,8 +5979,11 @@ err_out_free_tieoff: macb_free_tieoff(bp); err_out_unregister_mdio: - mdiobus_unregister(bp->mii_bus); - mdiobus_free(bp->mii_bus); + if (bp->mii_bus) { + mdiobus_unregister(bp->mii_bus); + mdiobus_free(bp->mii_bus); + } + phylink_destroy(bp->phylink); err_out_phy_exit: phy_exit(bp->phy); @@ -6001,8 +6012,10 @@ static void macb_remove(struct platform_device *pdev) unregister_netdev(netdev); macb_free_tieoff(bp); phy_exit(bp->phy); - mdiobus_unregister(bp->mii_bus); - mdiobus_free(bp->mii_bus); + if (bp->mii_bus) { + mdiobus_unregister(bp->mii_bus); + mdiobus_free(bp->mii_bus); + } device_set_wakeup_enable(&bp->pdev->dev, 0); cancel_delayed_work_sync(&bp->tx_lpi_work); diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index e5195d7dac1d..6d9166389988 100644 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -334,6 +334,7 @@ void gem_ptp_init(struct net_device *netdev) bp->tsu_rate = bp->ptp_info->get_tsu_rate(bp); bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj(); gem_ptp_init_timer(bp); + gem_ptp_init_tsu(bp); bp->ptp_clock = ptp_clock_register(&bp->ptp_clock_info, &netdev->dev); if (IS_ERR(bp->ptp_clock)) { pr_err("ptp clock register failed: %ld\n", @@ -345,10 +346,6 @@ void gem_ptp_init(struct net_device *netdev) return; } - spin_lock_init(&bp->tsu_clk_lock); - - gem_ptp_init_tsu(bp); - dev_info(&bp->pdev->dev, "%s ptp clock registered.\n", GEM_PTP_TIMER_NAME); } diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 4a67947cfb9f..7a509a056990 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -137,6 +137,12 @@ struct enic_port_profile { u8 mac_addr[ETH_ALEN]; }; +enum enic_vf_link_state { + ENIC_VF_LINK_STATE_UNKNOWN, + ENIC_VF_LINK_STATE_DOWN, + ENIC_VF_LINK_STATE_UP, +}; + /* enic_rfs_fltr_node - rfs filter node in hash table * @@keys: IPv4 5 tuple * @flow_id: flow_id of clsf filter provided by kernel @@ -312,19 +318,25 @@ struct enic { unsigned int admin_msg_count; /* current depth of admin_msg_list */ void (*admin_rq_handler)(struct enic *enic, void *buf, unsigned int len); + /* The PF is authoritative for a V2 VF's carrier. Keep the last + * notification across an ordinary netdev close/open and serialize it + * against the open/stop carrier transition. + */ + spinlock_t vf_link_state_lock; + enum enic_vf_link_state vf_link_state; + bool vf_link_running; /* MBOX protocol state — mbox_lock serializes admin WQ sends */ struct mutex mbox_lock; u64 mbox_msg_num; - /* MBOX request-reply state. mbox_expected_reply is written and - * cleared by the process-context request helpers (capability/register/ - * unregister) and only read by the admin_msg_work receive handlers, so - * it is annotated with READ_ONCE()/WRITE_ONCE() rather than locked: - * only one request is in flight at a time (requesters run under RTNL or - * single-threaded probe/remove), so each request is serialized and its - * reply completes mbox_comp before the next request is issued. + /* MBOX request-reply state. Existing request callers allow only one + * request in flight. The state lock arbitrates reply acceptance against + * timeout invalidation, while mbox_comp publishes the accepted result to + * the requester. */ struct completion mbox_comp; + spinlock_t mbox_state_lock; /* protects expected reply state */ + u64 mbox_expected_msg_num; u8 mbox_expected_reply; bool mbox_initialized; diff --git a/drivers/net/ethernet/cisco/enic/enic_admin.c b/drivers/net/ethernet/cisco/enic/enic_admin.c index 7188f1b81c04..61c82b48044d 100644 --- a/drivers/net/ethernet/cisco/enic/enic_admin.c +++ b/drivers/net/ethernet/cisco/enic/enic_admin.c @@ -137,7 +137,7 @@ static void enic_admin_msg_enqueue(struct enic *enic, void *buf, { struct enic_admin_msg *msg; - msg = kmalloc(struct_size(msg, data, len), GFP_KERNEL); + msg = kmalloc_flex(*msg, data, len); if (!msg) return; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 0baef7a120ec..6b5824f45a14 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1800,6 +1800,8 @@ static int enic_open(struct net_device *netdev) enic_notify_timer_start(enic); enic_rfs_timer_start(enic); + if (enic_is_sriov_vf_v2(enic)) + enic_mbox_vf_link_state_set_running(enic, true); return 0; @@ -1853,7 +1855,10 @@ static int enic_stop(struct net_device *netdev) for (i = 0; i < enic->rq_count; i++) napi_disable(&enic->napi[i]); - netif_carrier_off(netdev); + if (enic_is_sriov_vf_v2(enic)) + enic_mbox_vf_link_state_set_running(enic, false); + else + netif_carrier_off(netdev); if (vnic_dev_get_intr_mode(enic->vdev) == VNIC_DEV_INTR_MODE_MSIX) for (i = 0; i < enic->wq_count; i++) napi_disable(&enic->napi[enic_cq_wq(enic, i)]); @@ -2201,9 +2206,10 @@ static void enic_admin_chan_reopen(struct enic *enic) { int err; - /* Install the MBOX receive handler and reset the sequence number - * before opening the channel, so the handler is in place before the - * admin interrupt is unmasked and no early completion is dropped. + /* Install the MBOX receive handler and clear pending reply state before + * opening the channel, so the handler is in place before the admin + * interrupt is unmasked and no early completion is dropped. Keep the + * sequence number monotonic across channel generations. */ enic_mbox_init(enic); @@ -2215,7 +2221,7 @@ static void enic_admin_chan_reopen(struct enic *enic) * registration over a dead channel. */ if (enic_is_sriov_vf_v2(enic)) - enic->vf_registered = false; + WRITE_ONCE(enic->vf_registered, false); err = enic_admin_channel_open(enic); if (err) { @@ -2271,6 +2277,8 @@ static void enic_reset(struct work_struct *work) enic_admin_channel_close(enic); enic_stop(enic->netdev); + if (enic_is_sriov_vf_v2(enic)) + enic_mbox_vf_link_state_reset(enic); enic_dev_soft_reset(enic); enic_reset_addr_lists(enic); @@ -2315,6 +2323,8 @@ static void enic_tx_hang_reset(struct work_struct *work) enic_dev_hang_notify(enic); enic_stop(enic->netdev); + if (enic_is_sriov_vf_v2(enic)) + enic_mbox_vf_link_state_reset(enic); enic_dev_hang_reset(enic); enic_reset_addr_lists(enic); @@ -2858,7 +2868,7 @@ enic_sriov_v2_enable(struct enic *enic, int num_vfs) return -EOPNOTSUPP; } - enic->vf_state = kcalloc(num_vfs, sizeof(*enic->vf_state), GFP_KERNEL); + enic->vf_state = kzalloc_objs(*enic->vf_state, num_vfs); if (!enic->vf_state) return -ENOMEM; @@ -2946,7 +2956,7 @@ enic_sriov_configure(struct pci_dev *pdev, int num_vfs) if (enic->vf_type == ENIC_VF_TYPE_V2) return enic_sriov_v2_enable(enic, num_vfs); - pp = kcalloc(num_vfs, sizeof(*pp), GFP_KERNEL); + pp = kzalloc_objs(*pp, num_vfs); if (!pp) return -ENOMEM; @@ -2971,7 +2981,7 @@ enic_sriov_configure(struct pci_dev *pdev, int num_vfs) return 0; } - pp = kzalloc_obj(*enic->pp, GFP_KERNEL); + pp = kzalloc_obj(*enic->pp); if (!pp) return -ENOMEM; @@ -3015,6 +3025,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) enic = netdev_priv(netdev); enic->netdev = netdev; enic->pdev = pdev; + spin_lock_init(&enic->vf_link_state_lock); /* Setup PCI resources */ @@ -3339,7 +3350,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_out_admin_close: if (enic_is_sriov_vf_v2(enic)) { - if (enic->vf_registered) { + if (READ_ONCE(enic->vf_registered)) { int unreg_err = enic_mbox_vf_unregister(enic); if (unreg_err) @@ -3392,7 +3403,7 @@ static void enic_remove(struct pci_dev *pdev) * touching a netdev that is being torn down. */ if (enic_is_sriov_vf_v2(enic)) { - if (enic->vf_registered) { + if (READ_ONCE(enic->vf_registered)) { int unreg_err = enic_mbox_vf_unregister(enic); if (unreg_err) diff --git a/drivers/net/ethernet/cisco/enic/enic_mbox.c b/drivers/net/ethernet/cisco/enic/enic_mbox.c index 2fb0f1e2ff50..5c93ca49552a 100644 --- a/drivers/net/ethernet/cisco/enic/enic_mbox.c +++ b/drivers/net/ethernet/cisco/enic/enic_mbox.c @@ -18,22 +18,25 @@ #define ENIC_MBOX_POLL_TIMEOUT_US 5000000 #define ENIC_MBOX_POLL_INTERVAL_US 100 -static void enic_mbox_fill_hdr(struct enic *enic, struct enic_mbox_hdr *hdr, - u8 msg_type, u16 dst_vnic_id, u16 msg_len) +static void enic_mbox_fill_hdr(struct enic_mbox_hdr *hdr, u8 msg_type, + u16 dst_vnic_id, u16 msg_len, u64 msg_num) { memset(hdr, 0, sizeof(*hdr)); hdr->dst_vnic_id = cpu_to_le16(dst_vnic_id); hdr->msg_type = msg_type; hdr->msg_len = cpu_to_le16(msg_len); - hdr->msg_num = cpu_to_le64(++enic->mbox_msg_num); + hdr->msg_num = cpu_to_le64(msg_num); } -int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id, - void *payload, u16 payload_len) +static int enic_mbox_send_msg_id(struct enic *enic, u8 msg_type, + u16 dst_vnic_id, void *payload, + u16 payload_len, u64 msg_num, bool reuse_msg_num, + u8 expected_reply) { size_t total_len = sizeof(struct enic_mbox_hdr) + payload_len; struct vnic_wq *wq = &enic->admin_wq; struct wq_enet_desc *desc; + bool reply_expected = false; unsigned long timeout; dma_addr_t dma_addr; u16 vlan_tag; @@ -68,7 +71,21 @@ int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id, goto unlock; } - enic_mbox_fill_hdr(enic, buf, msg_type, dst_vnic_id, total_len); + /* Replies reuse the initiating message number. Requests and + * notifications allocate a new one. + */ + if (!reuse_msg_num) + msg_num = ++enic->mbox_msg_num; + if (expected_reply) { + reinit_completion(&enic->mbox_comp); + spin_lock_bh(&enic->mbox_state_lock); + enic->mbox_expected_reply = expected_reply; + enic->mbox_expected_msg_num = msg_num; + spin_unlock_bh(&enic->mbox_state_lock); + reply_expected = true; + } + + enic_mbox_fill_hdr(buf, msg_type, dst_vnic_id, total_len, msg_num); if (payload_len) { void *dst = buf + sizeof(struct enic_mbox_hdr); @@ -139,18 +156,66 @@ int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id, "MBOX send msg_type %u dst %u vlan %u err %d\n", msg_type, dst_vnic_id, vlan_tag, err); unlock: + if (err && reply_expected) { + spin_lock_bh(&enic->mbox_state_lock); + if (enic->mbox_expected_reply == expected_reply && + enic->mbox_expected_msg_num == msg_num) { + enic->mbox_expected_reply = 0; + enic->mbox_expected_msg_num = 0; + } + spin_unlock_bh(&enic->mbox_state_lock); + } mutex_unlock(&enic->mbox_lock); return err; } +int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id, + void *payload, u16 payload_len) +{ + return enic_mbox_send_msg_id(enic, msg_type, dst_vnic_id, payload, + payload_len, 0, false, 0); +} + +static int enic_mbox_send_reply(struct enic *enic, u8 msg_type, + u16 dst_vnic_id, void *payload, u16 payload_len, + u64 msg_num) +{ + return enic_mbox_send_msg_id(enic, msg_type, dst_vnic_id, payload, + payload_len, msg_num, true, 0); +} + +static int enic_mbox_vf_send_request(struct enic *enic, u8 request_type, + u8 expected_reply, void *payload, + u16 payload_len) +{ + return enic_mbox_send_msg_id(enic, request_type, ENIC_MBOX_DST_PF, + payload, payload_len, 0, false, + expected_reply); +} + static int enic_mbox_wait_reply(struct enic *enic, unsigned long timeout_ms) { unsigned long left; + int err = 0; left = wait_for_completion_timeout(&enic->mbox_comp, msecs_to_jiffies(timeout_ms)); + if (left) + return 0; + + /* Invalidate a request that the handler has not already accepted. A + * delayed reply cannot match a later request because message numbers are + * monotonic across channel reopen. + */ + spin_lock_bh(&enic->mbox_state_lock); + if (enic->mbox_expected_reply) { + enic->mbox_expected_reply = 0; + enic->mbox_expected_msg_num = 0; + err = -ETIMEDOUT; + } + spin_unlock_bh(&enic->mbox_state_lock); - return left ? 0 : -ETIMEDOUT; + return err; } int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state) @@ -178,8 +243,8 @@ static int enic_mbox_pf_handle_capability(struct enic *enic, void *msg, reply.reply.ret_major = cpu_to_le16(0); reply.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1); - return enic_mbox_send_msg(enic, ENIC_MBOX_VF_CAPABILITY_REPLY, vf_id, - &reply, sizeof(reply)); + return enic_mbox_send_reply(enic, ENIC_MBOX_VF_CAPABILITY_REPLY, vf_id, + &reply, sizeof(reply), msg_num); } static int enic_mbox_pf_handle_register(struct enic *enic, void *msg, @@ -208,8 +273,8 @@ static int enic_mbox_pf_handle_register(struct enic *enic, void *msg, } reply.reply.ret_major = cpu_to_le16(0); - err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_REGISTER_REPLY, vf_id, - &reply, sizeof(reply)); + err = enic_mbox_send_reply(enic, ENIC_MBOX_VF_REGISTER_REPLY, vf_id, + &reply, sizeof(reply), msg_num); if (err) return err; @@ -253,8 +318,8 @@ static int enic_mbox_pf_handle_unregister(struct enic *enic, void *msg, enic->vf_state[vf_id].registered = false; reply.reply.ret_major = cpu_to_le16(0); - err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_UNREGISTER_REPLY, vf_id, - &reply, sizeof(reply)); + err = enic_mbox_send_reply(enic, ENIC_MBOX_VF_UNREGISTER_REPLY, vf_id, + &reply, sizeof(reply), msg_num); if (net_ratelimit()) netdev_info(enic->netdev, @@ -324,105 +389,116 @@ static void enic_mbox_pf_process_msg(struct enic *enic, hdr->msg_type, vf_id, err); } -static void enic_mbox_vf_handle_capability_reply(struct enic *enic, - void *payload) +static void enic_mbox_vf_handle_reply(struct enic *enic, u8 reply_type, + void *payload, u64 msg_num) { - struct enic_mbox_vf_capability_reply_msg *reply = payload; - - if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_CAPABILITY_REPLY) { + struct enic_mbox_generic_reply *reply = payload; + u16 ret_major = le16_to_cpu(reply->ret_major); + u64 expected_msg_num; + u8 expected_type; + + spin_lock_bh(&enic->mbox_state_lock); + expected_type = enic->mbox_expected_reply; + expected_msg_num = enic->mbox_expected_msg_num; + if (expected_type != reply_type || expected_msg_num != msg_num) { + spin_unlock_bh(&enic->mbox_state_lock); netdev_warn(enic->netdev, - "MBOX: stale capability reply (expected %u), drop\n", - READ_ONCE(enic->mbox_expected_reply)); + "MBOX: stale reply %u/%llu (expected %u/%llu), drop\n", + reply_type, (unsigned long long)msg_num, + expected_type, (unsigned long long)expected_msg_num); return; } - if (le16_to_cpu(reply->reply.ret_major) == 0) - enic->pf_cap_version = le32_to_cpu(reply->version); - else - netdev_warn(enic->netdev, - "MBOX: PF rejected capability request: %u/%u\n", - le16_to_cpu(reply->reply.ret_major), - le16_to_cpu(reply->reply.ret_minor)); - complete(&enic->mbox_comp); -} + if (!ret_major) { + switch (reply_type) { + case ENIC_MBOX_VF_CAPABILITY_REPLY: { + struct enic_mbox_vf_capability_reply_msg *cap = payload; -static void enic_mbox_vf_handle_register_reply(struct enic *enic, - void *payload) -{ - struct enic_mbox_vf_register_reply_msg *reply = payload; - - if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_REGISTER_REPLY) { - netdev_warn(enic->netdev, - "MBOX: stale register reply (expected %u), drop\n", - READ_ONCE(enic->mbox_expected_reply)); - return; - } - - if (le16_to_cpu(reply->reply.ret_major)) { - netdev_warn(enic->netdev, - "MBOX: VF register rejected by PF: %u/%u\n", - le16_to_cpu(reply->reply.ret_major), - le16_to_cpu(reply->reply.ret_minor)); - } else { - enic->vf_registered = true; + WRITE_ONCE(enic->pf_cap_version, + le32_to_cpu(cap->version)); + break; + } + case ENIC_MBOX_VF_REGISTER_REPLY: + WRITE_ONCE(enic->vf_registered, true); + break; + case ENIC_MBOX_VF_UNREGISTER_REPLY: + WRITE_ONCE(enic->vf_registered, false); + break; + } } + enic->mbox_expected_reply = 0; + enic->mbox_expected_msg_num = 0; complete(&enic->mbox_comp); -} + spin_unlock_bh(&enic->mbox_state_lock); -static void enic_mbox_vf_handle_unregister_reply(struct enic *enic, - void *payload) -{ - struct enic_mbox_vf_register_reply_msg *reply = payload; - - if (READ_ONCE(enic->mbox_expected_reply) != ENIC_MBOX_VF_UNREGISTER_REPLY) { + if (ret_major) netdev_warn(enic->netdev, - "MBOX: stale unregister reply (expected %u), drop\n", - READ_ONCE(enic->mbox_expected_reply)); - return; - } - - if (le16_to_cpu(reply->reply.ret_major)) { - netdev_warn(enic->netdev, - "MBOX: VF unregister rejected by PF: %u/%u\n", - le16_to_cpu(reply->reply.ret_major), - le16_to_cpu(reply->reply.ret_minor)); - } else { - enic->vf_registered = false; - } - complete(&enic->mbox_comp); + "MBOX: PF rejected reply type %u: %u/%u\n", + reply_type, ret_major, + le16_to_cpu(reply->ret_minor)); } -static void enic_mbox_vf_handle_link_state(struct enic *enic, void *payload) +static void enic_mbox_vf_handle_link_state(struct enic *enic, void *payload, + u64 msg_num) { struct enic_mbox_pf_link_state_notif_msg *notif = payload; struct enic_mbox_pf_link_state_ack_msg ack = {}; + u32 link_state = le32_to_cpu(notif->link_state); int err; - switch (le32_to_cpu(notif->link_state)) { + spin_lock_bh(&enic->vf_link_state_lock); + switch (link_state) { case ENIC_MBOX_LINK_STATE_ENABLE: - if (!netif_carrier_ok(enic->netdev)) + enic->vf_link_state = ENIC_VF_LINK_STATE_UP; + if (enic->vf_link_running && + !netif_carrier_ok(enic->netdev)) netif_carrier_on(enic->netdev); netdev_dbg(enic->netdev, "MBOX: link state -> UP\n"); break; case ENIC_MBOX_LINK_STATE_DISABLE: - if (netif_carrier_ok(enic->netdev)) + enic->vf_link_state = ENIC_VF_LINK_STATE_DOWN; + if (enic->vf_link_running && + netif_carrier_ok(enic->netdev)) netif_carrier_off(enic->netdev); netdev_dbg(enic->netdev, "MBOX: link state -> DOWN\n"); break; default: netdev_warn(enic->netdev, "MBOX: unknown link state %u\n", - le32_to_cpu(notif->link_state)); + link_state); ack.ack.ret_major = cpu_to_le16(ENIC_MBOX_ERR_GENERIC); break; } + spin_unlock_bh(&enic->vf_link_state_lock); - err = enic_mbox_send_msg(enic, ENIC_MBOX_PF_LINK_STATE_ACK, - ENIC_MBOX_DST_PF, &ack, sizeof(ack)); + err = enic_mbox_send_reply(enic, ENIC_MBOX_PF_LINK_STATE_ACK, + ENIC_MBOX_DST_PF, &ack, sizeof(ack), msg_num); if (err && net_ratelimit()) netdev_warn(enic->netdev, "MBOX: failed to send link state ACK: %d\n", err); } +void enic_mbox_vf_link_state_reset(struct enic *enic) +{ + spin_lock_bh(&enic->vf_link_state_lock); + enic->vf_link_state = ENIC_VF_LINK_STATE_UNKNOWN; + if (enic->vf_link_running && netif_carrier_ok(enic->netdev)) + netif_carrier_off(enic->netdev); + spin_unlock_bh(&enic->vf_link_state_lock); +} + +void enic_mbox_vf_link_state_set_running(struct enic *enic, bool running) +{ + spin_lock_bh(&enic->vf_link_state_lock); + enic->vf_link_running = running; + if (running && enic->vf_link_state == ENIC_VF_LINK_STATE_UP) { + if (!netif_carrier_ok(enic->netdev)) + netif_carrier_on(enic->netdev); + } else if (netif_carrier_ok(enic->netdev)) { + netif_carrier_off(enic->netdev); + } + spin_unlock_bh(&enic->vf_link_state_lock); +} + static bool enic_mbox_vf_payload_ok(struct enic *enic, u8 msg_type, u16 payload_len, size_t min_len) { @@ -439,6 +515,8 @@ static void enic_mbox_vf_process_msg(struct enic *enic, struct enic_mbox_hdr *hdr, void *payload, u16 payload_len) { + u64 msg_num = le64_to_cpu(hdr->msg_num); + switch (hdr->msg_type) { case ENIC_MBOX_VF_CAPABILITY_REPLY: { size_t exp = sizeof(struct enic_mbox_vf_capability_reply_msg); @@ -446,7 +524,7 @@ static void enic_mbox_vf_process_msg(struct enic *enic, if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type, payload_len, exp)) return; - enic_mbox_vf_handle_capability_reply(enic, payload); + enic_mbox_vf_handle_reply(enic, hdr->msg_type, payload, msg_num); break; } case ENIC_MBOX_VF_REGISTER_REPLY: { @@ -455,7 +533,7 @@ static void enic_mbox_vf_process_msg(struct enic *enic, if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type, payload_len, exp)) return; - enic_mbox_vf_handle_register_reply(enic, payload); + enic_mbox_vf_handle_reply(enic, hdr->msg_type, payload, msg_num); break; } case ENIC_MBOX_VF_UNREGISTER_REPLY: { @@ -464,7 +542,7 @@ static void enic_mbox_vf_process_msg(struct enic *enic, if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type, payload_len, exp)) return; - enic_mbox_vf_handle_unregister_reply(enic, payload); + enic_mbox_vf_handle_reply(enic, hdr->msg_type, payload, msg_num); break; } case ENIC_MBOX_PF_LINK_STATE_NOTIF: { @@ -473,7 +551,7 @@ static void enic_mbox_vf_process_msg(struct enic *enic, if (!enic_mbox_vf_payload_ok(enic, hdr->msg_type, payload_len, exp)) return; - enic_mbox_vf_handle_link_state(enic, payload); + enic_mbox_vf_handle_link_state(enic, payload, msg_num); break; } default: @@ -542,32 +620,31 @@ static void enic_mbox_recv_handler(struct enic *enic, void *buf, int enic_mbox_vf_capability_check(struct enic *enic) { struct enic_mbox_vf_capability_msg req = {}; + u32 version; int err; - enic->pf_cap_version = 0; - reinit_completion(&enic->mbox_comp); - WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_CAPABILITY_REPLY); + WRITE_ONCE(enic->pf_cap_version, 0); req.version = cpu_to_le32(ENIC_MBOX_CAP_VERSION_1); - err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_CAPABILITY_REQUEST, - ENIC_MBOX_DST_PF, &req, sizeof(req)); - if (err) { - WRITE_ONCE(enic->mbox_expected_reply, 0); + err = enic_mbox_vf_send_request(enic, + ENIC_MBOX_VF_CAPABILITY_REQUEST, + ENIC_MBOX_VF_CAPABILITY_REPLY, + &req, sizeof(req)); + if (err) return err; - } err = enic_mbox_wait_reply(enic, 3000); - WRITE_ONCE(enic->mbox_expected_reply, 0); + version = READ_ONCE(enic->pf_cap_version); if (err) { netdev_warn(enic->netdev, "MBOX: no capability reply from PF\n"); return err; } - if (enic->pf_cap_version < ENIC_MBOX_CAP_VERSION_1) { + if (version < ENIC_MBOX_CAP_VERSION_1) { netdev_warn(enic->netdev, "MBOX: PF rejected capability request or reported unsupported version %u\n", - enic->pf_cap_version); + version); return -EOPNOTSUPP; } @@ -576,28 +653,25 @@ int enic_mbox_vf_capability_check(struct enic *enic) int enic_mbox_vf_register(struct enic *enic) { + bool registered; int err; - enic->vf_registered = false; - reinit_completion(&enic->mbox_comp); - WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_REGISTER_REPLY); + WRITE_ONCE(enic->vf_registered, false); - err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_REGISTER_REQUEST, - ENIC_MBOX_DST_PF, NULL, 0); - if (err) { - WRITE_ONCE(enic->mbox_expected_reply, 0); + err = enic_mbox_vf_send_request(enic, ENIC_MBOX_VF_REGISTER_REQUEST, + ENIC_MBOX_VF_REGISTER_REPLY, NULL, 0); + if (err) return err; - } err = enic_mbox_wait_reply(enic, 3000); - WRITE_ONCE(enic->mbox_expected_reply, 0); + registered = READ_ONCE(enic->vf_registered); if (err) { netdev_warn(enic->netdev, "MBOX: VF registration with PF timed out\n"); return err; } - if (!enic->vf_registered) + if (!registered) return -ENODEV; return 0; @@ -605,43 +679,48 @@ int enic_mbox_vf_register(struct enic *enic) int enic_mbox_vf_unregister(struct enic *enic) { + bool registered; int err; - if (!enic->vf_registered) + if (!READ_ONCE(enic->vf_registered)) return 0; - reinit_completion(&enic->mbox_comp); - WRITE_ONCE(enic->mbox_expected_reply, ENIC_MBOX_VF_UNREGISTER_REPLY); - - err = enic_mbox_send_msg(enic, ENIC_MBOX_VF_UNREGISTER_REQUEST, - ENIC_MBOX_DST_PF, NULL, 0); - if (err) { - WRITE_ONCE(enic->mbox_expected_reply, 0); + err = enic_mbox_vf_send_request(enic, + ENIC_MBOX_VF_UNREGISTER_REQUEST, + ENIC_MBOX_VF_UNREGISTER_REPLY, + NULL, 0); + if (err) return err; - } err = enic_mbox_wait_reply(enic, 3000); - WRITE_ONCE(enic->mbox_expected_reply, 0); + registered = READ_ONCE(enic->vf_registered); if (err) return err; - if (enic->vf_registered) + if (registered) return -EACCES; return 0; } void enic_mbox_init(struct enic *enic) { - /* mbox_lock and mbox_comp must be initialized exactly once per + bool reinit = enic->mbox_initialized; + + /* MBOX locks and mbox_comp must be initialized exactly once per * device lifetime; the PF sriov_configure path can re-enter this * on each enable cycle where these primitives are already set up. */ - if (!enic->mbox_initialized) { + if (!reinit) { mutex_init(&enic->mbox_lock); init_completion(&enic->mbox_comp); + spin_lock_init(&enic->mbox_state_lock); + enic->mbox_msg_num = 0; enic->mbox_initialized = true; } else { reinit_completion(&enic->mbox_comp); } - enic->mbox_msg_num = 0; + spin_lock_bh(&enic->mbox_state_lock); + enic->mbox_expected_reply = 0; + enic->mbox_expected_msg_num = 0; + spin_unlock_bh(&enic->mbox_state_lock); enic->admin_rq_handler = enic_mbox_recv_handler; } diff --git a/drivers/net/ethernet/cisco/enic/enic_mbox.h b/drivers/net/ethernet/cisco/enic/enic_mbox.h index 15e30ee2b0ed..60409bad2f28 100644 --- a/drivers/net/ethernet/cisco/enic/enic_mbox.h +++ b/drivers/net/ethernet/cisco/enic/enic_mbox.h @@ -88,6 +88,8 @@ void enic_mbox_init(struct enic *enic); int enic_mbox_send_msg(struct enic *enic, u8 msg_type, u16 dst_vnic_id, void *payload, u16 payload_len); int enic_mbox_send_link_state(struct enic *enic, u16 vf_id, u32 link_state); +void enic_mbox_vf_link_state_reset(struct enic *enic); +void enic_mbox_vf_link_state_set_running(struct enic *enic, bool running); int enic_mbox_vf_capability_check(struct enic *enic); int enic_mbox_vf_register(struct enic *enic); int enic_mbox_vf_unregister(struct enic *enic); diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 4c762229ce42..f08de623e6f7 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -124,6 +124,7 @@ struct gemini_ethernet_port { unsigned int rx_coalesce_nsecs; struct sk_buff *rx_skb; unsigned int rx_frag_nr; + bool rx_dropping; unsigned int freeq_refill; struct gmac_txq txq[TX_QUEUE_NUM]; @@ -1439,7 +1440,8 @@ update_exit: return skb; } -static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) +static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget, + unsigned int *freeq_consumed) { struct gemini_ethernet_port *port = netdev_priv(netdev); unsigned short m = (1 << port->rxq_order) - 1; @@ -1447,9 +1449,12 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) void __iomem *ptr_reg = port->rxq_rwptr; unsigned int frag_nr = port->rx_frag_nr; struct sk_buff *skb = port->rx_skb; + unsigned int consumed = 0; unsigned int frame_len, frag_len; struct gmac_rxdesc *rx = NULL; struct gmac_queue_page *gpage; + unsigned int received = 0; + bool dropping = port->rx_dropping; union gmac_rxdesc_0 word0; union gmac_rxdesc_1 word1; union gmac_rxdesc_3 word3; @@ -1471,6 +1476,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) w = rw.bits.wptr; while (budget && w != r) { + page = NULL; rx = port->rxq_ring + r; word0 = rx->word0; word1 = rx->word1; @@ -1479,11 +1485,22 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) r++; r &= m; + consumed++; frag_len = word0.bits.buffer_size; frame_len = word1.bits.byte_count; page_offs = mapping & ~PAGE_MASK; + if (word3.bits32 & SOF_BIT) { + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; + frag_nr = 0; + } + dropping = false; + } + if (!mapping) { netdev_err(netdev, "rxq[%u]: HW BUG: zero DMA desc\n", r); @@ -1494,24 +1511,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); if (!gpage) { dev_err(geth->dev, "could not find mapping\n"); - port->stats.rx_dropped++; - if (skb) { - napi_free_frags(&port->napi); - skb = NULL; - frag_nr = 0; - } - continue; + goto err_drop; } page = gpage->page; if (word3.bits32 & SOF_BIT) { - if (skb) { - napi_free_frags(&port->napi); - port->stats.rx_dropped++; - skb = NULL; - frag_nr = 0; - } - skb = gmac_skb_if_good_frame(port, word0, frame_len); if (!skb) goto err_drop; @@ -1521,8 +1525,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) frag_nr = 0; } else if (!skb) { - put_page(page); - continue; + goto err_drop; } if (word3.bits32 & EOF_BIT) @@ -1545,9 +1548,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) napi_gro_frags(&port->napi); skb = NULL; frag_nr = 0; - --budget; } - continue; + goto next_desc; err_drop: if (skb) { @@ -1556,16 +1558,29 @@ err_drop: frag_nr = 0; } - if (mapping) + if (page) put_page(page); - port->stats.rx_dropped++; + if (!dropping) { + port->stats.rx_dropped++; + dropping = true; + } + +next_desc: + /* Final or single-descriptor fragment, advance things */ + if (word3.bits32 & EOF_BIT) { + budget--; + received++; + dropping = false; + } } port->rx_skb = skb; port->rx_frag_nr = frag_nr; + port->rx_dropping = dropping; + *freeq_consumed = consumed; writew(r, ptr_reg); - return budget; + return received; } static int gmac_napi_poll(struct napi_struct *napi, int budget) @@ -1573,26 +1588,27 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget) struct gemini_ethernet_port *port = netdev_priv(napi->dev); struct gemini_ethernet *geth = port->geth; unsigned int freeq_threshold; + unsigned int freeq_consumed; unsigned int received; freeq_threshold = 1 << (geth->freeq_order - 1); u64_stats_update_begin(&port->rx_stats_syncp); - received = gmac_rx(napi->dev, budget); - if (received < budget) { - napi_gro_flush(napi, false); - napi_complete_done(napi, received); - gmac_enable_rx_irq(napi->dev, 1); + received = gmac_rx(napi->dev, budget, &freeq_consumed); + if (received < budget) ++port->rx_napi_exits; - } - port->freeq_refill += (budget - received); + u64_stats_update_end(&port->rx_stats_syncp); + + port->freeq_refill += freeq_consumed; if (port->freeq_refill > freeq_threshold) { port->freeq_refill -= freeq_threshold; geth_fill_freeq(geth, true); } - u64_stats_update_end(&port->rx_stats_syncp); + if (received < budget && napi_complete_done(napi, received)) + gmac_enable_rx_irq(napi->dev, 1); + return received; } @@ -1893,6 +1909,7 @@ static int gmac_stop(struct net_device *netdev) napi_disable(&port->napi); port->rx_skb = NULL; port->rx_frag_nr = 0; + port->rx_dropping = false; gmac_enable_irq(netdev, 0); gmac_cleanup_rxq(netdev); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 79d4a77f72bd..8cabce0eb2ab 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -616,7 +616,7 @@ static int dpaa2_switch_lag_fdb_add(struct dpaa2_switch_lag *lag, goto out; } - a = kzalloc(sizeof(*a), GFP_KERNEL); + a = kzalloc_obj(*a); if (!a) { err = -ENOMEM; goto out; @@ -4131,8 +4131,7 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev) goto err_free_fdbs; } - ethsw->lags = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->lags), - GFP_KERNEL); + ethsw->lags = kzalloc_objs(*ethsw->lags, ethsw->sw_attr.num_ifs); if (!ethsw->lags) { err = -ENOMEM; goto err_free_filter; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c index 2784127327e6..6e67a6c9578e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c @@ -128,6 +128,7 @@ enum hinic_mbox_tx_status { #define SEQ_ID_START_VAL 0 #define SEQ_ID_MAX_VAL 42 +#define MBOX_LAST_SEG_MAX_LEN (MBOX_MAX_BUF_SZ - SEQ_ID_MAX_VAL * MBOX_SEG_LEN) #define NO_DMA_ATTRIBUTE_VAL 0 @@ -372,7 +373,8 @@ recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, static bool check_mbox_seq_id_and_seg_len(struct hinic_recv_mbox *recv_mbox, u8 seq_id, u8 seg_len) { - if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN) + if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN || + (seq_id == SEQ_ID_MAX_VAL && seg_len > MBOX_LAST_SEG_MAX_LEN)) return false; if (seq_id == 0) { diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index ef1ce106f81b..04633103e3e6 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -1051,14 +1051,13 @@ int ice_init_hw(struct ice_hw *hw) hw->evb_veb = true; - /* init xarray for identifying scheduling nodes uniquely */ - xa_init_flags(&hw->port_info->sched_node_ids, XA_FLAGS_ALLOC); + xa_init_flags(&hw->sched_node_ids, XA_FLAGS_ALLOC); /* Query the allocated resources for Tx scheduler */ status = ice_sched_query_res_alloc(hw); if (status) { ice_debug(hw, ICE_DBG_SCHED, "Failed to get scheduler allocated resources\n"); - goto err_unroll_alloc; + goto err_unroll_xarray; } ice_sched_get_psm_clk_freq(hw); @@ -1146,6 +1145,8 @@ err_unroll_fltr_mgmt_struct: ice_cleanup_fltr_mgmt_struct(hw); err_unroll_sched: ice_sched_cleanup_all(hw); +err_unroll_xarray: + xa_destroy(&hw->sched_node_ids); err_unroll_alloc: devm_kfree(ice_hw_to_dev(hw), hw->port_info); err_unroll_cqinit: @@ -1186,6 +1187,8 @@ void ice_deinit_hw(struct ice_hw *hw) /* Clear VSI contexts if not already cleared */ ice_clear_all_vsi_ctx(hw); + + xa_destroy(&hw->sched_node_ids); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c index fff0c1afdb41..ffa18d86729a 100644 --- a/drivers/net/ethernet/intel/ice/ice_sched.c +++ b/drivers/net/ethernet/intel/ice/ice_sched.c @@ -371,7 +371,7 @@ void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node) devm_kfree(ice_hw_to_dev(hw), node->children); kfree(node->name); - xa_erase(&pi->sched_node_ids, node->id); + xa_erase(&hw->sched_node_ids, node->id); devm_kfree(ice_hw_to_dev(hw), node); } @@ -977,7 +977,7 @@ ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node, if (!new_node->name) return -ENOMEM; - status = xa_alloc(&pi->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX), + status = xa_alloc(&hw->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX), GFP_KERNEL); if (status) { ice_debug(hw, ICE_DBG_SCHED, "xa_alloc failed for sched node status =%d\n", diff --git a/drivers/net/ethernet/intel/ice/ice_trace.h b/drivers/net/ethernet/intel/ice/ice_trace.h index 4f35ef8d6b29..7568c917cdbe 100644 --- a/drivers/net/ethernet/intel/ice/ice_trace.h +++ b/drivers/net/ethernet/intel/ice/ice_trace.h @@ -63,23 +63,33 @@ DECLARE_EVENT_CLASS(ice_rx_dim_template, TP_PROTO(struct ice_q_vector *q_vector, struct dim *dim), TP_ARGS(q_vector, dim), - TP_STRUCT__entry(__field(struct ice_q_vector *, q_vector) - __field(struct dim *, dim) + TP_STRUCT__entry(__field(u16, q_index) + __field(u8, state) + __field(u8, profile_ix) + __field(u8, tune_state) + __field(u8, steps_right) + __field(u8, steps_left) + __field(u8, tired) __string(devname, q_vector->rx.rx_ring->netdev->name)), - TP_fast_assign(__entry->q_vector = q_vector; - __entry->dim = dim; + TP_fast_assign(__entry->q_index = q_vector->rx.rx_ring->q_index; + __entry->state = dim->state; + __entry->profile_ix = dim->profile_ix; + __entry->tune_state = dim->tune_state; + __entry->steps_right = dim->steps_right; + __entry->steps_left = dim->steps_left; + __entry->tired = dim->tired; __assign_str(devname);), TP_printk("netdev: %s Rx-Q: %d dim-state: %d dim-profile: %d dim-tune: %d dim-st-right: %d dim-st-left: %d dim-tired: %d", __get_str(devname), - __entry->q_vector->rx.rx_ring->q_index, - __entry->dim->state, - __entry->dim->profile_ix, - __entry->dim->tune_state, - __entry->dim->steps_right, - __entry->dim->steps_left, - __entry->dim->tired) + __entry->q_index, + __entry->state, + __entry->profile_ix, + __entry->tune_state, + __entry->steps_right, + __entry->steps_left, + __entry->tired) ); DEFINE_EVENT(ice_rx_dim_template, ice_rx_dim_work, @@ -90,23 +100,33 @@ DEFINE_EVENT(ice_rx_dim_template, ice_rx_dim_work, DECLARE_EVENT_CLASS(ice_tx_dim_template, TP_PROTO(struct ice_q_vector *q_vector, struct dim *dim), TP_ARGS(q_vector, dim), - TP_STRUCT__entry(__field(struct ice_q_vector *, q_vector) - __field(struct dim *, dim) + TP_STRUCT__entry(__field(u16, q_index) + __field(u8, state) + __field(u8, profile_ix) + __field(u8, tune_state) + __field(u8, steps_right) + __field(u8, steps_left) + __field(u8, tired) __string(devname, q_vector->tx.tx_ring->netdev->name)), - TP_fast_assign(__entry->q_vector = q_vector; - __entry->dim = dim; + TP_fast_assign(__entry->q_index = q_vector->tx.tx_ring->q_index; + __entry->state = dim->state; + __entry->profile_ix = dim->profile_ix; + __entry->tune_state = dim->tune_state; + __entry->steps_right = dim->steps_right; + __entry->steps_left = dim->steps_left; + __entry->tired = dim->tired; __assign_str(devname);), TP_printk("netdev: %s Tx-Q: %d dim-state: %d dim-profile: %d dim-tune: %d dim-st-right: %d dim-st-left: %d dim-tired: %d", __get_str(devname), - __entry->q_vector->tx.tx_ring->q_index, - __entry->dim->state, - __entry->dim->profile_ix, - __entry->dim->tune_state, - __entry->dim->steps_right, - __entry->dim->steps_left, - __entry->dim->tired) + __entry->q_index, + __entry->state, + __entry->profile_ix, + __entry->tune_state, + __entry->steps_right, + __entry->steps_left, + __entry->tired) ); DEFINE_EVENT(ice_tx_dim_template, ice_tx_dim_work, diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h index d9a5c1aae7c2..cf147a212707 100644 --- a/drivers/net/ethernet/intel/ice/ice_type.h +++ b/drivers/net/ethernet/intel/ice/ice_type.h @@ -765,7 +765,6 @@ struct ice_port_info { /* List contain profile ID(s) and other params per layer */ struct list_head rl_prof_list[ICE_AQC_TOPO_MAX_LEVEL_NUM]; struct ice_qos_cfg qos_cfg; - struct xarray sched_node_ids; u8 is_vf:1; u8 is_custom_tx_enabled:1; }; @@ -930,6 +929,7 @@ struct ice_hw { u8 sw_entry_point_layer; u16 max_children[ICE_AQC_TOPO_MAX_LEVEL_NUM]; struct list_head agg_list; /* lists all aggregator */ + struct xarray sched_node_ids; struct ice_vsi_ctx *vsi_ctx[ICE_MAX_VSI]; u8 evb_veb; /* true for VEB, false for VEPA */ diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c index 9840580fbe51..129bccaa6baa 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_main.c +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c @@ -106,6 +106,11 @@ static int idpf_dev_init(struct idpf_adapter *adapter, */ static void idpf_decfg_device(struct idpf_adapter *adapter) { + struct pci_dev *pdev = adapter->pdev; + + if (pcie_ptm_enabled(pdev)) + pci_disable_ptm(pdev); + libie_pci_unmap_all_mmio_regions(&adapter->ctlq_ctx.mmio_info); } diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c index 24b91be25676..4311ffa30bb1 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c @@ -3299,6 +3299,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb, struct libeth_rx_pt decoded) { u16 rsc_segments, rsc_seg_len; + u16 l3_start = 0; bool ipv4, ipv6; int len; @@ -3321,7 +3322,10 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb, NAPI_GRO_CB(skb)->count = rsc_segments; skb_shinfo(skb)->gso_size = rsc_seg_len; - skb_reset_network_header(skb); + if (unlikely(eth_type_vlan(skb->protocol))) + l3_start = VLAN_HLEN; + + skb_set_network_header(skb, l3_start); if (ipv4) { struct iphdr *ipv4h = ip_hdr(skb); @@ -3329,7 +3333,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb, skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; /* Reset and set transport header offset in skb */ - skb_set_transport_header(skb, sizeof(struct iphdr)); + skb_set_transport_header(skb, l3_start + sizeof(struct iphdr)); len = skb->len - skb_transport_offset(skb); /* Compute the TCP pseudo header checksum*/ @@ -3339,7 +3343,7 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb, struct ipv6hdr *ipv6h = ipv6_hdr(skb); skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; - skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + skb_set_transport_header(skb, l3_start + sizeof(struct ipv6hdr)); len = skb->len - skb_transport_offset(skb); tcp_hdr(skb)->check = ~tcp_v6_check(len, &ipv6h->saddr, &ipv6h->daddr, 0); @@ -4146,6 +4150,26 @@ static void idpf_vport_intr_ena_irq_all(struct idpf_vport *vport, } /** + * idpf_vport_intr_dis_dim_all - Disable DIM work for all q_vectors + * @rsrc: pointer to queue and vector resources + * + * The DIM works are embedded in the q_vector array that + * idpf_vport_intr_rel() frees, and the poll arms them after + * napi_complete_done() has already cleared NAPI_STATE_SCHED. Disable + * rather than just cancel, so that a poll tail still running past + * napi_disable() cannot queue them again behind the drain. + */ +static void idpf_vport_intr_dis_dim_all(struct idpf_q_vec_rsrc *rsrc) +{ + for (u16 v_idx = 0; v_idx < rsrc->num_q_vectors; v_idx++) { + struct idpf_q_vector *q_vector = &rsrc->q_vectors[v_idx]; + + disable_work_sync(&q_vector->tx_dim.work); + disable_work_sync(&q_vector->rx_dim.work); + } +} + +/** * idpf_vport_intr_deinit - Release all vector associations for the vport * @vport: main vport structure * @rsrc: pointer to queue and vector resources @@ -4155,6 +4179,7 @@ void idpf_vport_intr_deinit(struct idpf_vport *vport, { idpf_vport_intr_dis_irq_all(rsrc); idpf_vport_intr_napi_dis_all(rsrc); + idpf_vport_intr_dis_dim_all(rsrc); idpf_vport_intr_napi_del_all(rsrc); idpf_vport_intr_rel_irq(vport, rsrc); } @@ -4235,7 +4260,6 @@ static void idpf_vport_intr_napi_ena_all(struct idpf_q_vec_rsrc *rsrc) for (u16 q_idx = 0; q_idx < rsrc->num_q_vectors; q_idx++) { struct idpf_q_vector *q_vector = &rsrc->q_vectors[q_idx]; - idpf_init_dim(q_vector); napi_enable(&q_vector->napi); } } @@ -4578,6 +4602,8 @@ int idpf_vport_intr_alloc(struct idpf_vport *vport, q_coal = &user_config->q_coalesce[v_idx]; q_vector->vport = vport; + idpf_init_dim(q_vector); + q_vector->tx_itr_value = q_coal->tx_coalesce_usecs; q_vector->tx_intr_mode = q_coal->tx_intr_mode; q_vector->tx_itr_idx = VIRTCHNL2_ITR_IDX_1; diff --git a/drivers/net/ethernet/intel/libie/controlq.c b/drivers/net/ethernet/intel/libie/controlq.c index 45a49eba6a82..6214fc036ce5 100644 --- a/drivers/net/ethernet/intel/libie/controlq.c +++ b/drivers/net/ethernet/intel/libie/controlq.c @@ -157,8 +157,7 @@ static void libie_ctlq_free_tx_msgs(struct libie_ctlq_info *ctlq, */ static int libie_ctlq_alloc_tx_msgs(struct libie_ctlq_info *ctlq) { - ctlq->tx_msg = kvzalloc_objs(*ctlq->tx_msg, ctlq->ring_len, - GFP_KERNEL); + ctlq->tx_msg = kvzalloc_objs(*ctlq->tx_msg, ctlq->ring_len); if (!ctlq->tx_msg) return -ENOMEM; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 3070700b952b..2b67671a2bd8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -3647,7 +3647,7 @@ static int npc_defrag_add_2_show_list(struct rvu *rvu, u16 old_midx, { struct npc_defrag_show_node *node; - node = kcalloc(1, sizeof(*node), GFP_KERNEL); + node = kzalloc_objs(*node, 1); if (!node) return -ENOMEM; @@ -4082,7 +4082,7 @@ int npc_cn20k_defrag(struct rvu *rvu) INIT_LIST_HEAD(&x4lh); INIT_LIST_HEAD(&x2lh); - node = kcalloc(npc_priv->num_subbanks, sizeof(*node), GFP_KERNEL); + node = kzalloc_objs(*node, npc_priv->num_subbanks); if (!node) return -ENOMEM; @@ -4711,7 +4711,7 @@ static int npc_priv_init(struct rvu *rvu) return -EINVAL; } - npc_priv = kcalloc(1, sizeof(*npc_priv), GFP_KERNEL); + npc_priv = kzalloc_objs(*npc_priv, 1); if (!npc_priv) return -ENOMEM; @@ -4729,8 +4729,7 @@ static int npc_priv_init(struct rvu *rvu) num_banks, bank_depth, num_subbanks, subbank_depth, npc_kw_name[npc_priv->kw]); - npc_priv->sb = kcalloc(num_subbanks, sizeof(struct npc_subbank), - GFP_KERNEL); + npc_priv->sb = kzalloc_objs(struct npc_subbank, num_subbanks); if (!npc_priv->sb) goto fail1; @@ -4757,9 +4756,7 @@ static int npc_priv_init(struct rvu *rvu) /* Get number of pcifuncs in the system */ npc_priv->pf_cnt = npc_pcifunc_map_create(rvu); - npc_priv->xa_pf2idx_map = kcalloc(npc_priv->pf_cnt, - sizeof(struct xarray), - GFP_KERNEL); + npc_priv->xa_pf2idx_map = kzalloc_objs(struct xarray, npc_priv->pf_cnt); if (!npc_priv->xa_pf2idx_map) { ret = -ENOMEM; goto fail3; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index a07e0b3d8d00..211c10aa5880 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -1417,6 +1417,16 @@ static int mcs_x2p_calibration(struct mcs *mcs) int i, err = 0; u64 val; + /* Clear any stale calibration state left by firmware/bootloader. + * Some firmware versions may leave MCSX_MIL_GLOBAL bit 5 set, + * preventing the hardware from detecting the rising edge needed to + * trigger X2P calibration. + */ + val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL); + val &= ~BIT_ULL(5); + mcs_reg_write(mcs, MCSX_MIL_GLOBAL, val); + usleep_range(100, 200); + /* set X2P calibration */ val = mcs_reg_read(mcs, MCSX_MIL_GLOBAL); val |= BIT_ULL(5); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 74c041ab5280..937b085582b5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -3468,6 +3468,8 @@ err: return ret; } +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 + static int rvu_enable_sriov(struct rvu *rvu) { struct pci_dev *pdev = rvu->pdev; @@ -3486,24 +3488,27 @@ static int rvu_enable_sriov(struct rvu *rvu) return 0; pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &rvu->vf_devid); - chans = rvu_get_num_lbk_chans(); - if (chans < 0) - return chans; - vfs = pci_sriov_get_totalvfs(pdev); - - /* Limit VFs in case we have more VFs than LBK channels available. */ - if (vfs > chans) - vfs = chans; - if (!vfs) return 0; - /* LBK channel number 63 is used for switching packets between - * CGX mapped VFs. Hence limit LBK pairs till 62 only. - */ - if (vfs > 62) - vfs = 62; + if (rvu->vf_devid == PCI_DEVID_OCTEONTX2_RVU_AFVF) { + chans = rvu_get_num_lbk_chans(); + if (chans < 0) + return chans; + + /* The last LBK channel is reserved for switching packets between + * CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF + * count to available LBK channels minus 2. + */ + vfs = min(vfs, chans - 2); + + if (vfs <= 0) { + dev_warn(&pdev->dev, + "Skipping SRIOV enablement, not enough LBK channels available\n"); + return 0; + } + } /* Save VFs number for reference in VF interrupts handlers. * Since interrupts might start arriving during SRIOV enablement diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 22ee99676879..904374baae6f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -829,19 +829,25 @@ static int rvu_dbg_rvu_pf_cgx_map_display(struct seq_file *filp, void *unused) int pf, domain, blkid; u8 cgx_id, lmac_id; u16 pcifunc; + u8 start; - domain = 2; + domain = pci_domain_nr(rvu->pdev->bus); mac_ops = get_mac_ops(rvu_first_cgx_pdata(rvu)); /* There can be no CGX devices at all */ if (!mac_ops) return 0; seq_printf(filp, "PCI dev\t\tRVU PF Func\tNIX block\t%s\tLMAC\tCHAN\n", mac_ops->name); + + /* All the PF devices are on contiguous PCI bus numbers, but the PF0(AF) + * may not start from 1 always. Hence get domain and bus from PCI device. + */ + start = rvu->pdev->bus->number; for (pf = 0; pf < rvu->hw->total_pfs; pf++) { if (!is_pf_cgxmapped(rvu, pf)) continue; - pdev = pci_get_domain_bus_and_slot(domain, pf + 1, 0); + pdev = pci_get_domain_bus_and_slot(domain, pf + start, 0); if (!pdev) continue; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 60922944675b..c34f8d86cc8a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -2957,10 +2957,9 @@ static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam, } } - /* Disable the entry */ - npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); - if (!cn20k_dft_rl) { + /* Disable the entry */ + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; /* Free the entry in bitmap */ npc_mcam_clear_bit(mcam, index); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c index 69c0911e28e9..f160b1618efa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/qos.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/qos.c @@ -235,13 +235,63 @@ static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf, return rc; } +static int otx2_qos_reset_schq_topology(struct otx2_nic *pfvf, u16 lvl, + u16 schq) +{ + struct mbox *mbox = &pfvf->mbox; + struct nix_txschq_config *cfg; + int rc; + + if (lvl < NIX_TXSCH_LVL_TL4 || lvl >= NIX_TXSCH_LVL_TL1) + return 0; + + mutex_lock(&mbox->lock); + + cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox); + if (!cfg) { + mutex_unlock(&mbox->lock); + return -ENOMEM; + } + + cfg->lvl = lvl; + cfg->num_regs = 1; + + if (lvl == NIX_TXSCH_LVL_TL4) + cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(schq); + else if (lvl == NIX_TXSCH_LVL_TL3) + cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(schq); + else if (lvl == NIX_TXSCH_LVL_TL2) + cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(schq); + + cfg->regval[0] = 0; + + rc = otx2_sync_mbox_msg(mbox); + + mutex_unlock(&mbox->lock); + + return rc; +} + +static void otx2_qos_free_hw_schq(struct otx2_nic *pfvf, u16 lvl, u16 schq) +{ + int err; + + err = otx2_qos_reset_schq_topology(pfvf, lvl, schq); + if (err) + netdev_warn(pfvf->netdev, + "QoS: failed to reset topology for schq %u at level %u: %d\n", + schq, lvl, err); + + otx2_txschq_free_one(pfvf, lvl, schq); +} + static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, struct otx2_qos_node *parent) { struct otx2_qos_node *node; list_for_each_entry_reverse(node, &parent->child_schq_list, list) - otx2_txschq_free_one(pfvf, node->level, node->schq); + otx2_qos_free_hw_schq(pfvf, node->level, node->schq); } static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, @@ -252,7 +302,7 @@ static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, list_for_each_entry_safe(node, tmp, &parent->child_list, list) { otx2_qos_free_hw_node(pfvf, node); otx2_qos_free_hw_node_schq(pfvf, node); - otx2_txschq_free_one(pfvf, node->level, node->schq); + otx2_qos_free_hw_schq(pfvf, node->level, node->schq); } } @@ -266,7 +316,7 @@ static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf, otx2_qos_free_hw_node_schq(pfvf, node); /* free node hw mappings */ - otx2_txschq_free_one(pfvf, node->level, node->schq); + otx2_qos_free_hw_schq(pfvf, node->level, node->schq); mutex_unlock(&pfvf->qos.qos_lock); } @@ -913,7 +963,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { for (idx = 0; idx < cfg->schq[lvl]; idx++) { schq = cfg->schq_list[lvl][idx]; - otx2_txschq_free_one(pfvf, lvl, schq); + otx2_qos_free_hw_schq(pfvf, lvl, schq); } } @@ -921,7 +971,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { if (cfg->schq_index_used[lvl][idx]) { schq = cfg->schq_contig_list[lvl][idx]; - otx2_txschq_free_one(pfvf, lvl, schq); + otx2_qos_free_hw_schq(pfvf, lvl, schq); } } } diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index be3bd025c41a..fd7a49ae88d0 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4828,7 +4828,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) phy_interface_t phy_mode; struct phylink *phylink; struct mtk_mac *mac; - int id, err; + int id, err, i; int txqs = 1; u32 val; @@ -4907,8 +4907,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) mac->phylink_config.type = PHYLINK_NETDEV; mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; - mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD | - MAC_2500FD; + /* LPI above 1 Gbps is not supported */ + mac->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD; mac->phylink_config.lpi_timer_default = 1000; /* MT7623 gmac0 is now missing its speed-specific PLL configuration @@ -4966,6 +4966,18 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) __set_bit(PHY_INTERFACE_MODE_INTERNAL, mac->phylink_config.supported_interfaces); + /* LPI wake-up timing is only verified on MTK_GMAC_EEE SoCs */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC_EEE)) { + phy_interface_copy(mac->phylink_config.lpi_interfaces, + mac->phylink_config.supported_interfaces); + __clear_bit(PHY_INTERFACE_MODE_2500BASEX, + mac->phylink_config.lpi_interfaces); + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) + if (mtk_interface_mode_is_xgmii(eth, i)) + __clear_bit(i, + mac->phylink_config.lpi_interfaces); + } + phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, mac_ops); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 0168e2fbc619..88a9b3b23bea 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -994,6 +994,7 @@ enum mkt_eth_capabilities { MTK_U3_COPHY_V2_BIT, MTK_SRAM_BIT, MTK_36BIT_DMA_BIT, + MTK_GMAC_EEE_BIT, /* MUX BITS*/ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, @@ -1034,6 +1035,7 @@ enum mkt_eth_capabilities { #define MTK_U3_COPHY_V2 BIT_ULL(MTK_U3_COPHY_V2_BIT) #define MTK_SRAM BIT_ULL(MTK_SRAM_BIT) #define MTK_36BIT_DMA BIT_ULL(MTK_36BIT_DMA_BIT) +#define MTK_GMAC_EEE BIT_ULL(MTK_GMAC_EEE_BIT) #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ BIT_ULL(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) @@ -1117,7 +1119,7 @@ enum mkt_eth_capabilities { #define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \ - MTK_RSTCTRL_PPE1 | MTK_SRAM) + MTK_RSTCTRL_PPE1 | MTK_SRAM | MTK_GMAC_EEE) #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c index 6049ccf475bc..a4c096a4fed2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c @@ -557,6 +557,7 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy) u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {}; int sz = MLX5_ST_SZ_BYTES(pplm_reg); u16 fec_policy_auto = 0; + bool fec_set = false; int err; int i; @@ -569,9 +570,6 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy) if (fec_policy >= (1 << MLX5E_FEC_LLRS_272_257_1) && !fec_50g_per_lane) return -EOPNOTSUPP; - if (fec_policy && !mlx5e_fec_in_caps(dev, fec_policy)) - return -EOPNOTSUPP; - MLX5_SET(pplm_reg, in, local_port, 1); err = mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 0); if (err) @@ -591,12 +589,17 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy) mlx5e_get_fec_cap_field(out, &fec_caps, i); /* policy supported for link speed */ - if (fec_caps & conf_fec) + if (fec_caps & conf_fec) { mlx5e_fec_admin_field(out, &conf_fec, 1, i); - else - /* set FEC to auto*/ + fec_set = true; + } else { + /* set FEC to auto */ mlx5e_fec_admin_field(out, &fec_policy_auto, 1, i); + } } + if (fec_policy && !fec_set) + return -EOPNOTSUPP; + return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h index fa2283dd383b..53dbdf77bcce 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h @@ -66,4 +66,8 @@ enum { MLX5E_FEC_LLRS_272_257_1 = 9, }; +#define MLX5E_FEC_RS_MASK (BIT(MLX5E_FEC_RS_528_514) | \ + BIT(MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD) | \ + BIT(MLX5E_FEC_RS_544_514)) + #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c index 89490f687a9c..93c62d3f3e5b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c @@ -311,12 +311,15 @@ err_alloc: static void sample_restore_put(struct mlx5e_tc_psample *tc_psample, struct mlx5e_sample_restore *restore) { + bool last; + mutex_lock(&tc_psample->restore_lock); - if (--restore->count == 0) + last = --restore->count == 0; + if (last) hash_del(&restore->hlist); mutex_unlock(&tc_psample->restore_lock); - if (!restore->count) { + if (last) { mlx5_del_flow_rules(restore->rule); mlx5_modify_header_dealloc(tc_psample->esw->dev, restore->modify_hdr); kfree(restore); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c index 4f984f6a2cb9..55ec6387ab28 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c @@ -3,6 +3,7 @@ #include "rx.h" #include "en/xdp.h" +#include <linux/bitmap.h> #include <net/xdp_sock_drv.h> #include <linux/filter.h> @@ -156,6 +157,7 @@ err_reuse_batch: xsk_buff_free(xsk_buffs[batch]); err: + bitmap_fill(wi->skip_release_bitmap, rq->mpwqe.pages_per_wqe); rq->stats->buff_alloc_err++; return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c index 00e706e1ede1..741f75b5bfec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c @@ -148,7 +148,7 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, if (err) return err; - if (ets->tc_tx_bw[i] < MLX5E_MAX_BW_ALLOC && + if (priv->dcbx.tc_tsa[i] == IEEE_8021QAZ_TSA_ETS && tc_group[i] == (MLX5E_LOWEST_PRIO_GROUP + 1)) is_zero_bw_ets_tc = true; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 112926d07634..3ed59ced0407 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1002,9 +1002,9 @@ static u32 pplm2ethtool_fec(u_long fec_mode, unsigned long size) return 0; } -#define MLX5E_ADVERTISE_SUPPORTED_FEC(mlx5_fec, ethtool_fec) \ +#define MLX5E_ADVERTISE_SUPPORTED_FEC(fec_mask, ethtool_fec) \ do { \ - if (mlx5e_fec_in_caps(dev, 1 << (mlx5_fec))) \ + if (mlx5e_fec_in_caps(dev, fec_mask)) \ __set_bit(ethtool_fec, \ link_ksettings->link_modes.supported);\ } while (0) @@ -1013,6 +1013,7 @@ static const u32 pplm_fec_2_ethtool_linkmodes[] = { [MLX5E_FEC_NOFEC] = ETHTOOL_LINK_MODE_FEC_NONE_BIT, [MLX5E_FEC_FIRECODE] = ETHTOOL_LINK_MODE_FEC_BASER_BIT, [MLX5E_FEC_RS_528_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, + [MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_RS_544_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_LLRS_272_257_1] = ETHTOOL_LINK_MODE_FEC_LLRS_BIT, }; @@ -1029,13 +1030,13 @@ static int get_fec_supported_advertised(struct mlx5_core_dev *dev, if (err) return (err == -EOPNOTSUPP) ? 0 : err; - MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_NOFEC, + MLX5E_ADVERTISE_SUPPORTED_FEC(BIT(MLX5E_FEC_NOFEC), ETHTOOL_LINK_MODE_FEC_NONE_BIT); - MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_FIRECODE, + MLX5E_ADVERTISE_SUPPORTED_FEC(BIT(MLX5E_FEC_FIRECODE), ETHTOOL_LINK_MODE_FEC_BASER_BIT); - MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_RS_528_514, + MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_RS_MASK, ETHTOOL_LINK_MODE_FEC_RS_BIT); - MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_LLRS_272_257_1, + MLX5E_ADVERTISE_SUPPORTED_FEC(BIT(MLX5E_FEC_LLRS_272_257_1), ETHTOOL_LINK_MODE_FEC_LLRS_BIT); active_fec_long = active_fec; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index ce765692fd19..88a170e40bd9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -56,7 +56,6 @@ #include "lib/vxlan.h" #define CREATE_TRACE_POINTS #include "diag/en_rep_tracepoint.h" -#include "diag/reporter_vnic.h" #include "en_accel/ipsec.h" #include "en/tc/int_port.h" #include "en/ptp.h" @@ -1439,51 +1438,6 @@ static unsigned int mlx5e_ul_rep_stats_grps_num(struct mlx5e_priv *priv) return ARRAY_SIZE(mlx5e_ul_rep_stats_grps); } -static int -mlx5e_rep_vnic_reporter_diagnose(struct devlink_health_reporter *reporter, - struct devlink_fmsg *fmsg, - struct netlink_ext_ack *extack) -{ - struct mlx5e_rep_priv *rpriv = devlink_health_reporter_priv(reporter); - struct mlx5_eswitch_rep *rep = rpriv->rep; - - mlx5_reporter_vnic_diagnose_counters(rep->esw->dev, fmsg, rep->vport, - true); - return 0; -} - -static const struct devlink_health_reporter_ops mlx5_rep_vnic_reporter_ops = { - .name = "vnic", - .diagnose = mlx5e_rep_vnic_reporter_diagnose, -}; - -static void mlx5e_rep_vnic_reporter_create(struct mlx5e_priv *priv, - struct devlink_port *dl_port) -{ - struct mlx5e_rep_priv *rpriv = priv->ppriv; - struct devlink_health_reporter *reporter; - - reporter = devl_port_health_reporter_create(dl_port, - &mlx5_rep_vnic_reporter_ops, - rpriv); - if (IS_ERR(reporter)) { - mlx5_core_err(priv->mdev, - "Failed to create representor vnic reporter, err = %pe\n", - reporter); - return; - } - - rpriv->rep_vnic_reporter = reporter; -} - -static void mlx5e_rep_vnic_reporter_destroy(struct mlx5e_priv *priv) -{ - struct mlx5e_rep_priv *rpriv = priv->ppriv; - - if (!IS_ERR_OR_NULL(rpriv->rep_vnic_reporter)) - devl_health_reporter_destroy(rpriv->rep_vnic_reporter); -} - static const struct mlx5e_profile mlx5e_rep_profile = { .init = mlx5e_init_rep, .cleanup = mlx5e_cleanup_rep, @@ -1607,10 +1561,8 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) dl_port = mlx5_esw_offloads_devlink_port(dev->priv.eswitch, rpriv->rep->vport); - if (!IS_ERR(dl_port)) { + if (!IS_ERR(dl_port)) SET_NETDEV_DEVLINK_PORT(netdev, dl_port); - mlx5e_rep_vnic_reporter_create(priv, dl_port); - } err = register_netdev(netdev); if (err) { @@ -1623,7 +1575,6 @@ mlx5e_vport_vf_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep) return 0; err_detach_netdev: - mlx5e_rep_vnic_reporter_destroy(priv); mlx5e_detach_netdev(netdev_priv(netdev)); err_cleanup_profile: priv->profile->cleanup(priv); @@ -1681,7 +1632,6 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch_rep *rep) } unregister_netdev(netdev); - mlx5e_rep_vnic_reporter_destroy(priv); mlx5e_detach_netdev(priv); priv->profile->cleanup(priv); mlx5e_destroy_netdev(netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 70640fa1ad7b..bcd7b4e814d0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -118,7 +118,6 @@ struct mlx5e_rep_priv { struct rtnl_link_stats64 prev_vf_vport_stats; struct mlx5_flow_handle *send_to_vport_meta_rule; struct rhashtable tc_ht; - struct devlink_health_reporter *rep_vnic_reporter; }; static inline diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 206cf9db3466..7bd0606a5253 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -410,8 +410,11 @@ static inline void mlx5e_free_rx_wqe(struct mlx5e_rq *rq, static void mlx5e_xsk_free_rx_wqe(struct mlx5e_wqe_frag_info *wi) { - if (!(wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE))) - xsk_buff_free(*wi->xskp); + if (wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE)) + return; + + xsk_buff_free(*wi->xskp); + wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); } static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index e7e6db7f6bf1..cd94bb44f6ab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -1199,50 +1199,39 @@ void mlx5e_stats_rmon_get(struct mlx5e_priv *priv, void mlx5e_stats_ts_get(struct mlx5e_priv *priv, struct ethtool_ts_stats *ts_stats) { - int i, j; + u16 nch = mlx5e_stats_nch_read(priv); + int i, tc; - mutex_lock(&priv->state_lock); + ts_stats->pkts = 0; - if (priv->tx_ptp_opened) { - struct mlx5e_ptp *ptp = priv->channels.ptp; + for (i = 0; i < nch; i++) { + struct mlx5e_channel_stats *channel_stats = + priv->channel_stats[i]; - ts_stats->pkts = 0; + for (tc = 0; tc < priv->max_opened_tc; tc++) + ts_stats->pkts += channel_stats->sq[tc].timestamps; + } + + /* Accumulate DMA and port timestamp counters so values stay monotonic + * across channel teardown and mode switches. + */ + if (priv->tx_ptp_opened) { + /* Err and Lost stats are only relevant for port timestamping, + * as the DMA layer will always successfully timestamp packets. + */ ts_stats->err = 0; ts_stats->lost = 0; - if (!ptp) - goto out; - - /* Aggregate stats across all TCs */ - for (i = 0; i < ptp->num_tc; i++) { + for (tc = 0; tc < priv->max_opened_tc; tc++) { struct mlx5e_ptp_cq_stats *stats = - ptp->ptpsq[i].cq_stats; + &priv->ptp_stats.cq[tc]; ts_stats->pkts += stats->cqe; ts_stats->err += stats->abort + stats->err_cqe + - stats->late_cqe; + stats->late_cqe; ts_stats->lost += stats->lost_cqe; } - } else { - /* DMA layer will always successfully timestamp packets. Other - * counters do not make sense for this layer. - */ - ts_stats->pkts = 0; - - /* Aggregate stats across all SQs */ - for (j = 0; j < priv->channels.num; j++) { - struct mlx5e_channel *c = priv->channels.c[j]; - - for (i = 0; i < c->num_tc; i++) { - struct mlx5e_sq_stats *stats = c->sq[i].stats; - - ts_stats->pkts += stats->timestamps; - } - } } - -out: - mutex_unlock(&priv->state_lock); } #define PPORT_PHY_LAYER_OFF(c) \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c index 36b00a856bc2..fdc960ea5331 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c @@ -4,6 +4,26 @@ #include <linux/mlx5/driver.h> #include "eswitch.h" #include "devlink.h" +#include "diag/reporter_vnic.h" + +static int +mlx5_esw_rep_vnic_reporter_diagnose(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, + struct netlink_ext_ack *extack) +{ + struct mlx5_vport *vport = devlink_health_reporter_priv(reporter); + + mlx5_reporter_vnic_diagnose_counters(vport->dev, fmsg, vport->vport, + true); + + return 0; +} + +static const +struct devlink_health_reporter_ops mlx5_esw_rep_vnic_reporter_ops = { + .name = "vnic", + .diagnose = mlx5_esw_rep_vnic_reporter_diagnose, +}; static void mlx5_esw_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_id *ppid) @@ -220,6 +240,7 @@ static void mlx5_esw_devlink_port_res_unregister(struct devlink_port *dl_port) int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx5_vport *vport) { + struct devlink_health_reporter *reporter; struct mlx5_core_dev *dev = esw->dev; const struct devlink_port_ops *ops; struct mlx5_devlink_port *dl_port; @@ -255,6 +276,16 @@ int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, struct mlx mlx5_core_dbg(dev, "Failed to register port resources: %d\n", err); + reporter = devl_port_health_reporter_create( + &dl_port->dl_port, &mlx5_esw_rep_vnic_reporter_ops, + vport); + if (IS_ERR(reporter)) + mlx5_core_err(dev, + "Failed to create vnic health reporter for vport %d: %pe\n", + vport_num, reporter); + else + dl_port->vnic_reporter = reporter; + return 0; rate_err: @@ -269,6 +300,12 @@ void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_vport *vport) if (!vport->dl_port) return; dl_port = vport->dl_port; + + if (dl_port->vnic_reporter) { + devl_health_reporter_destroy(dl_port->vnic_reporter); + dl_port->vnic_reporter = NULL; + } + mlx5_esw_devlink_port_res_unregister(&dl_port->dl_port); devl_rate_leaf_destroy(&dl_port->dl_port); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index b6e2c153b4f7..fc197d1dc9df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1040,13 +1040,19 @@ void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) (vport->info.ipsec_crypto_enabled || vport->info.ipsec_packet_enabled)) esw->enabled_ipsec_vf_count--; + /* Clear rx-mode before esw_vport_change_handle_locked(): on + * MLX5_VPORT_PROMISC_CHANGE it calls esw_update_vport_mc_promisc() + * when vport->allmulti_rule is set, repopulating mc_list with FDB + * rules that dangle once the FDB is destroyed. NULL allmulti_rule + * here skips that path. + */ + esw_apply_vport_rx_mode(esw, vport, false, false); /* We don't assume VFs will cleanup after themselves. * Calling vport change handler while vport is disabled will cleanup * the vport resources. */ esw_vport_change_handle_locked(vport); vport->enabled_events = 0; - esw_apply_vport_rx_mode(esw, vport, false, false); esw_vport_cleanup(esw, vport); esw->enabled_vports--; @@ -2288,8 +2294,7 @@ static int mlx5_esw_spfs_init(struct mlx5_eswitch *esw) if (!num_entries) goto out_free; - esw_funcs->spfs = kcalloc(num_entries, sizeof(*esw_funcs->spfs), - GFP_KERNEL); + esw_funcs->spfs = kzalloc_objs(*esw_funcs->spfs, num_entries); if (!esw_funcs->spfs) { err = -ENOMEM; goto out_free; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index c655f6e8da1c..8b1f93b13ea9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -189,6 +189,7 @@ struct mlx5_vport; struct mlx5_devlink_port { struct devlink_port dl_port; struct mlx5_vport *vport; + struct devlink_health_reporter *vnic_reporter; }; static inline void mlx5_devlink_port_init(struct mlx5_devlink_port *dl_port, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index d603e294ee0e..eb74b6260168 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -1524,7 +1524,7 @@ struct mlx5_flow_handle * mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, struct mlx5_flow_table *lag_ft) { - struct mlx5_flow_spec *spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + struct mlx5_flow_spec *spec = kvzalloc_obj(*spec); struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = {}; struct mlx5_flow_handle *ret; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c index 19f65d4c4def..d43f07360159 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c @@ -163,12 +163,15 @@ void mlx5_eswitch_termtbl_put(struct mlx5_eswitch *esw, struct mlx5_termtbl_handle *tt) { + bool last; + mutex_lock(&esw->offloads.termtbl_mutex); - if (--tt->ref_count == 0) + last = (--tt->ref_count == 0); + if (last) hash_del(&tt->termtbl_hlist); mutex_unlock(&esw->offloads.termtbl_mutex); - if (!tt->ref_count) { + if (last) { mlx5_del_flow_rules(tt->rule); mlx5_destroy_flow_table(tt->termtbl); kfree(tt); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index 2285c889c215..c655f6e32e9b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -63,14 +63,15 @@ static int get_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags) return MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY; } -static u8 lag_active_port_bits(struct mlx5_lag *ldev) +static u8 lag_active_port_bits(struct mlx5_lag *ldev, + struct lag_tracker *tracker) { u8 enabled_ports[MLX5_MAX_PORTS] = {}; u8 active_port = 0; int num_enabled; int idx; - mlx5_infer_tx_enabled(&ldev->tracker, ldev, enabled_ports, + mlx5_infer_tx_enabled(tracker, ldev, enabled_ports, &num_enabled); for (idx = 0; idx < num_enabled; idx++) active_port |= BIT_MASK(enabled_ports[idx]); @@ -79,7 +80,8 @@ static u8 lag_active_port_bits(struct mlx5_lag *ldev) } static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev, - int mode, unsigned long flags) + struct lag_tracker *tracker, int mode, + unsigned long flags) { bool fdb_sel_mode = test_bit(MLX5_LAG_MODE_FLAG_FDB_SEL_MODE_NATIVE, &flags); @@ -108,7 +110,7 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev, break; MLX5_SET(lagc, lag_ctx, active_port, - lag_active_port_bits(mlx5_lag_dev(dev))); + lag_active_port_bits(ldev, tracker)); break; default: break; @@ -787,7 +789,8 @@ static int mlx5_cmd_modify_active_port(struct mlx5_core_dev *dev, u8 ports) return mlx5_cmd_exec_in(dev, modify_lag, in); } -static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 *ports) +static int _mlx5_modify_lag(struct mlx5_lag *ldev, + struct lag_tracker *tracker, u8 *ports) { int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1); struct mlx5_core_dev *dev0; @@ -804,7 +807,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 *ports) !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass)) return ret; - active_ports = lag_active_port_bits(ldev); + active_ports = lag_active_port_bits(ldev, tracker); return mlx5_cmd_modify_active_port(dev0, active_ports); } @@ -868,7 +871,7 @@ void mlx5_modify_lag(struct mlx5_lag *ldev, idx = i * ldev->buckets + j; if (ports[idx] == ldev->v2p_map[idx]) continue; - err = _mlx5_modify_lag(ldev, ports); + err = _mlx5_modify_lag(ldev, tracker, ports); if (err) { mlx5_core_err(dev0, "Failed to modify LAG (%d)\n", @@ -976,7 +979,7 @@ static int mlx5_create_lag(struct mlx5_lag *ldev, mlx5_core_info(dev0, "shared_fdb:%d mode:%s\n", shared_fdb, mlx5_get_str_port_sel_mode(mode, flags)); - err = mlx5_cmd_create_lag(dev0, ldev, mode, flags); + err = mlx5_cmd_create_lag(dev0, ldev, tracker, mode, flags); if (err) { mlx5_core_err(dev0, "Failed to create LAG (%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c index 95a8b1e64ba4..1724bf7e0e7a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c @@ -317,7 +317,7 @@ int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev) num_spfs = mlx5_esw_get_num_spfs(dev); num_hwc = MLX5_SF_HWC_FIRST_SPF + num_spfs; - table->hwc = kcalloc(num_hwc, sizeof(*table->hwc), GFP_KERNEL); + table->hwc = kzalloc_objs(*table->hwc, num_hwc); if (!table->hwc) { err = -ENOMEM; goto hwc_alloc_err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c index fa4d24b3dfaa..1b928b40686e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_icm_pool.c @@ -239,7 +239,7 @@ static int dr_icm_buddy_init_ste_cache(struct mlx5dr_icm_buddy_mem *buddy) if (!buddy->hw_ste_arr) goto free_ste_arr; - buddy->miss_list = kvmalloc_array(num_of_entries, sizeof(struct list_head), GFP_KERNEL); + buddy->miss_list = kvmalloc_objs(struct list_head, num_of_entries); if (!buddy->miss_list) goto free_hw_ste_arr; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index 9939749c47bc..9c5862f4e16a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -572,6 +572,38 @@ mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_sp1_ptp_unmatched_ht_params); } +/* mlxsw_sp1_ptp_packet_finish() is reached both from the NAPI poll context + * (mlxsw_sp1_ptp_got_packet(), mlxsw_sp1_ptp_got_piece() and + * mlxsw_sp1_packet_timestamp()) and from process context, by way of the GC + * workqueue (mlxsw_sp1_ptp_ht_gc_collect() -> + * mlxsw_sp1_ptp_unmatched_finish()). + * + * mlxsw_sp_rx_listener_no_mark_func() ends in napi_gro_receive(), using the + * NAPI pointer that was placed in the SKB control block when the trapped + * packet was received in the NAPI context. That pointer may only be used + * from its own poll context, which this call site cannot guarantee. + * + * netif_receive_skb(), unlike napi_gro_receive(), can be called from outside + * of the NAPI instance's poll context. RX stats accounting and the skb->dev + * assignment are still preserved; the only change is the delivery call. + */ +static void mlxsw_sp1_ptp_rx_finish(struct mlxsw_sp_port *mlxsw_sp_port, + struct sk_buff *skb) +{ + struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + + skb->dev = mlxsw_sp_port->dev; + + pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; + u64_stats_update_end(&pcpu_stats->syncp); + + skb->protocol = eth_type_trans(skb, skb->dev); + netif_receive_skb(skb); +} + /* This function is called in the following scenarios: * * 1) When a packet is matched with its timestamp. @@ -600,7 +632,7 @@ static void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp, if (ingress) { if (hwtstamps) *skb_hwtstamps(skb) = *hwtstamps; - mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp); + mlxsw_sp1_ptp_rx_finish(mlxsw_sp_port, skb); } else { /* skb_tstamp_tx() allows hwtstamps to be NULL. */ skb_tstamp_tx(skb, hwtstamps); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c index 5e383d40abc7..ec3b628ecb2d 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c @@ -282,7 +282,7 @@ enum fbnic_msix_self_test_codes fbnic_msix_test(struct fbnic_dev *fbd) int i; /* Allocate bitmap and IRQ vector table */ - test_data = kzalloc_obj(*test_data, GFP_KERNEL); + test_data = kzalloc_obj(*test_data); /* memory allocation failure */ if (!test_data) diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c index 4afbb40bc0e4..d49f281c7867 100644 --- a/drivers/net/ethernet/micrel/ks8851_common.c +++ b/drivers/net/ethernet/micrel/ks8851_common.c @@ -143,9 +143,6 @@ static int ks8851_write_mac_addr(struct net_device *dev) ks8851_wrreg16(ks, KS_MAR(i), val); } - if (!netif_running(dev)) - ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); - ks8851_unlock(ks); return 0; @@ -478,8 +475,7 @@ static int ks8851_net_open(struct net_device *dev) * @dev: The device being closed. * * Called to close down a network device which has been active. Cancel any - * work, shutdown the RX and TX process and then place the chip into a low - * power state whilst it is not being used. + * work and shutdown the RX and TX process. */ static int ks8851_net_stop(struct net_device *dev) { @@ -506,8 +502,6 @@ static int ks8851_net_stop(struct net_device *dev) /* shutdown TX process */ ks8851_wrreg16(ks, KS_TXCR, 0x0000); - /* set powermode to soft power down to save power */ - ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); ks8851_unlock(ks); /* ensure any queued tx buffers are dumped */ diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index f92b2d0bf926..8e9bfc1d6a2a 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -429,11 +429,11 @@ int mana_gd_alloc_memory(struct gdma_context *gc, unsigned int length, /* length is a power of 2 above PAGE_SIZE, so this divides exactly. */ npages = length / PAGE_SIZE; - gmi->pages_va = kvcalloc(npages, sizeof(*gmi->pages_va), GFP_KERNEL); + gmi->pages_va = kvzalloc_objs(*gmi->pages_va, npages); if (!gmi->pages_va) return -ENOMEM; - gmi->pages_dma = kvcalloc(npages, sizeof(*gmi->pages_dma), GFP_KERNEL); + gmi->pages_dma = kvzalloc_objs(*gmi->pages_dma, npages); if (!gmi->pages_dma) goto free_va; @@ -825,7 +825,7 @@ int mana_schedule_serv_work(struct gdma_context *gc, enum gdma_eqe_type type) return -ENODEV; } - mns_wk = kzalloc(sizeof(*mns_wk), GFP_ATOMIC); + mns_wk = kzalloc_obj(*mns_wk, GFP_ATOMIC); if (!mns_wk) { module_put(THIS_MODULE); clear_bit(GC_IN_SERVICE, &gc->flags); @@ -1991,7 +1991,7 @@ struct gdma_irq_context *mana_gd_get_gic(struct gdma_context *gc, *msi_requested = msi; } - gic = kzalloc(sizeof(*gic), GFP_KERNEL); + gic = kzalloc_obj(*gic); if (!gic) { gic = ERR_PTR(-ENOMEM); if (irq_map.virq) diff --git a/drivers/net/ethernet/microsoft/mana/mana_bpf.c b/drivers/net/ethernet/microsoft/mana/mana_bpf.c index 53308e139cbe..5c9961ee9747 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_bpf.c +++ b/drivers/net/ethernet/microsoft/mana/mana_bpf.c @@ -208,6 +208,7 @@ static int mana_xdp_set(struct net_device *ndev, struct bpf_prog *prog, if (err) { NL_SET_ERR_MSG_MOD(extack, "XDP: Insufficient memory for tx/rx re-config"); + apc->bpf_prog = old_prog; return err; } diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 7a1ac853e3ab..591fb4191d90 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2986,6 +2986,10 @@ static int mana_alloc_rx_wqe(struct mana_port_context *apc, *cq_size += COMP_ENTRY_SIZE; } + /* Reserve an extra slot for Fence completion + * event (CQE_RX_OBJECT_FENCE) in case RX CQ is full. + */ + *cq_size += COMP_ENTRY_SIZE; return 0; } @@ -3080,7 +3084,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc, goto out; rq_size = MANA_PAGE_ALIGN(rq_size); - cq_size = MANA_PAGE_ALIGN(cq_size); + cq_size = MANA_PAGE_ALIGN(roundup_pow_of_two(cq_size)); /* Create RQ */ memset(&spec, 0, sizeof(spec)); @@ -3983,7 +3987,8 @@ static void mana_rdma_service_handle(struct work_struct *work) struct device *dev = gd->gdma_context->dev; int ret; - if (READ_ONCE(gd->rdma_teardown)) + /* Pairs with the smp_store_release() in mana_rdma_probe(). */ + if (smp_load_acquire(&gd->rdma_teardown)) goto out; switch (serv_work->event) { @@ -4279,6 +4284,21 @@ int mana_rdma_probe(struct gdma_dev *gd) if (err) return err; + /* Clear the state left by a previous mana_rdma_remove() so servicing + * events are handled again after a reset cycle. + */ + gd->is_suspended = false; + + /* Publish is_suspended before re-opening the gate, so the handler + * cannot observe an open gate with a stale is_suspended. Pairs + * with the smp_load_acquire() in mana_rdma_service_handle(). This + * matters on the reset path, where mana_rdma_remove() closed the + * gate and drained the workqueue; on the initial probe path the + * gate was never closed and both flags are already clear. It does + * not order gd->adev, which add_adev() publishes below. + */ + smp_store_release(&gd->rdma_teardown, false); + err = add_adev(gd, "rdma"); if (err) mana_gd_deregister_device(gd); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index a2a89d48e3ca..4e83637715e0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -1421,7 +1421,8 @@ static int nfp_net_get_fs_rule(struct nfp_net *nn, struct ethtool_rxnfc *cmd) return -ENOENT; } -static int nfp_net_get_fs_loc(struct nfp_net *nn, u32 *rule_locs) +static int nfp_net_get_fs_loc(struct nfp_net *nn, struct ethtool_rxnfc *cmd, + u32 *rule_locs) { struct nfp_fs_entry *entry; u32 count = 0; @@ -1429,8 +1430,12 @@ static int nfp_net_get_fs_loc(struct nfp_net *nn, u32 *rule_locs) if (!(nn->cap_w1 & NFP_NET_CFG_CTRL_FLOW_STEER)) return -EOPNOTSUPP; - list_for_each_entry(entry, &nn->fs.list, node) + list_for_each_entry(entry, &nn->fs.list, node) { + if (count == cmd->rule_cnt) + return -EMSGSIZE; rule_locs[count++] = entry->loc; + } + cmd->rule_cnt = count; return 0; } @@ -1455,7 +1460,7 @@ static int nfp_net_get_rxnfc(struct net_device *netdev, return nfp_net_get_fs_rule(nn, cmd); case ETHTOOL_GRXCLSRLALL: cmd->data = NFP_FS_MAX_ENTRY; - return nfp_net_get_fs_loc(nn, rule_locs); + return nfp_net_get_fs_loc(nn, cmd, rule_locs); default: return -EOPNOTSUPP; } @@ -1698,8 +1703,14 @@ static int nfp_net_fs_add(struct nfp_net *nn, struct ethtool_rxnfc *cmd) nn->fs.count--; err = nfp_net_fs_add_hw(nn, new); - if (err) + if (err) { + /* mbox broken, adding the old rule back will + * likely also fail. + */ + list_del(&entry->node); + kfree(entry); goto err; + } nn->fs.count++; list_replace(&entry->node, &new->node); diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 417c15d1ff42..6fcc5f561d56 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -83,7 +83,7 @@ struct oa_tc6 { u16 spi_data_tx_buf_offset; u16 tx_credits; u8 rx_chunks_available; - bool rx_buf_overflow; + bool wait_until_start_valid; bool int_flag; bool disable_traffic; bool prot_ctrl; @@ -455,7 +455,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum) { struct oa_tc6 *tc6 = bus->priv; u32 regval; - bool ret; + int ret; ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK), @@ -693,6 +693,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6) return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value); } +/* Called when a frame that is meant to be transmitted, is dropped. */ +static void oa_tc6_drop_tx_skb(struct oa_tc6 *tc6, struct sk_buff *skb) +{ + if (skb) { + tc6->netdev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + } +} + +static struct sk_buff *oa_tc6_detach_waiting_tx_skb(struct oa_tc6 *tc6) +{ + struct sk_buff *skb; + + lockdep_assert_held(&tc6->tx_skb_lock); + skb = tc6->waiting_tx_skb; + tc6->waiting_tx_skb = NULL; + + return skb; +} + static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6) { if (tc6->rx_skb) { @@ -704,29 +724,39 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6) static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6) { - if (tc6->ongoing_tx_skb) { - tc6->netdev->stats.tx_dropped++; - kfree_skb(tc6->ongoing_tx_skb); - tc6->ongoing_tx_skb = NULL; - } + oa_tc6_drop_tx_skb(tc6, tc6->ongoing_tx_skb); + tc6->ongoing_tx_skb = NULL; } static void oa_tc6_cleanup_waiting_tx_skb(struct oa_tc6 *tc6) { - if (tc6->waiting_tx_skb) { - tc6->netdev->stats.tx_dropped++; - kfree_skb(tc6->waiting_tx_skb); - tc6->waiting_tx_skb = NULL; - } + struct sk_buff *skb; + + spin_lock_bh(&tc6->tx_skb_lock); + skb = oa_tc6_detach_waiting_tx_skb(tc6); + spin_unlock_bh(&tc6->tx_skb_lock); + + oa_tc6_drop_tx_skb(tc6, skb); } -static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6) +static void oa_tc6_free_ongoing_skbs(struct oa_tc6 *tc6) { oa_tc6_cleanup_ongoing_tx_skb(tc6); oa_tc6_cleanup_ongoing_rx_skb(tc6); +} + +static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6) +{ + oa_tc6_free_ongoing_skbs(tc6); oa_tc6_cleanup_waiting_tx_skb(tc6); } +static void oa_tc6_look_for_new_frame(struct oa_tc6 *tc6) +{ + tc6->wait_until_start_valid = true; + oa_tc6_cleanup_ongoing_rx_skb(tc6); +} + /* If the failure is at SPI interface level, masking and clearing * the interrupt of the device won't work. Since SPI interrupt is * disabled, it should stop the repeated interrupts. @@ -734,9 +764,19 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6) static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) { u32 regval = OA_TC6_INT_MASK0_ALL_INTERRUPTS; + struct sk_buff *skb; + spin_lock_bh(&tc6->tx_skb_lock); tc6->disable_traffic = true; - oa_tc6_free_pending_skbs(tc6); + skb = oa_tc6_detach_waiting_tx_skb(tc6); + spin_unlock_bh(&tc6->tx_skb_lock); + + /* disable_traffic, when set, is a point of no return to + * working state. Keeping the TX queues disabled. + */ + netif_tx_disable(tc6->netdev); + oa_tc6_drop_tx_skb(tc6, skb); + oa_tc6_free_ongoing_skbs(tc6); oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval); oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, ®val); oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval); @@ -755,6 +795,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) return ret; } + /* This function is called for each chunk received in a given SPI + * transaction. In case, extended status bit is set in more than + * one chunk, skip the write, if status0 is already cleared. + */ + if (!value) + return 0; + /* Clear the error interrupts status */ ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value); if (ret) { @@ -764,8 +811,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) } if (FIELD_GET(OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) { - tc6->rx_buf_overflow = true; - oa_tc6_cleanup_ongoing_rx_skb(tc6); + oa_tc6_look_for_new_frame(tc6); net_err_ratelimited("%s: Receive buffer overflow error\n", tc6->netdev->name); return -EAGAIN; @@ -791,6 +837,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) { + int ret = 0; + /* Process rx chunk footer for the following, * 1. tx credits * 2. errors if any from MAC-PHY @@ -801,9 +849,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) footer); if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) { - int ret = oa_tc6_process_extended_status(tc6); - - if (ret) + ret = oa_tc6_process_extended_status(tc6); + /* EAGAIN error is recoverable. Move on to check + * HEADER and SYNC errors before returning. + */ + if (ret && ret != -EAGAIN) return ret; } @@ -821,7 +871,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) return -ENODEV; } - return 0; + return ret; } static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) @@ -846,13 +896,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) tc6->rx_skb = NULL; } -static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length) +/* On oversubscribed traffic condition, particularly with overwhelming rx + * buffer overflow errors, there could be data chunk loss. If tail + length + * goes beyond end pointer, that is an indication that the data chunk with + * end_valid bit is lost. Time to look for a data chunk with start_valid bit. + * + * If rx_skb is NULL, it is time to start looking for data chunk with + * start_bit. + */ +static int oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length) { + if (!tc6->rx_skb || + skb_tailroom(tc6->rx_skb) < length) { + oa_tc6_look_for_new_frame(tc6); + return -EAGAIN; + } + memcpy(skb_put(tc6->rx_skb, length), payload, length); + return 0; } +/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is + * possible that we get two data chunks with start_valid bit set, without + * end_valid bit set in between. In this case, rx_skb would have a valid + * buffer pointer. We should release, if a valid pointer is found before + * allocating a new one. + */ static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6) { + oa_tc6_cleanup_ongoing_rx_skb(tc6); tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu + ETH_HLEN + ETH_FCS_LEN); if (!tc6->rx_skb) { @@ -872,7 +944,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload, if (ret) return ret; - oa_tc6_update_rx_skb(tc6, payload, size); + ret = oa_tc6_update_rx_skb(tc6, payload, size); + if (ret) + return ret; oa_tc6_submit_rx_skb(tc6); @@ -887,22 +961,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size) if (ret) return ret; - oa_tc6_update_rx_skb(tc6, payload, size); - - return 0; + return oa_tc6_update_rx_skb(tc6, payload, size); } -static void oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size) +static int oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size) { - oa_tc6_update_rx_skb(tc6, payload, size); + int ret; - oa_tc6_submit_rx_skb(tc6); + ret = oa_tc6_update_rx_skb(tc6, payload, size); + if (!ret) + oa_tc6_submit_rx_skb(tc6); + return ret; } -static void oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload, - u32 footer) +static int oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload, + u32 footer) { - oa_tc6_update_rx_skb(tc6, payload, OA_TC6_CHUNK_PAYLOAD_SIZE); + return oa_tc6_update_rx_skb(tc6, payload, + OA_TC6_CHUNK_PAYLOAD_SIZE); } static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, @@ -917,10 +993,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, u16 size; /* Restart the new rx frame after receiving rx buffer overflow error */ - if (start_valid && tc6->rx_buf_overflow) - tc6->rx_buf_overflow = false; + if (start_valid && tc6->wait_until_start_valid) + tc6->wait_until_start_valid = false; - if (tc6->rx_buf_overflow) + if (tc6->wait_until_start_valid) return 0; /* Process the chunk with complete rx frame */ @@ -942,8 +1018,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, /* Process the chunk with only rx frame end */ if (end_valid && !start_valid) { size = end_byte_offset + 1; - oa_tc6_prcs_rx_frame_end(tc6, data, size); - return 0; + return oa_tc6_prcs_rx_frame_end(tc6, data, size); } /* Process the chunk with previous rx frame end and next rx frame @@ -957,6 +1032,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, if (tc6->rx_skb) { size = end_byte_offset + 1; oa_tc6_prcs_rx_frame_end(tc6, data, size); + + /* Return value from oa_tc6_prcs_rx_frame_end is not + * checked. If it returned an error, it is to make + * the code to look for new frame. At this stage, + * code below is going to process a new frame. So, + * error condition is set to false, in case it is + * set before proceeding. + */ + tc6->wait_until_start_valid = false; } size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6, @@ -965,9 +1049,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, } /* Process the chunk with ongoing rx frame data */ - oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer); - - return 0; + return oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer); } static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset) @@ -983,8 +1065,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset) static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) { u16 no_of_rx_chunks = length / OA_TC6_CHUNK_SIZE; + bool retry = false; + int ret = 0; u32 footer; - int ret; /* All the rx chunks in the receive SPI data buffer are examined here */ for (int i = 0; i < no_of_rx_chunks; i++) { @@ -993,8 +1076,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) OA_TC6_CHUNK_PAYLOAD_SIZE); ret = oa_tc6_process_rx_chunk_footer(tc6, footer); - if (ret) - return ret; + if (ret) { + if (ret != -EAGAIN) + return ret; + retry = true; + } /* If there is a data valid chunks then process it for the * information needed to determine the validity and the location @@ -1006,12 +1092,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload, footer); - if (ret) - return ret; + if (ret) { + if (ret != -ENOMEM && ret != -EAGAIN) + return ret; + retry = true; + } } } - return 0; + /* Not bailing out on recoverable error codes, -EAGAIN and + * -ENOMEM. If subsequent loop iterations, if any, succeeds, + * error code would be overwritten. retry flag helps to + * make the caller to continue and retry. Since recovery + * action for -ENOMEM and -EAGAIN are same, we are returning + * one of the error codes, that is -EAGAIN. + * + * Successful recovery depends on how small the frames are, + * how many chunks, among the received chunks triggered the + * error, whether data is intact even with error conditions. + * As a result, there is no single, best method to recover + * most data when error conditions hit. We do our best by + * processing all the chunks with good "footer header" and + * "data valid" bit set. + */ + if (retry) { + ret = -EAGAIN; + oa_tc6_look_for_new_frame(tc6); + } + + return ret; } static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid, @@ -1173,12 +1282,9 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) } ret = oa_tc6_process_spi_data_rx_buf(tc6, spi_len); - if (ret) { - if (ret == -EAGAIN) - continue; - oa_tc6_cleanup_ongoing_tx_skb(tc6); - oa_tc6_cleanup_ongoing_rx_skb(tc6); + if (ret && ret != -EAGAIN) { + oa_tc6_free_ongoing_skbs(tc6); netdev_err(tc6->netdev, "Device error: %d\n", ret); return ret; } @@ -1200,15 +1306,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data) * no need to attempt spi transfer, once it fails. Pending skbs * are already freed. */ - if (!tc6->disable_traffic) { - while (tc6->int_flag || - (tc6->waiting_tx_skb && tc6->tx_credits)) { - ret = oa_tc6_try_spi_transfer(tc6); - if (ret) { - disable_irq_nosync(tc6->spi->irq); - oa_tc6_disable_traffic(tc6); - break; - } + spin_lock_bh(&tc6->tx_skb_lock); + if (tc6->disable_traffic) { + spin_unlock_bh(&tc6->tx_skb_lock); + return IRQ_HANDLED; + } + spin_unlock_bh(&tc6->tx_skb_lock); + + while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret = oa_tc6_try_spi_transfer(tc6); + if (ret) { + disable_irq_nosync(tc6->spi->irq); + oa_tc6_disable_traffic(tc6); + break; } } @@ -1287,23 +1398,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable); * @tc6: oa_tc6 struct. * @skb: socket buffer in which the ethernet frame is stored. * - * Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q - * otherwise returns NETDEV_TX_BUSY. + * Return: NETDEV_TX_OK either on successful queueing of the packet for + * transmission, or on packet getting dropped. Packet can be dropped due to + * failure in linearizing the buffer or disable_traffic is set due to + * earlier fatal error. Returns NETDEV_TX_BUSY when there is no room + * to queue the packet. */ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) { - if (tc6->disable_traffic || tc6->waiting_tx_skb) { - netif_stop_queue(tc6->netdev); - return NETDEV_TX_BUSY; - } - if (skb_linearize(skb)) { - dev_kfree_skb_any(skb); - tc6->netdev->stats.tx_dropped++; + oa_tc6_drop_tx_skb(tc6, skb); return NETDEV_TX_OK; } spin_lock_bh(&tc6->tx_skb_lock); + if (tc6->waiting_tx_skb) { + netif_stop_queue(tc6->netdev); + spin_unlock_bh(&tc6->tx_skb_lock); + return NETDEV_TX_BUSY; + } + if (tc6->disable_traffic) { + spin_unlock_bh(&tc6->tx_skb_lock); + oa_tc6_drop_tx_skb(tc6, skb); + return NETDEV_TX_OK; + } tc6->waiting_tx_skb = skb; spin_unlock_bh(&tc6->tx_skb_lock); @@ -1462,8 +1580,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init); */ void oa_tc6_exit(struct oa_tc6 *tc6) { - tc6->disable_traffic = true; disable_irq(tc6->spi->irq); + spin_lock_bh(&tc6->tx_skb_lock); + tc6->disable_traffic = true; + spin_unlock_bh(&tc6->tx_skb_lock); oa_tc6_phy_exit(tc6); oa_tc6_free_pending_skbs(tc6); } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index e436e3231e86..2543a8ff8547 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -1672,15 +1672,22 @@ static int ionic_tx(struct net_device *netdev, struct ionic_queue *q, stats->pkts++; stats->bytes += skb->len; + ionic_txq_post(q, false); + if (likely(!ionic_txq_hwstamp_enabled(q))) { struct netdev_queue *ndq = q_to_ndq(netdev, q); - if (unlikely(!ionic_q_has_space(q, MAX_SKB_FRAGS + 1))) - netif_tx_stop_queue(ndq); + netif_txq_maybe_stop(ndq, ionic_q_space_avail(q), + MAX_SKB_FRAGS + 1, MAX_SKB_FRAGS + 1); ring_dbell = __netdev_tx_sent_queue(ndq, skb->len, netdev_xmit_more()); } - ionic_txq_post(q, ring_dbell); + + if (ring_dbell) { + ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, + q->dbval | q->head_idx); + q->dbell_jiffies = jiffies; + } return 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f2fc89176654..62c3441911e7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3676,6 +3676,14 @@ static int stmmac_hw_setup(struct net_device *dev) /* Initialize MTL*/ stmmac_mtl_configuration(priv); + /* Apply the RX packet parser table */ + if (priv->tc_entries) { + ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries, + priv->tc_entries_max); + if (ret) + return ret; + } + /* Initialize Safety Features */ stmmac_safety_feat_configuration(priv); @@ -4319,6 +4327,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb, /** * stmmac_tso_allocator - close entry point of the driver * @priv: driver private structure + * @entry: TX queue buffer index * @des: buffer start address * @total_len: total length to fill in descriptors * @last_segment: condition for the last descriptor @@ -4327,8 +4336,9 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb, * This function fills descriptor and request new descriptors according to * buffer length to fill */ -static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, - int total_len, bool last_segment, u32 queue) +static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry, + dma_addr_t des, int total_len, + bool last_segment, u32 queue) { struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; struct dma_desc *desc; @@ -4340,14 +4350,13 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, while (tmp_len > 0) { dma_addr_t curr_addr; - tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx, - priv->dma_conf.dma_tx_size); - WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); + *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[*entry]); if (tx_q->tbs & STMMAC_TBS_AVAIL) - desc = &tx_q->dma_entx[tx_q->cur_tx].basic; + desc = &tx_q->dma_entx[*entry].basic; else - desc = &tx_q->dma_tx[tx_q->cur_tx]; + desc = &tx_q->dma_tx[*entry]; curr_addr = des + (total_len - tmp_len); stmmac_set_desc_addr(priv, desc, curr_addr); @@ -4445,6 +4454,26 @@ static bool stmmac_tso_valid_packet(struct sk_buff *skb) header_len + gso_size < 16383; } +static int stmmac_tso_get_num_desc(struct stmmac_tx_queue *tx_q, + struct sk_buff *skb, u32 pay_len) +{ + int i, ndesc = 1; + + /* head payload */ + ndesc += DIV_ROUND_UP(pay_len, TSO_MAX_BUFF_SIZE); + /* frag payload */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + ndesc += DIV_ROUND_UP(skb_frag_size(frag), + TSO_MAX_BUFF_SIZE); + } + /* MSS update requires a new descriptor */ + ndesc += !!(skb_shinfo(skb)->gso_size != tx_q->mss); + + return ndesc; +} + /** * stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO) * @skb : the socket buffer @@ -4486,12 +4515,12 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) { struct dma_desc *desc, *first, *mss_desc = NULL; struct stmmac_priv *priv = netdev_priv(dev); - unsigned int first_entry, tx_packets; + unsigned int first_entry, entry, tx_packets; struct stmmac_txq_stats *txq_stats; + int i, first_tx, nfrags, ndesc; struct stmmac_tx_queue *tx_q; bool set_ic, is_last_segment; u32 pay_len, mss, queue; - int i, first_tx, nfrags; u8 proto_hdr_len, hdr; dma_addr_t des; @@ -4504,14 +4533,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* Compute header lengths */ proto_hdr_len = stmmac_tso_header_size(skb); + pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */ + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) hdr = sizeof(struct udphdr); else hdr = tcp_hdrlen(skb); - /* Desc availability based on threshold should be enough safe */ - if (unlikely(stmmac_tx_avail(priv, queue) < - (((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) { + ndesc = stmmac_tso_get_num_desc(tx_q, skb, pay_len); + if (unlikely(stmmac_tx_avail(priv, queue) < ndesc)) { if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); @@ -4523,8 +4553,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - pay_len = skb_headlen(skb) - proto_hdr_len; /* no frags */ - mss = skb_shinfo(skb)->gso_size; /* set new MSS value if needed */ @@ -4549,22 +4577,24 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) } first_entry = tx_q->cur_tx; - WARN_ON(tx_q->tx_skbuff[first_entry]); + entry = first_entry; + + WARN_ON(tx_q->tx_skbuff[entry]); if (tx_q->tbs & STMMAC_TBS_AVAIL) - desc = &tx_q->dma_entx[first_entry].basic; + desc = &tx_q->dma_entx[entry].basic; else - desc = &tx_q->dma_tx[first_entry]; + desc = &tx_q->dma_tx[entry]; first = desc; /* first descriptor: fill Headers on Buf1 */ des = dma_map_single(priv->device, skb->data, skb_headlen(skb), DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) - goto dma_map_err; + goto error; stmmac_set_desc_addr(priv, first, des); - stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len, + stmmac_tso_allocator(priv, &entry, des + proto_hdr_len, pay_len, (nfrags == 0), queue); /* In case two or more DMA transmit descriptors are allocated for this @@ -4579,8 +4609,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) * this DMA buffer right after the DMA engine completely finishes the * full buffer transmission. */ - stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, skb_headlen(skb), - false); + stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_headlen(skb), false); /* Prepare fragments */ for (i = 0; i < nfrags; i++) { @@ -4590,14 +4619,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) skb_frag_size(frag), DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) - goto dma_map_err; + goto error_dma_unmap; - stmmac_tso_allocator(priv, des, skb_frag_size(frag), + stmmac_tso_allocator(priv, &entry, des, skb_frag_size(frag), (i == nfrags - 1), queue); - stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, + stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_frag_size(frag), true); } + tx_q->cur_tx = entry; stmmac_set_tx_dma_last_segment(tx_q, tx_q->cur_tx); @@ -4702,7 +4732,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; -dma_map_err: +error_dma_unmap: + for (;;) { + desc = stmmac_get_tx_desc(priv, tx_q, first_entry); + stmmac_release_tx_desc(priv, desc, priv->descriptor_mode); + stmmac_free_tx_buffer(priv, &priv->dma_conf, queue, + first_entry); + if (first_entry == entry) + break; + + first_entry = STMMAC_NEXT_ENTRY(first_entry, + priv->dma_conf.dma_tx_size); + } +error: dev_err(priv->device, "Tx dma map failed\n"); dev_kfree_skb(skb); priv->xstats.tx_dropped++; @@ -8002,6 +8044,7 @@ static int __stmmac_dvr_probe(struct device *device, stmmac_napi_add(ndev); mutex_init(&priv->lock); + rwlock_init(&priv->ptp_lock); stmmac_fpe_init(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 960249960004..3bfcc9760dce 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -365,7 +365,6 @@ void stmmac_ptp_register(struct stmmac_priv *priv) if (priv->plat->crosststamp) priv->ptp_clock_ops.getcrosststamp = stmmac_getcrosststamp; - rwlock_init(&priv->ptp_lock); mutex_init(&priv->aux_ts_lock); priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops, diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 298efc76a56b..69fe5717846b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -318,6 +318,11 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen, static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, unsigned int role, __u16 inner_proto) { + if (skb_is_gso(skb)) { + netdev_dbg(pctx->dev, "GSO is not supported in GTP\n"); + goto err; + } + if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) { netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); return 1; diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index 2b7034193a00..abfcfe07246a 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -1156,11 +1156,10 @@ static void cc2520_remove(struct spi_device *spi) { struct cc2520_private *priv = spi_get_drvdata(spi); - mutex_destroy(&priv->buffer_mutex); - flush_work(&priv->fifop_irqwork); - + disable_work_sync(&priv->fifop_irqwork); ieee802154_unregister_hw(priv->hw); ieee802154_free_hw(priv->hw); + mutex_destroy(&priv->buffer_mutex); } static const struct spi_device_id cc2520_ids[] = { diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index 6daa0f198b9f..a9bd1555d2dc 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -72,6 +72,8 @@ struct hwsim_phy { struct ieee802154_hw *hw; u32 idx; + /* Serializes phy->pib_updates. */ + spinlock_t pib_lock; struct hwsim_pib __rcu *pib; bool suspended; @@ -102,8 +104,6 @@ static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel, if (!pib) return -ENOMEM; - pib_old = rtnl_dereference(phy->pib); - pib->page = page; pib->channel = channel; pib->filt.short_addr = filt->short_addr; @@ -112,7 +112,10 @@ static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel, pib->filt.pan_coord = filt->pan_coord; pib->filt_level = filt_level; - rcu_assign_pointer(phy->pib, pib); + spin_lock_bh(&phy->pib_lock); + pib_old = rcu_replace_pointer(phy->pib, pib, + lockdep_is_held(&phy->pib_lock)); + spin_unlock_bh(&phy->pib_lock); kfree_rcu(pib_old, rcu); return 0; } @@ -952,6 +955,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, goto err_pib; } + spin_lock_init(&phy->pib_lock); pib->channel = 13; pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 4939cf67b336..f29864db662a 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -848,7 +848,6 @@ static int ipvlan_device_event(struct notifier_block *unused, __ipvlan_link_delete(net, ipvlan->dev, &lst_kill); } - unregister_netdevice_many(&lst_kill); break; } case NETDEV_FEAT_CHANGE: @@ -899,6 +898,9 @@ static int ipvlan_device_event(struct notifier_block *unused, mutex_unlock(&port->pnodes_lock); + /* Avoid invoking nested netdevice notifiers under pnodes_lock. */ + unregister_netdevice_many(&lst_kill); + ipvlan_port_put(port); return ret; diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c index 88d9e36cd4a2..4e857dd5df64 100644 --- a/drivers/net/mctp/mctp-i3c.c +++ b/drivers/net/mctp/mctp-i3c.c @@ -288,6 +288,7 @@ err: static int mctp_i3c_probe(struct i3c_device *i3c) { struct mctp_i3c_bus *b = NULL, *mbus = NULL; + int rc; /* Look for a known bus */ mutex_lock(&busdevs_lock); @@ -296,14 +297,16 @@ static int mctp_i3c_probe(struct i3c_device *i3c) mbus = b; break; } - mutex_unlock(&busdevs_lock); if (!mbus) { /* probably no "mctp-controller" property on the i3c bus */ - return -ENODEV; + rc = -ENODEV; + } else { + rc = mctp_i3c_add_device(mbus, i3c); } + mutex_unlock(&busdevs_lock); - return mctp_i3c_add_device(mbus, i3c); + return rc; } static void mctp_i3c_remove_device(struct mctp_i3c_device *mi) diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index 3f7d31033bae..1b5213e87070 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -675,7 +675,7 @@ static int net_failover_slave_name_change(struct net_device *slave_dev, /* We need to bring up the slave after the rename by udev in case * open failed with EBUSY when it was registered. */ - dev_open(slave_dev, NULL); + netif_open(slave_dev, NULL); return 0; } diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 9c171697e762..7a0d5e892a1a 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -127,8 +127,10 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, { struct ntb_netdev_queue *q = qp_data; struct ntb_netdev *dev = q->ntdev; + struct pcpu_sw_netstats *tstats; struct sk_buff *skb, *new_skb; struct net_device *ndev; + unsigned long flags; int rc; ndev = dev->ndev; @@ -139,17 +141,20 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len); if (len < 0) { - ndev->stats.rx_errors++; - ndev->stats.rx_length_errors++; + DEV_STATS_INC(ndev, rx_errors); + DEV_STATS_INC(ndev, rx_length_errors); goto enqueue_again; } - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += len; + tstats = this_cpu_ptr(ndev->tstats); + flags = u64_stats_update_begin_irqsave(&tstats->syncp); + u64_stats_inc(&tstats->rx_packets); + u64_stats_add(&tstats->rx_bytes, len); + u64_stats_update_end_irqrestore(&tstats->syncp, flags); new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); if (!new_skb) { - ndev->stats.rx_dropped++; + DEV_STATS_INC(ndev, rx_dropped); goto enqueue_again; } @@ -166,8 +171,8 @@ enqueue_again: rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); if (rc) { dev_kfree_skb_any(skb); - ndev->stats.rx_errors++; - ndev->stats.rx_fifo_errors++; + DEV_STATS_INC(ndev, rx_errors); + DEV_STATS_INC(ndev, rx_fifo_errors); } } @@ -210,25 +215,39 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, { struct ntb_netdev_queue *q = qp_data; struct ntb_netdev *dev = q->ntdev; + struct pcpu_sw_netstats *tstats; struct net_device *ndev; struct sk_buff *skb; + unsigned long flags; + bool registered; ndev = dev->ndev; skb = data; if (!skb || !ndev) return; + rcu_read_lock(); + registered = READ_ONCE(ndev->reg_state) == NETREG_REGISTERED; + if (!registered) + goto free_skb; + if (len > 0) { - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; + /* The memcpy kthread can migrate, so pin the per-CPU update. */ + tstats = get_cpu_ptr(ndev->tstats); + flags = u64_stats_update_begin_irqsave(&tstats->syncp); + u64_stats_inc(&tstats->tx_packets); + u64_stats_add(&tstats->tx_bytes, skb->len); + u64_stats_update_end_irqrestore(&tstats->syncp, flags); + put_cpu_ptr(ndev->tstats); } else { - ndev->stats.tx_errors++; - ndev->stats.tx_aborted_errors++; + DEV_STATS_INC(ndev, tx_errors); + DEV_STATS_INC(ndev, tx_aborted_errors); } +free_skb: dev_kfree_skb_any(skb); - if (ntb_transport_tx_free_entry(qp) >= tx_start) { + if (registered && ntb_transport_tx_free_entry(qp) >= tx_start) { /* Make sure anybody stopping the queue after this sees the new * value of ntb_transport_tx_free_entry() */ @@ -237,6 +256,7 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, ntb_transport_link_query(q->qp)) netif_wake_subqueue(ndev, q->qid); } + rcu_read_unlock(); } static const struct ntb_queue_handlers ntb_netdev_handlers = { @@ -277,7 +297,7 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, drop: dev_kfree_skb_any(skb); - ndev->stats.tx_dropped++; + DEV_STATS_INC(ndev, tx_dropped); return NETDEV_TX_OK; } @@ -639,14 +659,14 @@ static int ntb_netdev_probe(struct device *client_dev) dev->client_dev = client_dev; dev->num_queues = 0; - dev->queues = kzalloc_objs(*dev->queues, NTB_NETDEV_MAX_QUEUES, - GFP_KERNEL); + dev->queues = kzalloc_objs(*dev->queues, NTB_NETDEV_MAX_QUEUES); if (!dev->queues) { rc = -ENOMEM; goto err_free_netdev; } ndev->features = NETIF_F_HIGHDMA; + ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 88255e92b4cd..61a941aa02d9 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -1150,6 +1150,9 @@ static int dp83867_led_polarity_set(struct phy_device *phydev, int index, case PHY_LED_ACTIVE_LOW: polarity = 0; break; + case PHY_LED_ACTIVE_HIGH: + polarity = DP83867_LED_POLARITY(index); + break; default: return -EINVAL; } diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c index d75dae6071ad..9e9a41bf6457 100644 --- a/drivers/net/phy/dp83td510.c +++ b/drivers/net/phy/dp83td510.c @@ -439,6 +439,9 @@ static int dp83td510_led_polarity_set(struct phy_device *phydev, int index, case PHY_LED_ACTIVE_LOW: polarity = 0; break; + case PHY_LED_ACTIVE_HIGH: + polarity = DP83TD510E_LED_POLARITY(index); + break; default: return -EINVAL; } diff --git a/drivers/net/phy/mediatek/mtk-ge.c b/drivers/net/phy/mediatek/mtk-ge.c index 73d9b72f9d9e..96d8ac5154e5 100644 --- a/drivers/net/phy/mediatek/mtk-ge.c +++ b/drivers/net/phy/mediatek/mtk-ge.c @@ -62,10 +62,38 @@ static void mtk_gephy_config_init(struct phy_device *phydev) FIELD_PREP(MTK_MCC_NEARECHO_OFFSET_MASK, 0x3)); } +static int mt7530_phy_probe(struct phy_device *phydev) +{ + /* The MT7530 internal GE PHY has broken EEE: with EEE advertised, + * some link partners fail to establish a stable link (on a 2-pair + * cable, 1000BASE-T training fails and the port loops instead of + * falling back). MediaTek recommends disabling EEE on this PHY. + * Clear the advertisement as early as possible, before anything + * can negotiate EEE with the link partner. + */ + return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); +} + static int mt7530_phy_config_init(struct phy_device *phydev) { + int ret; + mtk_gephy_config_init(phydev); + /* The probe() clear alone is not durable: phy_init_hw() replays only + * ->config_init after a PHY reset, with the register back at its + * EEE-advertising hardware default, and phy_probe() zeroes + * eee_disabled_modes (of_set_phy_eee_broken()) after ->probe already + * ran. Clear the advertisement again and mark EEE disabled, so that + * neither phylib nor userspace can re-enable it; dp83867 disables + * broken EEE from config_init() the same way. + */ + ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); + if (ret) + return ret; + + phy_disable_eee(phydev); + /* Increase post_update_timer */ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11, 0x4b); @@ -100,6 +128,7 @@ static struct phy_driver mtk_gephy_driver[] = { { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530), .name = "MediaTek MT7530 PHY", + .probe = mt7530_phy_probe, .config_init = mt7530_phy_config_init, /* Interrupts are handled by the switch, not the PHY * itself. diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 3ec3bb439109..a1458da8111b 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1630,8 +1630,10 @@ static void phylink_resolve(struct work_struct *w) if (pl->phylink_disable_state) { pl->link_failed = false; + link_state = pl->link_config; link_state.link = false; } else if (pl->link_failed) { + link_state = pl->link_config; link_state.link = false; retrigger = true; } else if (pl->act_link_an_mode == MLO_AN_FIXED) { diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c index 583426d06381..6e6e2b944128 100644 --- a/drivers/net/ppp/ppp_async.c +++ b/drivers/net/ppp/ppp_async.c @@ -63,8 +63,6 @@ struct asyncppp { struct tasklet_struct tsk; - refcount_t refcnt; - struct completion dead; struct ppp_channel chan; /* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE]; }; @@ -115,38 +113,6 @@ static const struct ppp_channel_ops async_ops = { */ /* - * We have a potential race on dereferencing tty->disc_data, - * because the tty layer provides no locking at all - thus one - * cpu could be running ppp_asynctty_receive while another - * calls ppp_asynctty_close, which zeroes tty->disc_data and - * frees the memory that ppp_asynctty_receive is using. The best - * way to fix this is to use a rwlock in the tty struct, but for now - * we use a single global rwlock for all ttys in ppp line discipline. - * - * FIXME: this is no longer true. The _close path for the ldisc is - * now guaranteed to be sane. - */ -static DEFINE_RWLOCK(disc_data_lock); - -static struct asyncppp *ap_get(struct tty_struct *tty) -{ - struct asyncppp *ap; - - read_lock(&disc_data_lock); - ap = tty->disc_data; - if (ap != NULL) - refcount_inc(&ap->refcnt); - read_unlock(&disc_data_lock); - return ap; -} - -static void ap_put(struct asyncppp *ap) -{ - if (refcount_dec_and_test(&ap->refcnt)) - complete(&ap->dead); -} - -/* * Called when a tty is put into PPP line discipline. Called in process * context. */ @@ -180,9 +146,6 @@ ppp_asynctty_open(struct tty_struct *tty) skb_queue_head_init(&ap->rqueue); tasklet_setup(&ap->tsk, ppp_async_process); - refcount_set(&ap->refcnt, 1); - init_completion(&ap->dead); - ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu = PPP_MRU; @@ -203,34 +166,18 @@ ppp_asynctty_open(struct tty_struct *tty) } /* - * Called when the tty is put into another line discipline - * or it hangs up. We have to wait for any cpu currently - * executing in any of the other ppp_asynctty_* routines to - * finish before we can call ppp_unregister_channel and free - * the asyncppp struct. This routine must be called from - * process context, not interrupt or softirq context. + * Called when the tty is put into another line discipline or it hangs up. + * This call is serialized against other ldisc functions. */ static void ppp_asynctty_close(struct tty_struct *tty) { - struct asyncppp *ap; + struct asyncppp *ap = tty->disc_data; - write_lock_irq(&disc_data_lock); - ap = tty->disc_data; - tty->disc_data = NULL; - write_unlock_irq(&disc_data_lock); if (!ap) return; - /* - * We have now ensured that nobody can start using ap from now - * on, but we have to wait for all existing users to finish. - * Note that ppp_unregister_channel ensures that no calls to - * our channel ops (i.e. ppp_async_send/ioctl) are in progress - * by the time it returns. - */ - if (!refcount_dec_and_test(&ap->refcnt)) - wait_for_completion(&ap->dead); + tty->disc_data = NULL; tasklet_kill(&ap->tsk); ppp_unregister_channel(&ap->chan); @@ -241,17 +188,6 @@ ppp_asynctty_close(struct tty_struct *tty) } /* - * Called on tty hangup in process context. - * - * Wait for I/O to driver to complete and unregister PPP channel. - * This is already done by the close routine, so just call that. - */ -static void ppp_asynctty_hangup(struct tty_struct *tty) -{ - ppp_asynctty_close(tty); -} - -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead. */ @@ -281,7 +217,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf, static int ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data; int err, val; int __user *p = (int __user *)arg; @@ -322,7 +258,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) err = tty_mode_ioctl(tty, cmd, arg); } - ap_put(ap); return err; } @@ -331,7 +266,7 @@ static void ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data; unsigned long flags; if (!ap) @@ -341,21 +276,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, spin_unlock_irqrestore(&ap->recv_lock, flags); if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); - ap_put(ap); tty_unthrottle(tty); } static void ppp_asynctty_wakeup(struct tty_struct *tty) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data; clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (!ap) return; set_bit(XMIT_WAKEUP, &ap->xmit_flags); tasklet_schedule(&ap->tsk); - ap_put(ap); } @@ -365,7 +298,6 @@ static struct tty_ldisc_ops ppp_ldisc = { .name = "ppp", .open = ppp_asynctty_open, .close = ppp_asynctty_close, - .hangup = ppp_asynctty_hangup, .read = ppp_asynctty_read, .write = ppp_asynctty_write, .ioctl = ppp_asynctty_ioctl, @@ -810,11 +742,8 @@ process_input_packet(struct asyncppp *ap) err: /* frame had an error, remember that, reset SC_TOSS & SC_ESCAPE */ ap->state = SC_PREV_ERROR; - if (skb) { - /* make skb appear as freshly allocated */ - skb_trim(skb, 0); - skb_reserve(skb, - skb_headroom(skb)); - } + kfree_skb(skb); + ap->rpkt = NULL; } /* Called when the tty driver has data for us. Runs parallel with the diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index 0b1bd1635c39..ebd62a7ab54b 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -38,11 +38,9 @@ #include <linux/ppp-ioctl.h> #include <linux/ppp_channel.h> #include <linux/spinlock.h> -#include <linux/completion.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> -#include <linux/refcount.h> #include <linux/unaligned.h> #include <linux/uaccess.h> @@ -67,8 +65,6 @@ struct syncppp { struct tasklet_struct tsk; - refcount_t refcnt; - struct completion dead_cmp; struct ppp_channel chan; /* interface to generic ppp layer */ }; @@ -117,37 +113,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count) */ /* - * We have a potential race on dereferencing tty->disc_data, - * because the tty layer provides no locking at all - thus one - * cpu could be running ppp_synctty_receive while another - * calls ppp_synctty_close, which zeroes tty->disc_data and - * frees the memory that ppp_synctty_receive is using. The best - * way to fix this is to use a rwlock in the tty struct, but for now - * we use a single global rwlock for all ttys in ppp line discipline. - * - * FIXME: Fixed in tty_io nowadays. - */ -static DEFINE_RWLOCK(disc_data_lock); - -static struct syncppp *sp_get(struct tty_struct *tty) -{ - struct syncppp *ap; - - read_lock(&disc_data_lock); - ap = tty->disc_data; - if (ap != NULL) - refcount_inc(&ap->refcnt); - read_unlock(&disc_data_lock); - return ap; -} - -static void sp_put(struct syncppp *ap) -{ - if (refcount_dec_and_test(&ap->refcnt)) - complete(&ap->dead_cmp); -} - -/* * Called when a tty is put into sync-PPP line discipline. */ static int @@ -177,9 +142,6 @@ ppp_sync_open(struct tty_struct *tty) skb_queue_head_init(&ap->rqueue); tasklet_setup(&ap->tsk, ppp_sync_process); - refcount_set(&ap->refcnt, 1); - init_completion(&ap->dead_cmp); - ap->chan.private = ap; ap->chan.ops = &sync_ops; ap->chan.mtu = PPP_MRU; @@ -201,34 +163,18 @@ ppp_sync_open(struct tty_struct *tty) } /* - * Called when the tty is put into another line discipline - * or it hangs up. We have to wait for any cpu currently - * executing in any of the other ppp_synctty_* routines to - * finish before we can call ppp_unregister_channel and free - * the syncppp struct. This routine must be called from - * process context, not interrupt or softirq context. + * Called when the tty is put into another line discipline or it hangs up. + * This call is serialized against other ldisc functions. */ static void ppp_sync_close(struct tty_struct *tty) { - struct syncppp *ap; + struct syncppp *ap = tty->disc_data; - write_lock_irq(&disc_data_lock); - ap = tty->disc_data; - tty->disc_data = NULL; - write_unlock_irq(&disc_data_lock); if (!ap) return; - /* - * We have now ensured that nobody can start using ap from now - * on, but we have to wait for all existing users to finish. - * Note that ppp_unregister_channel ensures that no calls to - * our channel ops (i.e. ppp_sync_send/ioctl) are in progress - * by the time it returns. - */ - if (!refcount_dec_and_test(&ap->refcnt)) - wait_for_completion(&ap->dead_cmp); + tty->disc_data = NULL; tasklet_kill(&ap->tsk); ppp_unregister_channel(&ap->chan); @@ -238,17 +184,6 @@ ppp_sync_close(struct tty_struct *tty) } /* - * Called on tty hangup in process context. - * - * Wait for I/O to driver to complete and unregister PPP channel. - * This is already done by the close routine, so just call that. - */ -static void ppp_sync_hangup(struct tty_struct *tty) -{ - ppp_sync_close(tty); -} - -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead. */ @@ -273,7 +208,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf, static int ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data; int __user *p = (int __user *)arg; int err, val; @@ -314,7 +249,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) break; } - sp_put(ap); return err; } @@ -323,7 +257,7 @@ static void ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data; unsigned long flags; if (!ap) @@ -333,21 +267,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, spin_unlock_irqrestore(&ap->recv_lock, flags); if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); - sp_put(ap); tty_unthrottle(tty); } static void ppp_sync_wakeup(struct tty_struct *tty) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data; clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); if (!ap) return; set_bit(XMIT_WAKEUP, &ap->xmit_flags); tasklet_schedule(&ap->tsk); - sp_put(ap); } @@ -357,7 +289,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = { .name = "pppsync", .open = ppp_sync_open, .close = ppp_sync_close, - .hangup = ppp_sync_hangup, .read = ppp_sync_read, .write = ppp_sync_write, .ioctl = ppp_synctty_ioctl, @@ -524,17 +455,9 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb) /* prepend address/control fields if necessary */ if ((ap->flags & SC_COMP_AC) == 0 || islcp) { - if (skb_headroom(skb) < 2) { - struct sk_buff *npkt = dev_alloc_skb(skb->len + 2); - if (npkt == NULL) { - kfree_skb(skb); - return NULL; - } - skb_reserve(npkt,2); - skb_copy_from_linear_data(skb, - skb_put(npkt, skb->len), skb->len); - consume_skb(skb); - skb = npkt; + if (skb_cow_head(skb, 2)) { + kfree_skb(skb); + return NULL; } skb_push(skb,2); skb->data[0] = PPP_ALLSTATIONS; diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 068acb052adb..5df657acf3d5 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -282,6 +282,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb) if (len == 0xffff) { netdev_info(dev->net, "router was rebooted, re-enabling ethernet mode"); schedule_work(&priv->reenable_work); + return 0; } else if (len > CX82310_MTU) { netdev_err(dev->net, "RX packet too long: %d B\n", len); return 0; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index c1aec67688ae..71caa3764b23 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1547,10 +1547,10 @@ hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) spin_unlock_irq(&serial->serial_lock); add_wait_queue(&tiocmget->waitq, &wait); for (;;) { + set_current_state(TASK_INTERRUPTIBLE); spin_lock_irq(&serial->serial_lock); memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); spin_unlock_irq(&serial->serial_lock); - set_current_state(TASK_INTERRUPTIBLE); if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 8178a8758cd3..f51cf9cb9421 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1084,6 +1084,7 @@ static const struct usb_device_id products[] = { }, {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0122)}, /* Quectel RG650V */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x013d)}, /* Quectel RG660QB */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ @@ -1446,6 +1447,7 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x2c7c, 0x0316, 3)}, /* Quectel RG255C */ {QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */ {QMI_QUIRK_SET_DTR(0x2cb7, 0x0112, 0)}, /* Fibocom FG132 */ + {QMI_QUIRK_SET_DTR(0x04b7, 0x8217, 8)}, /* Compal EXM-G1x */ {QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */ {QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/ {QMI_FIXED_INTF(0x2692, 0x9025, 4)}, /* Cellient MPL200 (rebranded Qualcomm 05c6:9025) */ diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 459f19f7071e..c1d54339fa2b 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -996,6 +996,12 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan, return -EOPNOTSUPP; } + if (rcu_access_pointer(f->nh) && + !(state & (NUD_PERMANENT | NUD_NOARP))) { + NL_SET_ERR_MSG(extack, "Cannot make a nexthop fdb dynamic"); + return -EOPNOTSUPP; + } + /* Do not allow an externally learned entry to take over an entry added * by the user. */ @@ -1257,6 +1263,11 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], if (err) return err; + if (nhid && !(ndm->ndm_state & (NUD_PERMANENT | NUD_NOARP))) { + NL_SET_ERR_MSG(extack, "A nexthop fdb cannot be dynamic"); + return -EINVAL; + } + if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family) return -EAFNOSUPPORT; @@ -2362,7 +2373,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct ip_tunnel_key key; struct vxlan_dev *vxlan = netdev_priv(dev); const struct iphdr *old_iph; - struct vxlan_metadata _md; + struct vxlan_metadata _md = {}; struct vxlan_metadata *md = &_md; unsigned int pkt_len = skb->len; __be16 src_port = 0, dst_port; diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index d71e1925ecfd..841f42ffecb9 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto, NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed"); return false; } + if (ipv4_is_zeronet(nla_get_in_addr(attr))) { + NL_SET_ERR_MSG_MOD(extack, "IPv4 all-zeros source address is not allowed"); + return false; + } break; #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): { @@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto, NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed"); return false; } + if (ipv6_addr_any(&src)) { + NL_SET_ERR_MSG_MOD(extack, "IPv6 all-zeros source address is not allowed"); + return false; + } break; } #endif diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 0fc55c9169e1..d89a49c6ebb7 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -870,7 +870,7 @@ static struct ath12k_ahb_rproc_info *ath12k_ahb_rproc_info_alloc(struct ath12k_b lockdep_assert_held(&ath12k_rproc_info_lock); - rproc_info = kzalloc_obj(*rproc_info, GFP_KERNEL); + rproc_info = kzalloc_obj(*rproc_info); if (!rproc_info) return NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c index 533870cf443f..6db84c2117f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -514,8 +514,7 @@ void iwl_mld_init_ap_type_tables(struct iwl_mld *mld) return; if (iwl_fw_lookup_cmd_ver(mld->fw, cmd.id, 1) == 1) { - struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd_v1 = - kzalloc(sizeof(*cmd_v1), GFP_KERNEL); + struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd_v1 = kzalloc_obj(*cmd_v1); if (!cmd_v1) return; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index a118a301564c..40546005c743 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -477,18 +477,23 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) for (offset = 0; offset < len; offset += clc_len) { if (len - offset < sizeof(*clc)) { + dev_err(mdev->dev, "Invalid CLC record\n"); ret = -EINVAL; goto out; } clc = (const struct mt7921_clc *)(clc_base + offset); clc_len = le32_to_cpu(clc->len); - if (clc_len < sizeof(*clc) || clc_len > len - offset || - clc->idx >= ARRAY_SIZE(phy->clc)) { + if (clc_len < sizeof(*clc) || clc_len > len - offset) { + dev_err(mdev->dev, "Invalid CLC record\n"); ret = -EINVAL; goto out; } + /* Newer firmware may add records this driver does not use yet */ + if (clc->idx >= ARRAY_SIZE(phy->clc)) + continue; + /* do not init buf again if chip reset triggered */ if (phy->clc[clc->idx]) continue; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c index 4a8ea4624fee..fcdeb65cd8ef 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -289,7 +289,7 @@ int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2) goto err; } - regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + regd = kzalloc_flex(*regd, reg_rules, num_of_rules); if (!regd) { ret = -ENOMEM; goto err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 84b55f008b3d..5993b31e1aae 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1105,7 +1105,7 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, mlink = &msta->deflink; is_deflink = true; } else { - mlink = kzalloc(sizeof(*mlink), GFP_KERNEL); + mlink = kzalloc_obj(*mlink); if (!mlink) { err = -ENOMEM; break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c index f4beb7f52043..57f1736b54a3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c @@ -336,7 +336,7 @@ int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2) goto err; } - regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + regd = kzalloc_flex(*regd, reg_rules, num_of_rules); if (!regd) { ret = -ENOMEM; goto err; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c index 946dd7956e4a..b468051fbe68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c @@ -323,7 +323,8 @@ int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default) const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa; int i; - if (!phy->acpisar || !((struct mt792x_acpi_sar *)phy->acpisar)->dyn) + if (!capa || !phy->acpisar || + !((struct mt792x_acpi_sar *)phy->acpisar)->dyn) return 0; /* When ACPI SAR enabled in HW, we should apply rules for .frp diff --git a/drivers/net/wireless/morsemicro/mm81x/mac.c b/drivers/net/wireless/morsemicro/mm81x/mac.c index 08ca116a68b4..0fa80b1488aa 100644 --- a/drivers/net/wireless/morsemicro/mm81x/mac.c +++ b/drivers/net/wireless/morsemicro/mm81x/mac.c @@ -706,8 +706,7 @@ static int mm81x_hw_scan_h_init_chan_list(struct mm81x_hw_scan_params *params, params->num_chans = 0; params->allocated_chans = 0; - params->channels = kcalloc(chans_to_allocate, sizeof(*params->channels), - GFP_KERNEL); + params->channels = kzalloc_objs(*params->channels, chans_to_allocate); if (!params->channels) return -ENOMEM; @@ -728,8 +727,8 @@ static int mm81x_hw_scan_h_init_chan_list(struct mm81x_hw_scan_params *params, } } - params->powers_qdbm = kmalloc_array( - num_pwrs_coarse, sizeof(*params->powers_qdbm), GFP_KERNEL); + params->powers_qdbm = kmalloc_objs(*params->powers_qdbm, + num_pwrs_coarse); if (!params->powers_qdbm) return -ENOMEM; @@ -822,7 +821,7 @@ __mm81x_hw_scan_h_init_params(struct mm81x *mors) struct mm81x_hw_scan_params *params = mors->hw_scan.params; if (!params) { - params = kzalloc_obj(*params, GFP_KERNEL); + params = kzalloc_obj(*params); if (params) mors->hw_scan.params = params; } else { diff --git a/drivers/net/wireless/morsemicro/mm81x/yaps.c b/drivers/net/wireless/morsemicro/mm81x/yaps.c index e98a2a58726f..2d1728c1a6de 100644 --- a/drivers/net/wireless/morsemicro/mm81x/yaps.c +++ b/drivers/net/wireless/morsemicro/mm81x/yaps.c @@ -22,15 +22,13 @@ static int mm81x_yaps_alloc_pkt_buffers(struct mm81x_yaps *yaps) { - yaps->hw.to_chip_pkts = kcalloc(MAX_PKTS_PER_TX_TXN, - sizeof(*yaps->hw.to_chip_pkts), - GFP_KERNEL); + yaps->hw.to_chip_pkts = kzalloc_objs(*yaps->hw.to_chip_pkts, + MAX_PKTS_PER_TX_TXN); if (!yaps->hw.to_chip_pkts) return -ENOMEM; - yaps->hw.from_chip_pkts = kcalloc(MAX_PKTS_PER_RX_TXN, - sizeof(*yaps->hw.from_chip_pkts), - GFP_KERNEL); + yaps->hw.from_chip_pkts = kzalloc_objs(*yaps->hw.from_chip_pkts, + MAX_PKTS_PER_RX_TXN); if (!yaps->hw.from_chip_pkts) { kfree(yaps->hw.to_chip_pkts); yaps->hw.to_chip_pkts = NULL; diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c index 5cc8cdf594d3..86304ea3331c 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c +++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c @@ -641,7 +641,7 @@ nxpwifi_cfg80211_set_wiphy_params(struct wiphy *wiphy, int radio_idx, u32 change switch (priv->bss_role) { case NXPWIFI_BSS_ROLE_UAP: - bss_cfg = kzalloc_obj(*bss_cfg, GFP_KERNEL); + bss_cfg = kzalloc_obj(*bss_cfg); if (!bss_cfg) { ret = -ENOMEM; break; @@ -1713,7 +1713,7 @@ static int nxpwifi_cfg80211_start_ap(struct wiphy *wiphy, if (!nxpwifi_is_channel_setting_allowable(priv, params->chandef.chan)) return -EOPNOTSUPP; - bss_cfg = kzalloc_obj(*bss_cfg, GFP_KERNEL); + bss_cfg = kzalloc_obj(*bss_cfg); if (!bss_cfg) return -ENOMEM; @@ -1866,7 +1866,7 @@ nxpwifi_cfg80211_scan(struct wiphy *wiphy, if (!nxpwifi_stop_bg_scan(priv)) cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0); - user_scan_cfg = kzalloc_obj(*user_scan_cfg, GFP_KERNEL); + user_scan_cfg = kzalloc_obj(*user_scan_cfg); if (!user_scan_cfg) return -ENOMEM; @@ -1973,7 +1973,7 @@ nxpwifi_cfg80211_sched_scan_start(struct wiphy *wiphy, request->n_channels, request->scan_plans->interval, (int)request->ie_len); - bgscan_cfg = kzalloc_obj(*bgscan_cfg, GFP_KERNEL); + bgscan_cfg = kzalloc_obj(*bgscan_cfg); if (!bgscan_cfg) return -ENOMEM; @@ -2304,7 +2304,7 @@ nxpwifi_setup_he_caps(struct nxpwifi_private *priv, if (!hw_he_cap_len) return; - iftype_data = kmalloc_obj(*iftype_data, GFP_KERNEL); + iftype_data = kmalloc_obj(*iftype_data); if (!iftype_data) return; memset(iftype_data, 0, sizeof(*iftype_data)); @@ -2759,7 +2759,7 @@ static int nxpwifi_set_mef_filter(struct nxpwifi_private *priv, if (wowlan->n_patterns || wowlan->magic_pkt) num_entries++; - mef_entry = kzalloc_objs(*mef_entry, num_entries, GFP_KERNEL); + mef_entry = kzalloc_objs(*mef_entry, num_entries); if (!mef_entry) return -ENOMEM; @@ -3227,7 +3227,7 @@ static int nxpwifi_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, if (!tb[NXPWIFI_TM_ATTR_DATA]) return -EINVAL; - hostcmd = kzalloc_obj(*hostcmd, GFP_KERNEL); + hostcmd = kzalloc_obj(*hostcmd); if (!hostcmd) return -ENOMEM; diff --git a/drivers/net/wireless/nxp/nxpwifi/cmdevt.c b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c index 4eb17ada5db8..fc1950a125b6 100644 --- a/drivers/net/wireless/nxp/nxpwifi/cmdevt.c +++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c @@ -302,7 +302,7 @@ int nxpwifi_alloc_cmd_buffer(struct nxpwifi_adapter *adapter) /* Allocate and initialize struct cmd_ctrl_node */ cmd_array = kzalloc_objs(struct cmd_ctrl_node, - NXPWIFI_NUM_OF_CMD_BUFFER, GFP_KERNEL); + NXPWIFI_NUM_OF_CMD_BUFFER); if (!cmd_array) return -ENOMEM; diff --git a/drivers/net/wireless/nxp/nxpwifi/ie.c b/drivers/net/wireless/nxp/nxpwifi/ie.c index 158755c0c905..86b18d50feaf 100644 --- a/drivers/net/wireless/nxp/nxpwifi/ie.c +++ b/drivers/net/wireless/nxp/nxpwifi/ie.c @@ -143,7 +143,7 @@ nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv, u16 len; int ret; - ap_custom_ie = kzalloc_obj(*ap_custom_ie, GFP_KERNEL); + ap_custom_ie = kzalloc_obj(*ap_custom_ie); if (!ap_custom_ie) return -ENOMEM; @@ -209,7 +209,7 @@ static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len, vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); if (vendor_ie) { if (!*ie_ptr) { - *ie_ptr = kzalloc_obj(struct nxpwifi_ie, GFP_KERNEL); + *ie_ptr = kzalloc_obj(struct nxpwifi_ie); if (!*ie_ptr) return -ENOMEM; ie = *ie_ptr; @@ -309,7 +309,7 @@ static int nxpwifi_uap_parse_tail_ies(struct nxpwifi_private *priv, if (!info->tail || !info->tail_len) return 0; - gen_ie = kzalloc_obj(*gen_ie, GFP_KERNEL); + gen_ie = kzalloc_obj(*gen_ie); if (!gen_ie) return -ENOMEM; @@ -417,7 +417,7 @@ int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) int ret = 0; if (priv->gen_idx != NXPWIFI_AUTO_IDX_MASK) { - gen_ie = kmalloc_obj(*gen_ie, GFP_KERNEL); + gen_ie = kmalloc_obj(*gen_ie); if (!gen_ie) return -ENOMEM; @@ -434,7 +434,7 @@ int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) } if (priv->beacon_idx != NXPWIFI_AUTO_IDX_MASK) { - beacon_ie = kmalloc_obj(*beacon_ie, GFP_KERNEL); + beacon_ie = kmalloc_obj(*beacon_ie); if (!beacon_ie) { ret = -ENOMEM; goto done; @@ -444,7 +444,7 @@ int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) beacon_ie->ie_length = 0; } if (priv->proberesp_idx != NXPWIFI_AUTO_IDX_MASK) { - pr_ie = kmalloc_obj(*pr_ie, GFP_KERNEL); + pr_ie = kmalloc_obj(*pr_ie); if (!pr_ie) { ret = -ENOMEM; goto done; @@ -454,7 +454,7 @@ int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv) pr_ie->ie_length = 0; } if (priv->assocresp_idx != NXPWIFI_AUTO_IDX_MASK) { - ar_ie = kmalloc_obj(*ar_ie, GFP_KERNEL); + ar_ie = kmalloc_obj(*ar_ie); if (!ar_ie) { ret = -ENOMEM; goto done; diff --git a/drivers/net/wireless/nxp/nxpwifi/init.c b/drivers/net/wireless/nxp/nxpwifi/init.c index b128fc9fe31a..7c75dca5d0a8 100644 --- a/drivers/net/wireless/nxp/nxpwifi/init.c +++ b/drivers/net/wireless/nxp/nxpwifi/init.c @@ -20,7 +20,7 @@ static int nxpwifi_add_bss_prio_tbl(struct nxpwifi_private *priv) struct nxpwifi_bss_prio_node *bss_prio; struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - bss_prio = kzalloc_obj(*bss_prio, GFP_KERNEL); + bss_prio = kzalloc_obj(*bss_prio); if (!bss_prio) return -ENOMEM; diff --git a/drivers/net/wireless/nxp/nxpwifi/main.c b/drivers/net/wireless/nxp/nxpwifi/main.c index b4c63829024a..55b962430f37 100644 --- a/drivers/net/wireless/nxp/nxpwifi/main.c +++ b/drivers/net/wireless/nxp/nxpwifi/main.c @@ -32,7 +32,7 @@ static struct nxpwifi_adapter *nxpwifi_register(void *card, struct device *dev, int ret = 0; int i; - adapter = kzalloc_obj(*adapter, GFP_KERNEL); + adapter = kzalloc_obj(*adapter); if (!adapter) return ERR_PTR(-ENOMEM); @@ -55,7 +55,7 @@ static struct nxpwifi_adapter *nxpwifi_register(void *card, struct device *dev, for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) { /* Allocate memory for private structure */ adapter->priv[i] = - kzalloc_obj(struct nxpwifi_private, GFP_KERNEL); + kzalloc_obj(struct nxpwifi_private); if (!adapter->priv[i]) { ret = -ENOMEM; goto error; @@ -1106,7 +1106,7 @@ void nxpwifi_drv_info_dump(struct nxpwifi_adapter *adapter) p += adapter->if_ops.reg_dump(adapter, p); } p += sprintf(p, "\n=== more debug information\n"); - debug_info = kzalloc_obj(*debug_info, GFP_KERNEL); + debug_info = kzalloc_obj(*debug_info); if (debug_info) { for (i = 0; i < adapter->priv_num; i++) { if (!adapter->priv[i]->netdev) @@ -1242,7 +1242,7 @@ void nxpwifi_init_priv_params(struct nxpwifi_private *priv, if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA || GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) { - priv->hist_data = kmalloc_obj(*priv->hist_data, GFP_KERNEL); + priv->hist_data = kmalloc_obj(*priv->hist_data); if (priv->hist_data) nxpwifi_hist_data_reset(priv); } diff --git a/drivers/net/wireless/nxp/nxpwifi/scan.c b/drivers/net/wireless/nxp/nxpwifi/scan.c index b77056983e83..67d7ff2f10c5 100644 --- a/drivers/net/wireless/nxp/nxpwifi/scan.c +++ b/drivers/net/wireless/nxp/nxpwifi/scan.c @@ -1341,15 +1341,14 @@ int nxpwifi_scan_networks(struct nxpwifi_private *priv, adapter->scan_processing = true; spin_unlock_bh(&adapter->nxpwifi_cmd_lock); - scan_cfg_out = kzalloc_obj(union nxpwifi_scan_cmd_config_tlv, - GFP_KERNEL); + scan_cfg_out = kzalloc_obj(union nxpwifi_scan_cmd_config_tlv); if (!scan_cfg_out) { ret = -ENOMEM; goto done; } scan_chan_list = kzalloc_objs(struct nxpwifi_chan_scan_param_set, - NXPWIFI_USER_SCAN_CHAN_MAX, GFP_KERNEL); + NXPWIFI_USER_SCAN_CHAN_MAX); if (!scan_chan_list) { kfree(scan_cfg_out); ret = -ENOMEM; @@ -1471,7 +1470,7 @@ static int nxpwifi_save_hidden_ssid_channels(struct nxpwifi_private *priv, int chid; /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL); + bss_desc = kzalloc_obj(*bss_desc); if (!bss_desc) return -ENOMEM; @@ -1512,7 +1511,7 @@ static int nxpwifi_update_curr_bss_params(struct nxpwifi_private *priv, int ret; /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL); + bss_desc = kzalloc_obj(*bss_desc); if (!bss_desc) return -ENOMEM; @@ -1751,7 +1750,7 @@ nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private *priv) nxpwifi_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); return 0; } - user_scan_cfg = kzalloc_obj(*user_scan_cfg, GFP_KERNEL); + user_scan_cfg = kzalloc_obj(*user_scan_cfg); if (!user_scan_cfg) return -ENOMEM; @@ -2258,7 +2257,7 @@ int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv) return 0; } - bgscan_cfg = kzalloc_obj(*bgscan_cfg, GFP_KERNEL); + bgscan_cfg = kzalloc_obj(*bgscan_cfg); if (!bgscan_cfg) return -ENOMEM; diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c index 702fa1531da1..56cf63fbf7fe 100644 --- a/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cfg.c @@ -245,7 +245,7 @@ int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss, return -EINVAL; /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL); + bss_desc = kzalloc_obj(*bss_desc); if (!bss_desc) return -ENOMEM; diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c index 5e8ffd306b31..9bfa4aebc3e5 100644 --- a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c +++ b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c @@ -2822,7 +2822,7 @@ nxpwifi_create_custom_regdomain(struct nxpwifi_private *priv, if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES)) return ERR_PTR(-EINVAL); - regd = kzalloc_flex(*regd, reg_rules, num_chan, GFP_KERNEL); + regd = kzalloc_flex(*regd, reg_rules, num_chan); if (!regd) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_event.c b/drivers/net/wireless/nxp/nxpwifi/uap_event.c index 9f717a3d7ec5..ab5c15686f54 100644 --- a/drivers/net/wireless/nxp/nxpwifi/uap_event.c +++ b/drivers/net/wireless/nxp/nxpwifi/uap_event.c @@ -88,7 +88,7 @@ nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv) struct nxpwifi_sta_node *node; int len, i; - sinfo = kzalloc_obj(*sinfo, GFP_KERNEL); + sinfo = kzalloc_obj(*sinfo); if (!sinfo) return -ENOMEM; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 24f42b4650ba..09842ca68110 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -1002,8 +1002,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, 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); + nd_region->lane = kzalloc_objs(*nd_region->lane, nd_region->num_lanes); if (!nd_region->lane) goto err_percpu; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 1322c678f4eb..beea23d04a70 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -155,8 +155,6 @@ static const struct class nvme_ns_chr_class = { }; static void nvme_put_subsystem(struct nvme_subsystem *subsys); -static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl, - unsigned nsid); static void nvme_update_keep_alive(struct nvme_ctrl *ctrl, struct nvme_command *cmd); static int nvme_get_log_lsi(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, @@ -1612,7 +1610,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, } if (nvme_multi_css(ctrl) && !csi_seen) { - dev_warn(ctrl->device, "Command set not reported for nsid:%d\n", + dev_warn(ctrl->device, "Command set not reported for nsid:%u\n", info->nsid); status = -EINVAL; } @@ -2341,14 +2339,6 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info) size_t size; int i, ret; - /* - * The FDP configuration is static for the lifetime of the namespace, - * so return immediately if we've already registered this namespace's - * streams. - */ - if (head->nr_plids) - return 0; - ret = nvme_get_features(ctrl, NVME_FEAT_FDP, info->endgid, NULL, 0, &fdp); if (ret) { @@ -2382,8 +2372,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info) if (!head->nr_plids) goto free; - head->plids = kcalloc(head->nr_plids, sizeof(*head->plids), - GFP_KERNEL); + head->plids = kzalloc_objs(*head->plids, head->nr_plids); if (!head->plids) { dev_warn(ctrl->device, "failed to allocate %u FDP placement IDs\n", @@ -2395,6 +2384,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info) for (i = 0; i < head->nr_plids; i++) head->plids[i] = le16_to_cpu(ruhs->ruhsd[i].pid); + head->write_stream_granularity = min(info->runs, U32_MAX); free: kfree(ruhs); return ret; @@ -2442,12 +2432,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, goto out; } - if (ns->ctrl->ctratt & NVME_CTRL_ATTR_FDPS) { - ret = nvme_query_fdp_info(ns, info); - if (ret < 0) - goto out; - } - if (nvme_invalid_lba_sz(le64_to_cpu(id->nsze), id->lbaf[lbaf].ds - SECTOR_SHIFT, &capacity)) { dev_warn_once(ns->ctrl->device, @@ -2468,9 +2452,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, if (!nvme_update_disk_info(ns, id, nvm, &lim)) capacity = 0; + /* + * A failed zone info query leaves zi zero-initialized, so skip the + * zoned limits update instead of configuring the queue from it. + * During a revalidation that keeps the zone geometry the queue was + * last validated with; on a first scan the namespace is registered + * without zoned limits, so that it is still available as a handle + * for admin commands. + */ if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) && - ns->head->ids.csi == NVME_CSI_ZNS) - nvme_update_zone_info(ns, &lim, &zi); + ns->head->ids.csi == NVME_CSI_ZNS) { + if (zi.zone_size) + nvme_update_zone_info(ns, &lim, &zi); + else + dev_warn(ns->ctrl->device, + "zone info query failed for nsid %u, %s\n", + ns->head->ns_id, + blk_queue_is_zoned(ns->disk->queue) ? + "keeping the previous zone limits" : + "not enabling zoned mode"); + } if ((ns->ctrl->vwc & NVME_CTRL_VWC_PRESENT) && !info->no_vwc) lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA; @@ -2490,10 +2491,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, capacity = 0; lim.max_write_streams = ns->head->nr_plids; - if (lim.max_write_streams) - lim.write_stream_granularity = min(info->runs, U32_MAX); - else - lim.write_stream_granularity = 0; + lim.write_stream_granularity = ns->head->write_stream_granularity; /* * Only set the DEAC bit if the device guarantees that reads from @@ -4001,10 +3999,11 @@ static void nvme_add_ns_cdev(struct nvme_ns *ns) set_bit(NVME_NS_CDEV_LIVE, &ns->flags); } -static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, +static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) - __must_hold(&ctrl->subsys->lock) + __must_hold(&ns->ctrl->subsys->lock) { + struct nvme_ctrl *ctrl = ns->ctrl; struct nvme_ns_head *head; size_t size = sizeof(*head); int ret = -ENOMEM; @@ -4032,6 +4031,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, ratelimit_state_init(&head->rs_nuse, 5 * HZ, 1); ratelimit_set_flags(&head->rs_nuse, RATELIMIT_MSG_ON_RELEASE); kref_init(&head->ref); + ns->head = head; if (head->ids.csi) { ret = nvme_get_effects_log(ctrl, head->ids.csi, &head->effects); @@ -4040,21 +4040,30 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, } else head->effects = ctrl->effects; + if (ctrl->ctratt & NVME_CTRL_ATTR_FDPS) { + ret = nvme_query_fdp_info(ns, info); + if (ret < 0) + goto out_cleanup_srcu; + } + ret = nvme_mpath_alloc_disk(ctrl, head); if (ret) - goto out_cleanup_srcu; + goto out_cleanup_fdp; list_add_tail(&head->entry, &ctrl->subsys->nsheads); kref_get(&ctrl->subsys->ref); return head; +out_cleanup_fdp: + kfree(head->plids); out_cleanup_srcu: cleanup_srcu_struct(&head->srcu); out_ida_remove: ida_free(&ctrl->subsys->ns_ida, head->instance); out_free_head: kfree(head); + ns->head = NULL; out: if (ret > 0) ret = blk_status_to_errno(nvme_error_status(ret)); @@ -4116,13 +4125,13 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) ((ns->ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) && info->is_shared)) { dev_err(ctrl->device, - "ignoring nsid %d because of duplicate IDs\n", + "ignoring nsid %u because of duplicate IDs\n", info->nsid); return ret; } dev_err(ctrl->device, - "clearing duplicate IDs for nsid %d\n", info->nsid); + "clearing duplicate IDs for nsid %u\n", info->nsid); dev_err(ctrl->device, "use of /dev/disk/by-id/ may cause data corruption\n"); memset(&info->ids.nguid, 0, sizeof(info->ids.nguid)); @@ -4137,11 +4146,11 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) ret = nvme_subsys_check_duplicate_ids(ctrl->subsys, &info->ids); if (ret) { dev_err(ctrl->device, - "duplicate IDs in subsystem for nsid %d\n", + "duplicate IDs in subsystem for nsid %u\n", info->nsid); goto out_unlock; } - head = nvme_alloc_ns_head(ctrl, info); + head = nvme_alloc_ns_head(ns, info); if (IS_ERR(head)) { ret = PTR_ERR(head); goto out_unlock; @@ -4151,20 +4160,20 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) if ((!info->is_shared || !head->shared) && !list_empty(&head->list)) { dev_err(ctrl->device, - "Duplicate unshared namespace %d\n", + "Duplicate unshared namespace %u\n", info->nsid); goto out_put_ns_head; } if (!nvme_ns_ids_equal(&head->ids, &info->ids)) { dev_err(ctrl->device, - "IDs don't match for shared namespace %d\n", + "IDs don't match for shared namespace %u\n", info->nsid); goto out_put_ns_head; } if (!multipath) { dev_warn(ctrl->device, - "Found shared namespace %d, but multipathing not supported.\n", + "Found shared namespace %u, but multipathing not supported.\n", info->nsid); dev_warn_once(ctrl->device, "Shared namespace support requires core_nvme.multipath=Y.\n"); @@ -4333,6 +4342,9 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info) last_path = true; } mutex_unlock(&ctrl->subsys->lock); + + /* guarantee not available in head->list */ + synchronize_srcu(&ns->head->srcu); if (last_path) nvme_put_ns_head(ns->head); nvme_put_ns_head(ns->head); @@ -4410,7 +4422,7 @@ static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info) if (!nvme_ns_ids_equal(&ns->head->ids, &info->ids)) { dev_err(ns->ctrl->device, - "identifiers changed for nsid %d\n", ns->head->ns_id); + "identifiers changed for nsid %u\n", ns->head->ns_id); goto out; } @@ -4437,7 +4449,7 @@ static void nvme_scan_ns(struct nvme_ctrl *ctrl, unsigned nsid) if (info.ids.csi != NVME_CSI_NVM && !nvme_multi_css(ctrl)) { dev_warn(ctrl->device, - "command set not reported for nsid: %d\n", nsid); + "command set not reported for nsid: %u\n", nsid); return; } @@ -4501,15 +4513,16 @@ static void nvme_scan_ns_async(void *data, async_cookie_t cookie) nvme_scan_ns(scan_info->ctrl, nsid); } -static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl, - unsigned nsid) +static void nvme_remove_nsid_range(struct nvme_ctrl *ctrl, u32 start, u32 end) { struct nvme_ns *ns, *next; LIST_HEAD(rm_list); mutex_lock(&ctrl->namespaces_lock); list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) { - if (ns->head->ns_id > nsid) { + if (ns->head->ns_id >= end) + break; + if (ns->head->ns_id > start) { list_del_rcu(&ns->list); synchronize_srcu(&ctrl->srcu); list_add_tail_rcu(&ns->list, &rm_list); @@ -4559,13 +4572,14 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl) goto out; async_schedule_domain(nvme_scan_ns_async, &scan_info, &domain); - while (++prev < nsid) - nvme_ns_remove_by_nsid(ctrl, prev); + if (prev + 1 < nsid) + nvme_remove_nsid_range(ctrl, prev, nsid); + prev = max(prev + 1, nsid); } async_synchronize_full_domain(&domain); } out: - nvme_remove_invalid_namespaces(ctrl, prev); + nvme_remove_nsid_range(ctrl, prev, UINT_MAX); free: async_synchronize_full_domain(&domain); kfree(ns_list); @@ -4585,7 +4599,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl) for (i = 1; i <= nn; i++) nvme_scan_ns(ctrl, i); - nvme_remove_invalid_namespaces(ctrl, nn); + nvme_remove_nsid_range(ctrl, nn, UINT_MAX); } static void nvme_clear_changed_ns_log(struct nvme_ctrl *ctrl) diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index fd5abd04e080..59f823dfbbcc 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -1028,6 +1028,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { pr_err("Invalid DH-CHAP secret %s\n", p); + kfree_sensitive(p); ret = -EINVAL; goto out; } @@ -1042,6 +1043,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { pr_err("Invalid DH-CHAP secret %s\n", p); + kfree_sensitive(p); ret = -EINVAL; goto out; } diff --git a/drivers/nvme/host/fault_inject.c b/drivers/nvme/host/fault_inject.c index 105d6cb41c72..783e1999fef4 100644 --- a/drivers/nvme/host/fault_inject.c +++ b/drivers/nvme/host/fault_inject.c @@ -42,9 +42,11 @@ void nvme_fault_inject_init(struct nvme_fault_inject *fault_inj, } fault_inj->parent = parent; - /* create debugfs for status code and dont_retry */ + /* create debugfs for opcode, status code, and dont_retry */ + fault_inj->opcode = 0xffff; fault_inj->status = NVME_SC_INVALID_OPCODE; fault_inj->dont_retry = true; + debugfs_create_x16("opcode", 0600, dir, &fault_inj->opcode); debugfs_create_x16("status", 0600, dir, &fault_inj->status); debugfs_create_bool("dont_retry", 0600, dir, &fault_inj->dont_retry); } @@ -59,6 +61,7 @@ void nvme_should_fail(struct request *req) { struct gendisk *disk = req->q->disk; struct nvme_fault_inject *fault_inject = NULL; + struct nvme_command *cmd = nvme_req(req)->cmd; u16 status; if (disk) { @@ -72,7 +75,14 @@ void nvme_should_fail(struct request *req) fault_inject = &nvme_req(req)->ctrl->fault_inject; } - if (fault_inject && should_fail(&fault_inject->attr, 1)) { + if (!fault_inject) + return; + + if (fault_inject->opcode <= 0xff && + fault_inject->opcode != cmd->common.opcode) + return; + + if (should_fail(&fault_inject->attr, 1)) { /* inject status code and DNR bit */ status = fault_inject->status; if (fault_inject->dont_retry) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 023710e08e0d..48454cb7a0fc 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2364,9 +2364,15 @@ nvme_fc_ctrl_free(struct kref *ref) struct nvme_fc_ctrl *ctrl = container_of(ref, struct nvme_fc_ctrl, ref); unsigned long flags; + bool owns_opts; - /* remove from rport list */ + /* + * Presence on the rport list means nvme_fc_init_ctrl() completed, + * and with it ownership of the fabrics options passed to it. If it + * failed instead, the options still belong to nvmf_create_ctrl(). + */ spin_lock_irqsave(&ctrl->rport->lock, flags); + owns_opts = !list_empty(&ctrl->ctrl_list); list_del(&ctrl->ctrl_list); spin_unlock_irqrestore(&ctrl->rport->lock, flags); @@ -2376,7 +2382,7 @@ nvme_fc_ctrl_free(struct kref *ref) nvme_fc_rport_put(ctrl->rport); ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum); - if (ctrl->ctrl.opts) + if (owns_opts) nvmf_free_options(ctrl->ctrl.opts); kfree(ctrl); } @@ -3575,14 +3581,14 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) { dev_err(ctrl->ctrl.device, "NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum); - goto fail_ctrl; + goto fail_unlist; } if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) { dev_err(ctrl->ctrl.device, "NVME-FC{%d}: failed to schedule initial connect\n", ctrl->cnum); - goto fail_ctrl; + goto fail_unlist; } flush_delayed_work(&ctrl->connect_work); @@ -3593,14 +3599,22 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, return &ctrl->ctrl; +fail_unlist: + /* + * Leaving the list hands the options back to nvmf_create_ctrl(); + * see nvme_fc_ctrl_free(). Re-init so that list_empty() there + * reports the controller as unlisted. + */ + spin_lock_irqsave(&rport->lock, flags); + list_del_init(&ctrl->ctrl_list); + spin_unlock_irqrestore(&rport->lock, flags); + fail_ctrl: nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING); cancel_work_sync(&ctrl->ioerr_work); cancel_work_sync(&ctrl->ctrl.reset_work); cancel_delayed_work_sync(&ctrl->connect_work); - ctrl->ctrl.opts = NULL; - if (ctrl->ctrl.admin_tagset) nvme_remove_admin_tag_set(&ctrl->ctrl); /* initiate nvme ctrl ref counting teardown */ diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 75e5d5a8a77c..2cff9fcbf740 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -323,6 +323,7 @@ struct nvme_fault_inject { #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS struct fault_attr attr; struct dentry *parent; + u16 opcode; bool dont_retry; /* DNR, do not retry */ u16 status; /* status code */ #endif @@ -571,6 +572,7 @@ struct nvme_ns_head { u16 nr_plids; u16 *plids; + u32 write_stream_granularity; #ifdef CONFIG_NVME_MULTIPATH struct bio_list requeue_list __guarded_by(&requeue_lock); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index da93b505d239..5440cf18b55b 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -201,7 +201,7 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) count++; } - qlist = kcalloc(count, sizeof(*qlist), GFP_KERNEL); + qlist = kzalloc_objs(*qlist, count); if (!qlist) { err = -ENOMEM; goto out_free_val; diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 538385138e45..9cb811a2ce1f 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -2036,7 +2036,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, struct ib_device *dev; bool queue_ready = test_bit(NVME_RDMA_Q_LIVE, &queue->flags); blk_status_t ret; - int err; + int err = 0; WARN_ON_ONCE(rq->tag < 0); @@ -2092,16 +2092,18 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx, err_unmap: nvme_rdma_unmap_data(queue, rq); err: - if (err == -EIO) - ret = nvme_host_path_error(rq); - else if (err == -ENOMEM || err == -EAGAIN) - ret = BLK_STS_RESOURCE; - else - ret = BLK_STS_IOERR; - nvme_cleanup_cmd(rq); + if (err != -EIO) { + nvme_cleanup_cmd(rq); + if (err == -ENOMEM || err == -EAGAIN) + ret = BLK_STS_RESOURCE; + else + ret = BLK_STS_IOERR; + } unmap_qe: ib_dma_unmap_single(dev, req->sqe.dma, sizeof(struct nvme_command), DMA_TO_DEVICE); + if (err == -EIO) + return nvme_host_path_error(rq); return ret; } diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index abf8edaae371..02a2490a9ed7 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -166,7 +166,7 @@ static DEVICE_ATTR_RO(eui); static ssize_t nsid_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id); + return sysfs_emit(buf, "%u\n", dev_to_ns_head(dev)->ns_id); } static DEVICE_ATTR_RO(nsid); diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 5fda9661bdb7..921934028e0b 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -413,8 +413,13 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req, * if we're the first on the send_list and we can try to send * directly, otherwise queue io_work. Also, only do that if we * are on the same cpu, so we don't introduce contention. + * + * TLS kTLS send takes ctx->tx_lock while blk_mq holds set->srcu. + * lockdep reports circular locking via elevator_lock. Defer TLS + * sends to the io workqueue instead of inline from this path. */ if (queue->io_cpu == raw_smp_processor_id() && + !nvme_tcp_queue_tls(queue) && empty && mutex_trylock(&queue->send_mutex)) { nvme_tcp_send_all(queue); mutex_unlock(&queue->send_mutex); @@ -684,6 +689,13 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue, return -ENOENT; } + if (rq_data_dir(rq) != READ) { + dev_err(queue->ctrl->ctrl.device, + "queue %d tag %#x unexpected data for a write\n", + nvme_tcp_queue_id(queue), rq->tag); + return -EPROTO; + } + req = blk_mq_rq_to_pdu(rq); if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) { dev_err(queue->ctrl->ctrl.device, diff --git a/drivers/nvme/host/zns.c b/drivers/nvme/host/zns.c index 2a152e87bd76..e31ec6f4f94f 100644 --- a/drivers/nvme/host/zns.c +++ b/drivers/nvme/host/zns.c @@ -48,12 +48,12 @@ int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf, NVME_CMD_EFFECTS_CSUPP)) { if (test_and_clear_bit(NVME_NS_FORCE_RO, &ns->flags)) dev_warn(ns->ctrl->device, - "Zone Append supported for zoned namespace:%d. Remove read-only mode\n", + "Zone Append supported for zoned namespace:%u. Remove read-only mode\n", ns->head->ns_id); } else { set_bit(NVME_NS_FORCE_RO, &ns->flags); dev_warn(ns->ctrl->device, - "Zone Append not supported for zoned namespace:%d. Forcing to read-only mode\n", + "Zone Append not supported for zoned namespace:%u. Forcing to read-only mode\n", ns->head->ns_id); } diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index edb9627d97b0..a55319bcdbd1 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -238,6 +238,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq) sq->dhchap_skey = NULL; } +void nvmet_auth_sq_destroy(struct nvmet_sq *sq) +{ + cancel_delayed_work_sync(&sq->auth_expired_work); + nvmet_auth_sq_free(sq); +} + void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) { ctrl->shash_id = 0; diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 413ee2d16d29..6286e38436dd 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -814,7 +814,7 @@ static ssize_t nvmet_ns_resv_enable_store(struct config_item *item, mutex_lock(&ns->subsys->lock); if (ns->enabled) { - pr_err("the ns:%d is already enabled.\n", ns->nsid); + pr_err("the ns:%u is already enabled.\n", ns->nsid); mutex_unlock(&ns->subsys->lock); return -EINVAL; } @@ -880,7 +880,7 @@ static struct config_group *nvmet_ns_make(struct config_group *group, goto out; config_group_init_type_name(&ns->group, name, &nvmet_ns_type); - pr_info("adding nsid %d to subsystem %s\n", nsid, subsys->subsysnqn); + pr_info("adding nsid %u to subsystem %s\n", nsid, subsys->subsysnqn); return &ns->group; out: diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index d74c01c98f19..43871a8f56ca 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -558,7 +558,7 @@ static void nvmet_p2pmem_ns_add_p2p(struct nvmet_ctrl *ctrl, if (ret < 0) pci_dev_put(p2p_dev); - pr_info("using p2pmem on %s for nsid %d\n", pci_name(p2p_dev), + pr_info("using p2pmem on %s for nsid %u\n", pci_name(p2p_dev), ns->nsid); } @@ -591,6 +591,11 @@ int nvmet_ns_enable(struct nvmet_ns *ns) if (ns->enabled) goto out_unlock; + if (!ns->device_path) { + ret = -EINVAL; + goto out_unlock; + } + ret = nvmet_bdev_ns_enable(ns); if (ret == -ENOTBLK) ret = nvmet_file_ns_enable(ns); @@ -980,7 +985,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq) wait_for_completion(&sq->confirm_done); wait_for_completion(&sq->free_done); percpu_ref_exit(&sq->ref); - nvmet_auth_sq_free(sq); + nvmet_auth_sq_destroy(sq); nvmet_cq_put(sq->cq); /* diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index e362d7913a38..dbda55895f4f 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -924,6 +924,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset); 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); +void nvmet_auth_sq_destroy(struct nvmet_sq *sq); int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id); bool nvmet_check_auth_status(struct nvmet_req *req); int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, @@ -950,6 +951,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq) } static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {}; static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {}; +static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {}; static inline bool nvmet_check_auth_status(struct nvmet_req *req) { return true; diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c index 0948a690a1c0..09d8c63f5680 100644 --- a/drivers/nvme/target/pr.c +++ b/drivers/nvme/target/pr.c @@ -145,7 +145,7 @@ static void nvmet_pr_add_resv_log(struct nvmet_ctrl *ctrl, u8 log_type, log.nsid = cpu_to_le32(nsid); if (!kfifo_put(&log_mgr->log_queue, log)) { - pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%d\n", + pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%u\n", ctrl->cntlid, log_type, nsid); log_mgr->lost_count++; } diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c index 49cba690914a..ffa1e172c259 100644 --- a/drivers/nvme/target/rdma.c +++ b/drivers/nvme/target/rdma.c @@ -1631,19 +1631,13 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, mutex_unlock(&nvmet_rdma_queue_mutex); if (pending > NVMET_RDMA_BACKLOG) { ret = NVME_SC_CONNECT_CTRL_BUSY; - goto put_device; + goto free_queue; } } ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn); - if (ret) { - /* - * Don't destroy the cm_id in free path, as we implicitly - * destroy the cm_id here with non-zero ret code. - */ - queue->cm_id = NULL; + if (ret) goto free_queue; - } mutex_lock(&nvmet_rdma_queue_mutex); list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list); @@ -1652,6 +1646,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id, return 0; free_queue: + /* + * Don't destroy the cm_id in free path, as we implicitly + * destroy the cm_id here with non-zero ret code. + */ + queue->cm_id = NULL; nvmet_rdma_free_queue(queue); put_device: kref_put(&ndev->ref, nvmet_rdma_free_dev); diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index e4f603b2ace7..e59810175262 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -103,6 +103,7 @@ enum nvmet_tcp_recv_state { enum { NVMET_TCP_F_INIT_FAILED = (1 << 0), + NVMET_TCP_F_R2T_SENT = (1 << 1), }; struct nvmet_tcp_cmd { @@ -776,6 +777,7 @@ static int nvmet_try_send_r2t(struct nvmet_tcp_cmd *cmd, bool last_in_batch) return -EAGAIN; cmd->queue->snd_cmd = NULL; + cmd->flags |= NVMET_TCP_F_R2T_SENT; return 1; } @@ -1009,6 +1011,12 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) cmd = &queue->connect; } + if (unlikely(!(cmd->flags & NVMET_TCP_F_R2T_SENT))) { + pr_err("queue %d: unsolicited H2CData (ttag %u)\n", + queue->idx, data->ttag); + goto err_proto; + } + if (le32_to_cpu(data->data_offset) != cmd->rbytes_done) { pr_err("ttag %u unexpected data offset %u (expected %u)\n", data->ttag, le32_to_cpu(data->data_offset), @@ -1244,6 +1252,8 @@ recv: } queue->left = hdr->hlen - queue->offset + hdgst; + if (queue->left > sizeof(queue->pdu) - queue->offset) + return -EPROTO; goto recv; } diff --git a/drivers/opp/core.c b/drivers/opp/core.c index cd0e82dae776..4ccce7b6afcb 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -348,7 +348,7 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) count = opp_table->regulator_count; - uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); + uV = kmalloc_objs(*uV, count); if (!uV) return 0; @@ -453,8 +453,8 @@ int dev_pm_opp_get_opp_count(struct device *dev) _find_opp_table(dev); if (IS_ERR(opp_table)) { - dev_dbg(dev, "%s: OPP table not found (%ld)\n", - __func__, PTR_ERR(opp_table)); + dev_dbg(dev, "%s: OPP table not found (%pe)\n", + __func__, opp_table); return PTR_ERR(opp_table); } @@ -611,8 +611,8 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available, _find_opp_table(dev); if (IS_ERR(opp_table)) { - dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, - PTR_ERR(opp_table)); + dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, + opp_table); return ERR_CAST(opp_table); } @@ -722,8 +722,8 @@ struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); if (IS_ERR(opp_table)) { - dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, - PTR_ERR(opp_table)); + dev_err(dev, "%s: OPP table not found (%pe)\n", __func__, + opp_table); return ERR_CAST(opp_table); } @@ -1036,8 +1036,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, /* Regulator not available for device */ if (IS_ERR(reg)) { - dev_dbg(dev, "%s: regulator not available: %ld\n", __func__, - PTR_ERR(reg)); + dev_dbg(dev, "%s: regulator not available: %pe\n", __func__, + reg); return 0; } @@ -1448,8 +1448,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) temp_freq = freq; opp = _find_freq_ceil(opp_table, &temp_freq); if (IS_ERR(opp)) { - dev_err(dev, "%s: failed to find OPP for freq %lu (%ld)\n", - __func__, freq, PTR_ERR(opp)); + dev_err(dev, "%s: failed to find OPP for freq %lu (%pe)\n", + __func__, freq, opp); return PTR_ERR(opp); } @@ -1505,7 +1505,7 @@ struct opp_device *_add_opp_dev(const struct device *dev, { struct opp_device *opp_dev; - opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL); + opp_dev = kzalloc_obj(*opp_dev); if (!opp_dev) return NULL; @@ -1581,6 +1581,8 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, struct opp_table *opp_table, bool getclk) { + int ret; + /* * Return early if we don't need to get clk or we have already done it * earlier. @@ -1607,9 +1609,9 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, opp_table->clk = clk_get_optional(dev, NULL); if (IS_ERR(opp_table->clk)) { + ret = dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); dev_pm_opp_put_opp_table(opp_table); - dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); - return ERR_CAST(opp_table->clk); + return ERR_PTR(ret); } if (opp_table->clk) @@ -2869,8 +2871,8 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; if (IS_ERR(opp_table)) { - dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__, - PTR_ERR(opp_table)); + dev_warn(dev, "%s: Device OPP not found (%pe)\n", __func__, + opp_table); return PTR_ERR(opp_table); } diff --git a/drivers/opp/of.c b/drivers/opp/of.c index c02e20632fa6..2f3bbde9a9e1 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -1039,7 +1039,7 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) val = prop->value; while (nr) { - unsigned long freq = be32_to_cpup(val++) * 1000; + unsigned long freq = (unsigned long)be32_to_cpup(val++) * 1000; unsigned long volt = be32_to_cpup(val++); struct dev_pm_opp_data data = { .freq = freq, @@ -1345,8 +1345,8 @@ int of_get_required_opp_performance_state(struct device_node *np, int index) _find_table_of_opp_np(required_np); if (IS_ERR(opp_table)) { - pr_err("%s: Failed to find required OPP table %pOF: %ld\n", - __func__, np, PTR_ERR(opp_table)); + pr_err("%s: Failed to find required OPP table %pOF: %pe\n", + __func__, np, opp_table); return PTR_ERR(opp_table); } diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 0855c7930abb..9c13f193537d 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -57,7 +57,7 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) return -ENODEV; struct pci_epc_aux_resource *res __free(kfree) = - kcalloc(count, sizeof(*res), GFP_KERNEL); + kzalloc_objs(*res, count); if (!res) return -ENOMEM; @@ -98,7 +98,7 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) addr = iova_base + off; } - msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); + msg = kzalloc_objs(*msg, num_db); if (!msg) { ret = -ENOMEM; goto err_unmap; diff --git a/drivers/perf/riscv_pmu_legacy.c b/drivers/perf/riscv_pmu_legacy.c index 4d6461d6a74f..1b8e4789cb3c 100644 --- a/drivers/perf/riscv_pmu_legacy.c +++ b/drivers/perf/riscv_pmu_legacy.c @@ -110,8 +110,9 @@ static void pmu_legacy_init(struct riscv_pmu *pmu) { pr_info("Legacy PMU implementation is available\n"); - pmu->cmask = BIT(RISCV_PMU_LEGACY_CYCLE) | - BIT(RISCV_PMU_LEGACY_INSTRET); + bitmap_zero(pmu->cmask, RISCV_MAX_COUNTERS); + set_bit(RISCV_PMU_LEGACY_CYCLE, pmu->cmask); + set_bit(RISCV_PMU_LEGACY_INSTRET, pmu->cmask); pmu->ctr_start = pmu_legacy_ctr_start; pmu->ctr_stop = NULL; pmu->event_map = pmu_legacy_event_map; diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 50220f7b46d9..2991dd92def2 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -97,7 +97,7 @@ static unsigned int riscv_pmu_irq_mask; static unsigned int riscv_pmu_irq; /* Cache the available counters in a bitmask */ -static unsigned long cmask; +static DECLARE_BITMAP(cmask, RISCV_MAX_COUNTERS); static int pmu_event_find_cache(u64 config); struct sbi_pmu_event_data { @@ -359,16 +359,38 @@ free_mem: return result; } +static struct sbiret pmu_sbi_ctr_cfg_match(unsigned long cbase, + unsigned long ctr_mask, + unsigned long cflags, + unsigned long event_idx, + u64 config) +{ +#if defined(CONFIG_32BIT) + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, + ctr_mask, cflags, event_idx, config, config >> 32); +#else + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, + ctr_mask, cflags, event_idx, config, 0); +#endif +} + static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata) { - struct sbiret ret; + struct sbiret ret = { .error = SBI_ERR_NOT_SUPPORTED }; + int i; - ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, - 0, cmask, 0, edata->event_idx, 0, 0); - if (!ret.error) { - sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, - ret.value, 0x1, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0); - } else if (ret.error == SBI_ERR_NOT_SUPPORTED) { + for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) { + if (!cmask[i]) + continue; + ret = pmu_sbi_ctr_cfg_match(i * BITS_PER_LONG, cmask[i], 0, + edata->event_idx, 0); + if (!ret.error) { + sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, + ret.value, 0x1, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0); + return; + } + } + if (ret.error == SBI_ERR_NOT_SUPPORTED) { /* This event cannot be monitored by any counter */ edata->event_idx = -ENOENT; } @@ -488,10 +510,10 @@ int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr) union sbi_pmu_ctr_info *info; u32 hpm_width = 0, hpm_count = 0; - if (!cmask) + if (bitmap_empty(cmask, RISCV_MAX_COUNTERS)) return -EINVAL; - for_each_set_bit(i, &cmask, RISCV_MAX_COUNTERS) { + for_each_set_bit(i, cmask, RISCV_MAX_COUNTERS) { info = &pmu_ctr_list[i]; if (!info) continue; @@ -540,8 +562,8 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu); struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events); struct sbiret ret; - int idx; - uint64_t cbase = 0, cmask = rvpmu->cmask; + int idx, i; + u64 cbase = 0, cmask = 0; unsigned long cflags = 0; cflags = pmu_sbi_get_filter_flags(event); @@ -562,14 +584,21 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) } /* retrieve the available counter index */ -#if defined(CONFIG_32BIT) - ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, - cmask, cflags, hwc->event_base, hwc->config, - hwc->config >> 32); -#else - ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, - cmask, cflags, hwc->event_base, hwc->config, 0); -#endif + if (cmask) { + ret = pmu_sbi_ctr_cfg_match(cbase, cmask, cflags, hwc->event_base, + hwc->config); + } else { + ret.error = SBI_ERR_NOT_SUPPORTED; + for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) { + if (!rvpmu->cmask[i]) + continue; + cbase = i * BITS_PER_LONG; + ret = pmu_sbi_ctr_cfg_match(cbase, rvpmu->cmask[i], cflags, + hwc->event_base, hwc->config); + if (!ret.error) + break; + } + } if (ret.error) { pr_debug("Not able to find a counter for event %lx config %llx\n", hwc->event_base, hwc->config); @@ -577,7 +606,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) } idx = ret.value; - if (!test_bit(idx, &rvpmu->cmask) || !pmu_ctr_list[idx].value) + if (!test_bit(idx, rvpmu->cmask) || !pmu_ctr_list[idx].value) return -ENOENT; /* Additional sanity check for the counter id */ @@ -881,7 +910,7 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask) /* The logical counter ids are not expected to be contiguous */ continue; - *mask |= BIT(i); + set_bit(i, mask); cinfo.value = ret.value; if (cinfo.type == SBI_PMU_CTR_TYPE_FW) @@ -898,12 +927,19 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask) static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu) { + int i; + /* * No need to check the error because we are disabling all the counters * which may include counters that are not enabled yet. */ - sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, - 0, pmu->cmask, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0); + for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) { + if (!pmu->cmask[i]) + continue; + sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, + i * BITS_PER_LONG, pmu->cmask[i], + SBI_PMU_STOP_FLAG_RESET, 0, 0, 0); + } } static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu) @@ -1002,7 +1038,7 @@ static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_ struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; for_each_set_bit(idx, cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS) { - if (ctr_ovf_mask & BIT(idx)) { + if (ctr_ovf_mask & BIT_ULL(idx)) { event = cpu_hw_evt->events[idx]; hwc = &event->hw; max_period = riscv_pmu_ctr_get_width_mask(event); @@ -1050,11 +1086,13 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) u64 overflowed_ctrs = 0; struct cpu_hw_events *cpu_hw_evt = dev; u64 start_clock = sched_clock(); - struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; + struct riscv_pmu_snapshot_data *sdata; if (WARN_ON_ONCE(!cpu_hw_evt)) return IRQ_NONE; + sdata = cpu_hw_evt->snapshot_addr; + /* Firmware counter don't support overflow yet */ fidx = find_first_bit(cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS); if (fidx == RISCV_MAX_COUNTERS) { @@ -1109,14 +1147,14 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) hidx = info->csr - CSR_CYCLE; /* check if the corresponding bit is set in scountovf or overflow mask in shmem */ - if (!(overflow & BIT(hidx))) + if (!(overflow & BIT_ULL(hidx))) continue; /* * Keep a track of overflowed counters so that they can be started * with updated initial value. */ - overflowed_ctrs |= BIT(lidx); + overflowed_ctrs |= BIT_ULL(lidx); hw_evt = &event->hw; /* Update the event states here so that we know the state while reading */ hw_evt->state |= PERF_HES_STOPPED; @@ -1451,7 +1489,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev) } /* cache all the information about counters now */ - if (pmu_sbi_get_ctrinfo(num_counters, &cmask)) + if (pmu_sbi_get_ctrinfo(num_counters, cmask)) goto out_free; ret = pmu_sbi_setup_irqs(pmu, pdev); @@ -1464,7 +1502,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev) pmu->pmu.attr_groups = riscv_pmu_attr_groups; pmu->pmu.parent = &pdev->dev; - pmu->cmask = cmask; + bitmap_copy(pmu->cmask, cmask, RISCV_MAX_COUNTERS); pmu->ctr_start = pmu_sbi_ctr_start; pmu->ctr_stop = pmu_sbi_ctr_stop; pmu->event_map = pmu_sbi_event_map; diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 8257cd1da48e..ddd7a04ee753 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -720,9 +720,8 @@ static int hsmp_acpi_probe(struct platform_device *pdev) return -ENODEV; } - hsmp_pdev->sock = kcalloc(hsmp_pdev->num_sockets, - sizeof(*hsmp_pdev->sock), - GFP_KERNEL); + hsmp_pdev->sock = kzalloc_objs(*hsmp_pdev->sock, + hsmp_pdev->num_sockets); if (!hsmp_pdev->sock) return -ENOMEM; diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 94355b435a66..592ba4de4c7f 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -17,7 +17,6 @@ #include <linux/units.h> #include "pmf.h" -#ifdef CONFIG_AMD_PMF_DEBUG u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) { switch (index) { @@ -31,6 +30,7 @@ u32 amd_pmf_get_ta_custom_bios_inputs(struct ta_pmf_enact_table *in, int index) } EXPORT_SYMBOL(amd_pmf_get_ta_custom_bios_inputs); +#ifdef CONFIG_AMD_PMF_DEBUG void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) { int i; diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 449addd1ac7a..79a575d0f5b4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1882,7 +1882,7 @@ static int asus_acpi_probe(struct platform_device *pdev) if (result && result != -ENODEV) goto fail_pega_rfkill; - result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, asus_acpi_notify, asus); if (result) goto fail_pega_rfkill; @@ -1912,7 +1912,7 @@ static void asus_acpi_remove(struct platform_device *pdev) { struct asus_laptop *asus = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(asus->device, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(asus->device, ACPI_ALL_NOTIFY, asus_acpi_notify); asus_backlight_exit(asus); asus_rfkill_exit(asus); diff --git a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c index 446dd18d2cee..72a4c2107d7e 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c @@ -96,8 +96,8 @@ int hp_alloc_enumeration_data(void) if (!bioscfg_drv.enumeration_instances_count) return -EINVAL; - bioscfg_drv.enumeration_data = kvcalloc(bioscfg_drv.enumeration_instances_count, - sizeof(*bioscfg_drv.enumeration_data), GFP_KERNEL); + bioscfg_drv.enumeration_data = kvzalloc_objs(*bioscfg_drv.enumeration_data, + bioscfg_drv.enumeration_instances_count); if (!bioscfg_drv.enumeration_data) { bioscfg_drv.enumeration_instances_count = 0; diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index b2773fc1aca4..615b4cf6fc45 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -337,7 +337,7 @@ static const struct dmi_system_id hp_wmi_feature_boards[] __initconst = { }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") }, - .driver_data = (void *)&omen_v1_no_ec_thermal_params, + .driver_data = (void *)&omen_v1_no_ec_board_params, }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, diff --git a/drivers/platform/x86/intel/pmc/pwrm_telemetry.c b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c index 4cde241e01d6..013f779f20a4 100644 --- a/drivers/platform/x86/intel/pmc/pwrm_telemetry.c +++ b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c @@ -93,8 +93,7 @@ acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj, if (header->num_entries != num_regions) return ERR_PTR(-EINVAL); - acpi_disc_t disc __free(kfree) = kmalloc_array(num_regions, sizeof(*disc), - GFP_KERNEL); + acpi_disc_t disc __free(kfree) = kmalloc_objs(*disc, num_regions); if (!disc) return ERR_PTR(-ENOMEM); diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 5db794d65eb5..cfff7f5eac5d 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -156,7 +156,6 @@ static struct platform_device **pdevs; static struct serdev_device **serdevs; static const struct software_node **gpio_button_swnodes; static const struct software_node **swnode_group; -static const struct software_node **gpiochip_node_group; static void (*exit_handler)(void); static __init struct i2c_adapter * @@ -362,11 +361,21 @@ static const struct software_node *cherryview_gpiochip_node_group[] = { NULL }; +const struct software_node crystalcove_gpiochip_node = { + .name = "INT33FD:00", +}; + +static const struct software_node *crystalcove_gpiochip_node_group[] = { + &crystalcove_gpiochip_node, + NULL +}; + static void gpio_secondary_unset(void *data) { struct device *dev = data; set_secondary_fwnode(dev, NULL); + put_device(dev); } static void gpio_secondary_unregister_node_group(void *data) @@ -376,26 +385,28 @@ static void gpio_secondary_unregister_node_group(void *data) software_node_unregister_node_group(nodes); } -static int gpio_secondary_fwnode_init(struct device *parent) +static int gpio_secondary_fwnode_init(struct device *parent, + const struct software_node * const *node_group) { const struct software_node *const *swnode; struct fwnode_handle *fwnode; + struct device *phys_dev; int ret; - if (!gpiochip_node_group) + if (!node_group) return 0; - ret = software_node_register_node_group(gpiochip_node_group); + ret = software_node_register_node_group(node_group); if (ret) return ret; ret = devm_add_action_or_reset(parent, gpio_secondary_unregister_node_group, - gpiochip_node_group); + (void *)node_group); if (ret) return ret; - for (swnode = gpiochip_node_group; *swnode; swnode++) { + for (swnode = node_group; *swnode; swnode++) { struct device *dev __free(put_device) = acpi_bus_find_device_by_name((*swnode)->name); if (!dev) @@ -407,9 +418,15 @@ static int gpio_secondary_fwnode_init(struct device *parent) if (WARN_ON(!fwnode)) return -ENOENT; - set_secondary_fwnode(dev, fwnode); + phys_dev = acpi_get_first_physical_node(to_acpi_device(dev)); + if (!phys_dev) + return dev_err_probe(parent, -ENODEV, + "No physical device for ACPI GPIO dev: %pfwP\n", + fwnode); + + set_secondary_fwnode(phys_dev, fwnode); - ret = devm_add_action_or_reset(parent, gpio_secondary_unset, dev); + ret = devm_add_action_or_reset(parent, gpio_secondary_unset, get_device(phys_dev)); if (ret) return ret; } @@ -452,6 +469,7 @@ static void x86_android_tablet_remove(struct platform_device *pdev) static __init int x86_android_tablet_probe(struct platform_device *pdev) { + const struct software_node * const *gpiochip_node_group; const struct x86_dev_info *dev_info; const struct dmi_system_id *id; int i, ret = 0; @@ -483,12 +501,20 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) break; } - ret = gpio_secondary_fwnode_init(&pdev->dev); + ret = gpio_secondary_fwnode_init(&pdev->dev, gpiochip_node_group); if (ret) { x86_android_tablet_remove(pdev); return ret; } + if (dev_info->has_crystalcove) { + ret = gpio_secondary_fwnode_init(&pdev->dev, crystalcove_gpiochip_node_group); + if (ret) { + x86_android_tablet_remove(pdev); + return ret; + } + } + ret = software_node_register_node_group(dev_info->swnode_group); if (ret) { x86_android_tablet_remove(pdev); diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 8d825e0b4661..52d96ae89078 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -61,13 +61,6 @@ static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { .initial_brightness = 128, }; -static const struct software_node arizona_gpiochip_node = { - .name = "arizona", -}; - -static const struct software_node crystalcove_gpiochip_node = { - .name = "gpio_crystalcove", -}; /* Lenovo Yoga Book X90F / X90L's Android factory image has everything hardcoded */ @@ -416,15 +409,17 @@ static const struct platform_device_info lenovo_yoga_tab2_830_1050_pdevs[] __ini #define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" +static const struct software_node lenovo_yoga_tab2_830_1050_wm5102; + static const struct property_entry lenovo_yoga_tab2_830_1050_wm1502_props[] = { PROPERTY_ENTRY_GPIO("reset-gpios", &crystalcove_gpiochip_node, 3, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", &baytrail_gpiochip_nodes[1], 23, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", - &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), + &lenovo_yoga_tab2_830_1050_wm5102, 2, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", - &arizona_gpiochip_node, 4, GPIO_ACTIVE_LOW), + &lenovo_yoga_tab2_830_1050_wm5102, 4, GPIO_ACTIVE_LOW), { } }; @@ -432,14 +427,6 @@ static const struct software_node lenovo_yoga_tab2_830_1050_wm5102 = { .properties = lenovo_yoga_tab2_830_1050_wm1502_props, }; -static const struct software_node *lenovo_yoga_tab2_830_1050_swnodes[] = { - &crystalcove_gpiochip_node, - &arizona_gpiochip_node, - &lenovo_yoga_tab2_830_1050_wm5102, - &generic_lipo_hv_4v35_battery_node, - NULL -}; - static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev); static void lenovo_yoga_tab2_830_1050_exit(void); @@ -455,8 +442,9 @@ const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { .pdev_info = lenovo_yoga_tab2_830_1050_pdevs, .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_pdevs), .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, - .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, + .swnode_group = generic_lipo_hv_4v35_battery_swnodes, .modules = lenovo_yoga_tab2_modules, + .has_crystalcove = true, .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, .init = lenovo_yoga_tab2_830_1050_init, .exit = lenovo_yoga_tab2_830_1050_exit, @@ -800,8 +788,9 @@ const struct x86_dev_info lenovo_yoga_tab2_1380_info __initconst = { .pdev_info = lenovo_yoga_tab2_1380_pdevs, .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_pdevs), .gpio_button_swnodes = lenovo_yoga_tab2_830_1050_lid_swnodes, - .swnode_group = lenovo_yoga_tab2_830_1050_swnodes, + .swnode_group = generic_lipo_hv_4v35_battery_swnodes, .modules = lenovo_yoga_tab2_modules, + .has_crystalcove = true, .gpiochip_type = X86_GPIOCHIP_BAYTRAIL, .init = lenovo_yoga_tab2_1380_init, .exit = lenovo_yoga_tab2_830_1050_exit, @@ -985,13 +974,15 @@ static struct arizona_pdata lenovo_yt3_wm5102_pdata = { }, }; +static const struct software_node lenovo_yt3_wm5102; + static const struct property_entry lenovo_yt3_wm1502_props[] = { PROPERTY_ENTRY_GPIO("wlf,spkvdd-ena-gpios", &cherryview_gpiochip_nodes[0], 75, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("wlf,ldoena-gpios", &cherryview_gpiochip_nodes[0], 81, GPIO_ACTIVE_HIGH), PROPERTY_ENTRY_GPIO("reset-gpios", &cherryview_gpiochip_nodes[0], 82, GPIO_ACTIVE_HIGH), - PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &arizona_gpiochip_node, 2, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("wlf,micd-pol-gpios", &lenovo_yt3_wm5102, 2, GPIO_ACTIVE_HIGH), { } }; @@ -1000,11 +991,6 @@ static const struct software_node lenovo_yt3_wm5102 = { .name = "wm5102", }; -static const struct software_node *lenovo_yt3_swnodes[] = { - &arizona_gpiochip_node, - &lenovo_yt3_wm5102, - NULL -}; static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = { { @@ -1072,7 +1058,6 @@ const struct x86_dev_info lenovo_yt3_info __initconst = { .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), .spi_dev_info = lenovo_yt3_spi_devs, .spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs), - .swnode_group = lenovo_yt3_swnodes, .modules = lenovo_yt3_modules, .gpiochip_type = X86_GPIOCHIP_CHERRYVIEW, .init = lenovo_yt3_init, diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index c756961ae5fd..6e6534f8fa6c 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -96,6 +96,7 @@ struct x86_dev_info { int (*init)(struct device *dev); void (*exit)(void); bool use_pci; + bool has_crystalcove; enum x86_gpiochip_type gpiochip_type; }; @@ -107,6 +108,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); /* Software nodes representing GPIO chips used by various tablets */ extern const struct software_node baytrail_gpiochip_nodes[]; extern const struct software_node cherryview_gpiochip_nodes[]; +extern const struct software_node crystalcove_gpiochip_node; /* * Extern declarations of x86_dev_info structs so there can be a single diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 842c4169e290..052ec478dfcc 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -2962,7 +2962,7 @@ int of_genpd_add_child_ids(struct device_node *np, return -EINVAL; /* Allocate tracking array for error unwind (parent/child pairs) */ - pairs = kmalloc_array(count * 2, sizeof(*pairs), GFP_KERNEL); + pairs = kmalloc_objs(*pairs, count * 2); if (!pairs) return -ENOMEM; diff --git a/drivers/pmdomain/mediatek/Kconfig b/drivers/pmdomain/mediatek/Kconfig index 8923e6516441..a2eb704a773c 100644 --- a/drivers/pmdomain/mediatek/Kconfig +++ b/drivers/pmdomain/mediatek/Kconfig @@ -43,9 +43,12 @@ config MTK_MFG_PM_DOMAIN This driver is required for the Mali GPU to work at all on MT8196 and MT6991. +endmenu + config AIROHA_CPU_PM_DOMAIN tristate "Airoha CPU power domain" default ARCH_AIROHA + depends on ARCH_AIROHA || COMPILE_TEST depends on HAVE_ARM_SMCCC depends on PM select PM_GENERIC_DOMAINS @@ -54,5 +57,3 @@ config AIROHA_CPU_PM_DOMAIN CPU frequency and power is controlled by ATF with SMC command to set performance states. - -endmenu diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 96e4bd2f5a14..90743275942d 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -241,13 +241,9 @@ static struct rpmhpd *eliza_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx, - [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP] = &nsp, }; diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c index af00c00eceee..3611bed341e1 100644 --- a/drivers/power/reset/reboot-mode.c +++ b/drivers/power/reset/reboot-mode.c @@ -122,14 +122,14 @@ static int reboot_mode_create_device(struct reboot_mode_driver *reboot) struct mode_info *info; int ret; - priv = kzalloc_obj(*priv, GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; INIT_LIST_HEAD(&priv->head); list_for_each_entry(info, &reboot->head, list) { - sysfs_info = kzalloc_obj(*sysfs_info, GFP_KERNEL); + sysfs_info = kzalloc_obj(*sysfs_info); if (!sysfs_info) { ret = -ENOMEM; goto error; @@ -188,7 +188,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot) continue; } - info = kzalloc_obj(*info, GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) { ret = -ENOMEM; goto error; diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig index 1c5f5820f5b7..226c62704d9b 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -29,7 +29,8 @@ config POWER_SEQUENCING_QCOM_WCN config POWER_SEQUENCING_TH1520_GPU tristate "T-HEAD TH1520 GPU power sequencing driver" - depends on (ARCH_THEAD && AUXILIARY_BUS) || COMPILE_TEST + depends on ARCH_THEAD || COMPILE_TEST + select AUXILIARY_BUS help Say Y here to enable the power sequencing driver for the TH1520 SoC GPU. This driver handles the complex clock and reset sequence diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 721e888b658d..3076b3879af9 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -101,6 +101,7 @@ static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data) } kref_init(&unit->ref); + INIT_LIST_HEAD(&unit->list); INIT_LIST_HEAD(&unit->deps); unit->enable = data->enable; unit->disable = data->disable; @@ -480,7 +481,7 @@ pwrseq_device_register(const struct pwrseq_config *config) !config->targets[0]) return ERR_PTR(-EINVAL); - pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL); + pwrseq = kzalloc_obj(*pwrseq); if (!pwrseq) return ERR_PTR(-ENOMEM); @@ -504,10 +505,6 @@ pwrseq_device_register(const struct pwrseq_config *config) */ device_initialize(&pwrseq->dev); - ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id); - if (ret) - goto err_put_pwrseq; - pwrseq->owner = config->owner ?: THIS_MODULE; pwrseq->match = config->match; @@ -516,6 +513,10 @@ pwrseq_device_register(const struct pwrseq_config *config) INIT_LIST_HEAD(&pwrseq->targets); INIT_LIST_HEAD(&pwrseq->units); + ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id); + if (ret) + goto err_put_pwrseq; + ret = pwrseq_setup_targets(config->targets, pwrseq); if (ret) goto err_put_pwrseq; @@ -912,6 +913,8 @@ int pwrseq_enable(struct pwrseq_desc *desc) if (!ret) desc->powered_on = true; } + if (ret) + return ret; if (target->post_enable) { ret = target->post_enable(pwrseq); diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c index de9848a9a9f1..471ffe914a4e 100644 --- a/drivers/power/sequencing/pwrseq-pcie-m2.c +++ b/drivers/power/sequencing/pwrseq-pcie-m2.c @@ -291,7 +291,7 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx, } } - pci_dev = kzalloc(sizeof(*pci_dev), GFP_KERNEL); + pci_dev = kzalloc_obj(*pci_dev); if (!pci_dev) { ret = -ENOMEM; goto err_put_ctrl; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 00d8bc98d588..47e307709e5e 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -904,7 +904,7 @@ int power_supply_get_battery_info(struct power_supply *psy, goto out_put_node; } - u32 *propdata __free(kfree) = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); + u32 *propdata __free(kfree) = kzalloc_objs(*propdata, proplen); if (!propdata) { power_supply_put_battery_info(psy, info); err = -EINVAL; @@ -944,7 +944,7 @@ int power_supply_get_battery_info(struct power_supply *psy, goto out_put_node; } - propdata = kcalloc(proplen, sizeof(*propdata), GFP_KERNEL); + propdata = kzalloc_objs(*propdata, proplen); if (!propdata) { power_supply_put_battery_info(psy, info); err = -ENOMEM; @@ -1726,7 +1726,7 @@ __power_supply_register(struct device *parent, pr_warn("%s: Expected proper parent device for '%s'\n", __func__, desc->name); - psy = kzalloc(sizeof(*psy), GFP_KERNEL); + psy = kzalloc_obj(*psy); if (!psy) return ERR_PTR(-ENOMEM); diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index dc23cd708cfe..f63b45f5ee6a 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -136,7 +136,7 @@ int ptp_open(struct posix_clock_context *pccontext, fmode_t fmode) struct timestamp_event_queue *queue; char debugfsname[32]; - queue = kzalloc(sizeof(*queue), GFP_KERNEL); + queue = kzalloc_obj(*queue); if (!queue) return -EINVAL; queue->mask = bitmap_alloc(PTP_MAX_CHANNELS, GFP_KERNEL); diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c index 0f7cd6dab0b0..d77dacdd4f56 100644 --- a/drivers/ras/amd/atl/core.c +++ b/drivers/ras/amd/atl/core.c @@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = { X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL), { } }; -MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids); static int __init amd_atl_init(void) { diff --git a/drivers/ras/amd/atl/map.c b/drivers/ras/amd/atl/map.c index 24a05af747d5..4ec9333ef745 100644 --- a/drivers/ras/amd/atl/map.c +++ b/drivers/ras/amd/atl/map.c @@ -771,9 +771,5 @@ int get_address_map(struct addr_ctx *ctx) dump_address_map(&ctx->map); - ret = validate_address_map(ctx); - if (ret) - return ret; - - return ret; + return validate_address_map(ctx); } diff --git a/drivers/regulator/pf1550-regulator.c b/drivers/regulator/pf1550-regulator.c index 610eac9bb9cb..ceee553a84b2 100644 --- a/drivers/regulator/pf1550-regulator.c +++ b/drivers/regulator/pf1550-regulator.c @@ -283,63 +283,61 @@ static struct pf1550_desc pf1550_regulators[] = { PF_LDO1(PF1550, "ldo3", LDO3, 0x1f, pf1550_ldo13_volts), }; +/* + * The _LS interrupts indicate an over-current event. The _HS + * interrupts, which are more accurate and can detect catastrophic + * faults, issue an error event. The current limit FAULT interrupt is + * similar to the _HS. + */ +static const struct pf1550_regulator_irq { + unsigned int event; + u8 id; +} pf1550_regulator_irqs[] = { + [PF1550_PMIC_IRQ_SW1_LS] = { REGULATOR_EVENT_OVER_CURRENT_WARN, PF1550_SW1 }, + [PF1550_PMIC_IRQ_SW2_LS] = { REGULATOR_EVENT_OVER_CURRENT_WARN, PF1550_SW2 }, + [PF1550_PMIC_IRQ_SW3_LS] = { REGULATOR_EVENT_OVER_CURRENT_WARN, PF1550_SW3 }, + [PF1550_PMIC_IRQ_SW1_HS] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_SW1 }, + [PF1550_PMIC_IRQ_SW2_HS] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_SW2 }, + [PF1550_PMIC_IRQ_SW3_HS] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_SW3 }, + [PF1550_PMIC_IRQ_LDO1_FAULT] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_LDO1 }, + [PF1550_PMIC_IRQ_LDO2_FAULT] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_LDO2 }, + [PF1550_PMIC_IRQ_LDO3_FAULT] = { REGULATOR_EVENT_OVER_CURRENT, PF1550_LDO3 }, +}; + static irqreturn_t pf1550_regulator_irq_handler(int irq, void *data) { + const struct pf1550_regulator_irq *map; struct pf1550_regulator_info *info = data; struct device *dev = info->dev; struct platform_device *pdev = to_platform_device(dev); int i, irq_type = -1; - unsigned int event; for (i = 0; i < PF1550_REGULATOR_IRQ_NR; i++) if (irq == platform_get_irq(pdev, i)) irq_type = i; - switch (irq_type) { - /* The _LS interrupts indicate over-current event. The _HS interrupts - * which are more accurate and can detect catastrophic faults, issue - * an error event. The current limit FAULT interrupt is similar to the - * _HS' - */ - case PF1550_PMIC_IRQ_SW1_LS: - case PF1550_PMIC_IRQ_SW2_LS: - case PF1550_PMIC_IRQ_SW3_LS: - event = REGULATOR_EVENT_OVER_CURRENT_WARN; - for (i = 0; i < PF1550_MAX_REGULATOR; i++) - if (!strcmp(rdev_get_name(info->rdevs[i]), "SW3")) - regulator_notifier_call_chain(info->rdevs[i], - event, NULL); - break; - case PF1550_PMIC_IRQ_SW1_HS: - case PF1550_PMIC_IRQ_SW2_HS: - case PF1550_PMIC_IRQ_SW3_HS: - event = REGULATOR_EVENT_OVER_CURRENT; - for (i = 0; i < PF1550_MAX_REGULATOR; i++) - if (!strcmp(rdev_get_name(info->rdevs[i]), "SW3")) - regulator_notifier_call_chain(info->rdevs[i], - event, NULL); - break; - case PF1550_PMIC_IRQ_LDO1_FAULT: - case PF1550_PMIC_IRQ_LDO2_FAULT: - case PF1550_PMIC_IRQ_LDO3_FAULT: - event = REGULATOR_EVENT_OVER_CURRENT; - for (i = 0; i < PF1550_MAX_REGULATOR; i++) - if (!strcmp(rdev_get_name(info->rdevs[i]), "LDO3")) - regulator_notifier_call_chain(info->rdevs[i], - event, NULL); - break; - case PF1550_PMIC_IRQ_TEMP_110: - case PF1550_PMIC_IRQ_TEMP_125: - event = REGULATOR_EVENT_OVER_TEMP; + /* The die temperature concerns every rail. */ + if (irq_type == PF1550_PMIC_IRQ_TEMP_110 || + irq_type == PF1550_PMIC_IRQ_TEMP_125) { for (i = 0; i < PF1550_MAX_REGULATOR; i++) regulator_notifier_call_chain(info->rdevs[i], - event, NULL); - break; - default: - dev_err(dev, "regulator interrupt: irq %d occurred\n", - irq_type); + REGULATOR_EVENT_OVER_TEMP, + NULL); + return IRQ_HANDLED; + } + + if (irq_type < 0 || irq_type >= (int)ARRAY_SIZE(pf1550_regulator_irqs)) { + dev_err(dev, "regulator interrupt: irq %d occurred\n", irq_type); + return IRQ_HANDLED; } + map = &pf1550_regulator_irqs[irq_type]; + + for (i = 0; i < PF1550_MAX_REGULATOR; i++) + if (rdev_get_id(info->rdevs[i]) == map->id) + regulator_notifier_call_chain(info->rdevs[i], + map->event, NULL); + return IRQ_HANDLED; } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 5979758311c8..2ffe404b890f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -87,7 +87,7 @@ struct dasd_device *dasd_alloc_device(void) { struct dasd_device *device; - device = kzalloc_obj(struct dasd_device, GFP_KERNEL); + device = kzalloc_obj(struct dasd_device); if (!device) return ERR_PTR(-ENOMEM); diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h index f5907b67db29..8a69eed75040 100644 --- a/drivers/s390/crypto/zcrypt_cca_key.h +++ b/drivers/s390/crypto/zcrypt_cca_key.h @@ -219,6 +219,7 @@ static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt, void *p) copy_from_user(key->key_parts + 2 * long_len + 2 * short_len, crt->u_mult_inv, long_len)) return -EFAULT; + memset(key->key_parts + 3 * long_len + 2 * short_len, 0, pad_len); memset(key->key_parts + 3 * long_len + 2 * short_len + pad_len, 0xff, crt->inputdatalength); pub = (struct cca_public_sec *)(key->key_parts + key_len); diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index d4ce6352b5b2..19909bf43dc9 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1158,8 +1158,21 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, /* do not check the key here, it may be incomplete */ - /* copy the vlsc key token back */ + /* + * Copy the vlsc key token back. + * The available space in the destination (key_token) and the source + * (t) buffer is always larger as the valid range of prepparm->kb.len. + * Validate t->len by comparing it with the length information in the + * param block of the request (prepparm->kb.len) + * The value range of prepparm->kb.len has been checked above. + */ t = (struct cipherkeytoken *)prepparm->kb.tlv1.key_token; + if (t->len != prepparm->kb.len - 3 * sizeof(uint16_t)) { + ZCRYPT_DBF_ERR("%s reply with invalid key_token length %u\n", + __func__, t->len); + rc = -EIO; + goto out; + } memcpy(key_token, t, t->len); *key_token_size = t->len; diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index 08e36685e578..61c88fe853c5 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -826,7 +826,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg) fsm_deltimer(&grp->timer); - if (grp->saved_xid2->xid2_flag2 == 0x40) { + if (priv->xid->xid2_flag2 == 0x40) { priv->xid->xid2_flag2 = 0x00; if (grp->estconnfunc) { grp->estconnfunc(grp->port_num, 1, @@ -1636,7 +1636,6 @@ done: "The XID used in the MPC protocol is not valid, " "rc = %d\n", rc); priv->xid->xid2_flag2 = 0x40; - grp->saved_xid2->xid2_flag2 = 0x40; } return rc; diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 242da20f27e0..035b233abb4e 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -231,6 +231,7 @@ static void ism_free_dmb(struct ism_dev *ism, struct dibs_dmb *dmb) dma_unmap_page(&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len, DMA_FROM_DEVICE); folio_put(virt_to_folio(dmb->cpu_addr)); + dmb->cpu_addr = NULL; } static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb) @@ -274,7 +275,8 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb) return 0; out_free: - kfree(dmb->cpu_addr); + folio_put(folio); + dmb->cpu_addr = NULL; out_bit: clear_bit(dmb->idx, ism->sba_bitmap); return rc; diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h index 7c646e2fed7e..f94975e970ca 100644 --- a/drivers/s390/net/qeth_l2.h +++ b/drivers/s390/net/qeth_l2.h @@ -13,7 +13,8 @@ extern const struct attribute_group *qeth_l2_attr_groups[]; int qeth_bridgeport_query_ports(struct qeth_card *card, enum qeth_sbp_roles *role, - enum qeth_sbp_states *state); + enum qeth_sbp_states *state, + bool *os_mismatch); int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role); int qeth_bridgeport_an_set(struct qeth_card *card, int enable); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a9e7d1d637a2..2935c2ecc314 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1158,7 +1158,7 @@ static void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) qeth_bridgeport_setrole(card, card->options.sbp.role); /* Let the callback function refresh the stored role value. */ qeth_bridgeport_query_ports(card, &card->options.sbp.role, - NULL); + NULL, NULL); } if (card->options.sbp.hostnotification) { if (qeth_bridgeport_an_set(card, 1)) @@ -1545,6 +1545,7 @@ struct _qeth_sbp_cbctl { struct { enum qeth_sbp_roles *role; enum qeth_sbp_states *state; + bool *os_mismatch; } qports; } data; }; @@ -1721,10 +1722,19 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param; struct qeth_sbp_port_data *qports; + u16 sbp_rc; int rc; QETH_CARD_TEXT(card, 2, "brqprtcb"); - rc = qeth_bridgeport_makerc(card, cmd); + sbp_rc = cmd->data.sbp.hdr.return_code; + + /* on OS family mismatch, query still returns valid port data; + * treat as success + */ + if (sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH && !cmd->hdr.return_code) + rc = 0; + else + rc = qeth_bridgeport_makerc(card, cmd); if (rc) return rc; @@ -1740,6 +1750,9 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, if (cbctl->data.qports.state) *cbctl->data.qports.state = qports->entry[0].state; } + if (cbctl->data.qports.os_mismatch) + *cbctl->data.qports.os_mismatch = + (sbp_rc == IPA_RC_SBP_IQD_OS_MISMATCH); return 0; } @@ -1748,13 +1761,17 @@ static int qeth_bridgeport_query_ports_cb(struct qeth_card *card, * @card: qeth_card structure pointer. * @role: Role of the port: 0-none, 1-primary, 2-secondary. * @state: State of the port: 0-inactive, 1-standby, 2-active. + * @os_mismatch: if non-NULL, set to true when firmware reports + * OS family mismatch. * * Returns negative errno-compatible error indication or 0 on success. * - * 'role' and 'state' are not updated in case of hardware operation failure. + * 'role', 'state' and 'os_mismatch' are not updated in case of + * hardware operation failure. */ int qeth_bridgeport_query_ports(struct qeth_card *card, - enum qeth_sbp_roles *role, enum qeth_sbp_states *state) + enum qeth_sbp_roles *role, enum qeth_sbp_states *state, + bool *os_mismatch) { struct qeth_cmd_buffer *iob; struct _qeth_sbp_cbctl cbctl = { @@ -1762,6 +1779,7 @@ int qeth_bridgeport_query_ports(struct qeth_card *card, .qports = { .role = role, .state = state, + .os_mismatch = os_mismatch, }, }, }; diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 7f592f912517..7101be62eb1d 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -15,6 +15,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); enum qeth_sbp_states state = QETH_SBP_STATE_INACTIVE; + bool os_mismatch = false; int rc = 0; char *word; @@ -25,7 +26,7 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, if (qeth_card_hw_is_reachable(card) && card->options.sbp.supported_funcs) rc = qeth_bridgeport_query_ports(card, - &card->options.sbp.role, &state); + &card->options.sbp.role, &state, &os_mismatch); if (!rc) { if (show_state) switch (state) { @@ -52,6 +53,10 @@ static ssize_t qeth_bridge_port_role_state_show(struct device *dev, if (rc) QETH_CARD_TEXT_(card, 2, "SBP%02x:%02x", card->options.sbp.role, state); + else if (!show_state && + card->options.sbp.role == QETH_SBP_ROLE_NONE && + os_mismatch) + rc = sysfs_emit(buf, "%s (OS family mismatch)\n", word); else rc = sysfs_emit(buf, "%s\n", word); } diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c index 61f167e20574..21b7e9666b74 100644 --- a/drivers/scsi/fnic/fnic_debugfs.c +++ b/drivers/scsi/fnic/fnic_debugfs.c @@ -767,7 +767,7 @@ static int fnic_nvmef_debugfs_open(struct inode *inode, struct file *file) struct fnic_nvmef_info *info; int buf_size = 2 * PAGE_SIZE; - info = kzalloc_obj(struct fnic_nvmef_info, GFP_KERNEL); + info = kzalloc_obj(struct fnic_nvmef_info); if (!info) return -ENOMEM; diff --git a/drivers/scsi/fnic/fnic_nvme.c b/drivers/scsi/fnic/fnic_nvme.c index b237948dcafd..00d9d5d439a3 100644 --- a/drivers/scsi/fnic/fnic_nvme.c +++ b/drivers/scsi/fnic/fnic_nvme.c @@ -2216,7 +2216,7 @@ int nvfnic_add_tport(struct fnic *fnic, struct fnic_tport_s *tport, int nvfnic_add_lport(struct fnic *fnic) { - struct nvme_fc_port_info pinfo; + struct nvme_fc_port_info pinfo = {}; struct fnic_iport_s *iport = &fnic->iport; int ret = 0; diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c index b3bc3ce872d6..3534ac45e9b8 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c @@ -1894,7 +1894,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt) } /** - * ibmvfc_relogin - Log back into the specified device + * ibmvfc_scsi_relogin - Log back into the specified device * @sdev: scsi device struct * **/ @@ -4987,6 +4987,7 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt) * ibmvfc_alloc_target - Allocate and initialize an ibmvfc target * @vhost: ibmvfc host struct * @target: Holds SCSI ID to allocate target forand the WWPN + * @protocol: protocol of the target to allocate * * Returns: * 0 on success / other on failure diff --git a/drivers/scsi/leapraid/leapraid_func.c b/drivers/scsi/leapraid/leapraid_func.c index 089d0810bd13..66b43c1c72e1 100644 --- a/drivers/scsi/leapraid/leapraid_func.c +++ b/drivers/scsi/leapraid/leapraid_func.c @@ -2984,8 +2984,7 @@ static void leapraid_fw_evt_put(struct leapraid_fw_evt_work *fw_work) static struct leapraid_fw_evt_work *leapraid_alloc_fw_evt_work(void) { - struct leapraid_fw_evt_work *fw_evt = - kzalloc(sizeof(*fw_evt), GFP_ATOMIC); + struct leapraid_fw_evt_work *fw_evt = kzalloc_obj(*fw_evt, GFP_ATOMIC); if (fw_evt) kref_init(&fw_evt->refcnt); @@ -3644,8 +3643,7 @@ static void leapraid_sas_host_add(struct leapraid_adapter *adapter, return; adapter->dev_topo.card.card_phy = - kcalloc(phys_num, - sizeof(struct leapraid_card_phy), GFP_KERNEL); + kzalloc_objs(struct leapraid_card_phy, phys_num); if (!adapter->dev_topo.card.card_phy) return; @@ -3763,8 +3761,7 @@ static int leapraid_internal_exp_add(struct leapraid_adapter *adapter, } topo_node_exp->card_phy = - kcalloc(topo_node_exp->phys_num, - sizeof(struct leapraid_card_phy), GFP_KERNEL); + kzalloc_objs(struct leapraid_card_phy, topo_node_exp->phys_num); if (!topo_node_exp->card_phy) { dev_err(&adapter->pdev->dev, "%s: Failed to alloc expander phy array, count=%u\n", @@ -4352,7 +4349,7 @@ static void leapraid_sas_volume_add( return; } - raid_volume = kzalloc(sizeof(*raid_volume), GFP_KERNEL); + raid_volume = kzalloc_obj(*raid_volume); if (!raid_volume) return; @@ -6090,17 +6087,15 @@ static void leapraid_update_card_port_after_reset( if (!adapter->dev_topo.card.card_phy) { adapter->dev_topo.card.card_phy = - kcalloc(nr_phys, sizeof(struct leapraid_card_phy), - GFP_KERNEL); + kzalloc_objs(struct leapraid_card_phy, nr_phys); if (!adapter->dev_topo.card.card_phy) return; } adapter->dev_topo.card.phys_num = nr_phys; - new_card_port_table = kcalloc(adapter->dev_topo.card.phys_num, - sizeof(struct leapraid_card_port), - GFP_KERNEL); + new_card_port_table = kzalloc_objs(struct leapraid_card_port, + adapter->dev_topo.card.phys_num); if (!new_card_port_table) return; @@ -7224,8 +7219,8 @@ static int leapraid_set_legacy_int(struct leapraid_adapter *adapter) adapter->notification_desc.iopoll_qdex, adapter->notification_desc.iopoll_qcnt); adapter->notification_desc.int_rqs = - kcalloc(adapter->notification_desc.iopoll_qdex, - sizeof(struct leapraid_int_rq), GFP_KERNEL); + kzalloc_objs(struct leapraid_int_rq, + adapter->notification_desc.iopoll_qdex); if (!adapter->notification_desc.int_rqs) return -ENOMEM; @@ -7268,9 +7263,8 @@ static int leapraid_set_msix(struct leapraid_adapter *adapter) } if (iopoll_qcnt) { adapter->notification_desc.blk_mq_poll_rqs = - kcalloc(iopoll_qcnt, - sizeof(struct leapraid_blk_mq_poll_rq), - GFP_KERNEL); + kzalloc_objs(struct leapraid_blk_mq_poll_rq, + iopoll_qcnt); if (!adapter->notification_desc.blk_mq_poll_rqs) return -ENOMEM; adapter->adapter_attr.rq_cnt = @@ -7289,8 +7283,8 @@ static int leapraid_set_msix(struct leapraid_adapter *adapter) adapter->notification_desc.iopoll_qcnt); adapter->notification_desc.int_rqs = - kcalloc(adapter->notification_desc.iopoll_qdex, - sizeof(struct leapraid_int_rq), GFP_KERNEL); + kzalloc_objs(struct leapraid_int_rq, + adapter->notification_desc.iopoll_qdex); if (!adapter->notification_desc.int_rqs) return -ENOMEM; @@ -7365,9 +7359,8 @@ static int leapraid_set_msi(struct leapraid_adapter *adapter) if (iopoll_qcnt) { adapter->notification_desc.blk_mq_poll_rqs = - kcalloc(iopoll_qcnt, - sizeof(struct leapraid_blk_mq_poll_rq), - GFP_KERNEL); + kzalloc_objs(struct leapraid_blk_mq_poll_rq, + iopoll_qcnt); if (!adapter->notification_desc.blk_mq_poll_rqs) return -ENOMEM; @@ -7404,9 +7397,8 @@ static int leapraid_set_msi(struct leapraid_adapter *adapter) adapter->notification_desc.iopoll_qcnt); adapter->notification_desc.int_rqs = - kcalloc(adapter->notification_desc.iopoll_qdex, - sizeof(struct leapraid_int_rq), - GFP_KERNEL); + kzalloc_objs(struct leapraid_int_rq, + adapter->notification_desc.iopoll_qdex); if (!adapter->notification_desc.int_rqs) return -ENOMEM; @@ -7958,18 +7950,16 @@ try_again: DIV_ROUND_UP(adapter->adapter_attr.rq_cnt, LEAPRAID_REP_DESC_CHUNK_SIZE); adapter->mem_desc.rep_desc_seg_maint = - kcalloc(adapter->adapter_attr.rep_desc_q_seg_cnt, - sizeof(struct leapraid_rep_desc_seg_maint), - GFP_KERNEL); + kzalloc_objs(struct leapraid_rep_desc_seg_maint, + adapter->adapter_attr.rep_desc_q_seg_cnt); if (!adapter->mem_desc.rep_desc_seg_maint) return -ENOMEM; rep_desc_q_cnt_allocated = 0; for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) { adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint = - kcalloc(LEAPRAID_REP_DESC_CHUNK_SIZE, - sizeof(struct leapraid_rep_desc_maint), - GFP_KERNEL); + kzalloc_objs(struct leapraid_rep_desc_maint, + LEAPRAID_REP_DESC_CHUNK_SIZE); if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint) return -ENOMEM; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index f0152b043e18..b95f187297ae 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1973,12 +1973,23 @@ megasas_set_nvme_device_properties(struct scsi_device *sdev, { struct megasas_instance *instance; u32 mr_nvme_pg_size; + u64 max_prp_io; instance = (struct megasas_instance *)sdev->host->hostdata; mr_nvme_pg_size = max_t(u32, instance->nvme_page_size, MR_DEFAULT_NVME_PAGE_SIZE); - lim->max_hw_sectors = max_io_size / 512; + /* + * megasas_make_prp_nvme() builds the PRP list in cmd->sg_frame without + * bounding it against that buffer, and spends one entry per page of + * it on the chain pointer. Cap the transfer at what the buffer holds, + * less one page for lists that start off a page boundary. + */ + max_prp_io = (u64)((instance->max_chain_frame_sz / sizeof(u64)) - + (instance->max_chain_frame_sz / mr_nvme_pg_size) - 1) * + mr_nvme_pg_size; + + lim->max_hw_sectors = min_t(u64, max_io_size, max_prp_io) >> SECTOR_SHIFT; lim->virt_boundary_mask = mr_nvme_pg_size - 1; } diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index f80a21ec161b..0f7380448718 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -1094,10 +1094,13 @@ static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc) { struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next; struct mpi3mr_stgt_priv_data *tgt_priv; + struct scsi_target *starget; + unsigned long flags; dprint_reset(mrioc, "refresh target devices: check for removals\n"); list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, list) { + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); if (((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) || tgtdev->is_hidden) && tgtdev->host_exposed && tgtdev->starget && @@ -1106,6 +1109,7 @@ static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc) tgt_priv->dev_removed = 1; atomic_set(&tgt_priv->block_io, 0); } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); } list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, @@ -1127,15 +1131,25 @@ static void mpi3mr_refresh_tgtdevs(struct mpi3mr_ioc *mrioc) tgtdev = NULL; list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { if ((tgtdev->dev_handle != MPI3MR_INVALID_DEV_HANDLE) && - !tgtdev->is_hidden) { - if (!tgtdev->host_exposed) + !tgtdev->is_hidden) { + if (!tgtdev->host_exposed) { mpi3mr_report_tgtdev_to_host(mrioc, - tgtdev->perst_id); - else if (tgtdev->starget) - starget_for_each_device(tgtdev->starget, - (void *)tgtdev, mpi3mr_update_sdev); - } + tgtdev->perst_id); + continue; + } + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + starget = tgtdev->starget; + if (starget) + get_device(&starget->dev); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + if (starget) { + starget_for_each_device(starget, (void *)tgtdev, + mpi3mr_update_sdev); + put_device(&starget->dev); + } + } } + dprint_reset(mrioc, "refresh target devices: done\n"); } /** @@ -1515,6 +1529,8 @@ static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, struct mpi3_device_page0 *dev_pg0) { struct mpi3mr_tgt_dev *tgtdev = NULL; + struct scsi_target *starget; + unsigned long flags; u16 dev_handle = 0, perst_id = 0; perst_id = le16_to_cpu(dev_pg0->persistent_id); @@ -1535,9 +1551,18 @@ static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, mpi3mr_report_tgtdev_to_host(mrioc, perst_id); if (tgtdev->is_hidden && tgtdev->host_exposed) mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev); - if (!tgtdev->is_hidden && tgtdev->host_exposed && tgtdev->starget) - starget_for_each_device(tgtdev->starget, (void *)tgtdev, - mpi3mr_update_sdev); + if (!tgtdev->is_hidden && tgtdev->host_exposed) { + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + starget = tgtdev->starget; + if (starget) + get_device(&starget->dev); + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); + if (starget) { + starget_for_each_device(starget, (void *)tgtdev, + mpi3mr_update_sdev); + put_device(&starget->dev); + } + } out: if (tgtdev) mpi3mr_tgtdev_put(tgtdev); diff --git a/drivers/scsi/mpi3mr/mpi3mr_transport.c b/drivers/scsi/mpi3mr/mpi3mr_transport.c index 240f67a8e2e3..232af978d737 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_transport.c +++ b/drivers/scsi/mpi3mr/mpi3mr_transport.c @@ -1428,9 +1428,15 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, } port = sas_port_alloc_num(mr_sas_node->parent_dev); + if (!port) { + ioc_err(mrioc, "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + goto out_fail; + } if ((sas_port_add(port))) { ioc_err(mrioc, "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); + sas_port_free(port); goto out_fail; } @@ -1501,6 +1507,8 @@ static struct mpi3mr_sas_port *mpi3mr_sas_port_add(struct mpi3mr_ioc *mrioc, list_for_each_entry_safe(mr_sas_phy, next, &mr_sas_port->phy_list, port_siblings) list_del(&mr_sas_phy->port_siblings); + if (tgtdev) + mpi3mr_tgtdev_put(tgtdev); kfree(mr_sas_port); return NULL; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index fed7aeffec58..1af25a22611a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -3238,7 +3238,10 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc) * corresponding to high iops queues. */ if (ioc->high_iops_queues) { - mask = cpumask_of_node(dev_to_node(&ioc->pdev->dev)); + int node = dev_to_node(&ioc->pdev->dev); + + mask = (node == NUMA_NO_NODE) ? + cpu_online_mask : cpumask_of_node(node); for (index = 0; index < ioc->high_iops_queues; index++) { irq = pci_irq_vector(ioc->pdev, index); diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index e93ea76b565e..54b35893261a 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -1029,8 +1029,8 @@ static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha) &(pm8001_ha->irq_vector[i])); if (rc) { for (j = 0; j < i; j++) { - free_irq(pci_irq_vector(pm8001_ha->pdev, i), - &(pm8001_ha->irq_vector[i])); + free_irq(pci_irq_vector(pm8001_ha->pdev, j), + &pm8001_ha->irq_vector[j]); } pci_free_irq_vectors(pm8001_ha->pdev); break; diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index e80dec53174e..5eec248a77a6 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -18,6 +18,7 @@ struct scsi_bsg_uring_cmd_pdu { struct bio *bio; /* mapped user buffer, unmap in task work */ struct request *req; /* block request, freed in task work */ u64 response_addr; /* user space response buffer address */ + u32 max_response_len; /* user response buffer size */ }; static_assert(sizeof(struct scsi_bsg_uring_cmd_pdu) <= sizeof_field(struct io_uring_cmd, pdu)); @@ -45,8 +46,8 @@ static void scsi_bsg_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw) if (scsi_status_is_check_condition(scmd->result)) { driver_status = DRIVER_SENSE; if (pdu->response_addr) - sense_len_wr = min_t(u8, scmd->sense_len, - SCSI_SENSE_BUFFERSIZE); + sense_len_wr = min_t(unsigned int, pdu->max_response_len, + scmd->sense_len); } if (sense_len_wr) { @@ -76,12 +77,10 @@ static enum rq_end_io_ret scsi_bsg_uring_cmd_done(struct request *req, static int scsi_bsg_map_user_buffer(struct request *req, struct io_uring_cmd *ioucmd, - unsigned int issue_flags, gfp_t gfp_mask) + unsigned int issue_flags, gfp_t gfp_mask, + bool is_write, u64 buf_addr, + unsigned long buf_len) { - const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); - bool is_write = cmd->dout_xfer_len > 0; - u64 buf_addr = is_write ? cmd->dout_xferp : cmd->din_xferp; - unsigned long buf_len = is_write ? cmd->dout_xfer_len : cmd->din_xfer_len; struct iov_iter iter; int ret; @@ -104,21 +103,28 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc unsigned int issue_flags, bool open_for_write) { struct scsi_bsg_uring_cmd_pdu *pdu = scsi_bsg_uring_cmd_pdu(ioucmd); - const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); + const struct bsg_uring_cmd *cmd = + io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); struct scsi_cmnd *scmd; struct request *req; blk_mq_req_flags_t blk_flags = 0; gfp_t gfp_mask = GFP_KERNEL; + u64 request = READ_ONCE(cmd->request); + u32 request_len = READ_ONCE(cmd->request_len); + u64 dout_xferp = READ_ONCE(cmd->dout_xferp); + u32 dout_xfer_len = READ_ONCE(cmd->dout_xfer_len); + u64 din_xferp = READ_ONCE(cmd->din_xferp); + u32 din_xfer_len = READ_ONCE(cmd->din_xfer_len); int ret; if (cmd->protocol != BSG_PROTOCOL_SCSI || cmd->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD) return -EINVAL; - if (!cmd->request || cmd->request_len == 0) + if (!request || request_len == 0) return -EINVAL; - if (cmd->dout_xfer_len && cmd->din_xfer_len) { + if (dout_xfer_len && din_xfer_len) { pr_warn_once("BIDI support in bsg has been removed.\n"); return -EOPNOTSUPP; } @@ -131,20 +137,20 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc gfp_mask = GFP_NOWAIT; } - req = scsi_alloc_request(q, cmd->dout_xfer_len ? + req = scsi_alloc_request(q, dout_xfer_len ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, blk_flags); if (IS_ERR(req)) return PTR_ERR(req); scmd = blk_mq_rq_to_pdu(req); - if (cmd->request_len > sizeof(scmd->cmnd)) { + if (request_len > sizeof(scmd->cmnd)) { ret = -EINVAL; goto out_free_req; } - scmd->cmd_len = cmd->request_len; + scmd->cmd_len = request_len; scmd->allowed = SG_DEFAULT_RETRIES; - if (copy_from_user(scmd->cmnd, uptr64(cmd->request), cmd->request_len)) { + if (copy_from_user(scmd->cmnd, uptr64(request), request_len)) { ret = -EFAULT; goto out_free_req; } @@ -155,11 +161,16 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc } pdu->response_addr = cmd->response; - scmd->sense_len = cmd->max_response_len ? - min(cmd->max_response_len, SCSI_SENSE_BUFFERSIZE) : SCSI_SENSE_BUFFERSIZE; + pdu->max_response_len = cmd->max_response_len; - if (cmd->dout_xfer_len || cmd->din_xfer_len) { - ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, gfp_mask); + if (dout_xfer_len || din_xfer_len) { + bool is_write = dout_xfer_len > 0; + u64 buf_addr = is_write ? dout_xferp : din_xferp; + unsigned long buf_len = is_write ? dout_xfer_len : din_xfer_len; + + ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, + gfp_mask, is_write, buf_addr, + buf_len); if (ret) goto out_free_req; pdu->bio = req->bio; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 3b82e80e807a..0f0f243c2561 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -158,7 +158,7 @@ int scsi_complete_async_scans(void) * sleep a little. Even if we never get memory, the async * scans will finish eventually. */ - data = kmalloc(sizeof(*data), GFP_KERNEL); + data = kmalloc_obj(*data); if (!data) msleep(1); } while (!data); diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c index 7be0374f5943..a903fa44e4e5 100644 --- a/drivers/soc/bcm/brcmstb/common.c +++ b/drivers/soc/bcm/brcmstb/common.c @@ -65,7 +65,7 @@ static int __init brcmstb_soc_device_init(void) goto out_put_node; } - soc_info = kzalloc(sizeof(*soc_info), GFP_KERNEL); + soc_info = kzalloc_obj(*soc_info); if (!soc_info) { ret = -ENOMEM; goto out_unmap; diff --git a/drivers/spi/spi-offload.c b/drivers/spi/spi-offload.c index a579ef33b2d2..a446927a51d3 100644 --- a/drivers/spi/spi-offload.c +++ b/drivers/spi/spi-offload.c @@ -434,7 +434,7 @@ int devm_spi_offload_trigger_register(struct device *dev, if (!info->fwnode || !info->ops || !info->ops->match) return -EINVAL; - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); + trigger = kzalloc_obj(*trigger); if (!trigger) return -ENOMEM; diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index ca0c38221c16..7925d974de80 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -298,14 +298,15 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) { struct fbtft_par *par = info->par; struct fb_deferred_io *fbdefio = info->fbdefio; + unsigned long flags; /* Mark display lines/area as dirty */ - spin_lock(&par->dirty_lock); + spin_lock_irqsave(&par->dirty_lock, flags); if (y < par->dirty_lines_start) par->dirty_lines_start = y; if (y + height - 1 > par->dirty_lines_end) par->dirty_lines_end = y + height - 1; - spin_unlock(&par->dirty_lock); + spin_unlock_irqrestore(&par->dirty_lock, flags); /* Schedule deferred_io to update display (no-op if already on queue)*/ schedule_delayed_work(&info->deferred_work, fbdefio->delay); @@ -318,13 +319,13 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflis struct fb_deferred_io_pageref *pageref; unsigned int y_low = 0, y_high = 0; - spin_lock(&par->dirty_lock); + spin_lock_irq(&par->dirty_lock); dirty_lines_start = par->dirty_lines_start; dirty_lines_end = par->dirty_lines_end; /* set display line markers as clean */ par->dirty_lines_start = par->info->var.yres - 1; par->dirty_lines_end = 0; - spin_unlock(&par->dirty_lock); + spin_unlock_irq(&par->dirty_lock); /* Mark display lines as dirty */ list_for_each_entry(pageref, pagereflist, list) { diff --git a/drivers/staging/greybus/raw.c b/drivers/staging/greybus/raw.c index 459aed0f1240..4f1b3f4db404 100644 --- a/drivers/staging/greybus/raw.c +++ b/drivers/staging/greybus/raw.c @@ -178,7 +178,7 @@ static int gb_raw_probe(struct gb_bundle *bundle, if (minor < 0) return minor; - raw = kzalloc_obj(*raw, GFP_KERNEL); + raw = kzalloc_obj(*raw); if (!raw) { ida_free(&minors, minor); return -ENOMEM; diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 00082276f1db..4a182dc384ca 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5820,36 +5820,31 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( } descr->in_info = kmalloc_objs(*descr->in_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->in_info) { err = -ENOMEM; goto ERR; } descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } descr->out_info = kmalloc_objs(*descr->out_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->out_info) { err = -ENOMEM; goto ERR; } descr->vf_info = kmalloc_objs(*descr->vf_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; @@ -5970,36 +5965,31 @@ ia_css_pipe_create_cas_scaler_desc(struct ia_css_pipe *pipe, descr->num_stage = num_stages; descr->in_info = kmalloc_objs(*descr->in_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->in_info) { err = -ENOMEM; goto ERR; } descr->internal_out_info = kmalloc_objs(*descr->internal_out_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->internal_out_info) { err = -ENOMEM; goto ERR; } descr->out_info = kmalloc_objs(*descr->out_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->out_info) { err = -ENOMEM; goto ERR; } descr->vf_info = kmalloc_objs(*descr->vf_info, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->vf_info) { err = -ENOMEM; goto ERR; } descr->is_output_stage = kmalloc_objs(*descr->is_output_stage, - descr->num_stage, - GFP_KERNEL); + descr->num_stage); if (!descr->is_output_stage) { err = -ENOMEM; goto ERR; diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c index af12df2f9b09..b895dee77568 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c +++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c @@ -254,8 +254,8 @@ sh_css_load_firmware(struct device *dev, const char *fw_data, /* Only allocate memory for ISP blob info */ if (sh_css_num_binaries > NUM_OF_SPS) { sh_css_blob_info = - kmalloc_array(sh_css_num_binaries - NUM_OF_SPS, - sizeof(*sh_css_blob_info), GFP_KERNEL); + kmalloc_objs(*sh_css_blob_info, + sh_css_num_binaries - NUM_OF_SPS); if (!sh_css_blob_info) return -ENOMEM; } else { diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index 66f476a46aad..4d211711f2ba 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -741,6 +741,10 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); u16 attr_len = attr_data_len + 4; + /* Reject attributes whose claimed length runs past the IE */ + if (attr_ptr + attr_len > wps_ie + wps_ielen) + break; + if (attr_id == target_attr_id) { target_attr_ptr = attr_ptr; @@ -1149,6 +1153,9 @@ int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *act u8 c; u8 a = ACT_PUBLIC_MAX; + if (frame_len < sizeof(struct ieee80211_hdr_3addr) + 2) + return false; + fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control); if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index fc46b33b836a..d18768a51b19 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1975,6 +1975,9 @@ int rtw_restruct_wmm_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_ break; } + if (i + 1 >= in_len) + break; + i += (in_ie[i + 1] + 2); /* to the next IE element */ } diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 039e2033f84e..8b93bfeb217b 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -252,7 +252,7 @@ static void lynxfb_ops_imageblit(struct fb_info *info, spin_lock(&sm750_dev->slock); sm750_dev->accel.de_imageblit(&sm750_dev->accel, - image->data, image->width >> 3, 0, + image->data, 0, base, pitch, bpp, image->dx, image->dy, image->width, image->height, diff --git a/drivers/staging/sm750fb/sm750.h b/drivers/staging/sm750fb/sm750.h index 89a61bf80779..fd1cf9eb5797 100644 --- a/drivers/staging/sm750fb/sm750.h +++ b/drivers/staging/sm750fb/sm750.h @@ -64,7 +64,7 @@ struct lynx_accel { u32 rop2); int (*de_imageblit)(struct lynx_accel *accel, const char *p_srcbuf, - u32 src_delta, u32 start_bit, u32 d_base, u32 d_pitch, + u32 start_bit, u32 d_base, u32 d_pitch, u32 byte_per_pixel, u32 dx, u32 dy, u32 width, u32 height, u32 f_color, u32 b_color, u32 rop2); diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c index 0316ea69d009..bac9a209899c 100644 --- a/drivers/staging/sm750fb/sm750_accel.c +++ b/drivers/staging/sm750fb/sm750_accel.c @@ -288,8 +288,6 @@ static unsigned int de_get_transparency(struct lynx_accel *accel) * sm750_hw_imageblit * @accel: Acceleration device data * @src_buf: pointer to start of source buffer in system memory - * @src_delta: Pitch value (in bytes) of the source buffer, +ive means top down - * and -ive mean button up * @start_bit: Mono data can start at any bit in a byte, this value should be * 0 to 7 * @dest_base: Address of destination: offset in frame buffer @@ -304,7 +302,7 @@ static unsigned int de_get_transparency(struct lynx_accel *accel) * @rop2: ROP value */ int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf, - u32 src_delta, u32 start_bit, u32 dest_base, u32 dest_pitch, + u32 start_bit, u32 dest_base, u32 dest_pitch, u32 byte_per_pixel, u32 dx, u32 dy, u32 width, u32 height, u32 fg_color, u32 bg_color, u32 rop2) { @@ -395,7 +393,7 @@ int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf, write_dp_port(accel, *(unsigned int *)remain); } - src_buf += src_delta; + src_buf += bytes_per_scan; } return 0; diff --git a/drivers/staging/sm750fb/sm750_accel.h b/drivers/staging/sm750fb/sm750_accel.h index 617885431661..efceefaaafb0 100644 --- a/drivers/staging/sm750fb/sm750_accel.h +++ b/drivers/staging/sm750fb/sm750_accel.h @@ -220,8 +220,6 @@ int sm750_hw_copyarea(struct lynx_accel *accel, /** * sm750_hw_imageblit * @src_buf: pointer to start of source buffer in system memory - * @src_delta: Pitch value (in bytes) of the source buffer, +ive means top down - *>----- and -ive mean button up * @start_bit: Mono data can start at any bit in a byte, this value should be *>----- 0 to 7 * @dest_base: Address of destination: offset in frame buffer @@ -236,7 +234,7 @@ int sm750_hw_copyarea(struct lynx_accel *accel, * @rop2: ROP value */ int sm750_hw_imageblit(struct lynx_accel *accel, const char *src_buf, - u32 src_delta, u32 start_bit, u32 dest_base, u32 dest_pitch, + u32 start_bit, u32 dest_base, u32 dest_pitch, u32 byte_per_pixel, u32 dx, u32 dy, u32 width, u32 height, u32 fg_color, u32 bg_color, u32 rop2); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 62ada3a52210..124ff269b8e7 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1533,8 +1533,10 @@ __iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf, */ if (se_cmd->transport_state & CMD_T_ABORTED) { if (hdr->flags & ISCSI_FLAG_CMD_FINAL && - --cmd->outstanding_r2ts < 1) + --cmd->outstanding_r2ts < 1) { iscsit_stop_dataout_timer(cmd); + target_complete_cmd(se_cmd, SAM_STAT_TASK_ABORTED); + } return iscsit_dump_data_payload(conn, payload_length, 1); } diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index aafc94bcb635..c282b6a70296 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -47,7 +47,7 @@ static struct iscsi_login *iscsi_login_init_conn(struct iscsit_conn *conn) login->conn = conn; login->first_request = 1; - login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); + login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS + 1, GFP_KERNEL); if (!login->req_buf) { pr_err("Unable to allocate memory for response buffer.\n"); goto out_login; diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c index 10452fcc7ccb..a06eb94e0bae 100644 --- a/drivers/tee/qcomtee/user_obj.c +++ b/drivers/tee/qcomtee/user_obj.c @@ -230,8 +230,7 @@ static int qcomtee_user_object_dispatch(struct qcomtee_object_invoke_ctx *oic, struct qcomtee_context_data *ctxdata = uo->ctx->data; int errno; - struct qcomtee_ureq *ureq __free(kfree) = kzalloc(sizeof(*ureq), - GFP_KERNEL); + struct qcomtee_ureq *ureq __free(kfree) = kzalloc_obj(*ureq); if (!ureq) return -ENOMEM; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index adbcb2c011e8..96fe5d591a0a 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -400,8 +400,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) struct thermal_trip_attrs *trip_attrs = &td->trip_attrs; /* create trip type attribute */ - snprintf(trip_attrs->type.name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", i); + scnprintf(trip_attrs->type.name, sizeof(trip_attrs->type.name), + "trip_point_%d_type", i); sysfs_attr_init(&trip_attrs->type.attr.attr); trip_attrs->type.attr.attr.name = trip_attrs->type.name; @@ -410,8 +410,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) attrs[i] = &trip_attrs->type.attr.attr; /* create trip temp attribute */ - snprintf(trip_attrs->temp.name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", i); + scnprintf(trip_attrs->temp.name, sizeof(trip_attrs->temp.name), + "trip_point_%d_temp", i); sysfs_attr_init(&trip_attrs->temp.attr.attr); trip_attrs->temp.attr.attr.name = trip_attrs->temp.name; @@ -423,8 +423,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) } attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; - snprintf(trip_attrs->hyst.name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", i); + scnprintf(trip_attrs->hyst.name, sizeof(trip_attrs->hyst.name), + "trip_point_%d_hyst", i); sysfs_attr_init(&trip_attrs->hyst.attr.attr); trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name; diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c index c737dd0ca6e7..25c259dd0760 100644 --- a/drivers/thunderbolt/stream.c +++ b/drivers/thunderbolt/stream.c @@ -408,8 +408,7 @@ static int tbstream_dev_alloc_rx_buffers(struct tbstream_dev *sdev) size_t ring_size = tb_ring_size(sdev->rx_ring.ring); int i; - sdev->rx_ring.frames = kcalloc(ring_size, sizeof(struct tbstream_frame), - GFP_KERNEL); + sdev->rx_ring.frames = kzalloc_objs(struct tbstream_frame, ring_size); if (!sdev->rx_ring.frames) return -ENOMEM; @@ -463,8 +462,7 @@ static int tbstream_dev_alloc_tx_buffers(struct tbstream_dev *sdev) size_t ring_size = tb_ring_size(sdev->tx_ring.ring); int i; - sdev->tx_ring.frames = kcalloc(ring_size, sizeof(struct tbstream_frame), - GFP_KERNEL); + sdev->tx_ring.frames = kzalloc_objs(struct tbstream_frame, ring_size); if (!sdev->tx_ring.frames) return -ENOMEM; @@ -1498,7 +1496,7 @@ tbstream_dev_make_group(struct config_group *group, const char *name) if (strlen(name) > TB_PROPERTY_KEY_SIZE) return ERR_PTR(-ENAMETOOLONG); - sdev = kzalloc_obj(*sdev, GFP_KERNEL); + sdev = kzalloc_obj(*sdev); if (!sdev) return ERR_PTR(-ENOMEM); @@ -1592,7 +1590,7 @@ tbstream_make_group(struct config_group *group, const char *name) if (sscanf(name, "%u-%llx.%u", &domain, &route, &index) != 3) return ERR_PTR(-EINVAL); - sg = kzalloc_obj(*sg, GFP_KERNEL); + sg = kzalloc_obj(*sg); if (!sg) return ERR_PTR(-ENOMEM); @@ -1698,7 +1696,7 @@ static int tbstream_probe(struct tb_service *svc) { struct tbstream *stream; - stream = kzalloc_obj(*stream, GFP_KERNEL); + stream = kzalloc_obj(*stream); if (!stream) return -ENOMEM; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 1bb2376af85c..40a1c614e23b 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -954,8 +954,7 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) unsigned int i, first_idx; int ret; - brd->ports = kcalloc(MAX_PORTS_PER_BOARD, sizeof(*brd->ports), - GFP_KERNEL); + brd->ports = kzalloc_objs(*brd->ports, MAX_PORTS_PER_BOARD); if (brd->ports == NULL) { printk(KERN_ERR "cannot allocate memory for ports\n"); ret = -ENOMEM; diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 3fa89a2dbeba..7d564341a7eb 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -776,7 +776,8 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni_pagedict *dict; unsigned int d, r, g; - struct unipair *unilist __free(kvfree) = kvmalloc_array(ct, sizeof(*unilist), GFP_KERNEL); + struct unipair *unilist __free(kvfree) = kvmalloc_objs(*unilist, ct, + GFP_KERNEL); if (!unilist) return -ENOMEM; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8f467b22b799..57edf37495a8 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4986,8 +4986,8 @@ static int con_font_set(struct vc_data *vc, const struct console_font_op *op) if (!vc->vc_sw->con_font_set) return -ENOSYS; - if (vc_is_sel(vc)) - clear_selection(); + /* hide selection and cursor prior font changes */ + hide_cursor(vc); return vc->vc_sw->con_font_set(vc, &font, vpitch, op->flags); } @@ -5011,8 +5011,9 @@ static int con_font_default(struct vc_data *vc, struct console_font_op *op) if (!vc->vc_sw->con_font_default) return -ENOSYS; - if (vc_is_sel(vc)) - clear_selection(); + /* hide selection and cursor prior font changes */ + hide_cursor(vc); + int ret = vc->vc_sw->con_font_default(vc, &font, s); if (ret) return ret; diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index fa5f539632eb..6192512e29de 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -1073,7 +1073,7 @@ static int __ufshcd_tx_eqtr(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode) { struct ufshcd_tx_eqtr_data *eqtr_data __free(kfree) = - kzalloc(sizeof(*eqtr_data), GFP_KERNEL); + kzalloc_obj(*eqtr_data); struct tx_eqtr_iter h_iter = {}; struct tx_eqtr_iter d_iter = {}; u32 gear = pwr_mode->gear_tx; diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 62396212a0a7..954be9343f42 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -715,6 +715,7 @@ static void ufs_qcom_link_startup_post_change(struct ufs_hba *hba) static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) { + struct ufs_qcom_host *host = ufshcd_get_variant(hba); int err = 0; switch (status) { @@ -737,6 +738,14 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, */ err = ufshcd_disable_host_tx_lcc(hba); + /* + * Restore HS/LS link startup mode set by bootloader + * after UFS reset clears REG_UFS_DEBUG_SPARE_CFG. + */ + if (host->hw_ver.major > 0x6 || + (host->hw_ver.major == 0x6 && host->hw_ver.minor >= 0x2)) + ufshcd_writel(hba, host->boot_spare_cfg, + REG_UFS_DEBUG_SPARE_CFG); break; case POST_CHANGE: ufs_qcom_link_startup_post_change(hba); @@ -1325,7 +1334,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host) { struct ufs_host_params *host_params = &host->host_params; - u32 val, dev_major; + u32 dev_major; /* * Default to powering up the PHY to the max gear possible, which is @@ -1344,8 +1353,8 @@ static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host) */ host->phy_gear = UFS_HS_G2; } else if (host->hw_ver.major >= 0x5) { - val = ufshcd_readl(host->hba, REG_UFS_DEBUG_SPARE_CFG); - dev_major = FIELD_GET(UFS_DEV_VER_MAJOR_MASK, val); + host->boot_spare_cfg = ufshcd_readl(host->hba, REG_UFS_DEBUG_SPARE_CFG); + dev_major = FIELD_GET(UFS_DEV_VER_MAJOR_MASK, host->boot_spare_cfg); /* * Since the UFS device version is populated, let's remove the @@ -2282,7 +2291,7 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, p->polling_ms = 60; p->timer = DEVFREQ_TIMER_DELAYED; d->upthreshold = 70; - d->downdifferential = 5; + d->downdifferential = 65; hba->clk_scaling.suspend_on_no_request = true; } @@ -2761,7 +2770,7 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, struct tx_eqtr_iter *d_iter) { struct ufshcd_tx_eq_params *params __free(kfree) = - kzalloc(sizeof(*params), GFP_KERNEL); + kzalloc_obj(*params); struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_pa_layer_attr old_pwr_info; u32 fom[PA_MAXDATALANES] = { 0 }; diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index e20b3ca50577..a5ad5ce44a19 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -361,6 +361,7 @@ struct ufs_qcom_host { bool esi_enabled; u32 saved_tx_eq_g1_setting; + u32 boot_spare_cfg; }; struct ufs_qcom_drvdata { diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index f2433879b0eb..21bb11c724be 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -181,6 +181,25 @@ static int ufs_intel_lkf_pwr_change_notify(struct ufs_hba *hba, return err; } +static int ufs_intel_nvl_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status stage, + struct ufs_pa_layer_attr *dev_req_params) +{ + int adapt_val; + + if (stage != PRE_CHANGE || hba->ufs_version < ufshci_version(4, 0)) + return 0; + + if (dev_req_params->pwr_tx == FAST_MODE || dev_req_params->pwr_tx == FASTAUTO_MODE) + adapt_val = PA_INITIAL_ADAPT; + else + adapt_val = PA_NO_ADAPT; + + ufshcd_dme_configure_adapt(hba, dev_req_params->gear_tx, adapt_val); + + return 0; +} + static int ufs_intel_lkf_apply_dev_quirks(struct ufs_hba *hba) { u32 granularity, peer_granularity; @@ -441,6 +460,43 @@ static int ufs_intel_mtl_init(struct ufs_hba *hba) return ufs_intel_common_init(hba); } +static int ufs_intel_mcq_config_resource(struct ufs_hba *hba) +{ + hba->mcq_base = hba->mmio_base + ufshcd_mcq_queue_cfg_addr(hba); + + return 0; +} + +/* + * This Intel UFS4.0 controller maps MCQ doorbell and interrupt-status + * registers into the same PCI BAR as the legacy HCI space, at this + * fixed offset/stride. + */ +#define UFS_INTEL_SQDAO0 0x2800 +#define UFS_INTEL_SQISAO0 0x2814 +#define UFS_INTEL_CQDAO0 0x281C +#define UFS_INTEL_CQISAO0 0x2824 +#define UFS_INTEL_MCQ_STRIDE 0x30 + +static int ufs_intel_op_runtime_config(struct ufs_hba *hba) +{ + struct ufshcd_mcq_opr_info_t *opr; + int i; + + hba->mcq_opr[OPR_SQD].offset = UFS_INTEL_SQDAO0; + hba->mcq_opr[OPR_SQIS].offset = UFS_INTEL_SQISAO0; + hba->mcq_opr[OPR_CQD].offset = UFS_INTEL_CQDAO0; + hba->mcq_opr[OPR_CQIS].offset = UFS_INTEL_CQISAO0; + + for (i = 0; i < OPR_MAX; i++) { + opr = &hba->mcq_opr[i]; + opr->stride = UFS_INTEL_MCQ_STRIDE; + opr->base = hba->mmio_base + opr->offset; + } + + return 0; +} + static int ufs_qemu_get_hba_mac(struct ufs_hba *hba) { return MAX_SUPP_MAC; @@ -527,6 +583,9 @@ static struct ufs_hba_variant_ops ufs_intel_mtl_hba_vops = { .exit = ufs_intel_common_exit, .hce_enable_notify = ufs_intel_hce_enable_notify, .link_startup_notify = ufs_intel_link_startup_notify, + .pwr_change_notify = ufs_intel_nvl_pwr_change_notify, + .mcq_config_resource = ufs_intel_mcq_config_resource, + .op_runtime_config = ufs_intel_op_runtime_config, .resume = ufs_intel_resume, .device_reset = ufs_intel_device_reset, }; diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 7a516e509198..e84405352caa 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -1338,7 +1338,6 @@ static int cdnsp_run(struct cdnsp_device *pdev, cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - ret = cdnsp_start(pdev); if (ret) { ret = -ENODEV; @@ -1837,6 +1836,82 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev) readl(&pdev->rev_cap->tx_buff_size)); } +static void cdnsp_set_event_deq(struct cdnsp_device *pdev) +{ + dma_addr_t deq; + u64 temp; + + deq = cdnsp_trb_virt_to_dma(pdev->event_ring->deq_seg, + pdev->event_ring->dequeue); + + /* Update controller event ring dequeue pointer */ + temp = cdnsp_read_64(&pdev->ir_set->erst_dequeue); + temp &= ERST_PTR_MASK; + + /* + * Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + temp &= ~ERST_EHB; + + cdnsp_write_64(((u64)deq & (u64)~ERST_PTR_MASK) | temp, + &pdev->ir_set->erst_dequeue); +} + +static void cdnsp_add_interrupter(struct cdnsp_device *pdev) +{ + u64 erst_base; + u32 erst_size; + + /* Set ERST count with the number of entries in the segment table. */ + erst_size = readl(&pdev->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &pdev->ir_set->erst_size); + + /* Set the segment table base address. */ + erst_base = cdnsp_read_64(&pdev->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (pdev->erst.erst_dma_addr & (u64)~ERST_PTR_MASK); + cdnsp_write_64(erst_base, &pdev->ir_set->erst_base); + + /* Set the event ring dequeue address. */ + cdnsp_set_event_deq(pdev); +} + +/* Set up basic CDNSP registers */ +static void cdnsp_init(struct cdnsp_device *pdev) +{ + unsigned int val; + u64 val_64; + + val = readl(&pdev->op_regs->config_reg); + val |= ((val & ~MAX_DEVS) | CDNSP_DEV_MAX_SLOTS) | CONFIG_U3E; + writel(val, &pdev->op_regs->config_reg); + + /* Initialize the Command ring */ + cdnsp_ring_init(pdev, pdev->cmd_ring); + + /* Set the address in the Command Ring Control register */ + val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring); + val_64 = (val_64 & (u64)CMD_RING_RSVD_BITS) | + (pdev->cmd_ring->first_seg->dma & (u64)~CMD_RING_RSVD_BITS) | + pdev->cmd_ring->cycle_state; + cdnsp_write_64(val_64, &pdev->op_regs->cmd_ring); + + /* Set Device Context Base Address Array pointer */ + cdnsp_write_64(pdev->dcbaa->dma, &pdev->op_regs->dcbaa_ptr); + + /* Set Doorbell array pointer */ + val = readl(&pdev->cap_regs->db_off); + val &= DBOFF_MASK; + pdev->dba = (void __iomem *)pdev->cap_regs + val; + + /* Initialize the Primary interrupter */ + cdnsp_ring_init(pdev, pdev->event_ring); + cdnsp_add_interrupter(pdev); +} + static int cdnsp_gen_setup(struct cdnsp_device *pdev) { int ret; @@ -1902,6 +1977,8 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev) if (ret) return ret; + cdnsp_init(pdev); + /* * Software workaround for U1: after transition * to U1 the controller starts gating clock, and in some cases, @@ -2031,9 +2108,6 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup) struct cdnsp_device *pdev = cdns->gadget_dev; unsigned long flags; - if (pdev->link_state == XDEV_U3) - return 0; - spin_lock_irqsave(&pdev->lock, flags); cdnsp_disconnect_gadget(pdev); cdnsp_stop(pdev); @@ -2047,12 +2121,38 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) struct cdnsp_device *pdev = cdns->gadget_dev; enum usb_device_speed max_speed; unsigned long flags; + bool context_lost; + u32 val; int ret; if (!pdev->gadget_driver) return 0; spin_lock_irqsave(&pdev->lock, flags); + val = readl(&pdev->port3x_regs->mode_2); + context_lost = !!(val & CFG_3XPORT_U1_PIPE_CLK_GATE_EN) || lost_power; + + if (context_lost) { + cdnsp_halt(pdev); + cdnsp_set_apb_timeout_value(pdev); + + /* Reset the internal controller memory state and registers. */ + ret = cdnsp_reset(pdev); + if (ret) + goto unlock; + + val = readl(&pdev->port3x_regs->mode_2); + val &= ~CFG_3XPORT_U1_PIPE_CLK_GATE_EN; + writel(val, &pdev->port3x_regs->mode_2); + + cdnsp_clear_cmd_ring(pdev); + + memset(pdev->event_ring->first_seg->trbs, 0, + sizeof(union cdnsp_trb) * (TRBS_PER_SEGMENT)); + + cdnsp_init(pdev); + } + max_speed = pdev->gadget_driver->max_speed; /* Limit speed if necessary. */ @@ -2060,9 +2160,10 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) ret = cdnsp_run(pdev, max_speed); - if (pdev->link_state == XDEV_U3) + if (!context_lost && pdev->link_state == XDEV_U3) __cdnsp_gadget_wakeup(pdev); +unlock: spin_unlock_irqrestore(&pdev->lock, flags); return ret; diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index c44bca348a41..c3ae5040f9cc 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -1510,6 +1510,7 @@ int cdnsp_endpoint_init(struct cdnsp_device *pdev, int cdnsp_ring_expansion(struct cdnsp_device *pdev, struct cdnsp_ring *ring, unsigned int num_trbs, gfp_t flags); +void cdnsp_ring_init(struct cdnsp_device *pdev, struct cdnsp_ring *ring); struct cdnsp_ring *cdnsp_dma_to_transfer_ring(struct cdnsp_ep *ep, u64 address); int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, struct cdnsp_ep *pep, diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 83f3384b735d..419309c8439e 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -394,13 +394,6 @@ static struct cdnsp_ring *cdnsp_ring_alloc(struct cdnsp_device *pdev, if (ret) goto fail; - /* Only event ring does not use link TRB. */ - if (type != TYPE_EVENT) - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= - cpu_to_le32(LINK_TOGGLE); - - cdnsp_initialize_ring_info(ring); - trace_cdnsp_ring_alloc(ring); return ring; fail: kfree(ring); @@ -603,6 +596,7 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, if (!cur_ring) goto cleanup_rings; + cdnsp_ring_init(pdev, cur_ring); cur_ring->stream_id = cur_stream; cur_ring->trb_address_map = &stream_info->trb_address_map; @@ -698,6 +692,8 @@ static int cdnsp_alloc_priv_device(struct cdnsp_device *pdev) if (!pdev->eps[0].ring) goto fail; + cdnsp_ring_init(pdev, pdev->eps[0].ring); + /* Point to output device context in dcbaa. */ pdev->dcbaa->dev_context_ptrs[1] = cpu_to_le64(pdev->out_ctx.dma); pdev->cmd.in_ctx = &pdev->in_ctx; @@ -991,6 +987,8 @@ int cdnsp_endpoint_init(struct cdnsp_device *pdev, if (!pep->ring) return -ENOMEM; + cdnsp_ring_init(pdev, pep->ring); + pep->skip = false; /* Fill the endpoint context */ @@ -1096,28 +1094,6 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev) pdev->active_port = NULL; } -static void cdnsp_set_event_deq(struct cdnsp_device *pdev) -{ - dma_addr_t deq; - u64 temp; - - deq = cdnsp_trb_virt_to_dma(pdev->event_ring->deq_seg, - pdev->event_ring->dequeue); - - /* Update controller event ring dequeue pointer */ - temp = cdnsp_read_64(&pdev->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; - - /* - * Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - temp &= ~ERST_EHB; - - cdnsp_write_64(((u64)deq & (u64)~ERST_PTR_MASK) | temp, - &pdev->ir_set->erst_dequeue); -} - static void cdnsp_add_in_port(struct cdnsp_device *pdev, struct cdnsp_port *port, __le32 __iomem *addr) @@ -1226,6 +1202,36 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) return 0; } +static void cdnsp_initialize_ring_segments(struct cdnsp_device *pdev, struct cdnsp_ring *ring) +{ + struct cdnsp_segment *seg; + + /* Only event ring does not use link TRB. */ + if (ring->type == TYPE_EVENT) + return; + + seg = ring->first_seg; + + while (seg) { + struct cdnsp_segment *next = seg->next; + + cdnsp_link_segments(pdev, seg, next, ring->type); + if (next == ring->first_seg) + break; + + seg = next; + } + + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); +} + +void cdnsp_ring_init(struct cdnsp_device *pdev, struct cdnsp_ring *ring) +{ + cdnsp_initialize_ring_segments(pdev, ring); + cdnsp_initialize_ring_info(ring); + trace_cdnsp_ring_alloc(ring); +} + /* * Initialize memory for CDNSP (one-time init). * @@ -1237,10 +1243,8 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) { struct device *dev = pdev->dev; int ret = -ENOMEM; - unsigned int val; dma_addr_t dma; u32 page_size; - u64 val_64; /* * Use 4K pages, since that's common and the minimum the @@ -1248,10 +1252,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) */ page_size = 1 << 12; - val = readl(&pdev->op_regs->config_reg); - val |= ((val & ~MAX_DEVS) | CDNSP_DEV_MAX_SLOTS) | CONFIG_U3E; - writel(val, &pdev->op_regs->config_reg); - /* * Doorbell array must be physically contiguous * and 64-byte (cache line) aligned. @@ -1263,8 +1263,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) pdev->dcbaa->dma = dma; - cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr); - /* * Initialize the ring segment pool. The ring must be a contiguous * structure comprised of TRBs. The TRBs must be 16 byte aligned, @@ -1290,17 +1288,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (!pdev->cmd_ring) goto destroy_device_pool; - /* Set the address in the Command Ring Control register */ - val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring); - val_64 = (val_64 & (u64)CMD_RING_RSVD_BITS) | - (pdev->cmd_ring->first_seg->dma & (u64)~CMD_RING_RSVD_BITS) | - pdev->cmd_ring->cycle_state; - cdnsp_write_64(val_64, &pdev->op_regs->cmd_ring); - - val = readl(&pdev->cap_regs->db_off); - val &= DBOFF_MASK; - pdev->dba = (void __iomem *)pdev->cap_regs + val; - /* Set ir_set to interrupt register set 0 */ pdev->ir_set = &pdev->run_regs->ir_set[0]; @@ -1317,21 +1304,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (ret) goto free_event_ring; - /* Set ERST count with the number of entries in the segment table. */ - val = readl(&pdev->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - writel(val, &pdev->ir_set->erst_size); - - /* Set the segment table base address. */ - val_64 = cdnsp_read_64(&pdev->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (pdev->erst.erst_dma_addr & (u64)~ERST_PTR_MASK); - cdnsp_write_64(val_64, &pdev->ir_set->erst_base); - - /* Set the event ring dequeue address. */ - cdnsp_set_event_deq(pdev); - ret = cdnsp_setup_port_arrays(pdev); if (ret) goto free_erst; diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 60ee4cc99b28..a01ca23cb6a8 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -442,6 +442,7 @@ static int dwc3_google_probe(struct platform_device *pdev) probe_data.dwc = &google->dwc; probe_data.res = res; probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; ret = dwc3_core_probe(&probe_data); if (ret) { ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index bfe616194dfa..310b5ffb236a 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -304,7 +304,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; if (dwc->connected) - dwc3_stop_active_transfer(dwc3_ep, true, true); + dwc3_stop_active_transfer(dwc3_ep, false, true); else dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fa944856f956..f245e66cd13d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1004,7 +1004,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * controller to generate an ERDY to initiate the * stream. */ - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); /* * All stream eps will reinitiate stream on NoStream @@ -1032,7 +1032,7 @@ void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status) { struct dwc3_request *req; - dwc3_stop_active_transfer(dep, true, false); + dwc3_stop_active_transfer(dep, false, false); /* If endxfer is delayed, avoid unmapping requests */ if (dep->flags & DWC3_EP_DELAY_STOP) @@ -1720,7 +1720,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) if (ret == -EAGAIN) return ret; - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); @@ -1757,6 +1757,11 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) * the controller won't update the TRB progress on command * completion. It also won't clear the HWO bit in the TRB. * The command will also not complete immediately in that case. + * + * Older programming guide revisions recommended setting ForceRM to 1 + * when ending a transfer. Newer programming guide revisions now + * recommend keeping ForceRM cleared, and TRBs are properly updated + * on command completion. */ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { @@ -1882,7 +1887,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) * to wait for the next XferNotReady to test the command again */ if (cmd_status == 0) { - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); return 0; } } @@ -2165,7 +2170,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct dwc3_request *t; /* wait until it is processed */ - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); /* * Remove any started request if the transfer is @@ -2242,7 +2247,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) return 0; } - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); @@ -3368,7 +3373,7 @@ static void dwc3_nostream_work(struct work_struct *work) dwc3_send_gadget_generic_command(dwc, cmd, dep->number); } else { dep->flags |= DWC3_EP_DELAY_START; - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); spin_unlock_irqrestore(&dwc->lock, flags); return; } @@ -3726,7 +3731,7 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->started_list) && (list_empty(&dep->pending_list) || status == -EXDEV)) - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); else if (dwc3_gadget_ep_should_continue(dep)) if (__dwc3_gadget_kick_transfer(dep) == 0) no_started_trb = false; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index a50743caf083..fc4818fb2a8b 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2747,6 +2747,9 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) struct fsg_buffhd *bh, *buffhds; int i; + if (n < 2) + return -EINVAL; + buffhds = kzalloc_objs(*buffhds, n); if (!buffhds) return -ENOMEM; @@ -2960,7 +2963,7 @@ EXPORT_SYMBOL_GPL(fsg_common_create_lun); int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg) { - char buf[8]; /* enough for 100000000 different numbers, decimal */ + char buf[14]; int i, rc; fsg_common_remove_luns(common); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index fba8cf787d6c..63fb6ee70a3d 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -879,7 +879,6 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0; midi->gadget = cdev->gadget; - INIT_WORK(&midi->work, f_midi_in_work); status = f_midi_register_card(midi); if (status < 0) goto fail_register; @@ -1377,6 +1376,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) status = -ENOMEM; goto midi_free; } + INIT_WORK(&midi->work, f_midi_in_work); midi->out_ports = opts->out_ports; midi->index = opts->index; midi->buflen = opts->buflen; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index a4b72a6fad8a..5b8b18281989 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1145,7 +1145,7 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) if (!usb_ep->reqs) return -EINVAL; - for (i = 0; i < midi2->info.num_reqs; i++) { + for (i = 0; i < usb_ep->num_reqs; i++) { if (usb_ep->reqs[i].req) continue; usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep, @@ -1160,10 +1160,9 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) /* Free allocated requests */ static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep) { - struct f_midi2 *midi2 = usb_ep->card; int i; - for (i = 0; i < midi2->info.num_reqs; i++) { + for (i = 0; i < usb_ep->num_reqs; i++) { if (!usb_ep->reqs[i].req) continue; free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req); @@ -2178,13 +2177,13 @@ end: /* generic show/store for string */ static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts, - const char *str, char *page) + const char **strp, char *page) { int result = 0; mutex_lock(&opts->lock); - if (str) - result = scnprintf(page, PAGE_SIZE, "%s\n", str); + if (*strp) + result = scnprintf(page, PAGE_SIZE, "%s\n", *strp); mutex_unlock(&opts->lock); return result; } @@ -2278,7 +2277,7 @@ static ssize_t f_midi2_block_opts_name_show(struct config_item *item, { struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); - return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page); + return f_midi2_opts_str_show(opts->ep->opts, &opts->info.name, page); } static ssize_t f_midi2_block_opts_name_store(struct config_item *item, @@ -2435,7 +2434,7 @@ static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ char *page) \ { \ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ - return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\ + return f_midi2_opts_str_show(opts->opts, &opts->info.name, page);\ } \ \ static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ @@ -2590,7 +2589,7 @@ static ssize_t f_midi2_opts_iface_name_show(struct config_item *item, { struct f_midi2_opts *opts = to_f_midi2_opts(item); - return f_midi2_opts_str_show(opts, opts->info.iface_name, page); + return f_midi2_opts_str_show(opts, &opts->info.iface_name, page); } static ssize_t f_midi2_opts_iface_name_store(struct config_item *item, diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index bf02545b37a2..500c9c19c78b 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1766,7 +1766,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) int status; /* allocate and initialize one new instance */ - ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); + ncm = kzalloc_obj(*ncm); if (!ncm) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index 203361a64212..70e31c40e267 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -70,7 +70,7 @@ void usb_put_function_instance(struct usb_function_instance *fi) { struct module *mod; - if (!fi) + if (!fi || !fi->fd) return; mod = fi->fd->mod; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index db961aaa3740..67c6ffaf4f72 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1260,10 +1260,11 @@ out: static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value) { struct dev_data *dev = fd->private_data; - struct usb_gadget *gadget = dev->gadget; + struct usb_gadget *gadget; long ret = -ENOTTY; spin_lock_irq(&dev->lock); + gadget = dev->gadget; if (dev->state == STATE_DEV_OPENED || dev->state == STATE_DEV_UNBOUND) { /* Not bound to a UDC */ diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 7a21ac81f9c8..af8d4b74c4ba 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2301,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) if (!segs) segs = ERST_DEFAULT_SEGS; - max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; + max_segs = BIT(FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2)); segs = min(segs, max_segs); ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 97a1b53c18ef..ec278a9f9540 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -824,21 +824,18 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, usb_hcd_giveback_urb(hcd, urb, status); } -static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, - struct xhci_ring *ring, struct xhci_td *td) +static void xhci_unmap_one_bounce_buffer(struct xhci_hcd *xhci, + struct xhci_ring *ring, struct xhci_td *td, + struct xhci_segment *seg) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - struct xhci_segment *seg = td->bounce_seg; struct urb *urb = td->urb; size_t len; - if (!ring || !seg || !urb) - return; - if (usb_urb_dir_out(urb)) { dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, DMA_TO_DEVICE); - return; + goto done; } dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, @@ -854,10 +851,29 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, memcpy(urb->transfer_buffer + seg->bounce_offs, seg->bounce_buf, seg->bounce_len); } +done: seg->bounce_len = 0; seg->bounce_offs = 0; } +static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, + struct xhci_ring *ring, struct xhci_td *td) +{ + struct xhci_segment *seg; + int i = 0; + + if (!td->bounce_seg || !ring || !td->urb) + return; + + /* td->bounce_seg is the last one bounced, unmap them all */ + for (seg = td->start_seg; i++ < ring->num_segs; seg = seg->next) { + if (seg->bounce_len) + xhci_unmap_one_bounce_buffer(xhci, ring, td, seg); + if (seg == td->bounce_seg) + break; + } +} + static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ep_ring, int status) { @@ -3685,7 +3701,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, &trb_buff_len, ring->enq_seg)) { send_addr = ring->enq_seg->bounce_dma; - /* assuming TD won't span 2 segs */ + /* TD bounced at least, and last on this seg */ td->bounce_seg = ring->enq_seg; } } @@ -4312,11 +4328,16 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, check_interval(urb, ep_ctx); /* - * Check if this starts the isoc data flow. Relies on hw setting ep ctx - * state after doorbell ring. Consider adding list_empty(td_list) check + * Schedule the URB discontiguously if all previous URBs have completed. + * XXX core can't tell if completions are pending but not running yet. */ - if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) + if (list_empty(&ep_ring->td_list) && + !hcd_periodic_completion_in_progress(xhci_to_hcd(xhci), urb->ep)) { + if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) + xhci_dbg(xhci, "Unexpected running ring at isoc stream start, uframe: %d\n", + xep->next_uframe); xep->next_uframe = -1; + } return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); } diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index ca287b770e8c..f7caa1c5cbb7 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -1000,13 +1000,13 @@ static int __init usb_mdc800_init (void) mdc800->downloaded = 0; mdc800->written = 0; - mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL); + mdc800->irq_urb_buffer=kzalloc (8, GFP_KERNEL); if (!mdc800->irq_urb_buffer) goto cleanup_on_fail; mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL); if (!mdc800->write_urb_buffer) goto cleanup_on_fail; - mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL); + mdc800->download_urb_buffer=kzalloc (64, GFP_KERNEL); if (!mdc800->download_urb_buffer) goto cleanup_on_fail; diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index ed49a3bc859c..895f90c7a3fa 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2357,7 +2357,9 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* probe card type */ + mutex_lock(&us->dev_mutex); result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf); + mutex_unlock(&us->dev_mutex); if (result != USB_STOR_XFER_GOOD) { usb_stor_disconnect(intf); return USB_STOR_TRANSPORT_ERROR; diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index af038b897c6b..c4b28744693b 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -916,7 +916,6 @@ static int realtek_cr_autosuspend_setup(struct us_data *us) us->proto_handler = rts51x_invoke_transport; chip->timer_expires = 0; - timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0); fw5895_init(us); /* enable autosuspend function of the usb device */ @@ -934,10 +933,7 @@ static void realtek_cr_destructor(void *extra) return; #ifdef CONFIG_REALTEK_AUTOPM - if (ss_en) { - timer_delete(&chip->rts51x_suspend_timer); - chip->timer_expires = 0; - } + timer_shutdown_sync(&chip->rts51x_suspend_timer); #endif kfree(chip->status); } @@ -982,6 +978,9 @@ static int init_realtek_cr(struct us_data *us) us->extra = chip; us->extra_destructor = realtek_cr_destructor; +#ifdef CONFIG_REALTEK_AUTOPM + timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0); +#endif us->max_lun = chip->max_lun = rts51x_get_max_lun(us); chip->us = us; diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index d0de5a2488f9..4eec90c82bae 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -62,6 +62,7 @@ struct hd3ss3220 { int id_irq; struct regulator *vbus; + bool vbus_enabled; }; static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) @@ -208,7 +209,7 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on) { int ret; - if (regulator_is_enabled(hd3ss3220->vbus) == on) + if (hd3ss3220->vbus_enabled == on) return; if (on) @@ -216,9 +217,13 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on) else ret = regulator_disable(hd3ss3220->vbus); - if (ret) + if (ret) { dev_err(hd3ss3220->dev, "vbus regulator %s failed: %d\n", on ? "enable" : "disable", ret); + return; + } + + hd3ss3220->vbus_enabled = on; } static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 9b908c46bd7d..afa6fc181397 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -57,6 +57,8 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, */ dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); /* Skip duplicates */ for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) @@ -65,7 +67,7 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, return NULL; } - return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); + return to_typec_switch_dev(dev); } /** @@ -275,7 +277,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_mux_dev **mux_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -290,8 +294,18 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); - return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_mux_dev(dev) == mux_devs[i]) { + put_device(dev); + return NULL; + } + + + return to_typec_mux_dev(dev); } /** @@ -316,7 +330,8 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - NULL, typec_mux_match, + (void **)mux_devs, + typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c index e6b28648f440..926fa017ebaf 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -543,6 +543,8 @@ static void qcom_pmic_typec_pdphy_stop(struct pmic_typec *tcpm) for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++) disable_irq(pmic_typec_pdphy->irq_data[i].irq); + cancel_work_sync(&pmic_typec_pdphy->reset_work); + qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy); regulator_disable(pmic_typec_pdphy->vdd_pdphy); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index bf985efe1cd6..d3523435f3e0 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -683,6 +683,9 @@ static int qcom_pmic_typec_port_start(struct pmic_typec *tcpm, enable_irq(pmic_typec_port->irq_data[i].irq); done: + if (ret) + disable_delayed_work_sync(&pmic_typec_port->cc_debounce_dwork); + return ret; } @@ -693,6 +696,8 @@ static void qcom_pmic_typec_port_stop(struct pmic_typec *tcpm) for (i = 0; i < pmic_typec_port->nr_irqs; i++) disable_irq(pmic_typec_port->irq_data[i].irq); + + disable_delayed_work_sync(&pmic_typec_port->cc_debounce_dwork); } int qcom_pmic_typec_port_probe(struct platform_device *pdev, diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a8cd1959c426..2d6b14aa2085 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -7119,16 +7119,32 @@ static void tcpm_pd_event_handler(struct kthread_work *work) } } if (events & TCPM_SOURCING_VBUS) { - tcpm_log(port, "sourcing vbus"); /* * In fast role swap case TCPC autonomously sources vbus. Set vbus_source - * true as TCPM wouldn't have called tcpm_set_vbus. + * true conditionally as TCPM wouldn't have called tcpm_set_vbus. + * If TCPM calls tcpm_set_vbus to source vbus, vbus_source would already + * be true. * - * When vbus is sourced on the command on TCPM i.e. TCPM called - * tcpm_set_vbus to source vbus, vbus_source would already be true. + * When TCPM_FRS_EVENT and TCPM_SOURCING_VBUS arrive simultaneously, + * handling TCPM_FRS_EVENT above transitions the state to AMS_START + * with upcoming_state FR_SWAP_SEND. */ - port->vbus_source = true; - _tcpm_pd_vbus_on(port); + + if (tcpm_port_is_source(port) || + tcpm_port_is_debug_source(port) || + (port->state == AMS_START && port->upcoming_state == FR_SWAP_SEND) || + port->state == FR_SWAP_SEND || + port->state == FR_SWAP_SEND_TIMEOUT || + port->state == FR_SWAP_SNK_SRC_TRANSITION_TO_OFF || + port->state == FR_SWAP_SNK_SRC_NEW_SINK_READY || + port->state == FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED) { + tcpm_log(port, "sourcing vbus"); + port->vbus_source = true; + _tcpm_pd_vbus_on(port); + } else { + tcpm_log(port, "Discarding sourcing vbus! Invalid state %s", + tcpm_states[port->state]); + } } if (events & TCPM_PORT_CLEAN) { tcpm_log(port, "port clean"); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 522f56742aa9..f76f563dc42b 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -114,7 +114,6 @@ struct tps6598x_intel_vid_status_reg { __le32 attention_vdo; __le16 enter_vdo; __le16 device_mode; - __le16 cable_mode; } __packed; /* Standard Task return codes */ @@ -731,9 +730,19 @@ static void cd321x_typec_update_mode(struct tps6598x *tps, struct cd321x_status cd321x->state.mode == TYPEC_TBT_MODE) return; - tbt_data.cable_mode = le16_to_cpu(st->intel_vid_status.cable_mode); - tbt_data.device_mode = le16_to_cpu(st->intel_vid_status.device_mode); - tbt_data.enter_vdo = le16_to_cpu(st->intel_vid_status.enter_vdo); + tbt_data.cable_mode = TBT_MODE | + TBT_SET_CABLE_SPEED(TPS_DATA_STATUS_TBT_CABLE_SPEED(st->data_status)) | + TBT_SET_CABLE_ROUNDED(TPS_DATA_STATUS_TBT_CABLE_GEN(st->data_status)); + if (st->data_status & TPS_DATA_STATUS_OPTICAL_CABLE) + tbt_data.cable_mode |= TBT_CABLE_OPTICAL; + if (st->data_status & TPS_DATA_STATUS_ACTIVE_LINK_TRAIN) + tbt_data.cable_mode |= TBT_CABLE_LINK_TRAINING; + if (st->data_status & TPS_DATA_STATUS_ACTIVE_CABLE) + tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE; + tbt_data.device_mode = TBT_MODE | + (u32)le16_to_cpu(st->intel_vid_status.device_mode) << 16; + tbt_data.enter_vdo = + (u32)le16_to_cpu(st->intel_vid_status.enter_vdo) << 16; cd321x->state.alt = cd321x->port_altmode_tbt; cd321x->state.mode = TYPEC_TBT_MODE; cd321x->state.data = &tbt_data; diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index d4140f4da5bb..11ab58ba9a18 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -210,10 +210,10 @@ #define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x)) #define TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK GENMASK(27, 25) -#define TPS_DATA_STATUS_TBT_CABLE_SPEED \ +#define TPS_DATA_STATUS_TBT_CABLE_SPEED(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK, (x)) #define TPS_DATA_STATUS_TBT_CABLE_GEN_MASK GENMASK(29, 28) -#define TPS_DATA_STATUS_TBT_CABLE_GEN \ +#define TPS_DATA_STATUS_TBT_CABLE_GEN(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_GEN_MASK, (x)) /* Map data status to DP spec assignments */ diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 7067f2561b84..8d2032d0762c 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -74,7 +74,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) cur = 0xff; } - if (cur != 0xff) { + if (cur < UCSI_MAX_ALTMODES) { ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; goto err_unlock; } diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index a5837c0feb05..100f8920624b 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -696,7 +696,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) size = np * sizeof(*iso); - buff = kcalloc(np, sizeof(*iso), GFP_KERNEL); + buff = kzalloc_objs(*iso, np); if (!buff) return -ENOMEM; diff --git a/drivers/vdpa/alibaba/eni_vdpa.c b/drivers/vdpa/alibaba/eni_vdpa.c index fd6fdba46094..1288402d3dd8 100644 --- a/drivers/vdpa/alibaba/eni_vdpa.c +++ b/drivers/vdpa/alibaba/eni_vdpa.c @@ -216,7 +216,10 @@ static void eni_vdpa_set_status(struct vdpa_device *vdpa, u8 status) if (status & VIRTIO_CONFIG_S_DRIVER_OK && !(s & VIRTIO_CONFIG_S_DRIVER_OK)) { - eni_vdpa_request_irq(eni_vdpa); + if (eni_vdpa_request_irq(eni_vdpa)) { + WARN_ON(1); + return; + } } vp_legacy_set_status(ldev, status); diff --git a/drivers/vdpa/ifcvf/ifcvf_main.c b/drivers/vdpa/ifcvf/ifcvf_main.c index ab6d6ab3b3d8..2af1cec95884 100644 --- a/drivers/vdpa/ifcvf/ifcvf_main.c +++ b/drivers/vdpa/ifcvf/ifcvf_main.c @@ -724,7 +724,8 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, if (config->device_features & ~device_features) { IFCVF_ERR(pdev, "The provisioned features 0x%llx are not supported by this device with features 0x%llx\n", config->device_features, device_features); - return -EINVAL; + ret = -EINVAL; + goto err; } device_features &= config->device_features; } diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c index 23e280a29209..85a3d35ea1e4 100644 --- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c +++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c @@ -600,6 +600,8 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, ret = dev_set_name(&vdpa_dev->dev, "%s", name); else ret = dev_set_name(&vdpa_dev->dev, "vdpa%u", vdpa_dev->index); + if (ret) + goto vdpa_dev_put; ret = _vdpa_register_device(&oct_vdpa->vdpa, oct_hw->nr_vring); if (ret) { diff --git a/drivers/vdpa/pds/vdpa_dev.c b/drivers/vdpa/pds/vdpa_dev.c index 43426bd971ac..77d679f6763d 100644 --- a/drivers/vdpa/pds/vdpa_dev.c +++ b/drivers/vdpa/pds/vdpa_dev.c @@ -731,6 +731,12 @@ static int pds_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, notify = vp_modern_map_vq_notify(&pdsv->vdpa_aux->vd_mdev, i, &pdsv->vqs[i].notify_pa); + if (!notify) { + err = -EINVAL; + dev_err(dev, "Fail to map vq notify %d\n", i); + goto err_unmap; + } + pds_vdpa_init_vqs_entry(pdsv, i, notify); } diff --git a/drivers/vdpa/solidrun/snet_main.c b/drivers/vdpa/solidrun/snet_main.c index 28d55315df2a..3e2cea1e45f3 100644 --- a/drivers/vdpa/solidrun/snet_main.c +++ b/drivers/vdpa/solidrun/snet_main.c @@ -418,11 +418,15 @@ static int snet_request_irqs(struct pci_dev *pdev, struct snet *snet) snet->vqs[i]->irq_name, snet->vqs[i]); if (ret) { SNET_ERR(pdev, "Failed to request IRQ\n"); - return ret; + goto err_free_irqs; } snet->vqs[i]->irq = irq; } return 0; + +err_free_irqs: + snet_free_irqs(snet); + return ret; } static void snet_set_status(struct vdpa_device *vdev, u8 status) diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index f70f454dde8e..76dd5b0828d7 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -79,10 +79,11 @@ static void vdpasim_blk_buffer_unlock(struct vdpasim_blk *blk) static bool vdpasim_blk_check_range(struct vdpasim *vdpasim, u64 start_sector, u64 num_sectors, u64 max_sectors) { - if (start_sector > VDPASIM_BLK_CAPACITY) { + if (start_sector >= VDPASIM_BLK_CAPACITY) { dev_dbg(&vdpasim->vdpa.dev, "starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n", start_sector, VDPASIM_BLK_CAPACITY); + return false; } if (num_sectors > max_sectors) { diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c index 29fd14ce5860..a6514b5ccd86 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c @@ -225,10 +225,15 @@ static void vdpasim_net_work(struct vdpasim *vdpasim) break; } - ++tx_pkts; read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov, net->buffer, PAGE_SIZE); + if (read <= 0) { + ++tx_errors; + vdpasim_net_complete(txq, 0); + continue; + } + ++tx_pkts; tx_bytes += read; if (!receive_filter(vdpasim, read)) { diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 9891cd2cf712..49a231bdf948 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -506,7 +506,7 @@ static void vduse_dev_reset(struct vduse_dev *dev) } scoped_guard(rwsem_write, &dev->rwsem) { - dev->suspended = false; + WRITE_ONCE(dev->suspended, false); dev->status = 0; dev->driver_features = 0; dev->generation++; @@ -567,11 +567,17 @@ static int vduse_vdpa_set_vq_address(struct vdpa_device *vdpa, u16 idx, static void vduse_vq_kick(struct vduse_virtqueue *vq) { - guard(rwsem_read)(&vq->dev->rwsem); - if (vq->dev->suspended) + /* + * This runs in the context of the vdpa kick_vq op, which may be + * atomic (e.g. virtio-blk kicks from blk-mq dispatch under + * rcu_read_lock()), so dev->rwsem must not be taken here. + * dev->suspended is checked under kick_lock instead and + * vduse_vdpa_suspend() cycles every kick_lock after setting it. + */ + guard(spinlock)(&vq->kick_lock); + if (READ_ONCE(vq->dev->suspended)) return; - guard(spinlock)(&vq->kick_lock); scoped_guard(spinlock_bh, &vq->ready_lock) if (!vq->ready) return; @@ -946,7 +952,17 @@ static int vduse_vdpa_suspend(struct vdpa_device *vdpa) ret = vduse_dev_msg_sync(dev, &msg); if (ret == 0) { scoped_guard(rwsem_write, &dev->rwsem) - dev->suspended = true; + WRITE_ONCE(dev->suspended, true); + + /* + * Kicks check dev->suspended under kick_lock without taking + * the rwsem: cycle each kick_lock so that no kick that has + * already passed the check is still in flight after this. + */ + for (u32 i = 0; i < dev->vq_num; i++) { + spin_lock(&dev->vqs[i]->kick_lock); + spin_unlock(&dev->vqs[i]->kick_lock); + } cancel_work_sync(&dev->inject); for (u32 i = 0; i < dev->vq_num; i++) @@ -1866,11 +1882,11 @@ static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd, break; } default: - ret = -ENOIOCTLCMD; - break; + return vduse_dev_ioctl(file, cmd, + (unsigned long)compat_ptr(arg)); } - return vduse_dev_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + return ret; } #else #define vduse_dev_compat_ioctl compat_ptr_ioctl @@ -2211,7 +2227,9 @@ static bool vduse_validate_config(struct vduse_dev_config *config, return false; } - if (config->vq_align > PAGE_SIZE) + if (config->vq_align < VRING_USED_ALIGN_SIZE || + !is_power_of_2(config->vq_align) || + config->vq_align > PAGE_SIZE) return false; if (config->config_size > PAGE_SIZE) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index c25929dd4425..2cc730729e08 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -1705,6 +1705,8 @@ static int vhost_net_set_features(struct vhost_net *n, const u64 *features) if (virtio_features_test_bit(features, VIRTIO_F_ACCESS_PLATFORM)) { if (vhost_init_device_iotlb(&n->dev)) goto out_unlock; + } else { + vhost_clear_device_iotlb(&n->dev); } for (i = 0; i < VHOST_NET_VQ_MAX; ++i) { diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 7a1f39a327da..4f8c0260bc9e 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -210,7 +210,37 @@ static const int vhost_scsi_bits[] = { #define VHOST_SCSI_MAX_EVENT 128 static unsigned vhost_scsi_max_io_vqs = 128; -module_param_named(max_io_vqs, vhost_scsi_max_io_vqs, uint, 0644); + +static int vhost_scsi_set_max_io_vqs(const char *val, + const struct kernel_param *kp) +{ + unsigned int max_io_vqs; + int ret; + + ret = kstrtouint(val, 0, &max_io_vqs); + if (ret) + return ret; + + if (max_io_vqs > VHOST_SCSI_MAX_IO_VQ) { + pr_err("Invalid max_io_vqs of %u. Using %u.\n", + max_io_vqs, VHOST_SCSI_MAX_IO_VQ); + max_io_vqs = VHOST_SCSI_MAX_IO_VQ; + } else if (!max_io_vqs) { + pr_err("Invalid max_io_vqs of 0. Using 1.\n"); + max_io_vqs = 1; + } + + WRITE_ONCE(vhost_scsi_max_io_vqs, max_io_vqs); + return 0; +} + +static const struct kernel_param_ops vhost_scsi_max_io_vqs_op = { + .set = vhost_scsi_set_max_io_vqs, + .get = param_get_uint, +}; + +module_param_cb(max_io_vqs, &vhost_scsi_max_io_vqs_op, + &vhost_scsi_max_io_vqs, 0644); MODULE_PARM_DESC(max_io_vqs, "Set the max number of IO virtqueues a vhost scsi device can support. The default is 128. The max is 1024."); struct vhost_scsi_virtqueue { @@ -2290,21 +2320,14 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) struct vhost_scsi_virtqueue *svq; struct vhost_scsi *vs; struct vhost_virtqueue **vqs; - int r = -ENOMEM, i, nvqs = vhost_scsi_max_io_vqs; + int r = -ENOMEM, i, nvqs; vs = kvzalloc_obj(*vs); if (!vs) goto err_vs; vs->inline_sg_cnt = vhost_scsi_inline_sg_cnt; - if (nvqs > VHOST_SCSI_MAX_IO_VQ) { - pr_err("Invalid max_io_vqs of %d. Using %d.\n", nvqs, - VHOST_SCSI_MAX_IO_VQ); - nvqs = VHOST_SCSI_MAX_IO_VQ; - } else if (nvqs == 0) { - pr_err("Invalid max_io_vqs of %d. Using 1.\n", nvqs); - nvqs = 1; - } + nvqs = READ_ONCE(vhost_scsi_max_io_vqs); nvqs += VHOST_SCSI_VQ_IO; vs->old_inflight = kmalloc_objs(*vs->old_inflight, nvqs, @@ -2312,7 +2335,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) if (!vs->old_inflight) goto err_inflight; - vs->vqs = kmalloc_objs(*vs->vqs, nvqs, GFP_KERNEL | __GFP_ZERO); + vs->vqs = kvzalloc_objs(*vs->vqs, nvqs); if (!vs->vqs) goto err_vqs; @@ -2348,7 +2371,7 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) return 0; err_local_vqs: - kfree(vs->vqs); + kvfree(vs->vqs); err_vqs: kfree(vs->old_inflight); err_inflight: @@ -2369,7 +2392,7 @@ static int vhost_scsi_release(struct inode *inode, struct file *f) vhost_dev_stop(&vs->dev); vhost_dev_cleanup(&vs->dev); kfree(vs->dev.vqs); - kfree(vs->vqs); + kvfree(vs->vqs); kfree(vs->old_inflight); kvfree(vs); return 0; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index c3d913bd7cac..a31786796d4c 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -58,9 +58,12 @@ struct vhost_vdpa { struct cdev cdev; atomic_t opened; u32 nvqs; + u16 vq_num_max; int virtio_id; int minor; struct eventfd_ctx *config_ctx; + /* Serialises vhost_vdpa_config_cb() against config_ctx being replaced. */ + spinlock_t config_lock; int in_batch; struct vdpa_iova_range range; u32 batch_asid; @@ -194,10 +197,12 @@ static irqreturn_t vhost_vdpa_virtqueue_cb(void *private) static irqreturn_t vhost_vdpa_config_cb(void *private) { struct vhost_vdpa *v = private; - struct eventfd_ctx *config_ctx = v->config_ctx; + unsigned long flags; - if (config_ctx) - eventfd_signal(config_ctx); + spin_lock_irqsave(&v->config_lock, flags); + if (v->config_ctx) + eventfd_signal(v->config_ctx); + spin_unlock_irqrestore(&v->config_lock, flags); return IRQ_HANDLED; } @@ -236,7 +241,9 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) static int _compat_vdpa_reset(struct vhost_vdpa *v) { struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; u32 flags = 0; + int ret; v->suspended = false; @@ -246,7 +253,14 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v) VDPA_RESET_F_CLEAN_MAP : 0; } - return vdpa_reset(vdpa, flags); + v->vq_num_max = 0; + ret = vdpa_reset(vdpa, flags); + if (!ret) { + /* Some backends derive the max from mutable queue state. */ + v->vq_num_max = ops->get_vq_num_max(vdpa); + } + + return ret; } static int vhost_vdpa_reset(struct vhost_vdpa *v) @@ -518,15 +532,22 @@ static long vhost_vdpa_get_vring_num(struct vhost_vdpa *v, u16 __user *argp) static void vhost_vdpa_config_put(struct vhost_vdpa *v) { - if (v->config_ctx) { - eventfd_ctx_put(v->config_ctx); - v->config_ctx = NULL; - } + struct eventfd_ctx *ctx; + unsigned long flags; + + spin_lock_irqsave(&v->config_lock, flags); + ctx = v->config_ctx; + v->config_ctx = NULL; + spin_unlock_irqrestore(&v->config_lock, flags); + + if (ctx) + eventfd_ctx_put(ctx); } static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp) { struct vdpa_callback cb; + unsigned long flags; int fd; struct eventfd_ctx *ctx; @@ -536,18 +557,20 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp) return -EFAULT; ctx = fd == VHOST_FILE_UNBIND ? NULL : eventfd_ctx_fdget(fd); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + spin_lock_irqsave(&v->config_lock, flags); swap(ctx, v->config_ctx); + spin_unlock_irqrestore(&v->config_lock, flags); - if (!IS_ERR_OR_NULL(ctx)) + /* + * The callback can no longer reach the old context, so this is the + * last reference to it. + */ + if (ctx) eventfd_ctx_put(ctx); - if (IS_ERR(v->config_ctx)) { - long ret = PTR_ERR(v->config_ctx); - - v->config_ctx = NULL; - return ret; - } - v->vdpa->config->set_config_cb(v->vdpa, &cb); return 0; @@ -648,9 +671,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, u32 idx; long r; - r = get_user(idx, (u32 __user *)argp); - if (r < 0) - return r; + if (cmd == VHOST_SET_VRING_NUM) { + if (copy_from_user(&s, argp, sizeof(s))) + return -EFAULT; + idx = s.index; + } else { + r = get_user(idx, (u32 __user *)argp); + if (r < 0) + return r; + } if (idx >= v->nvqs) return -ENOBUFS; @@ -659,6 +688,23 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, vq = &v->vqs[idx]; switch (cmd) { + case VHOST_SET_VRING_NUM: + mutex_lock(&vq->mutex); + if (vq->private_data) { + r = -EBUSY; + } else if (!s.num || s.num > 0xffff || + s.num > v->vq_num_max || + (s.num & (s.num - 1))) { + r = -EINVAL; + } else { + vq->num = s.num; + r = 0; + } + mutex_unlock(&vq->mutex); + if (r) + return r; + ops->set_vq_num(vdpa, idx, s.num); + return 0; case VHOST_VDPA_SET_VRING_ENABLE: if (copy_from_user(&s, argp, sizeof(s))) return -EFAULT; @@ -772,9 +818,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, ops->set_vq_cb(vdpa, idx, &cb); break; - case VHOST_SET_VRING_NUM: - ops->set_vq_num(vdpa, idx, vq->num); - break; } return r; @@ -1613,6 +1656,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa) } atomic_set(&v->opened, 0); + spin_lock_init(&v->config_lock); v->minor = minor; v->vdpa = vdpa; v->nvqs = vdpa->nvqs; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 14637cff0bd4..44cac11b68d2 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -344,6 +344,17 @@ static void __vhost_vq_meta_reset(struct vhost_virtqueue *vq) vq->meta_iotlb[j] = NULL; } +/* Caller must hold the virtqueue mutex. */ +static void vhost_vq_invalidate_access(struct vhost_virtqueue *vq) +{ + vq->desc = NULL; + vq->avail = NULL; + vq->used = NULL; + vq->log_used = false; + vq->log_addr = -1ull; + __vhost_vq_meta_reset(vq); +} + static void vhost_vq_meta_reset(struct vhost_dev *d) { int i; @@ -392,6 +403,7 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->busyloop_timeout = 0; vq->umem = NULL; vq->iotlb = NULL; + vq->iotlb_miss = NULL; rcu_assign_pointer(vq->worker, NULL); vhost_vring_call_reset(&vq->call_ctx); __vhost_vq_meta_reset(vq); @@ -1180,6 +1192,21 @@ void vhost_dev_stop(struct vhost_dev *dev) } EXPORT_SYMBOL_GPL(vhost_dev_stop); +static void vhost_free_msg_locked(struct vhost_msg_node *node) +{ + if (node->vq->iotlb_miss == node) + node->vq->iotlb_miss = NULL; + kfree(node); +} + +static void vhost_free_msg(struct vhost_dev *dev, + struct vhost_msg_node *node) +{ + spin_lock(&dev->iotlb_lock); + vhost_free_msg_locked(node); + spin_unlock(&dev->iotlb_lock); +} + void vhost_clear_msg(struct vhost_dev *dev) { struct vhost_msg_node *node, *n; @@ -1188,12 +1215,12 @@ void vhost_clear_msg(struct vhost_dev *dev) list_for_each_entry_safe(node, n, &dev->read_list, node) { list_del(&node->node); - kfree(node); + vhost_free_msg_locked(node); } list_for_each_entry_safe(node, n, &dev->pending_list, node) { list_del(&node->node); - kfree(node); + vhost_free_msg_locked(node); } spin_unlock(&dev->iotlb_lock); @@ -1602,7 +1629,7 @@ static void vhost_iotlb_notify_vq(struct vhost_dev *d, vq_msg->type == VHOST_IOTLB_MISS) { vhost_poll_queue(&node->vq->poll); list_del(&node->node); - kfree(node); + vhost_free_msg_locked(node); } } @@ -1816,7 +1843,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to, ret = copy_to_iter(start, size, to); if (ret != size || msg->type != VHOST_IOTLB_MISS) { - kfree(node); + vhost_free_msg(dev, node); return ret; } vhost_enqueue_msg(dev, &dev->pending_list, node); @@ -1848,7 +1875,19 @@ static int vhost_iotlb_miss(struct vhost_virtqueue *vq, u64 iova, int access) msg->iova = iova; msg->perm = access; - vhost_enqueue_msg(dev, &dev->read_list, node); + spin_lock(&dev->iotlb_lock); + /* VQ processing stops at the first miss until userspace resolves it. */ + if (vq->iotlb_miss) { + spin_unlock(&dev->iotlb_lock); + kfree(node); + return 0; + } + + vq->iotlb_miss = node; + list_add_tail(&node->node, &dev->read_list); + spin_unlock(&dev->iotlb_lock); + + wake_up_interruptible_poll(&dev->wait, EPOLLIN | EPOLLRDNORM); return 0; } @@ -1918,6 +1957,13 @@ int vq_meta_prefetch(struct vhost_virtqueue *vq) { unsigned int num = vq->num; + /* + * vhost_vq_invalidate_access() clears all three addresses together. + * A single zero address may be a valid GIOVA in IOTLB mode. + */ + if (!vq->desc && !vq->avail && !vq->used) + return 0; + if (!vq->iotlb) return 1; @@ -2287,6 +2333,40 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg } EXPORT_SYMBOL_GPL(vhost_vring_ioctl); +/* Caller must hold the device mutex. */ +void vhost_clear_device_iotlb(struct vhost_dev *d) +{ + struct vhost_iotlb *iotlb; + int i; + + iotlb = d->iotlb; + if (!iotlb) + return; + + vhost_dev_lock_vqs(d); + + /* + * vhost_dev_lock_vqs() takes all VQ mutexes in index order. Drop the + * device-wide view while they are held, then clear each per-VQ view + * and its cached ring access before releasing the locks. Workers + * cannot observe a mixed address-space state during this handoff. + */ + d->iotlb = NULL; + + for (i = 0; i < d->nvqs; ++i) { + struct vhost_virtqueue *vq = d->vqs[i]; + + vq->iotlb = NULL; + vhost_vq_invalidate_access(vq); + } + + vhost_dev_unlock_vqs(d); + vhost_clear_msg(d); + vhost_iotlb_free(iotlb); + wake_up_interruptible_poll(&d->wait, EPOLLIN | EPOLLRDNORM); +} +EXPORT_SYMBOL_GPL(vhost_clear_device_iotlb); + int vhost_init_device_iotlb(struct vhost_dev *d) { struct vhost_iotlb *niotlb, *oiotlb; @@ -2307,7 +2387,10 @@ int vhost_init_device_iotlb(struct vhost_dev *d) mutex_lock(&vq->mutex); vq->iotlb = niotlb; - __vhost_vq_meta_reset(vq); + if (oiotlb) + __vhost_vq_meta_reset(vq); + else + vhost_vq_invalidate_access(vq); mutex_unlock(&vq->mutex); } diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 0192ade6e749..39e6121f7525 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -29,6 +29,7 @@ struct vhost_work { struct vhost_worker; struct vhost_dev; +struct vhost_msg_node; struct vhost_worker_ops { int (*create)(struct vhost_worker *worker, struct vhost_dev *dev, @@ -148,6 +149,8 @@ struct vhost_virtqueue { /* Protected by virtqueue mutex. */ struct vhost_iotlb *umem; struct vhost_iotlb *iotlb; + /* Protected by dev->iotlb_lock. */ + struct vhost_msg_node *iotlb_miss; void *private_data; VIRTIO_DECLARE_FEATURES(acked_features); u64 acked_backend_features; @@ -277,6 +280,7 @@ ssize_t vhost_chr_read_iter(struct vhost_dev *dev, struct iov_iter *to, int noblock); ssize_t vhost_chr_write_iter(struct vhost_dev *dev, struct iov_iter *from); +void vhost_clear_device_iotlb(struct vhost_dev *d); int vhost_init_device_iotlb(struct vhost_dev *d); void vhost_iotlb_map_free(struct vhost_iotlb *iotlb, diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index 9aaab6bb8061..abed1fbcf66c 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -868,6 +868,8 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features) if ((features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) { if (vhost_init_device_iotlb(&vsock->dev)) goto err; + } else { + vhost_clear_device_iotlb(&vsock->dev); } vsock->seqpacket_allow = features & (1ULL << VIRTIO_VSOCK_F_SEQPACKET); diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c index 5bca34c45cef..c3011b61a94b 100644 --- a/drivers/video/fbdev/atafb.c +++ b/drivers/video/fbdev/atafb.c @@ -3360,7 +3360,11 @@ static int __init atafb_probe(struct platform_device *pdev) memset (screen_base, 0, external_len); /* framebuffer in SV RAM: enable the SuperBlitter */ - if (external_addr >= 0xa0000000) { + if (external_addr >= 0xa0000000 && + ((external_pmode == FB_TYPE_PACKED_PIXELS && + external_depth == 8) || + (external_pmode == -1 && + (external_depth == 16 || external_depth == 32)))) { svblit_regs = ioremap(SVBLIT_REGS_PHYS, 0x100); if (svblit_regs) { svblit_fw = svblit_rd(SVBLIT_VERSION) & 0x1ff; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 23b3c536d53d..01715873ea49 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -660,6 +660,13 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, erase &= ~0x400; logo_height = fb_prepare_logo(info, par->rotate); logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height); + logo_lines = min(logo_lines, rows); + logo_lines = min(logo_lines, new_rows - 1); + if (logo_lines <= 0) { + logo_lines = 0; + logo_shown = FBCON_LOGO_DONTSHOW; + return; + } q = (unsigned short *) (vc->vc_origin + vc->vc_size_row * rows); step = logo_lines * cols; diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c index 8f430d9e8054..0202ca8cbfc2 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-sony-acx565akm.c @@ -210,12 +210,13 @@ static void set_display_state(struct panel_drv_data *ddata, int enabled) static int panel_enabled(struct panel_drv_data *ddata) { + __be32 disp_status_be; u32 disp_status; int enabled; acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS, - (u8 *)&disp_status, 4); - disp_status = __be32_to_cpu(disp_status); + (u8 *)&disp_status_be, 4); + disp_status = __be32_to_cpu(disp_status_be); enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); dev_dbg(&ddata->spi->dev, "LCD panel %senabled by bootloader (status 0x%04x)\n", diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index c4fdecafd856..4d185c754284 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -665,6 +665,10 @@ static int ssd1307fb_probe(struct i2c_client *client) spin_lock_init(&par->damage_lock); par->device_info = device_get_match_data(dev); + if (!par->device_info) { + ret = -ENODEV; + goto fb_alloc_error; + } par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(par->reset)) { diff --git a/drivers/video/fbdev/vfb.c b/drivers/video/fbdev/vfb.c index 5b7965f36c5e..de137b2bdaed 100644 --- a/drivers/video/fbdev/vfb.c +++ b/drivers/video/fbdev/vfb.c @@ -78,6 +78,13 @@ static int vfb_pan_display(struct fb_var_screeninfo *var, static int vfb_mmap(struct fb_info *info, struct vm_area_struct *vma); +static void vfb_destroy(struct fb_info *info) +{ + vfree(info->screen_buffer); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); +} + static const struct fb_ops vfb_ops = { .owner = THIS_MODULE, __FB_DEFAULT_SYSMEM_OPS_RDWR, @@ -87,6 +94,7 @@ static const struct fb_ops vfb_ops = { .fb_pan_display = vfb_pan_display, __FB_DEFAULT_SYSMEM_OPS_DRAW, .fb_mmap = vfb_mmap, + .fb_destroy = vfb_destroy, }; /* @@ -485,9 +493,6 @@ static void vfb_remove(struct platform_device *dev) if (info) { unregister_framebuffer(info); - vfree(videomemory); - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); } } diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 75bb4ffe3b87..b6c9e927bef5 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -604,8 +604,8 @@ void unregister_virtio_device(struct virtio_device *dev) { int index = dev->index; /* save for after device release */ - device_unregister(&dev->dev); virtio_debug_device_exit(dev); + device_unregister(&dev->dev); ida_free(&virtio_index_ida, index); } EXPORT_SYMBOL_GPL(unregister_virtio_device); diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c index deec24e8e682..e3bd0b9616f9 100644 --- a/drivers/virtio/virtio_input.c +++ b/drivers/virtio/virtio_input.c @@ -49,9 +49,12 @@ static void virtinput_recv_events(struct virtqueue *vq) le16_to_cpu(event->code), le32_to_cpu(event->value)); spin_lock_irqsave(&vi->lock, flags); + if (!vi->ready) + continue; virtinput_queue_evtbuf(vi, event); } - virtqueue_kick(vq); + if (vi->ready) + virtqueue_kick(vq); } spin_unlock_irqrestore(&vi->lock, flags); } @@ -331,6 +334,7 @@ err_input_register: spin_lock_irqsave(&vi->lock, flags); vi->ready = false; spin_unlock_irqrestore(&vi->lock, flags); + virtio_reset_device(vdev); err_mt_init_slots: input_free_device(vi->idev); err_input_alloc: @@ -350,8 +354,9 @@ static void virtinput_remove(struct virtio_device *vdev) vi->ready = false; spin_unlock_irqrestore(&vi->lock, flags); - input_unregister_device(vi->idev); + /* Callbacks use vi->idev. */ virtio_reset_device(vdev); + input_unregister_device(vi->idev); while ((buf = virtqueue_detach_unused_buf(vi->sts)) != NULL) kfree(buf); vdev->config->del_vqs(vdev); diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 316f03b97356..faae58e3401a 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -88,6 +88,9 @@ struct virtio_mmio_device { void __iomem *base; unsigned long version; + + /* True if enable_irq_wake() succeeded for the shared IRQ. */ + bool wake_irq_enabled; }; /* Configuration interface */ @@ -336,11 +339,17 @@ static void vm_del_vqs(struct virtio_device *vdev) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); struct virtqueue *vq, *n; + int irq = platform_get_irq(vm_dev->pdev, 0); list_for_each_entry_safe(vq, n, &vdev->vqs, list) vm_del_vq(vq); - free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); + if (vm_dev->wake_irq_enabled) { + disable_irq_wake(irq); + vm_dev->wake_irq_enabled = false; + } + + free_irq(irq, vm_dev); } static void vm_synchronize_cbs(struct virtio_device *vdev) @@ -467,8 +476,9 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, if (err) return err; - if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) - enable_irq_wake(irq); + if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source") && + !enable_irq_wake(irq)) + vm_dev->wake_irq_enabled = true; for (i = 0; i < nvqs; ++i) { struct virtqueue_info *vqi = &vqs_info[i]; diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 10371ecbc054..b90c174450b2 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -120,7 +120,9 @@ static irqreturn_t vp_interrupt(int irq, void *opaque) if (isr & VIRTIO_PCI_ISR_CONFIG) vp_config_changed(irq, opaque); - return vp_vring_interrupt(irq, opaque); + vp_vring_interrupt(irq, opaque); + + return IRQ_HANDLED; } static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5c169fbb418a..db678f5a80e0 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -1670,7 +1670,7 @@ static inline int virtqueue_add_packed(struct vring_virtqueue *vq, struct scatterlist *sg; unsigned int i, n, c, descs_used, err_idx, len; __le16 head_flags, flags; - u16 head, id, prev, curr, avail_used_flags; + u16 head, id, prev, curr, avail_used_flags, unpub_flags; int err; START_USE(vq); @@ -1798,15 +1798,30 @@ unmap_release: curr = vq->free_head; vq->packed.avail_used_flags = avail_used_flags; + unpub_flags = avail_used_flags ^ (1 << VRING_PACKED_DESC_F_AVAIL | + 1 << VRING_PACKED_DESC_F_USED); for (n = 0; n < total_sg; n++) { if (i == err_idx) break; + /* + * The mapping loop made every descriptor but the head + * available. Stamp the previous wrap counter's AVAIL and USED + * bits on those, so that a later and shorter chain at this head + * does not leave one of them available beyond its own last + * descriptor. Marking them used instead would hand + * is_used_desc_packed() a completion we never made. + */ + if (i != head) + desc[i].flags = cpu_to_le16(unpub_flags); vring_unmap_extra_packed(vq, &vq->packed.desc_extra[curr]); curr = vq->packed.desc_extra[curr].next; i++; - if (i >= vq->packed.vring.num) + if (i >= vq->packed.vring.num) { i = 0; + unpub_flags ^= 1 << VRING_PACKED_DESC_F_AVAIL | + 1 << VRING_PACKED_DESC_F_USED; + } } END_USE(vq); @@ -1828,7 +1843,7 @@ static inline int virtqueue_add_packed_in_order(struct vring_virtqueue *vq, struct scatterlist *sg; unsigned int i, n, sg_count, err_idx, total_in_len = 0; __le16 head_flags, flags; - u16 head, avail_used_flags; + u16 head, avail_used_flags, unpub_flags; bool avail_wrap_counter; int err; @@ -1955,14 +1970,29 @@ unmap_release: i = head; vq->packed.avail_used_flags = avail_used_flags; vq->packed.avail_wrap_counter = avail_wrap_counter; + unpub_flags = avail_used_flags ^ (1 << VRING_PACKED_DESC_F_AVAIL | + 1 << VRING_PACKED_DESC_F_USED); for (n = 0; n < total_sg; n++) { if (i == err_idx) break; + /* + * The mapping loop made every descriptor but the head + * available. Stamp the previous wrap counter's AVAIL and USED + * bits on those, so that a later and shorter chain at this head + * does not leave one of them available beyond its own last + * descriptor. Marking them used instead would hand + * is_used_desc_packed() a completion we never made. + */ + if (i != head) + desc[i].flags = cpu_to_le16(unpub_flags); vring_unmap_extra_packed(vq, &vq->packed.desc_extra[i]); i++; - if (i >= vq->packed.vring.num) + if (i >= vq->packed.vring.num) { i = 0; + unpub_flags ^= 1 << VRING_PACKED_DESC_F_AVAIL | + 1 << VRING_PACKED_DESC_F_USED; + } } END_USE(vq); diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index de2af696de6c..6bcf4567a929 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -352,7 +352,7 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, continue; } - vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, vqi->callback, + vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx, vqi->callback, vqi->name, vqi->ctx); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); @@ -360,7 +360,8 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, } if (has_affinity) - ops->set_vq_affinity(vdpa, i, &masks[i]); + ops->set_vq_affinity(vdpa, queue_idx, &masks[i]); + queue_idx++; } cb.callback = virtio_vdpa_config_cb; diff --git a/drivers/watchdog/msc313e_wdt.c b/drivers/watchdog/msc313e_wdt.c index d962589e2c55..4a5cce2a16b1 100644 --- a/drivers/watchdog/msc313e_wdt.c +++ b/drivers/watchdog/msc313e_wdt.c @@ -31,20 +31,36 @@ struct msc313e_wdt_priv { struct clk *clk; }; +static u32 msc313e_wdt_get_hw_timeout(struct msc313e_wdt_priv *priv) +{ + u16 low, high; + + low = readw(priv->base + REG_WDT_MAX_PRD_L); + high = readw(priv->base + REG_WDT_MAX_PRD_H); + + return ((u32)high << 16) | low; +} + +static void msc313e_wdt_set_hw_timeout(struct msc313e_wdt_priv *priv, + unsigned int timeout) +{ + u32 t = timeout * clk_get_rate(priv->clk); + + writew(t & 0xffff, priv->base + REG_WDT_MAX_PRD_L); + writew((t >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H); + writew(1, priv->base + REG_WDT_CLR); +} + static int msc313e_wdt_start(struct watchdog_device *wdev) { struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev); - u32 timeout; int err; err = clk_prepare_enable(priv->clk); if (err) return err; - timeout = wdev->timeout * clk_get_rate(priv->clk); - writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L); - writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H); - writew(1, priv->base + REG_WDT_CLR); + msc313e_wdt_set_hw_timeout(priv, wdev->timeout); return 0; } @@ -69,9 +85,13 @@ static int msc313e_wdt_stop(struct watchdog_device *wdev) static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time) { + struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev); + wdev->timeout = new_time; - return msc313e_wdt_start(wdev); + if (watchdog_hw_running(wdev) || watchdog_active(wdev)) + msc313e_wdt_set_hw_timeout(priv, wdev->timeout); + return 0; } static const struct watchdog_info msc313e_wdt_ident = { @@ -97,6 +117,8 @@ static int msc313e_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct msc313e_wdt_priv *priv; + unsigned long rate; + int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -116,27 +138,51 @@ static int msc313e_wdt_probe(struct platform_device *pdev) priv->wdev.ops = &msc313e_wdt_ops, priv->wdev.parent = dev; priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT; - priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk); + rate = clk_get_rate(priv->clk); + if (!rate) + return -EINVAL; + priv->wdev.max_timeout = U32_MAX / rate; priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT; - /* If the period is non-zero the WDT is running */ - if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16)) - set_bit(WDOG_HW_RUNNING, &priv->wdev.status); - watchdog_set_drvdata(&priv->wdev, priv); + platform_set_drvdata(pdev, priv); watchdog_init_timeout(&priv->wdev, timeout, dev); watchdog_stop_on_reboot(&priv->wdev); watchdog_stop_on_unregister(&priv->wdev); + watchdog_stop_ping_on_suspend(&priv->wdev); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + /* If the period is non-zero the WDT is running */ + if (msc313e_wdt_get_hw_timeout(priv)) { + msc313e_wdt_set_hw_timeout(priv, priv->wdev.timeout); + set_bit(WDOG_HW_RUNNING, &priv->wdev.status); + /* + * Keep the clock enabled. The watchdog core will skip the next + * start() and a future stop() will balance the CCF reference + * count. + */ + } else { + clk_disable_unprepare(priv->clk); + } + + ret = devm_watchdog_register_device(dev, &priv->wdev); + + /* If the WDT is running and anything goes wrong, disable the clock. */ + if (ret && test_bit(WDOG_HW_RUNNING, &priv->wdev.status)) + clk_disable_unprepare(priv->clk); - return devm_watchdog_register_device(dev, &priv->wdev); + return ret; } static int __maybe_unused msc313e_wdt_suspend(struct device *dev) { struct msc313e_wdt_priv *priv = dev_get_drvdata(dev); - if (watchdog_active(&priv->wdev)) + if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev)) msc313e_wdt_stop(&priv->wdev); return 0; @@ -146,7 +192,7 @@ static int __maybe_unused msc313e_wdt_resume(struct device *dev) { struct msc313e_wdt_priv *priv = dev_get_drvdata(dev); - if (watchdog_active(&priv->wdev)) + if (watchdog_active(&priv->wdev) || watchdog_hw_running(&priv->wdev)) msc313e_wdt_start(&priv->wdev); return 0; diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b6c761acc3de..3db34524ed13 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -128,6 +128,38 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) return 0; } +static bool sunxi_wdt_is_running(struct watchdog_device *wdt_dev) +{ + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; + + return readl(sunxi_wdt->wdt_base + regs->wdt_mode) & WDT_MODE_EN; +} + +static unsigned int sunxi_wdt_get_timeout(struct watchdog_device *wdt_dev) +{ + struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); + const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; + unsigned int timeout; + u32 interval; + + interval = readl(sunxi_wdt->wdt_base + regs->wdt_mode); + interval >>= regs->wdt_timeout_shift; + interval &= WDT_TIMEOUT_MASK; + /* Round the 0.5-second interval up to the minimum representable timeout. */ + if (!interval) + return WDT_MIN_TIMEOUT; + + for (timeout = WDT_MIN_TIMEOUT; + timeout < ARRAY_SIZE(wdt_timeout_map); timeout++) { + if (wdt_timeout_map[timeout] == interval) + return timeout; + } + + /* Reserved interval encoding. */ + return 0; +} + static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int timeout) { @@ -259,6 +291,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sunxi_wdt_dev *sunxi_wdt; + unsigned int running_timeout; int err; sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL); @@ -286,7 +319,17 @@ static int sunxi_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt); - sunxi_wdt_stop(&sunxi_wdt->wdt_dev); + if (sunxi_wdt_is_running(&sunxi_wdt->wdt_dev)) { + running_timeout = sunxi_wdt_get_timeout(&sunxi_wdt->wdt_dev); + if (running_timeout) + sunxi_wdt->wdt_dev.timeout = running_timeout; + + err = sunxi_wdt_start(&sunxi_wdt->wdt_dev); + if (err) + return err; + + set_bit(WDOG_HW_RUNNING, &sunxi_wdt->wdt_dev.status); + } watchdog_stop_on_reboot(&sunxi_wdt->wdt_dev); err = devm_watchdog_register_device(dev, &sunxi_wdt->wdt_dev); diff --git a/drivers/watchdog/watchdog_hrtimer_pretimeout.c b/drivers/watchdog/watchdog_hrtimer_pretimeout.c index fbc7eecd8b20..49a05ea60c97 100644 --- a/drivers/watchdog/watchdog_hrtimer_pretimeout.c +++ b/drivers/watchdog/watchdog_hrtimer_pretimeout.c @@ -30,6 +30,7 @@ void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd) void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd) { if (!(wdd->info->options & WDIOF_PRETIMEOUT) && + wdd->pretimeout && !watchdog_pretimeout_invalid(wdd, wdd->pretimeout)) hrtimer_start(&wdd->wd_data->pretimeout_timer, ktime_set(wdd->timeout - wdd->pretimeout, 0), diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 69922be28b54..076c1b0ab87f 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -1478,7 +1478,7 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) /* No need for kzalloc as it is initialized in following hypercall * GNTTABOP_setup_table. */ - frames = kmalloc_array(nr_gframes, sizeof(*frames), GFP_ATOMIC); + frames = kmalloc_objs(*frames, nr_gframes, GFP_ATOMIC); if (!frames) return -ENOMEM; diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 1ac0b3dcc077..13cf87a5f90c 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -54,11 +54,37 @@ static void v9fs_begin_writeback(struct netfs_io_request *wreq) static void v9fs_issue_write(struct netfs_io_subrequest *subreq) { struct p9_fid *fid = subreq->rreq->netfs_priv; + struct inode *inode = subreq->rreq->inode; + struct netfs_inode *ictx = netfs_inode(inode); int err, len; len = p9_client_write(fid, subreq->start, &subreq->io_iter, &err); - if (len > 0) + if (len > 0) { + uoff_t end = subreq->start + len, i_size, remote, zp; + bool set = false; + + spin_lock(&inode->i_lock); + + /* We can read the sizes directly as we hold i_lock. */ + i_size = inode->i_size; + remote = ictx->_remote_i_size; + zp = ictx->_zero_point; + + if (end > i_size) { + i_size = end; + set = true; + } + if (end > remote) { + remote = end; + set = true; + } + + if (set) + netfs_write_sizes(inode, i_size, remote, zp); + spin_unlock(&inode->i_lock); + __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); + } netfs_write_subrequest_terminated(subreq, len ?: err); } diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index e549e222602e..fa6b7143db98 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -113,8 +113,7 @@ void v9fs_dentry_fid_remove(struct dentry *dentry) */ static int v9fs_dentry_init(struct dentry *dentry) { - struct v9fs_dentry *v9fs_dentry = kzalloc(sizeof(*v9fs_dentry), - GFP_KERNEL); + struct v9fs_dentry *v9fs_dentry = kzalloc_obj(*v9fs_dentry); if (!v9fs_dentry) return -ENOMEM; diff --git a/fs/adfs/super.c b/fs/adfs/super.c index a4cd0a5159dd..888aa81a6b39 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -92,10 +92,7 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr) static void adfs_put_super(struct super_block *sb) { - struct adfs_sb_info *asb = ADFS_SB(sb); - adfs_free_map(sb); - kfree_rcu(asb, rcu); } static int adfs_show_options(struct seq_file *seq, struct dentry *root) @@ -365,7 +362,7 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc) ret = -EINVAL; } if (ret) - goto error; + return ret; /* set up enough so that we can read an inode */ sb->s_op = &adfs_sops; @@ -406,15 +403,9 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc) if (!sb->s_root) { adfs_free_map(sb); adfs_error(sb, "get root inode failed\n"); - ret = -EIO; - goto error; + return -EIO; } return 0; - -error: - sb->s_fs_info = NULL; - kfree(asb); - return ret; } static int adfs_get_tree(struct fs_context *fc) @@ -465,10 +456,19 @@ static int adfs_init_fs_context(struct fs_context *fc) return 0; } +static void adfs_kill_sb(struct super_block *sb) +{ + struct adfs_sb_info *asb = ADFS_SB(sb); + + kill_block_super(sb); + + kfree_rcu(asb, rcu); +} + static struct file_system_type adfs_fs_type = { .owner = THIS_MODULE, .name = "adfs", - .kill_sb = kill_block_super, + .kill_sb = adfs_kill_sb, .fs_flags = FS_REQUIRES_DEV, .init_fs_context = adfs_init_fs_context, .parameters = adfs_param_spec, diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c index 63bf096b721a..73195d76b481 100644 --- a/fs/afs/addr_list.c +++ b/fs/afs/addr_list.c @@ -394,8 +394,11 @@ void afs_set_peer_appdata(struct afs_server *server, struct rxrpc_peer *pn = new_alist->addrs[n].peer; struct rxrpc_peer *po = old_alist->addrs[o].peer; - if (pn == po) + if (pn == po) { + n++; + o++; continue; + } if (pn < po) { rxrpc_kernel_set_peer_data(pn, data); n++; diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 81565366d937..2db534a2c7cc 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -1801,7 +1801,7 @@ static int afs_symlink(struct mnt_idmap *idmap, struct inode *dir, goto error; ret = -ENOMEM; - symlink = kmalloc_flex(struct afs_symlink, content, clen + 1, GFP_KERNEL); + symlink = kmalloc_flex(struct afs_symlink, content, clen + 1); if (!symlink) goto error; refcount_set(&symlink->ref, 1); diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c index 3ead36a07048..c31303059444 100644 --- a/fs/afs/dir_edit.c +++ b/fs/afs/dir_edit.c @@ -442,7 +442,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, /* Check and clear the entry. */ de = &block->dirents[slot]; if (de->u.valid != 1) - goto error_unmap; + goto error; trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot, ntohl(de->u.vnode), ntohl(de->u.unique), @@ -458,7 +458,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, /* Clear the constituent entries. */ next = de->u.hash_next; memset(de, 0, sizeof(*de) * iter.nr_slots); - kunmap_local(block); /* Adjust the hash chain: if iter->prev_entry is 0, the hashtable head * index is previous; otherwise it's slot number of the previous entry. @@ -485,7 +484,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, pde = &pblock->dirents[ps]; prev_next = pde->u.hash_next; if (prev_next != htons(entry)) { - kunmap_local(pblock); pr_warn("%llx:%llx:%x: not prev in chain b=%x p=%x,%x e=%x %*s", vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, iter.bucket, iter.prev_entry, prev_next, entry, @@ -493,7 +491,6 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, goto error; } pde->u.hash_next = next; - kunmap_local(pblock); } netfs_single_mark_inode_dirty(&vnode->netfs.inode); @@ -503,18 +500,16 @@ void afs_edit_dir_remove(struct afs_vnode *vnode, _debug("Remove %s from %u[%u]", name->name, b, slot); out_unmap: + afs_dir_end_iter(&iter); kunmap_local(meta); _leave(""); return; already_invalidated: - kunmap_local(block); trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval, 0, 0, 0, 0, name->name); goto out_unmap; -error_unmap: - kunmap_local(block); error: trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error, 0, 0, 0, 0, name->name); diff --git a/fs/afs/dir_search.c b/fs/afs/dir_search.c index 104411c0692f..11ebdfffcb1d 100644 --- a/fs/afs/dir_search.c +++ b/fs/afs/dir_search.c @@ -75,10 +75,7 @@ union afs_xdr_dir_block *afs_dir_find_block(struct afs_dir_iter *iter, size_t bl _enter("%zx,%d", block, slot); - if (iter->block) { - kunmap_local(iter->block); - iter->block = NULL; - } + afs_dir_end_iter(iter); if (dvnode->directory_size < blend) goto fail; @@ -173,12 +170,8 @@ int afs_dir_search_bucket(struct afs_dir_iter *iter, const struct qstr *name, ret = -ENOENT; found: - if (iter->block) { - kunmap_local(iter->block); - iter->block = NULL; - } - bad: + afs_dir_end_iter(iter); if (ret == -ESTALE) afs_invalidate_dir(iter->dvnode, afs_dir_invalid_iter_stale); _leave(" = %d", ret); diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c index a91ad1938d07..8c62334dbfe7 100644 --- a/fs/afs/fs_probe.c +++ b/fs/afs/fs_probe.c @@ -258,6 +258,7 @@ int afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server, lockdep_is_held(&server->fs_lock)); if (old) { estate->responsive_set = old->responsive_set; + old_alist = old->addresses; if (!new_alist) new_alist = old->addresses; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 290873bac89b..330654ed16ec 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1133,6 +1133,14 @@ int afs_dir_search_bucket(struct afs_dir_iter *iter, const struct qstr *name, int afs_dir_search(struct afs_vnode *dvnode, const struct qstr *name, struct afs_fid *_fid, afs_dataversion_t *_dir_version); +static inline void afs_dir_end_iter(struct afs_dir_iter *iter) +{ + if (iter->block) { + kunmap_local(iter->block); + iter->block = NULL; + } +} + /* * dir_silly.c */ diff --git a/fs/afs/server.c b/fs/afs/server.c index 0fe162ea2a36..189138bd6d71 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -242,7 +242,6 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key, out: afs_put_addrlist(alist, afs_alist_trace_put_server_create); if (candidate) { - kfree(rcu_access_pointer(server->endpoint_state)); kfree(candidate); afs_dec_servers_outstanding(cell->net); } diff --git a/fs/afs/symlink.c b/fs/afs/symlink.c index 16b4823cb7b7..6b8c122877ca 100644 --- a/fs/afs/symlink.c +++ b/fs/afs/symlink.c @@ -119,8 +119,7 @@ static ssize_t afs_do_read_symlink(struct afs_vnode *vnode) vnode->directory_size = i_size; /* Copy the symlink. */ - symlink = kmalloc_flex(struct afs_symlink, content, i_size + 1, - GFP_KERNEL); + symlink = kmalloc_flex(struct afs_symlink, content, i_size + 1); if (!symlink) return -ENOMEM; diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index c1e210cec436..6b15a3717ba7 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -323,8 +323,10 @@ static int autofs_fill_super(struct super_block *s, struct fs_context *fc) return -ENOMEM; root_inode = autofs_get_inode(s, S_IFDIR | 0755); - if (!root_inode) + if (!root_inode) { + autofs_free_ino(ino); return -ENOMEM; + } root_inode->i_uid = ctx->uid; root_inode->i_gid = ctx->gid; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index ddfd3aa57ac8..620da85948b4 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -331,8 +331,8 @@ static int entry_attach_interpreter(struct binfmt_misc_entry *e, return -ENOSPC; /* One allocation, both strings in it, like the entry's own buffer. */ - interp = kmalloc(struct_size(interp, name, nlen + plen + 2), - GFP_KERNEL_ACCOUNT); + interp = kmalloc_flex(*interp, name, nlen + plen + 2, + GFP_KERNEL_ACCOUNT); if (!interp) { dec_ucount(ucounts, UCOUNT_BINFMT_MISC_INTERPRETERS); return -ENOMEM; @@ -858,8 +858,7 @@ static struct binfmt_misc_entry *create_entry(const char __user *buffer, if ((count < 11) || (count > MAX_REGISTER_LENGTH)) return ERR_PTR(-EINVAL); - e = kmalloc(struct_size(e, buf, count + MISC_DELIM_PAD), - GFP_KERNEL_ACCOUNT); + e = kmalloc_flex(*e, buf, count + MISC_DELIM_PAD, GFP_KERNEL_ACCOUNT); if (!e) return ERR_PTR(-ENOMEM); diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 830460a40e86..ee182369254c 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -3074,21 +3074,25 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran return ERR_PTR(ret); } - ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL); - btrfs_free_excluded_extents(cache); - if (ret) { - btrfs_put_block_group(cache); - return ERR_PTR(ret); - } - /* * Ensure the corresponding space_info object is created and * assigned to our block group. We want our bg to be added to the rbtree * with its ->space_info set. + * + * On a zoned filesystem btrfs_add_new_free_space() ends up in + * __btrfs_add_free_space_zoned(), which dereferences + * block_group->space_info, so it has to be set beforehand. */ cache->space_info = space_info; ASSERT(cache->space_info); + ret = btrfs_add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL); + btrfs_free_excluded_extents(cache); + if (ret) { + btrfs_put_block_group(cache); + return ERR_PTR(ret); + } + ret = btrfs_add_block_group_cache(cache); if (ret) { btrfs_remove_free_space_cache(cache); diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index dc0834f920c3..af1b898029e8 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -636,7 +636,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, ret = mark_block_group_to_copy(fs_info, src_device); if (ret) - return ret; + goto leave; down_write(&dev_replace->rwsem); dev_replace->replace_task = current; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3c10a0ef0002..93ef3cec191e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3436,6 +3436,9 @@ out: */ btrfs_remove_ordered_extent(ordered_extent); + /* Cleanup any remaining biocs attached to the OE. */ + btrfs_cleanup_ordered_bioc_list(ordered_extent); + /* once for us */ btrfs_put_ordered_extent(ordered_extent); /* once for the tree */ diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 72bc9d4f7708..e4b2da31a0d5 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -384,6 +384,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_flags &= ~BTRFS_INODE_COMPRESS; inode_flags |= BTRFS_INODE_NOCOMPRESS; } else if (fsflags & FS_COMPR_FL) { + enum btrfs_compression_type comp_type; if (IS_SWAPFILE(&inode->vfs_inode)) return -ETXTBSY; @@ -391,9 +392,23 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_flags |= BTRFS_INODE_COMPRESS; inode_flags &= ~BTRFS_INODE_NOCOMPRESS; - comp = btrfs_compress_type2str(fs_info->compress_type); - if (!comp || comp[0] == 0) - comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB); + /* + * Keep the algorithm recorded in the compression property, + * otherwise changing an unrelated attribute would reset it to + * the mount default, since FS_IOC_SETFLAGS callers write back + * the whole flag set they got from FS_IOC_GETFLAGS and that + * includes FS_COMPR_FL for any inode carrying the property. + * + * Inodes with the compress flag set but no property keep using + * the mount default, so they behave as before. + */ + if (inode->prop_compress) + comp_type = inode->prop_compress; + else if (fs_info->compress_type) + comp_type = fs_info->compress_type; + else + comp_type = BTRFS_COMPRESS_ZLIB; + comp = btrfs_compress_type2str(comp_type); } else { inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); } diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index b210371ce91e..d9e660447205 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -310,8 +310,10 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans, ret = btrfs_search_slot(trans, trans->fs_info->stripe_root, key, path, 0, 1); - if (ret) - return (ret == 1 ? ret : -EINVAL); + if (ret > 0) + ret = -ENOENT; + if (ret < 0) + return ret; leaf = path->nodes[0]; slot = path->slots[0]; @@ -337,7 +339,6 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, stripe_extent = kzalloc(item_size, GFP_NOFS); if (unlikely(!stripe_extent)) { btrfs_abort_transaction(trans, -ENOMEM); - btrfs_end_transaction(trans); return -ENOMEM; } @@ -374,7 +375,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, struct btrfs_ordered_extent *ordered_extent) { struct btrfs_io_context *bioc; - int ret; + int ret = 0; if (!btrfs_fs_incompat(trans->fs_info, RAID_STRIPE_TREE)) return 0; @@ -382,17 +383,23 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, list_for_each_entry(bioc, &ordered_extent->bioc_list, rst_ordered_entry) { ret = btrfs_insert_one_raid_extent(trans, bioc); if (ret) - return ret; + break; } - while (!list_empty(&ordered_extent->bioc_list)) { - bioc = list_first_entry(&ordered_extent->bioc_list, + btrfs_cleanup_ordered_bioc_list(ordered_extent); + return ret; +} + +void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered) +{ + while (!list_empty(&ordered->bioc_list)) { + struct btrfs_io_context *bioc; + + bioc = list_first_entry(&ordered->bioc_list, typeof(*bioc), rst_ordered_entry); list_del(&bioc->rst_ordered_entry); btrfs_put_bioc(bioc); } - - return 0; } int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, diff --git a/fs/btrfs/raid-stripe-tree.h b/fs/btrfs/raid-stripe-tree.h index 69942ad43140..eb02cf48511b 100644 --- a/fs/btrfs/raid-stripe-tree.h +++ b/fs/btrfs/raid-stripe-tree.h @@ -28,6 +28,7 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, u32 stripe_index, struct btrfs_io_stripe *stripe); int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, struct btrfs_ordered_extent *ordered_extent); +void btrfs_cleanup_ordered_bioc_list(struct btrfs_ordered_extent *ordered); #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index f209e75f0ff5..c09d4213ad89 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -1023,6 +1023,10 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, skip: for_each_set_bit(sector_nr, &extent_bitmap, stripe->nr_sectors) { + const u64 sector_logical = stripe->logical + + ((u64)sector_nr << fs_info->sectorsize_bits); + const u64 sector_physical = physical + + ((u64)sector_nr << fs_info->sectorsize_bits); bool repaired = false; if (scrub_bitmap_test_bit_is_metadata(stripe, sector_nr)) { @@ -1051,12 +1055,12 @@ skip: if (dev) { btrfs_err_rl(fs_info, "scrub: fixed up error at logical %llu on dev %s physical %llu", - stripe->logical, btrfs_dev_name(dev), - physical); + sector_logical, btrfs_dev_name(dev), + sector_physical); } else { btrfs_err_rl(fs_info, "scrub: fixed up error at logical %llu on mirror %u", - stripe->logical, stripe->mirror_num); + sector_logical, stripe->mirror_num); } continue; } @@ -1065,30 +1069,30 @@ skip: if (dev) { btrfs_err_rl(fs_info, "scrub: unable to fixup (regular) error at logical %llu on dev %s physical %llu", - stripe->logical, btrfs_dev_name(dev), - physical); + sector_logical, btrfs_dev_name(dev), + sector_physical); } else { btrfs_err_rl(fs_info, "scrub: unable to fixup (regular) error at logical %llu on mirror %u", - stripe->logical, stripe->mirror_num); + sector_logical, stripe->mirror_num); } if (scrub_bitmap_test_bit_io_error(stripe, sector_nr)) if (__ratelimit(&rs) && dev) scrub_print_common_warning("i/o error", dev, false, - stripe->logical, physical); + sector_logical, sector_physical); if (scrub_bitmap_test_bit_csum_error(stripe, sector_nr)) if (__ratelimit(&rs) && dev) scrub_print_common_warning("checksum error", dev, false, - stripe->logical, physical); + sector_logical, sector_physical); if (scrub_bitmap_test_bit_meta_error(stripe, sector_nr)) if (__ratelimit(&rs) && dev) scrub_print_common_warning("header error", dev, false, - stripe->logical, physical); + sector_logical, sector_physical); if (scrub_bitmap_test_bit_meta_gen_error(stripe, sector_nr)) if (__ratelimit(&rs) && dev) scrub_print_common_warning("generation error", dev, false, - stripe->logical, physical); + sector_logical, sector_physical); } /* Update the device stats. */ diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index dca3570168c7..5c59b9abedcd 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -2065,7 +2065,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, ret = is_inode_existent(sctx, dir, dir_gen, NULL, &parent_root_dir_gen); if (ret <= 0) - return 0; + return ret; /* * If we have a parent root we need to verify that the parent dir was @@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx, if (S_ISLNK(sctx->cur_inode_mode)) return 0; + if (unlikely(!S_ISREG(sctx->cur_inode_mode))) { + btrfs_crit(sctx->send_root->fs_info, + "send: extent for non-regular inode %llu root %llu mode 0%llo", + key->objectid, btrfs_root_id(sctx->send_root), + sctx->cur_inode_mode & S_IFMT); + return -EUCLEAN; + } if (sctx->parent_root && !sctx->cur_inode_new) { ret = is_extent_unchanged(sctx, path, key); diff --git a/fs/btrfs/tests/extent-io-tests.c b/fs/btrfs/tests/extent-io-tests.c index b2aacf846c8b..23459cd4e503 100644 --- a/fs/btrfs/tests/extent-io-tests.c +++ b/fs/btrfs/tests/extent-io-tests.c @@ -133,14 +133,14 @@ static int test_find_delalloc(u32 sectorsize, u32 nodesize) if (IS_ERR(root)) { test_std_err(TEST_ALLOC_ROOT); ret = PTR_ERR(root); - goto out; + goto out_root_info; } inode = btrfs_new_test_inode(); if (!inode) { test_std_err(TEST_ALLOC_INODE); ret = -ENOMEM; - goto out; + goto out_root_info; } tmp = &BTRFS_I(inode)->io_tree; BTRFS_I(inode)->root = root; @@ -333,6 +333,7 @@ out: process_page_range(inode, 0, total_dirty - 1, PROCESS_UNLOCK | PROCESS_RELEASE); iput(inode); +out_root_info: btrfs_free_dummy_root(root); btrfs_free_dummy_fs_info(fs_info); return ret; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index bafc62cf5ebc..6802b94ed76f 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -458,8 +458,19 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans, * through btrfs_record_root_in_trans without having to take the * lock. smp_wmb() makes sure that all the writes above are * done before we pop in the zero below + * + * If @force is true, it means the call is from + * qgroup_account_snapshot(), which only requires radix tree + * tracking. + * We should not force reloc root creation here, as the root + * may have already been modified, and in that case + * root->commit_root has already been dropped. + * + * Using that commit root will cause the reloc root to refer + * to a deleted extent, causing extent tree corruption. */ - ret = btrfs_init_reloc_root(trans, root); + if (!force) + ret = btrfs_init_reloc_root(trans, root); smp_mb__before_atomic(); clear_bit(BTRFS_ROOT_IN_TRANS_SETUP, &root->state); } @@ -2583,6 +2594,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ret = btrfs_write_and_wait_transaction(trans); if (unlikely(ret)) { btrfs_err(fs_info, "error while writing out transaction: %pe", ERR_PTR(ret)); + /* + * Abort before releasing tree_log_mutex, so a log sync waiting + * on it sees the fs error and skips writing super_for_commit + * for this failed transaction. See btrfs_sync_log(). + */ + btrfs_abort_transaction(trans, ret); mutex_unlock(&fs_info->tree_log_mutex); goto scrub_continue; } diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 0ce91396b517..ab5abbb475e2 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1909,6 +1909,16 @@ static int check_inode_ref(struct extent_buffer *leaf, return -EUCLEAN; } + if (unlikely(btrfs_is_fstree(btrfs_header_owner(leaf)) && + (key->offset < BTRFS_FIRST_FREE_OBJECTID || + key->offset > BTRFS_LAST_FREE_OBJECTID))) { + inode_ref_err(leaf, slot, + "invalid offset for ref key, have %llu expect [%llu, %lld]", + key->offset, BTRFS_FIRST_FREE_OBJECTID, + BTRFS_LAST_FREE_OBJECTID); + return -EUCLEAN; + } + ptr = btrfs_item_ptr_offset(leaf, slot); end = ptr + btrfs_item_size(leaf, slot); while (ptr < end) { @@ -1952,12 +1962,14 @@ static int check_inode_extref(struct extent_buffer *leaf, { unsigned long ptr = btrfs_item_ptr_offset(leaf, slot); unsigned long end = ptr + btrfs_item_size(leaf, slot); + const bool is_fstree = btrfs_is_fstree(btrfs_header_owner(leaf)); if (unlikely(!check_prev_ino(leaf, key, slot, prev_key))) return -EUCLEAN; while (ptr < end) { struct btrfs_inode_extref *extref = (struct btrfs_inode_extref *)ptr; + u64 parent; u16 namelen; if (unlikely(ptr + sizeof(*extref) > end)) { @@ -1967,7 +1979,24 @@ static int check_inode_extref(struct extent_buffer *leaf, return -EUCLEAN; } + parent = btrfs_inode_extref_parent(leaf, extref); + if (unlikely(is_fstree && (parent < BTRFS_FIRST_FREE_OBJECTID || + parent > BTRFS_LAST_FREE_OBJECTID))) { + inode_ref_err(leaf, slot, + "invalid parent for extref key, have %llu expect [%llu, %lld]", + parent, BTRFS_FIRST_FREE_OBJECTID, + BTRFS_LAST_FREE_OBJECTID); + return -EUCLEAN; + } + namelen = btrfs_inode_extref_name_len(leaf, extref); + if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) { + inode_ref_err(leaf, slot, + "invalid inode extref name length, has %u expect [1, %u]", + namelen, BTRFS_NAME_LEN); + return -EUCLEAN; + } + if (unlikely(ptr + sizeof(*extref) + namelen > end)) { inode_ref_err(leaf, slot, "inode extref overflow, ptr %lu end %lu namelen %u", diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 7ba7b6098aa5..a00094604e54 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -7286,6 +7286,22 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); if (ret < 0) goto out; + /* + * There can't be an inode ref key with offset 0 because inode numbers + * start at BTRFS_FIRST_FREE_OBJECTID. + */ + if (WARN_ON_ONCE(ret == 0)) { + btrfs_err(trans->fs_info, + "found inode ref key with offset 0 for root %llu inode %llu", + btrfs_root_id(root), ino); + ret = BTRFS_LOG_FORCE_COMMIT; + goto out; + } + /* + * Set to 0 so that in case we don't do any work below, we won't return + * 1 and trigger an unnecessary transaction commit. + */ + ret = 0; while (true) { struct extent_buffer *leaf = path->nodes[0]; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9b66eb584ece..74584669507f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3117,7 +3117,11 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path error_sysfs: btrfs_sysfs_remove_device(device); mutex_lock(&fs_info->fs_devices->device_list_mutex); + if (seeding_dev) + btrfs_assign_next_active_device(device, seed_devices->latest_dev); mutex_lock(&fs_info->chunk_mutex); + if (!list_empty(&device->post_commit_list)) + list_del_init(&device->post_commit_list); list_del_rcu(&device->dev_list); list_del(&device->dev_alloc_list); fs_info->fs_devices->num_devices--; diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index a016cb471beb..9cc2c9c1a606 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -2626,16 +2626,13 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ down_read(&dev_replace->rwsem); map = block_group->physical_map; for (i = 0; i < map->num_stripes; i++) { - ret = call_zone_finish(block_group, &map->stripes[i]); - if (ret) { - up_read(&dev_replace->rwsem); - return ret; - } + if (ret) + break; } up_read(&dev_replace->rwsem); - if (!fully_written) + if (!ret && !fully_written) btrfs_dec_block_group_ro(block_group); spin_lock(&fs_info->zone_active_bgs_lock); @@ -2648,7 +2645,7 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writ clear_and_wake_up_bit(BTRFS_FS_NEED_ZONE_FINISH, &fs_info->flags); - return 0; + return ret; } int btrfs_zone_finish(struct btrfs_block_group *block_group) @@ -2713,6 +2710,7 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng { struct btrfs_block_group *block_group; u64 min_alloc_bytes; + int ret = 0; if (!btrfs_is_zoned(fs_info)) return 0; @@ -2732,11 +2730,11 @@ int btrfs_zone_finish_endio(struct btrfs_fs_info *fs_info, u64 logical, u64 leng block_group->start + block_group->zone_capacity) goto out; - do_zone_finish(block_group, true); + ret = do_zone_finish(block_group, true); out: btrfs_put_block_group(block_group); - return 0; + return ret; } static void btrfs_zone_finish_endio_workfn(struct work_struct *work) diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index 86919293fd54..58d9ff76fe07 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -307,8 +307,17 @@ again: DEFINE_WAIT(wait); prepare_to_wait(&zwsm->wait, &wait, TASK_UNINTERRUPTIBLE); - schedule(); + /* + * Re-check after being queued: zstd_put_workspace() only wakes + * a queue that already has a sleeper, so a workspace returned + * since the failed allocation woke nobody. + */ + ws = zstd_find_workspace(fs_info, level); + if (!ws) + schedule(); finish_wait(&zwsm->wait, &wait); + if (ws) + return ws; goto again; } diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c index f8ae78b3f7b6..c70bf67e52b0 100644 --- a/fs/cachefiles/xattr.c +++ b/fs/cachefiles/xattr.c @@ -13,6 +13,7 @@ #include <linux/quotaops.h> #include <linux/xattr.h> #include <linux/slab.h> +#include <linux/unaligned.h> #include "internal.h" #define CACHEFILES_COOKIE_TYPE_DATA 1 @@ -50,7 +51,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) _enter("%x,#%d", object->debug_id, len); - buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL); + buf = kmalloc(sizeof(struct cachefiles_xattr) + max(len, sizeof(__be64)), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -60,6 +61,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) buf->content = object->content_info; if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags)) buf->content = CACHEFILES_CONTENT_DIRTY; + put_unaligned_be64(0, (__be64 *)buf->data); if (len > 0) memcpy(buf->data, fscache_get_aux(object->cookie), len); @@ -77,8 +79,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) trace_cachefiles_vfs_error(object, file_inode(file), ret, cachefiles_trace_setxattr_error); trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_fail); if (ret != -ENOMEM) cachefiles_io_error_obj( @@ -86,8 +87,7 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object) "Failed to set xattr with error %d", ret); } else { trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, + buf->data, buf->content, cachefiles_coherency_set_ok); } @@ -110,9 +110,10 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file int ret = -ESTALE; tlen = sizeof(struct cachefiles_xattr) + len; - buf = kmalloc(tlen, GFP_KERNEL); + buf = kmalloc(sizeof(struct cachefiles_xattr) + max(len, sizeof(__be64)), GFP_KERNEL); if (!buf) return -ENOMEM; + put_unaligned_be64(0, (__be64 *)buf->data); xlen = cachefiles_inject_read_error(); if (xlen == 0) @@ -148,8 +149,7 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file out: trace_cachefiles_coherency(object, file_inode(file)->i_ino, - be64_to_cpup((__be64 *)buf->data), - buf->content, why); + buf->data, buf->content, why); kfree(buf); return ret; } diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 657c2cb0f881..e598b2d424ec 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -2546,7 +2546,7 @@ static int __ceph_pool_perm_get(struct ceph_inode_info *ci, } pool_ns_len = pool_ns ? pool_ns->len : 0; - perm = kmalloc_flex(*perm, pool_ns, pool_ns_len + 1, GFP_KERNEL); + perm = kmalloc_flex(*perm, pool_ns, pool_ns_len + 1); if (!perm) { err = -ENOMEM; goto out_unlock; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index a091f77cedaf..085ae0cfb5f7 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -5492,7 +5492,7 @@ static void ceph_mdsc_reset_workfn(struct work_struct *work) goto out_complete; } - sessions = kcalloc(max_sessions, sizeof(*sessions), GFP_KERNEL); + sessions = kzalloc_objs(*sessions, max_sessions); if (!sessions) { mutex_unlock(&mdsc->mutex); ret = -ENOMEM; @@ -6600,11 +6600,13 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n", tpath, mask, caller_uid, caller_gid); + mutex_lock(&mdsc->mutex); for (i = 0; i < mdsc->s_cap_auths_num; i++) { struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i]; err = ceph_mds_auth_match(mdsc, s, cred, tpath); if (err < 0) { + mutex_unlock(&mdsc->mutex); put_cred(cred); return err; } else if (err > 0) { @@ -6626,6 +6628,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms, rw_perms_s); if (root_squash_perms && rw_perms_s == NULL) { + mutex_unlock(&mdsc->mutex); doutc(cl, "access allowed\n"); return 0; } @@ -6640,6 +6643,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) !!(mask & MAY_READ), !!(mask & MAY_WRITE)); } doutc(cl, "access denied\n"); + mutex_unlock(&mdsc->mutex); return -EACCES; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 3c62e3c3530b..e7a262c9c2ab 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -604,6 +604,7 @@ struct ceph_mds_client { struct rw_semaphore pool_perm_rwsem; struct rb_root pool_perm_tree; + /* protected by mutex */ u32 s_cap_auths_num; struct ceph_mds_cap_auth *s_cap_auths; diff --git a/fs/ceph/subvolume_metrics.c b/fs/ceph/subvolume_metrics.c index 03fda1f9257b..01419c9482f1 100644 --- a/fs/ceph/subvolume_metrics.c +++ b/fs/ceph/subvolume_metrics.c @@ -245,7 +245,7 @@ int ceph_subvolume_metrics_snapshot(struct ceph_subvolume_metrics_tracker *track return 0; } - snap = kcalloc(count, sizeof(*snap), GFP_NOFS); + snap = kzalloc_objs(*snap, count, GFP_NOFS); if (!snap) { atomic64_inc(&tracker->snapshot_failures); return -ENOMEM; diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 15edea30dc8b..72935f665f11 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -1420,6 +1420,11 @@ static int ceph_reconfigure_fc(struct fs_context *fc) else ceph_clear_mount_opt(fsc, SPARSEREAD); + if (fsopt->flags & CEPH_MOUNT_OPT_NEARFULL_SYNC) + ceph_set_mount_opt(fsc, NEARFULL_SYNC); + else + ceph_clear_mount_opt(fsc, NEARFULL_SYNC); + if (strcmp_null(fsc->mount_options->mon_addr, fsopt->mon_addr)) { kfree(fsc->mount_options->mon_addr); fsc->mount_options->mon_addr = fsopt->mon_addr; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 3c88f13f1ca2..eda80c2a2d38 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -416,6 +416,15 @@ static void configfs_remove_dir(struct dentry *d) if (d_really_is_positive(d)) { if (unlikely(simple_rmdir(d_inode(parent), d))) pr_warn("remove_dir (%pd): attributes remain", d); + else + /* + * configfs_get_config_item() takes a hashed dentry as + * proof that ->s_element is still alive. Our caller + * is about to drop the last reference to the item and + * the VFS will not unhash until after we return, so + * unhash it here. + */ + d_drop(d); } pr_debug(" o %pd removing done (%d)\n", d, d_count(d)); diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index 4929f3431189..d8cac1cbf3bd 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -9,6 +9,7 @@ */ #include <linux/fs.h> +#include <linux/magic.h> #include <linux/module.h> #include <linux/mount.h> #include <linux/fs_context.h> @@ -19,9 +20,6 @@ #include <linux/configfs.h> #include "configfs_internal.h" -/* Random magic number */ -#define CONFIGFS_MAGIC 0x62656570 - static struct vfsmount *configfs_mount = NULL; struct kmem_cache *configfs_dir_cachep; static int configfs_mnt_count = 0; diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 31eb28b27309..3b31c714400f 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -76,9 +76,9 @@ static int configfs_get_target_path(struct config_item *item, static int create_link(struct config_item *parent_item, struct config_item *item, + struct configfs_dirent *target_sd, struct dentry *dentry) { - struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata; char *body; int ret; @@ -115,6 +115,7 @@ static int create_link(struct config_item *parent_item, static int get_target(const char *symname, struct config_item **target, + struct configfs_dirent **target_sd, struct super_block *sb) { struct path path __free(path_put) = {}; @@ -125,7 +126,20 @@ static int get_target(const char *symname, struct config_item **target, return ret; if (path.dentry->d_sb != sb) return -EPERM; - *target = configfs_get_config_item(path.dentry); + /* + * A hashed dentry guarantees that neither the item nor the dirent + * have been released yet, as removals unhash before dropping. + * Grab both references here. An item reference alone would not keep + * ->ci_dentry alive. + */ + spin_lock(&path.dentry->d_lock); + if (!d_unhashed(path.dentry)) { + struct configfs_dirent *sd = path.dentry->d_fsdata; + + *target = config_item_get(sd->s_element); + *target_sd = configfs_get(sd); + } + spin_unlock(&path.dentry->d_lock); if (!*target) return -ENOENT; return 0; @@ -139,6 +153,7 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, struct configfs_dirent *sd; struct config_item *parent_item; struct config_item *target_item = NULL; + struct configfs_dirent *target_sd = NULL; const struct config_item_type *type; sd = dentry->d_parent->d_fsdata; @@ -182,7 +197,7 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, * AV, a thoroughly annoyed bastard. */ inode_unlock(dir); - ret = get_target(symname, &target_item, dentry->d_sb); + ret = get_target(symname, &target_item, &target_sd, dentry->d_sb); inode_lock(dir); if (ret) goto out_put; @@ -196,13 +211,14 @@ int configfs_symlink(struct mnt_idmap *idmap, struct inode *dir, ret = type->ct_item_ops->allow_link(parent_item, target_item); if (!ret) { mutex_lock(&configfs_symlink_mutex); - ret = create_link(parent_item, target_item, dentry); + ret = create_link(parent_item, target_item, target_sd, dentry); mutex_unlock(&configfs_symlink_mutex); if (ret && type->ct_item_ops->drop_link) type->ct_item_ops->drop_link(parent_item, target_item); } + configfs_put(target_sd); config_item_put(target_item); out_put: diff --git a/fs/coredump.c b/fs/coredump.c index ac3cd74808c6..6114839f5178 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -1000,7 +1000,7 @@ static bool coredump_pipe(struct core_name *cn, struct coredump_params *cprm, return false; } - helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv), GFP_KERNEL); + helper_argv = kmalloc_objs(*helper_argv, argc + 1); if (!helper_argv) { coredump_report_failure("%s failed to allocate memory", __func__); return false; @@ -480,11 +480,12 @@ static void dax_associate_entry(void *entry, struct address_space *mapping, unsigned long address, bool shared) { unsigned long size = dax_entry_size(entry), index; - struct folio *folio = dax_to_folio(entry); + struct folio *folio; if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) return; + folio = dax_to_folio(entry); index = linear_page_index(vma, address & ~(size - 1)); if (shared && (folio->mapping || dax_folio_is_shared(folio))) { if (folio->mapping) @@ -505,21 +506,23 @@ static void dax_associate_entry(void *entry, struct address_space *mapping, static void dax_disassociate_entry(void *entry, struct address_space *mapping, bool trunc) { - struct folio *folio = dax_to_folio(entry); + struct folio *folio; if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) return; + folio = dax_to_folio(entry); dax_folio_put(folio); } static struct page *dax_busy_page(void *entry) { - struct folio *folio = dax_to_folio(entry); + struct folio *folio; if (dax_is_zero_entry(entry) || dax_is_empty_entry(entry)) return NULL; + folio = dax_to_folio(entry); if (folio_ref_count(folio) - folio_mapcount(folio)) return &folio->page; else diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 0885b1f2fc92..be63b89f0862 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -48,7 +48,7 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, bool need_kmap) return NULL; if (!buf->base) buf->base = kmap_local_page(buf->page); - return buf->base + (offset & ~PAGE_MASK); + return buf->base + ((buf->off + offset) & ~PAGE_MASK); } int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb, diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c index 27caf4bebddc..d387b27c4ee2 100644 --- a/fs/erofs/decompressor.c +++ b/fs/erofs/decompressor.c @@ -7,8 +7,6 @@ #include "compress.h" #include <linux/lz4.h> -#define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1) - static int z_erofs_load_lz4_config(struct super_block *sb, struct erofs_super_block *dsb, void *data, int size) { @@ -21,8 +19,6 @@ static int z_erofs_load_lz4_config(struct super_block *sb, erofs_err(sb, "invalid lz4 cfgs, size=%u", size); return -EINVAL; } - distance = le16_to_cpu(lz4->max_distance); - sbi->lz4.max_pclusterblks = le16_to_cpu(lz4->max_pclusterblks); if (!sbi->lz4.max_pclusterblks) { sbi->lz4.max_pclusterblks = 1; /* reserved case */ @@ -39,45 +35,25 @@ static int z_erofs_load_lz4_config(struct super_block *sb, sbi->lz4.max_pclusterblks = 1; sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4; } - - sbi->lz4.max_distance_pages = distance ? - DIV_ROUND_UP(distance, PAGE_SIZE) + 1 : - LZ4_MAX_DISTANCE_PAGES; return z_erofs_gbuf_growsize(sbi->lz4.max_pclusterblks); } /* - * Fill all gaps with bounce pages if it's a sparse page list. Also check if - * all physical pages are consecutive, which can be seen for moderate CR. + * Fill all gaps with bounce pages if it's a sparse page list (for example some + * folios are already uptodate and thus can be mapped into userspace). Also + * check if pages are physically consecutive, which can be seen for moderate CR. */ -static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq, - struct page **pagepool) +static int z_erofs_oneshot_prepare_dstpages(struct z_erofs_decompress_req *rq, + struct page **pagepool) { - struct page *availables[LZ4_MAX_DISTANCE_PAGES] = { NULL }; - unsigned long bounced[DIV_ROUND_UP(LZ4_MAX_DISTANCE_PAGES, - BITS_PER_LONG)] = { 0 }; - unsigned int lz4_max_distance_pages = - EROFS_SB(rq->sb)->lz4.max_distance_pages; void *kaddr = NULL; - unsigned int i, j, top; + unsigned int i; - top = 0; - for (i = j = 0; i < rq->outpages; ++i, ++j) { - struct page *const page = rq->out[i]; - struct page *victim; - - if (j >= lz4_max_distance_pages) - j = 0; - - /* 'valid' bounced can only be tested after a complete round */ - if (!rq->fillgaps && test_bit(j, bounced)) { - DBG_BUGON(i < lz4_max_distance_pages); - DBG_BUGON(top >= lz4_max_distance_pages); - availables[top++] = rq->out[i - lz4_max_distance_pages]; - } + for (i = 0; i < rq->outpages; ++i) { + struct page *page, *victim; + page = rq->out[i]; if (page) { - __clear_bit(j, bounced); if (!PageHighMem(page)) { if (!i) { kaddr = page_address(page); @@ -89,21 +65,14 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq, continue; } } - kaddr = NULL; - continue; - } - kaddr = NULL; - __set_bit(j, bounced); - - if (top) { - victim = availables[--top]; } else { victim = __erofs_allocpage(pagepool, rq->gfp, true); if (!victim) return -ENOMEM; set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE); + rq->out[i] = victim; } - rq->out[i] = victim; + kaddr = NULL; } return kaddr ? 1 : 0; } @@ -266,7 +235,7 @@ static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, dst_maptype = 0; } else { /* general decoding path which can be used for all cases */ - ret = z_erofs_lz4_prepare_dstpages(rq, pagepool); + ret = z_erofs_oneshot_prepare_dstpages(rq, pagepool); if (ret < 0) return ERR_PTR(ret); if (ret > 0) { diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c index 6b0cdb446c6a..9d15f94cbee1 100644 --- a/fs/erofs/decompressor_lzma.c +++ b/fs/erofs/decompressor_lzma.c @@ -5,6 +5,7 @@ struct z_erofs_lzma { struct z_erofs_lzma *next; struct xz_dec_microlzma *state; + unsigned int dict_size; u8 bounce[PAGE_SIZE]; }; @@ -128,11 +129,19 @@ again: err = 0; /* 2. walk each isolated stream and grow max dict_size if needed */ for (strm = head; strm; strm = strm->next) { + struct xz_dec_microlzma *state; + + if (strm->dict_size >= dict_size) + continue; + state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size); + if (!state) { + err = -ENOMEM; + break; + } if (strm->state) xz_dec_microlzma_end(strm->state); - strm->state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size); - if (!strm->state) - err = -ENOMEM; + strm->state = state; + strm->dict_size = dict_size; } /* 3. push back all to the global list and update max dict_size */ @@ -142,7 +151,8 @@ again: spin_unlock(&z_erofs_lzma_lock); wake_up_all(&z_erofs_lzma_wq); - z_erofs_lzma_max_dictsize = dict_size; + if (!err) + z_erofs_lzma_max_dictsize = dict_size; mutex_unlock(&lzma_resize_mutex); return err; } diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 65974e57aebf..12e3a5b80a5a 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -71,12 +71,8 @@ struct erofs_dev_context { bool flatdev; }; -/* all filesystem-wide lz4 configurations */ struct erofs_sb_lz4_info { - /* # of pages needed for EROFS lz4 rolling decompression */ - u16 max_distance_pages; - /* maximum possible blocks for pclusters in the filesystem */ - u16 max_pclusterblks; + u16 max_pclusterblks; /* maximum physical blocks for LZ4 pclusters */ }; struct erofs_xattr_prefix_item { diff --git a/fs/erofs/sysfs.c b/fs/erofs/sysfs.c index 6734483a440f..dfcec9376cd5 100644 --- a/fs/erofs/sysfs.c +++ b/fs/erofs/sysfs.c @@ -95,6 +95,7 @@ EROFS_ATTR_FEATURE(sb_chksum); EROFS_ATTR_FEATURE(ztailpacking); EROFS_ATTR_FEATURE(fragments); EROFS_ATTR_FEATURE(dedupe); +EROFS_ATTR_FEATURE(xattr_prefixes); EROFS_ATTR_FEATURE(48bit); EROFS_ATTR_FEATURE(metabox); @@ -108,6 +109,7 @@ static struct attribute *erofs_feat_attrs[] = { ATTR_LIST(ztailpacking), ATTR_LIST(fragments), ATTR_LIST(dedupe), + ATTR_LIST(xattr_prefixes), ATTR_LIST(48bit), ATTR_LIST(metabox), NULL, diff --git a/fs/erofs/xattr.c b/fs/erofs/xattr.c index df7ea019526d..57cfb7520782 100644 --- a/fs/erofs/xattr.c +++ b/fs/erofs/xattr.c @@ -620,8 +620,8 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, { struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); struct erofs_xattr_prefix_item *prefix; + int domainlen, valuelen, base_index; const char *infix; - int valuelen, base_index; if (!test_opt(&sbi->opt, INODE_SHARE)) return -EOPNOTSUPP; @@ -633,17 +633,18 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp, valuelen = erofs_getxattr(inode, base_index, infix, NULL, 0); if (valuelen <= 0 || valuelen > (1 << sbi->blkszbits)) return -EFSCORRUPTED; - fp->size = valuelen + (domain_id ? strlen(domain_id) : 0); + domainlen = strlen(domain_id); + fp->size = domainlen + 1 + valuelen; fp->opaque = kmalloc(fp->size, GFP_KERNEL); if (!fp->opaque) return -ENOMEM; + memcpy(fp->opaque, domain_id, domainlen + 1); if (valuelen != erofs_getxattr(inode, base_index, infix, - fp->opaque, valuelen)) { + fp->opaque + domainlen + 1, valuelen)) { kfree(fp->opaque); fp->opaque = NULL; return -EFSCORRUPTED; } - memcpy(fp->opaque + valuelen, domain_id, fp->size - valuelen); return 0; } #endif diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index e1e25ca0d190..6b07e73ee2aa 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1259,7 +1259,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) const struct z_erofs_decompressor *alg = z_erofs_decomp[pcl->algorithmformat]; bool try_free = true; - int i, j, jtop, err2, err = eio ? -EIO : 0; + int i, err2, err = eio ? -EIO : 0; struct page *page; bool overlapped; const char *reason; @@ -1348,7 +1348,6 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) be->compressed_pages >= be->onstack_pages + Z_EROFS_ONSTACK_PAGES) kvfree(be->compressed_pages); - jtop = 0; z_erofs_fill_other_copies(be, err); for (i = 0; i < be->nr_pages; ++i) { page = be->decompressed_pages[i]; @@ -1356,22 +1355,11 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) continue; DBG_BUGON(z_erofs_page_is_invalidated(page)); - if (!z_erofs_is_shortlived_page(page)) { + if (!z_erofs_is_shortlived_page(page)) erofs_onlinefolio_end(page_folio(page), err, true); - continue; - } - if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) { + else erofs_pagepool_add(be->pagepool, page); - continue; - } - for (j = 0; j < jtop && be->decompressed_pages[j] != page; ++j) - ; - if (j >= jtop) /* this bounce page is newly detected */ - be->decompressed_pages[jtop++] = page; } - while (jtop) - erofs_pagepool_add(be->pagepool, - be->decompressed_pages[--jtop]); if (be->decompressed_pages != be->onstack_pages) kvfree(be->decompressed_pages); diff --git a/fs/exec.c b/fs/exec.c index 745f6eb5279e..d3081c8f7c10 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1165,6 +1165,20 @@ int begin_new_exec(struct linux_binprm * bprm) goto out; /* + * We have to apply CLOEXEC before we change whether the process is + * dumpable (in setup_new_exec) to avoid a race with a process in userspace + * trying to access the should-be-closed file descriptors of a process + * undergoing exec(2). + * + * This can block on filesystem ->flush() handlers, including waiting + * for FUSE daemons, so do it before exec_mmap takes the + * exec_update_lock. + * This must happen after the point of no return, and after unsharing + * the FD table. + */ + do_close_on_exec(me->files); + + /* * Must be called _before_ exec_mmap() as bprm->mm is * not visible until then. Doing it here also ensures * we don't race against replace_mm_exe_file(). @@ -1214,14 +1228,6 @@ int begin_new_exec(struct linux_binprm * bprm) clear_syscall_work_syscall_user_dispatch(me); - /* - * We have to apply CLOEXEC before we change whether the process is - * dumpable (in setup_new_exec) to avoid a race with a process in userspace - * trying to access the should-be-closed file descriptors of a process - * undergoing exec(2). - */ - do_close_on_exec(me->files); - if (bprm->secureexec) { /* Make sure parent cannot signal privileged process. */ me->pdeath_signal = 0; @@ -1472,9 +1478,9 @@ static void free_bprm(struct linux_binprm *bprm) /* exec swapped the mm but failed before setup_new_exec() freed it */ if (bprm->old_mm) exec_mm_put_old(bprm->old_mm); - do_close_execat(bprm->file); /* An unconsumed PT_INTERP substitute from a binfmt_misc loader entry. */ bprm_drop_loader(bprm); + do_close_execat(bprm->file); do_close_execat(bprm->executable); /* If a binfmt changed the interp, free it. */ if (bprm->interp != bprm->filename) diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index 062103e42cd8..0cac890cf370 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -1116,7 +1116,7 @@ static int ext4_fc_snapshot_inode(struct inode *inode, else if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) inode_len += ei->i_extra_isize; - snap = kmalloc(struct_size(snap, inode_buf, inode_len), GFP_NOFS); + snap = kmalloc_flex(*snap, inode_buf, inode_len, GFP_NOFS); if (!snap) { atomic64_inc(&stats->snap_fail_nomem); ext4_fc_set_snap_err(snap_err, EXT4_FC_SNAP_ERR_NOMEM); @@ -1522,7 +1522,7 @@ static int ext4_fc_alloc_snapshot_inodes(struct super_block *sb, if (nr_inodes > EXT4_FC_SNAPSHOT_MAX_INODES) return -E2BIG; - inodes = kvcalloc(nr_inodes, sizeof(*inodes), GFP_NOFS); + inodes = kvzalloc_objs(*inodes, nr_inodes, GFP_NOFS); if (!inodes) return -ENOMEM; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index bd4b778df9eb..26f0f9714f03 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -6456,9 +6456,10 @@ int ext4_chunk_trans_blocks(struct inode *inode, int nrblocks) int ext4_mark_iloc_dirty(handle_t *handle, struct inode *inode, struct ext4_iloc *iloc) { + struct super_block *sb = inode->i_sb; int err = 0; - err = ext4_emergency_state(inode->i_sb); + err = ext4_emergency_state(sb); if (unlikely(err)) { put_bh(iloc->bh); return err; @@ -6473,9 +6474,13 @@ int ext4_mark_iloc_dirty(handle_t *handle, put_bh(iloc->bh); /* * Mark that there's metadata writeout pending for the inode so that it - * gets properly flushed on fsync(2) and similar. + * gets properly flushed on fsync(2) and similar. We don't bother for + * fastcommit replay as that flushes the whole bdev afterwards anyway. + * It is faster this way and we avoid entering fs writeback paths which + * aren't fully initialized yet. */ - if (!EXT4_SB(inode->i_sb)->s_journal) { + if (!ext4_handle_valid(handle) && + !(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY)) { /* * Inode didn't need to go through dirtying, make sure it is * attached to wb so that writeback can handle it. diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8d6135a6108a..9a36d0329e22 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1597,8 +1597,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, * manually extract pages using iov_iter_extract_pages() and then * copy that to a folios array. */ - struct page **pages = kcalloc(max_pages, sizeof(struct page *), - GFP_KERNEL); + struct page **pages = kzalloc_objs(struct page *, max_pages); if (!pages) { ret = -ENOMEM; goto out; diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index 5ca87151d70d..d2599043f7ec 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -336,7 +336,7 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, static struct page **fuse_readdir_alloc_buf(struct fuse_args_pages *ap, size_t *bufsize) { unsigned int i, nr_alloc, nr_pages = DIV_ROUND_UP(*bufsize, PAGE_SIZE); - struct page **pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); + struct page **pages = kzalloc_objs(*pages, nr_pages); if (!pages) return NULL; diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 1b331108d9c0..fcb5b9cd17f6 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -312,7 +312,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) return NULL; } - node = kzalloc_flex(*node, page, tree->pages_per_bnode, GFP_KERNEL); + node = kzalloc_flex(*node, page, tree->pages_per_bnode); if (!node) return NULL; node->tree = tree; diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 237dcdd73fc2..abb286bc3474 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -142,10 +142,8 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size) struct kernfs_iattrs *attrs; attrs = kernfs_iattrs_noalloc(kn); - if (!attrs) - return 0; - return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); + return simple_xattr_list(d_inode(dentry), attrs ? &attrs->xattrs : NULL, buf, size); } static inline void set_default_inode_attr(struct inode *inode, umode_t mode) diff --git a/fs/namespace.c b/fs/namespace.c index 1ecd96c918b3..ae5dc64f8b45 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5999,7 +5999,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, return -EPERM; } - ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); + ks = kmalloc_obj(*ks, GFP_KERNEL_ACCOUNT); if (!ks) return -ENOMEM; diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 7fdfa4f27e34..424df70a5c30 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -55,6 +55,42 @@ static void netfs_rreq_expand(struct netfs_io_request *rreq, } /* + * Drop the folio refs acquired from the readahead API. + */ +static void netfs_bulk_drop_ra_refs(struct netfs_io_request *rreq) +{ + struct folio_batch fbatch; + struct folio *folio; + pgoff_t nr_pages = DIV_ROUND_UP(rreq->len, PAGE_SIZE); + pgoff_t first = rreq->start / PAGE_SIZE; + XA_STATE(xas, &rreq->mapping->i_pages, first); + + folio_batch_init(&fbatch); + + rcu_read_lock(); + + xas_for_each(&xas, folio, first + nr_pages - 1) { + if (xas_retry(&xas, folio)) + continue; + + if (!folio_batch_add(&fbatch, folio)) + folio_batch_release(&fbatch); + } + + rcu_read_unlock(); + folio_batch_release(&fbatch); + trace_netfs_rreq(rreq, netfs_rreq_trace_ra_put_ref); + clear_bit_unlock(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags); + wake_up(&rreq->waitq); +} + +static void netfs_maybe_bulk_drop_ra_refs(struct netfs_io_request *rreq) +{ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_bulk_drop_ra_refs(rreq); +} + +/* * Begin an operation, and fetch the stored zero point value from the cookie if * available. */ @@ -74,12 +110,8 @@ static int netfs_begin_cache_read(struct netfs_io_request *rreq, struct netfs_in * * Returns the limited size if successful and -ENOMEM if insufficient memory * available. - * - * [!] NOTE: This must be run in the same thread as ->issue_read() was called - * in as we access the readahead_control struct. */ -static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, - struct readahead_control *ractl) +static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; size_t rsize = subreq->len; @@ -87,30 +119,6 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, if (subreq->source == NETFS_DOWNLOAD_FROM_SERVER) rsize = umin(rsize, rreq->io_streams[0].sreq_max_len); - if (ractl) { - /* If we don't have sufficient folios in the rolling buffer, - * extract a folioq's worth from the readahead region at a time - * into the buffer. Note that this acquires a ref on each page - * that we will need to release later - but we don't want to do - * that until after we've started the I/O. - */ - struct folio_batch put_batch; - - folio_batch_init(&put_batch); - while (rreq->submitted < subreq->start + rsize) { - ssize_t added; - - added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, - &put_batch); - if (added < 0) { - folio_batch_release(&put_batch); - return added; - } - rreq->submitted += added; - } - folio_batch_release(&put_batch); - } - subreq->len = rsize; if (unlikely(rreq->io_streams[0].sreq_max_segs)) { size_t limit = netfs_limit_iter(&rreq->buffer.iter, 0, rsize, @@ -204,16 +212,67 @@ static void netfs_issue_read(struct netfs_io_request *rreq, } /* + * Mark folios that we want to copy to the cache. For filesystems that use + * netfslib fully, we set folio->private to NETFS_FOLIO_COPY_TO_CACHE; + * otherwise we set the deprecated PG_private_2. + */ +static void netfs_mark_copy_to_cache(struct netfs_io_request *rreq, + struct folio_queue **fq, + unsigned int *offset, + int *slot, + size_t len, + bool copy) +{ + while (len > 0) { + struct folio *folio; + size_t fsize, overlap; + + if (!*fq) + break; + if (*slot >= folioq_count(*fq)) { + *fq = (*fq)->next; + *slot = 0; + *offset = 0; + continue; + } + + /* Determine how much the subreq overlaps the folio, if at all. */ + fsize = folioq_folio_size(*fq, *slot); + overlap = min(len, fsize - *offset); + + if (overlap > 0 && copy) { + folio = folioq_folio(*fq, *slot); + if (unlikely(test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags))) { + if (!folio_test_private_2(folio)) + folio_start_private_2(folio); + } else { + if (!folio_get_private(folio)) + folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); + } + trace_netfs_folio(folio, netfs_folio_trace_mark_copy); + } + + len -= overlap; + *offset += overlap; + if (*offset >= fsize) { + *slot += 1; + *offset = 0; + } + } +} + +/* * Perform a read to the pagecache from a series of sources of different types, * slicing up the region to be read according to available cache blocks and * network rsize. */ -static void netfs_read_to_pagecache(struct netfs_io_request *rreq, - struct readahead_control *ractl) +static void netfs_read_to_pagecache(struct netfs_io_request *rreq) { + struct folio_queue *fq = rreq->buffer.tail; unsigned long long start = rreq->start; + unsigned int offset = 0; ssize_t size = rreq->len; - int ret = 0; + int ret = 0, slot = 0; do { struct netfs_io_subrequest *subreq; @@ -288,7 +347,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, break; issue: - slice = netfs_prepare_read_iterator(subreq, ractl); + slice = netfs_prepare_read_iterator(subreq); if (slice < 0) { ret = slice; netfs_cancel_read(subreq, ret); @@ -301,7 +360,15 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); } + if (fq) { + /* See if the cache indicated this should be cached. */ + bool copy = test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags); + + netfs_mark_copy_to_cache(rreq, &fq, &slot, &offset, slice, copy); + } + netfs_issue_read(rreq, subreq); + netfs_maybe_bulk_drop_ra_refs(rreq); if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) netfs_wait_for_paused_read(rreq); @@ -339,7 +406,8 @@ void netfs_readahead(struct readahead_control *ractl) { struct netfs_io_request *rreq; struct netfs_inode *ictx = netfs_inode(ractl->mapping->host); - unsigned long long start = readahead_pos(ractl); + ssize_t added; + uoff_t start = readahead_pos(ractl); size_t size = readahead_length(ractl); int ret; @@ -360,11 +428,24 @@ void netfs_readahead(struct readahead_control *ractl) netfs_rreq_expand(rreq, ractl); - rreq->submitted = rreq->start; - if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) + /* Load the folios to be read into a bvecq chain. Note that this + * acquires a ref on each folio that we will need to release later - + * but we don't want to do that until after we've started the I/O. + */ + added = rolling_buffer_bulk_load_from_ra(&rreq->buffer, ractl, + rreq->debug_id, rreq->gfp); + if (added < 0) { + ret = added; goto cleanup_free; - netfs_read_to_pagecache(rreq, ractl); + } + __set_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags); + + rreq->submitted = rreq->start + added; + rreq->cleaned_to = rreq->start; + netfs_read_set_unlock_at(rreq); + netfs_read_to_pagecache(rreq); + netfs_maybe_bulk_drop_ra_refs(rreq); return netfs_put_request(rreq, netfs_rreq_trace_put_return); cleanup_free: @@ -387,6 +468,7 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo if (added < 0) return added; rreq->submitted = rreq->start + added; + rreq->progress_at = added; return 0; } @@ -457,7 +539,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) iov_iter_bvec(&rreq->buffer.iter, ITER_DEST, bvec, i, rreq->len); rreq->submitted = rreq->start + flen; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); if (ret >= 0) { @@ -532,7 +614,7 @@ int netfs_read_folio(struct file *file, struct folio *folio) if (ret < 0) goto discard; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; @@ -689,7 +771,7 @@ retry: if (ret < 0) goto error_put; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); if (ret < 0) @@ -754,7 +836,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, if (ret < 0) goto error_put; - netfs_read_to_pagecache(rreq, NULL); + netfs_read_to_pagecache(rreq); ret = netfs_wait_for_read(rreq); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index c16fbad286a1..2361277416c7 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -21,7 +21,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq) /* Okay, declare that all I/O is complete. */ trace_netfs_rreq(wreq, netfs_rreq_trace_write_done); - if (!wreq->error) + if (wreq->transferred) netfs_update_i_size(ictx, &ictx->inode, wreq->start, wreq->transferred); if (wreq->origin == NETFS_DIO_WRITE && @@ -51,7 +51,7 @@ static void netfs_unbuffered_write_done(struct netfs_io_request *wreq) wreq->iocb->ki_pos += written; if (wreq->iocb->ki_complete) { trace_netfs_rreq(wreq, netfs_rreq_trace_ki_complete); - wreq->iocb->ki_complete(wreq->iocb, wreq->error ?: written); + wreq->iocb->ki_complete(wreq->iocb, written ?: wreq->error); } wreq->iocb = VFS_PTR_POISON; } @@ -95,7 +95,7 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) { struct netfs_io_subrequest *subreq = NULL; struct netfs_io_stream *stream = &wreq->io_streams[0]; - int ret; + int ret = 0; _enter("%llx", wreq->len); @@ -110,6 +110,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) if (!subreq) { netfs_prepare_write(wreq, stream, wreq->start + wreq->transferred); subreq = stream->construct; + if (!subreq) { + wreq->error = -ENOMEM; + ret = -ENOMEM; + break; + } stream->construct = NULL; } @@ -121,8 +126,14 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) } iov_iter_truncate(&subreq->io_iter, wreq->len - wreq->transferred); - if (!iov_iter_count(&subreq->io_iter)) + if (!iov_iter_count(&subreq->io_iter)) { + pr_warn("netfs: Unexpected zero-length iterator R=%08x\n", + wreq->debug_id); + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + netfs_write_subrequest_terminated(subreq, -EIO); + wreq->error = -EIO; break; + } subreq->len = netfs_limit_iter(&subreq->io_iter, 0, stream->sreq_max_len, @@ -139,13 +150,11 @@ static int netfs_unbuffered_write(struct netfs_io_request *wreq) if (test_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags)) { retry = true; } else if (test_bit(NETFS_SREQ_FAILED, &subreq->flags)) { - ret = subreq->error; - wreq->error = ret; + wreq->error = subreq->error; netfs_see_subrequest(subreq, netfs_sreq_trace_see_failed); subreq = NULL; break; } - ret = 0; if (!retry) { netfs_unbuffered_write_collect(wreq, stream, subreq); @@ -288,11 +297,11 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * ret = -EIOCBQUEUED; } else { ret = netfs_unbuffered_write(wreq); - if (ret < 0) { - _debug("begin = %zd", ret); - } else { + if (wreq->transferred) { iocb->ki_pos += wreq->transferred; - ret = wreq->transferred ?: wreq->error; + ret = wreq->transferred; + } else if (wreq->error) { + ret = wreq->error; } netfs_put_request(wreq, netfs_rreq_trace_put_complete); diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 420ee7b26580..c79c8e69d60c 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -79,6 +79,7 @@ ssize_t netfs_wait_for_read(struct netfs_io_request *rreq); ssize_t netfs_wait_for_write(struct netfs_io_request *rreq); void netfs_wait_for_paused_read(struct netfs_io_request *rreq); void netfs_wait_for_paused_write(struct netfs_io_request *rreq); +void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq); /* * objects.c @@ -109,6 +110,8 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq, /* * read_collect.c */ +void netfs_cancel_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio); +void netfs_read_set_unlock_at(struct netfs_io_request *rreq); bool netfs_read_collection(struct netfs_io_request *rreq); void netfs_read_collection_worker(struct work_struct *work); void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 5d554512ed23..f5c1c463f4ff 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -563,3 +563,22 @@ void netfs_wait_for_paused_write(struct netfs_io_request *rreq) { return netfs_wait_for_pause(rreq, netfs_write_collection); } + +/* + * Wait for the readahead-acquired refs to be put. + */ +void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq) +{ + DEFINE_WAIT(myself); + + for (;;) { + trace_netfs_rreq(rreq, netfs_rreq_trace_wait_put_ra_refs); + prepare_to_wait(&rreq->waitq, &myself, TASK_UNINTERRUPTIBLE); + if (!test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + break; + schedule(); + } + + trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs); + finish_wait(&rreq->waitq, &myself); +} diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index 01461a74642d..7f6a3e912602 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -41,24 +41,32 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping, memset(rreq, 0, kmem_cache_size(cache)); INIT_WORK(&rreq->cleanup_work, netfs_free_request); - rreq->gfp = gfp; - rreq->start = start; - rreq->len = len; - rreq->origin = origin; - rreq->netfs_ops = ctx->ops; - rreq->mapping = mapping; - rreq->inode = inode; - rreq->i_size = i_size_read(inode); - rreq->debug_id = atomic_inc_return(&debug_ids); - rreq->wsize = INT_MAX; + rreq->gfp = gfp; + rreq->start = start; + rreq->collected_to = start; + rreq->cleaned_to = start; + rreq->len = len; + rreq->progress_at = 0; + rreq->origin = origin; + rreq->netfs_ops = ctx->ops; + rreq->mapping = mapping; + rreq->inode = inode; + rreq->i_size = i_size_read(inode); + rreq->debug_id = atomic_inc_return(&debug_ids); + rreq->wsize = INT_MAX; rreq->io_streams[0].sreq_max_len = ULONG_MAX; rreq->io_streams[0].sreq_max_segs = 0; spin_lock_init(&rreq->lock); - INIT_LIST_HEAD(&rreq->io_streams[0].subrequests); - INIT_LIST_HEAD(&rreq->io_streams[1].subrequests); init_waitqueue_head(&rreq->waitq); refcount_set(&rreq->ref, 2); + for (int s = 0; s < NR_IO_STREAMS; s++) { + struct netfs_io_stream *stream = &rreq->io_streams[s]; + + INIT_LIST_HEAD(&stream->subrequests); + stream->collected_to = rreq->start; + } + if (origin == NETFS_READAHEAD || origin == NETFS_READPAGE || origin == NETFS_READ_GAPS || diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index 23660a590124..5cf22087d243 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -19,7 +19,6 @@ #define MADE_PROGRESS 0x04 /* Made progress cleaning up a stream or the folio set */ #define BUFFERED 0x08 /* The pagecache needs cleaning up */ #define NEED_RETRY 0x10 /* A front op requests retrying */ -#define COPY_TO_CACHE 0x40 /* Need to copy subrequest to cache */ #define ABANDON_SREQ 0x80 /* Need to abandon untransferred part of subrequest */ /* @@ -35,6 +34,30 @@ static void netfs_clear_unread(struct netfs_io_subrequest *subreq) } /* + * Cancel the copy-to-cache mark on a folio. + */ +void netfs_cancel_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio) +{ + if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { + if (folio_get_private(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + folio_detach_private(folio); + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } else if (netfs_folio_group(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + struct netfs_folio *finfo = netfs_folio_info(folio); + + finfo->netfs_group = NULL; + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } + } else { + // TODO: Use of PG_private_2 is deprecated. + if (folio_test_private_2(folio)) { + folio_end_private_2(folio); + trace_netfs_folio(folio, netfs_folio_trace_cancel_copy); + } + } +} + +/* * Flush, mark and unlock a folio that's now completely read. If we want to * cache the folio, we set the group to NETFS_FOLIO_COPY_TO_CACHE, mark it * dirty and let writeback handle it. @@ -48,37 +71,37 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, if (unlikely(folio_pos(folio) < rreq->abandon_to)) { trace_netfs_folio(folio, netfs_folio_trace_abandon); + netfs_cancel_copy_to_cache(rreq, folio); goto just_unlock; } flush_dcache_folio(folio); folio_mark_uptodate(folio); - if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { - finfo = netfs_folio_info(folio); - if (finfo) { - trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); - kfree(finfo); - } + if (unlikely(test_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags))) + netfs_cancel_copy_to_cache(rreq, folio); - if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) { - if (!WARN_ON_ONCE(folio_get_private(folio) != NULL)) { - trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache); - folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE); - folio_mark_dirty(folio); - } + if (!test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) { + if (netfs_folio_group(folio) == NETFS_FOLIO_COPY_TO_CACHE) { + trace_netfs_folio(folio, netfs_folio_trace_sched_copy); + folio_mark_dirty(folio); } else { + finfo = netfs_folio_info(folio); + if (finfo) { + trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); + if (finfo->netfs_group) + folio_change_private(folio, finfo->netfs_group); + else + folio_detach_private(folio); + kfree(finfo); + } trace_netfs_folio(folio, netfs_folio_trace_read_done); } folioq_clear(folioq, slot); } else { // TODO: Use of PG_private_2 is deprecated. - if (test_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags)) + if (folio_test_private_2(folio)) netfs_pgpriv2_copy_to_cache(rreq, folio); } @@ -95,6 +118,35 @@ just_unlock: } /* + * Determine how much to gather before unlocking more folios. + */ +void netfs_read_set_unlock_at(struct netfs_io_request *rreq) +{ + struct folio_queue *folioq = rreq->buffer.tail; + unsigned int slot = rreq->buffer.first_tail_slot; + size_t cleaned_to = rreq->cleaned_to - rreq->start; + size_t progress_at = cleaned_to; + size_t minimum = 256 * 1024; + + while (progress_at < rreq->len) { + if (slot >= folioq_count(folioq)) { + folioq = folioq->next; + if (!folioq) + break; + slot = 0; + } + + progress_at += folioq_folio_size(folioq, slot); + if (progress_at - cleaned_to >= minimum) + break; + slot++; + } + + WRITE_ONCE(rreq->progress_at, progress_at); + trace_netfs_read_progress_at(rreq); +} + +/* * Unlock any folios we've finished with. */ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, @@ -112,30 +164,31 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, if (slot >= folioq_nr_slots(folioq)) { folioq = rolling_buffer_delete_spent(&rreq->buffer); if (!folioq) { - rreq->front_folio_order = 0; + WRITE_ONCE(rreq->progress_at, rreq->len); return; } slot = 0; } + /* We have to wait for readahead refs to have been released before we + * can unlock any folios as the ref-dropper walks i_pages and the only + * thing preventing these folios from being removed is the folio lock. + */ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_wait_for_put_ra_refs(rreq); + for (;;) { struct folio *folio; unsigned long long fpos, fend; - unsigned int order; size_t fsize; - if (*notes & COPY_TO_CACHE) - set_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); - folio = folioq_folio(folioq, slot); if (WARN_ONCE(!folio_test_locked(folio), "R=%08x: folio %lx is not locked\n", rreq->debug_id, folio->index)) trace_netfs_folio(folio, netfs_folio_trace_not_locked); - order = folioq_folio_order(folioq, slot); - rreq->front_folio_order = order; - fsize = PAGE_SIZE << order; + fsize = folioq_folio_size(folioq, slot); fpos = folio_pos(folio); fend = fpos + fsize; @@ -149,8 +202,6 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, WRITE_ONCE(rreq->cleaned_to, fpos + fsize); *notes |= MADE_PROGRESS; - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); - /* Clean up the head folioq. If we clear an entire folioq, then * we can get rid of it provided it's not also the tail folioq * being filled by the issuer. @@ -172,6 +223,8 @@ static void netfs_read_unlock_folios(struct netfs_io_request *rreq, rreq->buffer.tail = folioq; done: rreq->buffer.first_tail_slot = slot; + + netfs_read_set_unlock_at(rreq); } /* @@ -232,7 +285,7 @@ reassess: * subreqs. */ if (notes & BUFFERED) { - size_t fsize = PAGE_SIZE << rreq->front_folio_order; + uoff_t unlock_at = rreq->start + rreq->progress_at; /* Clear the tail of a short read. */ if (!(notes & HIT_PENDING) && @@ -248,16 +301,13 @@ reassess: stream->collected_to = front->start + transferred; rreq->collected_to = stream->collected_to; - if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &front->flags)) - notes |= COPY_TO_CACHE; - if (test_bit(NETFS_SREQ_FAILED, &front->flags)) { rreq->abandon_to = front->start + front->len; front->transferred = front->len; transferred = front->len; trace_netfs_rreq(rreq, netfs_rreq_trace_set_abandon); } - if (front->start + transferred >= rreq->cleaned_to + fsize || + if (front->start + transferred >= unlock_at || test_bit(NETFS_SREQ_HIT_EOF, &front->flags)) netfs_read_unlock_folios(rreq, ¬es); } else { @@ -477,20 +527,22 @@ void netfs_read_collection_worker(struct work_struct *work) void netfs_read_subreq_progress(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; - struct netfs_io_stream *stream = &rreq->io_streams[0]; - size_t fsize = PAGE_SIZE << rreq->front_folio_order; - - trace_netfs_sreq(subreq, netfs_sreq_trace_progress); + struct netfs_io_stream *stream = &rreq->io_streams[subreq->stream_nr]; + size_t progress_at = READ_ONCE(rreq->progress_at); + uoff_t update_at = rreq->start + progress_at; + uoff_t transferred_to = subreq->start + subreq->transferred; /* If we are at the head of the queue, wake up the collector, * getting a ref to it if we were the ones to do so. */ - if (subreq->start + subreq->transferred > rreq->cleaned_to + fsize && + if (progress_at < rreq->len && + transferred_to >= update_at && (rreq->origin == NETFS_READAHEAD || rreq->origin == NETFS_READPAGE || rreq->origin == NETFS_READ_FOR_WRITE) && list_is_first(&subreq->rreq_link, &stream->subrequests) ) { + trace_netfs_sreq(subreq, netfs_sreq_trace_progress); __set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags); netfs_wake_collector(rreq); } diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index c31190993b76..a4b7bb88cbdb 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -54,8 +54,8 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio /* Attach the folio to the rolling buffer. */ if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { + set_bit(NETFS_RREQ_CANCEL_CACHING, &creq->flags); folio_end_private_2(folio); - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; } @@ -122,13 +122,14 @@ cancel_put: netfs_put_failed_request(creq); cancel: rreq->copy_to_cache = ERR_PTR(-ENOBUFS); - clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &rreq->flags); + set_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags); return ERR_PTR(-ENOBUFS); } /* * [DEPRECATED] Mark page as requiring copy-to-cache using PG_private_2 and add - * it to the copy write request. + * it to the copy write request. PG_private_2 should already be set on the + * folio. */ void netfs_pgpriv2_copy_to_cache(struct netfs_io_request *rreq, struct folio *folio) { @@ -136,11 +137,13 @@ void netfs_pgpriv2_copy_to_cache(struct netfs_io_request *rreq, struct folio *fo if (!creq) creq = netfs_pgpriv2_begin_copy_to_cache(rreq, folio); - if (IS_ERR(creq)) + if (IS_ERR(creq)) { + set_bit(NETFS_RREQ_CANCEL_CACHING, &rreq->flags); + netfs_cancel_copy_to_cache(rreq, folio); return; + } - trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache); - folio_start_private_2(folio); + trace_netfs_folio(folio, netfs_folio_trace_pgpriv2_copy); netfs_pgpriv2_copy_folio(creq, folio); } diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index 2b42758e01ec..4f6a36c6e214 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -292,11 +292,22 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) { struct folio_queue *p; + /* We have to wait for readahead refs to have been released before we + * can unlock any folios as the ref-dropper walks i_pages and the only + * thing preventing these folios from being removed is the folio lock. + */ + if (test_bit(NETFS_RREQ_NEED_PUT_RA_REFS, &rreq->flags)) + netfs_wait_for_put_ra_refs(rreq); + for (p = rreq->buffer.tail; p; p = p->next) { for (int slot = 0; slot < folioq_count(p); slot++) { struct folio *folio = folioq_folio(p, slot); - if (folio && !folioq_is_marked2(p, slot)) { + if (!folio) + continue; + netfs_cancel_copy_to_cache(rreq, folio); + + if (!folioq_is_marked2(p, slot)) { if (folio == rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c index 8833550d2eb6..de67ac41548d 100644 --- a/fs/netfs/read_single.c +++ b/fs/netfs/read_single.c @@ -170,6 +170,8 @@ ssize_t netfs_read_single(struct inode *inode, struct file *file, struct iov_ite if (IS_ERR(rreq)) return PTR_ERR(rreq); + rreq->progress_at = rreq->len; + ret = netfs_single_begin_cache_read(rreq, ictx); if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) goto cleanup_free; diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c index 8c0026836f9c..424e77a9a109 100644 --- a/fs/netfs/rolling_buffer.c +++ b/fs/netfs/rolling_buffer.c @@ -115,42 +115,65 @@ int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) } /* - * Decant the list of folios to read into a rolling buffer. + * Decant the entire list of folios to read into a rolling buffer. */ -ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, - struct readahead_control *ractl, - struct folio_batch *put_batch) +ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll, + struct readahead_control *ractl, + unsigned int rreq_id, gfp_t gfp) { struct folio_queue *fq; - struct page **vec; - int nr, ix, to; - ssize_t size = 0; + ssize_t loaded = 0; - if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) - return -ENOMEM; + while (ractl->_nr_pages - ractl->_batch_count > 0) { + unsigned int nr; - fq = roll->head; - vec = (struct page **)fq->vec.folios; - nr = __readahead_batch(ractl, vec + folio_batch_count(&fq->vec), - folio_batch_space(&fq->vec)); - ix = fq->vec.nr; - to = ix + nr; - fq->vec.nr = to; - for (; ix < to; ix++) { - struct folio *folio = folioq_folio(fq, ix); - unsigned int order = folio_order(folio); - - fq->orders[ix] = order; - size += PAGE_SIZE << order; - trace_netfs_folio(folio, netfs_folio_trace_read); - if (!folio_batch_add(put_batch, folio)) - folio_batch_release(put_batch); + /* Allocate a folioq to put some folios into and attach it to + * the rolling buffer. + */ + fq = netfs_folioq_alloc(rreq_id, gfp, + netfs_trace_folioq_make_space); + if (!fq) + goto nomem_unlock; + fq->prev = roll->head; + if (!roll->tail) + roll->tail = fq; + else + roll->head->next = fq; + roll->head = fq; + + /* Get a batch of folios and note their orders. */ + nr = __readahead_batch(ractl, (struct page **)fq->vec.folios, + folioq_nr_slots(fq)); + if (WARN_ON_ONCE(!nr)) + break; + fq->vec.nr = nr; + + for (int slot = 0; slot < nr; slot++) { + struct folio *folio = folioq_folio(fq, slot); + unsigned int order; + + order = folio_order(folio); + fq->orders[slot] = order; + loaded += PAGE_SIZE << order; + trace_netfs_folio(folio, netfs_folio_trace_read); + } } - WRITE_ONCE(roll->iter.count, roll->iter.count + size); - /* Store the counter after setting the slot. */ - smp_store_release(&roll->next_head_slot, to); - return size; + WRITE_ONCE(roll->iter.count, loaded); + iov_iter_folio_queue(&roll->iter, ITER_DEST, roll->tail, 0, 0, loaded); + return loaded; + +nomem_unlock: + for (fq = roll->tail; fq; fq = fq->next) { + for (int slot = 0; slot < folioq_count(fq); slot++) { + folio_unlock(fq->vec.folios[slot]); + folioq_mark(fq, slot); + } + } + rolling_buffer_clear(roll); + roll->head = NULL; + roll->tail = NULL; + return -ENOMEM; } /* diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2d9cfcd43658..851f6f93ad45 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -170,6 +170,8 @@ void netfs_prepare_write(struct netfs_io_request *wreq, rolling_buffer_make_space(&wreq->buffer, wreq->gfp); subreq = netfs_alloc_subrequest(wreq); + if (!subreq) + return; subreq->source = stream->source; subreq->start = start; subreq->stream_nr = stream->stream_nr; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a47c90f40422..b6e0c543e028 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -358,7 +358,7 @@ int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, goto out_unlock; } - items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + items = kzalloc_objs(*items, cnt); seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); if (!items || !seqnos) { ret = -ENOMEM; @@ -685,7 +685,7 @@ int nfsd_nl_svc_export_get_reqs_dumpit(struct sk_buff *skb, goto out_unlock; } - items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + items = kzalloc_objs(*items, cnt); seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (!items || !seqnos || !pathbuf) { @@ -786,8 +786,7 @@ static int nfsd_nl_parse_fslocations(struct nlattr *attr, if (!count) return 0; - fsloc->locations = kcalloc(count, sizeof(struct nfsd4_fs_location), - GFP_KERNEL); + fsloc->locations = kzalloc_objs(struct nfsd4_fs_location, count); if (!fsloc->locations) return -ENOMEM; @@ -1006,7 +1005,8 @@ static int nfsd_nl_parse_one_export(struct cache_detail *cd, goto out_uuid; err = 0; - nfsd4_setup_layout_type(&exp); + if (exp.ex_flags & NFSEXP_PNFS) + nfsd4_setup_layout_type(&exp); } expp = svc_export_lookup(&exp); diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index a901bbe67e03..19dc337502ca 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -1981,12 +1981,12 @@ int nfsd_net_cb_init(struct nfsd_net *nn) { struct nfsd_net_cb *cb; - cb = kzalloc(sizeof(*cb), GFP_KERNEL); + cb = kzalloc_obj(*cb); if (!cb) return -ENOMEM; cb->version4.counts = kzalloc_objs(unsigned int, - ARRAY_SIZE(nfs4_cb_procedures), GFP_KERNEL); + ARRAY_SIZE(nfs4_cb_procedures)); if (!cb->version4.counts) { kfree(cb); return -ENOMEM; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 18e17232cf94..9c4adf3110ae 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1341,7 +1341,7 @@ alloc_init_dir_deleg(struct nfs4_client *clp, struct nfs4_file *fp) return NULL; } - ncn->ncn_nf = kcalloc(NOTIFY4_EVENT_QUEUE_SIZE, sizeof(*ncn->ncn_nf), GFP_KERNEL); + ncn->ncn_nf = kzalloc_objs(*ncn->ncn_nf, NOTIFY4_EVENT_QUEUE_SIZE); if (!ncn->ncn_nf) { nfs4_put_stid(&dp->dl_stid); return NULL; @@ -10419,8 +10419,9 @@ alloc_nfsd_notify_event(u32 mask, const struct qstr *q, struct dentry *dentry, newnamelen = newname.name.len; } - ne = kmalloc(struct_size(ne, ne_name, q->len + 1 + - (newnamelen ? newnamelen + 1 : 0)), GFP_NOFS); + ne = kmalloc_flex(*ne, ne_name, + q->len + 1 + (newnamelen ? newnamelen + 1 : 0), + GFP_NOFS); if (!ne) goto out; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index adb032b7311a..5abb2d4274c9 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1647,7 +1647,7 @@ static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn) k1 = get_unaligned_le64(nla_data(attr) + 8); if (!fh_key) { - fh_key = kmalloc(sizeof(siphash_key_t), GFP_KERNEL); + fh_key = kmalloc_obj(siphash_key_t); if (!fh_key) { trace_nfsd_ctl_fh_key_set(false, -ENOMEM); return -ENOMEM; diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 60264833bb63..848a0d338b89 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1737,8 +1737,8 @@ static struct attr_def *ntfs_attr_find_in_attrdef(const struct ntfs_volume *vol, struct attr_def *ad; WARN_ON(!type); - for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef < - vol->attrdef_size && ad->type; ++ad) { + for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef <= + vol->attrdef_size - (s32)sizeof(*ad) && ad->type; ++ad) { /* We have not found it yet, carry on searching. */ if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type))) continue; @@ -2500,7 +2500,7 @@ int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type, return offset; put_err_out: ntfs_attr_put_search_ctx(ctx); - return -EIO; + return err; } /* @@ -2639,7 +2639,7 @@ static int ntfs_non_resident_attr_record_add(struct ntfs_inode *ni, __le32 type, return offset; put_err_out: ntfs_attr_put_search_ctx(ctx); - return -1; + return err; } /* @@ -5704,12 +5704,12 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo lcn << vol->cluster_size_bits, alloc_cnt << vol->cluster_size_bits); - if (err > 0) + if (err) goto out; } if (signal_pending(current)) - goto out; + goto signal_out; vcn += alloc_cnt; try_alloc_cnt -= alloc_cnt; @@ -5730,7 +5730,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo up_write(&ni->runlist.lock); mutex_unlock(&ni->mrec_lock); if (err || signal_pending(current)) - goto out; + goto signal_out; vcn += alloc_cnt; try_alloc_cnt -= alloc_cnt; @@ -5756,4 +5756,8 @@ out_unmap: mutex_unlock(&ni->mrec_lock); out: return err >= 0 ? 0 : err; +signal_out: + if (!err) + err = -EINTR; + goto out; } diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c index 86db4d9298ed..4f27eed3b072 100644 --- a/fs/ntfs/bdev-io.c +++ b/fs/ntfs/bdev-io.c @@ -34,7 +34,7 @@ int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t s int error; struct bio *bio; blk_opf_t op; - sector_t sector = start >> SECTOR_SHIFT; + sector_t sector = ntfs_bytes_to_bio_sector(start); if (start & (SECTOR_SIZE - 1)) return -EINVAL; diff --git a/fs/ntfs/bitmap.c b/fs/ntfs/bitmap.c index b1436b3151b9..5a4457551306 100644 --- a/fs/ntfs/bitmap.c +++ b/fs/ntfs/bitmap.c @@ -40,7 +40,7 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range) end_cluster = vol->nr_clusters; } - ra = kzalloc(sizeof(*ra), GFP_NOFS); + ra = kzalloc_obj(*ra, GFP_NOFS); if (!ra) return -ENOMEM; @@ -64,7 +64,7 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range) end = start_buf; while (end < end_buf) { - u64 aligned_start, aligned_count; + u64 aligned_start, aligned_end, aligned_count; u64 start = find_next_zero_bit(bitmap, end_buf - start_buf, end - start_buf) + start_buf; if (start >= end_buf) @@ -74,8 +74,10 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range) start - start_buf) + start_buf; aligned_start = ALIGN(ntfs_cluster_to_bytes(vol, start), dq); - aligned_count = - ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end - start), dq); + aligned_end = ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end), dq); + if (aligned_start >= aligned_end) + continue; + aligned_count = aligned_end - aligned_start; if (aligned_count >= range->minlen) { ret = blkdev_issue_discard(vol->sb->s_bdev, aligned_start >> 9, aligned_count >> 9, GFP_NOFS); diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index 2225630b19d7..99a3ea2b5c55 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c @@ -514,8 +514,8 @@ int ntfs_read_compressed_block(struct folio *folio) return -EIO; } - pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_NOFS); - completed_pages = kmalloc_array(nr_pages + 1, sizeof(int), GFP_NOFS); + pages = kmalloc_objs(struct page *, nr_pages, GFP_NOFS); + completed_pages = kmalloc_objs(int, nr_pages + 1, GFP_NOFS); if (unlikely(!pages || !completed_pages)) { kfree(pages); @@ -1262,7 +1262,7 @@ static int ntfs_compress_workspace_init(struct ntfs_inode *ni, size = ni->itype.compressed.block_size + 2 * (ni->itype.compressed.block_size / NTFS_SB_SIZE) + 2; ws->nr_pages = DIV_ROUND_UP(size, PAGE_SIZE); - ws->pages = kcalloc(ws->nr_pages, sizeof(*ws->pages), GFP_NOFS); + ws->pages = kzalloc_objs(*ws->pages, ws->nr_pages, GFP_NOFS); if (!ws->pages) return -ENOMEM; @@ -1414,7 +1414,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages, bio_pos = ntfs_cluster_to_bytes(vol, bio_lcn); bio = bio_alloc(vol->sb->s_bdev, DIV_ROUND_UP(bio_size, PAGE_SIZE), REQ_OP_WRITE, GFP_NOIO); - bio->bi_iter.bi_sector = ntfs_bytes_to_sector(vol, bio_pos); + bio->bi_iter.bi_sector = ntfs_bytes_to_bio_sector(bio_pos); for (i = 0; bio_size; i++) { unsigned int len = min_t(unsigned int, bio_size, PAGE_SIZE); @@ -1483,7 +1483,7 @@ int ntfs_compress_write(struct ntfs_inode *ni, loff_t pos, size_t count, pages_per_cb = DIV_ROUND_UP(offset_in_page(pos & ~(cb_size - 1)) + cb_size, PAGE_SIZE); - pages = kmalloc_array(pages_per_cb, sizeof(struct page *), GFP_NOFS); + pages = kmalloc_objs(struct page *, pages_per_cb, GFP_NOFS); if (!pages) return -ENOMEM; ctx = kvzalloc_obj(*ctx, GFP_NOFS); diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 2d594cbb4ebe..df60138f9b2d 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -166,8 +166,8 @@ found_it: */ if (ie->key.file_name.file_name_type == FILE_NAME_DOS) { if (!name) { - name = kmalloc(sizeof(struct ntfs_name), - GFP_NOFS); + name = kmalloc_obj(struct ntfs_name, + GFP_NOFS); if (!name) { err = -ENOMEM; goto err_out; @@ -401,8 +401,8 @@ found_it2: */ if (ie->key.file_name.file_name_type == FILE_NAME_DOS) { if (!name) { - name = kmalloc(sizeof(struct ntfs_name), - GFP_NOFS); + name = kmalloc_obj(struct ntfs_name, + GFP_NOFS); if (!name) { err = -ENOMEM; goto unm_err_out; @@ -700,7 +700,7 @@ static int ntfs_ia_blocks_readahead(struct ntfs_inode *ia_ni, loff_t pos) if (dir_start_index >= dir_end_index) return 0; - dir_ra = kzalloc(sizeof(*dir_ra), GFP_NOFS); + dir_ra = kzalloc_obj(*dir_ra, GFP_NOFS); if (!dir_ra) return -ENOMEM; @@ -777,7 +777,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) return -ENOMEM; } - ra = kzalloc(sizeof(struct file_ra_state), GFP_NOFS); + ra = kzalloc_obj(struct file_ra_state, GFP_NOFS); if (!ra) { kfree(name); ntfs_index_ctx_put(ictx); @@ -813,7 +813,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *actor) goto out; } } else if (!private) { - private = kzalloc(sizeof(struct ntfs_file_private), GFP_KERNEL); + private = kzalloc_obj(struct ntfs_file_private); if (!private) { err = -ENOMEM; goto out; @@ -949,7 +949,7 @@ nextdir: } if (!nir) { - nir = kzalloc(sizeof(struct ntfs_index_ra), GFP_KERNEL); + nir = kzalloc_obj(struct ntfs_index_ra); if (nir) { nir->start_index = index; nir->count = 1; diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c index cdd306933d73..b4fcfbe2da4c 100644 --- a/fs/ntfs/ea.c +++ b/fs/ntfs/ea.c @@ -235,7 +235,7 @@ static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len, ea_info_qsize = le32_to_cpu(p_ea_info->ea_query_length); } else { create_ea_info: - p_ea_info = kzalloc(sizeof(struct ea_information), GFP_NOFS); + p_ea_info = kzalloc_obj(struct ea_information, GFP_NOFS); if (!p_ea_info) return -ENOMEM; @@ -404,10 +404,12 @@ alloc_new_ea: *packed_ea_size = p_ea_info->ea_length; mark_mft_record_dirty(ni); out: - if (ea_info_qsize > 0) - NInoSetHasEA(ni); - else - NInoClearHasEA(ni); + if (!err) { + if (ea_info_qsize > 0) + NInoSetHasEA(ni); + else + NInoClearHasEA(ni); + } kvfree(ea_buf); kvfree(old_ea_buf); @@ -615,7 +617,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, if (!buffer) { err = sizeof(u8); } else if (size < sizeof(u8)) { - err = -ENODATA; + err = -ERANGE; } else { err = sizeof(u8); *(u8 *)buffer = (u8)(le32_to_cpu(ni->flags) & 0x3F); @@ -628,7 +630,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, if (!buffer) { err = sizeof(u32); } else if (size < sizeof(u32)) { - err = -ENODATA; + err = -ERANGE; } else { err = sizeof(u32); *(u32 *)buffer = le32_to_cpu(ni->flags); @@ -753,18 +755,39 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr) old_arec_size = le32_to_cpu(a->length); /* - * Move payloads before shrinking the record. Otherwise resizing moves + * Move payloads before shrinking the record. Otherwise resizing moves * the following attribute over the old payload before it can be copied. + * + * When offsets increase, move mapping_pairs first to avoid name + * overwriting the start of mapping_pairs. */ if (arec_size < old_arec_size) { - if (a->name_length && name_ofs != old_name_ofs) - memmove((u8 *)a + name_ofs, (u8 *)a + old_name_ofs, - a->name_length * sizeof(__le16)); - if (mp_ofs != old_mp_ofs) - memmove((u8 *)a + mp_ofs, (u8 *)a + old_mp_ofs, mp_size); + if (name_ofs > old_name_ofs) { + /* Payload offsets increased: move mapping pairs first. */ + if (mp_ofs != old_mp_ofs) + memmove((u8 *)a + mp_ofs, + (u8 *)a + old_mp_ofs, + mp_size); + if (a->name_length && name_ofs != old_name_ofs) + memmove((u8 *)a + name_ofs, + (u8 *)a + old_name_ofs, + a->name_length * + sizeof(__le16)); + } else { + /* Payload offsets decreased or unchanged: move name first. */ + if (a->name_length && name_ofs != old_name_ofs) + memmove((u8 *)a + name_ofs, + (u8 *)a + old_name_ofs, + a->name_length * + sizeof(__le16)); + if (mp_ofs != old_mp_ofs) + memmove((u8 *)a + mp_ofs, + (u8 *)a + old_mp_ofs, + mp_size); + } } - err = ntfs_attr_record_resize(m, a, arec_size); + err = ntfs_attr_record_resize(ctx->mrec, a, arec_size); if (unlikely(err)) goto err_out; diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 88747217ba61..8164326b7812 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -270,18 +270,25 @@ static int ntfs_setattr_size(struct inode *vi, struct iattr *attr) return err; inode_dio_wait(vi); + + /* + * Serialize with page faults and pagecache instantiation so that + * readers cannot observe the size change until the attribute + * updates below have completed. + */ + filemap_invalidate_lock(vi->i_mapping); if (attr->ia_size > old_size) { truncate_pagecache(vi, old_size); i_size_write(vi, attr->ia_size); pagecache_isize_extended(vi, old_size, attr->ia_size); - } else + } else { truncate_setsize(vi, attr->ia_size); + } err = ntfs_truncate_vfs(vi, attr->ia_size, old_size); - if (err) { + if (err) i_size_write(vi, old_size); - return err; - } + filemap_invalidate_unlock(vi->i_mapping); return err; } @@ -669,6 +676,7 @@ out_lock: static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); + struct address_space *mapping = inode->i_mapping; vm_fault_t ret; if (NInoWofCompressed(NTFS_I(inode))) @@ -677,7 +685,14 @@ static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf) sb_start_pagefault(inode->i_sb); file_update_time(vmf->vma->vm_file); + /* + * Serialize against truncate/fallocate which hold the lock + * exclusively while invalidating pagecache and changing extents. + */ + filemap_invalidate_lock_shared(mapping); ret = iomap_page_mkwrite(vmf, &ntfs_page_mkwrite_iomap_ops, NULL); + filemap_invalidate_unlock_shared(mapping); + sb_end_pagefault(inode->i_sb); return ret; } @@ -1116,7 +1131,6 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le struct ntfs_volume *vol = ni->vol; int err = 0; loff_t old_size; - bool map_locked = false; if (mode & ~(NTFS_FALLOC_FL_SUPPORTED)) return -EOPNOTSUPP; @@ -1148,16 +1162,13 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le inode_lock(vi); if (NInoCompressed(ni) || NInoEncrypted(ni) || NInoWofCompressed(ni)) { - err = -EOPNOTSUPP; - goto out; + inode_unlock(vi); + return -EOPNOTSUPP; } inode_dio_wait(vi); - if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | - FALLOC_FL_INSERT_RANGE)) { - filemap_invalidate_lock(vi->i_mapping); - map_locked = true; - } + /* Take invalidate_lock for all fallocate operations to prevent races */ + filemap_invalidate_lock(vi->i_mapping); switch (mode & FALLOC_FL_MODE_MASK) { case FALLOC_FL_ALLOCATE_RANGE: @@ -1182,14 +1193,15 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le err = file_modified(file); out: - if (map_locked) - filemap_invalidate_unlock(vi->i_mapping); + if (!err && mode == 0 && NInoNonResident(ni) && + offset > old_size) { + truncate_pagecache(vi, old_size); + pagecache_isize_extended(vi, old_size, offset); + } + + filemap_invalidate_unlock(vi->i_mapping); + if (!err) { - if (mode == 0 && NInoNonResident(ni) && - offset > old_size) { - truncate_pagecache(vi, old_size); - pagecache_isize_extended(vi, old_size, offset); - } NInoSetFileNameDirty(ni); inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi)); mark_inode_dirty(vi); diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c index 46a8b19c0723..580998990bc9 100644 --- a/fs/ntfs/index.c +++ b/fs/ntfs/index.c @@ -1660,7 +1660,7 @@ resplit: goto out; } } else { - si = kzalloc(sizeof(struct split_info), GFP_NOFS); + si = kzalloc_obj(struct split_info, GFP_NOFS); if (!si) { ntfs_ibm_clear(icx, new_vcn); ret = -ENOMEM; diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 32edb4045178..5aedc045f65a 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1852,7 +1852,7 @@ int ntfs_read_inode_mount(struct inode *vi) struct mft_record *m = NULL; struct attr_record *a; struct ntfs_attr_search_ctx *ctx; - unsigned int i, nr_blocks; + unsigned int i; int err; size_t new_rl_count; @@ -1896,11 +1896,6 @@ int ntfs_read_inode_mount(struct inode *vi) goto err_out; } - /* Determine the first block of the $MFT/$DATA attribute. */ - nr_blocks = ntfs_bytes_to_sector(vol, vol->mft_record_size); - if (!nr_blocks) - nr_blocks = 1; - /* Load $MFT/$DATA's first mft record. */ err = ntfs_bdev_read(sb->s_bdev, (char *)m, ntfs_cluster_to_bytes(vol, vol->mft_lcn), i); @@ -3780,8 +3775,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi, bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); bio->bi_iter.bi_sector = - ntfs_bytes_to_sector(vol, - ntfs_cluster_to_bytes(vol, lcn) + + ntfs_bytes_to_bio_sector(ntfs_cluster_to_bytes(vol, lcn) + lcn_folio_off); length = min_t(unsigned long, diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c index aa2e017a4384..0d6cd08ee2e7 100644 --- a/fs/ntfs/lcnalloc.c +++ b/fs/ntfs/lcnalloc.c @@ -53,10 +53,10 @@ int ntfs_cluster_free_from_rl_nolock(struct ntfs_volume *vol, if (rl->lcn < 0) continue; err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); - if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err)) - ret = err; - else + if (likely(!err)) nr_freed += rl->length; + else if (!ret || ret == -ENOMEM) + ret = err; } ntfs_inc_free_clusters(vol, nr_freed); ntfs_debug("Done."); @@ -1045,8 +1045,9 @@ err_out: "Failed to rollback (error %i). Leaving inconsistent metadata! Unmount and run chkdsk.", (int)delta); NVolSetErrors(vol); + } else { + ntfs_dec_free_clusters(vol, delta); } - ntfs_dec_free_clusters(vol, delta); up_write(&vol->lcnbmp_lock); memalloc_nofs_restore(memalloc_flags); ntfs_error(vol->sb, "Aborting (error %i).", err); diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index 024ddee42dc8..1404664dacc0 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c @@ -691,7 +691,7 @@ map_vcn: memset(empty_buf, 0xff, vol->cluster_size); - ra = kzalloc(sizeof(*ra), GFP_NOFS); + ra = kzalloc_obj(*ra, GFP_NOFS); if (!ra) goto err; diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index 984a0827f9ac..98ab686a5ea2 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -499,8 +499,8 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no, bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); bio->bi_iter.bi_sector = - NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) + - lcn_folio_off + folio_ofs); + ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) + + lcn_folio_off + folio_ofs); if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs)) err = submit_bio_wait(bio); @@ -580,7 +580,7 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn err = pre_write_mst_fixup((struct ntfs_record *)fixup_m, vol->mft_record_size); if (err) { ntfs_error(vol->sb, "Failed to apply mst fixups!"); - goto err_out; + goto unmap_err_out; } folio_size = vol->mft_record_size / ni->mft_lcn_count; @@ -592,8 +592,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); bio->bi_iter.bi_sector = - NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) + - clu_off); + ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) + + clu_off); if (!bio_add_folio(bio, folio, folio_size, ni->folio_ofs + offset)) { @@ -645,6 +645,8 @@ done: return 0; put_bio_out: bio_put(bio); +unmap_err_out: + kunmap_local(kaddr); err_out: /* * The caller should mark the base inode as bad so no more I/O @@ -2633,11 +2635,13 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w struct ntfs_inode *ni = NTFS_I(vi); struct ntfs_volume *vol = ni->vol; u8 *kaddr; - struct ntfs_inode **locked_nis __free(kfree) = kmalloc_array(PAGE_SIZE / NTFS_BLOCK_SIZE, - sizeof(struct ntfs_inode *), GFP_NOFS); + struct ntfs_inode **locked_nis __free(kfree) = kmalloc_objs(struct ntfs_inode *, + PAGE_SIZE / NTFS_BLOCK_SIZE, + GFP_NOFS); int nr_locked_nis = 0, err = 0, mft_ofs, prev_mft_ofs; - struct inode **ref_inos __free(kfree) = kmalloc_array(PAGE_SIZE / NTFS_BLOCK_SIZE, - sizeof(struct inode *), GFP_NOFS); + struct inode **ref_inos __free(kfree) = kmalloc_objs(struct inode *, + PAGE_SIZE / NTFS_BLOCK_SIZE, + GFP_NOFS); int nr_ref_inos = 0; struct bio *bio = NULL; u64 mft_no; @@ -2740,8 +2744,8 @@ flush_bio: bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); bio->bi_iter.bi_sector = - ntfs_bytes_to_sector(vol, - ntfs_cluster_to_bytes(vol, lcn) + off); + ntfs_bytes_to_bio_sector( + ntfs_cluster_to_bytes(vol, lcn) + off); } if (vol->cluster_size == NTFS_BLOCK_SIZE && diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h index df5a75d506f6..45f77848a9cf 100644 --- a/fs/ntfs/ntfs.h +++ b/fs/ntfs/ntfs.h @@ -19,6 +19,7 @@ #include <linux/nls.h> #include <linux/smp.h> #include <linux/pagemap.h> +#include <linux/blk_types.h> #include <linux/uidgid.h> #include "volume.h" @@ -71,8 +72,6 @@ #define NTFS_CLU_TO_POFS(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) & \ ~PAGE_MASK) -#define NTFS_B_TO_SECTOR(vol, b) ((b) >> ((vol)->sb)->s_blocksize_bits) - enum { NTFS_BLOCK_SIZE = 512, NTFS_BLOCK_SIZE_BITS = 9, @@ -154,11 +153,10 @@ static inline u64 ntfs_cluster_to_poff(const struct ntfs_volume *vol, return (clu << vol->cluster_size_bits) & ~PAGE_MASK; } -/* Convert byte offset to sector (block) number. */ -static inline sector_t ntfs_bytes_to_sector(const struct ntfs_volume *vol, - u64 bytes) +/* Convert a byte offset on the volume to a bio sector number. */ +static inline sector_t ntfs_bytes_to_bio_sector(u64 bytes) { - return bytes >> vol->sb->s_blocksize_bits; + return bytes >> SECTOR_SHIFT; } /* Global variables. */ diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c index 5e483a2f9060..1a6073e22677 100644 --- a/fs/ntfs/reparse.c +++ b/fs/ntfs/reparse.c @@ -405,7 +405,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr vi = ntfs_iget(vol->sb, mref); if (IS_ERR(vi)) - return PTR_ERR(vi); + return DT_UNKNOWN; reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi), AT_REPARSE_POINT, NULL, 0, &attr_size); @@ -694,8 +694,9 @@ static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context goto put_rp_inode; } - if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) && - oldsize > 0) { + err = set_reparse_index(ni, xr, + ((const struct reparse_point *)value)->reparse_tag); + if (err && oldsize > 0) { /* * If cannot index, try to remove the reparse * data and log the error. There will be an diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index 00373e450ea7..3a61f19bcbee 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -1804,7 +1804,7 @@ merge_src_rle: new_2nd_cnt = src_cnt; new_cnt = new_1st_cnt + new_2nd_cnt + new_3rd_cnt; new_cnt += dst_rl_split.lcn >= LCN_HOLE ? 1 : 0; - new_rl = kvcalloc(new_cnt, sizeof(*new_rl), GFP_NOFS); + new_rl = kvzalloc_objs(*new_rl, new_cnt, GFP_NOFS); if (!new_rl) return ERR_PTR(-ENOMEM); @@ -1888,13 +1888,13 @@ struct runlist_element *ntfs_rl_punch_hole(struct runlist_element *dst_rl, int d punch_cnt = (int)(e_rl - s_rl) + 1; - *punch_rl = kvcalloc(punch_cnt + 1, sizeof(struct runlist_element), - GFP_NOFS); + *punch_rl = kvzalloc_objs(struct runlist_element, punch_cnt + 1, + GFP_NOFS); if (!*punch_rl) return ERR_PTR(-ENOMEM); new_cnt = dst_cnt - (int)(e_rl - s_rl + 1) + 3; - new_rl = kvcalloc(new_cnt, sizeof(struct runlist_element), GFP_NOFS); + new_rl = kvzalloc_objs(struct runlist_element, new_cnt, GFP_NOFS); if (!new_rl) { kvfree(*punch_rl); *punch_rl = NULL; @@ -2038,13 +2038,13 @@ struct runlist_element *ntfs_rl_collapse_range(struct runlist_element *dst_rl, i one_split_3 = e_rl == s_rl && begin_split && end_split; punch_cnt = (int)(e_rl - s_rl) + 1; - *punch_rl = kvcalloc(punch_cnt + 1, sizeof(struct runlist_element), - GFP_NOFS); + *punch_rl = kvzalloc_objs(struct runlist_element, punch_cnt + 1, + GFP_NOFS); if (!*punch_rl) return ERR_PTR(-ENOMEM); new_cnt = dst_cnt - (int)(e_rl - s_rl + 1) + 3; - new_rl = kvcalloc(new_cnt, sizeof(struct runlist_element), GFP_NOFS); + new_rl = kvzalloc_objs(struct runlist_element, new_cnt, GFP_NOFS); if (!new_rl) { kvfree(*punch_rl); *punch_rl = NULL; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 30481e5d5dd4..5aad2d2a36bb 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -557,8 +557,8 @@ static bool is_boot_sector_ntfs(const struct super_block *sb, * Check sectors per cluster value is valid and the cluster size * is not above the maximum (2MB). */ - if (b->bpb.sectors_per_cluster > 0x80 && - b->bpb.sectors_per_cluster < 0xf4) + if (b->bpb.sectors_per_cluster < 0xf4 && + !is_power_of_2(b->bpb.sectors_per_cluster)) goto not_ntfs; /* Check reserved/unused fields are really zero. */ @@ -695,7 +695,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, * = -log2(mft_record_size) bytes. mft_record_size normaly is * 1024 bytes, which is encoded as 0xF6 (-10 in decimal). */ - vol->mft_record_size = 1 << -clusters_per_mft_record; + vol->mft_record_size = 1U << -clusters_per_mft_record; vol->mft_record_size_mask = vol->mft_record_size - 1; vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; ntfs_debug("vol->mft_record_size = %i (0x%x)", vol->mft_record_size, @@ -732,7 +732,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, * index_record_size normaly equals 4096 bytes, which is * encoded as 0xF4 (-12 in decimal). */ - vol->index_record_size = 1 << -clusters_per_index_record; + vol->index_record_size = 1U << -clusters_per_index_record; vol->index_record_size_mask = vol->index_record_size - 1; vol->index_record_size_bits = ffs(vol->index_record_size) - 1; ntfs_debug("vol->index_record_size = %i (0x%x)", @@ -1241,9 +1241,9 @@ static bool load_and_init_attrdef(struct ntfs_volume *vol) goto failed; } NInoSetSparseDisabled(NTFS_I(ino)); - /* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */ + /* FILE_AttrDef must hold at least one entry and fit inside 31 bits. */ i_size = i_size_read(ino); - if (i_size <= 0 || i_size > 0x7fffffff) + if (i_size < (s64)sizeof(struct attr_def) || i_size > 0x7fffffff) goto iput_failed; vol->attrdef = kvzalloc(i_size, GFP_NOFS); if (!vol->attrdef) @@ -1862,7 +1862,8 @@ static int ntfs_sync_fs(struct super_block *sb, int wait) return 0; /* If there are some dirty buffers in the bdev inode */ - if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) { + if (!NVolErrors(vol) && + ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) { ntfs_warning(sb, "Failed to clear dirty bit in volume information flags. Run chkdsk."); err = -EIO; } @@ -2538,7 +2539,7 @@ static int ntfs_init_fs_context(struct fs_context *fc) struct ntfs_volume *vol; /* Allocate a new struct ntfs_volume and place it in sb->s_fs_info. */ - vol = kmalloc(sizeof(struct ntfs_volume), GFP_NOFS); + vol = kmalloc_obj(struct ntfs_volume, GFP_NOFS); if (!vol) return -ENOMEM; diff --git a/fs/ntfs/wof.c b/fs/ntfs/wof.c index 8f84c2212eee..9847259e5b1a 100644 --- a/fs/ntfs/wof.c +++ b/fs/ntfs/wof.c @@ -39,8 +39,6 @@ struct ntfs_wof_workspace { struct mutex *lock; const struct ntfs_codec_ops *codec; u32 comp_unit; - void *input; - size_t input_size; void *output; void *scratch; }; @@ -97,30 +95,36 @@ static struct ntfs_wof_workspace *ntfs_wof_workspace(u8 block_size_bits) } } +/* + * Size of the buffer a chunk is read into. A chunk is read straight off the + * device, so the buffer has to hold @comp_unit bytes plus the leading partial + * sector. + */ +static size_t ntfs_wof_input_size(const struct ntfs_wof_workspace *ws) +{ + return round_up((size_t)ws->comp_unit + 511, 512); +} + static int ntfs_wof_workspace_prepare(struct ntfs_wof_workspace *ws) { - void *input, *output, *scratch; + void *output, *scratch; size_t scratch_size; - if (ws->input) + if (ws->output) return 0; - ws->input_size = round_up((size_t)ws->comp_unit + 511, 512); scratch_size = ws->codec->scratch_size(ws->comp_unit); if (!scratch_size) return -EINVAL; - input = kvmalloc(ws->input_size, GFP_NOFS); output = kvmalloc(ws->comp_unit, GFP_NOFS); scratch = kvzalloc(scratch_size, GFP_NOFS); - if (!input || !output || !scratch) { - kvfree(input); + if (!output || !scratch) { kvfree(output); kvfree(scratch); return -ENOMEM; } - ws->input = input; ws->output = output; ws->scratch = scratch; return 0; @@ -134,10 +138,8 @@ void ntfs_wof_free_workspaces(void) struct ntfs_wof_workspace *ws = ntfs_wof_workspaces[i]; mutex_lock(ws->lock); - kvfree(ws->input); kvfree(ws->output); kvfree(ws->scratch); - ws->input = NULL; ws->output = NULL; ws->scratch = NULL; mutex_unlock(ws->lock); @@ -602,6 +604,51 @@ static int ntfs_wof_try_direct(struct ntfs_wof_workspace *ws, chunk_end, src, src_len, dst_len); } +/* + * Decompress one chunk into @folio. Only this step needs the workspace, so it + * is the only step that takes the workspace lock. + */ +static int ntfs_wof_decompress_chunk(struct ntfs_wof_workspace *ws, + struct ntfs_volume *vol, + struct address_space *mapping, + struct folio *folio, loff_t folio_start, + loff_t folio_end, u64 chunk_file_offset, + char *chunk_mem, u32 chunk_size, + u32 decomp_size) +{ + loff_t chunk_end = chunk_file_offset + decomp_size; + loff_t copy_start, copy_end; + int err; + + mutex_lock(ws->lock); + err = ntfs_wof_workspace_prepare(ws); + if (err) + goto out_unlock; + + err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset, + chunk_end, chunk_mem, chunk_size, + decomp_size); + if (err != -EAGAIN) + goto out_unlock; + + err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output, + decomp_size); + if (err) { + ntfs_error(vol->sb, "Decompression failed: %d", err); + err = -EINVAL; + goto out_unlock; + } + + copy_start = max_t(loff_t, folio_start, chunk_file_offset); + copy_end = min_t(loff_t, folio_end, chunk_file_offset + decomp_size); + memcpy_to_folio(folio, copy_start - folio_start, + ws->output + copy_start - chunk_file_offset, + copy_end - copy_start); +out_unlock: + mutex_unlock(ws->lock); + return err; +} + int ntfs_read_wof_compressed_block(struct folio *folio) { struct address_space *mapping = folio->mapping; @@ -613,6 +660,8 @@ int ntfs_read_wof_compressed_block(struct folio *folio) loff_t folio_start = folio_pos(folio); loff_t folio_end = folio_next_pos(folio); char *chunk_mem; + void *input; + size_t input_size; u32 decomp_size; u64 chunk_count, chunk_idx, last_chunk, chunk_offset; int err = 0; @@ -652,10 +701,12 @@ int ntfs_read_wof_compressed_block(struct folio *folio) goto out_iput; } - mutex_lock(ws->lock); - err = ntfs_wof_workspace_prepare(ws); - if (err) - goto out_unlock_ws; + input_size = ntfs_wof_input_size(ws); + input = kvmalloc(input_size, GFP_NOFS); + if (!input) { + err = -ENOMEM; + goto out_iput; + } chunk_idx = div_u64(folio_start, ws->comp_unit); last_chunk = @@ -663,55 +714,35 @@ int ntfs_read_wof_compressed_block(struct folio *folio) chunk_count = DIV_ROUND_UP_ULL(i_size, ws->comp_unit); for (; chunk_idx <= last_chunk; chunk_idx++) { u32 chunk_size; - u64 chunk_file_offset; - loff_t chunk_end, copy_start, copy_end; decomp_size = chunk_idx + 1 == chunk_count ? i_size - chunk_idx * ws->comp_unit : ws->comp_unit; err = parse_wof_chunk_table(ni, wof_ni, chunk_idx, chunk_count, decomp_size, &chunk_offset, - &chunk_size, ws->input, - ws->input_size); + &chunk_size, input, input_size); if (err) - goto out_unlock_ws; + goto out_free_input; err = ntfs_read_wof_chunk(vol, wof_ni, chunk_offset, chunk_size, - ws->input, ws->input_size, - &chunk_mem); + input, input_size, &chunk_mem); if (err) - goto out_unlock_ws; - - chunk_file_offset = chunk_idx * ws->comp_unit; - chunk_end = chunk_file_offset + decomp_size; - err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset, - chunk_end, chunk_mem, chunk_size, - decomp_size); - if (!err) - continue; - if (err != -EAGAIN) - goto out_unlock_ws; + goto out_free_input; - err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output, - decomp_size); - if (err) { - ntfs_error(vol->sb, "Decompression failed: %d", err); - err = -EINVAL; - goto out_unlock_ws; - } - copy_start = max_t(loff_t, folio_start, chunk_file_offset); - copy_end = min_t(loff_t, folio_end, - chunk_file_offset + decomp_size); - memcpy_to_folio(folio, copy_start - folio_start, - ws->output + copy_start - chunk_file_offset, - copy_end - copy_start); + err = ntfs_wof_decompress_chunk(ws, vol, mapping, folio, + folio_start, folio_end, + chunk_idx * ws->comp_unit, + chunk_mem, chunk_size, + decomp_size); + if (err) + goto out_free_input; } if (folio_end > i_size) folio_zero_segment(folio, i_size - folio_start, folio_size(folio)); -out_unlock_ws: - mutex_unlock(ws->lock); +out_free_input: + kvfree(input); out_iput: iput(wof_inode); out: diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index e7fe29cb6028..7d6f7f6022eb 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -1044,7 +1044,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file) struct ovl_dir_file *od; enum ovl_path_type type; - od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL); + od = kzalloc_obj(struct ovl_dir_file); if (!od) return -ENOMEM; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e487597337e8..bd0a3f9039d2 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1543,7 +1543,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) struct ovl_fs *ofs = sb->s_fs_info; int err; - err = -EIO; + err = -EINVAL; /* The fscontext fd may have been passed to another user namespace. */ if (fc->user_ns != current_user_ns()) goto out_err; diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 204afc5e984b..1c78c695d0dd 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1240,7 +1240,7 @@ static int ignore_hardlimit(struct dquot *dquot) { struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type]; - return capable(CAP_SYS_RESOURCE) && + return capable_noaudit(CAP_SYS_RESOURCE) && (info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & DQF_ROOT_SQUASH)); } diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c index fe10719e627e..c49ecddf4a33 100644 --- a/fs/smb/client/cifs_swn.c +++ b/fs/smb/client/cifs_swn.c @@ -443,7 +443,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) goto unlock; } - reg = kmalloc_obj(struct cifs_swn_reg, GFP_KERNEL); + reg = kmalloc_obj(struct cifs_swn_reg); if (reg == NULL) { ret = -ENOMEM; goto fail_unlock; diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 12005f46307d..c5e47a835f99 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -100,8 +100,23 @@ cifs_idmap_key_destroy(struct key *key) kfree(key->payload.data[0]); } +static int +cifs_idmap_key_vet_description(const char *description) +{ + /* + * cifs.idmap descriptions are authority-bearing inputs to the + * cifs.idmap upcall helper. Only allow the kernel to create this + * type of key using the private root_cred installed in + * init_cifs_idmap; reject userspace request_key(2)/add_key(2). + */ + if (current_cred() != root_cred) + return -EPERM; + return 0; +} + static struct key_type cifs_idmap_key_type = { .name = "cifs.idmap", + .vet_description = cifs_idmap_key_vet_description, .instantiate = cifs_idmap_key_instantiate, .destroy = cifs_idmap_key_destroy, .describe = user_describe, @@ -1081,13 +1096,13 @@ unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace) static void populate_new_aces(char *nacl_base, struct smb_sid *pownersid, struct smb_sid *pgrpsid, - __u64 *pnmode, u16 *pnum_aces, u16 *pnsize, + __u64 *pnmode, u16 *pnum_aces, u32 *pnsize, bool modefromsid, bool posix) { __u64 nmode; u16 num_aces = 0; - u16 nsize = 0; + u32 nsize = 0; __u64 user_mode; __u64 group_mode; __u64 other_mode; @@ -1186,17 +1201,17 @@ set_size: *pnsize = nsize; } -static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl, - struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_sid *pnownersid, struct smb_sid *pngrpsid, - int *aclflag) +static int replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_sid *pnownersid, struct smb_sid *pngrpsid, + int *aclflag, u16 *pnsize) { int i; u16 size = 0; struct smb_ace *pntace = NULL; char *acl_base = NULL; u16 src_num_aces = 0; - u16 nsize = 0; + u32 nsize = 0; struct smb_ace *pnntace = NULL; char *nacl_base = NULL; u16 ace_size = 0; @@ -1225,9 +1240,12 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p size += le16_to_cpu(pntace->size); nsize += ace_size; + if (nsize > U16_MAX) + return -EOVERFLOW; } - return nsize; + *pnsize = nsize; + return 0; } static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, @@ -1239,7 +1257,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, struct smb_ace *pntace = NULL; char *acl_base = NULL; u16 src_num_aces = 0; - u16 nsize = 0; + u32 nsize = 0; struct smb_ace *pnntace = NULL; char *nacl_base = NULL; u16 num_aces = 0; @@ -1290,6 +1308,8 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl, nsize += cifs_copy_ace(pnntace, pntace, NULL); num_aces++; + if (nsize > U16_MAX) + return -EOVERFLOW; next_ace: size += le16_to_cpu(pntace->size); @@ -1306,6 +1326,10 @@ next_ace: } finalize_dacl: + /* The DACL size field is 16-bit on the wire, see MS-DTYP 2.4.5 */ + if (nsize > U16_MAX) + return -EOVERFLOW; + pndacl->num_aces = cpu_to_le16(num_aces); pndacl->size = cpu_to_le16(nsize); @@ -1331,6 +1355,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, { int rc = 0; struct smb_sid *owner_sid_ptr, *group_sid_ptr; + unsigned int sbflags = cifs_sb_flags(cifs_sb); struct smb_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl; __u32 dacloffset, osidoffset, gsidoffset; @@ -1349,17 +1374,21 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", pntsd->revision, pntsd->type, osidoffset, gsidoffset, le32_to_cpu(pntsd->sacloffset), dacloffset); -/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + rc = sid_from_sd(pntsd, acl_len, osidoffset, &owner_sid_ptr); if (rc) { cifs_dbg(FYI, "%s: Error %d parsing Owner SID\n", __func__, rc); return rc; } - rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", - __func__, rc); - return rc; + if (!(sbflags & CIFS_MOUNT_OVERR_UID)) { + rc = sid_to_id(cifs_sb, owner_sid_ptr, fattr, SIDOWNER); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + return rc; + } } rc = sid_from_sd(pntsd, acl_len, gsidoffset, &group_sid_ptr); @@ -1368,11 +1397,13 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, __func__, rc); return rc; } - rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); - if (rc) { - cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", - __func__, rc); - return rc; + if (!(sbflags & CIFS_MOUNT_OVERR_GID)) { + rc = sid_to_id(cifs_sb, group_sid_ptr, fattr, SIDGROUP); + if (rc) { + cifs_dbg(FYI, "%s: Error %d mapping Group SID to gid\n", + __func__, rc); + return rc; + } } if (dacloffset) { @@ -1451,6 +1482,8 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, pnmode, mode_from_sid, posix); + if (rc) + return rc; sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); /* copy the non-dacl portion of secdesc */ @@ -1526,10 +1559,12 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, if (dacloffset) { /* Replace ACEs for old owner with new one */ - size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, - owner_sid_ptr, group_sid_ptr, - nowner_sid_ptr, ngroup_sid_ptr, - aclflag); + rc = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, + owner_sid_ptr, group_sid_ptr, + nowner_sid_ptr, ngroup_sid_ptr, + aclflag, &size); + if (rc) + goto chown_chgrp_exit; ndacl_ptr->size = cpu_to_le16(size); } @@ -1815,11 +1850,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, cifs_put_tlink(tlink); return rc; } - if (mode_from_sid) - nsecdesclen += - le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); - else /* cifsacl */ - nsecdesclen += le16_to_cpu(dacl_ptr->size); + /* + * Worst case: every ACE is rewritten with a new SID of + * SID_MAX_SUB_AUTHORITIES sub-auths -> sizeof(smb_ace) each, + * plus the smb_acl header replace_sids_and_copy_aces() emits. + */ + nsecdesclen += sizeof(struct smb_acl) + + le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); } } diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index f5aad5f61dce..f9aff0712794 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -1719,8 +1719,17 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMBr = (READ_RSP *)rsp_iov.iov_base; if (rc) { cifs_dbg(VFS, "Send error in read = %d\n", rc); + } else if (rsp_iov.iov_len < tcon->ses->server->vals->read_rsp_size) { + /* check that the received response can hold a whole READ_RSP */ + cifs_dbg(FYI, "%s: server returned short header. got=%zu expected=%zu\n", + __func__, rsp_iov.iov_len, + tcon->ses->server->vals->read_rsp_size); + rc = smb_EIO2(smb_eio_trace_read_rsp_short, + rsp_iov.iov_len, tcon->ses->server->vals->read_rsp_size); + *nbytes = 0; } else { - int data_length = le16_to_cpu(pSMBr->DataLengthHigh); + unsigned int data_length = le16_to_cpu(pSMBr->DataLengthHigh); + __u16 data_offset = le16_to_cpu(pSMBr->DataOffset); data_length = data_length << 16; data_length += le16_to_cpu(pSMBr->DataLength); *nbytes = data_length; @@ -1728,14 +1737,21 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, /*check that DataLength would not go beyond end of SMB */ if ((data_length > CIFSMaxBufSize) || (data_length > count)) { - cifs_dbg(FYI, "bad length %d for count %d\n", - data_length, count); + cifs_dbg(FYI, "%s: bad length %u for count %u\n", + __func__, data_length, count); rc = smb_EIO2(smb_eio_trace_read_overlarge, data_length, count); *nbytes = 0; + } else if (data_offset < sizeof(*pSMBr) || + (size_t)data_offset + data_length > rsp_iov.iov_len) { + /* check that the data lies within the received response */ + cifs_dbg(FYI, "%s: bad data offset %u length %u for response of %zu\n", + __func__, data_offset, data_length, rsp_iov.iov_len); + rc = smb_EIO2(smb_eio_trace_read_bad_offset, + data_offset, data_length); + *nbytes = 0; } else { - pReadData = (char *) (&pSMBr->hdr.Protocol) + - le16_to_cpu(pSMBr->DataOffset); + pReadData = (char *) (&pSMBr->hdr.Protocol) + data_offset; /* if (rc = copy_to_user(buf, pReadData, data_length)) { cifs_dbg(VFS, "Faulting on read rc = %d\n",rc); rc = -EFAULT; @@ -3555,6 +3571,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; int bytes_returned = 0; __u16 params, byte_count, data_count, param_offset, offset; + size_t cifs_acl_size, bytes_available; cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName); setAclRetry: @@ -3574,8 +3591,7 @@ setAclRetry: } params = 6 + name_len; pSMB->MaxParameterCount = cpu_to_le16(2); - /* BB find max SMB size from sess */ - pSMB->MaxDataCount = cpu_to_le16(1000); + pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX)); pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; pSMB->Flags = 0; @@ -3587,6 +3603,15 @@ setAclRetry: parm_data = ((char *)pSMB) + offset; pSMB->ParameterOffset = cpu_to_le16(param_offset); + /* make sure we can fit the larger cifs_posix_aces in the buffer */ + cifs_acl_size = sizeof(struct cifs_posix_acl) + + (acl->a_count * sizeof(struct cifs_posix_ace)); + bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset; + if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) { + rc = -E2BIG; + goto setACLerrorExit; + } + /* convert to on the wire format for POSIX ACL */ data_count = posix_acl_to_cifs(parm_data, acl, acl_type); @@ -6325,8 +6350,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon, int name_len; int rc = 0; int bytes_returned = 0; - __u16 params, param_offset, byte_count, offset, count; + __u16 params, param_offset; + unsigned int byte_count, offset, count; int remap = cifs_remap(cifs_sb); + unsigned int total_len; cifs_dbg(FYI, "In SetEA\n"); SetEARetry: @@ -6378,6 +6405,13 @@ SetEARetry: pSMB->Reserved3 = 0; pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); byte_count = 3 /* pad */ + params + count; + if (check_add_overflow(in_len, byte_count, &total_len) || + byte_count > U16_MAX || + total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len); + cifs_buf_release(pSMB); + return -E2BIG; + } pSMB->DataCount = cpu_to_le16(count); parm_data->list_len = cpu_to_le32(count); parm_data->list.EA_flags = 0; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index bcd7f1ae99ba..b6e98eb31673 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -4189,14 +4189,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, return rc; } -static int -cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) +static int set_fs_context_auth(struct smb3_fs_context *ctx, + struct cifs_ses *ses) { ctx->sectype = ses->sectype; - /* krb5 is special, since we don't need username or pw */ - if (ctx->sectype == Kerberos) + /* + * krb5 is special as we might need to pass username (passwordless) down + * to cifs.upcall(8) for keytab. + */ + if (ctx->sectype == Kerberos) { + if (ses->user_name && ses->user_name[0]) { + ctx->username = kstrndup(ses->user_name, + CIFS_MAX_USERNAME_LEN, + GFP_KERNEL); + if (!ctx->username) + return -ENOMEM; + } return 0; + } return cifs_set_cifscreds(ctx, ses); } @@ -4236,7 +4247,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses; ctx->unicode = master_tcon->ses->unicode; - rc = cifs_set_vol_auth(ctx, master_tcon->ses); + rc = set_fs_context_auth(ctx, master_tcon->ses); if (rc) { tcon = ERR_PTR(rc); goto out; diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 86dba25b7a5a..29dfd7595941 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -123,6 +123,7 @@ static inline void free_tgts(struct cache_entry *ce) kfree(t); } + ce->numtgts = 0; WRITE_ONCE(ce->tgthint, NULL); } @@ -365,7 +366,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) { struct cache_dfs_tgt *t; - t = kmalloc_obj(*t, GFP_KERNEL); + t = kmalloc_obj(*t); if (!t) return ERR_PTR(-ENOMEM); t->name = kstrdup(name, GFP_KERNEL); @@ -388,13 +389,6 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, struct cache_dfs_tgt *target; int i; - ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); - ce->etime = get_expire_time(ce->ttl); - ce->srvtype = refs[0].server_type; - ce->hdr_flags = refs[0].flags; - ce->ref_flags = refs[0].ref_flag; - ce->path_consumed = refs[0].path_consumed; - for (i = 0; i < numrefs; i++) { struct cache_dfs_tgt *t; @@ -409,12 +403,19 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, } else { list_add_tail(&t->list, &ce->tlist); } - ce->numtgts++; } target = list_first_entry_or_null(&ce->tlist, struct cache_dfs_tgt, list); + WRITE_ONCE(ce->tgthint, target); + ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); + ce->etime = get_expire_time(ce->ttl); + ce->srvtype = refs[0].server_type; + ce->hdr_flags = refs[0].flags; + ce->ref_flags = refs[0].ref_flag; + ce->path_consumed = refs[0].path_consumed; + ce->numtgts = numrefs; return 0; } @@ -634,7 +635,6 @@ static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_in } free_tgts(ce); - ce->numtgts = 0; rc = copy_ref_data(refs, numrefs, ce, th); diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index bdcd54157e6c..1aa4844f8b8a 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -999,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry) struct cifs_tcon *tcon; int rc; - rc = filemap_write_and_wait(inode->i_mapping); - if (is_interrupt_error(rc)) + rc = inode_lock_killable(inode); + if (rc) return -ERESTARTSYS; + + filemap_invalidate_lock(inode->i_mapping); + + rc = filemap_write_and_wait(inode->i_mapping); + if (is_interrupt_error(rc)) { + rc = -ERESTARTSYS; + goto out; + } mapping_set_error(inode->i_mapping, rc); cfile = find_writable_file(cinode, FIND_FSUID_ONLY); rc = cifs_file_flush(xid, inode, cfile); if (!rc) { if (cfile) { + struct netfs_inode *ictx = netfs_inode(inode); + tcon = tlink_tcon(cfile->tlink); server = tcon->ses->server; + netfs_wb_begin(ictx, false); rc = server->ops->set_file_size(xid, tcon, cfile, 0, false); - } - if (!rc) { - netfs_resize_file(&cinode->netfs, 0, true); - cifs_setsize(inode, 0); + if (!rc) { + netfs_resize_file(&cinode->netfs, 0, true); + cifs_setsize(inode, 0); + cifs_invalidate_cache(inode, 0); + } + netfs_wb_end(ictx); + } else { + /* + * No cached handle; evict stale pages so they can't + * be served after the file is later extended; let + * the server's O_TRUNC open response set the i_size + */ + truncate_inode_pages(inode->i_mapping, 0); cifs_invalidate_cache(inode, 0); } } + +out: + filemap_invalidate_unlock(inode->i_mapping); + inode_unlock(inode); if (cfile) cifsFileInfo_put(cfile); return rc; @@ -1491,11 +1515,18 @@ int cifs_close(struct inode *inode, struct file *file) trace_smb3_close_cached(tcon->tid, tcon->ses->Suid, cfile->fid.persistent_fid, cifs_sb->ctx->closetimeo); - queue_delayed_work(deferredclose_wq, - &cfile->deferred, cifs_sb->ctx->closetimeo); - cfile->deferred_close_scheduled = true; - spin_unlock(&cinode->deferred_lock); - return 0; + /* + * Each queued execution owns one reference. + * If nothing was queued, the reference of + * the closing file is dropped below. + */ + if (queue_delayed_work(deferredclose_wq, + &cfile->deferred, + cifs_sb->ctx->closetimeo)) { + cfile->deferred_close_scheduled = true; + spin_unlock(&cinode->deferred_lock); + return 0; + } } spin_unlock(&cinode->deferred_lock); _cifsFileInfo_put(cfile, true, false); @@ -3324,8 +3355,11 @@ void cifs_oplock_break(struct work_struct *work) TASK_UNINTERRUPTIBLE); tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) + if (IS_ERR(tlink)) { + /* drop the reference taken when the break was queued */ + _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); goto out; + } tcon = tlink_tcon(tlink); server = tcon->ses->server; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 12ed8db10e00..1fe0ef0a95db 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -851,6 +851,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *info = &data->posix_fi; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + unsigned int sbflags = cifs_sb_flags(cifs_sb); memset(fattr, 0, sizeof(*fattr)); @@ -895,8 +896,12 @@ out_reparse: fattr->cf_symlink_target = data->symlink_target; data->symlink_target = NULL; } - sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER); - sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP); + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + if (!(sbflags & CIFS_MOUNT_OVERR_UID)) + sid_to_id(cifs_sb, &data->posix_owner, fattr, SIDOWNER); + if (!(sbflags & CIFS_MOUNT_OVERR_GID)) + sid_to_id(cifs_sb, &data->posix_group, fattr, SIDGROUP); cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); @@ -2992,14 +2997,14 @@ int cifs_getattr(struct mnt_idmap *idmap, const struct path *path, stat->attributes |= STATX_ATTR_ENCRYPTED; /* - * If on a multiuser mount without unix extensions or cifsacl being - * enabled, and the admin hasn't overridden them, set the ownership - * to the fsuid/fsgid of the current process. + * If on a multiuser mount without unix extensions, posix extensions + * or cifsacl being enabled, and the admin hasn't overridden them, + * set the ownership to the fsuid/fsgid of the current process. */ sbflags = cifs_sb_flags(cifs_sb); if ((sbflags & CIFS_MOUNT_MULTIUSER) && !(sbflags & CIFS_MOUNT_CIFS_ACL) && - !tcon->unix_ext) { + !tcon->unix_ext && !tcon->posix_extensions) { if (!(sbflags & CIFS_MOUNT_OVERR_UID)) stat->uid = current_fsuid(); if (!(sbflags & CIFS_MOUNT_OVERR_GID)) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 46e1382e8e04..945194fe7a97 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -378,10 +378,11 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile) * open_file_lock to enforce the validity of it for the oplock * break handler. The matching put is done at the end of the * handler. + * + * Only take a reference if the work is actually queued. */ - cifsFileInfo_get(cfile); - - queue_work(cifsoplockd_wq, &cfile->oplock_break); + if (queue_work(cifsoplockd_wq, &cfile->oplock_break)) + cifsFileInfo_get(cfile); } void cifs_done_oplock_break(struct cifsInodeInfo *cinode) @@ -891,8 +892,14 @@ static void tcon_super_cb(struct super_block *sb, void *arg) t1->ses->dfs_root_ses == t2->ses->dfs_root_ses) && t1->ses->server == t2->ses->server && t2->origin_fullpath && - dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) + dfs_src_pathname_equal(t2->origin_fullpath, t1->origin_fullpath)) { + /* + * Take the active reference while iterate_supers_type() still + * holds s_umount shared. + */ + cifs_sb_active(sb); sd->sb = sb; + } spin_unlock(&t2->tc_lock); } @@ -909,15 +916,8 @@ static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void for (; *fs_type; fs_type++) { iterate_supers_type(*fs_type, f, &sd); - if (sd.sb) { - /* - * Grab an active reference in order to prevent automounts (DFS links) - * of expiring and then freeing up our cifs superblock pointer while - * we're doing failover. - */ - cifs_sb_active(sd.sb); + if (sd.sb) return sd.sb; - } } pr_warn_once("%s: could not find dfs superblock\n", __func__); return ERR_PTR(-EINVAL); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 32a75afca8f5..9530e5b01564 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -242,9 +242,11 @@ static void cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, struct cifs_sb_info *cifs_sb) { + unsigned int sbflags = cifs_sb_flags(cifs_sb); struct smb2_posix_info_parsed parsed; + int rc; - posix_info_parse(info, NULL, &parsed); + rc = posix_info_parse(info, NULL, &parsed); memset(fattr, 0, sizeof(*fattr)); fattr->cf_uniqueid = le64_to_cpu(info->Inode); @@ -281,8 +283,17 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, le32_to_cpu(info->ReparseTag), le32_to_cpu(info->Mode)); - sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); - sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + if (rc < 0) { + cifs_dbg(VFS, "%s: failed to parse SIDs: %d\n", + __func__, rc); + } else { + if (!(sbflags & CIFS_MOUNT_OVERR_UID)) + sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); + if (!(sbflags & CIFS_MOUNT_OVERR_GID)) + sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); + } } static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index 5cc5b0410d48..8a1b9e8be5ba 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -971,7 +971,8 @@ globalroot: linux_target[i*3 + 1] = '.'; linux_target[i*3 + 2] = sep; } - memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */ + /* +1 to skip leading sep */ + memcpy(linux_target + levels*3, smb_target+1, smb_target_len-1); } else { /* * This is either an absolute symlink in POSIX-style format @@ -1137,10 +1138,15 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data, struct cifs_sb_info *cifs_sb, u32 tag, struct cifs_fattr *fattr) { + unsigned int sbflags = cifs_sb_flags(cifs_sb); struct smb2_file_full_ea_info *ea; bool have_xattr_dev = false; u32 next = 0; + fattr->cf_uid = cifs_sb->ctx->linux_uid; + fattr->cf_gid = cifs_sb->ctx->linux_gid; + + fattr->cf_mode &= ~S_IFMT; switch (tag) { case IO_REPARSE_TAG_LX_SYMLINK: fattr->cf_mode |= S_IFLNK; @@ -1177,11 +1183,13 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data, nlen = ea->ea_name_length; v = (void *)((u8 *)ea->ea_data + ea->ea_name_length + 1); - if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen)) - fattr->cf_uid = wsl_make_kuid(cifs_sb, v); - else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen)) - fattr->cf_gid = wsl_make_kgid(cifs_sb, v); - else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) { + if (!strncmp(name, SMB2_WSL_XATTR_UID, nlen)) { + if (!(sbflags & CIFS_MOUNT_OVERR_UID)) + fattr->cf_uid = wsl_make_kuid(cifs_sb, v); + } else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen)) { + if (!(sbflags & CIFS_MOUNT_OVERR_GID)) + fattr->cf_gid = wsl_make_kgid(cifs_sb, v); + } else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) { /* File type in reparse point tag and in xattr mode must match. */ if (S_DT(fattr->cf_mode) != S_DT(le32_to_cpu(*(__le32 *)v))) return false; @@ -1205,6 +1213,7 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_open_info_data *data) { struct reparse_nfs_data_buffer *buf = (struct reparse_nfs_data_buffer *)data->reparse.buf; + umode_t ftype; if (buf == NULL) return true; @@ -1220,7 +1229,7 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb, WARN_ON_ONCE(1); return false; } - fattr->cf_mode |= S_IFCHR; + ftype = S_IFCHR; fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); break; case NFS_SPECFILE_BLK: @@ -1228,22 +1237,23 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb, WARN_ON_ONCE(1); return false; } - fattr->cf_mode |= S_IFBLK; + ftype = S_IFBLK; fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); break; case NFS_SPECFILE_FIFO: - fattr->cf_mode |= S_IFIFO; + ftype = S_IFIFO; break; case NFS_SPECFILE_SOCK: - fattr->cf_mode |= S_IFSOCK; + ftype = S_IFSOCK; break; case NFS_SPECFILE_LNK: - fattr->cf_mode |= S_IFLNK; + ftype = S_IFLNK; break; default: WARN_ON_ONCE(1); return false; } + fattr->cf_mode = (fattr->cf_mode & ~S_IFMT) | ftype; return true; } @@ -1271,6 +1281,7 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, break; case 0: /* SMB1 symlink */ case IO_REPARSE_TAG_SYMLINK: + fattr->cf_mode &= ~S_IFMT; fattr->cf_mode |= S_IFLNK; break; default: diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 98ea5c6c34af..96063e355186 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -237,7 +237,7 @@ replay_again: num_rqst = 0; server = cifs_pick_channel(ses); - vars = kzalloc_obj(*vars, GFP_KERNEL); + vars = kzalloc_obj(*vars); if (vars == NULL) { rc = -ENOMEM; goto out; diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 7d6738ffcb80..cb4fd09f996e 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -1839,31 +1839,31 @@ free_vars: * * @tcon: destination file tcon * @bytes_left: how many bytes are left to copy + * @chunk_size: maximum size of a single chunk * * Return: maximum number of chunks with which Chunks[] can be filled. */ static inline u32 -calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left) +calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size) { u32 max_chunks = READ_ONCE(tcon->max_chunks); u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy); - u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk); u64 need; u32 allowed; - if (!max_bytes_chunk || !max_bytes_copy || !max_chunks) + if (!chunk_size || !max_bytes_copy || !max_chunks) return 0; /* chunks needed for the remaining bytes */ - need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk); + need = DIV_ROUND_UP_ULL(bytes_left, chunk_size); /* chunks allowed per cc request */ - allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk); + allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size); return (u32)umin(need, umin(max_chunks, allowed)); } /** - * smb2_copychunk_range - server-side copy of data range + * __smb2_copychunk_range - server-side copy of data range * * @xid: transaction id * @src_file: source file @@ -1875,15 +1875,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left) * Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE * IOCTLs, splitting the request into chunks limited by tcon->max_*. * - * Return: @len on success; negative errno on failure. + * Return: 0 on success; negative errno on failure. */ -static ssize_t -smb2_copychunk_range(const unsigned int xid, - struct cifsFileInfo *src_file, - struct cifsFileInfo *dst_file, - u64 src_off, - u64 len, - u64 dst_off) +static int +__smb2_copychunk_range(const unsigned int xid, + struct cifsFileInfo *src_file, + struct cifsFileInfo *dst_file, + u64 src_off, + u64 len, + u64 dst_off) { int rc = 0; unsigned int ret_data_len = 0; @@ -1891,12 +1891,14 @@ smb2_copychunk_range(const unsigned int xid, struct copychunk_ioctl_rsp *cc_rsp = NULL; struct cifs_tcon *tcon; struct srv_copychunk *chunk; - u32 chunks, chunk_count, chunk_bytes; + u32 chunks, chunk_count, chunk_bytes, chunk_size; u32 copy_bytes, copy_bytes_left; u32 chunks_written, bytes_written; u64 total_bytes_left = len; u64 src_off_prev, dst_off_prev; + u64 max_chunk = 0; u32 retries = 0; + bool reverse = false; tcon = tlink_tcon(dst_file->tlink); @@ -1904,8 +1906,50 @@ smb2_copychunk_range(const unsigned int xid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); + /* + * Same-file left shifts are safe in forward order. For a right shift, + * let L be the copy length, delta the distance between the source and + * destination, and C the normal chunk size: + * + * delta >= L: copy forwards using C + * delta < L: + * delta >= C: copy backwards using C + * delta < C: copy backwards with chunks limited to delta + * + * Copying backwards prevents one chunk from overwriting data needed by + * a later chunk. Limiting the chunk size to delta prevents an individual + * chunk from overlapping itself. + * This limit can be removed once all supported servers handle overlapping + * descriptors safely. + * + * A small right shift over a large range may therefore require many + * chunks. + */ + if (src_file == dst_file && dst_off > src_off) { + u64 delta = dst_off - src_off; + + if (delta < len) { + reverse = true; + max_chunk = delta; + } + } + + /* + * A backward copy walks the offsets down from the end of the range. + * Do this once, outside the retry loop, so a retry does not move the + * offsets again. + */ + if (reverse) { + src_off += len; + dst_off += len; + } + retry: - chunk_count = calc_chunk_count(tcon, total_bytes_left); + chunk_size = READ_ONCE(tcon->max_bytes_chunk); + if (max_chunk && max_chunk < chunk_size) + chunk_size = (u32)max_chunk; + + chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size); if (!chunk_count) { rc = -EOPNOTSUPP; goto out; @@ -1946,16 +1990,21 @@ retry: while (copy_bytes_left > 0 && chunks < chunk_count) { chunk = &cc_req->Chunks[chunks++]; + chunk_bytes = umin(copy_bytes_left, chunk_size); + if (reverse) { + src_off -= chunk_bytes; + dst_off -= chunk_bytes; + } + chunk->SourceOffset = cpu_to_le64(src_off); chunk->TargetOffset = cpu_to_le64(dst_off); - - chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk); - chunk->Length = cpu_to_le32(chunk_bytes); /* Buffer is zeroed, no need to set chunk->Reserved = 0 */ - src_off += chunk_bytes; - dst_off += chunk_bytes; + if (!reverse) { + src_off += chunk_bytes; + dst_off += chunk_bytes; + } copy_bytes_left -= chunk_bytes; copy_bytes += chunk_bytes; @@ -2003,6 +2052,18 @@ retry: goto out; } + /* + * A successful COPYCHUNK should copy every descriptor (MS-SMB2 + * 3.3.5.15.6). Reject a short backward copy because the rewind + * below only supports forward copying. + */ + if (unlikely(reverse && bytes_written < copy_bytes)) { + cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n", + bytes_written, copy_bytes); + rc = -EIO; + goto out; + } + /* Partial write: rewind */ if (bytes_written < copy_bytes) { u32 delta = copy_bytes - bytes_written; @@ -2064,10 +2125,27 @@ out: trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid, dst_file->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dst_off, len); - return len; + return 0; } } +static ssize_t +smb2_copychunk_range(const unsigned int xid, + struct cifsFileInfo *src_file, + struct cifsFileInfo *dst_file, + u64 src_off, + u64 len, + u64 dst_off) +{ + int rc; + + rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len, + dst_off); + if (rc) + return rc; + return len; +} + static int smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *fid) @@ -2218,7 +2296,7 @@ smb2_duplicate_extents(const unsigned int xid, trgtfile->fid.volatile_fid, tcon->tid, tcon->ses->Suid, src_off, dest_off, len); inode = d_inode(trgtfile->dentry); - if (inode->i_size < dest_off + len) { + if (i_size_read(inode) < dest_off + len) { rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); if (rc) goto duplicate_extents_out; @@ -2235,7 +2313,10 @@ smb2_duplicate_extents(const unsigned int xid, if (ret_data_len > 0) cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); - if (rc == 0) { + if (rc) { + CIFS_I(inode)->time = 0; /* force reval */ + cifs_invalidate_cache(inode, 0); + } else { qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid, trgtfile->fid.volatile_fid, &file_inf); spin_lock(&inode->i_lock); @@ -3441,6 +3522,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, ses->Suid, offset, len); + new_size = offset + len; + if (!keep_size && i_size_read(inode) < new_size) { + rc = inode_newsize_ok(inode, new_size); + if (rc) + goto out; + } + filemap_invalidate_lock(inode->i_mapping); netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); @@ -3464,6 +3552,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, if (keep_size == false && !CIFS_CACHE_READ(cifsi)) goto zero_range_exit; + fscache_invalidate(cifs_inode_cookie(inode), NULL, + i_size_read(inode), 0); + rc = smb3_zero_data(file, tcon, offset, len, xid); if (rc < 0) goto zero_range_exit; @@ -3471,7 +3562,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, /* * do we also need to change the size of the file? */ - new_size = offset + len; if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) { rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_size); @@ -3488,6 +3578,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, zero_range_exit: filemap_invalidate_unlock(inode->i_mapping); + out: free_xid(xid); if (rc) trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, @@ -3533,6 +3624,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, */ truncate_pagecache_range(inode, offset, offset + len - 1); netfs_wait_for_outstanding_io(inode); + fscache_invalidate(cifs_inode_cookie(inode), NULL, + i_size_read(inode), 0); cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); @@ -3938,18 +4031,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, } filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); + rc = filemap_write_and_wait_range(inode->i_mapping, + round_down(off, PAGE_SIZE), + old_eof - 1); if (rc < 0) goto out_2; - truncate_pagecache_range(inode, off, old_eof); + netfs_wait_for_outstanding_io(inode); + /* + * Invalidate cached folios from the page containing off to EOF before + * moving data on the server, so subsequent reads do not see stale data. + */ + truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1); + fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0); + spin_lock(&inode->i_lock); netfs_write_zero_point(inode, old_eof); spin_unlock(&inode->i_lock); - netfs_wait_for_outstanding_io(inode); - rc = smb2_copychunk_range(xid, cfile, cfile, off + len, - old_eof - off - len, off); + rc = __smb2_copychunk_range(xid, cfile, cfile, off + len, + old_eof - off - len, off); if (rc < 0) goto out_2; @@ -3982,7 +4083,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, struct cifsFileInfo *cfile = file->private_data; struct inode *inode = file_inode(file); struct cifsInodeInfo *cifsi = CIFS_I(inode); - __u64 count, old_eof, new_eof; + loff_t old_eof, new_eof; xid = get_xid(); @@ -3992,15 +4093,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, goto out; } - count = old_eof - off; - new_eof = old_eof + len; + if (check_add_overflow(old_eof, len, &new_eof)) { + rc = -EFBIG; + goto out; + } + rc = inode_newsize_ok(inode, new_eof); + if (rc) + goto out; + + /* SET_ZERO_DATA creates a hole only in a sparse file. */ + rc = smb2_set_sparse(xid, tcon, cfile, inode, true); + if (rc) + goto out; filemap_invalidate_lock(inode->i_mapping); - rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1); + rc = filemap_write_and_wait_range(inode->i_mapping, + round_down(off, PAGE_SIZE), + old_eof - 1); if (rc < 0) goto out_2; - truncate_pagecache_range(inode, off, old_eof); netfs_wait_for_outstanding_io(inode); + /* + * Invalidate cached folios from the page containing off to EOF before + * moving data on the server, so subsequent reads do not see stale data. + */ + truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1); + fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0); rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_eof); @@ -4013,7 +4131,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, spin_unlock(&inode->i_lock); fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode)); - rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); + /* + * Move [off, old_eof) right by len. The helper copies backwards if the + * source and destination ranges overlap. + */ + rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off, + off + len); if (rc < 0) goto out_2; spin_lock(&inode->i_lock); diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index 12241abb8e2e..b442cccd1530 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -79,6 +79,7 @@ EM(smb_eio_trace_qreparse_setup_count, "qreparse_setup_count") \ EM(smb_eio_trace_qreparse_sizes_wrong, "qreparse_sizes_wrong") \ EM(smb_eio_trace_qsym_bcc_too_small, "qsym_bcc_too_small") \ + EM(smb_eio_trace_read_bad_offset, "read_bad_offset") \ EM(smb_eio_trace_read_mid_state_unknown, "read_mid_state_unknown") \ EM(smb_eio_trace_read_overlarge, "read_overlarge") \ EM(smb_eio_trace_read_rsp_malformed, "read_rsp_malformed") \ diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index fdf4e50c27ce..e266859818a4 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry) trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid, midEntry->when_sent, midEntry->when_received); if (cifsFYI & CIFS_TIMER) { - pr_debug("slow rsp: cmd %d mid %llu", - midEntry->command, midEntry->mid); - cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n", - now - midEntry->when_alloc, - now - midEntry->when_sent, - now - midEntry->when_received); + pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n", + midEntry->command, midEntry->mid, + now - midEntry->when_alloc, + now - midEntry->when_sent, + now - midEntry->when_received); } } #endif diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 91fdd1ddc61f..4cb92d6599ee 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -13,6 +13,7 @@ #include "mgmt/ksmbd_ida.h" #include "mgmt/user_session.h" #include "connection.h" +#include "vfs_cache.h" #include "compress.h" #include "transport_tcp.h" #include "transport_rdma.h" @@ -384,12 +385,12 @@ static void ksmbd_conn_cancel_async_requests(struct ksmbd_conn *conn) spin_lock(&conn->request_lock); list_for_each_entry_safe(work, tmp, &conn->async_requests, async_request_entry) { - if (work->state != KSMBD_WORK_ACTIVE) + if (cmpxchg(&work->state, KSMBD_WORK_ACTIVE, + KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE) continue; ksmbd_debug(CONN, "Cancel async request id %d\n", work->async_id); - work->state = KSMBD_WORK_CANCELLED; if (work->cancel_fn) work->cancel_fn(work->cancel_argv); } @@ -473,6 +474,9 @@ retry_idle: if (retry_count >= max_timeout) return -EIO; + /* A blocked byte-range lock cannot drain until teardown wakes it. */ + ksmbd_wake_session_blocked_works(sess); + down_read(&conn_list_lock); hash_for_each(conn_list, bkt, conn, hlist) { if (ksmbd_session_is_bound_to_conn(sess, conn)) { diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index f35335307670..d307aefe0aec 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -30,7 +30,7 @@ static int ksmbd_reserve_iov(struct ksmbd_work *work, int need_iov_cnt) } while (new_alloc_cnt < work->iov_cnt + need_iov_cnt); if (work->iov == work->iov_inline) { - new = kcalloc(new_alloc_cnt, sizeof(*new), KSMBD_DEFAULT_GFP); + new = kzalloc_objs(*new, new_alloc_cnt, KSMBD_DEFAULT_GFP); if (!new) return -ENOMEM; diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index 5f1d3ebab4fb..0844aa929f55 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -82,7 +82,7 @@ struct ksmbd_work { /* Contiguous SMB2 compression transform owned by this work item. */ void *compress_buf; - unsigned char state; + unsigned int state; /* No response for cancelled request */ bool send_no_response:1; /* Request is encrypted */ diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c index b2d9580bddc6..cc9f18ede80d 100644 --- a/fs/smb/server/mgmt/share_config.c +++ b/fs/smb/server/mgmt/share_config.c @@ -146,9 +146,9 @@ static struct ksmbd_share_config *__share_lookup(const char *name) static int parse_veto_list(struct ksmbd_share_config *share, char *veto_list, - int veto_list_sz) + size_t veto_list_sz) { - int sz = 0; + size_t sz; if (!veto_list_sz) return 0; @@ -156,7 +156,7 @@ static int parse_veto_list(struct ksmbd_share_config *share, while (veto_list_sz > 0) { struct ksmbd_veto_pattern *p; - sz = strlen(veto_list); + sz = strnlen(veto_list, veto_list_sz); if (!sz) break; @@ -164,7 +164,7 @@ static int parse_veto_list(struct ksmbd_share_config *share, if (!p) return -ENOMEM; - p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP); + p->pattern = kstrndup(veto_list, sz, KSMBD_DEFAULT_GFP); if (!p->pattern) { kfree(p); return -ENOMEM; @@ -172,6 +172,9 @@ static int parse_veto_list(struct ksmbd_share_config *share, list_add(&p->list, &share->veto_list); + if (sz == veto_list_sz) + break; + veto_list += sz + 1; veto_list_sz -= (sz + 1); } @@ -224,17 +227,28 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, } if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { - int path_len = PATH_MAX; - - if (resp->payload_sz) - path_len = resp->payload_sz - resp->veto_list_sz; + size_t path_len; - share->path = kstrndup(ksmbd_share_config_path(resp), path_len, - KSMBD_DEFAULT_GFP); - if (!share->path) { - ret = -ENOMEM; + if (resp->payload_sz <= resp->veto_list_sz) { + ret = -EINVAL; } else { - ret = 0; + path_len = resp->payload_sz - resp->veto_list_sz; + if (resp->veto_list_sz) + path_len--; + + if (!path_len) { + ret = -EINVAL; + } else { + share->path = kstrndup( + ksmbd_share_config_path(resp), + path_len, KSMBD_DEFAULT_GFP); + if (!share->path) + ret = -ENOMEM; + else + ret = 0; + } + } + if (share->path) { share->path_sz = strlen(share->path); while (share->path_sz > 1 && share->path[share->path_sz - 1] == '/') diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index 5f63e236267a..dd1db3554cae 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -82,6 +82,8 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) down_write(&sess->tree_conns_lock); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, KSMBD_DEFAULT_GFP)); + if (!ret) + atomic_inc(&tree_conn->refcount); up_write(&sess->tree_conns_lock); if (ret) { status.ret = -ENOMEM; @@ -129,6 +131,12 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn) { down_write(&sess->tree_conns_lock); + if (tree_conn->t_state == TREE_DISCONNECTED || + xa_load(&sess->tree_conns, tree_conn->id) != tree_conn) { + up_write(&sess->tree_conns_lock); + return -ENOENT; + } + tree_conn->t_state = TREE_DISCONNECTED; xa_erase(&sess->tree_conns, tree_conn->id); up_write(&sess->tree_conns_lock); diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 7022d5d656b4..2eb8f730e99e 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -666,10 +666,21 @@ void destroy_previous_session(struct ksmbd_conn *conn, memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) goto out; + down_write(&prev_sess->chann_lock); + if (prev_sess->tearing_down) { + up_write(&prev_sess->chann_lock); + goto out; + } + prev_sess->tearing_down = true; + up_write(&prev_sess->chann_lock); + ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT); err = ksmbd_conn_wait_idle_sess(conn, prev_sess); if (err) { - ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP); + down_write(&prev_sess->chann_lock); + prev_sess->tearing_down = false; + up_write(&prev_sess->chann_lock); + ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_GOOD); goto out; } diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index f8a24c33f7fe..3e52d4cc1324 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -42,6 +42,7 @@ struct ksmbd_session { bool sign; bool enc; + bool tearing_down; int state; __u8 *Preauth_HashValue; diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 58af0fddf39f..1b8c3482d1e4 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -924,31 +924,69 @@ out: ksmbd_conn_put(conn); } +/* + * Select and pin the connection used for an oplock break before doing any + * allocations which may sleep. The caller of oplock_break() holds a live + * reference on ci (a file being opened, a file being operated on, or an + * explicit ksmbd_inode_lookup_lock() reference in the parent lease break + * paths), so the inode cannot be freed during the call and its lock is + * reachable without dereferencing opinfo->o_fp, which is not pinned by + * the oplock reference and may be freed by a concurrent close. + * + * opinfo->conn is cleared under ci->m_lock by session_fd_check() when the + * durable handle owning the oplock is disconnected, reassigned by + * ksmbd_reopen_durable_fd() under the same lock, and the last + * ksmbd_conn_put() of the old connection frees it. Holding the read lock + * excludes both writers, so the connection cannot be freed while it is + * selected. + */ +static struct ksmbd_conn *smb2_oplock_break_conn_get(struct oplock_info *opinfo, + struct ksmbd_inode *ci) +{ + struct ksmbd_conn *conn; + + down_read(&ci->m_lock); + conn = READ_ONCE(opinfo->conn); + if (conn && !ksmbd_conn_releasing(conn)) + conn = ksmbd_conn_get(conn); + else + conn = NULL; + up_read(&ci->m_lock); + + return conn; +} + /** * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock * break command from server to client * @opinfo: oplock info object + * @ci: inode owning the break target's oplock list, pinned by + * the caller * * Return: 0 on success, otherwise error */ -static int smb2_oplock_break_noti(struct oplock_info *opinfo) +static int smb2_oplock_break_noti(struct oplock_info *opinfo, + struct ksmbd_inode *ci) { struct ksmbd_conn *conn; struct oplock_break_info *br_info; int ret = 0; struct ksmbd_work *work; - conn = READ_ONCE(opinfo->conn); + conn = smb2_oplock_break_conn_get(opinfo, ci); if (!conn) return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); - if (!work) + if (!work) { + ksmbd_conn_put(conn); return -ENOMEM; + } br_info = kmalloc_obj(struct oplock_break_info, KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); + ksmbd_conn_put(conn); return -ENOMEM; } @@ -957,7 +995,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) br_info->open_trunc = opinfo->open_trunc; work->request_buf = (char *)br_info; - work->conn = ksmbd_conn_get(conn); + /* Transfer the reference acquired by smb2_oplock_break_conn_get(). */ + work->conn = conn; work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); @@ -1154,9 +1193,9 @@ static void wait_lease_breaking(struct oplock_info *opinfo) } } -static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, - struct ksmbd_work *in_work, bool share_break, - bool sync_lease_break) +static int oplock_break(struct oplock_info *brk_opinfo, struct ksmbd_inode *ci, + int req_op_level, struct ksmbd_work *in_work, + bool share_break, bool sync_lease_break) { int err = 0; bool sent_interim = false; @@ -1298,7 +1337,7 @@ again: } } - err = smb2_oplock_break_noti(brk_opinfo); + err = smb2_oplock_break_noti(brk_opinfo, ci); ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) @@ -1326,13 +1365,14 @@ static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo) return 0; } -static void oplock_break_drain_none(struct list_head *head) +static void oplock_break_drain_none(struct list_head *head, + struct ksmbd_inode *ci) { struct oplock_break_entry *ent, *tmp; list_for_each_entry_safe(ent, tmp, head, list) { - oplock_break(ent->opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false, - false); + oplock_break(ent->opinfo, ci, SMB2_OPLOCK_LEVEL_NONE, NULL, + false, false); list_del(&ent->list); opinfo_put(ent->opinfo); kfree(ent); @@ -1481,7 +1521,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, } up_read(&p_ci->m_lock); - oplock_break_drain_none(&brk_list); + oplock_break_drain_none(&brk_list, p_ci); ksmbd_inode_put(p_ci); } @@ -1525,7 +1565,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) } up_read(&p_ci->m_lock); - oplock_break_drain_none(&brk_list); + oplock_break_drain_none(&brk_list, p_ci); ksmbd_inode_put(p_ci); } @@ -1665,7 +1705,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_durable_detached = prev_op_snapshot.durable_detached; prev_fid = prev_op_snapshot.fid; - err = oplock_break(prev_opinfo, break_level, work, + err = oplock_break(prev_opinfo, ci, break_level, work, share_ret < 0 && prev_opinfo->is_lease, false); if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) ksmbd_invalidate_durable_fd(prev_fid); @@ -1771,7 +1811,8 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, } brk_opinfo->open_trunc = is_trunc; - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false, false); + oplock_break(brk_opinfo, fp->f_ci, SMB2_OPLOCK_LEVEL_II, work, false, + false); sent_break = true; opinfo_put(brk_opinfo); @@ -1863,7 +1904,7 @@ next: brk_op->op_state = OPLOCK_STATE_NONE; spin_unlock(&brk_op->state_lock); } else { - oplock_break(brk_op, + oplock_break(brk_op, ci, brk_op->is_lease && !is_trunc ? SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, send_interim && !sent_interim ? work : NULL, diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index a8046f477d54..b7ce67094626 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -97,6 +97,11 @@ static int register_session_channel(struct ksmbd_session *sess, int rc = 0; down_write(&sess->chann_lock); + if (sess->tearing_down) { + rc = -ESHUTDOWN; + goto out; + } + if (xa_load(&sess->ksmbd_chann_list, (long)conn)) goto out; @@ -873,7 +878,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) req = smb_get_msg(work->request_buf); if ((req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || - req->FileInfoClass == FILE_ALL_INFORMATION)) || + req->FileInfoClass == FILE_ALL_INFORMATION || + req->FileInfoClass == FILE_NORMALIZED_NAME_INFORMATION)) || req->InfoType == SMB2_O_INFO_SECURITY) sz = large_sz; } @@ -2784,6 +2790,7 @@ int smb2_tree_connect(struct ksmbd_work *work) struct ksmbd_session *sess = work->sess; char *treename = NULL, *name = NULL; struct ksmbd_tree_conn_status status; + struct ksmbd_tree_connect *tree_conn = NULL; struct ksmbd_share_config *share = NULL; int rc = -EINVAL; @@ -2811,6 +2818,7 @@ int smb2_tree_connect(struct ksmbd_work *work) status = ksmbd_tree_conn_connect(work, name); if (status.ret == KSMBD_TREE_CONN_STATUS_OK) { + tree_conn = status.tree_conn; rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); share = status.tree_conn->share_conf; @@ -2854,8 +2862,15 @@ int smb2_tree_connect(struct ksmbd_work *work) status.tree_conn->posix_extensions = true; down_write(&sess->tree_conns_lock); - status.tree_conn->t_state = TREE_CONNECTED; + if (status.tree_conn->t_state == TREE_DISCONNECTED) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + share = NULL; + } else { + status.tree_conn->t_state = TREE_CONNECTED; + } up_write(&sess->tree_conns_lock); + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_err1; rsp->StructureSize = cpu_to_le16(16); out_err1: /* @@ -2882,9 +2897,6 @@ out_err1: rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp)); if (rc) { if (status.ret == KSMBD_TREE_CONN_STATUS_OK) { - down_write(&sess->tree_conns_lock); - status.tree_conn->t_state = TREE_DISCONNECTED; - up_write(&sess->tree_conns_lock); ksmbd_tree_conn_disconnect(sess, status.tree_conn); status.tree_conn = NULL; } @@ -2925,6 +2937,9 @@ out_err1: if (status.ret != KSMBD_TREE_CONN_STATUS_OK) smb2_set_err_rsp(work); + if (tree_conn) + ksmbd_tree_connect_put(tree_conn); + return rc; } @@ -3028,17 +3043,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_close_tree_conn_fds(work); - down_write(&sess->tree_conns_lock); - if (tcon->t_state == TREE_DISCONNECTED) { - up_write(&sess->tree_conns_lock); - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - err = -ENOENT; - goto err_out; - } - - tcon->t_state = TREE_DISCONNECTED; - up_write(&sess->tree_conns_lock); - err = ksmbd_tree_conn_disconnect(sess, tcon); if (err) { rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -3086,17 +3090,41 @@ int smb2_session_logoff(struct ksmbd_work *work) smb2_set_err_rsp(work); return -ENOENT; } + + down_write(&sess->chann_lock); + if (sess->tearing_down) { + up_write(&sess->chann_lock); + ksmbd_conn_unlock(conn); + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + smb2_set_err_rsp(work); + return -ENOENT; + } + sess->tearing_down = true; + up_write(&sess->chann_lock); + ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT); ksmbd_conn_unlock(conn); + err = ksmbd_conn_wait_idle_sess(conn, sess); + if (err) { + down_write(&sess->chann_lock); + sess->tearing_down = false; + up_write(&sess->chann_lock); + ksmbd_all_conn_set_status(sess, KSMBD_SESS_GOOD); + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + return err; + } + ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn); if (ksmbd_tree_conn_session_logoff(sess)) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); - return -ENOENT; + err = -ENOENT; + } else { + err = 0; } down_write(&conn->session_lock); @@ -3106,6 +3134,9 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP); + if (err) + return err; + rsp->StructureSize = cpu_to_le16(4); err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); if (err) { @@ -6757,7 +6788,7 @@ static int get_file_normalized_name_info(struct ksmbd_work *work, { struct smb2_file_alt_name_info *file_info; char *filename, *normalized, *stream_name; - int conv_len, filename_len; + int buf_free_len, conv_len, filename_len; if (work->conn->dialect < SMB311_PROT_ID) { rsp->hdr.Status = STATUS_NOT_SUPPORTED; @@ -6781,6 +6812,14 @@ static int get_file_normalized_name_info(struct ksmbd_work *work, return -ENOMEM; filename_len = strlen(normalized); + buf_free_len = smb2_resp_buf_len(work, sizeof(*rsp) + + sizeof(*file_info)); + if (buf_free_len < 0 || + (size_t)buf_free_len < (filename_len + 1) * sizeof(__le16)) { + kfree(normalized); + return -EINVAL; + } + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, normalized, filename_len, @@ -7444,6 +7483,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, struct object_id_info *info; info = (struct object_id_info *)(rsp->Buffer); + memset(info, 0, sizeof(*info)); if (path.mnt->mnt_sb->s_uuid_len == 16) memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b, @@ -7499,6 +7539,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->FreeSpaceStopFiltering = 0; info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->FileSystemControlFlags = 0; info->Padding = 0; rsp->OutputBufferLength = cpu_to_le32(48); fixed_len = 48; @@ -7521,6 +7562,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); info->TotalFileNodes = cpu_to_le64(stfs.f_files); info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + info->FileSysIdentifier = + cpu_to_le64((u64)(u32)stfs.f_fsid.val[1] << 32 | + (u32)stfs.f_fsid.val[0]); rsp->OutputBufferLength = cpu_to_le32(56); fixed_len = 56; } @@ -8620,13 +8664,18 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP); + kvmalloc(ALIGN(rpc_resp->payload_sz, 8), + KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; } memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); + if (rpc_resp->payload_sz & 7) + memset(aux_payload_buf + rpc_resp->payload_sz, 0, + ALIGN(rpc_resp->payload_sz, 8) - + rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, @@ -9680,14 +9729,14 @@ int smb2_cancel(struct ksmbd_work *work) * still on conn->async_requests with a live cancel_fn * pointing at the freed file_lock. */ - if (iter->state != KSMBD_WORK_ACTIVE) + if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE, + KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE) break; ksmbd_debug(SMB, "smb2 with AsyncId %llu cancelled command = 0x%x\n", le64_to_cpu(hdr->Id.AsyncId), le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; if (iter->cancel_fn == smb2_notify_cancel_fn) cancelled_notify = smb2_notify_cancel_claim(iter->cancel_argv); @@ -9716,11 +9765,16 @@ int smb2_cancel(struct ksmbd_work *work) iter == work) continue; + if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE, + KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE) + break; + ksmbd_debug(SMB, "smb2 with mid %llu cancelled command = 0x%x\n", le64_to_cpu(hdr->MessageId), le16_to_cpu(chdr->Command)); - iter->state = KSMBD_WORK_CANCELLED; + if (iter->cancel_fn) + iter->cancel_fn(iter->cancel_argv); break; } spin_unlock(&conn->request_lock); @@ -11766,7 +11820,7 @@ static void smb2_notify_cancel_fn(void **argv) return; conn = in_work->conn; - ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC); + ctx = kmalloc_obj(*ctx, GFP_ATOMIC); if (!ctx) { /* Can't defer the response -- free without sending one. */ list_del_init(&in_work->async_request_entry); diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 8ad2e5a5cca8..1fad6ccf3a72 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -383,10 +383,10 @@ void free_acl_state(struct posix_acl_state *state) kfree(state->groups); } -static void parse_dacl(struct mnt_idmap *idmap, - struct smb_acl *pdacl, char *end_of_acl, - struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_fattr *fattr) +static int parse_dacl(struct mnt_idmap *idmap, + struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) { int i, ret; u16 num_aces = 0; @@ -400,13 +400,13 @@ static void parse_dacl(struct mnt_idmap *idmap, bool owner_found = false, group_found = false, others_found = false; if (!pdacl) - return; + return 0; /* validate that we do not go past end of acl */ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { pr_err("ACL too small to parse DACL\n"); - return; + return -EINVAL; } ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", @@ -418,31 +418,31 @@ static void parse_dacl(struct mnt_idmap *idmap, num_aces = le16_to_cpu(pdacl->num_aces); if (num_aces <= 0) - return; + return 0; dacl_size = le16_to_cpu(pdacl->size); if (dacl_size < sizeof(struct smb_acl)) - return; + return -EINVAL; if (num_aces > (dacl_size - sizeof(struct smb_acl)) / (offsetof(struct smb_ace, sid) + offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) - return; + return -EINVAL; ret = init_acl_state(&acl_state, num_aces); if (ret) - return; + return ret; ret = init_acl_state(&default_acl_state, num_aces); if (ret) { free_acl_state(&acl_state); - return; + return ret; } ppace = kmalloc_objs(struct smb_ace *, num_aces, KSMBD_DEFAULT_GFP); if (!ppace) { free_acl_state(&default_acl_state); free_acl_state(&acl_state); - return; + return -ENOMEM; } /* @@ -451,8 +451,10 @@ static void parse_dacl(struct mnt_idmap *idmap, * user/group/other have no permissions */ for (i = 0; i < num_aces; ++i) { - if (end_of_acl - acl_base < acl_size) - break; + if (end_of_acl - acl_base < acl_size) { + ret = -EINVAL; + goto out; + } ppace[i] = (struct smb_ace *)(acl_base + acl_size); acl_base = (char *)ppace[i]; @@ -465,8 +467,10 @@ static void parse_dacl(struct mnt_idmap *idmap, (end_of_acl - acl_base < acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || (le16_to_cpu(ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) { + ret = -EINVAL; + goto out; + } acl_size = le16_to_cpu(ppace[i]->size); ppace[i]->access_req = @@ -524,8 +528,8 @@ static void parse_dacl(struct mnt_idmap *idmap, temp_fattr.cf_uid = INVALID_UID; ret = sid_to_id(idmap, &ppace[i]->sid, SIDOWNER, &temp_fattr); if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { - pr_err("%s: Error %d mapping Owner SID to uid\n", - __func__, ret); + pr_err_ratelimited("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); continue; } @@ -541,7 +545,6 @@ static void parse_dacl(struct mnt_idmap *idmap, ((acl_mode & 0700) >> 6) | 0004; } } - kfree(ppace); if (owner_found) { /* The owner must be set to at least read-only. */ @@ -584,10 +587,12 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_acls = posix_acl_alloc(acl_state.users->n + acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); - if (fattr->cf_acls) { - cf_pace = fattr->cf_acls->a_entries; - posix_state_to_acl(&acl_state, cf_pace); + if (!fattr->cf_acls) { + ret = -ENOMEM; + goto out; } + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); } } @@ -598,14 +603,20 @@ static void parse_dacl(struct mnt_idmap *idmap, fattr->cf_dacls = posix_acl_alloc(default_acl_state.users->n + default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP); - if (fattr->cf_dacls) { - cf_pdace = fattr->cf_dacls->a_entries; - posix_state_to_acl(&default_acl_state, cf_pdace); + if (!fattr->cf_dacls) { + ret = -ENOMEM; + goto out; } + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); } } + ret = 0; +out: + kfree(ppace); free_acl_state(&acl_state); free_acl_state(&default_acl_state); + return ret; } static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, @@ -966,8 +977,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, if (dacloffset < sizeof(struct smb_ntsd)) return -EINVAL; - parse_dacl(idmap, dacl_ptr, end_of_acl, - owner_sid_ptr, group_sid_ptr, fattr); + rc = parse_dacl(idmap, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); + if (rc) + return rc; } return 0; diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c index 4b0b572a3e1b..e550aa41ad2c 100644 --- a/fs/smb/server/transport_ipc.c +++ b/fs/smb/server/transport_ipc.c @@ -532,14 +532,21 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry) if (entry->msg_sz < sizeof(struct ksmbd_share_config_response)) return -EINVAL; - if (resp->payload_sz) { - if (resp->payload_sz < resp->veto_list_sz) - return -EINVAL; + if (strnlen(resp->share_name, sizeof(resp->share_name)) == + sizeof(resp->share_name)) + return -EINVAL; - if (check_add_overflow(sizeof(struct ksmbd_share_config_response), - resp->payload_sz, &msg_sz)) - return -EINVAL; - } + if (resp->veto_list_sz > resp->payload_sz) + return -EINVAL; + + if (resp->flags != KSMBD_SHARE_FLAG_INVALID && + !(resp->flags & KSMBD_SHARE_FLAG_PIPE) && + resp->payload_sz <= resp->veto_list_sz) + return -EINVAL; + + if (check_add_overflow(sizeof(struct ksmbd_share_config_response), + resp->payload_sz, &msg_sz)) + return -EINVAL; break; } case KSMBD_EVENT_LOGIN_REQUEST_EXT: diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 832e93084605..4968cfc1a572 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -39,6 +39,7 @@ struct tcp_transport { static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops; static void tcp_stop_kthread(struct task_struct *kthread); +static void ksmbd_tcp_stop_listener(struct interface *iface); static struct interface *alloc_iface(char *ifname); static void ksmbd_tcp_disconnect(struct ksmbd_transport *t); @@ -321,13 +322,20 @@ static int ksmbd_tcp_run_kthread(struct interface *iface) int rc; struct task_struct *kthread; - kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", - iface->name); + kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); if (IS_ERR(kthread)) { rc = PTR_ERR(kthread); return rc; } + + /* + * The listener can exit after its socket is shutdown, so keep the + * task_struct alive until the caller has stopped it. + */ + get_task_struct(kthread); iface->ksmbd_kthread = kthread; + wake_up_process(kthread); return 0; } @@ -598,12 +606,7 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, if (iface && iface->state == IFACE_STATE_CONFIGURED) { ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n", iface->name); - kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR); - tcp_stop_kthread(iface->ksmbd_kthread); - iface->ksmbd_kthread = NULL; - sock_release(iface->ksmbd_socket); - iface->ksmbd_socket = NULL; - + ksmbd_tcp_stop_listener(iface); iface->state = IFACE_STATE_DOWN; break; } @@ -631,11 +634,25 @@ static void tcp_stop_kthread(struct task_struct *kthread) if (!kthread) return; - ret = kthread_stop(kthread); + ret = kthread_stop_put(kthread); if (ret) pr_err("failed to stop forker thread\n"); } +static void ksmbd_tcp_stop_listener(struct interface *iface) +{ + if (iface->ksmbd_socket) + kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR); + + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + + if (iface->ksmbd_socket) { + sock_release(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + } +} + void ksmbd_tcp_destroy(void) { struct interface *iface, *tmp; @@ -643,6 +660,7 @@ void ksmbd_tcp_destroy(void) unregister_netdevice_notifier(&ksmbd_netdev_notifier); list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + ksmbd_tcp_stop_listener(iface); list_del(&iface->entry); kfree(iface->name); kfree(iface); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d2b524f79cbe..c2c9aaa5de1b 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -2007,6 +2007,11 @@ out: return ret; } +static bool ksmbd_vfs_copy_range_valid(loff_t offset, size_t len) +{ + return offset >= 0 && (loff_t)len <= MAX_LFS_FILESIZE - offset; +} + int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, struct ksmbd_file *src_fp, struct ksmbd_file *dst_fp, @@ -2042,6 +2047,10 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, dst_off = le64_to_cpu(chunks[i].TargetOffset); len = le32_to_cpu(chunks[i].Length); + if (!ksmbd_vfs_copy_range_valid(src_off, len) || + !ksmbd_vfs_copy_range_valid(dst_off, len)) + return -E2BIG; + if (check_lock_range(src_fp->filp, src_off, src_off + len - 1, READ)) return -EAGAIN; @@ -2134,7 +2143,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, len = le32_to_cpu(chunks[i].Length); copy_len = len; - if (src_off < 0) + if (!ksmbd_vfs_copy_range_valid(src_off, len) || + !ksmbd_vfs_copy_range_valid(dst_off, len)) return -E2BIG; if (src_off > src_file_size || len > src_file_size - src_off) { diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 81626d204249..fd2c595f0486 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -846,12 +846,25 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp) spin_lock(&fp->f_lock); list_for_each_entry(cancel_work, &fp->blocked_works, fp_entry) { - cancel_work->state = KSMBD_WORK_CLOSED; - cancel_work->cancel_fn(cancel_work->cancel_argv); + if (xchg(&cancel_work->state, KSMBD_WORK_CLOSED) == + KSMBD_WORK_ACTIVE) + cancel_work->cancel_fn(cancel_work->cancel_argv); } spin_unlock(&fp->f_lock); } +void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess) +{ + struct ksmbd_file_table *ft = &sess->file_table; + struct ksmbd_file *fp; + unsigned int id; + + read_lock(&ft->lock); + idr_for_each_entry(ft->idr, fp, id) + set_close_state_blocked_works(fp); + read_unlock(&ft->lock); +} + int ksmbd_close_fd(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp; diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 502efb16f05f..1884f6deb9d0 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -226,6 +226,7 @@ void ksmbd_stop_durable_scavenger(void); bool ksmbd_durable_scavenger_active(void); void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); void ksmbd_close_session_fds(struct ksmbd_work *work); +void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess); int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); diff --git a/fs/super.c b/fs/super.c index 05e443173038..9d4025213521 100644 --- a/fs/super.c +++ b/fs/super.c @@ -172,19 +172,6 @@ static void super_wake(struct super_block *sb, unsigned int flag) } /* - * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs) - * operate on filesystem-global state and ignore sc->memcg. Driving them - * from per-memcg shrink_slab_memcg() invocations only burns CPU walking - * per-cpu counters and queueing duplicate work: the actual reclaim happens on - * the global path (kswapd or root direct reclaim) regardless. Restrict them - * to that path. - */ -static inline bool super_fs_objects_eligible(struct shrink_control *sc) -{ - return !sc->memcg || mem_cgroup_is_root(sc->memcg); -} - -/* * One thing we have to be careful of with a per-sb shrinker is that we don't * drop the last active reference to the superblock from within the shrinker. * If that happens we could trigger unregistering the shrinker from within the @@ -213,7 +200,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink, if (!super_trylock_shared(sb)) return SHRINK_STOP; - if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc)) + if (sb->s_op->nr_cached_objects) fs_objects = sb->s_op->nr_cached_objects(sb, sc); inodes = list_lru_shrink_count(&sb->s_inode_lru, sc); @@ -274,8 +261,7 @@ static unsigned long super_cache_count(struct shrinker *shrink, return 0; smp_rmb(); - if (sb->s_op && sb->s_op->nr_cached_objects && - super_fs_objects_eligible(sc)) + if (sb->s_op && sb->s_op->nr_cached_objects) total_objects = sb->s_op->nr_cached_objects(sb, sc); total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc); @@ -2369,11 +2355,14 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who, goto out_unlock; /* - * All freezers share a single active reference. - * So just unlock in case there are any left. + * All freezers share a single active reference. If other freezers + * remain, drop our hold and report success; the superblock stays + * frozen until the last holder thaws it. */ - if (freeze_dec(sb, who)) + if (freeze_dec(sb, who)) { + error = 0; goto out_unlock; + } if (sb_rdonly(sb)) { sb->s_writers.frozen = SB_UNFROZEN; diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index a2813270c303..b930ee1cf853 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -68,6 +68,16 @@ static bool ufs_read_cylinder(struct super_block *sb, ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff); ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff); ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks); + + /* these on-disk values become array and bitmap indices */ + if (ucpi->c_cgx != cgno || + ucpi->c_rotor >= uspi->s_fpg || + ucpi->c_frotor >= uspi->s_fpg || + ucpi->c_irotor >= uspi->s_ipg) { + ufs_error(sb, __func__, + "inconsistent metadata in cylinder group %u\n", cgno); + goto failed; + } UFSD("EXIT\n"); return true; diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index e62fe5667671..ce43cf20b07c 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -590,7 +590,7 @@ int ufs_empty_dir(struct inode * inode) kaddr = ufs_get_folio(inode, i, &folio); if (IS_ERR(kaddr)) - continue; + return 0; de = (struct ufs_dir_entry *)kaddr; kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 6dcf6d048cce..3569ac92b065 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1199,6 +1199,15 @@ magic_found: sb->s_maxbytes = ufs_max_bytes(sb); sb->s_max_links = UFS_LINK_MAX; + ufs_setup_cstotal(sb); + /* + * Read cylinder group structures + */ + if (!sb_rdonly(sb)) + if (!ufs_read_cylinder_structures(sb)) + goto failed; + + /* create the root dentry last, once UFS_SB(sb) is fully set up */ inode = ufs_iget(sb, UFS_ROOTINO); if (IS_ERR(inode)) { ret = PTR_ERR(inode); @@ -1210,14 +1219,6 @@ magic_found: goto failed; } - ufs_setup_cstotal(sb); - /* - * Read cylinder group structures - */ - if (!sb_rdonly(sb)) - if (!ufs_read_cylinder_structures(sb)) - goto failed; - UFSD("EXIT\n"); return 0; diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index 9f7133e02576..399a207f2d0e 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -91,6 +91,7 @@ xfs-y += xfs_aops.o \ xfs_healthmon.o \ xfs_icache.o \ xfs_ioctl.o \ + xfs_ioend.o \ xfs_iomap.o \ xfs_iops.o \ xfs_inode.o \ diff --git a/fs/xfs/libxfs/xfs_btree_mem.c b/fs/xfs/libxfs/xfs_btree_mem.c index 37136a70e56d..1d83a4251cee 100644 --- a/fs/xfs/libxfs/xfs_btree_mem.c +++ b/fs/xfs/libxfs/xfs_btree_mem.c @@ -117,6 +117,7 @@ xfbtree_init( struct xfs_buftarg *btp, const struct xfs_btree_ops *ops) { + unsigned long long owner = xfbt->owner; unsigned int blocklen = xfbtree_rec_bytes(mp, ops); unsigned int keyptr_len; int error; @@ -133,6 +134,7 @@ xfbtree_init( memset(xfbt, 0, sizeof(*xfbt)); xfbt->target = btp; + xfbt->owner = owner; /* Set up min/maxrecs for this btree. */ keyptr_len = ops->key_len + sizeof(__be64); diff --git a/fs/xfs/libxfs/xfs_btree_staging.c b/fs/xfs/libxfs/xfs_btree_staging.c index 7314dab4bcfb..561fd2c2e950 100644 --- a/fs/xfs/libxfs/xfs_btree_staging.c +++ b/fs/xfs/libxfs/xfs_btree_staging.c @@ -336,8 +336,10 @@ xfs_btree_bload_prep_block( xfs_btree_set_sibling(cur, *blockp, &new_ptr, XFS_BB_RIGHTSIB); ret = xfs_btree_bload_drop_buf(bbl, buffers_list, bpp); - if (ret) + if (ret) { + xfs_buf_relse(new_bp); return ret; + } /* Initialize the new btree block. */ xfs_btree_init_block_cur(cur, new_bp, level, nr_this_block); diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c index f190c088591b..3d02a0d7ba44 100644 --- a/fs/xfs/libxfs/xfs_da_btree.c +++ b/fs/xfs/libxfs/xfs_da_btree.c @@ -130,7 +130,7 @@ xfs_da_state_reset( state->mp = state->args->dp->i_mount; } -static inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork) +inline int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork) { if (whichfork == XFS_DATA_FORK) return mp->m_dir_geo->fsbcount; @@ -2384,6 +2384,7 @@ xfs_da_grow_inode_int( } /* account for newly allocated blocks in reserved blocks total */ + ASSERT(args->total >= dp->i_nblocks - nblks); args->total -= dp->i_nblocks - nblks; out_free_map: @@ -2746,8 +2747,8 @@ xfs_dabuf_map( * larger one that needs to be free by the caller. */ if (nirecs > 1) { - map = kcalloc(nirecs, sizeof(struct xfs_buf_map), - GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL); + map = kzalloc_objs(struct xfs_buf_map, nirecs, + GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL); *mapp = map; } diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h index afcf2d3c7a21..a718b1ceb0aa 100644 --- a/fs/xfs/libxfs/xfs_da_btree.h +++ b/fs/xfs/libxfs/xfs_da_btree.h @@ -244,4 +244,6 @@ xfs_failaddr_t xfs_da3_node_header_check(struct xfs_buf *bp, xfs_ino_t owner); extern struct kmem_cache *xfs_da_state_cache; +int xfs_dabuf_nfsb(struct xfs_mount *mp, int whichfork); + #endif /* __XFS_DA_BTREE_H__ */ diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 89501e8bd2f8..3152acdc335d 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -229,6 +229,7 @@ xfs_defer_barrier_cancel_item( } static const struct xfs_defer_op_type xfs_barrier_defer_type = { + .name = "barrier", .max_items = 1, .create_intent = xfs_defer_barrier_create_intent, .abort_intent = xfs_defer_barrier_abort_intent, @@ -583,7 +584,7 @@ xfs_defer_finish_one( const struct xfs_defer_op_type *ops = dfp->dfp_ops; struct xfs_btree_cur *state = NULL; struct list_head *li, *n; - int error; + int error = 0; trace_xfs_defer_pending_finish(tp->t_mountp, dfp); @@ -655,6 +656,7 @@ xfs_defer_finish_noroll( struct xfs_trans **tp) { struct xfs_defer_pending *dfp = NULL; + const char *what = "chain"; int error = 0; LIST_HEAD(dop_pending); LIST_HEAD(dop_paused); @@ -704,9 +706,17 @@ xfs_defer_finish_noroll( struct xfs_defer_pending, dfp_list); if (!dfp) break; + what = dfp->dfp_ops->name; error = xfs_defer_finish_one(*tp, dfp); if (error && error != -EAGAIN) goto out_shutdown; + /* + * A finished item is no longer a candidate for a later + * failure. An -EAGAIN one is not finished, so it keeps the + * attribution across the roll that completes it. + */ + if (!error) + what = "chain"; } /* Requeue the paused items in the outgoing transaction. */ @@ -718,8 +728,12 @@ xfs_defer_finish_noroll( out_shutdown: list_splice_tail_init(&dop_paused, &dop_pending); xfs_defer_trans_abort(*tp, &dop_pending); - xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE); trace_xfs_defer_finish_error(*tp, error); + if (!xfs_is_shutdown((*tp)->t_mountp)) + xfs_alert((*tp)->t_mountp, + "deferred %s work failed, error %d, %u blocks reserved", + what, error, (*tp)->t_blk_res); + xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE); xfs_defer_cancel_list((*tp)->t_mountp, &dop_pending); xfs_defer_cancel(*tp); return error; diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index 3efed37cb98a..49eda8d0994d 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -969,6 +969,16 @@ xmi_can_exchange_reflink_flags( if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN) return false; + /* + * The INO1_WRITTEN optimization can skip exchanging hole and + * unwritten mappings, which means we cannot guarantee that all + * shared extents actually moved to the other file. Clearing the + * reflink flag of an inode that still holds shared extents breaks + * the CoW write path, so refuse to exchange the flags in that case. + */ + if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN) + return false; + if (hweight32(reflink_state) != 1) return false; if (req->startoff1 != 0 || req->startoff2 != 0) diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c index 8d111c9b6527..a2f2f5fa640e 100644 --- a/fs/xfs/libxfs/xfs_parent.c +++ b/fs/xfs/libxfs/xfs_parent.c @@ -193,7 +193,7 @@ xfs_parent_addname( const struct xfs_name *parent_name, struct xfs_inode *child) { - int error; + int error, local; error = xfs_parent_iread_extents(tp, child); if (error) @@ -203,6 +203,10 @@ xfs_parent_addname( xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child, I_INO(child), parent_name); + /* Growing the attr fork needs a real reservation in args->total. */ + ppargs->args.total = xfs_attr_calc_size(&ppargs->args, &local); + ASSERT(local); + return xfs_attr_setname(&ppargs->args, 0); } @@ -239,7 +243,7 @@ xfs_parent_replacename( const struct xfs_name *new_name, struct xfs_inode *child) { - int error; + int error, local; error = xfs_parent_iread_extents(tp, child); if (error) @@ -249,6 +253,10 @@ xfs_parent_replacename( xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child, I_INO(child), old_name); + /* Growing the attr fork needs a real reservation in args->total. */ + ppargs->args.total = xfs_attr_calc_size(&ppargs->args, &local); + ASSERT(local); + xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp); ppargs->args.new_name = new_name->name; diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h index c0b9f9f2c413..fca2eb74908c 100644 --- a/fs/xfs/libxfs/xfs_rtgroup.h +++ b/fs/xfs/libxfs/xfs_rtgroup.h @@ -359,7 +359,11 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp, # define xfs_rtgroup_unlock(rtg, gf) ((void)0) # define xfs_rtgroup_trans_join(tp, rtg, gf) ((void)0) # define xfs_update_rtsb(bp, sb_bp) ((void)0) -# define xfs_log_rtsb(tp, sb_bp) (NULL) +static inline struct xfs_buf *xfs_log_rtsb(struct xfs_trans *tp, + const struct xfs_buf *sb_bp) +{ + return NULL; +} # define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP) #endif /* CONFIG_XFS_RT */ diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c index 22acc1411aac..e2950dbe2068 100644 --- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c +++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c @@ -489,8 +489,11 @@ xfs_rtrefcountbt_maxlevels_ondisk(void) minrecs[0] = xfs_rtrefcountbt_block_maxrecs(blocklen, true) / 2; minrecs[1] = xfs_rtrefcountbt_block_maxrecs(blocklen, false) / 2; - /* We need at most one record for every block in an rt group. */ - return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS); + /* + * We need at most one record for every block in an rt group, and + * one extra level for the inode root. + */ + return xfs_btree_compute_maxlevels(minrecs, XFS_MAX_RGBLOCKS) + 1; } int __init diff --git a/fs/xfs/libxfs/xfs_rtrmap_btree.c b/fs/xfs/libxfs/xfs_rtrmap_btree.c index c264bc5651c0..a15e460a1ec7 100644 --- a/fs/xfs/libxfs/xfs_rtrmap_btree.c +++ b/fs/xfs/libxfs/xfs_rtrmap_btree.c @@ -618,7 +618,7 @@ xfs_rtrmapbt_mem_cursor( struct xfs_btree_cur *cur; cur = xfs_btree_alloc_cursor(mp, tp, &xfs_rtrmapbt_mem_ops, - mp->m_rtrmap_maxlevels, xfs_rtrmapbt_cur_cache); + xfs_rtrmapbt_maxlevels_ondisk(), xfs_rtrmapbt_cur_cache); cur->bc_mem.xfbtree = xfbt; cur->bc_nlevels = xfbt->nlevels; cur->bc_group = xfs_group_hold(rtg_group(rtg)); @@ -716,10 +716,12 @@ xfs_rtrmapbt_maxlevels_ondisk(void) * happens, which means that we must compute the max height based on * what the btree will look like if it consumes almost all the blocks * in the data device due to maximal sharing factor. + * + * Add one extra level for the inode root. */ max_dblocks = -1U; /* max ag count */ max_dblocks *= XFS_MAX_CRC_AG_BLOCKS; - return xfs_btree_space_to_height(minrecs, max_dblocks); + return xfs_btree_space_to_height(minrecs, max_dblocks) + 1; } int __init diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index 75f2a021ee6d..f0341adbb879 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -1470,36 +1470,33 @@ xfs_sync_sb_buf( bool update_rtsb) { struct xfs_trans *tp; - struct xfs_buf *bp; - struct xfs_buf *rtsb_bp = NULL; int error; error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp); if (error) return error; - bp = xfs_trans_getsb(tp); xfs_log_sb(tp); - xfs_trans_bhold(tp, bp); - if (update_rtsb) { - rtsb_bp = xfs_log_rtsb(tp, bp); - if (rtsb_bp) - xfs_trans_bhold(tp, rtsb_bp); - } + if (update_rtsb) + xfs_log_rtsb(tp, xfs_trans_getsb(tp)); xfs_trans_set_sync(tp); error = xfs_trans_commit(tp); if (error) - goto out; - /* - * write out the sb buffer to get the changes to disk - */ - error = xfs_bwrite(bp); - if (!error && rtsb_bp) - error = xfs_bwrite(rtsb_bp); -out: - if (rtsb_bp) - xfs_buf_relse(rtsb_bp); - xfs_buf_relse(bp); + return error; + + /* Re-acquire and write the sb and rtsb to disk. */ + xfs_buf_lock(mp->m_sb_bp); + error = xfs_bwrite(mp->m_sb_bp); + xfs_buf_unlock(mp->m_sb_bp); + if (error) + return error; + + if (update_rtsb && mp->m_rtsb_bp) { + xfs_buf_lock(mp->m_rtsb_bp); + error = xfs_bwrite(mp->m_rtsb_bp); + xfs_buf_unlock(mp->m_rtsb_bp); + } + return error; } diff --git a/fs/xfs/libxfs/xfs_trans_space.c b/fs/xfs/libxfs/xfs_trans_space.c index 9b8f495c9049..c4cd547033e5 100644 --- a/fs/xfs/libxfs/xfs_trans_space.c +++ b/fs/xfs/libxfs/xfs_trans_space.c @@ -22,8 +22,23 @@ xfs_parent_calc_space_res( unsigned int namelen) { /* - * Parent pointers are always the first attr in an attr tree, and never - * larger than a block + * A parent pointer is recorded per dirent, so an inode with N links + * carries N of them and the attr fork can already be in leaf or node + * format when one is added. That does not affect the reservation: + * XFS_DAENTER_SPACE_RES covers a split at every level of a + * maximum-depth attr dabtree, whatever format the fork is in now. + * + * The name is a dirent name and the value is a struct xfs_parent_rec, + * so the leaf entry is always local and never exceeds 272 bytes. + * Parent pointers require V5, hence a 1k minimum block size, so the + * entry always stays under half a block and this needs none of the + * double split allowance that xfs_attr_calc_size() makes. + * + * The second term hands a byte count to a macro whose parameter counts + * mappings, so it asks for more extent-add allowance than the single + * mapping a parent pointer adds - how much more depends on the block + * size. It over-reserves either way, which is why it is left alone: + * correcting the unit would shrink a reservation that is only generous. */ return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) + XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK); diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 1fa66aa68e16..fa5d32ec020a 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -418,6 +418,13 @@ xchk_superblock( xchk_block_set_corrupt(sc, bp); } + if (xfs_has_zoned(mp)) { + if (sb->sb_rtstart != cpu_to_be64(mp->m_sb.sb_rtstart)) + xchk_block_set_corrupt(sc, bp); + if (sb->sb_rtreserved != cpu_to_be64(mp->m_sb.sb_rtreserved)) + xchk_block_set_corrupt(sc, bp); + } + /* Everything else must be zero. */ sblen = xchk_superblock_ondisk_size(mp); if (memchr_inv((char *)sb + sblen, 0, BBTOB(bp->b_length) - sblen)) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 2104512f1ee1..a66b611588c4 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -668,14 +668,16 @@ xrep_agfl_init_header( struct xfs_scrub *sc, struct xfs_buf *agfl_bp, struct xagb_bitmap *agfl_extents, - xfs_agblock_t flcount) + xfs_agblock_t flcount, + struct xfs_agfl *old_agfl) { struct xrep_agfl_fill af = { .sc = sc, .flcount = flcount, }; struct xfs_mount *mp = sc->mp; - struct xfs_agfl *agfl; + struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(agfl_bp); + const size_t agfl_sz = BBTOB(agfl_bp->b_length); int error; ASSERT(flcount <= xfs_agfl_size(mp)); @@ -684,8 +686,8 @@ xrep_agfl_init_header( * Start rewriting the header by setting the bno[] array to * NULLAGBLOCK, then setting AGFL header fields. */ - agfl = XFS_BUF_TO_AGFL(agfl_bp); - memset(agfl, 0xFF, BBTOB(agfl_bp->b_length)); + memcpy(old_agfl, agfl, agfl_sz); + memset(agfl, 0xFF, agfl_sz); agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC); agfl->agfl_seqno = cpu_to_be32(pag_agno(sc->sa.pag)); uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid); @@ -697,16 +699,23 @@ xrep_agfl_init_header( */ xagb_bitmap_init(&af.used_extents); af.agfl_bno = xfs_buf_to_agfl_bno(agfl_bp); - xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af); + error = xagb_bitmap_walk(agfl_extents, xrep_agfl_fill, &af); + if (error && error != -ECANCELED) + goto err_undo; error = xagb_bitmap_disunion(agfl_extents, &af.used_extents); if (error) - return error; + goto err_undo; /* Write new AGFL to disk. */ xfs_trans_buf_set_type(sc->tp, agfl_bp, XFS_BLFT_AGFL_BUF); - xfs_trans_log_buf(sc->tp, agfl_bp, 0, BBTOB(agfl_bp->b_length) - 1); + xfs_trans_log_buf(sc->tp, agfl_bp, 0, agfl_sz - 1); xagb_bitmap_destroy(&af.used_extents); return 0; + +err_undo: + xagb_bitmap_destroy(&af.used_extents); + memcpy(agfl, old_agfl, agfl_sz); + return error; } /* Repair the AGFL. */ @@ -718,6 +727,7 @@ xrep_agfl( struct xfs_mount *mp = sc->mp; struct xfs_buf *agf_bp; struct xfs_buf *agfl_bp; + struct xfs_agfl *old_agfl; xfs_agblock_t flcount; int error; @@ -725,6 +735,10 @@ xrep_agfl( if (!xfs_has_rmapbt(mp)) return -EOPNOTSUPP; + old_agfl = kzalloc(BBTOB(XFS_FSS_TO_BB(mp, 1)), XCHK_GFP_FLAGS); + if (!old_agfl) + return -ENOMEM; + xagb_bitmap_init(&agfl_extents); /* @@ -734,7 +748,7 @@ xrep_agfl( */ error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp); if (error) - return error; + goto err_old_agfl; /* * Make sure we have the AGFL buffer, as scrub might have decided it @@ -745,7 +759,7 @@ xrep_agfl( XFS_AGFL_DADDR(mp)), XFS_FSS_TO_BB(mp, 1), 0, &agfl_bp, NULL); if (error) - return error; + goto err_old_agfl; agfl_bp->b_ops = &xfs_agfl_buf_ops; /* Gather all the extents we're going to put on the new AGFL. */ @@ -762,10 +776,11 @@ xrep_agfl( * we adjust the AGF flcount (which can fail) so avoid updating any * buffers until we know that part works. */ - xrep_agfl_update_agf(sc, agf_bp, flcount); - error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount); + error = xrep_agfl_init_header(sc, agfl_bp, &agfl_extents, flcount, + old_agfl); if (error) goto err; + xrep_agfl_update_agf(sc, agf_bp, flcount); /* * Ok, the AGFL should be ready to go now. Roll the transaction to @@ -785,6 +800,8 @@ xrep_agfl( err: xagb_bitmap_destroy(&agfl_extents); +err_old_agfl: + kfree(old_agfl); return error; } diff --git a/fs/xfs/scrub/alloc_repair.c b/fs/xfs/scrub/alloc_repair.c index dce6ab0429dc..95e318e4f3a6 100644 --- a/fs/xfs/scrub/alloc_repair.c +++ b/fs/xfs/scrub/alloc_repair.c @@ -571,7 +571,7 @@ xrep_abt_dispose_one( * allocation, and blocks that didn't get used can be freed via the usual * (deferred) means. */ -STATIC void +STATIC int xrep_abt_dispose_reservations( struct xrep_abt *ra, int error) @@ -582,9 +582,13 @@ xrep_abt_dispose_reservations( goto junkit; list_for_each_entry_safe(resv, n, &ra->new_bnobt.resv_list, list) { - error = xrep_abt_dispose_one(ra, resv); - if (error) + int error2 = xrep_abt_dispose_one(ra, resv); + + if (error2) { + if (!error) + error = error2; goto junkit; + } } junkit: @@ -596,6 +600,7 @@ junkit: xrep_newbt_cancel(&ra->new_bnobt); xrep_newbt_cancel(&ra->new_cntbt); + return error; } /* Retrieve free space data for bulk load. */ @@ -801,7 +806,9 @@ xrep_abt_build_new_trees( goto err_newbt; /* Dispose of any unused blocks and the accounting information. */ - xrep_abt_dispose_reservations(ra, error); + error = xrep_abt_dispose_reservations(ra, error); + if (error) + return error; return xrep_roll_ag_trans(sc); @@ -812,8 +819,7 @@ err_cur: xfs_btree_del_cursor(cnt_cur, error); xfs_btree_del_cursor(bno_cur, error); err_newbt: - xrep_abt_dispose_reservations(ra, error); - return error; + return xrep_abt_dispose_reservations(ra, error); } /* diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c index 6e6af142f1fb..28f92e9ba72b 100644 --- a/fs/xfs/scrub/attr_repair.c +++ b/fs/xfs/scrub/attr_repair.c @@ -1294,7 +1294,7 @@ xrep_xattr_swap_prep( .geo = sc->mp->m_attr_geo, .whichfork = XFS_ATTR_FORK, .trans = sc->tp, - .total = 1, + .total = xfs_dabuf_nfsb(sc->mp, XFS_ATTR_FORK), .owner = I_INO(sc->ip), }; diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 401c278725d2..4f3c7f681bd9 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -274,7 +274,7 @@ xchk_bmap_xref_rmap_cow( unsigned long long rmap_end; uint64_t owner = XFS_RMAP_OWN_COW; - if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm)) + if (xchk_skip_xref(info->sc->sm)) return; /* Find the rmap record for this irec. */ @@ -1103,8 +1103,9 @@ xchk_bmap( * the rmap must match the combined mapping exactly. */ while (xchk_bmap_iext_iter(&info, &irec)) { - if (xchk_should_terminate(sc, &error) || - (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) + if (xchk_should_terminate(sc, &error)) + return error; + if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return 0; if (irec.br_startoff >= endoff) { diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index 9d627fd50687..f0f073a93413 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -74,7 +74,6 @@ int xchk_setup_ag_rmapbt(struct xfs_scrub *sc); int xchk_setup_ag_refcountbt(struct xfs_scrub *sc); int xchk_setup_inode(struct xfs_scrub *sc); int xchk_setup_inode_bmap(struct xfs_scrub *sc); -int xchk_setup_inode_bmap_data(struct xfs_scrub *sc); int xchk_setup_directory(struct xfs_scrub *sc); int xchk_setup_xattr(struct xfs_scrub *sc); int xchk_setup_symlink(struct xfs_scrub *sc); diff --git a/fs/xfs/scrub/dabtree.h b/fs/xfs/scrub/dabtree.h index de291e3b77dd..d654c125feb4 100644 --- a/fs/xfs/scrub/dabtree.h +++ b/fs/xfs/scrub/dabtree.h @@ -37,8 +37,6 @@ bool xchk_da_process_error(struct xchk_da_btree *ds, int level, int *error); void xchk_da_set_corrupt(struct xchk_da_btree *ds, int level); void xchk_da_set_preen(struct xchk_da_btree *ds, int level); -void xchk_da_set_preen(struct xchk_da_btree *ds, int level); - int xchk_da_btree_hash(struct xchk_da_btree *ds, int level, __be32 *hashp); int xchk_da_btree(struct xfs_scrub *sc, int whichfork, xchk_da_btree_rec_fn scrub_fn, void *private); diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index 1c088cfba10e..2cfcf1c35679 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -484,18 +484,24 @@ xrep_dir_recover_data( while (offset < end) { struct xfs_dir2_data_unused *dup = bp->b_addr + offset; struct xfs_dir2_data_entry *dep = bp->b_addr + offset; + unsigned int advance; if (xchk_should_terminate(rd->sc, &error)) return error; /* Skip unused entries. */ if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { + if (!dup->length) + break; offset += be16_to_cpu(dup->length); continue; } /* Don't walk off the end of the block. */ - offset += xfs_dir2_data_entsize(rd->sc->mp, dep->namelen); + advance = xfs_dir2_data_entsize(rd->sc->mp, dep->namelen); + if (!advance) + break; + offset += advance; if (offset > end) break; @@ -721,7 +727,7 @@ xrep_dir_replay_removename( const struct xfs_name *name, xfs_extlen_t total) { - struct xfs_inode *dp = rd->args.dp; + struct xfs_inode *dp = rd->sc->tempip; ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); @@ -1375,9 +1381,24 @@ xrep_dir_live_update( if (p->delta > 0) error = xrep_dir_stash_createname(rd, p->name, I_INO(p->ip)); - else - error = xrep_dir_stash_removename(rd, p->name, + else { + /* + * xfs_dentry_to_name in unlink or rename-exchange can + * pass us names with ftype FT_UNKNOWN, but we really + * must know the ftype of the child that is being + * removed so that we can do nlink updates correctly + * without holding inode references. + */ + struct xfs_name name = { + .name = p->name->name, + .len = p->name->len, + .type = xfs_mode_to_ftype( + VFS_IC(p->ip)->i_mode), + }; + + error = xrep_dir_stash_removename(rd, &name, I_INO(p->ip)); + } mutex_unlock(&rd->pscan.lock); if (error) goto out_abort; @@ -1467,7 +1488,7 @@ xrep_dir_swap_prep( .geo = sc->mp->m_dir_geo, .whichfork = XFS_DATA_FORK, .trans = sc->tp, - .total = 1, + .total = xfs_dabuf_nfsb(sc->mp, XFS_DATA_FORK), .owner = I_INO(sc->ip), }; diff --git a/fs/xfs/scrub/dirtree.c b/fs/xfs/scrub/dirtree.c index b2cf6e5439d9..9b0ab2316612 100644 --- a/fs/xfs/scrub/dirtree.c +++ b/fs/xfs/scrub/dirtree.c @@ -259,6 +259,7 @@ xchk_dirtree_create_path( dl->nr_paths++; return 0; out_path: + xino_bitmap_destroy(&path->seen_inodes); kfree(path); return error; } @@ -368,12 +369,38 @@ xchk_dirpath_step_up( struct xfs_inode *dp; xfs_ino_t parent_ino = be64_to_cpu(dl->pptr_rec.p_ino); unsigned int lock_mode; - int error; + int error = 0; + + if (xchk_should_terminate(sc, &error)) + return error; /* Grab and lock the parent directory. */ error = xchk_iget(sc, parent_ino, &dp); - if (error) + switch (error) { + case -EINVAL: + case -ENOENT: + mutex_lock(&dl->lock); + + if (dl->stale) { + /* live update detected a change in this path */ + error = -ESTALE; + } else { + /* inode doesn't exist, path invalid */ + error = -EFSCORRUPTED; + + trace_xchk_dirpath_badino(dl->sc, path->path_nr, + path->nr_steps, &dl->xname, + &dl->pptr_rec); + } + + mutex_unlock(&dl->lock); + return error; + case 0: + /* keep going */ + break; + default: return error; + } lock_mode = xfs_ilock_attr_map_shared(dp); mutex_lock(&dl->lock); diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c index bbf6acf6fd40..1d1eafcf6eb5 100644 --- a/fs/xfs/scrub/dirtree_repair.c +++ b/fs/xfs/scrub/dirtree_repair.c @@ -479,6 +479,7 @@ again: } if (xfs_has_parent(sc->mp)) { + memset(&dl->ppargs, 0, sizeof(dl->ppargs)); error = xfs_parent_removename(sc->tp, &dl->ppargs, dp, &dl->xname, sc->ip); if (error) @@ -618,6 +619,7 @@ xrep_dirtree_create_adoption_path( return 0; out_path: + xino_bitmap_destroy(&path->seen_inodes); kfree(path); return error; } diff --git a/fs/xfs/scrub/findparent.c b/fs/xfs/scrub/findparent.c index 04b6b96b0a30..eab3ac2704be 100644 --- a/fs/xfs/scrub/findparent.c +++ b/fs/xfs/scrub/findparent.c @@ -139,32 +139,52 @@ xrep_findparent_dirent( return 0; } -/* - * If this is a directory, walk the dirents looking for any that point to the - * scrub target inode. - */ -STATIC int -xrep_findparent_walk_directory( - struct xrep_findparent_info *fpi) +static inline bool +xrep_findparent_want_scan_file( + const struct xrep_findparent_info *fpi) { - struct xfs_scrub *sc = fpi->sc; - struct xfs_inode *dp = fpi->dp; - unsigned int lock_mode; - int error = 0; + const struct xfs_scrub *sc = fpi->sc; + const struct xfs_inode *dp = fpi->dp; + + /* Only directories can be parents */ + if (!S_ISDIR(VFS_IC(dp)->i_mode)) + return false; /* * The inode being scanned cannot be its own parent, nor can any * temporary directory we created to stage this repair. */ if (dp == sc->ip || dp == sc->tempip) - return 0; + return false; /* * Similarly, temporary files created to stage a repair cannot be the * parent of this inode. */ if (xrep_is_tempfile(dp)) + return false; + + return true; +} + +/* + * If this is a directory, walk the dirents looking for any that point to the + * scrub target inode. + */ +STATIC int +xrep_findparent_walk_file( + struct xrep_findparent_info *fpi) +{ + struct xfs_scrub *sc = fpi->sc; + struct xfs_inode *dp = fpi->dp; + unsigned int lock_mode; + int error = 0; + + if (!xrep_findparent_want_scan_file(fpi)) { + if (fpi->parent_scan) + xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp); return 0; + } /* * Scan the directory to see if there it contains an entry pointing to @@ -201,6 +221,8 @@ xrep_findparent_walk_directory( goto out_unlock; out_unlock: + if (fpi->parent_scan) + xchk_iscan_mark_visited(&fpi->parent_scan->iscan, dp); xfs_iunlock(dp, lock_mode); return error; } @@ -308,11 +330,7 @@ xrep_findparent_scan( ASSERT(S_ISDIR(VFS_IC(sc->ip)->i_mode)); while ((ret = xchk_iscan_iter(&pscan->iscan, &fpi.dp)) == 1) { - if (S_ISDIR(VFS_I(fpi.dp)->i_mode)) - ret = xrep_findparent_walk_directory(&fpi); - else - ret = 0; - xchk_iscan_mark_visited(&pscan->iscan, fpi.dp); + ret = xrep_findparent_walk_file(&fpi); xchk_irele(sc, fpi.dp); if (ret) break; @@ -401,7 +419,7 @@ xrep_findparent_confirm( goto out_rele; } - error = xrep_findparent_walk_directory(&fpi); + error = xrep_findparent_walk_file(&fpi); if (error) goto out_rele; diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c index 19c0b1b2a787..9270ad075fe0 100644 --- a/fs/xfs/scrub/ialloc.c +++ b/fs/xfs/scrub/ialloc.c @@ -85,6 +85,8 @@ xchk_inobt_xref_finobt( goto no_record; error = xfs_inobt_get_rec(cur, &frec, &has_record); + if (error) + return error; if (!has_record) return -EFSCORRUPTED; @@ -188,6 +190,8 @@ xchk_finobt_xref_inobt( goto no_record; error = xfs_inobt_get_rec(cur, &irec, &has_record); + if (error) + return error; if (!has_record) return -EFSCORRUPTED; diff --git a/fs/xfs/scrub/metapath.c b/fs/xfs/scrub/metapath.c index ff1ff762b300..e0ee7d9b903f 100644 --- a/fs/xfs/scrub/metapath.c +++ b/fs/xfs/scrub/metapath.c @@ -23,6 +23,7 @@ #include "xfs_rtgroup.h" #include "xfs_rtrmap_btree.h" #include "xfs_rtrefcount_btree.h" +#include "xfs_ag.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -348,12 +349,78 @@ out_cancel: } #ifdef CONFIG_XFS_ONLINE_REPAIR +/* + * Given a directory @dp, an existing inode @ip, and a @name, link @ip into @dp + * under the given @name. + */ +static int +xrep_metadir_add_child( + struct xchk_metapath *mpath, + xfs_ino_t old_dotdot) +{ + struct xfs_trans *tp = mpath->sc->tp; + struct xfs_dir_update *du = &mpath->du; + struct xfs_inode *dp = du->dp; + const struct xfs_name *name = du->name; + struct xfs_inode *ip = du->ip; + struct xfs_mount *mp = tp->t_mountp; + const unsigned int resblks = mpath->link_resblks; + int error; + + /* + * The metadata file shouldn't be on the unlinked list, but we'll fix + * it if that is the case. + */ + if (VFS_I(ip)->i_nlink == 0) { + struct xfs_perag *pag; + + pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, I_INO(ip))); + error = xfs_iunlink_remove(tp, pag, ip); + xfs_perag_put(pag); + if (error) + return error; + } + + error = xfs_dir_createname(tp, dp, name, I_INO(ip), resblks); + if (error) + return error; + + xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); + + xfs_bumplink(tp, ip); + + /* update dotdot entry in child */ + if (S_ISDIR(VFS_I(ip)->i_mode)) { + xfs_bumplink(tp, dp); + + /* Replace the dotdot entry in the child */ + if (old_dotdot != I_INO(dp)) { + error = xfs_dir_replace(tp, ip, &xfs_name_dotdot, + I_INO(dp), resblks); + if (error) + return error; + } + } + + /* Update the child's parent pointer */ + if (du->ppargs) { + error = xfs_parent_addname(tp, du->ppargs, dp, name, ip); + if (error) + return error; + } + + xfs_dir_update_hook(dp, ip, 1, name); + return 0; +} + /* Create the dirent represented by the final component of the path. */ STATIC int xrep_metapath_link( struct xchk_metapath *mpath) { struct xfs_scrub *sc = mpath->sc; + xfs_ino_t old_dotdot = NULLFSINO; + int error; mpath->du.dp = mpath->dp; mpath->du.name = &mpath->xname; @@ -366,7 +433,21 @@ xrep_metapath_link( trace_xrep_metapath_link(sc, mpath->path, mpath->dp, I_INO(sc->ip)); - return xfs_dir_add_child(sc->tp, mpath->link_resblks, &mpath->du); + if (S_ISDIR(VFS_I(sc->ip)->i_mode)) { + error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, + &old_dotdot); + if (error && error != -ENOENT) + return error; + + /* + * subdir didn't give us a dotdot entry, so we just give up + * and let the repair get marked as failed. + */ + if (old_dotdot == NULLFSINO) + return 0; + } + + return xrep_metadir_add_child(mpath, old_dotdot); } /* Remove the dirent at the final component of the path. */ @@ -397,7 +478,7 @@ xrep_metapath_unlink( /* Figure out if we're removing a parent pointer too. */ if (xfs_has_parent(mp)) { - xfs_inode_to_parent_rec(&rec, ip); + xfs_inode_to_parent_rec(&rec, mpath->dp); error = xfs_parent_lookup(sc->tp, ip, &mpath->xname, &rec, &mpath->pptr_args); switch (error) { @@ -556,6 +637,8 @@ xrep_metapath_try_unlink( error = xchk_metapath_ilock_parent_and_child(mpath, ip); if (error) { xchk_trans_cancel(sc); + if (ip) + xchk_irele(sc, ip); return error; } xfs_trans_ijoin(sc->tp, mpath->dp, 0); diff --git a/fs/xfs/scrub/quota_repair.c b/fs/xfs/scrub/quota_repair.c index 487bd4f68ebb..59302e8afc7e 100644 --- a/fs/xfs/scrub/quota_repair.c +++ b/fs/xfs/scrub/quota_repair.c @@ -325,7 +325,7 @@ xrep_quota_block( * If there's nothing that would impede a dqiterate, we're * done. */ - if ((ddq->d_type & XFS_DQTYPE_REC_MASK) != dqtype || + if ((ddq->d_type & XFS_DQTYPE_REC_MASK) == dqtype && id == be32_to_cpu(ddq->d_id)) { xfs_trans_brelse(sc->tp, bp); return 0; @@ -363,11 +363,18 @@ xrep_quota_block( ddq->d_rtbcount, &ddq->d_rtbtimer, defq->rtb.time); + /* + * This transaction operates on raw disk buffers, so we don't + * have a dquot log item to assign the LSN for us. Instead, + * set it to zero so that log recovery will always replay any + * logged dquot item atop this buffer. + */ + dqblk->dd_lsn = 0; + /* We only support v5 filesystems so always set these. */ uuid_copy(&dqblk->dd_uuid, &sc->mp->m_sb.sb_meta_uuid); xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk), XFS_DQUOT_CRC_OFF); - dqblk->dd_lsn = 0; } switch (dqtype) { case XFS_DQTYPE_USER: @@ -455,8 +462,7 @@ xrep_quota_data_fork( if (truncate) { /* Erase everything after the block containing the max dquot */ - error = xfs_bunmapi_range(&sc->tp, sc->ip, 0, - max_dqid_off * sc->mp->m_sb.sb_blocksize, + error = xfs_bunmapi_range(&sc->tp, sc->ip, 0, max_dqid_off + 1, XFS_MAX_FILEOFF); if (error) goto out; diff --git a/fs/xfs/scrub/quotacheck.c b/fs/xfs/scrub/quotacheck.c index c199d128538e..c32030a05440 100644 --- a/fs/xfs/scrub/quotacheck.c +++ b/fs/xfs/scrub/quotacheck.c @@ -263,8 +263,10 @@ xqcheck_mod_live_ino_dqtrx( dqa->tx_id = p->tx_id; error = rhashtable_insert_fast(&xqc->shadow_dquot_acct, &dqa->hash, xqcheck_dqacct_hash_params); - if (error) + if (error) { + kfree(dqa); goto out_abort; + } } /* Find the shadow dqtrx (or an empty slot) here. */ diff --git a/fs/xfs/scrub/reap.c b/fs/xfs/scrub/reap.c index fcd14c1703ea..f698b9be3dd1 100644 --- a/fs/xfs/scrub/reap.c +++ b/fs/xfs/scrub/reap.c @@ -601,7 +601,7 @@ xreap_configure_agextent_limits( /* Maximum overhead of invalidating one buffer. */ const unsigned int per_binval = - xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval)); + xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval)); /* * For each transaction in a reap chain, we can delete some number of @@ -680,7 +680,7 @@ xreap_configure_agcow_limits( /* Overhead of invalidating one buffer */ const unsigned int per_binval = - xfs_buf_inval_log_space(1, XFS_B_TO_FSBT(mp, max_binval)); + xfs_buf_inval_log_space(1, XFS_FSB_TO_B(mp, max_binval)); /* * For each transaction in a reap chain, we can delete some number of @@ -1399,7 +1399,7 @@ xreap_bmapi_binval( * far we've gotten. */ if (!xreap_inc_binval(rs)) { - imap->br_blockcount = agbno_next - bno; + imap->br_blockcount = bno - agbno; goto out; } } diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c index 4e1bf23e5b89..f8c51d8fbb3d 100644 --- a/fs/xfs/scrub/refcount.c +++ b/fs/xfs/scrub/refcount.c @@ -410,7 +410,7 @@ xchk_refcount_mergeable( const struct xfs_refcount_irec *r1 = &rrc->prev_rec; /* Ignore if prev_rec is not yet initialized. */ - if (r1->rc_blockcount > 0) + if (r1->rc_blockcount == 0) return false; if (r1->rc_domain != r2->rc_domain) @@ -581,8 +581,12 @@ xchk_xref_is_cow_staging( if (rc.rc_domain != XFS_REFC_DOMAIN_COW) xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); + /* Can't start after bno */ + if (rc.rc_startblock > agbno) + xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); + /* Must be at least as long as what was passed in */ - if (rc.rc_blockcount < len) + if (rc.rc_startblock + rc.rc_blockcount < agbno + len) xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); } diff --git a/fs/xfs/scrub/rgsuper.c b/fs/xfs/scrub/rgsuper.c index 2bd2c0351b35..6e2abe5dc27c 100644 --- a/fs/xfs/scrub/rgsuper.c +++ b/fs/xfs/scrub/rgsuper.c @@ -36,8 +36,10 @@ xchk_rgsuperblock_xref( if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return; - xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), 1); - xchk_xref_is_only_rt_owned_by(sc, 0, 1, &XFS_RMAP_OINFO_FS); + xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), + sc->mp->m_sb.sb_rextsize); + xchk_xref_is_only_rt_owned_by(sc, 0, sc->mp->m_sb.sb_rextsize, + &XFS_RMAP_OINFO_FS); } int diff --git a/fs/xfs/scrub/rtrefcount.c b/fs/xfs/scrub/rtrefcount.c index 4e7c540c8d23..3d916d71a135 100644 --- a/fs/xfs/scrub/rtrefcount.c +++ b/fs/xfs/scrub/rtrefcount.c @@ -20,6 +20,7 @@ #include "xfs_metafile.h" #include "xfs_rtrefcount_btree.h" #include "xfs_rtalloc.h" +#include "xfs_ag.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" @@ -375,7 +376,7 @@ xchk_rtrefcount_mergeable( const struct xfs_refcount_irec *r1 = &rrc->prev_rec; /* Ignore if prev_rec is not yet initialized. */ - if (r1->rc_blockcount > 0) + if (r1->rc_blockcount == 0) return false; if (r1->rc_startblock + r1->rc_blockcount != r2->rc_startblock) @@ -428,7 +429,7 @@ static inline void xchk_rtrefcountbt_xref_gaps( struct xfs_scrub *sc, struct xchk_rtrefcbt_records *rrc, - xfs_rtblock_t bno) + xfs_rgblock_t bno) { struct xfs_rmap_irec low; struct xfs_rmap_irec high; @@ -504,30 +505,75 @@ xchk_rtrefcountbt_rec( return 0; } +/* Count the number of blocks used by the rtrefcount btree file in this AG. */ +static int +xchk_rtrefcount_count_agblocks( + struct xfs_scrub *sc, + xfs_agnumber_t agno, + const struct xfs_owner_info *btree_oinfo, + xfs_filblks_t *blocks) +{ + xfs_filblks_t agblocks = 0; + int error; + + error = xchk_ag_init_existing(sc, agno, &sc->sa); + if (error) + goto out_free; + + /* + * If we don't have an rmap cursor, we can't complete the cross + * referencing, so return EFSCORRUPTED to end the loop and trigger the + * XFAIL flag. + */ + if (!sc->sa.rmap_cur) { + error = -EFSCORRUPTED; + goto out_free; + } + + error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo, + &agblocks); + if (error) + goto out_free; + + *blocks += agblocks; +out_free: + xchk_ag_free(sc, &sc->sa); + return error; +} + /* Make sure we have as many refc blocks as the rmap says. */ STATIC void -xchk_refcount_xref_rmap( +xchk_rtrefcount_xref_rmap( struct xfs_scrub *sc, const struct xfs_owner_info *btree_oinfo, xfs_extlen_t cow_blocks) { xfs_filblks_t refcbt_blocks = 0; - xfs_filblks_t blocks; - int error; + xfs_filblks_t blocks = 1; /* one for the iroot */ + xfs_agnumber_t agno; + int error = 0; - if (!sc->sr.rmap_cur || !sc->sa.rmap_cur || xchk_skip_xref(sc->sm)) + if (!xfs_has_rmapbt(sc->mp) || xchk_skip_xref(sc->sm)) return; /* Check that we saw as many refcbt blocks as the rmap knows about. */ error = xfs_btree_count_blocks(sc->sr.refc_cur, &refcbt_blocks); if (!xchk_btree_process_error(sc, sc->sr.refc_cur, 0, &error)) return; - error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur, btree_oinfo, - &blocks); - if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur)) + + for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) { + error = xchk_rtrefcount_count_agblocks(sc, agno, btree_oinfo, + &blocks); + if (error) + break; + } + if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) return; if (blocks != refcbt_blocks) - xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); + xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0); + + if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm)) + return; /* Check that we saw as many cow blocks as the rmap knows about. */ error = xchk_count_rmap_ownedby_ag(sc, sc->sr.rmap_cur, @@ -538,7 +584,7 @@ xchk_refcount_xref_rmap( xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0); } -/* Scrub the refcount btree for some AG. */ +/* Scrub the refcount btree for some rtgroup. */ int xchk_rtrefcountbt( struct xfs_scrub *sc) @@ -564,11 +610,11 @@ xchk_rtrefcountbt( /* * Check that all blocks between the last refcount > 1 record and the - * end of the rt volume have at most one reverse mapping. + * end of the rtgroup have at most one reverse mapping. */ - xchk_rtrefcountbt_xref_gaps(sc, &rrc, sc->mp->m_sb.sb_rblocks); - - xchk_refcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks); + xchk_rtrefcountbt_xref_gaps(sc, &rrc, + xfs_rtx_to_rgbno(sc->sr.rtg, sc->mp->m_sb.sb_rgextents)); + xchk_rtrefcount_xref_rmap(sc, &btree_oinfo, rrc.cow_blocks); return 0; } @@ -609,8 +655,12 @@ xchk_xref_is_rt_cow_staging( if (rc.rc_domain != XFS_REFC_DOMAIN_COW) xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); + /* Can't start after bno */ + if (rc.rc_startblock > bno) + xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); + /* Must be at least as long as what was passed in */ - if (rc.rc_blockcount < len) + if (rc.rc_startblock + rc.rc_blockcount < bno + len) xchk_btree_xref_set_corrupt(sc, sc->sr.refc_cur, 0); } diff --git a/fs/xfs/scrub/rtsummary_repair.c b/fs/xfs/scrub/rtsummary_repair.c index f065c3e51ce2..ed763290aec1 100644 --- a/fs/xfs/scrub/rtsummary_repair.c +++ b/fs/xfs/scrub/rtsummary_repair.c @@ -164,9 +164,10 @@ xrep_rtsummary( /* * Now exchange the contents. Nothing in repair uses the temporary * buffer, so we can reuse it for the tempfile exchrange information. + * Use XFS_MAX_FILEOFF here so that we correct the rtsummary file size. */ error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, 0, - rts->rsumblocks, &rts->tempexch); + XFS_MAX_FILEOFF, &rts->tempexch); if (error) return error; diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 8742445c86f4..12c228b7f477 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -765,8 +765,7 @@ out_nofix: out_teardown: error = xchk_teardown(sc, error); out_sc: - if (error != -ENOENT) - xchk_stats_merge(mp, sm, &run); + xchk_stats_merge(mp, sm, error, &run); kfree(sc); out: trace_xchk_done(XFS_I(file_inode(file)), sm, error); diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h index 6d7d3523b71f..737a5d6db15f 100644 --- a/fs/xfs/scrub/scrub.h +++ b/fs/xfs/scrub/scrub.h @@ -261,7 +261,6 @@ static inline int xchk_nothing(struct xfs_scrub *sc) } /* Metadata scrubbers */ -int xchk_tester(struct xfs_scrub *sc); int xchk_superblock(struct xfs_scrub *sc); int xchk_agf(struct xfs_scrub *sc); int xchk_agfl(struct xfs_scrub *sc); diff --git a/fs/xfs/scrub/stats.c b/fs/xfs/scrub/stats.c index ef3f6abdb706..3339cae4b39d 100644 --- a/fs/xfs/scrub/stats.c +++ b/fs/xfs/scrub/stats.c @@ -29,6 +29,7 @@ struct xchk_scrub_stats { uint32_t incomplete; uint32_t warning; uint32_t retries; + uint32_t runtime_errors; /* repair stats */ uint32_t repair_invocations; @@ -84,6 +85,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = { [XFS_SCRUB_TYPE_RGSUPER] = "rgsuper", [XFS_SCRUB_TYPE_RTRMAPBT] = "rtrmapbt", [XFS_SCRUB_TYPE_RTREFCBT] = "rtrefcountbt", + [XFS_SCRUB_TYPE_HEALTHY] = "healthy", }; /* Format the scrub stats into a text buffer, similar to pcp style. */ @@ -99,25 +101,32 @@ xchk_stats_format( int ret = 0; for (i = 0; i < XFS_SCRUB_TYPE_NR; i++, css++) { + struct xchk_scrub_stats fss; + if (!name_map[i]) continue; + spin_lock(&css->css_lock); + memcpy(&fss, css, offsetof(struct xchk_scrub_stats, css_lock)); + spin_unlock(&css->css_lock); + ret = scnprintf(buf, remaining, - "%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu\n", + "%s %u %u %u %u %u %u %u %u %u %llu %u %u %llu %u\n", name_map[i], - (unsigned int)css->invocations, - (unsigned int)css->clean, - (unsigned int)css->corrupt, - (unsigned int)css->preen, - (unsigned int)css->xfail, - (unsigned int)css->xcorrupt, - (unsigned int)css->incomplete, - (unsigned int)css->warning, - (unsigned int)css->retries, - (unsigned long long)css->checktime_us, - (unsigned int)css->repair_invocations, - (unsigned int)css->repair_success, - (unsigned long long)css->repairtime_us); + (unsigned int)fss.invocations, + (unsigned int)fss.clean, + (unsigned int)fss.corrupt, + (unsigned int)fss.preen, + (unsigned int)fss.xfail, + (unsigned int)fss.xcorrupt, + (unsigned int)fss.incomplete, + (unsigned int)fss.warning, + (unsigned int)fss.retries, + (unsigned long long)fss.checktime_us, + (unsigned int)fss.repair_invocations, + (unsigned int)fss.repair_success, + (unsigned long long)fss.repairtime_us, + (unsigned int)fss.runtime_errors); if (ret <= 0) break; @@ -188,31 +197,41 @@ STATIC void xchk_stats_merge_one( struct xchk_stats *cs, const struct xfs_scrub_metadata *sm, + int error, const struct xchk_stats_run *run) { struct xchk_scrub_stats *css; + unsigned int sm_flags = sm->sm_flags; if (sm->sm_type >= XFS_SCRUB_TYPE_NR) { ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR); return; } + /* caller applies this same transformation after we return */ + if (error == -EFSCORRUPTED || error == -EFSBADCRC) { + sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; + error = 0; + } + css = &cs->cs_stats[sm->sm_type]; spin_lock(&css->css_lock); css->invocations++; - if (!(sm->sm_flags & XFS_SCRUB_OFLAG_UNCLEAN)) + if (error) + css->runtime_errors++; + else if (!(sm_flags & XFS_SCRUB_OFLAG_UNCLEAN)) css->clean++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + if (sm_flags & XFS_SCRUB_OFLAG_CORRUPT) css->corrupt++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_PREEN) + if (sm_flags & XFS_SCRUB_OFLAG_PREEN) css->preen++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL) + if (sm_flags & XFS_SCRUB_OFLAG_XFAIL) css->xfail++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT) + if (sm_flags & XFS_SCRUB_OFLAG_XCORRUPT) css->xcorrupt++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE) + if (sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE) css->incomplete++; - if (sm->sm_flags & XFS_SCRUB_OFLAG_WARNING) + if (sm_flags & XFS_SCRUB_OFLAG_WARNING) css->warning++; css->retries += run->retries; css->checktime_us += howmany_64(run->scrub_ns, NSEC_PER_USEC); @@ -230,10 +249,14 @@ void xchk_stats_merge( struct xfs_mount *mp, const struct xfs_scrub_metadata *sm, + int error, const struct xchk_stats_run *run) { - xchk_stats_merge_one(&global_stats, sm, run); - xchk_stats_merge_one(mp->m_scrub_stats, sm, run); + if (error == -ENOENT) + return; + + xchk_stats_merge_one(&global_stats, sm, error, run); + xchk_stats_merge_one(mp->m_scrub_stats, sm, error, run); } /* debugfs boilerplate */ diff --git a/fs/xfs/scrub/stats.h b/fs/xfs/scrub/stats.h index b358ad8d8b90..221052b95dd0 100644 --- a/fs/xfs/scrub/stats.h +++ b/fs/xfs/scrub/stats.h @@ -27,7 +27,7 @@ void xchk_stats_register(struct xchk_stats *cs, struct dentry *parent); void xchk_stats_unregister(struct xchk_stats *cs); void xchk_stats_merge(struct xfs_mount *mp, const struct xfs_scrub_metadata *sm, - const struct xchk_stats_run *run); + int error, const struct xchk_stats_run *run); static inline u64 xchk_stats_now(void) { return ktime_get_ns(); } static inline u64 xchk_stats_elapsed_ns(u64 since) @@ -53,7 +53,7 @@ static inline u64 xchk_stats_elapsed_ns(u64 since) # define xchk_stats_unregister(cs) ((void)0) # define xchk_stats_now() (0) # define xchk_stats_elapsed_ns(x) (0 * (x)) -# define xchk_stats_merge(mp, sm, run) ((void)0) +# define xchk_stats_merge(mp, sm, error, run) ((void)0) #endif /* CONFIG_XFS_ONLINE_SCRUB_STATS */ #endif /* __XFS_SCRUB_STATS_H__ */ diff --git a/fs/xfs/scrub/symlink_repair.c b/fs/xfs/scrub/symlink_repair.c index 91c86ea0e0f1..181961364233 100644 --- a/fs/xfs/scrub/symlink_repair.c +++ b/fs/xfs/scrub/symlink_repair.c @@ -291,7 +291,7 @@ xrep_symlink_swap_prep( if (error) return error; - xfs_trans_log_inode(sc->tp, sc->ip, 0); + xfs_trans_log_inode(sc->tp, sc->tempip, logflags); error = xfs_defer_finish(&sc->tp); if (error) diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index 98820003b929..59a9213a3c7d 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -649,6 +649,19 @@ xrep_tempexch_prep_request( return 0; } +static inline unsigned int +xrep_tempexch_estimate_sf_resblks( + struct xfs_scrub *sc, + int whichfork) +{ + /* repairing a symlink target */ + if (S_ISLNK(VFS_I(sc->ip)->i_mode) && whichfork == XFS_DATA_FORK) + return 1; + + /* everything else is a directory or an xattr structure */ + return xfs_dabuf_nfsb(sc->mp, whichfork); +} + /* * Fill out the mapping exchange resource estimation structures in preparation * for exchanging the contents of a metadata file that we've rebuilt in the @@ -663,6 +676,8 @@ xrep_tempexch_estimate( struct xfs_ifork *ifp; struct xfs_ifork *tifp; int whichfork = xfs_exchmaps_reqfork(req); + unsigned int sf_resblks = + xrep_tempexch_estimate_sf_resblks(sc, whichfork); int state = 0; /* @@ -693,9 +708,9 @@ xrep_tempexch_estimate( * plus the block we converted. */ req->ip1_bcount = sc->tempip->i_nblocks; - req->ip2_bcount = 1; + req->ip2_bcount = sf_resblks; req->nr_exchanges = 1 + tifp->if_nextents; - req->resblks = 1; + req->resblks = sf_resblks; break; case 2: /* @@ -707,10 +722,10 @@ xrep_tempexch_estimate( * is (worst case) the extent count of the file being repaired * plus the block we converted. */ - req->ip1_bcount = 1; + req->ip1_bcount = sf_resblks; req->ip2_bcount = sc->ip->i_nblocks; req->nr_exchanges = 1 + ifp->if_nextents; - req->resblks = 1; + req->resblks = sf_resblks; break; case 3: /* @@ -722,10 +737,10 @@ xrep_tempexch_estimate( * fileoff 0. Presumably, the caller could not exchange the * two inode fork areas directly. */ - req->ip1_bcount = 1; - req->ip2_bcount = 1; + req->ip1_bcount = sf_resblks; + req->ip2_bcount = sf_resblks; req->nr_exchanges = 1; - req->resblks = 2; + req->resblks = 2 * sf_resblks; break; } diff --git a/fs/xfs/scrub/tempfile.h b/fs/xfs/scrub/tempfile.h index 71c1b54599c3..d44ed43bafe0 100644 --- a/fs/xfs/scrub/tempfile.h +++ b/fs/xfs/scrub/tempfile.h @@ -39,10 +39,6 @@ int xrep_tempfile_roll_trans(struct xfs_scrub *sc); void xrep_tempfile_copyout_local(struct xfs_scrub *sc, int whichfork); bool xrep_is_tempfile(const struct xfs_inode *ip); #else -static inline void xrep_tempfile_iolock_both(struct xfs_scrub *sc) -{ - xchk_ilock(sc, XFS_IOLOCK_EXCL); -} # define xrep_is_tempfile(ip) (false) # define xrep_tempfile_adjust_directory_tree(sc) (0) # define xrep_tempfile_rele(sc) diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 14aa0ec1f09e..0f5adc293962 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -1640,7 +1640,7 @@ DECLARE_EVENT_CLASS(xchk_pptr_class, __entry->dev = ip->i_mount->m_super->s_dev; __entry->ino = I_INO(ip); __entry->namelen = name->len; - memcpy(__get_str(name), name, name->len); + memcpy(__get_str(name), name->name, name->len); __entry->far_ino = far_ino; ), TP_printk("dev %d:%d ino 0x%llx name '%.*s' far_ino 0x%llx", @@ -1706,6 +1706,39 @@ DEFINE_EVENT(xchk_dirtree_class, name, \ DEFINE_XCHK_DIRTREE_EVENT(xchk_dirtree_create_path); DEFINE_XCHK_DIRTREE_EVENT(xchk_dirpath_walk_upwards); +TRACE_EVENT(xchk_dirpath_badino, + TP_PROTO(struct xfs_scrub *sc, unsigned int path_nr, + unsigned int step_nr, const struct xfs_name *name, + const struct xfs_parent_rec *pptr), + TP_ARGS(sc, path_nr, step_nr, name, pptr), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, path_nr) + __field(unsigned int, step_nr) + __field(xfs_ino_t, parent_ino) + __field(unsigned int, parent_gen) + __field(unsigned int, namelen) + __dynamic_array(char, name, name->len) + ), + TP_fast_assign( + __entry->dev = sc->mp->m_super->s_dev; + __entry->path_nr = path_nr; + __entry->step_nr = step_nr; + __entry->parent_ino = be64_to_cpu(pptr->p_ino); + __entry->parent_gen = be32_to_cpu(pptr->p_gen); + __entry->namelen = name->len; + memcpy(__get_str(name), name->name, name->len); + ), + TP_printk("dev %d:%d path %u step %u parent_ino 0x%llx parent_gen 0x%x name '%.*s'", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->path_nr, + __entry->step_nr, + __entry->parent_ino, + __entry->parent_gen, + __entry->namelen, + __get_str(name)) +); + DECLARE_EVENT_CLASS(xchk_dirpath_class, TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip, unsigned int path_nr, unsigned int step_nr, diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 74a6089abadf..8b6119776fb3 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -20,6 +20,7 @@ #include "xfs_errortag.h" #include "xfs_error.h" #include "xfs_icache.h" +#include "xfs_ioend.h" #include "xfs_zone_alloc.h" #include "xfs_rtgroup.h" #include <linux/bio-integrity.h> @@ -37,15 +38,6 @@ XFS_WPC(struct iomap_writepage_ctx *ctx) } /* - * Fast and loose check if this write could update the on-disk inode size. - */ -static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend) -{ - return ioend->io_offset + ioend->io_size > - XFS_I(ioend->io_inode)->i_disk_size; -} - -/* * Update on-disk file size now that data has been written to disk. */ int @@ -80,175 +72,6 @@ xfs_setfilesize( return xfs_trans_commit(tp); } -static void -xfs_ioend_put_open_zones( - struct iomap_ioend *ioend) -{ - struct iomap_ioend *tmp; - - /* - * Put the open zone for all ioends merged into this one (if any). - */ - list_for_each_entry(tmp, &ioend->io_list, io_list) - xfs_open_zone_put(tmp->io_private); - - /* - * The main ioend might not have an open zone if the submission failed - * before xfs_zone_alloc_and_submit got called. - */ - if (ioend->io_private) - xfs_open_zone_put(ioend->io_private); -} - -/* - * IO write completion. - */ -STATIC void -xfs_end_ioend_write( - struct iomap_ioend *ioend) -{ - struct xfs_inode *ip = XFS_I(ioend->io_inode); - struct xfs_mount *mp = ip->i_mount; - bool is_zoned = xfs_is_zoned_inode(ip); - xfs_off_t offset = ioend->io_offset; - size_t size = ioend->io_size; - unsigned int nofs_flag; - int error; - - /* - * We can allocate memory here while doing writeback on behalf of - * memory reclaim. To avoid memory allocation deadlocks set the - * task-wide nofs context for the following operations. - */ - nofs_flag = memalloc_nofs_save(); - - /* - * Just clean up the in-memory structures if the fs has been shut down. - */ - if (xfs_is_shutdown(mp)) { - error = -EIO; - goto done; - } - - /* - * Clean up all COW blocks and underlying data fork delalloc blocks on - * I/O error. The delalloc punch is required because this ioend was - * mapped to blocks in the COW fork and the associated pages are no - * longer dirty. If we don't remove delalloc blocks here, they become - * stale and can corrupt free space accounting on unmount. - */ - error = blk_status_to_errno(ioend->io_bio.bi_status); - if (unlikely(error)) { - /* - * Zoned writes update the in-core open zone accounting before - * I/O submission. A failed write leaves that state - * inconsistent, so shut down the filesystem instead of letting - * later writers wait forever for open zone space to become - * available. - */ - if (is_zoned) { - xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); - goto done; - } - if (ioend->io_flags & IOMAP_IOEND_SHARED) { - ASSERT(!is_zoned); - xfs_reflink_cancel_cow_range(ip, offset, size, true); - xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset, - offset + size, NULL); - } - goto done; - } - - /* - * Success: commit the COW or unwritten blocks if needed. - */ - if (is_zoned) - error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector, - ioend->io_private, NULLFSBLOCK); - else if (ioend->io_flags & IOMAP_IOEND_SHARED) - error = xfs_reflink_end_cow(ip, offset, size); - else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN) - error = xfs_iomap_write_unwritten(ip, offset, size, false); - - if (!error && - !(ioend->io_flags & IOMAP_IOEND_DIRECT) && - xfs_ioend_is_append(ioend)) - error = xfs_setfilesize(ip, offset, size); -done: - if (is_zoned) - xfs_ioend_put_open_zones(ioend); - iomap_finish_ioends(ioend, error); - memalloc_nofs_restore(nofs_flag); -} - -/* - * Finish all pending IO completions that require transactional modifications. - * - * We try to merge physical and logically contiguous ioends before completion to - * minimise the number of transactions we need to perform during IO completion. - * Both unwritten extent conversion and COW remapping need to iterate and modify - * one physical extent at a time, so we gain nothing by merging physically - * discontiguous extents here. - * - * The ioend chain length that we can be processing here is largely unbound in - * length and we may have to perform significant amounts of work on each ioend - * to complete it. Hence we have to be careful about holding the CPU for too - * long in this loop. - */ -void -xfs_end_io( - struct work_struct *work) -{ - struct xfs_inode *ip = - container_of(work, struct xfs_inode, i_ioend_work); - struct iomap_ioend *ioend; - struct list_head tmp; - unsigned long flags; - - spin_lock_irqsave(&ip->i_ioend_lock, flags); - list_replace_init(&ip->i_ioend_list, &tmp); - spin_unlock_irqrestore(&ip->i_ioend_lock, flags); - - iomap_sort_ioends(&tmp); - while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend, - io_list))) { - list_del_init(&ioend->io_list); - iomap_ioend_try_merge(ioend, &tmp); - if (bio_op(&ioend->io_bio) == REQ_OP_READ) - iomap_finish_ioends(ioend, - blk_status_to_errno(ioend->io_bio.bi_status)); - else - xfs_end_ioend_write(ioend); - cond_resched(); - } -} - -void -xfs_end_bio( - struct bio *bio) -{ - struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); - struct xfs_inode *ip = XFS_I(ioend->io_inode); - struct xfs_mount *mp = ip->i_mount; - unsigned long flags; - - /* - * For Appends record the actually written block number and set the - * boundary flag if needed. - */ - if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) { - ioend->io_sector = bio->bi_iter.bi_sector; - xfs_mark_rtg_boundary(ioend); - } - - spin_lock_irqsave(&ip->i_ioend_lock, flags); - if (list_empty(&ip->i_ioend_list)) - WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue, - &ip->i_ioend_work)); - list_add_tail(&ioend->io_list, &ip->i_ioend_list); - spin_unlock_irqrestore(&ip->i_ioend_lock, flags); -} - /* * We cannot cancel the ioend directly on error. We may have already set other * pages under writeback and hence we have to run I/O completion to mark the @@ -631,13 +454,8 @@ xfs_zoned_map_blocks( XFS_BMAPI_REMAP); xfs_iunlock(ip, XFS_ILOCK_EXCL); - wpc->iomap.type = IOMAP_MAPPED; - wpc->iomap.flags = IOMAP_F_DIRTY; - wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev; - wpc->iomap.offset = offset; - wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb); - wpc->iomap.flags = IOMAP_F_ANON_WRITE; - + xfs_iomap_set_anon_write(ip, &wpc->iomap, offset, + XFS_FSB_TO_B(mp, count_fsb)); trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length); return 0; } diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h index 5a7a0f1a0b49..d5ae5c9d4c26 100644 --- a/fs/xfs/xfs_aops.h +++ b/fs/xfs/xfs_aops.h @@ -10,6 +10,5 @@ extern const struct address_space_operations xfs_address_space_operations; extern const struct address_space_operations xfs_dax_aops; int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size); -void xfs_end_bio(struct bio *bio); #endif /* __XFS_AOPS_H__ */ diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index ee7c2e9c0340..8256c1d13ce2 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -139,7 +139,7 @@ xfs_buf_free( ASSERT(list_empty(&bp->b_lru)); if (!xfs_buftarg_is_mem(bp->b_target) && size >= PAGE_SIZE) - mm_account_reclaimed_pages(howmany(size, PAGE_SHIFT)); + mm_account_reclaimed_pages(howmany(size, PAGE_SIZE)); if (is_vmalloc_addr(bp->b_addr)) vfree(bp->b_addr); @@ -176,7 +176,7 @@ xfs_buf_alloc_kmem( ASSERT(is_power_of_2(size)); ASSERT(size < PAGE_SIZE); - bp->b_addr = kmalloc(size, gfp_mask); + bp->b_addr = kmalloc(size, gfp_mask | __GFP_RECLAIMABLE); if (!bp->b_addr) return -ENOMEM; diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 3159325dd17b..28c79989d725 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -60,7 +60,6 @@ static inline void xfs_buf_dquot_iodone(struct xfs_buf *bp) { } #endif /* CONFIG_XFS_QUOTA */ -void xfs_buf_iodone(struct xfs_buf *); bool xfs_buf_log_check_iovec(struct kvec *iovec); unsigned int xfs_buf_inval_log_space(unsigned int map_count, diff --git a/fs/xfs/xfs_exchmaps_item.c b/fs/xfs/xfs_exchmaps_item.c index c3745d33e54e..dd5d92ca1010 100644 --- a/fs/xfs/xfs_exchmaps_item.c +++ b/fs/xfs/xfs_exchmaps_item.c @@ -344,7 +344,17 @@ xfs_xmi_validate( if (!xfs_verify_fileext(mp, xlf->xmi_startoff1, xlf->xmi_blockcount)) return false; - return xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount); + if (!xfs_verify_fileext(mp, xlf->xmi_startoff2, xlf->xmi_blockcount)) + return false; + + if (xlf->xmi_flags & XFS_EXCHMAPS_SET_SIZES) { + if ((int64_t)xlf->xmi_isize1 < 0) + return false; + if ((int64_t)xlf->xmi_isize2 < 0) + return false; + } + + return true; } /* @@ -403,6 +413,13 @@ xfs_xmi_item_recover_intent( *ipp1 = ip1; *ipp2 = ip2; xmi = xfs_exchmaps_init_intent(req); + + /* Restore intended file sizes from recovered logged item */ + if (req->flags & XFS_EXCHMAPS_SET_SIZES) { + xmi->xmi_isize1 = xlf->xmi_isize1; + xmi->xmi_isize2 = xlf->xmi_isize2; + } + xfs_defer_add_item(dfp, &xmi->xmi_list); return xmi; diff --git a/fs/xfs/xfs_exchrange.c b/fs/xfs/xfs_exchrange.c index 94965a6c2187..c69ecd6a19de 100644 --- a/fs/xfs/xfs_exchrange.c +++ b/fs/xfs/xfs_exchrange.c @@ -504,6 +504,9 @@ xfs_exchange_range_finish( { int error; + if (fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN) + return 0; + error = file_remove_privs(fxr->file1); if (error) return error; @@ -783,9 +786,12 @@ xfs_exchange_range( if (ret) return ret; - fsnotify_modify(fxr->file1); - if (fxr->file2 != fxr->file1) - fsnotify_modify(fxr->file2); + if (!(fxr->flags & XFS_EXCHANGE_RANGE_DRY_RUN)) { + fsnotify_modify(fxr->file1); + if (fxr->file2 != fxr->file1) + fsnotify_modify(fxr->file2); + } + return 0; } diff --git a/fs/xfs/xfs_extent_busy.c b/fs/xfs/xfs_extent_busy.c index 41cf0605ec22..6da8c1f938aa 100644 --- a/fs/xfs/xfs_extent_busy.c +++ b/fs/xfs/xfs_extent_busy.c @@ -161,8 +161,8 @@ xfs_extent_busy_update_extent( xfs_agblock_t fbno, xfs_extlen_t flen, bool userdata) - __releases(&eb->eb_lock) - __acquires(&eb->eb_lock) + __releases(&xg->xg_busy_extents->eb_lock) + __acquires(&xg->xg_busy_extents->eb_lock) { struct xfs_extent_busy_tree *eb = xg->xg_busy_extents; xfs_agblock_t fend = fbno + flen; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 7bff07e31cbd..d8202da15aca 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -25,7 +25,7 @@ #include "xfs_iomap.h" #include "xfs_reflink.h" #include "xfs_file.h" -#include "xfs_aops.h" +#include "xfs_ioend.h" #include "xfs_zone_alloc.h" #include "xfs_error.h" #include "xfs_errortag.h" @@ -129,9 +129,8 @@ xfs_file_fsync( int datasync) { struct xfs_inode *ip = XFS_I(file->f_mapping->host); - struct xfs_mount *mp = ip->i_mount; - int error, err2; int log_flushed = 0; + int error; trace_xfs_file_fsync(ip); @@ -139,30 +138,22 @@ xfs_file_fsync( if (error) return error; - if (xfs_is_shutdown(mp)) + if (xfs_is_shutdown(ip->i_mount)) return -EIO; xfs_iflags_clear(ip, XFS_ITRUNCATED); /* - * If we have an RT and/or log subvolume we need to make sure to flush - * the write cache the device used for file data first. This is to - * ensure newly written file data make it to disk before logging the new - * inode size in case of an extending write. - */ - if (XFS_IS_REALTIME_INODE(ip) && mp->m_rtdev_targp != mp->m_ddev_targp) - error = blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev); - else if (mp->m_logdev_targp != mp->m_ddev_targp) - error = blkdev_issue_flush(mp->m_ddev_targp->bt_bdev); - - /* - * If the inode has a inode log item attached, it may need the journal - * flushed to persist any changes the log item might be tracking. + * If the inode has a log item attached, we must force the log up to the + * last LSN in which the inode was modified to ensure all metadata is + * persisted. The log force will flush the caches for all devices + * before writing the log records unless it is a no-op because there are + * no modifications to this inode that need to be pushed out. */ if (ip->i_itemp) { - err2 = xfs_fsync_flush_log(ip, datasync, &log_flushed); - if (err2 && !error) - error = err2; + error = xfs_fsync_flush_log(ip, datasync, &log_flushed); + if (error) + return error; } /* @@ -171,21 +162,11 @@ xfs_file_fsync( * when no metadata needed to be committed. * * Use the inode's actual file data target rather than assuming the - * main data device. Realtime inodes with a separate realtime device - * are flushed before the log force, so this fallback only applies - * when the file data target is the same as the log target. + * main data device. */ - if (!log_flushed) { - struct xfs_buftarg *file_targp = xfs_inode_buftarg(ip); - - if (mp->m_logdev_targp == file_targp) { - err2 = blkdev_issue_flush(file_targp->bt_bdev); - if (err2 && !error) - error = err2; - } - } - - return error; + if (!log_flushed) + return blkdev_issue_flush(xfs_inode_buftarg(ip)->bt_bdev); + return 0; } static int diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c index b6a3bc9f143c..041bb2105ec6 100644 --- a/fs/xfs/xfs_fsmap.c +++ b/fs/xfs/xfs_fsmap.c @@ -1174,8 +1174,7 @@ xfs_getfsmap( if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1])) return -EINVAL; - use_rmap = xfs_has_rmapbt(mp) && - has_capability_noaudit(current, CAP_SYS_ADMIN); + use_rmap = xfs_has_rmapbt(mp) && capable_noaudit(CAP_SYS_ADMIN); head->fmh_entries = 0; /* Set up our device handlers. */ diff --git a/fs/xfs/xfs_healthmon.c b/fs/xfs/xfs_healthmon.c index 4521ffdab9f1..c3749675ef19 100644 --- a/fs/xfs/xfs_healthmon.c +++ b/fs/xfs/xfs_healthmon.c @@ -87,12 +87,10 @@ xfs_healthmon_put( struct xfs_healthmon *hm) { if (refcount_dec_and_test(&hm->ref)) { - struct xfs_healthmon_event *event; - struct xfs_healthmon_event *next = hm->first_event; + struct xfs_healthmon_event *event, *s; - while ((event = next) != NULL) { + list_for_each_entry_safe(event, s, &hm->event_list, entry) { trace_xfs_healthmon_drop(hm, event); - next = event->next; kfree(event); } @@ -173,9 +171,13 @@ static inline void xfs_healthmon_bump_lost(struct xfs_healthmon *hm) */ static bool xfs_healthmon_merge_events( - struct xfs_healthmon_event *existing, + struct xfs_healthmon *hm, const struct xfs_healthmon_event *new) { + struct xfs_healthmon_event *existing = + list_last_entry_or_null(&hm->event_list, struct + xfs_healthmon_event, entry); + if (!existing) return false; @@ -192,7 +194,7 @@ xfs_healthmon_merge_events( case XFS_HEALTHMON_LOST: existing->lostcount += new->lostcount; - return true; + goto out_merge; case XFS_HEALTHMON_SICK: case XFS_HEALTHMON_CORRUPT: @@ -200,19 +202,19 @@ xfs_healthmon_merge_events( switch (existing->domain) { case XFS_HEALTHMON_FS: existing->fsmask |= new->fsmask; - return true; + goto out_merge; case XFS_HEALTHMON_AG: case XFS_HEALTHMON_RTGROUP: if (existing->group == new->group){ existing->grpmask |= new->grpmask; - return true; + goto out_merge; } return false; case XFS_HEALTHMON_INODE: if (existing->ino == new->ino && existing->gen == new->gen) { existing->imask |= new->imask; - return true; + goto out_merge; } return false; default: @@ -224,18 +226,18 @@ xfs_healthmon_merge_events( case XFS_HEALTHMON_SHUTDOWN: /* yes, we can race to shutdown */ existing->flags |= new->flags; - return true; + goto out_merge; case XFS_HEALTHMON_MEDIA_ERROR: /* physically adjacent errors can merge */ if (existing->daddr + existing->bbcount == new->daddr) { existing->bbcount += new->bbcount; - return true; + goto out_merge; } if (new->daddr + new->bbcount == existing->daddr) { existing->daddr = new->daddr; existing->bbcount += new->bbcount; - return true; + goto out_merge; } return false; @@ -250,63 +252,58 @@ xfs_healthmon_merge_events( if (existing->fpos + existing->flen == new->fpos) { existing->flen += new->flen; - return true; + goto out_merge; } if (new->fpos + new->flen == existing->fpos) { existing->fpos = new->fpos; existing->flen += new->flen; - return true; + goto out_merge; } return false; } return false; + +out_merge: + trace_xfs_healthmon_merge(hm, existing); + return true; } -/* Insert an event onto the start of the queue. */ +enum insert_where { + INSERT_HEAD, + INSERT_TAIL, +}; + +/* Add an event onto the start or the end of the queue. */ static inline void __xfs_healthmon_insert( struct xfs_healthmon *hm, + enum insert_where where, struct xfs_healthmon_event *event) { struct timespec64 now; + lockdep_assert_held(&hm->lock); + ktime_get_coarse_real_ts64(&now); event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec; - event->next = hm->first_event; - if (!hm->first_event) - hm->first_event = event; - if (!hm->last_event) - hm->last_event = event; - xfs_healthmon_bump_events(hm); - wake_up(&hm->wait); - - trace_xfs_healthmon_insert(hm, event); -} + switch (where) { + case INSERT_HEAD: + trace_xfs_healthmon_insert_head(hm, event); -/* Push an event onto the end of the queue. */ -static inline void -__xfs_healthmon_push( - struct xfs_healthmon *hm, - struct xfs_healthmon_event *event) -{ - struct timespec64 now; + list_add(&event->entry, &hm->event_list); + break; + case INSERT_TAIL: + trace_xfs_healthmon_insert_tail(hm, event); - ktime_get_coarse_real_ts64(&now); - event->time_ns = (now.tv_sec * NSEC_PER_SEC) + now.tv_nsec; + list_add_tail(&event->entry, &hm->event_list); + break; + } - if (!hm->first_event) - hm->first_event = event; - if (hm->last_event) - hm->last_event->next = event; - hm->last_event = event; - event->next = NULL; xfs_healthmon_bump_events(hm); wake_up(&hm->wait); - - trace_xfs_healthmon_push(hm, event); } /* Deal with any previously lost events */ @@ -321,8 +318,7 @@ xfs_healthmon_clear_lost_prev( }; struct xfs_healthmon_event *event = NULL; - if (xfs_healthmon_merge_events(hm->last_event, &lost_event)) { - trace_xfs_healthmon_merge(hm, hm->last_event); + if (xfs_healthmon_merge_events(hm, &lost_event)) { wake_up(&hm->wait); goto cleared; } @@ -330,10 +326,12 @@ xfs_healthmon_clear_lost_prev( if (hm->events < XFS_HEALTHMON_MAX_EVENTS) event = kmemdup(&lost_event, sizeof(struct xfs_healthmon_event), GFP_NOFS); - if (!event) + if (!event) { + xfs_healthmon_bump_lost(hm); return -ENOMEM; + } - __xfs_healthmon_push(hm, event); + __xfs_healthmon_insert(hm, INSERT_TAIL, event); cleared: hm->lost_prev_event = 0; return 0; @@ -369,8 +367,7 @@ xfs_healthmon_push( } /* Try to merge with the newest event */ - if (xfs_healthmon_merge_events(hm->last_event, template)) { - trace_xfs_healthmon_merge(hm, hm->last_event); + if (xfs_healthmon_merge_events(hm, template)) { wake_up(&hm->wait); goto out_unlock; } @@ -387,7 +384,7 @@ xfs_healthmon_push( goto out_unlock; } - __xfs_healthmon_push(hm, event); + __xfs_healthmon_insert(hm, INSERT_TAIL, event); out_unlock: mutex_unlock(&hm->lock); @@ -415,8 +412,10 @@ xfs_healthmon_unmount( * There's nothing actionable for userspace after an unmount. Once * we've inserted the unmount event, hm no longer owns that event. */ - __xfs_healthmon_insert(hm, hm->unmount_event); + mutex_lock(&hm->lock); + __xfs_healthmon_insert(hm, INSERT_HEAD, hm->unmount_event); hm->unmount_event = NULL; + mutex_unlock(&hm->lock); xfs_healthmon_detach(hm); xfs_healthmon_put(hm); @@ -738,6 +737,13 @@ static const unsigned int type_map[] = { [XFS_HEALTHMON_DATALOST] = XFS_HEALTH_MONITOR_TYPE_DATALOST, }; +static inline bool +xfs_healthmon_check_outbuffer_space(const struct xfs_healthmon *hm) +{ + return hm->bufhead + sizeof(struct xfs_health_monitor_event) <= + hm->bufsize; +} + /* Render event as a V0 structure */ STATIC int xfs_healthmon_format_v0( @@ -804,10 +810,10 @@ xfs_healthmon_format_v0( break; } - ASSERT(hm->bufhead + sizeof(hme) <= hm->bufsize); + ASSERT(xfs_healthmon_check_outbuffer_space(hm)); /* copy formatted object to the outbuf */ - if (hm->bufhead + sizeof(hme) <= hm->bufsize) { + if (xfs_healthmon_check_outbuffer_space(hm)) { memcpy(hm->buffer + hm->bufhead, &hme, sizeof(hme)); hm->bufhead += sizeof(hme); } @@ -890,15 +896,18 @@ xfs_healthmon_format_pop( { struct xfs_healthmon_event *event; - if (hm->bufhead + sizeof(*event) > hm->bufsize) + /* + * Don't bother if there's not enough space to format even one event in + * the outbuffer. + */ + if (!xfs_healthmon_check_outbuffer_space(hm)) return NULL; mutex_lock(&hm->lock); - event = hm->first_event; + event = list_first_entry_or_null(&hm->event_list, + struct xfs_healthmon_event, entry); if (event) { - if (hm->last_event == event) - hm->last_event = NULL; - hm->first_event = event->next; + list_del_init(&event->entry); hm->events--; trace_xfs_healthmon_pop(hm, event); @@ -1198,6 +1207,7 @@ xfs_ioc_health_monitor( return -ENOMEM; hm->dev = mp->m_super->s_dev; refcount_set(&hm->ref, 1); + INIT_LIST_HEAD(&hm->event_list); mutex_init(&hm->lock); init_waitqueue_head(&hm->wait); @@ -1213,7 +1223,9 @@ xfs_ioc_health_monitor( } running_event->type = XFS_HEALTHMON_RUNNING; running_event->domain = XFS_HEALTHMON_MOUNT; - __xfs_healthmon_insert(hm, running_event); + mutex_lock(&hm->lock); + __xfs_healthmon_insert(hm, INSERT_HEAD, running_event); + mutex_unlock(&hm->lock); /* * Preallocate the unmount event so that we can't fail to notify the diff --git a/fs/xfs/xfs_healthmon.h b/fs/xfs/xfs_healthmon.h index 0e936507037f..fa3deb187a2b 100644 --- a/fs/xfs/xfs_healthmon.h +++ b/fs/xfs/xfs_healthmon.h @@ -31,8 +31,7 @@ struct xfs_healthmon { struct mutex lock; /* list of event objects */ - struct xfs_healthmon_event *first_event; - struct xfs_healthmon_event *last_event; + struct list_head event_list; /* preallocated event for unmount */ struct xfs_healthmon_event *unmount_event; @@ -110,7 +109,7 @@ enum xfs_healthmon_domain { }; struct xfs_healthmon_event { - struct xfs_healthmon_event *next; + struct list_head entry; enum xfs_healthmon_type type; enum xfs_healthmon_domain domain; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 9d8dd30bd927..82dac88e3c4c 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -82,24 +82,20 @@ static inline xa_mark_t ici_tag_to_mark(unsigned int tag) /* * Allocate and initialise an xfs_inode. + * + * This can happen in context of already dirtied transactions, so the memory + * allocations must not fail. */ struct xfs_inode * xfs_inode_alloc( struct xfs_mount *mp, xfs_ino_t ino) { + gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL; struct xfs_inode *ip; - /* - * XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL - * and return NULL here on ENOMEM. - */ - ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, GFP_KERNEL | __GFP_NOFAIL); - - if (inode_init_always(mp->m_super, VFS_I(ip))) { - kmem_cache_free(xfs_inode_cache, ip); - return NULL; - } + ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, gfp); + inode_init_always_gfp(mp->m_super, VFS_I(ip), gfp); VFS_I(ip)->i_ino = ino; /* VFS doesn't initialise i_mode! */ @@ -501,7 +497,8 @@ xfs_iget_cache_hit( struct xfs_inode *ip, xfs_ino_t ino, int flags, - int lock_flags) __releases(RCU) + int lock_flags) + __releases_shared(RCU) { struct inode *inode = VFS_I(ip); struct xfs_mount *mp = ip->i_mount; diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 34c1038ebfcd..1602027cd0aa 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -585,7 +585,6 @@ uint xfs_ilock_attr_map_shared(struct xfs_inode *); int xfs_ifree(struct xfs_trans *, struct xfs_inode *); int xfs_itruncate_extents_flags(struct xfs_trans **, struct xfs_inode *, int, xfs_fsize_t, int); -void xfs_iext_realloc(xfs_inode_t *, int, int); int xfs_log_force_inode(struct xfs_inode *ip); void xfs_iunpin_wait(xfs_inode_t *); diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 1b53701bebea..96ca3e480cb9 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -647,7 +647,7 @@ xfs_ioctl_setattr_get_trans( goto out_error; error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp, - has_capability_noaudit(current, CAP_FOWNER), &tp); + capable_noaudit(CAP_FOWNER), &tp); if (error) goto out_error; diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c new file mode 100644 index 000000000000..40695d18dac0 --- /dev/null +++ b/fs/xfs/xfs_ioend.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2025 Christoph Hellwig. + * All Rights Reserved. + */ +#include "xfs_platform.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_log_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_inode.h" +#include "xfs_iomap.h" +#include "xfs_trace.h" +#include "xfs_bmap_util.h" +#include "xfs_reflink.h" +#include "xfs_zone_alloc.h" +#include "xfs_ioend.h" + +static void +xfs_ioend_put_open_zones( + struct iomap_ioend *ioend) +{ + struct iomap_ioend *tmp; + + /* + * Put the open zone for all ioends merged into this one (if any). + */ + list_for_each_entry(tmp, &ioend->io_list, io_list) + xfs_open_zone_put(tmp->io_private); + + /* + * The main ioend might not have an open zone if the submission failed + * before xfs_zone_alloc_and_submit got called. + */ + if (ioend->io_private) + xfs_open_zone_put(ioend->io_private); +} + +static void +xfs_end_ioend_write( + struct iomap_ioend *ioend) +{ + struct xfs_inode *ip = XFS_I(ioend->io_inode); + struct xfs_mount *mp = ip->i_mount; + bool is_zoned = xfs_is_zoned_inode(ip); + xfs_off_t offset = ioend->io_offset; + size_t size = ioend->io_size; + unsigned int nofs_flag; + int error; + + /* + * We can allocate memory here while doing writeback on behalf of + * memory reclaim. To avoid memory allocation deadlocks set the + * task-wide nofs context for the following operations. + */ + nofs_flag = memalloc_nofs_save(); + + /* + * Just clean up the in-memory structures if the fs has been shut down. + */ + if (xfs_is_shutdown(mp)) { + error = -EIO; + goto done; + } + + /* + * Clean up all COW blocks and underlying data fork delalloc blocks on + * I/O error. The delalloc punch is required because this ioend was + * mapped to blocks in the COW fork and the associated pages are no + * longer dirty. If we don't remove delalloc blocks here, they become + * stale and can corrupt free space accounting on unmount. + */ + error = blk_status_to_errno(ioend->io_bio.bi_status); + if (unlikely(error)) { + /* + * Zoned writes update the in-core open zone accounting before + * I/O submission. A failed write leaves that state + * inconsistent, so shut down the filesystem instead of letting + * later writers wait forever for open zone space to become + * available. + */ + if (is_zoned) { + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); + goto done; + } + if (ioend->io_flags & IOMAP_IOEND_SHARED) { + ASSERT(!is_zoned); + xfs_reflink_cancel_cow_range(ip, offset, size, true); + xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset, + offset + size, NULL); + } + goto done; + } + + /* + * Success: commit the COW or unwritten blocks if needed. + */ + if (is_zoned) + error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector, + ioend->io_private, NULLFSBLOCK); + else if (ioend->io_flags & IOMAP_IOEND_SHARED) + error = xfs_reflink_end_cow(ip, offset, size); + else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN) + error = xfs_iomap_write_unwritten(ip, offset, size, false); + + if (!error && + !(ioend->io_flags & IOMAP_IOEND_DIRECT) && + xfs_ioend_is_append(ioend)) + error = xfs_setfilesize(ip, offset, size); +done: + if (is_zoned) + xfs_ioend_put_open_zones(ioend); + iomap_finish_ioends(ioend, error); + memalloc_nofs_restore(nofs_flag); +} + +/* + * Finish all pending IO completions that require transactional modifications. + * + * We try to merge physical and logically contiguous ioends before completion to + * minimise the number of transactions we need to perform during IO completion. + * Both unwritten extent conversion and COW remapping need to iterate and modify + * one physical extent at a time, so we gain nothing by merging physically + * discontiguous extents here. + * + * The ioend chain length that we can be processing here is largely unbound in + * length and we may have to perform significant amounts of work on each ioend + * to complete it. Hence we have to be careful about holding the CPU for too + * long in this loop. + */ +void +xfs_end_io( + struct work_struct *work) +{ + struct xfs_inode *ip = + container_of(work, struct xfs_inode, i_ioend_work); + struct iomap_ioend *ioend; + struct list_head tmp; + unsigned long flags; + + spin_lock_irqsave(&ip->i_ioend_lock, flags); + list_replace_init(&ip->i_ioend_list, &tmp); + spin_unlock_irqrestore(&ip->i_ioend_lock, flags); + + iomap_sort_ioends(&tmp); + while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend, + io_list))) { + list_del_init(&ioend->io_list); + iomap_ioend_try_merge(ioend, &tmp); + if (bio_op(&ioend->io_bio) == REQ_OP_READ) + iomap_finish_ioends(ioend, + blk_status_to_errno(ioend->io_bio.bi_status)); + else + xfs_end_ioend_write(ioend); + cond_resched(); + } +} + +void +xfs_end_bio( + struct bio *bio) +{ + struct iomap_ioend *ioend = iomap_ioend_from_bio(bio); + struct xfs_inode *ip = XFS_I(ioend->io_inode); + struct xfs_mount *mp = ip->i_mount; + unsigned long flags; + + /* + * For Appends record the actually written block number and set the + * boundary flag if needed. + */ + if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) { + ioend->io_sector = bio->bi_iter.bi_sector; + xfs_mark_rtg_boundary(ioend); + } + + spin_lock_irqsave(&ip->i_ioend_lock, flags); + if (list_empty(&ip->i_ioend_list)) + WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue, + &ip->i_ioend_work)); + list_add_tail(&ioend->io_list, &ip->i_ioend_list); + spin_unlock_irqrestore(&ip->i_ioend_lock, flags); +} diff --git a/fs/xfs/xfs_ioend.h b/fs/xfs/xfs_ioend.h new file mode 100644 index 000000000000..525865767fca --- /dev/null +++ b/fs/xfs/xfs_ioend.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XFS_IOEND_H +#define __XFS_IOEND_H + +/* + * Fast and loose check if this write could update the on-disk inode size. + */ +static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend) +{ + return ioend->io_offset + ioend->io_size > + XFS_I(ioend->io_inode)->i_disk_size; +} + +void xfs_end_bio(struct bio *bio); + +#endif /* __XFS_IOEND_H */ diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 71c45be8c652..7c6238fed61e 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -1083,12 +1083,7 @@ xfs_zoned_direct_write_iomap_begin( return error; } - iomap->type = IOMAP_MAPPED; - iomap->flags = IOMAP_F_DIRTY; - iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev; - iomap->offset = offset; - iomap->length = length; - iomap->flags = IOMAP_F_ANON_WRITE; + xfs_iomap_set_anon_write(ip, iomap, offset, length); return 0; } diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index cffcec532ea6..f2520a9b3a13 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -29,6 +29,22 @@ int xfs_zero_range(struct xfs_inode *ip, loff_t pos, loff_t len, int xfs_truncate_page(struct xfs_inode *ip, loff_t pos, struct xfs_zone_alloc_ctx *ac, bool *did_zero); +static inline void +xfs_iomap_set_anon_write( + struct xfs_inode *ip, + struct iomap *iomap, + loff_t offset, + loff_t length) +{ + iomap->type = IOMAP_MAPPED; + iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev; + iomap->offset = offset; + iomap->length = length; + iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY; + if (bdev_has_integrity_csum(iomap->bdev)) + iomap->flags |= IOMAP_F_INTEGRITY; +} + static inline xfs_filblks_t xfs_aligned_fsb_count( xfs_fileoff_t offset_fsb, diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 4a3299abf774..d1306e723899 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -834,7 +834,7 @@ xfs_setattr_nonsize( } error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL, - has_capability_noaudit(current, CAP_FOWNER), &tp); + capable_noaudit(CAP_FOWNER), &tp); if (error) goto out_dqrele; diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index f807f8f4f705..f4f81d893e8c 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -422,6 +422,8 @@ out_error: static void xlog_state_shutdown_callbacks( struct xlog *log) + __releases(&log->l_icloglock) + __acquires(&log->l_icloglock) { struct xlog_in_core *iclog; LIST_HEAD(cb_list); @@ -470,6 +472,8 @@ xlog_state_release_iclog( struct xlog *log, struct xlog_in_core *iclog, struct xlog_ticket *ticket) + __releases(&log->l_icloglock) + __acquires(&log->l_icloglock) { bool last_ref; @@ -744,13 +748,16 @@ xfs_log_mount_cancel( */ static inline int xlog_force_iclog( + struct xlog *log, struct xlog_in_core *iclog) + __releases(&log->l_icloglock) + __acquires(&log->l_icloglock) { atomic_inc(&iclog->ic_refcnt); iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA; if (iclog->ic_state == XLOG_STATE_ACTIVE) - xlog_state_switch_iclogs(iclog->ic_log, iclog, 0); - return xlog_state_release_iclog(iclog->ic_log, iclog, NULL); + xlog_state_switch_iclogs(log, iclog, 0); + return xlog_state_release_iclog(log, iclog, NULL); } /* @@ -778,11 +785,10 @@ xlog_wait_iclog_completion(struct xlog *log) */ int xlog_wait_on_iclog( + struct xlog *log, struct xlog_in_core *iclog) - __releases(iclog->ic_log->l_icloglock) + __releases(log->l_icloglock) { - struct xlog *log = iclog->ic_log; - trace_xlog_iclog_wait_on(iclog, _RET_IP_); if (!xlog_is_shutdown(log) && iclog->ic_state != XLOG_STATE_ACTIVE && @@ -879,8 +885,8 @@ out_err: spin_lock(&log->l_icloglock); iclog = log->l_iclog; - error = xlog_force_iclog(iclog); - xlog_wait_on_iclog(iclog); + error = xlog_force_iclog(log, iclog); + xlog_wait_on_iclog(log, iclog); if (tic) { trace_xfs_log_umount_write(log, tic); @@ -1538,6 +1544,35 @@ xlog_bio_end_io( &iclog->ic_end_io_work); } +/* + * When using multiple devices, we also need to flush the data and RT device + * caches first to ensure that all metadata writeback covered by the LSN in + * this iclog is on stable storage. This is slow, but it *must* complete + * before we issue the external log IO. + * + * If the flush fails, we cannot conclude that past metadata writeback from + * the log succeeded. Repeating the flush is not possible, hence we must + * shut down with log IO error to avoid shutdown re-entering this path and + * erroring out again. + */ +static int +xlog_flush_data_caches( + struct xlog *log) +{ + struct xfs_mount *mp = log->l_mp; + + if (log->l_targ != mp->m_ddev_targp) { + if (blkdev_issue_flush(mp->m_ddev_targp->bt_bdev)) + return -EIO; + } + if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) { + if (blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev)) + return -EIO; + } + + return 0; +} + STATIC void xlog_write_iclog( struct xlog *log, @@ -1582,21 +1617,9 @@ xlog_write_iclog( iclog->ic_bio.bi_private = iclog; if (iclog->ic_flags & XLOG_ICL_NEED_FLUSH) { - iclog->ic_bio.bi_opf |= REQ_PREFLUSH; - /* - * For external log devices, we also need to flush the data - * device cache first to ensure all metadata writeback covered - * by the LSN in this iclog is on stable storage. This is slow, - * but it *must* complete before we issue the external log IO. - * - * If the flush fails, we cannot conclude that past metadata - * writeback from the log succeeded. Repeating the flush is - * not possible, hence we must shut down with log IO error to - * avoid shutdown re-entering this path and erroring out again. - */ - if (log->l_targ != log->l_mp->m_ddev_targp && - blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev)) + if (xlog_flush_data_caches(log)) goto shutdown; + iclog->ic_bio.bi_opf |= REQ_PREFLUSH; } if (iclog->ic_flags & XLOG_ICL_NEED_FUA) iclog->ic_bio.bi_opf |= REQ_FUA; @@ -2741,14 +2764,17 @@ xlog_state_switch_iclogs( */ static int xlog_force_and_check_iclog( + struct xlog *log, struct xlog_in_core *iclog, bool *completed) + __releases(&log->l_icloglock) + __acquires(&log->l_icloglock) { xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header->h_lsn); int error; *completed = false; - error = xlog_force_iclog(iclog); + error = xlog_force_iclog(log, iclog); if (error) return error; @@ -2825,7 +2851,7 @@ xfs_log_force( /* We have exclusive access to this iclog. */ bool completed; - if (xlog_force_and_check_iclog(iclog, &completed)) + if (xlog_force_and_check_iclog(log, iclog, &completed)) goto out_error; if (completed) @@ -2850,7 +2876,7 @@ xfs_log_force( iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA; if (flags & XFS_LOG_SYNC) - return xlog_wait_on_iclog(iclog); + return xlog_wait_on_iclog(log, iclog); out_unlock: spin_unlock(&log->l_icloglock); return 0; @@ -2920,7 +2946,7 @@ xlog_force_lsn( &log->l_icloglock); return -EAGAIN; } - if (xlog_force_and_check_iclog(iclog, &completed)) + if (xlog_force_and_check_iclog(log, iclog, &completed)) goto out_error; if (log_flushed) *log_flushed = 1; @@ -2948,7 +2974,7 @@ xlog_force_lsn( } if (flags & XFS_LOG_SYNC) - return xlog_wait_on_iclog(iclog); + return xlog_wait_on_iclog(log, iclog); out_unlock: spin_unlock(&log->l_icloglock); return 0; diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index ca66429bf6c9..f715695e8fcb 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -105,8 +105,6 @@ int xfs_log_mount(struct xfs_mount *mp, int num_bblocks); int xfs_log_mount_finish(struct xfs_mount *mp); void xfs_log_mount_cancel(struct xfs_mount *); -xfs_lsn_t xlog_assign_tail_lsn(struct xfs_mount *mp); -xfs_lsn_t xlog_assign_tail_lsn_locked(struct xfs_mount *mp); void xfs_log_space_wake(struct xfs_mount *mp); int xfs_log_reserve(struct xfs_mount *mp, int length, int count, struct xlog_ticket **ticket, bool permanent); diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 639f875a8fb2..f9e07a32f60f 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -1055,9 +1055,10 @@ xlog_cil_set_ctx_write_state( spin_unlock(&cil->xc_push_lock); /* - * Make sure the metadata we are about to overwrite in the log - * has been flushed to stable storage before this iclog is - * issued. + * Flush the write cache before writing the start record so that + * the metadata we are about to overwrite in the log and the + * data that new allocations in this context refer to are + * persisted to stable storage before this iclog is written. */ spin_lock(&cil->xc_log->l_icloglock); iclog->ic_flags |= XLOG_ICL_NEED_FLUSH; @@ -1556,7 +1557,7 @@ xlog_cil_push_work( * iclogs older than ic_prev. Hence we only need to wait * on the most recent older iclog here. */ - xlog_wait_on_iclog(ctx->commit_iclog->ic_prev); + xlog_wait_on_iclog(log, ctx->commit_iclog->ic_prev); spin_lock(&log->l_icloglock); } @@ -1627,6 +1628,7 @@ out_abort_free_ticket: static void xlog_cil_push_background( struct xlog *log) + __releases_shared(&log->l_cilp->xc_ctx_lock) { struct xfs_cil *cil = log->l_cilp; int space_used = atomic_read(&cil->xc_ctx->space_used); diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index cf1e4ce61a8c..6d9673c41cdf 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -605,8 +605,8 @@ xlog_wait( remove_wait_queue(wq, &wait); } -int xlog_wait_on_iclog(struct xlog_in_core *iclog) - __releases(iclog->ic_log->l_icloglock); +int xlog_wait_on_iclog(struct xlog *log, struct xlog_in_core *iclog) + __releases(log->l_icloglock); /* Calculate the distance between two LSNs in bytes */ static inline uint64_t diff --git a/fs/xfs/xfs_mru_cache.c b/fs/xfs/xfs_mru_cache.c index d61ec8cb126d..3f3af2e2e31c 100644 --- a/fs/xfs/xfs_mru_cache.c +++ b/fs/xfs/xfs_mru_cache.c @@ -520,7 +520,7 @@ xfs_mru_cache_lookup( if (elem) { list_del(&elem->list_node); _xfs_mru_cache_list_insert(mru, elem); - __release(mru_lock); /* help sparse not be stupid */ + __release(&mru->lock); } else spin_unlock(&mru->lock); diff --git a/fs/xfs/xfs_platform.h b/fs/xfs/xfs_platform.h index 59a33c60e0ca..5d542e95fe44 100644 --- a/fs/xfs/xfs_platform.h +++ b/fs/xfs/xfs_platform.h @@ -289,15 +289,4 @@ int xfs_rw_bdev(struct block_device *bdev, sector_t sector, unsigned int count, # define PTR_FMT "%p" #endif -/* - * Helper for IO routines to grab backing pages from allocated kernel memory. - */ -static inline struct page * -kmem_to_page(void *addr) -{ - if (is_vmalloc_addr(addr)) - return vmalloc_to_page(addr); - return virt_to_page(addr); -} - #endif /* _XFS_PLATFORM_H */ diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 4b2eeb7783f7..b24db75eaedc 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -445,7 +445,7 @@ xfs_shutdown_devices( blkdev_issue_flush(mp->m_logdev_targp->bt_bdev); invalidate_bdev(mp->m_logdev_targp->bt_bdev); } - if (mp->m_rtdev_targp) { + if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) { blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev); invalidate_bdev(mp->m_rtdev_targp->bt_bdev); } diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index f333c938fbd9..6aa379c2cf0c 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -6139,8 +6139,8 @@ DEFINE_EVENT(xfs_healthmon_event_class, name, \ TP_PROTO(const struct xfs_healthmon *hm, \ const struct xfs_healthmon_event *event), \ TP_ARGS(hm, event)) -DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert); -DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_push); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert_head); +DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_insert_tail); DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_pop); DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format); DEFINE_HEALTHMONEVENT_EVENT(xfs_healthmon_format_overflow); diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 99a9bf3762b7..f955479a08fd 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -33,7 +33,7 @@ STATIC void xfs_ail_check( struct xfs_ail *ailp, struct xfs_log_item *lip) - __must_hold(&ailp->ail_lock) + __must_hold(&ailp->ail_lock) { struct xfs_log_item *prev_lip; struct xfs_log_item *next_lip; @@ -321,6 +321,7 @@ static void xfs_ail_delete( struct xfs_ail *ailp, struct xfs_log_item *lip) + __must_hold(&ailp->ail_lock) { xfs_ail_check(ailp, lip); list_del(&lip->li_ail); @@ -899,6 +900,7 @@ xfs_lsn_t xfs_ail_delete_one( struct xfs_ail *ailp, struct xfs_log_item *lip) + __must_hold(&ailp->ail_lock) { struct xfs_log_item *mlip = xfs_ail_min(ailp); xfs_lsn_t lsn = lip->li_lsn; diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 1e025848811a..a5d25b703dfc 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -521,7 +521,8 @@ xfs_trans_log_buf( { struct xfs_buf_log_item *bip = bp->b_log_item; - ASSERT(first <= last && last < BBTOB(bp->b_length)); + ASSERT(first <= last); + ASSERT(last < BBTOB(bp->b_length)); ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED)); xfs_trans_dirty_buf(tp, bp); diff --git a/fs/xfs/xfs_verify_media.c b/fs/xfs/xfs_verify_media.c index 5ead3976d511..b75c81f8fcc0 100644 --- a/fs/xfs/xfs_verify_media.c +++ b/fs/xfs/xfs_verify_media.c @@ -268,6 +268,8 @@ xfs_verify_media( struct xfs_buftarg *btp = NULL; struct bio *bio; struct folio *folio; + xfs_daddr_t dev_start = 0; + xfs_daddr_t dev_end = 0; xfs_daddr_t daddr; uint64_t bbcount; int error = 0; @@ -277,24 +279,33 @@ xfs_verify_media( switch (me->me_dev) { case XFS_DEV_DATA: btp = mp->m_ddev_targp; + dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); break; case XFS_DEV_LOG: - if (mp->m_logdev_targp != mp->m_ddev_targp) + if (mp->m_logdev_targp != mp->m_ddev_targp) { btp = mp->m_logdev_targp; + dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); + } break; case XFS_DEV_RT: btp = mp->m_rtdev_targp; + dev_start = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart); + dev_end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rtstart + + mp->m_sb.sb_rblocks); break; } if (!btp) return -ENODEV; /* - * If the caller told us to verify beyond the end of the disk, tell the - * user exactly where that was. + * If the caller told us to verify before the start or beyond the end + * of the disk volume, tell the user exactly where the volume starts + * and ends. */ - if (me->me_end_daddr > btp->bt_nr_sectors) - me->me_end_daddr = btp->bt_nr_sectors; + if (me->me_end_daddr > dev_end) + me->me_end_daddr = dev_end; + if (me->me_start_daddr < dev_start) + me->me_start_daddr = dev_start; /* start and end have to be aligned to the lba size */ if (!IS_ALIGNED(BBTOB(me->me_start_daddr | me->me_end_daddr), @@ -323,8 +334,7 @@ xfs_verify_media( * verifying. */ daddr = me->me_start_daddr; - bbcount = min_t(sector_t, me->me_end_daddr, btp->bt_nr_sectors) - - me->me_start_daddr; + bbcount = me->me_end_daddr - me->me_start_daddr; folio = xfs_verify_alloc_folio(xfs_verify_iosize(me, btp, bbcount)); if (!folio) diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c index 7d13fa7ab30a..28c1e48909fa 100644 --- a/fs/xfs/xfs_zone_alloc.c +++ b/fs/xfs/xfs_zone_alloc.c @@ -475,6 +475,8 @@ static struct xfs_open_zone * xfs_try_open_zone( struct xfs_mount *mp, enum rw_hint write_hint) + __releases(&mp->m_zone_info->zi_open_zones_lock) + __acquires(&mp->m_zone_info->zi_open_zones_lock) { struct xfs_zone_info *zi = mp->m_zone_info; struct xfs_open_zone *oz; @@ -793,17 +795,35 @@ xfs_get_cached_zone( rcu_read_lock(); oz = VFS_I(ip)->i_private; - if (oz) { - /* - * GC only steals open zones at mount time, so no GC zones - * should end up in the cache. - */ - ASSERT(!oz->oz_is_gc); - if (!atomic_inc_not_zero(&oz->oz_ref)) + if (!oz) + goto out_unlock; + + /* + * GC only steals open zones at mount time, so no GC zones should end up + * in the cache. + */ + ASSERT(!oz->oz_is_gc); + + /* + * Drop the old cached open zone if it is full. + */ + if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) { + spin_lock(&ip->i_flags_lock); + oz = VFS_I(ip)->i_private; + if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) { + VFS_I(ip)->i_private = NULL; + spin_unlock(&ip->i_flags_lock); + xfs_open_zone_put(oz); oz = NULL; + goto out_unlock; + } + spin_unlock(&ip->i_flags_lock); } - rcu_read_unlock(); + if (!atomic_inc_not_zero(&oz->oz_ref)) + oz = NULL; +out_unlock: + rcu_read_unlock(); return oz; } @@ -818,18 +838,41 @@ xfs_get_cached_zone( * that were every written to, but significantly simplifies the cached zone * lookup. Because the open_zone is clearly marked as full when all data * in the underlying RTG was written, the caching is always safe. + * + * Called with a reference on @oz held. And returns two references on the + * returned zone: one for the caller and one for pinning the zone in + * inode->i_private. */ -static void +static struct xfs_open_zone * xfs_set_cached_zone( struct xfs_inode *ip, struct xfs_open_zone *oz) { struct xfs_open_zone *old_oz; + /* + * If the open zone cached in the inode still has free space, use that + * instead of the new open zone just selected. This can happen when + * multiple threads race to perform zone selection for an inode. + * io_uring worker threads seem to be good way to trigger this. + * + * We need to grab an extra reference to this open zone as the caller + * owns a reference in addition to the i_private pointer. + */ + spin_lock(&ip->i_flags_lock); + old_oz = VFS_I(ip)->i_private; + if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) && + atomic_inc_not_zero(&old_oz->oz_ref)) { + spin_unlock(&ip->i_flags_lock); + xfs_open_zone_put(oz); + return old_oz; + } + VFS_I(ip)->i_private = oz; atomic_inc(&oz->oz_ref); - old_oz = xchg(&VFS_I(ip)->i_private, oz); + spin_unlock(&ip->i_flags_lock); if (old_oz) xfs_open_zone_put(old_oz); + return oz; } static void @@ -873,14 +916,13 @@ xfs_zone_alloc_and_submit( * the inode is still associated with a zone and use that if so. */ if (!*oz) +select_zone: *oz = xfs_get_cached_zone(ip); - if (!*oz) { -select_zone: *oz = xfs_select_zone(mp, write_hint, pack_tight); if (!*oz) goto out_error; - xfs_set_cached_zone(ip, *oz); + *oz = xfs_set_cached_zone(ip, *oz); } alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size), diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index d0b85179a3d2..5fdcf98a2133 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -869,6 +869,11 @@ xfs_zone_gc_write_chunk( WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW); list_move_tail(&chunk->entry, &data->writing); + /* + * If we run on top of stacked block device, the read I/O might have + * reset bi_bdev, restore it to the one we want. + */ + bio_set_dev(&chunk->bio, mp->m_rtdev_targp->bt_bdev); bio_reuse(&chunk->bio, REQ_OP_WRITE); while ((split_chunk = xfs_zone_gc_split_write(data, chunk))) xfs_zone_gc_submit_write(data, split_chunk); diff --git a/fs/xfs/xfs_zone_space_resv.c b/fs/xfs/xfs_zone_space_resv.c index 5c6e6ef627e4..7aa3c74fb2e0 100644 --- a/fs/xfs/xfs_zone_space_resv.c +++ b/fs/xfs/xfs_zone_space_resv.c @@ -85,13 +85,13 @@ xfs_zoned_add_available( struct xfs_zone_info *zi = mp->m_zone_info; struct xfs_zone_reservation *reservation; - if (list_empty_careful(&zi->zi_reclaim_reservations)) { - xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb); + spin_lock(&zi->zi_reservation_lock); + xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb); + if (list_empty(&zi->zi_reclaim_reservations)) { + spin_unlock(&zi->zi_reservation_lock); return; } - spin_lock(&zi->zi_reservation_lock); - xfs_add_freecounter(mp, XC_FREE_RTAVAILABLE, count_fsb); count_fsb = xfs_sum_freecounter(mp, XC_FREE_RTAVAILABLE); list_for_each_entry(reservation, &zi->zi_reclaim_reservations, entry) { if (reservation->count_fsb > count_fsb) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 1a45e0d521d8..a10a591c18b2 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -202,12 +202,8 @@ struct acpi_device_dir { /* Plug and Play */ -#define MAX_ACPI_DEVICE_NAME_LEN 40 -#define MAX_ACPI_CLASS_NAME_LEN 20 typedef char acpi_bus_id[8]; typedef u64 acpi_bus_address; -typedef char acpi_device_name[MAX_ACPI_DEVICE_NAME_LEN]; -typedef char acpi_device_class[MAX_ACPI_CLASS_NAME_LEN]; struct acpi_hardware_id { struct list_head list; @@ -229,16 +225,12 @@ struct acpi_device_pnp { acpi_bus_address bus_address; /* _ADR */ char *unique_id; /* _UID */ struct list_head ids; /* _HID and _CIDs */ - acpi_device_name device_name; /* Driver-determined */ - acpi_device_class device_class; /* " */ }; #define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_adr(d) ((d)->pnp.bus_address) const char *acpi_device_hid(struct acpi_device *device); #define acpi_device_uid(d) ((d)->pnp.unique_id) -#define acpi_device_name(d) ((d)->pnp.device_name) -#define acpi_device_class(d) ((d)->pnp.device_class) /* Power Management */ @@ -578,6 +570,9 @@ int acpi_dev_for_each_child_reverse(struct acpi_device *adev, * ------ */ +#define MAX_ACPI_CLASS_NAME_LEN 20 +typedef char acpi_device_class[MAX_ACPI_CLASS_NAME_LEN]; + struct acpi_bus_event { struct list_head node; acpi_device_class device_class; diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h index 95eb4b66b057..ebbd3b0ddf36 100644 --- a/include/drm/drm_pagemap.h +++ b/include/drm/drm_pagemap.h @@ -2,6 +2,7 @@ #ifndef _DRM_PAGEMAP_H_ #define _DRM_PAGEMAP_H_ +#include <linux/bits.h> #include <linux/dma-direction.h> #include <linux/hmm.h> #include <linux/memremap.h> @@ -339,6 +340,9 @@ struct drm_pagemap_migrate_details { #if IS_ENABLED(CONFIG_ZONE_DEVICE) +#define DRM_PAGEMAP_ZDD_FLAG_MIGRATED BIT(0) +#define DRM_PAGEMAP_ZDD_FLAG_MASK DRM_PAGEMAP_ZDD_FLAG_MIGRATED + int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, struct mm_struct *mm, unsigned long start, unsigned long end, @@ -373,7 +377,9 @@ static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct p { struct folio *folio = page_folio(page); - return folio_zone_device_data(folio); + return (struct drm_pagemap_zdd *) + ((unsigned long)folio_zone_device_data(folio) & + ~DRM_PAGEMAP_ZDD_FLAG_MASK); } #else diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ffa5626411ac..e57af902560c 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -894,6 +894,7 @@ enum bpf_arg_type { ARG_PTR_TO_CTX, /* pointer to context */ ARG_ANYTHING, /* any (initialized) argument is ok */ + ARG_SCALAR, /* scalar argument */ ARG_PTR_TO_SPIN_LOCK, /* pointer to bpf_spin_lock */ ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */ ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */ @@ -4209,7 +4210,7 @@ static inline int bpf_map_check_op_flags(struct bpf_map *map, u64 flags, u64 all return -EINVAL; cpu = flags >> 32; - if ((flags & BPF_F_CPU) && cpu >= num_possible_cpus()) + if ((flags & BPF_F_CPU) && (cpu >= nr_cpu_ids || !cpu_possible(cpu))) return -ERANGE; } diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 5fad59fdab0d..36b65797877d 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -706,6 +706,8 @@ struct bpf_insn_aux_data { */ u32 calls_callback:1; u32 indirect_target:1; /* if it is an indirect jump target */ + /* true if some jump or call instruction targets this instruction */ + u32 jump_target:1; /* * CFG strongly connected component this instruction belongs to, * zero if it is a singleton SCC. @@ -1142,6 +1144,16 @@ static inline void mark_jmp_point(struct bpf_verifier_env *env, int idx) env->insn_aux_data[idx].jmp_point = true; } +static inline void mark_jump_target(struct bpf_verifier_env *env, int idx) +{ + env->insn_aux_data[idx].jump_target = true; +} + +static inline bool bpf_is_jump_target(struct bpf_verifier_env *env, int insn_idx) +{ + return env->insn_aux_data[insn_idx].jump_target; +} + static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env) { struct bpf_verifier_state *cur = env->cur_state; @@ -1369,7 +1381,9 @@ static inline bool bpf_type_has_unsafe_modifiers(u32 type) static inline bool type_is_ptr_alloc_obj(u32 type) { - return base_type(type) == PTR_TO_BTF_ID && type_flag(type) & MEM_ALLOC; + return base_type(type) == PTR_TO_BTF_ID && + type_flag(type) & MEM_ALLOC && + !(type_flag(type) & PTR_UNTRUSTED); } static inline bool type_is_non_owning_ref(u32 type) diff --git a/include/linux/capability.h b/include/linux/capability.h index 37db92b3d6f8..f8532d92fcad 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap); extern bool has_ns_capability_noaudit(struct task_struct *t, struct user_namespace *ns, int cap); extern bool capable(int cap); +bool capable_noaudit(int cap); extern bool ns_capable(struct user_namespace *ns, int cap); extern bool ns_capable_noaudit(struct user_namespace *ns, int cap); extern bool ns_capable_setid(struct user_namespace *ns, int cap); @@ -167,6 +168,10 @@ static inline bool capable(int cap) { return true; } +static inline bool capable_noaudit(int cap) +{ + return true; +} static inline bool ns_capable(struct user_namespace *ns, int cap) { return true; diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 7a631a257613..3754d697854b 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -527,7 +527,10 @@ struct cgroup { int nr_threaded_children; /* # of live threaded child cgroups */ - /* sequence number for cgroup.kill, serialized by css_set_lock. */ + /* + * Sequence number for cgroup.kill. Incremented with both cgroup_mutex + * and css_set_lock held. Readers hold either one. + */ unsigned int kill_seq; struct kernfs_node *kn; /* cgroup kernfs entry */ diff --git a/include/linux/console.h b/include/linux/console.h index d624200cfc17..502d1abe3f50 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -173,7 +173,7 @@ static inline void con_debug_leave(void) { } * @CON_BRL: Indicates a braille device which is exempt from * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of - * /dev/kmesg which requires a larger output buffer. + * /dev/kmsg which requires a larger output buffer. * @CON_SUSPENDED: Indicates if a console is suspended. If true, the * printing callbacks must not be called. * @CON_NBCON: Console can operate outside of the legacy style console_lock diff --git a/include/linux/device-id/ap.h b/include/linux/device-id/ap.h index 0992333a34db..e050abebbf3d 100644 --- a/include/linux/device-id/ap.h +++ b/include/linux/device-id/ap.h @@ -4,7 +4,6 @@ #ifdef __KERNEL__ #include <linux/types.h> -typedef unsigned long kernel_ulong_t; #endif #define AP_DEVICE_ID_MATCH_CARD_TYPE 0x01 @@ -14,7 +13,6 @@ typedef unsigned long kernel_ulong_t; struct ap_device_id { __u16 match_flags; /* which fields to match against */ __u8 dev_type; /* device type */ - kernel_ulong_t driver_info; }; #endif /* ifndef LINUX_DEVICE_ID_AP_H */ diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index d1203da56fc5..d15b2b31d3c9 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -567,6 +567,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach); struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info); int dma_buf_fd(struct dma_buf *dmabuf, int flags); +void dma_buf_fd_install(struct dma_buf *dmabuf, int fd); struct dma_buf *dma_buf_get(int fd); void dma_buf_put(struct dma_buf *dmabuf); diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h index 1b1d87579c38..0c49d7ccefb6 100644 --- a/include/linux/dma-fence-array.h +++ b/include/linux/dma-fence-array.h @@ -28,7 +28,6 @@ struct dma_fence_array_cb { /** * struct dma_fence_array - fence to represent an array of fences * @base: fence base class - * @lock: spinlock for fence handling * @num_fences: number of fences in the array * @num_pending: fences in the array still pending * @fences: array of the fences diff --git a/include/linux/dma-fence-chain.h b/include/linux/dma-fence-chain.h index df3beadf1515..705c4394ac0d 100644 --- a/include/linux/dma-fence-chain.h +++ b/include/linux/dma-fence-chain.h @@ -20,7 +20,6 @@ * @prev: previous fence of the chain * @prev_seqno: original previous seqno before garbage collection * @fence: encapsulated fence - * @lock: spinlock for fence handling */ struct dma_fence_chain { struct dma_fence base; @@ -81,9 +80,8 @@ dma_fence_chain_contained(struct dma_fence *fence) } /** - * dma_fence_chain_alloc - * - * Returns a new struct dma_fence_chain object or NULL on failure. + * dma_fence_chain_alloc - Returns a new &struct dma_fence_chain object or + * %NULL on failure. * * This specialized allocator has to be a macro for its allocations to be * accounted separately (to have a separate alloc_tag). The typecast is @@ -93,7 +91,8 @@ dma_fence_chain_contained(struct dma_fence *fence) kmalloc_obj(struct dma_fence_chain) /** - * dma_fence_chain_free + * dma_fence_chain_free - Frees an allocated but not used + * &struct dma_fence_chain object. * @chain: chain node to free * * Frees up an allocated but not used struct dma_fence_chain object. This diff --git a/include/linux/edac.h b/include/linux/edac.h index e6b4e51130e5..f7a8218f9cc0 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -598,9 +598,6 @@ struct mem_ctl_info { int op_state; struct dentry *debugfs; - u8 fake_inject_layer[EDAC_MAX_LAYERS]; - bool fake_inject_ue; - u16 fake_inject_count; /* * Memory Controller hierarchy diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h index 6574b7183c01..fa2854fed1f2 100644 --- a/include/linux/entry-common.h +++ b/include/linux/entry-common.h @@ -102,7 +102,14 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) trace_syscall_enter(regs); - if (unlikely(audit_context())) + /* + * The config check works around broken compilers which fail to + * eliminate the dead code in case of CONFIG_AUDITSYSCALL=n as they + * insist on creating a always false runtime condition based on + * audit_context() which returns NULL in that case. The explicit + * IS_ENABLED() check makes that madness go away. + */ + if (IS_ENABLED(CONFIG_AUDITSYSCALL) && unlikely(audit_context())) syscall_enter_audit(regs); return true; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 12683b5d125e..253600c0eccd 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -1057,6 +1057,12 @@ struct kernel_ethtool_ts_info { * @get_sset_count: Get number of strings that @get_strings will write. * @get_rxnfc: Get RX flow classification rules. Returns a negative * error code or zero. + * Note that for %ETHTOOL_GRXCLSRLALL rule_cnt and size of the arrays + * is user-provided, and not guaranteed to match what driver would + * have reported via %ETHTOOL_GRXCLSRLCNT. Drivers must return -%EMSGSIZE + * when rule_cnt is too small. rule_locs is %NULL when rule_cnt is zero. + * On success drivers must set rule_cnt to the number of locations they + * filled in, the core copies out exactly that many. * @set_rxnfc: Set RX flow classification rules. Returns a negative * error code or zero. * @flash_device: Write a firmware image to device's flash memory. diff --git a/include/linux/filter.h b/include/linux/filter.h index 4a9bc6a848f2..39decde7fc73 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1376,7 +1376,6 @@ bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr, void bpf_jit_binary_free(struct bpf_binary_header *hdr); u64 bpf_jit_alloc_exec_limit(void); void *bpf_jit_alloc_exec(unsigned long size); -void *bpf_jit_alloc_exec_rw(unsigned long size); void bpf_jit_free_exec(void *addr); void bpf_jit_free(struct bpf_prog *fp); struct bpf_binary_header * diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 02bc5027523a..bd76a16a63af 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -866,8 +866,9 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec); unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec); extern ftrace_func_t ftrace_trace_function; +struct trace_array; -int ftrace_regex_open(struct ftrace_ops *ops, int flag, +int ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag, struct inode *inode, struct file *file); ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos); @@ -1077,7 +1078,7 @@ static inline unsigned long ftrace_location(unsigned long ip) * have them defined when ftrace is not enabled, but these * functions may still be called. Use a macro instead of inline. */ -#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) +#define ftrace_regex_open(tr, ops, flag, inode, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) #define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; }) #define ftrace_set_filter_ips(ops, ips, cnt, remove, reset) ({ -ENODEV; }) diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 3a2d35a9f307..a0cf0398519f 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -57,20 +57,21 @@ struct ip_mc_socklist { }; struct ip_sf_list { - struct ip_sf_list *sf_next; + struct ip_sf_list __rcu *sf_next; unsigned long sf_count[2]; /* include/exclude counts */ __be32 sf_inaddr; unsigned char sf_gsresp; /* include in g & s response? */ unsigned char sf_oldin; /* change state */ unsigned char sf_crcount; /* retrans. left to send */ + struct rcu_head rcu; }; struct ip_mc_list { struct in_device *interface; __be32 multiaddr; unsigned int sfmode; - struct ip_sf_list *sources; - struct ip_sf_list *tomb; + struct ip_sf_list __rcu *sources; + struct ip_sf_list __rcu *tomb; unsigned long sfcount[2]; union { struct ip_mc_list *next; diff --git a/include/linux/interrupt_rc.h b/include/linux/interrupt_rc.h index b9a7f05ecf42..a9ed937a80e7 100644 --- a/include/linux/interrupt_rc.h +++ b/include/linux/interrupt_rc.h @@ -20,11 +20,8 @@ /* Per-CPU interrupt disabling state for local_interrupt_{disable,enable}(). */ DECLARE_PER_CPU(unsigned long, local_interrupt_disable_state); -static __always_inline void __local_interrupt_disable(void) +static __always_inline void __local_interrupt_save_state(unsigned long flags) { - unsigned long flags; - - local_irq_save(flags); raw_cpu_write(local_interrupt_disable_state, flags); } @@ -36,9 +33,9 @@ static __always_inline void __local_interrupt_enable(void) } #ifndef INSTANTIATE_EXPORTED_INTERRUPT_DISABLE -static __always_inline void _local_interrupt_disable(void) +static __always_inline void _local_interrupt_save_state(unsigned long flags) { - __local_interrupt_disable(); + __local_interrupt_save_state(flags); } static __always_inline void _local_interrupt_enable(void) @@ -46,27 +43,30 @@ static __always_inline void _local_interrupt_enable(void) __local_interrupt_enable(); } #else -extern void _local_interrupt_disable(void); +extern void _local_interrupt_save_state(unsigned long flags); extern void _local_interrupt_enable(void); #endif #else /* !MODULE */ -extern void _local_interrupt_disable(void); +extern void _local_interrupt_save_state(unsigned long flags); extern void _local_interrupt_enable(void); #endif /* !MODULE */ +#define hardirq_disable_enter() __preempt_count_add_return(HARDIRQ_DISABLE_OFFSET) +#define hardirq_disable_exit() __preempt_count_sub_return(HARDIRQ_DISABLE_OFFSET) + static inline void local_interrupt_disable(void) { int new_count; + unsigned long flags; WARN_ON_ONCE(in_nmi()); + local_irq_save(flags); new_count = hardirq_disable_enter(); - /* Interrupts can happen here, but it's OK, see __irq_exit_rcu(). */ - if ((new_count & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET) - _local_interrupt_disable(); + _local_interrupt_save_state(flags); } static inline void local_interrupt_enable(void) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 73c25d40846c..3ba75a4ed3da 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -752,24 +752,6 @@ static inline void msi_device_domain_free_wired(struct irq_domain *domain, unsig } #endif -static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node, - unsigned int size, - const struct irq_domain_ops *ops, - void *host_data) -{ - struct irq_domain_info info = { - .fwnode = of_fwnode_handle(of_node), - .size = size, - .hwirq_max = size, - .ops = ops, - .host_data = host_data, - }; - struct irq_domain *d; - - d = irq_domain_instantiate(&info); - return IS_ERR(d) ? NULL : d; -} - #else /* CONFIG_IRQ_DOMAIN */ static inline void irq_dispose_mapping(unsigned int virq) { } static inline struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode, diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 8c4f3bb24429..e6de7ae55bda 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -181,6 +181,7 @@ struct kprobe_blacklist_entry { struct list_head list; unsigned long start_addr; unsigned long end_addr; + struct rcu_head rcu; }; #ifdef CONFIG_KPROBES diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index 05631a25e622..8b8d472eff34 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -56,23 +56,4 @@ void arpt_unregister_table(struct net *net, const char *name); extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT -#include <net/compat.h> - -struct compat_arpt_entry { - struct arpt_arp arp; - __u16 target_offset; - __u16 next_offset; - compat_uint_t comefrom; - struct compat_xt_counters counters; - unsigned char elems[]; -}; - -static inline struct xt_entry_target * -compat_arpt_get_target(struct compat_arpt_entry *e) -{ - return (void *)e + e->target_offset; -} - -#endif /* CONFIG_COMPAT */ #endif /* _ARPTABLES_H */ diff --git a/include/linux/netfs.h b/include/linux/netfs.h index f837a501008c..b4dd32863dd4 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -246,6 +246,7 @@ struct netfs_io_request { unsigned long long submitted; /* Amount submitted for I/O so far */ unsigned long long len; /* Length of the request */ size_t transferred; /* Amount to be indicated as transferred */ + size_t progress_at; /* Report read progress when hit this much read */ long error; /* 0 or error that occurred */ unsigned long long i_size; /* Size of the file */ unsigned long long start; /* Start position */ @@ -262,7 +263,6 @@ struct netfs_io_request { atomic_t subreq_counter; /* Next subreq->debug_index */ unsigned int nr_group_rel; /* Number of refs to release on ->group */ spinlock_t lock; /* Lock for queuing subreqs */ - unsigned char front_folio_order; /* Order (size) of front folio */ enum netfs_io_origin origin; /* Origin of the request */ bool direct_bv_unpin; /* T if direct_bv[] must be unpinned */ refcount_t ref; @@ -275,9 +275,10 @@ struct netfs_io_request { #define NETFS_RREQ_SHORT_TRANSFER 5 /* Set if we have a short transfer */ #define NETFS_RREQ_OFFLOAD_COLLECTION 8 /* Offload collection to workqueue */ #define NETFS_RREQ_NO_UNLOCK_FOLIO 9 /* Don't unlock no_unlock_folio on completion */ -#define NETFS_RREQ_FOLIO_COPY_TO_CACHE 10 /* Copy current folio to cache from read */ +#define NETFS_RREQ_CANCEL_CACHING 10 /* Set to cancel caching */ #define NETFS_RREQ_UPLOAD_TO_SERVER 11 /* Need to write to the server */ #define NETFS_RREQ_USE_IO_ITER 12 /* Use ->io_iter rather than ->i_pages */ +#define NETFS_RREQ_NEED_PUT_RA_REFS 17 /* Need to put the folio refs RA gave us */ #define NETFS_RREQ_USE_PGPRIV2 31 /* [DEPRECATED] Use PG_private_2 to mark * write to cache on read */ const struct netfs_request_ops *netfs_ops; diff --git a/include/linux/ns/ns_common_types.h b/include/linux/ns/ns_common_types.h index ea45c54e4435..6ed6b497831c 100644 --- a/include/linux/ns/ns_common_types.h +++ b/include/linux/ns/ns_common_types.h @@ -116,10 +116,8 @@ struct ns_common { struct dentry *stashed; const struct proc_ns_operations *ops; unsigned int inum; - union { - struct ns_tree; - struct rcu_head ns_rcu; - }; + struct ns_tree; + struct rcu_head ns_rcu; }; #define to_ns_common(__ns) \ diff --git a/include/linux/nvme-tcp.h b/include/linux/nvme-tcp.h index e435250fcb4d..859338da8573 100644 --- a/include/linux/nvme-tcp.h +++ b/include/linux/nvme-tcp.h @@ -77,7 +77,7 @@ struct nvme_tcp_hdr { __le32 plen; }; -/** +/* * struct nvme_tcp_icreq_pdu - nvme tcp initialize connection request pdu * * @hdr: pdu generic header @@ -95,7 +95,7 @@ struct nvme_tcp_icreq_pdu { __u8 rsvd2[112]; }; -/** +/* * struct nvme_tcp_icresp_pdu - nvme tcp initialize connection response pdu * * @hdr: pdu common header @@ -113,12 +113,13 @@ struct nvme_tcp_icresp_pdu { __u8 rsvd[112]; }; -/** +/* * struct nvme_tcp_term_pdu - nvme tcp terminate connection pdu * * @hdr: pdu common header * @fes: fatal error status - * @fei: fatal error information + * @feil: fatal error information (low 16 bits) + * @feih: fatal error information (high 16 bits) */ struct nvme_tcp_term_pdu { struct nvme_tcp_hdr hdr; @@ -128,7 +129,7 @@ struct nvme_tcp_term_pdu { __u8 rsvd[10]; }; -/** +/* * struct nvme_tcp_cmd_pdu - nvme tcp command capsule pdu * * @hdr: pdu common header @@ -139,10 +140,9 @@ struct nvme_tcp_cmd_pdu { struct nvme_command cmd; }; -/** +/* * struct nvme_tcp_rsp_pdu - nvme tcp response capsule pdu * - * @hdr: pdu common header * @hdr: nvme-tcp generic header * @cqe: nvme completion queue entry */ @@ -151,7 +151,7 @@ struct nvme_tcp_rsp_pdu { struct nvme_completion cqe; }; -/** +/* * struct nvme_tcp_r2t_pdu - nvme tcp ready-to-transfer pdu * * @hdr: pdu common header @@ -169,7 +169,7 @@ struct nvme_tcp_r2t_pdu { __u8 rsvd[4]; }; -/** +/* * struct nvme_tcp_data_pdu - nvme tcp data pdu * * @hdr: pdu common header diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h index f82a28040594..ecaa40370830 100644 --- a/include/linux/perf/riscv_pmu.h +++ b/include/linux/perf/riscv_pmu.h @@ -55,7 +55,7 @@ struct riscv_pmu { irqreturn_t (*handle_irq)(int irq_num, void *dev); - unsigned long cmask; + DECLARE_BITMAP(cmask, RISCV_MAX_COUNTERS); u64 (*ctr_read)(struct perf_event *event); int (*ctr_get_idx)(struct perf_event *event); int (*ctr_get_width)(int idx); diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 8299657f0f86..2e689de7b29a 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -168,10 +168,6 @@ static __always_inline unsigned char interrupt_context_level(void) #define in_softirq() (softirq_count()) #define in_interrupt() (irq_count()) -#define hardirq_disable_count() ((preempt_count() & HARDIRQ_DISABLE_MASK) >> HARDIRQ_DISABLE_SHIFT) -#define hardirq_disable_enter() __preempt_count_add_return(HARDIRQ_DISABLE_OFFSET) -#define hardirq_disable_exit() __preempt_count_sub_return(HARDIRQ_DISABLE_OFFSET) - /* * The preempt_count offset after preempt_disable(); */ diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h index 6dbc5a1bf6a8..d2fa1c41bfd2 100644 --- a/include/linux/rbtree_augmented.h +++ b/include/linux/rbtree_augmented.h @@ -87,18 +87,18 @@ rb_add_augmented_cached(struct rb_node *node, struct rb_root_cached *tree, } /* - * Template for declaring augmented rbtree callbacks (generic case) + * Template for declaring augmented rbtree callbacks (generic multi fields) * * RBSTATIC: 'static' or empty * RBNAME: name of the rb_augment_callbacks structure * RBSTRUCT: struct type of the tree nodes * RBFIELD: name of struct rb_node field within RBSTRUCT - * RBAUGMENTED: name of field within RBSTRUCT holding data for subtree - * RBCOMPUTE: name of function that recomputes the RBAUGMENTED data + * RBCOPY: name of function that copies the RBAUGMENTED datas + * RBCOMPUTE: name of function that recomputes the RBAUGMENTED datas */ -#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \ - RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE) \ +#define RB_DECLARE_CALLBACKS_MULTI(RBSTATIC, RBNAME, \ + RBSTRUCT, RBFIELD, RBCOPY, RBCOMPUTE) \ static inline void \ RBNAME ## _propagate(struct rb_node *rb, struct rb_node *stop) \ { \ @@ -114,14 +114,14 @@ RBNAME ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \ { \ RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD); \ RBSTRUCT *new = rb_entry(rb_new, RBSTRUCT, RBFIELD); \ - new->RBAUGMENTED = old->RBAUGMENTED; \ + RBCOPY(new, old); \ } \ static void \ RBNAME ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \ { \ RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD); \ RBSTRUCT *new = rb_entry(rb_new, RBSTRUCT, RBFIELD); \ - new->RBAUGMENTED = old->RBAUGMENTED; \ + RBCOPY(new, old); \ RBCOMPUTE(old, false); \ } \ RBSTATIC const struct rb_augment_callbacks RBNAME = { \ @@ -131,6 +131,27 @@ RBSTATIC const struct rb_augment_callbacks RBNAME = { \ }; /* + * Template for declaring augmented rbtree callbacks (generic single field) + * + * RBSTATIC: 'static' or empty + * RBNAME: name of the rb_augment_callbacks structure + * RBSTRUCT: struct type of the tree nodes + * RBFIELD: name of struct rb_node field within RBSTRUCT + * RBAUGMENTED: name of field within RBSTRUCT holding data for subtree + * RBCOMPUTE: name of function that recomputes the RBAUGMENTED data + */ + +#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \ + RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE) \ +static inline void \ +RBNAME ## _copy_single(RBSTRUCT *new, RBSTRUCT *old) \ +{ \ + new->RBAUGMENTED = old->RBAUGMENTED; \ +} \ +RB_DECLARE_CALLBACKS_MULTI(RBSTATIC, RBNAME, \ + RBSTRUCT, RBFIELD, RBNAME ## _copy_single, RBCOMPUTE) + +/* * Template for declaring augmented rbtree callbacks, * computing RBAUGMENTED scalar as max(RBCOMPUTE(node)) for all subtree nodes. * diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 0670742b2d60..eac3e9080c3c 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -3,8 +3,9 @@ #define _LINUX_RING_BUFFER_H #include <linux/mm.h> -#include <linux/seq_file.h> #include <linux/poll.h> +#include <linux/ring_buffer_types.h> +#include <linux/seq_file.h> #include <uapi/linux/trace_mmap.h> @@ -218,14 +219,15 @@ bool ring_buffer_time_stamp_abs(struct trace_buffer *buffer); size_t ring_buffer_nr_dirty_pages(struct trace_buffer *buffer, int cpu); struct buffer_data_read_page; -struct buffer_data_read_page * -ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu); +int ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu, + struct buffer_data_read_page **rpage); void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, struct buffer_data_read_page *page); int ring_buffer_read_page(struct trace_buffer *buffer, struct buffer_data_read_page *data_page, size_t len, int cpu, int full); void *ring_buffer_read_page_data(struct buffer_data_read_page *page); +unsigned int ring_buffer_read_page_size(struct buffer_data_read_page *rpage); struct trace_seq; @@ -278,11 +280,25 @@ static inline struct ring_buffer_desc *__first_ring_buffer_desc(struct trace_buf return (struct ring_buffer_desc *)(&desc->__data[0]); } +/* + * Returns the number of pages for a ring_buffer_desc. The caller must ensure it + * does not overflow ring_buffer_desc::nr_page_va. + */ +static inline unsigned long __calc_nr_pages_ring_buffer_desc(size_t size) +{ + /* Takes into account the reader page */ + return max(DIV_ROUND_UP(size, PAGE_SIZE - BUF_PAGE_HDR_SIZE), 2UL) + 1; +} + static inline size_t trace_buffer_desc_size(size_t buffer_size, unsigned int nr_cpus) { - unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; + unsigned long nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size); struct ring_buffer_desc *rbdesc; + /* Capped by ring_buffer_desc::nr_page_va */ + if (nr_pages > UINT_MAX) + return SIZE_MAX; + return size_add(offsetof(struct trace_buffer_desc, __data), size_mul(nr_cpus, struct_size(rbdesc, page_va, nr_pages))); } diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h index 9e5dad29669c..a97f7cfaacaa 100644 --- a/include/linux/rolling_buffer.h +++ b/include/linux/rolling_buffer.h @@ -45,9 +45,9 @@ struct rolling_buffer_snapshot { int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, unsigned int direction, gfp_t gfp); int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); -ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, - struct readahead_control *ractl, - struct folio_batch *put_batch); +ssize_t rolling_buffer_bulk_load_from_ra(struct rolling_buffer *roll, + struct readahead_control *ractl, + unsigned int rreq_id, gfp_t gfp); ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, unsigned int flags, gfp_t gfp); struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll); diff --git a/include/linux/sched.h b/include/linux/sched.h index 8b3d47a325cc..705970d07614 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1787,7 +1787,7 @@ static inline bool is_lazy_mmu_mode_active(void) } #endif -extern struct pid *cad_pid; +extern struct pid __rcu *cad_pid; /* * Per process flags diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h index 4e3338103654..922935cc3383 100644 --- a/include/linux/sched/rt.h +++ b/include/linux/sched/rt.h @@ -52,8 +52,10 @@ static inline bool rt_or_dl_task_policy(struct task_struct *tsk) #ifdef CONFIG_RT_MUTEXES extern void rt_mutex_pre_schedule(void); +extern void rt_mutex_futex_pre_schedule(void); extern void rt_mutex_schedule(void); extern void rt_mutex_post_schedule(void); +extern void rt_mutex_futex_post_schedule(void); /* * Must hold either p->pi_lock or task_rq(p)->lock. diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index 584ae88b435e..d45a5476b97d 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -562,10 +562,7 @@ static inline sigset_t *sigmask_to_save(void) return res; } -static inline int kill_cad_pid(int sig, int priv) -{ - return kill_pid(cad_pid, sig, priv); -} +int kill_cad_pid(int sig, int priv); /* These can be the second arg to send_sig_info/send_group_sig_info. */ #define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0) diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index 4cc52698e214..8d7e5521f7cd 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h @@ -25,7 +25,8 @@ struct user_struct { #if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \ defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \ - defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) + defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) || \ + defined(CONFIG_SECRETMEM) atomic_long_t locked_vm; #endif #ifdef CONFIG_WATCH_QUEUE diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 671c13494566..421f6fc45451 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3082,6 +3082,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb) return skb->transport_header != (typeof(skb->transport_header))~0U; } +static inline void skb_unset_transport_header(struct sk_buff *skb) +{ + skb->transport_header = (typeof(skb->transport_header))~0U; +} + static inline unsigned char *skb_transport_header(const struct sk_buff *skb) { DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb)); diff --git a/include/media/v4l2-hevc.h b/include/media/v4l2-hevc.h new file mode 100644 index 000000000000..973c96be16be --- /dev/null +++ b/include/media/v4l2-hevc.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Helper functions for HEVC stateless codecs. + */ + +#ifndef _MEDIA_V4L2_HEVC_H +#define _MEDIA_V4L2_HEVC_H + +#include <linux/minmax.h> +#include <media/v4l2-ctrls.h> + +/** + * v4l2_hevc_pps_num_tile_columns - number of HEVC tile columns, bounded + * @pps: the V4L2 HEVC PPS control + * + * Return the number of tile columns (num_tile_columns_minus1 + 1) clamped to + * the capacity of column_width_minus1[]. The control validation already + * rejects out-of-range counts; this keeps the consuming drivers bounded too. + */ +static inline unsigned int +v4l2_hevc_pps_num_tile_columns(const struct v4l2_ctrl_hevc_pps *pps) +{ + return min_t(unsigned int, pps->num_tile_columns_minus1 + 1, + ARRAY_SIZE(pps->column_width_minus1)); +} + +/** + * v4l2_hevc_pps_num_tile_rows - number of HEVC tile rows, bounded + * @pps: the V4L2 HEVC PPS control + * + * Return the number of tile rows (num_tile_rows_minus1 + 1) clamped to the + * capacity of row_height_minus1[]. + */ +static inline unsigned int +v4l2_hevc_pps_num_tile_rows(const struct v4l2_ctrl_hevc_pps *pps) +{ + return min_t(unsigned int, pps->num_tile_rows_minus1 + 1, + ARRAY_SIZE(pps->row_height_minus1)); +} + +#endif /* _MEDIA_V4L2_HEVC_H */ diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 3357ee62d10b..5549298c1ec6 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -229,6 +229,9 @@ struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr, struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src, struct sockaddr_vm *dst, struct net *net); +bool vsock_check_source(const struct vsock_sock *vsk, + const struct vsock_transport *transport, + const struct sockaddr_vm *src); void vsock_remove_sock(struct vsock_sock *vsk); void vsock_for_each_connected_socket(struct vsock_transport *transport, void (*fn)(struct sock *sk)); diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 76d2cd2e2b30..2e960441ea49 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -376,6 +376,7 @@ struct cfg802154_mac_pkt { struct list_head node; struct sk_buff *skb; struct ieee802154_sub_if_data *sdata; + netdevice_tracker dev_tracker; u8 page; u8 channel; }; diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 238ad3349456..795fb41b45f5 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -88,8 +88,6 @@ struct ip6_sf_socklist { struct in6_addr sl_addr[] __counted_by(sl_max); }; -#define IP6_SFBLOCK 10 /* allocate this many at once */ - struct ipv6_mc_socklist { struct in6_addr addr; int ifindex; diff --git a/include/net/ip.h b/include/net/ip.h index a8f57b4f4aa2..6f602df72ee6 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -704,7 +704,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast, static __inline__ void inet_reset_saddr(struct sock *sk) { - inet_sk(sk)->inet_rcv_saddr = inet_sk(sk)->inet_saddr = 0; + inet_sk(sk)->inet_saddr = 0; + WRITE_ONCE(inet_sk(sk)->inet_rcv_saddr, 0); #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == PF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index c69f1c871922..b9e8d2b759e9 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -384,6 +384,8 @@ static inline unsigned int ip6_dst_mtu_maybe_forward(const struct dst_entry *dst rcu_read_unlock(); out: + mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); + return mtu - lwtunnel_headroom(dst->lwtstate, mtu); } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index cbc248776511..f35bd06a6bad 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -793,7 +793,7 @@ void qdisc_offload_query_caps(struct net_device *dev, struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, const struct Qdisc_ops *ops, struct netlink_ext_ack *extack); -void qdisc_free(struct Qdisc *qdisc); +void qdisc_free_rcu(struct Qdisc *qdisc); struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, const struct Qdisc_ops *ops, u32 parentid, struct netlink_ext_ack *extack); diff --git a/include/net/tcp.h b/include/net/tcp.h index 670c20876f26..436495ff2271 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -765,8 +765,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, void tcp_send_probe0(struct sock *); int tcp_write_wakeup(struct sock *, int mib); void tcp_send_fin(struct sock *sk); -void tcp_send_active_reset(struct sock *sk, gfp_t priority, - enum sk_rst_reason reason); +void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason); int tcp_send_synack(struct sock *); void tcp_push_one(struct sock *, unsigned int mss_now); void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags); diff --git a/include/rdma/uverbs_types.h b/include/rdma/uverbs_types.h index 5a07f9a6dcd1..6f3622892c0c 100644 --- a/include/rdma/uverbs_types.h +++ b/include/rdma/uverbs_types.h @@ -180,8 +180,6 @@ struct ib_uverbs_file { struct page *disassociate_page; struct xarray idr; - - struct mutex disassociation_lock; }; extern const struct uverbs_obj_type_class uverbs_idr_class; diff --git a/include/sound/soc.h b/include/sound/soc.h index f46b2bc2a022..5afc34b147b5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -699,7 +699,8 @@ struct snd_soc_dai_link_component { struct snd_soc_dai_link_ch_map { unsigned int cpu; unsigned int codec; - unsigned int ch_mask; + unsigned int cpu_ch_mask; + unsigned int codec_ch_mask; }; struct snd_soc_dai_link { diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 9b28e9aef4f1..9fbb69b9052d 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -250,8 +250,6 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct asoc_sdw_codec_info *info, bool playback); -int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai); int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix); /* MAXIM codec support */ diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cachefiles.h index 9259bc71049e..e3101410e8b2 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -372,7 +372,7 @@ TRACE_EVENT(cachefiles_rename, TRACE_EVENT(cachefiles_coherency, TP_PROTO(struct cachefiles_object *obj, ino_t ino, - u64 disk_aux, + const void *disk_aux, enum cachefiles_content content, enum cachefiles_coherency_trace why), @@ -389,12 +389,27 @@ TRACE_EVENT(cachefiles_coherency, ), TP_fast_assign( + union { + __be16 s[4]; + __be64 ll; + } x; + __entry->obj = obj->debug_id; __entry->why = why; __entry->content = content; __entry->ino = ino; __entry->aux = be64_to_cpup((__be64 *)obj->cookie->inline_aux); - __entry->disk_aux = disk_aux; + + /* cachefiles_xattr::data is 2-byte aligned but not 8-byte aligned. */ + if (disk_aux) { + x.s[0] = ((__be16 *)disk_aux)[0]; + x.s[1] = ((__be16 *)disk_aux)[1]; + x.s[2] = ((__be16 *)disk_aux)[2]; + x.s[3] = ((__be16 *)disk_aux)[3]; + __entry->disk_aux = be64_to_cpu(x.ll); + } else { + __entry->disk_aux = 0; + } ), TP_printk("o=%08x %s B=%llx c=%u aux=%llx dsk=%llx", diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h index 9df02c1511de..b06d8f99922d 100644 --- a/include/trace/events/dma.h +++ b/include/trace/events/dma.h @@ -134,7 +134,7 @@ DECLARE_EVENT_CLASS(dma_alloc_class, TP_fast_assign( __assign_str(device); __entry->virt_addr = virt_addr; - __entry->dma_addr = dma_addr; + __entry->dma_addr = virt_addr ? dma_addr : 0; __entry->size = size; __entry->flags = flags; __entry->dir = dir; diff --git a/include/trace/events/icmp.h b/include/trace/events/icmp.h index 09ae115099df..6937b778ae54 100644 --- a/include/trace/events/icmp.h +++ b/include/trace/events/icmp.h @@ -27,17 +27,20 @@ TRACE_EVENT(icmp_send, TP_fast_assign( struct iphdr *iph = ip_hdr(skb); - struct udphdr *uh = udp_hdr(skb); - int proto_4 = iph->protocol; + struct udphdr _uh, *uh = NULL; __be32 *p32; __entry->skbaddr = skb; __entry->type = type; __entry->code = code; - if (proto_4 != IPPROTO_UDP || (u8 *)uh < skb->head || - (u8 *)uh + sizeof(struct udphdr) - > skb_tail_pointer(skb)) { + if (iph->protocol == IPPROTO_UDP) + uh = skb_header_pointer(skb, + skb_network_offset(skb) + + (iph->ihl << 2), + sizeof(_uh), &_uh); + + if (!uh) { __entry->sport = 0; __entry->dport = 0; __entry->ulen = 0; diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h index f82588f6f90e..d05253afaf59 100644 --- a/include/trace/events/landlock.h +++ b/include/trace/events/landlock.h @@ -28,6 +28,16 @@ struct task_struct; #ifdef CREATE_TRACE_POINTS +/* About 6 KiB, leaving about 2 KiB for sibling helpers and fixed fields. */ +#define TRACE_UNTRUSTED_STR_OUTPUT_SIZE \ + (TRACE_SEQ_BUFFER_SIZE - TRACE_SEQ_BUFFER_SIZE / 4) + +/* + * A raw UTF-8 ellipsis (…) marks truncation and cannot collide with escaped + * input: ESCAPE_NAP renders every non-ASCII input byte in octal. + */ +#define TRACE_TRUNCATION_MARKER "\xe2\x80\xa6" + /* * Escapes @len bytes of an untrusted string into the trace sequence @p so it * cannot inject field separators or control characters into the ftrace text @@ -37,33 +47,59 @@ struct task_struct; * NUL-terminated or carries embedded NUL bytes (an abstract socket name) is * escaped in full instead of being truncated at the first NUL. * - * Return: a pointer into @p's buffer, or NULL if @src is NULL or the buffer is - * exhausted (normal when the trace buffer is full). + * Strings that exceed the output limit retain the largest complete escaped + * prefix followed by the truncation marker. + * + * Return: a pointer into @p's buffer, or NULL if @src is NULL or the fixed + * output reservation is unavailable. */ static inline const char * __trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len) { + const unsigned int escape_flags = ESCAPE_SPACE | ESCAPE_SPECIAL | + ESCAPE_NAP | ESCAPE_APPEND | + ESCAPE_OCTAL; + const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1; + size_t buf_size, prefix_len, prefix_size; int escaped_size; char *buf; - size_t buf_size = seq_buf_get_buf(&p->seq, &buf); - const char *ret = trace_seq_buffer_ptr(p); + const char *ret; - /* Buffer exhaustion is normal when the trace buffer is full. */ - if (!src || buf_size == 0) + buf_size = seq_buf_get_buf(&p->seq, &buf); + if (!src || buf_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE) return NULL; - escaped_size = - string_escape_mem(src, len, buf, buf_size, - ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NAP | - ESCAPE_APPEND | ESCAPE_OCTAL, - " ='\"\\"); - if (unlikely(escaped_size >= buf_size)) { - /* We need some room for the final '\0'. */ - seq_buf_set_overflow(&p->seq); - p->full = 1; - return NULL; + ret = trace_seq_buffer_ptr(p); + escaped_size = string_escape_mem(src, len, buf, + TRACE_UNTRUSTED_STR_OUTPUT_SIZE, + escape_flags, " ='\"\\"); + if (likely(escaped_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE)) { + seq_buf_commit(&p->seq, escaped_size); + trace_seq_putc(p, 0); + return ret; + } + + prefix_len = 0; + prefix_size = 0; + while (prefix_len < len) { + const char *const src_char = src + prefix_len; + int char_size; + + char_size = string_escape_mem(src_char, 1, NULL, 0, + escape_flags, " ='\"\\"); + if (char_size > TRACE_UNTRUSTED_STR_OUTPUT_SIZE - marker_len - + 1 - prefix_size) + break; + prefix_size += char_size; + prefix_len++; } - seq_buf_commit(&p->seq, escaped_size); + + escaped_size = string_escape_mem(src, prefix_len, buf, prefix_size, + escape_flags, " ='\"\\"); + if (WARN_ON_ONCE(escaped_size != prefix_size)) + return NULL; + memcpy(buf + prefix_size, TRACE_TRUNCATION_MARKER, marker_len); + seq_buf_commit(&p->seq, prefix_size + marker_len); trace_seq_putc(p, 0); return ret; } diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 082cb03c6131..3fec3e8f91c8 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -59,6 +59,7 @@ EM(netfs_rreq_trace_free, "FREE ") \ EM(netfs_rreq_trace_intr, "INTR ") \ EM(netfs_rreq_trace_ki_complete, "KI-CMPL") \ + EM(netfs_rreq_trace_ra_put_ref, "RA-PUT ") \ EM(netfs_rreq_trace_recollect, "RECLLCT") \ EM(netfs_rreq_trace_redirty, "REDIRTY") \ EM(netfs_rreq_trace_resubmit, "RESUBMT") \ @@ -70,9 +71,11 @@ EM(netfs_rreq_trace_unpause, "UNPAUSE") \ EM(netfs_rreq_trace_wait_ip, "WAIT-IP") \ EM(netfs_rreq_trace_wait_pause, "--PAUSED--") \ + EM(netfs_rreq_trace_wait_put_ra_refs, "WAIT-P-RA") \ EM(netfs_rreq_trace_wait_quiesce, "WAIT-QUIESCE") \ EM(netfs_rreq_trace_waited_ip, "DONE-IP") \ EM(netfs_rreq_trace_waited_pause, "--UNPAUSED--") \ + EM(netfs_rreq_trace_waited_put_ra_refs, "DONE-P-RA") \ EM(netfs_rreq_trace_waited_quiesce, "DONE-QUIESCE") \ EM(netfs_rreq_trace_wake_ip, "WAKE-IP") \ EM(netfs_rreq_trace_wake_queue, "WAKE-Q ") \ @@ -195,7 +198,6 @@ EM(netfs_folio_trace_clear_cc, "clear-cc") \ EM(netfs_folio_trace_clear_g, "clear-g") \ EM(netfs_folio_trace_clear_s, "clear-s") \ - EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ EM(netfs_folio_trace_invalidate_all, "inval-all") \ @@ -206,16 +208,19 @@ EM(netfs_folio_trace_kill_cc, "kill-cc") \ EM(netfs_folio_trace_kill_g, "kill-g") \ EM(netfs_folio_trace_kill_s, "kill-s") \ + EM(netfs_folio_trace_mark_copy, "mark-copy") \ EM(netfs_folio_trace_mkwrite, "mkwrite") \ EM(netfs_folio_trace_mkwrite_plus, "mkwrite+") \ - EM(netfs_folio_trace_not_under_wback, "!wback") \ EM(netfs_folio_trace_not_locked, "!locked") \ + EM(netfs_folio_trace_not_under_wback, "!wback") \ + EM(netfs_folio_trace_pgpriv2_copy, "pgpriv2-copy") \ EM(netfs_folio_trace_put, "put") \ EM(netfs_folio_trace_read, "read") \ EM(netfs_folio_trace_read_done, "read-done") \ EM(netfs_folio_trace_read_gaps, "read-gaps") \ EM(netfs_folio_trace_read_unlock, "read-unlock") \ EM(netfs_folio_trace_redirtied, "redirtied") \ + EM(netfs_folio_trace_sched_copy, "sched-copy") \ EM(netfs_folio_trace_store, "store") \ EM(netfs_folio_trace_store_copy, "store-copy") \ EM(netfs_folio_trace_store_plus, "store+") \ @@ -786,6 +791,27 @@ TRACE_EVENT(netfs_folioq, __print_symbolic(__entry->trace, netfs_folioq_traces)) ); +TRACE_EVENT(netfs_read_progress_at, + TP_PROTO(const struct netfs_io_request *rreq), + + TP_ARGS(rreq), + + TP_STRUCT__entry( + __field(unsigned int, rreq) + __field(size_t, progress_at) + __field(size_t, cleaned_to) + ), + + TP_fast_assign( + __entry->rreq = rreq->debug_id; + __entry->cleaned_to = rreq->cleaned_to - rreq->start; + __entry->progress_at = rreq->progress_at; + ), + + TP_printk("R=%08x cln=%zx prg=%zx", + __entry->rreq, __entry->cleaned_to, __entry->progress_at) + ); + #undef EM #undef E_ #endif /* _TRACE_NETFS_H */ diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h index ca82fd62dc30..3aa0608c6361 100644 --- a/include/trace/events/timer.h +++ b/include/trace/events/timer.h @@ -302,7 +302,7 @@ DECLARE_EVENT_CLASS(hrtimer_class, * hrtimer_start_expired - Invoked when a expired timer was started * @hrtimer: pointer to struct hrtimer * - * Preceeded by a hrtimer_start tracepoint. + * Preceded by a hrtimer_start tracepoint. */ DEFINE_EVENT(hrtimer_class, hrtimer_start_expired, diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index b32c72a662b6..42a5fa8ad6b0 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -1512,6 +1512,7 @@ struct drm_amdgpu_info_device { __u64 high_va_max; /* gfx10 pa_sc_tile_steering_override */ __u32 pa_sc_tile_steering_override; + __u32 pad; /* disabled TCCs */ __u64 tcc_disabled_mask; __u64 min_engine_clock; @@ -1536,7 +1537,6 @@ struct drm_amdgpu_info_device { __u32 csa_alignment; /* Userq IP mask (1 << AMDGPU_HW_IP_*) */ __u32 userq_ip_mask; - __u32 pad; }; struct drm_amdgpu_info_hw_ip { diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index fd5f0e95648e..66a91c8b1cb9 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -8,6 +8,7 @@ #define AUTOFS_SUPER_MAGIC 0x0187 #define CEPH_SUPER_MAGIC 0x00c36400 #define CODA_SUPER_MAGIC 0x73757245 +#define CONFIGFS_MAGIC 0x62656570 /* some random number */ #define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ #define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */ #define DEBUGFS_MAGIC 0x64626720 diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index 856a1b3036e9..15ed2a51f8f1 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -250,7 +250,7 @@ struct bnxt_re_query_device_ex_resp { struct bnxt_re_db_region { __u32 dpi; __u32 reserved; - __aligned_u64 umdbr; + __aligned_u64 reserved2; }; enum bnxt_re_obj_dbr_alloc_attrs { diff --git a/init/initramfs_test.c b/init/initramfs_test.c index 9cf316c13ffa..1154547721ea 100644 --- a/init/initramfs_test.c +++ b/init/initramfs_test.c @@ -514,7 +514,7 @@ static void __init initramfs_test_hdr_hex(struct kunit *test) char fdata[] = "this file data will not be unpacked"; struct initramfs_test_bufs { char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2]; - } *tbufs = kzalloc(sizeof(struct initramfs_test_bufs), GFP_KERNEL); + } *tbufs = kzalloc_obj(struct initramfs_test_bufs); struct initramfs_test_cpio c[] = { { .magic = "070701", .ino = 1, diff --git a/init/main.c b/init/main.c index 2613d3f9b3ce..31f2bf54976a 100644 --- a/init/main.c +++ b/init/main.c @@ -277,7 +277,8 @@ static void * __init get_boot_config_from_initrd(size_t *_size) u8 *hdr; int i; - if (!initrd_end) + if (!initrd_end || initrd_end < initrd_start || + initrd_end - initrd_start < BOOTCONFIG_MAGIC_LEN + 8) return NULL; data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN; @@ -294,16 +295,26 @@ static void * __init get_boot_config_from_initrd(size_t *_size) found: hdr = (u8 *)(data - 8); + if ((unsigned long)hdr < initrd_start) + return NULL; + size = get_unaligned_le32(hdr); csum = get_unaligned_le32(hdr + 4); - data = ((void *)hdr) - size; - if ((unsigned long)data < initrd_start) { - pr_err("bootconfig size %d is greater than initrd size %ld\n", + if (size > XBC_DATA_MAX) { + pr_err("bootconfig size %u is greater than max size %d\n", + size, XBC_DATA_MAX); + return NULL; + } + + if (size > ((unsigned long)hdr - initrd_start)) { + pr_err("bootconfig size %u is greater than initrd size %lu\n", size, initrd_end - initrd_start); return NULL; } + data = ((void *)hdr) - size; + if (xbc_calc_checksum(data, size) != csum) { pr_err("bootconfig checksum failed\n"); return NULL; @@ -394,12 +405,6 @@ static void __init setup_boot_config(void) return; } - if (size >= XBC_DATA_MAX) { - pr_err("bootconfig size %ld greater than max size %d\n", - (long)size, XBC_DATA_MAX); - return; - } - ret = xbc_init(data, size, &msg, &pos); if (ret < 0) { if (pos < 0) @@ -1648,7 +1653,7 @@ static noinline void __init kernel_init_freeable(void) */ set_mems_allowed(node_states[N_MEMORY]); - cad_pid = get_pid(task_pid(current)); + rcu_assign_pointer(cad_pid, get_pid(task_pid(current))); smp_prepare_cpus(setup_max_cpus); diff --git a/io_uring/napi.c b/io_uring/napi.c index bfc771445912..ca1c814fa73a 100644 --- a/io_uring/napi.c +++ b/io_uring/napi.c @@ -58,7 +58,7 @@ int __io_napi_add_id(struct io_ring_ctx *ctx, unsigned int napi_id, } } - e = kmalloc(sizeof(*e), GFP_NOWAIT); + e = kmalloc_obj(*e, GFP_NOWAIT); if (!e) return -ENOMEM; diff --git a/io_uring/net.c b/io_uring/net.c index fbe719d86c46..050ed274170a 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -853,7 +853,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) static inline bool io_recv_finish(struct io_kiocb *req, struct io_async_msghdr *kmsg, struct io_br_sel *sel, bool mshot_finished, - unsigned issue_flags) + unsigned issue_flags, int consumed) { struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); unsigned int cflags = 0; @@ -877,7 +877,7 @@ static inline bool io_recv_finish(struct io_kiocb *req, if (sr->flags & IORING_RECVSEND_BUNDLE) { size_t this_ret = sel->val - sr->done_io; - cflags |= io_put_kbufs(req, this_ret, sel->buf_list, io_bundle_nbufs(kmsg, this_ret)); + cflags |= io_put_kbufs(req, consumed, sel->buf_list, io_bundle_nbufs(kmsg, consumed)); if (sr->flags & IORING_RECV_RETRY) cflags = req->cqe.flags | (cflags & CQE_F_MASK); if (sr->mshot_len && sel->val >= sr->mshot_len) @@ -899,7 +899,7 @@ static inline bool io_recv_finish(struct io_kiocb *req, return false; } } else { - cflags |= io_put_kbuf(req, sel->val, sel->buf_list); + cflags |= io_put_kbuf(req, consumed, sel->buf_list); } /* @@ -1027,6 +1027,8 @@ int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags) int ret, min_ret = 0; bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; bool mshot_finished = true; + int consumed = 0; + size_t len; sock = sock_from_file(req->file); if (unlikely(!sock)) @@ -1042,9 +1044,8 @@ int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags) retry_multishot: sel.buf_list = NULL; + len = sr->len; if (io_do_buffer_select(req)) { - size_t len = sr->len; - sel = io_buffer_select(req, &len, sr->buf_group, issue_flags); if (!sel.addr) return -ENOBUFS; @@ -1065,6 +1066,7 @@ retry_multishot: if (req->flags & REQ_F_APOLL_MULTISHOT) { ret = io_recvmsg_multishot(sock, sr, kmsg, flags, &mshot_finished); + consumed = ret; } else { /* disable partial retry for recvmsg with cmsg attached */ if (flags & MSG_WAITALL && !kmsg->msg.msg_controllen) @@ -1072,6 +1074,15 @@ retry_multishot: ret = __sys_recvmsg_sock(sock, &kmsg->msg, sr->umsg, kmsg->uaddr, flags); + /* + * With MSG_TRUNC, the net layer will return the full size of + * the packet, even if we only filled part of it in the buffers. + * Adjust the returned size to consume only the real part of the + * buffer. + */ + consumed = ret; + if (ret > 0) + consumed = min_t(size_t, ret, len); } if (ret < min_ret) { @@ -1098,7 +1109,7 @@ retry_multishot: io_kbuf_recycle(req, sel.buf_list, issue_flags); sel.val = ret; - if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags)) + if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed)) goto retry_multishot; return sel.val; @@ -1108,6 +1119,7 @@ static int io_recv_buf_select(struct io_kiocb *req, struct io_async_msghdr *kmsg struct io_br_sel *sel, unsigned int issue_flags) { struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); + size_t len; int ret; /* @@ -1153,13 +1165,14 @@ static int io_recv_buf_select(struct io_kiocb *req, struct io_async_msghdr *kmsg /* special case 1 vec, can be a fast path */ if (ret == 1) { sr->buf = arg.iovs[0].iov_base; - sr->len = arg.iovs[0].iov_len; + len = sr->len = arg.iovs[0].iov_len; goto map_ubuf; } iov_iter_init(&kmsg->msg.msg_iter, ITER_DEST, arg.iovs, ret, - arg.out_len); + arg.out_len); + len = arg.out_len; } else { - size_t len = sel->val; + len = sel->val; *sel = io_buffer_select(req, &len, sr->buf_group, issue_flags); if (!sel->addr) @@ -1173,7 +1186,7 @@ map_ubuf: return ret; } - return 0; + return len; } int io_recv(struct io_kiocb *req, unsigned int issue_flags) @@ -1183,9 +1196,10 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags) struct io_br_sel sel; struct socket *sock; unsigned flags; - int ret, min_ret = 0; + int ret, min_ret = 0, consumed = 0; bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; bool mshot_finished; + size_t len = 0; sock = sock_from_file(req->file); if (unlikely(!sock)) @@ -1213,6 +1227,7 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags) retry_multishot: sel.buf_list = NULL; + len = sr->len; if (io_do_buffer_select(req)) { sel.val = sr->len; ret = io_recv_buf_select(req, kmsg, &sel, issue_flags); @@ -1220,6 +1235,7 @@ retry_multishot: kmsg->msg.msg_inq = -1; goto out_free; } + len = ret; sr->buf = NULL; } @@ -1250,6 +1266,17 @@ out_free: } mshot_finished = ret <= 0; + + /* + * With MSG_TRUNC, the net layer will return the full size of + * the packet, even if we only filled part of it in the buffers. + * Adjust the returned size to consume only the real part of the + * buffer. + */ + consumed = ret; + if (ret > 0) + consumed = min_t(size_t, ret, len); + if (ret > 0) ret += sr->done_io; else if (sr->done_io) @@ -1258,7 +1285,7 @@ out_free: io_kbuf_recycle(req, sel.buf_list, issue_flags); sel.val = ret; - if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags)) + if (!io_recv_finish(req, kmsg, &sel, mshot_finished, issue_flags, consumed)) goto retry_multishot; return sel.val; diff --git a/io_uring/rw.c b/io_uring/rw.c index 95106dd1d7eb..432820f86251 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -517,20 +517,25 @@ static void io_req_end_write(struct io_kiocb *req) } } -/* - * Trigger the notifications after having done some IO, and finish the write - * accounting, if any. - */ -static void io_req_io_end(struct io_kiocb *req) +/* Trigger the notifications after having done some IO. */ +static void io_req_io_notify(struct io_kiocb *req) { struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); - if (rw->kiocb.ki_flags & IOCB_WRITE) { - io_req_end_write(req); + if (rw->kiocb.ki_flags & IOCB_WRITE) fsnotify_modify(req->file); - } else { + else fsnotify_access(req->file); - } +} + +/* Finish write accounting and notify, for inline completions only. */ +static void io_req_io_end(struct io_kiocb *req) +{ + struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); + + if (rw->kiocb.ki_flags & IOCB_WRITE) + io_req_end_write(req); + io_req_io_notify(req); } static void __io_complete_rw_common(struct io_kiocb *req, long res) @@ -563,7 +568,7 @@ void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw) { struct io_kiocb *req = tw_req.req; - io_req_io_end(req); + io_req_io_notify(req); if (req->flags & (REQ_F_BUFFER_SELECTED|REQ_F_BUFFER_RING)) req->cqe.flags |= io_put_kbuf(req, max(req->cqe.res, 0), NULL); @@ -577,6 +582,10 @@ static void io_complete_rw(struct kiocb *kiocb, long res) struct io_rw *rw = container_of(kiocb, struct io_rw, kiocb); struct io_kiocb *req = cmd_to_io_kiocb(rw); + /* ring owner may block in freeze_super() before task_work runs */ + if (kiocb->ki_flags & IOCB_WRITE) + io_req_end_write(req); + __io_complete_rw_common(req, res); io_req_set_res(req, io_fixup_rw_res(req, res), 0); req->io_task_work.func = io_req_rw_complete; @@ -871,6 +880,7 @@ static int io_rw_init_file(struct io_kiocb *req, fmode_t mode, int rw_type) kiocb->private = NULL; kiocb->ki_flags |= IOCB_HIPRI; req->iopoll_completed = 0; + req->cqe.flags = 0; if (ctx->flags & IORING_SETUP_HYBRID_IOPOLL) { /* make sure every req only blocks once*/ req->flags &= ~REQ_F_IOPOLL_STATE; @@ -1373,7 +1383,7 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin) list_del(&req->iopoll_node); wq_list_add_tail(&req->comp_list, &ctx->submit_state.compl_reqs); nr_events++; - req->cqe.flags = io_put_kbuf(req, max(req->cqe.res, 0), NULL); + req->cqe.flags |= io_put_kbuf(req, max(req->cqe.res, 0), NULL); if (!io_is_uring_cmd(req)) io_req_rw_cleanup(req, 0); } diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 1b3b11405dac..86d580d4410d 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -483,8 +483,8 @@ static int io_zcrx_append_area(struct io_zcrx_ifq *ifq, old_areas = ifq->areas; old_nr = ifq->nr_areas; - areas = kmalloc_array(old_nr + 1, sizeof(areas[0]), - GFP_KERNEL_ACCOUNT | __GFP_ZERO); + areas = kmalloc_objs(areas[0], old_nr + 1, + GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!areas) return -ENOMEM; if (old_areas) diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index ef315b168b29..0ce26b538075 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -436,7 +436,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value, void __percpu *pptr; void *ptr, *val; u32 size; - int cpu; + int cpu, off = 0; if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS)) /* unknown flags */ @@ -468,9 +468,10 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value, } for_each_possible_cpu(cpu) { ptr = per_cpu_ptr(pptr, cpu); - val = (map_flags & BPF_F_ALL_CPUS) ? value : value + size * cpu; + val = (map_flags & BPF_F_ALL_CPUS) ? value : value + off; copy_map_value(map, ptr, val); bpf_obj_cancel_fields(map, ptr); + off += size; } unlock: rcu_read_unlock(); diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c index a2b18a9f1694..47282ffeeaf9 100644 --- a/kernel/bpf/backtrack.c +++ b/kernel/bpf/backtrack.c @@ -520,37 +520,34 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, return -EFAULT; } } else if (opcode == BPF_EXIT) { - bool r0_precise; + bool from_subprog_call, r0_precise; + + /* BPF_EXIT in subprog or callback always returns + * right after the call instruction, so by checking + * whether the instruction at subseq_idx-1 is subprog + * call or not we can distinguish actual exit from + * *subprog* from exit from *callback*. In the former + * case, we need to propagate r0 precision, if + * necessary. In the former we never do that. + */ + from_subprog_call = subseq_idx - 1 >= 0 && + bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]); + + r0_precise = from_subprog_call && bt_is_reg_set(bt, BPF_REG_0); /* Backtracking to a nested function call, 'idx' is a part of * the inner frame 'subseq_idx' is a part of the outer frame. * In case of a regular function call, instructions giving * precision to registers R1-R5 should have been found already. - * In case of a callback, it is ok to have R1-R5 marked for - * backtracking, as these registers are set by the function - * invoking callback. + * In case of a callback from bpf_loop(), R{1,4} in the calling + * frame would be set as precise and that is correct. */ - if (subseq_idx >= 0 && bpf_calls_callback(env, subseq_idx)) - for (i = BPF_REG_1; i <= BPF_REG_5; i++) - bt_clear_reg(bt, i); - if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { + if (from_subprog_call && (bt_reg_mask(bt) & BPF_REGMASK_ARGS)) { verifier_bug(env, "backtracking exit unexpected regs %x", bt_reg_mask(bt)); return -EFAULT; } - /* BPF_EXIT in subprog or callback always returns - * right after the call instruction, so by checking - * whether the instruction at subseq_idx-1 is subprog - * call or not we can distinguish actual exit from - * *subprog* from exit from *callback*. In the former - * case, we need to propagate r0 precision, if - * necessary. In the former we never do that. - */ - r0_precise = subseq_idx - 1 >= 0 && - bpf_pseudo_call(&env->prog->insnsi[subseq_idx - 1]) && - bt_is_reg_set(bt, BPF_REG_0); - bt_clear_reg(bt, BPF_REG_0); if (bt_subprog_enter(bt)) return -EFAULT; @@ -582,16 +579,29 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, */ } } else if (class == BPF_LD) { - if (!bt_is_reg_set(bt, dreg)) - return 0; - bt_clear_reg(bt, dreg); /* It's ld_imm64 or ld_abs or ld_ind. * For ld_imm64 no further tracking of precision * into parent is necessary */ - if (mode == BPF_IND || mode == BPF_ABS) - /* to be analyzed */ - return -ENOTSUPP; + if (mode == BPF_IMM) { + bt_clear_reg(bt, dreg); + return 0; + } + /* + * BPF_{IND,ABS} are modelled as two branches: + * - fallthrough; + * - implicit subprogram exit. + * It is necessary to switch current frame if + * implicit subprogram exit branch is backtracked. + */ + if (mode == BPF_IND || mode == BPF_ABS) { + if (bt_is_reg_set(bt, dreg)) + return -ENOTSUPP; + if (subseq_idx != idx + 1) + if (bt_subprog_enter(bt)) + return -EFAULT; + return 0; + } } /* Propagate precision marks to linked registers, to account for * registers marked as precise in this function. diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c index 14a5fdfa0421..b40eb404adab 100644 --- a/kernel/bpf/bpf_iter.c +++ b/kernel/bpf/bpf_iter.c @@ -754,7 +754,7 @@ const struct bpf_func_proto bpf_loop_proto = { .func = bpf_loop, .gpl_only = false, .ret_type = RET_INTEGER, - .arg1_type = ARG_ANYTHING, + .arg1_type = ARG_SCALAR, .arg2_type = ARG_PTR_TO_FUNC, .arg3_type = ARG_PTR_TO_STACK_OR_NULL, .arg4_type = ARG_ANYTHING, diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index da36d4b9d31a..9f33e95d5741 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -2911,14 +2911,29 @@ static void btf_modifier_show(const struct btf *btf, else t = btf_type_skip_modifiers(btf, type_id, NULL); - btf_type_ops(t)->show(btf, t, type_id, data, bits_offset, show); + /* + * A modifier can resolve to void, which has no show op; print a + * placeholder rather than dereferencing NULL. + */ + if (!btf_type_ops(t)) + btf_df_show(btf, t, type_id, data, bits_offset, show); + else + btf_type_ops(t)->show(btf, t, type_id, data, bits_offset, show); } static void btf_var_show(const struct btf *btf, const struct btf_type *t, u32 type_id, void *data, u8 bits_offset, struct btf_show *show) { - t = btf_type_id_resolve(btf, &type_id); + /* + * btf_type_id_resolve() dereferences btf->resolved_ids, which is NULL + * for a base BTF (e.g. the vmlinux BTF that bpf_snprintf_btf() uses). + * Resolve the var's type directly in that case. + */ + if (btf->resolved_ids) + t = btf_type_id_resolve(btf, &type_id); + else + t = btf_type_skip_modifiers(btf, t->type, &type_id); btf_type_ops(t)->show(btf, t, type_id, data, bits_offset, show); } @@ -6657,6 +6672,10 @@ struct bpf_raw_tp_null_args { static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { /* sched */ { "sched_pi_setprio", 0x10 }, + /* + * do_wait() passes NULL for wait4(-1) and waitid(P_ALL). + */ + { "sched_process_wait", 0x1 }, /* ... from sched_numa_pair_template event class */ { "sched_stick_numa", 0x100 }, { "sched_swap_numa", 0x100 }, @@ -6717,6 +6736,9 @@ static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { { "rxrpc_resend", 0x10 }, { "rxrpc_tq", 0x10 }, { "rxrpc_client", 0x1 }, + /* signal */ + { "signal_generate", 0x20 }, + { "signal_deliver", 0x20 }, /* skb */ {"kfree_skb", 0x1000}, /* sunrpc */ @@ -8727,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = { .func = bpf_btf_find_by_name_kind, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY, .arg2_type = ARG_MEM_SIZE, diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c index 0f13c13f4133..842c7d1eabcc 100644 --- a/kernel/bpf/cfg.c +++ b/kernel/bpf/cfg.c @@ -125,6 +125,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env) /* mark branch target for state pruning */ mark_prune_point(env, w); mark_jmp_point(env, w); + mark_jump_target(env, w); } if (insn_state[w] == 0) { @@ -403,6 +404,7 @@ static int visit_gotox_insn(int t, struct bpf_verifier_env *env) } mark_jmp_point(env, w); + mark_jump_target(env, w); /* EXPLORED || DISCOVERED */ if (insn_state[w]) @@ -564,6 +566,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env) mark_prune_point(env, t + off + 1); mark_jmp_point(env, t + off + 1); + mark_jump_target(env, t + off + 1); return ret; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index d55e737ed75a..8b294dfc1ad4 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1128,11 +1128,6 @@ void *bpf_jit_alloc_exec(unsigned long size) return execmem_alloc(EXECMEM_BPF, size); } -void *bpf_jit_alloc_exec_rw(unsigned long size) -{ - return execmem_alloc_rw(EXECMEM_BPF, size); -} - void bpf_jit_free_exec(void *addr) { execmem_free(addr); diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c index b682fd2be443..0abbbe177e31 100644 --- a/kernel/bpf/diagnostics.c +++ b/kernel/bpf/diagnostics.c @@ -266,7 +266,7 @@ static char *diag_fmt_alloc(struct bpf_verifier_env *env, size_t size) } capacity = max_t(size_t, BPF_DIAG_FMT_CHUNK_SIZE, size); - chunk = kmalloc(struct_size(chunk, data, capacity), GFP_KERNEL_ACCOUNT); + chunk = kmalloc_flex(*chunk, data, capacity, GFP_KERNEL_ACCOUNT); if (!chunk) return NULL; diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c index 50b3ca5149a0..3ce8d74b0e40 100644 --- a/kernel/bpf/disasm.c +++ b/kernel/bpf/disasm.c @@ -7,6 +7,9 @@ #include "disasm.h" +/* Only defined by the non-UAPI linux/filter.h, which this file cannot use. */ +#define BPF_PROBE_ATOMIC 0xe0 + #define __BPF_FUNC_STR_FN(x) [BPF_FUNC_ ## x] = __stringify(bpf_ ## x) static const char * const func_id_str[] = { __BPF_FUNC_MAPPER(__BPF_FUNC_STR_FN) @@ -226,57 +229,57 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs, insn->imm); } } else if (class == BPF_STX) { + const char *probe_pfx = BPF_MODE(insn->code) == BPF_PROBE_ATOMIC ? "probe " : ""; + bool atomic = BPF_MODE(insn->code) == BPF_ATOMIC || + BPF_MODE(insn->code) == BPF_PROBE_ATOMIC; + if (BPF_MODE(insn->code) == BPF_MEM) verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); - else if (BPF_MODE(insn->code) == BPF_ATOMIC && + else if (atomic && (insn->imm == BPF_ADD || insn->imm == BPF_AND || insn->imm == BPF_OR || insn->imm == BPF_XOR)) { - verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d", - insn->code, + verbose(cbs->private_data, "(%02x) %slock *(%s *)(r%d %+d) %s r%d", + insn->code, probe_pfx, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, bpf_alu_string[BPF_OP(insn->imm) >> 4], insn->src_reg); - } else if (BPF_MODE(insn->code) == BPF_ATOMIC && + } else if (atomic && (insn->imm == (BPF_ADD | BPF_FETCH) || insn->imm == (BPF_AND | BPF_FETCH) || insn->imm == (BPF_OR | BPF_FETCH) || insn->imm == (BPF_XOR | BPF_FETCH))) { - verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)", - insn->code, insn->src_reg, + verbose(cbs->private_data, "(%02x) %sr%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)", + insn->code, probe_pfx, insn->src_reg, BPF_SIZE(insn->code) == BPF_DW ? "64" : "", bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4], bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); - } else if (BPF_MODE(insn->code) == BPF_ATOMIC && - insn->imm == BPF_CMPXCHG) { - verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)", - insn->code, + } else if (atomic && insn->imm == BPF_CMPXCHG) { + verbose(cbs->private_data, "(%02x) %sr0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)", + insn->code, probe_pfx, BPF_SIZE(insn->code) == BPF_DW ? "64" : "", bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); - } else if (BPF_MODE(insn->code) == BPF_ATOMIC && - insn->imm == BPF_XCHG) { - verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)", - insn->code, insn->src_reg, + } else if (atomic && insn->imm == BPF_XCHG) { + verbose(cbs->private_data, "(%02x) %sr%d = atomic%s_xchg((%s *)(r%d %+d), r%d)", + insn->code, probe_pfx, insn->src_reg, BPF_SIZE(insn->code) == BPF_DW ? "64" : "", bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); - } else if (BPF_MODE(insn->code) == BPF_ATOMIC && - insn->imm == BPF_LOAD_ACQ) { - verbose(cbs->private_data, "(%02x) r%d = load_acquire((%s *)(r%d %+d))", - insn->code, insn->dst_reg, + } else if (atomic && insn->imm == BPF_LOAD_ACQ) { + verbose(cbs->private_data, "(%02x) %sr%d = load_acquire((%s *)(r%d %+d))", + insn->code, probe_pfx, insn->dst_reg, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->src_reg, insn->off); - } else if (BPF_MODE(insn->code) == BPF_ATOMIC && - insn->imm == BPF_STORE_REL) { - verbose(cbs->private_data, "(%02x) store_release((%s *)(r%d %+d), r%d)", - insn->code, + } else if (atomic && insn->imm == BPF_STORE_REL) { + verbose(cbs->private_data, "(%02x) %sstore_release((%s *)(r%d %+d), r%d)", + insn->code, probe_pfx, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); } else { @@ -295,7 +298,8 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs, verbose(cbs->private_data, "BUG_st_%02x", insn->code); } } else if (class == BPF_LDX) { - if (BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_MEMSX) { + if ((BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_MEMSX) || + (BPF_MODE(insn->code) == BPF_MEMSX && BPF_SIZE(insn->code) == BPF_DW)) { verbose(cbs->private_data, "BUG_ldx_%02x", insn->code); return; } diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 65b441e4a351..52d3cec33672 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -13,10 +13,15 @@ #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args) +/* + * Matches BPF_PROBE_ATOMIC too: bpf_convert_ctx_accesses() rewrites arena + * atomics before bpf_opt_subreg_zext_lo32_rnd_hi32() runs. + */ static bool is_cmpxchg_insn(const struct bpf_insn *insn) { return BPF_CLASS(insn->code) == BPF_STX && - BPF_MODE(insn->code) == BPF_ATOMIC && + (BPF_MODE(insn->code) == BPF_ATOMIC || + BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) && insn->imm == BPF_CMPXCHG; } diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d40cb5dd446c..6f331c80130d 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -511,7 +511,7 @@ static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma, if (IS_ERR_OR_NULL(map->record)) return 0; - hrec = kzalloc(sizeof(*hrec), GFP_KERNEL); + hrec = kzalloc_obj(*hrec); if (!hrec) return -ENOMEM; hrec->key_size = map->key_size; @@ -530,6 +530,9 @@ static int htab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + if (btf_type_is_void(key_type)) + return -EINVAL; + if (htab_is_prealloc(htab)) return 0; /* @@ -1025,7 +1028,7 @@ static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, } else { u32 size = round_up(htab->map.value_size, 8); void *val; - int cpu; + int cpu, off = 0; if (map_flags & BPF_F_CPU) { cpu = map_flags >> 32; @@ -1037,9 +1040,10 @@ static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, for_each_possible_cpu(cpu) { ptr = per_cpu_ptr(pptr, cpu); - val = (map_flags & BPF_F_ALL_CPUS) ? value : value + size * cpu; + val = (map_flags & BPF_F_ALL_CPUS) ? value : value + off; copy_map_value(&htab->map, ptr, val); bpf_obj_cancel_fields(&htab->map, ptr); + off += size; } } } @@ -2864,16 +2868,6 @@ static int rhtab_map_alloc_check(union bpf_attr *attr) return htab_map_alloc_check(attr); } -static void rhtab_check_and_free_fields(struct bpf_rhtab *rhtab, - struct rhtab_elem *elem) -{ - if (IS_ERR_OR_NULL(rhtab->map.record)) - return; - - bpf_obj_free_fields(rhtab->map.record, - rhtab_elem_value(elem, rhtab->map.key_size)); -} - static void rhtab_mem_dtor(void *obj, void *ctx) { struct htab_btf_record *hrec = ctx; @@ -2963,8 +2957,8 @@ static int rhtab_delete_elem(struct bpf_rhtab *rhtab, struct rhtab_elem *elem, v rhtab_read_elem_value(&rhtab->map, copy, elem, flags); check_and_init_map_value(&rhtab->map, copy); } - /* Release internal structs: kptr, bpf_timer, task_work, wq */ - rhtab_check_and_free_fields(rhtab, elem); + bpf_obj_cancel_fields(&rhtab->map, + rhtab_elem_value(elem, rhtab->map.key_size)); bpf_mem_cache_free_rcu(&rhtab->ma, elem); return 0; } @@ -3005,7 +2999,6 @@ static int rhtab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, void static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *elem, void *value, u64 map_flags) { - struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map); void *old_val = rhtab_elem_value(elem, map->key_size); if (map_flags & BPF_NOEXIST) @@ -3025,7 +3018,7 @@ static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *el * kptrs/etc. still sit in the slot. Cancel them after the copy * to match arraymap's update semantics. */ - rhtab_check_and_free_fields(rhtab, elem); + bpf_obj_cancel_fields(map, old_val); return 0; } @@ -3066,7 +3059,6 @@ static long rhtab_map_update_elem(struct bpf_map *map, void *key, void *value, u memcpy(elem->data, key, map->key_size); copy_map_value(map, rhtab_elem_value(elem, map->key_size), value); - check_and_init_map_value(map, rhtab_elem_value(elem, map->key_size)); /* Prevent deadlock for NMI programs attempting to take bucket lock */ bpf_disable_instrumentation(); @@ -3110,6 +3102,9 @@ static int rhtab_map_check_btf(struct bpf_map *map, const struct btf *btf, { struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map); + if (btf_type_is_void(key_type)) + return -EINVAL; + return bpf_ma_set_dtor(map, &rhtab->ma, rhtab_mem_dtor); } diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 74fc4b3f80d6..301fc60bddc4 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c @@ -85,7 +85,7 @@ static struct func_instance *call_instance(struct bpf_verifier_env *env, if (f) return f; - f = kvzalloc(sizeof(*f), GFP_KERNEL_ACCOUNT); + f = kvzalloc_obj(*f, GFP_KERNEL_ACCOUNT); if (!f) return ERR_PTR(-ENOMEM); f->callsite = lookup_key; diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 23267213a17f..83cd527a2542 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c @@ -220,7 +220,7 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *_map, void *key, struct bpf_cgroup_storage *storage; void *val; u32 size; - int cpu; + int cpu, off = 0; if ((u32)map_flags & ~(BPF_ANY | BPF_EXIST | BPF_F_CPU | BPF_F_ALL_CPUS)) return -EINVAL; @@ -245,8 +245,9 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *_map, void *key, } size = round_up(_map->value_size, 8); for_each_possible_cpu(cpu) { - val = (map_flags & BPF_F_ALL_CPUS) ? value : value + size * cpu; + val = (map_flags & BPF_F_ALL_CPUS) ? value : value + off; copy_map_value(_map, per_cpu_ptr(storage->percpu_buf, cpu), val); + off += size; } unlock: rcu_read_unlock(); diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c index 589770ca3d3a..fb032dfdc0de 100644 --- a/kernel/bpf/log.c +++ b/kernel/bpf/log.c @@ -862,7 +862,7 @@ struct bpf_verifier_log *bpf_log_attr_create_vlog(struct bpf_log_attr *attr_log, if (!size) return NULL; - log = kzalloc_obj(*log, GFP_KERNEL); + log = kzalloc_obj(*log); if (!log) return ERR_PTR(-ENOMEM); diff --git a/kernel/bpf/percpu_freelist.c b/kernel/bpf/percpu_freelist.c index 632762b57299..06ce588d13a3 100644 --- a/kernel/bpf/percpu_freelist.c +++ b/kernel/bpf/percpu_freelist.c @@ -17,6 +17,8 @@ int pcpu_freelist_init(struct pcpu_freelist *s) raw_res_spin_lock_init(&head->lock); head->first = NULL; } + raw_res_spin_lock_init(&s->extralist.lock); + s->extralist.first = NULL; return 0; } @@ -46,22 +48,28 @@ void __pcpu_freelist_push(struct pcpu_freelist *s, struct pcpu_freelist_node *node) { struct pcpu_freelist_head *head; - int cpu; + int cpu, this_cpu; if (___pcpu_freelist_push(this_cpu_ptr(s->freelist), node)) return; + this_cpu = raw_smp_processor_id(); while (true) { - for_each_cpu_wrap(cpu, cpu_possible_mask, raw_smp_processor_id()) { - if (cpu == raw_smp_processor_id()) + for_each_cpu_wrap(cpu, cpu_possible_mask, this_cpu) { + if (cpu == this_cpu) continue; + head = per_cpu_ptr(s->freelist, cpu); - if (raw_res_spin_lock(&head->lock)) - continue; - pcpu_freelist_push_node(head, node); - raw_res_spin_unlock(&head->lock); - return; + if (___pcpu_freelist_push(head, node)) + return; } + + /* + * Push cannot fail. Use the extra list when none of the + * per-CPU freelists can accept the node. + */ + if (___pcpu_freelist_push(&s->extralist, node)) + return; } } @@ -117,6 +125,17 @@ static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s) } raw_res_spin_unlock(&head->lock); } + + /* Per-CPU lists are empty or unavailable, try the extra list. */ + head = &s->extralist; + if (!READ_ONCE(head->first)) + return NULL; + if (raw_res_spin_lock(&head->lock)) + return NULL; + node = head->first; + if (node) + WRITE_ONCE(head->first, node->next); + raw_res_spin_unlock(&head->lock); return node; } diff --git a/kernel/bpf/percpu_freelist.h b/kernel/bpf/percpu_freelist.h index 914798b74967..980cf2884fd2 100644 --- a/kernel/bpf/percpu_freelist.h +++ b/kernel/bpf/percpu_freelist.h @@ -14,6 +14,7 @@ struct pcpu_freelist_head { struct pcpu_freelist { struct pcpu_freelist_head __percpu *freelist; + struct pcpu_freelist_head extralist; }; struct pcpu_freelist_node { diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index a839041e0d00..d09d4c3fe547 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si const struct bpf_func_proto bpf_get_stack_sleepable_proto = { .func = bpf_get_stack_sleepable, .gpl_only = true, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_CTX, .arg2_type = ARG_PTR_TO_UNINIT_MEM, @@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = { .func = bpf_get_task_stack_sleepable, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_BTF_ID, .arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK], diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 4e6aafad33bd..66fb11b6c6a7 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -445,22 +445,19 @@ static void __clean_func_state(struct bpf_verifier_env *env, struct bpf_reg_state *spill = &st->stack[i].spilled_ptr; if (lo_live && stype == STACK_SPILL) { - u8 val = STACK_MISC; - if (spill->type != SCALAR_VALUE) continue; - /* - * 8 byte spill of scalar 0 where half slot is dead - * should become STACK_ZERO in lo 4 bytes. + * Can't replace with STACK_ZERO, because + * that requires bpf_mark_chain_precision(). */ if (bpf_register_is_null(spill)) - val = STACK_ZERO; + continue; for (j = 0; j < 4; j++) { u8 *t = &st->stack[i].slot_type[j]; if (*t == STACK_SPILL) - *t = val; + *t = STACK_MISC; } } bpf_mark_reg_not_init(env, spill); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6874ba1424af..c7bc9ba9b331 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -6568,6 +6568,7 @@ EXPORT_SYMBOL_NS(kern_sys_bpf, "BPF_INTERNAL"); static const struct bpf_func_proto bpf_sys_bpf_proto = { .func = bpf_sys_bpf, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_ANYTHING, .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, @@ -6593,6 +6594,7 @@ BPF_CALL_1(bpf_sys_close, u32, fd) static const struct bpf_func_proto bpf_sys_close_proto = { .func = bpf_sys_close, .gpl_only = false, + .might_sleep = true, .ret_type = RET_INTEGER, .arg1_type = ARG_ANYTHING, }; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e421ea2b80c3..72a3f5998dd2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -352,9 +352,18 @@ static bool reg_not_null(struct bpf_verifier_env *env, const struct bpf_reg_stat if (type_may_be_null(type)) return false; + /* + * The types below guarantee a non-NULL base, an unbounded offset can + * still wrap base + offset to zero. + */ + if (reg_smin(reg) <= -BPF_MAX_VAR_OFF || reg_smax(reg) >= BPF_MAX_VAR_OFF) + return false; + type = base_type(type); return type == PTR_TO_SOCKET || type == PTR_TO_TCP_SOCK || + type == PTR_TO_XDP_SOCK || + type == PTR_TO_BUF || type == PTR_TO_MAP_VALUE || type == PTR_TO_MAP_KEY || type == PTR_TO_SOCK_COMMON || @@ -4237,6 +4246,15 @@ static int mark_stack_arg_precision(struct bpf_verifier_env *env, int arg_idx) return mark_chain_precision_batch(env, env->cur_state); } +static int mark_arg_precision(struct bpf_verifier_env *env, argno_t argno) +{ + int regno = reg_from_argno(argno); + + if (regno >= 0) + return mark_chain_precision(env, regno); + return mark_stack_arg_precision(env, arg_idx_from_argno(argno)); +} + static int check_outgoing_stack_args(struct bpf_verifier_env *env, struct bpf_func_state *caller, int nargs, const char *callee_name, const struct btf *btf, const struct btf_param *args) @@ -4486,6 +4504,13 @@ static int map_kptr_match_type(struct bpf_verifier_env *env, if (type_flag(reg->type) & ~perm_flags) goto bad_type; + /* + * A BPF_KPTR_PERCPU field is read back as MEM_PERCPU, so the value + * stored in it must carry the same flag. + */ + if ((kptr_field->type == BPF_KPTR_PERCPU) != !!(reg->type & MEM_PERCPU)) + goto bad_type; + /* We need to verify reg->type and reg->btf, before accessing reg->btf */ reg_name = btf_type_name(reg->btf, reg->btf_id); @@ -4692,8 +4717,15 @@ static int check_map_kptr_access(struct bpf_verifier_env *env, return ret; } else if (class == BPF_STX) { val_reg = reg_state(env, value_regno); - if (!bpf_register_is_null(val_reg) && - map_kptr_match_type(env, kptr_field, val_reg, value_regno)) + if (bpf_register_is_null(val_reg)) { + /* + * This store is valid only because the scalar is known to be + * zero. Mark it precise so another scalar cannot be pruned + * against this state. + */ + return mark_chain_precision(env, value_regno); + } + if (map_kptr_match_type(env, kptr_field, val_reg, value_regno)) return -EACCES; } else if (class == BPF_ST) { if (insn->imm) { @@ -5300,6 +5332,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, if (!priv_stack_supported) subprog[idx].priv_stack_mode = NO_PRIV_STACK; process_func: + if (subprog[idx].has_ld_abs) { + for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) { + if (subprog[tmp].is_cb) { + verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n"); + return -EINVAL; + } + } + } + /* protect against potential stack overflow that might happen when * bpf2bpf calls get combined with tailcalls. Limit the caller's stack * depth for such case down to 256 so that the worst case scenario @@ -5483,7 +5524,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) bool priv_stack_supported; int ret; - dinfo = kvcalloc(env->subprog_cnt, sizeof(*dinfo), GFP_KERNEL_ACCOUNT); + dinfo = kvzalloc_objs(*dinfo, env->subprog_cnt, GFP_KERNEL_ACCOUNT); if (!dinfo) return -ENOMEM; @@ -6020,7 +6061,13 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, return -EACCES; } - if (type_is_alloc(reg->type) && !type_is_non_owning_ref(reg->type) && + /* + * A fault-prone allocated object may still be read through a + * BPF_PROBE_MEM load after its lifetime protection ends. Writes + * through such pointers were rejected above. + */ + if (type_is_alloc(reg->type) && !bpf_may_fault_on_deref(reg->type) && + !type_is_non_owning_ref(reg->type) && !(reg->type & MEM_RCU) && !reg_is_referenced(env, reg)) { verifier_bug(env, "allocated object must have a referenced id"); return -EFAULT; @@ -7113,14 +7160,8 @@ static int check_mem_size_reg(struct bpf_verifier_env *env, if (err && failure) *failure = BPF_MEM_SIZE_FAIL_MEMORY; - if (!err) { - int regno = reg_from_argno(size_argno); - - if (regno >= 0) - err = mark_chain_precision(env, regno); - else - err = mark_stack_arg_precision(env, arg_idx_from_argno(size_argno)); - } + if (!err) + err = mark_arg_precision(env, size_argno); return err; @@ -7137,7 +7178,7 @@ static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg int size, err = 0; if (bpf_register_is_null(reg)) - return 0; + return mark_arg_precision(env, argno); if (known_memory) *known_memory = true; @@ -7398,10 +7439,14 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state lock); return -EINVAL; } + /* + * Invalidate non-owning refs before RCU demotion clears their + * NON_OWN_REF flag. + */ + invalidate_non_owning_refs(env); + if (!in_rcu_cs(env)) invalidate_rcu_protected_refs(env); - - invalidate_non_owning_refs(env); } return 0; } @@ -8166,6 +8211,7 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = { [ARG_MEM_SIZE] = &scalar_types, [ARG_MEM_SIZE_OR_ZERO] = &scalar_types, [ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types, + [ARG_SCALAR] = &scalar_types, [ARG_CONST_MAP_PTR] = &const_map_ptr_types, [ARG_PTR_TO_CTX] = &context_types, [ARG_PTR_TO_SOCK_COMMON] = &sock_types, @@ -8717,11 +8763,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, return err; } - if (bpf_register_is_null(reg) && type_may_be_null(arg_type)) + if (bpf_register_is_null(reg) && type_may_be_null(arg_type)) { /* A NULL register has a SCALAR_VALUE type, so skip * type checking. */ + err = mark_chain_precision(env, regno); + if (err) + return err; goto skip_type_check; + } /* arg_btf_id and arg_size are in a union. */ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID || @@ -9501,7 +9551,7 @@ static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env) bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, stack, clear_mask, ({ if (reg->type & MEM_RCU) { bpf_diag_mod_begin(env, reg, NULL, BPF_DIAG_MOD_WRITE); - reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL); + reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL | NON_OWN_REF); reg->type |= PTR_UNTRUSTED; bpf_diag_mod_end(env); } @@ -9719,8 +9769,12 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, struct bpf_call_arg_meta meta; int err; - if (bpf_register_is_null(reg) && type_may_be_null(arg->arg_type)) + if (bpf_register_is_null(reg) && type_may_be_null(arg->arg_type)) { + err = mark_arg_precision(env, argno); + if (err) + return err; continue; + } memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */ err = check_reg_type(env, reg, argno, arg->arg_type, &arg->btf_id, &meta, @@ -9976,10 +10030,12 @@ int map_set_for_each_callback_args(struct bpf_verifier_env *env, callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY; __mark_reg_known_zero(&callee->regs[BPF_REG_2]); callee->regs[BPF_REG_2].map_ptr = caller->regs[BPF_REG_1].map_ptr; + callee->regs[BPF_REG_2].map_uid = caller->regs[BPF_REG_1].map_uid; callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE; __mark_reg_known_zero(&callee->regs[BPF_REG_3]); callee->regs[BPF_REG_3].map_ptr = caller->regs[BPF_REG_1].map_ptr; + callee->regs[BPF_REG_3].map_uid = caller->regs[BPF_REG_1].map_uid; /* pointer to stack or null */ callee->regs[BPF_REG_4] = caller->regs[BPF_REG_3]; @@ -10057,6 +10113,7 @@ static int set_timer_callback_state(struct bpf_verifier_env *env, int insn_idx) { struct bpf_map *map_ptr = caller->regs[BPF_REG_1].map_ptr; + u32 map_uid = caller->regs[BPF_REG_1].map_uid; /* bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn); * callback_fn(struct bpf_map *map, void *key, void *value); @@ -10064,14 +10121,17 @@ static int set_timer_callback_state(struct bpf_verifier_env *env, callee->regs[BPF_REG_1].type = CONST_PTR_TO_MAP; __mark_reg_known_zero(&callee->regs[BPF_REG_1]); callee->regs[BPF_REG_1].map_ptr = map_ptr; + callee->regs[BPF_REG_1].map_uid = map_uid; callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY; __mark_reg_known_zero(&callee->regs[BPF_REG_2]); callee->regs[BPF_REG_2].map_ptr = map_ptr; + callee->regs[BPF_REG_2].map_uid = map_uid; callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE; __mark_reg_known_zero(&callee->regs[BPF_REG_3]); callee->regs[BPF_REG_3].map_ptr = map_ptr; + callee->regs[BPF_REG_3].map_uid = map_uid; /* unused */ bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]); @@ -10171,6 +10231,7 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env, int insn_idx) { struct bpf_map *map_ptr = caller->regs[BPF_REG_3].map_ptr; + u32 map_uid = caller->regs[BPF_REG_3].map_uid; /* * callback_fn(struct bpf_map *map, void *key, void *value); @@ -10178,14 +10239,17 @@ static int set_task_work_schedule_callback_state(struct bpf_verifier_env *env, callee->regs[BPF_REG_1].type = CONST_PTR_TO_MAP; __mark_reg_known_zero(&callee->regs[BPF_REG_1]); callee->regs[BPF_REG_1].map_ptr = map_ptr; + callee->regs[BPF_REG_1].map_uid = map_uid; callee->regs[BPF_REG_2].type = PTR_TO_MAP_KEY; __mark_reg_known_zero(&callee->regs[BPF_REG_2]); callee->regs[BPF_REG_2].map_ptr = map_ptr; + callee->regs[BPF_REG_2].map_uid = map_uid; callee->regs[BPF_REG_3].type = PTR_TO_MAP_VALUE; __mark_reg_known_zero(&callee->regs[BPF_REG_3]); callee->regs[BPF_REG_3].map_ptr = map_ptr; + callee->regs[BPF_REG_3].map_uid = map_uid; /* unused */ bpf_mark_reg_not_init(env, &callee->regs[BPF_REG_4]); @@ -10233,9 +10297,10 @@ static void account_current_path(struct bpf_verifier_env *env) frame ? state->frame[frame - 1] : NULL); } -/* Are we currently verifying the callback for a rbtree helper that must - * be called with lock held? If so, no need to complain about unreleased - * lock +/* + * Are we currently verifying the callback for an rbtree kfunc that must + * be called with a lock held, or one of that callback's subprogs? If so, + * no need to complain about an unreleased lock. */ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env) { @@ -10243,17 +10308,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env) struct bpf_insn *insn = env->prog->insnsi; struct bpf_func_state *callee; int kfunc_btf_id; + u32 frame; - if (!state->curframe) - return false; - - callee = state->frame[state->curframe]; + for (frame = state->curframe; frame; frame--) { + callee = state->frame[frame]; + if (!callee->in_callback_fn) + continue; - if (!callee->in_callback_fn) - return false; + kfunc_btf_id = insn[callee->callsite].imm; + if (is_rbtree_lock_required_kfunc(kfunc_btf_id)) + return true; + } - kfunc_btf_id = insn[callee->callsite].imm; - return is_rbtree_lock_required_kfunc(kfunc_btf_id); + return false; } static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg) @@ -10630,33 +10697,45 @@ static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env) return &env->insn_aux_data[env->insn_idx]; } -static bool loop_flag_is_zero(struct bpf_verifier_env *env) +/* Returns 1 if R4 is a known zero, 0 if it is not, a negative errno on error. */ +static int loop_flag_is_zero(struct bpf_verifier_env *env) { struct bpf_reg_state *reg = reg_state(env, BPF_REG_4); - bool reg_is_null = bpf_register_is_null(reg); + int err; - if (reg_is_null) - mark_chain_precision(env, BPF_REG_4); + if (!bpf_register_is_null(reg)) + return 0; - return reg_is_null; + err = mark_chain_precision(env, BPF_REG_4); + if (err) + return err; + return 1; } -static void update_loop_inline_state(struct bpf_verifier_env *env, u32 subprogno) +static int update_loop_inline_state(struct bpf_verifier_env *env, u32 subprogno) { struct bpf_loop_inline_state *state = &cur_aux(env)->loop_inline_state; + int flag_is_zero; if (!state->initialized) { + flag_is_zero = loop_flag_is_zero(env); + if (flag_is_zero < 0) + return flag_is_zero; state->initialized = 1; - state->fit_for_inline = loop_flag_is_zero(env); + state->fit_for_inline = flag_is_zero; state->callback_subprogno = subprogno; - return; + return 0; } if (!state->fit_for_inline) - return; + return 0; - state->fit_for_inline = (loop_flag_is_zero(env) && + flag_is_zero = loop_flag_is_zero(env); + if (flag_is_zero < 0) + return flag_is_zero; + state->fit_for_inline = (flag_is_zero && state->callback_subprogno == subprogno); + return 0; } /* Returns whether or not the given map can potentially elide @@ -10868,6 +10947,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn verbose(env, "get_local_storage() doesn't support non-zero flags\n"); return -EINVAL; } + err = mark_chain_precision(env, BPF_REG_2); + if (err) + return err; break; case BPF_FUNC_for_each_map_elem: err = push_callback_call(env, insn, insn_idx, meta.subprogno, @@ -10885,7 +10967,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn err = check_bpf_snprintf_call(env, regs); break; case BPF_FUNC_loop: - update_loop_inline_state(env, meta.subprogno); + err = update_loop_inline_state(env, meta.subprogno); + if (err) + return err; /* Verifier relies on R1 value to determine if bpf_loop() iteration * is finished, thus mark it precise. */ @@ -11226,6 +11310,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn if (env->cur_state->curframe) { struct bpf_verifier_state *branch; + /* + * A taken tail call is modeled as a return from the current + * frame. A callback frame cannot be left that way because + * prepare_func_exit() would apply its return contract to the + * unknown R0 synthesized below. Stack-depth validation rejects + * this construct anyway. + */ + if (cur_func(env)->in_callback_fn) { + verbose(env, "cannot tail call within callback\n"); + return -EINVAL; + } mark_reg_scratched(env, BPF_REG_0); branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false); if (IS_ERR(branch)) @@ -12651,8 +12746,12 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me if (reg_is_referenced(env, reg)) update_ref_obj(&meta->ref_obj, reg); - if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type)) + if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type)) { + ret = mark_arg_precision(env, argno); + if (ret) + return ret; continue; + } if (is_kfunc_arg_map(btf, &args[i])) { ref_id = *reg2btf_ids[CONST_PTR_TO_MAP]; @@ -13146,7 +13245,7 @@ check_ok: bpf_diag_reg_type_plain(env, reg->type)); return -EINVAL; } - if (!type_is_non_owning_ref(reg->type)) + if (!type_is_non_owning_ref(reg->type) && reg_is_referenced(env, reg)) meta->arg_owning_ref = true; rec = reg_btf_record(reg); @@ -13228,6 +13327,11 @@ check_ok: { int flags = PROCESS_RES_LOCK; + if (in_rbtree_lock_required_cb(env)) { + verbose(env, "can't res_spin_{lock,unlock} in rbtree cb\n"); + return -EACCES; + } + if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { verbose(env, "%s doesn't point to map value or allocated object\n", reg_arg_name(env, argno)); @@ -14560,9 +14664,6 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn return -EINVAL; } - /* pointer types do not carry 32-bit bounds at the moment. */ - __mark_reg32_unbounded(dst_reg); - if (sanitize_needed(opcode)) { ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg, &info, false); @@ -14570,6 +14671,14 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn return sanitize_err(env, insn, ret); } + /* + * Pointer types do not carry 32-bit bounds at the moment. Blank r32 + * only after sanitize_ptr_alu() may have snapshotted dst_reg into a + * speculative path: otherwise reg_bounds_sanity_check() might hit some + * constraints violations. + */ + __mark_reg32_unbounded(dst_reg); + switch (opcode) { case BPF_ADD: /* @@ -16295,6 +16404,13 @@ static int is_branch_taken(struct bpf_verifier_env *env, struct bpf_reg_state *r if (__is_pointer_value(false, reg1) || __is_pointer_value(false, reg2)) { u64 val; + /* + * The low 32 bits of a valid pointer may well be zero, hence + * nothing below applies to a 32-bit comparison. + */ + if (is_jmp32) + return -1; + /* arrange that reg2 is a scalar, and reg1 is a pointer */ if (!is_reg_const(reg2, is_jmp32)) { opcode = flip_opcode(opcode); @@ -16856,6 +16972,16 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, return err; } + /* + * Collect the linked registers before env->{true,false}_reg{1,2} setup, + * otherwise ids dropped by collect_linked_regs() would be resurrected + * when env->{true,false}_reg{1,2} are copied back. + */ + if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id) + collect_linked_regs(env, this_branch, src_reg->id, &linked_regs); + if (dst_reg->type == SCALAR_VALUE && dst_reg->id) + collect_linked_regs(env, this_branch, dst_reg->id, &linked_regs); + is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; env->false_reg1 = *dst_reg; env->false_reg2 = *src_reg; @@ -16910,10 +17036,6 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, * 'this_branch' and 'other_branch' share this history * if parent state is created. */ - if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id) - collect_linked_regs(env, this_branch, src_reg->id, &linked_regs); - if (dst_reg->type == SCALAR_VALUE && dst_reg->id) - collect_linked_regs(env, this_branch, dst_reg->id, &linked_regs); if (linked_regs.cnt > 1) { err = bpf_push_jmp_history(env, this_branch, 0, 0, 0, linked_regs_pack(&linked_regs)); if (err) @@ -16963,7 +17085,6 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, */ if (!is_jmp32 && BPF_SRC(insn->code) == BPF_X && __is_pointer_value(false, src_reg) && __is_pointer_value(false, dst_reg) && - type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type) && base_type(src_reg->type) != PTR_TO_BTF_ID && base_type(dst_reg->type) != PTR_TO_BTF_ID) { eq_branch_regs = NULL; @@ -16979,9 +17100,11 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, break; } if (eq_branch_regs) { - if (type_may_be_null(src_reg->type)) + /* src == dst && dst != NULL => src != NULL */ + if (reg_not_null(env, dst_reg) && type_may_be_null(src_reg->type)) mark_ptr_not_null_reg(&eq_branch_regs[insn->src_reg]); - else + /* src == dst && src != NULL => dst != NULL */ + if (reg_not_null(env, src_reg) && type_may_be_null(dst_reg->type)) mark_ptr_not_null_reg(&eq_branch_regs[insn->dst_reg]); } } @@ -16996,6 +17119,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, type_may_be_null(dst_reg->type) && ((BPF_SRC(insn->code) == BPF_K && insn->imm == 0) || (BPF_SRC(insn->code) == BPF_X && bpf_register_is_null(src_reg)))) { + /* + * For BPF_X the zero is a property of this execution path, + * hence src_reg has to be precise. + */ + if (BPF_SRC(insn->code) == BPF_X) { + err = mark_chain_precision(env, insn->src_reg); + if (err) + return err; + } /* Mark all identical registers in each branch as either * safe or unknown depending R == 0 or R != 0 conditional. */ @@ -17081,6 +17213,15 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) verbose(env, "callback function not static\n"); return -EINVAL; } + /* + * When env->subprog_cnt == 1 this instruction won't be rewritten + * to hold a real function address. Assume that no usable program + * combines e.g. main and timer callback and just reject here. + */ + if (subprogno == 0) { + verbose(env, "callback function cannot be the main program\n"); + return -EINVAL; + } dst_reg->type = PTR_TO_FUNC; dst_reg->subprogno = subprogno; @@ -17146,6 +17287,7 @@ static bool may_access_skb(enum bpf_prog_type type) */ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) { + struct bpf_verifier_state *state = env->cur_state; struct bpf_reg_state *regs = cur_regs(env); static const int ctx_reg = BPF_REG_6; u8 mode = BPF_MODE(insn->code); @@ -17156,6 +17298,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) return -EINVAL; } + for (i = state->curframe; i; i--) { + if (state->frame[i]->in_callback_fn) { + verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n"); + return -EINVAL; + } + } + if (!env->ops->gen_ld_abs) { verifier_bug(env, "gen_ld_abs is null"); return -EFAULT; @@ -17623,6 +17772,10 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call, * r0 = *(u64 *)(r10 - 8); r0 += r1; * r0 += r1; exit; * exit; + * + * Both uses of the marks assume that a pattern is entered at its first + * spill and thus executes as a unit, hence a pattern is not grown past + * an instruction targeted by a jump. */ static void mark_fastcall_pattern_for_call(struct bpf_verifier_env *env, struct bpf_subprog_info *subprog, @@ -17661,6 +17814,10 @@ static void mark_fastcall_pattern_for_call(struct bpf_verifier_env *env, for (i = 1, off = lowest_off; i <= ARRAY_SIZE(caller_saved); ++i, off += BPF_REG_SIZE) { if (insn_idx - i < 0 || insn_idx + i >= env->prog->len) break; + /* stx/ldx/call must not be a jump targets, a jump to the first stx is fine */ + if (bpf_is_jump_target(env, insn_idx - i + 1) || + bpf_is_jump_target(env, insn_idx + i)) + break; stx = &insns[insn_idx - i]; ldx = &insns[insn_idx + i]; /* must be a stack spill/fill pair */ @@ -20536,8 +20693,7 @@ static int process_fd_array_continuous(struct bpf_verifier_env *env, return -E2BIG; } - env->fd_array = kvcalloc(cnt, sizeof(*env->fd_array), - GFP_KERNEL_ACCOUNT); + env->fd_array = kvzalloc_objs(*env->fd_array, cnt, GFP_KERNEL_ACCOUNT); if (!env->fd_array) return -ENOMEM; env->fd_array_cnt = cnt; diff --git a/kernel/capability.c b/kernel/capability.c index 829f49ae07b9..90e6ab62f6db 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap) { return has_ns_capability_noaudit(t, &init_user_ns, cap); } -EXPORT_SYMBOL(has_capability_noaudit); static bool ns_capable_common(struct user_namespace *ns, int cap, @@ -416,6 +415,24 @@ bool capable(int cap) return ns_capable(&init_user_ns, cap); } EXPORT_SYMBOL(capable); + +/** + * capable_noaudit - Determine if the current task has a superior + * capability in effect by checking the process's effective + * capabilities (unaudited). + * @cap: The capability to be tested for + * + * This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent + * issuing spurious audit messages. + * + * This sets PF_SUPERPRIV on the task if the capability is available on the + * assumption that it's about to be used. + */ +bool capable_noaudit(int cap) +{ + return ns_capable_noaudit(&init_user_ns, cap); +} +EXPORT_SYMBOL(capable_noaudit); #endif /* CONFIG_MULTIUSER */ /** diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index c3a12fee7528..227d09704ca5 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -5303,10 +5303,13 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it) if (it->flags & CSS_TASK_ITER_SKIPPED) css_task_iter_advance(it); - if (it->task_pos) { + while (it->task_pos && !it->cur_task) { it->cur_task = list_entry(it->task_pos, struct task_struct, cg_list); - get_task_struct(it->cur_task); + /* a task on dying_tasks with zero refcount is only valid for + * RCU readers, not even interesting for + * CSS_TASK_ITER_WITH_DEAD, find another one */ + it->cur_task = tryget_task_struct(it->cur_task); css_task_iter_advance(it); } @@ -6873,10 +6876,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs) spin_lock_irq(&css_set_lock); cset = task_css_set(current); get_css_set(cset); - if (kargs->cgrp) - kargs->kill_seq = kargs->cgrp->kill_seq; - else - kargs->kill_seq = cset->dfl_cgrp->kill_seq; + kargs->kill_seq = cset->dfl_cgrp->kill_seq; spin_unlock_irq(&css_set_lock); if (!(kargs->flags & CLONE_INTO_CGROUP)) { @@ -6940,6 +6940,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs) put_css_set(cset); kargs->cgrp = dst_cgrp; + kargs->kill_seq = dst_cgrp->kill_seq; return ret; err: diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index d100634fa12b..2538faac9aba 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1259,6 +1259,28 @@ static void reset_partition_data(struct cpuset *cs) cpumask_copy(cs->effective_cpus, parent->effective_cpus); } +/* Return true if isolated_cpus changes. */ +static bool isolated_cpu_update(int new_prs, int cpu) +{ + lockdep_assert_held(&callback_lock); + lockdep_assert_held(&cpuset_mutex); + + if (new_prs == PRS_ISOLATED) { + if (cpumask_test_cpu(cpu, isolated_cpus)) + return false; + cpumask_set_cpu(cpu, isolated_cpus); + return true; + } + + /* CPUs isolated at boot must remain isolated. */ + if (!cpumask_test_cpu(cpu, + housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)) || + !cpumask_test_cpu(cpu, isolated_cpus)) + return false; + cpumask_clear_cpu(cpu, isolated_cpus); + return true; +} + /* * isolated_cpus_update - Update the isolated_cpus mask * @old_prs: old partition_root_state @@ -1267,19 +1289,16 @@ static void reset_partition_data(struct cpuset *cs) */ static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus) { + bool updated = false; + int cpu; + WARN_ON_ONCE(old_prs == new_prs); lockdep_assert_held(&callback_lock); lockdep_assert_held(&cpuset_mutex); - if (new_prs == PRS_ISOLATED) { - if (cpumask_subset(xcpus, isolated_cpus)) - return; - cpumask_or(isolated_cpus, isolated_cpus, xcpus); - } else { - if (!cpumask_intersects(xcpus, isolated_cpus)) - return; - cpumask_andnot(isolated_cpus, isolated_cpus, xcpus); - } - update_housekeeping = true; + for_each_cpu(cpu, xcpus) + updated |= isolated_cpu_update(new_prs, cpu); + if (updated) + update_housekeeping = true; } /* diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c index 45bbae947f4b..4d0266893bcc 100644 --- a/kernel/dma/coherent.c +++ b/kernel/dma/coherent.c @@ -352,8 +352,7 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit)) dev_warn(dev, "reserved memory is beyond device's set DMA address range\n"); - dma_assign_coherent_memory(dev, mem); - return 0; + return dma_assign_coherent_memory(dev, mem); } static void rmem_dma_device_release(struct reserved_mem *rmem, diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c index fdc070f419f6..957707158ff6 100644 --- a/kernel/dma/map_benchmark.c +++ b/kernel/dma/map_benchmark.c @@ -51,8 +51,7 @@ struct dma_single_map_param { static void *dma_single_map_benchmark_prepare(struct map_benchmark_data *map) { - struct dma_single_map_param *params __free(kfree) = kzalloc(sizeof(*params), - GFP_KERNEL); + struct dma_single_map_param *params __free(kfree) = kzalloc_obj(*params); if (!params) return NULL; diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index ded7016a46a7..aa2f1c4588b9 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -1019,7 +1019,6 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT; phys_addr_t orig_addr = mem->slots[index].orig_addr; size_t alloc_size = mem->slots[index].alloc_size; - unsigned long pfn = PFN_DOWN(orig_addr); unsigned char *vaddr = mem->vaddr + tlb_addr - mem->start; int tlb_offset; @@ -1052,7 +1051,8 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size size = alloc_size; } - if (PageHighMem(pfn_to_page(pfn))) { + if (PhysHighMem(orig_addr)) { + unsigned long pfn = PFN_DOWN(orig_addr); unsigned int offset = orig_addr & ~PAGE_MASK; struct page *page; unsigned int sz = 0; diff --git a/kernel/entry/syscall_user_dispatch.c b/kernel/entry/syscall_user_dispatch.c index 2002c7aae435..59c861866941 100644 --- a/kernel/entry/syscall_user_dispatch.c +++ b/kernel/entry/syscall_user_dispatch.c @@ -178,7 +178,7 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz (char __user *)(uintptr_t)cfg.selector); } -#ifdef CONFIG_PROC_SYSCTL +#ifdef CONFIG_SYSCTL static const struct ctl_table syscall_user_dispatch_sysctls[] = { { .procname = "syscall_user_dispatch", @@ -195,4 +195,4 @@ static int __init syscall_user_dispatch_sysctl_init(void) return 0; } late_initcall(syscall_user_dispatch_sysctl_init); -#endif /* CONFIG_PROC_SYSCTL */ +#endif /* CONFIG_SYSCTL */ diff --git a/kernel/events/core.c b/kernel/events/core.c index a6c8e38a3110..fe33fe15689d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3925,13 +3925,13 @@ static void perf_pmu_sched_task(struct task_struct *prev, bool sched_in) { struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); - struct perf_cpu_pmu_context *cpc; + struct perf_cpu_pmu_context *cpc, *cpc2; /* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */ if (prev == next || cpuctx->task_ctx) return; - list_for_each_entry(cpc, this_cpu_ptr(&sched_cb_list), sched_cb_entry) + list_for_each_entry_safe(cpc, cpc2, this_cpu_ptr(&sched_cb_list), sched_cb_entry) __perf_pmu_sched_task(cpc, sched_in ? next : prev, sched_in); } @@ -7029,7 +7029,6 @@ static void perf_mmap_close(struct vm_area_struct *vma) mapped_f unmapped = get_mapped(event, event_unmapped); struct perf_buffer *rb = ring_buffer_get(event); struct user_struct *mmap_user = rb->mmap_user; - bool detach_rest = false; /* FIXIES vs perf_pmu_unregister() */ if (unmapped) @@ -7060,17 +7059,18 @@ static void perf_mmap_close(struct vm_area_struct *vma) mutex_unlock(&rb->aux_mutex); } - if (refcount_dec_and_test(&rb->mmap_count)) - detach_rest = true; - - if (!refcount_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) - goto out_put; - - ring_buffer_attach(event, NULL); - mutex_unlock(&event->mmap_mutex); + /* + * Drop references in reverse order of perf_mmap() to prevent + * rb revival after rb->mmap_count reaches zero. + */ + if (refcount_dec_and_mutex_lock(&event->mmap_count, + &event->mmap_mutex)) { + ring_buffer_attach(event, NULL); + mutex_unlock(&event->mmap_mutex); + } /* If there's still other mmap()s of this buffer, we're done. */ - if (!detach_rest) + if (!refcount_dec_and_test(&rb->mmap_count)) goto out_put; /* @@ -13558,9 +13558,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, return ERR_PTR(err); if (has_addr_filter(event)) { - event->addr_filter_ranges = kcalloc(pmu->nr_addr_filters, - sizeof(struct perf_addr_filter_range), - GFP_KERNEL); + event->addr_filter_ranges = kzalloc_objs(struct perf_addr_filter_range, + pmu->nr_addr_filters); if (!event->addr_filter_ranges) return ERR_PTR(-ENOMEM); diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 9fe92161715e..1b1ffe0533e5 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -509,7 +509,10 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size) /* * Only send RECORD_AUX if we have something useful to communicate * - * Note: the OVERWRITE records by themselves are not considered + * PMU_FORMAT bits identify the PMU type rather than an AUX event + * has occurred, so ignore them for zero-sized records. + * + * The OVERWRITE records by themselves are not considered * useful, as they don't communicate any *new* information, * aside from the short-lived offset, that becomes history at * the next event sched-in and therefore isn't useful. @@ -518,7 +521,9 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size) * offset. So, from now on we don't output AUX records that * have *only* OVERWRITE flag set. */ - if (size || (handle->aux_flags & ~(u64)PERF_AUX_FLAG_OVERWRITE)) + if (size || + (handle->aux_flags & ~(u64)(PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK | + PERF_AUX_FLAG_OVERWRITE))) perf_event_aux_event(handle->event, aux_head, size, handle->aux_flags); diff --git a/kernel/exit.c b/kernel/exit.c index 97686af89501..4e028f157597 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -261,8 +261,11 @@ repeat: pidfs_exit(p); cgroup_task_release(p); - /* Retrieve @thread_pid before __unhash_process() may set it to NULL. */ - thread_pid = task_pid(p); + /* + * Pin @thread_pid before __unhash_process() clears it. The last + * PIDTYPE detach can otherwise free it before proc_flush_pid(). + */ + thread_pid = get_pid(task_pid(p)); write_lock_irq(&tasklist_lock); ptrace_release_task(p); @@ -291,8 +294,8 @@ repeat: } write_unlock_irq(&tasklist_lock); - /* @thread_pid can't go away until free_pids() below */ proc_flush_pid(thread_pid); + put_pid(thread_pid); exit_cred_namespaces(p); add_device_randomness(&p->se.sum_exec_runtime, sizeof(p->se.sum_exec_runtime)); diff --git a/kernel/fork.c b/kernel/fork.c index 416758c8a3d4..a5934a317634 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2133,6 +2133,11 @@ __latent_entropy struct task_struct *copy_process( p = dup_task_struct(current, node); if (!p) goto fork_out; + /* + * Must run before the first fallible op, so error paths never + * free the parent's ret_stack. + */ + ftrace_graph_init_task(p); retval = copy_exec_state(clone_flags, p); if (retval) goto bad_fork_free; @@ -2159,8 +2164,6 @@ __latent_entropy struct task_struct *copy_process( */ p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? args->child_tid : NULL; - ftrace_graph_init_task(p); - rt_mutex_init_task(p); raw_spin_lock_init(&p->blocked_lock); diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 51ba5e1257c0..a061f54b606d 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -1874,8 +1874,8 @@ static int futex_hash_allocate(unsigned int hash_slots, unsigned int flags) free_percpu(ref); } - fph = kvzalloc(struct_size(fph, queues, hash_slots), - GFP_KERNEL_ACCOUNT | __GFP_NOWARN); + fph = kvzalloc_flex(*fph, queues, hash_slots, + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (!fph) return -ENOMEM; @@ -2103,7 +2103,7 @@ static int __init futex_init(void) size = sizeof(struct futex_hash_bucket) * hashsize; order = get_order(size); - __futex_queues = kcalloc(nr_node_ids, sizeof(*__futex_queues), GFP_KERNEL); + __futex_queues = kzalloc_objs(*__futex_queues, nr_node_ids); kmemleak_not_leak(__futex_queues); runtime_const_init(shift, __futex_shift); diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c index 88788e584ec8..98f1b962e59a 100644 --- a/kernel/futex/pi.c +++ b/kernel/futex/pi.c @@ -1070,17 +1070,11 @@ retry_private: * Caution; releasing @hb in-scope. The hb->lock is still locked * while the reference is dropped. The reference can not be dropped * after the unlock because if a user initiated resize is in progress - * then we might need to wake him. This can not be done after the - * rt_mutex_pre_schedule() invocation. The hb will remain valid because - * the thread, performing resize, will block on hb->lock during - * the requeue. + * then we might need to wake him. The hb will remain valid + * because the thread, performing resize, will block on + * hb->lock during the requeue. */ futex_private_hash_put(no_free_ptr(hbr.fph)); - /* - * Must be done before we enqueue the waiter, here is unfortunately - * under the hb lock, but that *should* work because it does nothing. - */ - rt_mutex_pre_schedule(); rt_mutex_init_waiter(&rt_waiter); @@ -1146,10 +1140,6 @@ cleanup: * the */ futex_q_lockptr_lock(&q); - /* - * Waiter is unqueued. - */ - rt_mutex_post_schedule(); no_block: /* * Fixup the pi_state owner and possibly acquire the lock if we diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index 79823ad13683..b3f4a4bccb12 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -154,8 +154,16 @@ static inline void futex_requeue_pi_complete(struct futex_q *q, int locked) } while (!atomic_try_cmpxchg(&q->requeue_state, &old, new)); #ifdef CONFIG_PREEMPT_RT - /* If the waiter interleaved with the requeue let it know */ - if (unlikely(old == Q_REQUEUE_PI_WAIT)) + /* + * The waiter in futex_requeue_pi_wakeup_sync() can interleave with the + * wake below: It will assign Q_REQUEUE_PI_IN_PROGRESS and here it will + * be updated to Q_REQUEUE_PI_LOCKED (locked = 1). The rcuwait_wait_event() + * will already read Q_REQUEUE_PI_LOCKED and skip the schedule() invocation, + * leading to an access of futex_q::requeue_wait after the waiter returned. + * In this case only we skip the wake here and rely on following wake in + * requeue_pi_wake_futex() to perform the wake if needed. + */ + if (unlikely(old == Q_REQUEUE_PI_WAIT) && new != Q_REQUEUE_PI_LOCKED) rcuwait_wake_up(&q->requeue_wait); #endif } diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 2fbff2618a1e..57eff26fa646 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -2306,7 +2306,7 @@ int request_nmi(unsigned int irq, irq_handler_t handler, !irq_supports_nmi(desc)) return -EINVAL; - action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); + action = kzalloc_obj(struct irqaction); if (!action) return -ENOMEM; diff --git a/kernel/jump_label.c b/kernel/jump_label.c index e851e4b37d0e..ab6b774bcfd7 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -726,12 +726,11 @@ static int jump_label_add_module(struct module *mod) if (static_key_sealed(key)) goto do_poke; - jlm = kzalloc(sizeof(struct static_key_mod), GFP_KERNEL); + jlm = kzalloc_obj(struct static_key_mod); if (!jlm) return -ENOMEM; if (!static_key_linked(key)) { - jlm2 = kzalloc(sizeof(struct static_key_mod), - GFP_KERNEL); + jlm2 = kzalloc_obj(struct static_key_mod); if (!jlm2) { kfree(jlm); return -ENOMEM; diff --git a/kernel/kprobes.c b/kernel/kprobes.c index bfc89083daa9..6337da5cab9e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1447,8 +1447,14 @@ static bool __within_kprobe_blacklist(unsigned long addr) /* * If 'kprobe_blacklist' is defined, check the address and * reject any probe registration in the prohibited area. + * Note: this can return true during transition period where + * (start_addr, end_addr) in the black list is shrinking + * but old entry has not been removed yet. This is acceptable + * because the worst case is that we reject more probes than + * we should. */ - list_for_each_entry(ent, &kprobe_blacklist, list) { + guard(rcu)(); + list_for_each_entry_rcu(ent, &kprobe_blacklist, list) { if (addr >= ent->start_addr && addr < ent->end_addr) return true; } @@ -2509,7 +2515,7 @@ int kprobe_add_ksym_blacklist(unsigned long entry) ent->start_addr = entry; ent->end_addr = entry + size; INIT_LIST_HEAD(&ent->list); - list_add_tail(&ent->list, &kprobe_blacklist); + list_add_tail_rcu(&ent->list, &kprobe_blacklist); return (int)size; } @@ -2603,8 +2609,8 @@ static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end) list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) { if (ent->start_addr < start || ent->start_addr >= end) continue; - list_del(&ent->list); - kfree(ent); + list_del_rcu(&ent->list); + kfree_rcu(ent, rcu); } } diff --git a/kernel/kthread.c b/kernel/kthread.c index 63beb59b7a3d..a3f95c90456b 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -852,7 +852,7 @@ int kthread_affine_preferred(struct task_struct *p, const struct cpumask *mask) if (!zalloc_cpumask_var(&affinity, GFP_KERNEL)) return -ENOMEM; - kthread->preferred_affinity = kzalloc(sizeof(struct cpumask), GFP_KERNEL); + kthread->preferred_affinity = kzalloc_obj(struct cpumask); if (!kthread->preferred_affinity) { ret = -ENOMEM; goto out; diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index c56a7f91d72e..c3dc84a7cef2 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -963,6 +963,34 @@ look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass) return NULL; } +static __always_inline bool lock_class_cache_is_valid(const struct lockdep_map *lock, + const struct lock_class *class, + unsigned int subclass) +{ + unsigned int class_subclass; + + if (!class) + return false; + + if (unlikely(class < lock_classes || class >= lock_classes + MAX_LOCKDEP_KEYS)) + return false; + + if (unlikely(!arch_test_bit(class - lock_classes, lock_classes_in_use))) + return false; + + if (unlikely(!lock->key)) + return false; + + class_subclass = subclass ? subclass : class->subclass; + if (unlikely(class_subclass >= MAX_LOCKDEP_SUBCLASSES)) + return false; + + if (unlikely(READ_ONCE(class->key) != lock->key->subkeys + class_subclass)) + return false; + + return true; +} + /* * Static locks do not have their class-keys yet - for them the key is * the lock object itself. If the lock is in the per cpu area, the @@ -1395,9 +1423,9 @@ out_unlock_set: out_set_class_cache: if (!subclass || force) - lock->class_cache[0] = class; + WRITE_ONCE(lock->class_cache[0], class); else if (subclass < NR_LOCKDEP_CACHING_CLASSES) - lock->class_cache[subclass] = class; + WRITE_ONCE(lock->class_cache[subclass], class); /* * Hash collision, did we smoke some? We found a class with a matching @@ -4957,7 +4985,7 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name, int i; for (i = 0; i < NR_LOCKDEP_CACHING_CLASSES; i++) - lock->class_cache[i] = NULL; + WRITE_ONCE(lock->class_cache[i], NULL); #ifdef CONFIG_LOCK_STAT lock->cpu = raw_smp_processor_id(); @@ -5022,12 +5050,15 @@ EXPORT_SYMBOL_GPL(__lockdep_no_track__); void lockdep_set_lock_cmp_fn(struct lockdep_map *lock, lock_cmp_fn cmp_fn, lock_print_fn print_fn) { - struct lock_class *class = lock->class_cache[0]; + struct lock_class *class = READ_ONCE(lock->class_cache[0]); unsigned long flags; raw_local_irq_save(flags); lockdep_recursion_inc(); + if (!lock_class_cache_is_valid(lock, class, 0)) + class = NULL; + if (!class) class = register_lock_class(lock, 0, 0); @@ -5119,8 +5150,11 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, if (DEBUG_LOCKS_WARN_ON(subclass >= MAX_LOCKDEP_SUBCLASSES)) return 0; - if (subclass < NR_LOCKDEP_CACHING_CLASSES) - class = lock->class_cache[subclass]; + if (subclass < NR_LOCKDEP_CACHING_CLASSES) { + class = READ_ONCE(lock->class_cache[subclass]); + if (!lock_class_cache_is_valid(lock, class, subclass)) + class = NULL; + } /* * Not cached? */ @@ -5324,9 +5358,9 @@ static noinstr int match_held_lock(const struct held_lock *hlock, return 1; if (hlock->references) { - const struct lock_class *class = lock->class_cache[0]; + const struct lock_class *class = READ_ONCE(lock->class_cache[0]); - if (!class) + if (!lock_class_cache_is_valid(lock, class, 0)) class = look_up_lock_class(lock, 0); /* diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 5d48d64725b1..eb18b094473c 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -423,6 +423,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, { int ret; + rt_mutex_futex_pre_schedule(); raw_spin_lock_irq(&lock->wait_lock); /* sleep on the mutex */ set_current_state(TASK_INTERRUPTIBLE); @@ -433,6 +434,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, */ fixup_rt_mutex_waiters(lock, true); raw_spin_unlock_irq(&lock->wait_lock); + rt_mutex_futex_post_schedule(); return ret; } diff --git a/kernel/nstree.c b/kernel/nstree.c index 6d12e5900ac0..831f279d174a 100644 --- a/kernel/nstree.c +++ b/kernel/nstree.c @@ -533,19 +533,13 @@ DEFINE_FREE(ns_put, struct ns_common *, if (!IS_ERR_OR_NULL(_T)) ns_put(_T)) static inline struct ns_common *__must_check legitimize_ns(const struct klistns *kls, struct ns_common *candidate) { - struct ns_common *ns __free(ns_put) = NULL; - if (!ns_requested(kls, candidate)) return NULL; - ns = ns_get_unless_inactive(candidate); - if (!ns) - return NULL; - - if (!may_list_ns(kls, ns)) + if (!may_list_ns(kls, candidate)) return NULL; - return no_free_ptr(ns); + return ns_get_unless_inactive(candidate); } static ssize_t do_listns_userns(struct klistns *kls) diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c index 4b03b019cd5e..d17704fe93ae 100644 --- a/kernel/printk/nbcon.c +++ b/kernel/printk/nbcon.c @@ -1382,7 +1382,7 @@ bool nbcon_kthread_create(struct console *con) return true; kt = kthread_run(nbcon_kthread_func, con, "pr/%s%d", con->name, con->index); - if (WARN_ON(IS_ERR(kt))) { + if (IS_ERR(kt)) { con_printk(KERN_ERR, con, "failed to start printing thread\n"); return false; } @@ -1782,7 +1782,7 @@ bool nbcon_alloc(struct console *con) } rcuwait_init(&con->rcuwait); - init_irq_work(&con->irq_work, nbcon_irq_work); + con->irq_work = IRQ_WORK_INIT_LAZY(nbcon_irq_work); atomic_long_set(&ACCESS_PRIVATE(con, nbcon_prev_seq), -1UL); nbcon_state_set(con, &state); @@ -1837,6 +1837,8 @@ void nbcon_free(struct console *con) /* Synchronize the kthread stop. */ lockdep_assert_console_list_lock_held(); + irq_work_sync(&con->irq_work); + if (printk_kthreads_running) { nbcon_kthread_stop(con); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 3fcdf4b4e2e5..6d3d18a50da7 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -3732,7 +3732,7 @@ static bool legacy_kthread_create(void) lockdep_assert_console_list_lock_held(); kt = kthread_run(legacy_kthread_func, NULL, "pr/legacy"); - if (WARN_ON(IS_ERR(kt))) { + if (IS_ERR(kt)) { pr_err("failed to start legacy printing thread\n"); return false; } diff --git a/kernel/reboot.c b/kernel/reboot.c index f070c5c1103a..d177d89fcc33 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -13,7 +13,9 @@ #include <linux/kexec.h> #include <linux/kmod.h> #include <linux/kmsg_dump.h> +#include <linux/rcupdate.h> #include <linux/reboot.h> +#include <linux/sched/signal.h> #include <linux/suspend.h> #include <linux/syscalls.h> #include <linux/syscore_ops.h> @@ -24,8 +26,7 @@ */ static int C_A_D = 1; -struct pid *cad_pid; -EXPORT_SYMBOL(cad_pid); +struct pid __rcu *cad_pid; #if defined(CONFIG_ARM) #define DEFAULT_REBOOT_MODE = REBOOT_HARD @@ -1371,10 +1372,14 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe { struct ctl_table tmp_table = *table; struct pid *new_pid; + struct pid *old_pid; pid_t tmp_pid; int r; - tmp_pid = pid_vnr(cad_pid); + rcu_read_lock(); + tmp_pid = pid_vnr(rcu_dereference(cad_pid)); + rcu_read_unlock(); + tmp_table.data = &tmp_pid; r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); @@ -1385,7 +1390,13 @@ static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffe if (!new_pid) return -ESRCH; - put_pid(xchg(&cad_pid, new_pid)); + old_pid = unrcu_pointer(xchg(&cad_pid, RCU_INITIALIZER(new_pid))); + /* + * Wait for cad_pid readers before put_pid(). We cannot use + * call_rcu() here because free_pid() already owns pid->rcu. + */ + synchronize_rcu(); + put_pid(old_pid); return 0; } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f78275192036..7885ff76e69f 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3742,11 +3742,17 @@ static inline void ttwu_do_wakeup(struct task_struct *p) void update_rq_avg_idle(struct rq *rq) { - u64 delta = rq_clock(rq) - rq->idle_stamp; - u64 max = 2*rq->max_idle_balance_cost; + u64 idle_stamp = rq->idle_stamp; + u64 delta, max; + + if (!idle_stamp) + return; + + delta = rq_clock(rq) - idle_stamp; update_avg(&rq->avg_idle, delta); + max = 2 * rq->max_idle_balance_cost; if (rq->avg_idle > max) rq->avg_idle = max; rq->idle_stamp = 0; @@ -5770,8 +5776,8 @@ void sched_tick(void) { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); - /* accounting goes to the donor task */ - struct task_struct *donor; + /* scheduler accounting goes to the donor task */ + struct task_struct *curr, *donor; struct rq_flags rf; unsigned long hw_pressure; u64 resched_latency; @@ -5782,6 +5788,7 @@ void sched_tick(void) sched_clock_tick(); rq_lock(rq, &rf); + curr = rq->curr; donor = rq->donor; psi_account_irqtime(rq, donor, NULL); @@ -5807,8 +5814,8 @@ void sched_tick(void) perf_event_task_tick(); - if (donor->flags & PF_WQ_WORKER) - wq_worker_tick(donor); + if (curr->flags & PF_WQ_WORKER) + wq_worker_tick(curr); if (!scx_switched_all()) { rq->idle_balance = idle_cpu(cpu); @@ -7637,6 +7644,17 @@ void rt_mutex_pre_schedule(void) sched_submit_work(current); } +/* + * Used within the futex syscall context, skips sched_submit_work() because none + * its work will be done. Asserts ensure that it is indeed the case. + */ +void rt_mutex_futex_pre_schedule(void) +{ + lockdep_assert(!(current->flags & (PF_WQ_WORKER | PF_IO_WORKER))); + lockdep_assert(!current->plug); + lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); +} + void rt_mutex_schedule(void) { lockdep_assert(current->sched_rt_mutex); @@ -7649,6 +7667,11 @@ void rt_mutex_post_schedule(void) lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); } +void rt_mutex_futex_post_schedule(void) +{ + lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); +} + /* * rt_mutex_setprio - set the current priority of a task * @p: task to boost diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 857dbe3519a8..0663c00c41c0 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -3028,8 +3028,8 @@ static struct task_struct *pick_next_pushable_dl_task(struct rq *rq) next_node = rb_first_cached(&rq->dl.pushable_dl_tasks_root); while (next_node) { i = __node_2_pdl(next_node); - /* make sure task isn't on_cpu (possible with proxy-exec) */ - if (!task_on_cpu(rq, i)) { + /* skip tasks that cannot be migrated */ + if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { p = i; break; } diff --git a/kernel/sched/ext/cid.c b/kernel/sched/ext/cid.c index 39f88deb94bc..bc4eee5bb4cb 100644 --- a/kernel/sched/ext/cid.c +++ b/kernel/sched/ext/cid.c @@ -98,16 +98,16 @@ static struct scx_cid_tables *scx_cid_alloc_tables(void) u32 npossible = num_possible_cpus(); struct scx_cid_tables *tbls; - tbls = kzalloc_obj(*tbls, GFP_KERNEL); + tbls = kzalloc_obj(*tbls); if (!tbls) return NULL; - tbls->cid_to_cpu = kvcalloc(npossible, sizeof(*tbls->cid_to_cpu), GFP_KERNEL); - tbls->cpu_to_cid = kvcalloc(nr_cpu_ids, sizeof(*tbls->cpu_to_cid), GFP_KERNEL); - tbls->cid_to_shard = kvcalloc(npossible, sizeof(*tbls->cid_to_shard), GFP_KERNEL); - tbls->shard_node = kvcalloc(npossible, sizeof(*tbls->shard_node), GFP_KERNEL); - tbls->shard_ranges = kvcalloc(npossible, sizeof(*tbls->shard_ranges), GFP_KERNEL); - tbls->topo = kvcalloc(npossible, sizeof(*tbls->topo), GFP_KERNEL); + tbls->cid_to_cpu = kvzalloc_objs(*tbls->cid_to_cpu, npossible); + tbls->cpu_to_cid = kvzalloc_objs(*tbls->cpu_to_cid, nr_cpu_ids); + tbls->cid_to_shard = kvzalloc_objs(*tbls->cid_to_shard, npossible); + tbls->shard_node = kvzalloc_objs(*tbls->shard_node, npossible); + tbls->shard_ranges = kvzalloc_objs(*tbls->shard_ranges, npossible); + tbls->topo = kvzalloc_objs(*tbls->topo, npossible); if (!tbls->cid_to_cpu || !tbls->cpu_to_cid || !tbls->cid_to_shard || !tbls->shard_node || !tbls->shard_ranges || !tbls->topo) { @@ -490,7 +490,7 @@ __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid__arena, u32 cpu_to_c * region that arena fault recovery covers. */ alloced = zalloc_cpumask_var(&seen, GFP_KERNEL); - node_counts = kcalloc(nr_node_ids, sizeof(*node_counts), GFP_KERNEL); + node_counts = kzalloc_objs(*node_counts, nr_node_ids); if (cpu_to_cid_cnt == nr_cpu_ids) cpu_to_cid = kmemdup(cpu_to_cid__arena, cpu_to_cid_cnt * sizeof(s32), GFP_KERNEL); diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 10af28a9f2c0..3219f0da0fe4 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -876,9 +876,9 @@ struct task_struct *scx_task_iter_next_locked(struct scx_task_iter *iter) * unloading. The init_tasks ("swappers") should be excluded * from the iteration because: * - * - It's unsafe to use __setschduler_prio() on an init_task to - * determine the sched_class to use as it won't preserve its - * idle_sched_class. + * - It's unsafe to use __setscheduler_class() on an init_task + * to determine the sched_class to use as it won't preserve + * its idle_sched_class. * * - ops.init/exit_task() can easily be confused if called with * init_tasks as they, e.g., share PID 0. @@ -2806,6 +2806,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, * @p: task to finish dispatching * @qseq_at_dispatch: qseq when @p started getting dispatched * @dsq_id: destination DSQ ID + * @slice: slice carried by the insert verdict, 0 keeps the current value + * @vtime: vtime carried by the insert verdict, committed on PRIQ inserts * @enq_flags: %SCX_ENQ_* * * Dispatching to local DSQs may need to wait for queueing to complete or @@ -2917,7 +2919,7 @@ static inline void maybe_queue_balance_callback(struct rq *rq) static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev) { - struct scx_sched *sch = scx_root_protected_live(); + struct scx_sched *root_sch = scx_root_protected_live(); enum scx_dsp_verdict verdict; s32 cpu = cpu_of(rq); @@ -2926,7 +2928,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev scx_process_sync_ecaps(rq, prev); - if ((sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) && + if ((root_sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) && unlikely(rq->scx.cpu_released)) { /* * If the previous sched_class for the current CPU was not SCX, @@ -2934,8 +2936,8 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev * core. This callback complements ->cpu_release(), which is * emitted in switch_class(). */ - if (sch->ops.cpu_acquire) - SCX_CALL_OP(sch, cpu_acquire, rq, cpu, NULL); + if (root_sch->ops.cpu_acquire) + SCX_CALL_OP(root_sch, cpu_acquire, rq, cpu, NULL); rq->scx.cpu_released = false; } @@ -2953,7 +2955,7 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev * test. */ if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice && - !scx_bypassing(sch, cpu)) { + !scx_bypassing(scx_task_sched(prev), cpu)) { verdict = SCX_DSP_PREV; goto has_tasks; } @@ -2965,20 +2967,25 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev goto has_tasks; } - verdict = scx_dispatch_sched(sch, rq, prev, false); + verdict = scx_dispatch_sched(root_sch, rq, prev, false); if (verdict != SCX_DSP_NONE) goto has_tasks; /* - * Didn't find another task to run. Keep running @prev unless - * %SCX_OPS_ENQ_LAST is in effect. + * Didn't find another task to run. Keep running @prev unless its own + * scheduler set %SCX_OPS_ENQ_LAST and takes the enqueue instead, see + * put_prev_task_scx(). Read the scheduler here as the dispatch above + * may have dropped the rq lock while @prev changed class or scheduler. */ - if ((prev->scx.flags & SCX_TASK_QUEUED) && - (!(sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(sch, cpu)) && - scx_task_can_stay_on_cpu(rq, prev)) { - __scx_add_event(sch, SCX_EV_DISPATCH_KEEP_LAST, 1); - verdict = SCX_DSP_PREV; - goto has_tasks; + if (prev->scx.flags & SCX_TASK_QUEUED) { + struct scx_sched *prev_sch = scx_task_sched(prev); + + if ((!(prev_sch->ops.flags & SCX_OPS_ENQ_LAST) || + scx_bypassing(prev_sch, cpu)) && scx_task_can_stay_on_cpu(rq, prev)) { + __scx_add_event(prev_sch, SCX_EV_DISPATCH_KEEP_LAST, 1); + verdict = SCX_DSP_PREV; + goto has_tasks; + } } rq->scx.flags &= ~SCX_RQ_IN_DISPATCH; return SCX_DSP_NONE; @@ -3663,8 +3670,20 @@ static void handle_hotplug(struct rq *rq, bool online) s16 *tbl = rcu_dereference_check(scx_cpu_to_cid_tbl, lockdep_is_cpus_held()); - if (tbl) + if (tbl) { + struct scx_sched *pos; + cpu_or_cid = tbl[cpu]; + + guard(raw_spinlock_irqsave)(&scx_sched_lock); + list_for_each_entry(pos, &scx_sched_all, all) { + struct scx_cmask *mask = pos->online_cmask; + + if (mask) + __assign_bit(cpu_or_cid, (unsigned long *)mask->bits, + online); + } + } } if (online && SCX_HAS_OP(sch, cpu_online)) @@ -4764,7 +4783,8 @@ int scx_tg_online(struct task_group *tg) { .weight = tg->scx.weight, .bw_period_us = tg->scx.bw_period_us, .bw_quota_us = tg->scx.bw_quota_us, - .bw_burst_us = tg->scx.bw_burst_us }; + .bw_burst_us = tg->scx.bw_burst_us, + .sched_idle = tg->scx.idle }; ret = SCX_CALL_OP_RET(sch, cgroup_init, NULL, tg->css.cgroup, &args); @@ -4930,7 +4950,8 @@ void scx_group_set_idle(struct task_group *tg, bool idle) percpu_down_read(&scx_cgroup_ops_rwsem); sch = scx_tg_knob_sched(tg); - if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle)) + if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) && + tg->scx.idle != idle) SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle); /* Update the task group's idle state */ @@ -5185,6 +5206,7 @@ static int scx_cgroup_init(struct scx_sched *sch) .bw_period_us = tg->scx.bw_period_us, .bw_quota_us = tg->scx.bw_quota_us, .bw_burst_us = tg->scx.bw_burst_us, + .sched_idle = tg->scx.idle, }; ret = SCX_CALL_OP_RET(sch, cgroup_init, NULL, css->cgroup, &args); @@ -5270,12 +5292,17 @@ static void free_exit_info(struct scx_exit_info *ei); static const char *scx_exit_reason(enum scx_exit_kind kind); static bool scx_claim_exit(struct scx_sched *sch, enum scx_exit_kind kind); -s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch) +s32 scx_alloc_kern_arena_objs(struct scx_sched *sch) { size_t size = struct_size_t(struct scx_cmask, bits, SCX_CMASK_NR_WORDS(num_possible_cpus())); + struct scx_cmask *online; + struct scx_cmask_ref ref; int cpu; + /* hotplug stays excluded until the online mask is published */ + lockdep_assert_cpus_held(); + if (!sch->is_cid_type || !sch->arena_pool) return 0; @@ -5291,15 +5318,28 @@ s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch) return -ENOMEM; scx_cmask_init(*slot, 0, num_possible_cpus()); } + + /* pack the online mask alongside the scratch masks */ + online = scx_arena_alloc(sch, size); + if (!online) + return -ENOMEM; + + scoped_guard(rcu) { + scx_cmask_ref_init_kern(sch, online, 0, num_possible_cpus(), &ref); + scx_cmask_ref_from_cpumask(&ref, cpu_active_mask); + } + sch->online_cmask = online; + return 0; } -static void scx_set_cmask_scratch_free(struct scx_sched *sch) +static void scx_free_kern_arena_objs(struct scx_sched *sch) { size_t size = struct_size_t(struct scx_cmask, bits, SCX_CMASK_NR_WORDS(num_possible_cpus())); int cpu; + scx_arena_free(sch, sch->online_cmask, size); if (!sch->set_cmask_scratch) return; @@ -5386,7 +5426,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work) rhashtable_free_and_destroy(&sch->dsq_hash, NULL, NULL); free_exit_info(sch->exit_info); - scx_set_cmask_scratch_free(sch); + scx_free_kern_arena_objs(sch); scx_arena_pool_destroy(sch); if (sch->arena_map) bpf_map_put(sch->arena_map); @@ -5447,7 +5487,7 @@ static ssize_t scx_attr_caps_show(struct kobject *kobj, struct scx_sched *sch = container_of(kobj, struct scx_sched, kobj); u32 npossible = num_possible_cpus(); struct scx_cmask *agg __free(kfree) = - kzalloc(struct_size(agg, bits, SCX_CMASK_NR_WORDS(npossible)), GFP_KERNEL); + kzalloc_flex(*agg, bits, SCX_CMASK_NR_WORDS(npossible)); unsigned long *agg_bm __free(bitmap) = bitmap_zalloc(npossible, GFP_KERNEL); ssize_t count = 0; s32 cap, si; @@ -5514,7 +5554,7 @@ static const struct kset_uevent_ops scx_uevent_ops = { }; /* - * Used by sched_fork() and __setscheduler_prio() to pick the matching + * Used by sched_fork() and __setscheduler_class() to pick the matching * sched_class. dl/rt are already handled. */ bool task_should_scx(int policy) @@ -7506,22 +7546,24 @@ static void scx_root_enable_workfn(struct kthread_work *work) #ifdef CONFIG_EXT_SUB_SCHED cgroup_get(cgrp); #endif + /* + * Transition to ENABLING to arm the disable path. Allocation failure + * still unwinds locally. Full disabling on failure applies only after + * scx_alloc_and_add_sched() succeeds. + */ + WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED); + WARN_ON_ONCE(scx_root); + sch = scx_alloc_and_add_sched(cmd, cgrp, NULL); if (IS_ERR(sch)) { ret = PTR_ERR(sch); + WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_ENABLING); goto err_free_tid_hash; } if (sch->is_cid_type) static_branch_enable(&__scx_is_cid_type); - /* - * Transition to ENABLING and clear exit info to arm the disable path. - * Failure triggers full disabling from here on. - */ - WARN_ON_ONCE(scx_set_enable_state(SCX_ENABLING) != SCX_DISABLED); - WARN_ON_ONCE(scx_root); - atomic_long_set(&scx_nr_rejected, 0); for_each_possible_cpu(cpu) { @@ -7589,7 +7631,7 @@ static void scx_root_enable_workfn(struct kthread_work *work) goto err_disable; } - ret = scx_set_cmask_scratch_alloc(sch); + ret = scx_alloc_kern_arena_objs(sch); if (ret) { cpus_read_unlock(); goto err_disable; @@ -7694,7 +7736,7 @@ static void scx_root_enable_workfn(struct kthread_work *work) /* * Enable ops for every task. Fork is excluded by scx_fork_rwsem * preventing new tasks from being added. No need to exclude tasks - * leaving as sched_ext_free() can handle both prepped and enabled + * leaving as sched_ext_dead() can handle both prepped and enabled * tasks. Prep all tasks first and then enable them with preemption * disabled. * @@ -7786,7 +7828,7 @@ static void scx_root_enable_workfn(struct kthread_work *work) /* * We're fully committed and can't fail. The task READY -> ENABLED - * transitions here are synchronized against sched_ext_free() through + * transitions here are synchronized against sched_ext_dead() through * scx_tasks_lock. */ percpu_down_write(&scx_fork_rwsem); @@ -8079,6 +8121,7 @@ static int bpf_scx_check_member(const struct btf_type *t, case offsetof(struct sched_ext_ops, cgroup_init): case offsetof(struct sched_ext_ops, cgroup_exit): case offsetof(struct sched_ext_ops, cgroup_prep_move): + case offsetof(struct sched_ext_ops, cgroup_set_bandwidth): #endif case offsetof(struct sched_ext_ops, cpu_online): case offsetof(struct sched_ext_ops, cpu_offline): @@ -8943,10 +8986,17 @@ __bpf_kfunc void scx_bpf_dsq_insert_vtime(struct task_struct *p, u64 dsq_id, #ifdef CONFIG_EXT_SUB_SCHED /* * Disallow if any sub-scheds are attached. There is no way to tell - * which scheduler called us, just error out @p's scheduler. + * which scheduler called us, so error out @p's scheduler -- read it + * under RCU as @p's locks aren't necessarily held here. @p may be a + * task past sched_ext_dead() or an idle task, in which case its + * scheduler can't be determined and there is nothing obviously wrong + * to report; just refuse the call. */ if (unlikely(!list_empty(&sch->children))) { - scx_error(scx_task_sched(p), "__scx_bpf_dsq_insert_vtime() must be used"); + struct scx_sched *tsch = scx_task_sched_rcu(p); + + if (tsch) + scx_error(tsch, "__scx_bpf_dsq_insert_vtime() must be used"); return; } #endif @@ -9003,12 +9053,6 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, if (unlikely(READ_ONCE(sch->aborting))) return false; - if (unlikely(!scx_task_on_sched(sch, p))) { - scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler", - p->comm, p->pid); - return false; - } - /* * Can be called from either ops.dispatch() holding the dispatched rq's * lock or any context where no rq lock is held. If latter, lock @p's @@ -9040,6 +9084,17 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, goto out; } + /* + * @p has been on $src_dsq and can't move anymore. If @p is not on @sch, + * the caller didn't have authority over @p at the time of the call. + */ + if (unlikely(!scx_task_on_sched(sch, p))) { + scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler", + p->comm, p->pid); + raw_spin_unlock(&src_dsq->lock); + goto out; + } + /* @p is still on $src_dsq and stable, determine the destination */ dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p)); @@ -9765,7 +9820,7 @@ __bpf_kfunc struct task_struct *bpf_iter_scx_dsq_next(struct bpf_iter_scx_dsq *i * bpf_iter_scx_dsq_destroy - Destroy a DSQ iterator * @it: iterator to destroy * - * Undo scx_iter_scx_dsq_new(). + * Undo bpf_iter_scx_dsq_new(). */ __bpf_kfunc void bpf_iter_scx_dsq_destroy(struct bpf_iter_scx_dsq *it) { @@ -10313,7 +10368,8 @@ __bpf_kfunc u32 scx_bpf_nr_cids(void) * hotplug, which lets schedulers treat [0, nr_online_cids) as the online * range. Schedulers that prefer to handle hotplug without a restart should * install a custom mapping via scx_bpf_cid_override() and track onlining - * through the ops.cid_online / ops.cid_offline callbacks. + * through the ops.cid_online / ops.cid_offline callbacks, starting from the + * mask scx_bpf_online_cmask() returns. */ __bpf_kfunc u32 scx_bpf_nr_online_cids(void) { @@ -10321,6 +10377,37 @@ __bpf_kfunc u32 scx_bpf_nr_online_cids(void) } /** + * scx_bpf_online_cmask - Return the online cid mask in the scheduler arena + * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs + * + * Return a kernel-maintained cmask covering [0, scx_bpf_nr_cids()), or NULL if + * the calling program is not associated with a live cid-form scheduler or the + * mask is not allocated yet, as in ops.init_cids(). Treat the mask as read-only + * even though arena memory stays writable by the BPF scheduler. The mask + * follows the SCX hotplug notifications: a cid's bit is updated before + * ops.cid_online/offline() runs for it. The pointer is valid from ops.init() + * through ops.exit(). Root ops.init() runs with hotplug excluded. Other + * contexts can observe concurrent updates. + */ +__bpf_kfunc const void *scx_bpf_online_cmask(const struct bpf_prog_aux *aux) +{ + struct scx_sched *sch; + struct scx_cmask *online; + + guard(rcu)(); + + sch = scx_prog_sched(aux); + if (unlikely(!sch)) + return NULL; + online = sch->online_cmask; + if (unlikely(!online)) + return NULL; + + /* BPF rebases by the low 32 bits, like __arena callback args */ + return (void *)((unsigned long)online - sch->arena_kern_base); +} + +/** * scx_bpf_this_cid - Return the cid of the CPU this program is running on * * cid-addressed equivalent of bpf_get_smp_processor_id() for scx programs. @@ -10683,6 +10770,7 @@ BTF_ID_FLAGS(func, scx_bpf_nr_node_ids) BTF_ID_FLAGS(func, scx_bpf_nr_cpu_ids) BTF_ID_FLAGS(func, scx_bpf_nr_cids) BTF_ID_FLAGS(func, scx_bpf_nr_online_cids) +BTF_ID_FLAGS(func, scx_bpf_online_cmask, KF_IMPLICIT_ARGS | KF_ARENA_RET) BTF_ID_FLAGS(func, scx_bpf_this_cid) BTF_ID_FLAGS(func, scx_bpf_get_possible_cpumask, KF_ACQUIRE) BTF_ID_FLAGS(func, scx_bpf_get_online_cpumask, KF_ACQUIRE) @@ -11041,3 +11129,16 @@ static int __init scx_init(void) return 0; } __initcall(scx_init); + +/* + * Compatibility markers for userspace. Existence of a marker function + * represents that the kernel supports that sched-ext feature. + */ + +/* + * scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that + * ops.cgroup_set_bandwidth() may be implemented as a sleepable callback. + */ +#ifdef CONFIG_EXT_GROUP_SCHED +DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep); +#endif /* CONFIG_EXT_GROUP_SCHED */ diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c index d2973fb3af6d..aa9fb6de0ad6 100644 --- a/kernel/sched/ext/idle.c +++ b/kernel/sched/ext/idle.c @@ -1142,10 +1142,17 @@ __bpf_kfunc s32 scx_bpf_select_cpu_and(struct task_struct *p, s32 prev_cpu, u64 #ifdef CONFIG_EXT_SUB_SCHED /* * Disallow if any sub-scheds are attached. There is no way to tell - * which scheduler called us, just error out @p's scheduler. + * which scheduler called us, so error out @p's scheduler -- read it + * under RCU as @p's locks aren't necessarily held here. @p may be a + * task past sched_ext_dead() or an idle task, in which case its + * scheduler can't be determined and there is nothing obviously wrong + * to report; just refuse the call. */ if (unlikely(!list_empty(&sch->children))) { - scx_error(scx_task_sched(p), "__scx_bpf_select_cpu_and() must be used"); + struct scx_sched *tsch = scx_task_sched_rcu(p); + + if (tsch) + scx_error(tsch, "__scx_bpf_select_cpu_and() must be used"); return -EINVAL; } #endif diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h index 27bbf5e04d90..3464e0f113c1 100644 --- a/kernel/sched/ext/internal.h +++ b/kernel/sched/ext/internal.h @@ -259,6 +259,9 @@ struct scx_cgroup_init_args { u64 bw_period_us; u64 bw_quota_us; u64 bw_burst_us; + + /* whether the cgroup is configured SCHED_IDLE via cpu.idle */ + bool sched_idle; }; enum scx_cpu_preempt_reason { @@ -442,7 +445,7 @@ struct sched_ext_ops { * * Note that this callback may be called from a CPU other than the * one the task is going to run on. This can happen when a task - * property is changed (i.e., affinity), since scx_next_task_scx(), + * property is changed (i.e., affinity), since set_next_task_scx(), * which triggers this callback, may run on a CPU different from * the task's assigned CPU. * @@ -569,6 +572,12 @@ struct sched_ext_ops { * * Specify the %SCX_OPS_KEEP_BUILTIN_IDLE flag to keep the built-in idle * tracking. + * + * Only actual transitions are reported. A CPU that is claimed with an + * idle pick and kicked but dispatches no task returns to idle without a + * transition. A scheduler tracking idle CPUs itself must restore the + * idle state from ops.dispatch() when it returns without the next task + * to run. */ void (*update_idle)(s32 cpu, bool idle); @@ -753,7 +762,7 @@ struct sched_ext_ops { * @burst_us: bandwidth control burst * * Update @cgrp's bandwidth control parameters. This is from the cpu.max - * cgroup interface. + * cgroup interface. This operation may block. * * @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled * to. For example, if @period_us is 1_000_000 and @quota_us is @@ -1552,6 +1561,7 @@ struct scx_sched { * and passes it to the callback's __arena argument. */ struct scx_cmask * __percpu *set_cmask_scratch; + struct scx_cmask *online_cmask; DECLARE_BITMAP(has_op, SCX_OPI_END); @@ -2001,6 +2011,27 @@ struct scx_bstr_buf { char line[SCX_EXIT_MSG_LEN]; }; +/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */ +#define DECLARE_SCX_COMPAT_MARKER(func) \ + extern void scx_compat_marker_##func(void) + +/** + * DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker + * @func: marker suffix; the defined symbol is scx_compat_marker_@func + * + * Emit an empty, callerless function that is retained in the kernel's BTF. + * Its presence is part of the kernel<->userspace contract: userspace probes + * scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports + * the corresponding feature. + * + * The leading declaration suppresses the missing-prototype warning; the + * trailing declaration consumes the semicolon at the use site. + */ +#define DEFINE_SCX_COMPAT_MARKER(func) \ + DECLARE_SCX_COMPAT_MARKER(func); \ + __used __retain void scx_compat_marker_##func(void) {} \ + DECLARE_SCX_COMPAT_MARKER(func) + extern struct scx_sched __rcu *scx_root; DECLARE_PER_CPU(struct rq *, scx_locked_rq_state); @@ -2057,7 +2088,7 @@ void scx_disable_and_exit_task(struct scx_sched *sch, struct task_struct *p); void scx_cgroup_lock(void); void scx_cgroup_unlock(void); #endif -s32 scx_set_cmask_scratch_alloc(struct scx_sched *sch); +s32 scx_alloc_kern_arena_objs(struct scx_sched *sch); void scx_disable_bypass_dsp(struct scx_sched *sch); void scx_bypass(struct scx_sched *sch, bool bypass); s32 scx_link_sched(struct scx_sched *sch); diff --git a/kernel/sched/ext/sub.c b/kernel/sched/ext/sub.c index 0554448835bd..34e642a1a403 100644 --- a/kernel/sched/ext/sub.c +++ b/kernel/sched/ext/sub.c @@ -194,7 +194,7 @@ s32 scx_alloc_pshards(struct scx_sched *sch) shard_node = rcu_dereference_protected(scx_shard_node, lockdep_is_held(&scx_enable_mutex)); - pshard = kzalloc_objs(pshard[0], scx_nr_cid_shards, GFP_KERNEL); + pshard = kzalloc_objs(pshard[0], scx_nr_cid_shards); if (!pshard) return -ENOMEM; @@ -1361,6 +1361,7 @@ static s32 scx_cgroup_claim_subtree(struct scx_sched *sch) .bw_period_us = tg->scx.bw_period_us, .bw_quota_us = tg->scx.bw_quota_us, .bw_burst_us = tg->scx.bw_burst_us, + .sched_idle = tg->scx.idle, }; if (tg->scx.sched != parent || @@ -1464,6 +1465,7 @@ static void scx_cgroup_return_subtree(struct scx_sched *sch) .bw_period_us = tg->scx.bw_period_us, .bw_quota_us = tg->scx.bw_quota_us, .bw_burst_us = tg->scx.bw_burst_us, + .sched_idle = tg->scx.idle, }; /* the first pass must have transferred everything */ @@ -1803,6 +1805,12 @@ void scx_sub_enable_workfn(struct kthread_work *work) goto err_disable; } + scoped_guard(cpus_read_lock) { + ret = scx_alloc_kern_arena_objs(sch); + if (ret) + goto err_disable; + } + if (sch->ops.init) { ret = SCX_CALL_OP_RET(sch, init, NULL); if (ret) { @@ -1813,10 +1821,6 @@ void scx_sub_enable_workfn(struct kthread_work *work) sch->exit_info->flags |= SCX_EFLAG_INITIALIZED; } - ret = scx_set_cmask_scratch_alloc(sch); - if (ret) - goto err_disable; - struct scx_sub_attach_args sub_attach_args = { .ops = &sch->ops, .cgroup_path = sch->cgrp_path, diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8dff37059faf..7455a83a6a99 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1032,6 +1032,13 @@ static inline void __max_slice_update(struct sched_entity *se, struct rb_node *n } } +static inline void min_vruntime_copy(struct sched_entity *new, struct sched_entity *old) +{ + new->min_vruntime = old->min_vruntime; + new->min_slice = old->min_slice; + new->max_slice = old->max_slice; +} + /* * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime) */ @@ -1059,8 +1066,9 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit) se->max_slice == old_max_slice; } -RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity, - run_node, min_vruntime, min_vruntime_update); + +RB_DECLARE_CALLBACKS_MULTI(static, min_vruntime_cb, struct sched_entity, + run_node, min_vruntime_copy, min_vruntime_update); /* * Enqueue an entity into the rb-tree: @@ -1073,6 +1081,8 @@ static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) sum_w_vruntime_add(cfs_rq, se); se->min_vruntime = se->vruntime; se->min_slice = se->slice; + se->max_slice = se->slice; + rb_add_augmented_cached(&se->run_node, &cfs_rq->tasks_timeline, __entity_less, &min_vruntime_cb); } @@ -1404,7 +1414,6 @@ static s64 update_se(struct rq *rq, struct sched_entity *se) se->exec_start = now; if (entity_is_task(se)) { - struct task_struct *donor = task_of(se); struct task_struct *running = rq->curr; /* * If se is a task, we account the time against the running @@ -1417,8 +1426,7 @@ static s64 update_se(struct rq *rq, struct sched_entity *se) account_group_exec_runtime(running, delta_exec); account_mm_sched(rq, running, delta_exec); - /* cgroup time is always accounted against the donor */ - cgroup_account_cputime(donor, delta_exec); + cgroup_account_cputime(running, delta_exec); } else { /* If not task, account the time against donor se */ se->sum_exec_runtime += delta_exec; @@ -6978,14 +6986,14 @@ static int tg_throttle_down(struct task_group *tg, void *data) static bool throttle_cfs_rq(struct cfs_rq *cfs_rq) { struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg); - struct sched_entity *curr = cfs_rq->curr; + struct sched_entity *curr = cfs_rq->h_curr; struct rq *rq = rq_of(cfs_rq); scoped_guard(raw_spinlock, &cfs_b->lock) { u64 target_runtime = 1; /* - * If cfs_rq->curr is still runnable, we are here from an + * If cfs_rq->h_curr is still runnable, we are here from an * update_curr(). Request sysctl_sched_cfs_bandwidth_slice * worth of bandwidth to continue running. * @@ -7192,7 +7200,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b) if (!list_empty(&cfs_rq->throttled_csd_list)) continue; - if (cfs_rq->curr) { + if (cfs_rq->h_curr) { update_rq_clock(rq); update_curr(cfs_rq); } @@ -10057,7 +10065,7 @@ again: /* Might not have done put_prev_entity() */ if (cfs_rq->curr && cfs_rq->curr->on_rq) - update_curr(cfs_rq); + update_curr_eevdf(cfs_rq); se = pick_next_entity(rq, true); if (!se) @@ -10160,7 +10168,7 @@ static void yield_task_fair(struct rq *rq) /* * Update run-time statistics of the 'current'. */ - update_curr(cfs_rq); + update_curr_eevdf(cfs_rq); /* * Tell update_rq_clock() that we've just updated, * so we don't do microscopic update in schedule() @@ -10691,17 +10699,40 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, return mig_llc; } +static inline bool task_misfits_asym_cpu(struct lb_env *env, struct task_struct *p) +{ + /* + * On asymmetric CPU capacity domains, do not let cache-aware + * balancing pull the task onto a destination CPU that cannot + * accommodate it. Doing so would turn the task into a misfit on + * the destination, trading a cache-locality gain for a capacity + * loss. If the task already does not fit its source CPU, the move + * cannot make things worse, so let the LLC preference decide. + */ + if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && p && + !task_fits_cpu(p, env->dst_cpu) && + task_fits_cpu(p, env->src_cpu)) + return true; + + return false; +} + /* * Check if task p can migrate from source LLC to * destination LLC in terms of cache aware load balance. */ -static enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu, +static enum llc_mig can_migrate_llc_task(struct lb_env *env, struct task_struct *p) { struct mm_struct *mm; bool to_pref; - int cpu; + int cpu, src_cpu, dst_cpu; + + if (task_misfits_asym_cpu(env, p)) + return mig_forbid; + src_cpu = env->src_cpu; + dst_cpu = env->dst_cpu; mm = p->mm; if (!mm) return mig_unrestricted; @@ -10758,6 +10789,14 @@ alb_break_llc(struct lb_env *env) unsigned long util = 0; struct task_struct *cur; + /* + * Migrating misfit tasks from current CPU + * to CPU with a better fit. + * Prioritize that over LLC preference. + */ + if (env->migration_type == migrate_misfit) + return false; + if (env->src_rq->nr_running <= 1) return true; @@ -10765,7 +10804,8 @@ alb_break_llc(struct lb_env *env) if (cur && cur->sched_class == &fair_sched_class) util = task_util(cur); - if (can_migrate_llc(env->src_cpu, env->dst_cpu, + if (task_misfits_asym_cpu(env, cur) || + can_migrate_llc(env->src_cpu, env->dst_cpu, util, false) == mig_forbid) return true; } @@ -10805,8 +10845,7 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu)) return true; - if (can_migrate_llc_task(env->src_cpu, - env->dst_cpu, p) != mig_forbid) + if (can_migrate_llc_task(env, p) != mig_forbid) return false; return true; @@ -11870,6 +11909,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, return false; /* + * On asymmetric domains, group_misfit_task_load + * should be prioritized to move tasks to CPU that fit them + * over aggregating tasks to their preferred LLC. + */ + if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && + sgs->group_misfit_task_load) + return false; + + /* * Skip cache aware tagging if nr_balanced_failed is sufficiently high. * Threshold of cache_nice_tries is set to 1 higher than nr_balance_failed * to avoid excessive task migration at the same time. diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index e6e5f8a2caaf..85303add726d 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1872,8 +1872,8 @@ static struct task_struct *pick_next_pushable_task(struct rq *rq) return NULL; plist_for_each_entry(i, head, pushable_tasks) { - /* make sure task isn't on_cpu (possible with proxy-exec) */ - if (!task_on_cpu(rq, i)) { + /* skip tasks that cannot be migrated */ + if (!task_on_cpu(rq, i) && !is_migration_disabled(i)) { p = i; break; } diff --git a/kernel/signal.c b/kernel/signal.c index a5e15bf09d31..ec30550951ec 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1892,6 +1892,18 @@ int kill_pid(struct pid *pid, int sig, int priv) } EXPORT_SYMBOL(kill_pid); +int kill_cad_pid(int sig, int priv) +{ + int ret; + + rcu_read_lock(); + ret = kill_pid(rcu_dereference(cad_pid), sig, priv); + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(kill_cad_pid); + #ifdef CONFIG_POSIX_TIMERS /* * These functions handle POSIX timer signals. POSIX timers use diff --git a/kernel/softirq.c b/kernel/softirq.c index 7980a4a232f9..5d02c36c40e3 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -91,11 +91,11 @@ EXPORT_PER_CPU_SYMBOL_GPL(hardirq_context); DEFINE_PER_CPU(unsigned long, local_interrupt_disable_state); -void _local_interrupt_disable(void) +void _local_interrupt_save_state(unsigned long flags) { - __local_interrupt_disable(); + __local_interrupt_save_state(flags); } -EXPORT_SYMBOL(_local_interrupt_disable); +EXPORT_SYMBOL(_local_interrupt_save_state); void _local_interrupt_enable(void) { @@ -749,16 +749,7 @@ static inline void __irq_exit_rcu(void) #endif account_hardirq_exit(current); preempt_count_sub(HARDIRQ_OFFSET); - /* - * Interrupts may happen between hardirq_disable_enter() and - * local_irq_save() in local_interrupt_disable(), if irq_exit() invokes - * softirq here, we may have a softirq handler calling - * local_interrupt_disable() but it won't disable the IRQ because - * hardirq disabling count is already 1, hence we need to prevent - * invoking softirq when a local_interrupt_disable() is ongoing. - */ - if (!in_interrupt() && !hardirq_disable_count() && - local_softirq_pending()) { + if (!in_interrupt() && local_softirq_pending()) { /* * If we left hrtimers unarmed, make sure to arm them now, * before enabling interrupts to run softirq. diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 0014d163f989..62ad7c000386 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -615,34 +615,41 @@ void clockevents_handle_noop(struct clock_event_device *dev) { } -/** - * clockevents_exchange_device - release and request clock devices - * @old: device to release (can be NULL) - * @new: device to request (can be NULL) - * - * Called from various tick functions with clockevents_lock held and - * interrupts disabled. - */ -void clockevents_exchange_device(struct clock_event_device *old, - struct clock_event_device *new) +void __clockevents_exchange_device(struct clock_event_device *old, + struct clock_event_device *new) { /* * Caller releases a clock event device. We queue it into the * released list and do a notify add later. */ if (old) { - module_put(old->owner); clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED); list_move(&old->list, &clockevents_released); } if (new) { - BUG_ON(!clockevent_state_detached(new)); + WARN_ON(!clockevent_state_detached(new)); clockevents_shutdown(new); } } /** + * clockevents_exchange_device - release and request clock devices + * @old: device to release (can be NULL) + * @new: device to request (can be NULL) + * + * Called from various tick functions with clockevents_lock held and + * interrupts disabled. + */ +void clockevents_exchange_device(struct clock_event_device *old, + struct clock_event_device *new) +{ + __clockevents_exchange_device(old, new); + if (old) + module_put(old->owner); +} + +/** * clockevents_suspend - suspend clock devices */ void clockevents_suspend(void) @@ -699,7 +706,7 @@ void tick_offline_cpu(unsigned int cpu) if (cpumask_test_cpu(cpu, dev->cpumask) && cpumask_weight(dev->cpumask) == 1 && !tick_is_broadcast_device(dev)) { - BUG_ON(!clockevent_state_detached(dev)); + WARN_ON(!clockevent_state_detached(dev)); list_del(&dev->list); } } diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 530d61257b9a..cbf1693c86b3 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1263,13 +1263,23 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b { bool was_first = false; + /* + * Updating the sort key while @timer is queued can temporarily + * make the tree inconsistent. This is safe under cpu_base->lock: + * no other queue operation can observe that state. + * hrtimer_can_update_in_place() either confirms that the new expiry + * fits between the neighbours or timerqueue_linked_del() removes the + * timer without consulting the expiry. + */ + hrtimer_set_expires_range_ns(timer, expires, delta_ns); + expires = hrtimer_get_expires(timer); + /* Remove it from the timer queue if active */ if (timer->is_queued) { was_first = !timerqueue_linked_prev(&timer->node); /* Try to update in place to avoid the de/enqueue dance */ if (hrtimer_can_update_in_place(timer, base, expires)) { - hrtimer_set_expires_range_ns(timer, expires, delta_ns); trace_hrtimer_start(timer, mode, true); if (was_first) base->expires_next = expires; @@ -1280,9 +1290,6 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b timerqueue_linked_del(&base->active, &timer->node); } - /* Set the new expiry time */ - hrtimer_set_expires_range_ns(timer, expires, delta_ns); - debug_activate(timer, mode, timer->is_queued); base->cpu_base->active_bases |= 1 << base->index; diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index 213ae1d6a014..80c354811538 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -136,6 +136,8 @@ static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr static ulong sysctl_msecs_to_jiffies(const ulong val) { + if (val > jiffies_to_msecs(MAX_JIFFY_OFFSET)) + return MAX_JIFFY_OFFSET; return msecs_to_jiffies(val); } @@ -181,7 +183,7 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, int *k_ptr, int dir, const struct ctl_table *tbl) { - return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true, sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms); } @@ -195,10 +197,10 @@ static int sysctl_k2u_ulong_conv_ms(ulong *u_ptr, const ulong *k_ptr) return proc_ulong_k2u_conv_kop(u_ptr, k_ptr, sysctl_jiffies_to_msecs); } -static int do_proc_ulong_conv_ms_jiffies(bool *negp, ulong *u_ptr, ulong *k_ptr, - int dir, const struct ctl_table *tbl) +static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr, + int dir, const struct ctl_table *tbl) { - return proc_ulong_conv(u_ptr, k_ptr, dir, tbl, false, + return proc_ulong_conv(u_ptr, k_ptr, dir, tbl, true, sysctl_u2k_ulong_conv_ms, sysctl_k2u_ulong_conv_ms); } @@ -229,8 +231,8 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, return -ENOSYS; } -static int do_proc_ulong_conv_ms_jiffies(bool *negp, ulong *u_ptr, ulong *k_ptr, - int dir, const struct ctl_table *tbl) +static int do_proc_ulong_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, ulong *k_ptr, + int dir, const struct ctl_table *tbl) { return -ENOSYS; } @@ -333,7 +335,7 @@ int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos) { return proc_doulongvec_conv(table, dir, buffer, lenp, ppos, - do_proc_ulong_conv_ms_jiffies); + do_proc_ulong_conv_ms_jiffies_minmax); } EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 115e0bf01276..bda3d2391a60 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -165,23 +165,31 @@ static bool tick_set_oneshot_wakeup_device(struct clock_event_device *newdev, */ void tick_install_broadcast_device(struct clock_event_device *dev, int cpu) { - struct clock_event_device *cur = tick_broadcast_device.evtdev; + struct clock_event_device *cur; - if (tick_set_oneshot_wakeup_device(dev, cpu)) - return; + scoped_guard(raw_spinlock_irqsave, &tick_broadcast_lock) { - if (!tick_check_broadcast_device(cur, dev)) - return; + if (tick_set_oneshot_wakeup_device(dev, cpu)) + return; - if (!try_module_get(dev->owner)) - return; + cur = tick_broadcast_device.evtdev; + if (!tick_check_broadcast_device(cur, dev)) + return; - clockevents_exchange_device(cur, dev); + if (!try_module_get(dev->owner)) + return; + + __clockevents_exchange_device(cur, dev); + if (cur) + cur->event_handler = clockevents_handle_noop; + WRITE_ONCE(tick_broadcast_device.evtdev, dev); + if (!cpumask_empty(tick_broadcast_mask)) + tick_broadcast_start_periodic(dev); + } + + /* Module release must be outside of the lock */ if (cur) - cur->event_handler = clockevents_handle_noop; - tick_broadcast_device.evtdev = dev; - if (!cpumask_empty(tick_broadcast_mask)) - tick_broadcast_start_periodic(dev); + module_put(cur->owner); if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT)) return; @@ -1218,7 +1226,7 @@ int tick_broadcast_oneshot_active(void) */ bool tick_broadcast_oneshot_available(void) { - struct clock_event_device *bc = tick_broadcast_device.evtdev; + struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev); return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false; } @@ -1226,7 +1234,7 @@ bool tick_broadcast_oneshot_available(void) #else int __tick_broadcast_oneshot_control(enum tick_broadcast_state state) { - struct clock_event_device *bc = tick_broadcast_device.evtdev; + struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev); if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER)) return -EBUSY; diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 182974c4f21b..65680db95053 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -55,6 +55,8 @@ static inline void clockevent_set_state(struct clock_event_device *dev, } extern void clockevents_shutdown(struct clock_event_device *dev); +extern void __clockevents_exchange_device(struct clock_event_device *old, + struct clock_event_device *new); extern void clockevents_exchange_device(struct clock_event_device *old, struct clock_event_device *new); extern void clockevents_switch_state(struct clock_event_device *dev, diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c index 40d373d65f9b..ed455b53513b 100644 --- a/kernel/trace/fgraph.c +++ b/kernel/trace/fgraph.c @@ -143,9 +143,6 @@ enum { #define FGRAPH_DATA_INDEX_MASK GENMASK(FGRAPH_DATA_INDEX_BITS - 1, 0) #define FGRAPH_DATA_INDEX_SHIFT (FGRAPH_DATA_SHIFT + FGRAPH_DATA_BITS) -#define FGRAPH_MAX_INDEX \ - ((FGRAPH_INDEX_SIZE << FGRAPH_DATA_BITS) + FGRAPH_RET_INDEX) - #define FGRAPH_ARRAY_SIZE FGRAPH_INDEX_BITS /* diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index ddb0b12a5c4a..1e9b00997ff2 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -945,7 +945,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter if (num < 0) return num; - addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL); + addrs = kzalloc_objs(*addrs, num); if (!addrs) return -ENOMEM; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f9d80c7bd9f1..673a54fdf392 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -75,6 +75,8 @@ .func_hash = &opsname.local_hash, \ .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock), \ .subop_list = LIST_HEAD_INIT(opsname.subop_list), +/* Used only to synchronize the initialization of ftrace_ops */ +static DEFINE_MUTEX(ops_mutex); #else #define INIT_OPS_HASH(opsname) #endif @@ -159,11 +161,18 @@ const struct ftrace_ops ftrace_nop_ops = { static inline void ftrace_ops_init(struct ftrace_ops *ops) { #ifdef CONFIG_DYNAMIC_FTRACE - if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) { + unsigned long flags = smp_load_acquire(&ops->flags); + + if (!(flags & FTRACE_OPS_FL_INITIALIZED)) { + guard(mutex)(&ops_mutex); + /* Could have been initialized before lock taken */ + if (unlikely(ops->flags & FTRACE_OPS_FL_INITIALIZED)) + return; mutex_init(&ops->local_hash.regex_lock); INIT_LIST_HEAD(&ops->subop_list); ops->func_hash = &ops->local_hash; - ops->flags |= FTRACE_OPS_FL_INITIALIZED; + flags = ops->flags | FTRACE_OPS_FL_INITIALIZED; + smp_store_release(&ops->flags, flags); } #endif } @@ -4677,7 +4686,8 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file) /** * ftrace_regex_open - initialize function tracer filter files - * @ops: The ftrace_ops that hold the hash filters + * @tr: The trace_array that holds the ftrace_ops [optional] + * @ops: The ftrace_ops that hold the hash filters [optional] * @flag: The type of filter to process * @inode: The inode, usually passed in to your open routine * @file: The file, usually passed in to your open routine @@ -4691,26 +4701,45 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file) * tracing_lseek() should be used as the lseek routine, and * release must call ftrace_regex_release(). * + * Note, If @tr is not NULL, its reference has to be taken before + * @ops may be referenced. + * If @ops is NULL and @tr is not, then @tr->ops is used. + * If @tr is NULL and @ops is not then @ops->private is uesd for @tr. + * If both @tr and @ops are NULL, then the &global_ops is + * to be used, and @tr will be the global_ops.private pointer. + * * Returns: 0 on success or a negative errno value on failure */ int -ftrace_regex_open(struct ftrace_ops *ops, int flag, +ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag, struct inode *inode, struct file *file) { - struct ftrace_iterator *iter; + struct ftrace_iterator *iter = NULL; struct ftrace_hash *hash; struct list_head *mod_head; - struct trace_array *tr = ops->private; - int ret = -ENOMEM; - - ftrace_ops_init(ops); + int ret = -ENODEV; if (unlikely(ftrace_disabled)) return -ENODEV; + if (!tr) { + if (!ops) + ops = &global_ops; + tr = ops->private; + } + if (tracing_check_open_get_tr(tr)) return -ENODEV; + if (!ops) + ops = tr->ops; + + if (WARN_ON_ONCE(!ops)) + goto out; + + ftrace_ops_init(ops); + + ret = -ENOMEM; iter = kzalloc_obj(*iter); if (!iter) goto out; @@ -4788,21 +4817,19 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, static int ftrace_filter_open(struct inode *inode, struct file *file) { - struct ftrace_ops *ops = inode->i_private; + struct trace_array *tr = inode->i_private; - /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, - FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES, - inode, file); + return ftrace_regex_open(tr, NULL, + FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES, + inode, file); } static int ftrace_notrace_open(struct inode *inode, struct file *file) { - struct ftrace_ops *ops = inode->i_private; + struct trace_array *tr = inode->i_private; - /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE, + return ftrace_regex_open(tr, NULL, FTRACE_ITER_NOTRACE, inode, file); } @@ -6648,7 +6675,7 @@ int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, b /* Enable the tmp_ops to have the same functions as the hash object. */ ftrace_ops_init(&tmp_ops); - tmp_ops.func_hash->filter_hash = hash; + rcu_assign_pointer(tmp_ops.func_hash->filter_hash, hash); err = register_ftrace_function_nolock(&tmp_ops); if (err) @@ -7492,15 +7519,15 @@ static const struct file_operations ftrace_graph_notrace_fops = { }; #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -void ftrace_create_filter_files(struct ftrace_ops *ops, +void ftrace_create_filter_files(struct trace_array *tr, struct dentry *parent) { trace_create_file("set_ftrace_filter", TRACE_MODE_WRITE, parent, - ops, &ftrace_filter_fops); + tr, &ftrace_filter_fops); trace_create_file("set_ftrace_notrace", TRACE_MODE_WRITE, parent, - ops, &ftrace_notrace_fops); + tr, &ftrace_notrace_fops); } /* @@ -7525,7 +7552,6 @@ void ftrace_destroy_filter_files(struct ftrace_ops *ops) static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer) { - trace_create_file("available_filter_functions", TRACE_MODE_READ, d_tracer, NULL, &ftrace_avail_fops); @@ -7538,7 +7564,7 @@ static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer) trace_create_file("touched_functions", TRACE_MODE_READ, d_tracer, NULL, &ftrace_touched_fops); - ftrace_create_filter_files(&global_ops, d_tracer); + ftrace_create_filter_files(NULL, d_tracer); #ifdef CONFIG_FUNCTION_GRAPH_TRACER trace_create_file("set_graph_function", TRACE_MODE_WRITE, d_tracer, diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c index 5a8bdf88999a..87a27f3aa4a6 100644 --- a/kernel/trace/rethook.c +++ b/kernel/trace/rethook.c @@ -171,7 +171,7 @@ struct rethook_node *rethook_try_get(struct rethook *rh) * This expects the caller will set up a rethook on a function entry. * When the function returns, the rethook will eventually be reclaimed * or released in the rethook_recycle() with call_rcu(). - * This means the caller must be run in the RCU-availabe context. + * This means the caller must be run in the RCU-available context. */ if (unlikely(!rcu_is_watching())) return NULL; diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index b0963ac6fd16..04bb94c29f58 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -330,6 +330,14 @@ struct buffer_data_read_page { struct buffer_data_page *data; /* actual data, stored in this page */ }; +static __always_inline unsigned int rb_read_page_capacity(struct buffer_data_read_page *rpage) +{ + return (PAGE_SIZE << rpage->order) - BUF_PAGE_HDR_SIZE; +} + +/* The number of bits for static buffer ids */ +#define RB_STATIC_BITS 30 + /* * Note, the buffer_page list must be first. The buffer pages * are allocated in cache lines, which means that each buffer @@ -345,7 +353,7 @@ struct buffer_page { local_t entries; /* entries on this page */ unsigned long real_end; /* real end of data */ unsigned order; /* order of the page */ - u32 id:30; /* ID for external mapping */ + u32 id:RB_STATIC_BITS; /* ID for external mapping */ u32 range:1; /* Mapped via a range */ struct buffer_data_page *page; /* Actual data page */ }; @@ -652,6 +660,15 @@ static bool rb_is_static(struct ring_buffer_per_cpu *cpu_buffer) return cpu_buffer->user_mapped || cpu_buffer->remote || cpu_buffer->ring_meta; } +static unsigned long rb_static_max_pages(void) +{ + /* + * Static ring buffers are using bpage::id and must account for the + * reader page. + */ + return (1UL << RB_STATIC_BITS) - 1; +} + struct ring_buffer_iter { struct ring_buffer_per_cpu *cpu_buffer; unsigned long head; @@ -887,14 +904,13 @@ static void rb_wake_up_waiters(struct irq_work *work) struct ring_buffer_per_cpu *cpu_buffer = container_of(rbwork, struct ring_buffer_per_cpu, irq_work); - /* Called from interrupt context */ - raw_spin_lock(&cpu_buffer->reader_lock); - rbwork->wakeup_full = false; - rbwork->full_waiters_pending = false; + scoped_guard(raw_spinlock_irqsave, &cpu_buffer->reader_lock) { + rbwork->wakeup_full = false; + rbwork->full_waiters_pending = false; - /* Waking up all waiters, they will reset the shortest full */ - cpu_buffer->shortest_full = 0; - raw_spin_unlock(&cpu_buffer->reader_lock); + /* Waking up all waiters, they will reset the shortest full */ + cpu_buffer->shortest_full = 0; + } wake_up_all(&rbwork->full_waiters); } @@ -1669,7 +1685,7 @@ out_locked: * This is used to help find the next per cpu subbuffer within a mapped range. */ static unsigned long -rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs) +rb_range_align_subbuf(unsigned long addr, unsigned int subbuf_size, unsigned long nr_subbufs) { addr += sizeof(struct ring_buffer_cpu_meta) + sizeof(int) * nr_subbufs; @@ -1679,13 +1695,12 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs) /* * Return the ring_buffer_meta for a given @cpu. */ -static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu) +static void *rb_range_meta(struct trace_buffer *buffer, unsigned long nr_pages, int cpu) { - int subbuf_size = rb_subbuf_size(buffer); + unsigned int subbuf_size = rb_subbuf_size(buffer); struct ring_buffer_cpu_meta *meta; struct ring_buffer_meta *bmeta; - unsigned long ptr; - int nr_subbufs; + unsigned long ptr, nr_subbufs; bmeta = buffer->meta; if (!bmeta) @@ -1731,7 +1746,7 @@ static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu) /* Return the start of subbufs given the meta pointer */ static void *rb_subbufs_from_meta(struct ring_buffer_cpu_meta *meta) { - int subbuf_size = meta->subbuf_size; + unsigned int subbuf_size = meta->subbuf_size; unsigned long ptr; ptr = (unsigned long)meta; @@ -1743,11 +1758,11 @@ static void *rb_subbufs_from_meta(struct ring_buffer_cpu_meta *meta) /* * Return a specific sub-buffer for a given @cpu defined by @idx. */ -static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx) +static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, unsigned long idx) { struct ring_buffer_cpu_meta *meta; + unsigned int subbuf_size; unsigned long ptr; - int subbuf_size; meta = rb_range_meta(cpu_buffer->buffer, 0, cpu_buffer->cpu); if (!meta) @@ -1763,7 +1778,7 @@ static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx) ptr = (unsigned long)rb_subbufs_from_meta(meta); - ptr += subbuf_size * idx; + ptr += (unsigned long)subbuf_size * idx; if (ptr + subbuf_size > cpu_buffer->buffer->range_addr_end) return NULL; @@ -1840,13 +1855,12 @@ static bool rb_meta_init(struct trace_buffer *buffer, int scratch_size) * must be the same. */ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, - struct trace_buffer *buffer, int nr_pages, + struct trace_buffer *buffer, unsigned long nr_pages, unsigned long *subbuf_mask) { - int subbuf_size = PAGE_SIZE; unsigned long buffers_start; unsigned long buffers_end; - int i; + unsigned long i; if (!subbuf_mask) return false; @@ -1856,8 +1870,13 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu, return false; } + if (meta->nr_subbufs != nr_pages + 1) { + pr_info("Ring buffer boot meta [%d] invalid nr_subbufs\n", cpu); + return false; + } + buffers_start = meta->first_buffer; - buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs); + buffers_end = meta->first_buffer + (meta->nr_subbufs * PAGE_SIZE); /* Is the head and commit buffers within the range of buffers? */ if (meta->head_buffer < buffers_start || @@ -2095,8 +2114,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) struct buffer_page *head_page, *orig_head, *orig_reader; struct rb_validation_state state = { 0 }; bool skip = false; + unsigned long i; int ret; - int i; if (!meta || !meta->head_buffer) return; @@ -2147,7 +2166,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) rb_validate_buffer(head_page, cpu_buffer, meta, &state, 0, state.ts); } if (i) - pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i); + pr_info("Ring buffer [%d] rewound %lu pages\n", cpu_buffer->cpu, i); /* The last rewound page must be skipped. */ if (head_page != orig_head) @@ -2231,7 +2250,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) } } -static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages, int scratch_size) +static void rb_range_meta_init(struct trace_buffer *buffer, unsigned long nr_pages, + int scratch_size) { struct ring_buffer_cpu_meta *meta; unsigned long *subbuf_mask; @@ -2331,8 +2351,8 @@ static int rbm_show(struct seq_file *m, void *v) rb_meta_subbuf_idx(meta, (void *)meta->head_buffer)); seq_printf(m, "commit_buffer: %d\n", rb_meta_subbuf_idx(meta, (void *)meta->commit_buffer)); - seq_printf(m, "subbuf_size: %d\n", meta->subbuf_size); - seq_printf(m, "nr_subbufs: %d\n", meta->nr_subbufs); + seq_printf(m, "subbuf_size: %u\n", meta->subbuf_size); + seq_printf(m, "nr_subbufs: %u\n", meta->nr_subbufs); return 0; } @@ -2417,7 +2437,7 @@ static void *ring_buffer_desc_page(struct ring_buffer_desc *desc, unsigned int p } static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, - long nr_pages, struct list_head *pages) + unsigned long nr_pages, struct list_head *pages) { struct trace_buffer *buffer = cpu_buffer->buffer; struct ring_buffer_cpu_meta *meta = NULL; @@ -2545,7 +2565,7 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, } static struct ring_buffer_per_cpu * -rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) +rb_allocate_cpu_buffer(struct trace_buffer *buffer, unsigned long nr_pages, int cpu) { struct ring_buffer_per_cpu *cpu_buffer __free(kfree) = alloc_cpu_buffer(cpu); @@ -2600,8 +2620,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) cpu_buffer->remote = buffer->remote; cpu_buffer->meta_page = (struct trace_buffer_meta *)(void *)desc->meta_va; cpu_buffer->nr_pages = nr_pages; - cpu_buffer->subbuf_ids = kcalloc(cpu_buffer->nr_pages + 1, - sizeof(*cpu_buffer->subbuf_ids), GFP_KERNEL); + cpu_buffer->subbuf_ids = kzalloc_objs(*cpu_buffer->subbuf_ids, + cpu_buffer->nr_pages + 1); if (!cpu_buffer->subbuf_ids) goto fail_free_reader; @@ -2702,8 +2722,8 @@ static void rb_test_inject_invalid_pages(struct trace_buffer *buffer) struct ring_buffer_cpu_meta *meta; struct buffer_data_page *dpage; unsigned long entry_bytes = 0; + unsigned int subbuf_size; unsigned long ptr; - int subbuf_size; int invalid = 0; int cpu; int i; @@ -2773,8 +2793,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, struct ring_buffer_remote *remote) { struct trace_buffer *buffer __free(kfree) = NULL; - long nr_pages; - int subbuf_size; + unsigned int subbuf_size; + unsigned long nr_pages; int bsize; int cpu; int ret; @@ -2828,6 +2848,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, size = end - buffers_start; size = size / nr_cpu_ids; + if (size < sizeof(struct ring_buffer_cpu_meta)) + goto fail_free_buffers; /* * The number of sub-buffers (nr_pages) is determined by the * total size allocated minus the meta data size. @@ -2837,6 +2859,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, */ nr_pages = (size - sizeof(struct ring_buffer_cpu_meta)) / (subbuf_size + sizeof(int)); + + if (nr_pages > rb_static_max_pages()) + goto fail_free_buffers; + /* Need at least two pages plus the reader page */ if (nr_pages < 3) goto fail_free_buffers; @@ -2869,6 +2895,10 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, /* The writer is remote. This ring-buffer is read-only */ atomic_inc(&buffer->record_disabled); nr_pages = desc->nr_page_va - 1; + + if (nr_pages > rb_static_max_pages()) + goto fail_free_buffers; + if (nr_pages < 2) goto fail_free_buffers; } else { @@ -5853,12 +5883,12 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) static struct buffer_page * __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) { - int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3; + unsigned long max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3; struct buffer_page *reader = NULL; + unsigned long nr_loops = 0; unsigned long overwrite; unsigned long flags; int missed_events = 0; - int nr_loops = 0; bool ret; local_irq_save(flags); @@ -6176,8 +6206,8 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts) struct trace_buffer *buffer; struct ring_buffer_per_cpu *cpu_buffer; struct ring_buffer_event *event; - int nr_loops = 0; - int max_loops; + unsigned long nr_loops = 0; + unsigned long max_loops; if (ts) *ts = 0; @@ -6993,56 +7023,78 @@ EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu); * ring_buffer_alloc_read_page - allocate a page to read from buffer * @buffer: the buffer to allocate for. * @cpu: the cpu buffer to allocate. + * @rpage: pointer to pass in an already allocated page (can be NULL) + * and returns the allocated page. * - * This function is used in conjunction with ring_buffer_read_page. + * This function is used in conjunction with ring_buffer_read_page(). * When reading a full page from the ring buffer, these functions * can be used to speed up the process. The calling function should * allocate a few pages first with this function. Then when it * needs to get pages from the ring buffer, it passes the result - * of this function into ring_buffer_read_page, which will swap + * of this function into ring_buffer_read_page(), which will swap * the page that was allocated, with the read page of the buffer. * + * If @rpage is provided, and it has a different order than the current + * subbuffer order, its payload will be freed and re-allocated. If it + * already matches the order, it is simply returned. + * * Returns: - * The page allocated, or ERR_PTR + * 0 on success, < 0 on error */ -struct buffer_data_read_page * -ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu) +int ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu, + struct buffer_data_read_page **rpage) { struct ring_buffer_per_cpu *cpu_buffer; - struct buffer_data_read_page *bpage = NULL; unsigned long flags; + unsigned int order; if (!cpumask_test_cpu(cpu, buffer->cpumask)) - return ERR_PTR(-ENODEV); + return -ENODEV; - bpage = kzalloc_obj(*bpage); - if (!bpage) - return ERR_PTR(-ENOMEM); + if (!rpage) + return -EINVAL; + + order = READ_ONCE(buffer->subbuf_order); - bpage->order = buffer->subbuf_order; + if (*rpage) { + if ((*rpage)->order == order) + return 0; + + /* We can reuse rpage, but we discard the payload */ + free_pages((unsigned long)(*rpage)->data, (*rpage)->order); + (*rpage)->data = NULL; + } else { + *rpage = kzalloc_obj(**rpage); + if (!*rpage) + return -ENOMEM; + } + + (*rpage)->order = order; cpu_buffer = buffer->buffers[cpu]; + local_irq_save(flags); arch_spin_lock(&cpu_buffer->lock); if (cpu_buffer->free_page.data) { - *bpage = cpu_buffer->free_page; + **rpage = cpu_buffer->free_page; cpu_buffer->free_page.data = NULL; } arch_spin_unlock(&cpu_buffer->lock); local_irq_restore(flags); - if (bpage->data) { - rb_init_data_page(bpage->data); + if ((*rpage)->data) { + rb_init_data_page((*rpage)->data); } else { - bpage->data = alloc_cpu_data(cpu, bpage->order); - if (!bpage->data) { - kfree(bpage); - return ERR_PTR(-ENOMEM); + (*rpage)->data = alloc_cpu_data(cpu, (*rpage)->order); + if (!(*rpage)->data) { + kfree(*rpage); + *rpage = NULL; + return -ENOMEM; } } - return bpage; + return 0; } EXPORT_SYMBOL_GPL(ring_buffer_alloc_read_page); @@ -7050,21 +7102,30 @@ EXPORT_SYMBOL_GPL(ring_buffer_alloc_read_page); * ring_buffer_free_read_page - free an allocated read page * @buffer: the buffer the page was allocate for * @cpu: the cpu buffer the page came from - * @data_page: the page to free + * @rpage: the buffer_data_read_page to free * * Free a page allocated from ring_buffer_alloc_read_page. */ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, - struct buffer_data_read_page *data_page) + struct buffer_data_read_page *rpage) { struct ring_buffer_per_cpu *cpu_buffer; - struct buffer_data_page *dpage = data_page->data; - struct page *page = virt_to_page(dpage); + struct buffer_data_page *dpage; unsigned long flags; + struct page *page; if (!buffer || !buffer->buffers || !buffer->buffers[cpu]) return; + if (!rpage) + return; + + dpage = rpage->data; + if (!dpage) + goto out; + + page = virt_to_page(dpage); + cpu_buffer = buffer->buffers[cpu]; /* @@ -7072,14 +7133,14 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, * is different from the subbuffer order of the buffer - * we can't reuse it */ - if (page_ref_count(page) > 1 || data_page->order != buffer->subbuf_order) + if (page_ref_count(page) > 1 || rpage->order != READ_ONCE(buffer->subbuf_order)) goto out; local_irq_save(flags); arch_spin_lock(&cpu_buffer->lock); if (!cpu_buffer->free_page.data) { - cpu_buffer->free_page = *data_page; + cpu_buffer->free_page = *rpage; dpage = NULL; } @@ -7087,8 +7148,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, local_irq_restore(flags); out: - free_pages((unsigned long)dpage, data_page->order); - kfree(data_page); + free_pages((unsigned long)dpage, rpage->order); + kfree(rpage); } EXPORT_SYMBOL_GPL(ring_buffer_free_read_page); @@ -7159,10 +7220,9 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (!dpage) return -1; - guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); + len = min_t(size_t, len, rb_read_page_capacity(data_page)); - if (data_page->order != cpu_buffer->reader_page->order) - return -1; + guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); reader = rb_get_reader_page(cpu_buffer); if (!reader) @@ -7177,31 +7237,26 @@ int ring_buffer_read_page(struct trace_buffer *buffer, /* Check if any events were dropped */ missed_events = cpu_buffer->lost_events; - /* - * If this page has been partially read or - * if len is not big enough to read the rest of the page or - * a writer is still on the page, then - * we must copy the data from the page to the buffer. - * Otherwise, we can simply swap the page with the one passed in. - */ + /* + * It is not possible to swap the reader page if: + * - It has been partially read + * - len is not big enough to read it entirely + * - A writer is still on it + * - The ring buffer is static + * - The order doesn't match + */ if (read || (len < (size - read)) || cpu_buffer->reader_page == cpu_buffer->commit_page || - rb_is_static(cpu_buffer)) { + rb_is_static(cpu_buffer) || + data_page->order != reader->order) { struct buffer_data_page *rpage = cpu_buffer->reader_page->page; unsigned int rpos = read; unsigned int pos = 0; unsigned int event_size; unsigned int flags = 0; - /* - * If a full page is expected, this can still be returned - * if there's been a previous partial read and the - * rest of the page can be read and the commit page is off - * the reader page. - */ - if (full && - (!read || (len < (size - read)) || - cpu_buffer->reader_page == cpu_buffer->commit_page)) + /* If a full page is requested, it cannot be the commit page */ + if (full && cpu_buffer->reader_page == cpu_buffer->commit_page) return -1; if (len > (size - read)) @@ -7287,7 +7342,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * missed events, then record it there. */ if (missed_events > 0 && - rb_page_capacity(reader) - size >= sizeof(missed_events)) { + rb_read_page_capacity(data_page) - size >= sizeof(missed_events)) { memcpy(&dpage->data[size], &missed_events, sizeof(missed_events)); local_add(RB_MISSED_STORED, &dpage->commit); @@ -7307,8 +7362,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, /* * This page may be off to user land. Zero it out here. */ - if (size < rb_page_capacity(reader)) - memset(&dpage->data[size], 0, rb_page_capacity(reader) - size); + if (size < rb_read_page_capacity(data_page)) + memset(&dpage->data[size], 0, rb_read_page_capacity(data_page) - size); return read; } @@ -7327,6 +7382,18 @@ void *ring_buffer_read_page_data(struct buffer_data_read_page *page) EXPORT_SYMBOL_GPL(ring_buffer_read_page_data); /** + * ring_buffer_read_page_size - get size of the read page. + * @rpage: the page to get the size from + * + * Returns size of the page in bytes. + */ +unsigned int ring_buffer_read_page_size(struct buffer_data_read_page *rpage) +{ + return rpage ? PAGE_SIZE << rpage->order : 0; +} +EXPORT_SYMBOL_GPL(ring_buffer_read_page_size); + +/** * ring_buffer_subbuf_size_get - get size of the sub buffer. * @buffer: the buffer to get the sub buffer size from * @@ -7380,8 +7447,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) struct ring_buffer_per_cpu *cpu_buffer; struct buffer_page *bpage, *tmp; unsigned int old_capacity; + unsigned long nr_pages; int old_order; - int nr_pages; int psize; int err; int cpu; @@ -7406,12 +7473,20 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) old_capacity = rb_subbuf_capacity(buffer); + /* The mmap fast path reads subbuf_order without buffer->mutex. */ + for_each_buffer_cpu(buffer, cpu) { + if (!cpumask_test_cpu(cpu, buffer->cpumask)) + continue; + if (atomic_read(&buffer->buffers[cpu]->resize_disabled)) + return -EBUSY; + } + atomic_inc(&buffer->record_disabled); /* Make sure all commits have finished */ synchronize_rcu(); - buffer->subbuf_order = order; + WRITE_ONCE(buffer->subbuf_order, order); /* Make sure all new buffers are allocated, before deleting the old ones */ for_each_buffer_cpu(buffer, cpu) { @@ -7515,7 +7590,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) return 0; error: - buffer->subbuf_order = old_order; + WRITE_ONCE(buffer->subbuf_order, old_order); atomic_dec(&buffer->record_disabled); @@ -7563,10 +7638,10 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page **subbuf_ids) { struct trace_buffer_meta *meta = cpu_buffer->meta_page; - unsigned int nr_subbufs = cpu_buffer->nr_pages + 1; + unsigned long nr_subbufs = cpu_buffer->nr_pages + 1; struct buffer_page *first_subbuf, *subbuf; - int cnt = 0; - int id = 0; + unsigned int cnt = 0; + unsigned int id = 0; id = rb_page_id(cpu_buffer, cpu_buffer->reader_page, id); subbuf_ids[id++] = cpu_buffer->reader_page; @@ -7794,6 +7869,9 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu, /* prevent another thread from changing buffer/sub-buffer sizes */ guard(mutex)(&buffer->mutex); + if (cpu_buffer->nr_pages > rb_static_max_pages()) + return -E2BIG; + err = rb_alloc_meta_page(cpu_buffer); if (err) return err; diff --git a/kernel/trace/ring_buffer_benchmark.c b/kernel/trace/ring_buffer_benchmark.c index 593e3b59e42e..c3d34c0e64e2 100644 --- a/kernel/trace/ring_buffer_benchmark.c +++ b/kernel/trace/ring_buffer_benchmark.c @@ -104,7 +104,7 @@ static enum event_status read_event(int cpu) static enum event_status read_page(int cpu) { - struct buffer_data_read_page *bpage; + struct buffer_data_read_page *bpage = NULL; struct ring_buffer_event *event; struct rb_page *rpage; unsigned long commit; @@ -114,8 +114,8 @@ static enum event_status read_page(int cpu) int inc; int i; - bpage = ring_buffer_alloc_read_page(buffer, cpu); - if (IS_ERR(bpage)) + ret = ring_buffer_alloc_read_page(buffer, cpu, &bpage); + if (ret < 0) return EVENT_DROPPED; page_size = ring_buffer_subbuf_size_get(buffer); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a946e0183fd1..e4a490d3d08c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7082,8 +7082,8 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, { struct ftrace_buffer_info *info = filp->private_data; struct trace_iterator *iter = &info->iter; + unsigned int spare_size; void *trace_data; - int page_size; ssize_t ret = 0; ssize_t size; @@ -7093,36 +7093,22 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, if (iter->snapshot && tracer_uses_snapshot(iter->tr->current_trace)) return -EBUSY; - page_size = ring_buffer_subbuf_size_get(iter->array_buffer->buffer); + spare_size = ring_buffer_read_page_size(info->spare); - /* Make sure the spare matches the current sub buffer size */ - if (info->spare) { - if (page_size != info->spare_size) { - ring_buffer_free_read_page(iter->array_buffer->buffer, - info->spare_cpu, info->spare); - info->spare = NULL; - } - } +again: + /* Do we have previous read data to read? */ + if (info->read < spare_size) + goto read; - if (!info->spare) { - info->spare = ring_buffer_alloc_read_page(iter->array_buffer->buffer, - iter->cpu_file); - if (IS_ERR(info->spare)) { - ret = PTR_ERR(info->spare); - info->spare = NULL; - } else { - info->spare_cpu = iter->cpu_file; - info->spare_size = page_size; - } - } - if (!info->spare) + ret = ring_buffer_alloc_read_page(iter->array_buffer->buffer, iter->cpu_file, + &info->spare); + if (ret) return ret; - /* Do we have previous read data to read? */ - if (info->read < page_size) - goto read; + spare_size = ring_buffer_read_page_size(info->spare); + info->read = spare_size; + info->spare_cpu = iter->cpu_file; - again: trace_access_lock(iter->cpu_file); ret = ring_buffer_read_page(iter->array_buffer->buffer, info->spare, @@ -7148,8 +7134,9 @@ ssize_t tracing_buffers_read(struct file *filp, char __user *ubuf, } info->read = 0; + read: - size = page_size - info->read; + size = spare_size - info->read; if (size > count) size = count; trace_data = ring_buffer_read_page_data(info->spare); @@ -7190,26 +7177,24 @@ int tracing_buffers_release(struct inode *inode, struct file *file) __trace_array_put(iter->tr); - if (info->spare) - ring_buffer_free_read_page(iter->array_buffer->buffer, - info->spare_cpu, info->spare); + ring_buffer_free_read_page(iter->array_buffer->buffer, info->spare_cpu, info->spare); kvfree(info); return 0; } struct buffer_ref { - struct trace_buffer *buffer; - void *page; - int cpu; - refcount_t refcount; + struct trace_buffer *buffer; + struct buffer_data_read_page *rpage; + int cpu; + refcount_t refcount; }; static void buffer_ref_release(struct buffer_ref *ref) { if (!refcount_dec_and_test(&ref->refcount)) return; - ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->page); + ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->rpage); kfree(ref); } @@ -7268,25 +7253,15 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, .ops = &buffer_pipe_buf_ops, .spd_release = buffer_spd_release, }; + unsigned int page_size = 0; struct buffer_ref *ref; bool woken = false; - int page_size; int entries, i; ssize_t ret = 0; if (iter->snapshot && tracer_uses_snapshot(iter->tr->current_trace)) return -EBUSY; - page_size = ring_buffer_subbuf_size_get(iter->array_buffer->buffer); - if (*ppos & (page_size - 1)) - return -EINVAL; - - if (len & (page_size - 1)) { - if (len < page_size) - return -EINVAL; - len &= (~(page_size - 1)); - } - if (splice_grow_spd(pipe, &spd)) return -ENOMEM; @@ -7306,25 +7281,39 @@ ssize_t tracing_buffers_splice_read(struct file *file, loff_t *ppos, refcount_set(&ref->refcount, 1); ref->buffer = iter->array_buffer->buffer; - ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file); - if (IS_ERR(ref->page)) { - ret = PTR_ERR(ref->page); - ref->page = NULL; + + ret = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file, &ref->rpage); + if (ret) { kfree(ref); break; } ref->cpu = iter->cpu_file; - r = ring_buffer_read_page(ref->buffer, ref->page, - len, iter->cpu_file, 1); + page_size = ring_buffer_read_page_size(ref->rpage); + + r = -EINVAL; + if (IS_ALIGNED(*ppos, page_size) && len >= page_size) { + r = ring_buffer_read_page(ref->buffer, ref->rpage, len, iter->cpu_file, 1); + } else if (!i) { + /* + * If this fails to read on the first iteration, it + * means the length was too small and an error should + * be returned to user space. Otherwise, at least + * one sub-buffer was successfully read but this failed + * due to either the length was unaligned or the + * subbuf order changed. Either case, do not report + * an error. + */ + ret = -EINVAL; + } + if (r < 0) { - ring_buffer_free_read_page(ref->buffer, ref->cpu, - ref->page); + ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->rpage); kfree(ref); break; } - page = virt_to_page(ring_buffer_read_page_data(ref->page)); + page = virt_to_page(ring_buffer_read_page_data(ref->rpage)); spd.pages[i] = page; spd.partial[i].len = page_size; @@ -7728,12 +7717,55 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +static bool tr_option_match(struct trace_array *tr, void *topt) +{ + for (int i = 0; i < tr->nr_topts; i++) { + struct trace_options *tr_topts = &tr->topts[i]; + + if (topt >= (void *)&tr_topts->topts[0] && + topt < (void *)&tr_topts->topts[tr_topts->nr_topts]) + return true; + } + return false; +} + +/* + * The topt is the address of a trace_array->topts[] element that holds the + * the tracer options descriptor. But since the trace_array reference has not + * been taken yet, it cannot be dereferenced as it could have been freed by + * a rmdir of the instance the trace_array represents. + * + * Search the list of trace_arrays and compare the topt to the address of + * the entire trace_array topts array for each trace_array in the list. + * If one is matched, then take the reference and return it. If not, the + * trace_array no longer exits. + */ +static int trace_array_tracer_options_get(void *topt) +{ + struct trace_array *tr; + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + + if (tracing_disabled) + return -ENODEV; + + guard(mutex)(&trace_types_lock); + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (tr_option_match(tr, topt)) + return __trace_array_get(tr); + } + return -ENODEV; +} + static int tracing_open_options(struct inode *inode, struct file *filp) { struct trace_option_dentry *topt = inode->i_private; int ret; - ret = tracing_check_open_get_tr(topt->tr); + ret = trace_array_tracer_options_get(topt); if (ret) return ret; @@ -7842,11 +7874,70 @@ trace_options_core_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +/* + * The tr_index is the address of a trace_array->trace_flags_index[] + * element that holds the index of the trace flag. But since the + * trace_array reference has not been taken yet, it cannot be referenced + * as it could have been freed by a rmdir of the instance the trace_array + * represents. + * + * Search the list of trace_arrays and compare the tr_index to the + * address of the entire trace_array trace_flags_index array for each + * trace_array in the list. If one is matched, then take the reference + * and return it. If not, the trace_array no longer exits. + */ +static int trace_array_options_get(void *tr_index) +{ + struct trace_array *tr; + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + + if (tracing_disabled) + return -ENODEV; + + guard(mutex)(&trace_types_lock); + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (tr_index >= (void *)&tr->trace_flags_index[0] && + tr_index < (void *)&tr->trace_flags_index[TRACE_FLAGS_MAX_SIZE]) + return __trace_array_get(tr); + } + return -ENODEV; +} + +static int trace_options_open(struct inode *inode, struct file *filp) +{ + void *tr_index = inode->i_private; + + if (trace_array_options_get(tr_index) < 0) + return -ENODEV; + + filp->private_data = tr_index; + + return 0; +} + +static int trace_options_release(struct inode *inode, struct file *filp) +{ + void *tr_index = filp->private_data; + struct trace_array *tr; + unsigned int index; + + get_tr_index(tr_index, &tr, &index); + + trace_array_put(tr); + + return 0; +} + static const struct file_operations trace_options_core_fops = { - .open = tracing_open_generic, - .read = trace_options_core_read, - .write = trace_options_core_write, - .llseek = generic_file_llseek, + .open = trace_options_open, + .read = trace_options_core_read, + .write = trace_options_core_write, + .llseek = generic_file_llseek, + .release = trace_options_release, }; struct dentry *trace_create_file(const char *name, @@ -7936,6 +8027,7 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer, tr->topts = tr_topts; tr->topts[tr->nr_topts].tracer = tracer; tr->topts[tr->nr_topts].topts = topts; + tr->topts[tr->nr_topts].nr_topts = cnt; tr->nr_topts++; for (cnt = 0; opts[cnt].name; cnt++) { @@ -9651,6 +9743,11 @@ __init static void enable_instances(void) if (flag_delim) *flag_delim++ = '\0'; + if (trace_array_find(name)) { + pr_warn("Tracing: Instance %s already exists\n", name); + continue; + } + if (backup) { if (backup_instance_area(backup, &addr, &size) < 0) continue; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 74a7a50d1e78..3749485a7d85 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -227,6 +227,7 @@ struct array_buffer { struct trace_options { struct tracer *tracer; struct trace_option_dentry *topts; + int nr_topts; }; struct trace_pid_list *trace_pid_list_alloc(void); @@ -745,11 +746,10 @@ static inline int tracing_get_cpu(struct inode *inode) void tracing_reset_cpu(struct array_buffer *buf, int cpu); struct ftrace_buffer_info { - struct trace_iterator iter; - void *spare; - unsigned int spare_cpu; - unsigned int spare_size; - unsigned int read; + struct trace_iterator iter; + struct buffer_data_read_page *spare; + unsigned int spare_cpu; + unsigned int read; }; /** @@ -1340,7 +1340,7 @@ extern void clear_ftrace_function_probes(struct trace_array *tr); int register_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd); -void ftrace_create_filter_files(struct ftrace_ops *ops, +void ftrace_create_filter_files(struct trace_array *tr, struct dentry *parent); void ftrace_destroy_filter_files(struct ftrace_ops *ops); @@ -1363,11 +1363,12 @@ static inline void clear_ftrace_function_probes(struct trace_array *tr) { } +static inline void ftrace_create_filter_files(struct trace_array *tr, + struct dentry *parent) { } /* * The ops parameter passed in is usually undefined. * This must be a macro. */ -#define ftrace_create_filter_files(ops, parent) do { } while (0) #define ftrace_destroy_filter_files(ops) do { } while (0) #endif /* CONFIG_FUNCTION_TRACER && CONFIG_DYNAMIC_FTRACE */ @@ -1952,6 +1953,7 @@ struct event_trigger_data { struct enable_trigger_data { struct trace_event_file *file; + struct trace_event_call *call; bool enable; bool hist; }; diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c index 00172f301f25..ee7a04886bf6 100644 --- a/kernel/trace/trace_btf.c +++ b/kernel/trace/trace_btf.c @@ -61,47 +61,50 @@ struct btf_anon_stack { /* * Find a member of data structure/union by name and return it. - * Return NULL if not found, or -EINVAL if parameter is invalid. - * If the member is an member of anonymous union/structure, the offset - * of that anonymous union/structure is stored into @anon_offset. Caller - * can calculate the correct offset from the root data structure by - * adding anon_offset to the member's offset. + * Return NULL if not found, or ERR_PTR(-EINVAL) if parameter is invalid. + * If the member is a member of an anonymous union/structure, the bit offset + * of that anonymous union/structure is stored into @anon_offset. + * If @member_type is non-NULL, the actual containing structure/union type + * of the found member is stored into @member_type. */ const struct btf_member *btf_find_struct_member(struct btf *btf, const struct btf_type *type, const char *member_name, - u32 *anon_offset) + u32 *anon_offset, + const struct btf_type **member_type) { struct btf_anon_stack *anon_stack; const struct btf_member *member; + const struct btf_type *mtype; u32 tid, cur_offset = 0; const char *name; int i, top = 0; + if (!btf_type_is_struct(type)) + return ERR_PTR(-EINVAL); + anon_stack = kzalloc_objs(*anon_stack, BTF_ANON_STACK_MAX); if (!anon_stack) return ERR_PTR(-ENOMEM); retry: - if (!btf_type_is_struct(type)) { - member = ERR_PTR(-EINVAL); - goto out; - } - for_each_member(i, type, member) { if (!member->name_off) { /* Anonymous union/struct: push it for later use */ - if (btf_type_skip_modifiers(btf, member->type, &tid) && + mtype = btf_type_skip_modifiers(btf, member->type, &tid); + if (mtype && btf_type_is_struct(mtype) && top < BTF_ANON_STACK_MAX) { anon_stack[top].tid = tid; - anon_stack[top++].offset = - cur_offset + member->offset; + anon_stack[top++].offset = cur_offset + + __btf_member_bit_offset(type, member); } } else { name = btf_name_by_offset(btf, member->name_off); if (name && !strcmp(member_name, name)) { if (anon_offset) *anon_offset = cur_offset; + if (member_type) + *member_type = type; goto out; } } diff --git a/kernel/trace/trace_btf.h b/kernel/trace/trace_btf.h index 4bc44bc261e6..4bd26bceae23 100644 --- a/kernel/trace/trace_btf.h +++ b/kernel/trace/trace_btf.h @@ -8,4 +8,5 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, const struct btf_member *btf_find_struct_member(struct btf *btf, const struct btf_type *type, const char *member_name, - u32 *anon_offset); + u32 *anon_offset, + const struct btf_type **member_type); diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index 78fa1cbda9ac..998e6390937a 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -930,7 +930,7 @@ static int __trace_eprobe_create(int argc, const char *argv[]) } else ep->filter_str = NULL; - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; ctx->event = ep->event; diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 1d39eaf6a0f7..30c0ddf90887 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1489,6 +1489,8 @@ int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set) /* Put back the colon to allow this to be called again */ if (buf) *(buf - 1) = ':'; + if (mod) + *(mod - 5) = ':'; return ret; } @@ -2736,14 +2738,14 @@ static const struct file_operations ftrace_show_event_filters_fops = { .open = ftrace_event_show_filters_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ftrace_event_release, }; static const struct file_operations ftrace_show_event_triggers_fops = { .open = ftrace_event_show_triggers_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ftrace_event_release, }; static const struct file_operations ftrace_set_event_pid_fops = { @@ -2908,7 +2910,17 @@ ftrace_event_set_open(struct inode *inode, struct file *file) static int ftrace_event_show_filters_open(struct inode *inode, struct file *file) { - return ftrace_event_open(inode, file, &show_show_event_filters_seq_ops); + struct trace_array *tr = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; + + ret = ftrace_event_open(inode, file, &show_show_event_filters_seq_ops); + if (ret < 0) + trace_array_put(tr); + return ret; } /** @@ -2922,7 +2934,17 @@ ftrace_event_show_filters_open(struct inode *inode, struct file *file) static int ftrace_event_show_triggers_open(struct inode *inode, struct file *file) { - return ftrace_event_open(inode, file, &show_show_event_triggers_seq_ops); + struct trace_array *tr = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; + + ret = ftrace_event_open(inode, file, &show_show_event_triggers_seq_ops); + if (ret < 0) + trace_array_put(tr); + return ret; } static int diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 963e0d6b61fd..8af97fd4ee2d 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -169,7 +169,6 @@ struct hist_field { struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; struct hist_trigger_data *hist_data; enum hist_field_fn fn_num; - unsigned int ref; unsigned int size; unsigned int offset; unsigned int is_signed; @@ -1913,16 +1912,8 @@ out: return field_op; } -static void get_hist_field(struct hist_field *hist_field) -{ - hist_field->ref++; -} - static void __destroy_hist_field(struct hist_field *hist_field) { - if (--hist_field->ref > 1) - return; - kfree(hist_field->var.name); kfree(hist_field->name); @@ -1969,8 +1960,6 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, if (!hist_field) return NULL; - hist_field->ref = 1; - hist_field->hist_data = hist_data; if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS) @@ -2223,10 +2212,8 @@ static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data, for (i = 0; i < hist_data->n_var_refs; i++) { ref_field = hist_data->var_refs[i]; if (ref_field->var.idx == var_field->var.idx && - ref_field->var.hist_data == var_field->hist_data) { - get_hist_field(ref_field); + ref_field->var.hist_data == var_field->hist_data) return ref_field; - } } /* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */ if (hist_data->n_var_refs >= TRACING_MAP_VARS_MAX) @@ -2234,7 +2221,7 @@ static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data, ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL); if (ref_field) { if (init_var_ref(ref_field, var_field, system, event_name)) { - destroy_hist_field(ref_field, 0); + __destroy_hist_field(ref_field); return NULL; } @@ -2330,6 +2317,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, struct ftrace_event_field *field = NULL; char *field_name, *modifier, *str; struct trace_array *tr = file->tr; + bool stack_modifier = false; modifier = str = kstrdup(field_str, GFP_KERNEL); if (!modifier) @@ -2352,9 +2340,10 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, *flags |= HIST_FIELD_FL_EXECNAME; else if (strcmp(modifier, "syscall") == 0) *flags |= HIST_FIELD_FL_SYSCALL; - else if (strcmp(modifier, "stacktrace") == 0) + else if (strcmp(modifier, "stacktrace") == 0) { *flags |= HIST_FIELD_FL_STACKTRACE; - else if (strcmp(modifier, "log2") == 0) + stack_modifier = true; + } else if (strcmp(modifier, "log2") == 0) *flags |= HIST_FIELD_FL_LOG2; else if (strcmp(modifier, "usecs") == 0) *flags |= HIST_FIELD_FL_TIMESTAMP_USECS; @@ -2415,6 +2404,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, *flags |= HIST_FIELD_FL_CPU; } else if (field && field->filter_type == FILTER_STACKTRACE) { *flags |= HIST_FIELD_FL_STACKTRACE; + field = NULL; } else if (field && field->filter_type == FILTER_COMM) { *flags |= HIST_FIELD_FL_COMM | HIST_FIELD_FL_STRING; } else { @@ -2425,6 +2415,12 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, } } } + + if (stack_modifier && + (!field || field->filter_type != FILTER_STACKTRACE)) { + hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str)); + field = ERR_PTR(-EINVAL); + } out: kfree(str); @@ -3276,7 +3272,6 @@ static struct hist_field *create_var(struct hist_trigger_data *hist_data, goto out; } - var->ref = 1; var->flags = HIST_FIELD_FL_VAR; var->var.idx = idx; var->var.hist_data = var->hist_data = hist_data; @@ -4313,8 +4308,7 @@ static int __create_val_field(struct hist_trigger_data *hist_data, goto err; } else { /* Value */ - if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT | - HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 | + if (hist_field->flags & (HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 | HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET | HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE)) goto err; @@ -4331,6 +4325,7 @@ static int __create_val_field(struct hist_trigger_data *hist_data, return ret; err: hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str)); + destroy_hist_field(hist_field, 0); return -EINVAL; } @@ -5690,7 +5685,7 @@ static int print_entries(struct seq_file *m, { struct tracing_map_sort_entry **sort_entries = NULL; struct tracing_map *map = hist_data->map; - int i, j, n_entries; + int i, j, n_entries, ret; struct hist_val_stat *stats = NULL; u64 val; @@ -5700,6 +5695,8 @@ static int print_entries(struct seq_file *m, if (n_entries < 0) return n_entries; + ret = n_entries; + /* Calculate the max and the total for each field if needed. */ for (j = 0; j < hist_data->n_vals; j++) { if (!(hist_data->fields[j]->flags & @@ -5708,7 +5705,7 @@ static int print_entries(struct seq_file *m, if (!stats) { stats = kzalloc_objs(*stats, hist_data->n_vals); if (!stats) { - n_entries = -ENOMEM; + ret = -ENOMEM; goto out; } } @@ -5729,7 +5726,7 @@ static int print_entries(struct seq_file *m, out: tracing_map_destroy_sort_entries(sort_entries, n_entries); - return n_entries; + return ret; } static void hist_trigger_show(struct seq_file *m, @@ -6383,17 +6380,18 @@ static int event_hist_trigger_named_init(struct event_trigger_data *data) { int ret; - data->ref++; - - save_named_trigger(data->named_data->name, data); - ret = event_hist_trigger_init(data->named_data); if (ret < 0) { kfree(data->cmd_ops); data->cmd_ops = &trigger_hist_cmd; + return ret; } - return ret; + data->ref++; + + save_named_trigger(data->named_data->name, data); + + return 0; } static void event_hist_trigger_named_free(struct event_trigger_data *data) @@ -6643,12 +6641,6 @@ static int hist_register_trigger(char *glob, data->cmd_ops = cmd_ops; } - if (data->cmd_ops->init) { - ret = data->cmd_ops->init(data); - if (ret < 0) - goto out; - } - if (hist_data->enable_timestamps) { char *clock = hist_data->attrs->clock; @@ -6661,6 +6653,15 @@ static int hist_register_trigger(char *glob, tracing_set_filter_buffering(file->tr, true); } + if (data->cmd_ops->init) { + ret = data->cmd_ops->init(data); + if (ret < 0) { + if (hist_data->enable_timestamps) + tracing_set_filter_buffering(file->tr, false); + goto out; + } + } + if (named_data) { remove_hist_vars(hist_data); destroy_hist_data(hist_data); @@ -6678,11 +6679,12 @@ static int hist_trigger_enable(struct event_trigger_data *data, update_cond_flag(file); - if (trace_event_trigger_enable_disable(file, 1) < 0) { - list_del_rcu(&data->list); - update_cond_flag(file); + /* + * On failure the caller undoes the registration, and + * hist_unregister_trigger() can only find the trigger here. + */ + if (trace_event_trigger_enable_disable(file, 1) < 0) ret--; - } return ret; } @@ -6760,13 +6762,13 @@ static void hist_unregister_trigger(char *glob, } } - if (test && test->cmd_ops->free) - test->cmd_ops->free(test); - if (hist_data->enable_timestamps) { if (!hist_data->remove || test) tracing_set_filter_buffering(file->tr, false); } + + if (test && test->cmd_ops->free) + test->cmd_ops->free(test); } static bool hist_file_check_refs(struct trace_event_file *file) @@ -6971,6 +6973,8 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops, return ret; out_unreg: event_trigger_unregister(cmd_ops, file, glob+1, trigger_data); + /* The unregister frees trigger_data, skip out_free */ + goto out; out_free: remove_hist_vars(hist_data); diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index 149300cc5e8a..4d2fde996c0f 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -1728,7 +1728,8 @@ static void enable_trigger_private_data_free(struct event_trigger_data *data) { struct enable_trigger_data *enable_data = data->private_data; - trace_event_put_ref(enable_data->file->event_call); + /* The file may already be freed here, only the call is kept alive */ + trace_event_put_ref(enable_data->call); kfree(enable_data); } @@ -1801,6 +1802,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops, enable_data->hist = hist; enable_data->enable = enable; enable_data->file = event_enable_file; + enable_data->call = event_enable_file->event_call; trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data); if (!trigger_data) diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index 93cda2f6f269..f658c3a77aa7 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -1122,10 +1122,9 @@ static void user_event_destroy_validators(struct user_event *user) } } -static void user_event_destroy_fields(struct user_event *user) +static void user_event_destroy_fields(struct list_head *head) { struct ftrace_event_field *field, *next; - struct list_head *head = &user->fields; list_for_each_entry_safe(field, next, head, link) { list_del(&field->link); @@ -1502,17 +1501,32 @@ static int user_event_set_call_visible(struct user_event *user, bool visible) static int destroy_user_event(struct user_event *user) { + LIST_HEAD(fields); int ret = 0; lockdep_assert_held(&event_mutex); - /* Must destroy fields before call removal */ - user_event_destroy_fields(user); + /* + * Detach the fields before removing the call. Removing the event + * frees the field list memory (trace_destroy_fields() is run on + * successful removal and kmem_cache_free()s the fields), but the + * fields here are allocated and owned by user_events. Destroy + * them separately once removal has succeeded. + */ + list_splice_init(&user->fields, &fields); ret = user_event_set_call_visible(user, false); - if (ret) + if (ret) { + /* + * Removal failed and the event stays registered, recover + * the fields so it is left in a consistent state. + */ + list_splice(&fields, &user->fields); return ret; + } + + user_event_destroy_fields(&fields); dyn_event_remove(&user->devent); hash_del(&user->node); @@ -2212,7 +2226,7 @@ static int user_event_parse(struct user_event_group *group, char *name, put_user_lock: mutex_unlock(&event_mutex); put_user: - user_event_destroy_fields(user); + user_event_destroy_fields(&user->fields); user_event_destroy_validators(user); kfree(user->call.print_fmt); diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index cd37f2013758..c879d43a5fbb 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -101,7 +101,7 @@ int ftrace_create_function_files(struct trace_array *tr, return ret; } - ftrace_create_filter_files(tr->ops, parent); + ftrace_create_filter_files(tr, parent); return 0; } diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index ff7cb1a76b95..c5befd0c5b9a 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -52,6 +52,7 @@ struct fgraph_data { }; struct ftrace_graph_ret_entry ret; int failed; + int ent_size; int cpu; }; @@ -1274,6 +1275,7 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, if (s->full) { data->failed = 1; data->cpu = cpu; + data->ent_size = iter->ent_size; } else data->failed = 0; } @@ -1457,6 +1459,7 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) if (data && data->failed) { field = &data->ent.ent; iter->cpu = data->cpu; + iter->ent_size = data->ent_size; ret = print_graph_entry(field, s, iter, flags); if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) { per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1; diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index c4163904ba74..804442b2f7d2 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -625,6 +625,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty { const struct btf_type *type = *ptype; const struct btf_member *field; + const struct btf_type *mtype; struct btf *btf = ctx_btf(ctx); char *fieldname = *pfieldname; int bitoffs = 0; @@ -640,7 +641,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty anon_offs = 0; field = btf_find_struct_member(btf, type, fieldname, - &anon_offs); + &anon_offs, &mtype); if (IS_ERR(field)) { trace_probe_log_err(ctx->offset, BAD_BTF_TID); return PTR_ERR(field); @@ -653,7 +654,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty bitoffs += anon_offs; /* Accumulate the bit-offsets of the dot-connected fields */ - if (btf_type_kflag(type)) { + if (btf_type_kflag(mtype)) { bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset); ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset); } else { @@ -661,11 +662,11 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty ctx->last_bitsize = 0; } - type = btf_type_skip_modifiers(btf, field->type, NULL); - if (!type) { - trace_probe_log_err(ctx->offset, BAD_BTF_TID); - return -EINVAL; - } + type = btf_type_skip_modifiers(btf, field->type, NULL); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } if (next) ctx->offset += next - fieldname; @@ -2552,19 +2553,60 @@ int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype int traceprobe_define_arg_fields(struct trace_event_call *event_call, size_t offset, struct trace_probe *tp) { + struct trace_probe_event *tpe = trace_probe_event_from_call(event_call); int ret, i; + /* + * A field created by trace_define_field() only stores the name and + * type pointers, it does not copy the strings. Here they point into + * the probe_arg of @tp, which is freed when @tp is removed. For an + * event with multiple probes attached, the field list is defined + * once by the first probe but kept alive by the surviving siblings, + * so removing that first probe would leave the fields referencing + * freed memory. Duplicate the strings and anchor the copies on the + * trace_probe_event, which lives as long as the field list itself. + * + * event_define_fields() ignores the return value of this hook, so + * if a previous attempt failed before creating any field, it may + * call here again. Release duplicates left behind by such an + * attempt before starting over. + */ + for (i = 0; i < tpe->nr_field_strings; i++) + kfree(tpe->field_strings[i]); + kfree(tpe->field_strings); + tpe->field_strings = NULL; + tpe->nr_field_strings = 0; + + if (tp->nr_args) { + tpe->field_strings = kcalloc(tp->nr_args * 2, sizeof(char *), + GFP_KERNEL); + if (!tpe->field_strings) + return -ENOMEM; + } + /* Set argument names as fields */ for (i = 0; i < tp->nr_args; i++) { struct probe_arg *parg = &tp->args[i]; const char *fmt = parg->type->fmttype; int size = parg->type->size; + char *name, *type; if (parg->fmt) fmt = parg->fmt; if (parg->count) size *= parg->count; - ret = trace_define_field(event_call, fmt, parg->name, + + name = kstrdup(parg->name, GFP_KERNEL); + type = kstrdup(fmt, GFP_KERNEL); + if (!name || !type) { + kfree(name); + kfree(type); + return -ENOMEM; + } + tpe->field_strings[tpe->nr_field_strings++] = name; + tpe->field_strings[tpe->nr_field_strings++] = type; + + ret = trace_define_field(event_call, type, name, offset + parg->offset, size, parg->type->is_signed, FILTER_OTHER); @@ -2576,6 +2618,11 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call, static void trace_probe_event_free(struct trace_probe_event *tpe) { + int i; + + for (i = 0; i < tpe->nr_field_strings; i++) + kfree(tpe->field_strings[i]); + kfree(tpe->field_strings); kfree(tpe->class.system); kfree(tpe->call.name); kfree(tpe->call.print_fmt); diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index fba1af092a9b..d1fb3520700f 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -264,6 +264,8 @@ struct trace_probe_event { struct trace_event_call call; struct list_head files; struct list_head probes; + char **field_strings; + int nr_field_strings; struct trace_uprobe_filter filter[]; }; diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c index e6724f947170..2d5bc423efca 100644 --- a/kernel/trace/trace_remote.c +++ b/kernel/trace/trace_remote.c @@ -251,8 +251,8 @@ static int trace_remote_get(struct trace_remote *remote, int cpu) if (cpu != RING_BUFFER_ALL_CPUS && !remote->pcpu_reader_locks) { int lock_cpu; - remote->pcpu_reader_locks = kcalloc(nr_cpu_ids, sizeof(*remote->pcpu_reader_locks), - GFP_KERNEL); + remote->pcpu_reader_locks = kzalloc_objs(*remote->pcpu_reader_locks, + nr_cpu_ids); if (!remote->pcpu_reader_locks) { trace_remote_try_unload(remote); return -ENOMEM; @@ -324,7 +324,7 @@ static int __alloc_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu) return iter->rb_iter ? 0 : -ENOMEM; } - iter->rb_iters = kcalloc(nr_cpu_ids, sizeof(*iter->rb_iters), GFP_KERNEL); + iter->rb_iters = kzalloc_objs(*iter->rb_iters, nr_cpu_ids); if (!iter->rb_iters) return -ENOMEM; @@ -980,9 +980,12 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, const struct cpumask *cpumask) { size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask)); - unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; struct ring_buffer_desc *rb_desc; int cpu, ret = -ENOMEM; + unsigned int nr_pages; + + if (min_desc_size == SIZE_MAX) + return -E2BIG; if (desc_size < min_desc_size) return -EINVAL; @@ -991,6 +994,7 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, desc->struct_len = min_desc_size; rb_desc = __first_ring_buffer_desc(desc); + nr_pages = __calc_nr_pages_ring_buffer_desc(buffer_size); for_each_cpu(cpu, cpumask) { unsigned int id; @@ -1204,7 +1208,7 @@ remote_events_dir_header_page_read(struct file *filp, char __user *ubuf, size_t struct trace_seq *s; int ret; - s = kmalloc(sizeof(*s), GFP_KERNEL); + s = kmalloc_obj(*s); if (!s) return -ENOMEM; @@ -1227,7 +1231,7 @@ remote_events_dir_header_event_read(struct file *filp, char __user *ubuf, size_t struct trace_seq *s; int ret; - s = kmalloc(sizeof(*s), GFP_KERNEL); + s = kmalloc_obj(*s); if (!s) return -ENOMEM; diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 0aa2514a6593..e7f4e523587d 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -499,7 +499,7 @@ stack_trace_filter_open(struct inode *inode, struct file *file) struct ftrace_ops *ops = inode->i_private; /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, FTRACE_ITER_FILTER, + return ftrace_regex_open(NULL, ops, FTRACE_ITER_FILTER, inode, file); } diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 861d857adadb..22cc3c8181b8 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -368,7 +368,7 @@ error: static void free_trace_uprobe(struct trace_uprobe *tu) { - if (!tu) + if (IS_ERR_OR_NULL(tu)) return; path_put(&tu->path); @@ -533,7 +533,7 @@ static int register_trace_uprobe(struct trace_uprobe *tu) return ret; } -DEFINE_FREE(free_trace_uprobe, struct trace_uprobe *, if (_T) free_trace_uprobe(_T)) +DEFINE_FREE(free_trace_uprobe, struct trace_uprobe *, free_trace_uprobe(_T)) /* * Argument syntax: diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 3c034cbc5bb3..1ae3732a2c51 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -3197,7 +3197,16 @@ restart: #ifdef CONFIG_PREEMPT_RT static void worker_lock_callback(struct worker_pool *pool) { - spin_lock(&pool->cb_lock); + /* + * SINGLE_DEPTH_NESTING is for a dead pool's bh_worker() running from + * drain_dead_softirq_workfn() inside a live pool's bh_worker(). The + * unlocked read is stable: the flag is only set while @pool's CPU is + * dead, inside a serialized hotplug operation. data_race() as the value + * only affects the lockdep annotation and the read can be elided when + * lockdep is disabled. + */ + spin_lock_nested(&pool->cb_lock, + data_race(pool->flags) & POOL_BH_DRAINING ? SINGLE_DEPTH_NESTING : 0); } static void worker_unlock_callback(struct worker_pool *pool) @@ -5285,12 +5294,6 @@ static void pwq_release_workfn(struct kthread_work *work) mutex_unlock(&wq->mutex); } - if (!is_percpu_pool(pool)) { - mutex_lock(&wq_pool_mutex); - put_unbound_pool(pool); - mutex_unlock(&wq_pool_mutex); - } - if (!list_empty(&pwq->pending_node)) { struct wq_node_nr_active *nna = wq_node_nr_active(pwq->wq, pwq->pool->node); @@ -5300,6 +5303,12 @@ static void pwq_release_workfn(struct kthread_work *work) raw_spin_unlock_irq(&nna->lock); } + if (!is_percpu_pool(pool)) { + mutex_lock(&wq_pool_mutex); + put_unbound_pool(pool); + mutex_unlock(&wq_pool_mutex); + } + kfree_rcu(pwq, rcu); /* @@ -8050,6 +8059,9 @@ static int wq_watchdog_param_set_thresh(const char *val, if (ret) return ret; + if (thresh > MAX_JIFFY_OFFSET / HZ) + return -ERANGE; + if (system_percpu_wq) wq_watchdog_set_thresh(thresh); else @@ -8080,12 +8092,12 @@ static inline void wq_watchdog_init(void) { } static void bh_pool_kick_normal(struct irq_work *irq_work) { - raise_softirq_irqoff(TASKLET_SOFTIRQ); + raise_softirq(TASKLET_SOFTIRQ); } static void bh_pool_kick_highpri(struct irq_work *irq_work) { - raise_softirq_irqoff(HI_SOFTIRQ); + raise_softirq(HI_SOFTIRQ); } static void __init restrict_unbound_cpumask(const char *name, const struct cpumask *mask) diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c deleted file mode 100644 index e5b218176c5a..000000000000 --- a/lib/alloc_tag.c +++ /dev/null @@ -1,1029 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include <linux/alloc_tag.h> -#include <linux/execmem.h> -#include <linux/fs.h> -#include <linux/gfp.h> -#include <linux/kallsyms.h> -#include <linux/module.h> -#include <linux/page_ext.h> -#include <linux/pgalloc_tag.h> -#include <linux/proc_fs.h> -#include <linux/rcupdate.h> -#include <linux/seq_buf.h> -#include <linux/seq_file.h> -#include <linux/string_choices.h> -#include <linux/vmalloc.h> -#include <linux/kmemleak.h> - -#define ALLOCINFO_FILE_NAME "allocinfo" -#define MODULE_ALLOC_TAG_VMAP_SIZE (100000UL * sizeof(struct alloc_tag)) -#define SECTION_START(NAME) (CODETAG_SECTION_START_PREFIX NAME) -#define SECTION_STOP(NAME) (CODETAG_SECTION_STOP_PREFIX NAME) - -#ifdef CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT -static bool mem_profiling_support = true; -#else -static bool mem_profiling_support; -#endif - -/* - * Memory allocation profiling is permanently disabled and cannot be enabled. - * Must be called after setup_early_mem_profiling(). - */ -bool mem_alloc_profiling_permanently_disabled(void) -{ - return !mem_profiling_support; -} - -static struct codetag_type *alloc_tag_cttype; - -#ifdef CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU -DEFINE_PER_CPU(struct alloc_tag_counters, _shared_alloc_tag); -EXPORT_SYMBOL(_shared_alloc_tag); -#endif - -DEFINE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT, - mem_alloc_profiling_key); -EXPORT_SYMBOL(mem_alloc_profiling_key); - -DEFINE_STATIC_KEY_FALSE(mem_profiling_compressed); - -struct alloc_tag_kernel_section kernel_tags = { NULL, 0 }; -unsigned long alloc_tag_ref_mask; -int alloc_tag_ref_offs; - -struct allocinfo_private { - struct codetag_iterator iter; - struct codetag_iterator reported_iter; - bool print_header; -}; - -static void *allocinfo_start(struct seq_file *m, loff_t *pos) -{ - struct allocinfo_private *priv; - loff_t node = *pos; - - priv = (struct allocinfo_private *)m->private; - codetag_lock_module_list(alloc_tag_cttype); - if (node == 0) { - priv->print_header = true; - priv->iter = codetag_get_ct_iter(alloc_tag_cttype); - } else { - priv->iter = priv->reported_iter; - } - codetag_next_ct(&priv->iter); - return priv->iter.ct ? priv : NULL; -} - -static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos) -{ - struct allocinfo_private *priv = (struct allocinfo_private *)arg; - struct codetag *ct; - - priv->reported_iter = priv->iter; - ct = codetag_next_ct(&priv->iter); - (*pos)++; - if (!ct) - return NULL; - - return priv; -} - -static void allocinfo_stop(struct seq_file *m, void *arg) -{ - codetag_unlock_module_list(alloc_tag_cttype); -} - -static void print_allocinfo_header(struct seq_buf *buf) -{ - /* Output format version, so we can change it. */ - seq_buf_printf(buf, "allocinfo - version: 2.0\n"); - seq_buf_printf(buf, "# <size> <calls> <tag info>\n"); -} - -static void alloc_tag_to_text(struct seq_buf *out, struct codetag *ct) -{ - struct alloc_tag *tag = ct_to_alloc_tag(ct); - struct alloc_tag_counters counter = alloc_tag_read(tag); - s64 bytes = counter.bytes; - - seq_buf_printf(out, "%12lli %8llu ", bytes, counter.calls); - codetag_to_text(out, ct); - if (unlikely(alloc_tag_is_inaccurate(tag))) - seq_buf_printf(out, " accurate:no"); - seq_buf_putc(out, ' '); - seq_buf_putc(out, '\n'); -} - -static int allocinfo_show(struct seq_file *m, void *arg) -{ - struct allocinfo_private *priv = (struct allocinfo_private *)arg; - char *bufp; - size_t n = seq_get_buf(m, &bufp); - struct seq_buf buf; - - seq_buf_init(&buf, bufp, n); - if (priv->print_header) { - print_allocinfo_header(&buf); - priv->print_header = false; - } - alloc_tag_to_text(&buf, priv->iter.ct); - seq_commit(m, seq_buf_used(&buf)); - return 0; -} - -static const struct seq_operations allocinfo_seq_op = { - .start = allocinfo_start, - .next = allocinfo_next, - .stop = allocinfo_stop, - .show = allocinfo_show, -}; - -size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep) -{ - struct codetag_iterator iter; - struct codetag *ct; - struct codetag_bytes n; - unsigned int i, nr = 0; - - if (IS_ERR_OR_NULL(alloc_tag_cttype)) - return 0; - - if (can_sleep) - codetag_lock_module_list(alloc_tag_cttype); - else if (!codetag_trylock_module_list(alloc_tag_cttype)) - return 0; - - iter = codetag_get_ct_iter(alloc_tag_cttype); - while ((ct = codetag_next_ct(&iter))) { - struct alloc_tag_counters counter = alloc_tag_read(ct_to_alloc_tag(ct)); - - n.ct = ct; - n.bytes = counter.bytes; - - for (i = 0; i < nr; i++) - if (n.bytes > tags[i].bytes) - break; - - if (i < count) { - nr -= nr == count; - memmove(&tags[i + 1], - &tags[i], - sizeof(tags[0]) * (nr - i)); - nr++; - tags[i] = n; - } - } - - codetag_unlock_module_list(alloc_tag_cttype); - - return nr; -} - -void pgalloc_tag_split(struct folio *folio, int old_order, int new_order) -{ - int i; - struct alloc_tag *tag; - unsigned int nr_pages = 1 << new_order; - - if (!mem_alloc_profiling_enabled()) - return; - - tag = __pgalloc_tag_get(&folio->page); - if (!tag) - return; - - for (i = nr_pages; i < (1 << old_order); i += nr_pages) { - union pgtag_ref_handle handle; - union codetag_ref ref; - - if (get_page_tag_ref(folio_page(folio, i), &ref, &handle)) { - /* Set new reference to point to the original tag */ - alloc_tag_ref_set(&ref, tag); - update_page_tag_ref(handle, &ref); - put_page_tag_ref(handle); - } - } -} - -void pgalloc_tag_swap(struct folio *new, struct folio *old) -{ - union pgtag_ref_handle handle_old, handle_new; - union codetag_ref ref_old, ref_new; - struct alloc_tag *tag_old, *tag_new; - - if (!mem_alloc_profiling_enabled()) - return; - - tag_old = __pgalloc_tag_get(&old->page); - if (!tag_old) - return; - tag_new = __pgalloc_tag_get(&new->page); - if (!tag_new) - return; - - if (!get_page_tag_ref(&old->page, &ref_old, &handle_old)) - return; - if (!get_page_tag_ref(&new->page, &ref_new, &handle_new)) { - put_page_tag_ref(handle_old); - return; - } - - /* - * Clear tag references to avoid debug warning when using - * __alloc_tag_ref_set() with non-empty reference. - */ - set_codetag_empty(&ref_old); - set_codetag_empty(&ref_new); - - /* swap tags */ - __alloc_tag_ref_set(&ref_old, tag_new); - update_page_tag_ref(handle_old, &ref_old); - __alloc_tag_ref_set(&ref_new, tag_old); - update_page_tag_ref(handle_new, &ref_new); - - put_page_tag_ref(handle_old); - put_page_tag_ref(handle_new); -} - -static void shutdown_mem_profiling(bool remove_file) -{ - if (mem_alloc_profiling_enabled()) - static_branch_disable(&mem_alloc_profiling_key); - - if (!mem_profiling_support) - return; - - if (remove_file) - remove_proc_entry(ALLOCINFO_FILE_NAME, NULL); - mem_profiling_support = false; -} - -void __init alloc_tag_sec_init(void) -{ - struct alloc_tag *last_codetag; - - if (!mem_profiling_support) - return; - - if (!static_key_enabled(&mem_profiling_compressed)) - return; - - kernel_tags.first_tag = (struct alloc_tag *)kallsyms_lookup_name( - SECTION_START(ALLOC_TAG_SECTION_NAME)); - last_codetag = (struct alloc_tag *)kallsyms_lookup_name( - SECTION_STOP(ALLOC_TAG_SECTION_NAME)); - kernel_tags.count = last_codetag - kernel_tags.first_tag; - - /* Check if kernel tags fit into page flags */ - if (kernel_tags.count > (1UL << NR_UNUSED_PAGEFLAG_BITS)) { - shutdown_mem_profiling(false); /* allocinfo file does not exist yet */ - pr_err("%lu allocation tags cannot be references using %d available page flag bits. Memory allocation profiling is disabled!\n", - kernel_tags.count, NR_UNUSED_PAGEFLAG_BITS); - return; - } - - alloc_tag_ref_offs = (LRU_REFS_PGOFF - NR_UNUSED_PAGEFLAG_BITS); - alloc_tag_ref_mask = ((1UL << NR_UNUSED_PAGEFLAG_BITS) - 1); - pr_debug("Memory allocation profiling compression is using %d page flag bits!\n", - NR_UNUSED_PAGEFLAG_BITS); -} - -#ifdef CONFIG_MODULES - -static struct maple_tree mod_area_mt = MTREE_INIT(mod_area_mt, MT_FLAGS_ALLOC_RANGE); -static struct vm_struct *vm_module_tags; -/* A dummy object used to indicate an unloaded module */ -static struct module unloaded_mod; -/* A dummy object used to indicate a module prepended area */ -static struct module prepend_mod; - -struct alloc_tag_module_section module_tags; - -static inline unsigned long alloc_tag_align(unsigned long val) -{ - if (!static_key_enabled(&mem_profiling_compressed)) { - /* No alignment requirements when we are not indexing the tags */ - return val; - } - - if (val % sizeof(struct alloc_tag) == 0) - return val; - return ((val / sizeof(struct alloc_tag)) + 1) * sizeof(struct alloc_tag); -} - -static bool ensure_alignment(unsigned long align, unsigned int *prepend) -{ - if (!static_key_enabled(&mem_profiling_compressed)) { - /* No alignment requirements when we are not indexing the tags */ - return true; - } - - /* - * If alloc_tag size is not a multiple of required alignment, tag - * indexing does not work. - */ - if (!IS_ALIGNED(sizeof(struct alloc_tag), align)) - return false; - - /* Ensure prepend consumes multiple of alloc_tag-sized blocks */ - if (*prepend) - *prepend = alloc_tag_align(*prepend); - - return true; -} - -static inline bool tags_addressable(void) -{ - unsigned long tag_idx_count; - - if (!static_key_enabled(&mem_profiling_compressed)) - return true; /* with page_ext tags are always addressable */ - - tag_idx_count = CODETAG_ID_FIRST + kernel_tags.count + - module_tags.size / sizeof(struct alloc_tag); - - return tag_idx_count < (1UL << NR_UNUSED_PAGEFLAG_BITS); -} - -static bool needs_section_mem(struct module *mod, unsigned long size) -{ - if (!mem_profiling_support) - return false; - - return size >= sizeof(struct alloc_tag); -} - -static bool clean_unused_counters(struct alloc_tag *start_tag, - struct alloc_tag *end_tag) -{ - struct alloc_tag *tag; - bool ret = true; - - for (tag = start_tag; tag <= end_tag; tag++) { - struct alloc_tag_counters counter; - - if (!tag->counters) - continue; - - counter = alloc_tag_read(tag); - if (!counter.bytes) { - free_percpu(tag->counters); - tag->counters = NULL; - } else { - ret = false; - } - } - - return ret; -} - -/* Called with mod_area_mt locked */ -static void clean_unused_module_areas_locked(void) -{ - MA_STATE(mas, &mod_area_mt, 0, module_tags.size); - struct module *val; - - mas_for_each(&mas, val, module_tags.size) { - struct alloc_tag *start_tag; - struct alloc_tag *end_tag; - - if (val != &unloaded_mod) - continue; - - /* Release area if all tags are unused */ - start_tag = (struct alloc_tag *)(module_tags.start_addr + mas.index); - end_tag = (struct alloc_tag *)(module_tags.start_addr + mas.last); - if (clean_unused_counters(start_tag, end_tag)) - mas_erase(&mas); - } -} - -/* Called with mod_area_mt locked */ -static bool find_aligned_area(struct ma_state *mas, unsigned long section_size, - unsigned long size, unsigned int prepend, unsigned long align) -{ - bool cleanup_done = false; - -repeat: - /* Try finding exact size and hope the start is aligned */ - if (!mas_empty_area(mas, 0, section_size - 1, prepend + size)) { - if (IS_ALIGNED(mas->index + prepend, align)) - return true; - - /* Try finding larger area to align later */ - mas_reset(mas); - if (!mas_empty_area(mas, 0, section_size - 1, - size + prepend + align - 1)) - return true; - } - - /* No free area, try cleanup stale data and repeat the search once */ - if (!cleanup_done) { - clean_unused_module_areas_locked(); - cleanup_done = true; - mas_reset(mas); - goto repeat; - } - - return false; -} - -static int vm_module_tags_populate(void) -{ - unsigned long phys_end = ALIGN_DOWN(module_tags.start_addr, PAGE_SIZE) + - (vm_module_tags->nr_pages << PAGE_SHIFT); - unsigned long new_end = module_tags.start_addr + module_tags.size; - - if (phys_end < new_end) { - struct page **next_page = vm_module_tags->pages + vm_module_tags->nr_pages; - unsigned long old_shadow_end = ALIGN(phys_end, MODULE_ALIGN); - unsigned long new_shadow_end = ALIGN(new_end, MODULE_ALIGN); - unsigned long more_pages; - unsigned long nr = 0; - - more_pages = ALIGN(new_end - phys_end, PAGE_SIZE) >> PAGE_SHIFT; - while (nr < more_pages) { - unsigned long allocated; - - allocated = alloc_pages_bulk_node(GFP_KERNEL | __GFP_NOWARN, - NUMA_NO_NODE, more_pages - nr, next_page + nr); - - if (!allocated) - break; - nr += allocated; - } - - if (nr < more_pages || - vmap_pages_range(phys_end, phys_end + (nr << PAGE_SHIFT), PAGE_KERNEL, - next_page, PAGE_SHIFT) < 0) { - release_pages_arg arg = { .pages = next_page }; - - /* Clean up and error out */ - release_pages(arg, nr); - return -ENOMEM; - } - - vm_module_tags->nr_pages += nr; - - /* - * Kasan allocates 1 byte of shadow for every 8 bytes of data. - * When kasan_alloc_module_shadow allocates shadow memory, - * its unit of allocation is a page. - * Therefore, here we need to align to MODULE_ALIGN. - */ - if (old_shadow_end < new_shadow_end) - kasan_alloc_module_shadow((void *)old_shadow_end, - new_shadow_end - old_shadow_end, - GFP_KERNEL); - } - - /* - * Mark the pages as accessible, now that they are mapped. - * With hardware tag-based KASAN, marking is skipped for - * non-VM_ALLOC mappings, see __kasan_unpoison_vmalloc(). - */ - kasan_unpoison_vmalloc((void *)module_tags.start_addr, - new_end - module_tags.start_addr, - KASAN_VMALLOC_PROT_NORMAL); - - return 0; -} - -static void *reserve_module_tags(struct module *mod, unsigned long size, - unsigned int prepend, unsigned long align) -{ - unsigned long section_size = module_tags.end_addr - module_tags.start_addr; - MA_STATE(mas, &mod_area_mt, 0, section_size - 1); - unsigned long offset; - void *ret = NULL; - - /* If no tags return error */ - if (size < sizeof(struct alloc_tag)) - return ERR_PTR(-EINVAL); - - /* - * align is always power of 2, so we can use IS_ALIGNED and ALIGN. - * align 0 or 1 means no alignment, to simplify set to 1. - */ - if (!align) - align = 1; - - if (!ensure_alignment(align, &prepend)) { - shutdown_mem_profiling(true); - pr_err("%s: alignment %lu is incompatible with allocation tag indexing. Memory allocation profiling is disabled!\n", - mod->name, align); - return ERR_PTR(-EINVAL); - } - - mas_lock(&mas); - if (!find_aligned_area(&mas, section_size, size, prepend, align)) { - ret = ERR_PTR(-ENOMEM); - goto unlock; - } - - /* Mark found area as reserved */ - offset = mas.index; - offset += prepend; - offset = ALIGN(offset, align); - if (offset != mas.index) { - unsigned long pad_start = mas.index; - - mas.last = offset - 1; - mas_store(&mas, &prepend_mod); - if (mas_is_err(&mas)) { - ret = ERR_PTR(xa_err(mas.node)); - goto unlock; - } - mas.index = offset; - mas.last = offset + size - 1; - mas_store(&mas, mod); - if (mas_is_err(&mas)) { - mas.index = pad_start; - mas_erase(&mas); - ret = ERR_PTR(xa_err(mas.node)); - } - } else { - mas.last = offset + size - 1; - mas_store(&mas, mod); - if (mas_is_err(&mas)) - ret = ERR_PTR(xa_err(mas.node)); - } -unlock: - mas_unlock(&mas); - - if (IS_ERR(ret)) - return ret; - - if (module_tags.size < offset + size) { - int grow_res; - - module_tags.size = offset + size; - if (mem_alloc_profiling_enabled() && !tags_addressable()) { - shutdown_mem_profiling(true); - pr_warn("With module %s there are too many tags to fit in %d page flag bits. Memory allocation profiling is disabled!\n", - mod->name, NR_UNUSED_PAGEFLAG_BITS); - } - - grow_res = vm_module_tags_populate(); - if (grow_res) { - shutdown_mem_profiling(true); - pr_err("Failed to allocate memory for allocation tags in the module %s. Memory allocation profiling is disabled!\n", - mod->name); - return ERR_PTR(grow_res); - } - } - - return (struct alloc_tag *)(module_tags.start_addr + offset); -} - -static void release_module_tags(struct module *mod, bool used) -{ - MA_STATE(mas, &mod_area_mt, module_tags.size, module_tags.size); - struct alloc_tag *start_tag; - struct alloc_tag *end_tag; - struct module *val; - - mas_lock(&mas); - mas_for_each_rev(&mas, val, 0) - if (val == mod) - break; - - if (!val) /* module not found */ - goto out; - - if (!used) - goto release_area; - - start_tag = (struct alloc_tag *)(module_tags.start_addr + mas.index); - end_tag = (struct alloc_tag *)(module_tags.start_addr + mas.last); - if (!clean_unused_counters(start_tag, end_tag)) { - struct alloc_tag *tag; - - for (tag = start_tag; tag <= end_tag; tag++) { - struct alloc_tag_counters counter; - - if (!tag->counters) - continue; - - counter = alloc_tag_read(tag); - pr_info("%s:%u module %s func:%s has %llu allocated at module unload\n", - tag->ct.filename, tag->ct.lineno, tag->ct.modname, - tag->ct.function, counter.bytes); - } - } else { - used = false; - } -release_area: - mas_store(&mas, used ? &unloaded_mod : NULL); - val = mas_prev_range(&mas, 0); - if (val == &prepend_mod) - mas_store(&mas, NULL); -out: - mas_unlock(&mas); -} - -static int load_module(struct module *mod, struct codetag *start, struct codetag *stop) -{ - /* Allocate module alloc_tag percpu counters */ - struct alloc_tag *start_tag; - struct alloc_tag *stop_tag; - struct alloc_tag *tag; - - /* percpu counters for core allocations are already statically allocated */ - if (!mod) - return 0; - - start_tag = ct_to_alloc_tag(start); - stop_tag = ct_to_alloc_tag(stop); - for (tag = start_tag; tag < stop_tag; tag++) { - WARN_ON(tag->counters); - tag->counters = alloc_percpu(struct alloc_tag_counters); - if (!tag->counters) { - while (--tag >= start_tag) { - free_percpu(tag->counters); - tag->counters = NULL; - } - pr_err("Failed to allocate memory for allocation tag percpu counters in the module %s\n", - mod->name); - return -ENOMEM; - } - - /* - * Avoid a kmemleak false positive. The pointer to the counters is stored - * in the alloc_tag section of the module and cannot be directly accessed. - */ - kmemleak_ignore_percpu(tag->counters); - } - return 0; -} - -static void replace_module(struct module *mod, struct module *new_mod) -{ - MA_STATE(mas, &mod_area_mt, 0, module_tags.size); - struct module *val; - - mas_lock(&mas); - mas_for_each(&mas, val, module_tags.size) { - if (val != mod) - continue; - - mas_store_gfp(&mas, new_mod, GFP_KERNEL); - break; - } - mas_unlock(&mas); -} - -static int __init alloc_mod_tags_mem(void) -{ - /* Map space to copy allocation tags */ - vm_module_tags = execmem_vmap(MODULE_ALLOC_TAG_VMAP_SIZE); - if (!vm_module_tags) { - pr_err("Failed to map %lu bytes for module allocation tags\n", - MODULE_ALLOC_TAG_VMAP_SIZE); - module_tags.start_addr = 0; - return -ENOMEM; - } - - vm_module_tags->pages = kmalloc_objs(struct page *, - get_vm_area_size(vm_module_tags) >> PAGE_SHIFT, - GFP_KERNEL | __GFP_ZERO); - if (!vm_module_tags->pages) { - free_vm_area(vm_module_tags); - return -ENOMEM; - } - - module_tags.start_addr = (unsigned long)vm_module_tags->addr; - module_tags.end_addr = module_tags.start_addr + MODULE_ALLOC_TAG_VMAP_SIZE; - /* Ensure the base is alloc_tag aligned when required for indexing */ - module_tags.start_addr = alloc_tag_align(module_tags.start_addr); - - return 0; -} - -static void __init free_mod_tags_mem(void) -{ - release_pages_arg arg = { .pages = vm_module_tags->pages }; - - module_tags.start_addr = 0; - release_pages(arg, vm_module_tags->nr_pages); - kfree(vm_module_tags->pages); - free_vm_area(vm_module_tags); -} - -#else /* CONFIG_MODULES */ - -static inline int alloc_mod_tags_mem(void) { return 0; } -static inline void free_mod_tags_mem(void) {} - -#endif /* CONFIG_MODULES */ - -/* See: Documentation/mm/allocation-profiling.rst */ -static int __init setup_early_mem_profiling(char *str) -{ - bool compressed = false; - bool enable; - - if (!str || !str[0]) - return -EINVAL; - - if (!strncmp(str, "never", 5)) { - enable = false; - mem_profiling_support = false; - pr_info("Memory allocation profiling is disabled!\n"); - } else { - char *token = strsep(&str, ","); - - if (kstrtobool(token, &enable)) - return -EINVAL; - - if (str) { - - if (strcmp(str, "compressed")) - return -EINVAL; - - compressed = true; - } - mem_profiling_support = true; - pr_info("Memory allocation profiling is enabled %s compression and is turned %s!\n", - compressed ? "with" : "without", str_on_off(enable)); - } - - if (enable != mem_alloc_profiling_enabled()) { - if (enable) - static_branch_enable(&mem_alloc_profiling_key); - else - static_branch_disable(&mem_alloc_profiling_key); - } - if (compressed != static_key_enabled(&mem_profiling_compressed)) { - if (compressed) - static_branch_enable(&mem_profiling_compressed); - else - static_branch_disable(&mem_profiling_compressed); - } - - return 0; -} -early_param("sysctl.vm.mem_profiling", setup_early_mem_profiling); - -static __init bool need_page_alloc_tagging(void) -{ - if (static_key_enabled(&mem_profiling_compressed)) - return false; - - return mem_profiling_support; -} - -#ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG -/* - * Track page allocations before page_ext is initialized. - * Some pages are allocated before page_ext becomes available, leaving - * their codetag uninitialized. Track these early PFNs so we can clear - * their codetag refs later to avoid warnings when they are freed. - * - * Each page is cast to a pfn_pool: the first few bytes hold metadata - * (next pointer and slot count), the remainder stores PFNs. - */ -struct pfn_pool { - struct pfn_pool *next; - atomic_t count; - unsigned long pfns[]; -}; - -#define PFN_POOL_SIZE ((PAGE_SIZE - offsetof(struct pfn_pool, pfns)) / \ - sizeof(unsigned long)) - -/* - * Skip early PFN recording for a page allocation. Reuses the - * %__GFP_NO_OBJ_EXT bit. Used by __alloc_tag_add_early_pfn() to avoid - * recursion when allocating pages for the early PFN tracking list - * itself. - * - * Codetags of the pages allocated with __GFP_NO_CODETAG should be - * cleared (via clear_page_tag_ref()) before freeing the pages to prevent - * alloc_tag_sub_check() from triggering a warning. - */ -#define __GFP_NO_CODETAG __GFP_NO_OBJ_EXT - -static struct pfn_pool *current_pfn_pool __initdata; - -static void __init __alloc_tag_add_early_pfn(unsigned long pfn) -{ - struct pfn_pool *pool; - int idx; - - do { - pool = READ_ONCE(current_pfn_pool); - if (!pool || atomic_read(&pool->count) >= PFN_POOL_SIZE) { - struct page *new_page = alloc_page(__GFP_HIGH | __GFP_NO_CODETAG); - struct pfn_pool *new; - - if (!new_page) { - pr_warn_once("early PFN tracking page allocation failed\n"); - return; - } - new = page_address(new_page); - new->next = pool; - atomic_set(&new->count, 0); - if (cmpxchg(¤t_pfn_pool, pool, new) != pool) { - clear_page_tag_ref(new_page); - __free_page(new_page); - continue; - } - pool = new; - } - idx = atomic_read(&pool->count); - if (idx >= PFN_POOL_SIZE) - continue; - if (atomic_cmpxchg(&pool->count, idx, idx + 1) == idx) - break; - } while (1); - - pool->pfns[idx] = pfn; -} - -typedef void alloc_tag_add_func(unsigned long pfn); -static alloc_tag_add_func __rcu *alloc_tag_add_early_pfn_ptr __refdata = - RCU_INITIALIZER(__alloc_tag_add_early_pfn); - -void alloc_tag_add_early_pfn(unsigned long pfn, gfp_t gfp_flags) -{ - alloc_tag_add_func *alloc_tag_add; - - if (static_key_enabled(&mem_profiling_compressed)) - return; - - /* Skip allocations for the tracking list itself to avoid recursion. */ - if (gfp_flags & __GFP_NO_CODETAG) - return; - - rcu_read_lock(); - alloc_tag_add = rcu_dereference(alloc_tag_add_early_pfn_ptr); - if (alloc_tag_add) - alloc_tag_add(pfn); - rcu_read_unlock(); -} - -static void __init clear_early_alloc_pfn_tag_refs(void) -{ - struct pfn_pool *pool, *next; - struct page *page; - int i; - - if (static_key_enabled(&mem_profiling_compressed)) - return; - - rcu_assign_pointer(alloc_tag_add_early_pfn_ptr, NULL); - /* Make sure we are not racing with __alloc_tag_add_early_pfn() */ - synchronize_rcu(); - - for (pool = current_pfn_pool; pool; pool = next) { - int nr_pfns = atomic_read(&pool->count); - - for (i = 0; i < nr_pfns; i++) { - unsigned long pfn = pool->pfns[i]; - - if (pfn_valid(pfn)) { - union pgtag_ref_handle handle; - union codetag_ref ref; - - if (get_page_tag_ref(pfn_to_page(pfn), &ref, &handle)) { - /* - * An early-allocated page could be freed and reallocated - * after its page_ext is initialized but before we clear it. - * In that case, it already has a valid tag set. - * We should not overwrite that valid tag - * with CODETAG_EMPTY. - * - * Note: there is still a small race window between checking - * ref.ct and calling set_codetag_empty(). We accept this - * race as it's unlikely and the extra complexity of atomic - * cmpxchg is not worth it for this debug-only code path. - */ - if (ref.ct) { - put_page_tag_ref(handle); - continue; - } - - set_codetag_empty(&ref); - update_page_tag_ref(handle, &ref); - put_page_tag_ref(handle); - } - } - } - - next = pool->next; - page = virt_to_page(pool); - clear_page_tag_ref(page); - __free_page(page); - } -} -#else /* !CONFIG_MEM_ALLOC_PROFILING_DEBUG */ -static inline void __init clear_early_alloc_pfn_tag_refs(void) {} -#endif /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ - -static __init void init_page_alloc_tagging(void) -{ - clear_early_alloc_pfn_tag_refs(); -} - -struct page_ext_operations page_alloc_tagging_ops = { - .size = sizeof(union codetag_ref), - .need = need_page_alloc_tagging, - .init = init_page_alloc_tagging, -}; -EXPORT_SYMBOL(page_alloc_tagging_ops); - -#ifdef CONFIG_SYSCTL -/* - * Not using proc_do_static_key() directly to prevent enabling profiling - * after it was shut down. - */ -static int proc_mem_profiling_handler(const struct ctl_table *table, int write, - void *buffer, size_t *lenp, loff_t *ppos) -{ - if (write) { - /* - * Call from do_sysctl_args() which is a no-op since the same - * value was already set by setup_early_mem_profiling. - * Return success to avoid warnings from do_sysctl_args(). - */ - if (!current->mm) - return 0; - -#ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG - /* User can't toggle profiling while debugging */ - return -EACCES; -#endif - if (!mem_profiling_support) - return -EINVAL; - } - - return proc_do_static_key(table, write, buffer, lenp, ppos); -} - - -static const struct ctl_table memory_allocation_profiling_sysctls[] = { - { - .procname = "mem_profiling", - .data = &mem_alloc_profiling_key, - .mode = 0644, - .proc_handler = proc_mem_profiling_handler, - }, -}; - -static void __init sysctl_init(void) -{ - register_sysctl_init("vm", memory_allocation_profiling_sysctls); -} -#else /* CONFIG_SYSCTL */ -static inline void sysctl_init(void) {} -#endif /* CONFIG_SYSCTL */ - -static int __init alloc_tag_init(void) -{ - const struct codetag_type_desc desc = { - .section = ALLOC_TAG_SECTION_NAME, - .tag_size = sizeof(struct alloc_tag), -#ifdef CONFIG_MODULES - .needs_section_mem = needs_section_mem, - .alloc_section_mem = reserve_module_tags, - .free_section_mem = release_module_tags, - .module_load = load_module, - .module_replaced = replace_module, -#endif - }; - int res; - - sysctl_init(); - - if (!mem_profiling_support) { - pr_info("Memory allocation profiling is not supported!\n"); - return 0; - } - - if (!proc_create_seq_private(ALLOCINFO_FILE_NAME, 0400, NULL, &allocinfo_seq_op, - sizeof(struct allocinfo_private), NULL)) { - pr_err("Failed to create %s file\n", ALLOCINFO_FILE_NAME); - shutdown_mem_profiling(false); - return -ENOMEM; - } - - res = alloc_mod_tags_mem(); - if (res) { - pr_err("Failed to reserve address space for module tags, errno = %d\n", res); - shutdown_mem_profiling(true); - return res; - } - - alloc_tag_cttype = codetag_register_type(&desc); - if (IS_ERR(alloc_tag_cttype)) { - pr_err("Allocation tags registration failed, errno = %pe\n", alloc_tag_cttype); - free_mod_tags_mem(); - shutdown_mem_profiling(true); - return PTR_ERR(alloc_tag_cttype); - } - - return 0; -} -module_init(alloc_tag_init); diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 6665372ecf71..2072c04e99d0 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1921,15 +1921,29 @@ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv, unsigned short max_vecs, unsigned mem_align_mask, iov_iter_extraction_t extraction_flags) { - unsigned long start = (unsigned long)iter_iov_addr(iter); unsigned short entries_left = max_vecs - *nr_vecs; unsigned short nr_pages, i = 0; size_t left, offset, len; struct page **pages; ssize_t size; - if ((start | iter_iov_len(iter)) & mem_align_mask) + /* + * DMA engines typically have both memory address and length alignment + * requirements, so check these against the alignment mask. For UBUF, + * IOVEC and KVEC, only the current segment will be extracted from; for + * everything else we might extract from multiple segments, so we need + * to check those too. + */ + if (likely(iter_is_ubuf(iter) || + iter_is_iovec(iter) || + iov_iter_is_kvec(iter))) { + unsigned long start = (unsigned long)iter_iov_addr(iter); + + if ((start | iter_iov_len(iter)) & mem_align_mask) + return -EINVAL; + } else if (iov_iter_alignment(iter) & mem_align_mask) { return -EINVAL; + } /* * Move page array up in the allocated memory for the bio vecs as far as diff --git a/lib/once.c b/lib/once.c index d801bfa945e6..0a0a919156e0 100644 --- a/lib/once.c +++ b/lib/once.c @@ -93,6 +93,6 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key, { *done = true; mutex_unlock(&once_mutex); - static_branch_disable(once_key); + once_disable_jump(once_key, mod); } EXPORT_SYMBOL(__do_once_sleepable_done); diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index b767a38a74f9..2f922b63d545 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c @@ -696,7 +696,7 @@ static int __init test_rhashtable_next_key(void) if (err) return err; - objs = kcalloc(n, sizeof(*objs), GFP_KERNEL); + objs = kzalloc_objs(*objs, n); if (!objs) { rhashtable_destroy(&ht); return -ENOMEM; diff --git a/lib/test_workqueue.c b/lib/test_workqueue.c index 99e160bd5ad1..2bdfcbbcabb4 100644 --- a/lib/test_workqueue.c +++ b/lib/test_workqueue.c @@ -149,11 +149,11 @@ static int __init run_bench(int n_threads, const char *scope, const char *label) if (ret) return ret; - ctxs = kcalloc(n_threads, sizeof(*ctxs), GFP_KERNEL); + ctxs = kzalloc_objs(*ctxs, n_threads); if (!ctxs) return -ENOMEM; - tasks = kcalloc(n_threads, sizeof(*tasks), GFP_KERNEL); + tasks = kzalloc_objs(*tasks, n_threads); if (!tasks) { kfree(ctxs); return -ENOMEM; diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index d9690ba1db88..32e42d8c7ca1 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -57,7 +57,7 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, void *buffer; unsigned int i; - pages = kzalloc_objs(struct page *, npages, GFP_KERNEL); + pages = kzalloc_objs(struct page *, npages); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; diff --git a/mm/damon/tests/vaddr-kunit.h b/mm/damon/tests/vaddr-kunit.h index 6a95441d193a..d61b503e319a 100644 --- a/mm/damon/tests/vaddr-kunit.h +++ b/mm/damon/tests/vaddr-kunit.h @@ -136,7 +136,7 @@ static void damon_do_test_apply_three_regions(struct kunit *test, if (!t) kunit_skip(test, "target alloc fail"); - ranges = kmalloc_array(nr_regions / 2, sizeof(*ranges), GFP_KERNEL); + ranges = kmalloc_objs(*ranges, nr_regions / 2); if (!ranges) { damon_destroy_target(t, NULL); kunit_skip(test, "ranges alloc fail"); diff --git a/mm/filemap.c b/mm/filemap.c index 6afec636881f..00fd89cf6f55 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1616,7 +1616,7 @@ static void filemap_end_dropbehind(struct folio *folio) return; if (!folio_test_clear_dropbehind(folio)) return; - if (mapping) + if (mapping && !folio_mapped(folio)) folio_unmap_invalidate(mapping, folio, 0); } diff --git a/mm/folio.c b/mm/folio.c index c02dcea9c03c..50a6dbe55998 100644 --- a/mm/folio.c +++ b/mm/folio.c @@ -33,6 +33,7 @@ #include <linux/page_idle.h> #include <linux/local_lock.h> #include <linux/buffer_head.h> +#include <linux/kvm_types.h> #include "internal.h" #include "page_alloc.h" @@ -926,6 +927,7 @@ void lru_cache_drain_for_folio(const struct folio *folio, *drained = LRU_CACHE_DRAINED_ALL; } } +EXPORT_SYMBOL_FOR_KVM(lru_cache_drain_for_folio); atomic_t lru_disable_count = ATOMIC_INIT(0); diff --git a/mm/huge_memory.c b/mm/huge_memory.c index ced400f72d43..1e5d68acf62a 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -92,7 +92,7 @@ unsigned long huge_anon_orders_madvise __read_mostly; unsigned long huge_anon_orders_inherit __read_mostly; static bool anon_orders_configured __initdata; -static inline bool file_thp_enabled(struct vm_area_struct *vma) +static inline bool file_thp_enabled(const struct vm_area_struct *vma) { struct inode *inode; @@ -118,6 +118,67 @@ static bool vma_is_special_huge(const struct vm_area_struct *vma) return vma_test_any(vma, VMA_PFNMAP_BIT, VMA_MIXEDMAP_BIT); } +static bool vma_file_bypass_thp_tuneables(const struct vm_area_struct *vma, + enum tva_type type) +{ + const bool has_huge_fault = vma->vm_ops->huge_fault; + + /* MADV_COLLAPSE ignores tuneables. */ + if (type == TVA_FORCED_COLLAPSE) + return true; + /* Huge PFN mappings are uncompactable so the policy doesn't apply. */ + if (vma_test(vma, VMA_PFNMAP_BIT) && has_huge_fault) + return true; + return false; +} + +static bool vma_file_allow_thp_tuneables(vm_flags_t vm_flags) +{ + /* THP=always? */ + if (hugepage_global_always()) + return true; + /* THP=madvise and marked MADV_HUGEPAGE? */ + if (hugepage_global_enabled() && (vm_flags & VM_HUGEPAGE)) + return true; + return false; +} + +static bool vma_file_check_thp_tuneables(const struct vm_area_struct *vma, + vm_flags_t vm_flags, enum tva_type type) +{ + return vma_file_bypass_thp_tuneables(vma, type) || + vma_file_allow_thp_tuneables(vm_flags); +} + +static bool vma_can_map_huge_file(const struct vm_area_struct *vma, + vm_flags_t vm_flags, enum tva_type type) +{ + const bool has_huge_fault = vma->vm_ops->huge_fault; + + /* + * Enforce THP collapse requirements as necessary. Anonymous vmas + * were already handled in thp_vma_allowable_orders(). + */ + if (!vma_file_check_thp_tuneables(vma, vm_flags, type)) + return false; + + switch (type) { + case TVA_PAGEFAULT: + /* + * Trust that ->huge_fault() handlers know what they are doing + * in fault path. + */ + return has_huge_fault; + case TVA_SMAPS: + if (has_huge_fault) + return true; + fallthrough; + default: + /* Only regular file is valid in collapse path. */ + return file_thp_enabled(vma); + } +} + unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma, vm_flags_t vm_flags, enum tva_type type, @@ -190,27 +251,8 @@ unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma, vma, vma_start_pgoff(vma), 0, forced_collapse); - if (!vma_is_anonymous(vma)) { - /* - * Enforce THP collapse requirements as necessary. Anonymous vmas - * were already handled in thp_vma_allowable_orders(). - */ - if (!forced_collapse && - (!hugepage_global_enabled() || (!(vm_flags & VM_HUGEPAGE) && - !hugepage_global_always()))) - return 0; - - /* - * Trust that ->huge_fault() handlers know what they are doing - * in fault path. - */ - if (((in_pf || smaps)) && vma->vm_ops->huge_fault) - return orders; - /* Only regular file is valid in collapse path */ - if (((!in_pf || smaps)) && file_thp_enabled(vma)) - return orders; - return 0; - } + if (!vma_is_anonymous(vma)) + return vma_can_map_huge_file(vma, vm_flags, type) ? orders : 0; if (vma_is_temporary_stack(vma)) return 0; @@ -2449,6 +2491,8 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma, add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); + if (is_present && pmd_dirty(pmdval)) + folio_mark_dirty(folio); if (is_present && pmd_young(pmdval) && likely(vma_has_recency(vma))) folio_mark_accessed(folio); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 785772845795..4f6f58bf3db6 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1992,7 +1992,8 @@ retry: if (h->surplus_huge_pages_node[folio_nid(folio)]) adjust_surplus = true; remove_hugetlb_folio(h, folio, adjust_surplus); - h->max_huge_pages--; + if (!adjust_surplus) + h->max_huge_pages--; spin_unlock_irq(&hugetlb_lock); /* @@ -2012,7 +2013,8 @@ retry: if (rc) { spin_lock_irq(&hugetlb_lock); add_hugetlb_folio(h, folio, adjust_surplus); - h->max_huge_pages++; + if (!adjust_surplus) + h->max_huge_pages++; goto out; } } else { @@ -7330,14 +7332,14 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, * There is no need to transfer the per-node surplus state * when we do not cross the node. */ - if (new_nid == old_nid) - return; - spin_lock_irq(&hugetlb_lock); - if (h->surplus_huge_pages_node[old_nid]) { - h->surplus_huge_pages_node[old_nid]--; - h->surplus_huge_pages_node[new_nid]++; + if (new_nid != old_nid) { + spin_lock_irq(&hugetlb_lock); + if (h->surplus_huge_pages_node[old_nid]) { + h->surplus_huge_pages_node[old_nid]--; + h->surplus_huge_pages_node[new_nid]++; + } + spin_unlock_irq(&hugetlb_lock); } - spin_unlock_irq(&hugetlb_lock); } /* diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index e0083de1ca82..ecb6e0b7819a 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -97,6 +97,7 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup, struct page_counter *fault, *fault_parent = NULL; struct page_counter *rsvd, *rsvd_parent = NULL; unsigned long limit; + int ret; if (parent_h_cgroup) { fault_parent = hugetlb_cgroup_counter_from_cgroup( @@ -118,8 +119,10 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup, limit = round_down(PAGE_COUNTER_MAX, pages_per_huge_page(&hstates[idx])); - VM_BUG_ON(page_counter_set_max(fault, limit)); - VM_BUG_ON(page_counter_set_max(rsvd, limit)); + ret = page_counter_set_max(fault, limit); + VM_WARN_ON_ONCE(ret); + ret = page_counter_set_max(rsvd, limit); + VM_WARN_ON_ONCE(ret); } } diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c index db0680e82847..95fd2d190f0d 100644 --- a/mm/hugetlb_cma.c +++ b/mm/hugetlb_cma.c @@ -3,6 +3,7 @@ #include <linux/mm.h> #include <linux/cma.h> #include <linux/compiler.h> +#include <linux/cpuset.h> #include <linux/mm_inline.h> #include <asm/page.h> @@ -55,15 +56,25 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask, int node; struct folio *folio; struct page *page = NULL; + const nodemask_t *nmask; + unsigned int cpuset_mems_cookie; if (!hugetlb_cma_size) return NULL; - if (hugetlb_cma[nid]) +retry_cpuset: + if (!nodemask) { + cpuset_mems_cookie = read_mems_allowed_begin(); + nmask = &cpuset_current_mems_allowed; + } else { + nmask = nodemask; + } + + if (hugetlb_cma[nid] && node_isset(nid, *nmask)) page = cma_alloc_frozen_compound(hugetlb_cma[nid], order); if (!page && !(gfp_mask & __GFP_THISNODE)) { - for_each_node_mask(node, *nodemask) { + for_each_node_mask(node, *nmask) { if (node == nid || !hugetlb_cma[node]) continue; @@ -73,8 +84,12 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask, } } - if (!page) + if (!page) { + if (!nodemask && + unlikely(read_mems_allowed_retry(cpuset_mems_cookie))) + goto retry_cpuset; return NULL; + } folio = page_folio(page); folio_set_hugetlb_cma(folio); diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 11ff98d55c76..75639298efc2 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -454,12 +454,6 @@ int hugepage_madvise(struct vm_area_struct *vma, case MADV_HUGEPAGE: *vm_flags &= ~VM_NOHUGEPAGE; *vm_flags |= VM_HUGEPAGE; - /* - * If the vma become good for khugepaged to scan, - * register it here without waiting a page fault that - * may not happen any time soon. - */ - khugepaged_enter_vma(vma, *vm_flags); break; case MADV_NOHUGEPAGE: *vm_flags &= ~VM_HUGEPAGE; diff --git a/mm/madvise.c b/mm/madvise.c index 96f2387b2f46..eeee82cf2b3f 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -178,6 +178,14 @@ static int madvise_update_vma(vm_flags_t new_flags, /* vm_flags is protected by the mmap_lock held in write mode. */ vma_start_write(vma); vma->flags = new_vma_flags; + /* + * If the vma become good for khugepaged to scan, + * register it here without waiting a page fault that + * may not happen any time soon. + */ + if (vma_flags_test(&new_vma_flags, VMA_HUGEPAGE_BIT)) + khugepaged_enter_vma(vma, vma_flags_to_legacy(new_vma_flags)); + if (set_new_anon_name) return replace_anon_vma_name(vma, anon_name); diff --git a/mm/memblock.c b/mm/memblock.c index 9ce86349a29f..021db49eb7fc 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2908,14 +2908,18 @@ static int memblock_debug_show(struct seq_file *m, void *private) else seq_printf(m, "%4c ", 'x'); if (reg->flags) { - for (j = 0; j < count; j++) { - if (reg->flags & (1U << j)) { - seq_printf(m, "%s\n", flagname[j]); - break; - } + unsigned int flags = reg->flags; + bool first = true; + + for (j = 0; flags; j++, flags >>= 1) { + if (!(flags & 1)) + continue; + if (!first) + seq_putc(m, '|'); + seq_puts(m, j < count ? flagname[j] : "UNKNOWN"); + first = false; } - if (j == count) - seq_printf(m, "%s\n", "UNKNOWN"); + seq_putc(m, '\n'); } else { seq_printf(m, "%s\n", "NONE"); } diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 835fc8e51184..05ef55cae4dc 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -96,7 +96,6 @@ enum { RES_LIMIT, RES_MAX_USAGE, RES_FAILCNT, - RES_SOFT_LIMIT, }; #ifdef CONFIG_LOCKDEP @@ -1888,6 +1887,30 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, return -EINVAL; } +static u64 mem_cgroup_soft_limit_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return (u64)PAGE_COUNTER_MAX * PAGE_SIZE; +} + +static ssize_t mem_cgroup_soft_limit_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + unsigned long nr_pages; + int ret; + + ret = page_counter_memparse(strstrip(buf), "-1", &nr_pages); + if (ret) + return ret; + + pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. " + "Writing any value to this file has no effect. " + "Please report your usecase to linux-mm@kvack.org if you " + "depend on this functionality.\n"); + + return nbytes; +} + static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css, struct cftype *cft) { @@ -1924,8 +1947,6 @@ static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css, return (u64)counter->watermark * PAGE_SIZE; case RES_FAILCNT: return counter->failcnt; - case RES_SOFT_LIMIT: - return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE; default: BUG(); } @@ -2020,17 +2041,6 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of, break; } break; - case RES_SOFT_LIMIT: - if (IS_ENABLED(CONFIG_PREEMPT_RT)) { - ret = -EOPNOTSUPP; - } else { - pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. " - "Please report your usecase to linux-mm@kvack.org if you " - "depend on this functionality.\n"); - WRITE_ONCE(memcg->soft_limit, nr_pages); - ret = 0; - } - break; } return ret ?: nbytes; } @@ -2384,9 +2394,8 @@ struct cftype mem_cgroup_legacy_files[] = { }, { .name = "soft_limit_in_bytes", - .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT), - .write = mem_cgroup_write, - .read_u64 = mem_cgroup_read_u64, + .write = mem_cgroup_soft_limit_write, + .read_u64 = mem_cgroup_soft_limit_read, }, { .name = "failcnt", diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 1271d390b617..856a7d07586c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3158,7 +3158,7 @@ static int obj_cgroup_charge_pages(struct obj_cgroup *objcg, gfp_t gfp, memcg = get_mem_cgroup_from_objcg(objcg); - ret = try_charge_memcg(memcg, gfp, nr_pages); + ret = try_charge(memcg, gfp, nr_pages); if (ret) goto out; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 3498a5651d50..79053ece02cd 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -2679,7 +2679,7 @@ static unsigned long alloc_pages_bulk_weighted_interleave(gfp_t gfp, prev_node = node; /* create a local copy of node weights to operate on outside rcu */ - weights = kzalloc(nr_node_ids, GFP_KERNEL); + weights = kmalloc(nr_node_ids, gfp & GFP_RECLAIM_MASK); if (!weights) return total_allocated; diff --git a/mm/migrate_device.c b/mm/migrate_device.c index 762c5cee8fec..009bfa8b212d 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -1423,6 +1423,15 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start, src_pfns[i] = migrate_device_pfn_lock(pfn); nr = folio_nr_pages(folio); + if (nr > npages - i) { + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { + folio_unlock(folio); + folio_put(folio); + } + memset(&src_pfns[i], 0, + (npages - i) * sizeof(*src_pfns)); + break; + } if (nr > 1) { src_pfns[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < nr; j++) @@ -1457,6 +1466,15 @@ int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages) src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]); nr = folio_nr_pages(folio); + if (nr > npages - i) { + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { + folio_unlock(folio); + folio_put(folio); + } + memset(&src_pfns[i], 0, + (npages - i) * sizeof(*src_pfns)); + break; + } if (nr > 1) { src_pfns[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < nr; j++) diff --git a/mm/mlock.c b/mm/mlock.c index efa6716e4dfb..39215a3eab1f 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -141,7 +141,7 @@ static struct lruvec *__munlock_folio(struct folio *folio, struct lruvec *lruvec munlock: if (folio_test_clear_mlocked(folio)) { - __zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages); + zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages); if (isolated || !folio_test_unevictable(folio)) __count_vm_events(UNEVICTABLE_PGMUNLOCKED, nr_pages); else diff --git a/mm/mremap.c b/mm/mremap.c index e8df5cdb0ac9..7c368440fafe 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -1331,24 +1331,35 @@ static void dontunmap_complete(struct vma_remap_struct *vrm, { unsigned long start = vrm->addr; unsigned long end = vrm->addr + vrm->old_len; - unsigned long old_start = vrm->vma->vm_start; - unsigned long old_end = vrm->vma->vm_end; + struct vm_area_struct *vma = vrm->vma; + unsigned long old_start = vma->vm_start; + unsigned long old_end = vma->vm_end; /* We always clear VMA_LOCKED[ONFAULT]_BIT on the old VMA. */ - vma_clear_flags_mask(vrm->vma, VMA_LOCKED_MASK); + vma_clear_flags_mask(vma, VMA_LOCKED_MASK); /* * anon_vma links of the old vma is no longer needed after its page * table has been moved. */ - if (new_vma != vrm->vma && start == old_start && end == old_end) - unlink_anon_vmas(vrm->vma); + if (new_vma != vma && start == old_start && end == old_end) { + const pgoff_t pgoff_unfaulted = vma->vm_start >> PAGE_SHIFT; - /* Because we won't unmap we don't need to touch locked_vm. */ + unlink_anon_vmas(vma); + /* + * The VMA is now unfaulted and it is an invariant that + * unfaulted anonymous VMAs have page offset equal to + * vma->vm_start >> PAGE_SHIFT. + */ + vma_set_anon_pgoff(vma, pgoff_unfaulted); + if (vma_is_anonymous(vma) && !vma->vm_file) + vma_set_pgoff(vma, pgoff_unfaulted); + } } static unsigned long move_vma(struct vma_remap_struct *vrm) { + const bool is_dontunmap = vrm->flags & MREMAP_DONTUNMAP; struct mm_struct *mm = current->mm; struct vm_area_struct *new_vma; unsigned long hiwater_vm; @@ -1389,10 +1400,10 @@ static unsigned long move_vma(struct vma_remap_struct *vrm) */ hiwater_vm = mm->hiwater_vm; - vrm_stat_account(vrm, vrm->new_len); - if (unlikely(!err && (vrm->flags & MREMAP_DONTUNMAP))) + if (unlikely(is_dontunmap && !err)) dontunmap_complete(vrm, new_vma); - else + vrm_stat_account(vrm, vrm->new_len); + if (!is_dontunmap || err) unmap_source_vma(vrm); mm->hiwater_vm = hiwater_vm; diff --git a/mm/secretmem.c b/mm/secretmem.c index d29865075b6e..384f5cfc457f 100644 --- a/mm/secretmem.c +++ b/mm/secretmem.c @@ -18,6 +18,8 @@ #include <linux/secretmem.h> #include <linux/set_memory.h> #include <linux/sched/signal.h> +#include <linux/sched/user.h> +#include <linux/cred.h> #include <uapi/linux/magic.h> @@ -47,10 +49,69 @@ bool secretmem_active(void) return !!atomic_read(&secretmem_users); } +struct secretmem_inode_state { + struct user_struct *user; + atomic_long_t nr_pages_accounted; +}; + +static bool __secretmem_account_pages(struct user_struct *user, + unsigned long nr_pages) +{ + unsigned long page_limit, cur_pages, new_pages; + + if (!nr_pages) + return true; + + page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + cur_pages = atomic_long_read(&user->locked_vm); + do { + new_pages = cur_pages + nr_pages; + if (new_pages > page_limit) + return false; + } while (!atomic_long_try_cmpxchg(&user->locked_vm, + &cur_pages, new_pages)); + return true; +} + +static bool secretmem_account_folio(struct secretmem_inode_state *state, + const struct folio *folio) +{ + const unsigned long nr_pages = folio_nr_pages(folio); + + if (!__secretmem_account_pages(state->user, nr_pages)) + return false; + + atomic_long_add(nr_pages, &state->nr_pages_accounted); + return true; +} + +static void __secretmem_unaccount_pages(struct secretmem_inode_state *state, + unsigned long nr_pages) +{ + atomic_long_sub(nr_pages, &state->user->locked_vm); + atomic_long_sub(nr_pages, &state->nr_pages_accounted); +} + +static void secretmem_unaccount_folio(struct secretmem_inode_state *state, + struct folio *folio) +{ + __secretmem_unaccount_pages(state, folio_nr_pages(folio)); +} + +static void secretmem_unaccount_all_folios(struct secretmem_inode_state *state) +{ + const unsigned long nr_pages_accounted = + atomic_long_read(&state->nr_pages_accounted); + + __secretmem_unaccount_pages(state, nr_pages_accounted); +} + static vm_fault_t secretmem_fault(struct vm_fault *vmf) { struct address_space *mapping = vmf->vma->vm_file->f_mapping; struct inode *inode = file_inode(vmf->vma->vm_file); + struct secretmem_inode_state *state = inode->i_private; pgoff_t offset = vmf->pgoff; gfp_t gfp = vmf->gfp_mask; unsigned long addr; @@ -72,8 +133,15 @@ retry: goto out; } + if (!secretmem_account_folio(state, folio)) { + folio_put(folio); + ret = VM_FAULT_SIGBUS; + goto out; + } + err = set_direct_map_invalid_noflush(folio_page(folio, 0)); if (err) { + secretmem_unaccount_folio(state, folio); folio_put(folio); ret = vmf_error(err); goto out; @@ -82,6 +150,7 @@ retry: __folio_mark_uptodate(folio); err = filemap_add_folio(mapping, folio, offset, gfp); if (unlikely(err)) { + secretmem_unaccount_folio(state, folio); /* * If a split of large page was required, it * already happened when we marked the page invalid @@ -112,22 +181,30 @@ static const struct vm_operations_struct secretmem_vm_ops = { .fault = secretmem_fault, }; +static void secretmem_destroy_inode_priv(struct inode *inode) +{ + struct secretmem_inode_state *state = inode->i_private; + + secretmem_unaccount_all_folios(state); + free_uid(state->user); + kfree(state); + inode->i_private = NULL; +} + static int secretmem_release(struct inode *inode, struct file *file) { atomic_dec(&secretmem_users); + secretmem_destroy_inode_priv(inode); + return 0; } static int secretmem_mmap_prepare(struct vm_area_desc *desc) { - const unsigned long len = vma_desc_size(desc); - if (!vma_desc_test_any(desc, VMA_SHARED_BIT, VMA_MAYSHARE_BIT)) return -EINVAL; - vma_desc_set_flags(desc, VMA_LOCKED_BIT, VMA_DONTDUMP_BIT); - if (!mlock_future_ok(desc->mm, /*is_vma_locked=*/ true, len)) - return -EAGAIN; + vma_desc_set_flags(desc, VMA_DONTDUMP_BIT); desc->vm_ops = &secretmem_vm_ops; return 0; @@ -187,20 +264,40 @@ static const struct inode_operations secretmem_iops = { static struct vfsmount *secretmem_mnt; +static int secretmem_init_inode_priv(struct inode *inode) +{ + struct secretmem_inode_state *state; + + state = kzalloc_obj(*state); + if (!state) + return -ENOMEM; + + state->user = get_uid(current_user()); + inode->i_private = state; + return 0; +} + static struct file *secretmem_file_create(unsigned long flags) { struct file *file; struct inode *inode; const char *anon_name = "[secretmem]"; + int err; inode = anon_inode_make_secure_inode(secretmem_mnt->mnt_sb, anon_name, NULL); if (IS_ERR(inode)) return ERR_CAST(inode); + err = secretmem_init_inode_priv(inode); + if (err) + goto err_free_inode; + file = alloc_file_pseudo(inode, secretmem_mnt, "secretmem", O_RDWR | O_LARGEFILE, &secretmem_fops); - if (IS_ERR(file)) - goto err_free_inode; + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto err_free_priv; + } mapping_set_gfp_mask(inode->i_mapping, GFP_USER); mapping_set_unevictable(inode->i_mapping); @@ -215,10 +312,11 @@ static struct file *secretmem_file_create(unsigned long flags) atomic_inc(&secretmem_users); return file; - +err_free_priv: + secretmem_destroy_inode_priv(inode); err_free_inode: iput(inode); - return file; + return ERR_PTR(err); } SYSCALL_DEFINE1(memfd_secret, unsigned int, flags) diff --git a/mm/shrinker.c b/mm/shrinker.c index a70aab124a0e..7ec2a9704f6f 100644 --- a/mm/shrinker.c +++ b/mm/shrinker.c @@ -227,6 +227,8 @@ static int shrinker_memcg_alloc(struct shrinker *shrinker) { int id; + shrinker->id = -1; + if (mem_cgroup_disabled()) return -ENOSYS; if (mem_cgroup_kmem_disabled() && !(shrinker->flags & SHRINKER_NONSLAB)) diff --git a/mm/slab_common.c b/mm/slab_common.c index b19ba1b31484..7223a7596dab 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1667,14 +1667,6 @@ static bool kfree_rcu_sheaf(void *obj) { struct kmem_cache *s; struct slab *slab; - unsigned int free_flags = SLAB_FREE_DEFAULT; - - /* - * It is not safe to spin on PREEMPT_RT because the kernel might be - * holding a raw spinlock and slab acquires sleeping locks. - */ - if (IS_ENABLED(CONFIG_PREEMPT_RT)) - free_flags = SLAB_FREE_NOLOCK; if (is_vmalloc_addr(obj)) return false; @@ -1685,7 +1677,7 @@ static bool kfree_rcu_sheaf(void *obj) s = slab->slab_cache; if (likely(!IS_ENABLED(CONFIG_NUMA) || slab_nid(slab) == numa_mem_id())) - return __kfree_rcu_sheaf(s, obj, free_flags); + return __kfree_rcu_sheaf(s, obj, SLAB_FREE_DEFAULT); return false; } @@ -2034,7 +2026,13 @@ void kvfree_call_rcu(struct kvfree_rcu_head *head, void *ptr) if (!head) might_sleep(); - if (kfree_rcu_sheaf(ptr)) + /* + * kvfree_rcu() is called by set_cpus_allowed_force() with + * task_struct::pi_lock acquired. On PREEMPT_RT the local_trylock() + * usage below will acquire the waitlock which must be avoided. + * Therefore avoid it on PREEMPT_RT. + */ + if (!IS_ENABLED(CONFIG_PREEMPT_RT) && kfree_rcu_sheaf(ptr)) return; // Queue the object but don't yet schedule the batch. diff --git a/mm/slub.c b/mm/slub.c index f9b56cb439e7..54ec12503357 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -5680,10 +5680,12 @@ static noinline void free_to_partial_list( * * Fail if the slab isn't full anymore due to a concurrent free. */ -static bool __slab_try_return_freelist(struct kmem_cache *s, struct slab *slab, - void *head, int cnt) +static bool __slab_try_return_freelist(struct kmem_cache *s, + struct kmem_cache_node *n, + struct slab *slab, void *head, int cnt) { struct freelist_counters old, new; + unsigned long flags; old.freelist = slab->freelist; old.counters = slab->counters; @@ -5695,9 +5697,15 @@ static bool __slab_try_return_freelist(struct kmem_cache *s, struct slab *slab, new.counters = old.counters; new.inuse -= cnt; - if (!slab_update_freelist(s, slab, &old, &new, "__slab_try_return_freelist")) + spin_lock_irqsave(&n->list_lock, flags); + + if (!slab_update_freelist(s, slab, &old, &new, "__slab_try_return_freelist")) { + spin_unlock_irqrestore(&n->list_lock, flags); return false; + } + add_partial(n, slab, ADD_TO_TAIL); + spin_unlock_irqrestore(&n->list_lock, flags); return true; } @@ -6088,8 +6096,9 @@ empty: /* * kvfree_call_rcu() can be called while holding a raw_spinlock_t. Since * __kfree_rcu_sheaf() may acquire a spinlock_t (sleeping lock on PREEMPT_RT), - * this would violate lock nesting rules. Therefore, kvfree_call_rcu() avoids - * this problem by passing SLAB_FREE_NOLOCK on PREEMPT_RT. + * this would violate lock nesting rules. Therefore, kfree_call_rcu_nolock() + * avoids this problem by passing SLAB_FREE_NOLOCK. kvfree_call_rcu() is + * bypassing the sheaves layer completely on PREEMPT_RT. * * However, lockdep still complains that it is invalid to acquire spinlock_t * while holding raw_spinlock_t, even on !PREEMPT_RT where spinlock_t is a @@ -7296,10 +7305,8 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi void *head = object; void *tail; - if (__slab_try_return_freelist(s, slab, head, count)) { - list_add(&slab->slab_list, &pc.slabs); + if (__slab_try_return_freelist(s, n, slab, head, count)) break; - } do { tail = object; @@ -7312,7 +7319,7 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi break; } - if (!list_empty(&pc.slabs)) { + if (unlikely(!list_empty(&pc.slabs))) { spin_lock_irqsave(&n->list_lock, flags); list_for_each_entry(slab, &pc.slabs, slab_list) diff --git a/mm/swapfile.c b/mm/swapfile.c index 53bf01d5f7f1..601979b97f95 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -156,7 +156,7 @@ static struct swap_info_struct *swap_entry_to_info(swp_entry_t entry) * This bit will be set if the device is not on the plist and not * usable, will be cleared if the device is on the plist. */ -#define SWAP_USAGE_OFFLIST_BIT (1UL << (BITS_PER_TYPE(atomic_t) - 2)) +#define SWAP_USAGE_OFFLIST_BIT (1UL << (BITS_PER_TYPE(atomic_long_t) - 2)) #define SWAP_USAGE_COUNTER_MASK (~SWAP_USAGE_OFFLIST_BIT) static long swap_usage_in_pages(struct swap_info_struct *si) { diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 23fb68fce000..74f04c323c50 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2171,8 +2171,10 @@ static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, } if (err) { - if (err == -EAGAIN) + if (err == -EAGAIN) { + err = 0; continue; + } break; } @@ -2859,10 +2859,12 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, map.check_ksm_early = can_set_ksm_flags_early(&map); error = __mmap_setup(&map, &desc, uf); - if (!error && have_mmap_prepare) - error = call_mmap_prepare(&map, &desc); if (error) goto abort_munmap; + if (have_mmap_prepare) + error = call_mmap_prepare(&map, &desc); + if (error) + goto unacct_error; if (map.check_ksm_early) update_ksm_flags(&map); diff --git a/net/6lowpan/nhc.h b/net/6lowpan/nhc.h index ab7b4977c32b..c995029696d0 100644 --- a/net/6lowpan/nhc.h +++ b/net/6lowpan/nhc.h @@ -15,7 +15,7 @@ * @__nhc: variable name of the lowpan_nhc struct. * @_name: const char * of common header compression name. * @_nexthdr: ipv6 nexthdr field for the header compression. - * @_nexthdrlen: ipv6 nexthdr len for the reserved space. + * @_hdrlen: ipv6 nexthdr len for the reserved space. * @_id: one byte nhc id value. * @_idmask: one byte nhc id mask value. * @_uncompress: callback for uncompression call. @@ -102,7 +102,6 @@ int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr, /** * lowpan_nhc_do_uncompression - calling uncompress callback for nhc * - * @nhc: 6LoWPAN nhc context, get by lowpan_nhc_by_ functions. * @skb: skb of 6LoWPAN header, skb->data should be pointed to nhc id value. * @dev: netdevice for print logging information. * @hdr: ipv6hdr for setting nexthdr value. diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index e7ad295504e4..c5bc60d16e40 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -981,7 +981,7 @@ batadv_wifi_net_device_insert(struct net_device *net_dev, u32 wifi_flags) ASSERT_RTNL(); - device_state = kzalloc_obj(*device_state, GFP_KERNEL); + device_state = kzalloc_obj(*device_state); if (!device_state) return -ENOMEM; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 35a1be57e386..d7355c73f93e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev) if (error) BT_WARN("register suspend notifier failed error:%d\n", error); - queue_work(hdev->req_workqueue, &hdev->power_on); - idr_init(&hdev->adv_monitors_idr); msft_register(hdev); + queue_work(hdev->req_workqueue, &hdev->power_on); + return id; err_wqueue: diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index ffd7b37e7401..2a651a4d60e6 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -4797,6 +4797,24 @@ static int hci_le_set_def_rate_sync(struct hci_dev *hdev) cp.cont_num = cpu_to_le16(0x0001); cp.supv_timeout = cpu_to_le16(0x000c); /* 120 ms */ + /* The connection event length recommended in requests by a Peripheral + * uses units of 125 us with a valid range of 0x0001 to 0x7CFF + * (0.125 ms to 3.999875 s), so 0x0000 cannot be used. Also note that + * the Controller is not required to use these values: + * + * BLUETOOTH CORE SPECIFICATION Version 6.2 | Vol 4, Part E + * 7.8.158. LE Set Default Rate Parameters command + * + * The Min_CE_Length and Max_CE_Length parameters provide the + * Controller with the expected minimum and maximum length of the + * connection events. The Controller is not required to use these + * values. + * + * So it is safe to just use the minimum. + */ + cp.min_ce_len = cpu_to_le16(0x0001); + cp.max_ce_len = cpu_to_le16(0x0001); + return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_DEF_RATE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); } @@ -6423,8 +6441,7 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev) goto update_scan; } - accept_list = kmalloc_array(num_entries, sizeof(*accept_list), - GFP_KERNEL); + accept_list = kmalloc_objs(*accept_list, num_entries); if (!accept_list) { hci_dev_unlock(hdev); return -ENOMEM; @@ -7467,8 +7484,24 @@ static int hci_le_conn_rate_request_sync(struct hci_dev *hdev, void *data) cp.max_latency = cpu_to_le16(params->max_latency); cp.cont_num = cpu_to_le16(params->cont_num); cp.supv_timeout = cpu_to_le16(params->rate_supv_timeout); - cp.min_ce_len = cpu_to_le16(0x0000); - cp.max_ce_len = cpu_to_le16(0x0000); + + /* The connection event length recommended in requests by a Peripheral + * uses units of 125 us with a valid range of 0x0001 to 0x7CFF + * (0.125 ms to 3.999875 s), so 0x0000 cannot be used. Also note that + * the Controller is not required to use these values: + * + * BLUETOOTH CORE SPECIFICATION Version 6.2 | Vol 4, Part E + * 7.8.157. LE Connection Rate Request command + * + * The Min_CE_Length and Max_CE_Length parameters provide the + * Controller with the expected minimum and maximum length of the + * connection events. The Controller is not required to use these + * values. + * + * So it is safe to just use the minimum. + */ + cp.min_ce_len = cpu_to_le16(0x0001); + cp.max_ce_len = cpu_to_le16(0x0001); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 8957ce7c21b7..c2065abf753e 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -13,7 +13,10 @@ static const struct class bt_class = { static void bt_link_release(struct device *dev) { struct hci_conn *conn = to_hci_conn(dev); + struct device *parent = dev->parent; + kfree(conn); + put_device(parent); } static const struct device_type bt_link = { @@ -21,6 +24,16 @@ static const struct device_type bt_link = { .release = bt_link_release, }; +/* + * The rfcomm tty device will possibly retain even when conn + * is down, and sysfs doesn't support move zombie device, + * so we should move the device before conn device is destroyed. + */ +static int __match_tty(struct device *dev, const void *data) +{ + return !strncmp(dev_name(dev), "rfcomm", 6); +} + void hci_conn_init_sysfs(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; @@ -29,7 +42,7 @@ void hci_conn_init_sysfs(struct hci_conn *conn) conn->dev.type = &bt_link; conn->dev.class = &bt_class; - conn->dev.parent = &hdev->dev; + conn->dev.parent = get_device(&hdev->dev); device_initialize(&conn->dev); } @@ -69,7 +82,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn) while (1) { struct device *dev; - dev = device_find_any_child(&conn->dev); + dev = device_find_child(&conn->dev, NULL, __match_tty); if (!dev) break; device_move(dev, NULL, DPM_ORDER_DEV_LAST); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ee459dd411f5..644e31160d55 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan) struct l2cap_ecred_conn_data { struct { struct l2cap_ecred_conn_req_hdr req; - __le16 scid[5]; + __le16 scid[L2CAP_ECRED_CONN_SCID_MAX]; } __packed pdu; struct l2cap_chan *chan; struct pid *pid; @@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) if (chan == conn->chan) return; - if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) + if (!test_bit(FLAG_DEFER_SETUP, &chan->flags)) return; pid = chan->ops->get_peer_pid(chan); @@ -1362,9 +1362,16 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT) return; + if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) + return; + if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) return; + /* Unreachable, checked in l2cap_connect (+timer drops it if reached) */ + if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid))) + return; + l2cap_ecred_init(chan, 0); /* Set the same ident so we can match on the rsp */ @@ -3894,6 +3901,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) struct l2cap_ecred_conn_rsp *rsp_flex = container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr); + if (chan->mode != L2CAP_MODE_EXT_FLOWCTL) + return; + /* Check if channel for outgoing connection or if it wasn't deferred * since in those cases it must be skipped. */ @@ -3904,6 +3914,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) /* Reset ident so only one response is sent */ chan->ident = 0; + /* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */ + if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid))) + rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM); + /* Include all channels pending with the same ident */ if (!rsp->pdu.rsp.result) rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid); @@ -5063,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); chan->ident = cmd->ident; + chan->mode = L2CAP_MODE_LE_FLOWCTL; if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { l2cap_state_change(chan, BT_CONNECT2); @@ -7369,6 +7384,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, goto done; } + mutex_lock(&conn->lock); + l2cap_chan_lock(chan); + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) { struct l2cap_chan_data data; @@ -7376,19 +7394,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, data.pid = chan->ops->get_peer_pid(chan); data.count = 1; - l2cap_chan_list(conn, l2cap_chan_by_pid, &data); + __l2cap_chan_list(conn, l2cap_chan_by_pid, &data); + + /* Leave room for non-deferred channel that ends the group. */ + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + data.count += 1; /* Check if there isn't too many channels being connected */ if (data.count > L2CAP_ECRED_CONN_SCID_MAX) { hci_conn_drop(hcon); err = -EPROTO; - goto done; + goto chan_unlock; } } - mutex_lock(&conn->lock); - l2cap_chan_lock(chan); - if (cid && __l2cap_get_chan_by_dcid(conn, cid)) { hci_conn_drop(hcon); err = -EBUSY; diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c index ded68568e6c9..d9dd722db3eb 100644 --- a/net/bluetooth/msft.c +++ b/net/bluetooth/msft.c @@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev) INIT_LIST_HEAD(&msft->handle_map); INIT_LIST_HEAD(&msft->address_filters); - hdev->msft_data = msft; mutex_init(&msft->filter_lock); + hdev->msft_data = msft; } void msft_release(struct hci_dev *hdev) diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c index dea56fffa1c1..9dcc97d63a6f 100644 --- a/net/bridge/br_cfm.c +++ b/net/bridge/br_cfm.c @@ -367,7 +367,7 @@ static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index, } /* note: already called with rcu_read_lock */ -static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) +int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) { u32 mdlevel, interval, size, index, max; const struct br_cfm_common_hdr *hdr; @@ -489,11 +489,6 @@ static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) return 1; } -static struct br_frame_type cfm_frame_type __read_mostly = { - .type = cpu_to_be16(ETH_P_CFM), - .frame_handler = br_cfm_frame_rx, -}; - int br_cfm_mep_create(struct net_bridge *br, const u32 instance, struct br_cfm_mep_create *const create, @@ -559,7 +554,7 @@ int br_cfm_mep_create(struct net_bridge *br, INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired); if (hlist_empty(&br->mep_list)) - br_add_frame(br, &cfm_frame_type); + br_opt_toggle(br, BROPT_CFM_ENABLED, true); hlist_add_tail_rcu(&mep->head, &br->mep_list); @@ -588,7 +583,7 @@ static void mep_delete_implementation(struct net_bridge *br, kfree_rcu(mep, rcu); if (hlist_empty(&br->mep_list)) - br_del_frame(br, &cfm_frame_type); + br_opt_toggle(br, BROPT_CFM_ENABLED, false); } int br_cfm_mep_delete(struct net_bridge *br, diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ff55dab73632..e01c44a90d84 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -503,7 +503,6 @@ void br_dev_setup(struct net_device *dev) spin_lock_init(&br->lock); INIT_LIST_HEAD(&br->port_list); INIT_HLIST_HEAD(&br->fdb_list); - INIT_HLIST_HEAD(&br->frame_type_list); #if IS_ENABLED(CONFIG_BRIDGE_MRP) INIT_HLIST_HEAD(&br->mrp_list); #endif diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index d87a5f9fa92b..8bed72baf161 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -317,17 +317,25 @@ frame_finish: return RX_HANDLER_CONSUMED; } +#define BR_CFM_MRP_OPTS \ + ((IS_ENABLED(CONFIG_BRIDGE_CFM) ? BIT(BROPT_CFM_ENABLED) : 0UL) | \ + (IS_ENABLED(CONFIG_BRIDGE_MRP) ? BIT(BROPT_MRP_ENABLED) : 0UL)) + /* Return 0 if the frame was not processed otherwise 1 * note: already called with rcu_read_lock */ static int br_process_frame_type(struct net_bridge_port *p, struct sk_buff *skb) { - struct br_frame_type *tmp; + struct net_bridge *br = p->br; + + if (skb->protocol == htons(ETH_P_CFM) && + br_opt_get(br, BROPT_CFM_ENABLED)) + return br_cfm_frame_rx(p, skb); - hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list) - if (unlikely(tmp->type == skb->protocol)) - return tmp->frame_handler(p, skb); + if (skb->protocol == htons(ETH_P_MRP) && + br_opt_get(br, BROPT_MRP_ENABLED)) + return br_mrp_process(p, skb); return 0; } @@ -425,7 +433,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) } } - if (unlikely(br_process_frame_type(p, skb))) + if (unlikely((READ_ONCE(p->br->options) & BR_CFM_MRP_OPTS) && + br_process_frame_type(p, skb))) return RX_HANDLER_PASS; forward: @@ -467,19 +476,3 @@ rx_handler_func_t *br_get_rx_handler(const struct net_device *dev) return br_handle_frame; } - -void br_add_frame(struct net_bridge *br, struct br_frame_type *ft) -{ - hlist_add_head_rcu(&ft->list, &br->frame_type_list); -} - -void br_del_frame(struct net_bridge *br, struct br_frame_type *ft) -{ - struct br_frame_type *tmp; - - hlist_for_each_entry(tmp, &br->frame_type_list, list) - if (ft == tmp) { - hlist_del_rcu(&ft->list); - return; - } -} diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index ef16d0703924..dce6efa96c4c 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -6,13 +6,6 @@ static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 }; static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 }; -static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); - -static struct br_frame_type mrp_frame_type __read_mostly = { - .type = cpu_to_be16(ETH_P_MRP), - .frame_handler = br_mrp_process, -}; - static bool br_mrp_is_ring_port(struct net_bridge_port *p_port, struct net_bridge_port *s_port, struct net_bridge_port *port) @@ -486,7 +479,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) kfree_rcu(mrp, rcu); if (hlist_empty(&br->mrp_list)) - br_del_frame(br, &mrp_frame_type); + br_opt_toggle(br, BROPT_MRP_ENABLED, false); } /* Adds a new MRP instance. @@ -536,7 +529,7 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance) rcu_assign_pointer(mrp->s_port, p); if (hlist_empty(&br->mrp_list)) - br_add_frame(br, &mrp_frame_type); + br_opt_toggle(br, BROPT_MRP_ENABLED, true); INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired); INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired); @@ -1241,7 +1234,7 @@ no_forward: * normal forwarding. * note: already called with rcu_read_lock */ -static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) { /* If there is no MRP instance do normal forwarding */ if (likely(!test_bit(BR_MRP_AWARE_BIT, &p->flags))) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 3ef5d8bbf552..2f9bb30e1a1f 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp, struct hlist_node *tmp; rcu_assign_pointer(*pp, pg->next); - hlist_del_init(&pg->mglist); + /* Keep ->next (held under multicast_lock, freed later by the GC work): + * a port->mglist teardown walk may have latched this node as its next, + * and deleting other groups of the same port must not truncate it. + */ + hlist_del_init_rcu(&pg->mglist); br_multicast_eht_clean_sets(pg); hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node) br_multicast_del_group_src(ent, false); @@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br, struct net_bridge_mdb_entry *mp; struct net_bridge_port_group *p; + /* A teardown walk over port->mglist can reach a group that an earlier + * iteration already deleted as a side effect. It is off mp->ports by + * now, so skip it instead of falling through to the WARN_ON() below. + */ + if (hlist_unhashed(&pg->mglist)) + return; + mp = br_mdb_ip_get(br, &pg->key.addr); if (WARN_ON(!mp)) return; @@ -1430,16 +1441,17 @@ struct net_bridge_port_group *br_multicast_new_port_group( goto free_out; } - rcu_assign_pointer(p->next, next); timer_setup(&p->timer, br_multicast_port_group_expired, 0); timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0); - hlist_add_head(&p->mglist, &port->mglist); if (src) memcpy(p->eth_addr, src, ETH_ALEN); else eth_broadcast_addr(p->eth_addr); + RCU_INIT_POINTER(p->next, next); + hlist_add_head_rcu(&p->mglist, &port->mglist); + return p; free_out: @@ -1454,11 +1466,11 @@ void br_multicast_del_port_group(struct net_bridge_port_group *p) struct net_bridge_port *port = p->key.port; __u16 vid = p->key.addr.vid; - hlist_del_init(&p->mglist); + hlist_del_init_rcu(&p->mglist); if (!br_multicast_is_star_g(&p->key.addr)) rhashtable_remove_fast(&port->br->sg_port_tbl, &p->rhnode, br_sg_port_rht_params); - kfree(p); + kfree_rcu(p, rcu); br_multicast_port_ngroups_dec(port, vid); } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d337b1cfb980..b01997ea9508 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -495,12 +495,13 @@ enum net_bridge_opts { BROPT_MST_ENABLED, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION, BROPT_FDB_LOCAL_VLAN_0, + BROPT_CFM_ENABLED, + BROPT_MRP_ENABLED, }; struct net_bridge { spinlock_t lock; spinlock_t hash_lock; - struct hlist_head frame_type_list; struct net_device *dev; unsigned long options; /* These fields are accessed on each packet */ @@ -932,16 +933,6 @@ int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); -struct br_frame_type { - __be16 type; - int (*frame_handler)(struct net_bridge_port *port, - struct sk_buff *skb); - struct hlist_node list; -}; - -void br_add_frame(struct net_bridge *br, struct br_frame_type *ft); -void br_del_frame(struct net_bridge *br, struct br_frame_type *ft); - static inline bool br_rx_handler_check_rcu(const struct net_device *dev) { return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev); @@ -2080,6 +2071,7 @@ int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p, bool br_mrp_enabled(struct net_bridge *br); void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p); int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br); +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); #else static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *attr, int cmd, @@ -2103,6 +2095,11 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br) return 0; } +static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) +{ + return 0; +} + #endif /* br_cfm.c */ @@ -2111,6 +2108,7 @@ int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *attr, int cmd, struct netlink_ext_ack *extack); bool br_cfm_created(struct net_bridge *br); void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p); +int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb); int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br); int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br, @@ -2135,6 +2133,12 @@ static inline void br_cfm_port_del(struct net_bridge *br, { } +static inline int br_cfm_frame_rx(struct net_bridge_port *port, + struct sk_buff *skb) +{ + return 0; +} + static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br) { return -EOPNOTSUPP; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 9c1b6cf8c36f..212e7797f9e4 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -1003,7 +1003,6 @@ static struct page *ceph_msg_data_iter_next(struct ceph_msg_data_cursor *cursor, * we'll get an iov_iter_get_pages2 variant that doesn't take * page refs. Until then, just put the page ref. */ - VM_BUG_ON_PAGE(!PageWriteback(page) && page_count(page) < 2, page); put_page(page); *length = min_t(size_t, len, cursor->resid); diff --git a/net/core/dev.c b/net/core/dev.c index 38336858c168..ecfbd72d5d1a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -9982,7 +9982,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len) unsigned int orig_len = dev->tx_queue_len; int res; - if (new_len != (unsigned int)new_len) + if (new_len > S16_MAX) return -ERANGE; if (new_len != orig_len) { @@ -12703,7 +12703,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat, int new_ifindex, struct netlink_ext_ack *extack) { - struct netdev_name_node *name_node; + struct netdev_name_node *name_node, *tmp; struct net *net_old = dev_net(dev); char new_name[IFNAMSIZ] = {}; int err, new_nsid; @@ -12749,13 +12749,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, } /* Check that none of the altnames conflicts. */ err = -EEXIST; - netdev_for_each_altname(dev, name_node) { - if (netdev_name_in_use(net, name_node->name)) { - NL_SET_ERR_MSG_FMT(extack, - "An interface with the altname %s exists in the target netns", - name_node->name); - goto out; + netdev_for_each_altname_safe(dev, name_node, tmp) { + if (!netdev_name_in_use(net, name_node->name)) + continue; + + if (!check_net(net_old)) { + __netdev_name_node_alt_destroy(name_node); + continue; } + + NL_SET_ERR_MSG_FMT(extack, + "An interface with the altname %s exists in the target netns", + name_node->name); + goto out; } /* Check that new_ifindex isn't used yet. */ @@ -13210,7 +13216,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = { static void __net_exit default_device_exit_net(struct net *net) { - struct netdev_name_node *name_node, *tmp; struct net_device *dev, *aux; /* * Push all migratable network devices back to the @@ -13234,10 +13239,6 @@ static void __net_exit default_device_exit_net(struct net *net) if (netdev_name_in_use(&init_net, fb_name)) snprintf(fb_name, IFNAMSIZ, "dev%%d"); - netdev_for_each_altname_safe(dev, name_node, tmp) - if (netdev_name_in_use(&init_net, name_node->name)) - __netdev_name_node_alt_destroy(name_node); - err = dev_change_net_namespace(dev, &init_net, fb_name); if (err) { pr_emerg("%s: failed to move %s to init_net: %d\n", diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c index 1b84385c04bd..d8c0a2867120 100644 --- a/net/core/gro_cells.c +++ b/net/core/gro_cells.c @@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) if (unlikely(!(dev->flags & IFF_UP))) goto drop; + skb_unset_transport_header(skb); + if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) { res = netif_rx(skb); goto unlock; diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 8f8956fb061b..08d7f35cf608 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -1073,7 +1073,8 @@ netmem_ref page_pool_alloc_frag_netmem(struct page_pool *pool, if (WARN_ON(size > max_size)) return 0; - size = ALIGN(size, dma_get_cache_alignment()); + size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(), + __alignof__(struct skb_shared_info))); *offset = pool->frag_offset; if (netmem && *offset + size > max_size) { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 81c5a6104dea..be9d1625bac3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2287,6 +2287,11 @@ nla_put_failure: return -EMSGSIZE; } +static const struct netlink_range_validation txqlen_range = { + .min = 0, + .max = S16_MAX, +}; + static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_UNSPEC] = { .strict_start_type = IFLA_DPLL_PIN }, [IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 }, @@ -2297,7 +2302,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, [IFLA_CARRIER] = { .type = NLA_U8 }, - [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = NLA_POLICY_FULL_RANGE(NLA_U32, &txqlen_range), [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, [IFLA_LINKMODE] = { .type = NLA_U8 }, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 966af3beed94..cc3b4b70288b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -6690,6 +6690,13 @@ int skb_mpls_pop(struct sk_buff *skb, __be16 next_proto, int mac_len, } skb->protocol = next_proto; + /* The last label is gone, so the inner header recorded by + * skb_mpls_push() no longer describes this packet. Drop it, or a + * later push keeps the stale offset. + */ + if (!eth_p_mpls(next_proto)) + skb->inner_protocol = 0; + return 0; } EXPORT_SYMBOL_GPL(skb_mpls_pop); diff --git a/net/core/sock.c b/net/core/sock.c index 1ad41904db25..fa60b7494c58 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2494,6 +2494,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority, #ifdef CONFIG_BPF_SYSCALL RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL); #endif +#if IS_ENABLED(CONFIG_INET_PSP) + RCU_INIT_POINTER(newsk->psp_assoc, NULL); +#endif /* SANITY */ if (likely(newsk->sk_net_refcnt)) { diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c index 300580c1a217..a8eda727bff1 100644 --- a/net/devlink/netlink.c +++ b/net/devlink/netlink.c @@ -251,7 +251,7 @@ devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs) if (!attrs[DEVLINK_ATTR_PARENT_DEV]) return ERR_PTR(-EINVAL); - tb = kcalloc(maxtype + 1, sizeof(*tb), GFP_KERNEL); + tb = kzalloc_objs(*tb, maxtype + 1); if (!tb) return ERR_PTR(-ENOMEM); diff --git a/net/devlink/param.c b/net/devlink/param.c index 8ca0f3ed646c..95ac9db8c993 100644 --- a/net/devlink/param.c +++ b/net/devlink/param.c @@ -330,13 +330,12 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink, int err; int i; - default_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1, - sizeof(*default_value), GFP_KERNEL); + default_value = kzalloc_objs(*default_value, + DEVLINK_PARAM_CMODE_MAX + 1); if (!default_value) return -ENOMEM; - param_value = kcalloc(DEVLINK_PARAM_CMODE_MAX + 1, - sizeof(*param_value), GFP_KERNEL); + param_value = kzalloc_objs(*param_value, DEVLINK_PARAM_CMODE_MAX + 1); if (!param_value) { kfree(default_value); return -ENOMEM; diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 411e3b57d16a..b7c49822ca88 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -373,6 +373,7 @@ static const struct dsa_device_ops brcm_legacy_fcs_netdev_ops = { .xmit = brcm_leg_fcs_tag_xmit, .rcv = brcm_leg_tag_rcv, .needed_headroom = BRCM_LEG_TAG_LEN, + .needed_tailroom = ETH_FCS_LEN, }; DSA_TAG_DRIVER(brcm_legacy_fcs_netdev_ops); diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c index 01c73b4b50dd..a546f70f9cc8 100644 --- a/net/hsr/hsr_slave.c +++ b/net/hsr/hsr_slave.c @@ -149,9 +149,12 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev, int res; /* Don't use promiscuous mode for offload since L2 frame forward - * happens at the offloaded hardware. + * happens at the offloaded hardware. The interlink port never + * gets forwarding offload (RedBox forwarding to/from it is done + * by this driver), so it still needs promiscuous mode to receive + * frames addressed to hsr_dev's MAC rather than its own. */ - if (!port->hsr->fwd_offloaded) { + if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK) { res = dev_set_promiscuity(dev, 1); if (res) return res; @@ -176,7 +179,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev, fail_rx_handler: netdev_upper_dev_unlink(dev, hsr_dev); fail_upper_dev_link: - if (!port->hsr->fwd_offloaded) + if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK) dev_set_promiscuity(dev, -1); return res; @@ -240,7 +243,7 @@ void hsr_del_port(struct hsr_port *port) netdev_update_features(master->dev); dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); netdev_rx_handler_unregister(port->dev); - if (!port->hsr->fwd_offloaded) + if (!port->hsr->fwd_offloaded || port->type == HSR_PT_INTERLINK) dev_set_promiscuity(port->dev, -1); if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) vlan_vids_del_by_dev(port->dev, master->dev); diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index 018929563c6b..6a8d6852cb93 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -150,7 +150,7 @@ static int lowpan_newlink(struct net_device *ldev, wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK])); if (!wdev) return -ENODEV; - if (wdev->type != ARPHRD_IEEE802154) { + if (wdev->type != ARPHRD_IEEE802154 || !wdev->ieee802154_ptr) { dev_put(wdev); return -EINVAL; } diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 4edb0dca7be8..060501b376a8 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -214,6 +214,8 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, return 1; } +#define FIB_MAX_AUTO_TABLE_ID 4096 + static struct fib_table *fib_empty_table(struct net *net) { u32 id = 1; @@ -222,7 +224,7 @@ static struct fib_table *fib_empty_table(struct net *net) if (!fib_get_table(net, id)) return fib_new_table(net, id); - if (id++ == RT_TABLE_MAX) + if (id++ == FIB_MAX_AUTO_TABLE_ID) break; } return NULL; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 0483519b7fb0..7a362f2e2c2b 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -874,7 +874,7 @@ static void fib_rebalance(struct fib_info *fi) change_nexthops(fi) { int upper_bound; - if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) { + if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) { upper_bound = -1; } else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) && nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) { diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index b80b8a92f46e..d56355aca797 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im) } } +#define pmc_dereference(e, pmc) \ + rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \ + lockdep_is_held(&(pmc)->interface->mc_tomb_lock)) + #define for_each_pmc_rcu(in_dev, pmc) \ for (pmc = rcu_dereference(in_dev->mc_list); \ pmc != NULL; \ @@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im) pmc != NULL; \ pmc = rtnl_dereference(pmc->next_rcu)) +#define for_each_psf_mclock(pmc, psf) \ + for (psf = pmc_dereference((pmc)->sources, pmc); \ + psf; \ + psf = pmc_dereference(psf->sf_next, pmc)) + +#define for_each_psf_rcu(im, psf) \ + for (psf = rcu_dereference((im)->sources); \ + psf; \ + psf = rcu_dereference(psf->sf_next)) + +#define for_each_psf_tomb(pmc, psf) \ + for (psf = pmc_dereference((pmc)->tomb, pmc); \ + psf; \ + psf = pmc_dereference(psf->sf_next, pmc)) + static void ip_sf_list_clear_all(struct ip_sf_list *psf) { struct ip_sf_list *next; while (psf) { - next = psf->sf_next; - kfree(psf); + next = rcu_dereference_protected(psf->sf_next, 1); + kfree_rcu(psf, rcu); psf = next; } } @@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) struct ip_sf_list *psf; int scount = 0; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (!is_in(pmc, psf, type, gdeleted, sdeleted)) continue; scount++; @@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, struct net *net = dev_net(dev); struct igmpv3_report *pih; struct igmpv3_grec *pgr = NULL; - struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list; + struct ip_sf_list *psf, *psf_next, *psf_prev; + struct ip_sf_list __rcu **psf_list; int scount, stotal, first, isquery, truncate; unsigned int mtu; @@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, psf_list = sdeleted ? &pmc->tomb : &pmc->sources; - if (!*psf_list) + if (!rcu_access_pointer(*psf_list)) goto empty_source; pih = skb ? igmpv3_report_hdr(skb) : NULL; @@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, } first = 1; psf_prev = NULL; - for (psf = *psf_list; psf; psf = psf_next) { + for (psf = pmc_dereference(*psf_list, pmc); + psf; + psf = psf_next) { __be32 *psrc; - psf_next = psf->sf_next; + psf_next = pmc_dereference(psf->sf_next, pmc); if (!is_in(pmc, psf, type, gdeleted, sdeleted)) { psf_prev = psf; @@ -583,10 +605,12 @@ decrease_sf_crcount: psf->sf_crcount--; if ((sdeleted || gdeleted) && psf->sf_crcount == 0) { if (psf_prev) - psf_prev->sf_next = psf->sf_next; + rcu_assign_pointer(psf_prev->sf_next, + psf_next); else - *psf_list = psf->sf_next; - kfree(psf); + rcu_assign_pointer(*psf_list, + psf_next); + kfree_rcu(psf, rcu); continue; } } @@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc) /* * remove zero-count source records from a source filter list */ -static void igmpv3_clear_zeros(struct ip_sf_list **ppsf) +static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf) { struct ip_sf_list *psf_prev, *psf_next, *psf; psf_prev = NULL; - for (psf = *ppsf; psf; psf = psf_next) { - psf_next = psf->sf_next; + for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) { + psf_next = rcu_dereference_protected(psf->sf_next, 1); if (psf->sf_crcount == 0) { if (psf_prev) - psf_prev->sf_next = psf->sf_next; + rcu_assign_pointer(psf_prev->sf_next, psf_next); else - *ppsf = psf->sf_next; - kfree(psf); - } else + rcu_assign_pointer(*ppsf, psf_next); + kfree_rcu(psf, rcu); + } else { psf_prev = psf; + } } } static void kfree_pmc(struct ip_mc_list *pmc) { - ip_sf_list_clear_all(pmc->sources); - ip_sf_list_clear_all(pmc->tomb); + ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1)); + ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1)); kfree(pmc); } @@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev) igmpv3_clear_zeros(&pmc->sources); } } - if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) { + if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) && + !rcu_access_pointer(pmc->sources)) { if (pmc_prev) pmc_prev->next = pmc_next; else @@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) int i, scount; scount = 0; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (scount == nsrcs) break; for (i = 0; i < nsrcs; i++) { @@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs) /* mark INCLUDE-mode sources */ scount = 0; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (scount == nsrcs) break; for (i = 0; i < nsrcs; i++) @@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im, if (pmc->sfmode == MCAST_INCLUDE) { struct ip_sf_list *psf; + for_each_psf_mclock(im, psf) + psf->sf_crcount = pmc->crcount; pmc->tomb = im->tomb; pmc->sources = im->sources; - im->tomb = im->sources = NULL; - for (psf = pmc->sources; psf; psf = psf->sf_next) - psf->sf_crcount = pmc->crcount; + RCU_INIT_POINTER(im->tomb, NULL); + RCU_INIT_POINTER(im->sources, NULL); } spin_unlock_bh(&im->lock); @@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im) if (pmc) { im->interface = pmc->interface; if (im->sfmode == MCAST_INCLUDE) { - swap(im->tomb, pmc->tomb); - swap(im->sources, pmc->sources); - for (psf = im->sources; psf; psf = psf->sf_next) + struct ip_sf_list *sources, *tomb; + + tomb = rcu_replace_pointer(im->tomb, + rcu_dereference_protected(pmc->tomb, 1), + lockdep_is_held(&im->lock)); + rcu_assign_pointer(pmc->tomb, tomb); + + sources = rcu_replace_pointer(im->sources, + rcu_dereference_protected(pmc->sources, 1), + lockdep_is_held(&im->lock)); + rcu_assign_pointer(pmc->sources, sources); + for_each_psf_mclock(im, psf) psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); } else { @@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev) struct ip_sf_list *psf; spin_lock_bh(&pmc->lock); - psf = pmc->tomb; - pmc->tomb = NULL; + psf = pmc_dereference(pmc->tomb, pmc); + RCU_INIT_POINTER(pmc->tomb, NULL); spin_unlock_bh(&pmc->lock); ip_sf_list_clear_all(psf); } @@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, int rv = 0; psf_prev = NULL; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (psf->sf_inaddr == *psfsrc) break; psf_prev = psf; @@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, /* source filter not found, or count wrong => bug */ return -ESRCH; } - psf->sf_count[sfmode]--; + WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1); if (psf->sf_count[sfmode] == 0) { ip_rt_multicast_event(pmc->interface); } @@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, /* no more filters for this source */ if (psf_prev) - psf_prev->sf_next = psf->sf_next; + rcu_assign_pointer(psf_prev->sf_next, + pmc_dereference(psf->sf_next, pmc)); else - pmc->sources = psf->sf_next; + rcu_assign_pointer(pmc->sources, + pmc_dereference(psf->sf_next, pmc)); #ifdef CONFIG_IP_MULTICAST if (psf->sf_oldin && !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) { - psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); - psf->sf_next = pmc->tomb; - pmc->tomb = psf; - rv = 1; - } else + struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC); + + if (dpsf) { + *dpsf = *psf; + dpsf->sf_crcount = in_dev->mr_qrv ?: + READ_ONCE(net->ipv4.sysctl_igmp_qrv); + rcu_assign_pointer(dpsf->sf_next, + pmc_dereference(pmc->tomb, pmc)); + rcu_assign_pointer(pmc->tomb, dpsf); + rv = 1; + } + } #endif - kfree(psf); + kfree_rcu(psf, rcu); } return rv; } @@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, err = -EINVAL; if (!pmc->sfcount[sfmode]) goto out_unlock; - pmc->sfcount[sfmode]--; + WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1); } err = 0; for (i = 0; i < sfcount; i++) { @@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode, #ifdef CONFIG_IP_MULTICAST pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); - for (psf = pmc->sources; psf; psf = psf->sf_next) + for_each_psf_mclock(pmc, psf) psf->sf_crcount = 0; igmp_ifc_event(pmc->interface); } else if (sf_setstate(pmc) || changerec) { @@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, struct ip_sf_list *psf, *psf_prev; psf_prev = NULL; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (psf->sf_inaddr == *psfsrc) break; psf_prev = psf; @@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode, if (!psf) return -ENOBUFS; psf->sf_inaddr = *psfsrc; - if (psf_prev) { - psf_prev->sf_next = psf; - } else - pmc->sources = psf; + if (psf_prev) + rcu_assign_pointer(psf_prev->sf_next, psf); + else + rcu_assign_pointer(pmc->sources, psf); } - psf->sf_count[sfmode]++; + WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1); if (psf->sf_count[sfmode] == 1) { ip_rt_multicast_event(pmc->interface); } @@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc) struct ip_sf_list *psf; int mca_xcount = pmc->sfcount[MCAST_EXCLUDE]; - for (psf = pmc->sources; psf; psf = psf->sf_next) + for_each_psf_mclock(pmc, psf) { if (pmc->sfcount[MCAST_EXCLUDE]) { psf->sf_oldin = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && !psf->sf_count[MCAST_INCLUDE]; - } else + } else { psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0; + } + } } static int sf_setstate(struct ip_mc_list *pmc) @@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc) int new_in, rv; rv = 0; - for (psf = pmc->sources; psf; psf = psf->sf_next) { + for_each_psf_mclock(pmc, psf) { if (pmc->sfcount[MCAST_EXCLUDE]) { new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] && !psf->sf_count[MCAST_INCLUDE]; - } else + } else { new_in = psf->sf_count[MCAST_INCLUDE] != 0; + } if (new_in) { if (!psf->sf_oldin) { struct ip_sf_list *prev = NULL; - for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) { + for_each_psf_tomb(pmc, dpsf) { if (dpsf->sf_inaddr == psf->sf_inaddr) break; prev = dpsf; } if (dpsf) { + struct ip_sf_list *dpsf_next; + + dpsf_next = pmc_dereference(dpsf->sf_next, pmc); if (prev) - prev->sf_next = dpsf->sf_next; + rcu_assign_pointer(prev->sf_next, dpsf_next); else - pmc->tomb = dpsf->sf_next; - kfree(dpsf); + rcu_assign_pointer(pmc->tomb, dpsf_next); + kfree_rcu(dpsf, rcu); } psf->sf_crcount = qrv; rv++; @@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc) * add or update "delete" records if an active filter * is now inactive */ - for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) + for_each_psf_tomb(pmc, dpsf) { if (dpsf->sf_inaddr == psf->sf_inaddr) break; + } if (!dpsf) { dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC); if (!dpsf) continue; *dpsf = *psf; /* pmc->lock held by callers */ - dpsf->sf_next = pmc->tomb; - pmc->tomb = dpsf; + rcu_assign_pointer(dpsf->sf_next, + pmc_dereference(pmc->tomb, pmc)); + rcu_assign_pointer(pmc->tomb, dpsf); } dpsf->sf_crcount = qrv; rv++; @@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, #endif isexclude = pmc->sfmode == MCAST_EXCLUDE; if (!delta) - pmc->sfcount[sfmode]++; + WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1); err = 0; for (i = 0; i < sfcount; i++) { err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]); @@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, int j; if (!delta) - pmc->sfcount[sfmode]--; + WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1); for (j = 0; j < i; j++) (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]); } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) { @@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode, pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv); WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount); - for (psf = pmc->sources; psf; psf = psf->sf_next) + for_each_psf_mclock(pmc, psf) psf->sf_crcount = 0; igmp_ifc_event(in_dev); } else if (sf_setstate(pmc)) { @@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc) struct ip_sf_list *tomb, *sources; spin_lock_bh(&pmc->lock); - tomb = pmc->tomb; - pmc->tomb = NULL; - sources = pmc->sources; - pmc->sources = NULL; + tomb = pmc_dereference(pmc->tomb, pmc); + RCU_INIT_POINTER(pmc->tomb, NULL); + sources = pmc_dereference(pmc->sources, pmc); + RCU_INIT_POINTER(pmc->sources, NULL); pmc->sfmode = MCAST_EXCLUDE; - pmc->sfcount[MCAST_INCLUDE] = 0; - pmc->sfcount[MCAST_EXCLUDE] = 1; + WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0); + WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1); spin_unlock_bh(&pmc->lock); ip_sf_list_clear_all(tomb); @@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u rv = 1; } else if (im) { if (src_addr) { - spin_lock_bh(&im->lock); - for (psf = im->sources; psf; psf = psf->sf_next) { + for_each_psf_rcu(im, psf) { if (psf->sf_inaddr == src_addr) break; } if (psf) - rv = psf->sf_count[MCAST_INCLUDE] || - psf->sf_count[MCAST_EXCLUDE] != - im->sfcount[MCAST_EXCLUDE]; + rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) || + READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) != + READ_ONCE(im->sfcount[MCAST_EXCLUDE]); else - rv = im->sfcount[MCAST_EXCLUDE] != 0; - spin_unlock_bh(&im->lock); - } else + rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0; + } else { rv = 1; /* unspecified source; tentatively allow */ + } } return rv; } @@ -3043,7 +3095,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) im = rcu_dereference(idev->mc_list); if (likely(im)) { spin_lock_bh(&im->lock); - psf = im->sources; + psf = pmc_dereference(im->sources, im); if (likely(psf)) { state->im = im; state->idev = idev; @@ -3059,7 +3111,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l { struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); - psf = psf->sf_next; + psf = pmc_dereference(psf->sf_next, state->im); while (!psf) { spin_unlock_bh(&state->im->lock); state->im = state->im->next; @@ -3075,7 +3127,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l state->im = rcu_dereference(state->idev->mc_list); } spin_lock_bh(&state->im->lock); - psf = state->im->sources; + psf = pmc_dereference(state->im->sources, state->im); } out: return psf; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index c17e57ec7d5c..b286ee429da8 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -235,6 +235,8 @@ void fqdir_pre_exit(struct fqdir *fqdir) rhashtable_walk_start(&hti); while ((fq = rhashtable_walk_next(&hti))) { + int refs = 0; + if (IS_ERR(fq)) { if (PTR_ERR(fq) != -EAGAIN) break; @@ -242,8 +244,12 @@ void fqdir_pre_exit(struct fqdir *fqdir) } spin_lock_bh(&fq->lock); if (!(fq->flags & INET_FRAG_COMPLETE)) + inet_frag_kill(fq, &refs); + + if (fq->flags & INET_FRAG_HASH_DEAD) inet_frag_queue_flush(fq, 0); spin_unlock_bh(&fq->lock); + inet_frag_putn(fq, refs); } rhashtable_walk_stop(&hti); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 74e095b6b7ca..a24cc8ee11d3 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1303,6 +1303,7 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, cork->fragsize = ip_sk_use_pmtu(sk) ? dst4_mtu(&rt->dst) : READ_ONCE(rt->dst.dev->mtu); + cork->fragsize = min(cork->fragsize, IP_MAX_MTU); if (!inetdev_valid_mtu(cork->fragsize)) return -ENETUNREACH; diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index d3c677e9bff2..5168d546ea2f 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -268,6 +268,9 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0); skb_reset_mac_header(skb); + if (skb_valid_dst(skb)) + skb_dst_drop(skb); + return skb->len; } @@ -371,6 +374,9 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0); skb_reset_mac_header(skb); + if (skb_valid_dst(skb)) + skb_dst_drop(skb); + return skb->len; } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index e5f2b1c6150d..b9c544d48c45 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -3376,7 +3376,8 @@ int __init ip_mr_init(void) { int err; - mrt_cachep = KMEM_CACHE(mfc_cache, SLAB_HWCACHE_ALIGN | SLAB_PANIC); + mrt_cachep = KMEM_CACHE(mfc_cache, + SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT); err = register_pernet_subsys(&ipmr_net_ops); if (err) diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c index 867b24beded1..a0ec6d19a237 100644 --- a/net/ipv4/ipmr_base.c +++ b/net/ipv4/ipmr_base.c @@ -52,7 +52,7 @@ mr_table_alloc(struct net *net, u32 id, struct mr_table *mrt; int err; - mrt = kzalloc_obj(*mrt); + mrt = kzalloc_obj(*mrt, GFP_KERNEL_ACCOUNT); if (!mrt) return ERR_PTR(-ENOMEM); mrt->id = id; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index a87e07e80d0d..db307fa49f3f 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -23,7 +23,6 @@ #include <linux/init.h> #include <linux/mutex.h> #include <linux/err.h> -#include <net/compat.h> #include <net/sock.h> #include <linux/uaccess.h> @@ -724,80 +723,6 @@ static int copy_entries_to_user(unsigned int total_size, return ret; } -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT -static void compat_standard_from_user(void *dst, const void *src) -{ - int v = *(compat_int_t *)src; - - if (v > 0) - v += xt_compat_calc_jump(NFPROTO_ARP, v); - memcpy(dst, &v, sizeof(v)); -} - -static int compat_standard_to_user(void __user *dst, const void *src) -{ - compat_int_t cv = *(int *)src; - - if (cv > 0) - cv -= xt_compat_calc_jump(NFPROTO_ARP, cv); - return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0; -} - -static int compat_calc_entry(const struct arpt_entry *e, - const struct xt_table_info *info, - const void *base, struct xt_table_info *newinfo) -{ - const struct xt_entry_target *t; - unsigned int entry_offset; - int off, i, ret; - - off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); - entry_offset = (void *)e - base; - - t = arpt_get_target_c(e); - off += xt_compat_target_offset(t->u.kernel.target); - newinfo->size -= off; - ret = xt_compat_add_offset(NFPROTO_ARP, entry_offset, off); - if (ret) - return ret; - - for (i = 0; i < NF_ARP_NUMHOOKS; i++) { - if (info->hook_entry[i] && - (e < (struct arpt_entry *)(base + info->hook_entry[i]))) - newinfo->hook_entry[i] -= off; - if (info->underflow[i] && - (e < (struct arpt_entry *)(base + info->underflow[i]))) - newinfo->underflow[i] -= off; - } - return 0; -} - -static int compat_table_info(const struct xt_table_info *info, - struct xt_table_info *newinfo) -{ - struct arpt_entry *iter; - const void *loc_cpu_entry; - int ret; - - if (!newinfo || !info) - return -EINVAL; - - /* we dont care about newinfo->entries */ - memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); - newinfo->initial_entries = 0; - loc_cpu_entry = info->entries; - ret = xt_compat_init_offsets(NFPROTO_ARP, info->number); - if (ret) - return ret; - xt_entry_foreach(iter, loc_cpu_entry, info->size) { - ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo); - if (ret != 0) - return ret; - } - return 0; -} -#endif - static int get_info(struct net *net, void __user *user, const int *len) { char name[XT_TABLE_MAXNAMELEN]; @@ -811,23 +736,11 @@ static int get_info(struct net *net, void __user *user, const int *len) return -EFAULT; name[XT_TABLE_MAXNAMELEN-1] = '\0'; -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - if (in_compat_syscall()) - xt_compat_lock(NFPROTO_ARP); -#endif t = xt_request_find_table_lock(net, NFPROTO_ARP, name); if (!IS_ERR(t)) { struct arpt_getinfo info; const struct xt_table_info *private = t->private; -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - struct xt_table_info tmp; - if (in_compat_syscall()) { - ret = compat_table_info(private, &tmp); - xt_compat_flush_offsets(NFPROTO_ARP); - private = &tmp; - } -#endif memset(&info, 0, sizeof(info)); info.valid_hooks = t->valid_hooks; memcpy(info.hook_entry, private->hook_entry, @@ -846,10 +759,7 @@ static int get_info(struct net *net, void __user *user, const int *len) module_put(t->me); } else ret = PTR_ERR(t); -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - if (in_compat_syscall()) - xt_compat_unlock(NFPROTO_ARP); -#endif + return ret; } @@ -1059,367 +969,6 @@ static int do_add_counters(struct net *net, sockptr_t arg, unsigned int len) return ret; } -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT -struct compat_arpt_replace { - char name[XT_TABLE_MAXNAMELEN]; - u32 valid_hooks; - u32 num_entries; - u32 size; - u32 hook_entry[NF_ARP_NUMHOOKS]; - u32 underflow[NF_ARP_NUMHOOKS]; - u32 num_counters; - compat_uptr_t counters; - struct compat_arpt_entry entries[]; -}; - -static inline void compat_release_entry(struct compat_arpt_entry *e) -{ - struct xt_entry_target *t; - - t = compat_arpt_get_target(e); - module_put(t->u.kernel.target->me); -} - -static int -check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, - struct xt_table_info *newinfo, - unsigned int *size, - const unsigned char *base, - const unsigned char *limit) -{ - struct xt_entry_target *t; - struct xt_target *target; - unsigned int entry_offset; - int ret, off; - - if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 || - (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit || - (unsigned char *)e + e->next_offset > limit) - return -EINVAL; - - if (e->next_offset < sizeof(struct compat_arpt_entry) + - sizeof(struct compat_xt_entry_target)) - return -EINVAL; - - if (!arp_checkentry(&e->arp)) - return -EINVAL; - - ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset, - e->next_offset); - if (ret) - return ret; - - off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); - entry_offset = (void *)e - (void *)base; - - t = compat_arpt_get_target(e); - target = xt_request_find_target(NFPROTO_ARP, t->u.user.name, - t->u.user.revision); - if (IS_ERR(target)) { - ret = PTR_ERR(target); - goto out; - } - t->u.kernel.target = target; - - off += xt_compat_target_offset(target); - *size += off; - ret = xt_compat_add_offset(NFPROTO_ARP, entry_offset, off); - if (ret) - goto release_target; - - return 0; - -release_target: - module_put(t->u.kernel.target->me); -out: - return ret; -} - -static void -compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, - unsigned int *size, - struct xt_table_info *newinfo, unsigned char *base) -{ - struct xt_entry_target *t; - struct arpt_entry *de; - unsigned int origsize; - int h; - - origsize = *size; - de = *dstptr; - memcpy(de, e, sizeof(struct arpt_entry)); - memcpy(&de->counters, &e->counters, sizeof(e->counters)); - - *dstptr += sizeof(struct arpt_entry); - *size += sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); - - de->target_offset = e->target_offset - (origsize - *size); - t = compat_arpt_get_target(e); - xt_compat_target_from_user(t, dstptr, size); - - de->next_offset = e->next_offset - (origsize - *size); - for (h = 0; h < NF_ARP_NUMHOOKS; h++) { - if ((unsigned char *)de - base < newinfo->hook_entry[h]) - newinfo->hook_entry[h] -= origsize - *size; - if ((unsigned char *)de - base < newinfo->underflow[h]) - newinfo->underflow[h] -= origsize - *size; - } -} - -static int translate_compat_table(struct net *net, - struct xt_table_info **pinfo, - void **pentry0, - const struct compat_arpt_replace *compatr) -{ - unsigned int i, j; - struct xt_table_info *newinfo, *info; - void *pos, *entry0, *entry1; - struct compat_arpt_entry *iter0; - struct arpt_replace repl; - unsigned int size; - int ret; - - info = *pinfo; - entry0 = *pentry0; - size = compatr->size; - info->number = compatr->num_entries; - - j = 0; - xt_compat_lock(NFPROTO_ARP); - ret = xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries); - if (ret) - goto out_unlock; - /* Walk through entries, checking offsets. */ - xt_entry_foreach(iter0, entry0, compatr->size) { - ret = check_compat_entry_size_and_hooks(iter0, info, &size, - entry0, - entry0 + compatr->size); - if (ret != 0) - goto out_unlock; - ++j; - } - - ret = -EINVAL; - if (j != compatr->num_entries) - goto out_unlock; - - ret = -ENOMEM; - newinfo = xt_alloc_table_info(size); - if (!newinfo) - goto out_unlock; - - memset(newinfo->entries, 0, size); - - newinfo->number = compatr->num_entries; - for (i = 0; i < NF_ARP_NUMHOOKS; i++) { - newinfo->hook_entry[i] = compatr->hook_entry[i]; - newinfo->underflow[i] = compatr->underflow[i]; - } - entry1 = newinfo->entries; - pos = entry1; - size = compatr->size; - xt_entry_foreach(iter0, entry0, compatr->size) - compat_copy_entry_from_user(iter0, &pos, &size, - newinfo, entry1); - - /* all module references in entry0 are now gone */ - - xt_compat_flush_offsets(NFPROTO_ARP); - xt_compat_unlock(NFPROTO_ARP); - - memcpy(&repl, compatr, sizeof(*compatr)); - - for (i = 0; i < NF_ARP_NUMHOOKS; i++) { - repl.hook_entry[i] = newinfo->hook_entry[i]; - repl.underflow[i] = newinfo->underflow[i]; - } - - repl.num_counters = 0; - repl.counters = NULL; - repl.size = newinfo->size; - ret = translate_table(net, newinfo, entry1, &repl); - if (ret) - goto free_newinfo; - - *pinfo = newinfo; - *pentry0 = entry1; - xt_free_table_info(info); - return 0; - -free_newinfo: - xt_free_table_info(newinfo); - return ret; -out_unlock: - xt_compat_flush_offsets(NFPROTO_ARP); - xt_compat_unlock(NFPROTO_ARP); - xt_entry_foreach(iter0, entry0, compatr->size) { - if (j-- == 0) - break; - compat_release_entry(iter0); - } - return ret; -} - -static int compat_do_replace(struct net *net, sockptr_t arg, unsigned int len) -{ - int ret; - struct compat_arpt_replace tmp; - struct xt_table_info *newinfo; - void *loc_cpu_entry; - struct arpt_entry *iter; - - if (len < sizeof(tmp)) - return -EINVAL; - if (copy_from_sockptr(&tmp, arg, sizeof(tmp)) != 0) - return -EFAULT; - - /* overflow check */ - if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) - return -ENOMEM; - if (tmp.num_counters == 0) - return -EINVAL; - if ((u64)len < (u64)tmp.size + sizeof(tmp)) - return -EINVAL; - - tmp.name[sizeof(tmp.name)-1] = 0; - - newinfo = xt_alloc_table_info(tmp.size); - if (!newinfo) - return -ENOMEM; - - loc_cpu_entry = newinfo->entries; - if (copy_from_sockptr_offset(loc_cpu_entry, arg, sizeof(tmp), - tmp.size) != 0) { - ret = -EFAULT; - goto free_newinfo; - } - - ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp); - if (ret != 0) - goto free_newinfo; - - ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo, - tmp.num_counters, compat_ptr(tmp.counters)); - if (ret) - goto free_newinfo_untrans; - return 0; - - free_newinfo_untrans: - xt_entry_foreach(iter, loc_cpu_entry, newinfo->size) - cleanup_entry(iter, net); - free_newinfo: - xt_free_table_info(newinfo); - return ret; -} - -static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, - compat_uint_t *size, - struct xt_counters *counters, - unsigned int i) -{ - struct xt_entry_target *t; - struct compat_arpt_entry __user *ce; - u_int16_t target_offset, next_offset; - compat_uint_t origsize; - int ret; - - origsize = *size; - ce = *dstptr; - if (copy_to_user(ce, e, offsetof(struct compat_arpt_entry, counters)) || - copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) - return -EFAULT; - - *dstptr += sizeof(struct compat_arpt_entry); - *size -= sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); - - target_offset = e->target_offset - (origsize - *size); - - t = arpt_get_target(e); - ret = xt_compat_target_to_user(t, dstptr, size); - if (ret) - return ret; - next_offset = e->next_offset - (origsize - *size); - if (put_user(target_offset, &ce->target_offset) != 0 || - put_user(next_offset, &ce->next_offset) != 0) - return -EFAULT; - return 0; -} - -static int compat_copy_entries_to_user(unsigned int total_size, - struct xt_table *table, - void __user *userptr) -{ - struct xt_counters *counters; - const struct xt_table_info *private = table->private; - void __user *pos; - unsigned int size; - int ret = 0; - unsigned int i = 0; - struct arpt_entry *iter; - - counters = alloc_counters(table); - if (IS_ERR(counters)) - return PTR_ERR(counters); - - pos = userptr; - size = total_size; - xt_entry_foreach(iter, private->entries, total_size) { - ret = compat_copy_entry_to_user(iter, &pos, - &size, counters, i++); - if (ret != 0) - break; - } - vfree(counters); - return ret; -} - -struct compat_arpt_get_entries { - char name[XT_TABLE_MAXNAMELEN]; - compat_uint_t size; - struct compat_arpt_entry entrytable[]; -}; - -static int compat_get_entries(struct net *net, - struct compat_arpt_get_entries __user *uptr, - int *len) -{ - int ret; - struct compat_arpt_get_entries get; - struct xt_table *t; - - if (*len < sizeof(get)) - return -EINVAL; - if (copy_from_user(&get, uptr, sizeof(get)) != 0) - return -EFAULT; - if (*len != sizeof(struct compat_arpt_get_entries) + get.size) - return -EINVAL; - - get.name[sizeof(get.name) - 1] = '\0'; - - xt_compat_lock(NFPROTO_ARP); - t = xt_find_table_lock(net, NFPROTO_ARP, get.name); - if (!IS_ERR(t)) { - const struct xt_table_info *private = t->private; - struct xt_table_info info; - - ret = compat_table_info(private, &info); - if (!ret && get.size == info.size) { - ret = compat_copy_entries_to_user(private->size, - t, uptr->entrytable); - } else if (!ret) - ret = -EAGAIN; - - xt_compat_flush_offsets(NFPROTO_ARP); - module_put(t->me); - xt_table_unlock(t); - } else - ret = PTR_ERR(t); - - xt_compat_unlock(NFPROTO_ARP); - return ret; -} -#endif - static int do_arpt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, unsigned int len) { @@ -1432,12 +981,7 @@ static int do_arpt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, switch (cmd) { case ARPT_SO_SET_REPLACE: -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - if (in_compat_syscall()) - ret = compat_do_replace(sock_net(sk), arg, len); - else -#endif - ret = do_replace(sock_net(sk), arg, len); + ret = do_replace(sock_net(sk), arg, len); break; case ARPT_SO_SET_ADD_COUNTERS: @@ -1466,12 +1010,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len break; case ARPT_SO_GET_ENTRIES: -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - if (in_compat_syscall()) - ret = compat_get_entries(sock_net(sk), user, len); - else -#endif - ret = get_entries(sock_net(sk), user, len); + ret = get_entries(sock_net(sk), user, len); break; case ARPT_SO_GET_REVISION_TARGET: { @@ -1568,11 +1107,6 @@ static struct xt_target arpt_builtin_tg[] __read_mostly = { .name = XT_STANDARD_TARGET, .targetsize = sizeof(int), .family = NFPROTO_ARP, -#ifdef CONFIG_NETFILTER_XTABLES_COMPAT - .compatsize = sizeof(compat_int_t), - .compat_from_user = compat_standard_from_user, - .compat_to_user = compat_standard_to_user, -#endif }, { .name = XT_ERROR_TARGET, diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index a7c2b8dced4e..42e55b5a755e 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -2036,7 +2036,7 @@ remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, { struct nh_grp_entry *nhges, *new_nhges; struct nexthop *nhp = nhge->nh_parent; - struct netlink_ext_ack extack; + struct netlink_ext_ack extack = {}; struct nexthop *nh = nhge->nh; struct nh_group *nhg, *newg; int i, j, err; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b4237d0e994d..562752352afe 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1169,8 +1169,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) zc = MSG_SPLICE_PAGES; } - if (!sockc_err && sockc.dmabuf_id && - (!(flags & MSG_ZEROCOPY) || !sock_flag(sk, SOCK_ZEROCOPY))) { + if (!sockc_err && sockc.dmabuf_id && (zc != MSG_ZEROCOPY || !binding)) { err = -EINVAL; goto out_err; } @@ -3182,8 +3181,7 @@ void __tcp_close(struct sock *sk, long timeout) /* Unread data was tossed, zap the connection. */ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); tcp_set_state(sk, TCP_CLOSE); - tcp_send_active_reset(sk, sk->sk_allocation, - SK_RST_REASON_TCP_ABORT_ON_CLOSE); + tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_CLOSE); } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); @@ -3257,7 +3255,7 @@ adjudge_to_death: struct tcp_sock *tp = tcp_sk(sk); if (READ_ONCE(tp->linger2) < 0) { tcp_set_state(sk, TCP_CLOSE); - tcp_send_active_reset(sk, GFP_ATOMIC, + tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_LINGER); __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONLINGER); @@ -3276,7 +3274,7 @@ adjudge_to_death: if (sk->sk_state != TCP_CLOSE) { if (tcp_check_oom(sk, 0)) { tcp_set_state(sk, TCP_CLOSE); - tcp_send_active_reset(sk, GFP_ATOMIC, + tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_MEMORY); __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); @@ -3377,14 +3375,14 @@ int tcp_disconnect(struct sock *sk, int flags) } else if (unlikely(tp->repair)) { WRITE_ONCE(sk->sk_err, ECONNABORTED); } else if (tcp_need_reset(old_state)) { - tcp_send_active_reset(sk, gfp_any(), SK_RST_REASON_TCP_STATE); + tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE); WRITE_ONCE(sk->sk_err, ECONNRESET); } else if (tp->snd_nxt != tp->write_seq && (1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) { /* The last check adjusts for discrepancy of Linux wrt. RFC * states */ - tcp_send_active_reset(sk, gfp_any(), + tcp_send_active_reset(sk, SK_RST_REASON_TCP_DISCONNECT_WITH_DATA); WRITE_ONCE(sk->sk_err, ECONNRESET); } else if (old_state == TCP_SYN_SENT) @@ -4562,9 +4560,11 @@ int do_tcp_getsockopt(struct sock *sk, int level, if (copy_from_sockptr(&len, optlen, sizeof(int))) return -EFAULT; - ca_ops = icsk->icsk_ca_ops; + rcu_read_lock(); + ca_ops = READ_ONCE(icsk->icsk_ca_ops); if (ca_ops && ca_ops->get_info) sz = ca_ops->get_info(sk, ~0U, &attr, &info); + rcu_read_unlock(); len = min_t(unsigned int, len, sz); if (copy_to_sockptr(optlen, &len, sizeof(int))) @@ -4577,16 +4577,24 @@ int do_tcp_getsockopt(struct sock *sk, int level, val = !inet_csk_in_pingpong_mode(sk); break; - case TCP_CONGESTION: + case TCP_CONGESTION: { + char ca_name[TCP_CA_NAME_MAX] = {}; + if (copy_from_sockptr(&len, optlen, sizeof(int))) return -EFAULT; len = min_t(unsigned int, len, TCP_CA_NAME_MAX); if (copy_to_sockptr(optlen, &len, sizeof(int))) return -EFAULT; - if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len)) + + rcu_read_lock(); + memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name, + sizeof(ca_name)); + rcu_read_unlock(); + + if (copy_to_sockptr(optval, ca_name, len)) return -EFAULT; return 0; - + } case TCP_ULP: if (copy_from_sockptr(&len, optlen, sizeof(int))) return -EFAULT; @@ -5147,8 +5155,7 @@ int tcp_abort(struct sock *sk, int err) bh_lock_sock(sk); if (tcp_need_reset(sk->sk_state)) - tcp_send_active_reset(sk, GFP_ATOMIC, - SK_RST_REASON_TCP_STATE); + tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE); tcp_done_with_error(sk, err); bh_unlock_sock(sk); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index e9f6c77e0631..8e83ef81fc18 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -223,7 +223,7 @@ void tcp_assign_congestion_control(struct sock *sk) ca = rcu_dereference(net->ipv4.tcp_congestion_control); if (unlikely(!bpf_try_module_get(ca, ca->owner))) ca = &tcp_reno; - icsk->icsk_ca_ops = ca; + WRITE_ONCE(icsk->icsk_ca_ops, ca); rcu_read_unlock(); memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv)); @@ -253,7 +253,7 @@ static void tcp_reinit_congestion_control(struct sock *sk, struct inet_connection_sock *icsk = inet_csk(sk); tcp_cleanup_congestion_control(sk); - icsk->icsk_ca_ops = ca; + WRITE_ONCE(icsk->icsk_ca_ops, ca); icsk->icsk_ca_setsockopt = 1; memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv)); diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c index 274e628e7cf8..5b457f68a581 100644 --- a/net/ipv4/tcp_dctcp.c +++ b/net/ipv4/tcp_dctcp.c @@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struct sock *sk) /* No ECN support? Fall back to Reno. Also need to clear * ECT from sk since it is set during 3WHS for DCTCP. */ - inet_csk(sk)->icsk_ca_ops = &dctcp_reno; + WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno); INET_ECN_dontxmit(sk); } @@ -228,7 +228,7 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr, if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) || ext & (1 << (INET_DIAG_VEGASINFO - 1))) { memset(&info->dctcp, 0, sizeof(info->dctcp)); - if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) { + if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) { info->dctcp.dctcp_enabled = 1; info->dctcp.dctcp_ce_state = (u16) ca->ce_state; info->dctcp.dctcp_alpha = ca->dctcp_alpha; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9f053eb8b46e..04dbb2babbcd 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2931,8 +2931,8 @@ static int bpf_iter_tcp_realloc_batch(struct bpf_tcp_iter_state *iter, { union bpf_tcp_iter_batch_item *new_batch; - new_batch = kvmalloc_array(new_batch_sz, sizeof(*new_batch), - flags | __GFP_NOWARN); + new_batch = kvmalloc_objs(*new_batch, new_batch_sz, + flags | __GFP_NOWARN); if (!new_batch) return -ENOMEM; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f3fa0b18eda0..0ddfd5af6e58 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -507,7 +507,7 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst) ca = tcp_ca_find_key(ca_key); if (likely(ca && bpf_try_module_get(ca, ca->owner))) { icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst); - icsk->icsk_ca_ops = ca; + WRITE_ONCE(icsk->icsk_ca_ops, ca); ca_got_dst = true; } rcu_read_unlock(); diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 3b1fdcd3cb29..e74d99ca9fac 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, flush |= skb->ip_summed != p->ip_summed; flush |= skb->csum_level != p->csum_level; flush |= NAPI_GRO_CB(p)->count >= 64; + flush |= NAPI_GRO_CB(p)->is_flist != NAPI_GRO_CB(skb)->is_flist; skb_set_network_header(skb, skb_gro_receive_network_offset(skb)); if (flush || skb_gro_receive_list(p, skb)) @@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, struct net *net; int iif, sdif; - if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST))) - return; - p = tcp_gro_lookup(head, th); if (p) { - NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; + /* flist GRO applies to consecutive non-GSO skbs */ + if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) { + NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; + return; + } + + /* Fall back to the regular GRO path */ + if (NAPI_GRO_CB(p)->count == 1) + NAPI_GRO_CB(p)->is_flist = 0; + + NAPI_GRO_CB(skb)->is_flist = 0; + return; } @@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, sk = __inet_lookup_established(net, iph->saddr, th->source, iph->daddr, ntohs(th->dest), iif, sdif); - NAPI_GRO_CB(skb)->is_flist = !sk; + NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb); if (sk) sock_gen_put(sk); } @@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb) if (!th) goto flush; - tcp4_check_fraglist_gro(head, skb, th); + if (unlikely(skb->dev->features & NETIF_F_GRO_FRAGLIST)) + tcp4_check_fraglist_gro(head, skb, th); return tcp_gro_receive(head, skb, th); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6f4dca4a4de9..d960e3de7d50 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3849,9 +3849,9 @@ void tcp_send_fin(struct sock *sk) * was unread data in the receive queue. This behavior is recommended * by RFC 2525, section 2.17. -DaveM */ -void tcp_send_active_reset(struct sock *sk, gfp_t priority, - enum sk_rst_reason reason) +void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason) { + gfp_t priority = sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN); struct sk_buff *skb; TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS); @@ -4092,7 +4092,7 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst) if (likely(ca && bpf_try_module_get(ca, ca->owner))) { bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner); icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst); - icsk->icsk_ca_ops = ca; + WRITE_ONCE(icsk->icsk_ca_ops, ca); } rcu_read_unlock(); } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 1038e7ba9c2e..e56eae4bc341 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -126,7 +126,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset) (!tp->snd_wnd && !tp->packets_out)) do_reset = true; if (do_reset) - tcp_send_active_reset(sk, GFP_ATOMIC, + tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_MEMORY); tcp_done(sk); __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); @@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t) goto out; } } - tcp_send_active_reset(sk, GFP_ATOMIC, SK_RST_REASON_TCP_STATE); + tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE); goto death; } @@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t) icsk->icsk_probes_out > 0) || (user_timeout == 0 && icsk->icsk_probes_out >= keepalive_probes(tp))) { - tcp_send_active_reset(sk, GFP_ATOMIC, + tcp_send_active_reset(sk, SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT); tcp_write_err(sk); goto out; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index af9603217444..bb8cfc62cb00 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -900,6 +900,15 @@ out: return sk; } +static void udp_err_update_exception(struct net *net, struct sk_buff *skb, + int type, int code, u32 info) +{ + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) + ipv4_update_pmtu(skb, net, info, 0, IPPROTO_UDP); + else if (type == ICMP_REDIRECT) + ipv4_redirect(skb, net, 0, IPPROTO_UDP); +} + /* * This routine is called by the ICMP module when it gets some * sort of error condition. If err < 0 then the socket should @@ -923,6 +932,8 @@ int udp_err(struct sk_buff *skb, u32 info) int harderr; int err; + udp_err_update_exception(net, skb, type, code, info); + uh = (struct udphdr *)(skb->data + (iph->ihl << 2)); sk = __udp4_lib_lookup(net, iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex, @@ -2166,10 +2177,10 @@ int __udp_disconnect(struct sock *sk, int flags) */ sk->sk_state = TCP_CLOSE; - inet->inet_daddr = 0; + WRITE_ONCE(inet->inet_daddr, 0); inet->inet_dport = 0; sock_rps_reset_rxhash(sk); - sk->sk_bound_dev_if = 0; + WRITE_ONCE(sk->sk_bound_dev_if, 0); if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) { inet_reset_saddr(sk); if (sk->sk_prot->rehash && diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 51941ad656a3..09a4552f7f08 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -445,7 +445,7 @@ looped_back: hdr->segments_left--; addr = hdr->segments + hdr->segments_left; - skb_push(skb, sizeof(struct ipv6hdr)); + skb_push(skb, -skb_network_offset(skb)); if (skb->ip_summed == CHECKSUM_COMPLETE) seg6_update_csum(skb); @@ -469,7 +469,7 @@ looped_back: } ipv6_hdr(skb)->hop_limit--; - skb_pull(skb, sizeof(struct ipv6hdr)); + skb_pull(skb, skb_transport_offset(skb)); goto looped_back; } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 3e382ba1573e..9ea75703b38d 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -85,7 +85,7 @@ static void fib6_walker_link(struct net *net, struct fib6_walker *w) static void fib6_walker_unlink(struct net *net, struct fib6_walker *w) { write_lock_bh(&net->ipv6.fib6_walker_lock); - list_del(&w->lh); + list_del_init(&w->lh); write_unlock_bh(&net->ipv6.fib6_walker_lock); } @@ -2760,7 +2760,7 @@ static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos) static bool ipv6_route_iter_active(struct ipv6_route_iter *iter) { struct fib6_walker *w = &iter->w; - return w->node && !(w->state == FWS_U && w->node == w->root); + return w->node && !list_empty(&w->lh); } static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 1ab5ad0dcf24..006585dc8b5c 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -461,6 +461,21 @@ done: return NULL; } +static bool fl_sock_at_lease_limit(const struct sock *sk) +{ + const struct ipv6_fl_socklist *sfl; + int count = 0; + + rcu_read_lock(); + for_each_sk_fl_rcu(sk, sfl) { + if (++count >= FL_MAX_PER_SOCK) + break; + } + rcu_read_unlock(); + + return count >= FL_MAX_PER_SOCK; +} + static int mem_check(struct sock *sk) { const int unpriv_total_limit = FL_MAX_SIZE - (FL_MAX_SIZE / 4); @@ -679,6 +694,10 @@ recheck: err = -ENOMEM; if (!sfl1) goto release; + err = -ENOBUFS; + if (fl_sock_at_lease_limit(sk) && + !capable(CAP_NET_ADMIN)) + goto release; if (fl->linger > fl1->linger) fl1->linger = fl->linger; if ((long)(fl->expires - fl1->expires) > 0) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 69c51f1a5bf0..8ebda0b6a78b 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { + struct ip_tunnel_info *tun_info = NULL; struct ip6_tnl *t = netdev_priv(dev); __be16 payload_protocol; int ret; @@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) goto tx_err; + if (t->parms.collect_md) + tun_info = skb_tunnel_info_txcheck(skb); + payload_protocol = skb_protocol(skb, true); switch (payload_protocol) { case htons(ETH_P_IP): @@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, return NETDEV_TX_OK; tx_err: - if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb))) + if (!IS_ERR(tun_info)) DEV_STATS_INC(dev, tx_errors); DEV_STATS_INC(dev, tx_dropped); kfree_skb(skb); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 8fc4766c8da9..550965058991 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1432,6 +1432,8 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, if (frag_size && frag_size < mtu) mtu = frag_size; + if (sk_is_udp(sk)) + mtu = min(mtu, IP6_MAX_MTU); cork->base.fragsize = mtu; cork->base.gso_size = ipc6->gso_size; cork->base.tx_flags = 0; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 3f2ed9b77deb..9d8116b5edb1 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1427,7 +1427,7 @@ int __init ip6_mr_init(void) { int err; - mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN); + mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT); if (!mrt_cachep) return -ENOMEM; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index aaba4c2aae23..ecef55f26189 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex, return err; } - mc_lst->next = np->ipv6_mc_list; + rcu_assign_pointer(mc_lst->next, + sock_dereference(np->ipv6_mc_list, sk)); rcu_assign_pointer(np->ipv6_mc_list, mc_lst); return 0; @@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) lnk = &mc_lst->next) { if ((ifindex == 0 || mc_lst->ifindex == ifindex) && ipv6_addr_equal(&mc_lst->addr, addr)) { - *lnk = mc_lst->next; + rcu_assign_pointer(*lnk, + sock_dereference(mc_lst->next, sk)); __ipv6_sock_mc_drop(sk, mc_lst); return 0; } @@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk) struct ipv6_mc_socklist *mc_lst; while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) { - np->ipv6_mc_list = mc_lst->next; + rcu_assign_pointer(np->ipv6_mc_list, + sock_dereference(mc_lst->next, sk)); __ipv6_sock_mc_drop(sk, mc_lst); } } @@ -355,12 +358,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk, { struct ipv6_pinfo *inet6 = inet6_sk(sk); struct in6_addr *source, *group; + struct ip6_sf_socklist *newpsl, *psl; struct net *net = sock_net(sk); struct ipv6_mc_socklist *pmc; - struct ip6_sf_socklist *psl; struct inet6_dev *idev; int leavegroup = 0; - int i, j, rv; + int i, j; int err; source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr; @@ -409,13 +412,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk, if (!add) { if (!psl) goto done; /* err = -EADDRNOTAVAIL */ - rv = !0; for (i = 0; i < psl->sl_count; i++) { - rv = !ipv6_addr_equal(&psl->sl_addr[i], source); - if (rv == 0) + if (ipv6_addr_equal(&psl->sl_addr[i], source)) break; } - if (rv) /* source not found */ + if (i == psl->sl_count) /* source not found */ goto done; /* err = -EADDRNOTAVAIL */ /* special case - (INCLUDE, empty) == LEAVE_GROUP */ @@ -424,58 +425,74 @@ int ip6_mc_source(int add, int omode, struct sock *sk, goto done; } + atomic_sub(struct_size(psl, sl_addr, psl->sl_max), + &sk->sk_omem_alloc); + + if (psl->sl_count == 1) { + newpsl = NULL; + } else { + newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, + psl->sl_count - 1), + GFP_KERNEL); + if (!newpsl) { + atomic_add(struct_size(psl, sl_addr, psl->sl_max), + &sk->sk_omem_alloc); + err = -ENOBUFS; + goto done; + } + newpsl->sl_max = psl->sl_count - 1; + newpsl->sl_count = psl->sl_count - 1; + for (j = 0; j < i; j++) + newpsl->sl_addr[j] = psl->sl_addr[j]; + for (j = i + 1; j < psl->sl_count; j++) + newpsl->sl_addr[j - 1] = psl->sl_addr[j]; + } + /* update the interface filter */ ip6_mc_del_src(idev, group, omode, 1, source, 1); - for (j = i+1; j < psl->sl_count; j++) - psl->sl_addr[j-1] = psl->sl_addr[j]; - psl->sl_count--; + rcu_assign_pointer(pmc->sflist, newpsl); + kfree_rcu(psl, rcu); err = 0; goto done; } /* else, add a new source to the filter */ - if (psl && psl->sl_count >= sysctl_mld_max_msf) { + if (psl && psl->sl_count >= READ_ONCE(sysctl_mld_max_msf)) { err = -ENOBUFS; goto done; } - if (!psl || psl->sl_count == psl->sl_max) { - struct ip6_sf_socklist *newpsl; - int count = IP6_SFBLOCK; - - if (psl) - count += psl->sl_max; - newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count), - GFP_KERNEL); - if (!newpsl) { - err = -ENOBUFS; - goto done; - } - newpsl->sl_max = count; - newpsl->sl_count = count - IP6_SFBLOCK; - if (psl) { - for (i = 0; i < psl->sl_count; i++) - newpsl->sl_addr[i] = psl->sl_addr[i]; - atomic_sub(struct_size(psl, sl_addr, psl->sl_max), - &sk->sk_omem_alloc); + if (psl) { + for (i = 0; i < psl->sl_count; i++) { + if (ipv6_addr_equal(&psl->sl_addr[i], source)) + goto done; /* err = -EADDRNOTAVAIL */ } - rcu_assign_pointer(pmc->sflist, newpsl); - kfree_rcu(psl, rcu); - psl = newpsl; } - rv = 1; /* > 0 for insert logic below if sl_count is 0 */ - for (i = 0; i < psl->sl_count; i++) { - rv = !ipv6_addr_equal(&psl->sl_addr[i], source); - if (rv == 0) /* There is an error in the address. */ - goto done; + + i = psl ? psl->sl_count + 1 : 1; + newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, i), + GFP_KERNEL); + if (!newpsl) { + err = -ENOBUFS; + goto done; } - for (j = psl->sl_count-1; j >= i; j--) - psl->sl_addr[j+1] = psl->sl_addr[j]; - psl->sl_addr[i] = *source; - psl->sl_count++; - err = 0; + newpsl->sl_max = i; + newpsl->sl_count = i; + if (psl) { + for (j = 0; j < psl->sl_count; j++) + newpsl->sl_addr[j] = psl->sl_addr[j]; + } + newpsl->sl_addr[i - 1] = *source; + /* update the interface list */ ip6_mc_add_src(idev, group, omode, 1, source, 1); + + if (psl) + atomic_sub(struct_size(psl, sl_addr, psl->sl_max), + &sk->sk_omem_alloc); + rcu_assign_pointer(pmc->sflist, newpsl); + kfree_rcu(psl, rcu); + err = 0; done: mutex_unlock(&idev->mc_lock); in6_dev_put(idev); @@ -784,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im) if (!pmc) return; if (pmc_prev) - rcu_assign_pointer(pmc_prev->next, pmc->next); + rcu_assign_pointer(pmc_prev->next, + mc_dereference(pmc->next, idev)); else - rcu_assign_pointer(idev->mc_tomb, pmc->next); + rcu_assign_pointer(idev->mc_tomb, + mc_dereference(pmc->next, idev)); im->idev = pmc->idev; if (im->mca_sfmode == MCAST_INCLUDE) { @@ -966,7 +985,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev, return -ENOMEM; } - rcu_assign_pointer(mc->next, idev->mc_list); + rcu_assign_pointer(mc->next, mc_dereference(idev->mc_list, idev)); rcu_assign_pointer(idev->mc_list, mc); mld_del_delrec(idev, mc); @@ -1000,7 +1019,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) WRITE_ONCE(ma->mca_users, new_users); if (new_users == 0) { - *map = ma->next; + rcu_assign_pointer(*map, + mc_dereference(ma->next, idev)); igmp6_group_dropped(ma); inet6_ifmcaddr_notify(idev->dev, ma, @@ -2351,14 +2371,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) && !mld_in_v1_mode(idev)) { - psf->sf_crcount = idev->mc_qrv; - rcu_assign_pointer(psf->sf_next, - mc_dereference(pmc->mca_tomb, idev)); - rcu_assign_pointer(pmc->mca_tomb, psf); - rv = 1; - } else { - kfree_rcu(psf, rcu); + struct ip6_sf_list *dpsf = kmalloc_obj(*dpsf); + + if (dpsf) { + *dpsf = *psf; + dpsf->sf_crcount = idev->mc_qrv; + rcu_assign_pointer(dpsf->sf_next, + mc_dereference(pmc->mca_tomb, idev)); + rcu_assign_pointer(pmc->mca_tomb, dpsf); + rv = 1; + } } + kfree_rcu(psf, rcu); } return rv; } @@ -2621,7 +2645,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc) static void igmp6_join_group(struct ifmcaddr6 *ma) { - unsigned long delay; + unsigned long delay, interval; mc_assert_locked(ma->idev); @@ -2630,13 +2654,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); - delay = get_random_u32_below(unsolicited_report_interval(ma->idev)); + interval = unsolicited_report_interval(ma->idev); + delay = interval; if (cancel_delayed_work(&ma->mca_work)) { refcount_dec(&ma->mca_refcnt); delay = ma->mca_work.timer.expires - jiffies; } + if (delay >= interval) + delay = get_random_u32_below(interval); + if (!mod_delayed_work(mld_wq, &ma->mca_work, delay)) refcount_inc(&ma->mca_refcnt); WRITE_ONCE(ma->mca_flags, ma->mca_flags | @@ -3001,7 +3029,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v) struct ifmcaddr6 *im = (struct ifmcaddr6 *)v; struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); unsigned int mca_flags = READ_ONCE(im->mca_flags); - unsigned long expires = READ_ONCE(im->mca_work.timer.expires); + long delta = READ_ONCE(im->mca_work.timer.expires) - jiffies; seq_printf(seq, "%-4d %-15s %pi6 %5d %08X %ld\n", @@ -3009,7 +3037,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v) &im->mca_addr, READ_ONCE(im->mca_users), mca_flags, (mca_flags & MAF_TIMER_RUNNING) ? - jiffies_to_clock_t(expires - jiffies) : 0); + jiffies_delta_to_clock_t(delta) : 0); return 0; } diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index f42fb96ef64b..313c4aac377a 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -647,6 +647,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e, /* Clear counters and comefrom */ e->counters = ((struct xt_counters) { 0, 0 }); e->comefrom = 0; + + /* set F_PROTO, else ip6_packet_match won't do the right thing. */ + if (e->ipv6.proto) + e->ipv6.flags |= IP6T_F_PROTO; + return 0; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6a40c5074543..08bd68f1b5bb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3255,7 +3255,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif) void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { - ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, + ip6_redirect(skb, sock_net(sk), skb->dev->ifindex, READ_ONCE(sk->sk_mark), sk_uid(sk)); } @@ -4019,6 +4019,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) struct net *net = info->nl_net; struct sk_buff *skb = NULL; struct fib6_table *table; + struct fib6_node *fn; int err = -ENOENT; if (rt == net->ipv6.fib6_null_entry) @@ -4026,9 +4027,13 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) table = rt->fib6_table; spin_lock_bh(&table->tb6_lock); + fn = rcu_dereference_protected(rt->fib6_node, + lockdep_is_held(&table->tb6_lock)); + if (!fn) + goto out_unlock; + if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { struct fib6_info *sibling, *next_sibling; - struct fib6_node *fn; /* prefer to send a single notification with all hops */ skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC); @@ -4051,8 +4056,6 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) * and emit a replace or delete notification, respectively. */ info->skip_notify_kernel = 1; - fn = rcu_dereference_protected(rt->fib6_node, - lockdep_is_held(&table->tb6_lock)); if (rcu_access_pointer(fn->leaf) == rt) { struct fib6_info *last_sibling, *replace_rt; diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c index f2a659cd6183..eec3778855eb 100644 --- a/net/ipv6/tcpv6_offload.c +++ b/net/ipv6/tcpv6_offload.c @@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, p = tcp_gro_lookup(head, th); if (p) { - NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; + /* flist GRO applies to consecutive non-GSO skbs */ + if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) { + NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist; + return; + } + + /* Fall back to the regular GRO path */ + if (NAPI_GRO_CB(p)->count == 1) + NAPI_GRO_CB(p)->is_flist = 0; + + NAPI_GRO_CB(skb)->is_flist = 0; + return; } @@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, sk = __inet6_lookup_established(net, &hdr->saddr, th->source, &hdr->daddr, ntohs(th->dest), iif, sdif); - NAPI_GRO_CB(skb)->is_flist = !sk; + NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb); if (sk) sock_gen_put(sk); #endif /* IS_ENABLED(CONFIG_IPV6) */ diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fd875908ac0c..93478d1ad576 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -690,6 +690,17 @@ out: return sk; } +static void udpv6_err_update_exception(struct net *net, struct sk_buff *skb, + u8 type, __be32 info) +{ + if (type == ICMPV6_PKT_TOOBIG) + ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); + else if (type == NDISC_REDIRECT) + ip6_redirect(skb, net, skb->dev->ifindex, 0, + sock_net_uid(net, NULL)); +} + static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { @@ -703,6 +714,8 @@ static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, int harderr; int err; + udpv6_err_update_exception(net, skb, type, info); + daddr = seg6_get_daddr(skb, opt) ? : &hdr->daddr; saddr = &hdr->saddr; sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 4e5cc9da6e06..db261ecd19af 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -210,12 +210,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, phs_hdr->flags = flags; if (flags == AF_IUCV_FLAG_SYN) phs_hdr->window = iucv->msglimit; - else if ((flags == AF_IUCV_FLAG_WIN) || !flags) { - confirm_recv = atomic_read(&iucv->msg_recv); - phs_hdr->window = confirm_recv; - if (confirm_recv) - phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN; - } memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8); memcpy(phs_hdr->destAppName, iucv->dst_name, 8); memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8); @@ -250,13 +244,22 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, } skb->protocol = cpu_to_be16(ETH_P_AF_IUCV); + /* Claim the receive credit here, not while building the header: every + * way this frame can be dropped has now been ruled out, so the window + * is zeroed only for as long as the transmit itself takes. + */ + if (flags == AF_IUCV_FLAG_WIN || !flags) { + confirm_recv = atomic_xchg(&iucv->msg_recv, 0); + phs_hdr->window = confirm_recv; + if (confirm_recv) + phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN; + } + atomic_inc(&iucv->skbs_in_xmit); err = dev_queue_xmit(skb); if (net_xmit_eval(err)) { atomic_dec(&iucv->skbs_in_xmit); - } else { - atomic_sub(confirm_recv, &iucv->msg_recv); - WARN_ON(atomic_read(&iucv->msg_recv) < 0); + atomic_add(confirm_recv, &iucv->msg_recv); } return net_xmit_eval(err); @@ -1241,6 +1244,7 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, struct iucv_sock *iucv = iucv_sk(sk); unsigned int copied, rlen; struct sk_buff *skb, *rskb, *cskb; + bool send_win = false; int err = 0; u32 offset; @@ -1331,16 +1335,20 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg, if (skb_queue_empty(&iucv->backlog_skb_q)) { if (!list_empty(&iucv->message_q.list)) iucv_process_message_q(sk); - if (atomic_read(&iucv->msg_recv) >= - iucv->msglimit / 2) { - err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN); - if (err) { - sk->sk_state = IUCV_DISCONN; - sk->sk_state_change(sk); - } - } + if (iucv->transport == AF_IUCV_TRANS_HIPER && + atomic_read(&iucv->msg_recv) >= + iucv->msglimit / 2) + send_win = true; } spin_unlock_bh(&iucv->message_q.lock); + + if (send_win) { + err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN); + if (err) { + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + } + } } done: diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 19e08661be43..c7769da9617b 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -659,8 +659,7 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, if (!sta) return -ENOENT; - new_sched = kzalloc(struct_size(new_sched, channels, sched->n_channels), - GFP_KERNEL); + new_sched = kzalloc_flex(*new_sched, channels, sched->n_channels); if (!new_sched) return -ENOMEM; diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 8f2bff268392..992ce6698c20 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -74,9 +74,18 @@ struct ieee802154_local { struct work_struct rx_beacon_work; struct list_head rx_mac_cmd_list; struct work_struct rx_mac_cmd_work; + /* Serializes rx_beacon_list and rx_mac_cmd_list against the RX + * softirq producer, the mac_wq workers and the teardown flush. + */ + spinlock_t rx_lock; /* Association */ - struct ieee802154_pan_device *assoc_dev; + /* assoc_lock protects assoc_dev_extended_addr, assoc_addr, + * assoc_status, the assoc_done reinit/complete pairing and the + * IEEE802154_IS_ASSOCIATING bit in @ongoing. + */ + spinlock_t assoc_lock; + __le64 assoc_dev_extended_addr; struct completion assoc_done; __le16 assoc_addr; u8 assoc_status; @@ -300,6 +309,10 @@ static inline bool mac802154_is_beaconing(struct ieee802154_local *local) } void mac802154_rx_mac_cmd_worker(struct work_struct *work); +void mac802154_flush_list(struct list_head *list, + struct ieee802154_sub_if_data *sdata); +void mac802154_flush_queued_pkts(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata); int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, struct ieee802154_pan_device *coord, diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index b823720630e7..31353795fa24 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -694,6 +694,7 @@ void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata) mutex_unlock(&sdata->local->iflist_mtx); synchronize_rcu(); + mac802154_flush_queued_pkts(sdata->local, sdata); unregister_netdevice(sdata->dev); } @@ -705,6 +706,11 @@ void ieee802154_remove_interfaces(struct ieee802154_local *local) list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { list_del_rcu(&sdata->list); + /* Best-effort: a frame the RX softirq queues for this sdata + * after the flush still pins the netdev, so the + * unregister_netdevice() below waits it out. + */ + mac802154_flush_queued_pkts(local, sdata); unregister_netdevice(sdata->dev); } mutex_unlock(&local->iflist_mtx); diff --git a/net/mac802154/main.c b/net/mac802154/main.c index ea1efef3572a..8ed6de111f5a 100644 --- a/net/mac802154/main.c +++ b/net/mac802154/main.c @@ -91,6 +91,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) INIT_LIST_HEAD(&local->interfaces); INIT_LIST_HEAD(&local->rx_beacon_list); INIT_LIST_HEAD(&local->rx_mac_cmd_list); + spin_lock_init(&local->rx_lock); mutex_init(&local->iflist_mtx); tasklet_setup(&local->tasklet, ieee802154_tasklet_handler); @@ -104,6 +105,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker); init_completion(&local->assoc_done); + spin_lock_init(&local->assoc_lock); /* init supported flags with 802.15.4 default ranges */ phy->supported.max_minbe = 8; diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index cd8f2a11920d..19b5382e85a8 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -35,16 +35,23 @@ void mac802154_rx_beacon_worker(struct work_struct *work) container_of(work, struct ieee802154_local, rx_beacon_work); struct cfg802154_mac_pkt *mac_pkt; - mac_pkt = list_first_entry_or_null(&local->rx_beacon_list, - struct cfg802154_mac_pkt, node); - if (!mac_pkt) - return; + for (;;) { + spin_lock_bh(&local->rx_lock); + mac_pkt = list_first_entry_or_null(&local->rx_beacon_list, + struct cfg802154_mac_pkt, node); + if (mac_pkt) + list_del(&mac_pkt->node); + spin_unlock_bh(&local->rx_lock); + if (!mac_pkt) + break; - mac802154_process_beacon(local, mac_pkt->skb, mac_pkt->page, mac_pkt->channel); + mac802154_process_beacon(local, mac_pkt->skb, + mac_pkt->page, mac_pkt->channel); - list_del(&mac_pkt->node); - kfree_skb(mac_pkt->skb); - kfree(mac_pkt); + netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker); + kfree_skb(mac_pkt->skb); + kfree(mac_pkt); + } } static bool mac802154_should_answer_beacon_req(struct ieee802154_local *local) @@ -68,22 +75,15 @@ static bool mac802154_should_answer_beacon_req(struct ieee802154_local *local) return interval == IEEE802154_ACTIVE_SCAN_DURATION; } -void mac802154_rx_mac_cmd_worker(struct work_struct *work) +static void mac802154_rx_mac_cmd(struct ieee802154_local *local, + struct cfg802154_mac_pkt *mac_pkt) { - struct ieee802154_local *local = - container_of(work, struct ieee802154_local, rx_mac_cmd_work); - struct cfg802154_mac_pkt *mac_pkt; u8 mac_cmd; int rc; - mac_pkt = list_first_entry_or_null(&local->rx_mac_cmd_list, - struct cfg802154_mac_pkt, node); - if (!mac_pkt) - return; - rc = ieee802154_get_mac_cmd(mac_pkt->skb, &mac_cmd); if (rc) - goto out; + return; switch (mac_cmd) { case IEEE802154_CMD_BEACON_REQ: @@ -121,11 +121,81 @@ void mac802154_rx_mac_cmd_worker(struct work_struct *work) default: break; } +} -out: - list_del(&mac_pkt->node); - kfree_skb(mac_pkt->skb); - kfree(mac_pkt); +void mac802154_rx_mac_cmd_worker(struct work_struct *work) +{ + struct ieee802154_local *local = + container_of(work, struct ieee802154_local, rx_mac_cmd_work); + struct cfg802154_mac_pkt *mac_pkt; + + for (;;) { + spin_lock_bh(&local->rx_lock); + mac_pkt = list_first_entry_or_null(&local->rx_mac_cmd_list, + struct cfg802154_mac_pkt, node); + if (mac_pkt) + list_del(&mac_pkt->node); + spin_unlock_bh(&local->rx_lock); + if (!mac_pkt) + break; + + /* A stopped interface cannot transmit; skipping avoids a + * needless association response (and the !netif_running() + * warning it would trip) during teardown. The beacon worker + * needs no such check as it never transmits. + */ + if (ieee802154_sdata_running(mac_pkt->sdata)) + mac802154_rx_mac_cmd(local, mac_pkt); + + netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker); + kfree_skb(mac_pkt->skb); + kfree(mac_pkt); + } +} + +/** + * mac802154_flush_list - free queued RX frames on @list + * @list: rx_beacon_list or rx_mac_cmd_list + * @sdata: only free frames received on this interface, or %NULL for all + * + * Each frame pins the net_device it was received on (via netdev_hold()), + * so release that reference as the frame is dropped. Caller must hold + * local->rx_lock. + */ +void mac802154_flush_list(struct list_head *list, + struct ieee802154_sub_if_data *sdata) +{ + struct cfg802154_mac_pkt *mac_pkt, *tmp; + + list_for_each_entry_safe(mac_pkt, tmp, list, node) { + if (sdata && mac_pkt->sdata != sdata) + continue; + list_del(&mac_pkt->node); + netdev_put(mac_pkt->sdata->dev, &mac_pkt->dev_tracker); + kfree_skb(mac_pkt->skb); + kfree(mac_pkt); + } +} + +/** + * mac802154_flush_queued_pkts - drop queued RX work referencing @sdata + * @local: the mac802154 device + * @sdata: interface being removed + * + * The workers dereference the queued frame's interface directly + * (mac_pkt->sdata) or through skb->dev in mac802154_process_beacon(). Drop + * the not-yet-started entries belonging to @sdata before it is unregistered + * so their netdev reference is released; an entry already dequeued by a + * running worker keeps its own reference until the worker completes, which + * unregister_netdevice() then waits out. + */ +void mac802154_flush_queued_pkts(struct ieee802154_local *local, + struct ieee802154_sub_if_data *sdata) +{ + spin_lock_bh(&local->rx_lock); + mac802154_flush_list(&local->rx_beacon_list, sdata); + mac802154_flush_list(&local->rx_mac_cmd_list, sdata); + spin_unlock_bh(&local->rx_lock); } static int @@ -221,7 +291,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, mac_pkt->sdata = sdata; mac_pkt->page = sdata->local->scan_page; mac_pkt->channel = sdata->local->scan_channel; + netdev_hold(sdata->dev, &mac_pkt->dev_tracker, GFP_ATOMIC); + spin_lock(&sdata->local->rx_lock); list_add_tail(&mac_pkt->node, &sdata->local->rx_beacon_list); + spin_unlock(&sdata->local->rx_lock); queue_work(sdata->local->mac_wq, &sdata->local->rx_beacon_work); return NET_RX_SUCCESS; @@ -233,7 +306,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, mac_pkt->skb = skb_get(skb); mac_pkt->sdata = sdata; + netdev_hold(sdata->dev, &mac_pkt->dev_tracker, GFP_ATOMIC); + spin_lock(&sdata->local->rx_lock); list_add_tail(&mac_pkt->node, &sdata->local->rx_mac_cmd_list); + spin_unlock(&sdata->local->rx_lock); queue_work(sdata->local->mac_wq, &sdata->local->rx_mac_cmd_work); return NET_RX_SUCCESS; diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 005338f89b75..d393b1f4e74e 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -104,13 +104,9 @@ static unsigned int mac802154_scan_get_channel_time(u8 duration_order, static void mac802154_flush_queued_beacons(struct ieee802154_local *local) { - struct cfg802154_mac_pkt *mac_pkt, *tmp; - - list_for_each_entry_safe(mac_pkt, tmp, &local->rx_beacon_list, node) { - list_del(&mac_pkt->node); - kfree_skb(mac_pkt->skb); - kfree(mac_pkt); - } + spin_lock_bh(&local->rx_lock); + mac802154_flush_list(&local->rx_beacon_list, NULL); + spin_unlock_bh(&local->rx_lock); } static void @@ -536,7 +532,9 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, struct ieee802154_association_req_frame frame = {}; struct ieee802154_local *local = sdata->local; struct wpan_dev *wpan_dev = &sdata->wpan_dev; + __le16 resp_short_addr; struct sk_buff *skb; + u8 resp_status; int ret; frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD; @@ -578,9 +576,11 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, return ret; } - local->assoc_dev = coord; + spin_lock(&local->assoc_lock); reinit_completion(&local->assoc_done); + local->assoc_dev_extended_addr = coord->extended_addr; set_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing); + spin_unlock(&local->assoc_lock); ret = ieee802154_mlme_tx_one_locked(local, sdata, skb); if (ret) { @@ -599,25 +599,37 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, goto clear_assoc; } - if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) { - if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY) + /* The association is complete: mac802154_process_association_resp() + * cleared the associating bit before waking us, so a second (e.g. + * malicious) ASSOC RESP can no longer pass the recheck and overwrite + * the result. Snapshot assoc_status/assoc_addr under the lock. + */ + spin_lock(&local->assoc_lock); + resp_status = local->assoc_status; + resp_short_addr = local->assoc_addr; + spin_unlock(&local->assoc_lock); + + if (resp_status != IEEE802154_ASSOCIATION_SUCCESSFUL) { + if (resp_status == IEEE802154_PAN_AT_CAPACITY) ret = -ERANGE; else ret = -EPERM; dev_warn(&sdata->dev->dev, "Negative ASSOC RESP received from %8phC: %s\n", &ceaddr, - local->assoc_status == IEEE802154_PAN_AT_CAPACITY ? + resp_status == IEEE802154_PAN_AT_CAPACITY ? "PAN at capacity" : "access denied"); - goto clear_assoc; + return ret; } - ret = 0; - *short_addr = local->assoc_addr; + *short_addr = resp_short_addr; + + return 0; clear_assoc: + spin_lock(&local->assoc_lock); clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing); - local->assoc_dev = NULL; + spin_unlock(&local->assoc_lock); return ret; } @@ -639,19 +651,28 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata, dest->mode != IEEE802154_EXTENDED_ADDRESSING)) return -EINVAL; - if (unlikely(dest->extended_addr != wpan_dev->extended_addr || - src->extended_addr != local->assoc_dev->extended_addr)) + spin_lock(&local->assoc_lock); + if (unlikely(!test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing) || + dest->extended_addr != wpan_dev->extended_addr || + src->extended_addr != local->assoc_dev_extended_addr)) { + spin_unlock(&local->assoc_lock); return -ENODEV; + } memcpy(&resp_pl, skb->data, sizeof(resp_pl)); local->assoc_addr = resp_pl.short_addr; local->assoc_status = resp_pl.status; + /* Clear the associating bit before waking the waiter: once the result + * is saved, any subsequent (e.g. malicious) ASSOC RESP must fail the + * test_bit() recheck above and can no longer overwrite the result. + */ + clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing); + complete(&local->assoc_done); + spin_unlock(&local->assoc_lock); dev_dbg(&skb->dev->dev, "ASSOC RESP 0x%x received from %8phC, getting short address %04x\n", - local->assoc_status, &deaddr, local->assoc_addr); - - complete(&local->assoc_done); + resp_pl.status, &deaddr, resp_pl.short_addr); return 0; } diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c index c92e3abb40d7..f05f75bf93e0 100644 --- a/net/mctp/test/route-test.c +++ b/net/mctp/test/route-test.c @@ -193,7 +193,7 @@ static void __mctp_route_test_init(struct kunit *test, if (netid != MCTP_NET_ANY) WRITE_ONCE(dev->mdev->net, netid); - dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL); + dev->mdev->addrs = kmalloc_objs(u8, 1); dev->mdev->num_addrs = 1; dev->mdev->addrs[0] = 8; diff --git a/net/mctp/test/utils.c b/net/mctp/test/utils.c index 6eef8d485c25..6b131084a249 100644 --- a/net/mctp/test/utils.c +++ b/net/mctp/test/utils.c @@ -88,7 +88,7 @@ struct mctp_test_dev *mctp_test_create_dev_with_addr(mctp_eid_t addr) if (!dev) return NULL; - dev->mdev->addrs = kmalloc_objs(u8, 1, GFP_KERNEL); + dev->mdev->addrs = kmalloc_objs(u8, 1); if (!dev->mdev->addrs) { mctp_test_destroy_dev(dev); return NULL; diff --git a/net/mptcp/options.c b/net/mptcp/options.c index b8318e030138..ce0de02f5a3a 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -93,7 +93,8 @@ static void mptcp_parse_option(const struct sk_buff *skb, * In other words, the only way for checksums not to be used * is if both hosts in their SYNs set A=0." */ - if (flags & MPTCP_CAP_CHECKSUM_REQD) + if ((flags & MPTCP_CAP_CHECKSUM_REQD) && + opsize < TCPOLEN_MPTCP_MPC_ACK_DATA) mp_opt->suboptions |= OPTION_MPTCP_CSUMREQD; mp_opt->deny_join_id0 = !!(flags & MPTCP_CAP_DENY_JOIN_ID0); @@ -529,7 +530,7 @@ static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb, return false; /* MPC/MPJ needed only on 3rd ack packet, DATA_FIN and TCP shutdown take precedence */ - if (READ_ONCE(subflow->fully_established) || snd_data_fin_enable || + if (subflow->fully_established || snd_data_fin_enable || subflow->snd_isn != TCP_SKB_CB(skb)->seq || sk->sk_state != TCP_ESTABLISHED) return false; @@ -611,6 +612,7 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow, ext->data_seq = data_fin_tx_seq; ext->subflow_seq = 0; ext->data_len = 1; + ext->csum = 0; } else if (ext->data_seq + ext->data_len == data_fin_tx_seq) { /* If there's an existing DSS mapping and it is the * final mapping, DATA_FIN consumes 1 additional byte of @@ -980,7 +982,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk, /* here we can process OoO, in-window pkts, only in-sequence 4th ack * will make the subflow fully established */ - if (likely(READ_ONCE(subflow->fully_established))) { + if (likely(subflow->fully_established)) { /* on passive sockets, check for 3rd ack retransmission * note that msk is always set by subflow_syn_recv_sock() * for mp_join subflows diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 8b68868255c5..b0b71adefb8f 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -462,10 +462,10 @@ bool mptcp_pm_announced_alloc(struct mptcp_sock *msk, add_entry->addr = *addr; add_entry->sock = msk; - add_entry->retrans_times = 0; timer_setup(&add_entry->timer, mptcp_pm_add_addr_timer, 0); reset_timer: + add_entry->retrans_times = 0; add_entry->timer_done = false; timeout = mptcp_adjust_add_addr_timeout(msk); if (timeout) diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c index 424f1a7f9248..1a7750813235 100644 --- a/net/mptcp/pm_kernel.c +++ b/net/mptcp/pm_kernel.c @@ -1137,6 +1137,8 @@ static int mptcp_nl_remove_id_zero_address(struct net *net, while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) { struct sock *sk = (struct sock *)msk; struct mptcp_addr_info msk_local; + struct mptcp_addr_info anno_addr; + bool announced; if (list_empty(&msk->conn_list) || mptcp_pm_is_userspace(msk)) goto next; @@ -1146,7 +1148,13 @@ static int mptcp_nl_remove_id_zero_address(struct net *net, goto next; lock_sock(sk); + /* Drop a possibly pending ADD_ADDR for this address. */ + anno_addr = msk_local; + anno_addr.port = 0; + announced = mptcp_pm_announced_remove(msk, &anno_addr); spin_lock_bh(&msk->pm.lock); + if (announced) + msk->pm.add_addr_signaled--; mptcp_pm_remove_addr(msk, &list); mptcp_pm_rm_subflow(msk, &list); __mark_subflow_endp_available(msk, 0); diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index b94fbb483bf9..fab16d953dbf 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -69,6 +69,19 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, } if (!addr_match && !id_match) { + unsigned int id; + + if (!entry->addr.id && needs_id) { + id = find_next_zero_bit(id_bitmap, + MPTCP_PM_MAX_ADDR_ID + 1, 1); + if (id > MPTCP_PM_MAX_ADDR_ID) { + ret = -ENOSPC; + goto append_err; + } + } else { + id = entry->addr.id; + } + /* Memory for the entry is allocated from the * sock option buffer. */ @@ -78,10 +91,7 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, goto append_err; } - if (!e->addr.id && needs_id) - e->addr.id = find_next_zero_bit(id_bitmap, - MPTCP_PM_MAX_ADDR_ID + 1, - 1); + e->addr.id = id; list_add_tail_rcu(&e->list, &msk->pm.userspace_pm_local_addr_list); msk->pm.local_addr_used++; ret = e->addr.id; diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index b474d03620a7..0098e2830931 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -95,6 +95,7 @@ bool __mptcp_try_fallback(struct mptcp_sock *msk, int fb_mib) msk->allow_subflows = false; set_bit(MPTCP_FALLBACK_DONE, &msk->flags); + clear_bit(MPTCP_RTX_ENABLED, &msk->flags); __MPTCP_INC_STATS(net, fb_mib); spin_unlock_bh(&msk->fallback_lock); return true; @@ -288,8 +289,8 @@ static void mptcp_prune_ofo_queue(struct sock *sk, */ static bool mptcp_can_ingest(const struct sock *sk) { - return unlikely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) || - __mptcp_check_fallback(mptcp_sk(sk)); + return likely(sk_rmem_alloc_get(sk) <= READ_ONCE(sk->sk_rcvbuf)) || + __mptcp_check_fallback(mptcp_sk(sk)); } static bool mptcp_try_rmem_schedule(struct sock *sk, const struct sk_buff *skb) @@ -312,12 +313,6 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb) u64 seq, end_seq, max_seq; struct sk_buff *skb1; - if (!mptcp_try_rmem_schedule(sk, skb)) { - MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED); - mptcp_drop(sk, skb); - return; - } - seq = MPTCP_SKB_CB(skb)->map_seq; end_seq = MPTCP_SKB_CB(skb)->end_seq; max_seq = atomic64_read(&msk->rcv_wnd_sent); @@ -334,6 +329,12 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb) return; } + if (!mptcp_try_rmem_schedule(sk, skb)) { + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED); + mptcp_drop(sk, skb); + return; + } + p = &msk->out_of_order_queue.rb_node; MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUE); if (RB_EMPTY_ROOT(&msk->out_of_order_queue)) { @@ -1084,13 +1085,14 @@ static bool mptcp_rtx_timer_pending(struct sock *sk) static void mptcp_reset_rtx_timer(struct sock *sk) { + struct mptcp_sock *msk = mptcp_sk(sk); unsigned long tout; - /* prevent rescheduling on close */ - if (unlikely(inet_sk_state_load(sk) == TCP_CLOSE)) + /* Prevent rescheduling on close and in case of fallback. */ + if (!test_bit(MPTCP_RTX_ENABLED, &msk->flags)) return; - tout = mptcp_sk(sk)->timer_ival; + tout = msk->timer_ival; sk_reset_timer(sk, &sk->mptcp_retransmit_timer, jiffies + tout); } @@ -3109,8 +3111,7 @@ static void mptcp_do_fastclose(struct sock *sk) */ inet_csk(ssk)->icsk_ack.rcv_mss = TCP_MIN_MSS; - tcp_send_active_reset(ssk, ssk->sk_allocation, - SK_RST_REASON_TCP_ABORT_ON_CLOSE); + tcp_send_active_reset(ssk, SK_RST_REASON_TCP_ABORT_ON_CLOSE); unlock: release_sock(ssk); } @@ -3324,6 +3325,9 @@ void mptcp_set_state(struct sock *sk, int state) * transition from TCP_SYN_RECV to TCP_CLOSE_WAIT. */ break; + case TCP_CLOSE: + clear_bit(MPTCP_RTX_ENABLED, &mptcp_sk(sk)->flags); + fallthrough; default: if (oldstate == TCP_ESTABLISHED || oldstate == TCP_CLOSE_WAIT) MPTCP_DEC_STATS(sock_net(sk), MPTCP_MIB_CURRESTAB); @@ -3584,6 +3588,7 @@ static void mptcp_destroy_common(struct mptcp_sock *msk) static int mptcp_disconnect(struct sock *sk, int flags) { + struct inet_connection_sock *icsk = inet_csk(sk); struct mptcp_sock *msk = mptcp_sk(sk); /* We are on the fastopen error path. We can't call straight into the @@ -3596,8 +3601,13 @@ static int mptcp_disconnect(struct sock *sk, int flags) mptcp_check_listen_stop(sk); mptcp_set_state(sk, TCP_CLOSE); - mptcp_stop_rtx_timer(sk); - mptcp_stop_tout_timer(sk); + /* The later subflow close can not kick again the tout timer, + * as the msk is already in closed status. + */ + msk->timer_ival = icsk->icsk_rto_min; + sk_stop_timer_sync(sk, &sk->mptcp_retransmit_timer); + icsk->icsk_mtup.probe_timestamp = 0; + sk_stop_timer_sync(sk, &icsk->mptcp_tout_timer); mptcp_pm_connection_closed(msk); @@ -3876,7 +3886,7 @@ static void schedule_3rdack_retransmission(struct sock *ssk) struct tcp_sock *tp = tcp_sk(ssk); unsigned long timeout; - if (READ_ONCE(mptcp_subflow_ctx(ssk)->fully_established)) + if (mptcp_subflow_ctx(ssk)->fully_established) return; /* reschedule with a timeout above RTT, as we must look only for drop */ @@ -4142,6 +4152,7 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr, if (IS_ERR(ssk)) return PTR_ERR(ssk); + set_bit(MPTCP_RTX_ENABLED, &msk->flags); mptcp_set_state(sk, TCP_SYN_SENT); subflow = mptcp_subflow_ctx(ssk); #ifdef CONFIG_TCP_MD5SIG @@ -4289,6 +4300,7 @@ static int mptcp_listen(struct socket *sock, int backlog) goto unlock; } + set_bit(MPTCP_RTX_ENABLED, &msk->flags); mptcp_set_state(sk, TCP_LISTEN); sock_set_flag(sk, SOCK_RCU_FREE); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 06a107d4e839..2b4c27426477 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -116,6 +116,7 @@ #define MPTCP_WORK_RTX 1 #define MPTCP_FALLBACK_DONE 2 #define MPTCP_WORK_CLOSE_SUBFLOW 3 +#define MPTCP_RTX_ENABLED 4 /* MPTCP socket release cb flags */ #define MPTCP_PUSH_PENDING 1 @@ -690,7 +691,7 @@ mptcp_send_active_reset_reason(struct sock *sk) enum sk_rst_reason reason; reason = sk_rst_convert_mptcp_reason(subflow->reset_reason); - tcp_send_active_reset(sk, GFP_ATOMIC, reason); + tcp_send_active_reset(sk, reason); } /* Made the fwd mem carried by the given skb available to the msk, diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index af81ad5e699d..01db7edce18a 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -2084,7 +2084,6 @@ static void subflow_ulp_clone(const struct request_sock *req, new_ctx->request_bkup = subflow_req->request_bkup; WRITE_ONCE(new_ctx->remote_id, subflow_req->remote_id); new_ctx->token = subflow_req->token; - new_ctx->thmac = subflow_req->thmac; /* the subflow req id is valid, fetched via subflow_check_req() * and subflow_token_join_request() diff --git a/net/mptcp/syncookies.c b/net/mptcp/syncookies.c index b5cac5701122..9474706641c1 100644 --- a/net/mptcp/syncookies.c +++ b/net/mptcp/syncookies.c @@ -26,7 +26,8 @@ struct join_entry { u32 local_nonce; u8 join_id; u8 local_id; - u8 backup; + u8 backup:1, + request_bkup:1; u8 valid; }; @@ -63,6 +64,7 @@ static void mptcp_join_store_state(struct join_entry *entry, entry->remote_nonce = subflow_req->remote_nonce; entry->local_nonce = subflow_req->local_nonce; entry->backup = subflow_req->backup; + entry->request_bkup = subflow_req->request_bkup; entry->join_id = subflow_req->remote_id; entry->local_id = subflow_req->local_id; entry->valid = 1; @@ -117,6 +119,7 @@ bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subfl subflow_req->remote_nonce = e->remote_nonce; subflow_req->local_nonce = e->local_nonce; subflow_req->backup = e->backup; + subflow_req->request_bkup = e->request_bkup; subflow_req->remote_id = e->join_id; subflow_req->local_id = e->local_id; subflow_req->token = e->token; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 4c04cd8d40a2..09874c26fd13 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -739,7 +739,7 @@ config NETFILTER_XTABLES_COMPAT bool "Netfilter Xtables 32bit support" depends on COMPAT help - This option provides a translation layer to run 32bit arp,ip(6),ebtables + This option provides a translation layer to run 32bit ip(6),ebtables binaries on 64bit kernels. If unsure, say N. diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index ea5fdd4f4ce7..5383aeafb0ae 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -747,9 +747,9 @@ sloop: if (cp->flags & IP_VS_CONN_F_SEQ_MASK) { *(p++) = IPVS_OPT_SEQ_DATA; *(p++) = sizeof(struct ip_vs_sync_conn_options); - hton_seq((struct ip_vs_seq *)p, &cp->in_seq); + hton_seq(&cp->in_seq, (struct ip_vs_seq *)p); p += sizeof(struct ip_vs_seq); - hton_seq((struct ip_vs_seq *)p, &cp->out_seq); + hton_seq(&cp->out_seq, (struct ip_vs_seq *)p); p += sizeof(struct ip_vs_seq); } /* Handle pe data */ @@ -999,10 +999,10 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer pp->name, state); continue; } - } else { - if (state >= IP_VS_CTPL_S_LAST) - IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n", - state); + } else if (state >= IP_VS_CTPL_S_LAST) { + IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n", + state); + continue; } ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol, @@ -1159,10 +1159,10 @@ static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *m retc = 40; goto out; } - } else { - if (state >= IP_VS_CTPL_S_LAST) - IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n", - state); + } else if (state >= IP_VS_CTPL_S_LAST) { + IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n", state); + retc = 40; + goto out; } if (ip_vs_conn_fill_param_sync(ipvs, af, s, ¶m, pe_data, pe_data_len, pe_name, pe_name_len)) { diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9b4e29557ec3..579ada063b1b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1077,6 +1077,8 @@ static int ctnetlink_start(struct netlink_callback *cb) } cb->data = filter; + if (filter) + cb->answer_flags = NLM_F_DUMP_FILTERED; return 0; } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 3ccf34fc1c53..64bc440b1181 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -423,7 +423,7 @@ static const char *sip_skip_whitespace(const char *dptr, const char *limit) dptr = sip_follow_continuation(dptr, limit); break; } - return dptr; + return dptr < limit ? dptr : NULL; } /* Search within a SIP header value, dealing with continuation lines */ diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c index f24288088c0d..c3fd398ffcd7 100644 --- a/net/netfilter/nf_log_syslog.c +++ b/net/netfilter/nf_log_syslog.c @@ -1073,12 +1073,12 @@ err1: static void __exit nf_log_syslog_exit(void) { - unregister_pernet_subsys(&nf_log_syslog_net_ops); nf_log_unregister(&nf_ip_logger); nf_log_unregister(&nf_arp_logger); nf_log_unregister(&nf_ip6_logger); nf_log_unregister(&nf_netdev_logger); nf_log_unregister(&nf_bridge_logger); + unregister_pernet_subsys(&nf_log_syslog_net_ops); } module_init(nf_log_syslog_init); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 765a92fa90d6..31fbd5a28937 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3215,7 +3215,7 @@ static int nft_trans_delhook(struct nft_hook *hook, { struct nft_trans_hook *trans_hook; - trans_hook = kmalloc_obj(*trans_hook, GFP_KERNEL); + trans_hook = kmalloc_obj(*trans_hook); if (!trans_hook) return -ENOMEM; diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 66c2016f6049..e751f2b9c51d 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -150,7 +150,7 @@ static int cttimeout_new_timeout(struct sk_buff *skb, goto err_proto_put; } - timeout = kzalloc(sizeof(*timeout), GFP_KERNEL); + timeout = kzalloc_obj(*timeout); if (timeout == NULL) { ret = -ENOMEM; goto err_proto_put; @@ -652,9 +652,9 @@ static void __exit cttimeout_exit(void) { nfnetlink_subsys_unregister(&cttimeout_subsys); - unregister_pernet_subsys(&cttimeout_ops); RCU_INIT_POINTER(nf_ct_timeout_hook, NULL); synchronize_net(); + unregister_pernet_subsys(&cttimeout_ops); } module_init(cttimeout_init); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 6c7fa2ed34f5..d923f2cb1398 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -228,13 +228,18 @@ static void __nfulnl_flush(struct nfulnl_instance *inst); static void __instance_destroy(struct nfulnl_instance *inst) { + spin_lock(&inst->lock); + if (inst->copy_mode == NFULNL_COPY_DISABLED) { + /* attempt to UNBIND a queue already pending + * destruction via netlink close event. Ignore. + */ + spin_unlock(&inst->lock); + return; + } + /* first pull it out of the global list */ hlist_del_rcu(&inst->hlist); - /* then flush all pending packets from skb */ - - spin_lock(&inst->lock); - /* lockless readers wont be able to use us */ inst->copy_mode = NFULNL_COPY_DISABLED; @@ -1233,8 +1238,8 @@ static void __exit nfnetlink_log_fini(void) { nfnetlink_subsys_unregister(&nfulnl_subsys); netlink_unregister_notifier(&nfulnl_rtnl_notifier); - unregister_pernet_subsys(&nfnl_log_net_ops); nf_log_unregister(&nfulnl_logger); + unregister_pernet_subsys(&nfnl_log_net_ops); } MODULE_DESCRIPTION("netfilter userspace logging"); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 0da39eaed255..41d37442f186 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -1513,6 +1513,7 @@ struct ctrl_dump_policy_ctx { struct netlink_policy_dump_state *state; const struct genl_family *rt; struct genl_op_iter *op_iter; + struct module *owner; u32 op; u16 fam_id; u8 dump_map:1, @@ -1555,6 +1556,9 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) return -ENOENT; ctx->rt = rt; + ctx->owner = rt->module; + if (!try_module_get(ctx->owner)) + return -ENOENT; if (tb[CTRL_ATTR_OP]) { struct genl_split_ops doit, dump; @@ -1565,7 +1569,7 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) err = genl_get_cmd_both(ctx->op, rt, &doit, &dump); if (err) { NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); - return err; + goto err_put_owner; } if (doit.policy) { @@ -1583,16 +1587,20 @@ static int ctrl_dumppolicy_start(struct netlink_callback *cb) goto err_free_state; } - if (!ctx->state) - return -ENODATA; + if (!ctx->state) { + err = -ENODATA; + goto err_put_owner; + } ctx->dump_map = 1; return 0; } ctx->op_iter = kmalloc_obj(*ctx->op_iter); - if (!ctx->op_iter) - return -ENOMEM; + if (!ctx->op_iter) { + err = -ENOMEM; + goto err_put_owner; + } genl_op_iter_init(rt, ctx->op_iter); ctx->dump_map = genl_op_iter_next(ctx->op_iter); @@ -1624,6 +1632,8 @@ err_free_state: netlink_policy_dump_free(ctx->state); err_free_op_iter: kfree(ctx->op_iter); +err_put_owner: + module_put(ctx->owner); return err; } @@ -1760,6 +1770,7 @@ static int ctrl_dumppolicy_done(struct netlink_callback *cb) kfree(ctx->op_iter); netlink_policy_dump_free(ctx->state); + module_put(ctx->owner); return 0; } diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 46c1d66aad8c..868d9fdf3afd 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -288,7 +288,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh, if (*ext_hdrs & OFPIEH12_ESP) *ext_hdrs |= OFPIEH12_UNREP; if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST | - OFPIEH12_ROUTER | IPPROTO_FRAGMENT | + OFPIEH12_ROUTER | OFPIEH12_FRAG | OFPIEH12_AUTH | OFPIEH12_UNREP)) || dest_options_header_count >= 2) { *ext_hdrs |= OFPIEH12_UNSEQ; @@ -301,7 +301,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh, *ext_hdrs |= OFPIEH12_UNREP; if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER | - IPPROTO_FRAGMENT | OFPIEH12_UNREP)) || + OFPIEH12_FRAG | OFPIEH12_UNREP)) || dest_options_header_count >= 2) { *ext_hdrs |= OFPIEH12_UNSEQ; } diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index 67d5b8c0fe79..1e0f9d193eb0 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -257,11 +257,13 @@ static int tbl_mask_array_realloc(struct flow_table *tbl, int size) if (ovsl_dereference(old->masks[i])) new->masks[new->count++] = old->masks[i]; } - call_rcu(&old->rcu, mask_array_rcu_cb); } rcu_assign_pointer(tbl->mask_array, new); + if (old) + call_rcu(&old->rcu, mask_array_rcu_cb); + return 0; } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index b22cda322136..76bde7906d49 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, int size_max, void **data) { union tpacket_uhdr ph; - int tp_len, off; + u32 tp_len; + int off; ph.raw = frame; @@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, break; } if (unlikely(tp_len > size_max)) { - pr_err("packet size is too long (%d > %d)\n", tp_len, size_max); + pr_err("packet size is too long (%u > %d)\n", tp_len, size_max); return -EMSGSIZE; } diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c index a30fa56e6aa3..78347c937af7 100644 --- a/net/qrtr/af_qrtr.c +++ b/net/qrtr/af_qrtr.c @@ -9,6 +9,7 @@ #include <linux/termios.h> /* For TIOCINQ/OUTQ */ #include <linux/spinlock.h> #include <linux/wait.h> +#include <linux/workqueue.h> #include <net/sock.h> @@ -120,8 +121,10 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports); * @nid: node id * @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port * @qrtr_tx_lock: lock for qrtr_tx_flow inserts + * @hello_sent: hello packet send successful * @rx_queue: receive queue * @item: list item for broadcast list + * @say_hello: delayed work for sending hello packet */ struct qrtr_node { struct mutex ep_lock; @@ -132,8 +135,11 @@ struct qrtr_node { struct xarray qrtr_tx_flow; struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */ + bool hello_sent; + struct sk_buff_head rx_queue; struct list_head item; + struct delayed_work say_hello; }; /** @@ -187,6 +193,8 @@ static void __qrtr_node_release(struct kref *kref) list_del(&node->item); mutex_unlock(&qrtr_node_lock); + cancel_delayed_work_sync(&node->say_hello); + skb_queue_purge(&node->rx_queue); /* Free tx flow counters */ @@ -341,6 +349,14 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, size_t len = skb->len; int rc, confirm_rx; + mutex_lock(&node->ep_lock); + if (!node->hello_sent && type != QRTR_TYPE_HELLO) { + mutex_unlock(&node->ep_lock); + kfree_skb(skb); + return -EAGAIN; + } + mutex_unlock(&node->ep_lock); + confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type); if (confirm_rx < 0) { kfree_skb(skb); @@ -353,7 +369,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, hdr->src_node_id = cpu_to_le32(from->sq_node); hdr->src_port_id = cpu_to_le32(from->sq_port); if (to->sq_port == QRTR_PORT_CTRL) { - hdr->dst_node_id = cpu_to_le32(node->nid); + hdr->dst_node_id = cpu_to_le32(READ_ONCE(node->nid)); hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL); } else { hdr->dst_node_id = cpu_to_le32(to->sq_node); @@ -372,6 +388,8 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, rc = node->ep->xmit(node->ep, skb); else kfree_skb(skb); + if (!rc && type == QRTR_TYPE_HELLO) + node->hello_sent = true; mutex_unlock(&node->ep_lock); } /* Need to ensure that a subsequent message carries the otherwise lost @@ -379,6 +397,9 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb, if (rc && confirm_rx) qrtr_tx_flow_failed(node, to->sq_node, to->sq_port); + if (rc == -EAGAIN && type == QRTR_TYPE_HELLO) + schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100)); + return rc; } @@ -416,7 +437,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid) spin_lock_irqsave(&qrtr_nodes_lock, flags); radix_tree_insert(&qrtr_nodes, nid, node); if (node->nid == QRTR_EP_NID_AUTO) - node->nid = nid; + WRITE_ONCE(node->nid, nid); spin_unlock_irqrestore(&qrtr_nodes_lock, flags); } @@ -570,6 +591,38 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt, return skb; } +static void qrtr_hello_work(struct work_struct *work) +{ + struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL}; + struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL}; + struct qrtr_ctrl_pkt *pkt; + struct qrtr_node *node; + struct qrtr_sock *ctrl; + struct sk_buff *skb; + + node = container_of(to_delayed_work(work), struct qrtr_node, say_hello); + + /* NS must be bound before we can send; retry with backoff if not ready */ + ctrl = qrtr_port_lookup(QRTR_PORT_CTRL); + if (!ctrl) { + schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100)); + return; + } + + skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL); + if (!skb) { + qrtr_port_put(ctrl); + schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100)); + return; + } + + pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO); + from.sq_node = qrtr_local_nid; + to.sq_node = node->nid; + qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to); + qrtr_port_put(ctrl); +} + /** * qrtr_endpoint_register() - register a new endpoint * @ep: endpoint to register @@ -595,6 +648,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) node->nid = QRTR_EP_NID_AUTO; node->ep = ep; + node->hello_sent = false; + INIT_DELAYED_WORK(&node->say_hello, qrtr_hello_work); + xa_init(&node->qrtr_tx_flow); mutex_init(&node->qrtr_tx_lock); @@ -605,6 +661,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid) mutex_unlock(&qrtr_node_lock); ep->node = node; + /* Initiate HELLO handshake from the core layer */ + schedule_delayed_work(&node->say_hello, 0); + return 0; } EXPORT_SYMBOL_GPL(qrtr_endpoint_register); @@ -879,6 +938,9 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb, mutex_lock(&qrtr_node_lock); list_for_each_entry(node, &qrtr_all_nodes, item) { + /* Skip nodes with no assigned node ID yet. */ + if (READ_ONCE(node->nid) == QRTR_EP_NID_AUTO) + continue; skbn = pskb_copy(skb, GFP_KERNEL); if (!skbn) break; diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index c5e7e01db249..bcb090ee79d4 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -212,6 +212,7 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv, pr_err("failed to send lookup notification\n"); } +/* Announce the list of servers registered on the local node */ static int announce_servers(struct sockaddr_qrtr *sq) { struct qrtr_server *srv; @@ -326,38 +327,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast) return 0; } -static int say_hello(struct sockaddr_qrtr *dest) -{ - struct qrtr_ctrl_pkt pkt; - struct msghdr msg = { }; - struct kvec iv; - int ret; - - iv.iov_base = &pkt; - iv.iov_len = sizeof(pkt); - - memset(&pkt, 0, sizeof(pkt)); - pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO); - - msg.msg_name = (struct sockaddr *)dest; - msg.msg_namelen = sizeof(*dest); - - ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); - if (ret < 0) - pr_err("failed to send hello msg\n"); - - return ret; -} - -/* Announce the list of servers registered on the local node */ static int ctrl_cmd_hello(struct sockaddr_qrtr *sq) { - int ret; - - ret = say_hello(sq); - if (ret < 0) - return ret; - return announce_servers(sq); } @@ -774,10 +745,6 @@ int qrtr_ns_init(void) qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST; qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL; - ret = say_hello(&qrtr_ns.bcast_sq); - if (ret < 0) - goto err_wq; - /* As the qrtr ns socket owner and creator is the same module, we have * to decrease the qrtr module reference count to guarantee that it * remains zero after the ns socket is created, otherwise, executing diff --git a/net/rds/connection.c b/net/rds/connection.c index 7c8ab8e973e1..b6c4beb50eaf 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net, } /* - * This is called by transports as they're bringing down a connection. - * It clears partial message state so that the transport can start sending - * and receiving over this connection again in the future. It is up to - * the transport to have serialized this call with its send and recv. + * This is called by rds_conn_shutdown() once the transport has brought + * a path down. It clears partial message state so that the transport + * can start sending and receiving over this path again in the future. + * The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call, + * which is what serializes it against the send and receive-refill + * paths. */ static void rds_conn_path_reset(struct rds_conn_path *cp) { @@ -120,7 +122,16 @@ static void rds_conn_path_reset(struct rds_conn_path *cp) rds_stats_inc(s_conn_reset); rds_send_path_reset(cp); - cp->cp_flags = 0; + + /* Clear the bits the reset is responsible for individually: a + * blanket cp_flags = 0 is a plain store that can clobber a + * concurrent atomic read-modify-write on the same word. + * RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the + * caller, rds_conn_shutdown(), which releases them once the + * teardown is complete. + */ + clear_bit(RDS_LL_SEND_FULL, &cp->cp_flags); + clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags); /* Do not clear next_rx_seq here, else we cannot distinguish * retransmitted packets from new packets, and will hand all @@ -406,28 +417,70 @@ void rds_conn_shutdown(struct rds_conn_path *cp) } mutex_unlock(&cp->cp_cm_lock); + /* Quiesce the transmit and receive-refill paths by + * acquiring their bit locks, not merely waiting for + * them to be released: with a plain wait, either path + * can re-take its lock the instant after we sample it + * clear and then run concurrently with the transport + * shutdown and the path reset below. Holding both + * locks across the teardown makes that structurally + * impossible. + */ wait_event(cp->cp_waitq, - !test_bit(RDS_IN_XMIT, &cp->cp_flags)); + !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags)); wait_event(cp->cp_waitq, - !test_bit(RDS_RECV_REFILL, &cp->cp_flags)); + !test_and_set_bit(RDS_RECV_REFILL, &cp->cp_flags)); conn->c_trans->conn_path_shutdown(cp); rds_conn_path_reset(cp); + /* Release the two locks and wake any waiter (e.g. + * rds_tcp_reset_callbacks()) that blocked on them while + * we held them. The unlock orders the transport's ring + * re-initialization and the path reset above before + * either bit is seen clear. rds_conn_path_reset() leaves + * both bits alone: ownership ends here, not inside the + * reset. + */ + clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags); + clear_bit_unlock(RDS_RECV_REFILL, &cp->cp_flags); + wake_up_all(&cp->cp_waitq); + if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING, - RDS_CONN_DOWN) && - !rds_conn_path_transition(cp, RDS_CONN_ERROR, RDS_CONN_DOWN)) { - /* This can happen - eg when we're in the middle of tearing - * down the connection, and someone unloads the rds module. - * Quite reproducible with loopback connections. - * Mostly harmless. + /* The path was dropped again while we tore it + * down: by a socket state-change callback in + * irq context on receipt of a FIN, or by an + * accept that claimed the path just before a + * drop put it back to RDS_CONN_ERROR and then + * installed a fresh socket on it. Unless a + * pending destroy suppressed it, the drop also + * queued another shutdown pass, and that pass + * must run, because it is what tears down + * whatever attached to the path after the + * transport shutdown above sampled its state. + * Consuming the RDS_CONN_ERROR here would turn + * that pass into a no-op: leave the state + * alone, and let the pass finish the job. * - * Note that this also happens with rds-tcp because - * we could have triggered rds_conn_path_drop in irq - * mode from rds_tcp_state change on the receipt of - * a FIN, thus we need to recheck for RDS_CONN_ERROR - * here. + * Quiesce the reconnect timer before bailing + * out, though. When a pending destroy did + * suppress the queue, no later pass runs, and + * rds_conn_path_destroy() is about to flush + * cp_down_w and free the path: it must not + * find cp_conn_w still armed. A successor + * pass, when there is one, re-arms the + * reconnect from its own tail. + */ + cancel_delayed_work_sync(&cp->cp_conn_w); + clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags); + + if (rds_conn_path_state(cp) == RDS_CONN_ERROR) + return; + /* No current cp_state writer leaves a + * DISCONNECTING path in any state but + * RDS_CONN_ERROR; report loudly if one ever + * does. */ rds_conn_path_error(cp, "%s: failed to transition " "to state DOWN, current state " diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index 357128d34a54..bd6cb3ffaa57 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn) static void release_refill(struct rds_connection *conn) { - clear_bit(RDS_RECV_REFILL, &conn->c_flags); - smp_mb__after_atomic(); + clear_bit_unlock(RDS_RECV_REFILL, &conn->c_flags); /* We don't use wait_on_bit()/wake_up_bit() because our waking is in a * hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. */ - if (waitqueue_active(&conn->c_waitq)) + if (wq_has_sleeper(&conn->c_waitq)) wake_up_all(&conn->c_waitq); } @@ -392,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp) /* the goal here is to just make sure that someone, somewhere * is posting buffers. If we can't get the refill lock, - * let them do their thing + * let them do their thing. The holder may also be + * rds_conn_shutdown() tearing the path down, in which case + * there is nothing to post. */ if (!acquire_refill(conn)) return; diff --git a/net/rds/info.c b/net/rds/info.c index 31e7ad108459..a57f81a05c87 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -205,7 +205,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, sockopt_t *opt) * iterator code to allocate and hand it back. */ npages = iov_iter_npages(&opt->iter_out, INT_MAX); - pages = kvmalloc_array(npages, sizeof(*pages), GFP_KERNEL); + pages = kvmalloc_objs(*pages, npages); if (!pages) { ret = -ENOMEM; goto out; diff --git a/net/rds/message.c b/net/rds/message.c index f25f2592586f..47d5e9ab9b10 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -431,7 +431,9 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in for (i = 0; i < rm->data.op_nents; ++i) { sg_set_page(&rm->data.op_sg[i], virt_to_page((void *)page_addrs[i]), - PAGE_SIZE, 0); + i == rm->data.op_nents - 1 + ? total_len - (i * PAGE_SIZE) + : PAGE_SIZE, 0); } return rm; diff --git a/net/rds/send.c b/net/rds/send.c index 15a1b97f13e7..1afa981e5c06 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp) * hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. + * + * wq_has_sleeper() supplies the full barrier that orders the wait + * queue read after the bit clear; clear_bit_unlock() alone is only + * a release and would let this check read a stale empty queue, + * losing the wake-up. */ - if (waitqueue_active(&cp->cp_waitq)) + if (wq_has_sleeper(&cp->cp_waitq)) wake_up_all(&cp->cp_waitq); } @@ -239,8 +244,11 @@ restart: WRITE_ONCE(cp->cp_send_gen, send_gen); /* - * rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT, - * we do the opposite to avoid races. + * rds_conn_shutdown() sets the conn state and then acquires + * RDS_IN_XMIT; we take the lock first and then check the state. + * Ownership is decided by the atomic RMW on the cp_flags word: + * if the teardown won the bit we back off here, and if we won + * it the teardown waits until we release it. */ if (!rds_conn_path_up(cp)) { release_in_xmit(cp); diff --git a/net/rds/tcp.c b/net/rds/tcp.c index b263634ac750..774a71f88d37 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -115,46 +115,90 @@ void rds_tcp_restore_callbacks(struct socket *sock, } /* - * rds_tcp_reset_callbacks() switches the to the new sock and - * returns the existing tc->t_sock. + * rds_tcp_reset_callbacks() switches a path to a new socket and + * releases the old one it finds in tc->t_sock, resolving a duelling + * SYN. * - * The only functions that set tc->t_sock are rds_tcp_set_callbacks - * and rds_tcp_reset_callbacks. Send and receive trust that - * it is set. The absence of RDS_CONN_UP bit protects those paths - * from being called while it isn't set. + * tc->t_sock is set by rds_tcp_set_callbacks() and cleared by + * rds_tcp_restore_callbacks(). Four paths write it: the active + * connect in rds_tcp_conn_path_connect(), which sets it and clears it + * again on failure; the accept path in rds_tcp_accept_one(), which + * sets it for a path with no socket yet; the teardown in + * rds_tcp_conn_path_shutdown(), which clears it; and the swap done + * here, which does both. The connect and accept paths are serialized + * against each other by t_conn_path_lock. Send and receive trust + * that it is set: the absence of RDS_CONN_UP protects those paths + * from being called while it isn't, and the swap done here runs under + * RDS_IN_XMIT so that it cannot interleave with a sender already + * inside rds_send_xmit(). */ void rds_tcp_reset_callbacks(struct socket *sock, struct rds_conn_path *cp) { struct rds_tcp_connection *tc = cp->cp_transport_data; - struct socket *osock = tc->t_sock; - - if (!osock) - goto newsock; + struct socket *osock; /* Need to resolve a duelling SYN between peers. * We have an outstanding SYN to this peer, which may * potentially have transitioned to the RDS_CONN_UP state, * so we must quiesce any send threads before resetting - * cp_transport_data. We quiesce these threads by setting - * cp_state to something other than RDS_CONN_UP, and then - * waiting for any existing threads in rds_send_xmit to - * complete release_in_xmit(). (Subsequent threads entering - * rds_send_xmit() will bail on !rds_conn_up(). + * cp_transport_data. Setting cp_state to something other + * than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT + * excludes any thread already inside rds_send_xmit() - or a + * teardown in rds_conn_shutdown(), which holds the same lock + * for the duration of the transport shutdown - for the whole + * socket swap and the rds_send_path_reset() below. * - * However an incoming syn-ack at this point would end up - * marking the conn as RDS_CONN_UP, and would again permit - * rds_send_xmi() threads through, so ideally we would - * synchronize on RDS_CONN_UP after lock_sock(), but cannot - * do that: waiting on !RDS_IN_XMIT after lock_sock() may - * end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT - * would not get set. As a result, we set c_state to - * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change - * cannot mark rds_conn_path_up() in the window before lock_sock() + * An incoming syn-ack at this point would end up marking the + * conn as RDS_CONN_UP, and would again permit rds_send_xmit() + * threads through, so ideally we would synchronize on + * RDS_CONN_UP after lock_sock(), but cannot do that: acquiring + * RDS_IN_XMIT after lock_sock() may end up deadlocking with + * tcp_sendmsg(), which takes the socket lock while holding + * RDS_IN_XMIT. As a result, we set c_state to + * RDS_CONN_RESETTING, to ensure that rds_tcp_state_change + * cannot mark rds_conn_path_up() in the window before + * lock_sock(). + * + * Only make that transition if the path is still connecting + * (or already resetting from an earlier duel). A path in any + * other state - typically RDS_CONN_DISCONNECTING or + * RDS_CONN_ERROR with a shutdown in flight - is dropped + * instead. That still replaces its state, with RDS_CONN_ERROR, + * and, unless a pending destroy is about to reap the whole + * connection anyway, queues one more shutdown pass. A shutdown + * already in flight leaves that RDS_CONN_ERROR alone when it + * finishes; the queued pass then completes the transition to + * RDS_CONN_DOWN and tears down anything that attached to the + * path in the meantime. + */ + if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING, + RDS_CONN_RESETTING) && + !rds_conn_path_transition(cp, RDS_CONN_RESETTING, + RDS_CONN_RESETTING)) + rds_conn_path_drop(cp, 0); + wait_event(cp->cp_waitq, + !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags)); + + /* Read t_sock only while owning RDS_IN_XMIT, never before the + * wait: the teardown in rds_conn_shutdown() releases the old + * socket and clears t_sock, so a pointer sampled earlier can + * be stale by the time we wake up. The teardown holds the + * same lock while it does so, so what we read here cannot + * change under us until we release it. + */ + osock = tc->t_sock; + if (!osock) + goto newsock; + + /* reset receive side state for rds_tcp_data_recv() for osock. + * + * The sync cancels while owning RDS_IN_XMIT rely on cp_wq + * being ordered: a teardown blocked on the bit occupies + * cp_wq's only execution slot, so cp_send_w and cp_recv_w are + * pending at most and the cancels never flush. Nothing here + * may flush or wait on cp_wq itself. */ - atomic_set(&cp->cp_state, RDS_CONN_RESETTING); - wait_event(cp->cp_waitq, !test_bit(RDS_IN_XMIT, &cp->cp_flags)); - /* reset receive side state for rds_tcp_data_recv() for osock */ cancel_delayed_work_sync(&cp->cp_send_w); cancel_delayed_work_sync(&cp->cp_recv_w); lock_sock(osock->sk); @@ -172,6 +216,9 @@ newsock: lock_sock(sock->sk); rds_tcp_set_callbacks(sock, cp); release_sock(sock->sk); + + clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags); + wake_up_all(&cp->cp_waitq); } /* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index a3db9b057084..13fa60c1985b 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn) if (rs_tcp->t_sock) { /* Duelling SYN has been handled in rds_tcp_accept_one() */ rds_tcp_reset_callbacks(new_sock, cp); - /* rds_connect_path_complete() marks RDS_CONN_UP */ + /* rds_connect_path_complete() marks RDS_CONN_UP, or, + * if a concurrent shutdown won the duel, drops the + * path again and the pass that drop queues reaps the + * socket installed above. + */ rds_connect_path_complete(cp, RDS_CONN_RESETTING); } else { rds_tcp_set_callbacks(new_sock, cp); diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index a0aa78d89289..cbd26da44951 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -210,7 +210,7 @@ static int rxrpc_preparse_xdr_yfs_rxgk(struct key_preparsed_payload *prep, if (!token) goto nomem; - token->rxgk = kzalloc(struct_size_t(struct rxgk_key, _key, raw_keylen), GFP_KERNEL); + token->rxgk = kzalloc_flex(struct rxgk_key, _key, raw_keylen); if (!token->rxgk) goto nomem_token; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index b4415d358c91..19501dc99464 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act) + nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */ + cookie_len /* TCA_ACT_COOKIE */ + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */ + /* TCA_ACT_USED_HW_STATS */ + + nla_total_size(sizeof(struct nla_bitfield32)) + + nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */ + nla_total_size(0) /* TCA_ACT_STATS nested */ + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */ /* TCA_STATS_BASIC */ + nla_total_size_64bit(sizeof(struct gnet_stats_basic)) - /* TCA_STATS_PKT64 */ - + nla_total_size_64bit(sizeof(u64)) + /* TCA_STATS_BASIC_HW */ + + nla_total_size_64bit(sizeof(struct gnet_stats_basic)) + /* TCA_STATS_PKT64, emitted by both of the basic copies above */ + + 2 * nla_total_size_64bit(sizeof(u64)) + /* TCA_STATS_RATE_EST */ + + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est)) + /* TCA_STATS_RATE_EST64 */ + + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64)) /* TCA_STATS_QUEUE */ + nla_total_size_64bit(sizeof(struct gnet_stats_queue)) + nla_total_size(0) /* TCA_ACT_OPTIONS nested */ @@ -1191,18 +1200,13 @@ EXPORT_SYMBOL(tcf_action_exec); int tcf_action_destroy(struct tc_action *actions[], int bind) { - const struct tc_action_ops *ops; struct tc_action *a; int ret = 0, i; tcf_act_for_each_action(i, a, actions) { actions[i] = NULL; - ops = a->ops; - ret = __tcf_idr_release(a, bind, true); - if (ret == ACT_P_DELETED) - module_put(ops->owner); - else if (ret < 0) - return ret; + /* Drop our reference even if the action is still bound to a filter. */ + ret = tcf_idr_release(a, bind); } return ret; } @@ -1688,12 +1692,12 @@ out_nlmsg_trim: static int tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, - struct tc_action *actions[], int event, + struct tc_action *actions[], size_t attr_size, int event, struct netlink_ext_ack *extack) { struct sk_buff *skb; - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL); if (!skb) return -ENOBUFS; if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, @@ -1858,11 +1862,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[]) static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net, struct tc_action *action) { - size_t attr_size = tcf_action_fill_size(action); struct tc_action *actions[TCA_ACT_MAX_PRIO] = { [0] = action, }; struct sk_buff *skb; + size_t attr_size; + + attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action)); skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL); if (!skb) @@ -1879,15 +1885,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net, static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action) { const struct tc_action_ops *ops = action->ops; - struct sk_buff *skb; + struct sk_buff *skb = NULL; int ret; - if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) { - skb = NULL; - } else { + if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) { skb = tcf_reoffload_del_notify_msg(net, action); + /* The action has already lost its hardware instance and is + * skip_sw, so it must be released whether or not the + * notification can be built. Drop the notification rather + * than leave an action behind that processes no packets. + */ if (IS_ERR(skb)) - return PTR_ERR(skb); + skb = NULL; } ret = tcf_idr_release_unsafe(action); @@ -2044,7 +2053,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, attr_size = tcf_action_full_attrs_size(attr_size); if (event == RTM_GETACTION) - ret = tcf_get_notify(net, portid, n, actions, event, extack); + ret = tcf_get_notify(net, portid, n, actions, attr_size, event, + extack); else { /* delete */ ret = tcf_del_notify(net, n, actions, portid, attr_size, extack); if (ret) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index fdbfcaa3e2ab..5d228a402204 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -240,7 +240,7 @@ static int tcf_gate_copy_entries(struct tcf_gate_params *dst, list_for_each_entry(entry, &src->entries, list) { struct tcfg_gate_entry *new; - new = kzalloc(sizeof(*new), GFP_ATOMIC); + new = kzalloc_obj(*new, GFP_ATOMIC); if (!new) { NL_SET_ERR_MSG(extack, "Not enough memory for entry"); return -ENOMEM; @@ -415,7 +415,7 @@ static int tcf_gate_init(struct net *net, struct nlattr *nla, if (err < 0) goto release_idr; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc_obj(*p); if (!p) { err = -ENOMEM; goto chain_put; diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index ff401ace4f3d..503834853306 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -526,7 +526,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, } t = to_tunnel_key(*a); - params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); + params_new = kzalloc_obj(*params_new); if (unlikely(!params_new)) { NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters"); ret = -ENOMEM; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 9966766661d5..c47d2ee13641 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3254,7 +3254,7 @@ errout: tcf_chain_put(chain); errout_block: tcf_block_release(q, block, true); - if (err == -EAGAIN) + if (err == -EAGAIN && n->nlmsg_type == RTM_NEWCHAIN) /* Replay the request. */ goto replay; return err; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 0e275b58151c..1cefea571efd 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -1703,6 +1703,11 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, return -EINVAL; } nla_opt_msk = nla_next(nla_opt_msk, &msk_depth); + + if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) { + NL_SET_ERR_MSG(extack, "A mask attribute is invalid"); + return -EINVAL; + } } return 0; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 0d1324c90583..0f211f030fd9 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/refcount.h> #include <linux/skbuff.h> #include <net/dst.h> #include <net/route.h> @@ -41,6 +42,7 @@ struct route4_head { struct route4_bucket { /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ struct route4_filter __rcu *ht[16 + 16 + 1]; + refcount_t filters_ref; struct rcu_head rcu; }; @@ -336,7 +338,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, struct route4_filter *nf; struct route4_bucket *b; unsigned int h = 0; - int i, h1; + int h1; if (!head || !f) return -EINVAL; @@ -362,23 +364,14 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, tcf_exts_get_net(&f->exts); tcf_queue_work(&f->rwork, route4_delete_filter_work); - /* Strip RTNL protected tree */ - for (i = 0; i <= 32; i++) { - struct route4_filter *rt; - - rt = rtnl_dereference(b->ht[i]); - if (rt) - goto out; + if (refcount_dec_and_test(&b->filters_ref)) { + RCU_INIT_POINTER(head->table[to_hash(h)], NULL); + kfree_rcu(b, rcu); } - - /* OK, session has no flows */ - RCU_INIT_POINTER(head->table[to_hash(h)], NULL); - kfree_rcu(b, rcu); break; } } -out: *last = true; for (h1 = 0; h1 <= 256; h1++) { if (rcu_access_pointer(head->table[h1])) { @@ -400,8 +393,9 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = { static int route4_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct route4_filter *f, u32 handle, struct route4_head *head, - struct nlattr **tb, struct nlattr *est, int new, - u32 flags, struct netlink_ext_ack *extack) + struct nlattr **tb, struct nlattr *est, + struct route4_filter *fold, u32 flags, + struct netlink_ext_ack *extack) { u32 id = 0, to = 0, nhandle = 0x8000; struct route4_filter *fp; @@ -414,7 +408,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, return err; if (tb[TCA_ROUTE4_TO]) { - if (new && handle & 0x8000) { + if (!fold && handle & 0x8000) { NL_SET_ERR_MSG(extack, "Invalid handle"); return -EINVAL; } @@ -437,14 +431,14 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, } else nhandle |= 0xFFFF << 16; - if (handle && new) { + if (handle && (!fold || nhandle == (handle & ~0x7F00))) nhandle |= handle & 0x7F00; - if (nhandle != handle) { - NL_SET_ERR_MSG_FMT(extack, - "Handle mismatch constructed: %x (expected: %x)", - handle, nhandle); - return -EINVAL; - } + + if (handle && !fold && nhandle != handle) { + NL_SET_ERR_MSG_FMT(extack, + "Handle mismatch constructed: %x (expected: %x)", + handle, nhandle); + return -EINVAL; } if (!nhandle) { @@ -459,6 +453,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, if (b == NULL) return -ENOBUFS; + refcount_set(&b->filters_ref, 1); rcu_assign_pointer(head->table[h1], b); } else { unsigned int h2 = from_hash(nhandle >> 16); @@ -466,8 +461,14 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, for (fp = rtnl_dereference(b->ht[h2]); fp; fp = rtnl_dereference(fp->next)) - if (fp->handle == f->handle) + if (fp != fold && fp->handle == nhandle) { + NL_SET_ERR_MSG_FMT(extack, + "Handle %x is already in use", + nhandle); return -EEXIST; + } + + refcount_inc(&b->filters_ref); } if (tb[TCA_ROUTE4_TO]) @@ -500,9 +501,8 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, struct route4_filter *fold, *f1, *pfp, *f = NULL; struct route4_bucket *b; struct nlattr *tb[TCA_ROUTE4_MAX + 1]; - unsigned int h, th; + unsigned int h; int err; - bool new = true; if (!handle) { NL_SET_ERR_MSG(extack, "Creating with handle of 0 is invalid"); @@ -539,11 +539,10 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, f->tp = fold->tp; f->bkt = fold->bkt; - new = false; } err = route4_set_parms(net, tp, base, f, handle, head, tb, - tca[TCA_RATE], new, flags, extack); + tca[TCA_RATE], fold, flags, extack); if (err < 0) goto errout; @@ -560,17 +559,20 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, rcu_assign_pointer(*fp, f); if (fold) { - th = to_hash(fold->handle); + b = fold->bkt; h = from_hash(fold->handle >> 16); - b = rtnl_dereference(head->table[th]); - if (b) { - fp = &b->ht[h]; - for (pfp = rtnl_dereference(*fp); pfp; - fp = &pfp->next, pfp = rtnl_dereference(*fp)) { - if (pfp == fold) { - rcu_assign_pointer(*fp, fold->next); - break; + fp = &b->ht[h]; + for (pfp = rtnl_dereference(*fp); pfp; + fp = &pfp->next, pfp = rtnl_dereference(*fp)) { + if (pfp == fold) { + rcu_assign_pointer(*fp, fold->next); + if (refcount_dec_and_test(&b->filters_ref)) { + unsigned int th = to_hash(fold->handle); + + RCU_INIT_POINTER(head->table[th], NULL); + kfree_rcu(b, rcu); } + break; } } } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index ac6d0fa5a40e..a3e65c8cf29e 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp) refcount_set(&root_ht->refcnt, 1); root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0); + if (root_ht->handle == 0) { + kfree(root_ht); + return -ENOMEM; + } root_ht->prio = tp->prio; root_ht->is_root = true; idr_init(&root_ht->handle_idr); @@ -695,21 +699,33 @@ out: return ret; } -static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid) +static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid, int *err) { u32 index = htid | 0x800; u32 max = htid | 0xFFF; + *err = 0; + if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) { index = htid + 1; - if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, - GFP_KERNEL)) - index = max; + *err = idr_alloc_u32(&ht->handle_idr, NULL, &index, max, + GFP_KERNEL); + if (*err) + return 0; } return index; } +static int u32_kid_extack(int err, struct netlink_ext_ack *extack) +{ + if (err == -ENOSPC) + NL_SET_ERR_MSG_MOD(extack, "Hash table node ID pool exhausted"); + else + NL_SET_ERR_MSG_MOD(extack, "Failed to allocate node ID"); + return err; +} + static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { [TCA_U32_CLASSID] = { .type = NLA_U32 }, [TCA_U32_HASH] = { .type = NLA_U32 }, @@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, * handle which is used to uniquely identify the match entry. */ if (!TC_U32_NODE(handle)) { - handle = gen_new_kid(ht, htid); + handle = gen_new_kid(ht, htid, &err); + if (err) + return u32_kid_extack(err, extack); } else { handle = htid | TC_U32_NODE(handle); err = idr_alloc_u32(&ht->handle_idr, NULL, &handle, @@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, /* The user did not give us a handle; lets just generate one * from the table's pool of nodeids. */ - handle = gen_new_kid(ht, htid); + handle = gen_new_kid(ht, htid, &err); + if (err) + return u32_kid_extack(err, extack); } if (tb[TCA_U32_SEL] == NULL) { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 90503e59e6e3..463ededcdcfe 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1385,7 +1385,7 @@ err_out4: err_out3: qdisc_lock_uninit(sch, ops); netdev_put(dev, &sch->dev_tracker); - qdisc_free(sch); + qdisc_free_rcu(sch); err_out2: bpf_module_put(ops, ops->owner); err_out: diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 91b1ef824afa..8621d057edd9 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -82,8 +82,9 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero"); return -EINVAL; } + quantum = clamp_t(u32, quantum, 256, 1 << 20); } else - quantum = psched_mtu(qdisc_dev(sch)); + quantum = clamp_t(u32, (u32)psched_mtu(qdisc_dev(sch)), 256, 1 << 20); if (cl != NULL) { if (tca[TCA_RATE]) { diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 4f678d4ff10e..4947def7c49e 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -208,9 +208,11 @@ static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q) static void dualpi2_calculate_c_protection(struct Qdisc *sch, struct dualpi2_sched_data *q, u32 wc) { + u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20); + q->c_protection_wc = wc; q->c_protection_wl = MAX_WC - wc; - q->c_protection_init = (s32)psched_mtu(qdisc_dev(sch)) * + q->c_protection_init = (s32)mtu * ((int)q->c_protection_wc - (int)q->c_protection_wl); dualpi2_reset_c_protection(q); } @@ -285,8 +287,9 @@ static bool must_drop(struct Qdisc *sch, struct dualpi2_sched_data *q, u64 local_l_prob; bool overload; u32 prob; + u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20); - if (sch->qstats.backlog < 2 * psched_mtu(qdisc_dev(sch))) + if (sch->qstats.backlog < 2 * mtu) return false; prob = READ_ONCE(q->pi2_prob); @@ -712,7 +715,8 @@ static u32 get_memory_limit(struct Qdisc *sch, u32 limit) /* Apply rule of thumb, i.e., doubling the packet length, * to further include per packet overhead in memory_limit. */ - u64 memlim = mul_u32_u32(limit, 2 * psched_mtu(qdisc_dev(sch))); + u64 memlim = mul_u32_u32(limit, 2 * clamp_t(u32, psched_mtu(qdisc_dev(sch)), + 1, 1 << 20)); if (upper_32_bits(memlim)) return U32_MAX; diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c index 25fcf4079fec..6cc902a03838 100644 --- a/net/sched/sch_ets.c +++ b/net/sched/sch_ets.c @@ -83,11 +83,7 @@ static int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr, unsigned int *quantum, struct netlink_ext_ack *extack) { - *quantum = nla_get_u32(attr); - if (!*quantum) { - NL_SET_ERR_MSG(extack, "ETS quantum cannot be zero"); - return -EINVAL; - } + *quantum = clamp_t(u32, nla_get_u32(attr), 256, 1 << 20); return 0; } @@ -632,11 +628,13 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt, return err; } /* If there are more bands than strict + quanta provided, the remaining - * ones are ETS with quantum of MTU. Initialize the missing values here. + * ones are ETS with quantum of max(MTU, 256). Initialize the missing + * values here. */ for (i = nstrict; i < nbands; i++) { if (!quanta[i]) - quanta[i] = psched_mtu(qdisc_dev(sch)); + quanta[i] = clamp_t(u32, (u32)psched_mtu(qdisc_dev(sch)), + 256, 1 << 20); } /* Before commit, make sure we can allocate all new qdiscs */ diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index 6144b5686f13..35f940b2205d 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -980,7 +980,7 @@ static int fq_resize(struct Qdisc *sch, u32 log) } static const struct netlink_range_validation iq_range = { - .max = INT_MAX, + .max = 1 << 20, }; static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = { @@ -1106,14 +1106,10 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt, nla_get_u32(tb[TCA_FQ_FLOW_PLIMIT])); if (tb[TCA_FQ_QUANTUM]) { - u32 quantum = nla_get_u32(tb[TCA_FQ_QUANTUM]); + u32 quantum = clamp_t(u32, nla_get_u32(tb[TCA_FQ_QUANTUM]), + 256, 1 << 20); - if (quantum > 0 && quantum <= (1 << 20)) { - WRITE_ONCE(q->quantum, quantum); - } else { - NL_SET_ERR_MSG_MOD(extack, "invalid quantum"); - err = -EINVAL; - } + WRITE_ONCE(q->quantum, quantum); } if (tb[TCA_FQ_INITIAL_QUANTUM]) @@ -1232,7 +1228,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt, sch->limit = 10000; q->flow_plimit = 100; mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20); - q->quantum = min_t(u32, 2 * mtu, 1 << 20); + q->quantum = clamp_t(u32, 2 * mtu, 256, 1 << 20); q->initial_quantum = min_t(u32, 10 * mtu, 1 << 20); q->flow_refill_delay = msecs_to_jiffies(40); q->flow_max_rate = ~0UL; diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index b27d95418707..5982847df8f8 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -341,7 +341,8 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt, nla_get_u32(tb[TCA_FQ_PIE_BETA])); if (tb[TCA_FQ_PIE_QUANTUM]) - WRITE_ONCE(q->quantum, nla_get_u32(tb[TCA_FQ_PIE_QUANTUM])); + WRITE_ONCE(q->quantum, + max(256U, nla_get_u32(tb[TCA_FQ_PIE_QUANTUM]))); if (tb[TCA_FQ_PIE_MEMORY_LIMIT]) WRITE_ONCE(q->memory_limit, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 4539dc2c6d38..6f6a6f0d5eb0 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1086,21 +1086,21 @@ void qdisc_reset(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_reset); -void qdisc_free(struct Qdisc *qdisc) +static void qdisc_free_cb(struct rcu_head *head) { - if (qdisc_is_percpu_stats(qdisc)) { - free_percpu(qdisc->cpu_bstats); - free_percpu(qdisc->cpu_qstats); + struct Qdisc *q = container_of(head, struct Qdisc, rcu); + + if (qdisc_is_percpu_stats(q)) { + free_percpu(q->cpu_bstats); + free_percpu(q->cpu_qstats); } - kfree(qdisc); + kfree(q); } -static void qdisc_free_cb(struct rcu_head *head) +void qdisc_free_rcu(struct Qdisc *qdisc) { - struct Qdisc *q = container_of(head, struct Qdisc, rcu); - - qdisc_free(q); + call_rcu(&qdisc->rcu, qdisc_free_cb); } static void __qdisc_destroy(struct Qdisc *qdisc) @@ -1127,7 +1127,7 @@ static void __qdisc_destroy(struct Qdisc *qdisc) trace_qdisc_destroy(qdisc); - call_rcu(&qdisc->rcu, qdisc_free_cb); + qdisc_free_rcu(qdisc); } void qdisc_destroy(struct Qdisc *qdisc) diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 96acab6a8da0..fc72f825fbd9 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -551,7 +551,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt, return err; if (tb[TCA_HHF_QUANTUM]) - new_quantum = nla_get_u32(tb[TCA_HHF_QUANTUM]); + new_quantum = max(256U, nla_get_u32(tb[TCA_HHF_QUANTUM])); if (tb[TCA_HHF_NON_HH_WEIGHT]) new_hhf_non_hh_weight = nla_get_u32(tb[TCA_HHF_NON_HH_WEIGHT]); @@ -613,7 +613,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt, int i; sch->limit = 1000; - q->quantum = psched_mtu(qdisc_dev(sch)); + q->quantum = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, 1 << 20); get_random_bytes(&q->perturbation, sizeof(q->perturbation)); INIT_LIST_HEAD(&q->new_buckets); INIT_LIST_HEAD(&q->old_buckets); @@ -624,10 +624,6 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt, q->hhf_evict_timeout = HZ; /* 1 sec */ q->hhf_non_hh_weight = 2; - if ((int)q->quantum <= 0 || - (u64)q->quantum * q->hhf_non_hh_weight > INT_MAX) - q->quantum = 256; - if (opt) { int err = hhf_change(sch, opt, extack); diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index b41f2def2e2c..3b7863ffd284 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -35,7 +35,7 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, { u64 rnd; u64 local_prob = vars->prob; - u32 mtu = psched_mtu(qdisc_dev(sch)); + u32 mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 1, 1 << 20); /* If there is still burst allowance left skip random early drop */ if (vars->burst_time > 0) diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 187d3ed578f2..8bbcfc9e85d9 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -660,6 +660,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt, return -EINVAL; } + if (ctl->quantum > 1 << 20) { + NL_SET_ERR_MSG_MOD(extack, "quantum too large"); + return -EINVAL; + } + if (ctl->perturb_period < 0 || ctl->perturb_period > INT_MAX / HZ) { NL_SET_ERR_MSG_MOD(extack, "invalid perturb period"); @@ -688,7 +693,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt, /* update and validate configuration */ if (ctl->quantum) - quantum = ctl->quantum; + quantum = max(256U, ctl->quantum); if (ctl->flows) maxflows = min_t(u32, ctl->flows, SFQ_MAX_FLOWS); if (ctl->divisor) { diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 5f988b3a8814..d666cec6b194 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -212,8 +212,10 @@ new_skb: chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length)); skb_pull(chunk->skb, sizeof(*ch)); chunk->subh.v = NULL; /* Subheader is no longer valid. */ - - if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) { + if (unlikely(ntohs(ch->length) < sizeof(*ch))) { + chunk->pdiscard = 1; + } else if (chunk->chunk_end + sizeof(*ch) <= + skb_tail_pointer(chunk->skb)) { /* This is not a singleton */ chunk->singleton = 0; } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 236e25abc7a4..84a4c97d0f75 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3215,6 +3215,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, *errp = param.p; switch (param.p->type) { case SCTP_PARAM_ERR_CAUSE: + if (length < sizeof(struct sctp_addip_param) + + sizeof(struct sctp_errhdr)) + return false; break; case SCTP_PARAM_IPV4_ADDRESS: if (length != sizeof(struct sctp_ipv4addr_param)) @@ -3448,20 +3451,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, case SCTP_PARAM_ERR_CAUSE: length = sizeof(*asconf_ack_param); err_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; - if (asconf_ack_len > 0) - return err_param->cause; - else - return SCTP_ERROR_INV_PARAM; - break; + return err_param->cause; default: return SCTP_ERROR_INV_PARAM; } } length = ntohs(asconf_ack_param->param_hdr.length); - asconf_ack_param = (void *)asconf_ack_param + length; - asconf_ack_len -= length; + asconf_ack_param = (void *)asconf_ack_param + SCTP_PAD4(length); + asconf_ack_len -= SCTP_PAD4(length); } return err_code; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 94716406d602..0d99b7e8c082 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type, timeout = asoc->timeouts[cmd->obj.to]; BUG_ON(!timeout); - /* - * SCTP has a hard time with timer starts. Because we process - * timer starts as side effects, it can be hard to tell if we - * have already started a timer or not, which leads to BUG - * halts when we call add_timer. So here, instead of just starting - * a timer, if the timer is already started, and just mod - * the timer with the shorter of the two expiration times - */ - if (!timer_pending(timer)) + if (!timer_reduce(timer, jiffies + timeout)) sctp_association_hold(asoc); - timer_reduce(timer, jiffies + timeout); break; case SCTP_CMD_TIMER_RESTART: diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index cf461ebcdde5..09a7c97e87da 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -282,9 +282,8 @@ int gss_krb5_mic_build_sg(const struct xdr_buf *body, unsigned int overflow_nents = nsg - XDR_BUF_TO_SG_NENTS + 1; - *sg_overflow = kmalloc_array(overflow_nents, - sizeof(**sg_overflow), - GFP_NOFS); + *sg_overflow = kmalloc_objs(**sg_overflow, overflow_nents, + GFP_NOFS); if (!*sg_overflow) return -ENOMEM; diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index aebd97e7f66c..31a1bc60a5f6 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -625,7 +625,7 @@ int sunrpc_nl_unix_gid_get_reqs_dumpit(struct sk_buff *skb, if (!cnt) return 0; - items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + items = kzalloc_objs(*items, cnt); seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); if (!items || !seqnos) { ret = -ENOMEM; @@ -1326,7 +1326,7 @@ int sunrpc_nl_ip_map_get_reqs_dumpit(struct sk_buff *skb, if (!cnt) return 0; - items = kcalloc(cnt, sizeof(*items), GFP_KERNEL); + items = kzalloc_objs(*items, cnt); seqnos = kcalloc(cnt, sizeof(*seqnos), GFP_KERNEL); if (!items || !seqnos) { ret = -ENOMEM; diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index cb2ef428651f..c42fd338c607 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -371,8 +371,7 @@ int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset, unsigned int overflow_nents = nsg - sg_head_nents + 1; struct scatterlist *overflow; - overflow = kmalloc_array(overflow_nents, sizeof(*overflow), - gfp); + overflow = kmalloc_objs(*overflow, overflow_nents, gfp); if (!overflow) return -ENOMEM; diff --git a/net/tipc/link.c b/net/tipc/link.c index 49dfc098d89b..6427c69f8929 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id, snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown", self_str, if_name, peer_str); - strcpy(l->if_name, if_name); + strscpy(l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; l->net = net; @@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id, snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name, peer_str); } else { - strcpy(l->name, tipc_bclink_name); + strscpy(l->name, tipc_bclink_name); } trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!"); tipc_link_reset(l); @@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe, msg_set_dest_session(hdr, l->peer_session); } msg_set_max_pkt(hdr, l->advertised_mtu); - strcpy(data, l->if_name); + memcpy(data, l->if_name, TIPC_MAX_IF_NAME); msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME); skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME); } diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 253c72d1366e..6fda36ab1766 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk, u32 key) { struct name_table *nt = tipc_name_table(net); + u32 max_user_pub = TIPC_MAX_PUBL - 1; struct tipc_net *tn = tipc_net(net); struct publication *p = NULL; struct sk_buff *skb = NULL; + bool protocol_type = false; u32 rc_dests; + if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE || + ua->sr.type == TIPC_TOP_SRV) + protocol_type = true; + spin_lock_bh(&tn->nametbl_lock); + if (protocol_type) + goto insert; - if (nt->local_publ_count >= TIPC_MAX_PUBL) { - pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL); + /* Reserve one entry for node state service type because it has cluster + * scope and it is distributed in bulk. So, the maximum number of user's + * publications is (TIPC_MAX_PUBL - 1). + */ + if (nt->local_publ_count >= max_user_pub) { + pr_warn("Bind failed, max limit %u reached\n", max_user_pub); goto exit; } +insert: p = tipc_nametbl_insert_publ(net, ua, sk, key); if (p) { - nt->local_publ_count++; + /* Not count node state, link state and topology server types + * so that maximum nt->local_publ_count does not prevent + * protocol service types from being inserted into the name + * table. + */ + if (!protocol_type) + nt->local_publ_count++; skb = tipc_named_publish(net, p); } rc_dests = nt->rc_dests; @@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, p = tipc_nametbl_remove_publ(net, ua, sk, key); if (p) { - nt->local_publ_count--; + if (p->sr.type != TIPC_NODE_STATE && + p->sr.type != TIPC_LINK_STATE && + p->sr.type != TIPC_TOP_SRV) + nt->local_publ_count--; skb = tipc_named_withdraw(net, p); list_del_init(&p->binding_sock); kfree_rcu(p, rcu); diff --git a/net/tipc/node.c b/net/tipc/node.c index 683a136e53ef..bd91378b7540 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n) pr_warn("Resetting all links to %x\n", n->addr); + tipc_node_write_lock(n); trace_tipc_node_reset_links(n, true, " "); + tipc_node_write_unlock_fast(n); for (i = 0; i < MAX_BEARERS; i++) { tipc_node_link_down(n, i, false); } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 13f9926bf205..42cffeafc8c1 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2812,8 +2812,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state) return 1; } -static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, - int flags, int copied) +static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last, + struct sock *sk, int flags, int copied) { struct sk_buff *read_skb = NULL, *unread_skb = NULL; struct unix_sock *u = unix_sk(sk); @@ -2827,11 +2827,13 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, if (copied && (!u->oob_skb || skb == u->oob_skb)) { skb = NULL; } else if (flags & MSG_PEEK) { + *last = skb; skb = skb_peek_next(skb, &sk->sk_receive_queue); } else { read_skb = skb; skb = skb_peek_next(skb, &sk->sk_receive_queue); __skb_unlink(read_skb, &sk->sk_receive_queue); + *last = skb; } if (!skb) @@ -2850,8 +2852,10 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, __skb_unlink(skb, &sk->sk_receive_queue); unread_skb = skb; skb = skb_peek(&sk->sk_receive_queue); + *last = skb; } } else if (!sock_flag(sk, SOCK_URGINLINE)) { + *last = skb; skb = skb_peek_next(skb, &sk->sk_receive_queue); } @@ -2971,8 +2975,8 @@ redo: again: #if IS_ENABLED(CONFIG_AF_UNIX_OOB) if (skb) { - skb = manage_oob(skb, sk, flags, copied); - if (!skb && copied) { + skb = manage_oob(skb, &last, sk, flags, copied); + if (!skb && (copied || !state->size)) { unix_state_unlock(sk); break; } @@ -3667,8 +3671,8 @@ static int bpf_iter_unix_realloc_batch(struct bpf_unix_iter_state *iter, { struct sock **new_batch; - new_batch = kvmalloc_array(new_batch_sz, sizeof(*new_batch), - GFP_USER | __GFP_NOWARN); + new_batch = kvmalloc_objs(*new_batch, new_batch_sz, + GFP_USER | __GFP_NOWARN); if (!new_batch) return -ENOMEM; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index a33b2a2d381d..f840498b58af 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -438,6 +438,38 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, } EXPORT_SYMBOL_GPL(vsock_find_connected_socket); +/** + * vsock_check_source - validate a packet source against a socket peer + * @vsk: socket receiving the packet + * @transport: transport receiving the packet + * @src: source address from the packet + * + * Return: true if the packet arrived on the socket's assigned transport and + * its source matches the stored peer. Loopback packets are generated + * internally and always use the local CID as their source, including + * connections using a valid CID alias. + * + * The caller must hold the socket lock and must not call this for listening + * sockets, which accept packets from any source and have no assigned + * transport. + */ +bool vsock_check_source(const struct vsock_sock *vsk, + const struct vsock_transport *transport, + const struct sockaddr_vm *src) +{ + if (vsk->transport != transport) + return false; + + if (src->svm_port != vsk->remote_addr.svm_port) + return false; + + if (src->svm_cid == vsk->remote_addr.svm_cid) + return true; + + return transport->get_local_cid() == VMADDR_CID_LOCAL; +} +EXPORT_SYMBOL_GPL(vsock_check_source); + void vsock_remove_sock(struct vsock_sock *vsk) { /* Transport reassignment must not remove the binding. */ diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 88df82364f77..f225f53ed4ba 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -1836,7 +1836,8 @@ void virtio_transport_recv_pkt(struct virtio_transport *t, * lock_sock (note: listener sockets are not assigned to any transport) */ if (sock_flag(sk, SOCK_DONE) || - (sk->sk_state != TCP_LISTEN && vsk->transport != &t->transport)) { + (sk->sk_state != TCP_LISTEN && + !vsock_check_source(vsk, &t->transport, &src))) { (void)virtio_transport_reset_no_sock(t, skb, net); release_sock(sk); sock_put(sk); diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 1c4ee039c166..1f186e8f8364 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -680,11 +680,13 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg) struct vmci_transport_packet *pkt; struct vsock_sock *vsk; bool bh_process_pkt; + bool drop_pkt; int err; sk = NULL; err = VMCI_SUCCESS; bh_process_pkt = false; + drop_pkt = false; /* Ignore incoming packets from resources that aren't vsock * implementations. @@ -765,17 +767,29 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg) bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { - /* The local context ID may be out of date, update it. */ - vsk->local_addr.svm_cid = dst.svm_cid; + if (sk->sk_state != TCP_LISTEN && + !vsock_check_source(vsk, &vmci_transport, &src)) { + drop_pkt = true; + err = VMCI_ERROR_NO_ACCESS; + } else { + /* The local context ID may be out of date, update it. */ + vsk->local_addr.svm_cid = dst.svm_cid; - if (sk->sk_state == TCP_ESTABLISHED) - vmci_trans(vsk)->notify_ops->handle_notify_pkt( - sk, pkt, true, &dst, &src, - &bh_process_pkt); + if (sk->sk_state == TCP_ESTABLISHED) + vmci_trans(vsk)->notify_ops->handle_notify_pkt(sk, pkt, true, + &dst, &src, + &bh_process_pkt); + } } bh_unlock_sock(sk); + if (drop_pkt) { + if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0) + pr_err("unable to send reset\n"); + goto out; + } + if (!bh_process_pkt) { struct vmci_transport_recv_pkt_info *recv_pkt_info; @@ -900,6 +914,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work) { struct vmci_transport_recv_pkt_info *recv_pkt_info; struct vmci_transport_packet *pkt; + struct sockaddr_vm src; struct sock *sk; recv_pkt_info = @@ -908,6 +923,12 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work) pkt = &recv_pkt_info->pkt; lock_sock(sk); + vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port); + if (sk->sk_state != TCP_LISTEN && + !vsock_check_source(vsock_sk(sk), &vmci_transport, &src)) { + vmci_transport_reply_reset(pkt); + goto out; + } /* The local context ID may be out of date. */ vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context; @@ -937,6 +958,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work) break; } +out: release_sock(sk); kfree(recv_pkt_info); /* Release reference obtained in the stream callback when we fetched diff --git a/net/wireless/core.c b/net/wireless/core.c index d13310fef691..3032993ba5dc 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -311,9 +311,8 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev, if (!sched->n_channels) return 0; - wdev->u.nan.chandefs = kcalloc(sched->n_channels, - sizeof(*wdev->u.nan.chandefs), - GFP_KERNEL); + wdev->u.nan.chandefs = kzalloc_objs(*wdev->u.nan.chandefs, + sched->n_channels); if (!wdev->u.nan.chandefs) return -ENOMEM; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 44f2bad08670..899b6374c550 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -17330,8 +17330,7 @@ static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev, u8 n_rx_nss; int ret; - channel_parsed = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*channel_parsed), - GFP_KERNEL); + channel_parsed = kzalloc_objs(*channel_parsed, NL80211_ATTR_MAX + 1); if (!channel_parsed) return -ENOMEM; @@ -17554,8 +17553,7 @@ static int nl80211_nan_set_peer_sched(struct sk_buff *skb, } if (n_channels) { - nan_channels = kcalloc(n_channels, sizeof(*nan_channels), - GFP_KERNEL); + nan_channels = kzalloc_objs(*nan_channels, n_channels); if (!nan_channels) return -ENOMEM; } @@ -17693,8 +17691,7 @@ static int nl80211_nan_set_local_sched(struct sk_buff *skb, info->nlhdr, GENL_HDRLEN, rem) n_channels++; - sched = kzalloc(struct_size(sched, nan_channels, n_channels), - GFP_KERNEL); + sched = kzalloc_flex(*sched, nan_channels, n_channels); if (!sched) return -ENOMEM; diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 812f8e5a08d5..439ab88a5da1 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -22,11 +22,13 @@ #![feature(cfi_encoding)] #[allow(dead_code)] +#[allow(clippy::as_underscore)] #[allow(clippy::cast_lossless)] #[allow(clippy::ptr_as_ptr)] #[allow(clippy::ref_as_ptr)] #[allow(clippy::undocumented_unsafe_blocks)] -#[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#[cfg_attr(not(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES), allow(unknown_lints))] +#[allow(unnecessary_transmutes)] #[cfg_attr( CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, allow(suspicious_runtime_symbol_definitions) diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs index dbe848e30efe..de589792a77a 100644 --- a/rust/kernel/num.rs +++ b/rust/kernel/num.rs @@ -15,9 +15,14 @@ pub enum Unsigned {} /// Designates signed primitive types. pub enum Signed {} +mod private { + pub trait Sealed {} +} + /// Describes core properties of integer types. pub trait Integer: - Sized + private::Sealed + + Sized + Copy + Clone + PartialEq @@ -56,6 +61,8 @@ pub trait Integer: macro_rules! impl_integer { ($($type:ty: $signedness:ty), *) => { $( + impl private::Sealed for $type {} + impl Integer for $type { type Signedness = $signedness; diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs index 6741046ec1c0..22e2cdf82a21 100644 --- a/rust/kernel/pci/irq.rs +++ b/rust/kernel/pci/irq.rs @@ -151,8 +151,10 @@ impl<'a> IrqVectorRegistration<'a> { /// [`Self::len()`]. #[inline] pub fn index(&self, index: usize) -> Result<IrqVector<'_>> { + let index = u32::try_from(index)?; + // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`. - let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index as u32) }; + let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index) }; if irq < 0 { return Err(Error::from_errno(irq)); } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 7600cdbbbf98..f1463be9479d 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -490,13 +490,7 @@ macro_rules! stack_pin_init { (let $var:ident $(: $t:ty)? = $val:expr) => { let val = $val; let mut $var = ::core::pin::pin!($crate::__internal::StackInit$(::<$t>)?::uninit()); - let mut $var = match $crate::__internal::StackInit::init($var, val) { - Ok(res) => res, - Err(x) => { - let x: ::core::convert::Infallible = x; - match x {} - } - }; + let Ok(mut $var) = $crate::__internal::StackInit::init($var, val); }; } diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs index 797ead5b5626..003e6d4f7c4b 100644 --- a/rust/uapi/lib.rs +++ b/rust/uapi/lib.rs @@ -10,6 +10,7 @@ #![no_std] #![allow( clippy::all, + clippy::as_underscore, clippy::cast_lossless, clippy::ptr_as_ptr, clippy::ref_as_ptr, @@ -23,7 +24,8 @@ unreachable_pub, unsafe_op_in_unsafe_fn )] -#![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] +#![cfg_attr(not(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES), allow(unknown_lints))] +#![allow(unnecessary_transmutes)] #![cfg_attr( CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, allow(suspicious_runtime_symbol_definitions) diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs index 682207c81fc2..01729e87d6b5 100644 --- a/samples/rust/rust_print_main.rs +++ b/samples/rust/rust_print_main.rs @@ -23,10 +23,10 @@ fn arc_print() -> Result { let b = UniqueArc::new("hello, world", GFP_KERNEL)?; // Prints the value of data in `a`. - pr_info!("{}", a); + pr_info!("{}\n", a); // Uses ":?" to print debug fmt of `b`. - pr_info!("{:?}", b); + pr_info!("{:?}\n", b); let a: Arc<&str> = b.into(); let c = a.clone(); @@ -42,7 +42,7 @@ fn arc_print() -> Result { use kernel::fmt::Display; fn arc_dyn_print(arc: &Arc<dyn Display>) { - pr_info!("Arc<dyn Display> says {arc}"); + pr_info!("Arc<dyn Display> says {arc}\n"); } let a_i32_display: Arc<dyn Display> = Arc::new(42i32, GFP_KERNEL)?; @@ -53,7 +53,7 @@ fn arc_print() -> Result { } // Pretty-prints the debug formatting with lower-case hexadecimal integers. - pr_info!("{:#x?}", a); + pr_info!("{:#x?}\n", a); Ok(()) } diff --git a/scripts/Makefile.asm-headers b/scripts/Makefile.asm-headers index b38931314ad7..f1c3d287c14b 100644 --- a/scripts/Makefile.asm-headers +++ b/scripts/Makefile.asm-headers @@ -48,7 +48,7 @@ syscall-y := $(addprefix $(obj)/, $(syscall-y)) generated-y := $(addprefix $(obj)/, $(generated-y)) # Remove stale wrappers when the corresponding files are removed from generic-y -old-headers := $(shell test -d $(obj) && find $(obj) -name *.h) +old-headers := $(shell test -d $(obj) && find $(obj) -name '*.h' ! -name '.*') unwanted := $(filter-out $(generic-y) $(generated-y) $(syscall-y),$(old-headers)) filechk_wrap = echo "\#include <asm-generic/$*.h>" diff --git a/scripts/livepatch/init.c b/scripts/livepatch/init.c index f14d8c8fb35f..16aff8f736eb 100644 --- a/scripts/livepatch/init.c +++ b/scripts/livepatch/init.c @@ -51,7 +51,7 @@ static int __init livepatch_mod_init(void) if (!funcs) { ret = -ENOMEM; for (int j = 0; j < i; j++) - kfree(objs[i].funcs); + kfree(objs[j].funcs); goto err_free_objs; } diff --git a/scripts/mksysmap b/scripts/mksysmap index c4531eacde20..856b26ba2ac0 100755 --- a/scripts/mksysmap +++ b/scripts/mksysmap @@ -35,7 +35,7 @@ / __efistub_/d # arm64 local symbols in PIE namespace -/ __pi_\\$/d +/ __pi_\$/d / __pi_\.L/d # arm64 local symbols in non-VHE KVM namespace @@ -83,7 +83,7 @@ / _SDA2_BASE_$/d # MODULE_INFO() -/ __UNIQUE_ID_modinfo[0-9]*$/d +/ __UNIQUE_ID_modinfo_[0-9]*$/d # --------------------------------------------------------------------------- # Ignored patterns diff --git a/scripts/sorttable.c b/scripts/sorttable.c index d8dc2a1b7c31..d7b50581c732 100644 --- a/scripts/sorttable.c +++ b/scripts/sorttable.c @@ -116,7 +116,7 @@ static inline void *get_index(void *start, int entsize, int index) } static int extable_ent_size; -static int long_size; +static int long_size __maybe_unused; #define ERRSTR_MAXSZ 256 diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index 6923036e1a2f..81c81ad983ad 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -278,7 +278,7 @@ config CC_HAS_RANDSTRUCT choice prompt "Randomize layout of sensitive kernel structures" - default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT) + default RANDSTRUCT_FULL if !(RUST_IS_AVAILABLE && HAVE_RUST) && COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT) default RANDSTRUCT_NONE help If you enable this, the layouts of structures that are entirely diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b3a9f86809b0..72654cf797cd 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -46,12 +46,16 @@ config IMA_KEXEC config IMA_MEASURE_PCR_IDX int + prompt "PCR Index for Aggregate" if EXPERT range 8 14 default 10 help IMA_MEASURE_PCR_IDX determines the TPM PCR register index that IMA uses to maintain the integrity aggregate of the - measurement list. If unsure, use the default 10. + measurement list. Most attestation tooling expects PCR 10. + + The default is almost always what you want. Only change this + if you know what you are doing. config IMA_LSM_RULES bool diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index ced2e131b061..b280488e15fc 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -748,6 +748,8 @@ static int validate_hash_algo(struct dentry *dentry, return -EACCES; path = dentry_path(dentry, pathbuf, PATH_MAX); + if (IS_ERR(path)) + path = NULL; integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path, "set_data", errmsg, -EACCES, 0); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f79d07bb63c6..68d9a5e6c232 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -165,7 +165,10 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, - {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC} + {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, + .flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE, .fsmagic = CONFIGFS_MAGIC, + .flags = IMA_FSMAGIC} }; static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { @@ -211,6 +214,8 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { {.action = DONT_APPRAISE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE, .fsmagic = CONFIGFS_MAGIC, + .flags = IMA_FSMAGIC}, #ifdef CONFIG_IMA_WRITE_POLICY {.action = APPRAISE, .func = POLICY_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index f89f0ca3d4ed..0f1b7e4113c4 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -75,8 +75,7 @@ static struct hlist_head *ima_alloc_replace_htable(void) struct hlist_head *old_htable, *new_htable; /* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */ - new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head), - GFP_KERNEL); + new_htable = kzalloc_objs(struct hlist_head, IMA_MEASURE_HTABLE_SIZE); if (!new_htable) return ERR_PTR(-ENOMEM); diff --git a/security/landlock/.kunitconfig b/security/landlock/.kunitconfig index f9423f01ac5b..fe36228d37ea 100644 --- a/security/landlock/.kunitconfig +++ b/security/landlock/.kunitconfig @@ -1,6 +1,8 @@ CONFIG_AUDIT=y +CONFIG_FTRACE=y CONFIG_KUNIT=y CONFIG_NET=y +CONFIG_SCHED_TRACER=y CONFIG_SECURITY=y CONFIG_SECURITY_LANDLOCK=y CONFIG_SECURITY_LANDLOCK_KUNIT_TEST=y diff --git a/security/landlock/domain.c b/security/landlock/domain.c index 93c7104fd6b2..4031b581be07 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -439,10 +439,11 @@ landlock_merge_ruleset(struct landlock_domain *const parent, int err; might_sleep(); - lockdep_assert_held(&ruleset->lock); if (WARN_ON_ONCE(!ruleset)) return ERR_PTR(-EINVAL); + lockdep_assert_held(&ruleset->lock); + if (parent) { if (parent->num_layers >= LANDLOCK_MAX_NUM_LAYERS) return ERR_PTR(-E2BIG); diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 30aa6ce13590..330a1871bf94 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1298,11 +1298,12 @@ static int current_check_refer_path(struct dentry *const old_dentry, /* * old_dentry may be the root of the common mount point and * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and - * OPEN_TREE_CLONE). We do not need to call dget(old_parent) because - * we keep a reference to old_dentry. + * OPEN_TREE_CLONE). Pin the dentry used as old_parent in either case. + * Otherwise, dget_parent() safely fetches and pins the current parent + * against a concurrent rename(2). */ - old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry : - old_dentry->d_parent; + old_parent = (old_dentry == mnt_dir.dentry) ? dget(old_dentry) : + dget_parent(old_dentry); /* new_dir->dentry is equal to new_dentry->d_parent */ allow_parent1 = collect_domain_accesses(subject->domain, mnt_dir.dentry, @@ -1311,8 +1312,10 @@ static int current_check_refer_path(struct dentry *const old_dentry, allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); - if (allow_parent1 && allow_parent2) + if (allow_parent1 && allow_parent2) { + dput(old_parent); return 0; + } /* * To be able to compare source and destination domain access rights, @@ -1324,8 +1327,10 @@ static int current_check_refer_path(struct dentry *const old_dentry, subject->domain, &mnt_dir, access_request_parent1, &layer_masks_parent1, &request1, old_dentry, access_request_parent2, &layer_masks_parent2, &request2, - exchange ? new_dentry : NULL)) + exchange ? new_dentry : NULL)) { + dput(old_parent); return 0; + } if (request1.access) { request1.audit.u.path.dentry = old_parent; @@ -1335,6 +1340,7 @@ static int current_check_refer_path(struct dentry *const old_dentry, request2.audit.u.path.dentry = new_dir->dentry; landlock_log_denial(subject, &request2); } + dput(old_parent); /* * This prioritizes EACCES over EXDEV for all actions, including diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 0d07707523cd..a5d135d085cb 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -58,7 +58,7 @@ landlock_create_ruleset(const access_mask_t fs_access_mask, new_ruleset->id = landlock_get_id_range(1); #endif /* CONFIG_TRACEPOINTS */ - /* Should already be checked in landlock_create_ruleset(). */ + /* The caller must only pass supported access rights and scopes. */ if (fs_access_mask) { const access_mask_t mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS; diff --git a/security/landlock/trace.c b/security/landlock/trace.c index 2ea7aac8d75d..8c21e5de6f0d 100644 --- a/security/landlock/trace.c +++ b/security/landlock/trace.c @@ -6,6 +6,7 @@ * Copyright © 2026 Cloudflare, Inc. */ +#include <kunit/test.h> #include <linux/cleanup.h> #include <linux/dcache.h> #include <linux/err.h> @@ -183,3 +184,184 @@ void landlock_trace_denial( break; } } + +#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST + +static void test_trace_seq_init(struct trace_seq *const seq, const size_t size) +{ + memset(seq, 0, sizeof(*seq)); + seq_buf_init(&seq->seq, seq->buffer, size); +} + +static void test_untrusted_str_data(struct kunit *const test) +{ + const char binary[] = { 'a', '\0', '<' }; + static const char ellipsis[] = "\xe2\x80\xa6"; + struct trace_seq *const seq = + kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); + const char *output; + + KUNIT_ASSERT_NOT_NULL(test, seq); + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, "<too_long>", 10); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, "<too_long>"); + + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, binary, sizeof(binary)); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, "a\\000<"); + + /* Input ellipsis bytes are escaped and cannot mimic the raw marker. */ + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, ellipsis, + sizeof(ellipsis) - 1); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, "\\342\\200\\246"); +} + +static void test_untrusted_str_boundaries(struct kunit *const test) +{ + static const char escaped_space[] = "\\040"; + const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE; + const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1; + const size_t escape_len = sizeof(escaped_space) - 1; + const size_t exact_prefix_len = + output_size - marker_len - 1 - escape_len; + const size_t short_prefix_len = exact_prefix_len + 1; + struct trace_seq *const seq = + kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); + char *const input = kunit_kmalloc(test, output_size + 1, GFP_KERNEL); + char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL); + const char *output; + + KUNIT_ASSERT_NOT_NULL(test, seq); + KUNIT_ASSERT_NOT_NULL(test, input); + KUNIT_ASSERT_NOT_NULL(test, expected); + + /* The escaped string and its trailing NUL exactly fit the limit. */ + memset(input, 'a', output_size - 1); + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, input, output_size - 1); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_EQ(test, seq->seq.len, output_size); + KUNIT_EXPECT_EQ(test, memcmp(output, input, output_size - 1), 0); + + /* Stop before a four-byte escape when only three bytes remain. */ + memset(input, 'a', short_prefix_len); + input[short_prefix_len] = ' '; + memset(input + short_prefix_len + 1, 'b', 5); + memset(expected, 'a', short_prefix_len); + memcpy(expected + short_prefix_len, TRACE_TRUNCATION_MARKER, + marker_len + 1); + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, input, short_prefix_len + 6); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, expected); + + /* Include a four-byte escape that exactly fills the prefix capacity. */ + memset(input, 'a', exact_prefix_len); + input[exact_prefix_len] = ' '; + memset(input + exact_prefix_len + 1, 'b', marker_len + 1); + memset(expected, 'a', exact_prefix_len); + memcpy(expected + exact_prefix_len, escaped_space, escape_len); + memcpy(expected + exact_prefix_len + escape_len, + TRACE_TRUNCATION_MARKER, marker_len + 1); + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, input, + exact_prefix_len + marker_len + 2); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, expected); + + /* Literal backslashes remain escaped in complete output. */ + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + output = __trace_print_untrusted_str(seq, "/\\000", 5); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, "/\\\\000"); +} + +static void test_untrusted_str_cursor(struct kunit *const test) +{ + const size_t padding_len = + TRACE_SEQ_BUFFER_SIZE - TRACE_UNTRUSTED_STR_OUTPUT_SIZE + 1; + struct trace_seq *const seq = + kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); + char *const padding = kunit_kzalloc(test, padding_len, GFP_KERNEL); + const char *output; + + KUNIT_ASSERT_NOT_NULL(test, seq); + KUNIT_ASSERT_NOT_NULL(test, padding); + + /* Accept available space exactly equal to the fixed reservation. */ + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + trace_seq_putmem(seq, padding, padding_len - 1); + output = __trace_print_untrusted_str(seq, "/a", 2); + KUNIT_ASSERT_NOT_NULL(test, output); + KUNIT_EXPECT_STREQ(test, output, "/a"); + KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len - 1 + sizeof("/a")); + + /* Reject one byte less without changing the scratch cursor. */ + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + trace_seq_putmem(seq, padding, padding_len); + output = __trace_print_untrusted_str(seq, "/a", 2); + KUNIT_EXPECT_NULL(test, output); + KUNIT_EXPECT_EQ(test, seq->seq.len, padding_len); +} + +static void test_untrusted_str_composition(struct kunit *const test) +{ + static const struct trace_print_flags flags[] = { + { .mask = 1, .name = "read" }, + }; + const size_t output_size = TRACE_UNTRUSTED_STR_OUTPUT_SIZE; + const size_t prefix_len = output_size - sizeof(TRACE_TRUNCATION_MARKER); + struct trace_seq *const seq = + kunit_kzalloc(test, sizeof(*seq), GFP_KERNEL); + char *const expected = kunit_kmalloc(test, output_size, GFP_KERNEL); + char *const path = kunit_kmalloc(test, output_size, GFP_KERNEL); + const char *flags_output, *path_output; + + KUNIT_ASSERT_NOT_NULL(test, seq); + KUNIT_ASSERT_NOT_NULL(test, expected); + KUNIT_ASSERT_NOT_NULL(test, path); + memset(path, 'a', output_size); + memset(expected, 'a', prefix_len); + memcpy(expected + prefix_len, TRACE_TRUNCATION_MARKER, + sizeof(TRACE_TRUNCATION_MARKER)); + + /* Exercise both legal TP_printk() sibling evaluation orders. */ + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + path_output = __trace_print_untrusted_str(seq, path, output_size); + flags_output = + trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags)); + KUNIT_ASSERT_NOT_NULL(test, path_output); + KUNIT_EXPECT_STREQ(test, path_output, expected); + KUNIT_EXPECT_STREQ(test, flags_output, "read"); + + test_trace_seq_init(seq, TRACE_SEQ_BUFFER_SIZE); + flags_output = + trace_print_flags_seq(seq, "|", 1, flags, ARRAY_SIZE(flags)); + path_output = __trace_print_untrusted_str(seq, path, output_size); + KUNIT_ASSERT_NOT_NULL(test, path_output); + KUNIT_EXPECT_STREQ(test, path_output, expected); + KUNIT_EXPECT_STREQ(test, flags_output, "read"); +} + +static struct kunit_case test_cases[] = { + /* clang-format off */ + KUNIT_CASE(test_untrusted_str_data), + KUNIT_CASE(test_untrusted_str_boundaries), + KUNIT_CASE(test_untrusted_str_cursor), + KUNIT_CASE(test_untrusted_str_composition), + {} + /* clang-format on */ +}; + +static struct kunit_suite test_suite = { + .name = "landlock_trace", + .test_cases = test_cases, +}; + +kunit_test_suite(test_suite); + +#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 035aaf113d1d..e5e17f100aae 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7267,24 +7267,6 @@ static int selinux_bpf_prog(struct bpf_prog *prog) BPF__PROG_RUN, NULL); } -static u32 selinux_bpffs_creator_sid(u32 fd) -{ - struct path path; - struct super_block *sb; - struct superblock_security_struct *sbsec; - - CLASS(fd, f)(fd); - - if (fd_empty(f)) - return SECSID_NULL; - - path = fd_file(f)->f_path; - sb = path.dentry->d_sb; - sbsec = selinux_superblock(sb); - - return sbsec->creator_sid; -} - static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr, struct bpf_token *token, bool kernel) { @@ -7297,7 +7279,7 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr, if (!token) ssid = bpfsec->sid; else - ssid = selinux_bpffs_creator_sid(attr->map_token_fd); + ssid = selinux_bpf_token_security(token)->grantor_sid; return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL); @@ -7315,7 +7297,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr, if (!token) ssid = bpfsec->sid; else - ssid = selinux_bpffs_creator_sid(attr->prog_token_fd); + ssid = selinux_bpf_token_security(token)->grantor_sid; return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL); @@ -7329,12 +7311,14 @@ static int selinux_bpf_token_create(struct bpf_token *token, const struct path *path) { struct bpf_security_struct *bpfsec; - u32 sid = selinux_bpffs_creator_sid(attr->token_create.bpffs_fd); + struct superblock_security_struct *sbsec; int err; + sbsec = selinux_superblock(path->dentry->d_sb); + bpfsec = selinux_bpf_token_security(token); bpfsec->sid = current_sid(); - bpfsec->grantor_sid = sid; + bpfsec->grantor_sid = sbsec->creator_sid; bpfsec->perms = 0; /** @@ -7343,15 +7327,15 @@ static int selinux_bpf_token_create(struct bpf_token *token, * in the allowed_cmds bitmap. */ if (bpf_token_cmd(token, BPF_MAP_CREATE)) { - err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, - BPF__MAP_CREATE_AS, NULL); + err = avc_has_perm(bpfsec->sid, bpfsec->grantor_sid, + SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL); if (err) return err; bpfsec->perms |= BPF__MAP_CREATE; } if (bpf_token_cmd(token, BPF_PROG_LOAD)) { - err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, - BPF__PROG_LOAD_AS, NULL); + err = avc_has_perm(bpfsec->sid, bpfsec->grantor_sid, + SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL); if (err) return err; bpfsec->perms |= BPF__PROG_LOAD; diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 23d62fede06e..7c397b1c9231 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -107,7 +107,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) return -EINVAL; } - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) { snd_card_unref(compr->card); return -ENOMEM; @@ -119,7 +119,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) data->stream.direction = dirn; data->stream.private_data = compr->private_data; data->stream.device = compr; - runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + runtime = kzalloc_obj(*runtime); if (!runtime) { kfree(data); snd_card_unref(compr->card); diff --git a/sound/core/control.c b/sound/core/control.c index 78ce7bc936d2..4199342d4ffe 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -69,7 +69,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) err = -ENODEV; goto __error2; } - ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + ctl = kzalloc_obj(*ctl); if (ctl == NULL) { err = -ENOMEM; goto __error; @@ -174,7 +174,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, goto _found; } } - ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + ev = kzalloc_obj(*ev, GFP_ATOMIC); if (ev) { ev->id = *id; ev->mask = mask; @@ -871,7 +871,7 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, unsigned int cmd, void __user *arg) { struct snd_ctl_card_info *info __free(kfree) = - kzalloc(sizeof(*info), GFP_KERNEL); + kzalloc_obj(*info); ssize_t n; if (! info) diff --git a/sound/core/control_led.c b/sound/core/control_led.c index 3d13bbec1c54..ec0e6c7ad657 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -158,7 +158,7 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access, UPDATE_ROUTE(route, snd_ctl_led_get(lctl)); } if (!found && kctl && card) { - lctl = kzalloc(sizeof(*lctl), GFP_KERNEL); + lctl = kzalloc_obj(*lctl); if (lctl) { lctl->card = card; lctl->access = access; diff --git a/sound/core/init.c b/sound/core/init.c index 2f7f83a7611b..1bcb6a2e7550 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -310,7 +310,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent, kfree(card); /* manually free here, as no destructor called */ return err; } - card->dev = parent; + card->dev = get_device(parent); card->number = idx; WARN_ON(IS_MODULE(CONFIG_SND) && !module); card->module = module; @@ -364,7 +364,7 @@ static int snd_card_init(struct snd_card *card, struct device *parent, sound_debugfs_root); #endif #ifdef CONFIG_SND_CTL_DEBUG - card->value_buf = kmalloc(sizeof(*card->value_buf), GFP_KERNEL); + card->value_buf = kmalloc_obj(*card->value_buf); if (!card->value_buf) return -ENOMEM; #endif @@ -603,6 +603,7 @@ static int snd_card_do_free(struct snd_card *card) dev_warn(card->dev, "unable to free card info\n"); /* Not fatal error */ } + put_device(card->dev); if (card->release_completion) complete(card->release_completion); if (!managed) diff --git a/sound/core/misc.c b/sound/core/misc.c index 4772b2a3b808..066fb9ecdcdc 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -125,7 +125,7 @@ int snd_fasync_helper(int fd, struct file *file, int on, struct snd_fasync *fasync = NULL; if (on) { - fasync = kzalloc(sizeof(*fasync), GFP_KERNEL); + fasync = kzalloc_obj(*fasync); if (!fasync) return -ENOMEM; INIT_LIST_HEAD(&fasync->list); diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index ff9d7fd60a7e..c533d767c29a 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -890,7 +890,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl int err; struct snd_ctl_elem_info *info __free(kfree) = - kmalloc(sizeof(*info), GFP_KERNEL); + kmalloc_obj(*info); if (!info) return -ENOMEM; scoped_guard(rwsem_read, &card->controls_rwsem) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4a5057e7629d..6d32c12fb79b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1468,6 +1468,8 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->state != SNDRV_PCM_STATE_PREPARED) return -EBADFD; + if (atomic_read(&runtime->buffer_accessing) < 0) + return -EBADFD; /* during hw_params, hw_free or prepare */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !snd_pcm_playback_data(substream)) return -EPIPE; @@ -2328,7 +2330,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) return -EINVAL; struct snd_pcm_group *group __free(kfree) = - kzalloc(sizeof(*group), GFP_KERNEL); + kzalloc_obj(*group); if (!group) return -ENOMEM; snd_pcm_group_init(group); @@ -4021,20 +4023,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, return -EINVAL; } runtime = substream->runtime; - if (runtime->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) - return -ENXIO; + /* don't race with buffer reallocation in hw_params/hw_free */ + if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) + return -EBUSY; + if (runtime->state == SNDRV_PCM_STATE_OPEN) { + err = -EBADFD; + goto out; + } + if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { + err = -ENXIO; + goto out; + } if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; + runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { + err = -EINVAL; + goto out; + } size = area->vm_end - area->vm_start; offset = area->vm_pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); - if ((size_t)size > dma_bytes) - return -EINVAL; - if (offset > dma_bytes - size) - return -EINVAL; + if ((size_t)size > dma_bytes) { + err = -EINVAL; + goto out; + } + if (offset > dma_bytes - size) { + err = -EINVAL; + goto out; + } area->vm_ops = &snd_pcm_vm_ops_data; area->vm_private_data = substream; @@ -4044,6 +4059,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, err = snd_pcm_lib_default_mmap(substream, area); if (!err) atomic_inc(&substream->mmap_count); +out: + atomic_dec(&runtime->buffer_accessing); return err; } EXPORT_SYMBOL(snd_pcm_mmap_data); diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index ab0e5bd70f8f..18bedd66435d 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -111,12 +111,15 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream) snd_pcm_direction_name(substream->stream), tid.card, tid.device, tid.subdevice); timer->hw = snd_pcm_timer; + /* Set before registering: a concurrent reader can invoke our hw + * callbacks as soon as the timer is on the global list. + */ + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free; if (snd_device_register(timer->card, timer) < 0) { snd_device_free(timer->card, timer); return; } - timer->private_data = substream; - timer->private_free = snd_pcm_timer_free; substream->timer = timer; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 34b4c7d6dbe6..2617bb5b4faf 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -785,7 +785,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, substream->framing = framing; substream->clock_type = clock_type; } - return 0; + return err; } EXPORT_SYMBOL(snd_rawmidi_input_params); diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index c4b82e29ab05..21a0a98c8e92 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -86,7 +86,7 @@ snd_seq_oss_synth_probe(struct snd_seq_device *dev) struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); - rec = kzalloc(sizeof(*rec), GFP_KERNEL); + rec = kzalloc_obj(*rec); if (!rec) return -ENOMEM; rec->seq_device = -1; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 5b86e75c2658..239809ce48d7 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -210,7 +210,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) struct snd_seq_client *client; /* init client data */ - client = kzalloc(sizeof(*client), GFP_KERNEL); + client = kzalloc_obj(*client); if (client == NULL) return NULL; client->pool = snd_seq_pool_new(poolsize); diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 6208bf7f57bf..a01785a6de9b 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -188,7 +188,7 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) struct snd_rawmidi_runtime *runtime = substream->runtime; struct snd_virmidi *vmidi; - vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL); + vmidi = kzalloc_obj(*vmidi); if (vmidi == NULL) return -ENOMEM; vmidi->substream = substream; diff --git a/sound/core/timer.c b/sound/core/timer.c index f666f05e9d45..679b26435670 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1855,7 +1855,7 @@ static int snd_timer_user_info(struct file *file, return -EBADFD; struct snd_timer_info *info __free(kfree) = - kzalloc(sizeof(*info), GFP_KERNEL); + kzalloc_obj(*info); if (! info) return -ENOMEM; info->card = t->card ? t->card->number : -1; diff --git a/sound/core/ump.c b/sound/core/ump.c index d183c8a000bd..3d1a2ed3b476 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1335,6 +1335,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) { struct snd_rawmidi *rmidi = ump->legacy_rmidi; + if (!rmidi) + return; update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); } @@ -1343,6 +1345,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) { struct snd_rawmidi *rmidi = ump->legacy_rmidi; + if (!rmidi) + return; snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", ump->core.name); } diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 4e3ea23ca913..81cb1f59f703 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1365,7 +1365,7 @@ static int loopback_open(struct snd_pcm_substream *substream) int dev = get_cable_index(substream); guard(mutex)(&loopback->cable_lock); - dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + dpcm = kzalloc_obj(*dpcm); if (!dpcm) return -ENOMEM; dpcm->loopback = loopback; @@ -1373,7 +1373,7 @@ static int loopback_open(struct snd_pcm_substream *substream) cable = loopback->cables[substream->number][dev]; if (!cable) { - cable = kzalloc(sizeof(*cable), GFP_KERNEL); + cable = kzalloc_obj(*cable); if (!cable) { err = -ENOMEM; goto unlock; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index b908d2564aee..3f6bfee29986 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -808,7 +808,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; guard(spinlock_irq)(&dummy->mixer_lock); - change = dummy->capture_source[addr][0] != left && + change = dummy->capture_source[addr][0] != left || dummy->capture_source[addr][1] != right; dummy->capture_source[addr][0] = left; dummy->capture_source[addr][1] = right; diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c index 85c2ecf46d38..6cba01228c27 100644 --- a/sound/hda/codecs/cirrus/cs420x.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -571,6 +571,7 @@ static const struct hda_model_fixup cs4208_models[] = { static const struct hda_quirk cs4208_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO), + SND_PCI_QUIRK(0x8086, 0x7270, "MacBookAir 7,2", CS4208_MAC_AUTO), {} /* terminator */ }; @@ -583,6 +584,7 @@ static const struct hda_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x7800, "MacPro 6,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), SND_PCI_QUIRK(0x106b, 0x7f00, "iMac 16,1", CS4208_MBP11), + SND_PCI_QUIRK(0x8086, 0x7270, "MacBookAir 7,2", CS4208_MBA6), {} /* terminator */ }; diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 7357dc91ac49..9bfdbf5c9032 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -249,6 +249,25 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ } } +#define SN6140_S3_AFG_D0_DELAY_MS 1000 + +static void cx_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + + /* + * SN6140 may not respond to AFG D0 immediately after S3. + * Wait before the D0 verb so the power-state command itself succeeds. + */ + if (codec->core.vendor_id == 0x14f11f87 && + power_state == AC_PWRST_D0 && + codec->core.dev.power.power_state.event == PM_EVENT_RESUME) + msleep(SN6140_S3_AFG_D0_DELAY_MS); + + snd_hda_codec_set_power_to_all(codec, fg, power_state); +} + static int cx_suspend(struct hda_codec *codec) { cx_auto_shutdown(codec); @@ -1308,6 +1327,7 @@ static const struct hda_codec_ops cx_codec_ops = { .init = cx_init, .unsol_event = snd_hda_jack_unsol_event, .suspend = cx_suspend, + .set_power_state = cx_set_power_state, .check_power_status = snd_hda_gen_check_power_status, .stream_pm = snd_hda_gen_stream_pm, }; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index e4349743a251..5127a111c59c 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -2379,6 +2379,33 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, } } +/* + * On the Acer Aspire A515-57G (and possibly other models sharing this + * board), if headphones are already inserted into the combo jack before + * the codec powers up (cold boot), the impedance-based headset-type + * sensing races and misclassifies the jack, driving the wrong output + * configuration (audible as missing center-panned/vocal content). A + * genuine physical unplug/replug after boot fixes it by forcing a fresh + * sense transient. Mirror that here on cold boot only: give the sense + * hardware time to settle, then force a fresh classification. + */ +static void alc_fixup_headset_mode_acer_coldboot(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + alc_fixup_headset_mode(codec, fix, action); + + if (action == HDA_FIXUP_ACT_INIT && + !is_s3_resume(codec) && !is_s4_resume(codec) && + spec->current_headset_mode != ALC_HEADSET_MODE_UNPLUGGED) { + msleep(500); + spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; + spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; + alc_fixup_headset_mode(codec, fix, action); + } +} + static void alc288_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -4248,6 +4275,7 @@ enum { ALC282_FIXUP_ACER_DISABLE_LINEOUT, ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST, ALC256_FIXUP_ACER_HEADSET_MIC, + ALC256_FIXUP_ACER_COLDBOOT, ALC285_FIXUP_IDEAPAD_S740_COEF, ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST, ALC295_FIXUP_ASUS_DACS, @@ -4275,6 +4303,7 @@ enum { ALC287_FIXUP_LEGION_16ACHG6, ALC287_FIXUP_CS35L41_I2C_2, ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, + ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS, ALC287_FIXUP_CS35L41_I2C_4, ALC245_FIXUP_CS35L41_SPI_1, ALC245_FIXUP_CS35L41_SPI_2, @@ -6311,6 +6340,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC256_FIXUP_ACER_COLDBOOT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_acer_coldboot, + .chained = true, + .chain_id = ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, + }, [ALC285_FIXUP_IDEAPAD_S740_COEF] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_ideapad_s740_coef, @@ -6580,6 +6615,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_HP_MUTE_LED, }, + [ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + .chained = true, + .chain_id = ALC245_FIXUP_HP_X360_MUTE_LEDS, + }, [ALC287_FIXUP_CS35L41_I2C_4] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_i2c_four, @@ -7148,13 +7189,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1616, "Acer Aspire A515-57", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1616, "Acer Aspire A515-57", ALC256_FIXUP_ACER_COLDBOOT), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x166c, "Acer Predator PH16-71", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1731, "Acer Predator PHN16-72", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), @@ -7323,6 +7365,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x8259, "HP OMEN 15-ax202nf", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), @@ -7416,6 +7459,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8890, "HP Elite Dragonfly Max G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), @@ -7531,6 +7575,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8bb1, "HP Victus 15-fa1xxx (MB 8BB1)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX), @@ -7623,7 +7669,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8dcd, "HP Victus 15-fa2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), @@ -8100,6 +8146,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x380b, "Lenovo Yoga Slim 9 14ILL10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), /* Yoga Pro 9 16IMH9 and Legion 7 16ITHG6 share PCI SSID 17aa:3811 * with Legion S7 15IMH05; use codec SSID to distinguish them */ @@ -8287,6 +8334,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), + SND_PCI_QUIRK(0x1d19, 0x0006, "VAIO VJS131", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 7d17d773cfbf..5d9b53bd64ea 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -2277,6 +2277,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, int idx = kcontrol->private_value; struct hda_spdif_out *spdif; hda_nid_t nid; + unsigned int old_status; unsigned short val; int change; @@ -2285,6 +2286,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, guard(mutex)(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); nid = spdif->nid; + old_status = spdif->status; spdif->status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | @@ -2295,7 +2297,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, spdif->ctls = val; if (change && nid != (u16)-1) set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); - return change; + return change || spdif->status != old_status; } #define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info diff --git a/sound/hda/common/controller.c b/sound/hda/common/controller.c index afec5c5546ec..18dae022b324 100644 --- a/sound/hda/common/controller.c +++ b/sound/hda/common/controller.c @@ -586,11 +586,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); - trace_azx_pcm_open(chip, azx_dev); if (azx_dev == NULL) { err = -EBUSY; goto unlock; } + trace_azx_pcm_open(chip, azx_dev); runtime->private_data = azx_dev; runtime->hw = azx_pcm_hw; diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c index 776d629ba252..a45f61e12c1a 100644 --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c @@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec) */ int snd_hdac_refresh_widgets(struct hdac_device *codec) { + hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; hda_nid_t start_nid; int nums, err = 0; @@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) * widgets array. */ guard(mutex)(&codec->widget_lock); - nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); + nums = snd_hdac_get_sub_nodes(codec, fg, &start_nid); if (!start_nid || nums <= 0 || nums >= 0xff) { dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", - codec->afg); + fg); return -EINVAL; } diff --git a/sound/hda/core/ext/controller.c b/sound/hda/core/ext/controller.c index b1f1eff1d181..6d0af7e6f63c 100644 --- a/sound/hda/core/ext/controller.c +++ b/sound/hda/core/ext/controller.c @@ -90,8 +90,10 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus) for (idx = 0; idx < link_count; idx++) { hlink = kzalloc_obj(*hlink); - if (!hlink) + if (!hlink) { + snd_hdac_ext_link_free_all(bus); return -ENOMEM; + } hlink->index = idx; hlink->bus = bus; hlink->ml_addr = bus->mlcap + AZX_ML_BASE + diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c index 517bd151fcc3..1f96e0484660 100644 --- a/sound/hda/core/ext/stream.c +++ b/sound/hda/core/ext/stream.c @@ -102,8 +102,10 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, for (i = 0; i < num_stream; i++) { struct hdac_ext_stream *hext_stream = kzalloc_obj(*hext_stream); - if (!hext_stream) + if (!hext_stream) { + snd_hdac_ext_stream_free_all(bus); return -ENOMEM; + } tag = ++stream_tag; snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); idx++; @@ -111,7 +113,6 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, } return 0; - } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index 30bd76eee96e..7be6ff201ecb 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -214,7 +214,7 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus, struct snd_gf1_dma_block *block; struct snd_gf1_dma_block *free_block = NULL; - block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL); + block = kmalloc_obj(*block, atomic ? GFP_ATOMIC : GFP_KERNEL); if (!block) return -ENOMEM; diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index fb40476c6c91..a9625aedf2a4 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -868,6 +868,9 @@ snd_harmony_create(struct snd_card *card, goto free_and_ret; } + spin_lock_init(&h->mixer_lock); + spin_lock_init(&h->lock); + err = request_irq(padev->irq, snd_harmony_interrupt, 0, "harmony", h); if (err) { @@ -877,9 +880,6 @@ snd_harmony_create(struct snd_card *card, } h->irq = padev->irq; - spin_lock_init(&h->mixer_lock); - spin_lock_init(&h->lock); - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops); if (err < 0) goto free_and_ret; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 19a6927c079d..f6db08b75649 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1444,7 +1444,7 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in struct snd_cs46xx_pcm * cpcm; struct snd_pcm_runtime *runtime = substream->runtime; - cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL); + cpcm = kzalloc_obj(*cpcm); if (cpcm == NULL) return -ENOMEM; if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev, diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c index 5fc1c922620a..f356917cb6ac 100644 --- a/sound/pci/ctxfi/ctamixer.c +++ b/sound/pci/ctxfi/ctamixer.c @@ -236,7 +236,7 @@ static int get_amixer_rsc(struct amixer_mgr *mgr, *ramixer = NULL; /* Allocate mem for amixer resource */ - amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); + amixer = kzalloc_obj(*amixer); if (!amixer) return -ENOMEM; @@ -390,7 +390,7 @@ static int get_sum_rsc(struct sum_mgr *mgr, *rsum = NULL; /* Allocate mem for sum resource */ - sum = kzalloc(sizeof(*sum), GFP_KERNEL); + sum = kzalloc_obj(*sum); if (!sum) return -ENOMEM; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 69aacd06716c..9be70c6862ab 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -540,7 +540,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, err = -ENOMEM; /* Allocate mem for daio resource */ if (desc->output) { - struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL); + struct dao *dao = kzalloc_obj(*dao); if (!dao) goto error; @@ -552,7 +552,7 @@ static int get_daio_rsc(struct daio_mgr *mgr, *rdaio = &dao->daio; } else { - struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL); + struct dai *dai = kzalloc_obj(*dai); if (!dai) goto error; diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 07e1490a6d17..36066ffe8784 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -994,6 +994,7 @@ static int daio_mgr_dao_init(struct hw *hw, void *blk, unsigned int idx, unsigne /* S/PDIF output */ switch ((conf & 0xf)) { case 1: + case 9: set_field(&ctl->txctl[idx], ATXCTL_NUC, 0); break; case 2: diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c index 46dc1f509234..1fadaf22309f 100644 --- a/sound/pci/ctxfi/ctsrc.c +++ b/sound/pci/ctxfi/ctsrc.c @@ -432,9 +432,9 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc) /* Allocate mem for master src resource */ if (MEMRD == desc->mode) - src = kcalloc(desc->multi, sizeof(*src), GFP_KERNEL); + src = kzalloc_objs(*src, desc->multi); else - src = kzalloc(sizeof(*src), GFP_KERNEL); + src = kzalloc_obj(*src); if (!src) { err = -ENOMEM; diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index cc379d880cad..9d6f5df2bc7e 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -318,7 +318,7 @@ ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm) { struct ct_timer_instance *ti; - ti = kzalloc(sizeof(*ti), GFP_KERNEL); + ti = kzalloc_obj(*ti); if (!ti) return NULL; spin_lock_init(&ti->lock); diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 49cabb2eb2b7..a33817aba17f 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2470,7 +2470,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un emu->support_tlv = 1; return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp); case SNDRV_EMU10K1_IOCTL_INFO: - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info, GFP_KERNEL); if (!info) return -ENOMEM; snd_emu10k1_fx8010_info(emu, info); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index d8bbedbc8ff6..95fd82bb1c6d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1071,14 +1071,8 @@ struct hdspm { static const struct pci_device_id snd_hdspm_ids[] = { - { - .vendor = PCI_VENDOR_ID_XILINX, - .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .class = 0, - .class_mask = 0, - .driver_data = 0}, + { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI) }, + { PCI_DEVICE(0x1d18, 0x3fc6) }, /* RME HDSPe AIO PCI express audio */ {0,} }; diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index af559653e625..1ac729a58bb4 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/input.h> #include <linux/acpi.h> +#include <linux/pci.h> #include "acp.h" #include "../codecs/da7219.h" @@ -742,6 +743,18 @@ static const struct regulator_desc acp_da7219_desc = { .n_voltages = 1, }; +/* + * The ACP3.x+ (Raven/Picasso and later) audio coprocessor is a dedicated PCI + * function. Carrizo/Stoney - the only platforms handled by this driver - reach + * the ACP through the GPU driver and have no such device. + */ +#define ACP3X_PCI_DEV_ID 0x15e2 + +static const struct pci_device_id acp3x_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP3X_PCI_DEV_ID) }, + { 0, }, +}; + static int cz_probe(struct platform_device *pdev) { int ret; @@ -750,6 +763,16 @@ static int cz_probe(struct platform_device *pdev) struct regulator_dev *rdev; struct device *dev = &pdev->dev; + /* + * AMDI5682 is also matched by acp3x-alc5682-max98357 (Raven/Picasso). + * If the ACP3.x PCI function is present this is such a board; return + * -ENODEV so that driver binds instead. + */ + if (pci_dev_present(acp3x_pci_ids)) { + dev_info(dev, "ACP3.x PCI device present, deferring to acp3x-alc5682-max98357\n"); + return -ENODEV; + } + card = (struct snd_soc_card *)acp_soc_is_rltk_max(dev); if (!card) return -ENODEV; diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 6eac42bac855..1a05d4288a46 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -205,9 +205,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -EINVAL; } + if (!soc_end->link_mask) { + dev_err(dev, "invalid zero link_mask\n"); + return -EINVAL; + } + if ((ffs(soc_end->link_mask) - 1) >= amd_ctx->max_sdw_links) { + dev_err(dev, "link_id %d exceeds max_sdw_links %d\n", + ffs(soc_end->link_mask) - 1, amd_ctx->max_sdw_links); + return -EINVAL; + } + switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: - ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1), + ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -215,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, case ACP70_PCI_REV: case ACP71_PCI_REV: case ACP72_PCI_REV: - ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1), + ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a9cd1f335167..ec3e1f5f1052 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -121,9 +121,18 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -EINVAL; } + if (!sof_end->link_mask) { + dev_err(dev, "invalid zero link_mask\n"); + return -EINVAL; + } + if ((ffs(sof_end->link_mask) - 1) >= amd_ctx->max_sdw_links) { + dev_err(dev, "link_id %d exceeds max_sdw_links %d\n", + ffs(sof_end->link_mask) - 1, amd_ctx->max_sdw_links); + return -EINVAL; + } switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: - ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1), + ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -131,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, case ACP70_PCI_REV: case ACP71_PCI_REV: case ACP72_PCI_REV: - ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1), + ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask) - 1, *be_id, &cpu_pin_id, dev); if (ret) return ret; @@ -277,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) int num_devs = 0; int num_ends = 0; int num_aux = 0; + int num_confs; int num_links; int be_id = 0; int ret; @@ -287,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) return ret; } + num_confs = num_ends; /* One per DAI link, worst case is a DAI link for every endpoint */ struct asoc_sdw_dailink *sof_dais __free(kfree) = kzalloc_objs(*sof_dais, num_ends); @@ -303,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) return ret; @@ -315,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num); - codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL); + codec_conf = devm_kcalloc(dev, num_confs, sizeof(*codec_conf), GFP_KERNEL); if (!codec_conf) return -ENOMEM; @@ -326,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) return -ENOMEM; card->codec_conf = codec_conf; - card->num_configs = num_devs; + card->num_configs = num_confs; card->dai_link = dai_links; card->num_links = num_links; card->aux_dev = sof_aux; @@ -379,7 +390,7 @@ static int mc_probe(struct platform_device *pdev) ctx->private = amd_ctx; card = &ctx->card; card->dev = &pdev->dev; - card->name = "amd-soundwire"; + card->name = "amd-sdw"; card->owner = THIS_MODULE; card->late_probe = asoc_sdw_card_late_probe; diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c index e60e3821703c..3f59b753243d 100644 --- a/sound/soc/amd/renoir/acp3x-pdm-dma.c +++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c @@ -104,7 +104,7 @@ static void disable_pdm_interrupts(void __iomem *acp_base) u32 ext_int_ctrl; ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK; rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL); } diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 21d7ec0e7dbf..385fede6d77f 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -391,6 +391,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "RB"), + DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN17-41"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "83J2"), } @@ -892,6 +899,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "TM2423"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP 255R 15.6 inch G10 Notebook PC"), + } + }, {} }; diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index 710db721ffa4..40c4d833f4ed 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -275,9 +275,11 @@ static int acp6x_pdm_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct pdm_dev_data *adata = dev_get_drvdata(component->dev); + struct snd_pcm_runtime *runtime = substream->runtime; acp6x_disable_pdm_interrupts(adata->acp6x_base); adata->capture_stream = NULL; + kfree(runtime->private_data); return 0; } diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c index c47ed1e6ea2b..14d111fe29d8 100644 --- a/sound/soc/bcm/bcm63xx-i2s-whistler.c +++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c @@ -285,6 +285,7 @@ static const struct of_device_id snd_soc_bcm_audio_match[] = { {.compatible = "brcm,bcm63xx-i2s"}, { } }; +MODULE_DEVICE_TABLE(of, snd_soc_bcm_audio_match); #endif static struct platform_driver bcm63xx_i2s_driver = { diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f9a47e262a77..d88593c2bac8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -524,13 +524,13 @@ config SND_SOC_ADAU1977 tristate config SND_SOC_ADAU1977_SPI - tristate + tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - SPI" depends on SPI_MASTER select SND_SOC_ADAU1977 select REGMAP_SPI config SND_SOC_ADAU1977_I2C - tristate + tristate "Analog Devices ADAU1977/ADAU1978/ADAU1979 CODEC - I2C" depends on I2C select SND_SOC_ADAU1977 select REGMAP_I2C diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 2cf96cbdd294..e1a0e35836e6 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -14,17 +14,14 @@ * for ST-Ericsson. */ -#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/delay.h> #include <linux/pm.h> #include <linux/platform_device.h> -#include <linux/mutex.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-sysctrl.h> @@ -54,32 +51,9 @@ /* Macrocell register definitions */ #define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ -/* Nr of FIR/IIR-coeff banks in ANC-block */ -#define AB8500_NR_OF_ANC_COEFF_BANKS 2 - -/* Minimum duration to keep ANC IIR Init bit high or -low before proceeding with the configuration sequence */ -#define AB8500_ANC_SM_DELAY 2000 - -/* Sidetone states */ -static const char * const enum_sid_state[] = { - "Unconfigured", - "Apply FIR", - "FIR is configured", -}; -enum sid_state { - SID_UNCONFIGURED = 0, - SID_APPLY_FIR = 1, - SID_FIR_CONFIGURED = 2, -}; - /* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata { struct regmap *regmap; - struct mutex ctrl_lock; - - /* Sidetone */ - enum sid_state sid_status; }; static inline const char *amic_micbias_str(enum amic_micbias micbias) @@ -259,7 +233,7 @@ static const struct snd_kcontrol_new dapm_anc_in_select[] = { /* ANC - Enable/Disable */ static const struct snd_kcontrol_new dapm_anc_enable[] = { SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1, - AB8500_ANCCONF1_ENANC, 0, 0), + AB8500_ANCCONF1_ENANC, 1, 0), }; /* ANC to Earpiece - Mute */ @@ -341,12 +315,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { /* DA/AD */ - SND_SOC_DAPM_INPUT("ADC Input"), - SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_OUTPUT("DAC Output"), - SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0), @@ -538,9 +506,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_DAPM_MIXER("AD3 Channel Volume", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("AD3 Enable", - AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0, - NULL, 0), + SND_SOC_DAPM_SUPPLY("AD34 Enable", AB8500_ADPATHENA, + AB8500_ADPATHENA_ENAD34, 0, NULL, 0), /* Mic 2 */ @@ -599,9 +566,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("AD12 Enable", - AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0, - NULL, 0), + SND_SOC_DAPM_SUPPLY("AD12 Enable", AB8500_ADPATHENA, + AB8500_ADPATHENA_ENAD12, 0, NULL, 0), /* HD Capture path */ @@ -615,12 +581,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_DAPM_MIXER("AD6 Channel Volume", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("AD57 Enable", - AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, - NULL, 0), - SND_SOC_DAPM_MIXER("AD68 Enable", - AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, - NULL, 0), + SND_SOC_DAPM_SUPPLY("AD5768 Enable", AB8500_ADPATHENA, + AB8500_ADPATHENA_ENAD5768, 0, NULL, 0), /* Digital Microphone path */ @@ -652,15 +614,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_DAPM_MIXER("AD4 Channel Volume", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("AD4 Enable", - AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, - 0, NULL, 0), - /* Acoustical Noise Cancellation path */ - SND_SOC_DAPM_INPUT("ANC Configure Input"), - SND_SOC_DAPM_OUTPUT("ANC Configure Output"), - SND_SOC_DAPM_MUX("ANC Source", SND_SOC_NOPM, 0, 0, dapm_anc_in_select), @@ -703,24 +658,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"Main Supply", NULL, "Audio Power"}, {"Main Supply", NULL, "Audio Analog Power"}, - {"DAC", NULL, "ab8500_0p"}, - {"DAC", NULL, "Main Supply"}, - {"ADC", NULL, "ab8500_0c"}, - {"ADC", NULL, "Main Supply"}, - - /* ANC Configure */ - {"ANC Configure Input", NULL, "Main Supply"}, - {"ANC Configure Output", NULL, "ANC Configure Input"}, - - /* AD/DA */ - {"ADC", NULL, "ADC Input"}, - {"DAC Output", NULL, "DAC"}, - /* Powerup charge pump if DA1/2 is in use */ {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Main Supply"}, {"DA_IN1", NULL, "Charge Pump"}, {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Main Supply"}, {"DA_IN2", NULL, "Charge Pump"}, /* Headset path */ @@ -755,8 +699,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { /* HF or LineOut path */ {"DA_IN3", NULL, "ab8500_0p"}, + {"DA_IN3", NULL, "Main Supply"}, {"DA3 Channel Volume", NULL, "DA_IN3"}, {"DA_IN4", NULL, "ab8500_0p"}, + {"DA_IN4", NULL, "Main Supply"}, {"DA4 Channel Volume", NULL, "DA_IN4"}, {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"}, @@ -814,8 +760,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { /* Vibrator path */ {"DA_IN5", NULL, "ab8500_0p"}, + {"DA_IN5", NULL, "Main Supply"}, {"DA5 Channel Volume", NULL, "DA_IN5"}, {"DA_IN6", NULL, "ab8500_0p"}, + {"DA_IN6", NULL, "Main Supply"}, {"DA6 Channel Volume", NULL, "DA_IN6"}, {"VIB1 DAC", NULL, "DA5 Channel Volume"}, @@ -857,13 +805,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD1 Channel Volume", NULL, "AD1 Source Select"}, {"AD2 Channel Volume", NULL, "AD2 Source Select"}, - {"AD12 Enable", NULL, "AD1 Channel Volume"}, - {"AD12 Enable", NULL, "AD2 Channel Volume"}, + {"AD1 Channel Volume", NULL, "AD12 Enable"}, + {"AD2 Channel Volume", NULL, "AD12 Enable"}, - {"AD_OUT1", NULL, "ab8500_0c"}, - {"AD_OUT1", NULL, "AD12 Enable"}, - {"AD_OUT2", NULL, "ab8500_0c"}, - {"AD_OUT2", NULL, "AD12 Enable"}, + {"ab8500_0c", NULL, "AD_OUT1"}, + {"AD_OUT1", NULL, "Main Supply"}, + {"AD_OUT1", NULL, "AD1 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT2"}, + {"AD_OUT2", NULL, "Main Supply"}, + {"AD_OUT2", NULL, "AD2 Channel Volume"}, /* Mic 1 */ @@ -880,11 +830,11 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + {"AD3 Channel Volume", NULL, "AD34 Enable"}, - {"AD3 Enable", NULL, "AD3 Channel Volume"}, - - {"AD_OUT3", NULL, "ab8500_0c"}, - {"AD_OUT3", NULL, "AD3 Enable"}, + {"ab8500_0c", NULL, "AD_OUT3"}, + {"AD_OUT3", NULL, "Main Supply"}, + {"AD_OUT3", NULL, "AD3 Channel Volume"}, /* HD Capture path */ @@ -893,14 +843,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD5 Channel Volume", NULL, "AD5 Source Select"}, {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + {"AD5 Channel Volume", NULL, "AD5768 Enable"}, + {"AD6 Channel Volume", NULL, "AD5768 Enable"}, - {"AD57 Enable", NULL, "AD5 Channel Volume"}, - {"AD68 Enable", NULL, "AD6 Channel Volume"}, - - {"AD_OUT57", NULL, "ab8500_0c"}, - {"AD_OUT57", NULL, "AD57 Enable"}, - {"AD_OUT68", NULL, "ab8500_0c"}, - {"AD_OUT68", NULL, "AD68 Enable"}, + {"ab8500_0c", NULL, "AD_OUT57"}, + {"AD_OUT57", NULL, "Main Supply"}, + {"AD_OUT57", NULL, "AD5 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT68"}, + {"AD_OUT68", NULL, "Main Supply"}, + {"AD_OUT68", NULL, "AD6 Channel Volume"}, /* Digital Microphone path */ @@ -911,17 +862,25 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"DMic 5", NULL, "V-DMIC"}, {"DMic 6", NULL, "V-DMIC"}, - {"AD1 Source Select", NULL, "DMic 1"}, - {"AD2 Source Select", NULL, "DMic 2"}, - {"AD3 Source Select", NULL, "DMic 3"}, - {"AD5 Source Select", NULL, "DMic 5"}, - {"AD6 Source Select", NULL, "DMic 6"}, + {"DMIC1", NULL, "DMic 1"}, + {"DMIC2", NULL, "DMic 2"}, + {"DMIC3", NULL, "DMic 3"}, + {"DMIC4", NULL, "DMic 4"}, + {"DMIC5", NULL, "DMic 5"}, + {"DMIC6", NULL, "DMic 6"}, - {"AD4 Channel Volume", NULL, "DMic 4"}, - {"AD4 Enable", NULL, "AD4 Channel Volume"}, + {"AD1 Source Select", "DMic 1", "DMIC1"}, + {"AD2 Source Select", "DMic 2", "DMIC2"}, + {"AD3 Source Select", "DMic 3", "DMIC3"}, + {"AD5 Source Select", "DMic 5", "DMIC5"}, + {"AD6 Source Select", "DMic 6", "DMIC6"}, - {"AD_OUT4", NULL, "ab8500_0c"}, - {"AD_OUT4", NULL, "AD4 Enable"}, + {"AD4 Channel Volume", NULL, "DMIC4"}, + {"AD4 Channel Volume", NULL, "AD34 Enable"}, + + {"ab8500_0c", NULL, "AD_OUT4"}, + {"AD_OUT4", NULL, "Main Supply"}, + {"AD_OUT4", NULL, "AD4 Channel Volume"}, /* LineIn Bypass path */ @@ -946,13 +905,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { /* Sidetone Filter path */ - {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, - {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "LineIn Left", "AD1 Channel Volume"}, + {"Sidetone Left Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Left Source", "Mic 1", "AD3 Channel Volume"}, {"Sidetone Left Source", "Headset Left", "DA_IN1"}, - {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, - {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"}, + {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"}, {"Sidetone Right Source", "Headset Right", "DA_IN2"}, {"STFIR1 Control", NULL, "Sidetone Left Source"}, @@ -981,75 +940,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { }; /* - * Control-events - */ - -static int sid_status_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - - guard(mutex)(&drvdata->ctrl_lock); - ucontrol->value.enumerated.item[0] = drvdata->sid_status; - - return 0; -} - -/* Write sidetone FIR-coefficients configuration sequence */ -static int sid_status_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); - unsigned int param, sidconf; - int status = 1; - - dev_dbg(component->dev, "%s: Enter\n", __func__); - - if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { - dev_err(component->dev, - "%s: ERROR: This control supports '%s' only!\n", - __func__, enum_sid_state[SID_APPLY_FIR]); - return -EIO; - } - - guard(mutex)(&drvdata->ctrl_lock); - - sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); - if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { - if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { - dev_err(component->dev, "%s: Sidetone busy while off!\n", - __func__); - status = -EPERM; - } else { - status = -EBUSY; - } - dev_dbg(component->dev, "%s: Exit\n", __func__); - return status; - } - - snd_soc_component_write(component, AB8500_SIDFIRADR, 0); - - for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { - snd_soc_component_write(component, AB8500_SIDFIRCOEF1, 0); - snd_soc_component_write(component, AB8500_SIDFIRCOEF2, 0); - } - - snd_soc_component_update_bits(component, AB8500_SIDFIRADR, - BIT(AB8500_SIDFIRADR_FIRSIDSET), - BIT(AB8500_SIDFIRADR_FIRSIDSET)); - snd_soc_component_update_bits(component, AB8500_SIDFIRADR, - BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); - - drvdata->sid_status = SID_FIR_CONFIGURED; - - dev_dbg(component->dev, "%s: Exit\n", __func__); - - return status; -} - -/* * Controls - Non-DAPM ASoC */ @@ -1332,9 +1222,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, enum_slavemaster); -/* Sidetone */ -static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); - /* ANC */ static struct snd_kcontrol_new ab8500_ctrls[] = { @@ -1625,8 +1512,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), /* Sidetone */ - SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, - sid_status_control_get, sid_status_control_put), SOC_SINGLE_STROBE("Sidetone Reset", AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), }; @@ -1638,23 +1523,25 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { static int ab8500_audio_init_audioblock(struct snd_soc_component *component) { int status; + u8 mask = AB8500_STW4500CTRL3_CLK32KOUT2DIS | + AB8500_STW4500CTRL3_RESETAUDN; dev_dbg(component->dev, "%s: Enter.\n", __func__); - /* Reset audio-registers and disable 32kHz-clock output 2 */ - status = ab8500_sysctrl_write(AB8500_STW4500CTRL3, - AB8500_STW4500CTRL3_CLK32KOUT2DIS | - AB8500_STW4500CTRL3_RESETAUDN, - AB8500_STW4500CTRL3_RESETAUDN); + /* Reset the audio registers and disable the unused 32 kHz output. */ + status = ab8500_sysctrl_write(AB8500_STW4500CTRL3, mask, + AB8500_STW4500CTRL3_CLK32KOUT2DIS); if (status < 0) return status; - return 0; + return ab8500_sysctrl_write(AB8500_STW4500CTRL3, mask, mask); } static int ab8500_audio_setup_mics(struct snd_soc_component *component, struct amic_settings *amics) { + struct device *dev = component->dev; + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); u8 value8; unsigned int value; @@ -1663,20 +1550,21 @@ static int ab8500_audio_setup_mics(struct snd_soc_component *component, dev_dbg(component->dev, "%s: Enter.\n", __func__); - /* Set DMic-clocks to outputs */ - status = abx500_get_register_interruptible(component->dev, AB8500_MISC, - AB8500_GPIO_DIR4_REG, - &value8); - if (status < 0) - return status; - value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | - GPIO31_DIR_OUTPUT; - status = abx500_set_register_interruptible(component->dev, - AB8500_MISC, - AB8500_GPIO_DIR4_REG, - value); - if (status < 0) - return status; + /* Set DMic-clocks to outputs; these GPIOs do not exist on AB8505. */ + if (!is_ab8505(ab8500)) { + status = abx500_get_register_interruptible(dev, AB8500_MISC, + AB8500_GPIO_DIR4_REG, + &value8); + if (status < 0) + return status; + value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | + GPIO31_DIR_OUTPUT; + status = abx500_set_register_interruptible(dev, AB8500_MISC, + AB8500_GPIO_DIR4_REG, + value); + if (status < 0) + return status; + } /* Attach regulators to AMic DAPM-paths */ dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__, @@ -1745,149 +1633,91 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component, return 0; } -static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, - unsigned int delay) -{ - unsigned int mask, val; - struct snd_soc_component *component = dai->component; - - mask = BIT(AB8500_DIGIFCONF2_IF0DEL); - val = 0; - - switch (delay) { - case 0: - break; - case 1: - val |= BIT(AB8500_DIGIFCONF2_IF0DEL); - break; - default: - dev_err(dai->component->dev, - "%s: ERROR: Unsupported bit-delay (0x%x)!\n", - __func__, delay); - return -EINVAL; - } - - dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n", - __func__, delay); - snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); - - return 0; -} - -/* Gates clocking according format mask */ -static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component, - unsigned int fmt) -{ - unsigned int mask; - unsigned int val; - - mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) | - BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); - - val = BIT(AB8500_DIGIFCONF1_ENMASTGEN); - - switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { - case SND_SOC_DAIFMT_CONT: /* continuous clock */ - dev_dbg(component->dev, "%s: IF0 Clock is continuous.\n", - __func__); - val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); - break; - case SND_SOC_DAIFMT_GATED: /* clock is gated */ - dev_dbg(component->dev, "%s: IF0 Clock is gated.\n", - __func__); - break; - default: - dev_err(component->dev, - "%s: ERROR: Unsupported clock mask (0x%x)!\n", - __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK); - return -EINVAL; - } - - snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val); - - return 0; -} - static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - unsigned int mask; - unsigned int val; struct snd_soc_component *component = dai->component; - int status; + unsigned int conf1_mask, conf1_val = 0; + unsigned int conf2_mask, conf2_val = 0; + unsigned int conf3_mask, conf3_val = 0; + bool provider = false; + int ret; dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt); - mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) | + conf3_mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) | BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) | BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) | BIT(AB8500_DIGIFCONF3_IF0MASTER); - val = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBP_CFP: - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0 Master-mode: AB8500 provider.\n", __func__); - val |= BIT(AB8500_DIGIFCONF3_IF0MASTER); + conf3_val |= BIT(AB8500_DIGIFCONF3_IF0MASTER); + provider = true; break; case SND_SOC_DAIFMT_CBC_CFC: - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0 Master-mode: AB8500 consumer.\n", __func__); break; case SND_SOC_DAIFMT_CBC_CFP: case SND_SOC_DAIFMT_CBP_CFC: - dev_err(dai->component->dev, + dev_err(component->dev, "%s: ERROR: The device is either a provider or a consumer.\n", __func__); fallthrough; default: - dev_err(dai->component->dev, - "%s: ERROR: Unsupporter clocking mask 0x%x\n", + dev_err(component->dev, + "%s: ERROR: Unsupported clocking mask 0x%x\n", __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); return -EINVAL; } - snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val); - - /* Set clock gating */ - status = ab8500_codec_set_dai_clock_gate(component, fmt); - if (status) { - dev_err(dai->component->dev, - "%s: ERROR: Failed to set clock gate (%d).\n", - __func__, status); - return status; + conf1_mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) | + BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + if (provider) + conf1_val = conf1_mask; + break; + case SND_SOC_DAIFMT_GATED: + if (provider) + conf1_val = BIT(AB8500_DIGIFCONF1_ENMASTGEN); + break; + default: + dev_err(component->dev, "%s: Unsupported clock mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK); + return -EINVAL; } - /* Setting data transfer format */ - - mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) | - BIT(AB8500_DIGIFCONF2_IF0FORMAT1) | - BIT(AB8500_DIGIFCONF2_FSYNC0P) | - BIT(AB8500_DIGIFCONF2_BITCLK0P); - val = 0; + conf2_mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) | + BIT(AB8500_DIGIFCONF2_IF0FORMAT1) | + BIT(AB8500_DIGIFCONF2_IF0DEL) | + BIT(AB8500_DIGIFCONF2_FSYNC0P) | + BIT(AB8500_DIGIFCONF2_BITCLK0P); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* I2S mode */ - dev_dbg(dai->component->dev, "%s: IF0 Protocol: I2S\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1); - ab8500_audio_set_bit_delay(dai, 0); + dev_dbg(component->dev, "%s: IF0 Protocol: I2S\n", __func__); + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1) | + BIT(AB8500_DIGIFCONF2_IF0DEL); break; case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0 Protocol: DSP A (TDM)\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); - ab8500_audio_set_bit_delay(dai, 1); + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0) | + BIT(AB8500_DIGIFCONF2_IF0DEL); break; case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0 Protocol: DSP B (TDM)\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); - ab8500_audio_set_bit_delay(dai, 0); + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); break; default: - dev_err(dai->component->dev, + dev_err(component->dev, "%s: ERROR: Unsupported format (0x%x)!\n", __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); return -EINVAL; @@ -1895,39 +1725,50 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0: Normal bit clock, normal frame\n", __func__); break; case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0: Normal bit clock, inverted frame\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); + conf2_val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); break; case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0: Inverted bit clock, normal frame\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + conf2_val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); break; case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */ - dev_dbg(dai->component->dev, + dev_dbg(component->dev, "%s: IF0: Inverted bit clock, inverted frame\n", __func__); - val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); - val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + conf2_val |= BIT(AB8500_DIGIFCONF2_FSYNC0P) | + BIT(AB8500_DIGIFCONF2_BITCLK0P); break; default: - dev_err(dai->component->dev, + dev_err(component->dev, "%s: ERROR: Unsupported INV mask 0x%x\n", __func__, fmt & SND_SOC_DAIFMT_INV_MASK); return -EINVAL; } - snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); + ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, + conf3_mask, conf3_val); + if (ret < 0) + return ret; - return 0; + ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, + conf1_mask, conf1_val); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, + conf2_mask, conf2_val); + + return ret < 0 ? ret : 0; } static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, @@ -1935,23 +1776,27 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct snd_soc_component *component = dai->component; - unsigned int val, mask, slot, slots_active; + unsigned int active_mask, clock_ratio, slot, value, ad_out, reg; + unsigned int tx_active, rx_active; + unsigned int conf1_val, conf2_val; + unsigned int mask; + int channel, ret; mask = BIT(AB8500_DIGIFCONF2_IF0WL0) | BIT(AB8500_DIGIFCONF2_IF0WL1); - val = 0; + conf2_val = 0; switch (slot_width) { case 16: break; case 20: - val |= BIT(AB8500_DIGIFCONF2_IF0WL0); + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL0); break; case 24: - val |= BIT(AB8500_DIGIFCONF2_IF0WL1); + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL1); break; case 32: - val |= BIT(AB8500_DIGIFCONF2_IF0WL1) | + conf2_val |= BIT(AB8500_DIGIFCONF2_IF0WL1) | BIT(AB8500_DIGIFCONF2_IF0WL0); break; default: @@ -1960,27 +1805,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n", - __func__, slot_width); - snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); - - /* Setup TDM clocking according to slot count */ - dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots); - mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | - BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); switch (slots) { case 2: - val = AB8500_MASK_NONE; - break; case 4: - val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0); - break; case 8: - val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); - break; case 16: - val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | - BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); break; default: dev_err(dai->component->dev, @@ -1988,92 +1817,134 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, __func__, slots); return -EINVAL; } - snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val); - - /* Setup TDM DA according to active tx slots */ - if (tx_mask & ~0xff) - return -EINVAL; - - mask = AB8500_DASLOTCONFX_SLTODAX_MASK; - tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET; - slots_active = hweight32(tx_mask); - - dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__, - slots_active); - - switch (slots_active) { - case 0: + clock_ratio = slots * slot_width; + switch (clock_ratio) { + case 32: + conf1_val = 0; break; - case 1: - slot = ffs(tx_mask); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot); + case 64: + conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0); break; - case 2: - slot = ffs(tx_mask); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot); - slot = fls(tx_mask); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot); - snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot); + case 128: + conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); break; - case 8: - dev_dbg(dai->component->dev, - "%s: In 8-channel mode DA-from-slot mapping is set manually.", - __func__); + case 256: + conf1_val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); break; default: - dev_err(dai->component->dev, - "%s: Unsupported number of active TX-slots (%d)!\n", - __func__, slots_active); + dev_err(component->dev, "%s: Unsupported BCLK ratio (%u)!\n", + __func__, clock_ratio); return -EINVAL; } - /* Setup TDM AD according to active RX-slots */ + active_mask = GENMASK(min(slots, 8) - 1, 0); + if ((tx_mask | rx_mask) & ~active_mask) { + dev_err(component->dev, "%s: Slot mask exceeds slot count\n", + __func__); + return -EINVAL; + } - if (rx_mask & ~0xff) + tx_active = hweight32(tx_mask); + rx_active = hweight32(rx_mask); + if (tx_active != 0 && tx_active != 1 && tx_active != 2 && + tx_active != 8) { + dev_err(component->dev, "%s: Unsupported active TX slots (%u)!\n", + __func__, tx_active); return -EINVAL; + } + if (rx_active != 0 && rx_active != 1 && rx_active != 2 && + rx_active != 8) { + dev_err(component->dev, "%s: Unsupported active RX slots (%u)!\n", + __func__, rx_active); + return -EINVAL; + } - rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET; - slots_active = hweight32(rx_mask); + dev_dbg(component->dev, + "%s: %d slots of %d bits, TX active: %u, RX active: %u\n", + __func__, slots, slot_width, tx_active, rx_active); - dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__, - slots_active); + ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, + mask, conf2_val); + if (ret < 0) + return ret; - switch (slots_active) { - case 0: - break; - case 1: - slot = ffs(rx_mask); - snd_soc_component_update_bits(component, AB8500_ADSLOTSEL(slot), - AB8500_MASK_SLOT(slot), - AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); - break; - case 2: - slot = ffs(rx_mask); - snd_soc_component_update_bits(component, - AB8500_ADSLOTSEL(slot), - AB8500_MASK_SLOT(slot), - AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); - slot = fls(rx_mask); - snd_soc_component_update_bits(component, - AB8500_ADSLOTSEL(slot), - AB8500_MASK_SLOT(slot), - AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot)); - break; - case 8: - dev_dbg(dai->component->dev, - "%s: In 8-channel mode AD-to-slot mapping is set manually.", - __func__); - break; - default: - dev_err(dai->component->dev, - "%s: Unsupported number of active RX-slots (%d)!\n", - __func__, slots_active); - return -EINVAL; + mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + ret = snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, + mask, conf1_val); + if (ret < 0) + return ret; + + mask = AB8500_DASLOTCONFX_SLTODAX_MASK; + if (tx_active == 1 || tx_active == 2) { + slot = __ffs(tx_mask) + AB8500_DA_DATA0_OFFSET; + reg = AB8500_DASLOTCONF1; + ret = snd_soc_component_update_bits(component, reg, mask, slot); + if (ret < 0) + return ret; + reg = AB8500_DASLOTCONF3; + ret = snd_soc_component_update_bits(component, reg, mask, slot); + if (ret < 0) + return ret; + + if (tx_active == 2) + slot = __fls(tx_mask) + AB8500_DA_DATA0_OFFSET; + reg = AB8500_DASLOTCONF2; + ret = snd_soc_component_update_bits(component, reg, mask, slot); + if (ret < 0) + return ret; + reg = AB8500_DASLOTCONF4; + ret = snd_soc_component_update_bits(component, reg, mask, slot); + if (ret < 0) + return ret; + } else if (tx_active == 8) { + channel = 0; + for (slot = 0; slot < 8; slot++) { + if (!(tx_mask & BIT(slot))) + continue; + reg = AB8500_DASLOTCONF1 + channel++; + value = slot + AB8500_DA_DATA0_OFFSET; + ret = snd_soc_component_update_bits(component, reg, mask, value); + if (ret < 0) + return ret; + } + } + + if (rx_active == 1 || rx_active == 2) { + slot = __ffs(rx_mask) + AB8500_AD_DATA0_OFFSET; + value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, + slot); + reg = AB8500_ADSLOTSEL(slot); + mask = AB8500_MASK_SLOT(slot); + ret = snd_soc_component_update_bits(component, reg, mask, value); + if (ret < 0) + return ret; + + if (rx_active == 2) { + slot = __fls(rx_mask) + AB8500_AD_DATA0_OFFSET; + value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, + slot); + reg = AB8500_ADSLOTSEL(slot); + mask = AB8500_MASK_SLOT(slot); + ret = snd_soc_component_update_bits(component, reg, mask, value); + if (ret < 0) + return ret; + } + } else if (rx_active == 8) { + channel = 0; + for (slot = 0; slot < 8; slot++) { + if (!(rx_mask & BIT(slot))) + continue; + ad_out = AB8500_AD_OUT1 + channel++; + value = AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(ad_out, slot); + reg = AB8500_ADSLOTSEL(slot); + mask = AB8500_MASK_SLOT(slot); + ret = snd_soc_component_update_bits(component, reg, mask, value); + if (ret < 0) + return ret; + } } return 0; @@ -2170,10 +2041,8 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, static int ab8500_codec_probe(struct snd_soc_component *component) { - struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct device *dev = component->dev; struct device_node *np = dev->of_node; - struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); struct ab8500_codec_platform_data codec_pdata; int status; @@ -2181,6 +2050,13 @@ static int ab8500_codec_probe(struct snd_soc_component *component) ab8500_codec_of_probe(dev, np, &codec_pdata); + status = ab8500_audio_init_audioblock(component); + if (status < 0) { + dev_err(dev, "%s: failed to init audio-block (%d)!\n", + __func__, status); + return status; + } + status = ab8500_audio_setup_mics(component, &codec_pdata.amics); if (status < 0) { pr_err("%s: Failed to setup mics (%d)!\n", __func__, status); @@ -2193,23 +2069,12 @@ static int ab8500_codec_probe(struct snd_soc_component *component) return status; } - status = ab8500_audio_init_audioblock(component); - if (status < 0) { - dev_err(dev, "%s: failed to init audio-block (%d)!\n", - __func__, status); - return status; - } - /* Override HW-defaults */ snd_soc_component_write(component, AB8500_ANACONF5, BIT(AB8500_ANACONF5_HSAUTOEN)); snd_soc_component_write(component, AB8500_SHORTCIRCONF, BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); - snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); - - mutex_init(&drvdata->ctrl_lock); - return status; } @@ -2238,7 +2103,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) GFP_KERNEL); if (!drvdata) return -ENOMEM; - drvdata->sid_status = SID_UNCONFIGURED; dev_set_drvdata(&pdev->dev, drvdata); drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index d1c6c4ddf506..5a11cafdff36 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); +static const struct of_device_id adau1977_i2c_of_match[] = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); + static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", + .of_match_table = adau1977_i2c_of_match, }, .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c index 878cde9d1014..c98da5ba9e9e 100644 --- a/sound/soc/codecs/adau1977-spi.c +++ b/sound/soc/codecs/adau1977-spi.c @@ -53,18 +53,18 @@ static const struct spi_device_id adau1977_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); -static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = { - { .compatible = "adi,adau1977" }, - { .compatible = "adi,adau1978" }, - { .compatible = "adi,adau1979" }, - { }, +static const struct of_device_id adau1977_spi_of_match[] = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, }; MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); static struct spi_driver adau1977_spi_driver = { .driver = { .name = "adau1977", - .of_match_table = of_match_ptr(adau1977_spi_of_match), + .of_match_table = adau1977_spi_of_match, }, .probe = adau1977_spi_probe, .id_table = adau1977_spi_ids, diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 41a9a5b005c6..9bc19d2e1639 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -317,6 +317,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name, unsigned long size = 0; status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL); + if (status == EFI_SUCCESS) + return ERR_PTR(-ENOENT); if (status != EFI_BUFFER_TOO_SMALL) return ERR_PTR(cs_amp_convert_efi_status(status)); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 4fba59e80c37..98bb4542b914 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -386,11 +386,8 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - if (!cs35l56->base.init_done) - return 0; - - /* runtime_resume unmasks the interrupt */ - cs35l56_mask_soundwire_interrupts(cs35l56); + if (cs35l56->sdw_attached) + cs35l56_mask_soundwire_interrupts(cs35l56); return cs35l56_system_suspend(dev); } diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index b9118ad8fab5..35d210626627 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -18,9 +18,11 @@ #include <linux/interrupt.h> #include <linux/math.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -37,6 +39,13 @@ #include "wm_adsp.h" #include "cs35l56.h" +/* + * snd_soc_register_component() can call component_probe() on all instances + * in a card, so deferred registration must be protected across all instances. + */ +static DEFINE_MUTEX(cs35l56_component_register_lock); +static bool cs35l56_shutting_down; + void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56) { /* @@ -1365,12 +1374,6 @@ static int _cs35l56_component_probe(struct snd_soc_component *component) BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); - if (!wait_for_completion_timeout(&cs35l56->init_completion, - msecs_to_jiffies(5000))) { - dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__); - return -ENODEV; - } - cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type); if (!cs35l56->dsp.part) return -ENOMEM; @@ -1939,6 +1942,45 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 return ret; } +static int cs35l56_component_register(struct cs35l56_private *cs35l56) +{ + int ret; + + ret = snd_soc_register_component(cs35l56->base.dev, + &soc_component_dev_cs35l56, + cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Register codec failed: %d\n", ret); + return ret; + } + + cs35l56->component_registered = true; + + return 0; +} + +static void cs35l56_component_register_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + component_register_work); + int ret; + + guard(mutex)(&cs35l56_component_register_lock); + + if (cs35l56_shutting_down) + return; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err); + if (ret) { + dev_err(cs35l56->base.dev, "register_work failed to get pm_runtime: %d\n", ret); + return; + } + + cs35l56_component_register(cs35l56); +} + int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) { int ret; @@ -1947,6 +1989,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) mutex_init(&cs35l56->base.irq_lock); cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; + INIT_WORK(&cs35l56->component_register_work, cs35l56_component_register_work); dev_set_drvdata(cs35l56->base.dev, cs35l56); @@ -2020,12 +2063,17 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq) if (ret) goto err_remove_wm_adsp; - ret = snd_soc_register_component(cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); - if (ret < 0) { - dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err_free_irq; + /* + * Defer calling snd_soc_register_component() on SoundWire to prevent + * a deadlock where it calls our component_probe(), which requires the + * SoundWire enumeration to complete, but because we are still in probe() + * the SoundWire core will not call the update_status() callback. At time + * of writing snd_soc_register_component() never returns EPROBE_DEFER. + */ + if (!cs35l56->sdw_peripheral) { + ret = cs35l56_component_register(cs35l56); + if (ret < 0) + goto err_free_irq; } return 0; @@ -2055,6 +2103,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE"); int cs35l56_init(struct cs35l56_private *cs35l56) { + bool first_time_init = !cs35l56->base.init_done; int ret; /* @@ -2131,13 +2180,23 @@ post_soft_reset: cs35l56->base.init_done = true; complete_all(&cs35l56->init_completion); + if (cs35l56->sdw_peripheral && first_time_init) { + /* + * Hardware now accessible, queue work to call + * snd_soc_register_component(). + */ + queue_work(system_freezable_wq, &cs35l56->component_register_work); + } + return 0; } EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE"); void cs35l56_remove(struct cs35l56_private *cs35l56) { - snd_soc_unregister_component(cs35l56->base.dev); + cancel_work_sync(&cs35l56->component_register_work); + if (cs35l56->component_registered) + snd_soc_unregister_component(cs35l56->base.dev); cs35l56->base.init_done = false; @@ -2172,6 +2231,37 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { }; #endif +static int cs35l56_reboot_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + guard(mutex)(&cs35l56_component_register_lock); + cs35l56_shutting_down = true; + + return NOTIFY_DONE; +} + +static struct notifier_block cs35l56_reboot_notifier = { + .notifier_call = cs35l56_reboot_notify, +}; + +static int __init cs35l56_modinit(void) +{ + /* + * Use reboot notifier to prevent race between shutdown and + * snd_soc_register_component(). Driver shutdown() callback would + * run too late, after device_shutdown() is already walking the + * device list that component registration can modify. + */ + return register_reboot_notifier(&cs35l56_reboot_notifier); +} +module_init(cs35l56_modinit); + +static void __exit cs35l56_modexit(void) +{ + unregister_reboot_notifier(&cs35l56_reboot_notifier); +} +module_exit(cs35l56_modexit); + MODULE_DESCRIPTION("ASoC CS35L56 driver"); MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED"); MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 35c02ae17de3..f7cf8aa653e2 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -32,6 +32,7 @@ struct sdw_slave; struct cs35l56_private { struct wm_adsp dsp; /* must be first member */ struct cs35l56_base base; + struct work_struct component_register_work; struct work_struct dsp_work; struct workqueue_struct *dsp_wq; struct snd_soc_component *component; @@ -41,6 +42,7 @@ struct cs35l56_private { const char *fallback_fw_suffix; bool soft_resetting; bool sdw_attached; + bool component_registered; struct completion init_completion; int speaker_id; diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index c5460589a88b..b6eadc2e9659 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -26,6 +26,7 @@ struct es8326_priv { struct snd_soc_component *component; struct delayed_work jack_detect_work; struct delayed_work button_press_work; + struct delayed_work capture_pop_work; struct snd_soc_jack *jack; int irq; /* The lock protects the situation that an irq is generated @@ -628,6 +629,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x00); } else { + cancel_delayed_work_sync(&es8326->capture_pop_work); regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, 0x0F, 0x0F); if (es8326->version > ES8326_VERSION_B) { @@ -666,8 +668,9 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70); regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00); } - regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, - 0x0F, 0x00); + + queue_delayed_work(system_dfl_wq, &es8326->capture_pop_work, + msecs_to_jiffies(40)); } } return 0; @@ -773,6 +776,15 @@ static void es8326_disable_micbias(struct snd_soc_component *component) snd_soc_dapm_mutex_unlock(dapm); } +static void es8326_capture_pop_handler(struct work_struct *work) +{ + struct es8326_priv *es8326 = + container_of(work, struct es8326_priv, capture_pop_work.work); + + regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE, + 0x0F, 0x00); +} + /* * For button detection, set the following in soundcard * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); @@ -1140,6 +1152,7 @@ static int es8326_suspend(struct snd_soc_component *component) struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); cancel_delayed_work_sync(&es8326->jack_detect_work); + cancel_delayed_work_sync(&es8326->capture_pop_work); es8326_disable_micbias(component); es8326->calibrated = false; regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d); @@ -1291,6 +1304,8 @@ static int es8326_i2c_probe(struct i2c_client *i2c) es8326_jack_detect_handler); INIT_DELAYED_WORK(&es8326->button_press_work, es8326_jack_button_handler); + INIT_DELAYED_WORK(&es8326->capture_pop_work, + es8326_capture_pop_handler); /* ES8316 is level-based while ES8326 is edge-based */ ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 80efce3e0a22..2ea0c4052ce1 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -812,6 +812,23 @@ static int es8389_pcm_hw_free(struct snd_pcm_substream *substream, return 0; } +static void es8389_standby(struct snd_soc_component *component) +{ + struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); + + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); + regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); + regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); + usleep_range(70000, 72000); + regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); + regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); + regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); + regmap_write(es8389->regmap, ES8389_RESET, 0x3E); + regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); + usleep_range(8000, 8500); + regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); +} + static int es8389_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -834,18 +851,7 @@ static int es8389_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04); - regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04); - regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4); - usleep_range(70000, 72000); - regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59); - regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00); - regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00); - regmap_write(es8389->regmap, ES8389_RESET, 0x3E); - regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80); - usleep_range(8000, 8500); - regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00); - + es8389_standby(component); clk_disable_unprepare(es8389->mclk); break; case SND_SOC_BIAS_OFF: @@ -1015,7 +1021,7 @@ static int es8389_suspend(struct snd_soc_component *component) { struct es8389_private *es8389 = snd_soc_component_get_drvdata(component); - es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + es8389_standby(component); regcache_cache_only(es8389->regmap, true); regcache_mark_dirty(es8389->regmap); @@ -1084,7 +1090,7 @@ static int es8389_probe(struct snd_soc_component *component) es8389->hpf_freq = ES8389_HPF_DEFAULT; es8389_init(component); - es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY); + es8389_standby(component); return 0; } diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index bc2c22436ba6..7aa50c5bd3df 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -426,10 +426,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + if (!memcmp(hcp->iec_status, ucontrol->value.iec958.status, + sizeof(hcp->iec_status))) + return 0; + memcpy(hcp->iec_status, ucontrol->value.iec958.status, sizeof(hcp->iec_status)); - return 0; + return 1; } static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c index 1768c249650d..3be0491a2588 100644 --- a/sound/soc/codecs/mt6351.c +++ b/sound/soc/codecs/mt6351.c @@ -1478,6 +1478,7 @@ static const struct of_device_id mt6351_of_match[] = { {.compatible = "mediatek,mt6351-sound",}, {} }; +MODULE_DEVICE_TABLE(of, mt6351_of_match); static struct platform_driver mt6351_codec_driver = { .driver = { diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index efadb6b1b82d..f9ba16269817 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -16,6 +16,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-dapm.h> +#include <sound/sdw.h> #include <sound/initval.h> #include "rt1318-sdw.h" @@ -564,11 +565,10 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; - int retval, port, num_channels, ch_mask; + int retval, port; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -582,23 +582,13 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ /* port 1 for playback */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) port = 1; - } else { - direction = SDW_DATA_DIR_TX; + else port = 2; - } - - num_channels = params_channels(params); - ch_mask = (1 << num_channels) - 1; - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = num_channels; - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - - port_config.ch_mask = ch_mask; + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = port; retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config, diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 90333779971d..72af413306fa 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -3246,7 +3246,7 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; struct sdw_port_config dmic_port_config[2]; struct sdw_stream_runtime *sdw_stream; diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 8860d81134e7..a9f3aa4e143a 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -13,6 +13,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt712-sdca.h" #include "rt712-sdca-dmic.h" @@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; struct sdw_stream_runtime *sdw_stream; - int retval, num_channels; + int retval; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, if (!rt712->slave) return -EINVAL; - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = SDW_DATA_DIR_TX; - - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = 2; retval = sdw_stream_add_slave(rt712->slave, &stream_config, diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index c50e74e20a88..edba0367d9ce 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -18,12 +18,16 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0x201a ... 0x201f: case 0x2029 ... 0x202a: case 0x202d ... 0x2034: case 0x2230 ... 0x2232: case 0x2f01 ... 0x2f0a: case 0x2f35 ... 0x2f36: + case 0x2f3a: + case 0x2f3d: + case 0x2f41: case 0x2f50: case 0x2f54: case 0x2f58 ... 0x2f5d: @@ -48,6 +52,7 @@ static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x004d: case 0x201b: case 0x201c: case 0x201d: @@ -56,6 +61,9 @@ static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x2230: case 0x2f01: case 0x2f35: + case 0x2f3a: + case 0x2f3d: + case 0x2f41: case 0x320c: case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 13574513b181..38052cb19790 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -23,6 +23,7 @@ #include <linux/soundwire/sdw_registers.h> #include <linux/slab.h> #include <sound/soc-dapm.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt712-sdca.h" @@ -72,14 +73,57 @@ static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, return rt712_sdca_index_write(rt712, nid, reg, tmp); } +static void rt712_sdca_clk_patch(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); + usleep_range(1000, 1100); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0017); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0001); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF1, 0x0081, 0x0081); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x0080, 0x0080); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF2, 0x001f, 0x0000); + regmap_update_bits(rt712->regmap, RT712_PLL2_CONF3, 0x0010, 0x0010); + usleep_range(1000, 1100); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); +} + +static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0x0800, + 0x0000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, 0x49, 0xf000, + 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x0c, 0xc000, + 0xc000); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_ANALOG_CTL, 0x00, 0xc000, + 0xc000); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0081); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x02); + usleep_range(1000, 1100); + regmap_write(rt712->regmap, RT712_SDW_ROOT_CLK, 0x03); + usleep_range(1000, 1100); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000); +} + static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) { unsigned int val, loop_rc = 0, loop_dc = 0; struct device *dev; struct regmap *regmap = rt712->regmap; + unsigned int clk_base; int chk_cnt = 100; int ret = 0; + regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); + mutex_lock(&rt712->calibrate_mutex); dev = regmap_get_device(regmap); @@ -108,8 +152,35 @@ static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) if (ret < 0) goto _cali_fail_; } - if (loop_dc == chk_cnt) - dev_err(dev, "%s, calibration time-out!\n", __func__); + + if (loop_dc == chk_cnt) { + if (clk_base == RT712_CLK_FREQ_24_576MHZ) { + rt712_sdca_clk_patch(rt712); + rt712_sdca_clk_patch2(rt712); + } + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, 0x7883); + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, 0xf893); + rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + for (loop_dc = 0; loop_dc < chk_cnt && + (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { + usleep_range(10000, 11000); + ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + if (ret < 0) + goto _cali_fail_; + } + + if (loop_dc == chk_cnt) + dev_err(dev, "%s, calibration time-out!\n", __func__); + else + dev_dbg(dev, "%s, calibration success!\n", __func__); + } if (loop_dc == chk_cnt || loop_rc == chk_cnt) ret = -ETIMEDOUT; @@ -1449,11 +1520,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; - int retval, port, num_channels; + int retval, port; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id); @@ -1471,7 +1541,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; if (dai->id == RT712_AIF1) port = 1; else if (dai->id == RT712_AIF2) @@ -1479,7 +1548,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } else { - direction = SDW_DATA_DIR_TX; if (dai->id == RT712_AIF1) port = 4; else if (dai->id == RT712_AIF3) @@ -1488,13 +1556,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = port; retval = sdw_stream_add_slave(rt712->slave, &stream_config, @@ -1766,9 +1829,13 @@ static void rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712) static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) { - int ret = 0; unsigned int jack_func_status, mic_func_status, amp_func_status; struct device *dev = &rt712->slave->dev; + unsigned int clk_base; + int ret = 0; + + regmap_read(rt712->regmap, RT712_SDW_ROOT_CLK, &clk_base); + dev_dbg(dev, "%s clk_base=%x", __func__, clk_base); regmap_read(rt712->regmap, SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status); @@ -1780,6 +1847,12 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) __func__, jack_func_status, mic_func_status, amp_func_status); rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL3, 0x7778); + + if (clk_base == RT712_CLK_FREQ_24_576MHZ) { + rt712_sdca_clk_patch(rt712); + rt712_sdca_clk_patch2(rt712); + } + /* DMIC */ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) { rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index 46740281a5c1..6229fe341bb5 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -162,6 +162,16 @@ struct rt712_dmic_kctrl_priv { #define RT712_EAPD_HIGH 0x2 #define RT712_EAPD_LOW 0x0 +/* SDW clock root frequency */ +#define RT712_SDW_ROOT_CLK 0x004d +#define RT712_SDW_SCALE_CLK0 0x0062 +#define RT712_SDW_SCALE_CLK1 0x0072 + +/* PLL2 config */ +#define RT712_PLL2_CONF1 0x2f3a +#define RT712_PLL2_CONF2 0x2f3d +#define RT712_PLL2_CONF3 0x2f41 + /* RC Calibration register */ #define RT712_RC_CAL 0x3201 @@ -254,6 +264,14 @@ enum rt712_sdca_version { RT712_VB, }; +enum { + RT712_CLK_FREQ_19_2_MHZ = 1, + RT712_CLK_FREQ_24MHZ = 2, + RT712_CLK_FREQ_24_576MHZ = 3, + RT712_CLK_FREQ_22_5792MHZ = 4, +}; + + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); int rt712_sdca_init(struct device *dev, struct regmap *regmap, struct regmap *mbq_regmap, struct sdw_slave *slave); diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index eae7d662efae..910583162d3e 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -70,6 +70,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x0310100: case 0x2000000 ... 0x2000003: case 0x2000013: + case 0x2000026: case 0x200002c: case 0x200003c: case 0x2000046: @@ -142,6 +143,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x200000d: case 0x2000019: case 0x2000020: + case 0x2000026: case 0x200002c: case 0x2000030: case 0x2000046: @@ -155,6 +157,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x5810039: case 0x5b10018: case 0x5b10019: + case 0x6100006: return true; default: return false; diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index 159c35d19dba..738644018fb9 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -21,6 +21,7 @@ #include <linux/soundwire/sdw_registers.h> #include <linux/slab.h> #include <sound/soc-dapm.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt721-sdca.h" @@ -206,6 +207,7 @@ static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721) regmap_write(rt721->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00); + regmap_write(rt721->regmap, 0x2f5d, 0x1); } static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721) @@ -1268,11 +1270,10 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; - int retval, port, num_channels; + int retval, port; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -1291,7 +1292,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, * RT721_AIF3 with port = 6 for digital-mic capture */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; if (dai->id == RT721_AIF1) port = 1; else if (dai->id == RT721_AIF2) @@ -1299,7 +1299,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } else { - direction = SDW_DATA_DIR_TX; if (dai->id == RT721_AIF1) port = 2; else if (dai->id == RT721_AIF3) @@ -1307,13 +1306,9 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = port; retval = sdw_stream_add_slave(rt721->slave, &stream_config, @@ -1502,6 +1497,15 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap, &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai)); } +static void rt721_sdca_reset(struct rt721_sdca_priv *rt721) +{ + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_VENDOR_REG, + RT721_VD_HIDDEN_CTRL, RT721_HIDDEN_REG_SW_RESET, + RT721_HIDDEN_REG_SW_RESET); + rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT, + RT721_HDA_LEGACY_RESET_CTL, 0x1, 0x1); +} + int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); @@ -1535,9 +1539,17 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) } pm_runtime_get_noresume(&slave->dev); + + if (!rt721->first_hw_init) + rt721_sdca_reset(rt721); + rt721_sdca_dmic_preset(rt721); rt721_sdca_amp_preset(rt721); rt721_sdca_jack_preset(rt721); + + if (rt721->hs_jack && (!rt721->first_hw_init)) + rt721_sdca_jack_init(rt721); + if (rt721->first_hw_init) { regcache_cache_bypass(rt721->regmap, false); regcache_mark_dirty(rt721->regmap); diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 4cbe9e909585..149cb6617126 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -24,6 +24,7 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc-dapm.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt722-sdca.h" @@ -1442,11 +1443,10 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; - int retval, port, num_channels; + int retval, port; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -1465,7 +1465,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, * RT722_AIF3 with port = 6 for digital-mic capture */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; if (dai->id == RT722_AIF1) port = 1; else if (dai->id == RT722_AIF2) @@ -1473,7 +1472,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } else { - direction = SDW_DATA_DIR_TX; if (dai->id == RT722_AIF1) port = 2; else if (dai->id == RT722_AIF3) @@ -1481,13 +1479,9 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = port; retval = sdw_stream_add_slave(rt722->slave, &stream_config, diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c index 64d763b96a06..5a1c1e10e6d7 100644 --- a/sound/soc/codecs/rt766-sdca.c +++ b/sound/soc/codecs/rt766-sdca.c @@ -936,9 +936,8 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; unsigned int sampling_rate; int retval, port; @@ -957,7 +956,6 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; if (dai->id == RT766_AIF1) port = 3; else if (dai->id == RT766_AIF2) @@ -965,7 +963,6 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream, else return -EINVAL; } else { - direction = SDW_DATA_DIR_TX; if (dai->id == RT766_AIF1) port = 12; else if (dai->id == RT766_AIF3) diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index ca53b08c0b33..07c040355c37 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -371,7 +371,7 @@ static unsigned int *simple_amp_alloc_tlv_ranges(const struct simple_amp_ranges unsigned int *t; unsigned int i; - tlv = kzalloc_objs(*tlv, 2 + ranges->nb_ranges * 6, GFP_KERNEL); + tlv = kzalloc_objs(*tlv, 2 + ranges->nb_ranges * 6); if (!tlv) return NULL; diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index eaebb0ebbe0d..7fcf8214bf34 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1001,6 +1001,31 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* + * The two mono amps each render one channel of the stereo stream: + * snd_sdw_params_to_config() hands every codec the full mask for + * playback, which leaves the pair in mirror mode and one channel + * unreproduced. Claim a single channel instead, keyed off the + * machine-assigned component prefix rather than the SoundWire + * address, which is board-specific: soc_sdw_ti_amp.c names the amps + * tas2783-1..4. + * + * Which side an amp then renders does not follow from the bit that + * is set - sdw_compute_slave_ports() advances the payload offset by + * the popcount of ch_mask and never looks at which bit it is - but + * from the amp's position in the codec order of the DAI link, which + * on these boards matches the prefix numbering. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + params_channels(params) == 2 && component->name_prefix) { + const char *idx_str = strrchr(component->name_prefix, '-'); + unsigned long idx; + + if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx) + port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1); + } + /* port 1 for playback */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) port_config.num = 1; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 90c24c4b318e..b8f3035af3ba 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -775,9 +775,12 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, s++; } + adsp_dbg(dsp, "Try '%s'\n", fw->filename); ret = wm_adsp_firmware_request(&fw->firmware, fw->filename, cs_dsp->dev); if (ret < 0) { - adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); + if (ret != -ENOENT) + adsp_dbg(dsp, "Failed to request '%s': %d\n", fw->filename, ret); + kfree(fw->filename); fw->filename = NULL; if (ret != -ENOENT) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 60ac8eabab9d..5d8f0f76ab46 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -953,12 +953,17 @@ static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int s /* Get root clock */ clk = micfil->mclk; - /* Disable clock first, for it was enabled by pm_runtime */ + /* Reparent root clock to the PLL matching this sample rate */ fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk, micfil->pll11k_clk, ratio); - ret = clk_prepare_enable(clk); - if (ret) - return ret; + + /* Enable only once; hw_params can be called multiple times */ + if (!micfil->mclk_flag) { + ret = clk_prepare_enable(clk); + if (ret) + return ret; + micfil->mclk_flag = true; + } return 0; } @@ -991,8 +996,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - micfil->mclk_flag = true; - /* floor(K * CLKDIV) */ switch (micfil->quality) { case QUALITY_HIGH: @@ -1068,8 +1071,10 @@ static int fsl_micfil_hw_free(struct snd_pcm_substream *substream, { struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); - clk_disable_unprepare(micfil->mclk); - micfil->mclk_flag = false; + if (micfil->mclk_flag) { + clk_disable_unprepare(micfil->mclk); + micfil->mclk_flag = false; + } return 0; } diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 42019daa5e04..5f3423129b13 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -168,7 +168,7 @@ int simple_util_parse_tdm_width_map(struct simple_util_priv *priv, struct device if (!dai->tdm_width_map) return simple_ret(priv, ret); /* see NOTE */ - u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), GFP_KERNEL); + u32 *array_values __free(kfree) = kzalloc_objs(*array_values, n); if (!array_values) goto end; diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 44bb11c69490..1a53c993b57f 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -167,6 +167,7 @@ static const struct pci_device_id intel_sst_ids[] = { { PCI_DEVICE_DATA(INTEL, SST_TNG, 0) }, { 0, } }; +MODULE_DEVICE_TABLE(pci, intel_sst_ids); static struct pci_driver sst_driver = { .name = SST_DRV_NAME, diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index 03cfd91202d3..2e15a293298d 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -15,6 +15,22 @@ #include "../../../codecs/hda.h" #include "../utils.h" +static int avs_link_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + const struct snd_soc_pcm_stream *stream_info; + struct snd_soc_dai *codec_dai; + + codec_dai = snd_soc_rtd_to_codec(rtd, 0); + stream_info = snd_soc_dai_get_pcm_stream(codec_dai, substream->stream); + + return snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, stream_info->sig_bits); +} + +static const struct snd_soc_ops avs_link_ops = { + .startup = avs_link_startup, +}; + static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count, struct snd_soc_dai_link **links) { @@ -43,6 +59,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int dl[i].platforms = platform; dl[i].num_platforms = 1; dl[i].ignore_pmdown_time = 1; + dl[i].ops = &avs_link_ops; dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 2afe59646896..611ae9f034d4 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -92,16 +92,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) { unsigned int cp_streams, pb_streams; unsigned int gcap; + int ret; gcap = snd_hdac_chip_readw(bus, GCAP); cp_streams = (gcap >> 8) & 0x0F; pb_streams = (gcap >> 12) & 0x0F; bus->num_streams = cp_streams + pb_streams; - snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); - snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + ret = snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + ret = snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto err; + + ret = snd_hdac_bus_alloc_stream_pages(bus); + if (ret) + goto err; - return snd_hdac_bus_alloc_stream_pages(bus); + return 0; +err: + snd_hdac_ext_stream_free_all(bus); + return ret; } static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) @@ -383,6 +395,18 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct struct device *dev = &pci->dev; int ret; + ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return -ENOMEM; + + adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!adev->modcfg_buf) + return -ENOMEM; + + ret = avs_ipc_init(ipc, dev); + if (ret < 0) + return ret; + ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, &soc_hda_ext_bus_ops); if (ret < 0) return ret; @@ -394,17 +418,6 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct bus->mixer_assigned = -1; mutex_init(&bus->prepare_mutex); - ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); - if (!ipc) - return -ENOMEM; - ret = avs_ipc_init(ipc, dev); - if (ret < 0) - return ret; - - adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL); - if (!adev->modcfg_buf) - return -ENOMEM; - adev->dev = dev; adev->spec = (const struct avs_spec *)id->driver_data; adev->ipc = ipc; @@ -456,13 +469,14 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) ret = pcim_request_all_regions(pci, "AVS HDAudio"); if (ret < 0) - return ret; + goto err_request_regions; bus->addr = pci_resource_start(pci, 0); bus->remap_addr = pci_ioremap_bar(pci, 0); if (!bus->remap_addr) { dev_err(bus->dev, "ioremap error\n"); - return -ENXIO; + ret = -ENXIO; + goto err_request_regions; } adev->dsp_ba = pci_ioremap_bar(pci, 4); @@ -473,8 +487,13 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) } snd_hdac_bus_parse_capabilities(bus); - if (bus->mlcap) - snd_hdac_ext_bus_get_ml_capabilities(bus); + if (bus->mlcap) { + ret = snd_hdac_ext_bus_get_ml_capabilities(bus); + if (ret < 0) { + dev_err(dev, "failed to get ml capabilities: %d\n", ret); + goto err_ml_cap; + } + } if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); @@ -516,9 +535,13 @@ err_acquire_irq: snd_hdac_bus_free_stream_pages(bus); snd_hdac_ext_stream_free_all(bus); err_init_streams: + snd_hdac_ext_link_free_all(bus); +err_ml_cap: iounmap(adev->dsp_ba); err_remap_bar4: iounmap(bus->remap_addr); +err_request_regions: + snd_hdac_ext_bus_exit(bus); return ret; } diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 9ab503da3b75..bc02737720ab 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -9,6 +9,7 @@ #include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/kfifo.h> +#include <linux/module.h> #include <linux/wait.h> #include <linux/sched/signal.h> #include <linux/string_helpers.h> @@ -236,15 +237,20 @@ static int strace_open(struct inode *inode, struct file *file) if (!try_module_get(adev->dev->driver->owner)) return -ENODEV; - if (kfifo_initialized(&adev->trace_fifo)) - return -EBUSY; + if (kfifo_initialized(&adev->trace_fifo)) { + ret = -EBUSY; + goto err; + } ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); if (ret < 0) - return ret; + goto err; file->private_data = adev; return 0; +err: + module_put(adev->dev->driver->owner); + return ret; } static int strace_release(struct inode *inode, struct file *file) diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 39b0de9831da..5010b0f5be47 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -172,7 +172,7 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg /* Avoid deadlock as the exception may be the response to SET_D0IX. */ if (current_work() != &ipc->d0ix_work.work) - cancel_delayed_work_sync(&ipc->d0ix_work); + cancel_delayed_work(&ipc->d0ix_work); ipc->in_d0ix = false; /* Re-enabled on recovery completion. */ pm_runtime_disable(adev->dev); @@ -395,11 +395,11 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request struct avs_ipc *ipc = adev->ipc; int ret; + guard(mutex)(&ipc->msg_mutex); + if (!ipc->ready) return -EPERM; - guard(mutex)(&ipc->msg_mutex); - spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, reply); avs_dsp_send_tx(adev, request, true); diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 213d6ecdd7cc..a8a2b3484338 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -836,15 +836,10 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod) { - struct avs_soc_component *acomp; - - acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp); - - u32 num_ids = mod->template->num_config_ids; - u32 *ids = mod->template->config_ids; + struct avs_tplg_module *template = mod->template; - for (int i = 0; i < num_ids; i++) { - struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]]; + for (int i = 0; i < template->num_init_configs; i++) { + struct avs_tplg_init_config *config = template->init_configs[i]; size_t len = config->length; void *data = config->data; u32 param = config->param; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 2b886fae8209..ad25bd355769 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <sound/hda_register.h> @@ -987,13 +988,25 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) return ret; } +static int avs_request_topology(struct snd_soc_component *component, const char *name, + const struct firmware **fw) +{ + char *fullname __free(kfree) = NULL; + + fullname = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, name); + if (!fullname) + return -ENOMEM; + + return request_firmware(fw, fullname, component->dev); +} + static int avs_component_probe(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; struct snd_soc_acpi_mach *mach; struct avs_soc_component *acomp; + const struct firmware *fw; struct avs_dev *adev; - char *filename; int ret; dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name); @@ -1009,13 +1022,7 @@ static int avs_component_probe(struct snd_soc_component *component) goto finalize; /* Load specified topology and create debugfs for it. */ - filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, - mach->tplg_filename); - if (!filename) - return -ENOMEM; - - ret = avs_load_topology(component, filename); - kfree(filename); + ret = avs_request_topology(component, mach->tplg_filename, &fw); if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) { unsigned int vendor_id; @@ -1030,18 +1037,17 @@ static int avs_component_probe(struct snd_soc_component *component) "hda-generic-tplg.bin"); if (!mach->tplg_filename) return -ENOMEM; - filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, - mach->tplg_filename); - if (!filename) - return -ENOMEM; dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename); - ret = avs_load_topology(component, filename); - kfree(filename); + ret = avs_request_topology(component, mach->tplg_filename, &fw); } if (ret < 0) return ret; + ret = snd_soc_tplg_component_load(component, &avs_tplg_ops, fw); + if (ret) + return ret; + ret = avs_component_load_libraries(acomp); if (ret < 0) { dev_err(card->dev, "libraries loading failed: %d\n", ret); diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 673ac31f2fea..5d70be63a4a7 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -350,6 +350,7 @@ AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base); AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext); AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs); AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings); +AVS_DEFINE_PTR_PARSER(init_config, struct avs_tplg_init_config, init_configs); AVS_DEFINE_PTR_PARSER(nhlt_config, struct avs_tplg_nhlt_config, nhlt_configs); static int @@ -1198,7 +1199,7 @@ static const struct avs_tplg_token_parser module_parsers[] = { { .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32, .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, - .offset = offsetof(struct avs_tplg_module, num_config_ids), + .offset = offsetof(struct avs_tplg_module, num_init_configs), .parse = avs_parse_byte_token, }, { @@ -1214,10 +1215,32 @@ static const struct avs_tplg_token_parser init_config_parsers[] = { .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, .offset = 0, - .parse = avs_parse_word_token, + .parse = avs_parse_init_config_ptr, }, }; +static int avs_tplg_module_init_configs(struct snd_soc_component *comp, + struct avs_tplg_module *module, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size) +{ + struct avs_tplg_init_config **cfgs; + int ret; + + if (!module->num_init_configs) + return -EINVAL; + + cfgs = devm_kcalloc(comp->card->dev, module->num_init_configs, sizeof(*cfgs), GFP_KERNEL); + if (!cfgs) + return -ENOMEM; + + ret = parse_dictionary_entries(comp, tuples, block_size, cfgs, module->num_init_configs, + sizeof(*cfgs), AVS_TKN_MOD_INIT_CONFIG_ID_U32, + init_config_parsers, ARRAY_SIZE(init_config_parsers)); + if (!ret) + module->init_configs = cfgs; + return ret; +} + static struct avs_tplg_module * avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner, struct snd_soc_tplg_vendor_array *tuples, u32 block_size) @@ -1244,27 +1267,11 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline block_size -= esize; /* Parse trailing config ids if any. */ if (block_size) { - u32 num_config_ids = module->num_config_ids; - u32 *config_ids; - - if (!num_config_ids) - return ERR_PTR(-EINVAL); - - config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids), - GFP_KERNEL); - if (!config_ids) - return ERR_PTR(-ENOMEM); - tuples = avs_tplg_vendor_array_at(tuples, esize); - ret = parse_dictionary_entries(comp, tuples, block_size, - config_ids, num_config_ids, sizeof(*config_ids), - AVS_TKN_MOD_INIT_CONFIG_ID_U32, - init_config_parsers, - ARRAY_SIZE(init_config_parsers)); + + ret = avs_tplg_module_init_configs(comp, module, tuples, block_size); if (ret) return ERR_PTR(ret); - - module->config_ids = config_ids; } module->owner = owner; @@ -2194,7 +2201,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ return 0; } -static const struct snd_soc_tplg_ops avs_tplg_ops = { +const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count = ARRAY_SIZE(avs_control_ops), .control_load = avs_control_load, diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 1cf7455b6c01..189984ce7b51 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -221,8 +221,8 @@ struct avs_tplg_module { u8 domain; struct avs_tplg_modcfg_ext *cfg_ext; u32 ctl_id; - u32 num_config_ids; - u32 *config_ids; + u32 num_init_configs; + struct avs_tplg_init_config **init_configs; struct avs_tplg_nhlt_config *nhlt_config; struct avs_tplg_pipeline *owner; @@ -230,6 +230,7 @@ struct avs_tplg_module { struct list_head node; }; +extern const struct snd_soc_tplg_ops avs_tplg_ops; struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp); int avs_load_topology(struct snd_soc_component *comp, const char *filename); diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cddbd2aa424e..b795bcade8e9 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -533,12 +533,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_CS35L56_SPI select SND_SOC_CS35L56_SDW select SND_SOC_ES9356 + imply SND_SOC_TAC5XX2_SDW select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON imply SND_SOC_SDW_MOCKUP help Add support for Intel SoundWire-based platforms connected to - MAX98373, RT700, RT711, RT1308 and RT715 + MAX98373, RT700, RT711, RT1308, RT715, TAC5XX2_SDW family (including + TAC5572, TAC5682). If unsure select "N". endif diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 9b016136c639..f1e62c2e79fe 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -360,6 +360,15 @@ static const struct dmi_system_id sof_es8336_quirk_table[] = { .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | SOC_ES8336_HEADSET_MIC1) }, + { + .callback = sof_es8336_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), + DMI_MATCH(DMI_PRODUCT_NAME, "NDZ-WXX9"), + }, + .driver_data = (void *)(SOF_ES8336_HEADPHONE_GPIO | + SOC_ES8336_HEADSET_MIC1) + }, {} }; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 7899f7ffd99b..88cf5c0ab2e3 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -909,6 +909,14 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_PRESENT), }, { + .name = "nvl_rt5682_def", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_SSP_PORT_CODEC(0) | + SOF_SSP_PORT_AMP(1) | + SOF_SSP_PORT_BT_OFFLOAD(2) | + SOF_BT_OFFLOAD_PRESENT), + }, + { .name = "ptl_rt5682_c1_h02", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_SSP_PORT_CODEC(1) | diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 8f6e987c791e..4a67f6b72fa5 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -51,10 +51,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { }, { .comp_ids = &nvl_rt5682_rt5682s_hp, - .drv_name = "sof_rt5682", - .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */ - .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | - SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, + .drv_name = "nvl_rt5682_def", + .sof_tplg_filename = "sof-nvl", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | + SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, }, /* place amp/hdmi-in only boards in the end of table */ { diff --git a/sound/soc/meson/gx-formatter.c b/sound/soc/meson/gx-formatter.c index 311e63affb23..2d3218cce426 100644 --- a/sound/soc/meson/gx-formatter.c +++ b/sound/soc/meson/gx-formatter.c @@ -253,7 +253,7 @@ struct gx_stream *gx_stream_alloc(struct gx_iface *iface) { struct gx_stream *ts; - ts = kzalloc(sizeof(*ts), GFP_KERNEL); + ts = kzalloc_obj(*ts); if (ts) { INIT_LIST_HEAD(&ts->formatter_list); mutex_init(&ts->lock); diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 1d68a80e8e0c..f0ff1350e9dd 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -1857,7 +1857,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) return ERR_PTR(-EINVAL); } - port = kzalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc_obj(*port, GFP_KERNEL); if (!port) return ERR_PTR(-ENOMEM); diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e01d91eb3cc8..32d9b7f30a4f 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1249,8 +1249,7 @@ static int find_sdca_entity_pde(struct device *dev, return -EINVAL; } - u32 *delay_list __free(kfree) = kcalloc(num_delays, sizeof(*delay_list), - GFP_KERNEL); + u32 *delay_list __free(kfree) = kzalloc_objs(*delay_list, num_delays); if (!delay_list) return -ENOMEM; @@ -1313,8 +1312,8 @@ static int find_sdca_entity_ge(struct device *dev, return -EINVAL; } - u8 *affected_list __free(kfree) = kcalloc(num_affected, sizeof(*affected_list), - GFP_KERNEL); + u8 *affected_list __free(kfree) = kzalloc_objs(*affected_list, + num_affected); if (!affected_list) return -ENOMEM; @@ -1552,8 +1551,8 @@ static int find_sdca_entities(struct device *dev, struct fwnode_handle *function if (!entities) return -ENOMEM; - u32 *entity_list __free(kfree) = kcalloc(num_entities, sizeof(*entity_list), - GFP_KERNEL); + u32 *entity_list __free(kfree) = kzalloc_objs(*entity_list, + num_entities); if (!entity_list) return -ENOMEM; @@ -1715,8 +1714,8 @@ static int find_sdca_entity_connection_pde(struct device *dev, if (!managed) return -ENOMEM; - u32 *managed_list __free(kfree) = kcalloc(num_managed, sizeof(*managed_list), - GFP_KERNEL); + u32 *managed_list __free(kfree) = kzalloc_objs(*managed_list, + num_managed); if (!managed_list) return -ENOMEM; @@ -2033,8 +2032,8 @@ static int find_sdca_clusters(struct device *dev, if (!clusters) return -ENOMEM; - u32 *cluster_list __free(kfree) = kcalloc(num_clusters, sizeof(*cluster_list), - GFP_KERNEL); + u32 *cluster_list __free(kfree) = kzalloc_objs(*cluster_list, + num_clusters); if (!cluster_list) return -ENOMEM; diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c index 325ab7230481..6e21ef8f87e2 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c @@ -14,7 +14,6 @@ #include <sound/soc-dai.h> #include <sound/soc_sdw_utils.h> -#define CS_AMP_CHANNELS_PER_AMP 4 #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ int asoc_sdw_cs35l56_volume_limit(struct snd_soc_card *card, const char *name_prefix) @@ -64,51 +63,6 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai } EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS"); -int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) -{ - const struct snd_soc_dai_link *dai_link = rtd->dai_link; - const struct snd_soc_dai_link_ch_map *ch_map; - const struct snd_soc_dai_link_component *codec_dlc; - struct snd_soc_dai *codec_dai; - u8 ch_slot[8] = {}; - unsigned int amps_per_bus, ch_per_amp, mask; - int i, ret; - - WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot)); - - /* - * CS35L56 has 4 TX channels. When the capture is aggregated the - * same bus slots will be allocated to all the amps on a bus. Only - * one amp on that bus can be transmitting in each slot so divide - * the available 4 slots between all the amps on a bus. - */ - amps_per_bus = dai_link->num_codecs / dai_link->num_cpus; - if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) { - dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n", - dai_link->num_codecs, dai_link->num_cpus); - return -EINVAL; - } - - ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus; - - for_each_rtd_ch_maps(rtd, i, ch_map) { - codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i); - codec_dai = snd_soc_find_dai(codec_dlc); - mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu]; - - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret); - return ret; - } - - ch_slot[ch_map->cpu] += ch_per_amp; - } - - return 0; -} -EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS"); - int asoc_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct asoc_sdw_codec_info *info, diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index a66dcc02fb59..d2eeef4931c6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -811,7 +811,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -840,7 +839,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -869,7 +867,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -898,7 +895,6 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_name = "cs35l56-sdw1c", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, - .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, }, }, .dai_num = 2, @@ -1565,7 +1561,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai_link_ch_map *ch_maps; int ch = params_channels(params); - unsigned int ch_mask; + unsigned int cpu_ch_mask, codec_ch_mask; int num_codecs; int step; int i; @@ -1575,8 +1571,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, /* Identical data will be sent to all codecs in playback */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ch_mask = GENMASK(ch - 1, 0); + cpu_ch_mask = GENMASK(ch - 1, 0); step = 0; + codec_ch_mask = 0; } else { num_codecs = rtd->dai_link->num_codecs; @@ -1586,17 +1583,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ch_mask = GENMASK(ch / num_codecs - 1, 0); - step = hweight_long(ch_mask); + cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0); + step = hweight_long(cpu_ch_mask); + codec_ch_mask = cpu_ch_mask; } /* * The captured data will be combined from each cpu DAI if the dai * link has more than one codec DAIs. Set codec channel mask and * ASoC will set the corresponding channel numbers for each cpu dai. + * + * sdw_stream_add_slave() assigns different payload offsets to each + * codec in a capture stream, so that the same channels on each + * codec map to different channels on the CPU. */ - for_each_link_ch_maps(rtd->dai_link, i, ch_maps) - ch_maps->ch_mask = ch_mask << (i * step); + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { + ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step); + ch_maps->codec_ch_mask = codec_ch_mask; + } return 0; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0e49290a8c90..3137c091bdb8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1206,7 +1206,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, goto out; for_each_rtd_codec_dais(rtd, i, codec_dai) { - unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); + unsigned int ch_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); + struct snd_soc_dai_link_ch_map *ch_maps; + int j; /* * Skip CODECs which don't support the current stream type, @@ -1228,9 +1230,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, /* copy params for each codec */ tmp_params = *params; - /* fixup params based on TDM slot masks */ - if (tdm_mask) - soc_pcm_codec_params_fixup(&tmp_params, tdm_mask); + /* fixup params based on TDM or ch_map masks */ + if (!ch_mask) { + for_each_rtd_ch_maps(rtd, j, ch_maps) + if (ch_maps->codec == i) + ch_mask |= ch_maps->codec_ch_mask; + } + + if (ch_mask) + soc_pcm_codec_params_fixup(&tmp_params, ch_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, &tmp_params); @@ -1264,7 +1272,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, */ for_each_rtd_ch_maps(rtd, j, ch_maps) if (ch_maps->cpu == i) - ch_mask |= ch_maps->ch_mask; + ch_mask |= ch_maps->cpu_ch_mask; /* fixup cpu channel number */ if (ch_mask) diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c index 2eef32b55395..c547ea61fb3d 100644 --- a/sound/soc/sof/sof-client-probes-ipc4.c +++ b/sound/soc/sof/sof-client-probes-ipc4.c @@ -260,7 +260,7 @@ static int ipc4_probes_points_info(struct sof_client_dev *cdev, *num_desc = info->num_elems; dev_dbg(dev, "%s: got %zu probe points", __func__, *num_desc); - *desc = kcalloc(*num_desc, sizeof(**desc), GFP_KERNEL); + *desc = kzalloc_objs(**desc, *num_desc); if (!*desc) { kfree(msg.data_ptr); return -ENOMEM; diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index c7bbf09e547f..64da8df15bf2 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -230,7 +230,7 @@ int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, struct sof_client_dev *cdev; int ret; - centry = kzalloc(sizeof(*centry), GFP_KERNEL); + centry = kzalloc_obj(*centry); if (!centry) return -ENOMEM; diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c index a7d437b49fbf..e5249924b54d 100644 --- a/sound/soc/sprd/sprd-pcm-compress.c +++ b/sound/soc/sprd/sprd-pcm-compress.c @@ -17,7 +17,7 @@ /* Default values if userspace does not set */ #define SPRD_COMPR_MIN_FRAGMENT_SIZE SZ_8K -#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_128K +#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_32K #define SPRD_COMPR_MIN_NUM_FRAGMENTS 4 #define SPRD_COMPR_MAX_NUM_FRAGMENTS 64 @@ -272,6 +272,19 @@ static int sprd_platform_compr_set_params(struct snd_soc_component *component, int ret; /* + * The stage 0 IRAM buffer and the stage 1 DDR buffer are allocated + * with fixed sizes at open time, so the requested fragment size and + * fragments must fit into them, otherwise sprd_platform_compr_copy() + * would overflow the buffers. Note the compress core only checks the + * fragment size and fragments against an u32 overflow, not against + * the buffer sizes advertised by get_caps. + */ + if (params->buffer.fragment_size > SPRD_COMPR_IRAM_BUF_SIZE || + (u64)params->buffer.fragment_size * params->buffer.fragments > + SPRD_COMPR_AREA_BUF_SIZE) + return -EINVAL; + + /* * Configure the DMA engine 2-stage transfer mode. Channel 1 set as the * destination channel, and channel 0 set as the source channel, that * means once the source channel's transaction is done, it will trigger diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index 45d7613f595c..5347f9620f25 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -416,6 +416,8 @@ int uni_reader_init(struct platform_device *pdev, else reader->hw = &uni_reader_pcm_hw; + spin_lock_init(&reader->irq_lock); + ret = devm_request_irq(&pdev->dev, reader->irq, uni_reader_irq_handler, IRQF_SHARED, dev_name(&pdev->dev), reader); @@ -424,8 +426,6 @@ int uni_reader_init(struct platform_device *pdev, return -EBUSY; } - spin_lock_init(&reader->irq_lock); - return 0; } EXPORT_SYMBOL_GPL(uni_reader_init); diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 499e826d7120..37c48cc70394 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -14,7 +14,7 @@ #include <linux/clk.h> #include <linux/of.h> #include <linux/regulator/consumer.h> -#include <linux/mfd/db8500-prcmu.h> +#include <linux/reset.h> #include <sound/soc.h> #include <sound/soc-dai.h> @@ -34,8 +34,10 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai, if (drvdata->slots > 1) { msp_config->multichannel_configured = 1; - multi->tx_multichannel_enable = true; - multi->rx_multichannel_enable = true; + multi->tx_multichannel_enable = + msp_config->direction & MSP_DIR_TX; + multi->rx_multichannel_enable = + msp_config->direction & MSP_DIR_RX; multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; multi->tx_channel_0_enable = drvdata->tx_mask; @@ -57,72 +59,21 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai, return 0; } -static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, - struct msp_protdesc *prot_desc) +static void setup_frameper(struct snd_soc_dai *dai, + struct msp_protdesc *prot_desc) { struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); - switch (drvdata->slots) { - case 1: - switch (rate) { - case 8000: - prot_desc->frame_period = - FRAME_PER_SINGLE_SLOT_8_KHZ; - break; - - case 16000: - prot_desc->frame_period = - FRAME_PER_SINGLE_SLOT_16_KHZ; - break; - - case 44100: - prot_desc->frame_period = - FRAME_PER_SINGLE_SLOT_44_1_KHZ; - break; - - case 48000: - prot_desc->frame_period = - FRAME_PER_SINGLE_SLOT_48_KHZ; - break; - - default: - dev_err(dai->dev, - "%s: Error: Unsupported sample-rate (freq = %d)!\n", - __func__, rate); - return -EINVAL; - } - break; - - case 2: - prot_desc->frame_period = FRAME_PER_2_SLOTS; - break; - - case 8: - prot_desc->frame_period = FRAME_PER_8_SLOTS; - break; - - case 16: - prot_desc->frame_period = FRAME_PER_16_SLOTS; - break; - default: - dev_err(dai->dev, - "%s: Error: Unsupported slot-count (slots = %d)!\n", - __func__, drvdata->slots); - return -EINVAL; - } - - prot_desc->clocks_per_frame = - prot_desc->frame_period+1; + prot_desc->clocks_per_frame = drvdata->slots * drvdata->slot_width; + prot_desc->frame_period = prot_desc->clocks_per_frame - 1; dev_dbg(dai->dev, "%s: Clocks per frame: %u\n", __func__, prot_desc->clocks_per_frame); - - return 0; } -static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, - struct msp_protdesc *prot_desc) +static int setup_pcm_framing(struct snd_soc_dai *dai, + struct msp_protdesc *prot_desc) { struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); @@ -163,7 +114,9 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; - return setup_frameper(dai, rate, prot_desc); + setup_frameper(dai, prot_desc); + + return 0; } static int setup_clocking(struct snd_soc_dai *dai, @@ -177,7 +130,16 @@ static int setup_clocking(struct snd_soc_dai *dai, case SND_SOC_DAIFMT_NB_IF: msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; + break; + + case SND_SOC_DAIFMT_IB_NF: + msp_config->bclk_inverted = true; + break; + case SND_SOC_DAIFMT_IB_IF: + msp_config->bclk_inverted = true; + msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; + msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; break; default: @@ -192,6 +154,7 @@ static int setup_clocking(struct snd_soc_dai *dai, case SND_SOC_DAIFMT_BC_FC: dev_dbg(dai->dev, "%s: Codec is master.\n", __func__); + msp_config->clock_provider = false; msp_config->iodelay = 0x20; msp_config->rx_fsync_sel = 0; msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; @@ -204,6 +167,7 @@ static int setup_clocking(struct snd_soc_dai *dai, case SND_SOC_DAIFMT_BP_FP: dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__); + msp_config->clock_provider = true; msp_config->tx_clk_sel = TX_CLK_SEL_SRG; msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; msp_config->rx_clk_sel = RX_CLK_SEL_SRG; @@ -362,7 +326,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = setup_pcm_framing(dai, runtime->rate, prot_desc); + ret = setup_pcm_framing(dai, prot_desc); if (ret < 0) return ret; @@ -424,21 +388,21 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, int ret; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + unsigned int configured = is_playback ? PLAYBACK_CONFIGURED : + CAPTURE_CONFIGURED; + unsigned int dir = is_playback ? MSP_DIR_TX : MSP_DIR_RX; dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); - if (drvdata->vape_opp_constraint == 1) { - prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, - "ux500_msp_i2s", 50); - drvdata->vape_opp_constraint = 0; - } - - if (ux500_msp_i2s_close(drvdata->msp, - is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { - dev_err(dai->dev, - "%s: Error: MSP %d (%s): Unable to close i2s.\n", - __func__, dai->id, snd_pcm_stream_str(substream)); + if (drvdata->configured & configured) { + if (ux500_msp_i2s_close(drvdata->msp, dir)) { + dev_err(dai->dev, + "%s: Error: MSP %d (%s): Unable to close i2s.\n", + __func__, dai->id, + snd_pcm_stream_str(substream)); + } + drvdata->configured &= ~configured; } /* Disable and unprepare clocks */ @@ -456,15 +420,23 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret = 0; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); struct snd_pcm_runtime *runtime = substream->runtime; struct ux500_msp_config msp_config; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int configured = is_playback ? PLAYBACK_CONFIGURED : + CAPTURE_CONFIGURED; + int ret; dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, dai->id, snd_pcm_stream_str(substream), runtime->rate); - setup_msp_config(substream, dai, &msp_config); + if (drvdata->configured & configured) + return 0; + + ret = setup_msp_config(substream, dai, &msp_config); + if (ret) + return ret; ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); if (ret < 0) { @@ -473,22 +445,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, return ret; } - /* Set OPP-level */ - if ((drvdata->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) && - (drvdata->msp->f_bitclk > 19200000)) { - /* If the bit-clock is higher than 19.2MHz, Vape should be - * run in 100% OPP. Only when bit-clock is used (MSP master) - */ - prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, - "ux500-msp-i2s", 100); - drvdata->vape_opp_constraint = 1; - } else { - prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, - "ux500-msp-i2s", 50); - drvdata->vape_opp_constraint = 0; - } + drvdata->configured |= configured; - return ret; + return 0; } static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, @@ -496,7 +455,6 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { unsigned int mask, slots_active; - struct snd_pcm_runtime *runtime = substream->runtime; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", @@ -504,9 +462,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 1, 2); + if (params_channels(params) < 1 || params_channels(params) > 2) + return -EINVAL; break; case SND_SOC_DAIFMT_DSP_B: @@ -518,9 +475,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, slots_active = hweight32(mask); dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); - snd_pcm_hw_constraint_single(runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - slots_active); + if (!slots_active || params_channels(params) != slots_active) + return -EINVAL; break; default: @@ -553,20 +509,21 @@ static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, default: dev_err(dai->dev, "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n", - __func__, drvdata->fmt); + __func__, fmt); return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_NF: case SND_SOC_DAIFMT_IB_IF: break; default: dev_err(dai->dev, "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", - __func__, drvdata->fmt); + __func__, fmt); return -EINVAL; } @@ -600,17 +557,23 @@ static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, __func__, slots); return -EINVAL; } - drvdata->slots = slots; - if (!(slot_width == 16)) { + if (slot_width != 16) { dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n", __func__, slot_width); return -EINVAL; } - drvdata->slot_width = slot_width; - drvdata->tx_mask = tx_mask & cap; - drvdata->rx_mask = rx_mask & cap; + if ((tx_mask | rx_mask) & ~cap) { + dev_err(dai->dev, "%s: Slot mask exceeds %d slots\n", + __func__, slots); + return -EINVAL; + } + + drvdata->slots = slots; + drvdata->slot_width = slot_width; + drvdata->tx_mask = tx_mask; + drvdata->rx_mask = rx_mask; return 0; } @@ -716,6 +679,7 @@ static const struct snd_soc_component_driver ux500_msp_component = { static int ux500_msp_drv_probe(struct platform_device *pdev) { struct ux500_msp_i2s_drvdata *drvdata; + struct reset_control *reset; int ret = 0; drvdata = devm_kzalloc(&pdev->dev, @@ -729,7 +693,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) drvdata->tx_mask = 0x01; drvdata->rx_mask = 0x01; drvdata->slot_width = 16; - drvdata->master_clk = MSP_INPUT_FREQ_APB; drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape"); if (IS_ERR(drvdata->reg_vape)) { @@ -739,8 +702,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret); return ret; } - prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); - drvdata->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(drvdata->pclk)) { ret = PTR_ERR(drvdata->pclk); @@ -758,9 +719,19 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret); return ret; } + drvdata->master_clk = clk_get_rate(drvdata->clk); + if (!drvdata->master_clk) { + dev_err(&pdev->dev, "MSP clock has no rate\n"); + return -EINVAL; + } + + reset = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(reset), + "Failed to deassert MSP reset\n"); ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp); - if (!drvdata->msp) { + if (ret) { dev_err(&pdev->dev, "%s: ERROR: Failed to init MSP-struct (%d)!", __func__, ret); @@ -799,8 +770,6 @@ static void ux500_msp_drv_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); - prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); - ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); } diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h index 30bf70838196..aae582030d95 100644 --- a/sound/soc/ux500/ux500_msp_dai.h +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -22,17 +22,6 @@ #define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) -#define FRAME_PER_SINGLE_SLOT_8_KHZ 31 -#define FRAME_PER_SINGLE_SLOT_16_KHZ 124 -#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63 -#define FRAME_PER_SINGLE_SLOT_48_KHZ 49 -#define FRAME_PER_2_SLOTS 31 -#define FRAME_PER_8_SLOTS 138 -#define FRAME_PER_16_SLOTS 277 - -#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000 -#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ - #define UX500_MSP_MIN_CHANNELS 1 #define UX500_MSP_MAX_CHANNELS 8 @@ -47,6 +36,7 @@ struct ux500_msp_i2s_drvdata { struct ux500_msp *msp; struct regulator *reg_vape; unsigned int fmt; + unsigned int configured; unsigned int tx_mask; unsigned int rx_mask; int slots; @@ -57,8 +47,6 @@ struct ux500_msp_i2s_drvdata { struct clk *clk; struct clk *pclk; - /* Regulators */ - int vape_opp_constraint; }; int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay); diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index fbfeefa418ca..683b485fb570 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -201,10 +201,12 @@ static int configure_protocol(struct ux500_msp *msp, /* The code below should not be separated. */ temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; - temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol); + temp_reg |= MSP_TX_CLKPOL_BIT(!protdesc->tx_clk_pol ^ + config->bclk_inverted); writel(temp_reg, msp->registers + MSP_GCR); temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; - temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol); + temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol ^ + config->bclk_inverted); writel(temp_reg, msp->registers + MSP_GCR); return 0; @@ -212,35 +214,20 @@ static int configure_protocol(struct ux500_msp *msp, static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) { + struct msp_protdesc *protdesc; + u64 desired_bitclk; + unsigned int bitclk; u32 reg_val_GCR; - u32 frame_per = 0; - u32 sck_div = 0; - u32 frame_width = 0; - u32 temp_reg = 0; - struct msp_protdesc *protdesc = NULL; + u32 sck_div; + u32 temp_reg; reg_val_GCR = readl(msp->registers + MSP_GCR); writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR); - if (config->default_protdesc) - protdesc = - (struct msp_protdesc *)&prot_descs[config->protocol]; - else - protdesc = (struct msp_protdesc *)&config->protdesc; - switch (config->protocol) { case MSP_PCM_PROTOCOL: case MSP_PCM_COMPAND_PROTOCOL: - frame_width = protdesc->frame_width; - sck_div = config->f_inputclk / (config->frame_freq * - (protdesc->clocks_per_frame)); - frame_per = protdesc->frame_period; - break; case MSP_I2S_PROTOCOL: - frame_width = protdesc->frame_width; - sck_div = config->f_inputclk / (config->frame_freq * - (protdesc->clocks_per_frame)); - frame_per = protdesc->frame_period; break; default: dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n", @@ -249,12 +236,35 @@ static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) return -EINVAL; } + if (config->default_protdesc) + protdesc = (struct msp_protdesc *)&prot_descs[config->protocol]; + else + protdesc = &config->protdesc; + + if (!config->frame_freq || !protdesc->clocks_per_frame) + return -EINVAL; + + desired_bitclk = (u64)config->frame_freq * protdesc->clocks_per_frame; + if (desired_bitclk > config->f_inputclk) + return -EINVAL; + bitclk = desired_bitclk; + if (config->f_inputclk % bitclk) { + dev_err(msp->dev, + "Input clock %u cannot generate bit clock %u\n", + config->f_inputclk, bitclk); + return -EINVAL; + } + + sck_div = config->f_inputclk / bitclk; + if (!sck_div || sck_div > SCK_DIV_MASK + 1) + return -EINVAL; + temp_reg = (sck_div - 1) & SCK_DIV_MASK; - temp_reg |= FRAME_WIDTH_BITS(frame_width); - temp_reg |= FRAME_PERIOD_BITS(frame_per); + temp_reg |= FRAME_WIDTH_BITS(protdesc->frame_width); + temp_reg |= FRAME_PERIOD_BITS(protdesc->frame_period); writel(temp_reg, msp->registers + MSP_SRG); - msp->f_bitclk = (config->f_inputclk)/(sck_div + 1); + msp->f_bitclk = config->f_inputclk / sck_div; /* Enable bit-clock */ udelay(100); @@ -344,20 +354,27 @@ static int configure_multichannel(struct ux500_msp *msp, return 0; } -static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) +static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config, + bool first) { - int status = 0; - u32 reg_val_DMACR, reg_val_GCR; + int status; + u32 reg_val_DMACR; /* Configure msp with protocol dependent settings */ - configure_protocol(msp, config); - setup_bitclk(msp, config); + status = configure_protocol(msp, config); + if (status) + return status; + + if (first && config->clock_provider) { + status = setup_bitclk(msp, config); + if (status) + return status; + } + if (config->multichannel_configured == 1) { status = configure_multichannel(msp, config); if (status) - dev_warn(msp->dev, - "%s: WARN: configure_multichannel failed (%d)!\n", - __func__, status); + return status; } reg_val_DMACR = readl(msp->registers + MSP_DMACR); @@ -369,11 +386,7 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) writel(config->iodelay, msp->registers + MSP_IODLY); - /* Enable frame generation logic */ - reg_val_GCR = readl(msp->registers + MSP_GCR); - writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR); - - return status; + return 0; } static void flush_fifo_rx(struct ux500_msp *msp) @@ -411,12 +424,37 @@ static void flush_fifo_tx(struct ux500_msp *msp) writel(reg_val_GCR, msp->registers + MSP_GCR); } +static bool ux500_msp_config_compatible(struct ux500_msp *msp, + struct ux500_msp_config *config) +{ + struct ux500_msp_config *active = &msp->config; + + return active->f_inputclk == config->f_inputclk && + active->tx_clk_sel == config->tx_clk_sel && + active->rx_clk_sel == config->rx_clk_sel && + active->srg_clk_sel == config->srg_clk_sel && + active->rx_fsync_pol == config->rx_fsync_pol && + active->tx_fsync_pol == config->tx_fsync_pol && + active->rx_fsync_sel == config->rx_fsync_sel && + active->tx_fsync_sel == config->tx_fsync_sel && + active->default_protdesc == config->default_protdesc && + active->protocol == config->protocol && + active->frame_freq == config->frame_freq && + active->data_size == config->data_size && + active->def_elem_len == config->def_elem_len && + active->clock_provider == config->clock_provider && + active->bclk_inverted == config->bclk_inverted && + !memcmp(&active->protdesc, &config->protdesc, + sizeof(active->protdesc)); +} + int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config) { u32 old_reg, new_reg, mask; int res; unsigned int tx_sel, rx_sel, tx_busy, rx_busy; + bool first; if (in_interrupt()) { dev_err(msp->dev, @@ -444,40 +482,68 @@ int ux500_msp_i2s_open(struct ux500_msp *msp, return -EBUSY; } - msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0); - - /* First do the global config register */ - mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | - TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | - RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK | - LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; - - new_reg = (config->tx_clk_sel | config->rx_clk_sel | - config->rx_fsync_pol | config->tx_fsync_pol | - config->rx_fsync_sel | config->tx_fsync_sel | - config->rx_fifo_config | config->tx_fifo_config | - config->srg_clk_sel | config->loopback_enable | - config->tx_data_enable); + first = !msp->dir_busy; + if (!first && !ux500_msp_config_compatible(msp, config)) { + dev_err(msp->dev, "%s: Incompatible duplex configuration\n", + __func__); + return -EBUSY; + } - old_reg = readl(msp->registers + MSP_GCR); - old_reg &= ~mask; - new_reg |= old_reg; - writel(new_reg, msp->registers + MSP_GCR); + if (first) { + /* First do the global config register */ + mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | + TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | + RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | + SRG_CLK_SEL_MASK | LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; + + new_reg = config->tx_clk_sel | config->rx_clk_sel | + config->rx_fsync_pol | config->tx_fsync_pol | + config->rx_fsync_sel | config->tx_fsync_sel | + config->rx_fifo_config | config->tx_fifo_config | + config->srg_clk_sel | config->loopback_enable | + config->tx_data_enable; + + old_reg = readl(msp->registers + MSP_GCR); + old_reg &= ~mask; + new_reg |= old_reg; + writel(new_reg, msp->registers + MSP_GCR); + writel(MSP_WMRK_TX_4_ELEMENTS | MSP_WMRK_RX_4_ELEMENTS, + msp->registers + MSP_WMRK); + } - res = enable_msp(msp, config); + res = enable_msp(msp, config, first); if (res < 0) { dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n", __func__, res); - return -EBUSY; + if (tx_sel) + writel(0, msp->registers + MSP_TCF); + if (rx_sel) + writel(0, msp->registers + MSP_RCF); + if (first) { + writel(0, msp->registers + MSP_GCR); + writel(0, msp->registers + MSP_DMACR); + writel(0, msp->registers + MSP_SRG); + writel(0, msp->registers + MSP_MCR); + } + return res; + } + + msp->dir_busy |= config->direction; + if (first) { + msp->config = *config; + msp->clock_provider = config->clock_provider; } if (config->loopback_enable & 0x80) msp->loopback_enable = 1; /* Flush FIFOs */ - flush_fifo_tx(msp); - flush_fifo_rx(msp); + if (tx_sel) + flush_fifo_tx(msp); + if (rx_sel) + flush_fifo_rx(msp); - msp->msp_state = MSP_STATE_CONFIGURED; + if (!msp->dir_running) + msp->msp_state = MSP_STATE_CONFIGURED; return 0; } @@ -494,7 +560,6 @@ static void disable_msp_rx(struct ux500_msp *msp) ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT), msp->registers + MSP_IMSC); - msp->dir_busy &= ~MSP_DIR_RX; } static void disable_msp_tx(struct ux500_msp *msp) @@ -510,7 +575,6 @@ static void disable_msp_tx(struct ux500_msp *msp) ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT), msp->registers + MSP_IMSC); - msp->dir_busy &= ~MSP_DIR_TX; } static int disable_msp(struct ux500_msp *msp, unsigned int dir) @@ -520,7 +584,7 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir) reg_val_GCR = readl(msp->registers + MSP_GCR); disable_tx = dir & MSP_DIR_TX; - disable_rx = dir & MSP_DIR_TX; + disable_rx = dir & MSP_DIR_RX; if (disable_tx && disable_rx) { reg_val_GCR = readl(msp->registers + MSP_GCR); writel(reg_val_GCR | LOOPBACK_MASK, @@ -553,7 +617,15 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir) int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) { - u32 reg_val_GCR, enable_bit; + u32 reg_val_DMACR, reg_val_GCR, dma_enable_bit, enable_bit; + unsigned int dir; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dir = MSP_DIR_TX; + else if (direction == SNDRV_PCM_STREAM_CAPTURE) + dir = MSP_DIR_RX; + else + return -EINVAL; if (msp->msp_state == MSP_STATE_IDLE) { dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n", @@ -565,21 +637,44 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (direction == SNDRV_PCM_STREAM_PLAYBACK) + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { enable_bit = TX_ENABLE; - else + dma_enable_bit = TX_DMA_ENABLE; + } else { enable_bit = RX_ENABLE; + dma_enable_bit = RX_DMA_ENABLE; + } + if (!(msp->dir_busy & dir)) + return -EINVAL; + reg_val_DMACR = readl(msp->registers + MSP_DMACR); + writel(reg_val_DMACR | dma_enable_bit, + msp->registers + MSP_DMACR); reg_val_GCR = readl(msp->registers + MSP_GCR); + if (msp->clock_provider) + enable_bit |= FRAME_GEN_ENABLE; writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR); + msp->dir_running |= dir; + msp->msp_state = MSP_STATE_RUNNING; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (direction == SNDRV_PCM_STREAM_PLAYBACK) + if (!(msp->dir_busy & dir)) + return -EINVAL; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { disable_msp_tx(msp); - else + msp->dir_running &= ~MSP_DIR_TX; + } else { disable_msp_rx(msp); + msp->dir_running &= ~MSP_DIR_RX; + } + if (!msp->dir_running) { + reg_val_GCR = readl(msp->registers + MSP_GCR); + writel(reg_val_GCR & ~FRAME_GEN_ENABLE, + msp->registers + MSP_GCR); + msp->msp_state = MSP_STATE_CONFIGURED; + } break; default: return -EINVAL; @@ -594,7 +689,18 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); + if (!dir || dir & ~(MSP_DIR_TX | MSP_DIR_RX) || + (msp->dir_busy & dir) != dir) + return -EINVAL; + status = disable_msp(msp, dir); + msp->dir_busy &= ~dir; + msp->dir_running &= ~dir; + if (msp->dir_busy && !msp->dir_running) { + writel(readl(msp->registers + MSP_GCR) & ~FRAME_GEN_ENABLE, + msp->registers + MSP_GCR); + msp->msp_state = MSP_STATE_CONFIGURED; + } if (msp->dir_busy == 0) { /* disable sample rate and frame generators */ msp->msp_state = MSP_STATE_IDLE; @@ -618,6 +724,8 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) writel(0, msp->registers + MSP_RCE1); writel(0, msp->registers + MSP_RCE2); writel(0, msp->registers + MSP_RCE3); + memset(&msp->config, 0, sizeof(msp->config)); + msp->clock_provider = false; } return status; @@ -627,7 +735,7 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) int ux500_msp_i2s_init_msp(struct platform_device *pdev, struct ux500_msp **msp_p) { - struct resource *res = NULL; + struct resource *res; struct ux500_msp *msp; *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); @@ -637,20 +745,10 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, msp->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n", - __func__); - return -ENOMEM; - } - + msp->registers = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(msp->registers)) + return PTR_ERR(msp->registers); msp->tx_rx_addr = res->start + MSP_DR; - msp->registers = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (msp->registers == NULL) { - dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__); - return -ENOMEM; - } msp->msp_state = MSP_STATE_IDLE; msp->loopback_enable = 0; diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 69d4ebc409fc..c66ef455e138 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -12,8 +12,6 @@ #include <linux/platform_device.h> -#define MSP_INPUT_FREQ_APB 48000000 - /*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), * 32 bits accesses (stereo). ***/ @@ -64,6 +62,7 @@ enum msp_direction { #define MSP_SRG 0x10 #define MSP_FLR 0x14 #define MSP_DMACR 0x18 +#define MSP_WMRK 0x1c #define MSP_IMSC 0x20 #define MSP_RIS 0x24 @@ -148,8 +147,8 @@ enum msp_direction { #define RCKPOL_MASK BIT(0) #define TCKPOL_MASK BIT(0) #define SPICKM_MASK (BIT(1) | BIT(0)) -#define MSP_RX_CLKPOL_BIT(n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) -#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) +#define MSP_RX_CLKPOL_BIT(n) (((n) & RCKPOL_MASK) << RCKPOL_SHIFT) +#define MSP_TX_CLKPOL_BIT(n) (((n) & TCKPOL_MASK) << TCKPOL_SHIFT) #define P1ELEN_SHIFT 0 #define P1FLEN_SHIFT 3 @@ -230,6 +229,10 @@ enum msp_direction { #define RDMAE_SHIFT 0 #define TDMAE_SHIFT 1 +/* FIFO watermark register */ +#define MSP_WMRK_RX_4_ELEMENTS BIT(0) +#define MSP_WMRK_TX_4_ELEMENTS BIT(3) + /* Interrupt Register */ #define RX_SERVICE_INT BIT(0) #define RX_OVERRUN_ERROR_INT BIT(1) @@ -460,6 +463,8 @@ struct ux500_msp_config { enum msp_data_size data_size; unsigned int def_elem_len; unsigned int iodelay; + bool clock_provider; + bool bclk_inverted; }; struct ux500_msp { @@ -470,8 +475,11 @@ struct ux500_msp { enum msp_state msp_state; int def_elem_len; unsigned int dir_busy; + unsigned int dir_running; int loopback_enable; unsigned int f_bitclk; + bool clock_provider; + struct ux500_msp_config config; }; int ux500_msp_i2s_init_msp(struct platform_device *pdev, diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index da04ed5cbac4..d746b2586d88 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -903,7 +903,7 @@ static int snd_amd7930_create(struct snd_card *card, int err; *ramd = NULL; - amd = kzalloc(sizeof(*amd), GFP_KERNEL); + amd = kzalloc_obj(*amd); if (amd == NULL) return -ENOMEM; diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index d3b7cab85699..510d310824e9 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -21,7 +21,6 @@ enum { static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb, u8 *buffer, void *context, void(*handler)(struct urb *urb)) { - usb_init_urb(urb); urb->transfer_buffer = buffer; urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP); urb->complete = handler; @@ -142,6 +141,19 @@ static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, return ret; } +static void usb6fire_comm_free(struct comm_runtime *rt) +{ + if (!rt) + return; + + if (rt->chip) + rt->chip->comm = NULL; + + usb_free_urb(rt->receiver); + kfree(rt->receiver_buffer); + kfree(rt); +} + int usb6fire_comm_init(struct sfire_chip *chip) { struct comm_runtime *rt = kzalloc_obj(struct comm_runtime); @@ -153,14 +165,18 @@ int usb6fire_comm_init(struct sfire_chip *chip) rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL); if (!rt->receiver_buffer) { - kfree(rt); - return -ENOMEM; + ret = -ENOMEM; + goto error; } - urb = &rt->receiver; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ret = -ENOMEM; + goto error; + } + rt->receiver = urb; rt->serial = 1; rt->chip = chip; - usb_init_urb(urb); rt->init_urb = usb6fire_comm_init_urb; rt->write8 = usb6fire_comm_write8; rt->write16 = usb6fire_comm_write16; @@ -175,13 +191,15 @@ int usb6fire_comm_init(struct sfire_chip *chip) urb->interval = 1; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { - kfree(rt->receiver_buffer); - kfree(rt); dev_err(&chip->dev->dev, "cannot create comm data receiver."); - return ret; + goto error; } chip->comm = rt; return 0; + + error: + usb6fire_comm_free(rt); + return ret; } void usb6fire_comm_abort(struct sfire_chip *chip) @@ -189,14 +207,10 @@ void usb6fire_comm_abort(struct sfire_chip *chip) struct comm_runtime *rt = chip->comm; if (rt) - usb_poison_urb(&rt->receiver); + usb_poison_urb(rt->receiver); } void usb6fire_comm_destroy(struct sfire_chip *chip) { - struct comm_runtime *rt = chip->comm; - - kfree(rt->receiver_buffer); - kfree(rt); - chip->comm = NULL; + usb6fire_comm_free(chip->comm); } diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h index 2447d7ecf179..89976f510f6c 100644 --- a/sound/usb/6fire/comm.h +++ b/sound/usb/6fire/comm.h @@ -19,7 +19,7 @@ enum /* settings for comm */ struct comm_runtime { struct sfire_chip *chip; - struct urb receiver; + struct urb *receiver; u8 *receiver_buffer; u8 serial; /* urb serial */ diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 6b0bb096f27a..279b449936e7 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -66,7 +66,7 @@ static void usb6fire_midi_out_trigger( struct snd_rawmidi_substream *alsa_sub, int up) { struct midi_runtime *rt = alsa_sub->rmidi->private_data; - struct urb *urb = &rt->out_urb; + struct urb *urb = rt->out_urb; __s8 ret; guard(spinlock_irqsave)(&rt->out_lock); @@ -137,6 +137,19 @@ static const struct snd_rawmidi_ops in_ops = { .trigger = usb6fire_midi_in_trigger }; +static void usb6fire_midi_free(struct midi_runtime *rt) +{ + if (!rt) + return; + + if (rt->chip) + rt->chip->midi = NULL; + + usb_free_urb(rt->out_urb); + kfree(rt->out_buffer); + kfree(rt); +} + int usb6fire_midi_init(struct sfire_chip *chip) { int ret; @@ -148,8 +161,14 @@ int usb6fire_midi_init(struct sfire_chip *chip) rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); if (!rt->out_buffer) { - kfree(rt); - return -ENOMEM; + ret = -ENOMEM; + goto error; + } + + rt->out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rt->out_urb) { + ret = -ENOMEM; + goto error; } rt->chip = chip; @@ -160,15 +179,13 @@ int usb6fire_midi_init(struct sfire_chip *chip) spin_lock_init(&rt->in_lock); spin_lock_init(&rt->out_lock); - comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, + comm_rt->init_urb(comm_rt, rt->out_urb, rt->out_buffer, rt, usb6fire_midi_out_handler); ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); if (ret < 0) { - kfree(rt->out_buffer); - kfree(rt); dev_err(&chip->dev->dev, "unable to create midi.\n"); - return ret; + goto error; } rt->instance->private_data = rt; strscpy(rt->instance->name, "DMX6FireUSB MIDI"); @@ -182,6 +199,10 @@ int usb6fire_midi_init(struct sfire_chip *chip) chip->midi = rt; return 0; + + error: + usb6fire_midi_free(rt); + return ret; } void usb6fire_midi_abort(struct sfire_chip *chip) @@ -189,14 +210,10 @@ void usb6fire_midi_abort(struct sfire_chip *chip) struct midi_runtime *rt = chip->midi; if (rt) - usb_poison_urb(&rt->out_urb); + usb_poison_urb(rt->out_urb); } void usb6fire_midi_destroy(struct sfire_chip *chip) { - struct midi_runtime *rt = chip->midi; - - kfree(rt->out_buffer); - kfree(rt); - chip->midi = NULL; + usb6fire_midi_free(chip->midi); } diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h index 47640c845903..8716ab8a863a 100644 --- a/sound/usb/6fire/midi.h +++ b/sound/usb/6fire/midi.h @@ -22,7 +22,7 @@ struct midi_runtime { spinlock_t in_lock; spinlock_t out_lock; struct snd_rawmidi_substream *out; - struct urb out_urb; + struct urb *out_urb; u8 out_serial; /* serial number of out packet */ u8 *out_buffer; int buffer_offset; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index d2e274b731fe..0285d79ace0f 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -138,8 +138,8 @@ static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) rt->stream_state = STREAM_STOPPING; for (i = 0; i < PCM_N_URBS; i++) { - usb_kill_urb(&rt->in_urbs[i].instance); - usb_kill_urb(&rt->out_urbs[i].instance); + usb_kill_urb(rt->in_urbs[i].instance); + usb_kill_urb(rt->out_urbs[i].instance); } ctrl_rt->usb_streaming = false; ctrl_rt->update_streaming(ctrl_rt); @@ -161,13 +161,13 @@ static int usb6fire_pcm_stream_start(struct pcm_runtime *rt) rt->stream_state = STREAM_STARTING; for (i = 0; i < PCM_N_URBS; i++) { for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) { - packet = &rt->in_urbs[i].packets[k]; + packet = &rt->in_urbs[i].instance->iso_frame_desc[k]; packet->offset = k * rt->in_packet_size; packet->length = rt->in_packet_size; packet->actual_length = 0; packet->status = 0; } - ret = usb_submit_urb(&rt->in_urbs[i].instance, + ret = usb_submit_urb(rt->in_urbs[i].instance, GFP_ATOMIC); if (ret) { usb6fire_pcm_stream_stop(rt); @@ -197,6 +197,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) unsigned int total_length = 0; struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; + struct usb_iso_packet_descriptor *isoc; u32 *src = NULL; u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off * (alsa_rt->frame_bits >> 3)); @@ -207,8 +208,9 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { /* at least 4 header bytes for valid packet. * after that: 32 bits per sample for analog channels */ - if (urb->packets[i].actual_length > 4) - frame_count = (urb->packets[i].actual_length - 4) + isoc = &urb->instance->iso_frame_desc[i]; + if (isoc->actual_length > 4) + frame_count = (isoc->actual_length - 4) / (rt->in_n_analog << 2); else frame_count = 0; @@ -220,7 +222,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) else return; src++; /* skip leading 4 bytes of every packet */ - total_length += urb->packets[i].length; + total_length += isoc->length; for (frame = 0; frame < frame_count; frame++) { memcpy(dest, src, bytes_per_frame); dest += alsa_rt->channels; @@ -244,6 +246,7 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub, int frame_count; struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; + struct usb_iso_packet_descriptor *isoc; u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off * (alsa_rt->frame_bits >> 3)); u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size @@ -263,8 +266,9 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub, for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { /* at least 4 header bytes for valid packet. * after that: 32 bits per sample for analog channels */ - if (urb->packets[i].length > 4) - frame_count = (urb->packets[i].length - 4) + isoc = &urb->instance->iso_frame_desc[i]; + if (isoc->length > 4) + frame_count = (isoc->length - 4) / (rt->out_n_analog << 2); else frame_count = 0; @@ -289,6 +293,7 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) struct pcm_urb *out_urb = in_urb->peer; struct pcm_runtime *rt = in_urb->chip->pcm; struct pcm_substream *sub; + struct usb_iso_packet_descriptor *isoc_out, *isoc_in; bool period_elapsed; int total_length = 0; int frame_count; @@ -299,11 +304,13 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING) return; - for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) - if (in_urb->packets[i].status) { + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + isoc_in = &in_urb->instance->iso_frame_desc[i]; + if (isoc_in->status) { rt->panic = true; return; } + } if (rt->stream_state == STREAM_DISABLED) { dev_err(&rt->chip->dev->dev, @@ -328,12 +335,21 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) /* setup out urb structure */ for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { - out_urb->packets[i].offset = total_length; - out_urb->packets[i].length = (in_urb->packets[i].actual_length - - 4) / (rt->in_n_analog << 2) - * (rt->out_n_analog << 2) + 4; - out_urb->packets[i].status = 0; - total_length += out_urb->packets[i].length; + unsigned int frames = 0; + + isoc_out = &out_urb->instance->iso_frame_desc[i]; + isoc_in = &in_urb->instance->iso_frame_desc[i]; + if (isoc_in->actual_length > 4) + frames = (isoc_in->actual_length - 4) + / (rt->in_n_analog << 2); + frames = min_t(unsigned int, frames, + (rt->out_packet_size - 4) + / (rt->out_n_analog << 2)); + + isoc_out->offset = total_length; + isoc_out->length = frames * (rt->out_n_analog << 2) + 4; + isoc_out->status = 0; + total_length += isoc_out->length; } memset(out_urb->buffer, 0, total_length); @@ -354,9 +370,10 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) /* setup the 4th byte of each sample (0x40 for analog channels) */ dest = out_urb->buffer; - for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) - if (out_urb->packets[i].length >= 4) { - frame_count = (out_urb->packets[i].length - 4) + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + isoc_out = &out_urb->instance->iso_frame_desc[i]; + if (isoc_out->length >= 4) { + frame_count = (isoc_out->length - 4) / (rt->out_n_analog << 2); *(dest++) = 0xaa; *(dest++) = 0xaa; @@ -370,8 +387,10 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) *(dest++) = 0x40; } } - usb_submit_urb(&out_urb->instance, GFP_ATOMIC); - usb_submit_urb(&in_urb->instance, GFP_ATOMIC); + } + + usb_submit_urb(out_urb->instance, GFP_ATOMIC); + usb_submit_urb(in_urb->instance, GFP_ATOMIC); } static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb) @@ -534,22 +553,25 @@ static const struct snd_pcm_ops pcm_ops = { .pointer = usb6fire_pcm_pointer, }; -static void usb6fire_pcm_init_urb(struct pcm_urb *urb, - struct sfire_chip *chip, bool in, int ep, - void (*handler)(struct urb *)) +static int usb6fire_pcm_init_urb(struct pcm_urb *urb, + struct sfire_chip *chip, bool in, int ep, + void (*handler)(struct urb *)) { urb->chip = chip; - usb_init_urb(&urb->instance); - urb->instance.transfer_buffer = urb->buffer; - urb->instance.transfer_buffer_length = + urb->instance = usb_alloc_urb(PCM_N_PACKETS_PER_URB, GFP_KERNEL); + if (!urb->instance) + return -ENOMEM; + urb->instance->transfer_buffer = urb->buffer; + urb->instance->transfer_buffer_length = PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE; - urb->instance.dev = chip->dev; - urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep) + urb->instance->dev = chip->dev; + urb->instance->pipe = in ? usb_rcvisocpipe(chip->dev, ep) : usb_sndisocpipe(chip->dev, ep); - urb->instance.interval = 1; - urb->instance.complete = handler; - urb->instance.context = urb; - urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB; + urb->instance->interval = 1; + urb->instance->complete = handler; + urb->instance->context = urb; + urb->instance->number_of_packets = PCM_N_PACKETS_PER_URB; + return 0; } static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt) @@ -571,14 +593,23 @@ static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt) return 0; } -static void usb6fire_pcm_buffers_destroy(struct pcm_runtime *rt) +static void usb6fire_pcm_free(struct pcm_runtime *rt) { int i; + if (!rt) + return; + + if (rt->chip) + rt->chip->pcm = NULL; + for (i = 0; i < PCM_N_URBS; i++) { + usb_free_urb(rt->out_urbs[i].instance); kfree(rt->out_urbs[i].buffer); + usb_free_urb(rt->in_urbs[i].instance); kfree(rt->in_urbs[i].buffer); } + kfree(rt); } int usb6fire_pcm_init(struct sfire_chip *chip) @@ -593,11 +624,8 @@ int usb6fire_pcm_init(struct sfire_chip *chip) return -ENOMEM; ret = usb6fire_pcm_buffers_init(rt); - if (ret) { - usb6fire_pcm_buffers_destroy(rt); - kfree(rt); - return ret; - } + if (ret) + goto error; rt->chip = chip; rt->stream_state = STREAM_DISABLED; @@ -609,10 +637,14 @@ int usb6fire_pcm_init(struct sfire_chip *chip) spin_lock_init(&rt->capture.lock); for (i = 0; i < PCM_N_URBS; i++) { - usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP, - usb6fire_pcm_in_urb_handler); - usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP, - usb6fire_pcm_out_urb_handler); + ret = usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP, + usb6fire_pcm_in_urb_handler); + if (ret < 0) + goto error; + ret = usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP, + usb6fire_pcm_out_urb_handler); + if (ret < 0) + goto error; rt->in_urbs[i].peer = &rt->out_urbs[i]; rt->out_urbs[i].peer = &rt->in_urbs[i]; @@ -620,10 +652,8 @@ int usb6fire_pcm_init(struct sfire_chip *chip) ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm); if (ret < 0) { - usb6fire_pcm_buffers_destroy(rt); - kfree(rt); dev_err(&chip->dev->dev, "cannot create pcm instance.\n"); - return ret; + goto error; } pcm->private_data = rt; @@ -636,6 +666,10 @@ int usb6fire_pcm_init(struct sfire_chip *chip) chip->pcm = rt; return 0; + + error: + usb6fire_pcm_free(rt); + return ret; } void usb6fire_pcm_abort(struct sfire_chip *chip) @@ -653,8 +687,8 @@ void usb6fire_pcm_abort(struct sfire_chip *chip) snd_pcm_stop_xrun(rt->capture.instance); for (i = 0; i < PCM_N_URBS; i++) { - usb_poison_urb(&rt->in_urbs[i].instance); - usb_poison_urb(&rt->out_urbs[i].instance); + usb_poison_urb(rt->in_urbs[i].instance); + usb_poison_urb(rt->out_urbs[i].instance); } } @@ -662,9 +696,5 @@ void usb6fire_pcm_abort(struct sfire_chip *chip) void usb6fire_pcm_destroy(struct sfire_chip *chip) { - struct pcm_runtime *rt = chip->pcm; - - usb6fire_pcm_buffers_destroy(rt); - kfree(rt); - chip->pcm = NULL; + usb6fire_pcm_free(chip->pcm); } diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h index 5a092dfd69f5..b586fe220fd1 100644 --- a/sound/usb/6fire/pcm.h +++ b/sound/usb/6fire/pcm.h @@ -24,10 +24,7 @@ enum /* settings for pcm */ struct pcm_urb { struct sfire_chip *chip; - /* BEGIN DO NOT SEPARATE */ - struct urb instance; - struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB]; - /* END DO NOT SEPARATE */ + struct urb *instance; u8 *buffer; struct pcm_urb *peer; diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index c5c542d17ccc..2bd49bf82748 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -43,6 +43,7 @@ struct bcd2000 { struct usb_interface *intf; int card_index; + spinlock_t midi_lock; int midi_out_active; struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *midi_receive_substream; @@ -90,6 +91,8 @@ static void bcd2000_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) { struct bcd2000 *bcd2k = substream->rmidi->private_data; + + guard(spinlock_irqsave)(&bcd2k->midi_lock); bcd2k->midi_receive_substream = up ? substream : NULL; } @@ -195,6 +198,8 @@ static void bcd2000_midi_output_trigger(struct snd_rawmidi_substream *substream, { struct bcd2000 *bcd2k = substream->rmidi->private_data; + guard(spinlock_irqsave)(&bcd2k->midi_lock); + if (up) { bcd2k->midi_out_substream = substream; /* check if there is data userspace wants to send */ @@ -219,6 +224,7 @@ static void bcd2000_output_complete(struct urb *urb) return; /* check if there is more data userspace wants to send */ + guard(spinlock_irqsave)(&bcd2k->midi_lock); bcd2000_midi_send(bcd2k); } @@ -234,6 +240,8 @@ static void bcd2000_input_complete(struct urb *urb) if (!bcd2k || urb->status == -ESHUTDOWN) return; + guard(spinlock_irqsave)(&bcd2k->midi_lock); + if (urb->actual_length > 0) bcd2000_midi_handle_input(bcd2k, urb->transfer_buffer, urb->actual_length); @@ -348,16 +356,26 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) return 0; } +static void bcd2000_midi_free(struct bcd2000 *bcd2k, + struct urb **urb_p) +{ + struct urb *urb = *urb_p; + + if (!urb) + return; + + usb_poison_urb(urb); + scoped_guard(spinlock_irq, &bcd2k->midi_lock) + *urb_p = NULL; + + usb_free_urb(urb); +} + static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k, struct usb_interface *interface) { - usb_poison_urb(bcd2k->midi_out_urb); - usb_poison_urb(bcd2k->midi_in_urb); - - usb_free_urb(bcd2k->midi_out_urb); - usb_free_urb(bcd2k->midi_in_urb); - bcd2k->midi_out_urb = NULL; - bcd2k->midi_in_urb = NULL; + bcd2000_midi_free(bcd2k, &bcd2k->midi_out_urb); + bcd2000_midi_free(bcd2k, &bcd2k->midi_in_urb); if (bcd2k->intf) { usb_set_intfdata(bcd2k->intf, NULL); @@ -393,6 +411,7 @@ static int bcd2000_probe(struct usb_interface *interface, bcd2k->card = card; bcd2k->card_index = card_index; bcd2k->intf = interface; + spin_lock_init(&bcd2k->midi_lock); snd_card_set_dev(card, &interface->dev); diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index ba3f73455ebe..bb6280aa3533 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -828,16 +828,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret); if (ret < 0) { - kfree(cdev->data_cb_info); - free_urbs(cdev->data_urbs_in); + snd_usb_caiaq_audio_free(cdev); return ret; } cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret); if (ret < 0) { - kfree(cdev->data_cb_info); - free_urbs(cdev->data_urbs_in); - free_urbs(cdev->data_urbs_out); + snd_usb_caiaq_audio_free(cdev); return ret; } @@ -858,6 +855,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev) dev_dbg(dev, "%s(%p)\n", __func__, cdev); free_urbs(cdev->data_urbs_in); + cdev->data_urbs_in = NULL; free_urbs(cdev->data_urbs_out); + cdev->data_urbs_out = NULL; kfree(cdev->data_cb_info); + cdev->data_cb_info = NULL; } diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index a16e59248480..3d821fde4582 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -192,8 +192,8 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb) break; } - cdev->ep1_in_urb.actual_length = 0; - ret = usb_submit_urb(&cdev->ep1_in_urb, GFP_ATOMIC); + cdev->ep1_in_urb->actual_length = 0; + ret = usb_submit_urb(cdev->ep1_in_urb, GFP_ATOMIC); if (ret < 0) dev_err(dev, "unable to submit urb. OOM!?\n"); } @@ -408,6 +408,10 @@ static void card_free(struct snd_card *card) #endif snd_usb_caiaq_audio_free(cdev); usb_put_dev(cdev->chip.dev); + usb_free_urb(cdev->ep1_in_urb); + cdev->ep1_in_urb = NULL; + usb_free_urb(cdev->midi_out_urb); + cdev->midi_out_urb = NULL; } static int create_card(struct usb_device *usb_dev, @@ -457,22 +461,30 @@ static int init_card(struct snd_usb_caiaqdev *cdev) return -EIO; } - usb_init_urb(&cdev->ep1_in_urb); - usb_init_urb(&cdev->midi_out_urb); + cdev->ep1_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cdev->ep1_in_urb) + return -ENOMEM; - usb_fill_bulk_urb(&cdev->ep1_in_urb, usb_dev, + cdev->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cdev->midi_out_urb) { + usb_free_urb(cdev->ep1_in_urb); + cdev->ep1_in_urb = NULL; + return -ENOMEM; + } + + usb_fill_bulk_urb(cdev->ep1_in_urb, usb_dev, usb_rcvbulkpipe(usb_dev, 0x1), cdev->ep1_in_buf, EP1_BUFSIZE, usb_ep1_command_reply_dispatch, cdev); - usb_fill_bulk_urb(&cdev->midi_out_urb, usb_dev, + usb_fill_bulk_urb(cdev->midi_out_urb, usb_dev, usb_sndbulkpipe(usb_dev, 0x1), cdev->midi_out_buf, EP1_BUFSIZE, snd_usb_caiaq_midi_output_done, cdev); /* sanity checks of EPs before actually submitting */ - if (usb_urb_ep_type_check(&cdev->ep1_in_urb) || - usb_urb_ep_type_check(&cdev->midi_out_urb)) { + if (usb_urb_ep_type_check(cdev->ep1_in_urb) || + usb_urb_ep_type_check(cdev->midi_out_urb)) { dev_err(dev, "invalid EPs\n"); return -EINVAL; } @@ -480,7 +492,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev) init_waitqueue_head(&cdev->ep1_wait_queue); init_waitqueue_head(&cdev->prepare_wait_queue); - if (usb_submit_urb(&cdev->ep1_in_urb, GFP_KERNEL) != 0) + if (usb_submit_urb(cdev->ep1_in_urb, GFP_KERNEL) != 0) return -EIO; err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); @@ -530,7 +542,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev) return 0; err_kill_urb: - usb_kill_urb(&cdev->ep1_in_urb); + usb_kill_urb(cdev->ep1_in_urb); return err; } @@ -576,8 +588,8 @@ static void snd_disconnect(struct usb_interface *intf) #endif snd_usb_caiaq_audio_disconnect(cdev); - usb_kill_urb(&cdev->ep1_in_urb); - usb_kill_urb(&cdev->midi_out_urb); + usb_kill_urb(cdev->ep1_in_urb); + usb_kill_urb(cdev->midi_out_urb); snd_card_free_when_closed(card); } diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index 743eb0387b5f..1c6f34693fa8 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -60,8 +60,8 @@ struct snd_usb_caiaq_cb_info; struct snd_usb_caiaqdev { struct snd_usb_audio chip; - struct urb ep1_in_urb; - struct urb midi_out_urb; + struct urb *ep1_in_urb; + struct urb *midi_out_urb; struct urb **data_urbs_in; struct urb **data_urbs_out; struct snd_usb_caiaq_cb_info *data_cb_info; diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c index c656d0162432..18529484c8dc 100644 --- a/sound/usb/caiaq/midi.c +++ b/sound/usb/caiaq/midi.c @@ -43,7 +43,7 @@ static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substre { struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data; if (cdev->midi_out_active) { - usb_kill_urb(&cdev->midi_out_urb); + usb_kill_urb(cdev->midi_out_urb); cdev->midi_out_active = 0; } return 0; @@ -64,9 +64,9 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev, return; cdev->midi_out_buf[2] = len; - cdev->midi_out_urb.transfer_buffer_length = len+3; + cdev->midi_out_urb->transfer_buffer_length = len+3; - ret = usb_submit_urb(&cdev->midi_out_urb, GFP_ATOMIC); + ret = usb_submit_urb(cdev->midi_out_urb, GFP_ATOMIC); if (ret < 0) dev_err(dev, "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed," diff --git a/sound/usb/card.h b/sound/usb/card.h index e34d92d576a2..8299ac241c60 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -116,6 +116,7 @@ struct snd_usb_endpoint { unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ + unsigned int max_urb_packs; /* packets allocated per data URB */ unsigned int max_urb_frames; /* max URB size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 0835943d7b0e..879d0451536e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -449,7 +449,9 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep, struct snd_urb_ctx *ctx) { guard(spinlock_irqsave)(&ep->lock); - list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + /* ctx may still be linked: a stale completion racing a stop/restart. */ + if (list_empty(&ctx->ready_list)) + list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); } /* @@ -492,9 +494,10 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, /* copy over the length information */ if (implicit_fb) { - ctx->packets = packet->packets; + ctx->packets = min_t(int, packet->packets, + ep->max_urb_packs); memcpy(ctx->packet_size, packet->packet_size, - packet->packets * sizeof(packet->packet_size[0])); + ctx->packets * sizeof(packet->packet_size[0])); } /* call the data handler to fill in playback data */ @@ -1036,6 +1039,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) */ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) { + struct snd_urb_ctx *ctx, *n; unsigned int i; if (!force && atomic_read(&ep->running)) @@ -1045,7 +1049,9 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) return 0; scoped_guard(spinlock_irqsave, &ep->lock) { - INIT_LIST_HEAD(&ep->ready_playback_urbs); + /* Unlink each ctx; INIT_LIST_HEAD() alone would leave them looking linked. */ + list_for_each_entry_safe(ctx, n, &ep->ready_playback_urbs, ready_list) + list_del_init(&ctx->ready_list); ep->next_packet_head = 0; ep->next_packet_queued = 0; } @@ -1242,15 +1248,16 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } + if (fmt->fmt_type == UAC_FORMAT_TYPE_II) + urb_packs++; /* for transfer delimiter */ + ep->max_urb_packs = urb_packs; + /* allocate and initialize data urbs */ for (i = 0; i < ep->nurbs; i++) { struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; u->packets = urb_packs; - - if (fmt->fmt_type == UAC_FORMAT_TYPE_II) - u->packets++; /* for transfer delimiter */ u->buffer_size = maxsize * u->packets; u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); if (!u->urb) diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 5fc2131b4561..68bb7eabf107 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -191,6 +191,10 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, const int max_retries = 5; int err; + CLASS(snd_usb_lock, pm)(mixer->chip); + if (pm.err < 0) + return -EIO; + if (!private->urb) return -ENODEV; @@ -1026,6 +1030,10 @@ static int fcp_init(struct usb_mixer_interface *mixer, struct usb_device *dev = mixer->chip->dev; int err; + CLASS(snd_usb_lock, pm)(mixer->chip); + if (pm.err < 0) + return -EIO; + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), FCP_USB_REQ_STEP0, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index cd1a4c871c5d..3157952e4c1d 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -24,7 +24,7 @@ struct pcm_urb { struct hiface_chip *chip; - struct urb instance; + struct urb *instance; struct usb_anchor submitted; u8 *buffer; }; @@ -193,7 +193,7 @@ static void hiface_pcm_stream_stop(struct pcm_runtime *rt) if (!time) usb_kill_anchored_urbs( &rt->out_urbs[i].submitted); - usb_kill_urb(&rt->out_urbs[i].instance); + usb_kill_urb(rt->out_urbs[i].instance); } rt->stream_state = STREAM_DISABLED; @@ -215,9 +215,9 @@ static int hiface_pcm_stream_start(struct pcm_runtime *rt) rt->stream_state = STREAM_STARTING; for (i = 0; i < PCM_N_URBS; i++) { memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE); - usb_anchor_urb(&rt->out_urbs[i].instance, + usb_anchor_urb(rt->out_urbs[i].instance, &rt->out_urbs[i].submitted); - ret = usb_submit_urb(&rt->out_urbs[i].instance, + ret = usb_submit_urb(rt->out_urbs[i].instance, GFP_ATOMIC); if (ret) { hiface_pcm_stream_stop(rt); @@ -334,7 +334,7 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb) if (do_period_elapsed) snd_pcm_period_elapsed(sub->instance); - ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC); + ret = usb_submit_urb(out_urb->instance, GFP_ATOMIC); if (ret < 0) goto out_fail; @@ -492,16 +492,18 @@ static int hiface_pcm_init_urb(struct pcm_urb *urb, void (*handler)(struct urb *)) { urb->chip = chip; - usb_init_urb(&urb->instance); + urb->instance = usb_alloc_urb(0, GFP_KERNEL); + if (!urb->instance) + return -ENOMEM; urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL); if (!urb->buffer) return -ENOMEM; - usb_fill_bulk_urb(&urb->instance, chip->dev, + usb_fill_bulk_urb(urb->instance, chip->dev, usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer, PCM_PACKET_SIZE, handler, urb); - if (usb_urb_ep_type_check(&urb->instance)) + if (usb_urb_ep_type_check(urb->instance)) return -EINVAL; init_usb_anchor(&urb->submitted); @@ -520,24 +522,26 @@ void hiface_pcm_abort(struct hiface_chip *chip) } } -static void hiface_pcm_destroy(struct hiface_chip *chip) +static void hiface_pcm_destroy(struct pcm_runtime *rt) { - struct pcm_runtime *rt = chip->pcm; int i; - for (i = 0; i < PCM_N_URBS; i++) - kfree(rt->out_urbs[i].buffer); + if (!rt) + return; + + if (rt->chip) + rt->chip->pcm = NULL; - kfree(chip->pcm); - chip->pcm = NULL; + for (i = 0; i < PCM_N_URBS; i++) { + usb_free_urb(rt->out_urbs[i].instance); + kfree(rt->out_urbs[i].buffer); + } + kfree(rt); } static void hiface_pcm_free(struct snd_pcm *pcm) { - struct pcm_runtime *rt = pcm->private_data; - - if (rt) - hiface_pcm_destroy(rt->chip); + hiface_pcm_destroy(pcm->private_data); } int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) @@ -587,8 +591,6 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) return 0; error: - for (i = 0; i < PCM_N_URBS; i++) - kfree(rt->out_urbs[i].buffer); - kfree(rt); + hiface_pcm_destroy(rt); return ret; } diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 77f06da93151..bd4d569a8190 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -76,6 +76,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { /* Implicit feedback quirk table for capture: only FIXED type */ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0004, 0x01, 1), /* Behringer FCA1616 */ {} /* terminator */ }; diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 8a9bc37f0b6e..7e5b1b13360f 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -971,6 +971,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, default: count = 2; } + if (ep->max_transfer < count) + return; count = snd_rawmidi_transmit(ep->ports[0].substream, urb->transfer_buffer, count); diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index b9a62e94e06c..860a62a3d74b 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -109,10 +109,10 @@ struct ua101 { unsigned int buffer_pos; unsigned int queue_length; struct ua101_urb { - struct urb urb; - struct usb_iso_packet_descriptor iso_frame_desc[1]; + struct urb *urb; struct list_head ready_list; - } *urbs[MAX_QUEUE_LENGTH]; + struct ua101 *ua; + } urbs[MAX_QUEUE_LENGTH]; struct { unsigned int size; void *addr; @@ -167,15 +167,15 @@ static void abort_usb_playback(struct ua101 *ua) wake_up(&ua->alsa_playback_wait); } -static void playback_urb_complete(struct urb *usb_urb) +static void playback_urb_complete(struct urb *urb) { - struct ua101_urb *urb = (struct ua101_urb *)usb_urb; - struct ua101 *ua = urb->urb.context; + struct ua101_urb *ua_urb = urb->context; + struct ua101 *ua = ua_urb->ua; - if (unlikely(urb->urb.status == -ENOENT || /* unlinked */ - urb->urb.status == -ENODEV || /* device removed */ - urb->urb.status == -ECONNRESET || /* unlinked */ - urb->urb.status == -ESHUTDOWN)) { /* device disabled */ + if (unlikely(urb->status == -ENOENT || /* unlinked */ + urb->status == -ENODEV || /* device removed */ + urb->status == -ECONNRESET || /* unlinked */ + urb->status == -ESHUTDOWN)) { /* device disabled */ abort_usb_playback(ua); abort_alsa_playback(ua); return; @@ -184,18 +184,19 @@ static void playback_urb_complete(struct urb *usb_urb) if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) { /* append URB to FIFO */ guard(spinlock_irqsave)(&ua->lock); - list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); + list_add_tail(&ua_urb->ready_list, &ua->ready_playback_urbs); if (ua->rate_feedback_count > 0) queue_work(system_highpri_wq, &ua->playback_work); ua->playback.substream->runtime->delay -= - urb->urb.iso_frame_desc[0].length / + urb->iso_frame_desc[0].length / ua->playback.frame_bytes; } } static void first_playback_urb_complete(struct urb *urb) { - struct ua101 *ua = urb->context; + struct ua101_urb *ua_urb = urb->context; + struct ua101 *ua = ua_urb->ua; urb->complete = playback_urb_complete; playback_urb_complete(urb); @@ -248,7 +249,8 @@ static void playback_work(struct work_struct *work) { struct ua101 *ua = container_of(work, struct ua101, playback_work); unsigned int frames; - struct ua101_urb *urb; + struct ua101_urb *ua_urb; + struct urb *urb; bool do_period_elapsed = false; int err; @@ -275,23 +277,24 @@ static void playback_work(struct work_struct *work) ua->rate_feedback_count--; /* take URB out of FIFO */ - urb = list_first_entry(&ua->ready_playback_urbs, - struct ua101_urb, ready_list); - list_del(&urb->ready_list); + ua_urb = list_first_entry(&ua->ready_playback_urbs, + struct ua101_urb, ready_list); + list_del(&ua_urb->ready_list); + urb = ua_urb->urb; /* fill packet with data or silence */ - urb->urb.iso_frame_desc[0].length = + urb->iso_frame_desc[0].length = frames * ua->playback.frame_bytes; if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) do_period_elapsed |= copy_playback_data(&ua->playback, - &urb->urb, + urb, frames); else - memset(urb->urb.transfer_buffer, 0, - urb->urb.iso_frame_desc[0].length); + memset(urb->transfer_buffer, 0, + urb->iso_frame_desc[0].length); /* and off you go ... */ - err = usb_submit_urb(&urb->urb, GFP_ATOMIC); + err = usb_submit_urb(urb, GFP_ATOMIC); if (unlikely(err < 0)) { abort_usb_playback(ua); abort_alsa_playback(ua); @@ -342,7 +345,8 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb, static void capture_urb_complete(struct urb *urb) { - struct ua101 *ua = urb->context; + struct ua101_urb *ua_urb = urb->context; + struct ua101 *ua = ua_urb->ua; struct ua101_stream *stream = &ua->capture; unsigned int frames, write_ptr; bool do_period_elapsed; @@ -413,7 +417,8 @@ stream_stopped: static void first_capture_urb_complete(struct urb *urb) { - struct ua101 *ua = urb->context; + struct ua101_urb *ua_urb = urb->context; + struct ua101 *ua = ua_urb->ua; urb->complete = capture_urb_complete; capture_urb_complete(urb); @@ -427,7 +432,7 @@ static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream) unsigned int i; for (i = 0; i < stream->queue_length; ++i) { - int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL); + int err = usb_submit_urb(stream->urbs[i].urb, GFP_KERNEL); if (err < 0) { dev_err(&ua->dev->dev, "USB request error %d: %s\n", err, usb_error_string(err)); @@ -442,8 +447,8 @@ static void kill_stream_urbs(struct ua101_stream *stream) unsigned int i; for (i = 0; i < stream->queue_length; ++i) - if (stream->urbs[i]) - usb_kill_urb(&stream->urbs[i]->urb); + if (stream->urbs[i].urb) + usb_kill_urb(stream->urbs[i].urb); } static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index) @@ -508,7 +513,7 @@ static int start_usb_capture(struct ua101 *ua) return err; clear_bit(CAPTURE_URB_COMPLETED, &ua->states); - ua->capture.urbs[0]->urb.complete = first_capture_urb_complete; + ua->capture.urbs[0].urb->complete = first_capture_urb_complete; ua->rate_feedback_start = 0; ua->rate_feedback_count = 0; @@ -550,7 +555,7 @@ static int start_usb_playback(struct ua101 *ua) return err; clear_bit(PLAYBACK_URB_COMPLETED, &ua->states); - ua->playback.urbs[0]->urb.complete = + ua->playback.urbs[0].urb->complete = first_playback_urb_complete; scoped_guard(spinlock_irq, &ua->lock) { INIT_LIST_HEAD(&ua->ready_playback_urbs); @@ -580,7 +585,7 @@ static int start_usb_playback(struct ua101 *ua) add_with_wraparound(ua, &ua->rate_feedback_start, 1); ua->rate_feedback_count--; } - urb = &ua->playback.urbs[i]->urb; + urb = ua->playback.urbs[i].urb; urb->iso_frame_desc[0].length = frames * ua->playback.frame_bytes; memset(urb->transfer_buffer, 0, @@ -1059,7 +1064,7 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, void (*urb_complete)(struct urb *)) { unsigned max_packet_size = stream->max_packet_bytes; - struct ua101_urb *urb; + struct urb *urb; unsigned int b, u = 0; for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) { @@ -1070,23 +1075,24 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream, while (size >= max_packet_size) { if (u >= stream->queue_length) goto bufsize_error; - urb = kmalloc_obj(*urb); + urb = usb_alloc_urb(1, GFP_KERNEL); if (!urb) return -ENOMEM; - usb_init_urb(&urb->urb); - urb->urb.dev = ua->dev; - urb->urb.pipe = stream->usb_pipe; - urb->urb.transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->urb.transfer_buffer = addr; - urb->urb.transfer_dma = dma; - urb->urb.transfer_buffer_length = max_packet_size; - urb->urb.number_of_packets = 1; - urb->urb.interval = 1; - urb->urb.context = ua; - urb->urb.complete = urb_complete; - urb->urb.iso_frame_desc[0].offset = 0; - urb->urb.iso_frame_desc[0].length = max_packet_size; - stream->urbs[u++] = urb; + urb->dev = ua->dev; + urb->pipe = stream->usb_pipe; + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer = addr; + urb->transfer_dma = dma; + urb->transfer_buffer_length = max_packet_size; + urb->number_of_packets = 1; + urb->interval = 1; + urb->context = &stream->urbs[u]; + urb->complete = urb_complete; + urb->iso_frame_desc[0].offset = 0; + urb->iso_frame_desc[0].length = max_packet_size; + stream->urbs[u].ua = ua; + stream->urbs[u].urb = urb; + u++; size -= max_packet_size; addr += max_packet_size; dma += max_packet_size; @@ -1104,8 +1110,8 @@ static void free_stream_urbs(struct ua101_stream *stream) unsigned int i; for (i = 0; i < stream->queue_length; ++i) { - kfree(stream->urbs[i]); - stream->urbs[i] = NULL; + usb_free_urb(stream->urbs[i].urb); + stream->urbs[i].urb = NULL; } } diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index ace4ccad8f51..41454cb403d0 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -519,6 +519,28 @@ static const struct usbmix_name_map audient_id14_map[] = { }; /* + * Audient iD24: feature unit 12 ("Speaker Playback Volume") sits in the + * monitor-mixer branch and does not apply volume to all of its channels; + * when userspace adopts it as the master playback volume, the left main + * output stays at 0 dB while the right one is attenuated, producing a + * stereo imbalance. Rename it so that it is not picked up as the + * stream's master volume control. + */ +static const struct usbmix_name_map audient_id24_map[] = { + { 12, "Monitor Mix Playback" }, /* FU, partial channel coverage */ + {} +}; + +/* + * The GC553Pro returns no data for GET_CUR on its advertised mute control. + * SET_CUR succeeds but does not mute capture, so skip the control entirely. + */ +static const struct usbmix_name_map avermedia_gc553pro_map[] = { + { 3, NULL, UAC_FU_MUTE }, + {} +}; + +/* * Control map entries */ @@ -565,6 +587,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .selector_map = c400_selectors, }, { + .id = USB_ID(0x07ca, 0x1553), + .map = avermedia_gc553pro_map, + }, + { .id = USB_ID(0x08bb, 0x2702), .map = linex_map, }, @@ -612,6 +638,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = audient_id14_map, }, { + /* Audient iD24 */ + .id = USB_ID(0x2708, 0x000d), + .map = audient_id24_map, + }, + { /* KEF X300A */ .id = USB_ID(0x27ac, 0x1000), .map = scms_usb3318_map, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index a1f5592cc5d5..fc622eb95dc5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3480,6 +3480,10 @@ static int snd_rme_digiface_write_reg(struct snd_kcontrol *kcontrol, int item, u struct usb_device *dev = chip->dev; int err; + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return -EIO; + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), item, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, @@ -3499,6 +3503,10 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu __le32 buf[4] = {}; int err; + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return -EIO; + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), RME_DIGIFACE_READ_STATUS, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c index 2e5a8d37ec57..bdb5e3aaff3b 100644 --- a/sound/usb/mixer_s1810c.c +++ b/sound/usb/mixer_s1810c.c @@ -474,6 +474,10 @@ snd_s1810c_switch_get(struct snd_kcontrol *kctl, u32 state = 0; int ret; + CLASS(snd_usb_lock, pm)(mixer->chip); + if (pm.err < 0) + return -EIO; + guard(mutex)(&private->data_mutex); ret = snd_s1810c_get_switch_state(mixer, kctl, &state); if (ret < 0) @@ -504,6 +508,10 @@ snd_s1810c_switch_set(struct snd_kcontrol *kctl, u32 newval = 0; int ret = 0; + CLASS(snd_usb_lock, pm)(mixer->chip); + if (pm.err < 0) + return -EIO; + guard(mutex)(&private->data_mutex); ret = snd_s1810c_get_switch_state(mixer, kctl, &curval); if (ret < 0) diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index 673eb8d8724d..369968565c19 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -707,6 +707,10 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, int idx = snd_usb_ctrl_intf(elem->head.mixer->hostif) | (elem->head.id << 8); int err; + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return -EIO; + err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_MEM, diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 502854cc9f9f..ed5fe746d438 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2603,9 +2603,9 @@ static int scarlett2_usb_rx(struct usb_device *dev, int interface, } /* Send a proprietary format request to the Scarlett interface */ -static int scarlett2_usb( - struct usb_mixer_interface *mixer, u32 cmd, - void *req_data, u16 req_size, void *resp_data, u16 resp_size) +static int scarlett2_usb_nopm(struct usb_mixer_interface *mixer, u32 cmd, + void *req_data, u16 req_size, + void *resp_data, u16 resp_size) { struct scarlett2_data *private = mixer->private_data; struct usb_device *dev = mixer->chip->dev; @@ -2713,6 +2713,18 @@ retry: return err; } +static int scarlett2_usb(struct usb_mixer_interface *mixer, u32 cmd, + void *req_data, u16 req_size, + void *resp_data, u16 resp_size) +{ + CLASS(snd_usb_lock, pm)(mixer->chip); + if (pm.err < 0) + return -EIO; + + return scarlett2_usb_nopm(mixer, cmd, req_data, req_size, + resp_data, resp_size); +} + /* Send a USB message to get data; result placed in *buf */ static int scarlett2_usb_get( struct usb_mixer_interface *mixer, @@ -3020,9 +3032,21 @@ static int scarlett2_usb_set_config_buf( /* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ static void scarlett2_config_save(struct usb_mixer_interface *mixer) { - int err; + __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE); + int err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, + &req, sizeof(req), NULL, 0); + + if (err < 0) + usb_audio_err(mixer->chip, "config save failed: %d\n", err); +} + +/* The USB suspend callback must not acquire another PM reference. */ +static void scarlett2_config_save_nopm(struct usb_mixer_interface *mixer) +{ + __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE); + int err = scarlett2_usb_nopm(mixer, SCARLETT2_USB_DATA_CMD, + &req, sizeof(req), NULL, 0); - err = scarlett2_usb_activate_config(mixer, SCARLETT2_USB_CONFIG_SAVE); if (err < 0) usb_audio_err(mixer->chip, "config save failed: %d\n", err); } @@ -8639,7 +8663,7 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) struct scarlett2_data *private = mixer->private_data; if (cancel_delayed_work_sync(&private->work)) - scarlett2_config_save(private->mixer); + scarlett2_config_save_nopm(private->mixer); scarlett2_cleanup_urb(mixer); } diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index ebff185cbd2c..14fb1ad764a7 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -151,6 +151,9 @@ static const char *const route_names[] = { static int snd_us16x08_recv_urb(struct snd_usb_audio *chip, unsigned char *buf, int size) { + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return -EIO; guard(mutex)(&chip->mutex); snd_usb_ctl_msg(chip->dev, @@ -165,6 +168,10 @@ static int snd_us16x08_recv_urb(struct snd_usb_audio *chip, */ static int snd_us16x08_send_urb(struct snd_usb_audio *chip, char *buf, int size) { + CLASS(snd_usb_lock, pm)(chip); + if (pm.err < 0) + return -EIO; + return snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), SND_US16X08_URB_REQUEST, SND_US16X08_URB_REQUESTTYPE, 0, 0, buf, size); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 91938172912a..c5870901a8d4 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -850,6 +850,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) } /* + * A post configuration device descriptor read is needed to make the CM1A + * operational after reenumeration. + */ +static int snd_usb_cm1a_boot_quirk(struct usb_device *dev) +{ + struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc); + int err; + + if (!desc) + return -ENOMEM; + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); + if (err < 0) { + dev_err(&dev->dev, "failed to read device descriptor: %d\n", err); + return err; + } + + return 0; +} + +/* * Some sound cards from Native Instruments are in fact compliant to the USB * audio standard of version 2 and other approved USB standards, even though * they come up as vendor-specific device when first connected. @@ -1681,6 +1702,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, switch (id) { case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ return snd_usb_motu_m_series_boot_quirk(dev); + case USB_ID(0x1397, 0x1234): /* Behringer CM1A */ + return snd_usb_cm1a_boot_quirk(dev); } return 0; @@ -2390,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */ QUIRK_FLAG_IFB_SILENCE_ON_EMPTY), + DEVICE_FLG(0x1397, 0x0510, /* Behringer UV1 */ + QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */ diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index f00b53346abd..a5db0d044ef9 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -180,8 +180,11 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw, guard(mutex)(&us122l->mutex); s = us122l->sk.s; read = offset < s->read_size; - if (read && area->vm_flags & VM_WRITE) - return -EPERM; + if (read) { + if (area->vm_flags & VM_WRITE) + return -EPERM; + vm_flags_clear(area, VM_MAYWRITE); + } /* if userspace tries to mmap beyond end of our buffer, fail */ if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) { dev_warn(hw->card->dev, "%s: size %lu > %u\n", __func__, diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 4190227c5a2a..108f9bddf9aa 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -189,6 +189,9 @@ static void i_usx2y_in04_int(struct urb *urb) return; } + if (urb->actual_length < USX2Y_IN04_SIZE) + goto resubmit; + if (us428ctls) { diff = -1; if (us428ctls->ctl_snapshot_last == -2) { @@ -196,7 +199,7 @@ static void i_usx2y_in04_int(struct urb *urb) memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last)); us428ctls->ctl_snapshot_last = -1; } else { - for (i = 0; i < 21; i++) { + for (i = 0; i < USX2Y_IN04_SIZE; i++) { if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) { if (diff < 0) diff = i; @@ -253,6 +256,7 @@ static void i_usx2y_in04_int(struct urb *urb) if (err) dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n", err); +resubmit: urb->dev = usx2y->dev; usb_submit_urb(urb, GFP_ATOMIC); } @@ -305,7 +309,7 @@ int usx2y_in04_init(struct usx2ydev *usx2y) goto error; } - usx2y->in04_buf = kmalloc(21, GFP_KERNEL); + usx2y->in04_buf = kzalloc(USX2Y_IN04_SIZE, GFP_KERNEL); if (!usx2y->in04_buf) { err = -ENOMEM; goto error; @@ -313,7 +317,7 @@ int usx2y_in04_init(struct usx2ydev *usx2y) init_waitqueue_head(&usx2y->in04_wait_queue); usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4), - usx2y->in04_buf, 21, + usx2y->in04_buf, USX2Y_IN04_SIZE, i_usx2y_in04_int, usx2y, 10); if (usb_urb_ep_type_check(usx2y->in04_urb)) { diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index 6a76d04bf1c7..7b6deed17d4b 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -5,6 +5,8 @@ #include "../midi.h" #include "usbus428ctldefs.h" +#define USX2Y_IN04_SIZE sizeof(struct us428_ctls) + #define NRURBS 2 /* Default value used for nr of packs per urb. @@ -55,7 +57,7 @@ struct usx2ydev { int stride; struct urb *in04_urb; void *in04_buf; - char in04_last[24]; + char in04_last[USX2Y_IN04_SIZE]; unsigned int in04_int_calls; struct snd_usx2y_urb_seq *us04; wait_queue_head_t in04_wait_queue; diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index 647190f4d5af..6f35276416fe 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -354,8 +354,8 @@ static void virtsnd_remove(struct virtio_device *vdev) if (snd->card) snd_card_free(snd->card); - vdev->config->del_vqs(vdev); virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) { struct virtio_pcm_substream *vss = &snd->substreams[i]; @@ -383,8 +383,8 @@ static int virtsnd_freeze(struct virtio_device *vdev) virtsnd_disable_event_vq(snd); virtsnd_ctl_msg_cancel_all(snd); - vdev->config->del_vqs(vdev); virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); for (i = 0; i < snd->nsubstreams; ++i) cancel_work_sync(&snd->substreams[i].elapsed_period); diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 7dc9fff9b637..17d971d47f87 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -140,6 +140,9 @@ static int load_xbc_fd(int fd, char **buf, int size) { int ret; + if (size < 0 || size > XBC_DATA_MAX) + return -EINVAL; + *buf = malloc(size + 1); if (!*buf) return -ENOMEM; @@ -168,6 +171,13 @@ static int load_xbc_file(const char *path, char **buf) return ret; } + if (stat.st_size > XBC_DATA_MAX) { + pr_err("%s size is too big\n", path); + ret = -E2BIG; + close(fd); + return ret; + } + ret = load_xbc_fd(fd, buf, stat.st_size); close(fd); @@ -218,7 +228,8 @@ static int load_xbc_from_initrd(int fd, char **buf) csum = le32toh(csum); /* Wrong size error */ - if (stat.st_size < size + BOOTCONFIG_FOOTER_SIZE) { + if (size > XBC_DATA_MAX || + size > stat.st_size - BOOTCONFIG_FOOTER_SIZE) { pr_err("bootconfig size is too big\n"); return -E2BIG; } diff --git a/tools/objtool/klp-checksum.c b/tools/objtool/klp-checksum.c index b8e47f28997e..ebe25f9c5260 100644 --- a/tools/objtool/klp-checksum.c +++ b/tools/objtool/klp-checksum.c @@ -54,6 +54,19 @@ static int checksum_debug_init(struct objtool_file *file) return 0; } +/* + * Detect a reference to anonymous constant pool data which the compiler places + * in .rodata.cst<num> and which either has an .LC<num> symbol associated with + * it or (with Clang) no symbol at all. These are typically initializers for + * local function stack data, so they're considered part of the function rather + * than data per se. + */ +static bool is_anonymous_const_data(struct symbol *sym) +{ + return strstarts(sym->sec->name, ".rodata.cst") && + (is_sec_sym(sym) || strstarts(sym->name, ".LC")); +} + static void checksum_update_insn(struct objtool_file *file, struct symbol *func, struct instruction *insn) { @@ -129,6 +142,14 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func, goto alts; } + if (is_anonymous_const_data(sym)) { + void *cst; + + cst = sym->sec->data->d_buf + sym->offset + offset; + __checksum_update_insn(func, insn, cst, sym->sec->sh.sh_entsize); + goto alts; + } + if (is_sec_sym(sym)) { sym = find_symbol_containing(reloc->sym->sec, offset); if (!sym) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 0ba307e78fe1..4d5993da9f94 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -27,6 +27,7 @@ CFLAGS += -fno-strict-aliasing ifeq ($(CC_NO_CLANG), 0) CLANG_TARGET_FLAGS_arm := arm-linux-gnueabi CLANG_TARGET_FLAGS_arm64 := aarch64-linux-gnu + CLANG_TARGET_FLAGS_loongarch := loongarch64-linux-gnu CLANG_TARGET_FLAGS_m68k := m68k-linux-gnu CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu @@ -1142,6 +1143,7 @@ ifndef NO_RUST ifneq ($(CROSS_COMPILE),) RUST_TARGET_FLAGS_arm := arm-unknown-linux-gnueabi RUST_TARGET_FLAGS_arm64 := aarch64-unknown-linux-gnu + RUST_TARGET_FLAGS_loongarch := loongarch64-unknown-linux-gnu RUST_TARGET_FLAGS_m68k := m68k-unknown-linux-gnu RUST_TARGET_FLAGS_mips := mipsel-unknown-linux-gnu RUST_TARGET_FLAGS_powerpc := powerpc64le-unknown-linux-gnu diff --git a/tools/perf/util/powerpc-vpadtl.c b/tools/perf/util/powerpc-vpadtl.c index 710f3093f3f9..af6783cfdb53 100644 --- a/tools/perf/util/powerpc-vpadtl.c +++ b/tools/perf/util/powerpc-vpadtl.c @@ -196,7 +196,7 @@ static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record, sample.cpumode = PERF_RECORD_MISC_KERNEL; sample.time = save; sample.raw_data = record; - sample.raw_size = sizeof(record); + sample.raw_size = sizeof(*record); event.sample.header.type = PERF_RECORD_SAMPLE; event.sample.header.misc = sample.cpumode; event.sample.header.size = sizeof(struct perf_event_header); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 94f9c8faedda..3587ad243159 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1947,7 +1947,16 @@ int dso__load(struct dso *dso, struct map *map) if (next_slot) { ss_pos++; - if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) + /* + * The binary type is used to find the file containing + * the executed instructions, so prefer the types that + * refer to the actual object over debug-only files such + * as DSO_BINARY_TYPE__DEBUGLINK. + */ + if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND || + symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE || + (symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_DSO && + dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE)) dso__set_binary_type(dso, symtab_type); if (syms_ss && runtime_ss) diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h index 979d4cabfaf9..2ddb01a059fd 100644 --- a/tools/sched_ext/include/scx/common.bpf.h +++ b/tools/sched_ext/include/scx/common.bpf.h @@ -48,6 +48,7 @@ extern int LINUX_KERNEL_VERSION __kconfig; extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak; extern const char CONFIG_LOCALVERSION[64] __kconfig __weak; +extern bool CONFIG_PREEMPT_RCU __kconfig __weak; /* * Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can @@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym; bool scx_bpf_task_running(const struct task_struct *p) __ksym; s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym; +struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak; struct rq *scx_bpf_locked_rq(void) __ksym; struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak; struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak; @@ -111,6 +113,7 @@ s32 scx_bpf_this_cid(void) __ksym __weak; struct task_struct *scx_bpf_cid_curr(s32 cid) __ksym __weak; u32 scx_bpf_nr_cids(void) __ksym __weak; u32 scx_bpf_nr_online_cids(void) __ksym __weak; +const void __arena *scx_bpf_online_cmask(void) __ksym __weak; u32 scx_bpf_cidperf_cap(s32 cid) __ksym __weak; u32 scx_bpf_cidperf_cur(s32 cid) __ksym __weak; s32 scx_bpf_cidperf_set(s32 cid, u32 perf) __ksym __weak; @@ -528,31 +531,102 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask) } /* + * True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls + * migrate_disable() for the current task. Recorded once by + * scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during + * the natural scheduler-attach call chain (auto-attached by scx_ops_attach!). + * + * Defaults to true (conservative). Over-reporting in is_migration_disabled() + * causes local-only dispatch, which is safe. Under-reporting can crash the + * scheduler, so we err high if the probe somehow fails to run. + */ +bool __scx_prolog_disables_migration __weak = true; + +/* + * scx_lib_init_probe - non-sleepable prolog probe. + * + * Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops + * (kernel/sched/ext.c). The kernel's struct_ops machinery invokes + * bpf_scx_reg when userspace creates the scheduler link, before + * ops.init() fires. Its address is taken in the vtable, so the symbol + * is non-inlinable and has been stable since introduction. + * + * Entering via fentry runs us through __bpf_prog_enter -- the + * non-sleepable prolog that consumers of is_migration_disabled() live + * under. + * + * Loud warning: the prolog adds at most 1 to migration_disabled. + * Reading > 1 means something upstream in the + * bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration + * before the prolog ran, invalidating the probe; audit and adjust. + */ +SEC("fentry/bpf_scx_reg") __weak +int scx_lib_init_probe(void *ctx) +{ + if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) { + const struct task_struct *p = bpf_get_current_task_btf(); + unsigned int md = p->migration_disabled; + + if (md > 1) + bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u " + "upstream of BPF prolog; probe result unreliable", + md); + + __scx_prolog_disables_migration = md > 0; + } + return 0; +} + +/* * Return true if task @p cannot migrate to a different CPU, false * otherwise. + * + * IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable + * contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"), + * SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or + * __bpf_prog_enter_sleepable_recur(), both of which unconditionally + * call migrate_disable(); this helper can yield a false negative for + * p == current there, which can crash the scheduler. */ static inline bool is_migration_disabled(const struct task_struct *p) { /* - * Testing p->migration_disabled in a BPF code is tricky because the - * migration is _always_ disabled while running the BPF code. - * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF - * code execution disable and re-enable the migration of the current - * task, respectively. So, the _current_ task of the sched_ext ops is - * always migration-disabled. Moreover, p->migration_disabled could be - * two or greater when a sched_ext ops BPF code (e.g., ops.tick) is - * executed in the middle of the other BPF code execution. + * Testing p->migration_disabled in BPF is tricky because the BPF prolog + * (__bpf_prog_enter) may call migrate_disable() for the current task, + * making migration_disabled == 1 even for tasks that are not truly + * migration-disabled. + * + * Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for + * trampoline.c"), the BPF prolog calls migrate_disable() only when + * CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases: + * + * 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so + * migration_disabled == 1 for the current task is ambiguous. + * Disambiguate by checking p == current. + * + * 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls + * migrate_disable(), so migration_disabled == 1 is unambiguously + * a real migrate_disable() call. * - * Therefore, we should decide that the _current_ task is - * migration-disabled only when its migration_disabled count is greater - * than one. In other words, when p->migration_disabled == 1, there is - * an ambiguity, so we should check if @p is the current task or not. + * A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU, + * where the prolog historically called migrate_disable() unconditionally + * but a cherry-picked downstream kernel may not. The runtime-probed flag + * __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes + * the two cases without relying on the kernel version alone. */ if (bpf_core_field_exists(p->migration_disabled)) { - if (p->migration_disabled == 1) - return bpf_get_current_task_btf() != p; - else - return p->migration_disabled; + if (p->migration_disabled == 1) { + /* Fast path: prolog always disables migration */ + if (CONFIG_PREEMPT_RCU) + return bpf_get_current_task_btf() != p; + /* Fast path: prolog never disables migration */ + if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0)) + return true; + /* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */ + return __scx_prolog_disables_migration ? + bpf_get_current_task_btf() != p : true; + } + return p->migration_disabled; } return false; } @@ -1021,7 +1095,20 @@ static inline u64 scx_clock_task(u32 cpu) { struct rq___local *rq = get_current_rq(cpu); - /* Equivalent to the kernel's rq_clock_task(). */ + /* + * Equivalent to the kernel's rq_clock_task(): wall-clock time minus + * cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor + * steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs, + * it equals rq->clock. + * + * Conceptually this clock advances during idle (the idle task counts + * as a running task), but rq->clock_task is only updated on scheduling + * events. With NO_HZ_IDLE (the default), the periodic tick is stopped + * on idle CPUs, so rq->clock_task is not refreshed while a CPU is + * idle. Reading this clock for a remote idle CPU from a BPF timer + * callback returns the value from when the CPU last went idle, making + * the delta over an idle interval effectively zero. + */ return rq ? rq->clock_task : 0; } @@ -1032,9 +1119,23 @@ static inline u64 scx_clock_pelt(u32 cpu) /* * Equivalent to the kernel's rq_clock_pelt(): subtracts * lost_idle_time from clock_pelt to absorb the jump that occurs - * when clock_pelt resyncs with clock_task at idle exit. The result - * is a continuous, capacity-invariant clock safe for both task - * execution time stamping and cross-idle measurements. + * when clock_pelt resyncs with clock_task at idle exit. The intent + * is a continuous, capacity- and frequency-invariant clock that is + * frozen during idle, IRQ, and hypervisor steal. + * + * However, like scx_clock_task(), this clock has a stale-read issue + * for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt + * itself advances at wall-clock rate (hardware-clock based), but + * lost_idle_time is only updated via update_rq_clock_pelt(), which + * requires update_rq_clock() to be called. With NO_HZ_IDLE, the + * periodic tick is stopped on idle CPUs, so lost_idle_time is not + * refreshed during idle. Reading this clock for a remote idle CPU + * from a BPF timer callback therefore returns a value that drifts + * at wall-clock rate -- the same stale behaviour as scx_clock_task(). + * + * Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in + * sync (stale by at most one tick period, ~1 ms), so the result is + * accurate. */ return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0; } diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h index 3ab642f92c8a..6944221f96cc 100644 --- a/tools/sched_ext/include/scx/compat.bpf.h +++ b/tools/sched_ext/include/scx/compat.bpf.h @@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k /* * v6.19: Introduce lockless peek API for user DSQs. + * v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty + * FIFO DSQs (2f2ea7709266). * - * Preserve the following macro until v6.21. + * The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ + * before the v7.1 fix. Require kernel version >= 7.1.0 before calling it; + * otherwise fall through to the bpf_iter_scx_dsq fallback below. */ static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id) { struct task_struct *p = NULL; struct bpf_iter_scx_dsq it; - if (bpf_ksym_exists(scx_bpf_dsq_peek)) + if (bpf_ksym_exists(scx_bpf_dsq_peek) && + LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0)) return scx_bpf_dsq_peek(dsq_id); if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0)) p = bpf_iter_scx_dsq_next(&it); @@ -239,6 +244,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags) scx_bpf_pick_any_cpu(cpus_allowed, flags)) /* + * v6.18: Add a helper to retrieve the current task running on a CPU. + * + * The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in + * this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't + * resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until + * pre-v6.18 kernels fall out of the support window. + */ +static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu) +{ + struct rq *rq; + + if (bpf_ksym_exists(scx_bpf_cpu_curr)) + return scx_bpf_cpu_curr(cpu); + + rq = scx_bpf_cpu_rq(cpu); + + return rq ? rq->curr : NULL; +} + +/* * v6.19: To work around BPF maximum parameter limit, the following kfuncs are * replaced with variants that pack scalar arguments in a struct. Wrappers are * provided to maintain source compatibility. @@ -379,6 +404,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime) } /* + * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This + * will eventually deprecate scx_bpf_reenqueue_local(). + */ +void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak; + +static inline bool __COMPAT_has_generic_reenq(void) +{ + return bpf_ksym_exists(scx_bpf_dsq_reenq___compat); +} + +/* * v6.19: The new void variant can be called from anywhere while the older v1 * variant can only be called from ops.cpu_release(). The double ___ prefixes on * the v2 variant need to be removed once libbpf is updated to ignore ___ prefix @@ -395,21 +431,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void) static inline void scx_bpf_reenqueue_local(void) { - if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) + if (__COMPAT_has_generic_reenq()) + scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0); + else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) scx_bpf_reenqueue_local___v2___compat(); else scx_bpf_reenqueue_local___v1(); } -/* - * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This - * will eventually deprecate scx_bpf_reenqueue_local(). - */ -void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak; - -static inline bool __COMPAT_has_generic_reenq(void) +static inline int scx_bpf_reenqueue_local_from_anywhere(void) { - return bpf_ksym_exists(scx_bpf_dsq_reenq___compat); + /* + * The generic reenq kfunc and the v2 reenqueue-local variant can both be + * called from anywhere; v1 cannot. Test each ksym in its own branch with a + * distinct call: combining them with || would fold into a bitwise OR of the + * two ksym addresses, which the verifier rejects. + */ + if (__COMPAT_has_generic_reenq()) { + scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0); + return 0; + } + if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) { + scx_bpf_reenqueue_local___v2___compat(); + return 0; + } + return -EOPNOTSUPP; } static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags) diff --git a/tools/sched_ext/include/scx/compat.h b/tools/sched_ext/include/scx/compat.h index d2e4384df5af..7c12df45fdba 100644 --- a/tools/sched_ext/include/scx/compat.h +++ b/tools/sched_ext/include/scx/compat.h @@ -10,9 +10,14 @@ #include <bpf/btf.h> #include <bpf/libbpf.h> #include <fcntl.h> +#include <stdint.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> +#include "enums_abi.autogen.h" + struct btf *__COMPAT_vmlinux_btf __attribute__((weak)); static inline void __COMPAT_load_vmlinux_btf(void) @@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void) } } +/* + * Recover the true value of a 64-bit enum enumerator whose kernel BTF entry + * was truncated to its low 32 bits. + * + * Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode + * 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only + * carry the low 32 bits. This happens with pahole < 1.24, which predates + * ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's + * Container-Optimized OS / GKE kernels deliberately pass it for backward + * compatibility with older BTF consumers). The high bits + * can't be recovered from kernel BTF, so substitute the value from the + * vmlinux.h this tree was built against, cross-checked against the low 32 + * bits the kernel did provide. + * + * Note that this is a best-effort recovery, not a ground truth. The + * substitution assumes the running kernel agrees with this tree's vmlinux.h + * on the high 32 bits, but only the low 32 bits can actually be verified. + * The cross-check is vacuous for enumerators whose value has no low bits + * set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK, + * SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything, + * so those substitutions rest entirely on the high bits never moving. An + * enumerator missing from the table (a kernel newer than this tree's + * vmlinux.h, or a stale autogen table) can't be recovered at all. If a + * substitution is ever wrong, the scheduler operates on bogus values (e.g. + * dispatching to nonexistent DSQ ids or silently dropping flags) and can + * wildly malfunction, which is why the mismatch and table-miss paths refuse + * instead of guessing. + */ +static inline bool __COMPAT_recover_truncated_enum64(const char *type, + const char *name, + u32 lo32, u64 *v) +{ + static bool warned; + size_t i; + + for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) { + const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i]; + + if (strcmp(e->type, type) || strcmp(e->name, name)) + continue; + + if (e->val <= (u64)UINT32_MAX) { + *v = lo32; + return true; + } + + if ((u32)e->val != lo32) { + fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n", + type, name, lo32, (unsigned long long)e->val); + return false; + } + + if (!warned) { + fprintf(stderr, + "WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n" + "WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n" + "WARNING: scx enum values are truncated to their low 32 bits in kernel\n" + "WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n" + "WARNING: this binary was built against, cross-checked against the low\n" + "WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n" + "WARNING: verified: if the running kernel's actual values differ from\n" + "WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n" + "WARNING: kernel), the scheduler will operate on bogus values, such as\n" + "WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n"); + warned = true; + } + *v = e->val; + return true; + } + + /* + * Unknown enumerator (likely a stale autogen table). Fail + * pessimistically to avoid returning an invalid value. + */ + fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n", + type, name, lo32); + return false; +} + static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v) { const struct btf_type *t; @@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off); SCX_BUG_ON(!n, "btf__name_by_offset()"); if (!strcmp(n, name)) { + /* + * Try to recover a 64-bit enum from an 8-byte + * BTF_KIND_ENUM that was encoded without ENUM64 + * support (old pahole or + * --skip_encoding_btf_enum64). Only scx_* + * types are covered by the substitution table; + * non-scx types fall through to the raw value + * so this generic utility keeps working for + * them. + */ + if (t->size == 8 && !strncmp(type, "scx_", 4)) + return __COMPAT_recover_truncated_enum64(type, name, + (u32)e[i].val, v); *v = e[i].val; return true; } diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h index 19aa1de3e700..63b6b14b19bd 100644 --- a/tools/sched_ext/include/scx/enum_defs.autogen.h +++ b/tools/sched_ext/include/scx/enum_defs.autogen.h @@ -56,6 +56,10 @@ #define HAVE_SCX_DEQ_SLEEP #define HAVE_SCX_DEQ_CORE_SCHED_EXEC #define HAVE_SCX_DEQ_SCHED_CHANGE +#define HAVE_SCX_DSP_NONE +#define HAVE_SCX_DSP_LOCAL +#define HAVE_SCX_DSP_PREV +#define HAVE_SCX_DSP_RETRY #define HAVE_SCX_DSQ_FLAG_BUILTIN #define HAVE_SCX_DSQ_FLAG_LOCAL_ON #define HAVE_SCX_DSQ_INVALID @@ -188,7 +192,6 @@ #define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY #define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY #define HAVE_SCX_RQ_IN_WAKEUP -#define HAVE_SCX_RQ_IN_BALANCE #define HAVE_SCX_RQ_IN_DISPATCH #define HAVE_SCX_SCHED_PCPU_BYPASSING #define HAVE_SCX_SLICE_OOB_DUR_BITS diff --git a/tools/sched_ext/include/scx/enums_abi.autogen.h b/tools/sched_ext/include/scx/enums_abi.autogen.h new file mode 100644 index 000000000000..d53899764f5a --- /dev/null +++ b/tools/sched_ext/include/scx/enums_abi.autogen.h @@ -0,0 +1,223 @@ +/* + * WARNING: This file is autogenerated from gen_enum_defs.py [1]. + * + * scx enumerator values from the vmlinux.h this tree is built against. + * Used as the substitution source when the running kernel's BTF lacks + * BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated. + * + * [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py + */ + +#ifndef __ENUMS_ABI_AUTOGEN_H__ +#define __ENUMS_ABI_AUTOGEN_H__ + +struct __scx_enum_abi_val { + const char *type; + const char *name; + u64 val; +}; + +static const struct __scx_enum_abi_val __scx_enum_abi_vals[] + __attribute__((unused)) = { + { "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU }, + { "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU }, + { "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU }, + { "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU }, + { "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU }, + { "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU }, + { "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU }, + { "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU }, + { "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU }, + { "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU }, + { "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU }, + { "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU }, + { "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU }, + { "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU }, + { "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU }, + { "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU }, + { "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU }, + { "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU }, + { "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU }, + { "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU }, + { "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU }, + { "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU }, + { "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU }, + { "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU }, + { "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU }, + { "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU }, + { "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU }, + { "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU }, + { "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU }, + { "scx_consts", "SCX_TID_CHUNK", 0x400LLU }, + { "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU }, + { "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU }, + { "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU }, + { "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU }, + { "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU }, + { "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU }, + { "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU }, + { "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU }, + { "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU }, + { "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU }, + { "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU }, + { "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU }, + { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU }, + { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU }, + { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU }, + { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU }, + { "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU }, + { "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU }, + { "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU }, + { "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU }, + { "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU }, + { "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU }, + { "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU }, + { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU }, + { "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU }, + { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU }, + { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU }, + { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU }, + { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU }, + { "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU }, + { "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU }, + { "scx_enable_state", "SCX_ENABLING", 0x0LLU }, + { "scx_enable_state", "SCX_ENABLED", 0x1LLU }, + { "scx_enable_state", "SCX_DISABLING", 0x2LLU }, + { "scx_enable_state", "SCX_DISABLED", 0x3LLU }, + { "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU }, + { "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU }, + { "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU }, + { "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU }, + { "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU }, + { "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU }, + { "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU }, + { "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU }, + { "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU }, + { "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU }, + { "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU }, + { "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU }, + { "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU }, + { "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU }, + { "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU }, + { "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU }, + { "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU }, + { "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU }, + { "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU }, + { "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU }, + { "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU }, + { "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU }, + { "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU }, + { "scx_ent_flags", "SCX_TASK_READY", 0x300LLU }, + { "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU }, + { "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU }, + { "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU }, + { "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU }, + { "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU }, + { "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU }, + { "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU }, + { "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU }, + { "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU }, + { "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU }, + { "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU }, + { "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU }, + { "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU }, + { "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU }, + { "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU }, + { "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU }, + { "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU }, + { "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU }, + { "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU }, + { "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU }, + { "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU }, + { "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU }, + { "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU }, + { "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU }, + { "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU }, + { "scx_opi", "SCX_OPI_BEGIN", 0x0LLU }, + { "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU }, + { "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU }, + { "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU }, + { "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU }, + { "scx_opi", "SCX_OPI_END", 0x23LLU }, + { "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU }, + { "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU }, + { "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU }, + { "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU }, + { "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU }, + { "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU }, + { "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU }, + { "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU }, + { "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU }, + { "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU }, + { "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU }, + { "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU }, + { "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU }, + { "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU }, + { "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU }, + { "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU }, + { "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU }, + { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU }, + { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU }, + { "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU }, + { "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU }, + { "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU }, + { "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU }, + { "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU }, + { "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU }, + { "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU }, + { "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU }, + { "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU }, + { "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU }, + { "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU }, + { "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU }, + { "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU }, + { "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU }, + { "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU }, + { "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU }, + { "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU }, + { "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU }, + { "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU }, + { "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU }, + { "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU }, + { "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU }, + { "scx_tg_flags", "SCX_TG_INITED", 0x2LLU }, + { "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU }, + { "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU }, + { "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU }, + { "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU }, +}; + +#endif /* __ENUMS_ABI_AUTOGEN_H__ */ diff --git a/tools/sched_ext/scx_central.bpf.c b/tools/sched_ext/scx_central.bpf.c index 64dd60b3e922..65dae9e45400 100644 --- a/tools/sched_ext/scx_central.bpf.c +++ b/tools/sched_ext/scx_central.bpf.c @@ -299,6 +299,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer) u64 now = scx_bpf_now(); u64 nr_to_kick = nr_queued; s32 i, curr_cpu; + int ret; curr_cpu = bpf_get_smp_processor_id(); if (timer_pinned && (curr_cpu != central_cpu)) { @@ -332,7 +333,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer) scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT); } - bpf_timer_start(timer, TIMER_INTERVAL_NS, BPF_F_TIMER_CPU_PIN); + ret = bpf_timer_start(timer, TIMER_INTERVAL_NS, + timer_pinned ? BPF_F_TIMER_CPU_PIN : 0); + if (ret) + scx_bpf_error("bpf_timer_start failed (%d)", ret); __sync_fetch_and_add(&nr_timers, 1); return 0; } diff --git a/tools/sched_ext/scx_flatcg.bpf.c b/tools/sched_ext/scx_flatcg.bpf.c index 64cf4dd964d6..454ebb820c5e 100644 --- a/tools/sched_ext/scx_flatcg.bpf.c +++ b/tools/sched_ext/scx_flatcg.bpf.c @@ -937,7 +937,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p, if (!(from_cgc = find_cgrp_ctx(from)) || !(to_cgc = find_cgrp_ctx(to))) return; - delta = time_delta(p->scx.dsq_vtime, from_cgc->tvtime_now); + delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now); scx_bpf_task_set_dsq_vtime(p, to_cgc->tvtime_now + delta); } diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c index 5bb8b90a275a..67b7c01cae55 100644 --- a/tools/sched_ext/scx_qmap.bpf.c +++ b/tools/sched_ext/scx_qmap.bpf.c @@ -24,6 +24,9 @@ * time-share that stays self-local. * self - The excl cpus the node kept for itself, plus all of held_shared. * owner - Who holds a cid - a child slot, CID_SELF, or CID_NONE. + * avail - Cpus whose caps are in effect, per ops.sub_ecaps_updated(). + * usable - self AND avail. Placement decisions use this: self is the + * delegation split and can run ahead of what the cpus honor. * * The scheduler splits its held-excl cpus among self and the children in * proportion to each node's cpu.weight, handing each the floor of its share as @@ -208,8 +211,8 @@ static int qmap_spin_lock(struct bpf_res_spin_lock *lock) } /* - * Try prev_cid, then scan cpus_allowed AND idle_cids AND self_cids round-robin - * from prev_cid + 1. Atomic claim retries on race; bounded by + * Try prev_cid, then scan cpus_allowed AND idle_cids AND usable_cids + * round-robin from prev_cid + 1. Atomic claim retries on race; bounded by * IDLE_PICK_RETRIES to keep the verifier's insn budget in check. */ #define IDLE_PICK_RETRIES 16 @@ -221,7 +224,7 @@ static s32 pick_direct_dispatch_cid(struct task_struct *p, s32 prev_cid, s32 cid; u32 i; - if (cmask_test(prev_cid, &qa.self_cids.mask) && + if (cmask_test(prev_cid, &qa.usable_cids.mask) && cmask_test_and_clear(prev_cid, &qa.idle_cids.mask)) return prev_cid; @@ -229,7 +232,7 @@ static s32 pick_direct_dispatch_cid(struct task_struct *p, s32 prev_cid, bpf_for(i, 0, IDLE_PICK_RETRIES) { cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed, &qa.idle_cids.mask, - &qa.self_cids.mask, cid + 1); + &qa.usable_cids.mask, cid + 1); barrier_var(cid); if (cid >= nr_cids) return -1; @@ -358,8 +361,8 @@ s32 BPF_STRUCT_OPS(qmap_select_cid, struct task_struct *p, } /* - * A received time-shared cid is held ENQ_IMMED-only, so inserts must set - * SCX_ENQ_IMMED. + * A received time-shared cid is held ENQ_IMMED-only, so inserts meant to run + * there must set SCX_ENQ_IMMED. */ static u64 needs_immed(s32 cid) { @@ -444,9 +447,11 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags) * didn't grant them or we delegated them to children - would starve in * SHARED/FIFO since we only pull from those on self cids. * - * Force it onto its first allowed cid's local DSQ. If we hold that cid - * it runs. Otherwise the insert carries SCX_ENQ_RESCUE and the kernel - * diverts the task to its rescue path. + * Force it onto its first allowed cid's local DSQ with SCX_ENQ_RESCUE. + * If we hold ENQ on that cid it runs. Otherwise the kernel diverts the + * task to its rescue path. IMMED would turn the insert into a legal + * placement on a time-shared cid and the kernel would bounce it back + * here instead of rescuing it. */ if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) { s32 c = cmask_next_set_wrap(&taskc->cpus_allowed, 0); @@ -455,7 +460,7 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags) taskc->force_local = false; __sync_fetch_and_add(&qa.nr_rescue_dsp, 1); scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | c, slice_ns, - enq_flags | needs_immed(c) | SCX_ENQ_RESCUE); + enq_flags | SCX_ENQ_RESCUE); return; } } @@ -540,7 +545,7 @@ void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags) scx_bpf_dsq_insert(p, SHARED_DSQ, 0, enq_flags); cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed, &qa.idle_cids.mask, - &qa.self_cids.mask, 0); + &qa.usable_cids.mask, 0); if (cid < scx_bpf_nr_cids()) scx_bpf_kick_cid(cid, SCX_KICK_IDLE); return; @@ -618,7 +623,7 @@ static bool scan_shared_dsq(bool from_timer) if (c >= 0 && c < scx_bpf_nr_cids()) { __sync_fetch_and_add(&qa.nr_rescue_dsp, 1); scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c, - needs_immed(c) | SCX_ENQ_RESCUE); + SCX_ENQ_RESCUE); } continue; } @@ -644,22 +649,27 @@ static bool scan_shared_dsq(bool from_timer) if (!(taskc = lookup_task_ctx(p))) return false; - /* only run highpri tasks on cids this node holds, not delegated ones */ + /* only run highpri tasks on cids this node can use right now */ if (cmask_test(this_cid, &taskc->cpus_allowed) && - cmask_test(this_cid, &qa.self_cids.mask)) + cmask_test(this_cid, &qa.usable_cids.mask)) cid = this_cid; else cid = cmask_next_and_set_wrap(&taskc->cpus_allowed, - &qa.self_cids.mask, + &qa.usable_cids.mask, this_cid + 1); if (cid >= nr_cids) { - /* stranded after the cull - rescue it from here */ - s32 c = cmask_next_set_wrap(&taskc->cpus_allowed, 0); + s32 c; + + /* self cids lack caps in effect yet, leave it queued */ + if (cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) + continue; + /* stranded after the cull - rescue it from here */ + c = cmask_next_set_wrap(&taskc->cpus_allowed, 0); if (c >= 0 && c < nr_cids) { __sync_fetch_and_add(&qa.nr_rescue_dsp, 1); scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c, - needs_immed(c) | SCX_ENQ_RESCUE); + SCX_ENQ_RESCUE); } continue; } @@ -808,10 +818,10 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev) batch--; cpuc->dsp_cnt--; if (!batch || !scx_bpf_dispatch_nr_slots()) { - if (scan_shared_dsq(false)) + if (scan_shared_dsq(false) || + scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid))) return; - scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)); - return; + goto prev; } if (!cpuc->dsp_cnt) break; @@ -822,10 +832,14 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev) if (scan_shared_dsq(false)) return; - +prev: /* * No other tasks. @prev will keep running. Update its core_sched_seq as * if the task were enqueued and dispatched immediately. + * + * No @prev to keep running means the CPU goes idle. If its claim was + * never used, that is not a transition and ops.update_idle() stays + * silent. Restore the claim here. */ if (prev) { taskc = lookup_task_ctx(prev); @@ -834,6 +848,8 @@ void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev) taskc->core_sched_seq = qa.core_sched_tail_seqs[weight_to_idx(prev->scx.weight)]++; + } else { + cmask_set(cid, &qa.idle_cids.mask); } } @@ -1113,7 +1129,7 @@ void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle) /* * The kernel delivers update_idle() for every cid this node holds * SCX_CAP_BASE on. Track every cid's idle state regardless of - * delegation: the direct-dispatch pick masks idle_cids with self_cids + * delegation: the direct-dispatch pick masks idle_cids with usable_cids * at selection, so a cid already idle when it returns to self needs no * reseed here. */ @@ -1246,7 +1262,8 @@ static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer) scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE)); } - bpf_timer_start(timer, ONE_SEC_IN_NS, 0); + if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0)) + scx_bpf_error("failed to re-arm stats timer"); return 0; } @@ -1268,7 +1285,8 @@ struct { static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer) { scx_bpf_dsq_reenq(LOWPRI_DSQ, 0); - bpf_timer_start(timer, LOWPRI_INTV_NS, 0); + if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0)) + scx_bpf_error("failed to re-arm lowpri timer"); return 0; } @@ -1283,11 +1301,16 @@ struct { __type(value, struct round_robin_timer); } round_robin_timer SEC(".maps"); +enum part_pending_flags { + PART_REFRESH = BIT_U64(0), + PART_REDISTRIBUTE = BIT_U64(1), +}; + /* * Partition update synchronization. qa.part can be written from concurrent * contexts. This single-runner guard admits one writer at a time without * holding a lock across the grant/revoke kfuncs. part_pending coalesces - * repartition requests that arrive while it is held. + * refresh and repartition requests that arrive while it is held. * * They live in .bss, not the arena: rr_advance() runs from a bpf_timer * callback, where the verifier rejects atomic ops on arena memory. @@ -1536,6 +1559,19 @@ static __noinline void account_alloc(void) } /* + * usable_cids = self_cids & avail_cids. The inputs have separate writers, + * apply_partition() and qmap_sub_ecaps_updated(), so the result is rebuilt in + * full under the partition guard, in scratch first so that readers never see + * self_cids alone. + */ +static void refresh_usable(void) +{ + cmask_copy(&qa.usable_scratch.mask, &qa.self_cids.mask); + cmask_and(&qa.usable_scratch.mask, &qa.avail_cids.mask); + cmask_copy(&qa.usable_cids.mask, &qa.usable_scratch.mask); +} + +/* * apply_partition - execute the plan compute_partition() built * * Turn the owner map into the per-child, shared and self cmasks and issue the @@ -1557,6 +1593,7 @@ __noinline void apply_partition(void) /* no excl cpu: run own tasks on the held shares, evict children */ if (!qa.part.nr_excl) { cmask_copy(&qa.self_cids.mask, &qa.held_shared.mask); + refresh_usable(); bpf_for(i, 0, MAX_SUB_SCHEDS) if (qa.sub_sched_ctxs[i].cgroup_id) scx_bpf_sub_kill(qa.sub_sched_ctxs[i].cgroup_id, @@ -1594,6 +1631,7 @@ __noinline void apply_partition(void) else if (o == CID_SELF) cmask_set(cid, &qa.self_cids.mask); } + refresh_usable(); /* * Apply each child's exclusive cids as a delta against its previous @@ -1641,33 +1679,46 @@ __noinline void apply_partition(void) } } -/* - * Recompute the split off the node's held caps and apply it. The contexts this - * runs from (the sub-sched and cgroup callbacks, the rr timer) are not - * serialized by the kernel, so a single runner does the work. A caller that - * finds the guard held leaves part_pending set; the holder drains it before - * releasing, with the rr timer as a backstop. +/** + * execute_partition - Run pending partition updates + * + * The rr timer is the backstop if the loop reaches its iteration limit. */ -static void redistribute(void) +static void execute_partition(void) { + u64 pending; s32 i; - __sync_fetch_and_or(&part_pending, 1); + bpf_for(i, 0, 1024) { + if (!part_try_start()) + break; - if (!part_try_start()) - return; + pending = __sync_fetch_and_and(&part_pending, 0); + if (pending & PART_REDISTRIBUTE) { + /* charge elapsed time before repartitioning */ + account_alloc(); + compute_partition(); + apply_partition(); + } else if (pending & PART_REFRESH) { + refresh_usable(); + } - bpf_for(i, 0, 1024) { - __sync_fetch_and_and(&part_pending, 0); - /* charge elapsed time to the current partition before rebuilding it */ - account_alloc(); - compute_partition(); - apply_partition(); + /* + * Requests are published before trying the guard. Releasing it + * before checking pending work ensures a racing request is + * either observed here or handled by a caller that acquires the + * guard. + */ + part_end(); if (!__sync_fetch_and_or(&part_pending, 0)) break; } +} - part_end(); +static void redistribute(void) +{ + __sync_fetch_and_or(&part_pending, PART_REDISTRIBUTE); + execute_partition(); } /* @@ -1681,6 +1732,7 @@ int flush_alloc(void *ctx) if (part_try_start()) { account_alloc(); part_end(); + execute_partition(); } return 0; } @@ -1738,16 +1790,15 @@ static void rr_advance(void) part_end(); - /* a resplit queued while we held the guard supersedes this rotation */ - if (__sync_fetch_and_or(&part_pending, 0)) - redistribute(); + execute_partition(); } /* advance the time-shared cid pool every round_robin_ns */ static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer) { rr_advance(); - bpf_timer_start(timer, round_robin_ns, 0); + if (bpf_timer_start(timer, round_robin_ns, 0)) + scx_bpf_error("failed to re-arm round-robin timer"); return 0; } @@ -1834,8 +1885,11 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init) cmask_init(&qa.rr_cids.mask, 0, nr_cids); cmask_init(&qa.prev_rr_cids.mask, 0, nr_cids); cmask_init(&qa.self_cids.mask, 0, nr_cids); + cmask_init(&qa.avail_cids.mask, 0, nr_cids); + cmask_init(&qa.usable_cids.mask, 0, nr_cids); cmask_init(&qa.to_revoke_cids.mask, 0, nr_cids); cmask_init(&qa.to_grant_cids.mask, 0, nr_cids); + cmask_init(&qa.usable_scratch.mask, 0, nr_cids); cmask_init(&qa.held_excl.mask, 0, nr_cids); cmask_init(&qa.held_shared.mask, 0, nr_cids); @@ -1849,14 +1903,16 @@ s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init) } /* - * The root starts holding every cid. qmap_sub_ecaps_updated() maintains - * per-cid shared state as effective caps settle, and redistribute() - * rebuilds owner and self from held caps. A non-root node starts with - * nothing. + * The root starts holding every cid and gets no ecaps notifications, so + * its avail set is fixed here. qmap_sub_ecaps_updated() maintains the + * per-cid state as effective caps settle, and redistribute() rebuilds + * owner and self from held caps. A non-root node starts with nothing. */ bpf_for(i, 0, nr_cids) { if (!sub_cgroup_id) { cmask_set(i, &qa.self_cids.mask); + cmask_set(i, &qa.avail_cids.mask); + cmask_set(i, &qa.usable_cids.mask); qa.part.cid_owner[i] = CID_SELF; } else { qa.part.cid_owner[i] = CID_NONE; @@ -1997,12 +2053,19 @@ void BPF_STRUCT_OPS(qmap_sub_ecaps_updated, s32 cid, u64 before, u64 after) { /* * Effective caps updated. Track which cids hold shared caps so a self - * task placed there enqueues IMMED. + * task placed there enqueues IMMED, and which cids have ENQ_IMMED in + * effect at all (avail, see the header comment). */ - if (after & SCX_CAP_ENQ_IMMED) + if (after & SCX_CAP_ENQ_IMMED) { qa.cid_shared[cid] = (after & SCX_CAP_ENQ) ? 0 : 1; - else + cmask_set(cid, &qa.avail_cids.mask); + } else { qa.cid_shared[cid] = 0; + cmask_clear(cid, &qa.avail_cids.mask); + } + + __sync_fetch_and_or(&part_pending, PART_REFRESH); + execute_partition(); } SCX_OPS_CID_DEFINE(qmap_ops, diff --git a/tools/sched_ext/scx_qmap.h b/tools/sched_ext/scx_qmap.h index c78d61806b39..e95fffcf7b23 100644 --- a/tools/sched_ext/scx_qmap.h +++ b/tools/sched_ext/scx_qmap.h @@ -165,12 +165,15 @@ struct qmap_arena { /* bpf-internal cmasks (embedded, see struct qmap_cmask) */ struct qmap_cmask self_cids; /* cids this node runs its own tasks on */ + struct qmap_cmask avail_cids; /* cids with caps in effect on the cpu */ + struct qmap_cmask usable_cids; /* self_cids & avail_cids, placeable right now */ struct qmap_cmask idle_cids; /* idle state of all cids regardless of delegation */ struct qmap_cmask rr_cids; /* the shared pool, as a mask for grant/revoke */ /* scratch cmasks */ struct qmap_cmask to_revoke_cids; /* delta cids to revoke */ struct qmap_cmask to_grant_cids; /* delta cids to grant */ + struct qmap_cmask usable_scratch; /* refresh_usable() build area */ struct qmap_cmask prev_rr_cids; /* previous shared pool, to clear stale grants */ struct qmap_cmask held_excl; /* cids held excl (ENQ): delegatable */ struct qmap_cmask held_shared; /* cids held shared (ENQ_IMMED only): self-local */ diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index a329f901c5ed..0857d64c322a 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -319,8 +319,8 @@ static bool ctl_value_index_valid(struct ctl_data *ctl, /* Only check step size if there is one and we're in bounds */ if (snd_ctl_elem_info_get_step(ctl->info) && - (int_val - snd_ctl_elem_info_get_min(ctl->info) % - snd_ctl_elem_info_get_step(ctl->info))) { + (int_val - snd_ctl_elem_info_get_min(ctl->info)) % + snd_ctl_elem_info_get_step(ctl->info)) { ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", ctl->name, index, int_val, snd_ctl_elem_info_get_step(ctl->info), diff --git a/tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c b/tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c new file mode 100644 index 000000000000..3248bccc3557 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/btf_map_keyless.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <bpf/btf.h> + +/* + * A hash map with a key-less BTF (btf_key_type_id == 0) used to be accepted + * and then NULL-deref in btf_type_show() when dumped through bpffs. A fixed + * kernel rejects it at creation; verify that rejection, with a keyed positive + * control so the -EINVAL is about the missing key type and not some unrelated + * failure. + */ +static void check_keyless(int map_type, __u32 map_flags, int btf_fd, int val_id) +{ + LIBBPF_OPTS(bpf_map_create_opts, opts); + int map_fd; + + opts.map_flags = map_flags; + opts.btf_fd = btf_fd; + opts.btf_value_type_id = val_id; + + /* Positive control: the same map with a real key type is accepted. */ + opts.btf_key_type_id = val_id; + map_fd = bpf_map_create(map_type, "keyed_map", 4, 4, 8, &opts); + if (!ASSERT_GE(map_fd, 0, "keyed create is accepted")) + return; + close(map_fd); + + /* A key-less BTF must be rejected. */ + opts.btf_key_type_id = 0; + map_fd = bpf_map_create(map_type, "keyless_map", 4, 4, 8, &opts); + ASSERT_EQ(map_fd, -EINVAL, "key-less create is rejected"); + if (map_fd >= 0) + close(map_fd); +} + +void test_btf_map_keyless(void) +{ + int btf_fd, val_id; + struct btf *btf; + + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "btf__new_empty")) + return; + + val_id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); + if (!ASSERT_GT(val_id, 0, "btf__add_int")) + goto out; + + if (!ASSERT_OK(btf__load_into_kernel(btf), "btf__load_into_kernel")) + goto out; + btf_fd = btf__fd(btf); + + if (test__start_subtest("hash")) + check_keyless(BPF_MAP_TYPE_HASH, 0, btf_fd, val_id); + if (test__start_subtest("rhash")) + check_keyless(BPF_MAP_TYPE_RHASH, BPF_F_NO_PREALLOC, btf_fd, val_id); +out: + btf__free(btf); +} diff --git a/tools/testing/selftests/bpf/prog_tests/rhash.c b/tools/testing/selftests/bpf/prog_tests/rhash.c index 98bb66907b7f..0641bd5b0a9e 100644 --- a/tools/testing/selftests/bpf/prog_tests/rhash.c +++ b/tools/testing/selftests/bpf/prog_tests/rhash.c @@ -172,6 +172,12 @@ void test_rhash(void) if (test__start_subtest("test_rhash_delete_nonexistent")) rhash_run("test_rhash_delete_nonexistent"); + if (test__start_subtest("test_rhash_kptr_update")) + rhash_run("test_rhash_kptr_update"); + + if (test__start_subtest("test_rhash_kptr_delete")) + rhash_run("test_rhash_kptr_delete"); + if (test__start_subtest("test_rhash_map_extra_presize")) rhash_map_extra_presize(); diff --git a/tools/testing/selftests/bpf/prog_tests/rhash_timer.c b/tools/testing/selftests/bpf/prog_tests/rhash_timer.c new file mode 100644 index 000000000000..3aad9fc02e06 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/rhash_timer.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <sched.h> + +#include <test_progs.h> +#include "rhash_timer.skel.h" + +#define MAX_ATTEMPTS 256 +#define RCU_SYNC_INTERVAL 64 + +static int pin_to_first_cpu(cpu_set_t *old_mask) +{ + cpu_set_t new_mask; + int cpu; + + if (sched_getaffinity(0, sizeof(*old_mask), old_mask)) + return -errno; + + for (cpu = 0; cpu < CPU_SETSIZE; cpu++) + if (CPU_ISSET(cpu, old_mask)) + break; + if (cpu == CPU_SETSIZE) + return -EINVAL; + + CPU_ZERO(&new_mask); + CPU_SET(cpu, &new_mask); + if (sched_setaffinity(0, sizeof(new_mask), &new_mask)) + return -errno; + return 0; +} + +static int update_timer_map(int map_fd, __u64 key) +{ + __u64 value[3] = {}; + + return bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST); +} + +static int run_prog(int prog_fd, struct bpf_test_run_opts *opts) +{ + int err; + + err = bpf_prog_test_run_opts(prog_fd, opts); + if (err) + return err; + return opts->retval; +} + +void test_rhash_timer(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + struct rhash_timer *skel = NULL; + cpu_set_t old_mask; + int map_fd = -1, arm_fd, cancel_fd; + bool affinity_set = false; + __u64 key = 1; + int attempt, err; + + err = pin_to_first_cpu(&old_mask); + if (!ASSERT_OK(err, "pin_to_first_cpu")) + return; + affinity_set = true; + + skel = rhash_timer__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + goto out; + + map_fd = bpf_map__fd(skel->maps.timer_map); + if (!ASSERT_GE(map_fd, 0, "timer_map fd")) + goto out; + arm_fd = bpf_program__fd(skel->progs.arm_deleted_timer); + if (!ASSERT_GE(arm_fd, 0, "arm_deleted_timer fd")) + goto out; + cancel_fd = bpf_program__fd(skel->progs.cancel_recycled_timer); + if (!ASSERT_GE(cancel_fd, 0, "cancel_recycled_timer fd")) + goto out; + + err = update_timer_map(map_fd, key); + if (!ASSERT_OK(err, "seed_timer_map")) + goto out; + + for (attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { + err = run_prog(arm_fd, &opts); + if (err) { + ASSERT_OK(err, "arm_deleted_timer"); + goto out; + } + if (skel->bss->armed != attempt + 1) { + ASSERT_EQ(skel->bss->armed, attempt + 1, "armed"); + goto out; + } + if (skel->bss->timer_init_err) { + ASSERT_OK(skel->bss->timer_init_err, "timer_init_err"); + goto out; + } + if (skel->bss->timer_set_callback_err) { + ASSERT_OK(skel->bss->timer_set_callback_err, + "timer_set_callback_err"); + goto out; + } + if (skel->bss->timer_start_err) { + ASSERT_OK(skel->bss->timer_start_err, "timer_start_err"); + goto out; + } + + if ((attempt + 1) % RCU_SYNC_INTERVAL == 0) { + err = kern_sync_rcu(); + if (err) { + ASSERT_OK(err, "kern_sync_rcu"); + goto out; + } + } + + err = update_timer_map(map_fd, ++key); + if (err) { + ASSERT_OK(err, "replace_timer_map"); + goto out; + } + + err = run_prog(cancel_fd, &opts); + if (err) { + ASSERT_OK(err, "cancel_recycled_timer"); + goto out; + } + if (skel->bss->timer_cancel_err) { + ASSERT_OK(skel->bss->timer_cancel_err, "timer_cancel_err"); + goto out; + } + if (skel->bss->cancelled) + break; + } + + ASSERT_GT(skel->bss->cancelled, 0, "preserved timer"); +out: + if (map_fd >= 0) + bpf_map_delete_elem(map_fd, &key); + rhash_timer__destroy(skel); + if (affinity_set) + sched_setaffinity(0, sizeof(old_mask), &old_mask); +} diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c b/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c index dd41b826be30..edce9c1b54fb 100644 --- a/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c +++ b/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 #include <test_progs.h> #include <linux/btf.h> +#include <bpf/btf.h> #include "netif_receive_skb.skel.h" +#include "snprintf_btf_void.skel.h" /* Demonstrate that bpf_snprintf_btf succeeds and that various data types * are formatted correctly. @@ -58,3 +60,80 @@ void serial_test_snprintf_btf(void) cleanup: netif_receive_skb__destroy(skel); } + +/* + * bpf_snprintf_btf() renders a type_id taken straight from the vmlinux BTF. + * Two such type_ids used to NULL-deref in the BTF show path: + * - a "const void" (a modifier resolving to void) in btf_modifier_show() + * - a BTF_KIND_VAR in btf_var_show() (base BTF has no resolved_ids) + * A fixed kernel renders both without crashing. + */ +static long run(struct snprintf_btf_void *skel, __u32 type_id) +{ + LIBBPF_OPTS(bpf_test_run_opts, topts); + char ctx[8] = {}; + + skel->bss->type_id = type_id; + topts.ctx_in = ctx; + topts.ctx_size_in = sizeof(ctx); + if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.dump_type), + &topts), "test_run")) + return -1; + return skel->bss->ret; +} + +void test_snprintf_btf_void(void) +{ + const struct btf_type *t; + struct snprintf_btf_void *skel; + int i, n, cv = 0, var = 0; + struct btf *btf; + + btf = btf__parse("/sys/kernel/btf/vmlinux", NULL); + if (!btf) { + test__skip(); + return; + } + + skel = snprintf_btf_void__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + goto out_btf; + + n = btf__type_cnt(btf); + for (i = 1; i < n && !(cv && var); i++) { + t = btf__type_by_id(btf, i); + if (!cv && btf_kind(t) == BTF_KIND_CONST && t->type == 0) + cv = i; + /* Pick a VAR small enough to render from the program's buffer. */ + if (!var && btf_kind(t) == BTF_KIND_VAR) { + long sz = btf__resolve_size(btf, t->type); + + if (sz > 0 && sz <= (long)sizeof(skel->bss->obj)) + var = i; + } + } + + /* "const void" renders the "<unsupported kind:0>" placeholder. */ + if (test__start_subtest("const_void")) { + if (cv) { + ASSERT_EQ(run(skel, cv), + sizeof("<unsupported kind:0>") - 1, "ret"); + ASSERT_STREQ(skel->bss->out, "<unsupported kind:0>", + "placeholder"); + } else { + test__skip(); + } + } + + /* A BTF_KIND_VAR must resolve and render without error. */ + if (test__start_subtest("var")) { + if (var) + ASSERT_GT(run(skel, var), 0, "ret"); + else + test__skip(); + } + + snprintf_btf_void__destroy(skel); +out_btf: + btf__free(btf); +} diff --git a/tools/testing/selftests/bpf/prog_tests/timer_mim.c b/tools/testing/selftests/bpf/prog_tests/timer_mim.c index c930c7d7105b..fa7bb769ca31 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer_mim.c +++ b/tools/testing/selftests/bpf/prog_tests/timer_mim.c @@ -59,10 +59,32 @@ void serial_test_timer_mim(void) int err; old_print_fn = libbpf_set_print(NULL); - timer_reject_skel = timer_mim_reject__open_and_load(); - libbpf_set_print(old_print_fn); - if (!ASSERT_ERR_PTR(timer_reject_skel, "timer_reject_skel_load")) + timer_reject_skel = timer_mim_reject__open(); + if (!ASSERT_OK_PTR(timer_reject_skel, "timer_reject_skel_open")) + goto cleanup; + bpf_program__set_autoload(timer_reject_skel->progs.test1, true); + err = timer_mim_reject__load(timer_reject_skel); + ASSERT_ERR(err, "timer_reject_skel_load"); + timer_mim_reject__destroy(timer_reject_skel); + + timer_reject_skel = timer_mim_reject__open(); + if (!ASSERT_OK_PTR(timer_reject_skel, "callback_reject_skel_open")) goto cleanup; + bpf_program__set_autoload(timer_reject_skel->progs.callback_map_uid_mismatch, true); + err = timer_mim_reject__load(timer_reject_skel); + ASSERT_ERR(err, "callback_reject_skel_load"); + timer_mim_reject__destroy(timer_reject_skel); + + timer_reject_skel = timer_mim_reject__open(); + if (!ASSERT_OK_PTR(timer_reject_skel, "callback_accept_skel_open")) + goto cleanup; + bpf_program__set_autoload(timer_reject_skel->progs.callback_map_uid_match, true); + err = timer_mim_reject__load(timer_reject_skel); + if (!ASSERT_OK(err, "callback_accept_skel_load")) + goto cleanup; + timer_mim_reject__destroy(timer_reject_skel); + timer_reject_skel = NULL; + libbpf_set_print(old_print_fn); timer_skel = timer_mim__open_and_load(); if (!timer_skel && errno == EOPNOTSUPP) { @@ -75,6 +97,7 @@ void serial_test_timer_mim(void) err = timer_mim(timer_skel); ASSERT_OK(err, "timer_mim"); cleanup: + libbpf_set_print(old_print_fn); timer_mim__destroy(timer_skel); timer_mim_reject__destroy(timer_reject_skel); } diff --git a/tools/testing/selftests/bpf/progs/iters.c b/tools/testing/selftests/bpf/progs/iters.c index 62d7df9e80be..c6699159dacd 100644 --- a/tools/testing/selftests/bpf/progs/iters.c +++ b/tools/testing/selftests/bpf/progs/iters.c @@ -2149,4 +2149,43 @@ __naked int stack_misc_vs_scalar_in_a_loop(void) ); } +__used +static int loop_cb5(int i, __u64 *ctx) +{ + /* unsafe on a second iteration */ + small_arr[*ctx] = i; + *ctx = 100500; + return 0; +} + +SEC("raw_tp") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("memory access is {{.*}} and is outside of the object of size 64") +__naked void loop_counter_precision_2nd_iter(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "*(u64 *)(r10 - 8) = 0;" + "r1 = 2;" + "if r0 == 42 goto +1;" + "r1 = 1;" + "r2 = loop_cb5 ll;" + "r3 = r10;" + "r3 += -8;" + "r4 = 0;" + /* + * Explore with nr_loops=1 on a first path and nr_loops=2 on a second path. + * Buggy verifier did not propagate r1 precision properly, + * and thus checkpoints created for nr_loops=1 case matched nr_loops=2 case. + */ + "call %[bpf_loop];" + "r0 = 0;" + "exit;" + : + : __imm(bpf_loop), + __imm(bpf_get_prandom_u32) + : __clobber_all + ); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c index 5e25ca806060..eee35d203b66 100644 --- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c +++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c @@ -409,4 +409,41 @@ int reject_scalar_store_to_kptr(struct __sk_buff *ctx) return 0; } +SEC("?tc") +__description("reject imprecise scalar store to kptr after state pruning") +__failure __msg("invalid kptr access, R7 type=scalar") +__naked void reject_imprecise_scalar_store_to_kptr(void) +{ + asm volatile ( + "r0 = 0;" + "*(u32 *)(r10 - 4) = r0;" + "r2 = r10;" + "r2 += -4;" + "r1 = %[array_map] ll;" + "call %[bpf_map_lookup_elem];" + "if r0 == 0 goto l2_%=;" + "r6 = r0;" + "r9 = *(u64 *)(r6 + 0);" + "if r9 != 0 goto l0_%=;" + "r7 = 0;" + ".rept 10;" + "r5 = 1;" + ".endr;" + "goto l1_%=;" + "l0_%=:" + "r7 = 0x4141414141414141 ll;" + ".rept 10;" + "r5 = 1;" + ".endr;" + "l1_%=:" + "*(u64 *)(r6 + 8) = r7;" + "l2_%=:" + "r0 = 0;" + "exit;" + : + : __imm(bpf_map_lookup_elem), + __imm_addr(array_map) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c b/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c index 08379c3b6a03..3701f4ea58c7 100644 --- a/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c +++ b/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c @@ -33,6 +33,20 @@ struct { __type(value, struct elem); } array SEC(".maps"); +struct kernel_percpu_elem { + struct task_struct __percpu_kptr *task; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct kernel_percpu_elem); +} kernel_percpu_array SEC(".maps"); + +struct task_struct *bpf_task_from_pid(s32 pid) __ksym; +void bpf_task_release(struct task_struct *p) __ksym; + long ret; SEC("?fentry/bpf_fentry_test1") @@ -137,6 +151,51 @@ int BPF_PROG(test_array_map_5) return 0; } +SEC("?syscall") +__failure __msg("invalid kptr access, R2 type=trusted_ptr_ expected=ptr_task_struct") +int reject_kernel_ptr_into_percpu_kptr(void *ctx) +{ + struct kernel_percpu_elem *e; + struct task_struct *p, *old; + int index = 0; + + e = bpf_map_lookup_elem(&kernel_percpu_array, &index); + if (!e) + return 0; + + p = bpf_task_from_pid(1); + if (!p) + return 0; + + old = bpf_kptr_xchg(&e->task, p); + if (old) + bpf_task_release(old); + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("invalid kptr access, R2 type=ptr_ expected=ptr_val_t") +int BPF_PROG(reject_plain_alloc_into_percpu_kptr) +{ + struct val_t __percpu_kptr *old; + struct val_t *p; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = bpf_obj_new(struct val_t); + if (!p) + return 0; + + old = bpf_kptr_xchg(&e->pc, p); + if (old) + bpf_percpu_obj_drop(old); + return 0; +} + SEC("?fentry.s/bpf_fentry_test1") __failure __msg("bpf_percpu_obj_new type ID argument must be of a struct of scalars") int BPF_PROG(test_array_map_6) diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c index 6d5fce7e6ffc..81c459435680 100644 --- a/tools/testing/selftests/bpf/progs/preempt_lock.c +++ b/tools/testing/selftests/bpf/progs/preempt_lock.c @@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx) return 0; } +SEC("?uprobe.s") +__failure __msg("sleepable helper bpf_get_stack#") +int preempt_sleepable_get_stack(struct pt_regs *ctx) +{ + struct bpf_stack_build_id stack; + + bpf_preempt_disable(); + bpf_get_stack(ctx, &stack, sizeof(stack), + BPF_F_USER_STACK | BPF_F_USER_BUILD_ID); + bpf_preempt_enable(); + return 0; +} + +SEC("?uprobe.s") +__failure __msg("sleepable helper bpf_get_task_stack#") +int preempt_sleepable_get_task_stack(void *ctx) +{ + struct bpf_stack_build_id stack; + struct task_struct *task; + + task = bpf_get_current_task_btf(); + bpf_preempt_disable(); + bpf_get_task_stack(task, &stack, sizeof(stack), + BPF_F_USER_STACK | BPF_F_USER_BUILD_ID); + bpf_preempt_enable(); + return 0; +} + +SEC("?uprobe.s") +__success +int sleepable_get_stack(struct pt_regs *ctx) +{ + struct bpf_stack_build_id stack; + + bpf_get_stack(ctx, &stack, sizeof(stack), + BPF_F_USER_STACK | BPF_F_USER_BUILD_ID); + return 0; +} + +SEC("?uprobe.s") +__success +int sleepable_get_task_stack(void *ctx) +{ + struct bpf_stack_build_id stack; + struct task_struct *task; + + task = bpf_get_current_task_btf(); + bpf_get_task_stack(task, &stack, sizeof(stack), + BPF_F_USER_STACK | BPF_F_USER_BUILD_ID); + return 0; +} + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") __failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region") int preempt_sleepable_kfunc(void *ctx) diff --git a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c index 0d58114a4955..725d73c9ffe1 100644 --- a/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c +++ b/tools/testing/selftests/bpf/progs/raw_tp_null_fail.c @@ -22,3 +22,56 @@ int test_raw_tp_null_sched_pi_setprio_arg_2(void *ctx) { asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u64 *)(r1 +0);" ::: __clobber_all); return 0; } + +/* Plain raw tracepoint arguments remain scalar values. */ +SEC("raw_tp/signal_generate") +__success +int test_raw_tp_signal_generate_info_scalar(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all); + return 0; +} + +/* tp_btf programs may inspect the sentinel as a scalar value. */ +SEC("tp_btf/signal_generate") +__success +int test_tp_btf_signal_generate_info_scalar(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 != 1 goto +0;" ::: __clobber_all); + return 0; +} + +/* SEND_SIG_PRIV is non-NULL, so a NULL check cannot make info safe. */ +SEC("tp_btf/signal_generate") +__failure __msg("R1 invalid mem access 'scalar'") +int test_tp_btf_signal_generate_info_no_deref(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); if r1 == 0 goto +1; " + "r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} + +SEC("tp_btf/signal_deliver") +__failure __msg("R1 invalid mem access 'scalar'") +int test_tp_btf_signal_deliver_info_no_deref(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +8); r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} + +SEC("tp_btf/sched_process_wait") +__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") +int test_raw_tp_null_sched_process_wait_arg_1(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +0); r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} + +SEC("tp_btf/sched_process_wait") +__success +int test_raw_tp_null_sched_process_wait_arg_1_checked(void *ctx) +{ + asm volatile("r1 = *(u64 *)(r1 +0); if r1 == 0 goto +1; " + "r1 = *(u32 *)(r1 +0);" ::: __clobber_all); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c index 555379952dcc..4504608196ab 100644 --- a/tools/testing/selftests/bpf/progs/rbtree_fail.c +++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c @@ -16,6 +16,7 @@ struct node_data { private(A) struct bpf_spin_lock glock; private(A) struct bpf_rb_root groot __contains(node_data, node); private(A) struct bpf_rb_root groot2 __contains(node_data, node); +private(B) struct bpf_res_spin_lock res_glock; static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b) { @@ -265,6 +266,53 @@ static bool less__bad_fn_call_first_unlock_after(struct bpf_rb_node *a, const st return node_a->key < node_b->key; } +static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b) +{ + bpf_res_spin_unlock(&res_glock); + return false; +} + +static __noinline void rbtree_cb_unlock_relock(void) +{ + bpf_spin_unlock(&glock); + bpf_spin_lock(&glock); +} + +static __noinline void rbtree_cb_nested_unlock(void) +{ + rbtree_cb_unlock_relock(); + asm volatile (""); +} + +static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b) +{ + struct node_data *node_a; + struct node_data *node_b; + + node_a = container_of(a, struct node_data, node); + node_b = container_of(b, struct node_data, node); + rbtree_cb_nested_unlock(); + + return node_a->key < node_b->key; +} + +static __noinline void rbtree_cb_noop(void) +{ + asm volatile (""); +} + +static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b) +{ + struct node_data *node_a; + struct node_data *node_b; + + node_a = container_of(a, struct node_data, node); + node_b = container_of(b, struct node_data, node); + rbtree_cb_noop(); + + return node_a->key < node_b->key; +} + static __always_inline long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b)) { @@ -301,4 +349,40 @@ long rbtree_api_add_bad_cb_bad_fn_call_first_unlock_after(void *ctx) return add_with_cb(less__bad_fn_call_first_unlock_after); } +SEC("?tc") +__failure __msg("can't res_spin_{lock,unlock} in rbtree cb") +long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx) +{ + struct node_data *n; + + n = bpf_obj_new(typeof(*n)); + if (!n) + return 1; + + bpf_spin_lock(&glock); + if (bpf_res_spin_lock(&res_glock)) { + bpf_spin_unlock(&glock); + bpf_obj_drop(n); + return 1; + } + bpf_rbtree_add(&groot, &n->node, less__bad_res_spin_unlock); + bpf_res_spin_unlock(&res_glock); + bpf_spin_unlock(&glock); + return 0; +} + +SEC("?tc") +__failure __msg("can't spin_{lock,unlock} in rbtree cb") +long rbtree_api_add_bad_cb_subprog_unlock(void *ctx) +{ + return add_with_cb(less__bad_subprog_unlock); +} + +SEC("?tc") +__success +long rbtree_api_add_cb_subprog_allowed(void *ctx) +{ + return add_with_cb(less__subprog_allowed); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/rcu_read_lock.c b/tools/testing/selftests/bpf/progs/rcu_read_lock.c index 31d4081c3a9f..cdb255addbc3 100644 --- a/tools/testing/selftests/bpf/progs/rcu_read_lock.c +++ b/tools/testing/selftests/bpf/progs/rcu_read_lock.c @@ -592,9 +592,9 @@ int non_own_ref_untrusted_ld(void *ctx) } bpf_rcu_read_unlock(); /* - * The unlock leaves node as PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED - * | NON_OWN_REF, and the load below has to get the BPF_PROBE_MEM - * rewrite for it, otherwise a bad address panics the kernel. + * The unlock leaves node as PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED, + * and the load below has to get the BPF_PROBE_MEM rewrite for it, + * otherwise a bad address panics the kernel. */ non_own_ref_key = node->key; return 0; diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr.c b/tools/testing/selftests/bpf/progs/refcounted_kptr.c index 61906f48025c..cae00f7b0a24 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr.c @@ -23,6 +23,15 @@ struct map_value { struct node_data __kptr *node; }; +struct node_refcount_only { + long key; + struct bpf_refcount refcount; +}; + +struct map_value_refcount_only { + struct node_refcount_only __kptr *node; +}; + struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, int); @@ -30,6 +39,13 @@ struct { __uint(max_entries, 2); } stashed_nodes SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct map_value_refcount_only); + __uint(max_entries, 1); +} stashed_refcount_only SEC(".maps"); + struct node_acquire { long key; long data; @@ -832,6 +848,51 @@ long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx) return 0; } +SEC("tc") +__success +long refcount_acquire_owning_input_no_null_check(void *ctx) +{ + struct node_refcount_only *n, *m; + + n = bpf_obj_new(typeof(*n)); + if (!n) + return 1; + + m = bpf_refcount_acquire(n); + bpf_obj_drop(m); + bpf_obj_drop(n); + + return 0; +} + +SEC("?syscall") +__success +long refcount_acquire_rcu_map_kptr_null_checked(void *ctx) +{ + struct map_value_refcount_only *mapval; + struct node_refcount_only *n, *m; + int idx = 0; + + mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx); + if (!mapval) + return 1; + + bpf_rcu_read_lock(); + n = mapval->node; + if (!n) { + bpf_rcu_read_unlock(); + return 2; + } + m = bpf_refcount_acquire(n); + bpf_rcu_read_unlock(); + + if (!m) + return 3; + bpf_obj_drop(m); + + return 0; +} + static long __stash_map_empty_xchg(struct node_data *n, int idx) { struct map_value *mapval = bpf_map_lookup_elem(&stashed_nodes, &idx); diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c index eaaed0859f94..338e43822ffe 100644 --- a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c @@ -19,6 +19,26 @@ struct node_refcounted { struct bpf_refcount refcount; }; +struct node_refcount_only { + long key; + struct bpf_refcount refcount; +}; + +struct map_value_refcount_only { + struct node_refcount_only __kptr *node; +}; + +struct rcu_graph_node { + struct bpf_rb_node node; + long data; +}; + +struct rcu_graph_node *just_here_because_btf_bug; + +struct map_value_rcu_graph { + struct rcu_graph_node __kptr *node; +}; + extern void bpf_rcu_read_lock(void) __ksym; extern void bpf_rcu_read_unlock(void) __ksym; @@ -27,6 +47,22 @@ private(A) struct bpf_spin_lock glock; private(A) struct bpf_rb_root groot __contains(node_acquire, node); private(B) struct bpf_spin_lock lock; private(B) struct bpf_list_head head __contains(node_refcounted, list); +private(C) struct bpf_spin_lock graph_lock; +private(C) struct bpf_rb_root graph_root __contains(rcu_graph_node, node); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct map_value_refcount_only); + __uint(max_entries, 1); +} stashed_refcount_only SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct map_value_rcu_graph); + __uint(max_entries, 1); +} stashed_rcu_graph SEC(".maps"); static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b) { @@ -89,6 +125,120 @@ long refcount_acquire_non_object(void *ctx) return bpf_refcount_acquire(ctx) != NULL; } +SEC("?syscall") +__failure __msg("Possibly NULL pointer passed to trusted R1") +long refcount_acquire_rcu_map_kptr_unchecked_drop(void *ctx) +{ + struct map_value_refcount_only *mapval; + struct node_refcount_only *tmp, *n, *m; + int idx = 0; + + /* Force Clang to emit complete BTF for struct node_refcount_only. */ + tmp = bpf_obj_new(typeof(*tmp)); + if (!tmp) + return 3; + bpf_obj_drop(tmp); + + mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx); + if (!mapval) + return 1; + + bpf_rcu_read_lock(); + n = mapval->node; + if (!n) { + bpf_rcu_read_unlock(); + return 2; + } + m = bpf_refcount_acquire(n); + bpf_rcu_read_unlock(); + + bpf_obj_drop(m); + + return 0; +} + +SEC("?syscall") +__failure +__msg("bpf_rbtree_remove can only take non-owning or refcounted " + "bpf_rb_node pointer") +long rbtree_remove_after_rcu_unlock(void *ctx) +{ + struct map_value_rcu_graph *mapval; + struct bpf_rb_node *rb_node; + struct rcu_graph_node *node; + int idx = 0; + + mapval = bpf_map_lookup_elem(&stashed_rcu_graph, &idx); + if (!mapval) + return 0; + + bpf_rcu_read_lock(); + node = mapval->node; + if (!node) { + bpf_rcu_read_unlock(); + return 0; + } + bpf_rcu_read_unlock(); + + bpf_spin_lock(&graph_lock); + rb_node = bpf_rbtree_remove(&graph_root, &node->node); + bpf_spin_unlock(&graph_lock); + if (rb_node) + bpf_obj_drop(container_of(rb_node, struct rcu_graph_node, node)); + + return 0; +} + +SEC("?syscall") +__failure __msg("R1 is neither owning or non-owning ref") +long refcount_acquire_after_rcu_unlock(void *ctx) +{ + struct map_value_refcount_only *mapval; + struct node_refcount_only *node, *ref; + int idx = 0; + + mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx); + if (!mapval) + return 0; + + bpf_rcu_read_lock(); + node = mapval->node; + if (!node) { + bpf_rcu_read_unlock(); + return 0; + } + bpf_rcu_read_unlock(); + + ref = bpf_refcount_acquire(node); + if (ref) + bpf_obj_drop(ref); + + return 0; +} + +SEC("?syscall") +__failure __msg("invalid mem access 'scalar'") +long graph_kptr_after_spin_unlock(void *ctx) +{ + struct map_value_rcu_graph *mapval; + struct rcu_graph_node *node; + int idx = 0; + + mapval = bpf_map_lookup_elem(&stashed_rcu_graph, &idx); + if (!mapval) + return 0; + + bpf_spin_lock(&graph_lock); + node = mapval->node; + if (!node) { + bpf_spin_unlock(&graph_lock); + return 0; + } + bpf_spin_unlock(&graph_lock); + + return node->data; +} + SEC("?tc") __failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}") long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx) diff --git a/tools/testing/selftests/bpf/progs/rhash.c b/tools/testing/selftests/bpf/progs/rhash.c index fc2dac3a719e..aea4de8dc781 100644 --- a/tools/testing/selftests/bpf/progs/rhash.c +++ b/tools/testing/selftests/bpf/progs/rhash.c @@ -19,6 +19,11 @@ struct elem { int val; }; +struct special_elem { + struct task_struct __kptr *task; + int val; +}; + struct { __uint(type, BPF_MAP_TYPE_RHASH); __uint(map_flags, BPF_F_NO_PREALLOC); @@ -27,6 +32,17 @@ struct { __type(value, struct elem); } rhmap SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RHASH); + __uint(map_flags, BPF_F_NO_PREALLOC); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct special_elem); +} special_fields SEC(".maps"); + +extern struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; +extern void bpf_task_release(struct task_struct *p) __ksym; + SEC("syscall") int test_rhash_lookup_update(void *ctx) { @@ -246,3 +262,99 @@ int test_rhash_delete_nonexistent(void *ctx) err = 0; return 0; } + +SEC("syscall") +int test_rhash_kptr_update(void *ctx) +{ + struct special_elem val1 = { .val = 1 }; + struct special_elem val2 = { .val = 2 }; + struct task_struct *task, *old; + struct special_elem *elem; + int key = 0; + + err = 1; + if (bpf_map_update_elem(&special_fields, &key, &val1, BPF_NOEXIST)) + return 1; + + err = 2; + elem = bpf_map_lookup_elem(&special_fields, &key); + if (!elem) + return 2; + + err = 3; + task = bpf_task_acquire(bpf_get_current_task_btf()); + if (!task) + return 3; + + err = 4; + old = bpf_kptr_xchg(&elem->task, task); + if (old) { + bpf_task_release(old); + return 4; + } + + err = 5; + if (bpf_map_update_elem(&special_fields, &key, &val2, BPF_EXIST)) + return 5; + + err = 6; + elem = bpf_map_lookup_elem(&special_fields, &key); + if (!elem || elem->val != 2) + return 6; + + err = 7; + old = bpf_kptr_xchg(&elem->task, NULL); + if (!old) + return 7; + bpf_task_release(old); + + err = 8; + if (bpf_map_delete_elem(&special_fields, &key)) + return 8; + + err = 0; + return 0; +} + +SEC("syscall") +int test_rhash_kptr_delete(void *ctx) +{ + struct special_elem val = {}; + struct task_struct *task, *old; + struct special_elem *elem; + int key = 0; + + err = 1; + if (bpf_map_update_elem(&special_fields, &key, &val, BPF_NOEXIST)) + return 1; + + err = 2; + elem = bpf_map_lookup_elem(&special_fields, &key); + if (!elem) + return 2; + + err = 3; + task = bpf_task_acquire(bpf_get_current_task_btf()); + if (!task) + return 3; + + err = 4; + old = bpf_kptr_xchg(&elem->task, task); + if (old) { + bpf_task_release(old); + return 4; + } + + err = 5; + if (bpf_map_delete_elem(&special_fields, &key)) + return 5; + + err = 6; + old = bpf_kptr_xchg(&elem->task, NULL); + if (!old) + return 6; + bpf_task_release(old); + + err = 0; + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/rhash_timer.c b/tools/testing/selftests/bpf/progs/rhash_timer.c new file mode 100644 index 000000000000..2e06a463c605 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/rhash_timer.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <vmlinux.h> +#include <errno.h> +#include <bpf/bpf_helpers.h> + +#define CLOCK_MONOTONIC 1 +#define TIMER_NSEC (60ULL * 1000 * 1000 * 1000) + +struct timer_value { + struct bpf_timer timer; + u64 data; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RHASH); + __uint(map_flags, BPF_F_NO_PREALLOC); + __uint(max_entries, 1); + __type(key, u64); + __type(value, struct timer_value); +} timer_map SEC(".maps"); + +u64 armed; +u64 cancelled; +long timer_init_err; +long timer_set_callback_err; +long timer_start_err; +long timer_cancel_err; + +static int timer_cb(void *map, u64 *key, struct timer_value *value) +{ + return 0; +} + +static long arm_timer_cb(struct bpf_map *map, u64 *key, + struct timer_value *value, void *ctx) +{ + u64 key_copy = *key; + long err; + + err = bpf_map_delete_elem(map, &key_copy); + if (err) + return 1; + + err = bpf_timer_init(&value->timer, map, CLOCK_MONOTONIC); + if (err) { + timer_init_err = err; + return 1; + } + + err = bpf_timer_set_callback(&value->timer, timer_cb); + if (err) { + timer_set_callback_err = err; + return 1; + } + + err = bpf_timer_start(&value->timer, TIMER_NSEC, BPF_F_TIMER_CPU_PIN); + if (err) { + timer_start_err = err; + return 1; + } + + __sync_fetch_and_add(&armed, 1); + return 1; +} + +static long cancel_timer_cb(struct bpf_map *map, u64 *key, + struct timer_value *value, void *ctx) +{ + long err; + + err = bpf_timer_cancel(&value->timer); + if (err == -EINVAL) + return 1; + if (err < 0) { + timer_cancel_err = err; + return 1; + } + + __sync_fetch_and_add(&cancelled, 1); + return 1; +} + +SEC("syscall") +int arm_deleted_timer(void *ctx) +{ + bpf_for_each_map_elem(&timer_map, arm_timer_cb, NULL, 0); + return 0; +} + +SEC("syscall") +int cancel_recycled_timer(void *ctx) +{ + bpf_for_each_map_elem(&timer_map, cancel_timer_cb, NULL, 0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/snprintf_btf_void.c b/tools/testing/selftests/bpf/progs/snprintf_btf_void.c new file mode 100644 index 000000000000..44af80fbbb80 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/snprintf_btf_void.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "btf_ptr.h" +#include <bpf/bpf_helpers.h> + +__u32 type_id; +/* A buffer we own to render the selected type from, kept in bounds. */ +char obj[256]; +char out[64]; +long ret; + +SEC("raw_tp/sys_enter") +int dump_type(void *ctx) +{ + struct btf_ptr ptr = { + .ptr = obj, + .type_id = type_id, + .flags = 0, + }; + + ret = bpf_snprintf_btf(out, sizeof(out), &ptr, sizeof(ptr), 0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/tailcall_callback.c b/tools/testing/selftests/bpf/progs/tailcall_callback.c index c41632cf423b..14fa7a87028e 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_callback.c +++ b/tools/testing/selftests/bpf/progs/tailcall_callback.c @@ -45,6 +45,13 @@ int callback_loop(int index, void **cb_ctx) } static __noinline +int callback_tail(int index, void **cb_ctx) +{ + bpf_tail_call_static(*cb_ctx, &jmp_table, 0); + return 0; +} + +static __noinline int callback_empty(int index, void *data) { return 0; @@ -78,4 +85,13 @@ int tailcall_callback_2(struct __sk_buff *skb) return 0; } +/* callback with a direct tail call is rejected without a verifier bug */ +SEC("tc") +__failure __msg("cannot tail call within callback") +int tailcall_callback_3(struct __sk_buff *skb) +{ + bpf_loop(1, callback_tail, &skb, 0); + return 0; +} + char __license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/task_local_data.bpf.h b/tools/testing/selftests/bpf/progs/task_local_data.bpf.h index 0df8a12fd61e..a31a399870be 100644 --- a/tools/testing/selftests/bpf/progs/task_local_data.bpf.h +++ b/tools/testing/selftests/bpf/progs/task_local_data.bpf.h @@ -61,6 +61,7 @@ #define TLD_ROUND_UP(x, y) ((((x) - 1) | TLD_ROUND_MASK(x, y)) + 1) #define TLD_MAX_DATA_CNT (__PAGE_SIZE / sizeof(struct tld_metadata) - 1) +#define TLD_DATA_SIZE (__PAGE_SIZE - sizeof(__u64)) #ifndef TLD_NAME_LEN #define TLD_NAME_LEN 62 @@ -189,6 +190,8 @@ static int __tld_fetch_key(struct tld_object *tld_obj, const char *name, int i_s return start + off; off += TLD_ROUND_UP(metadata[i].size, 8); + if (off > TLD_DATA_SIZE) + break; } return -cnt; diff --git a/tools/testing/selftests/bpf/progs/test_bpf_nf.c b/tools/testing/selftests/bpf/progs/test_bpf_nf.c index df43649ecb78..eda9b7bbab75 100644 --- a/tools/testing/selftests/bpf/progs/test_bpf_nf.c +++ b/tools/testing/selftests/bpf/progs/test_bpf_nf.c @@ -190,8 +190,8 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); if (ct) { - __u16 sport = bpf_get_prandom_u32(); - __u16 dport = bpf_get_prandom_u32(); + __u16 sport = bpf_get_prandom_u32() % 65535 + 1; + __u16 dport = bpf_get_prandom_u32() % 65535 + 1; union nf_inet_addr saddr = {}; union nf_inet_addr daddr = {}; struct nf_conn *ct_ins; @@ -293,8 +293,8 @@ nf_ct_opts_new_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple * ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); if (ct) { - __u16 sport = bpf_get_prandom_u32(); - __u16 dport = bpf_get_prandom_u32(); + __u16 sport = bpf_get_prandom_u32() % 65535 + 1; + __u16 dport = bpf_get_prandom_u32() % 65535 + 1; union nf_inet_addr saddr = {}; union nf_inet_addr daddr = {}; struct nf_conn *ct_ins; diff --git a/tools/testing/selftests/bpf/progs/timer_mim_reject.c b/tools/testing/selftests/bpf/progs/timer_mim_reject.c index dd3f1ed6d6e6..83f31138336b 100644 --- a/tools/testing/selftests/bpf/progs/timer_mim_reject.c +++ b/tools/testing/selftests/bpf/progs/timer_mim_reject.c @@ -43,7 +43,7 @@ static int timer_cb(void *map, int *key, struct hmap_elem *val) return 0; } -SEC("fentry/bpf_fentry_test1") +SEC("?fentry/bpf_fentry_test1") int BPF_PROG(test1, int a) { struct hmap_elem init = {}; @@ -72,3 +72,85 @@ int BPF_PROG(test1, int a) err |= 8; return 0; } + +struct callback_ctx { + void *map; +}; + +static int mismatch_iter_cb(void *map, int *key, struct hmap_elem *val, struct callback_ctx *ctx) +{ + bpf_timer_init(&val->timer, ctx->map, CLOCK_MONOTONIC); + return 0; +} + +static int timer_mismatch_cb(void *map, int *key, struct hmap_elem *val) +{ + struct callback_ctx ctx = { .map = map }; + struct bpf_map *inner_map2; + int array_key2 = ARRAY_KEY2; + + inner_map2 = bpf_map_lookup_elem(&outer_arr, &array_key2); + if (!inner_map2) + return 0; + bpf_for_each_map_elem(inner_map2, mismatch_iter_cb, &ctx, 0); + return 0; +} + +static int match_iter_cb(void *map, int *key, struct hmap_elem *val, struct callback_ctx *ctx) +{ + bpf_timer_init(&val->timer, map, CLOCK_MONOTONIC); + return 0; +} + +static int timer_match_cb(void *map, int *key, struct hmap_elem *val) +{ + struct callback_ctx ctx = {}; + struct bpf_map *inner_map2; + int array_key2 = ARRAY_KEY2; + + inner_map2 = bpf_map_lookup_elem(&outer_arr, &array_key2); + if (!inner_map2) + return 0; + bpf_for_each_map_elem(inner_map2, match_iter_cb, &ctx, 0); + return 0; +} + +SEC("?fentry/bpf_fentry_test1") +int BPF_PROG(callback_map_uid_mismatch, int a) +{ + struct hmap_elem *val; + struct bpf_map *inner_map; + int array_key = ARRAY_KEY; + int hash_key = HASH_KEY; + + inner_map = bpf_map_lookup_elem(&outer_arr, &array_key); + if (!inner_map) + return 0; + val = bpf_map_lookup_elem(inner_map, &hash_key); + if (!val) + return 0; + + bpf_timer_init(&val->timer, inner_map, CLOCK_MONOTONIC); + bpf_timer_set_callback(&val->timer, timer_mismatch_cb); + return 0; +} + +SEC("?fentry/bpf_fentry_test1") +int BPF_PROG(callback_map_uid_match, int a) +{ + struct hmap_elem *val; + struct bpf_map *inner_map; + int array_key = ARRAY_KEY; + int hash_key = HASH_KEY; + + inner_map = bpf_map_lookup_elem(&outer_arr, &array_key); + if (!inner_map) + return 0; + val = bpf_map_lookup_elem(inner_map, &hash_key); + if (!val) + return 0; + + bpf_timer_init(&val->timer, inner_map, CLOCK_MONOTONIC); + bpf_timer_set_callback(&val->timer, timer_match_cb); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c index 6bf95550a024..e0926767bbd3 100644 --- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c +++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c @@ -62,6 +62,104 @@ int timer_sleepable_prog(void *ctx) return 0; } +static int timer_sys_bpf_cb(void *map, int *key, struct bpf_timer *timer) +{ + __u64 attr = 0; + + bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); + return 0; +} + +SEC("syscall") +__failure __msg("sleepable helper bpf_sys_bpf#{{[0-9]+}} in non-sleepable prog") +int timer_sys_bpf_prog(void *ctx) +{ + struct timer_elem *val; + int key = 0; + + val = bpf_map_lookup_elem(&timer_map, &key); + if (!val) + return 0; + + bpf_timer_init(&val->t, &timer_map, 0); + bpf_timer_set_callback(&val->t, timer_sys_bpf_cb); + return 0; +} + +static int timer_sys_close_cb(void *map, int *key, struct bpf_timer *timer) +{ + bpf_sys_close(0); + return 0; +} + +SEC("syscall") +__failure __msg("sleepable helper bpf_sys_close#{{[0-9]+}} in non-sleepable prog") +int timer_sys_close_prog(void *ctx) +{ + struct timer_elem *val; + int key = 0; + + val = bpf_map_lookup_elem(&timer_map, &key); + if (!val) + return 0; + + bpf_timer_init(&val->t, &timer_map, 0); + bpf_timer_set_callback(&val->t, timer_sys_close_cb); + return 0; +} + +static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer) +{ + char name[] = "task_struct"; + + bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0); + return 0; +} + +SEC("syscall") +__failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog") +int timer_btf_find_prog(void *ctx) +{ + struct timer_elem *val; + int key = 0; + + val = bpf_map_lookup_elem(&timer_map, &key); + if (!val) + return 0; + + bpf_timer_init(&val->t, &timer_map, 0); + bpf_timer_set_callback(&val->t, timer_btf_find_cb); + return 0; +} + +SEC("syscall") +__success +int syscall_sys_bpf_prog(void *ctx) +{ + __u64 attr = 0; + + bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); + return 0; +} + +SEC("syscall") +__success +int syscall_sys_close_prog(void *ctx) +{ + bpf_sys_close(0); + return 0; +} + +SEC("syscall") +__success +int syscall_btf_find_prog(void *ctx) +{ + char name[] = "task_struct"; + + bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0); + return 0; +} + /* Workqueue tests */ struct wq_elem { diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c index 1a273e416fed..df8d5309657e 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bounds.c +++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c @@ -2267,6 +2267,47 @@ __naked void deduce64_from_32_wrapping_32bit(void) : __clobber_all); } +/* + * Unprivileged variable pointer arithmetic on a PTR_TO_MAP_VALUE whose + * offset collapses to a constant. The Spectre-v1 speculative path snapshots + * the pointer while its r32 has just been blanked but its offset not yet + * synced; the following register move used to trip reg_bounds_sanity_check() + * ("const subreg tnum out of sync with range bounds"). With + * BPF_F_TEST_REG_INVARIANTS that violation turns into a load failure, so the + * unprivileged program must still load. + */ +SEC("socket") +__success __success_unpriv +__flag(BPF_F_TEST_REG_INVARIANTS) +__naked void spec_ptr_alu_const_offset(void) +{ + asm volatile (" \ + call %[bpf_ktime_get_ns]; \ + *(u64*)(r10 - 16) = r0; \ + r1 = 0; \ + *(u64*)(r10 - 8) = r1; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto l0_%=; \ + r1 = *(u64*)(r10 - 16); \ + r2 = 0x40000000; \ + if r1 > r2 goto l0_%=; \ + if r1 s> 1 goto l0_%=; /* r1 in [0, 1] */ \ + r0 += r1; /* ptr += bounded scalar */ \ + r9 = r0; /* used to trip the warning */ \ + *(u8*)(r0 + 0) = r1; \ +l0_%=: r0 = 0; \ + exit; \ + " + : + : __imm(bpf_ktime_get_ns), + __imm(bpf_map_lookup_elem), + __imm_addr(map_hash_8b) + : __clobber_all); +} + /* Check that range_within() compares cnum ranges, not min/max projections. */ SEC("socket") __failure __msg("div by zero") diff --git a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c index 328cf630210a..a73b837553fb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c +++ b/tools/testing/selftests/bpf/progs/verifier_bpf_fastcall.c @@ -621,6 +621,116 @@ __naked void helper_call_does_not_prevent_bpf_fastcall(void) : __clobber_all); } +/* A jump to the first spill executes the whole pattern, rewrite is safe. */ +SEC("raw_tp") +__arch_x86_64 +__log_level(4) +__msg("subprog 0 (jump_to_first_spill) main {{.*}} stack 0") +__xlated("2: if r0 == 0x2a goto pc+0") +__xlated("3: r0 = ") +__xlated("4: r0 = &(void __percpu *)(r0)") +__success +__naked void jump_to_first_spill(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 1;" + "if r0 == 42 goto l0_%=;" +"l0_%=:" + "*(u64 *)(r10 - 8) = r1;" + "call %[bpf_get_smp_processor_id];" + "r1 = *(u64 *)(r10 - 8);" + "exit;" + : + : __imm(bpf_get_prandom_u32), + __imm(bpf_get_smp_processor_id) + : __clobber_all); +} + +/* A jump to the call skips the spill, the pattern must be kept. */ +SEC("raw_tp") +__arch_x86_64 +__log_level(4) +__msg("subprog 0 (jump_to_call) main {{.*}} stack 8") +__xlated("2: if r0 == 0x2a goto pc+1") +__xlated("3: *(u64 *)(r10 -8) = r1") +__xlated("...") +__xlated("7: r1 = *(u64 *)(r10 -8)") +__success +__naked void jump_to_call(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 1;" + "if r0 == 42 goto l0_%=;" + "*(u64 *)(r10 - 8) = r1;" +"l0_%=:" + "call %[bpf_get_smp_processor_id];" + "r1 = *(u64 *)(r10 - 8);" + "exit;" + : + : __imm(bpf_get_prandom_u32), + __imm(bpf_get_smp_processor_id) + : __clobber_all); +} + +/* A jump to the fill skips the spill, the pattern must be kept. */ +SEC("raw_tp") +__arch_x86_64 +__log_level(4) +__msg("subprog 0 (jump_to_fill) main {{.*}} stack 8") +__xlated("2: if r0 == 0x2a goto pc+4") +__xlated("3: *(u64 *)(r10 -8) = r1") +__xlated("...") +__xlated("7: r1 = *(u64 *)(r10 -8)") +__success +__naked void jump_to_fill(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 1;" + "if r0 == 42 goto l0_%=;" + "*(u64 *)(r10 - 8) = r1;" + "call %[bpf_get_smp_processor_id];" +"l0_%=:" + "r1 = *(u64 *)(r10 - 8);" + "exit;" + : + : __imm(bpf_get_prandom_u32), + __imm(bpf_get_smp_processor_id) + : __clobber_all); +} + +/* Same as above, but the fill is entered by an unconditional jump. */ +SEC("raw_tp") +__arch_x86_64 +__log_level(4) +__msg("subprog 0 (unconditional_jump_to_fill) main {{.*}} stack 8") +__xlated("3: *(u64 *)(r10 -8) = r1") +__xlated("...") +__xlated("7: r1 = *(u64 *)(r10 -8)") +__xlated("8: exit") +__xlated("9: goto pc-3") +__success +__naked void unconditional_jump_to_fill(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 1;" + "if r0 == 42 goto l1_%=;" + "*(u64 *)(r10 - 8) = r1;" + "call %[bpf_get_smp_processor_id];" +"l0_%=:" + "r1 = *(u64 *)(r10 - 8);" + "exit;" +"l1_%=:" + "goto l0_%=;" + : + : __imm(bpf_get_prandom_u32), + __imm(bpf_get_smp_processor_id) + : __clobber_all); +} + SEC("raw_tp") __arch_x86_64 __log_level(4) diff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c index c1f55e1d80a4..3c3bb03e8217 100644 --- a/tools/testing/selftests/bpf/progs/verifier_cfg.c +++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c @@ -3,6 +3,7 @@ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> +#include "../../../include/linux/filter.h" #include "bpf_misc.h" SEC("socket") @@ -56,6 +57,19 @@ __naked void out_of_range_jump2(void) } SEC("socket") +__description("invalid DW LDSX instruction in diagnostics") +__failure __msg("BUG_ldx_99") +__log_level(2) +__naked void invalid_dw_ldsx(void) +{ + asm volatile (" \ + .8byte %[ldsx_dw]; \ +" : + : __imm_insn(ldsx_dw, BPF_RAW_INSN(BPF_LDX | BPF_MEMSX | BPF_DW, BPF_REG_0, BPF_REG_0, 0, 0)) + : __clobber_all); +} + +SEC("socket") __description("loop (back-edge)") __failure __msg("unreachable insn 1") __msg_unpriv("back-edge") diff --git a/tools/testing/selftests/bpf/progs/verifier_cgroup_storage.c b/tools/testing/selftests/bpf/progs/verifier_cgroup_storage.c index 9a13f5c11ac7..884080a5bffc 100644 --- a/tools/testing/selftests/bpf/progs/verifier_cgroup_storage.c +++ b/tools/testing/selftests/bpf/progs/verifier_cgroup_storage.c @@ -305,4 +305,33 @@ __naked void cpu_cgroup_storage_access_6(void) : __clobber_all); } +/* + * Verification takes two paths: with r2 being scalar zero on path (1) + * and with r2 being some other scalar on path (2). + * Check that the verifier does not use checkpoints created + * on path (1) to prune path (2). + */ +SEC("cgroup/skb") +__failure +__flag(BPF_F_TEST_STATE_FREQ) +__msg("get_local_storage() doesn't support non-zero flags") +__naked void non_zero_flags_on_a_pruned_path(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + /* r2 is 0 on the path explored first, 1 on the other */\ + r2 = 1; \ + if r0 == 0 goto 1f; \ + r2 = 0; \ +1: r1 = %[cgroup_storage] ll; \ + call %[bpf_get_local_storage]; \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32), + __imm(bpf_get_local_storage), + __imm_addr(cgroup_storage) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c index 0bdeb7bc4687..a3d2af8dc839 100644 --- a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c +++ b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c @@ -56,6 +56,30 @@ int trusted_task_arg_nullable(void *ctx) return res; } +/* + * Check that the verifier does not use checkpoints created + * on path with r1 == 0 to prune path with r1 != 0. + */ +SEC("?tp_btf/task_newtask") +__failure +__flag(BPF_F_TEST_STATE_FREQ) +__msg("R1 type=scalar expected=ptr_, trusted_ptr_, rcu_ptr_") +__naked int null_btf_id_arg_global_subprog(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 42;" + "if r0 > 42 goto 1f;" + "r1 = 0;" + "1:" + "call subprog_trusted_task_nullable;" + "r0 = 0;" + "exit;" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + __weak int subprog_trusted_task_nonnull(struct task_struct *task __arg_trusted) { return task->pid + task->tgid; diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c index 75dd922e4e9f..1fbcc5228306 100644 --- a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -169,6 +169,23 @@ static int iter_limit_cb(__u32 idx, struct num_context *ctx) } SEC("?raw_tp") +__failure __msg("R1 type=ctx expected=scalar") +__naked void bpf_loop_reject_pointer(void) +{ + asm volatile ( + "r2 = %[iter_limit_cb];" + "r3 = 0;" + "r4 = 0;" + "call %[bpf_loop];" + "exit;" + : + : __imm_ptr(iter_limit_cb), + __imm(bpf_loop) + : __clobber_common + ); +} + +SEC("?raw_tp") __success int bpf_loop_iter_limit_ok(void *unused) { diff --git a/tools/testing/selftests/bpf/progs/verifier_jeq_infer_not_null.c b/tools/testing/selftests/bpf/progs/verifier_jeq_infer_not_null.c index 3d1e8de4390c..3c789c565b18 100644 --- a/tools/testing/selftests/bpf/progs/verifier_jeq_infer_not_null.c +++ b/tools/testing/selftests/bpf/progs/verifier_jeq_infer_not_null.c @@ -3,7 +3,9 @@ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> +#include <stdbool.h> #include "bpf_misc.h" +#include "bpf_kfuncs.h" struct { __uint(type, BPF_MAP_TYPE_XSKMAP); @@ -12,6 +14,13 @@ struct { __type(value, int); } map_xskmap SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} map_hash SEC(".maps"); + /* This is equivalent to the following program: * * r6 = skb->sk; @@ -264,4 +273,188 @@ __naked void jne_reg_reg_null_check(void) : __clobber_all); } +/* + * A comparison between PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED and + * PTR_TO_MAP_VALUE_OR_NULL should not infer that map pointer is not null. + * A bug in check_cond_jmp_op() made such inference possible. + */ +SEC("raw_tp") +__failure +__msg("error: invalid dereference of R0 (a nullable map value pointer)") +__msg(">>> 11 | (61) r0 = *(u32 *)(r0 +0)") +__naked void untrusted_mem_does_not_infer_map_value_non_null(void) +{ + asm volatile (" \ + /* r6 = bpf_rdonly_cast(0, 0); */ \ + r1 = 0; \ + r2 = 0; \ + call %[bpf_rdonly_cast]; \ + r6 = r0; \ + /* r0 = bpf_map_lookup_elem(map_hash, &key); */ \ + *(u64 *)(r10 - 8) = 0; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + /* \ + * buggy verifier assumed that r6 can't be null \ + * and marked r0 non-null as well. \ + */ \ + if r6 != r0 goto 1f; \ + r0 = *(u32 *)(r0 + 0); \ +1: r0 = 0; \ + exit; \ +" : + : __imm(bpf_rdonly_cast), + __imm(bpf_map_lookup_elem), + __imm_addr(map_hash) + : __clobber_all); +} + +/* + * A pointer with an offset that is not bounded from above may be null at + * runtime, hence it is not a witness for the pointer it is compared with. + */ +SEC("socket") +__failure +__msg("error: invalid dereference of R7 (a nullable map value pointer)") +__naked void unbounded_offset_does_not_infer_map_value_non_null(void) +{ + asm volatile (" \ + /* r6 = bpf_map_lookup_elem(map_hash, &0); */ \ + *(u64 *)(r10 - 8) = 0; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto 1f; \ + r6 = r0; \ + /* r7 = bpf_map_lookup_elem(map_hash, &1); */ \ + *(u64 *)(r10 - 8) = 1; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + r7 = r0; \ + /* pointer - pointer is an unknown scalar */ \ + r8 = r7; \ + r8 -= r6; \ + /* r8 is in [0, S64_MAX] */ \ + r8 <<= 1; \ + r8 >>= 1; \ + /* r6 may wrap to zero at runtime */ \ + r6 += r8; \ + if r7 != r6 goto 1f; \ + r0 = *(u8 *)(r7 + 0); \ +1: r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash) + : __clobber_all); +} + +/* Same, but the offset is bounded, so the inference is still done. */ +SEC("socket") +__success +__naked void bounded_offset_infers_map_value_non_null(void) +{ + asm volatile (" \ + /* r6 = bpf_map_lookup_elem(map_hash, &0); */ \ + *(u64 *)(r10 - 8) = 0; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto 1f; \ + r6 = r0; \ + /* r7 = bpf_map_lookup_elem(map_hash, &1); */ \ + *(u64 *)(r10 - 8) = 1; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + r7 = r0; \ + /* pointer - pointer is an unknown scalar */ \ + r8 = r7; \ + r8 -= r6; \ + /* r8 is in [0, 3] */ \ + r8 &= 3; \ + r6 += r8; \ + if r7 != r6 goto 1f; \ + r0 = *(u8 *)(r7 + 0); \ +1: r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash) + : __clobber_all); +} + +/* + * The low 32 bits of a map value pointer may be zero, hence a 32-bit + * compare with zero cannot be predicted from the pointer being non-NULL + * and both successors of such a jump have to be verified. + */ +SEC("socket") +__failure __msg("invalid access to map value, value_size=4 off=32 size=4") +__naked void jmp32_ptr_vs_zero_jne(void) +{ + asm volatile (" \ + /* r0 = bpf_map_lookup_elem(map_hash, &key); */ \ + *(u64 *)(r10 - 8) = 0; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + if r0 == 0 goto 1f; \ + if w0 != 0 goto 1f; \ + r0 = *(u32 *)(r0 + 32); \ +1: r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash) + : __clobber_all); +} + +/* + * The below program is explored in two paths: r6 == 0 and r6 == 1. + * On the first path comparison "if r0 == r6 goto 2f" should mark r6 as precise, + * otherwise unsafe path with r6 == 1 would be incorrectly pruned. + */ +SEC("socket") +__failure +__flag(BPF_F_TEST_STATE_FREQ) +__msg("error: invalid dereference of R0 (a nullable map value pointer)") +__naked void imprecise_zero_does_not_infer_map_value_non_null(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + /* r6 is 0 on the path explored first, 1 on the other */\ + r6 = 1; \ + if r0 == 0 goto 1f; \ + r6 = 0; \ + /* r0 = bpf_map_lookup_elem(map_hash, &0); */ \ +1: *(u64 *)(r10 - 8) = 0; \ + r1 = %[map_hash] ll; \ + r2 = r10; \ + r2 += -8; \ + call %[bpf_map_lookup_elem]; \ + if r0 == r6 goto 2f; \ + r0 = *(u8 *)(r0 + 0); \ +2: r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32), + __imm(bpf_map_lookup_elem), + __imm_addr(map_hash) + : __clobber_all); +} + +void kfunc_root(void) +{ + bpf_rdonly_cast(0, 0); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c index 09e81b99eecb..32989f981fb6 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c +++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c @@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void) ::: __clobber_all); } +__naked __noinline __used +static int ld_abs_callback(void) +{ + asm volatile ( + "r6 = *(u64 *)(r2 + 0);" + ".8byte %[ld_abs];" + "r0 = 0;" + "exit;" + : + : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0)) + : __clobber_all); +} + +SEC("socket") +__description("ld_abs: reject in callback") +__failure __msg("cannot use BPF_LD_[ABS|IND] within callback") +int ld_abs_callback_reject(struct __sk_buff *skb) +{ + bpf_loop(1, ld_abs_callback, &skb, 0); + return 0; +} + +__naked __noinline __used +static int ld_ind_callback_subprog(void) +{ + asm volatile ( + "r6 = r1;" + "r7 = 0;" + ".8byte %[ld_ind];" + "r0 = 0;" + "exit;" + : + : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0)) + : __clobber_all); +} + +__naked __noinline __used +static int ld_ind_callback(void) +{ + asm volatile ( + "r1 = *(u64 *)(r2 + 0);" + "call ld_ind_callback_subprog;" + "exit;" + ::: __clobber_all); +} + +SEC("socket") +__description("ld_ind: reject in callback subprog") +__failure __msg("cannot use BPF_LD_[ABS|IND] within callback") +int ld_ind_callback_subprog_reject(struct __sk_buff *skb) +{ + bpf_loop(1, ld_ind_callback, &skb, 0); + return 0; +} + +static __noinline int ld_ind_global_static(struct __sk_buff *skb) +{ + asm volatile ( + "r6 = %[skb];" + "r7 = 0;" + ".8byte %[ld_ind];" + : + : [skb] "r"(skb), + __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0)) + : __clobber_common, "r6", "r7"); + return skb->mark; +} + +__noinline int ld_ind_global(struct __sk_buff *skb) +{ + return ld_ind_global_static(skb); +} + +static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx) +{ + ld_ind_global(*ctx); + return 0; +} + +SEC("socket") +__description("ld_ind: reject in callback global subprog") +__failure __msg("cannot use BPF_LD_[ABS|IND] within callback") +int ld_ind_global_callback_reject(struct __sk_buff *skb) +{ + bpf_loop(1, ld_ind_global_callback, &skb, 0); + return 0; +} + +SEC("socket") +__description("ld_ind: allow in non-callback global subprog") +__success +int ld_ind_global_subprog_ok(struct __sk_buff *skb) +{ + return ld_ind_global(skb); +} + /* * ld_{abs,ind} in subprogs require scalar (int) return type in BTF. * A test with void return must be rejected. diff --git a/tools/testing/selftests/bpf/progs/verifier_netfilter_ctx.c b/tools/testing/selftests/bpf/progs/verifier_netfilter_ctx.c index e2cbc5bda65e..b5d7f567d0d4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_netfilter_ctx.c +++ b/tools/testing/selftests/bpf/progs/verifier_netfilter_ctx.c @@ -113,4 +113,82 @@ int with_valid_ctx_access_test6(struct bpf_nf_ctx *ctx) return th->dest == bpf_htons(22) ? NF_ACCEPT : NF_DROP; } +SEC("netfilter") +__description("netfilter test prog with skb write access") +__failure __msg("only read is supported") +int skb_len_write(struct bpf_nf_ctx *ctx) +{ + ctx->skb->len = 1; + return 1; +} + +SEC("netfilter") +__description("netfilter test prog with skb data write access") +__failure __msg("cannot write into rdonly_untrusted_mem") +int skb_data_write(struct bpf_nf_ctx *ctx) +{ + ctx->skb->data[0] = 0; + return 1; +} + +SEC("netfilter") +__description("netfilter test prog with bpf_dynptr_write") +__success __failure_unpriv +__retval(0) +int with_dynptr_write(struct bpf_nf_ctx *ctx) +{ + struct __sk_buff *skb = (struct __sk_buff *)ctx->skb; + struct bpf_dynptr ptr; + u8 buffer[1] = {}; + + if (bpf_dynptr_from_skb(skb, 0, &ptr)) + return 1; + + if (bpf_dynptr_write(&ptr, 0, buffer, sizeof(buffer), 0)) + return 0; /* must always fail */ + + return 1; +} + +SEC("netfilter") +__description("netfilter test prog with bpf_dynptr_slice_rdwr") +__failure __msg("the prog does not allow writes to packet data") +int with_dynptr_rdwr(struct bpf_nf_ctx *ctx) +{ + struct __sk_buff *skb = (struct __sk_buff *)ctx->skb; + u8 buffer_iph[20] = {}; + struct bpf_dynptr ptr; + struct iphdr *iph; + + if (bpf_dynptr_from_skb(skb, 0, &ptr)) + return 1; + + iph = bpf_dynptr_slice_rdwr(&ptr, 0, buffer_iph, sizeof(buffer_iph)); + if (!iph) + return 0; + + return 1; +} + +SEC("netfilter") +__description("netfilter test prog with bpf_dynptr_slice + write") +__failure __msg("cannot write into rdonly_mem") +int with_dynptr_store(struct bpf_nf_ctx *ctx) +{ + struct __sk_buff *skb = (struct __sk_buff *)ctx->skb; + u8 buffer_iph[20] = {}; + struct bpf_dynptr ptr; + struct iphdr *iph; + + if (bpf_dynptr_from_skb(skb, 0, &ptr)) + return 1; + + iph = bpf_dynptr_slice(&ptr, 0, buffer_iph, sizeof(buffer_iph)); + if (!iph) + return 0; + iph->protocol = 42; + + return 1; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 6f325876efdd..f4459561bf39 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -2,8 +2,10 @@ /* Copyright (C) 2023 SUSE LLC */ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> +#include <stdbool.h> #include "../../../include/linux/filter.h" #include "bpf_misc.h" +#include "bpf_kfuncs.h" struct { __uint(type, BPF_MAP_TYPE_ARRAY); @@ -642,4 +644,102 @@ __naked int bpf_atomic_cmpxchg_32bit_precision(void) : __clobber_all); } +/* + * Verification takes two paths: with r1 being scalar zero on path (1) + * and with r1 being some other scalar on path (2). + * Check that the verifier does not use checkpoints created + * on path (1) to prune path (2). + */ +SEC("?tc") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("R1 type=scalar expected=fp") +__naked int null_mem_arg_zero_size(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 42;" + "if r0 > 42 goto 1f;" + "r1 = 0;" + "1:" + "r2 = 0;" + "r3 = 0;" + "r4 = 0;" + "r5 = 0;" + /* + * ARG_PTR_TO_MEM | PTR_MAYBE_NULL parameter can be NULL, + * but can't be some other scalar value. + */ + "call %[bpf_csum_diff];" + "r0 = 0;" + "exit;" + : + : __imm(bpf_get_prandom_u32), + __imm(bpf_csum_diff) + : __clobber_all); +} + +__weak int subprog_mem_arg(int *p) +{ + if (p) + return *p; + return 0; +} + +/* + * Verification takes two paths: with r1 being scalar zero on path (1) + * and with r1 being some other scalar on path (2). + * Check that the verifier does not use checkpoints created + * on path (1) to prune path (2). + */ +SEC("?raw_tp") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("R1 type=scalar expected=fp") +__naked int null_mem_arg_global_subprog(void) +{ + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r1 = 42;" + "if r0 > 42 goto 1f;" + "r1 = 0;" + "1:" + "call subprog_mem_arg;" + "r0 = 0;" + "exit;" + : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same as above, check that path with r3 == 0 does not prune the path with r3 != 0 */ +SEC("?tc") +__flag(BPF_F_TEST_STATE_FREQ) +__failure __msg("R3 type=scalar expected=fp") +int null_kfunc_arg_dynptr_slice(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + + bpf_dynptr_from_skb(skb, 0, &ptr); + asm volatile ( + "call %[bpf_get_prandom_u32];" + "r3 = 42;" + "if r0 > 42 goto 1f;" + "r3 = 0;" + "1:" + "r1 = %[ptr];" + "r2 = 0;" + "r4 = 8;" + "call %[bpf_dynptr_slice];" + : + : __imm_ptr(ptr), + __imm(bpf_get_prandom_u32), + __imm(bpf_dynptr_slice) + : __clobber_common); + return 0; +} + +void __kfunc_btf_root(void) +{ + bpf_dynptr_slice(0, 0, 0, 0); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c b/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c index 663d15fc5fd2..256547048cc4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c +++ b/tools/testing/selftests/bpf/progs/verifier_scalar_ids.c @@ -380,13 +380,14 @@ SEC("socket") __success __log_level(2) __flag(BPF_F_TEST_STATE_FREQ) /* - * check that r0 and r5 have different IDs after 'if', - * collect_linked_regs() can't tie more than 5 registers for a single insn. + * check that r5 is unlinked after 'if', collect_linked_regs() can't tie + * more than 5 registers for a single insn and the register compared by + * the jump is not exempt from that. */ -__msg("7: (25) if r0 > 0x7 goto pc+0 ; R0=scalar(id=1") +__msg("7: (25) if r5 > 0x7 goto pc+0 ; R5=scalar(smin=") __msg("12: (bf) r5 = r5 ; R5=scalar(id=2") /* check that r{0-4} are marked precise after 'if' */ -__msg("frame0: regs=r0 stack= before 7: (25) if r0 > 0x7 goto pc+0") +__msg("frame0: regs=r0 stack= before 7: (25) if r5 > 0x7 goto pc+0") __msg("frame0: parent state regs=r0,r1,r2,r3,r4 stack=:") __naked void linked_regs_too_many_regs(void) { @@ -400,8 +401,8 @@ __naked void linked_regs_too_many_regs(void) "r3 = r0;" "r4 = r0;" "r5 = r0;" - /* propagate range for r{0-5} */ - "if r0 > 7 goto +0;" + /* r{0-4} fill the record, r5 does not fit and is unlinked */ + "if r5 > 7 goto +0;" /* keep r{1-4} live */ "r1 = r1;" "r2 = r2;" diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c index 8b166c42c4e0..39a1766dae3f 100644 --- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c +++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c @@ -1403,6 +1403,46 @@ __naked void partial_fill_from_cleaned_pointer_spill(void) ::: __clobber_all); } +SEC("raw_tp") +__failure +__msg("access may be outside object bounds") +__flag(BPF_F_TEST_STATE_FREQ) +__naked void imprecise_scalar_spill_half_dead(void) +{ + asm volatile ( + /* + * Fork two paths: the one explored first spills an imprecise zero, + * the one explored second, an imprecise non-zero scalar. + */ + "call %[bpf_get_prandom_u32];" + "if r0 > 42 goto 1f;" + "r6 = 0;" + "goto 2f;" +"1:" + /* causes out of bounds access on a second path. */ + "r6 = 100500;" +"2:" + /* Force a checkpoint before the spill. */ + "goto +0;" + "*(u64 *)(r10 - 8) = r6;" + /* + * Force stack cleanup, only the low half of the spill is alive, + * so the dead high half is degraded to raw stack bytes. + * Buggy verifier converted it to STACK_ZERO w/o proper precision propagation. + */ + "goto +0;" + "r7 = *(u32 *)(r10 - 4);" + /* Use r7 as an offset into a one-byte buffer. */ + "r1 = %[single_byte_buf] ll;" + "r1 += r7;" + "r0 = *(u8 *)(r1 + 0);" + "exit;" +: +: __imm(bpf_get_prandom_u32), + __imm_addr(single_byte_buf) +: __clobber_all); +} + /* check valid spill/fill, ptr to tp buffer */ SEC("raw_tracepoint.w") __success diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c index d21d32f6a676..dc0c7034c04f 100644 --- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c @@ -287,9 +287,9 @@ __msg("17: (b7) r0 = 0") __msg("18: (95) exit") __msg("returning from callee:") __msg("to caller at 9:") -__msg("frame 0: propagating r1,r4") +__msg("frame 0: propagating r1,r3,r4") __msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4 stack= before 18: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4 stack= before 18: (95) exit") __msg("from 18 to 9: safe") __naked int callback_result_precise(void) { @@ -419,9 +419,9 @@ __msg("to caller at 9:") /* r1, r4 are always precise for bpf_loop(), * r6 was marked before backtracking to callback body. */ -__msg("frame 0: propagating r1,r4,r6") +__msg("frame 0: propagating r1,r3,r4,r6") __msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4,r6 stack= before 16: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4,r6 stack= before 16: (95) exit") __msg("mark_precise: frame1: regs= stack= before 15: (b7) r0 = 0") __msg("mark_precise: frame1: regs= stack= before 9: (85) call bpf_loop") __msg("mark_precise: frame0: parent state regs= stack=:") @@ -575,9 +575,9 @@ __msg("to caller at 10:") /* r1, r4 are always precise for bpf_loop(), * fp-8 was marked before backtracking to callback body. */ -__msg("frame 0: propagating r1,r4,fp-8") +__msg("frame 0: propagating r1,r3,r4,fp-8") __msg("mark_precise: frame0: last_idx 10 first_idx 10 subseq_idx -1") -__msg("mark_precise: frame0: regs=r1,r4 stack=-8 before 18: (95) exit") +__msg("mark_precise: frame0: regs=r1,r3,r4 stack=-8 before 18: (95) exit") __msg("mark_precise: frame1: regs= stack= before 17: (b7) r0 = 0") __msg("mark_precise: frame1: regs= stack= before 10: (85) call bpf_loop#181") __msg("mark_precise: frame0: parent state regs= stack=:") @@ -846,4 +846,55 @@ __naked int subprog_result_tail_call(void) ); } +__naked __noinline __used +static int ld_abs_subprog(void) +{ + asm volatile ( + "r6 = r1;" + "r7 = r1;" + ".8byte %[ld_abs];" + "exit;" + : + : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0)) + : __clobber_all); +} + +/* + * Buggy verifier did not properly backtrack early subprogram exit + * modelled for BPF_LD | BPF_ABS instruction, causing a segfault. + */ +SEC("socket") +__success +__log_level(2) +/* early exit path */ +__msg("3: (0f) r1 += r7") +__msg("mark_precise: frame0: regs=r7 stack= before 2: (bf) r1 = r10") +__msg("mark_precise: frame0: regs=r7 stack= before 9: (20) r0 = *(u32 *)skb[0]") +__msg("mark_precise: frame1: regs= stack= before 8: (bf) r7 = r1") +__msg("mark_precise: frame1: regs= stack= before 7: (bf) r6 = r1") +__msg("mark_precise: frame1: regs= stack= before 1: (85) call pc+5") +__msg("mark_precise: frame0: regs=r7 stack= before 0: (b7) r7 = -8") +/* fallthrough path */ +__msg("3: (0f) r1 += r7") +__msg("mark_precise: frame0: regs=r7 stack= before 2: (bf) r1 = r10") +__msg("mark_precise: frame0: regs=r7 stack= before 10: (95) exit") +__msg("mark_precise: frame1: regs= stack= before 9: (20) r0 = *(u32 *)skb[0]") +__msg("mark_precise: frame1: regs= stack= before 8: (bf) r7 = r1") +__msg("mark_precise: frame1: regs= stack= before 7: (bf) r6 = r1") +__msg("mark_precise: frame1: regs= stack= before 1: (85) call pc+5") +__msg("mark_precise: frame0: regs=r7 stack= before 0: (b7) r7 = -8") +__naked int ld_abs_backtrack_both_paths(void) +{ + asm volatile ( + "r7 = -8;" + "call ld_abs_subprog;" + "r1 = r10;" + "r1 += r7;" /* mark r7 as precise */ + "*(u64 *)(r1 + 0) = 0;" + "r0 = 0;" + "exit;" + ::: __clobber_all + ); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c index 8f2362da91d6..572017fe28fb 100644 --- a/tools/testing/selftests/bpf/progs/verifier_zext.c +++ b/tools/testing/selftests/bpf/progs/verifier_zext.c @@ -356,6 +356,32 @@ __naked void arena_ptr(void) : __clobber_all); } +/* + * Result of a 32-bit cmpxchg is always explicitly zero extended. + * Check that this holds for arenas (BPF_PROBE_ATOMIC instruction flavor). + */ +SEC("socket") +__success +__xlated("probe r0 = atomic_cmpxchg((u32 *)(r1 +0), r0, r2)") +__xlated("w0 = w0") +__naked void zext_arena_cmpxchg32(void) +{ + asm volatile (" \ + r9 = %[arena] ll; /* associate the arena with the program */ \ + r1 = 0; \ + r1 = addr_space_cast(r1, 0, 1); \ + r0 = 0; \ + r2 = 0; \ + .8byte %[cmpxchg32]; \ + r0 >>= 32; /* make the upper half live */ \ + exit; \ +" : + : __imm_addr(arena), + __imm_insn(cmpxchg32, + BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_1, BPF_REG_2, 0)) + : __clobber_all); +} + #endif /* Check if probe mem loads keep their zero extension. */ diff --git a/tools/testing/selftests/bpf/verifier/pseudo_func.c b/tools/testing/selftests/bpf/verifier/pseudo_func.c new file mode 100644 index 000000000000..63c5c67d51de --- /dev/null +++ b/tools/testing/selftests/bpf/verifier/pseudo_func.c @@ -0,0 +1,45 @@ +/* + * Buggy verifier accepted the program below while not patching BPF_PSEUDO_FUNC + * load instruction to contain a real address. Which resulted in a function call + * to a bogus address. + */ +{ + "BPF_PSEUDO_FUNC reference to the main program", + .insns = { + /* r6 = bpf_map_lookup_elem(&timer_map, &(int){0}); */ + BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_0), + /* bpf_timer_init(r6, &timer_map, 0); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_timer_init), + /* bpf_timer_set_callback(r6, <insn #0>); */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, BPF_REG_2, BPF_PSEUDO_FUNC, 0, -15), + BPF_RAW_INSN(0, 0, 0, 0, 0), + BPF_EMIT_CALL(BPF_FUNC_timer_set_callback), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + .fixup_map_timer = { 3, 9 }, + .result = REJECT, + .errstr = "callback function cannot be the main program", + .func_info = { { 0, 4 /* main_prog */ } }, + .func_info_cnt = 1, + .btf_strings = "\0int\0ctx\0main_prog", + .btf_types = { + /* 1: int */ BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), + /* 2: void* */ BTF_PTR_ENC(0), + /* 3: int __(void *) */ BTF_FUNC_PROTO_ENC(1, 1), + BTF_FUNC_PROTO_ARG_ENC(5, 2), + /* 4: main_prog */ BTF_FUNC_ENC(9, 3), + BTF_END_RAW + } +}, diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index e9bee164bb70..20d2b63774c3 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -919,7 +919,6 @@ int main(int argc, char *argv[]) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) { if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME)) ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n"); @@ -932,6 +931,7 @@ int main(int argc, char *argv[]) ksft_exit_skip("Failed to set memory controller\n"); post_v2_setup: + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_cpu.c b/tools/testing/selftests/cgroup/test_cpu.c index f9f7017d9299..735a53bb222b 100644 --- a/tools/testing/selftests/cgroup/test_cpu.c +++ b/tools/testing/selftests/cgroup/test_cpu.c @@ -832,7 +832,6 @@ int main(int argc, char *argv[]) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -840,6 +839,7 @@ int main(int argc, char *argv[]) if (cg_write(root, "cgroup.subtree_control", "+cpu")) ksft_exit_skip("Failed to set cpu controller\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_cpuset.c b/tools/testing/selftests/cgroup/test_cpuset.c index 8c2d4d4ef1fc..3dfadd280c1c 100644 --- a/tools/testing/selftests/cgroup/test_cpuset.c +++ b/tools/testing/selftests/cgroup/test_cpuset.c @@ -497,7 +497,6 @@ int main(int argc, char *argv[]) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -505,6 +504,7 @@ int main(int argc, char *argv[]) if (cg_write(root, "cgroup.subtree_control", "+cpuset")) ksft_exit_skip("Failed to set cpuset controller\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index da8f7b920178..131d8b4551ef 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -797,7 +797,6 @@ check_isolcpus() EXPECTED_ISOLCPUS=$1 ISCPUS=${CGROUP2}/cpuset.cpus.isolated ISOLCPUS=$(cat $ISCPUS) - HKICPUS=$(cat /sys/devices/system/cpu/isolated) LASTISOLCPU= SCHED_DOMAINS=/sys/kernel/debug/sched/domains if [[ $EXPECTED_ISOLCPUS = . ]] @@ -836,11 +835,6 @@ check_isolcpus() EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN # - # The inverse of HK_TYPE_DOMAIN cpumask in $HKICPUS should match $ISOLCPUS - # - [[ "$ISOLCPUS" != "$HKICPUS" ]] && return 1 - - # # Use the sched domain in debugfs to check isolated CPUs, if available # [[ -d $SCHED_DOMAINS ]] || return 0 @@ -1162,6 +1156,63 @@ test_isolated() } # +# Select an online CPU isolated from scheduler domains at boot. +# $1: test name used in the skip message +# +get_boot_isolated_cpu() +{ + TEST_NAME=$1 + BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated + + [[ -r $BOOT_ISOLATED_FILE ]] || { + echo "$TEST_NAME test SKIPPED: boot isolation state unavailable" + return 1 + } + BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE) + [[ -n "$BOOT_CPUS" ]] || { + echo "$TEST_NAME test SKIPPED: no boot-isolated CPU" + return 1 + } + + BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//') + CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online + [[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || { + echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline" + return 1 + } +} + +# +# A CPU isolated at boot must stay isolated after it is released by a dynamic +# isolated partition. +# +test_boot_isolated() +{ + TEST_NAME="Boot-isolated CPU partition release" + get_boot_isolated_cpu "$TEST_NAME" || return 0 + echo "Running $TEST_NAME test ..." + + cd $CGROUP2/test + echo member > cpuset.cpus.partition + echo $BOOT_CPU > cpuset.cpus + [[ $(cat cpuset.cpus.effective) = "$BOOT_CPU" ]] || { + echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is unavailable" + echo "" > cpuset.cpus + cd $CGROUP2 + return 0 + } + test_partition isolated + test_partition member + check_isolcpus "." || { + echo "Boot-isolated CPU $BOOT_CPU was lost after partition release" + exit 1 + } + echo "" > cpuset.cpus + cd $CGROUP2 + echo "$TEST_NAME test PASSED." +} + +# # Wait for inotify event for the given file and read it # $1: cgroup file to wait for # $2: file to store the read result @@ -1232,5 +1283,6 @@ trap cleanup 0 2 3 6 run_state_test TEST_MATRIX run_remote_state_test REMOTE_TEST_MATRIX test_isolated +test_boot_isolated test_inotify echo "All tests PASSED." diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c index 0569e93fa6b0..f28bb02e9783 100644 --- a/tools/testing/selftests/cgroup/test_freezer.c +++ b/tools/testing/selftests/cgroup/test_freezer.c @@ -1491,9 +1491,9 @@ int main(int argc, char *argv[]) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_kill.c b/tools/testing/selftests/cgroup/test_kill.c index f6cd23a8ecc7..bac1ddd8cb94 100644 --- a/tools/testing/selftests/cgroup/test_kill.c +++ b/tools/testing/selftests/cgroup/test_kill.c @@ -7,6 +7,7 @@ #include <stdlib.h> #include <string.h> #include <sys/types.h> +#include <sys/wait.h> #include <unistd.h> #include "kselftest.h" @@ -261,6 +262,59 @@ cleanup: return ret; } +/* + * Test that a cgroup that was killed in the past can still be the target + * of clone3(CLONE_INTO_CGROUP): writing cgroup.kill must only kill the + * tasks in the cgroup at the time of the write, not tasks cloned into + * it afterwards. + */ +static int test_cgkill_clone_into_killed(const char *root) +{ + pid_t pid; + int cgroup_fd = -EBADF; + int ret = KSFT_FAIL; + char *cgroup = NULL; + + cgroup = cg_name(root, "cg_test_clone_into_killed"); + if (!cgroup) + goto cleanup; + + if (cg_create(cgroup)) + goto cleanup; + + /* Kill the cgroup while it is still empty. */ + if (cg_write(cgroup, "cgroup.kill", "1")) + goto cleanup; + + cgroup_fd = dirfd_open_opath(cgroup); + if (cgroup_fd < 0) + goto cleanup; + + pid = clone_into_cgroup(cgroup_fd); + if (pid < 0) { + if (errno == ENOSYS) + ret = KSFT_SKIP; + goto cleanup; + } + + if (pid == 0) + exit(EXIT_SUCCESS); + + /* The child must not be SIGKILLed; it has to exit cleanly. */ + if (clone_reap(pid, WEXITED) != EXIT_SUCCESS) + goto cleanup; + + ret = KSFT_PASS; + +cleanup: + if (cgroup_fd >= 0) + close(cgroup_fd); + if (cgroup) + cg_destroy(cgroup); + free(cgroup); + return ret; +} + #define T(x) { x, #x } struct cgkill_test { int (*fn)(const char *root); @@ -269,6 +323,7 @@ struct cgkill_test { T(test_cgkill_simple), T(test_cgkill_tree), T(test_cgkill_forkbomb), + T(test_cgkill_clone_into_killed), }; #undef T @@ -278,9 +333,9 @@ int main(int argc, char *argv[]) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index 1db0ba1226b9..437f2d35f205 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -145,7 +145,7 @@ static int cg_run_in_subcgroups(const char *parent, return -1; } - if (cg_run(child, fn, NULL)) { + if (cg_run(child, fn, arg)) { cg_destroy(child); free(child); return -1; @@ -426,7 +426,6 @@ int main(int argc, char **argv) int i; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -441,6 +440,7 @@ int main(int argc, char **argv) if (cg_write(root, "cgroup.subtree_control", "+memory")) ksft_exit_skip("Failed to set memory controller\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c index 0ebf796f3cff..3a84d068fbf3 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c @@ -1798,7 +1798,6 @@ int main(int argc, char **argv) page_size = BUF_SIZE; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -1823,6 +1822,7 @@ int main(int argc, char **argv) ksft_exit_skip("Failed to query cgroup mount option\n"); has_localevents = proc_status; + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_pids.c b/tools/testing/selftests/cgroup/test_pids.c index 9a387c815d2c..710109b53dfe 100644 --- a/tools/testing/selftests/cgroup/test_pids.c +++ b/tools/testing/selftests/cgroup/test_pids.c @@ -148,7 +148,6 @@ int main(int argc, char **argv) char root[PATH_MAX]; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -163,6 +162,7 @@ int main(int argc, char **argv) if (cg_write(root, "cgroup.subtree_control", "+pids")) ksft_exit_skip("Failed to set pids controller\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (int i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index f7b4c4370db6..609c48f38524 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -819,7 +819,6 @@ int main(int argc, char **argv) page_size = BUF_SIZE; ksft_print_header(); - ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) ksft_exit_skip("cgroup v2 isn't mounted\n"); @@ -836,6 +835,7 @@ int main(int argc, char **argv) if (cg_write(root, "cgroup.subtree_control", "+memory")) ksft_exit_skip("Failed to set memory controller\n"); + ksft_set_plan(ARRAY_SIZE(tests)); for (i = 0; i < ARRAY_SIZE(tests); i++) { switch (tests[i].fn(root)) { case KSFT_PASS: diff --git a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c index fc9694fc4e89..1d49df671919 100644 --- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c +++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c @@ -390,6 +390,116 @@ static void test_alloc_errors(char *heap_name) close(heap_fd); } +/* + * count_open_fds - return the number of open file descriptors. + * + * The fd opened by opendir() itself is counted, but since it is opened + * and closed within each call, it cancels out when comparing two counts. + * Returns -1 on error. + */ +static int count_open_fds(void) +{ + DIR *d = opendir("/proc/self/fd"); + struct dirent *de; + int count = 0; + + if (!d) + return -1; + + while ((de = readdir(d))) + if (de->d_name[0] != '.') + count++; + closedir(d); + return count; +} + +/* + * test_alloc_no_fd_leak_on_efault - verify no fd is leaked when + * copy_to_user() fails during DMA_HEAP_IOCTL_ALLOC. + * + * The bug: dma_buf_fd() called fd_install() before copy_to_user(). + * If copy_to_user() then failed (e.g. via mprotect), the fd was + * silently installed in the fd table but never returned to userspace. + * + * The fix: reserve the fd with get_unused_fd_flags() first, attempt + * copy_to_user(), and only call fd_install() on success. + * + * We trigger the failure by placing the ioctl argument in a private + * anonymous page and flipping it to PROT_READ before the ioctl. + * Inside the kernel, copy_from_user() reads from the page (reads are + * allowed under PROT_READ, so it succeeds), but copy_to_user() that + * writes the fd number back faults, returning -EFAULT. We then + * count open file descriptors before and after; with the bug an extra + * fd is left in the table. + */ +static void test_alloc_no_fd_leak_on_efault(char *heap_name) +{ + int heap_fd = -1; + int fd_before, fd_after; + int ret; + long page_size; + struct dma_heap_allocation_data *req; + + ksft_print_msg("Testing fd leak when copy_to_user() fails:\n"); + + heap_fd = dmabuf_heap_open(heap_name); + + page_size = sysconf(_SC_PAGESIZE); + + /* + * Place the ioctl argument in its own private anonymous page so + * we can flip its protection independently. + */ + req = mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (req == MAP_FAILED) { + ksft_test_result_fail("mmap failed: %s\n", strerror(errno)); + goto out; + } + + memset(req, 0, sizeof(*req)); + req->len = page_size; + req->fd_flags = O_RDWR | O_CLOEXEC; + + fd_before = count_open_fds(); + if (fd_before < 0) { + ksft_test_result_fail("count_open_fds: %s\n", strerror(errno)); + munmap(req, page_size); + goto out; + } + + /* + * Make the page read-only so copy_to_user() will fault. The + * ioctl must fail with -1; if it returns success the test setup + * is broken (mprotect is synchronous, so there is no race). + */ + mprotect(req, page_size, PROT_READ); + + ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, req); + + /* Re-allow writes so munmap can clean up */ + mprotect(req, page_size, PROT_READ | PROT_WRITE); + munmap(req, page_size); + + if (ret != -1) { + ksft_test_result_fail("ioctl returned %d, expected -1 EFAULT\n", + ret); + goto out; + } + + fd_after = count_open_fds(); + if (fd_after < 0) { + ksft_test_result_fail("count_open_fds: %s\n", strerror(errno)); + goto out; + } + + ksft_test_result(fd_before == fd_after, + "fd leak on EFAULT: before=%d after=%d\n", + fd_before, fd_after); +out: + close(heap_fd); +} + static int numer_of_heaps(void) { DIR *d = opendir(DEVPATH); @@ -420,7 +530,7 @@ int main(void) return KSFT_SKIP; } - ksft_set_plan(11 * numer_of_heaps()); + ksft_set_plan(12 * numer_of_heaps()); while ((dir = readdir(d))) { if (!strncmp(dir->d_name, ".", 2)) @@ -435,6 +545,7 @@ int main(void) test_alloc_zeroed(dir->d_name, ONE_MEG); test_alloc_compat(dir->d_name); test_alloc_errors(dir->d_name); + test_alloc_no_fd_leak_on_efault(dir->d_name); } closedir(d); diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index b851339308c2..7ab86296ff23 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -67,14 +67,17 @@ struct test_program { int insert_head; }; #define LOAD_PROGRAMS(progs) \ - load_programs(progs, ARRAY_SIZE(progs), _metadata, self, variant) + load_programs(progs, ARRAY_SIZE(progs), false, _metadata, self, variant) +#define LOAD_PROGRAMS_MAY_FAIL(progs) \ + load_programs(progs, ARRAY_SIZE(progs), true, _metadata, self, variant) #define LOAD_BPF \ - load_programs(NULL, 0, _metadata, self, variant) -static void load_programs(const struct test_program programs[], - const size_t progs_count, - struct __test_metadata *_metadata, - FIXTURE_DATA(hid_bpf) * self, - const FIXTURE_VARIANT(hid_bpf) * variant) + load_programs(NULL, 0, false, _metadata, self, variant) +static int load_programs(const struct test_program programs[], + const size_t progs_count, + bool load_may_fail, + struct __test_metadata *_metadata, + FIXTURE_DATA(hid_bpf) * self, + const FIXTURE_VARIANT(hid_bpf) * variant) { struct bpf_map *iter_map; int err = -EINVAL; @@ -128,6 +131,9 @@ static void load_programs(const struct test_program programs[], } err = hid__load(self->skel); + if (err && load_may_fail) + return err; + ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err); for (int i = 0; i < progs_count; i++) { @@ -147,6 +153,7 @@ static void load_programs(const struct test_program programs[], self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); + return 0; } /* @@ -904,11 +911,39 @@ TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow) { .name = "hid_rdesc_fixup_get_data_overflow" }, }; - LOAD_PROGRAMS(progs); + /* newer verifier can detect the overflow at load time */ + if (LOAD_PROGRAMS_MAY_FAIL(progs)) + return; ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1); } +TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys) +{ + const struct test_program progs[] = { + { .name = "hid_rdesc_fixup_change_uniq_name_phys" }, + }; + char expected[256], buf[256] = {}; + int err; + + LOAD_PROGRAMS(progs); + + err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf); + ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME"); + ASSERT_STREQ("name coming from bpf", buf); + + snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id); + + err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf); + ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS"); + ASSERT_STREQ(expected, buf); + + err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf); + ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ"); + ASSERT_STREQ("uniq:coming:from:bpf", buf); + +} + 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 b21fbb13c926..361dc7eaad22 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -255,6 +255,32 @@ struct hid_bpf_ops rdesc_fixup_get_data_overflow = { .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow, }; +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys, struct hid_bpf_ctx *hid_ctx) +{ +#define HID_BPF_MEMCPY(target, str) \ + __builtin_memcpy(target, str, sizeof(str)) + + HID_BPF_MEMCPY(hid_ctx->hid->name, "name coming from bpf"); + HID_BPF_MEMCPY(hid_ctx->hid->uniq, "uniq:coming:from:bpf"); + /* hid_bpf relies on a phys being a rand % 1024 */ + for (int i = 0; i < 5; i++) { + if (!hid_ctx->hid->phys[i]) { + HID_BPF_MEMCPY(hid_ctx->hid->phys + i, " phys:coming:from:bpf"); + break; + } + } + +#undef HID_BPF_MEMCPY + + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops rdesc_fixup_change_uniq_name_phys = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_change_uniq_name_phys, +}; + SEC("?struct_ops/hid_device_event") int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type) { diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index cdca912f3afd..05698793762a 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -61,6 +61,9 @@ enum hid_report_type { struct hid_device { unsigned int id; + char name[128]; + char phys[64]; + char uniq[64]; } __attribute__((preserve_access_index)); struct bpf_wq { diff --git a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c index 6dbe863ea571..5dc0debacb2a 100644 --- a/tools/testing/selftests/landlock/scoped_abstract_unix_test.c +++ b/tools/testing/selftests/landlock/scoped_abstract_unix_test.c @@ -1222,7 +1222,7 @@ FIXTURE_SETUP(trace_unix) int ret; set_cap(_metadata, CAP_SYS_ADMIN); - ASSERT_EQ(0, unshare(CLONE_NEWNS)); + ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWNET)); ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)); ret = tracefs_fixture_setup(); @@ -1252,6 +1252,11 @@ FIXTURE_TEARDOWN(trace_unix) clear_cap(_metadata, CAP_SYS_ADMIN); } +static const char + trace_unix_max_name[sizeof(((struct sockaddr_un *)0)->sun_path)] = { + [0 ... sizeof(trace_unix_max_name) - 2] = 'x', + }; + /* clang-format off */ FIXTURE_VARIANT(trace_unix) { /* clang-format on */ @@ -1259,6 +1264,8 @@ FIXTURE_VARIANT(trace_unix) { bool sandbox; bool sandbox_target; /* Peer owned by a domain: peer_domain != 0. */ int expect_denied; + const char *name; /* NULL generates a PID-based binary name. */ + size_t name_len; }; /* clang-format off */ @@ -1281,6 +1288,26 @@ FIXTURE_VARIANT_ADD(trace_unix, stream_allowed) { .sandbox_target = false, .expect_denied = 0, }; +/* Stream: lower abstract-name length boundary. */ +FIXTURE_VARIANT_ADD(trace_unix, stream_denied_empty_name) { + .sock_type = SOCK_STREAM, + .sandbox = true, + .sandbox_target = false, + .expect_denied = 1, + .name = "", + .name_len = 0, +}; + +/* Stream: upper abstract-name length boundary. */ +FIXTURE_VARIANT_ADD(trace_unix, stream_denied_max_name) { + .sock_type = SOCK_STREAM, + .sandbox = true, + .sandbox_target = false, + .expect_denied = 1, + .name = trace_unix_max_name, + .name_len = sizeof(trace_unix_max_name) - 1, +}; + /* Datagram: sandboxed client sendto() an unsandboxed peer (peer_domain=0). */ FIXTURE_VARIANT_ADD(trace_unix, dgram_denied) { .sock_type = SOCK_DGRAM, .sandbox = true, @@ -1304,12 +1331,11 @@ FIXTURE_VARIANT_ADD(trace_unix, dgram_allowed) { /* * A sandboxed thread reaching an abstract unix socket peer through connect(2) * (stream) or sendto(2) (datagram) is denied and emits - * landlock_deny_scope_abstract_unix_socket. The abstract name is crafted with - * a space and an embedded NUL followed by an "END" marker to check the - * tracepoint escaping and its length handling (a raw space would break the - * sun_path field regex; strlen() would truncate at the NUL and drop "END"). - * peer_pid is only meaningful for a stream peer (a datagram peer has no - * SO_PEERCRED), so it is asserted only there. + * landlock_deny_scope_abstract_unix_socket. The default abstract name has a + * space and an embedded NUL followed by an "END" marker to check escaping and + * binary length handling. Additional stream variants cover the minimum and + * maximum abstract-name lengths. peer_pid is only meaningful for a stream peer + * (a datagram peer has no SO_PEERCRED), so it is asserted only there. */ TEST_F(trace_unix, deny_scope_unix) { @@ -1336,12 +1362,19 @@ TEST_F(trace_unix, deny_scope_unix) ASSERT_LE(0, server_fd); addr.sun_path[0] = '\0'; - name_len = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, - "landlock_trace_test_%d ", getpid()); - addr.sun_path[1 + name_len] = '\0'; - memcpy(addr.sun_path + 1 + name_len + 1, "END", 3); - addr_len = - offsetof(struct sockaddr_un, sun_path) + 1 + name_len + 1 + 3; + if (variant->name) { + ASSERT_LE(variant->name_len, sizeof(addr.sun_path) - 1); + memcpy(addr.sun_path + 1, variant->name, variant->name_len); + name_len = variant->name_len; + } else { + name_len = snprintf(addr.sun_path + 1, + sizeof(addr.sun_path) - 1, + "landlock_trace_test_%d ", getpid()); + addr.sun_path[1 + name_len] = '\0'; + memcpy(addr.sun_path + 1 + name_len + 1, "END", 3); + name_len += 1 + 3; + } + addr_len = offsetof(struct sockaddr_un, sun_path) + 1 + name_len; ASSERT_EQ(0, bind(server_fd, (struct sockaddr *)&addr, addr_len)); if (variant->sock_type == SOCK_STREAM) @@ -1430,19 +1463,18 @@ TEST_F(trace_unix, deny_scope_unix) count, buf); } - /* - * sun_path is escaped: a raw space would break this field's [^ ]*$ - * regex, so a successful extract proves the space was escaped, and its - * full length is honored: the "END" marker after the embedded NUL must - * survive (strlen() would truncate it at the NUL). - */ ASSERT_EQ(0, tracefs_extract_field( buf, REGEX_DENY_SCOPE_ABSTRACT_UNIX_SOCKET(TRACE_TASK), "sun_path", field, sizeof(field))); - EXPECT_NE(NULL, strstr(field, "END")) - { - TH_LOG("sun_path truncated or unescaped: %s", field); + if (variant->name) { + EXPECT_STREQ(variant->name, field); + } else { + /* An embedded NUL must not truncate the following marker. */ + EXPECT_NE(NULL, strstr(field, "END")) + { + TH_LOG("sun_path truncated or unescaped: %s", field); + } } /* peer_pid is the parent's PID for a stream peer (0 for datagram). */ diff --git a/tools/testing/selftests/landlock/trace_fs_test.c b/tools/testing/selftests/landlock/trace_fs_test.c index 5220f6a4bee1..4543a25c1f55 100644 --- a/tools/testing/selftests/landlock/trace_fs_test.c +++ b/tools/testing/selftests/landlock/trace_fs_test.c @@ -6,8 +6,10 @@ */ #define _GNU_SOURCE +#include <assert.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <linux/landlock.h> #include <sched.h> #include <stdio.h> @@ -23,6 +25,63 @@ #define TRACE_TASK "trace_fs_test" +/* Mirrors TRACE_SEQ_SIZE, conservatively larger than the usable buffer. */ +#define TRACE_SEQUENCE_SIZE 8192 +#define OCTAL_ESCAPE_LEN 4 +#define LONG_PATH_COMPONENT_COUNT 11 +#define LONG_PATH_COMPONENT_LEN 240 +#define LONG_PATH_LEN \ + (LONG_PATH_COMPONENT_COUNT * (LONG_PATH_COMPONENT_LEN + 1) + \ + sizeof("/tmp")) +#define LONG_ESCAPED_PATH_LEN \ + (LONG_PATH_COMPONENT_COUNT * LONG_PATH_COMPONENT_LEN * OCTAL_ESCAPE_LEN) + +static_assert(LONG_ESCAPED_PATH_LEN > TRACE_SEQUENCE_SIZE, + "escaped path must exceed the trace sequence"); +static_assert(LONG_PATH_LEN < PATH_MAX, "path must fit in PATH_MAX"); + +static void create_long_path(struct __test_metadata *const _metadata, + char *path) +{ + size_t path_len; + + strcpy(path, "/tmp"); + path_len = strlen(path); + + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, mount("tmpfs", "/tmp", "tmpfs", 0, NULL)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + for (int i = 0; i < LONG_PATH_COMPONENT_COUNT; i++) { + path[path_len++] = '/'; + memset(path + path_len, ' ', LONG_PATH_COMPONENT_LEN); + path_len += LONG_PATH_COMPONENT_LEN; + path[path_len] = '\0'; + ASSERT_EQ(0, mkdir(path, 0700)); + } +} + +static void expect_truncated_path(struct __test_metadata *const _metadata, + const char *const trace, + const char *const event_regex) +{ + static const char marker[] = "\xe2\x80\xa6"; + char *path; + size_t path_len; + + path = malloc(TRACE_SEQUENCE_SIZE); + ASSERT_NE(NULL, path); + ASSERT_EQ(0, tracefs_extract_field(trace, event_regex, "path", path, + TRACE_SEQUENCE_SIZE)); + EXPECT_EQ(path, strstr(path, "/tmp/")); + EXPECT_NE(NULL, strstr(path, "\\040")); + + path_len = strlen(path); + ASSERT_LE(sizeof(marker) - 1, path_len); + EXPECT_STREQ(marker, path + path_len - (sizeof(marker) - 1)); + free(path); +} + /* * Like REGEX_DENY_ACCESS_FS(), but pins the logged field to a specific value * ("0" or "1") so a test can tell a suppressed (quiet) denial from a logged @@ -184,6 +243,107 @@ TEST_F(trace_fs, add_rule_fs) } /* + * Verifies that a path whose escaping exceeds the trace scratch sequence does + * not corrupt a sibling symbolic field. + */ +TEST_F(trace_fs, add_rule_fs_escaped_path_overflow) +{ + static const char access_prefix[] = "execute|write_file|read_file|"; + static const char access_suffix[] = "|ioctl_dev|resolve_unix"; + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, + }; + struct landlock_path_beneath_attr path_beneath = { + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, + }; + char path[PATH_MAX]; + char *buf, field_buf[256]; + size_t field_len; + int ruleset_fd, count; + + create_long_path(_metadata, path); + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + path_beneath.parent_fd = open(path, O_PATH | O_DIRECTORY | O_CLOEXEC); + ASSERT_LE(0, path_beneath.parent_fd); + + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath, 0)); + ASSERT_EQ(0, close(path_beneath.parent_fd)); + ASSERT_EQ(0, close(ruleset_fd)); + + buf = tracefs_read_buf(); + ASSERT_NE(NULL, buf); + + count = tracefs_count_matches(buf, REGEX_ADD_RULE_FS(TRACE_TASK)); + EXPECT_EQ(1, count) + { + TH_LOG("Expected 1 add_rule_fs event, got %d\n%s", count, buf); + } + + /* + * The marker catches a full revert with any compiler. The symbolic + * field also catches scratch-sequence poisoning when the compiler + * evaluates the overflowing path first, as GCC currently does. + */ + ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_ADD_RULE_FS(TRACE_TASK), + "access_rights", field_buf, + sizeof(field_buf))); + EXPECT_EQ(0, + strncmp(field_buf, access_prefix, sizeof(access_prefix) - 1)); + EXPECT_EQ(NULL, strstr(field_buf, "|refer|")); + field_len = strlen(field_buf); + ASSERT_LE(sizeof(access_suffix) - 1, field_len); + EXPECT_STREQ(access_suffix, + field_buf + field_len - (sizeof(access_suffix) - 1)); + expect_truncated_path(_metadata, buf, REGEX_ADD_RULE_FS(TRACE_TASK)); + + free(buf); +} + +/* + * Verifies that an overflowing denied path does not corrupt its sibling + * symbolic blockers field. + */ +TEST_F(trace_fs, deny_access_fs_escaped_path_overflow) +{ + char path[PATH_MAX]; + char *buf, field_buf[64]; + int count, err; + + create_long_path(_metadata, path); + ASSERT_EQ(0, tracefs_clear_buf()); + + sandbox_child_fs_access(_metadata, "/usr", LANDLOCK_ACCESS_FS_READ_DIR, + LANDLOCK_ACCESS_FS_READ_DIR, path); + + buf = tracefs_read_buf(); + ASSERT_NE(NULL, buf); + + count = tracefs_count_matches(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); + EXPECT_EQ(1, count) + { + TH_LOG("Expected 1 deny_access_fs event, got %d\n%s", count, + buf); + } + + /* + * The marker catches a full revert with any compiler. The symbolic + * field also catches scratch-sequence poisoning when the compiler + * evaluates the overflowing path first, as GCC currently does. + */ + err = tracefs_extract_field(buf, REGEX_DENY_ACCESS_FS(TRACE_TASK), + "blockers", field_buf, sizeof(field_buf)); + ASSERT_EQ(0, err); + EXPECT_STREQ("read_dir", field_buf); + expect_truncated_path(_metadata, buf, REGEX_DENY_ACCESS_FS(TRACE_TASK)); + + free(buf); +} + +/* * Verifies that an allowed access emits check_rule events (rule matched during * pathwalk) but does NOT emit deny_access events (no denial). */ diff --git a/tools/testing/selftests/mm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c index aac4f795c327..c55d84c5e613 100644 --- a/tools/testing/selftests/mm/memfd_secret.c +++ b/tools/testing/selftests/mm/memfd_secret.c @@ -57,33 +57,6 @@ static void test_file_apis(int fd) pass("file IO is blocked as expected\n"); } -static void test_mlock_limit(int fd) -{ - size_t len; - char *mem; - - len = mlock_limit_cur; - if (len % page_size != 0) - len = (len/page_size) * page_size; - - mem = mmap(NULL, len, prot, mode, fd, 0); - if (mem == MAP_FAILED) { - fail("unable to mmap secret memory\n"); - return; - } - munmap(mem, len); - - len = mlock_limit_max * 2; - mem = mmap(NULL, len, prot, mode, fd, 0); - if (mem != MAP_FAILED) { - fail("unexpected mlock limit violation\n"); - munmap(mem, len); - return; - } - - pass("mlock limit is respected\n"); -} - static void test_vmsplice(int fd, const char *desc) { ssize_t transferred; @@ -297,7 +270,7 @@ static void prepare(void) strerror(errno)); } -#define NUM_TESTS 6 +#define NUM_TESTS 5 int main(int argc, char *argv[]) { @@ -319,7 +292,6 @@ int main(int argc, char *argv[]) if (ftruncate(fd, page_size)) ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno)); - test_mlock_limit(fd); test_file_apis(fd); /* * We have to run the first vmsplice test before any secretmem page was diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 0f5c178bc224..3ee3378f8b26 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -25,9 +25,11 @@ TEST_PROGS := \ cmsg_so_mark.sh \ cmsg_so_priority.sh \ cmsg_time.sh \ + cork_fragsize.py \ double_udp_encap.sh \ drop_monitor_tests.sh \ ecmp_rehash.sh \ + exception_cache.sh \ fcnal-ipv4.sh \ fcnal-ipv6.sh \ fcnal-other.sh \ diff --git a/tools/testing/selftests/net/af_unix/msg_oob.c b/tools/testing/selftests/net/af_unix/msg_oob.c index 1b499d56656c..f051d79f7a8e 100644 --- a/tools/testing/selftests/net/af_unix/msg_oob.c +++ b/tools/testing/selftests/net/af_unix/msg_oob.c @@ -290,6 +290,25 @@ static void __setinlinepair(struct __test_metadata *_metadata, } } +static void __setblockingpair(struct __test_metadata *_metadata, + FIXTURE_DATA(msg_oob) *self) +{ + int i; + + for (i = 0; i < 2; i++) { + int ret, old_flags, flags; + + old_flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0); + ASSERT_NE(-1, old_flags); + + ret = fcntl(self->fd[i * 2 + 1], F_SETFL, old_flags & ~O_NONBLOCK); + ASSERT_EQ(0, ret); + + flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0); + ASSERT_EQ(old_flags & ~O_NONBLOCK, flags); + } +} + static void __siocatmarkpair(struct __test_metadata *_metadata, FIXTURE_DATA(msg_oob) *self, bool oob_head) @@ -347,6 +366,9 @@ static void __resetpair(struct __test_metadata *_metadata, #define setinlinepair() \ __setinlinepair(_metadata, self) +#define setblockingpair() \ + __setblockingpair(_metadata, self) + #define resetpair(reset) \ __resetpair(_metadata, self, variant, reset) @@ -888,4 +910,49 @@ TEST_F(msg_oob, inline_ex_oob_siocatmark) resetpair(true); } +TEST_F(msg_oob, zero_buf_oob) +{ + sendpair("a", 1, MSG_OOB); + recvpair("", 0, 0, 0); +} + +TEST_F(msg_oob, zero_buf_oob_blocking) +{ + sendpair("a", 1, MSG_OOB); + setblockingpair(); + recvpair("", 0, 0, 0); +} + +TEST_F(msg_oob, zero_buf_non_oob_oob) +{ + sendpair("ab", 2, MSG_OOB); + recvpair("", 0, 0, 0); +} + +TEST_F(msg_oob, zero_buf_non_oob_oob_blocking) +{ + sendpair("ab", 2, MSG_OOB); + setblockingpair(); + recvpair("", 0, 0, 0); +} + +TEST_F(msg_oob, zero_buf_ex_oob_oob) +{ + sendpair("a", 1, MSG_OOB); + recvpair("a", 1, 1, MSG_OOB); + + sendpair("b", 1, MSG_OOB); + recvpair("", 0, 0, 0); +} + +TEST_F(msg_oob, zero_buf_ex_oob_oob_blocking) +{ + sendpair("a", 1, MSG_OOB); + recvpair("a", 1, 1, MSG_OOB); + + sendpair("b", 1, MSG_OOB); + setblockingpair(); + recvpair("", 0, 0, 0); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/cork_fragsize.py b/tools/testing/selftests/net/cork_fragsize.py new file mode 100755 index 000000000000..7afd643d07ec --- /dev/null +++ b/tools/testing/selftests/net/cork_fragsize.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +'''Test possible UDP length overflow in udp_send_skb/udp_v6_send_skb.''' + +import errno +import gzip +import os +import socket +import struct +import subprocess +from contextlib import contextmanager + +from lib.py import ( + KsftNamedVariant, + KsftSkipEx, + NetNS, + NetNSEnter, + defer, + ip, + ksft_eq, + ksft_exit, + ksft_pr, + ksft_raises, + ksft_run, + ksft_true, + ksft_variants, +) + +IP_MTU_DISCOVER = 10 +IP_PMTUDISC_PROBE = 3 +IPV6_MTU_DISCOVER = 23 +IPV6_PMTUDISC_DO = 2 +IPV6_PMTUDISC_PROBE = 3 +IPV6_TLV_JUMBO = 194 + + +def check_kernel_config(option: str) -> bool | None: + ''' + Check whether the option is enabled in the config of the running kernel. + Returns None if the config is not found; otherwise returns True/False + depending on the option value in the config. + ''' + + for filename, method in [ + ('/proc/config.gz', gzip.open), + (f'/boot/config-{os.uname().release}', open), + ]: + try: + with method(filename, 'rt') as config: + for line in config: + if line.rstrip() == f'{option}=y': + return True + return False + except OSError: + continue + return None + + +def assert_debug_kernel() -> None: + ''' + Skip the test if CONFIG_DEBUG_NET is not set in the kernel config. + ''' + + res = check_kernel_config('CONFIG_DEBUG_NET') + if res is None: + ksft_pr("WARN: Can't read kernel config; assuming debug kernel, and running the test") + elif not res: + raise KsftSkipEx('CONFIG_DEBUG_NET is not set') + + +def check_dmesg_clean(func: str) -> bool: + ''' + Check if the given function produced a WARN in dmesg. + ''' + + with subprocess.Popen(['dmesg'], stdout=subprocess.PIPE) as dmesg: + res = subprocess.run(['grep', '-q', f'WARNING:.*{func}'], stdin=dmesg.stdout, check=False) + return res.returncode != 0 and dmesg.returncode == 0 + + +@contextmanager +def dummy_netdev(ns: NetNS, mtu: int, ipv6: bool) -> None: + ''' + Create a dummy netdev inside the given namespace, and tune it for the test. + ''' + + ip('link add dummy type dummy', ns=ns) + with defer(ip, 'link del dummy', ns=ns): + ip(f'link set dummy mtu {mtu}', ns=ns) + ip('link set dummy up', ns=ns) + flag = '-6' if ipv6 else '' + nodad = 'nodad' if ipv6 else '' + local = 'fd00::1/64' if ipv6 else '10.0.0.1/24' + remote = 'fd00::2' if ipv6 else '10.0.0.2' + ip(f'{flag} addr add {local} dev dummy {nodad}', ns=ns) + ip(f'{flag} neigh add {remote} lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns) + yield + + +@ksft_variants([ + KsftNamedVariant( + 'ipv6', + True, + socket.AF_INET6, + (socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO), + 'fd00::2', + 'udp_v6_send_skb', + ), + KsftNamedVariant( + 'ipv4', + False, + socket.AF_INET, + (socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_PROBE), + '10.0.0.2', + 'udp_send_skb', + ), +]) +def test_udp( + ipv6: bool, + af: socket.AddressFamily, + sockopts: tuple[int, int, int], + destip: str, + func: str +) -> None: + ''' + Test that sending an oversized UDP packet over a UDP socket doesn't overflow + the 16-bit length field in the UDP header, which could happen on older + kernels in udp_send_skb/udp_v6_send_skb. + + IPv4: The packet will be dropped with EMSGSIZE, but the overflow could + happen before it happens. The only way to test this is to check dmesg on + CONFIG_DEBUG_NET=y kernels that have udp_set_len_short with the warning. + + IPv6: The packet will be dropped with EMSGSIZE on fixed kernels, and will be + sent corrupted on older kernels. Test both: sendto must return EMSGSIZE, and + dmesg must be clean of warnings on CONFIG_DEBUG_NET=y kernels. + ''' + + if not ipv6: + assert_debug_kernel() + + with ( + NetNS() as ns, + dummy_netdev(ns, 65556 + 20 * ipv6, ipv6), + NetNSEnter(ns), + socket.socket(af, socket.SOCK_DGRAM) as fd, + ): + fd.setsockopt(*sockopts) + with ksft_raises(OSError) as e: + fd.sendto(b' ' * 65528, (destip, 1234)) + # IPv6: EMSGSIZE happens on kernels with the fix. + # IPv4: EMSGSIZE happens on both fixed and unfixed kernels, after the + # WARN is printed - ignore it and rely on the dmesg check. + if e.exception is not None: + ksft_eq(e.exception.errno, errno.EMSGSIZE) + + ksft_true(check_dmesg_clean(func), 'WARNING detected in dmesg') + + +def test_ipv6_jumbo() -> None: + ''' + Test that sending UDP jumbograms over a raw IPv6 socket works, despite + having the fix for oversized UDP packets. sendto must not raise an OSError + exception (when raised, the test fails automatically). + ''' + + with ( + NetNS() as ns, + dummy_netdev(ns, 65584, True), + NetNSEnter(ns), + socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_UDP) as fd, + ): + hopopts = struct.pack('!BBBBI', 0, 0, IPV6_TLV_JUMBO, 4, 65544) + fd.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_HOPOPTS, hopopts) + fd.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_CHECKSUM, 6) + fd.setsockopt(socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_PROBE) + udp = struct.pack('!HHHH', 1234, 1234, 0, 0) + b' ' * 65528 + fd.sendto(udp, ('fd00::2', 0)) + + +if __name__ == "__main__": + ksft_run([ + test_udp, + test_ipv6_jumbo, + ]) + ksft_exit() diff --git a/tools/testing/selftests/net/exception_cache.sh b/tools/testing/selftests/net/exception_cache.sh new file mode 100755 index 000000000000..8d3eed5c532a --- /dev/null +++ b/tools/testing/selftests/net/exception_cache.sh @@ -0,0 +1,521 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test that the state of the route exception cache after an ICMP error is +# processed does not depend on whether the quoted packet was matched to a +# socket. Otherwise, an off-path attacker can probe the cache to discover the +# ephemeral port used by a connected UDP socket. +# +# When the quoted packet is not matched to a socket, the same exception is +# created as when it is matched, so that neither its presence nor its contents +# reveal the result of socket matching. +# +# +----+ +# +---------| r1 | +# | +----+ +# +----+ +--------+ | .1 +# | h1 |---| bridge | | 198.51.100.0/30 +# +----+ +--------+ | 2001:db8:2::/64 +# .1 | | .2 +# | +----+ +----+ +# +---------| r2 |-----------| h2 | +# .2 .3 +----+ .1 .2 +----+ +# 203.0.113.0/24 +# 2001:db8:3::/64 +# 192.0.2.0/24 +# 2001:db8:1::/64 +# +# Traffic from h1 to h2 is routed via r1, which reaches h2's network via r2 +# over the point-to-point link. The MTU of the r2 - h2 link is lowered so that +# r2 emits ICMP errors towards h1. +# +# For the redirect tests r1's route to h2's network is replaced with one via r2 +# on the shared segment, so that r1 forwards the packet back to the segment it +# arrived from and emits a redirect towards h1. +# +# The packets that provoke the ICMP errors are injected with a packet socket so +# that no socket is ever associated with them. A socket is created separately, +# with socat, when a test needs the ICMP error to be matched. + +# shellcheck disable=SC1091,SC2034,SC2154,SC2329 +source lib.sh + +require_command jq +require_command mausezahn +require_command nstat +require_command socat + +ALL_TESTS=" + pmtu_no_socket_ipv4 + pmtu_no_socket_ipv6 + pmtu_socket_ipv4 + pmtu_socket_ipv6 + pmtu_omit_ipv4 + pmtu_omit_ipv6 + redirect_no_socket_ipv4 + redirect_no_socket_ipv6 + redirect_socket_ipv4 + redirect_socket_ipv6 +" + +# Shared segment. +H1_ADDR4=192.0.2.1 +R1_ADDR4=192.0.2.2 +R2_ADDR4=192.0.2.3 +H1_ADDR6=2001:db8:1::1 +R1_ADDR6=2001:db8:1::2 +R2_ADDR6=2001:db8:1::3 + +# r1 - r2 link. +R2_R1_ADDR4=198.51.100.2 +R2_R1_ADDR6=2001:db8:2::2 + +# r2 - h2 link. +H2_ADDR4=203.0.113.2 +H2_NET4=203.0.113.0/24 +H2_ADDR6=2001:db8:3::2 +H2_NET6=2001:db8:3::/64 + +SPORT=12345 +DPORT=54321 + +# The MTU of the shared segment and of the r1 - r2 link. Large enough for the +# injected packets to reach r2 intact. +SEGMENT_MTU=2000 +# Size of the injected packets. The PMTU tests need a size that exceeds every +# MTU used for the r2 - h2 link, so that r2 responds with an ICMP error. The +# redirect tests need a size that does not, otherwise r2 would respond with an +# ICMP error in addition to the redirect emitted by r1. +PMTU_PACKET_SIZE=1800 +REDIRECT_PACKET_SIZE=100 + +# The MTUs used for the r2 - h2 link. All of them must be at least +# IPV6_MIN_MTU, otherwise IPv6 silently ignores the error instead of creating +# an exception. +MTU_MID=1400 +MTU_LOW=1300 + +# Values for the IP{,V6}_MTU_DISCOVER socket option. +PMTUDISC_DONT=0 +PMTUDISC_OMIT=5 + +SOCAT_PID= + +linklocal_get() +{ + local ns=$1; shift + local dev=$1; shift + + ip -n "$ns" -j -6 addr show dev "$dev" | \ + jq -r '.[]["addr_info"][] | select(.scope == "link") | .local' +} + +linklocal_exists() +{ + local ns=$1; shift + local dev=$1; shift + + [ -n "$(linklocal_get "$ns" "$dev")" ] +} + +family_vars_set() +{ + local family=$1; shift + + FAMILY=$family + + if [ "$family" -eq 4 ]; then + H1_ADDR=$H1_ADDR4 + H2_ADDR=$H2_ADDR4 + MZ_FAMILY_OPT=() + # Without the Don't Fragment bit set r2 fragments the packet + # instead of reporting the MTU of the next hop. + MZ_IP_OPTS="df," + SOCAT_DST="UDP4-CONNECT:$H2_ADDR4:$DPORT" + SOCAT_BIND="bind=$H1_ADDR4:$SPORT" + SOCAT_PMTUDISC="ip-mtu-discover" + else + H1_ADDR=$H1_ADDR6 + H2_ADDR=$H2_ADDR6 + MZ_FAMILY_OPT=(-6) + MZ_IP_OPTS= + SOCAT_DST="UDP6-CONNECT:[$H2_ADDR6]:$DPORT" + SOCAT_BIND="bind=[$H1_ADDR6]:$SPORT" + SOCAT_PMTUDISC="ipv6-mtu-discover" + fi +} + +topology_setup() +{ + local ns + + setup_ns h1 r1 r2 h2 sw + defer cleanup_all_ns + + # Link-local addresses are generated from the MAC address and read + # back during setup, so request that generation mode explicitly and + # make the addresses available as soon as the devices are brought up. + for ns in "$h1" "$r1" "$r2" "$h2" "$sw"; do + ip netns exec "$ns" sysctl -qw \ + net.ipv6.conf.default.addr_gen_mode=0 \ + net.ipv6.conf.default.accept_dad=0 \ + net.ipv6.conf.all.accept_dad=0 + done + + ip -n "$sw" link add name br0 type bridge + ip -n "$sw" link set dev br0 mtu "$SEGMENT_MTU" up + + ip -n "$h1" link add name eth0 mtu "$SEGMENT_MTU" type veth \ + peer name swp1 mtu "$SEGMENT_MTU" netns "$sw" + ip -n "$r1" link add name eth0 mtu "$SEGMENT_MTU" type veth \ + peer name swp2 mtu "$SEGMENT_MTU" netns "$sw" + ip -n "$r2" link add name eth0 mtu "$SEGMENT_MTU" type veth \ + peer name swp3 mtu "$SEGMENT_MTU" netns "$sw" + ip -n "$r1" link add name eth1 mtu "$SEGMENT_MTU" type veth \ + peer name eth1 mtu "$SEGMENT_MTU" netns "$r2" + ip -n "$r2" link add name eth2 type veth peer name eth0 netns "$h2" + + ip -n "$sw" link set dev swp1 master br0 up + ip -n "$sw" link set dev swp2 master br0 up + ip -n "$sw" link set dev swp3 master br0 up + + ip -n "$h1" link set dev eth0 up + ip -n "$r1" link set dev eth0 up + ip -n "$r1" link set dev eth1 up + ip -n "$r2" link set dev eth0 up + ip -n "$r2" link set dev eth1 up + ip -n "$r2" link set dev eth2 up + ip -n "$h2" link set dev eth0 up + + ip -n "$h1" address add "$H1_ADDR4/24" dev eth0 + ip -n "$r1" address add "$R1_ADDR4/24" dev eth0 + ip -n "$r2" address add "$R2_ADDR4/24" dev eth0 + ip -n "$r1" address add 198.51.100.1/30 dev eth1 + ip -n "$r2" address add "$R2_R1_ADDR4/30" dev eth1 + ip -n "$r2" address add 203.0.113.1/24 dev eth2 + ip -n "$h2" address add "$H2_ADDR4/24" dev eth0 + + ip -n "$h1" -6 address add "$H1_ADDR6/64" dev eth0 nodad + ip -n "$r1" -6 address add "$R1_ADDR6/64" dev eth0 nodad + ip -n "$r2" -6 address add "$R2_ADDR6/64" dev eth0 nodad + ip -n "$r1" -6 address add 2001:db8:2::1/64 dev eth1 nodad + ip -n "$r2" -6 address add "$R2_R1_ADDR6/64" dev eth1 nodad + ip -n "$r2" -6 address add 2001:db8:3::1/64 dev eth2 nodad + ip -n "$h2" -6 address add "$H2_ADDR6/64" dev eth0 nodad + + ip netns exec "$r1" sysctl -qw net.ipv4.ip_forward=1 + ip netns exec "$r1" sysctl -qw net.ipv4.conf.all.send_redirects=1 + ip netns exec "$r1" sysctl -qw net.ipv6.conf.all.forwarding=1 + ip netns exec "$r2" sysctl -qw net.ipv4.ip_forward=1 + ip netns exec "$r2" sysctl -qw net.ipv6.conf.all.forwarding=1 + + ip netns exec "$h1" sysctl -qw net.ipv4.conf.all.accept_redirects=1 + ip netns exec "$h1" sysctl -qw net.ipv4.conf.eth0.accept_redirects=1 + ip netns exec "$h1" sysctl -qw net.ipv6.conf.all.accept_redirects=1 + ip netns exec "$h1" sysctl -qw net.ipv6.conf.eth0.accept_redirects=1 + + slowwait 5 linklocal_exists "$r1" eth0 + check_err $? "r1: link-local address was not generated" + slowwait 5 linklocal_exists "$r2" eth0 + check_err $? "r2: link-local address was not generated" + + R1_LLADDR=$(linklocal_get "$r1" eth0) + R2_LLADDR=$(linklocal_get "$r2" eth0) + R1_MAC=$(ip -n "$r1" -j link show dev eth0 | jq -r '.[]["address"]') + R2_MAC=$(ip -n "$r2" -j link show dev eth0 | jq -r '.[]["address"]') + + ip -n "$h1" route add "$H2_NET4" via "$R1_ADDR4" dev eth0 + ip -n "$h1" -6 route add "$H2_NET6" via "$R1_LLADDR" dev eth0 + ip -n "$r1" route add "$H2_NET4" via "$R2_R1_ADDR4" dev eth1 + ip -n "$r1" -6 route add "$H2_NET6" via "$R2_R1_ADDR6" dev eth1 + ip -n "$h2" route add default via 203.0.113.1 dev eth0 + ip -n "$h2" -6 route add default via 2001:db8:3::1 dev eth0 + + far_mtu_set "$MTU_MID" +} + +# Make r1 forward towards h2's network over the segment it receives the packet +# from, so that it emits a redirect towards h1. +redirect_route_set() +{ + ip -n "$r1" route replace "$H2_NET4" via "$R2_ADDR4" dev eth0 + ip -n "$r1" -6 route replace "$H2_NET6" via "$R2_LLADDR" dev eth0 + + # __ip_do_redirect() only creates an exception if the new gateway is + # already a valid neighbour. Otherwise it merely triggers address + # resolution. IPv6 resolves the target itself, in rt6_do_redirect(). + ip -n "$h1" neigh replace "$R2_ADDR4" lladdr "$R2_MAC" dev eth0 \ + nud permanent +} + +far_mtu_set() +{ + local mtu=$1; shift + + ip -n "$r2" link set dev eth2 mtu "$mtu" + ip -n "$h2" link set dev eth0 mtu "$mtu" +} + +socket_is_open() +{ + ip netns exec "$h1" ss -uHn "sport = :$SPORT" | grep -q . +} + +socket_start() +{ + # Disable PMTU discovery by default so that ICMP errors are not + # reported to the socket. Otherwise socat would exit when the first one + # arrives and later packets in the same test would not be matched to a + # socket. The exception is still created, as ip{,6}_sk_accept_pmtu() + # only rejects IP{,V6}_PMTUDISC_{INTERFACE,OMIT}. + local pmtudisc=${1:-$PMTUDISC_DONT} + + # Send socat's diagnostics to /dev/null. It reports the ICMP errors + # that reach the socket, which is exactly what the tests provoke. + ip netns exec "$h1" socat -u -lf/dev/null \ + "$SOCAT_DST,$SOCAT_BIND,$SOCAT_PMTUDISC=$pmtudisc" \ + OPEN:/dev/null,wronly=1 & + SOCAT_PID=$! + defer socket_stop + + slowwait 5 socket_is_open + check_err $? "socket did not open" +} + +socket_stop() +{ + [ -z "$SOCAT_PID" ] && return 0 + + kill "$SOCAT_PID" &> /dev/null + wait "$SOCAT_PID" 2> /dev/null + SOCAT_PID= +} + +# Inject a packet towards h2 with a packet socket. No socket is associated with +# it, so an ICMP error quoting it is matched to a socket only if one was +# created separately with the same source port. +packet_send() +{ + local size=$1; shift + + ip netns exec "$h1" mausezahn "${MZ_FAMILY_OPT[@]}" eth0 \ + -a own -b "$R1_MAC" -A "$H1_ADDR" -B "$H2_ADDR" \ + -t udp "${MZ_IP_OPTS}sp=$SPORT,dp=$DPORT" \ + -p "$size" -c 1 -q +} + +exception_show() +{ + if [ "$FAMILY" -eq 4 ]; then + # IPv4 exceptions without a bound route are not dumped, but + # "route get" reports the exception and binds a route to it. + ip -n "$h1" route get "$H2_ADDR" + else + # IPv6 does not report a cache indication in "route get" + # output, so dump the exceptions instead. + ip -n "$h1" -6 route show cache | grep -F "$H2_ADDR" || true + fi +} + +exception_mtu_get() +{ + exception_show | grep -o "mtu [0-9]*" | cut -d ' ' -f 2 +} + +exception_gw_get() +{ + exception_show | grep -o "via [0-9a-f.:]*" | cut -d ' ' -f 2 +} + +exception_mtu_check() +{ + local expected=$1; shift + + [ "$(exception_mtu_get)" = "$expected" ] +} + +icmp_errors_get() +{ + local ctr=IcmpInDestUnreachs + + [ "$FAMILY" -eq 6 ] && ctr=Icmp6InPktTooBigs + + ip netns exec "$h1" nstat -asz "$ctr" | \ + awk -v ctr="$ctr" '$1 == ctr { print $2 }' +} + +exception_pmtu_check() +{ + local mtu=$1; shift + local desc=$1; shift + + busywait "$BUSYWAIT_TIMEOUT" exception_mtu_check "$mtu" + check_err $? "$desc: exception does not carry an MTU of $mtu" +} + +pmtu_no_socket() +{ + local family=$1; shift + + RET=0 + family_vars_set "$family" + topology_setup + + packet_send "$PMTU_PACKET_SIZE" + exception_pmtu_check "$MTU_MID" "No socket" + + log_test "IPv$family: PMTU: exception without a matching socket" +} + +pmtu_no_socket_ipv4() +{ + pmtu_no_socket 4 +} + +pmtu_no_socket_ipv6() +{ + pmtu_no_socket 6 +} + +pmtu_socket() +{ + local family=$1; shift + local t0 + + RET=0 + family_vars_set "$family" + topology_setup + socket_start + + packet_send "$PMTU_PACKET_SIZE" + exception_pmtu_check "$MTU_MID" "Matching socket" + + # A lower PMTU replaces the one currently stored in the exception. + far_mtu_set "$MTU_LOW" + packet_send "$PMTU_PACKET_SIZE" + exception_pmtu_check "$MTU_LOW" "Lower PMTU" + + # A higher PMTU is ignored, so the exception is left as it is. Wait + # for the error to be received, as otherwise the check below would + # pass even if it never was. + far_mtu_set "$MTU_MID" + t0=$(icmp_errors_get) + packet_send "$PMTU_PACKET_SIZE" + busywait "$BUSYWAIT_TIMEOUT" until_counter_is ">= $((t0 + 1))" \ + icmp_errors_get > /dev/null + check_err $? "Higher PMTU: ICMP error was not received" + + exception_mtu_check "$MTU_LOW" + check_err $? "Higher PMTU: exception does not carry an MTU of $MTU_LOW" + + log_test "IPv$family: PMTU: exception with a matching socket" +} + +pmtu_socket_ipv4() +{ + pmtu_socket 4 +} + +pmtu_socket_ipv6() +{ + pmtu_socket 6 +} + +pmtu_omit() +{ + local family=$1; shift + + RET=0 + family_vars_set "$family" + topology_setup + socket_start "$PMTUDISC_OMIT" + + packet_send "$PMTU_PACKET_SIZE" + exception_pmtu_check "$MTU_MID" "PMTU discovery disabled" + + log_test "IPv$family: PMTU: exception with a socket ignoring it" +} + +pmtu_omit_ipv4() +{ + pmtu_omit 4 +} + +pmtu_omit_ipv6() +{ + pmtu_omit 6 +} + +exception_gw_check() +{ + local expected=$1; shift + + [ -n "$expected" ] && [ "$(exception_gw_get)" = "$expected" ] +} + +redirect_gw_new() +{ + if [ "$FAMILY" -eq 4 ]; then + echo "$R2_ADDR4" + else + echo "$R2_LLADDR" + fi +} + +redirect_no_socket() +{ + local family=$1; shift + + RET=0 + family_vars_set "$family" + topology_setup + redirect_route_set + + packet_send "$REDIRECT_PACKET_SIZE" + busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)" + check_err $? "No socket: exception does not carry the new gateway" + + log_test "IPv$family: Redirect: exception without a matching socket" +} + +redirect_no_socket_ipv4() +{ + redirect_no_socket 4 +} + +redirect_no_socket_ipv6() +{ + redirect_no_socket 6 +} + +redirect_socket() +{ + local family=$1; shift + + RET=0 + family_vars_set "$family" + topology_setup + redirect_route_set + socket_start + + packet_send "$REDIRECT_PACKET_SIZE" + busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)" + check_err $? "Matching socket: exception does not carry the new gateway" + + log_test "IPv$family: Redirect: exception with a matching socket" +} + +redirect_socket_ipv4() +{ + redirect_socket 4 +} + +redirect_socket_ipv6() +{ + redirect_socket 6 +} + +trap defer_scopes_cleanup EXIT +tests_run + +exit "$EXIT_STATUS" diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index 3d347126730a..431d7bed7622 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -533,6 +533,20 @@ ipv6_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 61 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 66" log_test $? 2 "Route add with fdb nexthop" @@ -669,6 +683,20 @@ ipv4_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 12 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP ro add 172.16.0.0/22 nhid 16" log_test $? 2 "Route add with fdb nexthop" diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index ea4cb6c1bd5e..178d98d91fea 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -381,6 +381,9 @@ static int sock_connect_mptcp(const char * const remoteaddr, hints.ai_family = pf; + /* Keep the resolved address alive for the whole execution: it is + * used again when reconnecting, and will be released at exit time. + */ xgetaddrinfo(remoteaddr, port, &hints, &addr); for (a = addr; a; a = a->ai_next) { sock = socket(a->ai_family, a->ai_socktype, proto); @@ -421,7 +424,6 @@ static int sock_connect_mptcp(const char * const remoteaddr, sock = -1; } - freeaddrinfo(addr); if (sock != -1) SOCK_TEST_TCPULP(sock, proto); return sock; diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 5ef6033775c8..b9d14647f401 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -108,12 +108,14 @@ mptcp_lib_pr_info() { mptcp_lib_pr_nstat() { local ns="${1}" - local hist="/tmp/${ns}.out" + local cache="/tmp/${ns}.out" + local hist="/tmp/${ns}.nstat" - if [ -f "${hist}" ]; then - awk '$2 != 0 { print " "$0 }' "${hist}" + if [ -f "${cache}" ]; then + awk '$2 != 0 { print " "$0 }' "${cache}" else - ip netns exec "${ns}" nstat -as | grep Tcp + NSTAT_HISTORY="${hist}" ip netns exec "${ns}" nstat -s | + grep Tcp fi } @@ -414,19 +416,21 @@ mptcp_lib_nstat_get() { } # $1: ns, $2: MIB counter -# Get the counter from the history (mptcp_lib_nstat_{init,get}()) if available. -# If not, get the counter from nstat ignoring any history. +# Get the counter from the cache (mptcp_lib_nstat_{init,get}()) if available. +# If not, get the counter from nstat ignoring any cache, but using the history. mptcp_lib_get_counter() { local ns="${1}" local counter="${2}" - local hist="/tmp/${ns}.out" + local cache="/tmp/${ns}.out" + local hist="/tmp/${ns}.nstat" local count - if [[ -s "${hist}" && "${counter}" == *"Tcp"* ]]; then - count=$(awk "/^${counter} / {print \$2; exit}" "${hist}") + if [[ -s "${cache}" && "${counter}" == *"Tcp"* ]]; then + count=$(awk "/^${counter} / {print \$2; exit}" "${cache}") else - count=$(ip netns exec "${ns}" nstat -asz "${counter}" | - awk 'NR==1 {next} {print $2}') + count=$(NSTAT_HISTORY="${hist}" ip netns exec "${ns}" \ + nstat -sz "${counter}" | + awk 'NR==1 {next} {print $2}') fi if [ -z "${count}" ]; then mptcp_lib_fail_if_expected_feature "${counter} counter" diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh index a3323c21f001..c7cd271714ef 100755 --- a/tools/testing/selftests/net/pmtu.sh +++ b/tools/testing/selftests/net/pmtu.sh @@ -1457,7 +1457,7 @@ test_pmtu_ipvX_over_bridged_vxlanY_or_geneveY_exception() { mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000)) run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1 - run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s $((${ll_mtu} + 500)) ${dst} || return 1 + run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1 # Check that exceptions were created pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})" diff --git a/tools/testing/selftests/net/test_vxlan_mdb.sh b/tools/testing/selftests/net/test_vxlan_mdb.sh index 58da5de99ac4..f9600aabd4a2 100755 --- a/tools/testing/selftests/net/test_vxlan_mdb.sh +++ b/tools/testing/selftests/net/test_vxlan_mdb.sh @@ -685,6 +685,9 @@ star_g_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010" log_test $? 255 "Invalid source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010" + log_test $? 255 "All-zeros source in source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010" log_test $? 255 "Source list without filter mode" } @@ -784,6 +787,9 @@ sg_common() run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010" log_test $? 255 "(S, G) with an invalid source list" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010" + log_test $? 255 "(S, G) with an all-zeros source" + run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010" log_test $? 255 "All-zeros group with source" } diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c index ba2681a12cc7..9be5945f3b1f 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c @@ -8,6 +8,7 @@ #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> +#include <time.h> #include "ebb.h" @@ -22,6 +23,7 @@ static int test_body(void) { int i, orig_period, max_period; struct event event; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1 }; SKIP_IF(!ebb_is_supported()); @@ -57,10 +59,15 @@ static int test_body(void) * kernel to decide our timeslice is up and context switch to * the other thread. When we come back our EBB will have been * lost and we'll spin in this while loop forever. + * + * Use nanosleep(0) instead of sched_yield() to guarantee a + * context switch to the eat_cpu child regardless of the + * eligibility state. sched_yield() via yield_task_fair() may + * become a no-op when the task is ineligible (vruntime ahead + * of avg_vruntime), preventing the required context switch. */ - for (i = 0; i < 100000; i++) - sched_yield(); + nanosleep(&ts, NULL); /* Change the sample period slightly to try and hit the race */ if (sample_period >= (orig_period + 200)) diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c index 0a4bc479ae39..5dc0f12f467d 100644 --- a/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c +++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-force-tm.c @@ -60,7 +60,7 @@ void usr_signal_handler(int signo, siginfo_t *si, void *uc) ucp->uc_link = mmap(NULL, sizeof(ucontext_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); - if (ucp->uc_link == (void *)-1) { + if (ucp->uc_link == MAP_FAILED) { perror("Mmap failed"); exit(-1); } @@ -129,7 +129,7 @@ void tm_trap_test(void) ss.ss_size = SIGSTKSZ; ss.ss_flags = 0; - if (ss.ss_sp == (void *)-1) { + if (ss.ss_sp == MAP_FAILED) { perror("mmap error\n"); exit(-1); } diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index c03c6e778876..6024ce4ba6ff 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -105,8 +105,12 @@ static inline bool failure_is_nesting(void) static inline int tcheck(void) { long cr; - asm volatile ("tcheck 0" : "=r"(cr) : : "cr0"); - return (cr >> 28) & 4; + asm volatile("tcheck 0;" + "mfcr %0;" + : "=r"(cr) + : + : "cr0"); + return (cr >> 28) & 0xf; } static inline bool tcheck_doomed(void) diff --git a/tools/testing/selftests/riscv/cfi/cfi_rv_test.h b/tools/testing/selftests/riscv/cfi/cfi_rv_test.h index 1c8043f2b778..184df6903d01 100644 --- a/tools/testing/selftests/riscv/cfi/cfi_rv_test.h +++ b/tools/testing/selftests/riscv/cfi/cfi_rv_test.h @@ -56,7 +56,7 @@ #define CSR_SSP 0x011 -#ifdef __ASSEMBLY__ +#ifdef __ASSEMBLER__ #define __ASM_STR(x) x #else #define __ASM_STR(x) #x diff --git a/tools/testing/selftests/riscv/hwprobe/hwprobe.c b/tools/testing/selftests/riscv/hwprobe/hwprobe.c index 54c435af9923..eca4441ee77f 100644 --- a/tools/testing/selftests/riscv/hwprobe/hwprobe.c +++ b/tools/testing/selftests/riscv/hwprobe/hwprobe.c @@ -9,7 +9,7 @@ int main(int argc, char **argv) long out; ksft_print_header(); - ksft_set_plan(5); + ksft_set_plan(6); /* Fake the CPU_SET ops. */ cpus = -1; @@ -62,5 +62,23 @@ int main(int argc, char **argv) pairs[1].key == 1 && pairs[1].value != 0xAAAA, "Unknown key overwritten with -1 and doesn't block other elements\n"); + pairs[0].key = RISCV_HWPROBE_KEY_IMA_EXT_0; + out = riscv_hwprobe(pairs, 1, 0, 0, 0); + if (out != 0) + ksft_exit_fail_msg("hwprobe(IMA_EXT_0) failed with %ld\n", out); + + /* + * The RISC-V ISA manual specifies that Zfh implies Zfhmin and Zvfh + * implies Zvfhmin, so hwprobe must report the implied subset + * extensions whenever the supersets are present. + */ + if ((pairs[0].value & RISCV_HWPROBE_EXT_ZFH) && + !(pairs[0].value & RISCV_HWPROBE_EXT_ZFHMIN)) + ksft_exit_fail_msg("Zfh reported without implied Zfhmin\n"); + if ((pairs[0].value & RISCV_HWPROBE_EXT_ZVFH) && + !(pairs[0].value & RISCV_HWPROBE_EXT_ZVFHMIN)) + ksft_exit_fail_msg("Zvfh reported without implied Zvfhmin\n"); + ksft_test_result_pass("Zfh/Zvfh imply Zfhmin/Zvfhmin\n"); + ksft_finished(); } diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json new file mode 100644 index 000000000000..e92a4180db68 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gact-rollback.json @@ -0,0 +1,78 @@ +[ + { + "id": "e3b1", + "name": "Failed action batch releases a bound action reference", + "category": [ + "actions", + "gact" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC actions add action pass index 1", + "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1" + ], + "cmdUnderTest": "$TC actions replace action pass index 1 action goto chain 42 index 3", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "total acts 1.*index 1 ref 2 bind 1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "e3b2", + "name": "Failed action batch releases entries after a bound action", + "category": [ + "actions", + "gact" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC actions add action pass index 1", + "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1", + [ + "$TC actions replace action pass index 1 action pass index 2 action goto chain 42 index 3", + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pass index 2", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "total acts 2.*index 1 ref 2 bind 1.*index 2 ref 1 bind 0", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "e3b3", + "name": "Failed action batch releases repeated references to a bound action", + "category": [ + "actions", + "gact" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC actions add action pass index 1", + "$TC filter add dev $DEV1 protocol all ingress prio 1 matchall action gact index 1" + ], + "cmdUnderTest": "$TC actions replace action pass index 1 action pass index 1 action goto chain 42 index 3", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "total acts 1.*index 1 ref 2 bind 1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json index 05cedca67cca..2d5843aebd72 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json @@ -202,5 +202,215 @@ "teardown": [ "$TC qdisc del dev $DEV1 parent root drr" ] + }, + { + "id": "a7d2", + "name": "Delete a route filter that was moved to another bucket", + "category": [ + "filter", + "route" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 1 to 1 classid 1:1", + "$TC filter change dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10001 route from 1 to 2 classid 1:1", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 200 route from 5 to 5 classid 1:5" + ], + "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10002 route from 1 to 2", + "expExitCode": "0", + "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:", + "matchJSON": [ + { + "protocol": "ip", + "pref": 200, + "kind": "route", + "chain": 0 + }, + { + "protocol": "ip", + "pref": 200, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x50005", + "flowid": "1:5" + } + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "c05a", + "name": "Try to change a route filter onto an already used handle", + "category": [ + "filter", + "route" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 1 to 1 classid 1:1 action ok", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 route from 2 to 2 classid 1:2 action drop" + ], + "cmdUnderTest": "$TC filter change dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10001 route from 2 to 2 classid 1:1 action ok", + "expExitCode": "2", + "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:", + "matchJSON": [ + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0 + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x10001", + "flowid": "1:1", + "actions": [ + { + "order": 1, + "kind": "gact", + "control_action": { + "type": "pass" + } + } + ] + } + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x20002", + "flowid": "1:2", + "actions": [ + { + "order": 1, + "kind": "gact", + "control_action": { + "type": "drop" + } + } + ] + } + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "3f21", + "name": "Replace a route filter that shares its key with another filter", + "category": [ + "filter", + "route" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:1", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:2" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:9", + "expExitCode": "0", + "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:", + "matchJSON": [ + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0 + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x10101", + "flowid": "1:9" + } + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x10201", + "flowid": "1:2" + } + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + }, + { + "id": "9d0e", + "name": "Replace both route filters sharing a key", + "category": [ + "filter", + "route" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 ingress", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:1", + "$TC filter add dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:2", + "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10101 route from 1 to 1 classid 1:9" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: protocol ip prio 100 handle 0x10201 route from 1 to 1 classid 1:8", + "expExitCode": "0", + "verifyCmd": "$TC -j filter show dev $DEV1 parent ffff:", + "matchJSON": [ + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0 + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x10101", + "flowid": "1:9" + } + }, + { + "protocol": "ip", + "pref": 100, + "kind": "route", + "chain": 0, + "options": { + "fh": "0x10201", + "flowid": "1:8" + } + } + ], + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json index b2ca9d4e991b..e2b03f2b5e89 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json @@ -353,5 +353,28 @@ "teardown": [ "$TC qdisc del dev $DEV1 parent root drr" ] + }, + { + "id": "70fd", + "name": "Add u32 filter when node ID pool is exhausted (4096th filter rejected)", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY clsact", + "bash -c 'for i in {1..4095}; do echo filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0; done | $TC -b -'" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0", + "expExitCode": "2", + "verifyCmd": "$TC -d filter show dev $DUMMY ingress", + "matchPattern": "fh 800::", + "matchCount": "4095", + "teardown": [ + "$TC qdisc del dev $DUMMY clsact" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json index ee09e6d6fdf3..d2eab61c099a 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json @@ -987,7 +987,7 @@ }, { "id": "41f5", - "name": "ETS offload where the sum of quanta wraps u32", + "name": "ETS offload with out-of-range quanta clamped", "category": [ "qdisc", "ets" @@ -1002,7 +1002,7 @@ "cmdUnderTest": "$TC qdisc add dev $ETH root ets quanta 4294967294 1 1", "expExitCode": "0", "verifyCmd": "$TC qdisc show dev $ETH", - "matchPattern": "qdisc ets .*bands 3 quanta 4294967294 1 1", + "matchPattern": "qdisc ets .*bands 3 quanta 1048576 256 256", "matchCount": "1", "teardown": [ "echo \"1\" > /sys/bus/netdevsim/del_device" diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json index 30da27fe8806..a6e25e76ecb1 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json @@ -105,5 +105,209 @@ "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root" ] + }, + { + "id": "dbe3", + "name": "Set tx_queue_len to S16_MAX boundary (32767 accepted)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 32767", + "expExitCode": "0", + "verifyCmd": "$IP link show dev $DUMMY", + "matchPattern": "qlen 32767$", + "matchCount": "1", + "teardown": [] + }, + { + "id": "b50e", + "name": "Reject tx_queue_len above S16_MAX at set time (32768)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "$IP link set dev $DUMMY txqueuelen 32768", + "expExitCode": "2", + "verifyCmd": "$IP link show dev $DUMMY", + "matchPattern": "qlen 1000$", + "matchCount": "1", + "teardown": [] + }, + { + "id": "40f8", + "name": "Reject tx_queue_len above S16_MAX via sysfs (32768)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "sh -c 'echo 32768 > /sys/class/net/$DUMMY/tx_queue_len'", + "expExitCode": "1", + "verifyCmd": "$IP link show dev $DUMMY", + "matchPattern": "qlen 1000$", + "matchCount": "1", + "teardown": [] + }, + { + "id": "4b6e", + "name": "Set tx_queue_len to S16_MAX via sysfs (32767 accepted)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [], + "cmdUnderTest": "sh -c 'echo 32767 > /sys/class/net/$DUMMY/tx_queue_len'", + "expExitCode": "0", + "verifyCmd": "$IP link show dev $DUMMY", + "matchPattern": "qlen 32767$", + "matchCount": "1", + "teardown": [] + }, + { + "id": "b90d", + "name": "Create device with tx_queue_len at S16_MAX boundary (32767 accepted)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$IP link del dev $DUMMY", + 0, + 1 + ] + ], + "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 32767 type dummy", + "expExitCode": "0", + "verifyCmd": "$IP link show dev $DUMMY", + "matchPattern": "qlen 32767$", + "matchCount": "1", + "teardown": [ + [ + "$IP link del dev $DUMMY", + 0, + 1 + ] + ] + }, + { + "id": "57ab", + "name": "Reject creating device with tx_queue_len above S16_MAX (32768)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$IP link del dev $DUMMY", + 0, + 1 + ] + ], + "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 32768 type dummy", + "expExitCode": "2", + "verifyCmd": "$IP -o link show", + "matchPattern": "^[0-9]+: $DUMMY", + "matchCount": "0", + "teardown": [] + }, + { + "id": "e777", + "name": "Reject creating device with oversized tx_queue_len (500000)", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$IP link del dev $DUMMY", + 0, + 1 + ] + ], + "cmdUnderTest": "$IP link add dev $DUMMY txqueuelen 500000 type dummy", + "expExitCode": "2", + "verifyCmd": "$IP -o link show", + "matchPattern": "^[0-9]+: $DUMMY", + "matchCount": "0", + "teardown": [] + }, + { + "id": "31ac", + "name": "Reject veth peer nest tx_queue_len above S16_MAX at create", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$IP link del dev $DEV1", + 0, + 1 + ] + ], + "cmdUnderTest": "$IP link add dev $DEV1 type veth peer name $DEV0 txqueuelen 500000", + "expExitCode": "2", + "verifyCmd": "$IP -o link show", + "matchPattern": "^[0-9]+: $DEV1", + "matchCount": "0", + "teardown": [] + }, + { + "id": "b567", + "name": "Accept veth peer nest tx_queue_len within S16_MAX", + "category": [ + "qdisc", + "pfifo_fast" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + [ + "$IP link del dev $DEV1", + 0, + 1 + ] + ], + "cmdUnderTest": "$IP link add dev $DEV1 txqueuelen 100 type veth peer name $DEV0 txqueuelen 200", + "expExitCode": "0", + "verifyCmd": "$IP link show", + "matchPattern": "qlen (100|200)$", + "matchCount": "2", + "teardown": [ + [ + "$IP link del dev $DEV0", + 0, + 1 + ] + ] } ] diff --git a/tools/testing/selftests/ublk/Makefile b/tools/testing/selftests/ublk/Makefile index 5daf36c6c36c..37883e9d50ec 100644 --- a/tools/testing/selftests/ublk/Makefile +++ b/tools/testing/selftests/ublk/Makefile @@ -73,6 +73,8 @@ TEST_PROGS += test_stress_08.sh TEST_PROGS += test_stress_09.sh TEST_FILES := settings +TEST_FILES += test_common.sh +TEST_FILES += trace TEST_GEN_PROGS_EXTENDED = kublk metadata_size STANDALONE_UTILS := metadata_size.c diff --git a/tools/testing/selftests/ublk/test_recover_03.sh b/tools/testing/selftests/ublk/test_recover_03.sh index 2554805e5b02..92f4012178f0 100755 --- a/tools/testing/selftests/ublk/test_recover_03.sh +++ b/tools/testing/selftests/ublk/test_recover_03.sh @@ -29,6 +29,11 @@ _create_backfile 0 256M _create_backfile 1 128M _create_backfile 2 128M +ublk_run_quiesce_recover -t null -q 2 -r 1 -b & +ublk_run_quiesce_recover -t loop -q 2 -r 1 -b "${UBLK_BACKFILES[0]}" & +ublk_run_quiesce_recover -t stripe -q 2 -r 1 -b "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & +wait + ublk_run_quiesce_recover -t null -q 2 -r 1 & ublk_run_quiesce_recover -t loop -q 2 -r 1 "${UBLK_BACKFILES[0]}" & ublk_run_quiesce_recover -t stripe -q 2 -r 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py index 31afc24ef17b..9313ebe0c525 100644 --- a/tools/workqueue/wq_dump.py +++ b/tools/workqueue/wq_dump.py @@ -78,6 +78,12 @@ def cpumask_str(cpumask): wq_type_len = 9 +def wq_attrs(wq): + try: + return wq.attrs + except AttributeError: + return wq.unbound_attrs + def wq_type_str(wq): if wq.flags & WQ_BH: return f'{"bh":{wq_type_len}}' @@ -85,7 +91,7 @@ def wq_type_str(wq): if wq.flags & WQ_ORDERED: return f'{"ordered":{wq_type_len}}' else: - if wq.attrs.affn_strict: + if wq_attrs(wq).affn_strict: return f'{"unbound,S":{wq_type_len}}' else: return f'{"unbound":{wq_type_len}}' @@ -206,7 +212,7 @@ for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_( print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='') if wq.flags & WQ_UNBOUND: - print(f' {cpumask_str(wq.attrs.cpumask):{ucpus_len}}', end='') + print(f' {cpumask_str(wq_attrs(wq).cpumask):{ucpus_len}}', end='') else: print(f' {"":{ucpus_len}}', end='') |
