// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2019 Mellanox Technologies. */ #include #include "umem.h" #include "setup.h" #include "en/params.h" static int mlx5e_xsk_map_umem(struct mlx5e_priv *priv, struct xdp_umem *umem) { struct device *dev = priv->mdev->device; u32 i; for (i = 0; i < umem->npgs; i++) { dma_addr_t dma = dma_map_page(dev, umem->pgs[i], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(dev, dma))) goto err_unmap; umem->pages[i].dma = dma; } return 0; err_unmap: while (i--) { dma_unmap_page(dev, umem->pages[i].dma, PAGE_SIZE, DMA_BIDIRECTIONAL); umem->pages[i].dma = 0; } return -ENOMEM; } static void mlx5e_xsk_unmap_umem(struct mlx5e_priv *priv, struct xdp_umem *umem) { struct device *dev = priv->mdev->device; u32 i; for (i = 0; i < umem->npgs; i++) { dma_unmap_page(dev, umem->pages[i].dma, PAGE_SIZE, DMA_BIDIRECTIONAL); umem->pages[i].dma = 0; } } static int mlx5e_xsk_get_umems(struct mlx5e_xsk *xsk) { if (!xsk->umems) { xsk->umems = kcalloc(MLX5E_MAX_NUM_CHANNELS, sizeof(*xsk->umems), GFP_KERNEL); if (unlikely(!xsk->umems)) return -ENOMEM; } xsk->refcnt++; xsk->ever_used = true; return 0; } static void mlx5e_xsk_put_umems(struct mlx5e_xsk *xsk) { if (!--xsk->refcnt) { kfree(xsk->umems); xsk->umems = NULL; } } static int mlx5e_xsk_add_umem(struct mlx5e_xsk *xsk, struct xdp_umem *umem, u16 ix) { int err; err = mlx5e_xsk_get_umems(xsk); if (unlikely(err)) return err; xsk->umems[ix] = umem; return 0; } static void mlx5e_xsk_remove_umem(struct mlx5e_xsk *xsk, u16 ix) { xsk->umems[ix] = NULL; mlx5e_xsk_put_umems(xsk); } static bool mlx5e_xsk_is_umem_sane(struct xdp_umem *umem) { return umem->headroom <= 0xffff && umem->chunk_size_nohr <= 0xffff; } void mlx5e_build_xsk_param(struct xdp_umem *umem, struct mlx5e_xsk_param *xsk) { xsk->headroom = umem->headroom; xsk->chunk_size = umem->chunk_size_nohr + umem->headroom; } static int mlx5e_xsk_enable_locked(struct mlx5e_priv *priv, struct xdp_umem *umem, u16 ix) { struct mlx5e_params *params = &priv->channels.params; struct mlx5e_xsk_param xsk; struct mlx5e_channel *c; int err; if (unlikely(mlx5e_xsk_get_umem(&priv->channels.params, &priv->xsk, ix))) return -EBUSY; if (unlikely(!mlx5e_xsk_is_umem_sane(umem))) return -EINVAL; err = mlx5e_xsk_map_umem(priv, umem); if (unlikely(err)) return err; err = mlx5e_xsk_add_umem(&priv->xsk, umem, ix); if (unlikely(err)) goto err_unmap_umem; mlx5e_build_xsk_param(umem, &xsk); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { /* XSK objects will be created on open. */ goto validate_closed; } if (!params->xdp_prog) { /* XSK objects will be created when an XDP program is set, * and the channels are reopened. */ goto validate_closed; } c = priv->channels.c[ix]; err = mlx5e_open_xsk(priv, params, &xsk, umem, c); if (unlikely(err)) goto err_remove_umem; mlx5e_activate_xsk(c); /* Don't wait for WQEs, because the newer xdpsock sample doesn't provide * any Fill Ring entries at the setup stage. */ err = mlx5e_xsk_redirect_rqt_to_channel(priv, priv->channels.c[ix]); if (unlikely(err)) goto err_deactivate; return 0; err_deactivate: mlx5e_deactivate_xsk(c); mlx5e_close_xsk(c); err_remove_umem: mlx5e_xsk_remove_umem(&priv->xsk, ix); err_unmap_umem: mlx5e_xsk_unmap_umem(priv, umem); return err; validate_closed: /* Check the configuration in advance, rather than fail at a later stage * (in mlx5e_xdp_set or on open) and end up with no channels. */ if (!mlx5e_validate_xsk_param(params, &xsk, priv->mdev)) { err = -EINVAL; goto err_remove_umem; } return 0; } static int mlx5e_xsk_disable_locked(struct mlx5e_priv *priv, u16 ix) { struct xdp_umem *umem = mlx5e_xsk_get_umem(&priv->channels.params, &priv->xsk, ix); struct mlx5e_channel *c; if (unlikely(!umem)) return -EINVAL; if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) goto remove_umem; /* XSK RQ and SQ are only created if XDP program is set. */ if (!priv->channels.params.xdp_prog) goto remove_umem; c = priv->channels.c[ix]; mlx5e_xsk_redirect_rqt_to_drop(priv, ix); mlx5e_deactivate_xsk(c); mlx5e_close_xsk(c); remove_umem: mlx5e_xsk_remove_umem(&priv->xsk, ix); mlx5e_xsk_unmap_umem(priv, umem); return 0; } static int mlx5e_xsk_enable_umem(struct mlx5e_priv *priv, struct xdp_umem *umem, u16 ix) { int err; mutex_lock(&priv->state_lock); err = mlx5e_xsk_enable_locked(priv, umem, ix); mutex_unlock(&priv->state_lock); return err; } static int mlx5e_xsk_disable_umem(struct mlx5e_priv *priv, u16 ix) { int err; mutex_lock(&priv->state_lock); err = mlx5e_xsk_disable_locked(priv, ix); mutex_unlock(&priv->state_lock); return err; } int mlx5e_xsk_setup_umem(struct net_device *dev, struct xdp_umem *umem, u16 qid) { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5e_params *params = &priv->channels.params; u16 ix; if (unlikely(!mlx5e_qid_get_ch_if_in_group(params, qid, MLX5E_RQ_GROUP_XSK, &ix))) return -EINVAL; return umem ? mlx5e_xsk_enable_umem(priv, umem, ix) : mlx5e_xsk_disable_umem(priv, ix); } int mlx5e_xsk_resize_reuseq(struct xdp_umem *umem, u32 nentries) { struct xdp_umem_fq_reuse *reuseq; reuseq = xsk_reuseq_prepare(nentries); if (unlikely(!reuseq)) return -ENOMEM; xsk_reuseq_free(xsk_reuseq_swap(umem, reuseq)); return 0; } u16 mlx5e_xsk_first_unused_channel(struct mlx5e_params *params, struct mlx5e_xsk *xsk) { u16 res = xsk->refcnt ? params->num_channels : 0; while (res) { if (mlx5e_xsk_get_umem(params, xsk, res - 1)) break; --res; } return res; }