Commit 0fedefd4 authored by Valentine Sinitsyn's avatar Valentine Sinitsyn Committed by Greg Kroah-Hartman
Browse files

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 636b21b5 ("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: default avatarValentine Sinitsyn <valesini@yandex-team.ru>
Link: https://lore.kernel.org/r/20230925084013.309399-1-valesini@yandex-team.ru


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7360a48b
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -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,
+13 −0
Original line number Diff line number Diff line
@@ -167,6 +167,18 @@ static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
	return battr->mmap(of->file, kobj, battr, vma);
}

static loff_t sysfs_kf_bin_llseek(struct kernfs_open_file *of, loff_t offset,
				  int whence)
{
	struct bin_attribute *battr = of->kn->priv;
	struct kobject *kobj = of->kn->parent->priv;

	if (battr->llseek)
		return battr->llseek(of->file, kobj, battr, offset, whence);
	else
		return generic_file_llseek(of->file, offset, whence);
}

static int sysfs_kf_bin_open(struct kernfs_open_file *of)
{
	struct bin_attribute *battr = of->kn->priv;
@@ -249,6 +261,7 @@ static const struct kernfs_ops sysfs_bin_kfops_mmap = {
	.write		= sysfs_kf_bin_write,
	.mmap		= sysfs_kf_bin_mmap,
	.open		= sysfs_kf_bin_open,
	.llseek		= sysfs_kf_bin_llseek,
};

int sysfs_add_file_mode_ns(struct kernfs_node *parent,
+1 −0
Original line number Diff line number Diff line
@@ -316,6 +316,7 @@ struct kernfs_ops {
			 struct poll_table_struct *pt);

	int (*mmap)(struct kernfs_open_file *of, struct vm_area_struct *vma);
	loff_t (*llseek)(struct kernfs_open_file *of, loff_t offset, int whence);
};

/*
+2 −0
Original line number Diff line number Diff line
@@ -181,6 +181,8 @@ struct bin_attribute {
			char *, loff_t, size_t);
	ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,
			 char *, loff_t, size_t);
	loff_t (*llseek)(struct file *, struct kobject *, struct bin_attribute *,
			 loff_t, int);
	int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
		    struct vm_area_struct *vma);
};