@ -209,6 +209,21 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
vaddr , section - > readonly ) ;
llsize = int128_sub ( llend , int128_make64 ( iova ) ) ;
if ( v - > shadow_vqs_enabled ) {
DMAMap mem_region = {
. translated_addr = ( hwaddr ) ( uintptr_t ) vaddr ,
. size = int128_get64 ( llsize ) - 1 ,
. perm = IOMMU_ACCESS_FLAG ( true , section - > readonly ) ,
} ;
int r = vhost_iova_tree_map_alloc ( v - > iova_tree , & mem_region ) ;
if ( unlikely ( r ! = IOVA_OK ) ) {
error_report ( " Can't allocate a mapping (%d) " , r ) ;
goto fail ;
}
iova = mem_region . iova ;
}
vhost_vdpa_iotlb_batch_begin_once ( v ) ;
ret = vhost_vdpa_dma_map ( v , iova , int128_get64 ( llsize ) ,
@ -261,6 +276,20 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
llsize = int128_sub ( llend , int128_make64 ( iova ) ) ;
if ( v - > shadow_vqs_enabled ) {
const DMAMap * result ;
const void * vaddr = memory_region_get_ram_ptr ( section - > mr ) +
section - > offset_within_region +
( iova - section - > offset_within_address_space ) ;
DMAMap mem_region = {
. translated_addr = ( hwaddr ) ( uintptr_t ) vaddr ,
. size = int128_get64 ( llsize ) - 1 ,
} ;
result = vhost_iova_tree_find_iova ( v - > iova_tree , & mem_region ) ;
iova = result - > iova ;
vhost_iova_tree_remove ( v - > iova_tree , & mem_region ) ;
}
vhost_vdpa_iotlb_batch_begin_once ( v ) ;
ret = vhost_vdpa_dma_unmap ( v , iova , int128_get64 ( llsize ) ) ;
if ( ret ) {
@ -370,7 +399,7 @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
shadow_vqs = g_ptr_array_new_full ( hdev - > nvqs , vhost_svq_free ) ;
for ( unsigned n = 0 ; n < hdev - > nvqs ; + + n ) {
g_autoptr ( VhostShadowVirtqueue ) svq = vhost_svq_new ( ) ;
g_autoptr ( VhostShadowVirtqueue ) svq = vhost_svq_new ( v - > iova_tree ) ;
if ( unlikely ( ! svq ) ) {
error_setg ( errp , " Cannot create svq %u " , n ) ;
@ -807,33 +836,70 @@ static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev,
/**
* Unmap a SVQ area in the device
*/
static bool vhost_vdpa_svq_unmap_ring ( struct vhost_vdpa * v , hwaddr iova ,
hwaddr siz e)
static bool vhost_vdpa_svq_unmap_ring ( struct vhost_vdpa * v ,
const DMAMap * needl e)
{
const DMAMap * result = vhost_iova_tree_find_iova ( v - > iova_tree , needle ) ;
hwaddr size ;
int r ;
size = ROUND_UP ( size , qemu_real_host_page_size ) ;
r = vhost_vdpa_dma_unmap ( v , iova , size ) ;
if ( unlikely ( ! result ) ) {
error_report ( " Unable to find SVQ address to unmap " ) ;
return false ;
}
size = ROUND_UP ( result - > size , qemu_real_host_page_size ) ;
r = vhost_vdpa_dma_unmap ( v , result - > iova , size ) ;
return r = = 0 ;
}
static bool vhost_vdpa_svq_unmap_rings ( struct vhost_dev * dev ,
const VhostShadowVirtqueue * svq )
{
DMAMap needle = { } ;
struct vhost_vdpa * v = dev - > opaque ;
struct vhost_vring_addr svq_addr ;
size_t device_size = vhost_svq_device_area_size ( svq ) ;
size_t driver_size = vhost_svq_driver_area_size ( svq ) ;
bool ok ;
vhost_svq_get_vring_addr ( svq , & svq_addr ) ;
ok = vhost_vdpa_svq_unmap_ring ( v , svq_addr . desc_user_addr , driver_size ) ;
needle . translated_addr = svq_addr . desc_user_addr ;
ok = vhost_vdpa_svq_unmap_ring ( v , & needle ) ;
if ( unlikely ( ! ok ) ) {
return false ;
}
return vhost_vdpa_svq_unmap_ring ( v , svq_addr . used_user_addr , device_size ) ;
needle . translated_addr = svq_addr . used_user_addr ;
return vhost_vdpa_svq_unmap_ring ( v , & needle ) ;
}
/**
* Map the SVQ area in the device
*
* @ v : Vhost - vdpa device
* @ needle : The area to search iova
* @ errorp : Error pointer
*/
static bool vhost_vdpa_svq_map_ring ( struct vhost_vdpa * v , DMAMap * needle ,
Error * * errp )
{
int r ;
r = vhost_iova_tree_map_alloc ( v - > iova_tree , needle ) ;
if ( unlikely ( r ! = IOVA_OK ) ) {
error_setg ( errp , " Cannot allocate iova (%d) " , r ) ;
return false ;
}
r = vhost_vdpa_dma_map ( v , needle - > iova , needle - > size + 1 ,
( void * ) ( uintptr_t ) needle - > translated_addr ,
needle - > perm = = IOMMU_RO ) ;
if ( unlikely ( r ! = 0 ) ) {
error_setg_errno ( errp , - r , " Cannot map region to device " ) ;
vhost_iova_tree_remove ( v - > iova_tree , needle ) ;
}
return r = = 0 ;
}
/**
@ -849,28 +915,44 @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev,
struct vhost_vring_addr * addr ,
Error * * errp )
{
DMAMap device_region , driver_region ;
struct vhost_vring_addr svq_addr ;
struct vhost_vdpa * v = dev - > opaque ;
size_t device_size = vhost_svq_device_area_size ( svq ) ;
size_t driver_size = vhost_svq_driver_area_size ( svq ) ;
int r ;
size_t avail_offset ;
bool ok ;
ERRP_GUARD ( ) ;
vhost_svq_get_vring_addr ( svq , addr ) ;
vhost_svq_get_vring_addr ( svq , & svq_ addr) ;
r = vhost_vdpa_dma_map ( v , addr - > desc_user_addr , driver_size ,
( void * ) ( uintptr_t ) addr - > desc_user_addr , true ) ;
if ( unlikely ( r ! = 0 ) ) {
error_setg_errno ( errp , - r , " Cannot create vq driver region: " ) ;
driver_region = ( DMAMap ) {
. translated_addr = svq_addr . desc_user_addr ,
. size = driver_size - 1 ,
. perm = IOMMU_RO ,
} ;
ok = vhost_vdpa_svq_map_ring ( v , & driver_region , errp ) ;
if ( unlikely ( ! ok ) ) {
error_prepend ( errp , " Cannot create vq driver region: " ) ;
return false ;
}
addr - > desc_user_addr = driver_region . iova ;
avail_offset = svq_addr . avail_user_addr - svq_addr . desc_user_addr ;
addr - > avail_user_addr = driver_region . iova + avail_offset ;
r = vhost_vdpa_dma_map ( v , addr - > used_user_addr , device_size ,
( void * ) ( intptr_t ) addr - > used_user_addr , false ) ;
if ( unlikely ( r ! = 0 ) ) {
error_setg_errno ( errp , - r , " Cannot create vq device region: " ) ;
device_region = ( DMAMap ) {
. translated_addr = svq_addr . used_user_addr ,
. size = device_size - 1 ,
. perm = IOMMU_RW ,
} ;
ok = vhost_vdpa_svq_map_ring ( v , & device_region , errp ) ;
if ( unlikely ( ! ok ) ) {
error_prepend ( errp , " Cannot create vq device region: " ) ;
vhost_vdpa_svq_unmap_ring ( v , & driver_region ) ;
}
addr - > used_user_addr = device_region . iova ;
return r = = 0 ;
return ok ;
}
static bool vhost_vdpa_svq_setup ( struct vhost_dev * dev ,