M5 step 1: build OpenBSD hfsc/fq_codel back ends into pf.ko (ml_* API, ifq_q handshake, scheduler types); priq still attached, 14/14 suite unchanged

This commit is contained in:
lgcosta committed 2026-11-12 00:48:56 -03:00
1 parent ca75fc49cc
commit f34c4f92a9
18 files changed
+3923 -32

No files matched your search

+1 -1
View File
@@ -1 +1 @@
savedcmd_pf.mod := printf '%s\n' pfsrc/pfk_mod.o pfsrc/pfk_hooks.o compat/pfk_mbuf.o compat/pfk_kern.o compat/pfk_if.o compat/pfk_xmit.o compat/pfk_pcb.o compat/pfk_ip4.o compat/siphash.o compat/toeplitz.o compat/md5.o compat/sha2.o compat/radix.o compat/pfk_rtable.o compat/pfk_ip6.o compat/subr_tree.o pfsrc/pf_ruleset.o pfsrc/pf_table.o pfsrc/pf_norm.o pfsrc/pf_if.o pfsrc/pf_lb.o pfsrc/pf_osfp.o pfsrc/pf_syncookies.o pfsrc/pf_ioctl.o pfsrc/pf.o pfsrc/divert_sock.o pfsrc/pfk_pflog.o compat/pfk_ifq.o | awk '!x[$$0]++ { print("./"$$0) }' > pf.mod
savedcmd_pf.mod := printf '%s\n' pfsrc/pfk_mod.o pfsrc/pfk_hooks.o compat/pfk_mbuf.o compat/pfk_kern.o compat/pfk_if.o compat/pfk_xmit.o compat/pfk_pcb.o compat/pfk_ip4.o compat/siphash.o compat/toeplitz.o compat/md5.o compat/sha2.o compat/radix.o compat/pfk_rtable.o compat/pfk_ip6.o compat/subr_tree.o pfsrc/pf_ruleset.o pfsrc/pf_table.o pfsrc/pf_norm.o pfsrc/pf_if.o pfsrc/pf_lb.o pfsrc/pf_osfp.o pfsrc/pf_syncookies.o pfsrc/pf_ioctl.o pfsrc/pf.o pfsrc/divert_sock.o pfsrc/pfk_pflog.o pfsrc/fq_codel.o pfsrc/hfsc.o compat/pfk_ifq.o | awk '!x[$$0]++ { print("./"$$0) }' > pf.mod
+1 -1
View File
@@ -20,7 +20,7 @@ pf-objs := pfsrc/pfk_mod.o pfsrc/pfk_hooks.o \
# ported OpenBSD 8.0 engine
pf-objs += pfsrc/pf_ruleset.o pfsrc/pf_table.o pfsrc/pf_norm.o pfsrc/pf_if.o \
pfsrc/pf_lb.o pfsrc/pf_osfp.o pfsrc/pf_syncookies.o \
pfsrc/pf_ioctl.o pfsrc/pf.o pfsrc/divert_sock.o pfsrc/pfk_pflog.o
pfsrc/pf_ioctl.o pfsrc/pf.o pfsrc/divert_sock.o pfsrc/pfk_pflog.o pfsrc/fq_codel.o pfsrc/hfsc.o
# queueing framework (compat/pfk_ifq.c implements the ifq API with priq
# semantics; the OpenBSD hfsc/fq_codel back ends land in milestone M5)
+28 -20
View File
@@ -273,10 +273,11 @@ static const struct pfq_ops pfk_priq_pfq_ops = {
* packets are still queued, but the traffic shaping curves are not yet
* applied — recorded in the feature matrix as pending M5).
*/
const struct ifq_ops * const ifq_hfsc_ops = &pfk_priq_ifq_ops;
const struct ifq_ops * const ifq_fqcodel_ops = &pfk_priq_ifq_ops;
const struct pfq_ops * const pfq_hfsc_ops = &pfk_priq_pfq_ops;
const struct pfq_ops * const pfq_fqcodel_ops = &pfk_priq_pfq_ops;
/*
* M5: the real scheduler back ends (pfsrc/hfsc.c, pfsrc/fq_codel.c) define
* ifq_hfsc_ops/pfq_hfsc_ops/ifq_fqcodel_ops/pfq_fqcodel_ops themselves; the
* priq shim above stays as the default for interfaces without a queue rule.
*/
/* ---------------- framework ---------------- */
void
@@ -305,6 +306,29 @@ ifq_attach(struct ifqueue *ifq, const struct ifq_ops *ops, void *opsarg)
ifq->ifq_softc = opsarg;
}
/*
* Scheduler hand-off used by the ported back ends (hfsc/fq_codel) when they
* need their per-interface state. PFLINUX: the port has no ifq mutex (the
* data path is a single ordered work queue), so "enter" is a plain ownership
* check and "leave" is a no-op - the pair exists to mirror OpenBSD's API.
*/
void *
ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops)
{
if (ifq == NULL || ops == NULL || ifq->ifq_ops != ops)
return NULL; /* another scheduler owns it */
if (ifq->ifq_softc == NULL && ops->ifqop_alloc)
ifq->ifq_softc = ops->ifqop_alloc(0, NULL);
return ifq->ifq_softc;
}
void
ifq_q_leave(struct ifqueue *ifq, void *q)
{
(void)ifq;
(void)q;
}
void
ifq_destroy(struct ifqueue *ifq)
{
@@ -423,19 +447,3 @@ ifq_hdatalen(struct ifqueue *ifq)
return 0;
}
/* ---------------- scheduler back ends (milestone M5) ---------------- */
/* The staged OpenBSD hfsc.c/fq_codel.c are not in the build yet; the priq
* ops above are the active scheduler. These wrappers keep pf_ioctl's
* initialisation calls valid until M5 replaces them with the ported
* schedulers (docs/porting-notes.md). */
void
hfsc_initialize(void)
{
pr_info("pfk: hfsc back end pending M5 (priq active)\n");
}
void
fq_codel_initialize(void)
{
pr_info("pfk: fq_codel back end pending M5 (priq active)\n");
}
+11
View File
@@ -607,3 +607,14 @@ pfk_mbuf_exit(void)
{
kmem_cache_destroy(pfk_mbuf_cache);
}
void
ml_purge(struct mbuf_list *ml)
{
struct mbuf *m;
while ((m = ml_dequeue(ml)) != NULL)
m_freem(m);
ml->ml_first = ml->ml_last = NULL;
ml->ml_len = 0;
}
+89
View File
@@ -1212,5 +1212,94 @@ struct pfk_pflog_stat {
};
#define PFKIOCGETPFLOGSTAT _IOR('L', 1, struct pfk_pflog_stat)
/*
* Pieces the staged scheduler back ends (fq_codel(4)/hfsc(4) ports) need from
* OpenBSD <net/if_var.h>, <net/if_fq_codel.h> and <net/if_hfsc.h>.
*/
#ifndef nsecuptime
#define nsecuptime() getnsecuptime()
#endif
struct fqcodel_pktcntr { uint64_t packets; uint64_t bytes; };
struct hfsc_pktcntr { uint64_t packets; uint64_t bytes; };
struct fqcodel_stats {
struct fqcodel_pktcntr xmit_cnt;
struct fqcodel_pktcntr drop_cnt;
uint32_t target, interval, tc_interval_drop;
uint32_t rcvtime, mtu, qlimit, qlength;
uint64_t delaysum, delaysumsq;
uint8_t ecn, limit, flows, lscroll, flags;
};
/* ifq scheduler hand-off (see pfsrc/ifq.c): enter returns the scheduler's
* queue when it owns the interface queue, NULL otherwise. */
void *ifq_q_enter(struct ifqueue *, const struct ifq_ops *);
void ifq_q_leave(struct ifqueue *, void *);
/*
* OpenBSD <net/if_hfsc.h> pieces the staged hfsc(4) back end needs.
* PFLINUX: the port keeps the BSD scheduler sources verbatim, so the
* user-visible service curve, the class-stats record and the class-table
* limits live here instead of in a separate kernel header.
*/
struct hfsc_sc { /* user-supplied service curve */
u_int64_t m1; /* slope1 (bytes/sec) */
u_int64_t d; /* ordinate of the knee (us) */
u_int64_t m2; /* slope2 (bytes/sec) */
};
struct hfsc_class_stats {
u_int class_id;
u_int32_t class_handle;
struct hfsc_sc rsc, fsc, usc;
u_int64_t total, cumul;
u_int64_t d, e, vt, f;
u_int64_t initvt, vtperiod, parentperiod;
int nactive;
u_int64_t vtoff, cvtmax, myf, cfmin, cvtmin, myfadj, vtadj;
u_int period;
struct hfsc_pktcntr xmit_cnt, drop_cnt;
u_int64_t cur_time;
u_int64_t machclk_freq;
int qlength;
int qlimit;
int qtype;
};
/* OpenBSD's global nanoseconds-per-clocktick (HFSC_CLK_PER_TICK). The port
* has no clock_rate machinery, so derive it from the kernel's HZ. */
#ifndef tick_nsec
#define tick_nsec (1000000000LL / HZ)
#endif
#define HFSC_ROOT_CLASS 0x1 /* root class of the tree */
#define HFSC_DEFAULTCLASS 0x8 /* auto-created default class */
#define HFSC_MAX_CLASSES 64
#define HFSC_DEFAULT_CLASSES 8
#define HFSC_DEFAULT_QLIMIT 1000
/*
* PFLINUX notes on the scheduler back ends:
*
* - splnet()/splx(): OpenBSD raises the software interrupt level around
* class-table surgery. This port runs the whole data path in one ordered
* work queue and guards the queue with ifq_mtx, so these are no-ops; they
* are kept as markers of the critical regions.
* - KERNEL_ASSERT_LOCKED(): the schedulers are only reached from ifq/pf paths
* that already hold the queue mutex; asserted structurally, not by spl.
* - HFSC_ENABLED(): the scheduler owns the interface queue once it attached
* its private state through ifq_q_enter().
*/
#define splnet() 0
#define splx(s) ((void)(s))
#define KERNEL_ASSERT_LOCKED() do { } while (0)
#define HFSC_ENABLED(_ifq) ((_ifq)->ifq_q != NULL)
#define PKTCNTR_INC(_c, _len) do { (_c)->packets++; (_c)->bytes += (_len); } \
while (0)
#endif /* _PFK_H */
+73
View File
@@ -74,6 +74,79 @@ ml_dequeue(struct mbuf_list *ml)
return m;
}
/* BSD <net/if_var.h> mbuf_list helpers (net/if_var.h ml_*) */
static inline void
ml_init(struct mbuf_list *ml)
{
ml->ml_first = ml->ml_last = NULL;
ml->ml_len = 0;
}
static inline void
ml_enqueue(struct mbuf_list *ml, struct mbuf *m)
{
m->m_next = NULL;
if (ml->ml_last == NULL)
ml->ml_first = m;
else
ml->ml_last->m_next = m;
ml->ml_last = m;
ml->ml_len++;
}
static inline struct mbuf *
ml_dequeue_tail(struct mbuf_list *ml)
{
struct mbuf *m, *prev = NULL;
if (ml->ml_first == NULL)
return NULL;
for (m = ml->ml_first; m->m_next; m = m->m_next)
prev = m;
if (prev == NULL) /* single element */
ml->ml_first = NULL;
else
prev->m_next = NULL;
ml->ml_last = prev;
ml->ml_len--;
m->m_next = NULL;
return m;
}
/* append every packet in m2 to m1 (m2 becomes empty) */
static inline void
ml_enlist(struct mbuf_list *m1, struct mbuf_list *m2)
{
if (m2->ml_first == NULL)
return;
if (m1->ml_last == NULL)
m1->ml_first = m2->ml_first;
else
m1->ml_last->m_next = m2->ml_first;
m1->ml_last = m2->ml_last;
m1->ml_len += m2->ml_len;
m2->ml_first = m2->ml_last = NULL;
m2->ml_len = 0;
}
static inline unsigned int
ml_len(const struct mbuf_list *ml)
{
return (unsigned int)ml->ml_len;
}
static inline int
ml_empty(const struct mbuf_list *ml)
{
return ml->ml_first == NULL;
}
void ml_purge(struct mbuf_list *ml); /* compat/pfk_mbuf.c (needs m_freem) */
#define MBUF_LIST_FIRST(_ml) ((_ml)->ml_first)
#define MBUF_LIST_INITIALIZER() { NULL, NULL, 0 }
/* egress interface (default-route dev), maintained on fib/net events */
struct pfk_ifnet *pfk_egress_if(void);
void pfk_egress_refresh(void);
+1 -1
View File
@@ -73,7 +73,7 @@ struct pkthdr {
u_int32_t ph_flowid;
u_int ph_ifidx;
u_int ph_rtableid;
struct pfk_timeval ph_timestamp;
int64_t ph_timestamp; /* OpenBSD 8.0: nanoseconds */
struct pfk_ifnet *ifp;
struct pfk_ifnet *rcvif;
u_short ph; /* tso segs: unused, kept as pad */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+3 -1
View File
@@ -332,7 +332,9 @@ control_law(struct codel *cd, struct codel_params *cp, int64_t rts)
unsigned int idx;
KASSERT(cd->drops > 0);
idx = min(cd->drops - 1, nitems(codel_intervals) - 1);
/* PFLINUX: Linux' min() type-checks its arguments; nitems() is size_t */
idx = min_t(unsigned int, cd->drops - 1,
(unsigned int)(nitems(codel_intervals) - 1));
cd->next = rts + cp->intervals[idx];
}
+2 -2
View File
@@ -335,9 +335,9 @@ hfsc_class_purge(struct hfsc_class *cl, struct mbuf_list *ml)
}
static inline u_int
hfsc_more_slots(u_int current)
hfsc_more_slots(u_int cur) /* PFLINUX: 'current' is a kernel macro */
{
u_int want = current * 2;
u_int want = cur * 2;
return (want > HFSC_MAX_CLASSES ? HFSC_MAX_CLASSES : want);
}