Re: [PATCH net v2] net: core: make napi_disable more robust
From: Lijun Pan <hidden>
Date: 2021-04-14 17:31:49
On Wed, Apr 14, 2021 at 3:45 AM Yunsheng Lin [off-list ref] wrote:
On 2021/4/14 16:08, Lijun Pan wrote:quoted
There are chances that napi_disable can be called twice by NIC driver. This could generate deadlock. For example, the first napi_disable will spin until NAPI_STATE_SCHED is cleared by napi_complete_done, then set it again. When napi_disable is called the second time, it will loop infinitely because no dev->poll will be running to clear NAPI_STATE_SCHED. Though it is driver writer's responsibility to make sure it being called only once, making napi_disable more robust does not hurt, not to say it can prevent a buggy driver from crashing a system. So, we check the napi state bit to make sure that if napi is already disabled, we exit the call early enough to avoid spinning infinitely. Fixes: bea3348eef27 ("[NET]: Make NAPI polling independent of struct net_device objects.") Signed-off-by: Lijun Pan <redacted> --- v2: justify that this patch makes napi_disable more robust. net/core/dev.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)diff --git a/net/core/dev.c b/net/core/dev.c index 1f79b9aa9a3f..fa0aa212b7bb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c@@ -6830,6 +6830,24 @@ EXPORT_SYMBOL(netif_napi_add); void napi_disable(struct napi_struct *n) { might_sleep(); + + /* make sure napi_disable() runs only once, + * When napi is disabled, the state bits are like: + * NAPI_STATE_SCHED (set by previous napi_disable) + * NAPI_STATE_NPSVC (set by previous napi_disable) + * NAPI_STATE_DISABLE (cleared by previous napi_disable) + * NAPI_STATE_PREFER_BUSY_POLL (cleared by previous napi_complete_done) + * NAPI_STATE_MISSED (cleared by previous napi_complete_done) + */ + + if (napi_disable_pending(n)) + return; + if (test_bit(NAPI_STATE_SCHED, &n->state) && + test_bit(NAPI_STATE_NPSVC, &n->state) && + !test_bit(NAPI_STATE_MISSED, &n->state) && + !test_bit(NAPI_STATE_PREFER_BUSY_POLL, &n->state)) + return;The NAPI_STATE_DISABLE is cleared at the end of napi_disable(), and if a buggy driver/hw triggers a interrupt and driver calls napi_schedule_irqoff(), which may set NAPI_STATE_MISSED if NAPI_STATE_SCHED is set(in napi_schedule_prep()), the above checking does not seem to handle it?
What I described in the commit message is the napi_disable() being
called from the same instance, same cpu. e.g.,
funcA {
napi_disable();
...
funcB{
if (blah)
napi_disable();
...
}
funcC;
}
The scenario you mentioned above seems to have napi already enabled
and scheduled, such that napi_schedule_prep() would set NAPI_STATE_MISSED.
The two scenarios are different per my understanding. Is there a way
that your scenario will finally call into my scenario?
Let me know if I understand you correctly.
Maybe testing NAPI_STATE_MISSED bit is not needed
because this bit is not that reliable.
Lijun