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