Skip to content

Commit

Permalink
Merge branch 'net-remove-rtnl-from-fib_seq_sum'
Browse files Browse the repository at this point in the history
Eric Dumazet says:

====================
net: remove RTNL from fib_seq_sum()

This series is inspired by a syzbot report showing
rtnl contention and one thread blocked in:

7 locks held by syz-executor/10835:
  #0: ffff888033390420 (sb_writers#8){.+.+}-{0:0}, at: file_start_write include/linux/fs.h:2931 [inline]
  #0: ffff888033390420 (sb_writers#8){.+.+}-{0:0}, at: vfs_write+0x224/0xc90 fs/read_write.c:679
  #1: ffff88806df6bc88 (&of->mutex){+.+.}-{3:3}, at: kernfs_fop_write_iter+0x1ea/0x500 fs/kernfs/file.c:325
  #2: ffff888026fcf3c8 (kn->active#50){.+.+}-{0:0}, at: kernfs_fop_write_iter+0x20e/0x500 fs/kernfs/file.c:326
  #3: ffffffff8f56f848 (nsim_bus_dev_list_lock){+.+.}-{3:3}, at: new_device_store+0x1b4/0x890 drivers/net/netdevsim/bus.c:166
  #4: ffff88805e0140e8 (&dev->mutex){....}-{3:3}, at: device_lock include/linux/device.h:1014 [inline]
  #4: ffff88805e0140e8 (&dev->mutex){....}-{3:3}, at: __device_attach+0x8e/0x520 drivers/base/dd.c:1005
  #5: ffff88805c5fb250 (&devlink->lock_key#55){+.+.}-{3:3}, at: nsim_drv_probe+0xcb/0xb80 drivers/net/netdevsim/dev.c:1534
  #6: ffffffff8fcd1748 (rtnl_mutex){+.+.}-{3:3}, at: fib_seq_sum+0x31/0x290 net/core/fib_notifier.c:46
====================

Link: https://patch.msgid.link/20241009184405.3752829-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
kuba-moo committed Oct 11, 2024
2 parents d677aeb + 2698acd commit 356d568
Show file tree
Hide file tree
Showing 14 changed files with 40 additions and 44 deletions.
2 changes: 1 addition & 1 deletion include/net/fib_notifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ enum fib_event_type {
struct fib_notifier_ops {
int family;
struct list_head list;
unsigned int (*fib_seq_read)(struct net *net);
unsigned int (*fib_seq_read)(const struct net *net);
int (*fib_dump)(struct net *net, struct notifier_block *nb,
struct netlink_ext_ack *extack);
struct module *owner;
Expand Down
2 changes: 1 addition & 1 deletion include/net/fib_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table);
bool fib_rule_matchall(const struct fib_rule *rule);
int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
struct netlink_ext_ack *extack);
unsigned int fib_rules_seq_read(struct net *net, int family);
unsigned int fib_rules_seq_read(const struct net *net, int family);

int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack);
Expand Down
8 changes: 4 additions & 4 deletions include/net/ip6_fib.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ struct fib6_table {
struct fib6_node tb6_root;
struct inet_peer_base tb6_peers;
unsigned int flags;
unsigned int fib_seq;
unsigned int fib_seq; /* writes protected by rtnl_mutex */
struct hlist_head tb6_gc_hlist; /* GC candidates */
#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
};
Expand Down Expand Up @@ -563,7 +563,7 @@ int call_fib6_notifiers(struct net *net, enum fib_event_type event_type,
int __net_init fib6_notifier_init(struct net *net);
void __net_exit fib6_notifier_exit(struct net *net);

unsigned int fib6_tables_seq_read(struct net *net);
unsigned int fib6_tables_seq_read(const struct net *net);
int fib6_tables_dump(struct net *net, struct notifier_block *nb,
struct netlink_ext_ack *extack);

Expand Down Expand Up @@ -632,7 +632,7 @@ void fib6_rules_cleanup(void);
bool fib6_rule_default(const struct fib_rule *rule);
int fib6_rules_dump(struct net *net, struct notifier_block *nb,
struct netlink_ext_ack *extack);
unsigned int fib6_rules_seq_read(struct net *net);
unsigned int fib6_rules_seq_read(const struct net *net);

static inline bool fib6_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
Expand Down Expand Up @@ -676,7 +676,7 @@ static inline int fib6_rules_dump(struct net *net, struct notifier_block *nb,
{
return 0;
}
static inline unsigned int fib6_rules_seq_read(struct net *net)
static inline unsigned int fib6_rules_seq_read(const struct net *net)
{
return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions include/net/ip_fib.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ static inline int fib4_rules_dump(struct net *net, struct notifier_block *nb,
return 0;
}

static inline unsigned int fib4_rules_seq_read(struct net *net)
static inline unsigned int fib4_rules_seq_read(const struct net *net)
{
return 0;
}
Expand Down Expand Up @@ -411,7 +411,7 @@ static inline bool fib4_has_custom_rules(const struct net *net)
bool fib4_rule_default(const struct fib_rule *rule);
int fib4_rules_dump(struct net *net, struct notifier_block *nb,
struct netlink_ext_ack *extack);
unsigned int fib4_rules_seq_read(struct net *net);
unsigned int fib4_rules_seq_read(const struct net *net);

static inline bool fib4_rules_early_flow_dissect(struct net *net,
struct sk_buff *skb,
Expand Down
2 changes: 1 addition & 1 deletion include/net/netns/ipv4.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ struct netns_ipv4 {
#endif

struct fib_notifier_ops *notifier_ops;
unsigned int fib_seq; /* protected by rtnl_mutex */
unsigned int fib_seq; /* writes protected by rtnl_mutex */

struct fib_notifier_ops *ipmr_notifier_ops;
unsigned int ipmr_seq; /* protected by rtnl_mutex */
Expand Down
2 changes: 0 additions & 2 deletions net/core/fib_notifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ static unsigned int fib_seq_sum(struct net *net)
struct fib_notifier_ops *ops;
unsigned int fib_seq = 0;

rtnl_lock();
rcu_read_lock();
list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
if (!try_module_get(ops->owner))
Expand All @@ -52,7 +51,6 @@ static unsigned int fib_seq_sum(struct net *net)
module_put(ops->owner);
}
rcu_read_unlock();
rtnl_unlock();

return fib_seq;
}
Expand Down
14 changes: 8 additions & 6 deletions net/core/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ static void notify_rule_change(int event, struct fib_rule *rule,
struct fib_rules_ops *ops, struct nlmsghdr *nlh,
u32 pid);

static struct fib_rules_ops *lookup_rules_ops(struct net *net, int family)
static struct fib_rules_ops *lookup_rules_ops(const struct net *net,
int family)
{
struct fib_rules_ops *ops;

Expand Down Expand Up @@ -370,7 +371,9 @@ static int call_fib_rule_notifiers(struct net *net,
.rule = rule,
};

ops->fib_rules_seq++;
ASSERT_RTNL();
/* Paired with READ_ONCE() in fib_rules_seq() */
WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1);
return call_fib_notifiers(net, event_type, &info.info);
}

Expand All @@ -397,17 +400,16 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
}
EXPORT_SYMBOL_GPL(fib_rules_dump);

unsigned int fib_rules_seq_read(struct net *net, int family)
unsigned int fib_rules_seq_read(const struct net *net, int family)
{
unsigned int fib_rules_seq;
struct fib_rules_ops *ops;

ASSERT_RTNL();

ops = lookup_rules_ops(net, family);
if (!ops)
return 0;
fib_rules_seq = ops->fib_rules_seq;
/* Paired with WRITE_ONCE() in call_fib_rule_notifiers() */
fib_rules_seq = READ_ONCE(ops->fib_rules_seq);
rules_ops_put(ops);

return fib_rules_seq;
Expand Down
10 changes: 5 additions & 5 deletions net/ipv4/fib_notifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ int call_fib4_notifiers(struct net *net, enum fib_event_type event_type,
ASSERT_RTNL();

info->family = AF_INET;
net->ipv4.fib_seq++;
/* Paired with READ_ONCE() in fib4_seq_read() */
WRITE_ONCE(net->ipv4.fib_seq, net->ipv4.fib_seq + 1);
return call_fib_notifiers(net, event_type, info);
}

static unsigned int fib4_seq_read(struct net *net)
static unsigned int fib4_seq_read(const struct net *net)
{
ASSERT_RTNL();

return net->ipv4.fib_seq + fib4_rules_seq_read(net);
/* Paired with WRITE_ONCE() in call_fib4_notifiers() */
return READ_ONCE(net->ipv4.fib_seq) + fib4_rules_seq_read(net);
}

static int fib4_dump(struct net *net, struct notifier_block *nb,
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/fib_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ int fib4_rules_dump(struct net *net, struct notifier_block *nb,
return fib_rules_dump(net, nb, AF_INET, extack);
}

unsigned int fib4_rules_seq_read(struct net *net)
unsigned int fib4_rules_seq_read(const struct net *net)
{
return fib_rules_seq_read(net, AF_INET);
}
Expand Down
10 changes: 4 additions & 6 deletions net/ipv4/ipmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ static int ipmr_rules_dump(struct net *net, struct notifier_block *nb,
return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR, extack);
}

static unsigned int ipmr_rules_seq_read(struct net *net)
static unsigned int ipmr_rules_seq_read(const struct net *net)
{
return fib_rules_seq_read(net, RTNL_FAMILY_IPMR);
}
Expand Down Expand Up @@ -346,7 +346,7 @@ static int ipmr_rules_dump(struct net *net, struct notifier_block *nb,
return 0;
}

static unsigned int ipmr_rules_seq_read(struct net *net)
static unsigned int ipmr_rules_seq_read(const struct net *net)
{
return 0;
}
Expand Down Expand Up @@ -3035,11 +3035,9 @@ static const struct net_protocol pim_protocol = {
};
#endif

static unsigned int ipmr_seq_read(struct net *net)
static unsigned int ipmr_seq_read(const struct net *net)
{
ASSERT_RTNL();

return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net);
return READ_ONCE(net->ipv4.ipmr_seq) + ipmr_rules_seq_read(net);
}

static int ipmr_dump(struct net *net, struct notifier_block *nb,
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/fib6_notifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ int call_fib6_notifiers(struct net *net, enum fib_event_type event_type,
return call_fib_notifiers(net, event_type, info);
}

static unsigned int fib6_seq_read(struct net *net)
static unsigned int fib6_seq_read(const struct net *net)
{
return fib6_tables_seq_read(net) + fib6_rules_seq_read(net);
}
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/fib6_rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ int fib6_rules_dump(struct net *net, struct notifier_block *nb,
return fib_rules_dump(net, nb, AF_INET6, extack);
}

unsigned int fib6_rules_seq_read(struct net *net)
unsigned int fib6_rules_seq_read(const struct net *net)
{
return fib_rules_seq_read(net, AF_INET6);
}
Expand Down
14 changes: 7 additions & 7 deletions net/ipv6/ip6_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,17 @@ static void __net_init fib6_tables_init(struct net *net)

#endif

unsigned int fib6_tables_seq_read(struct net *net)
unsigned int fib6_tables_seq_read(const struct net *net)
{
unsigned int h, fib_seq = 0;

rcu_read_lock();
for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
struct hlist_head *head = &net->ipv6.fib_table_hash[h];
struct fib6_table *tb;
const struct hlist_head *head = &net->ipv6.fib_table_hash[h];
const struct fib6_table *tb;

hlist_for_each_entry_rcu(tb, head, tb6_hlist)
fib_seq += tb->fib_seq;
fib_seq += READ_ONCE(tb->fib_seq);
}
rcu_read_unlock();

Expand Down Expand Up @@ -400,7 +400,7 @@ int call_fib6_entry_notifiers(struct net *net,
.rt = rt,
};

rt->fib6_table->fib_seq++;
WRITE_ONCE(rt->fib6_table->fib_seq, rt->fib6_table->fib_seq + 1);
return call_fib6_notifiers(net, event_type, &info.info);
}

Expand All @@ -416,7 +416,7 @@ int call_fib6_multipath_entry_notifiers(struct net *net,
.nsiblings = nsiblings,
};

rt->fib6_table->fib_seq++;
WRITE_ONCE(rt->fib6_table->fib_seq, rt->fib6_table->fib_seq + 1);
return call_fib6_notifiers(net, event_type, &info.info);
}

Expand All @@ -427,7 +427,7 @@ int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt)
.nsiblings = rt->fib6_nsiblings,
};

rt->fib6_table->fib_seq++;
WRITE_ONCE(rt->fib6_table->fib_seq, rt->fib6_table->fib_seq + 1);
return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info);
}

Expand Down
10 changes: 4 additions & 6 deletions net/ipv6/ip6mr.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack);
}

static unsigned int ip6mr_rules_seq_read(struct net *net)
static unsigned int ip6mr_rules_seq_read(const struct net *net)
{
return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR);
}
Expand Down Expand Up @@ -335,7 +335,7 @@ static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
return 0;
}

static unsigned int ip6mr_rules_seq_read(struct net *net)
static unsigned int ip6mr_rules_seq_read(const struct net *net)
{
return 0;
}
Expand Down Expand Up @@ -1260,11 +1260,9 @@ static int ip6mr_device_event(struct notifier_block *this,
return NOTIFY_DONE;
}

static unsigned int ip6mr_seq_read(struct net *net)
static unsigned int ip6mr_seq_read(const struct net *net)
{
ASSERT_RTNL();

return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net);
return READ_ONCE(net->ipv6.ipmr_seq) + ip6mr_rules_seq_read(net);
}

static int ip6mr_dump(struct net *net, struct notifier_block *nb,
Expand Down

0 comments on commit 356d568

Please sign in to comment.