this is tx mitigation again, ie, defer calling an interfaces start
routine until at least 4 packets are queued, or a task fires.
the task firing is a problem for things like gif or vxlan that encap
a packet in ip and send it through the ip stack again. the ip stack
expects NET_RLOCK to be held. that is implicitly true when sending
out of the network stack, but not when the bundle task fires.
this has the bundle tasks take the network read lock on behalf of
the start routines, like the stack does. this avoids having to patch
every driver to cope with this.
tests?
Index: ifq.c
===================================================================
RCS file: /cvs/src/sys/net/ifq.c,v
retrieving revision 1.21
diff -u -p -r1.21 ifq.c
--- ifq.c 4 Jan 2018 11:02:57 -0000 1.21
+++ ifq.c 8 Jan 2018 01:09:05 -0000
@@ -70,9 +70,16 @@ struct priq {
void ifq_start_task(void *);
void ifq_restart_task(void *);
void ifq_barrier_task(void *);
+void ifq_bundle_task(void *);
#define TASK_ONQUEUE 0x1
+static inline void
+ifq_run_start(struct ifqueue *ifq)
+{
+ ifq_serialize(ifq, &ifq->ifq_start);
+}
+
void
ifq_serialize(struct ifqueue *ifq, struct task *t)
{
@@ -114,6 +121,16 @@ ifq_is_serialized(struct ifqueue *ifq)
}
void
+ifq_start(struct ifqueue *ifq)
+{
+ if (ifq_len(ifq) >= min(4, ifq->ifq_maxlen)) {
+ task_del(ifq->ifq_softnet, &ifq->ifq_bundle);
+ ifq_run_start(ifq);
+ } else
+ task_add(ifq->ifq_softnet, &ifq->ifq_bundle);
+}
+
+void
ifq_start_task(void *p)
{
struct ifqueue *ifq = p;
@@ -137,11 +154,36 @@ ifq_restart_task(void *p)
}
void
+ifq_bundle_task(void *p)
+{
+ struct ifqueue *ifq = p;
+ int s;
+
+ NET_RLOCK();
+ s = splnet();
+ ifq_run_start(ifq);
+ splx(s);
+ NET_RUNLOCK();
+}
+
+void
ifq_barrier(struct ifqueue *ifq)
{
struct cond c = COND_INITIALIZER();
struct task t = TASK_INITIALIZER(ifq_barrier_task, &c);
+ if (!task_del(ifq->ifq_softnet, &ifq->ifq_bundle)) {
+ int netlocked = (rw_status(&netlock) == RW_WRITE);
+
+ if (netlocked) /* XXXSMP breaks atomicity */
+ NET_UNLOCK();
+
+ taskq_barrier(ifq->ifq_softnet);
+
+ if (netlocked)
+ NET_LOCK();
+ }
+
if (ifq->ifq_serializer == NULL)
return;
@@ -166,6 +208,7 @@ void
ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx)
{
ifq->ifq_if = ifp;
+ ifq->ifq_softnet = net_tq(ifp->if_index);
ifq->ifq_softc = NULL;
mtx_init(&ifq->ifq_mtx, IPL_NET);
@@ -187,6 +230,7 @@ ifq_init(struct ifqueue *ifq, struct ifn
mtx_init(&ifq->ifq_task_mtx, IPL_NET);
TAILQ_INIT(&ifq->ifq_task_list);
ifq->ifq_serializer = NULL;
+ task_set(&ifq->ifq_bundle, ifq_bundle_task, ifq);
task_set(&ifq->ifq_start, ifq_start_task, ifq);
task_set(&ifq->ifq_restart, ifq_restart_task, ifq);
@@ -237,6 +281,8 @@ void
ifq_destroy(struct ifqueue *ifq)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+
+ ifq_barrier(ifq); /* ensure nothing is running with the ifq */
/* don't need to lock because this is the last use of the ifq */
Index: ifq.h
===================================================================
RCS file: /cvs/src/sys/net/ifq.h,v
retrieving revision 1.20
diff -u -p -r1.20 ifq.h
--- ifq.h 4 Jan 2018 11:02:57 -0000 1.20
+++ ifq.h 8 Jan 2018 01:09:05 -0000
@@ -25,6 +25,7 @@ struct ifq_ops;
struct ifqueue {
struct ifnet *ifq_if;
+ struct taskq *ifq_softnet;
union {
void *_ifq_softc;
/*
@@ -57,6 +58,7 @@ struct ifqueue {
struct mutex ifq_task_mtx;
struct task_list ifq_task_list;
void *ifq_serializer;
+ struct task ifq_bundle;
/* work to be serialised */
struct task ifq_start;
@@ -405,6 +407,7 @@ void ifq_attach(struct ifqueue *, cons
void ifq_destroy(struct ifqueue *);
void ifq_add_data(struct ifqueue *, struct if_data *);
int ifq_enqueue(struct ifqueue *, struct mbuf *);
+void ifq_start(struct ifqueue *);
struct mbuf *ifq_deq_begin(struct ifqueue *);
void ifq_deq_commit(struct ifqueue *, struct mbuf *);
void ifq_deq_rollback(struct ifqueue *, struct mbuf *);
@@ -438,12 +441,6 @@ static inline unsigned int
ifq_is_oactive(struct ifqueue *ifq)
{
return (ifq->ifq_oactive);
-}
-
-static inline void
-ifq_start(struct ifqueue *ifq)
-{
- ifq_serialize(ifq, &ifq->ifq_start);
}
static inline void