Add a regression test for a libtpool bug

Test that tpool_dispatch returns an error if it cannot start even one
worker.  Previously, it would hang.  The test must reside here rather
than in the OpenZFS repo because the latter has no infrastructure for
writing libtpool tests.

https://github.com/openzfs/zfs/issues/16172

MFC after:	2 weeks
Sponsored by:	Axcient
Differential Revision: https://reviews.freebsd.org/D45587
This commit is contained in:
Alan Somers 2024-05-07 11:48:33 -06:00
parent ece617cda6
commit 5c1ba994a8
4 changed files with 97 additions and 0 deletions

View file

@ -22,4 +22,9 @@ CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccomp
CFLAGS+= -DHAVE_ISSETUGID
CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
.include <src.opts.mk>
HAS_TESTS=
SUBDIR.${MK_TESTS}+= tests
.include <bsd.lib.mk>

View file

@ -0,0 +1,12 @@
ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
ATF_TESTS_C+= libtpool_test
TEST_METADATA+= timeout="10"
CFLAGS+= -I${ZFSTOP}/include \
-I${ZFSTOP}/lib/libspl/include
LIBADD+= pthread tpool
.include <bsd.test.mk>

View file

@ -0,0 +1,78 @@
#include <sys/stdtypes.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <pthread.h>
#include <thread_pool.h>
#include <atf-c.h>
static void
tp_delay(void *arg)
{
pthread_barrier_t *barrier = arg;
/* Block this task until all thread pool workers have been created. */
pthread_barrier_wait(barrier);
}
/*
* NB: we could reduce the test's resource cost by using rctl(4). But that
* isn't enabled by default. And even with a thread limit of 1500, it takes <
* 0.1s to run on my machine. So I don't think it's worth optimizing for the
* case where rctl is available.
*/
ATF_TC(complete_exhaustion);
ATF_TC_HEAD(complete_exhaustion, tc)
{
atf_tc_set_md_var(tc, "descr",
"A thread pool should fail to schedule tasks if it is completely impossible to spawn any threads.");
}
ATF_TC_BODY(complete_exhaustion, tc)
{
pthread_barrier_t barrier;
tpool_t *tp0, *tp1;
size_t len;
int max_threads_per_proc = 0;
int nworkers;
int r, i;
len = sizeof(max_threads_per_proc);
r = sysctlbyname("kern.threads.max_threads_per_proc",
&max_threads_per_proc, &len, NULL, 0);
ATF_REQUIRE_EQ_MSG(r, 0, "sysctlbyname: %s", strerror(errno));
nworkers = max_threads_per_proc - 1;
pthread_barrier_init(&barrier, NULL, max_threads_per_proc);
/*
* Create the first thread pool and spawn the maximum allowed number of
* processes.
*/
tp0 = tpool_create(nworkers, nworkers, 1, NULL);
ATF_REQUIRE(tp0 != NULL);
for (i = 0; i < nworkers; i++) {
ATF_REQUIRE_EQ(tpool_dispatch(tp0, tp_delay, &barrier), 0);
}
/*
* Now create a second thread pool. Unable to create new threads, the
* dispatch function should return an error.
*/
tp1 = tpool_create(nworkers, 2 * nworkers, 1, NULL);
ATF_REQUIRE(tp1 != NULL);
ATF_REQUIRE_EQ(tpool_dispatch(tp1, tp_delay, NULL), -1);
/* Cleanup */
ATF_REQUIRE_EQ(pthread_barrier_wait(&barrier), 0);
tpool_wait(tp1);
tpool_wait(tp0);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, complete_exhaustion);
return (atf_no_error());
}

View file

@ -80,6 +80,8 @@
..
cddl
lib
libtpool
..
..
sbin
..