1519 lines · 38.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/netdevice.h>#include <linux/notifier.h>#include <linux/pid_namespace.h>#include <linux/rtnetlink.h>#include <net/busy_poll.h>#include <net/net_namespace.h>#include <net/netdev_queues.h>#include <net/netdev_rx_queue.h>#include <net/sock.h>#include <net/xdp.h>#include <net/xdp_sock.h>#include <net/page_pool/memory_provider.h> #include "dev.h"#include "devmem.h"#include "netdev-genl-gen.h" struct netdev_nl_dump_ctx {	unsigned long	ifindex;	unsigned int	rxq_idx;	unsigned int	txq_idx;	unsigned int	napi_id;}; static struct netdev_nl_dump_ctx *netdev_dump_ctx(struct netlink_callback *cb){	NL_ASSERT_CTX_FITS(struct netdev_nl_dump_ctx); 	return (struct netdev_nl_dump_ctx *)cb->ctx;} static intnetdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp,		   const struct genl_info *info){	u64 xsk_features = 0;	u64 xdp_rx_meta = 0;	void *hdr; 	netdev_assert_locked(netdev); /* note: rtnl_lock may not be held! */ 	hdr = genlmsg_iput(rsp, info);	if (!hdr)		return -EMSGSIZE; #define XDP_METADATA_KFUNC(_, flag, __, xmo) \	if (netdev->xdp_metadata_ops && netdev->xdp_metadata_ops->xmo) \		xdp_rx_meta |= flag;XDP_METADATA_KFUNC_xxx#undef XDP_METADATA_KFUNC 	if (netdev->xsk_tx_metadata_ops) {		if (netdev->xsk_tx_metadata_ops->tmo_fill_timestamp)			xsk_features |= NETDEV_XSK_FLAGS_TX_TIMESTAMP;		if (netdev->xsk_tx_metadata_ops->tmo_request_checksum)			xsk_features |= NETDEV_XSK_FLAGS_TX_CHECKSUM;		if (netdev->xsk_tx_metadata_ops->tmo_request_launch_time)			xsk_features |= NETDEV_XSK_FLAGS_TX_LAUNCH_TIME_FIFO;	} 	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) ||	    nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_RX_METADATA_FEATURES,			      xdp_rx_meta, NETDEV_A_DEV_PAD) ||	    nla_put_u64_64bit(rsp, NETDEV_A_DEV_XSK_FEATURES,			      xsk_features, NETDEV_A_DEV_PAD))		goto err_cancel_msg; 	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))			goto err_cancel_msg;	} 	genlmsg_end(rsp, hdr); 	return 0; err_cancel_msg:	genlmsg_cancel(rsp, hdr);	return -EMSGSIZE;} static voidnetdev_genl_dev_notify(struct net_device *netdev, int cmd){	struct genl_info info;	struct sk_buff *ntf; 	if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev),				NETDEV_NLGRP_MGMT))		return; 	genl_info_init_ntf(&info, &netdev_nl_family, cmd); 	ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!ntf)		return; 	if (netdev_nl_dev_fill(netdev, ntf, &info)) {		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; 	netdev = netdev_get_by_index_lock(genl_info_net(info), ifindex);	if (!netdev) {		err = -ENODEV;		goto err_free_msg;	} 	err = netdev_nl_dev_fill(netdev, rsp, info);	netdev_unlock(netdev); 	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 netdev_nl_dump_ctx *ctx = netdev_dump_ctx(cb);	struct net *net = sock_net(skb->sk);	int err; 	for_each_netdev_lock_scoped(net, netdev, ctx->ifindex) {		err = netdev_nl_dev_fill(netdev, skb, genl_info_dump(cb));		if (err < 0)			return err;	} 	return 0;} static intnetdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi,			const struct genl_info *info){	unsigned long irq_suspend_timeout;	unsigned long gro_flush_timeout;	u32 napi_defer_hard_irqs;	void *hdr;	pid_t pid; 	if (!napi->dev->up)		return 0; 	hdr = genlmsg_iput(rsp, info);	if (!hdr)		return -EMSGSIZE; 	if (nla_put_u32(rsp, NETDEV_A_NAPI_ID, napi->napi_id))		goto nla_put_failure; 	if (nla_put_u32(rsp, NETDEV_A_NAPI_IFINDEX, napi->dev->ifindex))		goto nla_put_failure; 	if (napi->irq >= 0 && nla_put_u32(rsp, NETDEV_A_NAPI_IRQ, napi->irq))		goto nla_put_failure; 	if (nla_put_uint(rsp, NETDEV_A_NAPI_THREADED,			 napi_get_threaded(napi)))		goto nla_put_failure; 	if (napi->thread) {		pid = task_pid_nr_ns(napi->thread,				     task_active_pid_ns(current));		if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid))			goto nla_put_failure;	} 	napi_defer_hard_irqs = napi_get_defer_hard_irqs(napi);	if (nla_put_s32(rsp, NETDEV_A_NAPI_DEFER_HARD_IRQS,			napi_defer_hard_irqs))		goto nla_put_failure; 	irq_suspend_timeout = napi_get_irq_suspend_timeout(napi);	if (nla_put_uint(rsp, NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT,			 irq_suspend_timeout))		goto nla_put_failure; 	gro_flush_timeout = napi_get_gro_flush_timeout(napi);	if (nla_put_uint(rsp, NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT,			 gro_flush_timeout))		goto nla_put_failure; 	genlmsg_end(rsp, hdr); 	return 0; nla_put_failure:	genlmsg_cancel(rsp, hdr);	return -EMSGSIZE;} int netdev_nl_napi_get_doit(struct sk_buff *skb, struct genl_info *info){	struct napi_struct *napi;	struct sk_buff *rsp;	u32 napi_id;	int err; 	if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_NAPI_ID))		return -EINVAL; 	napi_id = nla_get_u32(info->attrs[NETDEV_A_NAPI_ID]); 	rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!rsp)		return -ENOMEM; 	napi = netdev_napi_by_id_lock(genl_info_net(info), napi_id);	if (napi) {		err = netdev_nl_napi_fill_one(rsp, napi, info);		netdev_unlock(napi->dev);	} else {		NL_SET_BAD_ATTR(info->extack, info->attrs[NETDEV_A_NAPI_ID]);		err = -ENOENT;	} 	if (err) {		goto err_free_msg;	} else if (!rsp->len) {		err = -ENOENT;		goto err_free_msg;	} 	return genlmsg_reply(rsp, info); err_free_msg:	nlmsg_free(rsp);	return err;} static intnetdev_nl_napi_dump_one(struct net_device *netdev, struct sk_buff *rsp,			const struct genl_info *info,			struct netdev_nl_dump_ctx *ctx){	struct napi_struct *napi;	unsigned int prev_id;	int err = 0; 	if (!netdev->up)		return err; 	prev_id = UINT_MAX;	list_for_each_entry(napi, &netdev->napi_list, dev_list) {		if (!napi_id_valid(napi->napi_id))			continue; 		/* Dump continuation below depends on the list being sorted */		WARN_ON_ONCE(napi->napi_id >= prev_id);		prev_id = napi->napi_id; 		if (ctx->napi_id && napi->napi_id >= ctx->napi_id)			continue; 		err = netdev_nl_napi_fill_one(rsp, napi, info);		if (err)			return err;		ctx->napi_id = napi->napi_id;	}	return err;} int netdev_nl_napi_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb){	struct netdev_nl_dump_ctx *ctx = netdev_dump_ctx(cb);	const struct genl_info *info = genl_info_dump(cb);	struct net *net = sock_net(skb->sk);	struct net_device *netdev;	u32 ifindex = 0;	int err = 0; 	if (info->attrs[NETDEV_A_NAPI_IFINDEX])		ifindex = nla_get_u32(info->attrs[NETDEV_A_NAPI_IFINDEX]); 	if (ifindex) {		netdev = netdev_get_by_index_lock(net, ifindex);		if (netdev) {			err = netdev_nl_napi_dump_one(netdev, skb, info, ctx);			netdev_unlock(netdev);		} else {			err = -ENODEV;		}	} else {		for_each_netdev_lock_scoped(net, netdev, ctx->ifindex) {			err = netdev_nl_napi_dump_one(netdev, skb, info, ctx);			if (err < 0)				break;			ctx->napi_id = 0;		}	} 	return err;} static intnetdev_nl_napi_set_config(struct napi_struct *napi, struct genl_info *info){	u64 irq_suspend_timeout = 0;	u64 gro_flush_timeout = 0;	u8 threaded = 0;	u32 defer = 0; 	if (info->attrs[NETDEV_A_NAPI_THREADED]) {		int ret; 		threaded = nla_get_uint(info->attrs[NETDEV_A_NAPI_THREADED]);		ret = napi_set_threaded(napi, threaded);		if (ret)			return ret;	} 	if (info->attrs[NETDEV_A_NAPI_DEFER_HARD_IRQS]) {		defer = nla_get_u32(info->attrs[NETDEV_A_NAPI_DEFER_HARD_IRQS]);		napi_set_defer_hard_irqs(napi, defer);	} 	if (info->attrs[NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT]) {		irq_suspend_timeout = nla_get_uint(info->attrs[NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT]);		napi_set_irq_suspend_timeout(napi, irq_suspend_timeout);	} 	if (info->attrs[NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT]) {		gro_flush_timeout = nla_get_uint(info->attrs[NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT]);		napi_set_gro_flush_timeout(napi, gro_flush_timeout);	} 	return 0;} int netdev_nl_napi_set_doit(struct sk_buff *skb, struct genl_info *info){	struct napi_struct *napi;	unsigned int napi_id;	int err; 	if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_NAPI_ID))		return -EINVAL; 	napi_id = nla_get_u32(info->attrs[NETDEV_A_NAPI_ID]); 	napi = netdev_napi_by_id_lock(genl_info_net(info), napi_id);	if (napi) {		err = netdev_nl_napi_set_config(napi, info);		netdev_unlock(napi->dev);	} else {		NL_SET_BAD_ATTR(info->extack, info->attrs[NETDEV_A_NAPI_ID]);		err = -ENOENT;	} 	return err;} static int nla_put_napi_id(struct sk_buff *skb, const struct napi_struct *napi){	if (napi && napi_id_valid(napi->napi_id))		return nla_put_u32(skb, NETDEV_A_QUEUE_NAPI_ID, napi->napi_id);	return 0;} static intnetdev_nl_queue_fill_lease(struct sk_buff *rsp, struct net_device *netdev,			   u32 q_idx, u32 q_type){	struct net_device *orig_netdev = netdev;	struct nlattr *nest_lease, *nest_queue;	struct netdev_rx_queue *rxq;	struct net *net, *peer_net; 	rxq = __netif_get_rx_queue_lease(&netdev, &q_idx, NETIF_PHYS_TO_VIRT);	if (!rxq || orig_netdev == netdev)		return 0; 	nest_lease = nla_nest_start(rsp, NETDEV_A_QUEUE_LEASE);	if (!nest_lease)		goto nla_put_failure; 	nest_queue = nla_nest_start(rsp, NETDEV_A_LEASE_QUEUE);	if (!nest_queue)		goto nla_put_failure;	if (nla_put_u32(rsp, NETDEV_A_QUEUE_ID, q_idx))		goto nla_put_failure;	if (nla_put_u32(rsp, NETDEV_A_QUEUE_TYPE, q_type))		goto nla_put_failure;	nla_nest_end(rsp, nest_queue); 	if (nla_put_u32(rsp, NETDEV_A_LEASE_IFINDEX,			READ_ONCE(netdev->ifindex)))		goto nla_put_failure; 	rcu_read_lock();	peer_net = dev_net_rcu(netdev);	net = dev_net_rcu(orig_netdev);	if (!net_eq(net, peer_net)) {		s32 id = peernet2id_alloc(net, peer_net, GFP_ATOMIC); 		if (nla_put_s32(rsp, NETDEV_A_LEASE_NETNS_ID, id))			goto nla_put_failure_unlock;	}	rcu_read_unlock();	nla_nest_end(rsp, nest_lease);	return 0; nla_put_failure_unlock:	rcu_read_unlock();nla_put_failure:	return -ENOMEM;} static int__netdev_nl_queue_fill_mp(struct sk_buff *rsp, struct netdev_rx_queue *rxq){	struct pp_memory_provider_params *params = &rxq->mp_params; 	if (params->mp_ops &&	    params->mp_ops->nl_fill(params->mp_priv, rsp, rxq))		return -EMSGSIZE; #ifdef CONFIG_XDP_SOCKETS	if (rxq->pool)		if (nla_put_empty_nest(rsp, NETDEV_A_QUEUE_XSK))			return -EMSGSIZE;#endif	return 0;} static intnetdev_nl_queue_fill_mp(struct sk_buff *rsp, struct net_device *netdev,			struct netdev_rx_queue *rxq){	struct netdev_rx_queue *hw_rxq;	int ret; 	hw_rxq = rxq->lease;	if (!hw_rxq || !netif_is_queue_leasee(netdev))		return __netdev_nl_queue_fill_mp(rsp, rxq); 	netdev_lock(hw_rxq->dev);	ret = __netdev_nl_queue_fill_mp(rsp, hw_rxq);	netdev_unlock(hw_rxq->dev);	return ret;} static intnetdev_nl_queue_fill_one(struct sk_buff *rsp, struct net_device *netdev,			 u32 q_idx, u32 q_type, const struct genl_info *info){	struct netdev_rx_queue *rxq;	struct netdev_queue *txq;	void *hdr; 	hdr = genlmsg_iput(rsp, info);	if (!hdr)		return -EMSGSIZE; 	if (nla_put_u32(rsp, NETDEV_A_QUEUE_ID, q_idx) ||	    nla_put_u32(rsp, NETDEV_A_QUEUE_TYPE, q_type) ||	    nla_put_u32(rsp, NETDEV_A_QUEUE_IFINDEX, netdev->ifindex))		goto nla_put_failure; 	switch (q_type) {	case NETDEV_QUEUE_TYPE_RX:		rxq = __netif_get_rx_queue(netdev, q_idx);		if (nla_put_napi_id(rsp, rxq->napi))			goto nla_put_failure;		if (netdev_nl_queue_fill_lease(rsp, netdev, q_idx, q_type))			goto nla_put_failure;		if (netdev_nl_queue_fill_mp(rsp, netdev, rxq))			goto nla_put_failure;		break;

Lines 1–500 of 1519.