162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Shared Memory Communications Direct over ISM devices (SMC-D) 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Functions for ISM device. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright IBM Corp. 2018 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/if_vlan.h> 1062306a36Sopenharmony_ci#include <linux/spinlock.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <asm/page.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "smc.h" 1662306a36Sopenharmony_ci#include "smc_core.h" 1762306a36Sopenharmony_ci#include "smc_ism.h" 1862306a36Sopenharmony_ci#include "smc_pnet.h" 1962306a36Sopenharmony_ci#include "smc_netlink.h" 2062306a36Sopenharmony_ci#include "linux/ism.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct smcd_dev_list smcd_dev_list = { 2362306a36Sopenharmony_ci .list = LIST_HEAD_INIT(smcd_dev_list.list), 2462306a36Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(smcd_dev_list.mutex) 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic bool smc_ism_v2_capable; 2862306a36Sopenharmony_cistatic u8 smc_ism_v2_system_eid[SMC_MAX_EID_LEN]; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 3162306a36Sopenharmony_cistatic void smcd_register_dev(struct ism_dev *ism); 3262306a36Sopenharmony_cistatic void smcd_unregister_dev(struct ism_dev *ism); 3362306a36Sopenharmony_cistatic void smcd_handle_event(struct ism_dev *ism, struct ism_event *event); 3462306a36Sopenharmony_cistatic void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno, 3562306a36Sopenharmony_ci u16 dmbemask); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct ism_client smc_ism_client = { 3862306a36Sopenharmony_ci .name = "SMC-D", 3962306a36Sopenharmony_ci .add = smcd_register_dev, 4062306a36Sopenharmony_ci .remove = smcd_unregister_dev, 4162306a36Sopenharmony_ci .handle_event = smcd_handle_event, 4262306a36Sopenharmony_ci .handle_irq = smcd_handle_irq, 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci#endif 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Test if an ISM communication is possible - same CPC */ 4762306a36Sopenharmony_ciint smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0, 5062306a36Sopenharmony_ci vlan_id); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_civoid smc_ism_get_system_eid(u8 **eid) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci if (!smc_ism_v2_capable) 5662306a36Sopenharmony_ci *eid = NULL; 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci *eid = smc_ism_v2_system_eid; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciu16 smc_ism_get_chid(struct smcd_dev *smcd) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return smcd->ops->get_chid(smcd); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* HW supports ISM V2 and thus System EID is defined */ 6762306a36Sopenharmony_cibool smc_ism_is_v2_capable(void) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci return smc_ism_v2_capable; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Set a connection using this DMBE. */ 7362306a36Sopenharmony_civoid smc_ism_set_conn(struct smc_connection *conn) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned long flags; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 7862306a36Sopenharmony_ci conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = conn; 7962306a36Sopenharmony_ci spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Unset a connection using this DMBE. */ 8362306a36Sopenharmony_civoid smc_ism_unset_conn(struct smc_connection *conn) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned long flags; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!conn->rmb_desc) 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 9162306a36Sopenharmony_ci conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = NULL; 9262306a36Sopenharmony_ci spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Register a VLAN identifier with the ISM device. Use a reference count 9662306a36Sopenharmony_ci * and add a VLAN identifier only when the first DMB using this VLAN is 9762306a36Sopenharmony_ci * registered. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ciint smc_ism_get_vlan(struct smcd_dev *smcd, unsigned short vlanid) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct smc_ism_vlanid *new_vlan, *vlan; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci int rc = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!vlanid) /* No valid vlan id */ 10662306a36Sopenharmony_ci return -EINVAL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* create new vlan entry, in case we need it */ 10962306a36Sopenharmony_ci new_vlan = kzalloc(sizeof(*new_vlan), GFP_KERNEL); 11062306a36Sopenharmony_ci if (!new_vlan) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci new_vlan->vlanid = vlanid; 11362306a36Sopenharmony_ci refcount_set(&new_vlan->refcnt, 1); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* if there is an existing entry, increase count and return */ 11662306a36Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 11762306a36Sopenharmony_ci list_for_each_entry(vlan, &smcd->vlan, list) { 11862306a36Sopenharmony_ci if (vlan->vlanid == vlanid) { 11962306a36Sopenharmony_ci refcount_inc(&vlan->refcnt); 12062306a36Sopenharmony_ci kfree(new_vlan); 12162306a36Sopenharmony_ci goto out; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* no existing entry found. 12662306a36Sopenharmony_ci * add new entry to device; might fail, e.g., if HW limit reached 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci if (smcd->ops->add_vlan_id(smcd, vlanid)) { 12962306a36Sopenharmony_ci kfree(new_vlan); 13062306a36Sopenharmony_ci rc = -EIO; 13162306a36Sopenharmony_ci goto out; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci list_add_tail(&new_vlan->list, &smcd->vlan); 13462306a36Sopenharmony_ciout: 13562306a36Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 13662306a36Sopenharmony_ci return rc; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* Unregister a VLAN identifier with the ISM device. Use a reference count 14062306a36Sopenharmony_ci * and remove a VLAN identifier only when the last DMB using this VLAN is 14162306a36Sopenharmony_ci * unregistered. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ciint smc_ism_put_vlan(struct smcd_dev *smcd, unsigned short vlanid) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct smc_ism_vlanid *vlan; 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci bool found = false; 14862306a36Sopenharmony_ci int rc = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!vlanid) /* No valid vlan id */ 15162306a36Sopenharmony_ci return -EINVAL; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 15462306a36Sopenharmony_ci list_for_each_entry(vlan, &smcd->vlan, list) { 15562306a36Sopenharmony_ci if (vlan->vlanid == vlanid) { 15662306a36Sopenharmony_ci if (!refcount_dec_and_test(&vlan->refcnt)) 15762306a36Sopenharmony_ci goto out; 15862306a36Sopenharmony_ci found = true; 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci if (!found) { 16362306a36Sopenharmony_ci rc = -ENOENT; 16462306a36Sopenharmony_ci goto out; /* VLAN id not in table */ 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Found and the last reference just gone */ 16862306a36Sopenharmony_ci if (smcd->ops->del_vlan_id(smcd, vlanid)) 16962306a36Sopenharmony_ci rc = -EIO; 17062306a36Sopenharmony_ci list_del(&vlan->list); 17162306a36Sopenharmony_ci kfree(vlan); 17262306a36Sopenharmony_ciout: 17362306a36Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 17462306a36Sopenharmony_ci return rc; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct smcd_dmb dmb; 18062306a36Sopenharmony_ci int rc = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!dmb_desc->dma_addr) 18362306a36Sopenharmony_ci return rc; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci memset(&dmb, 0, sizeof(dmb)); 18662306a36Sopenharmony_ci dmb.dmb_tok = dmb_desc->token; 18762306a36Sopenharmony_ci dmb.sba_idx = dmb_desc->sba_idx; 18862306a36Sopenharmony_ci dmb.cpu_addr = dmb_desc->cpu_addr; 18962306a36Sopenharmony_ci dmb.dma_addr = dmb_desc->dma_addr; 19062306a36Sopenharmony_ci dmb.dmb_len = dmb_desc->len; 19162306a36Sopenharmony_ci rc = smcd->ops->unregister_dmb(smcd, &dmb); 19262306a36Sopenharmony_ci if (!rc || rc == ISM_ERROR) { 19362306a36Sopenharmony_ci dmb_desc->cpu_addr = NULL; 19462306a36Sopenharmony_ci dmb_desc->dma_addr = 0; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return rc; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len, 20162306a36Sopenharmony_ci struct smc_buf_desc *dmb_desc) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 20462306a36Sopenharmony_ci struct smcd_dmb dmb; 20562306a36Sopenharmony_ci int rc; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci memset(&dmb, 0, sizeof(dmb)); 20862306a36Sopenharmony_ci dmb.dmb_len = dmb_len; 20962306a36Sopenharmony_ci dmb.sba_idx = dmb_desc->sba_idx; 21062306a36Sopenharmony_ci dmb.vlan_id = lgr->vlan_id; 21162306a36Sopenharmony_ci dmb.rgid = lgr->peer_gid; 21262306a36Sopenharmony_ci rc = lgr->smcd->ops->register_dmb(lgr->smcd, &dmb, &smc_ism_client); 21362306a36Sopenharmony_ci if (!rc) { 21462306a36Sopenharmony_ci dmb_desc->sba_idx = dmb.sba_idx; 21562306a36Sopenharmony_ci dmb_desc->token = dmb.dmb_tok; 21662306a36Sopenharmony_ci dmb_desc->cpu_addr = dmb.cpu_addr; 21762306a36Sopenharmony_ci dmb_desc->dma_addr = dmb.dma_addr; 21862306a36Sopenharmony_ci dmb_desc->len = dmb.dmb_len; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci return rc; 22162306a36Sopenharmony_ci#else 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci#endif 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int smc_nl_handle_smcd_dev(struct smcd_dev *smcd, 22762306a36Sopenharmony_ci struct sk_buff *skb, 22862306a36Sopenharmony_ci struct netlink_callback *cb) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci char smc_pnet[SMC_MAX_PNETID_LEN + 1]; 23162306a36Sopenharmony_ci struct smc_pci_dev smc_pci_dev; 23262306a36Sopenharmony_ci struct nlattr *port_attrs; 23362306a36Sopenharmony_ci struct nlattr *attrs; 23462306a36Sopenharmony_ci struct ism_dev *ism; 23562306a36Sopenharmony_ci int use_cnt = 0; 23662306a36Sopenharmony_ci void *nlh; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ism = smcd->priv; 23962306a36Sopenharmony_ci nlh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 24062306a36Sopenharmony_ci &smc_gen_nl_family, NLM_F_MULTI, 24162306a36Sopenharmony_ci SMC_NETLINK_GET_DEV_SMCD); 24262306a36Sopenharmony_ci if (!nlh) 24362306a36Sopenharmony_ci goto errmsg; 24462306a36Sopenharmony_ci attrs = nla_nest_start(skb, SMC_GEN_DEV_SMCD); 24562306a36Sopenharmony_ci if (!attrs) 24662306a36Sopenharmony_ci goto errout; 24762306a36Sopenharmony_ci use_cnt = atomic_read(&smcd->lgr_cnt); 24862306a36Sopenharmony_ci if (nla_put_u32(skb, SMC_NLA_DEV_USE_CNT, use_cnt)) 24962306a36Sopenharmony_ci goto errattr; 25062306a36Sopenharmony_ci if (nla_put_u8(skb, SMC_NLA_DEV_IS_CRIT, use_cnt > 0)) 25162306a36Sopenharmony_ci goto errattr; 25262306a36Sopenharmony_ci memset(&smc_pci_dev, 0, sizeof(smc_pci_dev)); 25362306a36Sopenharmony_ci smc_set_pci_values(to_pci_dev(ism->dev.parent), &smc_pci_dev); 25462306a36Sopenharmony_ci if (nla_put_u32(skb, SMC_NLA_DEV_PCI_FID, smc_pci_dev.pci_fid)) 25562306a36Sopenharmony_ci goto errattr; 25662306a36Sopenharmony_ci if (nla_put_u16(skb, SMC_NLA_DEV_PCI_CHID, smc_pci_dev.pci_pchid)) 25762306a36Sopenharmony_ci goto errattr; 25862306a36Sopenharmony_ci if (nla_put_u16(skb, SMC_NLA_DEV_PCI_VENDOR, smc_pci_dev.pci_vendor)) 25962306a36Sopenharmony_ci goto errattr; 26062306a36Sopenharmony_ci if (nla_put_u16(skb, SMC_NLA_DEV_PCI_DEVICE, smc_pci_dev.pci_device)) 26162306a36Sopenharmony_ci goto errattr; 26262306a36Sopenharmony_ci if (nla_put_string(skb, SMC_NLA_DEV_PCI_ID, smc_pci_dev.pci_id)) 26362306a36Sopenharmony_ci goto errattr; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci port_attrs = nla_nest_start(skb, SMC_NLA_DEV_PORT); 26662306a36Sopenharmony_ci if (!port_attrs) 26762306a36Sopenharmony_ci goto errattr; 26862306a36Sopenharmony_ci if (nla_put_u8(skb, SMC_NLA_DEV_PORT_PNET_USR, smcd->pnetid_by_user)) 26962306a36Sopenharmony_ci goto errportattr; 27062306a36Sopenharmony_ci memcpy(smc_pnet, smcd->pnetid, SMC_MAX_PNETID_LEN); 27162306a36Sopenharmony_ci smc_pnet[SMC_MAX_PNETID_LEN] = 0; 27262306a36Sopenharmony_ci if (nla_put_string(skb, SMC_NLA_DEV_PORT_PNETID, smc_pnet)) 27362306a36Sopenharmony_ci goto errportattr; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci nla_nest_end(skb, port_attrs); 27662306a36Sopenharmony_ci nla_nest_end(skb, attrs); 27762306a36Sopenharmony_ci genlmsg_end(skb, nlh); 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cierrportattr: 28162306a36Sopenharmony_ci nla_nest_cancel(skb, port_attrs); 28262306a36Sopenharmony_cierrattr: 28362306a36Sopenharmony_ci nla_nest_cancel(skb, attrs); 28462306a36Sopenharmony_cierrout: 28562306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 28662306a36Sopenharmony_cierrmsg: 28762306a36Sopenharmony_ci return -EMSGSIZE; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void smc_nl_prep_smcd_dev(struct smcd_dev_list *dev_list, 29162306a36Sopenharmony_ci struct sk_buff *skb, 29262306a36Sopenharmony_ci struct netlink_callback *cb) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); 29562306a36Sopenharmony_ci int snum = cb_ctx->pos[0]; 29662306a36Sopenharmony_ci struct smcd_dev *smcd; 29762306a36Sopenharmony_ci int num = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci mutex_lock(&dev_list->mutex); 30062306a36Sopenharmony_ci list_for_each_entry(smcd, &dev_list->list, list) { 30162306a36Sopenharmony_ci if (num < snum) 30262306a36Sopenharmony_ci goto next; 30362306a36Sopenharmony_ci if (smc_nl_handle_smcd_dev(smcd, skb, cb)) 30462306a36Sopenharmony_ci goto errout; 30562306a36Sopenharmony_cinext: 30662306a36Sopenharmony_ci num++; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_cierrout: 30962306a36Sopenharmony_ci mutex_unlock(&dev_list->mutex); 31062306a36Sopenharmony_ci cb_ctx->pos[0] = num; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciint smcd_nl_get_device(struct sk_buff *skb, struct netlink_callback *cb) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci smc_nl_prep_smcd_dev(&smcd_dev_list, skb, cb); 31662306a36Sopenharmony_ci return skb->len; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 32062306a36Sopenharmony_cistruct smc_ism_event_work { 32162306a36Sopenharmony_ci struct work_struct work; 32262306a36Sopenharmony_ci struct smcd_dev *smcd; 32362306a36Sopenharmony_ci struct ism_event event; 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci#define ISM_EVENT_REQUEST 0x0001 32762306a36Sopenharmony_ci#define ISM_EVENT_RESPONSE 0x0002 32862306a36Sopenharmony_ci#define ISM_EVENT_REQUEST_IR 0x00000001 32962306a36Sopenharmony_ci#define ISM_EVENT_CODE_SHUTDOWN 0x80 33062306a36Sopenharmony_ci#define ISM_EVENT_CODE_TESTLINK 0x83 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciunion smcd_sw_event_info { 33362306a36Sopenharmony_ci u64 info; 33462306a36Sopenharmony_ci struct { 33562306a36Sopenharmony_ci u8 uid[SMC_LGR_ID_SIZE]; 33662306a36Sopenharmony_ci unsigned short vlan_id; 33762306a36Sopenharmony_ci u16 code; 33862306a36Sopenharmony_ci }; 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void smcd_handle_sw_event(struct smc_ism_event_work *wrk) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci union smcd_sw_event_info ev_info; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ev_info.info = wrk->event.info; 34662306a36Sopenharmony_ci switch (wrk->event.code) { 34762306a36Sopenharmony_ci case ISM_EVENT_CODE_SHUTDOWN: /* Peer shut down DMBs */ 34862306a36Sopenharmony_ci smc_smcd_terminate(wrk->smcd, wrk->event.tok, ev_info.vlan_id); 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci case ISM_EVENT_CODE_TESTLINK: /* Activity timer */ 35162306a36Sopenharmony_ci if (ev_info.code == ISM_EVENT_REQUEST) { 35262306a36Sopenharmony_ci ev_info.code = ISM_EVENT_RESPONSE; 35362306a36Sopenharmony_ci wrk->smcd->ops->signal_event(wrk->smcd, 35462306a36Sopenharmony_ci wrk->event.tok, 35562306a36Sopenharmony_ci ISM_EVENT_REQUEST_IR, 35662306a36Sopenharmony_ci ISM_EVENT_CODE_TESTLINK, 35762306a36Sopenharmony_ci ev_info.info); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* worker for SMC-D events */ 36462306a36Sopenharmony_cistatic void smc_ism_event_work(struct work_struct *work) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct smc_ism_event_work *wrk = 36762306a36Sopenharmony_ci container_of(work, struct smc_ism_event_work, work); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci switch (wrk->event.type) { 37062306a36Sopenharmony_ci case ISM_EVENT_GID: /* GID event, token is peer GID */ 37162306a36Sopenharmony_ci smc_smcd_terminate(wrk->smcd, wrk->event.tok, VLAN_VID_MASK); 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case ISM_EVENT_DMB: 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case ISM_EVENT_SWR: /* Software defined event */ 37662306a36Sopenharmony_ci smcd_handle_sw_event(wrk); 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci kfree(wrk); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic struct smcd_dev *smcd_alloc_dev(struct device *parent, const char *name, 38362306a36Sopenharmony_ci const struct smcd_ops *ops, int max_dmbs) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct smcd_dev *smcd; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci smcd = devm_kzalloc(parent, sizeof(*smcd), GFP_KERNEL); 38862306a36Sopenharmony_ci if (!smcd) 38962306a36Sopenharmony_ci return NULL; 39062306a36Sopenharmony_ci smcd->conn = devm_kcalloc(parent, max_dmbs, 39162306a36Sopenharmony_ci sizeof(struct smc_connection *), GFP_KERNEL); 39262306a36Sopenharmony_ci if (!smcd->conn) 39362306a36Sopenharmony_ci return NULL; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", 39662306a36Sopenharmony_ci WQ_MEM_RECLAIM, name); 39762306a36Sopenharmony_ci if (!smcd->event_wq) 39862306a36Sopenharmony_ci return NULL; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci smcd->ops = ops; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_lock_init(&smcd->lock); 40362306a36Sopenharmony_ci spin_lock_init(&smcd->lgr_lock); 40462306a36Sopenharmony_ci INIT_LIST_HEAD(&smcd->vlan); 40562306a36Sopenharmony_ci INIT_LIST_HEAD(&smcd->lgr_list); 40662306a36Sopenharmony_ci init_waitqueue_head(&smcd->lgrs_deleted); 40762306a36Sopenharmony_ci return smcd; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void smcd_register_dev(struct ism_dev *ism) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci const struct smcd_ops *ops = ism_get_smcd_ops(); 41362306a36Sopenharmony_ci struct smcd_dev *smcd; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!ops) 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci smcd = smcd_alloc_dev(&ism->pdev->dev, dev_name(&ism->pdev->dev), ops, 41962306a36Sopenharmony_ci ISM_NR_DMBS); 42062306a36Sopenharmony_ci if (!smcd) 42162306a36Sopenharmony_ci return; 42262306a36Sopenharmony_ci smcd->priv = ism; 42362306a36Sopenharmony_ci ism_set_priv(ism, &smc_ism_client, smcd); 42462306a36Sopenharmony_ci if (smc_pnetid_by_dev_port(&ism->pdev->dev, 0, smcd->pnetid)) 42562306a36Sopenharmony_ci smc_pnetid_by_table_smcd(smcd); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 42862306a36Sopenharmony_ci if (list_empty(&smcd_dev_list.list)) { 42962306a36Sopenharmony_ci u8 *system_eid = NULL; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci system_eid = smcd->ops->get_system_eid(); 43262306a36Sopenharmony_ci if (smcd->ops->supports_v2()) { 43362306a36Sopenharmony_ci smc_ism_v2_capable = true; 43462306a36Sopenharmony_ci memcpy(smc_ism_v2_system_eid, system_eid, 43562306a36Sopenharmony_ci SMC_MAX_EID_LEN); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci /* sort list: devices without pnetid before devices with pnetid */ 43962306a36Sopenharmony_ci if (smcd->pnetid[0]) 44062306a36Sopenharmony_ci list_add_tail(&smcd->list, &smcd_dev_list.list); 44162306a36Sopenharmony_ci else 44262306a36Sopenharmony_ci list_add(&smcd->list, &smcd_dev_list.list); 44362306a36Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n", 44662306a36Sopenharmony_ci dev_name(&ism->dev), smcd->pnetid, 44762306a36Sopenharmony_ci smcd->pnetid_by_user ? " (user defined)" : ""); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void smcd_unregister_dev(struct ism_dev *ism) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci pr_warn_ratelimited("smc: removing smcd device %s\n", 45762306a36Sopenharmony_ci dev_name(&ism->dev)); 45862306a36Sopenharmony_ci smcd->going_away = 1; 45962306a36Sopenharmony_ci smc_smcd_terminate_all(smcd); 46062306a36Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 46162306a36Sopenharmony_ci list_del_init(&smcd->list); 46262306a36Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 46362306a36Sopenharmony_ci destroy_workqueue(smcd->event_wq); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* SMCD Device event handler. Called from ISM device interrupt handler. 46762306a36Sopenharmony_ci * Parameters are ism device pointer, 46862306a36Sopenharmony_ci * - event->type (0 --> DMB, 1 --> GID), 46962306a36Sopenharmony_ci * - event->code (event code), 47062306a36Sopenharmony_ci * - event->tok (either DMB token when event type 0, or GID when event type 1) 47162306a36Sopenharmony_ci * - event->time (time of day) 47262306a36Sopenharmony_ci * - event->info (debug info). 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Context: 47562306a36Sopenharmony_ci * - Function called in IRQ context from ISM device driver event handler. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_cistatic void smcd_handle_event(struct ism_dev *ism, struct ism_event *event) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client); 48062306a36Sopenharmony_ci struct smc_ism_event_work *wrk; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (smcd->going_away) 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci /* copy event to event work queue, and let it be handled there */ 48562306a36Sopenharmony_ci wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); 48662306a36Sopenharmony_ci if (!wrk) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci INIT_WORK(&wrk->work, smc_ism_event_work); 48962306a36Sopenharmony_ci wrk->smcd = smcd; 49062306a36Sopenharmony_ci wrk->event = *event; 49162306a36Sopenharmony_ci queue_work(smcd->event_wq, &wrk->work); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* SMCD Device interrupt handler. Called from ISM device interrupt handler. 49562306a36Sopenharmony_ci * Parameters are the ism device pointer, DMB number, and the DMBE bitmask. 49662306a36Sopenharmony_ci * Find the connection and schedule the tasklet for this connection. 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Context: 49962306a36Sopenharmony_ci * - Function called in IRQ context from ISM device driver IRQ handler. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_cistatic void smcd_handle_irq(struct ism_dev *ism, unsigned int dmbno, 50262306a36Sopenharmony_ci u16 dmbemask) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct smcd_dev *smcd = ism_get_priv(ism, &smc_ism_client); 50562306a36Sopenharmony_ci struct smc_connection *conn = NULL; 50662306a36Sopenharmony_ci unsigned long flags; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci spin_lock_irqsave(&smcd->lock, flags); 50962306a36Sopenharmony_ci conn = smcd->conn[dmbno]; 51062306a36Sopenharmony_ci if (conn && !conn->killed) 51162306a36Sopenharmony_ci tasklet_schedule(&conn->rx_tsklet); 51262306a36Sopenharmony_ci spin_unlock_irqrestore(&smcd->lock, flags); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci#endif 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciint smc_ism_signal_shutdown(struct smc_link_group *lgr) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int rc = 0; 51962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 52062306a36Sopenharmony_ci union smcd_sw_event_info ev_info; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (lgr->peer_shutdown) 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci memcpy(ev_info.uid, lgr->id, SMC_LGR_ID_SIZE); 52662306a36Sopenharmony_ci ev_info.vlan_id = lgr->vlan_id; 52762306a36Sopenharmony_ci ev_info.code = ISM_EVENT_REQUEST; 52862306a36Sopenharmony_ci rc = lgr->smcd->ops->signal_event(lgr->smcd, lgr->peer_gid, 52962306a36Sopenharmony_ci ISM_EVENT_REQUEST_IR, 53062306a36Sopenharmony_ci ISM_EVENT_CODE_SHUTDOWN, 53162306a36Sopenharmony_ci ev_info.info); 53262306a36Sopenharmony_ci#endif 53362306a36Sopenharmony_ci return rc; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ciint smc_ism_init(void) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci int rc = 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 54162306a36Sopenharmony_ci smc_ism_v2_capable = false; 54262306a36Sopenharmony_ci memset(smc_ism_v2_system_eid, 0, SMC_MAX_EID_LEN); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci rc = ism_register_client(&smc_ism_client); 54562306a36Sopenharmony_ci#endif 54662306a36Sopenharmony_ci return rc; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_civoid smc_ism_exit(void) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISM) 55262306a36Sopenharmony_ci ism_unregister_client(&smc_ism_client); 55362306a36Sopenharmony_ci#endif 55462306a36Sopenharmony_ci} 555