xref: /linux/drivers/dibs/dibs_main.c (revision 6dfafbd0299a60bfb5d5e277fdf100037c7ded07)
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