Hello,

mikeb@ found a fundamental problem in my earlier patch. The ip_send() function
was using `softnettq` (softnet task queue) to dispatch packet via ip*_output(). 
Doing so it's risky business as ip*_output() is not unlocked yet.

So new patch version introduces a new task: ipsendtq. The ipsendtq will be
running at IPL_SOFTNET and will be servicing both protocols (IPv4, IPv6).

The calls to ip*_output() functions will be guarded by KERNEL_LOCK().
diff below illustrates the change against earlier patch version. New
version of complete patch is appended to email.

diff -r 259e5a3a782a src/sys/netinet/ip_input.c
--- a/src/sys/netinet/ip_input.c        Thu Dec 03 09:40:12 2015 +0100
+++ b/src/sys/netinet/ip_input.c        Thu Dec 03 10:52:47 2015 +0100
@@ -122,6 +122,7 @@
 
 struct ipstat ipstat;
 
+struct taskq *ipsendtq;
 static struct mbuf_queue       ipsend_mq;
 
 void   ip_ours(struct mbuf *);
@@ -191,6 +192,7 @@
        strlcpy(ipsec_def_comp, IPSEC_DEFAULT_DEF_COMP, sizeof(ipsec_def_comp));
 
        mq_init(&ipsend_mq, 64, IPL_SOFTNET);
+       ipsendtq = taskq_create("ipsend", 1, IPL_SOFTNET, TASKQ_CANTSLEEP);
 }
 
 struct route ipforward_rt;
@@ -1754,13 +1756,16 @@
 {
        struct mbuf *m;
 
-       while ((m = mq_dequeue((struct mbuf_queue *)cx)) != NULL)
+       while ((m = mq_dequeue((struct mbuf_queue *)cx)) != NULL) {
+               KERNEL_LOCK();
                ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
+               KERNEL_UNLOCK();
+       }
 }
 
regards
sasha

--------8<---------------8<---------------8<------------------8<--------

Index: net/if.h
===================================================================
RCS file: /cvs/src/sys/net/if.h,v
retrieving revision 1.174
diff -u -p -r1.174 if.h
--- net/if.h    3 Dec 2015 12:22:51 -0000       1.174
+++ net/if.h    3 Dec 2015 15:16:00 -0000
@@ -484,4 +484,7 @@ int if_setlladdr(struct ifnet *, const u
 
 #endif /* __BSD_VISIBLE */
 
+#ifdef _KERNEL
+extern struct taskq *softnettq;
+#endif /* _KERNEL */
 #endif /* _NET_IF_H_ */
Index: net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.955
diff -u -p -r1.955 pf.c
--- net/pf.c    3 Dec 2015 09:49:15 -0000       1.955
+++ net/pf.c    3 Dec 2015 15:16:00 -0000
@@ -2424,11 +2424,11 @@ pf_send_tcp(const struct pf_rule *r, sa_
 
        switch (af) {
        case AF_INET:
-               ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
+               ip_send(m);
                break;
 #ifdef INET6
        case AF_INET6:
-               ip6_output(m, NULL, NULL, 0, NULL, NULL);
+               ip6_send(m);
                break;
 #endif /* INET6 */
        }
Index: netinet/ip_icmp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.149
diff -u -p -r1.149 ip_icmp.c
--- netinet/ip_icmp.c   2 Dec 2015 16:35:53 -0000       1.149
+++ netinet/ip_icmp.c   3 Dec 2015 15:16:01 -0000
@@ -854,7 +854,10 @@ icmp_send(struct mbuf *m, struct mbuf *o
                printf("icmp_send dst %s src %s\n", dst, src);
        }
 #endif
-       ip_output(m, opts, NULL, 0, NULL, NULL, 0);
+       if (opts != NULL)
+               m = ip_insertoptions(m, opts, &hlen);
+
+       ip_send(m);
 }
 
 u_int32_t
Index: netinet/ip_input.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.263
diff -u -p -r1.263 ip_input.c
--- netinet/ip_input.c  2 Dec 2015 13:29:26 -0000       1.263
+++ netinet/ip_input.c  3 Dec 2015 15:16:01 -0000
@@ -44,6 +44,7 @@
 #include <sys/socketvar.h>
 #include <sys/sysctl.h>
 #include <sys/pool.h>
+#include <sys/task.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
@@ -121,11 +122,16 @@ struct pool ipq_pool;
 
 struct ipstat ipstat;
 
+struct taskq *ipsendtq;
+static struct mbuf_queue       ipsend_mq;
+
 void   ip_ours(struct mbuf *);
 int    ip_dooptions(struct mbuf *, struct ifnet *);
 int    in_ouraddr(struct mbuf *, struct ifnet *, struct in_addr);
 void   ip_forward(struct mbuf *, struct ifnet *, int);
 
+static void ip_send_dispatch(void *);
+static struct task ipsend_task = TASK_INITIALIZER(ip_send_dispatch, 
&ipsend_mq);
 /*
  * Used to save the IP options in case a protocol wants to respond
  * to an incoming packet over the same route if the packet got here
@@ -184,6 +190,9 @@ ip_init(void)
        strlcpy(ipsec_def_enc, IPSEC_DEFAULT_DEF_ENC, sizeof(ipsec_def_enc));
        strlcpy(ipsec_def_auth, IPSEC_DEFAULT_DEF_AUTH, sizeof(ipsec_def_auth));
        strlcpy(ipsec_def_comp, IPSEC_DEFAULT_DEF_COMP, sizeof(ipsec_def_comp));
+
+       mq_init(&ipsend_mq, 64, IPL_SOFTNET);
+       ipsendtq = taskq_create("ipsend", 1, IPL_SOFTNET, TASKQ_CANTSLEEP);
 }
 
 struct route ipforward_rt;
@@ -1742,3 +1751,21 @@ ip_savecontrol(struct inpcb *inp, struct
        }
 }
 
+static void
+ip_send_dispatch(void *cx)
+{
+       struct mbuf *m;
+
+       while ((m = mq_dequeue((struct mbuf_queue *)cx)) != NULL) {
+               KERNEL_LOCK();
+               ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
+               KERNEL_UNLOCK();
+       }
+}
+
+void
+ip_send(struct mbuf *m)
+{
+       mq_enqueue(&ipsend_mq, m);
+       task_add(ipsendtq, &ipsend_task);
+}
Index: netinet/ip_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_var.h,v
retrieving revision 1.60
diff -u -p -r1.60 ip_var.h
--- netinet/ip_var.h    16 Jul 2015 21:14:21 -0000      1.60
+++ netinet/ip_var.h    3 Dec 2015 15:16:01 -0000
@@ -180,6 +180,8 @@ void         ip_freef(struct ipq *);
 void    ip_freemoptions(struct ip_moptions *);
 int     ip_getmoptions(int, struct ip_moptions *, struct mbuf **);
 void    ip_init(void);
+struct mbuf*
+        ip_insertoptions(struct mbuf *, struct mbuf *, int *);
 int     ip_mforward(struct mbuf *, struct ifnet *);
 int     ip_optcopy(struct ip *, struct ip *);
 int     ip_output(struct mbuf *, struct mbuf *, struct route *, int,
@@ -191,6 +193,7 @@ struct in_ifaddr *
         ip_rtaddr(struct in_addr, u_int);
 u_int16_t
         ip_randomid(void);
+void    ip_send(struct mbuf *);
 int     ip_setmoptions(int, struct ip_moptions **, struct mbuf *, u_int);
 void    ip_slowtimo(void);
 struct mbuf *
@@ -207,5 +210,7 @@ void         rip_input(struct mbuf *, ...);
 int     rip_output(struct mbuf *, ...);
 int     rip_usrreq(struct socket *,
            int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *);
+
+extern struct taskq *ipsendtq;
 #endif /* _KERNEL */
 #endif /* _NETINET_IP_VAR_H_ */
Index: netinet6/icmp6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.181
diff -u -p -r1.181 icmp6.c
--- netinet6/icmp6.c    2 Dec 2015 16:35:53 -0000       1.181
+++ netinet6/icmp6.c    3 Dec 2015 15:16:01 -0000
@@ -1302,7 +1302,7 @@ icmp6_reflect(struct mbuf *m, size_t off
 #if NPF > 0
        pf_pkt_addr_changed(m);
 #endif
-       ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL);
+       ip6_send(m);
        return;
 
  bad:
Index: netinet6/ip6_input.c
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.151
diff -u -p -r1.151 ip6_input.c
--- netinet6/ip6_input.c        11 Nov 2015 10:23:23 -0000      1.151
+++ netinet6/ip6_input.c        3 Dec 2015 15:16:01 -0000
@@ -77,6 +77,7 @@
 #include <sys/timeout.h>
 #include <sys/kernel.h>
 #include <sys/syslog.h>
+#include <sys/task.h>
 
 #include <net/if.h>
 #include <net/if_var.h>
@@ -89,6 +90,7 @@
 #include <netinet/ip.h>
 
 #include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
 #include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
@@ -123,6 +125,11 @@ int ip6_check_rh0hdr(struct mbuf *, int 
 int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *);
 struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int);
 
+static struct mbuf_queue       ip6send_mq;
+
+static void ip6_send_dispatch(void *);
+static struct task ip6send_task = TASK_INITIALIZER(ip6_send_dispatch, 
&ip6send_mq);
+
 /*
  * IP6 initialization: fill in IP6 protocol switch table.
  * All protocols not implemented in kernel go to raw IP6 protocol handler.
@@ -149,6 +156,8 @@ ip6_init(void)
        nd6_init();
        frag6_init();
        ip6_init2((void *)0);
+
+       mq_init(&ip6send_mq, 64, IPL_SOFTNET);
 }
 
 void
@@ -1430,4 +1439,23 @@ ip6_sysctl(int *name, u_int namelen, voi
                return (EOPNOTSUPP);
        }
        /* NOTREACHED */
+}
+
+static void
+ip6_send_dispatch(void *cx)
+{
+       struct mbuf *m;
+
+       while ((m = mq_dequeue((struct mbuf_queue *)cx)) != NULL) {
+               KERNEL_LOCK();
+               ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL);
+               KERNEL_UNLOCK();
+       }
+}
+
+void
+ip6_send(struct mbuf *m)
+{
+       mq_enqueue(&ip6send_mq, m);
+       task_add(ipsendtq, &ip6send_task);
 }
Index: netinet6/ip6_var.h
===================================================================
RCS file: /cvs/src/sys/netinet6/ip6_var.h,v
retrieving revision 1.56
diff -u -p -r1.56 ip6_var.h
--- netinet6/ip6_var.h  25 Oct 2015 14:43:06 -0000      1.56
+++ netinet6/ip6_var.h  3 Dec 2015 15:16:01 -0000
@@ -275,6 +275,7 @@ int ip6_setpktopts(struct mbuf *, struct
 void   ip6_clearpktopts(struct ip6_pktopts *, int);
 void   ip6_randomid_init(void);
 u_int32_t ip6_randomid(void);
+void    ip6_send(struct mbuf *);
 
 int    route6_input(struct mbuf **, int *, int);
 

Reply via email to