drivers: virtio: fix cleanup upon failure during virtqueues initialization
This commit fixes infinite loop reported in https://github.com/zephyrproject-rtos/zephyr/issues/91242, not deallocating array with virtqueues and not deactivating the virtqueues in case of failure Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
This commit is contained in:
parent
d5bb10bf98
commit
c47cc3abda
2 changed files with 47 additions and 20 deletions
|
@ -246,30 +246,43 @@ static int virtio_mmio_set_virtqueues(const struct device *dev, uint16_t queue_c
|
||||||
}
|
}
|
||||||
data->virtqueue_count = queue_count;
|
data->virtqueue_count = queue_count;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
int created_queues = 0;
|
||||||
|
int activated_queues = 0;
|
||||||
|
|
||||||
for (int i = 0; i < queue_count; i++) {
|
for (int i = 0; i < queue_count; i++) {
|
||||||
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SEL, i);
|
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SEL, i);
|
||||||
|
|
||||||
const uint16_t queue_size =
|
const uint16_t queue_size =
|
||||||
cb(i, virtio_mmio_read32(dev, VIRTIO_MMIO_QUEUE_SIZE_MAX), opaque);
|
cb(i, virtio_mmio_read32(dev, VIRTIO_MMIO_QUEUE_SIZE_MAX), opaque);
|
||||||
|
|
||||||
int ret = virtq_create(&data->virtqueues[i], queue_size);
|
ret = virtq_create(&data->virtqueues[i], queue_size);
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
for (int j = 0; j < i; i++) {
|
goto fail;
|
||||||
virtq_free(&data->virtqueues[j]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
created_queues++;
|
||||||
|
|
||||||
ret = virtio_mmio_set_virtqueue(dev, i, &data->virtqueues[i]);
|
ret = virtio_mmio_set_virtqueue(dev, i, &data->virtqueues[i]);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
for (int j = 0; j < i; i++) {
|
goto fail;
|
||||||
virtq_free(&data->virtqueues[j]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
activated_queues++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
for (int j = 0; j < activated_queues; j++) {
|
||||||
|
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SEL, j);
|
||||||
|
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_READY, 0);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < created_queues; j++) {
|
||||||
|
virtq_free(&data->virtqueues[j]);
|
||||||
|
}
|
||||||
|
k_free(data->virtqueues);
|
||||||
|
data->virtqueue_count = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int virtio_mmio_init_virtqueues(const struct device *dev, uint16_t num_queues,
|
static int virtio_mmio_init_virtqueues(const struct device *dev, uint16_t num_queues,
|
||||||
|
|
|
@ -271,30 +271,44 @@ static int virtio_pci_init_virtqueues(
|
||||||
}
|
}
|
||||||
data->virtqueue_count = queue_count;
|
data->virtqueue_count = queue_count;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
int created_queues = 0;
|
||||||
|
int activated_queues = 0;
|
||||||
|
|
||||||
for (int i = 0; i < queue_count; i++) {
|
for (int i = 0; i < queue_count; i++) {
|
||||||
data->common_cfg->queue_select = sys_cpu_to_le16(i);
|
data->common_cfg->queue_select = sys_cpu_to_le16(i);
|
||||||
barrier_dmem_fence_full();
|
barrier_dmem_fence_full();
|
||||||
|
|
||||||
uint16_t queue_size = cb(i, sys_le16_to_cpu(data->common_cfg->queue_size), opaque);
|
uint16_t queue_size = cb(i, sys_le16_to_cpu(data->common_cfg->queue_size), opaque);
|
||||||
|
|
||||||
int ret = virtq_create(&data->virtqueues[i], queue_size);
|
ret = virtq_create(&data->virtqueues[i], queue_size);
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
for (int j = 0; j < i; i++) {
|
goto fail;
|
||||||
virtq_free(&data->virtqueues[j]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
created_queues++;
|
||||||
|
|
||||||
ret = virtio_pci_set_virtqueue(dev, i, &data->virtqueues[i]);
|
ret = virtio_pci_set_virtqueue(dev, i, &data->virtqueues[i]);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
for (int j = 0; j < i; i++) {
|
goto fail;
|
||||||
virtq_free(&data->virtqueues[j]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
activated_queues++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
for (int j = 0; j < activated_queues; j++) {
|
||||||
|
data->common_cfg->queue_select = sys_cpu_to_le16(j);
|
||||||
|
barrier_dmem_fence_full();
|
||||||
|
data->common_cfg->queue_enable = sys_cpu_to_le16(0);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < created_queues; j++) {
|
||||||
|
virtq_free(&data->virtqueues[j]);
|
||||||
|
}
|
||||||
|
k_free(data->virtqueues);
|
||||||
|
data->virtqueue_count = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool virtio_pci_map_cap(pcie_bdf_t bdf, struct virtio_pci_cap *cap, void **virt_ptr)
|
static bool virtio_pci_map_cap(pcie_bdf_t bdf, struct virtio_pci_cap *cap, void **virt_ptr)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue