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 /* use an array rather a list for fast mapping: */ 24 static struct dibs_client *clients[MAX_DIBS_CLIENTS]; 25 static u8 max_client; 26 static DEFINE_MUTEX(clients_lock); 27 struct dibs_dev_list { 28 struct list_head list; 29 struct mutex mutex; /* protects dibs device list */ 30 }; 31 32 static struct dibs_dev_list dibs_dev_list = { 33 .list = LIST_HEAD_INIT(dibs_dev_list.list), 34 .mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex), 35 }; 36 37 int dibs_register_client(struct dibs_client *client) 38 { 39 struct dibs_dev *dibs; 40 int i, rc = -ENOSPC; 41 42 mutex_lock(&dibs_dev_list.mutex); 43 mutex_lock(&clients_lock); 44 for (i = 0; i < MAX_DIBS_CLIENTS; ++i) { 45 if (!clients[i]) { 46 clients[i] = client; 47 client->id = i; 48 if (i == max_client) 49 max_client++; 50 rc = 0; 51 break; 52 } 53 } 54 mutex_unlock(&clients_lock); 55 56 if (i < MAX_DIBS_CLIENTS) { 57 /* initialize with all devices that we got so far */ 58 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 59 dibs->priv[i] = NULL; 60 client->ops->add_dev(dibs); 61 } 62 } 63 mutex_unlock(&dibs_dev_list.mutex); 64 65 return rc; 66 } 67 EXPORT_SYMBOL_GPL(dibs_register_client); 68 69 int dibs_unregister_client(struct dibs_client *client) 70 { 71 struct dibs_dev *dibs; 72 int rc = 0; 73 74 mutex_lock(&dibs_dev_list.mutex); 75 list_for_each_entry(dibs, &dibs_dev_list.list, list) { 76 clients[client->id]->ops->del_dev(dibs); 77 dibs->priv[client->id] = NULL; 78 } 79 80 mutex_lock(&clients_lock); 81 clients[client->id] = NULL; 82 if (client->id + 1 == max_client) 83 max_client--; 84 mutex_unlock(&clients_lock); 85 86 mutex_unlock(&dibs_dev_list.mutex); 87 return rc; 88 } 89 EXPORT_SYMBOL_GPL(dibs_unregister_client); 90 91 static void dibs_dev_release(struct device *dev) 92 { 93 struct dibs_dev *dibs; 94 95 dibs = container_of(dev, struct dibs_dev, dev); 96 97 kfree(dibs); 98 } 99 100 struct dibs_dev *dibs_dev_alloc(void) 101 { 102 struct dibs_dev *dibs; 103 104 dibs = kzalloc(sizeof(*dibs), GFP_KERNEL); 105 if (!dibs) 106 return dibs; 107 dibs->dev.release = dibs_dev_release; 108 device_initialize(&dibs->dev); 109 110 return dibs; 111 } 112 EXPORT_SYMBOL_GPL(dibs_dev_alloc); 113 114 int dibs_dev_add(struct dibs_dev *dibs) 115 { 116 int i, ret; 117 118 ret = device_add(&dibs->dev); 119 if (ret) 120 return ret; 121 122 mutex_lock(&dibs_dev_list.mutex); 123 mutex_lock(&clients_lock); 124 for (i = 0; i < max_client; ++i) { 125 if (clients[i]) 126 clients[i]->ops->add_dev(dibs); 127 } 128 mutex_unlock(&clients_lock); 129 list_add(&dibs->list, &dibs_dev_list.list); 130 mutex_unlock(&dibs_dev_list.mutex); 131 132 return 0; 133 } 134 EXPORT_SYMBOL_GPL(dibs_dev_add); 135 136 void dibs_dev_del(struct dibs_dev *dibs) 137 { 138 int i; 139 140 mutex_lock(&dibs_dev_list.mutex); 141 mutex_lock(&clients_lock); 142 for (i = 0; i < max_client; ++i) { 143 if (clients[i]) 144 clients[i]->ops->del_dev(dibs); 145 } 146 mutex_unlock(&clients_lock); 147 list_del_init(&dibs->list); 148 mutex_unlock(&dibs_dev_list.mutex); 149 150 device_del(&dibs->dev); 151 } 152 EXPORT_SYMBOL_GPL(dibs_dev_del); 153 154 static int __init dibs_init(void) 155 { 156 int rc; 157 158 memset(clients, 0, sizeof(clients)); 159 max_client = 0; 160 161 rc = dibs_loopback_init(); 162 if (rc) 163 pr_err("%s fails with %d\n", __func__, rc); 164 165 return rc; 166 } 167 168 static void __exit dibs_exit(void) 169 { 170 dibs_loopback_exit(); 171 } 172 173 module_init(dibs_init); 174 module_exit(dibs_exit); 175