往期精彩推荐
其他
系列解读SMC-R:透明无感提升云上 TCP 应用网络性能(一)
The following article is from OpenAnolis龙蜥 Author 高性能网络 SIG
文/龙蜥社区高性能网络SIG
引言
一、从 RDMA 谈起
1.1 为什么需要 RDMA ?
1.2 RDMA 的优势
二、回到 SMC-R
2.1 透明替换 TCP
SMC-R is an open sockets over RDMA protocol that provides transparent exploitation of RDMA (for TCP based applications) while preserving key functions and qualities of service from the TCP/IP ecosystem that enterprise level servers/network depend on!
摘自:
https://www.openfabrics.org/images/eventpresos/workshops2014/IBUG/presos/Thursday/PDF/05_SMC-R_Update.pdf
/* must look like tcp */
static const struct proto_ops smc_sock_ops = {
.family = PF_SMC,
.owner = THIS_MODULE,
.release = smc_release,
.bind = smc_bind,
.connect = smc_connect,
.socketpair = sock_no_socketpair,
.accept = smc_accept,
.getname = smc_getname,
.poll = smc_poll,
.ioctl = smc_ioctl,
.listen = smc_listen,
.shutdown = smc_shutdown,
.setsockopt = smc_setsockopt,
.getsockopt = smc_getsockopt,
.sendmsg = smc_sendmsg,
.recvmsg = smc_recvmsg,
.mmap = sock_no_mmap,
.sendpage = smc_sendpage,
.splice_read = smc_splice_read,
};
使用 LD_PRELOAD 实现协议栈透明替换。在运行 TCP 应用程序时预加载一个动态库。在动态库中实现自定义 socket() 函数,将 TCP 应用程序创建的 AF_INET 类型 socket 转换为 AF_SMC 类型的 socket,再调用标准 socket 创建流程,从而将 TCP 应用流量引入 SMC-R 协议栈。
int socket(int domain, int type, int protocol)
{
int rc;
if (!dl_handle)
initialize();
/* check if socket is eligible for AF_SMC */
if ((domain == AF_INET || domain == AF_INET6) &&
// see kernel code, include/linux/net.h, SOCK_TYPE_MASK
(type & 0xf) == SOCK_STREAM &&
(protocol == IPPROTO_IP || protocol == IPPROTO_TCP)) {
dbg_msg(stderr, "libsmc-preload: map sock to AF_SMC\n");
if (domain == AF_INET)
protocol = SMCPROTO_SMC;
else /* AF_INET6 */
protocol = SMCPROTO_SMC6;
domain = AF_SMC;
}
rc = (*orig_socket)(domain, type, protocol);
return rc;
}
通过 ULP + eBPF 实现协议栈透明替换。SMC-R 支持 TCP ULP 是龙蜥社区高性能网络 SIG 贡献到上游 Linux 社区的新特性。用户可以通过 setsockopt() 指定新创建的 TCP 类型 socket 转换为 SMC 类型 socket。同时,为避免应用程序改造,用户可以通过 eBPF 在合适的 hook 点 (如 BPF_CGROUP_INET_SOCK_CREATE、BPF_CGROUP_INET4_BIND、BPF_CGROUP_INET6_BIND 等) 注入 setsockopt(),实现透明替换。这种方式更适合在容器场景下可以依据自定义规则,批量的完成协议转换。
static int smc_ulp_init(struct sock *sk)
{
struct socket *tcp = sk->sk_socket;
struct net *net = sock_net(sk);
struct socket *smcsock;
int protocol, ret;
/* only TCP can be replaced */
if (tcp->type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP ||
(sk->sk_family != AF_INET && sk->sk_family != AF_INET6))
return -ESOCKTNOSUPPORT;
/* don't handle wq now */
if (tcp->state != SS_UNCONNECTED || !tcp->file || tcp->wq.fasync_list)
return -ENOTCONN;
if (sk->sk_family == AF_INET)
protocol = SMCPROTO_SMC;
else
protocol = SMCPROTO_SMC6;
smcsock = sock_alloc();
if (!smcsock)
return -ENFILE;
<...>
}
SEC("cgroup/connect4")
int replace_to_smc(struct bpf_sock_addr *addr)
{
int pid = bpf_get_current_pid_tgid() >> 32;
long ret;
/* use-defined rules/filters, such as pid, tcp src/dst address, etc...*/
if (pid != DESIRED_PID)
return 0;
<...>
ret = bpf_setsockopt(addr, SOL_TCP, TCP_ULP, "smc", sizeof("smc"));
if (ret) {
bpf_printk("replace TCP with SMC error: %ld\n", ret);
return 0;
}
return 0;
}