Limit the number of malloc arenas for qemu-img convert
Under heavy load, the qemu-img convert method may attempt to create additional malloc arenas to complete its work. By default this number is 8 * ncpu * which at about 250 MB of memory which can be consumed in this mannor should the. Since this is only something which should realistically occur under heavy cross-thread load where memory locking prevents a thread from unlocking a range of memory, (such as what can happen with hypervisors and VMs inside those hypervisors), then a new arena gets created, and the memory consumption spikes. More information is avialable at: https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html Change-Id: I8b2b490f2cc0ac5f47c3aaaaa249ce59db6602d6 Story: 2008928 Task: 42529
This commit is contained in:
parent
36b8486419
commit
76cbb9623f
@ -488,10 +488,23 @@ def convert_image(source, dest, out_format, run_as_root=False, cache=None,
|
||||
if out_of_order:
|
||||
cmd.append('-W')
|
||||
cmd += [source, dest]
|
||||
# NOTE(TheJulia): Staticly set the MALLOC_ARENA_MAX to prevent leaking
|
||||
# and the creation of new malloc arenas which will consume the system
|
||||
# memory. If limited to 1, qemu-img consumes ~250 MB of RAM, but when
|
||||
# another thread tries to access a locked section of memory in use with
|
||||
# another thread, then by default a new malloc arena is created,
|
||||
# which essentially balloons the memory requirement of the machine.
|
||||
# Default for qemu-img is 8 * nCPU * ~250MB (based on defaults +
|
||||
# thread/code/process/library overhead. In other words, 64 GB. Limiting
|
||||
# this to 3 keeps the memory utilization in happy cases below the overall
|
||||
# threshold which is in place in case a malicious image is attempted to
|
||||
# be passed through qemu-img.
|
||||
env_vars = {'MALLOC_ARENA_MAX': '3'}
|
||||
try:
|
||||
utils.execute(*cmd, run_as_root=run_as_root,
|
||||
prlimit=_qemu_img_limits(),
|
||||
use_standard_locale=True)
|
||||
use_standard_locale=True,
|
||||
env_variables=env_vars)
|
||||
except processutils.ProcessExecutionError as e:
|
||||
if ('Resource temporarily unavailable' in e.stderr
|
||||
or 'Cannot allocate memory' in e.stderr):
|
||||
|
@ -1093,22 +1093,26 @@ class OtherFunctionTestCase(base.IronicLibTestCase):
|
||||
@mock.patch.object(utils, 'execute', autospec=True)
|
||||
def test_convert_image(self, execute_mock):
|
||||
disk_utils.convert_image('source', 'dest', 'out_format')
|
||||
execute_mock.assert_called_once_with('qemu-img', 'convert', '-O',
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
execute_mock.assert_called_once_with(
|
||||
'qemu-img', 'convert', '-O',
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
|
||||
@mock.patch.object(utils, 'execute', autospec=True)
|
||||
def test_convert_image_flags(self, execute_mock):
|
||||
disk_utils.convert_image('source', 'dest', 'out_format',
|
||||
cache='directsync', out_of_order=True)
|
||||
execute_mock.assert_called_once_with('qemu-img', 'convert', '-O',
|
||||
'out_format', '-t', 'directsync',
|
||||
'-W', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
execute_mock.assert_called_once_with(
|
||||
'qemu-img', 'convert', '-O',
|
||||
'out_format', '-t', 'directsync',
|
||||
'-W', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
|
||||
@mock.patch.object(utils, 'execute', autospec=True)
|
||||
def test_convert_image_retries(self, execute_mock):
|
||||
@ -1124,7 +1128,8 @@ class OtherFunctionTestCase(base.IronicLibTestCase):
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
execute_mock.assert_has_calls([
|
||||
convert_call,
|
||||
mock.call('sync'),
|
||||
@ -1147,7 +1152,8 @@ class OtherFunctionTestCase(base.IronicLibTestCase):
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
execute_mock.assert_has_calls([
|
||||
convert_call,
|
||||
mock.call('sync'),
|
||||
@ -1173,7 +1179,8 @@ class OtherFunctionTestCase(base.IronicLibTestCase):
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
execute_mock.assert_has_calls([
|
||||
convert_call,
|
||||
mock.call('sync'),
|
||||
@ -1196,7 +1203,8 @@ class OtherFunctionTestCase(base.IronicLibTestCase):
|
||||
'out_format', 'source', 'dest',
|
||||
run_as_root=False,
|
||||
prlimit=mock.ANY,
|
||||
use_standard_locale=True)
|
||||
use_standard_locale=True,
|
||||
env_variables={'MALLOC_ARENA_MAX': '3'})
|
||||
execute_mock.assert_has_calls([
|
||||
convert_call,
|
||||
])
|
||||
|
Loading…
x
Reference in New Issue
Block a user