vfs-7.2-rc1.misc

Please consider pulling these changes from the signed vfs-7.2-rc1.misc tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaiwLKgAKCRCRxhvAZXjc
 ou/zAP9SOUE6n58i0BdhLYw0RA9Ge5tz42e4inSFi4tkgfCrDwEAlxbRHDcMyhWB
 dHmx8OW6b5riMrW+lGPMH58RMoTkdQ4=
 =3vuc
 -----END PGP SIGNATURE-----

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()
  ...
This commit is contained in:
Linus Torvalds 2026-06-15 03:59:45 +05:30
commit 7e0e7bd60d
82 changed files with 1463 additions and 389 deletions

View File

@ -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.

View File

@ -43,6 +43,7 @@ algorithms work.
caching/index
porting
adding-new-filesystems
Filesystem support layers
=========================

View File

@ -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
---

View File

@ -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_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 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_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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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. */

View File

@ -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)
{

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -818,7 +818,8 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc)
* 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;

View File

@ -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);
}

View File

@ -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);
};
/*

View File

@ -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 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
/* 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 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
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;
}

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -1174,7 +1174,10 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
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 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
/*
* 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;

View File

@ -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 = {

View File

@ -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,

View File

@ -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 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
if (wake)
wake_up(&res->wq);
if (mres)
free_page((unsigned long)mres);
kfree(mres);
dlm_put(dlm);

View File

@ -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);
}

View File

@ -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

View File

@ -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;

106
fs/pipe.c
View File

@ -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.

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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),

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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);

View File

@ -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);
return copy_struct_from_user(dst, ksize, src.user, usize);
if (usize < ksize) {
memset(dst + size, 0, rest);
} else if (usize > ksize) {
char *p = src.kernel;
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);

View File

@ -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);

View File

@ -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 */

View File

@ -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 @@ void __init mount_root_generic(char *name, char *pretty_name, int flags)
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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -91,6 +91,7 @@ TARGETS += pcie_bwctrl
TARGETS += perf_events
TARGETS += pidfd
TARGETS += pid_namespace
TARGETS += pipe
TARGETS += power_supply
TARGETS += powerpc
TARGETS += prctl

View File

@ -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);

View File

@ -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 */

View File

@ -0,0 +1 @@
pipe_bench

View File

@ -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

View File

@ -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;
}

View File

@ -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;