Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Valentine Sinitsyn and committed by
Greg Kroah-Hartman
0fedefd4 7360a48b

+44 -1
+28 -1
fs/kernfs/file.c
··· 903 903 return ret; 904 904 } 905 905 906 + static loff_t kernfs_fop_llseek(struct file *file, loff_t offset, int whence) 907 + { 908 + struct kernfs_open_file *of = kernfs_of(file); 909 + const struct kernfs_ops *ops; 910 + loff_t ret; 911 + 912 + /* 913 + * @of->mutex nests outside active ref and is primarily to ensure that 914 + * the ops aren't called concurrently for the same open file. 915 + */ 916 + mutex_lock(&of->mutex); 917 + if (!kernfs_get_active(of->kn)) { 918 + mutex_unlock(&of->mutex); 919 + return -ENODEV; 920 + } 921 + 922 + ops = kernfs_ops(of->kn); 923 + if (ops->llseek) 924 + ret = ops->llseek(of, offset, whence); 925 + else 926 + ret = generic_file_llseek(file, offset, whence); 927 + 928 + kernfs_put_active(of->kn); 929 + mutex_unlock(&of->mutex); 930 + return ret; 931 + } 932 + 906 933 static void kernfs_notify_workfn(struct work_struct *work) 907 934 { 908 935 struct kernfs_node *kn; ··· 1032 1005 const struct file_operations kernfs_file_fops = { 1033 1006 .read_iter = kernfs_fop_read_iter, 1034 1007 .write_iter = kernfs_fop_write_iter, 1035 - .llseek = generic_file_llseek, 1008 + .llseek = kernfs_fop_llseek, 1036 1009 .mmap = kernfs_fop_mmap, 1037 1010 .open = kernfs_fop_open, 1038 1011 .release = kernfs_fop_release,
+13
fs/sysfs/file.c
··· 167 167 return battr->mmap(of->file, kobj, battr, vma); 168 168 } 169 169 170 + static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset, 171 + int whence) 172 + { 173 + struct bin_attribute *battr = of->kn->priv; 174 + struct kobject *kobj = of->kn->parent->priv; 175 + 176 + if (battr->llseek) 177 + return battr->llseek(of->file, kobj, battr, offset, whence); 178 + else 179 + return generic_file_llseek(of->file, offset, whence); 180 + } 181 + 170 182 static int sysfs_kf_bin_open(struct kernfs_open_file *of) 171 183 { 172 184 struct bin_attribute *battr = of->kn->priv; ··· 261 249 .write = sysfs_kf_bin_write, 262 250 .mmap = sysfs_kf_bin_mmap, 263 251 .open = sysfs_kf_bin_open, 252 + .llseek = sysfs_kf_bin_llseek, 264 253 }; 265 254 266 255 int sysfs_add_file_mode_ns(struct kernfs_node *parent,
+1
include/linux/kernfs.h
··· 316 316 struct poll_table_struct *pt); 317 317 318 318 int (*mmap)(struct kernfs_open_file *of, struct vm_area_struct *vma); 319 + loff_t (*llseek)(struct kernfs_open_file *of, loff_t offset, int whence); 319 320 }; 320 321 321 322 /*
+2
include/linux/sysfs.h
··· 181 181 char *, loff_t, size_t); 182 182 ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, 183 183 char *, loff_t, size_t); 184 + loff_t (*llseek)(struct file *, struct kobject *, struct bin_attribute *, 185 + loff_t, int); 184 186 int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, 185 187 struct vm_area_struct *vma); 186 188 };