@@ -233,10 +233,8 @@ def _is_attached_to(self, vm):
233233
234234 for disk in xml_desc .findall ("devices/disk[source][@type='block']" ):
235235 backend_domain , port_id = _try_get_block_device_info (vm .app , disk )
236-
237236 if backend_domain .name != self .backend_domain .name :
238237 continue
239-
240238 if self .port_id == port_id :
241239 return True
242240 return False
@@ -307,6 +305,11 @@ def _try_get_block_device_info(app, disk):
307305
308306class BlockDeviceExtension (qubes .ext .Extension ):
309307
308+ def __init__ (self ):
309+ super ().__init__ ()
310+ self ._attachments = {}
311+ self ._attachments_with_options = {}
312+
310313 @qubes .ext .handler ("domain-init" , "domain-load" )
311314 def on_domain_init_load (self , vm , event ):
312315 """Initialize watching for changes"""
@@ -327,6 +330,26 @@ def on_domain_init_load(self, vm, event):
327330 else :
328331 self .devices_cache [vm .name ] = {}
329332
333+ @qubes .ext .handler ("device-attach:block" )
334+ def on_device_block_attach (self , vm , event , device , options ):
335+ # pylint: disable=unused-argument
336+ self ._attachments [device .port_id ] = device .backend_domain
337+
338+ @qubes .ext .handler ("device-detach:block" )
339+ def on_device_block_detach (self , vm , event , port ):
340+ # pylint: disable=unused-argument
341+ if port in self ._attachments :
342+ self ._attachments .pop (port )
343+ if vm not in self ._attachments_with_options :
344+ return
345+ if port not in self ._attachments_with_options [vm ]:
346+ return
347+ for index , (device , options ) in enumerate (
348+ self ._attachments_with_options [vm ]
349+ ):
350+ if device .port_id == port :
351+ self ._attachments_with_options [vm ].pop (device )
352+
330353 @qubes .ext .handler ("domain-qdb-change:/qubes-block-devices" )
331354 def on_qdb_change (self , vm , event , path ):
332355 """A change in QubesDB means a change in a device list."""
@@ -338,8 +361,7 @@ def on_qdb_change(self, vm, event, path):
338361 )
339362 utils .device_list_change (self , current_devices , vm , path , BlockDevice )
340363
341- @staticmethod
342- def get_device_attachments (vm_ ):
364+ def get_device_attachments (self , vm_ ):
343365 result = {}
344366 for vm in vm_ .app .domains :
345367 if not vm .is_running () or isinstance (vm , RemoteVM ):
@@ -348,7 +370,10 @@ def get_device_attachments(vm_):
348370 if vm .app .vmm .offline_mode :
349371 return result
350372
351- # TODO: ben: cache
373+ if vm in self ._attachments :
374+ result = self ._attachments [vm ]
375+ continue
376+
352377 xml_desc = lxml .etree .fromstring (vm .libvirt_domain .XMLDesc ())
353378
354379 for disk in xml_desc .findall ("devices/disk[source][@type='block']" ):
@@ -359,6 +384,7 @@ def get_device_attachments(vm_):
359384 continue
360385
361386 result [port_id ] = vm
387+ self ._attachments [vm ] = result
362388 return result
363389
364390 @staticmethod
@@ -422,10 +448,14 @@ def on_device_list_attached(self, vm, event, **kwargs):
422448 if not vm .is_running ():
423449 return
424450
451+ if vm in self ._attachments_with_options :
452+ yield from self ._attachments_with_options [vm ]
453+ return
454+ self ._attachments_with_options [vm ] = []
455+
425456 system_disks = SYSTEM_DISKS
426457 if getattr (vm , "kernel" , None ):
427458 system_disks = SYSTEM_DISKS_DOM0_KERNEL
428- # TODO: ben: cache
429459 xml_desc = lxml .etree .fromstring (vm .libvirt_domain .XMLDesc ())
430460
431461 for disk in xml_desc .findall (
@@ -450,7 +480,10 @@ def on_device_list_attached(self, vm, event, **kwargs):
450480 if devtype != "disk" :
451481 options ["devtype" ] = devtype
452482
453- yield BlockDevice (Port (backend_domain , port_id , "block" )), options
483+ dev = BlockDevice (Port (backend_domain , port_id , "block" )), options
484+ yield dev
485+ self ._attachments_with_options [vm ].append (dev )
486+
454487
455488 @staticmethod
456489 def _is_block (vm , block : str ) -> bool :
0 commit comments