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