From 940f62d616c8bdfaa5dc585e387044d1cc99ea3a Mon Sep 17 00:00:00 2001 From: Eric Joyner Date: Tue, 23 Oct 2018 04:37:29 +0000 Subject: [PATCH] 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 Reviewed by: shurd@, erj@ Sponsored by: Intel Corporation Differential Revision: https://reviews.freebsd.org/D17404 --- sys/kern/subr_gtaskqueue.c | 1 + sys/net/iflib.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/sys/kern/subr_gtaskqueue.c b/sys/kern/subr_gtaskqueue.c index 96dc530373a..32084be0711 100644 --- a/sys/kern/subr_gtaskqueue.c +++ b/sys/kern/subr_gtaskqueue.c @@ -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, >ask->gt_task); gtask->gt_taskqueue = NULL; } diff --git a/sys/net/iflib.c b/sys/net/iflib.c index 594d50395d8..7a7420c008a 100644 --- a/sys/net/iflib.c +++ b/sys/net/iflib.c @@ -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); }