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