how to offload NAPI poll function to workqueue

Chan Kim ckim at
Mon Jun 8 04:53:48 EDT 2015

I just found that ndo_start_xmit function is also in softirq context. This means the FIFO write DMA should be also moved to workqueue because softirq doesn't allow sleeping.  Does anyone know how to move this nod_start_xmit and NAPI poll function including DMA (with sleep) to workqueue? or any idea on whether it's impossible, or if there is any example driver using DMA controller for Ethernet chip? (not Ethernet chips with DMA master function insde)
Best regards,

보낸 사람 : "Chan Kim" <ckim at>
보낸 날짜 : 2015-06-08 16:06:59 ( +09:00 )
받는 사람 : kernelnewbies at <kernelnewbies at>
참조 : 
제목 : how to offload NAPI poll function to workqueue


I'm working with linux ver 3.3, Ethernet driver for smsc911x. Current NAPI poll function reads recevie FIFO directly which I want to do with DMA controller. For this DMA, I trigger DMA, sleep with wait_event_interruptible, and get woken up by DMA's ISR with wake_up_interruptible. As you know, NAPI poll function is in interrupt context (softirq) so I cannot sleep there for DMA completion. I want to move the NAPI poll function(reading RX FIFO) to waitqueue(process context) usnig a work_struct.

The problem is, NAPI poll function is called by the kernel with two arguments : struct napi_struct *napi and int budget.
I want to pass those argument to the work_struct and queue the work_struct to the workqueue (using queue_work function).

the work_struct looks like below. (include/linux/workqueue.h)

struct work_struct { 
atomic_long_t data;
struct list_head entry; 
work_func_t func;
struct lockdep_map lockdep_map; 

I take that atomic_long_t data is for passing the argument to the work_struct. how can I pass the arguments to the work_struct?
I tried this (I added in the structure for device driver struct smsc911x_data a member struct work_struct rx_work for passing the 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-> = (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;

For "THIS LINE" above, I'm getting error during compile.
with pdata-> = p; , I get error: incompatible types when assigning to type 'atomic_long_t' from type 'struct work_arg *'
with pdata-> = (atomic_long_t) p; , I get error: conversion to non-scalar type requested.

Also, in the new work function, How can I extract the original argments? I tried this below which gives me errors.

/* 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
>From the above 'THIS LINE's, I get erros below.
error: 'atomic_long_t' has no member named 'napi'
error: 'atomic_long_t' has no member named 'budget'

and I don't know how to pass the return value to the original NAPI poll functino caller.

So my questions are : 
1. How do I pass the NAPI poll function arguments to the work_struct?
2. How can I get the NAPI poll functino arguments back from the work_struct? (related to Q.1)
3. How can I return the npackets value to the original NAPI poll function caller?
I'm not sure if this kind of conversion (from NAPI poll to workqueue) is possible.
Sorry for the long questions but any help will be greatly appreciated.

Kernelnewbies mailing list
Kernelnewbies at

More information about the Kernelnewbies mailing list