mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:09:30 +02:00
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
...
This commit is contained in:
@@ -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.
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+1
-2
@@ -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;
|
||||
}
|
||||
|
||||
+6
-1
@@ -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)
|
||||
|
||||
+14
-9
@@ -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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+4
-2
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+9
-9
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
+3
-3
@@ -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--;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+2
-2
@@ -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++;
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+19
-23
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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__);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-6
@@ -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;
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+31
-12
@@ -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);
|
||||
|
||||
@@ -111,47 +111,6 @@ void pipe_double_lock(struct pipe_inode_info *pipe1,
|
||||
pipe_lock(pipe2);
|
||||
}
|
||||
|
||||
#define PIPE_PREALLOC_MAX 8
|
||||
|
||||
struct anon_pipe_prealloc {
|
||||
struct page *pages[PIPE_PREALLOC_MAX];
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
/*
|
||||
* Pre-allocate pages outside pipe->mutex for multi-page writes.
|
||||
* alloc_page() with GFP_HIGHUSER can sleep in reclaim and runs memcg
|
||||
* charging; doing it under the mutex stalls a concurrent reader.
|
||||
*
|
||||
* Loop alloc_page() instead of alloc_pages_bulk_*(): the bulk path refuses
|
||||
* __GFP_ACCOUNT under memcg (see commit 8dcb3060d81d "memcg: page_alloc:
|
||||
* skip bulk allocator for __GFP_ACCOUNT") and silently degrades to a single
|
||||
* page. A per-page loop keeps memcg accounting and the task NUMA mempolicy
|
||||
* honoured for every page; the per-call overhead is small compared to the
|
||||
* pipe->mutex hold-time being shrunk. Any shortfall is covered by the
|
||||
* in-lock alloc_page() fallback in anon_pipe_get_page().
|
||||
*/
|
||||
static void anon_pipe_get_page_prealloc(struct anon_pipe_prealloc *prealloc,
|
||||
size_t total_len)
|
||||
{
|
||||
unsigned int want, i;
|
||||
struct page *page;
|
||||
|
||||
prealloc->count = 0;
|
||||
if (total_len <= PAGE_SIZE)
|
||||
return;
|
||||
|
||||
want = min_t(unsigned int, DIV_ROUND_UP(total_len, PAGE_SIZE),
|
||||
PIPE_PREALLOC_MAX);
|
||||
|
||||
for (i = 0; i < want; i++) {
|
||||
page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
|
||||
if (!page)
|
||||
break;
|
||||
prealloc->pages[prealloc->count++] = page;
|
||||
}
|
||||
}
|
||||
|
||||
static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc)
|
||||
{
|
||||
if (!prealloc->count)
|
||||
@@ -162,24 +121,85 @@ static struct page *anon_pipe_prealloc_pop(struct anon_pipe_prealloc *prealloc)
|
||||
return prealloc->pages[prealloc->count];
|
||||
}
|
||||
|
||||
static struct page *anon_pipe_get_page(struct pipe_inode_info *pipe,
|
||||
struct anon_pipe_prealloc *prealloc)
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_prefill_and_lock(struct pipe_inode_info *pipe, size_t total_len)
|
||||
{
|
||||
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);
|
||||
|
||||
if (!page)
|
||||
break;
|
||||
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]);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct page *excess[PIPE_PREALLOC_MAX];
|
||||
unsigned int nexcess = 0;
|
||||
|
||||
while (pipe->prealloc.count > PIPE_PREALLOC_KEEP)
|
||||
excess[nexcess++] = anon_pipe_prealloc_pop(&pipe->prealloc);
|
||||
mutex_unlock(&pipe->mutex);
|
||||
|
||||
while (nexcess)
|
||||
put_page(excess[--nexcess]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+5
-6
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
+17
-2
@@ -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 */
|
||||
@@ -169,6 +170,19 @@ static void super_wake(struct super_block *sb, unsigned int flag)
|
||||
wake_up_var(&sb->s_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+1
-1
@@ -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 *
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
+10
-10
@@ -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)
|
||||
|
||||
+10
-2
@@ -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 ((int)l->count < 0);
|
||||
return (READ_ONCE(l->count) == __LOCKREF_DEAD_VAL);
|
||||
}
|
||||
|
||||
static inline bool lockref_is_dead_or_zero(const struct lockref *l)
|
||||
{
|
||||
int count = READ_ONCE(l->count);
|
||||
return (count == __LOCKREF_DEAD_VAL || count == 0);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_LOCKREF_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
|
||||
@@ -58,6 +61,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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
mntns_cleanup_test
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Reference in New Issue
Block a user