File Index
Files and directories
Sign in
FILE

dibs_main.c

File preview with syntax highlighting.

linux-6.19.14/drivers/dibs/dibs_main.c C
  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 struct class *dibs_class;
 23
 24/* use an array rather a list for fast mapping: */
 25static struct dibs_client *clients[MAX_DIBS_CLIENTS];
 26static u8 max_client;
 27static DEFINE_MUTEX(clients_lock);
 28struct dibs_dev_list {
 29	struct list_head list;
 30	struct mutex mutex; /* protects dibs device list */
 31};
 32
 33static 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
 38static 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
 48int 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}
 79EXPORT_SYMBOL_GPL(dibs_register_client);
 80
 81int 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
116err_reg_dmb:
117	spin_unlock_irqrestore(&dibs->lock, flags);
118	mutex_unlock(&dibs_dev_list.mutex);
119	return rc;
120}
121EXPORT_SYMBOL_GPL(dibs_unregister_client);
122
123static 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
132struct 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}
145EXPORT_SYMBOL_GPL(dibs_dev_alloc);
146
147static 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}
156static DEVICE_ATTR_RO(gid);
157
158static 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}
169static DEVICE_ATTR_RO(fabric_id);
170
171static struct attribute *dibs_dev_attrs[] = {
172	&dev_attr_gid.attr,
173	&dev_attr_fabric_id.attr,
174	NULL,
175};
176
177static const struct attribute_group dibs_dev_attr_group = {
178	.attrs = dibs_dev_attrs,
179};
180
181int 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
216err_device_del:
217	device_del(&dibs->dev);
218free_client_arr:
219	kfree(dibs->dmb_clientid_arr);
220	return ret;
221
222}
223EXPORT_SYMBOL_GPL(dibs_dev_add);
224
225void 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}
250EXPORT_SYMBOL_GPL(dibs_dev_del);
251
252static 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
267static void __exit dibs_exit(void)
268{
269	dibs_loopback_exit();
270	class_destroy(dibs_class);
271}
272
273subsys_initcall(dibs_init);
274module_exit(dibs_exit);
~/ Read-only file preview.