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