mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
synced 2025-10-07 16:59:52 +10:00
Introduce new netlink attribute NETDEV_A_DEV_XDP_ZC_MAX_SEGS that will carry maximum fragments that underlying ZC driver is able to handle on TX side. It is going to be included in netlink response only when driver supports ZC. Any value higher than 1 implies multi-buffer ZC support on underlying device. Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com> Link: https://lore.kernel.org/r/20230719132421.584801-11-maciej.fijalkowski@intel.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
188 lines
3.7 KiB
C
188 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <net/net_namespace.h>
|
|
#include <net/sock.h>
|
|
|
|
#include "netdev-genl-gen.h"
|
|
|
|
static int
|
|
netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,
|
|
u32 portid, u32 seq, int flags, u32 cmd)
|
|
{
|
|
void *hdr;
|
|
|
|
hdr = genlmsg_put(rsp, portid, seq, &netdev_nl_family, flags, cmd);
|
|
if (!hdr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) ||
|
|
nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES,
|
|
netdev->xdp_features, NETDEV_A_DEV_PAD)) {
|
|
genlmsg_cancel(rsp, hdr);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (netdev->xdp_features & NETDEV_XDP_ACT_XSK_ZEROCOPY) {
|
|
if (nla_put_u32(rsp, NETDEV_A_DEV_XDP_ZC_MAX_SEGS,
|
|
netdev->xdp_zc_max_segs)) {
|
|
genlmsg_cancel(rsp, hdr);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
genlmsg_end(rsp, hdr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
netdev_genl_dev_notify(struct net_device *netdev, int cmd)
|
|
{
|
|
struct sk_buff *ntf;
|
|
|
|
if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev),
|
|
NETDEV_NLGRP_MGMT))
|
|
return;
|
|
|
|
ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!ntf)
|
|
return;
|
|
|
|
if (netdev_nl_dev_fill(netdev, ntf, 0, 0, 0, cmd)) {
|
|
nlmsg_free(ntf);
|
|
return;
|
|
}
|
|
|
|
genlmsg_multicast_netns(&netdev_nl_family, dev_net(netdev), ntf,
|
|
0, NETDEV_NLGRP_MGMT, GFP_KERNEL);
|
|
}
|
|
|
|
int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct net_device *netdev;
|
|
struct sk_buff *rsp;
|
|
u32 ifindex;
|
|
int err;
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_DEV_IFINDEX))
|
|
return -EINVAL;
|
|
|
|
ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
|
|
|
|
rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!rsp)
|
|
return -ENOMEM;
|
|
|
|
rtnl_lock();
|
|
|
|
netdev = __dev_get_by_index(genl_info_net(info), ifindex);
|
|
if (netdev)
|
|
err = netdev_nl_dev_fill(netdev, rsp, info->snd_portid,
|
|
info->snd_seq, 0, info->genlhdr->cmd);
|
|
else
|
|
err = -ENODEV;
|
|
|
|
rtnl_unlock();
|
|
|
|
if (err)
|
|
goto err_free_msg;
|
|
|
|
return genlmsg_reply(rsp, info);
|
|
|
|
err_free_msg:
|
|
nlmsg_free(rsp);
|
|
return err;
|
|
}
|
|
|
|
int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
|
{
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net_device *netdev;
|
|
int idx = 0, s_idx;
|
|
int h, s_h;
|
|
int err;
|
|
|
|
s_h = cb->args[0];
|
|
s_idx = cb->args[1];
|
|
|
|
rtnl_lock();
|
|
|
|
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
|
|
struct hlist_head *head;
|
|
|
|
idx = 0;
|
|
head = &net->dev_index_head[h];
|
|
hlist_for_each_entry(netdev, head, index_hlist) {
|
|
if (idx < s_idx)
|
|
goto cont;
|
|
err = netdev_nl_dev_fill(netdev, skb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq, 0,
|
|
NETDEV_CMD_DEV_GET);
|
|
if (err < 0)
|
|
break;
|
|
cont:
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
rtnl_unlock();
|
|
|
|
if (err != -EMSGSIZE)
|
|
return err;
|
|
|
|
cb->args[1] = idx;
|
|
cb->args[0] = h;
|
|
cb->seq = net->dev_base_seq;
|
|
|
|
return skb->len;
|
|
}
|
|
|
|
static int netdev_genl_netdevice_event(struct notifier_block *nb,
|
|
unsigned long event, void *ptr)
|
|
{
|
|
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
|
|
|
|
switch (event) {
|
|
case NETDEV_REGISTER:
|
|
netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF);
|
|
break;
|
|
case NETDEV_UNREGISTER:
|
|
netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF);
|
|
break;
|
|
case NETDEV_XDP_FEAT_CHANGE:
|
|
netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF);
|
|
break;
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block netdev_genl_nb = {
|
|
.notifier_call = netdev_genl_netdevice_event,
|
|
};
|
|
|
|
static int __init netdev_genl_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = register_netdevice_notifier(&netdev_genl_nb);
|
|
if (err)
|
|
return err;
|
|
|
|
err = genl_register_family(&netdev_nl_family);
|
|
if (err)
|
|
goto err_unreg_ntf;
|
|
|
|
return 0;
|
|
|
|
err_unreg_ntf:
|
|
unregister_netdevice_notifier(&netdev_genl_nb);
|
|
return err;
|
|
}
|
|
|
|
subsys_initcall(netdev_genl_init);
|