Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
19MODULE_DESCRIPTION("Direct Internal Buffer Sharing class");
20MODULE_LICENSE("GPL");
21
22static const struct class dibs_class = {
23 .name = "dibs",
24};
25
26/* use an array rather a list for fast mapping: */
27static struct dibs_client *clients[MAX_DIBS_CLIENTS];
28static u8 max_client;
29static DEFINE_MUTEX(clients_lock);
30struct dibs_dev_list {
31 struct list_head list;
32 struct mutex mutex; /* protects dibs device list */
33};
34
35static 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
40static 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
50int 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}
81EXPORT_SYMBOL_GPL(dibs_register_client);
82
83int 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
118err_reg_dmb:
119 spin_unlock_irqrestore(&dibs->lock, flags);
120 mutex_unlock(&dibs_dev_list.mutex);
121 return rc;
122}
123EXPORT_SYMBOL_GPL(dibs_unregister_client);
124
125static 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
134struct 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}
147EXPORT_SYMBOL_GPL(dibs_dev_alloc);
148
149static 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}
158static DEVICE_ATTR_RO(gid);
159
160static 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}
171static DEVICE_ATTR_RO(fabric_id);
172
173static struct attribute *dibs_dev_attrs[] = {
174 &dev_attr_gid.attr,
175 &dev_attr_fabric_id.attr,
176 NULL,
177};
178
179static const struct attribute_group dibs_dev_attr_group = {
180 .attrs = dibs_dev_attrs,
181};
182
183int 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
218err_device_del:
219 device_del(&dibs->dev);
220free_client_arr:
221 kfree(dibs->dmb_clientid_arr);
222 return ret;
223
224}
225EXPORT_SYMBOL_GPL(dibs_dev_add);
226
227void 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}
252EXPORT_SYMBOL_GPL(dibs_dev_del);
253
254static 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
269static void __exit dibs_exit(void)
270{
271 dibs_loopback_exit();
272 class_unregister(&dibs_class);
273}
274
275subsys_initcall(dibs_init);
276module_exit(dibs_exit);