162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * VDPA simulator for networking device.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020, Red Hat Inc. All rights reserved.
662306a36Sopenharmony_ci *     Author: Jason Wang <jasowang@redhat.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/etherdevice.h>
1562306a36Sopenharmony_ci#include <linux/vringh.h>
1662306a36Sopenharmony_ci#include <linux/vdpa.h>
1762306a36Sopenharmony_ci#include <net/netlink.h>
1862306a36Sopenharmony_ci#include <uapi/linux/virtio_net.h>
1962306a36Sopenharmony_ci#include <uapi/linux/vdpa.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "vdpa_sim.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define DRV_VERSION  "0.1"
2462306a36Sopenharmony_ci#define DRV_AUTHOR   "Jason Wang <jasowang@redhat.com>"
2562306a36Sopenharmony_ci#define DRV_DESC     "vDPA Device Simulator for networking device"
2662306a36Sopenharmony_ci#define DRV_LICENSE  "GPL v2"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define VDPASIM_NET_FEATURES	(VDPASIM_FEATURES | \
2962306a36Sopenharmony_ci				 (1ULL << VIRTIO_NET_F_MAC) | \
3062306a36Sopenharmony_ci				 (1ULL << VIRTIO_NET_F_STATUS) | \
3162306a36Sopenharmony_ci				 (1ULL << VIRTIO_NET_F_MTU) | \
3262306a36Sopenharmony_ci				 (1ULL << VIRTIO_NET_F_CTRL_VQ) | \
3362306a36Sopenharmony_ci				 (1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* 3 virtqueues, 2 address spaces, 2 virtqueue groups */
3662306a36Sopenharmony_ci#define VDPASIM_NET_VQ_NUM	3
3762306a36Sopenharmony_ci#define VDPASIM_NET_AS_NUM	2
3862306a36Sopenharmony_ci#define VDPASIM_NET_GROUP_NUM	2
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct vdpasim_dataq_stats {
4162306a36Sopenharmony_ci	struct u64_stats_sync syncp;
4262306a36Sopenharmony_ci	u64 pkts;
4362306a36Sopenharmony_ci	u64 bytes;
4462306a36Sopenharmony_ci	u64 drops;
4562306a36Sopenharmony_ci	u64 errors;
4662306a36Sopenharmony_ci	u64 overruns;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct vdpasim_cq_stats {
5062306a36Sopenharmony_ci	struct u64_stats_sync syncp;
5162306a36Sopenharmony_ci	u64 requests;
5262306a36Sopenharmony_ci	u64 successes;
5362306a36Sopenharmony_ci	u64 errors;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct vdpasim_net{
5762306a36Sopenharmony_ci	struct vdpasim vdpasim;
5862306a36Sopenharmony_ci	struct vdpasim_dataq_stats tx_stats;
5962306a36Sopenharmony_ci	struct vdpasim_dataq_stats rx_stats;
6062306a36Sopenharmony_ci	struct vdpasim_cq_stats cq_stats;
6162306a36Sopenharmony_ci	void *buffer;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct vdpasim_net *sim_to_net(struct vdpasim *vdpasim)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return container_of(vdpasim, struct vdpasim_net, vdpasim);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	/* Make sure data is wrote before advancing index */
7262306a36Sopenharmony_ci	smp_wmb();
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	vringh_complete_iotlb(&vq->vring, vq->head, len);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Make sure used is visible before rasing the interrupt. */
7762306a36Sopenharmony_ci	smp_wmb();
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	local_bh_disable();
8062306a36Sopenharmony_ci	if (vringh_need_notify_iotlb(&vq->vring) > 0)
8162306a36Sopenharmony_ci		vringh_notify(&vq->vring);
8262306a36Sopenharmony_ci	local_bh_enable();
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic bool receive_filter(struct vdpasim *vdpasim, size_t len)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	bool modern = vdpasim->features & (1ULL << VIRTIO_F_VERSION_1);
8862306a36Sopenharmony_ci	size_t hdr_len = modern ? sizeof(struct virtio_net_hdr_v1) :
8962306a36Sopenharmony_ci				  sizeof(struct virtio_net_hdr);
9062306a36Sopenharmony_ci	struct virtio_net_config *vio_config = vdpasim->config;
9162306a36Sopenharmony_ci	struct vdpasim_net *net = sim_to_net(vdpasim);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (len < ETH_ALEN + hdr_len)
9462306a36Sopenharmony_ci		return false;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (is_broadcast_ether_addr(net->buffer + hdr_len) ||
9762306a36Sopenharmony_ci	    is_multicast_ether_addr(net->buffer + hdr_len))
9862306a36Sopenharmony_ci		return true;
9962306a36Sopenharmony_ci	if (!strncmp(net->buffer + hdr_len, vio_config->mac, ETH_ALEN))
10062306a36Sopenharmony_ci		return true;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return false;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic virtio_net_ctrl_ack vdpasim_handle_ctrl_mac(struct vdpasim *vdpasim,
10662306a36Sopenharmony_ci						   u8 cmd)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct virtio_net_config *vio_config = vdpasim->config;
10962306a36Sopenharmony_ci	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
11062306a36Sopenharmony_ci	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
11162306a36Sopenharmony_ci	size_t read;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	switch (cmd) {
11462306a36Sopenharmony_ci	case VIRTIO_NET_CTRL_MAC_ADDR_SET:
11562306a36Sopenharmony_ci		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov,
11662306a36Sopenharmony_ci					     vio_config->mac, ETH_ALEN);
11762306a36Sopenharmony_ci		if (read == ETH_ALEN)
11862306a36Sopenharmony_ci			status = VIRTIO_NET_OK;
11962306a36Sopenharmony_ci		break;
12062306a36Sopenharmony_ci	default:
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return status;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void vdpasim_handle_cvq(struct vdpasim *vdpasim)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2];
13062306a36Sopenharmony_ci	struct vdpasim_net *net = sim_to_net(vdpasim);
13162306a36Sopenharmony_ci	virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
13262306a36Sopenharmony_ci	struct virtio_net_ctrl_hdr ctrl;
13362306a36Sopenharmony_ci	size_t read, write;
13462306a36Sopenharmony_ci	u64 requests = 0, errors = 0, successes = 0;
13562306a36Sopenharmony_ci	int err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!(vdpasim->features & (1ULL << VIRTIO_NET_F_CTRL_VQ)))
13862306a36Sopenharmony_ci		return;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!cvq->ready)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	while (true) {
14462306a36Sopenharmony_ci		err = vringh_getdesc_iotlb(&cvq->vring, &cvq->in_iov,
14562306a36Sopenharmony_ci					   &cvq->out_iov,
14662306a36Sopenharmony_ci					   &cvq->head, GFP_ATOMIC);
14762306a36Sopenharmony_ci		if (err <= 0)
14862306a36Sopenharmony_ci			break;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		++requests;
15162306a36Sopenharmony_ci		read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov, &ctrl,
15262306a36Sopenharmony_ci					     sizeof(ctrl));
15362306a36Sopenharmony_ci		if (read != sizeof(ctrl)) {
15462306a36Sopenharmony_ci			++errors;
15562306a36Sopenharmony_ci			break;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		switch (ctrl.class) {
15962306a36Sopenharmony_ci		case VIRTIO_NET_CTRL_MAC:
16062306a36Sopenharmony_ci			status = vdpasim_handle_ctrl_mac(vdpasim, ctrl.cmd);
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci		default:
16362306a36Sopenharmony_ci			break;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		if (status == VIRTIO_NET_OK)
16762306a36Sopenharmony_ci			++successes;
16862306a36Sopenharmony_ci		else
16962306a36Sopenharmony_ci			++errors;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* Make sure data is wrote before advancing index */
17262306a36Sopenharmony_ci		smp_wmb();
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		write = vringh_iov_push_iotlb(&cvq->vring, &cvq->out_iov,
17562306a36Sopenharmony_ci					      &status, sizeof(status));
17662306a36Sopenharmony_ci		vringh_complete_iotlb(&cvq->vring, cvq->head, write);
17762306a36Sopenharmony_ci		vringh_kiov_cleanup(&cvq->in_iov);
17862306a36Sopenharmony_ci		vringh_kiov_cleanup(&cvq->out_iov);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		/* Make sure used is visible before rasing the interrupt. */
18162306a36Sopenharmony_ci		smp_wmb();
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		local_bh_disable();
18462306a36Sopenharmony_ci		if (cvq->cb)
18562306a36Sopenharmony_ci			cvq->cb(cvq->private);
18662306a36Sopenharmony_ci		local_bh_enable();
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	u64_stats_update_begin(&net->cq_stats.syncp);
19062306a36Sopenharmony_ci	net->cq_stats.requests += requests;
19162306a36Sopenharmony_ci	net->cq_stats.errors += errors;
19262306a36Sopenharmony_ci	net->cq_stats.successes += successes;
19362306a36Sopenharmony_ci	u64_stats_update_end(&net->cq_stats.syncp);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void vdpasim_net_work(struct vdpasim *vdpasim)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct vdpasim_virtqueue *txq = &vdpasim->vqs[1];
19962306a36Sopenharmony_ci	struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0];
20062306a36Sopenharmony_ci	struct vdpasim_net *net = sim_to_net(vdpasim);
20162306a36Sopenharmony_ci	ssize_t read, write;
20262306a36Sopenharmony_ci	u64 tx_pkts = 0, rx_pkts = 0, tx_bytes = 0, rx_bytes = 0;
20362306a36Sopenharmony_ci	u64 rx_drops = 0, rx_overruns = 0, rx_errors = 0, tx_errors = 0;
20462306a36Sopenharmony_ci	int err;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	mutex_lock(&vdpasim->mutex);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!vdpasim->running)
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
21262306a36Sopenharmony_ci		goto out;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	vdpasim_handle_cvq(vdpasim);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!txq->ready || !rxq->ready)
21762306a36Sopenharmony_ci		goto out;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	while (true) {
22062306a36Sopenharmony_ci		err = vringh_getdesc_iotlb(&txq->vring, &txq->out_iov, NULL,
22162306a36Sopenharmony_ci					   &txq->head, GFP_ATOMIC);
22262306a36Sopenharmony_ci		if (err <= 0) {
22362306a36Sopenharmony_ci			if (err)
22462306a36Sopenharmony_ci				++tx_errors;
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		++tx_pkts;
22962306a36Sopenharmony_ci		read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov,
23062306a36Sopenharmony_ci					     net->buffer, PAGE_SIZE);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		tx_bytes += read;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		if (!receive_filter(vdpasim, read)) {
23562306a36Sopenharmony_ci			++rx_drops;
23662306a36Sopenharmony_ci			vdpasim_net_complete(txq, 0);
23762306a36Sopenharmony_ci			continue;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->in_iov,
24162306a36Sopenharmony_ci					   &rxq->head, GFP_ATOMIC);
24262306a36Sopenharmony_ci		if (err <= 0) {
24362306a36Sopenharmony_ci			++rx_overruns;
24462306a36Sopenharmony_ci			vdpasim_net_complete(txq, 0);
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		write = vringh_iov_push_iotlb(&rxq->vring, &rxq->in_iov,
24962306a36Sopenharmony_ci					      net->buffer, read);
25062306a36Sopenharmony_ci		if (write <= 0) {
25162306a36Sopenharmony_ci			++rx_errors;
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		++rx_pkts;
25662306a36Sopenharmony_ci		rx_bytes += write;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		vdpasim_net_complete(txq, 0);
25962306a36Sopenharmony_ci		vdpasim_net_complete(rxq, write);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		if (tx_pkts > 4) {
26262306a36Sopenharmony_ci			vdpasim_schedule_work(vdpasim);
26362306a36Sopenharmony_ci			goto out;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ciout:
26862306a36Sopenharmony_ci	mutex_unlock(&vdpasim->mutex);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	u64_stats_update_begin(&net->tx_stats.syncp);
27162306a36Sopenharmony_ci	net->tx_stats.pkts += tx_pkts;
27262306a36Sopenharmony_ci	net->tx_stats.bytes += tx_bytes;
27362306a36Sopenharmony_ci	net->tx_stats.errors += tx_errors;
27462306a36Sopenharmony_ci	u64_stats_update_end(&net->tx_stats.syncp);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	u64_stats_update_begin(&net->rx_stats.syncp);
27762306a36Sopenharmony_ci	net->rx_stats.pkts += rx_pkts;
27862306a36Sopenharmony_ci	net->rx_stats.bytes += rx_bytes;
27962306a36Sopenharmony_ci	net->rx_stats.drops += rx_drops;
28062306a36Sopenharmony_ci	net->rx_stats.errors += rx_errors;
28162306a36Sopenharmony_ci	net->rx_stats.overruns += rx_overruns;
28262306a36Sopenharmony_ci	u64_stats_update_end(&net->rx_stats.syncp);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int vdpasim_net_get_stats(struct vdpasim *vdpasim, u16 idx,
28662306a36Sopenharmony_ci				 struct sk_buff *msg,
28762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct vdpasim_net *net = sim_to_net(vdpasim);
29062306a36Sopenharmony_ci	u64 rx_pkts, rx_bytes, rx_errors, rx_overruns, rx_drops;
29162306a36Sopenharmony_ci	u64 tx_pkts, tx_bytes, tx_errors, tx_drops;
29262306a36Sopenharmony_ci	u64 cq_requests, cq_successes, cq_errors;
29362306a36Sopenharmony_ci	unsigned int start;
29462306a36Sopenharmony_ci	int err = -EMSGSIZE;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch(idx) {
29762306a36Sopenharmony_ci	case 0:
29862306a36Sopenharmony_ci		do {
29962306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&net->rx_stats.syncp);
30062306a36Sopenharmony_ci			rx_pkts = net->rx_stats.pkts;
30162306a36Sopenharmony_ci			rx_bytes = net->rx_stats.bytes;
30262306a36Sopenharmony_ci			rx_errors = net->rx_stats.errors;
30362306a36Sopenharmony_ci			rx_overruns = net->rx_stats.overruns;
30462306a36Sopenharmony_ci			rx_drops = net->rx_stats.drops;
30562306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&net->rx_stats.syncp, start));
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
30862306a36Sopenharmony_ci					"rx packets"))
30962306a36Sopenharmony_ci			break;
31062306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
31162306a36Sopenharmony_ci				      rx_pkts, VDPA_ATTR_PAD))
31262306a36Sopenharmony_ci			break;
31362306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
31462306a36Sopenharmony_ci				  "rx bytes"))
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
31762306a36Sopenharmony_ci				      rx_bytes, VDPA_ATTR_PAD))
31862306a36Sopenharmony_ci			break;
31962306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
32062306a36Sopenharmony_ci				  "rx errors"))
32162306a36Sopenharmony_ci			break;
32262306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
32362306a36Sopenharmony_ci				      rx_errors, VDPA_ATTR_PAD))
32462306a36Sopenharmony_ci			break;
32562306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
32662306a36Sopenharmony_ci				  "rx overruns"))
32762306a36Sopenharmony_ci			break;
32862306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
32962306a36Sopenharmony_ci				      rx_overruns, VDPA_ATTR_PAD))
33062306a36Sopenharmony_ci			break;
33162306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
33262306a36Sopenharmony_ci				  "rx drops"))
33362306a36Sopenharmony_ci			break;
33462306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
33562306a36Sopenharmony_ci				      rx_drops, VDPA_ATTR_PAD))
33662306a36Sopenharmony_ci			break;
33762306a36Sopenharmony_ci		err = 0;
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	case 1:
34062306a36Sopenharmony_ci		do {
34162306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&net->tx_stats.syncp);
34262306a36Sopenharmony_ci			tx_pkts = net->tx_stats.pkts;
34362306a36Sopenharmony_ci			tx_bytes = net->tx_stats.bytes;
34462306a36Sopenharmony_ci			tx_errors = net->tx_stats.errors;
34562306a36Sopenharmony_ci			tx_drops = net->tx_stats.drops;
34662306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&net->tx_stats.syncp, start));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
34962306a36Sopenharmony_ci				  "tx packets"))
35062306a36Sopenharmony_ci			break;
35162306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
35262306a36Sopenharmony_ci				      tx_pkts, VDPA_ATTR_PAD))
35362306a36Sopenharmony_ci			break;
35462306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
35562306a36Sopenharmony_ci				  "tx bytes"))
35662306a36Sopenharmony_ci			break;
35762306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
35862306a36Sopenharmony_ci				      tx_bytes, VDPA_ATTR_PAD))
35962306a36Sopenharmony_ci			break;
36062306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
36162306a36Sopenharmony_ci				  "tx errors"))
36262306a36Sopenharmony_ci			break;
36362306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
36462306a36Sopenharmony_ci				      tx_errors, VDPA_ATTR_PAD))
36562306a36Sopenharmony_ci			break;
36662306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
36762306a36Sopenharmony_ci				  "tx drops"))
36862306a36Sopenharmony_ci			break;
36962306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
37062306a36Sopenharmony_ci				      tx_drops, VDPA_ATTR_PAD))
37162306a36Sopenharmony_ci			break;
37262306a36Sopenharmony_ci		err = 0;
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	case 2:
37562306a36Sopenharmony_ci		do {
37662306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&net->cq_stats.syncp);
37762306a36Sopenharmony_ci			cq_requests = net->cq_stats.requests;
37862306a36Sopenharmony_ci			cq_successes = net->cq_stats.successes;
37962306a36Sopenharmony_ci			cq_errors = net->cq_stats.errors;
38062306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&net->cq_stats.syncp, start));
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
38362306a36Sopenharmony_ci				  "cvq requests"))
38462306a36Sopenharmony_ci			break;
38562306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
38662306a36Sopenharmony_ci				      cq_requests, VDPA_ATTR_PAD))
38762306a36Sopenharmony_ci			break;
38862306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
38962306a36Sopenharmony_ci				  "cvq successes"))
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
39262306a36Sopenharmony_ci				      cq_successes, VDPA_ATTR_PAD))
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci		if (nla_put_string(msg, VDPA_ATTR_DEV_VENDOR_ATTR_NAME,
39562306a36Sopenharmony_ci				  "cvq errors"))
39662306a36Sopenharmony_ci			break;
39762306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,
39862306a36Sopenharmony_ci				      cq_errors, VDPA_ATTR_PAD))
39962306a36Sopenharmony_ci			break;
40062306a36Sopenharmony_ci		err = 0;
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	default:
40362306a36Sopenharmony_ci		err = -EINVAL;
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return err;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct virtio_net_config *net_config = config;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void vdpasim_net_setup_config(struct vdpasim *vdpasim,
41862306a36Sopenharmony_ci				     const struct vdpa_dev_set_config *config)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct virtio_net_config *vio_config = vdpasim->config;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR))
42362306a36Sopenharmony_ci		memcpy(vio_config->mac, config->net.mac, ETH_ALEN);
42462306a36Sopenharmony_ci	if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MTU))
42562306a36Sopenharmony_ci		vio_config->mtu = cpu_to_vdpasim16(vdpasim, config->net.mtu);
42662306a36Sopenharmony_ci	else
42762306a36Sopenharmony_ci		/* Setup default MTU to be 1500 */
42862306a36Sopenharmony_ci		vio_config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void vdpasim_net_free(struct vdpasim *vdpasim)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct vdpasim_net *net = sim_to_net(vdpasim);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	kvfree(net->buffer);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void vdpasim_net_mgmtdev_release(struct device *dev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic struct device vdpasim_net_mgmtdev = {
44362306a36Sopenharmony_ci	.init_name = "vdpasim_net",
44462306a36Sopenharmony_ci	.release = vdpasim_net_mgmtdev_release,
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int vdpasim_net_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
44862306a36Sopenharmony_ci			       const struct vdpa_dev_set_config *config)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct vdpasim_dev_attr dev_attr = {};
45162306a36Sopenharmony_ci	struct vdpasim_net *net;
45262306a36Sopenharmony_ci	struct vdpasim *simdev;
45362306a36Sopenharmony_ci	int ret;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	dev_attr.mgmt_dev = mdev;
45662306a36Sopenharmony_ci	dev_attr.name = name;
45762306a36Sopenharmony_ci	dev_attr.id = VIRTIO_ID_NET;
45862306a36Sopenharmony_ci	dev_attr.supported_features = VDPASIM_NET_FEATURES;
45962306a36Sopenharmony_ci	dev_attr.nvqs = VDPASIM_NET_VQ_NUM;
46062306a36Sopenharmony_ci	dev_attr.ngroups = VDPASIM_NET_GROUP_NUM;
46162306a36Sopenharmony_ci	dev_attr.nas = VDPASIM_NET_AS_NUM;
46262306a36Sopenharmony_ci	dev_attr.alloc_size = sizeof(struct vdpasim_net);
46362306a36Sopenharmony_ci	dev_attr.config_size = sizeof(struct virtio_net_config);
46462306a36Sopenharmony_ci	dev_attr.get_config = vdpasim_net_get_config;
46562306a36Sopenharmony_ci	dev_attr.work_fn = vdpasim_net_work;
46662306a36Sopenharmony_ci	dev_attr.get_stats = vdpasim_net_get_stats;
46762306a36Sopenharmony_ci	dev_attr.free = vdpasim_net_free;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	simdev = vdpasim_create(&dev_attr, config);
47062306a36Sopenharmony_ci	if (IS_ERR(simdev))
47162306a36Sopenharmony_ci		return PTR_ERR(simdev);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	vdpasim_net_setup_config(simdev, config);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	net = sim_to_net(simdev);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	u64_stats_init(&net->tx_stats.syncp);
47862306a36Sopenharmony_ci	u64_stats_init(&net->rx_stats.syncp);
47962306a36Sopenharmony_ci	u64_stats_init(&net->cq_stats.syncp);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	net->buffer = kvmalloc(PAGE_SIZE, GFP_KERNEL);
48262306a36Sopenharmony_ci	if (!net->buffer) {
48362306a36Sopenharmony_ci		ret = -ENOMEM;
48462306a36Sopenharmony_ci		goto reg_err;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/*
48862306a36Sopenharmony_ci	 * Initialization must be completed before this call, since it can
48962306a36Sopenharmony_ci	 * connect the device to the vDPA bus, so requests can arrive after
49062306a36Sopenharmony_ci	 * this call.
49162306a36Sopenharmony_ci	 */
49262306a36Sopenharmony_ci	ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_NET_VQ_NUM);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		goto reg_err;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cireg_err:
49962306a36Sopenharmony_ci	put_device(&simdev->vdpa.dev);
50062306a36Sopenharmony_ci	return ret;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic void vdpasim_net_dev_del(struct vdpa_mgmt_dev *mdev,
50462306a36Sopenharmony_ci				struct vdpa_device *dev)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	_vdpa_unregister_device(&simdev->vdpa);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic const struct vdpa_mgmtdev_ops vdpasim_net_mgmtdev_ops = {
51262306a36Sopenharmony_ci	.dev_add = vdpasim_net_dev_add,
51362306a36Sopenharmony_ci	.dev_del = vdpasim_net_dev_del
51462306a36Sopenharmony_ci};
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = {
51762306a36Sopenharmony_ci	{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
51862306a36Sopenharmony_ci	{ 0 },
51962306a36Sopenharmony_ci};
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic struct vdpa_mgmt_dev mgmt_dev = {
52262306a36Sopenharmony_ci	.device = &vdpasim_net_mgmtdev,
52362306a36Sopenharmony_ci	.id_table = id_table,
52462306a36Sopenharmony_ci	.ops = &vdpasim_net_mgmtdev_ops,
52562306a36Sopenharmony_ci	.config_attr_mask = (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR |
52662306a36Sopenharmony_ci			     1 << VDPA_ATTR_DEV_NET_CFG_MTU |
52762306a36Sopenharmony_ci		             1 << VDPA_ATTR_DEV_FEATURES),
52862306a36Sopenharmony_ci	.max_supported_vqs = VDPASIM_NET_VQ_NUM,
52962306a36Sopenharmony_ci	.supported_features = VDPASIM_NET_FEATURES,
53062306a36Sopenharmony_ci};
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int __init vdpasim_net_init(void)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	int ret;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	ret = device_register(&vdpasim_net_mgmtdev);
53762306a36Sopenharmony_ci	if (ret) {
53862306a36Sopenharmony_ci		put_device(&vdpasim_net_mgmtdev);
53962306a36Sopenharmony_ci		return ret;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	ret = vdpa_mgmtdev_register(&mgmt_dev);
54362306a36Sopenharmony_ci	if (ret)
54462306a36Sopenharmony_ci		goto parent_err;
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciparent_err:
54862306a36Sopenharmony_ci	device_unregister(&vdpasim_net_mgmtdev);
54962306a36Sopenharmony_ci	return ret;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void __exit vdpasim_net_exit(void)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	vdpa_mgmtdev_unregister(&mgmt_dev);
55562306a36Sopenharmony_ci	device_unregister(&vdpasim_net_mgmtdev);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cimodule_init(vdpasim_net_init);
55962306a36Sopenharmony_cimodule_exit(vdpasim_net_exit);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
56262306a36Sopenharmony_ciMODULE_LICENSE(DRV_LICENSE);
56362306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR);
56462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
565