Hi,

Ethernet drivers connected via USB might sleep when their multicast
group filter is modified.  Unfortunately this happens from softclock
or softnet interrupt when IPv6 decides to unconfigure its addresses
automatically.

An obvious solution is to use a work queue.  I have put the workq
storage into struct in6_multi_mship.  That requires to include
sys/workq.h before compiling this struct.

Is it a good idea to include sys/workq.h directly form netinet6/in6_var.h?
An alternative is to include sys/workq.h from 50 kernel .c files.

What is the right way?

Note that netinet6/in6_var.h is included from user space and even
from some ports.

bluhm

Index: netinet6/in6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6.c,v
retrieving revision 1.120
diff -u -p -u -p -r1.120 in6.c
--- netinet6/in6.c      17 Oct 2013 16:27:45 -0000      1.120
+++ netinet6/in6.c      18 Oct 2013 10:08:27 -0000
@@ -124,6 +124,7 @@ int in6_lifaddr_ioctl(struct socket *, u
 int in6_ifinit(struct ifnet *, struct in6_ifaddr *, int);
 void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
 void in6_ifloop_request(int, struct ifaddr *);
+void in6_leavegroup_task(void *, void *);
 
 const struct sockaddr_in6 sa6_any = {
        sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0
@@ -1816,9 +1817,20 @@ in6_leavegroup(struct in6_multi_mship *i
 {
 
        if (imm->i6mm_maddr)
-               in6_delmulti(imm->i6mm_maddr);
-       free(imm,  M_IPMADDR);
+               workq_queue_task(NULL, &imm->wqt, 0, in6_leavegroup_task, imm,
+                   NULL);
+       else 
+               free(imm,  M_IPMADDR);
        return 0;
+}
+
+void
+in6_leavegroup_task(void *arg1, void *arg2)
+{
+       struct in6_multi_mship *imm = arg1;
+
+       in6_delmulti(imm->i6mm_maddr);
+       free(imm,  M_IPMADDR);
 }
 
 /*
Index: netinet6/in6_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_var.h,v
retrieving revision 1.42
diff -u -p -u -p -r1.42 in6_var.h
--- netinet6/in6_var.h  14 Oct 2013 11:07:42 -0000      1.42
+++ netinet6/in6_var.h  18 Oct 2013 10:09:52 -0000
@@ -64,6 +64,8 @@
 #ifndef _NETINET6_IN6_VAR_H_
 #define _NETINET6_IN6_VAR_H_
 
+#include <sys/workq.h>
+
 /*
  * Interface address, Internet version.  One of these structures
  * is allocated for each interface with an Internet address.
@@ -486,6 +488,7 @@ do {                                                        
                \
  * belongs to.
  */
 struct in6_multi_mship {
+       struct  workq_task wqt;         /* Allow network driver to sleep */
        struct  in6_multi *i6mm_maddr;  /* Multicast address pointer */
        LIST_ENTRY(in6_multi_mship) i6mm_chain;  /* multicast options chain */
 };

Reply via email to