Zephyr网络传输Offloading--添加offload socket

Creative Commons
本作品采用知识共享署名

本文说明如何将第三方的socket以offload的方式添加到zephyr内。

Zephyr网络传输Offloading–概览一文中提到了Zephyr网络传输offloading支持socket offloading API, 本文就如何将第三方socket以offloading的方式添加到zephyr中进行说明。
本文只说明如何添加,不说明第三方socket的具体实现形式,后文中以vender_ 开头的函数均为第三方socket需要porting的函数。

1.配置

配置CONFIG_NET_SOCKETS_OFFLOAD=y,编译时会启用socket offload

2. 注册dns查询函数

按照下列函数指针的模式使用第三方socket API实现dns查询函数vender_getaddrinfo/vender_freeaddrinfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct socket_dns_offload {
int (*getaddrinfo)(const char *node, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res);
void (*freeaddrinfo)(struct zsock_addrinfo *res);
};

static int vender_getaddrinfo(const char *node, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res)
{
...
}

static voidvender_freeaddrinfo(struct zsock_addrinfo *res)
{
...
}

用实现的函数初始化一个socket_dns_offload结构体变量, 并注册到socket_offload中

1
2
3
4
5
6
const struct socket_dns_offload vender_dns_ops = {
.getaddrinfo = vender_getaddrinfo,
.freeaddrinfo = vender_getaddrinfo,
};

socket_offload_dns_register(&vender_dns_ops);

3. 实现socket API

按照struct socket_op_vtable的定义使用第三方socket API实现zephyr的socket抽象,总计要实现read/write/close/ioctrl/bind/connect/listen/accept/sendto/recvfrom/getsockopt/setsockopt/sendmsg/getsockname总计14个,这里以列出read和bind示意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct socket_op_vtable {
struct fd_op_vtable fd_vtable;
int (*bind)(void *obj, const struct sockaddr *addr, socklen_t addrlen);
int (*connect)(void *obj, const struct sockaddr *addr,
socklen_t addrlen);
int (*listen)(void *obj, int backlog);
int (*accept)(void *obj, struct sockaddr *addr, socklen_t *addrlen);
ssize_t (*sendto)(void *obj, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t (*recvfrom)(void *obj, void *buf, size_t max_len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
int (*getsockopt)(void *obj, int level, int optname,
void *optval, socklen_t *optlen);
int (*setsockopt)(void *obj, int level, int optname,
const void *optval, socklen_t optlen);
ssize_t (*sendmsg)(void *obj, const struct msghdr *msg, int flags);
int (*getsockname)(void *obj, struct sockaddr *addr,
socklen_t *addrlen);
};

static ssize_t vender_read(void *obj, void *buffer, size_t count)
{
...
}

static int vender_bind(void *obj, const struct sockaddr *addr,
socklen_t addrlen)
{
...
}

用实现的函数初始化一个struct socket_op_vtable结构体变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static const struct socket_op_vtable vender_socket_fd_op_vtable = {
.fd_vtable = {
.read = vender_read,
.write = vender_write,
.close = vender_close,
.ioctl = vender_ioctl,
},
.bind = vender_bind,
.connect = vender_connect,
.listen = vender_listen,
.accept = vender_socket_accept,
.sendto = vender_sendto,
.sendmsg = vender_sendmsg,
.recvfrom = vender_recvfrom,
.getsockopt = vender_getsockopt,
.setsockopt = vender_setsockopt,
};

4.实现注册函数并注册

按照下列形式实现is_supported和handler,

1
2
3
4
5
struct net_socket_register {
int family;
bool (*is_supported)(int family, int type, int proto);
int (*handler)(int family, int type, int proto);
};

is_supported用于判断socket是否支持该协议族(family),socket类型(type), 和协议(proto), 例如我的第三方offload socket全部都支持就直接返回true

1
2
3
4
5
static bool vender_is_supported(int family, int type, int proto)
{
/* TODO offloading always enabled for now. */
return true;
}

handler是用于创建socket的函数,该函数中负责将vender_socket_fd_op_vtable注册到fdtable内,实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static int vender_socket_create(int family, int type, int proto)
{
//预定fd
int fd = z_reserve_fd();
int sock;

if (fd < 0) {
return -1;
}

//这里是实际的vender socket操作
sock = vender_socket(family, type, proto);
if (sock < 0) {
z_free_fd(fd);
return -1;
}

//将vender_socket_fd_op_vtable注册给fd,之后zephyr的socket实现可以通过fd的vtable来调用vender_socket_fd_op_vtable中的socket API
z_finalize_fd(fd, SD_TO_OBJ(sock),
(const struct fd_op_vtable *)
&vender_socket_fd_op_vtable);

return fd;
}

使用下面宏进行注册,Zephyr的socket就可以调用到vender的socket实现了。

1
NET_SOCKET_REGISTER(vender, AF_UNSPEC, vender_is_supported, vender_socket_create);

以上4步做完后,zephyr中使用socket编程,最终都会调用到vender_ 这一组由第三方socket porting的API。