如何将NAPI轮询函数卸载到工作队列

时间:2015-06-08 08:30:47

标签: c linux linux-kernel linux-device-driver embedded-linux

我正在使用linux 3.3,smsc911x的以太网驱动程序。我想将NAPI轮询功能移动到workqueue。

我的问题是:
1.如何将NAPI轮询函数参数传递给work_struct?
2.如何从work_struct获取NAPI轮询函数参数? (与上述Q.1相关)
3.如何将npackets值返回给原始NAPI轮询函数调用者?

以下是一些解释:

当前的NAPI轮询功能直接读取接收FIFO,我想用DMA控制器进行更改。对于这个DMA,我触发DMA,使用wait_event_interruptible睡眠,并通过带有wake_up_interruptible的DMA的ISR唤醒。如您所知,NAPI轮询功能处于中断上下文(softirq),因此我无法在那里睡眠以完成DMA。我想将NAPI轮询功能(读取RX FIFO)移动到waitqueue(进程上下文)以使用work_struct。

问题是,NAPI poll函数由内核调用,有两个参数:struct napi_struct * napi和int budget。 我想将这些参数传递给work_struct并将work_struct排队到工作队列(使用queue_work函数)。

work_struct如下所示。 (包括/ LINUX / workqueue.h)

struct work_struct { 
        atomic_long_t data;
        struct list_head entry; 
        work_func_t func;
#ifdef CONFIG_LOCKDEP
        struct lockdep_map lockdep_map;
#endif
};

我认为atomic_long_t数据用于将参数传递给work_struct。如何将参数传递给work_struct? 我尝试了这个(我在设备驱动程序struct smsc911x_data的结构中添加了一个成员struct work_struct rx_work来传递工作。):

struct work_arg { // a new struct for pass the arguments
        struct napi_struct *napi;
        int budget;
};

/* NAPI poll function */
static int smsc911x_poll(struct napi_struct *napi, int budget) {
        struct smsc911x_data *pdata =
                container_of(napi, struct smsc911x_data, napi);
        struct net_device *dev = pdata->dev;
        int npackets = 0;
if (enable_rx_use_dma == 1) {  // when using DMA for FIFO read
        prom_printf("moving it to workqueue\n");
        struct work_arg *p;
        p = kzalloc(sizeof(struct work_arg), GFP_KERNEL);
        p->napi = napi;
        p->budget = budget;
        pdata->rx_work.data = (atomic_long_t) p; // <== THIS LINE
        prom_printf("queue work, with napi = %x, budget = %d\n", napi, budget);
        queue_work(rx_work_workqueue, &pdata->rx_work); // smsc911x_poll_work } else {
        -- original NAP poll function, reads FIFO until it's empty and enables the RX interrupt and 
        -- keeps the number of processed packets to npackets.
        return npackets;
}

对于&#34;本行&#34;上面,我在编译过程中遇到错误。

with pdata->rx_work.data = p; , I get error: incompatible types when assigning to type 'atomic_long_t' from type 'struct work_arg *'
with pdata->rx_work.data = (atomic_long_t) p; , I get error: conversion to non-scalar type requested.

另外,在新的工作功能中,我如何提取原始证据?我在下面尝试了这个,这给了我错误。

/* New work function called by the default worker thread */ static int smsc911x_poll_work(struct work_struct *work) {
        struct smsc911x_data *pdata =
                container_of(work, struct smsc911x_data, rx_work);
        struct net_device *dev = pdata->dev;
        int npackets = 0;
        struct napi_struct *napi = (struct work_struct *)work->data.napi;  // <== THIS LINE
        int budget = (struct work_struct *)work->data.budget;  // <== THIS LINE ..
}

从上面的“本行”中,我得到了以下的错误。

错误:&#39; atomic_long_t&#39;没有名为&#39; napi&#39; 错误:&#39; atomic_long_t&#39;没有名为&#39; budget&#39;

的会员

并且我不知道如何将返回值传递给原来的NAPI民意调查functino调用者。

我不确定这种转换(从NAPI轮询到工作队列)是否可行。 很抱歉这些问题很长,但我们将非常感谢您的帮助。

ADD:因为struct smsc911x_data既有struct napi napi;和struct work_struct rx_work;作为成员,我可以通过以下方式轻松地从work_struct * work(工作函数的参数)获取struct napi * napi:

struct smsc911x_data * pdata =                  container_of(work,struct smsc911x_data,rx_work); struct napi_struct * napi =&amp; pdata.napi;

所以也许我可以通过struct smsc911x_data中的新成员值传递int预算。我想知道这种情况的正确做法。

2 个答案:

答案 0 :(得分:1)

  
      
  1. 如何将NAPI轮询函数参数传递给work_struct?
  2.   

只需创建新结构,嵌入work_struct并将参数添加到其中:

struct my_work {
    struct work_struct base_work;// Embedded work_struct
    struct napi_struct *napi; // Your arguments
    int budget;
};

static int smsc911x_poll(struct napi_struct *napi, int budget) {
    struct my_work* p = kmalloc(sizeof(*p), GFP_ATOMIC /* Flag usable for interrupt context */);
    INIT_WORK(&p->base_work, smsc911x_poll_work); // Initialize underliying structure.
    p->budget = budget; // Initialize your members
    p->napi = napi;
    ...
}
  
      
  1. 如何从work_struct获取NAPI轮询函数参数? (与上述Q.1相关)
  2.   

使用container_of

static int smsc911x_poll_work(struct work_struct *work) {
    struct my_work* p = container_of(work, struct my_work, base_work);
    ...
}
  
      
  1. 如何将npackets值返回给原始NAPI轮询函数调用者?
  2.   

正如我从描述中理解的那样(参见,例如,http://www.linuxfoundation.org/collaborate/workgroups/networking/napi),这个函数应该处理准备好的数据包。此处理应在函数内部完成,而不是推迟到workqueue或类似。

答案 1 :(得分:0)

这种方法似乎效率很低,因为您需要两个中断,一个中断是在接收到数据包时,一个中断是在DMA传输完成时。

我认为这是支持DMA的网络接口的工作方式: 当数据包到达时,套接字缓冲区已经分配并映射到DMA内存缓冲区,并且DMA处于就绪状态。

  1. 数据包通过DMA从NIC传输到套接字缓冲区
  2. NIC引发硬件中断(完成DMA传输后)。
  3. 硬件中断处理程序安排数据包接收软件中断(SOFTIRQ)
  4. SOFTIRQ执行NAPI poll()进行进一步处理。
  5. NAPI poll()处理DMA缓冲区中的数据包,并将其作为sk_buff传递到上层,并初始化新的DMA缓冲区。如果处理了所有数据包(配额),则启用IRQ,并告知NAPI停止轮询。