diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-17 12:56:12 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-17 12:56:12 -0700 |
| commit | 1c3e8cef79ea5f1415cff0d3c507e2e07b71ade8 (patch) | |
| tree | 250813712d5f13fe5cb38a9923d8a70ee0043aab | |
| parent | ab5ed08f2d8396fb8e3942569bbbd5cd569a753e (diff) | |
| parent | dcacab904fe78d60840ba947a104993ee9ded887 (diff) | |
Merge tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull misc vfs updates from Christian Brauner:
"Bigger cleanups:
- The lockref dead-count handling is tidied up.
The open-coded check for a count below zero as the dead marker
relies on information the caller should not have.
- make put_mnt_ns() leave mounts connected. Destroying a mount
namespace disconnected its mounts from their mount points. So a
file descriptor still open on the parent of a mount point could be
used to peek under it.
Locked mounts were already kept connected to prevent exactly that.
But a mount is only locked when its tree is copied across a user
namespace boundary. So a mount namespace set up by a privileged
component had no locked mounts and its mounts were disconnected.
Passing UMOUNT_CONNECTED keeps every mount connected and prevents
that bug.
- vfs_prepare_mode() passes S_IFDIR for directories. I meant to fix
that ago but didn't get to it. So now someone finally did it.
This kills the exception where the mode could be 0 when a directory
was created whereas every other creation operation passed it
explicitly already.
- move long delayed work for ufs, jffs2, hfsplus, hfs and affs from
the per-cpu system_long_wq to the new unbound system_dfl_long_wq.
None of that work relies on per-cpu state and the work item is
enqueued with queue_delayed_work() whose timer is global anyway. So
it may as well benefit from scheduler task placement.
Smaller fixes and cleanups:
- unlock_buffer() and journal_end_buffer_io_sync() use
clear_and_wake_up_bit()
- the pipe page pools are unified into a single per-pipe pool and the
extra wake_up(rd_wait) is limited to EPOLLET consumers
- eventpoll now computes its timer slack lazily in ep_poll()
- shrink_dcache_for_umount() keeps making progress on busy roots
- excess xarray nodes are freed in clear_inode()
- romfs detects hard link cycles
- the user path of nested backing files is fixed
- pidfd holds exec_update_lock around the namespace ioctl
- non-memcg-aware nr_cached_objects is skipped during memcg slab
shrink
- iomap_write_iter() always returns status
- mangle_path() is renamed to seq_mangle_path()
- inode timestamp accessors are annotated
- new regression test for pipe->poll_usage.
- a few documentation, kernel-doc and selftest fixes"
* tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (67 commits)
selftests/namespaces: Fix racy pipe handshake in timens and pidns_separate
selftests/epoll: add a regression test for pipe->poll_usage
pipe: only enable the extra wake_up(rd_wait) for EPOLLET consumers
pidfd: hold exec_update_lock around namespace ioctl
fs: fix user path of nested backing files
fs: remove stale inode_insert5() kernel-doc parameter
fs: fix switch/case indentation in sysfs() syscall
fs: document semantics of kstat::{uid,gid} fields
dcache: keep shrink_dcache_for_umount() making progress on busy roots
seq_file: rename mangle_path to seq_mangle_path
nstree: add/fix struct ns_id_req kernel-doc member fields
dcache: use lockref routines for dead count checks
lockref: tidy up dead count handling
initramfs: fix typo in reserve_initrd_mem comment
fs/pipe: unify the page pools into a single per-pipe pool
fs: annotate inode timestamp accessors
eventpoll: compute timer slack lazily in ep_poll()
selftests/filesystems: add mntns cleanup test
put_mnt_ns(): leave mounts connected
affs: Move long delayed work on system_dfl_long_wq
...
79 files changed, 419 insertions, 266 deletions
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index 02522fbfd968..60880eb0c49d 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -1173,7 +1173,7 @@ these conditions don't require explicit checks: - if LOOKUP_CREATE is NOT given, then the dentry won't be negative, ERR_PTR(-ENOENT) is returned instead - if LOOKUP_EXCL IS given, then the dentry won't be positive, - ERR_PTR(-EEXIST) is rreturned instread + ERR_PTR(-EEXIST) is returned instead LOOKUP_EXCL now means "target must not exist". It can be combined with LOOK_CREATE or LOOKUP_RENAME_TARGET. diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index e47b90e70837..3829554ca369 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -688,7 +688,7 @@ static struct dentry *v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); v9ses = v9fs_inode2v9ses(dir); - perm = unixmode2p9mode(v9ses, mode | S_IFDIR); + perm = unixmode2p9mode(v9ses, mode); fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); if (IS_ERR(fid)) return ERR_CAST(fid); diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index d17c3b6eebb2..116b29e95f21 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -361,7 +361,6 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); v9ses = v9fs_inode2v9ses(dir); - omode |= S_IFDIR; if (dir->i_mode & S_ISGID) omode |= S_ISGID; diff --git a/fs/affs/namei.c b/fs/affs/namei.c index b0001084727a..5311828b19e8 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -287,7 +287,7 @@ affs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!inode) return ERR_PTR(-ENOSPC); - inode->i_mode = S_IFDIR | mode; + inode->i_mode = mode; affs_mode_to_prot(inode); inode->i_op = &affs_dir_inode_operations; diff --git a/fs/affs/super.c b/fs/affs/super.c index b232251aa7bb..4f331f784db2 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -88,7 +88,7 @@ void affs_mark_sb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sb_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sb_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 6401fbdd10aa..81565366d937 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -1333,7 +1333,7 @@ static struct dentry *afs_mkdir(struct mnt_idmap *idmap, struct inode *dir, op->file[0].modification = true; op->file[0].update_ctime = true; op->dentry = dentry; - op->create.mode = S_IFDIR | mode; + op->create.mode = mode; op->create.reason = afs_edit_dir_for_mkdir; op->mtime = current_time(dir); op->ops = &afs_mkdir_operation; diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 186e960f1e23..b36439f4521e 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -741,7 +741,7 @@ static struct dentry *autofs_dir_mkdir(struct mnt_idmap *idmap, autofs_del_active(dentry); - inode = autofs_get_inode(dir->i_sb, S_IFDIR | mode); + inode = autofs_get_inode(dir->i_sb, mode); if (!inode) return ERR_PTR(-ENOMEM); diff --git a/fs/backing-file.c b/fs/backing-file.c index 080c99696cd0..cc101143f921 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -35,7 +35,7 @@ struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred) { - const struct path *user_path = &user_file->f_path; + const struct path *user_path = file_user_path(user_file); struct file *f; int error; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 910c06297119..7e82dad952ec 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7111,7 +7111,7 @@ static struct dentry *btrfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, inode = new_inode(dir->i_sb); if (!inode) return ERR_PTR(-ENOMEM); - inode_init_owner(idmap, inode, dir, S_IFDIR | mode); + inode_init_owner(idmap, inode, dir, mode); inode->i_op = &btrfs_dir_inode_operations; inode->i_fop = &btrfs_dir_file_operations; return ERR_PTR(btrfs_create_common(dir, dentry, inode)); diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index cee70570bd0b..4a5e0290f2e3 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1147,7 +1147,6 @@ static struct dentry *ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, goto out; } - mode |= S_IFDIR; req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); if (IS_ERR(req->r_new_inode)) { ret = ERR_CAST(req->r_new_inode); @@ -1673,7 +1672,7 @@ __dentry_leases_walk(struct ceph_mds_client *mdsc, if (!spin_trylock(&dentry->d_lock)) continue; - if (__lockref_is_dead(&dentry->d_lockref)) { + if (lockref_is_dead(&dentry->d_lockref)) { list_del_init(&di->lease_list); goto next; } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index ea710a5dbbb7..67148edfadee 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -179,7 +179,12 @@ static struct dentry *coda_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (is_root_inode(dir) && coda_iscontrol(name, len)) return ERR_PTR(-EPERM); - attrs.va_mode = mode; + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to userspace, which has only ever been given the permission + * bits. Strip the type bit until venus is known to cope with it. + */ + attrs.va_mode = mode & ~S_IFDIR; error = venus_mkdir(dir->i_sb, coda_i2f(dir), name, len, &newfid, &attrs); if (error) diff --git a/fs/dcache.c b/fs/dcache.c index 3e9af9de7074..1b1a81f10da6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -434,7 +434,7 @@ static inline void __d_clear_type_and_inode(struct dentry *dentry) static void dentry_free(struct dentry *dentry) { DENTRY_WARN_ONCE(d_really_is_positive(dentry), dentry); - DENTRY_WARN_ONCE(dentry->d_lockref.count >= 0, dentry); + DENTRY_WARN_ONCE(!lockref_is_dead(&dentry->d_lockref), dentry); D_FLAG_VERIFY(dentry, 0); if (unlikely(dname_external(dentry))) { struct external_name *p = external_name(dentry); @@ -782,7 +782,7 @@ static bool lock_for_kill(struct dentry *dentry) * * If @dentry is idle and remains such after we assemble the full * locking environment for eviction (see lock_for_kill() for details) - * we mark it doomed (->d_lockref.count < 0) and proceed to detaching + * we mark it doomed (see lockref_mark_dead()) and proceed to detaching * it from any filesystem objects. Otherwise we drop ->d_lock and * return %NULL. * @@ -946,7 +946,7 @@ static inline bool fast_dput(struct dentry *dentry) if (unlikely(ret < 0)) { spin_lock(&dentry->d_lock); rcu_read_unlock(); - if (WARN_ON_ONCE(dentry->d_lockref.count <= 0)) { + if (WARN_ON_ONCE(lockref_is_dead_or_zero(&dentry->d_lockref))) { spin_unlock(&dentry->d_lock); return true; } @@ -1644,7 +1644,7 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry) if (data->start == dentry) goto out; - if (dentry->d_lockref.count <= 0) { + if (lockref_is_dead_or_zero(&dentry->d_lockref)) { __move_to_shrink_list(dentry, &data->dispose); data->found++; } @@ -1676,7 +1676,7 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry) if (data->start == dentry) goto out; - if (dentry->d_lockref.count <= 0) { + if (lockref_is_dead_or_zero(&dentry->d_lockref)) { if (!__move_to_shrink_list(dentry, &data->dispose)) { /* * We need an enter RCU read-side critical area that @@ -1747,7 +1747,7 @@ static void shrink_dcache_tree(struct dentry *parent, bool for_umount) spin_lock(&v->d_lock); rcu_read_unlock(); - if (unlikely(v->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&v->d_lockref))) { // It's doomed; if it isn't dead yet, notify us // once it becomes invisible to d_walk(). need_wait = d_add_waiter(v, &wait); @@ -1794,7 +1794,12 @@ static void do_one_tree(struct dentry *dentry) { shrink_dcache_tree(dentry, true); d_walk(dentry, dentry, umount_check); - d_drop(dentry); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + /* A busy root survives the dput() below so don't leave it on ->s_roots. */ + if (unlikely(!hlist_unhashed(&dentry->d_sib))) + unlink_secondary_root(dentry); + spin_unlock(&dentry->d_lock); dput(dentry); } @@ -1823,7 +1828,7 @@ void shrink_dcache_for_umount(struct super_block *sb) spin_unlock(&sb->s_roots_lock); spin_lock(&dentry->d_lock); rcu_read_unlock(); - if (unlikely(dentry->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&dentry->d_lockref))) { struct completion_list wait; bool need_wait = d_add_waiter(dentry, &wait); @@ -2822,7 +2827,7 @@ retry: spin_lock(&dentry->d_lock); rcu_read_unlock(); /* now we can try to grab a reference */ - if (unlikely(dentry->d_lockref.count < 0)) { + if (unlikely(lockref_is_dead(&dentry->d_lockref))) { spin_unlock(&dentry->d_lock); goto retry; } diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 74520e910259..d022d1dff5a1 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -725,7 +725,7 @@ static bool z_erofs_get_pcluster(struct z_erofs_pcluster *pcl) return true; spin_lock(&pcl->lockref.lock); - if (__lockref_is_dead(&pcl->lockref)) { + if (lockref_is_dead(&pcl->lockref)) { spin_unlock(&pcl->lockref.lock); return false; } @@ -945,7 +945,7 @@ static void z_erofs_put_pcluster(struct erofs_sb_info *sbi, if (lockref_put_or_lock(&pcl->lockref)) return; - DBG_BUGON(__lockref_is_dead(&pcl->lockref)); + DBG_BUGON(lockref_is_dead(&pcl->lockref)); if (!--pcl->lockref.count) { if (try_free && xa_trylock(&sbi->managed_pslots)) { free = __erofs_try_to_release_pcluster(sbi, pcl); diff --git a/fs/eventpoll.c b/fs/eventpoll.c index eed8cecd94e3..e0c4bf88a838 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2264,7 +2264,6 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, lockdep_assert_irqs_enabled(); if (timeout && (timeout->tv_sec | timeout->tv_nsec)) { - slack = select_estimate_accuracy(timeout); to = &expires; *to = timespec64_to_ktime(*timeout); } else if (timeout) { @@ -2343,10 +2342,13 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, spin_unlock_irq(&ep->lock); - if (!eavail) + if (!eavail) { + if (to) + slack = select_estimate_accuracy(timeout); timed_out = !ep_schedule_timeout(to) || !schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS); + } __set_current_state(TASK_RUNNING); /* diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 742a78e165d4..8666233ec63b 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -236,7 +236,7 @@ static struct dentry *ext2_mkdir(struct mnt_idmap * idmap, inode_inc_link_count(dir); - inode = ext2_new_inode(dir, S_IFDIR | mode, &dentry->d_name); + inode = ext2_new_inode(dir, mode, &dentry->d_name); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c3de64d2a2df..640a03ee02c7 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3009,7 +3009,7 @@ static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); retry: - inode = ext4_new_inode_start_handle(idmap, dir, S_IFDIR | mode, + inode = ext4_new_inode_start_handle(idmap, dir, mode, &dentry->d_name, 0, NULL, EXT4_HT_DIR, credits); handle = ext4_journal_current_handle(); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 648681c5ba50..5ae647a352aa 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -742,7 +742,7 @@ static struct dentry *f2fs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (err) return ERR_PTR(err); - inode = f2fs_new_inode(idmap, dir, S_IFDIR | mode, NULL); + inode = f2fs_new_inode(idmap, dir, mode, NULL); if (IS_ERR(inode)) return ERR_CAST(inode); diff --git a/fs/filesystems.c b/fs/filesystems.c index 673a03b5f32b..3083d904df9b 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -201,17 +201,17 @@ SYSCALL_DEFINE3(sysfs, int, option, unsigned long, arg1, unsigned long, arg2) int retval = -EINVAL; switch (option) { - case 1: - retval = fs_index((const char __user *) arg1); - break; + case 1: + retval = fs_index((const char __user *) arg1); + break; - case 2: - retval = fs_name(arg1, (char __user *) arg2); - break; + case 2: + retval = fs_name(arg1, (char __user *) arg2); + break; - case 3: - retval = fs_maxindex(); - break; + case 3: + retval = fs_maxindex(); + break; } return retval; } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0efb3141f7f7..d4e0029810c0 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1117,6 +1117,14 @@ static struct dentry *fuse_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!fm->fc->dont_mask) mode &= ~current_umask(); + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to the userspace server which has only ever been given the + * permission bits. Strip the type bit until the protocol is known to + * cope with it. + */ + mode &= ~S_IFDIR; + memset(&inarg, 0, sizeof(inarg)); inarg.mode = mode; inarg.umask = current_umask(); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index b8a144d3a73b..eaa2980051ed 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2080,7 +2080,7 @@ static void clear_glock(struct gfs2_glock *gl) gfs2_glock_remove_from_lru(gl); spin_lock(&gl->gl_lockref.lock); - if (!__lockref_is_dead(&gl->gl_lockref)) { + if (!lockref_is_dead(&gl->gl_lockref)) { gl->gl_lockref.count++; if (gl->gl_state != LM_ST_UNLOCKED) request_demote(gl, LM_ST_UNLOCKED, 0, false); @@ -2115,7 +2115,7 @@ static void dump_glock_func(struct gfs2_glock *gl) static void withdraw_glock(struct gfs2_glock *gl) { spin_lock(&gl->gl_lockref.lock); - if (!__lockref_is_dead(&gl->gl_lockref)) { + if (!lockref_is_dead(&gl->gl_lockref)) { /* * We don't want to write back any more dirty data. Unlock the * remaining inode and resource group glocks; this will cause @@ -2483,7 +2483,7 @@ static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi, loff_t n) continue; break; } else { - if (__lockref_is_dead(&gl->gl_lockref)) + if (lockref_is_dead(&gl->gl_lockref)) continue; n--; } diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 17bfa283b320..f361876c5583 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1350,7 +1350,7 @@ static struct dentry *gfs2_mkdir(struct mnt_idmap *idmap, struct inode *dir, { unsigned dsize = gfs2_max_stuffed_size(GFS2_I(dir)); - return ERR_PTR(gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0)); + return ERR_PTR(gfs2_create_inode(dir, dentry, NULL, mode, 0, NULL, dsize, 0)); } /** diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 7828ad0b6f5a..cc901fb97da0 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -126,7 +126,7 @@ static void gdlm_ast(void *arg) clear_bit(GLF_BLOCKING, &gl->gl_flags); /* If the glock is dead, we only react to a dlm_unlock() reply. */ - if (__lockref_is_dead(&gl->gl_lockref) && + if (lockref_is_dead(&gl->gl_lockref) && gl->gl_lksb.sb_status != -DLM_EUNLOCK) return; @@ -182,7 +182,7 @@ static void gdlm_bast(void *arg, int mode) { struct gfs2_glock *gl = arg; - if (__lockref_is_dead(&gl->gl_lockref)) + if (lockref_is_dead(&gl->gl_lockref)) return; switch (mode) { @@ -329,7 +329,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl) uint32_t flags = 0; int error; - BUG_ON(!__lockref_is_dead(&gl->gl_lockref)); + BUG_ON(!lockref_is_dead(&gl->gl_lockref)); if (test_bit(GLF_INITIAL, &gl->gl_flags)) { gfs2_glock_free(gl); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 91e9975d25e8..001c8b39ca55 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -342,7 +342,7 @@ static void qd_put(struct gfs2_quota_data *qd) if (lockref_put_or_lock(&qd->qd_lockref)) return; - BUG_ON(__lockref_is_dead(&qd->qd_lockref)); + BUG_ON(lockref_is_dead(&qd->qd_lockref)); sdp = qd->qd_sbd; if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) { lockref_mark_dead(&qd->qd_lockref); @@ -486,7 +486,7 @@ static bool qd_grab_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, qd->qd_sync_gen >= sync_gen) goto out; - if (__lockref_is_dead(&qd->qd_lockref)) + if (lockref_is_dead(&qd->qd_lockref)) goto out; qd->qd_lockref.count++; diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index 93edc5a80c81..e1f1fb351464 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -219,7 +219,7 @@ static struct dentry *hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; int res; - inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode); + inode = hfs_new_inode(dir, &dentry->d_name, mode); if (IS_ERR(inode)) return ERR_CAST(inode); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index a466c401f6bb..ecdafc658928 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -82,7 +82,7 @@ void hfs_mark_mdb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->mdb_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->mdb_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index f0aae2cd6fcf..51fcba2e6d40 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -570,7 +570,7 @@ static int hfsplus_create(struct mnt_idmap *idmap, struct inode *dir, static struct dentry *hfsplus_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - return ERR_PTR(hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0)); + return ERR_PTR(hfsplus_mknod(&nop_mnt_idmap, dir, dentry, mode, 0)); } static int hfsplus_rename(struct mnt_idmap *idmap, diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 5777e31de45a..ff7d6b3336a6 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -312,7 +312,7 @@ void hfsplus_mark_mdb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sync_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sync_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 809113d8248d..9446f4038874 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -105,10 +105,10 @@ static struct dentry *hpfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!uid_eq(result->i_uid, current_fsuid()) || !gid_eq(result->i_gid, current_fsgid()) || - result->i_mode != (mode | S_IFDIR)) { + result->i_mode != mode) { result->i_uid = current_fsuid(); result->i_gid = current_fsgid(); - result->i_mode = mode | S_IFDIR; + result->i_mode = mode; hpfs_write_inode_nolock(result); } hpfs_update_directory_times(dir); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 16d8437aed51..38e9e59f64d8 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -971,7 +971,7 @@ static struct dentry *hugetlbfs_mkdir(struct mnt_idmap *idmap, struct inode *dir struct dentry *dentry, umode_t mode) { int retval = hugetlbfs_mknod(idmap, dir, dentry, - mode | S_IFDIR, 0); + mode, 0); if (!retval) inc_nlink(dir); return ERR_PTR(retval); diff --git a/fs/inode.c b/fs/inode.c index 31c5b9ee3a81..ba7da39be4a3 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -763,21 +763,18 @@ void clear_inode(struct inode *inode) fsverity_cleanup_inode(inode); /* - * We have to cycle the i_pages lock here because reclaim can be in the - * process of removing the last page (in __filemap_remove_folio()) - * and we must not free the mapping under it. + * We have to cycle the i_pages lock here because reclaim + * can be in the process of removing the last page (in + * __filemap_remove_folio()) and we must not free the mapping + * under it. We also remove nodes which are empty; these + * can occur in two different ways. The first is that radix + * tree expansion can fail partway and the second is that THP + * collapse_file() can allocate some temporary nodes and not + * clean them up. */ - xa_lock_irq(&inode->i_data.i_pages); + xa_destroy(&inode->i_data.i_pages); + BUG_ON(inode->i_data.nrpages); - /* - * Almost always, mapping_empty(&inode->i_data) here; but there are - * two known and long-standing ways in which nodes may get left behind - * (when deep radix-tree node allocation failed partway; or when THP - * collapse_file() failed). Until those two known cases are cleaned up, - * or a cleanup function is called here, do not BUG_ON(!mapping_empty), - * nor even WARN_ON(!mapping_empty). - */ - xa_unlock_irq(&inode->i_data.i_pages); BUG_ON(!(inode_state_read_once(inode) & I_FREEING)); BUG_ON(inode_state_read_once(inode) & I_CLEAR); BUG_ON(!list_empty(&inode->i_wb_list)); @@ -1285,7 +1282,6 @@ EXPORT_SYMBOL(unlock_two_nondirectories); * @test: callback used for comparisons between inodes * @set: callback used to initialize a new struct inode * @data: opaque data pointer to pass to @test and @set - * @isnew: pointer to a bool which will indicate whether I_NEW is set * * Search for the inode specified by @hashval and @data in the inode cache, * and if present return it with an increased reference count. This is a @@ -2833,8 +2829,8 @@ struct timespec64 inode_set_ctime_to_ts(struct inode *inode, struct timespec64 t { trace_inode_set_ctime_to_ts(inode, &ts); set_normalized_timespec64(&ts, ts.tv_sec, ts.tv_nsec); - inode->i_ctime_sec = ts.tv_sec; - inode->i_ctime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_ctime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_ctime_nsec, ts.tv_nsec); return ts; } EXPORT_SYMBOL(inode_set_ctime_to_ts); @@ -2908,7 +2904,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode) */ cns = smp_load_acquire(&inode->i_ctime_nsec); if (cns & I_CTIME_QUERIED) { - struct timespec64 ctime = { .tv_sec = inode->i_ctime_sec, + struct timespec64 ctime = { .tv_sec = inode_get_ctime_sec(inode), .tv_nsec = cns & ~I_CTIME_QUERIED }; if (timespec64_compare(&now, &ctime) <= 0) { @@ -2920,7 +2916,7 @@ struct timespec64 inode_set_ctime_current(struct inode *inode) mgtime_counter_inc(mg_ctime_updates); /* No need to cmpxchg if it's exactly the same */ - if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec) { + if (cns == now.tv_nsec && inode_get_ctime_sec(inode) == now.tv_sec) { trace_ctime_xchg_skip(inode, &now); goto out; } @@ -2929,7 +2925,7 @@ retry: /* Try to swap the nsec value into place. */ if (try_cmpxchg(&inode->i_ctime_nsec, &cur, now.tv_nsec)) { /* If swap occurred, then we're (mostly) done */ - inode->i_ctime_sec = now.tv_sec; + WRITE_ONCE(inode->i_ctime_sec, now.tv_sec); trace_ctime_ns_xchg(inode, cns, now.tv_nsec, cur); mgtime_counter_inc(mg_ctime_swaps); } else { @@ -2944,7 +2940,7 @@ retry: goto retry; } /* Otherwise, keep the existing ctime */ - now.tv_sec = inode->i_ctime_sec; + now.tv_sec = inode_get_ctime_sec(inode); now.tv_nsec = cur & ~I_CTIME_QUERIED; } out: @@ -2977,7 +2973,7 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, struct timespec64 u /* pairs with try_cmpxchg below */ cur = smp_load_acquire(&inode->i_ctime_nsec); cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED; - cur_ts.tv_sec = inode->i_ctime_sec; + cur_ts.tv_sec = inode_get_ctime_sec(inode); /* If the update is older than the existing value, skip it. */ if (timespec64_compare(&update, &cur_ts) <= 0) @@ -3003,7 +2999,7 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, struct timespec64 u retry: old = cur; if (try_cmpxchg(&inode->i_ctime_nsec, &cur, update.tv_nsec)) { - inode->i_ctime_sec = update.tv_sec; + WRITE_ONCE(inode->i_ctime_sec, update.tv_sec); mgtime_counter_inc(mg_ctime_swaps); return update; } @@ -3019,7 +3015,7 @@ retry: goto retry; /* Otherwise, it was a new timestamp. */ - cur_ts.tv_sec = inode->i_ctime_sec; + cur_ts.tv_sec = inode_get_ctime_sec(inode); cur_ts.tv_nsec = cur & ~I_CTIME_QUERIED; return cur_ts; } diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index fc66b4aea37a..0a5ebfda90f1 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1188,7 +1188,6 @@ static bool iomap_write_end(struct iomap_iter *iter, size_t len, size_t copied, static int iomap_write_iter(struct iomap_iter *iter, struct iov_iter *i, const struct iomap_write_ops *write_ops) { - ssize_t total_written = 0; int status = 0; struct address_space *mapping = iter->inode->i_mapping; size_t chunk = mapping_max_folio_size(mapping); @@ -1284,12 +1283,11 @@ retry: goto retry; } } else { - total_written += written; iomap_iter_advance(iter, written); } } while (iov_iter_count(i) && iomap_length(iter)); - return total_written ? 0 : status; + return status; } ssize_t diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 3d4695b838ed..656c920864c5 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -462,8 +462,6 @@ static struct dentry *jffs2_mkdir (struct mnt_idmap *idmap, struct inode *dir_i, uint32_t alloclen; int ret; - mode |= S_IFDIR; - ri = jffs2_alloc_raw_inode(); if (!ri) return ERR_PTR(-ENOMEM); diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 8ff7a0b6add2..3b7803c75d58 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -1177,7 +1177,7 @@ void jffs2_dirty_trigger(struct jffs2_sb_info *c) return; delay = msecs_to_jiffies(dirty_writeback_interval * 10); - if (queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay)) + if (queue_delayed_work(system_dfl_long_wq, &c->wbuf_dwork, delay)) jffs2_dbg(1, "%s()\n", __func__); } diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 2cf4e280ee18..8a36c218f0f7 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -223,7 +223,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, * block there while holding dtree page, so we allocate the inode & * begin the transaction before we search the directory. */ - ip = ialloc(dip, S_IFDIR | mode); + ip = ialloc(dip, mode); if (IS_ERR(ip)) { rc = PTR_ERR(ip); goto out2; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 79e591bdfdc1..5525ba367ed7 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -110,7 +110,7 @@ static struct dentry *minix_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct inode * inode; int err; - inode = minix_new_inode(dir, S_IFDIR | mode); + inode = minix_new_inode(dir, mode); if (IS_ERR(inode)) return ERR_CAST(inode); diff --git a/fs/namei.c b/fs/namei.c index 9c34304d6671..20a6534ea3ef 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4140,11 +4140,6 @@ EXPORT_SYMBOL(end_renaming); * after setgid stripping allows the same ordering for both non-POSIX ACL and * POSIX ACL supporting filesystems. * - * Note that it's currently valid for @type to be 0 if a directory is created. - * Filesystems raise that flag individually and we need to check whether each - * filesystem can deal with receiving S_IFDIR from the vfs before we enforce a - * non-zero type. - * * Returns: mode to be passed to the filesystem */ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, @@ -5399,7 +5394,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!dir->i_op->mkdir) goto err; - mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, 0); + mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR); error = security_inode_mkdir(dir, dentry, mode); if (error) goto err; diff --git a/fs/namespace.c b/fs/namespace.c index eca22cea4380..426ac9e4eece 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -6288,7 +6288,7 @@ void put_mnt_ns(struct mnt_namespace *ns) guard(namespace_excl)(); emptied_ns = ns; guard(mount_writer)(); - umount_tree(ns->root, 0); + umount_tree(ns->root, UMOUNT_CONNECTED); } struct vfsmount *kern_mount(struct file_system_type *type) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 95c45c8d9114..36f2e8588922 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2474,7 +2474,7 @@ struct dentry *nfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, dir->i_sb->s_id, dir->i_ino, dentry); attr.ia_valid = ATTR_MODE; - attr.ia_mode = mode | S_IFDIR; + attr.ia_mode = mode; trace_nfs_mkdir_enter(dir, dentry); ret = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 0e0a9850ff76..77c5f7f74fbf 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -231,7 +231,7 @@ static struct dentry *nilfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, inc_nlink(dir); - inode = nilfs_new_inode(dir, S_IFDIR | mode); + inode = nilfs_new_inode(dir, mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 2e37b2056b11..8364d7bb571d 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -1083,7 +1083,7 @@ static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (!(vol->vol_flags & VOLUME_IS_DIRTY)) ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY); - ni = __ntfs_create(idmap, dir, uname, uname_len, S_IFDIR | mode, 0, NULL, 0); + ni = __ntfs_create(idmap, dir, uname, uname_len, mode, 0, NULL, 0); kmem_cache_free(ntfs_name_cache, uname); if (IS_ERR(ni)) { err = PTR_ERR(ni); diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c index 6d032b22c97d..5ab45d17333e 100644 --- a/fs/ntfs3/namei.c +++ b/fs/ntfs3/namei.c @@ -213,7 +213,7 @@ static struct dentry *ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { return ERR_PTR(ntfs_create_inode(idmap, dir, dentry, NULL, - S_IFDIR | mode, 0, NULL, 0, NULL)); + mode, 0, NULL, 0, NULL)); } /* diff --git a/fs/nullfs.c b/fs/nullfs.c index c6f5b9493e26..e06352c7b2cc 100644 --- a/fs/nullfs.c +++ b/fs/nullfs.c @@ -4,6 +4,8 @@ #include <linux/fs_context.h> #include <linux/magic.h> +#include "mount.h" + static const struct super_operations nullfs_super_operations = { .statfs = simple_statfs, }; diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index f0124f81df29..53df5dd10ad0 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -422,7 +422,7 @@ static struct dentry *dlmfs_mkdir(struct mnt_idmap * idmap, goto bail; } - inode = dlmfs_get_inode(dir, dentry, mode | S_IFDIR); + inode = dlmfs_get_inode(dir, dentry, mode); if (!inode) { status = -ENOMEM; mlog_errno(status); diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 12a1fef3ee74..b23dd678a7e0 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -657,7 +657,7 @@ static struct dentry *ocfs2_mkdir(struct mnt_idmap *idmap, trace_ocfs2_mkdir(dir, dentry, dentry->d_name.len, dentry->d_name.name, OCFS2_I(dir)->ip_blkno, mode); - ret = ocfs2_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); + ret = ocfs2_mknod(&nop_mnt_idmap, dir, dentry, mode, 0); if (ret) mlog_errno(ret); diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c index a09a98f7e30b..692297cf84e7 100644 --- a/fs/omfs/dir.c +++ b/fs/omfs/dir.c @@ -282,7 +282,7 @@ out_free_inode: static struct dentry *omfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - return ERR_PTR(omfs_add_node(dir, dentry, mode | S_IFDIR)); + return ERR_PTR(omfs_add_node(dir, dentry, mode)); } static int omfs_create(struct mnt_idmap *idmap, struct inode *dir, diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c index 91f97db18971..8ebc34e112d5 100644 --- a/fs/orangefs/namei.c +++ b/fs/orangefs/namei.c @@ -332,7 +332,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, ref = new_op->downcall.resp.mkdir.refn; - inode = orangefs_new_inode(dir->i_sb, dir, S_IFDIR | mode, 0, &ref); + inode = orangefs_new_inode(dir->i_sb, dir, mode, 0, &ref); if (IS_ERR(inode)) { gossip_err("*** Failed to allocate orangefs dir inode\n"); ret = PTR_ERR(inode); diff --git a/fs/pidfs.c b/fs/pidfs.c index b57ecc96e967..a6a643f15d08 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -532,6 +532,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; struct ns_common *ns_common = NULL; + int error; if (!pidfs_ioctl_valid(cmd)) return -ENOIOCTLCMD; @@ -555,20 +556,33 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (arg) return -EINVAL; + /* + * We're trying to open a file descriptor to the namespace so perform a + * filesystem cred ptrace check. Hold @task's exec_update_lock for the + * duration of the ptrace check and the namespace lookup so that the + * credentials used for the access decision match those of @task at the + * time its namespace is read, preventing a concurrent execve() from + * swapping the task's credentials in between the check and the use. We + * mirror nsfs behavior. + */ + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + return error; + + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + error = -EACCES; + goto out_unlock; + } + scoped_guard(task_lock, task) { nsp = task->nsproxy; if (nsp) get_nsproxy(nsp); } - if (!nsp) - return -ESRCH; /* just pretend it didn't exist */ - - /* - * We're trying to open a file descriptor to the namespace so perform a - * filesystem cred ptrace check. Also, we mirror nsfs behavior. - */ - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - return -EACCES; + if (!nsp) { + error = -ESRCH; /* just pretend it didn't exist */ + goto out_unlock; + } switch (cmd) { /* Namespaces that hang of nsproxy. */ @@ -650,11 +664,16 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #endif break; default: - return -ENOIOCTLCMD; + error = -ENOIOCTLCMD; } - if (!ns_common) - return -EOPNOTSUPP; + if (!error && !ns_common) + error = -EOPNOTSUPP; + +out_unlock: + up_read(&task->signal->exec_update_lock); + if (error) + return error; /* open_namespace() unconditionally consumes the reference */ return open_namespace(ns_common); diff --git a/fs/pipe.c b/fs/pipe.c index 429b0714ec57..0c77191a050c 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -111,75 +111,95 @@ void pipe_double_lock(struct pipe_inode_info *pipe1, pipe_lock(pipe2); } -#define PIPE_PREALLOC_MAX 8 +static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc) +{ + if (!prealloc->count) + return NULL; -struct anon_pipe_prealloc { - struct page *pages[PIPE_PREALLOC_MAX]; - unsigned int count; -}; + prealloc->count--; + + return prealloc->pages[prealloc->count]; +} + +/* Push a page to the prealloc pool. Returns true if added, false if full. */ +static bool anon_pipe_prealloc_push(struct anon_pipe_prealloc *prealloc, + struct page *page) +{ + if (prealloc->count >= PIPE_PREALLOC_MAX) + return false; + prealloc->pages[prealloc->count++] = page; + return true; +} /* - * 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(). + * Top up the pipe's own pool, then take pipe->mutex and return with it held. + * The shortfall is allocated outside the lock; the push and the caller's write + * then run under a single lock acquisition, avoiding a separate prefill + * lock/unlock cycle. anon_pipe_get_page() drains the pool instead of allocating + * under the lock. */ -static void anon_pipe_get_page_prealloc(struct anon_pipe_prealloc *prealloc, - size_t total_len) +static void anon_pipe_prefill_and_lock(struct pipe_inode_info *pipe, size_t total_len) { - unsigned int want, i; - struct page *page; - - prealloc->count = 0; - if (total_len <= PAGE_SIZE) - return; + struct page *pages[PIPE_PREALLOC_MAX]; + unsigned int want, have, need, n = 0; want = min_t(unsigned int, DIV_ROUND_UP(total_len, PAGE_SIZE), PIPE_PREALLOC_MAX); + /* Unlocked read; the pool is refilled under the lock below. */ + have = min_t(unsigned int, READ_ONCE(pipe->prealloc.count), want); + need = want - have; + + if (!need) { + mutex_lock(&pipe->mutex); + return; + } + + while (n < need) { + struct page *page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT); - for (i = 0; i < want; i++) { - page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT); if (!page) break; - prealloc->pages[prealloc->count++] = page; + pages[n++] = page; } + + mutex_lock(&pipe->mutex); + while (n && anon_pipe_prealloc_push(&pipe->prealloc, pages[n - 1])) + n--; + + /* + * Just flush any extra page that got affected by the TOCTOU + * effect + */ + while (n) + put_page(pages[--n]); } -static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc) +/* + * Called with pipe->mutex held. Trim the pool down to PIPE_PREALLOC_KEEP under + * the lock, drop it, then free the excess outside the critical section. + */ +static void anon_pipe_trim_and_unlock(struct pipe_inode_info *pipe) { - if (!prealloc->count) - return NULL; + struct page *excess[PIPE_PREALLOC_MAX]; + unsigned int nexcess = 0; - prealloc->count--; + while (pipe->prealloc.count > PIPE_PREALLOC_KEEP) + excess[nexcess++] = anon_pipe_prealloc_pop(&pipe->prealloc); + mutex_unlock(&pipe->mutex); - return prealloc->pages[prealloc->count]; + while (nexcess) + put_page(excess[--nexcess]); } -static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe, - struct anon_pipe_prealloc *prealloc) +static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe) { struct page *page; - /* Drain prealloc first to keep tmp_page[] hot for later small writes. */ - page = anon_pipe_prealloc_pop(prealloc); + /* Drain the prealloc pool before allocating. Called with mutex held. */ + page = anon_pipe_prealloc_pop(&pipe->prealloc); if (page) return page; - for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (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); } @@ -187,48 +207,11 @@ static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe, static void anon_pipe_put_page(struct pipe_inode_info *pipe, struct page *page) { - if (page_count(page) == 1) { - for (int i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (!pipe->tmp_page[i]) { - pipe->tmp_page[i] = page; - return; - } - } - } - - 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) + if (page_count(page) == 1 && + anon_pipe_prealloc_push(&pipe->prealloc, page)) 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]); - } + put_page(page); } static void anon_pipe_buf_release(struct pipe_inode_info *pipe, @@ -485,7 +468,8 @@ anon_pipe_read(struct kiocb *iocb, struct iov_iter *to) } if (pipe_is_empty(pipe)) wake_next_reader = false; - mutex_unlock(&pipe->mutex); + /* Consumed buffers may have refilled the pool; trim it and unlock. */ + anon_pipe_trim_and_unlock(pipe); if (wake_writer) wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM); @@ -524,7 +508,6 @@ 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); @@ -548,9 +531,7 @@ 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); + anon_pipe_prefill_and_lock(pipe, total_len); if (!pipe->readers) { if ((iocb->ki_flags & IOCB_NOSIGNAL) == 0) @@ -607,7 +588,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from) struct page *page; int copied; - page = anon_pipe_get_page(pipe, &prealloc); + page = anon_pipe_get_page(pipe); if (unlikely(!page)) { if (!ret) ret = -ENOMEM; @@ -671,11 +652,9 @@ 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); + anon_pipe_trim_and_unlock(pipe); /* * If we do do a wakeup event, we do a 'sync' wakeup, because we @@ -686,10 +665,9 @@ out: * how (for example) the GNU make jobserver uses small writes to * wake up pending jobs * - * Epoll nonsensically wants a wakeup whether the pipe - * was already empty or not. + * ->pseudo_edgetrigger enables per-write wakeups, see pipe_poll() */ - if (was_empty || pipe->poll_usage) + if (was_empty || READ_ONCE(pipe->pseudo_edgetrigger)) wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); if (wake_next_writer) @@ -752,7 +730,6 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } } -/* No kernel lock held - fine */ static __poll_t pipe_poll(struct file *filp, poll_table *wait) { @@ -760,9 +737,17 @@ pipe_poll(struct file *filp, poll_table *wait) struct pipe_inode_info *pipe = filp->private_data; union pipe_index idx; - /* Epoll has some historical nasty semantics, this enables them */ - if (unlikely(!READ_ONCE(pipe->poll_usage))) - WRITE_ONCE(pipe->poll_usage, true); + /* + * Legacy epoll(EPOLLET) users depend on historical per-write wakeups, + * see 3a34b13a88ca ("pipe: make pipe writes always wake up readers") + * and the ->pseudo_edgetrigger check in anon_pipe_write(). + * Currently io_uring sets EPOLLET for multishot polls, so it gets the + * same behaviour. + */ + if ((filp->f_mode & FMODE_READ) && + wait && (wait->_key & EPOLLET) && + unlikely(!READ_ONCE(pipe->pseudo_edgetrigger))) + WRITE_ONCE(pipe->pseudo_edgetrigger, true); /* * Reading pipe state only -- no need for acquiring the semaphore. @@ -956,10 +941,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) if (pipe->watch_queue) put_watch_queue(pipe->watch_queue); #endif - for (i = 0; i < ARRAY_SIZE(pipe->tmp_page); i++) { - if (pipe->tmp_page[i]) - __free_page(pipe->tmp_page[i]); - } + for (i = 0; i < pipe->prealloc.count; i++) + __free_page(pipe->prealloc.pages[i]); kfree(pipe->bufs); kfree(pipe); } diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 3dc62c1c2708..18b302f94174 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -747,8 +747,6 @@ static int posix_acl_fix_xattr_common(const void *value, size_t size) count = posix_acl_xattr_count(size); if (count < 0) return -EINVAL; - if (count == 0) - return 0; return count; } diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 0f52ba22aac0..0a88ede48e0a 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -121,7 +121,7 @@ out: static struct dentry *ramfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode) { - int retval = ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFDIR, 0); + int retval = ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode, 0); if (!retval) inc_nlink(dir); return ERR_PTR(retval); diff --git a/fs/romfs/super.c b/fs/romfs/super.c index ac55193bf398..3a836af3ca7e 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -240,6 +240,8 @@ static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) offset = be32_to_cpu(ri.spec) & ROMFH_MASK; inode = romfs_iget(dir->i_sb, offset); + if (IS_ERR(inode)) + return ERR_CAST(inode); break; } @@ -262,6 +264,8 @@ static const struct inode_operations romfs_dir_inode_operations = { .lookup = romfs_lookup, }; +#define ROMFS_MAX_HARDLINK_DEPTH 64 + /* * get a romfs inode based on its position in the image (which doubles as the * inode number) @@ -273,6 +277,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) struct inode *i; unsigned long nlen; unsigned nextfh; + unsigned int depth = 0; int ret; umode_t mode; @@ -289,6 +294,9 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) break; + if (++depth > ROMFS_MAX_HARDLINK_DEPTH) + return ERR_PTR(-ELOOP); + pos = be32_to_cpu(ri.spec) & ROMFH_MASK; } diff --git a/fs/seq_file.c b/fs/seq_file.c index 4745db2a34d1..456c78719fd0 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -428,7 +428,7 @@ EXPORT_SYMBOL(seq_bprintf); #endif /* CONFIG_BINARY_PRINTF */ /** - * mangle_path - mangle and copy path to buffer beginning + * seq_mangle_path - mangle and copy path to buffer beginning * @s: buffer start * @p: beginning of path in above buffer * @esc: set of characters that need escaping @@ -438,7 +438,7 @@ EXPORT_SYMBOL(seq_bprintf); * Returns pointer past last written character in @s, or NULL in case of * failure. */ -char *mangle_path(char *s, const char *p, const char *esc) +char *seq_mangle_path(char *s, const char *p, const char *esc) { while (s <= p) { char c = *p++; @@ -457,7 +457,6 @@ char *mangle_path(char *s, const char *p, const char *esc) } return NULL; } -EXPORT_SYMBOL(mangle_path); /** * seq_path - seq_file interface to print a pathname @@ -477,7 +476,7 @@ int seq_path(struct seq_file *m, const struct path *path, const char *esc) if (size) { char *p = d_path(path, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } @@ -520,7 +519,7 @@ int seq_path_root(struct seq_file *m, const struct path *path, return SEQ_SKIP; res = PTR_ERR(p); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; else @@ -544,7 +543,7 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc) if (size) { char *p = dentry_path(dentry, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 0afff761aab9..18f562ac172e 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2287,6 +2287,13 @@ struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, const char *full_path; void *page; + /* + * vfs_mkdir() now passes S_IFDIR in @mode, but @mode is forwarded + * verbatim to the server and in the past only contained permission + * bits. Strip the type bit until SMB is verified to deal with it. + */ + mode &= ~S_IFDIR; + cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", mode, inode); diff --git a/fs/stat.c b/fs/stat.c index 89909746bed1..c461c3054234 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -53,7 +53,7 @@ void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode) } stat->mtime = inode_get_mtime(inode); - stat->ctime.tv_sec = inode->i_ctime_sec; + stat->ctime.tv_sec = inode_get_ctime_sec(inode); stat->ctime.tv_nsec = (u32)atomic_read(pcn); if (!(stat->ctime.tv_nsec & I_CTIME_QUERIED)) stat->ctime.tv_nsec = ((u32)atomic_fetch_or(I_CTIME_QUERIED, pcn)); diff --git a/fs/super.c b/fs/super.c index ffdcc6a2e0de..2c52c321885f 100644 --- a/fs/super.c +++ b/fs/super.c @@ -24,6 +24,7 @@ #include <linux/export.h> #include <linux/slab.h> #include <linux/blkdev.h> +#include <linux/memcontrol.h> #include <linux/mount.h> #include <linux/security.h> #include <linux/writeback.h> /* for the emergency remount stuff */ @@ -170,6 +171,19 @@ static void super_wake(struct super_block *sb, unsigned int flag) } /* + * The s_op->nr_cached_objects hooks (used for example by btrfs and xfs) + * operate on filesystem-global state and ignore sc->memcg. Driving them + * from per-memcg shrink_slab_memcg() invocations only burns CPU walking + * per-cpu counters and queueing duplicate work: the actual reclaim happens on + * the global path (kswapd or root direct reclaim) regardless. Restrict them + * to that path. + */ +static inline bool super_fs_objects_eligible(struct shrink_control *sc) +{ + return !sc->memcg || mem_cgroup_is_root(sc->memcg); +} + +/* * One thing we have to be careful of with a per-sb shrinker is that we don't * drop the last active reference to the superblock from within the shrinker. * If that happens we could trigger unregistering the shrinker from within the @@ -198,7 +212,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink, if (!super_trylock_shared(sb)) return SHRINK_STOP; - if (sb->s_op->nr_cached_objects) + if (sb->s_op->nr_cached_objects && super_fs_objects_eligible(sc)) fs_objects = sb->s_op->nr_cached_objects(sb, sc); inodes = list_lru_shrink_count(&sb->s_inode_lru, sc); @@ -259,7 +273,8 @@ static unsigned long super_cache_count(struct shrinker *shrink, return 0; smp_rmb(); - if (sb->s_op && sb->s_op->nr_cached_objects) + if (sb->s_op && sb->s_op->nr_cached_objects && + super_fs_objects_eligible(sc)) total_objects = sb->s_op->nr_cached_objects(sb, sc); total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc); diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index fd8df10547bf..23ec924162d6 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -1031,7 +1031,7 @@ static struct dentry *ubifs_mkdir(struct mnt_idmap *idmap, struct inode *dir, sz_change = CALC_DENT_SIZE(fname_len(&nm)); - inode = ubifs_new_inode(c, dir, S_IFDIR | mode, false); + inode = ubifs_new_inode(c, dir, mode, false); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_fname; diff --git a/fs/udf/namei.c b/fs/udf/namei.c index fd9b6f16f614..b90841ac0a40 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -428,7 +428,7 @@ static struct dentry *udf_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct udf_inode_info *dinfo = UDF_I(dir); struct udf_inode_info *iinfo; - inode = udf_new_inode(dir, S_IFDIR | mode); + inode = udf_new_inode(dir, mode); if (IS_ERR(inode)) return ERR_CAST(inode); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 5012e056200a..6703f3bcf76f 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -173,7 +173,7 @@ static struct dentry *ufs_mkdir(struct mnt_idmap * idmap, struct inode * dir, inode_inc_link_count(dir); - inode = ufs_new_inode(dir, S_IFDIR|mode); + inode = ufs_new_inode(dir, mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_dir; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index c4831a8b9b3f..6dcf6d048cce 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -672,7 +672,7 @@ void ufs_mark_sb_dirty(struct super_block *sb) spin_lock(&sbi->work_lock); if (!sbi->work_queued) { delay = msecs_to_jiffies(dirty_writeback_interval * 10); - queue_delayed_work(system_long_wq, &sbi->sync_work, delay); + queue_delayed_work(system_dfl_long_wq, &sbi->sync_work, delay); sbi->work_queued = 1; } spin_unlock(&sbi->work_lock); diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 48d7dfd3e15f..7b1a54da615a 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -80,7 +80,7 @@ xfs_buf_stale( spin_lock(&bp->b_lockref.lock); atomic_set(&bp->b_lru_ref, 0); - if (!__lockref_is_dead(&bp->b_lockref)) + if (!lockref_is_dead(&bp->b_lockref)) list_lru_del_obj(&bp->b_target->bt_lru, &bp->b_lru); spin_unlock(&bp->b_lockref.lock); } @@ -841,7 +841,7 @@ static void xfs_buf_destroy( struct xfs_buf *bp) { - ASSERT(__lockref_is_dead(&bp->b_lockref)); + ASSERT(lockref_is_dead(&bp->b_lockref)); ASSERT(!(bp->b_flags & _XBF_DELWRI_Q)); if (bp->b_pag) diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index e48f9e5a1b8a..4a3299abf774 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -305,7 +305,7 @@ xfs_vn_mkdir( struct dentry *dentry, umode_t mode) { - return ERR_PTR(xfs_generic_create(idmap, dir, dentry, mode | S_IFDIR, 0, NULL)); + return ERR_PTR(xfs_generic_create(idmap, dir, dentry, mode, 0, NULL)); } STATIC struct dentry * diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 896b24f87ac9..99a82107b8e6 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -128,7 +128,7 @@ xfs_qm_dqpurge( struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo; spin_lock(&dqp->q_lockref.lock); - if (dqp->q_lockref.count > 0 || __lockref_is_dead(&dqp->q_lockref)) { + if (dqp->q_lockref.count > 0 || lockref_is_dead(&dqp->q_lockref)) { spin_unlock(&dqp->q_lockref.lock); return -EAGAIN; } @@ -429,7 +429,7 @@ xfs_qm_dquot_isolate( * from the LRU, leave it for the freeing task to complete the freeing * process rather than risk it being free from under us here. */ - if (__lockref_is_dead(&dqp->q_lockref)) + if (lockref_is_dead(&dqp->q_lockref)) goto out_miss_unlock; /* diff --git a/include/linux/fs.h b/include/linux/fs.h index c3c4a40c90a0..2f243b1554d8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1598,12 +1598,12 @@ struct timespec64 inode_set_ctime_deleg(struct inode *inode, static inline time64_t inode_get_atime_sec(const struct inode *inode) { - return inode->i_atime_sec; + return READ_ONCE(inode->i_atime_sec); } static inline long inode_get_atime_nsec(const struct inode *inode) { - return inode->i_atime_nsec; + return READ_ONCE(inode->i_atime_nsec); } static inline struct timespec64 inode_get_atime(const struct inode *inode) @@ -1617,8 +1617,8 @@ static inline struct timespec64 inode_get_atime(const struct inode *inode) static inline struct timespec64 inode_set_atime_to_ts(struct inode *inode, struct timespec64 ts) { - inode->i_atime_sec = ts.tv_sec; - inode->i_atime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_atime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_atime_nsec, ts.tv_nsec); return ts; } @@ -1633,12 +1633,12 @@ static inline struct timespec64 inode_set_atime(struct inode *inode, static inline time64_t inode_get_mtime_sec(const struct inode *inode) { - return inode->i_mtime_sec; + return READ_ONCE(inode->i_mtime_sec); } static inline long inode_get_mtime_nsec(const struct inode *inode) { - return inode->i_mtime_nsec; + return READ_ONCE(inode->i_mtime_nsec); } static inline struct timespec64 inode_get_mtime(const struct inode *inode) @@ -1651,8 +1651,8 @@ static inline struct timespec64 inode_get_mtime(const struct inode *inode) static inline struct timespec64 inode_set_mtime_to_ts(struct inode *inode, struct timespec64 ts) { - inode->i_mtime_sec = ts.tv_sec; - inode->i_mtime_nsec = ts.tv_nsec; + WRITE_ONCE(inode->i_mtime_sec, ts.tv_sec); + WRITE_ONCE(inode->i_mtime_nsec, ts.tv_nsec); return ts; } @@ -1677,12 +1677,12 @@ static inline struct timespec64 inode_set_mtime(struct inode *inode, static inline time64_t inode_get_ctime_sec(const struct inode *inode) { - return inode->i_ctime_sec; + return READ_ONCE(inode->i_ctime_sec); } static inline long inode_get_ctime_nsec(const struct inode *inode) { - return inode->i_ctime_nsec & ~I_CTIME_QUERIED; + return READ_ONCE(inode->i_ctime_nsec) & ~I_CTIME_QUERIED; } static inline struct timespec64 inode_get_ctime(const struct inode *inode) diff --git a/include/linux/lockref.h b/include/linux/lockref.h index 6ded24cdb4a8..ddfb7d3b8cec 100644 --- a/include/linux/lockref.h +++ b/include/linux/lockref.h @@ -34,6 +34,8 @@ struct lockref { }; }; +#define __LOCKREF_DEAD_VAL -128 + /** * lockref_init - Initialize a lockref * @lockref: pointer to lockref structure @@ -55,9 +57,15 @@ void lockref_mark_dead(struct lockref *lockref); bool lockref_get_not_dead(struct lockref *lockref); /* Must be called under spinlock for reliable results */ -static inline bool __lockref_is_dead(const struct lockref *l) +static inline bool lockref_is_dead(const struct lockref *l) +{ + return (READ_ONCE(l->count) == __LOCKREF_DEAD_VAL); +} + +static inline bool lockref_is_dead_or_zero(const struct lockref *l) { - return ((int)l->count < 0); + int count = READ_ONCE(l->count); + return (count == __LOCKREF_DEAD_VAL || count == 0); } #endif /* __LINUX_LOCKREF_H */ diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 7f6a92ac9704..6402930282e5 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -14,6 +14,9 @@ #define PIPE_BUF_FLAG_LOSS 0x40 /* Message loss happened after this buffer */ #endif +#define PIPE_PREALLOC_MAX 8 /* max pages in prealloc pool */ +#define PIPE_PREALLOC_KEEP 2 /* keep at least this many after trim */ + /** * struct pipe_buffer - a linux kernel pipe buffer * @page: the page containing the data for the pipe buffer @@ -59,6 +62,21 @@ union pipe_index { }; /** + * struct anon_pipe_prealloc - per-pipe page preallocation pool + * @pages: array of cached pages (pool) + * @count: number of pages currently in the pool + * + * Each pipe keeps a small bounded pool of preallocated pages to reduce + * allocation overhead during writes. The pool is bounded at PIPE_PREALLOC_MAX + * and trimmed down to PIPE_PREALLOC_KEEP after a write completes. + */ +struct anon_pipe_prealloc { + struct page *pages[PIPE_PREALLOC_MAX]; + + unsigned int __data_racy count; +}; + +/** * struct pipe_inode_info - a linux kernel pipe * @mutex: mutex protecting the whole thing * @rd_wait: reader wait point in case of empty pipe @@ -68,13 +86,13 @@ union pipe_index { * @max_usage: The maximum number of slots that may be used in the ring * @ring_size: total number of buffers (should be a power of 2) * @nr_accounted: The amount this pipe accounts for in user->pipe_bufs - * @tmp_page: cached released page + * @prealloc: per-pipe page preallocation pool * @readers: number of current readers of this pipe * @writers: number of current writers of this pipe * @files: number of struct file referring this pipe (protected by ->i_lock) * @r_counter: reader counter * @w_counter: writer counter - * @poll_usage: is this pipe used for epoll, which has crazy wakeups? + * @pseudo_edgetrigger: has an EPOLLET consumer, enable per-write wakeups * @fasync_readers: reader side fasync * @fasync_writers: writer side fasync * @bufs: the circular array of pipe buffers @@ -95,11 +113,11 @@ struct pipe_inode_info { unsigned int files; unsigned int r_counter; unsigned int w_counter; - bool poll_usage; + bool pseudo_edgetrigger; #ifdef CONFIG_WATCH_QUEUE bool note_loss; #endif - struct page *tmp_page[2]; + struct anon_pipe_prealloc prealloc; struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct pipe_buffer *bufs; diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 2fb266ea69fa..dc0e8c62d9e0 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -104,7 +104,7 @@ static inline void seq_setwidth(struct seq_file *m, size_t size) } void seq_pad(struct seq_file *m, char c); -char *mangle_path(char *s, const char *p, const char *esc); +char *seq_mangle_path(char *s, const char *p, const char *esc); int seq_open(struct file *, const struct seq_operations *); ssize_t seq_read(struct file *, char __user *, size_t, loff_t *); ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter); diff --git a/include/linux/stat.h b/include/linux/stat.h index e3d00e7bb26d..9c5709132862 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -41,8 +41,8 @@ struct kstat { u64 ino; dev_t dev; dev_t rdev; - kuid_t uid; - kgid_t gid; + kuid_t uid; /* This is logically a vfsuid_t. */ + kgid_t gid; /* This is logically a vfsgid_t. */ loff_t size; struct timespec64 atime; struct timespec64 mtime; diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h index a25e38d1c874..007fed5971b4 100644 --- a/include/uapi/linux/nsfs.h +++ b/include/uapi/linux/nsfs.h @@ -96,9 +96,10 @@ enum ns_type { * struct ns_id_req - namespace ID request structure * @size: size of this structure * @spare: reserved for future use - * @filter: filter mask - * @ns_id: last namespace id - * @user_ns_id: owning user namespace ID + * @ns_id: last namespace ID + * @ns_type: bit mask of namespace types to include + * @spare2: reserved for future use + * @user_ns_id: filter on this user namespace ID (or 0) * * Structure for passing namespace ID and miscellaneous parameters to * statns(2) and listns(2). diff --git a/init/initramfs.c b/init/initramfs.c index 4e27b97a8844..3cee8b50ad82 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -619,7 +619,7 @@ void __init reserve_initrd_mem(void) phys_addr_t start; unsigned long size; - /* Ignore the virtul address computed during device tree parsing */ + /* Ignore the virtual address computed during device tree parsing */ initrd_start = initrd_end = 0; if (!phys_initrd_size) diff --git a/lib/lockref.c b/lib/lockref.c index 5d8e3ef3860e..9b3dd688d8cd 100644 --- a/lib/lockref.c +++ b/lib/lockref.c @@ -131,7 +131,7 @@ EXPORT_SYMBOL(lockref_put_or_lock); void lockref_mark_dead(struct lockref *lockref) { assert_spin_locked(&lockref->lock); - lockref->count = -128; + lockref->count = __LOCKREF_DEAD_VAL; } EXPORT_SYMBOL(lockref_mark_dead); diff --git a/lib/seq_buf.c b/lib/seq_buf.c index b59488fa8135..a92093f346da 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -321,7 +321,7 @@ int seq_buf_path(struct seq_buf *s, const struct path *path, const char *esc) if (size) { char *p = d_path(path, buf, size); if (!IS_ERR(p)) { - char *end = mangle_path(buf, p, esc); + char *end = seq_mangle_path(buf, p, esc); if (end) res = end - buf; } diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 4854a8eda449..8a4b6ddc68df 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -44,6 +44,7 @@ TARGETS += filesystems/move_mount TARGETS += filesystems/empty_mntns TARGETS += filesystems/fsmount_ns TARGETS += filesystems/fscontext_ns +TARGETS += filesystems/mntns_cleanup TARGETS += firmware TARGETS += fpu TARGETS += ftrace diff --git a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c index f6f1a7ff01b0..81a994943e12 100644 --- a/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c +++ b/tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c @@ -3538,4 +3538,27 @@ TEST(epoll65) close(ctx.efd[1]); } +TEST(epoll66) +{ + struct epoll_event event; + int pfd[2], efd; + + ASSERT_EQ(pipe(pfd), 0); + + efd = epoll_create1(0); + ASSERT_GE(efd, 0); + + event.events = EPOLLIN | EPOLLET; + ASSERT_EQ(epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event), 0); + + for (int i = 0; i < 2; ++i) { + ASSERT_EQ(write(pfd[1], "", 1), 1); + EXPECT_EQ(epoll_wait(efd, &event, 1, 0), 1); + } + + close(pfd[0]); + close(pfd[1]); + close(efd); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore b/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore new file mode 100644 index 000000000000..493fbcf8d9ec --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +mntns_cleanup_test diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/Makefile b/tools/testing/selftests/filesystems/mntns_cleanup/Makefile new file mode 100644 index 000000000000..0e09e7030a5c --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_GEN_PROGS := mntns_cleanup_test + +CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) + +include ../../lib.mk diff --git a/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c b/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c new file mode 100644 index 000000000000..5209712568b1 --- /dev/null +++ b/tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "../../kselftest_harness.h" + +FIXTURE(mntns_cleanup) { +}; + +FIXTURE_SETUP(mntns_cleanup) +{ + if (geteuid() != 0) + SKIP(return, "test requires CAP_SYS_ADMIN"); + + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0); + + rmdir("/mnt_dir"); + ASSERT_EQ(mkdir("/mnt_dir", 0755), 0); + ASSERT_EQ(mount("tmpfs", "/mnt_dir", "tmpfs", 0, NULL), 0); + ASSERT_EQ(mkdir("/mnt_dir/hidden", 0755), 0); + ASSERT_EQ(mkdir("/mnt_dir/hidden/secret", 0755), 0); + ASSERT_EQ(mount("tmpfs", "/mnt_dir/hidden", "tmpfs", 0, NULL), 0); +} + +FIXTURE_TEARDOWN(mntns_cleanup) +{ +} + +/* Mounts must stay connected when a mount namespace is cleaned up. */ +TEST_F(mntns_cleanup, keeps_mounts_connected) +{ + int fd, sfd, err; + + fd = open("/mnt_dir", O_PATH | O_DIRECTORY | O_CLOEXEC); + ASSERT_GE(fd, 0); + + /* Destroy the namespace; the fd keeps /mnt_dir alive. */ + ASSERT_EQ(unshare(CLONE_NEWNS), 0); + + sfd = openat(fd, "hidden/secret", O_RDONLY); + err = errno; + if (sfd >= 0) + close(sfd); + close(fd); + + ASSERT_LT(sfd, 0) + TH_LOG("mount namespace teardown revealed what the overmount covered"); + ASSERT_EQ(err, ENOENT); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test.c b/tools/testing/selftests/filesystems/statmount/statmount_test.c index 8dc018d47a93..60c2c544db6a 100644 --- a/tools/testing/selftests/filesystems/statmount/statmount_test.c +++ b/tools/testing/selftests/filesystems/statmount/statmount_test.c @@ -82,6 +82,9 @@ static void cleanup_namespace(void) { int ret; + if (f_mountinfo) + fclose(f_mountinfo); + ret = fchdir(orig_root); if (ret == -1) ksft_perror("fchdir to original root"); @@ -515,7 +518,7 @@ static void test_statmount_mnt_opts(void) return; } - ksft_test_result_fail("didnt't find mount entry\n"); + ksft_test_result_fail("didn't find mount entry\n"); free(sm); free(line); } diff --git a/tools/testing/selftests/namespaces/nsid_test.c b/tools/testing/selftests/namespaces/nsid_test.c index 46dc838cba82..a16f31f41d38 100644 --- a/tools/testing/selftests/namespaces/nsid_test.c +++ b/tools/testing/selftests/namespaces/nsid_test.c @@ -649,8 +649,6 @@ TEST_F(nsid, timens_separate) /* Fork a grandchild to actually enter the new namespace */ pid_t grandchild = fork(); if (grandchild == 0) { - /* Grandchild is in the new namespace */ - write(pipefd[1], "Y", 1); close(pipefd[1]); pause(); _exit(0); @@ -771,8 +769,6 @@ TEST_F(nsid, pidns_separate) /* Fork a grandchild to actually enter the new namespace */ pid_t grandchild = fork(); if (grandchild == 0) { - /* Grandchild is in the new namespace */ - write(pipefd[1], "Y", 1); close(pipefd[1]); pause(); _exit(0); diff --git a/tools/testing/selftests/proc/proc-pidns.c b/tools/testing/selftests/proc/proc-pidns.c index 25b9a2933c45..6f7c10fe97b3 100644 --- a/tools/testing/selftests/proc/proc-pidns.c +++ b/tools/testing/selftests/proc/proc-pidns.c @@ -6,6 +6,7 @@ #include <assert.h> #include <errno.h> +#include <fcntl.h> #include <sched.h> #include <stdbool.h> #include <stdlib.h> |
