summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-09 21:05:30 +0200
committerMark Brown <broonie@kernel.org>2026-10-09 21:05:30 +0200
commit0bc99dcffd027019fb47fa61735e5a8843dfc698 (patch)
treeb2425fe02b6b8281d181224babbe8071c4c2849e
parentf3e80afbdf1062ea584754bb17569ee363adb267 (diff)
parent56de113da14706d6a1b28648e9ff5c261f536201 (diff)
downloadlinux-next-0bc99dcffd027019fb47fa61735e5a8843dfc698.tar.gz
linux-next-0bc99dcffd027019fb47fa61735e5a8843dfc698.zip
Merge branch 'mm-nonmm-stable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
-rw-r--r--.mailmap3
-rw-r--r--CREDITS181
-rw-r--r--Documentation/admin-guide/sysctl/kernel.rst5
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/Kconfig8
-rw-r--r--arch/s390/kernel/traps.c7
-rw-r--r--arch/sparc/kernel/setup.c9
-rw-r--r--drivers/infiniband/hw/hfi1/fault.c1
-rw-r--r--drivers/rapidio/devices/rio_mport_cdev.c5
-rw-r--r--drivers/usb/gadget/legacy/inode.c4
-rw-r--r--drivers/watchdog/hpwdt.c2
-rw-r--r--fs/fat/fat.h2
-rw-r--r--fs/fat/fatent.c21
-rw-r--r--fs/fat/file.c3
-rw-r--r--fs/fat/inode.c23
-rw-r--r--fs/fat/misc.c6
-rw-r--r--fs/ocfs2/alloc.c27
-rw-r--r--fs/ocfs2/dir.c55
-rw-r--r--fs/ocfs2/dlmglue.c4
-rw-r--r--fs/ocfs2/inode.c53
-rw-r--r--fs/ocfs2/journal.c3
-rw-r--r--fs/ocfs2/journal.h1
-rw-r--r--fs/ocfs2/ocfs2.h17
-rw-r--r--fs/ocfs2/quota_local.c9
-rw-r--r--fs/ocfs2/refcounttree.c27
-rw-r--r--fs/ocfs2/suballoc.c235
-rw-r--r--fs/ocfs2/suballoc.h2
-rw-r--r--fs/ocfs2/super.c32
-rw-r--r--fs/ocfs2/xattr.c194
-rw-r--r--fs/proc/base.c8
-rw-r--r--include/linux/fault-inject.h10
-rw-r--r--include/linux/minmax.h6
-rw-r--r--include/linux/panic.h5
-rw-r--r--init/main.c8
-rw-r--r--init/version.c11
-rw-r--r--ipc/mqueue.c7
-rw-r--r--kernel/fork.c3
-rw-r--r--kernel/gcov/fs.c2
-rw-r--r--kernel/hung_task.c55
-rw-r--r--kernel/panic.c102
-rw-r--r--kernel/resource.c16
-rw-r--r--kernel/taskstats.c50
-rw-r--r--lib/Kconfig14
-rw-r--r--lib/Kconfig.debug15
-rw-r--r--lib/bootconfig.c46
-rw-r--r--lib/decompress_bunzip2.c2
-rw-r--r--lib/decompress_unlz4.c4
-rw-r--r--lib/decompress_unxz.c10
-rw-r--r--lib/fault-inject.c7
-rw-r--r--lib/klist.c16
-rw-r--r--lib/plist.c7
-rw-r--r--lib/raid/Kconfig2
-rw-r--r--lib/raid/raid6/x86/avx2.c6
-rw-r--r--lib/raid/raid6/x86/avx512.c6
-rw-r--r--lib/raid/raid6/x86/recov_avx2.c2
-rw-r--r--lib/raid/raid6/x86/recov_avx512.c2
-rw-r--r--lib/raid/xor/x86/xor-avx.c1
-rw-r--r--lib/tests/Makefile1
-rw-r--r--lib/tests/errseq_kunit.c237
-rwxr-xr-xscripts/checkpatch.pl24
-rwxr-xr-xscripts/checkstack.pl6
-rw-r--r--scripts/spelling.txt4
-rw-r--r--tools/bootconfig/main.c104
-rwxr-xr-xtools/bootconfig/test-bootconfig.sh12
-rw-r--r--tools/testing/selftests/core/unshare_test.c19
-rw-r--r--tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c32
-rw-r--r--tools/testing/selftests/membarrier/membarrier_test_impl.h17
67 files changed, 1368 insertions, 451 deletions
diff --git a/.mailmap b/.mailmap
index 0a0be49bc4de..f92fd649a6dc 100644
--- a/.mailmap
+++ b/.mailmap
@@ -67,6 +67,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>
@@ -91,6 +92,7 @@ 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>
@@ -172,6 +174,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>
diff --git a/CREDITS b/CREDITS
index a0bd941ca5dd..3ae05ff1a092 100644
--- a/CREDITS
+++ b/CREDITS
@@ -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 ;)
@@ -1945,16 +1943,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.
@@ -2099,6 +2087,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
@@ -2483,6 +2481,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
@@ -2542,11 +2543,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
@@ -2575,6 +2571,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
@@ -2625,10 +2626,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
@@ -2912,13 +2909,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
@@ -3005,6 +2995,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
@@ -3200,13 +3201,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
@@ -3214,6 +3208,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.
@@ -3328,14 +3329,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
@@ -4023,12 +4024,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
@@ -4082,17 +4077,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
@@ -4109,6 +4093,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
@@ -4135,6 +4130,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 ffea61d448eb..c03369e234a9 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 3cbdb0c4c9dd..2e430551fb86 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9700,6 +9700,7 @@ M: Jeff Layton <jlayton@kernel.org>
S: Maintained
F: include/linux/errseq.h
F: lib/errseq.c
+F: lib/tests/errseq_kunit.c
ESD CAN NETWORK DRIVERS
M: Stefan Mätje <stefan.maetje@esd.eu>
diff --git a/arch/Kconfig b/arch/Kconfig
index 6f7516916797..a438eda84cd4 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 b6ba4465f59d..115cb3373247 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/entry-percpu.h>
#include <asm/asm-extable.h>
#include <asm/irqflags.h>
@@ -33,6 +34,7 @@
#include <asm/vtime.h>
#include <asm/fpu.h>
#include <asm/fault.h>
+#include <asm/processor.h>
#include "entry.h"
struct pgm_stat {
@@ -283,6 +285,11 @@ static void monitor_event_exception(struct pt_regs *regs)
}
}
+void arch_do_panic(void)
+{
+ disabled_wait();
+}
+
void kernel_stack_invalid(struct pt_regs *regs)
{
/*
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..47ba34b4afb2 100644
--- a/drivers/rapidio/devices/rio_mport_cdev.c
+++ b/drivers/rapidio/devices/rio_mport_cdev.c
@@ -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 67c6ffaf4f72..7e383e3e0f66 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -613,7 +613,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;
@@ -675,7 +675,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 8af1fad2de0b..78227d200afe 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -199,7 +199,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 61338413d9f3..fbd207c55859 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -392,7 +392,7 @@ 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);
extern int fat_free_clusters(struct inode *inode, int cluster);
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index f0801d99dd62..df23fc85f313 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,
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 1c835ca5f21a..6c475c53334c 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -363,7 +363,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..0b0bbe777842 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>
@@ -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 e79762cf1975..c44296756eae 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -133,11 +133,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/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index be09e766ac1f..2fdc5403b10b 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",
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 0075e1624310..55c4a305a282 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -605,6 +605,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 +733,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,
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index a23dd8f86c89..cf3318b0d3a8 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3525,6 +3525,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/inode.c b/fs/ocfs2/inode.c
index 180107a11046..92f3450010fb 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)))
@@ -1520,14 +1524,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 d8afbc1a76bb..a3938a03e93b 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/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 f55810c59b1b..d351cda9211f 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -1071,10 +1071,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..3e9cccf06e48 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)
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..1e76b1d9fe0a 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1162,15 +1162,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 +1775,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 +1857,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 +2151,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 bfafe059bedf..a2d6c3d0f2e8 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,
@@ -517,6 +532,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);
@@ -989,7 +1029,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 +1049,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 +1068,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 +1150,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 +1200,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 +1223,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 +1371,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 +1394,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;
@@ -2909,6 +2982,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 +2996,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 +3083,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 +3094,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 +3114,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;
}
@@ -3687,6 +3762,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 +3834,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 +3853,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 +3923,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 +3964,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 +3975,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 +3984,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 +4002,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 +4011,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 +4141,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;
@@ -4508,6 +4615,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 +4634,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 +4703,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 +4782,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 +4801,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 +5073,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);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 58be38942460..0f9efd25bb05 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/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 f1dd417e54b2..17e61b61c45f 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);
@@ -110,4 +111,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/main.c b/init/main.c
index a87a3d52f3e2..1a3199e695ff 100644
--- a/init/main.c
+++ b/init/main.c
@@ -548,12 +548,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 {
@@ -581,7 +581,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;
}
@@ -594,7 +594,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/ipc/mqueue.c b/ipc/mqueue.c
index d1dd36a651b0..d1a1965c9811 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -528,6 +528,13 @@ static void mqueue_evict_inode(struct inode *inode)
list_add_tail(&msg->m_list, &tmp_msg);
kfree(info->node_cache);
spin_unlock(&info->lock);
+ /*
+ * A shared file table can let the notification owner exit without
+ * running ->flush(). No users of the inode remain during eviction, so
+ * tear down any stale notification after dropping info->lock because
+ * netlink_sendskb() may release the final socket reference.
+ */
+ remove_notification(info);
list_for_each_entry_safe(msg, nmsg, &tmp_msg, m_list) {
list_del(&msg->m_list);
diff --git a/kernel/fork.c b/kernel/fork.c
index ceedb07a5a00..ea823b28d625 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/panic.c b/kernel/panic.c
index 213725b612aa..170744163fc2 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;
@@ -1225,14 +1224,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/resource.c b/kernel/resource.c
index e60539a55541..cfc1a00e86aa 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);
@@ -1833,7 +1833,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;
@@ -1843,12 +1843,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
@@ -1859,8 +1857,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/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/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 6b24a2c89b84..1c6638b13b87 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2797,6 +2797,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/bootconfig.c b/lib/bootconfig.c
index 89c88e359179..192a60a9f833 100644
--- a/lib/bootconfig.c
+++ b/lib/bootconfig.c
@@ -836,7 +836,6 @@ static int __init xbc_parse_array(char **__v)
return -ENOMEM;
*__v = next;
} while (c == ',');
- node->child = 0;
return c;
}
@@ -1003,9 +1002,30 @@ static int __init xbc_close_brace(char **k, char *n)
return __xbc_close_brace(n - 1);
}
+#ifndef __KERNEL__
+/* Sanity check for regression: node indices must be within bounds */
+static int __init xbc_sanity_check_tree(void)
+{
+ int i;
+
+ for (i = 0; i < xbc_node_num; i++) {
+ if (xbc_nodes[i].next >= xbc_node_num) {
+ return xbc_parse_error("No closing brace",
+ xbc_node_get_data(xbc_nodes + i));
+ }
+ if (xbc_nodes[i].child >= xbc_node_num) {
+ return xbc_parse_error("Broken child node",
+ xbc_node_get_data(xbc_nodes + i));
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int __init xbc_verify_tree(void)
{
- int i, depth;
+ int depth;
size_t len, wlen;
struct xbc_node *n, *m;
@@ -1022,17 +1042,6 @@ static int __init xbc_verify_tree(void)
return -ENOENT;
}
- for (i = 0; i < xbc_node_num; i++) {
- if (xbc_nodes[i].next >= xbc_node_num) {
- return xbc_parse_error("No closing brace",
- xbc_node_get_data(xbc_nodes + i));
- }
- if (xbc_nodes[i].child >= xbc_node_num) {
- return xbc_parse_error("Broken child node",
- xbc_node_get_data(xbc_nodes + i));
- }
- }
-
/* Key tree limitation check */
n = &xbc_nodes[0];
depth = 1;
@@ -1119,6 +1128,13 @@ static int __init xbc_parse_tree(void)
}
} while (!ret);
+ if (!ret) {
+ while (p < xbc_data + xbc_data_size - 1 && *p == '\0')
+ p++;
+ if (p < xbc_data + xbc_data_size - 1)
+ ret = xbc_parse_error("Unexpected data after null character", p);
+ }
+
return ret;
}
@@ -1196,6 +1212,10 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos)
ret = xbc_parse_tree();
if (!ret)
ret = xbc_verify_tree();
+#ifndef __KERNEL__
+ if (!ret)
+ ret = xbc_sanity_check_tree();
+#endif
if (ret < 0) {
if (epos)
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index 1288f146661f..aaa75404250e 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -439,7 +439,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/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/tests/Makefile b/lib/tests/Makefile
index 3cac3b63a752..8e11b125433b 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -13,6 +13,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/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/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 8a7787d228a6..3614cfe4dcbb 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
@@ -1206,6 +1209,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"`;
@@ -2665,7 +2670,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 {
@@ -5946,6 +5956,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) {
@@ -6684,7 +6696,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) ) {
@@ -6698,7 +6711,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);
@@ -6920,7 +6934,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/) {
@@ -7170,6 +7184,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;
@@ -7413,6 +7428,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/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/bootconfig/main.c b/tools/bootconfig/main.c
index 17d971d47f87..652e491b9c33 100644
--- a/tools/bootconfig/main.c
+++ b/tools/bootconfig/main.c
@@ -21,6 +21,39 @@
#define BOOTCONFIG_FOOTER_SIZE \
(sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN)
+static void show_xbc_error(const char *data, const char *msg, int pos)
+{
+ int lin = 1, col, i;
+
+ if (pos < 0) {
+ pr_err("Error: %s.\n", msg);
+ return;
+ }
+
+ /* Note that pos starts from 0 but lin and col should start from 1. */
+ col = pos + 1;
+ for (i = 0; i < pos; i++) {
+ if (data[i] == '\n') {
+ lin++;
+ col = pos - i;
+ }
+ }
+ pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
+
+}
+
+static int init_xbc_with_error(char *buf, int len)
+{
+ const char *msg;
+ int ret, pos;
+
+ ret = xbc_init(buf, len, &msg, &pos);
+ if (ret < 0)
+ show_xbc_error(buf, msg, pos);
+
+ return ret;
+}
+
static int xbc_show_value(struct xbc_node *node, bool semicolon)
{
const char *val, *eol;
@@ -197,7 +230,6 @@ static int load_xbc_from_initrd(int fd, char **buf)
int ret;
uint32_t size = 0, csum = 0, rcsum;
char magic[BOOTCONFIG_MAGIC_LEN];
- const char *msg;
ret = fstat(fd, &stat);
if (ret < 0)
@@ -249,52 +281,9 @@ static int load_xbc_from_initrd(int fd, char **buf)
return -EINVAL;
}
- ret = xbc_init(*buf, size, &msg, NULL);
- /* Wrong data */
- if (ret < 0) {
- pr_err("parse error: %s.\n", msg);
- return ret;
- }
-
- return size;
-}
-
-static void show_xbc_error(const char *data, const char *msg, int pos)
-{
- int lin = 1, col, i;
-
- if (pos < 0) {
- pr_err("Error: %s.\n", msg);
- return;
- }
-
- /* Note that pos starts from 0 but lin and col should start from 1. */
- col = pos + 1;
- for (i = 0; i < pos; i++) {
- if (data[i] == '\n') {
- lin++;
- col = pos - i;
- }
- }
- pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
+ ret = init_xbc_with_error(*buf, size);
-}
-
-static int init_xbc_with_error(char *buf, int len)
-{
- char *copy = strdup(buf);
- const char *msg;
- int ret, pos;
-
- if (!copy)
- return -ENOMEM;
-
- ret = xbc_init(buf, len, &msg, &pos);
- if (ret < 0)
- show_xbc_error(copy, msg, pos);
- free(copy);
-
- return ret;
+ return ret < 0 ? ret : size;
}
static int show_xbc_kernel_cmdline(void)
@@ -423,9 +412,8 @@ static int apply_xbc(const char *path, const char *xbc_path)
char *buf, *data;
size_t total_size;
struct stat stat;
- const char *msg;
uint32_t size, csum;
- int pos, pad;
+ int pad;
int ret, fd;
ret = load_xbc_file(xbc_path, &buf);
@@ -433,9 +421,18 @@ static int apply_xbc(const char *path, const char *xbc_path)
pr_err("Failed to load %s : %d\n", xbc_path, ret);
return ret;
}
- size = strlen(buf) + 1;
+ size = ret;
+ if (size == 0 || buf[size - 1] != '\0')
+ size++;
csum = xbc_calc_checksum(buf, size);
+ /* Verify the data format */
+ ret = init_xbc_with_error(buf, size);
+ if (ret < 0) {
+ free(buf);
+ return ret;
+ }
+
/* Backup the bootconfig data */
data = calloc(size + BOOTCONFIG_ALIGN + BOOTCONFIG_FOOTER_SIZE, 1);
if (!data) {
@@ -444,15 +441,6 @@ static int apply_xbc(const char *path, const char *xbc_path)
}
memcpy(data, buf, size);
- /* Check the data format */
- ret = xbc_init(buf, size, &msg, &pos);
- if (ret < 0) {
- show_xbc_error(data, msg, pos);
- free(data);
- free(buf);
-
- return ret;
- }
printf("Apply %s to %s\n", xbc_path, path);
xbc_get_info(&ret, NULL);
printf("\tNumber of nodes: %d\n", ret);
diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh
index fc69f815ce4a..530ce7e28d63 100755
--- a/tools/bootconfig/test-bootconfig.sh
+++ b/tools/bootconfig/test-bootconfig.sh
@@ -180,6 +180,18 @@ EOF
$BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE
xpass grep -q "1:1" $OUTFILE
+echo "Intermediate null character test"
+printf "key = value\n\0extra = data\n" > $TEMPCONF
+xfail $BOOTCONF -a $TEMPCONF $INITRD
+$BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE
+xpass grep -q "Unexpected" $OUTFILE
+
+echo "Trailing null character test"
+printf "key = value\n\0" > $TEMPCONF
+xpass $BOOTCONF -a $TEMPCONF $INITRD
+$BOOTCONF $INITRD > $OUTFILE
+xpass grep -q "value" $OUTFILE
+
echo "=== expected failure cases ==="
for i in samples/bad-* ; do
xfail $BOOTCONF -a $i $INITRD
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/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",