diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 15:51:31 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-13 15:51:31 -0700 |
| commit | 7fe6ac157b7e15c8976bd62ad7cb98e248884e83 (patch) | |
| tree | 64677a680f3bccc7efb8f4cfcb288006e1433cd3 /drivers/nvme | |
| parent | b8f82cb0d84d00c04cdbdce42f67df71b8507e8b (diff) | |
| parent | 36446de0c30c62b9d89502fd36c4904996d86ecd (diff) | |
| download | linux-next-7fe6ac157b7e15c8976bd62ad7cb98e248884e83.tar.gz linux-next-7fe6ac157b7e15c8976bd62ad7cb98e248884e83.zip | |
Merge tag 'for-7.1/block-20260411' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull block updates from Jens Axboe:
- Add shared memory zero-copy I/O support for ublk, bypassing per-I/O
copies between kernel and userspace by matching registered buffer
PFNs at I/O time. Includes selftests.
- Refactor bio integrity to support filesystem initiated integrity
operations and arbitrary buffer alignment.
- Clean up bio allocation, splitting bio_alloc_bioset() into clear fast
and slow paths. Add bio_await() and bio_submit_or_kill() helpers,
unify synchronous bi_end_io callbacks.
- Fix zone write plug refcount handling and plug removal races. Add
support for serializing zone writes at QD=1 for rotational zoned
devices, yielding significant throughput improvements.
- Add SED-OPAL ioctls for Single User Mode management and a STACK_RESET
command.
- Add io_uring passthrough (uring_cmd) support to the BSG layer.
- Replace pp_buf in partition scanning with struct seq_buf.
- zloop improvements and cleanups.
- drbd genl cleanup, switching to pre_doit/post_doit.
- NVMe pull request via Keith:
- Fabrics authentication updates
- Enhanced block queue limits support
- Workqueue usage updates
- A new write zeroes device quirk
- Tagset cleanup fix for loop device
- MD pull requests via Yu Kuai:
- Fix raid5 soft lockup in retry_aligned_read()
- Fix raid10 deadlock with check operation and nowait requests
- Fix raid1 overlapping writes on writemostly disks
- Fix sysfs deadlock on array_state=clear
- Proactive RAID-5 parity building with llbitmap, with
write_zeroes_unmap optimization for initial sync
- Fix llbitmap barrier ordering, rdev skipping, and bitmap_ops
version mismatch fallback
- Fix bcache use-after-free and uninitialized closure
- Validate raid5 journal metadata payload size
- Various cleanups
- Various other fixes, improvements, and cleanups
* tag 'for-7.1/block-20260411' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: (146 commits)
ublk: fix tautological comparison warning in ublk_ctrl_reg_buf
scsi: bsg: fix buffer overflow in scsi_bsg_uring_cmd()
block: refactor blkdev_zone_mgmt_ioctl
MAINTAINERS: update ublk driver maintainer email
Documentation: ublk: address review comments for SHMEM_ZC docs
ublk: allow buffer registration before device is started
ublk: replace xarray with IDA for shmem buffer index allocation
ublk: simplify PFN range loop in __ublk_ctrl_reg_buf
ublk: verify all pages in multi-page bvec fall within registered range
ublk: widen ublk_shmem_buf_reg.len to __u64 for 4GB buffer support
xfs: use bio_await in xfs_zone_gc_reset_sync
block: add a bio_submit_or_kill helper
block: factor out a bio_await helper
block: unify the synchronous bi_end_io callbacks
xfs: fix number of GC bvecs
selftests/ublk: add read-only buffer registration test
selftests/ublk: add filesystem fio verify test for shmem_zc
selftests/ublk: add hugetlbfs shmem_zc test for loop target
selftests/ublk: add shared memory zero-copy test
selftests/ublk: add UBLK_F_SHMEM_ZC support for loop target
...
Diffstat (limited to 'drivers/nvme')
| -rw-r--r-- | drivers/nvme/common/.kunitconfig | 6 | ||||
| -rw-r--r-- | drivers/nvme/common/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/nvme/common/Makefile | 2 | ||||
| -rw-r--r-- | drivers/nvme/common/auth.c | 587 | ||||
| -rw-r--r-- | drivers/nvme/common/tests/auth_kunit.c | 175 | ||||
| -rw-r--r-- | drivers/nvme/host/auth.c | 187 | ||||
| -rw-r--r-- | drivers/nvme/host/core.c | 89 | ||||
| -rw-r--r-- | drivers/nvme/host/multipath.c | 15 | ||||
| -rw-r--r-- | drivers/nvme/host/nvme.h | 6 | ||||
| -rw-r--r-- | drivers/nvme/host/pci.c | 2 | ||||
| -rw-r--r-- | drivers/nvme/host/sysfs.c | 50 | ||||
| -rw-r--r-- | drivers/nvme/target/admin-cmd.c | 4 | ||||
| -rw-r--r-- | drivers/nvme/target/auth.c | 202 | ||||
| -rw-r--r-- | drivers/nvme/target/configfs.c | 3 | ||||
| -rw-r--r-- | drivers/nvme/target/core.c | 7 | ||||
| -rw-r--r-- | drivers/nvme/target/fabrics-cmd-auth.c | 18 | ||||
| -rw-r--r-- | drivers/nvme/target/fc.c | 6 | ||||
| -rw-r--r-- | drivers/nvme/target/io-cmd-bdev.c | 19 | ||||
| -rw-r--r-- | drivers/nvme/target/loop.c | 2 | ||||
| -rw-r--r-- | drivers/nvme/target/nvmet.h | 8 | ||||
| -rw-r--r-- | drivers/nvme/target/tcp.c | 2 |
21 files changed, 706 insertions, 698 deletions
diff --git a/drivers/nvme/common/.kunitconfig b/drivers/nvme/common/.kunitconfig new file mode 100644 index 000000000000..60a038dc9423 --- /dev/null +++ b/drivers/nvme/common/.kunitconfig @@ -0,0 +1,6 @@ +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_BLOCK=y +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_HOST_AUTH=y +CONFIG_NVME_AUTH_KUNIT_TEST=y diff --git a/drivers/nvme/common/Kconfig b/drivers/nvme/common/Kconfig index da963e4f3f1f..f1639db65fd3 100644 --- a/drivers/nvme/common/Kconfig +++ b/drivers/nvme/common/Kconfig @@ -7,9 +7,15 @@ config NVME_KEYRING config NVME_AUTH tristate select CRYPTO - select CRYPTO_HMAC - select CRYPTO_SHA256 - select CRYPTO_SHA512 select CRYPTO_DH select CRYPTO_DH_RFC7919_GROUPS - select CRYPTO_HKDF + select CRYPTO_LIB_SHA256 + select CRYPTO_LIB_SHA512 + +config NVME_AUTH_KUNIT_TEST + tristate "KUnit tests for NVMe authentication" if !KUNIT_ALL_TESTS + depends on KUNIT && NVME_AUTH + default KUNIT_ALL_TESTS + help + Enable KUnit tests for some of the common code for NVMe over Fabrics + In-Band Authentication. diff --git a/drivers/nvme/common/Makefile b/drivers/nvme/common/Makefile index 681514cf2e2f..fd9d01a60946 100644 --- a/drivers/nvme/common/Makefile +++ b/drivers/nvme/common/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_NVME_KEYRING) += nvme-keyring.o nvme-auth-y += auth.o nvme-keyring-y += keyring.o + +obj-$(CONFIG_NVME_AUTH_KUNIT_TEST) += tests/auth_kunit.o diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c index e07e7d4bf8b6..2d325fb93083 100644 --- a/drivers/nvme/common/auth.c +++ b/drivers/nvme/common/auth.c @@ -9,14 +9,11 @@ #include <linux/prandom.h> #include <linux/scatterlist.h> #include <linux/unaligned.h> -#include <crypto/hash.h> #include <crypto/dh.h> -#include <crypto/hkdf.h> +#include <crypto/sha2.h> #include <linux/nvme.h> #include <linux/nvme-auth.h> -#define HKDF_MAX_HASHLEN 64 - static u32 nvme_dhchap_seqnum; static DEFINE_MUTEX(nvme_dhchap_mutex); @@ -38,9 +35,9 @@ u32 nvme_auth_get_seqnum(void) } EXPORT_SYMBOL_GPL(nvme_auth_get_seqnum); -static struct nvme_auth_dhgroup_map { - const char name[16]; - const char kpp[16]; +static const struct nvme_auth_dhgroup_map { + char name[16]; + char kpp[16]; } dhgroup_map[] = { [NVME_AUTH_DHGROUP_NULL] = { .name = "null", .kpp = "null" }, @@ -89,25 +86,21 @@ u8 nvme_auth_dhgroup_id(const char *dhgroup_name) } EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id); -static struct nvme_dhchap_hash_map { +static const struct nvme_dhchap_hash_map { int len; - const char hmac[15]; - const char digest[8]; + char hmac[15]; } hash_map[] = { [NVME_AUTH_HASH_SHA256] = { .len = 32, .hmac = "hmac(sha256)", - .digest = "sha256", }, [NVME_AUTH_HASH_SHA384] = { .len = 48, .hmac = "hmac(sha384)", - .digest = "sha384", }, [NVME_AUTH_HASH_SHA512] = { .len = 64, .hmac = "hmac(sha512)", - .digest = "sha512", }, }; @@ -119,14 +112,6 @@ const char *nvme_auth_hmac_name(u8 hmac_id) } EXPORT_SYMBOL_GPL(nvme_auth_hmac_name); -const char *nvme_auth_digest_name(u8 hmac_id) -{ - if (hmac_id >= ARRAY_SIZE(hash_map)) - return NULL; - return hash_map[hmac_id].digest; -} -EXPORT_SYMBOL_GPL(nvme_auth_digest_name); - u8 nvme_auth_hmac_id(const char *hmac_name) { int i; @@ -161,11 +146,10 @@ u32 nvme_auth_key_struct_size(u32 key_len) } EXPORT_SYMBOL_GPL(nvme_auth_key_struct_size); -struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, - u8 key_hash) +struct nvme_dhchap_key *nvme_auth_extract_key(const char *secret, u8 key_hash) { struct nvme_dhchap_key *key; - unsigned char *p; + const char *p; u32 crc; int ret, key_len; size_t allocated_len = strlen(secret); @@ -183,14 +167,14 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, pr_debug("base64 key decoding error %d\n", key_len); ret = key_len; - goto out_free_secret; + goto out_free_key; } if (key_len != 36 && key_len != 52 && key_len != 68) { pr_err("Invalid key len %d\n", key_len); ret = -EINVAL; - goto out_free_secret; + goto out_free_key; } /* The last four bytes is the CRC in little-endian format */ @@ -205,12 +189,12 @@ struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, pr_err("key crc mismatch (key %08x, crc %08x)\n", get_unaligned_le32(key->key + key_len), crc); ret = -EKEYREJECTED; - goto out_free_secret; + goto out_free_key; } key->len = key_len; key->hash = key_hash; return key; -out_free_secret: +out_free_key: nvme_auth_free_key(key); return ERR_PTR(ret); } @@ -237,12 +221,106 @@ void nvme_auth_free_key(struct nvme_dhchap_key *key) } EXPORT_SYMBOL_GPL(nvme_auth_free_key); +/* + * Start computing an HMAC value, given the algorithm ID and raw key. + * + * The context should be zeroized at the end of its lifetime. The caller can do + * that implicitly by calling nvme_auth_hmac_final(), or explicitly (needed when + * a context is abandoned without finalizing it) by calling memzero_explicit(). + */ +int nvme_auth_hmac_init(struct nvme_auth_hmac_ctx *hmac, u8 hmac_id, + const u8 *key, size_t key_len) +{ + hmac->hmac_id = hmac_id; + switch (hmac_id) { + case NVME_AUTH_HASH_SHA256: + hmac_sha256_init_usingrawkey(&hmac->sha256, key, key_len); + return 0; + case NVME_AUTH_HASH_SHA384: + hmac_sha384_init_usingrawkey(&hmac->sha384, key, key_len); + return 0; + case NVME_AUTH_HASH_SHA512: + hmac_sha512_init_usingrawkey(&hmac->sha512, key, key_len); + return 0; + } + pr_warn("%s: invalid hash algorithm %d\n", __func__, hmac_id); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_init); + +void nvme_auth_hmac_update(struct nvme_auth_hmac_ctx *hmac, const u8 *data, + size_t data_len) +{ + switch (hmac->hmac_id) { + case NVME_AUTH_HASH_SHA256: + hmac_sha256_update(&hmac->sha256, data, data_len); + return; + case NVME_AUTH_HASH_SHA384: + hmac_sha384_update(&hmac->sha384, data, data_len); + return; + case NVME_AUTH_HASH_SHA512: + hmac_sha512_update(&hmac->sha512, data, data_len); + return; + } + /* Unreachable because nvme_auth_hmac_init() validated hmac_id */ + WARN_ON_ONCE(1); +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_update); + +/* Finish computing an HMAC value. Note that this zeroizes the HMAC context. */ +void nvme_auth_hmac_final(struct nvme_auth_hmac_ctx *hmac, u8 *out) +{ + switch (hmac->hmac_id) { + case NVME_AUTH_HASH_SHA256: + hmac_sha256_final(&hmac->sha256, out); + return; + case NVME_AUTH_HASH_SHA384: + hmac_sha384_final(&hmac->sha384, out); + return; + case NVME_AUTH_HASH_SHA512: + hmac_sha512_final(&hmac->sha512, out); + return; + } + /* Unreachable because nvme_auth_hmac_init() validated hmac_id */ + WARN_ON_ONCE(1); +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_final); + +static int nvme_auth_hmac(u8 hmac_id, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *out) +{ + struct nvme_auth_hmac_ctx hmac; + int ret; + + ret = nvme_auth_hmac_init(&hmac, hmac_id, key, key_len); + if (ret == 0) { + nvme_auth_hmac_update(&hmac, data, data_len); + nvme_auth_hmac_final(&hmac, out); + } + return ret; +} + +static int nvme_auth_hash(u8 hmac_id, const u8 *data, size_t data_len, u8 *out) +{ + switch (hmac_id) { + case NVME_AUTH_HASH_SHA256: + sha256(data, data_len, out); + return 0; + case NVME_AUTH_HASH_SHA384: + sha384(data, data_len, out); + return 0; + case NVME_AUTH_HASH_SHA512: + sha512(data, data_len, out); + return 0; + } + pr_warn("%s: invalid hash algorithm %d\n", __func__, hmac_id); + return -EINVAL; +} + struct nvme_dhchap_key *nvme_auth_transform_key( - struct nvme_dhchap_key *key, char *nqn) + const struct nvme_dhchap_key *key, const char *nqn) { - const char *hmac_name; - struct crypto_shash *key_tfm; - SHASH_DESC_ON_STACK(shash, key_tfm); + struct nvme_auth_hmac_ctx hmac; struct nvme_dhchap_key *transformed_key; int ret, key_len; @@ -257,118 +335,33 @@ struct nvme_dhchap_key *nvme_auth_transform_key( return ERR_PTR(-ENOMEM); return transformed_key; } - hmac_name = nvme_auth_hmac_name(key->hash); - if (!hmac_name) { - pr_warn("Invalid key hash id %d\n", key->hash); - return ERR_PTR(-EINVAL); - } - - key_tfm = crypto_alloc_shash(hmac_name, 0, 0); - if (IS_ERR(key_tfm)) - return ERR_CAST(key_tfm); - - key_len = crypto_shash_digestsize(key_tfm); + ret = nvme_auth_hmac_init(&hmac, key->hash, key->key, key->len); + if (ret) + return ERR_PTR(ret); + key_len = nvme_auth_hmac_hash_len(key->hash); transformed_key = nvme_auth_alloc_key(key_len, key->hash); if (!transformed_key) { - ret = -ENOMEM; - goto out_free_key; + memzero_explicit(&hmac, sizeof(hmac)); + return ERR_PTR(-ENOMEM); } - - shash->tfm = key_tfm; - ret = crypto_shash_setkey(key_tfm, key->key, key->len); - if (ret < 0) - goto out_free_transformed_key; - ret = crypto_shash_init(shash); - if (ret < 0) - goto out_free_transformed_key; - ret = crypto_shash_update(shash, nqn, strlen(nqn)); - if (ret < 0) - goto out_free_transformed_key; - ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17); - if (ret < 0) - goto out_free_transformed_key; - ret = crypto_shash_final(shash, transformed_key->key); - if (ret < 0) - goto out_free_transformed_key; - - crypto_free_shash(key_tfm); - + nvme_auth_hmac_update(&hmac, nqn, strlen(nqn)); + nvme_auth_hmac_update(&hmac, "NVMe-over-Fabrics", 17); + nvme_auth_hmac_final(&hmac, transformed_key->key); return transformed_key; - -out_free_transformed_key: - nvme_auth_free_key(transformed_key); -out_free_key: - crypto_free_shash(key_tfm); - - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(nvme_auth_transform_key); -static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey) -{ - const char *digest_name; - struct crypto_shash *tfm; - int ret; - - digest_name = nvme_auth_digest_name(hmac_id); - if (!digest_name) { - pr_debug("%s: failed to get digest for %d\n", __func__, - hmac_id); - return -EINVAL; - } - tfm = crypto_alloc_shash(digest_name, 0, 0); - if (IS_ERR(tfm)) - return -ENOMEM; - - ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey); - if (ret < 0) - pr_debug("%s: Failed to hash digest len %zu\n", __func__, - skey_len); - - crypto_free_shash(tfm); - return ret; -} - -int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, - u8 *challenge, u8 *aug, size_t hlen) +int nvme_auth_augmented_challenge(u8 hmac_id, const u8 *skey, size_t skey_len, + const u8 *challenge, u8 *aug, size_t hlen) { - struct crypto_shash *tfm; - u8 *hashed_key; - const char *hmac_name; + u8 hashed_key[NVME_AUTH_MAX_DIGEST_SIZE]; int ret; - hashed_key = kmalloc(hlen, GFP_KERNEL); - if (!hashed_key) - return -ENOMEM; - - ret = nvme_auth_hash_skey(hmac_id, skey, - skey_len, hashed_key); - if (ret < 0) - goto out_free_key; - - hmac_name = nvme_auth_hmac_name(hmac_id); - if (!hmac_name) { - pr_warn("%s: invalid hash algorithm %d\n", - __func__, hmac_id); - ret = -EINVAL; - goto out_free_key; - } - - tfm = crypto_alloc_shash(hmac_name, 0, 0); - if (IS_ERR(tfm)) { - ret = PTR_ERR(tfm); - goto out_free_key; - } - - ret = crypto_shash_setkey(tfm, hashed_key, hlen); + ret = nvme_auth_hash(hmac_id, skey, skey_len, hashed_key); if (ret) - goto out_free_hash; - - ret = crypto_shash_tfm_digest(tfm, challenge, hlen, aug); -out_free_hash: - crypto_free_shash(tfm); -out_free_key: - kfree_sensitive(hashed_key); + return ret; + ret = nvme_auth_hmac(hmac_id, hashed_key, hlen, challenge, hlen, aug); + memzero_explicit(hashed_key, sizeof(hashed_key)); return ret; } EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); @@ -411,7 +404,7 @@ int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, - u8 *ctrl_key, size_t ctrl_key_len, + const u8 *ctrl_key, size_t ctrl_key_len, u8 *sess_key, size_t sess_key_len) { struct kpp_request *req; @@ -438,7 +431,7 @@ int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, } EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); -int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) +int nvme_auth_parse_key(const char *secret, struct nvme_dhchap_key **ret_key) { struct nvme_dhchap_key *key; u8 key_hash; @@ -461,7 +454,7 @@ int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) *ret_key = key; return 0; } -EXPORT_SYMBOL_GPL(nvme_auth_generate_key); +EXPORT_SYMBOL_GPL(nvme_auth_parse_key); /** * nvme_auth_generate_psk - Generate a PSK for TLS @@ -486,66 +479,32 @@ EXPORT_SYMBOL_GPL(nvme_auth_generate_key); * Returns 0 on success with a valid generated PSK pointer in @ret_psk and * the length of @ret_psk in @ret_len, or a negative error number otherwise. */ -int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len, - u8 *c1, u8 *c2, size_t hash_len, u8 **ret_psk, size_t *ret_len) +int nvme_auth_generate_psk(u8 hmac_id, const u8 *skey, size_t skey_len, + const u8 *c1, const u8 *c2, size_t hash_len, + u8 **ret_psk, size_t *ret_len) { - struct crypto_shash *tfm; - SHASH_DESC_ON_STACK(shash, tfm); + size_t psk_len = nvme_auth_hmac_hash_len(hmac_id); + struct nvme_auth_hmac_ctx hmac; u8 *psk; - const char *hmac_name; - int ret, psk_len; + int ret; if (!c1 || !c2) return -EINVAL; - hmac_name = nvme_auth_hmac_name(hmac_id); - if (!hmac_name) { - pr_warn("%s: invalid hash algorithm %d\n", - __func__, hmac_id); - return -EINVAL; - } - - tfm = crypto_alloc_shash(hmac_name, 0, 0); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - psk_len = crypto_shash_digestsize(tfm); + ret = nvme_auth_hmac_init(&hmac, hmac_id, skey, skey_len); + if (ret) + return ret; psk = kzalloc(psk_len, GFP_KERNEL); if (!psk) { - ret = -ENOMEM; - goto out_free_tfm; - } - - shash->tfm = tfm; - ret = crypto_shash_setkey(tfm, skey, skey_len); - if (ret) - goto out_free_psk; - - ret = crypto_shash_init(shash); - if (ret) - goto out_free_psk; - - ret = crypto_shash_update(shash, c1, hash_len); - if (ret) - goto out_free_psk; - - ret = crypto_shash_update(shash, c2, hash_len); - if (ret) - goto out_free_psk; - - ret = crypto_shash_final(shash, psk); - if (!ret) { - *ret_psk = psk; - *ret_len = psk_len; + memzero_explicit(&hmac, sizeof(hmac)); + return -ENOMEM; } - -out_free_psk: - if (ret) - kfree_sensitive(psk); -out_free_tfm: - crypto_free_shash(tfm); - - return ret; + nvme_auth_hmac_update(&hmac, c1, hash_len); + nvme_auth_hmac_update(&hmac, c2, hash_len); + nvme_auth_hmac_final(&hmac, psk); + *ret_psk = psk; + *ret_len = psk_len; + return 0; } EXPORT_SYMBOL_GPL(nvme_auth_generate_psk); @@ -584,159 +543,71 @@ EXPORT_SYMBOL_GPL(nvme_auth_generate_psk); * Returns 0 on success with a valid digest pointer in @ret_digest, or a * negative error number on failure. */ -int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len, - char *subsysnqn, char *hostnqn, u8 **ret_digest) +int nvme_auth_generate_digest(u8 hmac_id, const u8 *psk, size_t psk_len, + const char *subsysnqn, const char *hostnqn, + char **ret_digest) { - struct crypto_shash *tfm; - SHASH_DESC_ON_STACK(shash, tfm); - u8 *digest, *enc; - const char *hmac_name; - size_t digest_len, hmac_len; + struct nvme_auth_hmac_ctx hmac; + u8 digest[NVME_AUTH_MAX_DIGEST_SIZE]; + size_t hash_len = nvme_auth_hmac_hash_len(hmac_id); + char *enc; + size_t enc_len; int ret; if (WARN_ON(!subsysnqn || !hostnqn)) return -EINVAL; - hmac_name = nvme_auth_hmac_name(hmac_id); - if (!hmac_name) { + if (hash_len == 0) { pr_warn("%s: invalid hash algorithm %d\n", __func__, hmac_id); return -EINVAL; } - switch (nvme_auth_hmac_hash_len(hmac_id)) { + switch (hash_len) { case 32: - hmac_len = 44; + enc_len = 44; break; case 48: - hmac_len = 64; + enc_len = 64; break; default: pr_warn("%s: invalid hash algorithm '%s'\n", - __func__, hmac_name); + __func__, nvme_auth_hmac_name(hmac_id)); return -EINVAL; } - enc = kzalloc(hmac_len + 1, GFP_KERNEL); - if (!enc) - return -ENOMEM; - - tfm = crypto_alloc_shash(hmac_name, 0, 0); - if (IS_ERR(tfm)) { - ret = PTR_ERR(tfm); - goto out_free_enc; - } - - digest_len = crypto_shash_digestsize(tfm); - digest = kzalloc(digest_len, GFP_KERNEL); - if (!digest) { + enc = kzalloc(enc_len + 1, GFP_KERNEL); + if (!enc) { ret = -ENOMEM; - goto out_free_tfm; + goto out; } - shash->tfm = tfm; - ret = crypto_shash_setkey(tfm, psk, psk_len); - if (ret) - goto out_free_digest; - - ret = crypto_shash_init(shash); - if (ret) - goto out_free_digest; - - ret = crypto_shash_update(shash, hostnqn, strlen(hostnqn)); + ret = nvme_auth_hmac_init(&hmac, hmac_id, psk, psk_len); if (ret) - goto out_free_digest; - - ret = crypto_shash_update(shash, " ", 1); - if (ret) - goto out_free_digest; - - ret = crypto_shash_update(shash, subsysnqn, strlen(subsysnqn)); - if (ret) - goto out_free_digest; - - ret = crypto_shash_update(shash, " NVMe-over-Fabrics", 18); - if (ret) - goto out_free_digest; - - ret = crypto_shash_final(shash, digest); - if (ret) - goto out_free_digest; - - ret = base64_encode(digest, digest_len, enc, true, BASE64_STD); - if (ret < hmac_len) { + goto out; + nvme_auth_hmac_update(&hmac, hostnqn, strlen(hostnqn)); + nvme_auth_hmac_update(&hmac, " ", 1); + nvme_auth_hmac_update(&hmac, subsysnqn, strlen(subsysnqn)); + nvme_auth_hmac_update(&hmac, " NVMe-over-Fabrics", 18); + nvme_auth_hmac_final(&hmac, digest); + + ret = base64_encode(digest, hash_len, enc, true, BASE64_STD); + if (ret < enc_len) { ret = -ENOKEY; - goto out_free_digest; + goto out; } *ret_digest = enc; ret = 0; -out_free_digest: - kfree_sensitive(digest); -out_free_tfm: - crypto_free_shash(tfm); -out_free_enc: +out: if (ret) kfree_sensitive(enc); - + memzero_explicit(digest, sizeof(digest)); return ret; } EXPORT_SYMBOL_GPL(nvme_auth_generate_digest); /** - * hkdf_expand_label - HKDF-Expand-Label (RFC 8846 section 7.1) - * @hmac_tfm: hash context keyed with pseudorandom key - * @label: ASCII label without "tls13 " prefix - * @labellen: length of @label - * @context: context bytes - * @contextlen: length of @context - * @okm: output keying material - * @okmlen: length of @okm - * - * Build the TLS 1.3 HkdfLabel structure and invoke hkdf_expand(). - * - * Returns 0 on success with output keying material stored in @okm, - * or a negative errno value otherwise. - */ -static int hkdf_expand_label(struct crypto_shash *hmac_tfm, - const u8 *label, unsigned int labellen, - const u8 *context, unsigned int contextlen, - u8 *okm, unsigned int okmlen) -{ - int err; - u8 *info; - unsigned int infolen; - const char *tls13_prefix = "tls13 "; - unsigned int prefixlen = strlen(tls13_prefix); - - if (WARN_ON(labellen > (255 - prefixlen))) - return -EINVAL; - if (WARN_ON(contextlen > 255)) - return -EINVAL; - - infolen = 2 + (1 + prefixlen + labellen) + (1 + contextlen); - info = kzalloc(infolen, GFP_KERNEL); - if (!info) - return -ENOMEM; - - /* HkdfLabel.Length */ - put_unaligned_be16(okmlen, info); - - /* HkdfLabel.Label */ - info[2] = prefixlen + labellen; - memcpy(info + 3, tls13_prefix, prefixlen); - memcpy(info + 3 + prefixlen, label, labellen); - - /* HkdfLabel.Context */ - info[3 + prefixlen + labellen] = contextlen; - memcpy(info + 4 + prefixlen + labellen, context, contextlen); - - err = hkdf_expand(hmac_tfm, info, infolen, okm, okmlen); - kfree_sensitive(info); - return err; -} - -/** * nvme_auth_derive_tls_psk - Derive TLS PSK * @hmac_id: Hash function identifier * @psk: generated input PSK @@ -763,82 +634,92 @@ static int hkdf_expand_label(struct crypto_shash *hmac_tfm, * Returns 0 on success with a valid psk pointer in @ret_psk or a negative * error number otherwise. */ -int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len, - u8 *psk_digest, u8 **ret_psk) +int nvme_auth_derive_tls_psk(int hmac_id, const u8 *psk, size_t psk_len, + const char *psk_digest, u8 **ret_psk) { - struct crypto_shash *hmac_tfm; - const char *hmac_name; - const char *label = "nvme-tls-psk"; - static const char default_salt[HKDF_MAX_HASHLEN]; - size_t prk_len; - const char *ctx; - unsigned char *prk, *tls_key; + static const u8 default_salt[NVME_AUTH_MAX_DIGEST_SIZE]; + static const char label[] = "tls13 nvme-tls-psk"; + const size_t label_len = sizeof(label) - 1; + u8 prk[NVME_AUTH_MAX_DIGEST_SIZE]; + size_t hash_len, ctx_len; + u8 *hmac_data = NULL, *tls_key; + size_t i; int ret; - hmac_name = nvme_auth_hmac_name(hmac_id); - if (!hmac_name) { + hash_len = nvme_auth_hmac_hash_len(hmac_id); + if (hash_len == 0) { pr_warn("%s: invalid hash algorithm %d\n", __func__, hmac_id); return -EINVAL; } if (hmac_id == NVME_AUTH_HASH_SHA512) { pr_warn("%s: unsupported hash algorithm %s\n", - __func__, hmac_name); + __func__, nvme_auth_hmac_name(hmac_id)); return -EINVAL; } - hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0); - if (IS_ERR(hmac_tfm)) - return PTR_ERR(hmac_tfm); - - prk_len = crypto_shash_digestsize(hmac_tfm); - prk = kzalloc(prk_len, GFP_KERNEL); - if (!prk) { - ret = -ENOMEM; - goto out_free_shash; + if (psk_len != hash_len) { + pr_warn("%s: unexpected psk_len %zu\n", __func__, psk_len); + return -EINVAL; } - if (WARN_ON(prk_len > HKDF_MAX_HASHLEN)) { - ret = -EINVAL; - goto out_free_prk; - } - ret = hkdf_extract(hmac_tfm, psk, psk_len, - default_salt, prk_len, prk); + /* HKDF-Extract */ + ret = nvme_auth_hmac(hmac_id, default_salt, hash_len, psk, psk_len, + prk); if (ret) - goto out_free_prk; + goto out; - ret = crypto_shash_setkey(hmac_tfm, prk, prk_len); - if (ret) - goto out_free_prk; + /* + * HKDF-Expand-Label (RFC 8446 section 7.1), with output length equal to + * the hash length (so only a single HMAC operation is needed) + */ - ctx = kasprintf(GFP_KERNEL, "%02d %s", hmac_id, psk_digest); - if (!ctx) { + hmac_data = kmalloc(/* output length */ 2 + + /* label */ 1 + label_len + + /* context (max) */ 1 + 3 + 1 + strlen(psk_digest) + + /* counter */ 1, + GFP_KERNEL); + if (!hmac_data) { ret = -ENOMEM; - goto out_free_prk; + goto out; } + /* output length */ + i = 0; + hmac_data[i++] = hash_len >> 8; + hmac_data[i++] = hash_len; + + /* label */ + static_assert(label_len <= 255); + hmac_data[i] = label_len; + memcpy(&hmac_data[i + 1], label, label_len); + i += 1 + label_len; + + /* context */ + ctx_len = sprintf(&hmac_data[i + 1], "%02d %s", hmac_id, psk_digest); + if (ctx_len > 255) { + ret = -EINVAL; + goto out; + } + hmac_data[i] = ctx_len; + i += 1 + ctx_len; + + /* counter (this overwrites the NUL terminator written by sprintf) */ + hmac_data[i++] = 1; tls_key = kzalloc(psk_len, GFP_KERNEL); if (!tls_key) { ret = -ENOMEM; - goto out_free_ctx; + goto out; } - ret = hkdf_expand_label(hmac_tfm, - label, strlen(label), - ctx, strlen(ctx), - tls_key, psk_len); + ret = nvme_auth_hmac(hmac_id, prk, hash_len, hmac_data, i, tls_key); if (ret) { - kfree(tls_key); - goto out_free_ctx; + kfree_sensitive(tls_key); + goto out; } *ret_psk = tls_key; - -out_free_ctx: - kfree(ctx); -out_free_prk: - kfree(prk); -out_free_shash: - crypto_free_shash(hmac_tfm); - +out: + kfree_sensitive(hmac_data); + memzero_explicit(prk, sizeof(prk)); return ret; } EXPORT_SYMBOL_GPL(nvme_auth_derive_tls_psk); diff --git a/drivers/nvme/common/tests/auth_kunit.c b/drivers/nvme/common/tests/auth_kunit.c new file mode 100644 index 000000000000..28b8dd1e3b18 --- /dev/null +++ b/drivers/nvme/common/tests/auth_kunit.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Unit tests for NVMe authentication functions + * + * Copyright 2026 Google LLC + */ + +#include <crypto/sha2.h> +#include <kunit/test.h> +#include <linux/nvme.h> +#include <linux/nvme-auth.h> +#include <linux/slab.h> + +struct nvme_auth_test_values { + u8 hmac_id; + size_t hash_len; + u8 expected_psk[NVME_AUTH_MAX_DIGEST_SIZE]; + char *expected_psk_digest; + u8 expected_tls_psk[NVME_AUTH_MAX_DIGEST_SIZE]; +}; + +static void kfree_action(void *ptr) +{ + kfree(ptr); +} + +static void kunit_add_kfree_action(struct kunit *test, void *ptr) +{ + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, kfree_action, ptr)); +} + +/* + * Test the derivation of a TLS PSK from the initial skey. The vals parameter + * gives the expected value of tls_psk as well as the intermediate values psk + * and psk_digest. The inputs are implicitly the fixed values set below. + */ +static void +test_nvme_auth_derive_tls_psk(struct kunit *test, + const struct nvme_auth_test_values *vals) +{ + const u8 hmac_id = vals->hmac_id; + const size_t hash_len = vals->hash_len; + const size_t skey_len = hash_len; + u8 skey[NVME_AUTH_MAX_DIGEST_SIZE]; + u8 c1[NVME_AUTH_MAX_DIGEST_SIZE]; + u8 c2[NVME_AUTH_MAX_DIGEST_SIZE]; + const char *subsysnqn = "subsysnqn"; + const char *hostnqn = "hostnqn"; + u8 *psk = NULL, *tls_psk = NULL; + char *psk_digest = NULL; + size_t psk_len; + int ret; + + for (int i = 0; i < NVME_AUTH_MAX_DIGEST_SIZE; i++) { + skey[i] = 'A' + i; + c1[i] = i; + c2[i] = 0xff - i; + } + + ret = nvme_auth_generate_psk(hmac_id, skey, skey_len, c1, c2, hash_len, + &psk, &psk_len); + kunit_add_kfree_action(test, psk); + KUNIT_ASSERT_EQ(test, 0, ret); + KUNIT_ASSERT_EQ(test, hash_len, psk_len); + KUNIT_ASSERT_MEMEQ(test, vals->expected_psk, psk, psk_len); + + ret = nvme_auth_generate_digest(hmac_id, psk, psk_len, subsysnqn, + hostnqn, &psk_digest); + kunit_add_kfree_action(test, psk_digest); + if (vals->expected_psk_digest == NULL) { + /* + * Algorithm has an ID assigned but is not supported by + * nvme_auth_generate_digest(). + */ + KUNIT_ASSERT_EQ(test, -EINVAL, ret); + return; + } + KUNIT_ASSERT_EQ(test, 0, ret); + KUNIT_ASSERT_STREQ(test, vals->expected_psk_digest, psk_digest); + + ret = nvme_auth_derive_tls_psk(hmac_id, psk, psk_len, psk_digest, + &tls_psk); + kunit_add_kfree_action(test, tls_psk); + KUNIT_ASSERT_EQ(test, 0, ret); + KUNIT_ASSERT_MEMEQ(test, vals->expected_tls_psk, tls_psk, psk_len); +} + +static void test_nvme_auth_derive_tls_psk_hmac_sha256(struct kunit *test) +{ + static const struct nvme_auth_test_values vals = { + .hmac_id = NVME_AUTH_HASH_SHA256, + .hash_len = SHA256_DIGEST_SIZE, + .expected_psk = { + 0x17, 0x33, 0xc5, 0x9f, 0xa7, 0xf4, 0x8f, 0xcf, + 0x37, 0xf5, 0xf2, 0x6f, 0xc4, 0xff, 0x02, 0x68, + 0xad, 0x4f, 0x78, 0xe0, 0x30, 0xf4, 0xf3, 0xb0, + 0xbf, 0xd1, 0xd4, 0x7e, 0x7b, 0xb1, 0x44, 0x7a, + }, + .expected_psk_digest = "OldoKuTfKddMuyCznAZojkWD7P4D9/AtzDzLimtOxqI=", + .expected_tls_psk = { + 0x3c, 0x17, 0xda, 0x62, 0x84, 0x74, 0xa0, 0x4d, + 0x22, 0x47, 0xc4, 0xca, 0xb4, 0x79, 0x68, 0xc9, + 0x15, 0x38, 0x81, 0x93, 0xf7, 0xc0, 0x71, 0xbd, + 0x94, 0x89, 0xcc, 0x36, 0x66, 0xcd, 0x7c, 0xc8, + }, + }; + + test_nvme_auth_derive_tls_psk(test, &vals); +} + +static void test_nvme_auth_derive_tls_psk_hmac_sha384(struct kunit *test) +{ + static const struct nvme_auth_test_values vals = { + .hmac_id = NVME_AUTH_HASH_SHA384, + .hash_len = SHA384_DIGEST_SIZE, + .expected_psk = { + 0xf1, 0x4b, 0x2d, 0xd3, 0x23, 0x4c, 0x45, 0x96, + 0x94, 0xd3, 0xbc, 0x63, 0xf8, 0x96, 0x8b, 0xd6, + 0xb3, 0x7c, 0x2c, 0x6d, 0xe8, 0x49, 0xe2, 0x2e, + 0x11, 0x87, 0x49, 0x00, 0x1c, 0xe4, 0xbb, 0xe8, + 0x64, 0x0b, 0x9e, 0x3a, 0x74, 0x8c, 0xb1, 0x1c, + 0xe4, 0xb1, 0xd7, 0x1d, 0x35, 0x9c, 0xce, 0x39, + }, + .expected_psk_digest = "cffMWk8TSS7HOQebjgYEIkrPrjWPV4JE5cdPB8WhEvY4JBW5YynKyv66XscN4A9n", + .expected_tls_psk = { + 0x27, 0x74, 0x75, 0x32, 0x33, 0x53, 0x7b, 0x3f, + 0xa5, 0x0e, 0xb7, 0xd1, 0x6a, 0x8e, 0x43, 0x45, + 0x7d, 0x85, 0xf4, 0x90, 0x6c, 0x00, 0x5b, 0x22, + 0x36, 0x61, 0x6c, 0x5d, 0x80, 0x93, 0x9d, 0x08, + 0x98, 0xff, 0xf1, 0x5b, 0xb8, 0xb7, 0x71, 0x19, + 0xd2, 0xbe, 0x0a, 0xac, 0x42, 0x3e, 0x75, 0x90, + }, + }; + + test_nvme_auth_derive_tls_psk(test, &vals); +} + +static void test_nvme_auth_derive_tls_psk_hmac_sha512(struct kunit *test) +{ + static const struct nvme_auth_test_values vals = { + .hmac_id = NVME_AUTH_HASH_SHA512, + .hash_len = SHA512_DIGEST_SIZE, + .expected_psk = { + 0x9c, 0x9f, 0x08, 0x9a, 0x61, 0x8b, 0x47, 0xd2, + 0xd7, 0x5f, 0x4b, 0x6c, 0x28, 0x07, 0x04, 0x24, + 0x48, 0x7b, 0x44, 0x5d, 0xd9, 0x6e, 0x70, 0xc4, + 0xc0, 0x9b, 0x55, 0xe8, 0xb6, 0x00, 0x01, 0x52, + 0xa3, 0x36, 0x3c, 0x34, 0x54, 0x04, 0x3f, 0x38, + 0xf0, 0xb8, 0x50, 0x36, 0xde, 0xd4, 0x06, 0x55, + 0x35, 0x0a, 0xa8, 0x7b, 0x8b, 0x6a, 0x28, 0x2b, + 0x5c, 0x1a, 0xca, 0xe1, 0x62, 0x33, 0xdd, 0x5b, + }, + /* nvme_auth_generate_digest() doesn't support SHA-512 yet. */ + .expected_psk_digest = NULL, + }; + + test_nvme_auth_derive_tls_psk(test, &vals); +} + +static struct kunit_case nvme_auth_test_cases[] = { + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha256), + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha384), + KUNIT_CASE(test_nvme_auth_derive_tls_psk_hmac_sha512), + {}, +}; + +static struct kunit_suite nvme_auth_test_suite = { + .name = "nvme-auth", + .test_cases = nvme_auth_test_cases, +}; +kunit_test_suite(nvme_auth_test_suite); + +MODULE_DESCRIPTION("Unit tests for NVMe authentication functions"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index 405e7c03b1cf..bbedbe181c8a 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -7,7 +7,6 @@ #include <linux/base64.h> #include <linux/prandom.h> #include <linux/unaligned.h> -#include <crypto/hash.h> #include <crypto/dh.h> #include "nvme.h" #include "fabrics.h" @@ -22,7 +21,6 @@ struct nvme_dhchap_queue_context { struct list_head entry; struct work_struct auth_work; struct nvme_ctrl *ctrl; - struct crypto_shash *shash_tfm; struct crypto_kpp *dh_tfm; struct nvme_dhchap_key *transformed_key; void *buf; @@ -38,9 +36,9 @@ struct nvme_dhchap_queue_context { u8 hash_id; u8 sc_c; size_t hash_len; - u8 c1[64]; - u8 c2[64]; - u8 response[64]; + u8 c1[NVME_AUTH_MAX_DIGEST_SIZE]; + u8 c2[NVME_AUTH_MAX_DIGEST_SIZE]; + u8 response[NVME_AUTH_MAX_DIGEST_SIZE]; u8 *ctrl_key; u8 *host_key; u8 *sess_key; @@ -125,6 +123,8 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, { struct nvmf_auth_dhchap_negotiate_data *data = chap->buf; size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol); + u8 dh_list_offset = NVME_AUTH_DHCHAP_MAX_DH_IDS; + u8 *idlist = data->auth_protocol[0].dhchap.idlist; if (size > CHAP_BUF_SIZE) { chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; @@ -141,21 +141,22 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, data->sc_c = NVME_AUTH_SECP_NEWTLSPSK; } else data->sc_c = NVME_AUTH_SECP_NOSC; + chap->sc_c = data->sc_c; data->napd = 1; data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID; data->auth_protocol[0].dhchap.halen = 3; - data->auth_protocol[0].dhchap.dhlen = 6; - data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_HASH_SHA256; - data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_HASH_SHA384; - data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_HASH_SHA512; - data->auth_protocol[0].dhchap.idlist[30] = NVME_AUTH_DHGROUP_NULL; - data->auth_protocol[0].dhchap.idlist[31] = NVME_AUTH_DHGROUP_2048; - data->auth_protocol[0].dhchap.idlist[32] = NVME_AUTH_DHGROUP_3072; - data->auth_protocol[0].dhchap.idlist[33] = NVME_AUTH_DHGROUP_4096; - data->auth_protocol[0].dhchap.idlist[34] = NVME_AUTH_DHGROUP_6144; - data->auth_protocol[0].dhchap.idlist[35] = NVME_AUTH_DHGROUP_8192; - - chap->sc_c = data->sc_c; + idlist[0] = NVME_AUTH_HASH_SHA256; + idlist[1] = NVME_AUTH_HASH_SHA384; + idlist[2] = NVME_AUTH_HASH_SHA512; + if (chap->sc_c == NVME_AUTH_SECP_NOSC) + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_NULL; + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_2048; + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_3072; + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_4096; + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_6144; + idlist[dh_list_offset++] = NVME_AUTH_DHGROUP_8192; + data->auth_protocol[0].dhchap.dhlen = + dh_list_offset - NVME_AUTH_DHCHAP_MAX_DH_IDS; return size; } @@ -183,38 +184,17 @@ static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, return -EPROTO; } - if (chap->hash_id == data->hashid && chap->shash_tfm && - !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) && - crypto_shash_digestsize(chap->shash_tfm) == data->hl) { + if (chap->hash_id == data->hashid && chap->hash_len == data->hl) { dev_dbg(ctrl->device, "qid %d: reuse existing hash %s\n", chap->qid, hmac_name); goto select_kpp; } - /* Reset if hash cannot be reused */ - if (chap->shash_tfm) { - crypto_free_shash(chap->shash_tfm); - chap->hash_id = 0; - chap->hash_len = 0; - } - chap->shash_tfm = crypto_alloc_shash(hmac_name, 0, - CRYPTO_ALG_ALLOCATES_MEMORY); - if (IS_ERR(chap->shash_tfm)) { - dev_warn(ctrl->device, - "qid %d: failed to allocate hash %s, error %ld\n", - chap->qid, hmac_name, PTR_ERR(chap->shash_tfm)); - chap->shash_tfm = NULL; - chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; - return -ENOMEM; - } - - if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) { + if (nvme_auth_hmac_hash_len(data->hashid) != data->hl) { dev_warn(ctrl->device, "qid %d: invalid hash length %d\n", chap->qid, data->hl); - crypto_free_shash(chap->shash_tfm); - chap->shash_tfm = NULL; chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; return -EPROTO; } @@ -434,7 +414,7 @@ static int nvme_auth_set_dhchap_failure2_data(struct nvme_ctrl *ctrl, static int nvme_auth_dhchap_setup_host_response(struct nvme_ctrl *ctrl, struct nvme_dhchap_queue_context *chap) { - SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + struct nvme_auth_hmac_ctx hmac; u8 buf[4], *challenge = chap->c1; int ret; @@ -454,13 +434,11 @@ static int nvme_auth_dhchap_setup_host_response(struct nvme_ctrl *ctrl, __func__, chap->qid); } - ret = crypto_shash_setkey(chap->shash_tfm, - chap->transformed_key->key, chap->transformed_key->len); - if (ret) { - dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n", - chap->qid, ret); + ret = nvme_auth_hmac_init(&hmac, chap->hash_id, + chap->transformed_key->key, + chap->transformed_key->len); + if (ret) goto out; - } if (chap->dh_tfm) { challenge = kmalloc(chap->hash_len, GFP_KERNEL); @@ -477,51 +455,36 @@ static int nvme_auth_dhchap_setup_host_response(struct nvme_ctrl *ctrl, goto out; } - shash->tfm = chap->shash_tfm; - ret = crypto_shash_init(shash); - if (ret) - goto out; - ret = crypto_shash_update(shash, challenge, chap->hash_len); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, challenge, chap->hash_len); + put_unaligned_le32(chap->s1, buf); - ret = crypto_shash_update(shash, buf, 4); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 4); + put_unaligned_le16(chap->transaction, buf); - ret = crypto_shash_update(shash, buf, 2); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 2); + *buf = chap->sc_c; - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, "HostHost", 8); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->opts->host->nqn, - strlen(ctrl->opts->host->nqn)); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, "HostHost", 8); + nvme_auth_hmac_update(&hmac, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); memset(buf, 0, sizeof(buf)); - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->opts->subsysnqn, - strlen(ctrl->opts->subsysnqn)); - if (ret) - goto out; - ret = crypto_shash_final(shash, chap->response); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn)); + nvme_auth_hmac_final(&hmac, chap->response); + ret = 0; out: if (challenge != chap->c1) kfree(challenge); + memzero_explicit(&hmac, sizeof(hmac)); return ret; } static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl, struct nvme_dhchap_queue_context *chap) { - SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + struct nvme_auth_hmac_ctx hmac; struct nvme_dhchap_key *transformed_key; u8 buf[4], *challenge = chap->c2; int ret; @@ -533,10 +496,10 @@ static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl, return ret; } - ret = crypto_shash_setkey(chap->shash_tfm, - transformed_key->key, transformed_key->len); + ret = nvme_auth_hmac_init(&hmac, chap->hash_id, transformed_key->key, + transformed_key->len); if (ret) { - dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n", + dev_warn(ctrl->device, "qid %d: failed to init hmac, error %d\n", chap->qid, ret); goto out; } @@ -563,43 +526,29 @@ static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl, __func__, chap->qid, ctrl->opts->subsysnqn); dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n", __func__, chap->qid, ctrl->opts->host->nqn); - shash->tfm = chap->shash_tfm; - ret = crypto_shash_init(shash); - if (ret) - goto out; - ret = crypto_shash_update(shash, challenge, chap->hash_len); - if (ret) - goto out; + + nvme_auth_hmac_update(&hmac, challenge, chap->hash_len); + put_unaligned_le32(chap->s2, buf); - ret = crypto_shash_update(shash, buf, 4); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 4); + put_unaligned_le16(chap->transaction, buf); - ret = crypto_shash_update(shash, buf, 2); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 2); + memset(buf, 0, 4); - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, "Controller", 10); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->opts->subsysnqn, - strlen(ctrl->opts->subsysnqn)); - if (ret) - goto out; - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->opts->host->nqn, - strlen(ctrl->opts->host->nqn)); - if (ret) - goto out; - ret = crypto_shash_final(shash, chap->response); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, "Controller", 10); + nvme_auth_hmac_update(&hmac, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn)); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); + nvme_auth_hmac_final(&hmac, chap->response); + ret = 0; out: if (challenge != chap->c2) kfree(challenge); + memzero_explicit(&hmac, sizeof(hmac)); nvme_auth_free_key(transformed_key); return ret; } @@ -689,8 +638,6 @@ static void nvme_auth_free_dhchap(struct nvme_dhchap_queue_context *chap) { nvme_auth_reset_dhchap(chap); chap->authenticated = false; - if (chap->shash_tfm) - crypto_free_shash(chap->shash_tfm); if (chap->dh_tfm) crypto_free_kpp(chap->dh_tfm); } @@ -708,7 +655,8 @@ EXPORT_SYMBOL_GPL(nvme_auth_revoke_tls_key); static int nvme_auth_secure_concat(struct nvme_ctrl *ctrl, struct nvme_dhchap_queue_context *chap) { - u8 *psk, *digest, *tls_psk; + u8 *psk, *tls_psk; + char *digest; struct key *tls_key; size_t psk_len; int ret = 0; @@ -1071,12 +1019,11 @@ int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) INIT_WORK(&ctrl->dhchap_auth_work, nvme_ctrl_auth_work); if (!ctrl->opts) return 0; - ret = nvme_auth_generate_key(ctrl->opts->dhchap_secret, - &ctrl->host_key); + ret = nvme_auth_parse_key(ctrl->opts->dhchap_secret, &ctrl->host_key); if (ret) return ret; - ret = nvme_auth_generate_key(ctrl->opts->dhchap_ctrl_secret, - &ctrl->ctrl_key); + ret = nvme_auth_parse_key(ctrl->opts->dhchap_ctrl_secret, + &ctrl->ctrl_key); if (ret) goto err_free_dhchap_secret; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 766e9cc4ffca..1e33af94c24b 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1875,6 +1875,7 @@ static bool nvme_init_integrity(struct nvme_ns_head *head, break; } + bi->flags |= BLK_SPLIT_INTERVAL_CAPABLE; bi->metadata_size = head->ms; if (bi->csum_type) { bi->pi_tuple_size = head->pi_size; @@ -1883,26 +1884,6 @@ static bool nvme_init_integrity(struct nvme_ns_head *head, return true; } -static void nvme_config_discard(struct nvme_ns *ns, struct queue_limits *lim) -{ - struct nvme_ctrl *ctrl = ns->ctrl; - - if (ctrl->dmrsl && ctrl->dmrsl <= nvme_sect_to_lba(ns->head, UINT_MAX)) - lim->max_hw_discard_sectors = - nvme_lba_to_sect(ns->head, ctrl->dmrsl); - else if (ctrl->oncs & NVME_CTRL_ONCS_DSM) - lim->max_hw_discard_sectors = UINT_MAX; - else - lim->max_hw_discard_sectors = 0; - - lim->discard_granularity = lim->logical_block_size; - - if (ctrl->dmrl) - lim->max_discard_segments = ctrl->dmrl; - else - lim->max_discard_segments = NVME_DSM_MAX_RANGES; -} - static bool nvme_ns_ids_equal(struct nvme_ns_ids *a, struct nvme_ns_ids *b) { return uuid_equal(&a->uuid, &b->uuid) && @@ -2078,12 +2059,15 @@ static void nvme_set_ctrl_limits(struct nvme_ctrl *ctrl, } static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id, - struct queue_limits *lim) + struct nvme_id_ns_nvm *nvm, struct queue_limits *lim) { struct nvme_ns_head *head = ns->head; + struct nvme_ctrl *ctrl = ns->ctrl; u32 bs = 1U << head->lba_shift; u32 atomic_bs, phys_bs, io_opt = 0; + u32 npdg = 1, npda = 1; bool valid = true; + u8 optperf; /* * The block layer can't support LBA sizes larger than the page size @@ -2098,7 +2082,12 @@ static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id, phys_bs = bs; atomic_bs = nvme_configure_atomic_write(ns, id, lim, bs); - if (id->nsfeat & NVME_NS_FEAT_IO_OPT) { + optperf = id->nsfeat >> NVME_NS_FEAT_OPTPERF_SHIFT; + if (ctrl->vs >= NVME_VS(2, 1, 0)) + optperf &= NVME_NS_FEAT_OPTPERF_MASK_2_1; + else + optperf &= NVME_NS_FEAT_OPTPERF_MASK; + if (optperf) { /* NPWG = Namespace Preferred Write Granularity */ phys_bs = bs * (1 + le16_to_cpu(id->npwg)); /* NOWS = Namespace Optimal Write Size */ @@ -2115,11 +2104,54 @@ static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id, lim->physical_block_size = min(phys_bs, atomic_bs); lim->io_min = phys_bs; lim->io_opt = io_opt; - if ((ns->ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES) && - (ns->ctrl->oncs & NVME_CTRL_ONCS_DSM)) + if ((ctrl->quirks & NVME_QUIRK_DEALLOCATE_ZEROES) && + (ctrl->oncs & NVME_CTRL_ONCS_DSM)) lim->max_write_zeroes_sectors = UINT_MAX; else - lim->max_write_zeroes_sectors = ns->ctrl->max_zeroes_sectors; + lim->max_write_zeroes_sectors = ctrl->max_zeroes_sectors; + + if (ctrl->dmrsl && ctrl->dmrsl <= nvme_sect_to_lba(ns->head, UINT_MAX)) + lim->max_hw_discard_sectors = + nvme_lba_to_sect(ns->head, ctrl->dmrsl); + else if (ctrl->oncs & NVME_CTRL_ONCS_DSM) + lim->max_hw_discard_sectors = UINT_MAX; + else + lim->max_hw_discard_sectors = 0; + + /* + * NVMe namespaces advertise both a preferred deallocate granularity + * (for a discard length) and alignment (for a discard starting offset). + * However, Linux block devices advertise a single discard_granularity. + * From NVM Command Set specification 1.1 section 5.2.2, the NPDGL/NPDAL + * fields in the NVM Command Set Specific Identify Namespace structure + * are preferred to NPDG/NPDA in the Identify Namespace structure since + * they can represent larger values. However, NPDGL or NPDAL may be 0 if + * unsupported. NPDG and NPDA are 0's based. + * From Figure 115 of NVM Command Set specification 1.1, NPDGL and NPDAL + * are supported if the high bit of OPTPERF is set. NPDG is supported if + * the low bit of OPTPERF is set. NPDA is supported if either is set. + * NPDG should be a multiple of NPDA, and likewise NPDGL should be a + * multiple of NPDAL, but the spec doesn't say anything about NPDG vs. + * NPDAL or NPDGL vs. NPDA. So compute the maximum instead of assuming + * NPDG(L) is the larger. If neither NPDG, NPDGL, NPDA, nor NPDAL are + * supported, default the discard_granularity to the logical block size. + */ + if (optperf & 0x2 && nvm && nvm->npdgl) + npdg = le32_to_cpu(nvm->npdgl); + else if (optperf & 0x1) + npdg = from0based(id->npdg); + if (optperf & 0x2 && nvm && nvm->npdal) + npda = le32_to_cpu(nvm->npdal); + else if (optperf) + npda = from0based(id->npda); + if (check_mul_overflow(max(npdg, npda), lim->logical_block_size, + &lim->discard_granularity)) + lim->discard_granularity = lim->logical_block_size; + + if (ctrl->dmrl) + lim->max_discard_segments = ctrl->dmrl; + else + lim->max_discard_segments = NVME_DSM_MAX_RANGES; return valid; } @@ -2353,7 +2385,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, } lbaf = nvme_lbaf_index(id->flbas); - if (ns->ctrl->ctratt & NVME_CTRL_ATTR_ELBAS) { + if (nvme_id_cns_ok(ns->ctrl, NVME_ID_CNS_CS_NS)) { ret = nvme_identify_ns_nvm(ns->ctrl, info->nsid, &nvm); if (ret < 0) goto out; @@ -2381,10 +2413,9 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, nvme_set_ctrl_limits(ns->ctrl, &lim, false); nvme_configure_metadata(ns->ctrl, ns->head, id, nvm, info); nvme_set_chunk_sectors(ns, id, &lim); - if (!nvme_update_disk_info(ns, id, &lim)) + if (!nvme_update_disk_info(ns, id, nvm, &lim)) capacity = 0; - nvme_config_discard(ns, &lim); if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) && ns->head->ids.csi == NVME_CSI_ZNS) nvme_update_zone_info(ns, &lim, &zi); @@ -3388,7 +3419,7 @@ static int nvme_init_non_mdts_limits(struct nvme_ctrl *ctrl) ctrl->dmrl = id->dmrl; ctrl->dmrsl = le32_to_cpu(id->dmrsl); - if (id->wzsl) + if (id->wzsl && !(ctrl->quirks & NVME_QUIRK_DISABLE_WRITE_ZEROES)) ctrl->max_zeroes_sectors = nvme_mps_to_sectors(ctrl, id->wzsl); free_data: diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index fc6800a9f7f9..ba00f0b72b85 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -154,21 +154,8 @@ void nvme_failover_req(struct request *req) } spin_lock_irqsave(&ns->head->requeue_lock, flags); - for (bio = req->bio; bio; bio = bio->bi_next) { + for (bio = req->bio; bio; bio = bio->bi_next) bio_set_dev(bio, ns->head->disk->part0); - if (bio->bi_opf & REQ_POLLED) { - bio->bi_opf &= ~REQ_POLLED; - bio->bi_cookie = BLK_QC_T_NONE; - } - /* - * The alternate request queue that we may end up submitting - * the bio to may be frozen temporarily, in this case REQ_NOWAIT - * will fail the I/O immediately with EAGAIN to the issuer. - * We are not in the issuer context which cannot block. Clear - * the flag to avoid spurious EAGAIN I/O failures. - */ - bio->bi_opf &= ~REQ_NOWAIT; - } blk_steal_bios(&ns->head->requeue_list, req); spin_unlock_irqrestore(&ns->head->requeue_lock, flags); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 9971045dbc05..ccd5e05dac98 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -762,6 +762,12 @@ static inline u32 nvme_bytes_to_numd(size_t len) return (len >> 2) - 1; } +/* Decode a 2-byte "0's based"/"0-based" field */ +static inline u32 from0based(__le16 value) +{ + return (u32)le16_to_cpu(value) + 1; +} + static inline bool nvme_is_ana_error(u16 status) { switch (status & NVME_SCT_SC_MASK) { diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index b78ba239c8ea..db5fc9bf6627 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -4178,6 +4178,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x2646, 0x501E), /* KINGSTON OM3PGP4xxxxQ OS21011 NVMe SSD */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + { PCI_DEVICE(0x2646, 0x502F), /* KINGSTON OM3SGP4xxxxK NVMe SSD */ + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x1f40, 0x1202), /* Netac Technologies Co. NV3000 NVMe SSD */ .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1f40, 0x5236), /* Netac Technologies Co. NV7000 NVMe SSD */ diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c index 16c6fea4b2db..7bf2e972126b 100644 --- a/drivers/nvme/host/sysfs.c +++ b/drivers/nvme/host/sysfs.c @@ -658,7 +658,7 @@ static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev, struct nvme_dhchap_key *key, *host_key; int ret; - ret = nvme_auth_generate_key(dhchap_secret, &key); + ret = nvme_auth_parse_key(dhchap_secret, &key); if (ret) { kfree(dhchap_secret); return ret; @@ -716,7 +716,7 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev, struct nvme_dhchap_key *key, *ctrl_key; int ret; - ret = nvme_auth_generate_key(dhchap_secret, &key); + ret = nvme_auth_parse_key(dhchap_secret, &key); if (ret) { kfree(dhchap_secret); return ret; @@ -829,7 +829,49 @@ static ssize_t tls_configured_key_show(struct device *dev, return sysfs_emit(buf, "%08x\n", key_serial(key)); } -static DEVICE_ATTR_RO(tls_configured_key); + +static ssize_t tls_configured_key_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + int error, qid; + + error = kstrtoint(buf, 10, &qid); + if (error) + return error; + + /* + * We currently only allow userspace to write a `0` indicating + * generate a new key. + */ + if (qid) + return -EINVAL; + + if (!ctrl->opts || !ctrl->opts->concat) + return -EOPNOTSUPP; + + error = nvme_auth_negotiate(ctrl, 0); + if (error < 0) { + nvme_reset_ctrl(ctrl); + return error; + } + + error = nvme_auth_wait(ctrl, 0); + if (error < 0) { + nvme_reset_ctrl(ctrl); + return error; + } + + /* + * We need to reset the TLS connection, so let's just + * reset the controller. + */ + nvme_reset_ctrl(ctrl); + + return count; +} +static DEVICE_ATTR_RW(tls_configured_key); static ssize_t tls_keyring_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -861,7 +903,7 @@ static umode_t nvme_tls_attrs_are_visible(struct kobject *kobj, !ctrl->opts->tls && !ctrl->opts->concat) return 0; if (a == &dev_attr_tls_configured_key.attr && - (!ctrl->opts->tls_key || ctrl->opts->concat)) + !ctrl->opts->concat) return 0; if (a == &dev_attr_tls_keyring.attr && !ctrl->opts->keyring) diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index ca5b08ce1211..e4fd1caadfb0 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -1057,6 +1057,8 @@ static void nvme_execute_identify_ns_nvm(struct nvmet_req *req) status = NVME_SC_INTERNAL; goto out; } + if (req->ns->bdev) + nvmet_bdev_set_nvm_limits(req->ns->bdev, id); status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); kfree(id); out: @@ -1603,7 +1605,7 @@ void nvmet_execute_keep_alive(struct nvmet_req *req) pr_debug("ctrl %d update keep-alive timer for %d secs\n", ctrl->cntlid, ctrl->kato); - mod_delayed_work(system_wq, &ctrl->ka_work, ctrl->kato * HZ); + mod_delayed_work(system_percpu_wq, &ctrl->ka_work, ctrl->kato * HZ); out: nvmet_req_complete(req, status); } diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index 2eadeb7e06f2..b34610e2f19d 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -9,7 +9,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/err.h> -#include <crypto/hash.h> #include <linux/crc32.h> #include <linux/base64.h> #include <linux/ctype.h> @@ -45,15 +44,6 @@ int nvmet_auth_set_key(struct nvmet_host *host, const char *secret, key_hash); return -EINVAL; } - if (key_hash > 0) { - /* Validate selected hash algorithm */ - const char *hmac = nvme_auth_hmac_name(key_hash); - - if (!crypto_has_shash(hmac, 0, 0)) { - pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac); - return -ENOTSUPP; - } - } dhchap_secret = kstrdup(secret, GFP_KERNEL); if (!dhchap_secret) return -ENOMEM; @@ -140,7 +130,7 @@ int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id) return ret; } -u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq) +u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset) { int ret = 0; struct nvmet_host_link *p; @@ -166,7 +156,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq) goto out_unlock; } - if (nvmet_queue_tls_keyid(sq)) { + if (!reset && nvmet_queue_tls_keyid(sq)) { pr_debug("host %s tls enabled\n", ctrl->hostnqn); goto out_unlock; } @@ -292,47 +282,30 @@ bool nvmet_check_auth_status(struct nvmet_req *req) int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, unsigned int shash_len) { - struct crypto_shash *shash_tfm; - SHASH_DESC_ON_STACK(shash, shash_tfm); + struct nvme_auth_hmac_ctx hmac; struct nvmet_ctrl *ctrl = req->sq->ctrl; - const char *hash_name; u8 *challenge = req->sq->dhchap_c1; struct nvme_dhchap_key *transformed_key; u8 buf[4]; int ret; - hash_name = nvme_auth_hmac_name(ctrl->shash_id); - if (!hash_name) { - pr_warn("Hash ID %d invalid\n", ctrl->shash_id); - return -EINVAL; - } - - shash_tfm = crypto_alloc_shash(hash_name, 0, 0); - if (IS_ERR(shash_tfm)) { - pr_err("failed to allocate shash %s\n", hash_name); - return PTR_ERR(shash_tfm); - } - - if (shash_len != crypto_shash_digestsize(shash_tfm)) { - pr_err("%s: hash len mismatch (len %d digest %d)\n", - __func__, shash_len, - crypto_shash_digestsize(shash_tfm)); - ret = -EINVAL; - goto out_free_tfm; - } - transformed_key = nvme_auth_transform_key(ctrl->host_key, ctrl->hostnqn); - if (IS_ERR(transformed_key)) { - ret = PTR_ERR(transformed_key); - goto out_free_tfm; - } + if (IS_ERR(transformed_key)) + return PTR_ERR(transformed_key); - ret = crypto_shash_setkey(shash_tfm, transformed_key->key, + ret = nvme_auth_hmac_init(&hmac, ctrl->shash_id, transformed_key->key, transformed_key->len); if (ret) goto out_free_response; + if (shash_len != nvme_auth_hmac_hash_len(ctrl->shash_id)) { + pr_err("%s: hash len mismatch (len %u digest %zu)\n", __func__, + shash_len, nvme_auth_hmac_hash_len(ctrl->shash_id)); + ret = -EINVAL; + goto out_free_response; + } + if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { challenge = kmalloc(shash_len, GFP_KERNEL); if (!challenge) { @@ -345,101 +318,67 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, req->sq->dhchap_c1, challenge, shash_len); if (ret) - goto out; + goto out_free_challenge; } pr_debug("ctrl %d qid %d host response seq %u transaction %d\n", ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, req->sq->dhchap_tid); - shash->tfm = shash_tfm; - ret = crypto_shash_init(shash); - if (ret) - goto out; - ret = crypto_shash_update(shash, challenge, shash_len); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, challenge, shash_len); + put_unaligned_le32(req->sq->dhchap_s1, buf); - ret = crypto_shash_update(shash, buf, 4); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 4); + put_unaligned_le16(req->sq->dhchap_tid, buf); - ret = crypto_shash_update(shash, buf, 2); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 2); + *buf = req->sq->sc_c; - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, "HostHost", 8); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, "HostHost", 8); memset(buf, 0, 4); - ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); - if (ret) - goto out; - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->subsys->subsysnqn, - strlen(ctrl->subsys->subsysnqn)); - if (ret) - goto out; - ret = crypto_shash_final(shash, response); -out: + nvme_auth_hmac_update(&hmac, ctrl->hostnqn, strlen(ctrl->hostnqn)); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, ctrl->subsys->subsysnqn, + strlen(ctrl->subsys->subsysnqn)); + nvme_auth_hmac_final(&hmac, response); + ret = 0; +out_free_challenge: if (challenge != req->sq->dhchap_c1) kfree(challenge); out_free_response: + memzero_explicit(&hmac, sizeof(hmac)); nvme_auth_free_key(transformed_key); -out_free_tfm: - crypto_free_shash(shash_tfm); return ret; } int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, unsigned int shash_len) { - struct crypto_shash *shash_tfm; - struct shash_desc *shash; + struct nvme_auth_hmac_ctx hmac; struct nvmet_ctrl *ctrl = req->sq->ctrl; - const char *hash_name; u8 *challenge = req->sq->dhchap_c2; struct nvme_dhchap_key *transformed_key; u8 buf[4]; int ret; - hash_name = nvme_auth_hmac_name(ctrl->shash_id); - if (!hash_name) { - pr_warn("Hash ID %d invalid\n", ctrl->shash_id); - return -EINVAL; - } - - shash_tfm = crypto_alloc_shash(hash_name, 0, 0); - if (IS_ERR(shash_tfm)) { - pr_err("failed to allocate shash %s\n", hash_name); - return PTR_ERR(shash_tfm); - } - - if (shash_len != crypto_shash_digestsize(shash_tfm)) { - pr_debug("%s: hash len mismatch (len %d digest %d)\n", - __func__, shash_len, - crypto_shash_digestsize(shash_tfm)); - ret = -EINVAL; - goto out_free_tfm; - } - transformed_key = nvme_auth_transform_key(ctrl->ctrl_key, ctrl->subsys->subsysnqn); - if (IS_ERR(transformed_key)) { - ret = PTR_ERR(transformed_key); - goto out_free_tfm; - } + if (IS_ERR(transformed_key)) + return PTR_ERR(transformed_key); - ret = crypto_shash_setkey(shash_tfm, transformed_key->key, + ret = nvme_auth_hmac_init(&hmac, ctrl->shash_id, transformed_key->key, transformed_key->len); if (ret) goto out_free_response; + if (shash_len != nvme_auth_hmac_hash_len(ctrl->shash_id)) { + pr_err("%s: hash len mismatch (len %u digest %zu)\n", __func__, + shash_len, nvme_auth_hmac_hash_len(ctrl->shash_id)); + ret = -EINVAL; + goto out_free_response; + } + if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { challenge = kmalloc(shash_len, GFP_KERNEL); if (!challenge) { @@ -455,55 +394,29 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, goto out_free_challenge; } - shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), - GFP_KERNEL); - if (!shash) { - ret = -ENOMEM; - goto out_free_challenge; - } - shash->tfm = shash_tfm; + nvme_auth_hmac_update(&hmac, challenge, shash_len); - ret = crypto_shash_init(shash); - if (ret) - goto out; - ret = crypto_shash_update(shash, challenge, shash_len); - if (ret) - goto out; put_unaligned_le32(req->sq->dhchap_s2, buf); - ret = crypto_shash_update(shash, buf, 4); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 4); + put_unaligned_le16(req->sq->dhchap_tid, buf); - ret = crypto_shash_update(shash, buf, 2); - if (ret) - goto out; + nvme_auth_hmac_update(&hmac, buf, 2); + memset(buf, 0, 4); - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, "Controller", 10); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->subsys->subsysnqn, - strlen(ctrl->subsys->subsysnqn)); - if (ret) - goto out; - ret = crypto_shash_update(shash, buf, 1); - if (ret) - goto out; - ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); - if (ret) - goto out; - ret = crypto_shash_final(shash, response); -out: - kfree(shash); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, "Controller", 10); + nvme_auth_hmac_update(&hmac, ctrl->subsys->subsysnqn, + strlen(ctrl->subsys->subsysnqn)); + nvme_auth_hmac_update(&hmac, buf, 1); + nvme_auth_hmac_update(&hmac, ctrl->hostnqn, strlen(ctrl->hostnqn)); + nvme_auth_hmac_final(&hmac, response); + ret = 0; out_free_challenge: if (challenge != req->sq->dhchap_c2) kfree(challenge); out_free_response: + memzero_explicit(&hmac, sizeof(hmac)); nvme_auth_free_key(transformed_key); -out_free_tfm: - crypto_free_shash(shash_tfm); return ret; } @@ -531,7 +444,7 @@ int nvmet_auth_ctrl_exponential(struct nvmet_req *req, } int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, - u8 *pkey, int pkey_size) + const u8 *pkey, int pkey_size) { struct nvmet_ctrl *ctrl = req->sq->ctrl; int ret; @@ -557,7 +470,8 @@ int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, void nvmet_auth_insert_psk(struct nvmet_sq *sq) { int hash_len = nvme_auth_hmac_hash_len(sq->ctrl->shash_id); - u8 *psk, *digest, *tls_psk; + u8 *psk, *tls_psk; + char *digest; size_t psk_len; int ret; #ifdef CONFIG_NVME_TARGET_TCP_TLS diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 3088e044dbcb..463348c7f097 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -17,7 +17,6 @@ #include <linux/nvme-auth.h> #endif #include <linux/nvme-keyring.h> -#include <crypto/hash.h> #include <crypto/kpp.h> #include <linux/nospec.h> @@ -2181,8 +2180,6 @@ static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item, hmac_id = nvme_auth_hmac_id(page); if (hmac_id == NVME_AUTH_HASH_INVALID) return -EINVAL; - if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0)) - return -ENOTSUPP; host->dhchap_hash_id = hmac_id; return count; } diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 9238e13bd480..45f686175fea 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -1688,7 +1688,7 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) if (args->hostid) uuid_copy(&ctrl->hostid, args->hostid); - dhchap_status = nvmet_setup_auth(ctrl, args->sq); + dhchap_status = nvmet_setup_auth(ctrl, args->sq, false); if (dhchap_status) { pr_err("Failed to setup authentication, dhchap status %u\n", dhchap_status); @@ -1944,12 +1944,13 @@ static int __init nvmet_init(void) if (!nvmet_bvec_cache) return -ENOMEM; - zbd_wq = alloc_workqueue("nvmet-zbd-wq", WQ_MEM_RECLAIM, 0); + zbd_wq = alloc_workqueue("nvmet-zbd-wq", WQ_MEM_RECLAIM | WQ_PERCPU, + 0); if (!zbd_wq) goto out_destroy_bvec_cache; buffered_io_wq = alloc_workqueue("nvmet-buffered-io-wq", - WQ_MEM_RECLAIM, 0); + WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!buffered_io_wq) goto out_free_zbd_work_queue; diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 5946681cb0e3..b9ab80c7a694 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -8,7 +8,6 @@ #include <linux/blkdev.h> #include <linux/random.h> #include <linux/nvme-auth.h> -#include <crypto/hash.h> #include <crypto/kpp.h> #include "nvmet.h" @@ -75,8 +74,7 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d) for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) { u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i]; - if (!fallback_hash_id && - crypto_has_shash(nvme_auth_hmac_name(host_hmac_id), 0, 0)) + if (!fallback_hash_id && nvme_auth_hmac_hash_len(host_hmac_id)) fallback_hash_id = host_hmac_id; if (ctrl->shash_id != host_hmac_id) continue; @@ -293,7 +291,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req) pr_debug("%s: ctrl %d qid %d reset negotiation\n", __func__, ctrl->cntlid, req->sq->qid); if (!req->sq->qid) { - dhchap_status = nvmet_setup_auth(ctrl, req->sq); + dhchap_status = nvmet_setup_auth(ctrl, req->sq, + true); if (dhchap_status) { pr_err("ctrl %d qid 0 failed to setup re-authentication\n", ctrl->cntlid); @@ -391,14 +390,15 @@ done: req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) { unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120; - mod_delayed_work(system_wq, &req->sq->auth_expired_work, + mod_delayed_work(system_percpu_wq, &req->sq->auth_expired_work, auth_expire_secs * HZ); goto complete; } /* Final states, clear up variables */ - nvmet_auth_sq_free(req->sq); - if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) { + nvmet_auth_sq_free(req->sq); nvmet_ctrl_fatal_error(ctrl); + } complete: nvmet_req_complete(req, status); @@ -574,9 +574,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req) status = nvmet_copy_to_sgl(req, 0, d, al); kfree(d); done: - if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2) - nvmet_auth_sq_free(req->sq); - else if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { nvmet_auth_sq_free(req->sq); nvmet_ctrl_fatal_error(ctrl); } diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index 4eaadc711c99..d161707559ce 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -792,9 +792,9 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc, if (!queue) return NULL; - queue->work_q = alloc_workqueue("ntfc%d.%d.%d", 0, 0, - assoc->tgtport->fc_target_port.port_num, - assoc->a_id, qid); + queue->work_q = alloc_workqueue("ntfc%d.%d.%d", WQ_PERCPU, 0, + assoc->tgtport->fc_target_port.port_num, + assoc->a_id, qid); if (!queue->work_q) goto out_free_queue; diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c index f15d1c213bc6..f2d9e8901df4 100644 --- a/drivers/nvme/target/io-cmd-bdev.c +++ b/drivers/nvme/target/io-cmd-bdev.c @@ -30,11 +30,11 @@ void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id) id->nacwu = lpp0b; /* - * Bit 4 indicates that the fields NPWG, NPWA, NPDG, NPDA, and - * NOWS are defined for this namespace and should be used by - * the host for I/O optimization. + * OPTPERF = 11b indicates that the fields NPWG, NPWA, NPDG, NPDA, + * NPDGL, NPDAL, and NOWS are defined for this namespace and should be + * used by the host for I/O optimization. */ - id->nsfeat |= 1 << 4; + id->nsfeat |= 0x3 << NVME_NS_FEAT_OPTPERF_SHIFT; /* NPWG = Namespace Preferred Write Granularity. 0's based */ id->npwg = to0based(bdev_io_min(bdev) / bdev_logical_block_size(bdev)); /* NPWA = Namespace Preferred Write Alignment. 0's based */ @@ -52,6 +52,17 @@ void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id) id->dlfeat = (1 << 3) | 0x1; } +void nvmet_bdev_set_nvm_limits(struct block_device *bdev, + struct nvme_id_ns_nvm *id) +{ + /* + * NPDGL = Namespace Preferred Deallocate Granularity Large + * NPDAL = Namespace Preferred Deallocate Alignment Large + */ + id->npdgl = id->npdal = cpu_to_le32(bdev_discard_granularity(bdev) / + bdev_logical_block_size(bdev)); +} + void nvmet_bdev_ns_disable(struct nvmet_ns *ns) { if (ns->bdev_file) { diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index 4b3f4f11928d..d98d0cdc5d6f 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -419,7 +419,6 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) { if (ctrl->ctrl.queue_count > 1) { nvme_quiesce_io_queues(&ctrl->ctrl); - nvme_cancel_tagset(&ctrl->ctrl); nvme_loop_destroy_io_queues(ctrl); } @@ -427,7 +426,6 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) if (nvme_ctrl_state(&ctrl->ctrl) == NVME_CTRL_LIVE) nvme_disable_ctrl(&ctrl->ctrl, true); - nvme_cancel_admin_tagset(&ctrl->ctrl); nvme_loop_destroy_admin_queue(ctrl); } diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 319d6a5e9cf0..50070cfb782a 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -550,6 +550,8 @@ void nvmet_stop_keep_alive_timer(struct nvmet_ctrl *ctrl); u16 nvmet_parse_connect_cmd(struct nvmet_req *req); u32 nvmet_connect_cmd_data_len(struct nvmet_req *req); void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id); +void nvmet_bdev_set_nvm_limits(struct block_device *bdev, + struct nvme_id_ns_nvm *id); u16 nvmet_bdev_parse_io_cmd(struct nvmet_req *req); u16 nvmet_file_parse_io_cmd(struct nvmet_req *req); u16 nvmet_bdev_zns_parse_io_cmd(struct nvmet_req *req); @@ -896,7 +898,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req); int nvmet_auth_set_key(struct nvmet_host *host, const char *secret, bool set_ctrl); int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash); -u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq); +u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset); void nvmet_auth_sq_init(struct nvmet_sq *sq); void nvmet_destroy_auth(struct nvmet_ctrl *ctrl); void nvmet_auth_sq_free(struct nvmet_sq *sq); @@ -913,11 +915,11 @@ static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq) int nvmet_auth_ctrl_exponential(struct nvmet_req *req, u8 *buf, int buf_size); int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, - u8 *buf, int buf_size); + const u8 *pkey, int pkey_size); void nvmet_auth_insert_psk(struct nvmet_sq *sq); #else static inline u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, - struct nvmet_sq *sq) + struct nvmet_sq *sq, bool reset) { return 0; } diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index acc71a26733f..4b8b02341ddc 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -2225,7 +2225,7 @@ static int __init nvmet_tcp_init(void) int ret; nvmet_tcp_wq = alloc_workqueue("nvmet_tcp_wq", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0); if (!nvmet_tcp_wq) return -ENOMEM; |
