qemu/hw/nvme/dif.c
<<
>>
Prefs
   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
  19uint16_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) */
  38static 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) */
  51static 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
  63static 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
  97static 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
 137void 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
 153static 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
 212static 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
 279static 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
 296uint16_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
 349uint16_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
 403static 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
 423static 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
 479out:
 480    nvme_dif_rw_cb(ctx, ret);
 481}
 482
 483static 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
 510out:
 511    nvme_dif_rw_cb(ctx, ret);
 512}
 513
 514static 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
 534out:
 535    nvme_dif_rw_cb(ctx, ret);
 536}
 537
 538uint16_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
 706err:
 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}
 717