188eea45cSKlaus Jensen /*
288eea45cSKlaus Jensen * QEMU NVM Express End-to-End Data Protection support
388eea45cSKlaus Jensen *
488eea45cSKlaus Jensen * Copyright (c) 2021 Samsung Electronics Co., Ltd.
588eea45cSKlaus Jensen *
688eea45cSKlaus Jensen * Authors:
788eea45cSKlaus Jensen * Klaus Jensen <k.jensen@samsung.com>
888eea45cSKlaus Jensen * Gollu Appalanaidu <anaidu.gollu@samsung.com>
988eea45cSKlaus Jensen */
1088eea45cSKlaus Jensen
1188eea45cSKlaus Jensen #include "qemu/osdep.h"
1288eea45cSKlaus Jensen #include "qapi/error.h"
1388eea45cSKlaus Jensen #include "sysemu/block-backend.h"
1488eea45cSKlaus Jensen
1588eea45cSKlaus Jensen #include "nvme.h"
1605f7ae45SKlaus Jensen #include "dif.h"
1788eea45cSKlaus Jensen #include "trace.h"
1888eea45cSKlaus Jensen
nvme_check_prinfo(NvmeNamespace * ns,uint8_t prinfo,uint64_t slba,uint64_t reftag)192a132309SKlaus Jensen uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
2044219b60SNaveen Nagar uint64_t reftag)
2188eea45cSKlaus Jensen {
2244219b60SNaveen Nagar uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff;
2344219b60SNaveen Nagar
2488eea45cSKlaus Jensen if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
2544219b60SNaveen Nagar (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) {
2688eea45cSKlaus Jensen return NVME_INVALID_PROT_INFO | NVME_DNR;
2788eea45cSKlaus Jensen }
2888eea45cSKlaus Jensen
29d7fe639cSDmitry Tikhov if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_3) &&
30d7fe639cSDmitry Tikhov (prinfo & NVME_PRINFO_PRCHK_REF)) {
31d7fe639cSDmitry Tikhov return NVME_INVALID_PROT_INFO;
32d7fe639cSDmitry Tikhov }
33d7fe639cSDmitry Tikhov
3488eea45cSKlaus Jensen return NVME_SUCCESS;
3588eea45cSKlaus Jensen }
3688eea45cSKlaus Jensen
3788eea45cSKlaus Jensen /* from Linux kernel (crypto/crct10dif_common.c) */
crc16_t10dif(uint16_t crc,const unsigned char * buffer,size_t len)3844219b60SNaveen Nagar static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer,
3988eea45cSKlaus Jensen size_t len)
4088eea45cSKlaus Jensen {
4188eea45cSKlaus Jensen unsigned int i;
4288eea45cSKlaus Jensen
4388eea45cSKlaus Jensen for (i = 0; i < len; i++) {
4444219b60SNaveen Nagar crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff];
4588eea45cSKlaus Jensen }
4688eea45cSKlaus Jensen
4788eea45cSKlaus Jensen return crc;
4888eea45cSKlaus Jensen }
4988eea45cSKlaus Jensen
5044219b60SNaveen Nagar /* from Linux kernel (lib/crc64.c) */
crc64_nvme(uint64_t crc,const unsigned char * buffer,size_t len)5144219b60SNaveen Nagar static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer,
5244219b60SNaveen Nagar size_t len)
5344219b60SNaveen Nagar {
5444219b60SNaveen Nagar size_t i;
5544219b60SNaveen Nagar
5644219b60SNaveen Nagar for (i = 0; i < len; i++) {
5744219b60SNaveen Nagar crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]];
5844219b60SNaveen Nagar }
5944219b60SNaveen Nagar
6044219b60SNaveen Nagar return crc ^ (uint64_t)~0;
6144219b60SNaveen Nagar }
6244219b60SNaveen Nagar
nvme_dif_pract_generate_dif_crc16(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)6344219b60SNaveen Nagar static void nvme_dif_pract_generate_dif_crc16(NvmeNamespace *ns, uint8_t *buf,
6444219b60SNaveen Nagar size_t len, uint8_t *mbuf,
6544219b60SNaveen Nagar size_t mlen, uint16_t apptag,
6644219b60SNaveen Nagar uint64_t *reftag)
6788eea45cSKlaus Jensen {
6888eea45cSKlaus Jensen uint8_t *end = buf + len;
6988eea45cSKlaus Jensen int16_t pil = 0;
7088eea45cSKlaus Jensen
7188eea45cSKlaus Jensen if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
72ac0b34c5SKlaus Jensen pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
7388eea45cSKlaus Jensen }
7488eea45cSKlaus Jensen
7544219b60SNaveen Nagar trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz,
7644219b60SNaveen Nagar ns->lbasz + pil, apptag,
7744219b60SNaveen Nagar *reftag);
7888eea45cSKlaus Jensen
7988eea45cSKlaus Jensen for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
8088eea45cSKlaus Jensen NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
8144219b60SNaveen Nagar uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
8288eea45cSKlaus Jensen
8388eea45cSKlaus Jensen if (pil) {
8444219b60SNaveen Nagar crc = crc16_t10dif(crc, mbuf, pil);
8588eea45cSKlaus Jensen }
8688eea45cSKlaus Jensen
8744219b60SNaveen Nagar dif->g16.guard = cpu_to_be16(crc);
8844219b60SNaveen Nagar dif->g16.apptag = cpu_to_be16(apptag);
8944219b60SNaveen Nagar dif->g16.reftag = cpu_to_be32(*reftag);
9088eea45cSKlaus Jensen
9188eea45cSKlaus Jensen if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
920ca5c3ccSKlaus Jensen (*reftag)++;
9388eea45cSKlaus Jensen }
9488eea45cSKlaus Jensen }
9588eea45cSKlaus Jensen }
9688eea45cSKlaus Jensen
nvme_dif_pract_generate_dif_crc64(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)9744219b60SNaveen Nagar static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf,
9844219b60SNaveen Nagar size_t len, uint8_t *mbuf,
9944219b60SNaveen Nagar size_t mlen, uint16_t apptag,
10044219b60SNaveen Nagar uint64_t *reftag)
10144219b60SNaveen Nagar {
10244219b60SNaveen Nagar uint8_t *end = buf + len;
10344219b60SNaveen Nagar int16_t pil = 0;
10444219b60SNaveen Nagar
10544219b60SNaveen Nagar if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
10644219b60SNaveen Nagar pil = ns->lbaf.ms - 16;
10744219b60SNaveen Nagar }
10844219b60SNaveen Nagar
10944219b60SNaveen Nagar trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz,
11044219b60SNaveen Nagar ns->lbasz + pil, apptag,
11144219b60SNaveen Nagar *reftag);
11244219b60SNaveen Nagar
11344219b60SNaveen Nagar for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
11444219b60SNaveen Nagar NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
11544219b60SNaveen Nagar uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
11644219b60SNaveen Nagar
11744219b60SNaveen Nagar if (pil) {
118*dbdb13f9SAnkit Kumar crc = crc64_nvme(~crc, mbuf, pil);
11944219b60SNaveen Nagar }
12044219b60SNaveen Nagar
12144219b60SNaveen Nagar dif->g64.guard = cpu_to_be64(crc);
12244219b60SNaveen Nagar dif->g64.apptag = cpu_to_be16(apptag);
12344219b60SNaveen Nagar
12444219b60SNaveen Nagar dif->g64.sr[0] = *reftag >> 40;
12544219b60SNaveen Nagar dif->g64.sr[1] = *reftag >> 32;
12644219b60SNaveen Nagar dif->g64.sr[2] = *reftag >> 24;
12744219b60SNaveen Nagar dif->g64.sr[3] = *reftag >> 16;
12844219b60SNaveen Nagar dif->g64.sr[4] = *reftag >> 8;
12944219b60SNaveen Nagar dif->g64.sr[5] = *reftag;
13044219b60SNaveen Nagar
13144219b60SNaveen Nagar if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
13244219b60SNaveen Nagar (*reftag)++;
13344219b60SNaveen Nagar }
13444219b60SNaveen Nagar }
13544219b60SNaveen Nagar }
13644219b60SNaveen Nagar
nvme_dif_pract_generate_dif(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint16_t apptag,uint64_t * reftag)13744219b60SNaveen Nagar void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
13844219b60SNaveen Nagar uint8_t *mbuf, size_t mlen, uint16_t apptag,
13944219b60SNaveen Nagar uint64_t *reftag)
14044219b60SNaveen Nagar {
14144219b60SNaveen Nagar switch (ns->pif) {
14244219b60SNaveen Nagar case NVME_PI_GUARD_16:
14344219b60SNaveen Nagar return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen,
14444219b60SNaveen Nagar apptag, reftag);
14544219b60SNaveen Nagar case NVME_PI_GUARD_64:
14644219b60SNaveen Nagar return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen,
14744219b60SNaveen Nagar apptag, reftag);
14844219b60SNaveen Nagar }
14944219b60SNaveen Nagar
15044219b60SNaveen Nagar abort();
15144219b60SNaveen Nagar }
15244219b60SNaveen Nagar
nvme_dif_prchk_crc16(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)15344219b60SNaveen Nagar static uint16_t nvme_dif_prchk_crc16(NvmeNamespace *ns, NvmeDifTuple *dif,
15488eea45cSKlaus Jensen uint8_t *buf, uint8_t *mbuf, size_t pil,
1552a132309SKlaus Jensen uint8_t prinfo, uint16_t apptag,
15644219b60SNaveen Nagar uint16_t appmask, uint64_t reftag)
15788eea45cSKlaus Jensen {
15888eea45cSKlaus Jensen switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
15988eea45cSKlaus Jensen case NVME_ID_NS_DPS_TYPE_3:
16044219b60SNaveen Nagar if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) {
16188eea45cSKlaus Jensen break;
16288eea45cSKlaus Jensen }
16388eea45cSKlaus Jensen
16488eea45cSKlaus Jensen /* fallthrough */
16588eea45cSKlaus Jensen case NVME_ID_NS_DPS_TYPE_1:
16688eea45cSKlaus Jensen case NVME_ID_NS_DPS_TYPE_2:
16744219b60SNaveen Nagar if (be16_to_cpu(dif->g16.apptag) != 0xffff) {
16888eea45cSKlaus Jensen break;
16988eea45cSKlaus Jensen }
17088eea45cSKlaus Jensen
17144219b60SNaveen Nagar trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag),
17244219b60SNaveen Nagar be32_to_cpu(dif->g16.reftag));
17388eea45cSKlaus Jensen
17488eea45cSKlaus Jensen return NVME_SUCCESS;
17588eea45cSKlaus Jensen }
17688eea45cSKlaus Jensen
1772a132309SKlaus Jensen if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
17844219b60SNaveen Nagar uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
17988eea45cSKlaus Jensen
18088eea45cSKlaus Jensen if (pil) {
18144219b60SNaveen Nagar crc = crc16_t10dif(crc, mbuf, pil);
18288eea45cSKlaus Jensen }
18388eea45cSKlaus Jensen
18444219b60SNaveen Nagar trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc);
18588eea45cSKlaus Jensen
18644219b60SNaveen Nagar if (be16_to_cpu(dif->g16.guard) != crc) {
18788eea45cSKlaus Jensen return NVME_E2E_GUARD_ERROR;
18888eea45cSKlaus Jensen }
18988eea45cSKlaus Jensen }
19088eea45cSKlaus Jensen
1912a132309SKlaus Jensen if (prinfo & NVME_PRINFO_PRCHK_APP) {
19244219b60SNaveen Nagar trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag,
19388eea45cSKlaus Jensen appmask);
19488eea45cSKlaus Jensen
19544219b60SNaveen Nagar if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) {
19688eea45cSKlaus Jensen return NVME_E2E_APP_ERROR;
19788eea45cSKlaus Jensen }
19888eea45cSKlaus Jensen }
19988eea45cSKlaus Jensen
2002a132309SKlaus Jensen if (prinfo & NVME_PRINFO_PRCHK_REF) {
20144219b60SNaveen Nagar trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag),
20244219b60SNaveen Nagar reftag);
20388eea45cSKlaus Jensen
20444219b60SNaveen Nagar if (be32_to_cpu(dif->g16.reftag) != reftag) {
20588eea45cSKlaus Jensen return NVME_E2E_REF_ERROR;
20688eea45cSKlaus Jensen }
20788eea45cSKlaus Jensen }
20888eea45cSKlaus Jensen
20988eea45cSKlaus Jensen return NVME_SUCCESS;
21088eea45cSKlaus Jensen }
21188eea45cSKlaus Jensen
nvme_dif_prchk_crc64(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)21244219b60SNaveen Nagar static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif,
21344219b60SNaveen Nagar uint8_t *buf, uint8_t *mbuf, size_t pil,
21444219b60SNaveen Nagar uint8_t prinfo, uint16_t apptag,
21544219b60SNaveen Nagar uint16_t appmask, uint64_t reftag)
21644219b60SNaveen Nagar {
21744219b60SNaveen Nagar uint64_t r = 0;
21844219b60SNaveen Nagar
21944219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[0] << 40;
22044219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[1] << 32;
22144219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[2] << 24;
22244219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[3] << 16;
22344219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[4] << 8;
22444219b60SNaveen Nagar r |= (uint64_t)dif->g64.sr[5];
22544219b60SNaveen Nagar
22644219b60SNaveen Nagar switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
22744219b60SNaveen Nagar case NVME_ID_NS_DPS_TYPE_3:
22844219b60SNaveen Nagar if (r != 0xffffffffffff) {
22944219b60SNaveen Nagar break;
23044219b60SNaveen Nagar }
23144219b60SNaveen Nagar
23244219b60SNaveen Nagar /* fallthrough */
23344219b60SNaveen Nagar case NVME_ID_NS_DPS_TYPE_1:
23444219b60SNaveen Nagar case NVME_ID_NS_DPS_TYPE_2:
23544219b60SNaveen Nagar if (be16_to_cpu(dif->g64.apptag) != 0xffff) {
23644219b60SNaveen Nagar break;
23744219b60SNaveen Nagar }
23844219b60SNaveen Nagar
23944219b60SNaveen Nagar trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag),
24044219b60SNaveen Nagar r);
24144219b60SNaveen Nagar
24244219b60SNaveen Nagar return NVME_SUCCESS;
24344219b60SNaveen Nagar }
24444219b60SNaveen Nagar
24544219b60SNaveen Nagar if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
24644219b60SNaveen Nagar uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
24744219b60SNaveen Nagar
24844219b60SNaveen Nagar if (pil) {
249*dbdb13f9SAnkit Kumar crc = crc64_nvme(~crc, mbuf, pil);
25044219b60SNaveen Nagar }
25144219b60SNaveen Nagar
25244219b60SNaveen Nagar trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc);
25344219b60SNaveen Nagar
25444219b60SNaveen Nagar if (be64_to_cpu(dif->g64.guard) != crc) {
25544219b60SNaveen Nagar return NVME_E2E_GUARD_ERROR;
25644219b60SNaveen Nagar }
25744219b60SNaveen Nagar }
25844219b60SNaveen Nagar
25944219b60SNaveen Nagar if (prinfo & NVME_PRINFO_PRCHK_APP) {
26044219b60SNaveen Nagar trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag,
26144219b60SNaveen Nagar appmask);
26244219b60SNaveen Nagar
26344219b60SNaveen Nagar if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) {
26444219b60SNaveen Nagar return NVME_E2E_APP_ERROR;
26544219b60SNaveen Nagar }
26644219b60SNaveen Nagar }
26744219b60SNaveen Nagar
26844219b60SNaveen Nagar if (prinfo & NVME_PRINFO_PRCHK_REF) {
26944219b60SNaveen Nagar trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag);
27044219b60SNaveen Nagar
27144219b60SNaveen Nagar if (r != reftag) {
27244219b60SNaveen Nagar return NVME_E2E_REF_ERROR;
27344219b60SNaveen Nagar }
27444219b60SNaveen Nagar }
27544219b60SNaveen Nagar
27644219b60SNaveen Nagar return NVME_SUCCESS;
27744219b60SNaveen Nagar }
27844219b60SNaveen Nagar
nvme_dif_prchk(NvmeNamespace * ns,NvmeDifTuple * dif,uint8_t * buf,uint8_t * mbuf,size_t pil,uint8_t prinfo,uint16_t apptag,uint16_t appmask,uint64_t reftag)27944219b60SNaveen Nagar static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
28044219b60SNaveen Nagar uint8_t *buf, uint8_t *mbuf, size_t pil,
28144219b60SNaveen Nagar uint8_t prinfo, uint16_t apptag,
28244219b60SNaveen Nagar uint16_t appmask, uint64_t reftag)
28344219b60SNaveen Nagar {
28444219b60SNaveen Nagar switch (ns->pif) {
28544219b60SNaveen Nagar case NVME_PI_GUARD_16:
28644219b60SNaveen Nagar return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag,
28744219b60SNaveen Nagar appmask, reftag);
28844219b60SNaveen Nagar case NVME_PI_GUARD_64:
28944219b60SNaveen Nagar return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag,
29044219b60SNaveen Nagar appmask, reftag);
29144219b60SNaveen Nagar }
29244219b60SNaveen Nagar
29344219b60SNaveen Nagar abort();
29444219b60SNaveen Nagar }
29544219b60SNaveen Nagar
nvme_dif_check(NvmeNamespace * ns,uint8_t * buf,size_t len,uint8_t * mbuf,size_t mlen,uint8_t prinfo,uint64_t slba,uint16_t apptag,uint16_t appmask,uint64_t * reftag)29688eea45cSKlaus Jensen uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
2972a132309SKlaus Jensen uint8_t *mbuf, size_t mlen, uint8_t prinfo,
29888eea45cSKlaus Jensen uint64_t slba, uint16_t apptag,
29944219b60SNaveen Nagar uint16_t appmask, uint64_t *reftag)
30088eea45cSKlaus Jensen {
30144219b60SNaveen Nagar uint8_t *bufp, *end = buf + len;
30288eea45cSKlaus Jensen int16_t pil = 0;
30388eea45cSKlaus Jensen uint16_t status;
30488eea45cSKlaus Jensen
3052a132309SKlaus Jensen status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
30688eea45cSKlaus Jensen if (status) {
30788eea45cSKlaus Jensen return status;
30888eea45cSKlaus Jensen }
30988eea45cSKlaus Jensen
31088eea45cSKlaus Jensen if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
311ac0b34c5SKlaus Jensen pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
31288eea45cSKlaus Jensen }
31388eea45cSKlaus Jensen
3142a132309SKlaus Jensen trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
31588eea45cSKlaus Jensen
31644219b60SNaveen Nagar for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) {
31788eea45cSKlaus Jensen NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
31844219b60SNaveen Nagar status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag,
3190ca5c3ccSKlaus Jensen appmask, *reftag);
32088eea45cSKlaus Jensen if (status) {
32144219b60SNaveen Nagar /*
32244219b60SNaveen Nagar * The first block of a 'raw' image is always allocated, so we
32344219b60SNaveen Nagar * cannot reliably know if the block is all zeroes or not. For
32444219b60SNaveen Nagar * CRC16 this works fine because the T10 CRC16 is 0x0 for all
32544219b60SNaveen Nagar * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is
32644219b60SNaveen Nagar * detected for the first block, check if it is zeroed and manually
32744219b60SNaveen Nagar * set the protection information to all ones to disable protection
32844219b60SNaveen Nagar * information checking.
32944219b60SNaveen Nagar */
33044219b60SNaveen Nagar if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) {
33144219b60SNaveen Nagar g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz);
33244219b60SNaveen Nagar
33344219b60SNaveen Nagar if (memcmp(bufp, zeroes, ns->lbasz) == 0) {
33444219b60SNaveen Nagar memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns));
33544219b60SNaveen Nagar }
33644219b60SNaveen Nagar } else {
33788eea45cSKlaus Jensen return status;
33888eea45cSKlaus Jensen }
33944219b60SNaveen Nagar }
34088eea45cSKlaus Jensen
34188eea45cSKlaus Jensen if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
3420ca5c3ccSKlaus Jensen (*reftag)++;
34388eea45cSKlaus Jensen }
34488eea45cSKlaus Jensen }
34588eea45cSKlaus Jensen
34688eea45cSKlaus Jensen return NVME_SUCCESS;
34788eea45cSKlaus Jensen }
34888eea45cSKlaus Jensen
nvme_dif_mangle_mdata(NvmeNamespace * ns,uint8_t * mbuf,size_t mlen,uint64_t slba)34988eea45cSKlaus Jensen uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
35088eea45cSKlaus Jensen uint64_t slba)
35188eea45cSKlaus Jensen {
35288eea45cSKlaus Jensen BlockBackend *blk = ns->blkconf.blk;
35388eea45cSKlaus Jensen BlockDriverState *bs = blk_bs(blk);
35488eea45cSKlaus Jensen
35588eea45cSKlaus Jensen int64_t moffset = 0, offset = nvme_l2b(ns, slba);
35688eea45cSKlaus Jensen uint8_t *mbufp, *end;
35788eea45cSKlaus Jensen bool zeroed;
35888eea45cSKlaus Jensen int16_t pil = 0;
35988eea45cSKlaus Jensen int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
36088eea45cSKlaus Jensen int64_t pnum = 0;
36188eea45cSKlaus Jensen
36288eea45cSKlaus Jensen Error *err = NULL;
36388eea45cSKlaus Jensen
36488eea45cSKlaus Jensen
36588eea45cSKlaus Jensen if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
366ac0b34c5SKlaus Jensen pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
36788eea45cSKlaus Jensen }
36888eea45cSKlaus Jensen
36988eea45cSKlaus Jensen do {
37088eea45cSKlaus Jensen int ret;
37188eea45cSKlaus Jensen
37288eea45cSKlaus Jensen bytes -= pnum;
37388eea45cSKlaus Jensen
37488eea45cSKlaus Jensen ret = bdrv_block_status(bs, offset, bytes, &pnum, NULL, NULL);
37588eea45cSKlaus Jensen if (ret < 0) {
37688eea45cSKlaus Jensen error_setg_errno(&err, -ret, "unable to get block status");
37788eea45cSKlaus Jensen error_report_err(err);
37888eea45cSKlaus Jensen
37988eea45cSKlaus Jensen return NVME_INTERNAL_DEV_ERROR;
38088eea45cSKlaus Jensen }
38188eea45cSKlaus Jensen
38288eea45cSKlaus Jensen zeroed = !!(ret & BDRV_BLOCK_ZERO);
38388eea45cSKlaus Jensen
38488eea45cSKlaus Jensen trace_pci_nvme_block_status(offset, bytes, pnum, ret, zeroed);
38588eea45cSKlaus Jensen
38688eea45cSKlaus Jensen if (zeroed) {
38788eea45cSKlaus Jensen mbufp = mbuf + moffset;
38888eea45cSKlaus Jensen mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
38988eea45cSKlaus Jensen end = mbufp + mlen;
39088eea45cSKlaus Jensen
39188eea45cSKlaus Jensen for (; mbufp < end; mbufp += ns->lbaf.ms) {
392ac0b34c5SKlaus Jensen memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns));
39388eea45cSKlaus Jensen }
39488eea45cSKlaus Jensen }
39588eea45cSKlaus Jensen
39688eea45cSKlaus Jensen moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
39788eea45cSKlaus Jensen offset += pnum;
39888eea45cSKlaus Jensen } while (pnum != bytes);
39988eea45cSKlaus Jensen
40088eea45cSKlaus Jensen return NVME_SUCCESS;
40188eea45cSKlaus Jensen }
40288eea45cSKlaus Jensen
nvme_dif_rw_cb(void * opaque,int ret)40388eea45cSKlaus Jensen static void nvme_dif_rw_cb(void *opaque, int ret)
40488eea45cSKlaus Jensen {
40588eea45cSKlaus Jensen NvmeBounceContext *ctx = opaque;
40688eea45cSKlaus Jensen NvmeRequest *req = ctx->req;
40788eea45cSKlaus Jensen NvmeNamespace *ns = req->ns;
40888eea45cSKlaus Jensen BlockBackend *blk = ns->blkconf.blk;
40988eea45cSKlaus Jensen
41088eea45cSKlaus Jensen trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
41188eea45cSKlaus Jensen
41288eea45cSKlaus Jensen qemu_iovec_destroy(&ctx->data.iov);
41388eea45cSKlaus Jensen g_free(ctx->data.bounce);
41488eea45cSKlaus Jensen
41588eea45cSKlaus Jensen qemu_iovec_destroy(&ctx->mdata.iov);
41688eea45cSKlaus Jensen g_free(ctx->mdata.bounce);
41788eea45cSKlaus Jensen
41888eea45cSKlaus Jensen g_free(ctx);
41988eea45cSKlaus Jensen
42088eea45cSKlaus Jensen nvme_rw_complete_cb(req, ret);
42188eea45cSKlaus Jensen }
42288eea45cSKlaus Jensen
nvme_dif_rw_check_cb(void * opaque,int ret)42388eea45cSKlaus Jensen static void nvme_dif_rw_check_cb(void *opaque, int ret)
42488eea45cSKlaus Jensen {
42588eea45cSKlaus Jensen NvmeBounceContext *ctx = opaque;
42688eea45cSKlaus Jensen NvmeRequest *req = ctx->req;
42788eea45cSKlaus Jensen NvmeNamespace *ns = req->ns;
42888eea45cSKlaus Jensen NvmeCtrl *n = nvme_ctrl(req);
42988eea45cSKlaus Jensen NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
43088eea45cSKlaus Jensen uint64_t slba = le64_to_cpu(rw->slba);
4312a132309SKlaus Jensen uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
43288eea45cSKlaus Jensen uint16_t apptag = le16_to_cpu(rw->apptag);
43388eea45cSKlaus Jensen uint16_t appmask = le16_to_cpu(rw->appmask);
43444219b60SNaveen Nagar uint64_t reftag = le32_to_cpu(rw->reftag);
43544219b60SNaveen Nagar uint64_t cdw3 = le32_to_cpu(rw->cdw3);
43688eea45cSKlaus Jensen uint16_t status;
43788eea45cSKlaus Jensen
43844219b60SNaveen Nagar reftag |= cdw3 << 32;
43944219b60SNaveen Nagar
4402a132309SKlaus Jensen trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
4412a132309SKlaus Jensen reftag);
44288eea45cSKlaus Jensen
44388eea45cSKlaus Jensen if (ret) {
44488eea45cSKlaus Jensen goto out;
44588eea45cSKlaus Jensen }
44688eea45cSKlaus Jensen
44788eea45cSKlaus Jensen status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce, ctx->mdata.iov.size,
44888eea45cSKlaus Jensen slba);
44988eea45cSKlaus Jensen if (status) {
45088eea45cSKlaus Jensen req->status = status;
45188eea45cSKlaus Jensen goto out;
45288eea45cSKlaus Jensen }
45388eea45cSKlaus Jensen
45488eea45cSKlaus Jensen status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
4552a132309SKlaus Jensen ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
4560ca5c3ccSKlaus Jensen slba, apptag, appmask, &reftag);
45788eea45cSKlaus Jensen if (status) {
45888eea45cSKlaus Jensen req->status = status;
45988eea45cSKlaus Jensen goto out;
46088eea45cSKlaus Jensen }
46188eea45cSKlaus Jensen
46288eea45cSKlaus Jensen status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
46388eea45cSKlaus Jensen NVME_TX_DIRECTION_FROM_DEVICE, req);
46488eea45cSKlaus Jensen if (status) {
46588eea45cSKlaus Jensen req->status = status;
46688eea45cSKlaus Jensen goto out;
46788eea45cSKlaus Jensen }
46888eea45cSKlaus Jensen
469ac0b34c5SKlaus Jensen if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
47088eea45cSKlaus Jensen goto out;
47188eea45cSKlaus Jensen }
47288eea45cSKlaus Jensen
47388eea45cSKlaus Jensen status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
47488eea45cSKlaus Jensen NVME_TX_DIRECTION_FROM_DEVICE, req);
47588eea45cSKlaus Jensen if (status) {
47688eea45cSKlaus Jensen req->status = status;
47788eea45cSKlaus Jensen }
47888eea45cSKlaus Jensen
47988eea45cSKlaus Jensen out:
48088eea45cSKlaus Jensen nvme_dif_rw_cb(ctx, ret);
48188eea45cSKlaus Jensen }
48288eea45cSKlaus Jensen
nvme_dif_rw_mdata_in_cb(void * opaque,int ret)48388eea45cSKlaus Jensen static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
48488eea45cSKlaus Jensen {
48588eea45cSKlaus Jensen NvmeBounceContext *ctx = opaque;
48688eea45cSKlaus Jensen NvmeRequest *req = ctx->req;
48788eea45cSKlaus Jensen NvmeNamespace *ns = req->ns;
48888eea45cSKlaus Jensen NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
48988eea45cSKlaus Jensen uint64_t slba = le64_to_cpu(rw->slba);
49088eea45cSKlaus Jensen uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
49188eea45cSKlaus Jensen size_t mlen = nvme_m2b(ns, nlb);
49288eea45cSKlaus Jensen uint64_t offset = nvme_moff(ns, slba);
49388eea45cSKlaus Jensen BlockBackend *blk = ns->blkconf.blk;
49488eea45cSKlaus Jensen
49588eea45cSKlaus Jensen trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
49688eea45cSKlaus Jensen
49788eea45cSKlaus Jensen if (ret) {
49888eea45cSKlaus Jensen goto out;
49988eea45cSKlaus Jensen }
50088eea45cSKlaus Jensen
50188eea45cSKlaus Jensen ctx->mdata.bounce = g_malloc(mlen);
50288eea45cSKlaus Jensen
50388eea45cSKlaus Jensen qemu_iovec_reset(&ctx->mdata.iov);
50488eea45cSKlaus Jensen qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
50588eea45cSKlaus Jensen
50688eea45cSKlaus Jensen req->aiocb = blk_aio_preadv(blk, offset, &ctx->mdata.iov, 0,
50788eea45cSKlaus Jensen nvme_dif_rw_check_cb, ctx);
50888eea45cSKlaus Jensen return;
50988eea45cSKlaus Jensen
51088eea45cSKlaus Jensen out:
51188eea45cSKlaus Jensen nvme_dif_rw_cb(ctx, ret);
51288eea45cSKlaus Jensen }
51388eea45cSKlaus Jensen
nvme_dif_rw_mdata_out_cb(void * opaque,int ret)51488eea45cSKlaus Jensen static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
51588eea45cSKlaus Jensen {
51688eea45cSKlaus Jensen NvmeBounceContext *ctx = opaque;
51788eea45cSKlaus Jensen NvmeRequest *req = ctx->req;
51888eea45cSKlaus Jensen NvmeNamespace *ns = req->ns;
51988eea45cSKlaus Jensen NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
52088eea45cSKlaus Jensen uint64_t slba = le64_to_cpu(rw->slba);
52188eea45cSKlaus Jensen uint64_t offset = nvme_moff(ns, slba);
52288eea45cSKlaus Jensen BlockBackend *blk = ns->blkconf.blk;
52388eea45cSKlaus Jensen
52488eea45cSKlaus Jensen trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
52588eea45cSKlaus Jensen
52688eea45cSKlaus Jensen if (ret) {
52788eea45cSKlaus Jensen goto out;
52888eea45cSKlaus Jensen }
52988eea45cSKlaus Jensen
53088eea45cSKlaus Jensen req->aiocb = blk_aio_pwritev(blk, offset, &ctx->mdata.iov, 0,
53188eea45cSKlaus Jensen nvme_dif_rw_cb, ctx);
53288eea45cSKlaus Jensen return;
53388eea45cSKlaus Jensen
53488eea45cSKlaus Jensen out:
53588eea45cSKlaus Jensen nvme_dif_rw_cb(ctx, ret);
53688eea45cSKlaus Jensen }
53788eea45cSKlaus Jensen
nvme_dif_rw(NvmeCtrl * n,NvmeRequest * req)53888eea45cSKlaus Jensen uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
53988eea45cSKlaus Jensen {
54088eea45cSKlaus Jensen NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
54188eea45cSKlaus Jensen NvmeNamespace *ns = req->ns;
54288eea45cSKlaus Jensen BlockBackend *blk = ns->blkconf.blk;
54388eea45cSKlaus Jensen bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
54488eea45cSKlaus Jensen uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
54588eea45cSKlaus Jensen uint64_t slba = le64_to_cpu(rw->slba);
54688eea45cSKlaus Jensen size_t len = nvme_l2b(ns, nlb);
54788eea45cSKlaus Jensen size_t mlen = nvme_m2b(ns, nlb);
54888eea45cSKlaus Jensen size_t mapped_len = len;
54988eea45cSKlaus Jensen int64_t offset = nvme_l2b(ns, slba);
5502a132309SKlaus Jensen uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
55188eea45cSKlaus Jensen uint16_t apptag = le16_to_cpu(rw->apptag);
55288eea45cSKlaus Jensen uint16_t appmask = le16_to_cpu(rw->appmask);
55344219b60SNaveen Nagar uint64_t reftag = le32_to_cpu(rw->reftag);
55444219b60SNaveen Nagar uint64_t cdw3 = le32_to_cpu(rw->cdw3);
5552a132309SKlaus Jensen bool pract = !!(prinfo & NVME_PRINFO_PRACT);
55688eea45cSKlaus Jensen NvmeBounceContext *ctx;
55788eea45cSKlaus Jensen uint16_t status;
55888eea45cSKlaus Jensen
55944219b60SNaveen Nagar reftag |= cdw3 << 32;
56044219b60SNaveen Nagar
5612a132309SKlaus Jensen trace_pci_nvme_dif_rw(pract, prinfo);
56288eea45cSKlaus Jensen
56388eea45cSKlaus Jensen ctx = g_new0(NvmeBounceContext, 1);
56488eea45cSKlaus Jensen ctx->req = req;
56588eea45cSKlaus Jensen
56688eea45cSKlaus Jensen if (wrz) {
56788eea45cSKlaus Jensen BdrvRequestFlags flags = BDRV_REQ_MAY_UNMAP;
56888eea45cSKlaus Jensen
5692a132309SKlaus Jensen if (prinfo & NVME_PRINFO_PRCHK_MASK) {
57088eea45cSKlaus Jensen status = NVME_INVALID_PROT_INFO | NVME_DNR;
57188eea45cSKlaus Jensen goto err;
57288eea45cSKlaus Jensen }
57388eea45cSKlaus Jensen
57488eea45cSKlaus Jensen if (pract) {
57588eea45cSKlaus Jensen uint8_t *mbuf, *end;
576ac0b34c5SKlaus Jensen int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
57788eea45cSKlaus Jensen
57888eea45cSKlaus Jensen flags = 0;
57988eea45cSKlaus Jensen
58088eea45cSKlaus Jensen ctx->mdata.bounce = g_malloc0(mlen);
58188eea45cSKlaus Jensen
58288eea45cSKlaus Jensen qemu_iovec_init(&ctx->mdata.iov, 1);
58388eea45cSKlaus Jensen qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
58488eea45cSKlaus Jensen
58588eea45cSKlaus Jensen mbuf = ctx->mdata.bounce;
58688eea45cSKlaus Jensen end = mbuf + mlen;
58788eea45cSKlaus Jensen
58888eea45cSKlaus Jensen if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
58988eea45cSKlaus Jensen pil = 0;
59088eea45cSKlaus Jensen }
59188eea45cSKlaus Jensen
59288eea45cSKlaus Jensen for (; mbuf < end; mbuf += ns->lbaf.ms) {
59388eea45cSKlaus Jensen NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
59488eea45cSKlaus Jensen
59544219b60SNaveen Nagar switch (ns->pif) {
59644219b60SNaveen Nagar case NVME_PI_GUARD_16:
59744219b60SNaveen Nagar dif->g16.apptag = cpu_to_be16(apptag);
59844219b60SNaveen Nagar dif->g16.reftag = cpu_to_be32(reftag);
59944219b60SNaveen Nagar
60044219b60SNaveen Nagar break;
60144219b60SNaveen Nagar
60244219b60SNaveen Nagar case NVME_PI_GUARD_64:
60344219b60SNaveen Nagar dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e);
60444219b60SNaveen Nagar dif->g64.apptag = cpu_to_be16(apptag);
60544219b60SNaveen Nagar
60644219b60SNaveen Nagar dif->g64.sr[0] = reftag >> 40;
60744219b60SNaveen Nagar dif->g64.sr[1] = reftag >> 32;
60844219b60SNaveen Nagar dif->g64.sr[2] = reftag >> 24;
60944219b60SNaveen Nagar dif->g64.sr[3] = reftag >> 16;
61044219b60SNaveen Nagar dif->g64.sr[4] = reftag >> 8;
61144219b60SNaveen Nagar dif->g64.sr[5] = reftag;
61244219b60SNaveen Nagar
61344219b60SNaveen Nagar break;
61444219b60SNaveen Nagar
61544219b60SNaveen Nagar default:
61644219b60SNaveen Nagar abort();
61744219b60SNaveen Nagar }
61888eea45cSKlaus Jensen
61988eea45cSKlaus Jensen switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
62088eea45cSKlaus Jensen case NVME_ID_NS_DPS_TYPE_1:
62188eea45cSKlaus Jensen case NVME_ID_NS_DPS_TYPE_2:
62288eea45cSKlaus Jensen reftag++;
62388eea45cSKlaus Jensen }
62488eea45cSKlaus Jensen }
62588eea45cSKlaus Jensen }
62688eea45cSKlaus Jensen
62788eea45cSKlaus Jensen req->aiocb = blk_aio_pwrite_zeroes(blk, offset, len, flags,
62888eea45cSKlaus Jensen nvme_dif_rw_mdata_out_cb, ctx);
62988eea45cSKlaus Jensen return NVME_NO_COMPLETE;
63088eea45cSKlaus Jensen }
63188eea45cSKlaus Jensen
632ac0b34c5SKlaus Jensen if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
63388eea45cSKlaus Jensen mapped_len += mlen;
63488eea45cSKlaus Jensen }
63588eea45cSKlaus Jensen
63688eea45cSKlaus Jensen status = nvme_map_dptr(n, &req->sg, mapped_len, &req->cmd);
63788eea45cSKlaus Jensen if (status) {
63888eea45cSKlaus Jensen goto err;
63988eea45cSKlaus Jensen }
64088eea45cSKlaus Jensen
64188eea45cSKlaus Jensen ctx->data.bounce = g_malloc(len);
64288eea45cSKlaus Jensen
64388eea45cSKlaus Jensen qemu_iovec_init(&ctx->data.iov, 1);
64488eea45cSKlaus Jensen qemu_iovec_add(&ctx->data.iov, ctx->data.bounce, len);
64588eea45cSKlaus Jensen
64688eea45cSKlaus Jensen if (req->cmd.opcode == NVME_CMD_READ) {
64788eea45cSKlaus Jensen block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
64888eea45cSKlaus Jensen BLOCK_ACCT_READ);
64988eea45cSKlaus Jensen
65088eea45cSKlaus Jensen req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
65188eea45cSKlaus Jensen nvme_dif_rw_mdata_in_cb, ctx);
65288eea45cSKlaus Jensen return NVME_NO_COMPLETE;
65388eea45cSKlaus Jensen }
65488eea45cSKlaus Jensen
65588eea45cSKlaus Jensen status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
65688eea45cSKlaus Jensen NVME_TX_DIRECTION_TO_DEVICE, req);
65788eea45cSKlaus Jensen if (status) {
65888eea45cSKlaus Jensen goto err;
65988eea45cSKlaus Jensen }
66088eea45cSKlaus Jensen
66188eea45cSKlaus Jensen ctx->mdata.bounce = g_malloc(mlen);
66288eea45cSKlaus Jensen
66388eea45cSKlaus Jensen qemu_iovec_init(&ctx->mdata.iov, 1);
66488eea45cSKlaus Jensen qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
66588eea45cSKlaus Jensen
666ac0b34c5SKlaus Jensen if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
66788eea45cSKlaus Jensen status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
66888eea45cSKlaus Jensen NVME_TX_DIRECTION_TO_DEVICE, req);
66988eea45cSKlaus Jensen if (status) {
67088eea45cSKlaus Jensen goto err;
67188eea45cSKlaus Jensen }
67288eea45cSKlaus Jensen }
67388eea45cSKlaus Jensen
6742a132309SKlaus Jensen status = nvme_check_prinfo(ns, prinfo, slba, reftag);
67588eea45cSKlaus Jensen if (status) {
67688eea45cSKlaus Jensen goto err;
67788eea45cSKlaus Jensen }
67888eea45cSKlaus Jensen
67988eea45cSKlaus Jensen if (pract) {
68088eea45cSKlaus Jensen /* splice generated protection information into the buffer */
68188eea45cSKlaus Jensen nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
68288eea45cSKlaus Jensen ctx->mdata.bounce, ctx->mdata.iov.size,
6830ca5c3ccSKlaus Jensen apptag, &reftag);
68488eea45cSKlaus Jensen } else {
68588eea45cSKlaus Jensen status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
6862a132309SKlaus Jensen ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
6870ca5c3ccSKlaus Jensen slba, apptag, appmask, &reftag);
68888eea45cSKlaus Jensen if (status) {
68988eea45cSKlaus Jensen goto err;
69088eea45cSKlaus Jensen }
69188eea45cSKlaus Jensen }
69288eea45cSKlaus Jensen
69388eea45cSKlaus Jensen block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
69488eea45cSKlaus Jensen BLOCK_ACCT_WRITE);
69588eea45cSKlaus Jensen
69688eea45cSKlaus Jensen req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
69788eea45cSKlaus Jensen nvme_dif_rw_mdata_out_cb, ctx);
69888eea45cSKlaus Jensen
69988eea45cSKlaus Jensen return NVME_NO_COMPLETE;
70088eea45cSKlaus Jensen
70188eea45cSKlaus Jensen err:
70288eea45cSKlaus Jensen qemu_iovec_destroy(&ctx->data.iov);
70388eea45cSKlaus Jensen g_free(ctx->data.bounce);
70488eea45cSKlaus Jensen
70588eea45cSKlaus Jensen qemu_iovec_destroy(&ctx->mdata.iov);
70688eea45cSKlaus Jensen g_free(ctx->mdata.bounce);
70788eea45cSKlaus Jensen
70888eea45cSKlaus Jensen g_free(ctx);
70988eea45cSKlaus Jensen
71088eea45cSKlaus Jensen return status;
71188eea45cSKlaus Jensen }
712