Skip to content

Commit

Permalink
CA-397084: SR scan tries to deactivate LV in use by tapdisk
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
MarkSymsCtx committed Oct 14, 2024
1 parent bc27747 commit 0354246
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 6 deletions.
13 changes: 7 additions & 6 deletions drivers/LVHDSR.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down
44 changes: 44 additions & 0 deletions tests/test_LVHDSR.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import os
import unittest
import unittest.mock as mock
Expand Down Expand Up @@ -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):

Expand Down

0 comments on commit 0354246

Please sign in to comment.