summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-15 03:59:45 +0530
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-15 03:59:45 +0530
commit7e0e7bd60d4a812b694c477716597fcb038b00cb (patch)
tree4ff61d47485803e7dacab1c8ddef0a4c11b512da
parentff8747aacaff8266dd751b8a8648fb728dcc3b21 (diff)
parentaa5c4fe3ba0cb2af90bbcfa7a8ef4fefcd5c2370 (diff)
downloadlwn-7e0e7bd60d4a812b694c477716597fcb038b00cb.tar.gz
lwn-7e0e7bd60d4a812b694c477716597fcb038b00cb.zip
Merge tag 'vfs-7.2-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull misc vfs updates from Christian Brauner: "Features: - Reduce pipe->mutex contention by pre-allocating pages outside the lock in anon_pipe_write(). anon_pipe_write() called alloc_page() once per page while holding pipe->mutex. The allocation can sleep doing direct reclaim and runs memcg charging, which extends the critical section and stalls any concurrent reader on the same mutex. Now up to 8 pages are pre-allocated before the mutex is taken, leftovers are recycled into the per-pipe tmp_page[] cache before unlock, and any remainder is released after unlock, keeping the allocator out of the critical section on both sides. On a writers x readers sweep with 64KB writes against a 1 MB pipe throughput improves 6-28% and average write latency drops 5-22%; under memory pressure - when the cost of holding the mutex across reclaim is highest - throughput improves 21-48% and latency drops 17-33%. The microbenchmark is added to selftests. - uaccess/sockptr: fix the ignored_trailing logic in copy_struct_to_user() to behave as documented and the usize check in copy_struct_from_sockptr() for user pointers, and add copy_struct_{from,to}_bounce_buffer() and copy_struct_to_sockptr() helpers for upcoming users (IPPROTO_SMBDIRECT, IPPROTO_QUIC). - bpf: add a sleepable bpf_real_inode() kfunc that resolves the real inode backing a dentry via d_real_inode(). On overlayfs the inode attached to the dentry doesn't carry the underlying device information; this is used by the filesystem restriction BPF program that was merged into systemd. - docs: add guidelines for submitting new filesystems, motivated by the maintenance burden abandoned and untestable filesystems impose on VFS developers, blocking infrastructure work like folio conversions and iomap migration. Fixes: - libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() and drop the now-redundant assignments in callers. This began as a one-line dma-buf fix for a path_noexec() warning; a pseudo filesystem has no reason not to set SB_I_NOEXEC. All init_pseudo() callers were audited: the only visible effect is on dma-buf where SB_I_NOEXEC silences the warning. - Handle set_blocksize() failures in legacy filesystems (bfs, hpfs, qnx4, jfs, befs, affs, isofs, minix, ntfs3, omfs). Mounting a device with a sector size > PAGE_SIZE crashed roughly half of them; the rest had the same missing error handling pattern. Plus a follow-up releasing the superblock buffer_head when setting the minix v3 block size fails. - mount: honour SB_NOUSER in the new mount API. - fs/fcntl: fix a SOFTIRQ-unsafe lock order in fasync signaling by switching the process-group paths of send_sigio() and send_sigurg() from read_lock(&tasklist_lock) to RCU, matching the single-PID path. - vfs: add an FS_USERNS_DELEGATABLE flag and set it for NFS, fixing delegated NFS mounts (fsopen() in a container with the mount performed by a privileged daemon) that broke when non-init s_user_ns was tied to FS_USERNS_MOUNT. - selftests/namespaces: fix a hang in nsid_test where an unreaped grandchild kept the TAP pipe write-end open, a waitpid(-1) race in listns_efault_test, and a false FAIL on kernels without listns() where the tests should SKIP. - filelock: fix the break_lease() stub signature for CONFIG_FILE_LOCKING=n. - init/initramfs_test: wait for the async initramfs unpacking before running; the test and do_populate_rootfs() share the parser state. - fs/coredump: reduce redundant log noise in validate_coredump_safety(). - iomap: pass the correct length to fserror_report_io() in __iomap_write_begin(). - backing-file: fix the backing_file_open() kerneldoc. Cleanups: - initramfs: refactor the cpio hex header parsing to use hex2bin() instead of the hand-rolled simple_strntoul() which is reverted, and extend the initramfs KUnit tests to cover header fields with 0x prefixes. - Replace __get_free_pages() and friends with kmalloc()/kzalloc() across quota, proc, ocfs2/dlm, nilfs2, nfs, nfsd, libfs, jfs, jbd2, isofs, fuse, select, namespace, configfs, binfmt_misc, bfs, and the do_mounts init code - part of the larger work of replacing page allocator calls with kmalloc(). - Use clear_and_wake_up_bit() in unlock_buffer() and journal_end_buffer_io_sync() instead of open-coding the sequence. - Drop unused VFS exports: unexport drop_super_exclusive(), remove start_removing_user_path_at(), and fold __start_removing_path() into start_removing_path(). - fs/read_write: narrow the __kernel_write() export with EXPORT_SYMBOL_FOR_MODULES(). - vfs: uapi: retire octal and hex constants in favor of (1 << n) for the O_ flags. Finding a free bit for a new flag across the architectures was needlessly hard with the mixed bases. - dcache: add extra sanity checks of dead dentries in dentry_free() via a new DENTRY_WARN_ONCE() that also prints d_flags. - iov_iter: use kmemdup_array() in dup_iter() to harden the allocation against multiplication overflow. - fs/pipe: write to ->poll_usage only once. - vfs: remove an always-taken if-branch in find_next_fd(). - dcache: use kmalloc_flex() for struct external_name in __d_alloc(). - namei: use QSTR() instead of QSTR_INIT() in path_pts(). - sync_file_range: delete dead S_ISLNK code. - Comment fixes: retire a stale comment in fget_task_next() and fix assorted spelling mistakes" * tag 'vfs-7.2-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (73 commits) backing-file: fix backing_file_open() kerneldoc parameter iomap: pass the correct len to fserror_report_io in __iomap_write_begin vfs: add FS_USERNS_DELEGATABLE flag and set it for NFS filelock: fix break_lease() stub signature for CONFIG_FILE_LOCKING=n vfs: uapi: retire octal and hex numbers in favor of (1 << n) for O_ flags bpf: add bpf_real_inode() kfunc fs/read_write: Do not export __kernel_write() to the entire world libfs: drop redundant SB_I_NOEXEC/SB_I_NODEV in init_pseudo() callers libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() mount: honour SB_NOUSER in the new mount API fs/fcntl: fix SOFTIRQ-unsafe lock order in fasync signaling selftests/pipe: add pipe_bench microbenchmark fs/pipe: pre-allocate pages outside pipe->mutex in anon_pipe_write fs: retire stale comment in fget_task_next() fs: fix spelling mistakes in comment bfs: replace get_zeroed_page() with kzalloc() binfmt_misc: replace __get_free_page() with kmalloc() configfs: replace __get_free_pages() with kzalloc() fs/namespace: use __getname() to allocate mntpath buffer fs/select: replace __get_free_page() with kmalloc() ...
-rw-r--r--Documentation/filesystems/adding-new-filesystems.rst195
-rw-r--r--Documentation/filesystems/index.rst1
-rw-r--r--Documentation/filesystems/porting.rst1
-rw-r--r--arch/alpha/include/uapi/asm/fcntl.h34
-rw-r--r--arch/arm/include/uapi/asm/fcntl.h8
-rw-r--r--arch/arm64/include/uapi/asm/fcntl.h8
-rw-r--r--arch/m68k/include/uapi/asm/fcntl.h8
-rw-r--r--arch/mips/include/uapi/asm/fcntl.h22
-rw-r--r--arch/parisc/include/uapi/asm/fcntl.h28
-rw-r--r--arch/powerpc/include/uapi/asm/fcntl.h8
-rw-r--r--arch/sparc/include/uapi/asm/fcntl.h34
-rw-r--r--fs/affs/affs.h5
-rw-r--r--fs/affs/super.c6
-rw-r--r--fs/aio.c1
-rw-r--r--fs/anon_inodes.c2
-rw-r--r--fs/backing-file.c13
-rw-r--r--fs/befs/linuxvfs.c3
-rw-r--r--fs/bfs/inode.c7
-rw-r--r--fs/binfmt_misc.c4
-rw-r--r--fs/bpf_fs_kfuncs.c16
-rw-r--r--fs/buffer.c4
-rw-r--r--fs/configfs/file.c7
-rw-r--r--fs/coredump.c3
-rw-r--r--fs/dcache.c18
-rw-r--r--fs/exec.c6
-rw-r--r--fs/fcntl.c8
-rw-r--r--fs/file.c18
-rw-r--r--fs/file_table.c4
-rw-r--r--fs/fuse/ioctl.c5
-rw-r--r--fs/hpfs/super.c3
-rw-r--r--fs/iomap/buffered-io.c2
-rw-r--r--fs/isofs/dir.c5
-rw-r--r--fs/isofs/inode.c3
-rw-r--r--fs/jbd2/commit.c4
-rw-r--r--fs/jbd2/journal.c7
-rw-r--r--fs/jfs/jfs_dtree.c16
-rw-r--r--fs/jfs/super.c3
-rw-r--r--fs/libfs.c7
-rw-r--r--fs/minix/inode.c3
-rw-r--r--fs/namei.c25
-rw-r--r--fs/namespace.c11
-rw-r--r--fs/nfs/fs_context.c8
-rw-r--r--fs/nfs/nfs4namespace.c15
-rw-r--r--fs/nfs/super.c4
-rw-r--r--fs/nfsd/vfs.c4
-rw-r--r--fs/nilfs2/ioctl.c4
-rw-r--r--fs/nsfs.c1
-rw-r--r--fs/ntfs3/super.c8
-rw-r--r--fs/ocfs2/dlm/dlmdebug.c24
-rw-r--r--fs/ocfs2/dlm/dlmdomain.c8
-rw-r--r--fs/ocfs2/dlm/dlmmaster.c5
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c4
-rw-r--r--fs/omfs/inode.c6
-rw-r--r--fs/pidfs.c2
-rw-r--r--fs/pipe.c106
-rw-r--r--fs/proc/base.c16
-rw-r--r--fs/qnx4/inode.c3
-rw-r--r--fs/quota/dquot.c11
-rw-r--r--fs/read_write.c5
-rw-r--r--fs/select.c4
-rw-r--r--fs/super.c12
-rw-r--r--fs/sync.c3
-rw-r--r--include/linux/filelock.h2
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/kstrtox.h9
-rw-r--r--include/linux/namei.h1
-rw-r--r--include/linux/sockptr.h28
-rw-r--r--include/linux/uaccess.h65
-rw-r--r--include/uapi/asm-generic/fcntl.h50
-rw-r--r--init/do_mounts.c21
-rw-r--r--init/initramfs.c68
-rw-r--r--init/initramfs_test.c97
-rw-r--r--lib/iov_iter.c8
-rw-r--r--lib/vsprintf.c7
-rw-r--r--mm/secretmem.c2
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/namespaces/listns_efault_test.c33
-rw-r--r--tools/testing/selftests/namespaces/nsid_test.c14
-rw-r--r--tools/testing/selftests/pipe/.gitignore1
-rw-r--r--tools/testing/selftests/pipe/Makefile9
-rw-r--r--tools/testing/selftests/pipe/pipe_bench.c616
-rw-r--r--virt/kvm/guest_memfd.c2
82 files changed, 1464 insertions, 390 deletions
diff --git a/Documentation/filesystems/adding-new-filesystems.rst b/Documentation/filesystems/adding-new-filesystems.rst
new file mode 100644
index 000000000000..a3d0bf16f73a
--- /dev/null
+++ b/Documentation/filesystems/adding-new-filesystems.rst
@@ -0,0 +1,195 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _adding_new_filesystems:
+
+Adding New Filesystems
+======================
+
+This document describes what is involved in adding a new filesystem to the
+Linux kernel.
+
+Every filesystem merged into the kernel becomes the collective responsibility
+of the VFS maintainers and the wider filesystem development community.
+Experience has shown that filesystems which become unmaintained impose a
+significant and ongoing burden: they are hard or impossible to test, they
+block infrastructure changes because someone must update or preserve old APIs
+for code that nobody is actively looking after, and they accumulate unfixed
+bugs. The requirements and expectations described here are informed by this
+experience and are intended to ensure that new filesystems enter the kernel
+on a sustainable footing.
+
+
+Do You Need a New In-Kernel Filesystem?
+---------------------------------------
+
+Before proposing a new in-kernel filesystem, consider whether one of the
+alternatives might be more appropriate.
+
+ - If an existing in-kernel filesystem covers the same use case, improving it
+ is generally preferred over adding a new implementation. The kernel
+ community favors incremental improvement over parallel implementations.
+
+ - If the filesystem serves a niche audience or has a small user base, a FUSE
+ (Filesystem in Userspace) implementation may be a better fit. FUSE
+ filesystems avoid the long-term kernel maintenance commitment and can be
+ developed and released on their own schedule.
+
+ - If kernel-level performance, reliability, or integration is genuinely
+ required, make the case explicitly. Explain who the users are, what the
+ use case is, and why a FUSE implementation would not be sufficient.
+
+
+Technical Requirements
+----------------------
+
+New filesystems must use current kernel interfaces and practices.
+Submitting a filesystem built on outdated APIs creates an unacceptable
+maintenance debt and is likely to face pushback during review.
+
+Use modern VFS interfaces
+ Do not use interfaces listed in
+ :ref:`Documentation/process/deprecated.rst <deprecated>`.
+
+ Use folios rather than raw page operations for page cache management and
+ iomap rather than buffer heads for block mapping and I/O. See
+ ``Documentation/filesystems/iomap/index.rst`` for iomap documentation.
+
+ Block-based filesystems that need functionality not currently provided by
+ iomap should be prepared to explain why adding that functionality to iomap
+ is infeasible, rather than reimplementing their own block mapping layer.
+
+ Network filesystems should consider using the netfs library
+ (``Documentation/filesystems/netfs_library.rst``), or be prepared to explain
+ why it is not a good fit.
+
+Provide userspace utilities
+ A ``mkfs`` tool is expected so that the filesystem can be created and used
+ by testers and users. A ``fsck`` tool is strongly recommended; while not
+ strictly required for every filesystem type, the ability to verify
+ consistency and repair corruption is an important part of a mature
+ filesystem.
+
+Be testable
+ The filesystem must be testable in a meaningful way. The
+ `fstests <https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git>`_
+ framework (also known as xfstests) is the standard testing infrastructure
+ for Linux filesystems and its use is highly recommended. At a minimum,
+ there must be a credible and documented way to test the filesystem and
+ detect regressions. When submitting, include a summary of test results
+ indicating which tests pass, fail, or are not applicable.
+
+Provide documentation
+ A documentation file under ``Documentation/filesystems/`` describing the
+ filesystem, its on-disk format, mount options, and any notable design
+ decisions is recommended.
+
+
+Community and Maintainership Expectations
+-----------------------------------------
+
+Merging a filesystem is a long-term commitment. The kernel community
+needs confidence that the filesystem will be actively maintained after it
+is merged.
+
+Identified maintainers
+ The submission must include a ``MAINTAINERS`` entry with at least one
+ maintainer (``M:``), a mailing list (``L:``), and a git tree (``T:``).
+ Having two or more maintainers is strongly preferred so that coverage
+ does not depend on a single person. The maintainers are expected to be
+ the primary points of contact for the filesystem going forward.
+
+Demonstrated commitment
+ A track record of maintaining kernel code -- for example, in other
+ subsystems -- significantly strengthens the case for a new filesystem.
+ Maintainers who are already known and trusted within the community face
+ less friction during review.
+
+Sustained backing
+ Major filesystems in Linux have organizational or corporate support behind
+ their development. Filesystems that depend entirely on volunteer effort
+ face higher scrutiny about their long-term viability.
+
+Responsiveness
+ The maintainer is expected to respond to bug reports, address review
+ feedback, and adapt the filesystem to VFS infrastructure changes such as
+ folio conversions, iomap migration, and mount API updates. Unresponsive
+ maintainership is one of the primary reasons filesystems end up on the
+ path to deprecation.
+
+User base
+ Clearly describe who the users of this filesystem are and the scale of the
+ user base. Filesystems with a very small or unclear user base face a
+ harder path to acceptance and a higher risk of future deprecation.
+
+Building your track record
+ A practical way to demonstrate many of the qualities above is to maintain
+ the filesystem out-of-tree for a period before requesting a merge. This
+ shows sustained commitment, builds a visible user base, and gives reviewers
+ confidence that the code and its maintainer will persist after merging.
+ That said, it is recognized that for some filesystems the user base grows
+ significantly only after upstreaming, so a compelling case for expected
+ adoption can substitute for a large existing user base.
+
+
+Submission Process
+------------------
+
+This section covers what is specific to filesystem submissions, over and
+above the normal submission advice in
+:ref:`Documentation/process/submitting-patches.rst <submittingpatches>` and
+:ref:`Documentation/process/submit-checklist.rst <submitchecklist>`.
+
+ - Send patches to the linux-fsdevel mailing list
+ (``linux-fsdevel@vger.kernel.org``). CC the relevant VFS maintainers as
+ listed in the ``MAINTAINERS`` file under
+ ``FILESYSTEMS (VFS and infrastructure)``.
+
+ - Structure the submission logically. It is neither acceptable to send one
+ large patch containing the entire filesystem, nor is a replay of the full
+ development history helpful to reviewers. Instead, split the series by
+ topic -- for example: superblock and mount handling, inode operations,
+ directory operations, address space operations, and so on -- so that each
+ patch is reviewable in isolation.
+
+ - Separate any filesystem-specific ioctls into their own patches with
+ dedicated justification. Interfaces beyond those already common across
+ other filesystems will receive additional scrutiny because they are hard
+ to maintain and may conflict with future generic interfaces.
+
+ - Expect thorough review. Filesystem code interacts deeply with the VFS,
+ memory management, and block layers, so reviewers will examine the code
+ carefully. Address all review feedback and be prepared for multiple
+ revision cycles.
+
+ - It may be appropriate to mark the filesystem as experimental in its Kconfig
+ help text for the first few releases to set expectations while the code
+ stabilizes in-tree.
+
+
+Ongoing Obligations
+-------------------
+
+Merging is not the finish line. Maintaining a filesystem in the kernel is an
+ongoing commitment.
+
+ - Adapt to VFS infrastructure changes. The VFS layer evolves continuously;
+ maintainers are expected to keep up with conversions such as folio
+ migration, iomap adoption, and mount API updates.
+
+ - Maintain test coverage. As test suites evolve, the filesystem's test
+ results should be kept current.
+
+ - Handle security issues and regression promptly. Both those reported
+ by ordinary users and those reported by test bots and fuzzing tools.
+ The filesystem must handle corrupted input gracefully without corrupting
+ memory, hanging, or crashing the kernel.
+
+ - Engage with the wider filesystem community. Participate on linux-fsdevel,
+ share approaches to common problems, and look for opportunities to reuse
+ shared infrastructure. It is inappropriate to develop in isolation on a
+ private list and surface patches only at merge time.
+
+ - Filesystems that become unmaintained -- where the maintainer stops
+ responding, infrastructure changes go unadapted, and testing becomes
+ impossible -- are candidates for deprecation and eventual removal from
+ the kernel.
diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst
index fc7254d01a2b..1f71cf159547 100644
--- a/Documentation/filesystems/index.rst
+++ b/Documentation/filesystems/index.rst
@@ -43,6 +43,7 @@ algorithms work.
caching/index
porting
+ adding-new-filesystems
Filesystem support layers
=========================
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
index fdf074429cd3..f546b1d3897f 100644
--- a/Documentation/filesystems/porting.rst
+++ b/Documentation/filesystems/porting.rst
@@ -1297,7 +1297,6 @@ Several functions are renamed:
- kern_path_locked -> start_removing_path
- kern_path_create -> start_creating_path
- user_path_create -> start_creating_user_path
-- user_path_locked_at -> start_removing_user_path_at
- done_path_create -> end_creating_path
---
diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h
index 50bdc8e8a271..c7e1c5cf646d 100644
--- a/arch/alpha/include/uapi/asm/fcntl.h
+++ b/arch/alpha/include/uapi/asm/fcntl.h
@@ -2,20 +2,20 @@
#ifndef _ALPHA_FCNTL_H
#define _ALPHA_FCNTL_H
-#define O_CREAT 01000 /* not fcntl */
-#define O_TRUNC 02000 /* not fcntl */
-#define O_EXCL 04000 /* not fcntl */
-#define O_NOCTTY 010000 /* not fcntl */
-
-#define O_NONBLOCK 00004
-#define O_APPEND 00010
-#define O_DSYNC 040000 /* used to be O_SYNC, see below */
-#define O_DIRECTORY 0100000 /* must be a directory */
-#define O_NOFOLLOW 0200000 /* don't follow links */
-#define O_LARGEFILE 0400000 /* will be set by the kernel on every open */
-#define O_DIRECT 02000000 /* direct disk access - should check with OSF/1 */
-#define O_NOATIME 04000000
-#define O_CLOEXEC 010000000 /* set close_on_exec */
+#define O_CREAT (1 << 9) /* not fcntl */
+#define O_TRUNC (1 << 10) /* not fcntl */
+#define O_EXCL (1 << 11) /* not fcntl */
+#define O_NOCTTY (1 << 12) /* not fcntl */
+
+#define O_NONBLOCK (1 << 2)
+#define O_APPEND (1 << 3)
+#define O_DSYNC (1 << 14) /* used to be O_SYNC, see below */
+#define O_DIRECTORY (1 << 15) /* must be a directory */
+#define O_NOFOLLOW (1 << 16) /* don't follow links */
+#define O_LARGEFILE (1 << 17) /* will be set by the kernel on every open */
+#define O_DIRECT (1 << 19) /* direct disk access - should check with OSF/1 */
+#define O_NOATIME (1 << 20)
+#define O_CLOEXEC (1 << 21) /* set close_on_exec */
/*
* Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using
* the O_SYNC flag. We continue to use the existing numerical value
@@ -29,11 +29,11 @@
*
* Note: __O_SYNC must never be used directly.
*/
-#define __O_SYNC 020000000
+#define __O_SYNC (1 << 22)
#define O_SYNC (__O_SYNC|O_DSYNC)
-#define O_PATH 040000000
-#define __O_TMPFILE 0100000000
+#define O_PATH (1 << 23)
+#define __O_TMPFILE (1 << 24)
#define F_GETLK 7
#define F_SETLK 8
diff --git a/arch/arm/include/uapi/asm/fcntl.h b/arch/arm/include/uapi/asm/fcntl.h
index e6b5d7141c05..b576ff00beb2 100644
--- a/arch/arm/include/uapi/asm/fcntl.h
+++ b/arch/arm/include/uapi/asm/fcntl.h
@@ -2,10 +2,10 @@
#ifndef _ARM_FCNTL_H
#define _ARM_FCNTL_H
-#define O_DIRECTORY 040000 /* must be a directory */
-#define O_NOFOLLOW 0100000 /* don't follow links */
-#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */
-#define O_LARGEFILE 0400000
+#define O_DIRECTORY (1 << 14) /* must be a directory */
+#define O_NOFOLLOW (1 << 15) /* don't follow links */
+#define O_DIRECT (1 << 16) /* direct disk access hint - currently ignored */
+#define O_LARGEFILE (1 << 17)
#include <asm-generic/fcntl.h>
diff --git a/arch/arm64/include/uapi/asm/fcntl.h b/arch/arm64/include/uapi/asm/fcntl.h
index f8db34f2622d..e503fdb74ecb 100644
--- a/arch/arm64/include/uapi/asm/fcntl.h
+++ b/arch/arm64/include/uapi/asm/fcntl.h
@@ -20,10 +20,10 @@
/*
* Using our own definitions for AArch32 (compat) support.
*/
-#define O_DIRECTORY 040000 /* must be a directory */
-#define O_NOFOLLOW 0100000 /* don't follow links */
-#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */
-#define O_LARGEFILE 0400000
+#define O_DIRECTORY (1 << 14) /* must be a directory */
+#define O_NOFOLLOW (1 << 15) /* don't follow links */
+#define O_DIRECT (1 << 16) /* direct disk access hint - currently ignored */
+#define O_LARGEFILE (1 << 17)
#include <asm-generic/fcntl.h>
diff --git a/arch/m68k/include/uapi/asm/fcntl.h b/arch/m68k/include/uapi/asm/fcntl.h
index c6861e6ee313..66c0e5515105 100644
--- a/arch/m68k/include/uapi/asm/fcntl.h
+++ b/arch/m68k/include/uapi/asm/fcntl.h
@@ -2,10 +2,10 @@
#ifndef _M68K_FCNTL_H
#define _M68K_FCNTL_H
-#define O_DIRECTORY 040000 /* must be a directory */
-#define O_NOFOLLOW 0100000 /* don't follow links */
-#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */
-#define O_LARGEFILE 0400000
+#define O_DIRECTORY (1 << 14) /* must be a directory */
+#define O_NOFOLLOW (1 << 15) /* don't follow links */
+#define O_DIRECT (1 << 16) /* direct disk access hint - currently ignored */
+#define O_LARGEFILE (1 << 17)
#include <asm-generic/fcntl.h>
diff --git a/arch/mips/include/uapi/asm/fcntl.h b/arch/mips/include/uapi/asm/fcntl.h
index 0369a38e3d4f..549fc65d849d 100644
--- a/arch/mips/include/uapi/asm/fcntl.h
+++ b/arch/mips/include/uapi/asm/fcntl.h
@@ -11,15 +11,15 @@
#include <asm/sgidefs.h>
-#define O_APPEND 0x0008
-#define O_DSYNC 0x0010 /* used to be O_SYNC, see below */
-#define O_NONBLOCK 0x0080
-#define O_CREAT 0x0100 /* not fcntl */
-#define O_TRUNC 0x0200 /* not fcntl */
-#define O_EXCL 0x0400 /* not fcntl */
-#define O_NOCTTY 0x0800 /* not fcntl */
-#define FASYNC 0x1000 /* fcntl, for BSD compatibility */
-#define O_LARGEFILE 0x2000 /* allow large file opens */
+#define O_APPEND (1 << 3)
+#define O_DSYNC (1 << 4) /* used to be O_SYNC, see below */
+#define O_NONBLOCK (1 << 7)
+#define O_CREAT (1 << 8) /* not fcntl */
+#define O_TRUNC (1 << 9) /* not fcntl */
+#define O_EXCL (1 << 10) /* not fcntl */
+#define O_NOCTTY (1 << 11) /* not fcntl */
+#define FASYNC (1 << 12) /* fcntl, for BSD compatibility */
+#define O_LARGEFILE (1 << 13) /* allow large file opens */
/*
* Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using
* the O_SYNC flag. We continue to use the existing numerical value
@@ -33,9 +33,9 @@
*
* Note: __O_SYNC must never be used directly.
*/
-#define __O_SYNC 0x4000
+#define __O_SYNC (1 << 14)
#define O_SYNC (__O_SYNC|O_DSYNC)
-#define O_DIRECT 0x8000 /* direct disk access hint */
+#define O_DIRECT (1 << 15) /* direct disk access hint */
#define F_GETLK 14
#define F_SETLK 6
diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h
index 03dee816cb13..2e1bb18eefb8 100644
--- a/arch/parisc/include/uapi/asm/fcntl.h
+++ b/arch/parisc/include/uapi/asm/fcntl.h
@@ -2,23 +2,23 @@
#ifndef _PARISC_FCNTL_H
#define _PARISC_FCNTL_H
-#define O_APPEND 000000010
-#define O_CREAT 000000400 /* not fcntl */
-#define O_EXCL 000002000 /* not fcntl */
-#define O_LARGEFILE 000004000
-#define __O_SYNC 000100000
+#define O_APPEND (1 << 3)
+#define O_CREAT (1 << 8) /* not fcntl */
+#define O_EXCL (1 << 10) /* not fcntl */
+#define O_LARGEFILE (1 << 11)
+#define __O_SYNC (1 << 15)
#define O_SYNC (__O_SYNC|O_DSYNC)
-#define O_NONBLOCK 000200000
-#define O_NOCTTY 000400000 /* not fcntl */
-#define O_DSYNC 001000000
-#define O_NOATIME 004000000
-#define O_CLOEXEC 010000000 /* set close_on_exec */
+#define O_NONBLOCK (1 << 16)
+#define O_NOCTTY (1 << 17) /* not fcntl */
+#define O_DSYNC (1 << 18)
+#define O_NOATIME (1 << 20)
+#define O_CLOEXEC (1 << 21) /* set close_on_exec */
-#define O_DIRECTORY 000010000 /* must be a directory */
-#define O_NOFOLLOW 000000200 /* don't follow links */
+#define O_DIRECTORY (1 << 12) /* must be a directory */
+#define O_NOFOLLOW (1 << 7) /* don't follow links */
-#define O_PATH 020000000
-#define __O_TMPFILE 040000000
+#define O_PATH (1 << 22)
+#define __O_TMPFILE (1 << 23)
#define F_GETLK64 8
#define F_SETLK64 9
diff --git a/arch/powerpc/include/uapi/asm/fcntl.h b/arch/powerpc/include/uapi/asm/fcntl.h
index 65ce08322a89..003bc5ea78e1 100644
--- a/arch/powerpc/include/uapi/asm/fcntl.h
+++ b/arch/powerpc/include/uapi/asm/fcntl.h
@@ -2,10 +2,10 @@
#ifndef _ASM_FCNTL_H
#define _ASM_FCNTL_H
-#define O_DIRECTORY 040000 /* must be a directory */
-#define O_NOFOLLOW 0100000 /* don't follow links */
-#define O_LARGEFILE 0200000
-#define O_DIRECT 0400000 /* direct disk access hint */
+#define O_DIRECTORY (1 << 14) /* must be a directory */
+#define O_NOFOLLOW (1 << 15) /* don't follow links */
+#define O_LARGEFILE (1 << 16)
+#define O_DIRECT (1 << 17) /* direct disk access hint */
#include <asm-generic/fcntl.h>
diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h
index 67dae75e5274..29c5639bc3fa 100644
--- a/arch/sparc/include/uapi/asm/fcntl.h
+++ b/arch/sparc/include/uapi/asm/fcntl.h
@@ -2,23 +2,23 @@
#ifndef _SPARC_FCNTL_H
#define _SPARC_FCNTL_H
-#define O_APPEND 0x0008
-#define FASYNC 0x0040 /* fcntl, for BSD compatibility */
-#define O_CREAT 0x0200 /* not fcntl */
-#define O_TRUNC 0x0400 /* not fcntl */
-#define O_EXCL 0x0800 /* not fcntl */
-#define O_DSYNC 0x2000 /* used to be O_SYNC, see below */
-#define O_NONBLOCK 0x4000
+#define O_APPEND (1 << 3)
+#define FASYNC (1 << 6) /* fcntl, for BSD compatibility */
+#define O_CREAT (1 << 9) /* not fcntl */
+#define O_TRUNC (1 << 10) /* not fcntl */
+#define O_EXCL (1 << 11) /* not fcntl */
+#define O_DSYNC (1 << 13) /* used to be O_SYNC, see below */
+#define O_NONBLOCK (1 << 14)
#if defined(__sparc__) && defined(__arch64__)
-#define O_NDELAY 0x0004
+#define O_NDELAY (1 << 2)
#else
-#define O_NDELAY (0x0004 | O_NONBLOCK)
+#define O_NDELAY ((1 << 2) | O_NONBLOCK)
#endif
-#define O_NOCTTY 0x8000 /* not fcntl */
-#define O_LARGEFILE 0x40000
-#define O_DIRECT 0x100000 /* direct disk access hint */
-#define O_NOATIME 0x200000
-#define O_CLOEXEC 0x400000
+#define O_NOCTTY (1 << 15) /* not fcntl */
+#define O_LARGEFILE (1 << 18)
+#define O_DIRECT (1 << 20) /* direct disk access hint */
+#define O_NOATIME (1 << 21)
+#define O_CLOEXEC (1 << 22)
/*
* Before Linux 2.6.33 only O_DSYNC semantics were implemented, but using
* the O_SYNC flag. We continue to use the existing numerical value
@@ -32,11 +32,11 @@
*
* Note: __O_SYNC must never be used directly.
*/
-#define __O_SYNC 0x800000
+#define __O_SYNC (1 << 23)
#define O_SYNC (__O_SYNC|O_DSYNC)
-#define O_PATH 0x1000000
-#define __O_TMPFILE 0x2000000
+#define O_PATH (1 << 24)
+#define __O_TMPFILE (1 << 25)
#define F_GETOWN 5 /* for sockets. */
#define F_SETOWN 6 /* for sockets. */
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index a0caf6ace860..44a3f69d275f 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -227,11 +227,6 @@ static inline bool affs_validblock(struct super_block *sb, int block)
block < AFFS_SB(sb)->s_partition_size);
}
-static inline void
-affs_set_blocksize(struct super_block *sb, int size)
-{
- sb_set_blocksize(sb, size);
-}
static inline struct buffer_head *
affs_bread(struct super_block *sb, int block)
{
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 079f36e1ddec..b232251aa7bb 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -358,7 +358,8 @@ static int affs_fill_super(struct super_block *sb, struct fs_context *fc)
size = bdev_nr_sectors(sb->s_bdev);
pr_debug("initial blocksize=%d, #blocks=%d\n", 512, size);
- affs_set_blocksize(sb, PAGE_SIZE);
+ if (!sb_set_blocksize(sb, PAGE_SIZE))
+ return -EINVAL;
/* Try to find root block. Its location depends on the block size. */
i = bdev_logical_block_size(sb->s_bdev);
@@ -374,7 +375,8 @@ static int affs_fill_super(struct super_block *sb, struct fs_context *fc)
if (ctx->root_block < 0)
sbi->s_root_block = (ctx->reserved + size - 1) / 2;
pr_debug("setting blocksize to %d\n", blocksize);
- affs_set_blocksize(sb, blocksize);
+ if (!sb_set_blocksize(sb, blocksize))
+ return -EINVAL;
sbi->s_partition_size = size;
/* The root block location that was calculated above is not
diff --git a/fs/aio.c b/fs/aio.c
index 722476560848..f57fa21a2503 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -318,7 +318,6 @@ static int aio_init_fs_context(struct fs_context *fc)
pfc = init_pseudo(fc, AIO_RING_MAGIC);
if (!pfc)
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC;
pfc->ops = &aio_super_operations;
return 0;
}
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index b8381c7fb636..a7b9b948e33d 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -86,8 +86,6 @@ static int anon_inodefs_init_fs_context(struct fs_context *fc)
struct pseudo_fs_context *ctx = init_pseudo(fc, ANON_INODE_FS_MAGIC);
if (!ctx)
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC;
- fc->s_iflags |= SB_I_NODEV;
ctx->dops = &anon_inodefs_dentry_operations;
return 0;
}
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 1f3bbfc75882..080c99696cd0 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -18,17 +18,18 @@
/**
* backing_file_open - open a backing file for kernel internal use
- * @user_path: path that the user reuqested to open
+ * @user_file: file the user requested to open
* @flags: open flags
* @real_path: path of the backing file
* @cred: credentials for open
*
* Open a backing file for a stackable filesystem (e.g., overlayfs).
- * @user_path may be on the stackable filesystem and @real_path on the
- * underlying filesystem. In this case, we want to be able to return the
- * @user_path of the stackable filesystem. This is done by embedding the
- * returned file into a container structure that also stores the stacked
- * file's path, which can be retrieved using backing_file_user_path().
+ * @user_file->f_path may be on the stackable filesystem and @real_path
+ * on the underlying filesystem. In this case, we want to be able to
+ * return the path of the stackable filesystem. This is done by
+ * embedding the returned file into a container structure that also
+ * stores the stacked file's path, which can be retrieved using
+ * backing_file_user_path().
*/
struct file *backing_file_open(const struct file *user_file, int flags,
const struct path *real_path,
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index c12caae9a967..ee0cbae521b9 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -860,7 +860,8 @@ befs_fill_super(struct super_block *sb, struct fs_context *fc)
*/
sb->s_magic = BEFS_SUPER_MAGIC;
/* Set real blocksize of fs */
- sb_set_blocksize(sb, (ulong) befs_sb->block_size);
+ if (!sb_set_blocksize(sb, (ulong) befs_sb->block_size))
+ goto unacquire_priv_sbp;
sb->s_op = &befs_sops;
sb->s_export_op = &befs_export_operations;
sb->s_time_min = 0;
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index 19e49c8cf750..e41efdd35db9 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -311,7 +311,7 @@ void bfs_dump_imap(const char *prefix, struct super_block *s)
{
#ifdef DEBUG
int i;
- char *tmpbuf = (char *)get_zeroed_page(GFP_KERNEL);
+ char *tmpbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!tmpbuf)
return;
@@ -323,7 +323,7 @@ void bfs_dump_imap(const char *prefix, struct super_block *s)
strcat(tmpbuf, "0");
}
printf("%s: lasti=%08lx <%s>\n", prefix, BFS_SB(s)->si_lasti, tmpbuf);
- free_page((unsigned long)tmpbuf);
+ kfree(tmpbuf);
#endif
}
@@ -346,7 +346,8 @@ static int bfs_fill_super(struct super_block *s, struct fs_context *fc)
s->s_time_min = 0;
s->s_time_max = U32_MAX;
- sb_set_blocksize(s, BFS_BSIZE);
+ if (!sb_set_blocksize(s, BFS_BSIZE))
+ goto out;
sbh = sb_bread(s, 0);
if (!sbh)
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index b3d8fd70e8b1..84349fcb93f1 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -704,7 +704,7 @@ bm_entry_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
ssize_t res;
char *page;
- page = (char *) __get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -712,7 +712,7 @@ bm_entry_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
res = simple_read_from_buffer(buf, nbytes, ppos, page, strlen(page));
- free_page((unsigned long) page);
+ kfree(page);
return res;
}
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 606319dd69e8..11841c3d4260 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -359,6 +359,21 @@ __bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__s
}
#endif /* CONFIG_CGROUPS */
+/**
+ * bpf_real_inode - get the real inode backing a dentry
+ * @dentry: dentry to resolve
+ *
+ * If the dentry is on a union/overlay filesystem, return the underlying, real
+ * inode that hosts the data. Otherwise return the inode attached to the
+ * dentry itself.
+ *
+ * Return: The real inode backing the dentry, or NULL for a negative dentry.
+ */
+__bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
+{
+ return d_real_inode(dentry);
+}
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -369,6 +384,7 @@ BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
diff --git a/fs/buffer.c b/fs/buffer.c
index 7ed8dd77d221..9af5f061a1f8 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -71,9 +71,7 @@ EXPORT_SYMBOL(__lock_buffer);
void unlock_buffer(struct buffer_head *bh)
{
- clear_bit_unlock(BH_Lock, &bh->b_state);
- smp_mb__after_atomic();
- wake_up_bit(&bh->b_state, BH_Lock);
+ clear_and_wake_up_bit(BH_Lock, &bh->b_state);
}
EXPORT_SYMBOL(unlock_buffer);
diff --git a/fs/configfs/file.c b/fs/configfs/file.c
index ef8c3cd10cc6..a48cece775a3 100644
--- a/fs/configfs/file.c
+++ b/fs/configfs/file.c
@@ -59,7 +59,7 @@ static int fill_read_buffer(struct file *file, struct configfs_buffer *buffer)
ssize_t count = -ENOENT;
if (!buffer->page)
- buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
+ buffer->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
@@ -184,7 +184,7 @@ static int fill_write_buffer(struct configfs_buffer *buffer,
int copied;
if (!buffer->page)
- buffer->page = (char *)__get_free_pages(GFP_KERNEL, 0);
+ buffer->page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
@@ -381,8 +381,7 @@ static int configfs_release(struct inode *inode, struct file *filp)
struct configfs_buffer *buffer = filp->private_data;
module_put(buffer->owner);
- if (buffer->page)
- free_page((unsigned long)buffer->page);
+ kfree(buffer->page);
mutex_destroy(&buffer->mutex);
kfree(buffer);
return 0;
diff --git a/fs/coredump.c b/fs/coredump.c
index e943569e9b6d..e68a76ff92a3 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1482,7 +1482,8 @@ static int proc_dostring_coredump(const struct ctl_table *table, int write,
return -EINVAL;
}
- validate_coredump_safety();
+ if (strncmp(old_core_pattern, core_pattern, CORENAME_MAX_SIZE))
+ validate_coredump_safety();
return error;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index c8cc7c26b725..d6f505313205 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -426,9 +426,16 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
this_cpu_inc(nr_dentry_negative);
}
+#define DENTRY_WARN_ONCE(condition, dentry) \
+ WARN_ONCE((condition), "dentry=%p d_flags=0x%x\n", (dentry), (dentry)->d_flags)
+#define D_FLAG_VERIFY(dentry, x) \
+ DENTRY_WARN_ONCE(((dentry)->d_flags & (DCACHE_LRU_LIST | DCACHE_SHRINK_LIST)) != (x), (dentry))
+
static void dentry_free(struct dentry *dentry)
{
- WARN_ON(d_really_is_positive(dentry));
+ DENTRY_WARN_ONCE(d_really_is_positive(dentry), dentry);
+ DENTRY_WARN_ONCE(dentry->d_lockref.count >= 0, dentry);
+ D_FLAG_VERIFY(dentry, 0);
if (unlikely(dname_external(dentry))) {
struct external_name *p = external_name(dentry);
if (likely(atomic_dec_and_test(&p->count))) {
@@ -495,7 +502,6 @@ static void dentry_unlink_inode(struct dentry * dentry)
* These helper functions make sure we always follow the
* rules. d_lock must be held by the caller.
*/
-#define D_FLAG_VERIFY(dentry,x) WARN_ON_ONCE(((dentry)->d_flags & (DCACHE_LRU_LIST | DCACHE_SHRINK_LIST)) != (x))
static void d_lru_add(struct dentry *dentry)
{
D_FLAG_VERIFY(dentry, 0);
@@ -1820,10 +1826,10 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
name = &slash_name;
dname = dentry->d_shortname.string;
} else if (name->len > DNAME_INLINE_LEN-1) {
- size_t size = offsetof(struct external_name, name[1]);
- struct external_name *p = kmalloc(size + name->len,
- GFP_KERNEL_ACCOUNT |
- __GFP_RECLAIMABLE);
+ struct external_name *p;
+
+ p = kmalloc_flex(*p, name, name->len + 1,
+ GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE);
if (!p) {
kmem_cache_free(dentry_cache, dentry);
return NULL;
diff --git a/fs/exec.c b/fs/exec.c
index 824b46c069ae..b92fe7db176c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1994,9 +1994,11 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd,
static int proc_dointvec_minmax_coredump(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
- int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ int error, old = READ_ONCE(suid_dumpable);
- if (!error && write)
+ error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (!error && write && (old != READ_ONCE(suid_dumpable)))
validate_coredump_safety();
return error;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index b3ea135b74d8..c158f082f1da 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -929,11 +929,11 @@ void send_sigio(struct fown_struct *fown, int fd, int band)
send_sigio_to_task(p, fown, fd, band, type);
rcu_read_unlock();
} else {
- read_lock(&tasklist_lock);
+ rcu_read_lock();
do_each_pid_task(pid, type, p) {
send_sigio_to_task(p, fown, fd, band, type);
} while_each_pid_task(pid, type, p);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
}
out_unlock_fown:
read_unlock_irqrestore(&fown->lock, flags);
@@ -975,11 +975,11 @@ int send_sigurg(struct file *file)
send_sigurg_to_task(p, fown, type);
rcu_read_unlock();
} else {
- read_lock(&tasklist_lock);
+ rcu_read_lock();
do_each_pid_task(pid, type, p) {
send_sigurg_to_task(p, fown, type);
} while_each_pid_task(pid, type, p);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
}
out_unlock_fown:
read_unlock_irqrestore(&fown->lock, flags);
diff --git a/fs/file.c b/fs/file.c
index 2c81c0b162d0..628ca07dc4b1 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -544,24 +544,23 @@ struct files_struct init_files = {
static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
{
unsigned int maxfd = fdt->max_fds; /* always multiple of BITS_PER_LONG */
- unsigned int maxbit = maxfd / BITS_PER_LONG;
- unsigned int bitbit = start / BITS_PER_LONG;
+ unsigned int max_fds_words = maxfd / BITS_PER_LONG;
+ unsigned int fds_word_idx = start / BITS_PER_LONG;
unsigned int bit;
/*
* Try to avoid looking at the second level bitmap
*/
- bit = find_next_zero_bit(&fdt->open_fds[bitbit], BITS_PER_LONG,
+ bit = find_next_zero_bit(&fdt->open_fds[fds_word_idx], BITS_PER_LONG,
start & (BITS_PER_LONG - 1));
if (bit < BITS_PER_LONG)
- return bit + bitbit * BITS_PER_LONG;
+ return bit + (fds_word_idx * BITS_PER_LONG);
- bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;
- if (bitbit >= maxfd)
+ bit = BITS_PER_LONG *
+ find_next_zero_bit(fdt->full_fds_bits, max_fds_words, fds_word_idx + 1);
+ if (bit >= maxfd)
return maxfd;
- if (bitbit > start)
- start = bitbit;
- return find_next_zero_bit(fdt->open_fds, maxfd, start);
+ return find_next_zero_bit(fdt->open_fds, maxfd, bit);
}
/*
@@ -1134,7 +1133,6 @@ struct file *fget_task(struct task_struct *task, unsigned int fd)
struct file *fget_task_next(struct task_struct *task, unsigned int *ret_fd)
{
- /* Must be called with rcu_read_lock held */
struct files_struct *files;
unsigned int fd = *ret_fd;
struct file *file = NULL;
diff --git a/fs/file_table.c b/fs/file_table.c
index 16e52e7fc2ac..3c08832aa387 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -231,13 +231,13 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
}
/* Find an unused file structure and return a pointer to it.
- * Returns an error pointer if some error happend e.g. we over file
+ * Returns an error pointer if some error happened, e.g., we exceed the file
* structures limit, run out of memory or operation is not permitted.
*
* Be very careful using this. You are responsible for
* getting write access to any mount that you might assign
* to this filp, if it is opened for write. If this is not
- * done, you will imbalance int the mount's writer count
+ * done, the mount's writer count will be wrong
* and a warning at __fput() time.
*/
struct file *alloc_empty_file(int flags, const struct cred *cred)
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index fdc175e93f74..3614ea603913 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -10,6 +10,7 @@
#include <linux/fileattr.h>
#include <linux/fsverity.h>
+#include <linux/slab.h>
#define FUSE_VERITY_ENABLE_ARG_MAX_PAGES 256
static ssize_t fuse_send_ioctl(struct fuse_mount *fm, struct fuse_args *args,
@@ -252,7 +253,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
err = -ENOMEM;
ap.folios = fuse_folios_alloc(fm->fc->max_pages, GFP_KERNEL, &ap.descs);
- iov_page = (struct iovec *) __get_free_page(GFP_KERNEL);
+ iov_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ap.folios || !iov_page)
goto out;
@@ -400,7 +401,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
}
err = 0;
out:
- free_page((unsigned long) iov_page);
+ kfree(iov_page);
while (ap.num_folios)
folio_put(ap.folios[--ap.num_folios]);
kfree(ap.folios);
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index c16d5d4caead..8fbdbf080627 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -523,7 +523,8 @@ static int hpfs_fill_super(struct super_block *s, struct fs_context *fc)
hpfs_lock(s);
/*sbi->sb_mounting = 1;*/
- sb_set_blocksize(s, 512);
+ if (!sb_set_blocksize(s, 512))
+ goto bail0;
sbi->sb_fs_size = -1;
if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 5a107d59ae27..8d4806dc46d4 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -905,7 +905,7 @@ static int __iomap_write_begin(const struct iomap_iter *iter,
if (status < 0)
fserror_report_io(iter->inode,
FSERR_BUFFERED_READ, pos,
- len, status, GFP_NOFS);
+ plen, status, GFP_NOFS);
if (status)
return status;
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index 55385a72a4ce..cc587cd25162 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -13,6 +13,7 @@
*/
#include <linux/gfp.h>
#include <linux/filelock.h>
+#include <linux/slab.h>
#include "isofs.h"
#include <linux/fileattr.h>
@@ -256,7 +257,7 @@ static int isofs_readdir(struct file *file, struct dir_context *ctx)
struct iso_directory_record *tmpde;
struct inode *inode = file_inode(file);
- tmpname = (char *)__get_free_page(GFP_KERNEL);
+ tmpname = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (tmpname == NULL)
return -ENOMEM;
@@ -264,7 +265,7 @@ static int isofs_readdir(struct file *file, struct dir_context *ctx)
result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde);
- free_page((unsigned long) tmpname);
+ kfree(tmpname);
return result;
}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index efee53717f1c..337836a0a170 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -818,7 +818,8 @@ root_found:
* entries. By forcing the blocksize in this way, we ensure
* that we will never be required to do this.
*/
- sb_set_blocksize(s, orig_zonesize);
+ if (!sb_set_blocksize(s, orig_zonesize))
+ goto out_freesbi;
sbi->s_nls_iocharset = NULL;
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 4e91593d27e5..d8577725a2fb 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -41,9 +41,7 @@ static void journal_end_buffer_io_sync(struct bio *bio)
else
clear_buffer_uptodate(bh);
if (orig_bh) {
- clear_bit_unlock(BH_Shadow, &orig_bh->b_state);
- smp_mb__after_atomic();
- wake_up_bit(&orig_bh->b_state, BH_Shadow);
+ clear_and_wake_up_bit(BH_Shadow, &orig_bh->b_state);
}
unlock_buffer(bh);
}
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 2040af8c84cb..e82798680109 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -2782,7 +2782,7 @@ void *jbd2_alloc(size_t size, gfp_t flags)
if (size < PAGE_SIZE)
ptr = kmem_cache_alloc(get_slab(size), flags);
else
- ptr = (void *)__get_free_pages(flags, get_order(size));
+ ptr = kmalloc(size, flags);
/* Check alignment; SLUB has gotten this wrong in the past,
* and this can lead to user data corruption! */
@@ -2793,10 +2793,7 @@ void *jbd2_alloc(size_t size, gfp_t flags)
void jbd2_free(void *ptr, size_t size)
{
- if (size < PAGE_SIZE)
- kmem_cache_free(get_slab(size), ptr);
- else
- free_pages((unsigned long)ptr, get_order(size));
+ kfree(ptr);
};
/*
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index ac0f79fafaca..8ce6e4458cc2 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -2729,7 +2729,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
struct ldtentry *d;
struct dtslot *t;
int d_namleft, len, outlen;
- unsigned long dirent_buf;
+ void *dirent_buf;
char *name_ptr;
u32 dir_index;
int do_index = 0;
@@ -2884,7 +2884,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
}
}
- dirent_buf = __get_free_page(GFP_KERNEL);
+ dirent_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (dirent_buf == 0) {
DT_PUTPAGE(mp);
jfs_warn("jfs_readdir: __get_free_page failed!");
@@ -2893,7 +2893,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
}
while (1) {
- jfs_dirent = (struct jfs_dirent *) dirent_buf;
+ jfs_dirent = dirent_buf;
jfs_dirents = 0;
overflow = fix_page = 0;
@@ -2903,7 +2903,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
if (stbl[i] < 0) {
jfs_err("JFS: Invalid stbl[%d] = %d for inode %ld, block = %lld",
i, stbl[i], (long)ip->i_ino, (long long)bn);
- free_page(dirent_buf);
+ kfree(dirent_buf);
DT_PUTPAGE(mp);
return -EIO;
}
@@ -2911,7 +2911,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
d = (struct ldtentry *) & p->slot[stbl[i]];
if (((long) jfs_dirent + d->namlen + 1) >
- (dirent_buf + PAGE_SIZE)) {
+ ((long)dirent_buf + PAGE_SIZE)) {
/* DBCS codepages could overrun dirent_buf */
index = i;
overflow = 1;
@@ -3014,7 +3014,7 @@ skip_one:
/* unpin previous leaf page */
DT_PUTPAGE(mp);
- jfs_dirent = (struct jfs_dirent *) dirent_buf;
+ jfs_dirent = dirent_buf;
while (jfs_dirents--) {
ctx->pos = jfs_dirent->position;
if (!dir_emit(ctx, jfs_dirent->name,
@@ -3037,13 +3037,13 @@ skip_one:
DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
if (rc) {
- free_page(dirent_buf);
+ kfree(dirent_buf);
return rc;
}
}
out:
- free_page(dirent_buf);
+ kfree(dirent_buf);
return rc;
}
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 61575f7397ae..8180d83d33fe 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -491,7 +491,8 @@ static int jfs_fill_super(struct super_block *sb, struct fs_context *fc)
/*
* Initialize blocksize to 4K.
*/
- sb_set_blocksize(sb, PSIZE);
+ if (!sb_set_blocksize(sb, PSIZE))
+ goto out_unload;
/*
* Set method vectors.
diff --git a/fs/libfs.c b/fs/libfs.c
index 1bbea5e7bae3..124139645f7f 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -736,6 +736,7 @@ struct pseudo_fs_context *init_pseudo(struct fs_context *fc,
fc->fs_private = ctx;
fc->ops = &pseudo_fs_context_ops;
fc->sb_flags |= SB_NOUSER;
+ fc->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
fc->global = true;
}
return ctx;
@@ -1258,7 +1259,7 @@ char *simple_transaction_get(struct file *file, const char __user *buf, size_t s
if (size > SIMPLE_TRANSACTION_LIMIT - 1)
return ERR_PTR(-EFBIG);
- ar = (struct simple_transaction_argresp *)get_zeroed_page(GFP_KERNEL);
+ ar = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!ar)
return ERR_PTR(-ENOMEM);
@@ -1267,7 +1268,7 @@ char *simple_transaction_get(struct file *file, const char __user *buf, size_t s
/* only one write allowed per open */
if (file->private_data) {
spin_unlock(&simple_transaction_lock);
- free_page((unsigned long)ar);
+ kfree(ar);
return ERR_PTR(-EBUSY);
}
@@ -1294,7 +1295,7 @@ EXPORT_SYMBOL(simple_transaction_read);
int simple_transaction_release(struct inode *inode, struct file *file)
{
- free_page((unsigned long)file->private_data);
+ kfree(file->private_data);
return 0;
}
EXPORT_SYMBOL(simple_transaction_release);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 9c6bac248907..c30cc590698d 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -292,7 +292,8 @@ static int minix_fill_super(struct super_block *s, struct fs_context *fc)
sbi->s_namelen = 60;
sbi->s_version = MINIX_V3;
sbi->s_mount_state = MINIX_VALID_FS;
- sb_set_blocksize(s, m3s->s_blocksize);
+ if (!sb_set_blocksize(s, m3s->s_blocksize))
+ goto out_release;
s->s_max_links = MINIX2_LINK_MAX;
} else
goto out_no_fs;
diff --git a/fs/namei.c b/fs/namei.c
index 6b87b40c208f..8340dc4108be 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2960,16 +2960,17 @@ void end_dirop(struct dentry *de)
EXPORT_SYMBOL(end_dirop);
/* does lookup, returns the object with parent locked */
-static struct dentry *__start_removing_path(int dfd, struct filename *name,
- struct path *path)
+struct dentry *start_removing_path(const char *name, struct path *path)
{
+ CLASS(filename_kernel, filename)(name);
struct path parent_path __free(path_put) = {};
struct dentry *d;
struct qstr last;
enum last_type type;
int error;
- error = filename_parentat(dfd, name, 0, &parent_path, &last, &type);
+ error = filename_parentat(AT_FDCWD, filename, 0, &parent_path, &last,
+ &type);
if (error)
return ERR_PTR(error);
if (unlikely(type != LAST_NORM))
@@ -3030,21 +3031,6 @@ struct dentry *kern_path_parent(const char *name, struct path *path)
return d;
}
-struct dentry *start_removing_path(const char *name, struct path *path)
-{
- CLASS(filename_kernel, filename)(name);
- return __start_removing_path(AT_FDCWD, filename, path);
-}
-
-struct dentry *start_removing_user_path_at(int dfd,
- const char __user *name,
- struct path *path)
-{
- CLASS(filename, filename)(name);
- return __start_removing_path(dfd, filename, path);
-}
-EXPORT_SYMBOL(start_removing_user_path_at);
-
int kern_path(const char *name, unsigned int flags, struct path *path)
{
CLASS(filename_kernel, filename)(name);
@@ -3631,7 +3617,6 @@ int path_pts(struct path *path)
*/
struct dentry *parent = dget_parent(path->dentry);
struct dentry *child;
- struct qstr this = QSTR_INIT("pts", 3);
if (unlikely(!path_connected(path->mnt, parent))) {
dput(parent);
@@ -3639,7 +3624,7 @@ int path_pts(struct path *path)
}
dput(path->dentry);
path->dentry = parent;
- child = d_hash_and_lookup(parent, &this);
+ child = d_hash_and_lookup(parent, &QSTR("pts"));
if (IS_ERR_OR_NULL(child))
return -ENOENT;
diff --git a/fs/namespace.c b/fs/namespace.c
index 341ddd353b3a..d4cf40198e92 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3306,9 +3306,9 @@ static void mnt_warn_timestamp_expiry(const struct path *mountpoint,
(ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
char *buf, *mntpath;
- buf = (char *)__get_free_page(GFP_KERNEL);
+ buf = __getname();
if (buf)
- mntpath = d_path(mountpoint, buf, PAGE_SIZE);
+ mntpath = d_path(mountpoint, buf, PATH_MAX);
else
mntpath = ERR_PTR(-ENOMEM);
if (IS_ERR(mntpath))
@@ -3321,8 +3321,7 @@ static void mnt_warn_timestamp_expiry(const struct path *mountpoint,
(unsigned long long)sb->s_time_max);
sb->s_iflags |= SB_I_TS_EXPIRY_WARNED;
- if (buf)
- free_page((unsigned long)buf);
+ __putname(buf);
}
}
@@ -4502,6 +4501,10 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
new_mnt = vfs_create_mount(fc);
if (IS_ERR(new_mnt))
return PTR_ERR(new_mnt);
+ if (new_mnt->mnt_sb->s_flags & SB_NOUSER) {
+ mntput(new_mnt);
+ return -EINVAL;
+ }
new_mnt->mnt_flags = mnt_flags;
new_path.dentry = dget(fc->root);
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index c105882edd16..1967de7d1dff 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -1769,7 +1769,9 @@ struct file_system_type nfs_fs_type = {
.init_fs_context = nfs_init_fs_context,
.parameters = nfs_fs_parameters,
.kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+ .fs_flags = FS_RENAME_DOES_D_MOVE |
+ FS_BINARY_MOUNTDATA |
+ FS_USERNS_DELEGATABLE,
};
MODULE_ALIAS_FS("nfs");
EXPORT_SYMBOL_GPL(nfs_fs_type);
@@ -1781,7 +1783,9 @@ struct file_system_type nfs4_fs_type = {
.init_fs_context = nfs_init_fs_context,
.parameters = nfs_fs_parameters,
.kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+ .fs_flags = FS_RENAME_DOES_D_MOVE |
+ FS_BINARY_MOUNTDATA |
+ FS_USERNS_DELEGATABLE,
};
MODULE_ALIAS_FS("nfs4");
MODULE_ALIAS("nfs4");
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index 14f72baf3b30..2a03f02bba7c 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -481,7 +481,6 @@ int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
* Returns zero on success, or a negative errno value.
*/
static int nfs4_try_replacing_one_location(struct nfs_server *server,
- char *page, char *page2,
const struct nfs4_fs_location *location)
{
struct net *net = rpc_net_ns(server->client);
@@ -541,21 +540,12 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server,
int nfs4_replace_transport(struct nfs_server *server,
const struct nfs4_fs_locations *locations)
{
- char *page = NULL, *page2 = NULL;
int loc, error;
error = -ENOENT;
if (locations == NULL || locations->nlocations <= 0)
goto out;
- error = -ENOMEM;
- page = (char *) __get_free_page(GFP_USER);
- if (!page)
- goto out;
- page2 = (char *) __get_free_page(GFP_USER);
- if (!page2)
- goto out;
-
for (loc = 0; loc < locations->nlocations; loc++) {
const struct nfs4_fs_location *location =
&locations->locations[loc];
@@ -564,14 +554,11 @@ int nfs4_replace_transport(struct nfs_server *server,
location->rootpath.ncomponents == 0)
continue;
- error = nfs4_try_replacing_one_location(server, page,
- page2, location);
+ error = nfs4_try_replacing_one_location(server, location);
if (error == 0)
break;
}
out:
- free_page((unsigned long)page);
- free_page((unsigned long)page2);
return error;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 4cd420b14ce3..8f8a03a68d3d 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -623,7 +623,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
int nfs_show_devname(struct seq_file *m, struct dentry *root)
{
- char *page = (char *) __get_free_page(GFP_KERNEL);
+ char *page = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *devname, *dummy;
int err = 0;
if (!page)
@@ -633,7 +633,7 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root)
err = PTR_ERR(devname);
else
seq_escape(m, devname, " \t\n\\");
- free_page((unsigned long)page);
+ kfree(page);
return err;
}
EXPORT_SYMBOL_GPL(nfs_show_devname);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ba97e287c007..653cd6fea202 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2408,7 +2408,7 @@ static __be32 nfsd_buffered_readdir(struct file *file, struct svc_fh *fhp,
loff_t offset;
struct readdir_data buf = {
.ctx.actor = nfsd_buffered_filldir,
- .dirent = (void *)__get_free_page(GFP_KERNEL)
+ .dirent = kmalloc(PAGE_SIZE, GFP_KERNEL)
};
if (!buf.dirent)
@@ -2459,7 +2459,7 @@ static __be32 nfsd_buffered_readdir(struct file *file, struct svc_fh *fhp,
offset = vfs_llseek(file, 0, SEEK_CUR);
}
- free_page((unsigned long)(buf.dirent));
+ kfree((buf.dirent));
if (host_err)
return nfserrno(host_err);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index e0a606643e87..b73f2c5d10f0 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -69,7 +69,7 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
if (argv->v_index > ~(__u64)0 - argv->v_nmembs)
return -EINVAL;
- buf = (void *)get_zeroed_page(GFP_NOFS);
+ buf = kzalloc(PAGE_SIZE, GFP_NOFS);
if (unlikely(!buf))
return -ENOMEM;
maxmembs = PAGE_SIZE / argv->v_size;
@@ -107,7 +107,7 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
}
argv->v_nmembs = total;
- free_pages((unsigned long)buf, 0);
+ kfree(buf);
return ret;
}
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 160018c4fb36..c3b6ae76594a 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -664,7 +664,6 @@ static int nsfs_init_fs_context(struct fs_context *fc)
struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC);
if (!ctx)
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
ctx->s_d_flags |= DCACHE_DONTCACHE;
ctx->ops = &nsfs_ops;
ctx->eops = &nsfs_export_operations;
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 004f59937559..3305fe406cb2 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -1174,7 +1174,10 @@ read_boot:
rec->total = cpu_to_le32(sbi->record_size);
((struct ATTRIB *)Add2Ptr(rec, ao))->type = ATTR_END;
- sb_set_blocksize(sb, min_t(u32, sbi->cluster_size, PAGE_SIZE));
+ if (!sb_set_blocksize(sb, min_t(u32, sbi->cluster_size, PAGE_SIZE))) {
+ err = -EINVAL;
+ goto out;
+ }
sbi->block_mask = sb->s_blocksize - 1;
sbi->blocks_per_cluster = sbi->cluster_size >> sb->s_blocksize_bits;
@@ -1225,7 +1228,8 @@ out:
/*
* Try alternative boot (last sector)
*/
- sb_set_blocksize(sb, block_size);
+ if (!sb_set_blocksize(sb, block_size))
+ return -EINVAL;
hint = "Alternative boot";
dev_size = dev_size0; /* restore original size. */
goto read_boot;
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
index fe4fdd09bae3..6ca8b3b68eef 100644
--- a/fs/ocfs2/dlm/dlmdebug.c
+++ b/fs/ocfs2/dlm/dlmdebug.c
@@ -260,10 +260,10 @@ void dlm_print_one_mle(struct dlm_master_list_entry *mle)
{
char *buf;
- buf = (char *) get_zeroed_page(GFP_ATOMIC);
+ buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
if (buf) {
dump_mle(mle, buf, PAGE_SIZE - 1);
- free_page((unsigned long)buf);
+ kfree(buf);
}
}
@@ -280,7 +280,7 @@ static struct dentry *dlm_debugfs_root;
/* begin - utils funcs */
static int debug_release(struct inode *inode, struct file *file)
{
- free_page((unsigned long)file->private_data);
+ kfree(file->private_data);
return 0;
}
@@ -327,17 +327,15 @@ static int debug_purgelist_open(struct inode *inode, struct file *file)
struct dlm_ctxt *dlm = inode->i_private;
char *buf = NULL;
- buf = (char *) get_zeroed_page(GFP_NOFS);
+ buf = kzalloc(PAGE_SIZE, GFP_NOFS);
if (!buf)
- goto bail;
+ return -ENOMEM;
i_size_write(inode, debug_purgelist_print(dlm, buf, PAGE_SIZE - 1));
file->private_data = buf;
return 0;
-bail:
- return -ENOMEM;
}
static const struct file_operations debug_purgelist_fops = {
@@ -384,17 +382,15 @@ static int debug_mle_open(struct inode *inode, struct file *file)
struct dlm_ctxt *dlm = inode->i_private;
char *buf = NULL;
- buf = (char *) get_zeroed_page(GFP_NOFS);
+ buf = kzalloc(PAGE_SIZE, GFP_NOFS);
if (!buf)
- goto bail;
+ return -ENOMEM;
i_size_write(inode, debug_mle_print(dlm, buf, PAGE_SIZE - 1));
file->private_data = buf;
return 0;
-bail:
- return -ENOMEM;
}
static const struct file_operations debug_mle_fops = {
@@ -775,17 +771,15 @@ static int debug_state_open(struct inode *inode, struct file *file)
struct dlm_ctxt *dlm = inode->i_private;
char *buf = NULL;
- buf = (char *) get_zeroed_page(GFP_NOFS);
+ buf = kzalloc(PAGE_SIZE, GFP_NOFS);
if (!buf)
- goto bail;
+ return -ENOMEM;
i_size_write(inode, debug_state_print(dlm, buf, PAGE_SIZE - 1));
file->private_data = buf;
return 0;
-bail:
- return -ENOMEM;
}
static const struct file_operations debug_state_fops = {
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index dc9da9133c8e..97bb9400e24b 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -63,7 +63,7 @@ static inline void byte_copymap(u8 dmap[], unsigned long smap[],
static void dlm_free_pagevec(void **vec, int pages)
{
while (pages--)
- free_page((unsigned long)vec[pages]);
+ kfree(vec[pages]);
kfree(vec);
}
@@ -75,9 +75,11 @@ static void **dlm_alloc_pagevec(int pages)
if (!vec)
return NULL;
- for (i = 0; i < pages; i++)
- if (!(vec[i] = (void *)__get_free_page(GFP_KERNEL)))
+ for (i = 0; i < pages; i++) {
+ vec[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!vec[i])
goto out_free;
+ }
mlog(0, "Allocated DLM hash pagevec; %d pages (%lu expected), %lu buckets per page\n",
pages, (unsigned long)DLM_HASH_PAGES,
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index 93eff38fdadd..aee3b4c56dcc 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -2548,7 +2548,7 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
/* preallocate up front. if this fails, abort */
ret = -ENOMEM;
- mres = (struct dlm_migratable_lockres *) __get_free_page(GFP_NOFS);
+ mres = kmalloc(PAGE_SIZE, GFP_NOFS);
if (!mres) {
mlog_errno(ret);
goto leave;
@@ -2725,8 +2725,7 @@ leave:
if (wake)
wake_up(&res->wq);
- if (mres)
- free_page((unsigned long)mres);
+ kfree(mres);
dlm_put(dlm);
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 128872bd945d..9b97bf73df22 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -837,7 +837,7 @@ int dlm_request_all_locks_handler(struct o2net_msg *msg, u32 len, void *data,
}
/* this will get freed by dlm_request_all_locks_worker */
- buf = (char *) __get_free_page(GFP_NOFS);
+ buf = kmalloc(PAGE_SIZE, GFP_NOFS);
if (!buf) {
kfree(item);
dlm_put(dlm);
@@ -933,7 +933,7 @@ static void dlm_request_all_locks_worker(struct dlm_work_item *item, void *data)
}
}
leave:
- free_page((unsigned long)data);
+ kfree(data);
}
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
index 834cae1e6223..1d915ef72119 100644
--- a/fs/omfs/inode.c
+++ b/fs/omfs/inode.c
@@ -478,7 +478,8 @@ static int omfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_time_min = 0;
sb->s_time_max = U64_MAX / MSEC_PER_SEC;
- sb_set_blocksize(sb, 0x200);
+ if (!sb_set_blocksize(sb, 0x200))
+ goto end;
bh = sb_bread(sb, 0);
if (!bh)
@@ -530,7 +531,8 @@ static int omfs_fill_super(struct super_block *sb, struct fs_context *fc)
* Use sys_blocksize as the fs block since it is smaller than a
* page while the fs blocksize can be larger.
*/
- sb_set_blocksize(sb, sbi->s_sys_blocksize);
+ if (!sb_set_blocksize(sb, sbi->s_sys_blocksize))
+ goto out_brelse_bh;
/*
* ...and the difference goes into a shift. sys_blocksize is always
diff --git a/fs/pidfs.c b/fs/pidfs.c
index ae307b713dbd..fdd75c3eaff8 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -1089,8 +1089,6 @@ static int pidfs_init_fs_context(struct fs_context *fc)
if (!ctx)
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC;
- fc->s_iflags |= SB_I_NODEV;
ctx->s_d_flags |= DCACHE_DONTCACHE;
ctx->ops = &pidfs_sops;
ctx->eops = &pidfs_export_operations;
diff --git a/fs/pipe.c b/fs/pipe.c
index 9841648c9cf3..429b0714ec57 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -111,16 +111,76 @@ void pipe_double_lock(struct pipe_inode_info *pipe1,
pipe_lock(pipe2);
}
-static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe)
+#define PIPE_PREALLOC_MAX 8
+
+struct anon_pipe_prealloc {
+ struct page *pages[PIPE_PREALLOC_MAX];
+ unsigned int count;
+};
+
+/*
+ * Pre-allocate pages outside pipe->mutex for multi-page writes.
+ * alloc_page() with GFP_HIGHUSER can sleep in reclaim and runs memcg
+ * charging; doing it under the mutex stalls a concurrent reader.
+ *
+ * Loop alloc_page() instead of alloc_pages_bulk_*(): the bulk path refuses
+ * __GFP_ACCOUNT under memcg (see commit 8dcb3060d81d "memcg: page_alloc:
+ * skip bulk allocator for __GFP_ACCOUNT") and silently degrades to a single
+ * page. A per-page loop keeps memcg accounting and the task NUMA mempolicy
+ * honoured for every page; the per-call overhead is small compared to the
+ * pipe->mutex hold-time being shrunk. Any shortfall is covered by the
+ * in-lock alloc_page() fallback in anon_pipe_get_page().
+ */
+static void anon_pipe_get_page_prealloc(struct anon_pipe_prealloc *prealloc,
+ size_t total_len)
+{
+ unsigned int want, i;
+ struct page *page;
+
+ prealloc->count = 0;
+ if (total_len <= PAGE_SIZE)
+ return;
+
+ want = min_t(unsigned int, DIV_ROUND_UP(total_len, PAGE_SIZE),
+ PIPE_PREALLOC_MAX);
+
+ for (i = 0; i < want; i++) {
+ page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
+ if (!page)
+ break;
+ prealloc->pages[prealloc->count++] = page;
+ }
+}
+
+static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc)
+{
+ if (!prealloc->count)
+ return NULL;
+
+ prealloc->count--;
+
+ return prealloc->pages[prealloc->count];
+}
+
+static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe,
+ struct anon_pipe_prealloc *prealloc)
{
+ struct page *page;
+
+ /* Drain prealloc first to keep tmp_page[] hot for later small writes. */
+ page = anon_pipe_prealloc_pop(prealloc);
+ if (page)
+ return page;
+
for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) {
if (pipe->tmp_page[i]) {
- struct page *page = pipe->tmp_page[i];
+ page = pipe->tmp_page[i];
pipe->tmp_page[i] = NULL;
return page;
}
}
+ /* FWIW: This is called with pipe->mutex held */
return alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
}
@@ -139,6 +199,38 @@ static void anon_pipe_put_page(struct pipe_inode_info *pipe,
put_page(page);
}
+/*
+ * Stash leftover prealloc pages in tmp_page[] so the next write to this
+ * pipe gets a hot page without entering the allocator.
+ */
+static void anon_pipe_refill_tmp_pages(struct pipe_inode_info *pipe,
+ struct anon_pipe_prealloc *prealloc)
+{
+ int i, idx;
+
+ if (!prealloc->count)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) {
+ if (pipe->tmp_page[i])
+ continue;
+ if (!prealloc->count)
+ return;
+ idx = --prealloc->count;
+ pipe->tmp_page[i] = prealloc->pages[idx];
+ prealloc->pages[idx] = NULL;
+ }
+}
+
+/* Runs after mutex_unlock() to keep put_page() out of the critical section. */
+static void anon_pipe_free_pages(struct anon_pipe_prealloc *prealloc)
+{
+ while (prealloc->count) {
+ prealloc->count--;
+ put_page(prealloc->pages[prealloc->count]);
+ }
+}
+
static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
@@ -432,6 +524,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
struct file *filp = iocb->ki_filp;
struct pipe_inode_info *pipe = filp->private_data;
+ struct anon_pipe_prealloc prealloc;
unsigned int head;
ssize_t ret = 0;
size_t total_len = iov_iter_count(from);
@@ -455,6 +548,8 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
if (unlikely(total_len == 0))
return 0;
+ anon_pipe_get_page_prealloc(&prealloc, total_len);
+
mutex_lock(&pipe->mutex);
if (!pipe->readers) {
@@ -512,7 +607,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
struct page *page;
int copied;
- page = anon_pipe_get_page(pipe);
+ page = anon_pipe_get_page(pipe, &prealloc);
if (unlikely(!page)) {
if (!ret)
ret = -ENOMEM;
@@ -576,9 +671,11 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
wake_next_writer = true;
}
out:
+ anon_pipe_refill_tmp_pages(pipe, &prealloc);
if (pipe_is_full(pipe))
wake_next_writer = false;
mutex_unlock(&pipe->mutex);
+ anon_pipe_free_pages(&prealloc);
/*
* If we do do a wakeup event, we do a 'sync' wakeup, because we
@@ -664,7 +761,8 @@ pipe_poll(struct file *filp, poll_table *wait)
union pipe_index idx;
/* Epoll has some historical nasty semantics, this enables them */
- WRITE_ONCE(pipe->poll_usage, true);
+ if (unlikely(!READ_ONCE(pipe->poll_usage)))
+ WRITE_ONCE(pipe->poll_usage, true);
/*
* Reading pipe state only -- no need for acquiring the semaphore.
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 65f56136ec3f..126b98419adb 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -262,7 +262,7 @@ static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf,
if (pos >= PAGE_SIZE)
return 0;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -285,7 +285,7 @@ static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf,
ret = len;
}
}
- free_page((unsigned long)page);
+ kfree(page);
return ret;
}
@@ -348,7 +348,7 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
if (count > arg_end - pos)
count = arg_end - pos;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -372,7 +372,7 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
count -= got;
}
- free_page((unsigned long)page);
+ kfree(page);
return len;
}
@@ -909,7 +909,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
if (!mm)
return 0;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -950,7 +950,7 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
mmput(mm);
free:
- free_page((unsigned long) page);
+ kfree(page);
return copied;
}
@@ -1017,7 +1017,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
if (!mm || !mm->env_end)
return 0;
- page = (char *)__get_free_page(GFP_KERNEL);
+ page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1063,7 +1063,7 @@ static ssize_t environ_read(struct file *file, char __user *buf,
mmput(mm);
free:
- free_page((unsigned long) page);
+ kfree(page);
return ret;
}
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 4deb0eeadbde..42fcd500fad2 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -202,7 +202,8 @@ static int qnx4_fill_super(struct super_block *s, struct fs_context *fc)
return -ENOMEM;
s->s_fs_info = qs;
- sb_set_blocksize(s, QNX4_BLOCK_SIZE);
+ if (!sb_set_blocksize(s, QNX4_BLOCK_SIZE))
+ return -EINVAL;
s->s_op = &qnx4_sops;
s->s_magic = QNX4_SUPER_MAGIC;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 64cf42721496..9850de3955d3 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -3022,7 +3022,7 @@ static const struct ctl_table fs_dqstats_table[] = {
static int __init dquot_init(void)
{
int i, ret;
- unsigned long nr_hash, order;
+ unsigned long nr_hash;
struct shrinker *dqcache_shrinker;
printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__);
@@ -3035,8 +3035,7 @@ static int __init dquot_init(void)
SLAB_PANIC),
NULL);
- order = 0;
- dquot_hash = (struct hlist_head *)__get_free_pages(GFP_KERNEL, order);
+ dquot_hash = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!dquot_hash)
panic("Cannot create dquot hash table");
@@ -3046,7 +3045,7 @@ static int __init dquot_init(void)
panic("Cannot create dquot stat counters");
/* Find power-of-two hlist_heads which can fit into allocation */
- nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct hlist_head);
+ nr_hash = PAGE_SIZE / sizeof(struct hlist_head);
dq_hash_bits = ilog2(nr_hash);
nr_hash = 1UL << dq_hash_bits;
@@ -3054,8 +3053,8 @@ static int __init dquot_init(void)
for (i = 0; i < nr_hash; i++)
INIT_HLIST_HEAD(dquot_hash + i);
- pr_info("VFS: Dquot-cache hash table entries: %ld (order %ld,"
- " %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order));
+ pr_info("VFS: Dquot-cache hash table entries: %ld (%ld bytes)\n",
+ nr_hash, PAGE_SIZE);
dqcache_shrinker = shrinker_alloc(0, "dquota-cache");
if (!dqcache_shrinker)
diff --git a/fs/read_write.c b/fs/read_write.c
index 50bff7edc91f..e8c14e2760b2 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -641,13 +641,12 @@ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t
return __kernel_write_iter(file, &iter, pos);
}
/*
- * This "EXPORT_SYMBOL_GPL()" is more of a "EXPORT_SYMBOL_DONTUSE()",
- * but autofs is one of the few internal kernel users that actually
+ * autofs is one of the few internal kernel users that actually
* wants this _and_ can be built as a module. So we need to export
* this symbol for autofs, even though it really isn't appropriate
* for any other kernel modules.
*/
-EXPORT_SYMBOL_GPL(__kernel_write);
+EXPORT_SYMBOL_FOR_MODULES(__kernel_write, "autofs4");
ssize_t kernel_write(struct file *file, const void *buf, size_t count,
loff_t *pos)
diff --git a/fs/select.c b/fs/select.c
index bf71c9838dfe..95d76531015a 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -150,7 +150,7 @@ void poll_freewait(struct poll_wqueues *pwq)
} while (entry > p->entries);
old = p;
p = p->next;
- free_page((unsigned long) old);
+ kfree(old);
}
}
EXPORT_SYMBOL(poll_freewait);
@@ -165,7 +165,7 @@ static struct poll_table_entry *poll_get_entry(struct poll_wqueues *p)
if (!table || POLL_TABLE_FULL(table)) {
struct poll_table_page *new_table;
- new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);
+ new_table = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!new_table) {
p->error = -ENOMEM;
return NULL;
diff --git a/fs/super.c b/fs/super.c
index 5fe8cea9f8fe..a117e1f0dfe3 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -741,12 +741,13 @@ struct super_block *sget_fc(struct fs_context *fc,
int err;
/*
- * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT is
- * not set, as the filesystem is likely unprepared to handle it.
- * This can happen when fsconfig() is called from init_user_ns with
- * an fs_fd opened in another user namespace.
+ * Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or
+ * FS_USERNS_DELEGATABLE is not set, as the filesystem is likely
+ * unprepared to handle it. This can happen when fsconfig() is called
+ * from init_user_ns with an fs_fd opened in another user namespace.
*/
- if (user_ns != &init_user_ns && !(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) {
+ if (user_ns != &init_user_ns &&
+ !(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) {
errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed");
return ERR_PTR(-EPERM);
}
@@ -821,7 +822,6 @@ void drop_super_exclusive(struct super_block *sb)
super_unlock_excl(sb);
put_super(sb);
}
-EXPORT_SYMBOL(drop_super_exclusive);
enum super_iter_flags_t {
SUPER_ITER_EXCL = (1U << 0),
diff --git a/fs/sync.c b/fs/sync.c
index 942a60cfedfb..4a84dd837b86 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -266,8 +266,7 @@ int sync_file_range(struct file *file, loff_t offset, loff_t nbytes,
i_mode = file_inode(file)->i_mode;
ret = -ESPIPE;
- if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode) &&
- !S_ISLNK(i_mode))
+ if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode))
goto out;
mapping = file->f_mapping;
diff --git a/include/linux/filelock.h b/include/linux/filelock.h
index 7a7a6e8a9a08..ec11cc6b4c58 100644
--- a/include/linux/filelock.h
+++ b/include/linux/filelock.h
@@ -584,7 +584,7 @@ static inline bool is_delegated(struct delegated_inode *di)
return false;
}
-static inline int break_lease(struct inode *inode, bool wait)
+static inline int break_lease(struct inode *inode, unsigned int mode)
{
return 0;
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2a6d2cb674db..9674c3d1cb3f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2294,6 +2294,7 @@ struct file_system_type {
#define FS_MGTIME 64 /* FS uses multigrain timestamps */
#define FS_LBS 128 /* FS supports LBS */
#define FS_POWER_FREEZE 256 /* Always freeze on suspend/hibernate */
+#define FS_USERNS_DELEGATABLE 1024 /* Can be mounted inside userns from outside */
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
int (*init_fs_context)(struct fs_context *);
const struct fs_parameter_spec *parameters;
diff --git a/include/linux/kstrtox.h b/include/linux/kstrtox.h
index 6ea897222af1..6c9282866770 100644
--- a/include/linux/kstrtox.h
+++ b/include/linux/kstrtox.h
@@ -142,10 +142,9 @@ static inline int __must_check kstrtos32_from_user(const char __user *s, size_t
* Keep in mind above caveat.
*/
-extern unsigned long simple_strtoul(const char *,char **,unsigned int);
-extern unsigned long simple_strntoul(const char *,char **,unsigned int,size_t);
-extern long simple_strtol(const char *,char **,unsigned int);
-extern unsigned long long simple_strtoull(const char *,char **,unsigned int);
-extern long long simple_strtoll(const char *,char **,unsigned int);
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);
+long simple_strtol(const char *cp, char **endp, unsigned int base);
+unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
+long long simple_strtoll(const char *cp, char **endp, unsigned int base);
#endif /* _LINUX_KSTRTOX_H */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 3941b9f1dec7..ebe6e29f7e93 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -56,7 +56,6 @@ extern struct dentry *start_creating_path(int, const char *, struct path *, unsi
extern struct dentry *start_creating_user_path(int, const char __user *, struct path *, unsigned int);
extern void end_creating_path(const struct path *, struct dentry *);
extern struct dentry *start_removing_path(const char *, struct path *);
-extern struct dentry *start_removing_user_path_at(int , const char __user *, struct path *);
static inline void end_removing_path(const struct path *path , struct dentry *dentry)
{
end_creating_path(path, dentry);
diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index 3e6c8e9d67ae..9c2429c1a570 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -87,24 +87,10 @@ static inline int copy_safe_from_sockptr(void *dst, size_t ksize,
static inline int copy_struct_from_sockptr(void *dst, size_t ksize,
sockptr_t src, size_t usize)
{
- size_t size = min(ksize, usize);
- size_t rest = max(ksize, usize) - size;
-
if (!sockptr_is_kernel(src))
- return copy_struct_from_user(dst, ksize, src.user, size);
-
- if (usize < ksize) {
- memset(dst + size, 0, rest);
- } else if (usize > ksize) {
- char *p = src.kernel;
+ return copy_struct_from_user(dst, ksize, src.user, usize);
- while (rest--) {
- if (*p++)
- return -E2BIG;
- }
- }
- memcpy(dst, src.kernel, size);
- return 0;
+ return copy_struct_from_bounce_buffer(dst, ksize, src.kernel, usize);
}
static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
@@ -121,6 +107,16 @@ static inline int copy_to_sockptr(sockptr_t dst, const void *src, size_t size)
return copy_to_sockptr_offset(dst, 0, src, size);
}
+static inline int
+copy_struct_to_sockptr(sockptr_t dst, size_t usize, const void *src,
+ size_t ksize, bool *ignored_trailing)
+{
+ if (!sockptr_is_kernel(dst))
+ return copy_struct_to_user(dst.user, usize, src, ksize, ignored_trailing);
+
+ return copy_struct_to_bounce_buffer(dst.kernel, usize, src, ksize, ignored_trailing);
+}
+
static inline void *memdup_sockptr_noprof(sockptr_t src, size_t len)
{
void *p = kmalloc_track_caller_noprof(len, GFP_USER | __GFP_NOWARN);
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 56328601218c..e4a64976f1c5 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -510,7 +510,7 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
return -EFAULT;
}
if (ignored_trailing)
- *ignored_trailing = ksize < usize &&
+ *ignored_trailing = usize < ksize &&
memchr_inv(src + size, 0, rest) != NULL;
/* Copy the interoperable parts of the struct. */
if (copy_to_user(dst, src, size))
@@ -518,6 +518,69 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
return 0;
}
+static __always_inline void
+__copy_struct_generic_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize,
+ bool *ignored_trailing)
+{
+ size_t size = min(dstsize, srcsize);
+ size_t rest = max(dstsize, srcsize) - size;
+
+ /* Deal with trailing bytes. */
+ if (dstsize > srcsize)
+ memset(dst + size, 0, rest);
+ if (ignored_trailing)
+ *ignored_trailing = dstsize < srcsize &&
+ memchr_inv(src + size, 0, rest) != NULL;
+ /* Copy the interoperable parts of the struct. */
+ memcpy(dst, src, size);
+}
+
+/**
+ * This is like copy_struct_from_user(), but the
+ * src buffer was already copied into a kernel
+ * bounce buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_from_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize)
+{
+ bool ignored_trailing;
+
+ /* Double check if ksize is larger than a known object size. */
+ if (WARN_ON_ONCE(dstsize > __builtin_object_size(dst, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ &ignored_trailing);
+ if (unlikely(ignored_trailing))
+ return -E2BIG;
+
+ return 0;
+}
+
+/**
+ * This is like copy_struct_to_user(), but the
+ * dst buffer is a kernel bounce buffer instead
+ * of a direct userspace buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_to_bounce_buffer(void *dst, size_t dstsize,
+ const void *src,
+ size_t srcsize,
+ bool *ignored_trailing)
+{
+ /* Double check if srcsize is larger than a known object size. */
+ if (WARN_ON_ONCE(srcsize > __builtin_object_size(src, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ ignored_trailing);
+ return 0;
+}
+
bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size);
long copy_from_kernel_nofault(void *dst, const void *src, size_t size);
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index bfc68156b45a..883cfd7de2ef 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -15,51 +15,55 @@
* When introducing new O_* bits, please check its uniqueness in fcntl_init().
*/
-#define O_ACCMODE 00000003
-#define O_RDONLY 00000000
-#define O_WRONLY 00000001
-#define O_RDWR 00000002
+#define O_ACCMODE 3
+#define O_RDONLY 0
+#define O_WRONLY (1 << 0)
+#define O_RDWR (1 << 1)
+/* (1 << 2) must not be used -- it collides with flags on alpha, sparc */
+/* (1 << 3) must not be used -- it collides with flags on alpha, mips, parisc, sparc */
+/* (1 << 4) must not be used -- it collides with flags on mips */
+/* (1 << 5) is free */
#ifndef O_CREAT
-#define O_CREAT 00000100 /* not fcntl */
+#define O_CREAT (1 << 6) /* not fcntl */
#endif
#ifndef O_EXCL
-#define O_EXCL 00000200 /* not fcntl */
+#define O_EXCL (1 << 7) /* not fcntl */
#endif
#ifndef O_NOCTTY
-#define O_NOCTTY 00000400 /* not fcntl */
+#define O_NOCTTY (1 << 8) /* not fcntl */
#endif
#ifndef O_TRUNC
-#define O_TRUNC 00001000 /* not fcntl */
+#define O_TRUNC (1 << 9) /* not fcntl */
#endif
#ifndef O_APPEND
-#define O_APPEND 00002000
+#define O_APPEND (1 << 10)
#endif
#ifndef O_NONBLOCK
-#define O_NONBLOCK 00004000
+#define O_NONBLOCK (1 << 11)
#endif
#ifndef O_DSYNC
-#define O_DSYNC 00010000 /* used to be O_SYNC, see below */
+#define O_DSYNC (1 << 12) /* used to be O_SYNC, see below */
#endif
#ifndef FASYNC
-#define FASYNC 00020000 /* fcntl, for BSD compatibility */
+#define FASYNC (1 << 13) /* fcntl, for BSD compatibility */
#endif
#ifndef O_DIRECT
-#define O_DIRECT 00040000 /* direct disk access hint */
+#define O_DIRECT (1 << 14) /* direct disk access hint */
#endif
#ifndef O_LARGEFILE
-#define O_LARGEFILE 00100000
+#define O_LARGEFILE (1 << 15)
#endif
#ifndef O_DIRECTORY
-#define O_DIRECTORY 00200000 /* must be a directory */
+#define O_DIRECTORY (1 << 16) /* must be a directory */
#endif
#ifndef O_NOFOLLOW
-#define O_NOFOLLOW 00400000 /* don't follow links */
+#define O_NOFOLLOW (1 << 17) /* don't follow links */
#endif
#ifndef O_NOATIME
-#define O_NOATIME 01000000
+#define O_NOATIME (1 << 18)
#endif
#ifndef O_CLOEXEC
-#define O_CLOEXEC 02000000 /* set close_on_exec */
+#define O_CLOEXEC (1 << 19) /* set close_on_exec */
#endif
/*
@@ -76,16 +80,16 @@
* Note: __O_SYNC must never be used directly.
*/
#ifndef O_SYNC
-#define __O_SYNC 04000000
+#define __O_SYNC (1 << 20)
#define O_SYNC (__O_SYNC|O_DSYNC)
#endif
#ifndef O_PATH
-#define O_PATH 010000000
+#define O_PATH (1 << 21)
#endif
#ifndef __O_TMPFILE
-#define __O_TMPFILE 020000000
+#define __O_TMPFILE (1 << 22)
#endif
#ifndef O_EMPTYPATH
@@ -99,6 +103,10 @@
#define O_NDELAY O_NONBLOCK
#endif
+/* (1 << 23) must not be used -- it collides with flags on alpha, parisc, sparc */
+/* (1 << 24) must not be used -- it collides with flags on alpha, sparc */
+/* (1 << 25) must not be used -- it collides with flags on sparc */
+
#define F_DUPFD 0 /* dup */
#define F_GETFD 1 /* get close_on_exec */
#define F_SETFD 2 /* set/clear close_on_exec */
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 55ed3ac0b70f..95e0b3a0f711 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -143,16 +143,14 @@ static int __init do_mount_root(const char *name, const char *fs,
const int flags, const void *data)
{
struct super_block *s;
- struct page *p = NULL;
char *data_page = NULL;
int ret;
if (data) {
/* init_mount() requires a full page as fifth argument */
- p = alloc_page(GFP_KERNEL);
- if (!p)
+ data_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!data_page)
return -ENOMEM;
- data_page = page_address(p);
strscpy_pad(data_page, data, PAGE_SIZE);
}
@@ -170,19 +168,20 @@ static int __init do_mount_root(const char *name, const char *fs,
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
out:
- if (p)
- put_page(p);
+ kfree(data_page);
return ret;
}
void __init mount_root_generic(char *name, char *pretty_name, int flags)
{
- struct page *page = alloc_page(GFP_KERNEL);
- char *fs_names = page_address(page);
+ char *fs_names = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *p;
char b[BDEVNAME_SIZE];
int num_fs, i;
+ if (!fs_names)
+ panic("VFS: Unable to mount root fs: not enough memory");
+
scnprintf(b, BDEVNAME_SIZE, "unknown-block(%u,%u)",
MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
if (root_fs_names)
@@ -242,7 +241,7 @@ retry:
printk("\n");
panic("VFS: Unable to mount root fs on \"%s\" or %s", pretty_name, b);
out:
- put_page(page);
+ kfree(fs_names);
}
#ifdef CONFIG_ROOT_NFS
@@ -343,7 +342,7 @@ static int __init mount_nodev_root(char *root_device_name)
int err = -EINVAL;
int num_fs, i;
- fs_names = (void *)__get_free_page(GFP_KERNEL);
+ fs_names = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!fs_names)
return -EINVAL;
num_fs = split_fs_names(fs_names, PAGE_SIZE);
@@ -360,7 +359,7 @@ static int __init mount_nodev_root(char *root_device_name)
break;
}
- free_page((unsigned long)fs_names);
+ kfree(fs_names);
return err;
}
diff --git a/init/initramfs.c b/init/initramfs.c
index 58db15fb18fd..20a18fcda48e 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -1,25 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/init.h>
#include <linux/async.h>
-#include <linux/export.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
#include <linux/delay.h>
-#include <linux/string.h>
#include <linux/dirent.h>
-#include <linux/syscalls.h>
-#include <linux/utime.h>
+#include <linux/export.h>
+#include <linux/fcntl.h>
#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/hex.h>
+#include <linux/init.h>
+#include <linux/init_syscalls.h>
#include <linux/kstrtox.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/namei.h>
-#include <linux/init_syscalls.h>
-#include <linux/umh.h>
-#include <linux/security.h>
#include <linux/overflow.h>
+#include <linux/security.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/syscalls.h>
+#include <linux/types.h>
+#include <linux/umh.h>
+#include <linux/utime.h>
+
+#include <asm/byteorder.h>
#include "do_mounts.h"
#include "initramfs_internal.h"
@@ -190,26 +193,30 @@ static __initdata gid_t gid;
static __initdata unsigned rdev;
static __initdata u32 hdr_csum;
-static void __init parse_header(char *s)
+static int __init parse_header(char *s)
{
- unsigned long parsed[13];
- int i;
+ __be32 header[13];
+ int ret;
- for (i = 0, s += 6; i < 13; i++, s += 8)
- parsed[i] = simple_strntoul(s, NULL, 16, 8);
+ ret = hex2bin((u8 *)header, s + 6, sizeof(header));
+ if (ret) {
+ error("damaged header");
+ return ret;
+ }
- ino = parsed[0];
- mode = parsed[1];
- uid = parsed[2];
- gid = parsed[3];
- nlink = parsed[4];
- mtime = parsed[5]; /* breaks in y2106 */
- body_len = parsed[6];
- major = parsed[7];
- minor = parsed[8];
- rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
- name_len = parsed[11];
- hdr_csum = parsed[12];
+ ino = be32_to_cpu(header[0]);
+ mode = be32_to_cpu(header[1]);
+ uid = be32_to_cpu(header[2]);
+ gid = be32_to_cpu(header[3]);
+ nlink = be32_to_cpu(header[4]);
+ mtime = be32_to_cpu(header[5]); /* breaks in y2106 */
+ body_len = be32_to_cpu(header[6]);
+ major = be32_to_cpu(header[7]);
+ minor = be32_to_cpu(header[8]);
+ rdev = new_encode_dev(MKDEV(be32_to_cpu(header[9]), be32_to_cpu(header[10])));
+ name_len = be32_to_cpu(header[11]);
+ hdr_csum = be32_to_cpu(header[12]);
+ return 0;
}
/* Finite-state machine */
@@ -289,7 +296,8 @@ static int __init do_header(void)
error("no cpio magic");
return 1;
}
- parse_header(collected);
+ if (parse_header(collected))
+ return 1;
next_header = this_header + N_ALIGN(name_len) + body_len;
next_header = (next_header + 3) & ~3;
state = SkipIt;
diff --git a/init/initramfs_test.c b/init/initramfs_test.c
index 2ce38d9a8fd0..bc55306d226d 100644
--- a/init/initramfs_test.c
+++ b/init/initramfs_test.c
@@ -3,7 +3,9 @@
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/init.h>
#include <linux/init_syscalls.h>
+#include <linux/initrd.h>
#include <linux/stringify.h>
#include <linux/timekeeping.h>
#include "initramfs_internal.h"
@@ -27,7 +29,18 @@ struct initramfs_test_cpio {
char *data;
};
-static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, char *out)
+/* regular newc header format */
+#define CPIO_HDR_FMT "%s%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%s"
+/*
+ * Bogus newc header with "0x" prefixes on the uid, gid, and namesize values.
+ * parse_header()/simple_str[n]toul() accepted this, contrary to the initramfs
+ * specification. hex2bin() now fails.
+ */
+#define CPIO_HDR_OX_INJECT \
+ "%s%08x%08x0x%06x0X%06x%08x%08x%08x%08x%08x%08x%08x0x%06x%08x%s"
+
+static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz,
+ bool inject_ox, char *out)
{
int i;
size_t off = 0;
@@ -38,9 +51,8 @@ static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, char *out)
size_t thislen;
/* +1 to account for nulterm */
- thislen = sprintf(pos, "%s"
- "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x"
- "%s",
+ thislen = sprintf(pos,
+ inject_ox ? CPIO_HDR_OX_INJECT : CPIO_HDR_FMT,
c->magic, c->ino, c->mode, c->uid, c->gid, c->nlink,
c->mtime, c->filesize, c->devmajor, c->devminor,
c->rdevmajor, c->rdevminor, c->namesize, c->csum,
@@ -102,7 +114,7 @@ static void __init initramfs_test_extract(struct kunit *test)
/* +3 to cater for any 4-byte end-alignment */
cpio_srcbuf = kzalloc(ARRAY_SIZE(c) * (CPIO_HDRLEN + PATH_MAX + 3),
GFP_KERNEL);
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
ktime_get_real_ts64(&ts_before);
err = unpack_to_rootfs(cpio_srcbuf, len);
@@ -177,7 +189,7 @@ static void __init initramfs_test_fname_overrun(struct kunit *test)
/* limit overrun to avoid crashes / filp_open() ENAMETOOLONG */
cpio_srcbuf[CPIO_HDRLEN + strlen(c[0].fname) + 20] = '\0';
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
/* overwrite trailing fname terminator and padding */
suffix_off = len - 1;
while (cpio_srcbuf[suffix_off] == '\0') {
@@ -219,7 +231,7 @@ static void __init initramfs_test_data(struct kunit *test)
cpio_srcbuf = kmalloc(CPIO_HDRLEN + c[0].namesize + c[0].filesize + 6,
GFP_KERNEL);
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
@@ -274,7 +286,7 @@ static void __init initramfs_test_csum(struct kunit *test)
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
@@ -284,7 +296,7 @@ static void __init initramfs_test_csum(struct kunit *test)
/* mess up the csum and confirm that unpack fails */
c[0].csum--;
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NOT_NULL(test, err);
@@ -306,7 +318,7 @@ static void __init initramfs_test_hardlink(struct kunit *test)
{
char *err, *cpio_srcbuf;
size_t len;
- struct kstat st0, st1;
+ struct kstat st0 = {}, st1 = {};
struct initramfs_test_cpio c[] = { {
.magic = "070701",
.ino = 1,
@@ -330,7 +342,7 @@ static void __init initramfs_test_hardlink(struct kunit *test)
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
- len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
err = unpack_to_rootfs(cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
@@ -371,7 +383,7 @@ static void __init initramfs_test_many(struct kunit *test)
};
c.namesize = 1 + sprintf(thispath, "initramfs_test_many-%d", i);
- p += fill_cpio(&c, 1, p);
+ p += fill_cpio(&c, 1, false, p);
}
len = p - cpio_srcbuf;
@@ -425,7 +437,7 @@ static void __init initramfs_test_fname_pad(struct kunit *test)
} };
memcpy(tbufs->padded_fname, "padded_fname", sizeof("padded_fname"));
- len = fill_cpio(c, ARRAY_SIZE(c), tbufs->cpio_srcbuf);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_srcbuf);
err = unpack_to_rootfs(tbufs->cpio_srcbuf, len);
KUNIT_EXPECT_NULL(test, err);
@@ -451,7 +463,7 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
{
char *err;
size_t len;
- struct kstat st0, st1;
+ struct kstat st0 = {}, st1 = {};
char fdata[] = "this file data will not be unpacked";
struct test_fname_path_max {
char fname_oversize[PATH_MAX + 1];
@@ -481,7 +493,7 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
memcpy(tbufs->fname_oversize, "fname_oversize",
sizeof("fname_oversize") - 1);
memcpy(tbufs->fname_ok, "fname_ok", sizeof("fname_ok") - 1);
- len = fill_cpio(c, ARRAY_SIZE(c), tbufs->cpio_src);
+ len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_src);
/* unpack skips over fname_oversize instead of returning an error */
err = unpack_to_rootfs(tbufs->cpio_src, len);
@@ -494,6 +506,45 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
kfree(tbufs);
}
+static void __init initramfs_test_hdr_hex(struct kunit *test)
+{
+ char *err;
+ size_t len;
+ char fdata[] = "this file data will not be unpacked";
+ struct initramfs_test_bufs {
+ char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2];
+ } *tbufs = kzalloc(sizeof(struct initramfs_test_bufs), GFP_KERNEL);
+ struct initramfs_test_cpio c[] = { {
+ .magic = "070701",
+ .ino = 1,
+ .mode = S_IFREG | 0777,
+ .uid = 0x123456,
+ .gid = 0x123457,
+ .nlink = 1,
+ .namesize = sizeof("initramfs_test_hdr_hex_0"),
+ .fname = "initramfs_test_hdr_hex_0",
+ .filesize = sizeof(fdata),
+ .data = fdata,
+ }, {
+ .magic = "070701",
+ .ino = 2,
+ .mode = S_IFDIR | 0777,
+ .uid = 0x000056,
+ .gid = 0x000057,
+ .nlink = 1,
+ .namesize = sizeof("initramfs_test_hdr_hex_1"),
+ .fname = "initramfs_test_hdr_hex_1",
+ } };
+
+ /* inject_ox=true to add "0x" cpio field prefixes */
+ len = fill_cpio(c, ARRAY_SIZE(c), true, tbufs->cpio_src);
+
+ err = unpack_to_rootfs(tbufs->cpio_src, len);
+ KUNIT_EXPECT_NOT_NULL(test, err);
+
+ kfree(tbufs);
+}
+
/*
* The kunit_case/_suite struct cannot be marked as __initdata as this will be
* used in debugfs to retrieve results after test has run.
@@ -507,11 +558,25 @@ static struct kunit_case __refdata initramfs_test_cases[] = {
KUNIT_CASE(initramfs_test_many),
KUNIT_CASE(initramfs_test_fname_pad),
KUNIT_CASE(initramfs_test_fname_path_max),
+ KUNIT_CASE(initramfs_test_hdr_hex),
{},
};
-static struct kunit_suite initramfs_test_suite = {
+static int __init initramfs_test_init(struct kunit_suite *suite)
+{
+ /*
+ * unpack_to_rootfs() uses module-static state (victim, byte_count,
+ * state, ...). The boot-time async do_populate_rootfs() may still be
+ * running, so wait for it to finish before we call unpack_to_rootfs()
+ * from the test thread, otherwise the two writers race and crash.
+ */
+ wait_for_initramfs();
+ return 0;
+}
+
+static struct kunit_suite __refdata initramfs_test_suite = {
.name = "initramfs",
+ .suite_init = initramfs_test_init,
.test_cases = initramfs_test_cases,
};
kunit_test_init_section_suites(&initramfs_test_suite);
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 243662af1af7..273919b16161 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1224,13 +1224,13 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
{
*new = *old;
if (iov_iter_is_bvec(new))
- return new->bvec = kmemdup(new->bvec,
- new->nr_segs * sizeof(struct bio_vec),
+ return new->bvec = kmemdup_array(new->bvec,
+ new->nr_segs, sizeof(struct bio_vec),
flags);
else if (iov_iter_is_kvec(new) || iter_is_iovec(new))
/* iovec and kvec have identical layout */
- return new->__iov = kmemdup(new->__iov,
- new->nr_segs * sizeof(struct iovec),
+ return new->__iov = kmemdup_array(new->__iov,
+ new->nr_segs, sizeof(struct iovec),
flags);
return NULL;
}
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 9f359b31c8d1..a6169e9bcdc9 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -129,13 +129,6 @@ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
}
EXPORT_SYMBOL(simple_strtoul);
-unsigned long simple_strntoul(const char *cp, char **endp, unsigned int base,
- size_t max_chars)
-{
- return simple_strntoull(cp, endp, base, max_chars);
-}
-EXPORT_SYMBOL(simple_strntoul);
-
/**
* simple_strtol - convert a string to a signed long
* @cp: The start of the string
diff --git a/mm/secretmem.c b/mm/secretmem.c
index 5f57ac4720d3..4877c262cb1f 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -245,8 +245,6 @@ static int secretmem_init_fs_context(struct fs_context *fc)
if (!ctx)
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC;
- fc->s_iflags |= SB_I_NODEV;
return 0;
}
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 641a180fb35f..8d4db2241cc2 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -91,6 +91,7 @@ TARGETS += pcie_bwctrl
TARGETS += perf_events
TARGETS += pidfd
TARGETS += pid_namespace
+TARGETS += pipe
TARGETS += power_supply
TARGETS += powerpc
TARGETS += prctl
diff --git a/tools/testing/selftests/namespaces/listns_efault_test.c b/tools/testing/selftests/namespaces/listns_efault_test.c
index b570746e917c..26b452c98c66 100644
--- a/tools/testing/selftests/namespaces/listns_efault_test.c
+++ b/tools/testing/selftests/namespaces/listns_efault_test.c
@@ -38,7 +38,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
__u64 *ns_ids;
ssize_t ret;
long page_size;
- pid_t pid, iter_pid;
+ pid_t pid, iter_pid, ns_pids[5];
int pidfds[5];
int sv[5][2];
int iter_pidfd;
@@ -114,6 +114,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
+ ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@@ -164,7 +165,7 @@ TEST(listns_partial_fault_with_ns_cleanup)
/* Wait for all mount namespace children to exit and cleanup */
for (i = 0; i < 5; i++) {
- waitpid(-1, NULL, 0);
+ waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@@ -175,6 +176,12 @@ TEST(listns_partial_fault_with_ns_cleanup)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
+ /* If listns() is not supported the iterator exits cleanly via ENOSYS */
+ if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
+ munmap(map, page_size);
+ SKIP(return, "listns() not supported");
+ }
+
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);
@@ -250,7 +257,7 @@ TEST(listns_late_fault_with_ns_cleanup)
__u64 *ns_ids;
ssize_t ret;
long page_size;
- pid_t pid, iter_pid;
+ pid_t pid, iter_pid, ns_pids[10];
int pidfds[10];
int sv[10][2];
int iter_pidfd;
@@ -320,6 +327,7 @@ TEST(listns_late_fault_with_ns_cleanup)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
+ ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@@ -373,7 +381,7 @@ TEST(listns_late_fault_with_ns_cleanup)
/* Wait for all children and cleanup */
for (i = 0; i < 10; i++) {
- waitpid(-1, NULL, 0);
+ waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@@ -384,6 +392,12 @@ TEST(listns_late_fault_with_ns_cleanup)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
+ /* If listns() is not supported the iterator exits cleanly via ENOSYS */
+ if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
+ munmap(map, page_size);
+ SKIP(return, "listns() not supported");
+ }
+
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);
@@ -402,7 +416,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
__u64 *ns_ids;
ssize_t ret;
long page_size;
- pid_t pid, iter_pid;
+ pid_t pid, iter_pid, ns_pids[8];
int pidfds[8];
int sv[8][2];
int iter_pidfd;
@@ -462,6 +476,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
pid = create_child(&pidfds[i], CLONE_NEWNS);
ASSERT_NE(pid, -1);
+ ns_pids[i] = pid;
if (pid == 0) {
close(sv[i][0]); /* Close parent end */
@@ -508,7 +523,7 @@ TEST(listns_mnt_ns_cleanup_on_fault)
/* Wait for children and cleanup */
for (i = 0; i < 8; i++) {
- waitpid(-1, NULL, 0);
+ waitpid(ns_pids[i], NULL, 0);
close(sv[i][0]);
close(pidfds[i]);
}
@@ -519,6 +534,12 @@ TEST(listns_mnt_ns_cleanup_on_fault)
ASSERT_EQ(ret, iter_pid);
close(iter_pidfd);
+ /* If listns() is not supported the iterator exits cleanly via ENOSYS */
+ if (WIFEXITED(status) && WEXITSTATUS(status) == PIDFD_SKIP) {
+ munmap(map, page_size);
+ SKIP(return, "listns() not supported");
+ }
+
/* Should have been killed */
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGKILL);
diff --git a/tools/testing/selftests/namespaces/nsid_test.c b/tools/testing/selftests/namespaces/nsid_test.c
index b4a14c6693a5..46dc838cba82 100644
--- a/tools/testing/selftests/namespaces/nsid_test.c
+++ b/tools/testing/selftests/namespaces/nsid_test.c
@@ -25,14 +25,24 @@
/* Fixture for tests that create child processes */
FIXTURE(nsid) {
pid_t child_pid;
+ pid_t grandchild_pid;
};
FIXTURE_SETUP(nsid) {
self->child_pid = 0;
+ self->grandchild_pid = 0;
}
FIXTURE_TEARDOWN(nsid) {
- /* Clean up any child process that may still be running */
+ /*
+ * Kill grandchild first: timens_separate and pidns_separate fork a
+ * grandchild that calls pause(). It is reparented to init on child
+ * exit and keeps the test runner's tap pipe open, hanging the runner.
+ */
+ if (self->grandchild_pid > 0) {
+ kill(self->grandchild_pid, SIGKILL);
+ waitpid(self->grandchild_pid, NULL, 0);
+ }
if (self->child_pid > 0) {
kill(self->child_pid, SIGKILL);
waitpid(self->child_pid, NULL, 0);
@@ -676,6 +686,7 @@ TEST_F(nsid, timens_separate)
pid_t grandchild_pid;
ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
+ self->grandchild_pid = grandchild_pid;
close(pipefd[0]);
/* Open grandchild's time namespace */
@@ -797,6 +808,7 @@ TEST_F(nsid, pidns_separate)
pid_t grandchild_pid;
ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
+ self->grandchild_pid = grandchild_pid;
close(pipefd[0]);
/* Open grandchild's PID namespace */
diff --git a/tools/testing/selftests/pipe/.gitignore b/tools/testing/selftests/pipe/.gitignore
new file mode 100644
index 000000000000..20b549361a15
--- /dev/null
+++ b/tools/testing/selftests/pipe/.gitignore
@@ -0,0 +1 @@
+pipe_bench
diff --git a/tools/testing/selftests/pipe/Makefile b/tools/testing/selftests/pipe/Makefile
new file mode 100644
index 000000000000..1810c680117b
--- /dev/null
+++ b/tools/testing/selftests/pipe/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Meta Platforms, Inc. and affiliates
+# Copyright (c) 2026 Breno Leitao <leitao@debian.org>
+
+CFLAGS += -O2 -Wall -Wextra -pthread
+
+TEST_GEN_PROGS := pipe_bench
+
+include ../lib.mk
diff --git a/tools/testing/selftests/pipe/pipe_bench.c b/tools/testing/selftests/pipe/pipe_bench.c
new file mode 100644
index 000000000000..7e96429b8fb4
--- /dev/null
+++ b/tools/testing/selftests/pipe/pipe_bench.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pipe_bench - exercise concurrent pipe operation
+ *
+ * N writer threads hammer a single pipe with multi-page writes; M reader
+ * threads drain it. Each writer records its own write() latency histogram.
+ * Multi-page writes (msgsize >= PAGE_SIZE) force the loop in
+ * anon_pipe_write() to call alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT) under
+ * pipe->mutex, which is the critical section the patch shrinks.
+ *
+ * By default the benchmark sweeps writers in {1, 2, 5} x readers in
+ * {1, 5, 10} and prints one block per configuration so two runs (e.g.
+ * baseline vs patched) can be diffed directly. Pass -w and -r to run a
+ * single configuration instead. Pass --memory-pressure to spawn stress-ng
+ * alongside the sweep so the per-page alloc_page() path under pipe->mutex
+ * has to dip into reclaim.
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2026 Breno Leitao <leitao@debian.org>
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define HIST_BUCKETS 32
+
+static size_t g_msgsize = 16 * 4096;
+static int g_duration = 3;
+static int g_pipe_size = 1024 * 1024;
+static int g_memory_pressure;
+
+static atomic_int g_stop;
+static int g_pipe[2];
+
+struct wstats {
+ uint64_t writes;
+ uint64_t bytes;
+ uint64_t lat_sum_ns;
+ uint64_t lat_max_ns;
+ uint64_t lat_hist[HIST_BUCKETS];
+ char *buf;
+};
+
+struct rstats {
+ char *buf;
+};
+
+struct hist_totals {
+ uint64_t writes;
+ uint64_t bytes;
+ uint64_t lat_sum;
+ uint64_t lat_max;
+};
+
+static inline uint64_t now_ns(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
+}
+
+static inline int log2_bucket(uint64_t v)
+{
+ int b = 0;
+
+ if (!v)
+ return 0;
+ while (v >>= 1)
+ b++;
+ return b < HIST_BUCKETS ? b : HIST_BUCKETS - 1;
+}
+
+static void *writer(void *arg)
+{
+ struct wstats *s = arg;
+
+ while (!atomic_load_explicit(&g_stop, memory_order_relaxed)) {
+ uint64_t t0 = now_ns();
+ ssize_t n = write(g_pipe[1], s->buf, g_msgsize);
+ uint64_t dt = now_ns() - t0;
+
+ if (n > 0) {
+ s->writes++;
+ s->bytes += (uint64_t)n;
+ s->lat_sum_ns += dt;
+ if (dt > s->lat_max_ns)
+ s->lat_max_ns = dt;
+ s->lat_hist[log2_bucket(dt)]++;
+ } else if (n < 0 && (errno == EPIPE || errno == EBADF)) {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static void *reader(void *arg)
+{
+ struct rstats *s = arg;
+
+ /*
+ * Drain until EOF (write end closed by main). g_stop is not checked
+ * here on purpose: writers may be blocked in write() with the pipe
+ * full when g_stop is set, so the reader must keep draining until
+ * main closes the write end.
+ */
+ for (;;) {
+ ssize_t n = read(g_pipe[0], s->buf, g_msgsize);
+
+ if (n <= 0)
+ break;
+ }
+ return NULL;
+}
+
+/* Sum per-writer stats and per-bucket counts into the caller's aggregates. */
+static void aggregate_wstats(struct wstats *all, int nw,
+ uint64_t agg[HIST_BUCKETS],
+ struct hist_totals *t)
+{
+ memset(t, 0, sizeof(*t));
+ for (int i = 0; i < nw; i++) {
+ t->writes += all[i].writes;
+ t->bytes += all[i].bytes;
+ t->lat_sum += all[i].lat_sum_ns;
+ if (all[i].lat_max_ns > t->lat_max)
+ t->lat_max = all[i].lat_max_ns;
+ for (int b = 0; b < HIST_BUCKETS; b++)
+ agg[b] += all[i].lat_hist[b];
+ }
+}
+
+/*
+ * Walk @agg in order, returning the inclusive upper bound (in ns) of the
+ * log2 bucket where the running sum first reaches @target.
+ *
+ * A percentile is undefined with zero samples, and with very low sample
+ * counts integer truncation could make @target zero -- then "cum >= 0"
+ * would latch on the first (possibly empty) bucket. Callers must pass
+ * @target >= 1.
+ */
+static uint64_t bucket_at(const uint64_t agg[HIST_BUCKETS], uint64_t target)
+{
+ uint64_t cum = 0;
+
+ for (int b = 0; b < HIST_BUCKETS; b++) {
+ /* HIST_BUCKETS <= 63, so (b + 1) is always a safe shift. */
+ uint64_t upper = (1ULL << (b + 1)) - 1;
+
+ cum += agg[b];
+ if (cum >= target)
+ return upper;
+ }
+ return 0;
+}
+
+static void compute_p50_p99(const uint64_t agg[HIST_BUCKETS], uint64_t writes,
+ uint64_t *p50, uint64_t *p99)
+{
+ uint64_t p50_target, p99_target;
+
+ *p50 = *p99 = 0;
+ if (!writes)
+ return;
+
+ p50_target = writes * 50 / 100;
+ p99_target = writes * 99 / 100;
+ if (!p50_target)
+ p50_target = 1;
+ if (!p99_target)
+ p99_target = 1;
+
+ *p50 = bucket_at(agg, p50_target);
+ *p99 = bucket_at(agg, p99_target);
+}
+
+static void print_summary(int nw, int nr, const struct hist_totals *t,
+ uint64_t p50, uint64_t p99)
+{
+ double sec = g_duration;
+ uint64_t avg_ns = t->writes ? t->lat_sum / t->writes : 0;
+
+ printf("config: writers=%d readers=%d msgsize=%zu duration=%d pipe_size=%d memory_pressure=%s\n",
+ nw, nr, g_msgsize, g_duration, g_pipe_size,
+ g_memory_pressure ? "yes" : "no");
+ printf("writes: total=%llu rate=%.0f/s\n",
+ (unsigned long long)t->writes, (double)t->writes / sec);
+ printf("throughput_MBps: %.2f\n",
+ ((double)t->bytes / sec) / (1024.0 * 1024.0));
+ printf("lat_avg_ns: %llu\n", (unsigned long long)avg_ns);
+ printf("lat_p50_ns_upper: %llu\n", (unsigned long long)p50);
+ printf("lat_p99_ns_upper: %llu\n", (unsigned long long)p99);
+ printf("lat_max_ns: %llu\n", (unsigned long long)t->lat_max);
+}
+
+static void summarize(struct wstats *all, int nw, int nr)
+{
+ uint64_t agg[HIST_BUCKETS] = {0};
+ struct hist_totals t;
+ uint64_t p50, p99;
+
+ aggregate_wstats(all, nw, agg, &t);
+ compute_p50_p99(agg, t.writes, &p50, &p99);
+ print_summary(nw, nr, &t, p50, p99);
+}
+
+/*
+ * Child branch of fork(): restore SIGPIPE to default (parent ignores it),
+ * exec stress-ng, and on failure write the reason into @hs_wr before
+ * exiting. The parent observes EOF on hs_wr (closed via O_CLOEXEC) when
+ * exec succeeds.
+ */
+static void stress_ng_child(int hs_wr) __attribute__((noreturn));
+static void stress_ng_child(int hs_wr)
+{
+ char errbuf[256];
+
+ signal(SIGPIPE, SIG_DFL);
+ execlp("stress-ng", "stress-ng",
+ "--vm", "4", "--vm-bytes", "80%",
+ "--vm-method", "all",
+ (char *)NULL);
+ snprintf(errbuf, sizeof(errbuf),
+ "exec stress-ng failed: %s\n", strerror(errno));
+ (void)!write(hs_wr, errbuf, strlen(errbuf));
+ _exit(127);
+}
+
+/*
+ * Read from the O_CLOEXEC handshake pipe. Anything readable means the
+ * child wrote an error before exec; EOF (n == 0) means the write-end
+ * closed because exec succeeded. Returns 0 on exec success, -1 if the
+ * child failed and was reaped.
+ */
+static int stress_ng_wait_handshake(int hs_rd, pid_t pid)
+{
+ struct pollfd pfd = { .fd = hs_rd, .events = POLLIN };
+ char errbuf[256];
+ int status;
+ int ret;
+
+ ret = poll(&pfd, 1, 500);
+ if (ret <= 0)
+ return 0;
+
+ ssize_t n = read(hs_rd, errbuf, sizeof(errbuf) - 1);
+
+ if (n > 0) {
+ errbuf[n] = '\0';
+ fputs(errbuf, stderr);
+ waitpid(pid, &status, 0);
+ return -1;
+ }
+ return 0;
+}
+
+static pid_t spawn_stress_ng(void)
+{
+ int hs[2];
+ pid_t pid;
+
+ /*
+ * Handshake pipe: child writes one byte and _exit()s on exec
+ * failure. On exec success the O_CLOEXEC flag closes the write
+ * end, which the parent observes as EOF. This makes the "is
+ * stress-ng on $PATH?" check fail fast rather than silently.
+ */
+ if (pipe2(hs, O_CLOEXEC) < 0) {
+ perror("pipe2");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ close(hs[0]);
+ close(hs[1]);
+ return -1;
+ }
+ if (pid == 0) {
+ close(hs[0]);
+ stress_ng_child(hs[1]);
+ }
+
+ close(hs[1]);
+ if (stress_ng_wait_handshake(hs[0], pid) < 0) {
+ close(hs[0]);
+ return -1;
+ }
+ close(hs[0]);
+
+ /* Give stress-ng a moment to map its VM regions before measuring. */
+ sleep(1);
+ return pid;
+}
+
+static void kill_stress_ng(pid_t pid)
+{
+ int status;
+
+ if (pid <= 0)
+ return;
+ kill(pid, SIGTERM);
+ for (int i = 0; i < 20; i++) {
+ if (waitpid(pid, &status, WNOHANG) > 0)
+ return;
+ usleep(100 * 1000);
+ }
+ kill(pid, SIGKILL);
+ waitpid(pid, &status, 0);
+}
+
+/*
+ * Allocate per-thread page-aligned buffers in main so a failed
+ * aligned_alloc() aborts the run before any thread starts. Workers used
+ * to allocate their own buffer and return NULL on failure, which left
+ * peers blocked in write()/read() with nobody to unblock them.
+ */
+static int alloc_thread_bufs(struct wstats *ws, int nw,
+ struct rstats *rs, int nr)
+{
+ for (int i = 0; i < nw; i++) {
+ ws[i].buf = aligned_alloc(4096, g_msgsize);
+ if (!ws[i].buf) {
+ fprintf(stderr, "writer %d: aligned_alloc(%zu) failed\n",
+ i, g_msgsize);
+ return -1;
+ }
+ memset(ws[i].buf, 0xAA, g_msgsize);
+ }
+ for (int i = 0; i < nr; i++) {
+ rs[i].buf = aligned_alloc(4096, g_msgsize);
+ if (!rs[i].buf) {
+ fprintf(stderr, "reader %d: aligned_alloc(%zu) failed\n",
+ i, g_msgsize);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void free_thread_bufs(struct wstats *ws, int nw,
+ struct rstats *rs, int nr)
+{
+ if (ws)
+ for (int i = 0; i < nw; i++)
+ free(ws[i].buf);
+ if (rs)
+ for (int i = 0; i < nr; i++)
+ free(rs[i].buf);
+}
+
+static int start_readers(pthread_t *rt, struct rstats *rs, int nr,
+ int *created)
+{
+ for (int i = 0; i < nr; i++) {
+ int err = pthread_create(&rt[i], NULL, reader, &rs[i]);
+
+ if (err) {
+ fprintf(stderr, "pthread_create reader %d: %s\n",
+ i, strerror(err));
+ return -1;
+ }
+ (*created)++;
+ }
+ return 0;
+}
+
+static int start_writers(pthread_t *wt, struct wstats *ws, int nw,
+ int *created)
+{
+ for (int i = 0; i < nw; i++) {
+ int err = pthread_create(&wt[i], NULL, writer, &ws[i]);
+
+ if (err) {
+ fprintf(stderr, "pthread_create writer %d: %s\n",
+ i, strerror(err));
+ return -1;
+ }
+ (*created)++;
+ }
+ return 0;
+}
+
+static int open_bench_pipe(void)
+{
+ if (pipe(g_pipe) < 0) {
+ perror("pipe");
+ return -1;
+ }
+ if (fcntl(g_pipe[1], F_SETPIPE_SZ, g_pipe_size) < 0)
+ perror("F_SETPIPE_SZ (continuing)");
+ return 0;
+}
+
+/*
+ * Normal termination: g_stop tells writers to leave the loop after the
+ * current write() returns. Closing the shared write-end fd means once
+ * the in-flight writes drain, readers see EOF and exit. Writers are not
+ * unblocked by EPIPE here -- g_pipe[0] stays open so readers can keep
+ * draining.
+ *
+ * Error path: some threads may have been created and others skipped, so
+ * writers could be blocked in write() with no reader making progress.
+ * Close both ends -- closing the read end is what delivers EPIPE to a
+ * blocked writer.
+ */
+static void stop_and_join(pthread_t *wt, int nw_created,
+ pthread_t *rt, int nr_created, int rc)
+{
+ atomic_store(&g_stop, 1);
+ close(g_pipe[1]);
+ if (rc < 0)
+ close(g_pipe[0]);
+ for (int i = 0; i < nw_created; i++)
+ pthread_join(wt[i], NULL);
+ for (int i = 0; i < nr_created; i++)
+ pthread_join(rt[i], NULL);
+ if (rc == 0)
+ close(g_pipe[0]);
+}
+
+static int run_one(int nw, int nr)
+{
+ pthread_t *wt = NULL, *rt = NULL;
+ struct wstats *ws = NULL;
+ struct rstats *rs = NULL;
+ int nw_created = 0, nr_created = 0;
+ int rc = 0;
+
+ atomic_store(&g_stop, 0);
+
+ if (open_bench_pipe() < 0)
+ return -1;
+
+ wt = calloc((size_t)nw, sizeof(*wt));
+ rt = calloc((size_t)nr, sizeof(*rt));
+ ws = calloc((size_t)nw, sizeof(*ws));
+ rs = calloc((size_t)nr, sizeof(*rs));
+ if (!wt || !rt || !ws || !rs) {
+ fprintf(stderr, "alloc failed\n");
+ rc = -1;
+ goto teardown;
+ }
+
+ if (alloc_thread_bufs(ws, nw, rs, nr) < 0) {
+ rc = -1;
+ goto teardown;
+ }
+
+ if (start_readers(rt, rs, nr, &nr_created) < 0 ||
+ start_writers(wt, ws, nw, &nw_created) < 0) {
+ rc = -1;
+ goto teardown;
+ }
+
+ sleep((unsigned int)g_duration);
+
+teardown:
+ stop_and_join(wt, nw_created, rt, nr_created, rc);
+
+ if (rc == 0) {
+ summarize(ws, nw, nr);
+ fflush(stdout);
+ }
+
+ free_thread_bufs(ws, nw, rs, nr);
+ free(wt);
+ free(rt);
+ free(ws);
+ free(rs);
+ return rc;
+}
+
+static void usage(const char *prog)
+{
+ fprintf(stderr,
+ "usage: %s [-w writers] [-r readers] [-s msgsize] [-d secs] [-p pipe_size] [--memory-pressure]\n"
+ " default: sweep writers={1,2,5} x readers={1,5,10}\n"
+ " --memory-pressure: spawn stress-ng (--vm 4 --vm-bytes 80%% --vm-method all) for the run\n",
+ prog);
+}
+
+static int parse_args(int argc, char **argv,
+ int *writers_override, int *readers_override)
+{
+ static const struct option long_opts[] = {
+ {"memory-pressure", no_argument, NULL, 'M'},
+ {0, 0, 0, 0},
+ };
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "w:r:s:d:p:",
+ long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'w':
+ *writers_override = atoi(optarg);
+ break;
+ case 'r':
+ *readers_override = atoi(optarg);
+ break;
+ case 's':
+ g_msgsize = (size_t)atol(optarg);
+ break;
+ case 'd':
+ g_duration = atoi(optarg);
+ break;
+ case 'p':
+ g_pipe_size = atoi(optarg);
+ break;
+ case 'M':
+ g_memory_pressure = 1;
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * aligned_alloc(4096, size) requires size to be a multiple of the
+ * alignment (C11); glibc returns NULL otherwise, which would make
+ * writer/reader threads silently exit and the run report zero writes.
+ * Validate up front instead.
+ */
+static int validate_args(void)
+{
+ if (g_msgsize == 0 || g_msgsize % 4096 != 0) {
+ fprintf(stderr,
+ "msgsize must be a positive multiple of 4096 (got %zu)\n",
+ g_msgsize);
+ return -1;
+ }
+ if (g_duration <= 0) {
+ fprintf(stderr, "duration must be > 0 seconds (got %d)\n",
+ g_duration);
+ return -1;
+ }
+ if (g_pipe_size <= 0) {
+ fprintf(stderr, "pipe_size must be > 0 bytes (got %d)\n",
+ g_pipe_size);
+ return -1;
+ }
+ return 0;
+}
+
+static int run_sweep(void)
+{
+ static const int writers_sweep[] = {1, 2, 5};
+ static const int readers_sweep[] = {1, 5, 10};
+
+ for (size_t i = 0; i < ARRAY_SIZE(writers_sweep); i++) {
+ for (size_t j = 0; j < ARRAY_SIZE(readers_sweep); j++) {
+ printf("---\n");
+ if (run_one(writers_sweep[i], readers_sweep[j]) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int writers_override = 0, readers_override = 0;
+ pid_t stress_pid = -1;
+ int rc = 0;
+
+ if (parse_args(argc, argv, &writers_override, &readers_override) < 0)
+ return 1;
+ if (validate_args() < 0)
+ return 1;
+
+ signal(SIGPIPE, SIG_IGN);
+ setvbuf(stdout, NULL, _IOLBF, 0);
+ setvbuf(stderr, NULL, _IOLBF, 0);
+
+ fprintf(stderr, "pid=%d\n", getpid());
+ fflush(stderr);
+
+ if (g_memory_pressure) {
+ stress_pid = spawn_stress_ng();
+ if (stress_pid < 0) {
+ fprintf(stderr,
+ "memory_pressure requested but stress-ng could not be spawned\n");
+ return 1;
+ }
+ }
+
+ if (writers_override > 0 || readers_override > 0) {
+ int nw = writers_override > 0 ? writers_override : 1;
+ int nr = readers_override > 0 ? readers_override : 1;
+
+ rc = run_one(nw, nr) < 0 ? 1 : 0;
+ } else {
+ rc = run_sweep() < 0 ? 1 : 0;
+ }
+
+ kill_stress_ng(stress_pid);
+ return rc;
+}
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 69c9d6d546b2..80f201035d77 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -973,8 +973,6 @@ static int kvm_gmem_init_fs_context(struct fs_context *fc)
if (!init_pseudo(fc, GUEST_MEMFD_MAGIC))
return -ENOMEM;
- fc->s_iflags |= SB_I_NOEXEC;
- fc->s_iflags |= SB_I_NODEV;
ctx = fc->fs_private;
ctx->ops = &kvm_gmem_super_operations;