xref: /linux/drivers/dibs/dibs_main.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
135758b00SAlexandra Winter // SPDX-License-Identifier: GPL-2.0
235758b00SAlexandra Winter /*
335758b00SAlexandra Winter  *  DIBS - Direct Internal Buffer Sharing
435758b00SAlexandra Winter  *
535758b00SAlexandra Winter  *  Implementation of the DIBS class module
635758b00SAlexandra Winter  *
735758b00SAlexandra Winter  *  Copyright IBM Corp. 2025
835758b00SAlexandra Winter  */
935758b00SAlexandra Winter #define KMSG_COMPONENT "dibs"
1035758b00SAlexandra Winter #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1135758b00SAlexandra Winter 
1235758b00SAlexandra Winter #include <linux/module.h>
1335758b00SAlexandra Winter #include <linux/types.h>
1426972696SAlexandra Winter #include <linux/slab.h>
1535758b00SAlexandra Winter #include <linux/err.h>
1635758b00SAlexandra Winter #include <linux/dibs.h>
1735758b00SAlexandra Winter 
18cb990a45SAlexandra Winter #include "dibs_loopback.h"
19cb990a45SAlexandra Winter 
2035758b00SAlexandra Winter MODULE_DESCRIPTION("Direct Internal Buffer Sharing class");
2135758b00SAlexandra Winter MODULE_LICENSE("GPL");
2235758b00SAlexandra Winter 
2380473734SJulian Ruess static struct class *dibs_class;
2480473734SJulian Ruess 
2535758b00SAlexandra Winter /* use an array rather a list for fast mapping: */
2635758b00SAlexandra Winter static struct dibs_client *clients[MAX_DIBS_CLIENTS];
2735758b00SAlexandra Winter static u8 max_client;
28d324a2caSAlexandra Winter static DEFINE_MUTEX(clients_lock);
2926972696SAlexandra Winter struct dibs_dev_list {
3026972696SAlexandra Winter 	struct list_head list;
3126972696SAlexandra Winter 	struct mutex mutex; /* protects dibs device list */
3226972696SAlexandra Winter };
3326972696SAlexandra Winter 
3426972696SAlexandra Winter static struct dibs_dev_list dibs_dev_list = {
3526972696SAlexandra Winter 	.list = LIST_HEAD_INIT(dibs_dev_list.list),
3626972696SAlexandra Winter 	.mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex),
3726972696SAlexandra Winter };
38d324a2caSAlexandra Winter 
39cc21191bSAlexandra Winter static void dibs_setup_forwarding(struct dibs_client *client,
40cc21191bSAlexandra Winter 				  struct dibs_dev *dibs)
41cc21191bSAlexandra Winter {
42cc21191bSAlexandra Winter 	unsigned long flags;
43cc21191bSAlexandra Winter 
44cc21191bSAlexandra Winter 	spin_lock_irqsave(&dibs->lock, flags);
45cc21191bSAlexandra Winter 	dibs->subs[client->id] = client;
46cc21191bSAlexandra Winter 	spin_unlock_irqrestore(&dibs->lock, flags);
47cc21191bSAlexandra Winter }
48cc21191bSAlexandra Winter 
49d324a2caSAlexandra Winter int dibs_register_client(struct dibs_client *client)
50d324a2caSAlexandra Winter {
5169baaac9SAlexandra Winter 	struct dibs_dev *dibs;
52d324a2caSAlexandra Winter 	int i, rc = -ENOSPC;
53d324a2caSAlexandra Winter 
5469baaac9SAlexandra Winter 	mutex_lock(&dibs_dev_list.mutex);
55d324a2caSAlexandra Winter 	mutex_lock(&clients_lock);
56d324a2caSAlexandra Winter 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i) {
57d324a2caSAlexandra Winter 		if (!clients[i]) {
58d324a2caSAlexandra Winter 			clients[i] = client;
59d324a2caSAlexandra Winter 			client->id = i;
60d324a2caSAlexandra Winter 			if (i == max_client)
61d324a2caSAlexandra Winter 				max_client++;
62d324a2caSAlexandra Winter 			rc = 0;
63d324a2caSAlexandra Winter 			break;
64d324a2caSAlexandra Winter 		}
65d324a2caSAlexandra Winter 	}
66d324a2caSAlexandra Winter 	mutex_unlock(&clients_lock);
67d324a2caSAlexandra Winter 
6869baaac9SAlexandra Winter 	if (i < MAX_DIBS_CLIENTS) {
6969baaac9SAlexandra Winter 		/* initialize with all devices that we got so far */
7069baaac9SAlexandra Winter 		list_for_each_entry(dibs, &dibs_dev_list.list, list) {
7169baaac9SAlexandra Winter 			dibs->priv[i] = NULL;
7269baaac9SAlexandra Winter 			client->ops->add_dev(dibs);
73cc21191bSAlexandra Winter 			dibs_setup_forwarding(client, dibs);
7469baaac9SAlexandra Winter 		}
7569baaac9SAlexandra Winter 	}
7669baaac9SAlexandra Winter 	mutex_unlock(&dibs_dev_list.mutex);
7769baaac9SAlexandra Winter 
78d324a2caSAlexandra Winter 	return rc;
79d324a2caSAlexandra Winter }
80d324a2caSAlexandra Winter EXPORT_SYMBOL_GPL(dibs_register_client);
81d324a2caSAlexandra Winter 
82d324a2caSAlexandra Winter int dibs_unregister_client(struct dibs_client *client)
83d324a2caSAlexandra Winter {
8469baaac9SAlexandra Winter 	struct dibs_dev *dibs;
85cc21191bSAlexandra Winter 	unsigned long flags;
86cc21191bSAlexandra Winter 	int max_dmbs;
87d324a2caSAlexandra Winter 	int rc = 0;
88d324a2caSAlexandra Winter 
8969baaac9SAlexandra Winter 	mutex_lock(&dibs_dev_list.mutex);
9069baaac9SAlexandra Winter 	list_for_each_entry(dibs, &dibs_dev_list.list, list) {
91cc21191bSAlexandra Winter 		spin_lock_irqsave(&dibs->lock, flags);
92cc21191bSAlexandra Winter 		max_dmbs = dibs->ops->max_dmbs();
93cc21191bSAlexandra Winter 		for (int i = 0; i < max_dmbs; ++i) {
94cc21191bSAlexandra Winter 			if (dibs->dmb_clientid_arr[i] == client->id) {
95cc21191bSAlexandra Winter 				WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n",
96cc21191bSAlexandra Winter 				     __func__, client->name);
97cc21191bSAlexandra Winter 				rc = -EBUSY;
98cc21191bSAlexandra Winter 				goto err_reg_dmb;
99cc21191bSAlexandra Winter 			}
100cc21191bSAlexandra Winter 		}
101a612dbe8SJulian Ruess 		/* Stop forwarding IRQs and events */
102cc21191bSAlexandra Winter 		dibs->subs[client->id] = NULL;
103cc21191bSAlexandra Winter 		spin_unlock_irqrestore(&dibs->lock, flags);
10469baaac9SAlexandra Winter 		clients[client->id]->ops->del_dev(dibs);
10569baaac9SAlexandra Winter 		dibs->priv[client->id] = NULL;
10669baaac9SAlexandra Winter 	}
10769baaac9SAlexandra Winter 
108d324a2caSAlexandra Winter 	mutex_lock(&clients_lock);
109d324a2caSAlexandra Winter 	clients[client->id] = NULL;
110d324a2caSAlexandra Winter 	if (client->id + 1 == max_client)
111d324a2caSAlexandra Winter 		max_client--;
112d324a2caSAlexandra Winter 	mutex_unlock(&clients_lock);
11369baaac9SAlexandra Winter 
11469baaac9SAlexandra Winter 	mutex_unlock(&dibs_dev_list.mutex);
115d324a2caSAlexandra Winter 	return rc;
116cc21191bSAlexandra Winter 
117cc21191bSAlexandra Winter err_reg_dmb:
118cc21191bSAlexandra Winter 	spin_unlock_irqrestore(&dibs->lock, flags);
119cc21191bSAlexandra Winter 	mutex_unlock(&dibs_dev_list.mutex);
120cc21191bSAlexandra Winter 	return rc;
121d324a2caSAlexandra Winter }
122d324a2caSAlexandra Winter EXPORT_SYMBOL_GPL(dibs_unregister_client);
12335758b00SAlexandra Winter 
124845c334aSJulian Ruess static void dibs_dev_release(struct device *dev)
125845c334aSJulian Ruess {
126845c334aSJulian Ruess 	struct dibs_dev *dibs;
127845c334aSJulian Ruess 
128845c334aSJulian Ruess 	dibs = container_of(dev, struct dibs_dev, dev);
129845c334aSJulian Ruess 
130845c334aSJulian Ruess 	kfree(dibs);
131845c334aSJulian Ruess }
132845c334aSJulian Ruess 
13326972696SAlexandra Winter struct dibs_dev *dibs_dev_alloc(void)
13426972696SAlexandra Winter {
13526972696SAlexandra Winter 	struct dibs_dev *dibs;
13626972696SAlexandra Winter 
13726972696SAlexandra Winter 	dibs = kzalloc(sizeof(*dibs), GFP_KERNEL);
138845c334aSJulian Ruess 	if (!dibs)
139845c334aSJulian Ruess 		return dibs;
140845c334aSJulian Ruess 	dibs->dev.release = dibs_dev_release;
14180473734SJulian Ruess 	dibs->dev.class = dibs_class;
142845c334aSJulian Ruess 	device_initialize(&dibs->dev);
14326972696SAlexandra Winter 
14426972696SAlexandra Winter 	return dibs;
14526972696SAlexandra Winter }
14626972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_alloc);
14726972696SAlexandra Winter 
14805e68d8dSAlexandra Winter static ssize_t gid_show(struct device *dev, struct device_attribute *attr,
14905e68d8dSAlexandra Winter 			char *buf)
15005e68d8dSAlexandra Winter {
15105e68d8dSAlexandra Winter 	struct dibs_dev *dibs;
15205e68d8dSAlexandra Winter 
15305e68d8dSAlexandra Winter 	dibs = container_of(dev, struct dibs_dev, dev);
15405e68d8dSAlexandra Winter 
15505e68d8dSAlexandra Winter 	return sysfs_emit(buf, "%pUb\n", &dibs->gid);
15605e68d8dSAlexandra Winter }
15705e68d8dSAlexandra Winter static DEVICE_ATTR_RO(gid);
15805e68d8dSAlexandra Winter 
15980473734SJulian Ruess static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr,
16080473734SJulian Ruess 			      char *buf)
16180473734SJulian Ruess {
16280473734SJulian Ruess 	struct dibs_dev *dibs;
16380473734SJulian Ruess 	u16 fabric_id;
16480473734SJulian Ruess 
16580473734SJulian Ruess 	dibs = container_of(dev, struct dibs_dev, dev);
16680473734SJulian Ruess 	fabric_id = dibs->ops->get_fabric_id(dibs);
16780473734SJulian Ruess 
16880473734SJulian Ruess 	return sysfs_emit(buf, "0x%04x\n", fabric_id);
16980473734SJulian Ruess }
17080473734SJulian Ruess static DEVICE_ATTR_RO(fabric_id);
17180473734SJulian Ruess 
17280473734SJulian Ruess static struct attribute *dibs_dev_attrs[] = {
17305e68d8dSAlexandra Winter 	&dev_attr_gid.attr,
17480473734SJulian Ruess 	&dev_attr_fabric_id.attr,
17580473734SJulian Ruess 	NULL,
17680473734SJulian Ruess };
17780473734SJulian Ruess 
17880473734SJulian Ruess static const struct attribute_group dibs_dev_attr_group = {
17980473734SJulian Ruess 	.attrs = dibs_dev_attrs,
18080473734SJulian Ruess };
18180473734SJulian Ruess 
18226972696SAlexandra Winter int dibs_dev_add(struct dibs_dev *dibs)
18326972696SAlexandra Winter {
184cc21191bSAlexandra Winter 	int max_dmbs;
185845c334aSJulian Ruess 	int i, ret;
186845c334aSJulian Ruess 
187cc21191bSAlexandra Winter 	max_dmbs = dibs->ops->max_dmbs();
188cc21191bSAlexandra Winter 	spin_lock_init(&dibs->lock);
189cc21191bSAlexandra Winter 	dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
190cc21191bSAlexandra Winter 	if (!dibs->dmb_clientid_arr)
191cc21191bSAlexandra Winter 		return -ENOMEM;
192cc21191bSAlexandra Winter 	memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs);
193cc21191bSAlexandra Winter 
194845c334aSJulian Ruess 	ret = device_add(&dibs->dev);
195845c334aSJulian Ruess 	if (ret)
196cc21191bSAlexandra Winter 		goto free_client_arr;
19769baaac9SAlexandra Winter 
19880473734SJulian Ruess 	ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
19980473734SJulian Ruess 	if (ret) {
20080473734SJulian Ruess 		dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
20180473734SJulian Ruess 		goto err_device_del;
20280473734SJulian Ruess 	}
20326972696SAlexandra Winter 	mutex_lock(&dibs_dev_list.mutex);
20469baaac9SAlexandra Winter 	mutex_lock(&clients_lock);
20569baaac9SAlexandra Winter 	for (i = 0; i < max_client; ++i) {
206cc21191bSAlexandra Winter 		if (clients[i]) {
20769baaac9SAlexandra Winter 			clients[i]->ops->add_dev(dibs);
208cc21191bSAlexandra Winter 			dibs_setup_forwarding(clients[i], dibs);
209cc21191bSAlexandra Winter 		}
21069baaac9SAlexandra Winter 	}
21169baaac9SAlexandra Winter 	mutex_unlock(&clients_lock);
21226972696SAlexandra Winter 	list_add(&dibs->list, &dibs_dev_list.list);
21326972696SAlexandra Winter 	mutex_unlock(&dibs_dev_list.mutex);
21426972696SAlexandra Winter 
21526972696SAlexandra Winter 	return 0;
21680473734SJulian Ruess 
21780473734SJulian Ruess err_device_del:
21880473734SJulian Ruess 	device_del(&dibs->dev);
219cc21191bSAlexandra Winter free_client_arr:
220cc21191bSAlexandra Winter 	kfree(dibs->dmb_clientid_arr);
22180473734SJulian Ruess 	return ret;
22280473734SJulian Ruess 
22326972696SAlexandra Winter }
22426972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_add);
22526972696SAlexandra Winter 
22626972696SAlexandra Winter void dibs_dev_del(struct dibs_dev *dibs)
22726972696SAlexandra Winter {
228cc21191bSAlexandra Winter 	unsigned long flags;
22969baaac9SAlexandra Winter 	int i;
23069baaac9SAlexandra Winter 
231cc21191bSAlexandra Winter 	sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group);
232cc21191bSAlexandra Winter 
233cc21191bSAlexandra Winter 	spin_lock_irqsave(&dibs->lock, flags);
234cc21191bSAlexandra Winter 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i)
235cc21191bSAlexandra Winter 		dibs->subs[i] = NULL;
236cc21191bSAlexandra Winter 	spin_unlock_irqrestore(&dibs->lock, flags);
237cc21191bSAlexandra Winter 
23826972696SAlexandra Winter 	mutex_lock(&dibs_dev_list.mutex);
23969baaac9SAlexandra Winter 	mutex_lock(&clients_lock);
24069baaac9SAlexandra Winter 	for (i = 0; i < max_client; ++i) {
24169baaac9SAlexandra Winter 		if (clients[i])
24269baaac9SAlexandra Winter 			clients[i]->ops->del_dev(dibs);
24369baaac9SAlexandra Winter 	}
24469baaac9SAlexandra Winter 	mutex_unlock(&clients_lock);
24526972696SAlexandra Winter 	list_del_init(&dibs->list);
24626972696SAlexandra Winter 	mutex_unlock(&dibs_dev_list.mutex);
247845c334aSJulian Ruess 
248845c334aSJulian Ruess 	device_del(&dibs->dev);
249cc21191bSAlexandra Winter 	kfree(dibs->dmb_clientid_arr);
25026972696SAlexandra Winter }
25126972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_del);
25226972696SAlexandra Winter 
25335758b00SAlexandra Winter static int __init dibs_init(void)
25435758b00SAlexandra Winter {
255cb990a45SAlexandra Winter 	int rc;
256cb990a45SAlexandra Winter 
25735758b00SAlexandra Winter 	memset(clients, 0, sizeof(clients));
25835758b00SAlexandra Winter 	max_client = 0;
25935758b00SAlexandra Winter 
26080473734SJulian Ruess 	dibs_class = class_create("dibs");
261*231889d9SDan Carpenter 	if (IS_ERR(dibs_class))
262*231889d9SDan Carpenter 		return PTR_ERR(dibs_class);
26380473734SJulian Ruess 
264cb990a45SAlexandra Winter 	rc = dibs_loopback_init();
265cb990a45SAlexandra Winter 	if (rc)
266cb990a45SAlexandra Winter 		pr_err("%s fails with %d\n", __func__, rc);
267cb990a45SAlexandra Winter 
268cb990a45SAlexandra Winter 	return rc;
26935758b00SAlexandra Winter }
27035758b00SAlexandra Winter 
27135758b00SAlexandra Winter static void __exit dibs_exit(void)
27235758b00SAlexandra Winter {
273cb990a45SAlexandra Winter 	dibs_loopback_exit();
27480473734SJulian Ruess 	class_destroy(dibs_class);
27535758b00SAlexandra Winter }
27635758b00SAlexandra Winter 
27735758b00SAlexandra Winter module_init(dibs_init);
27835758b00SAlexandra Winter module_exit(dibs_exit);
279