| @@ -38,6 +38,132 @@ static bool vp_is_avq(struct virtio_device *vdev, unsigned int index) · 37 unmodified lines |
| 38 | return index == vp_dev->admin_vq.vq_index; | 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 | static void vp_transport_features(struct virtio_device *vdev, u64 features) | 167 | static void vp_transport_features(struct virtio_device *vdev, u64 features) |
| 42 | { | 168 | { |
| 43 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 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 | /* We should never be setting status to 0. */ | 339 | /* We should never be setting status to 0. */ |
| 214 | BUG_ON(status == 0); | 340 | BUG_ON(status == 0); |
| 215 | vp_modern_set_status(&vp_dev->mdev, status); | 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 | static void vp_reset(struct virtio_device *vdev) | 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 | while (vp_modern_get_status(mdev)) | 358 | while (vp_modern_get_status(mdev)) |
| 231 | msleep(1); | 359 | msleep(1); |
| | 360 | |
| | 361 | vp_modern_avq_deactivate(vdev); |
| | 362 | |
| 232 | /* Flush pending VQ/configuration callbacks. */ | 363 | /* Flush pending VQ/configuration callbacks. */ |
| 233 | vp_synchronize_vectors(vdev); | 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 | goto err; | 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 | vp_dev->admin_vq.info.vq = vq; | 540 | vp_dev->admin_vq.info.vq = vq; |
| | 541 | mutex_unlock(&vp_dev->admin_vq.cmd_lock); |
| | 542 | } |
| 409 | | 543 | |
| 410 | return vq; | 544 | return vq; |
| 411 | | 545 | |
| @@ -442,8 +576,11 @@ static void del_vq(struct virtio_pci_vq_info *info) · 30 unmodified lines |
| 442 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); | 576 | struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); |
| 443 | struct virtio_pci_modern_device *mdev = &vp_dev->mdev; | 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 | vp_dev->admin_vq.info.vq = NULL; | 581 | vp_dev->admin_vq.info.vq = NULL; |
| | 582 | mutex_unlock(&vp_dev->admin_vq.cmd_lock); |
| | 583 | } |
| 447 | | 584 | |
| 448 | if (vp_dev->msix_enabled) | 585 | if (vp_dev->msix_enabled) |
| 449 | vp_modern_queue_vector(mdev, vq->index, | 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 | vp_dev->isr = mdev->isr; | 799 | vp_dev->isr = mdev->isr; |
| 663 | vp_dev->vdev.id = mdev->id; | 800 | vp_dev->vdev.id = mdev->id; |
| 664 | | 801 | |
| | 802 | mutex_init(&vp_dev->admin_vq.cmd_lock); |
| 665 | return 0; | 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 | struct virtio_pci_modern_device *mdev = &vp_dev->mdev; | 808 | struct virtio_pci_modern_device *mdev = &vp_dev->mdev; |
| 671 | | 809 | |
| | 810 | mutex_destroy(&vp_dev->admin_vq.cmd_lock); |
| 672 | vp_modern_remove(mdev); | 811 | vp_modern_remove(mdev); |
| 673 | } | 812 | } |