| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* | |
| * Direct Internal Buffer Sharing | |
| * | |
| * Definitions for the DIBS module | |
| * | |
| * Copyright IBM Corp. 2025 | |
| */ | |
| #ifndef _DIBS_H | |
| #define _DIBS_H | |
| #include <linux/device.h> | |
| #include <linux/uuid.h> | |
| /* DIBS - Direct Internal Buffer Sharing - concept | |
| * ----------------------------------------------- | |
| * In the case of multiple system sharing the same hardware, dibs fabrics can | |
| * provide dibs devices to these systems. The systems use dibs devices of the | |
| * same fabric to communicate via dmbs (Direct Memory Buffers). Each dmb has | |
| * exactly one owning local dibs device and one remote using dibs device, that | |
| * is authorized to write into this dmb. This access control is provided by the | |
| * dibs fabric. | |
| * | |
| * Because the access to the dmb is based on access to physical memory, it is | |
| * lossless and synchronous. The remote devices can directly access any offset | |
| * of the dmb. | |
| * | |
| * Dibs fabrics, dibs devices and dmbs are identified by tokens and ids. | |
| * Dibs fabric id is unique within the same hardware (with the exception of the | |
| * dibs loopback fabric), dmb token is unique within the same fabric, dibs | |
| * device gids are guaranteed to be unique within the same fabric and | |
| * statistically likely to be globally unique. The exchange of these tokens and | |
| * ids between the systems is not part of the dibs concept. | |
| * | |
| * The dibs layer provides an abstraction between dibs device drivers and dibs | |
| * clients. | |
| */ | |
| /* DMB - Direct Memory Buffer | |
| * -------------------------- | |
| * A dibs client provides a dmb as input buffer for a local receiving | |
| * dibs device for exactly one (remote) sending dibs device. Only this | |
| * sending device can send data into this dmb using move_data(). Sender | |
| * and receiver can be the same device. A dmb belongs to exactly one client. | |
| */ | |
| struct dibs_dmb { | |
| /* tok - Token for this dmb | |
| * Used by remote and local devices and clients to address this dmb. | |
| * Provided by dibs fabric. Unique per dibs fabric. | |
| */ | |
| u64 dmb_tok; | |
| /* rgid - GID of designated remote sending device */ | |
| uuid_t rgid; | |
| /* cpu_addr - buffer address */ | |
| void *cpu_addr; | |
| /* len - buffer length */ | |
| u32 dmb_len; | |
| /* idx - Index of this DMB on this receiving device */ | |
| u32 idx; | |
| /* VLAN support (deprecated) | |
| * In order to write into a vlan-tagged dmb, the remote device needs | |
| * to belong to the this vlan | |
| */ | |
| u32 vlan_valid; | |
| u32 vlan_id; | |
| /* optional, used by device driver */ | |
| dma_addr_t dma_addr; | |
| }; | |
| /* DIBS events | |
| * ----------- | |
| * Dibs devices can optionally notify dibs clients about events that happened | |
| * in the fabric or at the remote device or remote dmb. | |
| */ | |
| enum dibs_event_type { | |
| /* Buffer event, e.g. a remote dmb was unregistered */ | |
| DIBS_BUF_EVENT, | |
| /* Device event, e.g. a remote dibs device was disabled */ | |
| DIBS_DEV_EVENT, | |
| /* Software event, a dibs client can send an event signal to a | |
| * remote dibs device. | |
| */ | |
| DIBS_SW_EVENT, | |
| DIBS_OTHER_TYPE }; | |
| enum dibs_event_subtype { | |
| DIBS_BUF_UNREGISTERED, | |
| DIBS_DEV_DISABLED, | |
| DIBS_DEV_ERR_STATE, | |
| DIBS_OTHER_SUBTYPE | |
| }; | |
| struct dibs_event { | |
| u32 type; | |
| u32 subtype; | |
| /* uuid_null if invalid */ | |
| uuid_t gid; | |
| /* zero if invalid */ | |
| u64 buffer_tok; | |
| u64 time; | |
| /* additional data or zero */ | |
| u64 data; | |
| }; | |
| struct dibs_dev; | |
| /* DIBS client | |
| * ----------- | |
| */ | |
| #define MAX_DIBS_CLIENTS 8 | |
| #define NO_DIBS_CLIENT 0xff | |
| /* All dibs clients have access to all dibs devices. | |
| * A dibs client provides the following functions to be called by dibs layer or | |
| * dibs device drivers: | |
| */ | |
| struct dibs_client_ops { | |
| /** | |
| * add_dev() - add a dibs device | |
| * @dev: device that was added | |
| * | |
| * Will be called during dibs_register_client() for all existing | |
| * dibs devices and whenever a new dibs device is registered. | |
| * dev is usable until dibs_client.remove() is called. | |
| * *dev is protected by device refcounting. | |
| */ | |
| void (*add_dev)(struct dibs_dev *dev); | |
| /** | |
| * del_dev() - remove a dibs device | |
| * @dev: device to be removed | |
| * | |
| * Will be called whenever a dibs device is removed. | |
| * Will be called during dibs_unregister_client() for all existing | |
| * dibs devices and whenever a dibs device is unregistered. | |
| * The device has already stopped initiative for this client: | |
| * No new handlers will be started. | |
| * The device is no longer usable by this client after this call. | |
| */ | |
| void (*del_dev)(struct dibs_dev *dev); | |
| /** | |
| * handle_irq() - Handle signaling for a DMB | |
| * @dev: device that owns the dmb | |
| * @idx: Index of the dmb that got signalled | |
| * @dmbemask: signaling mask of the dmb | |
| * | |
| * Handle signaling for a dmb that was registered by this client | |
| * for this device. | |
| * The dibs device can coalesce multiple signaling triggers into a | |
| * single call of handle_irq(). dmbemask can be used to indicate | |
| * different kinds of triggers. | |
| * | |
| * Context: Called in IRQ context by dibs device driver | |
| */ | |
| void (*handle_irq)(struct dibs_dev *dev, unsigned int idx, | |
| u16 dmbemask); | |
| /** | |
| * handle_event() - Handle control information sent by device | |
| * @dev: device reporting the event | |
| * @event: ism event structure | |
| * | |
| * * Context: Called in IRQ context by dibs device driver | |
| */ | |
| void (*handle_event)(struct dibs_dev *dev, | |
| const struct dibs_event *event); | |
| }; | |
| struct dibs_client { | |
| /* client name for logging and debugging purposes */ | |
| const char *name; | |
| const struct dibs_client_ops *ops; | |
| /* client index - provided and used by dibs layer */ | |
| u8 id; | |
| }; | |
| /* Functions to be called by dibs clients: | |
| */ | |
| /** | |
| * dibs_register_client() - register a client with dibs layer | |
| * @client: this client | |
| * | |
| * Will call client->ops->add_dev() for all existing dibs devices. | |
| * Return: zero on success. | |
| */ | |
| int dibs_register_client(struct dibs_client *client); | |
| /** | |
| * dibs_unregister_client() - unregister a client with dibs layer | |
| * @client: this client | |
| * | |
| * Will call client->ops->del_dev() for all existing dibs devices. | |
| * Return: zero on success. | |
| */ | |
| int dibs_unregister_client(struct dibs_client *client); | |
| /* dibs clients can call dibs device ops. */ | |
| /* DIBS devices | |
| * ------------ | |
| */ | |
| /* Defined fabric id / CHID for all loopback devices: | |
| * All dibs loopback devices report this fabric id. In this case devices with | |
| * the same fabric id can NOT communicate via dibs. Only loopback devices with | |
| * the same dibs device gid can communicate (=same device with itself). | |
| */ | |
| #define DIBS_LOOPBACK_FABRIC 0xFFFF | |
| /* A dibs device provides the following functions to be called by dibs clients. | |
| * They are mandatory, unless marked 'optional'. | |
| */ | |
| struct dibs_dev_ops { | |
| /** | |
| * get_fabric_id() | |
| * @dev: local dibs device | |
| * | |
| * Only devices on the same dibs fabric can communicate. Fabric_id is | |
| * unique inside the same HW system. Use fabric_id for fast negative | |
| * checks, but only query_remote_gid() can give a reliable positive | |
| * answer: | |
| * Different fabric_id: dibs is not possible | |
| * Same fabric_id: dibs may be possible or not | |
| * (e.g. different HW systems) | |
| * EXCEPTION: DIBS_LOOPBACK_FABRIC denotes an ism_loopback device | |
| * that can only communicate with itself. Use dibs_dev.gid | |
| * or query_remote_gid()to determine whether sender and | |
| * receiver use the same ism_loopback device. | |
| * Return: 2 byte dibs fabric id | |
| */ | |
| u16 (*get_fabric_id)(struct dibs_dev *dev); | |
| /** | |
| * query_remote_gid() | |
| * @dev: local dibs device | |
| * @rgid: gid of remote dibs device | |
| * @vid_valid: if zero, vid will be ignored; | |
| * deprecated, ignored if device does not support vlan | |
| * @vid: VLAN id; deprecated, ignored if device does not support vlan | |
| * | |
| * Query whether a remote dibs device is reachable via this local device | |
| * and this vlan id. | |
| * Return: 0 if remote gid is reachable. | |
| */ | |
| int (*query_remote_gid)(struct dibs_dev *dev, const uuid_t *rgid, | |
| u32 vid_valid, u32 vid); | |
| /** | |
| * max_dmbs() | |
| * Return: Max number of DMBs that can be registered for this kind of | |
| * dibs_dev | |
| */ | |
| int (*max_dmbs)(void); | |
| /** | |
| * register_dmb() - allocate and register a dmb | |
| * @dev: dibs device | |
| * @dmb: dmb struct to be registered | |
| * @client: dibs client | |
| * @vid: VLAN id; deprecated, ignored if device does not support vlan | |
| * | |
| * The following fields of dmb must provide valid input: | |
| * @rgid: gid of remote user device | |
| * @dmb_len: buffer length | |
| * @idx: Optionally:requested idx (if non-zero) | |
| * @vlan_valid: if zero, vlan_id will be ignored; | |
| * deprecated, ignored if device does not support vlan | |
| * @vlan_id: deprecated, ignored if device does not support vlan | |
| * Upon return in addition the following fields will be valid: | |
| * @dmb_tok: for usage by remote and local devices and clients | |
| * @cpu_addr: allocated buffer | |
| * @idx: dmb index, unique per dibs device | |
| * @dma_addr: to be used by device driver,if applicable | |
| * | |
| * Allocate a dmb buffer and register it with this device and for this | |
| * client. | |
| * Return: zero on success | |
| */ | |
| int (*register_dmb)(struct dibs_dev *dev, struct dibs_dmb *dmb, | |
| struct dibs_client *client); | |
| /** | |
| * unregister_dmb() - unregister and free a dmb | |
| * @dev: dibs device | |
| * @dmb: dmb struct to be unregistered | |
| * The following fields of dmb must provide valid input: | |
| * @dmb_tok | |
| * @cpu_addr | |
| * @idx | |
| * | |
| * Free dmb.cpu_addr and unregister the dmb from this device. | |
| * Return: zero on success | |
| */ | |
| int (*unregister_dmb)(struct dibs_dev *dev, struct dibs_dmb *dmb); | |
| /** | |
| * move_data() - write into a remote dmb | |
| * @dev: Local sending dibs device | |
| * @dmb_tok: Token of the remote dmb | |
| * @idx: signaling index in dmbemask | |
| * @sf: signaling flag; | |
| * if true, idx will be turned on at target dmbemask mask | |
| * and target device will be signaled. | |
| * @offset: offset within target dmb | |
| * @data: pointer to data to be sent | |
| * @size: length of data to be sent, can be zero. | |
| * | |
| * Use dev to write data of size at offset into a remote dmb | |
| * identified by dmb_tok. Data is moved synchronously, *data can | |
| * be freed when this function returns. | |
| * | |
| * If signaling flag (sf) is true, bit number idx bit will be turned | |
| * on in the dmbemask mask when handle_irq() is called at the remote | |
| * dibs client that owns the target dmb. The target device may chose | |
| * to coalesce the signaling triggers of multiple move_data() calls | |
| * to the same target dmb into a single handle_irq() call. | |
| * Return: zero on success | |
| */ | |
| int (*move_data)(struct dibs_dev *dev, u64 dmb_tok, unsigned int idx, | |
| bool sf, unsigned int offset, void *data, | |
| unsigned int size); | |
| /** | |
| * add_vlan_id() - add dibs device to vlan (optional, deprecated) | |
| * @dev: dibs device | |
| * @vlan_id: vlan id | |
| * | |
| * In order to write into a vlan-tagged dmb, the remote device needs | |
| * to belong to the this vlan. A device can belong to more than 1 vlan. | |
| * Any device can access an untagged dmb. | |
| * Deprecated, only supported for backwards compatibility. | |
| * Return: zero on success | |
| */ | |
| int (*add_vlan_id)(struct dibs_dev *dev, u64 vlan_id); | |
| /** | |
| * del_vlan_id() - remove dibs device from vlan (optional, deprecated) | |
| * @dev: dibs device | |
| * @vlan_id: vlan id | |
| * Return: zero on success | |
| */ | |
| int (*del_vlan_id)(struct dibs_dev *dev, u64 vlan_id); | |
| /** | |
| * signal_event() - trigger an event at a remote dibs device (optional) | |
| * @dev: local dibs device | |
| * @rgid: gid of remote dibs device | |
| * trigger_irq: zero: notification may be coalesced with other events | |
| * non-zero: notify immediately | |
| * @subtype: 4 byte event code, meaning is defined by dibs client | |
| * @data: 8 bytes of additional information, | |
| * meaning is defined by dibs client | |
| * | |
| * dibs devices can offer support for sending a control event of type | |
| * EVENT_SWR to a remote dibs device. | |
| * NOTE: handle_event() will be called for all registered dibs clients | |
| * at the remote device. | |
| * Return: zero on success | |
| */ | |
| int (*signal_event)(struct dibs_dev *dev, const uuid_t *rgid, | |
| u32 trigger_irq, u32 event_code, u64 info); | |
| /** | |
| * support_mmapped_rdmb() - can this device provide memory mapped | |
| * remote dmbs? (optional) | |
| * @dev: dibs device | |
| * | |
| * A dibs device can provide a kernel address + length, that represent | |
| * a remote target dmb (like MMIO). Alternatively to calling | |
| * move_data(), a dibs client can write into such a ghost-send-buffer | |
| * (= to this kernel address) and the data will automatically | |
| * immediately appear in the target dmb, even without calling | |
| * move_data(). | |
| * | |
| * Either all 3 function pointers for support_dmb_nocopy(), | |
| * attach_dmb() and detach_dmb() are defined, or all of them must | |
| * be NULL. | |
| * | |
| * Return: non-zero, if memory mapped remote dmbs are supported. | |
| */ | |
| int (*support_mmapped_rdmb)(struct dibs_dev *dev); | |
| /** | |
| * attach_dmb() - attach local memory to a remote dmb | |
| * @dev: Local sending ism device | |
| * @dmb: all other parameters are passed in the form of a | |
| * dmb struct | |
| * TODO: (THIS IS CONFUSING, should be changed) | |
| * dmb_tok: (in) Token of the remote dmb, we want to attach to | |
| * cpu_addr: (out) MMIO address | |
| * dma_addr: (out) MMIO address (if applicable, invalid otherwise) | |
| * dmb_len: (out) length of local MMIO region, | |
| * equal to length of remote DMB. | |
| * sba_idx: (out) index of remote dmb (NOT HELPFUL, should be removed) | |
| * | |
| * Provides a memory address to the sender that can be used to | |
| * directly write into the remote dmb. | |
| * Memory is available until detach_dmb is called | |
| * | |
| * Return: Zero upon success, Error code otherwise | |
| */ | |
| int (*attach_dmb)(struct dibs_dev *dev, struct dibs_dmb *dmb); | |
| /** | |
| * detach_dmb() - Detach the ghost buffer from a remote dmb | |
| * @dev: ism device | |
| * @token: dmb token of the remote dmb | |
| * | |
| * No need to free cpu_addr. | |
| * | |
| * Return: Zero upon success, Error code otherwise | |
| */ | |
| int (*detach_dmb)(struct dibs_dev *dev, u64 token); | |
| }; | |
| struct dibs_dev { | |
| struct list_head list; | |
| struct device dev; | |
| /* To be filled by device driver, before calling dibs_dev_add(): */ | |
| const struct dibs_dev_ops *ops; | |
| uuid_t gid; | |
| /* priv pointer for device driver */ | |
| void *drv_priv; | |
| /* priv pointer per client; for client usage only */ | |
| void *priv[MAX_DIBS_CLIENTS]; | |
| /* get this lock before accessing any of the fields below */ | |
| spinlock_t lock; | |
| /* array of client ids indexed by dmb idx; | |
| * can be used as indices into priv and subs arrays | |
| */ | |
| u8 *dmb_clientid_arr; | |
| /* Sparse array of all ISM clients */ | |
| struct dibs_client *subs[MAX_DIBS_CLIENTS]; | |
| }; | |
| static inline void dibs_set_priv(struct dibs_dev *dev, | |
| struct dibs_client *client, void *priv) | |
| { | |
| dev->priv[client->id] = priv; | |
| } | |
| static inline void *dibs_get_priv(struct dibs_dev *dev, | |
| struct dibs_client *client) | |
| { | |
| return dev->priv[client->id]; | |
| } | |
| /* ------- End of client-only functions ----------- */ | |
| /* Functions to be called by dibs device drivers: | |
| */ | |
| /** | |
| * dibs_dev_alloc() - allocate and reference device structure | |
| * | |
| * The following fields will be valid upon successful return: dev, lock | |
| * NOTE: Use put_device(dibs_get_dev(@dibs)) to give up your reference instead | |
| * of freeing @dibs @dev directly once you have successfully called this | |
| * function. | |
| * Return: Pointer to dibs device structure | |
| */ | |
| struct dibs_dev *dibs_dev_alloc(void); | |
| /** | |
| * dibs_dev_add() - register with dibs layer and all clients | |
| * @dibs: dibs device | |
| * | |
| * The following fields must be valid upon entry: dev, ops, drv_priv | |
| * All fields will be valid upon successful return. | |
| * Return: zero on success | |
| */ | |
| int dibs_dev_add(struct dibs_dev *dibs); | |
| /** | |
| * dibs_dev_del() - unregister from dibs layer and all clients | |
| * @dibs: dibs device | |
| */ | |
| void dibs_dev_del(struct dibs_dev *dibs); | |
| #endif /* _DIBS_H */ |