summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/filesystems/porting.rst2
-rw-r--r--fs/9p/vfs_inode.c2
-rw-r--r--fs/9p/vfs_inode_dotl.c1
-rw-r--r--fs/affs/namei.c2
-rw-r--r--fs/affs/super.c2
-rw-r--r--fs/afs/dir.c2
-rw-r--r--fs/autofs/root.c2
-rw-r--r--fs/backing-file.c2
-rw-r--r--fs/btrfs/inode.c2
-rw-r--r--fs/ceph/dir.c3
-rw-r--r--fs/coda/dir.c7
-rw-r--r--fs/dcache.c23
-rw-r--r--fs/erofs/zdata.c4
-rw-r--r--fs/eventpoll.c6
-rw-r--r--fs/ext2/namei.c2
-rw-r--r--fs/ext4/namei.c2
-rw-r--r--fs/f2fs/namei.c2
-rw-r--r--fs/filesystems.c18
-rw-r--r--fs/fuse/dir.c8
-rw-r--r--fs/gfs2/glock.c6
-rw-r--r--fs/gfs2/inode.c2
-rw-r--r--fs/gfs2/lock_dlm.c6
-rw-r--r--fs/gfs2/quota.c4
-rw-r--r--fs/hfs/dir.c2
-rw-r--r--fs/hfs/super.c2
-rw-r--r--fs/hfsplus/dir.c2
-rw-r--r--fs/hfsplus/super.c2
-rw-r--r--fs/hpfs/namei.c4
-rw-r--r--fs/hugetlbfs/inode.c2
-rw-r--r--fs/inode.c42
-rw-r--r--fs/iomap/buffered-io.c4
-rw-r--r--fs/jffs2/dir.c2
-rw-r--r--fs/jffs2/wbuf.c2
-rw-r--r--fs/jfs/namei.c2
-rw-r--r--fs/minix/namei.c2
-rw-r--r--fs/namei.c7
-rw-r--r--fs/namespace.c2
-rw-r--r--fs/nfs/dir.c2
-rw-r--r--fs/nilfs2/namei.c2
-rw-r--r--fs/ntfs/namei.c2
-rw-r--r--fs/ntfs3/namei.c2
-rw-r--r--fs/nullfs.c2
-rw-r--r--fs/ocfs2/dlmfs/dlmfs.c2
-rw-r--r--fs/ocfs2/namei.c2
-rw-r--r--fs/omfs/dir.c2
-rw-r--r--fs/orangefs/namei.c2
-rw-r--r--fs/pidfs.c43
-rw-r--r--fs/pipe.c191
-rw-r--r--fs/posix_acl.c2
-rw-r--r--fs/ramfs/inode.c2
-rw-r--r--fs/romfs/super.c8
-rw-r--r--fs/seq_file.c11
-rw-r--r--fs/smb/client/inode.c7
-rw-r--r--fs/stat.c2
-rw-r--r--fs/super.c19
-rw-r--r--fs/ubifs/dir.c2
-rw-r--r--fs/udf/namei.c2
-rw-r--r--fs/ufs/namei.c2
-rw-r--r--fs/ufs/super.c2
-rw-r--r--fs/xfs/xfs_buf.c4
-rw-r--r--fs/xfs/xfs_iops.c2
-rw-r--r--fs/xfs/xfs_qm.c4
-rw-r--r--include/linux/fs.h20
-rw-r--r--include/linux/lockref.h12
-rw-r--r--include/linux/pipe_fs_i.h26
-rw-r--r--include/linux/seq_file.h2
-rw-r--r--include/linux/stat.h4
-rw-r--r--include/uapi/linux/nsfs.h7
-rw-r--r--init/initramfs.c2
-rw-r--r--lib/lockref.c2
-rw-r--r--lib/seq_buf.c2
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/filesystems/epoll/epoll_wakeup_test.c23
-rw-r--r--tools/testing/selftests/filesystems/mntns_cleanup/.gitignore2
-rw-r--r--tools/testing/selftests/filesystems/mntns_cleanup/Makefile6
-rw-r--r--tools/testing/selftests/filesystems/mntns_cleanup/mntns_cleanup_test.c58
-rw-r--r--tools/testing/selftests/filesystems/statmount/statmount_test.c5
-rw-r--r--tools/testing/selftests/namespaces/nsid_test.c4
-rw-r--r--tools/testing/selftests/proc/proc-pidns.c1
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>