mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	 231889d9b6
			
		
	
	
		231889d9b6
		
	
	
	
	
		
			
			There is a typo in this code.  It should check "dibs_class" instead of
"&dibs_class".  Remove the &.
Fixes: 8047373498 ("dibs: Create class dibs")
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Link: https://patch.msgid.link/aNP-XcrjSUjZAu4a@stanley.mountain
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
		
	
			
		
			
				
	
	
		
			278 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  *  DIBS - Direct Internal Buffer Sharing
 | |
|  *
 | |
|  *  Implementation of the DIBS class module
 | |
|  *
 | |
|  *  Copyright IBM Corp. 2025
 | |
|  */
 | |
| #define KMSG_COMPONENT "dibs"
 | |
| #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/dibs.h>
 | |
| 
 | |
| #include "dibs_loopback.h"
 | |
| 
 | |
| MODULE_DESCRIPTION("Direct Internal Buffer Sharing class");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 | |
| static struct class *dibs_class;
 | |
| 
 | |
| /* use an array rather a list for fast mapping: */
 | |
| static struct dibs_client *clients[MAX_DIBS_CLIENTS];
 | |
| static u8 max_client;
 | |
| static DEFINE_MUTEX(clients_lock);
 | |
| struct dibs_dev_list {
 | |
| 	struct list_head list;
 | |
| 	struct mutex mutex; /* protects dibs device list */
 | |
| };
 | |
| 
 | |
| static struct dibs_dev_list dibs_dev_list = {
 | |
| 	.list = LIST_HEAD_INIT(dibs_dev_list.list),
 | |
| 	.mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex),
 | |
| };
 | |
| 
 | |
| static void dibs_setup_forwarding(struct dibs_client *client,
 | |
| 				  struct dibs_dev *dibs)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&dibs->lock, flags);
 | |
| 	dibs->subs[client->id] = client;
 | |
| 	spin_unlock_irqrestore(&dibs->lock, flags);
 | |
| }
 | |
| 
 | |
| int dibs_register_client(struct dibs_client *client)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 	int i, rc = -ENOSPC;
 | |
| 
 | |
| 	mutex_lock(&dibs_dev_list.mutex);
 | |
| 	mutex_lock(&clients_lock);
 | |
| 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i) {
 | |
| 		if (!clients[i]) {
 | |
| 			clients[i] = client;
 | |
| 			client->id = i;
 | |
| 			if (i == max_client)
 | |
| 				max_client++;
 | |
| 			rc = 0;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&clients_lock);
 | |
| 
 | |
| 	if (i < MAX_DIBS_CLIENTS) {
 | |
| 		/* initialize with all devices that we got so far */
 | |
| 		list_for_each_entry(dibs, &dibs_dev_list.list, list) {
 | |
| 			dibs->priv[i] = NULL;
 | |
| 			client->ops->add_dev(dibs);
 | |
| 			dibs_setup_forwarding(client, dibs);
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&dibs_dev_list.mutex);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(dibs_register_client);
 | |
| 
 | |
| int dibs_unregister_client(struct dibs_client *client)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 	unsigned long flags;
 | |
| 	int max_dmbs;
 | |
| 	int rc = 0;
 | |
| 
 | |
| 	mutex_lock(&dibs_dev_list.mutex);
 | |
| 	list_for_each_entry(dibs, &dibs_dev_list.list, list) {
 | |
| 		spin_lock_irqsave(&dibs->lock, flags);
 | |
| 		max_dmbs = dibs->ops->max_dmbs();
 | |
| 		for (int i = 0; i < max_dmbs; ++i) {
 | |
| 			if (dibs->dmb_clientid_arr[i] == client->id) {
 | |
| 				WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n",
 | |
| 				     __func__, client->name);
 | |
| 				rc = -EBUSY;
 | |
| 				goto err_reg_dmb;
 | |
| 			}
 | |
| 		}
 | |
| 		/* Stop forwarding IRQs and events */
 | |
| 		dibs->subs[client->id] = NULL;
 | |
| 		spin_unlock_irqrestore(&dibs->lock, flags);
 | |
| 		clients[client->id]->ops->del_dev(dibs);
 | |
| 		dibs->priv[client->id] = NULL;
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&clients_lock);
 | |
| 	clients[client->id] = NULL;
 | |
| 	if (client->id + 1 == max_client)
 | |
| 		max_client--;
 | |
| 	mutex_unlock(&clients_lock);
 | |
| 
 | |
| 	mutex_unlock(&dibs_dev_list.mutex);
 | |
| 	return rc;
 | |
| 
 | |
| err_reg_dmb:
 | |
| 	spin_unlock_irqrestore(&dibs->lock, flags);
 | |
| 	mutex_unlock(&dibs_dev_list.mutex);
 | |
| 	return rc;
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(dibs_unregister_client);
 | |
| 
 | |
| static void dibs_dev_release(struct device *dev)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 
 | |
| 	dibs = container_of(dev, struct dibs_dev, dev);
 | |
| 
 | |
| 	kfree(dibs);
 | |
| }
 | |
| 
 | |
| struct dibs_dev *dibs_dev_alloc(void)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 
 | |
| 	dibs = kzalloc(sizeof(*dibs), GFP_KERNEL);
 | |
| 	if (!dibs)
 | |
| 		return dibs;
 | |
| 	dibs->dev.release = dibs_dev_release;
 | |
| 	dibs->dev.class = dibs_class;
 | |
| 	device_initialize(&dibs->dev);
 | |
| 
 | |
| 	return dibs;
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(dibs_dev_alloc);
 | |
| 
 | |
| static ssize_t gid_show(struct device *dev, struct device_attribute *attr,
 | |
| 			char *buf)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 
 | |
| 	dibs = container_of(dev, struct dibs_dev, dev);
 | |
| 
 | |
| 	return sysfs_emit(buf, "%pUb\n", &dibs->gid);
 | |
| }
 | |
| static DEVICE_ATTR_RO(gid);
 | |
| 
 | |
| static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr,
 | |
| 			      char *buf)
 | |
| {
 | |
| 	struct dibs_dev *dibs;
 | |
| 	u16 fabric_id;
 | |
| 
 | |
| 	dibs = container_of(dev, struct dibs_dev, dev);
 | |
| 	fabric_id = dibs->ops->get_fabric_id(dibs);
 | |
| 
 | |
| 	return sysfs_emit(buf, "0x%04x\n", fabric_id);
 | |
| }
 | |
| static DEVICE_ATTR_RO(fabric_id);
 | |
| 
 | |
| static struct attribute *dibs_dev_attrs[] = {
 | |
| 	&dev_attr_gid.attr,
 | |
| 	&dev_attr_fabric_id.attr,
 | |
| 	NULL,
 | |
| };
 | |
| 
 | |
| static const struct attribute_group dibs_dev_attr_group = {
 | |
| 	.attrs = dibs_dev_attrs,
 | |
| };
 | |
| 
 | |
| int dibs_dev_add(struct dibs_dev *dibs)
 | |
| {
 | |
| 	int max_dmbs;
 | |
| 	int i, ret;
 | |
| 
 | |
| 	max_dmbs = dibs->ops->max_dmbs();
 | |
| 	spin_lock_init(&dibs->lock);
 | |
| 	dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
 | |
| 	if (!dibs->dmb_clientid_arr)
 | |
| 		return -ENOMEM;
 | |
| 	memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs);
 | |
| 
 | |
| 	ret = device_add(&dibs->dev);
 | |
| 	if (ret)
 | |
| 		goto free_client_arr;
 | |
| 
 | |
| 	ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
 | |
| 	if (ret) {
 | |
| 		dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n");
 | |
| 		goto err_device_del;
 | |
| 	}
 | |
| 	mutex_lock(&dibs_dev_list.mutex);
 | |
| 	mutex_lock(&clients_lock);
 | |
| 	for (i = 0; i < max_client; ++i) {
 | |
| 		if (clients[i]) {
 | |
| 			clients[i]->ops->add_dev(dibs);
 | |
| 			dibs_setup_forwarding(clients[i], dibs);
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&clients_lock);
 | |
| 	list_add(&dibs->list, &dibs_dev_list.list);
 | |
| 	mutex_unlock(&dibs_dev_list.mutex);
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_device_del:
 | |
| 	device_del(&dibs->dev);
 | |
| free_client_arr:
 | |
| 	kfree(dibs->dmb_clientid_arr);
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(dibs_dev_add);
 | |
| 
 | |
| void dibs_dev_del(struct dibs_dev *dibs)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	int i;
 | |
| 
 | |
| 	sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group);
 | |
| 
 | |
| 	spin_lock_irqsave(&dibs->lock, flags);
 | |
| 	for (i = 0; i < MAX_DIBS_CLIENTS; ++i)
 | |
| 		dibs->subs[i] = NULL;
 | |
| 	spin_unlock_irqrestore(&dibs->lock, flags);
 | |
| 
 | |
| 	mutex_lock(&dibs_dev_list.mutex);
 | |
| 	mutex_lock(&clients_lock);
 | |
| 	for (i = 0; i < max_client; ++i) {
 | |
| 		if (clients[i])
 | |
| 			clients[i]->ops->del_dev(dibs);
 | |
| 	}
 | |
| 	mutex_unlock(&clients_lock);
 | |
| 	list_del_init(&dibs->list);
 | |
| 	mutex_unlock(&dibs_dev_list.mutex);
 | |
| 
 | |
| 	device_del(&dibs->dev);
 | |
| 	kfree(dibs->dmb_clientid_arr);
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(dibs_dev_del);
 | |
| 
 | |
| static int __init dibs_init(void)
 | |
| {
 | |
| 	int rc;
 | |
| 
 | |
| 	memset(clients, 0, sizeof(clients));
 | |
| 	max_client = 0;
 | |
| 
 | |
| 	dibs_class = class_create("dibs");
 | |
| 	if (IS_ERR(dibs_class))
 | |
| 		return PTR_ERR(dibs_class);
 | |
| 
 | |
| 	rc = dibs_loopback_init();
 | |
| 	if (rc)
 | |
| 		pr_err("%s fails with %d\n", __func__, rc);
 | |
| 
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| static void __exit dibs_exit(void)
 | |
| {
 | |
| 	dibs_loopback_exit();
 | |
| 	class_destroy(dibs_class);
 | |
| }
 | |
| 
 | |
| module_init(dibs_init);
 | |
| module_exit(dibs_exit);
 |