Skip to content

Commit

Permalink
Merge branch 'net-more-heap-allocation-and-split-of-rtnl_newlink'
Browse files Browse the repository at this point in the history
Jakub Kicinski says:

====================
net: more heap allocation and split of rtnl_newlink()

Small refactoring of rtnl_newlink() to fix a stack usage warning
and make the function shorter.
====================

Link: https://lore.kernel.org/r/20220429235508.268349-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni committed May 2, 2022
2 parents d7ab15a + 02839cc commit 0decb97
Showing 1 changed file with 109 additions and 91 deletions.
200 changes: 109 additions & 91 deletions net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -3302,23 +3302,116 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
return 0;
}

static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attr, struct netlink_ext_ack *extack)
static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
const struct rtnl_link_ops *ops,
struct nlattr **tb, struct nlattr **data,
struct netlink_ext_ack *extack)
{
struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
unsigned char name_assign_type = NET_NAME_USER;
struct net *net = sock_net(skb->sk);
struct net *dest_net, *link_net;
struct net_device *dev;
char ifname[IFNAMSIZ];
int err;

if (!ops->alloc && !ops->setup)
return -EOPNOTSUPP;

if (tb[IFLA_IFNAME]) {
nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
} else {
snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
name_assign_type = NET_NAME_ENUM;
}

dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
if (IS_ERR(dest_net))
return PTR_ERR(dest_net);

if (tb[IFLA_LINK_NETNSID]) {
int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);

link_net = get_net_ns_by_id(dest_net, id);
if (!link_net) {
NL_SET_ERR_MSG(extack, "Unknown network namespace id");
err = -EINVAL;
goto out;
}
err = -EPERM;
if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
goto out;
} else {
link_net = NULL;
}

dev = rtnl_create_link(link_net ? : dest_net, ifname,
name_assign_type, ops, tb, extack);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out;
}

dev->ifindex = ifm->ifi_index;

if (ops->newlink)
err = ops->newlink(link_net ? : net, dev, tb, data, extack);
else
err = register_netdevice(dev);
if (err < 0) {
free_netdev(dev);
goto out;
}

err = rtnl_configure_link(dev, ifm);
if (err < 0)
goto out_unregister;
if (link_net) {
err = dev_change_net_namespace(dev, dest_net, ifname);
if (err < 0)
goto out_unregister;
}
if (tb[IFLA_MASTER]) {
err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
if (err)
goto out_unregister;
}
out:
if (link_net)
put_net(link_net);
put_net(dest_net);
return err;
out_unregister:
if (ops->newlink) {
LIST_HEAD(list_kill);

ops->dellink(dev, &list_kill);
unregister_netdevice_many(&list_kill);
} else {
unregister_netdevice(dev);
}
goto out;
}

struct rtnl_newlink_tbs {
struct nlattr *tb[IFLA_MAX + 1];
struct nlattr *attr[RTNL_MAX_TYPE + 1];
struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
};

static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
struct rtnl_newlink_tbs *tbs,
struct netlink_ext_ack *extack)
{
struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
struct nlattr ** const tb = tbs->tb;
const struct rtnl_link_ops *m_ops;
struct net_device *master_dev;
struct net *net = sock_net(skb->sk);
const struct rtnl_link_ops *ops;
struct nlattr *tb[IFLA_MAX + 1];
struct net *dest_net, *link_net;
struct nlattr **slave_data;
char kind[MODULE_NAME_LEN];
struct net_device *dev;
struct ifinfomsg *ifm;
char ifname[IFNAMSIZ];
struct nlattr **data;
bool link_specified;
int err;
Expand Down Expand Up @@ -3382,12 +3475,12 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
return -EINVAL;

if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
err = nla_parse_nested_deprecated(attr, ops->maxtype,
err = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
linkinfo[IFLA_INFO_DATA],
ops->policy, extack);
if (err < 0)
return err;
data = attr;
data = tbs->attr;
}
if (ops->validate) {
err = ops->validate(tb, data, extack);
Expand All @@ -3403,14 +3496,14 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,

if (m_ops->slave_maxtype &&
linkinfo[IFLA_INFO_SLAVE_DATA]) {
err = nla_parse_nested_deprecated(slave_attr,
err = nla_parse_nested_deprecated(tbs->slave_attr,
m_ops->slave_maxtype,
linkinfo[IFLA_INFO_SLAVE_DATA],
m_ops->slave_policy,
extack);
if (err < 0)
return err;
slave_data = slave_attr;
slave_data = tbs->slave_attr;
}
}

Expand Down Expand Up @@ -3478,96 +3571,21 @@ static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
return -EOPNOTSUPP;
}

if (!ops->alloc && !ops->setup)
return -EOPNOTSUPP;

if (tb[IFLA_IFNAME]) {
nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
} else {
snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
name_assign_type = NET_NAME_ENUM;
}

dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
if (IS_ERR(dest_net))
return PTR_ERR(dest_net);

if (tb[IFLA_LINK_NETNSID]) {
int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);

link_net = get_net_ns_by_id(dest_net, id);
if (!link_net) {
NL_SET_ERR_MSG(extack, "Unknown network namespace id");
err = -EINVAL;
goto out;
}
err = -EPERM;
if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
goto out;
} else {
link_net = NULL;
}

dev = rtnl_create_link(link_net ? : dest_net, ifname,
name_assign_type, ops, tb, extack);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out;
}

dev->ifindex = ifm->ifi_index;

if (ops->newlink)
err = ops->newlink(link_net ? : net, dev, tb, data, extack);
else
err = register_netdevice(dev);
if (err < 0) {
free_netdev(dev);
goto out;
}

err = rtnl_configure_link(dev, ifm);
if (err < 0)
goto out_unregister;
if (link_net) {
err = dev_change_net_namespace(dev, dest_net, ifname);
if (err < 0)
goto out_unregister;
}
if (tb[IFLA_MASTER]) {
err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
if (err)
goto out_unregister;
}
out:
if (link_net)
put_net(link_net);
put_net(dest_net);
return err;
out_unregister:
if (ops->newlink) {
LIST_HEAD(list_kill);

ops->dellink(dev, &list_kill);
unregister_netdevice_many(&list_kill);
} else {
unregister_netdevice(dev);
}
goto out;
return rtnl_newlink_create(skb, ifm, ops, tb, data, extack);
}

static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr **attr;
struct rtnl_newlink_tbs *tbs;
int ret;

attr = kmalloc_array(RTNL_MAX_TYPE + 1, sizeof(*attr), GFP_KERNEL);
if (!attr)
tbs = kmalloc(sizeof(*tbs), GFP_KERNEL);
if (!tbs)
return -ENOMEM;

ret = __rtnl_newlink(skb, nlh, attr, extack);
kfree(attr);
ret = __rtnl_newlink(skb, nlh, tbs, extack);
kfree(tbs);
return ret;
}

Expand Down

0 comments on commit 0decb97

Please sign in to comment.