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