diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-09 21:05:30 +0200 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-09 21:05:30 +0200 |
| commit | 0bc99dcffd027019fb47fa61735e5a8843dfc698 (patch) | |
| tree | b2425fe02b6b8281d181224babbe8071c4c2849e | |
| parent | f3e80afbdf1062ea584754bb17569ee363adb267 (diff) | |
| parent | 56de113da14706d6a1b28648e9ff5c261f536201 (diff) | |
| download | linux-next-0bc99dcffd027019fb47fa61735e5a8843dfc698.tar.gz linux-next-0bc99dcffd027019fb47fa61735e5a8843dfc698.zip | |
Merge branch 'mm-nonmm-stable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
67 files changed, 1368 insertions, 451 deletions
@@ -67,6 +67,7 @@ Alex Hung <alexhung@gmail.com> <alex.hung@canonical.com> Alex Shi <alexs@kernel.org> <alex.shi@intel.com> Alex Shi <alexs@kernel.org> <alex.shi@linaro.org> Alex Shi <alexs@kernel.org> <alex.shi@linux.alibaba.com> +Ali Ahmet Memiş <aliamemis@disroot.org> <ali@iusegentoo.com> Alice Mikityanska <alice.kernel@fastmail.im> <maxtram95@gmail.com> Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@mellanox.com> Alice Mikityanska <alice.kernel@fastmail.im> <maximmi@nvidia.com> @@ -91,6 +92,7 @@ Andrew Morton <akpm@linux-foundation.org> Andrew Murray <amurray@thegoodpenguin.co.uk> <amurray@embedded-bits.co.uk> Andrew Murray <amurray@thegoodpenguin.co.uk> <andrew.murray@arm.com> Andrew Vasquez <andrew.vasquez@qlogic.com> +Andrey Golovko <andrey.golovko@gmail.com> <andrey@golovko.me> Andrey Konovalov <andreyknvl@gmail.com> <andreyknvl@google.com> Andrey Ryabinin <ryabinin.a.a@gmail.com> <a.ryabinin@samsung.com> Andrey Ryabinin <ryabinin.a.a@gmail.com> <aryabinin@virtuozzo.com> @@ -172,6 +174,7 @@ Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com> Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com> Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com> Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com> +Bradley Morgan <brads@mainlining.org> <include@grrlz.net> Brendan Higgins <brendan.higgins@linux.dev> <brendanhiggins@google.com> Brendan Jackman <brendan.jackman@linux.dev> <jackmanb@google.com> Brian Avery <b.avery@hp.com> @@ -1,6 +1,7 @@ This is at least a partial credits-file of people that have - contributed to the Linux project. It is sorted by name and - formatted to allow easy grepping and beautification by + contributed to the Linux project. It is sorted by family name, + assuming the used locale is default, e.g. by setting LC_ALL=C, + and formatted to allow easy grepping and beautification by scripts. The fields are: name (N), email (E), web-address (W), PGP key ID and fingerprint (P), description (D), and snail-mail address (S). @@ -344,6 +345,14 @@ D: Hardware spinlock (hwspinlock) subsystem D: OMAP hwspinlock driver D: OMAP remoteproc driver +N: Muli Ben-Yehuda +E: mulix@mulix.org +E: muli@il.ibm.com +W: http://www.mulix.org +D: trident OSS sound driver, x86-64 dma-ops and Calgary IOMMU, +D: KVM and Xen bits and other misc. hackery. +S: Haifa, Israel + N: Krzysztof Benedyczak E: golbi@mat.uni.torun.pl W: http://www.mat.uni.torun.pl/~golbi @@ -362,14 +371,6 @@ S: 2322 37th Ave SW S: Seattle, Washington 98126-2010 S: USA -N: Muli Ben-Yehuda -E: mulix@mulix.org -E: muli@il.ibm.com -W: http://www.mulix.org -D: trident OSS sound driver, x86-64 dma-ops and Calgary IOMMU, -D: KVM and Xen bits and other misc. hackery. -S: Haifa, Israel - N: Johannes Berg E: johannes@sipsolutions.net W: https://johannes.sipsolutions.net/ @@ -393,14 +394,14 @@ S: 1549 Hiironen Rd. S: Brimson, MN 55602 S: USA -N: Arnd Bergmann -D: Maintainer of Cell Broadband Engine Architecture - N: Hennus Bergman P: 1024/77D50909 76 99 FD 31 91 E1 96 1C 90 BB 22 80 62 F6 BD 63 D: Author and maintainer of the QIC-02 tape driver S: The Netherlands +N: Arnd Bergmann +D: Maintainer of Cell Broadband Engine Architecture + N: Tomas Berndtsson E: tomas@nocrew.org W: http://tomas.nocrew.org/ @@ -492,10 +493,6 @@ D: Various fixes (mostly networking) S: Montreal, Quebec S: Canada -N: Zoltán Böszörményi -E: zboszor@mail.externet.hu -D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support - N: John Boyd E: boyd@cis.ohio-state.edu D: Co-author of wd7000 SCSI driver @@ -589,9 +586,6 @@ N: Zach Brown E: zab@zabbo.net D: maestro pci sound -N: Zefan Li -D: Contribution to control group stuff - N: David Brownell D: Kernel engineer, mentor, and friend. Maintained USB EHCI and D: gadget layers, SPI subsystem, GPIO subsystem, and more than a few @@ -632,6 +626,10 @@ S: Ravenhorst 58 S: 2317 AK Leiden S: The Netherlands +N: Zoltán Böszörményi +E: zboszor@mail.externet.hu +D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support + N: Michael Callahan E: callahan@maths.ox.ac.uk D: PPP for Linux @@ -703,6 +701,10 @@ S: Tamsui town, Taipei county, S: Taiwan 251 S: Republic of China +N: Landen Chao +E: Landen.Chao@mediatek.com +D: MT7531 Ethernet switch support + N: Michael Elizabeth Chastain E: mec@shout.net D: Configure, Menuconfig, xconfig @@ -715,10 +717,6 @@ D: Media subsystem (V4L/DVB) drivers and core D: EDAC drivers and EDAC 3.0 core rework S: Brazil -N: Landen Chao -E: Landen.Chao@mediatek.com -D: MT7531 Ethernet switch support - N: Raymond Chen E: raymondc@microsoft.com D: Author of Configure script @@ -1447,6 +1445,10 @@ D: Made support for modules, ramdisk, generic-serial, etc. optional. D: Transformed old user space bdflush into 1st kernel thread - kflushd. D: Many other patches, documentation files, mini kernels, utilities, ... +N: Andy Gospodarek +E: andy@greyhouse.net +D: Maintenance and contributions to the network interface bonding driver. + N: Masanori GOTO E: gotom@debian.or.jp D: Workbit NinjaSCSI-32Bi/UDE driver @@ -1459,10 +1461,6 @@ S: 8124 Constitution Apt. 7 S: Sterling Heights, Michigan 48313 S: USA -N: Andy Gospodarek -E: andy@greyhouse.net -D: Maintenance and contributions to the network interface bonding driver. - N: Vivek Goyal E: vgoyal@redhat.com D: KDUMP, KEXEC, and VIRTIO FILE SYSTEM @@ -1535,6 +1533,14 @@ S: 44 St. Joseph Street, Suite 506 S: Toronto, Ontario, M4Y 2W4 S: Canada +N: Nitin Gupta +E: ngupta@vflare.org +D: zsmalloc memory allocator and zram block device driver + +N: Justin Guyett +E: jguyett@andrew.cmu.edu +D: via-rhine net driver hacking + N: Richard Günther E: rguenth@tat.physik.uni-tuebingen.de W: http://www.tat.physik.uni-tuebingen.de/~rguenth @@ -1543,14 +1549,6 @@ D: binfmt_misc S: 72074 Tübingen S: Germany -N: Justin Guyett -E: jguyett@andrew.cmu.edu -D: via-rhine net driver hacking - -N: Nitin Gupta -E: ngupta@vflare.org -D: zsmalloc memory allocator and zram block device driver - N: Danny ter Haar E: dth@cistron.nl D: /proc/cpuinfo, reboot on panic , kernel pre-patch tester ;) @@ -1945,16 +1943,6 @@ E: sjenning@redhat.com D: Creation and maintenance of zswap D: Creation and maintenace of the zbud allocator -N: Jeremy Kerr -D: Maintainer of SPU File System - -N: Michael Kerrisk -E: mtk.manpages@gmail.com -W: https://man7.org/ -P: 4096R/3A35CE5E E522 595B 52ED A4E6 BFCC CB5E 8561 9911 3A35 CE5E -D: Maintainer of the Linux man-pages project -D: Linux man pages online, at <https://man7.org/linux/man-pages/> - N: Niels Kristian Bech Jensen E: nkbj1970@hotmail.com D: Miscellaneous kernel updates and fixes. @@ -2099,6 +2087,16 @@ S: Keplerstr. 6 S: 4050 Traun S: Austria +N: Jeremy Kerr +D: Maintainer of SPU File System + +N: Michael Kerrisk +E: mtk.manpages@gmail.com +W: https://man7.org/ +P: 4096R/3A35CE5E E522 595B 52ED A4E6 BFCC CB5E 8561 9911 3A35 CE5E +D: Maintainer of the Linux man-pages project +D: Linux man pages online, at <https://man7.org/linux/man-pages/> + N: Karl Keyte E: karl@koft.com D: Disk usage statistics and modifications to line printer driver @@ -2483,6 +2481,9 @@ D: and much more. He was the maintainer of MD from 2016 to 2018. Shaohua D: passed away late 2018, he will be greatly missed. W: https://www.spinics.net/lists/raid/msg61993.html +N: Zefan Li +D: Contribution to control group stuff + N: Stephan Linz E: linz@mazet.de E: Stephan.Linz@gmx.de @@ -2542,11 +2543,6 @@ D: misc. kernel hacking and debugging S: Cambridge, MA 02139 S: USA -N: Martin von Löwis -E: loewis@informatik.hu-berlin.de -D: script binary format -D: NTFS driver - N: H.J. Lu E: hjl@gnu.ai.mit.edu D: GCC + libraries hacker @@ -2575,6 +2571,11 @@ S: Puistokaari 1 E 18 S: 00200 Helsinki S: Finland +N: Martin von Löwis +E: loewis@informatik.hu-berlin.de +D: script binary format +D: NTFS driver + N: Daniel J. Maas E: dmaas@dcine.com W: https://www.maasdigital.com @@ -2625,10 +2626,6 @@ S: PO BOX 220, HFX. CENTRAL S: Halifax, Nova Scotia S: Canada B3J 3C8 -N: Kai Mäkisara -E: Kai.Makisara@kolumbus.fi -D: SCSI Tape Driver - N: Asit Mallick E: asit.k.mallick@intel.com D: Linux/IA-64 @@ -2912,13 +2909,6 @@ S: 12725 SW Millikan Way, Suite 400 S: Beaverton, Oregon 97005 S: USA -N: Eberhard Mönkeberg -E: emoenke@gwdg.de -D: CDROM driver "sbpcd" (Matsushita/Panasonic/Soundblaster) -S: Ruhstrathöhe 2 b. -S: D-37085 Göttingen -S: Germany - N: Thomas Molina E: tmolina@cablespeed.com D: bug fixes, documentation, minor hackery @@ -3005,6 +2995,17 @@ S: Dragonvagen 1 A 13 S: FIN-00330 Helsingfors S: Finland +N: Kai Mäkisara +E: Kai.Makisara@kolumbus.fi +D: SCSI Tape Driver + +N: Eberhard Mönkeberg +E: emoenke@gwdg.de +D: CDROM driver "sbpcd" (Matsushita/Panasonic/Soundblaster) +S: Ruhstrathöhe 2 b. +S: D-37085 Göttingen +S: Germany + N: Matija Nalis E: mnalis@jagor.srce.hr E: mnalis@voyager.hr @@ -3200,13 +3201,6 @@ S: RR #5, 497 Pole Line Road S: Thunder Bay, Ontario S: CANADA P7C 5M9 -N: Inaky Perez-Gonzalez -E: inaky.perez-gonzalez@intel.com -E: linux-wimax@intel.com -E: inakypg@yahoo.com -D: WiMAX stack -D: Intel Wireless WiMAX Connection 2400 driver - N: Yuri Per E: yuri@pts.mipt.ru D: Some smbfs fixes @@ -3214,6 +3208,13 @@ S: Demonstratsii 8-382 S: Tula 300000 S: Russia +N: Inaky Perez-Gonzalez +E: inaky.perez-gonzalez@intel.com +E: linux-wimax@intel.com +E: inakypg@yahoo.com +D: WiMAX stack +D: Intel Wireless WiMAX Connection 2400 driver + N: Thomas Petazzoni E: thomas.petazzoni@bootlin.com D: Driver for the Marvell Armada 370/XP network unit. @@ -3328,14 +3329,14 @@ N: Frederic Potter E: fpotter@cirpack.com D: Some PCI kernel support -N: Rui Prior -E: rprior@inescn.pt -D: ATM device driver for NICStAR based cards - N: Roopa Prabhu E: roopa@nvidia.com D: Bridge co-maintainer, vxlan and networking contributor +N: Rui Prior +E: rprior@inescn.pt +D: ATM device driver for NICStAR based cards + N: Stefan Probst E: sp@caldera.de D: The Linux Support Team Erlangen, 1993-97 @@ -4023,12 +4024,6 @@ S: C/ Federico Garcia Lorca 1 10-A S: Sevilla 41005 S: Spain -N: Björn Töpel -E: bjorn@kernel.org -D: AF_XDP -S: Gothenburg -S: Sweden - N: Linus Torvalds E: torvalds@linux-foundation.org D: Original kernel hacker @@ -4082,17 +4077,6 @@ D: Linux-Workshop Köln (aka LUG Cologne, Germany), Installfests S: Tacitusstr. 6 S: D-50968 Köln -N: Tsu-Sheng Tsao -E: tsusheng@scf.usc.edu -D: IGMP (Internet Group Management Protocol) version 2 -S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD -S: Taipei -S: Taiwan 112 -S: Republic of China -S: 24335 Delta Drive -S: Diamond Bar, California 91765 -S: USA - N: Theodore Ts'o E: tytso@mit.edu D: Random Linux hacker @@ -4109,6 +4093,17 @@ S: 1 Amherst Street S: Cambridge, Massachusetts 02139 S: USA +N: Tsu-Sheng Tsao +E: tsusheng@scf.usc.edu +D: IGMP (Internet Group Management Protocol) version 2 +S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD +S: Taipei +S: Taiwan 112 +S: Republic of China +S: 24335 Delta Drive +S: Diamond Bar, California 91765 +S: USA + N: Luben Tuikov E: Luben Tuikov <ltuikov89@gmail.com> D: Maintainer of the DRM GPU Scheduler @@ -4135,6 +4130,12 @@ S: 44 Campbell Park Crescent S: Edinburgh EH13 0HT S: United Kingdom +N: Björn Töpel +E: bjorn@kernel.org +D: AF_XDP +S: Gothenburg +S: Sweden + N: Thomas Uhl E: uhl@sun1.rz.fh-heilbronn.de D: Application programmer diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index ffea61d448eb..c03369e234a9 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -459,8 +459,9 @@ hung_task_warnings ================== The maximum number of warnings to report. During a check interval -if a hung task is detected, this value is decreased by 1. -When this value reaches 0, no more warnings will be reported. +if a hung task is detected, the internal warning budget is decreased by 1. +When this budget reaches 0, no more detailed warnings will be reported. The +warning budget is reset to the configured limit when no hung task is found. This file shows up if ``CONFIG_DETECT_HUNG_TASK`` is enabled. -1: report an infinite number of warnings. diff --git a/MAINTAINERS b/MAINTAINERS index 3cbdb0c4c9dd..2e430551fb86 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9700,6 +9700,7 @@ M: Jeff Layton <jlayton@kernel.org> S: Maintained F: include/linux/errseq.h F: lib/errseq.c +F: lib/tests/errseq_kunit.c ESD CAN NETWORK DRIVERS M: Stefan Mätje <stefan.maetje@esd.eu> diff --git a/arch/Kconfig b/arch/Kconfig index 6f7516916797..a438eda84cd4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1230,13 +1230,9 @@ config ARCH_MMAP_RND_BITS_MIN config ARCH_MMAP_RND_BITS_MAX int -config ARCH_MMAP_RND_BITS_DEFAULT - int - config ARCH_MMAP_RND_BITS int "Number of bits to use for ASLR of mmap base address" if EXPERT range ARCH_MMAP_RND_BITS_MIN ARCH_MMAP_RND_BITS_MAX - default ARCH_MMAP_RND_BITS_DEFAULT if ARCH_MMAP_RND_BITS_DEFAULT default ARCH_MMAP_RND_BITS_MIN depends on HAVE_ARCH_MMAP_RND_BITS help @@ -1264,13 +1260,9 @@ config ARCH_MMAP_RND_COMPAT_BITS_MIN config ARCH_MMAP_RND_COMPAT_BITS_MAX int -config ARCH_MMAP_RND_COMPAT_BITS_DEFAULT - int - config ARCH_MMAP_RND_COMPAT_BITS int "Number of bits to use for ASLR of mmap base address for compatible applications" if EXPERT range ARCH_MMAP_RND_COMPAT_BITS_MIN ARCH_MMAP_RND_COMPAT_BITS_MAX - default ARCH_MMAP_RND_COMPAT_BITS_DEFAULT if ARCH_MMAP_RND_COMPAT_BITS_DEFAULT default ARCH_MMAP_RND_COMPAT_BITS_MIN depends on HAVE_ARCH_MMAP_RND_COMPAT_BITS help diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index b6ba4465f59d..115cb3373247 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -26,6 +26,7 @@ #include <linux/entry-common.h> #include <linux/kmsan.h> #include <linux/bug.h> +#include <linux/panic.h> #include <asm/entry-percpu.h> #include <asm/asm-extable.h> #include <asm/irqflags.h> @@ -33,6 +34,7 @@ #include <asm/vtime.h> #include <asm/fpu.h> #include <asm/fault.h> +#include <asm/processor.h> #include "entry.h" struct pgm_stat { @@ -283,6 +285,11 @@ static void monitor_event_exception(struct pt_regs *regs) } } +void arch_do_panic(void) +{ + disabled_wait(); +} + void kernel_stack_invalid(struct pt_regs *regs) { /* diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 4975867d9001..5f43cef80638 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -2,6 +2,8 @@ #include <asm/setup.h> #include <linux/sysctl.h> +#include <linux/panic.h> +#include <linux/printk.h> static const struct ctl_table sparc_sysctl_table[] = { { @@ -36,6 +38,13 @@ static const struct ctl_table sparc_sysctl_table[] = { #endif }; +void arch_do_panic(void) +{ + /* Make sure the user can actually press Stop-A (L1-A) */ + stop_a_enabled = 1; + pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" + "twice on console to return to the boot prom\n"); +} static int __init init_sparc_sysctls(void) { diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index 4ab72ef03ba1..941a0b96590b 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -216,7 +216,6 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd) ibd->fault->attr.interval = 1; ibd->fault->attr.require_end = ULONG_MAX; ibd->fault->attr.stacktrace_depth = 32; - ibd->fault->attr.dname = NULL; ibd->fault->attr.verbose = 0; ibd->fault->enable = false; ibd->fault->opcode = false; diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index ad82c2108a56..47ba34b4afb2 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -2167,11 +2167,12 @@ static void mport_mm_open(struct vm_area_struct *vma) static void mport_mm_close(struct vm_area_struct *vma) { struct rio_mport_mapping *map = vma->vm_private_data; + struct mport_dev *md = map->md; rmcd_debug(MMAP, "%pad", &map->phys_addr); - mutex_lock(&map->md->buf_mutex); + mutex_lock(&md->buf_mutex); kref_put(&map->ref, mport_release_mapping); - mutex_unlock(&map->md->buf_mutex); + mutex_unlock(&md->buf_mutex); } static const struct vm_operations_struct vm_ops = { diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 67c6ffaf4f72..7e383e3e0f66 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -613,7 +613,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to) return -EBADMSG; } - buf = kmalloc(len, GFP_KERNEL); + buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN); if (unlikely(!buf)) { mutex_unlock(&epdata->lock); return -ENOMEM; @@ -675,7 +675,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from) return -EBADMSG; } - buf = kmalloc(len, GFP_KERNEL); + buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN); if (unlikely(!buf)) { mutex_unlock(&epdata->lock); return -ENOMEM; diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8af1fad2de0b..78227d200afe 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -199,7 +199,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) } hex_byte_pack(panic_msg, nmistat); - nmi_panic(regs, panic_msg); + nmi_panic(regs, "%s", panic_msg); return NMI_HANDLED; } diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 61338413d9f3..fbd207c55859 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -392,7 +392,7 @@ extern void fat_ent_access_init(struct super_block *sb); extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry); extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent, - int new, int wait); + int new, int old, int wait); extern int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster); extern int fat_free_clusters(struct inode *inode, int cluster); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index f0801d99dd62..df23fc85f313 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -413,7 +413,7 @@ error: } int fat_ent_write(struct inode *inode, struct fat_entry *fatent, - int new, int wait) + int new, int old, int wait) { struct super_block *sb = inode->i_sb; const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; @@ -422,10 +422,25 @@ int fat_ent_write(struct inode *inode, struct fat_entry *fatent, ops->ent_put(fatent, new); if (wait) { err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs); - if (err) + if (err) { + /* + * bhs are not uptodate after I/O error. So we + * can't simply re-dirty to revert. And it + * would not have value to write again on I/O + * error. + */ return err; + } } - return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs); + + err = fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs); + if (err) { + /* Try to revert if got the error on mirror FAT */ + ops->ent_put(fatent, old); + if (wait) + fat_sync_bhs(fatent->bhs, fatent->nr_bhs); + } + return err; } static inline int fat_ent_next(struct msdos_sb_info *sbi, diff --git a/fs/fat/file.c b/fs/fat/file.c index 1c835ca5f21a..6c475c53334c 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -363,7 +363,8 @@ static int fat_free(struct inode *inode, int skip) __func__, MSDOS_I(inode)->i_pos); ret = -EIO; } else if (ret > 0) { - err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait); + err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, ret, + wait); if (err) ret = err; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index f775a004cae1..0b0bbe777842 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -20,6 +20,7 @@ #include <linux/blkdev.h> #include <linux/backing-dev.h> #include <linux/unaligned.h> +#include <linux/overflow.h> #include <linux/random.h> #include <linux/iversion.h> #include <linux/fs_struct.h> @@ -1577,6 +1578,7 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, struct msdos_sb_info *sbi; u16 logical_sector_size; u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors; + u32 dir_start, data_start; long error; char buf[50]; struct timespec64 ts; @@ -1752,7 +1754,6 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry); sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1; - sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length; sbi->dir_entries = bpb.fat_dir_entries; if (sbi->dir_entries & (sbi->dir_per_block - 1)) { if (!silent) @@ -1763,20 +1764,30 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, rootdir_sectors = sbi->dir_entries * sizeof(struct msdos_dir_entry) / sb->s_blocksize; - sbi->data_start = sbi->dir_start + rootdir_sectors; + if (check_mul_overflow(sbi->fats, sbi->fat_length, &dir_start) || + check_add_overflow(sbi->fat_start, dir_start, &dir_start) || + check_add_overflow(dir_start, rootdir_sectors, &data_start)) { + if (!silent) + fat_msg(sb, KERN_ERR, + "overflow of root dir or data layout"); + goto out_invalid; + } + total_sectors = bpb.fat_sectors; if (total_sectors == 0) total_sectors = bpb.fat_total_sect; - if (total_sectors < sbi->data_start) { + if (total_sectors < data_start) { if (!silent) fat_msg(sb, KERN_ERR, - "data area starts beyond volume (%lu > %u)", - sbi->data_start, total_sectors); + "data area starts beyond volume (%u > %u)", + data_start, total_sectors); goto out_invalid; } - total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus; + sbi->dir_start = dir_start; + sbi->data_start = data_start; + total_clusters = (total_sectors - data_start) / sbi->sec_per_clus; if (!is_fat32(sbi)) sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index e79762cf1975..c44296756eae 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -133,11 +133,9 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) ret = fat_ent_read(inode, &fatent, last); if (ret >= 0) { int wait = inode_needs_sync(inode); - int old = ret; - ret = fat_ent_write(inode, &fatent, new_dclus, wait); - if (ret < 0) - fat_ent_write(inode, &fatent, old, wait); + ret = fat_ent_write(inode, &fatent, new_dclus, ret, + wait); fatent_brelse(&fatent); } if (ret < 0) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index be09e766ac1f..2fdc5403b10b 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -925,6 +925,33 @@ static int ocfs2_validate_extent_block(struct super_block *sb, goto bail; } + /* + * Extent blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(eb->h_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + rc = ocfs2_error(sb, + "Extent block #%llu has an invalid h_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_suballoc_slot)); + goto bail; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(eb->h_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, + "Extent block #%llu has an invalid h_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(eb->h_suballoc_bit)); + goto bail; + } + if (le16_to_cpu(eb->h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) { rc = ocfs2_error(sb, "Extent block #%llu has invalid l_count %u (expected %u)\n", diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 0075e1624310..55c4a305a282 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -605,6 +605,49 @@ static int ocfs2_validate_dx_root(struct super_block *sb, goto bail; } + if (le64_to_cpu(dx_root->dr_blkno) != bh->b_blocknr) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has an invalid dr_blkno of %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(dx_root->dr_blkno)); + goto bail; + } + + if (le32_to_cpu(dx_root->dr_fs_generation) != OCFS2_SB(sb)->fs_generation) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has an invalid dr_fs_generation of #%u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(dx_root->dr_fs_generation)); + goto bail; + } + + /* + * Dir index root blocks are allocated from a per-slot suballocator, + * so the slot must be in range. Otherwise removing the index passes + * it to get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(dx_root->dr_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid dr_suballoc_slot %u\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dx_root->dr_suballoc_slot)); + goto bail; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise removing the index will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(dx_root->dr_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid dr_suballoc_bit %u\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dx_root->dr_suballoc_bit)); + goto bail; + } + if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { struct ocfs2_extent_list *el = &dx_root->dr_list; @@ -690,6 +733,18 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb, return ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", 7, dx_leaf->dl_signature); + if (le64_to_cpu(dx_leaf->dl_blkno) != bh->b_blocknr) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has an invalid dl_blkno of %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno)); + + if (le32_to_cpu(dx_leaf->dl_fs_generation) != OCFS2_SB(sb)->fs_generation) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has an invalid dl_fs_generation of #%u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(dx_leaf->dl_fs_generation)); + if (le16_to_cpu(dx_leaf->dl_list.de_count) != ocfs2_dx_entries_per_leaf(sb)) return ocfs2_error(sb, diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index a23dd8f86c89..cf3318b0d3a8 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3525,6 +3525,10 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb, struct ocfs2_mask_waiter mw; unsigned long flags, flags2; + /* We didn't get anywhere near actually using this lockres. */ + if (!(lockres->l_flags & OCFS2_LOCK_INITIALIZED)) + return; + ocfs2_init_mask_waiter(&mw); spin_lock_irqsave(&lockres->l_lock, flags); diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 180107a11046..92f3450010fb 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -638,14 +638,18 @@ static int ocfs2_read_locked_inode(struct inode *inode, fe = (struct ocfs2_dinode *) bh->b_data; /* - * This is a code bug. Right now the caller needs to - * understand whether it is asking for a system file inode or - * not so the proper lock names can be built. + * The caller must know whether it is asking for a system file inode + * or not so the proper lock names can be built. Since i_flags comes + * from disk, a mismatch is filesystem corruption instead of a code + * bug, so handle it with ocfs2_error() rather than BUG_ON(). */ - mlog_bug_on_msg(!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) != - !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE), - "Inode %llu: system file state is ambiguous\n", - (unsigned long long)args->fi_blkno); + if (!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) != + !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE)) { + status = ocfs2_error(osb->sb, + "Inode %llu: system file state is ambiguous\n", + (unsigned long long)args->fi_blkno); + goto bail; + } if (S_ISCHR(le16_to_cpu(fe->i_mode)) || S_ISBLK(le16_to_cpu(fe->i_mode))) @@ -1520,14 +1524,45 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } - if (le16_to_cpu(di->i_suballoc_slot) != (u16)OCFS2_INVALID_SLOT && - (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) { + /* + * Only system inodes created by mkfs.ocfs2 are allocated from the + * global allocator and thus legitimately carry OCFS2_INVALID_SLOT. + * Regular inodes are always allocated from a per-slot suballocator. + * If a regular inode with OCFS2_INVALID_SLOT was accepted here, + * deleting it would pass the slot to get_local_system_inode() via + * ocfs2_remove_inode() and trigger BUG_ON(slot == OCFS2_INVALID_SLOT). + */ + if (le16_to_cpu(di->i_suballoc_slot) == (u16)OCFS2_INVALID_SLOT) { + if (!(le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: suballoc slot %u for non-system inode\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_suballoc_slot)); + goto bail; + } + } else if ((u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) { rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n", (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_suballoc_slot)); goto bail; } + /* + * A suballocator block group bitmap is contained in a single block + * and starts after the group descriptor header, so a valid suballoc + * bit can never exceed ocfs2_suballoc_bits_per_block(). Otherwise + * deleting the inode will pass the oversized bit to + * _ocfs2_free_suballoc_bits() via ocfs2_free_dinode() and trigger + * BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)), since any + * group holds at most ocfs2_suballoc_bits_per_block() bits. + */ + if (le16_to_cpu(di->i_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc bit %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_suballoc_bit)); + goto bail; + } + if ((le32_to_cpu(di->i_flags) & OCFS2_ORPHANED_FL) && le16_to_cpu(di->i_orphaned_slot) >= OCFS2_SB(sb)->max_slots) { rc = ocfs2_error(sb, "Invalid dinode %llu: orphaned slot %u\n", diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index d8afbc1a76bb..a3938a03e93b 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -156,7 +156,7 @@ static void ocfs2_queue_replay_slots(struct ocfs2_super *osb, replay_map->rm_state = REPLAY_DONE; } -void ocfs2_free_replay_slots(struct ocfs2_super *osb) +static void ocfs2_free_replay_slots(struct ocfs2_super *osb) { struct ocfs2_replay_map *replay_map = osb->replay_map; @@ -243,6 +243,7 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb) /* XXX: Should we bug if there are dirty entries? */ kfree(rm); + ocfs2_free_replay_slots(osb); } static int __ocfs2_recovery_map_test(struct ocfs2_super *osb, diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index f8b3b2a3d630..19fc920d26b1 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -151,7 +151,6 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb); void ocfs2_recovery_disable_quota(struct ocfs2_super *osb); int ocfs2_compute_replay_slots(struct ocfs2_super *osb); -void ocfs2_free_replay_slots(struct ocfs2_super *osb); /* * Journal Control: * Initialize, Load, Shutdown, Wipe a journal. diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 62cad6522c7a..e3bb3cc0b25a 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -502,6 +502,11 @@ struct ocfs2_super */ struct workqueue_struct *ocfs2_wq; + /* deferred reclaim of fully freed suballocator block groups */ + spinlock_t os_suballoc_reclaim_lock; + struct list_head os_suballoc_reclaim_list; + struct work_struct os_suballoc_reclaim_work; + /* sysfs directory per partition */ struct kset *osb_dev_kset; @@ -588,6 +593,18 @@ static inline int ocfs2_supports_discontig_bg(struct ocfs2_super *osb) return 0; } +/* + * A suballocator block group bitmap starts right after the group + * descriptor header, so a suballoc bit can never exceed this number + * of bits. Derive it from ocfs2_group_bitmap_size() which also caps + * it at OCFS2_MAX_BG_BITMAP_SIZE when discontig_bg is enabled. + */ +static inline u32 ocfs2_suballoc_bits_per_block(struct super_block *sb) +{ + return ocfs2_group_bitmap_size(sb, 1, + OCFS2_SB(sb)->s_feature_incompat) * 8; +} + static inline unsigned int ocfs2_link_max(struct ocfs2_super *osb) { if (ocfs2_supports_indexed_dirs(osb)) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index f55810c59b1b..d351cda9211f 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -1071,10 +1071,13 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( goto out; } + if (list_empty(&oinfo->dqi_chunk)) + chunk->qc_num = 0; + else + chunk->qc_num = list_entry(oinfo->dqi_chunk.prev, + struct ocfs2_quota_chunk, + qc_chunk)->qc_num + 1; list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk); - chunk->qc_num = list_entry(chunk->qc_chunk.prev, - struct ocfs2_quota_chunk, - qc_chunk)->qc_num + 1; chunk->qc_headerbh = bh; *offset = 0; return chunk; diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index d9f22b4a2654..3e9cccf06e48 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -118,6 +118,33 @@ static int ocfs2_validate_refcount_block(struct super_block *sb, } /* + * Refcount blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(rb->rf_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rf_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_suballoc_slot)); + goto out; + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(rb->rf_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rf_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_suballoc_bit)); + goto out; + } + + /* * rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when * this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL); * in that case the same union bytes hold an extent list (rf_list) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 20c3aec6b987..624152e4f7fe 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -2687,16 +2687,24 @@ bail: * cleanup rec/alloc_inode job, then switches to the main bitmap * to reclaim released space. * + * Callers must hold inode_lock() and ocfs2_inode_lock() on + * main_bm_inode, i.e. the global bitmap inode locks must be taken + * before starting the transaction. + * * handle: The transaction handle * alloc_inode: The suballoc inode * alloc_bh: The buffer_head of suballoc inode * group_bh: The group descriptor buffer_head of suballocator managed. - * Caller should release the input group_bh. + * This function takes ownership of it and will release it. + * main_bm_inode: The global bitmap inode + * main_bm_bh: The buffer_head of the global bitmap inode */ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, struct inode *alloc_inode, struct buffer_head *alloc_bh, - struct buffer_head *group_bh) + struct buffer_head *group_bh, + struct inode *main_bm_inode, + struct buffer_head *main_bm_bh) { int idx, status = 0; int i, next_free_rec, len = 0; @@ -2706,8 +2714,6 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, u64 bg_blkno, start_blk; unsigned int count; struct ocfs2_chain_rec *rec; - struct buffer_head *main_bm_bh = NULL; - struct inode *main_bm_inode = NULL; struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data; struct ocfs2_chain_list *cl = &fe->id2.i_chain; @@ -2794,24 +2800,12 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_remove_from_cache(INODE_CACHE(alloc_inode), group_bh); memset(group, 0, sizeof(struct ocfs2_group_desc)); - /* prepare job for reclaim clusters */ - main_bm_inode = ocfs2_get_system_file_inode(osb, - GLOBAL_BITMAP_SYSTEM_INODE, - OCFS2_INVALID_SLOT); - if (!main_bm_inode) - goto bail; /* ignore the error in reclaim path */ - - inode_lock(main_bm_inode); - - status = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 1); - if (status < 0) - goto free_bm_inode; /* ignore the error in reclaim path */ - ocfs2_block_to_cluster_group(main_bm_inode, start_blk, &bg_blkno, &start_bit); fe = (struct ocfs2_dinode *) main_bm_bh->b_data; cl = &fe->id2.i_chain; - /* reuse group_bh, caller will release the input group_bh */ + /* release the suballocator group descriptor before reuse */ + brelse(group_bh); group_bh = NULL; /* reclaim clusters to global_bitmap */ @@ -2819,7 +2813,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, &group_bh); if (status < 0) { mlog_errno(status); - goto free_bm_bh; + goto bail; } group = (struct ocfs2_group_desc *) group_bh->b_data; @@ -2827,7 +2821,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_error(alloc_inode->i_sb, "reclaim length (%d) beyands block group length (%d)", count + start_bit, le16_to_cpu(group->bg_bits)); - goto free_group_bh; + goto bail; } old_bg_contig_free_bits = group->bg_contig_free_bits; @@ -2837,7 +2831,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, _ocfs2_clear_bit); if (status < 0) { mlog_errno(status); - goto free_group_bh; + goto bail; } status = ocfs2_journal_access_di(handle, INODE_CACHE(main_bm_inode), @@ -2847,7 +2841,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, ocfs2_block_group_set_bits(handle, main_bm_inode, group, group_bh, start_bit, count, le16_to_cpu(old_bg_contig_free_bits), 1); - goto free_group_bh; + goto bail; } idx = le16_to_cpu(group->bg_chain); @@ -2858,19 +2852,168 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - count); ocfs2_journal_dirty(handle, main_bm_bh); -free_group_bh: +bail: brelse(group_bh); + return status; +} + +/* + * When a suballocator block group becomes fully freed, its space is + * reclaimed back to the global bitmap. Taking the global bitmap inode + * lock inside the freeing transaction would create a lock dependency + * of "j_trans_barrier -> global bitmap inode i_rwsem", which forms a + * circular dependency with paths like ocfs2_shutdown_local_alloc() that + * take the inode lock before starting a transaction, and can lead to a + * real deadlock with the ocfs2cmt journal commit thread. So queue the + * reclaim to the workqueue and let it run outside the freeing + * transaction. + */ +struct ocfs2_suballoc_reclaim_work { + struct list_head list; + struct inode *alloc_inode; + u64 bg_blkno; +}; -free_bm_bh: +static void ocfs2_queue_suballoc_reclaim(struct ocfs2_super *osb, + struct inode *alloc_inode, + u64 bg_blkno) +{ + struct ocfs2_suballoc_reclaim_work *reclaim_work; + + reclaim_work = kmalloc_obj(*reclaim_work, GFP_NOFS); + if (!reclaim_work) { + /* + * Reclaim is only a space return optimization. If we can't + * queue it, the freed block group just stays owned by the + * suballocator. + */ + return; + } + + igrab(alloc_inode); + reclaim_work->alloc_inode = alloc_inode; + reclaim_work->bg_blkno = bg_blkno; + + spin_lock(&osb->os_suballoc_reclaim_lock); + list_add_tail(&reclaim_work->list, &osb->os_suballoc_reclaim_list); + spin_unlock(&osb->os_suballoc_reclaim_lock); + + queue_work(osb->ocfs2_wq, &osb->os_suballoc_reclaim_work); +} + +static void ocfs2_do_suballoc_reclaim(struct ocfs2_super *osb, + struct ocfs2_suballoc_reclaim_work *reclaim_work) +{ + int status, i; + handle_t *handle; + struct inode *alloc_inode = reclaim_work->alloc_inode; + struct inode *main_bm_inode; + struct buffer_head *alloc_bh = NULL, *group_bh = NULL; + struct buffer_head *main_bm_bh = NULL; + struct ocfs2_dinode *fe; + struct ocfs2_chain_list *cl; + struct ocfs2_chain_rec *rec; + + /* journal already gone, e.g. during dismount cleanup */ + if (!osb->journal) + return; + + inode_lock(alloc_inode); + status = ocfs2_inode_lock(alloc_inode, &alloc_bh, 1); + if (status < 0) + goto out_alloc; + + fe = (struct ocfs2_dinode *) alloc_bh->b_data; + cl = &fe->id2.i_chain; + + /* + * The block group may have been allocated from again since the + * reclaim work was queued, re-check that it is still fully freed. + * A stale work item can also reference a group that is no longer + * chained, whose descriptor would fail validation and trigger a + * spurious ocfs2_error(), so verify chain membership first. + */ + for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) { + rec = &cl->cl_recs[i]; + if (le64_to_cpu(rec->c_blkno) == reclaim_work->bg_blkno) + break; + } + if (i == le16_to_cpu(cl->cl_next_free_rec) || + ocfs2_is_cluster_bitmap(alloc_inode) || + (le32_to_cpu(rec->c_free) != (le32_to_cpu(rec->c_total) - 1)) || + (le16_to_cpu(cl->cl_next_free_rec) == 1)) + goto out_alloc_unlock; + + status = ocfs2_read_group_descriptor(alloc_inode, fe, + reclaim_work->bg_blkno, &group_bh); + if (status < 0) + goto out_alloc_unlock; + + main_bm_inode = ocfs2_get_system_file_inode(osb, + GLOBAL_BITMAP_SYSTEM_INODE, + OCFS2_INVALID_SLOT); + if (!main_bm_inode) + goto out_group; + + inode_lock(main_bm_inode); + status = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 1); + if (status < 0) + goto out_main; + + handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE); + if (IS_ERR(handle)) { + status = PTR_ERR(handle); + mlog_errno(status); + goto out_main_unlock; + } + + status = _ocfs2_reclaim_suballoc_to_main(handle, alloc_inode, + alloc_bh, group_bh, + main_bm_inode, main_bm_bh); + /* group_bh ownership passed to _ocfs2_reclaim_suballoc_to_main() */ + group_bh = NULL; + if (status < 0) + mlog_errno(status); + + ocfs2_commit_trans(osb, handle); + +out_main_unlock: ocfs2_inode_unlock(main_bm_inode, 1); brelse(main_bm_bh); - -free_bm_inode: +out_main: inode_unlock(main_bm_inode); iput(main_bm_inode); +out_group: + brelse(group_bh); +out_alloc_unlock: + ocfs2_inode_unlock(alloc_inode, 1); + brelse(alloc_bh); +out_alloc: + inode_unlock(alloc_inode); +} -bail: - return status; +void ocfs2_suballoc_reclaim_worker(struct work_struct *work) +{ + struct ocfs2_super *osb = container_of(work, struct ocfs2_super, + os_suballoc_reclaim_work); + struct ocfs2_suballoc_reclaim_work *reclaim_work; + + while (1) { + spin_lock(&osb->os_suballoc_reclaim_lock); + if (list_empty(&osb->os_suballoc_reclaim_list)) { + spin_unlock(&osb->os_suballoc_reclaim_lock); + break; + } + reclaim_work = list_first_entry(&osb->os_suballoc_reclaim_list, + struct ocfs2_suballoc_reclaim_work, + list); + list_del(&reclaim_work->list); + spin_unlock(&osb->os_suballoc_reclaim_lock); + + ocfs2_do_suballoc_reclaim(osb, reclaim_work); + iput(reclaim_work->alloc_inode); + kfree(reclaim_work); + } } /* @@ -2897,10 +3040,22 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, /* The alloc_bh comes from ocfs2_free_dinode() or * ocfs2_free_clusters(). The callers have all locked the * allocator and gotten alloc_bh from the lock call. This - * validates the dinode buffer. Any corruption that has happened - * is a code bug. */ + * validates the dinode buffer. */ BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); - BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)); + + /* + * ocfs2_bits_per_group() is derived from cl_cpg and cl_bpc of the + * allocator dinode, which are not validated against the volume + * geometry. A corrupted image can carry a suballoc bit beyond it, + * so error out instead of crashing. + */ + if ((count + start_bit) > ocfs2_bits_per_group(cl)) { + return ocfs2_error(alloc_inode->i_sb, + "Allocator #%llu: freeing bits %u+%u exceeds bits per group %u\n", + (unsigned long long)le64_to_cpu(fe->i_blkno), + count, start_bit, + ocfs2_bits_per_group(cl)); + } trace_ocfs2_free_suballoc_bits( (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno, @@ -2915,7 +3070,20 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, } group = (struct ocfs2_group_desc *) group_bh->b_data; - BUG_ON((count + start_bit) > le16_to_cpu(group->bg_bits)); + /* + * Group descriptor validation only guarantees bg_bits within the + * physical bitmap size, so double check the freeing range here. + * Otherwise ocfs2_block_group_clear_bits() would clear bits beyond + * bg_bitmap. + */ + if ((count + start_bit) > le16_to_cpu(group->bg_bits)) { + status = ocfs2_error(alloc_inode->i_sb, + "Group descriptor #%llu has %u bits, cannot free bits %u+%u\n", + (unsigned long long)le64_to_cpu(group->bg_blkno), + le16_to_cpu(group->bg_bits), + count, start_bit); + goto bail; + } if (ocfs2_is_cluster_bitmap(alloc_inode)) old_bg_contig_free_bits = group->bg_contig_free_bits; @@ -2955,7 +3123,8 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, goto bail; } - _ocfs2_reclaim_suballoc_to_main(handle, alloc_inode, alloc_bh, group_bh); + ocfs2_queue_suballoc_reclaim(OCFS2_SB(alloc_inode->i_sb), alloc_inode, + bg_blkno); bail: brelse(group_bh); diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index bcf2ed4a8631..6042abc032f9 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -206,7 +206,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et, int ocfs2_test_inode_bit(struct ocfs2_super *osb, u64 blkno, int *res); - +void ocfs2_suballoc_reclaim_worker(struct work_struct *work); /* * The following two interfaces are for ocfs2_create_inode_in_orphan(). diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index c62e389d4dd6..1e76b1d9fe0a 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1162,15 +1162,23 @@ static int ocfs2_fill_super(struct super_block *sb, struct fs_context *fc) out_dismount: atomic_set(&osb->vol_state, VOLUME_DISABLED); wake_up(&osb->osb_mount_event); - ocfs2_free_replay_slots(osb); ocfs2_dismount_volume(sb, 1); goto out; out_debugfs: debugfs_remove_recursive(osb->osb_debug_root); out_super: + /* + * A recovery thread launched by a node failure event may still be + * waiting for the volume to be mounted. Set VOLUME_DISABLED and + * wake it up, then wait for it to exit before osb is freed, + * otherwise the kthread would leak and stay blocked on the wait + * queue embedded in the freed osb. + */ + atomic_set(&osb->vol_state, VOLUME_DISABLED); + wake_up(&osb->osb_mount_event); ocfs2_release_system_inodes(osb); - kfree(osb->recovery_map); + ocfs2_recovery_exit(osb); ocfs2_delete_osb(osb); kfree(osb); out: @@ -1767,17 +1775,18 @@ static int ocfs2_mount_volume(struct super_block *sb) status = ocfs2_truncate_log_init(osb); if (status < 0) { mlog_errno(status); - goto out_check_volume; + goto out_system_inodes; } ocfs2_super_unlock(osb, 1); return 0; -out_check_volume: - ocfs2_free_replay_slots(osb); out_system_inodes: if (osb->local_alloc_state == OCFS2_LA_ENABLED) ocfs2_shutdown_local_alloc(osb); + /* Drain pending suballoc reclaim work before the journal goes away */ + if (osb->ocfs2_wq) + flush_workqueue(osb->ocfs2_wq); ocfs2_release_system_inodes(osb); /* before journal shutdown, we should release slot_info */ ocfs2_free_slot_info(osb); @@ -1848,6 +1857,14 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) if (osb->cconn) ocfs2_super_unlock(osb, 1); + /* + * Drain pending suballoc reclaim work while the system inodes and + * the journal are still alive, since the worker needs to look up + * the global bitmap inode and start a transaction. + */ + if (osb->ocfs2_wq) + flush_workqueue(osb->ocfs2_wq); + ocfs2_release_system_inodes(osb); ocfs2_journal_shutdown(osb); @@ -2134,6 +2151,11 @@ static int ocfs2_initialize_super(struct super_block *sb, INIT_WORK(&osb->dquot_drop_work, ocfs2_drop_dquot_refs); init_llist_head(&osb->dquot_drop_list); + spin_lock_init(&osb->os_suballoc_reclaim_lock); + INIT_LIST_HEAD(&osb->os_suballoc_reclaim_list); + INIT_WORK(&osb->os_suballoc_reclaim_work, + ocfs2_suballoc_reclaim_worker); + /* get some pseudo constants for clustersize bits */ osb->s_clustersize_bits = le32_to_cpu(di->id2.i_super.s_clustersize_bits); diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index bfafe059bedf..a2d6c3d0f2e8 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -237,6 +237,21 @@ static int namevalue_size_xe(struct ocfs2_xattr_entry *xe) return namevalue_size(xe->xe_name_len, value_len); } +static int ocfs2_validate_xattr_entry(struct super_block *sb, u64 blkno, + struct ocfs2_xattr_entry *xe) +{ + u64 value_len = le64_to_cpu(xe->xe_value_size); + + if (value_len > OCFS2_XATTR_INLINE_SIZE && + ocfs2_xattr_is_local(xe)) + return ocfs2_error(sb, + "Invalid local xattr in block %llu: value size %llu\n", + (unsigned long long)blkno, + (unsigned long long)value_len); + + return 0; +} + static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb, struct ocfs2_xattr_header *xh, @@ -517,6 +532,31 @@ static int ocfs2_validate_xattr_block(struct super_block *sb, le32_to_cpu(xb->xb_fs_generation)); } + /* + * Xattr blocks are allocated from a per-slot suballocator, so the + * slot must be in range. Otherwise freeing the block passes it to + * get_local_system_inode(), which hits BUG_ON() for + * OCFS2_INVALID_SLOT or computes an out-of-bounds index otherwise. + */ + if ((u32)le16_to_cpu(xb->xb_suballoc_slot) >= OCFS2_SB(sb)->max_slots) { + return ocfs2_error(sb, + "Extended attribute block #%llu has an invalid xb_suballoc_slot of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(xb->xb_suballoc_slot)); + } + + /* + * Similarly the suballoc bit must fit in a block group bitmap. + * Otherwise freeing the block will pass the oversized bit to + * _ocfs2_free_suballoc_bits() and trigger ocfs2_error() there. + */ + if (le16_to_cpu(xb->xb_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) { + return ocfs2_error(sb, + "Extended attribute block #%llu has an invalid xb_suballoc_bit of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(xb->xb_suballoc_bit)); + } + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { size_t region_offset = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); @@ -989,7 +1029,7 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, size_t entries_limit = region_size; size_t nv_limit = region_size; size_t max_entries; - int i; + int i, ret; if (region_size < sizeof(*xh)) return ocfs2_error(sb, @@ -1009,6 +1049,11 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, struct ocfs2_xattr_entry *xe = &xh->xh_entries[i]; size_t name_offset = le16_to_cpu(xe->xe_name_offset); size_t value_offset; + u64 value_len = le64_to_cpu(xe->xe_value_size); + + ret = ocfs2_validate_xattr_entry(sb, blkno, xe); + if (ret) + return ret; if (name_offset > nv_limit || xe->xe_name_len > nv_limit - name_offset) @@ -1023,8 +1068,7 @@ static int ocfs2_validate_xattr_entries_flat(struct super_block *sb, u64 blkno, (unsigned long long)blkno, i); if (ocfs2_xattr_is_local(xe)) { - if (le64_to_cpu(xe->xe_value_size) > - nv_limit - value_offset) + if (value_len > nv_limit - value_offset) return ocfs2_error(sb, "Invalid xattr in block %llu: entry %d value is out of bounds\n", (unsigned long long)blkno, @@ -1106,10 +1150,29 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, struct ocfs2_xattr_header *xh = bucket_xh(bucket); u16 xattr_count = le16_to_cpu(xh->xh_count); size_t region_size = (size_t)sb->s_blocksize * bucket->bu_blocks; - size_t entries_limit = sb->s_blocksize; + /* + * The entry array grows up from the header across the whole + * bucket region, so it may extend beyond the first bucket block + * when the blocksize is smaller than OCFS2_XATTR_BUCKET_SIZE. + * Name/value pairs, however, always live within a single block. + */ + size_t entries_limit = region_size; size_t nv_limit = sb->s_blocksize; size_t max_entries; - int i; + int i, ret; + + /* + * The entry array is one contiguous region that may span the + * bucket's buffer_heads. Buckets are allocated within clusters, + * so their first block is always aligned to + * OCFS2_XATTR_BUCKET_SIZE and the whole bucket fits in one page. + * A corrupted xattr tree can point a bucket at blocks straddling + * a page, so reject it before touching the entry array. + */ + if (blkno & (bucket->bu_blocks - 1)) + return ocfs2_error(sb, + "Invalid xattr bucket %llu: unaligned block number\n", + (unsigned long long)blkno); if (region_size < sizeof(*xh)) return ocfs2_error(sb, @@ -1137,6 +1200,11 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, size_t block_off = name_offset >> sb->s_blocksize_bits; size_t block_offset = name_offset % nv_limit; size_t value_offset; + u64 value_len = le64_to_cpu(xe->xe_value_size); + + ret = ocfs2_validate_xattr_entry(sb, blkno, xe); + if (ret) + return ret; if (name_offset >= region_size || block_off >= bucket->bu_blocks) return ocfs2_error(sb, @@ -1155,8 +1223,7 @@ static int ocfs2_validate_xattr_bucket(struct ocfs2_xattr_bucket *bucket, (unsigned long long)blkno, i); if (ocfs2_xattr_is_local(xe)) { - if (le64_to_cpu(xe->xe_value_size) > - nv_limit - value_offset) + if (value_len > nv_limit - value_offset) return ocfs2_error(sb, "Invalid xattr bucket %llu: entry %d value is out of bounds\n", (unsigned long long)blkno, @@ -1304,7 +1371,7 @@ static int ocfs2_xattr_find_entry(struct inode *inode, int name_index, { struct ocfs2_xattr_entry *entry; size_t name_len; - int i, name_offset, cmp = 1; + int i, name_offset, cmp = 1, ret; if (name == NULL) return -EINVAL; @@ -1327,6 +1394,12 @@ static int ocfs2_xattr_find_entry(struct inode *inode, int name_index, return -EFSCORRUPTED; } cmp = memcmp(name, (xs->base + name_offset), name_len); + if (!cmp) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, + OCFS2_I(inode)->ip_blkno, entry); + if (ret) + return ret; + } } if (cmp == 0) break; @@ -2909,6 +2982,9 @@ static int ocfs2_xattr_has_space_inline(struct inode *inode, * * Find extended attribute in inode block and * fill search info into struct ocfs2_xattr_search. + * + * The inline free-space check races with truncate and allocation, so + * callers must hold ip_alloc_sem for writing. */ static int ocfs2_xattr_ibody_find(struct inode *inode, int name_index, @@ -2920,13 +2996,13 @@ static int ocfs2_xattr_ibody_find(struct inode *inode, int ret; int has_space = 0; + lockdep_assert_held_write(&oi->ip_alloc_sem); + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return 0; if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { - down_read(&oi->ip_alloc_sem); has_space = ocfs2_xattr_has_space_inline(inode, di); - up_read(&oi->ip_alloc_sem); if (!has_space) return 0; } @@ -3007,6 +3083,7 @@ out: * * Set, replace or remove an extended attribute into inode block. * + * Callers must hold ip_alloc_sem for writing. */ static int ocfs2_xattr_ibody_set(struct inode *inode, struct ocfs2_xattr_info *xi, @@ -3017,16 +3094,17 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_xa_loc loc; + lockdep_assert_held_write(&oi->ip_alloc_sem); + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return -ENOSPC; - down_write(&oi->ip_alloc_sem); if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { ret = ocfs2_xattr_ibody_init(inode, xs->inode_bh, ctxt); if (ret) { if (ret != -ENOSPC) mlog_errno(ret); - goto out; + return ret; } } @@ -3036,13 +3114,10 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, if (ret) { if (ret != -ENOSPC) mlog_errno(ret); - goto out; + return ret; } xs->here = loc.xl_entry; -out: - up_write(&oi->ip_alloc_sem); - return ret; } @@ -3687,6 +3762,18 @@ out: } /* + * ip_alloc_sem subclass for inodes being initialized before publication. + * ocfs2_xattr_set_handle() runs inside the create transaction, so taking + * ip_alloc_sem there adds a transaction -> ip_alloc_sem order that would + * form a lockdep cycle with the ip_alloc_sem -> transaction order used + * elsewhere, if not for this separate subclass. The inode is unpublished + * so the acquisition can never contend. + */ +enum { + OCFS2_IP_ALLOC_SEM_UNPUBLISHED = 1, +}; + +/* * This helper is only for setting initial ACL or security xattrs on an inode * that is still unpublished, unhashed, and unattached to a dentry. * Ordinary xattr updates must use ocfs2_xattr_set(). @@ -3747,6 +3834,13 @@ int ocfs2_xattr_set_handle(handle_t *handle, xis.inode_bh = xbs.inode_bh = di_bh; di = (struct ocfs2_dinode *)di_bh->b_data; + /* + * The inode is unpublished and cannot contend, but take the + * semaphore anyway so the helpers' lockdep assertions hold. + */ + down_write_nested(&OCFS2_I(inode)->ip_alloc_sem, + OCFS2_IP_ALLOC_SEM_UNPUBLISHED); + ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); if (ret) goto cleanup; @@ -3759,6 +3853,7 @@ int ocfs2_xattr_set_handle(handle_t *handle, ret = __ocfs2_xattr_set_handle(inode, di, &xi, &xis, &xbs, &ctxt); cleanup: + up_write(&OCFS2_I(inode)->ip_alloc_sem); brelse(xbs.xattr_bh); ocfs2_xattr_bucket_free(xbs.bucket); @@ -3828,29 +3923,37 @@ int ocfs2_xattr_set(struct inode *inode, down_write(&OCFS2_I(inode)->ip_xattr_sem); /* + * The allocation and truncate paths take ip_alloc_sem before + * starting a transaction, so take it here before xattr + * preparation, allocation reservations and ocfs2_start_trans() + * to keep that order. The xattr helpers below no longer take + * it themselves. + */ + down_write(&OCFS2_I(inode)->ip_alloc_sem); + /* * Scan inode and external block to find the same name * extended attribute and collect search information. */ ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); if (ret) - goto cleanup; + goto out_free_ac; if (xis.not_found) { ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs); if (ret) - goto cleanup; + goto out_free_ac; } if (xis.not_found && xbs.not_found) { ret = -ENODATA; if (flags & XATTR_REPLACE) - goto cleanup; + goto out_free_ac; ret = 0; if (!value) - goto cleanup; + goto out_free_ac; } else { ret = -EEXIST; if (flags & XATTR_CREATE) - goto cleanup; + goto out_free_ac; } /* Check whether the value is refcounted and do some preparation. */ @@ -3861,7 +3964,7 @@ int ocfs2_xattr_set(struct inode *inode, &ref_meta, &ref_credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_free_ac; } } @@ -3872,7 +3975,7 @@ int ocfs2_xattr_set(struct inode *inode, if (ret < 0) { inode_unlock(tl_inode); mlog_errno(ret); - goto cleanup; + goto out_free_ac; } } inode_unlock(tl_inode); @@ -3881,7 +3984,7 @@ int ocfs2_xattr_set(struct inode *inode, &xbs, &ctxt, ref_meta, &credits); if (ret) { mlog_errno(ret); - goto cleanup; + goto out_free_ac; } /* we need to update inode's ctime field, so add credit for it. */ @@ -3899,6 +4002,7 @@ int ocfs2_xattr_set(struct inode *inode, ocfs2_commit_trans(osb, ctxt.handle); out_free_ac: + up_write(&OCFS2_I(inode)->ip_alloc_sem); if (ctxt.data_ac) ocfs2_free_alloc_context(ctxt.data_ac); if (ctxt.meta_ac) @@ -3907,7 +4011,6 @@ out_free_ac: ocfs2_schedule_truncate_log_flush(osb, 1); ocfs2_run_deallocs(osb, &ctxt.dealloc); -cleanup: if (ref_tree) ocfs2_unlock_refcount_tree(osb, ref_tree, 1); up_write(&OCFS2_I(inode)->ip_xattr_sem); @@ -4038,6 +4141,10 @@ static int ocfs2_find_xe_in_bucket(struct inode *inode, xe_name = bucket_block(bucket, block_off) + new_offset; if (!memcmp(name, xe_name, name_len)) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, + OCFS2_I(inode)->ip_blkno, xe); + if (ret) + break; *xe_index = i; *found = 1; ret = 0; @@ -4508,6 +4615,10 @@ static void ocfs2_xattr_update_xattr_search(struct inode *inode, xs->here = &xs->header->xh_entries[i]; } +/* + * Caller must hold ip_alloc_sem for writing, since a new xattr block + * is allocated and the xattr block header is rewritten. + */ static int ocfs2_xattr_create_index_block(struct inode *inode, struct ocfs2_xattr_search *xs, struct ocfs2_xattr_set_ctxt *ctxt) @@ -4523,19 +4634,14 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, struct ocfs2_xattr_tree_root *xr; u16 xb_flags = le16_to_cpu(xb->xb_flags); + lockdep_assert_held_write(&oi->ip_alloc_sem); + trace_ocfs2_xattr_create_index_block_begin( (unsigned long long)xb_bh->b_blocknr); BUG_ON(xb_flags & OCFS2_XATTR_INDEXED); BUG_ON(!xs->bucket); - /* - * XXX: - * We can use this lock for now, and maybe move to a dedicated mutex - * if performance becomes a problem later. - */ - down_write(&oi->ip_alloc_sem); - ret = ocfs2_journal_access_xb(handle, INODE_CACHE(inode), xb_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { @@ -4597,8 +4703,6 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, ocfs2_journal_dirty(handle, xb_bh); out: - up_write(&oi->ip_alloc_sem); - return ret; } @@ -4678,6 +4782,9 @@ static int ocfs2_defrag_xattr_bucket(struct inode *inode, xe = xh->xh_entries; end = OCFS2_XATTR_BUCKET_SIZE; for (i = 0; i < le16_to_cpu(xh->xh_count); i++, xe++) { + ret = ocfs2_validate_xattr_entry(inode->i_sb, blkno, xe); + if (ret) + goto out; offset = le16_to_cpu(xe->xe_name_offset); len = namevalue_size_xe(xe); @@ -4694,16 +4801,22 @@ static int ocfs2_defrag_xattr_bucket(struct inode *inode, memmove(bucket_buf + end - len, bucket_buf + offset, len); xe->xe_name_offset = cpu_to_le16(end - len); + } else if (end < offset + len) { + ret = ocfs2_error(inode->i_sb, + "Defrag check failed for bucket %llu\n", + (unsigned long long)blkno); + goto out; } - mlog_bug_on_msg(end < offset + len, "Defrag check failed for " - "bucket %llu\n", (unsigned long long)blkno); - end -= len; } - mlog_bug_on_msg(xh_free_start > end, "Defrag check failed for " - "bucket %llu\n", (unsigned long long)blkno); + if (xh_free_start > end) { + ret = ocfs2_error(inode->i_sb, + "Defrag check failed for bucket %llu\n", + (unsigned long long)blkno); + goto out; + } if (xh_free_start == end) goto out; @@ -4960,6 +5073,9 @@ static int ocfs2_divide_xattr_bucket(struct inode *inode, name_value_len = 0; for (i = 0; i < start; i++) { xe = &xh->xh_entries[i]; + ret = ocfs2_validate_xattr_entry(inode->i_sb, blk, xe); + if (ret) + goto out; name_value_len += namevalue_size_xe(xe); if (le16_to_cpu(xe->xe_name_offset) < name_offset) name_offset = le16_to_cpu(xe->xe_name_offset); diff --git a/fs/proc/base.c b/fs/proc/base.c index 58be38942460..0f9efd25bb05 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2520,17 +2520,23 @@ static int show_timer(struct seq_file *m, void *v) struct k_itimer *timer = hlist_entry((struct hlist_node *)v, struct k_itimer, list); struct timers_private *tp = m->private; int notify = timer->it_sigev_notify; + pid_t nr = 0; guard(spinlock_irq)(&timer->it_lock); if (!posixtimer_valid(timer)) return 0; + if (timer->it_pid && pid_has_task(timer->it_pid, timer->it_pid_type)) + nr = pid_nr_ns(timer->it_pid, tp->ns); + else + notify = SIGEV_NONE; + seq_printf(m, "ID: %d\n", timer->it_id); seq_printf(m, "signal: %d/%px\n", timer->sigq.info.si_signo, timer->sigq.info.si_value.sival_ptr); seq_printf(m, "notify: %s/%s.%d\n", nstr[notify & ~SIGEV_THREAD_ID], (notify & SIGEV_THREAD_ID) ? "tid" : "pid", - pid_nr_ns(timer->it_pid, tp->ns)); + nr); seq_printf(m, "ClockID: %d\n", timer->it_clock); return 0; diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 58fd14c82270..5c74748a53f3 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h @@ -19,6 +19,13 @@ enum fault_flags { #include <linux/ratelimit.h> /* + * Length of the debugfs directory name embedded in struct fault_attr. + * Chosen to accommodate every in-tree caller of fault_create_debugfs_attr() + * (the longest is "fail_dma_array_full", 19 chars) with generous headroom. + */ +#define FAULT_ATTR_DNAME_LEN 64 + +/* * For explanation of the elements of this struct, see * Documentation/fault-injection/fault-injection.rst */ @@ -37,7 +44,7 @@ struct fault_attr { unsigned long count; struct ratelimit_state ratelimit_state; - struct dentry *dname; + char dname[FAULT_ATTR_DNAME_LEN]; }; #define FAULT_ATTR_INITIALIZER { \ @@ -47,7 +54,6 @@ struct fault_attr { .stacktrace_depth = 32, \ .ratelimit_state = RATELIMIT_STATE_INIT_DISABLED, \ .verbose = 2, \ - .dname = NULL, \ } #define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER diff --git a/include/linux/minmax.h b/include/linux/minmax.h index a0158db54a04..5ef4d58c0c42 100644 --- a/include/linux/minmax.h +++ b/include/linux/minmax.h @@ -38,9 +38,9 @@ * Note that 'x' is the original expression, and 'ux' is the unique variable * that contains the value. * - * We use 'ux' for pure type checking, and 'x' for when we need to look at the - * value (but without evaluating it for side effects! - * Careful to only ever evaluate it with sizeof() or __builtin_constant_p() etc). + * We use 'ux' for both the type and the value checks, so 'x' itself is only + * expanded twice: once to initialise 'ux', and once quoted in the error + * message. * * Pointers end up being checked by the normal C type rules at the actual * comparison, and these expressions only need to be careful to not cause diff --git a/include/linux/panic.h b/include/linux/panic.h index f1dd417e54b2..17e61b61c45f 100644 --- a/include/linux/panic.h +++ b/include/linux/panic.h @@ -13,7 +13,8 @@ __printf(1, 2) void panic(const char *fmt, ...) __noreturn __cold; __printf(1, 0) void vpanic(const char *fmt, va_list args) __noreturn __cold; -void nmi_panic(struct pt_regs *regs, const char *msg); +__printf(2, 3) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...); void check_panic_on_warn(const char *origin); extern void oops_enter(void); extern void oops_exit(void); @@ -110,4 +111,6 @@ extern void add_taint(unsigned flag, enum lockdep_ok); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); +void arch_do_panic(void); + #endif /* _LINUX_PANIC_H */ diff --git a/init/main.c b/init/main.c index a87a3d52f3e2..1a3199e695ff 100644 --- a/init/main.c +++ b/init/main.c @@ -548,12 +548,12 @@ static int __init unknown_bootoption(char *param, char *val, /* Environment option */ unsigned int i; for (i = 0; envp_init[i]; i++) { + if (!strncmp(param, envp_init[i], len+1)) + break; if (i == MAX_INIT_ENVS) { panic_later = "env"; panic_param = param; } - if (!strncmp(param, envp_init[i], len+1)) - break; } envp_init[i] = param; } else { @@ -581,7 +581,7 @@ static int __init init_setup(char *str) * the shell think it should execute a script with such name. * So we ignore all arguments entered _before_ init=... [MJ] */ - for (i = 1; i < MAX_INIT_ARGS; i++) + for (i = 1; i <= MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } @@ -594,7 +594,7 @@ static int __init rdinit_setup(char *str) ramdisk_execute_command = str; ramdisk_execute_command_set = true; /* See "auto" comment in init_setup */ - for (i = 1; i < MAX_INIT_ARGS; i++) + for (i = 1; i <= MAX_INIT_ARGS; i++) argv_init[i] = NULL; return 1; } diff --git a/init/version.c b/init/version.c index 94c96f6fbfe6..cbae11b6f768 100644 --- a/init/version.c +++ b/init/version.c @@ -21,13 +21,14 @@ static int __init early_hostname(char *arg) { size_t bufsize = sizeof(init_uts_ns.name.nodename); size_t maxlen = bufsize - 1; - ssize_t arglen; - arglen = strscpy(init_uts_ns.name.nodename, arg, bufsize); - if (arglen < 0) { - pr_warn("hostname parameter exceeds %zd characters and will be truncated", + if (!arg) + return -EINVAL; + + if (strscpy(init_uts_ns.name.nodename, arg, bufsize) < 0) + pr_warn("hostname parameter exceeds %zu characters and will be truncated\n", maxlen); - } + return 0; } early_param("hostname", early_hostname); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index d1dd36a651b0..d1a1965c9811 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -528,6 +528,13 @@ static void mqueue_evict_inode(struct inode *inode) list_add_tail(&msg->m_list, &tmp_msg); kfree(info->node_cache); spin_unlock(&info->lock); + /* + * A shared file table can let the notification owner exit without + * running ->flush(). No users of the inode remain during eviction, so + * tear down any stale notification after dropping info->lock because + * netlink_sendskb() may release the final socket reference. + */ + remove_notification(info); list_for_each_entry_safe(msg, nmsg, &tmp_msg, m_list) { list_del(&msg->m_list); diff --git a/kernel/fork.c b/kernel/fork.c index ceedb07a5a00..ea823b28d625 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -858,7 +858,8 @@ void __init fork_init(void) #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN 0 #endif - int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN); + int align = max3(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN, + __alignof__(struct task_struct)); unsigned long useroffset, usersize; /* create a slab on which task_structs can be allocated */ diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 1d19b1be207a..764918570de1 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -529,7 +529,7 @@ static void init_node(struct gcov_node *node, struct gcov_info *info, } node->parent = parent; if (name) - strcpy(node->name, name); + strscpy(node->name, name, strlen(name) + 1); } /* diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 6fcc94ce4ca9..b8554aedc589 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -57,8 +57,20 @@ unsigned long __read_mostly sysctl_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_ */ static unsigned long __read_mostly sysctl_hung_task_check_interval_secs; +/* + * Limit the number of printed hung tasks to prevent printing + * the same or similar backtraces repeatedly. + */ static int __read_mostly sysctl_hung_task_warnings = 10; +/* + * The number of hung tasks which still can be reported. + * The budget gets restored to the original limit when + * the previous stall is resolved. + */ +static int hung_task_warnings_budget = 10; +static atomic_t reset_hung_task_warnings = ATOMIC_INIT(0); + static int __read_mostly did_panic; static bool hung_task_call_panic; @@ -245,11 +257,11 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, /* * The given task did not get scheduled for more than * CONFIG_DEFAULT_HUNG_TASK_TIMEOUT. Therefore, complain - * accordingly + * accordingly with full details if the budget is not exhausted. */ - if (sysctl_hung_task_warnings || hung_task_call_panic) { - if (sysctl_hung_task_warnings > 0) - sysctl_hung_task_warnings--; + if (hung_task_warnings_budget || hung_task_call_panic) { + if (hung_task_warnings_budget > 0) + hung_task_warnings_budget--; pr_err("INFO: task %s:%d blocked%s for more than %ld seconds.\n", t->comm, t->pid, t->in_iowait ? " in I/O wait" : "", (jiffies - t->last_switch_time) / HZ); @@ -264,8 +276,8 @@ static void hung_task_info(struct task_struct *t, unsigned long timeout, sched_show_task(t); debug_show_blocker(t, timeout); - if (!sysctl_hung_task_warnings) - pr_info("Future hung task reports are suppressed, see sysctl kernel.hung_task_warnings\n"); + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: further per-task details suppressed until warning budget is reset (see sysctl kernel.hung_task_warnings)\n"); } touch_nmi_watchdog(); @@ -304,7 +316,7 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unsigned long last_break = jiffies; struct task_struct *g, *t; unsigned long this_round_count; - int need_warning = sysctl_hung_task_warnings; + int need_warning; unsigned long si_mask = hung_task_si_mask; /* @@ -314,6 +326,11 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) if (test_taint(TAINT_DIE) || did_panic) return; + if (atomic_xchg_acquire(&reset_hung_task_warnings, 0)) + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); + need_warning = hung_task_warnings_budget; + this_round_count = 0; rcu_read_lock(); for_each_process_thread(g, t) { @@ -340,8 +357,15 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) unlock: rcu_read_unlock(); - if (!this_round_count) + if (!this_round_count) { + hung_task_warnings_budget = + READ_ONCE(sysctl_hung_task_warnings); return; + } + + if (!hung_task_warnings_budget && !hung_task_call_panic) + pr_info("hung_task: %lu hung tasks detected, warning budget exhausted\n", + this_round_count); if (need_warning || hung_task_call_panic) { si_mask |= SYS_INFO_LOCKS; @@ -425,6 +449,19 @@ static int proc_dohung_task_timeout_secs(const struct ctl_table *table, int writ return ret; } +static int proc_dohung_task_warnings(const struct ctl_table *table, int write, + void *buffer, + size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!ret && write) + atomic_set_release(&reset_hung_task_warnings, 1); + + return ret; +} + /* * This is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs * and hung_task_check_interval_secs @@ -480,7 +517,7 @@ static const struct ctl_table hung_task_sysctls[] = { .data = &sysctl_hung_task_warnings, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dohung_task_warnings, .extra1 = SYSCTL_NEG_ONE, }, { diff --git a/kernel/panic.c b/kernel/panic.c index 213725b612aa..170744163fc2 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -307,7 +307,7 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); atomic_t panic_redirect_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); #if defined(CONFIG_SMP) && defined(CONFIG_CRASH_DUMP) -static char *panic_force_buf; +static char panic_force_buf[PANIC_MSG_BUFSZ]; static int __init panic_force_cpu_setup(char *str) { @@ -326,17 +326,6 @@ static int __init panic_force_cpu_setup(char *str) } early_param("panic_force_cpu", panic_force_cpu_setup); -static int __init panic_force_cpu_late_init(void) -{ - if (panic_force_cpu < 0) - return 0; - - panic_force_buf = kmalloc(PANIC_MSG_BUFSZ, GFP_KERNEL); - - return 0; -} -late_initcall(panic_force_cpu_late_init); - static void do_panic_on_target_cpu(void *info) { panic("%s", (char *)info); @@ -371,15 +360,16 @@ int __weak panic_smp_redirect_cpu(int target_cpu, void *msg) * for the crash kernel to function correctly. This function redirects * panic handling to the CPU specified via the panic_force_cpu= boot parameter. * - * Returns false if panic should proceed on current CPU. - * Returns true if panic was redirected. + * Returns true when this CPU must stop: the panic was redirected or is + * already running on another CPU. + * Returns false when panic() should proceed on this CPU. */ __printf(1, 0) static bool panic_try_force_cpu(const char *fmt, va_list args) { int this_cpu = raw_smp_processor_id(); int old_cpu = PANIC_CPU_INVALID; - const char *msg; + va_list ap; /* Feature not enabled via boot parameter */ if (panic_force_cpu < 0) @@ -396,27 +386,27 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) return false; } - /* Another panic already in progress */ + /* + * Don't redirect when a panic is already in progress. Stop this + * CPU when it's another one, proceed when it's this one. + */ if (panic_in_progress()) - return false; + return panic_on_other_cpu(); /* - * Only one CPU can do the redirect. Use atomic cmpxchg to ensure - * we don't race with another CPU also trying to redirect. + * Only one CPU can do the redirection. Others should go offline. + * Continue with panic() when we already tried the redirection + * from this CPU before, for example via nmi_panic(). */ if (!atomic_try_cmpxchg(&panic_redirect_cpu, &old_cpu, this_cpu)) - return false; + return old_cpu != this_cpu; /* - * Use dynamically allocated buffer if available, otherwise - * fall back to static message for early boot panics or allocation failure. + * Do not consume args, the caller reuses them if we fail. */ - if (panic_force_buf) { - vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, args); - msg = panic_force_buf; - } else { - msg = "Redirected panic (buffer unavailable)"; - } + va_copy(ap, args); + vsnprintf(panic_force_buf, PANIC_MSG_BUFSZ, fmt, ap); + va_end(ap); console_verbose(); bust_spinlocks(1); @@ -431,7 +421,7 @@ static bool panic_try_force_cpu(const char *fmt, va_list args) dump_stack(); } - if (panic_smp_redirect_cpu(panic_force_cpu, (void *)msg) != 0) { + if (panic_smp_redirect_cpu(panic_force_cpu, panic_force_buf) != 0) { atomic_set(&panic_redirect_cpu, PANIC_CPU_INVALID); pr_warn("panic: failed to redirect to CPU %d, continuing on CPU %d\n", panic_force_cpu, this_cpu); @@ -503,17 +493,35 @@ bool panic_on_other_cpu(void) EXPORT_SYMBOL(panic_on_other_cpu); /* - * A variant of panic() called from NMI context. We return if we've already - * panicked on this CPU. If another CPU already panicked, loop in - * nmi_panic_self_stop() which can provide architecture dependent code such - * as saving register state for crash dump. + * A variant of panic() called from NMI context. The panic is first + * redirected to the CPU requested via panic_force_cpu=, when configured. + * We return if we've already panicked on this CPU. If another CPU already + * panicked, loop in nmi_panic_self_stop() which can provide architecture + * dependent code for saving register state for crash dump. */ -void nmi_panic(struct pt_regs *regs, const char *msg) +void nmi_panic(struct pt_regs *regs, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + + /* Try to redirect to the requested CPU before claiming panic_cpu. */ + if (panic_try_force_cpu(fmt, args)) { + /* + * Mark ourselves offline so panic_other_cpus_shutdown() won't + * wait for us on architectures that check num_online_cpus(). + */ + set_cpu_online(raw_smp_processor_id(), false); + nmi_panic_self_stop(regs); + } + if (panic_try_start()) - panic("%s", msg); - else if (panic_on_other_cpu()) + vpanic(fmt, args); + + if (panic_on_other_cpu()) nmi_panic_self_stop(regs); + + va_end(args); } EXPORT_SYMBOL(nmi_panic); @@ -567,6 +575,8 @@ static void panic_other_cpus_shutdown(bool crash_kexec) crash_smp_send_stop(); } +void __weak arch_do_panic(void) {} + /** * vpanic - halt the system * @fmt: The text string to print @@ -742,20 +752,9 @@ void vpanic(const char *fmt, va_list args) reboot_mode = panic_reboot_mode; emergency_restart(); } -#ifdef __sparc__ - { - extern int stop_a_enabled; - /* Make sure the user can actually press Stop-A (L1-A) */ - stop_a_enabled = 1; - pr_emerg("Press Stop-A (L1-A) from sun keyboard or send break\n" - "twice on console to return to the boot prom\n"); - } -#endif -#if defined(CONFIG_S390) - disabled_wait(); -#endif pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); + arch_do_panic(); /* Do not scroll important messages printed above */ suppress_printk = 1; @@ -1225,14 +1224,9 @@ static int panic_print_set(const char *val, const struct kernel_param *kp) return param_set_ulong(val, kp); } -static int panic_print_get(char *val, const struct kernel_param *kp) -{ - return param_get_ulong(val, kp); -} - static const struct kernel_param_ops panic_print_ops = { .set = panic_print_set, - .get = panic_print_get, + .get = param_get_ulong, }; __core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644); diff --git a/kernel/resource.c b/kernel/resource.c index e60539a55541..cfc1a00e86aa 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1350,8 +1350,8 @@ static int __request_region_locked(struct resource *res, struct resource *parent } if (conflict->flags & flags & IORESOURCE_MUXED) { add_wait_queue(&muxed_resource_wait, &wait); - write_unlock(&resource_lock); set_current_state(TASK_UNINTERRUPTIBLE); + write_unlock(&resource_lock); schedule(); remove_wait_queue(&muxed_resource_wait, &wait); write_lock(&resource_lock); @@ -1833,7 +1833,7 @@ __setup("reserve=", reserve_setup); */ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) { - resource_size_t end = addr + size - 1; + struct resource mem = DEFINE_RES_MEM(addr, size); struct resource *p; int err = 0; @@ -1843,12 +1843,10 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) * We can probably skip the resources without * IORESOURCE_IO attribute? */ - if (p->start > end) - continue; - if (p->end < addr) + if (!resource_overlaps(p, &mem)) continue; - if (PFN_DOWN(p->start) <= PFN_DOWN(addr) && - PFN_DOWN(p->end) >= PFN_DOWN(end)) + if (PFN_DOWN(p->start) <= PFN_DOWN(mem.start) && + PFN_DOWN(p->end) >= PFN_DOWN(mem.end)) continue; /* * if a resource is "BUSY", it's not a hardware resource @@ -1859,8 +1857,8 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) if (p->flags & IORESOURCE_BUSY) continue; - pr_debug("resource sanity check: requesting [mem %pa-%pa], which spans more than %s %pR\n", - &addr, &end, p->name, p); + pr_debug("resource sanity check: requesting %pR, which spans more than %s %pR\n", + &mem, p->name, p); err = -1; break; } diff --git a/kernel/taskstats.c b/kernel/taskstats.c index f31df72f0e9d..bf517788b840 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -59,8 +59,7 @@ static DEFINE_PER_CPU(struct listener_list, listener_array); enum actions { REGISTER, - DEREGISTER, - CPU_DONT_CARE + DEREGISTER }; static int prepare_reply(struct genl_info *info, u8 cmd, struct sk_buff **skbp, @@ -473,7 +472,8 @@ static size_t taskstats_packet_size(void) return size; } -static int cmd_attr_pid(struct genl_info *info) +static int cmd_attr_pid_tgid(struct genl_info *info, int attr, + int (*fill)(pid_t, struct taskstats *)) { struct taskstats *stats; struct sk_buff *rep_skb; @@ -488,41 +488,15 @@ static int cmd_attr_pid(struct genl_info *info) return rc; rc = -EINVAL; - pid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_PID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_PID, pid); + pid = nla_get_u32(info->attrs[attr]); + stats = mk_reply(rep_skb, + attr == TASKSTATS_CMD_ATTR_PID + ? TASKSTATS_TYPE_PID : TASKSTATS_TYPE_TGID, + pid); if (!stats) goto err; - rc = fill_stats_for_pid(pid, stats); - if (rc < 0) - goto err; - return send_reply(rep_skb, info); -err: - nlmsg_free(rep_skb); - return rc; -} - -static int cmd_attr_tgid(struct genl_info *info) -{ - struct taskstats *stats; - struct sk_buff *rep_skb; - size_t size; - u32 tgid; - int rc; - - size = taskstats_packet_size(); - - rc = prepare_reply(info, TASKSTATS_CMD_NEW, &rep_skb, size); - if (rc < 0) - return rc; - - rc = -EINVAL; - tgid = nla_get_u32(info->attrs[TASKSTATS_CMD_ATTR_TGID]); - stats = mk_reply(rep_skb, TASKSTATS_TYPE_TGID, tgid); - if (!stats) - goto err; - - rc = fill_stats_for_tgid(tgid, stats); + rc = fill(pid, stats); if (rc < 0) goto err; return send_reply(rep_skb, info); @@ -542,9 +516,11 @@ static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info) TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, DEREGISTER); else if (info->attrs[TASKSTATS_CMD_ATTR_PID]) - return cmd_attr_pid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_PID, + fill_stats_for_pid); else if (info->attrs[TASKSTATS_CMD_ATTR_TGID]) - return cmd_attr_tgid(info); + return cmd_attr_pid_tgid(info, TASKSTATS_CMD_ATTR_TGID, + fill_stats_for_tgid); else return -EINVAL; } diff --git a/lib/Kconfig b/lib/Kconfig index 4e6b34c3346d..1e42f5167c68 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -219,29 +219,29 @@ source "lib/xz/Kconfig" # config DECOMPRESS_GZIP select ZLIB_INFLATE - tristate + bool config DECOMPRESS_BZIP2 - tristate + bool config DECOMPRESS_LZMA - tristate + bool config DECOMPRESS_XZ select XZ_DEC - tristate + bool config DECOMPRESS_LZO select LZO_DECOMPRESS - tristate + bool config DECOMPRESS_LZ4 select LZ4_DECOMPRESS - tristate + bool config DECOMPRESS_ZSTD select ZSTD_DECOMPRESS - tristate + bool # # Generic allocator support is selected if needed diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6b24a2c89b84..1c6638b13b87 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2797,6 +2797,21 @@ config SYSCTL_KUNIT_TEST If unsure, say N. +config ERRSEQ_KUNIT_TEST + tristate "KUnit test for errseq" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This builds the errseq KUnit test suite. + It tests the documented semantics of the errseq_t error-tracking + infrastructure (lib/errseq.c), which underpins writeback error + reporting. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config KFIFO_KUNIT_TEST tristate "KUnit Test for the generic kernel FIFO implementation" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 89c88e359179..192a60a9f833 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -836,7 +836,6 @@ static int __init xbc_parse_array(char **__v) return -ENOMEM; *__v = next; } while (c == ','); - node->child = 0; return c; } @@ -1003,9 +1002,30 @@ static int __init xbc_close_brace(char **k, char *n) return __xbc_close_brace(n - 1); } +#ifndef __KERNEL__ +/* Sanity check for regression: node indices must be within bounds */ +static int __init xbc_sanity_check_tree(void) +{ + int i; + + for (i = 0; i < xbc_node_num; i++) { + if (xbc_nodes[i].next >= xbc_node_num) { + return xbc_parse_error("No closing brace", + xbc_node_get_data(xbc_nodes + i)); + } + if (xbc_nodes[i].child >= xbc_node_num) { + return xbc_parse_error("Broken child node", + xbc_node_get_data(xbc_nodes + i)); + } + } + + return 0; +} +#endif + static int __init xbc_verify_tree(void) { - int i, depth; + int depth; size_t len, wlen; struct xbc_node *n, *m; @@ -1022,17 +1042,6 @@ static int __init xbc_verify_tree(void) return -ENOENT; } - for (i = 0; i < xbc_node_num; i++) { - if (xbc_nodes[i].next >= xbc_node_num) { - return xbc_parse_error("No closing brace", - xbc_node_get_data(xbc_nodes + i)); - } - if (xbc_nodes[i].child >= xbc_node_num) { - return xbc_parse_error("Broken child node", - xbc_node_get_data(xbc_nodes + i)); - } - } - /* Key tree limitation check */ n = &xbc_nodes[0]; depth = 1; @@ -1119,6 +1128,13 @@ static int __init xbc_parse_tree(void) } } while (!ret); + if (!ret) { + while (p < xbc_data + xbc_data_size - 1 && *p == '\0') + p++; + if (p < xbc_data + xbc_data_size - 1) + ret = xbc_parse_error("Unexpected data after null character", p); + } + return ret; } @@ -1196,6 +1212,10 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) ret = xbc_parse_tree(); if (!ret) ret = xbc_verify_tree(); +#ifndef __KERNEL__ + if (!ret) + ret = xbc_sanity_check_tree(); +#endif if (ret < 0) { if (epos) diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c index 1288f146661f..aaa75404250e 100644 --- a/lib/decompress_bunzip2.c +++ b/lib/decompress_bunzip2.c @@ -439,7 +439,7 @@ got_huff_bits: array.) */ if (runPos) { runPos = 0; - if (dbufCount+t >= dbufSize) + if (dbufCount+t > dbufSize) return RETVAL_DATA_ERROR; uc = symToByte[mtfSymbol[0]]; diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c index c0dbb3cea915..86e9aaec04f6 100644 --- a/lib/decompress_unlz4.c +++ b/lib/decompress_unlz4.c @@ -139,6 +139,10 @@ STATIC inline int INIT unlz4(u8 *input, long in_len, if (!fill) { inp += 4; size -= 4; + if (chunksize > size) { + error("data corrupted"); + goto exit_2; + } } else { if (chunksize > LZ4_compressBound(uncomp_chunksize)) { error("chunk length is longer than allocated"); diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c index 05d5cb490a44..9ccded9934c6 100644 --- a/lib/decompress_unxz.c +++ b/lib/decompress_unxz.c @@ -342,13 +342,13 @@ STATIC int INIT unxz(unsigned char *in, long in_size, b.out_pos = 0; } } while (ret == XZ_OK); + } - if (must_free_in) - free(in); + if (must_free_in) + free(in); - if (flush != NULL) - free(b.out); - } + if (flush != NULL) + free(b.out); if (in_used != NULL) *in_used += b.in_pos; diff --git a/lib/fault-inject.c b/lib/fault-inject.c index 999053fa133e..02916ef2761c 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -5,6 +5,7 @@ #include <linux/debugfs.h> #include <linux/sched.h> #include <linux/stat.h> +#include <linux/string.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/export.h> @@ -64,7 +65,7 @@ static void fail_dump(struct fault_attr *attr) { if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) { printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n" - "name %pd, interval %lu, probability %lu, " + "name %s, interval %lu, probability %lu, " "space %d, times %d\n", attr->dname, attr->interval, attr->probability, atomic_read(&attr->space), @@ -261,7 +262,9 @@ struct dentry *fault_create_debugfs_attr(const char *name, debugfs_create_xul("reject-end", mode, dir, &attr->reject_end); #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ - attr->dname = dget(dir); + if (strscpy(attr->dname, name, sizeof(attr->dname)) == -E2BIG) + pr_warn("FAULT_INJECTION: name '%s' truncated to '%s'\n", + name, attr->dname); return dir; } EXPORT_SYMBOL_GPL(fault_create_debugfs_attr); diff --git a/lib/klist.c b/lib/klist.c index 332a4fbf18ff..f133740b1c2c 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -36,6 +36,7 @@ #include <linux/klist.h> #include <linux/export.h> #include <linux/sched.h> +#include <linux/sched/task.h> /* * Use the lowest bit of n_klist to mark deleted nodes and exclude @@ -187,18 +188,24 @@ static void klist_release(struct kref *kref) WARN_ON(!knode_dead(n)); list_del(&n->n_node); + knode_set_klist(n, NULL); spin_lock(&klist_remove_lock); list_for_each_entry_safe(waiter, tmp, &klist_remove_waiters, list) { + struct task_struct *p; + if (waiter->node != n) continue; + p = waiter->process; + get_task_struct(p); list_del(&waiter->list); - waiter->woken = 1; + /* Publish only after the final waiter and n accesses */ + smp_store_release(&waiter->woken, 1); mb(); - wake_up_process(waiter->process); + wake_up_process(p); + put_task_struct(p); } spin_unlock(&klist_remove_lock); - knode_set_klist(n, NULL); } static int klist_dec_and_del(struct klist_node *n) @@ -250,7 +257,8 @@ void klist_remove(struct klist_node *n) for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); - if (waiter.woken) + /* Pairs with the release store in klist_release() */ + if (smp_load_acquire(&waiter.woken)) break; schedule(); } diff --git a/lib/plist.c b/lib/plist.c index a5bef38add43..b0273533c04d 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -174,8 +174,15 @@ void plist_requeue(struct plist_node *node, struct plist_head *head) /* * After plist_del(), iter is the replacement of the node. If the node * was on prio_list, take shortcut to find node_next instead of looping. + * + * prio_list is a headless ring, so from the LAST bucket ->next wraps + * round to the first one; in that case node_next is the list head. */ if (!list_empty(&iter->prio_list)) { + struct plist_node *first = plist_first(head); + + if (iter->prio_list.next == &first->prio_list) + goto queue; iter = list_entry(iter->prio_list.next, struct plist_node, prio_list); node_next = &iter->node_list; diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index 01f007b2522c..563a178aa930 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -32,6 +32,7 @@ config XOR_KUNIT_TEST config XOR_BENCHMARK bool "Benchmark for xor_gen" depends on XOR_KUNIT_TEST + default y if KUNIT_ALL_TESTS=m help Include benchmarks in the KUnit test suite for xor_gen. @@ -63,6 +64,7 @@ config RAID6_PQ_KUNIT_TEST config RAID6_PQ_KUNIT_BENCHMARK bool "Benchmark for RAID6 PQ" depends on RAID6_PQ_KUNIT_TEST + default y if KUNIT_ALL_TESTS=m help Include benchmarks in the KUnit test suite for raid P/Q generation. diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 7d829c669ea7..3cc2fe7ac42c 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -67,6 +67,7 @@ static void raid6_avx21_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -115,6 +116,7 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -175,6 +177,7 @@ static void raid6_avx22_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -243,6 +246,7 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -334,6 +338,7 @@ static void raid6_avx24_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -444,6 +449,7 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, asm volatile("vmovntdq %%ymm14,%0" : "=m" (q[d+96])); } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index e671eb5bde63..772bfc4af6df 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -78,6 +78,7 @@ static void raid6_avx5121_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -137,6 +138,7 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -208,6 +210,7 @@ static void raid6_avx5122_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -292,6 +295,7 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -396,6 +400,7 @@ static void raid6_avx5124_gen_syndrome(int disks, size_t bytes, void **ptrs) } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -529,6 +534,7 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, "m" (q[d+128]), "m" (q[d+192])); } asm volatile("sfence" : : : "memory"); + asm volatile("vzeroupper"); kernel_fpu_end(); } const struct raid6_calls raid6_avx512x4 = { diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index a714a780a2d8..820871046e30 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -176,6 +176,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -293,6 +294,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index ec72d5a30c01..299a3f044d61 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -211,6 +211,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } @@ -353,6 +354,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, #endif } + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/raid/xor/x86/xor-avx.c b/lib/raid/xor/x86/xor-avx.c index f7777d7aa269..95b21e7225e8 100644 --- a/lib/raid/xor/x86/xor-avx.c +++ b/lib/raid/xor/x86/xor-avx.c @@ -147,6 +147,7 @@ static void xor_gen_avx(void *dest, void **srcs, unsigned int src_cnt, { kernel_fpu_begin(); xor_gen_avx_inner(dest, srcs, src_cnt, bytes); + asm volatile("vzeroupper"); kernel_fpu_end(); } diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 3cac3b63a752..8e11b125433b 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o +obj-$(CONFIG_ERRSEQ_KUNIT_TEST) += errseq_kunit.o obj-$(CONFIG_FFS_KUNIT_TEST) += ffs_kunit.o CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced) CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread) diff --git a/lib/tests/errseq_kunit.c b/lib/tests/errseq_kunit.c new file mode 100644 index 000000000000..8f39ebc4a248 --- /dev/null +++ b/lib/tests/errseq_kunit.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for the errseq_t error-tracking infrastructure. + * + * These exercise the documented single-threaded semantics of the errseq + * API (see Documentation/core-api/errseq.rst and lib/errseq.c): error + * recording and overwriting, the "seen" handoff between errseq_sample() + * and errseq_check_and_advance(), and the re-reporting of an error that + * is recorded again after it has been seen. + * + * The lockless properties of errseq_t under concurrent updates are + * outside the scope of these deterministic tests, as is the WARN path + * for invalid error values. + */ +#include <kunit/test.h> + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/errseq.h> + +/* + * A zeroed errseq_t is the "no error has ever occurred" epoch: it + * samples as zero and no check against it reports anything. + */ +static void errseq_test_zero_epoch_reports_no_error(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + + KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), 0); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); + KUNIT_EXPECT_EQ(test, since, 0); +} + +static void errseq_test_set_records_error(struct kunit *test) +{ + errseq_t eseq = 0; + + /* errseq_set() returns the previous value; the epoch is zero. */ + KUNIT_EXPECT_EQ(test, errseq_set(&eseq, -EIO), 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -EIO); +} + +/* Any error set always overwrites an existing error. */ +static void errseq_test_set_overwrites_error(struct kunit *test) +{ + errseq_t eseq = 0; + + errseq_set(&eseq, -EIO); + errseq_set(&eseq, -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, 0), -ENOSPC); +} + +/* Both ends of the valid error range are recorded exactly. */ +static void errseq_test_errno_range_extremes(struct kunit *test) +{ + errseq_t lo = 0; + errseq_t hi = 0; + + errseq_set(&lo, -1); + KUNIT_EXPECT_EQ(test, errseq_check(&lo, 0), -1); + + errseq_set(&hi, -MAX_ERRNO); + KUNIT_EXPECT_EQ(test, errseq_check(&hi, 0), -MAX_ERRNO); +} + +/* + * An error nobody has seen yet samples as zero, so that a check against + * the sample still reports it (see commit b4678df184b3 ("errseq: Always + * report a writeback error once")). + */ +static void errseq_test_sample_of_unseen_error_is_zero(struct kunit *test) +{ + errseq_t eseq = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_sample(&eseq), 0); +} + +static void errseq_test_new_sampler_sees_unseen_error(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since; + + errseq_set(&eseq, -EIO); + since = errseq_sample(&eseq); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO); +} + +/* A given error is reported exactly once per advancing cursor. */ +static void errseq_test_check_and_advance_reports_once(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = errseq_sample(&eseq); + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); +} + +/* + * Once an error has been seen, a fresh sample is non-zero and checking + * against it reports nothing: handled errors do not reach new samplers. + */ +static void errseq_test_sample_after_seen_is_current(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t sample; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + sample = errseq_sample(&eseq); + KUNIT_EXPECT_NE(test, sample, 0); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, sample), 0); +} + +static void errseq_test_new_error_after_advance(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + errseq_set(&eseq, -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -ENOSPC); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); +} + +/* + * Recording the same error again after it has been seen must bump the + * sequence, so cursors that consumed the first occurrence see the + * second one too. + */ +static void errseq_test_same_error_reported_again_after_seen(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t seen_cursor; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + seen_cursor = since; + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, since), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + /* The repeat must advance the sequence, not just re-toggle "seen". */ + KUNIT_EXPECT_NE(test, since, seen_cursor); +} + +/* + * A cursor that consumed an error must still observe a repeat of that + * error even when another cursor has already marked the repeat seen: + * recording over a seen value must advance the sequence. + */ +static void errseq_test_repeat_error_visible_to_all_cursors(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t cursor_a = 0; + errseq_t cursor_b = 0; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0); +} + +/* An advance with no new error reports nothing and leaves the cursor put. */ +static void errseq_test_advance_stable_when_unchanged(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t since = 0; + errseq_t cursor; + + errseq_set(&eseq, -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), -EIO); + + cursor = since; + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &since), 0); + KUNIT_EXPECT_EQ(test, since, cursor); +} + +/* + * Cursors are independent: one subscriber consuming an error does not + * consume it for another, and each subscriber sees each error once. + */ +static void errseq_test_two_subscribers_independent(struct kunit *test) +{ + errseq_t eseq = 0; + errseq_t cursor_a = errseq_sample(&eseq); + errseq_t cursor_b = errseq_sample(&eseq); + + errseq_set(&eseq, -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check(&eseq, cursor_b), -EIO); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), -EIO); + + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_a), 0); + KUNIT_EXPECT_EQ(test, errseq_check_and_advance(&eseq, &cursor_b), 0); +} + +static struct kunit_case errseq_test_cases[] = { + KUNIT_CASE(errseq_test_zero_epoch_reports_no_error), + KUNIT_CASE(errseq_test_set_records_error), + KUNIT_CASE(errseq_test_set_overwrites_error), + KUNIT_CASE(errseq_test_errno_range_extremes), + KUNIT_CASE(errseq_test_sample_of_unseen_error_is_zero), + KUNIT_CASE(errseq_test_new_sampler_sees_unseen_error), + KUNIT_CASE(errseq_test_check_and_advance_reports_once), + KUNIT_CASE(errseq_test_sample_after_seen_is_current), + KUNIT_CASE(errseq_test_new_error_after_advance), + KUNIT_CASE(errseq_test_same_error_reported_again_after_seen), + KUNIT_CASE(errseq_test_repeat_error_visible_to_all_cursors), + KUNIT_CASE(errseq_test_advance_stable_when_unchanged), + KUNIT_CASE(errseq_test_two_subscribers_independent), + {} +}; + +static struct kunit_suite errseq_test_suite = { + .name = "errseq", + .test_cases = errseq_test_cases, +}; + +kunit_test_suite(errseq_test_suite); + +MODULE_DESCRIPTION("KUnit tests for the errseq infrastructure"); +MODULE_LICENSE("GPL"); diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8a7787d228a6..3614cfe4dcbb 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -63,6 +63,7 @@ my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; my $spdx_cxx_comments = 0; +my $userspace; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; @@ -143,6 +144,7 @@ Options: (required by old toolchains), allow also C++ comments (//). NOTE: it should *not* be used for Linux mainline. + --userspace Force rules specific for userspace. --codespell Use the codespell dictionary for spelling/typos (default:$codespellfile) --codespellfile Use this codespell dictionary @@ -358,6 +360,7 @@ GetOptions( 'codespell!' => \$codespell, 'codespellfile=s' => \$user_codespellfile, 'typedefsfile=s' => \$typedefsfile, + 'userspace!' => \$userspace, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor @@ -1206,6 +1209,8 @@ sub seed_camelcase_includes { my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } elsif (not defined $root) { + return; } else { my $last_mod_date = 0; $files = `find $root/include -name "*.h"`; @@ -2665,7 +2670,12 @@ sub exclude_global_initialisers { sub is_userspace { my ($realfile) = @_; - return ($realfile =~ m@^tools/@ || $realfile =~ m@^scripts/@); + + return $userspace if (defined $userspace); + + return ($realfile =~ m@^tools/@ || + $realfile =~ m@^scripts/@ || + $realfile =~ m@^arch/.*/tools/@); } sub process { @@ -5946,6 +5956,8 @@ sub process { #Ignore SI style variants like nS, mV and dB #(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && +#Ignore <inttypes.h> format macros (e.g. PRIu64, SCNu64) + (is_userspace($realfile) ? $var !~ /^(?:PRI|SCN)[dioux][A-Z0-9]+$/ : 1) && #Ignore some three character SI units explicitly, like MiB and KHz $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { while ($var =~ m{\b($Ident)}g) { @@ -6684,7 +6696,8 @@ sub process { } # prefer usleep_range over udelay - if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { + if (!is_userspace($realfile) && + $line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { my $delay = $1; # ignore udelay's < 10, however if (! ($delay < 10) ) { @@ -6698,7 +6711,8 @@ sub process { } # warn about unexpectedly long msleep's - if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if (!is_userspace($realfile) && + $line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", "msleep < 20ms can sleep for up to 20ms; see function description of msleep().\n" . $herecurr); @@ -6920,7 +6934,7 @@ sub process { # check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && - $realfile !~ m@\btools/@ && + !is_userspace($realfile) && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; if ($type =~ /\b($typeC99Typedefs)\b/) { @@ -7170,6 +7184,7 @@ sub process { # check usleep_range arguments if ($perl_version_ok && defined $stat && + !is_userspace($realfile) && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; @@ -7413,6 +7428,7 @@ sub process { # check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi if ($realfile !~ m@^include/uapi/@ && + !is_userspace($realfile) && $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { my $ull = ""; $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index 14ce31f732ee..f8011e789a9b 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -66,8 +66,10 @@ my (@stack, $re, $dre, $sub, $x, $xs, $funcre, $min_stack); #c0105234: 81 ec ac 05 00 00 sub $0x5ac,%esp # or # 2f60: 48 81 ec e8 05 00 00 sub $0x5e8,%rsp - $re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%(e|r)sp$/o; - $dre = qr/^.*[as][du][db] (%.*),\%(e|r)sp$/o; + # or + # 0: 48 81 ec e8 05 00 00 subq $0x5e8, %rsp + $re = qr/^.*[as][du][db][ql]?\s+\$(0x$x{1,8}),\s*\%(e|r)sp/o; + $dre = qr/^.*[as][du][db][ql]?\s+(%.*),\s*\%(e|r)sp/o; } elsif ($arch eq 'm68k') { # 2b6c: 4e56 fb70 linkw %fp,#-1168 # 1df770: defc ffe4 addaw #-28,%sp diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 3372873cd7bb..9ec2529bc584 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -872,8 +872,8 @@ infromation||information ingore||ignore inheritence||inheritance inital||initial -initalised||initialized -initalise||initialize +initalised||initialised +initalise||initialise initalized||initialized initalize||initialize initation||initiation diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 17d971d47f87..652e491b9c33 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -21,6 +21,39 @@ #define BOOTCONFIG_FOOTER_SIZE \ (sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN) +static void show_xbc_error(const char *data, const char *msg, int pos) +{ + int lin = 1, col, i; + + if (pos < 0) { + pr_err("Error: %s.\n", msg); + return; + } + + /* Note that pos starts from 0 but lin and col should start from 1. */ + col = pos + 1; + for (i = 0; i < pos; i++) { + if (data[i] == '\n') { + lin++; + col = pos - i; + } + } + pr_err("Parse Error: %s at %d:%d\n", msg, lin, col); + +} + +static int init_xbc_with_error(char *buf, int len) +{ + const char *msg; + int ret, pos; + + ret = xbc_init(buf, len, &msg, &pos); + if (ret < 0) + show_xbc_error(buf, msg, pos); + + return ret; +} + static int xbc_show_value(struct xbc_node *node, bool semicolon) { const char *val, *eol; @@ -197,7 +230,6 @@ static int load_xbc_from_initrd(int fd, char **buf) int ret; uint32_t size = 0, csum = 0, rcsum; char magic[BOOTCONFIG_MAGIC_LEN]; - const char *msg; ret = fstat(fd, &stat); if (ret < 0) @@ -249,52 +281,9 @@ static int load_xbc_from_initrd(int fd, char **buf) return -EINVAL; } - ret = xbc_init(*buf, size, &msg, NULL); - /* Wrong data */ - if (ret < 0) { - pr_err("parse error: %s.\n", msg); - return ret; - } - - return size; -} - -static void show_xbc_error(const char *data, const char *msg, int pos) -{ - int lin = 1, col, i; - - if (pos < 0) { - pr_err("Error: %s.\n", msg); - return; - } - - /* Note that pos starts from 0 but lin and col should start from 1. */ - col = pos + 1; - for (i = 0; i < pos; i++) { - if (data[i] == '\n') { - lin++; - col = pos - i; - } - } - pr_err("Parse Error: %s at %d:%d\n", msg, lin, col); + ret = init_xbc_with_error(*buf, size); -} - -static int init_xbc_with_error(char *buf, int len) -{ - char *copy = strdup(buf); - const char *msg; - int ret, pos; - - if (!copy) - return -ENOMEM; - - ret = xbc_init(buf, len, &msg, &pos); - if (ret < 0) - show_xbc_error(copy, msg, pos); - free(copy); - - return ret; + return ret < 0 ? ret : size; } static int show_xbc_kernel_cmdline(void) @@ -423,9 +412,8 @@ static int apply_xbc(const char *path, const char *xbc_path) char *buf, *data; size_t total_size; struct stat stat; - const char *msg; uint32_t size, csum; - int pos, pad; + int pad; int ret, fd; ret = load_xbc_file(xbc_path, &buf); @@ -433,9 +421,18 @@ static int apply_xbc(const char *path, const char *xbc_path) pr_err("Failed to load %s : %d\n", xbc_path, ret); return ret; } - size = strlen(buf) + 1; + size = ret; + if (size == 0 || buf[size - 1] != '\0') + size++; csum = xbc_calc_checksum(buf, size); + /* Verify the data format */ + ret = init_xbc_with_error(buf, size); + if (ret < 0) { + free(buf); + return ret; + } + /* Backup the bootconfig data */ data = calloc(size + BOOTCONFIG_ALIGN + BOOTCONFIG_FOOTER_SIZE, 1); if (!data) { @@ -444,15 +441,6 @@ static int apply_xbc(const char *path, const char *xbc_path) } memcpy(data, buf, size); - /* Check the data format */ - ret = xbc_init(buf, size, &msg, &pos); - if (ret < 0) { - show_xbc_error(data, msg, pos); - free(data); - free(buf); - - return ret; - } printf("Apply %s to %s\n", xbc_path, path); xbc_get_info(&ret, NULL); printf("\tNumber of nodes: %d\n", ret); diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh index fc69f815ce4a..530ce7e28d63 100755 --- a/tools/bootconfig/test-bootconfig.sh +++ b/tools/bootconfig/test-bootconfig.sh @@ -180,6 +180,18 @@ EOF $BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE xpass grep -q "1:1" $OUTFILE +echo "Intermediate null character test" +printf "key = value\n\0extra = data\n" > $TEMPCONF +xfail $BOOTCONF -a $TEMPCONF $INITRD +$BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE +xpass grep -q "Unexpected" $OUTFILE + +echo "Trailing null character test" +printf "key = value\n\0" > $TEMPCONF +xpass $BOOTCONF -a $TEMPCONF $INITRD +$BOOTCONF $INITRD > $OUTFILE +xpass grep -q "value" $OUTFILE + echo "=== expected failure cases ===" for i in samples/bad-* ; do xfail $BOOTCONF -a $i $INITRD diff --git a/tools/testing/selftests/core/unshare_test.c b/tools/testing/selftests/core/unshare_test.c index ffce75a6c228..d40e963dd520 100644 --- a/tools/testing/selftests/core/unshare_test.c +++ b/tools/testing/selftests/core/unshare_test.c @@ -40,6 +40,14 @@ TEST(unshare_EMFILE) ASSERT_EQ(sscanf(buf, "%d", &nr_open), 1); + /* + * Cap nr_open for the duration of the test to avoid ENOMEM from a + * huge fd table allocation; buf/n keep the real original value so + * fs.nr_open can be restored to it once the test is done. + */ + if (nr_open > 1024 * 1024) + nr_open = 1024 * 1024; + ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit)); /* bump fs.nr_open */ @@ -73,10 +81,13 @@ TEST(unshare_EMFILE) if (pid == 0) { int err; + char buf3[32]; + ssize_t n3; - /* restore fs.nr_open */ + /* restore fs.nr_open to the (possibly capped) test baseline */ + n3 = sprintf(buf3, "%d\n", nr_open); lseek(fd, 0, SEEK_SET); - write(fd, buf, n); + write(fd, buf3, n3); /* ... and now unshare(CLONE_FILES) must fail with EMFILE */ err = unshare(CLONE_FILES); EXPECT_EQ(err, -1) @@ -89,6 +100,10 @@ TEST(unshare_EMFILE) EXPECT_EQ(waitpid(pid, &status, 0), pid); EXPECT_EQ(true, WIFEXITED(status)); EXPECT_EQ(0, WEXITSTATUS(status)); + + /* restore the real fs.nr_open value */ + lseek(fd, 0, SEEK_SET); + write(fd, buf, n); } TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c index 81a994943e12..b4dcbcd79773 100644 --- a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c +++ b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c @@ -74,6 +74,24 @@ static void *waiter_entry1ap(void *data) return NULL; } +static void *waiter_entry1ap_loop(void *data) +{ + struct pollfd pfd; + struct epoll_event e; + struct epoll_mtcontext *ctx = data; + + pfd.fd = ctx->efd[0]; + pfd.events = POLLIN; + while (poll(&pfd, 1, 2000) > 0) { + if (epoll_wait(ctx->efd[0], &e, 1, 0) > 0) { + __sync_fetch_and_add(&ctx->count, 1); + break; + } + } + + return NULL; +} + static void *waiter_entry1o(void *data) { struct epoll_event e; @@ -809,7 +827,7 @@ TEST(epoll16) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.sfd[2], events), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); if (epoll_wait(ctx.efd[0], events, 1, -1) > 0) @@ -2925,7 +2943,7 @@ TEST(epoll56) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.efd[2], &e), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); if (epoll_wait(ctx.efd[0], &e, 1, -1) > 0) @@ -3030,7 +3048,6 @@ TEST(epoll57) TEST(epoll58) { pthread_t emitter; - struct pollfd pfd; struct epoll_event e; struct epoll_mtcontext ctx = { 0 }; @@ -3061,15 +3078,10 @@ TEST(epoll58) ASSERT_EQ(epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.efd[2], &e), 0); ctx.main = pthread_self(); - ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap, &ctx), 0); + ASSERT_EQ(pthread_create(&ctx.waiter, NULL, waiter_entry1ap_loop, &ctx), 0); ASSERT_EQ(pthread_create(&emitter, NULL, emitter_entry2, &ctx), 0); - pfd.fd = ctx.efd[0]; - pfd.events = POLLIN; - if (poll(&pfd, 1, -1) > 0) { - if (epoll_wait(ctx.efd[0], &e, 1, 0) > 0) - __sync_fetch_and_add(&ctx.count, 1); - } + waiter_entry1ap_loop(&ctx); ASSERT_EQ(pthread_join(ctx.waiter, NULL), 0); EXPECT_EQ(ctx.count, 2); diff --git a/tools/testing/selftests/membarrier/membarrier_test_impl.h b/tools/testing/selftests/membarrier/membarrier_test_impl.h index f6d7c44b2288..b4dcbb32538d 100644 --- a/tools/testing/selftests/membarrier/membarrier_test_impl.h +++ b/tools/testing/selftests/membarrier/membarrier_test_impl.h @@ -16,6 +16,11 @@ static int sys_membarrier(int cmd, int flags) return syscall(__NR_membarrier, cmd, flags); } +static int membarrier_get_registrations(void) +{ + return sys_membarrier(MEMBARRIER_CMD_GET_REGISTRATIONS, 0); +} + static int test_membarrier_get_registrations(int cmd) { int ret, flags = 0; @@ -24,7 +29,7 @@ static int test_membarrier_get_registrations(int cmd) registrations |= cmd; - ret = sys_membarrier(MEMBARRIER_CMD_GET_REGISTRATIONS, 0); + ret = membarrier_get_registrations(); if (ret < 0) { ksft_exit_fail_msg( "%s test: flags = %d, errno = %d\n", @@ -108,6 +113,16 @@ static int test_membarrier_private_expedited_fail(void) int cmd = MEMBARRIER_CMD_PRIVATE_EXPEDITED, flags = 0; const char *test_name = "sys membarrier MEMBARRIER_CMD_PRIVATE_EXPEDITED not registered failure"; + /* + * Some C libraries, like Musl, register the private expedited barrier + * command when creating a thread. Expecting an EPERM on an unregistered + * command will therefore no longer work. Skip the test in this case. + */ + if (MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED & membarrier_get_registrations()) { + ksft_test_result_skip("%s test: Command already registered\n", test_name); + return 0; + } + if (sys_membarrier(cmd, flags) != -1) { ksft_exit_fail_msg( "%s test: flags = %d. Should fail, but passed\n", |
