Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dibs/dibs_main.c
29265 views
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
280