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