1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 29bf9abeaSUrsula Braun /* 39bf9abeaSUrsula Braun * Shared Memory Communications over RDMA (SMC-R) and RoCE 49bf9abeaSUrsula Braun * 59bf9abeaSUrsula Braun * Link Layer Control (LLC) 69bf9abeaSUrsula Braun * 79bf9abeaSUrsula Braun * Copyright IBM Corp. 2016 89bf9abeaSUrsula Braun * 99bf9abeaSUrsula Braun * Author(s): Klaus Wacker <Klaus.Wacker@de.ibm.com> 109bf9abeaSUrsula Braun * Ursula Braun <ubraun@linux.vnet.ibm.com> 119bf9abeaSUrsula Braun */ 129bf9abeaSUrsula Braun 139bf9abeaSUrsula Braun #include <net/tcp.h> 149bf9abeaSUrsula Braun #include <rdma/ib_verbs.h> 159bf9abeaSUrsula Braun 169bf9abeaSUrsula Braun #include "smc.h" 179bf9abeaSUrsula Braun #include "smc_core.h" 189bf9abeaSUrsula Braun #include "smc_clc.h" 199bf9abeaSUrsula Braun #include "smc_llc.h" 209bf9abeaSUrsula Braun 210f627126SStefan Raspl #define SMC_LLC_DATA_LEN 40 220f627126SStefan Raspl 230f627126SStefan Raspl struct smc_llc_hdr { 240f627126SStefan Raspl struct smc_wr_rx_hdr common; 250f627126SStefan Raspl u8 length; /* 44 */ 2652bedf37SKarsten Graul #if defined(__BIG_ENDIAN_BITFIELD) 2752bedf37SKarsten Graul u8 reserved:4, 2852bedf37SKarsten Graul add_link_rej_rsn:4; 2952bedf37SKarsten Graul #elif defined(__LITTLE_ENDIAN_BITFIELD) 3052bedf37SKarsten Graul u8 add_link_rej_rsn:4, 3152bedf37SKarsten Graul reserved:4; 3252bedf37SKarsten Graul #endif 330f627126SStefan Raspl u8 flags; 340f627126SStefan Raspl }; 350f627126SStefan Raspl 3675d320d6SKarsten Graul #define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 3775d320d6SKarsten Graul 380f627126SStefan Raspl struct smc_llc_msg_confirm_link { /* type 0x01 */ 390f627126SStefan Raspl struct smc_llc_hdr hd; 400f627126SStefan Raspl u8 sender_mac[ETH_ALEN]; 410f627126SStefan Raspl u8 sender_gid[SMC_GID_SIZE]; 420f627126SStefan Raspl u8 sender_qp_num[3]; 430f627126SStefan Raspl u8 link_num; 440f627126SStefan Raspl u8 link_uid[SMC_LGR_ID_SIZE]; 450f627126SStefan Raspl u8 max_links; 460f627126SStefan Raspl u8 reserved[9]; 470f627126SStefan Raspl }; 480f627126SStefan Raspl 4952bedf37SKarsten Graul #define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 5052bedf37SKarsten Graul #define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 5152bedf37SKarsten Graul 5252bedf37SKarsten Graul #define SMC_LLC_ADD_LNK_MAX_LINKS 2 5352bedf37SKarsten Graul 5452bedf37SKarsten Graul struct smc_llc_msg_add_link { /* type 0x02 */ 5552bedf37SKarsten Graul struct smc_llc_hdr hd; 5652bedf37SKarsten Graul u8 sender_mac[ETH_ALEN]; 5752bedf37SKarsten Graul u8 reserved2[2]; 5852bedf37SKarsten Graul u8 sender_gid[SMC_GID_SIZE]; 5952bedf37SKarsten Graul u8 sender_qp_num[3]; 6052bedf37SKarsten Graul u8 link_num; 6152bedf37SKarsten Graul u8 flags2; /* QP mtu */ 6252bedf37SKarsten Graul u8 initial_psn[3]; 6352bedf37SKarsten Graul u8 reserved[8]; 6452bedf37SKarsten Graul }; 6552bedf37SKarsten Graul 6652bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 6752bedf37SKarsten Graul #define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 6852bedf37SKarsten Graul 6952bedf37SKarsten Graul struct smc_llc_msg_del_link { /* type 0x04 */ 7052bedf37SKarsten Graul struct smc_llc_hdr hd; 7152bedf37SKarsten Graul u8 link_num; 7252bedf37SKarsten Graul __be32 reason; 7352bedf37SKarsten Graul u8 reserved[35]; 7452bedf37SKarsten Graul } __packed; /* format defined in RFC7609 */ 7552bedf37SKarsten Graul 76313164daSKarsten Graul struct smc_llc_msg_test_link { /* type 0x07 */ 77313164daSKarsten Graul struct smc_llc_hdr hd; 78313164daSKarsten Graul u8 user_data[16]; 79313164daSKarsten Graul u8 reserved[24]; 80313164daSKarsten Graul }; 81313164daSKarsten Graul 824ed75de5SKarsten Graul struct smc_rmb_rtoken { 834ed75de5SKarsten Graul union { 844ed75de5SKarsten Graul u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ 854ed75de5SKarsten Graul /* is actually the num of rtokens, first */ 864ed75de5SKarsten Graul /* rtoken is always for the current link */ 874ed75de5SKarsten Graul u8 link_id; /* link id of the rtoken */ 884ed75de5SKarsten Graul }; 894ed75de5SKarsten Graul __be32 rmb_key; 904ed75de5SKarsten Graul __be64 rmb_vaddr; 914ed75de5SKarsten Graul } __packed; /* format defined in RFC7609 */ 924ed75de5SKarsten Graul 934ed75de5SKarsten Graul #define SMC_LLC_RKEYS_PER_MSG 3 944ed75de5SKarsten Graul 954ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey { /* type 0x06 */ 964ed75de5SKarsten Graul struct smc_llc_hdr hd; 974ed75de5SKarsten Graul struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 984ed75de5SKarsten Graul u8 reserved; 994ed75de5SKarsten Graul }; 1004ed75de5SKarsten Graul 1014ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey_cont { /* type 0x08 */ 1024ed75de5SKarsten Graul struct smc_llc_hdr hd; 1034ed75de5SKarsten Graul u8 num_rkeys; 1044ed75de5SKarsten Graul struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; 1054ed75de5SKarsten Graul }; 1064ed75de5SKarsten Graul 1074ed75de5SKarsten Graul #define SMC_LLC_DEL_RKEY_MAX 8 1084ed75de5SKarsten Graul #define SMC_LLC_FLAG_RKEY_NEG 0x20 1094ed75de5SKarsten Graul 1104ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey { /* type 0x09 */ 1114ed75de5SKarsten Graul struct smc_llc_hdr hd; 1124ed75de5SKarsten Graul u8 num_rkeys; 1134ed75de5SKarsten Graul u8 err_mask; 1144ed75de5SKarsten Graul u8 reserved[2]; 1154ed75de5SKarsten Graul __be32 rkey[8]; 1164ed75de5SKarsten Graul u8 reserved2[4]; 1174ed75de5SKarsten Graul }; 1184ed75de5SKarsten Graul 1190f627126SStefan Raspl union smc_llc_msg { 1200f627126SStefan Raspl struct smc_llc_msg_confirm_link confirm_link; 12152bedf37SKarsten Graul struct smc_llc_msg_add_link add_link; 12252bedf37SKarsten Graul struct smc_llc_msg_del_link delete_link; 1234ed75de5SKarsten Graul 1244ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey confirm_rkey; 1254ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont; 1264ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey delete_rkey; 1274ed75de5SKarsten Graul 128313164daSKarsten Graul struct smc_llc_msg_test_link test_link; 1290f627126SStefan Raspl struct { 1300f627126SStefan Raspl struct smc_llc_hdr hdr; 1310f627126SStefan Raspl u8 data[SMC_LLC_DATA_LEN]; 1320f627126SStefan Raspl } raw; 1330f627126SStefan Raspl }; 1340f627126SStefan Raspl 1350f627126SStefan Raspl #define SMC_LLC_FLAG_RESP 0x80 1360f627126SStefan Raspl 1379bf9abeaSUrsula Braun /********************************** send *************************************/ 1389bf9abeaSUrsula Braun 1399bf9abeaSUrsula Braun struct smc_llc_tx_pend { 1409bf9abeaSUrsula Braun }; 1419bf9abeaSUrsula Braun 1429bf9abeaSUrsula Braun /* handler for send/transmission completion of an LLC msg */ 1439bf9abeaSUrsula Braun static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, 1449bf9abeaSUrsula Braun struct smc_link *link, 1459bf9abeaSUrsula Braun enum ib_wc_status wc_status) 1469bf9abeaSUrsula Braun { 1479bf9abeaSUrsula Braun /* future work: handle wc_status error for recovery and failover */ 1489bf9abeaSUrsula Braun } 1499bf9abeaSUrsula Braun 1509bf9abeaSUrsula Braun /** 1519bf9abeaSUrsula Braun * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits 1529bf9abeaSUrsula Braun * @link: Pointer to SMC link used for sending LLC control message. 1539bf9abeaSUrsula Braun * @wr_buf: Out variable returning pointer to work request payload buffer. 1549bf9abeaSUrsula Braun * @pend: Out variable returning pointer to private pending WR tracking. 1559bf9abeaSUrsula Braun * It's the context the transmit complete handler will get. 1569bf9abeaSUrsula Braun * 1579bf9abeaSUrsula Braun * Reserves and pre-fills an entry for a pending work request send/tx. 1589bf9abeaSUrsula Braun * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. 1599bf9abeaSUrsula Braun * Can sleep due to smc_get_ctrl_buf (if not in softirq context). 1609bf9abeaSUrsula Braun * 1619bf9abeaSUrsula Braun * Return: 0 on success, otherwise an error value. 1629bf9abeaSUrsula Braun */ 1639bf9abeaSUrsula Braun static int smc_llc_add_pending_send(struct smc_link *link, 1649bf9abeaSUrsula Braun struct smc_wr_buf **wr_buf, 1659bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv **pend) 1669bf9abeaSUrsula Braun { 1679bf9abeaSUrsula Braun int rc; 1689bf9abeaSUrsula Braun 1699bf9abeaSUrsula Braun rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, pend); 1709bf9abeaSUrsula Braun if (rc < 0) 1719bf9abeaSUrsula Braun return rc; 1729bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 1739bf9abeaSUrsula Braun sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, 1749bf9abeaSUrsula Braun "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); 1759bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 1769bf9abeaSUrsula Braun sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, 1779bf9abeaSUrsula Braun "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()"); 1789bf9abeaSUrsula Braun BUILD_BUG_ON_MSG( 1799bf9abeaSUrsula Braun sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 1809bf9abeaSUrsula Braun "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); 1819bf9abeaSUrsula Braun return 0; 1829bf9abeaSUrsula Braun } 1839bf9abeaSUrsula Braun 1849bf9abeaSUrsula Braun /* high-level API to send LLC confirm link */ 185947541f3SUrsula Braun int smc_llc_send_confirm_link(struct smc_link *link, 1869bf9abeaSUrsula Braun enum smc_llc_reqresp reqresp) 1879bf9abeaSUrsula Braun { 18800e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 1899bf9abeaSUrsula Braun struct smc_llc_msg_confirm_link *confllc; 1909bf9abeaSUrsula Braun struct smc_wr_tx_pend_priv *pend; 1919bf9abeaSUrsula Braun struct smc_wr_buf *wr_buf; 1929bf9abeaSUrsula Braun int rc; 1939bf9abeaSUrsula Braun 1949bf9abeaSUrsula Braun rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 1959bf9abeaSUrsula Braun if (rc) 1969bf9abeaSUrsula Braun return rc; 1979bf9abeaSUrsula Braun confllc = (struct smc_llc_msg_confirm_link *)wr_buf; 1989bf9abeaSUrsula Braun memset(confllc, 0, sizeof(*confllc)); 1999bf9abeaSUrsula Braun confllc->hd.common.type = SMC_LLC_CONFIRM_LINK; 2009bf9abeaSUrsula Braun confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link); 20175d320d6SKarsten Graul confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; 2029bf9abeaSUrsula Braun if (reqresp == SMC_LLC_RESP) 2039bf9abeaSUrsula Braun confllc->hd.flags |= SMC_LLC_FLAG_RESP; 204947541f3SUrsula Braun memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], 205947541f3SUrsula Braun ETH_ALEN); 206*7005ada6SUrsula Braun memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); 2079bf9abeaSUrsula Braun hton24(confllc->sender_qp_num, link->roce_qp->qp_num); 2082be922f3SKarsten Graul confllc->link_num = link->link_id; 2099bf9abeaSUrsula Braun memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE); 21052bedf37SKarsten Graul confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */ 21152bedf37SKarsten Graul /* send llc message */ 21252bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 21352bedf37SKarsten Graul return rc; 21452bedf37SKarsten Graul } 21552bedf37SKarsten Graul 21644aa81ceSKarsten Graul /* send LLC confirm rkey request */ 21744aa81ceSKarsten Graul static int smc_llc_send_confirm_rkey(struct smc_link *link, 21844aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 21944aa81ceSKarsten Graul { 22044aa81ceSKarsten Graul struct smc_llc_msg_confirm_rkey *rkeyllc; 22144aa81ceSKarsten Graul struct smc_wr_tx_pend_priv *pend; 22244aa81ceSKarsten Graul struct smc_wr_buf *wr_buf; 22344aa81ceSKarsten Graul int rc; 22444aa81ceSKarsten Graul 22544aa81ceSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 22644aa81ceSKarsten Graul if (rc) 22744aa81ceSKarsten Graul return rc; 22844aa81ceSKarsten Graul rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; 22944aa81ceSKarsten Graul memset(rkeyllc, 0, sizeof(*rkeyllc)); 23044aa81ceSKarsten Graul rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY; 23144aa81ceSKarsten Graul rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey); 23244aa81ceSKarsten Graul rkeyllc->rtoken[0].rmb_key = 23344aa81ceSKarsten Graul htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); 23444aa81ceSKarsten Graul rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64( 23544aa81ceSKarsten Graul (u64)sg_dma_address(rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); 23644aa81ceSKarsten Graul /* send llc message */ 23744aa81ceSKarsten Graul rc = smc_wr_tx_send(link, pend); 23844aa81ceSKarsten Graul return rc; 23944aa81ceSKarsten Graul } 24044aa81ceSKarsten Graul 2412a4c57a9SKarsten Graul /* prepare an add link message */ 2422a4c57a9SKarsten Graul static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc, 243*7005ada6SUrsula Braun struct smc_link *link, u8 mac[], u8 gid[], 2442a4c57a9SKarsten Graul enum smc_llc_reqresp reqresp) 2452a4c57a9SKarsten Graul { 2462a4c57a9SKarsten Graul memset(addllc, 0, sizeof(*addllc)); 2472a4c57a9SKarsten Graul addllc->hd.common.type = SMC_LLC_ADD_LINK; 2482a4c57a9SKarsten Graul addllc->hd.length = sizeof(struct smc_llc_msg_add_link); 2492a4c57a9SKarsten Graul if (reqresp == SMC_LLC_RESP) { 2502a4c57a9SKarsten Graul addllc->hd.flags |= SMC_LLC_FLAG_RESP; 2512a4c57a9SKarsten Graul /* always reject more links for now */ 2522a4c57a9SKarsten Graul addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; 2532a4c57a9SKarsten Graul addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; 2542a4c57a9SKarsten Graul } 2552a4c57a9SKarsten Graul memcpy(addllc->sender_mac, mac, ETH_ALEN); 2562a4c57a9SKarsten Graul memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); 2572a4c57a9SKarsten Graul } 2582a4c57a9SKarsten Graul 25952bedf37SKarsten Graul /* send ADD LINK request or response */ 260*7005ada6SUrsula Braun int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], 26152bedf37SKarsten Graul enum smc_llc_reqresp reqresp) 26252bedf37SKarsten Graul { 26352bedf37SKarsten Graul struct smc_llc_msg_add_link *addllc; 26452bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 26552bedf37SKarsten Graul struct smc_wr_buf *wr_buf; 26652bedf37SKarsten Graul int rc; 26752bedf37SKarsten Graul 26852bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 26952bedf37SKarsten Graul if (rc) 27052bedf37SKarsten Graul return rc; 27152bedf37SKarsten Graul addllc = (struct smc_llc_msg_add_link *)wr_buf; 2722a4c57a9SKarsten Graul smc_llc_prep_add_link(addllc, link, mac, gid, reqresp); 27352bedf37SKarsten Graul /* send llc message */ 27452bedf37SKarsten Graul rc = smc_wr_tx_send(link, pend); 27552bedf37SKarsten Graul return rc; 27652bedf37SKarsten Graul } 27752bedf37SKarsten Graul 2782a4c57a9SKarsten Graul /* prepare a delete link message */ 2792a4c57a9SKarsten Graul static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc, 2802a4c57a9SKarsten Graul struct smc_link *link, 2812a4c57a9SKarsten Graul enum smc_llc_reqresp reqresp) 2822a4c57a9SKarsten Graul { 2832a4c57a9SKarsten Graul memset(delllc, 0, sizeof(*delllc)); 2842a4c57a9SKarsten Graul delllc->hd.common.type = SMC_LLC_DELETE_LINK; 2852a4c57a9SKarsten Graul delllc->hd.length = sizeof(struct smc_llc_msg_add_link); 2862a4c57a9SKarsten Graul if (reqresp == SMC_LLC_RESP) 2872a4c57a9SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_RESP; 2882a4c57a9SKarsten Graul /* DEL_LINK_ALL because only 1 link supported */ 2892a4c57a9SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; 2902a4c57a9SKarsten Graul delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; 2912a4c57a9SKarsten Graul delllc->link_num = link->link_id; 2922a4c57a9SKarsten Graul } 2932a4c57a9SKarsten Graul 29452bedf37SKarsten Graul /* send DELETE LINK request or response */ 29552bedf37SKarsten Graul int smc_llc_send_delete_link(struct smc_link *link, 29652bedf37SKarsten Graul enum smc_llc_reqresp reqresp) 29752bedf37SKarsten Graul { 29852bedf37SKarsten Graul struct smc_llc_msg_del_link *delllc; 29952bedf37SKarsten Graul struct smc_wr_tx_pend_priv *pend; 30052bedf37SKarsten Graul struct smc_wr_buf *wr_buf; 30152bedf37SKarsten Graul int rc; 30252bedf37SKarsten Graul 30352bedf37SKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 30452bedf37SKarsten Graul if (rc) 30552bedf37SKarsten Graul return rc; 30652bedf37SKarsten Graul delllc = (struct smc_llc_msg_del_link *)wr_buf; 3072a4c57a9SKarsten Graul smc_llc_prep_delete_link(delllc, link, reqresp); 3089bf9abeaSUrsula Braun /* send llc message */ 3099bf9abeaSUrsula Braun rc = smc_wr_tx_send(link, pend); 3109bf9abeaSUrsula Braun return rc; 3119bf9abeaSUrsula Braun } 3129bf9abeaSUrsula Braun 313d97935faSKarsten Graul /* send LLC test link request */ 314d97935faSKarsten Graul static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) 315313164daSKarsten Graul { 316313164daSKarsten Graul struct smc_llc_msg_test_link *testllc; 317313164daSKarsten Graul struct smc_wr_tx_pend_priv *pend; 318313164daSKarsten Graul struct smc_wr_buf *wr_buf; 319313164daSKarsten Graul int rc; 320313164daSKarsten Graul 321313164daSKarsten Graul rc = smc_llc_add_pending_send(link, &wr_buf, &pend); 322313164daSKarsten Graul if (rc) 323313164daSKarsten Graul return rc; 324313164daSKarsten Graul testllc = (struct smc_llc_msg_test_link *)wr_buf; 325313164daSKarsten Graul memset(testllc, 0, sizeof(*testllc)); 326313164daSKarsten Graul testllc->hd.common.type = SMC_LLC_TEST_LINK; 327313164daSKarsten Graul testllc->hd.length = sizeof(struct smc_llc_msg_test_link); 328313164daSKarsten Graul memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); 329313164daSKarsten Graul /* send llc message */ 330313164daSKarsten Graul rc = smc_wr_tx_send(link, pend); 331313164daSKarsten Graul return rc; 332313164daSKarsten Graul } 333313164daSKarsten Graul 3342a4c57a9SKarsten Graul struct smc_llc_send_work { 3352a4c57a9SKarsten Graul struct work_struct work; 3362a4c57a9SKarsten Graul struct smc_link *link; 3372a4c57a9SKarsten Graul int llclen; 3382a4c57a9SKarsten Graul union smc_llc_msg llcbuf; 3392a4c57a9SKarsten Graul }; 3402a4c57a9SKarsten Graul 3412a4c57a9SKarsten Graul /* worker that sends a prepared message */ 3422a4c57a9SKarsten Graul static void smc_llc_send_message_work(struct work_struct *work) 3434ed75de5SKarsten Graul { 3442a4c57a9SKarsten Graul struct smc_llc_send_work *llcwrk = container_of(work, 3452a4c57a9SKarsten Graul struct smc_llc_send_work, work); 3464ed75de5SKarsten Graul struct smc_wr_tx_pend_priv *pend; 3474ed75de5SKarsten Graul struct smc_wr_buf *wr_buf; 3484ed75de5SKarsten Graul int rc; 3494ed75de5SKarsten Graul 3502a4c57a9SKarsten Graul if (llcwrk->link->state == SMC_LNK_INACTIVE) 3512a4c57a9SKarsten Graul goto out; 3522a4c57a9SKarsten Graul rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend); 3534ed75de5SKarsten Graul if (rc) 3542a4c57a9SKarsten Graul goto out; 3552a4c57a9SKarsten Graul memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen); 3562a4c57a9SKarsten Graul smc_wr_tx_send(llcwrk->link, pend); 3572a4c57a9SKarsten Graul out: 3582a4c57a9SKarsten Graul kfree(llcwrk); 3592a4c57a9SKarsten Graul } 3602a4c57a9SKarsten Graul 3612a4c57a9SKarsten Graul /* copy llcbuf and schedule an llc send on link */ 3622a4c57a9SKarsten Graul static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen) 3632a4c57a9SKarsten Graul { 3642a4c57a9SKarsten Graul struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); 3652a4c57a9SKarsten Graul 3662a4c57a9SKarsten Graul if (!wrk) 3672a4c57a9SKarsten Graul return -ENOMEM; 3682a4c57a9SKarsten Graul INIT_WORK(&wrk->work, smc_llc_send_message_work); 3692a4c57a9SKarsten Graul wrk->link = link; 3702a4c57a9SKarsten Graul wrk->llclen = llclen; 3712a4c57a9SKarsten Graul memcpy(&wrk->llcbuf, llcbuf, llclen); 3722a4c57a9SKarsten Graul queue_work(link->llc_wq, &wrk->work); 3732a4c57a9SKarsten Graul return 0; 3744ed75de5SKarsten Graul } 3754ed75de5SKarsten Graul 3769bf9abeaSUrsula Braun /********************************* receive ***********************************/ 3779bf9abeaSUrsula Braun 3789bf9abeaSUrsula Braun static void smc_llc_rx_confirm_link(struct smc_link *link, 3799bf9abeaSUrsula Braun struct smc_llc_msg_confirm_link *llc) 3809bf9abeaSUrsula Braun { 38100e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 38275d320d6SKarsten Graul int conf_rc; 3839bf9abeaSUrsula Braun 38475d320d6SKarsten Graul /* RMBE eyecatchers are not supported */ 38575d320d6SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC) 38675d320d6SKarsten Graul conf_rc = 0; 38775d320d6SKarsten Graul else 38875d320d6SKarsten Graul conf_rc = ENOTSUPP; 38975d320d6SKarsten Graul 3909bf9abeaSUrsula Braun if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 39152bedf37SKarsten Graul if (lgr->role == SMC_SERV && 39252bedf37SKarsten Graul link->state == SMC_LNK_ACTIVATING) { 39375d320d6SKarsten Graul link->llc_confirm_resp_rc = conf_rc; 3949bf9abeaSUrsula Braun complete(&link->llc_confirm_resp); 39575d320d6SKarsten Graul } 3969bf9abeaSUrsula Braun } else { 39752bedf37SKarsten Graul if (lgr->role == SMC_CLNT && 39852bedf37SKarsten Graul link->state == SMC_LNK_ACTIVATING) { 39975d320d6SKarsten Graul link->llc_confirm_rc = conf_rc; 4009bf9abeaSUrsula Braun link->link_id = llc->link_num; 4019bf9abeaSUrsula Braun complete(&link->llc_confirm); 4029bf9abeaSUrsula Braun } 4039bf9abeaSUrsula Braun } 4049bf9abeaSUrsula Braun } 4059bf9abeaSUrsula Braun 40652bedf37SKarsten Graul static void smc_llc_rx_add_link(struct smc_link *link, 40752bedf37SKarsten Graul struct smc_llc_msg_add_link *llc) 40852bedf37SKarsten Graul { 40900e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 41052bedf37SKarsten Graul 41152bedf37SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 41252bedf37SKarsten Graul if (link->state == SMC_LNK_ACTIVATING) 41352bedf37SKarsten Graul complete(&link->llc_add_resp); 41452bedf37SKarsten Graul } else { 41552bedf37SKarsten Graul if (link->state == SMC_LNK_ACTIVATING) { 41652bedf37SKarsten Graul complete(&link->llc_add); 41752bedf37SKarsten Graul return; 41852bedf37SKarsten Graul } 41952bedf37SKarsten Graul 42052bedf37SKarsten Graul if (lgr->role == SMC_SERV) { 4212a4c57a9SKarsten Graul smc_llc_prep_add_link(llc, link, 42252bedf37SKarsten Graul link->smcibdev->mac[link->ibport - 1], 423*7005ada6SUrsula Braun link->gid, SMC_LLC_REQ); 42452bedf37SKarsten Graul 42552bedf37SKarsten Graul } else { 4262a4c57a9SKarsten Graul smc_llc_prep_add_link(llc, link, 42752bedf37SKarsten Graul link->smcibdev->mac[link->ibport - 1], 428*7005ada6SUrsula Braun link->gid, SMC_LLC_RESP); 42952bedf37SKarsten Graul } 4302a4c57a9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 43152bedf37SKarsten Graul } 43252bedf37SKarsten Graul } 43352bedf37SKarsten Graul 43452bedf37SKarsten Graul static void smc_llc_rx_delete_link(struct smc_link *link, 43552bedf37SKarsten Graul struct smc_llc_msg_del_link *llc) 43652bedf37SKarsten Graul { 43700e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 43852bedf37SKarsten Graul 43952bedf37SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 44052bedf37SKarsten Graul if (lgr->role == SMC_SERV) 44152bedf37SKarsten Graul smc_lgr_terminate(lgr); 44252bedf37SKarsten Graul } else { 44352bedf37SKarsten Graul if (lgr->role == SMC_SERV) { 4449651b934SKarsten Graul smc_lgr_forget(lgr); 4452a4c57a9SKarsten Graul smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ); 4462a4c57a9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 44752bedf37SKarsten Graul } else { 4482a4c57a9SKarsten Graul smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP); 4492a4c57a9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 45052bedf37SKarsten Graul smc_lgr_terminate(lgr); 45152bedf37SKarsten Graul } 45252bedf37SKarsten Graul } 45352bedf37SKarsten Graul } 45452bedf37SKarsten Graul 455313164daSKarsten Graul static void smc_llc_rx_test_link(struct smc_link *link, 456313164daSKarsten Graul struct smc_llc_msg_test_link *llc) 457313164daSKarsten Graul { 458313164daSKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 459877ae5beSKarsten Graul if (link->state == SMC_LNK_ACTIVE) 460877ae5beSKarsten Graul complete(&link->llc_testlink_resp); 461313164daSKarsten Graul } else { 462d97935faSKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 463d97935faSKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 464313164daSKarsten Graul } 465313164daSKarsten Graul } 466313164daSKarsten Graul 4674ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey(struct smc_link *link, 4684ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey *llc) 4694ed75de5SKarsten Graul { 4704ed75de5SKarsten Graul int rc; 4714ed75de5SKarsten Graul 4724ed75de5SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 47344aa81ceSKarsten Graul link->llc_confirm_rkey_rc = llc->hd.flags & 47444aa81ceSKarsten Graul SMC_LLC_FLAG_RKEY_NEG; 47544aa81ceSKarsten Graul complete(&link->llc_confirm_rkey); 4764ed75de5SKarsten Graul } else { 47700e5fb26SStefan Raspl rc = smc_rtoken_add(smc_get_lgr(link), 4784ed75de5SKarsten Graul llc->rtoken[0].rmb_vaddr, 4794ed75de5SKarsten Graul llc->rtoken[0].rmb_key); 4804ed75de5SKarsten Graul 4814ed75de5SKarsten Graul /* ignore rtokens for other links, we have only one link */ 4824ed75de5SKarsten Graul 4834ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 4844ed75de5SKarsten Graul if (rc < 0) 4854ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 4869fcdf8e9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 4874ed75de5SKarsten Graul } 4884ed75de5SKarsten Graul } 4894ed75de5SKarsten Graul 4904ed75de5SKarsten Graul static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link, 4914ed75de5SKarsten Graul struct smc_llc_msg_confirm_rkey_cont *llc) 4924ed75de5SKarsten Graul { 4934ed75de5SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 4944ed75de5SKarsten Graul /* unused as long as we don't send this type of msg */ 4954ed75de5SKarsten Graul } else { 4964ed75de5SKarsten Graul /* ignore rtokens for other links, we have only one link */ 4974ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 4989fcdf8e9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 4994ed75de5SKarsten Graul } 5004ed75de5SKarsten Graul } 5014ed75de5SKarsten Graul 5024ed75de5SKarsten Graul static void smc_llc_rx_delete_rkey(struct smc_link *link, 5034ed75de5SKarsten Graul struct smc_llc_msg_delete_rkey *llc) 5044ed75de5SKarsten Graul { 5054ed75de5SKarsten Graul u8 err_mask = 0; 5064ed75de5SKarsten Graul int i, max; 5074ed75de5SKarsten Graul 5084ed75de5SKarsten Graul if (llc->hd.flags & SMC_LLC_FLAG_RESP) { 5094ed75de5SKarsten Graul /* unused as long as we don't send this type of msg */ 5104ed75de5SKarsten Graul } else { 5114ed75de5SKarsten Graul max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); 5124ed75de5SKarsten Graul for (i = 0; i < max; i++) { 51300e5fb26SStefan Raspl if (smc_rtoken_delete(smc_get_lgr(link), llc->rkey[i])) 5144ed75de5SKarsten Graul err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); 5154ed75de5SKarsten Graul } 5164ed75de5SKarsten Graul 5174ed75de5SKarsten Graul if (err_mask) { 5184ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; 5194ed75de5SKarsten Graul llc->err_mask = err_mask; 5204ed75de5SKarsten Graul } 5214ed75de5SKarsten Graul 5224ed75de5SKarsten Graul llc->hd.flags |= SMC_LLC_FLAG_RESP; 5239fcdf8e9SKarsten Graul smc_llc_send_message(link, llc, sizeof(*llc)); 5244ed75de5SKarsten Graul } 5254ed75de5SKarsten Graul } 5264ed75de5SKarsten Graul 5279bf9abeaSUrsula Braun static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) 5289bf9abeaSUrsula Braun { 5299bf9abeaSUrsula Braun struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 5309bf9abeaSUrsula Braun union smc_llc_msg *llc = buf; 5319bf9abeaSUrsula Braun 5329bf9abeaSUrsula Braun if (wc->byte_len < sizeof(*llc)) 5339bf9abeaSUrsula Braun return; /* short message */ 5349bf9abeaSUrsula Braun if (llc->raw.hdr.length != sizeof(*llc)) 5359bf9abeaSUrsula Braun return; /* invalid message */ 5368f332a74SKarsten Graul if (link->state == SMC_LNK_INACTIVE) 5378f332a74SKarsten Graul return; /* link not active, drop msg */ 538313164daSKarsten Graul 539313164daSKarsten Graul switch (llc->raw.hdr.common.type) { 540313164daSKarsten Graul case SMC_LLC_TEST_LINK: 541313164daSKarsten Graul smc_llc_rx_test_link(link, &llc->test_link); 542313164daSKarsten Graul break; 543313164daSKarsten Graul case SMC_LLC_CONFIRM_LINK: 5449bf9abeaSUrsula Braun smc_llc_rx_confirm_link(link, &llc->confirm_link); 545313164daSKarsten Graul break; 54652bedf37SKarsten Graul case SMC_LLC_ADD_LINK: 54752bedf37SKarsten Graul smc_llc_rx_add_link(link, &llc->add_link); 54852bedf37SKarsten Graul break; 54952bedf37SKarsten Graul case SMC_LLC_DELETE_LINK: 55052bedf37SKarsten Graul smc_llc_rx_delete_link(link, &llc->delete_link); 55152bedf37SKarsten Graul break; 5524ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY: 5534ed75de5SKarsten Graul smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey); 5544ed75de5SKarsten Graul break; 5554ed75de5SKarsten Graul case SMC_LLC_CONFIRM_RKEY_CONT: 5564ed75de5SKarsten Graul smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont); 5574ed75de5SKarsten Graul break; 5584ed75de5SKarsten Graul case SMC_LLC_DELETE_RKEY: 5594ed75de5SKarsten Graul smc_llc_rx_delete_rkey(link, &llc->delete_rkey); 5604ed75de5SKarsten Graul break; 561313164daSKarsten Graul } 5629bf9abeaSUrsula Braun } 5639bf9abeaSUrsula Braun 56444aa81ceSKarsten Graul /***************************** worker, utils *********************************/ 565877ae5beSKarsten Graul 566877ae5beSKarsten Graul static void smc_llc_testlink_work(struct work_struct *work) 567877ae5beSKarsten Graul { 568877ae5beSKarsten Graul struct smc_link *link = container_of(to_delayed_work(work), 569877ae5beSKarsten Graul struct smc_link, llc_testlink_wrk); 570877ae5beSKarsten Graul unsigned long next_interval; 571877ae5beSKarsten Graul unsigned long expire_time; 572877ae5beSKarsten Graul u8 user_data[16] = { 0 }; 573877ae5beSKarsten Graul int rc; 574877ae5beSKarsten Graul 575877ae5beSKarsten Graul if (link->state != SMC_LNK_ACTIVE) 576877ae5beSKarsten Graul return; /* don't reschedule worker */ 577877ae5beSKarsten Graul expire_time = link->wr_rx_tstamp + link->llc_testlink_time; 578877ae5beSKarsten Graul if (time_is_after_jiffies(expire_time)) { 579877ae5beSKarsten Graul next_interval = expire_time - jiffies; 580877ae5beSKarsten Graul goto out; 581877ae5beSKarsten Graul } 582877ae5beSKarsten Graul reinit_completion(&link->llc_testlink_resp); 583d97935faSKarsten Graul smc_llc_send_test_link(link, user_data); 584877ae5beSKarsten Graul /* receive TEST LINK response over RoCE fabric */ 585877ae5beSKarsten Graul rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, 586877ae5beSKarsten Graul SMC_LLC_WAIT_TIME); 587877ae5beSKarsten Graul if (rc <= 0) { 58800e5fb26SStefan Raspl smc_lgr_terminate(smc_get_lgr(link)); 589877ae5beSKarsten Graul return; 590877ae5beSKarsten Graul } 591877ae5beSKarsten Graul next_interval = link->llc_testlink_time; 592877ae5beSKarsten Graul out: 5932a4c57a9SKarsten Graul queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, 5942a4c57a9SKarsten Graul next_interval); 595877ae5beSKarsten Graul } 596877ae5beSKarsten Graul 5972a4c57a9SKarsten Graul int smc_llc_link_init(struct smc_link *link) 598877ae5beSKarsten Graul { 59900e5fb26SStefan Raspl struct smc_link_group *lgr = smc_get_lgr(link); 6002a4c57a9SKarsten Graul link->llc_wq = alloc_ordered_workqueue("llc_wq-%x:%x)", WQ_MEM_RECLAIM, 6012a4c57a9SKarsten Graul *((u32 *)lgr->id), 6022a4c57a9SKarsten Graul link->link_id); 6032a4c57a9SKarsten Graul if (!link->llc_wq) 6042a4c57a9SKarsten Graul return -ENOMEM; 605b32cf4abSKarsten Graul init_completion(&link->llc_confirm); 606b32cf4abSKarsten Graul init_completion(&link->llc_confirm_resp); 607b32cf4abSKarsten Graul init_completion(&link->llc_add); 608b32cf4abSKarsten Graul init_completion(&link->llc_add_resp); 609b32cf4abSKarsten Graul init_completion(&link->llc_confirm_rkey); 610877ae5beSKarsten Graul init_completion(&link->llc_testlink_resp); 611877ae5beSKarsten Graul INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); 6122a4c57a9SKarsten Graul return 0; 613b32cf4abSKarsten Graul } 614b32cf4abSKarsten Graul 615b32cf4abSKarsten Graul void smc_llc_link_active(struct smc_link *link, int testlink_time) 616b32cf4abSKarsten Graul { 617877ae5beSKarsten Graul link->state = SMC_LNK_ACTIVE; 618877ae5beSKarsten Graul if (testlink_time) { 619877ae5beSKarsten Graul link->llc_testlink_time = testlink_time * HZ; 6202a4c57a9SKarsten Graul queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, 621877ae5beSKarsten Graul link->llc_testlink_time); 622877ae5beSKarsten Graul } 623877ae5beSKarsten Graul } 624877ae5beSKarsten Graul 625877ae5beSKarsten Graul /* called in tasklet context */ 626877ae5beSKarsten Graul void smc_llc_link_inactive(struct smc_link *link) 627877ae5beSKarsten Graul { 628877ae5beSKarsten Graul link->state = SMC_LNK_INACTIVE; 629877ae5beSKarsten Graul cancel_delayed_work(&link->llc_testlink_wrk); 630877ae5beSKarsten Graul } 631877ae5beSKarsten Graul 632877ae5beSKarsten Graul /* called in worker context */ 6332a4c57a9SKarsten Graul void smc_llc_link_clear(struct smc_link *link) 634877ae5beSKarsten Graul { 6352a4c57a9SKarsten Graul flush_workqueue(link->llc_wq); 6362a4c57a9SKarsten Graul destroy_workqueue(link->llc_wq); 637877ae5beSKarsten Graul } 638877ae5beSKarsten Graul 63944aa81ceSKarsten Graul /* register a new rtoken at the remote peer */ 64044aa81ceSKarsten Graul int smc_llc_do_confirm_rkey(struct smc_link *link, 64144aa81ceSKarsten Graul struct smc_buf_desc *rmb_desc) 64244aa81ceSKarsten Graul { 64344aa81ceSKarsten Graul int rc; 64444aa81ceSKarsten Graul 64544aa81ceSKarsten Graul reinit_completion(&link->llc_confirm_rkey); 64644aa81ceSKarsten Graul smc_llc_send_confirm_rkey(link, rmb_desc); 64744aa81ceSKarsten Graul /* receive CONFIRM RKEY response from server over RoCE fabric */ 64844aa81ceSKarsten Graul rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey, 64944aa81ceSKarsten Graul SMC_LLC_WAIT_TIME); 65044aa81ceSKarsten Graul if (rc <= 0 || link->llc_confirm_rkey_rc) 65144aa81ceSKarsten Graul return -EFAULT; 65244aa81ceSKarsten Graul return 0; 65344aa81ceSKarsten Graul } 65444aa81ceSKarsten Graul 6559bf9abeaSUrsula Braun /***************************** init, exit, misc ******************************/ 6569bf9abeaSUrsula Braun 6579bf9abeaSUrsula Braun static struct smc_wr_rx_handler smc_llc_rx_handlers[] = { 6589bf9abeaSUrsula Braun { 6599bf9abeaSUrsula Braun .handler = smc_llc_rx_handler, 6609bf9abeaSUrsula Braun .type = SMC_LLC_CONFIRM_LINK 6619bf9abeaSUrsula Braun }, 6629bf9abeaSUrsula Braun { 663313164daSKarsten Graul .handler = smc_llc_rx_handler, 664313164daSKarsten Graul .type = SMC_LLC_TEST_LINK 665313164daSKarsten Graul }, 666313164daSKarsten Graul { 6674ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 66852bedf37SKarsten Graul .type = SMC_LLC_ADD_LINK 66952bedf37SKarsten Graul }, 67052bedf37SKarsten Graul { 67152bedf37SKarsten Graul .handler = smc_llc_rx_handler, 67252bedf37SKarsten Graul .type = SMC_LLC_DELETE_LINK 67352bedf37SKarsten Graul }, 67452bedf37SKarsten Graul { 67552bedf37SKarsten Graul .handler = smc_llc_rx_handler, 6764ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY 6774ed75de5SKarsten Graul }, 6784ed75de5SKarsten Graul { 6794ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 6804ed75de5SKarsten Graul .type = SMC_LLC_CONFIRM_RKEY_CONT 6814ed75de5SKarsten Graul }, 6824ed75de5SKarsten Graul { 6834ed75de5SKarsten Graul .handler = smc_llc_rx_handler, 6844ed75de5SKarsten Graul .type = SMC_LLC_DELETE_RKEY 6854ed75de5SKarsten Graul }, 6864ed75de5SKarsten Graul { 6879bf9abeaSUrsula Braun .handler = NULL, 6889bf9abeaSUrsula Braun } 6899bf9abeaSUrsula Braun }; 6909bf9abeaSUrsula Braun 6919bf9abeaSUrsula Braun int __init smc_llc_init(void) 6929bf9abeaSUrsula Braun { 6939bf9abeaSUrsula Braun struct smc_wr_rx_handler *handler; 6949bf9abeaSUrsula Braun int rc = 0; 6959bf9abeaSUrsula Braun 6969bf9abeaSUrsula Braun for (handler = smc_llc_rx_handlers; handler->handler; handler++) { 6979bf9abeaSUrsula Braun INIT_HLIST_NODE(&handler->list); 6989bf9abeaSUrsula Braun rc = smc_wr_rx_register_handler(handler); 6999bf9abeaSUrsula Braun if (rc) 7009bf9abeaSUrsula Braun break; 7019bf9abeaSUrsula Braun } 7029bf9abeaSUrsula Braun return rc; 7039bf9abeaSUrsula Braun } 704