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