diff options
author | Valentine Sinitsyn <valesini@yandex-team.ru> | 2023-09-25 10:40:12 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-10-05 13:42:11 +0200 |
commit | 0fedefd4c4e33dd24f726b13b5d7c143e2b483be (patch) | |
tree | cf84459a4ef3e035265bd133847b1ea202d963ff /fs/kernfs | |
parent | debugfs: Fix __rcu type comparison warning (diff) | |
download | linux-0fedefd4c4e33dd24f726b13b5d7c143e2b483be.tar.xz linux-0fedefd4c4e33dd24f726b13b5d7c143e2b483be.zip |
kernfs: sysfs: support custom llseek method for sysfs entries
As of now, seeking in sysfs files is handled by generic_file_llseek().
There are situations where one may want to customize seeking logic:
- Many sysfs entries are fixed files while generic_file_llseek() accepts
past-the-end positions. Not only being useless by itself, this
also means a bug in userspace code will trigger not at lseek(), but at
some later point making debugging harder.
- generic_file_llseek() relies on f_mapping->host to get the file size
which might not be correct for all sysfs entries.
See commit 636b21b50152 ("PCI: Revoke mappings like devmem") as an example.
Implement llseek method to override this behavior at sysfs attribute
level. The method is optional, and if it is absent,
generic_file_llseek() is called to preserve backwards compatibility.
Signed-off-by: Valentine Sinitsyn <valesini@yandex-team.ru>
Link: https://lore.kernel.org/r/20230925084013.309399-1-valesini@yandex-team.ru
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r-- | fs/kernfs/file.c | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 180906c36f51..855e3f9d8dcc 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -903,6 +903,33 @@ static __poll_t kernfs_fop_poll(struct file *filp, poll_table *wait) return ret; } +static loff_t kernfs_fop_llseek(struct file *file, loff_t offset, int whence) +{ + struct kernfs_open_file *of = kernfs_of(file); + const struct kernfs_ops *ops; + loff_t ret; + + /* + * @of->mutex nests outside active ref and is primarily to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!kernfs_get_active(of->kn)) { + mutex_unlock(&of->mutex); + return -ENODEV; + } + + ops = kernfs_ops(of->kn); + if (ops->llseek) + ret = ops->llseek(of, offset, whence); + else + ret = generic_file_llseek(file, offset, whence); + + kernfs_put_active(of->kn); + mutex_unlock(&of->mutex); + return ret; +} + static void kernfs_notify_workfn(struct work_struct *work) { struct kernfs_node *kn; @@ -1005,7 +1032,7 @@ EXPORT_SYMBOL_GPL(kernfs_notify); const struct file_operations kernfs_file_fops = { .read_iter = kernfs_fop_read_iter, .write_iter = kernfs_fop_write_iter, - .llseek = generic_file_llseek, + .llseek = kernfs_fop_llseek, .mmap = kernfs_fop_mmap, .open = kernfs_fop_open, .release = kernfs_fop_release, |