提交 40400fdd 编写于 作者: G Guillaume Nault 提交者: Greg Kroah-Hartman

netns: fix GFP flags in rtnl_net_notifyid()

[ Upstream commit d4e4fdf9e4a27c87edb79b1478955075be141f67 ]

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: 9a963454 ("netns: notify netns id events")
Signed-off-by: NGuillaume Nault <gnault@redhat.com>
Acked-by: NNicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: NPravin B Shelar <pshelar@ovn.org>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 1d72dbb4
...@@ -322,7 +322,7 @@ static inline struct net *read_pnet(const possible_net_t *pnet) ...@@ -322,7 +322,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);
......
...@@ -9211,7 +9211,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char ...@@ -9211,7 +9211,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);
......
...@@ -226,11 +226,11 @@ static int __peernet2id(struct net *net, struct net *peer) ...@@ -226,11 +226,11 @@ static int __peernet2id(struct net *net, struct net *peer)
return __peernet2id_alloc(net, peer, &no); return __peernet2id_alloc(net, peer, &no);
} }
static void rtnl_net_notifyid(struct net *net, int cmd, int id); static void rtnl_net_notifyid(struct net *net, int cmd, int id, 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;
...@@ -249,7 +249,7 @@ int peernet2id_alloc(struct net *net, struct net *peer) ...@@ -249,7 +249,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); rtnl_net_notifyid(net, RTM_NEWNSID, id, gfp);
if (alive) if (alive)
put_net(peer); put_net(peer);
return id; return id;
...@@ -495,7 +495,8 @@ static void unhash_nsid(struct net *net, struct net *last) ...@@ -495,7 +495,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); rtnl_net_notifyid(tmp, RTM_DELNSID, id,
GFP_KERNEL);
if (tmp == last) if (tmp == last)
break; break;
} }
...@@ -720,7 +721,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -720,7 +721,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh,
err = alloc_netid(net, peer, nsid); err = alloc_netid(net, peer, nsid);
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); rtnl_net_notifyid(net, RTM_NEWNSID, err, GFP_KERNEL);
err = 0; err = 0;
} else if (err == -ENOSPC && nsid >= 0) { } else if (err == -ENOSPC && nsid >= 0) {
err = -EEXIST; err = -EEXIST;
...@@ -862,12 +863,12 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -862,12 +863,12 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
static void rtnl_net_notifyid(struct net *net, int cmd, int id) static void rtnl_net_notifyid(struct net *net, int cmd, int id, gfp_t gfp)
{ {
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;
...@@ -875,7 +876,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id) ...@@ -875,7 +876,7 @@ static void rtnl_net_notifyid(struct net *net, int cmd, int id)
if (err < 0) if (err < 0)
goto err_out; goto err_out;
rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, 0); rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, gfp);
return; return;
err_out: err_out:
......
...@@ -1519,7 +1519,7 @@ static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb, ...@@ -1519,7 +1519,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;
...@@ -1527,7 +1527,7 @@ static int rtnl_fill_link_netnsid(struct sk_buff *skb, ...@@ -1527,7 +1527,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;
...@@ -1585,7 +1585,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, ...@@ -1585,7 +1585,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;
...@@ -1677,7 +1677,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, ...@@ -1677,7 +1677,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 &&
...@@ -1933,7 +1933,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1933,7 +1933,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
cb->nlh->nlmsg_seq, 0, cb->nlh->nlmsg_seq, 0,
flags, 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))
...@@ -3215,7 +3215,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -3215,7 +3215,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);
...@@ -3325,7 +3325,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, ...@@ -3325,7 +3325,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);
......
...@@ -1843,7 +1843,7 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = { ...@@ -1843,7 +1843,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;
...@@ -1864,7 +1864,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, ...@@ -1864,7 +1864,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;
...@@ -1905,11 +1905,12 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net, ...@@ -1905,11 +1905,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;
...@@ -2042,7 +2043,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -2042,7 +2043,7 @@ static int ovs_vport_cmd_new(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_NEW); OVS_VPORT_CMD_NEW, GFP_KERNEL);
if (netdev_get_fwd_headroom(vport->dev) > dp->max_headroom) if (netdev_get_fwd_headroom(vport->dev) > dp->max_headroom)
update_headroom(dp); update_headroom(dp);
...@@ -2101,7 +2102,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -2101,7 +2102,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_NEW); OVS_VPORT_CMD_NEW, GFP_ATOMIC);
BUG_ON(err < 0); BUG_ON(err < 0);
ovs_unlock(); ovs_unlock();
...@@ -2140,7 +2141,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -2140,7 +2141,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 */
...@@ -2182,7 +2183,7 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -2182,7 +2183,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_NEW); OVS_VPORT_CMD_NEW, GFP_ATOMIC);
BUG_ON(err < 0); BUG_ON(err < 0);
rcu_read_unlock(); rcu_read_unlock();
...@@ -2218,7 +2219,8 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2218,7 +2219,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_NEW) < 0) OVS_VPORT_CMD_NEW,
GFP_ATOMIC) < 0)
goto out; goto out;
j++; j++;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册