| @@ -38,6 +38,132 @@ static bool vp_is_avq(struct virtio_device *vdev, unsigned int index) · 37 unmodified lines |
| 38 | 38 | return index == vp_dev->admin_vq.vq_index; |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | +static int virtqueue_exec_admin_cmd(struct virtio_pci_admin_vq *admin_vq, |
| 42 | + struct scatterlist **sgs, |
| 43 | + unsigned int out_num, |
| 44 | + unsigned int in_num, |
| 45 | + void *data) |
| 46 | +{ |
| 47 | + struct virtqueue *vq; |
| 48 | + int ret, len; |
| 49 | + |
| 50 | + vq = admin_vq->info.vq; |
| 51 | + if (!vq) |
| 52 | + return -EIO; |
| 53 | + |
| 54 | + ret = virtqueue_add_sgs(vq, sgs, out_num, in_num, data, GFP_KERNEL); |
| 55 | + if (ret < 0) |
| 56 | + return -EIO; |
| 57 | + |
| 58 | + if (unlikely(!virtqueue_kick(vq))) |
| 59 | + return -EIO; |
| 60 | + |
| 61 | + while (!virtqueue_get_buf(vq, &len) && |
| 62 | + !virtqueue_is_broken(vq)) |
| 63 | + cpu_relax(); |
| 64 | + |
| 65 | + if (virtqueue_is_broken(vq)) |
| 66 | + return -EIO; |
| 67 | + |
| 68 | + return 0; |
| 69 | +} |
| 70 | + |
| 71 | +int vp_modern_admin_cmd_exec(struct virtio_device *vdev, |
| 72 | + struct virtio_admin_cmd *cmd) |
| 73 | +{ |
| 74 | + struct scatterlist *sgs[VIRTIO_AVQ_SGS_MAX], hdr, stat; |
| 75 | + struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 76 | + struct virtio_admin_cmd_status *va_status; |
| 77 | + unsigned int out_num = 0, in_num = 0; |
| 78 | + struct virtio_admin_cmd_hdr *va_hdr; |
| 79 | + u16 status; |
| 80 | + int ret; |
| 81 | + |
| 82 | + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) |
| 83 | + return -EOPNOTSUPP; |
| 84 | + |
| 85 | + va_status = kzalloc(sizeof(*va_status), GFP_KERNEL); |
| 86 | + if (!va_status) |
| 87 | + return -ENOMEM; |
| 88 | + |
| 89 | + va_hdr = kzalloc(sizeof(*va_hdr), GFP_KERNEL); |
| 90 | + if (!va_hdr) { |
| 91 | + ret = -ENOMEM; |
| 92 | + goto err_alloc; |
| 93 | + } |
| 94 | + |
| 95 | + va_hdr->opcode = cmd->opcode; |
| 96 | + va_hdr->group_type = cmd->group_type; |
| 97 | + va_hdr->group_member_id = cmd->group_member_id; |
| 98 | + |
| 99 | + /* Add header */ |
| 100 | + sg_init_one(&hdr, va_hdr, sizeof(*va_hdr)); |
| 101 | + sgs[out_num] = &hdr; |
| 102 | + out_num++; |
| 103 | + |
| 104 | + if (cmd->data_sg) { |
| 105 | + sgs[out_num] = cmd->data_sg; |
| 106 | + out_num++; |
| 107 | + } |
| 108 | + |
| 109 | + /* Add return status */ |
| 110 | + sg_init_one(&stat, va_status, sizeof(*va_status)); |
| 111 | + sgs[out_num + in_num] = &stat; |
| 112 | + in_num++; |
| 113 | + |
| 114 | + if (cmd->result_sg) { |
| 115 | + sgs[out_num + in_num] = cmd->result_sg; |
| 116 | + in_num++; |
| 117 | + } |
| 118 | + |
| 119 | + mutex_lock(&vp_dev->admin_vq.cmd_lock); |
| 120 | + ret = virtqueue_exec_admin_cmd(&vp_dev->admin_vq, sgs, |
| 121 | + out_num, in_num, sgs); |
| 122 | + mutex_unlock(&vp_dev->admin_vq.cmd_lock); |
| 123 | + |
| 124 | + if (ret) { |
| 125 | + dev_err(&vdev->dev, |
| 126 | + "Failed to execute command on admin vq: %d\n.", ret); |
| 127 | + goto err_cmd_exec; |
| 128 | + } |
| 129 | + |
| 130 | + status = le16_to_cpu(va_status->status); |
| 131 | + if (status != VIRTIO_ADMIN_STATUS_OK) { |
| 132 | + dev_err(&vdev->dev, |
| 133 | + "admin command error: status(%#x) qualifier(%#x)\n", |
| 134 | + status, le16_to_cpu(va_status->status_qualifier)); |
| 135 | + ret = -status; |
| 136 | + } |
| 137 | + |
| 138 | +err_cmd_exec: |
| 139 | + kfree(va_hdr); |
| 140 | +err_alloc: |
| 141 | + kfree(va_status); |
| 142 | + return ret; |
| 143 | +} |
| 144 | + |
| 145 | +static void vp_modern_avq_activate(struct virtio_device *vdev) |
| 146 | +{ |
| 147 | + struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 148 | + struct virtio_pci_admin_vq *admin_vq = &vp_dev->admin_vq; |
| 149 | + |
| 150 | + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) |
| 151 | + return; |
| 152 | + |
| 153 | + __virtqueue_unbreak(admin_vq->info.vq); |
| 154 | +} |
| 155 | + |
| 156 | +static void vp_modern_avq_deactivate(struct virtio_device *vdev) |
| 157 | +{ |
| 158 | + struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| 159 | + struct virtio_pci_admin_vq *admin_vq = &vp_dev->admin_vq; |
| 160 | + |
| 161 | + if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ)) |
| 162 | + return; |
| 163 | + |
| 164 | + __virtqueue_break(admin_vq->info.vq); |
| 165 | +} |
| 166 | + |
| 41 | 167 | static void vp_transport_features(struct virtio_device *vdev, u64 features) |
| 42 | 168 | { |
| 43 | 169 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
| @@ -213,6 +339,8 @@ static void vp_set_status(struct virtio_device *vdev, u8 status) · 169 unmodified lines |
| 213 | 339 | /* We should never be setting status to 0. */ |
| 214 | 340 | BUG_ON(status == 0); |
| 215 | 341 | vp_modern_set_status(&vp_dev->mdev, status); |
| 342 | + if (status & VIRTIO_CONFIG_S_DRIVER_OK) |
| 343 | + vp_modern_avq_activate(vdev); |
| 216 | 344 | } |
| 217 | 345 | |
| 218 | 346 | static void vp_reset(struct virtio_device *vdev) |
| @@ -229,6 +357,9 @@ static void vp_reset(struct virtio_device *vdev) · 10 unmodified lines |
| 229 | 357 | */ |
| 230 | 358 | while (vp_modern_get_status(mdev)) |
| 231 | 359 | msleep(1); |
| 360 | + |
| 361 | + vp_modern_avq_deactivate(vdev); |
| 362 | + |
| 232 | 363 | /* Flush pending VQ/configuration callbacks. */ |
| 233 | 364 | vp_synchronize_vectors(vdev); |
| 234 | 365 | } |
| @@ -404,8 +535,11 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, · 169 unmodified lines |
| 404 | 535 | goto err; |
| 405 | 536 | } |
| 406 | 537 | |
| 407 | | − if (is_avq) |
| 538 | + if (is_avq) { |
| 539 | + mutex_lock(&vp_dev->admin_vq.cmd_lock); |
| 408 | 540 | vp_dev->admin_vq.info.vq = vq; |
| 541 | + mutex_unlock(&vp_dev->admin_vq.cmd_lock); |
| 542 | + } |
| 409 | 543 | |
| 410 | 544 | return vq; |
| 411 | 545 | |
| @@ -442,8 +576,11 @@ static void del_vq(struct virtio_pci_vq_info *info) · 30 unmodified lines |
| 442 | 576 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); |
| 443 | 577 | struct virtio_pci_modern_device *mdev = &vp_dev->mdev; |
| 444 | 578 | |
| 445 | | − if (vp_is_avq(&vp_dev->vdev, vq->index)) |
| 579 | + if (vp_is_avq(&vp_dev->vdev, vq->index)) { |
| 580 | + mutex_lock(&vp_dev->admin_vq.cmd_lock); |
| 446 | 581 | vp_dev->admin_vq.info.vq = NULL; |
| 582 | + mutex_unlock(&vp_dev->admin_vq.cmd_lock); |
| 583 | + } |
| 447 | 584 | |
| 448 | 585 | if (vp_dev->msix_enabled) |
| 449 | 586 | vp_modern_queue_vector(mdev, vq->index, |
| @@ -662,6 +799,7 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) · 212 unmodified lines |
| 662 | 799 | vp_dev->isr = mdev->isr; |
| 663 | 800 | vp_dev->vdev.id = mdev->id; |
| 664 | 801 | |
| 802 | + mutex_init(&vp_dev->admin_vq.cmd_lock); |
| 665 | 803 | return 0; |
| 666 | 804 | } |
| 667 | 805 | |
| @@ -669,5 +807,6 @@ void virtio_pci_modern_remove(struct virtio_pci_device *vp_dev) · 1 unmodified line |
| 669 | 807 | { |
| 670 | 808 | struct virtio_pci_modern_device *mdev = &vp_dev->mdev; |
| 671 | 809 | |
| 810 | + mutex_destroy(&vp_dev->admin_vq.cmd_lock); |
| 672 | 811 | vp_modern_remove(mdev); |
| 673 | 812 | } |