Reduce bufdaemon/bufspacedaemon shutdown time.

Before this change bufdaemon and bufspacedaemon threads used
kthread_shutdown() to stop activity on system shutdown.  The problem is
that kthread_shutdown() has no idea about the wait channel and lock used
by specific thread to wake them up reliably.  As result, up to 9 threads
could consume up to 9 seconds to shutdown for no good reason.

This change introduces specific shutdown functions, knowing how to
properly wake up specific threads, reducing wait for those threads on
shutdown/reboot from average 4 seconds to effectively zero.

MFC after:	2 weeks
Reviewed by:	kib, markj
Differential Revision:  https://reviews.freebsd.org/D33936
This commit is contained in:
Alexander Motin 2022-01-18 19:26:16 -05:00
parent 6596f19daa
commit b7ff445ffa

View File

@ -133,6 +133,7 @@ struct bufdomain {
int bd_lim;
/* atomics */
int bd_wanted;
bool bd_shutdown;
int __aligned(CACHE_LINE_SIZE) bd_numdirtybuffers;
int __aligned(CACHE_LINE_SIZE) bd_running;
long __aligned(CACHE_LINE_SIZE) bd_bufspace;
@ -340,6 +341,11 @@ static struct mtx_padalign __exclusive_cache_line rbreqlock;
*/
static struct mtx_padalign __exclusive_cache_line bdirtylock;
/*
* bufdaemon shutdown request and sleep channel.
*/
static bool bd_shutdown;
/*
* Wakeup point for bufdaemon, as well as indicator of whether it is already
* active. Set to 1 when the bufdaemon is already "on" the queue, 0 when it
@ -628,33 +634,6 @@ bufspace_daemon_wakeup(struct bufdomain *bd)
}
}
/*
* bufspace_daemon_wait:
*
* Sleep until the domain falls below a limit or one second passes.
*/
static void
bufspace_daemon_wait(struct bufdomain *bd)
{
/*
* Re-check our limits and sleep. bd_running must be
* cleared prior to checking the limits to avoid missed
* wakeups. The waker will adjust one of bufspace or
* freebuffers prior to checking bd_running.
*/
BD_RUN_LOCK(bd);
atomic_store_int(&bd->bd_running, 0);
if (bd->bd_bufspace < bd->bd_bufspacethresh &&
bd->bd_freebuffers > bd->bd_lofreebuffers) {
msleep(&bd->bd_running, BD_RUN_LOCKPTR(bd), PRIBIO|PDROP,
"-", hz);
} else {
/* Avoid spurious wakeups while running. */
atomic_store_int(&bd->bd_running, 1);
BD_RUN_UNLOCK(bd);
}
}
/*
* bufspace_adjust:
*
@ -785,6 +764,22 @@ bufspace_wait(struct bufdomain *bd, struct vnode *vp, int gbflags,
BD_UNLOCK(bd);
}
static void
bufspace_daemon_shutdown(void *arg, int howto __unused)
{
struct bufdomain *bd = arg;
int error;
BD_RUN_LOCK(bd);
bd->bd_shutdown = true;
wakeup(&bd->bd_running);
error = msleep(&bd->bd_shutdown, BD_RUN_LOCKPTR(bd), 0,
"bufspace_shutdown", 60 * hz);
BD_RUN_UNLOCK(bd);
if (error != 0)
printf("bufspacedaemon wait error: %d\n", error);
}
/*
* bufspace_daemon:
*
@ -795,14 +790,14 @@ bufspace_wait(struct bufdomain *bd, struct vnode *vp, int gbflags,
static void
bufspace_daemon(void *arg)
{
struct bufdomain *bd;
struct bufdomain *bd = arg;
EVENTHANDLER_REGISTER(shutdown_pre_sync, kthread_shutdown, curthread,
EVENTHANDLER_REGISTER(shutdown_pre_sync, bufspace_daemon_shutdown, bd,
SHUTDOWN_PRI_LAST + 100);
bd = arg;
for (;;) {
kthread_suspend_check();
BD_RUN_LOCK(bd);
while (!bd->bd_shutdown) {
BD_RUN_UNLOCK(bd);
/*
* Free buffers from the clean queue until we meet our
@ -852,8 +847,29 @@ bufspace_daemon(void *arg)
}
maybe_yield();
}
bufspace_daemon_wait(bd);
/*
* Re-check our limits and sleep. bd_running must be
* cleared prior to checking the limits to avoid missed
* wakeups. The waker will adjust one of bufspace or
* freebuffers prior to checking bd_running.
*/
BD_RUN_LOCK(bd);
if (bd->bd_shutdown)
break;
atomic_store_int(&bd->bd_running, 0);
if (bd->bd_bufspace < bd->bd_bufspacethresh &&
bd->bd_freebuffers > bd->bd_lofreebuffers) {
msleep(&bd->bd_running, BD_RUN_LOCKPTR(bd),
PRIBIO, "-", hz);
} else {
/* Avoid spurious wakeups while running. */
atomic_store_int(&bd->bd_running, 1);
}
}
wakeup(&bd->bd_shutdown);
BD_RUN_UNLOCK(bd);
kthread_exit();
}
/*
@ -3391,6 +3407,21 @@ buf_flush(struct vnode *vp, struct bufdomain *bd, int target)
return (flushed);
}
static void
buf_daemon_shutdown(void *arg __unused, int howto __unused)
{
int error;
mtx_lock(&bdlock);
bd_shutdown = true;
wakeup(&bd_request);
error = msleep(&bd_shutdown, &bdlock, 0, "buf_daemon_shutdown",
60 * hz);
mtx_unlock(&bdlock);
if (error != 0)
printf("bufdaemon wait error: %d\n", error);
}
static void
buf_daemon()
{
@ -3402,7 +3433,7 @@ buf_daemon()
/*
* This process needs to be suspended prior to shutdown sync.
*/
EVENTHANDLER_REGISTER(shutdown_pre_sync, kthread_shutdown, curthread,
EVENTHANDLER_REGISTER(shutdown_pre_sync, buf_daemon_shutdown, NULL,
SHUTDOWN_PRI_LAST + 100);
/*
@ -3422,12 +3453,10 @@ buf_daemon()
*/
curthread->td_pflags |= TDP_NORUNNINGBUF | TDP_BUFNEED;
mtx_lock(&bdlock);
for (;;) {
while (!bd_shutdown) {
bd_request = 0;
mtx_unlock(&bdlock);
kthread_suspend_check();
/*
* Save speedupreq for this pass and reset to capture new
* requests.
@ -3464,6 +3493,8 @@ buf_daemon()
* to avoid endless loops on unlockable buffers.
*/
mtx_lock(&bdlock);
if (bd_shutdown)
break;
if (BIT_EMPTY(BUF_DOMAINS, &bdlodirty)) {
/*
* We reached our low water mark, reset the
@ -3489,6 +3520,9 @@ buf_daemon()
msleep(&bd_request, &bdlock, PVM, "qsleep", hz / 10);
}
}
wakeup(&bd_shutdown);
mtx_unlock(&bdlock);
kthread_exit();
}
/*