Commit 784ee615 authored by David S. Miller's avatar David S. Miller

Merge branch 'netlink-emsgsize'

Jakub Kicinski says:

====================
netlink: handle EMSGSIZE errors in the core

Ido discovered some time back that we usually force NLMSG_DONE
to be delivered in a separate recv() syscall, even if it would
fit into the same skb as data messages. He made nexthop try
to fit DONE with data in commit 8743aeff ("nexthop: Fix
infinite nexthop bucket dump when using maximum nexthop ID"),
and nobody has complained so far.

We have since also tried to follow the same pattern in new
genetlink families, but explaining to people, or even remembering
the correct handling ourselves is tedious.

Let the netlink socket layer consume -EMSGSIZE errors.
Practically speaking most families use this error code
as "dump needs more space", anyway.

v2:
 - init err to 0 in last patch
v1: https://lore.kernel.org/all/20240301012845.2951053-1-kuba@kernel.org/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e3350ba4 87d38197
...@@ -152,10 +152,7 @@ int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -152,10 +152,7 @@ int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
} }
rtnl_unlock(); rtnl_unlock();
if (err != -EMSGSIZE) return err;
return err;
return skb->len;
} }
static int static int
...@@ -287,10 +284,7 @@ int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -287,10 +284,7 @@ int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
} }
rtnl_unlock(); rtnl_unlock();
if (err != -EMSGSIZE) return err;
return err;
return skb->len;
} }
static int static int
...@@ -463,10 +457,7 @@ int netdev_nl_queue_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -463,10 +457,7 @@ int netdev_nl_queue_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
} }
rtnl_unlock(); rtnl_unlock();
if (err != -EMSGSIZE) return err;
return err;
return skb->len;
} }
static int netdev_genl_netdevice_event(struct notifier_block *nb, static int netdev_genl_netdevice_event(struct notifier_block *nb,
......
...@@ -102,8 +102,6 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -102,8 +102,6 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb,
mutex_unlock(&page_pools_lock); mutex_unlock(&page_pools_lock);
rtnl_unlock(); rtnl_unlock();
if (skb->len && err == -EMSGSIZE)
return skb->len;
return err; return err;
} }
......
...@@ -2267,6 +2267,15 @@ static int netlink_dump(struct sock *sk, bool lock_taken) ...@@ -2267,6 +2267,15 @@ static int netlink_dump(struct sock *sk, bool lock_taken)
if (extra_mutex) if (extra_mutex)
mutex_unlock(extra_mutex); mutex_unlock(extra_mutex);
/* EMSGSIZE plus something already in the skb means
* that there's more to dump but current skb has filled up.
* If the callback really wants to return EMSGSIZE to user space
* it needs to do so again, on the next cb->dump() call,
* without putting data in the skb.
*/
if (nlk->dump_done_errno == -EMSGSIZE && skb->len)
nlk->dump_done_errno = skb->len;
cb->extack = NULL; cb->extack = NULL;
} }
......
...@@ -1232,7 +1232,7 @@ static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, ...@@ -1232,7 +1232,7 @@ static int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq,
hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd);
if (hdr == NULL) if (hdr == NULL)
return -1; return -EMSGSIZE;
if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) ||
nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) || nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) ||
...@@ -1355,6 +1355,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1355,6 +1355,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int fams_to_skip = cb->args[0]; int fams_to_skip = cb->args[0];
unsigned int id; unsigned int id;
int err = 0;
idr_for_each_entry(&genl_fam_idr, rt, id) { idr_for_each_entry(&genl_fam_idr, rt, id) {
if (!rt->netnsok && !net_eq(net, &init_net)) if (!rt->netnsok && !net_eq(net, &init_net))
...@@ -1363,16 +1364,17 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1363,16 +1364,17 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
if (n++ < fams_to_skip) if (n++ < fams_to_skip)
continue; continue;
if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, err = ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
skb, CTRL_CMD_NEWFAMILY) < 0) { skb, CTRL_CMD_NEWFAMILY);
if (err) {
n--; n--;
break; break;
} }
} }
cb->args[0] = n; cb->args[0] = n;
return skb->len; return err;
} }
static struct sk_buff *ctrl_build_family_msg(const struct genl_family *family, static struct sk_buff *ctrl_build_family_msg(const struct genl_family *family,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment