qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dif.c (21661B)


      1 /*
      2  * QEMU NVM Express End-to-End Data Protection support
      3  *
      4  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
      5  *
      6  * Authors:
      7  *   Klaus Jensen           <k.jensen@samsung.com>
      8  *   Gollu Appalanaidu      <anaidu.gollu@samsung.com>
      9  */
     10 
     11 #include "qemu/osdep.h"
     12 #include "qapi/error.h"
     13 #include "sysemu/block-backend.h"
     14 
     15 #include "nvme.h"
     16 #include "dif.h"
     17 #include "trace.h"
     18 
     19 uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
     20                            uint64_t reftag)
     21 {
     22     uint64_t mask = ns->pif ? 0xffffffffffff : 0xffffffff;
     23 
     24     if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
     25         (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & mask) != reftag) {
     26         return NVME_INVALID_PROT_INFO | NVME_DNR;
     27     }
     28 
     29     if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_3) &&
     30         (prinfo & NVME_PRINFO_PRCHK_REF)) {
     31         return NVME_INVALID_PROT_INFO;
     32     }
     33 
     34     return NVME_SUCCESS;
     35 }
     36 
     37 /* from Linux kernel (crypto/crct10dif_common.c) */
     38 static uint16_t crc16_t10dif(uint16_t crc, const unsigned char *buffer,
     39                              size_t len)
     40 {
     41     unsigned int i;
     42 
     43     for (i = 0; i < len; i++) {
     44         crc = (crc << 8) ^ crc16_t10dif_table[((crc >> 8) ^ buffer[i]) & 0xff];
     45     }
     46 
     47     return crc;
     48 }
     49 
     50 /* from Linux kernel (lib/crc64.c) */
     51 static uint64_t crc64_nvme(uint64_t crc, const unsigned char *buffer,
     52                            size_t len)
     53 {
     54     size_t i;
     55 
     56     for (i = 0; i < len; i++) {
     57         crc = (crc >> 8) ^ crc64_nvme_table[(crc & 0xff) ^ buffer[i]];
     58     }
     59 
     60     return crc ^ (uint64_t)~0;
     61 }
     62 
     63 static void nvme_dif_pract_generate_dif_crc16(NvmeNamespace *ns, uint8_t *buf,
     64                                               size_t len, uint8_t *mbuf,
     65                                               size_t mlen, uint16_t apptag,
     66                                               uint64_t *reftag)
     67 {
     68     uint8_t *end = buf + len;
     69     int16_t pil = 0;
     70 
     71     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
     72         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
     73     }
     74 
     75     trace_pci_nvme_dif_pract_generate_dif_crc16(len, ns->lbasz,
     76                                                 ns->lbasz + pil, apptag,
     77                                                 *reftag);
     78 
     79     for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
     80         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
     81         uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
     82 
     83         if (pil) {
     84             crc = crc16_t10dif(crc, mbuf, pil);
     85         }
     86 
     87         dif->g16.guard = cpu_to_be16(crc);
     88         dif->g16.apptag = cpu_to_be16(apptag);
     89         dif->g16.reftag = cpu_to_be32(*reftag);
     90 
     91         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
     92             (*reftag)++;
     93         }
     94     }
     95 }
     96 
     97 static void nvme_dif_pract_generate_dif_crc64(NvmeNamespace *ns, uint8_t *buf,
     98                                               size_t len, uint8_t *mbuf,
     99                                               size_t mlen, uint16_t apptag,
    100                                               uint64_t *reftag)
    101 {
    102     uint8_t *end = buf + len;
    103     int16_t pil = 0;
    104 
    105     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
    106         pil = ns->lbaf.ms - 16;
    107     }
    108 
    109     trace_pci_nvme_dif_pract_generate_dif_crc64(len, ns->lbasz,
    110                                                 ns->lbasz + pil, apptag,
    111                                                 *reftag);
    112 
    113     for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
    114         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
    115         uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
    116 
    117         if (pil) {
    118             crc = crc64_nvme(crc, mbuf, pil);
    119         }
    120 
    121         dif->g64.guard = cpu_to_be64(crc);
    122         dif->g64.apptag = cpu_to_be16(apptag);
    123 
    124         dif->g64.sr[0] = *reftag >> 40;
    125         dif->g64.sr[1] = *reftag >> 32;
    126         dif->g64.sr[2] = *reftag >> 24;
    127         dif->g64.sr[3] = *reftag >> 16;
    128         dif->g64.sr[4] = *reftag >> 8;
    129         dif->g64.sr[5] = *reftag;
    130 
    131         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
    132             (*reftag)++;
    133         }
    134     }
    135 }
    136 
    137 void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
    138                                  uint8_t *mbuf, size_t mlen, uint16_t apptag,
    139                                  uint64_t *reftag)
    140 {
    141     switch (ns->pif) {
    142     case NVME_PI_GUARD_16:
    143         return nvme_dif_pract_generate_dif_crc16(ns, buf, len, mbuf, mlen,
    144                                                  apptag, reftag);
    145     case NVME_PI_GUARD_64:
    146         return nvme_dif_pract_generate_dif_crc64(ns, buf, len, mbuf, mlen,
    147                                                  apptag, reftag);
    148     }
    149 
    150     abort();
    151 }
    152 
    153 static uint16_t nvme_dif_prchk_crc16(NvmeNamespace *ns, NvmeDifTuple *dif,
    154                                      uint8_t *buf, uint8_t *mbuf, size_t pil,
    155                                      uint8_t prinfo, uint16_t apptag,
    156                                      uint16_t appmask, uint64_t reftag)
    157 {
    158     switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
    159     case NVME_ID_NS_DPS_TYPE_3:
    160         if (be32_to_cpu(dif->g16.reftag) != 0xffffffff) {
    161             break;
    162         }
    163 
    164         /* fallthrough */
    165     case NVME_ID_NS_DPS_TYPE_1:
    166     case NVME_ID_NS_DPS_TYPE_2:
    167         if (be16_to_cpu(dif->g16.apptag) != 0xffff) {
    168             break;
    169         }
    170 
    171         trace_pci_nvme_dif_prchk_disabled_crc16(be16_to_cpu(dif->g16.apptag),
    172                                                 be32_to_cpu(dif->g16.reftag));
    173 
    174         return NVME_SUCCESS;
    175     }
    176 
    177     if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
    178         uint16_t crc = crc16_t10dif(0x0, buf, ns->lbasz);
    179 
    180         if (pil) {
    181             crc = crc16_t10dif(crc, mbuf, pil);
    182         }
    183 
    184         trace_pci_nvme_dif_prchk_guard_crc16(be16_to_cpu(dif->g16.guard), crc);
    185 
    186         if (be16_to_cpu(dif->g16.guard) != crc) {
    187             return NVME_E2E_GUARD_ERROR;
    188         }
    189     }
    190 
    191     if (prinfo & NVME_PRINFO_PRCHK_APP) {
    192         trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g16.apptag), apptag,
    193                                         appmask);
    194 
    195         if ((be16_to_cpu(dif->g16.apptag) & appmask) != (apptag & appmask)) {
    196             return NVME_E2E_APP_ERROR;
    197         }
    198     }
    199 
    200     if (prinfo & NVME_PRINFO_PRCHK_REF) {
    201         trace_pci_nvme_dif_prchk_reftag_crc16(be32_to_cpu(dif->g16.reftag),
    202                                               reftag);
    203 
    204         if (be32_to_cpu(dif->g16.reftag) != reftag) {
    205             return NVME_E2E_REF_ERROR;
    206         }
    207     }
    208 
    209     return NVME_SUCCESS;
    210 }
    211 
    212 static uint16_t nvme_dif_prchk_crc64(NvmeNamespace *ns, NvmeDifTuple *dif,
    213                                      uint8_t *buf, uint8_t *mbuf, size_t pil,
    214                                      uint8_t prinfo, uint16_t apptag,
    215                                      uint16_t appmask, uint64_t reftag)
    216 {
    217     uint64_t r = 0;
    218 
    219     r |= (uint64_t)dif->g64.sr[0] << 40;
    220     r |= (uint64_t)dif->g64.sr[1] << 32;
    221     r |= (uint64_t)dif->g64.sr[2] << 24;
    222     r |= (uint64_t)dif->g64.sr[3] << 16;
    223     r |= (uint64_t)dif->g64.sr[4] << 8;
    224     r |= (uint64_t)dif->g64.sr[5];
    225 
    226     switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
    227     case NVME_ID_NS_DPS_TYPE_3:
    228         if (r != 0xffffffffffff) {
    229             break;
    230         }
    231 
    232         /* fallthrough */
    233     case NVME_ID_NS_DPS_TYPE_1:
    234     case NVME_ID_NS_DPS_TYPE_2:
    235         if (be16_to_cpu(dif->g64.apptag) != 0xffff) {
    236             break;
    237         }
    238 
    239         trace_pci_nvme_dif_prchk_disabled_crc64(be16_to_cpu(dif->g16.apptag),
    240                                                 r);
    241 
    242         return NVME_SUCCESS;
    243     }
    244 
    245     if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
    246         uint64_t crc = crc64_nvme(~0ULL, buf, ns->lbasz);
    247 
    248         if (pil) {
    249             crc = crc64_nvme(crc, mbuf, pil);
    250         }
    251 
    252         trace_pci_nvme_dif_prchk_guard_crc64(be64_to_cpu(dif->g64.guard), crc);
    253 
    254         if (be64_to_cpu(dif->g64.guard) != crc) {
    255             return NVME_E2E_GUARD_ERROR;
    256         }
    257     }
    258 
    259     if (prinfo & NVME_PRINFO_PRCHK_APP) {
    260         trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->g64.apptag), apptag,
    261                                         appmask);
    262 
    263         if ((be16_to_cpu(dif->g64.apptag) & appmask) != (apptag & appmask)) {
    264             return NVME_E2E_APP_ERROR;
    265         }
    266     }
    267 
    268     if (prinfo & NVME_PRINFO_PRCHK_REF) {
    269         trace_pci_nvme_dif_prchk_reftag_crc64(r, reftag);
    270 
    271         if (r != reftag) {
    272             return NVME_E2E_REF_ERROR;
    273         }
    274     }
    275 
    276     return NVME_SUCCESS;
    277 }
    278 
    279 static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
    280                                uint8_t *buf, uint8_t *mbuf, size_t pil,
    281                                uint8_t prinfo, uint16_t apptag,
    282                                uint16_t appmask, uint64_t reftag)
    283 {
    284     switch (ns->pif) {
    285     case NVME_PI_GUARD_16:
    286         return nvme_dif_prchk_crc16(ns, dif, buf, mbuf, pil, prinfo, apptag,
    287                                     appmask, reftag);
    288     case NVME_PI_GUARD_64:
    289         return nvme_dif_prchk_crc64(ns, dif, buf, mbuf, pil, prinfo, apptag,
    290                                     appmask, reftag);
    291     }
    292 
    293     abort();
    294 }
    295 
    296 uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
    297                         uint8_t *mbuf, size_t mlen, uint8_t prinfo,
    298                         uint64_t slba, uint16_t apptag,
    299                         uint16_t appmask, uint64_t *reftag)
    300 {
    301     uint8_t *bufp, *end = buf + len;
    302     int16_t pil = 0;
    303     uint16_t status;
    304 
    305     status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
    306     if (status) {
    307         return status;
    308     }
    309 
    310     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
    311         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
    312     }
    313 
    314     trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
    315 
    316     for (bufp = buf; bufp < end; bufp += ns->lbasz, mbuf += ns->lbaf.ms) {
    317         NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
    318         status = nvme_dif_prchk(ns, dif, bufp, mbuf, pil, prinfo, apptag,
    319                                 appmask, *reftag);
    320         if (status) {
    321             /*
    322              * The first block of a 'raw' image is always allocated, so we
    323              * cannot reliably know if the block is all zeroes or not. For
    324              * CRC16 this works fine because the T10 CRC16 is 0x0 for all
    325              * zeroes, but the Rocksoft CRC64 is not. Thus, if a guard error is
    326              * detected for the first block, check if it is zeroed and manually
    327              * set the protection information to all ones to disable protection
    328              * information checking.
    329              */
    330             if (status == NVME_E2E_GUARD_ERROR && slba == 0x0 && bufp == buf) {
    331                 g_autofree uint8_t *zeroes = g_malloc0(ns->lbasz);
    332 
    333                 if (memcmp(bufp, zeroes, ns->lbasz) == 0) {
    334                     memset(mbuf + pil, 0xff, nvme_pi_tuple_size(ns));
    335                 }
    336             } else {
    337                 return status;
    338             }
    339         }
    340 
    341         if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
    342             (*reftag)++;
    343         }
    344     }
    345 
    346     return NVME_SUCCESS;
    347 }
    348 
    349 uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
    350                                uint64_t slba)
    351 {
    352     BlockBackend *blk = ns->blkconf.blk;
    353     BlockDriverState *bs = blk_bs(blk);
    354 
    355     int64_t moffset = 0, offset = nvme_l2b(ns, slba);
    356     uint8_t *mbufp, *end;
    357     bool zeroed;
    358     int16_t pil = 0;
    359     int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
    360     int64_t pnum = 0;
    361 
    362     Error *err = NULL;
    363 
    364 
    365     if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
    366         pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
    367     }
    368 
    369     do {
    370         int ret;
    371 
    372         bytes -= pnum;
    373 
    374         ret = bdrv_block_status(bs, offset, bytes, &pnum, NULL, NULL);
    375         if (ret < 0) {
    376             error_setg_errno(&err, -ret, "unable to get block status");
    377             error_report_err(err);
    378 
    379             return NVME_INTERNAL_DEV_ERROR;
    380         }
    381 
    382         zeroed = !!(ret & BDRV_BLOCK_ZERO);
    383 
    384         trace_pci_nvme_block_status(offset, bytes, pnum, ret, zeroed);
    385 
    386         if (zeroed) {
    387             mbufp = mbuf + moffset;
    388             mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
    389             end = mbufp + mlen;
    390 
    391             for (; mbufp < end; mbufp += ns->lbaf.ms) {
    392                 memset(mbufp + pil, 0xff, nvme_pi_tuple_size(ns));
    393             }
    394         }
    395 
    396         moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
    397         offset += pnum;
    398     } while (pnum != bytes);
    399 
    400     return NVME_SUCCESS;
    401 }
    402 
    403 static void nvme_dif_rw_cb(void *opaque, int ret)
    404 {
    405     NvmeBounceContext *ctx = opaque;
    406     NvmeRequest *req = ctx->req;
    407     NvmeNamespace *ns = req->ns;
    408     BlockBackend *blk = ns->blkconf.blk;
    409 
    410     trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
    411 
    412     qemu_iovec_destroy(&ctx->data.iov);
    413     g_free(ctx->data.bounce);
    414 
    415     qemu_iovec_destroy(&ctx->mdata.iov);
    416     g_free(ctx->mdata.bounce);
    417 
    418     g_free(ctx);
    419 
    420     nvme_rw_complete_cb(req, ret);
    421 }
    422 
    423 static void nvme_dif_rw_check_cb(void *opaque, int ret)
    424 {
    425     NvmeBounceContext *ctx = opaque;
    426     NvmeRequest *req = ctx->req;
    427     NvmeNamespace *ns = req->ns;
    428     NvmeCtrl *n = nvme_ctrl(req);
    429     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    430     uint64_t slba = le64_to_cpu(rw->slba);
    431     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
    432     uint16_t apptag = le16_to_cpu(rw->apptag);
    433     uint16_t appmask = le16_to_cpu(rw->appmask);
    434     uint64_t reftag = le32_to_cpu(rw->reftag);
    435     uint64_t cdw3 = le32_to_cpu(rw->cdw3);
    436     uint16_t status;
    437 
    438     reftag |= cdw3 << 32;
    439 
    440     trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
    441                                    reftag);
    442 
    443     if (ret) {
    444         goto out;
    445     }
    446 
    447     status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce, ctx->mdata.iov.size,
    448                                    slba);
    449     if (status) {
    450         req->status = status;
    451         goto out;
    452     }
    453 
    454     status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
    455                             ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
    456                             slba, apptag, appmask, &reftag);
    457     if (status) {
    458         req->status = status;
    459         goto out;
    460     }
    461 
    462     status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
    463                               NVME_TX_DIRECTION_FROM_DEVICE, req);
    464     if (status) {
    465         req->status = status;
    466         goto out;
    467     }
    468 
    469     if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == nvme_pi_tuple_size(ns)) {
    470         goto out;
    471     }
    472 
    473     status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
    474                                NVME_TX_DIRECTION_FROM_DEVICE, req);
    475     if (status) {
    476         req->status = status;
    477     }
    478 
    479 out:
    480     nvme_dif_rw_cb(ctx, ret);
    481 }
    482 
    483 static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
    484 {
    485     NvmeBounceContext *ctx = opaque;
    486     NvmeRequest *req = ctx->req;
    487     NvmeNamespace *ns = req->ns;
    488     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    489     uint64_t slba = le64_to_cpu(rw->slba);
    490     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
    491     size_t mlen = nvme_m2b(ns, nlb);
    492     uint64_t offset = nvme_moff(ns, slba);
    493     BlockBackend *blk = ns->blkconf.blk;
    494 
    495     trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
    496 
    497     if (ret) {
    498         goto out;
    499     }
    500 
    501     ctx->mdata.bounce = g_malloc(mlen);
    502 
    503     qemu_iovec_reset(&ctx->mdata.iov);
    504     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    505 
    506     req->aiocb = blk_aio_preadv(blk, offset, &ctx->mdata.iov, 0,
    507                                 nvme_dif_rw_check_cb, ctx);
    508     return;
    509 
    510 out:
    511     nvme_dif_rw_cb(ctx, ret);
    512 }
    513 
    514 static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
    515 {
    516     NvmeBounceContext *ctx = opaque;
    517     NvmeRequest *req = ctx->req;
    518     NvmeNamespace *ns = req->ns;
    519     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    520     uint64_t slba = le64_to_cpu(rw->slba);
    521     uint64_t offset = nvme_moff(ns, slba);
    522     BlockBackend *blk = ns->blkconf.blk;
    523 
    524     trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
    525 
    526     if (ret) {
    527         goto out;
    528     }
    529 
    530     req->aiocb = blk_aio_pwritev(blk, offset, &ctx->mdata.iov, 0,
    531                                  nvme_dif_rw_cb, ctx);
    532     return;
    533 
    534 out:
    535     nvme_dif_rw_cb(ctx, ret);
    536 }
    537 
    538 uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
    539 {
    540     NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    541     NvmeNamespace *ns = req->ns;
    542     BlockBackend *blk = ns->blkconf.blk;
    543     bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
    544     uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
    545     uint64_t slba = le64_to_cpu(rw->slba);
    546     size_t len = nvme_l2b(ns, nlb);
    547     size_t mlen = nvme_m2b(ns, nlb);
    548     size_t mapped_len = len;
    549     int64_t offset = nvme_l2b(ns, slba);
    550     uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
    551     uint16_t apptag = le16_to_cpu(rw->apptag);
    552     uint16_t appmask = le16_to_cpu(rw->appmask);
    553     uint64_t reftag = le32_to_cpu(rw->reftag);
    554     uint64_t cdw3 = le32_to_cpu(rw->cdw3);
    555     bool pract = !!(prinfo & NVME_PRINFO_PRACT);
    556     NvmeBounceContext *ctx;
    557     uint16_t status;
    558 
    559     reftag |= cdw3 << 32;
    560 
    561     trace_pci_nvme_dif_rw(pract, prinfo);
    562 
    563     ctx = g_new0(NvmeBounceContext, 1);
    564     ctx->req = req;
    565 
    566     if (wrz) {
    567         BdrvRequestFlags flags = BDRV_REQ_MAY_UNMAP;
    568 
    569         if (prinfo & NVME_PRINFO_PRCHK_MASK) {
    570             status = NVME_INVALID_PROT_INFO | NVME_DNR;
    571             goto err;
    572         }
    573 
    574         if (pract) {
    575             uint8_t *mbuf, *end;
    576             int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns);
    577 
    578             status = nvme_check_prinfo(ns, prinfo, slba, reftag);
    579             if (status) {
    580                 goto err;
    581             }
    582 
    583             flags = 0;
    584 
    585             ctx->mdata.bounce = g_malloc0(mlen);
    586 
    587             qemu_iovec_init(&ctx->mdata.iov, 1);
    588             qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    589 
    590             mbuf = ctx->mdata.bounce;
    591             end = mbuf + mlen;
    592 
    593             if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
    594                 pil = 0;
    595             }
    596 
    597             for (; mbuf < end; mbuf += ns->lbaf.ms) {
    598                 NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
    599 
    600                 switch (ns->pif) {
    601                 case NVME_PI_GUARD_16:
    602                     dif->g16.apptag = cpu_to_be16(apptag);
    603                     dif->g16.reftag = cpu_to_be32(reftag);
    604 
    605                     break;
    606 
    607                 case NVME_PI_GUARD_64:
    608                     dif->g64.guard = cpu_to_be64(0x6482d367eb22b64e);
    609                     dif->g64.apptag = cpu_to_be16(apptag);
    610 
    611                     dif->g64.sr[0] = reftag >> 40;
    612                     dif->g64.sr[1] = reftag >> 32;
    613                     dif->g64.sr[2] = reftag >> 24;
    614                     dif->g64.sr[3] = reftag >> 16;
    615                     dif->g64.sr[4] = reftag >> 8;
    616                     dif->g64.sr[5] = reftag;
    617 
    618                     break;
    619 
    620                 default:
    621                     abort();
    622                 }
    623 
    624                 switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
    625                 case NVME_ID_NS_DPS_TYPE_1:
    626                 case NVME_ID_NS_DPS_TYPE_2:
    627                     reftag++;
    628                 }
    629             }
    630         }
    631 
    632         req->aiocb = blk_aio_pwrite_zeroes(blk, offset, len, flags,
    633                                            nvme_dif_rw_mdata_out_cb, ctx);
    634         return NVME_NO_COMPLETE;
    635     }
    636 
    637     if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
    638         mapped_len += mlen;
    639     }
    640 
    641     status = nvme_map_dptr(n, &req->sg, mapped_len, &req->cmd);
    642     if (status) {
    643         goto err;
    644     }
    645 
    646     ctx->data.bounce = g_malloc(len);
    647 
    648     qemu_iovec_init(&ctx->data.iov, 1);
    649     qemu_iovec_add(&ctx->data.iov, ctx->data.bounce, len);
    650 
    651     if (req->cmd.opcode == NVME_CMD_READ) {
    652         block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
    653                          BLOCK_ACCT_READ);
    654 
    655         req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
    656                                     nvme_dif_rw_mdata_in_cb, ctx);
    657         return NVME_NO_COMPLETE;
    658     }
    659 
    660     status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
    661                               NVME_TX_DIRECTION_TO_DEVICE, req);
    662     if (status) {
    663         goto err;
    664     }
    665 
    666     ctx->mdata.bounce = g_malloc(mlen);
    667 
    668     qemu_iovec_init(&ctx->mdata.iov, 1);
    669     qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    670 
    671     if (!(pract && ns->lbaf.ms == nvme_pi_tuple_size(ns))) {
    672         status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
    673                                    NVME_TX_DIRECTION_TO_DEVICE, req);
    674         if (status) {
    675             goto err;
    676         }
    677     }
    678 
    679     status = nvme_check_prinfo(ns, prinfo, slba, reftag);
    680     if (status) {
    681         goto err;
    682     }
    683 
    684     if (pract) {
    685         /* splice generated protection information into the buffer */
    686         nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
    687                                     ctx->mdata.bounce, ctx->mdata.iov.size,
    688                                     apptag, &reftag);
    689     } else {
    690         status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
    691                                 ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
    692                                 slba, apptag, appmask, &reftag);
    693         if (status) {
    694             goto err;
    695         }
    696     }
    697 
    698     block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
    699                      BLOCK_ACCT_WRITE);
    700 
    701     req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
    702                                  nvme_dif_rw_mdata_out_cb, ctx);
    703 
    704     return NVME_NO_COMPLETE;
    705 
    706 err:
    707     qemu_iovec_destroy(&ctx->data.iov);
    708     g_free(ctx->data.bounce);
    709 
    710     qemu_iovec_destroy(&ctx->mdata.iov);
    711     g_free(ctx->mdata.bounce);
    712 
    713     g_free(ctx);
    714 
    715     return status;
    716 }