linux/net/smc/smc_llc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  Shared Memory Communications over RDMA (SMC-R) and RoCE
   4 *
   5 *  Link Layer Control (LLC)
   6 *
   7 *  Copyright IBM Corp. 2016
   8 *
   9 *  Author(s):  Klaus Wacker <Klaus.Wacker@de.ibm.com>
  10 *              Ursula Braun <ubraun@linux.vnet.ibm.com>
  11 */
  12
  13#include <net/tcp.h>
  14#include <rdma/ib_verbs.h>
  15
  16#include "smc.h"
  17#include "smc_core.h"
  18#include "smc_clc.h"
  19#include "smc_llc.h"
  20
  21#define SMC_LLC_DATA_LEN                40
  22
  23struct smc_llc_hdr {
  24        struct smc_wr_rx_hdr common;
  25        u8 length;      /* 44 */
  26#if defined(__BIG_ENDIAN_BITFIELD)
  27        u8 reserved:4,
  28           add_link_rej_rsn:4;
  29#elif defined(__LITTLE_ENDIAN_BITFIELD)
  30        u8 add_link_rej_rsn:4,
  31           reserved:4;
  32#endif
  33        u8 flags;
  34};
  35
  36#define SMC_LLC_FLAG_NO_RMBE_EYEC       0x03
  37
  38struct smc_llc_msg_confirm_link {       /* type 0x01 */
  39        struct smc_llc_hdr hd;
  40        u8 sender_mac[ETH_ALEN];
  41        u8 sender_gid[SMC_GID_SIZE];
  42        u8 sender_qp_num[3];
  43        u8 link_num;
  44        u8 link_uid[SMC_LGR_ID_SIZE];
  45        u8 max_links;
  46        u8 reserved[9];
  47};
  48
  49#define SMC_LLC_FLAG_ADD_LNK_REJ        0x40
  50#define SMC_LLC_REJ_RSN_NO_ALT_PATH     1
  51
  52#define SMC_LLC_ADD_LNK_MAX_LINKS       2
  53
  54struct smc_llc_msg_add_link {           /* type 0x02 */
  55        struct smc_llc_hdr hd;
  56        u8 sender_mac[ETH_ALEN];
  57        u8 reserved2[2];
  58        u8 sender_gid[SMC_GID_SIZE];
  59        u8 sender_qp_num[3];
  60        u8 link_num;
  61        u8 flags2;      /* QP mtu */
  62        u8 initial_psn[3];
  63        u8 reserved[8];
  64};
  65
  66#define SMC_LLC_FLAG_DEL_LINK_ALL       0x40
  67#define SMC_LLC_FLAG_DEL_LINK_ORDERLY   0x20
  68
  69struct smc_llc_msg_del_link {           /* type 0x04 */
  70        struct smc_llc_hdr hd;
  71        u8 link_num;
  72        __be32 reason;
  73        u8 reserved[35];
  74} __packed;                     /* format defined in RFC7609 */
  75
  76struct smc_llc_msg_test_link {          /* type 0x07 */
  77        struct smc_llc_hdr hd;
  78        u8 user_data[16];
  79        u8 reserved[24];
  80};
  81
  82struct smc_rmb_rtoken {
  83        union {
  84                u8 num_rkeys;   /* first rtoken byte of CONFIRM LINK msg */
  85                                /* is actually the num of rtokens, first */
  86                                /* rtoken is always for the current link */
  87                u8 link_id;     /* link id of the rtoken */
  88        };
  89        __be32 rmb_key;
  90        __be64 rmb_vaddr;
  91} __packed;                     /* format defined in RFC7609 */
  92
  93#define SMC_LLC_RKEYS_PER_MSG   3
  94
  95struct smc_llc_msg_confirm_rkey {       /* type 0x06 */
  96        struct smc_llc_hdr hd;
  97        struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
  98        u8 reserved;
  99};
 100
 101struct smc_llc_msg_confirm_rkey_cont {  /* type 0x08 */
 102        struct smc_llc_hdr hd;
 103        u8 num_rkeys;
 104        struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
 105};
 106
 107#define SMC_LLC_DEL_RKEY_MAX    8
 108#define SMC_LLC_FLAG_RKEY_NEG   0x20
 109
 110struct smc_llc_msg_delete_rkey {        /* type 0x09 */
 111        struct smc_llc_hdr hd;
 112        u8 num_rkeys;
 113        u8 err_mask;
 114        u8 reserved[2];
 115        __be32 rkey[8];
 116        u8 reserved2[4];
 117};
 118
 119union smc_llc_msg {
 120        struct smc_llc_msg_confirm_link confirm_link;
 121        struct smc_llc_msg_add_link add_link;
 122        struct smc_llc_msg_del_link delete_link;
 123
 124        struct smc_llc_msg_confirm_rkey confirm_rkey;
 125        struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont;
 126        struct smc_llc_msg_delete_rkey delete_rkey;
 127
 128        struct smc_llc_msg_test_link test_link;
 129        struct {
 130                struct smc_llc_hdr hdr;
 131                u8 data[SMC_LLC_DATA_LEN];
 132        } raw;
 133};
 134
 135#define SMC_LLC_FLAG_RESP               0x80
 136
 137/********************************** send *************************************/
 138
 139struct smc_llc_tx_pend {
 140};
 141
 142/* handler for send/transmission completion of an LLC msg */
 143static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
 144                               struct smc_link *link,
 145                               enum ib_wc_status wc_status)
 146{
 147        /* future work: handle wc_status error for recovery and failover */
 148}
 149
 150/**
 151 * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
 152 * @link: Pointer to SMC link used for sending LLC control message.
 153 * @wr_buf: Out variable returning pointer to work request payload buffer.
 154 * @pend: Out variable returning pointer to private pending WR tracking.
 155 *        It's the context the transmit complete handler will get.
 156 *
 157 * Reserves and pre-fills an entry for a pending work request send/tx.
 158 * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
 159 * Can sleep due to smc_get_ctrl_buf (if not in softirq context).
 160 *
 161 * Return: 0 on success, otherwise an error value.
 162 */
 163static int smc_llc_add_pending_send(struct smc_link *link,
 164                                    struct smc_wr_buf **wr_buf,
 165                                    struct smc_wr_tx_pend_priv **pend)
 166{
 167        int rc;
 168
 169        rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, pend);
 170        if (rc < 0)
 171                return rc;
 172        BUILD_BUG_ON_MSG(
 173                sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
 174                "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
 175        BUILD_BUG_ON_MSG(
 176                sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
 177                "must adapt SMC_WR_TX_SIZE to sizeof(struct smc_llc_msg); if not all smc_wr upper layer protocols use the same message size any more, must start to set link->wr_tx_sges[i].length on each individual smc_wr_tx_send()");
 178        BUILD_BUG_ON_MSG(
 179                sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
 180                "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
 181        return 0;
 182}
 183
 184/* high-level API to send LLC confirm link */
 185int smc_llc_send_confirm_link(struct smc_link *link,
 186                              enum smc_llc_reqresp reqresp)
 187{
 188        struct smc_link_group *lgr = smc_get_lgr(link);
 189        struct smc_llc_msg_confirm_link *confllc;
 190        struct smc_wr_tx_pend_priv *pend;
 191        struct smc_wr_buf *wr_buf;
 192        int rc;
 193
 194        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
 195        if (rc)
 196                return rc;
 197        confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
 198        memset(confllc, 0, sizeof(*confllc));
 199        confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
 200        confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
 201        confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC;
 202        if (reqresp == SMC_LLC_RESP)
 203                confllc->hd.flags |= SMC_LLC_FLAG_RESP;
 204        memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1],
 205               ETH_ALEN);
 206        memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
 207        hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
 208        confllc->link_num = link->link_id;
 209        memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
 210        confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */
 211        /* send llc message */
 212        rc = smc_wr_tx_send(link, pend);
 213        return rc;
 214}
 215
 216/* send LLC confirm rkey request */
 217static int smc_llc_send_confirm_rkey(struct smc_link *link,
 218                                     struct smc_buf_desc *rmb_desc)
 219{
 220        struct smc_llc_msg_confirm_rkey *rkeyllc;
 221        struct smc_wr_tx_pend_priv *pend;
 222        struct smc_wr_buf *wr_buf;
 223        int rc;
 224
 225        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
 226        if (rc)
 227                return rc;
 228        rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf;
 229        memset(rkeyllc, 0, sizeof(*rkeyllc));
 230        rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY;
 231        rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey);
 232        rkeyllc->rtoken[0].rmb_key =
 233                htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
 234        rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
 235                (u64)sg_dma_address(rmb_desc->sgt[SMC_SINGLE_LINK].sgl));
 236        /* send llc message */
 237        rc = smc_wr_tx_send(link, pend);
 238        return rc;
 239}
 240
 241/* prepare an add link message */
 242static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
 243                                  struct smc_link *link, u8 mac[], u8 gid[],
 244                                  enum smc_llc_reqresp reqresp)
 245{
 246        memset(addllc, 0, sizeof(*addllc));
 247        addllc->hd.common.type = SMC_LLC_ADD_LINK;
 248        addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
 249        if (reqresp == SMC_LLC_RESP) {
 250                addllc->hd.flags |= SMC_LLC_FLAG_RESP;
 251                /* always reject more links for now */
 252                addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
 253                addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
 254        }
 255        memcpy(addllc->sender_mac, mac, ETH_ALEN);
 256        memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
 257}
 258
 259/* send ADD LINK request or response */
 260int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
 261                          enum smc_llc_reqresp reqresp)
 262{
 263        struct smc_llc_msg_add_link *addllc;
 264        struct smc_wr_tx_pend_priv *pend;
 265        struct smc_wr_buf *wr_buf;
 266        int rc;
 267
 268        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
 269        if (rc)
 270                return rc;
 271        addllc = (struct smc_llc_msg_add_link *)wr_buf;
 272        smc_llc_prep_add_link(addllc, link, mac, gid, reqresp);
 273        /* send llc message */
 274        rc = smc_wr_tx_send(link, pend);
 275        return rc;
 276}
 277
 278/* prepare a delete link message */
 279static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc,
 280                                     struct smc_link *link,
 281                                     enum smc_llc_reqresp reqresp, bool orderly)
 282{
 283        memset(delllc, 0, sizeof(*delllc));
 284        delllc->hd.common.type = SMC_LLC_DELETE_LINK;
 285        delllc->hd.length = sizeof(struct smc_llc_msg_add_link);
 286        if (reqresp == SMC_LLC_RESP)
 287                delllc->hd.flags |= SMC_LLC_FLAG_RESP;
 288        /* DEL_LINK_ALL because only 1 link supported */
 289        delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
 290        if (orderly)
 291                delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
 292        delllc->link_num = link->link_id;
 293}
 294
 295/* send DELETE LINK request or response */
 296int smc_llc_send_delete_link(struct smc_link *link,
 297                             enum smc_llc_reqresp reqresp, bool orderly)
 298{
 299        struct smc_llc_msg_del_link *delllc;
 300        struct smc_wr_tx_pend_priv *pend;
 301        struct smc_wr_buf *wr_buf;
 302        int rc;
 303
 304        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
 305        if (rc)
 306                return rc;
 307        delllc = (struct smc_llc_msg_del_link *)wr_buf;
 308        smc_llc_prep_delete_link(delllc, link, reqresp, orderly);
 309        /* send llc message */
 310        rc = smc_wr_tx_send(link, pend);
 311        return rc;
 312}
 313
 314/* send LLC test link request */
 315static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
 316{
 317        struct smc_llc_msg_test_link *testllc;
 318        struct smc_wr_tx_pend_priv *pend;
 319        struct smc_wr_buf *wr_buf;
 320        int rc;
 321
 322        rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
 323        if (rc)
 324                return rc;
 325        testllc = (struct smc_llc_msg_test_link *)wr_buf;
 326        memset(testllc, 0, sizeof(*testllc));
 327        testllc->hd.common.type = SMC_LLC_TEST_LINK;
 328        testllc->hd.length = sizeof(struct smc_llc_msg_test_link);
 329        memcpy(testllc->user_data, user_data, sizeof(testllc->user_data));
 330        /* send llc message */
 331        rc = smc_wr_tx_send(link, pend);
 332        return rc;
 333}
 334
 335struct smc_llc_send_work {
 336        struct work_struct work;
 337        struct smc_link *link;
 338        int llclen;
 339        union smc_llc_msg llcbuf;
 340};
 341
 342/* worker that sends a prepared message */
 343static void smc_llc_send_message_work(struct work_struct *work)
 344{
 345        struct smc_llc_send_work *llcwrk = container_of(work,
 346                                                struct smc_llc_send_work, work);
 347        struct smc_wr_tx_pend_priv *pend;
 348        struct smc_wr_buf *wr_buf;
 349        int rc;
 350
 351        if (llcwrk->link->state == SMC_LNK_INACTIVE)
 352                goto out;
 353        rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend);
 354        if (rc)
 355                goto out;
 356        memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen);
 357        smc_wr_tx_send(llcwrk->link, pend);
 358out:
 359        kfree(llcwrk);
 360}
 361
 362/* copy llcbuf and schedule an llc send on link */
 363static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen)
 364{
 365        struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC);
 366
 367        if (!wrk)
 368                return -ENOMEM;
 369        INIT_WORK(&wrk->work, smc_llc_send_message_work);
 370        wrk->link = link;
 371        wrk->llclen = llclen;
 372        memcpy(&wrk->llcbuf, llcbuf, llclen);
 373        queue_work(link->llc_wq, &wrk->work);
 374        return 0;
 375}
 376
 377/********************************* receive ***********************************/
 378
 379static void smc_llc_rx_confirm_link(struct smc_link *link,
 380                                    struct smc_llc_msg_confirm_link *llc)
 381{
 382        struct smc_link_group *lgr = smc_get_lgr(link);
 383        int conf_rc;
 384
 385        /* RMBE eyecatchers are not supported */
 386        if (llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)
 387                conf_rc = 0;
 388        else
 389                conf_rc = ENOTSUPP;
 390
 391        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 392                if (lgr->role == SMC_SERV &&
 393                    link->state == SMC_LNK_ACTIVATING) {
 394                        link->llc_confirm_resp_rc = conf_rc;
 395                        complete(&link->llc_confirm_resp);
 396                }
 397        } else {
 398                if (lgr->role == SMC_CLNT &&
 399                    link->state == SMC_LNK_ACTIVATING) {
 400                        link->llc_confirm_rc = conf_rc;
 401                        link->link_id = llc->link_num;
 402                        complete(&link->llc_confirm);
 403                }
 404        }
 405}
 406
 407static void smc_llc_rx_add_link(struct smc_link *link,
 408                                struct smc_llc_msg_add_link *llc)
 409{
 410        struct smc_link_group *lgr = smc_get_lgr(link);
 411
 412        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 413                if (link->state == SMC_LNK_ACTIVATING)
 414                        complete(&link->llc_add_resp);
 415        } else {
 416                if (link->state == SMC_LNK_ACTIVATING) {
 417                        complete(&link->llc_add);
 418                        return;
 419                }
 420
 421                if (lgr->role == SMC_SERV) {
 422                        smc_llc_prep_add_link(llc, link,
 423                                        link->smcibdev->mac[link->ibport - 1],
 424                                        link->gid, SMC_LLC_REQ);
 425
 426                } else {
 427                        smc_llc_prep_add_link(llc, link,
 428                                        link->smcibdev->mac[link->ibport - 1],
 429                                        link->gid, SMC_LLC_RESP);
 430                }
 431                smc_llc_send_message(link, llc, sizeof(*llc));
 432        }
 433}
 434
 435static void smc_llc_rx_delete_link(struct smc_link *link,
 436                                   struct smc_llc_msg_del_link *llc)
 437{
 438        struct smc_link_group *lgr = smc_get_lgr(link);
 439
 440        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 441                if (lgr->role == SMC_SERV)
 442                        smc_lgr_schedule_free_work_fast(lgr);
 443        } else {
 444                smc_lgr_forget(lgr);
 445                smc_llc_link_deleting(link);
 446                if (lgr->role == SMC_SERV) {
 447                        /* client asks to delete this link, send request */
 448                        smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true);
 449                } else {
 450                        /* server requests to delete this link, send response */
 451                        smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
 452                }
 453                smc_llc_send_message(link, llc, sizeof(*llc));
 454                smc_lgr_schedule_free_work_fast(lgr);
 455        }
 456}
 457
 458static void smc_llc_rx_test_link(struct smc_link *link,
 459                                 struct smc_llc_msg_test_link *llc)
 460{
 461        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 462                if (link->state == SMC_LNK_ACTIVE)
 463                        complete(&link->llc_testlink_resp);
 464        } else {
 465                llc->hd.flags |= SMC_LLC_FLAG_RESP;
 466                smc_llc_send_message(link, llc, sizeof(*llc));
 467        }
 468}
 469
 470static void smc_llc_rx_confirm_rkey(struct smc_link *link,
 471                                    struct smc_llc_msg_confirm_rkey *llc)
 472{
 473        int rc;
 474
 475        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 476                link->llc_confirm_rkey_rc = llc->hd.flags &
 477                                            SMC_LLC_FLAG_RKEY_NEG;
 478                complete(&link->llc_confirm_rkey);
 479        } else {
 480                rc = smc_rtoken_add(smc_get_lgr(link),
 481                                    llc->rtoken[0].rmb_vaddr,
 482                                    llc->rtoken[0].rmb_key);
 483
 484                /* ignore rtokens for other links, we have only one link */
 485
 486                llc->hd.flags |= SMC_LLC_FLAG_RESP;
 487                if (rc < 0)
 488                        llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
 489                smc_llc_send_message(link, llc, sizeof(*llc));
 490        }
 491}
 492
 493static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link,
 494                                      struct smc_llc_msg_confirm_rkey_cont *llc)
 495{
 496        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 497                /* unused as long as we don't send this type of msg */
 498        } else {
 499                /* ignore rtokens for other links, we have only one link */
 500                llc->hd.flags |= SMC_LLC_FLAG_RESP;
 501                smc_llc_send_message(link, llc, sizeof(*llc));
 502        }
 503}
 504
 505static void smc_llc_rx_delete_rkey(struct smc_link *link,
 506                                   struct smc_llc_msg_delete_rkey *llc)
 507{
 508        u8 err_mask = 0;
 509        int i, max;
 510
 511        if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
 512                /* unused as long as we don't send this type of msg */
 513        } else {
 514                max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
 515                for (i = 0; i < max; i++) {
 516                        if (smc_rtoken_delete(smc_get_lgr(link), llc->rkey[i]))
 517                                err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
 518                }
 519
 520                if (err_mask) {
 521                        llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
 522                        llc->err_mask = err_mask;
 523                }
 524
 525                llc->hd.flags |= SMC_LLC_FLAG_RESP;
 526                smc_llc_send_message(link, llc, sizeof(*llc));
 527        }
 528}
 529
 530static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
 531{
 532        struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
 533        union smc_llc_msg *llc = buf;
 534
 535        if (wc->byte_len < sizeof(*llc))
 536                return; /* short message */
 537        if (llc->raw.hdr.length != sizeof(*llc))
 538                return; /* invalid message */
 539        if (link->state == SMC_LNK_INACTIVE)
 540                return; /* link not active, drop msg */
 541
 542        switch (llc->raw.hdr.common.type) {
 543        case SMC_LLC_TEST_LINK:
 544                smc_llc_rx_test_link(link, &llc->test_link);
 545                break;
 546        case SMC_LLC_CONFIRM_LINK:
 547                smc_llc_rx_confirm_link(link, &llc->confirm_link);
 548                break;
 549        case SMC_LLC_ADD_LINK:
 550                smc_llc_rx_add_link(link, &llc->add_link);
 551                break;
 552        case SMC_LLC_DELETE_LINK:
 553                smc_llc_rx_delete_link(link, &llc->delete_link);
 554                break;
 555        case SMC_LLC_CONFIRM_RKEY:
 556                smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey);
 557                break;
 558        case SMC_LLC_CONFIRM_RKEY_CONT:
 559                smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont);
 560                break;
 561        case SMC_LLC_DELETE_RKEY:
 562                smc_llc_rx_delete_rkey(link, &llc->delete_rkey);
 563                break;
 564        }
 565}
 566
 567/***************************** worker, utils *********************************/
 568
 569static void smc_llc_testlink_work(struct work_struct *work)
 570{
 571        struct smc_link *link = container_of(to_delayed_work(work),
 572                                             struct smc_link, llc_testlink_wrk);
 573        unsigned long next_interval;
 574        unsigned long expire_time;
 575        u8 user_data[16] = { 0 };
 576        int rc;
 577
 578        if (link->state != SMC_LNK_ACTIVE)
 579                return;         /* don't reschedule worker */
 580        expire_time = link->wr_rx_tstamp + link->llc_testlink_time;
 581        if (time_is_after_jiffies(expire_time)) {
 582                next_interval = expire_time - jiffies;
 583                goto out;
 584        }
 585        reinit_completion(&link->llc_testlink_resp);
 586        smc_llc_send_test_link(link, user_data);
 587        /* receive TEST LINK response over RoCE fabric */
 588        rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
 589                                                       SMC_LLC_WAIT_TIME);
 590        if (rc <= 0) {
 591                smc_lgr_terminate(smc_get_lgr(link));
 592                return;
 593        }
 594        next_interval = link->llc_testlink_time;
 595out:
 596        queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
 597                           next_interval);
 598}
 599
 600int smc_llc_link_init(struct smc_link *link)
 601{
 602        struct smc_link_group *lgr = smc_get_lgr(link);
 603        link->llc_wq = alloc_ordered_workqueue("llc_wq-%x:%x)", WQ_MEM_RECLAIM,
 604                                               *((u32 *)lgr->id),
 605                                               link->link_id);
 606        if (!link->llc_wq)
 607                return -ENOMEM;
 608        init_completion(&link->llc_confirm);
 609        init_completion(&link->llc_confirm_resp);
 610        init_completion(&link->llc_add);
 611        init_completion(&link->llc_add_resp);
 612        init_completion(&link->llc_confirm_rkey);
 613        init_completion(&link->llc_testlink_resp);
 614        INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
 615        return 0;
 616}
 617
 618void smc_llc_link_active(struct smc_link *link, int testlink_time)
 619{
 620        link->state = SMC_LNK_ACTIVE;
 621        if (testlink_time) {
 622                link->llc_testlink_time = testlink_time * HZ;
 623                queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
 624                                   link->llc_testlink_time);
 625        }
 626}
 627
 628void smc_llc_link_deleting(struct smc_link *link)
 629{
 630        link->state = SMC_LNK_DELETING;
 631}
 632
 633/* called in tasklet context */
 634void smc_llc_link_inactive(struct smc_link *link)
 635{
 636        link->state = SMC_LNK_INACTIVE;
 637        cancel_delayed_work(&link->llc_testlink_wrk);
 638}
 639
 640/* called in worker context */
 641void smc_llc_link_clear(struct smc_link *link)
 642{
 643        flush_workqueue(link->llc_wq);
 644        destroy_workqueue(link->llc_wq);
 645}
 646
 647/* register a new rtoken at the remote peer */
 648int smc_llc_do_confirm_rkey(struct smc_link *link,
 649                            struct smc_buf_desc *rmb_desc)
 650{
 651        int rc;
 652
 653        reinit_completion(&link->llc_confirm_rkey);
 654        smc_llc_send_confirm_rkey(link, rmb_desc);
 655        /* receive CONFIRM RKEY response from server over RoCE fabric */
 656        rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey,
 657                                                       SMC_LLC_WAIT_TIME);
 658        if (rc <= 0 || link->llc_confirm_rkey_rc)
 659                return -EFAULT;
 660        return 0;
 661}
 662
 663/***************************** init, exit, misc ******************************/
 664
 665static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
 666        {
 667                .handler        = smc_llc_rx_handler,
 668                .type           = SMC_LLC_CONFIRM_LINK
 669        },
 670        {
 671                .handler        = smc_llc_rx_handler,
 672                .type           = SMC_LLC_TEST_LINK
 673        },
 674        {
 675                .handler        = smc_llc_rx_handler,
 676                .type           = SMC_LLC_ADD_LINK
 677        },
 678        {
 679                .handler        = smc_llc_rx_handler,
 680                .type           = SMC_LLC_DELETE_LINK
 681        },
 682        {
 683                .handler        = smc_llc_rx_handler,
 684                .type           = SMC_LLC_CONFIRM_RKEY
 685        },
 686        {
 687                .handler        = smc_llc_rx_handler,
 688                .type           = SMC_LLC_CONFIRM_RKEY_CONT
 689        },
 690        {
 691                .handler        = smc_llc_rx_handler,
 692                .type           = SMC_LLC_DELETE_RKEY
 693        },
 694        {
 695                .handler        = NULL,
 696        }
 697};
 698
 699int __init smc_llc_init(void)
 700{
 701        struct smc_wr_rx_handler *handler;
 702        int rc = 0;
 703
 704        for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
 705                INIT_HLIST_NODE(&handler->list);
 706                rc = smc_wr_rx_register_handler(handler);
 707                if (rc)
 708                        break;
 709        }
 710        return rc;
 711}
 712