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

本文说明如何将第三方的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

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中

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示意

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结构体变量

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,

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

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内,实现如下

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实现了。

NET_SOCKET_REGISTER(vender, AF_UNSPEC, vender_is_supported, vender_socket_create);

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