xref: /linux/drivers/dibs/dibs_main.c (revision 05e68d8dedf34f270cc3769ffe7f0ed413f23add)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  DIBS - Direct Internal Buffer Sharing
4  *
5  *  Implementation of the DIBS class module
6  *
7  *  Copyright IBM Corp. 2025
8  */
9 #define KMSG_COMPONENT "dibs"
10 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11 
12 #include <linux/module.h>
13 #include <linux/types.h>
14 #include <linux/slab.h>
15 #include <linux/err.h>
16 #include <linux/dibs.h>
17 
18 #include "dibs_loopback.h"
19 
20 MODULE_DESCRIPTION("Direct Internal Buffer Sharing class");
21 MODULE_LICENSE("GPL");
22 
23 static struct class *dibs_class;
24 
25 /* use an array rather a list for fast mapping: */
26 static struct dibs_client *clients[MAX_DIBS_CLIENTS];
27 static u8 max_client;
28 static DEFINE_MUTEX(clients_lock);
29 struct dibs_dev_list {
30 	struct list_head list;
31 	struct mutex mutex; /* protects dibs device list */
32 };
33 
34 static struct dibs_dev_list dibs_dev_list = {
35 	.list = LIST_HEAD_INIT(dibs_dev_list.list),
36 	.mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex),
37 };
38 
39 int dibs_register_client(struct dibs_client *client)
40 {
41 	struct dibs_dev *dibs;
42 	int i, rc = -ENOSPC;
43 
44 	mutex_lock(&dibs_dev_list.mutex);
45 	mutex_lock(&clients_lock);
46 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i) {
47 		if (!clients[i]) {
48 			clients[i] = client;
49 			client->id = i;
50 			if (i == max_client)
51 				max_client++;
52 			rc = 0;
53 			break;
54 		}
55 	}
56 	mutex_unlock(&clients_lock);
57 
58 	if (i < MAX_DIBS_CLIENTS) {
59 		/* initialize with all devices that we got so far */
60 		list_for_each_entry(dibs, &dibs_dev_list.list, list) {
61 			dibs->priv[i] = NULL;
62 			client->ops->add_dev(dibs);
63 		}
64 	}
65 	mutex_unlock(&dibs_dev_list.mutex);
66 
67 	return rc;
68 }
69 EXPORT_SYMBOL_GPL(dibs_register_client);
70 
71 int dibs_unregister_client(struct dibs_client *client)
72 {
73 	struct dibs_dev *dibs;
74 	int rc = 0;
75 
76 	mutex_lock(&dibs_dev_list.mutex);
77 	list_for_each_entry(dibs, &dibs_dev_list.list, list) {
78 		clients[client->id]->ops->del_dev(dibs);
79 		dibs->priv[client->id] = NULL;
80 	}
81 
82 	mutex_lock(&clients_lock);
83 	clients[client->id] = NULL;
84 	if (client->id + 1 == max_client)
85 		max_client--;
86 	mutex_unlock(&clients_lock);
87 
88 	mutex_unlock(&dibs_dev_list.mutex);
89 	return rc;
90 }
91 EXPORT_SYMBOL_GPL(dibs_unregister_client);
92 
93 static void dibs_dev_release(struct device *dev)
94 {
95 	struct dibs_dev *dibs;
96 
97 	dibs = container_of(dev, struct dibs_dev, dev);
98 
99 	kfree(dibs);
100 }
101 
102 struct dibs_dev *dibs_dev_alloc(void)
103 {
104 	struct dibs_dev *dibs;
105 
106 	dibs = kzalloc(sizeof(*dibs), GFP_KERNEL);
107 	if (!dibs)
108 		return dibs;
109 	dibs->dev.release = dibs_dev_release;
110 	dibs->dev.class = dibs_class;
111 	device_initialize(&dibs->dev);
112 
113 	return dibs;
114 }
115 EXPORT_SYMBOL_GPL(dibs_dev_alloc);
116 
117 static ssize_t gid_show(struct device *dev, struct device_attribute *attr,
118 			char *buf)
119 {
120 	struct dibs_dev *dibs;
121 
122 	dibs = container_of(dev, struct dibs_dev, dev);
123 
124 	return sysfs_emit(buf, "%pUb\n", &dibs->gid);
125 }
126 static DEVICE_ATTR_RO(gid);
127 
128 static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr,
129 			      char *buf)
130 {
131 	struct dibs_dev *dibs;
132 	u16 fabric_id;
133 
134 	dibs = container_of(dev, struct dibs_dev, dev);
135 	fabric_id = dibs->ops->get_fabric_id(dibs);
136 
137 	return sysfs_emit(buf, "0x%04x\n", fabric_id);
138 }
139 static DEVICE_ATTR_RO(fabric_id);
140 
141 static struct attribute *dibs_dev_attrs[] = {
142 	&dev_attr_gid.attr,
143 	&dev_attr_fabric_id.attr,
144 	NULL,
145 };
146 
147 static const struct attribute_group dibs_dev_attr_group = {
148 	.attrs = dibs_dev_attrs,
149 };
150 
151 int dibs_dev_add(struct dibs_dev *dibs)
152 {
153 	int i, ret;
154 
155 	ret = device_add(&dibs->dev);
156 	if (ret)
157 		return ret;
158 
159 	ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
160 	if (ret) {
161 		dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
162 		goto err_device_del;
163 	}
164 	mutex_lock(&dibs_dev_list.mutex);
165 	mutex_lock(&clients_lock);
166 	for (i = 0; i < max_client; ++i) {
167 		if (clients[i])
168 			clients[i]->ops->add_dev(dibs);
169 	}
170 	mutex_unlock(&clients_lock);
171 	list_add(&dibs->list, &dibs_dev_list.list);
172 	mutex_unlock(&dibs_dev_list.mutex);
173 
174 	return 0;
175 
176 err_device_del:
177 	device_del(&dibs->dev);
178 	return ret;
179 
180 }
181 EXPORT_SYMBOL_GPL(dibs_dev_add);
182 
183 void dibs_dev_del(struct dibs_dev *dibs)
184 {
185 	int i;
186 
187 	mutex_lock(&dibs_dev_list.mutex);
188 	mutex_lock(&clients_lock);
189 	for (i = 0; i < max_client; ++i) {
190 		if (clients[i])
191 			clients[i]->ops->del_dev(dibs);
192 	}
193 	mutex_unlock(&clients_lock);
194 	list_del_init(&dibs->list);
195 	mutex_unlock(&dibs_dev_list.mutex);
196 
197 	device_del(&dibs->dev);
198 }
199 EXPORT_SYMBOL_GPL(dibs_dev_del);
200 
201 static int __init dibs_init(void)
202 {
203 	int rc;
204 
205 	memset(clients, 0, sizeof(clients));
206 	max_client = 0;
207 
208 	dibs_class = class_create("dibs");
209 	if (IS_ERR(&dibs_class))
210 		return PTR_ERR(&dibs_class);
211 
212 	rc = dibs_loopback_init();
213 	if (rc)
214 		pr_err("%s fails with %d\n", __func__, rc);
215 
216 	return rc;
217 }
218 
219 static void __exit dibs_exit(void)
220 {
221 	dibs_loopback_exit();
222 	class_destroy(dibs_class);
223 }
224 
225 module_init(dibs_init);
226 module_exit(dibs_exit);
227