aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2016-01-27 09:16:43 -0500
committerDavid S. Miller <davem@davemloft.net>2016-01-28 19:21:31 -0500
commit4f2c6ae5c64c353fb1b0425e4747e5603feadba1 (patch)
tree75e57cd0d11cd1cd39eb12784472b1aff6537242 /net
parent7d0105b5334b9722b7d33acad613096dfcf3330e (diff)
switchdev: Require RTNL mutex to be held when sending FDB notifications
When switchdev drivers process FDB notifications from the underlying device they resolve the netdev to which the entry points to and notify the bridge using the switchdev notifier. However, since the RTNL mutex is not held there is nothing preventing the netdev from disappearing in the middle, which will cause br_switchdev_event() to dereference a non-existing netdev. Make switchdev drivers hold the lock at the beginning of the notification processing session and release it once it ends, after notifying the bridge. Also, remove switchdev_mutex and fdb_lock, as they are no longer needed when RTNL mutex is held. Fixes: 03bf0c281234 ("switchdev: introduce switchdev notifier") Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br.c3
-rw-r--r--net/switchdev/switchdev.c15
2 files changed, 9 insertions, 9 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index a1abe4936fe1..3addc05b9a16 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -121,6 +121,7 @@ static struct notifier_block br_device_notifier = {
121 .notifier_call = br_device_event 121 .notifier_call = br_device_event
122}; 122};
123 123
124/* called with RTNL */
124static int br_switchdev_event(struct notifier_block *unused, 125static int br_switchdev_event(struct notifier_block *unused,
125 unsigned long event, void *ptr) 126 unsigned long event, void *ptr)
126{ 127{
@@ -130,7 +131,6 @@ static int br_switchdev_event(struct notifier_block *unused,
130 struct switchdev_notifier_fdb_info *fdb_info; 131 struct switchdev_notifier_fdb_info *fdb_info;
131 int err = NOTIFY_DONE; 132 int err = NOTIFY_DONE;
132 133
133 rtnl_lock();
134 p = br_port_get_rtnl(dev); 134 p = br_port_get_rtnl(dev);
135 if (!p) 135 if (!p)
136 goto out; 136 goto out;
@@ -155,7 +155,6 @@ static int br_switchdev_event(struct notifier_block *unused,
155 } 155 }
156 156
157out: 157out:
158 rtnl_unlock();
159 return err; 158 return err;
160} 159}
161 160
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index ebc661d3b6e3..47f7da58a7f0 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -20,6 +20,7 @@
20#include <linux/list.h> 20#include <linux/list.h>
21#include <linux/workqueue.h> 21#include <linux/workqueue.h>
22#include <linux/if_vlan.h> 22#include <linux/if_vlan.h>
23#include <linux/rtnetlink.h>
23#include <net/ip_fib.h> 24#include <net/ip_fib.h>
24#include <net/switchdev.h> 25#include <net/switchdev.h>
25 26
@@ -567,7 +568,6 @@ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
567} 568}
568EXPORT_SYMBOL_GPL(switchdev_port_obj_dump); 569EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
569 570
570static DEFINE_MUTEX(switchdev_mutex);
571static RAW_NOTIFIER_HEAD(switchdev_notif_chain); 571static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
572 572
573/** 573/**
@@ -582,9 +582,9 @@ int register_switchdev_notifier(struct notifier_block *nb)
582{ 582{
583 int err; 583 int err;
584 584
585 mutex_lock(&switchdev_mutex); 585 rtnl_lock();
586 err = raw_notifier_chain_register(&switchdev_notif_chain, nb); 586 err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
587 mutex_unlock(&switchdev_mutex); 587 rtnl_unlock();
588 return err; 588 return err;
589} 589}
590EXPORT_SYMBOL_GPL(register_switchdev_notifier); 590EXPORT_SYMBOL_GPL(register_switchdev_notifier);
@@ -600,9 +600,9 @@ int unregister_switchdev_notifier(struct notifier_block *nb)
600{ 600{
601 int err; 601 int err;
602 602
603 mutex_lock(&switchdev_mutex); 603 rtnl_lock();
604 err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb); 604 err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
605 mutex_unlock(&switchdev_mutex); 605 rtnl_unlock();
606 return err; 606 return err;
607} 607}
608EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); 608EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
@@ -616,16 +616,17 @@ EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
616 * Call all network notifier blocks. This should be called by driver 616 * Call all network notifier blocks. This should be called by driver
617 * when it needs to propagate hardware event. 617 * when it needs to propagate hardware event.
618 * Return values are same as for atomic_notifier_call_chain(). 618 * Return values are same as for atomic_notifier_call_chain().
619 * rtnl_lock must be held.
619 */ 620 */
620int call_switchdev_notifiers(unsigned long val, struct net_device *dev, 621int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
621 struct switchdev_notifier_info *info) 622 struct switchdev_notifier_info *info)
622{ 623{
623 int err; 624 int err;
624 625
626 ASSERT_RTNL();
627
625 info->dev = dev; 628 info->dev = dev;
626 mutex_lock(&switchdev_mutex);
627 err = raw_notifier_call_chain(&switchdev_notif_chain, val, info); 629 err = raw_notifier_call_chain(&switchdev_notif_chain, val, info);
628 mutex_unlock(&switchdev_mutex);
629 return err; 630 return err;
630} 631}
631EXPORT_SYMBOL_GPL(call_switchdev_notifiers); 632EXPORT_SYMBOL_GPL(call_switchdev_notifiers);