Module Name:    src
Committed By:   dyoung
Date:           Wed Oct 19 01:34:37 UTC 2011

Modified Files:
        src/sys/net: if.c if.h

Log Message:
Start to untangle the ifnet ioctls mess.

Add ifnet functions, if_mcast_op(), if_flags_set(), and if_addr_init()
for adding/deleting multicast addresses, modifying the if_flags,
and initializing local/remote addresses.  Make ifpromisc() use
if_flags_set().  Protocols and network drivers should use these
instead of ifp->if_ioctl() calls.  Subsequent commits will
replace ifp->if_ioctl(SIOCADDMULTI| SIOCDELMULTI| SIOCSIFDSTADDR|
SIOCINITIFADDR| SIOCSIFFLAGS) calls with calls to the new functions.

Use a mutex(9) to synchronize ifp->if_ioctl() calls originating in
userland.  Also synchronize ifp->if_ioctl() calls with ifnet detachment
and reclamation.


To generate a diff of this commit:
cvs rdiff -u -r1.251 -r1.252 src/sys/net/if.c
cvs rdiff -u -r1.151 -r1.152 src/sys/net/if.h

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.c
diff -u src/sys/net/if.c:1.251 src/sys/net/if.c:1.252
--- src/sys/net/if.c:1.251	Fri Aug 12 22:09:36 2011
+++ src/sys/net/if.c	Wed Oct 19 01:34:37 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.c,v 1.251 2011/08/12 22:09:36 dyoung Exp $	*/
+/*	$NetBSD: if.c,v 1.252 2011/10/19 01:34:37 dyoung Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001, 2008 The NetBSD Foundation, Inc.
@@ -90,7 +90,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.251 2011/08/12 22:09:36 dyoung Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if.c,v 1.252 2011/10/19 01:34:37 dyoung Exp $");
 
 #include "opt_inet.h"
 
@@ -168,6 +168,8 @@ struct pfil_head if_pfil;	/* packet filt
 
 static kauth_listener_t if_listener;
 
+static int ifioctl_attach(struct ifnet *);
+static void ifioctl_detach(struct ifnet *);
 static void if_detach_queues(struct ifnet *, struct ifqueue *);
 static void sysctl_sndq_setup(struct sysctllog **, const char *,
     struct ifaltq *);
@@ -292,6 +294,7 @@ int
 if_nullioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
 
+	cv_signal(&ifp->if_ioctl_emptied);
 	return ENXIO;
 }
 
@@ -497,8 +500,8 @@ if_attach(struct ifnet *ifp)
 	}
 	TAILQ_INIT(&ifp->if_addrlist);
 	TAILQ_INSERT_TAIL(&ifnet, ifp, if_list);
-	if (ifp->if_ioctl == NULL)
-		ifp->if_ioctl = ifioctl_common;
+
+	ifioctl_attach(ifp);	/* XXX ifioctl_attach can fail! */ 
 
 	mutex_enter(&index_gen_mtx);
 	ifp->if_index_gen = index_gen++;
@@ -842,6 +845,8 @@ again:
 
 	TAILQ_REMOVE(&ifnet, ifp, if_list);
 
+	ifioctl_detach(ifp);
+
 	/*
 	 * remove packets that came from ifp, from software interrupt queues.
 	 */
@@ -1403,8 +1408,7 @@ int
 ifpromisc(struct ifnet *ifp, int pswitch)
 {
 	int pcount, ret;
-	short flags;
-	struct ifreq ifr;
+	short flags, nflags;
 
 	pcount = ifp->if_pcount;
 	flags = ifp->if_flags;
@@ -1416,29 +1420,26 @@ ifpromisc(struct ifnet *ifp, int pswitch
 		 */
 		if (ifp->if_pcount++ != 0)
 			return 0;
-		ifp->if_flags |= IFF_PROMISC;
-		if ((ifp->if_flags & IFF_UP) == 0)
+		nflags = ifp->if_flags | IFF_PROMISC;
+		if ((nflags & IFF_UP) == 0)
 			return 0;
 	} else {
 		if (--ifp->if_pcount > 0)
 			return 0;
-		ifp->if_flags &= ~IFF_PROMISC;
+		nflags = ifp->if_flags & ~IFF_PROMISC;
 		/*
 		 * If the device is not configured up, we should not need to
 		 * turn off promiscuous mode (device should have turned it
 		 * off when interface went down; and will look at IFF_PROMISC
 		 * again next time interface comes up).
 		 */
-		if ((ifp->if_flags & IFF_UP) == 0)
+		if ((nflags & IFF_UP) == 0)
 			return 0;
 	}
-	memset(&ifr, 0, sizeof(ifr));
-	ifr.ifr_flags = ifp->if_flags;
-	ret = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, &ifr);
+	ret = if_flags_set(ifp, nflags);
 	/* Restore interface state if not successful. */
 	if (ret != 0) {
 		ifp->if_pcount = pcount;
-		ifp->if_flags = flags;
 	}
 	return ret;
 }
@@ -1801,11 +1802,15 @@ ifioctl(struct socket *so, u_long cmd, v
 
 	oif_flags = ifp->if_flags;
 
+	uint64_t *nenter = percpu_getref(ifp->if_ioctl_nenter);
+	(*nenter)++;
+	percpu_putref(ifp->if_ioctl_nenter);
+	mutex_enter(&ifp->if_ioctl_lock);
 	error = (*ifp->if_ioctl)(ifp, cmd, data);
 	if (error != ENOTTY)
 		;
 	else if (so->so_proto == NULL)
-		return EOPNOTSUPP;
+		error = EOPNOTSUPP;
 	else {
 #ifdef COMPAT_OSOCK
 		error = compat_ifioctl(so, ocmd, cmd, data, l);
@@ -1830,9 +1835,55 @@ ifioctl(struct socket *so, u_long cmd, v
 		ifreqn2o(oifr, ifr);
 #endif
 
+	ifp->if_ioctl_nexit++;
+	mutex_exit(&ifp->if_ioctl_lock);
 	return error;
 }
 
+static void
+ifioctl_sum(void *p, void *arg, struct cpu_info *ci)
+{
+	uint64_t *sum = arg, *nenter = p;
+
+	*sum += *nenter;
+}
+
+static uint64_t
+ifioctl_entrances(struct ifnet *ifp)
+{
+	uint64_t sum = 0;
+
+	percpu_foreach(ifp->if_ioctl_nenter, ifioctl_sum, &sum);
+
+	return sum;
+}
+
+static int
+ifioctl_attach(struct ifnet *ifp)
+{
+	if (ifp->if_ioctl == NULL)
+		ifp->if_ioctl = ifioctl_common;
+
+	ifp->if_ioctl_nenter = percpu_alloc(sizeof(uint64_t));
+	if (ifp->if_ioctl_nenter == NULL)
+		return ENOMEM;
+
+	mutex_init(&ifp->if_ioctl_lock, MUTEX_DEFAULT, IPL_NONE);
+	cv_init(&ifp->if_ioctl_emptied, ifp->if_xname);
+
+	return 0;
+}
+
+static void
+ifioctl_detach(struct ifnet *ifp)
+{
+	mutex_enter(&ifp->if_ioctl_lock);
+	ifp->if_ioctl = if_nullioctl;
+	while (ifioctl_entrances(ifp) != ifp->if_ioctl_nexit)
+		cv_wait(&ifp->if_ioctl_emptied, &ifp->if_ioctl_lock);
+	mutex_exit(&ifp->if_ioctl_lock);
+}
+
 /*
  * Return interface configuration
  * of system.  List may be used
@@ -2012,6 +2063,52 @@ ifq_enqueue2(struct ifnet *ifp, struct i
 	return 0;
 }
 
+int
+if_addr_init(ifnet_t *ifp, struct ifaddr *ifa, const bool src)
+{
+	int rc;
+
+	if (ifp->if_initaddr != NULL)
+		rc = (*ifp->if_initaddr)(ifp, ifa, src);
+	else if (src ||
+	         (rc = (*ifp->if_ioctl)(ifp, SIOCSIFDSTADDR, ifa)) == ENOTTY)
+		rc = (*ifp->if_ioctl)(ifp, SIOCINITIFADDR, ifa);
+
+	return rc;
+}
+
+int
+if_flags_set(ifnet_t *ifp, const short flags)
+{
+	int rc;
+	struct ifreq ifr;
+
+	if (ifp->if_setflags != NULL)
+		rc = (*ifp->if_setflags)(ifp, flags);
+	else {
+		memset(&ifr, 0, sizeof(ifr));
+		ifr.ifr_flags = flags;
+		rc = (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, &ifr);
+	}
+
+	return rc;
+}
+
+int
+if_mcast_op(ifnet_t *ifp, const unsigned long cmd, const struct sockaddr *sa)
+{
+	int rc;
+	struct ifreq ifr;
+
+	if (ifp->if_mcastop != NULL)
+		rc = (*ifp->if_mcastop)(ifp, cmd, sa);
+	else {
+		ifreq_setaddr(cmd, &ifr, sa);
+		rc = (*ifp->if_ioctl)(ifp, cmd, &ifr);
+	}
+
+	return rc;
+}
 
 static void
 sysctl_sndq_setup(struct sysctllog **clog, const char *ifname,

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.151 src/sys/net/if.h:1.152
--- src/sys/net/if.h:1.151	Fri Aug 12 22:09:17 2011
+++ src/sys/net/if.h	Wed Oct 19 01:34:37 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.151 2011/08/12 22:09:17 dyoung Exp $	*/
+/*	$NetBSD: if.h,v 1.152 2011/10/19 01:34:37 dyoung Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -63,6 +63,10 @@
 #ifndef _NET_IF_H_
 #define _NET_IF_H_
 
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdbool.h>
+#endif
+
 #include <sys/featuretest.h>
 
 /*
@@ -75,6 +79,7 @@
 
 #include <sys/mutex.h>
 #include <sys/condvar.h>
+#include <sys/percpu.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
 #include <net/dlt.h>
@@ -296,6 +301,14 @@ typedef struct ifnet {
 					 * same, they are the same ifnet.
 					 */
 	struct sysctllog	*if_sysctl_log;
+	int (*if_initaddr)(struct ifnet *, struct ifaddr *, bool);
+	int (*if_mcastop)(struct ifnet *, const unsigned long,
+	    const struct sockaddr *);
+	int (*if_setflags)(struct ifnet *, const short);
+	kmutex_t if_ioctl_lock;
+	uint64_t if_ioctl_nexit;
+	percpu_t *if_ioctl_nenter;
+	kcondvar_t if_ioctl_emptied;
 } ifnet_t;
 
 #define	if_mtu		if_data.ifi_mtu
@@ -850,6 +863,9 @@ int	ifioctl(struct socket *, u_long, voi
 int	ifioctl_common(struct ifnet *, u_long, void *);
 int	ifpromisc(struct ifnet *, int);
 struct	ifnet *ifunit(const char *);
+int	if_addr_init(ifnet_t *, struct ifaddr *, bool);
+int	if_mcast_op(ifnet_t *, const unsigned long, const struct sockaddr *);
+int	if_flags_set(struct ifnet *, const short);
 
 void ifa_insert(struct ifnet *, struct ifaddr *);
 void ifa_remove(struct ifnet *, struct ifaddr *);

Reply via email to