1*db1e8bb6SMax Gurtovoy // SPDX-License-Identifier: GPL-2.0-only
2*db1e8bb6SMax Gurtovoy /*
3*db1e8bb6SMax Gurtovoy  * VDPA simulator for networking device.
4*db1e8bb6SMax Gurtovoy  *
5*db1e8bb6SMax Gurtovoy  * Copyright (c) 2020, Red Hat Inc. All rights reserved.
6*db1e8bb6SMax Gurtovoy  *     Author: Jason Wang <jasowang@redhat.com>
7*db1e8bb6SMax Gurtovoy  *
8*db1e8bb6SMax Gurtovoy  */
9*db1e8bb6SMax Gurtovoy 
10*db1e8bb6SMax Gurtovoy #include <linux/init.h>
11*db1e8bb6SMax Gurtovoy #include <linux/module.h>
12*db1e8bb6SMax Gurtovoy #include <linux/device.h>
13*db1e8bb6SMax Gurtovoy #include <linux/kernel.h>
14*db1e8bb6SMax Gurtovoy #include <linux/sched.h>
15*db1e8bb6SMax Gurtovoy #include <linux/etherdevice.h>
16*db1e8bb6SMax Gurtovoy #include <linux/vringh.h>
17*db1e8bb6SMax Gurtovoy #include <linux/vdpa.h>
18*db1e8bb6SMax Gurtovoy #include <uapi/linux/virtio_net.h>
19*db1e8bb6SMax Gurtovoy 
20*db1e8bb6SMax Gurtovoy #include "vdpa_sim.h"
21*db1e8bb6SMax Gurtovoy 
22*db1e8bb6SMax Gurtovoy #define DRV_VERSION  "0.1"
23*db1e8bb6SMax Gurtovoy #define DRV_AUTHOR   "Jason Wang <jasowang@redhat.com>"
24*db1e8bb6SMax Gurtovoy #define DRV_DESC     "vDPA Device Simulator for networking device"
25*db1e8bb6SMax Gurtovoy #define DRV_LICENSE  "GPL v2"
26*db1e8bb6SMax Gurtovoy 
27*db1e8bb6SMax Gurtovoy #define VDPASIM_NET_FEATURES	(VDPASIM_FEATURES | \
28*db1e8bb6SMax Gurtovoy 				 (1ULL << VIRTIO_NET_F_MAC))
29*db1e8bb6SMax Gurtovoy 
30*db1e8bb6SMax Gurtovoy #define VDPASIM_NET_VQ_NUM	2
31*db1e8bb6SMax Gurtovoy 
32*db1e8bb6SMax Gurtovoy static char *macaddr;
33*db1e8bb6SMax Gurtovoy module_param(macaddr, charp, 0);
34*db1e8bb6SMax Gurtovoy MODULE_PARM_DESC(macaddr, "Ethernet MAC address");
35*db1e8bb6SMax Gurtovoy 
36*db1e8bb6SMax Gurtovoy u8 macaddr_buf[ETH_ALEN];
37*db1e8bb6SMax Gurtovoy 
38*db1e8bb6SMax Gurtovoy static struct vdpasim *vdpasim_net_dev;
39*db1e8bb6SMax Gurtovoy 
40*db1e8bb6SMax Gurtovoy static void vdpasim_net_work(struct work_struct *work)
41*db1e8bb6SMax Gurtovoy {
42*db1e8bb6SMax Gurtovoy 	struct vdpasim *vdpasim = container_of(work, struct vdpasim, work);
43*db1e8bb6SMax Gurtovoy 	struct vdpasim_virtqueue *txq = &vdpasim->vqs[1];
44*db1e8bb6SMax Gurtovoy 	struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0];
45*db1e8bb6SMax Gurtovoy 	ssize_t read, write;
46*db1e8bb6SMax Gurtovoy 	size_t total_write;
47*db1e8bb6SMax Gurtovoy 	int pkts = 0;
48*db1e8bb6SMax Gurtovoy 	int err;
49*db1e8bb6SMax Gurtovoy 
50*db1e8bb6SMax Gurtovoy 	spin_lock(&vdpasim->lock);
51*db1e8bb6SMax Gurtovoy 
52*db1e8bb6SMax Gurtovoy 	if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK))
53*db1e8bb6SMax Gurtovoy 		goto out;
54*db1e8bb6SMax Gurtovoy 
55*db1e8bb6SMax Gurtovoy 	if (!txq->ready || !rxq->ready)
56*db1e8bb6SMax Gurtovoy 		goto out;
57*db1e8bb6SMax Gurtovoy 
58*db1e8bb6SMax Gurtovoy 	while (true) {
59*db1e8bb6SMax Gurtovoy 		total_write = 0;
60*db1e8bb6SMax Gurtovoy 		err = vringh_getdesc_iotlb(&txq->vring, &txq->out_iov, NULL,
61*db1e8bb6SMax Gurtovoy 					   &txq->head, GFP_ATOMIC);
62*db1e8bb6SMax Gurtovoy 		if (err <= 0)
63*db1e8bb6SMax Gurtovoy 			break;
64*db1e8bb6SMax Gurtovoy 
65*db1e8bb6SMax Gurtovoy 		err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->in_iov,
66*db1e8bb6SMax Gurtovoy 					   &rxq->head, GFP_ATOMIC);
67*db1e8bb6SMax Gurtovoy 		if (err <= 0) {
68*db1e8bb6SMax Gurtovoy 			vringh_complete_iotlb(&txq->vring, txq->head, 0);
69*db1e8bb6SMax Gurtovoy 			break;
70*db1e8bb6SMax Gurtovoy 		}
71*db1e8bb6SMax Gurtovoy 
72*db1e8bb6SMax Gurtovoy 		while (true) {
73*db1e8bb6SMax Gurtovoy 			read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov,
74*db1e8bb6SMax Gurtovoy 						     vdpasim->buffer,
75*db1e8bb6SMax Gurtovoy 						     PAGE_SIZE);
76*db1e8bb6SMax Gurtovoy 			if (read <= 0)
77*db1e8bb6SMax Gurtovoy 				break;
78*db1e8bb6SMax Gurtovoy 
79*db1e8bb6SMax Gurtovoy 			write = vringh_iov_push_iotlb(&rxq->vring, &rxq->in_iov,
80*db1e8bb6SMax Gurtovoy 						      vdpasim->buffer, read);
81*db1e8bb6SMax Gurtovoy 			if (write <= 0)
82*db1e8bb6SMax Gurtovoy 				break;
83*db1e8bb6SMax Gurtovoy 
84*db1e8bb6SMax Gurtovoy 			total_write += write;
85*db1e8bb6SMax Gurtovoy 		}
86*db1e8bb6SMax Gurtovoy 
87*db1e8bb6SMax Gurtovoy 		/* Make sure data is wrote before advancing index */
88*db1e8bb6SMax Gurtovoy 		smp_wmb();
89*db1e8bb6SMax Gurtovoy 
90*db1e8bb6SMax Gurtovoy 		vringh_complete_iotlb(&txq->vring, txq->head, 0);
91*db1e8bb6SMax Gurtovoy 		vringh_complete_iotlb(&rxq->vring, rxq->head, total_write);
92*db1e8bb6SMax Gurtovoy 
93*db1e8bb6SMax Gurtovoy 		/* Make sure used is visible before rasing the interrupt. */
94*db1e8bb6SMax Gurtovoy 		smp_wmb();
95*db1e8bb6SMax Gurtovoy 
96*db1e8bb6SMax Gurtovoy 		local_bh_disable();
97*db1e8bb6SMax Gurtovoy 		if (vringh_need_notify_iotlb(&txq->vring) > 0)
98*db1e8bb6SMax Gurtovoy 			vringh_notify(&txq->vring);
99*db1e8bb6SMax Gurtovoy 		if (vringh_need_notify_iotlb(&rxq->vring) > 0)
100*db1e8bb6SMax Gurtovoy 			vringh_notify(&rxq->vring);
101*db1e8bb6SMax Gurtovoy 		local_bh_enable();
102*db1e8bb6SMax Gurtovoy 
103*db1e8bb6SMax Gurtovoy 		if (++pkts > 4) {
104*db1e8bb6SMax Gurtovoy 			schedule_work(&vdpasim->work);
105*db1e8bb6SMax Gurtovoy 			goto out;
106*db1e8bb6SMax Gurtovoy 		}
107*db1e8bb6SMax Gurtovoy 	}
108*db1e8bb6SMax Gurtovoy 
109*db1e8bb6SMax Gurtovoy out:
110*db1e8bb6SMax Gurtovoy 	spin_unlock(&vdpasim->lock);
111*db1e8bb6SMax Gurtovoy }
112*db1e8bb6SMax Gurtovoy 
113*db1e8bb6SMax Gurtovoy static void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config)
114*db1e8bb6SMax Gurtovoy {
115*db1e8bb6SMax Gurtovoy 	struct virtio_net_config *net_config =
116*db1e8bb6SMax Gurtovoy 		(struct virtio_net_config *)config;
117*db1e8bb6SMax Gurtovoy 
118*db1e8bb6SMax Gurtovoy 	net_config->mtu = cpu_to_vdpasim16(vdpasim, 1500);
119*db1e8bb6SMax Gurtovoy 	net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP);
120*db1e8bb6SMax Gurtovoy 	memcpy(net_config->mac, macaddr_buf, ETH_ALEN);
121*db1e8bb6SMax Gurtovoy }
122*db1e8bb6SMax Gurtovoy 
123*db1e8bb6SMax Gurtovoy static int __init vdpasim_net_init(void)
124*db1e8bb6SMax Gurtovoy {
125*db1e8bb6SMax Gurtovoy 	struct vdpasim_dev_attr dev_attr = {};
126*db1e8bb6SMax Gurtovoy 	int ret;
127*db1e8bb6SMax Gurtovoy 
128*db1e8bb6SMax Gurtovoy 	if (macaddr) {
129*db1e8bb6SMax Gurtovoy 		mac_pton(macaddr, macaddr_buf);
130*db1e8bb6SMax Gurtovoy 		if (!is_valid_ether_addr(macaddr_buf)) {
131*db1e8bb6SMax Gurtovoy 			ret = -EADDRNOTAVAIL;
132*db1e8bb6SMax Gurtovoy 			goto out;
133*db1e8bb6SMax Gurtovoy 		}
134*db1e8bb6SMax Gurtovoy 	} else {
135*db1e8bb6SMax Gurtovoy 		eth_random_addr(macaddr_buf);
136*db1e8bb6SMax Gurtovoy 	}
137*db1e8bb6SMax Gurtovoy 
138*db1e8bb6SMax Gurtovoy 	dev_attr.id = VIRTIO_ID_NET;
139*db1e8bb6SMax Gurtovoy 	dev_attr.supported_features = VDPASIM_NET_FEATURES;
140*db1e8bb6SMax Gurtovoy 	dev_attr.nvqs = VDPASIM_NET_VQ_NUM;
141*db1e8bb6SMax Gurtovoy 	dev_attr.config_size = sizeof(struct virtio_net_config);
142*db1e8bb6SMax Gurtovoy 	dev_attr.get_config = vdpasim_net_get_config;
143*db1e8bb6SMax Gurtovoy 	dev_attr.work_fn = vdpasim_net_work;
144*db1e8bb6SMax Gurtovoy 	dev_attr.buffer_size = PAGE_SIZE;
145*db1e8bb6SMax Gurtovoy 
146*db1e8bb6SMax Gurtovoy 	vdpasim_net_dev = vdpasim_create(&dev_attr);
147*db1e8bb6SMax Gurtovoy 	if (IS_ERR(vdpasim_net_dev)) {
148*db1e8bb6SMax Gurtovoy 		ret = PTR_ERR(vdpasim_net_dev);
149*db1e8bb6SMax Gurtovoy 		goto out;
150*db1e8bb6SMax Gurtovoy 	}
151*db1e8bb6SMax Gurtovoy 
152*db1e8bb6SMax Gurtovoy 	ret = vdpa_register_device(&vdpasim_net_dev->vdpa);
153*db1e8bb6SMax Gurtovoy 	if (ret)
154*db1e8bb6SMax Gurtovoy 		goto put_dev;
155*db1e8bb6SMax Gurtovoy 
156*db1e8bb6SMax Gurtovoy 	return 0;
157*db1e8bb6SMax Gurtovoy 
158*db1e8bb6SMax Gurtovoy put_dev:
159*db1e8bb6SMax Gurtovoy 	put_device(&vdpasim_net_dev->vdpa.dev);
160*db1e8bb6SMax Gurtovoy out:
161*db1e8bb6SMax Gurtovoy 	return ret;
162*db1e8bb6SMax Gurtovoy }
163*db1e8bb6SMax Gurtovoy 
164*db1e8bb6SMax Gurtovoy static void __exit vdpasim_net_exit(void)
165*db1e8bb6SMax Gurtovoy {
166*db1e8bb6SMax Gurtovoy 	struct vdpa_device *vdpa = &vdpasim_net_dev->vdpa;
167*db1e8bb6SMax Gurtovoy 
168*db1e8bb6SMax Gurtovoy 	vdpa_unregister_device(vdpa);
169*db1e8bb6SMax Gurtovoy }
170*db1e8bb6SMax Gurtovoy 
171*db1e8bb6SMax Gurtovoy module_init(vdpasim_net_init);
172*db1e8bb6SMax Gurtovoy module_exit(vdpasim_net_exit);
173*db1e8bb6SMax Gurtovoy 
174*db1e8bb6SMax Gurtovoy MODULE_VERSION(DRV_VERSION);
175*db1e8bb6SMax Gurtovoy MODULE_LICENSE(DRV_LICENSE);
176*db1e8bb6SMax Gurtovoy MODULE_AUTHOR(DRV_AUTHOR);
177*db1e8bb6SMax Gurtovoy MODULE_DESCRIPTION(DRV_DESC);
178