mirror of
https://github.com/torvalds/linux.git
synced 2025-11-01 17:18:25 +02:00
dibs: Move data path to dibs layer
Use struct dibs_dmb instead of struct smc_dmb and move the corresponding client tables to dibs_dev. Leave driver specific implementation details like sba in the device drivers. Register and unregister dmbs via dibs_dev_ops. A dmb is dedicated to a single client, but a dibs device can have dmbs for more than one client. Trigger dibs clients via dibs_client_ops->handle_irq(), when data is received into a dmb. For dibs_loopback replace scheduling an smcd receive tasklet with calling dibs_client_ops->handle_irq(). For loopback devices attach_dmb(), detach_dmb() and move_data() need to access the dmb tables, so move those to dibs_dev_ops in this patch as well. Remove remaining definitions of smc_loopback as they are no longer required, now that everything is in dibs_loopback. Note that struct ism_client and struct ism_dev are still required in smc until a follow-on patch moves event handling to dibs. (Loopback does not use events). Signed-off-by: Alexandra Winter <wintera@linux.ibm.com> Link: https://patch.msgid.link/20250918110500.1731261-14-wintera@linux.ibm.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
719c3b67bb
commit
cc21191b58
12 changed files with 592 additions and 501 deletions
|
|
@ -9,12 +9,18 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dibs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "dibs_loopback.h"
|
||||
|
||||
#define DIBS_LO_SUPPORT_NOCOPY 0x1
|
||||
#define DIBS_DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
static const char dibs_lo_dev_name[] = "lo";
|
||||
/* global loopback device */
|
||||
static struct dibs_lo_dev *lo_dev;
|
||||
|
|
@ -33,11 +39,259 @@ static int dibs_lo_query_rgid(struct dibs_dev *dibs, const uuid_t *rgid,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dibs_lo_max_dmbs(void)
|
||||
{
|
||||
return DIBS_LO_MAX_DMBS;
|
||||
}
|
||||
|
||||
static int dibs_lo_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb,
|
||||
struct dibs_client *client)
|
||||
{
|
||||
struct dibs_lo_dmb_node *dmb_node, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
unsigned long flags;
|
||||
int sba_idx, rc;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
sba_idx = dmb->idx;
|
||||
/* check space for new dmb */
|
||||
for_each_clear_bit(sba_idx, ldev->sba_idx_mask, DIBS_LO_MAX_DMBS) {
|
||||
if (!test_and_set_bit(sba_idx, ldev->sba_idx_mask))
|
||||
break;
|
||||
}
|
||||
if (sba_idx == DIBS_LO_MAX_DMBS)
|
||||
return -ENOSPC;
|
||||
|
||||
dmb_node = kzalloc(sizeof(*dmb_node), GFP_KERNEL);
|
||||
if (!dmb_node) {
|
||||
rc = -ENOMEM;
|
||||
goto err_bit;
|
||||
}
|
||||
|
||||
dmb_node->sba_idx = sba_idx;
|
||||
dmb_node->len = dmb->dmb_len;
|
||||
dmb_node->cpu_addr = kzalloc(dmb_node->len, GFP_KERNEL |
|
||||
__GFP_NOWARN | __GFP_NORETRY |
|
||||
__GFP_NOMEMALLOC);
|
||||
if (!dmb_node->cpu_addr) {
|
||||
rc = -ENOMEM;
|
||||
goto err_node;
|
||||
}
|
||||
dmb_node->dma_addr = DIBS_DMA_ADDR_INVALID;
|
||||
refcount_set(&dmb_node->refcnt, 1);
|
||||
|
||||
again:
|
||||
/* add new dmb into hash table */
|
||||
get_random_bytes(&dmb_node->token, sizeof(dmb_node->token));
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_node->token) {
|
||||
if (tmp_node->token == dmb_node->token) {
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
hash_add(ldev->dmb_ht, &dmb_node->list, dmb_node->token);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
atomic_inc(&ldev->dmb_cnt);
|
||||
|
||||
dmb->idx = dmb_node->sba_idx;
|
||||
dmb->dmb_tok = dmb_node->token;
|
||||
dmb->cpu_addr = dmb_node->cpu_addr;
|
||||
dmb->dma_addr = dmb_node->dma_addr;
|
||||
dmb->dmb_len = dmb_node->len;
|
||||
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->dmb_clientid_arr[sba_idx] = client->id;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err_node:
|
||||
kfree(dmb_node);
|
||||
err_bit:
|
||||
clear_bit(sba_idx, ldev->sba_idx_mask);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __dibs_lo_unregister_dmb(struct dibs_lo_dev *ldev,
|
||||
struct dibs_lo_dmb_node *dmb_node)
|
||||
{
|
||||
/* remove dmb from hash table */
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_del(&dmb_node->list);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask);
|
||||
kfree(dmb_node->cpu_addr);
|
||||
kfree(dmb_node);
|
||||
|
||||
if (atomic_dec_and_test(&ldev->dmb_cnt))
|
||||
wake_up(&ldev->ldev_release);
|
||||
}
|
||||
|
||||
static int dibs_lo_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
||||
{
|
||||
struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
unsigned long flags;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
/* find dmb from hash table */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
|
||||
if (tmp_node->token == dmb->dmb_tok) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
if (!dmb_node)
|
||||
return -EINVAL;
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt)) {
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->dmb_clientid_arr[dmb_node->sba_idx] = NO_DIBS_CLIENT;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
|
||||
__dibs_lo_unregister_dmb(ldev, dmb_node);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dibs_lo_support_dmb_nocopy(struct dibs_dev *dibs)
|
||||
{
|
||||
return DIBS_LO_SUPPORT_NOCOPY;
|
||||
}
|
||||
|
||||
static int dibs_lo_attach_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
||||
{
|
||||
struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
/* find dmb_node according to dmb->dmb_tok */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
|
||||
if (tmp_node->token == dmb->dmb_tok) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (!refcount_inc_not_zero(&dmb_node->refcnt))
|
||||
/* the dmb is being unregistered, but has
|
||||
* not been removed from the hash table.
|
||||
*/
|
||||
return -EINVAL;
|
||||
|
||||
/* provide dmb information */
|
||||
dmb->idx = dmb_node->sba_idx;
|
||||
dmb->dmb_tok = dmb_node->token;
|
||||
dmb->cpu_addr = dmb_node->cpu_addr;
|
||||
dmb->dma_addr = dmb_node->dma_addr;
|
||||
dmb->dmb_len = dmb_node->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dibs_lo_detach_dmb(struct dibs_dev *dibs, u64 token)
|
||||
{
|
||||
struct dibs_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
/* find dmb_node according to dmb->dmb_tok */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) {
|
||||
if (tmp_node->token == token) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt))
|
||||
__dibs_lo_unregister_dmb(ldev, dmb_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dibs_lo_move_data(struct dibs_dev *dibs, u64 dmb_tok,
|
||||
unsigned int idx, bool sf, unsigned int offset,
|
||||
void *data, unsigned int size)
|
||||
{
|
||||
struct dibs_lo_dmb_node *rmb_node = NULL, *tmp_node;
|
||||
struct dibs_lo_dev *ldev;
|
||||
u16 s_mask;
|
||||
u8 client_id;
|
||||
u32 sba_idx;
|
||||
|
||||
ldev = dibs->drv_priv;
|
||||
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_tok) {
|
||||
if (tmp_node->token == dmb_tok) {
|
||||
rmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy((char *)rmb_node->cpu_addr + offset, data, size);
|
||||
sba_idx = rmb_node->sba_idx;
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (!sf)
|
||||
return 0;
|
||||
|
||||
spin_lock(&dibs->lock);
|
||||
client_id = dibs->dmb_clientid_arr[sba_idx];
|
||||
s_mask = ror16(0x1000, idx);
|
||||
if (likely(client_id != NO_DIBS_CLIENT && dibs->subs[client_id]))
|
||||
dibs->subs[client_id]->ops->handle_irq(dibs, sba_idx, s_mask);
|
||||
spin_unlock(&dibs->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dibs_dev_ops dibs_lo_ops = {
|
||||
.get_fabric_id = dibs_lo_get_fabric_id,
|
||||
.query_remote_gid = dibs_lo_query_rgid,
|
||||
.max_dmbs = dibs_lo_max_dmbs,
|
||||
.register_dmb = dibs_lo_register_dmb,
|
||||
.unregister_dmb = dibs_lo_unregister_dmb,
|
||||
.move_data = dibs_lo_move_data,
|
||||
.support_mmapped_rdmb = dibs_lo_support_dmb_nocopy,
|
||||
.attach_dmb = dibs_lo_attach_dmb,
|
||||
.detach_dmb = dibs_lo_detach_dmb,
|
||||
};
|
||||
|
||||
static void dibs_lo_dev_init(struct dibs_lo_dev *ldev)
|
||||
{
|
||||
rwlock_init(&ldev->dmb_ht_lock);
|
||||
hash_init(ldev->dmb_ht);
|
||||
atomic_set(&ldev->dmb_cnt, 0);
|
||||
init_waitqueue_head(&ldev->ldev_release);
|
||||
}
|
||||
|
||||
static void dibs_lo_dev_exit(struct dibs_lo_dev *ldev)
|
||||
{
|
||||
if (atomic_read(&ldev->dmb_cnt))
|
||||
wait_event(ldev->ldev_release, !atomic_read(&ldev->dmb_cnt));
|
||||
}
|
||||
|
||||
static int dibs_lo_dev_probe(void)
|
||||
{
|
||||
struct dibs_lo_dev *ldev;
|
||||
|
|
@ -56,6 +310,7 @@ static int dibs_lo_dev_probe(void)
|
|||
|
||||
ldev->dibs = dibs;
|
||||
dibs->drv_priv = ldev;
|
||||
dibs_lo_dev_init(ldev);
|
||||
uuid_gen(&dibs->gid);
|
||||
dibs->ops = &dibs_lo_ops;
|
||||
|
||||
|
|
@ -69,6 +324,7 @@ static int dibs_lo_dev_probe(void)
|
|||
return 0;
|
||||
|
||||
err_reg:
|
||||
kfree(dibs->dmb_clientid_arr);
|
||||
/* pairs with dibs_dev_alloc() */
|
||||
put_device(&dibs->dev);
|
||||
kfree(ldev);
|
||||
|
|
@ -82,6 +338,7 @@ static void dibs_lo_dev_remove(void)
|
|||
return;
|
||||
|
||||
dibs_dev_del(lo_dev->dibs);
|
||||
dibs_lo_dev_exit(lo_dev);
|
||||
/* pairs with dibs_dev_alloc() */
|
||||
put_device(&lo_dev->dibs->dev);
|
||||
kfree(lo_dev);
|
||||
|
|
|
|||
|
|
@ -13,13 +13,32 @@
|
|||
#define _DIBS_LOOPBACK_H
|
||||
|
||||
#include <linux/dibs.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_DIBS_LO)
|
||||
#define DIBS_LO_DMBS_HASH_BITS 12
|
||||
#define DIBS_LO_MAX_DMBS 5000
|
||||
|
||||
struct dibs_lo_dmb_node {
|
||||
struct hlist_node list;
|
||||
u64 token;
|
||||
u32 len;
|
||||
u32 sba_idx;
|
||||
void *cpu_addr;
|
||||
dma_addr_t dma_addr;
|
||||
refcount_t refcnt;
|
||||
};
|
||||
|
||||
struct dibs_lo_dev {
|
||||
struct dibs_dev *dibs;
|
||||
atomic_t dmb_cnt;
|
||||
rwlock_t dmb_ht_lock;
|
||||
DECLARE_BITMAP(sba_idx_mask, DIBS_LO_MAX_DMBS);
|
||||
DECLARE_HASHTABLE(dmb_ht, DIBS_LO_DMBS_HASH_BITS);
|
||||
wait_queue_head_t ldev_release;
|
||||
};
|
||||
|
||||
int dibs_loopback_init(void);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,16 @@ static struct dibs_dev_list dibs_dev_list = {
|
|||
.mutex = __MUTEX_INITIALIZER(dibs_dev_list.mutex),
|
||||
};
|
||||
|
||||
static void dibs_setup_forwarding(struct dibs_client *client,
|
||||
struct dibs_dev *dibs)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->subs[client->id] = client;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
}
|
||||
|
||||
int dibs_register_client(struct dibs_client *client)
|
||||
{
|
||||
struct dibs_dev *dibs;
|
||||
|
|
@ -60,6 +70,7 @@ int dibs_register_client(struct dibs_client *client)
|
|||
list_for_each_entry(dibs, &dibs_dev_list.list, list) {
|
||||
dibs->priv[i] = NULL;
|
||||
client->ops->add_dev(dibs);
|
||||
dibs_setup_forwarding(client, dibs);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dibs_dev_list.mutex);
|
||||
|
|
@ -71,10 +82,25 @@ EXPORT_SYMBOL_GPL(dibs_register_client);
|
|||
int dibs_unregister_client(struct dibs_client *client)
|
||||
{
|
||||
struct dibs_dev *dibs;
|
||||
unsigned long flags;
|
||||
int max_dmbs;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&dibs_dev_list.mutex);
|
||||
list_for_each_entry(dibs, &dibs_dev_list.list, list) {
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
max_dmbs = dibs->ops->max_dmbs();
|
||||
for (int i = 0; i < max_dmbs; ++i) {
|
||||
if (dibs->dmb_clientid_arr[i] == client->id) {
|
||||
WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n",
|
||||
__func__, client->name);
|
||||
rc = -EBUSY;
|
||||
goto err_reg_dmb;
|
||||
}
|
||||
}
|
||||
/* Stop forwarding IRQs */
|
||||
dibs->subs[client->id] = NULL;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
clients[client->id]->ops->del_dev(dibs);
|
||||
dibs->priv[client->id] = NULL;
|
||||
}
|
||||
|
|
@ -87,6 +113,11 @@ int dibs_unregister_client(struct dibs_client *client)
|
|||
|
||||
mutex_unlock(&dibs_dev_list.mutex);
|
||||
return rc;
|
||||
|
||||
err_reg_dmb:
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
mutex_unlock(&dibs_dev_list.mutex);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dibs_unregister_client);
|
||||
|
||||
|
|
@ -150,11 +181,19 @@ static const struct attribute_group dibs_dev_attr_group = {
|
|||
|
||||
int dibs_dev_add(struct dibs_dev *dibs)
|
||||
{
|
||||
int max_dmbs;
|
||||
int i, ret;
|
||||
|
||||
max_dmbs = dibs->ops->max_dmbs();
|
||||
spin_lock_init(&dibs->lock);
|
||||
dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL);
|
||||
if (!dibs->dmb_clientid_arr)
|
||||
return -ENOMEM;
|
||||
memset(dibs->dmb_clientid_arr, NO_DIBS_CLIENT, max_dmbs);
|
||||
|
||||
ret = device_add(&dibs->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto free_client_arr;
|
||||
|
||||
ret = sysfs_create_group(&dibs->dev.kobj, &dibs_dev_attr_group);
|
||||
if (ret) {
|
||||
|
|
@ -164,8 +203,10 @@ int dibs_dev_add(struct dibs_dev *dibs)
|
|||
mutex_lock(&dibs_dev_list.mutex);
|
||||
mutex_lock(&clients_lock);
|
||||
for (i = 0; i < max_client; ++i) {
|
||||
if (clients[i])
|
||||
if (clients[i]) {
|
||||
clients[i]->ops->add_dev(dibs);
|
||||
dibs_setup_forwarding(clients[i], dibs);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&clients_lock);
|
||||
list_add(&dibs->list, &dibs_dev_list.list);
|
||||
|
|
@ -175,6 +216,8 @@ int dibs_dev_add(struct dibs_dev *dibs)
|
|||
|
||||
err_device_del:
|
||||
device_del(&dibs->dev);
|
||||
free_client_arr:
|
||||
kfree(dibs->dmb_clientid_arr);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
@ -182,8 +225,16 @@ EXPORT_SYMBOL_GPL(dibs_dev_add);
|
|||
|
||||
void dibs_dev_del(struct dibs_dev *dibs)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
sysfs_remove_group(&dibs->dev.kobj, &dibs_dev_attr_group);
|
||||
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
for (i = 0; i < MAX_DIBS_CLIENTS; ++i)
|
||||
dibs->subs[i] = NULL;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
|
||||
mutex_lock(&dibs_dev_list.mutex);
|
||||
mutex_lock(&clients_lock);
|
||||
for (i = 0; i < max_client; ++i) {
|
||||
|
|
@ -195,6 +246,7 @@ void dibs_dev_del(struct dibs_dev *dibs)
|
|||
mutex_unlock(&dibs_dev_list.mutex);
|
||||
|
||||
device_del(&dibs->dev);
|
||||
kfree(dibs->dmb_clientid_arr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dibs_dev_del);
|
||||
|
||||
|
|
|
|||
|
|
@ -98,14 +98,6 @@ int ism_unregister_client(struct ism_client *client)
|
|||
spin_lock_irqsave(&ism->lock, flags);
|
||||
/* Stop forwarding IRQs and events */
|
||||
ism->subs[client->id] = NULL;
|
||||
for (int i = 0; i < ISM_NR_DMBS; ++i) {
|
||||
if (ism->sba_client_arr[i] == client->id) {
|
||||
WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n",
|
||||
__func__, client->name);
|
||||
rc = -EBUSY;
|
||||
goto err_reg_dmb;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&ism->lock, flags);
|
||||
}
|
||||
mutex_unlock(&ism_dev_list.mutex);
|
||||
|
|
@ -116,11 +108,6 @@ int ism_unregister_client(struct ism_client *client)
|
|||
max_client--;
|
||||
mutex_unlock(&clients_lock);
|
||||
return rc;
|
||||
|
||||
err_reg_dmb:
|
||||
spin_unlock_irqrestore(&ism->lock, flags);
|
||||
mutex_unlock(&ism_dev_list.mutex);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ism_unregister_client);
|
||||
|
||||
|
|
@ -308,15 +295,20 @@ static int ism_query_rgid(struct dibs_dev *dibs, const uuid_t *rgid,
|
|||
return ism_cmd(ism, &cmd);
|
||||
}
|
||||
|
||||
static void ism_free_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
||||
static int ism_max_dmbs(void)
|
||||
{
|
||||
clear_bit(dmb->sba_idx, ism->sba_bitmap);
|
||||
return ISM_NR_DMBS;
|
||||
}
|
||||
|
||||
static void ism_free_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)
|
||||
{
|
||||
clear_bit(dmb->idx, ism->sba_bitmap);
|
||||
dma_unmap_page(&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len,
|
||||
DMA_FROM_DEVICE);
|
||||
folio_put(virt_to_folio(dmb->cpu_addr));
|
||||
}
|
||||
|
||||
static int ism_alloc_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
||||
static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)
|
||||
{
|
||||
struct folio *folio;
|
||||
unsigned long bit;
|
||||
|
|
@ -325,16 +317,16 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
|||
if (PAGE_ALIGN(dmb->dmb_len) > dma_get_max_seg_size(&ism->pdev->dev))
|
||||
return -EINVAL;
|
||||
|
||||
if (!dmb->sba_idx) {
|
||||
if (!dmb->idx) {
|
||||
bit = find_next_zero_bit(ism->sba_bitmap, ISM_NR_DMBS,
|
||||
ISM_DMB_BIT_OFFSET);
|
||||
if (bit == ISM_NR_DMBS)
|
||||
return -ENOSPC;
|
||||
|
||||
dmb->sba_idx = bit;
|
||||
dmb->idx = bit;
|
||||
}
|
||||
if (dmb->sba_idx < ISM_DMB_BIT_OFFSET ||
|
||||
test_and_set_bit(dmb->sba_idx, ism->sba_bitmap))
|
||||
if (dmb->idx < ISM_DMB_BIT_OFFSET ||
|
||||
test_and_set_bit(dmb->idx, ism->sba_bitmap))
|
||||
return -EINVAL;
|
||||
|
||||
folio = folio_alloc(GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC |
|
||||
|
|
@ -359,13 +351,14 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
|||
out_free:
|
||||
kfree(dmb->cpu_addr);
|
||||
out_bit:
|
||||
clear_bit(dmb->sba_idx, ism->sba_bitmap);
|
||||
clear_bit(dmb->idx, ism->sba_bitmap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb,
|
||||
struct ism_client *client)
|
||||
static int ism_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb,
|
||||
struct dibs_client *client)
|
||||
{
|
||||
struct ism_dev *ism = dibs->drv_priv;
|
||||
union ism_reg_dmb cmd;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
|
@ -380,10 +373,10 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb,
|
|||
|
||||
cmd.request.dmb = dmb->dma_addr;
|
||||
cmd.request.dmb_len = dmb->dmb_len;
|
||||
cmd.request.sba_idx = dmb->sba_idx;
|
||||
cmd.request.sba_idx = dmb->idx;
|
||||
cmd.request.vlan_valid = dmb->vlan_valid;
|
||||
cmd.request.vlan_id = dmb->vlan_id;
|
||||
cmd.request.rgid = dmb->rgid;
|
||||
memcpy(&cmd.request.rgid, &dmb->rgid, sizeof(u64));
|
||||
|
||||
ret = ism_cmd(ism, &cmd);
|
||||
if (ret) {
|
||||
|
|
@ -391,16 +384,16 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb,
|
|||
goto out;
|
||||
}
|
||||
dmb->dmb_tok = cmd.response.dmb_tok;
|
||||
spin_lock_irqsave(&ism->lock, flags);
|
||||
ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = client->id;
|
||||
spin_unlock_irqrestore(&ism->lock, flags);
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->dmb_clientid_arr[dmb->idx - ISM_DMB_BIT_OFFSET] = client->id;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ism_register_dmb);
|
||||
|
||||
int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
||||
static int ism_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
||||
{
|
||||
struct ism_dev *ism = dibs->drv_priv;
|
||||
union ism_unreg_dmb cmd;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
|
@ -411,9 +404,9 @@ int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
|||
|
||||
cmd.request.dmb_tok = dmb->dmb_tok;
|
||||
|
||||
spin_lock_irqsave(&ism->lock, flags);
|
||||
ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = NO_CLIENT;
|
||||
spin_unlock_irqrestore(&ism->lock, flags);
|
||||
spin_lock_irqsave(&dibs->lock, flags);
|
||||
dibs->dmb_clientid_arr[dmb->idx - ISM_DMB_BIT_OFFSET] = NO_DIBS_CLIENT;
|
||||
spin_unlock_irqrestore(&dibs->lock, flags);
|
||||
|
||||
ret = ism_cmd(ism, &cmd);
|
||||
if (ret && ret != ISM_ERROR)
|
||||
|
|
@ -423,7 +416,6 @@ int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
|
|||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ism_unregister_dmb);
|
||||
|
||||
static int ism_add_vlan_id(struct dibs_dev *dibs, u64 vlan_id)
|
||||
{
|
||||
|
|
@ -459,9 +451,11 @@ static unsigned int max_bytes(unsigned int start, unsigned int len,
|
|||
return min(boundary - (start & (boundary - 1)), len);
|
||||
}
|
||||
|
||||
int ism_move(struct ism_dev *ism, u64 dmb_tok, unsigned int idx, bool sf,
|
||||
unsigned int offset, void *data, unsigned int size)
|
||||
static int ism_move(struct dibs_dev *dibs, u64 dmb_tok, unsigned int idx,
|
||||
bool sf, unsigned int offset, void *data,
|
||||
unsigned int size)
|
||||
{
|
||||
struct ism_dev *ism = dibs->drv_priv;
|
||||
unsigned int bytes;
|
||||
u64 dmb_req;
|
||||
int ret;
|
||||
|
|
@ -482,7 +476,6 @@ int ism_move(struct ism_dev *ism, u64 dmb_tok, unsigned int idx, bool sf,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ism_move);
|
||||
|
||||
static u16 ism_get_chid(struct dibs_dev *dibs)
|
||||
{
|
||||
|
|
@ -518,14 +511,17 @@ static irqreturn_t ism_handle_irq(int irq, void *data)
|
|||
{
|
||||
struct ism_dev *ism = data;
|
||||
unsigned long bit, end;
|
||||
struct dibs_dev *dibs;
|
||||
unsigned long *bv;
|
||||
u16 dmbemask;
|
||||
u8 client_id;
|
||||
|
||||
dibs = ism->dibs;
|
||||
|
||||
bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET];
|
||||
end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET;
|
||||
|
||||
spin_lock(&ism->lock);
|
||||
spin_lock(&dibs->lock);
|
||||
ism->sba->s = 0;
|
||||
barrier();
|
||||
for (bit = 0;;) {
|
||||
|
|
@ -537,10 +533,13 @@ static irqreturn_t ism_handle_irq(int irq, void *data)
|
|||
dmbemask = ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET];
|
||||
ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0;
|
||||
barrier();
|
||||
client_id = ism->sba_client_arr[bit];
|
||||
if (unlikely(client_id == NO_CLIENT || !ism->subs[client_id]))
|
||||
client_id = dibs->dmb_clientid_arr[bit];
|
||||
if (unlikely(client_id == NO_DIBS_CLIENT ||
|
||||
!dibs->subs[client_id]))
|
||||
continue;
|
||||
ism->subs[client_id]->handle_irq(ism, bit + ISM_DMB_BIT_OFFSET, dmbemask);
|
||||
dibs->subs[client_id]->ops->handle_irq(dibs,
|
||||
bit + ISM_DMB_BIT_OFFSET,
|
||||
dmbemask);
|
||||
}
|
||||
|
||||
if (ism->sba->e) {
|
||||
|
|
@ -548,13 +547,17 @@ static irqreturn_t ism_handle_irq(int irq, void *data)
|
|||
barrier();
|
||||
ism_handle_event(ism);
|
||||
}
|
||||
spin_unlock(&ism->lock);
|
||||
spin_unlock(&dibs->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct dibs_dev_ops ism_ops = {
|
||||
.get_fabric_id = ism_get_chid,
|
||||
.query_remote_gid = ism_query_rgid,
|
||||
.max_dmbs = ism_max_dmbs,
|
||||
.register_dmb = ism_register_dmb,
|
||||
.unregister_dmb = ism_unregister_dmb,
|
||||
.move_data = ism_move,
|
||||
.add_vlan_id = ism_add_vlan_id,
|
||||
.del_vlan_id = ism_del_vlan_id,
|
||||
};
|
||||
|
|
@ -568,15 +571,10 @@ static int ism_dev_init(struct ism_dev *ism)
|
|||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
ism->sba_client_arr = kzalloc(ISM_NR_DMBS, GFP_KERNEL);
|
||||
if (!ism->sba_client_arr)
|
||||
goto free_vectors;
|
||||
memset(ism->sba_client_arr, NO_CLIENT, ISM_NR_DMBS);
|
||||
|
||||
ret = request_irq(pci_irq_vector(pdev, 0), ism_handle_irq, 0,
|
||||
pci_name(pdev), ism);
|
||||
if (ret)
|
||||
goto free_client_arr;
|
||||
goto free_vectors;
|
||||
|
||||
ret = register_sba(ism);
|
||||
if (ret)
|
||||
|
|
@ -605,8 +603,6 @@ static int ism_dev_init(struct ism_dev *ism)
|
|||
unregister_sba(ism);
|
||||
free_irq:
|
||||
free_irq(pci_irq_vector(pdev, 0), ism);
|
||||
free_client_arr:
|
||||
kfree(ism->sba_client_arr);
|
||||
free_vectors:
|
||||
pci_free_irq_vectors(pdev);
|
||||
out:
|
||||
|
|
@ -629,7 +625,6 @@ static void ism_dev_exit(struct ism_dev *ism)
|
|||
unregister_ieq(ism);
|
||||
unregister_sba(ism);
|
||||
free_irq(pci_irq_vector(pdev, 0), ism);
|
||||
kfree(ism->sba_client_arr);
|
||||
pci_free_irq_vectors(pdev);
|
||||
list_del_init(&ism->list);
|
||||
mutex_unlock(&ism_dev_list.mutex);
|
||||
|
|
@ -677,6 +672,9 @@ static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
dibs->drv_priv = ism;
|
||||
dibs->ops = &ism_ops;
|
||||
|
||||
/* enable ism device, but any interrupts and events will be ignored
|
||||
* before dibs_dev_add() adds it to any clients.
|
||||
*/
|
||||
ret = ism_dev_init(ism);
|
||||
if (ret)
|
||||
goto err_dibs;
|
||||
|
|
@ -766,17 +764,6 @@ module_exit(ism_exit);
|
|||
/*************************** SMC-D Implementation *****************************/
|
||||
|
||||
#if IS_ENABLED(CONFIG_SMC)
|
||||
static int smcd_register_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb,
|
||||
void *client)
|
||||
{
|
||||
return ism_register_dmb(smcd->priv, (struct ism_dmb *)dmb, client);
|
||||
}
|
||||
|
||||
static int smcd_unregister_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb)
|
||||
{
|
||||
return ism_unregister_dmb(smcd->priv, (struct ism_dmb *)dmb);
|
||||
}
|
||||
|
||||
static int ism_signal_ieq(struct ism_dev *ism, u64 rgid, u32 trigger_irq,
|
||||
u32 event_code, u64 info)
|
||||
{
|
||||
|
|
@ -801,18 +788,8 @@ static int smcd_signal_ieq(struct smcd_dev *smcd, struct smcd_gid *rgid,
|
|||
trigger_irq, event_code, info);
|
||||
}
|
||||
|
||||
static int smcd_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx,
|
||||
bool sf, unsigned int offset, void *data,
|
||||
unsigned int size)
|
||||
{
|
||||
return ism_move(smcd->priv, dmb_tok, idx, sf, offset, data, size);
|
||||
}
|
||||
|
||||
static const struct smcd_ops ism_smcd_ops = {
|
||||
.register_dmb = smcd_register_dmb,
|
||||
.unregister_dmb = smcd_unregister_dmb,
|
||||
.signal_event = smcd_signal_ieq,
|
||||
.move_data = smcd_move,
|
||||
};
|
||||
|
||||
const struct smcd_ops *ism_get_smcd_ops(void)
|
||||
|
|
|
|||
|
|
@ -36,12 +36,44 @@
|
|||
* 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;
|
||||
};
|
||||
|
||||
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:
|
||||
|
|
@ -69,6 +101,22 @@ struct dibs_client_ops {
|
|||
* 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);
|
||||
};
|
||||
|
||||
struct dibs_client {
|
||||
|
|
@ -147,6 +195,77 @@ struct dibs_dev_ops {
|
|||
*/
|
||||
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
|
||||
|
|
@ -166,6 +285,55 @@ struct dibs_dev_ops {
|
|||
* Return: zero on success
|
||||
*/
|
||||
int (*del_vlan_id)(struct dibs_dev *dev, u64 vlan_id);
|
||||
/**
|
||||
* 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 {
|
||||
|
|
@ -179,6 +347,15 @@ struct dibs_dev {
|
|||
|
||||
/* 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,
|
||||
|
|
|
|||
|
|
@ -11,17 +11,6 @@
|
|||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct ism_dmb {
|
||||
u64 dmb_tok;
|
||||
u64 rgid;
|
||||
u32 dmb_len;
|
||||
u32 sba_idx;
|
||||
u32 vlan_valid;
|
||||
u32 vlan_id;
|
||||
void *cpu_addr;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
/* Unless we gain unexpected popularity, this limit should hold for a while */
|
||||
#define MAX_CLIENTS 8
|
||||
#define ISM_NR_DMBS 1920
|
||||
|
|
@ -36,7 +25,6 @@ struct ism_dev {
|
|||
struct ism_sba *sba;
|
||||
dma_addr_t sba_dma_addr;
|
||||
DECLARE_BITMAP(sba_bitmap, ISM_NR_DMBS);
|
||||
u8 *sba_client_arr; /* entries are indices into 'clients' array */
|
||||
void *priv[MAX_CLIENTS];
|
||||
|
||||
struct ism_eq *ieq;
|
||||
|
|
@ -58,11 +46,6 @@ struct ism_event {
|
|||
struct ism_client {
|
||||
const char *name;
|
||||
void (*handle_event)(struct ism_dev *dev, struct ism_event *event);
|
||||
/* Parameter dmbemask contains a bit vector with updated DMBEs, if sent
|
||||
* via ism_move_data(). Callback function must handle all active bits
|
||||
* indicated by dmbemask.
|
||||
*/
|
||||
void (*handle_irq)(struct ism_dev *dev, unsigned int bit, u16 dmbemask);
|
||||
/* Private area - don't touch! */
|
||||
u8 id;
|
||||
};
|
||||
|
|
@ -79,12 +62,6 @@ static inline void ism_set_priv(struct ism_dev *dev, struct ism_client *client,
|
|||
dev->priv[client->id] = priv;
|
||||
}
|
||||
|
||||
int ism_register_dmb(struct ism_dev *dev, struct ism_dmb *dmb,
|
||||
struct ism_client *client);
|
||||
int ism_unregister_dmb(struct ism_dev *dev, struct ism_dmb *dmb);
|
||||
int ism_move(struct ism_dev *dev, u64 dmb_tok, unsigned int idx, bool sf,
|
||||
unsigned int offset, void *data, unsigned int size);
|
||||
|
||||
const struct smcd_ops *ism_get_smcd_ops(void);
|
||||
|
||||
#endif /* _ISM_H */
|
||||
|
|
|
|||
|
|
@ -28,17 +28,6 @@ struct smc_hashinfo {
|
|||
};
|
||||
|
||||
/* SMCD/ISM device driver interface */
|
||||
struct smcd_dmb {
|
||||
u64 dmb_tok;
|
||||
u64 rgid;
|
||||
u32 dmb_len;
|
||||
u32 sba_idx;
|
||||
u32 vlan_valid;
|
||||
u32 vlan_id;
|
||||
void *cpu_addr;
|
||||
dma_addr_t dma_addr;
|
||||
};
|
||||
|
||||
#define ISM_EVENT_DMB 0
|
||||
#define ISM_EVENT_GID 1
|
||||
#define ISM_EVENT_SWR 2
|
||||
|
|
@ -53,25 +42,14 @@ struct smcd_gid {
|
|||
};
|
||||
|
||||
struct smcd_ops {
|
||||
int (*register_dmb)(struct smcd_dev *dev, struct smcd_dmb *dmb,
|
||||
void *client);
|
||||
int (*unregister_dmb)(struct smcd_dev *dev, struct smcd_dmb *dmb);
|
||||
int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx,
|
||||
bool sf, unsigned int offset, void *data,
|
||||
unsigned int size);
|
||||
|
||||
/* optional operations */
|
||||
int (*signal_event)(struct smcd_dev *dev, struct smcd_gid *rgid,
|
||||
u32 trigger_irq, u32 event_code, u64 info);
|
||||
int (*support_dmb_nocopy)(struct smcd_dev *dev);
|
||||
int (*attach_dmb)(struct smcd_dev *dev, struct smcd_dmb *dmb);
|
||||
int (*detach_dmb)(struct smcd_dev *dev, u64 token);
|
||||
};
|
||||
|
||||
struct smcd_dev {
|
||||
const struct smcd_ops *ops;
|
||||
void *priv;
|
||||
void *client;
|
||||
struct dibs_dev *dibs;
|
||||
struct list_head list;
|
||||
spinlock_t lock;
|
||||
|
|
|
|||
|
|
@ -6,4 +6,3 @@ smc-y := af_smc.o smc_pnet.o smc_ib.o smc_clc.o smc_core.o smc_wr.o smc_llc.o
|
|||
smc-y += smc_cdc.o smc_tx.o smc_rx.o smc_close.o smc_ism.o smc_netlink.o smc_stats.o
|
||||
smc-y += smc_tracepoint.o smc_inet.o
|
||||
smc-$(CONFIG_SYSCTL) += smc_sysctl.o
|
||||
smc-y += smc_loopback.o
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#include "smc.h"
|
||||
#include "smc_core.h"
|
||||
#include "smc_ism.h"
|
||||
#include "smc_loopback.h"
|
||||
#include "smc_pnet.h"
|
||||
#include "smc_netlink.h"
|
||||
#include "linux/ism.h"
|
||||
|
|
@ -33,18 +32,19 @@ static void smcd_register_dev(struct dibs_dev *dibs);
|
|||
static void smcd_unregister_dev(struct dibs_dev *dibs);
|
||||
#if IS_ENABLED(CONFIG_ISM)
|
||||
static void smcd_handle_event(struct ism_dev *ism, struct ism_event *event);
|
||||
static void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno,
|
||||
u16 dmbemask);
|
||||
|
||||
static struct ism_client smc_ism_client = {
|
||||
.name = "SMC-D",
|
||||
.handle_event = smcd_handle_event,
|
||||
.handle_irq = smcd_handle_irq,
|
||||
};
|
||||
#endif
|
||||
static void smcd_handle_irq(struct dibs_dev *dibs, unsigned int dmbno,
|
||||
u16 dmbemask);
|
||||
|
||||
static struct dibs_client_ops smc_client_ops = {
|
||||
.add_dev = smcd_register_dev,
|
||||
.del_dev = smcd_unregister_dev,
|
||||
.handle_irq = smcd_handle_irq,
|
||||
};
|
||||
|
||||
static struct dibs_client smc_dibs_client = {
|
||||
|
|
@ -221,18 +221,19 @@ int smc_ism_put_vlan(struct smcd_dev *smcd, unsigned short vlanid)
|
|||
void smc_ism_unregister_dmb(struct smcd_dev *smcd,
|
||||
struct smc_buf_desc *dmb_desc)
|
||||
{
|
||||
struct smcd_dmb dmb;
|
||||
struct dibs_dmb dmb;
|
||||
|
||||
if (!dmb_desc->dma_addr)
|
||||
return;
|
||||
|
||||
memset(&dmb, 0, sizeof(dmb));
|
||||
dmb.dmb_tok = dmb_desc->token;
|
||||
dmb.sba_idx = dmb_desc->sba_idx;
|
||||
dmb.idx = dmb_desc->sba_idx;
|
||||
dmb.cpu_addr = dmb_desc->cpu_addr;
|
||||
dmb.dma_addr = dmb_desc->dma_addr;
|
||||
dmb.dmb_len = dmb_desc->len;
|
||||
smcd->ops->unregister_dmb(smcd, &dmb);
|
||||
|
||||
smcd->dibs->ops->unregister_dmb(smcd->dibs, &dmb);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -240,17 +241,20 @@ void smc_ism_unregister_dmb(struct smcd_dev *smcd,
|
|||
int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len,
|
||||
struct smc_buf_desc *dmb_desc)
|
||||
{
|
||||
struct smcd_dmb dmb;
|
||||
struct dibs_dev *dibs;
|
||||
struct dibs_dmb dmb;
|
||||
int rc;
|
||||
|
||||
memset(&dmb, 0, sizeof(dmb));
|
||||
dmb.dmb_len = dmb_len;
|
||||
dmb.sba_idx = dmb_desc->sba_idx;
|
||||
dmb.idx = dmb_desc->sba_idx;
|
||||
dmb.vlan_id = lgr->vlan_id;
|
||||
dmb.rgid = lgr->peer_gid.gid;
|
||||
rc = lgr->smcd->ops->register_dmb(lgr->smcd, &dmb, lgr->smcd->client);
|
||||
copy_to_dibsgid(&dmb.rgid, &lgr->peer_gid);
|
||||
|
||||
dibs = lgr->smcd->dibs;
|
||||
rc = dibs->ops->register_dmb(dibs, &dmb, &smc_dibs_client);
|
||||
if (!rc) {
|
||||
dmb_desc->sba_idx = dmb.sba_idx;
|
||||
dmb_desc->sba_idx = dmb.idx;
|
||||
dmb_desc->token = dmb.dmb_tok;
|
||||
dmb_desc->cpu_addr = dmb.cpu_addr;
|
||||
dmb_desc->dma_addr = dmb.dma_addr;
|
||||
|
|
@ -265,24 +269,24 @@ bool smc_ism_support_dmb_nocopy(struct smcd_dev *smcd)
|
|||
* merging sndbuf with peer DMB to avoid
|
||||
* data copies between them.
|
||||
*/
|
||||
return (smcd->ops->support_dmb_nocopy &&
|
||||
smcd->ops->support_dmb_nocopy(smcd));
|
||||
return (smcd->dibs->ops->support_mmapped_rdmb &&
|
||||
smcd->dibs->ops->support_mmapped_rdmb(smcd->dibs));
|
||||
}
|
||||
|
||||
int smc_ism_attach_dmb(struct smcd_dev *dev, u64 token,
|
||||
struct smc_buf_desc *dmb_desc)
|
||||
{
|
||||
struct smcd_dmb dmb;
|
||||
struct dibs_dmb dmb;
|
||||
int rc = 0;
|
||||
|
||||
if (!dev->ops->attach_dmb)
|
||||
if (!dev->dibs->ops->attach_dmb)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&dmb, 0, sizeof(dmb));
|
||||
dmb.dmb_tok = token;
|
||||
rc = dev->ops->attach_dmb(dev, &dmb);
|
||||
rc = dev->dibs->ops->attach_dmb(dev->dibs, &dmb);
|
||||
if (!rc) {
|
||||
dmb_desc->sba_idx = dmb.sba_idx;
|
||||
dmb_desc->sba_idx = dmb.idx;
|
||||
dmb_desc->token = dmb.dmb_tok;
|
||||
dmb_desc->cpu_addr = dmb.cpu_addr;
|
||||
dmb_desc->dma_addr = dmb.dma_addr;
|
||||
|
|
@ -294,10 +298,10 @@ int smc_ism_attach_dmb(struct smcd_dev *dev, u64 token,
|
|||
|
||||
int smc_ism_detach_dmb(struct smcd_dev *dev, u64 token)
|
||||
{
|
||||
if (!dev->ops->detach_dmb)
|
||||
if (!dev->dibs->ops->detach_dmb)
|
||||
return -EINVAL;
|
||||
|
||||
return dev->ops->detach_dmb(dev, token);
|
||||
return dev->dibs->ops->detach_dmb(dev->dibs, token);
|
||||
}
|
||||
|
||||
static int smc_nl_handle_smcd_dev(struct smcd_dev *smcd,
|
||||
|
|
@ -503,26 +507,20 @@ static void smcd_register_dev(struct dibs_dev *dibs)
|
|||
{
|
||||
struct smcd_dev *smcd, *fentry;
|
||||
const struct smcd_ops *ops;
|
||||
struct smc_lo_dev *smc_lo;
|
||||
struct ism_dev *ism;
|
||||
int max_dmbs;
|
||||
|
||||
max_dmbs = dibs->ops->max_dmbs();
|
||||
|
||||
if (smc_ism_is_loopback(dibs)) {
|
||||
if (smc_loopback_init(&smc_lo))
|
||||
return;
|
||||
}
|
||||
|
||||
if (smc_ism_is_loopback(dibs)) {
|
||||
ops = smc_lo_get_smcd_ops();
|
||||
smcd = smcd_alloc_dev(dev_name(&dibs->dev), ops,
|
||||
SMC_LO_MAX_DMBS);
|
||||
ops = NULL;
|
||||
} else {
|
||||
ism = dibs->drv_priv;
|
||||
#if IS_ENABLED(CONFIG_ISM)
|
||||
ops = ism_get_smcd_ops();
|
||||
#endif
|
||||
smcd = smcd_alloc_dev(dev_name(&dibs->dev), ops,
|
||||
ISM_NR_DMBS);
|
||||
}
|
||||
smcd = smcd_alloc_dev(dev_name(&dibs->dev), ops, max_dmbs);
|
||||
if (!smcd)
|
||||
return;
|
||||
|
||||
|
|
@ -530,13 +528,11 @@ static void smcd_register_dev(struct dibs_dev *dibs)
|
|||
dibs_set_priv(dibs, &smc_dibs_client, smcd);
|
||||
|
||||
if (smc_ism_is_loopback(dibs)) {
|
||||
smcd->priv = smc_lo;
|
||||
smc_lo->smcd = smcd;
|
||||
smcd->priv = NULL;
|
||||
} else {
|
||||
smcd->priv = ism;
|
||||
#if IS_ENABLED(CONFIG_ISM)
|
||||
ism_set_priv(ism, &smc_ism_client, smcd);
|
||||
smcd->client = &smc_ism_client;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -590,8 +586,6 @@ static void smcd_unregister_dev(struct dibs_dev *dibs)
|
|||
list_del_init(&smcd->list);
|
||||
mutex_unlock(&smcd_dev_list.mutex);
|
||||
destroy_workqueue(smcd->event_wq);
|
||||
if (smc_ism_is_loopback(dibs))
|
||||
smc_loopback_exit();
|
||||
kfree(smcd->conn);
|
||||
kfree(smcd);
|
||||
}
|
||||
|
|
@ -624,6 +618,7 @@ static void smcd_handle_event(struct ism_dev *ism, struct ism_event *event)
|
|||
wrk->event = *event;
|
||||
queue_work(smcd->event_wq, &wrk->work);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SMCD Device interrupt handler. Called from ISM device interrupt handler.
|
||||
* Parameters are the ism device pointer, DMB number, and the DMBE bitmask.
|
||||
|
|
@ -632,10 +627,10 @@ static void smcd_handle_event(struct ism_dev *ism, struct ism_event *event)
|
|||
* Context:
|
||||
* - Function called in IRQ context from ISM device driver IRQ handler.
|
||||
*/
|
||||
static void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno,
|
||||
static void smcd_handle_irq(struct dibs_dev *dibs, unsigned int dmbno,
|
||||
u16 dmbemask)
|
||||
{
|
||||
struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client);
|
||||
struct smcd_dev *smcd = dibs_get_priv(dibs, &smc_dibs_client);
|
||||
struct smc_connection *conn = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -645,7 +640,6 @@ static void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno,
|
|||
tasklet_schedule(&conn->rx_tsklet);
|
||||
spin_unlock_irqrestore(&smcd->lock, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
int smc_ism_signal_shutdown(struct smc_link_group *lgr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ static inline int smc_ism_write(struct smcd_dev *smcd, u64 dmb_tok,
|
|||
{
|
||||
int rc;
|
||||
|
||||
rc = smcd->ops->move_data(smcd, dmb_tok, idx, sf, offset, data, len);
|
||||
rc = smcd->dibs->ops->move_data(smcd->dibs, dmb_tok, idx, sf, offset,
|
||||
data, len);
|
||||
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,294 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Shared Memory Communications Direct over loopback-ism device.
|
||||
*
|
||||
* Functions for loopback-ism device.
|
||||
*
|
||||
* Copyright (c) 2024, Alibaba Inc.
|
||||
*
|
||||
* Author: Wen Gu <guwen@linux.alibaba.com>
|
||||
* Tony Lu <tonylu@linux.alibaba.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dibs.h>
|
||||
#include <net/smc.h>
|
||||
|
||||
#include "smc_cdc.h"
|
||||
#include "smc_ism.h"
|
||||
#include "smc_loopback.h"
|
||||
|
||||
#define SMC_LO_SUPPORT_NOCOPY 0x1
|
||||
#define SMC_DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
static struct smc_lo_dev *lo_dev;
|
||||
|
||||
static int smc_lo_register_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb,
|
||||
void *client_priv)
|
||||
{
|
||||
struct smc_lo_dmb_node *dmb_node, *tmp_node;
|
||||
struct smc_lo_dev *ldev = smcd->priv;
|
||||
int sba_idx, rc;
|
||||
|
||||
/* check space for new dmb */
|
||||
for_each_clear_bit(sba_idx, ldev->sba_idx_mask, SMC_LO_MAX_DMBS) {
|
||||
if (!test_and_set_bit(sba_idx, ldev->sba_idx_mask))
|
||||
break;
|
||||
}
|
||||
if (sba_idx == SMC_LO_MAX_DMBS)
|
||||
return -ENOSPC;
|
||||
|
||||
dmb_node = kzalloc(sizeof(*dmb_node), GFP_KERNEL);
|
||||
if (!dmb_node) {
|
||||
rc = -ENOMEM;
|
||||
goto err_bit;
|
||||
}
|
||||
|
||||
dmb_node->sba_idx = sba_idx;
|
||||
dmb_node->len = dmb->dmb_len;
|
||||
dmb_node->cpu_addr = kzalloc(dmb_node->len, GFP_KERNEL |
|
||||
__GFP_NOWARN | __GFP_NORETRY |
|
||||
__GFP_NOMEMALLOC);
|
||||
if (!dmb_node->cpu_addr) {
|
||||
rc = -ENOMEM;
|
||||
goto err_node;
|
||||
}
|
||||
dmb_node->dma_addr = SMC_DMA_ADDR_INVALID;
|
||||
refcount_set(&dmb_node->refcnt, 1);
|
||||
|
||||
again:
|
||||
/* add new dmb into hash table */
|
||||
get_random_bytes(&dmb_node->token, sizeof(dmb_node->token));
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_node->token) {
|
||||
if (tmp_node->token == dmb_node->token) {
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
hash_add(ldev->dmb_ht, &dmb_node->list, dmb_node->token);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
atomic_inc(&ldev->dmb_cnt);
|
||||
|
||||
dmb->sba_idx = dmb_node->sba_idx;
|
||||
dmb->dmb_tok = dmb_node->token;
|
||||
dmb->cpu_addr = dmb_node->cpu_addr;
|
||||
dmb->dma_addr = dmb_node->dma_addr;
|
||||
dmb->dmb_len = dmb_node->len;
|
||||
|
||||
return 0;
|
||||
|
||||
err_node:
|
||||
kfree(dmb_node);
|
||||
err_bit:
|
||||
clear_bit(sba_idx, ldev->sba_idx_mask);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __smc_lo_unregister_dmb(struct smc_lo_dev *ldev,
|
||||
struct smc_lo_dmb_node *dmb_node)
|
||||
{
|
||||
/* remove dmb from hash table */
|
||||
write_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_del(&dmb_node->list);
|
||||
write_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask);
|
||||
kvfree(dmb_node->cpu_addr);
|
||||
kfree(dmb_node);
|
||||
|
||||
if (atomic_dec_and_test(&ldev->dmb_cnt))
|
||||
wake_up(&ldev->ldev_release);
|
||||
}
|
||||
|
||||
static int smc_lo_unregister_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb)
|
||||
{
|
||||
struct smc_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct smc_lo_dev *ldev = smcd->priv;
|
||||
|
||||
/* find dmb from hash table */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
|
||||
if (tmp_node->token == dmb->dmb_tok) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt))
|
||||
__smc_lo_unregister_dmb(ldev, dmb_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smc_lo_support_dmb_nocopy(struct smcd_dev *smcd)
|
||||
{
|
||||
return SMC_LO_SUPPORT_NOCOPY;
|
||||
}
|
||||
|
||||
static int smc_lo_attach_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb)
|
||||
{
|
||||
struct smc_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct smc_lo_dev *ldev = smcd->priv;
|
||||
|
||||
/* find dmb_node according to dmb->dmb_tok */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
|
||||
if (tmp_node->token == dmb->dmb_tok) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (!refcount_inc_not_zero(&dmb_node->refcnt))
|
||||
/* the dmb is being unregistered, but has
|
||||
* not been removed from the hash table.
|
||||
*/
|
||||
return -EINVAL;
|
||||
|
||||
/* provide dmb information */
|
||||
dmb->sba_idx = dmb_node->sba_idx;
|
||||
dmb->dmb_tok = dmb_node->token;
|
||||
dmb->cpu_addr = dmb_node->cpu_addr;
|
||||
dmb->dma_addr = dmb_node->dma_addr;
|
||||
dmb->dmb_len = dmb_node->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smc_lo_detach_dmb(struct smcd_dev *smcd, u64 token)
|
||||
{
|
||||
struct smc_lo_dmb_node *dmb_node = NULL, *tmp_node;
|
||||
struct smc_lo_dev *ldev = smcd->priv;
|
||||
|
||||
/* find dmb_node according to dmb->dmb_tok */
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, token) {
|
||||
if (tmp_node->token == token) {
|
||||
dmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (refcount_dec_and_test(&dmb_node->refcnt))
|
||||
__smc_lo_unregister_dmb(ldev, dmb_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smc_lo_move_data(struct smcd_dev *smcd, u64 dmb_tok,
|
||||
unsigned int idx, bool sf, unsigned int offset,
|
||||
void *data, unsigned int size)
|
||||
{
|
||||
struct smc_lo_dmb_node *rmb_node = NULL, *tmp_node;
|
||||
struct smc_lo_dev *ldev = smcd->priv;
|
||||
struct smc_connection *conn;
|
||||
|
||||
read_lock_bh(&ldev->dmb_ht_lock);
|
||||
hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_tok) {
|
||||
if (tmp_node->token == dmb_tok) {
|
||||
rmb_node = tmp_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rmb_node) {
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy((char *)rmb_node->cpu_addr + offset, data, size);
|
||||
read_unlock_bh(&ldev->dmb_ht_lock);
|
||||
|
||||
if (!sf)
|
||||
return 0;
|
||||
|
||||
conn = smcd->conn[rmb_node->sba_idx];
|
||||
if (!conn || conn->killed)
|
||||
return -EPIPE;
|
||||
tasklet_schedule(&conn->rx_tsklet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct smcd_ops lo_ops = {
|
||||
.register_dmb = smc_lo_register_dmb,
|
||||
.unregister_dmb = smc_lo_unregister_dmb,
|
||||
.support_dmb_nocopy = smc_lo_support_dmb_nocopy,
|
||||
.attach_dmb = smc_lo_attach_dmb,
|
||||
.detach_dmb = smc_lo_detach_dmb,
|
||||
.signal_event = NULL,
|
||||
.move_data = smc_lo_move_data,
|
||||
};
|
||||
|
||||
const struct smcd_ops *smc_lo_get_smcd_ops(void)
|
||||
{
|
||||
return &lo_ops;
|
||||
}
|
||||
|
||||
static void smc_lo_dev_init(struct smc_lo_dev *ldev)
|
||||
{
|
||||
rwlock_init(&ldev->dmb_ht_lock);
|
||||
hash_init(ldev->dmb_ht);
|
||||
atomic_set(&ldev->dmb_cnt, 0);
|
||||
init_waitqueue_head(&ldev->ldev_release);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void smc_lo_dev_exit(struct smc_lo_dev *ldev)
|
||||
{
|
||||
if (atomic_read(&ldev->dmb_cnt))
|
||||
wait_event(ldev->ldev_release, !atomic_read(&ldev->dmb_cnt));
|
||||
}
|
||||
|
||||
static int smc_lo_dev_probe(void)
|
||||
{
|
||||
struct smc_lo_dev *ldev;
|
||||
|
||||
ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
|
||||
if (!ldev)
|
||||
return -ENOMEM;
|
||||
|
||||
smc_lo_dev_init(ldev);
|
||||
|
||||
lo_dev = ldev; /* global loopback device */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smc_lo_dev_remove(void)
|
||||
{
|
||||
if (!lo_dev)
|
||||
return;
|
||||
|
||||
smc_lo_dev_exit(lo_dev);
|
||||
kfree(lo_dev);
|
||||
lo_dev = NULL;
|
||||
}
|
||||
|
||||
int smc_loopback_init(struct smc_lo_dev **smc_lb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = smc_lo_dev_probe();
|
||||
if (!ret)
|
||||
*smc_lb = lo_dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void smc_loopback_exit(void)
|
||||
{
|
||||
smc_lo_dev_remove();
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Shared Memory Communications Direct over loopback-ism device.
|
||||
*
|
||||
* SMC-D loopback-ism device structure definitions.
|
||||
*
|
||||
* Copyright (c) 2024, Alibaba Inc.
|
||||
*
|
||||
* Author: Wen Gu <guwen@linux.alibaba.com>
|
||||
* Tony Lu <tonylu@linux.alibaba.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SMC_LOOPBACK_H
|
||||
#define _SMC_LOOPBACK_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <net/smc.h>
|
||||
|
||||
#define SMC_LO_MAX_DMBS 5000
|
||||
#define SMC_LO_DMBS_HASH_BITS 12
|
||||
|
||||
struct smc_lo_dmb_node {
|
||||
struct hlist_node list;
|
||||
u64 token;
|
||||
u32 len;
|
||||
u32 sba_idx;
|
||||
void *cpu_addr;
|
||||
dma_addr_t dma_addr;
|
||||
refcount_t refcnt;
|
||||
};
|
||||
|
||||
struct smc_lo_dev {
|
||||
struct smcd_dev *smcd;
|
||||
atomic_t dmb_cnt;
|
||||
rwlock_t dmb_ht_lock;
|
||||
DECLARE_BITMAP(sba_idx_mask, SMC_LO_MAX_DMBS);
|
||||
DECLARE_HASHTABLE(dmb_ht, SMC_LO_DMBS_HASH_BITS);
|
||||
wait_queue_head_t ldev_release;
|
||||
};
|
||||
|
||||
const struct smcd_ops *smc_lo_get_smcd_ops(void);
|
||||
|
||||
int smc_loopback_init(struct smc_lo_dev **smc_lb);
|
||||
void smc_loopback_exit(void);
|
||||
|
||||
#endif /* _SMC_LOOPBACK_H */
|
||||
Loading…
Reference in a new issue