⬢ DragonFlyBSD Kernel Audit
DF-4365 / pcn_callout_replica_fixed.c
← back to finding ↓ download raw
/*
 * pcn_callout_replica_fixed.c - replica of if_pcn.c's callout protocol WITH
 * the proposed fix.diff shape applied to pcn_tick():
 *
 *   pcn_tick() never blocks the softclock thread on the serializer; on
 *   contention it retries on the next tick (lwkt_serialize_try + 1-tick
 *   callout_reset retry).  pcn_stop() keeps its synchronous callout_stop()
 *   under the serializer - but now the callback can never be parked inside
 *   lwkt_serialize_enter() holding the stopper's lock, so callout_stop()
 *   always returns (no AB-BA).
 *
 * Sysctls (root):
 *   kern.pcnrepfix_arm = 1   arm the fixed-shape timer (fast, 1 tick)
 *   kern.pcnrepfix_stop = 1  hold the serializer ~3s (worst pcn_stop
 *                            duration), then callout_stop() - must RETURN.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/serialize.h>
#include <sys/callout.h>

static struct lwkt_serialize ser;
static struct callout tick_c;

static volatile int tick_fired;
static volatile int tick_ran;
static volatile int tick_retries;

static void
pcn_tick_fixed(void *arg)
{
	tick_fired++;
	/* FIX SHAPE: never block - try, else retry next tick */
	if (!lwkt_serialize_try(&ser)) {
		tick_retries++;
		callout_reset(&tick_c, 1, pcn_tick_fixed, NULL);
		return;
	}
	tick_ran++;
	callout_reset(&tick_c, hz, pcn_tick_fixed, NULL);
	lwkt_serialize_exit(&ser);
}

static int
arm_sysctl(SYSCTL_HANDLER_ARGS)
{
	int error, v = 0;

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

	callout_reset(&tick_c, 1, pcn_tick_fixed, NULL);
	kprintf("pcnrepfix[arm]: fixed-shape pcn_stat_timer armed\n");
	return (0);
}

static int
stop_sysctl(SYSCTL_HANDLER_ARGS)
{
	int error, v = 0;
	int i;

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

	lwkt_serialize_enter(&ser);		/* pcn_stop context */
	for (i = 0; i < 3000; i++)		/* hold it ~3s: worst case */
		DELAY(1000);

	kprintf("pcnrepfix[stop]: fires=%d ran=%d retries=%d - "
	    "calling callout_stop\n", tick_fired, tick_ran, tick_retries);
	callout_stop(&tick_c);			/* must RETURN now */
	kprintf("pcnrepfix[stop]: callout_stop RETURNED, pending=%d\n",
	    callout_pending(&tick_c));
	lwkt_serialize_exit(&ser);
	return (0);
}

static int
stat_sysctl(SYSCTL_HANDLER_ARGS)
{
	int error, v = 0;

	if (req->newptr != NULL) {		/* write: report counters */
		kprintf("pcnrepfix[stat]: fires=%d ran=%d retries=%d "
		    "pending=%d\n", tick_fired, tick_ran, tick_retries,
		    callout_pending(&tick_c));
		return (0);
	}
	error = sysctl_handle_int(oidp, &v, 0, req);
	return (error);
}

static int
pcnrepfix_load(module_t mod, int what, void *arg)
{
	switch (what) {
	case MOD_LOAD:
		lwkt_serialize_init(&ser);
		callout_init(&tick_c);		/* plain, like if_pcn.c:577 */
		tick_fired = tick_ran = tick_retries = 0;
		sysctl_add_oid(NULL, SYSCTL_STATIC_CHILDREN(_kern),
		    OID_AUTO, "pcnrepfix_arm", CTLTYPE_INT|CTLFLAG_RW,
		    NULL, 0, arm_sysctl, "I", "arm fixed-shape pcn tick");
		sysctl_add_oid(NULL, SYSCTL_STATIC_CHILDREN(_kern),
		    OID_AUTO, "pcnrepfix_stop", CTLTYPE_INT|CTLFLAG_RW,
		    NULL, 0, stop_sysctl, "I", "pcn_stop shape with fix");
		sysctl_add_oid(NULL, SYSCTL_STATIC_CHILDREN(_kern),
		    OID_AUTO, "pcnrepfix_stat", CTLTYPE_INT|CTLFLAG_RW,
		    NULL, 0, stat_sysctl, "I", "report tick counters");
		kprintf("pcnrepfix: loaded (fixed-shape replica)\n");
		return (0);
	case MOD_UNLOAD:
		/* stop the armed timer first (NOT under the serializer) */
		callout_stop(&tick_c);
		return (0);
	}
	return (EOPNOTSUPP);
}

static moduledata_t pcnrepfix_mod = { "pcnrepfix", pcnrepfix_load, NULL };
DECLARE_MODULE(pcnrepfix, pcnrepfix_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);