Commit feb3cf2e authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents b9f88982 2638eb8b
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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) {
+7 −2
Original line number Diff line number Diff line
@@ -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;
}
+5 −2
Original line number Diff line number Diff line
@@ -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);
		}
	}
+4 −2
Original line number Diff line number Diff line
@@ -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);
		}
	}
+8 −4
Original line number Diff line number Diff line
@@ -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);
		if (in) {
			struct in_ifaddr *ifa;

			ifa = rcu_dereference(in->ifa_list);
			if (ifa)
				local_ipaddr = ntohl(ifa->ifa_address);
		}

		rcu_read_unlock();
	} else {
Loading