Skip to content

Commit e960825

Browse files
committed
Merge branch 'inet_dump_ifaddr-no-rtnl'
Eric Dumazet says: ==================== inet: no longer use RTNL to protect inet_dump_ifaddr() This series convert inet so that a dump of addresses (ip -4 addr) no longer requires RTNL. ==================== Reviewed-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
2 parents 76f06cb + cdb2f80 commit e960825

File tree

2 files changed

+79
-92
lines changed

2 files changed

+79
-92
lines changed

net/core/dev.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,9 @@ static DECLARE_RWSEM(devnet_rename_sem);
180180

181181
static inline void dev_base_seq_inc(struct net *net)
182182
{
183-
while (++net->dev_base_seq == 0)
184-
;
183+
unsigned int val = net->dev_base_seq + 1;
184+
185+
WRITE_ONCE(net->dev_base_seq, val ?: 1);
185186
}
186187

187188
static inline struct hlist_head *dev_name_hash(struct net *net, const char *name)

net/ipv4/devinet.c

Lines changed: 76 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -713,34 +713,37 @@ static void check_lifetime(struct work_struct *work)
713713

714714
rcu_read_lock();
715715
hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
716-
unsigned long age;
716+
unsigned long age, tstamp;
717+
u32 preferred_lft;
718+
u32 valid_lft;
719+
u32 flags;
717720

718-
if (ifa->ifa_flags & IFA_F_PERMANENT)
721+
flags = READ_ONCE(ifa->ifa_flags);
722+
if (flags & IFA_F_PERMANENT)
719723
continue;
720724

725+
preferred_lft = READ_ONCE(ifa->ifa_preferred_lft);
726+
valid_lft = READ_ONCE(ifa->ifa_valid_lft);
727+
tstamp = READ_ONCE(ifa->ifa_tstamp);
721728
/* We try to batch several events at once. */
722-
age = (now - ifa->ifa_tstamp +
729+
age = (now - tstamp +
723730
ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
724731

725-
if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
726-
age >= ifa->ifa_valid_lft) {
732+
if (valid_lft != INFINITY_LIFE_TIME &&
733+
age >= valid_lft) {
727734
change_needed = true;
728-
} else if (ifa->ifa_preferred_lft ==
735+
} else if (preferred_lft ==
729736
INFINITY_LIFE_TIME) {
730737
continue;
731-
} else if (age >= ifa->ifa_preferred_lft) {
732-
if (time_before(ifa->ifa_tstamp +
733-
ifa->ifa_valid_lft * HZ, next))
734-
next = ifa->ifa_tstamp +
735-
ifa->ifa_valid_lft * HZ;
738+
} else if (age >= preferred_lft) {
739+
if (time_before(tstamp + valid_lft * HZ, next))
740+
next = tstamp + valid_lft * HZ;
736741

737-
if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
742+
if (!(flags & IFA_F_DEPRECATED))
738743
change_needed = true;
739-
} else if (time_before(ifa->ifa_tstamp +
740-
ifa->ifa_preferred_lft * HZ,
744+
} else if (time_before(tstamp + preferred_lft * HZ,
741745
next)) {
742-
next = ifa->ifa_tstamp +
743-
ifa->ifa_preferred_lft * HZ;
746+
next = tstamp + preferred_lft * HZ;
744747
}
745748
}
746749
rcu_read_unlock();
@@ -804,24 +807,26 @@ static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
804807
__u32 prefered_lft)
805808
{
806809
unsigned long timeout;
810+
u32 flags;
807811

808-
ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
812+
flags = ifa->ifa_flags & ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
809813

810814
timeout = addrconf_timeout_fixup(valid_lft, HZ);
811815
if (addrconf_finite_timeout(timeout))
812-
ifa->ifa_valid_lft = timeout;
816+
WRITE_ONCE(ifa->ifa_valid_lft, timeout);
813817
else
814-
ifa->ifa_flags |= IFA_F_PERMANENT;
818+
flags |= IFA_F_PERMANENT;
815819

816820
timeout = addrconf_timeout_fixup(prefered_lft, HZ);
817821
if (addrconf_finite_timeout(timeout)) {
818822
if (timeout == 0)
819-
ifa->ifa_flags |= IFA_F_DEPRECATED;
820-
ifa->ifa_preferred_lft = timeout;
823+
flags |= IFA_F_DEPRECATED;
824+
WRITE_ONCE(ifa->ifa_preferred_lft, timeout);
821825
}
822-
ifa->ifa_tstamp = jiffies;
826+
WRITE_ONCE(ifa->ifa_flags, flags);
827+
WRITE_ONCE(ifa->ifa_tstamp, jiffies);
823828
if (!ifa->ifa_cstamp)
824-
ifa->ifa_cstamp = ifa->ifa_tstamp;
829+
WRITE_ONCE(ifa->ifa_cstamp, ifa->ifa_tstamp);
825830
}
826831

827832
static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
@@ -1312,7 +1317,7 @@ static __be32 in_dev_select_addr(const struct in_device *in_dev,
13121317
const struct in_ifaddr *ifa;
13131318

13141319
in_dev_for_each_ifa_rcu(ifa, in_dev) {
1315-
if (ifa->ifa_flags & IFA_F_SECONDARY)
1320+
if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
13161321
continue;
13171322
if (ifa->ifa_scope != RT_SCOPE_LINK &&
13181323
ifa->ifa_scope <= scope)
@@ -1340,7 +1345,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13401345
localnet_scope = RT_SCOPE_LINK;
13411346

13421347
in_dev_for_each_ifa_rcu(ifa, in_dev) {
1343-
if (ifa->ifa_flags & IFA_F_SECONDARY)
1348+
if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
13441349
continue;
13451350
if (min(ifa->ifa_scope, localnet_scope) > scope)
13461351
continue;
@@ -1671,11 +1676,12 @@ static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16711676
return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16721677
}
16731678

1674-
static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1679+
static int inet_fill_ifaddr(struct sk_buff *skb, const struct in_ifaddr *ifa,
16751680
struct inet_fill_args *args)
16761681
{
16771682
struct ifaddrmsg *ifm;
16781683
struct nlmsghdr *nlh;
1684+
unsigned long tstamp;
16791685
u32 preferred, valid;
16801686

16811687
nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
@@ -1686,19 +1692,20 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
16861692
ifm = nlmsg_data(nlh);
16871693
ifm->ifa_family = AF_INET;
16881694
ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1689-
ifm->ifa_flags = ifa->ifa_flags;
1695+
ifm->ifa_flags = READ_ONCE(ifa->ifa_flags);
16901696
ifm->ifa_scope = ifa->ifa_scope;
16911697
ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16921698

16931699
if (args->netnsid >= 0 &&
16941700
nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
16951701
goto nla_put_failure;
16961702

1703+
tstamp = READ_ONCE(ifa->ifa_tstamp);
16971704
if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
1698-
preferred = ifa->ifa_preferred_lft;
1699-
valid = ifa->ifa_valid_lft;
1705+
preferred = READ_ONCE(ifa->ifa_preferred_lft);
1706+
valid = READ_ONCE(ifa->ifa_valid_lft);
17001707
if (preferred != INFINITY_LIFE_TIME) {
1701-
long tval = (jiffies - ifa->ifa_tstamp) / HZ;
1708+
long tval = (jiffies - tstamp) / HZ;
17021709

17031710
if (preferred > tval)
17041711
preferred -= tval;
@@ -1725,10 +1732,10 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
17251732
nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
17261733
(ifa->ifa_proto &&
17271734
nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
1728-
nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1735+
nla_put_u32(skb, IFA_FLAGS, ifm->ifa_flags) ||
17291736
(ifa->ifa_rt_priority &&
17301737
nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
1731-
put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
1738+
put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp,
17321739
preferred, valid))
17331740
goto nla_put_failure;
17341741

@@ -1798,15 +1805,15 @@ static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
17981805
}
17991806

18001807
static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
1801-
struct netlink_callback *cb, int s_ip_idx,
1808+
struct netlink_callback *cb, int *s_ip_idx,
18021809
struct inet_fill_args *fillargs)
18031810
{
18041811
struct in_ifaddr *ifa;
18051812
int ip_idx = 0;
18061813
int err;
18071814

1808-
in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1809-
if (ip_idx < s_ip_idx) {
1815+
in_dev_for_each_ifa_rcu(ifa, in_dev) {
1816+
if (ip_idx < *s_ip_idx) {
18101817
ip_idx++;
18111818
continue;
18121819
}
@@ -1818,9 +1825,9 @@ static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
18181825
ip_idx++;
18191826
}
18201827
err = 0;
1821-
1828+
ip_idx = 0;
18221829
done:
1823-
cb->args[2] = ip_idx;
1830+
*s_ip_idx = ip_idx;
18241831

18251832
return err;
18261833
}
@@ -1830,7 +1837,7 @@ static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
18301837
static u32 inet_base_seq(const struct net *net)
18311838
{
18321839
u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
1833-
net->dev_base_seq;
1840+
READ_ONCE(net->dev_base_seq);
18341841

18351842
/* Must not return 0 (see nl_dump_check_consistent()).
18361843
* Chose a value far away from 0.
@@ -1852,75 +1859,53 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18521859
};
18531860
struct net *net = sock_net(skb->sk);
18541861
struct net *tgt_net = net;
1855-
int h, s_h;
1856-
int idx, s_idx;
1857-
int s_ip_idx;
1858-
struct net_device *dev;
1862+
struct {
1863+
unsigned long ifindex;
1864+
int ip_idx;
1865+
} *ctx = (void *)cb->ctx;
18591866
struct in_device *in_dev;
1860-
struct hlist_head *head;
1867+
struct net_device *dev;
18611868
int err = 0;
18621869

1863-
s_h = cb->args[0];
1864-
s_idx = idx = cb->args[1];
1865-
s_ip_idx = cb->args[2];
1866-
1870+
rcu_read_lock();
18671871
if (cb->strict_check) {
18681872
err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18691873
skb->sk, cb);
18701874
if (err < 0)
1871-
goto put_tgt_net;
1875+
goto done;
18721876

1873-
err = 0;
18741877
if (fillargs.ifindex) {
1875-
dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1876-
if (!dev) {
1877-
err = -ENODEV;
1878-
goto put_tgt_net;
1879-
}
1880-
1881-
in_dev = __in_dev_get_rtnl(dev);
1882-
if (in_dev) {
1883-
err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
1884-
&fillargs);
1885-
}
1886-
goto put_tgt_net;
1887-
}
1888-
}
1889-
1890-
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1891-
idx = 0;
1892-
head = &tgt_net->dev_index_head[h];
1893-
rcu_read_lock();
1894-
cb->seq = inet_base_seq(tgt_net);
1895-
hlist_for_each_entry_rcu(dev, head, index_hlist) {
1896-
if (idx < s_idx)
1897-
goto cont;
1898-
if (h > s_h || idx > s_idx)
1899-
s_ip_idx = 0;
1878+
err = -ENODEV;
1879+
dev = dev_get_by_index_rcu(tgt_net, fillargs.ifindex);
1880+
if (!dev)
1881+
goto done;
19001882
in_dev = __in_dev_get_rcu(dev);
19011883
if (!in_dev)
1902-
goto cont;
1903-
1904-
err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
1905-
&fillargs);
1906-
if (err < 0) {
1907-
rcu_read_unlock();
19081884
goto done;
1909-
}
1910-
cont:
1911-
idx++;
1885+
err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
1886+
&fillargs);
1887+
goto done;
19121888
}
1913-
rcu_read_unlock();
19141889
}
19151890

1891+
cb->seq = inet_base_seq(tgt_net);
1892+
1893+
for_each_netdev_dump(net, dev, ctx->ifindex) {
1894+
in_dev = __in_dev_get_rcu(dev);
1895+
if (!in_dev)
1896+
continue;
1897+
err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
1898+
&fillargs);
1899+
if (err < 0)
1900+
goto done;
1901+
}
19161902
done:
1917-
cb->args[0] = h;
1918-
cb->args[1] = idx;
1919-
put_tgt_net:
1903+
if (err < 0 && likely(skb->len))
1904+
err = skb->len;
19201905
if (fillargs.netnsid >= 0)
19211906
put_net(tgt_net);
1922-
1923-
return skb->len ? : err;
1907+
rcu_read_unlock();
1908+
return err;
19241909
}
19251910

19261911
static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
@@ -2811,7 +2796,8 @@ void __init devinet_init(void)
28112796

28122797
rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
28132798
rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2814-
rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
2799+
rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr,
2800+
RTNL_FLAG_DUMP_UNLOCKED);
28152801
rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
28162802
inet_netconf_dump_devconf,
28172803
RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED);

0 commit comments

Comments
 (0)