Commit 55a2c86c authored by Eric Dumazet's avatar Eric Dumazet Committed by Paolo Abeni

net: write once on dev->allmulti and dev->promiscuity

In the following patch we want to read dev->allmulti
and dev->promiscuity locklessly from rtnl_fill_ifinfo()

In this patch I change __dev_set_promiscuity() and
__dev_set_allmulti() to write these fields (and dev->flags)
only if they succeed, with WRITE_ONCE() annotations.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarSimon Horman <horms@kernel.org>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent ad13b5b0
...@@ -8544,27 +8544,29 @@ static void dev_change_rx_flags(struct net_device *dev, int flags) ...@@ -8544,27 +8544,29 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
{ {
unsigned int old_flags = dev->flags; unsigned int old_flags = dev->flags;
unsigned int promiscuity, flags;
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
ASSERT_RTNL(); ASSERT_RTNL();
dev->flags |= IFF_PROMISC; promiscuity = dev->promiscuity + inc;
dev->promiscuity += inc; if (promiscuity == 0) {
if (dev->promiscuity == 0) {
/* /*
* Avoid overflow. * Avoid overflow.
* If inc causes overflow, untouch promisc and return error. * If inc causes overflow, untouch promisc and return error.
*/ */
if (inc < 0) if (unlikely(inc > 0)) {
dev->flags &= ~IFF_PROMISC;
else {
dev->promiscuity -= inc;
netdev_warn(dev, "promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n"); netdev_warn(dev, "promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n");
return -EOVERFLOW; return -EOVERFLOW;
} }
flags = old_flags & ~IFF_PROMISC;
} else {
flags = old_flags | IFF_PROMISC;
} }
if (dev->flags != old_flags) { WRITE_ONCE(dev->promiscuity, promiscuity);
if (flags != old_flags) {
WRITE_ONCE(dev->flags, flags);
netdev_info(dev, "%s promiscuous mode\n", netdev_info(dev, "%s promiscuous mode\n",
dev->flags & IFF_PROMISC ? "entered" : "left"); dev->flags & IFF_PROMISC ? "entered" : "left");
if (audit_enabled) { if (audit_enabled) {
...@@ -8615,25 +8617,27 @@ EXPORT_SYMBOL(dev_set_promiscuity); ...@@ -8615,25 +8617,27 @@ EXPORT_SYMBOL(dev_set_promiscuity);
static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify) static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify)
{ {
unsigned int old_flags = dev->flags, old_gflags = dev->gflags; unsigned int old_flags = dev->flags, old_gflags = dev->gflags;
unsigned int allmulti, flags;
ASSERT_RTNL(); ASSERT_RTNL();
dev->flags |= IFF_ALLMULTI; allmulti = dev->allmulti + inc;
dev->allmulti += inc; if (allmulti == 0) {
if (dev->allmulti == 0) {
/* /*
* Avoid overflow. * Avoid overflow.
* If inc causes overflow, untouch allmulti and return error. * If inc causes overflow, untouch allmulti and return error.
*/ */
if (inc < 0) if (unlikely(inc > 0)) {
dev->flags &= ~IFF_ALLMULTI;
else {
dev->allmulti -= inc;
netdev_warn(dev, "allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n"); netdev_warn(dev, "allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n");
return -EOVERFLOW; return -EOVERFLOW;
} }
flags = old_flags & ~IFF_ALLMULTI;
} else {
flags = old_flags | IFF_ALLMULTI;
} }
if (dev->flags ^ old_flags) { WRITE_ONCE(dev->allmulti, allmulti);
if (flags != old_flags) {
WRITE_ONCE(dev->flags, flags);
netdev_info(dev, "%s allmulticast mode\n", netdev_info(dev, "%s allmulticast mode\n",
dev->flags & IFF_ALLMULTI ? "entered" : "left"); dev->flags & IFF_ALLMULTI ? "entered" : "left");
dev_change_rx_flags(dev, IFF_ALLMULTI); dev_change_rx_flags(dev, IFF_ALLMULTI);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment