Commit 90357083 authored by Alex Williamson's avatar Alex Williamson Committed by Alex Williamson
Browse files

vfio/virtio: Convert list_lock from spinlock to mutex



The list_lock spinlock with IRQ disabling was copied from the mlx5
vfio-pci variant driver, where it is justified by a hardirq async
command completion callback that accesses the protected lists.  The
virtio driver has no such interrupt context usage; all list_lock
acquisitions occur in process context via file read/write operations
or state transitions under state_mutex.

Convert list_lock to a mutex to be consistent with peer vfio-pci
variant drivers (hisilicon, pds, qat, xe) which all use mutexes for
equivalent migration data protection.  This also fixes a mismatched
spin_lock()/spin_unlock_irq() pair in virtiovf_read_device_context_chunk()
that could incorrectly enable interrupts.

Reported-by: default avatarJinhui Guo <guojinhui.liam@bytedance.com>
Closes: https://lore.kernel.org/all/20260413073603.30538-1-guojinhui.liam@bytedance.com


Fixes: 0bbc82e4 ("vfio/virtio: Add support for the basic live migration functionality")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: default avatarAlex Williamson <alex.williamson@nvidia.com>
Reviewed-by: default avatarYishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20260414200625.3601509-2-alex.williamson@nvidia.com


Signed-off-by: default avatarAlex Williamson <alex@shazbot.org>
parent d9770870
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ struct virtiovf_migration_file {
	enum virtiovf_migf_state state;
	enum virtiovf_load_state load_state;
	/* synchronize access to the lists */
	spinlock_t list_lock;
	struct mutex list_lock;
	struct list_head buf_list;
	struct list_head avail_list;
	struct virtiovf_data_buffer *buf;
+17 −16
Original line number Diff line number Diff line
@@ -142,9 +142,9 @@ virtiovf_alloc_data_buffer(struct virtiovf_migration_file *migf, size_t length)

static void virtiovf_put_data_buffer(struct virtiovf_data_buffer *buf)
{
	spin_lock_irq(&buf->migf->list_lock);
	mutex_lock(&buf->migf->list_lock);
	list_add_tail(&buf->buf_elm, &buf->migf->avail_list);
	spin_unlock_irq(&buf->migf->list_lock);
	mutex_unlock(&buf->migf->list_lock);
}

static int
@@ -170,21 +170,21 @@ virtiovf_get_data_buffer(struct virtiovf_migration_file *migf, size_t length)

	INIT_LIST_HEAD(&free_list);

	spin_lock_irq(&migf->list_lock);
	mutex_lock(&migf->list_lock);
	list_for_each_entry_safe(buf, temp_buf, &migf->avail_list, buf_elm) {
		list_del_init(&buf->buf_elm);
		if (buf->allocated_length >= length) {
			spin_unlock_irq(&migf->list_lock);
			mutex_unlock(&migf->list_lock);
			goto found;
		}
		/*
		 * Prevent holding redundant buffers. Put in a free
		 * list and call at the end not under the spin lock
		 * list and call at the end not under the mutex
		 * (&migf->list_lock) to minimize its scope usage.
		 */
		list_add(&buf->buf_elm, &free_list);
	}
	spin_unlock_irq(&migf->list_lock);
	mutex_unlock(&migf->list_lock);
	buf = virtiovf_alloc_data_buffer(migf, length);

found:
@@ -295,6 +295,7 @@ static int virtiovf_release_file(struct inode *inode, struct file *filp)
	struct virtiovf_migration_file *migf = filp->private_data;

	virtiovf_disable_fd(migf);
	mutex_destroy(&migf->list_lock);
	mutex_destroy(&migf->lock);
	kfree(migf);
	return 0;
@@ -308,7 +309,7 @@ virtiovf_get_data_buff_from_pos(struct virtiovf_migration_file *migf,
	bool found = false;

	*end_of_data = false;
	spin_lock_irq(&migf->list_lock);
	mutex_lock(&migf->list_lock);
	if (list_empty(&migf->buf_list)) {
		*end_of_data = true;
		goto end;
@@ -329,7 +330,7 @@ virtiovf_get_data_buff_from_pos(struct virtiovf_migration_file *migf,
	migf->state = VIRTIOVF_MIGF_STATE_ERROR;

end:
	spin_unlock_irq(&migf->list_lock);
	mutex_unlock(&migf->list_lock);
	return found ? buf : NULL;
}

@@ -369,10 +370,10 @@ static ssize_t virtiovf_buf_read(struct virtiovf_data_buffer *vhca_buf,
	}

	if (*pos >= vhca_buf->start_pos + vhca_buf->length) {
		spin_lock_irq(&vhca_buf->migf->list_lock);
		mutex_lock(&vhca_buf->migf->list_lock);
		list_del_init(&vhca_buf->buf_elm);
		list_add_tail(&vhca_buf->buf_elm, &vhca_buf->migf->avail_list);
		spin_unlock_irq(&vhca_buf->migf->list_lock);
		mutex_unlock(&vhca_buf->migf->list_lock);
	}

	return done;
@@ -549,9 +550,9 @@ virtiovf_add_buf_header(struct virtiovf_data_buffer *header_buf,
	header_buf->length = sizeof(header);
	header_buf->start_pos = header_buf->migf->max_pos;
	migf->max_pos += header_buf->length;
	spin_lock_irq(&migf->list_lock);
	mutex_lock(&migf->list_lock);
	list_add_tail(&header_buf->buf_elm, &migf->buf_list);
	spin_unlock_irq(&migf->list_lock);
	mutex_unlock(&migf->list_lock);
	return 0;
}

@@ -616,9 +617,9 @@ virtiovf_read_device_context_chunk(struct virtiovf_migration_file *migf,

	buf->start_pos = buf->migf->max_pos;
	migf->max_pos += buf->length;
	spin_lock(&migf->list_lock);
	mutex_lock(&migf->list_lock);
	list_add_tail(&buf->buf_elm, &migf->buf_list);
	spin_unlock_irq(&migf->list_lock);
	mutex_unlock(&migf->list_lock);
	return 0;

out_header:
@@ -687,7 +688,7 @@ virtiovf_pci_save_device_data(struct virtiovf_pci_core_device *virtvdev,
	mutex_init(&migf->lock);
	INIT_LIST_HEAD(&migf->buf_list);
	INIT_LIST_HEAD(&migf->avail_list);
	spin_lock_init(&migf->list_lock);
	mutex_init(&migf->list_lock);
	migf->virtvdev = virtvdev;

	lockdep_assert_held(&virtvdev->state_mutex);
@@ -1077,7 +1078,7 @@ virtiovf_pci_resume_device_data(struct virtiovf_pci_core_device *virtvdev)
	mutex_init(&migf->lock);
	INIT_LIST_HEAD(&migf->buf_list);
	INIT_LIST_HEAD(&migf->avail_list);
	spin_lock_init(&migf->list_lock);
	mutex_init(&migf->list_lock);

	buf = virtiovf_alloc_data_buffer(migf, VIRTIOVF_TARGET_INITIAL_BUF_SIZE);
	if (IS_ERR(buf)) {