git: ca4ce6a56f9c - main - linuxkpi: Add `sysfs_create_bin_file()` and `sysfs_remove_bin_file()`

From: Jean-Sébastien Pédron <dumbbell_at_FreeBSD.org>
Date: Mon, 07 Apr 2025 18:01:24 UTC
The branch main has been updated by dumbbell:

URL: https://cgit.FreeBSD.org/src/commit/?id=ca4ce6a56f9c174917ab33191d367a850fc15c89

commit ca4ce6a56f9c174917ab33191d367a850fc15c89
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2025-02-08 20:28:10 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2025-04-07 17:34:40 +0000

    linuxkpi: Add `sysfs_create_bin_file()` and `sysfs_remove_bin_file()`
    
    They are used by the i915 DRM driver for quite some time, but that code
    was commented out. It was moved around in Linux 6.8, so instead of
    figuring out what should be commented out now, let's add an
    implementation of these functions.
    
    Reviewed by:    bz
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D49068
---
 sys/compat/linuxkpi/common/include/linux/sysfs.h | 83 ++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h
index b5ad73e4460b..c6692b803789 100644
--- a/sys/compat/linuxkpi/common/include/linux/sysfs.h
+++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h
@@ -50,6 +50,15 @@ struct attribute_group {
 	struct attribute	**attrs;
 };
 
+struct bin_attribute {
+	struct attribute	attr;
+	size_t			size;
+	ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
+			char *, loff_t, size_t);
+	ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,
+			 char *, loff_t, size_t);
+};
+
 #define	__ATTR(_name, _mode, _show, _store) {				\
 	.attr = { .name = __stringify(_name), .mode = _mode },		\
 	.show = _show, .store  = _store,				\
@@ -155,6 +164,80 @@ sysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
 		sysctl_remove_name(kobj->oidp, attr->name, 1, 1);
 }
 
+static inline int
+sysctl_handle_bin_attr(SYSCTL_HANDLER_ARGS)
+{
+	struct kobject *kobj;
+	struct bin_attribute *attr;
+	char *buf;
+	int error;
+	ssize_t len;
+
+	kobj = arg1;
+	attr = (struct bin_attribute *)(intptr_t)arg2;
+	if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL)
+		return (ENODEV);
+	buf = (char *)get_zeroed_page(GFP_KERNEL);
+	if (buf == NULL)
+		return (ENOMEM);
+
+	if (attr->read) {
+		len = attr->read(
+		    NULL, /* <-- struct file, unimplemented */
+		    kobj, attr, buf, req->oldidx, PAGE_SIZE);
+		if (len < 0) {
+			error = -len;
+			if (error != EIO)
+				goto out;
+		}
+	}
+
+	error = sysctl_handle_opaque(oidp, buf, PAGE_SIZE, req);
+	if (error != 0 || req->newptr == NULL || attr->write == NULL)
+		goto out;
+
+	len = attr->write(
+	    NULL, /* <-- struct file, unimplemented */
+	    kobj, attr, buf, req->newidx, req->newlen);
+	if (len < 0)
+		error = -len;
+out:
+	free_page((unsigned long)buf);
+
+	return (error);
+}
+
+static inline int
+sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+	struct sysctl_oid *oid;
+	int ctlflags;
+
+	ctlflags = CTLTYPE_OPAQUE | CTLFLAG_MPSAFE;
+	if (attr->attr.mode & (S_IRUSR | S_IWUSR))
+		ctlflags |= CTLFLAG_RW;
+	else if (attr->attr.mode & S_IRUSR)
+		ctlflags |= CTLFLAG_RD;
+	else if (attr->attr.mode & S_IWUSR)
+		ctlflags |= CTLFLAG_WR;
+
+	oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO,
+	    attr->attr.name, ctlflags, kobj,
+	    (uintptr_t)attr, sysctl_handle_bin_attr, "", "");
+	if (oid == NULL)
+		return (-ENOMEM);
+
+	return (0);
+}
+
+static inline void
+sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr)
+{
+
+	if (kobj->oidp)
+		sysctl_remove_name(kobj->oidp, attr->attr.name, 1, 1);
+}
+
 static inline int
 sysfs_create_link(struct kobject *kobj __unused,
     struct kobject *target __unused, const char *name __unused)