From d138bd1bdee7f98c0c1bc4c387e0b7a28761bd6a Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Thu, 10 Oct 2024 12:06:07 +0100 Subject: [PATCH] CA-397084: SR scan tries to deactivate LV in use by tapdisk Back when this code was written we didn't have leaf coalesce doing snapshot coalesace operations, and so for performance reasons the scan code didn't reference count LVs that it activated on the assuption that nothing else would be in a position to start using the LV in the meantime. This assumption is no longer valid and induces scan failures when a new VDI is detected and the scan tries to deactivate it whilst it is open and in use by tapdisk. Move the scan code to use the lvactivator so that reference counts will be taken and honoured. Additionally, it's possible that more than one VDI might appear so don't treat this as a singular event and track all VDIs that might have been activated and dereference them at the end. Signed-off-by: Mark Syms --- drivers/LVHDSR.py | 13 +++++++------ tests/test_LVHDSR.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/drivers/LVHDSR.py b/drivers/LVHDSR.py index 19175a7eb..6e0d71eba 100755 --- a/drivers/LVHDSR.py +++ b/drivers/LVHDSR.py @@ -655,9 +655,8 @@ def forget_vdi(self, uuid): super(LVHDSR, self).forget_vdi(uuid) def scan(self, uuid): - activated = True + activated_lvs = set() try: - lvname = '' util.SMlog("LVHDSR.scan for %s" % self.uuid) if not self.isMaster: util.SMlog('sr_scan blocked for non-master') @@ -699,8 +698,9 @@ def scan(self, uuid): sm_config['vdi_type'] = Dict[vdi][VDI_TYPE_TAG] lvname = "%s%s" % \ (lvhdutil.LV_PREFIX[sm_config['vdi_type']], vdi_uuid) - self.lvmCache.activateNoRefcount(lvname) - activated = True + self.lvActivator.activate( + vdi_uuid, lvname, LVActivator.NORMAL) + activated_lvs.add(vdi_uuid) lvPath = os.path.join(self.path, lvname) if Dict[vdi][VDI_TYPE_TAG] == vhdutil.VDI_TYPE_RAW: @@ -799,8 +799,9 @@ def scan(self, uuid): self._kickGC() finally: - if lvname != '' and activated: - self.lvmCache.deactivateNoRefcount(lvname) + for vdi in activated_lvs: + self.lvActivator.deactivate( + vdi, LVActivator.NORMAL, False) def update(self, uuid): if not lvutil._checkVG(self.vgname): diff --git a/tests/test_LVHDSR.py b/tests/test_LVHDSR.py index 3e8b71fc9..600f1379f 100644 --- a/tests/test_LVHDSR.py +++ b/tests/test_LVHDSR.py @@ -1,3 +1,4 @@ +import copy import os import unittest import unittest.mock as mock @@ -248,6 +249,49 @@ def cmd(args): mock_scsi_get_size.return_value = device_size + (2 * 1024 * 1024 * 1024) sr.scan(sr.uuid) + # Find new VDI during scan + extended_vdi_data = copy.deepcopy(vdi_data) + extended_vdi_data.update({ + 'vdi3_ref': { + 'uuid': str(uuid.uuid4()), + 'name_label': "VDI3", + 'name_description': "Third VDI", + 'is_a_snapshot': False, + 'snapshot_of': None, + 'snapshot_time': None, + 'type': 'User', + 'metadata-of-pool': None, + 'sm-config': { + 'vdi_type': 'vhd' + } + }}) + with mock.patch('LVHDSR.LVMMetadataHandler', autospec=True) as m, \ + mock.patch('LVHDSR.vhdutil', autotspec=True) as v: + m.return_value.getMetadata.return_value = [ + None, self.convert_vdi_to_meta(extended_vdi_data)] + v._getVHDParentNoCheck.return_value = None + sr.scan(sr.uuid) + + lvm_cache = mock_lvm_cache.return_value + self.assertEqual(1, lvm_cache.activate.call_count) + self.assertEqual(1, lvm_cache.deactivate.call_count) + + def convert_vdi_to_meta(self, vdi_data): + metadata = {} + for item in vdi_data.items(): + metadata[item[0]] = { + 'uuid': item[1]['uuid'], + 'is_a_snapshot': item[1]['is_a_snapshot'], + 'snapshot_of': item[1]['snapshot_of'], + 'vdi_type': item[1]['sm-config']['vdi_type'], + 'name_label': item[1]['name_label'], + 'name_description': item[1]['name_description'], + 'type': item[1]['type'], + 'read_only': False, + 'managed': True, + } + return metadata + class TestLVHDVDI(unittest.TestCase, Stubs):