1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * VDPA simulator for networking device. 4 * 5 * Copyright (c) 2020, Red Hat Inc. All rights reserved. 6 * Author: Jason Wang <jasowang@redhat.com> 7 * 8 */ 9 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/device.h> 13 #include <linux/kernel.h> 14 #include <linux/sched.h> 15 #include <linux/etherdevice.h> 16 #include <linux/vringh.h> 17 #include <linux/vdpa.h> 18 #include <uapi/linux/virtio_net.h> 19 #include <uapi/linux/vdpa.h> 20 21 #include "vdpa_sim.h" 22 23 #define DRV_VERSION "0.1" 24 #define DRV_AUTHOR "Jason Wang <jasowang@redhat.com>" 25 #define DRV_DESC "vDPA Device Simulator for networking device" 26 #define DRV_LICENSE "GPL v2" 27 28 #define VDPASIM_NET_FEATURES (VDPASIM_FEATURES | \ 29 (1ULL << VIRTIO_NET_F_MAC) | \ 30 (1ULL << VIRTIO_NET_F_STATUS) | \ 31 (1ULL << VIRTIO_NET_F_MTU) | \ 32 (1ULL << VIRTIO_NET_F_CTRL_VQ) | \ 33 (1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR)) 34 35 /* 3 virtqueues, 2 address spaces, 2 virtqueue groups */ 36 #define VDPASIM_NET_VQ_NUM 3 37 #define VDPASIM_NET_AS_NUM 2 38 #define VDPASIM_NET_GROUP_NUM 2 39 40 static void vdpasim_net_complete(struct vdpasim_virtqueue *vq, size_t len) 41 { 42 /* Make sure data is wrote before advancing index */ 43 smp_wmb(); 44 45 vringh_complete_iotlb(&vq->vring, vq->head, len); 46 47 /* Make sure used is visible before rasing the interrupt. */ 48 smp_wmb(); 49 50 local_bh_disable(); 51 if (vringh_need_notify_iotlb(&vq->vring) > 0) 52 vringh_notify(&vq->vring); 53 local_bh_enable(); 54 } 55 56 static bool receive_filter(struct vdpasim *vdpasim, size_t len) 57 { 58 bool modern = vdpasim->features & (1ULL << VIRTIO_F_VERSION_1); 59 size_t hdr_len = modern ? sizeof(struct virtio_net_hdr_v1) : 60 sizeof(struct virtio_net_hdr); 61 struct virtio_net_config *vio_config = vdpasim->config; 62 63 if (len < ETH_ALEN + hdr_len) 64 return false; 65 66 if (is_broadcast_ether_addr(vdpasim->buffer + hdr_len) || 67 is_multicast_ether_addr(vdpasim->buffer + hdr_len)) 68 return true; 69 if (!strncmp(vdpasim->buffer + hdr_len, vio_config->mac, ETH_ALEN)) 70 return true; 71 72 return false; 73 } 74 75 static virtio_net_ctrl_ack vdpasim_handle_ctrl_mac(struct vdpasim *vdpasim, 76 u8 cmd) 77 { 78 struct virtio_net_config *vio_config = vdpasim->config; 79 struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2]; 80 virtio_net_ctrl_ack status = VIRTIO_NET_ERR; 81 size_t read; 82 83 switch (cmd) { 84 case VIRTIO_NET_CTRL_MAC_ADDR_SET: 85 read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov, 86 vio_config->mac, ETH_ALEN); 87 if (read == ETH_ALEN) 88 status = VIRTIO_NET_OK; 89 break; 90 default: 91 break; 92 } 93 94 return status; 95 } 96 97 static void vdpasim_handle_cvq(struct vdpasim *vdpasim) 98 { 99 struct vdpasim_virtqueue *cvq = &vdpasim->vqs[2]; 100 virtio_net_ctrl_ack status = VIRTIO_NET_ERR; 101 struct virtio_net_ctrl_hdr ctrl; 102 size_t read, write; 103 int err; 104 105 if (!(vdpasim->features & (1ULL << VIRTIO_NET_F_CTRL_VQ))) 106 return; 107 108 if (!cvq->ready) 109 return; 110 111 while (true) { 112 err = vringh_getdesc_iotlb(&cvq->vring, &cvq->in_iov, 113 &cvq->out_iov, 114 &cvq->head, GFP_ATOMIC); 115 if (err <= 0) 116 break; 117 118 read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->in_iov, &ctrl, 119 sizeof(ctrl)); 120 if (read != sizeof(ctrl)) 121 break; 122 123 switch (ctrl.class) { 124 case VIRTIO_NET_CTRL_MAC: 125 status = vdpasim_handle_ctrl_mac(vdpasim, ctrl.cmd); 126 break; 127 default: 128 break; 129 } 130 131 /* Make sure data is wrote before advancing index */ 132 smp_wmb(); 133 134 write = vringh_iov_push_iotlb(&cvq->vring, &cvq->out_iov, 135 &status, sizeof(status)); 136 vringh_complete_iotlb(&cvq->vring, cvq->head, write); 137 vringh_kiov_cleanup(&cvq->in_iov); 138 vringh_kiov_cleanup(&cvq->out_iov); 139 140 /* Make sure used is visible before rasing the interrupt. */ 141 smp_wmb(); 142 143 local_bh_disable(); 144 if (cvq->cb) 145 cvq->cb(cvq->private); 146 local_bh_enable(); 147 } 148 } 149 150 static void vdpasim_net_work(struct work_struct *work) 151 { 152 struct vdpasim *vdpasim = container_of(work, struct vdpasim, work); 153 struct vdpasim_virtqueue *txq = &vdpasim->vqs[1]; 154 struct vdpasim_virtqueue *rxq = &vdpasim->vqs[0]; 155 ssize_t read, write; 156 int pkts = 0; 157 int err; 158 159 spin_lock(&vdpasim->lock); 160 161 if (!vdpasim->running) 162 goto out; 163 164 if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK)) 165 goto out; 166 167 vdpasim_handle_cvq(vdpasim); 168 169 if (!txq->ready || !rxq->ready) 170 goto out; 171 172 while (true) { 173 err = vringh_getdesc_iotlb(&txq->vring, &txq->out_iov, NULL, 174 &txq->head, GFP_ATOMIC); 175 if (err <= 0) 176 break; 177 178 read = vringh_iov_pull_iotlb(&txq->vring, &txq->out_iov, 179 vdpasim->buffer, 180 PAGE_SIZE); 181 182 if (!receive_filter(vdpasim, read)) { 183 vdpasim_net_complete(txq, 0); 184 continue; 185 } 186 187 err = vringh_getdesc_iotlb(&rxq->vring, NULL, &rxq->in_iov, 188 &rxq->head, GFP_ATOMIC); 189 if (err <= 0) { 190 vdpasim_net_complete(txq, 0); 191 break; 192 } 193 194 write = vringh_iov_push_iotlb(&rxq->vring, &rxq->in_iov, 195 vdpasim->buffer, read); 196 if (write <= 0) 197 break; 198 199 vdpasim_net_complete(txq, 0); 200 vdpasim_net_complete(rxq, write); 201 202 if (++pkts > 4) { 203 schedule_work(&vdpasim->work); 204 goto out; 205 } 206 } 207 208 out: 209 spin_unlock(&vdpasim->lock); 210 } 211 212 static void vdpasim_net_get_config(struct vdpasim *vdpasim, void *config) 213 { 214 struct virtio_net_config *net_config = config; 215 216 net_config->status = cpu_to_vdpasim16(vdpasim, VIRTIO_NET_S_LINK_UP); 217 } 218 219 static void vdpasim_net_setup_config(struct vdpasim *vdpasim, 220 const struct vdpa_dev_set_config *config) 221 { 222 struct virtio_net_config *vio_config = vdpasim->config; 223 224 if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) 225 memcpy(vio_config->mac, config->net.mac, ETH_ALEN); 226 if (config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MTU)) 227 vio_config->mtu = cpu_to_vdpasim16(vdpasim, config->net.mtu); 228 else 229 /* Setup default MTU to be 1500 */ 230 vio_config->mtu = cpu_to_vdpasim16(vdpasim, 1500); 231 } 232 233 static void vdpasim_net_mgmtdev_release(struct device *dev) 234 { 235 } 236 237 static struct device vdpasim_net_mgmtdev = { 238 .init_name = "vdpasim_net", 239 .release = vdpasim_net_mgmtdev_release, 240 }; 241 242 static int vdpasim_net_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, 243 const struct vdpa_dev_set_config *config) 244 { 245 struct vdpasim_dev_attr dev_attr = {}; 246 struct vdpasim *simdev; 247 int ret; 248 249 dev_attr.mgmt_dev = mdev; 250 dev_attr.name = name; 251 dev_attr.id = VIRTIO_ID_NET; 252 dev_attr.supported_features = VDPASIM_NET_FEATURES; 253 dev_attr.nvqs = VDPASIM_NET_VQ_NUM; 254 dev_attr.ngroups = VDPASIM_NET_GROUP_NUM; 255 dev_attr.nas = VDPASIM_NET_AS_NUM; 256 dev_attr.config_size = sizeof(struct virtio_net_config); 257 dev_attr.get_config = vdpasim_net_get_config; 258 dev_attr.work_fn = vdpasim_net_work; 259 dev_attr.buffer_size = PAGE_SIZE; 260 261 simdev = vdpasim_create(&dev_attr, config); 262 if (IS_ERR(simdev)) 263 return PTR_ERR(simdev); 264 265 vdpasim_net_setup_config(simdev, config); 266 267 ret = _vdpa_register_device(&simdev->vdpa, VDPASIM_NET_VQ_NUM); 268 if (ret) 269 goto reg_err; 270 271 return 0; 272 273 reg_err: 274 put_device(&simdev->vdpa.dev); 275 return ret; 276 } 277 278 static void vdpasim_net_dev_del(struct vdpa_mgmt_dev *mdev, 279 struct vdpa_device *dev) 280 { 281 struct vdpasim *simdev = container_of(dev, struct vdpasim, vdpa); 282 283 _vdpa_unregister_device(&simdev->vdpa); 284 } 285 286 static const struct vdpa_mgmtdev_ops vdpasim_net_mgmtdev_ops = { 287 .dev_add = vdpasim_net_dev_add, 288 .dev_del = vdpasim_net_dev_del 289 }; 290 291 static struct virtio_device_id id_table[] = { 292 { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, 293 { 0 }, 294 }; 295 296 static struct vdpa_mgmt_dev mgmt_dev = { 297 .device = &vdpasim_net_mgmtdev, 298 .id_table = id_table, 299 .ops = &vdpasim_net_mgmtdev_ops, 300 .config_attr_mask = (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR | 301 1 << VDPA_ATTR_DEV_NET_CFG_MTU | 302 1 << VDPA_ATTR_DEV_FEATURES), 303 .max_supported_vqs = VDPASIM_NET_VQ_NUM, 304 .supported_features = VDPASIM_NET_FEATURES, 305 }; 306 307 static int __init vdpasim_net_init(void) 308 { 309 int ret; 310 311 ret = device_register(&vdpasim_net_mgmtdev); 312 if (ret) { 313 put_device(&vdpasim_net_mgmtdev); 314 return ret; 315 } 316 317 ret = vdpa_mgmtdev_register(&mgmt_dev); 318 if (ret) 319 goto parent_err; 320 return 0; 321 322 parent_err: 323 device_unregister(&vdpasim_net_mgmtdev); 324 return ret; 325 } 326 327 static void __exit vdpasim_net_exit(void) 328 { 329 vdpa_mgmtdev_unregister(&mgmt_dev); 330 device_unregister(&vdpasim_net_mgmtdev); 331 } 332 333 module_init(vdpasim_net_init); 334 module_exit(vdpasim_net_exit); 335 336 MODULE_VERSION(DRV_VERSION); 337 MODULE_LICENSE(DRV_LICENSE); 338 MODULE_AUTHOR(DRV_AUTHOR); 339 MODULE_DESCRIPTION(DRV_DESC); 340