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: Eric Dumazet <edumazet@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
ad13b5b0d1
commit
55a2c86c8d
@ -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)
|
||||
{
|
||||
unsigned int old_flags = dev->flags;
|
||||
unsigned int promiscuity, flags;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
dev->flags |= IFF_PROMISC;
|
||||
dev->promiscuity += inc;
|
||||
if (dev->promiscuity == 0) {
|
||||
promiscuity = dev->promiscuity + inc;
|
||||
if (promiscuity == 0) {
|
||||
/*
|
||||
* Avoid overflow.
|
||||
* If inc causes overflow, untouch promisc and return error.
|
||||
*/
|
||||
if (inc < 0)
|
||||
dev->flags &= ~IFF_PROMISC;
|
||||
else {
|
||||
dev->promiscuity -= inc;
|
||||
if (unlikely(inc > 0)) {
|
||||
netdev_warn(dev, "promiscuity touches roof, set promiscuity failed. promiscuity feature of device might be broken.\n");
|
||||
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",
|
||||
dev->flags & IFF_PROMISC ? "entered" : "left");
|
||||
if (audit_enabled) {
|
||||
@ -8615,25 +8617,27 @@ EXPORT_SYMBOL(dev_set_promiscuity);
|
||||
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 allmulti, flags;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
dev->flags |= IFF_ALLMULTI;
|
||||
dev->allmulti += inc;
|
||||
if (dev->allmulti == 0) {
|
||||
allmulti = dev->allmulti + inc;
|
||||
if (allmulti == 0) {
|
||||
/*
|
||||
* Avoid overflow.
|
||||
* If inc causes overflow, untouch allmulti and return error.
|
||||
*/
|
||||
if (inc < 0)
|
||||
dev->flags &= ~IFF_ALLMULTI;
|
||||
else {
|
||||
dev->allmulti -= inc;
|
||||
if (unlikely(inc > 0)) {
|
||||
netdev_warn(dev, "allmulti touches roof, set allmulti failed. allmulti feature of device might be broken.\n");
|
||||
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",
|
||||
dev->flags & IFF_ALLMULTI ? "entered" : "left");
|
||||
dev_change_rx_flags(dev, IFF_ALLMULTI);
|
||||
|
Loading…
x
Reference in New Issue
Block a user