465 lines
15 KiB
C
465 lines
15 KiB
C
/* 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
|
|
* 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 */
|