@ -29,6 +29,7 @@ static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp)
error_setg_errno ( errp , errno , " vfio_dma_unmap_vaddr_all " ) ;
return false ;
}
container - > cpr . vaddr_unmapped = true ;
return true ;
}
@ -59,6 +60,14 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer,
return 0 ;
}
static void vfio_region_remap ( MemoryListener * listener ,
MemoryRegionSection * section )
{
VFIOContainer * container = container_of ( listener , VFIOContainer ,
cpr . remap_listener ) ;
vfio_container_region_add ( & container - > bcontainer , section , true ) ;
}
static bool vfio_cpr_supported ( VFIOContainer * container , Error * * errp )
{
if ( ! ioctl ( container - > fd , VFIO_CHECK_EXTENSION , VFIO_UPDATE_VADDR ) ) {
@ -120,6 +129,40 @@ static const VMStateDescription vfio_container_vmstate = {
}
} ;
static int vfio_cpr_fail_notifier ( NotifierWithReturn * notifier ,
MigrationEvent * e , Error * * errp )
{
VFIOContainer * container =
container_of ( notifier , VFIOContainer , cpr . transfer_notifier ) ;
VFIOContainerBase * bcontainer = & container - > bcontainer ;
if ( e - > type ! = MIG_EVENT_PRECOPY_FAILED ) {
return 0 ;
}
if ( container - > cpr . vaddr_unmapped ) {
/*
* Force a call to vfio_region_remap for each mapped section by
* temporarily registering a listener , and temporarily diverting
* dma_map to vfio_legacy_cpr_dma_map . The latter restores vaddr .
*/
VFIOIOMMUClass * vioc = VFIO_IOMMU_GET_CLASS ( bcontainer ) ;
vioc - > dma_map = vfio_legacy_cpr_dma_map ;
container - > cpr . remap_listener = ( MemoryListener ) {
. name = " vfio cpr recover " ,
. region_add = vfio_region_remap
} ;
memory_listener_register ( & container - > cpr . remap_listener ,
bcontainer - > space - > as ) ;
memory_listener_unregister ( & container - > cpr . remap_listener ) ;
container - > cpr . vaddr_unmapped = false ;
vioc - > dma_map = container - > cpr . saved_dma_map ;
}
return 0 ;
}
bool vfio_legacy_cpr_register_container ( VFIOContainer * container , Error * * errp )
{
VFIOContainerBase * bcontainer = & container - > bcontainer ;
@ -142,6 +185,10 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp)
container - > cpr . saved_dma_map = vioc - > dma_map ;
vioc - > dma_map = vfio_legacy_cpr_dma_map ;
}
migration_add_notifier_mode ( & container - > cpr . transfer_notifier ,
vfio_cpr_fail_notifier ,
MIG_MODE_CPR_TRANSFER ) ;
return true ;
}
@ -152,6 +199,50 @@ void vfio_legacy_cpr_unregister_container(VFIOContainer *container)
migration_remove_notifier ( & bcontainer - > cpr_reboot_notifier ) ;
migrate_del_blocker ( & container - > cpr . blocker ) ;
vmstate_unregister ( NULL , & vfio_container_vmstate , container ) ;
migration_remove_notifier ( & container - > cpr . transfer_notifier ) ;
}
/*
* In old QEMU , VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after
* succeeding for others , so the latter have lost their vaddr . Call this
* to restore vaddr for a section with a giommu .
*
* The giommu already exists . Find it and replay it , which calls
* vfio_legacy_cpr_dma_map further down the stack .
*/
void vfio_cpr_giommu_remap ( VFIOContainerBase * bcontainer ,
MemoryRegionSection * section )
{
VFIOGuestIOMMU * giommu = NULL ;
hwaddr as_offset = section - > offset_within_address_space ;
hwaddr iommu_offset = as_offset - section - > offset_within_region ;
QLIST_FOREACH ( giommu , & bcontainer - > giommu_list , giommu_next ) {
if ( giommu - > iommu_mr = = IOMMU_MEMORY_REGION ( section - > mr ) & &
giommu - > iommu_offset = = iommu_offset ) {
break ;
}
}
g_assert ( giommu ) ;
memory_region_iommu_replay ( giommu - > iommu_mr , & giommu - > n ) ;
}
/*
* In old QEMU , VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after
* succeeding for others , so the latter have lost their vaddr . Call this
* to restore vaddr for a section with a RamDiscardManager .
*
* The ram discard listener already exists . Call its populate function
* directly , which calls vfio_legacy_cpr_dma_map .
*/
bool vfio_cpr_ram_discard_register_listener ( VFIOContainerBase * bcontainer ,
MemoryRegionSection * section )
{
VFIORamDiscardListener * vrdl =
vfio_find_ram_discard_listener ( bcontainer , section ) ;
g_assert ( vrdl ) ;
return vrdl - > listener . notify_populate ( & vrdl - > listener , section ) = = 0 ;
}
int vfio_cpr_group_get_device_fd ( int d , const char * name )