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 @@
#define ISM_DMB_WORD_OFFSET 1
#define ISM_DMB_BIT_OFFSET (ISM_DMB_WORD_OFFSET * 32)
#define ISM_NR_DMBS 1920
#define ISM_IDENT_MASK 0x00FFFF
#define ISM_REG_SBA 0x1
#define ISM_REG_IEQ 0x2
......@@ -206,6 +207,12 @@ struct ism_dev {
#define ISM_CREATE_REQ(dmb, idx, sf, 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,
unsigned long offset, unsigned long len)
{
......
......@@ -13,6 +13,8 @@
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/processor.h>
#include <net/smc.h>
#include <asm/debug.h>
......@@ -387,6 +389,42 @@ static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx,
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)
{
struct smcd_event *entry;
......@@ -443,6 +481,8 @@ static const struct smcd_ops ism_ops = {
.reset_vlan_required = ism_reset_vlan_required,
.signal_event = ism_signal_ieq,
.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)
......@@ -471,6 +511,10 @@ static int ism_dev_init(struct ism_dev *ism)
if (ret)
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);
if (ret)
goto unreg_ieq;
......@@ -550,6 +594,9 @@ static void ism_dev_exit(struct ism_dev *ism)
struct pci_dev *pdev = ism->pdev;
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_sba(ism);
free_irq(pci_irq_vector(pdev, 0), ism);
......
......@@ -37,6 +37,8 @@ struct smcd_dmb {
#define ISM_EVENT_GID 1
#define ISM_EVENT_SWR 2
#define ISM_RESERVED_VLANID 0x1FFF
#define ISM_ERROR 0xFFFF
struct smcd_event {
......@@ -63,6 +65,8 @@ struct smcd_ops {
int (*move_data)(struct smcd_dev *dev, u64 dmb_tok, unsigned int idx,
bool sf, unsigned int offset, void *data,
unsigned int size);
void (*get_system_eid)(struct smcd_dev *dev, u8 **eid);
u16 (*get_chid)(struct smcd_dev *dev);
};
struct smcd_dev {
......
This diff is collapsed.
......@@ -19,10 +19,19 @@
#include "smc_ib.h"
#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_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_proto6;
......@@ -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 */
#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 */
static inline void hton24(u8 *net, u32 host)
{
......
This diff is collapsed.
......@@ -54,19 +54,19 @@
#define SMC_CLC_DECL_ERR_RDYLNK 0x09990002 /* ib ready link 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 */
u8 eyecatcher[4]; /* eye catcher */
u8 type; /* proposal / accept / confirm / decline */
__be16 length;
#if defined(__BIG_ENDIAN_BITFIELD)
u8 version : 4,
flag : 1,
rsvd : 1,
path : 2;
typev2 : 2,
typev1 : 2;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
u8 path : 2,
rsvd : 1,
flag : 1,
u8 typev1 : 2,
typev2 : 2,
version : 4;
#endif
} __packed; /* format defined in RFC7609 */
......@@ -81,8 +81,6 @@ struct smc_clc_msg_local { /* header2 of clc messages */
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
* to peers and must conform to RFC7609, hence we need to use packed here.
*/
......@@ -91,6 +89,44 @@ struct smc_clc_ipv6_prefix {
u8 prefix_len;
} __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*/
__be32 outgoing_subnet; /* subnet 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*/
} __aligned(4);
struct smc_clc_msg_smcd { /* SMC-D GID information */
u64 gid; /* ISM GID of requestor */
u8 res[32];
struct smc_clc_smcd_gid_chid ism; /* ISM native GID+CHID of requestor */
__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 */
......@@ -109,11 +152,16 @@ struct smc_clc_msg_proposal { /* clc proposal message sent by Linux */
__be16 iparea_offset; /* offset to IP address information area */
} __aligned(4);
#define SMC_CLC_MAX_V6_PREFIX 8
struct smc_clc_msg_proposal_area {
struct smc_clc_msg_proposal pclc_base;
struct smc_clc_msg_smcd pclc_smcd;
struct smc_clc_msg_proposal_prefix pclc_prfx;
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;
};
......@@ -134,11 +182,9 @@ struct smcr_clc_msg_accept_confirm { /* SMCR accept/confirm */
__be64 rmb_dma_addr; /* RMB virtual address */
u8 reserved2;
u8 psn[3]; /* packet sequence number */
struct smc_clc_msg_trail smcr_trl;
/* eye catcher "SMCR" EBCDIC */
} __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 token; /* DMB token */
u8 dmbe_idx; /* DMBE index */
......@@ -150,26 +196,63 @@ struct smcd_clc_msg_accept_confirm { /* SMCD accept/confirm */
dmbe_size : 4;
#endif
u16 reserved4;
u32 linkid; /* Link identifier */
u32 reserved5[3];
struct smc_clc_msg_trail smcd_trl;
/* eye catcher "SMCD" EBCDIC */
__be32 linkid; /* Link identifier */
} __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_hdr hdr;
union {
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 */
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_hdr hdr;
u8 id_for_peer[SMC_SYSTEMID_LEN]; /* sender peer_id */
__be32 peer_diagnosis; /* diagnosis information */
u8 reserved2[4];
struct smc_clc_msg_trail trl; /* eye catcher "SMCR" EBCDIC */
#if defined(__BIG_ENDIAN_BITFIELD)
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);
/* 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)
((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 */
static inline struct smc_clc_msg_smcd *
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 (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 smc_init_info;
......@@ -197,10 +322,12 @@ int smc_clc_prfx_match(struct socket *clcsock,
struct smc_clc_msg_proposal_prefix *prop);
int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
u8 expected_type, unsigned long timeout);
int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info);
int smc_clc_send_proposal(struct smc_sock *smc, int smc_type,
struct smc_init_info *ini);
int smc_clc_send_confirm(struct smc_sock *smc);
int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact);
int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version);
int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini);
int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact,
u8 version);
int smc_clc_send_accept(struct smc_sock *smc, bool srv_first_contact,
u8 version);
void smc_clc_init(void) __init;
#endif
......@@ -375,7 +375,8 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
int i;
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;
goto out;
}
......@@ -412,13 +413,14 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
lgr->conns_all = RB_ROOT;
if (ini->is_smcd) {
/* SMC-D specific settings */
get_device(&ini->ism_dev->dev);
lgr->peer_gid = ini->ism_peer_gid;
lgr->smcd = ini->ism_dev;
lgr_list = &ini->ism_dev->lgr_list;
get_device(&ini->ism_dev[ini->ism_selected]->dev);
lgr->peer_gid = ini->ism_peer_gid[ini->ism_selected];
lgr->smcd = ini->ism_dev[ini->ism_selected];
lgr_list = &ini->ism_dev[ini->ism_selected]->lgr_list;
lgr_lock = &lgr->smcd->lgr_lock;
lgr->smc_version = ini->smcd_version;
lgr->peer_shutdown = 0;
atomic_inc(&ini->ism_dev->lgr_cnt);
atomic_inc(&ini->ism_dev[ini->ism_selected]->lgr_cnt);
} else {
/* SMC-R specific settings */
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)
kfree(lgr);
ism_put_vlan:
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:
if (rc < 0) {
if (rc == -ENOMEM)
......@@ -1288,8 +1290,10 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
spinlock_t *lgr_lock;
int rc = 0;
lgr_list = ini->is_smcd ? &ini->ism_dev->lgr_list : &smc_lgr_list.list;
lgr_lock = ini->is_smcd ? &ini->ism_dev->lgr_lock : &smc_lgr_list.lock;
lgr_list = ini->is_smcd ? &ini->ism_dev[ini->ism_selected]->lgr_list :
&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;
role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
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)
list_for_each_entry(lgr, lgr_list, list) {
write_lock_bh(&lgr->conns_lock);
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)) &&
!lgr->sync_err &&
lgr->vlan_id == ini->vlan_id &&
......
......@@ -231,6 +231,11 @@ struct smc_link_group {
u8 freeing : 1; /* lgr is being freed */
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 {
struct { /* SMC-R */
enum smc_lgr_role role;
......@@ -291,6 +296,8 @@ struct smc_clc_msg_local;
struct smc_init_info {
u8 is_smcd;
u8 smc_type_v1;
u8 smc_type_v2;
u8 first_contact_peer;
u8 first_contact_local;
unsigned short vlan_id;
......@@ -301,8 +308,12 @@ struct smc_init_info {
u8 ib_port;
u32 ib_clcqpn;
/* SMC-D */
u64 ism_peer_gid;
struct smcd_dev *ism_dev;
u64 ism_peer_gid[SMC_MAX_ISM_DEVS + 1];
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.
......
......@@ -21,7 +21,9 @@ struct smcd_dev_list smcd_dev_list = {
.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)
{
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,
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. */
void smc_ism_set_conn(struct smc_connection *conn)
{
......@@ -319,7 +331,18 @@ EXPORT_SYMBOL_GPL(smcd_alloc_dev);
int smcd_register_dev(struct smcd_dev *smcd)
{
mutex_lock(&smcd_dev_list.mutex);
list_add_tail(&smcd->list, &smcd_dev_list.list);
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);
else
list_add(&smcd->list, &smcd_dev_list.list);
mutex_unlock(&smcd_dev_list.mutex);
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)
spin_unlock_irqrestore(&smcd->lock, flags);
}
EXPORT_SYMBOL_GPL(smcd_handle_irq);
void __init smc_ism_init(void)
{
smc_ism_v2_capable = false;
}
......@@ -19,7 +19,10 @@ struct smcd_dev_list { /* List of SMCD devices */
struct mutex mutex; /* Protects list of 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 list_head list;
......@@ -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,
void *data, size_t len);
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
......@@ -16,5 +16,6 @@ extern unsigned int smc_net_id;
/* per-network namespace private data */
struct smc_net {
struct smc_pnettable pnettable;
struct smc_pnetids_ndev pnetids_ndev;
};
#endif
......@@ -29,8 +29,7 @@
#include "smc_ism.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 const struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = {
......@@ -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 */
static bool smc_pnet_match(u8 *pnetid1, u8 *pnetid2)
{
int i;
for (i = 0; i < SMC_MAX_PNETID_LEN; i++) {
if ((pnetid1[i] == 0 || pnetid1[i] == SMC_ASCII_BLANK) &&
(pnetid2[i] == 0 || pnetid2[i] == SMC_ASCII_BLANK))
if ((pnetid1[i] == 0 || pnetid1[i] == _S) &&
(pnetid2[i] == 0 || pnetid2[i] == _S))
break;
if (pnetid1[i] != pnetid2[i])
return false;
......@@ -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,
char *pnet_name)
{
u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
bool applied = false;
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,
SMC_MAX_PNETID_LEN);
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,
*/
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;
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);
smcd_dev->pnetid_by_user = true;
applied = true;
......@@ -708,10 +713,115 @@ static struct genl_family smc_pnet_nl_family __ro_after_init = {
.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,
unsigned long event, void *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) {
case NETDEV_REBOOT:
......@@ -721,6 +831,17 @@ static int smc_pnet_netdev_event(struct notifier_block *this,
case NETDEV_REGISTER:
smc_pnet_add_by_ndev(event_dev);
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:
return NOTIFY_DONE;
}
......@@ -735,9 +856,14 @@ int smc_pnet_net_init(struct net *net)
{
struct smc_net *sn = net_generic(net, smc_net_id);
struct smc_pnettable *pnettable = &sn->pnettable;
struct smc_pnetids_ndev *pnetids_ndev = &sn->pnetids_ndev;
INIT_LIST_HEAD(&pnettable->pnetlist);
rwlock_init(&pnettable->lock);
INIT_LIST_HEAD(&pnetids_ndev->list);
rwlock_init(&pnetids_ndev->lock);
smc_pnet_create_pnetids_list(net);
return 0;
}
......@@ -752,6 +878,7 @@ int __init smc_pnet_init(void)
rc = register_netdevice_notifier(&smc_netdev_notifier);
if (rc)
genl_unregister_family(&smc_pnet_nl_family);
return rc;
}
......@@ -760,6 +887,7 @@ void smc_pnet_net_exit(struct net *net)
{
/* flush pnet table */
smc_pnet_remove_by_pnetid(net, NULL);
smc_pnet_destroy_pnetids_list(net);
}
void smc_pnet_exit(void)
......@@ -768,16 +896,11 @@ void smc_pnet_exit(void)
genl_unregister_family(&smc_pnet_nl_family);
}
/* 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)
static struct net_device *__pnet_find_base_ndev(struct net_device *ndev)
{
int i, nest_lvl;
rtnl_lock();
ASSERT_RTNL();
nest_lvl = ndev->lower_level;
for (i = 0; i < nest_lvl; i++) {
struct list_head *lower = &ndev->adj_list.lower;
......@@ -787,6 +910,18 @@ static struct net_device *pnet_find_base_ndev(struct net_device *ndev)
lower = lower->next;
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();
return 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) {
if (smc_pnet_match(ismdev->pnetid, ndev_pnetid) &&
!ismdev->going_away &&
(!ini->ism_peer_gid ||
!smc_ism_cantalk(ini->ism_peer_gid, ini->vlan_id,
(!ini->ism_peer_gid[0] ||
!smc_ism_cantalk(ini->ism_peer_gid[0], ini->vlan_id,
ismdev))) {
ini->ism_dev = ismdev;
ini->ism_dev[0] = ismdev;
break;
}
}
......@@ -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);
ini->ism_dev = NULL;
ini->ism_dev[0] = NULL;
if (!dst)
goto out;
if (!dst->dev)
......
......@@ -12,6 +12,8 @@
#ifndef _SMC_PNET_H
#define _SMC_PNET_H
#include <net/smc.h>
#if IS_ENABLED(CONFIG_HAVE_PNETID)
#include <asm/pnet.h>
#endif
......@@ -31,6 +33,17 @@ struct smc_pnettable {
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,
unsigned short port, u8 *pnetid)
{
......@@ -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,
struct smc_init_info *ini,
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
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