netns: fix GFP flags in rtnl_net_notifyid()
In rtnl_net_notifyid(), we certainly can't pass a null GFP flag to
rtnl_notify(). A GFP_KERNEL flag would be fine in most circumstances,
but there are a few paths calling rtnl_net_notifyid() from atomic
context or from RCU critical sections. The later also precludes the use
of gfp_any() as it wouldn't detect the RCU case. Also, the nlmsg_new()
call is wrong too, as it uses GFP_KERNEL unconditionally.
Therefore, we need to pass the GFP flags as parameter and propagate it
through function calls until the proper flags can be determined.
In most cases, GFP_KERNEL is fine. The exceptions are:
* openvswitch: ovs_vport_cmd_get() and ovs_vport_cmd_dump()
indirectly call rtnl_net_notifyid() from RCU critical section,
* rtnetlink: rtmsg_ifinfo_build_skb() already receives GFP flags as
parameter.
Also, in ovs_vport_cmd_build_info(), let's change the GFP flags used
by nlmsg_new(). The function is allowed to sleep, so better make the
flags consistent with the ones used in the following
ovs_vport_cmd_fill_info() call.
Found by code inspection.
Fixes: 9a9634545c
("netns: notify netns id events")
Signed-off-by: Guillaume Nault <gnault@redhat.com>
Acked-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
16d6528792
commit
d4e4fdf9e4
|
@ -342,7 +342,7 @@ static inline struct net *read_pnet(const possible_net_t *pnet)
|
||||||
#define __net_initconst __initconst
|
#define __net_initconst __initconst
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int peernet2id_alloc(struct net *net, struct net *peer);
|
int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp);
|
||||||
int peernet2id(struct net *net, struct net *peer);
|
int peernet2id(struct net *net, struct net *peer);
|
||||||
bool peernet_has_id(struct net *net, struct net *peer);
|
bool peernet_has_id(struct net *net, struct net *peer);
|
||||||
struct net *get_net_ns_by_id(struct net *net, int id);
|
struct net *get_net_ns_by_id(struct net *net, int id);
|
||||||
|
|
|
@ -9770,7 +9770,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
|
||||||
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
|
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
|
||||||
rcu_barrier();
|
rcu_barrier();
|
||||||
|
|
||||||
new_nsid = peernet2id_alloc(dev_net(dev), net);
|
new_nsid = peernet2id_alloc(dev_net(dev), net, GFP_KERNEL);
|
||||||
/* If there is an ifindex conflict assign a new one */
|
/* If there is an ifindex conflict assign a new one */
|
||||||
if (__dev_get_by_index(net, dev->ifindex))
|
if (__dev_get_by_index(net, dev->ifindex))
|
||||||
new_ifindex = dev_new_index(net);
|
new_ifindex = dev_new_index(net);
|
||||||
|
|
|
@ -246,11 +246,11 @@ static int __peernet2id(struct net *net, struct net *peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
||||||
struct nlmsghdr *nlh);
|
struct nlmsghdr *nlh, gfp_t gfp);
|
||||||
/* This function returns the id of a peer netns. If no id is assigned, one will
|
/* This function returns the id of a peer netns. If no id is assigned, one will
|
||||||
* be allocated and returned.
|
* be allocated and returned.
|
||||||
*/
|
*/
|
||||||
int peernet2id_alloc(struct net *net, struct net *peer)
|
int peernet2id_alloc(struct net *net, struct net *peer, gfp_t gfp)
|
||||||
{
|
{
|
||||||
bool alloc = false, alive = false;
|
bool alloc = false, alive = false;
|
||||||
int id;
|
int id;
|
||||||
|
@ -269,7 +269,7 @@ int peernet2id_alloc(struct net *net, struct net *peer)
|
||||||
id = __peernet2id_alloc(net, peer, &alloc);
|
id = __peernet2id_alloc(net, peer, &alloc);
|
||||||
spin_unlock_bh(&net->nsid_lock);
|
spin_unlock_bh(&net->nsid_lock);
|
||||||
if (alloc && id >= 0)
|
if (alloc && id >= 0)
|
||||||
rtnl_net_notifyid(net, RTM_NEWNSID, id, 0, NULL);
|
rtnl_net_notifyid(net, RTM_NEWNSID, id, 0, NULL, gfp);
|
||||||
if (alive)
|
if (alive)
|
||||||
put_net(peer);
|
put_net(peer);
|
||||||
return id;
|
return id;
|
||||||
|
@ -534,7 +534,8 @@ static void unhash_nsid(struct net *net, struct net *last)
|
||||||
idr_remove(&tmp->netns_ids, id);
|
idr_remove(&tmp->netns_ids, id);
|
||||||
spin_unlock_bh(&tmp->nsid_lock);
|
spin_unlock_bh(&tmp->nsid_lock);
|
||||||
if (id >= 0)
|
if (id >= 0)
|
||||||
rtnl_net_notifyid(tmp, RTM_DELNSID, id, 0, NULL);
|
rtnl_net_notifyid(tmp, RTM_DELNSID, id, 0, NULL,
|
||||||
|
GFP_KERNEL);
|
||||||
if (tmp == last)
|
if (tmp == last)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -767,7 +768,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
spin_unlock_bh(&net->nsid_lock);
|
spin_unlock_bh(&net->nsid_lock);
|
||||||
if (err >= 0) {
|
if (err >= 0) {
|
||||||
rtnl_net_notifyid(net, RTM_NEWNSID, err, NETLINK_CB(skb).portid,
|
rtnl_net_notifyid(net, RTM_NEWNSID, err, NETLINK_CB(skb).portid,
|
||||||
nlh);
|
nlh, GFP_KERNEL);
|
||||||
err = 0;
|
err = 0;
|
||||||
} else if (err == -ENOSPC && nsid >= 0) {
|
} else if (err == -ENOSPC && nsid >= 0) {
|
||||||
err = -EEXIST;
|
err = -EEXIST;
|
||||||
|
@ -1055,7 +1056,7 @@ end:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
||||||
struct nlmsghdr *nlh)
|
struct nlmsghdr *nlh, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct net_fill_args fillargs = {
|
struct net_fill_args fillargs = {
|
||||||
.portid = portid,
|
.portid = portid,
|
||||||
|
@ -1066,7 +1067,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
||||||
struct sk_buff *msg;
|
struct sk_buff *msg;
|
||||||
int err = -ENOMEM;
|
int err = -ENOMEM;
|
||||||
|
|
||||||
msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
|
msg = nlmsg_new(rtnl_net_get_size(), gfp);
|
||||||
if (!msg)
|
if (!msg)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1074,7 +1075,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id, u32 portid,
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
rtnl_notify(msg, net, portid, RTNLGRP_NSID, nlh, 0);
|
rtnl_notify(msg, net, portid, RTNLGRP_NSID, nlh, gfp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
|
|
|
@ -1523,7 +1523,7 @@ static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb,
|
||||||
|
|
||||||
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
|
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
|
||||||
const struct net_device *dev,
|
const struct net_device *dev,
|
||||||
struct net *src_net)
|
struct net *src_net, gfp_t gfp)
|
||||||
{
|
{
|
||||||
bool put_iflink = false;
|
bool put_iflink = false;
|
||||||
|
|
||||||
|
@ -1531,7 +1531,7 @@ static int rtnl_fill_link_netnsid(struct sk_buff *skb,
|
||||||
struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
|
struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
|
||||||
|
|
||||||
if (!net_eq(dev_net(dev), link_net)) {
|
if (!net_eq(dev_net(dev), link_net)) {
|
||||||
int id = peernet2id_alloc(src_net, link_net);
|
int id = peernet2id_alloc(src_net, link_net, gfp);
|
||||||
|
|
||||||
if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
|
if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
@ -1589,7 +1589,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
|
||||||
int type, u32 pid, u32 seq, u32 change,
|
int type, u32 pid, u32 seq, u32 change,
|
||||||
unsigned int flags, u32 ext_filter_mask,
|
unsigned int flags, u32 ext_filter_mask,
|
||||||
u32 event, int *new_nsid, int new_ifindex,
|
u32 event, int *new_nsid, int new_ifindex,
|
||||||
int tgt_netnsid)
|
int tgt_netnsid, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct ifinfomsg *ifm;
|
struct ifinfomsg *ifm;
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
@ -1681,7 +1681,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtnl_fill_link_netnsid(skb, dev, src_net))
|
if (rtnl_fill_link_netnsid(skb, dev, src_net, gfp))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
if (new_nsid &&
|
if (new_nsid &&
|
||||||
|
@ -2001,7 +2001,7 @@ walk_entries:
|
||||||
NETLINK_CB(cb->skb).portid,
|
NETLINK_CB(cb->skb).portid,
|
||||||
nlh->nlmsg_seq, 0, flags,
|
nlh->nlmsg_seq, 0, flags,
|
||||||
ext_filter_mask, 0, NULL, 0,
|
ext_filter_mask, 0, NULL, 0,
|
||||||
netnsid);
|
netnsid, GFP_KERNEL);
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (likely(skb->len))
|
if (likely(skb->len))
|
||||||
|
@ -3360,7 +3360,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||||
err = rtnl_fill_ifinfo(nskb, dev, net,
|
err = rtnl_fill_ifinfo(nskb, dev, net,
|
||||||
RTM_NEWLINK, NETLINK_CB(skb).portid,
|
RTM_NEWLINK, NETLINK_CB(skb).portid,
|
||||||
nlh->nlmsg_seq, 0, 0, ext_filter_mask,
|
nlh->nlmsg_seq, 0, 0, ext_filter_mask,
|
||||||
0, NULL, 0, netnsid);
|
0, NULL, 0, netnsid, GFP_KERNEL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
/* -EMSGSIZE implies BUG in if_nlmsg_size */
|
/* -EMSGSIZE implies BUG in if_nlmsg_size */
|
||||||
WARN_ON(err == -EMSGSIZE);
|
WARN_ON(err == -EMSGSIZE);
|
||||||
|
@ -3472,7 +3472,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
|
||||||
|
|
||||||
err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
|
err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
|
||||||
type, 0, 0, change, 0, 0, event,
|
type, 0, 0, change, 0, 0, event,
|
||||||
new_nsid, new_ifindex, -1);
|
new_nsid, new_ifindex, -1, flags);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
|
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
|
||||||
WARN_ON(err == -EMSGSIZE);
|
WARN_ON(err == -EMSGSIZE);
|
||||||
|
|
|
@ -1881,7 +1881,7 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = {
|
||||||
/* Called with ovs_mutex or RCU read lock. */
|
/* Called with ovs_mutex or RCU read lock. */
|
||||||
static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
|
static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
|
||||||
struct net *net, u32 portid, u32 seq,
|
struct net *net, u32 portid, u32 seq,
|
||||||
u32 flags, u8 cmd)
|
u32 flags, u8 cmd, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct ovs_header *ovs_header;
|
struct ovs_header *ovs_header;
|
||||||
struct ovs_vport_stats vport_stats;
|
struct ovs_vport_stats vport_stats;
|
||||||
|
@ -1902,7 +1902,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
if (!net_eq(net, dev_net(vport->dev))) {
|
if (!net_eq(net, dev_net(vport->dev))) {
|
||||||
int id = peernet2id_alloc(net, dev_net(vport->dev));
|
int id = peernet2id_alloc(net, dev_net(vport->dev), gfp);
|
||||||
|
|
||||||
if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
|
if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
@ -1943,11 +1943,12 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd);
|
retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd,
|
||||||
|
GFP_KERNEL);
|
||||||
BUG_ON(retval < 0);
|
BUG_ON(retval < 0);
|
||||||
|
|
||||||
return skb;
|
return skb;
|
||||||
|
@ -2089,7 +2090,7 @@ restart:
|
||||||
|
|
||||||
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
||||||
info->snd_portid, info->snd_seq, 0,
|
info->snd_portid, info->snd_seq, 0,
|
||||||
OVS_VPORT_CMD_NEW);
|
OVS_VPORT_CMD_NEW, GFP_KERNEL);
|
||||||
|
|
||||||
new_headroom = netdev_get_fwd_headroom(vport->dev);
|
new_headroom = netdev_get_fwd_headroom(vport->dev);
|
||||||
|
|
||||||
|
@ -2150,7 +2151,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
|
||||||
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
||||||
info->snd_portid, info->snd_seq, 0,
|
info->snd_portid, info->snd_seq, 0,
|
||||||
OVS_VPORT_CMD_SET);
|
OVS_VPORT_CMD_SET, GFP_KERNEL);
|
||||||
BUG_ON(err < 0);
|
BUG_ON(err < 0);
|
||||||
|
|
||||||
ovs_unlock();
|
ovs_unlock();
|
||||||
|
@ -2190,7 +2191,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
|
||||||
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
||||||
info->snd_portid, info->snd_seq, 0,
|
info->snd_portid, info->snd_seq, 0,
|
||||||
OVS_VPORT_CMD_DEL);
|
OVS_VPORT_CMD_DEL, GFP_KERNEL);
|
||||||
BUG_ON(err < 0);
|
BUG_ON(err < 0);
|
||||||
|
|
||||||
/* the vport deletion may trigger dp headroom update */
|
/* the vport deletion may trigger dp headroom update */
|
||||||
|
@ -2237,7 +2238,7 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
|
||||||
goto exit_unlock_free;
|
goto exit_unlock_free;
|
||||||
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
||||||
info->snd_portid, info->snd_seq, 0,
|
info->snd_portid, info->snd_seq, 0,
|
||||||
OVS_VPORT_CMD_GET);
|
OVS_VPORT_CMD_GET, GFP_ATOMIC);
|
||||||
BUG_ON(err < 0);
|
BUG_ON(err < 0);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
@ -2273,7 +2274,8 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
NETLINK_CB(cb->skb).portid,
|
NETLINK_CB(cb->skb).portid,
|
||||||
cb->nlh->nlmsg_seq,
|
cb->nlh->nlmsg_seq,
|
||||||
NLM_F_MULTI,
|
NLM_F_MULTI,
|
||||||
OVS_VPORT_CMD_GET) < 0)
|
OVS_VPORT_CMD_GET,
|
||||||
|
GFP_ATOMIC) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
j++;
|
j++;
|
||||||
|
|
Loading…
Reference in New Issue