/* DF-2986: cv_destroy performs no waiters validation - destroying a cv
 * with a live waiter (cv_waiters==1) is completely undiagnosed. */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/kthread.h>
#include <sys/lock.h>

static struct cv probe_cv;
static int waiter_done;

static void cv2986_thread(void *arg)
{
    cv_wait(&probe_cv, NULL);   /* blocks forever until signalled */
    waiter_done = 1;
    lwkt_exit();
}

static int cv2986_modevent(module_t mod, int type, void *data)
{
    switch (type) {
    case MOD_LOAD:
        cv_init(&probe_cv, "df2986");
        kthread_create(cv2986_thread, NULL, NULL, "df2986w");
        kprintf("DF2986: waiter thread parked in cv_wait\n");
        tsleep(&probe_cv, 0, "df2986a", hz);   /* let it block */
        kprintf("DF2986: cv_waiters=%u (thread is blocked on cv)\n",
                probe_cv.cv_waiters);
        cv_destroy(&probe_cv);   /* <-- destroy with live waiter */
        kprintf("DF2986: cv_destroy returned with NO diagnostic "
                "(spin_uninit is a no-op) - bug reproduced\n");
        /* Wake the waiter through the destroyed cv to release it cleanly */
        cv_signal(&probe_cv);
        tsleep(&probe_cv, 0, "df2986b", 2*hz);
        kprintf("DF2986: waiter_done=%d (stale cv identity still wakes it)\n",
                waiter_done);
        break;
    case MOD_UNLOAD:
        break;
    default:
        return EOPNOTSUPP;
    }
    return 0;
}
static moduledata_t cv2986_mod = { "cv2986", cv2986_modevent, NULL };
DECLARE_MODULE(cv2986, cv2986_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
