1c6ba7c9bSHans Wippel // SPDX-License-Identifier: GPL-2.0 2c6ba7c9bSHans Wippel /* Shared Memory Communications Direct over ISM devices (SMC-D) 3c6ba7c9bSHans Wippel * 4c6ba7c9bSHans Wippel * Functions for ISM device. 5c6ba7c9bSHans Wippel * 6c6ba7c9bSHans Wippel * Copyright IBM Corp. 2018 7c6ba7c9bSHans Wippel */ 8c6ba7c9bSHans Wippel 9c6ba7c9bSHans Wippel #include <linux/spinlock.h> 10c6ba7c9bSHans Wippel #include <linux/slab.h> 11c6ba7c9bSHans Wippel #include <asm/page.h> 12c6ba7c9bSHans Wippel 13c6ba7c9bSHans Wippel #include "smc.h" 14c6ba7c9bSHans Wippel #include "smc_core.h" 15c6ba7c9bSHans Wippel #include "smc_ism.h" 161619f770SHans Wippel #include "smc_pnet.h" 17c6ba7c9bSHans Wippel 18c6ba7c9bSHans Wippel struct smcd_dev_list smcd_dev_list = { 19c6ba7c9bSHans Wippel .list = LIST_HEAD_INIT(smcd_dev_list.list), 20c6ba7c9bSHans Wippel .lock = __SPIN_LOCK_UNLOCKED(smcd_dev_list.lock) 21c6ba7c9bSHans Wippel }; 22c6ba7c9bSHans Wippel 23c6ba7c9bSHans Wippel /* Test if an ISM communication is possible. */ 24c6ba7c9bSHans Wippel int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) 25c6ba7c9bSHans Wippel { 26c6ba7c9bSHans Wippel return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0, 27c6ba7c9bSHans Wippel vlan_id); 28c6ba7c9bSHans Wippel } 29c6ba7c9bSHans Wippel 30c6ba7c9bSHans Wippel int smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos, 31c6ba7c9bSHans Wippel void *data, size_t len) 32c6ba7c9bSHans Wippel { 33c6ba7c9bSHans Wippel int rc; 34c6ba7c9bSHans Wippel 35c6ba7c9bSHans Wippel rc = smcd->ops->move_data(smcd, pos->token, pos->index, pos->signal, 36c6ba7c9bSHans Wippel pos->offset, data, len); 37c6ba7c9bSHans Wippel 38c6ba7c9bSHans Wippel return rc < 0 ? rc : 0; 39c6ba7c9bSHans Wippel } 40c6ba7c9bSHans Wippel 41c6ba7c9bSHans Wippel /* Set a connection using this DMBE. */ 42c6ba7c9bSHans Wippel void smc_ism_set_conn(struct smc_connection *conn) 43c6ba7c9bSHans Wippel { 44c6ba7c9bSHans Wippel unsigned long flags; 45c6ba7c9bSHans Wippel 46c6ba7c9bSHans Wippel spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 47c6ba7c9bSHans Wippel conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = conn; 48c6ba7c9bSHans Wippel spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 49c6ba7c9bSHans Wippel } 50c6ba7c9bSHans Wippel 51c6ba7c9bSHans Wippel /* Unset a connection using this DMBE. */ 52c6ba7c9bSHans Wippel void smc_ism_unset_conn(struct smc_connection *conn) 53c6ba7c9bSHans Wippel { 54c6ba7c9bSHans Wippel unsigned long flags; 55c6ba7c9bSHans Wippel 56c6ba7c9bSHans Wippel if (!conn->rmb_desc) 57c6ba7c9bSHans Wippel return; 58c6ba7c9bSHans Wippel 59c6ba7c9bSHans Wippel spin_lock_irqsave(&conn->lgr->smcd->lock, flags); 60c6ba7c9bSHans Wippel conn->lgr->smcd->conn[conn->rmb_desc->sba_idx] = NULL; 61c6ba7c9bSHans Wippel spin_unlock_irqrestore(&conn->lgr->smcd->lock, flags); 62c6ba7c9bSHans Wippel } 63c6ba7c9bSHans Wippel 64c6ba7c9bSHans Wippel /* Register a VLAN identifier with the ISM device. Use a reference count 65c6ba7c9bSHans Wippel * and add a VLAN identifier only when the first DMB using this VLAN is 66c6ba7c9bSHans Wippel * registered. 67c6ba7c9bSHans Wippel */ 68c6ba7c9bSHans Wippel int smc_ism_get_vlan(struct smcd_dev *smcd, unsigned short vlanid) 69c6ba7c9bSHans Wippel { 70c6ba7c9bSHans Wippel struct smc_ism_vlanid *new_vlan, *vlan; 71c6ba7c9bSHans Wippel unsigned long flags; 72c6ba7c9bSHans Wippel int rc = 0; 73c6ba7c9bSHans Wippel 74c6ba7c9bSHans Wippel if (!vlanid) /* No valid vlan id */ 75c6ba7c9bSHans Wippel return -EINVAL; 76c6ba7c9bSHans Wippel 77c6ba7c9bSHans Wippel /* create new vlan entry, in case we need it */ 78c6ba7c9bSHans Wippel new_vlan = kzalloc(sizeof(*new_vlan), GFP_KERNEL); 79c6ba7c9bSHans Wippel if (!new_vlan) 80c6ba7c9bSHans Wippel return -ENOMEM; 81c6ba7c9bSHans Wippel new_vlan->vlanid = vlanid; 82c6ba7c9bSHans Wippel refcount_set(&new_vlan->refcnt, 1); 83c6ba7c9bSHans Wippel 84c6ba7c9bSHans Wippel /* if there is an existing entry, increase count and return */ 85c6ba7c9bSHans Wippel spin_lock_irqsave(&smcd->lock, flags); 86c6ba7c9bSHans Wippel list_for_each_entry(vlan, &smcd->vlan, list) { 87c6ba7c9bSHans Wippel if (vlan->vlanid == vlanid) { 88c6ba7c9bSHans Wippel refcount_inc(&vlan->refcnt); 89c6ba7c9bSHans Wippel kfree(new_vlan); 90c6ba7c9bSHans Wippel goto out; 91c6ba7c9bSHans Wippel } 92c6ba7c9bSHans Wippel } 93c6ba7c9bSHans Wippel 94c6ba7c9bSHans Wippel /* no existing entry found. 95c6ba7c9bSHans Wippel * add new entry to device; might fail, e.g., if HW limit reached 96c6ba7c9bSHans Wippel */ 97c6ba7c9bSHans Wippel if (smcd->ops->add_vlan_id(smcd, vlanid)) { 98c6ba7c9bSHans Wippel kfree(new_vlan); 99c6ba7c9bSHans Wippel rc = -EIO; 100c6ba7c9bSHans Wippel goto out; 101c6ba7c9bSHans Wippel } 102c6ba7c9bSHans Wippel list_add_tail(&new_vlan->list, &smcd->vlan); 103c6ba7c9bSHans Wippel out: 104c6ba7c9bSHans Wippel spin_unlock_irqrestore(&smcd->lock, flags); 105c6ba7c9bSHans Wippel return rc; 106c6ba7c9bSHans Wippel } 107c6ba7c9bSHans Wippel 108c6ba7c9bSHans Wippel /* Unregister a VLAN identifier with the ISM device. Use a reference count 109c6ba7c9bSHans Wippel * and remove a VLAN identifier only when the last DMB using this VLAN is 110c6ba7c9bSHans Wippel * unregistered. 111c6ba7c9bSHans Wippel */ 112c6ba7c9bSHans Wippel int smc_ism_put_vlan(struct smcd_dev *smcd, unsigned short vlanid) 113c6ba7c9bSHans Wippel { 114c6ba7c9bSHans Wippel struct smc_ism_vlanid *vlan; 115c6ba7c9bSHans Wippel unsigned long flags; 116c6ba7c9bSHans Wippel bool found = false; 117c6ba7c9bSHans Wippel int rc = 0; 118c6ba7c9bSHans Wippel 119c6ba7c9bSHans Wippel if (!vlanid) /* No valid vlan id */ 120c6ba7c9bSHans Wippel return -EINVAL; 121c6ba7c9bSHans Wippel 122c6ba7c9bSHans Wippel spin_lock_irqsave(&smcd->lock, flags); 123c6ba7c9bSHans Wippel list_for_each_entry(vlan, &smcd->vlan, list) { 124c6ba7c9bSHans Wippel if (vlan->vlanid == vlanid) { 125c6ba7c9bSHans Wippel if (!refcount_dec_and_test(&vlan->refcnt)) 126c6ba7c9bSHans Wippel goto out; 127c6ba7c9bSHans Wippel found = true; 128c6ba7c9bSHans Wippel break; 129c6ba7c9bSHans Wippel } 130c6ba7c9bSHans Wippel } 131c6ba7c9bSHans Wippel if (!found) { 132c6ba7c9bSHans Wippel rc = -ENOENT; 133c6ba7c9bSHans Wippel goto out; /* VLAN id not in table */ 134c6ba7c9bSHans Wippel } 135c6ba7c9bSHans Wippel 136c6ba7c9bSHans Wippel /* Found and the last reference just gone */ 137c6ba7c9bSHans Wippel if (smcd->ops->del_vlan_id(smcd, vlanid)) 138c6ba7c9bSHans Wippel rc = -EIO; 139c6ba7c9bSHans Wippel list_del(&vlan->list); 140c6ba7c9bSHans Wippel kfree(vlan); 141c6ba7c9bSHans Wippel out: 142c6ba7c9bSHans Wippel spin_unlock_irqrestore(&smcd->lock, flags); 143c6ba7c9bSHans Wippel return rc; 144c6ba7c9bSHans Wippel } 145c6ba7c9bSHans Wippel 146c6ba7c9bSHans Wippel int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc) 147c6ba7c9bSHans Wippel { 148c6ba7c9bSHans Wippel struct smcd_dmb dmb; 149c6ba7c9bSHans Wippel 150c6ba7c9bSHans Wippel memset(&dmb, 0, sizeof(dmb)); 151c6ba7c9bSHans Wippel dmb.dmb_tok = dmb_desc->token; 152c6ba7c9bSHans Wippel dmb.sba_idx = dmb_desc->sba_idx; 153c6ba7c9bSHans Wippel dmb.cpu_addr = dmb_desc->cpu_addr; 154c6ba7c9bSHans Wippel dmb.dma_addr = dmb_desc->dma_addr; 155c6ba7c9bSHans Wippel dmb.dmb_len = dmb_desc->len; 156c6ba7c9bSHans Wippel return smcd->ops->unregister_dmb(smcd, &dmb); 157c6ba7c9bSHans Wippel } 158c6ba7c9bSHans Wippel 159c6ba7c9bSHans Wippel int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len, 160c6ba7c9bSHans Wippel struct smc_buf_desc *dmb_desc) 161c6ba7c9bSHans Wippel { 162c6ba7c9bSHans Wippel struct smcd_dmb dmb; 163c6ba7c9bSHans Wippel int rc; 164c6ba7c9bSHans Wippel 165c6ba7c9bSHans Wippel memset(&dmb, 0, sizeof(dmb)); 166c6ba7c9bSHans Wippel dmb.dmb_len = dmb_len; 167c6ba7c9bSHans Wippel dmb.sba_idx = dmb_desc->sba_idx; 168c6ba7c9bSHans Wippel dmb.vlan_id = lgr->vlan_id; 169c6ba7c9bSHans Wippel dmb.rgid = lgr->peer_gid; 170c6ba7c9bSHans Wippel rc = lgr->smcd->ops->register_dmb(lgr->smcd, &dmb); 171c6ba7c9bSHans Wippel if (!rc) { 172c6ba7c9bSHans Wippel dmb_desc->sba_idx = dmb.sba_idx; 173c6ba7c9bSHans Wippel dmb_desc->token = dmb.dmb_tok; 174c6ba7c9bSHans Wippel dmb_desc->cpu_addr = dmb.cpu_addr; 175c6ba7c9bSHans Wippel dmb_desc->dma_addr = dmb.dma_addr; 176c6ba7c9bSHans Wippel dmb_desc->len = dmb.dmb_len; 177c6ba7c9bSHans Wippel } 178c6ba7c9bSHans Wippel return rc; 179c6ba7c9bSHans Wippel } 180c6ba7c9bSHans Wippel 181c6ba7c9bSHans Wippel struct smc_ism_event_work { 182c6ba7c9bSHans Wippel struct work_struct work; 183c6ba7c9bSHans Wippel struct smcd_dev *smcd; 184c6ba7c9bSHans Wippel struct smcd_event event; 185c6ba7c9bSHans Wippel }; 186c6ba7c9bSHans Wippel 1870d86caffSUrsula Braun #define ISM_EVENT_REQUEST 0x0001 1880d86caffSUrsula Braun #define ISM_EVENT_RESPONSE 0x0002 1890d86caffSUrsula Braun #define ISM_EVENT_REQUEST_IR 0x00000001 1900512f69eSHans Wippel #define ISM_EVENT_CODE_SHUTDOWN 0x80 1910d86caffSUrsula Braun #define ISM_EVENT_CODE_TESTLINK 0x83 1920d86caffSUrsula Braun 1930512f69eSHans Wippel union smcd_sw_event_info { 1940512f69eSHans Wippel u64 info; 1950512f69eSHans Wippel struct { 1960512f69eSHans Wippel u8 uid[SMC_LGR_ID_SIZE]; 1970512f69eSHans Wippel unsigned short vlan_id; 1980512f69eSHans Wippel u16 code; 1990512f69eSHans Wippel }; 2000512f69eSHans Wippel }; 2010512f69eSHans Wippel 2020d86caffSUrsula Braun static void smcd_handle_sw_event(struct smc_ism_event_work *wrk) 2030d86caffSUrsula Braun { 2040512f69eSHans Wippel union smcd_sw_event_info ev_info; 2050d86caffSUrsula Braun 2060d86caffSUrsula Braun ev_info.info = wrk->event.info; 2070512f69eSHans Wippel switch (wrk->event.code) { 2080512f69eSHans Wippel case ISM_EVENT_CODE_SHUTDOWN: /* Peer shut down DMBs */ 2090512f69eSHans Wippel smc_smcd_terminate(wrk->smcd, wrk->event.tok, ev_info.vlan_id); 2100512f69eSHans Wippel break; 2110512f69eSHans Wippel case ISM_EVENT_CODE_TESTLINK: /* Activity timer */ 2120d86caffSUrsula Braun if (ev_info.code == ISM_EVENT_REQUEST) { 2130d86caffSUrsula Braun ev_info.code = ISM_EVENT_RESPONSE; 2140d86caffSUrsula Braun wrk->smcd->ops->signal_event(wrk->smcd, 2150d86caffSUrsula Braun wrk->event.tok, 2160d86caffSUrsula Braun ISM_EVENT_REQUEST_IR, 2170d86caffSUrsula Braun ISM_EVENT_CODE_TESTLINK, 2180d86caffSUrsula Braun ev_info.info); 2190d86caffSUrsula Braun } 2200d86caffSUrsula Braun break; 2210d86caffSUrsula Braun } 2220d86caffSUrsula Braun } 2230d86caffSUrsula Braun 2240512f69eSHans Wippel int smc_ism_signal_shutdown(struct smc_link_group *lgr) 2250512f69eSHans Wippel { 2260512f69eSHans Wippel int rc; 2270512f69eSHans Wippel union smcd_sw_event_info ev_info; 2280512f69eSHans Wippel 22950c6b20eSUrsula Braun if (lgr->peer_shutdown) 23050c6b20eSUrsula Braun return 0; 23150c6b20eSUrsula Braun 2320512f69eSHans Wippel memcpy(ev_info.uid, lgr->id, SMC_LGR_ID_SIZE); 2330512f69eSHans Wippel ev_info.vlan_id = lgr->vlan_id; 2340512f69eSHans Wippel ev_info.code = ISM_EVENT_REQUEST; 2350512f69eSHans Wippel rc = lgr->smcd->ops->signal_event(lgr->smcd, lgr->peer_gid, 2360512f69eSHans Wippel ISM_EVENT_REQUEST_IR, 2370512f69eSHans Wippel ISM_EVENT_CODE_SHUTDOWN, 2380512f69eSHans Wippel ev_info.info); 2390512f69eSHans Wippel return rc; 2400512f69eSHans Wippel } 2410512f69eSHans Wippel 242c6ba7c9bSHans Wippel /* worker for SMC-D events */ 243c6ba7c9bSHans Wippel static void smc_ism_event_work(struct work_struct *work) 244c6ba7c9bSHans Wippel { 245c6ba7c9bSHans Wippel struct smc_ism_event_work *wrk = 246c6ba7c9bSHans Wippel container_of(work, struct smc_ism_event_work, work); 247c6ba7c9bSHans Wippel 248c6ba7c9bSHans Wippel switch (wrk->event.type) { 249c6ba7c9bSHans Wippel case ISM_EVENT_GID: /* GID event, token is peer GID */ 2500512f69eSHans Wippel smc_smcd_terminate(wrk->smcd, wrk->event.tok, VLAN_VID_MASK); 251c6ba7c9bSHans Wippel break; 252c6ba7c9bSHans Wippel case ISM_EVENT_DMB: 253c6ba7c9bSHans Wippel break; 2540d86caffSUrsula Braun case ISM_EVENT_SWR: /* Software defined event */ 2550d86caffSUrsula Braun smcd_handle_sw_event(wrk); 2560d86caffSUrsula Braun break; 257c6ba7c9bSHans Wippel } 258c6ba7c9bSHans Wippel kfree(wrk); 259c6ba7c9bSHans Wippel } 260c6ba7c9bSHans Wippel 261c6ba7c9bSHans Wippel static void smcd_release(struct device *dev) 262c6ba7c9bSHans Wippel { 263c6ba7c9bSHans Wippel struct smcd_dev *smcd = container_of(dev, struct smcd_dev, dev); 264c6ba7c9bSHans Wippel 265c6ba7c9bSHans Wippel kfree(smcd->conn); 266c6ba7c9bSHans Wippel kfree(smcd); 267c6ba7c9bSHans Wippel } 268c6ba7c9bSHans Wippel 269c6ba7c9bSHans Wippel struct smcd_dev *smcd_alloc_dev(struct device *parent, const char *name, 270c6ba7c9bSHans Wippel const struct smcd_ops *ops, int max_dmbs) 271c6ba7c9bSHans Wippel { 272c6ba7c9bSHans Wippel struct smcd_dev *smcd; 273c6ba7c9bSHans Wippel 274c6ba7c9bSHans Wippel smcd = kzalloc(sizeof(*smcd), GFP_KERNEL); 275c6ba7c9bSHans Wippel if (!smcd) 276c6ba7c9bSHans Wippel return NULL; 277c6ba7c9bSHans Wippel smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *), 278c6ba7c9bSHans Wippel GFP_KERNEL); 279c6ba7c9bSHans Wippel if (!smcd->conn) { 280c6ba7c9bSHans Wippel kfree(smcd); 281c6ba7c9bSHans Wippel return NULL; 282c6ba7c9bSHans Wippel } 283c6ba7c9bSHans Wippel 284c6ba7c9bSHans Wippel smcd->dev.parent = parent; 285c6ba7c9bSHans Wippel smcd->dev.release = smcd_release; 286c6ba7c9bSHans Wippel device_initialize(&smcd->dev); 287c6ba7c9bSHans Wippel dev_set_name(&smcd->dev, name); 288c6ba7c9bSHans Wippel smcd->ops = ops; 2891619f770SHans Wippel smc_pnetid_by_dev_port(parent, 0, smcd->pnetid); 290c6ba7c9bSHans Wippel 291c6ba7c9bSHans Wippel spin_lock_init(&smcd->lock); 292a0a62ee1SUrsula Braun spin_lock_init(&smcd->lgr_lock); 293c6ba7c9bSHans Wippel INIT_LIST_HEAD(&smcd->vlan); 294a2351c5dSUrsula Braun INIT_LIST_HEAD(&smcd->lgr_list); 295c6ba7c9bSHans Wippel smcd->event_wq = alloc_ordered_workqueue("ism_evt_wq-%s)", 296c6ba7c9bSHans Wippel WQ_MEM_RECLAIM, name); 297e183d4e4SKangjie Lu if (!smcd->event_wq) { 298e183d4e4SKangjie Lu kfree(smcd->conn); 299e183d4e4SKangjie Lu kfree(smcd); 300e183d4e4SKangjie Lu return NULL; 301e183d4e4SKangjie Lu } 302c6ba7c9bSHans Wippel return smcd; 303c6ba7c9bSHans Wippel } 304c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_alloc_dev); 305c6ba7c9bSHans Wippel 306c6ba7c9bSHans Wippel int smcd_register_dev(struct smcd_dev *smcd) 307c6ba7c9bSHans Wippel { 308c6ba7c9bSHans Wippel spin_lock(&smcd_dev_list.lock); 309c6ba7c9bSHans Wippel list_add_tail(&smcd->list, &smcd_dev_list.list); 310c6ba7c9bSHans Wippel spin_unlock(&smcd_dev_list.lock); 311c6ba7c9bSHans Wippel 312c6ba7c9bSHans Wippel return device_add(&smcd->dev); 313c6ba7c9bSHans Wippel } 314c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_register_dev); 315c6ba7c9bSHans Wippel 316c6ba7c9bSHans Wippel void smcd_unregister_dev(struct smcd_dev *smcd) 317c6ba7c9bSHans Wippel { 318c6ba7c9bSHans Wippel spin_lock(&smcd_dev_list.lock); 31950c6b20eSUrsula Braun list_del_init(&smcd->list); 320c6ba7c9bSHans Wippel spin_unlock(&smcd_dev_list.lock); 321c3d9494eSUrsula Braun smcd->going_away = 1; 32250c6b20eSUrsula Braun smc_smcd_terminate(smcd, 0, VLAN_VID_MASK); 323c6ba7c9bSHans Wippel flush_workqueue(smcd->event_wq); 324c6ba7c9bSHans Wippel destroy_workqueue(smcd->event_wq); 325c6ba7c9bSHans Wippel 326c6ba7c9bSHans Wippel device_del(&smcd->dev); 327c6ba7c9bSHans Wippel } 328c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_unregister_dev); 329c6ba7c9bSHans Wippel 330c6ba7c9bSHans Wippel void smcd_free_dev(struct smcd_dev *smcd) 331c6ba7c9bSHans Wippel { 332c6ba7c9bSHans Wippel put_device(&smcd->dev); 333c6ba7c9bSHans Wippel } 334c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_free_dev); 335c6ba7c9bSHans Wippel 336c6ba7c9bSHans Wippel /* SMCD Device event handler. Called from ISM device interrupt handler. 337c6ba7c9bSHans Wippel * Parameters are smcd device pointer, 338c6ba7c9bSHans Wippel * - event->type (0 --> DMB, 1 --> GID), 339c6ba7c9bSHans Wippel * - event->code (event code), 340c6ba7c9bSHans Wippel * - event->tok (either DMB token when event type 0, or GID when event type 1) 341c6ba7c9bSHans Wippel * - event->time (time of day) 342c6ba7c9bSHans Wippel * - event->info (debug info). 343c6ba7c9bSHans Wippel * 344c6ba7c9bSHans Wippel * Context: 345c6ba7c9bSHans Wippel * - Function called in IRQ context from ISM device driver event handler. 346c6ba7c9bSHans Wippel */ 347c6ba7c9bSHans Wippel void smcd_handle_event(struct smcd_dev *smcd, struct smcd_event *event) 348c6ba7c9bSHans Wippel { 349c6ba7c9bSHans Wippel struct smc_ism_event_work *wrk; 350c6ba7c9bSHans Wippel 351c3d9494eSUrsula Braun if (smcd->going_away) 352c3d9494eSUrsula Braun return; 353c6ba7c9bSHans Wippel /* copy event to event work queue, and let it be handled there */ 354c6ba7c9bSHans Wippel wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); 355c6ba7c9bSHans Wippel if (!wrk) 356c6ba7c9bSHans Wippel return; 357c6ba7c9bSHans Wippel INIT_WORK(&wrk->work, smc_ism_event_work); 358c6ba7c9bSHans Wippel wrk->smcd = smcd; 359c6ba7c9bSHans Wippel wrk->event = *event; 360c6ba7c9bSHans Wippel queue_work(smcd->event_wq, &wrk->work); 361c6ba7c9bSHans Wippel } 362c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_handle_event); 363c6ba7c9bSHans Wippel 364c6ba7c9bSHans Wippel /* SMCD Device interrupt handler. Called from ISM device interrupt handler. 365c6ba7c9bSHans Wippel * Parameters are smcd device pointer and DMB number. Find the connection and 366c6ba7c9bSHans Wippel * schedule the tasklet for this connection. 367c6ba7c9bSHans Wippel * 368c6ba7c9bSHans Wippel * Context: 369c6ba7c9bSHans Wippel * - Function called in IRQ context from ISM device driver IRQ handler. 370c6ba7c9bSHans Wippel */ 371c6ba7c9bSHans Wippel void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno) 372c6ba7c9bSHans Wippel { 373be244f28SHans Wippel struct smc_connection *conn = NULL; 374be244f28SHans Wippel unsigned long flags; 375be244f28SHans Wippel 376be244f28SHans Wippel spin_lock_irqsave(&smcd->lock, flags); 377be244f28SHans Wippel conn = smcd->conn[dmbno]; 378be244f28SHans Wippel if (conn) 379be244f28SHans Wippel tasklet_schedule(&conn->rx_tsklet); 380be244f28SHans Wippel spin_unlock_irqrestore(&smcd->lock, flags); 381c6ba7c9bSHans Wippel } 382c6ba7c9bSHans Wippel EXPORT_SYMBOL_GPL(smcd_handle_irq); 383