135758b00SAlexandra Winter // SPDX-License-Identifier: GPL-2.0 235758b00SAlexandra Winter /* 335758b00SAlexandra Winter * DIBS - Direct Internal Buffer Sharing 435758b00SAlexandra Winter * 535758b00SAlexandra Winter * Implementation of the DIBS class module 635758b00SAlexandra Winter * 735758b00SAlexandra Winter * Copyright IBM Corp. 2025 835758b00SAlexandra Winter */ 935758b00SAlexandra Winter #define KMSG_COMPONENT "dibs" 1035758b00SAlexandra Winter #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1135758b00SAlexandra Winter 1235758b00SAlexandra Winter #include <linux/module.h> 1335758b00SAlexandra Winter #include <linux/types.h> 1426972696SAlexandra Winter #include <linux/slab.h> 1535758b00SAlexandra Winter #include <linux/err.h> 1635758b00SAlexandra Winter #include <linux/dibs.h> 1735758b00SAlexandra Winter 18cb990a45SAlexandra Winter #include "dibs_loopback.h" 19cb990a45SAlexandra Winter 2035758b00SAlexandra Winter MODULE_DESCRIPTION("Direct Internal Buffer Sharing class"); 2135758b00SAlexandra Winter MODULE_LICENSE("GPL"); 2235758b00SAlexandra Winter 2335758b00SAlexandra Winter /* use an array rather a list for fast mapping: */ 2435758b00SAlexandra Winter static struct dibs_client *clients[MAX_DIBS_CLIENTS]; 2535758b00SAlexandra Winter static u8 max_client; 26d324a2caSAlexandra Winter static DEFINE_MUTEX(clients_lock); 2726972696SAlexandra Winter struct dibs_dev_list { 2826972696SAlexandra Winter struct list_head list; 2926972696SAlexandra Winter struct mutex mutex; /* protects dibs device list */ 3026972696SAlexandra Winter }; 3126972696SAlexandra Winter 3226972696SAlexandra Winter static struct dibs_dev_list dibs_dev_list = { 3326972696SAlexandra Winter .list = LIST_HEAD_INIT(dibs_dev_list.list), 3426972696SAlexandra Winter .mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex), 3526972696SAlexandra Winter }; 36d324a2caSAlexandra Winter 37d324a2caSAlexandra Winter int dibs_register_client(struct dibs_client *client) 38d324a2caSAlexandra Winter { 3969baaac9SAlexandra Winter struct dibs_dev *dibs; 40d324a2caSAlexandra Winter int i, rc = -ENOSPC; 41d324a2caSAlexandra Winter 4269baaac9SAlexandra Winter mutex_lock(&dibs_dev_list.mutex); 43d324a2caSAlexandra Winter mutex_lock(&clients_lock); 44d324a2caSAlexandra Winter for (i = 0; i < MAX_DIBS_CLIENTS; ++i) { 45d324a2caSAlexandra Winter if (!clients[i]) { 46d324a2caSAlexandra Winter clients[i] = client; 47d324a2caSAlexandra Winter client->id = i; 48d324a2caSAlexandra Winter if (i == max_client) 49d324a2caSAlexandra Winter max_client++; 50d324a2caSAlexandra Winter rc = 0; 51d324a2caSAlexandra Winter break; 52d324a2caSAlexandra Winter } 53d324a2caSAlexandra Winter } 54d324a2caSAlexandra Winter mutex_unlock(&clients_lock); 55d324a2caSAlexandra Winter 5669baaac9SAlexandra Winter if (i < MAX_DIBS_CLIENTS) { 5769baaac9SAlexandra Winter /* initialize with all devices that we got so far */ 5869baaac9SAlexandra Winter list_for_each_entry(dibs, &dibs_dev_list.list, list) { 5969baaac9SAlexandra Winter dibs->priv[i] = NULL; 6069baaac9SAlexandra Winter client->ops->add_dev(dibs); 6169baaac9SAlexandra Winter } 6269baaac9SAlexandra Winter } 6369baaac9SAlexandra Winter mutex_unlock(&dibs_dev_list.mutex); 6469baaac9SAlexandra Winter 65d324a2caSAlexandra Winter return rc; 66d324a2caSAlexandra Winter } 67d324a2caSAlexandra Winter EXPORT_SYMBOL_GPL(dibs_register_client); 68d324a2caSAlexandra Winter 69d324a2caSAlexandra Winter int dibs_unregister_client(struct dibs_client *client) 70d324a2caSAlexandra Winter { 7169baaac9SAlexandra Winter struct dibs_dev *dibs; 72d324a2caSAlexandra Winter int rc = 0; 73d324a2caSAlexandra Winter 7469baaac9SAlexandra Winter mutex_lock(&dibs_dev_list.mutex); 7569baaac9SAlexandra Winter list_for_each_entry(dibs, &dibs_dev_list.list, list) { 7669baaac9SAlexandra Winter clients[client->id]->ops->del_dev(dibs); 7769baaac9SAlexandra Winter dibs->priv[client->id] = NULL; 7869baaac9SAlexandra Winter } 7969baaac9SAlexandra Winter 80d324a2caSAlexandra Winter mutex_lock(&clients_lock); 81d324a2caSAlexandra Winter clients[client->id] = NULL; 82d324a2caSAlexandra Winter if (client->id + 1 == max_client) 83d324a2caSAlexandra Winter max_client--; 84d324a2caSAlexandra Winter mutex_unlock(&clients_lock); 8569baaac9SAlexandra Winter 8669baaac9SAlexandra Winter mutex_unlock(&dibs_dev_list.mutex); 87d324a2caSAlexandra Winter return rc; 88d324a2caSAlexandra Winter } 89d324a2caSAlexandra Winter EXPORT_SYMBOL_GPL(dibs_unregister_client); 9035758b00SAlexandra Winter 91*845c334aSJulian Ruess static void dibs_dev_release(struct device *dev) 92*845c334aSJulian Ruess { 93*845c334aSJulian Ruess struct dibs_dev *dibs; 94*845c334aSJulian Ruess 95*845c334aSJulian Ruess dibs = container_of(dev, struct dibs_dev, dev); 96*845c334aSJulian Ruess 97*845c334aSJulian Ruess kfree(dibs); 98*845c334aSJulian Ruess } 99*845c334aSJulian Ruess 10026972696SAlexandra Winter struct dibs_dev *dibs_dev_alloc(void) 10126972696SAlexandra Winter { 10226972696SAlexandra Winter struct dibs_dev *dibs; 10326972696SAlexandra Winter 10426972696SAlexandra Winter dibs = kzalloc(sizeof(*dibs), GFP_KERNEL); 105*845c334aSJulian Ruess if (!dibs) 106*845c334aSJulian Ruess return dibs; 107*845c334aSJulian Ruess dibs->dev.release = dibs_dev_release; 108*845c334aSJulian Ruess device_initialize(&dibs->dev); 10926972696SAlexandra Winter 11026972696SAlexandra Winter return dibs; 11126972696SAlexandra Winter } 11226972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_alloc); 11326972696SAlexandra Winter 11426972696SAlexandra Winter int dibs_dev_add(struct dibs_dev *dibs) 11526972696SAlexandra Winter { 116*845c334aSJulian Ruess int i, ret; 117*845c334aSJulian Ruess 118*845c334aSJulian Ruess ret = device_add(&dibs->dev); 119*845c334aSJulian Ruess if (ret) 120*845c334aSJulian Ruess return ret; 12169baaac9SAlexandra Winter 12226972696SAlexandra Winter mutex_lock(&dibs_dev_list.mutex); 12369baaac9SAlexandra Winter mutex_lock(&clients_lock); 12469baaac9SAlexandra Winter for (i = 0; i < max_client; ++i) { 12569baaac9SAlexandra Winter if (clients[i]) 12669baaac9SAlexandra Winter clients[i]->ops->add_dev(dibs); 12769baaac9SAlexandra Winter } 12869baaac9SAlexandra Winter mutex_unlock(&clients_lock); 12926972696SAlexandra Winter list_add(&dibs->list, &dibs_dev_list.list); 13026972696SAlexandra Winter mutex_unlock(&dibs_dev_list.mutex); 13126972696SAlexandra Winter 13226972696SAlexandra Winter return 0; 13326972696SAlexandra Winter } 13426972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_add); 13526972696SAlexandra Winter 13626972696SAlexandra Winter void dibs_dev_del(struct dibs_dev *dibs) 13726972696SAlexandra Winter { 13869baaac9SAlexandra Winter int i; 13969baaac9SAlexandra Winter 14026972696SAlexandra Winter mutex_lock(&dibs_dev_list.mutex); 14169baaac9SAlexandra Winter mutex_lock(&clients_lock); 14269baaac9SAlexandra Winter for (i = 0; i < max_client; ++i) { 14369baaac9SAlexandra Winter if (clients[i]) 14469baaac9SAlexandra Winter clients[i]->ops->del_dev(dibs); 14569baaac9SAlexandra Winter } 14669baaac9SAlexandra Winter mutex_unlock(&clients_lock); 14726972696SAlexandra Winter list_del_init(&dibs->list); 14826972696SAlexandra Winter mutex_unlock(&dibs_dev_list.mutex); 149*845c334aSJulian Ruess 150*845c334aSJulian Ruess device_del(&dibs->dev); 15126972696SAlexandra Winter } 15226972696SAlexandra Winter EXPORT_SYMBOL_GPL(dibs_dev_del); 15326972696SAlexandra Winter 15435758b00SAlexandra Winter static int __init dibs_init(void) 15535758b00SAlexandra Winter { 156cb990a45SAlexandra Winter int rc; 157cb990a45SAlexandra Winter 15835758b00SAlexandra Winter memset(clients, 0, sizeof(clients)); 15935758b00SAlexandra Winter max_client = 0; 16035758b00SAlexandra Winter 161cb990a45SAlexandra Winter rc = dibs_loopback_init(); 162cb990a45SAlexandra Winter if (rc) 163cb990a45SAlexandra Winter pr_err("%s fails with %d\n", __func__, rc); 164cb990a45SAlexandra Winter 165cb990a45SAlexandra Winter return rc; 16635758b00SAlexandra Winter } 16735758b00SAlexandra Winter 16835758b00SAlexandra Winter static void __exit dibs_exit(void) 16935758b00SAlexandra Winter { 170cb990a45SAlexandra Winter dibs_loopback_exit(); 17135758b00SAlexandra Winter } 17235758b00SAlexandra Winter 17335758b00SAlexandra Winter module_init(dibs_init); 17435758b00SAlexandra Winter module_exit(dibs_exit); 175