Module Name:    src
Committed By:   ozaki-r
Date:           Tue Jan 10 05:40:59 UTC 2017

Modified Files:
        src/sys/net: if_ethersubr.c

Log Message:
Replace adaptive mutex for ethercom with spin one

Unfortunately even wm(4) doesn't allow adaptive mutex because wm(4)
tries to hold it with holding its own spin mutex.


To generate a diff of this commit:
cvs rdiff -u -r1.232 -r1.233 src/sys/net/if_ethersubr.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_ethersubr.c
diff -u src/sys/net/if_ethersubr.c:1.232 src/sys/net/if_ethersubr.c:1.233
--- src/sys/net/if_ethersubr.c:1.232	Sat Dec 31 15:07:02 2016
+++ src/sys/net/if_ethersubr.c	Tue Jan 10 05:40:59 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_ethersubr.c,v 1.232 2016/12/31 15:07:02 ozaki-r Exp $	*/
+/*	$NetBSD: if_ethersubr.c,v 1.233 2017/01/10 05:40:59 ozaki-r Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.232 2016/12/31 15:07:02 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_ethersubr.c,v 1.233 2017/01/10 05:40:59 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -967,7 +967,7 @@ ether_ifattach(struct ifnet *ifp, const 
 		if_set_sadl(ifp, lla, ETHER_ADDR_LEN, !ETHER_IS_LOCAL(lla));
 
 	LIST_INIT(&ec->ec_multiaddrs);
-	ec->ec_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE);
+	ec->ec_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
 	ifp->if_broadcastaddr = etherbroadcastaddr;
 	bpf_attach(ifp, DLT_EN10MB, sizeof(struct ether_header));
 #ifdef MBUFTRACE
@@ -1535,13 +1535,15 @@ static int
 ether_multicast_sysctl(SYSCTLFN_ARGS)
 {
 	struct ether_multi *enm;
-	struct ether_multi_sysctl addr;
 	struct ifnet *ifp;
 	struct ethercom *ec;
 	int error = 0;
 	size_t written;
 	struct psref psref;
 	int bound, s;
+	unsigned int multicnt;
+	struct ether_multi_sysctl *addrs;
+	int i;
 
 	if (namelen != 1)
 		return EINVAL;
@@ -1561,30 +1563,55 @@ ether_multicast_sysctl(SYSCTLFN_ARGS)
 
 	if (oldp == NULL) {
 		if_put(ifp, &psref);
-		*oldlenp = ec->ec_multicnt * sizeof(addr);
+		*oldlenp = ec->ec_multicnt * sizeof(*addrs);
 		goto out;
 	}
 
-	memset(&addr, 0, sizeof(addr));
-	error = 0;
-	written = 0;
+	/*
+	 * ec->ec_lock is a spin mutex so we cannot call sysctl_copyout, which
+	 * is sleepable, with holding it. Copy data to a local buffer first
+	 * with holding it and then call sysctl_copyout without holding it.
+	 */
+retry:
+	multicnt = ec->ec_multicnt;
+	addrs = kmem_alloc(sizeof(*addrs) * multicnt, KM_SLEEP);
 
 	s = splnet();
 	mutex_enter(ec->ec_lock);
+	if (multicnt < ec->ec_multicnt) {
+		/* The number of multicast addresses have increased */
+		mutex_exit(ec->ec_lock);
+		splx(s);
+		kmem_free(addrs, sizeof(*addrs) * multicnt);
+		goto retry;
+	}
+
+	i = 0;
 	LIST_FOREACH(enm, &ec->ec_multiaddrs, enm_list) {
-		if (written + sizeof(addr) > *oldlenp)
+		struct ether_multi_sysctl *addr = &addrs[i];
+		addr->enm_refcount = enm->enm_refcount;
+		memcpy(addr->enm_addrlo, enm->enm_addrlo, ETHER_ADDR_LEN);
+		memcpy(addr->enm_addrhi, enm->enm_addrhi, ETHER_ADDR_LEN);
+		i++;
+	}
+	mutex_exit(ec->ec_lock);
+	splx(s);
+
+	error = 0;
+	written = 0;
+	for (i = 0; i < multicnt; i++) {
+		struct ether_multi_sysctl *addr = &addrs[i];
+
+		if (written + sizeof(*addr) > *oldlenp)
 			break;
-		addr.enm_refcount = enm->enm_refcount;
-		memcpy(addr.enm_addrlo, enm->enm_addrlo, ETHER_ADDR_LEN);
-		memcpy(addr.enm_addrhi, enm->enm_addrhi, ETHER_ADDR_LEN);
-		error = sysctl_copyout(l, &addr, oldp, sizeof(addr));
+		error = sysctl_copyout(l, addr, oldp, sizeof(*addr));
 		if (error)
 			break;
-		written += sizeof(addr);
-		oldp = (char *)oldp + sizeof(addr);
+		written += sizeof(*addr);
+		oldp = (char *)oldp + sizeof(*addr);
 	}
-	mutex_exit(ec->ec_lock);
-	splx(s);
+	kmem_free(addrs, sizeof(*addrs) * multicnt);
+
 	if_put(ifp, &psref);
 
 	*oldlenp = written;

Reply via email to