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