xref: /openbmc/qemu/hw/nvme/dif.c (revision 095859e5)
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 }
717