- pfk.h: BSD types/ioctl encoding (OpenBSD bit layout), locks (NET_LOCK=rwsem), pools, timeouts, mbuf shim over sk_buff, ifnet registry, toeplitz/siphash - pfk_mod/pfk_hooks: misc /dev/pf (0600), IPv4 netfilter hook skeleton, DIOCGETSTATUS answered (M1 skeleton; engine ioctls land with pf_ioctl.c) - vendorize/stage-port scripts: deterministic upstream staging with seds (struct renames, IFF_*->PFIFF_*, sa_len drops) logged to docs/porting-notes - fixes: modules_prepare+Module.symvers for out-of-tree builds
333 lines
6.5 KiB
C
333 lines
6.5 KiB
C
/*
|
|
* PF-on-Linux kernel API compatibility (log/lock/malloc/pool/timeout/tsleep)
|
|
* ISC (c) 2026 PF-on-Linux contributors
|
|
*/
|
|
#include <pfk.h>
|
|
|
|
/* ---------------- logging ---------------- */
|
|
void
|
|
pfk_log(int level, const char *fmt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list ap;
|
|
|
|
if (level < 0 || level > LOG_DEBUG)
|
|
level = LOG_INFO;
|
|
va_start(ap, fmt);
|
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
switch (level) {
|
|
case LOG_EMERG: pr_emerg("%s\n", buf); break;
|
|
case LOG_ALERT: pr_alert("%s\n", buf); break;
|
|
case LOG_CRIT: pr_crit("%s\n", buf); break;
|
|
case LOG_ERR: pr_err("%s\n", buf); break;
|
|
case LOG_WARNING: pr_warn("%s\n", buf); break;
|
|
case LOG_NOTICE: pr_notice("%s\n", buf); break;
|
|
default:
|
|
pr_info_ratelimited("%s\n", buf); break;
|
|
}
|
|
}
|
|
|
|
int
|
|
pfk_printk_compat(const char *fmt, ...)
|
|
{
|
|
char buf[512];
|
|
va_list ap;
|
|
int n;
|
|
|
|
va_start(ap, fmt);
|
|
n = vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
if (n > 0)
|
|
pr_info("%s", buf);
|
|
return n;
|
|
}
|
|
|
|
/* ---------------- NET_LOCK ---------------- */
|
|
static DECLARE_RWSEM(pfk_net_sem);
|
|
static atomic_t pfk_net_readers = ATOMIC_INIT(0);
|
|
|
|
void pfk_net_lock_init(void) { }
|
|
void pfk_net_lock(void) { down_write(&pfk_net_sem); }
|
|
void pfk_net_unlock(void) { up_write(&pfk_net_sem); }
|
|
void pfk_net_read_enter(void) { down_read(&pfk_net_sem);
|
|
atomic_inc(&pfk_net_readers); }
|
|
void pfk_net_read_release(void){ atomic_dec(&pfk_net_readers);
|
|
up_read(&pfk_net_sem); }
|
|
|
|
void
|
|
rw_init(struct rwlock *l, const char *name)
|
|
{
|
|
init_rwsem(&l->sem);
|
|
l->name = name;
|
|
l->is_writer = 0;
|
|
atomic_set(&l->readers, 0);
|
|
}
|
|
|
|
void
|
|
rw_enter(struct rwlock *l, int mode)
|
|
{
|
|
if (mode == RW_WRITER) {
|
|
down_write(&l->sem);
|
|
l->is_writer = 1;
|
|
} else {
|
|
down_read(&l->sem);
|
|
atomic_inc(&l->readers);
|
|
}
|
|
}
|
|
|
|
void
|
|
rw_exit(struct rwlock *l)
|
|
{
|
|
if (l->is_writer) {
|
|
l->is_writer = 0;
|
|
up_write(&l->sem);
|
|
} else {
|
|
atomic_dec(&l->readers);
|
|
up_read(&l->sem);
|
|
}
|
|
}
|
|
|
|
int
|
|
rw_status(struct rwlock *l)
|
|
{
|
|
return l->is_writer ? RW_WRITER :
|
|
atomic_read(&l->readers) ? RW_READER : RW_NAPUTS;
|
|
}
|
|
|
|
/* ---------------- malloc ---------------- */
|
|
void *
|
|
pfk_malloc(size_t sz, int flags)
|
|
{
|
|
gfp_t g = (flags & M_WAITOK) ? GFP_KERNEL : GFP_ATOMIC;
|
|
|
|
if (flags & M_ZERO)
|
|
return kzalloc(sz, g);
|
|
return kmalloc(sz, g);
|
|
}
|
|
|
|
void
|
|
pfk_free(void *p)
|
|
{
|
|
kfree(p);
|
|
}
|
|
|
|
/* ---------------- pools ---------------- */
|
|
static atomic64_t pfk_pool_id = ATOMIC64_INIT(0);
|
|
|
|
int
|
|
pool_init(struct pool *pl, size_t size, int align, int ipl, const char *use,
|
|
void *p, int flags, void (*ih)(void *, void *), void *arg,
|
|
struct srcptr_static *sp)
|
|
{
|
|
memset(pl, 0, sizeof(*pl));
|
|
pl->p_size = size;
|
|
return 0; /* slab lazily created on first pool_get */
|
|
}
|
|
|
|
static int
|
|
pool_get_cache(struct pool *pl)
|
|
{
|
|
char nm[64];
|
|
struct kmem_cache *c;
|
|
|
|
snprintf(nm, sizeof(nm), "pfk_pool_%llu",
|
|
(unsigned long long)atomic64_inc_return(&pfk_pool_id));
|
|
c = kmem_cache_create_usercopy(nm, pl->p_size, 0, SLAB_HWCACHE_ALIGN,
|
|
0, pl->p_size, NULL);
|
|
pl->slab = c;
|
|
return c ? 0 : -ENOMEM;
|
|
}
|
|
|
|
void
|
|
pool_destroy(struct pool *pl)
|
|
{
|
|
if (pl->slab) {
|
|
kmem_cache_destroy(pl->slab);
|
|
pl->slab = NULL;
|
|
}
|
|
}
|
|
|
|
void *
|
|
pool_get(struct pool *pl, int flags)
|
|
{
|
|
void *v;
|
|
|
|
if (!pl->slab && pool_get_cache(pl))
|
|
return NULL;
|
|
v = kmem_cache_alloc(pl->slab,
|
|
(flags & PR_WAITOK) ? GFP_KERNEL :
|
|
(flags & PR_NOWAIT) ? GFP_ATOMIC :
|
|
__GFP_RETRY_MAYFAIL | GFP_KERNEL);
|
|
if (v) {
|
|
pl->p_used++;
|
|
if (flags & PR_ZERO)
|
|
memset(v, 0, pl->p_size);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
void
|
|
pool_put(struct pool *pl, void *v)
|
|
{
|
|
if (pl->slab && v) {
|
|
kmem_cache_free(pl->slab, v);
|
|
if (pl->p_used)
|
|
pl->p_used--;
|
|
}
|
|
}
|
|
|
|
void
|
|
pool_sethardlimit(struct pool *pl, unsigned long lim, const char *msg, int lvl)
|
|
{
|
|
pl->p_hardlimit = lim;
|
|
}
|
|
|
|
/* ---------------- timeout ---------------- */
|
|
static void
|
|
pfk_timeout_work(struct work_struct *w)
|
|
{
|
|
struct timeout *to = container_of(w, struct timeout, work);
|
|
|
|
to->running = 1;
|
|
to->pending = 0;
|
|
if (to->fn)
|
|
to->fn(to->arg);
|
|
to->running = 0;
|
|
if (to->period_ms >= 0)
|
|
mod_timer(&to->timer, jiffies + msecs_to_jiffies(to->period_ms));
|
|
}
|
|
|
|
static void
|
|
pfk_timeout_fire(struct timer_list *tl)
|
|
{
|
|
struct timeout *to = container_of(tl, struct timeout, timer);
|
|
|
|
schedule_work(&to->work);
|
|
}
|
|
|
|
void
|
|
timeout_set(struct timeout *to, timeout_fn *fn, void *arg)
|
|
{
|
|
if (!to->fn) { /* one-time setup */
|
|
timer_setup(&to->timer, pfk_timeout_fire, 0);
|
|
INIT_WORK(&to->work, pfk_timeout_work);
|
|
to->period_ms = -1;
|
|
}
|
|
to->fn = fn;
|
|
to->arg = arg;
|
|
}
|
|
|
|
void
|
|
timeout_set_micro(struct timeout *to, timeout_fn *fn, void *arg)
|
|
{
|
|
timeout_set(to, fn, arg);
|
|
}
|
|
|
|
int
|
|
timeout_add(struct timeout *to, long ms)
|
|
{
|
|
if (ms < 0)
|
|
ms = 0;
|
|
to->pending = 1;
|
|
to->deadline_jif = jiffies + msecs_to_jiffies(ms);
|
|
return mod_timer(&to->timer, to->deadline_jif);
|
|
}
|
|
|
|
int
|
|
timeout_add_sec(struct timeout *to, int secs)
|
|
{
|
|
return timeout_add(to, (long)secs * 1000);
|
|
}
|
|
|
|
int
|
|
timeout_add_usec(struct timeout *to, int us)
|
|
{
|
|
return timeout_add(to, us > 1000 ? us / 1000 : (us ? 1 : 0));
|
|
}
|
|
|
|
int
|
|
timeout_pending(struct timeout *to)
|
|
{
|
|
return to->pending;
|
|
}
|
|
|
|
int
|
|
timeout_del(struct timeout *to)
|
|
{
|
|
to->pending = 0;
|
|
return timer_delete(&to->timer);
|
|
}
|
|
|
|
void
|
|
timeout_del_sync(struct timeout *to)
|
|
{
|
|
to->pending = 0;
|
|
timer_delete_sync(&to->timer);
|
|
cancel_work_sync(&to->work);
|
|
}
|
|
|
|
void
|
|
timeout_barrier(struct timeout *to, int flags)
|
|
{
|
|
timer_delete_sync(&to->timer);
|
|
cancel_work_sync(&to->work);
|
|
}
|
|
|
|
/* ---------------- tsleep/wakeup (control path only) ---------------- */
|
|
struct pfk_wait {
|
|
struct list_head link;
|
|
struct wait_queue_head wq;
|
|
int done;
|
|
};
|
|
|
|
static DEFINE_SPINLOCK(pfk_tsleep_lock);
|
|
/* PFLINUX: BSD queue.h shadows LIST_HEAD (2-arg) — use raw initializer */
|
|
static struct list_head pfk_tsleep_list = {
|
|
.next = &pfk_tsleep_list,
|
|
.prev = &pfk_tsleep_list,
|
|
};
|
|
|
|
int
|
|
tsleep(void *p, int pr, const char *w, int timo)
|
|
{
|
|
struct pfk_wait *pw;
|
|
long left;
|
|
|
|
pw = kmalloc(sizeof(*pw), GFP_KERNEL);
|
|
if (!pw)
|
|
return EINTR;
|
|
init_waitqueue_head(&pw->wq);
|
|
pw->done = 0;
|
|
spin_lock(&pfk_tsleep_lock);
|
|
list_add_tail(&pw->link, &pfk_tsleep_list);
|
|
pw->link.next = (void *)p; /* stash ident */
|
|
spin_unlock(&pfk_tsleep_lock);
|
|
left = wait_event_interruptible_timeout(pw->wq, pw->done,
|
|
timo ? msecs_to_jiffies(timo) : MAX_JIFFY_OFFSET);
|
|
spin_lock(&pfk_tsleep_lock);
|
|
list_del(&pw->link);
|
|
spin_unlock(&pfk_tsleep_lock);
|
|
kfree(pw);
|
|
if (!left)
|
|
return EWOULDBLOCK;
|
|
if (left == -ERESTARTSYS)
|
|
return EINTR;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
pfk_wakeup(void *p)
|
|
{
|
|
struct pfk_wait *pw;
|
|
|
|
spin_lock(&pfk_tsleep_lock);
|
|
list_for_each_entry(pw, &pfk_tsleep_list, link) {
|
|
if (pw->link.next == (void *)p) {
|
|
pw->done = 1;
|
|
wake_up_interruptible(&pw->wq);
|
|
}
|
|
}
|
|
spin_unlock(&pfk_tsleep_lock);
|
|
}
|