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 int dibs_register_client(struct dibs_client *client) 40 { 41 struct dibs_dev *dibs; 42 int i, rc = -ENOSPC; 43 44 mutex_lock(&dibs_dev_list.mutex); 45 mutex_lock(&clients_lock); 46 for (i = 0; i < MAX_DIBS_CLIENTS; ++i) { 47 if (!clients[i]) { 48 clients[i] = client; 49 client->id = i; 50 if (i == max_client) 51 max_client++; 52 rc = 0; 53 break; 54 } 55 } 56 mutex_unlock(&clients_lock); 57 58 if (i < MAX_DIBS_CLIENTS) { 59 /* initialize with all devices that we got so far */ 60 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 61 dibs->priv[i] = NULL; 62 client->ops->add_dev(dibs); 63 } 64 } 65 mutex_unlock(&dibs_dev_list.mutex); 66 67 return rc; 68 } 69 EXPORT_SYMBOL_GPL(dibs_register_client); 70 71 int dibs_unregister_client(struct dibs_client *client) 72 { 73 struct dibs_dev *dibs; 74 int rc = 0; 75 76 mutex_lock(&dibs_dev_list.mutex); 77 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 78 clients[client->id]->ops->del_dev(dibs); 79 dibs->priv[client->id] = NULL; 80 } 81 82 mutex_lock(&clients_lock); 83 clients[client->id] = NULL; 84 if (client->id + 1 == max_client) 85 max_client--; 86 mutex_unlock(&clients_lock); 87 88 mutex_unlock(&dibs_dev_list.mutex); 89 return rc; 90 } 91 EXPORT_SYMBOL_GPL(dibs_unregister_client); 92 93 static void dibs_dev_release(struct device *dev) 94 { 95 struct dibs_dev *dibs; 96 97 dibs = container_of(dev, struct dibs_dev, dev); 98 99 kfree(dibs); 100 } 101 102 struct dibs_dev *dibs_dev_alloc(void) 103 { 104 struct dibs_dev *dibs; 105 106 dibs = kzalloc(sizeof(*dibs), GFP_KERNEL); 107 if (!dibs) 108 return dibs; 109 dibs->dev.release = dibs_dev_release; 110 dibs->dev.class = dibs_class; 111 device_initialize(&dibs->dev); 112 113 return dibs; 114 } 115 EXPORT_SYMBOL_GPL(dibs_dev_alloc); 116 117 static ssize_t gid_show(struct device *dev, struct device_attribute *attr, 118 char *buf) 119 { 120 struct dibs_dev *dibs; 121 122 dibs = container_of(dev, struct dibs_dev, dev); 123 124 return sysfs_emit(buf, "%pUb\n", &dibs->gid); 125 } 126 static DEVICE_ATTR_RO(gid); 127 128 static ssize_t fabric_id_show(struct device *dev, struct device_attribute *attr, 129 char *buf) 130 { 131 struct dibs_dev *dibs; 132 u16 fabric_id; 133 134 dibs = container_of(dev, struct dibs_dev, dev); 135 fabric_id = dibs->ops->get_fabric_id(dibs); 136 137 return sysfs_emit(buf, "0x%04x\n", fabric_id); 138 } 139 static DEVICE_ATTR_RO(fabric_id); 140 141 static struct attribute *dibs_dev_attrs[] = { 142 &dev_attr_gid.attr, 143 &dev_attr_fabric_id.attr, 144 NULL, 145 }; 146 147 static const struct attribute_group dibs_dev_attr_group = { 148 .attrs = dibs_dev_attrs, 149 }; 150 151 int dibs_dev_add(struct dibs_dev *dibs) 152 { 153 int i, ret; 154 155 ret = device_add(&dibs->dev); 156 if (ret) 157 return ret; 158 159 ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group); 160 if (ret) { 161 dev_err(&dibs->dev, "sysfs_create_group failed for dibs_dev\n"); 162 goto err_device_del; 163 } 164 mutex_lock(&dibs_dev_list.mutex); 165 mutex_lock(&clients_lock); 166 for (i = 0; i < max_client; ++i) { 167 if (clients[i]) 168 clients[i]->ops->add_dev(dibs); 169 } 170 mutex_unlock(&clients_lock); 171 list_add(&dibs->list, &dibs_dev_list.list); 172 mutex_unlock(&dibs_dev_list.mutex); 173 174 return 0; 175 176 err_device_del: 177 device_del(&dibs->dev); 178 return ret; 179 180 } 181 EXPORT_SYMBOL_GPL(dibs_dev_add); 182 183 void dibs_dev_del(struct dibs_dev *dibs) 184 { 185 int i; 186 187 mutex_lock(&dibs_dev_list.mutex); 188 mutex_lock(&clients_lock); 189 for (i = 0; i < max_client; ++i) { 190 if (clients[i]) 191 clients[i]->ops->del_dev(dibs); 192 } 193 mutex_unlock(&clients_lock); 194 list_del_init(&dibs->list); 195 mutex_unlock(&dibs_dev_list.mutex); 196 197 device_del(&dibs->dev); 198 } 199 EXPORT_SYMBOL_GPL(dibs_dev_del); 200 201 static int __init dibs_init(void) 202 { 203 int rc; 204 205 memset(clients, 0, sizeof(clients)); 206 max_client = 0; 207 208 dibs_class = class_create("dibs"); 209 if (IS_ERR(&dibs_class)) 210 return PTR_ERR(&dibs_class); 211 212 rc = dibs_loopback_init(); 213 if (rc) 214 pr_err("%s fails with %d\n", __func__, rc); 215 216 return rc; 217 } 218 219 static void __exit dibs_exit(void) 220 { 221 dibs_loopback_exit(); 222 class_destroy(dibs_class); 223 } 224 225 module_init(dibs_init); 226 module_exit(dibs_exit); 227