xref: /openbmc/linux/drivers/vdpa/mlx5/net/mlx5_vnet.c (revision 3fe024193340b225d1fd410d78c495434a9d68e0)
11a86b377SEli Cohen // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
21a86b377SEli Cohen /* Copyright (c) 2020 Mellanox Technologies Ltd. */
31a86b377SEli Cohen 
474c9729dSLeon Romanovsky #include <linux/module.h>
51a86b377SEli Cohen #include <linux/vdpa.h>
674c9729dSLeon Romanovsky #include <linux/vringh.h>
774c9729dSLeon Romanovsky #include <uapi/linux/virtio_net.h>
81a86b377SEli Cohen #include <uapi/linux/virtio_ids.h>
9a007d940SEli Cohen #include <uapi/linux/vdpa.h>
101a86b377SEli Cohen #include <linux/virtio_config.h>
1174c9729dSLeon Romanovsky #include <linux/auxiliary_bus.h>
1274c9729dSLeon Romanovsky #include <linux/mlx5/cq.h>
131a86b377SEli Cohen #include <linux/mlx5/qp.h>
141a86b377SEli Cohen #include <linux/mlx5/device.h>
1574c9729dSLeon Romanovsky #include <linux/mlx5/driver.h>
161a86b377SEli Cohen #include <linux/mlx5/vport.h>
171a86b377SEli Cohen #include <linux/mlx5/fs.h>
180aae392bSLeon Romanovsky #include <linux/mlx5/mlx5_ifc_vdpa.h>
197c9f131fSEli Cohen #include <linux/mlx5/mpfs.h>
201a86b377SEli Cohen #include "mlx5_vdpa.h"
2172c67e9bSEli Cohen #include "mlx5_vnet.h"
221a86b377SEli Cohen 
2374c9729dSLeon Romanovsky MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
2474c9729dSLeon Romanovsky MODULE_DESCRIPTION("Mellanox VDPA driver");
2574c9729dSLeon Romanovsky MODULE_LICENSE("Dual BSD/GPL");
2674c9729dSLeon Romanovsky 
271a86b377SEli Cohen #define VALID_FEATURES_MASK                                                                        \
28cbb52359SNathan Chancellor 	(BIT_ULL(VIRTIO_NET_F_CSUM) | BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) |                                   \
29cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) | BIT_ULL(VIRTIO_NET_F_MTU) | BIT_ULL(VIRTIO_NET_F_MAC) |   \
30cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) | BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) |                             \
31cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_GUEST_ECN) | BIT_ULL(VIRTIO_NET_F_GUEST_UFO) | BIT_ULL(VIRTIO_NET_F_HOST_TSO4) | \
32cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_HOST_TSO6) | BIT_ULL(VIRTIO_NET_F_HOST_ECN) | BIT_ULL(VIRTIO_NET_F_HOST_UFO) |   \
33cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) | BIT_ULL(VIRTIO_NET_F_STATUS) | BIT_ULL(VIRTIO_NET_F_CTRL_VQ) |      \
34cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_CTRL_RX) | BIT_ULL(VIRTIO_NET_F_CTRL_VLAN) |                                 \
35cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_CTRL_RX_EXTRA) | BIT_ULL(VIRTIO_NET_F_GUEST_ANNOUNCE) |                      \
36cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_MQ) | BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) | BIT_ULL(VIRTIO_NET_F_HASH_REPORT) |  \
37cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_RSS) | BIT_ULL(VIRTIO_NET_F_RSC_EXT) | BIT_ULL(VIRTIO_NET_F_STANDBY) |           \
38cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX) | BIT_ULL(VIRTIO_F_NOTIFY_ON_EMPTY) |                          \
39cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_F_ANY_LAYOUT) | BIT_ULL(VIRTIO_F_VERSION_1) | BIT_ULL(VIRTIO_F_ACCESS_PLATFORM) |      \
40cbb52359SNathan Chancellor 	 BIT_ULL(VIRTIO_F_RING_PACKED) | BIT_ULL(VIRTIO_F_ORDER_PLATFORM) | BIT_ULL(VIRTIO_F_SR_IOV))
411a86b377SEli Cohen 
421a86b377SEli Cohen #define VALID_STATUS_MASK                                                                          \
431a86b377SEli Cohen 	(VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK |        \
441a86b377SEli Cohen 	 VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_NEEDS_RESET | VIRTIO_CONFIG_S_FAILED)
451a86b377SEli Cohen 
46e4fc6650SEli Cohen #define MLX5_FEATURE(_mvdev, _feature) (!!((_mvdev)->actual_features & BIT_ULL(_feature)))
47e4fc6650SEli Cohen 
48baf2ad3fSEli Cohen #define MLX5V_UNTAGGED 0x1000
49baf2ad3fSEli Cohen 
501a86b377SEli Cohen struct mlx5_vdpa_cq_buf {
511a86b377SEli Cohen 	struct mlx5_frag_buf_ctrl fbc;
521a86b377SEli Cohen 	struct mlx5_frag_buf frag_buf;
531a86b377SEli Cohen 	int cqe_size;
541a86b377SEli Cohen 	int nent;
551a86b377SEli Cohen };
561a86b377SEli Cohen 
571a86b377SEli Cohen struct mlx5_vdpa_cq {
581a86b377SEli Cohen 	struct mlx5_core_cq mcq;
591a86b377SEli Cohen 	struct mlx5_vdpa_cq_buf buf;
601a86b377SEli Cohen 	struct mlx5_db db;
611a86b377SEli Cohen 	int cqe;
621a86b377SEli Cohen };
631a86b377SEli Cohen 
641a86b377SEli Cohen struct mlx5_vdpa_umem {
651a86b377SEli Cohen 	struct mlx5_frag_buf_ctrl fbc;
661a86b377SEli Cohen 	struct mlx5_frag_buf frag_buf;
671a86b377SEli Cohen 	int size;
681a86b377SEli Cohen 	u32 id;
691a86b377SEli Cohen };
701a86b377SEli Cohen 
711a86b377SEli Cohen struct mlx5_vdpa_qp {
721a86b377SEli Cohen 	struct mlx5_core_qp mqp;
731a86b377SEli Cohen 	struct mlx5_frag_buf frag_buf;
741a86b377SEli Cohen 	struct mlx5_db db;
751a86b377SEli Cohen 	u16 head;
761a86b377SEli Cohen 	bool fw;
771a86b377SEli Cohen };
781a86b377SEli Cohen 
791a86b377SEli Cohen struct mlx5_vq_restore_info {
801a86b377SEli Cohen 	u32 num_ent;
811a86b377SEli Cohen 	u64 desc_addr;
821a86b377SEli Cohen 	u64 device_addr;
831a86b377SEli Cohen 	u64 driver_addr;
841a86b377SEli Cohen 	u16 avail_index;
85b35ccebeSEli Cohen 	u16 used_index;
86bc9a2b3eSEli Cohen 	struct msi_map map;
871a86b377SEli Cohen 	bool ready;
881a86b377SEli Cohen 	bool restore;
891a86b377SEli Cohen };
901a86b377SEli Cohen 
911a86b377SEli Cohen struct mlx5_vdpa_virtqueue {
921a86b377SEli Cohen 	bool ready;
931a86b377SEli Cohen 	u64 desc_addr;
941a86b377SEli Cohen 	u64 device_addr;
951a86b377SEli Cohen 	u64 driver_addr;
961a86b377SEli Cohen 	u32 num_ent;
971a86b377SEli Cohen 
981a86b377SEli Cohen 	/* Resources for implementing the notification channel from the device
991a86b377SEli Cohen 	 * to the driver. fwqp is the firmware end of an RC connection; the
1002f72b226SXiang wangx 	 * other end is vqqp used by the driver. cq is where completions are
1011a86b377SEli Cohen 	 * reported.
1021a86b377SEli Cohen 	 */
1031a86b377SEli Cohen 	struct mlx5_vdpa_cq cq;
1041a86b377SEli Cohen 	struct mlx5_vdpa_qp fwqp;
1051a86b377SEli Cohen 	struct mlx5_vdpa_qp vqqp;
1061a86b377SEli Cohen 
1071a86b377SEli Cohen 	/* umem resources are required for the virtqueue operation. They're use
1081a86b377SEli Cohen 	 * is internal and they must be provided by the driver.
1091a86b377SEli Cohen 	 */
1101a86b377SEli Cohen 	struct mlx5_vdpa_umem umem1;
1111a86b377SEli Cohen 	struct mlx5_vdpa_umem umem2;
1121a86b377SEli Cohen 	struct mlx5_vdpa_umem umem3;
1131a86b377SEli Cohen 
1141892a3d4SEli Cohen 	u32 counter_set_id;
1151a86b377SEli Cohen 	bool initialized;
1161a86b377SEli Cohen 	int index;
1171a86b377SEli Cohen 	u32 virtq_id;
1181a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev;
1191a86b377SEli Cohen 	u16 avail_idx;
120b35ccebeSEli Cohen 	u16 used_idx;
1211a86b377SEli Cohen 	int fw_state;
122bc9a2b3eSEli Cohen 	struct msi_map map;
1231a86b377SEli Cohen 
1241a86b377SEli Cohen 	/* keep last in the struct */
1251a86b377SEli Cohen 	struct mlx5_vq_restore_info ri;
1261a86b377SEli Cohen };
1271a86b377SEli Cohen 
128e4fc6650SEli Cohen static bool is_index_valid(struct mlx5_vdpa_dev *mvdev, u16 idx)
129e4fc6650SEli Cohen {
130f8ae3a48SEli Cohen 	if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_MQ))) {
131f8ae3a48SEli Cohen 		if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
132f8ae3a48SEli Cohen 			return idx < 2;
133f8ae3a48SEli Cohen 		else
134f8ae3a48SEli Cohen 			return idx < 3;
135f8ae3a48SEli Cohen 	}
136e4fc6650SEli Cohen 
137f8ae3a48SEli Cohen 	return idx <= mvdev->max_idx;
138e4fc6650SEli Cohen }
139e4fc6650SEli Cohen 
1401a86b377SEli Cohen static void free_resources(struct mlx5_vdpa_net *ndev);
1411a86b377SEli Cohen static void init_mvqs(struct mlx5_vdpa_net *ndev);
142ae0428deSEli Cohen static int setup_driver(struct mlx5_vdpa_dev *mvdev);
1431a86b377SEli Cohen static void teardown_driver(struct mlx5_vdpa_net *ndev);
1441a86b377SEli Cohen 
1451a86b377SEli Cohen static bool mlx5_vdpa_debug;
1461a86b377SEli Cohen 
1475262912eSEli Cohen #define MLX5_CVQ_MAX_ENT 16
1485262912eSEli Cohen 
1491a86b377SEli Cohen #define MLX5_LOG_VIO_FLAG(_feature)                                                                \
1501a86b377SEli Cohen 	do {                                                                                       \
151cbb52359SNathan Chancellor 		if (features & BIT_ULL(_feature))                                                  \
1521a86b377SEli Cohen 			mlx5_vdpa_info(mvdev, "%s\n", #_feature);                                  \
1531a86b377SEli Cohen 	} while (0)
1541a86b377SEli Cohen 
1551a86b377SEli Cohen #define MLX5_LOG_VIO_STAT(_status)                                                                 \
1561a86b377SEli Cohen 	do {                                                                                       \
1571a86b377SEli Cohen 		if (status & (_status))                                                            \
1581a86b377SEli Cohen 			mlx5_vdpa_info(mvdev, "%s\n", #_status);                                   \
1591a86b377SEli Cohen 	} while (0)
1601a86b377SEli Cohen 
16152893733SEli Cohen /* TODO: cross-endian support */
16252893733SEli Cohen static inline bool mlx5_vdpa_is_little_endian(struct mlx5_vdpa_dev *mvdev)
16352893733SEli Cohen {
16452893733SEli Cohen 	return virtio_legacy_is_little_endian() ||
16552893733SEli Cohen 		(mvdev->actual_features & BIT_ULL(VIRTIO_F_VERSION_1));
16652893733SEli Cohen }
16752893733SEli Cohen 
16852893733SEli Cohen static u16 mlx5vdpa16_to_cpu(struct mlx5_vdpa_dev *mvdev, __virtio16 val)
16952893733SEli Cohen {
17052893733SEli Cohen 	return __virtio16_to_cpu(mlx5_vdpa_is_little_endian(mvdev), val);
17152893733SEli Cohen }
17252893733SEli Cohen 
17352893733SEli Cohen static __virtio16 cpu_to_mlx5vdpa16(struct mlx5_vdpa_dev *mvdev, u16 val)
17452893733SEli Cohen {
17552893733SEli Cohen 	return __cpu_to_virtio16(mlx5_vdpa_is_little_endian(mvdev), val);
17652893733SEli Cohen }
17752893733SEli Cohen 
1785262912eSEli Cohen static u16 ctrl_vq_idx(struct mlx5_vdpa_dev *mvdev)
1795262912eSEli Cohen {
18052893733SEli Cohen 	if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_MQ)))
18152893733SEli Cohen 		return 2;
18252893733SEli Cohen 
183acde3929SEli Cohen 	return mvdev->max_vqs;
1845262912eSEli Cohen }
1855262912eSEli Cohen 
1865262912eSEli Cohen static bool is_ctrl_vq_idx(struct mlx5_vdpa_dev *mvdev, u16 idx)
1875262912eSEli Cohen {
1885262912eSEli Cohen 	return idx == ctrl_vq_idx(mvdev);
1895262912eSEli Cohen }
1905262912eSEli Cohen 
1911a86b377SEli Cohen static void print_status(struct mlx5_vdpa_dev *mvdev, u8 status, bool set)
1921a86b377SEli Cohen {
1931a86b377SEli Cohen 	if (status & ~VALID_STATUS_MASK)
1941a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "Warning: there are invalid status bits 0x%x\n",
1951a86b377SEli Cohen 			       status & ~VALID_STATUS_MASK);
1961a86b377SEli Cohen 
1971a86b377SEli Cohen 	if (!mlx5_vdpa_debug)
1981a86b377SEli Cohen 		return;
1991a86b377SEli Cohen 
2001a86b377SEli Cohen 	mlx5_vdpa_info(mvdev, "driver status %s", set ? "set" : "get");
2011a86b377SEli Cohen 	if (set && !status) {
2021a86b377SEli Cohen 		mlx5_vdpa_info(mvdev, "driver resets the device\n");
2031a86b377SEli Cohen 		return;
2041a86b377SEli Cohen 	}
2051a86b377SEli Cohen 
2061a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_ACKNOWLEDGE);
2071a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_DRIVER);
2081a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_DRIVER_OK);
2091a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_FEATURES_OK);
2101a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_NEEDS_RESET);
2111a86b377SEli Cohen 	MLX5_LOG_VIO_STAT(VIRTIO_CONFIG_S_FAILED);
2121a86b377SEli Cohen }
2131a86b377SEli Cohen 
2141a86b377SEli Cohen static void print_features(struct mlx5_vdpa_dev *mvdev, u64 features, bool set)
2151a86b377SEli Cohen {
2161a86b377SEli Cohen 	if (features & ~VALID_FEATURES_MASK)
2171a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "There are invalid feature bits 0x%llx\n",
2181a86b377SEli Cohen 			       features & ~VALID_FEATURES_MASK);
2191a86b377SEli Cohen 
2201a86b377SEli Cohen 	if (!mlx5_vdpa_debug)
2211a86b377SEli Cohen 		return;
2221a86b377SEli Cohen 
2231a86b377SEli Cohen 	mlx5_vdpa_info(mvdev, "driver %s feature bits:\n", set ? "sets" : "reads");
2241a86b377SEli Cohen 	if (!features)
2251a86b377SEli Cohen 		mlx5_vdpa_info(mvdev, "all feature bits are cleared\n");
2261a86b377SEli Cohen 
2271a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CSUM);
2281a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_CSUM);
2291a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
2301a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MTU);
2311a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MAC);
2321a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_TSO4);
2331a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_TSO6);
2341a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_ECN);
2351a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_UFO);
2361a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_TSO4);
2371a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_TSO6);
2381a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_ECN);
2391a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HOST_UFO);
2401a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MRG_RXBUF);
2411a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_STATUS);
2421a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_VQ);
2431a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_RX);
2441a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_VLAN);
2451a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_RX_EXTRA);
2461a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_GUEST_ANNOUNCE);
2471a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_MQ);
2481a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_CTRL_MAC_ADDR);
2491a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_HASH_REPORT);
2501a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_RSS);
2511a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_RSC_EXT);
2521a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_STANDBY);
2531a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_NET_F_SPEED_DUPLEX);
2541a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_NOTIFY_ON_EMPTY);
2551a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_ANY_LAYOUT);
2561a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_VERSION_1);
2571a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_ACCESS_PLATFORM);
2581a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_RING_PACKED);
2591a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_ORDER_PLATFORM);
2601a86b377SEli Cohen 	MLX5_LOG_VIO_FLAG(VIRTIO_F_SR_IOV);
2611a86b377SEli Cohen }
2621a86b377SEli Cohen 
2631a86b377SEli Cohen static int create_tis(struct mlx5_vdpa_net *ndev)
2641a86b377SEli Cohen {
2651a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = &ndev->mvdev;
2661a86b377SEli Cohen 	u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {};
2671a86b377SEli Cohen 	void *tisc;
2681a86b377SEli Cohen 	int err;
2691a86b377SEli Cohen 
2701a86b377SEli Cohen 	tisc = MLX5_ADDR_OF(create_tis_in, in, ctx);
2711a86b377SEli Cohen 	MLX5_SET(tisc, tisc, transport_domain, ndev->res.tdn);
2721a86b377SEli Cohen 	err = mlx5_vdpa_create_tis(mvdev, in, &ndev->res.tisn);
2731a86b377SEli Cohen 	if (err)
2741a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "create TIS (%d)\n", err);
2751a86b377SEli Cohen 
2761a86b377SEli Cohen 	return err;
2771a86b377SEli Cohen }
2781a86b377SEli Cohen 
2791a86b377SEli Cohen static void destroy_tis(struct mlx5_vdpa_net *ndev)
2801a86b377SEli Cohen {
2811a86b377SEli Cohen 	mlx5_vdpa_destroy_tis(&ndev->mvdev, ndev->res.tisn);
2821a86b377SEli Cohen }
2831a86b377SEli Cohen 
2841a86b377SEli Cohen #define MLX5_VDPA_CQE_SIZE 64
2851a86b377SEli Cohen #define MLX5_VDPA_LOG_CQE_SIZE ilog2(MLX5_VDPA_CQE_SIZE)
2861a86b377SEli Cohen 
2871a86b377SEli Cohen static int cq_frag_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_cq_buf *buf, int nent)
2881a86b377SEli Cohen {
2891a86b377SEli Cohen 	struct mlx5_frag_buf *frag_buf = &buf->frag_buf;
2901a86b377SEli Cohen 	u8 log_wq_stride = MLX5_VDPA_LOG_CQE_SIZE;
2911a86b377SEli Cohen 	u8 log_wq_sz = MLX5_VDPA_LOG_CQE_SIZE;
2921a86b377SEli Cohen 	int err;
2931a86b377SEli Cohen 
2941a86b377SEli Cohen 	err = mlx5_frag_buf_alloc_node(ndev->mvdev.mdev, nent * MLX5_VDPA_CQE_SIZE, frag_buf,
2951a86b377SEli Cohen 				       ndev->mvdev.mdev->priv.numa_node);
2961a86b377SEli Cohen 	if (err)
2971a86b377SEli Cohen 		return err;
2981a86b377SEli Cohen 
2991a86b377SEli Cohen 	mlx5_init_fbc(frag_buf->frags, log_wq_stride, log_wq_sz, &buf->fbc);
3001a86b377SEli Cohen 
3011a86b377SEli Cohen 	buf->cqe_size = MLX5_VDPA_CQE_SIZE;
3021a86b377SEli Cohen 	buf->nent = nent;
3031a86b377SEli Cohen 
3041a86b377SEli Cohen 	return 0;
3051a86b377SEli Cohen }
3061a86b377SEli Cohen 
3071a86b377SEli Cohen static int umem_frag_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_umem *umem, int size)
3081a86b377SEli Cohen {
3091a86b377SEli Cohen 	struct mlx5_frag_buf *frag_buf = &umem->frag_buf;
3101a86b377SEli Cohen 
3111a86b377SEli Cohen 	return mlx5_frag_buf_alloc_node(ndev->mvdev.mdev, size, frag_buf,
3121a86b377SEli Cohen 					ndev->mvdev.mdev->priv.numa_node);
3131a86b377SEli Cohen }
3141a86b377SEli Cohen 
3151a86b377SEli Cohen static void cq_frag_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_cq_buf *buf)
3161a86b377SEli Cohen {
3171a86b377SEli Cohen 	mlx5_frag_buf_free(ndev->mvdev.mdev, &buf->frag_buf);
3181a86b377SEli Cohen }
3191a86b377SEli Cohen 
3201a86b377SEli Cohen static void *get_cqe(struct mlx5_vdpa_cq *vcq, int n)
3211a86b377SEli Cohen {
3221a86b377SEli Cohen 	return mlx5_frag_buf_get_wqe(&vcq->buf.fbc, n);
3231a86b377SEli Cohen }
3241a86b377SEli Cohen 
3251a86b377SEli Cohen static void cq_frag_buf_init(struct mlx5_vdpa_cq *vcq, struct mlx5_vdpa_cq_buf *buf)
3261a86b377SEli Cohen {
3271a86b377SEli Cohen 	struct mlx5_cqe64 *cqe64;
3281a86b377SEli Cohen 	void *cqe;
3291a86b377SEli Cohen 	int i;
3301a86b377SEli Cohen 
3311a86b377SEli Cohen 	for (i = 0; i < buf->nent; i++) {
3321a86b377SEli Cohen 		cqe = get_cqe(vcq, i);
3331a86b377SEli Cohen 		cqe64 = cqe;
3341a86b377SEli Cohen 		cqe64->op_own = MLX5_CQE_INVALID << 4;
3351a86b377SEli Cohen 	}
3361a86b377SEli Cohen }
3371a86b377SEli Cohen 
3381a86b377SEli Cohen static void *get_sw_cqe(struct mlx5_vdpa_cq *cq, int n)
3391a86b377SEli Cohen {
3401a86b377SEli Cohen 	struct mlx5_cqe64 *cqe64 = get_cqe(cq, n & (cq->cqe - 1));
3411a86b377SEli Cohen 
3421a86b377SEli Cohen 	if (likely(get_cqe_opcode(cqe64) != MLX5_CQE_INVALID) &&
3431a86b377SEli Cohen 	    !((cqe64->op_own & MLX5_CQE_OWNER_MASK) ^ !!(n & cq->cqe)))
3441a86b377SEli Cohen 		return cqe64;
3451a86b377SEli Cohen 
3461a86b377SEli Cohen 	return NULL;
3471a86b377SEli Cohen }
3481a86b377SEli Cohen 
3491a86b377SEli Cohen static void rx_post(struct mlx5_vdpa_qp *vqp, int n)
3501a86b377SEli Cohen {
3511a86b377SEli Cohen 	vqp->head += n;
3521a86b377SEli Cohen 	vqp->db.db[0] = cpu_to_be32(vqp->head);
3531a86b377SEli Cohen }
3541a86b377SEli Cohen 
3551a86b377SEli Cohen static void qp_prepare(struct mlx5_vdpa_net *ndev, bool fw, void *in,
3561a86b377SEli Cohen 		       struct mlx5_vdpa_virtqueue *mvq, u32 num_ent)
3571a86b377SEli Cohen {
3581a86b377SEli Cohen 	struct mlx5_vdpa_qp *vqp;
3591a86b377SEli Cohen 	__be64 *pas;
3601a86b377SEli Cohen 	void *qpc;
3611a86b377SEli Cohen 
3621a86b377SEli Cohen 	vqp = fw ? &mvq->fwqp : &mvq->vqqp;
3631a86b377SEli Cohen 	MLX5_SET(create_qp_in, in, uid, ndev->mvdev.res.uid);
3641a86b377SEli Cohen 	qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
3651a86b377SEli Cohen 	if (vqp->fw) {
3661a86b377SEli Cohen 		/* Firmware QP is allocated by the driver for the firmware's
3671a86b377SEli Cohen 		 * use so we can skip part of the params as they will be chosen by firmware
3681a86b377SEli Cohen 		 */
3691a86b377SEli Cohen 		qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
3701a86b377SEli Cohen 		MLX5_SET(qpc, qpc, rq_type, MLX5_ZERO_LEN_RQ);
3711a86b377SEli Cohen 		MLX5_SET(qpc, qpc, no_sq, 1);
3721a86b377SEli Cohen 		return;
3731a86b377SEli Cohen 	}
3741a86b377SEli Cohen 
3751a86b377SEli Cohen 	MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
3761a86b377SEli Cohen 	MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
3771a86b377SEli Cohen 	MLX5_SET(qpc, qpc, pd, ndev->mvdev.res.pdn);
3781a86b377SEli Cohen 	MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
3791a86b377SEli Cohen 	MLX5_SET(qpc, qpc, uar_page, ndev->mvdev.res.uar->index);
3801a86b377SEli Cohen 	MLX5_SET(qpc, qpc, log_page_size, vqp->frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
3811a86b377SEli Cohen 	MLX5_SET(qpc, qpc, no_sq, 1);
3821a86b377SEli Cohen 	MLX5_SET(qpc, qpc, cqn_rcv, mvq->cq.mcq.cqn);
3831a86b377SEli Cohen 	MLX5_SET(qpc, qpc, log_rq_size, ilog2(num_ent));
3841a86b377SEli Cohen 	MLX5_SET(qpc, qpc, rq_type, MLX5_NON_ZERO_RQ);
3851a86b377SEli Cohen 	pas = (__be64 *)MLX5_ADDR_OF(create_qp_in, in, pas);
3861a86b377SEli Cohen 	mlx5_fill_page_frag_array(&vqp->frag_buf, pas);
3871a86b377SEli Cohen }
3881a86b377SEli Cohen 
3891a86b377SEli Cohen static int rq_buf_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp, u32 num_ent)
3901a86b377SEli Cohen {
3911a86b377SEli Cohen 	return mlx5_frag_buf_alloc_node(ndev->mvdev.mdev,
3921a86b377SEli Cohen 					num_ent * sizeof(struct mlx5_wqe_data_seg), &vqp->frag_buf,
3931a86b377SEli Cohen 					ndev->mvdev.mdev->priv.numa_node);
3941a86b377SEli Cohen }
3951a86b377SEli Cohen 
3961a86b377SEli Cohen static void rq_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp)
3971a86b377SEli Cohen {
3981a86b377SEli Cohen 	mlx5_frag_buf_free(ndev->mvdev.mdev, &vqp->frag_buf);
3991a86b377SEli Cohen }
4001a86b377SEli Cohen 
4011a86b377SEli Cohen static int qp_create(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
4021a86b377SEli Cohen 		     struct mlx5_vdpa_qp *vqp)
4031a86b377SEli Cohen {
4041a86b377SEli Cohen 	struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
4051a86b377SEli Cohen 	int inlen = MLX5_ST_SZ_BYTES(create_qp_in);
4061a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
4071a86b377SEli Cohen 	void *qpc;
4081a86b377SEli Cohen 	void *in;
4091a86b377SEli Cohen 	int err;
4101a86b377SEli Cohen 
4111a86b377SEli Cohen 	if (!vqp->fw) {
4121a86b377SEli Cohen 		vqp = &mvq->vqqp;
4131a86b377SEli Cohen 		err = rq_buf_alloc(ndev, vqp, mvq->num_ent);
4141a86b377SEli Cohen 		if (err)
4151a86b377SEli Cohen 			return err;
4161a86b377SEli Cohen 
4171a86b377SEli Cohen 		err = mlx5_db_alloc(ndev->mvdev.mdev, &vqp->db);
4181a86b377SEli Cohen 		if (err)
4191a86b377SEli Cohen 			goto err_db;
4201a86b377SEli Cohen 		inlen += vqp->frag_buf.npages * sizeof(__be64);
4211a86b377SEli Cohen 	}
4221a86b377SEli Cohen 
4231a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
4241a86b377SEli Cohen 	if (!in) {
4251a86b377SEli Cohen 		err = -ENOMEM;
4261a86b377SEli Cohen 		goto err_kzalloc;
4271a86b377SEli Cohen 	}
4281a86b377SEli Cohen 
4291a86b377SEli Cohen 	qp_prepare(ndev, vqp->fw, in, mvq, mvq->num_ent);
4301a86b377SEli Cohen 	qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
4311a86b377SEli Cohen 	MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
4321a86b377SEli Cohen 	MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
4331a86b377SEli Cohen 	MLX5_SET(qpc, qpc, pd, ndev->mvdev.res.pdn);
4341a86b377SEli Cohen 	MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
4351a86b377SEli Cohen 	if (!vqp->fw)
4361a86b377SEli Cohen 		MLX5_SET64(qpc, qpc, dbr_addr, vqp->db.dma);
4371a86b377SEli Cohen 	MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
4381a86b377SEli Cohen 	err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
4391a86b377SEli Cohen 	kfree(in);
4401a86b377SEli Cohen 	if (err)
4411a86b377SEli Cohen 		goto err_kzalloc;
4421a86b377SEli Cohen 
4431a86b377SEli Cohen 	vqp->mqp.uid = ndev->mvdev.res.uid;
4441a86b377SEli Cohen 	vqp->mqp.qpn = MLX5_GET(create_qp_out, out, qpn);
4451a86b377SEli Cohen 
4461a86b377SEli Cohen 	if (!vqp->fw)
4471a86b377SEli Cohen 		rx_post(vqp, mvq->num_ent);
4481a86b377SEli Cohen 
4491a86b377SEli Cohen 	return 0;
4501a86b377SEli Cohen 
4511a86b377SEli Cohen err_kzalloc:
4521a86b377SEli Cohen 	if (!vqp->fw)
4531a86b377SEli Cohen 		mlx5_db_free(ndev->mvdev.mdev, &vqp->db);
4541a86b377SEli Cohen err_db:
4551a86b377SEli Cohen 	if (!vqp->fw)
4561a86b377SEli Cohen 		rq_buf_free(ndev, vqp);
4571a86b377SEli Cohen 
4581a86b377SEli Cohen 	return err;
4591a86b377SEli Cohen }
4601a86b377SEli Cohen 
4611a86b377SEli Cohen static void qp_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_qp *vqp)
4621a86b377SEli Cohen {
4631a86b377SEli Cohen 	u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
4641a86b377SEli Cohen 
4651a86b377SEli Cohen 	MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
4661a86b377SEli Cohen 	MLX5_SET(destroy_qp_in, in, qpn, vqp->mqp.qpn);
4671a86b377SEli Cohen 	MLX5_SET(destroy_qp_in, in, uid, ndev->mvdev.res.uid);
4681a86b377SEli Cohen 	if (mlx5_cmd_exec_in(ndev->mvdev.mdev, destroy_qp, in))
4691a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "destroy qp 0x%x\n", vqp->mqp.qpn);
4701a86b377SEli Cohen 	if (!vqp->fw) {
4711a86b377SEli Cohen 		mlx5_db_free(ndev->mvdev.mdev, &vqp->db);
4721a86b377SEli Cohen 		rq_buf_free(ndev, vqp);
4731a86b377SEli Cohen 	}
4741a86b377SEli Cohen }
4751a86b377SEli Cohen 
4761a86b377SEli Cohen static void *next_cqe_sw(struct mlx5_vdpa_cq *cq)
4771a86b377SEli Cohen {
4781a86b377SEli Cohen 	return get_sw_cqe(cq, cq->mcq.cons_index);
4791a86b377SEli Cohen }
4801a86b377SEli Cohen 
4811a86b377SEli Cohen static int mlx5_vdpa_poll_one(struct mlx5_vdpa_cq *vcq)
4821a86b377SEli Cohen {
4831a86b377SEli Cohen 	struct mlx5_cqe64 *cqe64;
4841a86b377SEli Cohen 
4851a86b377SEli Cohen 	cqe64 = next_cqe_sw(vcq);
4861a86b377SEli Cohen 	if (!cqe64)
4871a86b377SEli Cohen 		return -EAGAIN;
4881a86b377SEli Cohen 
4891a86b377SEli Cohen 	vcq->mcq.cons_index++;
4901a86b377SEli Cohen 	return 0;
4911a86b377SEli Cohen }
4921a86b377SEli Cohen 
4931a86b377SEli Cohen static void mlx5_vdpa_handle_completions(struct mlx5_vdpa_virtqueue *mvq, int num)
4941a86b377SEli Cohen {
495db296d25SEli Cohen 	struct mlx5_vdpa_net *ndev = mvq->ndev;
496db296d25SEli Cohen 	struct vdpa_callback *event_cb;
497db296d25SEli Cohen 
498db296d25SEli Cohen 	event_cb = &ndev->event_cbs[mvq->index];
4991a86b377SEli Cohen 	mlx5_cq_set_ci(&mvq->cq.mcq);
50083ef73b2SEli Cohen 
50183ef73b2SEli Cohen 	/* make sure CQ cosumer update is visible to the hardware before updating
50283ef73b2SEli Cohen 	 * RX doorbell record.
50383ef73b2SEli Cohen 	 */
50483ef73b2SEli Cohen 	dma_wmb();
5051a86b377SEli Cohen 	rx_post(&mvq->vqqp, num);
506db296d25SEli Cohen 	if (event_cb->callback)
507db296d25SEli Cohen 		event_cb->callback(event_cb->private);
5081a86b377SEli Cohen }
5091a86b377SEli Cohen 
5101a86b377SEli Cohen static void mlx5_vdpa_cq_comp(struct mlx5_core_cq *mcq, struct mlx5_eqe *eqe)
5111a86b377SEli Cohen {
5121a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq = container_of(mcq, struct mlx5_vdpa_virtqueue, cq.mcq);
5131a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = mvq->ndev;
5141a86b377SEli Cohen 	void __iomem *uar_page = ndev->mvdev.res.uar->map;
5151a86b377SEli Cohen 	int num = 0;
5161a86b377SEli Cohen 
5171a86b377SEli Cohen 	while (!mlx5_vdpa_poll_one(&mvq->cq)) {
5181a86b377SEli Cohen 		num++;
5191a86b377SEli Cohen 		if (num > mvq->num_ent / 2) {
5201a86b377SEli Cohen 			/* If completions keep coming while we poll, we want to
5211a86b377SEli Cohen 			 * let the hardware know that we consumed them by
5221a86b377SEli Cohen 			 * updating the doorbell record.  We also let vdpa core
5231a86b377SEli Cohen 			 * know about this so it passes it on the virtio driver
5241a86b377SEli Cohen 			 * on the guest.
5251a86b377SEli Cohen 			 */
5261a86b377SEli Cohen 			mlx5_vdpa_handle_completions(mvq, num);
5271a86b377SEli Cohen 			num = 0;
5281a86b377SEli Cohen 		}
5291a86b377SEli Cohen 	}
5301a86b377SEli Cohen 
5311a86b377SEli Cohen 	if (num)
5321a86b377SEli Cohen 		mlx5_vdpa_handle_completions(mvq, num);
5331a86b377SEli Cohen 
5341a86b377SEli Cohen 	mlx5_cq_arm(&mvq->cq.mcq, MLX5_CQ_DB_REQ_NOT, uar_page, mvq->cq.mcq.cons_index);
5351a86b377SEli Cohen }
5361a86b377SEli Cohen 
5371a86b377SEli Cohen static int cq_create(struct mlx5_vdpa_net *ndev, u16 idx, u32 num_ent)
5381a86b377SEli Cohen {
5391a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[idx];
5401a86b377SEli Cohen 	struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
5411a86b377SEli Cohen 	void __iomem *uar_page = ndev->mvdev.res.uar->map;
5421a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(create_cq_out)];
5431a86b377SEli Cohen 	struct mlx5_vdpa_cq *vcq = &mvq->cq;
5441a86b377SEli Cohen 	__be64 *pas;
5451a86b377SEli Cohen 	int inlen;
5461a86b377SEli Cohen 	void *cqc;
5471a86b377SEli Cohen 	void *in;
5481a86b377SEli Cohen 	int err;
5491a86b377SEli Cohen 	int eqn;
5501a86b377SEli Cohen 
5511a86b377SEli Cohen 	err = mlx5_db_alloc(mdev, &vcq->db);
5521a86b377SEli Cohen 	if (err)
5531a86b377SEli Cohen 		return err;
5541a86b377SEli Cohen 
5551a86b377SEli Cohen 	vcq->mcq.set_ci_db = vcq->db.db;
5561a86b377SEli Cohen 	vcq->mcq.arm_db = vcq->db.db + 1;
5571a86b377SEli Cohen 	vcq->mcq.cqe_sz = 64;
5581a86b377SEli Cohen 
5591a86b377SEli Cohen 	err = cq_frag_buf_alloc(ndev, &vcq->buf, num_ent);
5601a86b377SEli Cohen 	if (err)
5611a86b377SEli Cohen 		goto err_db;
5621a86b377SEli Cohen 
5631a86b377SEli Cohen 	cq_frag_buf_init(vcq, &vcq->buf);
5641a86b377SEli Cohen 
5651a86b377SEli Cohen 	inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
5661a86b377SEli Cohen 		MLX5_FLD_SZ_BYTES(create_cq_in, pas[0]) * vcq->buf.frag_buf.npages;
5671a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
5681a86b377SEli Cohen 	if (!in) {
5691a86b377SEli Cohen 		err = -ENOMEM;
5701a86b377SEli Cohen 		goto err_vzalloc;
5711a86b377SEli Cohen 	}
5721a86b377SEli Cohen 
5731a86b377SEli Cohen 	MLX5_SET(create_cq_in, in, uid, ndev->mvdev.res.uid);
5741a86b377SEli Cohen 	pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas);
5751a86b377SEli Cohen 	mlx5_fill_page_frag_array(&vcq->buf.frag_buf, pas);
5761a86b377SEli Cohen 
5771a86b377SEli Cohen 	cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
5781a86b377SEli Cohen 	MLX5_SET(cqc, cqc, log_page_size, vcq->buf.frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
5791a86b377SEli Cohen 
5801a86b377SEli Cohen 	/* Use vector 0 by default. Consider adding code to choose least used
5811a86b377SEli Cohen 	 * vector.
5821a86b377SEli Cohen 	 */
583563476aeSShay Drory 	err = mlx5_vector2eqn(mdev, 0, &eqn);
5841a86b377SEli Cohen 	if (err)
5851a86b377SEli Cohen 		goto err_vec;
5861a86b377SEli Cohen 
5871a86b377SEli Cohen 	cqc = MLX5_ADDR_OF(create_cq_in, in, cq_context);
5881a86b377SEli Cohen 	MLX5_SET(cqc, cqc, log_cq_size, ilog2(num_ent));
5891a86b377SEli Cohen 	MLX5_SET(cqc, cqc, uar_page, ndev->mvdev.res.uar->index);
590616d5769STal Gilboa 	MLX5_SET(cqc, cqc, c_eqn_or_apu_element, eqn);
5911a86b377SEli Cohen 	MLX5_SET64(cqc, cqc, dbr_addr, vcq->db.dma);
5921a86b377SEli Cohen 
5931a86b377SEli Cohen 	err = mlx5_core_create_cq(mdev, &vcq->mcq, in, inlen, out, sizeof(out));
5941a86b377SEli Cohen 	if (err)
5951a86b377SEli Cohen 		goto err_vec;
5961a86b377SEli Cohen 
5971a86b377SEli Cohen 	vcq->mcq.comp = mlx5_vdpa_cq_comp;
5981a86b377SEli Cohen 	vcq->cqe = num_ent;
5991a86b377SEli Cohen 	vcq->mcq.set_ci_db = vcq->db.db;
6001a86b377SEli Cohen 	vcq->mcq.arm_db = vcq->db.db + 1;
6011a86b377SEli Cohen 	mlx5_cq_arm(&mvq->cq.mcq, MLX5_CQ_DB_REQ_NOT, uar_page, mvq->cq.mcq.cons_index);
6021a86b377SEli Cohen 	kfree(in);
6031a86b377SEli Cohen 	return 0;
6041a86b377SEli Cohen 
6051a86b377SEli Cohen err_vec:
6061a86b377SEli Cohen 	kfree(in);
6071a86b377SEli Cohen err_vzalloc:
6081a86b377SEli Cohen 	cq_frag_buf_free(ndev, &vcq->buf);
6091a86b377SEli Cohen err_db:
6101a86b377SEli Cohen 	mlx5_db_free(ndev->mvdev.mdev, &vcq->db);
6111a86b377SEli Cohen 	return err;
6121a86b377SEli Cohen }
6131a86b377SEli Cohen 
6141a86b377SEli Cohen static void cq_destroy(struct mlx5_vdpa_net *ndev, u16 idx)
6151a86b377SEli Cohen {
6161a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq = &ndev->vqs[idx];
6171a86b377SEli Cohen 	struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
6181a86b377SEli Cohen 	struct mlx5_vdpa_cq *vcq = &mvq->cq;
6191a86b377SEli Cohen 
6201a86b377SEli Cohen 	if (mlx5_core_destroy_cq(mdev, &vcq->mcq)) {
6211a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "destroy CQ 0x%x\n", vcq->mcq.cqn);
6221a86b377SEli Cohen 		return;
6231a86b377SEli Cohen 	}
6241a86b377SEli Cohen 	cq_frag_buf_free(ndev, &vcq->buf);
6251a86b377SEli Cohen 	mlx5_db_free(ndev->mvdev.mdev, &vcq->db);
6261a86b377SEli Cohen }
6271a86b377SEli Cohen 
62871ab6a7cSEli Cohen static void set_umem_size(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num,
6291a86b377SEli Cohen 			  struct mlx5_vdpa_umem **umemp)
6301a86b377SEli Cohen {
6311a86b377SEli Cohen 	struct mlx5_core_dev *mdev = ndev->mvdev.mdev;
6321a86b377SEli Cohen 	int p_a;
6331a86b377SEli Cohen 	int p_b;
6341a86b377SEli Cohen 
6351a86b377SEli Cohen 	switch (num) {
6361a86b377SEli Cohen 	case 1:
6371a86b377SEli Cohen 		p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_1_buffer_param_a);
6381a86b377SEli Cohen 		p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_1_buffer_param_b);
6391a86b377SEli Cohen 		*umemp = &mvq->umem1;
6401a86b377SEli Cohen 		break;
6411a86b377SEli Cohen 	case 2:
6421a86b377SEli Cohen 		p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_2_buffer_param_a);
6431a86b377SEli Cohen 		p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_2_buffer_param_b);
6441a86b377SEli Cohen 		*umemp = &mvq->umem2;
6451a86b377SEli Cohen 		break;
6461a86b377SEli Cohen 	case 3:
6471a86b377SEli Cohen 		p_a = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_3_buffer_param_a);
6481a86b377SEli Cohen 		p_b = MLX5_CAP_DEV_VDPA_EMULATION(mdev, umem_3_buffer_param_b);
6491a86b377SEli Cohen 		*umemp = &mvq->umem3;
6501a86b377SEli Cohen 		break;
6511a86b377SEli Cohen 	}
65271ab6a7cSEli Cohen 	(*umemp)->size = p_a * mvq->num_ent + p_b;
6531a86b377SEli Cohen }
6541a86b377SEli Cohen 
6551a86b377SEli Cohen static void umem_frag_buf_free(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_umem *umem)
6561a86b377SEli Cohen {
6571a86b377SEli Cohen 	mlx5_frag_buf_free(ndev->mvdev.mdev, &umem->frag_buf);
6581a86b377SEli Cohen }
6591a86b377SEli Cohen 
6601a86b377SEli Cohen static int create_umem(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num)
6611a86b377SEli Cohen {
6621a86b377SEli Cohen 	int inlen;
6631a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(create_umem_out)] = {};
6641a86b377SEli Cohen 	void *um;
6651a86b377SEli Cohen 	void *in;
6661a86b377SEli Cohen 	int err;
6671a86b377SEli Cohen 	__be64 *pas;
6681a86b377SEli Cohen 	struct mlx5_vdpa_umem *umem;
6691a86b377SEli Cohen 
67071ab6a7cSEli Cohen 	set_umem_size(ndev, mvq, num, &umem);
67171ab6a7cSEli Cohen 	err = umem_frag_buf_alloc(ndev, umem, umem->size);
6721a86b377SEli Cohen 	if (err)
6731a86b377SEli Cohen 		return err;
6741a86b377SEli Cohen 
6751a86b377SEli Cohen 	inlen = MLX5_ST_SZ_BYTES(create_umem_in) + MLX5_ST_SZ_BYTES(mtt) * umem->frag_buf.npages;
6761a86b377SEli Cohen 
6771a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
6781a86b377SEli Cohen 	if (!in) {
6791a86b377SEli Cohen 		err = -ENOMEM;
6801a86b377SEli Cohen 		goto err_in;
6811a86b377SEli Cohen 	}
6821a86b377SEli Cohen 
6831a86b377SEli Cohen 	MLX5_SET(create_umem_in, in, opcode, MLX5_CMD_OP_CREATE_UMEM);
6841a86b377SEli Cohen 	MLX5_SET(create_umem_in, in, uid, ndev->mvdev.res.uid);
6851a86b377SEli Cohen 	um = MLX5_ADDR_OF(create_umem_in, in, umem);
6861a86b377SEli Cohen 	MLX5_SET(umem, um, log_page_size, umem->frag_buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT);
6871a86b377SEli Cohen 	MLX5_SET64(umem, um, num_of_mtt, umem->frag_buf.npages);
6881a86b377SEli Cohen 
6891a86b377SEli Cohen 	pas = (__be64 *)MLX5_ADDR_OF(umem, um, mtt[0]);
6901a86b377SEli Cohen 	mlx5_fill_page_frag_array_perm(&umem->frag_buf, pas, MLX5_MTT_PERM_RW);
6911a86b377SEli Cohen 
6921a86b377SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
6931a86b377SEli Cohen 	if (err) {
6941a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "create umem(%d)\n", err);
6951a86b377SEli Cohen 		goto err_cmd;
6961a86b377SEli Cohen 	}
6971a86b377SEli Cohen 
6981a86b377SEli Cohen 	kfree(in);
6991a86b377SEli Cohen 	umem->id = MLX5_GET(create_umem_out, out, umem_id);
7001a86b377SEli Cohen 
7011a86b377SEli Cohen 	return 0;
7021a86b377SEli Cohen 
7031a86b377SEli Cohen err_cmd:
7041a86b377SEli Cohen 	kfree(in);
7051a86b377SEli Cohen err_in:
7061a86b377SEli Cohen 	umem_frag_buf_free(ndev, umem);
7071a86b377SEli Cohen 	return err;
7081a86b377SEli Cohen }
7091a86b377SEli Cohen 
7101a86b377SEli Cohen static void umem_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int num)
7111a86b377SEli Cohen {
7121a86b377SEli Cohen 	u32 in[MLX5_ST_SZ_DW(destroy_umem_in)] = {};
7131a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(destroy_umem_out)] = {};
7141a86b377SEli Cohen 	struct mlx5_vdpa_umem *umem;
7151a86b377SEli Cohen 
7161a86b377SEli Cohen 	switch (num) {
7171a86b377SEli Cohen 	case 1:
7181a86b377SEli Cohen 		umem = &mvq->umem1;
7191a86b377SEli Cohen 		break;
7201a86b377SEli Cohen 	case 2:
7211a86b377SEli Cohen 		umem = &mvq->umem2;
7221a86b377SEli Cohen 		break;
7231a86b377SEli Cohen 	case 3:
7241a86b377SEli Cohen 		umem = &mvq->umem3;
7251a86b377SEli Cohen 		break;
7261a86b377SEli Cohen 	}
7271a86b377SEli Cohen 
7281a86b377SEli Cohen 	MLX5_SET(destroy_umem_in, in, opcode, MLX5_CMD_OP_DESTROY_UMEM);
7291a86b377SEli Cohen 	MLX5_SET(destroy_umem_in, in, umem_id, umem->id);
7301a86b377SEli Cohen 	if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out)))
7311a86b377SEli Cohen 		return;
7321a86b377SEli Cohen 
7331a86b377SEli Cohen 	umem_frag_buf_free(ndev, umem);
7341a86b377SEli Cohen }
7351a86b377SEli Cohen 
7361a86b377SEli Cohen static int umems_create(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
7371a86b377SEli Cohen {
7381a86b377SEli Cohen 	int num;
7391a86b377SEli Cohen 	int err;
7401a86b377SEli Cohen 
7411a86b377SEli Cohen 	for (num = 1; num <= 3; num++) {
7421a86b377SEli Cohen 		err = create_umem(ndev, mvq, num);
7431a86b377SEli Cohen 		if (err)
7441a86b377SEli Cohen 			goto err_umem;
7451a86b377SEli Cohen 	}
7461a86b377SEli Cohen 	return 0;
7471a86b377SEli Cohen 
7481a86b377SEli Cohen err_umem:
7491a86b377SEli Cohen 	for (num--; num > 0; num--)
7501a86b377SEli Cohen 		umem_destroy(ndev, mvq, num);
7511a86b377SEli Cohen 
7521a86b377SEli Cohen 	return err;
7531a86b377SEli Cohen }
7541a86b377SEli Cohen 
7551a86b377SEli Cohen static void umems_destroy(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
7561a86b377SEli Cohen {
7571a86b377SEli Cohen 	int num;
7581a86b377SEli Cohen 
7591a86b377SEli Cohen 	for (num = 3; num > 0; num--)
7601a86b377SEli Cohen 		umem_destroy(ndev, mvq, num);
7611a86b377SEli Cohen }
7621a86b377SEli Cohen 
7631a86b377SEli Cohen static int get_queue_type(struct mlx5_vdpa_net *ndev)
7641a86b377SEli Cohen {
7651a86b377SEli Cohen 	u32 type_mask;
7661a86b377SEli Cohen 
7671a86b377SEli Cohen 	type_mask = MLX5_CAP_DEV_VDPA_EMULATION(ndev->mvdev.mdev, virtio_queue_type);
7681a86b377SEli Cohen 
7691a86b377SEli Cohen 	/* prefer split queue */
770879753c8SEli Cohen 	if (type_mask & MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT)
7711a86b377SEli Cohen 		return MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_SPLIT;
772879753c8SEli Cohen 
773879753c8SEli Cohen 	WARN_ON(!(type_mask & MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_PACKED));
774879753c8SEli Cohen 
775879753c8SEli Cohen 	return MLX5_VIRTIO_EMULATION_VIRTIO_QUEUE_TYPE_PACKED;
7761a86b377SEli Cohen }
7771a86b377SEli Cohen 
7781a86b377SEli Cohen static bool vq_is_tx(u16 idx)
7791a86b377SEli Cohen {
7801a86b377SEli Cohen 	return idx % 2;
7811a86b377SEli Cohen }
7821a86b377SEli Cohen 
783e9d67e59SEli Cohen enum {
784e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_MRG_RXBUF = 2,
785e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_HOST_ECN = 4,
786e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_GUEST_ECN = 6,
787e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_GUEST_TSO6 = 7,
788e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_GUEST_TSO4 = 8,
789e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_GUEST_CSUM = 9,
790e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_CSUM = 10,
791e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_HOST_TSO6 = 11,
792e9d67e59SEli Cohen 	MLX5_VIRTIO_NET_F_HOST_TSO4 = 12,
793e9d67e59SEli Cohen };
794e9d67e59SEli Cohen 
795e9d67e59SEli Cohen static u16 get_features(u64 features)
7961a86b377SEli Cohen {
797e9d67e59SEli Cohen 	return (!!(features & BIT_ULL(VIRTIO_NET_F_MRG_RXBUF)) << MLX5_VIRTIO_NET_F_MRG_RXBUF) |
798e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_HOST_ECN)) << MLX5_VIRTIO_NET_F_HOST_ECN) |
799e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_GUEST_ECN)) << MLX5_VIRTIO_NET_F_GUEST_ECN) |
800e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_GUEST_TSO6)) << MLX5_VIRTIO_NET_F_GUEST_TSO6) |
801e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_GUEST_TSO4)) << MLX5_VIRTIO_NET_F_GUEST_TSO4) |
802e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_CSUM)) << MLX5_VIRTIO_NET_F_CSUM) |
803e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_HOST_TSO6)) << MLX5_VIRTIO_NET_F_HOST_TSO6) |
804e9d67e59SEli Cohen 	       (!!(features & BIT_ULL(VIRTIO_NET_F_HOST_TSO4)) << MLX5_VIRTIO_NET_F_HOST_TSO4);
8051a86b377SEli Cohen }
8061a86b377SEli Cohen 
8071892a3d4SEli Cohen static bool counters_supported(const struct mlx5_vdpa_dev *mvdev)
8081892a3d4SEli Cohen {
8091892a3d4SEli Cohen 	return MLX5_CAP_GEN_64(mvdev->mdev, general_obj_types) &
8101892a3d4SEli Cohen 	       BIT_ULL(MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
8111892a3d4SEli Cohen }
8121892a3d4SEli Cohen 
813bc9a2b3eSEli Cohen static bool msix_mode_supported(struct mlx5_vdpa_dev *mvdev)
814bc9a2b3eSEli Cohen {
815bc9a2b3eSEli Cohen 	return MLX5_CAP_DEV_VDPA_EMULATION(mvdev->mdev, event_mode) &
816bc9a2b3eSEli Cohen 		(1 << MLX5_VIRTIO_Q_EVENT_MODE_MSIX_MODE) &&
817bc9a2b3eSEli Cohen 		pci_msix_can_alloc_dyn(mvdev->mdev->pdev);
818bc9a2b3eSEli Cohen }
819bc9a2b3eSEli Cohen 
8201a86b377SEli Cohen static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
8211a86b377SEli Cohen {
8221a86b377SEli Cohen 	int inlen = MLX5_ST_SZ_BYTES(create_virtio_net_q_in);
8231a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(create_virtio_net_q_out)] = {};
8241a86b377SEli Cohen 	void *obj_context;
825e9d67e59SEli Cohen 	u16 mlx_features;
8261a86b377SEli Cohen 	void *cmd_hdr;
8271a86b377SEli Cohen 	void *vq_ctx;
8281a86b377SEli Cohen 	void *in;
8291a86b377SEli Cohen 	int err;
8301a86b377SEli Cohen 
8311a86b377SEli Cohen 	err = umems_create(ndev, mvq);
8321a86b377SEli Cohen 	if (err)
8331a86b377SEli Cohen 		return err;
8341a86b377SEli Cohen 
8351a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
8361a86b377SEli Cohen 	if (!in) {
8371a86b377SEli Cohen 		err = -ENOMEM;
8381a86b377SEli Cohen 		goto err_alloc;
8391a86b377SEli Cohen 	}
8401a86b377SEli Cohen 
841e9d67e59SEli Cohen 	mlx_features = get_features(ndev->mvdev.actual_features);
8421a86b377SEli Cohen 	cmd_hdr = MLX5_ADDR_OF(create_virtio_net_q_in, in, general_obj_in_cmd_hdr);
8431a86b377SEli Cohen 
8441a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
8451a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
8461a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
8471a86b377SEli Cohen 
8481a86b377SEli Cohen 	obj_context = MLX5_ADDR_OF(create_virtio_net_q_in, in, obj_context);
8491a86b377SEli Cohen 	MLX5_SET(virtio_net_q_object, obj_context, hw_available_index, mvq->avail_idx);
850b35ccebeSEli Cohen 	MLX5_SET(virtio_net_q_object, obj_context, hw_used_index, mvq->used_idx);
8511a86b377SEli Cohen 	MLX5_SET(virtio_net_q_object, obj_context, queue_feature_bit_mask_12_3,
852e9d67e59SEli Cohen 		 mlx_features >> 3);
853e9d67e59SEli Cohen 	MLX5_SET(virtio_net_q_object, obj_context, queue_feature_bit_mask_2_0,
854e9d67e59SEli Cohen 		 mlx_features & 7);
8551a86b377SEli Cohen 	vq_ctx = MLX5_ADDR_OF(virtio_net_q_object, obj_context, virtio_q_context);
8561a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, virtio_q_type, get_queue_type(ndev));
8571a86b377SEli Cohen 
8581a86b377SEli Cohen 	if (vq_is_tx(mvq->index))
8591a86b377SEli Cohen 		MLX5_SET(virtio_net_q_object, obj_context, tisn_or_qpn, ndev->res.tisn);
8601a86b377SEli Cohen 
861bc9a2b3eSEli Cohen 	if (mvq->map.virq) {
862bc9a2b3eSEli Cohen 		MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_MSIX_MODE);
863bc9a2b3eSEli Cohen 		MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->map.index);
864bc9a2b3eSEli Cohen 	} else {
8651a86b377SEli Cohen 		MLX5_SET(virtio_q, vq_ctx, event_mode, MLX5_VIRTIO_Q_EVENT_MODE_QP_MODE);
8661a86b377SEli Cohen 		MLX5_SET(virtio_q, vq_ctx, event_qpn_or_msix, mvq->fwqp.mqp.qpn);
867bc9a2b3eSEli Cohen 	}
868bc9a2b3eSEli Cohen 
869bc9a2b3eSEli Cohen 	MLX5_SET(virtio_q, vq_ctx, queue_index, mvq->index);
8701a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, queue_size, mvq->num_ent);
8711a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, virtio_version_1_0,
8724b454a82SEli Cohen 		 !!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_F_VERSION_1)));
8731a86b377SEli Cohen 	MLX5_SET64(virtio_q, vq_ctx, desc_addr, mvq->desc_addr);
8741a86b377SEli Cohen 	MLX5_SET64(virtio_q, vq_ctx, used_addr, mvq->device_addr);
8751a86b377SEli Cohen 	MLX5_SET64(virtio_q, vq_ctx, available_addr, mvq->driver_addr);
87683fec3f1SAharon Landau 	MLX5_SET(virtio_q, vq_ctx, virtio_q_mkey, ndev->mvdev.mr.mkey);
8771a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_1_id, mvq->umem1.id);
8781a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_1_size, mvq->umem1.size);
8791a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_2_id, mvq->umem2.id);
880e3011776SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_2_size, mvq->umem2.size);
8811a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_3_id, mvq->umem3.id);
882e3011776SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, umem_3_size, mvq->umem3.size);
8831a86b377SEli Cohen 	MLX5_SET(virtio_q, vq_ctx, pd, ndev->mvdev.res.pdn);
8841892a3d4SEli Cohen 	if (counters_supported(&ndev->mvdev))
8851892a3d4SEli Cohen 		MLX5_SET(virtio_q, vq_ctx, counter_set_id, mvq->counter_set_id);
8861a86b377SEli Cohen 
8871a86b377SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
8881a86b377SEli Cohen 	if (err)
8891a86b377SEli Cohen 		goto err_cmd;
8901a86b377SEli Cohen 
891cae15c2eSEli Cohen 	mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT;
8921a86b377SEli Cohen 	kfree(in);
8931a86b377SEli Cohen 	mvq->virtq_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
8941a86b377SEli Cohen 
8951a86b377SEli Cohen 	return 0;
8961a86b377SEli Cohen 
8971a86b377SEli Cohen err_cmd:
8981a86b377SEli Cohen 	kfree(in);
8991a86b377SEli Cohen err_alloc:
9001a86b377SEli Cohen 	umems_destroy(ndev, mvq);
9011a86b377SEli Cohen 	return err;
9021a86b377SEli Cohen }
9031a86b377SEli Cohen 
9041a86b377SEli Cohen static void destroy_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
9051a86b377SEli Cohen {
9061a86b377SEli Cohen 	u32 in[MLX5_ST_SZ_DW(destroy_virtio_net_q_in)] = {};
9071a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(destroy_virtio_net_q_out)] = {};
9081a86b377SEli Cohen 
9091a86b377SEli Cohen 	MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.opcode,
9101a86b377SEli Cohen 		 MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
9111a86b377SEli Cohen 	MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.obj_id, mvq->virtq_id);
9121a86b377SEli Cohen 	MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.uid, ndev->mvdev.res.uid);
9131a86b377SEli Cohen 	MLX5_SET(destroy_virtio_net_q_in, in, general_obj_out_cmd_hdr.obj_type,
9141a86b377SEli Cohen 		 MLX5_OBJ_TYPE_VIRTIO_NET_Q);
9151a86b377SEli Cohen 	if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out))) {
9161a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "destroy virtqueue 0x%x\n", mvq->virtq_id);
9171a86b377SEli Cohen 		return;
9181a86b377SEli Cohen 	}
919cae15c2eSEli Cohen 	mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE;
9201a86b377SEli Cohen 	umems_destroy(ndev, mvq);
9211a86b377SEli Cohen }
9221a86b377SEli Cohen 
9231a86b377SEli Cohen static u32 get_rqpn(struct mlx5_vdpa_virtqueue *mvq, bool fw)
9241a86b377SEli Cohen {
9251a86b377SEli Cohen 	return fw ? mvq->vqqp.mqp.qpn : mvq->fwqp.mqp.qpn;
9261a86b377SEli Cohen }
9271a86b377SEli Cohen 
9281a86b377SEli Cohen static u32 get_qpn(struct mlx5_vdpa_virtqueue *mvq, bool fw)
9291a86b377SEli Cohen {
9301a86b377SEli Cohen 	return fw ? mvq->fwqp.mqp.qpn : mvq->vqqp.mqp.qpn;
9311a86b377SEli Cohen }
9321a86b377SEli Cohen 
9331a86b377SEli Cohen static void alloc_inout(struct mlx5_vdpa_net *ndev, int cmd, void **in, int *inlen, void **out,
9341a86b377SEli Cohen 			int *outlen, u32 qpn, u32 rqpn)
9351a86b377SEli Cohen {
9361a86b377SEli Cohen 	void *qpc;
9371a86b377SEli Cohen 	void *pp;
9381a86b377SEli Cohen 
9391a86b377SEli Cohen 	switch (cmd) {
9401a86b377SEli Cohen 	case MLX5_CMD_OP_2RST_QP:
9411a86b377SEli Cohen 		*inlen = MLX5_ST_SZ_BYTES(qp_2rst_in);
9421a86b377SEli Cohen 		*outlen = MLX5_ST_SZ_BYTES(qp_2rst_out);
9431a86b377SEli Cohen 		*in = kzalloc(*inlen, GFP_KERNEL);
9441a86b377SEli Cohen 		*out = kzalloc(*outlen, GFP_KERNEL);
945f31231bfSColin Ian King 		if (!*in || !*out)
9461a86b377SEli Cohen 			goto outerr;
9471a86b377SEli Cohen 
9481a86b377SEli Cohen 		MLX5_SET(qp_2rst_in, *in, opcode, cmd);
9491a86b377SEli Cohen 		MLX5_SET(qp_2rst_in, *in, uid, ndev->mvdev.res.uid);
9501a86b377SEli Cohen 		MLX5_SET(qp_2rst_in, *in, qpn, qpn);
9511a86b377SEli Cohen 		break;
9521a86b377SEli Cohen 	case MLX5_CMD_OP_RST2INIT_QP:
9531a86b377SEli Cohen 		*inlen = MLX5_ST_SZ_BYTES(rst2init_qp_in);
9541a86b377SEli Cohen 		*outlen = MLX5_ST_SZ_BYTES(rst2init_qp_out);
9551a86b377SEli Cohen 		*in = kzalloc(*inlen, GFP_KERNEL);
9561a86b377SEli Cohen 		*out = kzalloc(MLX5_ST_SZ_BYTES(rst2init_qp_out), GFP_KERNEL);
957f31231bfSColin Ian King 		if (!*in || !*out)
9581a86b377SEli Cohen 			goto outerr;
9591a86b377SEli Cohen 
9601a86b377SEli Cohen 		MLX5_SET(rst2init_qp_in, *in, opcode, cmd);
9611a86b377SEli Cohen 		MLX5_SET(rst2init_qp_in, *in, uid, ndev->mvdev.res.uid);
9621a86b377SEli Cohen 		MLX5_SET(rst2init_qp_in, *in, qpn, qpn);
9631a86b377SEli Cohen 		qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
9641a86b377SEli Cohen 		MLX5_SET(qpc, qpc, remote_qpn, rqpn);
9651a86b377SEli Cohen 		MLX5_SET(qpc, qpc, rwe, 1);
9661a86b377SEli Cohen 		pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
9671a86b377SEli Cohen 		MLX5_SET(ads, pp, vhca_port_num, 1);
9681a86b377SEli Cohen 		break;
9691a86b377SEli Cohen 	case MLX5_CMD_OP_INIT2RTR_QP:
9701a86b377SEli Cohen 		*inlen = MLX5_ST_SZ_BYTES(init2rtr_qp_in);
9711a86b377SEli Cohen 		*outlen = MLX5_ST_SZ_BYTES(init2rtr_qp_out);
9721a86b377SEli Cohen 		*in = kzalloc(*inlen, GFP_KERNEL);
9731a86b377SEli Cohen 		*out = kzalloc(MLX5_ST_SZ_BYTES(init2rtr_qp_out), GFP_KERNEL);
974f31231bfSColin Ian King 		if (!*in || !*out)
9751a86b377SEli Cohen 			goto outerr;
9761a86b377SEli Cohen 
9771a86b377SEli Cohen 		MLX5_SET(init2rtr_qp_in, *in, opcode, cmd);
9781a86b377SEli Cohen 		MLX5_SET(init2rtr_qp_in, *in, uid, ndev->mvdev.res.uid);
9791a86b377SEli Cohen 		MLX5_SET(init2rtr_qp_in, *in, qpn, qpn);
9801a86b377SEli Cohen 		qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
9811a86b377SEli Cohen 		MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_256_BYTES);
9821a86b377SEli Cohen 		MLX5_SET(qpc, qpc, log_msg_max, 30);
9831a86b377SEli Cohen 		MLX5_SET(qpc, qpc, remote_qpn, rqpn);
9841a86b377SEli Cohen 		pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
9851a86b377SEli Cohen 		MLX5_SET(ads, pp, fl, 1);
9861a86b377SEli Cohen 		break;
9871a86b377SEli Cohen 	case MLX5_CMD_OP_RTR2RTS_QP:
9881a86b377SEli Cohen 		*inlen = MLX5_ST_SZ_BYTES(rtr2rts_qp_in);
9891a86b377SEli Cohen 		*outlen = MLX5_ST_SZ_BYTES(rtr2rts_qp_out);
9901a86b377SEli Cohen 		*in = kzalloc(*inlen, GFP_KERNEL);
9911a86b377SEli Cohen 		*out = kzalloc(MLX5_ST_SZ_BYTES(rtr2rts_qp_out), GFP_KERNEL);
992f31231bfSColin Ian King 		if (!*in || !*out)
9931a86b377SEli Cohen 			goto outerr;
9941a86b377SEli Cohen 
9951a86b377SEli Cohen 		MLX5_SET(rtr2rts_qp_in, *in, opcode, cmd);
9961a86b377SEli Cohen 		MLX5_SET(rtr2rts_qp_in, *in, uid, ndev->mvdev.res.uid);
9971a86b377SEli Cohen 		MLX5_SET(rtr2rts_qp_in, *in, qpn, qpn);
9981a86b377SEli Cohen 		qpc = MLX5_ADDR_OF(rst2init_qp_in, *in, qpc);
9991a86b377SEli Cohen 		pp = MLX5_ADDR_OF(qpc, qpc, primary_address_path);
10001a86b377SEli Cohen 		MLX5_SET(ads, pp, ack_timeout, 14);
10011a86b377SEli Cohen 		MLX5_SET(qpc, qpc, retry_count, 7);
10021a86b377SEli Cohen 		MLX5_SET(qpc, qpc, rnr_retry, 7);
10031a86b377SEli Cohen 		break;
10041a86b377SEli Cohen 	default:
1005f31231bfSColin Ian King 		goto outerr_nullify;
10061a86b377SEli Cohen 	}
10071a86b377SEli Cohen 
10081a86b377SEli Cohen 	return;
10091a86b377SEli Cohen 
10101a86b377SEli Cohen outerr:
10111a86b377SEli Cohen 	kfree(*in);
10121a86b377SEli Cohen 	kfree(*out);
1013f31231bfSColin Ian King outerr_nullify:
10141a86b377SEli Cohen 	*in = NULL;
10151a86b377SEli Cohen 	*out = NULL;
10161a86b377SEli Cohen }
10171a86b377SEli Cohen 
10181a86b377SEli Cohen static void free_inout(void *in, void *out)
10191a86b377SEli Cohen {
10201a86b377SEli Cohen 	kfree(in);
10211a86b377SEli Cohen 	kfree(out);
10221a86b377SEli Cohen }
10231a86b377SEli Cohen 
10241a86b377SEli Cohen /* Two QPs are used by each virtqueue. One is used by the driver and one by
10251a86b377SEli Cohen  * firmware. The fw argument indicates whether the subjected QP is the one used
10261a86b377SEli Cohen  * by firmware.
10271a86b377SEli Cohen  */
10281a86b377SEli Cohen static int modify_qp(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, bool fw, int cmd)
10291a86b377SEli Cohen {
10301a86b377SEli Cohen 	int outlen;
10311a86b377SEli Cohen 	int inlen;
10321a86b377SEli Cohen 	void *out;
10331a86b377SEli Cohen 	void *in;
10341a86b377SEli Cohen 	int err;
10351a86b377SEli Cohen 
10361a86b377SEli Cohen 	alloc_inout(ndev, cmd, &in, &inlen, &out, &outlen, get_qpn(mvq, fw), get_rqpn(mvq, fw));
10371a86b377SEli Cohen 	if (!in || !out)
10381a86b377SEli Cohen 		return -ENOMEM;
10391a86b377SEli Cohen 
10401a86b377SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, outlen);
10411a86b377SEli Cohen 	free_inout(in, out);
10421a86b377SEli Cohen 	return err;
10431a86b377SEli Cohen }
10441a86b377SEli Cohen 
10451a86b377SEli Cohen static int connect_qps(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
10461a86b377SEli Cohen {
10471a86b377SEli Cohen 	int err;
10481a86b377SEli Cohen 
10491a86b377SEli Cohen 	err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_2RST_QP);
10501a86b377SEli Cohen 	if (err)
10511a86b377SEli Cohen 		return err;
10521a86b377SEli Cohen 
10531a86b377SEli Cohen 	err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_2RST_QP);
10541a86b377SEli Cohen 	if (err)
10551a86b377SEli Cohen 		return err;
10561a86b377SEli Cohen 
10571a86b377SEli Cohen 	err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_RST2INIT_QP);
10581a86b377SEli Cohen 	if (err)
10591a86b377SEli Cohen 		return err;
10601a86b377SEli Cohen 
10611a86b377SEli Cohen 	err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_RST2INIT_QP);
10621a86b377SEli Cohen 	if (err)
10631a86b377SEli Cohen 		return err;
10641a86b377SEli Cohen 
10651a86b377SEli Cohen 	err = modify_qp(ndev, mvq, true, MLX5_CMD_OP_INIT2RTR_QP);
10661a86b377SEli Cohen 	if (err)
10671a86b377SEli Cohen 		return err;
10681a86b377SEli Cohen 
10691a86b377SEli Cohen 	err = modify_qp(ndev, mvq, false, MLX5_CMD_OP_INIT2RTR_QP);
10701a86b377SEli Cohen 	if (err)
10711a86b377SEli Cohen 		return err;
10721a86b377SEli Cohen 
10731a86b377SEli Cohen 	return modify_qp(ndev, mvq, true, MLX5_CMD_OP_RTR2RTS_QP);
10741a86b377SEli Cohen }
10751a86b377SEli Cohen 
10761a86b377SEli Cohen struct mlx5_virtq_attr {
10771a86b377SEli Cohen 	u8 state;
10781a86b377SEli Cohen 	u16 available_index;
1079b35ccebeSEli Cohen 	u16 used_index;
10801a86b377SEli Cohen };
10811a86b377SEli Cohen 
10821a86b377SEli Cohen static int query_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
10831a86b377SEli Cohen 			   struct mlx5_virtq_attr *attr)
10841a86b377SEli Cohen {
10851a86b377SEli Cohen 	int outlen = MLX5_ST_SZ_BYTES(query_virtio_net_q_out);
10861a86b377SEli Cohen 	u32 in[MLX5_ST_SZ_DW(query_virtio_net_q_in)] = {};
10871a86b377SEli Cohen 	void *out;
10881a86b377SEli Cohen 	void *obj_context;
10891a86b377SEli Cohen 	void *cmd_hdr;
10901a86b377SEli Cohen 	int err;
10911a86b377SEli Cohen 
10921a86b377SEli Cohen 	out = kzalloc(outlen, GFP_KERNEL);
10931a86b377SEli Cohen 	if (!out)
10941a86b377SEli Cohen 		return -ENOMEM;
10951a86b377SEli Cohen 
10961a86b377SEli Cohen 	cmd_hdr = MLX5_ADDR_OF(query_virtio_net_q_in, in, general_obj_in_cmd_hdr);
10971a86b377SEli Cohen 
10981a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
10991a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
11001a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id);
11011a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
11021a86b377SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, outlen);
11031a86b377SEli Cohen 	if (err)
11041a86b377SEli Cohen 		goto err_cmd;
11051a86b377SEli Cohen 
11061a86b377SEli Cohen 	obj_context = MLX5_ADDR_OF(query_virtio_net_q_out, out, obj_context);
11071a86b377SEli Cohen 	memset(attr, 0, sizeof(*attr));
11081a86b377SEli Cohen 	attr->state = MLX5_GET(virtio_net_q_object, obj_context, state);
11091a86b377SEli Cohen 	attr->available_index = MLX5_GET(virtio_net_q_object, obj_context, hw_available_index);
1110b35ccebeSEli Cohen 	attr->used_index = MLX5_GET(virtio_net_q_object, obj_context, hw_used_index);
11111a86b377SEli Cohen 	kfree(out);
11121a86b377SEli Cohen 	return 0;
11131a86b377SEli Cohen 
11141a86b377SEli Cohen err_cmd:
11151a86b377SEli Cohen 	kfree(out);
11161a86b377SEli Cohen 	return err;
11171a86b377SEli Cohen }
11181a86b377SEli Cohen 
1119cae15c2eSEli Cohen static bool is_valid_state_change(int oldstate, int newstate)
1120cae15c2eSEli Cohen {
1121cae15c2eSEli Cohen 	switch (oldstate) {
1122cae15c2eSEli Cohen 	case MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT:
1123cae15c2eSEli Cohen 		return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY;
1124cae15c2eSEli Cohen 	case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY:
1125cae15c2eSEli Cohen 		return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND;
1126cae15c2eSEli Cohen 	case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND:
1127cae15c2eSEli Cohen 	case MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR:
1128cae15c2eSEli Cohen 	default:
1129cae15c2eSEli Cohen 		return false;
1130cae15c2eSEli Cohen 	}
1131cae15c2eSEli Cohen }
1132cae15c2eSEli Cohen 
11331a86b377SEli Cohen static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int state)
11341a86b377SEli Cohen {
11351a86b377SEli Cohen 	int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in);
11361a86b377SEli Cohen 	u32 out[MLX5_ST_SZ_DW(modify_virtio_net_q_out)] = {};
11371a86b377SEli Cohen 	void *obj_context;
11381a86b377SEli Cohen 	void *cmd_hdr;
11391a86b377SEli Cohen 	void *in;
11401a86b377SEli Cohen 	int err;
11411a86b377SEli Cohen 
1142cae15c2eSEli Cohen 	if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE)
1143cae15c2eSEli Cohen 		return 0;
1144cae15c2eSEli Cohen 
1145cae15c2eSEli Cohen 	if (!is_valid_state_change(mvq->fw_state, state))
1146cae15c2eSEli Cohen 		return -EINVAL;
1147cae15c2eSEli Cohen 
11481a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
11491a86b377SEli Cohen 	if (!in)
11501a86b377SEli Cohen 		return -ENOMEM;
11511a86b377SEli Cohen 
11521a86b377SEli Cohen 	cmd_hdr = MLX5_ADDR_OF(modify_virtio_net_q_in, in, general_obj_in_cmd_hdr);
11531a86b377SEli Cohen 
11541a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_MODIFY_GENERAL_OBJECT);
11551a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_NET_Q);
11561a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->virtq_id);
11571a86b377SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
11581a86b377SEli Cohen 
11591a86b377SEli Cohen 	obj_context = MLX5_ADDR_OF(modify_virtio_net_q_in, in, obj_context);
11601a86b377SEli Cohen 	MLX5_SET64(virtio_net_q_object, obj_context, modify_field_select,
11611a86b377SEli Cohen 		   MLX5_VIRTQ_MODIFY_MASK_STATE);
11621a86b377SEli Cohen 	MLX5_SET(virtio_net_q_object, obj_context, state, state);
11631a86b377SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, inlen, out, sizeof(out));
11641a86b377SEli Cohen 	kfree(in);
11651a86b377SEli Cohen 	if (!err)
11661a86b377SEli Cohen 		mvq->fw_state = state;
11671a86b377SEli Cohen 
11681a86b377SEli Cohen 	return err;
11691a86b377SEli Cohen }
11701a86b377SEli Cohen 
11711892a3d4SEli Cohen static int counter_set_alloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
11721892a3d4SEli Cohen {
11731892a3d4SEli Cohen 	u32 in[MLX5_ST_SZ_DW(create_virtio_q_counters_in)] = {};
11741892a3d4SEli Cohen 	u32 out[MLX5_ST_SZ_DW(create_virtio_q_counters_out)] = {};
11751892a3d4SEli Cohen 	void *cmd_hdr;
11761892a3d4SEli Cohen 	int err;
11771892a3d4SEli Cohen 
11781892a3d4SEli Cohen 	if (!counters_supported(&ndev->mvdev))
11791892a3d4SEli Cohen 		return 0;
11801892a3d4SEli Cohen 
11811892a3d4SEli Cohen 	cmd_hdr = MLX5_ADDR_OF(create_virtio_q_counters_in, in, hdr);
11821892a3d4SEli Cohen 
11831892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
11841892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
11851892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
11861892a3d4SEli Cohen 
11871892a3d4SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out));
11881892a3d4SEli Cohen 	if (err)
11891892a3d4SEli Cohen 		return err;
11901892a3d4SEli Cohen 
11911892a3d4SEli Cohen 	mvq->counter_set_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
11921892a3d4SEli Cohen 
11931892a3d4SEli Cohen 	return 0;
11941892a3d4SEli Cohen }
11951892a3d4SEli Cohen 
11961892a3d4SEli Cohen static void counter_set_dealloc(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
11971892a3d4SEli Cohen {
11981892a3d4SEli Cohen 	u32 in[MLX5_ST_SZ_DW(destroy_virtio_q_counters_in)] = {};
11991892a3d4SEli Cohen 	u32 out[MLX5_ST_SZ_DW(destroy_virtio_q_counters_out)] = {};
12001892a3d4SEli Cohen 
12011892a3d4SEli Cohen 	if (!counters_supported(&ndev->mvdev))
12021892a3d4SEli Cohen 		return;
12031892a3d4SEli Cohen 
12041892a3d4SEli Cohen 	MLX5_SET(destroy_virtio_q_counters_in, in, hdr.opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
12051892a3d4SEli Cohen 	MLX5_SET(destroy_virtio_q_counters_in, in, hdr.obj_id, mvq->counter_set_id);
12061892a3d4SEli Cohen 	MLX5_SET(destroy_virtio_q_counters_in, in, hdr.uid, ndev->mvdev.res.uid);
12071892a3d4SEli Cohen 	MLX5_SET(destroy_virtio_q_counters_in, in, hdr.obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
12081892a3d4SEli Cohen 	if (mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out)))
12091892a3d4SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "dealloc counter set 0x%x\n", mvq->counter_set_id);
12101892a3d4SEli Cohen }
12111892a3d4SEli Cohen 
1212bc9a2b3eSEli Cohen static irqreturn_t mlx5_vdpa_int_handler(int irq, void *priv)
1213bc9a2b3eSEli Cohen {
1214bc9a2b3eSEli Cohen 	struct vdpa_callback *cb = priv;
1215bc9a2b3eSEli Cohen 
1216bc9a2b3eSEli Cohen 	if (cb->callback)
1217bc9a2b3eSEli Cohen 		return cb->callback(cb->private);
1218bc9a2b3eSEli Cohen 
1219bc9a2b3eSEli Cohen 	return IRQ_HANDLED;
1220bc9a2b3eSEli Cohen }
1221bc9a2b3eSEli Cohen 
1222bc9a2b3eSEli Cohen static void alloc_vector(struct mlx5_vdpa_net *ndev,
1223bc9a2b3eSEli Cohen 			 struct mlx5_vdpa_virtqueue *mvq)
1224bc9a2b3eSEli Cohen {
1225bc9a2b3eSEli Cohen 	struct mlx5_vdpa_irq_pool *irqp = &ndev->irqp;
1226bc9a2b3eSEli Cohen 	struct mlx5_vdpa_irq_pool_entry *ent;
1227bc9a2b3eSEli Cohen 	int err;
1228bc9a2b3eSEli Cohen 	int i;
1229bc9a2b3eSEli Cohen 
1230bc9a2b3eSEli Cohen 	for (i = 0; i < irqp->num_ent; i++) {
1231bc9a2b3eSEli Cohen 		ent = &irqp->entries[i];
1232bc9a2b3eSEli Cohen 		if (!ent->used) {
1233bc9a2b3eSEli Cohen 			snprintf(ent->name, MLX5_VDPA_IRQ_NAME_LEN, "%s-vq-%d",
1234bc9a2b3eSEli Cohen 				 dev_name(&ndev->mvdev.vdev.dev), mvq->index);
1235bc9a2b3eSEli Cohen 			ent->dev_id = &ndev->event_cbs[mvq->index];
1236bc9a2b3eSEli Cohen 			err = request_irq(ent->map.virq, mlx5_vdpa_int_handler, 0,
1237bc9a2b3eSEli Cohen 					  ent->name, ent->dev_id);
1238bc9a2b3eSEli Cohen 			if (err)
1239bc9a2b3eSEli Cohen 				return;
1240bc9a2b3eSEli Cohen 
1241bc9a2b3eSEli Cohen 			ent->used = true;
1242bc9a2b3eSEli Cohen 			mvq->map = ent->map;
1243bc9a2b3eSEli Cohen 			return;
1244bc9a2b3eSEli Cohen 		}
1245bc9a2b3eSEli Cohen 	}
1246bc9a2b3eSEli Cohen }
1247bc9a2b3eSEli Cohen 
1248bc9a2b3eSEli Cohen static void dealloc_vector(struct mlx5_vdpa_net *ndev,
1249bc9a2b3eSEli Cohen 			   struct mlx5_vdpa_virtqueue *mvq)
1250bc9a2b3eSEli Cohen {
1251bc9a2b3eSEli Cohen 	struct mlx5_vdpa_irq_pool *irqp = &ndev->irqp;
1252bc9a2b3eSEli Cohen 	int i;
1253bc9a2b3eSEli Cohen 
1254bc9a2b3eSEli Cohen 	for (i = 0; i < irqp->num_ent; i++)
1255bc9a2b3eSEli Cohen 		if (mvq->map.virq == irqp->entries[i].map.virq) {
1256bc9a2b3eSEli Cohen 			free_irq(mvq->map.virq, irqp->entries[i].dev_id);
1257bc9a2b3eSEli Cohen 			irqp->entries[i].used = false;
1258bc9a2b3eSEli Cohen 			return;
1259bc9a2b3eSEli Cohen 		}
1260bc9a2b3eSEli Cohen }
1261bc9a2b3eSEli Cohen 
12621a86b377SEli Cohen static int setup_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
12631a86b377SEli Cohen {
12641a86b377SEli Cohen 	u16 idx = mvq->index;
12651a86b377SEli Cohen 	int err;
12661a86b377SEli Cohen 
12671a86b377SEli Cohen 	if (!mvq->num_ent)
12681a86b377SEli Cohen 		return 0;
12691a86b377SEli Cohen 
127052893733SEli Cohen 	if (mvq->initialized)
127152893733SEli Cohen 		return 0;
12721a86b377SEli Cohen 
12731a86b377SEli Cohen 	err = cq_create(ndev, idx, mvq->num_ent);
12741a86b377SEli Cohen 	if (err)
12751a86b377SEli Cohen 		return err;
12761a86b377SEli Cohen 
12771a86b377SEli Cohen 	err = qp_create(ndev, mvq, &mvq->fwqp);
12781a86b377SEli Cohen 	if (err)
12791a86b377SEli Cohen 		goto err_fwqp;
12801a86b377SEli Cohen 
12811a86b377SEli Cohen 	err = qp_create(ndev, mvq, &mvq->vqqp);
12821a86b377SEli Cohen 	if (err)
12831a86b377SEli Cohen 		goto err_vqqp;
12841a86b377SEli Cohen 
12851a86b377SEli Cohen 	err = connect_qps(ndev, mvq);
12861a86b377SEli Cohen 	if (err)
12871a86b377SEli Cohen 		goto err_connect;
12881a86b377SEli Cohen 
12891892a3d4SEli Cohen 	err = counter_set_alloc(ndev, mvq);
12901892a3d4SEli Cohen 	if (err)
1291bc9a2b3eSEli Cohen 		goto err_connect;
12921892a3d4SEli Cohen 
1293bc9a2b3eSEli Cohen 	alloc_vector(ndev, mvq);
12941a86b377SEli Cohen 	err = create_virtqueue(ndev, mvq);
12951a86b377SEli Cohen 	if (err)
1296bc9a2b3eSEli Cohen 		goto err_vq;
12971a86b377SEli Cohen 
12981a86b377SEli Cohen 	if (mvq->ready) {
12991a86b377SEli Cohen 		err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY);
13001a86b377SEli Cohen 		if (err) {
13011a86b377SEli Cohen 			mlx5_vdpa_warn(&ndev->mvdev, "failed to modify to ready vq idx %d(%d)\n",
13021a86b377SEli Cohen 				       idx, err);
1303bc9a2b3eSEli Cohen 			goto err_modify;
13041a86b377SEli Cohen 		}
13051a86b377SEli Cohen 	}
13061a86b377SEli Cohen 
13071a86b377SEli Cohen 	mvq->initialized = true;
13081a86b377SEli Cohen 	return 0;
13091a86b377SEli Cohen 
1310bc9a2b3eSEli Cohen err_modify:
1311bc9a2b3eSEli Cohen 	destroy_virtqueue(ndev, mvq);
1312bc9a2b3eSEli Cohen err_vq:
1313bc9a2b3eSEli Cohen 	dealloc_vector(ndev, mvq);
13141892a3d4SEli Cohen 	counter_set_dealloc(ndev, mvq);
1315bc9a2b3eSEli Cohen err_connect:
13161a86b377SEli Cohen 	qp_destroy(ndev, &mvq->vqqp);
13171a86b377SEli Cohen err_vqqp:
13181a86b377SEli Cohen 	qp_destroy(ndev, &mvq->fwqp);
13191a86b377SEli Cohen err_fwqp:
13201a86b377SEli Cohen 	cq_destroy(ndev, idx);
13211a86b377SEli Cohen 	return err;
13221a86b377SEli Cohen }
13231a86b377SEli Cohen 
13241a86b377SEli Cohen static void suspend_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
13251a86b377SEli Cohen {
13261a86b377SEli Cohen 	struct mlx5_virtq_attr attr;
13271a86b377SEli Cohen 
13281a86b377SEli Cohen 	if (!mvq->initialized)
13291a86b377SEli Cohen 		return;
13301a86b377SEli Cohen 
13311a86b377SEli Cohen 	if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY)
13321a86b377SEli Cohen 		return;
13331a86b377SEli Cohen 
13341a86b377SEli Cohen 	if (modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND))
13351a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "modify to suspend failed\n");
13363176e974SSi-Wei Liu 
13373176e974SSi-Wei Liu 	if (query_virtqueue(ndev, mvq, &attr)) {
13383176e974SSi-Wei Liu 		mlx5_vdpa_warn(&ndev->mvdev, "failed to query virtqueue\n");
13393176e974SSi-Wei Liu 		return;
13403176e974SSi-Wei Liu 	}
13413176e974SSi-Wei Liu 	mvq->avail_idx = attr.available_index;
1342bc04d93eSEli Cohen 	mvq->used_idx = attr.used_index;
13431a86b377SEli Cohen }
13441a86b377SEli Cohen 
13451a86b377SEli Cohen static void suspend_vqs(struct mlx5_vdpa_net *ndev)
13461a86b377SEli Cohen {
13471a86b377SEli Cohen 	int i;
13481a86b377SEli Cohen 
134975560522SEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++)
13501a86b377SEli Cohen 		suspend_vq(ndev, &ndev->vqs[i]);
13511a86b377SEli Cohen }
13521a86b377SEli Cohen 
13531a86b377SEli Cohen static void teardown_vq(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
13541a86b377SEli Cohen {
13551a86b377SEli Cohen 	if (!mvq->initialized)
13561a86b377SEli Cohen 		return;
13571a86b377SEli Cohen 
13581a86b377SEli Cohen 	suspend_vq(ndev, mvq);
13591a86b377SEli Cohen 	destroy_virtqueue(ndev, mvq);
1360bc9a2b3eSEli Cohen 	dealloc_vector(ndev, mvq);
13611892a3d4SEli Cohen 	counter_set_dealloc(ndev, mvq);
13621a86b377SEli Cohen 	qp_destroy(ndev, &mvq->vqqp);
13631a86b377SEli Cohen 	qp_destroy(ndev, &mvq->fwqp);
13641a86b377SEli Cohen 	cq_destroy(ndev, mvq->index);
13651a86b377SEli Cohen 	mvq->initialized = false;
13661a86b377SEli Cohen }
13671a86b377SEli Cohen 
13681a86b377SEli Cohen static int create_rqt(struct mlx5_vdpa_net *ndev)
13691a86b377SEli Cohen {
1370a43ae805SEli Cohen 	int rqt_table_size = roundup_pow_of_two(ndev->rqt_size);
1371a43ae805SEli Cohen 	int act_sz = roundup_pow_of_two(ndev->cur_num_vqs / 2);
13721a86b377SEli Cohen 	__be32 *list;
13731a86b377SEli Cohen 	void *rqtc;
13741a86b377SEli Cohen 	int inlen;
13751a86b377SEli Cohen 	void *in;
13761a86b377SEli Cohen 	int i, j;
13771a86b377SEli Cohen 	int err;
13781a86b377SEli Cohen 
1379a43ae805SEli Cohen 	inlen = MLX5_ST_SZ_BYTES(create_rqt_in) + rqt_table_size * MLX5_ST_SZ_BYTES(rq_num);
13801a86b377SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
13811a86b377SEli Cohen 	if (!in)
13821a86b377SEli Cohen 		return -ENOMEM;
13831a86b377SEli Cohen 
13841a86b377SEli Cohen 	MLX5_SET(create_rqt_in, in, uid, ndev->mvdev.res.uid);
13851a86b377SEli Cohen 	rqtc = MLX5_ADDR_OF(create_rqt_in, in, rqt_context);
13861a86b377SEli Cohen 
13871a86b377SEli Cohen 	MLX5_SET(rqtc, rqtc, list_q_type, MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q);
1388a43ae805SEli Cohen 	MLX5_SET(rqtc, rqtc, rqt_max_size, rqt_table_size);
13891a86b377SEli Cohen 	list = MLX5_ADDR_OF(rqtc, rqtc, rq_num[0]);
1390a43ae805SEli Cohen 	for (i = 0, j = 0; i < act_sz; i++, j += 2)
1391acde3929SEli Cohen 		list[i] = cpu_to_be32(ndev->vqs[j % ndev->cur_num_vqs].virtq_id);
13921a86b377SEli Cohen 
1393a43ae805SEli Cohen 	MLX5_SET(rqtc, rqtc, rqt_actual_size, act_sz);
13941a86b377SEli Cohen 	err = mlx5_vdpa_create_rqt(&ndev->mvdev, in, inlen, &ndev->res.rqtn);
13951a86b377SEli Cohen 	kfree(in);
13961a86b377SEli Cohen 	if (err)
13971a86b377SEli Cohen 		return err;
13981a86b377SEli Cohen 
13991a86b377SEli Cohen 	return 0;
14001a86b377SEli Cohen }
14011a86b377SEli Cohen 
140252893733SEli Cohen #define MLX5_MODIFY_RQT_NUM_RQS ((u64)1)
140352893733SEli Cohen 
140452893733SEli Cohen static int modify_rqt(struct mlx5_vdpa_net *ndev, int num)
140552893733SEli Cohen {
1406a43ae805SEli Cohen 	int act_sz = roundup_pow_of_two(num / 2);
140752893733SEli Cohen 	__be32 *list;
140852893733SEli Cohen 	void *rqtc;
140952893733SEli Cohen 	int inlen;
141052893733SEli Cohen 	void *in;
141152893733SEli Cohen 	int i, j;
141252893733SEli Cohen 	int err;
141352893733SEli Cohen 
1414a43ae805SEli Cohen 	inlen = MLX5_ST_SZ_BYTES(modify_rqt_in) + act_sz * MLX5_ST_SZ_BYTES(rq_num);
141552893733SEli Cohen 	in = kzalloc(inlen, GFP_KERNEL);
141652893733SEli Cohen 	if (!in)
141752893733SEli Cohen 		return -ENOMEM;
141852893733SEli Cohen 
141952893733SEli Cohen 	MLX5_SET(modify_rqt_in, in, uid, ndev->mvdev.res.uid);
142052893733SEli Cohen 	MLX5_SET64(modify_rqt_in, in, bitmask, MLX5_MODIFY_RQT_NUM_RQS);
142152893733SEli Cohen 	rqtc = MLX5_ADDR_OF(modify_rqt_in, in, ctx);
142252893733SEli Cohen 	MLX5_SET(rqtc, rqtc, list_q_type, MLX5_RQTC_LIST_Q_TYPE_VIRTIO_NET_Q);
142352893733SEli Cohen 
142452893733SEli Cohen 	list = MLX5_ADDR_OF(rqtc, rqtc, rq_num[0]);
1425a43ae805SEli Cohen 	for (i = 0, j = 0; i < act_sz; i++, j = j + 2)
1426a7f46ba4SEli Cohen 		list[i] = cpu_to_be32(ndev->vqs[j % num].virtq_id);
142752893733SEli Cohen 
1428a43ae805SEli Cohen 	MLX5_SET(rqtc, rqtc, rqt_actual_size, act_sz);
142952893733SEli Cohen 	err = mlx5_vdpa_modify_rqt(&ndev->mvdev, in, inlen, ndev->res.rqtn);
143052893733SEli Cohen 	kfree(in);
143152893733SEli Cohen 	if (err)
143252893733SEli Cohen 		return err;
143352893733SEli Cohen 
143452893733SEli Cohen 	return 0;
143552893733SEli Cohen }
143652893733SEli Cohen 
14371a86b377SEli Cohen static void destroy_rqt(struct mlx5_vdpa_net *ndev)
14381a86b377SEli Cohen {
14391a86b377SEli Cohen 	mlx5_vdpa_destroy_rqt(&ndev->mvdev, ndev->res.rqtn);
14401a86b377SEli Cohen }
14411a86b377SEli Cohen 
14421a86b377SEli Cohen static int create_tir(struct mlx5_vdpa_net *ndev)
14431a86b377SEli Cohen {
14441a86b377SEli Cohen #define HASH_IP_L4PORTS                                                                            \
14451a86b377SEli Cohen 	(MLX5_HASH_FIELD_SEL_SRC_IP | MLX5_HASH_FIELD_SEL_DST_IP | MLX5_HASH_FIELD_SEL_L4_SPORT |  \
14461a86b377SEli Cohen 	 MLX5_HASH_FIELD_SEL_L4_DPORT)
14471a86b377SEli Cohen 	static const u8 rx_hash_toeplitz_key[] = { 0x2c, 0xc6, 0x81, 0xd1, 0x5b, 0xdb, 0xf4, 0xf7,
14481a86b377SEli Cohen 						   0xfc, 0xa2, 0x83, 0x19, 0xdb, 0x1a, 0x3e, 0x94,
14491a86b377SEli Cohen 						   0x6b, 0x9e, 0x38, 0xd9, 0x2c, 0x9c, 0x03, 0xd1,
14501a86b377SEli Cohen 						   0xad, 0x99, 0x44, 0xa7, 0xd9, 0x56, 0x3d, 0x59,
14511a86b377SEli Cohen 						   0x06, 0x3c, 0x25, 0xf3, 0xfc, 0x1f, 0xdc, 0x2a };
14521a86b377SEli Cohen 	void *rss_key;
14531a86b377SEli Cohen 	void *outer;
14541a86b377SEli Cohen 	void *tirc;
14551a86b377SEli Cohen 	void *in;
14561a86b377SEli Cohen 	int err;
14571a86b377SEli Cohen 
14581a86b377SEli Cohen 	in = kzalloc(MLX5_ST_SZ_BYTES(create_tir_in), GFP_KERNEL);
14591a86b377SEli Cohen 	if (!in)
14601a86b377SEli Cohen 		return -ENOMEM;
14611a86b377SEli Cohen 
14621a86b377SEli Cohen 	MLX5_SET(create_tir_in, in, uid, ndev->mvdev.res.uid);
14631a86b377SEli Cohen 	tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
14641a86b377SEli Cohen 	MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
14651a86b377SEli Cohen 
14661a86b377SEli Cohen 	MLX5_SET(tirc, tirc, rx_hash_symmetric, 1);
14671a86b377SEli Cohen 	MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_TOEPLITZ);
14681a86b377SEli Cohen 	rss_key = MLX5_ADDR_OF(tirc, tirc, rx_hash_toeplitz_key);
14691a86b377SEli Cohen 	memcpy(rss_key, rx_hash_toeplitz_key, sizeof(rx_hash_toeplitz_key));
14701a86b377SEli Cohen 
14711a86b377SEli Cohen 	outer = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer);
14721a86b377SEli Cohen 	MLX5_SET(rx_hash_field_select, outer, l3_prot_type, MLX5_L3_PROT_TYPE_IPV4);
14731a86b377SEli Cohen 	MLX5_SET(rx_hash_field_select, outer, l4_prot_type, MLX5_L4_PROT_TYPE_TCP);
14741a86b377SEli Cohen 	MLX5_SET(rx_hash_field_select, outer, selected_fields, HASH_IP_L4PORTS);
14751a86b377SEli Cohen 
14761a86b377SEli Cohen 	MLX5_SET(tirc, tirc, indirect_table, ndev->res.rqtn);
14771a86b377SEli Cohen 	MLX5_SET(tirc, tirc, transport_domain, ndev->res.tdn);
14781a86b377SEli Cohen 
14791a86b377SEli Cohen 	err = mlx5_vdpa_create_tir(&ndev->mvdev, in, &ndev->res.tirn);
14801a86b377SEli Cohen 	kfree(in);
148129422100SEli Cohen 	if (err)
148229422100SEli Cohen 		return err;
148329422100SEli Cohen 
148429422100SEli Cohen 	mlx5_vdpa_add_tirn(ndev);
14851a86b377SEli Cohen 	return err;
14861a86b377SEli Cohen }
14871a86b377SEli Cohen 
14881a86b377SEli Cohen static void destroy_tir(struct mlx5_vdpa_net *ndev)
14891a86b377SEli Cohen {
149029422100SEli Cohen 	mlx5_vdpa_remove_tirn(ndev);
14911a86b377SEli Cohen 	mlx5_vdpa_destroy_tir(&ndev->mvdev, ndev->res.tirn);
14921a86b377SEli Cohen }
14931a86b377SEli Cohen 
1494baf2ad3fSEli Cohen #define MAX_STEERING_ENT 0x8000
1495baf2ad3fSEli Cohen #define MAX_STEERING_GROUPS 2
1496baf2ad3fSEli Cohen 
14970a599750SEli Cohen #if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
14980a599750SEli Cohen        #define NUM_DESTS 2
14990a599750SEli Cohen #else
15000a599750SEli Cohen        #define NUM_DESTS 1
15010a599750SEli Cohen #endif
15020a599750SEli Cohen 
15030a599750SEli Cohen static int add_steering_counters(struct mlx5_vdpa_net *ndev,
15040a599750SEli Cohen 				 struct macvlan_node *node,
15050a599750SEli Cohen 				 struct mlx5_flow_act *flow_act,
15060a599750SEli Cohen 				 struct mlx5_flow_destination *dests)
15071a86b377SEli Cohen {
15080a599750SEli Cohen #if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
15090a599750SEli Cohen 	int err;
15100a599750SEli Cohen 
15110a599750SEli Cohen 	node->ucast_counter.counter = mlx5_fc_create(ndev->mvdev.mdev, false);
15120a599750SEli Cohen 	if (IS_ERR(node->ucast_counter.counter))
15130a599750SEli Cohen 		return PTR_ERR(node->ucast_counter.counter);
15140a599750SEli Cohen 
15150a599750SEli Cohen 	node->mcast_counter.counter = mlx5_fc_create(ndev->mvdev.mdev, false);
15160a599750SEli Cohen 	if (IS_ERR(node->mcast_counter.counter)) {
15170a599750SEli Cohen 		err = PTR_ERR(node->mcast_counter.counter);
15180a599750SEli Cohen 		goto err_mcast_counter;
15190a599750SEli Cohen 	}
15200a599750SEli Cohen 
15210a599750SEli Cohen 	dests[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
15220a599750SEli Cohen 	flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
15230a599750SEli Cohen 	return 0;
15240a599750SEli Cohen 
15250a599750SEli Cohen err_mcast_counter:
15260a599750SEli Cohen 	mlx5_fc_destroy(ndev->mvdev.mdev, node->ucast_counter.counter);
15270a599750SEli Cohen 	return err;
15280a599750SEli Cohen #else
15290a599750SEli Cohen 	return 0;
15300a599750SEli Cohen #endif
15310a599750SEli Cohen }
15320a599750SEli Cohen 
15330a599750SEli Cohen static void remove_steering_counters(struct mlx5_vdpa_net *ndev,
15340a599750SEli Cohen 				     struct macvlan_node *node)
15350a599750SEli Cohen {
15360a599750SEli Cohen #if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
15370a599750SEli Cohen 	mlx5_fc_destroy(ndev->mvdev.mdev, node->mcast_counter.counter);
15380a599750SEli Cohen 	mlx5_fc_destroy(ndev->mvdev.mdev, node->ucast_counter.counter);
15390a599750SEli Cohen #endif
15400a599750SEli Cohen }
15410a599750SEli Cohen 
15420a599750SEli Cohen static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
15430a599750SEli Cohen 					struct macvlan_node *node)
15440a599750SEli Cohen {
15450a599750SEli Cohen 	struct mlx5_flow_destination dests[NUM_DESTS] = {};
15461a86b377SEli Cohen 	struct mlx5_flow_act flow_act = {};
1547540061acSEli Cohen 	struct mlx5_flow_spec *spec;
1548540061acSEli Cohen 	void *headers_c;
1549540061acSEli Cohen 	void *headers_v;
1550540061acSEli Cohen 	u8 *dmac_c;
1551540061acSEli Cohen 	u8 *dmac_v;
15521a86b377SEli Cohen 	int err;
15530a599750SEli Cohen 	u16 vid;
15541a86b377SEli Cohen 
1555540061acSEli Cohen 	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
1556540061acSEli Cohen 	if (!spec)
1557540061acSEli Cohen 		return -ENOMEM;
1558540061acSEli Cohen 
15590a599750SEli Cohen 	vid = key2vid(node->macvlan);
1560540061acSEli Cohen 	spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
1561540061acSEli Cohen 	headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers);
1562540061acSEli Cohen 	headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
1563baf2ad3fSEli Cohen 	dmac_c = MLX5_ADDR_OF(fte_match_param, headers_c, outer_headers.dmac_47_16);
1564540061acSEli Cohen 	dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16);
156571aa95a6SXu Qiang 	eth_broadcast_addr(dmac_c);
1566baf2ad3fSEli Cohen 	ether_addr_copy(dmac_v, mac);
1567a6ce72c0SEli Cohen 	if (ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)) {
1568baf2ad3fSEli Cohen 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
1569a6ce72c0SEli Cohen 		MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, first_vid);
1570a6ce72c0SEli Cohen 	}
15710a599750SEli Cohen 	if (node->tagged) {
1572baf2ad3fSEli Cohen 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, cvlan_tag, 1);
1573a6ce72c0SEli Cohen 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, vid);
1574baf2ad3fSEli Cohen 	}
15757becdd13SEli Cohen 	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
15760a599750SEli Cohen 	dests[0].type = MLX5_FLOW_DESTINATION_TYPE_TIR;
15770a599750SEli Cohen 	dests[0].tir_num = ndev->res.tirn;
15780a599750SEli Cohen 	err = add_steering_counters(ndev, node, &flow_act, dests);
15790a599750SEli Cohen 	if (err)
15800a599750SEli Cohen 		goto out_free;
1581540061acSEli Cohen 
15820a599750SEli Cohen #if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
15830a599750SEli Cohen 	dests[1].counter_id = mlx5_fc_id(node->ucast_counter.counter);
15840a599750SEli Cohen #endif
15850a599750SEli Cohen 	node->ucast_rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, dests, NUM_DESTS);
15860a599750SEli Cohen 	if (IS_ERR(node->ucast_rule)) {
15870a599750SEli Cohen 		err = PTR_ERR(node->ucast_rule);
15880a599750SEli Cohen 		goto err_ucast;
15890a599750SEli Cohen 	}
15900a599750SEli Cohen 
15910a599750SEli Cohen #if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
15920a599750SEli Cohen 	dests[1].counter_id = mlx5_fc_id(node->mcast_counter.counter);
15930a599750SEli Cohen #endif
15941a86b377SEli Cohen 
1595540061acSEli Cohen 	memset(dmac_c, 0, ETH_ALEN);
1596540061acSEli Cohen 	memset(dmac_v, 0, ETH_ALEN);
1597540061acSEli Cohen 	dmac_c[0] = 1;
1598540061acSEli Cohen 	dmac_v[0] = 1;
15990a599750SEli Cohen 	node->mcast_rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, dests, NUM_DESTS);
16000a599750SEli Cohen 	if (IS_ERR(node->mcast_rule)) {
16010a599750SEli Cohen 		err = PTR_ERR(node->mcast_rule);
1602baf2ad3fSEli Cohen 		goto err_mcast;
1603540061acSEli Cohen 	}
16040a599750SEli Cohen 	kvfree(spec);
16050a599750SEli Cohen 	mlx5_vdpa_add_rx_counters(ndev, node);
16061a86b377SEli Cohen 	return 0;
16071a86b377SEli Cohen 
1608baf2ad3fSEli Cohen err_mcast:
16090a599750SEli Cohen 	mlx5_del_flow_rules(node->ucast_rule);
16100a599750SEli Cohen err_ucast:
16110a599750SEli Cohen 	remove_steering_counters(ndev, node);
16120a599750SEli Cohen out_free:
16130a599750SEli Cohen 	kvfree(spec);
16141a86b377SEli Cohen 	return err;
16151a86b377SEli Cohen }
16161a86b377SEli Cohen 
1617baf2ad3fSEli Cohen static void mlx5_vdpa_del_mac_vlan_rules(struct mlx5_vdpa_net *ndev,
16180a599750SEli Cohen 					 struct macvlan_node *node)
16191a86b377SEli Cohen {
16200a599750SEli Cohen 	mlx5_vdpa_remove_rx_counters(ndev, node);
16210a599750SEli Cohen 	mlx5_del_flow_rules(node->ucast_rule);
16220a599750SEli Cohen 	mlx5_del_flow_rules(node->mcast_rule);
1623baf2ad3fSEli Cohen }
1624baf2ad3fSEli Cohen 
1625baf2ad3fSEli Cohen static u64 search_val(u8 *mac, u16 vlan, bool tagged)
1626baf2ad3fSEli Cohen {
1627baf2ad3fSEli Cohen 	u64 val;
1628baf2ad3fSEli Cohen 
1629baf2ad3fSEli Cohen 	if (!tagged)
1630baf2ad3fSEli Cohen 		vlan = MLX5V_UNTAGGED;
1631baf2ad3fSEli Cohen 
1632baf2ad3fSEli Cohen 	val = (u64)vlan << 48 |
1633baf2ad3fSEli Cohen 	      (u64)mac[0] << 40 |
1634baf2ad3fSEli Cohen 	      (u64)mac[1] << 32 |
1635baf2ad3fSEli Cohen 	      (u64)mac[2] << 24 |
1636baf2ad3fSEli Cohen 	      (u64)mac[3] << 16 |
1637baf2ad3fSEli Cohen 	      (u64)mac[4] << 8 |
1638baf2ad3fSEli Cohen 	      (u64)mac[5];
1639baf2ad3fSEli Cohen 
1640baf2ad3fSEli Cohen 	return val;
1641baf2ad3fSEli Cohen }
1642baf2ad3fSEli Cohen 
1643baf2ad3fSEli Cohen static struct macvlan_node *mac_vlan_lookup(struct mlx5_vdpa_net *ndev, u64 value)
1644baf2ad3fSEli Cohen {
1645baf2ad3fSEli Cohen 	struct macvlan_node *pos;
1646baf2ad3fSEli Cohen 	u32 idx;
1647baf2ad3fSEli Cohen 
1648baf2ad3fSEli Cohen 	idx = hash_64(value, 8); // tbd 8
1649baf2ad3fSEli Cohen 	hlist_for_each_entry(pos, &ndev->macvlan_hash[idx], hlist) {
1650baf2ad3fSEli Cohen 		if (pos->macvlan == value)
1651baf2ad3fSEli Cohen 			return pos;
1652baf2ad3fSEli Cohen 	}
1653baf2ad3fSEli Cohen 	return NULL;
1654baf2ad3fSEli Cohen }
1655baf2ad3fSEli Cohen 
16560a599750SEli Cohen static int mac_vlan_add(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vid, bool tagged)
1657baf2ad3fSEli Cohen {
1658baf2ad3fSEli Cohen 	struct macvlan_node *ptr;
1659baf2ad3fSEli Cohen 	u64 val;
1660baf2ad3fSEli Cohen 	u32 idx;
1661baf2ad3fSEli Cohen 	int err;
1662baf2ad3fSEli Cohen 
16630a599750SEli Cohen 	val = search_val(mac, vid, tagged);
1664baf2ad3fSEli Cohen 	if (mac_vlan_lookup(ndev, val))
1665baf2ad3fSEli Cohen 		return -EEXIST;
1666baf2ad3fSEli Cohen 
1667baf2ad3fSEli Cohen 	ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
1668baf2ad3fSEli Cohen 	if (!ptr)
1669baf2ad3fSEli Cohen 		return -ENOMEM;
1670baf2ad3fSEli Cohen 
16710a599750SEli Cohen 	ptr->tagged = tagged;
16720a599750SEli Cohen 	ptr->macvlan = val;
16730a599750SEli Cohen 	ptr->ndev = ndev;
16740a599750SEli Cohen 	err = mlx5_vdpa_add_mac_vlan_rules(ndev, ndev->config.mac, ptr);
1675baf2ad3fSEli Cohen 	if (err)
1676baf2ad3fSEli Cohen 		goto err_add;
1677baf2ad3fSEli Cohen 
1678baf2ad3fSEli Cohen 	idx = hash_64(val, 8);
1679baf2ad3fSEli Cohen 	hlist_add_head(&ptr->hlist, &ndev->macvlan_hash[idx]);
1680baf2ad3fSEli Cohen 	return 0;
1681baf2ad3fSEli Cohen 
1682baf2ad3fSEli Cohen err_add:
1683baf2ad3fSEli Cohen 	kfree(ptr);
1684baf2ad3fSEli Cohen 	return err;
1685baf2ad3fSEli Cohen }
1686baf2ad3fSEli Cohen 
1687baf2ad3fSEli Cohen static void mac_vlan_del(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tagged)
1688baf2ad3fSEli Cohen {
1689baf2ad3fSEli Cohen 	struct macvlan_node *ptr;
1690baf2ad3fSEli Cohen 
1691baf2ad3fSEli Cohen 	ptr = mac_vlan_lookup(ndev, search_val(mac, vlan, tagged));
1692baf2ad3fSEli Cohen 	if (!ptr)
16931a86b377SEli Cohen 		return;
16941a86b377SEli Cohen 
1695baf2ad3fSEli Cohen 	hlist_del(&ptr->hlist);
16960a599750SEli Cohen 	mlx5_vdpa_del_mac_vlan_rules(ndev, ptr);
16970a599750SEli Cohen 	remove_steering_counters(ndev, ptr);
1698baf2ad3fSEli Cohen 	kfree(ptr);
1699baf2ad3fSEli Cohen }
1700baf2ad3fSEli Cohen 
1701baf2ad3fSEli Cohen static void clear_mac_vlan_table(struct mlx5_vdpa_net *ndev)
1702baf2ad3fSEli Cohen {
1703baf2ad3fSEli Cohen 	struct macvlan_node *pos;
1704baf2ad3fSEli Cohen 	struct hlist_node *n;
1705baf2ad3fSEli Cohen 	int i;
1706baf2ad3fSEli Cohen 
1707baf2ad3fSEli Cohen 	for (i = 0; i < MLX5V_MACVLAN_SIZE; i++) {
1708baf2ad3fSEli Cohen 		hlist_for_each_entry_safe(pos, n, &ndev->macvlan_hash[i], hlist) {
1709baf2ad3fSEli Cohen 			hlist_del(&pos->hlist);
17100a599750SEli Cohen 			mlx5_vdpa_del_mac_vlan_rules(ndev, pos);
17110a599750SEli Cohen 			remove_steering_counters(ndev, pos);
1712baf2ad3fSEli Cohen 			kfree(pos);
1713baf2ad3fSEli Cohen 		}
1714baf2ad3fSEli Cohen 	}
1715baf2ad3fSEli Cohen }
1716baf2ad3fSEli Cohen 
1717baf2ad3fSEli Cohen static int setup_steering(struct mlx5_vdpa_net *ndev)
1718baf2ad3fSEli Cohen {
1719baf2ad3fSEli Cohen 	struct mlx5_flow_table_attr ft_attr = {};
1720baf2ad3fSEli Cohen 	struct mlx5_flow_namespace *ns;
1721baf2ad3fSEli Cohen 	int err;
1722baf2ad3fSEli Cohen 
1723baf2ad3fSEli Cohen 	ft_attr.max_fte = MAX_STEERING_ENT;
1724baf2ad3fSEli Cohen 	ft_attr.autogroup.max_num_groups = MAX_STEERING_GROUPS;
1725baf2ad3fSEli Cohen 
1726baf2ad3fSEli Cohen 	ns = mlx5_get_flow_namespace(ndev->mvdev.mdev, MLX5_FLOW_NAMESPACE_BYPASS);
1727baf2ad3fSEli Cohen 	if (!ns) {
1728baf2ad3fSEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "failed to get flow namespace\n");
1729baf2ad3fSEli Cohen 		return -EOPNOTSUPP;
1730baf2ad3fSEli Cohen 	}
1731baf2ad3fSEli Cohen 
1732baf2ad3fSEli Cohen 	ndev->rxft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
1733baf2ad3fSEli Cohen 	if (IS_ERR(ndev->rxft)) {
1734baf2ad3fSEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "failed to create flow table\n");
1735baf2ad3fSEli Cohen 		return PTR_ERR(ndev->rxft);
1736baf2ad3fSEli Cohen 	}
173729422100SEli Cohen 	mlx5_vdpa_add_rx_flow_table(ndev);
1738baf2ad3fSEli Cohen 
1739baf2ad3fSEli Cohen 	err = mac_vlan_add(ndev, ndev->config.mac, 0, false);
1740baf2ad3fSEli Cohen 	if (err)
1741baf2ad3fSEli Cohen 		goto err_add;
1742baf2ad3fSEli Cohen 
1743baf2ad3fSEli Cohen 	return 0;
1744baf2ad3fSEli Cohen 
1745baf2ad3fSEli Cohen err_add:
174629422100SEli Cohen 	mlx5_vdpa_remove_rx_flow_table(ndev);
1747baf2ad3fSEli Cohen 	mlx5_destroy_flow_table(ndev->rxft);
1748baf2ad3fSEli Cohen 	return err;
1749baf2ad3fSEli Cohen }
1750baf2ad3fSEli Cohen 
1751baf2ad3fSEli Cohen static void teardown_steering(struct mlx5_vdpa_net *ndev)
1752baf2ad3fSEli Cohen {
1753baf2ad3fSEli Cohen 	clear_mac_vlan_table(ndev);
175429422100SEli Cohen 	mlx5_vdpa_remove_rx_flow_table(ndev);
17551a86b377SEli Cohen 	mlx5_destroy_flow_table(ndev->rxft);
17561a86b377SEli Cohen }
17571a86b377SEli Cohen 
17585262912eSEli Cohen static virtio_net_ctrl_ack handle_ctrl_mac(struct mlx5_vdpa_dev *mvdev, u8 cmd)
17595262912eSEli Cohen {
17605262912eSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
17615262912eSEli Cohen 	struct mlx5_control_vq *cvq = &mvdev->cvq;
17625262912eSEli Cohen 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
17635262912eSEli Cohen 	struct mlx5_core_dev *pfmdev;
17645262912eSEli Cohen 	size_t read;
1765f1781bedSMichael Qiu 	u8 mac[ETH_ALEN], mac_back[ETH_ALEN];
17665262912eSEli Cohen 
17675262912eSEli Cohen 	pfmdev = pci_get_drvdata(pci_physfn(mvdev->mdev->pdev));
17685262912eSEli Cohen 	switch (cmd) {
17695262912eSEli Cohen 	case VIRTIO_NET_CTRL_MAC_ADDR_SET:
17705262912eSEli Cohen 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, (void *)mac, ETH_ALEN);
17715262912eSEli Cohen 		if (read != ETH_ALEN)
17725262912eSEli Cohen 			break;
17735262912eSEli Cohen 
17745262912eSEli Cohen 		if (!memcmp(ndev->config.mac, mac, 6)) {
17755262912eSEli Cohen 			status = VIRTIO_NET_OK;
17765262912eSEli Cohen 			break;
17775262912eSEli Cohen 		}
17785262912eSEli Cohen 
1779f1781bedSMichael Qiu 		if (is_zero_ether_addr(mac))
1780f1781bedSMichael Qiu 			break;
1781f1781bedSMichael Qiu 
17825262912eSEli Cohen 		if (!is_zero_ether_addr(ndev->config.mac)) {
17835262912eSEli Cohen 			if (mlx5_mpfs_del_mac(pfmdev, ndev->config.mac)) {
17845262912eSEli Cohen 				mlx5_vdpa_warn(mvdev, "failed to delete old MAC %pM from MPFS table\n",
17855262912eSEli Cohen 					       ndev->config.mac);
17865262912eSEli Cohen 				break;
17875262912eSEli Cohen 			}
17885262912eSEli Cohen 		}
17895262912eSEli Cohen 
17905262912eSEli Cohen 		if (mlx5_mpfs_add_mac(pfmdev, mac)) {
17915262912eSEli Cohen 			mlx5_vdpa_warn(mvdev, "failed to insert new MAC %pM into MPFS table\n",
17925262912eSEli Cohen 				       mac);
17935262912eSEli Cohen 			break;
17945262912eSEli Cohen 		}
17955262912eSEli Cohen 
1796f1781bedSMichael Qiu 		/* backup the original mac address so that if failed to add the forward rules
1797f1781bedSMichael Qiu 		 * we could restore it
1798f1781bedSMichael Qiu 		 */
1799f1781bedSMichael Qiu 		memcpy(mac_back, ndev->config.mac, ETH_ALEN);
1800f1781bedSMichael Qiu 
18015262912eSEli Cohen 		memcpy(ndev->config.mac, mac, ETH_ALEN);
1802f1781bedSMichael Qiu 
1803f1781bedSMichael Qiu 		/* Need recreate the flow table entry, so that the packet could forward back
1804f1781bedSMichael Qiu 		 */
18051ab53760SEli Cohen 		mac_vlan_del(ndev, mac_back, 0, false);
1806f1781bedSMichael Qiu 
1807baf2ad3fSEli Cohen 		if (mac_vlan_add(ndev, ndev->config.mac, 0, false)) {
1808f1781bedSMichael Qiu 			mlx5_vdpa_warn(mvdev, "failed to insert forward rules, try to restore\n");
1809f1781bedSMichael Qiu 
1810f1781bedSMichael Qiu 			/* Although it hardly run here, we still need double check */
1811f1781bedSMichael Qiu 			if (is_zero_ether_addr(mac_back)) {
1812f1781bedSMichael Qiu 				mlx5_vdpa_warn(mvdev, "restore mac failed: Original MAC is zero\n");
1813f1781bedSMichael Qiu 				break;
1814f1781bedSMichael Qiu 			}
1815f1781bedSMichael Qiu 
1816f1781bedSMichael Qiu 			/* Try to restore original mac address to MFPS table, and try to restore
1817f1781bedSMichael Qiu 			 * the forward rule entry.
1818f1781bedSMichael Qiu 			 */
1819f1781bedSMichael Qiu 			if (mlx5_mpfs_del_mac(pfmdev, ndev->config.mac)) {
1820f1781bedSMichael Qiu 				mlx5_vdpa_warn(mvdev, "restore mac failed: delete MAC %pM from MPFS table failed\n",
1821f1781bedSMichael Qiu 					       ndev->config.mac);
1822f1781bedSMichael Qiu 			}
1823f1781bedSMichael Qiu 
1824f1781bedSMichael Qiu 			if (mlx5_mpfs_add_mac(pfmdev, mac_back)) {
1825f1781bedSMichael Qiu 				mlx5_vdpa_warn(mvdev, "restore mac failed: insert old MAC %pM into MPFS table failed\n",
1826f1781bedSMichael Qiu 					       mac_back);
1827f1781bedSMichael Qiu 			}
1828f1781bedSMichael Qiu 
1829f1781bedSMichael Qiu 			memcpy(ndev->config.mac, mac_back, ETH_ALEN);
1830f1781bedSMichael Qiu 
1831baf2ad3fSEli Cohen 			if (mac_vlan_add(ndev, ndev->config.mac, 0, false))
1832f1781bedSMichael Qiu 				mlx5_vdpa_warn(mvdev, "restore forward rules failed: insert forward rules failed\n");
1833f1781bedSMichael Qiu 
1834f1781bedSMichael Qiu 			break;
1835f1781bedSMichael Qiu 		}
1836f1781bedSMichael Qiu 
18375262912eSEli Cohen 		status = VIRTIO_NET_OK;
18385262912eSEli Cohen 		break;
18395262912eSEli Cohen 
18405262912eSEli Cohen 	default:
18415262912eSEli Cohen 		break;
18425262912eSEli Cohen 	}
18435262912eSEli Cohen 
18445262912eSEli Cohen 	return status;
18455262912eSEli Cohen }
18465262912eSEli Cohen 
184752893733SEli Cohen static int change_num_qps(struct mlx5_vdpa_dev *mvdev, int newqps)
184852893733SEli Cohen {
184952893733SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
185052893733SEli Cohen 	int cur_qps = ndev->cur_num_vqs / 2;
185152893733SEli Cohen 	int err;
185252893733SEli Cohen 	int i;
185352893733SEli Cohen 
185452893733SEli Cohen 	if (cur_qps > newqps) {
185552893733SEli Cohen 		err = modify_rqt(ndev, 2 * newqps);
185652893733SEli Cohen 		if (err)
185752893733SEli Cohen 			return err;
185852893733SEli Cohen 
185952893733SEli Cohen 		for (i = ndev->cur_num_vqs - 1; i >= 2 * newqps; i--)
186052893733SEli Cohen 			teardown_vq(ndev, &ndev->vqs[i]);
186152893733SEli Cohen 
186252893733SEli Cohen 		ndev->cur_num_vqs = 2 * newqps;
186352893733SEli Cohen 	} else {
186452893733SEli Cohen 		ndev->cur_num_vqs = 2 * newqps;
186552893733SEli Cohen 		for (i = cur_qps * 2; i < 2 * newqps; i++) {
186652893733SEli Cohen 			err = setup_vq(ndev, &ndev->vqs[i]);
186752893733SEli Cohen 			if (err)
186852893733SEli Cohen 				goto clean_added;
186952893733SEli Cohen 		}
187052893733SEli Cohen 		err = modify_rqt(ndev, 2 * newqps);
187152893733SEli Cohen 		if (err)
187252893733SEli Cohen 			goto clean_added;
187352893733SEli Cohen 	}
187452893733SEli Cohen 	return 0;
187552893733SEli Cohen 
187652893733SEli Cohen clean_added:
187737e07e70SEli Cohen 	for (--i; i >= 2 * cur_qps; --i)
187852893733SEli Cohen 		teardown_vq(ndev, &ndev->vqs[i]);
187952893733SEli Cohen 
188037e07e70SEli Cohen 	ndev->cur_num_vqs = 2 * cur_qps;
188137e07e70SEli Cohen 
188252893733SEli Cohen 	return err;
188352893733SEli Cohen }
188452893733SEli Cohen 
188552893733SEli Cohen static virtio_net_ctrl_ack handle_ctrl_mq(struct mlx5_vdpa_dev *mvdev, u8 cmd)
188652893733SEli Cohen {
188752893733SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
188852893733SEli Cohen 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
188952893733SEli Cohen 	struct mlx5_control_vq *cvq = &mvdev->cvq;
189052893733SEli Cohen 	struct virtio_net_ctrl_mq mq;
189152893733SEli Cohen 	size_t read;
189252893733SEli Cohen 	u16 newqps;
189352893733SEli Cohen 
189452893733SEli Cohen 	switch (cmd) {
189552893733SEli Cohen 	case VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET:
1896ed0f849fSSi-Wei Liu 		/* This mq feature check aligns with pre-existing userspace
1897ed0f849fSSi-Wei Liu 		 * implementation.
1898ed0f849fSSi-Wei Liu 		 *
1899ed0f849fSSi-Wei Liu 		 * Without it, an untrusted driver could fake a multiqueue config
1900ed0f849fSSi-Wei Liu 		 * request down to a non-mq device that may cause kernel to
1901ed0f849fSSi-Wei Liu 		 * panic due to uninitialized resources for extra vqs. Even with
1902ed0f849fSSi-Wei Liu 		 * a well behaving guest driver, it is not expected to allow
1903ed0f849fSSi-Wei Liu 		 * changing the number of vqs on a non-mq device.
1904ed0f849fSSi-Wei Liu 		 */
1905ed0f849fSSi-Wei Liu 		if (!MLX5_FEATURE(mvdev, VIRTIO_NET_F_MQ))
1906ed0f849fSSi-Wei Liu 			break;
1907ed0f849fSSi-Wei Liu 
190852893733SEli Cohen 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, (void *)&mq, sizeof(mq));
190952893733SEli Cohen 		if (read != sizeof(mq))
191052893733SEli Cohen 			break;
191152893733SEli Cohen 
191252893733SEli Cohen 		newqps = mlx5vdpa16_to_cpu(mvdev, mq.virtqueue_pairs);
1913ed0f849fSSi-Wei Liu 		if (newqps < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
1914acde3929SEli Cohen 		    newqps > ndev->rqt_size)
1915ed0f849fSSi-Wei Liu 			break;
1916ed0f849fSSi-Wei Liu 
191752893733SEli Cohen 		if (ndev->cur_num_vqs == 2 * newqps) {
191852893733SEli Cohen 			status = VIRTIO_NET_OK;
191952893733SEli Cohen 			break;
192052893733SEli Cohen 		}
192152893733SEli Cohen 
192252893733SEli Cohen 		if (!change_num_qps(mvdev, newqps))
192352893733SEli Cohen 			status = VIRTIO_NET_OK;
192452893733SEli Cohen 
192552893733SEli Cohen 		break;
192652893733SEli Cohen 	default:
192752893733SEli Cohen 		break;
192852893733SEli Cohen 	}
192952893733SEli Cohen 
193052893733SEli Cohen 	return status;
193152893733SEli Cohen }
193252893733SEli Cohen 
1933baf2ad3fSEli Cohen static virtio_net_ctrl_ack handle_ctrl_vlan(struct mlx5_vdpa_dev *mvdev, u8 cmd)
1934baf2ad3fSEli Cohen {
1935baf2ad3fSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
1936baf2ad3fSEli Cohen 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
1937baf2ad3fSEli Cohen 	struct mlx5_control_vq *cvq = &mvdev->cvq;
1938baf2ad3fSEli Cohen 	__virtio16 vlan;
1939baf2ad3fSEli Cohen 	size_t read;
1940baf2ad3fSEli Cohen 	u16 id;
1941baf2ad3fSEli Cohen 
19425aec8049SEli Cohen 	if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)))
19435aec8049SEli Cohen 		return status;
19445aec8049SEli Cohen 
1945baf2ad3fSEli Cohen 	switch (cmd) {
1946baf2ad3fSEli Cohen 	case VIRTIO_NET_CTRL_VLAN_ADD:
1947baf2ad3fSEli Cohen 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &vlan, sizeof(vlan));
1948baf2ad3fSEli Cohen 		if (read != sizeof(vlan))
1949baf2ad3fSEli Cohen 			break;
1950baf2ad3fSEli Cohen 
1951baf2ad3fSEli Cohen 		id = mlx5vdpa16_to_cpu(mvdev, vlan);
1952baf2ad3fSEli Cohen 		if (mac_vlan_add(ndev, ndev->config.mac, id, true))
1953baf2ad3fSEli Cohen 			break;
1954baf2ad3fSEli Cohen 
1955baf2ad3fSEli Cohen 		status = VIRTIO_NET_OK;
1956baf2ad3fSEli Cohen 		break;
1957baf2ad3fSEli Cohen 	case VIRTIO_NET_CTRL_VLAN_DEL:
1958baf2ad3fSEli Cohen 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &vlan, sizeof(vlan));
1959baf2ad3fSEli Cohen 		if (read != sizeof(vlan))
1960baf2ad3fSEli Cohen 			break;
1961baf2ad3fSEli Cohen 
1962baf2ad3fSEli Cohen 		id = mlx5vdpa16_to_cpu(mvdev, vlan);
1963baf2ad3fSEli Cohen 		mac_vlan_del(ndev, ndev->config.mac, id, true);
1964f766c409SDan Carpenter 		status = VIRTIO_NET_OK;
1965baf2ad3fSEli Cohen 		break;
1966baf2ad3fSEli Cohen 	default:
1967baf2ad3fSEli Cohen 		break;
1968baf2ad3fSEli Cohen 	}
1969baf2ad3fSEli Cohen 
1970baf2ad3fSEli Cohen 	return status;
1971baf2ad3fSEli Cohen }
1972baf2ad3fSEli Cohen 
19735262912eSEli Cohen static void mlx5_cvq_kick_handler(struct work_struct *work)
19745262912eSEli Cohen {
19755262912eSEli Cohen 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
19765262912eSEli Cohen 	struct virtio_net_ctrl_hdr ctrl;
1977218bdd20SEli Cohen 	struct mlx5_vdpa_wq_ent *wqent;
19785262912eSEli Cohen 	struct mlx5_vdpa_dev *mvdev;
19795262912eSEli Cohen 	struct mlx5_control_vq *cvq;
19805262912eSEli Cohen 	struct mlx5_vdpa_net *ndev;
19815262912eSEli Cohen 	size_t read, write;
19825262912eSEli Cohen 	int err;
19835262912eSEli Cohen 
1984218bdd20SEli Cohen 	wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
19855262912eSEli Cohen 	mvdev = wqent->mvdev;
19865262912eSEli Cohen 	ndev = to_mlx5_vdpa_ndev(mvdev);
19875262912eSEli Cohen 	cvq = &mvdev->cvq;
19881c80cf03SJason Wang 
1989759ae7f9SEli Cohen 	down_write(&ndev->reslock);
19901c80cf03SJason Wang 
19911c80cf03SJason Wang 	if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK))
19921c80cf03SJason Wang 		goto out;
19931c80cf03SJason Wang 
19945262912eSEli Cohen 	if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
19951c80cf03SJason Wang 		goto out;
19965262912eSEli Cohen 
19975262912eSEli Cohen 	if (!cvq->ready)
19981c80cf03SJason Wang 		goto out;
19995262912eSEli Cohen 
20005262912eSEli Cohen 	while (true) {
20015262912eSEli Cohen 		err = vringh_getdesc_iotlb(&cvq->vring, &cvq->riov, &cvq->wiov, &cvq->head,
20025262912eSEli Cohen 					   GFP_ATOMIC);
20035262912eSEli Cohen 		if (err <= 0)
20045262912eSEli Cohen 			break;
20055262912eSEli Cohen 
20065262912eSEli Cohen 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &ctrl, sizeof(ctrl));
20075262912eSEli Cohen 		if (read != sizeof(ctrl))
20085262912eSEli Cohen 			break;
20095262912eSEli Cohen 
20101892a3d4SEli Cohen 		cvq->received_desc++;
20115262912eSEli Cohen 		switch (ctrl.class) {
20125262912eSEli Cohen 		case VIRTIO_NET_CTRL_MAC:
20135262912eSEli Cohen 			status = handle_ctrl_mac(mvdev, ctrl.cmd);
20145262912eSEli Cohen 			break;
201552893733SEli Cohen 		case VIRTIO_NET_CTRL_MQ:
201652893733SEli Cohen 			status = handle_ctrl_mq(mvdev, ctrl.cmd);
201752893733SEli Cohen 			break;
2018baf2ad3fSEli Cohen 		case VIRTIO_NET_CTRL_VLAN:
2019baf2ad3fSEli Cohen 			status = handle_ctrl_vlan(mvdev, ctrl.cmd);
2020baf2ad3fSEli Cohen 			break;
20215262912eSEli Cohen 		default:
20225262912eSEli Cohen 			break;
20235262912eSEli Cohen 		}
20245262912eSEli Cohen 
20255262912eSEli Cohen 		/* Make sure data is written before advancing index */
20265262912eSEli Cohen 		smp_wmb();
20275262912eSEli Cohen 
20285262912eSEli Cohen 		write = vringh_iov_push_iotlb(&cvq->vring, &cvq->wiov, &status, sizeof(status));
20295262912eSEli Cohen 		vringh_complete_iotlb(&cvq->vring, cvq->head, write);
20305262912eSEli Cohen 		vringh_kiov_cleanup(&cvq->riov);
20315262912eSEli Cohen 		vringh_kiov_cleanup(&cvq->wiov);
20325262912eSEli Cohen 
20335262912eSEli Cohen 		if (vringh_need_notify_iotlb(&cvq->vring))
20345262912eSEli Cohen 			vringh_notify(&cvq->vring);
203555ebf0d6SJason Wang 
20361892a3d4SEli Cohen 		cvq->completed_desc++;
203755ebf0d6SJason Wang 		queue_work(mvdev->wq, &wqent->work);
203855ebf0d6SJason Wang 		break;
20395262912eSEli Cohen 	}
20401c80cf03SJason Wang 
20411c80cf03SJason Wang out:
2042759ae7f9SEli Cohen 	up_write(&ndev->reslock);
20435262912eSEli Cohen }
20445262912eSEli Cohen 
20451a86b377SEli Cohen static void mlx5_vdpa_kick_vq(struct vdpa_device *vdev, u16 idx)
20461a86b377SEli Cohen {
20471a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
20481a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
20495262912eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
20501a86b377SEli Cohen 
2051e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2052e4fc6650SEli Cohen 		return;
2053e4fc6650SEli Cohen 
20545262912eSEli Cohen 	if (unlikely(is_ctrl_vq_idx(mvdev, idx))) {
2055ad6dc1daSEli Cohen 		if (!mvdev->wq || !mvdev->cvq.ready)
20565262912eSEli Cohen 			return;
20575262912eSEli Cohen 
205855ebf0d6SJason Wang 		queue_work(mvdev->wq, &ndev->cvq_ent.work);
20595262912eSEli Cohen 		return;
20605262912eSEli Cohen 	}
20615262912eSEli Cohen 
20625262912eSEli Cohen 	mvq = &ndev->vqs[idx];
20631a86b377SEli Cohen 	if (unlikely(!mvq->ready))
20641a86b377SEli Cohen 		return;
20651a86b377SEli Cohen 
20661a86b377SEli Cohen 	iowrite16(idx, ndev->mvdev.res.kick_addr);
20671a86b377SEli Cohen }
20681a86b377SEli Cohen 
20691a86b377SEli Cohen static int mlx5_vdpa_set_vq_address(struct vdpa_device *vdev, u16 idx, u64 desc_area,
20701a86b377SEli Cohen 				    u64 driver_area, u64 device_area)
20711a86b377SEli Cohen {
20721a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
20731a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
20745262912eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
20751a86b377SEli Cohen 
2076e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2077e4fc6650SEli Cohen 		return -EINVAL;
2078e4fc6650SEli Cohen 
20795262912eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx)) {
20805262912eSEli Cohen 		mvdev->cvq.desc_addr = desc_area;
20815262912eSEli Cohen 		mvdev->cvq.device_addr = device_area;
20825262912eSEli Cohen 		mvdev->cvq.driver_addr = driver_area;
20835262912eSEli Cohen 		return 0;
20845262912eSEli Cohen 	}
20855262912eSEli Cohen 
20865262912eSEli Cohen 	mvq = &ndev->vqs[idx];
20871a86b377SEli Cohen 	mvq->desc_addr = desc_area;
20881a86b377SEli Cohen 	mvq->device_addr = device_area;
20891a86b377SEli Cohen 	mvq->driver_addr = driver_area;
20901a86b377SEli Cohen 	return 0;
20911a86b377SEli Cohen }
20921a86b377SEli Cohen 
20931a86b377SEli Cohen static void mlx5_vdpa_set_vq_num(struct vdpa_device *vdev, u16 idx, u32 num)
20941a86b377SEli Cohen {
20951a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
20961a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
20971a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
20981a86b377SEli Cohen 
20995262912eSEli Cohen 	if (!is_index_valid(mvdev, idx) || is_ctrl_vq_idx(mvdev, idx))
2100e4fc6650SEli Cohen 		return;
2101e4fc6650SEli Cohen 
21021a86b377SEli Cohen 	mvq = &ndev->vqs[idx];
21031a86b377SEli Cohen 	mvq->num_ent = num;
21041a86b377SEli Cohen }
21051a86b377SEli Cohen 
21061a86b377SEli Cohen static void mlx5_vdpa_set_vq_cb(struct vdpa_device *vdev, u16 idx, struct vdpa_callback *cb)
21071a86b377SEli Cohen {
21081a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
21091a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
21101a86b377SEli Cohen 
2111db296d25SEli Cohen 	ndev->event_cbs[idx] = *cb;
211240f2f3e9SEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx))
211340f2f3e9SEli Cohen 		mvdev->cvq.event_cb = *cb;
21141a86b377SEli Cohen }
21151a86b377SEli Cohen 
21165262912eSEli Cohen static void mlx5_cvq_notify(struct vringh *vring)
21175262912eSEli Cohen {
21185262912eSEli Cohen 	struct mlx5_control_vq *cvq = container_of(vring, struct mlx5_control_vq, vring);
21195262912eSEli Cohen 
21205262912eSEli Cohen 	if (!cvq->event_cb.callback)
21215262912eSEli Cohen 		return;
21225262912eSEli Cohen 
21235262912eSEli Cohen 	cvq->event_cb.callback(cvq->event_cb.private);
21245262912eSEli Cohen }
21255262912eSEli Cohen 
21265262912eSEli Cohen static void set_cvq_ready(struct mlx5_vdpa_dev *mvdev, bool ready)
21275262912eSEli Cohen {
21285262912eSEli Cohen 	struct mlx5_control_vq *cvq = &mvdev->cvq;
21295262912eSEli Cohen 
21305262912eSEli Cohen 	cvq->ready = ready;
21315262912eSEli Cohen 	if (!ready)
21325262912eSEli Cohen 		return;
21335262912eSEli Cohen 
21345262912eSEli Cohen 	cvq->vring.notify = mlx5_cvq_notify;
21351a86b377SEli Cohen }
21361a86b377SEli Cohen 
21371a86b377SEli Cohen static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready)
21381a86b377SEli Cohen {
21391a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
21401a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
21415262912eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
2142cae15c2eSEli Cohen 	int err;
21431a86b377SEli Cohen 
2144759be899SEli Cohen 	if (!mvdev->actual_features)
2145759be899SEli Cohen 		return;
2146759be899SEli Cohen 
2147e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2148e4fc6650SEli Cohen 		return;
2149e4fc6650SEli Cohen 
21505262912eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx)) {
21515262912eSEli Cohen 		set_cvq_ready(mvdev, ready);
21525262912eSEli Cohen 		return;
21535262912eSEli Cohen 	}
21545262912eSEli Cohen 
21555262912eSEli Cohen 	mvq = &ndev->vqs[idx];
2156cae15c2eSEli Cohen 	if (!ready) {
21571a86b377SEli Cohen 		suspend_vq(ndev, mvq);
2158cae15c2eSEli Cohen 	} else {
2159cae15c2eSEli Cohen 		err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY);
2160cae15c2eSEli Cohen 		if (err) {
2161cae15c2eSEli Cohen 			mlx5_vdpa_warn(mvdev, "modify VQ %d to ready failed (%d)\n", idx, err);
2162cae15c2eSEli Cohen 			ready = false;
2163cae15c2eSEli Cohen 		}
2164cae15c2eSEli Cohen 	}
2165cae15c2eSEli Cohen 
21661a86b377SEli Cohen 
21671a86b377SEli Cohen 	mvq->ready = ready;
21681a86b377SEli Cohen }
21691a86b377SEli Cohen 
21701a86b377SEli Cohen static bool mlx5_vdpa_get_vq_ready(struct vdpa_device *vdev, u16 idx)
21711a86b377SEli Cohen {
21721a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
21731a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
21741a86b377SEli Cohen 
2175e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2176e4fc6650SEli Cohen 		return false;
2177e4fc6650SEli Cohen 
21785262912eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx))
21795262912eSEli Cohen 		return mvdev->cvq.ready;
21805262912eSEli Cohen 
21815262912eSEli Cohen 	return ndev->vqs[idx].ready;
21821a86b377SEli Cohen }
21831a86b377SEli Cohen 
21841a86b377SEli Cohen static int mlx5_vdpa_set_vq_state(struct vdpa_device *vdev, u16 idx,
21851a86b377SEli Cohen 				  const struct vdpa_vq_state *state)
21861a86b377SEli Cohen {
21871a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
21881a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
21895262912eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
21901a86b377SEli Cohen 
2191e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2192e4fc6650SEli Cohen 		return -EINVAL;
2193e4fc6650SEli Cohen 
21945262912eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx)) {
21955262912eSEli Cohen 		mvdev->cvq.vring.last_avail_idx = state->split.avail_index;
21965262912eSEli Cohen 		return 0;
21975262912eSEli Cohen 	}
21985262912eSEli Cohen 
21995262912eSEli Cohen 	mvq = &ndev->vqs[idx];
22001a86b377SEli Cohen 	if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY) {
22011a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "can't modify available index\n");
22021a86b377SEli Cohen 		return -EINVAL;
22031a86b377SEli Cohen 	}
22041a86b377SEli Cohen 
2205530a5678SJason Wang 	mvq->used_idx = state->split.avail_index;
2206530a5678SJason Wang 	mvq->avail_idx = state->split.avail_index;
22071a86b377SEli Cohen 	return 0;
22081a86b377SEli Cohen }
22091a86b377SEli Cohen 
22101a86b377SEli Cohen static int mlx5_vdpa_get_vq_state(struct vdpa_device *vdev, u16 idx, struct vdpa_vq_state *state)
22111a86b377SEli Cohen {
22121a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
22131a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
22145262912eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
22151a86b377SEli Cohen 	struct mlx5_virtq_attr attr;
22161a86b377SEli Cohen 	int err;
22171a86b377SEli Cohen 
2218e4fc6650SEli Cohen 	if (!is_index_valid(mvdev, idx))
2219e4fc6650SEli Cohen 		return -EINVAL;
2220e4fc6650SEli Cohen 
22215262912eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx)) {
22225262912eSEli Cohen 		state->split.avail_index = mvdev->cvq.vring.last_avail_idx;
22235262912eSEli Cohen 		return 0;
22245262912eSEli Cohen 	}
22255262912eSEli Cohen 
22265262912eSEli Cohen 	mvq = &ndev->vqs[idx];
22273176e974SSi-Wei Liu 	/* If the virtq object was destroyed, use the value saved at
22283176e974SSi-Wei Liu 	 * the last minute of suspend_vq. This caters for userspace
22293176e974SSi-Wei Liu 	 * that cares about emulating the index after vq is stopped.
22303176e974SSi-Wei Liu 	 */
22313176e974SSi-Wei Liu 	if (!mvq->initialized) {
2232bc04d93eSEli Cohen 		/* Firmware returns a wrong value for the available index.
2233bc04d93eSEli Cohen 		 * Since both values should be identical, we take the value of
2234bc04d93eSEli Cohen 		 * used_idx which is reported correctly.
2235bc04d93eSEli Cohen 		 */
2236530a5678SJason Wang 		state->split.avail_index = mvq->used_idx;
22373176e974SSi-Wei Liu 		return 0;
22383176e974SSi-Wei Liu 	}
22391a86b377SEli Cohen 
22401a86b377SEli Cohen 	err = query_virtqueue(ndev, mvq, &attr);
22411a86b377SEli Cohen 	if (err) {
22421a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "failed to query virtqueue\n");
22431a86b377SEli Cohen 		return err;
22441a86b377SEli Cohen 	}
2245530a5678SJason Wang 	state->split.avail_index = attr.used_index;
22461a86b377SEli Cohen 	return 0;
22471a86b377SEli Cohen }
22481a86b377SEli Cohen 
22491a86b377SEli Cohen static u32 mlx5_vdpa_get_vq_align(struct vdpa_device *vdev)
22501a86b377SEli Cohen {
22511a86b377SEli Cohen 	return PAGE_SIZE;
22521a86b377SEli Cohen }
22531a86b377SEli Cohen 
22548fcd20c3SEli Cohen static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdev, u16 idx)
2255d4821902SGautam Dawar {
22568fcd20c3SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
22578fcd20c3SEli Cohen 
22588fcd20c3SEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx))
22598fcd20c3SEli Cohen 		return MLX5_VDPA_CVQ_GROUP;
22608fcd20c3SEli Cohen 
22618fcd20c3SEli Cohen 	return MLX5_VDPA_DATAVQ_GROUP;
2262d4821902SGautam Dawar }
2263d4821902SGautam Dawar 
22641a86b377SEli Cohen static u64 mlx_to_vritio_features(u16 dev_features)
22651a86b377SEli Cohen {
22661a86b377SEli Cohen 	u64 result = 0;
22671a86b377SEli Cohen 
2268e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_MRG_RXBUF))
2269e9d67e59SEli Cohen 		result |= BIT_ULL(VIRTIO_NET_F_MRG_RXBUF);
2270e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_HOST_ECN))
2271e9d67e59SEli Cohen 		result |= BIT_ULL(VIRTIO_NET_F_HOST_ECN);
2272e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_GUEST_ECN))
2273e9d67e59SEli Cohen 		result |= BIT_ULL(VIRTIO_NET_F_GUEST_ECN);
2274e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_GUEST_TSO6))
2275e9d67e59SEli Cohen 		result |= BIT_ULL(VIRTIO_NET_F_GUEST_TSO6);
2276e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_GUEST_TSO4))
2277e9d67e59SEli Cohen 		result |= BIT_ULL(VIRTIO_NET_F_GUEST_TSO4);
2278e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_GUEST_CSUM))
2279cbb52359SNathan Chancellor 		result |= BIT_ULL(VIRTIO_NET_F_GUEST_CSUM);
2280e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_CSUM))
2281cbb52359SNathan Chancellor 		result |= BIT_ULL(VIRTIO_NET_F_CSUM);
2282e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_HOST_TSO6))
2283cbb52359SNathan Chancellor 		result |= BIT_ULL(VIRTIO_NET_F_HOST_TSO6);
2284e9d67e59SEli Cohen 	if (dev_features & BIT_ULL(MLX5_VIRTIO_NET_F_HOST_TSO4))
2285cbb52359SNathan Chancellor 		result |= BIT_ULL(VIRTIO_NET_F_HOST_TSO4);
22861a86b377SEli Cohen 
22871a86b377SEli Cohen 	return result;
22881a86b377SEli Cohen }
22891a86b377SEli Cohen 
229079de65edSEli Cohen static u64 get_supported_features(struct mlx5_core_dev *mdev)
229179de65edSEli Cohen {
229279de65edSEli Cohen 	u64 mlx_vdpa_features = 0;
229379de65edSEli Cohen 	u16 dev_features;
229479de65edSEli Cohen 
229579de65edSEli Cohen 	dev_features = MLX5_CAP_DEV_VDPA_EMULATION(mdev, device_features_bits_mask);
229679de65edSEli Cohen 	mlx_vdpa_features |= mlx_to_vritio_features(dev_features);
229779de65edSEli Cohen 	if (MLX5_CAP_DEV_VDPA_EMULATION(mdev, virtio_version_1_0))
229879de65edSEli Cohen 		mlx_vdpa_features |= BIT_ULL(VIRTIO_F_VERSION_1);
229979de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_F_ACCESS_PLATFORM);
230079de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VQ);
230179de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR);
230279de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MQ);
230379de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_STATUS);
230479de65edSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MTU);
2305baf2ad3fSEli Cohen 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VLAN);
2306deeacf35SSi-Wei Liu 	mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MAC);
230779de65edSEli Cohen 
230879de65edSEli Cohen 	return mlx_vdpa_features;
230979de65edSEli Cohen }
231079de65edSEli Cohen 
2311a64917bcSEli Cohen static u64 mlx5_vdpa_get_device_features(struct vdpa_device *vdev)
23121a86b377SEli Cohen {
23131a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
23141a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
23155262912eSEli Cohen 
23161a86b377SEli Cohen 	print_features(mvdev, ndev->mvdev.mlx_features, false);
23171a86b377SEli Cohen 	return ndev->mvdev.mlx_features;
23181a86b377SEli Cohen }
23191a86b377SEli Cohen 
232030c22f38SSi-Wei Liu static int verify_driver_features(struct mlx5_vdpa_dev *mvdev, u64 features)
23211a86b377SEli Cohen {
232230c22f38SSi-Wei Liu 	/* Minimum features to expect */
2323cbb52359SNathan Chancellor 	if (!(features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM)))
23241a86b377SEli Cohen 		return -EOPNOTSUPP;
23251a86b377SEli Cohen 
232630c22f38SSi-Wei Liu 	/* Double check features combination sent down by the driver.
232730c22f38SSi-Wei Liu 	 * Fail invalid features due to absence of the depended feature.
232830c22f38SSi-Wei Liu 	 *
232930c22f38SSi-Wei Liu 	 * Per VIRTIO v1.1 specification, section 5.1.3.1 Feature bit
233030c22f38SSi-Wei Liu 	 * requirements: "VIRTIO_NET_F_MQ Requires VIRTIO_NET_F_CTRL_VQ".
233130c22f38SSi-Wei Liu 	 * By failing the invalid features sent down by untrusted drivers,
233230c22f38SSi-Wei Liu 	 * we're assured the assumption made upon is_index_valid() and
233330c22f38SSi-Wei Liu 	 * is_ctrl_vq_idx() will not be compromised.
233430c22f38SSi-Wei Liu 	 */
233530c22f38SSi-Wei Liu 	if ((features & (BIT_ULL(VIRTIO_NET_F_MQ) | BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) ==
233630c22f38SSi-Wei Liu             BIT_ULL(VIRTIO_NET_F_MQ))
233730c22f38SSi-Wei Liu 		return -EINVAL;
233830c22f38SSi-Wei Liu 
23391a86b377SEli Cohen 	return 0;
23401a86b377SEli Cohen }
23411a86b377SEli Cohen 
2342ae0428deSEli Cohen static int setup_virtqueues(struct mlx5_vdpa_dev *mvdev)
23431a86b377SEli Cohen {
2344ae0428deSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
23451a86b377SEli Cohen 	int err;
23461a86b377SEli Cohen 	int i;
23471a86b377SEli Cohen 
2348acde3929SEli Cohen 	for (i = 0; i < mvdev->max_vqs; i++) {
23491a86b377SEli Cohen 		err = setup_vq(ndev, &ndev->vqs[i]);
23501a86b377SEli Cohen 		if (err)
23511a86b377SEli Cohen 			goto err_vq;
23521a86b377SEli Cohen 	}
23531a86b377SEli Cohen 
23541a86b377SEli Cohen 	return 0;
23551a86b377SEli Cohen 
23561a86b377SEli Cohen err_vq:
23571a86b377SEli Cohen 	for (--i; i >= 0; i--)
23581a86b377SEli Cohen 		teardown_vq(ndev, &ndev->vqs[i]);
23591a86b377SEli Cohen 
23601a86b377SEli Cohen 	return err;
23611a86b377SEli Cohen }
23621a86b377SEli Cohen 
23631a86b377SEli Cohen static void teardown_virtqueues(struct mlx5_vdpa_net *ndev)
23641a86b377SEli Cohen {
23651a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
23661a86b377SEli Cohen 	int i;
23671a86b377SEli Cohen 
23681a86b377SEli Cohen 	for (i = ndev->mvdev.max_vqs - 1; i >= 0; i--) {
23691a86b377SEli Cohen 		mvq = &ndev->vqs[i];
23701a86b377SEli Cohen 		if (!mvq->initialized)
23711a86b377SEli Cohen 			continue;
23721a86b377SEli Cohen 
23731a86b377SEli Cohen 		teardown_vq(ndev, mvq);
23741a86b377SEli Cohen 	}
23751a86b377SEli Cohen }
23761a86b377SEli Cohen 
2377e4fc6650SEli Cohen static void update_cvq_info(struct mlx5_vdpa_dev *mvdev)
23788a7c3213SMichael S. Tsirkin {
2379e4fc6650SEli Cohen 	if (MLX5_FEATURE(mvdev, VIRTIO_NET_F_CTRL_VQ)) {
2380e4fc6650SEli Cohen 		if (MLX5_FEATURE(mvdev, VIRTIO_NET_F_MQ)) {
2381e4fc6650SEli Cohen 			/* MQ supported. CVQ index is right above the last data virtqueue's */
2382e4fc6650SEli Cohen 			mvdev->max_idx = mvdev->max_vqs;
2383e4fc6650SEli Cohen 		} else {
2384e4fc6650SEli Cohen 			/* Only CVQ supportted. data virtqueues occupy indices 0 and 1.
2385e4fc6650SEli Cohen 			 * CVQ gets index 2
2386e4fc6650SEli Cohen 			 */
2387e4fc6650SEli Cohen 			mvdev->max_idx = 2;
23888a7c3213SMichael S. Tsirkin 		}
2389e4fc6650SEli Cohen 	} else {
2390e4fc6650SEli Cohen 		/* Two data virtqueues only: one for rx and one for tx */
2391e4fc6650SEli Cohen 		mvdev->max_idx = 1;
2392e4fc6650SEli Cohen 	}
239336bdcf31SEli Cohen }
239436bdcf31SEli Cohen 
2395c384c240SEli Cohen static u8 query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
2396c384c240SEli Cohen {
2397c384c240SEli Cohen 	u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {};
2398c384c240SEli Cohen 	u32 in[MLX5_ST_SZ_DW(query_vport_state_in)] = {};
2399c384c240SEli Cohen 	int err;
2400c384c240SEli Cohen 
2401c384c240SEli Cohen 	MLX5_SET(query_vport_state_in, in, opcode, MLX5_CMD_OP_QUERY_VPORT_STATE);
2402c384c240SEli Cohen 	MLX5_SET(query_vport_state_in, in, op_mod, opmod);
2403c384c240SEli Cohen 	MLX5_SET(query_vport_state_in, in, vport_number, vport);
2404c384c240SEli Cohen 	if (vport)
2405c384c240SEli Cohen 		MLX5_SET(query_vport_state_in, in, other_vport, 1);
2406c384c240SEli Cohen 
2407c384c240SEli Cohen 	err = mlx5_cmd_exec_inout(mdev, query_vport_state, in, out);
2408c384c240SEli Cohen 	if (err)
2409c384c240SEli Cohen 		return 0;
2410c384c240SEli Cohen 
2411c384c240SEli Cohen 	return MLX5_GET(query_vport_state_out, out, state);
2412c384c240SEli Cohen }
2413c384c240SEli Cohen 
2414c384c240SEli Cohen static bool get_link_state(struct mlx5_vdpa_dev *mvdev)
2415c384c240SEli Cohen {
2416c384c240SEli Cohen 	if (query_vport_state(mvdev->mdev, MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT, 0) ==
2417c384c240SEli Cohen 	    VPORT_STATE_UP)
2418c384c240SEli Cohen 		return true;
2419c384c240SEli Cohen 
2420c384c240SEli Cohen 	return false;
2421c384c240SEli Cohen }
2422c384c240SEli Cohen 
2423c384c240SEli Cohen static void update_carrier(struct work_struct *work)
2424c384c240SEli Cohen {
2425c384c240SEli Cohen 	struct mlx5_vdpa_wq_ent *wqent;
2426c384c240SEli Cohen 	struct mlx5_vdpa_dev *mvdev;
2427c384c240SEli Cohen 	struct mlx5_vdpa_net *ndev;
2428c384c240SEli Cohen 
2429c384c240SEli Cohen 	wqent = container_of(work, struct mlx5_vdpa_wq_ent, work);
2430c384c240SEli Cohen 	mvdev = wqent->mvdev;
2431c384c240SEli Cohen 	ndev = to_mlx5_vdpa_ndev(mvdev);
2432c384c240SEli Cohen 	if (get_link_state(mvdev))
2433c384c240SEli Cohen 		ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
2434c384c240SEli Cohen 	else
2435c384c240SEli Cohen 		ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
2436c384c240SEli Cohen 
2437c384c240SEli Cohen 	if (ndev->config_cb.callback)
2438c384c240SEli Cohen 		ndev->config_cb.callback(ndev->config_cb.private);
2439c384c240SEli Cohen 
2440c384c240SEli Cohen 	kfree(wqent);
2441c384c240SEli Cohen }
2442c384c240SEli Cohen 
2443c384c240SEli Cohen static int queue_link_work(struct mlx5_vdpa_net *ndev)
2444c384c240SEli Cohen {
2445c384c240SEli Cohen 	struct mlx5_vdpa_wq_ent *wqent;
2446c384c240SEli Cohen 
2447c384c240SEli Cohen 	wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC);
2448c384c240SEli Cohen 	if (!wqent)
2449c384c240SEli Cohen 		return -ENOMEM;
2450c384c240SEli Cohen 
2451c384c240SEli Cohen 	wqent->mvdev = &ndev->mvdev;
2452c384c240SEli Cohen 	INIT_WORK(&wqent->work, update_carrier);
2453c384c240SEli Cohen 	queue_work(ndev->mvdev.wq, &wqent->work);
2454c384c240SEli Cohen 	return 0;
2455c384c240SEli Cohen }
2456c384c240SEli Cohen 
2457c384c240SEli Cohen static int event_handler(struct notifier_block *nb, unsigned long event, void *param)
2458c384c240SEli Cohen {
2459c384c240SEli Cohen 	struct mlx5_vdpa_net *ndev = container_of(nb, struct mlx5_vdpa_net, nb);
2460c384c240SEli Cohen 	struct mlx5_eqe *eqe = param;
2461c384c240SEli Cohen 	int ret = NOTIFY_DONE;
2462c384c240SEli Cohen 
2463c384c240SEli Cohen 	if (event == MLX5_EVENT_TYPE_PORT_CHANGE) {
2464c384c240SEli Cohen 		switch (eqe->sub_type) {
2465c384c240SEli Cohen 		case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
2466c384c240SEli Cohen 		case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
2467c384c240SEli Cohen 			if (queue_link_work(ndev))
2468c384c240SEli Cohen 				return NOTIFY_DONE;
2469c384c240SEli Cohen 
2470c384c240SEli Cohen 			ret = NOTIFY_OK;
2471c384c240SEli Cohen 			break;
2472c384c240SEli Cohen 		default:
2473c384c240SEli Cohen 			return NOTIFY_DONE;
2474c384c240SEli Cohen 		}
2475c384c240SEli Cohen 		return ret;
2476c384c240SEli Cohen 	}
2477c384c240SEli Cohen 	return ret;
2478c384c240SEli Cohen }
2479c384c240SEli Cohen 
2480c384c240SEli Cohen static void register_link_notifier(struct mlx5_vdpa_net *ndev)
2481c384c240SEli Cohen {
2482c384c240SEli Cohen 	if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_STATUS)))
2483c384c240SEli Cohen 		return;
2484c384c240SEli Cohen 
2485c384c240SEli Cohen 	ndev->nb.notifier_call = event_handler;
2486c384c240SEli Cohen 	mlx5_notifier_register(ndev->mvdev.mdev, &ndev->nb);
2487c384c240SEli Cohen 	ndev->nb_registered = true;
2488c384c240SEli Cohen 	queue_link_work(ndev);
2489c384c240SEli Cohen }
2490c384c240SEli Cohen 
2491c384c240SEli Cohen static void unregister_link_notifier(struct mlx5_vdpa_net *ndev)
2492c384c240SEli Cohen {
2493c384c240SEli Cohen 	if (!ndev->nb_registered)
2494c384c240SEli Cohen 		return;
2495c384c240SEli Cohen 
2496c384c240SEli Cohen 	ndev->nb_registered = false;
2497c384c240SEli Cohen 	mlx5_notifier_unregister(ndev->mvdev.mdev, &ndev->nb);
2498c384c240SEli Cohen 	if (ndev->mvdev.wq)
2499c384c240SEli Cohen 		flush_workqueue(ndev->mvdev.wq);
2500c384c240SEli Cohen }
2501c384c240SEli Cohen 
2502a64917bcSEli Cohen static int mlx5_vdpa_set_driver_features(struct vdpa_device *vdev, u64 features)
25031a86b377SEli Cohen {
25041a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
25051a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
25061a86b377SEli Cohen 	int err;
25071a86b377SEli Cohen 
25081a86b377SEli Cohen 	print_features(mvdev, features, true);
25091a86b377SEli Cohen 
251030c22f38SSi-Wei Liu 	err = verify_driver_features(mvdev, features);
25111a86b377SEli Cohen 	if (err)
25121a86b377SEli Cohen 		return err;
25131a86b377SEli Cohen 
25141a86b377SEli Cohen 	ndev->mvdev.actual_features = features & ndev->mvdev.mlx_features;
2515b03fc43eSEli Cohen 	if (ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_MQ))
2516acde3929SEli Cohen 		ndev->rqt_size = mlx5vdpa16_to_cpu(mvdev, ndev->config.max_virtqueue_pairs);
2517b03fc43eSEli Cohen 	else
2518acde3929SEli Cohen 		ndev->rqt_size = 1;
2519acde3929SEli Cohen 
2520*3fe02419SDragos Tatulea 	/* Device must start with 1 queue pair, as per VIRTIO v1.2 spec, section
2521*3fe02419SDragos Tatulea 	 * 5.1.6.5.5 "Device operation in multiqueue mode":
2522*3fe02419SDragos Tatulea 	 *
2523*3fe02419SDragos Tatulea 	 * Multiqueue is disabled by default.
2524*3fe02419SDragos Tatulea 	 * The driver enables multiqueue by sending a command using class
2525*3fe02419SDragos Tatulea 	 * VIRTIO_NET_CTRL_MQ. The command selects the mode of multiqueue
2526*3fe02419SDragos Tatulea 	 * operation, as follows: ...
2527*3fe02419SDragos Tatulea 	 */
2528*3fe02419SDragos Tatulea 	ndev->cur_num_vqs = 2;
2529b03fc43eSEli Cohen 
2530e4fc6650SEli Cohen 	update_cvq_info(mvdev);
25311a86b377SEli Cohen 	return err;
25321a86b377SEli Cohen }
25331a86b377SEli Cohen 
25341a86b377SEli Cohen static void mlx5_vdpa_set_config_cb(struct vdpa_device *vdev, struct vdpa_callback *cb)
25351a86b377SEli Cohen {
2536edf747afSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
2537edf747afSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
2538edf747afSEli Cohen 
2539edf747afSEli Cohen 	ndev->config_cb = *cb;
25401a86b377SEli Cohen }
25411a86b377SEli Cohen 
25421a86b377SEli Cohen #define MLX5_VDPA_MAX_VQ_ENTRIES 256
25431a86b377SEli Cohen static u16 mlx5_vdpa_get_vq_num_max(struct vdpa_device *vdev)
25441a86b377SEli Cohen {
25451a86b377SEli Cohen 	return MLX5_VDPA_MAX_VQ_ENTRIES;
25461a86b377SEli Cohen }
25471a86b377SEli Cohen 
25481a86b377SEli Cohen static u32 mlx5_vdpa_get_device_id(struct vdpa_device *vdev)
25491a86b377SEli Cohen {
25501a86b377SEli Cohen 	return VIRTIO_ID_NET;
25511a86b377SEli Cohen }
25521a86b377SEli Cohen 
25531a86b377SEli Cohen static u32 mlx5_vdpa_get_vendor_id(struct vdpa_device *vdev)
25541a86b377SEli Cohen {
25551a86b377SEli Cohen 	return PCI_VENDOR_ID_MELLANOX;
25561a86b377SEli Cohen }
25571a86b377SEli Cohen 
25581a86b377SEli Cohen static u8 mlx5_vdpa_get_status(struct vdpa_device *vdev)
25591a86b377SEli Cohen {
25601a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
25611a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
25621a86b377SEli Cohen 
25631a86b377SEli Cohen 	print_status(mvdev, ndev->mvdev.status, false);
25641a86b377SEli Cohen 	return ndev->mvdev.status;
25651a86b377SEli Cohen }
25661a86b377SEli Cohen 
25671a86b377SEli Cohen static int save_channel_info(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq)
25681a86b377SEli Cohen {
25691a86b377SEli Cohen 	struct mlx5_vq_restore_info *ri = &mvq->ri;
257052893733SEli Cohen 	struct mlx5_virtq_attr attr = {};
25711a86b377SEli Cohen 	int err;
25721a86b377SEli Cohen 
257352893733SEli Cohen 	if (mvq->initialized) {
25741a86b377SEli Cohen 		err = query_virtqueue(ndev, mvq, &attr);
25751a86b377SEli Cohen 		if (err)
25761a86b377SEli Cohen 			return err;
257752893733SEli Cohen 	}
25781a86b377SEli Cohen 
25791a86b377SEli Cohen 	ri->avail_index = attr.available_index;
2580b35ccebeSEli Cohen 	ri->used_index = attr.used_index;
25811a86b377SEli Cohen 	ri->ready = mvq->ready;
25821a86b377SEli Cohen 	ri->num_ent = mvq->num_ent;
25831a86b377SEli Cohen 	ri->desc_addr = mvq->desc_addr;
25841a86b377SEli Cohen 	ri->device_addr = mvq->device_addr;
25851a86b377SEli Cohen 	ri->driver_addr = mvq->driver_addr;
2586bc9a2b3eSEli Cohen 	ri->map = mvq->map;
25871a86b377SEli Cohen 	ri->restore = true;
25881a86b377SEli Cohen 	return 0;
25891a86b377SEli Cohen }
25901a86b377SEli Cohen 
25911a86b377SEli Cohen static int save_channels_info(struct mlx5_vdpa_net *ndev)
25921a86b377SEli Cohen {
25931a86b377SEli Cohen 	int i;
25941a86b377SEli Cohen 
25951a86b377SEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++) {
25961a86b377SEli Cohen 		memset(&ndev->vqs[i].ri, 0, sizeof(ndev->vqs[i].ri));
25971a86b377SEli Cohen 		save_channel_info(ndev, &ndev->vqs[i]);
25981a86b377SEli Cohen 	}
25991a86b377SEli Cohen 	return 0;
26001a86b377SEli Cohen }
26011a86b377SEli Cohen 
26021a86b377SEli Cohen static void mlx5_clear_vqs(struct mlx5_vdpa_net *ndev)
26031a86b377SEli Cohen {
26041a86b377SEli Cohen 	int i;
26051a86b377SEli Cohen 
26061a86b377SEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++)
26071a86b377SEli Cohen 		memset(&ndev->vqs[i], 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
26081a86b377SEli Cohen }
26091a86b377SEli Cohen 
26101a86b377SEli Cohen static void restore_channels_info(struct mlx5_vdpa_net *ndev)
26111a86b377SEli Cohen {
26121a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
26131a86b377SEli Cohen 	struct mlx5_vq_restore_info *ri;
26141a86b377SEli Cohen 	int i;
26151a86b377SEli Cohen 
26161a86b377SEli Cohen 	mlx5_clear_vqs(ndev);
26171a86b377SEli Cohen 	init_mvqs(ndev);
26181a86b377SEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++) {
26191a86b377SEli Cohen 		mvq = &ndev->vqs[i];
26201a86b377SEli Cohen 		ri = &mvq->ri;
26211a86b377SEli Cohen 		if (!ri->restore)
26221a86b377SEli Cohen 			continue;
26231a86b377SEli Cohen 
26241a86b377SEli Cohen 		mvq->avail_idx = ri->avail_index;
2625b35ccebeSEli Cohen 		mvq->used_idx = ri->used_index;
26261a86b377SEli Cohen 		mvq->ready = ri->ready;
26271a86b377SEli Cohen 		mvq->num_ent = ri->num_ent;
26281a86b377SEli Cohen 		mvq->desc_addr = ri->desc_addr;
26291a86b377SEli Cohen 		mvq->device_addr = ri->device_addr;
26301a86b377SEli Cohen 		mvq->driver_addr = ri->driver_addr;
2631bc9a2b3eSEli Cohen 		mvq->map = ri->map;
26321a86b377SEli Cohen 	}
26331a86b377SEli Cohen }
26341a86b377SEli Cohen 
263538fc462fSEli Cohen static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
263638fc462fSEli Cohen 				struct vhost_iotlb *iotlb, unsigned int asid)
26371a86b377SEli Cohen {
2638ae0428deSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
26391a86b377SEli Cohen 	int err;
26401a86b377SEli Cohen 
26411a86b377SEli Cohen 	suspend_vqs(ndev);
26421a86b377SEli Cohen 	err = save_channels_info(ndev);
26431a86b377SEli Cohen 	if (err)
26441a86b377SEli Cohen 		goto err_mr;
26451a86b377SEli Cohen 
26461a86b377SEli Cohen 	teardown_driver(ndev);
2647ae0428deSEli Cohen 	mlx5_vdpa_destroy_mr(mvdev);
264838fc462fSEli Cohen 	err = mlx5_vdpa_create_mr(mvdev, iotlb, asid);
26491a86b377SEli Cohen 	if (err)
26501a86b377SEli Cohen 		goto err_mr;
26511a86b377SEli Cohen 
265209e65ee9SSi-Wei Liu 	if (!(mvdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || mvdev->suspended)
26531c80cf03SJason Wang 		goto err_mr;
26541897f0b6SEli Cohen 
26551a86b377SEli Cohen 	restore_channels_info(ndev);
2656ae0428deSEli Cohen 	err = setup_driver(mvdev);
26571a86b377SEli Cohen 	if (err)
26581a86b377SEli Cohen 		goto err_setup;
26591a86b377SEli Cohen 
26601a86b377SEli Cohen 	return 0;
26611a86b377SEli Cohen 
26621a86b377SEli Cohen err_setup:
2663ae0428deSEli Cohen 	mlx5_vdpa_destroy_mr(mvdev);
26641a86b377SEli Cohen err_mr:
26651a86b377SEli Cohen 	return err;
26661a86b377SEli Cohen }
26671a86b377SEli Cohen 
26681c80cf03SJason Wang /* reslock must be held for this function */
2669ae0428deSEli Cohen static int setup_driver(struct mlx5_vdpa_dev *mvdev)
26701a86b377SEli Cohen {
2671ae0428deSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
26721a86b377SEli Cohen 	int err;
26731a86b377SEli Cohen 
2674759ae7f9SEli Cohen 	WARN_ON(!rwsem_is_locked(&ndev->reslock));
26751c80cf03SJason Wang 
26761a86b377SEli Cohen 	if (ndev->setup) {
2677ae0428deSEli Cohen 		mlx5_vdpa_warn(mvdev, "setup driver called for already setup driver\n");
26781a86b377SEli Cohen 		err = 0;
26791a86b377SEli Cohen 		goto out;
26801a86b377SEli Cohen 	}
2681f0417e72SEli Cohen 	mlx5_vdpa_add_debugfs(ndev);
2682ae0428deSEli Cohen 	err = setup_virtqueues(mvdev);
26831a86b377SEli Cohen 	if (err) {
2684ae0428deSEli Cohen 		mlx5_vdpa_warn(mvdev, "setup_virtqueues\n");
2685f0417e72SEli Cohen 		goto err_setup;
26861a86b377SEli Cohen 	}
26871a86b377SEli Cohen 
26881a86b377SEli Cohen 	err = create_rqt(ndev);
26891a86b377SEli Cohen 	if (err) {
2690ae0428deSEli Cohen 		mlx5_vdpa_warn(mvdev, "create_rqt\n");
26911a86b377SEli Cohen 		goto err_rqt;
26921a86b377SEli Cohen 	}
26931a86b377SEli Cohen 
26941a86b377SEli Cohen 	err = create_tir(ndev);
26951a86b377SEli Cohen 	if (err) {
2696ae0428deSEli Cohen 		mlx5_vdpa_warn(mvdev, "create_tir\n");
26971a86b377SEli Cohen 		goto err_tir;
26981a86b377SEli Cohen 	}
26991a86b377SEli Cohen 
2700baf2ad3fSEli Cohen 	err = setup_steering(ndev);
27011a86b377SEli Cohen 	if (err) {
2702baf2ad3fSEli Cohen 		mlx5_vdpa_warn(mvdev, "setup_steering\n");
27031a86b377SEli Cohen 		goto err_fwd;
27041a86b377SEli Cohen 	}
27051a86b377SEli Cohen 	ndev->setup = true;
27061a86b377SEli Cohen 
27071a86b377SEli Cohen 	return 0;
27081a86b377SEli Cohen 
27091a86b377SEli Cohen err_fwd:
27101a86b377SEli Cohen 	destroy_tir(ndev);
27111a86b377SEli Cohen err_tir:
27121a86b377SEli Cohen 	destroy_rqt(ndev);
27131a86b377SEli Cohen err_rqt:
27141a86b377SEli Cohen 	teardown_virtqueues(ndev);
2715f0417e72SEli Cohen err_setup:
2716f0417e72SEli Cohen 	mlx5_vdpa_remove_debugfs(ndev->debugfs);
27171a86b377SEli Cohen out:
27181a86b377SEli Cohen 	return err;
27191a86b377SEli Cohen }
27201a86b377SEli Cohen 
27211c80cf03SJason Wang /* reslock must be held for this function */
27221a86b377SEli Cohen static void teardown_driver(struct mlx5_vdpa_net *ndev)
27231a86b377SEli Cohen {
27241c80cf03SJason Wang 
2725759ae7f9SEli Cohen 	WARN_ON(!rwsem_is_locked(&ndev->reslock));
27261c80cf03SJason Wang 
27271a86b377SEli Cohen 	if (!ndev->setup)
27281c80cf03SJason Wang 		return;
27291a86b377SEli Cohen 
2730f0417e72SEli Cohen 	mlx5_vdpa_remove_debugfs(ndev->debugfs);
2731f0417e72SEli Cohen 	ndev->debugfs = NULL;
2732baf2ad3fSEli Cohen 	teardown_steering(ndev);
27331a86b377SEli Cohen 	destroy_tir(ndev);
27341a86b377SEli Cohen 	destroy_rqt(ndev);
27351a86b377SEli Cohen 	teardown_virtqueues(ndev);
27361a86b377SEli Cohen 	ndev->setup = false;
27371a86b377SEli Cohen }
27381a86b377SEli Cohen 
2739e3aadf2eSEli Cohen static void clear_vqs_ready(struct mlx5_vdpa_net *ndev)
2740e3aadf2eSEli Cohen {
2741e3aadf2eSEli Cohen 	int i;
2742e3aadf2eSEli Cohen 
2743e3aadf2eSEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++)
2744e3aadf2eSEli Cohen 		ndev->vqs[i].ready = false;
2745ef12e4bfSEli Cohen 
2746ef12e4bfSEli Cohen 	ndev->mvdev.cvq.ready = false;
2747e3aadf2eSEli Cohen }
2748e3aadf2eSEli Cohen 
2749ace92524SEli Cohen static int setup_cvq_vring(struct mlx5_vdpa_dev *mvdev)
2750ace92524SEli Cohen {
2751ace92524SEli Cohen 	struct mlx5_control_vq *cvq = &mvdev->cvq;
2752ace92524SEli Cohen 	int err = 0;
2753ace92524SEli Cohen 
2754ace92524SEli Cohen 	if (mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))
2755ace92524SEli Cohen 		err = vringh_init_iotlb(&cvq->vring, mvdev->actual_features,
2756ace92524SEli Cohen 					MLX5_CVQ_MAX_ENT, false,
2757ace92524SEli Cohen 					(struct vring_desc *)(uintptr_t)cvq->desc_addr,
2758ace92524SEli Cohen 					(struct vring_avail *)(uintptr_t)cvq->driver_addr,
2759ace92524SEli Cohen 					(struct vring_used *)(uintptr_t)cvq->device_addr);
2760ace92524SEli Cohen 
2761ace92524SEli Cohen 	return err;
2762ace92524SEli Cohen }
2763ace92524SEli Cohen 
27641a86b377SEli Cohen static void mlx5_vdpa_set_status(struct vdpa_device *vdev, u8 status)
27651a86b377SEli Cohen {
27661a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
27671a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
27681a86b377SEli Cohen 	int err;
27691a86b377SEli Cohen 
27701a86b377SEli Cohen 	print_status(mvdev, status, true);
27711a86b377SEli Cohen 
2772759ae7f9SEli Cohen 	down_write(&ndev->reslock);
27731c80cf03SJason Wang 
27741a86b377SEli Cohen 	if ((status ^ ndev->mvdev.status) & VIRTIO_CONFIG_S_DRIVER_OK) {
27751a86b377SEli Cohen 		if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
2776ace92524SEli Cohen 			err = setup_cvq_vring(mvdev);
2777ace92524SEli Cohen 			if (err) {
2778ace92524SEli Cohen 				mlx5_vdpa_warn(mvdev, "failed to setup control VQ vring\n");
2779ace92524SEli Cohen 				goto err_setup;
2780ace92524SEli Cohen 			}
2781c384c240SEli Cohen 			register_link_notifier(ndev);
2782ae0428deSEli Cohen 			err = setup_driver(mvdev);
27831a86b377SEli Cohen 			if (err) {
27841a86b377SEli Cohen 				mlx5_vdpa_warn(mvdev, "failed to setup driver\n");
2785c384c240SEli Cohen 				goto err_driver;
27861a86b377SEli Cohen 			}
27871a86b377SEli Cohen 		} else {
27881a86b377SEli Cohen 			mlx5_vdpa_warn(mvdev, "did not expect DRIVER_OK to be cleared\n");
27891c80cf03SJason Wang 			goto err_clear;
27901a86b377SEli Cohen 		}
27911a86b377SEli Cohen 	}
27921a86b377SEli Cohen 
27931a86b377SEli Cohen 	ndev->mvdev.status = status;
2794759ae7f9SEli Cohen 	up_write(&ndev->reslock);
27951a86b377SEli Cohen 	return;
27961a86b377SEli Cohen 
2797c384c240SEli Cohen err_driver:
2798c384c240SEli Cohen 	unregister_link_notifier(ndev);
27991a86b377SEli Cohen err_setup:
28001a86b377SEli Cohen 	mlx5_vdpa_destroy_mr(&ndev->mvdev);
28011a86b377SEli Cohen 	ndev->mvdev.status |= VIRTIO_CONFIG_S_FAILED;
28021c80cf03SJason Wang err_clear:
2803759ae7f9SEli Cohen 	up_write(&ndev->reslock);
28041a86b377SEli Cohen }
28051a86b377SEli Cohen 
28068fcd20c3SEli Cohen static void init_group_to_asid_map(struct mlx5_vdpa_dev *mvdev)
28078fcd20c3SEli Cohen {
28088fcd20c3SEli Cohen 	int i;
28098fcd20c3SEli Cohen 
28108fcd20c3SEli Cohen 	/* default mapping all groups are mapped to asid 0 */
28118fcd20c3SEli Cohen 	for (i = 0; i < MLX5_VDPA_NUMVQ_GROUPS; i++)
28128fcd20c3SEli Cohen 		mvdev->group2asid[i] = 0;
28138fcd20c3SEli Cohen }
28148fcd20c3SEli Cohen 
28150686082dSXie Yongji static int mlx5_vdpa_reset(struct vdpa_device *vdev)
28160686082dSXie Yongji {
28170686082dSXie Yongji 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
28180686082dSXie Yongji 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
28190686082dSXie Yongji 
28200686082dSXie Yongji 	print_status(mvdev, 0, true);
28210686082dSXie Yongji 	mlx5_vdpa_info(mvdev, "performing device reset\n");
28221c80cf03SJason Wang 
2823759ae7f9SEli Cohen 	down_write(&ndev->reslock);
2824c384c240SEli Cohen 	unregister_link_notifier(ndev);
28250686082dSXie Yongji 	teardown_driver(ndev);
28260686082dSXie Yongji 	clear_vqs_ready(ndev);
28270686082dSXie Yongji 	mlx5_vdpa_destroy_mr(&ndev->mvdev);
28280686082dSXie Yongji 	ndev->mvdev.status = 0;
282909e65ee9SSi-Wei Liu 	ndev->mvdev.suspended = false;
2830b03fc43eSEli Cohen 	ndev->cur_num_vqs = 0;
28311892a3d4SEli Cohen 	ndev->mvdev.cvq.received_desc = 0;
28321892a3d4SEli Cohen 	ndev->mvdev.cvq.completed_desc = 0;
283375560522SEli Cohen 	memset(ndev->event_cbs, 0, sizeof(*ndev->event_cbs) * (mvdev->max_vqs + 1));
28340686082dSXie Yongji 	ndev->mvdev.actual_features = 0;
28358fcd20c3SEli Cohen 	init_group_to_asid_map(mvdev);
28360686082dSXie Yongji 	++mvdev->generation;
28378fcd20c3SEli Cohen 
28380686082dSXie Yongji 	if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
283938fc462fSEli Cohen 		if (mlx5_vdpa_create_mr(mvdev, NULL, 0))
28400686082dSXie Yongji 			mlx5_vdpa_warn(mvdev, "create MR failed\n");
28410686082dSXie Yongji 	}
2842759ae7f9SEli Cohen 	up_write(&ndev->reslock);
28430686082dSXie Yongji 
28440686082dSXie Yongji 	return 0;
28450686082dSXie Yongji }
28460686082dSXie Yongji 
2847442706f9SStefano Garzarella static size_t mlx5_vdpa_get_config_size(struct vdpa_device *vdev)
2848442706f9SStefano Garzarella {
2849442706f9SStefano Garzarella 	return sizeof(struct virtio_net_config);
2850442706f9SStefano Garzarella }
2851442706f9SStefano Garzarella 
28521a86b377SEli Cohen static void mlx5_vdpa_get_config(struct vdpa_device *vdev, unsigned int offset, void *buf,
28531a86b377SEli Cohen 				 unsigned int len)
28541a86b377SEli Cohen {
28551a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
28561a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
28571a86b377SEli Cohen 
2858dcfde163SStefano Garzarella 	if (offset + len <= sizeof(struct virtio_net_config))
28592874211fSDan Carpenter 		memcpy(buf, (u8 *)&ndev->config + offset, len);
28601a86b377SEli Cohen }
28611a86b377SEli Cohen 
28621a86b377SEli Cohen static void mlx5_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset, const void *buf,
28631a86b377SEli Cohen 				 unsigned int len)
28641a86b377SEli Cohen {
28651a86b377SEli Cohen 	/* not supported */
28661a86b377SEli Cohen }
28671a86b377SEli Cohen 
28681a86b377SEli Cohen static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev)
28691a86b377SEli Cohen {
28701a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
28711a86b377SEli Cohen 
28721a86b377SEli Cohen 	return mvdev->generation;
28731a86b377SEli Cohen }
28741a86b377SEli Cohen 
287538fc462fSEli Cohen static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
287638fc462fSEli Cohen 			unsigned int asid)
28778fcd20c3SEli Cohen {
28781a86b377SEli Cohen 	bool change_map;
28791a86b377SEli Cohen 	int err;
28801a86b377SEli Cohen 
288138fc462fSEli Cohen 	err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map, asid);
28821a86b377SEli Cohen 	if (err) {
28831a86b377SEli Cohen 		mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err);
28848fcd20c3SEli Cohen 		return err;
28851a86b377SEli Cohen 	}
28861a86b377SEli Cohen 
28871a86b377SEli Cohen 	if (change_map)
288838fc462fSEli Cohen 		err = mlx5_vdpa_change_map(mvdev, iotlb, asid);
28891a86b377SEli Cohen 
28908fcd20c3SEli Cohen 	return err;
28918fcd20c3SEli Cohen }
28928fcd20c3SEli Cohen 
28938fcd20c3SEli Cohen static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid,
28948fcd20c3SEli Cohen 			     struct vhost_iotlb *iotlb)
28958fcd20c3SEli Cohen {
28968fcd20c3SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
28978fcd20c3SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
289893e530d2SEli Cohen 	int err = -EINVAL;
28998fcd20c3SEli Cohen 
29008fcd20c3SEli Cohen 	down_write(&ndev->reslock);
290138fc462fSEli Cohen 	err = set_map_data(mvdev, iotlb, asid);
2902759ae7f9SEli Cohen 	up_write(&ndev->reslock);
29031c80cf03SJason Wang 	return err;
29041a86b377SEli Cohen }
29051a86b377SEli Cohen 
290636871fb9SJason Wang static struct device *mlx5_get_vq_dma_dev(struct vdpa_device *vdev, u16 idx)
290736871fb9SJason Wang {
290836871fb9SJason Wang 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
290936871fb9SJason Wang 
291036871fb9SJason Wang 	if (is_ctrl_vq_idx(mvdev, idx))
291136871fb9SJason Wang 		return &vdev->dev;
291236871fb9SJason Wang 
291336871fb9SJason Wang 	return mvdev->vdev.dma_dev;
291436871fb9SJason Wang }
291536871fb9SJason Wang 
2916bc9a2b3eSEli Cohen static void free_irqs(struct mlx5_vdpa_net *ndev)
2917bc9a2b3eSEli Cohen {
2918bc9a2b3eSEli Cohen 	struct mlx5_vdpa_irq_pool_entry *ent;
2919bc9a2b3eSEli Cohen 	int i;
2920bc9a2b3eSEli Cohen 
2921bc9a2b3eSEli Cohen 	if (!msix_mode_supported(&ndev->mvdev))
2922bc9a2b3eSEli Cohen 		return;
2923bc9a2b3eSEli Cohen 
2924bc9a2b3eSEli Cohen 	if (!ndev->irqp.entries)
2925bc9a2b3eSEli Cohen 		return;
2926bc9a2b3eSEli Cohen 
2927bc9a2b3eSEli Cohen 	for (i = ndev->irqp.num_ent - 1; i >= 0; i--) {
2928bc9a2b3eSEli Cohen 		ent = ndev->irqp.entries + i;
2929bc9a2b3eSEli Cohen 		if (ent->map.virq)
2930bc9a2b3eSEli Cohen 			pci_msix_free_irq(ndev->mvdev.mdev->pdev, ent->map);
2931bc9a2b3eSEli Cohen 	}
2932bc9a2b3eSEli Cohen 	kfree(ndev->irqp.entries);
2933bc9a2b3eSEli Cohen }
2934bc9a2b3eSEli Cohen 
29351a86b377SEli Cohen static void mlx5_vdpa_free(struct vdpa_device *vdev)
29361a86b377SEli Cohen {
29371a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
29387c9f131fSEli Cohen 	struct mlx5_core_dev *pfmdev;
29391a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev;
29401a86b377SEli Cohen 
29411a86b377SEli Cohen 	ndev = to_mlx5_vdpa_ndev(mvdev);
29421a86b377SEli Cohen 
29431a86b377SEli Cohen 	free_resources(ndev);
29446f5312f8SEli Cohen 	mlx5_vdpa_destroy_mr(mvdev);
29457c9f131fSEli Cohen 	if (!is_zero_ether_addr(ndev->config.mac)) {
29467c9f131fSEli Cohen 		pfmdev = pci_get_drvdata(pci_physfn(mvdev->mdev->pdev));
29477c9f131fSEli Cohen 		mlx5_mpfs_del_mac(pfmdev, ndev->config.mac);
29487c9f131fSEli Cohen 	}
29491a86b377SEli Cohen 	mlx5_vdpa_free_resources(&ndev->mvdev);
2950bc9a2b3eSEli Cohen 	free_irqs(ndev);
295175560522SEli Cohen 	kfree(ndev->event_cbs);
295275560522SEli Cohen 	kfree(ndev->vqs);
29531a86b377SEli Cohen }
29541a86b377SEli Cohen 
29551a86b377SEli Cohen static struct vdpa_notification_area mlx5_get_vq_notification(struct vdpa_device *vdev, u16 idx)
29561a86b377SEli Cohen {
2957b57c46cbSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
29581a86b377SEli Cohen 	struct vdpa_notification_area ret = {};
2959b57c46cbSEli Cohen 	struct mlx5_vdpa_net *ndev;
2960b57c46cbSEli Cohen 	phys_addr_t addr;
29611a86b377SEli Cohen 
29625262912eSEli Cohen 	if (!is_index_valid(mvdev, idx) || is_ctrl_vq_idx(mvdev, idx))
2963e4fc6650SEli Cohen 		return ret;
2964e4fc6650SEli Cohen 
2965b57c46cbSEli Cohen 	/* If SF BAR size is smaller than PAGE_SIZE, do not use direct
2966b57c46cbSEli Cohen 	 * notification to avoid the risk of mapping pages that contain BAR of more
2967b57c46cbSEli Cohen 	 * than one SF
2968b57c46cbSEli Cohen 	 */
2969b57c46cbSEli Cohen 	if (MLX5_CAP_GEN(mvdev->mdev, log_min_sf_size) + 12 < PAGE_SHIFT)
2970b57c46cbSEli Cohen 		return ret;
2971b57c46cbSEli Cohen 
2972b57c46cbSEli Cohen 	ndev = to_mlx5_vdpa_ndev(mvdev);
2973b57c46cbSEli Cohen 	addr = (phys_addr_t)ndev->mvdev.res.phys_kick_addr;
2974b57c46cbSEli Cohen 	ret.addr = addr;
2975b57c46cbSEli Cohen 	ret.size = PAGE_SIZE;
29761a86b377SEli Cohen 	return ret;
29771a86b377SEli Cohen }
29781a86b377SEli Cohen 
2979bc9a2b3eSEli Cohen static int mlx5_get_vq_irq(struct vdpa_device *vdev, u16 idx)
29801a86b377SEli Cohen {
2981bc9a2b3eSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
2982bc9a2b3eSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
2983bc9a2b3eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
2984bc9a2b3eSEli Cohen 
2985bc9a2b3eSEli Cohen 	if (!is_index_valid(mvdev, idx))
2986bc9a2b3eSEli Cohen 		return -EINVAL;
2987bc9a2b3eSEli Cohen 
2988bc9a2b3eSEli Cohen 	if (is_ctrl_vq_idx(mvdev, idx))
29891a86b377SEli Cohen 		return -EOPNOTSUPP;
2990bc9a2b3eSEli Cohen 
2991bc9a2b3eSEli Cohen 	mvq = &ndev->vqs[idx];
2992bc9a2b3eSEli Cohen 	if (!mvq->map.virq)
2993bc9a2b3eSEli Cohen 		return -EOPNOTSUPP;
2994bc9a2b3eSEli Cohen 
2995bc9a2b3eSEli Cohen 	return mvq->map.virq;
29961a86b377SEli Cohen }
29971a86b377SEli Cohen 
2998a64917bcSEli Cohen static u64 mlx5_vdpa_get_driver_features(struct vdpa_device *vdev)
2999a64917bcSEli Cohen {
3000a64917bcSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
3001a64917bcSEli Cohen 
3002a64917bcSEli Cohen 	return mvdev->actual_features;
3003a64917bcSEli Cohen }
3004a64917bcSEli Cohen 
30051892a3d4SEli Cohen static int counter_set_query(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq,
30061892a3d4SEli Cohen 			     u64 *received_desc, u64 *completed_desc)
30071892a3d4SEli Cohen {
30081892a3d4SEli Cohen 	u32 in[MLX5_ST_SZ_DW(query_virtio_q_counters_in)] = {};
30091892a3d4SEli Cohen 	u32 out[MLX5_ST_SZ_DW(query_virtio_q_counters_out)] = {};
30101892a3d4SEli Cohen 	void *cmd_hdr;
30111892a3d4SEli Cohen 	void *ctx;
30121892a3d4SEli Cohen 	int err;
30131892a3d4SEli Cohen 
30141892a3d4SEli Cohen 	if (!counters_supported(&ndev->mvdev))
30151892a3d4SEli Cohen 		return -EOPNOTSUPP;
30161892a3d4SEli Cohen 
30171892a3d4SEli Cohen 	if (mvq->fw_state != MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY)
30181892a3d4SEli Cohen 		return -EAGAIN;
30191892a3d4SEli Cohen 
30201892a3d4SEli Cohen 	cmd_hdr = MLX5_ADDR_OF(query_virtio_q_counters_in, in, hdr);
30211892a3d4SEli Cohen 
30221892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, opcode, MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
30231892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_type, MLX5_OBJ_TYPE_VIRTIO_Q_COUNTERS);
30241892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, uid, ndev->mvdev.res.uid);
30251892a3d4SEli Cohen 	MLX5_SET(general_obj_in_cmd_hdr, cmd_hdr, obj_id, mvq->counter_set_id);
30261892a3d4SEli Cohen 
30271892a3d4SEli Cohen 	err = mlx5_cmd_exec(ndev->mvdev.mdev, in, sizeof(in), out, sizeof(out));
30281892a3d4SEli Cohen 	if (err)
30291892a3d4SEli Cohen 		return err;
30301892a3d4SEli Cohen 
30311892a3d4SEli Cohen 	ctx = MLX5_ADDR_OF(query_virtio_q_counters_out, out, counters);
30321892a3d4SEli Cohen 	*received_desc = MLX5_GET64(virtio_q_counters, ctx, received_desc);
30331892a3d4SEli Cohen 	*completed_desc = MLX5_GET64(virtio_q_counters, ctx, completed_desc);
30341892a3d4SEli Cohen 	return 0;
30351892a3d4SEli Cohen }
30361892a3d4SEli Cohen 
30371892a3d4SEli Cohen static int mlx5_vdpa_get_vendor_vq_stats(struct vdpa_device *vdev, u16 idx,
30381892a3d4SEli Cohen 					 struct sk_buff *msg,
30391892a3d4SEli Cohen 					 struct netlink_ext_ack *extack)
30401892a3d4SEli Cohen {
30411892a3d4SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
30421892a3d4SEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
30431892a3d4SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
30441892a3d4SEli Cohen 	struct mlx5_control_vq *cvq;
30451892a3d4SEli Cohen 	u64 received_desc;
30461892a3d4SEli Cohen 	u64 completed_desc;
30471892a3d4SEli Cohen 	int err = 0;
30481892a3d4SEli Cohen 
3049759ae7f9SEli Cohen 	down_read(&ndev->reslock);
30501892a3d4SEli Cohen 	if (!is_index_valid(mvdev, idx)) {
30511892a3d4SEli Cohen 		NL_SET_ERR_MSG_MOD(extack, "virtqueue index is not valid");
30521892a3d4SEli Cohen 		err = -EINVAL;
30531892a3d4SEli Cohen 		goto out_err;
30541892a3d4SEli Cohen 	}
30551892a3d4SEli Cohen 
30561892a3d4SEli Cohen 	if (idx == ctrl_vq_idx(mvdev)) {
30571892a3d4SEli Cohen 		cvq = &mvdev->cvq;
30581892a3d4SEli Cohen 		received_desc = cvq->received_desc;
30591892a3d4SEli Cohen 		completed_desc = cvq->completed_desc;
30601892a3d4SEli Cohen 		goto out;
30611892a3d4SEli Cohen 	}
30621892a3d4SEli Cohen 
30631892a3d4SEli Cohen 	mvq = &ndev->vqs[idx];
30641892a3d4SEli Cohen 	err = counter_set_query(ndev, mvq, &received_desc, &completed_desc);
30651892a3d4SEli Cohen 	if (err) {
30661892a3d4SEli Cohen 		NL_SET_ERR_MSG_MOD(extack, "failed to query hardware");
30671892a3d4SEli Cohen 		goto out_err;
30681892a3d4SEli Cohen 	}
30691892a3d4SEli Cohen 
30701892a3d4SEli Cohen out:
30711892a3d4SEli Cohen 	err = -EMSGSIZE;
30721892a3d4SEli Cohen 	if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME, "received_desc"))
30731892a3d4SEli Cohen 		goto out_err;
30741892a3d4SEli Cohen 
30751892a3d4SEli Cohen 	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, received_desc,
30761892a3d4SEli Cohen 			      VDPA_ATTR_PAD))
30771892a3d4SEli Cohen 		goto out_err;
30781892a3d4SEli Cohen 
30791892a3d4SEli Cohen 	if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME, "completed_desc"))
30801892a3d4SEli Cohen 		goto out_err;
30811892a3d4SEli Cohen 
30821892a3d4SEli Cohen 	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, completed_desc,
30831892a3d4SEli Cohen 			      VDPA_ATTR_PAD))
30841892a3d4SEli Cohen 		goto out_err;
30851892a3d4SEli Cohen 
30861892a3d4SEli Cohen 	err = 0;
30871892a3d4SEli Cohen out_err:
3088759ae7f9SEli Cohen 	up_read(&ndev->reslock);
30891892a3d4SEli Cohen 	return err;
30901892a3d4SEli Cohen }
30911892a3d4SEli Cohen 
3092cae15c2eSEli Cohen static void mlx5_vdpa_cvq_suspend(struct mlx5_vdpa_dev *mvdev)
3093cae15c2eSEli Cohen {
3094cae15c2eSEli Cohen 	struct mlx5_control_vq *cvq;
3095cae15c2eSEli Cohen 
3096cae15c2eSEli Cohen 	if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ)))
3097cae15c2eSEli Cohen 		return;
3098cae15c2eSEli Cohen 
3099cae15c2eSEli Cohen 	cvq = &mvdev->cvq;
3100cae15c2eSEli Cohen 	cvq->ready = false;
3101cae15c2eSEli Cohen }
3102cae15c2eSEli Cohen 
3103cae15c2eSEli Cohen static int mlx5_vdpa_suspend(struct vdpa_device *vdev)
3104cae15c2eSEli Cohen {
3105cae15c2eSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
3106cae15c2eSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
3107cae15c2eSEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
3108cae15c2eSEli Cohen 	int i;
3109cae15c2eSEli Cohen 
311009e65ee9SSi-Wei Liu 	mlx5_vdpa_info(mvdev, "suspending device\n");
311109e65ee9SSi-Wei Liu 
3112cae15c2eSEli Cohen 	down_write(&ndev->reslock);
3113c384c240SEli Cohen 	unregister_link_notifier(ndev);
3114cae15c2eSEli Cohen 	for (i = 0; i < ndev->cur_num_vqs; i++) {
3115cae15c2eSEli Cohen 		mvq = &ndev->vqs[i];
3116cae15c2eSEli Cohen 		suspend_vq(ndev, mvq);
3117cae15c2eSEli Cohen 	}
3118cae15c2eSEli Cohen 	mlx5_vdpa_cvq_suspend(mvdev);
311909e65ee9SSi-Wei Liu 	mvdev->suspended = true;
3120cae15c2eSEli Cohen 	up_write(&ndev->reslock);
3121cae15c2eSEli Cohen 	return 0;
3122cae15c2eSEli Cohen }
3123cae15c2eSEli Cohen 
31248fcd20c3SEli Cohen static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group,
31258fcd20c3SEli Cohen 			       unsigned int asid)
31268fcd20c3SEli Cohen {
31278fcd20c3SEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
31288fcd20c3SEli Cohen 
31298fcd20c3SEli Cohen 	if (group >= MLX5_VDPA_NUMVQ_GROUPS)
31308fcd20c3SEli Cohen 		return -EINVAL;
31318fcd20c3SEli Cohen 
31328fcd20c3SEli Cohen 	mvdev->group2asid[group] = asid;
31338fcd20c3SEli Cohen 	return 0;
31348fcd20c3SEli Cohen }
31358fcd20c3SEli Cohen 
31361a86b377SEli Cohen static const struct vdpa_config_ops mlx5_vdpa_ops = {
31371a86b377SEli Cohen 	.set_vq_address = mlx5_vdpa_set_vq_address,
31381a86b377SEli Cohen 	.set_vq_num = mlx5_vdpa_set_vq_num,
31391a86b377SEli Cohen 	.kick_vq = mlx5_vdpa_kick_vq,
31401a86b377SEli Cohen 	.set_vq_cb = mlx5_vdpa_set_vq_cb,
31411a86b377SEli Cohen 	.set_vq_ready = mlx5_vdpa_set_vq_ready,
31421a86b377SEli Cohen 	.get_vq_ready = mlx5_vdpa_get_vq_ready,
31431a86b377SEli Cohen 	.set_vq_state = mlx5_vdpa_set_vq_state,
31441a86b377SEli Cohen 	.get_vq_state = mlx5_vdpa_get_vq_state,
31451892a3d4SEli Cohen 	.get_vendor_vq_stats = mlx5_vdpa_get_vendor_vq_stats,
31461a86b377SEli Cohen 	.get_vq_notification = mlx5_get_vq_notification,
31471a86b377SEli Cohen 	.get_vq_irq = mlx5_get_vq_irq,
31481a86b377SEli Cohen 	.get_vq_align = mlx5_vdpa_get_vq_align,
3149d4821902SGautam Dawar 	.get_vq_group = mlx5_vdpa_get_vq_group,
3150a64917bcSEli Cohen 	.get_device_features = mlx5_vdpa_get_device_features,
3151a64917bcSEli Cohen 	.set_driver_features = mlx5_vdpa_set_driver_features,
3152a64917bcSEli Cohen 	.get_driver_features = mlx5_vdpa_get_driver_features,
31531a86b377SEli Cohen 	.set_config_cb = mlx5_vdpa_set_config_cb,
31541a86b377SEli Cohen 	.get_vq_num_max = mlx5_vdpa_get_vq_num_max,
31551a86b377SEli Cohen 	.get_device_id = mlx5_vdpa_get_device_id,
31561a86b377SEli Cohen 	.get_vendor_id = mlx5_vdpa_get_vendor_id,
31571a86b377SEli Cohen 	.get_status = mlx5_vdpa_get_status,
31581a86b377SEli Cohen 	.set_status = mlx5_vdpa_set_status,
31590686082dSXie Yongji 	.reset = mlx5_vdpa_reset,
3160442706f9SStefano Garzarella 	.get_config_size = mlx5_vdpa_get_config_size,
31611a86b377SEli Cohen 	.get_config = mlx5_vdpa_get_config,
31621a86b377SEli Cohen 	.set_config = mlx5_vdpa_set_config,
31631a86b377SEli Cohen 	.get_generation = mlx5_vdpa_get_generation,
31641a86b377SEli Cohen 	.set_map = mlx5_vdpa_set_map,
31658fcd20c3SEli Cohen 	.set_group_asid = mlx5_set_group_asid,
316636871fb9SJason Wang 	.get_vq_dma_dev = mlx5_get_vq_dma_dev,
31671a86b377SEli Cohen 	.free = mlx5_vdpa_free,
3168cae15c2eSEli Cohen 	.suspend = mlx5_vdpa_suspend,
31691a86b377SEli Cohen };
31701a86b377SEli Cohen 
3171d084d996SSi-Wei Liu static int query_mtu(struct mlx5_core_dev *mdev, u16 *mtu)
3172d084d996SSi-Wei Liu {
3173d084d996SSi-Wei Liu 	u16 hw_mtu;
3174d084d996SSi-Wei Liu 	int err;
3175d084d996SSi-Wei Liu 
3176d084d996SSi-Wei Liu 	err = mlx5_query_nic_vport_mtu(mdev, &hw_mtu);
3177d084d996SSi-Wei Liu 	if (err)
3178d084d996SSi-Wei Liu 		return err;
3179d084d996SSi-Wei Liu 
3180d084d996SSi-Wei Liu 	*mtu = hw_mtu - MLX5V_ETH_HARD_MTU;
3181d084d996SSi-Wei Liu 	return 0;
3182d084d996SSi-Wei Liu }
3183d084d996SSi-Wei Liu 
31841a86b377SEli Cohen static int alloc_resources(struct mlx5_vdpa_net *ndev)
31851a86b377SEli Cohen {
31861a86b377SEli Cohen 	struct mlx5_vdpa_net_resources *res = &ndev->res;
31871a86b377SEli Cohen 	int err;
31881a86b377SEli Cohen 
31891a86b377SEli Cohen 	if (res->valid) {
31901a86b377SEli Cohen 		mlx5_vdpa_warn(&ndev->mvdev, "resources already allocated\n");
31911a86b377SEli Cohen 		return -EEXIST;
31921a86b377SEli Cohen 	}
31931a86b377SEli Cohen 
31941a86b377SEli Cohen 	err = mlx5_vdpa_alloc_transport_domain(&ndev->mvdev, &res->tdn);
31951a86b377SEli Cohen 	if (err)
31961a86b377SEli Cohen 		return err;
31971a86b377SEli Cohen 
31981a86b377SEli Cohen 	err = create_tis(ndev);
31991a86b377SEli Cohen 	if (err)
32001a86b377SEli Cohen 		goto err_tis;
32011a86b377SEli Cohen 
32021a86b377SEli Cohen 	res->valid = true;
32031a86b377SEli Cohen 
32041a86b377SEli Cohen 	return 0;
32051a86b377SEli Cohen 
32061a86b377SEli Cohen err_tis:
32071a86b377SEli Cohen 	mlx5_vdpa_dealloc_transport_domain(&ndev->mvdev, res->tdn);
32081a86b377SEli Cohen 	return err;
32091a86b377SEli Cohen }
32101a86b377SEli Cohen 
32111a86b377SEli Cohen static void free_resources(struct mlx5_vdpa_net *ndev)
32121a86b377SEli Cohen {
32131a86b377SEli Cohen 	struct mlx5_vdpa_net_resources *res = &ndev->res;
32141a86b377SEli Cohen 
32151a86b377SEli Cohen 	if (!res->valid)
32161a86b377SEli Cohen 		return;
32171a86b377SEli Cohen 
32181a86b377SEli Cohen 	destroy_tis(ndev);
32191a86b377SEli Cohen 	mlx5_vdpa_dealloc_transport_domain(&ndev->mvdev, res->tdn);
32201a86b377SEli Cohen 	res->valid = false;
32211a86b377SEli Cohen }
32221a86b377SEli Cohen 
32231a86b377SEli Cohen static void init_mvqs(struct mlx5_vdpa_net *ndev)
32241a86b377SEli Cohen {
32251a86b377SEli Cohen 	struct mlx5_vdpa_virtqueue *mvq;
32261a86b377SEli Cohen 	int i;
32271a86b377SEli Cohen 
3228acde3929SEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; ++i) {
32291a86b377SEli Cohen 		mvq = &ndev->vqs[i];
32301a86b377SEli Cohen 		memset(mvq, 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
32311a86b377SEli Cohen 		mvq->index = i;
32321a86b377SEli Cohen 		mvq->ndev = ndev;
32331a86b377SEli Cohen 		mvq->fwqp.fw = true;
3234cae15c2eSEli Cohen 		mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE;
32351a86b377SEli Cohen 	}
32361a86b377SEli Cohen 	for (; i < ndev->mvdev.max_vqs; i++) {
32371a86b377SEli Cohen 		mvq = &ndev->vqs[i];
32381a86b377SEli Cohen 		memset(mvq, 0, offsetof(struct mlx5_vdpa_virtqueue, ri));
32391a86b377SEli Cohen 		mvq->index = i;
32401a86b377SEli Cohen 		mvq->ndev = ndev;
32411a86b377SEli Cohen 	}
32421a86b377SEli Cohen }
32431a86b377SEli Cohen 
324458926c8aSEli Cohen struct mlx5_vdpa_mgmtdev {
324558926c8aSEli Cohen 	struct vdpa_mgmt_dev mgtdev;
324658926c8aSEli Cohen 	struct mlx5_adev *madev;
324758926c8aSEli Cohen 	struct mlx5_vdpa_net *ndev;
324858926c8aSEli Cohen };
324958926c8aSEli Cohen 
32501e00e821SEli Cohen static int config_func_mtu(struct mlx5_core_dev *mdev, u16 mtu)
32511e00e821SEli Cohen {
32521e00e821SEli Cohen 	int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
32531e00e821SEli Cohen 	void *in;
32541e00e821SEli Cohen 	int err;
32551e00e821SEli Cohen 
32561e00e821SEli Cohen 	in = kvzalloc(inlen, GFP_KERNEL);
32571e00e821SEli Cohen 	if (!in)
32581e00e821SEli Cohen 		return -ENOMEM;
32591e00e821SEli Cohen 
32601e00e821SEli Cohen 	MLX5_SET(modify_nic_vport_context_in, in, field_select.mtu, 1);
32611e00e821SEli Cohen 	MLX5_SET(modify_nic_vport_context_in, in, nic_vport_context.mtu,
32621e00e821SEli Cohen 		 mtu + MLX5V_ETH_HARD_MTU);
32631e00e821SEli Cohen 	MLX5_SET(modify_nic_vport_context_in, in, opcode,
32641e00e821SEli Cohen 		 MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
32651e00e821SEli Cohen 
32661e00e821SEli Cohen 	err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
32671e00e821SEli Cohen 
32681e00e821SEli Cohen 	kvfree(in);
32691e00e821SEli Cohen 	return err;
32701e00e821SEli Cohen }
32711e00e821SEli Cohen 
3272bc9a2b3eSEli Cohen static void allocate_irqs(struct mlx5_vdpa_net *ndev)
3273bc9a2b3eSEli Cohen {
3274bc9a2b3eSEli Cohen 	struct mlx5_vdpa_irq_pool_entry *ent;
3275bc9a2b3eSEli Cohen 	int i;
3276bc9a2b3eSEli Cohen 
3277bc9a2b3eSEli Cohen 	if (!msix_mode_supported(&ndev->mvdev))
3278bc9a2b3eSEli Cohen 		return;
3279bc9a2b3eSEli Cohen 
3280bc9a2b3eSEli Cohen 	if (!ndev->mvdev.mdev->pdev)
3281bc9a2b3eSEli Cohen 		return;
3282bc9a2b3eSEli Cohen 
3283bc9a2b3eSEli Cohen 	ndev->irqp.entries = kcalloc(ndev->mvdev.max_vqs, sizeof(*ndev->irqp.entries), GFP_KERNEL);
3284bc9a2b3eSEli Cohen 	if (!ndev->irqp.entries)
3285bc9a2b3eSEli Cohen 		return;
3286bc9a2b3eSEli Cohen 
3287bc9a2b3eSEli Cohen 
3288bc9a2b3eSEli Cohen 	for (i = 0; i < ndev->mvdev.max_vqs; i++) {
3289bc9a2b3eSEli Cohen 		ent = ndev->irqp.entries + i;
3290bc9a2b3eSEli Cohen 		snprintf(ent->name, MLX5_VDPA_IRQ_NAME_LEN, "%s-vq-%d",
3291bc9a2b3eSEli Cohen 			 dev_name(&ndev->mvdev.vdev.dev), i);
3292bc9a2b3eSEli Cohen 		ent->map = pci_msix_alloc_irq_at(ndev->mvdev.mdev->pdev, MSI_ANY_INDEX, NULL);
3293bc9a2b3eSEli Cohen 		if (!ent->map.virq)
3294bc9a2b3eSEli Cohen 			return;
3295bc9a2b3eSEli Cohen 
3296bc9a2b3eSEli Cohen 		ndev->irqp.num_ent++;
3297bc9a2b3eSEli Cohen 	}
3298bc9a2b3eSEli Cohen }
3299bc9a2b3eSEli Cohen 
3300d8ca2fa5SParav Pandit static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
3301d8ca2fa5SParav Pandit 			     const struct vdpa_dev_set_config *add_config)
33021a86b377SEli Cohen {
330358926c8aSEli Cohen 	struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
33041a86b377SEli Cohen 	struct virtio_net_config *config;
33057c9f131fSEli Cohen 	struct mlx5_core_dev *pfmdev;
33061a86b377SEli Cohen 	struct mlx5_vdpa_dev *mvdev;
33071a86b377SEli Cohen 	struct mlx5_vdpa_net *ndev;
330858926c8aSEli Cohen 	struct mlx5_core_dev *mdev;
3309deeacf35SSi-Wei Liu 	u64 device_features;
33101a86b377SEli Cohen 	u32 max_vqs;
3311246fd1caSEli Cohen 	u16 mtu;
33121a86b377SEli Cohen 	int err;
33131a86b377SEli Cohen 
331458926c8aSEli Cohen 	if (mgtdev->ndev)
331558926c8aSEli Cohen 		return -ENOSPC;
331658926c8aSEli Cohen 
331758926c8aSEli Cohen 	mdev = mgtdev->madev->mdev;
3318deeacf35SSi-Wei Liu 	device_features = mgtdev->mgtdev.supported_features;
3319deeacf35SSi-Wei Liu 	if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
3320deeacf35SSi-Wei Liu 		if (add_config->device_features & ~device_features) {
3321deeacf35SSi-Wei Liu 			dev_warn(mdev->device,
3322deeacf35SSi-Wei Liu 				 "The provisioned features 0x%llx are not supported by this device with features 0x%llx\n",
3323deeacf35SSi-Wei Liu 				 add_config->device_features, device_features);
3324deeacf35SSi-Wei Liu 			return -EINVAL;
3325deeacf35SSi-Wei Liu 		}
3326deeacf35SSi-Wei Liu 		device_features &= add_config->device_features;
3327791a1cb7SEli Cohen 	} else {
3328791a1cb7SEli Cohen 		device_features &= ~BIT_ULL(VIRTIO_NET_F_MRG_RXBUF);
3329deeacf35SSi-Wei Liu 	}
3330deeacf35SSi-Wei Liu 	if (!(device_features & BIT_ULL(VIRTIO_F_VERSION_1) &&
3331deeacf35SSi-Wei Liu 	      device_features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM))) {
3332deeacf35SSi-Wei Liu 		dev_warn(mdev->device,
3333deeacf35SSi-Wei Liu 			 "Must provision minimum features 0x%llx for this device",
3334deeacf35SSi-Wei Liu 			 BIT_ULL(VIRTIO_F_VERSION_1) | BIT_ULL(VIRTIO_F_ACCESS_PLATFORM));
3335deeacf35SSi-Wei Liu 		return -EOPNOTSUPP;
3336deeacf35SSi-Wei Liu 	}
3337deeacf35SSi-Wei Liu 
3338879753c8SEli Cohen 	if (!(MLX5_CAP_DEV_VDPA_EMULATION(mdev, virtio_queue_type) &
3339879753c8SEli Cohen 	    MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT)) {
3340879753c8SEli Cohen 		dev_warn(mdev->device, "missing support for split virtqueues\n");
3341879753c8SEli Cohen 		return -EOPNOTSUPP;
3342879753c8SEli Cohen 	}
3343879753c8SEli Cohen 
3344acde3929SEli Cohen 	max_vqs = min_t(int, MLX5_CAP_DEV_VDPA_EMULATION(mdev, max_num_virtio_queues),
3345acde3929SEli Cohen 			1 << MLX5_CAP_GEN(mdev, log_max_rqt_size));
334675560522SEli Cohen 	if (max_vqs < 2) {
334775560522SEli Cohen 		dev_warn(mdev->device,
334875560522SEli Cohen 			 "%d virtqueues are supported. At least 2 are required\n",
334975560522SEli Cohen 			 max_vqs);
335075560522SEli Cohen 		return -EAGAIN;
335175560522SEli Cohen 	}
335275560522SEli Cohen 
335375560522SEli Cohen 	if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP)) {
335475560522SEli Cohen 		if (add_config->net.max_vq_pairs > max_vqs / 2)
335575560522SEli Cohen 			return -EINVAL;
335675560522SEli Cohen 		max_vqs = min_t(u32, max_vqs, 2 * add_config->net.max_vq_pairs);
335775560522SEli Cohen 	} else {
335875560522SEli Cohen 		max_vqs = 2;
335975560522SEli Cohen 	}
33601a86b377SEli Cohen 
33611a86b377SEli Cohen 	ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops,
33628fcd20c3SEli Cohen 				 MLX5_VDPA_NUMVQ_GROUPS, MLX5_VDPA_NUM_AS, name, false);
33631a86b377SEli Cohen 	if (IS_ERR(ndev))
336474c9729dSLeon Romanovsky 		return PTR_ERR(ndev);
33651a86b377SEli Cohen 
33661a86b377SEli Cohen 	ndev->mvdev.max_vqs = max_vqs;
33671a86b377SEli Cohen 	mvdev = &ndev->mvdev;
33681a86b377SEli Cohen 	mvdev->mdev = mdev;
336975560522SEli Cohen 
337075560522SEli Cohen 	ndev->vqs = kcalloc(max_vqs, sizeof(*ndev->vqs), GFP_KERNEL);
337175560522SEli Cohen 	ndev->event_cbs = kcalloc(max_vqs + 1, sizeof(*ndev->event_cbs), GFP_KERNEL);
337275560522SEli Cohen 	if (!ndev->vqs || !ndev->event_cbs) {
337375560522SEli Cohen 		err = -ENOMEM;
337475560522SEli Cohen 		goto err_alloc;
337575560522SEli Cohen 	}
337675560522SEli Cohen 
33771a86b377SEli Cohen 	init_mvqs(ndev);
3378bc9a2b3eSEli Cohen 	allocate_irqs(ndev);
3379759ae7f9SEli Cohen 	init_rwsem(&ndev->reslock);
33801a86b377SEli Cohen 	config = &ndev->config;
33811e00e821SEli Cohen 
33821e00e821SEli Cohen 	if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU)) {
33831e00e821SEli Cohen 		err = config_func_mtu(mdev, add_config->net.mtu);
33841e00e821SEli Cohen 		if (err)
3385759ae7f9SEli Cohen 			goto err_alloc;
33861e00e821SEli Cohen 	}
33871e00e821SEli Cohen 
3388deeacf35SSi-Wei Liu 	if (device_features & BIT_ULL(VIRTIO_NET_F_MTU)) {
3389246fd1caSEli Cohen 		err = query_mtu(mdev, &mtu);
33901a86b377SEli Cohen 		if (err)
3391759ae7f9SEli Cohen 			goto err_alloc;
33921a86b377SEli Cohen 
3393246fd1caSEli Cohen 		ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, mtu);
3394033779a7SSi-Wei Liu 	}
33951a86b377SEli Cohen 
3396deeacf35SSi-Wei Liu 	if (device_features & BIT_ULL(VIRTIO_NET_F_STATUS)) {
3397edf747afSEli Cohen 		if (get_link_state(mvdev))
3398edf747afSEli Cohen 			ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
3399edf747afSEli Cohen 		else
3400edf747afSEli Cohen 			ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
3401033779a7SSi-Wei Liu 	}
3402edf747afSEli Cohen 
3403a007d940SEli Cohen 	if (add_config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
3404a007d940SEli Cohen 		memcpy(ndev->config.mac, add_config->net.mac, ETH_ALEN);
3405deeacf35SSi-Wei Liu 	/* No bother setting mac address in config if not going to provision _F_MAC */
3406deeacf35SSi-Wei Liu 	} else if ((add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) == 0 ||
3407deeacf35SSi-Wei Liu 		   device_features & BIT_ULL(VIRTIO_NET_F_MAC)) {
34081a86b377SEli Cohen 		err = mlx5_query_nic_vport_mac_address(mdev, 0, 0, config->mac);
34091a86b377SEli Cohen 		if (err)
3410759ae7f9SEli Cohen 			goto err_alloc;
3411a007d940SEli Cohen 	}
34121a86b377SEli Cohen 
34137c9f131fSEli Cohen 	if (!is_zero_ether_addr(config->mac)) {
34147c9f131fSEli Cohen 		pfmdev = pci_get_drvdata(pci_physfn(mdev->pdev));
34157c9f131fSEli Cohen 		err = mlx5_mpfs_add_mac(pfmdev, config->mac);
34167c9f131fSEli Cohen 		if (err)
3417759ae7f9SEli Cohen 			goto err_alloc;
3418deeacf35SSi-Wei Liu 	} else if ((add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) == 0) {
3419deeacf35SSi-Wei Liu 		/*
3420deeacf35SSi-Wei Liu 		 * We used to clear _F_MAC feature bit if seeing
3421deeacf35SSi-Wei Liu 		 * zero mac address when device features are not
3422deeacf35SSi-Wei Liu 		 * specifically provisioned. Keep the behaviour
3423deeacf35SSi-Wei Liu 		 * so old scripts do not break.
3424deeacf35SSi-Wei Liu 		 */
3425deeacf35SSi-Wei Liu 		device_features &= ~BIT_ULL(VIRTIO_NET_F_MAC);
3426deeacf35SSi-Wei Liu 	} else if (device_features & BIT_ULL(VIRTIO_NET_F_MAC)) {
3427deeacf35SSi-Wei Liu 		/* Don't provision zero mac address for _F_MAC */
3428deeacf35SSi-Wei Liu 		mlx5_vdpa_warn(&ndev->mvdev,
3429deeacf35SSi-Wei Liu 			       "No mac address provisioned?\n");
3430deeacf35SSi-Wei Liu 		err = -EINVAL;
3431deeacf35SSi-Wei Liu 		goto err_alloc;
34327c9f131fSEli Cohen 	}
34337c9f131fSEli Cohen 
3434deeacf35SSi-Wei Liu 	if (device_features & BIT_ULL(VIRTIO_NET_F_MQ))
3435acde3929SEli Cohen 		config->max_virtqueue_pairs = cpu_to_mlx5vdpa16(mvdev, max_vqs / 2);
3436deeacf35SSi-Wei Liu 
3437deeacf35SSi-Wei Liu 	ndev->mvdev.mlx_features = device_features;
34387d23dcdfSEli Cohen 	mvdev->vdev.dma_dev = &mdev->pdev->dev;
34391a86b377SEli Cohen 	err = mlx5_vdpa_alloc_resources(&ndev->mvdev);
34401a86b377SEli Cohen 	if (err)
34417c9f131fSEli Cohen 		goto err_mpfs;
34421a86b377SEli Cohen 
34436f5312f8SEli Cohen 	if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
344438fc462fSEli Cohen 		err = mlx5_vdpa_create_mr(mvdev, NULL, 0);
34451a86b377SEli Cohen 		if (err)
34461a86b377SEli Cohen 			goto err_res;
34476f5312f8SEli Cohen 	}
34486f5312f8SEli Cohen 
34496f5312f8SEli Cohen 	err = alloc_resources(ndev);
34506f5312f8SEli Cohen 	if (err)
34516f5312f8SEli Cohen 		goto err_mr;
34521a86b377SEli Cohen 
345355ebf0d6SJason Wang 	ndev->cvq_ent.mvdev = mvdev;
345455ebf0d6SJason Wang 	INIT_WORK(&ndev->cvq_ent.work, mlx5_cvq_kick_handler);
3455218bdd20SEli Cohen 	mvdev->wq = create_singlethread_workqueue("mlx5_vdpa_wq");
34565262912eSEli Cohen 	if (!mvdev->wq) {
34575262912eSEli Cohen 		err = -ENOMEM;
34585262912eSEli Cohen 		goto err_res2;
34595262912eSEli Cohen 	}
34605262912eSEli Cohen 
346158926c8aSEli Cohen 	mvdev->vdev.mdev = &mgtdev->mgtdev;
3462acde3929SEli Cohen 	err = _vdpa_register_device(&mvdev->vdev, max_vqs + 1);
34631a86b377SEli Cohen 	if (err)
34641a86b377SEli Cohen 		goto err_reg;
34651a86b377SEli Cohen 
346658926c8aSEli Cohen 	mgtdev->ndev = ndev;
346774c9729dSLeon Romanovsky 	return 0;
34681a86b377SEli Cohen 
34691a86b377SEli Cohen err_reg:
34705262912eSEli Cohen 	destroy_workqueue(mvdev->wq);
34715262912eSEli Cohen err_res2:
34721a86b377SEli Cohen 	free_resources(ndev);
34736f5312f8SEli Cohen err_mr:
34746f5312f8SEli Cohen 	mlx5_vdpa_destroy_mr(mvdev);
34751a86b377SEli Cohen err_res:
34761a86b377SEli Cohen 	mlx5_vdpa_free_resources(&ndev->mvdev);
34777c9f131fSEli Cohen err_mpfs:
34787c9f131fSEli Cohen 	if (!is_zero_ether_addr(config->mac))
34797c9f131fSEli Cohen 		mlx5_mpfs_del_mac(pfmdev, config->mac);
348075560522SEli Cohen err_alloc:
34811a86b377SEli Cohen 	put_device(&mvdev->vdev.dev);
348274c9729dSLeon Romanovsky 	return err;
34831a86b377SEli Cohen }
34841a86b377SEli Cohen 
348558926c8aSEli Cohen static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *dev)
348658926c8aSEli Cohen {
348758926c8aSEli Cohen 	struct mlx5_vdpa_mgmtdev *mgtdev = container_of(v_mdev, struct mlx5_vdpa_mgmtdev, mgtdev);
34885262912eSEli Cohen 	struct mlx5_vdpa_dev *mvdev = to_mvdev(dev);
3489edf747afSEli Cohen 	struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
3490ad6dc1daSEli Cohen 	struct workqueue_struct *wq;
349158926c8aSEli Cohen 
349229422100SEli Cohen 	mlx5_vdpa_remove_debugfs(ndev->debugfs);
349329422100SEli Cohen 	ndev->debugfs = NULL;
3494c384c240SEli Cohen 	unregister_link_notifier(ndev);
349573790bdfSDragos Tatulea 	_vdpa_unregister_device(dev);
3496ad6dc1daSEli Cohen 	wq = mvdev->wq;
3497ad6dc1daSEli Cohen 	mvdev->wq = NULL;
3498ad6dc1daSEli Cohen 	destroy_workqueue(wq);
349958926c8aSEli Cohen 	mgtdev->ndev = NULL;
350058926c8aSEli Cohen }
350158926c8aSEli Cohen 
350258926c8aSEli Cohen static const struct vdpa_mgmtdev_ops mdev_ops = {
350358926c8aSEli Cohen 	.dev_add = mlx5_vdpa_dev_add,
350458926c8aSEli Cohen 	.dev_del = mlx5_vdpa_dev_del,
350558926c8aSEli Cohen };
350658926c8aSEli Cohen 
350758926c8aSEli Cohen static struct virtio_device_id id_table[] = {
350858926c8aSEli Cohen 	{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
350958926c8aSEli Cohen 	{ 0 },
351058926c8aSEli Cohen };
351158926c8aSEli Cohen 
351258926c8aSEli Cohen static int mlx5v_probe(struct auxiliary_device *adev,
351358926c8aSEli Cohen 		       const struct auxiliary_device_id *id)
351458926c8aSEli Cohen 
351558926c8aSEli Cohen {
351658926c8aSEli Cohen 	struct mlx5_adev *madev = container_of(adev, struct mlx5_adev, adev);
351758926c8aSEli Cohen 	struct mlx5_core_dev *mdev = madev->mdev;
351858926c8aSEli Cohen 	struct mlx5_vdpa_mgmtdev *mgtdev;
351958926c8aSEli Cohen 	int err;
352058926c8aSEli Cohen 
352158926c8aSEli Cohen 	mgtdev = kzalloc(sizeof(*mgtdev), GFP_KERNEL);
352258926c8aSEli Cohen 	if (!mgtdev)
352358926c8aSEli Cohen 		return -ENOMEM;
352458926c8aSEli Cohen 
352558926c8aSEli Cohen 	mgtdev->mgtdev.ops = &mdev_ops;
352658926c8aSEli Cohen 	mgtdev->mgtdev.device = mdev->device;
352758926c8aSEli Cohen 	mgtdev->mgtdev.id_table = id_table;
352875560522SEli Cohen 	mgtdev->mgtdev.config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR) |
35291e00e821SEli Cohen 					  BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP) |
3530deeacf35SSi-Wei Liu 					  BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU) |
3531deeacf35SSi-Wei Liu 					  BIT_ULL(VDPA_ATTR_DEV_FEATURES);
353279de65edSEli Cohen 	mgtdev->mgtdev.max_supported_vqs =
353379de65edSEli Cohen 		MLX5_CAP_DEV_VDPA_EMULATION(mdev, max_num_virtio_queues) + 1;
353479de65edSEli Cohen 	mgtdev->mgtdev.supported_features = get_supported_features(mdev);
353558926c8aSEli Cohen 	mgtdev->madev = madev;
353658926c8aSEli Cohen 
353758926c8aSEli Cohen 	err = vdpa_mgmtdev_register(&mgtdev->mgtdev);
353858926c8aSEli Cohen 	if (err)
353958926c8aSEli Cohen 		goto reg_err;
354058926c8aSEli Cohen 
354145e3a279SDavid E. Box 	auxiliary_set_drvdata(adev, mgtdev);
354258926c8aSEli Cohen 
354358926c8aSEli Cohen 	return 0;
354458926c8aSEli Cohen 
354558926c8aSEli Cohen reg_err:
354658926c8aSEli Cohen 	kfree(mgtdev);
354758926c8aSEli Cohen 	return err;
354858926c8aSEli Cohen }
354958926c8aSEli Cohen 
355074c9729dSLeon Romanovsky static void mlx5v_remove(struct auxiliary_device *adev)
35511a86b377SEli Cohen {
355258926c8aSEli Cohen 	struct mlx5_vdpa_mgmtdev *mgtdev;
355374c9729dSLeon Romanovsky 
355445e3a279SDavid E. Box 	mgtdev = auxiliary_get_drvdata(adev);
355558926c8aSEli Cohen 	vdpa_mgmtdev_unregister(&mgtdev->mgtdev);
355658926c8aSEli Cohen 	kfree(mgtdev);
35571a86b377SEli Cohen }
355874c9729dSLeon Romanovsky 
3559bc9a2b3eSEli Cohen static void mlx5v_shutdown(struct auxiliary_device *auxdev)
3560bc9a2b3eSEli Cohen {
3561bc9a2b3eSEli Cohen 	struct mlx5_vdpa_mgmtdev *mgtdev;
3562bc9a2b3eSEli Cohen 	struct mlx5_vdpa_net *ndev;
3563bc9a2b3eSEli Cohen 
3564bc9a2b3eSEli Cohen 	mgtdev = auxiliary_get_drvdata(auxdev);
3565bc9a2b3eSEli Cohen 	ndev = mgtdev->ndev;
3566bc9a2b3eSEli Cohen 
3567bc9a2b3eSEli Cohen 	free_irqs(ndev);
3568bc9a2b3eSEli Cohen }
3569bc9a2b3eSEli Cohen 
357074c9729dSLeon Romanovsky static const struct auxiliary_device_id mlx5v_id_table[] = {
357174c9729dSLeon Romanovsky 	{ .name = MLX5_ADEV_NAME ".vnet", },
357274c9729dSLeon Romanovsky 	{},
357374c9729dSLeon Romanovsky };
357474c9729dSLeon Romanovsky 
357574c9729dSLeon Romanovsky MODULE_DEVICE_TABLE(auxiliary, mlx5v_id_table);
357674c9729dSLeon Romanovsky 
357774c9729dSLeon Romanovsky static struct auxiliary_driver mlx5v_driver = {
357874c9729dSLeon Romanovsky 	.name = "vnet",
357974c9729dSLeon Romanovsky 	.probe = mlx5v_probe,
358074c9729dSLeon Romanovsky 	.remove = mlx5v_remove,
3581bc9a2b3eSEli Cohen 	.shutdown = mlx5v_shutdown,
358274c9729dSLeon Romanovsky 	.id_table = mlx5v_id_table,
358374c9729dSLeon Romanovsky };
358474c9729dSLeon Romanovsky 
358574c9729dSLeon Romanovsky module_auxiliary_driver(mlx5v_driver);
3586