Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dibs/dibs_main.c
49767 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 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
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
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
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
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
132
struct dibs_dev *dibs_dev_alloc(void)
133
{
134
struct dibs_dev *dibs;
135
136
dibs = kzalloc(sizeof(*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
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
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
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
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
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
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
276