lwtunnel: fix autoload of lwt modules
Trying to add an mpls encap route when the MPLS modules are not loaded
hangs. For example:
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
$ ip route add 10.10.10.10/32 encap mpls 100 via inet 10.100.1.2
The ip command hangs:
root 880 826 0 21:25 pts/0 00:00:00 ip route add 10.10.10.10/32 encap mpls 100 via inet 10.100.1.2
$ cat /proc/880/stack
[<ffffffff81065a9b>] call_usermodehelper_exec+0xd6/0x134
[<ffffffff81065efc>] __request_module+0x27b/0x30a
[<ffffffff814542f6>] lwtunnel_build_state+0xe4/0x178
[<ffffffff814aa1e4>] fib_create_info+0x47f/0xdd4
[<ffffffff814ae451>] fib_table_insert+0x90/0x41f
[<ffffffff814a8010>] inet_rtm_newroute+0x4b/0x52
...
modprobe is trying to load rtnl-lwt-MPLS:
root 881 5 0 21:25 ? 00:00:00 /sbin/modprobe -q -- rtnl-lwt-MPLS
and it hangs after loading mpls_router:
$ cat /proc/881/stack
[<ffffffff81441537>] rtnl_lock+0x12/0x14
[<ffffffff8142ca2a>] register_netdevice_notifier+0x16/0x179
[<ffffffffa0033025>] mpls_init+0x25/0x1000 [mpls_router]
[<ffffffff81000471>] do_one_initcall+0x8e/0x13f
[<ffffffff81119961>] do_init_module+0x5a/0x1e5
[<ffffffff810bd070>] load_module+0x13bd/0x17d6
...
The problem is that lwtunnel_build_state is called with rtnl lock
held preventing mpls_init from registering.
Given the potential references held by the time lwtunnel_build_state it
can not drop the rtnl lock to the load module. So, extract the module
loading code from lwtunnel_build_state into a new function to validate
the encap type. The new function is called while converting the user
request into a fib_config which is well before any table, device or
fib entries are examined.
Fixes: 745041e2aa
("lwtunnel: autoload of lwt modules")
Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
719ca81114
commit
9ed59592e3
|
@ -105,6 +105,8 @@ int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *op,
|
|||
unsigned int num);
|
||||
int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
|
||||
unsigned int num);
|
||||
int lwtunnel_valid_encap_type(u16 encap_type);
|
||||
int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len);
|
||||
int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
|
||||
struct nlattr *encap,
|
||||
unsigned int family, const void *cfg,
|
||||
|
@ -168,6 +170,15 @@ static inline int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *op,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int lwtunnel_valid_encap_type(u16 encap_type)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int len)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
|
||||
struct nlattr *encap,
|
||||
unsigned int family, const void *cfg,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <net/lwtunnel.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <net/ip6_fib.h>
|
||||
#include <net/nexthop.h>
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
|
@ -114,18 +115,6 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
|
|||
ret = -EOPNOTSUPP;
|
||||
rcu_read_lock();
|
||||
ops = rcu_dereference(lwtun_encaps[encap_type]);
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!ops) {
|
||||
const char *encap_type_str = lwtunnel_encap_str(encap_type);
|
||||
|
||||
if (encap_type_str) {
|
||||
rcu_read_unlock();
|
||||
request_module("rtnl-lwt-%s", encap_type_str);
|
||||
rcu_read_lock();
|
||||
ops = rcu_dereference(lwtun_encaps[encap_type]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (likely(ops && ops->build_state))
|
||||
ret = ops->build_state(dev, encap, family, cfg, lws);
|
||||
rcu_read_unlock();
|
||||
|
@ -134,6 +123,67 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
|
|||
}
|
||||
EXPORT_SYMBOL(lwtunnel_build_state);
|
||||
|
||||
int lwtunnel_valid_encap_type(u16 encap_type)
|
||||
{
|
||||
const struct lwtunnel_encap_ops *ops;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (encap_type == LWTUNNEL_ENCAP_NONE ||
|
||||
encap_type > LWTUNNEL_ENCAP_MAX)
|
||||
return ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ops = rcu_dereference(lwtun_encaps[encap_type]);
|
||||
rcu_read_unlock();
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!ops) {
|
||||
const char *encap_type_str = lwtunnel_encap_str(encap_type);
|
||||
|
||||
if (encap_type_str) {
|
||||
__rtnl_unlock();
|
||||
request_module("rtnl-lwt-%s", encap_type_str);
|
||||
rtnl_lock();
|
||||
|
||||
rcu_read_lock();
|
||||
ops = rcu_dereference(lwtun_encaps[encap_type]);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ops ? 0 : -EOPNOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(lwtunnel_valid_encap_type);
|
||||
|
||||
int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining)
|
||||
{
|
||||
struct rtnexthop *rtnh = (struct rtnexthop *)attr;
|
||||
struct nlattr *nla_entype;
|
||||
struct nlattr *attrs;
|
||||
struct nlattr *nla;
|
||||
u16 encap_type;
|
||||
int attrlen;
|
||||
|
||||
while (rtnh_ok(rtnh, remaining)) {
|
||||
attrlen = rtnh_attrlen(rtnh);
|
||||
if (attrlen > 0) {
|
||||
attrs = rtnh_attrs(rtnh);
|
||||
nla = nla_find(attrs, attrlen, RTA_ENCAP);
|
||||
nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
|
||||
|
||||
if (nla_entype) {
|
||||
encap_type = nla_get_u16(nla_entype);
|
||||
|
||||
if (lwtunnel_valid_encap_type(encap_type) != 0)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
rtnh = rtnh_next(rtnh, &remaining);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
|
||||
|
||||
void lwtstate_free(struct lwtunnel_state *lws)
|
||||
{
|
||||
const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <net/rtnetlink.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/l3mdev.h>
|
||||
#include <net/lwtunnel.h>
|
||||
#include <trace/events/fib.h>
|
||||
|
||||
#ifndef CONFIG_IP_MULTIPLE_TABLES
|
||||
|
@ -677,6 +678,10 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
|
|||
cfg->fc_mx_len = nla_len(attr);
|
||||
break;
|
||||
case RTA_MULTIPATH:
|
||||
err = lwtunnel_valid_encap_type_attr(nla_data(attr),
|
||||
nla_len(attr));
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
cfg->fc_mp = nla_data(attr);
|
||||
cfg->fc_mp_len = nla_len(attr);
|
||||
break;
|
||||
|
@ -691,6 +696,9 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
|
|||
break;
|
||||
case RTA_ENCAP_TYPE:
|
||||
cfg->fc_encap_type = nla_get_u16(attr);
|
||||
err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2896,6 +2896,11 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
if (tb[RTA_MULTIPATH]) {
|
||||
cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
|
||||
cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
|
||||
|
||||
err = lwtunnel_valid_encap_type_attr(cfg->fc_mp,
|
||||
cfg->fc_mp_len);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (tb[RTA_PREF]) {
|
||||
|
@ -2909,9 +2914,14 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|||
if (tb[RTA_ENCAP])
|
||||
cfg->fc_encap = tb[RTA_ENCAP];
|
||||
|
||||
if (tb[RTA_ENCAP_TYPE])
|
||||
if (tb[RTA_ENCAP_TYPE]) {
|
||||
cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
|
||||
|
||||
err = lwtunnel_valid_encap_type(cfg->fc_encap_type);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (tb[RTA_EXPIRES]) {
|
||||
unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
|
||||
|
||||
|
|
Loading…
Reference in New Issue