iflib: drain enqueued tasks before detaching from taskqgroup

The taskqgroup_detach function does not check if task is already enqueued when
detaching it. This may lead to kernel panic if enqueued task starts after
context state lock is destroyed. Ensure that the already enqueued admin tasks
are executed before detaching them.

The issue was discovered during validation of D16429. Unloading of if_ixlv
followed by immediate removal of VFs with iovctl -D may lead to panic on
NODEBUG kernel.

As well, check if iflib is in detach before enqueueing new admin or iov
tasks, to prevent new tasks from executing while the taskqgroup tasks
are being drained.

Submitted by:	Krzysztof Galazka <krzysztof.galazka@intel.com>
Reviewed by:	shurd@, erj@
Sponsored by:	Intel Corporation
Differential Revision:	https://reviews.freebsd.org/D17404
This commit is contained in:
Eric Joyner 2018-10-23 04:37:29 +00:00
parent 3c22d78997
commit 940f62d616
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=339634
2 changed files with 9 additions and 3 deletions

View File

@ -812,6 +812,7 @@ taskqgroup_detach(struct taskqgroup *qgroup, struct grouptask *gtask)
qgroup->tqg_queue[i].tgc_cnt--;
LIST_REMOVE(gtask, gt_list);
mtx_unlock(&qgroup->tqg_lock);
gtaskqueue_drain(gtask->gt_taskqueue, &gtask->gt_task);
gtask->gt_taskqueue = NULL;
}

View File

@ -2279,8 +2279,8 @@ iflib_timer(void *arg)
STATE_LOCK(ctx);
if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
ctx->ifc_flags |= (IFC_DO_WATCHDOG|IFC_DO_RESET);
iflib_admin_intr_deferred(ctx);
STATE_UNLOCK(ctx);
iflib_admin_intr_deferred(ctx);
}
static void
@ -2802,8 +2802,8 @@ iflib_rxeof(iflib_rxq_t rxq, qidx_t budget)
err:
STATE_LOCK(ctx);
ctx->ifc_flags |= IFC_DO_RESET;
iflib_admin_intr_deferred(ctx);
STATE_UNLOCK(ctx);
iflib_admin_intr_deferred(ctx);
return (false);
}
@ -5973,7 +5973,10 @@ iflib_admin_intr_deferred(if_ctx_t ctx)
{
#ifdef INVARIANTS
struct grouptask *gtask;
#endif
if (iflib_in_detach(ctx))
return;
#ifdef INVARIANTS
gtask = &ctx->ifc_admin_task;
MPASS(gtask != NULL && gtask->gt_taskqueue != NULL);
#endif
@ -5984,6 +5987,8 @@ iflib_admin_intr_deferred(if_ctx_t ctx)
void
iflib_iov_intr_deferred(if_ctx_t ctx)
{
if (iflib_in_detach(ctx))
return;
GROUPTASK_ENQUEUE(&ctx->ifc_vflr_task);
}