Skip to content

Commit 24a988f

Browse files
committed
Merge patch series "file: remove f_version"
Christian Brauner <brauner@kernel.org> says: The f_version member in struct file isn't particularly well-defined. It is mainly used as a cookie to detect concurrent seeks when iterating directories. But it is also abused by some subsystems for completely unrelated things. It is mostly a directory specific thing that doesn't really need to live in struct file and with its wonky semantics it really lacks a specific function. For pipes, f_version is (ab)used to defer poll notifications until a write has happened. And struct pipe_inode_info is used by multiple struct files in their ->private_data so there's no chance of pushing that down into file->private_data without introducing another pointer indirection. But this should be a solvable problem. Only regular files with FMODE_ATOMIC_POS and directories require f_pos_lock. Pipes and other files don't. So this adds a union into struct file encompassing f_pos_lock and a pipe specific f_pipe member that pipes can use. This union of course can be extended to other file types and is similar to what we do in struct inode already. * patches from https://lore.kernel.org/r/20240830-vfs-file-f_version-v1-0-6d3e4816aa7b@kernel.org: fs: remove f_version pipe: use f_pipe fs: add f_pipe ubifs: store cookie in private data ufs: store cookie in private data udf: store cookie in private data proc: store cookie in private data ocfs2: store cookie in private data input: remove f_version abuse ext4: store cookie in private data ext2: store cookie in private data affs: store cookie in private data fs: add generic_llseek_cookie() fs: use must_set_pos() fs: add must_set_pos() fs: add vfs_setpos_cookie() s390: remove unused f_version ceph: remove unused f_version adi: remove unused f_version file: remove pointless comment Link: https://lore.kernel.org/r/20240830-vfs-file-f_version-v1-0-6d3e4816aa7b@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
2 parents 0f389ad + 11068e0 commit 24a988f

File tree

20 files changed

+405
-143
lines changed

20 files changed

+405
-143
lines changed

drivers/char/adi.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
196196

197197
if (offset != file->f_pos) {
198198
file->f_pos = offset;
199-
file->f_version = 0;
200199
ret = offset;
201200
}
202201

drivers/input/input.c

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,33 +1079,31 @@ static inline void input_wakeup_procfs_readers(void)
10791079
wake_up(&input_devices_poll_wait);
10801080
}
10811081

1082+
struct input_seq_state {
1083+
unsigned short pos;
1084+
bool mutex_acquired;
1085+
int input_devices_state;
1086+
};
1087+
10821088
static __poll_t input_proc_devices_poll(struct file *file, poll_table *wait)
10831089
{
1090+
struct seq_file *seq = file->private_data;
1091+
struct input_seq_state *state = seq->private;
1092+
10841093
poll_wait(file, &input_devices_poll_wait, wait);
1085-
if (file->f_version != input_devices_state) {
1086-
file->f_version = input_devices_state;
1094+
if (state->input_devices_state != input_devices_state) {
1095+
state->input_devices_state = input_devices_state;
10871096
return EPOLLIN | EPOLLRDNORM;
10881097
}
10891098

10901099
return 0;
10911100
}
10921101

1093-
union input_seq_state {
1094-
struct {
1095-
unsigned short pos;
1096-
bool mutex_acquired;
1097-
};
1098-
void *p;
1099-
};
1100-
11011102
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
11021103
{
1103-
union input_seq_state *state = (union input_seq_state *)&seq->private;
1104+
struct input_seq_state *state = seq->private;
11041105
int error;
11051106

1106-
/* We need to fit into seq->private pointer */
1107-
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
1108-
11091107
error = mutex_lock_interruptible(&input_mutex);
11101108
if (error) {
11111109
state->mutex_acquired = false;
@@ -1124,7 +1122,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
11241122

11251123
static void input_seq_stop(struct seq_file *seq, void *v)
11261124
{
1127-
union input_seq_state *state = (union input_seq_state *)&seq->private;
1125+
struct input_seq_state *state = seq->private;
11281126

11291127
if (state->mutex_acquired)
11301128
mutex_unlock(&input_mutex);
@@ -1210,25 +1208,23 @@ static const struct seq_operations input_devices_seq_ops = {
12101208

12111209
static int input_proc_devices_open(struct inode *inode, struct file *file)
12121210
{
1213-
return seq_open(file, &input_devices_seq_ops);
1211+
return seq_open_private(file, &input_devices_seq_ops,
1212+
sizeof(struct input_seq_state));
12141213
}
12151214

12161215
static const struct proc_ops input_devices_proc_ops = {
12171216
.proc_open = input_proc_devices_open,
12181217
.proc_poll = input_proc_devices_poll,
12191218
.proc_read = seq_read,
12201219
.proc_lseek = seq_lseek,
1221-
.proc_release = seq_release,
1220+
.proc_release = seq_release_private,
12221221
};
12231222

12241223
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
12251224
{
1226-
union input_seq_state *state = (union input_seq_state *)&seq->private;
1225+
struct input_seq_state *state = seq->private;
12271226
int error;
12281227

1229-
/* We need to fit into seq->private pointer */
1230-
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
1231-
12321228
error = mutex_lock_interruptible(&input_mutex);
12331229
if (error) {
12341230
state->mutex_acquired = false;
@@ -1243,7 +1239,7 @@ static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
12431239

12441240
static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
12451241
{
1246-
union input_seq_state *state = (union input_seq_state *)&seq->private;
1242+
struct input_seq_state *state = seq->private;
12471243

12481244
state->pos = *pos + 1;
12491245
return seq_list_next(v, &input_handler_list, pos);
@@ -1252,7 +1248,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
12521248
static int input_handlers_seq_show(struct seq_file *seq, void *v)
12531249
{
12541250
struct input_handler *handler = container_of(v, struct input_handler, node);
1255-
union input_seq_state *state = (union input_seq_state *)&seq->private;
1251+
struct input_seq_state *state = seq->private;
12561252

12571253
seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
12581254
if (handler->filter)
@@ -1273,14 +1269,15 @@ static const struct seq_operations input_handlers_seq_ops = {
12731269

12741270
static int input_proc_handlers_open(struct inode *inode, struct file *file)
12751271
{
1276-
return seq_open(file, &input_handlers_seq_ops);
1272+
return seq_open_private(file, &input_handlers_seq_ops,
1273+
sizeof(struct input_seq_state));
12771274
}
12781275

12791276
static const struct proc_ops input_handlers_proc_ops = {
12801277
.proc_open = input_proc_handlers_open,
12811278
.proc_read = seq_read,
12821279
.proc_lseek = seq_lseek,
1283-
.proc_release = seq_release,
1280+
.proc_release = seq_release_private,
12841281
};
12851282

12861283
static int __init input_proc_init(void)

drivers/s390/char/hmcdrv_dev.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,6 @@ static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence)
186186
if (pos < 0)
187187
return -EINVAL;
188188

189-
if (fp->f_pos != pos)
190-
++fp->f_version;
191-
192189
fp->f_pos = pos;
193190
return pos;
194191
}

fs/affs/dir.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,44 @@
1717
#include <linux/iversion.h>
1818
#include "affs.h"
1919

20+
struct affs_dir_data {
21+
unsigned long ino;
22+
u64 cookie;
23+
};
24+
2025
static int affs_readdir(struct file *, struct dir_context *);
2126

27+
static loff_t affs_dir_llseek(struct file *file, loff_t offset, int whence)
28+
{
29+
struct affs_dir_data *data = file->private_data;
30+
31+
return generic_llseek_cookie(file, offset, whence, &data->cookie);
32+
}
33+
34+
static int affs_dir_open(struct inode *inode, struct file *file)
35+
{
36+
struct affs_dir_data *data;
37+
38+
data = kzalloc(sizeof(struct affs_dir_data), GFP_KERNEL);
39+
if (!data)
40+
return -ENOMEM;
41+
file->private_data = data;
42+
return 0;
43+
}
44+
45+
static int affs_dir_release(struct inode *inode, struct file *file)
46+
{
47+
kfree(file->private_data);
48+
return 0;
49+
}
50+
2251
const struct file_operations affs_dir_operations = {
52+
.open = affs_dir_open,
2353
.read = generic_read_dir,
24-
.llseek = generic_file_llseek,
54+
.llseek = affs_dir_llseek,
2555
.iterate_shared = affs_readdir,
2656
.fsync = affs_file_fsync,
57+
.release = affs_dir_release,
2758
};
2859

2960
/*
@@ -45,6 +76,7 @@ static int
4576
affs_readdir(struct file *file, struct dir_context *ctx)
4677
{
4778
struct inode *inode = file_inode(file);
79+
struct affs_dir_data *data = file->private_data;
4880
struct super_block *sb = inode->i_sb;
4981
struct buffer_head *dir_bh = NULL;
5082
struct buffer_head *fh_bh = NULL;
@@ -59,7 +91,7 @@ affs_readdir(struct file *file, struct dir_context *ctx)
5991
pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos);
6092

6193
if (ctx->pos < 2) {
62-
file->private_data = (void *)0;
94+
data->ino = 0;
6395
if (!dir_emit_dots(file, ctx))
6496
return 0;
6597
}
@@ -80,8 +112,8 @@ affs_readdir(struct file *file, struct dir_context *ctx)
80112
/* If the directory hasn't changed since the last call to readdir(),
81113
* we can jump directly to where we left off.
82114
*/
83-
ino = (u32)(long)file->private_data;
84-
if (ino && inode_eq_iversion(inode, file->f_version)) {
115+
ino = data->ino;
116+
if (ino && inode_eq_iversion(inode, data->cookie)) {
85117
pr_debug("readdir() left off=%d\n", ino);
86118
goto inside;
87119
}
@@ -131,8 +163,8 @@ affs_readdir(struct file *file, struct dir_context *ctx)
131163
} while (ino);
132164
}
133165
done:
134-
file->f_version = inode_query_iversion(inode);
135-
file->private_data = (void *)(long)ino;
166+
data->cookie = inode_query_iversion(inode);
167+
data->ino = ino;
136168
affs_brelse(fh_bh);
137169

138170
out_brelse_dir:

fs/ceph/dir.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,6 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
707707

708708
if (offset != file->f_pos) {
709709
file->f_pos = offset;
710-
file->f_version = 0;
711710
dfi->file_info.flags &= ~CEPH_F_ATEND;
712711
}
713712
retval = offset;

fs/ext2/dir.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
263263
unsigned long n = pos >> PAGE_SHIFT;
264264
unsigned long npages = dir_pages(inode);
265265
unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
266-
bool need_revalidate = !inode_eq_iversion(inode, file->f_version);
266+
bool need_revalidate = !inode_eq_iversion(inode, *(u64 *)file->private_data);
267267
bool has_filetype;
268268

269269
if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
@@ -290,7 +290,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
290290
offset = ext2_validate_entry(kaddr, offset, chunk_mask);
291291
ctx->pos = (n<<PAGE_SHIFT) + offset;
292292
}
293-
file->f_version = inode_query_iversion(inode);
293+
*(u64 *)file->private_data = inode_query_iversion(inode);
294294
need_revalidate = false;
295295
}
296296
de = (ext2_dirent *)(kaddr+offset);
@@ -703,8 +703,30 @@ int ext2_empty_dir(struct inode *inode)
703703
return 0;
704704
}
705705

706+
static int ext2_dir_open(struct inode *inode, struct file *file)
707+
{
708+
file->private_data = kzalloc(sizeof(u64), GFP_KERNEL);
709+
if (!file->private_data)
710+
return -ENOMEM;
711+
return 0;
712+
}
713+
714+
static int ext2_dir_release(struct inode *inode, struct file *file)
715+
{
716+
kfree(file->private_data);
717+
return 0;
718+
}
719+
720+
static loff_t ext2_dir_llseek(struct file *file, loff_t offset, int whence)
721+
{
722+
return generic_llseek_cookie(file, offset, whence,
723+
(u64 *)file->private_data);
724+
}
725+
706726
const struct file_operations ext2_dir_operations = {
707-
.llseek = generic_file_llseek,
727+
.open = ext2_dir_open,
728+
.release = ext2_dir_release,
729+
.llseek = ext2_dir_llseek,
708730
.read = generic_read_dir,
709731
.iterate_shared = ext2_readdir,
710732
.unlocked_ioctl = ext2_ioctl,

fs/ext4/dir.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
133133
struct super_block *sb = inode->i_sb;
134134
struct buffer_head *bh = NULL;
135135
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
136+
struct dir_private_info *info = file->private_data;
136137

137138
err = fscrypt_prepare_readdir(inode);
138139
if (err)
@@ -229,7 +230,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
229230
* readdir(2), then we might be pointing to an invalid
230231
* dirent right now. Scan from the start of the block
231232
* to make sure. */
232-
if (!inode_eq_iversion(inode, file->f_version)) {
233+
if (!inode_eq_iversion(inode, info->cookie)) {
233234
for (i = 0; i < sb->s_blocksize && i < offset; ) {
234235
de = (struct ext4_dir_entry_2 *)
235236
(bh->b_data + i);
@@ -249,7 +250,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
249250
offset = i;
250251
ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
251252
| offset;
252-
file->f_version = inode_query_iversion(inode);
253+
info->cookie = inode_query_iversion(inode);
253254
}
254255

255256
while (ctx->pos < inode->i_size
@@ -384,6 +385,7 @@ static inline loff_t ext4_get_htree_eof(struct file *filp)
384385
static loff_t ext4_dir_llseek(struct file *file, loff_t offset, int whence)
385386
{
386387
struct inode *inode = file->f_mapping->host;
388+
struct dir_private_info *info = file->private_data;
387389
int dx_dir = is_dx_dir(inode);
388390
loff_t ret, htree_max = ext4_get_htree_eof(file);
389391

@@ -392,7 +394,7 @@ static loff_t ext4_dir_llseek(struct file *file, loff_t offset, int whence)
392394
htree_max, htree_max);
393395
else
394396
ret = ext4_llseek(file, offset, whence);
395-
file->f_version = inode_peek_iversion(inode) - 1;
397+
info->cookie = inode_peek_iversion(inode) - 1;
396398
return ret;
397399
}
398400

@@ -429,18 +431,15 @@ static void free_rb_tree_fname(struct rb_root *root)
429431
*root = RB_ROOT;
430432
}
431433

432-
433-
static struct dir_private_info *ext4_htree_create_dir_info(struct file *filp,
434-
loff_t pos)
434+
static void ext4_htree_init_dir_info(struct file *filp, loff_t pos)
435435
{
436-
struct dir_private_info *p;
437-
438-
p = kzalloc(sizeof(*p), GFP_KERNEL);
439-
if (!p)
440-
return NULL;
441-
p->curr_hash = pos2maj_hash(filp, pos);
442-
p->curr_minor_hash = pos2min_hash(filp, pos);
443-
return p;
436+
struct dir_private_info *p = filp->private_data;
437+
438+
if (is_dx_dir(file_inode(filp)) && !p->initialized) {
439+
p->curr_hash = pos2maj_hash(filp, pos);
440+
p->curr_minor_hash = pos2min_hash(filp, pos);
441+
p->initialized = true;
442+
}
444443
}
445444

446445
void ext4_htree_free_dir_info(struct dir_private_info *p)
@@ -552,12 +551,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
552551
struct fname *fname;
553552
int ret = 0;
554553

555-
if (!info) {
556-
info = ext4_htree_create_dir_info(file, ctx->pos);
557-
if (!info)
558-
return -ENOMEM;
559-
file->private_data = info;
560-
}
554+
ext4_htree_init_dir_info(file, ctx->pos);
561555

562556
if (ctx->pos == ext4_get_htree_eof(file))
563557
return 0; /* EOF */
@@ -590,10 +584,10 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
590584
* cached entries.
591585
*/
592586
if ((!info->curr_node) ||
593-
!inode_eq_iversion(inode, file->f_version)) {
587+
!inode_eq_iversion(inode, info->cookie)) {
594588
info->curr_node = NULL;
595589
free_rb_tree_fname(&info->root);
596-
file->f_version = inode_query_iversion(inode);
590+
info->cookie = inode_query_iversion(inode);
597591
ret = ext4_htree_fill_tree(file, info->curr_hash,
598592
info->curr_minor_hash,
599593
&info->next_hash);
@@ -664,7 +658,19 @@ int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf,
664658
return 0;
665659
}
666660

661+
static int ext4_dir_open(struct inode *inode, struct file *file)
662+
{
663+
struct dir_private_info *info;
664+
665+
info = kzalloc(sizeof(*info), GFP_KERNEL);
666+
if (!info)
667+
return -ENOMEM;
668+
file->private_data = info;
669+
return 0;
670+
}
671+
667672
const struct file_operations ext4_dir_operations = {
673+
.open = ext4_dir_open,
668674
.llseek = ext4_dir_llseek,
669675
.read = generic_read_dir,
670676
.iterate_shared = ext4_readdir,

0 commit comments

Comments
 (0)