diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-27 13:11:13 +0000 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-27 13:11:13 +0000 |
| commit | 5b3bb1004643efe9bfa0d9fc08be1508e304a6bb (patch) | |
| tree | c25042695b484e2d32e7e4f23753632c6474fc93 | |
| parent | 3ac1c79c17f48b111bd04563d2c32755db2bfce5 (diff) | |
| parent | b6232ca8d194c60ee603586e42b5ea584d4b7be9 (diff) | |
| download | linux-next-5b3bb1004643efe9bfa0d9fc08be1508e304a6bb.tar.gz linux-next-5b3bb1004643efe9bfa0d9fc08be1508e304a6bb.zip | |
Merge branch 'mm-nonmm-stable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
# Conflicts:
# arch/s390/kernel/traps.c
102 files changed, 2174 insertions, 669 deletions
@@ -68,6 +68,7 @@ Alex Hung <alexhung@gmail.com> <alex.hung@canonical.com> Alex Shi <alexs@kernel.org> <alex.shi@intel.com> Alex Shi <alexs@kernel.org> <alex.shi@linaro.org> Alex Shi <alexs@kernel.org> <alex.shi@linux.alibaba.com> +Ali Ahmet Memiş <aliamemis@disroot.org> <ali@iusegentoo.com> Alice Mikityanska <alice.kernel@fastmail.im> <maxtram95@gmail.com> Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@mellanox.com> Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@nvidia.com> @@ -88,10 +89,15 @@ Andrej Shadura <andrew@shadura.me> <andrew@beldisplaytech.com> Andrew Donnellan <andrew+kernel@donnellan.id.au> <andrew@donnellan.id.au> Andrew Donnellan <andrew+kernel@donnellan.id.au> <ajd@linux.ibm.com> Andrew Donnellan <andrew+kernel@donnellan.id.au> <andrew.donnellan@au1.ibm.com> +Andrew Jones <andrew.jones@linux.dev> <ajones@ventanamicro.com> +Andrew Jones <andrew.jones@linux.dev> <andrew.jones@oss.qualcomm.com> +Andrew Jones <andrew.jones@linux.dev> <anjones@nvidia.com> +Andrew Jones <andrew.jones@linux.dev> <drjones@redhat.com> Andrew Morton <akpm@linux-foundation.org> Andrew Murray <amurray@thegoodpenguin.co.uk> <amurray@embedded-bits.co.uk> Andrew Murray <amurray@thegoodpenguin.co.uk> <andrew.murray@arm.com> Andrew Vasquez <andrew.vasquez@qlogic.com> +Andrey Golovko <andrey.golovko@gmail.com> <andrey@golovko.me> Andrey Konovalov <andreyknvl@gmail.com> <andreyknvl@google.com> Andrey Ryabinin <ryabinin.a.a@gmail.com> <a.ryabinin@samsung.com> Andrey Ryabinin <ryabinin.a.a@gmail.com> <aryabinin@virtuozzo.com> @@ -174,6 +180,7 @@ Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com> Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com> Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com> Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com> +Bradley Morgan <brads@mainlining.org> <include@grrlz.net> Brendan Higgins <brendan.higgins@linux.dev> <brendanhiggins@google.com> Brendan Jackman <brendan.jackman@linux.dev> <jackmanb@google.com> Brian Avery <b.avery@hp.com> @@ -599,6 +606,8 @@ Marek Lindner <marek.lindner@mailbox.org> <lindner_marek@yahoo.de> Marek Lindner <marek.lindner@mailbox.org> <mareklindner@neomailbox.ch> Mark Brown <broonie@sirena.org.uk> Mark Starovoytov <mstarovo@pm.me> <mstarovoitov@marvell.com> +Markus Mayer <markus.mayer@broadcom.com> <mmayer@broadcom.com> +Markus Mayer <markus.mayer@broadcom.com> <markus.mayer@linaro.org> Markus Schneider-Pargmann <msp@baylibre.com> <mpa@pengutronix.de> Mark Yao <markyao0591@gmail.com> <mark.yao@rock-chips.com> Martin K. Petersen <mkp@kernel.org> <martin.petersen@oracle.com> @@ -1,6 +1,7 @@ This is at least a partial credits-file of people that have - contributed to the Linux project. It is sorted by name and - formatted to allow easy grepping and beautification by + contributed to the Linux project. It is sorted by family name, + assuming the used locale is default, e.g. by setting LC_ALL=C, + and formatted to allow easy grepping and beautification by scripts. The fields are: name (N), email (E), web-address (W), PGP key ID and fingerprint (P), description (D), and snail-mail address (S). @@ -344,6 +345,14 @@ D: Hardware spinlock (hwspinlock) subsystem D: OMAP hwspinlock driver D: OMAP remoteproc driver +N: Muli Ben-Yehuda +E: mulix@mulix.org +E: muli@il.ibm.com +W: http://www.mulix.org +D: trident OSS sound driver, x86-64 dma-ops and Calgary IOMMU, +D: KVM and Xen bits and other misc. hackery. +S: Haifa, Israel + N: Krzysztof Benedyczak E: golbi@mat.uni.torun.pl W: http://www.mat.uni.torun.pl/~golbi @@ -362,14 +371,6 @@ S: 2322 37th Ave SW S: Seattle, Washington 98126-2010 S: USA -N: Muli Ben-Yehuda -E: mulix@mulix.org -E: muli@il.ibm.com -W: http://www.mulix.org -D: trident OSS sound driver, x86-64 dma-ops and Calgary IOMMU, -D: KVM and Xen bits and other misc. hackery. -S: Haifa, Israel - N: Johannes Berg E: johannes@sipsolutions.net W: https://johannes.sipsolutions.net/ @@ -393,14 +394,14 @@ S: 1549 Hiironen Rd. S: Brimson, MN 55602 S: USA -N: Arnd Bergmann -D: Maintainer of Cell Broadband Engine Architecture - N: Hennus Bergman P: 1024/77D50909 76 99 FD 31 91 E1 96 1C 90 BB 22 80 62 F6 BD 63 D: Author and maintainer of the QIC-02 tape driver S: The Netherlands +N: Arnd Bergmann +D: Maintainer of Cell Broadband Engine Architecture + N: Tomas Berndtsson E: tomas@nocrew.org W: http://tomas.nocrew.org/ @@ -492,10 +493,6 @@ D: Various fixes (mostly networking) S: Montreal, Quebec S: Canada -N: Zoltán Böszörményi -E: zboszor@mail.externet.hu -D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support - N: John Boyd E: boyd@cis.ohio-state.edu D: Co-author of wd7000 SCSI driver @@ -589,9 +586,6 @@ N: Zach Brown E: zab@zabbo.net D: maestro pci sound -N: Zefan Li -D: Contribution to control group stuff - N: David Brownell D: Kernel engineer, mentor, and friend. Maintained USB EHCI and D: gadget layers, SPI subsystem, GPIO subsystem, and more than a few @@ -632,6 +626,10 @@ S: Ravenhorst 58 S: 2317 AK Leiden S: The Netherlands +N: Zoltán Böszörményi +E: zboszor@mail.externet.hu +D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support + N: Michael Callahan E: callahan@maths.ox.ac.uk D: PPP for Linux @@ -703,6 +701,10 @@ S: Tamsui town, Taipei county, S: Taiwan 251 S: Republic of China +N: Landen Chao +E: Landen.Chao@mediatek.com +D: MT7531 Ethernet switch support + N: Michael Elizabeth Chastain E: mec@shout.net D: Configure, Menuconfig, xconfig @@ -715,10 +717,6 @@ D: Media subsystem (V4L/DVB) drivers and core D: EDAC drivers and EDAC 3.0 core rework S: Brazil -N: Landen Chao -E: Landen.Chao@mediatek.com -D: MT7531 Ethernet switch support - N: Raymond Chen E: raymondc@microsoft.com D: Author of Configure script @@ -1447,6 +1445,10 @@ D: Made support for modules, ramdisk, generic-serial, etc. optional. D: Transformed old user space bdflush into 1st kernel thread - kflushd. D: Many other patches, documentation files, mini kernels, utilities, ... +N: Andy Gospodarek +E: andy@greyhouse.net +D: Maintenance and contributions to the network interface bonding driver. + N: Masanori GOTO E: gotom@debian.or.jp D: Workbit NinjaSCSI-32Bi/UDE driver @@ -1459,10 +1461,6 @@ S: 8124 Constitution Apt. 7 S: Sterling Heights, Michigan 48313 S: USA -N: Andy Gospodarek -E: andy@greyhouse.net -D: Maintenance and contributions to the network interface bonding driver. - N: Vivek Goyal E: vgoyal@redhat.com D: KDUMP, KEXEC, and VIRTIO FILE SYSTEM @@ -1535,6 +1533,14 @@ S: 44 St. Joseph Street, Suite 506 S: Toronto, Ontario, M4Y 2W4 S: Canada +N: Nitin Gupta +E: ngupta@vflare.org +D: zsmalloc memory allocator and zram block device driver + +N: Justin Guyett +E: jguyett@andrew.cmu.edu +D: via-rhine net driver hacking + N: Richard Günther E: rguenth@tat.physik.uni-tuebingen.de W: http://www.tat.physik.uni-tuebingen.de/~rguenth @@ -1543,14 +1549,6 @@ D: binfmt_misc S: 72074 Tübingen S: Germany -N: Justin Guyett -E: jguyett@andrew.cmu.edu -D: via-rhine net driver hacking - -N: Nitin Gupta -E: ngupta@vflare.org -D: zsmalloc memory allocator and zram block device driver - N: Danny ter Haar E: dth@cistron.nl D: /proc/cpuinfo, reboot on panic , kernel pre-patch tester ;) @@ -1949,16 +1947,6 @@ E: sjenning@redhat.com D: Creation and maintenance of zswap D: Creation and maintenace of the zbud allocator -N: Jeremy Kerr -D: Maintainer of SPU File System - -N: Michael Kerrisk -E: mtk.manpages@gmail.com -W: https://man7.org/ -P: 4096R/3A35CE5E E522 595B 52ED A4E6 BFCC CB5E 8561 9911 3A35 CE5E -D: Maintainer of the Linux man-pages project -D: Linux man pages online, at <https://man7.org/linux/man-pages/> - N: Niels Kristian Bech Jensen E: nkbj1970@hotmail.com D: Miscellaneous kernel updates and fixes. @@ -2103,6 +2091,16 @@ S: Keplerstr. 6 S: 4050 Traun S: Austria +N: Jeremy Kerr +D: Maintainer of SPU File System + +N: Michael Kerrisk +E: mtk.manpages@gmail.com +W: https://man7.org/ +P: 4096R/3A35CE5E E522 595B 52ED A4E6 BFCC CB5E 8561 9911 3A35 CE5E +D: Maintainer of the Linux man-pages project +D: Linux man pages online, at <https://man7.org/linux/man-pages/> + N: Karl Keyte E: karl@koft.com D: Disk usage statistics and modifications to line printer driver @@ -2487,6 +2485,9 @@ D: and much more. He was the maintainer of MD from 2016 to 2018. Shaohua D: passed away late 2018, he will be greatly missed. W: https://www.spinics.net/lists/raid/msg61993.html +N: Zefan Li +D: Contribution to control group stuff + N: Stephan Linz E: linz@mazet.de E: Stephan.Linz@gmx.de @@ -2546,11 +2547,6 @@ D: misc. kernel hacking and debugging S: Cambridge, MA 02139 S: USA -N: Martin von Löwis -E: loewis@informatik.hu-berlin.de -D: script binary format -D: NTFS driver - N: H.J. Lu E: hjl@gnu.ai.mit.edu D: GCC + libraries hacker @@ -2579,6 +2575,11 @@ S: Puistokaari 1 E 18 S: 00200 Helsinki S: Finland +N: Martin von Löwis +E: loewis@informatik.hu-berlin.de +D: script binary format +D: NTFS driver + N: Daniel J. Maas E: dmaas@dcine.com W: https://www.maasdigital.com @@ -2629,10 +2630,6 @@ S: PO BOX 220, HFX. CENTRAL S: Halifax, Nova Scotia S: Canada B3J 3C8 -N: Kai Mäkisara -E: Kai.Makisara@kolumbus.fi -D: SCSI Tape Driver - N: Asit Mallick E: asit.k.mallick@intel.com D: Linux/IA-64 @@ -2916,13 +2913,6 @@ S: 12725 SW Millikan Way, Suite 400 S: Beaverton, Oregon 97005 S: USA -N: Eberhard Mönkeberg -E: emoenke@gwdg.de -D: CDROM driver "sbpcd" (Matsushita/Panasonic/Soundblaster) -S: Ruhstrathöhe 2 b. -S: D-37085 Göttingen -S: Germany - N: Thomas Molina E: tmolina@cablespeed.com D: bug fixes, documentation, minor hackery @@ -3009,6 +2999,17 @@ S: Dragonvagen 1 A 13 S: FIN-00330 Helsingfors S: Finland +N: Kai Mäkisara +E: Kai.Makisara@kolumbus.fi +D: SCSI Tape Driver + +N: Eberhard Mönkeberg +E: emoenke@gwdg.de +D: CDROM driver "sbpcd" (Matsushita/Panasonic/Soundblaster) +S: Ruhstrathöhe 2 b. +S: D-37085 Göttingen +S: Germany + N: Matija Nalis E: mnalis@jagor.srce.hr E: mnalis@voyager.hr @@ -3209,13 +3210,6 @@ S: RR #5, 497 Pole Line Road S: Thunder Bay, Ontario S: CANADA P7C 5M9 -N: Inaky Perez-Gonzalez -E: inaky.perez-gonzalez@intel.com -E: linux-wimax@intel.com -E: inakypg@yahoo.com -D: WiMAX stack -D: Intel Wireless WiMAX Connection 2400 driver - N: Yuri Per E: yuri@pts.mipt.ru D: Some smbfs fixes @@ -3223,6 +3217,13 @@ S: Demonstratsii 8-382 S: Tula 300000 S: Russia +N: Inaky Perez-Gonzalez +E: inaky.perez-gonzalez@intel.com +E: linux-wimax@intel.com +E: inakypg@yahoo.com +D: WiMAX stack +D: Intel Wireless WiMAX Connection 2400 driver + N: Thomas Petazzoni E: thomas.petazzoni@bootlin.com D: Driver for the Marvell Armada 370/XP network unit. @@ -3337,14 +3338,14 @@ N: Frederic Potter E: fpotter@cirpack.com D: Some PCI kernel support -N: Rui Prior -E: rprior@inescn.pt -D: ATM device driver for NICStAR based cards - N: Roopa Prabhu E: roopa@nvidia.com D: Bridge co-maintainer, vxlan and networking contributor +N: Rui Prior +E: rprior@inescn.pt +D: ATM device driver for NICStAR based cards + N: Stefan Probst E: sp@caldera.de D: The Linux Support Team Erlangen, 1993-97 @@ -4032,12 +4033,6 @@ S: C/ Federico Garcia Lorca 1 10-A S: Sevilla 41005 S: Spain -N: Björn Töpel -E: bjorn@kernel.org -D: AF_XDP -S: Gothenburg -S: Sweden - N: Linus Torvalds E: torvalds@linux-foundation.org D: Original kernel hacker @@ -4091,17 +4086,6 @@ D: Linux-Workshop Köln (aka LUG Cologne, Germany), Installfests S: Tacitusstr. 6 S: D-50968 Köln -N: Tsu-Sheng Tsao -E: tsusheng@scf.usc.edu -D: IGMP (Internet Group Management Protocol) version 2 -S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD -S: Taipei -S: Taiwan 112 -S: Republic of China -S: 24335 Delta Drive -S: Diamond Bar, California 91765 -S: USA - N: Theodore Ts'o E: tytso@mit.edu D: Random Linux hacker @@ -4118,6 +4102,17 @@ S: 1 Amherst Street S: Cambridge, Massachusetts 02139 S: USA +N: Tsu-Sheng Tsao +E: tsusheng@scf.usc.edu +D: IGMP (Internet Group Management Protocol) version 2 +S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD +S: Taipei +S: Taiwan 112 +S: Republic of China +S: 24335 Delta Drive +S: Diamond Bar, California 91765 +S: USA + N: Luben Tuikov E: Luben Tuikov <ltuikov89@gmail.com> D: Maintainer of the DRM GPU Scheduler @@ -4144,6 +4139,12 @@ S: 44 Campbell Park Crescent S: Edinburgh EH13 0HT S: United Kingdom +N: Björn Töpel +E: bjorn@kernel.org +D: AF_XDP +S: Gothenburg +S: Sweden + N: Thomas Uhl E: uhl@sun1.rz.fh-heilbronn.de D: Application programmer diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 2f99024f8592..608055d7ccc4 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -459,8 +459,9 @@ hung_task_warnings ================== The maximum number of warnings to report. During a check interval -if a hung task is detected, this value is decreased by 1. -When this value reaches 0, no more warnings will be reported. +if a hung task is detected, the internal warning budget is decreased by 1. +When this budget reaches 0, no more detailed warnings will be reported. The +warning budget is reset to the configured limit when no hung task is found. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -1: report an infinite number of warnings. diff --git a/MAINTAINERS b/MAINTAINERS index 5d3e328062eb..5d624f417913 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5413,7 +5413,7 @@ F: Documentation/devicetree/bindings/usb/brcm,bdc.yaml F: drivers/usb/gadget/udc/bdc/ BROADCOM BMIPS CPUFREQ DRIVER -M: Markus Mayer <mmayer@broadcom.com> +M: Markus Mayer <markus.mayer@broadcom.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> L: linux-pm@vger.kernel.org S: Maintained @@ -5688,7 +5688,7 @@ F: drivers/spi/spi-brcmstb-qspi.c F: drivers/spi/spi-iproc-qspi.c BROADCOM STB AVS CPUFREQ DRIVER -M: Markus Mayer <mmayer@broadcom.com> +M: Markus Mayer <markus.mayer@broadcom.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> L: linux-pm@vger.kernel.org S: Maintained @@ -5696,7 +5696,7 @@ F: Documentation/devicetree/bindings/cpufreq/brcm,stb-avs-cpu-freq.txt F: drivers/cpufreq/brcmstb* BROADCOM STB AVS TMON DRIVER -M: Markus Mayer <mmayer@broadcom.com> +M: Markus Mayer <markus.mayer@broadcom.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> L: linux-pm@vger.kernel.org S: Maintained @@ -5704,7 +5704,7 @@ F: Documentation/devicetree/bindings/thermal/brcm,avs-tmon.yaml F: drivers/thermal/broadcom/brcmstb* BROADCOM STB DPFE DRIVER -M: Markus Mayer <mmayer@broadcom.com> +M: Markus Mayer <markus.mayer@broadcom.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -9915,6 +9915,7 @@ M: Jeff Layton <jlayton@kernel.org> S: Maintained F: include/linux/errseq.h F: lib/errseq.c +F: lib/tests/errseq_kunit.c ES9039Q2M AUDIO CODEC DRIVER M: Karl Asseily <karl@asseily.com> @@ -11130,6 +11131,7 @@ M: Peter Oberparleiter <oberpar@linux.ibm.com> S: Maintained F: Documentation/dev-tools/gcov.rst F: kernel/gcov/ +F: scripts/Makefile.gcov GDB KERNEL DEBUGGING HELPER SCRIPTS M: Jan Kiszka <jan.kiszka@siemens.com> @@ -1184,27 +1184,6 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -# Use atomic counter updates to avoid concurrent-access crashes in GCOV. -# Only enable if -fprofile-update=prefer-atomic does not introduce new -# undefined symbols (e.g. libatomic calls that the kernel cannot link). -CFLAGS_GCOV += $(call try-run,\ - echo 'long long x; void f(void){x++;}' | \ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ - -ftest-coverage -x c - -c -o "$$TMP.base" && \ - echo 'long long x; void f(void){x++;}' | \ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ - -ftest-coverage -fprofile-update=prefer-atomic \ - -x c - -c -o "$$TMP" && \ - $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ - $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ - cmp -s "$$TMP.ubase" "$$TMP.utest",\ - -fprofile-update=prefer-atomic) -endif -export CFLAGS_GCOV - # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree CFLAGS_PREFIX_MAP := -fmacro-prefix-map=$(srcroot)/= @@ -1225,6 +1204,7 @@ include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan include-$(CONFIG_KCOV) += scripts/Makefile.kcov +include-$(CONFIG_GCOV_KERNEL) += scripts/Makefile.gcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase include-$(CONFIG_AUTOFDO_CLANG) += scripts/Makefile.autofdo diff --git a/arch/Kconfig b/arch/Kconfig index 50601c0301e8..1163736de1f9 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1230,13 +1230,9 @@ config ARCH_MMAP_RND_BITS_MIN config ARCH_MMAP_RND_BITS_MAX int -config ARCH_MMAP_RND_BITS_DEFAULT - int - config ARCH_MMAP_RND_BITS int "Number of bits to use for ASLR of mmap base address" if EXPERT range ARCH_MMAP_RND_BITS_MIN ARCH_MMAP_RND_BITS_MAX - default ARCH_MMAP_RND_BITS_DEFAULT if ARCH_MMAP_RND_BITS_DEFAULT default ARCH_MMAP_RND_BITS_MIN depends on HAVE_ARCH_MMAP_RND_BITS help @@ -1264,13 +1260,9 @@ config ARCH_MMAP_RND_COMPAT_BITS_MIN config ARCH_MMAP_RND_COMPAT_BITS_MAX int -config ARCH_MMAP_RND_COMPAT_BITS_DEFAULT - int - config ARCH_MMAP_RND_COMPAT_BITS int "Number of bits to use for ASLR of mmap base address for compatible applications" if EXPERT range ARCH_MMAP_RND_COMPAT_BITS_MIN ARCH_MMAP_RND_COMPAT_BITS_MAX - default ARCH_MMAP_RND_COMPAT_BITS_DEFAULT if ARCH_MMAP_RND_COMPAT_BITS_DEFAULT default ARCH_MMAP_RND_COMPAT_BITS_MIN depends on HAVE_ARCH_MMAP_RND_COMPAT_BITS help diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 6b083b9584eb..5152f77fa30d 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -26,6 +26,7 @@ #include <linux/entry-common.h> #include <linux/kmsan.h> #include <linux/bug.h> +#include <linux/panic.h> #include <asm/pgmcheck-defs.h> #include <asm/entry-percpu.h> #include <asm/asm-extable.h> @@ -34,6 +35,7 @@ #include <asm/vtime.h> #include <asm/fpu.h> #include <asm/fault.h> +#include <asm/processor.h> #include "entry.h" struct pgm_stat { @@ -290,6 +292,11 @@ static void monitor_event_exception(struct pt_regs *regs) } } +void arch_do_panic(void) +{ + disabled_wait(); +} + /* * Content of the faulting CPU's lowcore, copied by stack_invalid(). */ diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 4975867d9001..5f43cef80638 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -2,6 +2,8 @@ #include <asm/setup.h> #include <linux/sysctl.h> +#include <linux/panic.h> +#include <linux/printk.h> static const struct ctl_table sparc_sysctl_table[] = { { @@ -36,6 +38,13 @@ static const struct ctl_table sparc_sysctl_table[] = { #endif }; +void arch_do_panic(void) +{ + /* Make sure the user can actually press Stop-A (L1-A) */ + stop_a_enabled = 1; + pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" + "twice on console to return to the boot prom\n"); +} static int __init init_sparc_sysctls(void) { diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index 4ab72ef03ba1..941a0b96590b 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -216,7 +216,6 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd) ibd->fault->attr.interval = 1; ibd->fault->attr.require_end = ULONG_MAX; ibd->fault->attr.stacktrace_depth = 32; - ibd->fault->attr.dname = NULL; ibd->fault->attr.verbose = 0; ibd->fault->enable = false; ibd->fault->opcode = false; diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index ad82c2108a56..40d2953a9a7f 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -555,7 +555,7 @@ static void dma_req_free(struct kref *ref) refcount); struct mport_cdev_priv *priv = req->priv; - dma_unmap_sg(req->dmach->device->dev, + dma_unmap_sg(dmaengine_get_dma_device(req->dmach), req->sgt.sgl, req->sgt.nents, req->dir); sg_free_table(&req->sgt); if (req->page_list) { @@ -916,7 +916,7 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode, xfer->offset, xfer->length); } - nents = dma_map_sg(chan->device->dev, + nents = dma_map_sg(dmaengine_get_dma_device(chan), req->sgt.sgl, req->sgt.nents, dir); if (nents == 0) { rmcd_error("Failed to map SG list"); @@ -2167,11 +2167,12 @@ static void mport_mm_open(struct vm_area_struct *vma) static void mport_mm_close(struct vm_area_struct *vma) { struct rio_mport_mapping *map = vma->vm_private_data; + struct mport_dev *md = map->md; rmcd_debug(MMAP, "%pad", &map->phys_addr); - mutex_lock(&map->md->buf_mutex); + mutex_lock(&md->buf_mutex); kref_put(&map->ref, mport_release_mapping); - mutex_unlock(&map->md->buf_mutex); + mutex_unlock(&md->buf_mutex); } static const struct vm_operations_struct vm_ops = { diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 25fcc4992d8b..7f6d8460152f 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -733,7 +733,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to) return -EBADMSG; } - buf = kmalloc(len, GFP_KERNEL); + buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN); if (unlikely(!buf)) { mutex_unlock(&epdata->lock); return -ENOMEM; @@ -796,7 +796,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from) return -EBADMSG; } - buf = kmalloc(len, GFP_KERNEL); + buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN); if (unlikely(!buf)) { mutex_unlock(&epdata->lock); return -ENOMEM; diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 7b9eb4e4b6e7..18f486e55323 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -200,7 +200,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) } hex_byte_pack(panic_msg, nmistat); - nmi_panic(regs, panic_msg); + nmi_panic(regs, "%s", panic_msg); return NMI_HANDLED; } diff --git a/fs/fat/fat.h b/fs/fat/fat.h index dbbcfc90a9c2..76978a268410 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -392,9 +392,10 @@ extern void fat_ent_access_init(struct super_block *sb); extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry); extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent, - int new, int wait); + int new, int old, int wait); extern int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster); +int fat_alloc_chain(struct inode *inode, int *cluster, int nr_cluster); extern int fat_free_clusters(struct inode *inode, int cluster); extern int fat_count_free_clusters(struct super_block *sb); extern int fat_trim_fs(struct inode *inode, struct fstrim_range *range); @@ -440,7 +441,7 @@ static inline unsigned long fat_dir_hash(int logstart) { return hash_32(logstart, FAT_HASH_BITS); } -extern int fat_add_cluster(struct inode *inode); +extern int fat_add_clusters(struct inode *inode, int nr_cluster); /* fat/misc.c */ extern __printf(3, 4) __cold diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index f0801d99dd62..2d10a815a579 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -413,7 +413,7 @@ error: } int fat_ent_write(struct inode *inode, struct fat_entry *fatent, - int new, int wait) + int new, int old, int wait) { struct super_block *sb = inode->i_sb; const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; @@ -422,10 +422,25 @@ int fat_ent_write(struct inode *inode, struct fat_entry *fatent, ops->ent_put(fatent, new); if (wait) { err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs); - if (err) + if (err) { + /* + * bhs are not uptodate after I/O error. So we + * can't simply re-dirty to revert. And it + * would not have value to write again on I/O + * error. + */ return err; + } } - return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs); + + err = fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs); + if (err) { + /* Try to revert if got the error on mirror FAT */ + ops->ent_put(fatent, old); + if (wait) + fat_sync_bhs(fatent->bhs, fatent->nr_bhs); + } + return err; } static inline int fat_ent_next(struct msdos_sb_info *sbi, @@ -468,7 +483,28 @@ static void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs, } } -int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) +static int fat_flush_bhs(struct super_block *sb, struct buffer_head **bhs, + int *nr_bhs, bool sync) +{ + int err, i; + + if (sync) { + err = fat_sync_bhs(bhs, *nr_bhs); + if (err) + return err; + } + err = fat_mirror_bhs(sb, bhs, *nr_bhs); + if (err) + return err; + for (i = 0; i < *nr_bhs; i++) + brelse(bhs[i]); + *nr_bhs = 0; + + return 0; +} + +static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster, + bool return_all) { struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); @@ -477,8 +513,6 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) struct buffer_head *bhs[MAX_BUF_PER_PAGE]; int i, count, err, nr_bhs, idx_clus; - BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */ - lock_fat(sbi); if (sbi->free_clusters != -1 && sbi->free_clus_valid && sbi->free_clusters < nr_cluster) { @@ -509,14 +543,24 @@ int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) if (prev_ent.nr_bhs) ops->ent_put(&prev_ent, entry); - fat_collect_bhs(bhs, &nr_bhs, &fatent); - sbi->prev_free = entry; if (sbi->free_clusters != -1) sbi->free_clusters--; - cluster[idx_clus] = entry; + if (return_all) + cluster[idx_clus] = entry; + else if (!idx_clus) + *cluster = entry; idx_clus++; + + if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) { + err = fat_flush_bhs(sb, bhs, &nr_bhs, + inode_needs_sync(inode)); + if (err) + goto out; + } + fat_collect_bhs(bhs, &nr_bhs, &fatent); + if (idx_clus == nr_cluster) goto out; @@ -556,6 +600,16 @@ out: return err; } +int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) +{ + return __fat_alloc_clusters(inode, cluster, nr_cluster, true); +} + +int fat_alloc_chain(struct inode *inode, int *cluster, int nr_cluster) +{ + return __fat_alloc_clusters(inode, cluster, nr_cluster, false); +} + int fat_free_clusters(struct inode *inode, int cluster) { struct super_block *sb = inode->i_sb; @@ -606,17 +660,10 @@ int fat_free_clusters(struct inode *inode, int cluster) } if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) { - if (sb->s_flags & SB_SYNCHRONOUS) { - err = fat_sync_bhs(bhs, nr_bhs); - if (err) - goto error; - } - err = fat_mirror_bhs(sb, bhs, nr_bhs); + err = fat_flush_bhs(sb, bhs, &nr_bhs, + sb->s_flags & SB_SYNCHRONOUS); if (err) goto error; - for (i = 0; i < nr_bhs; i++) - brelse(bhs[i]); - nr_bhs = 0; } fat_collect_bhs(bhs, &nr_bhs, &fatent); } while (cluster != FAT_ENT_EOF); diff --git a/fs/fat/file.c b/fs/fat/file.c index 2c6aee9f0305..d4f1b360b974 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -290,12 +290,10 @@ static long fat_fallocate(struct file *file, int mode, nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >> sbi->cluster_bits; - /* Start the allocation.We are not zeroing out the clusters */ - while (nr_cluster-- > 0) { - err = fat_add_cluster(inode); - if (err) - goto error; - } + /* Allocate the clusters. We are not zeroing them out */ + err = fat_add_clusters(inode, nr_cluster); + if (err) + goto error; } else { if ((offset + len) <= i_size_read(inode)) goto error; @@ -363,7 +361,8 @@ static int fat_free(struct inode *inode, int skip) __func__, MSDOS_I(inode)->i_pos); ret = -EIO; } else if (ret > 0) { - err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait); + err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, ret, + wait); if (err) ret = err; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index f775a004cae1..d85c8fbdcca4 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -20,6 +20,7 @@ #include <linux/blkdev.h> #include <linux/backing-dev.h> #include <linux/unaligned.h> +#include <linux/overflow.h> #include <linux/random.h> #include <linux/iversion.h> #include <linux/fs_struct.h> @@ -101,16 +102,16 @@ static struct fat_floppy_defaults { }, }; -int fat_add_cluster(struct inode *inode) +int fat_add_clusters(struct inode *inode, int nr_cluster) { int err, cluster; - err = fat_alloc_clusters(inode, &cluster, 1); + err = fat_alloc_chain(inode, &cluster, nr_cluster); if (err) return err; /* FIXME: this cluster should be added after data of this * cluster is writed */ - err = fat_chain_add(inode, cluster, 1); + err = fat_chain_add(inode, cluster, nr_cluster); if (err) fat_free_clusters(inode, cluster); return err; @@ -152,7 +153,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, */ if (!offset && !(iblock < last_block)) { /* TODO: multiple cluster allocation would be desirable. */ - err = fat_add_cluster(inode); + err = fat_add_clusters(inode, 1); if (err) return err; } @@ -1577,6 +1578,7 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, struct msdos_sb_info *sbi; u16 logical_sector_size; u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors; + u32 dir_start, data_start; long error; char buf[50]; struct timespec64 ts; @@ -1752,7 +1754,6 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry); sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1; - sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length; sbi->dir_entries = bpb.fat_dir_entries; if (sbi->dir_entries & (sbi->dir_per_block - 1)) { if (!silent) @@ -1763,20 +1764,30 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, rootdir_sectors = sbi->dir_entries * sizeof(struct msdos_dir_entry) / sb->s_blocksize; - sbi->data_start = sbi->dir_start + rootdir_sectors; + if (check_mul_overflow(sbi->fats, sbi->fat_length, &dir_start) || + check_add_overflow(sbi->fat_start, dir_start, &dir_start) || + check_add_overflow(dir_start, rootdir_sectors, &data_start)) { + if (!silent) + fat_msg(sb, KERN_ERR, + "overflow of root dir or data layout"); + goto out_invalid; + } + total_sectors = bpb.fat_sectors; if (total_sectors == 0) total_sectors = bpb.fat_total_sect; - if (total_sectors < sbi->data_start) { + if (total_sectors < data_start) { if (!silent) fat_msg(sb, KERN_ERR, - "data area starts beyond volume (%lu > %u)", - sbi->data_start, total_sectors); + "data area starts beyond volume (%u > %u)", + data_start, total_sectors); goto out_invalid; } - total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus; + sbi->dir_start = dir_start; + sbi->data_start = data_start; + total_clusters = (total_sectors - data_start) / sbi->sec_per_clus; if (!is_fat32(sbi)) sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 0d04228f916e..65d02f2edec3 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -62,6 +62,7 @@ void _fat_msg(struct super_block *sb, const char *level, const char *fmt, ...) _printk(FAT_PRINTK_PREFIX "%pV\n", level, sb->s_id, &vaf); va_end(args); } +EXPORT_SYMBOL_GPL(_fat_msg); /* Flushes the number of free clusters on FAT32 */ /* XXX: Need to write one per FSINFO block. Currently only writes 1 */ @@ -133,11 +134,9 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) ret = fat_ent_read(inode, &fatent, last); if (ret >= 0) { int wait = inode_needs_sync(inode); - int old = ret; - ret = fat_ent_write(inode, &fatent, new_dclus, wait); - if (ret < 0) - fat_ent_write(inode, &fatent, old, wait); + ret = fat_ent_write(inode, &fatent, new_dclus, ret, + wait); fatent_brelse(&fatent); } if (ret < 0) diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index dde4215616f9..c7500c2859e0 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -292,11 +292,17 @@ static int msdos_create(const struct mnt_idmap *idmap, struct inode *dir, if (err) goto out; inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); - brelse(sinfo.bh); if (IS_ERR(inode)) { + int err2; + err = PTR_ERR(inode); + err2 = fat_remove_entries(dir, &sinfo); + if (err2) + fat_msg(sb, KERN_WARNING, + "failed to roll back new entry (%d)", err2); goto out; } + brelse(sinfo.bh); fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); /* timestamp is already written, so mark_inode_dirty() is unneeded. */ @@ -381,12 +387,20 @@ static struct dentry *msdos_mkdir(const struct mnt_idmap *idmap, struct inode *d inc_nlink(dir); inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); - brelse(sinfo.bh); if (IS_ERR(inode)) { + int err2; + err = PTR_ERR(inode); - /* the directory was completed, just return a error */ - goto out; + err2 = fat_remove_entries(dir, &sinfo); + if (err2) { + fat_msg(sb, KERN_WARNING, + "failed to roll back new entry (%d)", err2); + goto out; + } + drop_nlink(dir); + goto out_free; } + brelse(sinfo.bh); set_nlink(inode, 2); fat_truncate_time(inode, &ts, FAT_UPDATE_ATIME | FAT_UPDATE_CMTIME); /* timestamp is already written, so mark_inode_dirty() is unneeded. */ diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 3dc063ba0a73..ff7652bf0410 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -771,11 +771,17 @@ static int vfat_create(const struct mnt_idmap *idmap, struct inode *dir, inode_inc_iversion(dir); inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); - brelse(sinfo.bh); if (IS_ERR(inode)) { + int err2; + err = PTR_ERR(inode); + err2 = fat_remove_entries(dir, &sinfo); + if (err2) + fat_msg(sb, KERN_WARNING, + "failed to roll back new entry (%d)", err2); goto out; } + brelse(sinfo.bh); inode_inc_iversion(inode); d_instantiate(dentry, inode); @@ -870,12 +876,20 @@ static struct dentry *vfat_mkdir(const struct mnt_idmap *idmap, struct inode *di inc_nlink(dir); inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); - brelse(sinfo.bh); if (IS_ERR(inode)) { + int err2; + err = PTR_ERR(inode); - /* the directory was completed, just return a error */ - goto out; + err2 = fat_remove_entries(dir, &sinfo); + if (err2) { + fat_msg(sb, KERN_WARNING, + "failed to roll back new entry (%d)", err2); + goto out; + } + drop_nlink(dir); + goto out_free; } + brelse(sinfo.bh); inode_inc_iversion(inode); set_nlink(inode, 2); diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index be09e766ac1f..fc949e594626 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -925,6 +925,33 @@ static int ocfs2_validate_extent_block(struct super_block *sb, goto bail; } + /* + * Extent blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(eb->h_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + rc = ocfs2_error(sb, + "Extent block #%llu has an invalid h_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_suballoc_slot)); + goto bail; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(eb->h_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, + "Extent block #%llu has an invalid h_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_suballoc_bit)); + goto bail; + } + if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) { rc = ocfs2_error(sb, "Extent block #%llu has invalid l_count %u (expected %u)\n", @@ -1817,6 +1844,7 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci, int i, ret = 0; u32 range; u64 blkno; + u32 prev_depth = OCFS2_MAX_PATH_DEPTH; struct buffer_head *bh = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; @@ -1824,14 +1852,16 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci, el = root_el; while (el->l_tree_depth) { - if (unlikely(le16_to_cpu(el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH)) { + if (unlikely(le16_to_cpu(el->l_tree_depth) >= prev_depth)) { ocfs2_error(ocfs2_metadata_cache_get_super(ci), - "Owner %llu has invalid tree depth %u in extent list\n", + "Owner %llu has invalid tree depth %u in extent list (max %u)\n", (unsigned long long)ocfs2_metadata_cache_owner(ci), - le16_to_cpu(el->l_tree_depth)); + le16_to_cpu(el->l_tree_depth), prev_depth - 1); ret = -EROFS; goto out; } + prev_depth = le16_to_cpu(el->l_tree_depth); + if (!el->l_next_free_rec || !el->l_count) { ocfs2_error(ocfs2_metadata_cache_get_super(ci), "Owner %llu has empty extent list at depth %u\n" diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 1c3def99bb07..b45adc39aacc 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -880,8 +880,11 @@ static void o2hb_shutdown_slot(struct o2hb_disk_slot *slot) int queued = 0; node = o2nm_get_node_by_num(slot->ds_node_num); - if (!node) - return; + /* + * The node may have been removed from the node table while this + * region's slot is still linked. The list member must still be + * removed before the region releases its slot array. + */ spin_lock(&o2hb_live_lock); if (!list_empty(&slot->ds_live_item)) { @@ -903,7 +906,8 @@ static void o2hb_shutdown_slot(struct o2hb_disk_slot *slot) if (queued) o2hb_run_event_list(&event); - o2nm_node_put(node); + if (node) + o2nm_node_put(node); } static void o2hb_set_quorum_device(struct o2hb_region *reg) @@ -1038,7 +1042,7 @@ static int o2hb_check_slot(struct o2hb_region *reg, fire_callbacks: /* dead nodes only come to life after some number of * changes at any time during their dead time */ - if (list_empty(&slot->ds_live_item) && + if (node && list_empty(&slot->ds_live_item) && slot->ds_changed_samples >= O2HB_LIVE_THRESHOLD) { mlog(ML_HEARTBEAT, "Node %d (id 0x%llx) joined my region\n", slot->ds_node_num, (long long)slot->ds_last_generation); @@ -1443,6 +1447,7 @@ static ssize_t o2hb_debug_read(struct file *file, char __user *buf, #endif /* CONFIG_DEBUG_FS */ static const struct file_operations o2hb_debug_fops = { + .owner = THIS_MODULE, .open = o2hb_debug_open, .release = o2hb_debug_release, .read = o2hb_debug_read, @@ -1966,18 +1971,22 @@ static ssize_t o2hb_region_dev_store(struct config_item *item, atomic_set(®->hr_unsteady_iterations, (live_threshold * 3)); o2hb_set_region_stopping(reg, false); - hb_task = kthread_run(o2hb_thread, reg, "o2hb-%s", - reg->hr_item.ci_name); + hb_task = kthread_create(o2hb_thread, reg, "o2hb-%s", + reg->hr_item.ci_name); if (IS_ERR(hb_task)) { ret = PTR_ERR(hb_task); mlog_errno(ret); goto out; } + /* The thread may exit on its own, so pin it before it can run. */ + get_task_struct(hb_task); spin_lock(&o2hb_live_lock); reg->hr_task = hb_task; spin_unlock(&o2hb_live_lock); + wake_up_process(hb_task); + ret = wait_event_interruptible(o2hb_steady_queue, atomic_read(®->hr_steady_iterations) == 0 || reg->hr_node_deleted); @@ -2022,7 +2031,7 @@ out: spin_unlock(&o2hb_live_lock); if (hb_task) - kthread_stop(hb_task); + kthread_stop_put(hb_task); o2hb_unmap_slot_data(reg); @@ -2132,6 +2141,18 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g } reg->hr_node_num = O2NM_MAX_NODES; + INIT_LIST_HEAD(®->hr_all_item); + mutex_init(®->hr_arming_mutex); + reg->hr_stopping = true; + INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout); + INIT_DELAYED_WORK(®->hr_nego_timeout_work, o2hb_nego_timeout); + INIT_LIST_HEAD(®->hr_handler_list); + config_item_init_type_name(®->hr_item, name, &o2hb_region_type); + if (!config_item_name(®->hr_item)) { + ret = -ENOMEM; + goto put_item; + } + spin_lock(&o2hb_live_lock); reg->hr_region_num = 0; if (o2hb_global_heartbeat_active()) { @@ -2140,26 +2161,19 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g if (reg->hr_region_num >= O2NM_MAX_REGIONS) { spin_unlock(&o2hb_live_lock); ret = -EFBIG; - goto free; + goto put_item; } set_bit(reg->hr_region_num, o2hb_region_bitmap); } list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); - config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - /* this is the same way to generate msg key as dlm, for local heartbeat, * name is also the same, so make initial crc value different to avoid * message key conflict. */ reg->hr_key = crc32_le(reg->hr_region_num + O2NM_MAX_REGIONS, name, strlen(name)); - mutex_init(®->hr_arming_mutex); - reg->hr_stopping = true; - INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout); - INIT_DELAYED_WORK(®->hr_nego_timeout_work, o2hb_nego_timeout); - INIT_LIST_HEAD(®->hr_handler_list); ret = o2net_register_handler(O2HB_NEGO_TIMEOUT_MSG, reg->hr_key, sizeof(struct o2hb_nego_msg), o2hb_nego_timeout_handler, @@ -2182,10 +2196,13 @@ unregister_handler: o2net_unregister_and_flush_handler_list(®->hr_handler_list); remove_item: spin_lock(&o2hb_live_lock); - list_del(®->hr_all_item); + list_del_init(®->hr_all_item); if (o2hb_global_heartbeat_active()) clear_bit(reg->hr_region_num, o2hb_region_bitmap); spin_unlock(&o2hb_live_lock); +put_item: + config_item_put(®->hr_item); + return ERR_PTR(ret); free: kfree(reg); return ERR_PTR(ret); @@ -2208,7 +2225,7 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, spin_unlock(&o2hb_live_lock); if (hb_task) - kthread_stop(hb_task); + kthread_stop_put(hb_task); if (o2hb_global_heartbeat_active()) { spin_lock(&o2hb_live_lock); diff --git a/fs/ocfs2/cluster/netdebug.c b/fs/ocfs2/cluster/netdebug.c index f7ab66e70799..5d373002297d 100644 --- a/fs/ocfs2/cluster/netdebug.c +++ b/fs/ocfs2/cluster/netdebug.c @@ -183,6 +183,7 @@ static int nst_fop_release(struct inode *inode, struct file *file) } static const struct file_operations nst_seq_fops = { + .owner = THIS_MODULE, .open = nst_fop_open, .read = seq_read, .llseek = seq_lseek, @@ -415,6 +416,7 @@ static int stats_fop_open(struct inode *inode, struct file *file) } static const struct file_operations stats_seq_fops = { + .owner = THIS_MODULE, .open = stats_fop_open, .read = seq_read, .llseek = seq_lseek, @@ -427,6 +429,7 @@ static int sc_fop_open(struct inode *inode, struct file *file) } static const struct file_operations sc_seq_fops = { + .owner = THIS_MODULE, .open = sc_fop_open, .read = seq_read, .llseek = seq_lseek, @@ -476,6 +479,7 @@ static ssize_t o2net_debug_read(struct file *file, char __user *buf, } static const struct file_operations nodes_fops = { + .owner = THIS_MODULE, .open = nodes_fop_open, .release = o2net_debug_release, .read = o2net_debug_read, diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index e08850a5d736..91f8fcc6193f 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -12,6 +12,7 @@ #include "tcp.h" #include "nodemanager.h" #include "heartbeat.h" +#include "quorum.h" #include "masklog.h" #include "sys.h" @@ -626,8 +627,11 @@ static void o2nm_node_group_drop_item(struct config_group *group, write_lock(&cluster->cl_nodes_lock); - /* XXX sloppy */ - if (node->nd_ipv4_address) + /* + * 0.0.0.0 is a valid configured address, so nd_ipv4_address does + * not tell us whether nd_ip_node was published in the IP tree. + */ + if (test_bit(O2NM_NODE_ATTR_ADDRESS, &node->nd_set_attributes)) rb_erase(&node->nd_ip_node, &cluster->cl_node_ip_tree); /* nd_num might be 0 if the node number hasn't been set.. */ @@ -740,6 +744,8 @@ static void o2nm_cluster_group_drop_item(struct config_group *group, struct conf struct o2nm_cluster *cluster = to_o2nm_cluster(item); BUG_ON(o2nm_single_cluster != cluster); + /* Drain quorum work before it loses access to the cluster. */ + o2quo_flush_work(); o2nm_single_cluster = NULL; configfs_remove_default_groups(&cluster->cl_group); diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c index bfb8b456876c..ab5b3d4fd0e8 100644 --- a/fs/ocfs2/cluster/quorum.c +++ b/fs/ocfs2/cluster/quorum.c @@ -318,9 +318,14 @@ void o2quo_init(void) INIT_WORK(&qs->qs_work, o2quo_make_decision); } -void o2quo_exit(void) +void o2quo_flush_work(void) { struct o2quo_state *qs = &o2quo_state; flush_work(&qs->qs_work); } + +void o2quo_exit(void) +{ + o2quo_flush_work(); +} diff --git a/fs/ocfs2/cluster/quorum.h b/fs/ocfs2/cluster/quorum.h index d64bf4482a4a..1ce5fc2a8ca9 100644 --- a/fs/ocfs2/cluster/quorum.h +++ b/fs/ocfs2/cluster/quorum.h @@ -8,6 +8,7 @@ void o2quo_init(void); void o2quo_exit(void); +void o2quo_flush_work(void); void o2quo_hb_up(u8 node); void o2quo_hb_down(u8 node); diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 474fe1414cee..8b3830b601ba 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -301,6 +301,11 @@ static int o2net_prep_nsw(struct o2net_node *nn, struct o2net_status_wait *nsw) { int ret; + /* Initialize all completion-visible state before publishing the waiter. */ + init_waitqueue_head(&nsw->ns_wq); + nsw->ns_sys_status = O2NET_ERR_NONE; + nsw->ns_status = 0; + spin_lock(&nn->nn_lock); ret = idr_alloc(&nn->nn_status_idr, nsw, 0, 0, GFP_ATOMIC); if (ret >= 0) { @@ -311,9 +316,6 @@ static int o2net_prep_nsw(struct o2net_node *nn, struct o2net_status_wait *nsw) if (ret < 0) return ret; - init_waitqueue_head(&nsw->ns_wq); - nsw->ns_sys_status = O2NET_ERR_NONE; - nsw->ns_status = 0; return 0; } diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 0075e1624310..3f159a20a248 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -221,7 +221,8 @@ static void TEA_transform(__u32 buf[4], __u32 const in[]) buf[1] += b1; } -static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) +static void str2hashbuf(const char *msg, int len, __u32 *buf, int num, + bool legacy_signed) { __u32 pad, val; int i; @@ -235,7 +236,10 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) for (i = 0; i < len; i++) { if ((i % 4) == 0) val = pad; - val = msg[i] + (val << 8); + if (legacy_signed) + val = (signed char)msg[i] + (val << 8); + else + val = (unsigned char)msg[i] + (val << 8); if ((i % 4) == 3) { *buf++ = val; val = pad; @@ -248,8 +252,9 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num) *buf++ = pad; } -static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len, - struct ocfs2_dx_hinfo *hinfo) +static void __ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, + int len, struct ocfs2_dx_hinfo *hinfo, + bool legacy_signed) { struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); const char *p; @@ -279,7 +284,7 @@ static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len, p = name; while (len > 0) { - str2hashbuf(p, len, in, 4); + str2hashbuf(p, len, in, 4, legacy_signed); TEA_transform(buf, in); len -= 16; p += 16; @@ -290,6 +295,19 @@ out: hinfo->minor_hash = buf[1]; } +static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len, + struct ocfs2_dx_hinfo *hinfo) +{ + __ocfs2_dx_dir_name_hash(dir, name, len, hinfo, false); +} + +static void ocfs2_dx_dir_name_hash_signed(struct inode *dir, const char *name, + int len, + struct ocfs2_dx_hinfo *hinfo) +{ + __ocfs2_dx_dir_name_hash(dir, name, len, hinfo, true); +} + /* * bh passed here can be an inode block or a dir data block, depending * on the inode inline data flag. @@ -605,6 +623,49 @@ static int ocfs2_validate_dx_root(struct super_block *sb, goto bail; } + if (le64_to_cpu(dx_root->dr_blkno) != bh->b_blocknr) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has an invalid dr_blkno of %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(dx_root->dr_blkno)); + goto bail; + } + + if (le32_to_cpu(dx_root->dr_fs_generation) != OCFS2_SB(sb)->fs_generation) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has an invalid dr_fs_generation of #%u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(dx_root->dr_fs_generation)); + goto bail; + } + + /* + * Dir index root blocks are allocated from a per-slot suballocator, + * so the slot must be in range. Otherwise removing the index passes + * it to get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(dx_root->dr_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid dr_suballoc_slot %u\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dx_root->dr_suballoc_slot)); + goto bail; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise removing the index will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(dx_root->dr_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid dr_suballoc_bit %u\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dx_root->dr_suballoc_bit)); + goto bail; + } + if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { struct ocfs2_extent_list *el = &dx_root->dr_list; @@ -690,6 +751,18 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb, return ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", 7, dx_leaf->dl_signature); + if (le64_to_cpu(dx_leaf->dl_blkno) != bh->b_blocknr) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has an invalid dl_blkno of %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno)); + + if (le32_to_cpu(dx_leaf->dl_fs_generation) != OCFS2_SB(sb)->fs_generation) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has an invalid dl_fs_generation of #%u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(dx_leaf->dl_fs_generation)); + if (le16_to_cpu(dx_leaf->dl_list.de_count) != ocfs2_dx_entries_per_leaf(sb)) return ocfs2_error(sb, @@ -966,10 +1039,10 @@ out: return ret; } -static int ocfs2_dx_dir_search(const char *name, int namelen, - struct inode *dir, - struct ocfs2_dx_root_block *dx_root, - struct ocfs2_dir_lookup_result *res) +static int __ocfs2_dx_dir_search(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dx_root_block *dx_root, + struct ocfs2_dir_lookup_result *res) { int ret, i, found; u64 phys; @@ -982,8 +1055,6 @@ static int ocfs2_dx_dir_search(const char *name, int namelen, struct ocfs2_extent_list *dr_el; struct ocfs2_dx_entry_list *entry_list; - ocfs2_dx_dir_name_hash(dir, name, namelen, &res->dl_hinfo); - if (ocfs2_dx_root_inline(dx_root)) { entry_list = &dx_root->dr_entries; goto search; @@ -1080,6 +1151,44 @@ out: return ret; } +static int ocfs2_dx_dir_search(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dx_root_block *dx_root, + struct ocfs2_dir_lookup_result *res) +{ + struct ocfs2_dx_hinfo legacy; + int ret; + + ocfs2_dx_dir_name_hash(dir, name, namelen, &res->dl_hinfo); + + ret = __ocfs2_dx_dir_search(name, namelen, dir, dx_root, res); + if (ret != -ENOENT) + return ret; + + /* + * Nothing under the current hash. The entry may have been indexed by + * an older kernel, which sign-extended the name bytes when hashing. + * New entries are always indexed under the unsigned hash, so only fall + * back to the legacy signed one when it can actually differ: an ASCII + * name hashes the same either way, and a genuine miss on one should + * not have to walk the index twice. + */ + ocfs2_dx_dir_name_hash_signed(dir, name, namelen, &legacy); + if (legacy.major_hash == res->dl_hinfo.major_hash && + legacy.minor_hash == res->dl_hinfo.minor_hash) + return ret; + + res->dl_hinfo = legacy; + + ret = __ocfs2_dx_dir_search(name, namelen, dir, dx_root, res); + if (ret) + return ret; + + pr_warn_once("ocfs2: directory index with signed name hash\n"); + + return 0; +} + static int ocfs2_find_entry_dx(const char *name, int namelen, struct inode *dir, struct ocfs2_dir_lookup_result *lookup) diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c index 450d46eefab3..8a828448ba95 100644 --- a/fs/ocfs2/dlm/dlmconvert.c +++ b/fs/ocfs2/dlm/dlmconvert.c @@ -534,15 +534,19 @@ int dlm_convert_lock_handler(struct o2net_msg *msg, u32 len, void *data, } leave: - if (lock) - dlm_lock_put(lock); - /* either queue the ast or release it, if reserved */ if (call_ast) dlm_queue_ast(dlm, lock); else if (ast_reserved) dlm_lockres_release_ast(dlm, res); + /* + * Keep the lock alive through AST enqueue: a concurrent + * dlm_free_dead_locks() drops the list and base references. + */ + if (lock) + dlm_lock_put(lock); + if (kick_thread) dlm_kick_thread(dlm, res); diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index a91f167ed683..a064591801e2 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -7,6 +7,7 @@ * Copyright (C) 2004, 2008 Oracle. All rights reserved. */ +#include <linux/module.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/highmem.h> @@ -339,6 +340,7 @@ static int debug_purgelist_open(struct inode *inode, struct file *file) } static const struct file_operations debug_purgelist_fops = { + .owner = THIS_MODULE, .open = debug_purgelist_open, .release = debug_release, .read = debug_read, @@ -394,6 +396,7 @@ static int debug_mle_open(struct inode *inode, struct file *file) } static const struct file_operations debug_mle_fops = { + .owner = THIS_MODULE, .open = debug_mle_open, .release = debug_release, .read = debug_read, @@ -601,6 +604,7 @@ static int debug_lockres_release(struct inode *inode, struct file *file) } static const struct file_operations debug_lockres_fops = { + .owner = THIS_MODULE, .open = debug_lockres_open, .release = debug_lockres_release, .read = seq_read, @@ -794,6 +798,7 @@ static int debug_state_open(struct inode *inode, struct file *file) } static const struct file_operations debug_state_fops = { + .owner = THIS_MODULE, .open = debug_state_open, .release = debug_release, .read = debug_read, diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 97bb9400e24b..a7dc3da6dfc7 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -2215,7 +2215,8 @@ static LIST_HEAD(dlm_join_handlers); static void dlm_unregister_net_handlers(void) { - o2net_unregister_handler_list(&dlm_join_handlers); + if (!list_empty(&dlm_join_handlers)) + o2net_unregister_and_flush_handler_list(&dlm_join_handlers); } static int dlm_register_net_handlers(void) diff --git a/fs/ocfs2/dlm/dlmlock.c b/fs/ocfs2/dlm/dlmlock.c index 46120eb75d90..660237c0d7cd 100644 --- a/fs/ocfs2/dlm/dlmlock.c +++ b/fs/ocfs2/dlm/dlmlock.c @@ -138,6 +138,8 @@ static enum dlm_status dlmlock_master(struct dlm_ctxt *dlm, res->lockname.len)) { kick_thread = 1; call_ast = 1; + /* Keep the lock alive through AST enqueue. */ + dlm_lock_get(lock); } else { mlog(0, "%s: returning DLM_NORMAL to " "node %u for reco lock\n", dlm->name, @@ -166,10 +168,12 @@ static enum dlm_status dlmlock_master(struct dlm_ctxt *dlm, wake_up(&res->wq); /* either queue the ast or release it */ - if (call_ast) + if (call_ast) { dlm_queue_ast(dlm, lock); - else + dlm_lock_put(lock); + } else { dlm_lockres_release_ast(dlm, res); + } dlm_lockres_calc_usage(dlm, res); if (kick_thread) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index a23dd8f86c89..88d946466083 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -7,6 +7,7 @@ * Copyright (C) 2003, 2004 Oracle. All rights reserved. */ +#include <linux/module.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/highmem.h> @@ -18,6 +19,7 @@ #include <linux/time.h> #include <linux/delay.h> #include <linux/quotaops.h> +#include <linux/sched/mm.h> #include <linux/sched/signal.h> #include <linux/string_choices.h> @@ -2861,21 +2863,34 @@ void ocfs2_rename_unlock(struct ocfs2_super *osb) ocfs2_cluster_unlock(osb, lockres, DLM_LOCK_EX); } -int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex) +int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex, unsigned int *nofs_flag) { int status; + unsigned int flags; struct ocfs2_lock_res *lockres = &osb->osb_nfs_sync_lockres; if (ocfs2_is_hard_readonly(osb)) return -EROFS; + /* + * ocfs2_delete_inode() takes this lock from ->evict_inode(), which the + * dentry shrinker reaches while holding fs_reclaim. Anything allocated + * under the lock must therefore stay out of filesystem reclaim, or + * reclaim recurses back into the shrinker and tries to take this lock + * again. Cover the whole critical section, including the cluster lock + * and the sysfile inode locks the callers take below it. + */ + flags = memalloc_nofs_save(); + if (ex) down_write(&osb->nfs_sync_rwlock); else down_read(&osb->nfs_sync_rwlock); - if (ocfs2_mount_local(osb)) + if (ocfs2_mount_local(osb)) { + *nofs_flag = flags; return 0; + } status = ocfs2_cluster_lock(osb, lockres, ex ? LKM_EXMODE : LKM_PRMODE, 0, 0); @@ -2886,12 +2901,17 @@ int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex) up_write(&osb->nfs_sync_rwlock); else up_read(&osb->nfs_sync_rwlock); + memalloc_nofs_restore(flags); + return status; } - return status; + *nofs_flag = flags; + + return 0; } -void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex) +void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex, + unsigned int nofs_flag) { struct ocfs2_lock_res *lockres = &osb->osb_nfs_sync_lockres; @@ -2902,6 +2922,8 @@ void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex) up_write(&osb->nfs_sync_rwlock); else up_read(&osb->nfs_sync_rwlock); + + memalloc_nofs_restore(nofs_flag); } int ocfs2_trim_fs_lock(struct ocfs2_super *osb, @@ -3311,6 +3333,7 @@ static int ocfs2_dlm_debug_open(struct inode *inode, struct file *file) } static const struct file_operations ocfs2_dlm_debug_fops = { + .owner = THIS_MODULE, .open = ocfs2_dlm_debug_open, .release = ocfs2_dlm_debug_release, .read = seq_read, @@ -3525,6 +3548,10 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb, struct ocfs2_mask_waiter mw; unsigned long flags, flags2; + /* We didn't get anywhere near actually using this lockres. */ + if (!(lockres->l_flags & OCFS2_LOCK_INITIALIZED)) + return; + ocfs2_init_mask_waiter(&mw); spin_lock_irqsave(&lockres->l_lock, flags); diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h index a3ebd7303ea2..faa58ed8699b 100644 --- a/fs/ocfs2/dlmglue.h +++ b/fs/ocfs2/dlmglue.h @@ -161,8 +161,9 @@ void ocfs2_orphan_scan_unlock(struct ocfs2_super *osb, u32 seqno); int ocfs2_rename_lock(struct ocfs2_super *osb); void ocfs2_rename_unlock(struct ocfs2_super *osb); -int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex); -void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex); +int ocfs2_nfs_sync_lock(struct ocfs2_super *osb, int ex, unsigned int *nofs_flag); +void ocfs2_nfs_sync_unlock(struct ocfs2_super *osb, int ex, + unsigned int nofs_flag); void ocfs2_trim_fs_lock_res_init(struct ocfs2_super *osb); void ocfs2_trim_fs_lock_res_uninit(struct ocfs2_super *osb); int ocfs2_trim_fs_lock(struct ocfs2_super *osb, diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index 9c2665dd24e2..90b9e510e700 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -37,6 +37,7 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, struct inode *inode; struct ocfs2_super *osb = OCFS2_SB(sb); u64 blkno = handle->ih_blkno; + unsigned int nofs_flag = 0; int status, set; struct dentry *result; @@ -59,7 +60,7 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, * This will synchronize us against ocfs2_delete_inode() on * all nodes */ - status = ocfs2_nfs_sync_lock(osb, 1); + status = ocfs2_nfs_sync_lock(osb, 1, &nofs_flag); if (status < 0) { mlog(ML_ERROR, "getting nfs sync lock(EX) failed %d\n", status); goto check_err; @@ -90,7 +91,7 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, inode = ocfs2_iget(osb, blkno, 0, 0); unlock_nfs_sync: - ocfs2_nfs_sync_unlock(osb, 1); + ocfs2_nfs_sync_unlock(osb, 1, nofs_flag); check_err: if (status < 0) { @@ -133,12 +134,13 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) u64 blkno; struct dentry *parent; struct inode *dir = d_inode(child); + unsigned int nofs_flag = 0; int set; trace_ocfs2_get_parent(child, child->d_name.len, child->d_name.name, (unsigned long long)OCFS2_I(dir)->ip_blkno); - status = ocfs2_nfs_sync_lock(OCFS2_SB(dir->i_sb), 1); + status = ocfs2_nfs_sync_lock(OCFS2_SB(dir->i_sb), 1, &nofs_flag); if (status < 0) { mlog(ML_ERROR, "getting nfs sync lock(EX) failed %d\n", status); parent = ERR_PTR(status); @@ -183,7 +185,7 @@ bail_unlock: ocfs2_inode_unlock(dir, 0); unlock_nfs_sync: - ocfs2_nfs_sync_unlock(OCFS2_SB(dir->i_sb), 1); + ocfs2_nfs_sync_unlock(OCFS2_SB(dir->i_sb), 1, nofs_flag); bail: trace_ocfs2_get_parent_end(parent); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 62f45a1b5ca1..82c88fa8e98f 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -85,10 +85,17 @@ static int ocfs2_file_open(struct inode *inode, struct file *file) int mode = file->f_flags; struct ocfs2_inode_info *oi = OCFS2_I(inode); - trace_ocfs2_file_open(inode, file, file->f_path.dentry, - (unsigned long long)oi->ip_blkno, - file->f_path.dentry->d_name.len, - file->f_path.dentry->d_name.name, mode); + if (trace_ocfs2_file_open_enabled()) { + struct dentry *dentry = file->f_path.dentry; + struct name_snapshot name; + + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_file_open(inode, file, dentry, + oi->ip_blkno, + name.name.len, name.name.name, + mode); + release_dentry_name_snapshot(&name); + } if (file->f_mode & FMODE_WRITE) { status = dquot_initialize(inode); @@ -134,18 +141,26 @@ leave: static int ocfs2_file_release(struct inode *inode, struct file *file) { struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct dentry *dentry = file->f_path.dentry; + struct name_snapshot name; + bool trace_enabled = trace_ocfs2_file_release_enabled(); + + if (trace_enabled) + take_dentry_name_snapshot(&name, dentry); spin_lock(&oi->ip_lock); if (!--oi->ip_open_count) oi->ip_flags &= ~OCFS2_INODE_OPEN_DIRECT; - trace_ocfs2_file_release(inode, file, file->f_path.dentry, - oi->ip_blkno, - file->f_path.dentry->d_name.len, - file->f_path.dentry->d_name.name, - oi->ip_open_count); + if (trace_enabled) + trace_ocfs2_file_release(inode, file, dentry, + oi->ip_blkno, name.name.len, + name.name.name, oi->ip_open_count); spin_unlock(&oi->ip_lock); + if (trace_enabled) + release_dentry_name_snapshot(&name); + ocfs2_free_file_private(inode, file); return 0; @@ -174,11 +189,17 @@ static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end, tid_t commit_tid; bool needs_barrier = false; - trace_ocfs2_sync_file(inode, file, file->f_path.dentry, - oi->ip_blkno, - file->f_path.dentry->d_name.len, - file->f_path.dentry->d_name.name, - (unsigned long long)datasync); + if (trace_ocfs2_sync_file_enabled()) { + struct dentry *dentry = file->f_path.dentry; + struct name_snapshot name; + + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_sync_file(inode, file, dentry, + oi->ip_blkno, + name.name.len, name.name.name, + (unsigned long long)datasync); + release_dentry_name_snapshot(&name); + } if (unlikely(ocfs2_emergency_state(osb))) return -EROFS; @@ -1132,15 +1153,19 @@ int ocfs2_setattr(const struct mnt_idmap *idmap, struct dentry *dentry, int had_lock; struct ocfs2_lock_holder oh; - trace_ocfs2_setattr(inode, dentry, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - dentry->d_name.len, dentry->d_name.name, - attr->ia_valid, - attr->ia_valid & ATTR_MODE ? attr->ia_mode : 0, - attr->ia_valid & ATTR_UID ? + if (trace_ocfs2_setattr_enabled()) { + struct name_snapshot name; + + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_setattr(inode, dentry, OCFS2_I(inode)->ip_blkno, + name.name.len, name.name.name, attr->ia_valid, + attr->ia_valid & ATTR_MODE ? attr->ia_mode : 0, + attr->ia_valid & ATTR_UID ? from_kuid(&init_user_ns, attr->ia_uid) : 0, - attr->ia_valid & ATTR_GID ? + attr->ia_valid & ATTR_GID ? from_kgid(&init_user_ns, attr->ia_gid) : 0); + release_dentry_name_snapshot(&name); + } status = ocfs2_emergency_state(osb); if (unlikely(status)) { @@ -2392,11 +2417,17 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, int direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0; int nowait = iocb->ki_flags & IOCB_NOWAIT ? 1 : 0; - trace_ocfs2_file_write_iter(inode, file, file->f_path.dentry, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - file->f_path.dentry->d_name.len, - file->f_path.dentry->d_name.name, - (unsigned int)from->nr_segs); /* GRRRRR */ + if (trace_ocfs2_file_write_iter_enabled()) { + struct dentry *dentry = file->f_path.dentry; + struct name_snapshot name; + + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_file_write_iter(inode, file, dentry, + OCFS2_I(inode)->ip_blkno, + name.name.len, name.name.name, + (unsigned int)from->nr_segs); + release_dentry_name_snapshot(&name); + } if (!direct_io && nowait) return -EOPNOTSUPP; @@ -2542,17 +2573,16 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, int direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0; int nowait = iocb->ki_flags & IOCB_NOWAIT ? 1 : 0; - trace_ocfs2_file_read_iter(inode, filp, filp->f_path.dentry, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - filp->f_path.dentry->d_name.len, - filp->f_path.dentry->d_name.name, - to->nr_segs); /* GRRRRR */ + if (trace_ocfs2_file_read_iter_enabled()) { + struct dentry *dentry = filp->f_path.dentry; + struct name_snapshot name; - - if (!inode) { - ret = -EINVAL; - mlog_errno(ret); - goto bail; + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_file_read_iter(inode, filp, dentry, + OCFS2_I(inode)->ip_blkno, + name.name.len, name.name.name, + to->nr_segs); + release_dentry_name_snapshot(&name); } if (!direct_io && nowait) @@ -2624,11 +2654,17 @@ static ssize_t ocfs2_file_splice_read(struct file *in, loff_t *ppos, ssize_t ret = 0; int lock_level = 0; - trace_ocfs2_file_splice_read(inode, in, in->f_path.dentry, - (unsigned long long)OCFS2_I(inode)->ip_blkno, - in->f_path.dentry->d_name.len, - in->f_path.dentry->d_name.name, - flags); + if (trace_ocfs2_file_splice_read_enabled()) { + struct dentry *dentry = in->f_path.dentry; + struct name_snapshot name; + + take_dentry_name_snapshot(&name, dentry); + trace_ocfs2_file_splice_read(inode, in, dentry, + OCFS2_I(inode)->ip_blkno, + name.name.len, name.name.name, + flags); + release_dentry_name_snapshot(&name); + } /* * We're fine letting folks race truncates and extending writes with diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c index be713d7d24a7..75334e4bd082 100644 --- a/fs/ocfs2/filecheck.c +++ b/fs/ocfs2/filecheck.c @@ -179,17 +179,20 @@ int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb) fcheck->fc_size = 0; fcheck->fc_done = 0; + /* Publish the initialized owner before activating its sysfs callbacks. */ + entry->fs_fcheck = fcheck; entry->fs_kobj.kset = osb->osb_dev_kset; init_completion(&entry->fs_kobj_unregister); ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck, NULL, "filecheck"); if (ret) { + /* Mount failure still calls ocfs2_filecheck_remove_sysfs(). */ + entry->fs_fcheck = NULL; kobject_put(&entry->fs_kobj); kfree(fcheck); return ret; } - entry->fs_fcheck = fcheck; return 0; } diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 180107a11046..80c36c60a8ce 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -638,14 +638,18 @@ static int ocfs2_read_locked_inode(struct inode *inode, fe = (struct ocfs2_dinode *) bh->b_data; /* - * This is a code bug. Right now the caller needs to - * understand whether it is asking for a system file inode or - * not so the proper lock names can be built. + * The caller must know whether it is asking for a system file inode + * or not so the proper lock names can be built. Since i_flags comes + * from disk, a mismatch is filesystem corruption instead of a code + * bug, so handle it with ocfs2_error() rather than BUG_ON(). */ - mlog_bug_on_msg(!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) != - !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE), - "Inode %llu: system file state is ambiguous\n", - (unsigned long long)args->fi_blkno); + if (!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) != + !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE)) { + status = ocfs2_error(osb->sb, + "Inode %llu: system file state is ambiguous\n", + (unsigned long long)args->fi_blkno); + goto bail; + } if (S_ISCHR(le16_to_cpu(fe->i_mode)) || S_ISBLK(le16_to_cpu(fe->i_mode))) @@ -1105,6 +1109,7 @@ static void ocfs2_delete_inode(struct inode *inode) { int wipe, status; sigset_t oldset; + unsigned int nofs_flag = 0; struct buffer_head *di_bh = NULL; struct ocfs2_dinode *di = NULL; @@ -1139,7 +1144,7 @@ static void ocfs2_delete_inode(struct inode *inode) * shared mode so that all nodes can still concurrently * process deletes. */ - status = ocfs2_nfs_sync_lock(OCFS2_SB(inode->i_sb), 0); + status = ocfs2_nfs_sync_lock(OCFS2_SB(inode->i_sb), 0, &nofs_flag); if (status < 0) { mlog(ML_ERROR, "getting nfs sync lock(PR) failed %d\n", status); ocfs2_cleanup_delete_inode(inode, 0); @@ -1211,7 +1216,7 @@ bail_unlock_inode: brelse(di_bh); bail_unlock_nfs_sync: - ocfs2_nfs_sync_unlock(OCFS2_SB(inode->i_sb), 0); + ocfs2_nfs_sync_unlock(OCFS2_SB(inode->i_sb), 0, nofs_flag); bail_unblock: ocfs2_unblock_signals(&oldset); @@ -1520,14 +1525,45 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } - if (le16_to_cpu(di->i_suballoc_slot) != (u16)OCFS2_INVALID_SLOT && - (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) { + /* + * Only system inodes created by mkfs.ocfs2 are allocated from the + * global allocator and thus legitimately carry OCFS2_INVALID_SLOT. + * Regular inodes are always allocated from a per-slot suballocator. + * If a regular inode with OCFS2_INVALID_SLOT was accepted here, + * deleting it would pass the slot to get_local_system_inode() via + * ocfs2_remove_inode() and trigger BUG_ON(slot == OCFS2_INVALID_SLOT). + */ + if (le16_to_cpu(di->i_suballoc_slot) == (u16)OCFS2_INVALID_SLOT) { + if (!(le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: suballoc slot %u for non-system inode\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_suballoc_slot)); + goto bail; + } + } else if ((u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) { rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n", (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_suballoc_slot)); goto bail; } + /* + * A suballocator block group bitmap is contained in a single block + * and starts after the group descriptor header, so a valid suballoc + * bit can never exceed ocfs2_suballoc_bits_per_block(). Otherwise + * deleting the inode will pass the oversized bit to + * _ocfs2_free_suballoc_bits() via ocfs2_free_dinode() and trigger + * BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)), since any + * group holds at most ocfs2_suballoc_bits_per_block() bits. + */ + if (le16_to_cpu(di->i_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc bit %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_suballoc_bit)); + goto bail; + } + if ((le32_to_cpu(di->i_flags) & OCFS2_ORPHANED_FL) && le16_to_cpu(di->i_orphaned_slot) >= OCFS2_SB(sb)->max_slots) { rc = ocfs2_error(sb, "Invalid dinode %llu: orphaned slot %u\n", diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index ea6802d894c2..cc60db6b9706 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -156,7 +156,7 @@ static void ocfs2_queue_replay_slots(struct ocfs2_super *osb, replay_map->rm_state = REPLAY_DONE; } -void ocfs2_free_replay_slots(struct ocfs2_super *osb) +static void ocfs2_free_replay_slots(struct ocfs2_super *osb) { struct ocfs2_replay_map *replay_map = osb->replay_map; @@ -243,6 +243,7 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb) /* XXX: Should we bug if there are dirty entries? */ kfree(rm); + ocfs2_free_replay_slots(osb); } static int __ocfs2_recovery_map_test(struct ocfs2_super *osb, diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index f8b3b2a3d630..19fc920d26b1 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -151,7 +151,6 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb); void ocfs2_recovery_disable_quota(struct ocfs2_super *osb); int ocfs2_compute_replay_slots(struct ocfs2_super *osb); -void ocfs2_free_replay_slots(struct ocfs2_super *osb); /* * Journal Control: * Initialize, Load, Shutdown, Wipe a journal. diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index fce9a31a3671..d8759605786c 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -248,7 +248,6 @@ static int ocfs2_mknod(const struct mnt_idmap *idmap, int want_meta = 0; int xattr_credits = 0; struct ocfs2_security_xattr_info si = { - .name = NULL, .enable = 1, }; int did_quota_inode = 0; @@ -475,7 +474,7 @@ leave: brelse(new_fe_bh); brelse(parent_fe_bh); - kfree(si.value); + ocfs2_free_security_xattrs(&si); ocfs2_acl_init_release(&acl_state); @@ -1831,7 +1830,6 @@ static int ocfs2_symlink(const struct mnt_idmap *idmap, int want_clusters = 0; int xattr_credits = 0; struct ocfs2_security_xattr_info si = { - .name = NULL, .enable = 1, }; int did_quota = 0, did_quota_inode = 0; @@ -2063,7 +2061,7 @@ bail: brelse(new_fe_bh); brelse(parent_fe_bh); - kfree(si.value); + ocfs2_free_security_xattrs(&si); ocfs2_free_dir_lookup_result(&lookup); if (inode_ac) ocfs2_free_alloc_context(inode_ac); diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 62cad6522c7a..e3bb3cc0b25a 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -502,6 +502,11 @@ struct ocfs2_super */ struct workqueue_struct *ocfs2_wq; + /* deferred reclaim of fully freed suballocator block groups */ + spinlock_t os_suballoc_reclaim_lock; + struct list_head os_suballoc_reclaim_list; + struct work_struct os_suballoc_reclaim_work; + /* sysfs directory per partition */ struct kset *osb_dev_kset; @@ -588,6 +593,18 @@ static inline int ocfs2_supports_discontig_bg(struct ocfs2_super *osb) return 0; } +/* + * A suballocator block group bitmap starts right after the group + * descriptor header, so a suballoc bit can never exceed this number + * of bits. Derive it from ocfs2_group_bitmap_size() which also caps + * it at OCFS2_MAX_BG_BITMAP_SIZE when discontig_bg is enabled. + */ +static inline u32 ocfs2_suballoc_bits_per_block(struct super_block *sb) +{ + return ocfs2_group_bitmap_size(sb, 1, + OCFS2_SB(sb)->s_feature_incompat) * 8; +} + static inline unsigned int ocfs2_link_max(struct ocfs2_super *osb) { if (ocfs2_supports_indexed_dirs(osb)) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index a2b276bcd0a8..8be76fa784ac 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -249,6 +249,25 @@ static void ocfs2_release_local_quota_bitmaps(struct list_head *head) } } +/* Check that the on-disk chunk and block counts match the file layout */ +static int ocfs2_check_local_quota_info(struct inode *inode, + unsigned int chunks, + unsigned int blocks) +{ + u64 chunk_len = ol_chunk_blocks(inode->i_sb) + 1; + u64 min_blocks = chunks ? 1 + chunk_len * (chunks - 1) + 1 : 1; + u64 max_blocks = 1 + chunk_len * chunks; + + if (blocks >= min_blocks && blocks <= max_blocks && + blocks <= i_size_read(inode) >> inode->i_sb->s_blocksize_bits) + return 0; + + return ocfs2_error(inode->i_sb, + "Quota file %llu has bad info: %u chunks, %u blocks\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + chunks, blocks); +} + /* Load quota bitmaps into memory */ static int ocfs2_load_local_quota_bitmaps(struct inode *inode, struct ocfs2_local_disk_dqinfo *ldinfo, @@ -737,6 +756,11 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks); oinfo->dqi_libh = bh; + status = ocfs2_check_local_quota_info(lqinode, oinfo->dqi_chunks, + oinfo->dqi_blocks); + if (status < 0) + goto out_err; + /* We crashed when using local quota file? */ if (!(oinfo->dqi_flags & OLQF_CLEAN)) { rec = OCFS2_SB(sb)->quota_rec; @@ -820,6 +844,9 @@ static int ocfs2_local_free_info(struct super_block *sb, int type) int mark_clean = 1, len; int status = 0; + /* Quota-core error paths can bypass ocfs2_disable_quotas(). */ + cancel_delayed_work_sync(&oinfo->dqi_sync_work); + iput(oinfo->dqi_gqinode); ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock); ocfs2_lock_res_free(&oinfo->dqi_gqlock); @@ -1075,10 +1102,13 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( goto out; } + if (list_empty(&oinfo->dqi_chunk)) + chunk->qc_num = 0; + else + chunk->qc_num = list_entry(oinfo->dqi_chunk.prev, + struct ocfs2_quota_chunk, + qc_chunk)->qc_num + 1; list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk); - chunk->qc_num = list_entry(chunk->qc_chunk.prev, - struct ocfs2_quota_chunk, - qc_chunk)->qc_num + 1; chunk->qc_headerbh = bh; *offset = 0; return chunk; diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index d9f22b4a2654..4f13b5a6faa7 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -118,6 +118,33 @@ static int ocfs2_validate_refcount_block(struct super_block *sb, } /* + * Refcount blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(rb->rf_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rf_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_suballoc_slot)); + goto out; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(rb->rf_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rf_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_suballoc_bit)); + goto out; + } + + /* * rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when * this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL); * in that case the same union bytes hold an extent list (rf_list) @@ -348,8 +375,10 @@ ocfs2_allocate_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno) return new; } +/* Callers without the refcount tree lock must request a reference. */ static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno, - struct ocfs2_refcount_tree **ret_tree) + struct ocfs2_refcount_tree **ret_tree, + bool get_ref) { int ret = 0; struct ocfs2_refcount_tree *tree, *new = NULL; @@ -406,6 +435,8 @@ static int ocfs2_get_refcount_tree(struct ocfs2_super *osb, u64 rf_blkno, new = NULL; out: + if (get_ref) + ocfs2_refcount_tree_get(tree); *ret_tree = tree; osb->osb_ref_tree_lru = tree; @@ -479,14 +510,12 @@ int ocfs2_lock_refcount_tree(struct ocfs2_super *osb, struct ocfs2_refcount_block *rb; again: - ret = ocfs2_get_refcount_tree(osb, ref_blkno, &tree); + ret = ocfs2_get_refcount_tree(osb, ref_blkno, &tree, true); if (ret) { mlog_errno(ret); return ret; } - ocfs2_refcount_tree_get(tree); - ret = __ocfs2_lock_refcount_tree(osb, tree, rw); if (ret) { mlog_errno(ret); @@ -2328,7 +2357,8 @@ int ocfs2_decrease_refcount(struct inode *inode, goto out; } - ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), ref_blkno, &tree); + ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), ref_blkno, + &tree, false); if (ret) { mlog_errno(ret); goto out; @@ -2563,7 +2593,7 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, BUG_ON(!ocfs2_is_refcount_inode(inode)); ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), - refcount_loc, &tree); + refcount_loc, &tree, false); if (ret) { mlog_errno(ret); goto out; diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 20c3aec6b987..624152e4f7fe 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -2687,16 +2687,24 @@ bail: * cleanup rec/alloc_inode job, then switches to the main bitmap * to reclaim released space. * + * Callers must hold inode_lock() and ocfs2_inode_lock() on + * main_bm_inode, i.e. the global bitmap inode locks must be taken + * before starting the transaction. + * * handle: The transaction handle * alloc_inode: The suballoc inode * alloc_bh: The buffer_head of suballoc inode * group_bh: The group descriptor buffer_head of suballocator managed. - * Caller should release the input group_bh. + * This function takes ownership of it and will release it. + * main_bm_inode: The global bitmap inode + * main_bm_bh: The buffer_head of the global bitmap inode */ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, struct inode *alloc_inode, struct buffer_head *alloc_bh, - struct buffer_head *group_bh) + struct buffer_head *group_bh, + struct inode *main_bm_inode, + struct buffer_head *main_bm_bh) { int idx, status = 0; int i, next_free_rec, len = 0; @@ -2706,8 +2714,6 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, u64 bg_blkno, start_blk; unsigned int count; struct ocfs2_chain_rec *rec; - struct buffer_head *main_bm_bh = NULL; - struct inode *main_bm_inode = NULL; struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data; struct ocfs2_chain_list *cl = &fe->id2.i_chain; @@ -2794,24 +2800,12 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_remove_from_cache(INODE_CACHE(alloc_inode), group_bh); memset(group, 0, sizeof(struct ocfs2_group_desc)); - /* prepare job for reclaim clusters */ - main_bm_inode = ocfs2_get_system_file_inode(osb, - GLOBAL_BITMAP_SYSTEM_INODE, - OCFS2_INVALID_SLOT); - if (!main_bm_inode) - goto bail; /* ignore the error in reclaim path */ - - inode_lock(main_bm_inode); - - status = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 1); - if (status < 0) - goto free_bm_inode; /* ignore the error in reclaim path */ - ocfs2_block_to_cluster_group(main_bm_inode, start_blk, &bg_blkno, &start_bit); fe = (struct ocfs2_dinode *) main_bm_bh->b_data; cl = &fe->id2.i_chain; - /* reuse group_bh, caller will release the input group_bh */ + /* release the suballocator group descriptor before reuse */ + brelse(group_bh); group_bh = NULL; /* reclaim clusters to global_bitmap */ @@ -2819,7 +2813,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, &group_bh); if (status < 0) { mlog_errno(status); - goto free_bm_bh; + goto bail; } group = (struct ocfs2_group_desc *) group_bh->b_data; @@ -2827,7 +2821,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_error(alloc_inode->i_sb, "reclaim length (%d) beyands block group length (%d)", count + start_bit, le16_to_cpu(group->bg_bits)); - goto free_group_bh; + goto bail; } old_bg_contig_free_bits = group->bg_contig_free_bits; @@ -2837,7 +2831,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, _ocfs2_clear_bit); if (status < 0) { mlog_errno(status); - goto free_group_bh; + goto bail; } status = ocfs2_journal_access_di(handle, INODE_CACHE(main_bm_inode), @@ -2847,7 +2841,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_block_group_set_bits(handle, main_bm_inode, group, group_bh, start_bit, count, le16_to_cpu(old_bg_contig_free_bits), 1); - goto free_group_bh; + goto bail; } idx = le16_to_cpu(group->bg_chain); @@ -2858,19 +2852,168 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - count); ocfs2_journal_dirty(handle, main_bm_bh); -free_group_bh: +bail: brelse(group_bh); + return status; +} + +/* + * When a suballocator block group becomes fully freed, its space is + * reclaimed back to the global bitmap. Taking the global bitmap inode + * lock inside the freeing transaction would create a lock dependency + * of "j_trans_barrier -> global bitmap inode i_rwsem", which forms a + * circular dependency with paths like ocfs2_shutdown_local_alloc() that + * take the inode lock before starting a transaction, and can lead to a + * real deadlock with the ocfs2cmt journal commit thread. So queue the + * reclaim to the workqueue and let it run outside the freeing + * transaction. + */ +struct ocfs2_suballoc_reclaim_work { + struct list_head list; + struct inode *alloc_inode; + u64 bg_blkno; +}; -free_bm_bh: +static void ocfs2_queue_suballoc_reclaim(struct ocfs2_super *osb, + struct inode *alloc_inode, + u64 bg_blkno) +{ + struct ocfs2_suballoc_reclaim_work *reclaim_work; + + reclaim_work = kmalloc_obj(*reclaim_work, GFP_NOFS); + if (!reclaim_work) { + /* + * Reclaim is only a space return optimization. If we can't + * queue it, the freed block group just stays owned by the + * suballocator. + */ + return; + } + + igrab(alloc_inode); + reclaim_work->alloc_inode = alloc_inode; + reclaim_work->bg_blkno = bg_blkno; + + spin_lock(&osb->os_suballoc_reclaim_lock); + list_add_tail(&reclaim_work->list, &osb->os_suballoc_reclaim_list); + spin_unlock(&osb->os_suballoc_reclaim_lock); + + queue_work(osb->ocfs2_wq, &osb->os_suballoc_reclaim_work); +} + +static void ocfs2_do_suballoc_reclaim(struct ocfs2_super *osb, + struct ocfs2_suballoc_reclaim_work *reclaim_work) +{ + int status, i; + handle_t *handle; + struct inode *alloc_inode = reclaim_work->alloc_inode; + struct inode *main_bm_inode; + struct buffer_head *alloc_bh = NULL, *group_bh = NULL; + struct buffer_head *main_bm_bh = NULL; + struct ocfs2_dinode *fe; + struct ocfs2_chain_list *cl; + struct ocfs2_chain_rec *rec; + + /* journal already gone, e.g. during dismount cleanup */ + if (!osb->journal) + return; + + inode_lock(alloc_inode); + status = ocfs2_inode_lock(alloc_inode, &alloc_bh, 1); + if (status < 0) + goto out_alloc; + + fe = (struct ocfs2_dinode *) alloc_bh->b_data; + cl = &fe->id2.i_chain; + + /* + * The block group may have been allocated from again since the + * reclaim work was queued, re-check that it is still fully freed. + * A stale work item can also reference a group that is no longer + * chained, whose descriptor would fail validation and trigger a + * spurious ocfs2_error(), so verify chain membership first. + */ + for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { + rec = &cl->cl_recs[i]; + if (le64_to_cpu(rec->c_blkno) == reclaim_work->bg_blkno) + break; + } + if (i == le16_to_cpu(cl->cl_next_free_rec) || + ocfs2_is_cluster_bitmap(alloc_inode) || + (le32_to_cpu(rec->c_free) != (le32_to_cpu(rec->c_total) - 1)) || + (le16_to_cpu(cl->cl_next_free_rec) == 1)) + goto out_alloc_unlock; + + status = ocfs2_read_group_descriptor(alloc_inode, fe, + reclaim_work->bg_blkno, &group_bh); + if (status < 0) + goto out_alloc_unlock; + + main_bm_inode = ocfs2_get_system_file_inode(osb, + GLOBAL_BITMAP_SYSTEM_INODE, + OCFS2_INVALID_SLOT); + if (!main_bm_inode) + goto out_group; + + inode_lock(main_bm_inode); + status = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 1); + if (status < 0) + goto out_main; + + handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE); + if (IS_ERR(handle)) { + status = PTR_ERR(handle); + mlog_errno(status); + goto out_main_unlock; + } + + status = _ocfs2_reclaim_suballoc_to_main(handle, alloc_inode, + alloc_bh, group_bh, + main_bm_inode, main_bm_bh); + /* group_bh ownership passed to _ocfs2_reclaim_suballoc_to_main() */ + group_bh = NULL; + if (status < 0) + mlog_errno(status); + + ocfs2_commit_trans(osb, handle); + +out_main_unlock: ocfs2_inode_unlock(main_bm_inode, 1); brelse(main_bm_bh); - -free_bm_inode: +out_main: inode_unlock(main_bm_inode); iput(main_bm_inode); +out_group: + brelse(group_bh); +out_alloc_unlock: + ocfs2_inode_unlock(alloc_inode, 1); + brelse(alloc_bh); +out_alloc: + inode_unlock(alloc_inode); +} -bail: - return status; +void ocfs2_suballoc_reclaim_worker(struct work_struct *work) +{ + struct ocfs2_super *osb = container_of(work, struct ocfs2_super, + os_suballoc_reclaim_work); + struct ocfs2_suballoc_reclaim_work *reclaim_work; + + while (1) { + spin_lock(&osb->os_suballoc_reclaim_lock); + if (list_empty(&osb->os_suballoc_reclaim_list)) { + spin_unlock(&osb->os_suballoc_reclaim_lock); + break; + } + reclaim_work = list_first_entry(&osb->os_suballoc_reclaim_list, + struct ocfs2_suballoc_reclaim_work, + list); + list_del(&reclaim_work->list); + spin_unlock(&osb->os_suballoc_reclaim_lock); + + ocfs2_do_suballoc_reclaim(osb, reclaim_work); + iput(reclaim_work->alloc_inode); + kfree(reclaim_work); + } } /* @@ -2897,10 +3040,22 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, /* The alloc_bh comes from ocfs2_free_dinode() or * ocfs2_free_clusters(). The callers have all locked the * allocator and gotten alloc_bh from the lock call. This - * validates the dinode buffer. Any corruption that has happened - * is a code bug. */ + * validates the dinode buffer. */ BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); - BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)); + + /* + * ocfs2_bits_per_group() is derived from cl_cpg and cl_bpc of the + * allocator dinode, which are not validated against the volume + * geometry. A corrupted image can carry a suballoc bit beyond it, + * so error out instead of crashing. + */ + if ((count + start_bit) > ocfs2_bits_per_group(cl)) { + return ocfs2_error(alloc_inode->i_sb, + "Allocator #%llu: freeing bits %u+%u exceeds bits per group %u\n", + (unsigned long long)le64_to_cpu(fe->i_blkno), + count, start_bit, + ocfs2_bits_per_group(cl)); + } trace_ocfs2_free_suballoc_bits( (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno, @@ -2915,7 +3070,20 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, } group = (struct ocfs2_group_desc *) group_bh->b_data; - BUG_ON((count + start_bit) > le16_to_cpu(group->bg_bits)); + /* + * Group descriptor validation only guarantees bg_bits within the + * physical bitmap size, so double check the freeing range here. + * Otherwise ocfs2_block_group_clear_bits() would clear bits beyond + * bg_bitmap. + */ + if ((count + start_bit) > le16_to_cpu(group->bg_bits)) { + status = ocfs2_error(alloc_inode->i_sb, + "Group descriptor #%llu has %u bits, cannot free bits %u+%u\n", + (unsigned long long)le64_to_cpu(group->bg_blkno), + le16_to_cpu(group->bg_bits), + count, start_bit); + goto bail; + } if (ocfs2_is_cluster_bitmap(alloc_inode)) old_bg_contig_free_bits = group->bg_contig_free_bits; @@ -2955,7 +3123,8 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, goto bail; } - _ocfs2_reclaim_suballoc_to_main(handle, alloc_inode, alloc_bh, group_bh); + ocfs2_queue_suballoc_reclaim(OCFS2_SB(alloc_inode->i_sb), alloc_inode, + bg_blkno); bail: brelse(group_bh); diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index bcf2ed4a8631..6042abc032f9 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -206,7 +206,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et, int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res); - +void ocfs2_suballoc_reclaim_worker(struct work_struct *work); /* * The following two interfaces are for ocfs2_create_inode_in_orphan(). diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index c62e389d4dd6..0af2245ae303 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -387,6 +387,7 @@ static ssize_t ocfs2_debug_read(struct file *file, char __user *buf, #endif /* CONFIG_DEBUG_FS */ static const struct file_operations ocfs2_osb_debug_fops = { + .owner = THIS_MODULE, .open = ocfs2_osb_debug_open, .release = ocfs2_debug_release, .read = ocfs2_debug_read, @@ -1162,15 +1163,23 @@ static int ocfs2_fill_super(struct super_block *sb, struct fs_context *fc) out_dismount: atomic_set(&osb->vol_state, VOLUME_DISABLED); wake_up(&osb->osb_mount_event); - ocfs2_free_replay_slots(osb); ocfs2_dismount_volume(sb, 1); goto out; out_debugfs: debugfs_remove_recursive(osb->osb_debug_root); out_super: + /* + * A recovery thread launched by a node failure event may still be + * waiting for the volume to be mounted. Set VOLUME_DISABLED and + * wake it up, then wait for it to exit before osb is freed, + * otherwise the kthread would leak and stay blocked on the wait + * queue embedded in the freed osb. + */ + atomic_set(&osb->vol_state, VOLUME_DISABLED); + wake_up(&osb->osb_mount_event); ocfs2_release_system_inodes(osb); - kfree(osb->recovery_map); + ocfs2_recovery_exit(osb); ocfs2_delete_osb(osb); kfree(osb); out: @@ -1767,17 +1776,18 @@ static int ocfs2_mount_volume(struct super_block *sb) status = ocfs2_truncate_log_init(osb); if (status < 0) { mlog_errno(status); - goto out_check_volume; + goto out_system_inodes; } ocfs2_super_unlock(osb, 1); return 0; -out_check_volume: - ocfs2_free_replay_slots(osb); out_system_inodes: if (osb->local_alloc_state == OCFS2_LA_ENABLED) ocfs2_shutdown_local_alloc(osb); + /* Drain pending suballoc reclaim work before the journal goes away */ + if (osb->ocfs2_wq) + flush_workqueue(osb->ocfs2_wq); ocfs2_release_system_inodes(osb); /* before journal shutdown, we should release slot_info */ ocfs2_free_slot_info(osb); @@ -1848,6 +1858,14 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) if (osb->cconn) ocfs2_super_unlock(osb, 1); + /* + * Drain pending suballoc reclaim work while the system inodes and + * the journal are still alive, since the worker needs to look up + * the global bitmap inode and start a transaction. + */ + if (osb->ocfs2_wq) + flush_workqueue(osb->ocfs2_wq); + ocfs2_release_system_inodes(osb); ocfs2_journal_shutdown(osb); @@ -2134,6 +2152,11 @@ static int ocfs2_initialize_super(struct super_block *sb, INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs); init_llist_head(&osb->dquot_drop_list); + spin_lock_init(&osb->os_suballoc_reclaim_lock); + INIT_LIST_HEAD(&osb->os_suballoc_reclaim_list); + INIT_WORK(&osb->os_suballoc_reclaim_work, + ocfs2_suballoc_reclaim_worker); + /* get some pseudo constants for clustersize bits */ osb->s_clustersize_bits = le32_to_cpu(di->id2.i_super.s_clustersize_bits); diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 5274f4d571b6..b2bd06083a14 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -237,6 +237,21 @@ static int namevalue_size_xe(struct ocfs2_xattr_entry *xe) return namevalue_size(xe->xe_name_len, value_len); } +static int ocfs2_validate_xattr_entry(struct super_block *sb, u64 blkno, + struct ocfs2_xattr_entry *xe) +{ + u64 value_len = le64_to_cpu(xe->xe_value_size); + + if (value_len > OCFS2_XATTR_INLINE_SIZE && + ocfs2_xattr_is_local(xe)) + return ocfs2_error(sb, + "Invalid local xattr in block %llu: value size %llu\n", + (unsigned long long)blkno, + (unsigned long long)value_len); + + return 0; +} + static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, struct ocfs2_xattr_header *xh, @@ -476,6 +491,7 @@ static int ocfs2_validate_xattr_block(struct super_block *sb, struct buffer_head *bh) { int rc; + struct ocfs2_extent_list *el; struct ocfs2_xattr_block *xb = (struct ocfs2_xattr_block *)bh->b_data; @@ -517,6 +533,31 @@ static int ocfs2_validate_xattr_block(struct super_block *sb, le32_to_cpu(xb->xb_fs_generation)); } + /* + * Xattr blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(xb->xb_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + return ocfs2_error(sb, + "Extended attribute block #%llu has an invalid xb_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(xb->xb_suballoc_slot)); + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(xb->xb_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + return ocfs2_error(sb, + "Extended attribute block #%llu has an invalid xb_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(xb->xb_suballoc_bit)); + } + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { size_t region_offset = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); @@ -533,6 +574,30 @@ static int ocfs2_validate_xattr_block(struct super_block *sb, region_offset); } + /* + * An indexed xattr block carries the root of the xattr tree. Its + * record count is fixed by the block size and l_next_free_rec never + * exceeds it. A zero l_next_free_rec is valid: emptying the tree on + * the inode deletion path leaves the root with no extents. + */ + el = &xb->xb_attrs.xb_root.xt_list; + + if (le16_to_cpu(el->l_count) != ocfs2_xattr_recs_per_xb(sb)) { + return ocfs2_error(sb, + "Xattr tree root #%llu has invalid l_count %u (expected %u)\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(el->l_count), + ocfs2_xattr_recs_per_xb(sb)); + } + + if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) { + return ocfs2_error(sb, + "Xattr tree root #%llu has l_next_free_rec %u past the end of its list (l_count %u)\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(el->l_next_free_rec), + le16_to_cpu(el->l_count)); + } + return 0; } @@ -561,9 +626,10 @@ static inline const char *ocfs2_xattr_prefix(int name_index) return handler ? xattr_prefix(handler) : NULL; } -static u32 ocfs2_xattr_name_hash(struct inode *inode, - const char *name, - int name_len) +static u32 __ocfs2_xattr_name_hash(struct inode *inode, + const char *name, + int name_len, + bool legacy_signed) { /* Get hash value of uuid from super block */ u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash; @@ -572,13 +638,30 @@ static u32 ocfs2_xattr_name_hash(struct inode *inode, /* hash extended attribute name */ for (i = 0; i < name_len; i++) { hash = (hash << OCFS2_HASH_SHIFT) ^ - (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ - *name++; + (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)); + if (legacy_signed) + hash ^= (signed char)name[i]; + else + hash ^= (unsigned char)name[i]; } return hash; } +static u32 ocfs2_xattr_name_hash(struct inode *inode, + const char *name, + int name_len) +{ + return __ocfs2_xattr_name_hash(inode, name, name_len, false); +} + +static u32 ocfs2_xattr_name_hash_signed(struct inode *inode, + const char *name, + int name_len) +{ + return __ocfs2_xattr_name_hash(inode, name, name_len, true); +} + static int ocfs2_xattr_entry_real_size(int name_len, size_t value_len) { return namevalue_size(name_len, value_len) + @@ -603,10 +686,15 @@ int ocfs2_calc_security_init(struct inode *dir, int *xattr_credits, struct ocfs2_alloc_context **xattr_ac) { + int i; int ret = 0; struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); - int s_size = ocfs2_xattr_entry_real_size(strlen(si->name), - si->value_len); + int s_size = 0; + + for (i = 0; i < si->count; i++) + s_size += ocfs2_xattr_entry_real_size( + strlen(si->xattrs[i].name), + si->xattrs[i].value_len); /* * The max space of security xattr taken inline is @@ -623,14 +711,27 @@ int ocfs2_calc_security_init(struct inode *dir, *xattr_credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; } - /* reserve clusters for xattr value which will be set in B tree*/ - if (si->value_len > OCFS2_XATTR_INLINE_SIZE) { - int new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, - si->value_len); + /* + * when blocksize = 512, the security xattrs may not fit in the + * single block reserved above and a bucket has to be allocated + * for them, so reserve the cluster it needs as well. + */ + if (dir->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE && + s_size > OCFS2_XATTR_FREE_IN_BLOCK(dir)) { + *want_clusters += 1; + *xattr_credits += ocfs2_blocks_per_xattr_bucket(dir->i_sb); + } - *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, - new_clusters); - *want_clusters += new_clusters; + /* reserve clusters for xattr value which will be set in B tree*/ + for (i = 0; i < si->count; i++) { + if (si->xattrs[i].value_len > OCFS2_XATTR_INLINE_SIZE) { + int new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, + si->xattrs[i].value_len); + + *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, + new_clusters); + *want_clusters += new_clusters; + } } return ret; } @@ -640,12 +741,16 @@ void ocfs2_calc_xattr_init(struct inode *dir, umode_t mode, int *want_clusters, int *xattr_credits, int *want_meta, struct ocfs2_acl_state *acl_state) { + int i; struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); int s_size = 0, a_size = 0, acl_len = 0, new_clusters; - if (si->enable) - s_size = ocfs2_xattr_entry_real_size(strlen(si->name), - si->value_len); + if (si->enable) { + for (i = 0; i < si->count; i++) + s_size += ocfs2_xattr_entry_real_size( + strlen(si->xattrs[i].name), + si->xattrs[i].value_len); + } if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { if (acl_state->default_acl && S_ISDIR(mode)) { @@ -692,12 +797,14 @@ void ocfs2_calc_xattr_init(struct inode *dir, umode_t mode, * reserve credits and clusters for xattrs which has large value * and have to be set outside */ - if (si->enable && si->value_len > OCFS2_XATTR_INLINE_SIZE) { - new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, - si->value_len); - *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, - new_clusters); - *want_clusters += new_clusters; + for (i = 0; si->enable && i < si->count; i++) { + if (si->xattrs[i].value_len > OCFS2_XATTR_INLINE_SIZE) { + new_clusters = ocfs2_clusters_for_bytes(dir->i_sb, + si->xattrs[i].value_len); + *xattr_credits += ocfs2_clusters_to_blocks(dir->i_sb, + new_clusters); + *want_clusters += new_clusters; + } } if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) { if (acl_state->default_acl && S_ISDIR(mode)) { @@ -989,7 +1096,7 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, size_t entries_limit = region_size; size_t nv_limit = region_size; size_t max_entries; - int i; + int i, ret; if (region_size < sizeof(*xh)) return ocfs2_error(sb, @@ -1009,6 +1116,11 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; size_t name_offset = le16_to_cpu(xe->xe_name_offset); size_t value_offset; + u64 value_len = le64_to_cpu(xe->xe_value_size); + + ret = ocfs2_validate_xattr_entry(sb, blkno, xe); + if (ret) + return ret; if (name_offset > nv_limit || xe->xe_name_len > nv_limit - name_offset) @@ -1023,8 +1135,7 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, (unsigned long long)blkno, i); if (ocfs2_xattr_is_local(xe)) { - if (le64_to_cpu(xe->xe_value_size) > - nv_limit - value_offset) + if (value_len > nv_limit - value_offset) return ocfs2_error(sb, "Invalid xattr in block %llu: entry %d value is out of bounds\n", (unsigned long long)blkno, @@ -1106,10 +1217,29 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, struct ocfs2_xattr_header *xh = bucket_xh(bucket); u16 xattr_count = le16_to_cpu(xh->xh_count); size_t region_size = (size_t)sb->s_blocksize * bucket->bu_blocks; - size_t entries_limit = sb->s_blocksize; + /* + * The entry array grows up from the header across the whole + * bucket region, so it may extend beyond the first bucket block + * when the blocksize is smaller than OCFS2_XATTR_BUCKET_SIZE. + * Name/value pairs, however, always live within a single block. + */ + size_t entries_limit = region_size; size_t nv_limit = sb->s_blocksize; size_t max_entries; - int i; + int i, ret; + + /* + * The entry array is one contiguous region that may span the + * bucket's buffer_heads. Buckets are allocated within clusters, + * so their first block is always aligned to + * OCFS2_XATTR_BUCKET_SIZE and the whole bucket fits in one page. + * A corrupted xattr tree can point a bucket at blocks straddling + * a page, so reject it before touching the entry array. + */ + if (blkno & (bucket->bu_blocks - 1)) + return ocfs2_error(sb, + "Invalid xattr bucket %llu: unaligned block number\n", + (unsigned long long)blkno); if (region_size < sizeof(*xh)) return ocfs2_error(sb, @@ -1137,6 +1267,11 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, size_t block_off = name_offset >> sb->s_blocksize_bits; size_t block_offset = name_offset % nv_limit; size_t value_offset; + u64 value_len = le64_to_cpu(xe->xe_value_size); + + ret = ocfs2_validate_xattr_entry(sb, blkno, xe); + if (ret) + return ret; if (name_offset >= region_size || block_off >= bucket->bu_blocks) return ocfs2_error(sb, @@ -1155,8 +1290,7 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, (unsigned long long)blkno, i); if (ocfs2_xattr_is_local(xe)) { - if (le64_to_cpu(xe->xe_value_size) > - nv_limit - value_offset) + if (value_len > nv_limit - value_offset) return ocfs2_error(sb, "Invalid xattr bucket %llu: entry %d value is out of bounds\n", (unsigned long long)blkno, @@ -1304,7 +1438,7 @@ static int ocfs2_xattr_find_entry(struct inode *inode, int name_index, { struct ocfs2_xattr_entry *entry; size_t name_len; - int i, name_offset, cmp = 1; + int i, name_offset, cmp = 1, ret; if (name == NULL) return -EINVAL; @@ -1327,6 +1461,12 @@ static int ocfs2_xattr_find_entry(struct inode *inode, int name_index, return -EFSCORRUPTED; } cmp = memcmp(name, (xs->base + name_offset), name_len); + if (!cmp) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, + OCFS2_I(inode)->ip_blkno, entry); + if (ret) + return ret; + } } if (cmp == 0) break; @@ -2073,12 +2213,17 @@ static void ocfs2_xa_bucket_add_entry(struct ocfs2_xa_loc *loc, u32 name_hash) } } + /* + * Increment xh_count before memmove() so __counted_by_le(xh_count) + * includes the new entry in the destination bounds. + */ + le16_add_cpu(&xh->xh_count, 1); + if (low != count) memmove(&xh->xh_entries[low + 1], &xh->xh_entries[low], ((count - low) * sizeof(struct ocfs2_xattr_entry))); - le16_add_cpu(&xh->xh_count, 1); loc->xl_entry = &xh->xh_entries[low]; memset(loc->xl_entry, 0, sizeof(struct ocfs2_xattr_entry)); } @@ -2909,6 +3054,9 @@ static int ocfs2_xattr_has_space_inline(struct inode *inode, * * Find extended attribute in inode block and * fill search info into struct ocfs2_xattr_search. + * + * The inline free-space check races with truncate and allocation, so + * callers must hold ip_alloc_sem for writing. */ static int ocfs2_xattr_ibody_find(struct inode *inode, int name_index, @@ -2920,13 +3068,13 @@ static int ocfs2_xattr_ibody_find(struct inode *inode, int ret; int has_space = 0; + lockdep_assert_held_write(&oi->ip_alloc_sem); + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return 0; if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { - down_read(&oi->ip_alloc_sem); has_space = ocfs2_xattr_has_space_inline(inode, di); - up_read(&oi->ip_alloc_sem); if (!has_space) return 0; } @@ -3007,6 +3155,7 @@ out: * * Set, replace or remove an extended attribute into inode block. * + * Callers must hold ip_alloc_sem for writing. */ static int ocfs2_xattr_ibody_set(struct inode *inode, struct ocfs2_xattr_info *xi, @@ -3017,16 +3166,17 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_xa_loc loc; + lockdep_assert_held_write(&oi->ip_alloc_sem); + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return -ENOSPC; - down_write(&oi->ip_alloc_sem); if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { ret = ocfs2_xattr_ibody_init(inode, xs->inode_bh, ctxt); if (ret) { if (ret != -ENOSPC) mlog_errno(ret); - goto out; + return ret; } } @@ -3036,13 +3186,10 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, if (ret) { if (ret != -ENOSPC) mlog_errno(ret); - goto out; + return ret; } xs->here = loc.xl_entry; -out: - up_write(&oi->ip_alloc_sem); - return ret; } @@ -3376,6 +3523,14 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode, credits += ocfs2_calc_extend_credits( inode->i_sb, &def_xv.xv.xr_list); + /* + * A value too big to stay inline is only charged as + * OCFS2_XATTR_ROOT_SIZE above, so it can still need a + * value tree of its own. No xattr block is allocated + * on this path, so the tree is all we reserve for. + */ + if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) + meta_add += ocfs2_extend_meta_needed(&def_xv.xv.xr_list); goto out; } } @@ -3397,12 +3552,21 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode, credits += ocfs2_remove_extent_credits(inode->i_sb); goto out; } else { - meta_add += ocfs2_extend_meta_needed(&xv->xr_list); clusters_add += new_clusters - old_clusters; credits += ocfs2_calc_extend_credits(inode->i_sb, &xv->xr_list); - if (value_size >= OCFS2_XATTR_ROOT_SIZE) + /* + * The old value occupies at least as much room as a + * value root, whether it sat outside or inline, so the + * new root fits where it was and no new xattr block or + * bucket is needed -- only the metadata to extend the + * tree hanging off it. A smaller one is replaced + * outright, which meta_guess below reserves for. + */ + if (value_size >= OCFS2_XATTR_ROOT_SIZE) { + meta_add += ocfs2_extend_meta_needed(&xv->xr_list); goto out; + } } } else { /* @@ -3454,6 +3618,10 @@ meta_guess: * Reserve metadata for the new xattr's value extent tree. * The not_found path above adds credits for this tree but * omits meta_add, leaving meta_ac NULL for large values. + * + * This is all of meta_ac on this side beyond the xattr tree + * above: no xattr block is allocated here, and a new bucket or + * index block is paid for out of data_ac. */ if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) meta_add += ocfs2_extend_meta_needed(&def_xv.xv.xr_list); @@ -3476,7 +3644,13 @@ meta_guess: credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { struct ocfs2_extent_list *el = &def_xv.xv.xr_list; - meta_add += ocfs2_extend_meta_needed(el); + /* + * One block for the xattr block we may have to + * allocate, the rest for growing the value tree it + * would hold. If ocfs2_xattr_ibody_set() succeeds + * instead, that first block goes unused. + */ + meta_add += 1 + ocfs2_extend_meta_needed(el); credits += ocfs2_calc_extend_credits(inode->i_sb, el); } else { @@ -3687,6 +3861,18 @@ out: } /* + * ip_alloc_sem subclass for inodes being initialized before publication. + * ocfs2_xattr_set_handle() runs inside the create transaction, so taking + * ip_alloc_sem there adds a transaction -> ip_alloc_sem order that would + * form a lockdep cycle with the ip_alloc_sem -> transaction order used + * elsewhere, if not for this separate subclass. The inode is unpublished + * so the acquisition can never contend. + */ +enum { + OCFS2_IP_ALLOC_SEM_UNPUBLISHED = 1, +}; + +/* * This helper is only for setting initial ACL or security xattrs on an inode * that is still unpublished, unhashed, and unattached to a dentry. * Ordinary xattr updates must use ocfs2_xattr_set(). @@ -3747,6 +3933,13 @@ int ocfs2_xattr_set_handle(handle_t *handle, xis.inode_bh = xbs.inode_bh = di_bh; di = (struct ocfs2_dinode *)di_bh->b_data; + /* + * The inode is unpublished and cannot contend, but take the + * semaphore anyway so the helpers' lockdep assertions hold. + */ + down_write_nested(&OCFS2_I(inode)->ip_alloc_sem, + OCFS2_IP_ALLOC_SEM_UNPUBLISHED); + ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); if (ret) goto cleanup; @@ -3759,6 +3952,7 @@ int ocfs2_xattr_set_handle(handle_t *handle, ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt); cleanup: + up_write(&OCFS2_I(inode)->ip_alloc_sem); brelse(xbs.xattr_bh); ocfs2_xattr_bucket_free(xbs.bucket); @@ -3828,29 +4022,37 @@ int ocfs2_xattr_set(struct inode *inode, down_write(&OCFS2_I(inode)->ip_xattr_sem); /* + * The allocation and truncate paths take ip_alloc_sem before + * starting a transaction, so take it here before xattr + * preparation, allocation reservations and ocfs2_start_trans() + * to keep that order. The xattr helpers below no longer take + * it themselves. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + /* * Scan inode and external block to find the same name * extended attribute and collect search information. */ ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); if (ret) - goto cleanup; + goto out_free_ac; if (xis.not_found) { ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs); if (ret) - goto cleanup; + goto out_free_ac; } if (xis.not_found && xbs.not_found) { ret = -ENODATA; if (flags & XATTR_REPLACE) - goto cleanup; + goto out_free_ac; ret = 0; if (!value) - goto cleanup; + goto out_free_ac; } else { ret = -EEXIST; if (flags & XATTR_CREATE) - goto cleanup; + goto out_free_ac; } /* Check whether the value is refcounted and do some preparation. */ @@ -3861,7 +4063,7 @@ int ocfs2_xattr_set(struct inode *inode, &ref_meta, &ref_credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_free_ac; } } @@ -3872,7 +4074,7 @@ int ocfs2_xattr_set(struct inode *inode, if (ret < 0) { inode_unlock(tl_inode); mlog_errno(ret); - goto cleanup; + goto out_free_ac; } } inode_unlock(tl_inode); @@ -3881,7 +4083,7 @@ int ocfs2_xattr_set(struct inode *inode, &xbs, &ctxt, ref_meta, &credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_free_ac; } /* we need to update inode's ctime field, so add credit for it. */ @@ -3899,6 +4101,7 @@ int ocfs2_xattr_set(struct inode *inode, ocfs2_commit_trans(osb, ctxt.handle); out_free_ac: + up_write(&OCFS2_I(inode)->ip_alloc_sem); if (ctxt.data_ac) ocfs2_free_alloc_context(ctxt.data_ac); if (ctxt.meta_ac) @@ -3907,7 +4110,6 @@ out_free_ac: ocfs2_schedule_truncate_log_flush(osb, 1); ocfs2_run_deallocs(osb, &ctxt.dealloc); -cleanup: if (ref_tree) ocfs2_unlock_refcount_tree(osb, ref_tree, 1); up_write(&OCFS2_I(inode)->ip_xattr_sem); @@ -4038,6 +4240,10 @@ static int ocfs2_find_xe_in_bucket(struct inode *inode, xe_name = bucket_block(bucket, block_off) + new_offset; if (!memcmp(name, xe_name, name_len)) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, + OCFS2_I(inode)->ip_blkno, xe); + if (ret) + break; *xe_index = i; *found = 1; ret = 0; @@ -4168,11 +4374,12 @@ out: return ret; } -static int ocfs2_xattr_index_block_find(struct inode *inode, - struct buffer_head *root_bh, - int name_index, - const char *name, - struct ocfs2_xattr_search *xs) +static int __ocfs2_xattr_index_block_find(struct inode *inode, + struct buffer_head *root_bh, + int name_index, + const char *name, + u32 name_hash, + struct ocfs2_xattr_search *xs) { int ret; struct ocfs2_xattr_block *xb = @@ -4181,7 +4388,6 @@ static int ocfs2_xattr_index_block_find(struct inode *inode, struct ocfs2_extent_list *el = &xb_root->xt_list; u64 p_blkno = 0; u32 first_hash, num_clusters = 0; - u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name)); if (le16_to_cpu(el->l_next_free_rec) == 0) return -ENODATA; @@ -4212,6 +4418,59 @@ out: return ret; } +static int ocfs2_xattr_index_block_find(struct inode *inode, + struct buffer_head *root_bh, + int name_index, + const char *name, + struct ocfs2_xattr_search *xs) +{ + u32 name_hash, legacy_hash; + int name_len = strlen(name); + int ret; + + name_hash = ocfs2_xattr_name_hash(inode, name, name_len); + + ret = __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name, + name_hash, xs); + if (ret != -ENODATA) + return ret; + + /* + * Nothing under the current hash. The entry may have been stored by + * an older kernel, which sign-extended the name bytes when hashing. + * Skip the retry when the two hashes are equal, so that a name made + * only of ASCII does not have to walk the tree twice. + */ + legacy_hash = ocfs2_xattr_name_hash_signed(inode, name, name_len); + if (legacy_hash == name_hash) + return ret; + + /* + * A miss still leaves xs->bucket holding the bucket a new entry would + * be inserted into, so drop it before searching again. + */ + ocfs2_xattr_bucket_relse(xs->bucket); + + ret = __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name, + legacy_hash, xs); + if (!ret) { + pr_warn_once("ocfs2: xattr tree with signed name hash\n"); + return ret; + } + if (ret != -ENODATA) + return ret; + + /* + * Not under either hash. Restore the unsigned placement, since that + * is where a new entry is stored: leaving the bucket where the legacy + * hash put it would break the ordering the search relies on. + */ + ocfs2_xattr_bucket_relse(xs->bucket); + + return __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name, + name_hash, xs); +} + static int ocfs2_iterate_xattr_buckets(struct inode *inode, u64 blkno, u32 clusters, @@ -4390,7 +4649,8 @@ static int ocfs2_xattr_tree_list_index_block(struct inode *inode, ret = ocfs2_iterate_xattr_index_block(inode, blk_bh, ocfs2_list_xattr_tree_rec, &xl); if (ret) { - mlog_errno(ret); + if (ret != -ERANGE) + mlog_errno(ret); goto out; } @@ -4508,6 +4768,10 @@ static void ocfs2_xattr_update_xattr_search(struct inode *inode, xs->here = &xs->header->xh_entries[i]; } +/* + * Caller must hold ip_alloc_sem for writing, since a new xattr block + * is allocated and the xattr block header is rewritten. + */ static int ocfs2_xattr_create_index_block(struct inode *inode, struct ocfs2_xattr_search *xs, struct ocfs2_xattr_set_ctxt *ctxt) @@ -4523,19 +4787,14 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, struct ocfs2_xattr_tree_root *xr; u16 xb_flags = le16_to_cpu(xb->xb_flags); + lockdep_assert_held_write(&oi->ip_alloc_sem); + trace_ocfs2_xattr_create_index_block_begin( (unsigned long long)xb_bh->b_blocknr); BUG_ON(xb_flags & OCFS2_XATTR_INDEXED); BUG_ON(!xs->bucket); - /* - * XXX: - * We can use this lock for now, and maybe move to a dedicated mutex - * if performance becomes a problem later. - */ - down_write(&oi->ip_alloc_sem); - ret = ocfs2_journal_access_xb(handle, INODE_CACHE(inode), xb_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { @@ -4597,8 +4856,6 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, ocfs2_journal_dirty(handle, xb_bh); out: - up_write(&oi->ip_alloc_sem); - return ret; } @@ -4678,6 +4935,9 @@ static int ocfs2_defrag_xattr_bucket(struct inode *inode, xe = xh->xh_entries; end = OCFS2_XATTR_BUCKET_SIZE; for (i = 0; i < le16_to_cpu(xh->xh_count); i++, xe++) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, blkno, xe); + if (ret) + goto out; offset = le16_to_cpu(xe->xe_name_offset); len = namevalue_size_xe(xe); @@ -4694,16 +4954,22 @@ static int ocfs2_defrag_xattr_bucket(struct inode *inode, memmove(bucket_buf + end - len, bucket_buf + offset, len); xe->xe_name_offset = cpu_to_le16(end - len); + } else if (end < offset + len) { + ret = ocfs2_error(inode->i_sb, + "Defrag check failed for bucket %llu\n", + (unsigned long long)blkno); + goto out; } - mlog_bug_on_msg(end < offset + len, "Defrag check failed for " - "bucket %llu\n", (unsigned long long)blkno); - end -= len; } - mlog_bug_on_msg(xh_free_start > end, "Defrag check failed for " - "bucket %llu\n", (unsigned long long)blkno); + if (xh_free_start > end) { + ret = ocfs2_error(inode->i_sb, + "Defrag check failed for bucket %llu\n", + (unsigned long long)blkno); + goto out; + } if (xh_free_start == end) goto out; @@ -4960,6 +5226,9 @@ static int ocfs2_divide_xattr_bucket(struct inode *inode, name_value_len = 0; for (i = 0; i < start; i++) { xe = &xh->xh_entries[i]; + ret = ocfs2_validate_xattr_entry(inode->i_sb, blk, xe); + if (ret) + goto out; name_value_len += namevalue_size_xe(xe); if (le16_to_cpu(xe->xe_name_offset) < name_offset) name_offset = le16_to_cpu(xe->xe_name_offset); @@ -5750,16 +6019,14 @@ out: /* * check whether the xattr bucket is filled up with the same hash value. - * If we want to insert the xattr with the same hash, return -ENOSPC. - * If we want to insert a xattr with different hash value, go ahead - * and ocfs2_divide_xattr_bucket will handle this. + * If the entry being set carries that same hash, return -ENOSPC, since + * ocfs2_divide_xattr_bucket() has no divide position to work with. + * Otherwise go ahead and ocfs2_divide_xattr_bucket() will handle this. */ -static int ocfs2_check_xattr_bucket_collision(struct inode *inode, - struct ocfs2_xattr_bucket *bucket, - const char *name) +static int ocfs2_check_xattr_bucket_collision(struct ocfs2_xattr_bucket *bucket, + u32 name_hash) { struct ocfs2_xattr_header *xh = bucket_xh(bucket); - u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name)); if (name_hash != le32_to_cpu(xh->xh_entries[0].xe_name_hash)) return 0; @@ -5828,6 +6095,7 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode, struct ocfs2_xattr_search *xs, struct ocfs2_xattr_set_ctxt *ctxt) { + u32 name_hash; int ret; trace_ocfs2_xattr_set_entry_index_block(xi->xi_name); @@ -5847,10 +6115,18 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode, * the maximum number of collisions we will allow for then is * one bucket's worth, so check it here whether we need to * add a new bucket for the insert. + * + * An existing entry keeps the hash it was stored under, and that is + * the hash a split has to work with. A new entry is stored under the + * unsigned one, which is what ocfs2_xa_add_entry() will write. */ - ret = ocfs2_check_xattr_bucket_collision(inode, - xs->bucket, - xi->xi_name); + if (xs->not_found) + name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name, + xi->xi_name_len); + else + name_hash = le32_to_cpu(xs->here->xe_name_hash); + + ret = ocfs2_check_xattr_bucket_collision(xs->bucket, name_hash); if (ret) { mlog_errno(ret); goto out; @@ -7514,17 +7790,46 @@ static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array { struct ocfs2_security_xattr_info *si = fs_info; const struct xattr *xattr; + struct ocfs2_security_xattr *xattrs; + int count = 0, i; int err = 0; if (si) { - si->value = kmemdup(xattr_array->value, xattr_array->value_len, - GFP_KERNEL); - if (!si->value) + for (xattr = xattr_array; xattr->name != NULL; xattr++) + count++; + + xattrs = kcalloc(count, sizeof(*xattrs), GFP_NOFS); + if (!xattrs) return -ENOMEM; - si->name = xattr_array->name; - si->value_len = xattr_array->value_len; + for (i = 0; i < count; i++) { + xattrs[i].name = kstrdup(xattr_array[i].name, GFP_NOFS); + if (!xattrs[i].name) { + err = -ENOMEM; + goto out_err; + } + + xattrs[i].value = kmemdup(xattr_array[i].value, + xattr_array[i].value_len, + GFP_NOFS); + if (!xattrs[i].value) { + err = -ENOMEM; + goto out_err; + } + xattrs[i].value_len = xattr_array[i].value_len; + } + + si->xattrs = xattrs; + si->count = count; return 0; + +out_err: + for (; i >= 0; i--) { + kfree(xattrs[i].name); + kfree(xattrs[i].value); + } + kfree(xattrs); + return err; } for (xattr = xattr_array; xattr->name != NULL; xattr++) { @@ -7537,6 +7842,19 @@ static int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array return err; } +void ocfs2_free_security_xattrs(struct ocfs2_security_xattr_info *si) +{ + int i; + + for (i = 0; i < si->count; i++) { + kfree(si->xattrs[i].name); + kfree(si->xattrs[i].value); + } + kfree(si->xattrs); + si->xattrs = NULL; + si->count = 0; +} + int ocfs2_init_security_get(struct inode *inode, struct inode *dir, const struct qstr *qstr, @@ -7554,7 +7872,7 @@ int ocfs2_init_security_get(struct inode *inode, * security_inode_init_security() does not return -EOPNOTSUPP, * we have to check the xattr ourselves. */ - if (!ret && !si->name) + if (!ret && !si->count) si->enable = 0; return ret; @@ -7571,10 +7889,21 @@ int ocfs2_init_security_set(handle_t *handle, struct ocfs2_alloc_context *xattr_ac, struct ocfs2_alloc_context *data_ac) { - return ocfs2_xattr_set_handle(handle, inode, di_bh, - OCFS2_XATTR_INDEX_SECURITY, - si->name, si->value, si->value_len, 0, - xattr_ac, data_ac); + int i; + int ret = 0; + + for (i = 0; i < si->count; i++) { + ret = ocfs2_xattr_set_handle(handle, inode, di_bh, + OCFS2_XATTR_INDEX_SECURITY, + si->xattrs[i].name, + si->xattrs[i].value, + si->xattrs[i].value_len, 0, + xattr_ac, data_ac); + if (ret) + break; + } + + return ret; } const struct xattr_handler ocfs2_xattr_security_handler = { diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h index 887cc1a18b1a..1dd08f979e3c 100644 --- a/fs/ocfs2/xattr.h +++ b/fs/ocfs2/xattr.h @@ -20,13 +20,18 @@ enum ocfs2_xattr_type { OCFS2_XATTR_MAX }; -struct ocfs2_security_xattr_info { - int enable; - const char *name; +struct ocfs2_security_xattr { + char *name; void *value; size_t value_len; }; +struct ocfs2_security_xattr_info { + int enable; + struct ocfs2_security_xattr *xattrs; + int count; +}; + extern const struct xattr_handler ocfs2_xattr_user_handler; extern const struct xattr_handler ocfs2_xattr_trusted_handler; extern const struct xattr_handler ocfs2_xattr_security_handler; @@ -49,6 +54,7 @@ int ocfs2_xattr_remove(struct inode *, struct buffer_head *); int ocfs2_init_security_get(struct inode *, struct inode *, const struct qstr *, struct ocfs2_security_xattr_info *); +void ocfs2_free_security_xattrs(struct ocfs2_security_xattr_info *); int ocfs2_init_security_set(handle_t *, struct inode *, struct buffer_head *, struct ocfs2_security_xattr_info *, diff --git a/fs/proc/base.c b/fs/proc/base.c index 3dff299bfe1b..c9aebee0a338 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2520,17 +2520,23 @@ static int show_timer(struct seq_file *m, void *v) struct k_itimer *timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list); struct timers_private *tp = m->private; int notify = timer->it_sigev_notify; + pid_t nr = 0; guard(spinlock_irq)(&timer->it_lock); if (!posixtimer_valid(timer)) return 0; + if (timer->it_pid && pid_has_task(timer->it_pid, timer->it_pid_type)) + nr = pid_nr_ns(timer->it_pid, tp->ns); + else + notify = SIGEV_NONE; + seq_printf(m, "ID: %d\n", timer->it_id); seq_printf(m, "signal: %d/%px\n", timer->sigq.info.si_signo, timer->sigq.info.si_value.sival_ptr); seq_printf(m, "notify: %s/%s.%d\n", nstr[notify & ~SIGEV_THREAD_ID], (notify & SIGEV_THREAD_ID) ? "tid" : "pid", - pid_nr_ns(timer->it_pid, tp->ns)); + nr); seq_printf(m, "ClockID: %d\n", timer->it_clock); return 0; diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 58fd14c82270..5c74748a53f3 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h @@ -19,6 +19,13 @@ enum fault_flags { #include <linux/ratelimit.h> /* + * Length of the debugfs directory name embedded in struct fault_attr. + * Chosen to accommodate every in-tree caller of fault_create_debugfs_attr() + * (the longest is "fail_dma_array_full", 19 chars) with generous headroom. + */ +#define FAULT_ATTR_DNAME_LEN 64 + +/* * For explanation of the elements of this struct, see * Documentation/fault-injection/fault-injection.rst */ @@ -37,7 +44,7 @@ struct fault_attr { unsigned long count; struct ratelimit_state ratelimit_state; - struct dentry *dname; + char dname[FAULT_ATTR_DNAME_LEN]; }; #define FAULT_ATTR_INITIALIZER { \ @@ -47,7 +54,6 @@ struct fault_attr { .stacktrace_depth = 32, \ .ratelimit_state = RATELIMIT_STATE_INIT_DISABLED, \ .verbose = 2, \ - .dname = NULL, \ } #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER diff --git a/include/linux/kdev_t.h b/include/linux/kdev_t.h index 4856706fbfeb..2dbbd47f1e68 100644 --- a/include/linux/kdev_t.h +++ b/include/linux/kdev_t.h @@ -9,7 +9,7 @@ #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) -#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) +#define MKDEV(ma, mi) (((dev_t)(ma) << MINORBITS) | (mi)) #define print_dev_t(buffer, dev) \ sprintf((buffer), "%u:%u\n", MAJOR(dev), MINOR(dev)) diff --git a/include/linux/minmax.h b/include/linux/minmax.h index a0158db54a04..5ef4d58c0c42 100644 --- a/include/linux/minmax.h +++ b/include/linux/minmax.h @@ -38,9 +38,9 @@ * Note that 'x' is the original expression, and 'ux' is the unique variable * that contains the value. * - * We use 'ux' for pure type checking, and 'x' for when we need to look at the - * value (but without evaluating it for side effects! - * Careful to only ever evaluate it with sizeof() or __builtin_constant_p() etc). + * We use 'ux' for both the type and the value checks, so 'x' itself is only + * expanded twice: once to initialise 'ux', and once quoted in the error + * message. * * Pointers end up being checked by the normal C type rules at the actual * comparison, and these expressions only need to be careful to not cause diff --git a/include/linux/panic.h b/include/linux/panic.h index 8e7250b0e913..23976b1dfdb6 100644 --- a/include/linux/panic.h +++ b/include/linux/panic.h @@ -13,7 +13,8 @@ __printf(1, 2) void panic(const char *fmt, ...) __noreturn __cold; __printf(1, 0) void vpanic(const char *fmt, va_list args) __noreturn __cold; -void nmi_panic(struct pt_regs *regs, const char *msg); +__printf(2, 3) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...); void check_panic_on_warn(const char *origin); extern void oops_enter(void); extern void oops_exit(void); @@ -111,4 +112,6 @@ extern void add_taint(unsigned flag, enum lockdep_ok); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); +void arch_do_panic(void); + #endif /* _LINUX_PANIC_H */ diff --git a/init/Makefile b/init/Makefile index d6f75d8907e0..9102fc34c0cc 100644 --- a/init/Makefile +++ b/init/Makefile @@ -6,10 +6,10 @@ ccflags-y := -fno-function-sections -fno-data-sections obj-y := main.o version.o mounts.o -ifneq ($(CONFIG_BLK_DEV_INITRD),y) -obj-y += noinitramfs.o +ifeq ($(CONFIG_BLK_DEV_INITRD),y) +obj-y += initramfs.o else -obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o +obj-y += noinitramfs.o endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o obj-$(CONFIG_INITRAMFS_TEST) += initramfs_test.o diff --git a/init/main.c b/init/main.c index f6b906ca782f..d7bf1ffc5f50 100644 --- a/init/main.c +++ b/init/main.c @@ -215,10 +215,6 @@ static bool __init obsolete_checksetup(char *line) * params and __setups of same names 8( */ if (line[n] == '\0' || line[n] == '=') had_early_param = true; - } else if (!p->setup_func) { - pr_warn("Parameter %s is obsolete, ignored\n", - p->str); - return true; } else if (p->setup_func(line + n)) return true; } @@ -609,12 +605,12 @@ static int __init unknown_bootoption(char *param, char *val, /* Environment option */ unsigned int i; for (i = 0; envp_init[i]; i++) { + if (!strncmp(param, envp_init[i], len+1)) + break; if (i == MAX_INIT_ENVS) { panic_later = "env"; panic_param = param; } - if (!strncmp(param, envp_init[i], len+1)) - break; } envp_init[i] = param; } else { @@ -642,7 +638,7 @@ static int __init init_setup(char *str) * the shell think it should execute a script with such name. * So we ignore all arguments entered _before_ init=... [MJ] */ - for (i = 1; i < MAX_INIT_ARGS; i++) + for (i = 1; i <= MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } @@ -655,7 +651,7 @@ static int __init rdinit_setup(char *str) ramdisk_execute_command = str; ramdisk_execute_command_set = true; /* See "auto" comment in init_setup */ - for (i = 1; i < MAX_INIT_ARGS; i++) + for (i = 1; i <= MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } diff --git a/init/version.c b/init/version.c index 94c96f6fbfe6..cbae11b6f768 100644 --- a/init/version.c +++ b/init/version.c @@ -21,13 +21,14 @@ static int __init early_hostname(char *arg) { size_t bufsize = sizeof(init_uts_ns.name.nodename); size_t maxlen = bufsize - 1; - ssize_t arglen; - arglen = strscpy(init_uts_ns.name.nodename, arg, bufsize); - if (arglen < 0) { - pr_warn("hostname parameter exceeds %zd characters and will be truncated", + if (!arg) + return -EINVAL; + + if (strscpy(init_uts_ns.name.nodename, arg, bufsize) < 0) + pr_warn("hostname parameter exceeds %zu characters and will be truncated\n", maxlen); - } + return 0; } early_param("hostname", early_hostname); diff --git a/kernel/fork.c b/kernel/fork.c index 2142c6f38119..18b6dd851642 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -858,7 +858,8 @@ void __init fork_init(void) #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN 0 #endif - int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN); + int align = max3(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN, + __alignof__(struct task_struct)); unsigned long useroffset, usersize; /* create a slab on which task_structs can be allocated */ diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 1d19b1be207a..764918570de1 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -529,7 +529,7 @@ static void init_node(struct gcov_node *node, struct gcov_info *info, } node->parent = parent; if (name) - strcpy(node->name, name); + strscpy(node->name, name, strlen(name) + 1); } /* diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 6fcc94ce4ca9..b8554aedc589 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -57,8 +57,20 @@ unsigned long __read_mostly sysctl_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_ */ static unsigned long __read_mostly sysctl_hung_task_check_interval_secs; +/* + * Limit the number of printed hung tasks to prevent printing + * the same or similar backtraces repeatedly. + */ static int __read_mostly sysctl_hung_task_warnings = 10; +/* + * The number of hung tasks which still can be reported. + * The budget gets restored to the original limit when + * the previous stall is resolved. + */ +static int hung_task_warnings_budget = 10; +static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0); + static int __read_mostly did_panic; static bool hung_task_call_panic; @@ -245,11 +257,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, /* * The given task did not get scheduled for more than * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain - * accordingly + * accordingly with full details if the budget is not exhausted. */ - if (sysctl_hung_task_warnings || hung_task_call_panic) { - if (sysctl_hung_task_warnings > 0) - sysctl_hung_task_warnings--; + if (hung_task_warnings_budget || hung_task_call_panic) { + if (hung_task_warnings_budget > 0) + hung_task_warnings_budget--; pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n", t->comm, t->pid, t->in_iowait ? " in I/O wait" : "", (jiffies - t->last_switch_time) / HZ); @@ -264,8 +276,8 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, sched_show_task(t); debug_show_blocker(t, timeout); - if (!sysctl_hung_task_warnings) - pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n"); + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: further per-task details suppressed until warning budget is reset (see sysctl kernel.hung_task_warnings)\n"); } touch_nmi_watchdog(); @@ -304,7 +316,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unsigned long last_break = jiffies; struct task_struct *g, *t; unsigned long this_round_count; - int need_warning = sysctl_hung_task_warnings; + int need_warning; unsigned long si_mask = hung_task_si_mask; /* @@ -314,6 +326,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) if (test_taint(TAINT_DIE) || did_panic) return; + if (atomic_xchg_acquire(&reset_hung_task_warnings, 0)) + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); + need_warning = hung_task_warnings_budget; + this_round_count = 0; rcu_read_lock(); for_each_process_thread(g, t) { @@ -340,8 +357,15 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unlock: rcu_read_unlock(); - if (!this_round_count) + if (!this_round_count) { + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); return; + } + + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: %lu hung tasks detected, warning budget exhausted\n", + this_round_count); if (need_warning || hung_task_call_panic) { si_mask |= SYS_INFO_LOCKS; @@ -425,6 +449,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ return ret; } +static int proc_dohung_task_warnings(const struct ctl_table *table, int write, + void *buffer, + size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!ret && write) + atomic_set_release(&reset_hung_task_warnings, 1); + + return ret; +} + /* * This is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs * and hung_task_check_interval_secs @@ -480,7 +517,7 @@ static const struct ctl_table hung_task_sysctls[] = { .data = &sysctl_hung_task_warnings, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dohung_task_warnings, .extra1 = SYSCTL_NEG_ONE, }, { diff --git a/kernel/kcov.c b/kernel/kcov.c index 35420f0ac524..9402227bd7ba 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -198,6 +198,16 @@ static notrace unsigned long canonicalize_ip(unsigned long ip) return ip; } +#ifdef CONFIG_KCOV_SELFTEST +static unsigned long __init decanonicalize_ip(unsigned long ip) +{ +#ifdef CONFIG_RANDOMIZE_BASE + ip += kaslr_offset(); +#endif + return ip; +} +#endif + /* * Entry point from instrumented code. * This is called once per basic-block/edge. @@ -253,6 +263,14 @@ static void notrace write_comp_data(u64 type, u64 arg1, u64 arg2, u64 ip) count = READ_ONCE(area[0]); + /* + * area[0] is writable by the collecting process, so count cannot be + * trusted. Bound it to the records that fit, as kcov_move_area() + * does, so the end_pos multiply below cannot wrap past its check. + */ + if (count >= max_pos / (sizeof(u64) * KCOV_WORDS_PER_CMP)) + return; + /* Every record is KCOV_WORDS_PER_CMP 64-bit words. */ start_index = 1 + count * KCOV_WORDS_PER_CMP; end_pos = (start_index + KCOV_WORDS_PER_CMP) * sizeof(u64); @@ -1099,9 +1117,11 @@ struct kcov_common_handle_id kcov_common_handle(void) EXPORT_SYMBOL(kcov_common_handle); #ifdef CONFIG_KCOV_SELFTEST +static unsigned long selftest_area[16] __initdata; + static void __init selftest(void) { - unsigned long start; + unsigned long start, i, ip, count; pr_err("running self test\n"); /* @@ -1111,15 +1131,26 @@ static void __init selftest(void) * leaks out of that section and leads to spurious coverage. * It's hard to call the actual interrupt handler directly, * so we just loop here for a bit waiting for a timer interrupt. - * We set kcov_mode to enable tracing, but don't setup the area, - * so any attempt to trace will crash. Note: we must not call any + * A small coverage area records the PCs of any leaks. The first word + * holds the count, leaving room for 15 PCs. Note: we must not call any * potentially traced functions in this region. */ + kcov_start(current, NULL, ARRAY_SIZE(selftest_area), + selftest_area, KCOV_MODE_TRACE_PC, 0); start = jiffies; - WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) - ; - WRITE_ONCE(current->kcov_mode, 0); + cpu_relax(); + kcov_stop(current); + + count = READ_ONCE(selftest_area[0]); + if (count) { + pr_err("spurious coverage detected during interrupt selftest:\n"); + for (i = 1; i <= count; i++) { + ip = decanonicalize_ip(selftest_area[i]); + pr_err(" %pB\n", (void *)ip); + } + panic("kcov: interrupt selftest detected spurious coverage"); + } pr_err("done running self test\n"); } #endif diff --git a/kernel/panic.c b/kernel/panic.c index a4dbb6b8cd2b..b824b68fcb08 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -307,7 +307,7 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); atomic_t panic_redirect_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); #if defined(CONFIG_SMP) && defined(CONFIG_CRASH_DUMP) -static char *panic_force_buf; +static char panic_force_buf[PANIC_MSG_BUFSZ]; static int __init panic_force_cpu_setup(char *str) { @@ -326,17 +326,6 @@ static int __init panic_force_cpu_setup(char *str) } early_param("panic_force_cpu", panic_force_cpu_setup); -static int __init panic_force_cpu_late_init(void) -{ - if (panic_force_cpu < 0) - return 0; - - panic_force_buf = kmalloc(PANIC_MSG_BUFSZ, GFP_KERNEL); - - return 0; -} -late_initcall(panic_force_cpu_late_init); - static void do_panic_on_target_cpu(void *info) { panic("%s", (char *)info); @@ -371,15 +360,16 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg) * for the crash kernel to function correctly. This function redirects * panic handling to the CPU specified via the panic_force_cpu= boot parameter. * - * Returns false if panic should proceed on current CPU. - * Returns true if panic was redirected. + * Returns true when this CPU must stop: the panic was redirected or is + * already running on another CPU. + * Returns false when panic() should proceed on this CPU. */ __printf(1, 0) static bool panic_try_force_cpu(const char *fmt, va_list args) { int this_cpu = raw_smp_processor_id(); int old_cpu = PANIC_CPU_INVALID; - const char *msg; + va_list ap; /* Feature not enabled via boot parameter */ if (panic_force_cpu < 0) @@ -396,27 +386,27 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) return false; } - /* Another panic already in progress */ + /* + * Don't redirect when a panic is already in progress. Stop this + * CPU when it's another one, proceed when it's this one. + */ if (panic_in_progress()) - return false; + return panic_on_other_cpu(); /* - * Only one CPU can do the redirect. Use atomic cmpxchg to ensure - * we don't race with another CPU also trying to redirect. + * Only one CPU can do the redirection. Others should go offline. + * Continue with panic() when we already tried the redirection + * from this CPU before, for example via nmi_panic(). */ if (!atomic_try_cmpxchg(&panic_redirect_cpu, &old_cpu, this_cpu)) - return false; + return old_cpu != this_cpu; /* - * Use dynamically allocated buffer if available, otherwise - * fall back to static message for early boot panics or allocation failure. + * Do not consume args, the caller reuses them if we fail. */ - if (panic_force_buf) { - vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, args); - msg = panic_force_buf; - } else { - msg = "Redirected panic (buffer unavailable)"; - } + va_copy(ap, args); + vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); + va_end(ap); console_verbose(); bust_spinlocks(1); @@ -431,7 +421,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) dump_stack(); } - if (panic_smp_redirect_cpu(panic_force_cpu, (void *)msg) != 0) { + if (panic_smp_redirect_cpu(panic_force_cpu, panic_force_buf) != 0) { atomic_set(&panic_redirect_cpu, PANIC_CPU_INVALID); pr_warn("panic: failed to redirect to CPU %d, continuing on CPU %d\n", panic_force_cpu, this_cpu); @@ -503,17 +493,35 @@ bool panic_on_other_cpu(void) EXPORT_SYMBOL(panic_on_other_cpu); /* - * A variant of panic() called from NMI context. We return if we've already - * panicked on this CPU. If another CPU already panicked, loop in - * nmi_panic_self_stop() which can provide architecture dependent code such - * as saving register state for crash dump. + * A variant of panic() called from NMI context. The panic is first + * redirected to the CPU requested via panic_force_cpu=, when configured. + * We return if we've already panicked on this CPU. If another CPU already + * panicked, loop in nmi_panic_self_stop() which can provide architecture + * dependent code for saving register state for crash dump. */ -void nmi_panic(struct pt_regs *regs, const char *msg) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + + /* Try to redirect to the requested CPU before claiming panic_cpu. */ + if (panic_try_force_cpu(fmt, args)) { + /* + * Mark ourselves offline so panic_other_cpus_shutdown() won't + * wait for us on architectures that check num_online_cpus(). + */ + set_cpu_online(raw_smp_processor_id(), false); + nmi_panic_self_stop(regs); + } + if (panic_try_start()) - panic("%s", msg); - else if (panic_on_other_cpu()) + vpanic(fmt, args); + + if (panic_on_other_cpu()) nmi_panic_self_stop(regs); + + va_end(args); } EXPORT_SYMBOL(nmi_panic); @@ -567,6 +575,8 @@ static void panic_other_cpus_shutdown(bool crash_kexec) crash_smp_send_stop(); } +void __weak arch_do_panic(void) {} + /** * vpanic - halt the system * @fmt: The text string to print @@ -742,20 +752,9 @@ void vpanic(const char *fmt, va_list args) reboot_mode = panic_reboot_mode; emergency_restart(); } -#ifdef __sparc__ - { - extern int stop_a_enabled; - /* Make sure the user can actually press Stop-A (L1-A) */ - stop_a_enabled = 1; - pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" - "twice on console to return to the boot prom\n"); - } -#endif -#if defined(CONFIG_S390) - disabled_wait(); -#endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); + arch_do_panic(); /* Do not scroll important messages printed above */ suppress_printk = 1; @@ -1227,14 +1226,9 @@ static int panic_print_set(const char *val, const struct kernel_param *kp) return param_set_ulong(val, kp); } -static int panic_print_get(char *val, const struct kernel_param *kp) -{ - return param_get_ulong(val, kp); -} - static const struct kernel_param_ops panic_print_ops = { .set = panic_print_set, - .get = panic_print_get, + .get = param_get_ulong, }; __core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644); diff --git a/kernel/profile.c b/kernel/profile.c index 1fcf1adcf4eb..e7defa90a34c 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -156,7 +156,7 @@ read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos) unsigned long p = *ppos; ssize_t read; char *pnt; - unsigned long sample_step = 1UL << prof_shift; + unsigned int sample_step = (unsigned int)(1UL << prof_shift); if (p >= (prof_len+1)*sizeof(unsigned int)) return 0; diff --git a/kernel/reboot.c b/kernel/reboot.c index a93c23b9e194..9a84401adde5 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -813,8 +813,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: - ret = hibernate(); - break; + mutex_unlock(&system_transition_mutex); + return hibernate(); #endif default: diff --git a/kernel/resource.c b/kernel/resource.c index 08bd7579cf40..0c315c917fbb 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1350,8 +1350,8 @@ static int __request_region_locked(struct resource *res, struct resource *parent } if (conflict->flags & flags & IORESOURCE_MUXED) { add_wait_queue(&muxed_resource_wait, &wait); - write_unlock(&resource_lock); set_current_state(TASK_UNINTERRUPTIBLE); + write_unlock(&resource_lock); schedule(); remove_wait_queue(&muxed_resource_wait, &wait); write_lock(&resource_lock); @@ -1838,7 +1838,7 @@ __setup("reserve=", reserve_setup); */ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) { - resource_size_t end = addr + size - 1; + struct resource mem = DEFINE_RES_MEM(addr, size); struct resource *p; int err = 0; @@ -1848,12 +1848,10 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) * We can probably skip the resources without * IORESOURCE_IO attribute? */ - if (p->start > end) - continue; - if (p->end < addr) + if (!resource_overlaps(p, &mem)) continue; - if (PFN_DOWN(p->start) <= PFN_DOWN(addr) && - PFN_DOWN(p->end) >= PFN_DOWN(end)) + if (PFN_DOWN(p->start) <= PFN_DOWN(mem.start) && + PFN_DOWN(p->end) >= PFN_DOWN(mem.end)) continue; /* * if a resource is "BUSY", it's not a hardware resource @@ -1864,8 +1862,8 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) if (p->flags & IORESOURCE_BUSY) continue; - pr_debug("resource sanity check: requesting [mem %pa-%pa], which spans more than %s %pR\n", - &addr, &end, p->name, p); + pr_debug("resource sanity check: requesting %pR, which spans more than %s %pR\n", + &mem, p->name, p); err = -1; break; } diff --git a/kernel/resource_kunit.c b/kernel/resource_kunit.c index 42785796f1db..9eeb2b1a85c0 100644 --- a/kernel/resource_kunit.c +++ b/kernel/resource_kunit.c @@ -225,6 +225,9 @@ static void resource_test_region_intersects(struct kunit *test) struct resource *parent; resource_size_t start; + if (!IS_ENABLED(CONFIG_GET_FREE_REGION)) + kunit_skip(test, "CONFIG_GET_FREE_REGION is disabled"); + /* Find an iomem_resource hole to hold test resources */ parent = alloc_free_mem_region(&iomem_resource, RES_TEST_TOTAL_SIZE, SZ_1M, "test resources"); diff --git a/kernel/sys.c b/kernel/sys.c index 35b538ba843c..755bfa5ccd92 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1077,6 +1077,9 @@ SYSCALL_DEFINE1(times, struct tms __user *, tbuf) #ifdef CONFIG_COMPAT static compat_clock_t clock_t_to_compat_clock_t(clock_t x) { + if (USER_HZ == COMPAT_USER_HZ) + return x; + return compat_jiffies_to_clock_t(clock_t_to_jiffies(x)); } diff --git a/kernel/taskstats.c b/kernel/taskstats.c index f31df72f0e9d..bf517788b840 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -59,8 +59,7 @@ static DEFINE_PER_CPU(struct listener_list, listener_array); enum actions { REGISTER, - DEREGISTER, - CPU_DONT_CARE + DEREGISTER }; static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, @@ -473,7 +472,8 @@ static size_t taskstats_packet_size(void) return size; } -static int cmd_attr_pid(struct genl_info *info) +static int cmd_attr_pid_tgid(struct genl_info *info, int attr, + int (*fill)(pid_t, struct taskstats *)) { struct taskstats *stats; struct sk_buff *rep_skb; @@ -488,41 +488,15 @@ static int cmd_attr_pid(struct genl_info *info) return rc; rc = -EINVAL; - pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); + pid = nla_get_u32(info->attrs[attr]); + stats = mk_reply(rep_skb, + attr == TASKSTATS_CMD_ATTR_PID + ? TASKSTATS_TYPE_PID : TASKSTATS_TYPE_TGID, + pid); if (!stats) goto err; - rc = fill_stats_for_pid(pid, stats); - if (rc < 0) - goto err; - return send_reply(rep_skb, info); -err: - nlmsg_free(rep_skb); - return rc; -} - -static int cmd_attr_tgid(struct genl_info *info) -{ - struct taskstats *stats; - struct sk_buff *rep_skb; - size_t size; - u32 tgid; - int rc; - - size = taskstats_packet_size(); - - rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); - if (rc < 0) - return rc; - - rc = -EINVAL; - tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); - if (!stats) - goto err; - - rc = fill_stats_for_tgid(tgid, stats); + rc = fill(pid, stats); if (rc < 0) goto err; return send_reply(rep_skb, info); @@ -542,9 +516,11 @@ static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, DEREGISTER); else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) - return cmd_attr_pid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_PID, + fill_stats_for_pid); else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) - return cmd_attr_tgid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_TGID, + fill_stats_for_tgid); else return -EINVAL; } diff --git a/kernel/watchdog.c b/kernel/watchdog.c index e5134ad7b663..64da2e500144 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -255,6 +255,9 @@ void watchdog_hardlockup_check(unsigned int cpu, struct pt_regs *regs) return; } + if (hardlockup_panic) + console_verbose(); + /* * NOTE: we call printk_cpu_sync_get_irqsave() after printing * the lockup message. While it would be nice to serialize @@ -880,6 +883,10 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* Start period for the next softlockup warning. */ update_report_ts(); + thresh_count = duration / get_softlockup_thresh(); + if (softlockup_panic && thresh_count >= softlockup_panic) + console_verbose(); + printk_cpu_sync_get_irqsave(flags); pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, @@ -901,7 +908,6 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK); sys_info(softlockup_si_mask & ~SYS_INFO_ALL_BT); - thresh_count = duration / get_softlockup_thresh(); if (softlockup_panic && thresh_count >= softlockup_panic) panic("softlockup: hung tasks"); diff --git a/kernel/watchdog_perf.c b/kernel/watchdog_perf.c index cf05775a96d3..a4f677c16b20 100644 --- a/kernel/watchdog_perf.c +++ b/kernel/watchdog_perf.c @@ -301,10 +301,10 @@ void __init hardlockup_config_perf_event(const char *str) } else { unsigned int len = comma - str; - if (len > sizeof(buf)) + if (len >= sizeof(buf)) return; - strscpy(buf, str, len); + strscpy(buf, str, len + 1); if (kstrtoull(buf, 16, &config)) return; } diff --git a/lib/Kconfig b/lib/Kconfig index 4e6b34c3346d..1e42f5167c68 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -219,29 +219,29 @@ source "lib/xz/Kconfig" # config DECOMPRESS_GZIP select ZLIB_INFLATE - tristate + bool config DECOMPRESS_BZIP2 - tristate + bool config DECOMPRESS_LZMA - tristate + bool config DECOMPRESS_XZ select XZ_DEC - tristate + bool config DECOMPRESS_LZO select LZO_DECOMPRESS - tristate + bool config DECOMPRESS_LZ4 select LZ4_DECOMPRESS - tristate + bool config DECOMPRESS_ZSTD select ZSTD_DECOMPRESS - tristate + bool # # Generic allocator support is selected if needed diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6759087154ed..2d22638ec13a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2810,7 +2810,6 @@ config RESOURCE_KUNIT_TEST tristate "KUnit test for resource API" if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS - select GET_FREE_REGION help This builds the resource API unit test. Tests the logic of API provided by resource.c and ioport.h. @@ -2831,6 +2830,21 @@ config SYSCTL_KUNIT_TEST If unsure, say N. +config ERRSEQ_KUNIT_TEST + tristate "KUnit test for errseq" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This builds the errseq KUnit test suite. + It tests the documented semantics of the errseq_t error-tracking + infrastructure (lib/errseq.c), which underpins writeback error + reporting. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config KFIFO_KUNIT_TEST tristate "KUnit Test for the generic kernel FIFO implementation" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/cmdline.c b/lib/cmdline.c index 16cce6621cec..40b98d943a9a 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -11,6 +11,7 @@ #include <linux/export.h> #include <linux/kernel.h> +#include <linux/overflow.h> #include <linux/string.h> #include <linux/ctype.h> @@ -26,7 +27,9 @@ static int get_range(char **str, int *pint, int n) (*str)++; upper_range = simple_strtol((*str), NULL, 0); - inc_counter = upper_range - *pint; + /* Keep the sign of the result when the difference doesn't fit */ + if (check_sub_overflow(upper_range, *pint, &inc_counter)) + inc_counter = upper_range < *pint ? -1 : INT_MAX; for (x = *pint; n && x < upper_range; x++, n--) *pint++ = x; return inc_counter; @@ -122,6 +125,14 @@ char *get_options(const char *str, int nints, int *ints) range_nums = get_range((char **)&str, pint, n); if (range_nums < 0) break; + /* The range didn't fit, so the array is full */ + if (!validate && range_nums > n) { + i = nints; + break; + } + /* Leave room for the upper number of the range */ + if (range_nums >= INT_MAX - i) + break; /* * Decrement the result by one to leave out the * last number in the range. The next iteration diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c index 1288f146661f..a9236d3e9678 100644 --- a/lib/decompress_bunzip2.c +++ b/lib/decompress_bunzip2.c @@ -428,6 +428,10 @@ got_huff_bits: t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */ + /* Bound the run so t and runPos cannot overflow. */ + if (t >= dbufSize) + return RETVAL_DATA_ERROR; + runPos <<= 1; continue; } @@ -439,7 +443,7 @@ got_huff_bits: array.) */ if (runPos) { runPos = 0; - if (dbufCount+t >= dbufSize) + if (dbufCount+t > dbufSize) return RETVAL_DATA_ERROR; uc = symToByte[mtfSymbol[0]]; diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c index c0dbb3cea915..86e9aaec04f6 100644 --- a/lib/decompress_unlz4.c +++ b/lib/decompress_unlz4.c @@ -139,6 +139,10 @@ STATIC inline int INIT unlz4(u8 *input, long in_len, if (!fill) { inp += 4; size -= 4; + if (chunksize > size) { + error("data corrupted"); + goto exit_2; + } } else { if (chunksize > LZ4_compressBound(uncomp_chunksize)) { error("chunk length is longer than allocated"); diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c index 05d5cb490a44..9ccded9934c6 100644 --- a/lib/decompress_unxz.c +++ b/lib/decompress_unxz.c @@ -342,13 +342,13 @@ STATIC int INIT unxz(unsigned char *in, long in_size, b.out_pos = 0; } } while (ret == XZ_OK); + } - if (must_free_in) - free(in); + if (must_free_in) + free(in); - if (flush != NULL) - free(b.out); - } + if (flush != NULL) + free(b.out); if (in_used != NULL) *in_used += b.in_pos; diff --git a/lib/fault-inject.c b/lib/fault-inject.c index 999053fa133e..02916ef2761c 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -5,6 +5,7 @@ #include <linux/debugfs.h> #include <linux/sched.h> #include <linux/stat.h> +#include <linux/string.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/export.h> @@ -64,7 +65,7 @@ static void fail_dump(struct fault_attr *attr) { if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) { printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n" - "name %pd, interval %lu, probability %lu, " + "name %s, interval %lu, probability %lu, " "space %d, times %d\n", attr->dname, attr->interval, attr->probability, atomic_read(&attr->space), @@ -261,7 +262,9 @@ struct dentry *fault_create_debugfs_attr(const char *name, debugfs_create_xul("reject-end", mode, dir, &attr->reject_end); #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ - attr->dname = dget(dir); + if (strscpy(attr->dname, name, sizeof(attr->dname)) == -E2BIG) + pr_warn("FAULT_INJECTION: name '%s' truncated to '%s'\n", + name, attr->dname); return dir; } EXPORT_SYMBOL_GPL(fault_create_debugfs_attr); diff --git a/lib/klist.c b/lib/klist.c index 332a4fbf18ff..f133740b1c2c 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -36,6 +36,7 @@ #include <linux/klist.h> #include <linux/export.h> #include <linux/sched.h> +#include <linux/sched/task.h> /* * Use the lowest bit of n_klist to mark deleted nodes and exclude @@ -187,18 +188,24 @@ static void klist_release(struct kref *kref) WARN_ON(!knode_dead(n)); list_del(&n->n_node); + knode_set_klist(n, NULL); spin_lock(&klist_remove_lock); list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) { + struct task_struct *p; + if (waiter->node != n) continue; + p = waiter->process; + get_task_struct(p); list_del(&waiter->list); - waiter->woken = 1; + /* Publish only after the final waiter and n accesses */ + smp_store_release(&waiter->woken, 1); mb(); - wake_up_process(waiter->process); + wake_up_process(p); + put_task_struct(p); } spin_unlock(&klist_remove_lock); - knode_set_klist(n, NULL); } static int klist_dec_and_del(struct klist_node *n) @@ -250,7 +257,8 @@ void klist_remove(struct klist_node *n) for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); - if (waiter.woken) + /* Pairs with the release store in klist_release() */ + if (smp_load_acquire(&waiter.woken)) break; schedule(); } diff --git a/lib/kstrtox.c b/lib/kstrtox.c index bac1c057e1b0..1b3f0d3fff37 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -69,7 +69,7 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon unsigned int lc = _tolower(c); unsigned int val; - if ('0' <= c && c <= '9') + if (isdigit(c)) val = c - '0'; else if ('a' <= lc && lc <= 'f') val = lc - 'a' + 10; diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 2891f94a11c6..87b261b87b0b 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -328,6 +328,7 @@ bool __percpu_counter_limited_add(struct percpu_counter *fbc, s64 limit, s64 amount, s32 batch) { s64 count; + s64 gcount; s64 unknown; unsigned long flags; bool good = false; @@ -338,11 +339,16 @@ bool __percpu_counter_limited_add(struct percpu_counter *fbc, local_irq_save(flags); unknown = batch * num_online_cpus(); count = __this_cpu_read(*fbc->counters); + /* + * Lockless on purpose: gcount may be stale, so this is only an + * approximation, bounded by the per-cpu slack ("unknown"). + */ + gcount = data_race(READ_ONCE(fbc->count)); /* Skip taking the lock when safe */ if (abs(count + amount) <= batch && - ((amount > 0 && fbc->count + unknown <= limit) || - (amount < 0 && fbc->count - unknown >= limit))) { + ((amount > 0 && gcount + unknown <= limit) || + (amount < 0 && gcount - unknown >= limit))) { this_cpu_add(*fbc->counters, amount); local_irq_restore(flags); return true; diff --git a/lib/plist.c b/lib/plist.c index a5bef38add43..b0273533c04d 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -174,8 +174,15 @@ void plist_requeue(struct plist_node *node, struct plist_head *head) /* * After plist_del(), iter is the replacement of the node. If the node * was on prio_list, take shortcut to find node_next instead of looping. + * + * prio_list is a headless ring, so from the LAST bucket ->next wraps + * round to the first one; in that case node_next is the list head. */ if (!list_empty(&iter->prio_list)) { + struct plist_node *first = plist_first(head); + + if (iter->prio_list.next == &first->prio_list) + goto queue; iter = list_entry(iter->prio_list.next, struct plist_node, prio_list); node_next = &iter->node_list; diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index 01f007b2522c..563a178aa930 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -32,6 +32,7 @@ config XOR_KUNIT_TEST config XOR_BENCHMARK bool "Benchmark for xor_gen" depends on XOR_KUNIT_TEST + default y if KUNIT_ALL_TESTS=m help Include benchmarks in the KUnit test suite for xor_gen. @@ -63,6 +64,7 @@ config RAID6_PQ_KUNIT_TEST config RAID6_PQ_KUNIT_BENCHMARK bool "Benchmark for RAID6 PQ" depends on RAID6_PQ_KUNIT_TEST + default y if KUNIT_ALL_TESTS=m help Include benchmarks in the KUnit test suite for raid P/Q generation. diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 7d829c669ea7..3cc2fe7ac42c 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -67,6 +67,7 @@ static void raid6_avx21_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -115,6 +116,7 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -175,6 +177,7 @@ static void raid6_avx22_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -243,6 +246,7 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -334,6 +338,7 @@ static void raid6_avx24_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -444,6 +449,7 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, asm volatile("vmovntdq %%ymm14,%0" : "=m" (q[d+96])); } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index e671eb5bde63..772bfc4af6df 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -78,6 +78,7 @@ static void raid6_avx5121_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -137,6 +138,7 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -208,6 +210,7 @@ static void raid6_avx5122_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -292,6 +295,7 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -396,6 +400,7 @@ static void raid6_avx5124_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -529,6 +534,7 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, "m" (q[d+128]), "m" (q[d+192])); } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } const struct raid6_calls raid6_avx512x4 = { diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index a714a780a2d8..820871046e30 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -176,6 +176,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -293,6 +294,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index ec72d5a30c01..299a3f044d61 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -211,6 +211,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -353,6 +354,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/xor/x86/xor-avx.c b/lib/raid/xor/x86/xor-avx.c index f7777d7aa269..95b21e7225e8 100644 --- a/lib/raid/xor/x86/xor-avx.c +++ b/lib/raid/xor/x86/xor-avx.c @@ -147,6 +147,7 @@ static void xor_gen_avx(void *dest, void **srcs, unsigned int src_cnt, { kernel_fpu_begin(); xor_gen_avx_inner(dest, srcs, src_cnt, bytes); + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/stackdepot.c b/lib/stackdepot.c index dd2717ff94bf..90c52f2e0d3f 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -323,7 +323,7 @@ static bool depot_init_pool(void **prealloc) * NULL; do not reset to NULL if we have reached the maximum number of * pools. */ - if (pools_num < stack_max_pools) + if (pools_num + 1 < stack_max_pools) WRITE_ONCE(new_pool, NULL); else WRITE_ONCE(new_pool, STACK_DEPOT_POISON); diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 5f6cf654a128..df0537e1b91f 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o +obj-$(CONFIG_ERRSEQ_KUNIT_TEST) += errseq_kunit.o obj-$(CONFIG_FFS_KUNIT_TEST) += ffs_kunit.o CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced) CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread) diff --git a/lib/tests/cmdline_kunit.c b/lib/tests/cmdline_kunit.c index 3f61ff8d3178..12b85f255b65 100644 --- a/lib/tests/cmdline_kunit.c +++ b/lib/tests/cmdline_kunit.c @@ -140,6 +140,44 @@ static void cmdline_test_range(struct kunit *test) } while (++i < ARRAY_SIZE(cmdline_test_range_strings)); } +static const struct { + const char *in; + int parsed[4]; + int validated; +} cmdline_test_range_overflow_cases[] = { + { "1-100", { 3, 1, 2, 3, }, 100, }, + { "1,5-100,7", { 3, 1, 5, 6, }, 98, }, + { "1-2147483646", { 3, 1, 2, 3, }, 2147483646, }, + { "0-2147483647", { 3, 0, 1, 2, }, 0, }, + { "-5-2147483647", { 3, -5, -4, -3, }, 0, }, + { "2147483647--5", { 0, 2147483647, }, 0, }, + { "1,0-2147483647", { 3, 1, 0, 1, }, 1, }, +}; + +static void cmdline_test_range_overflow(struct kunit *test) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(cmdline_test_range_overflow_cases); i++) { + const char *in = cmdline_test_range_overflow_cases[i].in; + const int *e = cmdline_test_range_overflow_cases[i].parsed; + /* Two guard elements past the array handed to get_options() */ + int r[ARRAY_SIZE(cmdline_test_range_overflow_cases[0].parsed) + 2]; + int n; + + memset(r, 0, sizeof(r)); + get_options(in, ARRAY_SIZE(r) - 2, r); + for (j = 0; j < ARRAY_SIZE(r) - 2; j++) + KUNIT_EXPECT_EQ_MSG(test, r[j], e[j], "Pattern: %s at %u", in, j); + for (; j < ARRAY_SIZE(r); j++) + KUNIT_EXPECT_EQ_MSG(test, r[j], 0, "Pattern: %s out of bound at %u", in, j); + + get_options(in, 0, &n); + KUNIT_EXPECT_EQ_MSG(test, n, cmdline_test_range_overflow_cases[i].validated, + "Pattern: %s (validated)", in); + } +} + static void cmdline_test_next_arg_quoted_value(struct kunit *test) { char in[] = "foo=\"bar baz\" qux=1"; @@ -258,6 +296,7 @@ static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_lead_int), KUNIT_CASE(cmdline_test_tail_int), KUNIT_CASE(cmdline_test_range), + KUNIT_CASE(cmdline_test_range_overflow), KUNIT_CASE(cmdline_test_next_arg_quoted_value), KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression), KUNIT_CASE(cmdline_test_next_arg_mixed_tokens), diff --git a/lib/tests/errseq_kunit.c b/lib/tests/errseq_kunit.c new file mode 100644 index 000000000000..8f39ebc4a248 --- /dev/null +++ b/lib/tests/errseq_kunit.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for the errseq_t error-tracking infrastructure. + * + * These exercise the documented single-threaded semantics of the errseq + * API (see Documentation/core-api/errseq.rst and lib/errseq.c): error + * recording and overwriting, the "seen" handoff between errseq_sample() + * and errseq_check_and_advance(), and the re-reporting of an error that + * is recorded again after it has been seen. + * + * The lockless properties of errseq_t under concurrent updates are + * outside the scope of these deterministic tests, as is the WARN path + * for invalid error values. + */ +#include <kunit/test.h> + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/errseq.h> + +/* + * A zeroed errseq_t is the "no error has ever occurred" epoch: it + * samples as zero and no check against it reports anything. + */ +static void errseq_test_zero_epoch_reports_no_error(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + + KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), 0); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); + KUNIT_EXPECT_EQ(test, since, 0); +} + +static void errseq_test_set_records_error(struct kunit *test) +{ + errseq_t eseq = 0; + + /* errseq_set() returns the previous value; the epoch is zero. */ + KUNIT_EXPECT_EQ(test, errseq_set(&eseq, -EIO), 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -EIO); +} + +/* Any error set always overwrites an existing error. */ +static void errseq_test_set_overwrites_error(struct kunit *test) +{ + errseq_t eseq = 0; + + errseq_set(&eseq, -EIO); + errseq_set(&eseq, -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -ENOSPC); +} + +/* Both ends of the valid error range are recorded exactly. */ +static void errseq_test_errno_range_extremes(struct kunit *test) +{ + errseq_t lo = 0; + errseq_t hi = 0; + + errseq_set(&lo, -1); + KUNIT_EXPECT_EQ(test, errseq_check(&lo, 0), -1); + + errseq_set(&hi, -MAX_ERRNO); + KUNIT_EXPECT_EQ(test, errseq_check(&hi, 0), -MAX_ERRNO); +} + +/* + * An error nobody has seen yet samples as zero, so that a check against + * the sample still reports it (see commit b4678df184b3 ("errseq: Always + * report a writeback error once")). + */ +static void errseq_test_sample_of_unseen_error_is_zero(struct kunit *test) +{ + errseq_t eseq = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0); +} + +static void errseq_test_new_sampler_sees_unseen_error(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since; + + errseq_set(&eseq, -EIO); + since = errseq_sample(&eseq); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO); +} + +/* A given error is reported exactly once per advancing cursor. */ +static void errseq_test_check_and_advance_reports_once(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = errseq_sample(&eseq); + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); +} + +/* + * Once an error has been seen, a fresh sample is non-zero and checking + * against it reports nothing: handled errors do not reach new samplers. + */ +static void errseq_test_sample_after_seen_is_current(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t sample; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + sample = errseq_sample(&eseq); + KUNIT_EXPECT_NE(test, sample, 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, sample), 0); +} + +static void errseq_test_new_error_after_advance(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + errseq_set(&eseq, -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); +} + +/* + * Recording the same error again after it has been seen must bump the + * sequence, so cursors that consumed the first occurrence see the + * second one too. + */ +static void errseq_test_same_error_reported_again_after_seen(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t seen_cursor; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + seen_cursor = since; + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + /* The repeat must advance the sequence, not just re-toggle "seen". */ + KUNIT_EXPECT_NE(test, since, seen_cursor); +} + +/* + * A cursor that consumed an error must still observe a repeat of that + * error even when another cursor has already marked the repeat seen: + * recording over a seen value must advance the sequence. + */ +static void errseq_test_repeat_error_visible_to_all_cursors(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t cursor_a = 0; + errseq_t cursor_b = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0); +} + +/* An advance with no new error reports nothing and leaves the cursor put. */ +static void errseq_test_advance_stable_when_unchanged(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t cursor; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + cursor = since; + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); + KUNIT_EXPECT_EQ(test, since, cursor); +} + +/* + * Cursors are independent: one subscriber consuming an error does not + * consume it for another, and each subscriber sees each error once. + */ +static void errseq_test_two_subscribers_independent(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t cursor_a = errseq_sample(&eseq); + errseq_t cursor_b = errseq_sample(&eseq); + + errseq_set(&eseq, -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_b), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), 0); +} + +static struct kunit_case errseq_test_cases[] = { + KUNIT_CASE(errseq_test_zero_epoch_reports_no_error), + KUNIT_CASE(errseq_test_set_records_error), + KUNIT_CASE(errseq_test_set_overwrites_error), + KUNIT_CASE(errseq_test_errno_range_extremes), + KUNIT_CASE(errseq_test_sample_of_unseen_error_is_zero), + KUNIT_CASE(errseq_test_new_sampler_sees_unseen_error), + KUNIT_CASE(errseq_test_check_and_advance_reports_once), + KUNIT_CASE(errseq_test_sample_after_seen_is_current), + KUNIT_CASE(errseq_test_new_error_after_advance), + KUNIT_CASE(errseq_test_same_error_reported_again_after_seen), + KUNIT_CASE(errseq_test_repeat_error_visible_to_all_cursors), + KUNIT_CASE(errseq_test_advance_stable_when_unchanged), + KUNIT_CASE(errseq_test_two_subscribers_independent), + {} +}; + +static struct kunit_suite errseq_test_suite = { + .name = "errseq", + .test_cases = errseq_test_cases, +}; + +kunit_test_suite(errseq_test_suite); + +MODULE_DESCRIPTION("KUnit tests for the errseq infrastructure"); +MODULE_LICENSE("GPL"); diff --git a/mm/shmem.c b/mm/shmem.c index 23fbad833f5b..3c0c7edd2020 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4711,6 +4711,9 @@ static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter * struct unicode_map *encoding; char *version_str = param->string + 5; + if (ctx->encoding) + return invalfc(fc, "casefold parameter cannot be specified twice\n"); + if (!latest_version) { if (strncmp(param->string, "utf8-", 5)) return invalfc(fc, "Only UTF-8 encodings are supported " @@ -5206,6 +5209,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) if (ctx->encoding) { sb->s_encoding = ctx->encoding; + ctx->encoding = NULL; set_default_d_op(sb, &shmem_ci_dentry_ops); if (ctx->strict_encoding) sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL; @@ -5307,6 +5311,9 @@ static void shmem_free_fc(struct fs_context *fc) struct shmem_options *ctx = fc->fs_private; if (ctx) { +#if IS_ENABLED(CONFIG_UNICODE) + utf8_unload(ctx->encoding); +#endif mpol_put(ctx->mpol); kfree(ctx); } diff --git a/scripts/Makefile.gcov b/scripts/Makefile.gcov new file mode 100644 index 000000000000..16c5b876440c --- /dev/null +++ b/scripts/Makefile.gcov @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_GCOV += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_GCOV += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_GCOV) \ + -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_GCOV) \ + -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_GCOV diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c9840f5219c2..b8b64f720191 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -63,6 +63,7 @@ my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; my $spdx_cxx_comments = 0; +my $userspace; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; @@ -143,6 +144,7 @@ Options: (required by old toolchains), allow also C++ comments (//). NOTE: it should *not* be used for Linux mainline. + --userspace Force rules specific for userspace. --codespell Use the codespell dictionary for spelling/typos (default:$codespellfile) --codespellfile Use this codespell dictionary @@ -358,6 +360,7 @@ GetOptions( 'codespell!' => \$codespell, 'codespellfile=s' => \$user_codespellfile, 'typedefsfile=s' => \$typedefsfile, + 'userspace!' => \$userspace, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor @@ -1205,6 +1208,8 @@ sub seed_camelcase_includes { my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } elsif (not defined $root) { + return; } else { my $last_mod_date = 0; $files = `find $root/include -name "*.h"`; @@ -2664,7 +2669,12 @@ sub exclude_global_initialisers { sub is_userspace { my ($realfile) = @_; - return ($realfile =~ m@^tools/@ || $realfile =~ m@^scripts/@); + + return $userspace if (defined $userspace); + + return ($realfile =~ m@^tools/@ || + $realfile =~ m@^scripts/@ || + $realfile =~ m@^arch/.*/tools/@); } sub process { @@ -5766,7 +5776,21 @@ sub process { my ($s, $c) = ($stat, $cond); my $fixed_assign_in_if = 0; + # ACQUIRE_ERR() and its wrappers, e.g. PM_RUNTIME_ACQUIRE_ERR() + # and IIO_DEV_ACQUIRE_FAILED(), are meant to be evaluated in an + # if condition, with the error assigned in the condition: + # if ((rc = ACQUIRE_ERR(name, &lock))) + # Allow that only when every assignment in the condition assigns + # the result of such a call, so that a mixed condition keeps + # getting flagged: + # if ((rc = regular_function()) || (ret = ACQUIRE_ERR(name, &lock))) + my $assign_in_if = 0; if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + my $has_assignment = $c =~ /\b$Lval\s*=\s*[^,)&|=]+/; + my $has_other_assignment = $c =~ /\b$Lval\s*=\s*(?!\s*\w*ACQUIRE_(?:ERR|FAILED)\s*\()[^,)&|=]+/; + $assign_in_if = !$has_assignment || $has_other_assignment; + } + if ($assign_in_if) { if (ERROR("ASSIGN_IN_IF", "do not use assignment in if condition\n" . $herecurr) && $fix && $perl_version_ok) { @@ -5935,6 +5959,8 @@ sub process { #Ignore SI style variants like nS, mV and dB #(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && +#Ignore <inttypes.h> format macros (e.g. PRIu64, SCNu64) + (is_userspace($realfile) ? $var !~ /^(?:PRI|SCN)[dioux][A-Z0-9]+$/ : 1) && #Ignore some three character SI units explicitly, like MiB and KHz $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { while ($var =~ m{\b($Ident)}g) { @@ -6673,7 +6699,8 @@ sub process { } # prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + if (!is_userspace($realfile) && + $line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { my $delay = $1; # ignore udelay's < 10, however if (! ($delay < 10) ) { @@ -6687,7 +6714,8 @@ sub process { } # warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if (!is_userspace($realfile) && + $line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", "msleep < 20ms can sleep for up to 20ms; see function description of msleep().\n" . $herecurr); @@ -6909,7 +6937,7 @@ sub process { # check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && - $realfile !~ m@\btools/@ && + !is_userspace($realfile) && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; if ($type =~ /\b($typeC99Typedefs)\b/) { @@ -7159,6 +7187,7 @@ sub process { # check usleep_range arguments if ($perl_version_ok && defined $stat && + !is_userspace($realfile) && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; @@ -7402,6 +7431,7 @@ sub process { # check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi if ($realfile !~ m@^include/uapi/@ && + !is_userspace($realfile) && $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { my $ull = ""; $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index 14ce31f732ee..f8011e789a9b 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -66,8 +66,10 @@ my (@stack, $re, $dre, $sub, $x, $xs, $funcre, $min_stack); #c0105234: 81 ec ac 05 00 00 sub $0x5ac,%esp # or # 2f60: 48 81 ec e8 05 00 00 sub $0x5e8,%rsp - $re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%(e|r)sp$/o; - $dre = qr/^.*[as][du][db] (%.*),\%(e|r)sp$/o; + # or + # 0: 48 81 ec e8 05 00 00 subq $0x5e8, %rsp + $re = qr/^.*[as][du][db][ql]?\s+\$(0x$x{1,8}),\s*\%(e|r)sp/o; + $dre = qr/^.*[as][du][db][ql]?\s+(%.*),\s*\%(e|r)sp/o; } elsif ($arch eq 'm68k') { # 2b6c: 4e56 fb70 linkw %fp,#-1168 # 1df770: defc ffe4 addaw #-28,%sp diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py index bc2954e45c32..8ce9936dfa50 100644 --- a/scripts/gdb/linux/radixtree.py +++ b/scripts/gdb/linux/radixtree.py @@ -27,6 +27,24 @@ def entry_to_node(node): indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INTERNAL_NODE return indirect_ptr.cast(radix_tree_node_type.get_type().pointer()) +def is_node(entry): + # Like xa_is_node(): internal entries below 4096 are sibling, retry + # or zero entries rather than pointers to a struct xa_node. + ulong_type = utils.get_ulong_type() + return is_internal_node(entry) and entry.cast(ulong_type) > 4096 + +def is_sibling(entry): + # Like xa_is_sibling(): a multi-index entry occupies several slots, + # and all but the first hold a sibling entry that encodes the offset + # of the first one. + ulong_type = utils.get_ulong_type() + return is_internal_node(entry) and entry.cast(ulong_type) < \ + xa_mk_internal(constants.LX_RADIX_TREE_MAP_SIZE - 1) + +def sibling_offset(entry): + ulong_type = utils.get_ulong_type() + return int(entry.cast(ulong_type)) >> 2 + def node_maxindex(node): return (constants.LX_RADIX_TREE_MAP_SIZE << node['shift']) - 1 @@ -40,39 +58,35 @@ def resolve_root(root): def lookup(root, index): root = resolve_root(root) - node = root['xa_head'] - if node == 0: - return None + entry = root['xa_head'] - if not (is_internal_node(node)): - if (index > 0): + if is_node(entry): + node = entry_to_node(entry) + if index > node_maxindex(node): return None - return node - node = entry_to_node(node) - maxindex = node_maxindex(node) - - if (index > maxindex): - return None - - shift = node['shift'] + constants.LX_RADIX_TREE_MAP_SHIFT - - while True: - offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK - slot = node['slots'][offset] + # Walk down the tree like xas_load() does. + while True: + offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK + entry = node['slots'][offset] - if slot == 0: - return None + while is_sibling(entry): + entry = node['slots'][sibling_offset(entry)] + if node['shift'] and is_node(entry): + # xas_descend() turns this into a retry entry. + return None - node = slot.cast(node.type.pointer()).dereference() - if node == 0: - return None + if not is_node(entry) or node['shift'] == 0: + break + node = entry_to_node(entry) + elif index > 0: + return None - shift -= constants.LX_RADIX_TREE_MAP_SHIFT - if (shift <= 0): - break + # Empty slots and retry or zero entries hold no data. + if entry == 0 or is_internal_node(entry): + return None - return node + return entry def descend(parent, index): offset = (index >> int(parent["shift"])) & constants.LX_RADIX_TREE_MAP_MASK diff --git a/scripts/gdb/linux/stackdepot.py b/scripts/gdb/linux/stackdepot.py index 37313a5a51a0..aaa18af25cd1 100644 --- a/scripts/gdb/linux/stackdepot.py +++ b/scripts/gdb/linux/stackdepot.py @@ -39,7 +39,7 @@ def stack_depot_fetch(handle): pool_index = parts['pool_index_plus_1'] - 1 if pool_index >= pools_num: - gdb.write("pool index %d out of bounds (%d) for stack id 0x%08x\n" % (parts['pool_index'], pools_num, handle)) + gdb.write("pool index %d out of bounds (%d) for stack id 0x%08x\n" % (pool_index, pools_num, handle)) return gdb.Value(0), 0 stack_pools = gdb.parse_and_eval('stack_pools') diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 3372873cd7bb..9ec2529bc584 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -872,8 +872,8 @@ infromation||information ingore||ignore inheritence||inheritance inital||initial -initalised||initialized -initalise||initialize +initalised||initialised +initalise||initialise initalized||initialized initalize||initialize initation||initiation diff --git a/tools/testing/selftests/core/unshare_test.c b/tools/testing/selftests/core/unshare_test.c index ffce75a6c228..d40e963dd520 100644 --- a/tools/testing/selftests/core/unshare_test.c +++ b/tools/testing/selftests/core/unshare_test.c @@ -40,6 +40,14 @@ TEST(unshare_EMFILE) ASSERT_EQ(sscanf(buf, "%d", &nr_open), 1); + /* + * Cap nr_open for the duration of the test to avoid ENOMEM from a + * huge fd table allocation; buf/n keep the real original value so + * fs.nr_open can be restored to it once the test is done. + */ + if (nr_open > 1024 * 1024) + nr_open = 1024 * 1024; + ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit)); /* bump fs.nr_open */ @@ -73,10 +81,13 @@ TEST(unshare_EMFILE) if (pid == 0) { int err; + char buf3[32]; + ssize_t n3; - /* restore fs.nr_open */ + /* restore fs.nr_open to the (possibly capped) test baseline */ + n3 = sprintf(buf3, "%d\n", nr_open); lseek(fd, 0, SEEK_SET); - write(fd, buf, n); + write(fd, buf3, n3); /* ... and now unshare(CLONE_FILES) must fail with EMFILE */ err = unshare(CLONE_FILES); EXPECT_EQ(err, -1) @@ -89,6 +100,10 @@ TEST(unshare_EMFILE) EXPECT_EQ(waitpid(pid, &status, 0), pid); EXPECT_EQ(true, WIFEXITED(status)); EXPECT_EQ(0, WEXITSTATUS(status)); + + /* restore the real fs.nr_open value */ + lseek(fd, 0, SEEK_SET); + write(fd, buf, n); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/filelock/ofdlocks.c b/tools/testing/selftests/filelock/ofdlocks.c index 68bac28b234b..0ab484cb075b 100644 --- a/tools/testing/selftests/filelock/ofdlocks.c +++ b/tools/testing/selftests/filelock/ofdlocks.c @@ -40,7 +40,7 @@ int main(void) int fd2 = open("/tmp/aa", O_RDONLY); ksft_print_header(); - ksft_set_plan(4); + ksft_set_plan(5); unlink("/tmp/aa"); assert(fd != -1); diff --git a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c index 81a994943e12..b4dcbcd79773 100644 --- a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c +++ b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c @@ -74,6 +74,24 @@ static void *waiter_entry1ap(void *data) return NULL; } +static void *waiter_entry1ap_loop(void *data) +{ + struct pollfd pfd; + struct epoll_event e; + struct epoll_mtcontext *ctx = data; + + pfd.fd = ctx->efd[0]; + pfd.events = POLLIN; + while (poll(&pfd, 1, 2000) > 0) { + if (epoll_wait(ctx->efd[0], &e, 1, 0) > 0) { + __sync_fetch_and_add(&ctx->count, 1); + break; + } + } + + return NULL; +} + static void *waiter_entry1o(void *data) { struct epoll_event e; @@ -809,7 +827,7 @@ TEST(epoll16) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.sfd[2], events), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); if (epoll_wait(ctx.efd[0], events, 1, -1) > 0) @@ -2925,7 +2943,7 @@ TEST(epoll56) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.efd[2], &e), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); if (epoll_wait(ctx.efd[0], &e, 1, -1) > 0) @@ -3030,7 +3048,6 @@ TEST(epoll57) TEST(epoll58) { pthread_t emitter; - struct pollfd pfd; struct epoll_event e; struct epoll_mtcontext ctx = { 0 }; @@ -3061,15 +3078,10 @@ TEST(epoll58) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.efd[2], &e), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); - pfd.fd = ctx.efd[0]; - pfd.events = POLLIN; - if (poll(&pfd, 1, -1) > 0) { - if (epoll_wait(ctx.efd[0], &e, 1, 0) > 0) - __sync_fetch_and_add(&ctx.count, 1); - } + waiter_entry1ap_loop(&ctx); ASSERT_EQ(pthread_join(ctx.waiter, NULL), 0); EXPECT_EQ(ctx.count, 2); diff --git a/tools/testing/selftests/membarrier/membarrier_test_impl.h b/tools/testing/selftests/membarrier/membarrier_test_impl.h index f6d7c44b2288..b4dcbb32538d 100644 --- a/tools/testing/selftests/membarrier/membarrier_test_impl.h +++ b/tools/testing/selftests/membarrier/membarrier_test_impl.h @@ -16,6 +16,11 @@ static int sys_membarrier(int cmd, int flags) return syscall(__NR_membarrier, cmd, flags); } +static int membarrier_get_registrations(void) +{ + return sys_membarrier(MEMBARRIER_CMD_GET_REGISTRATIONS, 0); +} + static int test_membarrier_get_registrations(int cmd) { int ret, flags = 0; @@ -24,7 +29,7 @@ static int test_membarrier_get_registrations(int cmd) registrations |= cmd; - ret = sys_membarrier(MEMBARRIER_CMD_GET_REGISTRATIONS, 0); + ret = membarrier_get_registrations(); if (ret < 0) { ksft_exit_fail_msg( "%s test: flags = %d, errno = %d\n", @@ -108,6 +113,16 @@ static int test_membarrier_private_expedited_fail(void) int cmd = MEMBARRIER_CMD_PRIVATE_EXPEDITED, flags = 0; const char *test_name = "sys membarrier MEMBARRIER_CMD_PRIVATE_EXPEDITED not registered failure"; + /* + * Some C libraries, like Musl, register the private expedited barrier + * command when creating a thread. Expecting an EPERM on an unregistered + * command will therefore no longer work. Skip the test in this case. + */ + if (MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED & membarrier_get_registrations()) { + ksft_test_result_skip("%s test: Command already registered\n", test_name); + return 0; + } + if (sys_membarrier(cmd, flags) != -1) { ksft_exit_fail_msg( "%s test: flags = %d. Should fail, but passed\n", diff --git a/tools/testing/selftests/proc/proc-pid-vm.c b/tools/testing/selftests/proc/proc-pid-vm.c index 4e6a3e53f975..98e882e56815 100644 --- a/tools/testing/selftests/proc/proc-pid-vm.c +++ b/tools/testing/selftests/proc/proc-pid-vm.c @@ -47,10 +47,6 @@ #include <sys/resource.h> #include <linux/fs.h> -#ifndef __maybe_unused -#define __maybe_unused __attribute__((__unused__)) -#endif - #include "kselftest.h" static inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags) @@ -214,7 +210,6 @@ static int make_exe(const uint8_t *payload, size_t len) return fd1; } -#endif /* * 0: vsyscall VMA doesn't exist vsyscall=none @@ -222,15 +217,14 @@ static int make_exe(const uint8_t *payload, size_t len) * 2: vsyscall VMA is r-xp vsyscall=emulate */ static volatile int g_vsyscall; -static const char *str_vsyscall __maybe_unused; +static const char *str_vsyscall; -static const char str_vsyscall_0[] __maybe_unused = ""; -static const char str_vsyscall_1[] __maybe_unused = +static const char str_vsyscall_0[] = ""; +static const char str_vsyscall_1[] = "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"; -static const char str_vsyscall_2[] __maybe_unused = +static const char str_vsyscall_2[] = "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; -#ifdef __x86_64__ static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___) { _exit(g_vsyscall); |
