mirror of
https://github.com/lgcosta/pf-linux-port.git
synced 2026-11-12 21:48:05 -03:00
M2 data path: BSD netisr workqueue + lock reentrancy fixes + diagnostics
- hooks: packets are queued to pfk_netisr ordered wq (process context) and re-injected with an skb->mark guard (PFK_MARK_PASSED); softirq-safe, documented divergence vs OpenBSD in-place ip_input continuation - pfk_rwlock: per-task writer recursion (NET/PF lock nesting like OpenBSD) - pfk_net: holder-task+depth reentrancy (pid-based hack removed) - ifnet events deferred to ordered workqueue (rtnl AB-BA fix) - idr iteration start index fix (kifs were never attached -> kif==NULL drop) - pfk_lock_trace module param for lock diagnostics - tests/vm: 01-state 02-block 03-tables 04-anchors + lib; vm-run-tests.sh - docs/en/install.md: apply/build guide (core patch set currently EMPTY)
This commit is contained in:
1 parent
5323ee2c71
commit
a0941b77fe
12 files changed
+413
-108
No files matched your search
+51
-11
@@ -331,15 +331,44 @@ static struct notifier_block pfk_netdev_nb = {
|
||||
|
||||
static void (*pfk_if_event_cb)(int added, struct pfk_ifnet *ifp);
|
||||
|
||||
/*
|
||||
* PFLINUX: netdev notifiers run with rtnl held; taking NET_LOCK there
|
||||
* deadlocks (AB-BA) against control paths that hold NET_LOCK and touch
|
||||
* rtnl (dev_get_by_name etc.). Interface attach/detach is therefore
|
||||
* deferred to an ordered workqueue (rtnl-free context) preserving order.
|
||||
*/
|
||||
struct pfk_if_event {
|
||||
struct work_struct work;
|
||||
int added;
|
||||
struct pfk_ifnet *ifp;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *pfk_if_wq;
|
||||
|
||||
static void
|
||||
pfk_if_event_work(struct work_struct *w)
|
||||
{
|
||||
struct pfk_if_event *ev = container_of(w, struct pfk_if_event, work);
|
||||
|
||||
if (pfk_if_event_cb)
|
||||
pfk_if_event_cb(ev->added, ev->ifp);
|
||||
kfree(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
pfk_if_event_call(int added, struct pfk_ifnet *ifp)
|
||||
{
|
||||
if (!pfk_if_event_cb)
|
||||
struct pfk_if_event *ev;
|
||||
|
||||
if (!pfk_if_event_cb || !pfk_if_wq)
|
||||
return;
|
||||
/* netdev notifiers run in process context; NET lock is sleepable */
|
||||
pfk_net_lock();
|
||||
pfk_if_event_cb(added, ifp);
|
||||
pfk_net_unlock();
|
||||
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
|
||||
if (!ev)
|
||||
return;
|
||||
INIT_WORK(&ev->work, pfk_if_event_work);
|
||||
ev->added = added;
|
||||
ev->ifp = ifp;
|
||||
queue_work(pfk_if_wq, &ev->work);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -352,7 +381,7 @@ void
|
||||
pfk_if_iterate(void (*cb)(struct pfk_ifnet *ifp))
|
||||
{
|
||||
struct pfk_ifnet *ifp;
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
|
||||
while ((ifp = pfk_if_next_from(&id)) != NULL)
|
||||
cb(ifp);
|
||||
@@ -385,7 +414,7 @@ pfk_egress_if(void)
|
||||
struct pfk_ifnet *
|
||||
pfk_if_first(void)
|
||||
{
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
|
||||
return pfk_if_next_from(&id);
|
||||
}
|
||||
@@ -407,7 +436,7 @@ pfk_if_next_from(int *idp)
|
||||
struct pfk_ifnet *
|
||||
pfk_if_next(struct pfk_ifnet *ifp)
|
||||
{
|
||||
int id = ifp ? ifp->if_index : -1;
|
||||
int id = ifp ? ifp->if_index : 0;
|
||||
|
||||
return pfk_if_next_from(&id);
|
||||
}
|
||||
@@ -418,9 +447,15 @@ pfk_if_init(void)
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
pfk_if_wq = alloc_ordered_workqueue("pfk_if", 0);
|
||||
if (!pfk_if_wq)
|
||||
return -ENOMEM;
|
||||
err = register_netdevice_notifier(&pfk_netdev_nb);
|
||||
if (err)
|
||||
if (err) {
|
||||
destroy_workqueue(pfk_if_wq);
|
||||
pfk_if_wq = NULL;
|
||||
return err;
|
||||
}
|
||||
/* populate existing devices */
|
||||
rtnl_lock();
|
||||
for_each_netdev(&init_net, dev) {
|
||||
@@ -449,8 +484,13 @@ pfk_if_init(void)
|
||||
void
|
||||
pfk_if_exit(void)
|
||||
{
|
||||
if (pfk_if_wq) {
|
||||
flush_workqueue(pfk_if_wq);
|
||||
destroy_workqueue(pfk_if_wq);
|
||||
pfk_if_wq = NULL;
|
||||
}
|
||||
struct pfk_ifnet *ifp;
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
unsigned long flags;
|
||||
|
||||
unregister_netdevice_notifier(&pfk_netdev_nb);
|
||||
@@ -459,7 +499,7 @@ pfk_if_exit(void)
|
||||
ifp->alive = 0;
|
||||
idr_remove(&pfk_ifnet_idr, id);
|
||||
kfree(ifp);
|
||||
id = -1;
|
||||
id = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
|
||||
}
|
||||
+44
-10
@@ -100,27 +100,61 @@ taskq_create(const char *name, unsigned int nthreads, int ipl,
|
||||
/* ---------------- NET_LOCK ---------------- */
|
||||
static DECLARE_RWSEM(pfk_net_sem);
|
||||
static atomic_t pfk_net_readers = ATOMIC_INIT(0);
|
||||
static atomic_t pfk_net_owner = ATOMIC_INIT(0); /* task pid holding write */
|
||||
|
||||
/*
|
||||
* PFLINUX: OpenBSD's NET_LOCK nesting is friendly for the holder (e.g.
|
||||
* pfioctl takes NET_LOCK and pf_status_read() does too). Linux rw_semaphores
|
||||
* are not recursive, so track the holder task + depth: nested lock/unlock by
|
||||
* the same task balance without touching the rwsem. (Tracking was by task
|
||||
* pointer on purpose: pids can be recycled.)
|
||||
*/
|
||||
static DEFINE_RAW_SPINLOCK(pfk_net_owner_lock);
|
||||
static struct task_struct *pfk_net_owner;
|
||||
static int pfk_net_depth;
|
||||
|
||||
void pfk_net_lock_init(void) { }
|
||||
|
||||
void
|
||||
pfk_net_lock(void)
|
||||
{
|
||||
/* Re-entrant for the holder: OpenBSD's NET_LOCK is friendly with
|
||||
* nested calls in the same path (e.g. our netdev notifier firing
|
||||
* while pfioctl holds the lock). */
|
||||
if (atomic_read(&pfk_net_owner) == current->pid)
|
||||
unsigned long flags;
|
||||
bool nested;
|
||||
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
nested = (pfk_net_owner == current);
|
||||
if (nested)
|
||||
pfk_net_depth++;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
if (nested)
|
||||
return;
|
||||
down_write(&pfk_net_sem);
|
||||
atomic_set(&pfk_net_owner, current->pid);
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
pfk_net_owner = current;
|
||||
pfk_net_depth = 1;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
}
|
||||
|
||||
void
|
||||
pfk_net_unlock(void)
|
||||
{
|
||||
if (atomic_read(&pfk_net_owner) != current->pid)
|
||||
return; /* nested no-op for non-holders */
|
||||
atomic_set(&pfk_net_owner, 0);
|
||||
up_write(&pfk_net_sem);
|
||||
unsigned long flags;
|
||||
int depth;
|
||||
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
if (pfk_net_owner != current) {
|
||||
/* not the holder: unbalanced unlock — ignore loudly */
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
depth = --pfk_net_depth;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
if (depth == 0) {
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
pfk_net_owner = NULL;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
up_write(&pfk_net_sem);
|
||||
}
|
||||
}
|
||||
void pfk_net_read_enter(void) { down_read(&pfk_net_sem);
|
||||
atomic_inc(&pfk_net_readers); }
|
||||
|
||||
Reference in new issue
Block a user