sockets - Zerocopy TCP 消息从内核模块发送错误 EFAULT
问题描述
我正在开发一个内核模块,该模块通过 DMA 从 FPGA 接收数据并将其存储在分配有dma_alloc_attrs(dev, size, &data->dma_addr, GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS)
. 每当环形缓冲区中有新数据可用时,completion
就会触发 a 。
在同一个内核模块中,我正在运行一个 TCP 服务器,并且在内核模块的生命周期中,只有一个客户端(在另一台机器上)连接到服务器(并保持连接)。completion
每当触发时,内核模块中的一个单独线程会将在环形缓冲区中接收到的数据发送到连接的客户端。在内核空间中拥有一个 tcp 服务器的想法是在数据应该发送到客户端时摆脱内核空间和用户空间的不必要的上下文切换,从而提高性能。到目前为止一切正常,但性能不如预期(在 TCP 端)。
在研究了如何提高性能之后,我找到了这个ZEROCOPY
选项。
我将服务器套接字的设置更改为设置SO_ZEROCOPY
标志:kernel_setsockopt(socket, SOL_SOCKET, SO_ZEROCOPY, (char *)&one, sizeof(one))
并将发送到客户端的实现更改为:
static DEFINE_MUTEX(tcp_send_mtx);
static int send(struct socket *sock, const char *buf,
const size_t length, unsigned long flags)
{
struct msghdr msg;
struct kvec vec;
int len, written = 0;
int left = length;
if(sock == NULL)
{
printk(KERN_ERR MODULE_NAME ": tcp server send socket is NULL\n");
return -EFAULT;
}
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = MSG_ZEROCOPY;
repeat_send:
vec.iov_len = left;
vec.iov_base = (char *)buf + written;
len = kernel_sendmsg(sock, &msg, &vec, left, left);
if((len == -ERESTARTSYS) || (!(flags & MSG_DONTWAIT) && (len == -EAGAIN)))
goto repeat_send;
if(len > 0)
{
written += len;
left -= len;
if(left)
goto repeat_send;
}
return written?written:len;
}
注意msg.msg_flags = MSG_ZEROCOPY;
发送函数中的分配。
现在当我尝试使用它时,我只是通过添加标志来获取EFAULT(-14)
错误代码。kernel_sendmsg
MSG_ZEROCOPY
更新:
我现在明白该ZEROCOPY
标志在内核空间中被错误地使用,因为它旨在删除用户空间和内核空间之间的额外副本。
我最初的问题仍然存在。TCP 传输仍然很慢,当 DMA 传输速度超过 120mb/s 时,环形缓冲区溢出。将消息转发给客户端的线程无法以超过 120mb/s 的速度发送 8kb 消息。
有人知道这里有什么问题吗?也许这个想法首先是错误的
解决方案
推荐阅读
- sql - Amazon Redshift - 横向列别名参考
- php - {{ $data or 'Default' }} 在 laravel 刀片中不起作用
- javascript - 调整表格大小时如何通过在字符串中间放置省略号来截断字符串
- anaconda - 递归“Anaconda-Navigator”想访问您在 mac OS Catalina 上的文件夹
- flutter - 按下按钮时颤动更改文本
- android - 如何让我的班级接收不同的数据模式 kotlin android?
- assembly - 在装配中更改中断时...?
- json - 创建角度应用程序时出错
- python - 将分箱值转换为分类值
- pandas - 如何根据B列pandas获取C列对应值?