#include <linux/if_vlan.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <asm/page.h>
#include "smc.h"
#include "smc_core.h"
#include "smc_ism.h"
#include "smc_pnet.h"
#include "smc_netlink.h"
#include "linux/ism.h"
struct smcd_dev_list smcd_dev_list = …;
static bool smc_ism_v2_capable;
static u8 smc_ism_v2_system_eid[SMC_MAX_EID_LEN];
#if IS_ENABLED(CONFIG_ISM)
static void smcd_register_dev(struct ism_dev *ism);
static void smcd_unregister_dev(struct ism_dev *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",
.add = smcd_register_dev,
.remove = smcd_unregister_dev,
.handle_event = smcd_handle_event,
.handle_irq = smcd_handle_irq,
};
#endif
static void smc_ism_create_system_eid(void)
{ … }
int smc_ism_cantalk(struct smcd_gid *peer_gid, unsigned short vlan_id,
struct smcd_dev *smcd)
{ … }
void smc_ism_get_system_eid(u8 **eid)
{ … }
u16 smc_ism_get_chid(struct smcd_dev *smcd)
{ … }
bool smc_ism_is_v2_capable(void)
{ … }
void smc_ism_set_v2_capable(void)
{ … }
void smc_ism_set_conn(struct smc_connection *conn)
{ … }
void smc_ism_unset_conn(struct smc_connection *conn)
{ … }
int smc_ism_get_vlan(struct smcd_dev *smcd, unsigned short vlanid)
{ … }
int smc_ism_put_vlan(struct smcd_dev *smcd, unsigned short vlanid)
{ … }
int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc)
{ … }
int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len,
struct smc_buf_desc *dmb_desc)
{ … }
bool smc_ism_support_dmb_nocopy(struct smcd_dev *smcd)
{ … }
int smc_ism_attach_dmb(struct smcd_dev *dev, u64 token,
struct smc_buf_desc *dmb_desc)
{ … }
int smc_ism_detach_dmb(struct smcd_dev *dev, u64 token)
{ … }
static int smc_nl_handle_smcd_dev(struct smcd_dev *smcd,
struct sk_buff *skb,
struct netlink_callback *cb)
{ … }
static void smc_nl_prep_smcd_dev(struct smcd_dev_list *dev_list,
struct sk_buff *skb,
struct netlink_callback *cb)
{ … }
int smcd_nl_get_device(struct sk_buff *skb, struct netlink_callback *cb)
{ … }
#if IS_ENABLED(CONFIG_ISM)
struct smc_ism_event_work {
struct work_struct work;
struct smcd_dev *smcd;
struct ism_event event;
};
#define ISM_EVENT_REQUEST …
#define ISM_EVENT_RESPONSE …
#define ISM_EVENT_REQUEST_IR …
#define ISM_EVENT_CODE_SHUTDOWN …
#define ISM_EVENT_CODE_TESTLINK …
union smcd_sw_event_info {
u64 info;
struct {
u8 uid[SMC_LGR_ID_SIZE];
unsigned short vlan_id;
u16 code;
};
};
static void smcd_handle_sw_event(struct smc_ism_event_work *wrk)
{
struct smcd_gid peer_gid = { .gid = wrk->event.tok,
.gid_ext = 0 };
union smcd_sw_event_info ev_info;
ev_info.info = wrk->event.info;
switch (wrk->event.code) {
case ISM_EVENT_CODE_SHUTDOWN:
smc_smcd_terminate(wrk->smcd, &peer_gid, ev_info.vlan_id);
break;
case ISM_EVENT_CODE_TESTLINK:
if (ev_info.code == ISM_EVENT_REQUEST &&
wrk->smcd->ops->signal_event) {
ev_info.code = ISM_EVENT_RESPONSE;
wrk->smcd->ops->signal_event(wrk->smcd,
&peer_gid,
ISM_EVENT_REQUEST_IR,
ISM_EVENT_CODE_TESTLINK,
ev_info.info);
}
break;
}
}
static void smc_ism_event_work(struct work_struct *work)
{
struct smc_ism_event_work *wrk =
container_of(work, struct smc_ism_event_work, work);
struct smcd_gid smcd_gid = { .gid = wrk->event.tok,
.gid_ext = 0 };
switch (wrk->event.type) {
case ISM_EVENT_GID:
smc_smcd_terminate(wrk->smcd, &smcd_gid, VLAN_VID_MASK);
break;
case ISM_EVENT_DMB:
break;
case ISM_EVENT_SWR:
smcd_handle_sw_event(wrk);
break;
}
kfree(wrk);
}
static struct smcd_dev *smcd_alloc_dev(struct device *parent, const char *name,
const struct smcd_ops *ops, int max_dmbs)
{
struct smcd_dev *smcd;
smcd = devm_kzalloc(parent, sizeof(*smcd), GFP_KERNEL);
if (!smcd)
return NULL;
smcd->conn = devm_kcalloc(parent, max_dmbs,
sizeof(struct smc_connection *), GFP_KERNEL);
if (!smcd->conn)
return NULL;
smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)",
WQ_MEM_RECLAIM, name);
if (!smcd->event_wq)
return NULL;
smcd->ops = ops;
spin_lock_init(&smcd->lock);
spin_lock_init(&smcd->lgr_lock);
INIT_LIST_HEAD(&smcd->vlan);
INIT_LIST_HEAD(&smcd->lgr_list);
init_waitqueue_head(&smcd->lgrs_deleted);
return smcd;
}
static void smcd_register_dev(struct ism_dev *ism)
{
const struct smcd_ops *ops = ism_get_smcd_ops();
struct smcd_dev *smcd, *fentry;
if (!ops)
return;
smcd = smcd_alloc_dev(&ism->pdev->dev, dev_name(&ism->pdev->dev), ops,
ISM_NR_DMBS);
if (!smcd)
return;
smcd->priv = ism;
smcd->client = &smc_ism_client;
ism_set_priv(ism, &smc_ism_client, smcd);
if (smc_pnetid_by_dev_port(&ism->pdev->dev, 0, smcd->pnetid))
smc_pnetid_by_table_smcd(smcd);
if (smcd->ops->supports_v2())
smc_ism_set_v2_capable();
mutex_lock(&smcd_dev_list.mutex);
if (!smcd->pnetid[0]) {
fentry = list_first_entry_or_null(&smcd_dev_list.list,
struct smcd_dev, list);
if (fentry && smc_ism_is_loopback(fentry))
list_add(&smcd->list, &fentry->list);
else
list_add(&smcd->list, &smcd_dev_list.list);
} else {
list_add_tail(&smcd->list, &smcd_dev_list.list);
}
mutex_unlock(&smcd_dev_list.mutex);
pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n",
dev_name(&ism->dev), smcd->pnetid,
smcd->pnetid_by_user ? " (user defined)" : "");
return;
}
static void smcd_unregister_dev(struct ism_dev *ism)
{
struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client);
pr_warn_ratelimited("smc: removing smcd device %s\n",
dev_name(&ism->dev));
smcd->going_away = 1;
smc_smcd_terminate_all(smcd);
mutex_lock(&smcd_dev_list.mutex);
list_del_init(&smcd->list);
mutex_unlock(&smcd_dev_list.mutex);
destroy_workqueue(smcd->event_wq);
}
static void smcd_handle_event(struct ism_dev *ism, struct ism_event *event)
{
struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client);
struct smc_ism_event_work *wrk;
if (smcd->going_away)
return;
wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC);
if (!wrk)
return;
INIT_WORK(&wrk->work, smc_ism_event_work);
wrk->smcd = smcd;
wrk->event = *event;
queue_work(smcd->event_wq, &wrk->work);
}
static void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno,
u16 dmbemask)
{
struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client);
struct smc_connection *conn = NULL;
unsigned long flags;
spin_lock_irqsave(&smcd->lock, flags);
conn = smcd->conn[dmbno];
if (conn && !conn->killed)
tasklet_schedule(&conn->rx_tsklet);
spin_unlock_irqrestore(&smcd->lock, flags);
}
#endif
int smc_ism_signal_shutdown(struct smc_link_group *lgr)
{ … }
int smc_ism_init(void)
{ … }
void smc_ism_exit(void)
{ … }