Skip to content

Commit 6ab0f86

Browse files
q2venPaolo Abeni
authored andcommitted
rtnetlink: Protect struct rtnl_af_ops with SRCU.
Once RTNL is replaced with rtnl_net_lock(), we need a mechanism to guarantee that rtnl_af_ops is alive during inflight RTM_SETLINK even when its module is being unloaded. Let's use SRCU to protect ops. rtnl_af_lookup() now iterates rtnl_af_ops under RCU and returns SRCU-protected ops pointer. The caller must call rtnl_af_put() to release the pointer after the use. Also, rtnl_af_unregister() unlinks the ops first and calls synchronize_srcu() to wait for inflight RTM_SETLINK requests to complete. Note that rtnl_af_ops needs to be protected by its dedicated lock when RTNL is removed. Note also that BUG_ON() in do_setlink() is changed to the normal error handling as a different af_ops might be found after validate_linkmsg(). Signed-off-by: Kuniyuki Iwashima <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 26eebdc commit 6ab0f86

File tree

2 files changed

+51
-17
lines changed

2 files changed

+51
-17
lines changed

include/net/rtnetlink.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops);
172172
/**
173173
* struct rtnl_af_ops - rtnetlink address family operations
174174
*
175-
* @list: Used internally
175+
* @list: Used internally, protected by RTNL and SRCU
176+
* @srcu: Used internally
176177
* @family: Address family
177178
* @fill_link_af: Function to fill IFLA_AF_SPEC with address family
178179
* specific netlink attributes.
@@ -185,6 +186,8 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops);
185186
*/
186187
struct rtnl_af_ops {
187188
struct list_head list;
189+
struct srcu_struct srcu;
190+
188191
int family;
189192

190193
int (*fill_link_af)(struct sk_buff *skb,

net/core/rtnetlink.c

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -666,18 +666,31 @@ static size_t rtnl_link_get_size(const struct net_device *dev)
666666

667667
static LIST_HEAD(rtnl_af_ops);
668668

669-
static const struct rtnl_af_ops *rtnl_af_lookup(const int family)
669+
static struct rtnl_af_ops *rtnl_af_lookup(const int family, int *srcu_index)
670670
{
671-
const struct rtnl_af_ops *ops;
671+
struct rtnl_af_ops *ops;
672672

673673
ASSERT_RTNL();
674674

675-
list_for_each_entry(ops, &rtnl_af_ops, list) {
676-
if (ops->family == family)
677-
return ops;
675+
rcu_read_lock();
676+
677+
list_for_each_entry_rcu(ops, &rtnl_af_ops, list) {
678+
if (ops->family == family) {
679+
*srcu_index = srcu_read_lock(&ops->srcu);
680+
goto unlock;
681+
}
678682
}
679683

680-
return NULL;
684+
ops = NULL;
685+
unlock:
686+
rcu_read_unlock();
687+
688+
return ops;
689+
}
690+
691+
static void rtnl_af_put(struct rtnl_af_ops *ops, int srcu_index)
692+
{
693+
srcu_read_unlock(&ops->srcu, srcu_index);
681694
}
682695

683696
/**
@@ -688,6 +701,11 @@ static const struct rtnl_af_ops *rtnl_af_lookup(const int family)
688701
*/
689702
int rtnl_af_register(struct rtnl_af_ops *ops)
690703
{
704+
int err = init_srcu_struct(&ops->srcu);
705+
706+
if (err)
707+
return err;
708+
691709
rtnl_lock();
692710
list_add_tail_rcu(&ops->list, &rtnl_af_ops);
693711
rtnl_unlock();
@@ -707,6 +725,8 @@ void rtnl_af_unregister(struct rtnl_af_ops *ops)
707725
rtnl_unlock();
708726

709727
synchronize_rcu();
728+
synchronize_srcu(&ops->srcu);
729+
cleanup_srcu_struct(&ops->srcu);
710730
}
711731
EXPORT_SYMBOL_GPL(rtnl_af_unregister);
712732

@@ -2579,20 +2599,24 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[],
25792599
int rem, err;
25802600

25812601
nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
2582-
const struct rtnl_af_ops *af_ops;
2602+
struct rtnl_af_ops *af_ops;
2603+
int af_ops_srcu_index;
25832604

2584-
af_ops = rtnl_af_lookup(nla_type(af));
2605+
af_ops = rtnl_af_lookup(nla_type(af), &af_ops_srcu_index);
25852606
if (!af_ops)
25862607
return -EAFNOSUPPORT;
25872608

25882609
if (!af_ops->set_link_af)
2589-
return -EOPNOTSUPP;
2590-
2591-
if (af_ops->validate_link_af) {
2610+
err = -EOPNOTSUPP;
2611+
else if (af_ops->validate_link_af)
25922612
err = af_ops->validate_link_af(dev, af, extack);
2593-
if (err < 0)
2594-
return err;
2595-
}
2613+
else
2614+
err = 0;
2615+
2616+
rtnl_af_put(af_ops, af_ops_srcu_index);
2617+
2618+
if (err < 0)
2619+
return err;
25962620
}
25972621
}
25982622

@@ -3172,11 +3196,18 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev,
31723196
int rem;
31733197

31743198
nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
3175-
const struct rtnl_af_ops *af_ops;
3199+
struct rtnl_af_ops *af_ops;
3200+
int af_ops_srcu_index;
31763201

3177-
BUG_ON(!(af_ops = rtnl_af_lookup(nla_type(af))));
3202+
af_ops = rtnl_af_lookup(nla_type(af), &af_ops_srcu_index);
3203+
if (!af_ops) {
3204+
err = -EAFNOSUPPORT;
3205+
goto errout;
3206+
}
31783207

31793208
err = af_ops->set_link_af(dev, af, extack);
3209+
rtnl_af_put(af_ops, af_ops_srcu_index);
3210+
31803211
if (err < 0)
31813212
goto errout;
31823213

0 commit comments

Comments
 (0)