Commit be589d04 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-smc-introduce-SMC-Dv2-support'

Karsten Graul says:

====================
net/smc: introduce SMC-Dv2 support

SMC-Dv2 support (see https://www.ibm.com/support/pages/node/6326337)
provides multi-subnet support for SMC-D, eliminating the current
same-subnet restriction. The new version detects if any of the virtual
ISM devices are on the same system and can therefore be used for an
SMC-Dv2 connection. Furthermore, SMC-Dv2 eliminates the need for
PNET IDs on s390.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 414698f6 e8d726c8
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define ISM_DMB_WORD_OFFSET 1 #define ISM_DMB_WORD_OFFSET 1
#define ISM_DMB_BIT_OFFSET (ISM_DMB_WORD_OFFSET * 32) #define ISM_DMB_BIT_OFFSET (ISM_DMB_WORD_OFFSET * 32)
#define ISM_NR_DMBS 1920 #define ISM_NR_DMBS 1920
#define ISM_IDENT_MASK 0x00FFFF
#define ISM_REG_SBA 0x1 #define ISM_REG_SBA 0x1
#define ISM_REG_IEQ 0x2 #define ISM_REG_IEQ 0x2
...@@ -206,6 +207,12 @@ struct ism_dev { ...@@ -206,6 +207,12 @@ struct ism_dev {
#define ISM_CREATE_REQ(dmb, idx, sf, offset) \ #define ISM_CREATE_REQ(dmb, idx, sf, offset) \
((dmb) | (idx) << 24 | (sf) << 23 | (offset)) ((dmb) | (idx) << 24 | (sf) << 23 | (offset))
struct ism_systemeid {
u8 seid_string[24];
u8 serial_number[4];
u8 type[4];
};
static inline void __ism_read_cmd(struct ism_dev *ism, void *data, static inline void __ism_read_cmd(struct ism_dev *ism, void *data,
unsigned long offset, unsigned long len) unsigned long offset, unsigned long len)
{ {
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/ctype.h>
#include <linux/processor.h>
#include <net/smc.h> #include <net/smc.h>
#include <asm/debug.h> #include <asm/debug.h>
...@@ -387,6 +389,42 @@ static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx, ...@@ -387,6 +389,42 @@ static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx,
return 0; return 0;
} }
static struct ism_systemeid SYSTEM_EID = {
.seid_string = "IBM-SYSZ-IBMSEID00000000",
.serial_number = "0000",
.type = "0000",
};
static void ism_create_system_eid(void)
{
struct cpuid id;
u16 ident_tail;
char tmp[5];
get_cpu_id(&id);
ident_tail = (u16)(id.ident & ISM_IDENT_MASK);
snprintf(tmp, 5, "%04X", ident_tail);
memcpy(&SYSTEM_EID.serial_number, tmp, 4);
snprintf(tmp, 5, "%04X", id.machine);
memcpy(&SYSTEM_EID.type, tmp, 4);
}
static void ism_get_system_eid(struct smcd_dev *smcd, u8 **eid)
{
*eid = &SYSTEM_EID.seid_string[0];
}
static u16 ism_get_chid(struct smcd_dev *smcd)
{
struct ism_dev *ismdev;
ismdev = (struct ism_dev *)smcd->priv;
if (!ismdev || !ismdev->pdev)
return 0;
return to_zpci(ismdev->pdev)->pchid;
}
static void ism_handle_event(struct ism_dev *ism) static void ism_handle_event(struct ism_dev *ism)
{ {
struct smcd_event *entry; struct smcd_event *entry;
...@@ -443,6 +481,8 @@ static const struct smcd_ops ism_ops = { ...@@ -443,6 +481,8 @@ static const struct smcd_ops ism_ops = {
.reset_vlan_required = ism_reset_vlan_required, .reset_vlan_required = ism_reset_vlan_required,
.signal_event = ism_signal_ieq, .signal_event = ism_signal_ieq,
.move_data = ism_move, .move_data = ism_move,
.get_system_eid = ism_get_system_eid,
.get_chid = ism_get_chid,
}; };
static int ism_dev_init(struct ism_dev *ism) static int ism_dev_init(struct ism_dev *ism)
...@@ -471,6 +511,10 @@ static int ism_dev_init(struct ism_dev *ism) ...@@ -471,6 +511,10 @@ static int ism_dev_init(struct ism_dev *ism)
if (ret) if (ret)
goto unreg_ieq; goto unreg_ieq;
if (!ism_add_vlan_id(ism->smcd, ISM_RESERVED_VLANID))
/* hardware is V2 capable */
ism_create_system_eid();
ret = smcd_register_dev(ism->smcd); ret = smcd_register_dev(ism->smcd);
if (ret) if (ret)
goto unreg_ieq; goto unreg_ieq;
...@@ -550,6 +594,9 @@ static void ism_dev_exit(struct ism_dev *ism) ...@@ -550,6 +594,9 @@ static void ism_dev_exit(struct ism_dev *ism)
struct pci_dev *pdev = ism->pdev; struct pci_dev *pdev = ism->pdev;
smcd_unregister_dev(ism->smcd); smcd_unregister_dev(ism->smcd);
if (SYSTEM_EID.serial_number[0] != '0' ||
SYSTEM_EID.type[0] != '0')
ism_del_vlan_id(ism->smcd, ISM_RESERVED_VLANID);
unregister_ieq(ism); unregister_ieq(ism);
unregister_sba(ism); unregister_sba(ism);
free_irq(pci_irq_vector(pdev, 0), ism); free_irq(pci_irq_vector(pdev, 0), ism);
......
...@@ -37,6 +37,8 @@ struct smcd_dmb { ...@@ -37,6 +37,8 @@ struct smcd_dmb {
#define ISM_EVENT_GID 1 #define ISM_EVENT_GID 1
#define ISM_EVENT_SWR 2 #define ISM_EVENT_SWR 2
#define ISM_RESERVED_VLANID 0x1FFF
#define ISM_ERROR 0xFFFF #define ISM_ERROR 0xFFFF
struct smcd_event { struct smcd_event {
...@@ -63,6 +65,8 @@ struct smcd_ops { ...@@ -63,6 +65,8 @@ struct smcd_ops {
int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx, int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx,
bool sf, unsigned int offset, void *data, bool sf, unsigned int offset, void *data,
unsigned int size); unsigned int size);
void (*get_system_eid)(struct smcd_dev *dev, u8 **eid);
u16 (*get_chid)(struct smcd_dev *dev);
}; };
struct smcd_dev { struct smcd_dev {
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/rcupdate_wait.h> #include <linux/rcupdate_wait.h>
#include <linux/ctype.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/tcp.h> #include <net/tcp.h>
...@@ -448,6 +449,16 @@ static void smcr_conn_save_peer_info(struct smc_sock *smc, ...@@ -448,6 +449,16 @@ static void smcr_conn_save_peer_info(struct smc_sock *smc,
smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1); smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
} }
static bool smc_isascii(char *hostname)
{
int i;
for (i = 0; i < SMC_MAX_HOSTNAME_LEN; i++)
if (!isascii(hostname[i]))
return false;
return true;
}
static void smcd_conn_save_peer_info(struct smc_sock *smc, static void smcd_conn_save_peer_info(struct smc_sock *smc,
struct smc_clc_msg_accept_confirm *clc) struct smc_clc_msg_accept_confirm *clc)
{ {
...@@ -459,6 +470,22 @@ static void smcd_conn_save_peer_info(struct smc_sock *smc, ...@@ -459,6 +470,22 @@ static void smcd_conn_save_peer_info(struct smc_sock *smc,
smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg); smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg);
atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size); atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx; smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx;
if (clc->hdr.version > SMC_V1 &&
(clc->hdr.typev2 & SMC_FIRST_CONTACT_MASK)) {
struct smc_clc_msg_accept_confirm_v2 *clc_v2 =
(struct smc_clc_msg_accept_confirm_v2 *)clc;
struct smc_clc_first_contact_ext *fce =
(struct smc_clc_first_contact_ext *)
(((u8 *)clc_v2) + sizeof(*clc_v2));
memcpy(smc->conn.lgr->negotiated_eid, clc_v2->eid,
SMC_MAX_EID_LEN);
smc->conn.lgr->peer_os = fce->os_type;
smc->conn.lgr->peer_smc_release = fce->release;
if (smc_isascii(fce->hostname))
memcpy(smc->conn.lgr->peer_hostname, fce->hostname,
SMC_MAX_HOSTNAME_LEN);
}
} }
static void smc_conn_save_peer_info(struct smc_sock *smc, static void smc_conn_save_peer_info(struct smc_sock *smc,
...@@ -504,7 +531,8 @@ static int smc_connect_fallback(struct smc_sock *smc, int reason_code) ...@@ -504,7 +531,8 @@ static int smc_connect_fallback(struct smc_sock *smc, int reason_code)
} }
/* decline and fall back during connect */ /* decline and fall back during connect */
static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code) static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code,
u8 version)
{ {
int rc; int rc;
...@@ -514,7 +542,7 @@ static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code) ...@@ -514,7 +542,7 @@ static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code)
return reason_code; return reason_code;
} }
if (reason_code != SMC_CLC_DECL_PEERDECL) { if (reason_code != SMC_CLC_DECL_PEERDECL) {
rc = smc_clc_send_decline(smc, reason_code); rc = smc_clc_send_decline(smc, reason_code, version);
if (rc < 0) { if (rc < 0) {
if (smc->sk.sk_state == SMC_INIT) if (smc->sk.sk_state == SMC_INIT)
sock_put(&smc->sk); /* passive closing */ sock_put(&smc->sk); /* passive closing */
...@@ -564,47 +592,121 @@ static int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -564,47 +592,121 @@ static int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini)
{ {
/* Find ISM device with same PNETID as connecting interface */ /* Find ISM device with same PNETID as connecting interface */
smc_pnet_find_ism_resource(smc->clcsock->sk, ini); smc_pnet_find_ism_resource(smc->clcsock->sk, ini);
if (!ini->ism_dev) if (!ini->ism_dev[0])
return SMC_CLC_DECL_NOSMCDDEV; return SMC_CLC_DECL_NOSMCDDEV;
else
ini->ism_chid[0] = smc_ism_get_chid(ini->ism_dev[0]);
return 0; return 0;
} }
/* determine possible V2 ISM devices (either without PNETID or with PNETID plus
* PNETID matching net_device)
*/
static int smc_find_ism_v2_device_clnt(struct smc_sock *smc,
struct smc_init_info *ini)
{
int rc = SMC_CLC_DECL_NOSMCDDEV;
struct smcd_dev *smcd;
int i = 1;
if (smcd_indicated(ini->smc_type_v1))
rc = 0; /* already initialized for V1 */
mutex_lock(&smcd_dev_list.mutex);
list_for_each_entry(smcd, &smcd_dev_list.list, list) {
if (smcd->going_away || smcd == ini->ism_dev[0])
continue;
if (!smc_pnet_is_pnetid_set(smcd->pnetid) ||
smc_pnet_is_ndev_pnetid(sock_net(&smc->sk), smcd->pnetid)) {
ini->ism_dev[i] = smcd;
ini->ism_chid[i] = smc_ism_get_chid(ini->ism_dev[i]);
ini->is_smcd = true;
rc = 0;
i++;
if (i > SMC_MAX_ISM_DEVS)
break;
}
}
mutex_unlock(&smcd_dev_list.mutex);
ini->ism_offered_cnt = i - 1;
if (!ini->ism_dev[0] && !ini->ism_dev[1])
ini->smcd_version = 0;
return rc;
}
/* Check for VLAN ID and register it on ISM device just for CLC handshake */ /* Check for VLAN ID and register it on ISM device just for CLC handshake */
static int smc_connect_ism_vlan_setup(struct smc_sock *smc, static int smc_connect_ism_vlan_setup(struct smc_sock *smc,
struct smc_init_info *ini) struct smc_init_info *ini)
{ {
if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev, ini->vlan_id)) if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev[0], ini->vlan_id))
return SMC_CLC_DECL_ISMVLANERR; return SMC_CLC_DECL_ISMVLANERR;
return 0; return 0;
} }
static int smc_find_proposal_devices(struct smc_sock *smc,
struct smc_init_info *ini)
{
int rc = 0;
/* check if there is an ism device available */
if (ini->smcd_version & SMC_V1) {
if (smc_find_ism_device(smc, ini) ||
smc_connect_ism_vlan_setup(smc, ini)) {
if (ini->smc_type_v1 == SMC_TYPE_B)
ini->smc_type_v1 = SMC_TYPE_R;
else
ini->smc_type_v1 = SMC_TYPE_N;
} /* else ISM V1 is supported for this connection */
if (smc_find_rdma_device(smc, ini)) {
if (ini->smc_type_v1 == SMC_TYPE_B)
ini->smc_type_v1 = SMC_TYPE_D;
else
ini->smc_type_v1 = SMC_TYPE_N;
} /* else RDMA is supported for this connection */
}
if (smc_ism_v2_capable && smc_find_ism_v2_device_clnt(smc, ini))
ini->smc_type_v2 = SMC_TYPE_N;
/* if neither ISM nor RDMA are supported, fallback */
if (!smcr_indicated(ini->smc_type_v1) &&
ini->smc_type_v1 == SMC_TYPE_N && ini->smc_type_v2 == SMC_TYPE_N)
rc = SMC_CLC_DECL_NOSMCDEV;
return rc;
}
/* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is /* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is
* used, the VLAN ID will be registered again during the connection setup. * used, the VLAN ID will be registered again during the connection setup.
*/ */
static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc, bool is_smcd, static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc,
struct smc_init_info *ini) struct smc_init_info *ini)
{ {
if (!is_smcd) if (!smcd_indicated(ini->smc_type_v1))
return 0; return 0;
if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev, ini->vlan_id)) if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id))
return SMC_CLC_DECL_CNFERR; return SMC_CLC_DECL_CNFERR;
return 0; return 0;
} }
#define SMC_CLC_MAX_ACCEPT_LEN \
(sizeof(struct smc_clc_msg_accept_confirm_v2) + \
sizeof(struct smc_clc_first_contact_ext) + \
sizeof(struct smc_clc_msg_trail))
/* CLC handshake during connect */ /* CLC handshake during connect */
static int smc_connect_clc(struct smc_sock *smc, int smc_type, static int smc_connect_clc(struct smc_sock *smc,
struct smc_clc_msg_accept_confirm *aclc, struct smc_clc_msg_accept_confirm_v2 *aclc2,
struct smc_init_info *ini) struct smc_init_info *ini)
{ {
int rc = 0; int rc = 0;
/* do inband token exchange */ /* do inband token exchange */
rc = smc_clc_send_proposal(smc, smc_type, ini); rc = smc_clc_send_proposal(smc, ini);
if (rc) if (rc)
return rc; return rc;
/* receive SMC Accept CLC message */ /* receive SMC Accept CLC message */
return smc_clc_wait_msg(smc, aclc, sizeof(*aclc), SMC_CLC_ACCEPT, return smc_clc_wait_msg(smc, aclc2, SMC_CLC_MAX_ACCEPT_LEN,
CLC_WAIT_TIME); SMC_CLC_ACCEPT, CLC_WAIT_TIME);
} }
/* setup for RDMA connection of client */ /* setup for RDMA connection of client */
...@@ -618,7 +720,7 @@ static int smc_connect_rdma(struct smc_sock *smc, ...@@ -618,7 +720,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
ini->is_smcd = false; ini->is_smcd = false;
ini->ib_lcl = &aclc->r0.lcl; ini->ib_lcl = &aclc->r0.lcl;
ini->ib_clcqpn = ntoh24(aclc->r0.qpn); ini->ib_clcqpn = ntoh24(aclc->r0.qpn);
ini->first_contact_peer = aclc->hdr.flag; ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
mutex_lock(&smc_client_lgr_pending); mutex_lock(&smc_client_lgr_pending);
reason_code = smc_conn_create(smc, ini); reason_code = smc_conn_create(smc, ini);
...@@ -678,7 +780,8 @@ static int smc_connect_rdma(struct smc_sock *smc, ...@@ -678,7 +780,8 @@ static int smc_connect_rdma(struct smc_sock *smc,
} }
smc_rmb_sync_sg_for_device(&smc->conn); smc_rmb_sync_sg_for_device(&smc->conn);
reason_code = smc_clc_send_confirm(smc); reason_code = smc_clc_send_confirm(smc, ini->first_contact_local,
SMC_V1);
if (reason_code) if (reason_code)
return smc_connect_abort(smc, reason_code, return smc_connect_abort(smc, reason_code,
ini->first_contact_local); ini->first_contact_local);
...@@ -704,6 +807,25 @@ static int smc_connect_rdma(struct smc_sock *smc, ...@@ -704,6 +807,25 @@ static int smc_connect_rdma(struct smc_sock *smc,
return 0; return 0;
} }
/* The server has chosen one of the proposed ISM devices for the communication.
* Determine from the CHID of the received CLC ACCEPT the ISM device chosen.
*/
static int
smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm_v2 *aclc,
struct smc_init_info *ini)
{
int i;
for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
if (ini->ism_chid[i] == ntohs(aclc->chid)) {
ini->ism_selected = i;
return 0;
}
}
return -EPROTO;
}
/* setup for ISM connection of client */ /* setup for ISM connection of client */
static int smc_connect_ism(struct smc_sock *smc, static int smc_connect_ism(struct smc_sock *smc,
struct smc_clc_msg_accept_confirm *aclc, struct smc_clc_msg_accept_confirm *aclc,
...@@ -712,8 +834,17 @@ static int smc_connect_ism(struct smc_sock *smc, ...@@ -712,8 +834,17 @@ static int smc_connect_ism(struct smc_sock *smc,
int rc = 0; int rc = 0;
ini->is_smcd = true; ini->is_smcd = true;
ini->ism_peer_gid = aclc->d0.gid; ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
ini->first_contact_peer = aclc->hdr.flag;
if (aclc->hdr.version == SMC_V2) {
struct smc_clc_msg_accept_confirm_v2 *aclc_v2 =
(struct smc_clc_msg_accept_confirm_v2 *)aclc;
rc = smc_v2_determine_accepted_chid(aclc_v2, ini);
if (rc)
return rc;
}
ini->ism_peer_gid[ini->ism_selected] = aclc->d0.gid;
/* there is only one lgr role for SMC-D; use server lock */ /* there is only one lgr role for SMC-D; use server lock */
mutex_lock(&smc_server_lgr_pending); mutex_lock(&smc_server_lgr_pending);
...@@ -736,7 +867,8 @@ static int smc_connect_ism(struct smc_sock *smc, ...@@ -736,7 +867,8 @@ static int smc_connect_ism(struct smc_sock *smc,
smc_rx_init(smc); smc_rx_init(smc);
smc_tx_init(smc); smc_tx_init(smc);
rc = smc_clc_send_confirm(smc); rc = smc_clc_send_confirm(smc, ini->first_contact_local,
aclc->hdr.version);
if (rc) if (rc)
return smc_connect_abort(smc, rc, ini->first_contact_local); return smc_connect_abort(smc, rc, ini->first_contact_local);
mutex_unlock(&smc_server_lgr_pending); mutex_unlock(&smc_server_lgr_pending);
...@@ -749,13 +881,32 @@ static int smc_connect_ism(struct smc_sock *smc, ...@@ -749,13 +881,32 @@ static int smc_connect_ism(struct smc_sock *smc,
return 0; return 0;
} }
/* check if received accept type and version matches a proposed one */
static int smc_connect_check_aclc(struct smc_init_info *ini,
struct smc_clc_msg_accept_confirm *aclc)
{
if ((aclc->hdr.typev1 == SMC_TYPE_R &&
!smcr_indicated(ini->smc_type_v1)) ||
(aclc->hdr.typev1 == SMC_TYPE_D &&
((!smcd_indicated(ini->smc_type_v1) &&
!smcd_indicated(ini->smc_type_v2)) ||
(aclc->hdr.version == SMC_V1 &&
!smcd_indicated(ini->smc_type_v1)) ||
(aclc->hdr.version == SMC_V2 &&
!smcd_indicated(ini->smc_type_v2)))))
return SMC_CLC_DECL_MODEUNSUPP;
return 0;
}
/* perform steps before actually connecting */ /* perform steps before actually connecting */
static int __smc_connect(struct smc_sock *smc) static int __smc_connect(struct smc_sock *smc)
{ {
bool ism_supported = false, rdma_supported = false; u8 version = smc_ism_v2_capable ? SMC_V2 : SMC_V1;
struct smc_clc_msg_accept_confirm aclc; struct smc_clc_msg_accept_confirm_v2 *aclc2;
struct smc_init_info ini = {0}; struct smc_clc_msg_accept_confirm *aclc;
int smc_type; struct smc_init_info *ini = NULL;
u8 *buf = NULL;
int rc = 0; int rc = 0;
if (smc->use_fallback) if (smc->use_fallback)
...@@ -765,58 +916,73 @@ static int __smc_connect(struct smc_sock *smc) ...@@ -765,58 +916,73 @@ static int __smc_connect(struct smc_sock *smc)
if (!tcp_sk(smc->clcsock->sk)->syn_smc) if (!tcp_sk(smc->clcsock->sk)->syn_smc)
return smc_connect_fallback(smc, SMC_CLC_DECL_PEERNOSMC); return smc_connect_fallback(smc, SMC_CLC_DECL_PEERNOSMC);
/* IPSec connections opt out of SMC-R optimizations */ /* IPSec connections opt out of SMC optimizations */
if (using_ipsec(smc)) if (using_ipsec(smc))
return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC); return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC,
version);
/* get vlan id from IP device */ ini = kzalloc(sizeof(*ini), GFP_KERNEL);
if (smc_vlan_by_tcpsk(smc->clcsock, &ini)) if (!ini)
return smc_connect_decline_fallback(smc, return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM,
SMC_CLC_DECL_GETVLANERR); version);
/* check if there is an ism device available */ ini->smcd_version = SMC_V1;
if (!smc_find_ism_device(smc, &ini) && ini->smcd_version |= smc_ism_v2_capable ? SMC_V2 : 0;
!smc_connect_ism_vlan_setup(smc, &ini)) { ini->smc_type_v1 = SMC_TYPE_B;
/* ISM is supported for this connection */ ini->smc_type_v2 = smc_ism_v2_capable ? SMC_TYPE_D : SMC_TYPE_N;
ism_supported = true;
smc_type = SMC_TYPE_D; /* get vlan id from IP device */
} if (smc_vlan_by_tcpsk(smc->clcsock, ini)) {
ini->smcd_version &= ~SMC_V1;
/* check if there is a rdma device available */ ini->smc_type_v1 = SMC_TYPE_N;
if (!smc_find_rdma_device(smc, &ini)) { if (!ini->smcd_version) {
/* RDMA is supported for this connection */ rc = SMC_CLC_DECL_GETVLANERR;
rdma_supported = true; goto fallback;
if (ism_supported) }
smc_type = SMC_TYPE_B; /* both */
else
smc_type = SMC_TYPE_R; /* only RDMA */
} }
/* if neither ISM nor RDMA are supported, fallback */ rc = smc_find_proposal_devices(smc, ini);
if (!rdma_supported && !ism_supported) if (rc)
return smc_connect_decline_fallback(smc, SMC_CLC_DECL_NOSMCDEV); goto fallback;
/* perform CLC handshake */ buf = kzalloc(SMC_CLC_MAX_ACCEPT_LEN, GFP_KERNEL);
rc = smc_connect_clc(smc, smc_type, &aclc, &ini); if (!buf) {
if (rc) { rc = SMC_CLC_DECL_MEM;
smc_connect_ism_vlan_cleanup(smc, ism_supported, &ini); goto fallback;
return smc_connect_decline_fallback(smc, rc);
} }
aclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf;
aclc = (struct smc_clc_msg_accept_confirm *)aclc2;
/* perform CLC handshake */
rc = smc_connect_clc(smc, aclc2, ini);
if (rc)
goto vlan_cleanup;
/* check if smc modes and versions of CLC proposal and accept match */
rc = smc_connect_check_aclc(ini, aclc);
version = aclc->hdr.version == SMC_V1 ? SMC_V1 : version;
if (rc)
goto vlan_cleanup;
/* depending on previous steps, connect using rdma or ism */ /* depending on previous steps, connect using rdma or ism */
if (rdma_supported && aclc.hdr.path == SMC_TYPE_R) if (aclc->hdr.typev1 == SMC_TYPE_R)
rc = smc_connect_rdma(smc, &aclc, &ini); rc = smc_connect_rdma(smc, aclc, ini);
else if (ism_supported && aclc.hdr.path == SMC_TYPE_D) else if (aclc->hdr.typev1 == SMC_TYPE_D)
rc = smc_connect_ism(smc, &aclc, &ini); rc = smc_connect_ism(smc, aclc, ini);
else if (rc)
rc = SMC_CLC_DECL_MODEUNSUPP; goto vlan_cleanup;
if (rc) {
smc_connect_ism_vlan_cleanup(smc, ism_supported, &ini);
return smc_connect_decline_fallback(smc, rc);
}
smc_connect_ism_vlan_cleanup(smc, ism_supported, &ini); smc_connect_ism_vlan_cleanup(smc, ini);
kfree(buf);
kfree(ini);
return 0; return 0;
vlan_cleanup:
smc_connect_ism_vlan_cleanup(smc, ini);
kfree(buf);
fallback:
kfree(ini);
return smc_connect_decline_fallback(smc, rc, version);
} }
static void smc_connect_work(struct work_struct *work) static void smc_connect_work(struct work_struct *work)
...@@ -1132,10 +1298,10 @@ static void smc_listen_out_err(struct smc_sock *new_smc) ...@@ -1132,10 +1298,10 @@ static void smc_listen_out_err(struct smc_sock *new_smc)
/* listen worker: decline and fall back if possible */ /* listen worker: decline and fall back if possible */
static void smc_listen_decline(struct smc_sock *new_smc, int reason_code, static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
bool local_first) struct smc_init_info *ini, u8 version)
{ {
/* RDMA setup failed, switch back to TCP */ /* RDMA setup failed, switch back to TCP */
if (local_first) if (ini->first_contact_local)
smc_lgr_cleanup_early(&new_smc->conn); smc_lgr_cleanup_early(&new_smc->conn);
else else
smc_conn_free(&new_smc->conn); smc_conn_free(&new_smc->conn);
...@@ -1146,7 +1312,7 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code, ...@@ -1146,7 +1312,7 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
smc_switch_to_fallback(new_smc); smc_switch_to_fallback(new_smc);
new_smc->fallback_rsn = reason_code; new_smc->fallback_rsn = reason_code;
if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) { if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
if (smc_clc_send_decline(new_smc, reason_code) < 0) { if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
smc_listen_out_err(new_smc); smc_listen_out_err(new_smc);
return; return;
} }
...@@ -1154,6 +1320,47 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code, ...@@ -1154,6 +1320,47 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
smc_listen_out_connected(new_smc); smc_listen_out_connected(new_smc);
} }
/* listen worker: version checking */
static int smc_listen_v2_check(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini)
{
struct smc_clc_smcd_v2_extension *pclc_smcd_v2_ext;
struct smc_clc_v2_extension *pclc_v2_ext;
ini->smc_type_v1 = pclc->hdr.typev1;
ini->smc_type_v2 = pclc->hdr.typev2;
ini->smcd_version = ini->smc_type_v1 != SMC_TYPE_N ? SMC_V1 : 0;
if (pclc->hdr.version > SMC_V1)
ini->smcd_version |=
ini->smc_type_v2 != SMC_TYPE_N ? SMC_V2 : 0;
if (!smc_ism_v2_capable) {
ini->smcd_version &= ~SMC_V2;
goto out;
}
pclc_v2_ext = smc_get_clc_v2_ext(pclc);
if (!pclc_v2_ext) {
ini->smcd_version &= ~SMC_V2;
goto out;
}
pclc_smcd_v2_ext = smc_get_clc_smcd_v2_ext(pclc_v2_ext);
if (!pclc_smcd_v2_ext)
ini->smcd_version &= ~SMC_V2;
out:
if (!ini->smcd_version) {
if (pclc->hdr.typev1 == SMC_TYPE_B ||
pclc->hdr.typev2 == SMC_TYPE_B)
return SMC_CLC_DECL_NOSMCDEV;
if (pclc->hdr.typev1 == SMC_TYPE_D ||
pclc->hdr.typev2 == SMC_TYPE_D)
return SMC_CLC_DECL_NOSMCDDEV;
return SMC_CLC_DECL_NOSMCRDEV;
}
return 0;
}
/* listen worker: check prefixes */ /* listen worker: check prefixes */
static int smc_listen_prfx_check(struct smc_sock *new_smc, static int smc_listen_prfx_check(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc) struct smc_clc_msg_proposal *pclc)
...@@ -1161,6 +1368,8 @@ static int smc_listen_prfx_check(struct smc_sock *new_smc, ...@@ -1161,6 +1368,8 @@ static int smc_listen_prfx_check(struct smc_sock *new_smc,
struct smc_clc_msg_proposal_prefix *pclc_prfx; struct smc_clc_msg_proposal_prefix *pclc_prfx;
struct socket *newclcsock = new_smc->clcsock; struct socket *newclcsock = new_smc->clcsock;
if (pclc->hdr.typev1 == SMC_TYPE_N)
return 0;
pclc_prfx = smc_clc_proposal_get_prefix(pclc); pclc_prfx = smc_clc_proposal_get_prefix(pclc);
if (smc_clc_prfx_match(newclcsock, pclc_prfx)) if (smc_clc_prfx_match(newclcsock, pclc_prfx))
return SMC_CLC_DECL_DIFFPREFIX; return SMC_CLC_DECL_DIFFPREFIX;
...@@ -1188,7 +1397,6 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc, ...@@ -1188,7 +1397,6 @@ static int smc_listen_rdma_init(struct smc_sock *new_smc,
/* listen worker: initialize connection and buffers for SMC-D */ /* listen worker: initialize connection and buffers for SMC-D */
static int smc_listen_ism_init(struct smc_sock *new_smc, static int smc_listen_ism_init(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini) struct smc_init_info *ini)
{ {
int rc; int rc;
...@@ -1211,6 +1419,125 @@ static int smc_listen_ism_init(struct smc_sock *new_smc, ...@@ -1211,6 +1419,125 @@ static int smc_listen_ism_init(struct smc_sock *new_smc,
return 0; return 0;
} }
static bool smc_is_already_selected(struct smcd_dev *smcd,
struct smc_init_info *ini,
int matches)
{
int i;
for (i = 0; i < matches; i++)
if (smcd == ini->ism_dev[i])
return true;
return false;
}
/* check for ISM devices matching proposed ISM devices */
static void smc_check_ism_v2_match(struct smc_init_info *ini,
u16 proposed_chid, u64 proposed_gid,
unsigned int *matches)
{
struct smcd_dev *smcd;
list_for_each_entry(smcd, &smcd_dev_list.list, list) {
if (smcd->going_away)
continue;
if (smc_is_already_selected(smcd, ini, *matches))
continue;
if (smc_ism_get_chid(smcd) == proposed_chid &&
!smc_ism_cantalk(proposed_gid, ISM_RESERVED_VLANID, smcd)) {
ini->ism_peer_gid[*matches] = proposed_gid;
ini->ism_dev[*matches] = smcd;
(*matches)++;
break;
}
}
}
static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini)
{
struct smc_clc_smcd_v2_extension *smcd_v2_ext;
struct smc_clc_v2_extension *smc_v2_ext;
struct smc_clc_msg_smcd *pclc_smcd;
unsigned int matches = 0;
u8 *eid = NULL;
int i;
if (!(ini->smcd_version & SMC_V2) || !smcd_indicated(ini->smc_type_v2))
return;
pclc_smcd = smc_get_clc_msg_smcd(pclc);
smc_v2_ext = smc_get_clc_v2_ext(pclc);
smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext);
if (!smcd_v2_ext ||
!smc_v2_ext->hdr.flag.seid) /* no system EID support for SMCD */
goto not_found;
mutex_lock(&smcd_dev_list.mutex);
if (pclc_smcd->ism.chid)
/* check for ISM device matching proposed native ISM device */
smc_check_ism_v2_match(ini, ntohs(pclc_smcd->ism.chid),
ntohll(pclc_smcd->ism.gid), &matches);
for (i = 1; i <= smc_v2_ext->hdr.ism_gid_cnt; i++) {
/* check for ISM devices matching proposed non-native ISM
* devices
*/
smc_check_ism_v2_match(ini,
ntohs(smcd_v2_ext->gidchid[i - 1].chid),
ntohll(smcd_v2_ext->gidchid[i - 1].gid),
&matches);
}
mutex_unlock(&smcd_dev_list.mutex);
if (ini->ism_dev[0]) {
smc_ism_get_system_eid(ini->ism_dev[0], &eid);
if (memcmp(eid, smcd_v2_ext->system_eid, SMC_MAX_EID_LEN))
goto not_found;
} else {
goto not_found;
}
/* separate - outside the smcd_dev_list.lock */
for (i = 0; i < matches; i++) {
ini->smcd_version = SMC_V2;
ini->is_smcd = true;
ini->ism_selected = i;
if (smc_listen_ism_init(new_smc, ini))
/* try next active ISM device */
continue;
return; /* matching and usable V2 ISM device found */
}
not_found:
ini->smcd_version &= ~SMC_V2;
ini->ism_dev[0] = NULL;
ini->is_smcd = false;
}
static void smc_find_ism_v1_device_serv(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini)
{
struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc);
/* check if ISM V1 is available */
if (!(ini->smcd_version & SMC_V1) || !smcd_indicated(ini->smc_type_v1))
goto not_found;
ini->is_smcd = true; /* prepare ISM check */
ini->ism_peer_gid[0] = ntohll(pclc_smcd->ism.gid);
if (smc_find_ism_device(new_smc, ini))
goto not_found;
ini->ism_selected = 0;
if (!smc_listen_ism_init(new_smc, ini))
return; /* V1 ISM device found */
not_found:
ini->ism_dev[0] = NULL;
ini->is_smcd = false;
}
/* listen worker: register buffers */ /* listen worker: register buffers */
static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first) static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
{ {
...@@ -1225,6 +1552,67 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first) ...@@ -1225,6 +1552,67 @@ static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
return 0; return 0;
} }
static int smc_find_rdma_v1_device_serv(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini)
{
int rc;
if (!smcr_indicated(ini->smc_type_v1))
return SMC_CLC_DECL_NOSMCDEV;
/* prepare RDMA check */
ini->ib_lcl = &pclc->lcl;
rc = smc_find_rdma_device(new_smc, ini);
if (rc) {
/* no RDMA device found */
if (ini->smc_type_v1 == SMC_TYPE_B)
/* neither ISM nor RDMA device found */
rc = SMC_CLC_DECL_NOSMCDEV;
return rc;
}
rc = smc_listen_rdma_init(new_smc, ini);
if (rc)
return rc;
return smc_listen_rdma_reg(new_smc, ini->first_contact_local);
}
/* determine the local device matching to proposal */
static int smc_listen_find_device(struct smc_sock *new_smc,
struct smc_clc_msg_proposal *pclc,
struct smc_init_info *ini)
{
int rc;
/* check for ISM device matching V2 proposed device */
smc_find_ism_v2_device_serv(new_smc, pclc, ini);
if (ini->ism_dev[0])
return 0;
if (!(ini->smcd_version & SMC_V1))
return SMC_CLC_DECL_NOSMCDEV;
/* check for matching IP prefix and subnet length */
rc = smc_listen_prfx_check(new_smc, pclc);
if (rc)
return rc;
/* get vlan id from IP device */
if (smc_vlan_by_tcpsk(new_smc->clcsock, ini))
return SMC_CLC_DECL_GETVLANERR;
/* check for ISM device matching V1 proposed device */
smc_find_ism_v1_device_serv(new_smc, pclc, ini);
if (ini->ism_dev[0])
return 0;
if (pclc->hdr.typev1 == SMC_TYPE_D)
return SMC_CLC_DECL_NOSMCDDEV; /* skip RDMA and decline */
/* check if RDMA is available */
return smc_find_rdma_v1_device_serv(new_smc, pclc, ini);
}
/* listen worker: finish RDMA setup */ /* listen worker: finish RDMA setup */
static int smc_listen_rdma_finish(struct smc_sock *new_smc, static int smc_listen_rdma_finish(struct smc_sock *new_smc,
struct smc_clc_msg_accept_confirm *cclc, struct smc_clc_msg_accept_confirm *cclc,
...@@ -1250,17 +1638,18 @@ static int smc_listen_rdma_finish(struct smc_sock *new_smc, ...@@ -1250,17 +1638,18 @@ static int smc_listen_rdma_finish(struct smc_sock *new_smc,
return reason_code; return reason_code;
} }
/* setup for RDMA connection of server */ /* setup for connection of server */
static void smc_listen_work(struct work_struct *work) static void smc_listen_work(struct work_struct *work)
{ {
struct smc_sock *new_smc = container_of(work, struct smc_sock, struct smc_sock *new_smc = container_of(work, struct smc_sock,
smc_listen_work); smc_listen_work);
u8 version = smc_ism_v2_capable ? SMC_V2 : SMC_V1;
struct socket *newclcsock = new_smc->clcsock; struct socket *newclcsock = new_smc->clcsock;
struct smc_clc_msg_accept_confirm cclc; struct smc_clc_msg_accept_confirm_v2 *cclc2;
struct smc_clc_msg_accept_confirm *cclc;
struct smc_clc_msg_proposal_area *buf; struct smc_clc_msg_proposal_area *buf;
struct smc_clc_msg_proposal *pclc; struct smc_clc_msg_proposal *pclc;
struct smc_init_info ini = {0}; struct smc_init_info *ini = NULL;
bool ism_supported = false;
int rc = 0; int rc = 0;
if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN) if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN)
...@@ -1292,101 +1681,76 @@ static void smc_listen_work(struct work_struct *work) ...@@ -1292,101 +1681,76 @@ static void smc_listen_work(struct work_struct *work)
SMC_CLC_PROPOSAL, CLC_WAIT_TIME); SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
if (rc) if (rc)
goto out_decl; goto out_decl;
version = pclc->hdr.version == SMC_V1 ? SMC_V1 : version;
/* IPSec connections opt out of SMC-R optimizations */ /* IPSec connections opt out of SMC optimizations */
if (using_ipsec(new_smc)) { if (using_ipsec(new_smc)) {
rc = SMC_CLC_DECL_IPSEC; rc = SMC_CLC_DECL_IPSEC;
goto out_decl; goto out_decl;
} }
/* check for matching IP prefix and subnet length */ ini = kzalloc(sizeof(*ini), GFP_KERNEL);
rc = smc_listen_prfx_check(new_smc, pclc); if (!ini) {
if (rc) rc = SMC_CLC_DECL_MEM;
goto out_decl; goto out_decl;
}
/* get vlan id from IP device */ /* initial version checking */
if (smc_vlan_by_tcpsk(new_smc->clcsock, &ini)) { rc = smc_listen_v2_check(new_smc, pclc, ini);
rc = SMC_CLC_DECL_GETVLANERR; if (rc)
goto out_decl; goto out_decl;
}
mutex_lock(&smc_server_lgr_pending); mutex_lock(&smc_server_lgr_pending);
smc_close_init(new_smc); smc_close_init(new_smc);
smc_rx_init(new_smc); smc_rx_init(new_smc);
smc_tx_init(new_smc); smc_tx_init(new_smc);
/* check if ISM is available */ /* determine ISM or RoCE device used for connection */
if (pclc->hdr.path == SMC_TYPE_D || pclc->hdr.path == SMC_TYPE_B) { rc = smc_listen_find_device(new_smc, pclc, ini);
struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc);
ini.is_smcd = true; /* prepare ISM check */
ini.ism_peer_gid = pclc_smcd->gid;
rc = smc_find_ism_device(new_smc, &ini);
if (!rc)
rc = smc_listen_ism_init(new_smc, pclc, &ini);
if (!rc)
ism_supported = true;
else if (pclc->hdr.path == SMC_TYPE_D)
goto out_unlock; /* skip RDMA and decline */
}
/* check if RDMA is available */
if (!ism_supported) { /* SMC_TYPE_R or SMC_TYPE_B */
/* prepare RDMA check */
ini.is_smcd = false;
ini.ism_dev = NULL;
ini.ib_lcl = &pclc->lcl;
rc = smc_find_rdma_device(new_smc, &ini);
if (rc) {
/* no RDMA device found */
if (pclc->hdr.path == SMC_TYPE_B)
/* neither ISM nor RDMA device found */
rc = SMC_CLC_DECL_NOSMCDEV;
goto out_unlock;
}
rc = smc_listen_rdma_init(new_smc, &ini);
if (rc)
goto out_unlock;
rc = smc_listen_rdma_reg(new_smc, ini.first_contact_local);
if (rc) if (rc)
goto out_unlock; goto out_unlock;
}
/* send SMC Accept CLC message */ /* send SMC Accept CLC message */
rc = smc_clc_send_accept(new_smc, ini.first_contact_local); rc = smc_clc_send_accept(new_smc, ini->first_contact_local,
ini->smcd_version == SMC_V2 ? SMC_V2 : SMC_V1);
if (rc) if (rc)
goto out_unlock; goto out_unlock;
/* SMC-D does not need this lock any more */ /* SMC-D does not need this lock any more */
if (ism_supported) if (ini->is_smcd)
mutex_unlock(&smc_server_lgr_pending); mutex_unlock(&smc_server_lgr_pending);
/* receive SMC Confirm CLC message */ /* receive SMC Confirm CLC message */
rc = smc_clc_wait_msg(new_smc, &cclc, sizeof(cclc), cclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf;
cclc = (struct smc_clc_msg_accept_confirm *)cclc2;
memset(buf, 0, sizeof(struct smc_clc_msg_proposal_area));
rc = smc_clc_wait_msg(new_smc, cclc2,
sizeof(struct smc_clc_msg_proposal_area),
SMC_CLC_CONFIRM, CLC_WAIT_TIME); SMC_CLC_CONFIRM, CLC_WAIT_TIME);
if (rc) { if (rc) {
if (!ism_supported) if (!ini->is_smcd)
goto out_unlock; goto out_unlock;
goto out_decl; goto out_decl;
} }
/* finish worker */ /* finish worker */
if (!ism_supported) { if (!ini->is_smcd) {
rc = smc_listen_rdma_finish(new_smc, &cclc, rc = smc_listen_rdma_finish(new_smc, cclc,
ini.first_contact_local); ini->first_contact_local);
if (rc) if (rc)
goto out_unlock; goto out_unlock;
mutex_unlock(&smc_server_lgr_pending); mutex_unlock(&smc_server_lgr_pending);
} }
smc_conn_save_peer_info(new_smc, &cclc); smc_conn_save_peer_info(new_smc, cclc);
smc_listen_out_connected(new_smc); smc_listen_out_connected(new_smc);
goto out_free; goto out_free;
out_unlock: out_unlock:
mutex_unlock(&smc_server_lgr_pending); mutex_unlock(&smc_server_lgr_pending);
out_decl: out_decl:
smc_listen_decline(new_smc, rc, ini.first_contact_local); smc_listen_decline(new_smc, rc, ini, version);
out_free: out_free:
kfree(ini);
kfree(buf); kfree(buf);
} }
...@@ -2092,6 +2456,9 @@ static int __init smc_init(void) ...@@ -2092,6 +2456,9 @@ static int __init smc_init(void)
if (rc) if (rc)
return rc; return rc;
smc_ism_init();
smc_clc_init();
rc = smc_pnet_init(); rc = smc_pnet_init();
if (rc) if (rc)
goto out_pernet_subsys; goto out_pernet_subsys;
......
...@@ -19,10 +19,19 @@ ...@@ -19,10 +19,19 @@
#include "smc_ib.h" #include "smc_ib.h"
#define SMC_V1 1 /* SMC version V1 */ #define SMC_V1 1 /* SMC version V1 */
#define SMC_V2 2 /* SMC version V2 */
#define SMC_RELEASE 0
#define SMCPROTO_SMC 0 /* SMC protocol, IPv4 */ #define SMCPROTO_SMC 0 /* SMC protocol, IPv4 */
#define SMCPROTO_SMC6 1 /* SMC protocol, IPv6 */ #define SMCPROTO_SMC6 1 /* SMC protocol, IPv6 */
#define SMC_MAX_ISM_DEVS 8 /* max # of proposed non-native ISM
* devices
*/
#define SMC_MAX_HOSTNAME_LEN 32
#define SMC_MAX_EID_LEN 32
extern struct proto smc_proto; extern struct proto smc_proto;
extern struct proto smc_proto6; extern struct proto smc_proto6;
...@@ -246,6 +255,9 @@ extern struct workqueue_struct *smc_close_wq; /* wq for close work */ ...@@ -246,6 +255,9 @@ extern struct workqueue_struct *smc_close_wq; /* wq for close work */
extern u8 local_systemid[SMC_SYSTEMID_LEN]; /* unique system identifier */ extern u8 local_systemid[SMC_SYSTEMID_LEN]; /* unique system identifier */
#define ntohll(x) be64_to_cpu(x)
#define htonll(x) cpu_to_be64(x)
/* convert an u32 value into network byte order, store it into a 3 byte field */ /* convert an u32 value into network byte order, store it into a 3 byte field */
static inline void hton24(u8 *net, u32 host) static inline void hton24(u8 *net, u32 host)
{ {
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/utsname.h>
#include <linux/ctype.h>
#include <net/addrconf.h> #include <net/addrconf.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -27,6 +29,7 @@ ...@@ -27,6 +29,7 @@
#define SMCR_CLC_ACCEPT_CONFIRM_LEN 68 #define SMCR_CLC_ACCEPT_CONFIRM_LEN 68
#define SMCD_CLC_ACCEPT_CONFIRM_LEN 48 #define SMCD_CLC_ACCEPT_CONFIRM_LEN 48
#define SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 78
#define SMC_CLC_RECV_BUF_LEN 100 #define SMC_CLC_RECV_BUF_LEN 100
/* eye catcher "SMCR" EBCDIC for CLC messages */ /* eye catcher "SMCR" EBCDIC for CLC messages */
...@@ -34,13 +37,88 @@ static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'}; ...@@ -34,13 +37,88 @@ static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'};
/* eye catcher "SMCD" EBCDIC for CLC messages */ /* eye catcher "SMCD" EBCDIC for CLC messages */
static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'}; static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'};
static u8 smc_hostname[SMC_MAX_HOSTNAME_LEN];
/* check arriving CLC proposal */
static bool smc_clc_msg_prop_valid(struct smc_clc_msg_proposal *pclc)
{
struct smc_clc_msg_proposal_prefix *pclc_prfx;
struct smc_clc_smcd_v2_extension *smcd_v2_ext;
struct smc_clc_msg_hdr *hdr = &pclc->hdr;
struct smc_clc_v2_extension *v2_ext;
v2_ext = smc_get_clc_v2_ext(pclc);
pclc_prfx = smc_clc_proposal_get_prefix(pclc);
if (hdr->version == SMC_V1) {
if (hdr->typev1 == SMC_TYPE_N)
return false;
if (ntohs(hdr->length) !=
sizeof(*pclc) + ntohs(pclc->iparea_offset) +
sizeof(*pclc_prfx) +
pclc_prfx->ipv6_prefixes_cnt *
sizeof(struct smc_clc_ipv6_prefix) +
sizeof(struct smc_clc_msg_trail))
return false;
} else {
if (ntohs(hdr->length) !=
sizeof(*pclc) +
sizeof(struct smc_clc_msg_smcd) +
(hdr->typev1 != SMC_TYPE_N ?
sizeof(*pclc_prfx) +
pclc_prfx->ipv6_prefixes_cnt *
sizeof(struct smc_clc_ipv6_prefix) : 0) +
(hdr->typev2 != SMC_TYPE_N ?
sizeof(*v2_ext) +
v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN : 0) +
(smcd_indicated(hdr->typev2) ?
sizeof(*smcd_v2_ext) + v2_ext->hdr.ism_gid_cnt *
sizeof(struct smc_clc_smcd_gid_chid) :
0) +
sizeof(struct smc_clc_msg_trail))
return false;
}
return true;
}
/* check arriving CLC accept or confirm */
static bool
smc_clc_msg_acc_conf_valid(struct smc_clc_msg_accept_confirm_v2 *clc_v2)
{
struct smc_clc_msg_hdr *hdr = &clc_v2->hdr;
if (hdr->typev1 != SMC_TYPE_R && hdr->typev1 != SMC_TYPE_D)
return false;
if (hdr->version == SMC_V1) {
if ((hdr->typev1 == SMC_TYPE_R &&
ntohs(hdr->length) != SMCR_CLC_ACCEPT_CONFIRM_LEN) ||
(hdr->typev1 == SMC_TYPE_D &&
ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN))
return false;
} else {
if (hdr->typev1 == SMC_TYPE_D &&
ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 &&
(ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 +
sizeof(struct smc_clc_first_contact_ext)))
return false;
}
return true;
}
static void smc_clc_fill_fce(struct smc_clc_first_contact_ext *fce, int *len)
{
memset(fce, 0, sizeof(*fce));
fce->os_type = SMC_CLC_OS_LINUX;
fce->release = SMC_RELEASE;
memcpy(fce->hostname, smc_hostname, sizeof(smc_hostname));
(*len) += sizeof(*fce);
}
/* check if received message has a correct header length and contains valid /* check if received message has a correct header length and contains valid
* heading and trailing eyecatchers * heading and trailing eyecatchers
*/ */
static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl) static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
{ {
struct smc_clc_msg_proposal_prefix *pclc_prfx; struct smc_clc_msg_accept_confirm_v2 *clc_v2;
struct smc_clc_msg_accept_confirm *clc;
struct smc_clc_msg_proposal *pclc; struct smc_clc_msg_proposal *pclc;
struct smc_clc_msg_decline *dclc; struct smc_clc_msg_decline *dclc;
struct smc_clc_msg_trail *trl; struct smc_clc_msg_trail *trl;
...@@ -51,29 +129,19 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl) ...@@ -51,29 +129,19 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
switch (clcm->type) { switch (clcm->type) {
case SMC_CLC_PROPOSAL: case SMC_CLC_PROPOSAL:
pclc = (struct smc_clc_msg_proposal *)clcm; pclc = (struct smc_clc_msg_proposal *)clcm;
pclc_prfx = smc_clc_proposal_get_prefix(pclc); if (!smc_clc_msg_prop_valid(pclc))
if (ntohs(pclc->hdr.length) <
sizeof(*pclc) + ntohs(pclc->iparea_offset) +
sizeof(*pclc_prfx) +
pclc_prfx->ipv6_prefixes_cnt *
sizeof(struct smc_clc_ipv6_prefix) +
sizeof(*trl))
return false; return false;
trl = (struct smc_clc_msg_trail *) trl = (struct smc_clc_msg_trail *)
((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl)); ((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl));
break; break;
case SMC_CLC_ACCEPT: case SMC_CLC_ACCEPT:
case SMC_CLC_CONFIRM: case SMC_CLC_CONFIRM:
if (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D) clc_v2 = (struct smc_clc_msg_accept_confirm_v2 *)clcm;
return false; if (!smc_clc_msg_acc_conf_valid(clc_v2))
clc = (struct smc_clc_msg_accept_confirm *)clcm;
if ((clcm->path == SMC_TYPE_R &&
ntohs(clc->hdr.length) != SMCR_CLC_ACCEPT_CONFIRM_LEN) ||
(clcm->path == SMC_TYPE_D &&
ntohs(clc->hdr.length) != SMCD_CLC_ACCEPT_CONFIRM_LEN))
return false; return false;
trl = (struct smc_clc_msg_trail *) trl = (struct smc_clc_msg_trail *)
((u8 *)clc + ntohs(clc->hdr.length) - sizeof(*trl)); ((u8 *)clc_v2 + ntohs(clc_v2->hdr.length) -
sizeof(*trl));
break; break;
case SMC_CLC_DECLINE: case SMC_CLC_DECLINE:
dclc = (struct smc_clc_msg_decline *)clcm; dclc = (struct smc_clc_msg_decline *)clcm;
...@@ -327,9 +395,6 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, ...@@ -327,9 +395,6 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
goto out; goto out;
} }
if (clcm->type == SMC_CLC_PROPOSAL && clcm->path == SMC_TYPE_N)
reason_code = SMC_CLC_DECL_VERSMISMAT; /* just V2 offered */
/* receive the complete CLC message */ /* receive the complete CLC message */
memset(&msg, 0, sizeof(struct msghdr)); memset(&msg, 0, sizeof(struct msghdr));
if (datlen > buflen) { if (datlen > buflen) {
...@@ -365,7 +430,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, ...@@ -365,7 +430,8 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
dclc = (struct smc_clc_msg_decline *)clcm; dclc = (struct smc_clc_msg_decline *)clcm;
reason_code = SMC_CLC_DECL_PEERDECL; reason_code = SMC_CLC_DECL_PEERDECL;
smc->peer_diagnosis = ntohl(dclc->peer_diagnosis); smc->peer_diagnosis = ntohl(dclc->peer_diagnosis);
if (((struct smc_clc_msg_decline *)buf)->hdr.flag) { if (((struct smc_clc_msg_decline *)buf)->hdr.typev2 &
SMC_FIRST_CONTACT_MASK) {
smc->conn.lgr->sync_err = 1; smc->conn.lgr->sync_err = 1;
smc_lgr_terminate_sched(smc->conn.lgr); smc_lgr_terminate_sched(smc->conn.lgr);
} }
...@@ -377,7 +443,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, ...@@ -377,7 +443,7 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
} }
/* send CLC DECLINE message across internal TCP socket */ /* send CLC DECLINE message across internal TCP socket */
int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info) int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version)
{ {
struct smc_clc_msg_decline dclc; struct smc_clc_msg_decline dclc;
struct msghdr msg; struct msghdr msg;
...@@ -388,8 +454,10 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info) ...@@ -388,8 +454,10 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
memcpy(dclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); memcpy(dclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
dclc.hdr.type = SMC_CLC_DECLINE; dclc.hdr.type = SMC_CLC_DECLINE;
dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline)); dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline));
dclc.hdr.version = SMC_V1; dclc.hdr.version = version;
dclc.hdr.flag = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ? 1 : 0; dclc.os_type = version == SMC_V1 ? 0 : SMC_CLC_OS_LINUX;
dclc.hdr.typev2 = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ?
SMC_FIRST_CONTACT_MASK : 0;
if ((!smc->conn.lgr || !smc->conn.lgr->is_smcd) && if ((!smc->conn.lgr || !smc->conn.lgr->is_smcd) &&
smc_ib_is_valid_local_systemid()) smc_ib_is_valid_local_systemid())
memcpy(dclc.id_for_peer, local_systemid, memcpy(dclc.id_for_peer, local_systemid,
...@@ -408,18 +476,20 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info) ...@@ -408,18 +476,20 @@ int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info)
} }
/* send CLC PROPOSAL message across internal TCP socket */ /* send CLC PROPOSAL message across internal TCP socket */
int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini)
struct smc_init_info *ini)
{ {
struct smc_clc_smcd_v2_extension *smcd_v2_ext;
struct smc_clc_msg_proposal_prefix *pclc_prfx; struct smc_clc_msg_proposal_prefix *pclc_prfx;
struct smc_clc_msg_proposal *pclc_base; struct smc_clc_msg_proposal *pclc_base;
struct smc_clc_smcd_gid_chid *gidchids;
struct smc_clc_msg_proposal_area *pclc; struct smc_clc_msg_proposal_area *pclc;
struct smc_clc_ipv6_prefix *ipv6_prfx; struct smc_clc_ipv6_prefix *ipv6_prfx;
struct smc_clc_v2_extension *v2_ext;
struct smc_clc_msg_smcd *pclc_smcd; struct smc_clc_msg_smcd *pclc_smcd;
struct smc_clc_msg_trail *trl; struct smc_clc_msg_trail *trl;
int len, i, plen, rc; int len, i, plen, rc;
int reason_code = 0; int reason_code = 0;
struct kvec vec[5]; struct kvec vec[8];
struct msghdr msg; struct msghdr msg;
pclc = kzalloc(sizeof(*pclc), GFP_KERNEL); pclc = kzalloc(sizeof(*pclc), GFP_KERNEL);
...@@ -430,50 +500,103 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, ...@@ -430,50 +500,103 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
pclc_smcd = &pclc->pclc_smcd; pclc_smcd = &pclc->pclc_smcd;
pclc_prfx = &pclc->pclc_prfx; pclc_prfx = &pclc->pclc_prfx;
ipv6_prfx = pclc->pclc_prfx_ipv6; ipv6_prfx = pclc->pclc_prfx_ipv6;
v2_ext = &pclc->pclc_v2_ext;
smcd_v2_ext = &pclc->pclc_smcd_v2_ext;
gidchids = pclc->pclc_gidchids;
trl = &pclc->pclc_trl; trl = &pclc->pclc_trl;
pclc_base->hdr.version = SMC_V2;
pclc_base->hdr.typev1 = ini->smc_type_v1;
pclc_base->hdr.typev2 = ini->smc_type_v2;
plen = sizeof(*pclc_base) + sizeof(*pclc_smcd) + sizeof(*trl);
/* retrieve ip prefixes for CLC proposal msg */ /* retrieve ip prefixes for CLC proposal msg */
if (ini->smc_type_v1 != SMC_TYPE_N) {
rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx); rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx);
if (rc) { if (rc) {
if (ini->smc_type_v2 == SMC_TYPE_N) {
kfree(pclc); kfree(pclc);
return SMC_CLC_DECL_CNFERR; /* configuration error */ return SMC_CLC_DECL_CNFERR;
}
pclc_base->hdr.typev1 = SMC_TYPE_N;
} else {
pclc_base->iparea_offset = htons(sizeof(*pclc_smcd));
plen += sizeof(*pclc_prfx) +
pclc_prfx->ipv6_prefixes_cnt *
sizeof(ipv6_prfx[0]);
}
} }
/* send SMC Proposal CLC message */ /* build SMC Proposal CLC message */
plen = sizeof(*pclc_base) + sizeof(*pclc_prfx) +
(pclc_prfx->ipv6_prefixes_cnt * sizeof(ipv6_prfx[0])) +
sizeof(*trl);
memcpy(pclc_base->hdr.eyecatcher, SMC_EYECATCHER, memcpy(pclc_base->hdr.eyecatcher, SMC_EYECATCHER,
sizeof(SMC_EYECATCHER)); sizeof(SMC_EYECATCHER));
pclc_base->hdr.type = SMC_CLC_PROPOSAL; pclc_base->hdr.type = SMC_CLC_PROPOSAL;
pclc_base->hdr.version = SMC_V1; /* SMC version */ if (smcr_indicated(ini->smc_type_v1)) {
pclc_base->hdr.path = smc_type;
if (smc_type == SMC_TYPE_R || smc_type == SMC_TYPE_B) {
/* add SMC-R specifics */ /* add SMC-R specifics */
memcpy(pclc_base->lcl.id_for_peer, local_systemid, memcpy(pclc_base->lcl.id_for_peer, local_systemid,
sizeof(local_systemid)); sizeof(local_systemid));
memcpy(pclc_base->lcl.gid, ini->ib_gid, SMC_GID_SIZE); memcpy(pclc_base->lcl.gid, ini->ib_gid, SMC_GID_SIZE);
memcpy(pclc_base->lcl.mac, &ini->ib_dev->mac[ini->ib_port - 1], memcpy(pclc_base->lcl.mac, &ini->ib_dev->mac[ini->ib_port - 1],
ETH_ALEN); ETH_ALEN);
pclc_base->iparea_offset = htons(0);
} }
if (smc_type == SMC_TYPE_D || smc_type == SMC_TYPE_B) { if (smcd_indicated(ini->smc_type_v1)) {
/* add SMC-D specifics */ /* add SMC-D specifics */
plen += sizeof(*pclc_smcd); if (ini->ism_dev[0]) {
pclc_base->iparea_offset = htons(sizeof(*pclc_smcd)); pclc_smcd->ism.gid = htonll(ini->ism_dev[0]->local_gid);
pclc_smcd->gid = ini->ism_dev->local_gid; pclc_smcd->ism.chid =
htons(smc_ism_get_chid(ini->ism_dev[0]));
} }
pclc_base->hdr.length = htons(plen); }
if (ini->smc_type_v2 == SMC_TYPE_N) {
pclc_smcd->v2_ext_offset = 0;
} else {
u16 v2_ext_offset;
u8 *eid = NULL;
v2_ext_offset = sizeof(*pclc_smcd) -
offsetofend(struct smc_clc_msg_smcd, v2_ext_offset);
if (ini->smc_type_v1 != SMC_TYPE_N)
v2_ext_offset += sizeof(*pclc_prfx) +
pclc_prfx->ipv6_prefixes_cnt *
sizeof(ipv6_prfx[0]);
pclc_smcd->v2_ext_offset = htons(v2_ext_offset);
v2_ext->hdr.eid_cnt = 0;
v2_ext->hdr.ism_gid_cnt = ini->ism_offered_cnt;
v2_ext->hdr.flag.release = SMC_RELEASE;
v2_ext->hdr.flag.seid = 1;
v2_ext->hdr.smcd_v2_ext_offset = htons(sizeof(*v2_ext) -
offsetofend(struct smc_clnt_opts_area_hdr,
smcd_v2_ext_offset) +
v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN);
if (ini->ism_dev[0])
smc_ism_get_system_eid(ini->ism_dev[0], &eid);
else
smc_ism_get_system_eid(ini->ism_dev[1], &eid);
if (eid)
memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN);
plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext);
if (ini->ism_offered_cnt) {
for (i = 1; i <= ini->ism_offered_cnt; i++) {
gidchids[i - 1].gid =
htonll(ini->ism_dev[i]->local_gid);
gidchids[i - 1].chid =
htons(smc_ism_get_chid(ini->ism_dev[i]));
}
plen += ini->ism_offered_cnt *
sizeof(struct smc_clc_smcd_gid_chid);
}
}
pclc_base->hdr.length = htons(plen);
memcpy(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); memcpy(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
/* send SMC Proposal CLC message */
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
i = 0; i = 0;
vec[i].iov_base = pclc_base; vec[i].iov_base = pclc_base;
vec[i++].iov_len = sizeof(*pclc_base); vec[i++].iov_len = sizeof(*pclc_base);
if (smc_type == SMC_TYPE_D || smc_type == SMC_TYPE_B) {
vec[i].iov_base = pclc_smcd; vec[i].iov_base = pclc_smcd;
vec[i++].iov_len = sizeof(*pclc_smcd); vec[i++].iov_len = sizeof(*pclc_smcd);
} if (ini->smc_type_v1 != SMC_TYPE_N) {
vec[i].iov_base = pclc_prfx; vec[i].iov_base = pclc_prfx;
vec[i++].iov_len = sizeof(*pclc_prfx); vec[i++].iov_len = sizeof(*pclc_prfx);
if (pclc_prfx->ipv6_prefixes_cnt > 0) { if (pclc_prfx->ipv6_prefixes_cnt > 0) {
...@@ -481,6 +604,18 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, ...@@ -481,6 +604,18 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt * vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt *
sizeof(ipv6_prfx[0]); sizeof(ipv6_prfx[0]);
} }
}
if (ini->smc_type_v2 != SMC_TYPE_N) {
vec[i].iov_base = v2_ext;
vec[i++].iov_len = sizeof(*v2_ext);
vec[i].iov_base = smcd_v2_ext;
vec[i++].iov_len = sizeof(*smcd_v2_ext);
if (ini->ism_offered_cnt) {
vec[i].iov_base = gidchids;
vec[i++].iov_len = ini->ism_offered_cnt *
sizeof(struct smc_clc_smcd_gid_chid);
}
}
vec[i].iov_base = trl; vec[i].iov_base = trl;
vec[i++].iov_len = sizeof(*trl); vec[i++].iov_len = sizeof(*trl);
/* due to the few bytes needed for clc-handshake this cannot block */ /* due to the few bytes needed for clc-handshake this cannot block */
...@@ -499,29 +634,47 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, ...@@ -499,29 +634,47 @@ int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
/* build and send CLC CONFIRM / ACCEPT message */ /* build and send CLC CONFIRM / ACCEPT message */
static int smc_clc_send_confirm_accept(struct smc_sock *smc, static int smc_clc_send_confirm_accept(struct smc_sock *smc,
struct smc_clc_msg_accept_confirm *clc, struct smc_clc_msg_accept_confirm_v2 *clc_v2,
int first_contact) int first_contact, u8 version)
{ {
struct smc_connection *conn = &smc->conn; struct smc_connection *conn = &smc->conn;
struct smc_clc_msg_accept_confirm *clc;
struct smc_clc_first_contact_ext fce;
struct smc_clc_msg_trail trl;
struct kvec vec[3];
struct msghdr msg; struct msghdr msg;
struct kvec vec; int i, len;
/* send SMC Confirm CLC msg */ /* send SMC Confirm CLC msg */
clc->hdr.version = SMC_V1; /* SMC version */ clc = (struct smc_clc_msg_accept_confirm *)clc_v2;
clc->hdr.version = version; /* SMC version */
if (first_contact) if (first_contact)
clc->hdr.flag = 1; clc->hdr.typev2 |= SMC_FIRST_CONTACT_MASK;
if (conn->lgr->is_smcd) { if (conn->lgr->is_smcd) {
/* SMC-D specific settings */ /* SMC-D specific settings */
memcpy(clc->hdr.eyecatcher, SMCD_EYECATCHER, memcpy(clc->hdr.eyecatcher, SMCD_EYECATCHER,
sizeof(SMCD_EYECATCHER)); sizeof(SMCD_EYECATCHER));
clc->hdr.path = SMC_TYPE_D; clc->hdr.typev1 = SMC_TYPE_D;
clc->hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN);
clc->d0.gid = conn->lgr->smcd->local_gid; clc->d0.gid = conn->lgr->smcd->local_gid;
clc->d0.token = conn->rmb_desc->token; clc->d0.token = conn->rmb_desc->token;
clc->d0.dmbe_size = conn->rmbe_size_short; clc->d0.dmbe_size = conn->rmbe_size_short;
clc->d0.dmbe_idx = 0; clc->d0.dmbe_idx = 0;
memcpy(&clc->d0.linkid, conn->lgr->id, SMC_LGR_ID_SIZE); memcpy(&clc->d0.linkid, conn->lgr->id, SMC_LGR_ID_SIZE);
memcpy(clc->d0.smcd_trl.eyecatcher, SMCD_EYECATCHER, if (version == SMC_V1) {
clc->hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN);
} else {
u8 *eid = NULL;
clc_v2->chid = htons(smc_ism_get_chid(conn->lgr->smcd));
smc_ism_get_system_eid(conn->lgr->smcd, &eid);
if (eid)
memcpy(clc_v2->eid, eid, SMC_MAX_EID_LEN);
len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2;
if (first_contact)
smc_clc_fill_fce(&fce, &len);
clc_v2->hdr.length = htons(len);
}
memcpy(trl.eyecatcher, SMCD_EYECATCHER,
sizeof(SMCD_EYECATCHER)); sizeof(SMCD_EYECATCHER));
} else { } else {
struct smc_link *link = conn->lnk; struct smc_link *link = conn->lnk;
...@@ -530,7 +683,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, ...@@ -530,7 +683,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc,
link = conn->lnk; link = conn->lnk;
memcpy(clc->hdr.eyecatcher, SMC_EYECATCHER, memcpy(clc->hdr.eyecatcher, SMC_EYECATCHER,
sizeof(SMC_EYECATCHER)); sizeof(SMC_EYECATCHER));
clc->hdr.path = SMC_TYPE_R; clc->hdr.typev1 = SMC_TYPE_R;
clc->hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN); clc->hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN);
memcpy(clc->r0.lcl.id_for_peer, local_systemid, memcpy(clc->r0.lcl.id_for_peer, local_systemid,
sizeof(local_systemid)); sizeof(local_systemid));
...@@ -554,29 +707,43 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, ...@@ -554,29 +707,43 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc,
clc->r0.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address clc->r0.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address
(conn->rmb_desc->sgt[link->link_idx].sgl)); (conn->rmb_desc->sgt[link->link_idx].sgl));
hton24(clc->r0.psn, link->psn_initial); hton24(clc->r0.psn, link->psn_initial);
memcpy(clc->r0.smcr_trl.eyecatcher, SMC_EYECATCHER, memcpy(trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER));
sizeof(SMC_EYECATCHER));
} }
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
vec.iov_base = clc; i = 0;
vec.iov_len = ntohs(clc->hdr.length); vec[i].iov_base = clc_v2;
return kernel_sendmsg(smc->clcsock, &msg, &vec, 1, if (version > SMC_V1)
vec[i++].iov_len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 - sizeof(trl);
else
vec[i++].iov_len = (clc->hdr.typev1 == SMC_TYPE_D ?
SMCD_CLC_ACCEPT_CONFIRM_LEN :
SMCR_CLC_ACCEPT_CONFIRM_LEN) -
sizeof(trl);
if (version > SMC_V1 && first_contact) {
vec[i].iov_base = &fce;
vec[i++].iov_len = sizeof(fce);
}
vec[i].iov_base = &trl;
vec[i++].iov_len = sizeof(trl);
return kernel_sendmsg(smc->clcsock, &msg, vec, 1,
ntohs(clc->hdr.length)); ntohs(clc->hdr.length));
} }
/* send CLC CONFIRM message across internal TCP socket */ /* send CLC CONFIRM message across internal TCP socket */
int smc_clc_send_confirm(struct smc_sock *smc) int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact,
u8 version)
{ {
struct smc_clc_msg_accept_confirm cclc; struct smc_clc_msg_accept_confirm_v2 cclc_v2;
int reason_code = 0; int reason_code = 0;
int len; int len;
/* send SMC Confirm CLC msg */ /* send SMC Confirm CLC msg */
memset(&cclc, 0, sizeof(cclc)); memset(&cclc_v2, 0, sizeof(cclc_v2));
cclc.hdr.type = SMC_CLC_CONFIRM; cclc_v2.hdr.type = SMC_CLC_CONFIRM;
len = smc_clc_send_confirm_accept(smc, &cclc, 0); len = smc_clc_send_confirm_accept(smc, &cclc_v2, clnt_first_contact,
if (len < ntohs(cclc.hdr.length)) { version);
if (len < ntohs(cclc_v2.hdr.length)) {
if (len >= 0) { if (len >= 0) {
reason_code = -ENETUNREACH; reason_code = -ENETUNREACH;
smc->sk.sk_err = -reason_code; smc->sk.sk_err = -reason_code;
...@@ -589,16 +756,28 @@ int smc_clc_send_confirm(struct smc_sock *smc) ...@@ -589,16 +756,28 @@ int smc_clc_send_confirm(struct smc_sock *smc)
} }
/* send CLC ACCEPT message across internal TCP socket */ /* send CLC ACCEPT message across internal TCP socket */
int smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact) int smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact,
u8 version)
{ {
struct smc_clc_msg_accept_confirm aclc; struct smc_clc_msg_accept_confirm_v2 aclc_v2;
int len; int len;
memset(&aclc, 0, sizeof(aclc)); memset(&aclc_v2, 0, sizeof(aclc_v2));
aclc.hdr.type = SMC_CLC_ACCEPT; aclc_v2.hdr.type = SMC_CLC_ACCEPT;
len = smc_clc_send_confirm_accept(new_smc, &aclc, srv_first_contact); len = smc_clc_send_confirm_accept(new_smc, &aclc_v2, srv_first_contact,
if (len < ntohs(aclc.hdr.length)) version);
if (len < ntohs(aclc_v2.hdr.length))
len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err; len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err;
return len > 0 ? 0 : len; return len > 0 ? 0 : len;
} }
void __init smc_clc_init(void)
{
struct new_utsname *u;
memset(smc_hostname, _S, sizeof(smc_hostname)); /* ASCII blanks */
u = utsname();
memcpy(smc_hostname, u->nodename,
min_t(size_t, strlen(u->nodename), sizeof(smc_hostname)));
}
...@@ -54,19 +54,19 @@ ...@@ -54,19 +54,19 @@
#define SMC_CLC_DECL_ERR_RDYLNK 0x09990002 /* ib ready link failed */ #define SMC_CLC_DECL_ERR_RDYLNK 0x09990002 /* ib ready link failed */
#define SMC_CLC_DECL_ERR_REGRMB 0x09990003 /* reg rmb failed */ #define SMC_CLC_DECL_ERR_REGRMB 0x09990003 /* reg rmb failed */
#define SMC_FIRST_CONTACT_MASK 0b10 /* first contact bit within typev2 */
struct smc_clc_msg_hdr { /* header1 of clc messages */ struct smc_clc_msg_hdr { /* header1 of clc messages */
u8 eyecatcher[4]; /* eye catcher */ u8 eyecatcher[4]; /* eye catcher */
u8 type; /* proposal / accept / confirm / decline */ u8 type; /* proposal / accept / confirm / decline */
__be16 length; __be16 length;
#if defined(__BIG_ENDIAN_BITFIELD) #if defined(__BIG_ENDIAN_BITFIELD)
u8 version : 4, u8 version : 4,
flag : 1, typev2 : 2,
rsvd : 1, typev1 : 2;
path : 2;
#elif defined(__LITTLE_ENDIAN_BITFIELD) #elif defined(__LITTLE_ENDIAN_BITFIELD)
u8 path : 2, u8 typev1 : 2,
rsvd : 1, typev2 : 2,
flag : 1,
version : 4; version : 4;
#endif #endif
} __packed; /* format defined in RFC7609 */ } __packed; /* format defined in RFC7609 */
...@@ -81,8 +81,6 @@ struct smc_clc_msg_local { /* header2 of clc messages */ ...@@ -81,8 +81,6 @@ struct smc_clc_msg_local { /* header2 of clc messages */
u8 mac[6]; /* mac of ib_device port */ u8 mac[6]; /* mac of ib_device port */
}; };
#define SMC_CLC_MAX_V6_PREFIX 8
/* Struct would be 4 byte aligned, but it is used in an array that is sent /* Struct would be 4 byte aligned, but it is used in an array that is sent
* to peers and must conform to RFC7609, hence we need to use packed here. * to peers and must conform to RFC7609, hence we need to use packed here.
*/ */
...@@ -91,6 +89,44 @@ struct smc_clc_ipv6_prefix { ...@@ -91,6 +89,44 @@ struct smc_clc_ipv6_prefix {
u8 prefix_len; u8 prefix_len;
} __packed; /* format defined in RFC7609 */ } __packed; /* format defined in RFC7609 */
#if defined(__BIG_ENDIAN_BITFIELD)
struct smc_clc_v2_flag {
u8 release : 4,
rsvd : 3,
seid : 1;
};
#elif defined(__LITTLE_ENDIAN_BITFIELD)
struct smc_clc_v2_flag {
u8 seid : 1,
rsvd : 3,
release : 4;
};
#endif
struct smc_clnt_opts_area_hdr {
u8 eid_cnt; /* number of user defined EIDs */
u8 ism_gid_cnt; /* number of ISMv2 GIDs */
u8 reserved1;
struct smc_clc_v2_flag flag;
u8 reserved2[2];
__be16 smcd_v2_ext_offset; /* SMC-Dv2 Extension Offset */
};
struct smc_clc_smcd_gid_chid {
__be64 gid; /* ISM GID */
__be16 chid; /* ISMv2 CHID */
} __packed; /* format defined in
* IBM Shared Memory Communications Version 2
* (https://www.ibm.com/support/pages/node/6326337)
*/
struct smc_clc_v2_extension {
struct smc_clnt_opts_area_hdr hdr;
u8 roce[16]; /* RoCEv2 GID */
u8 reserved[16];
u8 user_eids[0][SMC_MAX_EID_LEN];
};
struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/ struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/
__be32 outgoing_subnet; /* subnet mask */ __be32 outgoing_subnet; /* subnet mask */
u8 prefix_len; /* number of significant bits in mask */ u8 prefix_len; /* number of significant bits in mask */
...@@ -99,8 +135,15 @@ struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/ ...@@ -99,8 +135,15 @@ struct smc_clc_msg_proposal_prefix { /* prefix part of clc proposal message*/
} __aligned(4); } __aligned(4);
struct smc_clc_msg_smcd { /* SMC-D GID information */ struct smc_clc_msg_smcd { /* SMC-D GID information */
u64 gid; /* ISM GID of requestor */ struct smc_clc_smcd_gid_chid ism; /* ISM native GID+CHID of requestor */
u8 res[32]; __be16 v2_ext_offset; /* SMC Version 2 Extension Offset */
u8 reserved[28];
};
struct smc_clc_smcd_v2_extension {
u8 system_eid[SMC_MAX_EID_LEN];
u8 reserved[16];
struct smc_clc_smcd_gid_chid gidchid[0];
}; };
struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */
...@@ -109,11 +152,16 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */ ...@@ -109,11 +152,16 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */
__be16 iparea_offset; /* offset to IP address information area */ __be16 iparea_offset; /* offset to IP address information area */
} __aligned(4); } __aligned(4);
#define SMC_CLC_MAX_V6_PREFIX 8
struct smc_clc_msg_proposal_area { struct smc_clc_msg_proposal_area {
struct smc_clc_msg_proposal pclc_base; struct smc_clc_msg_proposal pclc_base;
struct smc_clc_msg_smcd pclc_smcd; struct smc_clc_msg_smcd pclc_smcd;
struct smc_clc_msg_proposal_prefix pclc_prfx; struct smc_clc_msg_proposal_prefix pclc_prfx;
struct smc_clc_ipv6_prefix pclc_prfx_ipv6[SMC_CLC_MAX_V6_PREFIX]; struct smc_clc_ipv6_prefix pclc_prfx_ipv6[SMC_CLC_MAX_V6_PREFIX];
struct smc_clc_v2_extension pclc_v2_ext;
struct smc_clc_smcd_v2_extension pclc_smcd_v2_ext;
struct smc_clc_smcd_gid_chid pclc_gidchids[SMC_MAX_ISM_DEVS];
struct smc_clc_msg_trail pclc_trl; struct smc_clc_msg_trail pclc_trl;
}; };
...@@ -134,11 +182,9 @@ struct smcr_clc_msg_accept_confirm { /* SMCR accept/confirm */ ...@@ -134,11 +182,9 @@ struct smcr_clc_msg_accept_confirm { /* SMCR accept/confirm */
__be64 rmb_dma_addr; /* RMB virtual address */ __be64 rmb_dma_addr; /* RMB virtual address */
u8 reserved2; u8 reserved2;
u8 psn[3]; /* packet sequence number */ u8 psn[3]; /* packet sequence number */
struct smc_clc_msg_trail smcr_trl;
/* eye catcher "SMCR" EBCDIC */
} __packed; } __packed;
struct smcd_clc_msg_accept_confirm { /* SMCD accept/confirm */ struct smcd_clc_msg_accept_confirm_common { /* SMCD accept/confirm */
u64 gid; /* Sender GID */ u64 gid; /* Sender GID */
u64 token; /* DMB token */ u64 token; /* DMB token */
u8 dmbe_idx; /* DMBE index */ u8 dmbe_idx; /* DMBE index */
...@@ -150,26 +196,63 @@ struct smcd_clc_msg_accept_confirm { /* SMCD accept/confirm */ ...@@ -150,26 +196,63 @@ struct smcd_clc_msg_accept_confirm { /* SMCD accept/confirm */
dmbe_size : 4; dmbe_size : 4;
#endif #endif
u16 reserved4; u16 reserved4;
u32 linkid; /* Link identifier */ __be32 linkid; /* Link identifier */
u32 reserved5[3];
struct smc_clc_msg_trail smcd_trl;
/* eye catcher "SMCD" EBCDIC */
} __packed; } __packed;
#define SMC_CLC_OS_ZOS 1
#define SMC_CLC_OS_LINUX 2
#define SMC_CLC_OS_AIX 3
struct smc_clc_first_contact_ext {
u8 reserved1;
#if defined(__BIG_ENDIAN_BITFIELD)
u8 os_type : 4,
release : 4;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
u8 release : 4,
os_type : 4;
#endif
u8 reserved2[2];
u8 hostname[SMC_MAX_HOSTNAME_LEN];
};
struct smc_clc_msg_accept_confirm { /* clc accept / confirm message */ struct smc_clc_msg_accept_confirm { /* clc accept / confirm message */
struct smc_clc_msg_hdr hdr; struct smc_clc_msg_hdr hdr;
union { union {
struct smcr_clc_msg_accept_confirm r0; /* SMC-R */ struct smcr_clc_msg_accept_confirm r0; /* SMC-R */
struct smcd_clc_msg_accept_confirm d0; /* SMC-D */ struct { /* SMC-D */
struct smcd_clc_msg_accept_confirm_common d0;
u32 reserved5[3];
};
}; };
} __packed; /* format defined in RFC7609 */ } __packed; /* format defined in RFC7609 */
struct smc_clc_msg_accept_confirm_v2 { /* clc accept / confirm message */
struct smc_clc_msg_hdr hdr;
union {
struct smcr_clc_msg_accept_confirm r0; /* SMC-R */
struct { /* SMC-D */
struct smcd_clc_msg_accept_confirm_common d0;
__be16 chid;
u8 eid[SMC_MAX_EID_LEN];
u8 reserved5[8];
};
};
};
struct smc_clc_msg_decline { /* clc decline message */ struct smc_clc_msg_decline { /* clc decline message */
struct smc_clc_msg_hdr hdr; struct smc_clc_msg_hdr hdr;
u8 id_for_peer[SMC_SYSTEMID_LEN]; /* sender peer_id */ u8 id_for_peer[SMC_SYSTEMID_LEN]; /* sender peer_id */
__be32 peer_diagnosis; /* diagnosis information */ __be32 peer_diagnosis; /* diagnosis information */
u8 reserved2[4]; #if defined(__BIG_ENDIAN_BITFIELD)
struct smc_clc_msg_trail trl; /* eye catcher "SMCR" EBCDIC */ u8 os_type : 4,
reserved : 4;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
u8 reserved : 4,
os_type : 4;
#endif
u8 reserved2[3];
struct smc_clc_msg_trail trl; /* eye catcher "SMCD" or "SMCR" EBCDIC */
} __aligned(4); } __aligned(4);
/* determine start of the prefix area within the proposal message */ /* determine start of the prefix area within the proposal message */
...@@ -180,16 +263,58 @@ smc_clc_proposal_get_prefix(struct smc_clc_msg_proposal *pclc) ...@@ -180,16 +263,58 @@ smc_clc_proposal_get_prefix(struct smc_clc_msg_proposal *pclc)
((u8 *)pclc + sizeof(*pclc) + ntohs(pclc->iparea_offset)); ((u8 *)pclc + sizeof(*pclc) + ntohs(pclc->iparea_offset));
} }
static inline bool smcr_indicated(int smc_type)
{
return smc_type == SMC_TYPE_R || smc_type == SMC_TYPE_B;
}
static inline bool smcd_indicated(int smc_type)
{
return smc_type == SMC_TYPE_D || smc_type == SMC_TYPE_B;
}
/* get SMC-D info from proposal message */ /* get SMC-D info from proposal message */
static inline struct smc_clc_msg_smcd * static inline struct smc_clc_msg_smcd *
smc_get_clc_msg_smcd(struct smc_clc_msg_proposal *prop) smc_get_clc_msg_smcd(struct smc_clc_msg_proposal *prop)
{ {
if (ntohs(prop->iparea_offset) != sizeof(struct smc_clc_msg_smcd)) if (smcd_indicated(prop->hdr.typev1) &&
ntohs(prop->iparea_offset) != sizeof(struct smc_clc_msg_smcd))
return NULL; return NULL;
return (struct smc_clc_msg_smcd *)(prop + 1); return (struct smc_clc_msg_smcd *)(prop + 1);
} }
static inline struct smc_clc_v2_extension *
smc_get_clc_v2_ext(struct smc_clc_msg_proposal *prop)
{
struct smc_clc_msg_smcd *prop_smcd = smc_get_clc_msg_smcd(prop);
if (!prop_smcd || !ntohs(prop_smcd->v2_ext_offset))
return NULL;
return (struct smc_clc_v2_extension *)
((u8 *)prop_smcd +
offsetof(struct smc_clc_msg_smcd, v2_ext_offset) +
sizeof(prop_smcd->v2_ext_offset) +
ntohs(prop_smcd->v2_ext_offset));
}
static inline struct smc_clc_smcd_v2_extension *
smc_get_clc_smcd_v2_ext(struct smc_clc_v2_extension *prop_v2ext)
{
if (!prop_v2ext)
return NULL;
if (!ntohs(prop_v2ext->hdr.smcd_v2_ext_offset))
return NULL;
return (struct smc_clc_smcd_v2_extension *)
((u8 *)prop_v2ext +
offsetof(struct smc_clc_v2_extension, hdr) +
offsetof(struct smc_clnt_opts_area_hdr, smcd_v2_ext_offset) +
sizeof(prop_v2ext->hdr.smcd_v2_ext_offset) +
ntohs(prop_v2ext->hdr.smcd_v2_ext_offset));
}
struct smcd_dev; struct smcd_dev;
struct smc_init_info; struct smc_init_info;
...@@ -197,10 +322,12 @@ int smc_clc_prfx_match(struct socket *clcsock, ...@@ -197,10 +322,12 @@ int smc_clc_prfx_match(struct socket *clcsock,
struct smc_clc_msg_proposal_prefix *prop); struct smc_clc_msg_proposal_prefix *prop);
int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
u8 expected_type, unsigned long timeout); u8 expected_type, unsigned long timeout);
int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info); int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version);
int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini);
struct smc_init_info *ini); int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact,
int smc_clc_send_confirm(struct smc_sock *smc); u8 version);
int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact); int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact,
u8 version);
void smc_clc_init(void) __init;
#endif #endif
...@@ -375,7 +375,8 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -375,7 +375,8 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
int i; int i;
if (ini->is_smcd && ini->vlan_id) { if (ini->is_smcd && ini->vlan_id) {
if (smc_ism_get_vlan(ini->ism_dev, ini->vlan_id)) { if (smc_ism_get_vlan(ini->ism_dev[ini->ism_selected],
ini->vlan_id)) {
rc = SMC_CLC_DECL_ISMVLANERR; rc = SMC_CLC_DECL_ISMVLANERR;
goto out; goto out;
} }
...@@ -412,13 +413,14 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -412,13 +413,14 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
lgr->conns_all = RB_ROOT; lgr->conns_all = RB_ROOT;
if (ini->is_smcd) { if (ini->is_smcd) {
/* SMC-D specific settings */ /* SMC-D specific settings */
get_device(&ini->ism_dev->dev); get_device(&ini->ism_dev[ini->ism_selected]->dev);
lgr->peer_gid = ini->ism_peer_gid; lgr->peer_gid = ini->ism_peer_gid[ini->ism_selected];
lgr->smcd = ini->ism_dev; lgr->smcd = ini->ism_dev[ini->ism_selected];
lgr_list = &ini->ism_dev->lgr_list; lgr_list = &ini->ism_dev[ini->ism_selected]->lgr_list;
lgr_lock = &lgr->smcd->lgr_lock; lgr_lock = &lgr->smcd->lgr_lock;
lgr->smc_version = ini->smcd_version;
lgr->peer_shutdown = 0; lgr->peer_shutdown = 0;
atomic_inc(&ini->ism_dev->lgr_cnt); atomic_inc(&ini->ism_dev[ini->ism_selected]->lgr_cnt);
} else { } else {
/* SMC-R specific settings */ /* SMC-R specific settings */
lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT; lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
...@@ -449,7 +451,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -449,7 +451,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
kfree(lgr); kfree(lgr);
ism_put_vlan: ism_put_vlan:
if (ini->is_smcd && ini->vlan_id) if (ini->is_smcd && ini->vlan_id)
smc_ism_put_vlan(ini->ism_dev, ini->vlan_id); smc_ism_put_vlan(ini->ism_dev[ini->ism_selected], ini->vlan_id);
out: out:
if (rc < 0) { if (rc < 0) {
if (rc == -ENOMEM) if (rc == -ENOMEM)
...@@ -1288,8 +1290,10 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -1288,8 +1290,10 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
spinlock_t *lgr_lock; spinlock_t *lgr_lock;
int rc = 0; int rc = 0;
lgr_list = ini->is_smcd ? &ini->ism_dev->lgr_list : &smc_lgr_list.list; lgr_list = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_list :
lgr_lock = ini->is_smcd ? &ini->ism_dev->lgr_lock : &smc_lgr_list.lock; &smc_lgr_list.list;
lgr_lock = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_lock :
&smc_lgr_list.lock;
ini->first_contact_local = 1; ini->first_contact_local = 1;
role = smc->listen_smc ? SMC_SERV : SMC_CLNT; role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
if (role == SMC_CLNT && ini->first_contact_peer) if (role == SMC_CLNT && ini->first_contact_peer)
...@@ -1301,7 +1305,8 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -1301,7 +1305,8 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
list_for_each_entry(lgr, lgr_list, list) { list_for_each_entry(lgr, lgr_list, list) {
write_lock_bh(&lgr->conns_lock); write_lock_bh(&lgr->conns_lock);
if ((ini->is_smcd ? if ((ini->is_smcd ?
smcd_lgr_match(lgr, ini->ism_dev, ini->ism_peer_gid) : smcd_lgr_match(lgr, ini->ism_dev[ini->ism_selected],
ini->ism_peer_gid[ini->ism_selected]) :
smcr_lgr_match(lgr, ini->ib_lcl, role, ini->ib_clcqpn)) && smcr_lgr_match(lgr, ini->ib_lcl, role, ini->ib_clcqpn)) &&
!lgr->sync_err && !lgr->sync_err &&
lgr->vlan_id == ini->vlan_id && lgr->vlan_id == ini->vlan_id &&
......
...@@ -231,6 +231,11 @@ struct smc_link_group { ...@@ -231,6 +231,11 @@ struct smc_link_group {
u8 freeing : 1; /* lgr is being freed */ u8 freeing : 1; /* lgr is being freed */
bool is_smcd; /* SMC-R or SMC-D */ bool is_smcd; /* SMC-R or SMC-D */
u8 smc_version;
u8 negotiated_eid[SMC_MAX_EID_LEN];
u8 peer_os; /* peer operating system */
u8 peer_smc_release;
u8 peer_hostname[SMC_MAX_HOSTNAME_LEN];
union { union {
struct { /* SMC-R */ struct { /* SMC-R */
enum smc_lgr_role role; enum smc_lgr_role role;
...@@ -291,6 +296,8 @@ struct smc_clc_msg_local; ...@@ -291,6 +296,8 @@ struct smc_clc_msg_local;
struct smc_init_info { struct smc_init_info {
u8 is_smcd; u8 is_smcd;
u8 smc_type_v1;
u8 smc_type_v2;
u8 first_contact_peer; u8 first_contact_peer;
u8 first_contact_local; u8 first_contact_local;
unsigned short vlan_id; unsigned short vlan_id;
...@@ -301,8 +308,12 @@ struct smc_init_info { ...@@ -301,8 +308,12 @@ struct smc_init_info {
u8 ib_port; u8 ib_port;
u32 ib_clcqpn; u32 ib_clcqpn;
/* SMC-D */ /* SMC-D */
u64 ism_peer_gid; u64 ism_peer_gid[SMC_MAX_ISM_DEVS + 1];
struct smcd_dev *ism_dev; struct smcd_dev *ism_dev[SMC_MAX_ISM_DEVS + 1];
u16 ism_chid[SMC_MAX_ISM_DEVS + 1];
u8 ism_offered_cnt; /* # of ISM devices offered */
u8 ism_selected; /* index of selected ISM dev*/
u8 smcd_version;
}; };
/* Find the connection associated with the given alert token in the link group. /* Find the connection associated with the given alert token in the link group.
......
...@@ -21,7 +21,9 @@ struct smcd_dev_list smcd_dev_list = { ...@@ -21,7 +21,9 @@ struct smcd_dev_list smcd_dev_list = {
.mutex = __MUTEX_INITIALIZER(smcd_dev_list.mutex) .mutex = __MUTEX_INITIALIZER(smcd_dev_list.mutex)
}; };
/* Test if an ISM communication is possible. */ bool smc_ism_v2_capable;
/* Test if an ISM communication is possible - same CPC */
int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd) int smc_ism_cantalk(u64 peer_gid, unsigned short vlan_id, struct smcd_dev *smcd)
{ {
return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0, return smcd->ops->query_remote_gid(smcd, peer_gid, vlan_id ? 1 : 0,
...@@ -39,6 +41,16 @@ int smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos, ...@@ -39,6 +41,16 @@ int smc_ism_write(struct smcd_dev *smcd, const struct smc_ism_position *pos,
return rc < 0 ? rc : 0; return rc < 0 ? rc : 0;
} }
void smc_ism_get_system_eid(struct smcd_dev *smcd, u8 **eid)
{
smcd->ops->get_system_eid(smcd, eid);
}
u16 smc_ism_get_chid(struct smcd_dev *smcd)
{
return smcd->ops->get_chid(smcd);
}
/* Set a connection using this DMBE. */ /* Set a connection using this DMBE. */
void smc_ism_set_conn(struct smc_connection *conn) void smc_ism_set_conn(struct smc_connection *conn)
{ {
...@@ -319,7 +331,18 @@ EXPORT_SYMBOL_GPL(smcd_alloc_dev); ...@@ -319,7 +331,18 @@ EXPORT_SYMBOL_GPL(smcd_alloc_dev);
int smcd_register_dev(struct smcd_dev *smcd) int smcd_register_dev(struct smcd_dev *smcd)
{ {
mutex_lock(&smcd_dev_list.mutex); mutex_lock(&smcd_dev_list.mutex);
if (list_empty(&smcd_dev_list.list)) {
u8 *system_eid = NULL;
smc_ism_get_system_eid(smcd, &system_eid);
if ((*system_eid) + 24 != '0' || (*system_eid) + 28 != '0')
smc_ism_v2_capable = true;
}
/* sort list: devices without pnetid before devices with pnetid */
if (smcd->pnetid[0])
list_add_tail(&smcd->list, &smcd_dev_list.list); list_add_tail(&smcd->list, &smcd_dev_list.list);
else
list_add(&smcd->list, &smcd_dev_list.list);
mutex_unlock(&smcd_dev_list.mutex); mutex_unlock(&smcd_dev_list.mutex);
pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n", pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n",
...@@ -399,3 +422,8 @@ void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno) ...@@ -399,3 +422,8 @@ void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno)
spin_unlock_irqrestore(&smcd->lock, flags); spin_unlock_irqrestore(&smcd->lock, flags);
} }
EXPORT_SYMBOL_GPL(smcd_handle_irq); EXPORT_SYMBOL_GPL(smcd_handle_irq);
void __init smc_ism_init(void)
{
smc_ism_v2_capable = false;
}
...@@ -20,6 +20,9 @@ struct smcd_dev_list { /* List of SMCD devices */ ...@@ -20,6 +20,9 @@ struct smcd_dev_list { /* List of SMCD devices */
}; };
extern struct smcd_dev_list smcd_dev_list; /* list of smcd devices */ extern struct smcd_dev_list smcd_dev_list; /* list of smcd devices */
extern bool smc_ism_v2_capable; /* HW supports ISM V2 and thus
* System EID is defined
*/
struct smc_ism_vlanid { /* VLAN id set on ISM device */ struct smc_ism_vlanid { /* VLAN id set on ISM device */
struct list_head list; struct list_head list;
...@@ -47,4 +50,7 @@ int smc_ism_unregister_dmb(struct smcd_dev *dev, struct smc_buf_desc *dmb_desc); ...@@ -47,4 +50,7 @@ int smc_ism_unregister_dmb(struct smcd_dev *dev, struct smc_buf_desc *dmb_desc);
int smc_ism_write(struct smcd_dev *dev, const struct smc_ism_position *pos, int smc_ism_write(struct smcd_dev *dev, const struct smc_ism_position *pos,
void *data, size_t len); void *data, size_t len);
int smc_ism_signal_shutdown(struct smc_link_group *lgr); int smc_ism_signal_shutdown(struct smc_link_group *lgr);
void smc_ism_get_system_eid(struct smcd_dev *dev, u8 **eid);
u16 smc_ism_get_chid(struct smcd_dev *dev);
void smc_ism_init(void);
#endif #endif
...@@ -16,5 +16,6 @@ extern unsigned int smc_net_id; ...@@ -16,5 +16,6 @@ extern unsigned int smc_net_id;
/* per-network namespace private data */ /* per-network namespace private data */
struct smc_net { struct smc_net {
struct smc_pnettable pnettable; struct smc_pnettable pnettable;
struct smc_pnetids_ndev pnetids_ndev;
}; };
#endif #endif
...@@ -29,8 +29,7 @@ ...@@ -29,8 +29,7 @@
#include "smc_ism.h" #include "smc_ism.h"
#include "smc_core.h" #include "smc_core.h"
#define SMC_ASCII_BLANK 32 static struct net_device *__pnet_find_base_ndev(struct net_device *ndev);
static struct net_device *pnet_find_base_ndev(struct net_device *ndev); static struct net_device *pnet_find_base_ndev(struct net_device *ndev);
static const struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = { static const struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = {
...@@ -73,14 +72,22 @@ struct smc_pnetentry { ...@@ -73,14 +72,22 @@ struct smc_pnetentry {
}; };
}; };
/* Check if the pnetid is set */
bool smc_pnet_is_pnetid_set(u8 *pnetid)
{
if (pnetid[0] == 0 || pnetid[0] == _S)
return false;
return true;
}
/* Check if two given pnetids match */ /* Check if two given pnetids match */
static bool smc_pnet_match(u8 *pnetid1, u8 *pnetid2) static bool smc_pnet_match(u8 *pnetid1, u8 *pnetid2)
{ {
int i; int i;
for (i = 0; i < SMC_MAX_PNETID_LEN; i++) { for (i = 0; i < SMC_MAX_PNETID_LEN; i++) {
if ((pnetid1[i] == 0 || pnetid1[i] == SMC_ASCII_BLANK) && if ((pnetid1[i] == 0 || pnetid1[i] == _S) &&
(pnetid2[i] == 0 || pnetid2[i] == SMC_ASCII_BLANK)) (pnetid2[i] == 0 || pnetid2[i] == _S))
break; break;
if (pnetid1[i] != pnetid2[i]) if (pnetid1[i] != pnetid2[i])
return false; return false;
...@@ -238,11 +245,10 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev) ...@@ -238,11 +245,10 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
static bool smc_pnet_apply_ib(struct smc_ib_device *ib_dev, u8 ib_port, static bool smc_pnet_apply_ib(struct smc_ib_device *ib_dev, u8 ib_port,
char *pnet_name) char *pnet_name)
{ {
u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
bool applied = false; bool applied = false;
mutex_lock(&smc_ib_devices.mutex); mutex_lock(&smc_ib_devices.mutex);
if (smc_pnet_match(ib_dev->pnetid[ib_port - 1], pnet_null)) { if (!smc_pnet_is_pnetid_set(ib_dev->pnetid[ib_port - 1])) {
memcpy(ib_dev->pnetid[ib_port - 1], pnet_name, memcpy(ib_dev->pnetid[ib_port - 1], pnet_name,
SMC_MAX_PNETID_LEN); SMC_MAX_PNETID_LEN);
ib_dev->pnetid_by_user[ib_port - 1] = true; ib_dev->pnetid_by_user[ib_port - 1] = true;
...@@ -256,11 +262,10 @@ static bool smc_pnet_apply_ib(struct smc_ib_device *ib_dev, u8 ib_port, ...@@ -256,11 +262,10 @@ static bool smc_pnet_apply_ib(struct smc_ib_device *ib_dev, u8 ib_port,
*/ */
static bool smc_pnet_apply_smcd(struct smcd_dev *smcd_dev, char *pnet_name) static bool smc_pnet_apply_smcd(struct smcd_dev *smcd_dev, char *pnet_name)
{ {
u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
bool applied = false; bool applied = false;
mutex_lock(&smcd_dev_list.mutex); mutex_lock(&smcd_dev_list.mutex);
if (smc_pnet_match(smcd_dev->pnetid, pnet_null)) { if (!smc_pnet_is_pnetid_set(smcd_dev->pnetid)) {
memcpy(smcd_dev->pnetid, pnet_name, SMC_MAX_PNETID_LEN); memcpy(smcd_dev->pnetid, pnet_name, SMC_MAX_PNETID_LEN);
smcd_dev->pnetid_by_user = true; smcd_dev->pnetid_by_user = true;
applied = true; applied = true;
...@@ -708,10 +713,115 @@ static struct genl_family smc_pnet_nl_family __ro_after_init = { ...@@ -708,10 +713,115 @@ static struct genl_family smc_pnet_nl_family __ro_after_init = {
.n_ops = ARRAY_SIZE(smc_pnet_ops) .n_ops = ARRAY_SIZE(smc_pnet_ops)
}; };
bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid)
{
struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnetids_ndev_entry *pe;
bool rc = false;
read_lock(&sn->pnetids_ndev.lock);
list_for_each_entry(pe, &sn->pnetids_ndev.list, list) {
if (smc_pnet_match(pnetid, pe->pnetid)) {
rc = true;
goto unlock;
}
}
unlock:
read_unlock(&sn->pnetids_ndev.lock);
return rc;
}
static int smc_pnet_add_pnetid(struct net *net, u8 *pnetid)
{
struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnetids_ndev_entry *pe, *pi;
pe = kzalloc(sizeof(*pe), GFP_KERNEL);
if (!pe)
return -ENOMEM;
write_lock(&sn->pnetids_ndev.lock);
list_for_each_entry(pi, &sn->pnetids_ndev.list, list) {
if (smc_pnet_match(pnetid, pe->pnetid)) {
refcount_inc(&pi->refcnt);
kfree(pe);
goto unlock;
}
}
refcount_set(&pe->refcnt, 1);
memcpy(pe->pnetid, pnetid, SMC_MAX_PNETID_LEN);
list_add_tail(&pe->list, &sn->pnetids_ndev.list);
unlock:
write_unlock(&sn->pnetids_ndev.lock);
return 0;
}
static void smc_pnet_remove_pnetid(struct net *net, u8 *pnetid)
{
struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnetids_ndev_entry *pe, *pe2;
write_lock(&sn->pnetids_ndev.lock);
list_for_each_entry_safe(pe, pe2, &sn->pnetids_ndev.list, list) {
if (smc_pnet_match(pnetid, pe->pnetid)) {
if (refcount_dec_and_test(&pe->refcnt)) {
list_del(&pe->list);
kfree(pe);
}
break;
}
}
write_unlock(&sn->pnetids_ndev.lock);
}
static void smc_pnet_add_base_pnetid(struct net *net, struct net_device *dev,
u8 *ndev_pnetid)
{
struct net_device *base_dev;
base_dev = __pnet_find_base_ndev(dev);
if (base_dev->flags & IFF_UP &&
!smc_pnetid_by_dev_port(base_dev->dev.parent, base_dev->dev_port,
ndev_pnetid)) {
/* add to PNETIDs list */
smc_pnet_add_pnetid(net, ndev_pnetid);
}
}
/* create initial list of netdevice pnetids */
static void smc_pnet_create_pnetids_list(struct net *net)
{
u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
struct net_device *dev;
rtnl_lock();
for_each_netdev(net, dev)
smc_pnet_add_base_pnetid(net, dev, ndev_pnetid);
rtnl_unlock();
}
/* clean up list of netdevice pnetids */
static void smc_pnet_destroy_pnetids_list(struct net *net)
{
struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnetids_ndev_entry *pe, *temp_pe;
write_lock(&sn->pnetids_ndev.lock);
list_for_each_entry_safe(pe, temp_pe, &sn->pnetids_ndev.list, list) {
list_del(&pe->list);
kfree(pe);
}
write_unlock(&sn->pnetids_ndev.lock);
}
static int smc_pnet_netdev_event(struct notifier_block *this, static int smc_pnet_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(event_dev);
u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
switch (event) { switch (event) {
case NETDEV_REBOOT: case NETDEV_REBOOT:
...@@ -721,6 +831,17 @@ static int smc_pnet_netdev_event(struct notifier_block *this, ...@@ -721,6 +831,17 @@ static int smc_pnet_netdev_event(struct notifier_block *this,
case NETDEV_REGISTER: case NETDEV_REGISTER:
smc_pnet_add_by_ndev(event_dev); smc_pnet_add_by_ndev(event_dev);
return NOTIFY_OK; return NOTIFY_OK;
case NETDEV_UP:
smc_pnet_add_base_pnetid(net, event_dev, ndev_pnetid);
return NOTIFY_OK;
case NETDEV_DOWN:
event_dev = __pnet_find_base_ndev(event_dev);
if (!smc_pnetid_by_dev_port(event_dev->dev.parent,
event_dev->dev_port, ndev_pnetid)) {
/* remove from PNETIDs list */
smc_pnet_remove_pnetid(net, ndev_pnetid);
}
return NOTIFY_OK;
default: default:
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -735,9 +856,14 @@ int smc_pnet_net_init(struct net *net) ...@@ -735,9 +856,14 @@ int smc_pnet_net_init(struct net *net)
{ {
struct smc_net *sn = net_generic(net, smc_net_id); struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnettable *pnettable = &sn->pnettable; struct smc_pnettable *pnettable = &sn->pnettable;
struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev;
INIT_LIST_HEAD(&pnettable->pnetlist); INIT_LIST_HEAD(&pnettable->pnetlist);
rwlock_init(&pnettable->lock); rwlock_init(&pnettable->lock);
INIT_LIST_HEAD(&pnetids_ndev->list);
rwlock_init(&pnetids_ndev->lock);
smc_pnet_create_pnetids_list(net);
return 0; return 0;
} }
...@@ -752,6 +878,7 @@ int __init smc_pnet_init(void) ...@@ -752,6 +878,7 @@ int __init smc_pnet_init(void)
rc = register_netdevice_notifier(&smc_netdev_notifier); rc = register_netdevice_notifier(&smc_netdev_notifier);
if (rc) if (rc)
genl_unregister_family(&smc_pnet_nl_family); genl_unregister_family(&smc_pnet_nl_family);
return rc; return rc;
} }
...@@ -760,6 +887,7 @@ void smc_pnet_net_exit(struct net *net) ...@@ -760,6 +887,7 @@ void smc_pnet_net_exit(struct net *net)
{ {
/* flush pnet table */ /* flush pnet table */
smc_pnet_remove_by_pnetid(net, NULL); smc_pnet_remove_by_pnetid(net, NULL);
smc_pnet_destroy_pnetids_list(net);
} }
void smc_pnet_exit(void) void smc_pnet_exit(void)
...@@ -768,16 +896,11 @@ void smc_pnet_exit(void) ...@@ -768,16 +896,11 @@ void smc_pnet_exit(void)
genl_unregister_family(&smc_pnet_nl_family); genl_unregister_family(&smc_pnet_nl_family);
} }
/* Determine one base device for stacked net devices. static struct net_device *__pnet_find_base_ndev(struct net_device *ndev)
* If the lower device level contains more than one devices
* (for instance with bonding slaves), just the first device
* is used to reach a base device.
*/
static struct net_device *pnet_find_base_ndev(struct net_device *ndev)
{ {
int i, nest_lvl; int i, nest_lvl;
rtnl_lock(); ASSERT_RTNL();
nest_lvl = ndev->lower_level; nest_lvl = ndev->lower_level;
for (i = 0; i < nest_lvl; i++) { for (i = 0; i < nest_lvl; i++) {
struct list_head *lower = &ndev->adj_list.lower; struct list_head *lower = &ndev->adj_list.lower;
...@@ -787,6 +910,18 @@ static struct net_device *pnet_find_base_ndev(struct net_device *ndev) ...@@ -787,6 +910,18 @@ static struct net_device *pnet_find_base_ndev(struct net_device *ndev)
lower = lower->next; lower = lower->next;
ndev = netdev_lower_get_next(ndev, &lower); ndev = netdev_lower_get_next(ndev, &lower);
} }
return ndev;
}
/* Determine one base device for stacked net devices.
* If the lower device level contains more than one devices
* (for instance with bonding slaves), just the first device
* is used to reach a base device.
*/
static struct net_device *pnet_find_base_ndev(struct net_device *ndev)
{
rtnl_lock();
ndev = __pnet_find_base_ndev(ndev);
rtnl_unlock(); rtnl_unlock();
return ndev; return ndev;
} }
...@@ -929,10 +1064,10 @@ static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev, ...@@ -929,10 +1064,10 @@ static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev,
list_for_each_entry(ismdev, &smcd_dev_list.list, list) { list_for_each_entry(ismdev, &smcd_dev_list.list, list) {
if (smc_pnet_match(ismdev->pnetid, ndev_pnetid) && if (smc_pnet_match(ismdev->pnetid, ndev_pnetid) &&
!ismdev->going_away && !ismdev->going_away &&
(!ini->ism_peer_gid || (!ini->ism_peer_gid[0] ||
!smc_ism_cantalk(ini->ism_peer_gid, ini->vlan_id, !smc_ism_cantalk(ini->ism_peer_gid[0], ini->vlan_id,
ismdev))) { ismdev))) {
ini->ism_dev = ismdev; ini->ism_dev[0] = ismdev;
break; break;
} }
} }
...@@ -966,7 +1101,7 @@ void smc_pnet_find_ism_resource(struct sock *sk, struct smc_init_info *ini) ...@@ -966,7 +1101,7 @@ void smc_pnet_find_ism_resource(struct sock *sk, struct smc_init_info *ini)
{ {
struct dst_entry *dst = sk_dst_get(sk); struct dst_entry *dst = sk_dst_get(sk);
ini->ism_dev = NULL; ini->ism_dev[0] = NULL;
if (!dst) if (!dst)
goto out; goto out;
if (!dst->dev) if (!dst->dev)
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#ifndef _SMC_PNET_H #ifndef _SMC_PNET_H
#define _SMC_PNET_H #define _SMC_PNET_H
#include <net/smc.h>
#if IS_ENABLED(CONFIG_HAVE_PNETID) #if IS_ENABLED(CONFIG_HAVE_PNETID)
#include <asm/pnet.h> #include <asm/pnet.h>
#endif #endif
...@@ -31,6 +33,17 @@ struct smc_pnettable { ...@@ -31,6 +33,17 @@ struct smc_pnettable {
struct list_head pnetlist; struct list_head pnetlist;
}; };
struct smc_pnetids_ndev { /* list of pnetids for net devices in UP state*/
struct list_head list;
rwlock_t lock;
};
struct smc_pnetids_ndev_entry {
struct list_head list;
u8 pnetid[SMC_MAX_PNETID_LEN];
refcount_t refcnt;
};
static inline int smc_pnetid_by_dev_port(struct device *dev, static inline int smc_pnetid_by_dev_port(struct device *dev,
unsigned short port, u8 *pnetid) unsigned short port, u8 *pnetid)
{ {
...@@ -52,4 +65,6 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcd); ...@@ -52,4 +65,6 @@ int smc_pnetid_by_table_smcd(struct smcd_dev *smcd);
void smc_pnet_find_alt_roce(struct smc_link_group *lgr, void smc_pnet_find_alt_roce(struct smc_link_group *lgr,
struct smc_init_info *ini, struct smc_init_info *ini,
struct smc_ib_device *known_dev); struct smc_ib_device *known_dev);
bool smc_pnet_is_ndev_pnetid(struct net *net, u8 *pnetid);
bool smc_pnet_is_pnetid_set(u8 *pnetid);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment