The zero-copy netback has far more interactions with core network driver than
the old copying backend. One significant thing is that netback now relies on
a callback from core driver to correctly release resources.
However correct synchronisation between core driver and netback is missing.
Currently netback relies on a loop to wait for core driver to release
resources. This is proven not enough and erronous recently, partly due to code
structure, partly due to missing synchronisation. Short-live domains like
OpenMirage unikernels can easily trigger race in backend, rendering backend
unresponsive.
This patch series aims to slove this issue by introducing proper
synchronisation between core driver and netback.
Wei.
Change in v2: fix Zoltan's email address in commit message
Wei Liu (3):
xen-netback: move NAPI add/remove calls
xen-netback: don't stop dealloc kthread too early
xen-netback: remove loop waiting function
drivers/net/xen-netback/common.h | 5 +++
drivers/net/xen-netback/interface.c | 57 +++++++++++++++--------------------
drivers/net/xen-netback/netback.c | 24 ++++++++++++---
3 files changed, 49 insertions(+), 37 deletions(-)
--
1.7.10.4
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove to _connect and _disconnect so that they reside togother
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
The original implementation relies on a loop to check if all inflight
packets are freed. Now we have proper reference counting, there's no
need to use loop anymore.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 29 -----------------------------
1 file changed, 29 deletions(-)
@@ -736,21 +717,11 @@ void xenvif_free(struct xenvif *vif)structxenvif_queue*queue=NULL;unsignedintnum_queues=vif->num_queues;unsignedintqueue_index;-/* Here we want to avoid timeout messages if an skb can be legitimately-*stucksomewhereelse.Realisticallythiscouldbeananothervif's-*internalorQDiscqueue.Thatanothervifalsohasthis-*rx_drain_timeout_msecstimeout,sogiveittimetodrainout.-*Althoughifthatotherguestwakesupjustbeforeitstimeouthappens-*andtakesonlyoneskbfromQDisc,itcanholdontootherskbsfora-*longerperiod.-*/-unsignedintworst_case_skb_lifetime=(rx_drain_timeout_msecs/1000);unregister_netdev(vif->dev);for(queue_index=0;queue_index<num_queues;++queue_index){queue=&vif->queues[queue_index];-xenvif_wait_unmap_timeout(queue,worst_case_skb_lifetime);xenvif_deinit_queue(queue);}
Reference count the number of packets in host stack, so that we don't
stop the deallocation thread too early. If not, we can end up with
xenvif_free permanently waiting for deallocation thread to unmap grefs.
Reported-by: Thomas Leonard <redacted>
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/common.h | 5 +++++
drivers/net/xen-netback/interface.c | 16 ++++++++++++++++
drivers/net/xen-netback/netback.c | 24 ++++++++++++++++++++----
3 files changed, 41 insertions(+), 4 deletions(-)
@@ -165,6 +165,8 @@ struct xenvif_queue { /* Per-queue data for xenvif */u16dealloc_ring[MAX_PENDING_REQS];structtask_struct*dealloc_task;wait_queue_head_tdealloc_wq;+wait_queue_head_tinflight_wq;+atomic_tinflight_packets;/* Use kthread for guest RX */structtask_struct*task;
@@ -107,6 +107,18 @@ static inline unsigned long idx_to_kaddr(struct xenvif_queue *queue,#define callback_param(vif, pending_idx) \(vif->pending_tx_info[pending_idx].callback_struct)+/* This function is used to set SKBTX_DEV_ZEROCOPY as well as+*increasingtheinflightcounter.Weneedtoincreasetheinflight+*counterbecausecoredrivercallsintoxenvif_zerocopy_callback+*whichcallsxenvif_dec_inflight_packets.+*/+staticvoidset_skb_zerocopy(structxenvif_queue*queue,+structsk_buff*skb)+{+skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+xenvif_inc_inflight_packets(queue);+}+/* Find the containing VIF's structure from a pointer in pending_tx_info array*/staticinlinestructxenvif_queue*ubuf_to_queue(conststructubuf_info*ubuf)
@@ -1525,10 +1537,13 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s/* remove traces of mapped pages and frag_list */skb_frag_list_init(skb);uarg=skb_shinfo(skb)->destructor_arg;+/* See comment on set_skb_zerocopy */+if(uarg->callback==xenvif_zerocopy_callback)+xenvif_inc_inflight_packets(queue);uarg->callback(uarg,true);skb_shinfo(skb)->destructor_arg=NULL;-skb_shinfo(nskb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+set_skb_zerocopy(queue,nskb);kfree_skb(nskb);return0;
@@ -1589,7 +1604,7 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)if(net_ratelimit())netdev_err(queue->vif->dev,"Not enough memory to consolidate frag_list!\n");-skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+set_skb_zerocopy(queue,skb);kfree_skb(skb);continue;}
@@ -1609,7 +1624,7 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)"Can't setup checksum in net_tx_action\n");/* We have to set this flag to trigger the callback */if(skb_shinfo(skb)->destructor_arg)-skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+set_skb_zerocopy(queue,skb);kfree_skb(skb);continue;}
@@ -1641,7 +1656,7 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)*skb.E.g.the__pskb_pull_tailearliercandosuchthing.*/if(skb_shinfo(skb)->destructor_arg){-skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+set_skb_zerocopy(queue,skb);queue->stats.tx_zerocopy_sent++;}
From: Sergei Shtylyov <hidden> Date: 2014-08-08 16:49:15
Hello.
On 08/08/2014 08:37 PM, Wei Liu wrote:
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove
netif_napi_{add|del}()?
> to _connect and _disconnect so that they reside togother
Together.
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
On Fri, Aug 08, 2014 at 08:49:10PM +0400, Sergei Shtylyov wrote:
Hello.
On 08/08/2014 08:37 PM, Wei Liu wrote:
quoted
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove
netif_napi_{add|del}()?
quoted
to _connect and _disconnect so that they reside togother
From: David Vrabel <hidden> Date: 2014-08-11 12:10:15
On 08/08/14 17:37, Wei Liu wrote:
Reference count the number of packets in host stack, so that we don't
stop the deallocation thread too early. If not, we can end up with
xenvif_free permanently waiting for deallocation thread to unmap grefs.
@@ -107,6 +107,18 @@ static inline unsigned long idx_to_kaddr(struct xenvif_queue *queue,#define callback_param(vif, pending_idx) \(vif->pending_tx_info[pending_idx].callback_struct)+/* This function is used to set SKBTX_DEV_ZEROCOPY as well as+*increasingtheinflightcounter.Weneedtoincreasetheinflight+*counterbecausecoredrivercallsintoxenvif_zerocopy_callback+*whichcallsxenvif_dec_inflight_packets.+*/+staticvoidset_skb_zerocopy(structxenvif_queue*queue,+structsk_buff*skb)+{+skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+xenvif_inc_inflight_packets(queue);+}
This name makes this look like a core function instead of a netback
specific one.
I would suggest a pair of functions:
xenvif_skb_zerocopy_prepare()
xenvif_skb_zerocopy_complete()
Or similar.
David
From: David Vrabel <hidden> Date: 2014-08-11 12:35:02
On 08/08/14 17:37, Wei Liu wrote:
quoted hunk
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove to _connect and _disconnect so that they reside togother
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
Why have you added an additional loop over all the queues? The ordering
looks wrong as well. I think you want
1. unbind from irqhandler
2. napi del
3. stop task
4. stop dealloc task
5. unmap frontend rings.
David
From: Zoltan Kiss <hidden> Date: 2014-08-11 12:49:23
On 11/08/14 13:35, David Vrabel wrote:
On 08/08/14 17:37, Wei Liu wrote:
quoted
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove to _connect and _disconnect so that they reside togother
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
Why have you added an additional loop over all the queues? The ordering
looks wrong as well. I think you want
1. unbind from irqhandler
2. napi del
3. stop task
4. stop dealloc task
5. unmap frontend rings.
And that's how they are ordered. The idea for having the netif_napi_del
as a separate loop came from me: it could be more efficient to start
tearing down all the NAPI instances first, so by the time we stop the
dealloc thread, it is likely it already done most of the work.
But now I realized that netif_napi_del just delete the instance from a
list, the real thing happens in xenvif_carrier_off: xenvif_down calls
napi_disable on all queues, and it waits until all the work is done. So
it doesn't makes sense to have the netif_napi_del in a separate loop any
more.
From: David Vrabel <hidden> Date: 2014-08-11 13:01:09
On 11/08/14 13:49, Zoltan Kiss wrote:
On 11/08/14 13:35, David Vrabel wrote:
quoted
On 08/08/14 17:37, Wei Liu wrote:
quoted
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove to _connect and _disconnect so that they reside togother
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/net/xen-netback/interface.c
b/drivers/net/xen-netback/interface.c
index 48a55cd..fdb4fca 100644
Why have you added an additional loop over all the queues? The ordering
looks wrong as well. I think you want
1. unbind from irqhandler
2. napi del
3. stop task
4. stop dealloc task
5. unmap frontend rings.
And that's how they are ordered.
No, it isn't. Did you mistakenly look at netfront which is correctly
ordered already?
You must unbind the irq handler before calling netif_napi_del() or an
interrupt may occur and the handler may call napi_schedule() with a
deleted instance.
David
From: Zoltan Kiss <hidden> Date: 2014-08-11 13:14:19
On 11/08/14 14:01, David Vrabel wrote:
On 11/08/14 13:49, Zoltan Kiss wrote:
quoted
On 11/08/14 13:35, David Vrabel wrote:
quoted
On 08/08/14 17:37, Wei Liu wrote:
quoted
Originally napi_add was in init_queue and napi_del was in deinit_queue,
while kthreads were handled in _connect and _disconnect. Move napi_add
and napi_remove to _connect and _disconnect so that they reside togother
with kthread operations.
Signed-off-by: Wei Liu <redacted>
Cc: Ian Campbell <redacted>
Cc: Zoltan Kiss <redacted>
---
drivers/net/xen-netback/interface.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/net/xen-netback/interface.c
b/drivers/net/xen-netback/interface.c
index 48a55cd..fdb4fca 100644
Why have you added an additional loop over all the queues? The ordering
looks wrong as well. I think you want
1. unbind from irqhandler
2. napi del
3. stop task
4. stop dealloc task
5. unmap frontend rings.
And that's how they are ordered.
No, it isn't. Did you mistakenly look at netfront which is correctly
ordered already?
You must unbind the irq handler before calling netif_napi_del() or an
interrupt may occur and the handler may call napi_schedule() with a
deleted instance.
I think xenvif_carrier_off (which call xenvif_down) does that. It is
right before this new napi_del in xenvif_disconnect.
Zoli
@@ -107,6 +107,18 @@ static inline unsigned long idx_to_kaddr(struct xenvif_queue *queue,#define callback_param(vif, pending_idx) \(vif->pending_tx_info[pending_idx].callback_struct)+/* This function is used to set SKBTX_DEV_ZEROCOPY as well as+*increasingtheinflightcounter.Weneedtoincreasetheinflight+*counterbecausecoredrivercallsintoxenvif_zerocopy_callback+*whichcallsxenvif_dec_inflight_packets.+*/+staticvoidset_skb_zerocopy(structxenvif_queue*queue,+structsk_buff*skb)+{+skb_shinfo(skb)->tx_flags|=SKBTX_DEV_ZEROCOPY;+xenvif_inc_inflight_packets(queue);+}
This name makes this look like a core function instead of a netback
specific one.
I would suggest a pair of functions:
xenvif_skb_zerocopy_prepare()
xenvif_skb_zerocopy_complete()
Just make the dealloc task not stop unless it has deallocated all
outstanding requests. There's no need for another wait queue here.
Are you suggesting something like
while (atomic_read(&queue->inflight_packets) !=0)
schedule_timeout(SOME_TIMEOUT);
No. That would be awful!
Add the test to the kthread itself:
int xenvif_dealloc_kthread(void *data)
{
struct xenvif_queue *queue = data;
while (atomic_read(&queue->inflight_packets) > 0
|| !kthread_should_stop()) {
[...]
etc.
Although, the main loop is a bit confused, so I suggest adding:
static bool xenvif_dealloc_thread_should_stop(struct xenvif_queue *q)
{
/* Dealloc thread must remain running if there are any inflight
* packets so it can properly dealloc them when they complete.
*/
return atomic_read(&queue->inflight_packets) == 0
&& kthread_should_stop();
}
And cleaning it up a bit (the while() could be a for(;;)).
David
On Mon, Aug 11, 2014 at 02:59:52PM +0100, David Vrabel wrote:
On 11/08/14 14:43, Wei Liu wrote:
quoted
In short, there's no need to reorder disconnect logic and no need have a
dedicated loop for netif_napi_del.
Not for now. But I would prefer it if it was re-ordered. And similarly
in xenvif_down(), irq_disable() should be before napi_disable().
Something for another day then. In any case, even if napi_disable goes
before irq_disable, it's still safe if a tx interrupt takes place in
between. napi_schedule has no effect on a disabled instance.
Wei.
Just make the dealloc task not stop unless it has deallocated all
outstanding requests. There's no need for another wait queue here.
Are you suggesting something like
while (atomic_read(&queue->inflight_packets) !=0)
schedule_timeout(SOME_TIMEOUT);
No. That would be awful!
Add the test to the kthread itself:
int xenvif_dealloc_kthread(void *data)
{
struct xenvif_queue *queue = data;
while (atomic_read(&queue->inflight_packets) > 0
|| !kthread_should_stop()) {
[...]
etc.
Although, the main loop is a bit confused, so I suggest adding:
static bool xenvif_dealloc_thread_should_stop(struct xenvif_queue *q)
{
/* Dealloc thread must remain running if there are any inflight
* packets so it can properly dealloc them when they complete.
*/
return atomic_read(&queue->inflight_packets) == 0
&& kthread_should_stop();
}
And cleaning it up a bit (the while() could be a for(;;)).
I would recommend this:
---
@@ -2066,7 +2066,7 @@ int xenvif_dealloc_kthread(void *data) wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());- if (kthread_should_stop())+ if (kthread_should_stop() && !atomic_read(&queue->inflight_packets)) break; xenvif_tx_dealloc_action(queue);
---
If kthread_stop called, this will keep the main loop running until all callbacks are called.
Then it proceeds to the exit branch, otherwise doesn't disrupt normal operation.
Just make the dealloc task not stop unless it has deallocated all
outstanding requests. There's no need for another wait queue here.
Are you suggesting something like
while (atomic_read(&queue->inflight_packets) !=0)
schedule_timeout(SOME_TIMEOUT);
No. That would be awful!
Add the test to the kthread itself:
int xenvif_dealloc_kthread(void *data)
{
struct xenvif_queue *queue = data;
while (atomic_read(&queue->inflight_packets) > 0
|| !kthread_should_stop()) {
[...]
etc.
Although, the main loop is a bit confused, so I suggest adding:
static bool xenvif_dealloc_thread_should_stop(struct xenvif_queue *q)
{
/* Dealloc thread must remain running if there are any inflight
* packets so it can properly dealloc them when they complete.
*/
return atomic_read(&queue->inflight_packets) == 0
&& kthread_should_stop();
}
And cleaning it up a bit (the while() could be a for(;;)).
Unfortunately this approach is bogus. If xenbus thread is not blocked it
can free up various resources while dealloc thread is running -- queue
can be gone under dealloc thread's feet.
Wei.
Just make the dealloc task not stop unless it has deallocated all
outstanding requests. There's no need for another wait queue here.
Are you suggesting something like
while (atomic_read(&queue->inflight_packets) !=0)
schedule_timeout(SOME_TIMEOUT);
No. That would be awful!
Add the test to the kthread itself:
int xenvif_dealloc_kthread(void *data)
{
struct xenvif_queue *queue = data;
while (atomic_read(&queue->inflight_packets) > 0
|| !kthread_should_stop()) {
[...]
etc.
Although, the main loop is a bit confused, so I suggest adding:
static bool xenvif_dealloc_thread_should_stop(struct xenvif_queue *q)
{
/* Dealloc thread must remain running if there are any inflight
* packets so it can properly dealloc them when they complete.
*/
return atomic_read(&queue->inflight_packets) == 0
&& kthread_should_stop();
}
And cleaning it up a bit (the while() could be a for(;;)).
Unfortunately this approach is bogus. If xenbus thread is not blocked it
can free up various resources while dealloc thread is running -- queue
can be gone under dealloc thread's feet.
kthread_stop() waits until the thread exits (like pthread_join()).
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit.
David
On Mon, Aug 11, 2014 at 03:34:48PM +0100, David Vrabel wrote:
[...]
quoted
quoted
And cleaning it up a bit (the while() could be a for(;;)).
Unfortunately this approach is bogus. If xenbus thread is not blocked it
can free up various resources while dealloc thread is running -- queue
can be gone under dealloc thread's feet.
kthread_stop() waits until the thread exits (like pthread_join()).
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit.
Ah, misremeber the behaviour of kthread_stop. Sorry for the noise.
Wei.
On Mon, Aug 11, 2014 at 03:13:41PM +0100, Zoltan Kiss wrote:
[...]
quoted hunk
quoted
And cleaning it up a bit (the while() could be a for(;;)).
I would recommend this:
---
@@ -2066,7 +2066,7 @@ int xenvif_dealloc_kthread(void *data) wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());- if (kthread_should_stop())+ if (kthread_should_stop() && !atomic_read(&queue->inflight_packets)) break; xenvif_tx_dealloc_action(queue);
---
If kthread_stop called, this will keep the main loop running until all callbacks are called.
Then it proceeds to the exit branch, otherwise doesn't disrupt normal operation.
This snippet lacks change to while().
I would generally go for a shorter solution if the code is
self-explanatory.
@@ -2078,21 +2066,19 @@ int xenvif_dealloc_kthread(void *data) { struct xenvif_queue *queue = data;- while (!kthread_should_stop()) {+ for (;;) { wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());- if (kthread_should_stop())+ if (kthread_should_stop() &&+ !atomic_read(&queue->inflight_packets) &&+ !tx_dealloc_work_todo(queue)) break; xenvif_tx_dealloc_action(queue); cond_resched(); }- /* Unmap anything remaining*/- if (tx_dealloc_work_todo(queue))- xenvif_tx_dealloc_action(queue);- return 0; }
From: David Vrabel <hidden> Date: 2014-08-11 15:23:30
On 11/08/14 15:44, Wei Liu wrote:
quoted hunk
On Mon, Aug 11, 2014 at 03:13:41PM +0100, Zoltan Kiss wrote:
[...]
quoted
quoted
And cleaning it up a bit (the while() could be a for(;;)).
I would recommend this:
---
@@ -2066,7 +2066,7 @@ int xenvif_dealloc_kthread(void *data) wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());- if (kthread_should_stop())+ if (kthread_should_stop() && !atomic_read(&queue->inflight_packets)) break; xenvif_tx_dealloc_action(queue);
---
If kthread_stop called, this will keep the main loop running until all callbacks are called.
Then it proceeds to the exit branch, otherwise doesn't disrupt normal operation.
This snippet lacks change to while().
I would generally go for a shorter solution if the code is
self-explanatory.
@@ -2078,21 +2066,19 @@ int xenvif_dealloc_kthread(void *data) { struct xenvif_queue *queue = data;- while (!kthread_should_stop()) {+ for (;;) { wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());
This will never sleep if the thread is being stopped when there are
packets in flight.
- if (kthread_should_stop())
+ if (kthread_should_stop() &&
+ !atomic_read(&queue->inflight_packets) &&
+ !tx_dealloc_work_todo(queue))
break;
Moving the final dealloc into the loop adds a cond_resched() call. This
is harmless but not really necessary when the thread is about to stop.
On Mon, Aug 11, 2014 at 04:23:28PM +0100, David Vrabel wrote:
On 11/08/14 15:44, Wei Liu wrote:
quoted
On Mon, Aug 11, 2014 at 03:13:41PM +0100, Zoltan Kiss wrote:
[...]
quoted
quoted
And cleaning it up a bit (the while() could be a for(;;)).
I would recommend this:
---
@@ -2066,7 +2066,7 @@ int xenvif_dealloc_kthread(void *data) wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());- if (kthread_should_stop())+ if (kthread_should_stop() && !atomic_read(&queue->inflight_packets)) break; xenvif_tx_dealloc_action(queue);
---
If kthread_stop called, this will keep the main loop running until all callbacks are called.
Then it proceeds to the exit branch, otherwise doesn't disrupt normal operation.
This snippet lacks change to while().
I would generally go for a shorter solution if the code is
self-explanatory.
@@ -2078,21 +2066,19 @@ int xenvif_dealloc_kthread(void *data) { struct xenvif_queue *queue = data;- while (!kthread_should_stop()) {+ for (;;) { wait_event_interruptible(queue->dealloc_wq, tx_dealloc_work_todo(queue) || kthread_should_stop());
This will never sleep if the thread is being stopped when there are
packets in flight.