mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
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:
commit
7e0e7bd60d
195
Documentation/filesystems/adding-new-filesystems.rst
Normal file
195
Documentation/filesystems/adding-new-filesystems.rst
Normal 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.
|
||||
|
|
@ -43,6 +43,7 @@ algorithms work.
|
|||
caching/index
|
||||
|
||||
porting
|
||||
adding-new-filesystems
|
||||
|
||||
Filesystem support layers
|
||||
=========================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1
fs/aio.c
1
fs/aio.c
|
|
@ -318,7 +318,6 @@ static int aio_init_fs_context(struct fs_context *fc)
|
|||
pfc = init_pseudo(fc, AIO_RING_MAGIC);
|
||||
if (!pfc)
|
||||
return -ENOMEM;
|
||||
fc->s_iflags |= SB_I_NOEXEC;
|
||||
pfc->ops = &aio_super_operations;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
18
fs/dcache.c
18
fs/dcache.c
|
|
@ -426,9 +426,16 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry)
|
|||
this_cpu_inc(nr_dentry_negative);
|
||||
}
|
||||
|
||||
#define DENTRY_WARN_ONCE(condition, dentry) \
|
||||
WARN_ONCE((condition), "dentry=%p d_flags=0x%x\n", (dentry), (dentry)->d_flags)
|
||||
#define D_FLAG_VERIFY(dentry, x) \
|
||||
DENTRY_WARN_ONCE(((dentry)->d_flags & (DCACHE_LRU_LIST | DCACHE_SHRINK_LIST)) != (x), (dentry))
|
||||
|
||||
static void dentry_free(struct dentry *dentry)
|
||||
{
|
||||
WARN_ON(d_really_is_positive(dentry));
|
||||
DENTRY_WARN_ONCE(d_really_is_positive(dentry), dentry);
|
||||
DENTRY_WARN_ONCE(dentry->d_lockref.count >= 0, dentry);
|
||||
D_FLAG_VERIFY(dentry, 0);
|
||||
if (unlikely(dname_external(dentry))) {
|
||||
struct external_name *p = external_name(dentry);
|
||||
if (likely(atomic_dec_and_test(&p->count))) {
|
||||
|
|
@ -495,7 +502,6 @@ static void dentry_unlink_inode(struct dentry * dentry)
|
|||
* These helper functions make sure we always follow the
|
||||
* rules. d_lock must be held by the caller.
|
||||
*/
|
||||
#define D_FLAG_VERIFY(dentry,x) WARN_ON_ONCE(((dentry)->d_flags & (DCACHE_LRU_LIST | DCACHE_SHRINK_LIST)) != (x))
|
||||
static void d_lru_add(struct dentry *dentry)
|
||||
{
|
||||
D_FLAG_VERIFY(dentry, 0);
|
||||
|
|
@ -1820,10 +1826,10 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
|||
name = &slash_name;
|
||||
dname = dentry->d_shortname.string;
|
||||
} else if (name->len > DNAME_INLINE_LEN-1) {
|
||||
size_t size = offsetof(struct external_name, name[1]);
|
||||
struct external_name *p = kmalloc(size + name->len,
|
||||
GFP_KERNEL_ACCOUNT |
|
||||
__GFP_RECLAIMABLE);
|
||||
struct external_name *p;
|
||||
|
||||
p = kmalloc_flex(*p, name, name->len + 1,
|
||||
GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE);
|
||||
if (!p) {
|
||||
kmem_cache_free(dentry_cache, dentry);
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
18
fs/file.c
18
fs/file.c
|
|
@ -544,24 +544,23 @@ struct files_struct init_files = {
|
|||
static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
|
||||
{
|
||||
unsigned int maxfd = fdt->max_fds; /* always multiple of BITS_PER_LONG */
|
||||
unsigned int maxbit = maxfd / BITS_PER_LONG;
|
||||
unsigned int bitbit = start / BITS_PER_LONG;
|
||||
unsigned int max_fds_words = maxfd / BITS_PER_LONG;
|
||||
unsigned int fds_word_idx = start / BITS_PER_LONG;
|
||||
unsigned int bit;
|
||||
|
||||
/*
|
||||
* Try to avoid looking at the second level bitmap
|
||||
*/
|
||||
bit = find_next_zero_bit(&fdt->open_fds[bitbit], BITS_PER_LONG,
|
||||
bit = find_next_zero_bit(&fdt->open_fds[fds_word_idx], BITS_PER_LONG,
|
||||
start & (BITS_PER_LONG - 1));
|
||||
if (bit < BITS_PER_LONG)
|
||||
return bit + bitbit * BITS_PER_LONG;
|
||||
return bit + (fds_word_idx * BITS_PER_LONG);
|
||||
|
||||
bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;
|
||||
if (bitbit >= maxfd)
|
||||
bit = BITS_PER_LONG *
|
||||
find_next_zero_bit(fdt->full_fds_bits, max_fds_words, fds_word_idx + 1);
|
||||
if (bit >= maxfd)
|
||||
return maxfd;
|
||||
if (bitbit > start)
|
||||
start = bitbit;
|
||||
return find_next_zero_bit(fdt->open_fds, maxfd, start);
|
||||
return find_next_zero_bit(fdt->open_fds, maxfd, bit);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1134,7 +1133,6 @@ struct file *fget_task(struct task_struct *task, unsigned int fd)
|
|||
|
||||
struct file *fget_task_next(struct task_struct *task, unsigned int *ret_fd)
|
||||
{
|
||||
/* Must be called with rcu_read_lock held */
|
||||
struct files_struct *files;
|
||||
unsigned int fd = *ret_fd;
|
||||
struct file *file = NULL;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
25
fs/namei.c
25
fs/namei.c
|
|
@ -2960,16 +2960,17 @@ void end_dirop(struct dentry *de)
|
|||
EXPORT_SYMBOL(end_dirop);
|
||||
|
||||
/* does lookup, returns the object with parent locked */
|
||||
static struct dentry *__start_removing_path(int dfd, struct filename *name,
|
||||
struct path *path)
|
||||
struct dentry *start_removing_path(const char *name, struct path *path)
|
||||
{
|
||||
CLASS(filename_kernel, filename)(name);
|
||||
struct path parent_path __free(path_put) = {};
|
||||
struct dentry *d;
|
||||
struct qstr last;
|
||||
enum last_type type;
|
||||
int error;
|
||||
|
||||
error = filename_parentat(dfd, name, 0, &parent_path, &last, &type);
|
||||
error = filename_parentat(AT_FDCWD, filename, 0, &parent_path, &last,
|
||||
&type);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
if (unlikely(type != LAST_NORM))
|
||||
|
|
@ -3030,21 +3031,6 @@ struct dentry *kern_path_parent(const char *name, struct path *path)
|
|||
return d;
|
||||
}
|
||||
|
||||
struct dentry *start_removing_path(const char *name, struct path *path)
|
||||
{
|
||||
CLASS(filename_kernel, filename)(name);
|
||||
return __start_removing_path(AT_FDCWD, filename, path);
|
||||
}
|
||||
|
||||
struct dentry *start_removing_user_path_at(int dfd,
|
||||
const char __user *name,
|
||||
struct path *path)
|
||||
{
|
||||
CLASS(filename, filename)(name);
|
||||
return __start_removing_path(dfd, filename, path);
|
||||
}
|
||||
EXPORT_SYMBOL(start_removing_user_path_at);
|
||||
|
||||
int kern_path(const char *name, unsigned int flags, struct path *path)
|
||||
{
|
||||
CLASS(filename_kernel, filename)(name);
|
||||
|
|
@ -3631,7 +3617,6 @@ int path_pts(struct path *path)
|
|||
*/
|
||||
struct dentry *parent = dget_parent(path->dentry);
|
||||
struct dentry *child;
|
||||
struct qstr this = QSTR_INIT("pts", 3);
|
||||
|
||||
if (unlikely(!path_connected(path->mnt, parent))) {
|
||||
dput(parent);
|
||||
|
|
@ -3639,7 +3624,7 @@ int path_pts(struct path *path)
|
|||
}
|
||||
dput(path->dentry);
|
||||
path->dentry = parent;
|
||||
child = d_hash_and_lookup(parent, &this);
|
||||
child = d_hash_and_lookup(parent, &QSTR("pts"));
|
||||
if (IS_ERR_OR_NULL(child))
|
||||
return -ENOENT;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
106
fs/pipe.c
|
|
@ -111,16 +111,76 @@ void pipe_double_lock(struct pipe_inode_info *pipe1,
|
|||
pipe_lock(pipe2);
|
||||
}
|
||||
|
||||
static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe)
|
||||
#define PIPE_PREALLOC_MAX 8
|
||||
|
||||
struct anon_pipe_prealloc {
|
||||
struct page *pages[PIPE_PREALLOC_MAX];
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
/*
|
||||
* Pre-allocate pages outside pipe->mutex for multi-page writes.
|
||||
* alloc_page() with GFP_HIGHUSER can sleep in reclaim and runs memcg
|
||||
* charging; doing it under the mutex stalls a concurrent reader.
|
||||
*
|
||||
* Loop alloc_page() instead of alloc_pages_bulk_*(): the bulk path refuses
|
||||
* __GFP_ACCOUNT under memcg (see commit 8dcb3060d81d "memcg: page_alloc:
|
||||
* skip bulk allocator for __GFP_ACCOUNT") and silently degrades to a single
|
||||
* page. A per-page loop keeps memcg accounting and the task NUMA mempolicy
|
||||
* honoured for every page; the per-call overhead is small compared to the
|
||||
* pipe->mutex hold-time being shrunk. Any shortfall is covered by the
|
||||
* in-lock alloc_page() fallback in anon_pipe_get_page().
|
||||
*/
|
||||
static void anon_pipe_get_page_prealloc(struct anon_pipe_prealloc *prealloc,
|
||||
size_t total_len)
|
||||
{
|
||||
unsigned int want, i;
|
||||
struct page *page;
|
||||
|
||||
prealloc->count = 0;
|
||||
if (total_len <= PAGE_SIZE)
|
||||
return;
|
||||
|
||||
want = min_t(unsigned int, DIV_ROUND_UP(total_len, PAGE_SIZE),
|
||||
PIPE_PREALLOC_MAX);
|
||||
|
||||
for (i = 0; i < want; i++) {
|
||||
page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
|
||||
if (!page)
|
||||
break;
|
||||
prealloc->pages[prealloc->count++] = page;
|
||||
}
|
||||
}
|
||||
|
||||
static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc)
|
||||
{
|
||||
if (!prealloc->count)
|
||||
return NULL;
|
||||
|
||||
prealloc->count--;
|
||||
|
||||
return prealloc->pages[prealloc->count];
|
||||
}
|
||||
|
||||
static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe,
|
||||
struct anon_pipe_prealloc *prealloc)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
/* Drain prealloc first to keep tmp_page[] hot for later small writes. */
|
||||
page = anon_pipe_prealloc_pop(prealloc);
|
||||
if (page)
|
||||
return page;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) {
|
||||
if (pipe->tmp_page[i]) {
|
||||
struct page *page = pipe->tmp_page[i];
|
||||
page = pipe->tmp_page[i];
|
||||
pipe->tmp_page[i] = NULL;
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
/* FWIW: This is called with pipe->mutex held */
|
||||
return alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +199,38 @@ static void anon_pipe_put_page(struct pipe_inode_info *pipe,
|
|||
put_page(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stash leftover prealloc pages in tmp_page[] so the next write to this
|
||||
* pipe gets a hot page without entering the allocator.
|
||||
*/
|
||||
static void anon_pipe_refill_tmp_pages(struct pipe_inode_info *pipe,
|
||||
struct anon_pipe_prealloc *prealloc)
|
||||
{
|
||||
int i, idx;
|
||||
|
||||
if (!prealloc->count)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) {
|
||||
if (pipe->tmp_page[i])
|
||||
continue;
|
||||
if (!prealloc->count)
|
||||
return;
|
||||
idx = --prealloc->count;
|
||||
pipe->tmp_page[i] = prealloc->pages[idx];
|
||||
prealloc->pages[idx] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Runs after mutex_unlock() to keep put_page() out of the critical section. */
|
||||
static void anon_pipe_free_pages(struct anon_pipe_prealloc *prealloc)
|
||||
{
|
||||
while (prealloc->count) {
|
||||
prealloc->count--;
|
||||
put_page(prealloc->pages[prealloc->count]);
|
||||
}
|
||||
}
|
||||
|
||||
static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
|
|
@ -432,6 +524,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
{
|
||||
struct file *filp = iocb->ki_filp;
|
||||
struct pipe_inode_info *pipe = filp->private_data;
|
||||
struct anon_pipe_prealloc prealloc;
|
||||
unsigned int head;
|
||||
ssize_t ret = 0;
|
||||
size_t total_len = iov_iter_count(from);
|
||||
|
|
@ -455,6 +548,8 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (unlikely(total_len == 0))
|
||||
return 0;
|
||||
|
||||
anon_pipe_get_page_prealloc(&prealloc, total_len);
|
||||
|
||||
mutex_lock(&pipe->mutex);
|
||||
|
||||
if (!pipe->readers) {
|
||||
|
|
@ -512,7 +607,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
struct page *page;
|
||||
int copied;
|
||||
|
||||
page = anon_pipe_get_page(pipe);
|
||||
page = anon_pipe_get_page(pipe, &prealloc);
|
||||
if (unlikely(!page)) {
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
|
|
@ -576,9 +671,11 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
wake_next_writer = true;
|
||||
}
|
||||
out:
|
||||
anon_pipe_refill_tmp_pages(pipe, &prealloc);
|
||||
if (pipe_is_full(pipe))
|
||||
wake_next_writer = false;
|
||||
mutex_unlock(&pipe->mutex);
|
||||
anon_pipe_free_pages(&prealloc);
|
||||
|
||||
/*
|
||||
* If we do do a wakeup event, we do a 'sync' wakeup, because we
|
||||
|
|
@ -664,7 +761,8 @@ pipe_poll(struct file *filp, poll_table *wait)
|
|||
union pipe_index idx;
|
||||
|
||||
/* Epoll has some historical nasty semantics, this enables them */
|
||||
WRITE_ONCE(pipe->poll_usage, true);
|
||||
if (unlikely(!READ_ONCE(pipe->poll_usage)))
|
||||
WRITE_ONCE(pipe->poll_usage, true);
|
||||
|
||||
/*
|
||||
* Reading pipe state only -- no need for acquiring the semaphore.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
12
fs/super.c
12
fs/super.c
|
|
@ -741,12 +741,13 @@ struct super_block *sget_fc(struct fs_context *fc,
|
|||
int err;
|
||||
|
||||
/*
|
||||
* Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT is
|
||||
* not set, as the filesystem is likely unprepared to handle it.
|
||||
* This can happen when fsconfig() is called from init_user_ns with
|
||||
* an fs_fd opened in another user namespace.
|
||||
* Never allow s_user_ns != &init_user_ns when FS_USERNS_MOUNT or
|
||||
* FS_USERNS_DELEGATABLE is not set, as the filesystem is likely
|
||||
* unprepared to handle it. This can happen when fsconfig() is called
|
||||
* from init_user_ns with an fs_fd opened in another user namespace.
|
||||
*/
|
||||
if (user_ns != &init_user_ns && !(fc->fs_type->fs_flags & FS_USERNS_MOUNT)) {
|
||||
if (user_ns != &init_user_ns &&
|
||||
!(fc->fs_type->fs_flags & (FS_USERNS_MOUNT | FS_USERNS_DELEGATABLE))) {
|
||||
errorfc(fc, "VFS: Mounting from non-initial user namespace is not allowed");
|
||||
return ERR_PTR(-EPERM);
|
||||
}
|
||||
|
|
@ -821,7 +822,6 @@ void drop_super_exclusive(struct super_block *sb)
|
|||
super_unlock_excl(sb);
|
||||
put_super(sb);
|
||||
}
|
||||
EXPORT_SYMBOL(drop_super_exclusive);
|
||||
|
||||
enum super_iter_flags_t {
|
||||
SUPER_ITER_EXCL = (1U << 0),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ TARGETS += pcie_bwctrl
|
|||
TARGETS += perf_events
|
||||
TARGETS += pidfd
|
||||
TARGETS += pid_namespace
|
||||
TARGETS += pipe
|
||||
TARGETS += power_supply
|
||||
TARGETS += powerpc
|
||||
TARGETS += prctl
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
1
tools/testing/selftests/pipe/.gitignore
vendored
Normal file
1
tools/testing/selftests/pipe/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
pipe_bench
|
||||
9
tools/testing/selftests/pipe/Makefile
Normal file
9
tools/testing/selftests/pipe/Makefile
Normal 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
|
||||
616
tools/testing/selftests/pipe/pipe_bench.c
Normal file
616
tools/testing/selftests/pipe/pipe_bench.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user