diff options
author | David S. Miller <davem@davemloft.net> | 2019-06-02 18:08:47 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-02 18:08:47 -0700 |
commit | feb3cf2e5ecc5840e27f328e0ce666a0f728f1a3 (patch) | |
tree | 84264a6f50aaedfac6854bef3d5972926d321c10 | |
parent | b9f88982750d5c28b3c55ab8b802ecfa62a0bbd2 (diff) | |
parent | 2638eb8b50cfc16240e0bb080b9afbf541a9b39d (diff) |
Merge branch 'ifa_list-RCU'
Florian Westphal says:
====================
net: add rcu annotations for ifa_list
v3: fix typo in patch1 commit message
All other patches are unchanged.
v2: remove ifa_list iteration in afs instead of conversion
Eric Dumazet reported following problem:
It looks that unless RTNL is held, accessing ifa_list needs proper RCU
protection. indev->ifa_list can be changed under us by another cpu
(which owns RTNL) [..]
A proper rcu_dereference() with an happy sparse support would require
adding __rcu attribute.
This patch series does that: add __rcu to the ifa_list pointers.
That makes sparse complain, so the series also adds the required
rcu_assign_pointer/dereference helpers where needed.
All patches except the last one are preparation work.
Two new macros are introduced for in_ifaddr walks.
Last patch adds the __rcu annotations and the assign_pointer/dereference
helper calls.
This patch is a bit large, but I found no better way -- other
approaches (annotate-first or add helpers-first) all result in
mid-series sparse warnings.
This series is submitted vs. net-next rather than net for several
reasons:
1. Its (mostly) compile-tested only
2. 3rd patch changes behaviour wrt. secondary addresses
(see changelog)
3. The problem exists for a very long time (2004), so it doesn't
seem to be urgent to fix this -- rcu use to free ifa_list
predates the git era.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
34 files changed, 250 insertions, 229 deletions
diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 558de0b9895c..2860def84f4d 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -330,6 +330,7 @@ static void bond_delete_netdev_default_gids(struct ib_device *ib_dev, static void enum_netdev_ipv4_ips(struct ib_device *ib_dev, u8 port, struct net_device *ndev) { + const struct in_ifaddr *ifa; struct in_device *in_dev; struct sin_list { struct list_head list; @@ -349,7 +350,7 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev, return; } - for_ifa(in_dev) { + in_dev_for_each_ifa_rcu(ifa, in_dev) { struct sin_list *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) @@ -359,7 +360,7 @@ static void enum_netdev_ipv4_ips(struct ib_device *ib_dev, entry->ip.sin_addr.s_addr = ifa->ifa_address; list_add_tail(&entry->list, &sin_list); } - endfor_ifa(in_dev); + rcu_read_unlock(); list_for_each_entry_safe(sin_iter, sin_temp, &sin_list, list) { diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 0f3b1193d5f8..09fcfc9e052d 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -3230,17 +3230,22 @@ static int pick_local_ipaddrs(struct c4iw_dev *dev, struct iw_cm_id *cm_id) int found = 0; struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->m_local_addr; struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->m_remote_addr; + const struct in_ifaddr *ifa; ind = in_dev_get(dev->rdev.lldi.ports[0]); if (!ind) return -EADDRNOTAVAIL; - for_primary_ifa(ind) { + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, ind) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; laddr->sin_addr.s_addr = ifa->ifa_address; raddr->sin_addr.s_addr = ifa->ifa_address; found = 1; break; } - endfor_ifa(ind); + rcu_read_unlock(); + in_dev_put(ind); return found ? 0 : -EADDRNOTAVAIL; } diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c index 8233f5a4e623..700a5d06b60c 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_cm.c +++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c @@ -1773,8 +1773,11 @@ static enum i40iw_status_code i40iw_add_mqh_4( if ((((rdma_vlan_dev_vlan_id(dev) < I40IW_NO_VLAN) && (rdma_vlan_dev_real_dev(dev) == iwdev->netdev)) || (dev == iwdev->netdev)) && (dev->flags & IFF_UP)) { + const struct in_ifaddr *ifa; + idev = in_dev_get(dev); - for_ifa(idev) { + + in_dev_for_each_ifa_rtnl(ifa, idev) { i40iw_debug(&iwdev->sc_dev, I40IW_DEBUG_CM, "Allocating child CM Listener forIP=%pI4, vlan_id=%d, MAC=%pM\n", @@ -1819,7 +1822,7 @@ static enum i40iw_status_code i40iw_add_mqh_4( cm_parent_listen_node->cm_core->stats_listen_nodes_created--; } } - endfor_ifa(idev); + in_dev_put(idev); } } diff --git a/drivers/infiniband/hw/i40iw/i40iw_main.c b/drivers/infiniband/hw/i40iw/i40iw_main.c index 10932baee279..d44cf33df81a 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_main.c +++ b/drivers/infiniband/hw/i40iw/i40iw_main.c @@ -1222,8 +1222,10 @@ static void i40iw_add_ipv4_addr(struct i40iw_device *iwdev) if ((((rdma_vlan_dev_vlan_id(dev) < 0xFFFF) && (rdma_vlan_dev_real_dev(dev) == iwdev->netdev)) || (dev == iwdev->netdev)) && (dev->flags & IFF_UP)) { + const struct in_ifaddr *ifa; + idev = in_dev_get(dev); - for_ifa(idev) { + in_dev_for_each_ifa_rtnl(ifa, idev) { i40iw_debug(&iwdev->sc_dev, I40IW_DEBUG_CM, "IP=%pI4, vlan_id=%d, MAC=%pM\n", &ifa->ifa_address, rdma_vlan_dev_vlan_id(dev), dev->dev_addr); @@ -1235,7 +1237,7 @@ static void i40iw_add_ipv4_addr(struct i40iw_device *iwdev) true, I40IW_ARP_ADD); } - endfor_ifa(idev); + in_dev_put(idev); } } diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c index 337410f40860..016524683e17 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_utils.c +++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c @@ -174,10 +174,14 @@ int i40iw_inetaddr_event(struct notifier_block *notifier, rcu_read_lock(); in = __in_dev_get_rcu(upper_dev); - if (!in->ifa_list) - local_ipaddr = 0; - else - local_ipaddr = ntohl(in->ifa_list->ifa_address); + local_ipaddr = 0; + if (in) { + struct in_ifaddr *ifa; + + ifa = rcu_dereference(in->ifa_list); + if (ifa) + local_ipaddr = ntohl(ifa->ifa_address); + } rcu_read_unlock(); } else { diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index e00add6d78ec..29b324726ea6 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -183,7 +183,13 @@ static int nes_inetaddr_event(struct notifier_block *notifier, rcu_read_lock(); in = __in_dev_get_rcu(upper_dev); - nesvnic->local_ipaddr = in->ifa_list->ifa_address; + if (in) { + struct in_ifaddr *ifa; + + ifa = rcu_dereference(in->ifa_list); + if (ifa) + nesvnic->local_ipaddr = ifa->ifa_address; + } rcu_read_unlock(); } else { nesvnic->local_ipaddr = ifa->ifa_address; diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c index d88d9f8a7f9a..34c1f9d6c915 100644 --- a/drivers/infiniband/hw/usnic/usnic_ib_main.c +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c @@ -427,11 +427,16 @@ static void *usnic_ib_device_add(struct pci_dev *dev) if (netif_carrier_ok(us_ibdev->netdev)) usnic_fwd_carrier_up(us_ibdev->ufdev); - ind = in_dev_get(netdev); - if (ind->ifa_list) - usnic_fwd_add_ipaddr(us_ibdev->ufdev, - ind->ifa_list->ifa_address); - in_dev_put(ind); + rcu_read_lock(); + ind = __in_dev_get_rcu(netdev); + if (ind) { + const struct in_ifaddr *ifa; + + ifa = rcu_dereference(ind->ifa_list); + if (ifa) + usnic_fwd_add_ipaddr(us_ibdev->ufdev, ifa->ifa_address); + } + rcu_read_unlock(); usnic_mac_ip_to_gid(us_ibdev->netdev->perm_addr, us_ibdev->ufdev->inaddr, &gid.raw[0]); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 84cb62434556..58e2eaf77014 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -3248,6 +3248,7 @@ netxen_config_indev_addr(struct netxen_adapter *adapter, struct net_device *dev, unsigned long event) { struct in_device *indev; + struct in_ifaddr *ifa; if (!netxen_destip_supported(adapter)) return; @@ -3256,7 +3257,8 @@ netxen_config_indev_addr(struct netxen_adapter *adapter, if (!indev) return; - for_ifa(indev) { + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, indev) { switch (event) { case NETDEV_UP: netxen_list_config_ip(adapter, ifa, NX_IP_UP); @@ -3267,8 +3269,8 @@ netxen_config_indev_addr(struct netxen_adapter *adapter, default: break; } - } endfor_ifa(indev); - + } + rcu_read_unlock(); in_dev_put(indev); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 7a873002e626..c07438db30ba 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -4119,13 +4119,14 @@ static void qlcnic_config_indev_addr(struct qlcnic_adapter *adapter, struct net_device *dev, unsigned long event) { + const struct in_ifaddr *ifa; struct in_device *indev; indev = in_dev_get(dev); if (!indev) return; - for_ifa(indev) { + in_dev_for_each_ifa_rtnl(ifa, indev) { switch (event) { case NETDEV_UP: qlcnic_config_ipaddr(adapter, @@ -4138,7 +4139,7 @@ qlcnic_config_indev_addr(struct qlcnic_adapter *adapter, default: break; } - } endfor_ifa(indev); + } in_dev_put(indev); } diff --git a/drivers/net/ethernet/via/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h index c0ecc6c7b5e0..cdfe7809e3c1 100644 --- a/drivers/net/ethernet/via/via-velocity.h +++ b/drivers/net/ethernet/via/via-velocity.h @@ -1509,7 +1509,7 @@ static inline int velocity_get_ip(struct velocity_info *vptr) rcu_read_lock(); in_dev = __in_dev_get_rcu(vptr->netdev); if (in_dev != NULL) { - ifa = (struct in_ifaddr *) in_dev->ifa_list; + ifa = rcu_dereference(in_dev->ifa_list); if (ifa != NULL) { memcpy(vptr->ip_addr, &ifa->ifa_address, 4); res = 0; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index feb92ecd1880..3e3ac2e496a1 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1012,7 +1012,7 @@ plip_rewrite_address(const struct net_device *dev, struct ethhdr *eth) in_dev = __in_dev_get_rcu(dev); if (in_dev) { /* Any address will do - we take the first */ - const struct in_ifaddr *ifa = in_dev->ifa_list; + const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); if (ifa) { memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); memset(eth->h_dest, 0xfc, 2); @@ -1107,7 +1107,7 @@ plip_open(struct net_device *dev) /* Any address will do - we take the first. We already have the first two bytes filled with 0xfc, from plip_init_dev(). */ - struct in_ifaddr *ifa=in_dev->ifa_list; + const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); if (ifa != NULL) { memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); } diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 89984fcab01e..1b2a18ea855c 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -3651,13 +3651,19 @@ vmxnet3_suspend(struct device *device) } if (adapter->wol & WAKE_ARP) { - in_dev = in_dev_get(netdev); - if (!in_dev) + rcu_read_lock(); + + in_dev = __in_dev_get_rcu(netdev); + if (!in_dev) { + rcu_read_unlock(); goto skip_arp; + } - ifa = (struct in_ifaddr *)in_dev->ifa_list; - if (!ifa) + ifa = rcu_dereference(in_dev->ifa_list); + if (!ifa) { + rcu_read_unlock(); goto skip_arp; + } pmConf->filters[i].patternSize = ETH_HLEN + /* Ethernet header*/ sizeof(struct arphdr) + /* ARP header */ @@ -3677,7 +3683,9 @@ vmxnet3_suspend(struct device *device) /* The Unicast IPv4 address in 'tip' field. */ arpreq += 2 * ETH_ALEN + sizeof(u32); - *(u32 *)arpreq = ifa->ifa_address; + *(__be32 *)arpreq = ifa->ifa_address; + + rcu_read_unlock(); /* The mask for the relevant bits. */ pmConf->filters[i].mask[0] = 0x00; @@ -3686,7 +3694,6 @@ vmxnet3_suspend(struct device *device) pmConf->filters[i].mask[3] = 0x00; pmConf->filters[i].mask[4] = 0xC0; /* IPv4 TIP */ pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */ - in_dev_put(in_dev); pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER; i++; diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 320039d329c7..4976ca3f30c7 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -196,16 +196,15 @@ static int cisco_rx(struct sk_buff *skb) mask = ~cpu_to_be32(0); /* is the mask correct? */ if (in_dev != NULL) { - struct in_ifaddr **ifap = &in_dev->ifa_list; + const struct in_ifaddr *ifa; - while (*ifap != NULL) { + in_dev_for_each_ifa_rcu(ifa, in_dev) { if (strcmp(dev->name, - (*ifap)->ifa_label) == 0) { - addr = (*ifap)->ifa_local; - mask = (*ifap)->ifa_mask; + ifa->ifa_label) == 0) { + addr = ifa->ifa_local; + mask = ifa->ifa_mask; break; } - ifap = &(*ifap)->ifa_next; } cisco_keepalive_send(dev, CISCO_ADDR_REPLY, diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 5477a014e1fb..37cf602d8adf 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2194,13 +2194,13 @@ static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif, if (!in_dev) return 0; - ifa = in_dev->ifa_list; + ifa = rtnl_dereference(in_dev->ifa_list); memset(&ips, 0, sizeof(ips)); /* Configure IP addr only if IP address count < MAX_IP_ADDRS */ while (index < MAX_IP_ADDRS && ifa) { ips[index] = ifa->ifa_local; - ifa = ifa->ifa_next; + ifa = rtnl_dereference(ifa->ifa_next); index++; } diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index e11a4bb67172..5a7cdb981789 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3268,7 +3268,7 @@ static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); if (!in_dev) continue; - ifa = in_dev->ifa_list; + ifa = rtnl_dereference(in_dev->ifa_list); if (!ifa || !ifa->ifa_local) continue; ips[i] = ifa->ifa_local; diff --git a/drivers/staging/isdn/hysdn/hysdn_net.c b/drivers/staging/isdn/hysdn/hysdn_net.c index 8e9c34f33d86..bea37ae30ebb 100644 --- a/drivers/staging/isdn/hysdn/hysdn_net.c +++ b/drivers/staging/isdn/hysdn/hysdn_net.c @@ -70,9 +70,13 @@ net_open(struct net_device *dev) for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = 0xfc; if ((in_dev = dev->ip_ptr) != NULL) { - struct in_ifaddr *ifa = in_dev->ifa_list; + const struct in_ifaddr *ifa; + + rcu_read_lock(); + ifa = rcu_dereference(in_dev->ifa_list); if (ifa != NULL) memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local)); + rcu_read_unlock(); } } else memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN); diff --git a/fs/afs/Makefile b/fs/afs/Makefile index cbf31f6cd177..10359bea7070 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -29,7 +29,6 @@ kafs-y := \ server.o \ server_list.o \ super.o \ - netdevices.o \ vlclient.o \ vl_list.o \ vl_probe.o \ diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 01437cfe5432..a61d2058c468 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -584,9 +584,8 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) */ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) { - struct afs_interface *ifs; struct afs_call *call = container_of(work, struct afs_call, work); - int loop, nifs; + int loop; struct { struct /* InterfaceAddr */ { @@ -604,19 +603,7 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) _enter(""); - nifs = 0; - ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL); - if (ifs) { - nifs = afs_get_ipv4_interfaces(call->net, ifs, 32, false); - if (nifs < 0) { - kfree(ifs); - ifs = NULL; - nifs = 0; - } - } - memset(&reply, 0, sizeof(reply)); - reply.ia.nifs = htonl(nifs); reply.ia.uuid[0] = call->net->uuid.time_low; reply.ia.uuid[1] = htonl(ntohs(call->net->uuid.time_mid)); @@ -626,15 +613,6 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) for (loop = 0; loop < 6; loop++) reply.ia.uuid[loop + 5] = htonl((s8) call->net->uuid.node[loop]); - if (ifs) { - for (loop = 0; loop < nifs; loop++) { - reply.ia.ifaddr[loop] = ifs[loop].address.s_addr; - reply.ia.netmask[loop] = ifs[loop].netmask.s_addr; - reply.ia.mtu[loop] = htonl(ifs[loop].mtu); - } - kfree(ifs); - } - reply.cap.capcount = htonl(1); reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION); afs_send_simple_reply(call, &reply, sizeof(reply)); diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 2073c1a3ab4b..a22fa3b77b3c 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -725,15 +725,6 @@ struct afs_permits { }; /* - * record of one of a system's set of network interfaces - */ -struct afs_interface { - struct in_addr address; /* IPv4 address bound to interface */ - struct in_addr netmask; /* netmask applied to address */ - unsigned mtu; /* MTU of interface */ -}; - -/* * Error prioritisation and accumulation. */ struct afs_error { @@ -1096,12 +1087,6 @@ extern struct vfsmount *afs_d_automount(struct path *); extern void afs_mntpt_kill_timer(void); /* - * netdevices.c - */ -extern int afs_get_ipv4_interfaces(struct afs_net *, struct afs_interface *, - size_t, bool); - -/* * proc.c */ #ifdef CONFIG_PROC_FS diff --git a/fs/afs/netdevices.c b/fs/afs/netdevices.c deleted file mode 100644 index 2a009d1939d7..000000000000 --- a/fs/afs/netdevices.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* AFS network device helpers - * - * Copyright (c) 2007 Patrick McHardy <kaber@trash.net> - */ - -#include <linux/string.h> -#include <linux/rtnetlink.h> -#include <linux/inetdevice.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <net/net_namespace.h> -#include "internal.h" - -/* - * get a list of this system's interface IPv4 addresses, netmasks and MTUs - * - maxbufs must be at least 1 - * - returns the number of interface records in the buffer - */ -int afs_get_ipv4_interfaces(struct afs_net *net, struct afs_interface *bufs, - size_t maxbufs, bool wantloopback) -{ - struct net_device *dev; - struct in_device *idev; - int n = 0; - - ASSERT(maxbufs > 0); - - rtnl_lock(); - for_each_netdev(net->net, dev) { - if (dev->type == ARPHRD_LOOPBACK && !wantloopback) - continue; - idev = __in_dev_get_rtnl(dev); - if (!idev) - continue; - for_primary_ifa(idev) { - bufs[n].address.s_addr = ifa->ifa_address; - bufs[n].netmask.s_addr = ifa->ifa_mask; - bufs[n].mtu = dev->mtu; - n++; - if (n >= maxbufs) - goto out; - } endfor_ifa(idev); - } -out: - rtnl_unlock(); - return n; -} diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 367dc2a0f84a..3515ca64e638 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -26,7 +26,7 @@ struct in_device { struct net_device *dev; refcount_t refcnt; int dead; - struct in_ifaddr *ifa_list; /* IP ifaddr chain */ + struct in_ifaddr __rcu *ifa_list;/* IP ifaddr chain */ struct ip_mc_list __rcu *mc_list; /* IP multicast filter chain */ struct ip_mc_list __rcu * __rcu *mc_hash; @@ -136,7 +136,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) struct in_ifaddr { struct hlist_node hash; - struct in_ifaddr *ifa_next; + struct in_ifaddr __rcu *ifa_next; struct in_device *ifa_dev; struct rcu_head rcu_head; __be32 ifa_local; @@ -186,7 +186,7 @@ __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst, struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, __be32 mask); struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr); -static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) +static inline bool inet_ifa_match(__be32 addr, const struct in_ifaddr *ifa) { return !((addr^ifa->ifa_address)&ifa->ifa_mask); } @@ -206,14 +206,13 @@ static __inline__ bool bad_mask(__be32 mask, __be32 addr) return false; } -#define for_primary_ifa(in_dev) { struct in_ifaddr *ifa; \ - for (ifa = (in_dev)->ifa_list; ifa && !(ifa->ifa_flags&IFA_F_SECONDARY); ifa = ifa->ifa_next) +#define in_dev_for_each_ifa_rtnl(ifa, in_dev) \ + for (ifa = rtnl_dereference((in_dev)->ifa_list); ifa; \ + ifa = rtnl_dereference(ifa->ifa_next)) -#define for_ifa(in_dev) { struct in_ifaddr *ifa; \ - for (ifa = (in_dev)->ifa_list; ifa; ifa = ifa->ifa_next) - - -#define endfor_ifa(in_dev) } +#define in_dev_for_each_ifa_rcu(ifa, in_dev) \ + for (ifa = rcu_dereference((in_dev)->ifa_list); ifa; \ + ifa = rcu_dereference(ifa->ifa_next)) static inline struct in_device *__in_dev_get_rcu(const struct net_device *dev) { diff --git a/net/core/netpoll.c b/net/core/netpoll.c index dd8b1a460d64..2cf27da1baeb 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -696,16 +696,22 @@ int netpoll_setup(struct netpoll *np) if (!np->local_ip.ip) { if (!np->ipv6) { + const struct in_ifaddr *ifa; + in_dev = __in_dev_get_rtnl(ndev); + if (!in_dev) + goto put_noaddr; - if (!in_dev || !in_dev->ifa_list) { + ifa = rtnl_dereference(in_dev->ifa_list); + if (!ifa) { +put_noaddr: np_err(np, "no IP address for %s, aborting\n", np->dev_name); err = -EDESTADDRREQ; goto put; } - np->local_ip.ip = in_dev->ifa_list->ifa_local; + np->local_ip.ip = ifa->ifa_local; np_info(np, "local IP %pI4\n", &np->local_ip.ip); } else { #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 319ad5490fb3..4cd120dc30ad 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2125,9 +2125,11 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev) rcu_read_lock(); in_dev = __in_dev_get_rcu(pkt_dev->odev); if (in_dev) { - if (in_dev->ifa_list) { - pkt_dev->saddr_min = - in_dev->ifa_list->ifa_address; + const struct in_ifaddr *ifa; + + ifa = rcu_dereference(in_dev->ifa_list); + if (ifa) { + pkt_dev->saddr_min = ifa->ifa_address; pkt_dev->saddr_max = pkt_dev->saddr_min; } } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 701c5d113a34..ebaea05b4033 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -194,7 +194,8 @@ static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); -static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, +static void inet_del_ifa(struct in_device *in_dev, + struct in_ifaddr __rcu **ifap, int destroy); #ifdef CONFIG_SYSCTL static int devinet_sysctl_register(struct in_device *idev); @@ -300,8 +301,8 @@ static void in_dev_rcu_put(struct rcu_head *head) static void inetdev_destroy(struct in_device *in_dev) { - struct in_ifaddr *ifa; struct net_device *dev; + struct in_ifaddr *ifa; ASSERT_RTNL(); @@ -311,7 +312,7 @@ static void inetdev_destroy(struct in_device *in_dev) ip_mc_destroy_dev(in_dev); - while ((ifa = in_dev->ifa_list) != NULL) { + while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) { inet_del_ifa(in_dev, &in_dev->ifa_list, 0); inet_free_ifa(ifa); } @@ -327,30 +328,35 @@ static void inetdev_destroy(struct in_device *in_dev) int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) { + const struct in_ifaddr *ifa; + rcu_read_lock(); - for_primary_ifa(in_dev) { + in_dev_for_each_ifa_rcu(ifa, in_dev) { if (inet_ifa_match(a, ifa)) { if (!b || inet_ifa_match(b, ifa)) { rcu_read_unlock(); return 1; } } - } endfor_ifa(in_dev); + } rcu_read_unlock(); return 0; } -static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, - int destroy, struct nlmsghdr *nlh, u32 portid) +static void __inet_del_ifa(struct in_device *in_dev, + struct in_ifaddr __rcu **ifap, + int destroy, struct nlmsghdr *nlh, u32 portid) { struct in_ifaddr *promote = NULL; - struct in_ifaddr *ifa, *ifa1 = *ifap; - struct in_ifaddr *last_prim = in_dev->ifa_list; + struct in_ifaddr *ifa, *ifa1; + struct in_ifaddr *last_prim; struct in_ifaddr *prev_prom = NULL; int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); ASSERT_RTNL(); + ifa1 = rtnl_dereference(*ifap); + last_prim = rtnl_dereference(in_dev->ifa_list); if (in_dev->dead) goto no_promotions; @@ -359,9 +365,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, **/ if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { - struct in_ifaddr **ifap1 = &ifa1->ifa_next; + struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; - while ((ifa = *ifap1) != NULL) { + while ((ifa = rtnl_dereference(*ifap1)) != NULL) { if (!(ifa-&g |