From 041fff5057b9efe5cf928393ad3422bc73f7735f Mon Sep 17 00:00:00 2001 From: Yauhen Date: Sun, 10 Apr 2022 16:30:10 +0400 Subject: [PATCH] Add persistent and perf options to the luks_device (#434) Read and write work queue significantly degrades performance on SSD/NVME devices[1]. In Debian 11 crypttab does not support no-read-workqueue and no-write-workqueue flags, so the persistent flag is workaround: once opened with perf parameters persists forever. [1] https://blog.cloudflare.com/speeding-up-linux-disk-encryption/ Signed-off-by: Yauhen Artsiukhou --- .../434-add-persistent-and-perf-options.yml | 4 + plugins/modules/luks_device.py | 65 ++++++++++- .../luks_device/tasks/tests/performance.yml | 103 ++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/434-add-persistent-and-perf-options.yml create mode 100644 tests/integration/targets/luks_device/tasks/tests/performance.yml diff --git a/changelogs/fragments/434-add-persistent-and-perf-options.yml b/changelogs/fragments/434-add-persistent-and-perf-options.yml new file mode 100644 index 00000000..b4a6717e --- /dev/null +++ b/changelogs/fragments/434-add-persistent-and-perf-options.yml @@ -0,0 +1,4 @@ +--- +minor_changes: + - luks_devices - added ``persistent`` option when opening LUKS2 containers (https://github.com/ansible-collections/community.crypto/pull/434). + - luks_devices - added ``perf_same_cpu_crypt``, ``perf_submit_from_crypt_cpus``, ``perf_no_read_workqueue``, ``perf_no_write_workqueue`` for performance tuning when opening LUKS2 containers (https://github.com/ansible-collections/community.crypto/issues/427). diff --git a/plugins/modules/luks_device.py b/plugins/modules/luks_device.py index cf17d4bc..07618615 100644 --- a/plugins/modules/luks_device.py +++ b/plugins/modules/luks_device.py @@ -217,6 +217,48 @@ options: - "Will only be used on container creation." type: int version_added: '1.5.0' + perf_same_cpu_crypt: + description: + - "Allows the user to perform encryption using the same CPU that IO was submitted on." + - "The default is to use an unbound workqueue so that encryption work is automatically balanced between available CPUs." + - "Will only be used when opening containers." + type: bool + default: false + version_added: '2.3.0' + perf_submit_from_crypt_cpus: + description: + - "Allows the user to disable offloading writes to a separate thread after encryption." + - "There are some situations where offloading block write IO operations from the encryption threads + to a single thread degrades performance significantly." + - "The default is to offload block write IO operations to the same thread." + - "Will only be used when opening containers." + type: bool + default: false + version_added: '2.3.0' + perf_no_read_workqueue: + description: + - "Allows the user to bypass dm-crypt internal workqueue and process read requests synchronously." + - "Will only be used when opening containers." + type: bool + default: false + version_added: '2.3.0' + perf_no_write_workqueue: + description: + - "Allows the user to bypass dm-crypt internal workqueue and process write requests synchronously." + - "Will only be used when opening containers." + type: bool + default: false + version_added: '2.3.0' + persistent: + description: + - "Allows the user to store options into container's metadata persistently and automatically use them next time. + Only I(perf_same_cpu_crypt), I(perf_submit_from_crypt_cpus), I(perf_no_read_workqueue), and I(perf_no_write_workqueue) + can be stored persistently." + - "Will only work with LUKS2 containers." + - "Will only be used when opening containers." + type: bool + default: false + version_added: '2.3.0' requirements: - "cryptsetup" @@ -517,10 +559,21 @@ class CryptHandler(Handler): raise ValueError('Error while creating LUKS on %s: %s' % (device, result[STDERR])) - def run_luks_open(self, device, keyfile, passphrase, name): + def run_luks_open(self, device, keyfile, passphrase, perf_same_cpu_crypt, perf_submit_from_crypt_cpus, + perf_no_read_workqueue, perf_no_write_workqueue, persistent, name): args = [self._cryptsetup_bin] if keyfile: args.extend(['--key-file', keyfile]) + if perf_same_cpu_crypt: + args.extend(['--perf-same_cpu_crypt']) + if perf_submit_from_crypt_cpus: + args.extend(['--perf-submit_from_crypt_cpus']) + if perf_no_read_workqueue: + args.extend(['--perf-no_read_workqueue']) + if perf_no_write_workqueue: + args.extend(['--perf-no_write_workqueue']) + if persistent: + args.extend(['--persistent']) args.extend(['open', '--type', 'luks', device, name]) result = self._run_command(args, data=passphrase) @@ -802,6 +855,11 @@ def run_module(): mutually_exclusive=[('iteration_time', 'iteration_count')], ), sector_size=dict(type='int'), + perf_same_cpu_crypt=dict(type='bool', default=False), + perf_submit_from_crypt_cpus=dict(type='bool', default=False), + perf_no_read_workqueue=dict(type='bool', default=False), + perf_no_write_workqueue=dict(type='bool', default=False), + persistent=dict(type='bool', default=False), ) mutually_exclusive = [ @@ -877,6 +935,11 @@ def run_module(): crypt.run_luks_open(conditions.device, module.params['keyfile'], module.params['passphrase'], + module.params['perf_same_cpu_crypt'], + module.params['perf_submit_from_crypt_cpus'], + module.params['perf_no_read_workqueue'], + module.params['perf_no_write_workqueue'], + module.params['persistent'], name) except ValueError as e: module.fail_json(msg="luks_device error: %s" % e) diff --git a/tests/integration/targets/luks_device/tasks/tests/performance.yml b/tests/integration/targets/luks_device/tasks/tests/performance.yml new file mode 100644 index 00000000..0216ab05 --- /dev/null +++ b/tests/integration/targets/luks_device/tasks/tests/performance.yml @@ -0,0 +1,103 @@ +--- +- name: Gather package facts + package_facts: + manager: auto + +- name: On kernel >= 5.9 use performance flags + block: + - name: Create and open (check) + luks_device: + device: "{{ cryptfile_device }}" + state: opened + keyfile: "{{ remote_tmp_dir }}/keyfile1" + perf_same_cpu_crypt: true + perf_submit_from_crypt_cpus: true + perf_no_read_workqueue: true + perf_no_write_workqueue: true + persistent: true + pbkdf: + iteration_time: 0.1 + check_mode: yes + become: yes + register: create_open_check + - name: Create and open + luks_device: + device: "{{ cryptfile_device }}" + state: opened + keyfile: "{{ remote_tmp_dir }}/keyfile1" + pbkdf: + iteration_time: 0.1 + perf_same_cpu_crypt: true + perf_submit_from_crypt_cpus: true + perf_no_read_workqueue: true + perf_no_write_workqueue: true + persistent: true + become: yes + register: create_open + - name: Create and open (idempotent) + luks_device: + device: "{{ cryptfile_device }}" + state: opened + keyfile: "{{ remote_tmp_dir }}/keyfile1" + pbkdf: + iteration_time: 0.1 + perf_same_cpu_crypt: true + perf_submit_from_crypt_cpus: true + perf_no_read_workqueue: true + perf_no_write_workqueue: true + persistent: true + become: yes + register: create_open_idem + - name: Create and open (idempotent, check) + luks_device: + device: "{{ cryptfile_device }}" + state: present + keyfile: "{{ remote_tmp_dir }}/keyfile1" + pbkdf: + iteration_time: 0.1 + perf_same_cpu_crypt: true + perf_submit_from_crypt_cpus: true + perf_no_read_workqueue: true + perf_no_write_workqueue: true + persistent: true + check_mode: yes + become: yes + register: create_open_idem_check + - assert: + that: + - create_open_check is changed + - create_open is changed + - create_open_idem is not changed + - create_open_idem_check is not changed + + - name: Dump LUKS Header + command: "cryptsetup luksDump {{ cryptfile_device }}" + become: yes + register: luks_header + - assert: + that: + - "'no-read-workqueue' in luks_header.stdout" + - "'no-write-workqueue' in luks_header.stdout" + - "'same-cpu-crypt' in luks_header.stdout" + - "'submit-from-crypt-cpus' in luks_header.stdout" + + - name: Dump device mapper table + command: "dmsetup table {{ create_open.name }}" + become: yes + register: dm_table + - assert: + that: + - "'no_read_workqueue' in dm_table.stdout" + - "'no_write_workqueue' in dm_table.stdout" + - "'same_cpu_crypt' in dm_table.stdout" + - "'submit_from_crypt_cpus' in dm_table.stdout" + + - name: Closed and Removed + luks_device: + name: "{{ cryptfile_device }}" + state: absent + become: yes + + when: + - ansible_facts.kernel is version('5.9.0', '>=') + - ansible_facts.packages['cryptsetup'][0].version is version('2.3.4', '>=')