summaryrefslogtreecommitdiff
path: root/drivers/nvme
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 15:51:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-13 15:51:31 -0700
commit7fe6ac157b7e15c8976bd62ad7cb98e248884e83 (patch)
tree64677a680f3bccc7efb8f4cfcb288006e1433cd3 /drivers/nvme
parentb8f82cb0d84d00c04cdbdce42f67df71b8507e8b (diff)
parent36446de0c30c62b9d89502fd36c4904996d86ecd (diff)
downloadlinux-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/.kunitconfig6
-rw-r--r--drivers/nvme/common/Kconfig14
-rw-r--r--drivers/nvme/common/Makefile2
-rw-r--r--drivers/nvme/common/auth.c587
-rw-r--r--drivers/nvme/common/tests/auth_kunit.c175
-rw-r--r--drivers/nvme/host/auth.c187
-rw-r--r--drivers/nvme/host/core.c89
-rw-r--r--drivers/nvme/host/multipath.c15
-rw-r--r--drivers/nvme/host/nvme.h6
-rw-r--r--drivers/nvme/host/pci.c2
-rw-r--r--drivers/nvme/host/sysfs.c50
-rw-r--r--drivers/nvme/target/admin-cmd.c4
-rw-r--r--drivers/nvme/target/auth.c202
-rw-r--r--drivers/nvme/target/configfs.c3
-rw-r--r--drivers/nvme/target/core.c7
-rw-r--r--drivers/nvme/target/fabrics-cmd-auth.c18
-rw-r--r--drivers/nvme/target/fc.c6
-rw-r--r--drivers/nvme/target/io-cmd-bdev.c19
-rw-r--r--drivers/nvme/target/loop.c2
-rw-r--r--drivers/nvme/target/nvmet.h8
-rw-r--r--drivers/nvme/target/tcp.c2
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;