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); 132 } 133 134 struct dibs_dev *dibs_dev_alloc(void) 135 { 136 struct dibs_dev *dibs; 137 138 dibs = kzalloc_obj(*dibs); 139 if (!dibs) 140 return dibs; 141 dibs->dev.release = dibs_dev_release; 142 dibs->dev.class = &dibs_class; 143 device_initialize(&dibs->dev); 144 145 return dibs; 146 } 147 EXPORT_SYMBOL_GPL(dibs_dev_alloc); 148 149 static ssize_t gid_show(struct device *dev, struct device_attribute *attr, 150 char *buf) 151 { 152 struct dibs_dev *dibs; 153 154 dibs = container_of(dev, struct dibs_dev, dev); 155 156 return sysfs_emit(buf, "%pUb\n", &dibs->gid); 157 } 158 static DEVICE_ATTR_RO(gid); 159 160 static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr, 161 char *buf) 162 { 163 struct dibs_dev *dibs; 164 u16 fabric_id; 165 166 dibs = container_of(dev, struct dibs_dev, dev); 167 fabric_id = dibs->ops->get_fabric_id(dibs); 168 169 return sysfs_emit(buf, "0x%04x\n", fabric_id); 170 } 171 static DEVICE_ATTR_RO(fabric_id); 172 173 static struct attribute *dibs_dev_attrs[] = { 174 &dev_attr_gid.attr, 175 &dev_attr_fabric_id.attr, 176 NULL, 177 }; 178 179 static const struct attribute_group dibs_dev_attr_group = { 180 .attrs = dibs_dev_attrs, 181 }; 182 183 int dibs_dev_add(struct dibs_dev *dibs) 184 { 185 int max_dmbs; 186 int i, ret; 187 188 max_dmbs = dibs->ops->max_dmbs(); 189 spin_lock_init(&dibs->lock); 190 dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL); 191 if (!dibs->dmb_clientid_arr) 192 return -ENOMEM; 193 memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs); 194 195 ret = device_add(&dibs->dev); 196 if (ret) 197 goto free_client_arr; 198 199 ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group); 200 if (ret) { 201 dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n"); 202 goto err_device_del; 203 } 204 mutex_lock(&dibs_dev_list.mutex); 205 mutex_lock(&clients_lock); 206 for (i = 0; i < max_client; ++i) { 207 if (clients[i]) { 208 clients[i]->ops->add_dev(dibs); 209 dibs_setup_forwarding(clients[i], dibs); 210 } 211 } 212 mutex_unlock(&clients_lock); 213 list_add(&dibs->list, &dibs_dev_list.list); 214 mutex_unlock(&dibs_dev_list.mutex); 215 216 return 0; 217 218 err_device_del: 219 device_del(&dibs->dev); 220 free_client_arr: 221 kfree(dibs->dmb_clientid_arr); 222 return ret; 223 224 } 225 EXPORT_SYMBOL_GPL(dibs_dev_add); 226 227 void dibs_dev_del(struct dibs_dev *dibs) 228 { 229 unsigned long flags; 230 int i; 231 232 sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group); 233 234 spin_lock_irqsave(&dibs->lock, flags); 235 for (i = 0; i < MAX_DIBS_CLIENTS; ++i) 236 dibs->subs[i] = NULL; 237 spin_unlock_irqrestore(&dibs->lock, flags); 238 239 mutex_lock(&dibs_dev_list.mutex); 240 mutex_lock(&clients_lock); 241 for (i = 0; i < max_client; ++i) { 242 if (clients[i]) 243 clients[i]->ops->del_dev(dibs); 244 } 245 mutex_unlock(&clients_lock); 246 list_del_init(&dibs->list); 247 mutex_unlock(&dibs_dev_list.mutex); 248 249 device_del(&dibs->dev); 250 kfree(dibs->dmb_clientid_arr); 251 } 252 EXPORT_SYMBOL_GPL(dibs_dev_del); 253 254 static int __init dibs_init(void) 255 { 256 int rc; 257 258 rc = class_register(&dibs_class); 259 if (rc) 260 return rc; 261 262 rc = dibs_loopback_init(); 263 if (rc) 264 pr_err("%s fails with %d\n", __func__, rc); 265 266 return rc; 267 } 268 269 static void __exit dibs_exit(void) 270 { 271 dibs_loopback_exit(); 272 class_unregister(&dibs_class); 273 } 274 275 subsys_initcall(dibs_init); 276 module_exit(dibs_exit); 277