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 <jsirex@gmail.com>
pull/444/head
Yauhen 2022-04-10 16:30:10 +04:00 committed by GitHub
parent c7f581daad
commit 041fff5057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 171 additions and 1 deletions

View File

@ -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).

View File

@ -217,6 +217,48 @@ options:
- "Will only be used on container creation." - "Will only be used on container creation."
type: int type: int
version_added: '1.5.0' 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: requirements:
- "cryptsetup" - "cryptsetup"
@ -517,10 +559,21 @@ class CryptHandler(Handler):
raise ValueError('Error while creating LUKS on %s: %s' raise ValueError('Error while creating LUKS on %s: %s'
% (device, result[STDERR])) % (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] args = [self._cryptsetup_bin]
if keyfile: if keyfile:
args.extend(['--key-file', 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]) args.extend(['open', '--type', 'luks', device, name])
result = self._run_command(args, data=passphrase) result = self._run_command(args, data=passphrase)
@ -802,6 +855,11 @@ def run_module():
mutually_exclusive=[('iteration_time', 'iteration_count')], mutually_exclusive=[('iteration_time', 'iteration_count')],
), ),
sector_size=dict(type='int'), 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 = [ mutually_exclusive = [
@ -877,6 +935,11 @@ def run_module():
crypt.run_luks_open(conditions.device, crypt.run_luks_open(conditions.device,
module.params['keyfile'], module.params['keyfile'],
module.params['passphrase'], 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) name)
except ValueError as e: except ValueError as e:
module.fail_json(msg="luks_device error: %s" % e) module.fail_json(msg="luks_device error: %s" % e)

View File

@ -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', '>=')