Skip to content

Commit

Permalink
smc: link layer control (LLC)
Browse files Browse the repository at this point in the history
send and receive LLC messages CONFIRM_LINK (via IB message send and CQE)

Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ursula Braun authored and David S. Miller committed Jan 9, 2017
1 parent bd4ad57 commit 9bf9abe
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 3 deletions.
2 changes: 1 addition & 1 deletion net/smc/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
obj-$(CONFIG_SMC) += smc.o
smc-y := af_smc.o smc_pnet.o smc_ib.o smc_clc.o smc_core.o smc_wr.o
smc-y := af_smc.o smc_pnet.o smc_ib.o smc_clc.o smc_core.o smc_wr.o smc_llc.o
94 changes: 92 additions & 2 deletions net/smc/af_smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "smc.h"
#include "smc_clc.h"
#include "smc_llc.h"
#include "smc_core.h"
#include "smc_ib.h"
#include "smc_pnet.h"
Expand Down Expand Up @@ -245,6 +246,41 @@ int smc_netinfo_by_tcpsk(struct socket *clcsock,
return rc;
}

static int smc_clnt_conf_first_link(struct smc_sock *smc, union ib_gid *gid)
{
struct smc_link_group *lgr = smc->conn.lgr;
struct smc_link *link;
int rest;
int rc;

link = &lgr->lnk[SMC_SINGLE_LINK];
/* receive CONFIRM LINK request from server over RoCE fabric */
rest = wait_for_completion_interruptible_timeout(
&link->llc_confirm,
SMC_LLC_WAIT_FIRST_TIME);
if (rest <= 0) {
struct smc_clc_msg_decline dclc;

rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
SMC_CLC_DECLINE);
return rc;
}

rc = smc_ib_modify_qp_rts(link);
if (rc)
return SMC_CLC_DECL_INTERR;

smc_wr_remember_qp_attr(link);
/* send CONFIRM LINK response over RoCE fabric */
rc = smc_llc_send_confirm_link(link,
link->smcibdev->mac[link->ibport - 1],
gid, SMC_LLC_RESP);
if (rc < 0)
return SMC_CLC_DECL_TCL;

return rc;
}

static void smc_conn_save_peer_info(struct smc_sock *smc,
struct smc_clc_msg_accept_confirm *clc)
{
Expand Down Expand Up @@ -358,7 +394,17 @@ static int smc_connect_rdma(struct smc_sock *smc)
if (rc)
goto out_err_unlock;

/* tbd in follow-on patch: llc_confirm */
if (local_contact == SMC_FIRST_CONTACT) {
/* QP confirmation over RoCE fabric */
reason_code = smc_clnt_conf_first_link(
smc, &smcibdev->gid[ibport - 1]);
if (reason_code < 0) {
rc = reason_code;
goto out_err_unlock;
}
if (reason_code > 0)
goto decline_rdma_unlock;
}

mutex_unlock(&smc_create_lgr_pending);
out_connected:
Expand Down Expand Up @@ -543,6 +589,36 @@ static void smc_close_non_accepted(struct sock *sk)
sock_put(sk);
}

static int smc_serv_conf_first_link(struct smc_sock *smc)
{
struct smc_link_group *lgr = smc->conn.lgr;
struct smc_link *link;
int rest;
int rc;

link = &lgr->lnk[SMC_SINGLE_LINK];
/* send CONFIRM LINK request to client over the RoCE fabric */
rc = smc_llc_send_confirm_link(link,
link->smcibdev->mac[link->ibport - 1],
&link->smcibdev->gid[link->ibport - 1],
SMC_LLC_REQ);
if (rc < 0)
return SMC_CLC_DECL_TCL;

/* receive CONFIRM LINK response from client over the RoCE fabric */
rest = wait_for_completion_interruptible_timeout(
&link->llc_confirm_resp,
SMC_LLC_WAIT_FIRST_TIME);
if (rest <= 0) {
struct smc_clc_msg_decline dclc;

rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
SMC_CLC_DECLINE);
}

return rc;
}

/* setup for RDMA connection of server */
static void smc_listen_work(struct work_struct *work)
{
Expand Down Expand Up @@ -655,13 +731,21 @@ static void smc_listen_work(struct work_struct *work)
goto decline_rdma;
}

/* tbd in follow-on patch: modify_qp, llc_confirm */
if (local_contact == SMC_FIRST_CONTACT) {
rc = smc_ib_ready_link(link);
if (rc) {
reason_code = SMC_CLC_DECL_INTERR;
goto decline_rdma;
}
/* QP confirmation over RoCE fabric */
reason_code = smc_serv_conf_first_link(new_smc);
if (reason_code < 0) {
/* peer is not aware of a problem */
rc = reason_code;
goto out_err;
}
if (reason_code > 0)
goto decline_rdma;
}

out_connected:
Expand Down Expand Up @@ -1111,6 +1195,12 @@ static int __init smc_init(void)
if (rc)
return rc;

rc = smc_llc_init();
if (rc) {
pr_err("%s: smc_llc_init fails with %d\n", __func__, rc);
goto out_pnet;
}

rc = proto_register(&smc_proto, 1);
if (rc) {
pr_err("%s: proto_register fails with %d\n", __func__, rc);
Expand Down
2 changes: 2 additions & 0 deletions net/smc/smc_clc.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'};
#define SMC_CLC_DECL_SYNCERR 0x04000000 /* synchronization error */
#define SMC_CLC_DECL_REPLY 0x06000000 /* reply to a received decline */
#define SMC_CLC_DECL_INTERR 0x99990000 /* internal error */
#define SMC_CLC_DECL_TCL 0x02040000 /* timeout w4 QP confirm */
#define SMC_CLC_DECL_SEND 0x07000000 /* sending problem */

struct smc_clc_msg_hdr { /* header1 of clc messages */
u8 eyecatcher[4]; /* eye catcher */
Expand Down
8 changes: 8 additions & 0 deletions net/smc/smc_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
#include "smc_core.h"
#include "smc_ib.h"
#include "smc_wr.h"
#include "smc_llc.h"

#define SMC_LGR_NUM_INCR 256
#define SMC_LGR_FREE_DELAY (600 * HZ)

static u32 smc_lgr_num; /* unique link group number */

/* Register connection's alert token in our lookup structure.
* To use rbtrees we have to implement our own insert core.
* Requires @conns_lock
Expand Down Expand Up @@ -152,6 +156,8 @@ static int smc_lgr_create(struct smc_sock *smc, __be32 peer_in_addr,
INIT_LIST_HEAD(&lgr->sndbufs[i]);
INIT_LIST_HEAD(&lgr->rmbs[i]);
}
smc_lgr_num += SMC_LGR_NUM_INCR;
memcpy(&lgr->id, (u8 *)&smc_lgr_num, SMC_LGR_ID_SIZE);
INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
lgr->conns_all = RB_ROOT;

Expand All @@ -177,6 +183,8 @@ static int smc_lgr_create(struct smc_sock *smc, __be32 peer_in_addr,
rc = smc_wr_create_link(lnk);
if (rc)
goto destroy_qp;
init_completion(&lnk->llc_confirm);
init_completion(&lnk->llc_confirm_resp);

smc->conn.lgr = lgr;
rwlock_init(&lgr->conns_lock);
Expand Down
6 changes: 6 additions & 0 deletions net/smc/smc_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ struct smc_link {
u32 peer_psn; /* QP rx initial packet seqno */
u8 peer_mac[ETH_ALEN]; /* = gid[8:10||13:15] */
u8 peer_gid[sizeof(union ib_gid)]; /* gid of peer*/
u8 link_id; /* unique # within link group */
struct completion llc_confirm; /* wait for rx of conf link */
struct completion llc_confirm_resp; /* wait 4 rx of cnf lnk rsp */
};

/* For now we just allow one parallel link per link group. The SMC protocol
Expand Down Expand Up @@ -102,6 +105,8 @@ struct smc_rtoken { /* address/key of remote RMB */
u32 rkey;
};

#define SMC_LGR_ID_SIZE 4

struct smc_link_group {
struct list_head list;
enum smc_lgr_role role; /* client or server */
Expand All @@ -125,6 +130,7 @@ struct smc_link_group {
SMC_RMBS_PER_LGR_MAX)];
/* used rtoken elements */

u8 id[SMC_LGR_ID_SIZE]; /* unique lgr id */
struct delayed_work free_work; /* delayed freeing of an lgr */
bool sync_err; /* lgr no longer fits to peer */
};
Expand Down
158 changes: 158 additions & 0 deletions net/smc/smc_llc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Shared Memory Communications over RDMA (SMC-R) and RoCE
*
* Link Layer Control (LLC)
*
* For now, we only support the necessary "confirm link" functionality
* which happens for the first RoCE link after successful CLC handshake.
*
* Copyright IBM Corp. 2016
*
* Author(s): Klaus Wacker <Klaus.Wacker@de.ibm.com>
* Ursula Braun <ubraun@linux.vnet.ibm.com>
*/

#include <net/tcp.h>
#include <rdma/ib_verbs.h>

#include "smc.h"
#include "smc_core.h"
#include "smc_clc.h"
#include "smc_llc.h"

/********************************** send *************************************/

struct smc_llc_tx_pend {
};

/* handler for send/transmission completion of an LLC msg */
static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend,
struct smc_link *link,
enum ib_wc_status wc_status)
{
/* future work: handle wc_status error for recovery and failover */
}

/**
* smc_llc_add_pending_send() - add LLC control message to pending WQE transmits
* @link: Pointer to SMC link used for sending LLC control message.
* @wr_buf: Out variable returning pointer to work request payload buffer.
* @pend: Out variable returning pointer to private pending WR tracking.
* It's the context the transmit complete handler will get.
*
* Reserves and pre-fills an entry for a pending work request send/tx.
* Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx.
* Can sleep due to smc_get_ctrl_buf (if not in softirq context).
*
* Return: 0 on success, otherwise an error value.
*/
static int smc_llc_add_pending_send(struct smc_link *link,
struct smc_wr_buf **wr_buf,
struct smc_wr_tx_pend_priv **pend)
{
int rc;

rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, pend);
if (rc < 0)
return rc;
BUILD_BUG_ON_MSG(
sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE,
"must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)");
BUILD_BUG_ON_MSG(
sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE,
"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()");
BUILD_BUG_ON_MSG(
sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE,
"must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)");
return 0;
}

/* high-level API to send LLC confirm link */
int smc_llc_send_confirm_link(struct smc_link *link, u8 mac[],
union ib_gid *gid,
enum smc_llc_reqresp reqresp)
{
struct smc_link_group *lgr = container_of(link, struct smc_link_group,
lnk[SMC_SINGLE_LINK]);
struct smc_llc_msg_confirm_link *confllc;
struct smc_wr_tx_pend_priv *pend;
struct smc_wr_buf *wr_buf;
int rc;

rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
if (rc)
return rc;
confllc = (struct smc_llc_msg_confirm_link *)wr_buf;
memset(confllc, 0, sizeof(*confllc));
confllc->hd.common.type = SMC_LLC_CONFIRM_LINK;
confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link);
if (reqresp == SMC_LLC_RESP)
confllc->hd.flags |= SMC_LLC_FLAG_RESP;
memcpy(confllc->sender_mac, mac, ETH_ALEN);
memcpy(confllc->sender_gid, gid, SMC_GID_SIZE);
hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
/* confllc->link_num = SMC_SINGLE_LINK; already done by memset above */
memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
confllc->max_links = SMC_LINKS_PER_LGR_MAX;
/* send llc message */
rc = smc_wr_tx_send(link, pend);
return rc;
}

/********************************* receive ***********************************/

static void smc_llc_rx_confirm_link(struct smc_link *link,
struct smc_llc_msg_confirm_link *llc)
{
struct smc_link_group *lgr;

lgr = container_of(link, struct smc_link_group, lnk[SMC_SINGLE_LINK]);
if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
if (lgr->role == SMC_SERV)
complete(&link->llc_confirm_resp);
} else {
if (lgr->role == SMC_CLNT) {
link->link_id = llc->link_num;
complete(&link->llc_confirm);
}
}
}

static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
{
struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
union smc_llc_msg *llc = buf;

if (wc->byte_len < sizeof(*llc))
return; /* short message */
if (llc->raw.hdr.length != sizeof(*llc))
return; /* invalid message */
if (llc->raw.hdr.common.type == SMC_LLC_CONFIRM_LINK)
smc_llc_rx_confirm_link(link, &llc->confirm_link);
}

/***************************** init, exit, misc ******************************/

static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
{
.handler = smc_llc_rx_handler,
.type = SMC_LLC_CONFIRM_LINK
},
{
.handler = NULL,
}
};

int __init smc_llc_init(void)
{
struct smc_wr_rx_handler *handler;
int rc = 0;

for (handler = smc_llc_rx_handlers; handler->handler; handler++) {
INIT_HLIST_NODE(&handler->list);
rc = smc_wr_rx_register_handler(handler);
if (rc)
break;
}
return rc;
}
Loading

0 comments on commit 9bf9abe

Please sign in to comment.