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;