net: hold netdev instance lock during ioctl operations
authorStanislav Fomichev <sdf@fomichev.me>
Wed, 5 Mar 2025 16:37:25 +0000 (08:37 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Mar 2025 20:59:44 +0000 (12:59 -0800)
Convert all ndo_eth_ioctl invocations to dev_eth_ioctl which does the
locking. Reflow some of the dev_siocxxx to drop else clause.

Cc: Saeed Mahameed <saeed@kernel.org>
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20250305163732.2766420-8-sdf@fomichev.me
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/bonding/bond_main.c
include/linux/netdevice.h
net/8021q/vlan_dev.c
net/core/dev.c
net/core/dev_api.c
net/core/dev_ioctl.c

index fe7072336e8fdbb9a528fed445a8d3c25a370b9e..cd4afeb9ad42e4c2da28a3e36c4b0bcd2378eaa6 100644 (file)
@@ -855,7 +855,6 @@ static int bond_check_dev_link(struct bonding *bond,
                               struct net_device *slave_dev, int reporting)
 {
        const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
-       int (*ioctl)(struct net_device *, struct ifreq *, int);
        struct ifreq ifr;
        struct mii_ioctl_data *mii;
 
@@ -871,8 +870,7 @@ static int bond_check_dev_link(struct bonding *bond,
                        BMSR_LSTATUS : 0;
 
        /* Ethtool can't be used, fallback to MII ioctls. */
-       ioctl = slave_ops->ndo_eth_ioctl;
-       if (ioctl) {
+       if (slave_ops->ndo_eth_ioctl) {
                /* TODO: set pointer to correct ioctl on a per team member
                 *       bases to make this more efficient. that is, once
                 *       we determine the correct ioctl, we will always
@@ -888,9 +886,10 @@ static int bond_check_dev_link(struct bonding *bond,
                /* Yes, the mii is overlaid on the ifreq.ifr_ifru */
                strscpy_pad(ifr.ifr_name, slave_dev->name, IFNAMSIZ);
                mii = if_mii(&ifr);
-               if (ioctl(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
+
+               if (dev_eth_ioctl(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
                        mii->reg_num = MII_BMSR;
-                       if (ioctl(slave_dev, &ifr, SIOCGMIIREG) == 0)
+                       if (dev_eth_ioctl(slave_dev, &ifr, SIOCGMIIREG) == 0)
                                return mii->val_out & BMSR_LSTATUS;
                }
        }
index be3d09b61e959b5612d799c552e95be2e328b1f2..8d243c0ec39d15ec4450f57b1cb2f6bf61580055 100644 (file)
@@ -4229,6 +4229,8 @@ int put_user_ifreq(struct ifreq *ifr, void __user *arg);
 int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
                void __user *data, bool *need_copyout);
 int dev_ifconf(struct net *net, struct ifconf __user *ifc);
+int dev_eth_ioctl(struct net_device *dev,
+                 struct ifreq *ifr, unsigned int cmd);
 int generic_hwtstamp_get_lower(struct net_device *dev,
                               struct kernel_hwtstamp_config *kernel_cfg);
 int generic_hwtstamp_set_lower(struct net_device *dev,
@@ -4251,6 +4253,7 @@ int netif_change_net_namespace(struct net_device *dev, struct net *net,
 int dev_change_net_namespace(struct net_device *dev, struct net *net,
                             const char *pat);
 int __dev_set_mtu(struct net_device *, int);
+int netif_set_mtu(struct net_device *dev, int new_mtu);
 int dev_set_mtu(struct net_device *, int);
 int dev_pre_changeaddr_notify(struct net_device *dev, const char *addr,
                              struct netlink_ext_ack *extack);
index 91d134961357c4737e6d53f801a2f5ffa778a002..ee328340071688e05b16881c8114db518e8d3180 100644 (file)
@@ -377,7 +377,6 @@ static int vlan_hwtstamp_set(struct net_device *dev,
 static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
-       const struct net_device_ops *ops = real_dev->netdev_ops;
        struct ifreq ifrr;
        int err = -EOPNOTSUPP;
 
@@ -388,8 +387,7 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        case SIOCGMIIPHY:
        case SIOCGMIIREG:
        case SIOCSMIIREG:
-               if (netif_device_present(real_dev) && ops->ndo_eth_ioctl)
-                       err = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
+               err = dev_eth_ioctl(real_dev, &ifrr, cmd);
                break;
        }
 
index 39b214fb462e4db9b6a5589b5da0a918a9aa00f5..97a4fc0889d344999bca67ca61a0912e0338f663 100644 (file)
@@ -9522,7 +9522,7 @@ int netif_set_mtu_ext(struct net_device *dev, int new_mtu,
        return err;
 }
 
-int dev_set_mtu(struct net_device *dev, int new_mtu)
+int netif_set_mtu(struct net_device *dev, int new_mtu)
 {
        struct netlink_ext_ack extack;
        int err;
@@ -9533,7 +9533,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
                net_err_ratelimited("%s: %s\n", dev->name, extack._msg);
        return err;
 }
-EXPORT_SYMBOL(dev_set_mtu);
+EXPORT_SYMBOL(netif_set_mtu);
 
 int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)
 {
index 21584f57e050d2e0a69b125a1eba3c0ef257b258..059413d9ef9d8e019c648c3e209c21419e5704df 100644 (file)
@@ -222,3 +222,33 @@ void dev_close(struct net_device *dev)
        netdev_unlock_ops(dev);
 }
 EXPORT_SYMBOL(dev_close);
+
+int dev_eth_ioctl(struct net_device *dev,
+                 struct ifreq *ifr, unsigned int cmd)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+       int ret = -ENODEV;
+
+       if (!ops->ndo_eth_ioctl)
+               return -EOPNOTSUPP;
+
+       netdev_lock_ops(dev);
+       if (netif_device_present(dev))
+               ret = ops->ndo_eth_ioctl(dev, ifr, cmd);
+       netdev_unlock_ops(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(dev_eth_ioctl);
+
+int dev_set_mtu(struct net_device *dev, int new_mtu)
+{
+       int ret;
+
+       netdev_lock_ops(dev);
+       ret = netif_set_mtu(dev, new_mtu);
+       netdev_unlock_ops(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(dev_set_mtu);
index 4c2098ac9d7243eded892759165e0f668741f50a..d9f35059312108f281d3e8a9403bf1a346a018b7 100644 (file)
@@ -110,7 +110,7 @@ static int dev_getifmap(struct net_device *dev, struct ifreq *ifr)
        return 0;
 }
 
-static int dev_setifmap(struct net_device *dev, struct ifreq *ifr)
+static int netif_setifmap(struct net_device *dev, struct ifreq *ifr)
 {
        struct compat_ifmap *cifmap = (struct compat_ifmap *)&ifr->ifr_map;
 
@@ -240,20 +240,6 @@ int net_hwtstamp_validate(const struct kernel_hwtstamp_config *cfg)
        return 0;
 }
 
-static int dev_eth_ioctl(struct net_device *dev,
-                        struct ifreq *ifr, unsigned int cmd)
-{
-       const struct net_device_ops *ops = dev->netdev_ops;
-
-       if (!ops->ndo_eth_ioctl)
-               return -EOPNOTSUPP;
-
-       if (!netif_device_present(dev))
-               return -ENODEV;
-
-       return ops->ndo_eth_ioctl(dev, ifr, cmd);
-}
-
 /**
  * dev_get_hwtstamp_phylib() - Get hardware timestamping settings of NIC
  *     or of attached phylib PHY
@@ -305,7 +291,9 @@ static int dev_get_hwtstamp(struct net_device *dev, struct ifreq *ifr)
                return -ENODEV;
 
        kernel_cfg.ifr = ifr;
+       netdev_lock_ops(dev);
        err = dev_get_hwtstamp_phylib(dev, &kernel_cfg);
+       netdev_unlock_ops(dev);
        if (err)
                return err;
 
@@ -429,7 +417,9 @@ static int dev_set_hwtstamp(struct net_device *dev, struct ifreq *ifr)
        if (!netif_device_present(dev))
                return -ENODEV;
 
+       netdev_lock_ops(dev);
        err = dev_set_hwtstamp_phylib(dev, &kernel_cfg, &extack);
+       netdev_unlock_ops(dev);
        if (err)
                return err;
 
@@ -504,10 +494,14 @@ static int dev_siocbond(struct net_device *dev,
        const struct net_device_ops *ops = dev->netdev_ops;
 
        if (ops->ndo_siocbond) {
+               int ret = -ENODEV;
+
+               netdev_lock_ops(dev);
                if (netif_device_present(dev))
-                       return ops->ndo_siocbond(dev, ifr, cmd);
-               else
-                       return -ENODEV;
+                       ret = ops->ndo_siocbond(dev, ifr, cmd);
+               netdev_unlock_ops(dev);
+
+               return ret;
        }
 
        return -EOPNOTSUPP;
@@ -519,10 +513,14 @@ static int dev_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
        const struct net_device_ops *ops = dev->netdev_ops;
 
        if (ops->ndo_siocdevprivate) {
+               int ret = -ENODEV;
+
+               netdev_lock_ops(dev);
                if (netif_device_present(dev))
-                       return ops->ndo_siocdevprivate(dev, ifr, data, cmd);
-               else
-                       return -ENODEV;
+                       ret = ops->ndo_siocdevprivate(dev, ifr, data, cmd);
+               netdev_unlock_ops(dev);
+
+               return ret;
        }
 
        return -EOPNOTSUPP;
@@ -533,10 +531,14 @@ static int dev_siocwandev(struct net_device *dev, struct if_settings *ifs)
        const struct net_device_ops *ops = dev->netdev_ops;
 
        if (ops->ndo_siocwandev) {
+               int ret = -ENODEV;
+
+               netdev_lock_ops(dev);
                if (netif_device_present(dev))
-                       return ops->ndo_siocwandev(dev, ifs);
-               else
-                       return -ENODEV;
+                       ret = ops->ndo_siocwandev(dev, ifs);
+               netdev_unlock_ops(dev);
+
+               return ret;
        }
 
        return -EOPNOTSUPP;
@@ -580,11 +582,16 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
                memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
                       min(sizeof(ifr->ifr_hwaddr.sa_data_min),
                           (size_t)dev->addr_len));
+               netdev_lock_ops(dev);
                call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+               netdev_unlock_ops(dev);
                return 0;
 
        case SIOCSIFMAP:
-               return dev_setifmap(dev, ifr);
+               netdev_lock_ops(dev);
+               err = netif_setifmap(dev, ifr);
+               netdev_unlock_ops(dev);
+               return err;
 
        case SIOCADDMULTI:
                if (!ops->ndo_set_rx_mode ||
@@ -592,7 +599,10 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
                        return -EINVAL;
                if (!netif_device_present(dev))
                        return -ENODEV;
-               return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
+               netdev_lock_ops(dev);
+               err = dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
+               netdev_unlock_ops(dev);
+               return err;
 
        case SIOCDELMULTI:
                if (!ops->ndo_set_rx_mode ||
@@ -600,7 +610,10 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
                        return -EINVAL;
                if (!netif_device_present(dev))
                        return -ENODEV;
-               return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
+               netdev_lock_ops(dev);
+               err = dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
+               netdev_unlock_ops(dev);
+               return err;
 
        case SIOCSIFTXQLEN:
                if (ifr->ifr_qlen < 0)