 a8ac88585d
			
		
	
	
		a8ac88585d
		
	
	
	
	
		
			
			This will make qemu aware of the device used buffers, allowing it to write the guest memory with its contents if needed. Signed-off-by: Eugenio Pérez <eperezma@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Jason Wang <jasowang@redhat.com>
		
			
				
	
	
		
			156 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * vhost shadow virtqueue
 | |
|  *
 | |
|  * SPDX-FileCopyrightText: Red Hat, Inc. 2021
 | |
|  * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: GPL-2.0-or-later
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "hw/virtio/vhost-shadow-virtqueue.h"
 | |
| 
 | |
| #include "qemu/error-report.h"
 | |
| #include "qemu/main-loop.h"
 | |
| #include "linux-headers/linux/vhost.h"
 | |
| 
 | |
| /**
 | |
|  * Forward guest notifications.
 | |
|  *
 | |
|  * @n: guest kick event notifier, the one that guest set to notify svq.
 | |
|  */
 | |
| static void vhost_handle_guest_kick(EventNotifier *n)
 | |
| {
 | |
|     VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick);
 | |
|     event_notifier_test_and_clear(n);
 | |
|     event_notifier_set(&svq->hdev_kick);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Forward vhost notifications
 | |
|  *
 | |
|  * @n: hdev call event notifier, the one that device set to notify svq.
 | |
|  */
 | |
| static void vhost_svq_handle_call(EventNotifier *n)
 | |
| {
 | |
|     VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue,
 | |
|                                              hdev_call);
 | |
|     event_notifier_test_and_clear(n);
 | |
|     event_notifier_set(&svq->svq_call);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the call notifier for the SVQ to call the guest
 | |
|  *
 | |
|  * @svq: Shadow virtqueue
 | |
|  * @call_fd: call notifier
 | |
|  *
 | |
|  * Called on BQL context.
 | |
|  */
 | |
| void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
 | |
| {
 | |
|     if (call_fd == VHOST_FILE_UNBIND) {
 | |
|         /*
 | |
|          * Fail event_notifier_set if called handling device call.
 | |
|          *
 | |
|          * SVQ still needs device notifications, since it needs to keep
 | |
|          * forwarding used buffers even with the unbind.
 | |
|          */
 | |
|         memset(&svq->svq_call, 0, sizeof(svq->svq_call));
 | |
|     } else {
 | |
|         event_notifier_init_fd(&svq->svq_call, call_fd);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set a new file descriptor for the guest to kick the SVQ and notify for avail
 | |
|  *
 | |
|  * @svq: The svq
 | |
|  * @svq_kick_fd: The svq kick fd
 | |
|  *
 | |
|  * Note that the SVQ will never close the old file descriptor.
 | |
|  */
 | |
| void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
 | |
| {
 | |
|     EventNotifier *svq_kick = &svq->svq_kick;
 | |
|     bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick);
 | |
|     bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND;
 | |
| 
 | |
|     if (poll_stop) {
 | |
|         event_notifier_set_handler(svq_kick, NULL);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * event_notifier_set_handler already checks for guest's notifications if
 | |
|      * they arrive at the new file descriptor in the switch, so there is no
 | |
|      * need to explicitly check for them.
 | |
|      */
 | |
|     if (poll_start) {
 | |
|         event_notifier_init_fd(svq_kick, svq_kick_fd);
 | |
|         event_notifier_set(svq_kick);
 | |
|         event_notifier_set_handler(svq_kick, vhost_handle_guest_kick);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Stop the shadow virtqueue operation.
 | |
|  * @svq: Shadow Virtqueue
 | |
|  */
 | |
| void vhost_svq_stop(VhostShadowVirtqueue *svq)
 | |
| {
 | |
|     event_notifier_set_handler(&svq->svq_kick, NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Creates vhost shadow virtqueue, and instructs the vhost device to use the
 | |
|  * shadow methods and file descriptors.
 | |
|  *
 | |
|  * Returns the new virtqueue or NULL.
 | |
|  *
 | |
|  * In case of error, reason is reported through error_report.
 | |
|  */
 | |
| VhostShadowVirtqueue *vhost_svq_new(void)
 | |
| {
 | |
|     g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
 | |
|     int r;
 | |
| 
 | |
|     r = event_notifier_init(&svq->hdev_kick, 0);
 | |
|     if (r != 0) {
 | |
|         error_report("Couldn't create kick event notifier: %s (%d)",
 | |
|                      g_strerror(errno), errno);
 | |
|         goto err_init_hdev_kick;
 | |
|     }
 | |
| 
 | |
|     r = event_notifier_init(&svq->hdev_call, 0);
 | |
|     if (r != 0) {
 | |
|         error_report("Couldn't create call event notifier: %s (%d)",
 | |
|                      g_strerror(errno), errno);
 | |
|         goto err_init_hdev_call;
 | |
|     }
 | |
| 
 | |
|     event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
 | |
|     event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call);
 | |
|     return g_steal_pointer(&svq);
 | |
| 
 | |
| err_init_hdev_call:
 | |
|     event_notifier_cleanup(&svq->hdev_kick);
 | |
| 
 | |
| err_init_hdev_kick:
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Free the resources of the shadow virtqueue.
 | |
|  *
 | |
|  * @pvq: gpointer to SVQ so it can be used by autofree functions.
 | |
|  */
 | |
| void vhost_svq_free(gpointer pvq)
 | |
| {
 | |
|     VhostShadowVirtqueue *vq = pvq;
 | |
|     vhost_svq_stop(vq);
 | |
|     event_notifier_cleanup(&vq->hdev_kick);
 | |
|     event_notifier_set_handler(&vq->hdev_call, NULL);
 | |
|     event_notifier_cleanup(&vq->hdev_call);
 | |
|     g_free(vq);
 | |
| }
 |