1db1e8bb6SMax Gurtovoy // SPDX-License-Identifier: GPL-2.0-only
2db1e8bb6SMax Gurtovoy /*
3db1e8bb6SMax Gurtovoy  * VDPA simulator for networking device.
4db1e8bb6SMax Gurtovoy  *
5db1e8bb6SMax Gurtovoy  * Copyright (c) 2020, Red Hat Inc. All rights reserved.
6db1e8bb6SMax Gurtovoy  *     Author: Jason Wang <jasowang@redhat.com>
7db1e8bb6SMax Gurtovoy  *
8db1e8bb6SMax Gurtovoy  */
9db1e8bb6SMax Gurtovoy 
10db1e8bb6SMax Gurtovoy #include <linux/init.h>
11db1e8bb6SMax Gurtovoy #include <linux/module.h>
12db1e8bb6SMax Gurtovoy #include <linux/device.h>
13db1e8bb6SMax Gurtovoy #include <linux/kernel.h>
14db1e8bb6SMax Gurtovoy #include <linux/etherdevice.h>
15db1e8bb6SMax Gurtovoy #include <linux/vringh.h>
16db1e8bb6SMax Gurtovoy #include <linux/vdpa.h>
170899774cSJason Wang #include <net/netlink.h>
18db1e8bb6SMax Gurtovoy #include <uapi/linux/virtio_net.h>
191138b981SParav Pandit #include <uapi/linux/vdpa.h>
20db1e8bb6SMax Gurtovoy 
21db1e8bb6SMax Gurtovoy #include "vdpa_sim.h"
22db1e8bb6SMax Gurtovoy 
23db1e8bb6SMax Gurtovoy #define DRV_VERSION  "0.1"
24db1e8bb6SMax Gurtovoy #define DRV_AUTHOR   "Jason Wang <jasowang@redhat.com>"
25db1e8bb6SMax Gurtovoy #define DRV_DESC     "vDPA Device Simulator for networking device"
26db1e8bb6SMax Gurtovoy #define DRV_LICENSE  "GPL v2"
27db1e8bb6SMax Gurtovoy 
28db1e8bb6SMax Gurtovoy #define VDPASIM_NET_FEATURES	(VDPASIM_FEATURES | \
2905b69762SGautam Dawar 				 (1ULL << VIRTIO_NET_F_MAC) | \
30d8b3832aSEugenio Pérez 				 (1ULL << VIRTIO_NET_F_STATUS) | \
31bda324fdSGautam Dawar 				 (1ULL << VIRTIO_NET_F_MTU) | \
32bda324fdSGautam Dawar 				 (1ULL << VIRTIO_NET_F_CTRL_VQ) | \
33bda324fdSGautam Dawar 				 (1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR))
34db1e8bb6SMax Gurtovoy 
35bda324fdSGautam Dawar /* 3 virtqueues, 2 address spaces, 2 virtqueue groups */
36bda324fdSGautam Dawar #define VDPASIM_NET_VQ_NUM	3
37bda324fdSGautam Dawar #define VDPASIM_NET_AS_NUM	2
38bda324fdSGautam Dawar #define VDPASIM_NET_GROUP_NUM	2
39db1e8bb6SMax Gurtovoy 
400899774cSJason Wang struct vdpasim_dataq_stats {
410899774cSJason Wang 	struct u64_stats_sync syncp;
420899774cSJason Wang 	u64 pkts;
430899774cSJason Wang 	u64 bytes;
440899774cSJason Wang 	u64 drops;
450899774cSJason Wang 	u64 errors;
460899774cSJason Wang 	u64 overruns;
470899774cSJason Wang };
480899774cSJason Wang 
490899774cSJason Wang struct vdpasim_cq_stats {
500899774cSJason Wang 	struct u64_stats_sync syncp;
510899774cSJason Wang 	u64 requests;
520899774cSJason Wang 	u64 successes;
530899774cSJason Wang 	u64 errors;
540899774cSJason Wang };
550899774cSJason Wang 
560899774cSJason Wang struct vdpasim_net{
570899774cSJason Wang 	struct vdpasim vdpasim;
580899774cSJason Wang 	struct vdpasim_dataq_stats tx_stats;
590899774cSJason Wang 	struct vdpasim_dataq_stats rx_stats;
600899774cSJason Wang 	struct vdpasim_cq_stats cq_stats;
61*112f23cdSStefano Garzarella 	void *buffer;
620899774cSJason Wang };
630899774cSJason Wang 
sim_to_net(struct vdpasim * vdpasim)640899774cSJason Wang static struct vdpasim_net *sim_to_net(struct vdpasim *vdpasim)
650899774cSJason Wang {
660899774cSJason Wang 	return container_of(vdpasim, struct vdpasim_net, vdpasim);
670899774cSJason Wang }
680899774cSJason Wang 
vdpasim_net_complete(struct vdpasim_virtqueue * vq,size_t len)69ec103d98SGautam Dawar static void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len)
70ec103d98SGautam Dawar {
71ec103d98SGautam Dawar 	/* Make sure data is wrote before advancing index */
72ec103d98SGautam Dawar 	smp_wmb();
73ec103d98SGautam Dawar 
74ec103d98SGautam Dawar 	vringh_complete_iotlb(&vq->vring, vq->head, len);
75ec103d98SGautam Dawar 
76ec103d98SGautam Dawar 	/* Make sure used is visible before rasing the interrupt. */
77ec103d98SGautam Dawar 	smp_wmb();
78ec103d98SGautam Dawar 
79ec103d98SGautam Dawar 	local_bh_disable();
80ec103d98SGautam Dawar 	if (vringh_need_notify_iotlb(&vq->vring) > 0)
81ec103d98SGautam Dawar 		vringh_notify(&vq->vring);
82ec103d98SGautam Dawar 	local_bh_enable();
83ec103d98SGautam Dawar }
84ec103d98SGautam Dawar 
receive_filter(struct vdpasim * vdpasim,size_t len)85cfe22689SGautam Dawar static bool receive_filter(struct vdpasim *vdpasim, size_t len)
86cfe22689SGautam Dawar {
87cfe22689SGautam Dawar 	bool modern = vdpasim->features & (1ULL << VIRTIO_F_VERSION_1);
88cfe22689SGautam Dawar 	size_t hdr_len = modern ? sizeof(struct virtio_net_hdr_v1) :
89cfe22689SGautam Dawar 				  sizeof(struct virtio_net_hdr);
90cfe22689SGautam Dawar 	struct virtio_net_config *vio_config = vdpasim->config;
91*112f23cdSStefano Garzarella 	struct vdpasim_net *net = sim_to_net(vdpasim);
92cfe22689SGautam Dawar 
93cfe22689SGautam Dawar 	if (len < ETH_ALEN + hdr_len)
94cfe22689SGautam Dawar 		return false;
95cfe22689SGautam Dawar 
96*112f23cdSStefano Garzarella 	if (is_broadcast_ether_addr(net->buffer + hdr_len) ||
97*112f23cdSStefano Garzarella 	    is_multicast_ether_addr(net->buffer + hdr_len))
9872455a11SCindy Lu 		return true;
99*112f23cdSStefano Garzarella 	if (!strncmp(net->buffer + hdr_len, vio_config->mac, ETH_ALEN))
100cfe22689SGautam Dawar 		return true;
101cfe22689SGautam Dawar 
102cfe22689SGautam Dawar 	return false;
103cfe22689SGautam Dawar }
104cfe22689SGautam Dawar 
vdpasim_handle_ctrl_mac(struct vdpasim * vdpasim,u8 cmd)105bda324fdSGautam Dawar static virtio_net_ctrl_ack vdpasim_handle_ctrl_mac(struct vdpasim *vdpasim,
106bda324fdSGautam Dawar 						   u8 cmd)
107bda324fdSGautam Dawar {
108bda324fdSGautam Dawar 	struct virtio_net_config *vio_config = vdpasim->config;
109bda324fdSGautam Dawar 	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
110bda324fdSGautam Dawar 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
111bda324fdSGautam Dawar 	size_t read;
112bda324fdSGautam Dawar 
113bda324fdSGautam Dawar 	switch (cmd) {
114bda324fdSGautam Dawar 	case VIRTIO_NET_CTRL_MAC_ADDR_SET:
115bda324fdSGautam Dawar 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov,
116bda324fdSGautam Dawar 					     vio_config->mac, ETH_ALEN);
117bda324fdSGautam Dawar 		if (read == ETH_ALEN)
118bda324fdSGautam Dawar 			status = VIRTIO_NET_OK;
119bda324fdSGautam Dawar 		break;
120bda324fdSGautam Dawar 	default:
121bda324fdSGautam Dawar 		break;
122bda324fdSGautam Dawar 	}
123bda324fdSGautam Dawar 
124bda324fdSGautam Dawar 	return status;
125bda324fdSGautam Dawar }
126bda324fdSGautam Dawar 
vdpasim_handle_cvq(struct vdpasim * vdpasim)127bda324fdSGautam Dawar static void vdpasim_handle_cvq(struct vdpasim *vdpasim)
128bda324fdSGautam Dawar {
129bda324fdSGautam Dawar 	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
1300899774cSJason Wang 	struct vdpasim_net *net = sim_to_net(vdpasim);
131bda324fdSGautam Dawar 	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
132bda324fdSGautam Dawar 	struct virtio_net_ctrl_hdr ctrl;
133bda324fdSGautam Dawar 	size_t read, write;
1340899774cSJason Wang 	u64 requests = 0, errors = 0, successes = 0;
135bda324fdSGautam Dawar 	int err;
136bda324fdSGautam Dawar 
137bda324fdSGautam Dawar 	if (!(vdpasim->features & (1ULL << VIRTIO_NET_F_CTRL_VQ)))
138bda324fdSGautam Dawar 		return;
139bda324fdSGautam Dawar 
140bda324fdSGautam Dawar 	if (!cvq->ready)
141bda324fdSGautam Dawar 		return;
142bda324fdSGautam Dawar 
143bda324fdSGautam Dawar 	while (true) {
144bda324fdSGautam Dawar 		err = vringh_getdesc_iotlb(&cvq->vring, &cvq->in_iov,
145bda324fdSGautam Dawar 					   &cvq->out_iov,
146bda324fdSGautam Dawar 					   &cvq->head, GFP_ATOMIC);
147bda324fdSGautam Dawar 		if (err <= 0)
148bda324fdSGautam Dawar 			break;
149bda324fdSGautam Dawar 
1500899774cSJason Wang 		++requests;
151bda324fdSGautam Dawar 		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov, &ctrl,
152bda324fdSGautam Dawar 					     sizeof(ctrl));
1530899774cSJason Wang 		if (read != sizeof(ctrl)) {
1540899774cSJason Wang 			++errors;
155bda324fdSGautam Dawar 			break;
1560899774cSJason Wang 		}
157bda324fdSGautam Dawar 
158bda324fdSGautam Dawar 		switch (ctrl.class) {
159bda324fdSGautam Dawar 		case VIRTIO_NET_CTRL_MAC:
160bda324fdSGautam Dawar 			status = vdpasim_handle_ctrl_mac(vdpasim, ctrl.cmd);
161bda324fdSGautam Dawar 			break;
162bda324fdSGautam Dawar 		default:
163bda324fdSGautam Dawar 			break;
164bda324fdSGautam Dawar 		}
165bda324fdSGautam Dawar 
1660899774cSJason Wang 		if (status == VIRTIO_NET_OK)
1670899774cSJason Wang 			++successes;
1680899774cSJason Wang 		else
1690899774cSJason Wang 			++errors;
1700899774cSJason Wang 
171bda324fdSGautam Dawar 		/* Make sure data is wrote before advancing index */
172bda324fdSGautam Dawar 		smp_wmb();
173bda324fdSGautam Dawar 
174bda324fdSGautam Dawar 		write = vringh_iov_push_iotlb(&cvq->vring, &cvq->out_iov,
175bda324fdSGautam Dawar 					      &status, sizeof(status));
176bda324fdSGautam Dawar 		vringh_complete_iotlb(&cvq->vring, cvq->head, write);
177bda324fdSGautam Dawar 		vringh_kiov_cleanup(&cvq->in_iov);
178bda324fdSGautam Dawar 		vringh_kiov_cleanup(&cvq->out_iov);
179bda324fdSGautam Dawar 
180bda324fdSGautam Dawar 		/* Make sure used is visible before rasing the interrupt. */
181bda324fdSGautam Dawar 		smp_wmb();
182bda324fdSGautam Dawar 
183bda324fdSGautam Dawar 		local_bh_disable();
184bda324fdSGautam Dawar 		if (cvq->cb)
185bda324fdSGautam Dawar 			cvq->cb(cvq->private);
186bda324fdSGautam Dawar 		local_bh_enable();
187bda324fdSGautam Dawar 	}
1880899774cSJason Wang 
1890899774cSJason Wang 	u64_stats_update_begin(&net->cq_stats.syncp);
1900899774cSJason Wang 	net->cq_stats.requests += requests;
1910899774cSJason Wang 	net->cq_stats.errors += errors;
1920899774cSJason Wang 	net->cq_stats.successes += successes;
1930899774cSJason Wang 	u64_stats_update_end(&net->cq_stats.syncp);
194bda324fdSGautam Dawar }
195bda324fdSGautam Dawar 
vdpasim_net_work(struct vdpasim * vdpasim)196e2a4f808SStefano Garzarella static void vdpasim_net_work(struct vdpasim *vdpasim)
197db1e8bb6SMax Gurtovoy {
198db1e8bb6SMax Gurtovoy 	struct vdpasim_virtqueue *txq = &vdpasim->vqs[1];
199db1e8bb6SMax Gurtovoy 	struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0];
2000899774cSJason Wang 	struct vdpasim_net *net = sim_to_net(vdpasim);
201db1e8bb6SMax Gurtovoy 	ssize_t read, write;
2020899774cSJason Wang 	u64 tx_pkts = 0, rx_pkts = 0, tx_bytes = 0, rx_bytes = 0;
2030899774cSJason Wang 	u64 rx_drops = 0, rx_overruns = 0, rx_errors = 0, tx_errors = 0;
204db1e8bb6SMax Gurtovoy 	int err;
205db1e8bb6SMax Gurtovoy 
206d7621c28SStefano Garzarella 	mutex_lock(&vdpasim->mutex);
207db1e8bb6SMax Gurtovoy 
2080c89e2a3SEugenio Pérez 	if (!vdpasim->running)
2090c89e2a3SEugenio Pérez 		goto out;
2100c89e2a3SEugenio Pérez 
211db1e8bb6SMax Gurtovoy 	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
212db1e8bb6SMax Gurtovoy 		goto out;
213db1e8bb6SMax Gurtovoy 
214bda324fdSGautam Dawar 	vdpasim_handle_cvq(vdpasim);
215bda324fdSGautam Dawar 
216db1e8bb6SMax Gurtovoy 	if (!txq->ready || !rxq->ready)
217db1e8bb6SMax Gurtovoy 		goto out;
218db1e8bb6SMax Gurtovoy 
219db1e8bb6SMax Gurtovoy 	while (true) {
220db1e8bb6SMax Gurtovoy 		err = vringh_getdesc_iotlb(&txq->vring, &txq->out_iov, NULL,
221db1e8bb6SMax Gurtovoy 					   &txq->head, GFP_ATOMIC);
2220899774cSJason Wang 		if (err <= 0) {
2230899774cSJason Wang 			if (err)
2240899774cSJason Wang 				++tx_errors;
225db1e8bb6SMax Gurtovoy 			break;
2260899774cSJason Wang 		}
227db1e8bb6SMax Gurtovoy 
2280899774cSJason Wang 		++tx_pkts;
229db1e8bb6SMax Gurtovoy 		read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov,
230*112f23cdSStefano Garzarella 					     net->buffer, PAGE_SIZE);
231cfe22689SGautam Dawar 
2320899774cSJason Wang 		tx_bytes += read;
2330899774cSJason Wang 
234cfe22689SGautam Dawar 		if (!receive_filter(vdpasim, read)) {
2350899774cSJason Wang 			++rx_drops;
236cfe22689SGautam Dawar 			vdpasim_net_complete(txq, 0);
237cfe22689SGautam Dawar 			continue;
238cfe22689SGautam Dawar 		}
239cfe22689SGautam Dawar 
240cfe22689SGautam Dawar 		err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->in_iov,
241cfe22689SGautam Dawar 					   &rxq->head, GFP_ATOMIC);
242cfe22689SGautam Dawar 		if (err <= 0) {
2430899774cSJason Wang 			++rx_overruns;
244cfe22689SGautam Dawar 			vdpasim_net_complete(txq, 0);
245db1e8bb6SMax Gurtovoy 			break;
246cfe22689SGautam Dawar 		}
247db1e8bb6SMax Gurtovoy 
248db1e8bb6SMax Gurtovoy 		write = vringh_iov_push_iotlb(&rxq->vring, &rxq->in_iov,
249*112f23cdSStefano Garzarella 					      net->buffer, read);
2500899774cSJason Wang 		if (write <= 0) {
2510899774cSJason Wang 			++rx_errors;
252db1e8bb6SMax Gurtovoy 			break;
2530899774cSJason Wang 		}
2540899774cSJason Wang 
2550899774cSJason Wang 		++rx_pkts;
2560899774cSJason Wang 		rx_bytes += write;
257db1e8bb6SMax Gurtovoy 
258ec103d98SGautam Dawar 		vdpasim_net_complete(txq, 0);
259cfe22689SGautam Dawar 		vdpasim_net_complete(rxq, write);
260db1e8bb6SMax Gurtovoy 
2610899774cSJason Wang 		if (tx_pkts > 4) {
262e2a4f808SStefano Garzarella 			vdpasim_schedule_work(vdpasim);
263db1e8bb6SMax Gurtovoy 			goto out;
264db1e8bb6SMax Gurtovoy 		}
265db1e8bb6SMax Gurtovoy 	}
266db1e8bb6SMax Gurtovoy 
267db1e8bb6SMax Gurtovoy out:
268d7621c28SStefano Garzarella 	mutex_unlock(&vdpasim->mutex);
2690899774cSJason Wang 
2700899774cSJason Wang 	u64_stats_update_begin(&net->tx_stats.syncp);
2710899774cSJason Wang 	net->tx_stats.pkts += tx_pkts;
2720899774cSJason Wang 	net->tx_stats.bytes += tx_bytes;
2730899774cSJason Wang 	net->tx_stats.errors += tx_errors;
2740899774cSJason Wang 	u64_stats_update_end(&net->tx_stats.syncp);
2750899774cSJason Wang 
2760899774cSJason Wang 	u64_stats_update_begin(&net->rx_stats.syncp);
2770899774cSJason Wang 	net->rx_stats.pkts += rx_pkts;
2780899774cSJason Wang 	net->rx_stats.bytes += rx_bytes;
2790899774cSJason Wang 	net->rx_stats.drops += rx_drops;
2800899774cSJason Wang 	net->rx_stats.errors += rx_errors;
2810899774cSJason Wang 	net->rx_stats.overruns += rx_overruns;
2820899774cSJason Wang 	u64_stats_update_end(&net->rx_stats.syncp);
2830899774cSJason Wang }
2840899774cSJason Wang 
vdpasim_net_get_stats(struct vdpasim * vdpasim,u16 idx,struct sk_buff * msg,struct netlink_ext_ack * extack)2850899774cSJason Wang static int vdpasim_net_get_stats(struct vdpasim *vdpasim, u16 idx,
2860899774cSJason Wang 				 struct sk_buff *msg,
2870899774cSJason Wang 				 struct netlink_ext_ack *extack)
2880899774cSJason Wang {
2890899774cSJason Wang 	struct vdpasim_net *net = sim_to_net(vdpasim);
2900899774cSJason Wang 	u64 rx_pkts, rx_bytes, rx_errors, rx_overruns, rx_drops;
2910899774cSJason Wang 	u64 tx_pkts, tx_bytes, tx_errors, tx_drops;
2920899774cSJason Wang 	u64 cq_requests, cq_successes, cq_errors;
2930899774cSJason Wang 	unsigned int start;
2940899774cSJason Wang 	int err = -EMSGSIZE;
2950899774cSJason Wang 
2960899774cSJason Wang 	switch(idx) {
2970899774cSJason Wang 	case 0:
2980899774cSJason Wang 		do {
2990899774cSJason Wang 			start = u64_stats_fetch_begin(&net->rx_stats.syncp);
3000899774cSJason Wang 			rx_pkts = net->rx_stats.pkts;
3010899774cSJason Wang 			rx_bytes = net->rx_stats.bytes;
3020899774cSJason Wang 			rx_errors = net->rx_stats.errors;
3030899774cSJason Wang 			rx_overruns = net->rx_stats.overruns;
3040899774cSJason Wang 			rx_drops = net->rx_stats.drops;
3050899774cSJason Wang 		} while (u64_stats_fetch_retry(&net->rx_stats.syncp, start));
3060899774cSJason Wang 
3070899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3080899774cSJason Wang 					"rx packets"))
3090899774cSJason Wang 			break;
3100899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3110899774cSJason Wang 				      rx_pkts, VDPA_ATTR_PAD))
3120899774cSJason Wang 			break;
3130899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3140899774cSJason Wang 				  "rx bytes"))
3150899774cSJason Wang 			break;
3160899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3170899774cSJason Wang 				      rx_bytes, VDPA_ATTR_PAD))
3180899774cSJason Wang 			break;
3190899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3200899774cSJason Wang 				  "rx errors"))
3210899774cSJason Wang 			break;
3220899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3230899774cSJason Wang 				      rx_errors, VDPA_ATTR_PAD))
3240899774cSJason Wang 			break;
3250899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
326699209fcSColin Ian King 				  "rx overruns"))
3270899774cSJason Wang 			break;
3280899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3290899774cSJason Wang 				      rx_overruns, VDPA_ATTR_PAD))
3300899774cSJason Wang 			break;
3310899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3320899774cSJason Wang 				  "rx drops"))
3330899774cSJason Wang 			break;
3340899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3350899774cSJason Wang 				      rx_drops, VDPA_ATTR_PAD))
3360899774cSJason Wang 			break;
3370899774cSJason Wang 		err = 0;
3380899774cSJason Wang 		break;
3390899774cSJason Wang 	case 1:
3400899774cSJason Wang 		do {
3410899774cSJason Wang 			start = u64_stats_fetch_begin(&net->tx_stats.syncp);
3420899774cSJason Wang 			tx_pkts = net->tx_stats.pkts;
3430899774cSJason Wang 			tx_bytes = net->tx_stats.bytes;
3440899774cSJason Wang 			tx_errors = net->tx_stats.errors;
3450899774cSJason Wang 			tx_drops = net->tx_stats.drops;
3460899774cSJason Wang 		} while (u64_stats_fetch_retry(&net->tx_stats.syncp, start));
3470899774cSJason Wang 
3480899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3490899774cSJason Wang 				  "tx packets"))
3500899774cSJason Wang 			break;
3510899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3520899774cSJason Wang 				      tx_pkts, VDPA_ATTR_PAD))
3530899774cSJason Wang 			break;
3540899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3550899774cSJason Wang 				  "tx bytes"))
3560899774cSJason Wang 			break;
3570899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3580899774cSJason Wang 				      tx_bytes, VDPA_ATTR_PAD))
3590899774cSJason Wang 			break;
3600899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3610899774cSJason Wang 				  "tx errors"))
3620899774cSJason Wang 			break;
3630899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3640899774cSJason Wang 				      tx_errors, VDPA_ATTR_PAD))
3650899774cSJason Wang 			break;
3660899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3670899774cSJason Wang 				  "tx drops"))
3680899774cSJason Wang 			break;
3690899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3700899774cSJason Wang 				      tx_drops, VDPA_ATTR_PAD))
3710899774cSJason Wang 			break;
3720899774cSJason Wang 		err = 0;
3730899774cSJason Wang 		break;
3740899774cSJason Wang 	case 2:
3750899774cSJason Wang 		do {
3760899774cSJason Wang 			start = u64_stats_fetch_begin(&net->cq_stats.syncp);
3770899774cSJason Wang 			cq_requests = net->cq_stats.requests;
3780899774cSJason Wang 			cq_successes = net->cq_stats.successes;
3790899774cSJason Wang 			cq_errors = net->cq_stats.errors;
3800899774cSJason Wang 		} while (u64_stats_fetch_retry(&net->cq_stats.syncp, start));
3810899774cSJason Wang 
3820899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3830899774cSJason Wang 				  "cvq requests"))
3840899774cSJason Wang 			break;
3850899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3860899774cSJason Wang 				      cq_requests, VDPA_ATTR_PAD))
3870899774cSJason Wang 			break;
3880899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3890899774cSJason Wang 				  "cvq successes"))
3900899774cSJason Wang 			break;
3910899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3920899774cSJason Wang 				      cq_successes, VDPA_ATTR_PAD))
3930899774cSJason Wang 			break;
3940899774cSJason Wang 		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
3950899774cSJason Wang 				  "cvq errors"))
3960899774cSJason Wang 			break;
3970899774cSJason Wang 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
3980899774cSJason Wang 				      cq_errors, VDPA_ATTR_PAD))
3990899774cSJason Wang 			break;
4000899774cSJason Wang 		err = 0;
4010899774cSJason Wang 		break;
4020899774cSJason Wang 	default:
4030899774cSJason Wang 		err = -EINVAL;
4040899774cSJason Wang 		break;
4050899774cSJason Wang 	}
4060899774cSJason Wang 
4070899774cSJason Wang 	return err;
408db1e8bb6SMax Gurtovoy }
409db1e8bb6SMax Gurtovoy 
vdpasim_net_get_config(struct vdpasim * vdpasim,void * config)410db1e8bb6SMax Gurtovoy static void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config)
411db1e8bb6SMax Gurtovoy {
412aa443ac2SParav Pandit 	struct virtio_net_config *net_config = config;
413db1e8bb6SMax Gurtovoy 
414db1e8bb6SMax Gurtovoy 	net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
4151138b981SParav Pandit }
4161138b981SParav Pandit 
vdpasim_net_setup_config(struct vdpasim * vdpasim,const struct vdpa_dev_set_config * config)4171138b981SParav Pandit static void vdpasim_net_setup_config(struct vdpasim *vdpasim,
4181138b981SParav Pandit 				     const struct vdpa_dev_set_config *config)
4191138b981SParav Pandit {
4201138b981SParav Pandit 	struct virtio_net_config *vio_config = vdpasim->config;
4211138b981SParav Pandit 
4221138b981SParav Pandit 	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR))
4231138b981SParav Pandit 		memcpy(vio_config->mac, config->net.mac, ETH_ALEN);
4241138b981SParav Pandit 	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MTU))
4251138b981SParav Pandit 		vio_config->mtu = cpu_to_vdpasim16(vdpasim, config->net.mtu);
4261138b981SParav Pandit 	else
4271138b981SParav Pandit 		/* Setup default MTU to be 1500 */
4281138b981SParav Pandit 		vio_config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
429db1e8bb6SMax Gurtovoy }
430db1e8bb6SMax Gurtovoy 
vdpasim_net_free(struct vdpasim * vdpasim)431*112f23cdSStefano Garzarella static void vdpasim_net_free(struct vdpasim *vdpasim)
432*112f23cdSStefano Garzarella {
433*112f23cdSStefano Garzarella 	struct vdpasim_net *net = sim_to_net(vdpasim);
434*112f23cdSStefano Garzarella 
435*112f23cdSStefano Garzarella 	kvfree(net->buffer);
436*112f23cdSStefano Garzarella }
437*112f23cdSStefano Garzarella 
vdpasim_net_mgmtdev_release(struct device * dev)438a3c06ae1SParav Pandit static void vdpasim_net_mgmtdev_release(struct device *dev)
439a3c06ae1SParav Pandit {
440a3c06ae1SParav Pandit }
441a3c06ae1SParav Pandit 
442a3c06ae1SParav Pandit static struct device vdpasim_net_mgmtdev = {
443a3c06ae1SParav Pandit 	.init_name = "vdpasim_net",
444a3c06ae1SParav Pandit 	.release = vdpasim_net_mgmtdev_release,
445a3c06ae1SParav Pandit };
446a3c06ae1SParav Pandit 
vdpasim_net_dev_add(struct vdpa_mgmt_dev * mdev,const char * name,const struct vdpa_dev_set_config * config)447d8ca2fa5SParav Pandit static int vdpasim_net_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
448d8ca2fa5SParav Pandit 			       const struct vdpa_dev_set_config *config)
449db1e8bb6SMax Gurtovoy {
450db1e8bb6SMax Gurtovoy 	struct vdpasim_dev_attr dev_attr = {};
4510899774cSJason Wang 	struct vdpasim_net *net;
452a3c06ae1SParav Pandit 	struct vdpasim *simdev;
453db1e8bb6SMax Gurtovoy 	int ret;
454db1e8bb6SMax Gurtovoy 
455a3c06ae1SParav Pandit 	dev_attr.mgmt_dev = mdev;
456a3c06ae1SParav Pandit 	dev_attr.name = name;
457db1e8bb6SMax Gurtovoy 	dev_attr.id = VIRTIO_ID_NET;
458db1e8bb6SMax Gurtovoy 	dev_attr.supported_features = VDPASIM_NET_FEATURES;
459db1e8bb6SMax Gurtovoy 	dev_attr.nvqs = VDPASIM_NET_VQ_NUM;
460bda324fdSGautam Dawar 	dev_attr.ngroups = VDPASIM_NET_GROUP_NUM;
461bda324fdSGautam Dawar 	dev_attr.nas = VDPASIM_NET_AS_NUM;
4620899774cSJason Wang 	dev_attr.alloc_size = sizeof(struct vdpasim_net);
463db1e8bb6SMax Gurtovoy 	dev_attr.config_size = sizeof(struct virtio_net_config);
464db1e8bb6SMax Gurtovoy 	dev_attr.get_config = vdpasim_net_get_config;
465db1e8bb6SMax Gurtovoy 	dev_attr.work_fn = vdpasim_net_work;
4660899774cSJason Wang 	dev_attr.get_stats = vdpasim_net_get_stats;
467*112f23cdSStefano Garzarella 	dev_attr.free = vdpasim_net_free;
468db1e8bb6SMax Gurtovoy 
469477f7197SJason Wang 	simdev = vdpasim_create(&dev_attr, config);
470a3c06ae1SParav Pandit 	if (IS_ERR(simdev))
471a3c06ae1SParav Pandit 		return PTR_ERR(simdev);
472db1e8bb6SMax Gurtovoy 
4731138b981SParav Pandit 	vdpasim_net_setup_config(simdev, config);
4741138b981SParav Pandit 
4750899774cSJason Wang 	net = sim_to_net(simdev);
4760899774cSJason Wang 
4770899774cSJason Wang 	u64_stats_init(&net->tx_stats.syncp);
4780899774cSJason Wang 	u64_stats_init(&net->rx_stats.syncp);
4790899774cSJason Wang 	u64_stats_init(&net->cq_stats.syncp);
4800899774cSJason Wang 
481*112f23cdSStefano Garzarella 	net->buffer = kvmalloc(PAGE_SIZE, GFP_KERNEL);
482*112f23cdSStefano Garzarella 	if (!net->buffer) {
483*112f23cdSStefano Garzarella 		ret = -ENOMEM;
484*112f23cdSStefano Garzarella 		goto reg_err;
485*112f23cdSStefano Garzarella 	}
486*112f23cdSStefano Garzarella 
4879da667e5SStefano Garzarella 	/*
4889da667e5SStefano Garzarella 	 * Initialization must be completed before this call, since it can
4899da667e5SStefano Garzarella 	 * connect the device to the vDPA bus, so requests can arrive after
4909da667e5SStefano Garzarella 	 * this call.
4919da667e5SStefano Garzarella 	 */
4929da667e5SStefano Garzarella 	ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_NET_VQ_NUM);
4939da667e5SStefano Garzarella 	if (ret)
4949da667e5SStefano Garzarella 		goto reg_err;
4959da667e5SStefano Garzarella 
496db1e8bb6SMax Gurtovoy 	return 0;
497db1e8bb6SMax Gurtovoy 
498a3c06ae1SParav Pandit reg_err:
499a3c06ae1SParav Pandit 	put_device(&simdev->vdpa.dev);
500a3c06ae1SParav Pandit 	return ret;
501a3c06ae1SParav Pandit }
502a3c06ae1SParav Pandit 
vdpasim_net_dev_del(struct vdpa_mgmt_dev * mdev,struct vdpa_device * dev)503a3c06ae1SParav Pandit static void vdpasim_net_dev_del(struct vdpa_mgmt_dev *mdev,
504a3c06ae1SParav Pandit 				struct vdpa_device *dev)
505a3c06ae1SParav Pandit {
506a3c06ae1SParav Pandit 	struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
507a3c06ae1SParav Pandit 
508a3c06ae1SParav Pandit 	_vdpa_unregister_device(&simdev->vdpa);
509a3c06ae1SParav Pandit }
510a3c06ae1SParav Pandit 
511a3c06ae1SParav Pandit static const struct vdpa_mgmtdev_ops vdpasim_net_mgmtdev_ops = {
512a3c06ae1SParav Pandit 	.dev_add = vdpasim_net_dev_add,
513a3c06ae1SParav Pandit 	.dev_del = vdpasim_net_dev_del
514a3c06ae1SParav Pandit };
515a3c06ae1SParav Pandit 
516a3c06ae1SParav Pandit static struct virtio_device_id id_table[] = {
517a3c06ae1SParav Pandit 	{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
518a3c06ae1SParav Pandit 	{ 0 },
519a3c06ae1SParav Pandit };
520a3c06ae1SParav Pandit 
521a3c06ae1SParav Pandit static struct vdpa_mgmt_dev mgmt_dev = {
522a3c06ae1SParav Pandit 	.device = &vdpasim_net_mgmtdev,
523a3c06ae1SParav Pandit 	.id_table = id_table,
524a3c06ae1SParav Pandit 	.ops = &vdpasim_net_mgmtdev_ops,
5251138b981SParav Pandit 	.config_attr_mask = (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR |
526477f7197SJason Wang 			     1 << VDPA_ATTR_DEV_NET_CFG_MTU |
527477f7197SJason Wang 		             1 << VDPA_ATTR_DEV_FEATURES),
528cbe777e9SEli Cohen 	.max_supported_vqs = VDPASIM_NET_VQ_NUM,
529b2ce6197SEli Cohen 	.supported_features = VDPASIM_NET_FEATURES,
530a3c06ae1SParav Pandit };
531a3c06ae1SParav Pandit 
vdpasim_net_init(void)532a3c06ae1SParav Pandit static int __init vdpasim_net_init(void)
533a3c06ae1SParav Pandit {
534a3c06ae1SParav Pandit 	int ret;
535a3c06ae1SParav Pandit 
536a3c06ae1SParav Pandit 	ret = device_register(&vdpasim_net_mgmtdev);
537aeca7ff2Sruanjinjie 	if (ret) {
538aeca7ff2Sruanjinjie 		put_device(&vdpasim_net_mgmtdev);
539a3c06ae1SParav Pandit 		return ret;
540aeca7ff2Sruanjinjie 	}
541a3c06ae1SParav Pandit 
542a3c06ae1SParav Pandit 	ret = vdpa_mgmtdev_register(&mgmt_dev);
543a3c06ae1SParav Pandit 	if (ret)
544a3c06ae1SParav Pandit 		goto parent_err;
545a3c06ae1SParav Pandit 	return 0;
546a3c06ae1SParav Pandit 
547a3c06ae1SParav Pandit parent_err:
548a3c06ae1SParav Pandit 	device_unregister(&vdpasim_net_mgmtdev);
549db1e8bb6SMax Gurtovoy 	return ret;
550db1e8bb6SMax Gurtovoy }
551db1e8bb6SMax Gurtovoy 
vdpasim_net_exit(void)552db1e8bb6SMax Gurtovoy static void __exit vdpasim_net_exit(void)
553db1e8bb6SMax Gurtovoy {
554a3c06ae1SParav Pandit 	vdpa_mgmtdev_unregister(&mgmt_dev);
555a3c06ae1SParav Pandit 	device_unregister(&vdpasim_net_mgmtdev);
556db1e8bb6SMax Gurtovoy }
557db1e8bb6SMax Gurtovoy 
558db1e8bb6SMax Gurtovoy module_init(vdpasim_net_init);
559db1e8bb6SMax Gurtovoy module_exit(vdpasim_net_exit);
560db1e8bb6SMax Gurtovoy 
561db1e8bb6SMax Gurtovoy MODULE_VERSION(DRV_VERSION);
562db1e8bb6SMax Gurtovoy MODULE_LICENSE(DRV_LICENSE);
563db1e8bb6SMax Gurtovoy MODULE_AUTHOR(DRV_AUTHOR);
564db1e8bb6SMax Gurtovoy MODULE_DESCRIPTION(DRV_DESC);
565