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