我有一个内核模块和一个使用Netlink进行通信的相应用户空间模块 . 内核模块中使用以下代码将数据发送到用户空间:

int msglen = len - FRAME_PACKET_HEADER_SIZE;
struct sk_buff* skb = nlmsg_new(msglen, GFP_ATOMIC);
if (skb)
{
    struct nlmsghdr* nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, msglen, GFP_ATOMIC);
    nlh->nlmsg_flags = NLM_F_REQUEST;
    NETLINK_CB(skb).dst_group = 0;

    memcpy(nlmsg_data(nlh), &buf[FRAME_PACKET_HEADER_SIZE], msglen);
    status = nlmsg_unicast(mod_data->netlink_sock, skb, mod_data->netlink_pid);

}

在高数据活动期间(从内核发送到用户空间的Netlink消息), nlmsg_new 开始返回 NULL 并且无法分配 . 高数据活动与文件传输有关,文件传输以16k块的形式推送到用户空间 . 经过一些调试,我发现当 nlmsg_new 失败时,我可以成功分配一个比我实际需要分配的小一点的消息(所需大小为16336字节,分配为16000个工作) .

问题:

  • 我读过的文档建议为每个要发送的消息调用 nlmsg_new 是正确的 . 似乎没有办法重用 sk_buff 对象,因为它可能在队列中等待一段时间,并且 nlmsg_unicast 在实际发送消息时处理释放(因此不需要手动 nlmsg_free ) . 这绝对是这样吗?有没有办法可以重用 nlmsg_new 分配的缓冲区?

  • 我想知道是否有一堆ACK消息正在排队,这些消息在某处填满了一些缓冲区 . 我将 nlmsg_flags 设置为 NLM_F_REQUEST ,因此用户空间模块不应该发送ACK . 它是否正确?

  • 为什么分配失败的任何其他想法?

对于上下文,这是在具有256 MiB RAM的嵌入式ARM上运行 . 内核是3.14.28 .

用户空间模块通过调用 recvmsg 来不断地为接收队列服务,因此我认为接收缓冲区不会变满 . 我认为如果是这种情况,内核模块将成功分配一个缓冲区,但对 nlmsg_unicast 的调用将返回 -EAGAIN (这不会发生) .

编辑:我在 linux/netlink.h 看到一个有趣的注释:

/*
 *  skb should fit one page. This choice is good for headerless malloc.
 *  But we should limit to 8K so that userspace does not have to
 *  use enormous buffer sizes on recvmsg() calls just to avoid
 *  MSG_TRUNC when PAGE_SIZE is very large.
 */

所以我尝试减少消息大小,以便netlink消息和标头<8192字节但仍然发生相同的故障(稍后,正如预期的那样) .

编辑2:查看报告的可用内存后,看起来通过 nlmsg_new 分配的 sk_buff 对象永远不会被释放 . 当引用计数( sk_buffsk_buff )变为0时,它们似乎应该被释放 . 是否有任何理由Netlink持有缓冲区,等待永远不会到来的ACK?