xref: /linux/drivers/dibs/dibs_main.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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 static void dibs_setup_forwarding(struct dibs_client *client,
40 				  struct dibs_dev *dibs)
41 {
42 	unsigned long flags;
43 
44 	spin_lock_irqsave(&dibs->lock, flags);
45 	dibs->subs[client->id] = client;
46 	spin_unlock_irqrestore(&dibs->lock, flags);
47 }
48 
49 int dibs_register_client(struct dibs_client *client)
50 {
51 	struct dibs_dev *dibs;
52 	int i, rc = -ENOSPC;
53 
54 	mutex_lock(&dibs_dev_list.mutex);
55 	mutex_lock(&clients_lock);
56 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i) {
57 		if (!clients[i]) {
58 			clients[i] = client;
59 			client->id = i;
60 			if (i == max_client)
61 				max_client++;
62 			rc = 0;
63 			break;
64 		}
65 	}
66 	mutex_unlock(&clients_lock);
67 
68 	if (i < MAX_DIBS_CLIENTS) {
69 		/* initialize with all devices that we got so far */
70 		list_for_each_entry(dibs, &dibs_dev_list.list, list) {
71 			dibs->priv[i] = NULL;
72 			client->ops->add_dev(dibs);
73 			dibs_setup_forwarding(client, dibs);
74 		}
75 	}
76 	mutex_unlock(&dibs_dev_list.mutex);
77 
78 	return rc;
79 }
80 EXPORT_SYMBOL_GPL(dibs_register_client);
81 
82 int dibs_unregister_client(struct dibs_client *client)
83 {
84 	struct dibs_dev *dibs;
85 	unsigned long flags;
86 	int max_dmbs;
87 	int rc = 0;
88 
89 	mutex_lock(&dibs_dev_list.mutex);
90 	list_for_each_entry(dibs, &dibs_dev_list.list, list) {
91 		spin_lock_irqsave(&dibs->lock, flags);
92 		max_dmbs = dibs->ops->max_dmbs();
93 		for (int i = 0; i < max_dmbs; ++i) {
94 			if (dibs->dmb_clientid_arr[i] == client->id) {
95 				WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n",
96 				     __func__, client->name);
97 				rc = -EBUSY;
98 				goto err_reg_dmb;
99 			}
100 		}
101 		/* Stop forwarding IRQs and events */
102 		dibs->subs[client->id] = NULL;
103 		spin_unlock_irqrestore(&dibs->lock, flags);
104 		clients[client->id]->ops->del_dev(dibs);
105 		dibs->priv[client->id] = NULL;
106 	}
107 
108 	mutex_lock(&clients_lock);
109 	clients[client->id] = NULL;
110 	if (client->id + 1 == max_client)
111 		max_client--;
112 	mutex_unlock(&clients_lock);
113 
114 	mutex_unlock(&dibs_dev_list.mutex);
115 	return rc;
116 
117 err_reg_dmb:
118 	spin_unlock_irqrestore(&dibs->lock, flags);
119 	mutex_unlock(&dibs_dev_list.mutex);
120 	return rc;
121 }
122 EXPORT_SYMBOL_GPL(dibs_unregister_client);
123 
124 static void dibs_dev_release(struct device *dev)
125 {
126 	struct dibs_dev *dibs;
127 
128 	dibs = container_of(dev, struct dibs_dev, dev);
129 
130 	kfree(dibs);
131 }
132 
133 struct dibs_dev *dibs_dev_alloc(void)
134 {
135 	struct dibs_dev *dibs;
136 
137 	dibs = kzalloc(sizeof(*dibs), GFP_KERNEL);
138 	if (!dibs)
139 		return dibs;
140 	dibs->dev.release = dibs_dev_release;
141 	dibs->dev.class = dibs_class;
142 	device_initialize(&dibs->dev);
143 
144 	return dibs;
145 }
146 EXPORT_SYMBOL_GPL(dibs_dev_alloc);
147 
148 static ssize_t gid_show(struct device *dev, struct device_attribute *attr,
149 			char *buf)
150 {
151 	struct dibs_dev *dibs;
152 
153 	dibs = container_of(dev, struct dibs_dev, dev);
154 
155 	return sysfs_emit(buf, "%pUb\n", &dibs->gid);
156 }
157 static DEVICE_ATTR_RO(gid);
158 
159 static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr,
160 			      char *buf)
161 {
162 	struct dibs_dev *dibs;
163 	u16 fabric_id;
164 
165 	dibs = container_of(dev, struct dibs_dev, dev);
166 	fabric_id = dibs->ops->get_fabric_id(dibs);
167 
168 	return sysfs_emit(buf, "0x%04x\n", fabric_id);
169 }
170 static DEVICE_ATTR_RO(fabric_id);
171 
172 static struct attribute *dibs_dev_attrs[] = {
173 	&dev_attr_gid.attr,
174 	&dev_attr_fabric_id.attr,
175 	NULL,
176 };
177 
178 static const struct attribute_group dibs_dev_attr_group = {
179 	.attrs = dibs_dev_attrs,
180 };
181 
182 int dibs_dev_add(struct dibs_dev *dibs)
183 {
184 	int max_dmbs;
185 	int i, ret;
186 
187 	max_dmbs = dibs->ops->max_dmbs();
188 	spin_lock_init(&dibs->lock);
189 	dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
190 	if (!dibs->dmb_clientid_arr)
191 		return -ENOMEM;
192 	memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs);
193 
194 	ret = device_add(&dibs->dev);
195 	if (ret)
196 		goto free_client_arr;
197 
198 	ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
199 	if (ret) {
200 		dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
201 		goto err_device_del;
202 	}
203 	mutex_lock(&dibs_dev_list.mutex);
204 	mutex_lock(&clients_lock);
205 	for (i = 0; i < max_client; ++i) {
206 		if (clients[i]) {
207 			clients[i]->ops->add_dev(dibs);
208 			dibs_setup_forwarding(clients[i], dibs);
209 		}
210 	}
211 	mutex_unlock(&clients_lock);
212 	list_add(&dibs->list, &dibs_dev_list.list);
213 	mutex_unlock(&dibs_dev_list.mutex);
214 
215 	return 0;
216 
217 err_device_del:
218 	device_del(&dibs->dev);
219 free_client_arr:
220 	kfree(dibs->dmb_clientid_arr);
221 	return ret;
222 
223 }
224 EXPORT_SYMBOL_GPL(dibs_dev_add);
225 
226 void dibs_dev_del(struct dibs_dev *dibs)
227 {
228 	unsigned long flags;
229 	int i;
230 
231 	sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group);
232 
233 	spin_lock_irqsave(&dibs->lock, flags);
234 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i)
235 		dibs->subs[i] = NULL;
236 	spin_unlock_irqrestore(&dibs->lock, flags);
237 
238 	mutex_lock(&dibs_dev_list.mutex);
239 	mutex_lock(&clients_lock);
240 	for (i = 0; i < max_client; ++i) {
241 		if (clients[i])
242 			clients[i]->ops->del_dev(dibs);
243 	}
244 	mutex_unlock(&clients_lock);
245 	list_del_init(&dibs->list);
246 	mutex_unlock(&dibs_dev_list.mutex);
247 
248 	device_del(&dibs->dev);
249 	kfree(dibs->dmb_clientid_arr);
250 }
251 EXPORT_SYMBOL_GPL(dibs_dev_del);
252 
253 static int __init dibs_init(void)
254 {
255 	int rc;
256 
257 	memset(clients, 0, sizeof(clients));
258 	max_client = 0;
259 
260 	dibs_class = class_create("dibs");
261 	if (IS_ERR(dibs_class))
262 		return PTR_ERR(dibs_class);
263 
264 	rc = dibs_loopback_init();
265 	if (rc)
266 		pr_err("%s fails with %d\n", __func__, rc);
267 
268 	return rc;
269 }
270 
271 static void __exit dibs_exit(void)
272 {
273 	dibs_loopback_exit();
274 	class_destroy(dibs_class);
275 }
276 
277 module_init(dibs_init);
278 module_exit(dibs_exit);
279