2017-01-09 22:55:26 +07:00
|
|
|
/*
|
|
|
|
* Shared Memory Communications over RDMA (SMC-R) and RoCE
|
|
|
|
*
|
|
|
|
* Monitoring SMC transport protocol sockets
|
|
|
|
*
|
|
|
|
* Copyright IBM Corp. 2016
|
|
|
|
*
|
|
|
|
* Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/sock_diag.h>
|
|
|
|
#include <linux/inet_diag.h>
|
|
|
|
#include <linux/smc_diag.h>
|
|
|
|
#include <net/netlink.h>
|
|
|
|
#include <net/smc.h>
|
|
|
|
|
|
|
|
#include "smc.h"
|
|
|
|
#include "smc_core.h"
|
|
|
|
|
|
|
|
static void smc_gid_be16_convert(__u8 *buf, u8 *gid_raw)
|
|
|
|
{
|
|
|
|
sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[0]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[1]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[2]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[3]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[4]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[5]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[6]),
|
|
|
|
be16_to_cpu(((__be16 *)gid_raw)[7]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void smc_diag_msg_common_fill(struct smc_diag_msg *r, struct sock *sk)
|
|
|
|
{
|
|
|
|
struct smc_sock *smc = smc_sk(sk);
|
|
|
|
|
|
|
|
if (!smc->clcsock)
|
|
|
|
return;
|
|
|
|
r->id.idiag_sport = htons(smc->clcsock->sk->sk_num);
|
|
|
|
r->id.idiag_dport = smc->clcsock->sk->sk_dport;
|
|
|
|
r->id.idiag_if = smc->clcsock->sk->sk_bound_dev_if;
|
|
|
|
sock_diag_save_cookie(sk, r->id.idiag_cookie);
|
2018-05-02 21:56:45 +07:00
|
|
|
if (sk->sk_protocol == SMCPROTO_SMC) {
|
|
|
|
r->diag_family = PF_INET;
|
|
|
|
memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src));
|
|
|
|
memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst));
|
|
|
|
r->id.idiag_src[0] = smc->clcsock->sk->sk_rcv_saddr;
|
|
|
|
r->id.idiag_dst[0] = smc->clcsock->sk->sk_daddr;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
} else if (sk->sk_protocol == SMCPROTO_SMC6) {
|
|
|
|
r->diag_family = PF_INET6;
|
|
|
|
memcpy(&r->id.idiag_src, &smc->clcsock->sk->sk_v6_rcv_saddr,
|
|
|
|
sizeof(smc->clcsock->sk->sk_v6_rcv_saddr));
|
|
|
|
memcpy(&r->id.idiag_dst, &smc->clcsock->sk->sk_v6_daddr,
|
|
|
|
sizeof(smc->clcsock->sk->sk_v6_daddr));
|
|
|
|
#endif
|
|
|
|
}
|
2017-01-09 22:55:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int smc_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
|
|
|
|
struct smc_diag_msg *r,
|
|
|
|
struct user_namespace *user_ns)
|
|
|
|
{
|
|
|
|
if (nla_put_u8(skb, SMC_DIAG_SHUTDOWN, sk->sk_shutdown))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
r->diag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
|
|
|
|
r->diag_inode = sock_i_ino(sk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb,
|
|
|
|
const struct smc_diag_req *req,
|
|
|
|
struct nlattr *bc)
|
|
|
|
{
|
|
|
|
struct smc_sock *smc = smc_sk(sk);
|
|
|
|
struct user_namespace *user_ns;
|
|
|
|
struct smc_diag_msg *r;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
|
|
|
|
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
|
|
|
cb->nlh->nlmsg_type, sizeof(*r), NLM_F_MULTI);
|
|
|
|
if (!nlh)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
r = nlmsg_data(nlh);
|
|
|
|
smc_diag_msg_common_fill(r, sk);
|
|
|
|
r->diag_state = sk->sk_state;
|
2018-07-23 18:53:08 +07:00
|
|
|
if (smc->use_fallback)
|
|
|
|
r->diag_mode = SMC_DIAG_MODE_FALLBACK_TCP;
|
|
|
|
else if (smc->conn.lgr && smc->conn.lgr->is_smcd)
|
|
|
|
r->diag_mode = SMC_DIAG_MODE_SMCD;
|
|
|
|
else
|
|
|
|
r->diag_mode = SMC_DIAG_MODE_SMCR;
|
2017-01-09 22:55:26 +07:00
|
|
|
user_ns = sk_user_ns(NETLINK_CB(cb->skb).sk);
|
|
|
|
if (smc_diag_msg_attrs_fill(sk, skb, r, user_ns))
|
|
|
|
goto errout;
|
|
|
|
|
2018-01-25 17:15:36 +07:00
|
|
|
if ((req->diag_ext & (1 << (SMC_DIAG_CONNINFO - 1))) &&
|
|
|
|
smc->conn.alert_token_local) {
|
2017-01-09 22:55:26 +07:00
|
|
|
struct smc_connection *conn = &smc->conn;
|
|
|
|
struct smc_diag_conninfo cinfo = {
|
|
|
|
.token = conn->alert_token_local,
|
2018-05-18 14:34:10 +07:00
|
|
|
.sndbuf_size = conn->sndbuf_desc ?
|
|
|
|
conn->sndbuf_desc->len : 0,
|
|
|
|
.rmbe_size = conn->rmb_desc ? conn->rmb_desc->len : 0,
|
2017-01-09 22:55:26 +07:00
|
|
|
.peer_rmbe_size = conn->peer_rmbe_size,
|
|
|
|
|
|
|
|
.rx_prod.wrap = conn->local_rx_ctrl.prod.wrap,
|
|
|
|
.rx_prod.count = conn->local_rx_ctrl.prod.count,
|
|
|
|
.rx_cons.wrap = conn->local_rx_ctrl.cons.wrap,
|
|
|
|
.rx_cons.count = conn->local_rx_ctrl.cons.count,
|
|
|
|
|
|
|
|
.tx_prod.wrap = conn->local_tx_ctrl.prod.wrap,
|
|
|
|
.tx_prod.count = conn->local_tx_ctrl.prod.count,
|
|
|
|
.tx_cons.wrap = conn->local_tx_ctrl.cons.wrap,
|
|
|
|
.tx_cons.count = conn->local_tx_ctrl.cons.count,
|
|
|
|
|
|
|
|
.tx_prod_flags =
|
|
|
|
*(u8 *)&conn->local_tx_ctrl.prod_flags,
|
|
|
|
.tx_conn_state_flags =
|
|
|
|
*(u8 *)&conn->local_tx_ctrl.conn_state_flags,
|
|
|
|
.rx_prod_flags = *(u8 *)&conn->local_rx_ctrl.prod_flags,
|
|
|
|
.rx_conn_state_flags =
|
|
|
|
*(u8 *)&conn->local_rx_ctrl.conn_state_flags,
|
|
|
|
|
|
|
|
.tx_prep.wrap = conn->tx_curs_prep.wrap,
|
|
|
|
.tx_prep.count = conn->tx_curs_prep.count,
|
|
|
|
.tx_sent.wrap = conn->tx_curs_sent.wrap,
|
|
|
|
.tx_sent.count = conn->tx_curs_sent.count,
|
|
|
|
.tx_fin.wrap = conn->tx_curs_fin.wrap,
|
|
|
|
.tx_fin.count = conn->tx_curs_fin.count,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (nla_put(skb, SMC_DIAG_CONNINFO, sizeof(cinfo), &cinfo) < 0)
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
net/smc: add base infrastructure for SMC-D and ISM
SMC supports two variants: SMC-R and SMC-D. For data transport, SMC-R
uses RDMA devices, SMC-D uses so-called Internal Shared Memory (ISM)
devices. An ISM device only allows shared memory communication between
SMC instances on the same machine. For example, this allows virtual
machines on the same host to communicate via SMC without RDMA devices.
This patch adds the base infrastructure for SMC-D and ISM devices to
the existing SMC code. It contains the following:
* ISM driver interface:
This interface allows an ISM driver to register ISM devices in SMC. In
the process, the driver provides a set of device ops for each device.
SMC uses these ops to execute SMC specific operations on or transfer
data over the device.
* Core SMC-D link group, connection, and buffer support:
Link groups, SMC connections and SMC buffers (in smc_core) are
extended to support SMC-D.
* SMC type checks:
Some type checks are added to prevent using SMC-R specific code for
SMC-D and vice versa.
To actually use SMC-D, additional changes to pnetid, CLC, CDC, etc. are
required. These are added in follow-up patches.
Signed-off-by: Hans Wippel <hwippel@linux.ibm.com>
Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Suggested-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-06-29 00:05:07 +07:00
|
|
|
if (smc->conn.lgr && !smc->conn.lgr->is_smcd &&
|
|
|
|
(req->diag_ext & (1 << (SMC_DIAG_LGRINFO - 1))) &&
|
2018-01-25 17:15:36 +07:00
|
|
|
!list_empty(&smc->conn.lgr->list)) {
|
2017-01-09 22:55:26 +07:00
|
|
|
struct smc_diag_lgrinfo linfo = {
|
|
|
|
.role = smc->conn.lgr->role,
|
|
|
|
.lnk[0].ibport = smc->conn.lgr->lnk[0].ibport,
|
|
|
|
.lnk[0].link_id = smc->conn.lgr->lnk[0].link_id,
|
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(linfo.lnk[0].ibname,
|
|
|
|
smc->conn.lgr->lnk[0].smcibdev->ibdev->name,
|
|
|
|
sizeof(smc->conn.lgr->lnk[0].smcibdev->ibdev->name));
|
|
|
|
smc_gid_be16_convert(linfo.lnk[0].gid,
|
|
|
|
smc->conn.lgr->lnk[0].gid.raw);
|
|
|
|
smc_gid_be16_convert(linfo.lnk[0].peer_gid,
|
|
|
|
smc->conn.lgr->lnk[0].peer_gid);
|
|
|
|
|
|
|
|
if (nla_put(skb, SMC_DIAG_LGRINFO, sizeof(linfo), &linfo) < 0)
|
|
|
|
goto errout;
|
|
|
|
}
|
2018-06-29 00:05:12 +07:00
|
|
|
if (smc->conn.lgr && smc->conn.lgr->is_smcd &&
|
|
|
|
(req->diag_ext & (1 << (SMC_DIAG_DMBINFO - 1))) &&
|
|
|
|
!list_empty(&smc->conn.lgr->list)) {
|
|
|
|
struct smc_connection *conn = &smc->conn;
|
|
|
|
struct smcd_diag_dmbinfo dinfo = {
|
|
|
|
.linkid = *((u32 *)conn->lgr->id),
|
|
|
|
.peer_gid = conn->lgr->peer_gid,
|
|
|
|
.my_gid = conn->lgr->smcd->local_gid,
|
|
|
|
.token = conn->rmb_desc->token,
|
|
|
|
.peer_token = conn->peer_token
|
|
|
|
};
|
|
|
|
|
|
|
|
if (nla_put(skb, SMC_DIAG_DMBINFO, sizeof(dinfo), &dinfo) < 0)
|
|
|
|
goto errout;
|
|
|
|
}
|
2017-01-09 22:55:26 +07:00
|
|
|
|
|
|
|
nlmsg_end(skb, nlh);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errout:
|
|
|
|
nlmsg_cancel(skb, nlh);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
2018-05-02 21:56:45 +07:00
|
|
|
static int smc_diag_dump_proto(struct proto *prot, struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb)
|
2017-01-09 22:55:26 +07:00
|
|
|
{
|
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
|
struct nlattr *bc = NULL;
|
|
|
|
struct hlist_head *head;
|
|
|
|
struct sock *sk;
|
|
|
|
int rc = 0;
|
|
|
|
|
2018-05-02 21:56:45 +07:00
|
|
|
read_lock(&prot->h.smc_hash->lock);
|
|
|
|
head = &prot->h.smc_hash->ht;
|
2017-01-09 22:55:26 +07:00
|
|
|
if (hlist_empty(head))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
sk_for_each(sk, head) {
|
|
|
|
if (!net_eq(sock_net(sk), net))
|
|
|
|
continue;
|
|
|
|
rc = __smc_diag_dump(sk, skb, cb, nlmsg_data(cb->nlh), bc);
|
|
|
|
if (rc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2018-05-02 21:56:45 +07:00
|
|
|
read_unlock(&prot->h.smc_hash->lock);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int smc_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
rc = smc_diag_dump_proto(&smc_proto, skb, cb);
|
|
|
|
if (!rc)
|
|
|
|
rc = smc_diag_dump_proto(&smc_proto6, skb, cb);
|
2017-01-09 22:55:26 +07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int smc_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
|
|
|
|
{
|
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
|
|
|
|
|
if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
|
|
|
|
h->nlmsg_flags & NLM_F_DUMP) {
|
|
|
|
{
|
|
|
|
struct netlink_dump_control c = {
|
|
|
|
.dump = smc_diag_dump,
|
|
|
|
.min_dump_alloc = SKB_WITH_OVERHEAD(32768),
|
|
|
|
};
|
|
|
|
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sock_diag_handler smc_diag_handler = {
|
|
|
|
.family = AF_SMC,
|
|
|
|
.dump = smc_diag_handler_dump,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init smc_diag_init(void)
|
|
|
|
{
|
|
|
|
return sock_diag_register(&smc_diag_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit smc_diag_exit(void)
|
|
|
|
{
|
|
|
|
sock_diag_unregister(&smc_diag_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(smc_diag_init);
|
|
|
|
module_exit(smc_diag_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 43 /* AF_SMC */);
|