git: fe84281803d6 - main - linuxkpi: Add `struct kset` support in <linux/kobject.h>

From: Jean-Sébastien Pédron <dumbbell_at_FreeBSD.org>
Date: Wed, 13 Dec 2023 19:00:13 UTC
The branch main has been updated by dumbbell:

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

commit fe84281803d62a6846ecab5f5a7c8b4e49b0f0e0
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2023-12-08 21:51:10 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2023-12-13 18:57:52 +0000

    linuxkpi: Add `struct kset` support in <linux/kobject.h>
    
    [Why]
    The amdgpu DRM driver started to use it in Linux 5.18.
    
    Reviewed by:    manu
    Approved by:    manu
    Differential Revision:  https://reviews.freebsd.org/D43020
---
 sys/compat/linuxkpi/common/include/linux/kobject.h |  49 +++++++
 sys/compat/linuxkpi/common/src/linux_kobject.c     | 141 ++++++++++++++++++++-
 2 files changed, 186 insertions(+), 4 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/kobject.h b/sys/compat/linuxkpi/common/include/linux/kobject.h
index 06d71faaa873..dadf3b3b0849 100644
--- a/sys/compat/linuxkpi/common/include/linux/kobject.h
+++ b/sys/compat/linuxkpi/common/include/linux/kobject.h
@@ -35,8 +35,10 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 
 struct kobject;
+struct kset;
 struct sysctl_oid;
 
 #define	KOBJ_CHANGE		0x01
@@ -57,6 +59,7 @@ struct kobject {
 	const struct kobj_type	*ktype;
 	struct list_head	entry;
 	struct sysctl_oid	*oidp;
+	struct kset		*kset;
 };
 
 extern struct kobject *mm_kobj;
@@ -77,6 +80,17 @@ struct kobj_attribute {
 	    const char *buf, size_t count);
 };
 
+struct kset_uevent_ops {
+	/* TODO */
+};
+
+struct kset {
+	struct list_head	list;
+	spinlock_t		list_lock;
+	struct kobject		kobj;
+	const struct kset_uevent_ops *uevent_ops;
+};
+
 static inline void
 kobject_init(struct kobject *kobj, const struct kobj_type *ktype)
 {
@@ -154,6 +168,41 @@ kobject_uevent_env(struct kobject *kobj, int action, char *envp[])
 	 */
 }
 
+void	kset_init(struct kset *kset);
+int	kset_register(struct kset *kset);
+void	kset_unregister(struct kset *kset);
+struct kset * kset_create_and_add(const char *name,
+    const struct kset_uevent_ops *u, struct kobject *parent_kobj);
+
+static inline struct kset *
+to_kset(struct kobject *kobj)
+{
+	if (kobj != NULL)
+		return container_of(kobj, struct kset, kobj);
+	else
+		return NULL;
+}
+
+static inline struct kset *
+kset_get(struct kset *kset)
+{
+	if (kset != NULL) {
+		struct kobject *kobj;
+
+		kobj = kobject_get(&kset->kobj);
+		return to_kset(kobj);
+	} else {
+		return NULL;
+	}
+}
+
+static inline void
+kset_put(struct kset *kset)
+{
+	if (kset != NULL)
+		kobject_put(&kset->kobj);
+}
+
 void linux_kobject_kfree_name(struct kobject *kobj);
 
 #endif /* _LINUXKPI_LINUX_KOBJECT_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_kobject.c b/sys/compat/linuxkpi/common/src/linux_kobject.c
index ddd0a58660f1..02f8b8d5b709 100644
--- a/sys/compat/linuxkpi/common/src/linux_kobject.c
+++ b/sys/compat/linuxkpi/common/src/linux_kobject.c
@@ -30,6 +30,10 @@
 #include <linux/kobject.h>
 #include <linux/sysfs.h>
 
+static void kset_join(struct kobject *kobj);
+static void kset_leave(struct kobject *kobj);
+static void kset_kfree(struct kobject *kobj);
+
 struct kobject *
 kobject_create(void)
 {
@@ -101,12 +105,16 @@ kobject_set_name(struct kobject *kobj, const char *fmt, ...)
 }
 
 static int
-kobject_add_complete(struct kobject *kobj, struct kobject *parent)
+kobject_add_complete(struct kobject *kobj)
 {
 	const struct kobj_type *t;
 	int error;
 
-	kobj->parent = parent;
+	if (kobj->kset != NULL) {
+		kset_join(kobj);
+		kobj->parent = &kobj->kset->kobj;
+	}
+
 	error = sysfs_create_dir(kobj);
 	if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) {
 		struct attribute **attr;
@@ -120,6 +128,10 @@ kobject_add_complete(struct kobject *kobj, struct kobject *parent)
 		if (error)
 			sysfs_remove_dir(kobj);
 	}
+
+	if (error != 0)
+		kset_leave(kobj);
+
 	return (error);
 }
 
@@ -129,13 +141,15 @@ kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
 	va_list args;
 	int error;
 
+	kobj->parent = parent;
+
 	va_start(args, fmt);
 	error = kobject_set_name_vargs(kobj, fmt, args);
 	va_end(args);
 	if (error)
 		return (error);
 
-	return kobject_add_complete(kobj, parent);
+	return kobject_add_complete(kobj);
 }
 
 int
@@ -155,7 +169,7 @@ kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype,
 	va_end(args);
 	if (error)
 		return (error);
-	return kobject_add_complete(kobj, parent);
+	return kobject_add_complete(kobj);
 }
 
 void
@@ -166,6 +180,7 @@ linux_kobject_release(struct kref *kref)
 
 	kobj = container_of(kref, struct kobject, kref);
 	sysfs_remove_dir(kobj);
+	kset_leave(kobj);
 	name = kobj->name;
 	if (kobj->ktype && kobj->ktype->release)
 		kobj->ktype->release(kobj);
@@ -219,3 +234,121 @@ const struct sysfs_ops kobj_sysfs_ops = {
 	.show	= lkpi_kobj_attr_show,
 	.store	= lkpi_kobj_attr_store,
 };
+
+const struct kobj_type linux_kset_kfree_type = {
+	.release = kset_kfree
+};
+
+static struct kset *
+kset_create(const char *name,
+    const struct kset_uevent_ops *uevent_ops,
+    struct kobject *parent_kobj)
+{
+	struct kset *kset;
+
+	kset = kzalloc(sizeof(*kset), GFP_KERNEL);
+	if (kset == NULL)
+		return (NULL);
+
+	kset->uevent_ops = uevent_ops;
+
+	kobject_set_name(&kset->kobj, "%s", name);
+	kset->kobj.parent = parent_kobj;
+	kset->kobj.kset = NULL;
+
+	return (kset);
+}
+
+void
+kset_init(struct kset *kset)
+{
+	kobject_init(&kset->kobj, &linux_kset_kfree_type);
+	INIT_LIST_HEAD(&kset->list);
+	spin_lock_init(&kset->list_lock);
+}
+
+static void
+kset_join(struct kobject *kobj)
+{
+	struct kset *kset;
+
+	kset = kobj->kset;
+	if (kset == NULL)
+		return;
+
+	kset_get(kobj->kset);
+
+	spin_lock(&kset->list_lock);
+	list_add_tail(&kobj->entry, &kset->list);
+	spin_unlock(&kset->list_lock);
+}
+
+static void
+kset_leave(struct kobject *kobj)
+{
+	struct kset *kset;
+
+	kset = kobj->kset;
+	if (kset == NULL)
+		return;
+
+	spin_lock(&kset->list_lock);
+	list_del_init(&kobj->entry);
+	spin_unlock(&kset->list_lock);
+
+	kset_put(kobj->kset);
+}
+
+struct kset *
+kset_create_and_add(const char *name, const struct kset_uevent_ops *u,
+    struct kobject *parent_kobj)
+{
+	int ret;
+	struct kset *kset;
+
+	kset = kset_create(name, u, parent_kobj);
+	if (kset == NULL)
+		return (NULL);
+
+	ret = kset_register(kset);
+	if (ret != 0) {
+		linux_kobject_kfree_name(&kset->kobj);
+		kfree(kset);
+		return (NULL);
+	}
+
+	return (kset);
+}
+
+int
+kset_register(struct kset *kset)
+{
+	int ret;
+
+	if (kset == NULL)
+		return -EINVAL;
+
+	kset_init(kset);
+	ret = kobject_add_complete(&kset->kobj);
+
+	return ret;
+}
+
+void
+kset_unregister(struct kset *kset)
+{
+	if (kset == NULL)
+		return;
+
+	kobject_del(&kset->kobj);
+	kobject_put(&kset->kobj);
+}
+
+static void
+kset_kfree(struct kobject *kobj)
+{
+	struct kset *kset;
+
+	kset = to_kset(kobj);
+	kfree(kset);
+}