From: Eric Dumazet <hidden> Date: 2017-04-06 10:26:20
On Wed, 2017-04-05 at 19:06 -0700, Tushar Dave wrote:
Reducing real_num_tx_queues needs to be in sync with skb queue_mapping
otherwise skbs with queue_mapping greater than real_num_tx_queues
can be sent to the underlying driver and can result in kernel panic.
One such event is running netconsole and enabling VF on the same
device. Or running netconsole and changing number of tx queues via
ethtool on same device.
e.g.
@@ -117,6 +118,12 @@ static void queue_process(struct work_struct *work)HARD_TX_LOCK(dev,txq,smp_processor_id());if(netif_xmit_frozen_or_stopped(txq)||netpoll_start_xmit(skb,dev,txq)!=NETDEV_TX_OK){+/* check if skb->queue_mapping has changed */+q_index=skb_get_queue_mapping(skb);+if(unlikely(q_index>=dev->real_num_tx_queues)){+q_index=q_index%dev->real_num_tx_queues;+skb_set_queue_mapping(skb,q_index);+}skb_queue_head(&npinfo->txq,skb);HARD_TX_UNLOCK(dev,txq);local_irq_restore(flags);
Hi Thushar, thank you for working on this issue.
Where and when skb->queue_mapping has changed ?
It looks that the real problem is that dev->real_num_tx_queues has been
changed instead of skb->queue_mapping
So maybe the more correct change would be to cap skb->queue_mapping even
before getting skb_get_tx_queue() ?
Otherwise, even after your patch, we might still access an invalid queue
on the device ?
Something like the following :
On Wed, 2017-04-05 at 19:06 -0700, Tushar Dave wrote:
quoted
Reducing real_num_tx_queues needs to be in sync with skb queue_mapping
otherwise skbs with queue_mapping greater than real_num_tx_queues
can be sent to the underlying driver and can result in kernel panic.
One such event is running netconsole and enabling VF on the same
device. Or running netconsole and changing number of tx queues via
ethtool on same device.
e.g.
@@ -117,6 +118,12 @@ static void queue_process(struct work_struct *work)HARD_TX_LOCK(dev,txq,smp_processor_id());if(netif_xmit_frozen_or_stopped(txq)||netpoll_start_xmit(skb,dev,txq)!=NETDEV_TX_OK){+/* check if skb->queue_mapping has changed */+q_index=skb_get_queue_mapping(skb);+if(unlikely(q_index>=dev->real_num_tx_queues)){+q_index=q_index%dev->real_num_tx_queues;+skb_set_queue_mapping(skb,q_index);+}skb_queue_head(&npinfo->txq,skb);HARD_TX_UNLOCK(dev,txq);local_irq_restore(flags);
Hi Tushar, thank you for working on this issue.
Eric,
Thank you for reviewing my change and your valuable comments.
Where and when skb->queue_mapping has changed ?
Well, I should amend the comment,
"/* check if skb->queue_mapping has changed */" is misleading.
I should say, /* check if dev->real_num_tx_queues has changed */
It looks that the real problem is that dev->real_num_tx_queues has been
changed instead of skb->queue_mapping
Yes.
So maybe the more correct change would be to cap skb->queue_mapping even
before getting skb_get_tx_queue() ?
Otherwise, even after your patch, we might still access an invalid queue
on the device ?
This is the case of direct xmit (netdev_start_xmit). Most of underlying
mq device drivers retrieve tx queue index from skb->queue_mapping.
One possibility I see where device driver still get invalid queue index
(skb->queue_mapping) with my patch is when following occurs:
- after executing "txq = skb_get_tx_queue(dev, skb)" cpu is interrupted';
- some other cpu reduced dev->real_num_tx_queues;
- Interrupted cpu resume (queue_process()).
With above sequence, at the underlying driver's xmit function we can
have skb->queue_mapping > dev->real_num_tx_queues [1].
I like your suggested patch more than mine though because it checks for
change in dev->real_num_tx_queues before even we retrieve 'netdev_queue
txq'. Less chance to have wrong txq.
However even your patch has same issue like [1] ? (see my comment below).
or cpu interrupted here and dev->real_num_tx_queues has reduced!
In any case we hit the bug or am I missing something?
The other concern is concurrent execution of netpoll's direct xmit path
and updates to dev->real_num_tx_queues (by other cpu)?
Thanks.
-Tushar
or cpu interrupted here and dev->real_num_tx_queues has reduced!
If dev->real_num_tx_queues can be changed while this code is running we
are in deep deep trouble.
Better make sure that when control path does this change, device (and/pr
netpoll) is frozen and no packet can be sent.
or cpu interrupted here and dev->real_num_tx_queues has reduced!
If dev->real_num_tx_queues can be changed while this code is running we
are in deep deep trouble.
Better make sure that when control path does this change, device (and/pr
netpoll) is frozen and no packet can be sent.
When control path is making change to real_num_tx_queues, underlying
device is disabled; also netdev tx queues are stopped/disabled so
certainly no transmit is happening.
The corner case I was referring is if netpoll's queue_process() code is
interrupted and while it is not running, control path makes change to
dev->real_num_tx_queues and exits. Later on, interrupted queue_process()
resume execution and it can end up with wrong skb->queue_mapping and txq.
We can prevent this case with below change:
or cpu interrupted here and dev->real_num_tx_queues has reduced!
If dev->real_num_tx_queues can be changed while this code is running we
are in deep deep trouble.
Better make sure that when control path does this change, device (and/pr
netpoll) is frozen and no packet can be sent.
When control path is making change to real_num_tx_queues, underlying
device is disabled; also netdev tx queues are stopped/disabled so
certainly no transmit is happening.
The corner case I was referring is if netpoll's queue_process() code is
interrupted and while it is not running, control path makes change to
dev->real_num_tx_queues and exits. Later on, interrupted queue_process()
resume execution and it can end up with wrong skb->queue_mapping and txq.
We can prevent this case with below change:
or cpu interrupted here and dev->real_num_tx_queues has reduced!
If dev->real_num_tx_queues can be changed while this code is running we
are in deep deep trouble.
Better make sure that when control path does this change, device (and/pr
netpoll) is frozen and no packet can be sent.
When control path is making change to real_num_tx_queues, underlying
device is disabled; also netdev tx queues are stopped/disabled so
certainly no transmit is happening.
The corner case I was referring is if netpoll's queue_process() code is
interrupted and while it is not running, control path makes change to
dev->real_num_tx_queues and exits. Later on, interrupted queue_process()
resume execution and it can end up with wrong skb->queue_mapping and txq.
We can prevent this case with below change: