首页 > 解决方案 > 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_sendmsgMSG_ZEROCOPY

更新:

我现在明白该ZEROCOPY标志在内核空间中被错误地使用,因为它旨在删除用户空间和内核空间之间的额外副本。

我最初的问题仍然存在。TCP 传输仍然很慢,当 DMA 传输速度超过 120mb/s 时,环形缓冲区溢出。将消息转发给客户端的线程无法以超过 120mb/s 的速度发送 8kb 消息。

有人知道这里有什么问题吗?也许这个想法首先是错误的

标签: socketstcplinux-kernelkernel-module

解决方案


推荐阅读