summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 08:39:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 08:39:24 -0700
commit53ee7569ce8beb3fd3fc0817116c29298d72353f (patch)
treef3dcce10508c2126347e40b468fd6d3c3cc7006a /net
parent4d9dec4db2efbd7edb549bd02373995b67496983 (diff)
parent1b6e2ceb4745b5838cb94463131d19dbea6cf0e3 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (27 commits) bnx2x: allow device properly initialize after hotplug bnx2x: fix DMAE timeout according to hw specifications bnx2x: properly handle CFC DEL in cnic flow bnx2x: call dev_kfree_skb_any instead of dev_kfree_skb net: filter: move forward declarations to avoid compile warnings pktgen: refactor pg_init() code pktgen: use vzalloc_node() instead of vmalloc_node() + memset() net: skb_trim explicitely check the linearity instead of data_len ipv4: Give backtrace in ip_rt_bug(). net: avoid synchronize_rcu() in dev_deactivate_many net: remove synchronize_net() from netdev_set_master() rtnetlink: ignore NETDEV_RELEASE and NETDEV_JOIN event net: rename NETDEV_BONDING_DESLAVE to NETDEV_RELEASE bridge: call NETDEV_JOIN notifiers when add a slave netpoll: disable netpoll when enslave a device macvlan: Forward unicast frames in bridge mode to lowerdev net: Remove linux/prefetch.h include from linux/skbuff.h ipv4: Include linux/prefetch.h in fib_trie.c netlabel: Remove prefetches from list handlers. drivers/net: add prefetch header for prefetch users ... Fixed up prefetch parts: removed a few duplicate prefetch.h includes, fixed the location of the igb prefetch.h, took my version of the skbuff.h code without the extra parentheses etc.
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_if.c3
-rw-r--r--net/caif/caif_dev.c7
-rw-r--r--net/caif/caif_socket.c13
-rw-r--r--net/caif/cfcnfg.c44
-rw-r--r--net/caif/cfctrl.c44
-rw-r--r--net/caif/cfmuxl.c49
-rw-r--r--net/core/dev.c4
-rw-r--r--net/core/pktgen.c22
-rw-r--r--net/core/rtnetlink.c2
-rw-r--r--net/ipv4/route.c1
-rw-r--r--net/sched/sch_generic.c17
11 files changed, 134 insertions, 72 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 5dbdfdfc3a3..1bacca4cb67 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -147,6 +147,7 @@ static void del_nbp(struct net_bridge_port *p)
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
+ synchronize_net();
netdev_set_master(dev, NULL);
@@ -338,6 +339,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (IS_ERR(p))
return PTR_ERR(p);
+ call_netdevice_notifiers(NETDEV_JOIN, dev);
+
err = dev_set_promiscuity(dev, 1);
if (err)
goto put_back;
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 366ca0fb7a2..682c0fedf36 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -142,6 +142,7 @@ static int receive(struct sk_buff *skb, struct net_device *dev,
{
struct cfpkt *pkt;
struct caif_device_entry *caifd;
+ int err;
pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
@@ -159,7 +160,11 @@ static int receive(struct sk_buff *skb, struct net_device *dev,
caifd_hold(caifd);
rcu_read_unlock();
- caifd->layer.up->receive(caifd->layer.up, pkt);
+ err = caifd->layer.up->receive(caifd->layer.up, pkt);
+
+ /* For -EILSEQ the packet is not freed so so it now */
+ if (err == -EILSEQ)
+ cfpkt_destroy(pkt);
/* Release reference to stack upwards */
caifd_put(caifd);
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index b840395ced1..a9862808645 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -19,7 +19,7 @@
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/caif/caif_socket.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <net/caif/caif_layer.h>
@@ -816,6 +816,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
if (sk->sk_shutdown & SHUTDOWN_MASK) {
/* Allow re-connect after SHUTDOWN_IND */
caif_disconnect_client(sock_net(sk), &cf_sk->layer);
+ caif_free_client(&cf_sk->layer);
break;
}
/* No reconnect on a seqpacket socket */
@@ -926,7 +927,6 @@ static int caif_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- int res = 0;
if (!sk)
return 0;
@@ -953,10 +953,7 @@ static int caif_release(struct socket *sock)
sk->sk_state = CAIF_DISCONNECTED;
sk->sk_shutdown = SHUTDOWN_MASK;
- if (cf_sk->sk.sk_socket->state == SS_CONNECTED ||
- cf_sk->sk.sk_socket->state == SS_CONNECTING)
- res = caif_disconnect_client(sock_net(sk), &cf_sk->layer);
-
+ caif_disconnect_client(sock_net(sk), &cf_sk->layer);
cf_sk->sk.sk_socket->state = SS_DISCONNECTING;
wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP);
@@ -964,7 +961,7 @@ static int caif_release(struct socket *sock)
sk_stream_kill_queues(&cf_sk->sk);
release_sock(sk);
sock_put(sk);
- return res;
+ return 0;
}
/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */
@@ -1120,7 +1117,7 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
set_rx_flow_on(cf_sk);
/* Set default options on configuration */
- cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL;
+ cf_sk->sk.sk_priority = CAIF_PRIO_NORMAL;
cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;
cf_sk->conn_req.protocol = protocol;
/* Increase the number of sockets created. */
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index 351c2ca7e7b..52fe33bee02 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -182,39 +182,26 @@ static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
{
- u8 channel_id = 0;
- int ret = 0;
- struct cflayer *servl = NULL;
+ u8 channel_id;
struct cfcnfg *cfg = get_cfcnfg(net);
caif_assert(adap_layer != NULL);
-
- channel_id = adap_layer->id;
- if (adap_layer->dn == NULL || channel_id == 0) {
- pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n");
- ret = -ENOTCONN;
- goto end;
- }
-
- servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
- if (servl == NULL) {
- pr_err("PROTOCOL ERROR - "
- "Error removing service_layer Channel_Id(%d)",
- channel_id);
- ret = -EINVAL;
- goto end;
- }
-
- ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
-
-end:
cfctrl_cancel_req(cfg->ctrl, adap_layer);
+ channel_id = adap_layer->id;
+ if (channel_id != 0) {
+ struct cflayer *servl;
+ servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
+ if (servl != NULL)
+ layer_set_up(servl, NULL);
+ } else
+ pr_debug("nothing to disconnect\n");
+ cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
/* Do RCU sync before initiating cleanup */
synchronize_rcu();
if (adap_layer->ctrlcmd != NULL)
adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
- return ret;
+ return 0;
}
EXPORT_SYMBOL(caif_disconnect_client);
@@ -400,6 +387,14 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
struct cfcnfg_phyinfo *phyinfo;
struct net_device *netdev;
+ if (channel_id == 0) {
+ pr_warn("received channel_id zero\n");
+ if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
+ adapt_layer->ctrlcmd(adapt_layer,
+ CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
+ return;
+ }
+
rcu_read_lock();
if (adapt_layer == NULL) {
@@ -523,7 +518,6 @@ got_phyid:
phyinfo->use_stx = stx;
phyinfo->use_fcs = fcs;
- phy_layer->type = phy_type;
frml = cffrml_create(phyid, fcs);
if (!frml) {
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 0c00a6015dd..e22671bed66 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -178,20 +178,23 @@ static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
{
struct cfctrl *cfctrl = container_obj(layer);
- int ret;
struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ struct cflayer *dn = cfctrl->serv.layer.dn;
if (!pkt) {
pr_warn("Out of memory\n");
return;
}
+ if (!dn) {
+ pr_debug("not able to send enum request\n");
+ return;
+ }
caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
init_info(cfpkt_info(pkt), cfctrl);
cfpkt_info(pkt)->dev_info->id = physlinkid;
cfctrl->serv.dev_info.id = physlinkid;
cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
cfpkt_addbdy(pkt, physlinkid);
- ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ dn->transmit(dn, pkt);
}
int cfctrl_linkup_request(struct cflayer *layer,
@@ -206,6 +209,12 @@ int cfctrl_linkup_request(struct cflayer *layer,
int ret;
char utility_name[16];
struct cfpkt *pkt;
+ struct cflayer *dn = cfctrl->serv.layer.dn;
+
+ if (!dn) {
+ pr_debug("not able to send linkup request\n");
+ return -ENODEV;
+ }
if (cfctrl_cancel_req(layer, user_layer) > 0) {
/* Slight Paranoia, check if already connecting */
@@ -282,7 +291,7 @@ int cfctrl_linkup_request(struct cflayer *layer,
*/
cfpkt_info(pkt)->dev_info->id = param->phyid;
ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ dn->transmit(dn, pkt);
if (ret < 0) {
int count;
@@ -301,15 +310,23 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
int ret;
struct cfctrl *cfctrl = container_obj(layer);
struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
+ struct cflayer *dn = cfctrl->serv.layer.dn;
+
if (!pkt) {
pr_warn("Out of memory\n");
return -ENOMEM;
}
+
+ if (!dn) {
+ pr_debug("not able to send link-down request\n");
+ return -ENODEV;
+ }
+
cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
cfpkt_addbdy(pkt, channelid);
init_info(cfpkt_info(pkt), cfctrl);
ret =
- cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
+ dn->transmit(dn, pkt);
#ifndef CAIF_NO_LOOP
cfctrl->loop_linkused[channelid] = 0;
#endif
@@ -351,7 +368,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &cmdrsp, 1);
cmd = cmdrsp & CFCTRL_CMD_MASK;
if (cmd != CFCTRL_CMD_LINK_ERR
- && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
+ && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
+ && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
if (handle_loop(cfctrl, cmd, pkt) != 0)
cmdrsp |= CFCTRL_ERR_BIT;
}
@@ -477,7 +495,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
cfpkt_extr_head(pkt, &param, len);
break;
default:
- pr_warn("Request setup - invalid link type (%d)\n",
+ pr_warn("Request setup, invalid type (%d)\n",
serv);
goto error;
}
@@ -489,7 +507,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
cfpkt_erroneous(pkt)) {
- pr_err("Invalid O/E bit or parse error on CAIF control channel\n");
+ pr_err("Invalid O/E bit or parse error "
+ "on CAIF control channel\n");
cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
0,
req ? req->client_layer
@@ -550,9 +569,8 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
case CAIF_CTRLCMD_FLOW_OFF_IND:
spin_lock_bh(&this->info_list_lock);
- if (!list_empty(&this->list)) {
+ if (!list_empty(&this->list))
pr_debug("Received flow off in control layer\n");
- }
spin_unlock_bh(&this->info_list_lock);
break;
case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
@@ -587,16 +605,16 @@ static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
case CFCTRL_CMD_LINK_SETUP:
spin_lock_bh(&ctrl->loop_linkid_lock);
if (!dec) {
- for (linkid = last_linkid + 1; linkid < 255; linkid++)
+ for (linkid = last_linkid + 1; linkid < 254; linkid++)
if (!ctrl->loop_linkused[linkid])
goto found;
}
dec = 1;
- for (linkid = last_linkid - 1; linkid > 0; linkid--)
+ for (linkid = last_linkid - 1; linkid > 1; linkid--)
if (!ctrl->loop_linkused[linkid])
goto found;
spin_unlock_bh(&ctrl->loop_linkid_lock);
-
+ return -1;
found:
if (linkid < 10)
dec = 0;
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index 2a56df7e0a4..3a66b8c10e0 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -62,16 +62,6 @@ struct cflayer *cfmuxl_create(void)
return &this->layer;
}
-int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
-{
- struct cfmuxl *muxl = container_obj(layr);
-
- spin_lock_bh(&muxl->receive_lock);
- list_add_rcu(&up->node, &muxl->srvl_list);
- spin_unlock_bh(&muxl->receive_lock);
- return 0;
-}
-
int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
{
struct cfmuxl *muxl = (struct cfmuxl *) layr;
@@ -93,6 +83,24 @@ static struct cflayer *get_from_id(struct list_head *list, u16 id)
return NULL;
}
+int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
+{
+ struct cfmuxl *muxl = container_obj(layr);
+ struct cflayer *old;
+
+ spin_lock_bh(&muxl->receive_lock);
+
+ /* Two entries with same id is wrong, so remove old layer from mux */
+ old = get_from_id(&muxl->srvl_list, linkid);
+ if (old != NULL)
+ list_del_rcu(&old->node);
+
+ list_add_rcu(&up->node, &muxl->srvl_list);
+ spin_unlock_bh(&muxl->receive_lock);
+
+ return 0;
+}
+
struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
{
struct cfmuxl *muxl = container_obj(layr);
@@ -146,6 +154,11 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
struct cfmuxl *muxl = container_obj(layr);
int idx = id % UP_CACHE_SIZE;
+ if (id == 0) {
+ pr_warn("Trying to remove control layer\n");
+ return NULL;
+ }
+
spin_lock_bh(&muxl->receive_lock);
up = get_from_id(&muxl->srvl_list, id);
if (up == NULL)
@@ -235,12 +248,26 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *layer;
+ int idx;
rcu_read_lock();
list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
- if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd)
+
+ if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {
+
+ if ((ctrl == _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND ||
+ ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
+ layer->id != 0) {
+
+ idx = layer->id % UP_CACHE_SIZE;
+ spin_lock_bh(&muxl->receive_lock);
+ rcu_assign_pointer(muxl->up_cache[idx], NULL);
+ list_del_rcu(&layer->node);
+ spin_unlock_bh(&muxl->receive_lock);
+ }
/* NOTE: ctrlcmd is not allowed to block */
layer->ctrlcmd(layer, ctrl, phyid);
+ }
}
rcu_read_unlock();
}
diff --git a/net/core/dev.c b/net/core/dev.c
index d94537914a7..bcb05cb799c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4294,10 +4294,8 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
slave->master = master;
- if (old) {
- synchronize_net();
+ if (old)
dev_put(old);
- }
return 0;
}
EXPORT_SYMBOL(netdev_set_master);
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 67870e9fd09..f76079cd750 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3544,13 +3544,12 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
return -ENOMEM;
strcpy(pkt_dev->odevname, ifname);
- pkt_dev->flows = vmalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
+ pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
node);
if (pkt_dev->flows == NULL) {
kfree(pkt_dev);
return -ENOMEM;
}
- memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
pkt_dev->removal_mark = 0;
pkt_dev->min_pkt_size = ETH_ZLEN;
@@ -3708,6 +3707,7 @@ static int __init pg_init(void)
{
int cpu;
struct proc_dir_entry *pe;
+ int ret = 0;
pr_info("%s", version);
@@ -3718,11 +3718,10 @@ static int __init pg_init(void)
pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops);
if (pe == NULL) {
pr_err("ERROR: cannot create %s procfs entry\n", PGCTRL);
- proc_net_remove(&init_net, PG_PROC_DIR);
- return -EINVAL;
+ ret = -EINVAL;
+ goto remove_dir;
}
- /* Register us to receive netdevice events */
register_netdevice_notifier(&pktgen_notifier_block);
for_each_online_cpu(cpu) {
@@ -3736,13 +3735,18 @@ static int __init pg_init(void)
if (list_empty(&pktgen_threads)) {
pr_err("ERROR: Initialization failed for all threads\n");
- unregister_netdevice_notifier(&pktgen_notifier_block);
- remove_proc_entry(PGCTRL, pg_proc_dir);
- proc_net_remove(&init_net, PG_PROC_DIR);
- return -ENODEV;
+ ret = -ENODEV;
+ goto unregister;
}
return 0;
+
+ unregister:
+ unregister_netdevice_notifier(&pktgen_notifier_block);
+ remove_proc_entry(PGCTRL, pg_proc_dir);
+ remove_dir:
+ proc_net_remove(&init_net, PG_PROC_DIR);
+ return ret;
}
static void __exit pg_cleanup(void)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index d2ba2597c75..d1644e317e7 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1956,6 +1956,8 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
case NETDEV_GOING_DOWN:
case NETDEV_UNREGISTER:
case NETDEV_UNREGISTER_BATCH:
+ case NETDEV_RELEASE:
+ case NETDEV_JOIN:
break;
default:
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index b24d58e6bbc..52b0b956508 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1665,6 +1665,7 @@ static int ip_rt_bug(struct sk_buff *skb)
&ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
skb->dev ? skb->dev->name : "?");
kfree_skb(skb);
+ WARN_ON(1);
return 0;
}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index c84b65920d1..b1721d71c27 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -815,9 +815,17 @@ static bool some_qdisc_is_busy(struct net_device *dev)
return false;
}
+/**
+ * dev_deactivate_many - deactivate transmissions on several devices
+ * @head: list of devices to deactivate
+ *
+ * This function returns only when all outstanding transmissions
+ * have completed, unless all devices are in dismantle phase.
+ */
void dev_deactivate_many(struct list_head *head)
{
struct net_device *dev;
+ bool sync_needed = false;
list_for_each_entry(dev, head, unreg_list) {
netdev_for_each_tx_queue(dev, dev_deactivate_queue,
@@ -827,10 +835,15 @@ void dev_deactivate_many(struct list_head *head)
&noop_qdisc);
dev_watchdog_down(dev);
+ sync_needed |= !dev->dismantle;
}
- /* Wait for outstanding qdisc-less dev_queue_xmit calls. */
- synchronize_rcu();
+ /* Wait for outstanding qdisc-less dev_queue_xmit calls.
+ * This is avoided if all devices are in dismantle phase :
+ * Caller will call synchronize_net() for us
+ */
+ if (sync_needed)
+ synchronize_net();
/* Wait for outstanding qdisc_run calls. */
list_for_each_entry(dev, head, unreg_list)