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