如何将 NAPI 轮询函数卸载到工作队列
How to offload NAPI poll function to workqueue
我正在使用 linux 3.3,smsc911x 的以太网驱动程序。我想将 NAPI 轮询功能移至工作队列。
我的问题是:
1. 如何将 NAPI 轮询函数参数传递给 work_struct?
2. 如何从 work_struct 获取 NAPI 轮询函数参数? (与上述Q.1相关)
3. 如何return 将npackets 的值传给原来的NAPI poll 函数调用者?
这里有一些解释:
当前的 NAPI 轮询功能直接读取接收 FIFO,我想更改为使用 DMA 控制器来完成。对于这个 DMA,我触发 DMA,用 wait_event_interruptible 休眠,然后用 wake_up_interruptible 被 DMA 的 ISR 唤醒。如您所知,NAPI 轮询函数处于中断上下文 (softirq) 中,所以我不能在那里睡觉等待 DMA 完成。我想将 NAPI 轮询功能(读取 RX FIFO)移动到等待队列(进程上下文)usnig a work_struct.
问题是,内核使用两个参数调用 NAPI poll 函数:struct napi_struct *napi 和 int budget。
我想将这些参数传递给 work_struct 并将 work_struct 排队到工作队列(使用 queue_work 函数)。
work_struct 如下所示。 (include/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?
我试过这个(我在设备驱动程序结构 smsc911x_data 的结构中添加了一个成员结构 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;
}
对于上面的 "THIS LINE",我在编译过程中遇到错误。
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 ..
}
从上面的“THIS LINE”中,我得到以下错误。
错误:'atomic_long_t' 没有名为 'napi' 的成员
错误:'atomic_long_t' 没有名为 'budget'
的成员
我不知道如何将 return 值传递给原始 NAPI 轮询函数调用者。
我不确定这种转换(从 NAPI 轮询到工作队列)是否可行。
很抱歉问题很长,但我们将不胜感激。
ADD : 因为 struct smsc911x_data 有两个 struct napi napi;和结构 work_struct rx_work;作为成员,我可以通过 :
从 work_struct *work (工作函数的一个参数)轻松获得 struct napi *napi
结构smsc911x_data *pdata =
container_of(工作, 结构 smsc911x_data, rx_work);结构 napi_struct *napi = &pdata.napi;
所以也许我可以通过结构 smsc911x_data 中的新成员值传递 int 预算。我仍然想知道这种情况下的正确做法。
- How do I pass the NAPI poll function arguments to the work_struct?
只需创建新结构,嵌入 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;
...
}
- How do I get the NAPI poll function arguments back from the work_struct? (related to Q.1 above)
使用container_of
:
static int smsc911x_poll_work(struct work_struct *work) {
struct my_work* p = container_of(work, struct my_work, base_work);
...
}
- How can I return the npackets value to the original NAPI poll function caller?
正如我从描述中了解到的(例如,参见 http://www.linuxfoundation.org/collaborate/workgroups/networking/napi),此函数应该处理准备就绪的数据包。并且这个处理应该在函数本身内完成,而不是推迟到 workqueue
或类似的。
这种方法似乎非常无效,因为您需要两次中断,一次是在接收到数据包时,一次是在完成 DMA 传输时。
我认为这是支持 DMA 的网络接口的工作方式:
当数据包到达时,套接字缓冲区已经分配并映射到 DMA 内存缓冲区,并且 DMA 已准备就绪。
- 数据包通过DMA从NIC传输到Socket Buffer
- NIC 引发硬件中断(当 DMA 传输完成时)。
- 硬件中断处理程序调度数据包接收软件中断 (SOFTIRQ)
- SOFTIRQ 执行 NAPI poll() 以进行进一步处理。
- NAPI poll() 处理 DMA 缓冲区中的数据包并将其作为
sk_buff
传递给上层并初始化新的 DMA 缓冲区。如果处理了所有数据包(配额),则启用 IRQ 并告知 NAPI 停止轮询。
我正在使用 linux 3.3,smsc911x 的以太网驱动程序。我想将 NAPI 轮询功能移至工作队列。
我的问题是:
1. 如何将 NAPI 轮询函数参数传递给 work_struct?
2. 如何从 work_struct 获取 NAPI 轮询函数参数? (与上述Q.1相关)
3. 如何return 将npackets 的值传给原来的NAPI poll 函数调用者?
这里有一些解释:
当前的 NAPI 轮询功能直接读取接收 FIFO,我想更改为使用 DMA 控制器来完成。对于这个 DMA,我触发 DMA,用 wait_event_interruptible 休眠,然后用 wake_up_interruptible 被 DMA 的 ISR 唤醒。如您所知,NAPI 轮询函数处于中断上下文 (softirq) 中,所以我不能在那里睡觉等待 DMA 完成。我想将 NAPI 轮询功能(读取 RX FIFO)移动到等待队列(进程上下文)usnig a work_struct.
问题是,内核使用两个参数调用 NAPI poll 函数:struct napi_struct *napi 和 int budget。 我想将这些参数传递给 work_struct 并将 work_struct 排队到工作队列(使用 queue_work 函数)。
work_struct 如下所示。 (include/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? 我试过这个(我在设备驱动程序结构 smsc911x_data 的结构中添加了一个成员结构 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;
}
对于上面的 "THIS LINE",我在编译过程中遇到错误。
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 ..
}
从上面的“THIS LINE”中,我得到以下错误。
错误:'atomic_long_t' 没有名为 'napi' 的成员 错误:'atomic_long_t' 没有名为 'budget'
的成员我不知道如何将 return 值传递给原始 NAPI 轮询函数调用者。
我不确定这种转换(从 NAPI 轮询到工作队列)是否可行。 很抱歉问题很长,但我们将不胜感激。
ADD : 因为 struct smsc911x_data 有两个 struct napi napi;和结构 work_struct rx_work;作为成员,我可以通过 :
从 work_struct *work (工作函数的一个参数)轻松获得 struct napi *napi结构smsc911x_data *pdata = container_of(工作, 结构 smsc911x_data, rx_work);结构 napi_struct *napi = &pdata.napi;
所以也许我可以通过结构 smsc911x_data 中的新成员值传递 int 预算。我仍然想知道这种情况下的正确做法。
- How do I pass the NAPI poll function arguments to the work_struct?
只需创建新结构,嵌入 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;
...
}
- How do I get the NAPI poll function arguments back from the work_struct? (related to Q.1 above)
使用container_of
:
static int smsc911x_poll_work(struct work_struct *work) {
struct my_work* p = container_of(work, struct my_work, base_work);
...
}
- How can I return the npackets value to the original NAPI poll function caller?
正如我从描述中了解到的(例如,参见 http://www.linuxfoundation.org/collaborate/workgroups/networking/napi),此函数应该处理准备就绪的数据包。并且这个处理应该在函数本身内完成,而不是推迟到 workqueue
或类似的。
这种方法似乎非常无效,因为您需要两次中断,一次是在接收到数据包时,一次是在完成 DMA 传输时。
我认为这是支持 DMA 的网络接口的工作方式: 当数据包到达时,套接字缓冲区已经分配并映射到 DMA 内存缓冲区,并且 DMA 已准备就绪。
- 数据包通过DMA从NIC传输到Socket Buffer
- NIC 引发硬件中断(当 DMA 传输完成时)。
- 硬件中断处理程序调度数据包接收软件中断 (SOFTIRQ)
- SOFTIRQ 执行 NAPI poll() 以进行进一步处理。
- NAPI poll() 处理 DMA 缓冲区中的数据包并将其作为
sk_buff
传递给上层并初始化新的 DMA 缓冲区。如果处理了所有数据包(配额),则启用 IRQ 并告知 NAPI 停止轮询。