/*
 * DF-2233 -- walk hammer harness (compiled into the same KLD as the
 * PRISTINE libiconv sources; see Makefile).
 *
 * sys/libiconv/iconv.c walks the iconv_cslist / iconv_converters TAILQs
 * WITHOUT holding iconv_lock in:
 *      iconv_lookupconv()  (iconv.c:145-160)
 *      iconv_lookupcs()    (iconv.c:162-176)
 *      iconv_open()        (iconv.c:232-266)
 * while the free paths (iconv_unregister_cspair via the sysctl-add `bad:`
 * path, iconv_unregister_converter / iconv_mod_unload via kldunload)
 * free the walked nodes under LK_EXCLUSIVE.
 *
 * This harness replicates exactly what a LIBICONV filesystem mount does
 * (msdosfs_vfsops.c:119-130 / cd9660 call the same iconv_open through the
 * VFS_DECLARE_ICONV bridge), but in a tight in-kernel loop so the race
 * window is exercised millions of times per second:
 *
 *   - failing lookup "ZZQ9"->"ZZQ9": walks the FULL cslist and then the
 *     FULL converter list (composite construction loop);
 *   - every 32nd iteration a successful "UTF-16BE"->"UTF-8" open+close:
 *     exercises the ICONV_CONVERTER_OPEN(csp->cp_dcp) dispatch on a live
 *     pair (kobj method call).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/iconv.h>

static long rh_walks;	/* failed lookups: full lockless walk of both lists */
static long rh_opens;	/* successful direct opens (dispatch via cp_dcp)   */
static long rh_iters;
static int  rh_doopens;	/* sysctl-tunable: add successful opens (holds refs) */

static int
rh_run(SYSCTL_HANDLER_ARGS)
{
	int iters = 0;
	int error, i;
	void *h;

	error = sysctl_handle_int(oidp, &iters, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	for (i = 0; i < iters; i++) {
		error = iconv_open("ZZQ9", "ZZQ9", &h);
		if (error == 0) {
			iconv_close(h);
		} else {
			atomic_add_long(&rh_walks, 1);
		}
		if (rh_doopens && (i & 0x1f) == 0) {
			error = iconv_open("UTF-16BE", "UTF-8", &h);
			if (error == 0) {
				iconv_close(h);
				atomic_add_long(&rh_opens, 1);
			}
		}
		atomic_add_long(&rh_iters, 1);
	}
	return (0);
}
/* CTLFLAG_NOLOCK: several hammer processes must run the handler concurrently */
SYSCTL_PROC(_debug, OID_AUTO, race2233_run,
    CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NOLOCK,
    0, 0, rh_run, "I", "hammer iconv_open lockless walks");

SYSCTL_LONG(_debug, OID_AUTO, race2233_walks, CTLFLAG_RD, &rh_walks, 0,
    "failed lookups (full walks)");
SYSCTL_LONG(_debug, OID_AUTO, race2233_opens, CTLFLAG_RD, &rh_opens, 0,
    "successful direct opens");
SYSCTL_LONG(_debug, OID_AUTO, race2233_iters, CTLFLAG_RD, &rh_iters, 0,
    "iterations completed");
SYSCTL_INT(_debug, OID_AUTO, race2233_doopens, CTLFLAG_RW, &rh_doopens, 0,
    "also perform successful opens (holds converter refs)");
