git: b411372b7d17 - main - mpi3mr: Don't hold fwevt_lock over call to taskqueue_drain
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 29 Nov 2023 01:55:30 UTC
The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=b411372b7d17ae7e5d6c944732d41b979bde2ac4 commit b411372b7d17ae7e5d6c944732d41b979bde2ac4 Author: Warner Losh <imp@FreeBSD.org> AuthorDate: 2023-11-29 01:48:48 +0000 Commit: Warner Losh <imp@FreeBSD.org> CommitDate: 2023-11-29 01:48:48 +0000 mpi3mr: Don't hold fwevt_lock over call to taskqueue_drain Holding fwevt_lock when we call taskqueue_drain can lead to deadlock because it's draining a queue needs fwevt_lock to do work, so that other thread will try to take out the lock and block, making the thread never finish and taskqueue_drain never complete. There's a witness warning/error for this which was exposed when the lock was converted to a MTX_DEF lock from a MTX_SPIN prior to committing to the FreeBSD tree. The lock appears to be to protect against additional items being added to the event list while we're doing a reset. Since the taskqueue is blocked, items can get added to the list, but won't be processed during the reset, but there is still a (likely small) race between the taskqueue_drain and the taskqueue_block calls where an interrupt could fire on another CPU, resulting in a task being enqueued and started before the block can take effect. The only way to fix that race is to turn off interrupt processing during a reset. So we replace a deadlock with a smaller race. Sponsored by: Netflix Reviewed by: sumit.saxena_broadcom.com, mav, jhb Differential Revision: https://reviews.freebsd.org/D42537 --- sys/dev/mpi3mr/mpi3mr.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sys/dev/mpi3mr/mpi3mr.c b/sys/dev/mpi3mr/mpi3mr.c index 776bfbdaee23..478b0944defa 100644 --- a/sys/dev/mpi3mr/mpi3mr.c +++ b/sys/dev/mpi3mr/mpi3mr.c @@ -2784,7 +2784,6 @@ int mpi3mr_initialize_ioc(struct mpi3mr_softc *sc, U8 init_type) mtx_init(&sc->sense_buf_q_lock, "Sense buffer Queue lock", NULL, MTX_SPIN); mtx_init(&sc->chain_buf_lock, "Chain buffer lock", NULL, MTX_SPIN); mtx_init(&sc->cmd_pool_lock, "Command pool lock", NULL, MTX_DEF); -// mtx_init(&sc->fwevt_lock, "Firmware Event lock", NULL, MTX_SPIN); mtx_init(&sc->fwevt_lock, "Firmware Event lock", NULL, MTX_DEF); mtx_init(&sc->target_lock, "Target lock", NULL, MTX_SPIN); mtx_init(&sc->reset_mutex, "Reset lock", NULL, MTX_DEF); @@ -5825,11 +5824,14 @@ static int mpi3mr_issue_reset(struct mpi3mr_softc *sc, U16 reset_type, inline void mpi3mr_cleanup_event_taskq(struct mpi3mr_softc *sc) { - mtx_lock(&sc->fwevt_lock); - taskqueue_drain(sc->cam_sc->ev_tq, &sc->cam_sc->ev_task); + /* + * Block the taskqueue before draining. This means any new tasks won't + * be queued to a worker thread. But it doesn't stop the current workers + * that are running. taskqueue_drain waits for those correctly in the + * case of thread backed taskqueues. + */ taskqueue_block(sc->cam_sc->ev_tq); - mtx_unlock(&sc->fwevt_lock); - return; + taskqueue_drain(sc->cam_sc->ev_tq, &sc->cam_sc->ev_task); } /**