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 const struct class dibs_class = { 23 .name = "dibs", 24 }; 25 26 /* use an array rather a list for fast mapping: */ 27 static struct dibs_client *clients[MAX_DIBS_CLIENTS]; 28 static u8 max_client; 29 static DEFINE_MUTEX(clients_lock); 30 struct dibs_dev_list { 31 struct list_head list; 32 struct mutex mutex; /* protects dibs device list */ 33 }; 34 35 static struct dibs_dev_list dibs_dev_list = { 36 .list = LIST_HEAD_INIT(dibs_dev_list.list), 37 .mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex), 38 }; 39 40 static void dibs_setup_forwarding(struct dibs_client *client, 41 struct dibs_dev *dibs) 42 { 43 unsigned long flags; 44 45 spin_lock_irqsave(&dibs->lock, flags); 46 dibs->subs[client->id] = client; 47 spin_unlock_irqrestore(&dibs->lock, flags); 48 } 49 50 int dibs_register_client(struct dibs_client *client) 51 { 52 struct dibs_dev *dibs; 53 int i, rc = -ENOSPC; 54 55 mutex_lock(&dibs_dev_list.mutex); 56 mutex_lock(&clients_lock); 57 for (i = 0; i < MAX_DIBS_CLIENTS; ++i) { 58 if (!clients[i]) { 59 clients[i] = client; 60 client->id = i; 61 if (i == max_client) 62 max_client++; 63 rc = 0; 64 break; 65 } 66 } 67 mutex_unlock(&clients_lock); 68 69 if (i < MAX_DIBS_CLIENTS) { 70 /* initialize with all devices that we got so far */ 71 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 72 dibs->priv[i] = NULL; 73 client->ops->add_dev(dibs); 74 dibs_setup_forwarding(client, dibs); 75 } 76 } 77 mutex_unlock(&dibs_dev_list.mutex); 78 79 return rc; 80 } 81 EXPORT_SYMBOL_GPL(dibs_register_client); 82 83 int dibs_unregister_client(struct dibs_client *client) 84 { 85 struct dibs_dev *dibs; 86 unsigned long flags; 87 int max_dmbs; 88 int rc = 0; 89 90 mutex_lock(&dibs_dev_list.mutex); 91 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 92 spin_lock_irqsave(&dibs->lock, flags); 93 max_dmbs = dibs->ops->max_dmbs(); 94 for (int i = 0; i < max_dmbs; ++i) { 95 if (dibs->dmb_clientid_arr[i] == client->id) { 96 WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n", 97 __func__, client->name); 98 rc = -EBUSY; 99 goto err_reg_dmb; 100 } 101 } 102 /* Stop forwarding IRQs and events */ 103 dibs->subs[client->id] = NULL; 104 spin_unlock_irqrestore(&dibs->lock, flags); 105 clients[client->id]->ops->del_dev(dibs); 106 dibs->priv[client->id] = NULL; 107 } 108 109 mutex_lock(&clients_lock); 110 clients[client->id] = NULL; 111 if (client->id + 1 == max_client) 112 max_client--; 113 mutex_unlock(&clients_lock); 114 115 mutex_unlock(&dibs_dev_list.mutex); 116 return rc; 117 118 err_reg_dmb: 119 spin_unlock_irqrestore(&dibs->lock, flags); 120 mutex_unlock(&dibs_dev_list.mutex); 121 return rc; 122 } 123 EXPORT_SYMBOL_GPL(dibs_unregister_client); 124 125 static void dibs_dev_release(struct device *dev) 126 { 127 struct dibs_dev *dibs; 128 129 dibs = container_of(dev, struct dibs_dev, dev); 130 131 kfree(dibs->dmb_clientid_arr); 132 kfree(dibs); 133 } 134 135 struct dibs_dev *dibs_dev_alloc(void) 136 { 137 struct dibs_dev *dibs; 138 139 dibs = kzalloc_obj(*dibs); 140 if (!dibs) 141 return dibs; 142 spin_lock_init(&dibs->lock); 143 dibs->dev.release = dibs_dev_release; 144 dibs->dev.class = &dibs_class; 145 device_initialize(&dibs->dev); 146 147 return dibs; 148 } 149 EXPORT_SYMBOL_GPL(dibs_dev_alloc); 150 151 static ssize_t gid_show(struct device *dev, struct device_attribute *attr, 152 char *buf) 153 { 154 struct dibs_dev *dibs; 155 156 dibs = container_of(dev, struct dibs_dev, dev); 157 158 return sysfs_emit(buf, "%pUb\n", &dibs->gid); 159 } 160 static DEVICE_ATTR_RO(gid); 161 162 static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr, 163 char *buf) 164 { 165 struct dibs_dev *dibs; 166 u16 fabric_id; 167 168 dibs = container_of(dev, struct dibs_dev, dev); 169 fabric_id = dibs->ops->get_fabric_id(dibs); 170 171 return sysfs_emit(buf, "0x%04x\n", fabric_id); 172 } 173 static DEVICE_ATTR_RO(fabric_id); 174 175 static struct attribute *dibs_dev_attrs[] = { 176 &dev_attr_gid.attr, 177 &dev_attr_fabric_id.attr, 178 NULL, 179 }; 180 181 static const struct attribute_group dibs_dev_attr_group = { 182 .attrs = dibs_dev_attrs, 183 }; 184 185 int dibs_dev_add(struct dibs_dev *dibs) 186 { 187 int max_dmbs; 188 int i, ret; 189 190 max_dmbs = dibs->ops->max_dmbs(); 191 dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL); 192 if (!dibs->dmb_clientid_arr) 193 return -ENOMEM; 194 memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs); 195 196 ret = device_add(&dibs->dev); 197 if (ret) 198 return ret; 199 200 ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group); 201 if (ret) { 202 dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n"); 203 device_del(&dibs->dev); 204 return ret; 205 } 206 mutex_lock(&dibs_dev_list.mutex); 207 mutex_lock(&clients_lock); 208 for (i = 0; i < max_client; ++i) { 209 if (clients[i]) { 210 clients[i]->ops->add_dev(dibs); 211 dibs_setup_forwarding(clients[i], dibs); 212 } 213 } 214 mutex_unlock(&clients_lock); 215 list_add(&dibs->list, &dibs_dev_list.list); 216 mutex_unlock(&dibs_dev_list.mutex); 217 218 return 0; 219 } 220 EXPORT_SYMBOL_GPL(dibs_dev_add); 221 222 void dibs_dev_del(struct dibs_dev *dibs) 223 { 224 unsigned long flags; 225 int i; 226 227 sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group); 228 229 spin_lock_irqsave(&dibs->lock, flags); 230 for (i = 0; i < MAX_DIBS_CLIENTS; ++i) 231 dibs->subs[i] = NULL; 232 spin_unlock_irqrestore(&dibs->lock, flags); 233 234 mutex_lock(&dibs_dev_list.mutex); 235 mutex_lock(&clients_lock); 236 for (i = 0; i < max_client; ++i) { 237 if (clients[i]) 238 clients[i]->ops->del_dev(dibs); 239 } 240 mutex_unlock(&clients_lock); 241 list_del_init(&dibs->list); 242 mutex_unlock(&dibs_dev_list.mutex); 243 244 device_del(&dibs->dev); 245 } 246 EXPORT_SYMBOL_GPL(dibs_dev_del); 247 248 static int __init dibs_init(void) 249 { 250 int rc; 251 252 rc = class_register(&dibs_class); 253 if (rc) 254 return rc; 255 256 rc = dibs_loopback_init(); 257 if (rc) 258 pr_err("%s fails with %d\n", __func__, rc); 259 260 return rc; 261 } 262 263 static void __exit dibs_exit(void) 264 { 265 dibs_loopback_exit(); 266 class_unregister(&dibs_class); 267 } 268 269 subsys_initcall(dibs_init); 270 module_exit(dibs_exit); 271