aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2007-07-14 21:51:31 -0400
committerDavid S. Miller <davem@davemloft.net>2007-07-14 21:51:31 -0400
commit24023451c8df726692e2f52288a20870d13b501f (patch)
treeb80c48dfe817a1a18484dadbc110ca07353c86c8 /net/core
parente6c9116d1dc984cb7ecf1b0fe26ca4a8ab36bb57 (diff)
[NET]: Add net_device change_rx_mode callback
Currently the set_multicast_list (and set_rx_mode) callbacks are responsible for configuring the device according to the IFF_PROMISC, IFF_MULTICAST and IFF_ALLMULTI flags and the mc_list (and uc_list in case of set_rx_mode). These callbacks can be invoked from BH context without the rtnl_mutex by dev_mc_add/dev_mc_delete, which makes reading the device flags and promiscous/allmulti count racy. For real hardware drivers that just commit all changes to the hardware this is not a real problem since the stack guarantees to call them for every change, so at least the final call will not race and commit the correct configuration to the hardware. For software devices that want to synchronize promiscous and multicast state to an underlying device however this can cause corruption of the underlying device's flags or promisc/allmulti counts. When the software device is concurrently put in promiscous or allmulti mode while set_multicast_list is invoked from bottem half context, the device might synchronize the change to the underlying device without holding the rtnl_mutex, which races with concurrent changes to the underlying device. Add a dev->change_rx_flags hook that is invoked when any of the flags that affect rx filtering change (under the rtnl_mutex), which allows drivers to perform synchronization immediately and only synchronize the address lists in set_multicast_list/set_rx_mode. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 96443055324e..59ec811d2b54 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2521,6 +2521,8 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
2521{ 2521{
2522 unsigned short old_flags = dev->flags; 2522 unsigned short old_flags = dev->flags;
2523 2523
2524 ASSERT_RTNL();
2525
2524 if ((dev->promiscuity += inc) == 0) 2526 if ((dev->promiscuity += inc) == 0)
2525 dev->flags &= ~IFF_PROMISC; 2527 dev->flags &= ~IFF_PROMISC;
2526 else 2528 else
@@ -2535,6 +2537,9 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc)
2535 dev->name, (dev->flags & IFF_PROMISC), 2537 dev->name, (dev->flags & IFF_PROMISC),
2536 (old_flags & IFF_PROMISC), 2538 (old_flags & IFF_PROMISC),
2537 audit_get_loginuid(current->audit_context)); 2539 audit_get_loginuid(current->audit_context));
2540
2541 if (dev->change_rx_flags)
2542 dev->change_rx_flags(dev, IFF_PROMISC);
2538 } 2543 }
2539} 2544}
2540 2545
@@ -2573,11 +2578,16 @@ void dev_set_allmulti(struct net_device *dev, int inc)
2573{ 2578{
2574 unsigned short old_flags = dev->flags; 2579 unsigned short old_flags = dev->flags;
2575 2580
2581 ASSERT_RTNL();
2582
2576 dev->flags |= IFF_ALLMULTI; 2583 dev->flags |= IFF_ALLMULTI;
2577 if ((dev->allmulti += inc) == 0) 2584 if ((dev->allmulti += inc) == 0)
2578 dev->flags &= ~IFF_ALLMULTI; 2585 dev->flags &= ~IFF_ALLMULTI;
2579 if (dev->flags ^ old_flags) 2586 if (dev->flags ^ old_flags) {
2587 if (dev->change_rx_flags)
2588 dev->change_rx_flags(dev, IFF_ALLMULTI);
2580 dev_set_rx_mode(dev); 2589 dev_set_rx_mode(dev);
2590 }
2581} 2591}
2582 2592
2583/* 2593/*
@@ -2778,6 +2788,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
2778 int ret, changes; 2788 int ret, changes;
2779 int old_flags = dev->flags; 2789 int old_flags = dev->flags;
2780 2790
2791 ASSERT_RTNL();
2792
2781 /* 2793 /*
2782 * Set the flags on our device. 2794 * Set the flags on our device.
2783 */ 2795 */
@@ -2792,6 +2804,9 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
2792 * Load in the correct multicast list now the flags have changed. 2804 * Load in the correct multicast list now the flags have changed.
2793 */ 2805 */
2794 2806
2807 if (dev->change_rx_flags && (dev->flags ^ flags) & IFF_MULTICAST)
2808 dev->change_rx_flags(dev, IFF_MULTICAST);
2809
2795 dev_set_rx_mode(dev); 2810 dev_set_rx_mode(dev);
2796 2811
2797 /* 2812 /*