LCOV - code coverage report
Current view: top level - lib/util - dif.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 1198 1313 91.2 %
Date: 2024-11-20 06:20:29 Functions: 105 107 98.1 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2022 Intel Corporation.
       3             :  *   All rights reserved.
       4             :  */
       5             : 
       6             : #include "spdk/dif.h"
       7             : #include "spdk/crc16.h"
       8             : #include "spdk/crc32.h"
       9             : #include "spdk/crc64.h"
      10             : #include "spdk/endian.h"
      11             : #include "spdk/log.h"
      12             : #include "spdk/util.h"
      13             : 
      14             : #define REFTAG_MASK_16 0x00000000FFFFFFFF
      15             : #define REFTAG_MASK_32 0xFFFFFFFFFFFFFFFF
      16             : #define REFTAG_MASK_64 0x0000FFFFFFFFFFFF
      17             : 
      18             : /* The variable size Storage Tag and Reference Tag is not supported yet,
      19             :  * so the maximum size of the Reference Tag is assumed.
      20             :  */
      21             : struct spdk_dif {
      22             :         union {
      23             :                 struct {
      24             :                         uint16_t guard;
      25             :                         uint16_t app_tag;
      26             :                         uint32_t stor_ref_space;
      27             :                 } g16;
      28             :                 struct {
      29             :                         uint32_t guard;
      30             :                         uint16_t app_tag;
      31             :                         uint16_t stor_ref_space_p1;
      32             :                         uint64_t stor_ref_space_p2;
      33             :                 } g32;
      34             :                 struct {
      35             :                         uint64_t guard;
      36             :                         uint16_t app_tag;
      37             :                         uint16_t stor_ref_space_p1;
      38             :                         uint32_t stor_ref_space_p2;
      39             :                 } g64;
      40             :         };
      41             : };
      42             : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g16) == 8, "Incorrect size");
      43             : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g32) == 16, "Incorrect size");
      44             : SPDK_STATIC_ASSERT(SPDK_SIZEOF_MEMBER(struct spdk_dif, g64) == 16, "Incorrect size");
      45             : 
      46             : /* Context to iterate or create a iovec array.
      47             :  * Each sgl is either iterated or created at a time.
      48             :  */
      49             : struct _dif_sgl {
      50             :         /* Current iovec in the iteration or creation */
      51             :         struct iovec *iov;
      52             : 
      53             :         /* Remaining count of iovecs in the iteration or creation. */
      54             :         int iovcnt;
      55             : 
      56             :         /* Current offset in the iovec */
      57             :         uint32_t iov_offset;
      58             : 
      59             :         /* Size of the created iovec array in bytes */
      60             :         uint32_t total_size;
      61             : };
      62             : 
      63             : static inline void
      64        2681 : _dif_sgl_init(struct _dif_sgl *s, struct iovec *iovs, int iovcnt)
      65             : {
      66        2681 :         s->iov = iovs;
      67        2681 :         s->iovcnt = iovcnt;
      68        2681 :         s->iov_offset = 0;
      69        2681 :         s->total_size = 0;
      70        2681 : }
      71             : 
      72             : static void
      73       17065 : _dif_sgl_advance(struct _dif_sgl *s, uint32_t step)
      74             : {
      75       17065 :         s->iov_offset += step;
      76       22943 :         while (s->iovcnt != 0) {
      77       21089 :                 if (s->iov_offset < s->iov->iov_len) {
      78       15211 :                         break;
      79             :                 }
      80             : 
      81        5878 :                 s->iov_offset -= s->iov->iov_len;
      82        5878 :                 s->iov++;
      83        5878 :                 s->iovcnt--;
      84             :         }
      85       17065 : }
      86             : 
      87             : static inline void
      88       16885 : _dif_sgl_get_buf(struct _dif_sgl *s, uint8_t **_buf, uint32_t *_buf_len)
      89             : {
      90       16885 :         if (_buf != NULL) {
      91       16885 :                 *_buf = (uint8_t *)s->iov->iov_base + s->iov_offset;
      92       16885 :         }
      93       16885 :         if (_buf_len != NULL) {
      94        9904 :                 *_buf_len = s->iov->iov_len - s->iov_offset;
      95        9904 :         }
      96       16885 : }
      97             : 
      98             : static inline bool
      99         120 : _dif_sgl_append(struct _dif_sgl *s, uint8_t *data, uint32_t data_len)
     100             : {
     101         120 :         assert(s->iovcnt > 0);
     102         120 :         s->iov->iov_base = data;
     103         120 :         s->iov->iov_len = data_len;
     104         120 :         s->total_size += data_len;
     105         120 :         s->iov++;
     106         120 :         s->iovcnt--;
     107             : 
     108         120 :         if (s->iovcnt > 0) {
     109         100 :                 return true;
     110             :         } else {
     111          20 :                 return false;
     112             :         }
     113         120 : }
     114             : 
     115             : static inline bool
     116         104 : _dif_sgl_append_split(struct _dif_sgl *dst, struct _dif_sgl *src, uint32_t data_len)
     117             : {
     118             :         uint8_t *buf;
     119             :         uint32_t buf_len;
     120             : 
     121         204 :         while (data_len != 0) {
     122         120 :                 _dif_sgl_get_buf(src, &buf, &buf_len);
     123         120 :                 buf_len = spdk_min(buf_len, data_len);
     124             : 
     125         120 :                 if (!_dif_sgl_append(dst, buf, buf_len)) {
     126          20 :                         return false;
     127             :                 }
     128             : 
     129         100 :                 _dif_sgl_advance(src, buf_len);
     130         100 :                 data_len -= buf_len;
     131             :         }
     132             : 
     133          84 :         return true;
     134         104 : }
     135             : 
     136             : /* This function must be used before starting iteration. */
     137             : static bool
     138        1053 : _dif_sgl_is_bytes_multiple(struct _dif_sgl *s, uint32_t bytes)
     139             : {
     140             :         int i;
     141             : 
     142        2713 :         for (i = 0; i < s->iovcnt; i++) {
     143        2100 :                 if (s->iov[i].iov_len % bytes) {
     144         440 :                         return false;
     145             :                 }
     146        1660 :         }
     147             : 
     148         613 :         return true;
     149        1053 : }
     150             : 
     151             : /* This function must be used before starting iteration. */
     152             : static bool
     153        2616 : _dif_sgl_is_valid(struct _dif_sgl *s, uint32_t bytes)
     154             : {
     155        2616 :         uint64_t total = 0;
     156             :         int i;
     157             : 
     158        9498 :         for (i = 0; i < s->iovcnt; i++) {
     159        6882 :                 total += s->iov[i].iov_len;
     160        6882 :         }
     161             : 
     162        2616 :         return total >= bytes;
     163             : }
     164             : 
     165             : static void
     166          72 : _dif_sgl_copy(struct _dif_sgl *to, struct _dif_sgl *from)
     167             : {
     168          72 :         memcpy(to, from, sizeof(struct _dif_sgl));
     169          72 : }
     170             : 
     171             : static bool
     172         926 : _dif_is_disabled(enum spdk_dif_type dif_type)
     173             : {
     174         926 :         if (dif_type == SPDK_DIF_DISABLE) {
     175           8 :                 return true;
     176             :         } else {
     177         918 :                 return false;
     178             :         }
     179         926 : }
     180             : 
     181             : static inline size_t
     182        3990 : _dif_size(enum spdk_dif_pi_format dif_pi_format)
     183             : {
     184             :         uint8_t size;
     185             : 
     186        3990 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     187        1431 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16);
     188        3990 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     189        1302 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32);
     190        1302 :         } else {
     191        1257 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64);
     192             :         }
     193             : 
     194        3990 :         return size;
     195             : }
     196             : 
     197             : static uint32_t
     198         540 : _get_guard_interval(uint32_t block_size, uint32_t md_size, bool dif_loc, bool md_interleave,
     199             :                     size_t dif_size)
     200             : {
     201         540 :         if (!dif_loc) {
     202             :                 /* For metadata formats with more than 8/16 bytes (depending on
     203             :                  * the PI format), if the DIF is contained in the last 8/16 bytes
     204             :                  * of metadata, then the CRC covers all metadata up to but excluding
     205             :                  * these last 8/16 bytes.
     206             :                  */
     207         325 :                 if (md_interleave) {
     208         247 :                         return block_size - dif_size;
     209             :                 } else {
     210          78 :                         return md_size - dif_size;
     211             :                 }
     212             :         } else {
     213             :                 /* For metadata formats with more than 8/16 bytes (depending on
     214             :                  * the PI format), if the DIF is contained in the first 8/16 bytes
     215             :                  * of metadata, then the CRC does not cover any metadata.
     216             :                  */
     217         215 :                 if (md_interleave) {
     218         177 :                         return block_size - md_size;
     219             :                 } else {
     220          38 :                         return 0;
     221             :                 }
     222             :         }
     223         540 : }
     224             : 
     225             : static inline uint8_t
     226         180 : _dif_guard_size(enum spdk_dif_pi_format dif_pi_format)
     227             : {
     228             :         uint8_t size;
     229             : 
     230         180 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     231          60 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.guard);
     232         180 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     233          60 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.guard);
     234          60 :         } else {
     235          60 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.guard);
     236             :         }
     237             : 
     238         180 :         return size;
     239             : }
     240             : 
     241             : static inline void
     242        1873 : _dif_set_guard(struct spdk_dif *dif, uint64_t guard, enum spdk_dif_pi_format dif_pi_format)
     243             : {
     244        1873 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     245         785 :                 to_be16(&(dif->g16.guard), (uint16_t)guard);
     246        1873 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     247         553 :                 to_be32(&(dif->g32.guard), (uint32_t)guard);
     248         553 :         } else {
     249         535 :                 to_be64(&(dif->g64.guard), guard);
     250             :         }
     251        1873 : }
     252             : 
     253             : static inline uint64_t
     254        1806 : _dif_get_guard(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
     255             : {
     256             :         uint64_t guard;
     257             : 
     258        1806 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     259         909 :                 guard = (uint64_t)from_be16(&(dif->g16.guard));
     260        1806 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     261         448 :                 guard = (uint64_t)from_be32(&(dif->g32.guard));
     262         448 :         } else {
     263         449 :                 guard = from_be64(&(dif->g64.guard));
     264             :         }
     265             : 
     266        1806 :         return guard;
     267             : }
     268             : 
     269             : static inline uint64_t
     270        5594 : _dif_generate_guard(uint64_t guard_seed, void *buf, size_t buf_len,
     271             :                     enum spdk_dif_pi_format dif_pi_format)
     272             : {
     273             :         uint64_t guard;
     274             : 
     275        5594 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     276        2837 :                 guard = (uint64_t)spdk_crc16_t10dif((uint16_t)guard_seed, buf, buf_len);
     277        5594 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     278        1393 :                 guard = (uint64_t)spdk_crc32c_nvme(buf, buf_len, guard_seed);
     279        1393 :         } else {
     280        1364 :                 guard = spdk_crc64_nvme(buf, buf_len, guard_seed);
     281             :         }
     282             : 
     283        5594 :         return guard;
     284             : }
     285             : 
     286             : static uint64_t
     287         568 : dif_generate_guard_split(uint64_t guard_seed, struct _dif_sgl *sgl, uint32_t start,
     288             :                          uint32_t len, const struct spdk_dif_ctx *ctx)
     289             : {
     290         568 :         uint64_t guard = guard_seed;
     291             :         uint32_t offset, end, buf_len;
     292             :         uint8_t *buf;
     293             : 
     294         568 :         offset = start;
     295         568 :         end = start + spdk_min(len, ctx->guard_interval - start);
     296             : 
     297        1411 :         while (offset < end) {
     298         843 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
     299         843 :                 buf_len = spdk_min(buf_len, end - offset);
     300             : 
     301         843 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     302         843 :                         guard = _dif_generate_guard(guard, buf, buf_len, ctx->dif_pi_format);
     303         843 :                 }
     304             : 
     305         843 :                 _dif_sgl_advance(sgl, buf_len);
     306         843 :                 offset += buf_len;
     307             :         }
     308             : 
     309         568 :         return guard;
     310             : }
     311             : 
     312             : static inline uint64_t
     313         914 : _dif_generate_guard_copy(uint64_t guard_seed, void *dst, void *src, size_t buf_len,
     314             :                          enum spdk_dif_pi_format dif_pi_format)
     315             : {
     316             :         uint64_t guard;
     317             : 
     318         914 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     319         290 :                 guard = (uint64_t)spdk_crc16_t10dif_copy((uint16_t)guard_seed, dst, src, buf_len);
     320         914 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     321         312 :                 memcpy(dst, src, buf_len);
     322         312 :                 guard = (uint64_t)spdk_crc32c_nvme(src, buf_len, guard_seed);
     323         312 :         } else {
     324         312 :                 memcpy(dst, src, buf_len);
     325         312 :                 guard = spdk_crc64_nvme(src, buf_len, guard_seed);
     326             :         }
     327             : 
     328         914 :         return guard;
     329             : }
     330             : 
     331             : static uint64_t
     332         126 : _dif_generate_guard_copy_split(uint64_t guard, struct _dif_sgl *dst_sgl,
     333             :                                struct _dif_sgl *src_sgl, uint32_t data_len,
     334             :                                enum spdk_dif_pi_format dif_pi_format)
     335             : {
     336         126 :         uint32_t offset = 0, src_len, dst_len, buf_len;
     337             :         uint8_t *src, *dst;
     338             : 
     339         380 :         while (offset < data_len) {
     340         254 :                 _dif_sgl_get_buf(src_sgl, &src, &src_len);
     341         254 :                 _dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
     342         254 :                 buf_len = spdk_min(src_len, dst_len);
     343         254 :                 buf_len = spdk_min(buf_len, data_len - offset);
     344             : 
     345         254 :                 guard = _dif_generate_guard_copy(guard, dst, src, buf_len, dif_pi_format);
     346             : 
     347         254 :                 _dif_sgl_advance(src_sgl, buf_len);
     348         254 :                 _dif_sgl_advance(dst_sgl, buf_len);
     349         254 :                 offset += buf_len;
     350             :         }
     351             : 
     352         126 :         return guard;
     353             : }
     354             : 
     355             : static void
     356           0 : _data_copy_split(struct _dif_sgl *dst_sgl, struct _dif_sgl *src_sgl, uint32_t data_len)
     357             : {
     358           0 :         uint32_t offset = 0, src_len, dst_len, buf_len;
     359             :         uint8_t *src, *dst;
     360             : 
     361           0 :         while (offset < data_len) {
     362           0 :                 _dif_sgl_get_buf(src_sgl, &src, &src_len);
     363           0 :                 _dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
     364           0 :                 buf_len = spdk_min(src_len, dst_len);
     365           0 :                 buf_len = spdk_min(buf_len, data_len - offset);
     366             : 
     367           0 :                 memcpy(dst, src, buf_len);
     368             : 
     369           0 :                 _dif_sgl_advance(src_sgl, buf_len);
     370           0 :                 _dif_sgl_advance(dst_sgl, buf_len);
     371           0 :                 offset += buf_len;
     372             :         }
     373           0 : }
     374             : 
     375             : static inline uint8_t
     376         120 : _dif_apptag_offset(enum spdk_dif_pi_format dif_pi_format)
     377             : {
     378         120 :         return _dif_guard_size(dif_pi_format);
     379             : }
     380             : 
     381             : static inline uint8_t
     382         120 : _dif_apptag_size(void)
     383             : {
     384         120 :         return SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.app_tag);
     385             : }
     386             : 
     387             : static inline void
     388        1865 : _dif_set_apptag(struct spdk_dif *dif, uint16_t app_tag, enum spdk_dif_pi_format dif_pi_format)
     389             : {
     390        1865 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     391         784 :                 to_be16(&(dif->g16.app_tag), app_tag);
     392        1865 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     393         550 :                 to_be16(&(dif->g32.app_tag), app_tag);
     394         550 :         } else {
     395         531 :                 to_be16(&(dif->g64.app_tag), app_tag);
     396             :         }
     397        1865 : }
     398             : 
     399             : static inline uint16_t
     400        4192 : _dif_get_apptag(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
     401             : {
     402             :         uint16_t app_tag;
     403             : 
     404        4192 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     405        2067 :                 app_tag = from_be16(&(dif->g16.app_tag));
     406        4192 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     407        1063 :                 app_tag = from_be16(&(dif->g32.app_tag));
     408        1063 :         } else {
     409        1062 :                 app_tag = from_be16(&(dif->g64.app_tag));
     410             :         }
     411             : 
     412        4192 :         return app_tag;
     413             : }
     414             : 
     415             : static inline bool
     416        2518 : _dif_apptag_ignore(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
     417             : {
     418        2518 :         return _dif_get_apptag(dif, dif_pi_format) == SPDK_DIF_APPTAG_IGNORE;
     419             : }
     420             : 
     421             : static inline uint8_t
     422          60 : _dif_reftag_offset(enum spdk_dif_pi_format dif_pi_format)
     423             : {
     424             :         uint8_t offset;
     425             : 
     426          60 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     427          20 :                 offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size();
     428          60 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     429          40 :                 offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size()
     430          20 :                          + SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.stor_ref_space_p1);
     431          20 :         } else {
     432          20 :                 offset = _dif_apptag_offset(dif_pi_format) + _dif_apptag_size();
     433             :         }
     434             : 
     435          60 :         return offset;
     436             : }
     437             : 
     438             : static inline uint8_t
     439          60 : _dif_reftag_size(enum spdk_dif_pi_format dif_pi_format)
     440             : {
     441             :         uint8_t size;
     442             : 
     443          60 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     444          20 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g16.stor_ref_space);
     445          60 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     446          20 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g32.stor_ref_space_p2);
     447          20 :         } else {
     448          20 :                 size = SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.stor_ref_space_p1) +
     449             :                        SPDK_SIZEOF_MEMBER(struct spdk_dif, g64.stor_ref_space_p2);
     450             :         }
     451             : 
     452          60 :         return size;
     453             : }
     454             : 
     455             : static inline void
     456        2134 : _dif_set_reftag(struct spdk_dif *dif, uint64_t ref_tag, enum spdk_dif_pi_format dif_pi_format)
     457             : {
     458        2134 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     459         959 :                 to_be32(&(dif->g16.stor_ref_space), (uint32_t)ref_tag);
     460        2134 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     461         596 :                 to_be64(&(dif->g32.stor_ref_space_p2), ref_tag);
     462         596 :         } else {
     463         579 :                 to_be16(&(dif->g64.stor_ref_space_p1), (uint16_t)(ref_tag >> 32));
     464         579 :                 to_be32(&(dif->g64.stor_ref_space_p2), (uint32_t)ref_tag);
     465             :         }
     466        2134 : }
     467             : 
     468             : static inline uint64_t
     469        1820 : _dif_get_reftag(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
     470             : {
     471             :         uint64_t ref_tag;
     472             : 
     473        1820 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     474         918 :                 ref_tag = (uint64_t)from_be32(&(dif->g16.stor_ref_space));
     475        1820 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     476         451 :                 ref_tag = from_be64(&(dif->g32.stor_ref_space_p2));
     477         451 :         } else {
     478         451 :                 ref_tag = (uint64_t)from_be16(&(dif->g64.stor_ref_space_p1));
     479         451 :                 ref_tag <<= 32;
     480         451 :                 ref_tag |= (uint64_t)from_be32(&(dif->g64.stor_ref_space_p2));
     481             :         }
     482             : 
     483        1820 :         return ref_tag;
     484             : }
     485             : 
     486             : static inline bool
     487        1752 : _dif_reftag_match(struct spdk_dif *dif, uint64_t ref_tag,
     488             :                   enum spdk_dif_pi_format dif_pi_format)
     489             : {
     490             :         uint64_t _ref_tag;
     491             :         bool match;
     492             : 
     493        1752 :         _ref_tag = _dif_get_reftag(dif, dif_pi_format);
     494             : 
     495        1752 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     496         894 :                 match = (_ref_tag == (ref_tag & REFTAG_MASK_16));
     497        1752 :         } else if (dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     498         429 :                 match = (_ref_tag == ref_tag);
     499         429 :         } else {
     500         429 :                 match = (_ref_tag == (ref_tag & REFTAG_MASK_64));
     501             :         }
     502             : 
     503        1752 :         return match;
     504             : }
     505             : 
     506             : static inline bool
     507           7 : _dif_reftag_ignore(struct spdk_dif *dif, enum spdk_dif_pi_format dif_pi_format)
     508             : {
     509           7 :         return _dif_reftag_match(dif, REFTAG_MASK_32, dif_pi_format);
     510             : }
     511             : 
     512             : static bool
     513        2518 : _dif_ignore(struct spdk_dif *dif, const struct spdk_dif_ctx *ctx)
     514             : {
     515        2518 :         switch (ctx->dif_type) {
     516             :         case SPDK_DIF_TYPE1:
     517             :         case SPDK_DIF_TYPE2:
     518             :                 /* If Type 1 or 2 is used, then all DIF checks are disabled when
     519             :                  * the Application Tag is 0xFFFF.
     520             :                  */
     521        2511 :                 if (_dif_apptag_ignore(dif, ctx->dif_pi_format)) {
     522           6 :                         return true;
     523             :                 }
     524        2505 :                 break;
     525             :         case SPDK_DIF_TYPE3:
     526             :                 /* If Type 3 is used, then all DIF checks are disabled when the
     527             :                  * Application Tag is 0xFFFF and the Reference Tag is 0xFFFFFFFF
     528             :                  * or 0xFFFFFFFFFFFFFFFF depending on the PI format.
     529             :                  */
     530             : 
     531           7 :                 if (_dif_apptag_ignore(dif, ctx->dif_pi_format) &&
     532           7 :                     _dif_reftag_ignore(dif, ctx->dif_pi_format)) {
     533           4 :                         return true;
     534             :                 }
     535           3 :                 break;
     536             :         default:
     537           0 :                 break;
     538             :         }
     539             : 
     540        2508 :         return false;
     541        2518 : }
     542             : 
     543             : static bool
     544         542 : _dif_pi_format_is_valid(enum spdk_dif_pi_format dif_pi_format)
     545             : {
     546         542 :         switch (dif_pi_format) {
     547             :         case SPDK_DIF_PI_FORMAT_16:
     548             :         case SPDK_DIF_PI_FORMAT_32:
     549             :         case SPDK_DIF_PI_FORMAT_64:
     550         541 :                 return true;
     551             :         default:
     552           1 :                 return false;
     553             :         }
     554         542 : }
     555             : 
     556             : static bool
     557         543 : _dif_type_is_valid(enum spdk_dif_type dif_type)
     558             : {
     559         543 :         switch (dif_type) {
     560             :         case SPDK_DIF_DISABLE:
     561             :         case SPDK_DIF_TYPE1:
     562             :         case SPDK_DIF_TYPE2:
     563             :         case SPDK_DIF_TYPE3:
     564         542 :                 return true;
     565             :         default:
     566           1 :                 return false;
     567             :         }
     568         543 : }
     569             : 
     570             : int
     571         538 : spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_size,
     572             :                   bool md_interleave, bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags,
     573             :                   uint32_t init_ref_tag, uint16_t apptag_mask, uint16_t app_tag,
     574             :                   uint32_t data_offset, uint64_t guard_seed, struct spdk_dif_ctx_init_ext_opts *opts)
     575             : {
     576             :         uint32_t data_block_size;
     577         538 :         enum spdk_dif_pi_format dif_pi_format = SPDK_DIF_PI_FORMAT_16;
     578             : 
     579         538 :         if (opts != NULL) {
     580         538 :                 if (!_dif_pi_format_is_valid(opts->dif_pi_format)) {
     581           0 :                         SPDK_ERRLOG("No valid DIF PI format provided.\n");
     582           0 :                         return -EINVAL;
     583             :                 }
     584             : 
     585         538 :                 dif_pi_format = opts->dif_pi_format;
     586         538 :         }
     587             : 
     588         538 :         if (!_dif_type_is_valid(dif_type)) {
     589           0 :                 SPDK_ERRLOG("No valid DIF type was provided.\n");
     590           0 :                 return -EINVAL;
     591             :         }
     592             : 
     593         538 :         if (md_size < _dif_size(dif_pi_format)) {
     594          11 :                 SPDK_ERRLOG("Metadata size is smaller than DIF size.\n");
     595          11 :                 return -EINVAL;
     596             :         }
     597             : 
     598         527 :         if (md_interleave) {
     599         406 :                 if (block_size < md_size) {
     600           0 :                         SPDK_ERRLOG("Block size is smaller than DIF size.\n");
     601           0 :                         return -EINVAL;
     602             :                 }
     603         406 :                 data_block_size = block_size - md_size;
     604         406 :         } else {
     605         121 :                 data_block_size = block_size;
     606             :         }
     607             : 
     608         527 :         if (data_block_size == 0) {
     609           2 :                 SPDK_ERRLOG("Zero data block size is not allowed\n");
     610           2 :                 return -EINVAL;
     611             :         }
     612             : 
     613         525 :         if (dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     614         194 :                 if ((data_block_size % 512) != 0) {
     615           0 :                         SPDK_ERRLOG("Data block size should be a multiple of 512B\n");
     616           0 :                         return -EINVAL;
     617             :                 }
     618         194 :         } else {
     619         331 :                 if ((data_block_size % 4096) != 0) {
     620           6 :                         SPDK_ERRLOG("Data block size should be a multiple of 4kB\n");
     621           6 :                         return -EINVAL;
     622             :                 }
     623             :         }
     624             : 
     625         519 :         ctx->block_size = block_size;
     626         519 :         ctx->md_size = md_size;
     627         519 :         ctx->md_interleave = md_interleave;
     628         519 :         ctx->dif_pi_format = dif_pi_format;
     629        1038 :         ctx->guard_interval = _get_guard_interval(block_size, md_size, dif_loc, md_interleave,
     630         519 :                               _dif_size(ctx->dif_pi_format));
     631         519 :         ctx->dif_type = dif_type;
     632         519 :         ctx->dif_flags = dif_flags;
     633         519 :         ctx->init_ref_tag = init_ref_tag;
     634         519 :         ctx->apptag_mask = apptag_mask;
     635         519 :         ctx->app_tag = app_tag;
     636         519 :         ctx->data_offset = data_offset;
     637         519 :         ctx->ref_tag_offset = data_offset / data_block_size;
     638         519 :         ctx->last_guard = guard_seed;
     639         519 :         ctx->guard_seed = guard_seed;
     640         519 :         ctx->remapped_init_ref_tag = 0;
     641             : 
     642         519 :         return 0;
     643         538 : }
     644             : 
     645             : void
     646          42 : spdk_dif_ctx_set_data_offset(struct spdk_dif_ctx *ctx, uint32_t data_offset)
     647             : {
     648             :         uint32_t data_block_size;
     649             : 
     650          42 :         if (ctx->md_interleave) {
     651          42 :                 data_block_size = ctx->block_size - ctx->md_size;
     652          42 :         } else {
     653           0 :                 data_block_size = ctx->block_size;
     654             :         }
     655             : 
     656          42 :         ctx->data_offset = data_offset;
     657          42 :         ctx->ref_tag_offset = data_offset / data_block_size;
     658          42 : }
     659             : 
     660             : void
     661          24 : spdk_dif_ctx_set_remapped_init_ref_tag(struct spdk_dif_ctx *ctx,
     662             :                                        uint32_t remapped_init_ref_tag)
     663             : {
     664          24 :         ctx->remapped_init_ref_tag = remapped_init_ref_tag;
     665          24 : }
     666             : 
     667             : static void
     668        2311 : _dif_generate(void *_dif, uint64_t guard, uint32_t offset_blocks,
     669             :               const struct spdk_dif_ctx *ctx)
     670             : {
     671        2311 :         struct spdk_dif *dif = _dif;
     672             :         uint64_t ref_tag;
     673             : 
     674        2311 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     675        1873 :                 _dif_set_guard(dif, guard, ctx->dif_pi_format);
     676        1873 :         }
     677             : 
     678        2311 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
     679        1865 :                 _dif_set_apptag(dif, ctx->app_tag, ctx->dif_pi_format);
     680        1865 :         }
     681             : 
     682        2311 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
     683             :                 /* For type 1 and 2, the reference tag is incremented for each
     684             :                  * subsequent logical block. For type 3, the reference tag
     685             :                  * remains the same as the initial reference tag.
     686             :                  */
     687        1862 :                 if (ctx->dif_type != SPDK_DIF_TYPE3) {
     688        1855 :                         ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
     689        1855 :                 } else {
     690           7 :                         ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset;
     691             :                 }
     692             : 
     693             :                 /* Overwrite reference tag if initialization reference tag is SPDK_DIF_REFTAG_IGNORE */
     694        1862 :                 if (ctx->init_ref_tag == SPDK_DIF_REFTAG_IGNORE) {
     695           2 :                         if (ctx->dif_pi_format == SPDK_DIF_PI_FORMAT_16) {
     696           2 :                                 ref_tag = REFTAG_MASK_16;
     697           2 :                         } else if (ctx->dif_pi_format == SPDK_DIF_PI_FORMAT_32) {
     698           0 :                                 ref_tag = REFTAG_MASK_32;
     699           0 :                         } else {
     700           0 :                                 ref_tag = REFTAG_MASK_64;
     701             :                         }
     702           2 :                 }
     703             : 
     704        1862 :                 _dif_set_reftag(dif, ref_tag, ctx->dif_pi_format);
     705        1862 :         }
     706        2311 : }
     707             : 
     708             : static void
     709         100 : dif_generate(struct _dif_sgl *sgl, uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
     710             : {
     711             :         uint32_t offset_blocks;
     712             :         uint8_t *buf;
     713         100 :         uint64_t guard = 0;
     714             : 
     715         677 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
     716         577 :                 _dif_sgl_get_buf(sgl, &buf, NULL);
     717             : 
     718         577 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     719         475 :                         guard = _dif_generate_guard(ctx->guard_seed, buf, ctx->guard_interval, ctx->dif_pi_format);
     720         475 :                 }
     721             : 
     722         577 :                 _dif_generate(buf + ctx->guard_interval, guard, offset_blocks, ctx);
     723             : 
     724         577 :                 _dif_sgl_advance(sgl, ctx->block_size);
     725         577 :         }
     726         100 : }
     727             : 
     728             : static void
     729         298 : dif_store_split(struct _dif_sgl *sgl, struct spdk_dif *dif,
     730             :                 const struct spdk_dif_ctx *ctx)
     731             : {
     732         298 :         uint32_t offset = 0, rest_md_len, buf_len;
     733             :         uint8_t *buf;
     734             : 
     735         298 :         rest_md_len = ctx->block_size - ctx->guard_interval;
     736             : 
     737         800 :         while (offset < rest_md_len) {
     738         502 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
     739             : 
     740         502 :                 if (offset < _dif_size(ctx->dif_pi_format)) {
     741         386 :                         buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
     742         386 :                         memcpy(buf, (uint8_t *)dif + offset, buf_len);
     743         386 :                 } else {
     744         116 :                         buf_len = spdk_min(buf_len, rest_md_len - offset);
     745             :                 }
     746             : 
     747         502 :                 _dif_sgl_advance(sgl, buf_len);
     748         502 :                 offset += buf_len;
     749             :         }
     750         298 : }
     751             : 
     752             : static uint64_t
     753         262 : _dif_generate_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
     754             :                     uint64_t guard, uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
     755             : {
     756         262 :         struct spdk_dif dif = {};
     757             : 
     758         262 :         assert(offset_in_block < ctx->guard_interval);
     759         262 :         assert(offset_in_block + data_len < ctx->guard_interval ||
     760             :                offset_in_block + data_len == ctx->block_size);
     761             : 
     762             :         /* Compute CRC over split logical block data. */
     763         262 :         guard = dif_generate_guard_split(guard, sgl, offset_in_block, data_len, ctx);
     764             : 
     765         262 :         if (offset_in_block + data_len < ctx->guard_interval) {
     766          39 :                 return guard;
     767             :         }
     768             : 
     769             :         /* If a whole logical block data is parsed, generate DIF
     770             :          * and save it to the temporary DIF area.
     771             :          */
     772         223 :         _dif_generate(&dif, guard, offset_blocks, ctx);
     773             : 
     774             :         /* Copy generated DIF field to the split DIF field, and then
     775             :          * skip metadata field after DIF field (if any).
     776             :          */
     777         223 :         dif_store_split(sgl, &dif, ctx);
     778             : 
     779         223 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     780         223 :                 guard = ctx->guard_seed;
     781         223 :         }
     782             : 
     783         223 :         return guard;
     784         262 : }
     785             : 
     786             : static void
     787         152 : dif_generate_split(struct _dif_sgl *sgl, uint32_t num_blocks,
     788             :                    const struct spdk_dif_ctx *ctx)
     789             : {
     790             :         uint32_t offset_blocks;
     791         152 :         uint64_t guard = 0;
     792             : 
     793         152 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     794         152 :                 guard = ctx->guard_seed;
     795         152 :         }
     796             : 
     797         329 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
     798         177 :                 _dif_generate_split(sgl, 0, ctx->block_size, guard, offset_blocks, ctx);
     799         177 :         }
     800         152 : }
     801             : 
     802             : int
     803         244 : spdk_dif_generate(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
     804             :                   const struct spdk_dif_ctx *ctx)
     805             : {
     806             :         struct _dif_sgl sgl;
     807             : 
     808         244 :         _dif_sgl_init(&sgl, iovs, iovcnt);
     809             : 
     810         244 :         if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
     811           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
     812           0 :                 return -EINVAL;
     813             :         }
     814             : 
     815         244 :         if (_dif_is_disabled(ctx->dif_type)) {
     816           1 :                 return 0;
     817             :         }
     818             : 
     819         243 :         if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
     820          91 :                 dif_generate(&sgl, num_blocks, ctx);
     821          91 :         } else {
     822         152 :                 dif_generate_split(&sgl, num_blocks, ctx);
     823             :         }
     824             : 
     825         243 :         return 0;
     826         244 : }
     827             : 
     828             : static void
     829         262 : _dif_error_set(struct spdk_dif_error *err_blk, uint8_t err_type,
     830             :                uint64_t expected, uint64_t actual, uint32_t err_offset)
     831             : {
     832         262 :         if (err_blk) {
     833         253 :                 err_blk->err_type = err_type;
     834         253 :                 err_blk->expected = expected;
     835         253 :                 err_blk->actual = actual;
     836         253 :                 err_blk->err_offset = err_offset;
     837         253 :         }
     838         262 : }
     839             : 
     840             : static bool
     841        2184 : _dif_reftag_check(struct spdk_dif *dif, const struct spdk_dif_ctx *ctx,
     842             :                   uint64_t expected_reftag, uint32_t offset_blocks, struct spdk_dif_error *err_blk)
     843             : {
     844             :         uint64_t reftag;
     845             : 
     846        2184 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK) {
     847        1745 :                 switch (ctx->dif_type) {
     848             :                 case SPDK_DIF_TYPE1:
     849             :                 case SPDK_DIF_TYPE2:
     850             :                         /* Compare the DIF Reference Tag field to the passed Reference Tag.
     851             :                          * The passed Reference Tag will be the least significant 4 bytes
     852             :                          * or 8 bytes (depending on the PI format)
     853             :                          * of the LBA when Type 1 is used, and application specific value
     854             :                          * if Type 2 is used.
     855             :                          */
     856        1745 :                         if (!_dif_reftag_match(dif, expected_reftag, ctx->dif_pi_format)) {
     857          66 :                                 reftag = _dif_get_reftag(dif, ctx->dif_pi_format);
     858         132 :                                 _dif_error_set(err_blk, SPDK_DIF_REFTAG_ERROR, expected_reftag,
     859          66 :                                                reftag, offset_blocks);
     860          66 :                                 SPDK_ERRLOG("Failed to compare Ref Tag: LBA=%" PRIu64 "," \
     861             :                                             " Expected=%lx, Actual=%lx\n",
     862             :                                             expected_reftag, expected_reftag, reftag);
     863          66 :                                 return false;
     864             :                         }
     865        1679 :                         break;
     866             :                 case SPDK_DIF_TYPE3:
     867             :                         /* For Type 3, computed Reference Tag remains unchanged.
     868             :                          * Hence ignore the Reference Tag field.
     869             :                          */
     870           0 :                         break;
     871             :                 default:
     872           0 :                         break;
     873             :                 }
     874        1679 :         }
     875             : 
     876        2118 :         return true;
     877        2184 : }
     878             : 
     879             : static int
     880        2246 : _dif_verify(void *_dif, uint64_t guard, uint32_t offset_blocks,
     881             :             const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
     882             : {
     883        2246 :         struct spdk_dif *dif = _dif;
     884             :         uint64_t _guard;
     885             :         uint16_t _app_tag;
     886             :         uint64_t ref_tag;
     887             : 
     888        2246 :         if (_dif_ignore(dif, ctx)) {
     889          10 :                 return 0;
     890             :         }
     891             : 
     892             :         /* For type 1 and 2, the reference tag is incremented for each
     893             :          * subsequent logical block. For type 3, the reference tag
     894             :          * remains the same as the initial reference tag.
     895             :          */
     896        2236 :         if (ctx->dif_type != SPDK_DIF_TYPE3) {
     897        2233 :                 ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
     898        2233 :         } else {
     899           3 :                 ref_tag = ctx->init_ref_tag + ctx->ref_tag_offset;
     900             :         }
     901             : 
     902        2236 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     903             :                 /* Compare the DIF Guard field to the CRC computed over the logical
     904             :                  * block data.
     905             :                  */
     906        1796 :                 _guard = _dif_get_guard(dif, ctx->dif_pi_format);
     907        1796 :                 if (_guard != guard) {
     908         248 :                         _dif_error_set(err_blk, SPDK_DIF_GUARD_ERROR, _guard, guard,
     909         124 :                                        offset_blocks);
     910         124 :                         SPDK_ERRLOG("Failed to compare Guard: LBA=%" PRIu64 "," \
     911             :                                     "  Expected=%lx, Actual=%lx\n",
     912             :                                     ref_tag, _guard, guard);
     913         124 :                         return -1;
     914             :                 }
     915        1672 :         }
     916             : 
     917        2112 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_APPTAG_CHECK) {
     918             :                 /* Compare unmasked bits in the DIF Application Tag field to the
     919             :                  * passed Application Tag.
     920             :                  */
     921        1672 :                 _app_tag = _dif_get_apptag(dif, ctx->dif_pi_format);
     922        1672 :                 if ((_app_tag & ctx->apptag_mask) != (ctx->app_tag & ctx->apptag_mask)) {
     923         144 :                         _dif_error_set(err_blk, SPDK_DIF_APPTAG_ERROR, ctx->app_tag,
     924          72 :                                        (_app_tag & ctx->apptag_mask), offset_blocks);
     925          72 :                         SPDK_ERRLOG("Failed to compare App Tag: LBA=%" PRIu64 "," \
     926             :                                     "  Expected=%x, Actual=%x\n",
     927             :                                     ref_tag, ctx->app_tag, (_app_tag & ctx->apptag_mask));
     928          72 :                         return -1;
     929             :                 }
     930        1600 :         }
     931             : 
     932        2040 :         if (!_dif_reftag_check(dif, ctx, ref_tag, offset_blocks, err_blk)) {
     933          66 :                 return -1;
     934             :         }
     935             : 
     936        1974 :         return 0;
     937        2246 : }
     938             : 
     939             : static int
     940         105 : dif_verify(struct _dif_sgl *sgl, uint32_t num_blocks,
     941             :            const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
     942             : {
     943             :         uint32_t offset_blocks;
     944             :         int rc;
     945             :         uint8_t *buf;
     946         105 :         uint64_t guard = 0;
     947             : 
     948         590 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
     949         522 :                 _dif_sgl_get_buf(sgl, &buf, NULL);
     950             : 
     951         522 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
     952         418 :                         guard = _dif_generate_guard(ctx->guard_seed, buf, ctx->guard_interval, ctx->dif_pi_format);
     953         418 :                 }
     954             : 
     955         522 :                 rc = _dif_verify(buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
     956         522 :                 if (rc != 0) {
     957          37 :                         return rc;
     958             :                 }
     959             : 
     960         485 :                 _dif_sgl_advance(sgl, ctx->block_size);
     961         485 :         }
     962             : 
     963          68 :         return 0;
     964         105 : }
     965             : 
     966             : static void
     967         232 : dif_load_split(struct _dif_sgl *sgl, struct spdk_dif *dif,
     968             :                const struct spdk_dif_ctx *ctx)
     969             : {
     970         232 :         uint32_t offset = 0, rest_md_len, buf_len;
     971             :         uint8_t *buf;
     972             : 
     973         232 :         rest_md_len = ctx->block_size - ctx->guard_interval;
     974             : 
     975         653 :         while (offset < rest_md_len) {
     976         421 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
     977             : 
     978         421 :                 if (offset < _dif_size(ctx->dif_pi_format)) {
     979         317 :                         buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
     980         317 :                         memcpy((uint8_t *)dif + offset, buf, buf_len);
     981         317 :                 } else {
     982         104 :                         buf_len = spdk_min(buf_len, rest_md_len - offset);
     983             :                 }
     984             : 
     985         421 :                 _dif_sgl_advance(sgl, buf_len);
     986         421 :                 offset += buf_len;
     987             :         }
     988         232 : }
     989             : 
     990             : static int
     991         196 : _dif_verify_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
     992             :                   uint64_t *_guard, uint32_t offset_blocks,
     993             :                   const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
     994             : {
     995         196 :         uint64_t guard = *_guard;
     996         196 :         struct spdk_dif dif = {};
     997             :         int rc;
     998             : 
     999         196 :         assert(_guard != NULL);
    1000         196 :         assert(offset_in_block < ctx->guard_interval);
    1001         196 :         assert(offset_in_block + data_len < ctx->guard_interval ||
    1002             :                offset_in_block + data_len == ctx->block_size);
    1003             : 
    1004         196 :         guard = dif_generate_guard_split(guard, sgl, offset_in_block, data_len, ctx);
    1005             : 
    1006         196 :         if (offset_in_block + data_len < ctx->guard_interval) {
    1007          15 :                 *_guard = guard;
    1008          15 :                 return 0;
    1009             :         }
    1010             : 
    1011         181 :         dif_load_split(sgl, &dif, ctx);
    1012             : 
    1013         181 :         rc = _dif_verify(&dif, guard, offset_blocks, ctx, err_blk);
    1014         181 :         if (rc != 0) {
    1015         120 :                 return rc;
    1016             :         }
    1017             : 
    1018          61 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1019          61 :                 guard = ctx->guard_seed;
    1020          61 :         }
    1021             : 
    1022          61 :         *_guard = guard;
    1023          61 :         return 0;
    1024         196 : }
    1025             : 
    1026             : static int
    1027         150 : dif_verify_split(struct _dif_sgl *sgl, uint32_t num_blocks,
    1028             :                  const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
    1029             : {
    1030             :         uint32_t offset_blocks;
    1031         150 :         uint64_t guard = 0;
    1032             :         int rc;
    1033             : 
    1034         150 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1035         150 :                 guard = ctx->guard_seed;
    1036         150 :         }
    1037             : 
    1038         199 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1039         338 :                 rc = _dif_verify_split(sgl, 0, ctx->block_size, &guard, offset_blocks,
    1040         169 :                                        ctx, err_blk);
    1041         169 :                 if (rc != 0) {
    1042         120 :                         return rc;
    1043             :                 }
    1044          49 :         }
    1045             : 
    1046          30 :         return 0;
    1047         150 : }
    1048             : 
    1049             : int
    1050         247 : spdk_dif_verify(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
    1051             :                 const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk)
    1052             : {
    1053             :         struct _dif_sgl sgl;
    1054             : 
    1055         247 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    1056             : 
    1057         247 :         if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
    1058           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    1059           0 :                 return -EINVAL;
    1060             :         }
    1061             : 
    1062         247 :         if (_dif_is_disabled(ctx->dif_type)) {
    1063           1 :                 return 0;
    1064             :         }
    1065             : 
    1066         246 :         if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
    1067          96 :                 return dif_verify(&sgl, num_blocks, ctx, err_blk);
    1068             :         } else {
    1069         150 :                 return dif_verify_split(&sgl, num_blocks, ctx, err_blk);
    1070             :         }
    1071         247 : }
    1072             : 
    1073             : static uint32_t
    1074           9 : dif_update_crc32c(struct _dif_sgl *sgl, uint32_t num_blocks,
    1075             :                   uint32_t crc32c,  const struct spdk_dif_ctx *ctx)
    1076             : {
    1077             :         uint32_t offset_blocks;
    1078             :         uint8_t *buf;
    1079             : 
    1080          45 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1081          36 :                 _dif_sgl_get_buf(sgl, &buf, NULL);
    1082             : 
    1083          36 :                 crc32c = spdk_crc32c_update(buf, ctx->block_size - ctx->md_size, crc32c);
    1084             : 
    1085          36 :                 _dif_sgl_advance(sgl, ctx->block_size);
    1086          36 :         }
    1087             : 
    1088           9 :         return crc32c;
    1089             : }
    1090             : 
    1091             : static uint32_t
    1092          51 : _dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t offset_in_block, uint32_t data_len,
    1093             :                          uint32_t crc32c, const struct spdk_dif_ctx *ctx)
    1094             : {
    1095             :         uint32_t data_block_size, buf_len;
    1096             :         uint8_t *buf;
    1097             : 
    1098          51 :         data_block_size = ctx->block_size - ctx->md_size;
    1099             : 
    1100          51 :         assert(offset_in_block + data_len <= ctx->block_size);
    1101             : 
    1102         174 :         while (data_len != 0) {
    1103         123 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
    1104         123 :                 buf_len = spdk_min(buf_len, data_len);
    1105             : 
    1106         123 :                 if (offset_in_block < data_block_size) {
    1107          69 :                         buf_len = spdk_min(buf_len, data_block_size - offset_in_block);
    1108          69 :                         crc32c = spdk_crc32c_update(buf, buf_len, crc32c);
    1109          69 :                 }
    1110             : 
    1111         123 :                 _dif_sgl_advance(sgl, buf_len);
    1112         123 :                 offset_in_block += buf_len;
    1113         123 :                 data_len -= buf_len;
    1114             :         }
    1115             : 
    1116          51 :         return crc32c;
    1117             : }
    1118             : 
    1119             : static uint32_t
    1120           6 : dif_update_crc32c_split(struct _dif_sgl *sgl, uint32_t num_blocks,
    1121             :                         uint32_t crc32c, const struct spdk_dif_ctx *ctx)
    1122             : {
    1123             :         uint32_t offset_blocks;
    1124             : 
    1125          30 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1126          24 :                 crc32c = _dif_update_crc32c_split(sgl, 0, ctx->block_size, crc32c, ctx);
    1127          24 :         }
    1128             : 
    1129           6 :         return crc32c;
    1130             : }
    1131             : 
    1132             : int
    1133          15 : spdk_dif_update_crc32c(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
    1134             :                        uint32_t *_crc32c, const struct spdk_dif_ctx *ctx)
    1135             : {
    1136             :         struct _dif_sgl sgl;
    1137             : 
    1138          15 :         if (_crc32c == NULL) {
    1139           0 :                 return -EINVAL;
    1140             :         }
    1141             : 
    1142          15 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    1143             : 
    1144          15 :         if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
    1145           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    1146           0 :                 return -EINVAL;
    1147             :         }
    1148             : 
    1149          15 :         if (_dif_sgl_is_bytes_multiple(&sgl, ctx->block_size)) {
    1150           9 :                 *_crc32c = dif_update_crc32c(&sgl, num_blocks, *_crc32c, ctx);
    1151           9 :         } else {
    1152           6 :                 *_crc32c = dif_update_crc32c_split(&sgl, num_blocks, *_crc32c, ctx);
    1153             :         }
    1154             : 
    1155          15 :         return 0;
    1156          15 : }
    1157             : 
    1158             : static void
    1159         492 : _dif_insert_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1160             :                  uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
    1161             : {
    1162             :         uint32_t data_block_size;
    1163             :         uint8_t *src, *dst;
    1164         492 :         uint64_t guard = 0;
    1165             : 
    1166         492 :         data_block_size = ctx->block_size - ctx->md_size;
    1167             : 
    1168         492 :         _dif_sgl_get_buf(src_sgl, &src, NULL);
    1169         492 :         _dif_sgl_get_buf(dst_sgl, &dst, NULL);
    1170             : 
    1171         492 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1172         684 :                 guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, data_block_size,
    1173         342 :                                                  ctx->dif_pi_format);
    1174         684 :                 guard = _dif_generate_guard(guard, dst + data_block_size,
    1175         342 :                                             ctx->guard_interval - data_block_size, ctx->dif_pi_format);
    1176         342 :         } else {
    1177         150 :                 memcpy(dst, src, data_block_size);
    1178             :         }
    1179             : 
    1180         492 :         _dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
    1181             : 
    1182         492 :         _dif_sgl_advance(src_sgl, data_block_size);
    1183         492 :         _dif_sgl_advance(dst_sgl, ctx->block_size);
    1184         492 : }
    1185             : 
    1186             : static void
    1187          56 : dif_insert_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1188             :                 uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1189             : {
    1190             :         uint32_t offset_blocks;
    1191             : 
    1192         548 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1193         492 :                 _dif_insert_copy(src_sgl, dst_sgl, offset_blocks, ctx);
    1194         492 :         }
    1195          56 : }
    1196             : 
    1197             : static void
    1198          67 : _dif_insert_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1199             :                        uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
    1200             : {
    1201             :         uint32_t data_block_size;
    1202          67 :         uint64_t guard = 0;
    1203          67 :         struct spdk_dif dif = {};
    1204             : 
    1205          67 :         data_block_size = ctx->block_size - ctx->md_size;
    1206             : 
    1207          67 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1208         134 :                 guard = _dif_generate_guard_copy_split(ctx->guard_seed, dst_sgl, src_sgl,
    1209          67 :                                                        data_block_size, ctx->dif_pi_format);
    1210         134 :                 guard = dif_generate_guard_split(guard, dst_sgl, data_block_size,
    1211          67 :                                                  ctx->guard_interval - data_block_size, ctx);
    1212          67 :         } else {
    1213           0 :                 _data_copy_split(dst_sgl, src_sgl, data_block_size);
    1214           0 :                 _dif_sgl_advance(dst_sgl, ctx->guard_interval - data_block_size);
    1215             :         }
    1216             : 
    1217          67 :         _dif_generate(&dif, guard, offset_blocks, ctx);
    1218             : 
    1219          67 :         dif_store_split(dst_sgl, &dif, ctx);
    1220          67 : }
    1221             : 
    1222             : static void
    1223          31 : dif_insert_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1224             :                       uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1225             : {
    1226             :         uint32_t offset_blocks;
    1227             : 
    1228          98 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1229          67 :                 _dif_insert_copy_split(src_sgl, dst_sgl, offset_blocks, ctx);
    1230          67 :         }
    1231          31 : }
    1232             : 
    1233             : static void
    1234          12 : _dif_disable_insert_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1235             :                          const struct spdk_dif_ctx *ctx)
    1236             : {
    1237          12 :         uint32_t offset = 0, src_len, dst_len, buf_len, data_block_size;
    1238             :         uint8_t *src, *dst;
    1239             : 
    1240          12 :         data_block_size = ctx->block_size - ctx->md_size;
    1241             : 
    1242          32 :         while (offset < data_block_size) {
    1243          20 :                 _dif_sgl_get_buf(src_sgl, &src, &src_len);
    1244          20 :                 _dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
    1245          20 :                 buf_len = spdk_min(src_len, dst_len);
    1246          20 :                 buf_len = spdk_min(buf_len, data_block_size - offset);
    1247             : 
    1248          20 :                 memcpy(dst, src, buf_len);
    1249             : 
    1250          20 :                 _dif_sgl_advance(src_sgl, buf_len);
    1251          20 :                 _dif_sgl_advance(dst_sgl, buf_len);
    1252          20 :                 offset += buf_len;
    1253             :         }
    1254             : 
    1255          12 :         _dif_sgl_advance(dst_sgl, ctx->md_size);
    1256          12 : }
    1257             : 
    1258             : static void
    1259           3 : dif_disable_insert_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1260             :                         uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1261             : {
    1262             :         uint32_t offset_blocks;
    1263             : 
    1264          15 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1265          12 :                 _dif_disable_insert_copy(src_sgl, dst_sgl, ctx);
    1266          12 :         }
    1267           3 : }
    1268             : 
    1269             : static int
    1270          90 : _spdk_dif_insert_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1271             :                       uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1272             : {
    1273             :         uint32_t data_block_size;
    1274             : 
    1275          90 :         data_block_size = ctx->block_size - ctx->md_size;
    1276             : 
    1277          90 :         if (!_dif_sgl_is_valid(src_sgl, data_block_size * num_blocks) ||
    1278          90 :             !_dif_sgl_is_valid(dst_sgl, ctx->block_size * num_blocks)) {
    1279           0 :                 SPDK_ERRLOG("Size of iovec arrays are not valid.\n");
    1280           0 :                 return -EINVAL;
    1281             :         }
    1282             : 
    1283          90 :         if (_dif_is_disabled(ctx->dif_type)) {
    1284           3 :                 dif_disable_insert_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1285           3 :                 return 0;
    1286             :         }
    1287             : 
    1288          87 :         if (_dif_sgl_is_bytes_multiple(src_sgl, data_block_size) &&
    1289          56 :             _dif_sgl_is_bytes_multiple(dst_sgl, ctx->block_size)) {
    1290          56 :                 dif_insert_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1291          56 :         } else {
    1292          31 :                 dif_insert_copy_split(src_sgl, dst_sgl, num_blocks, ctx);
    1293             :         }
    1294             : 
    1295          87 :         return 0;
    1296          90 : }
    1297             : 
    1298             : static void
    1299          96 : _dif_overwrite_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1300             :                     uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
    1301             : {
    1302             :         uint8_t *src, *dst;
    1303          96 :         uint64_t guard = 0;
    1304             : 
    1305          96 :         _dif_sgl_get_buf(src_sgl, &src, NULL);
    1306          96 :         _dif_sgl_get_buf(dst_sgl, &dst, NULL);
    1307             : 
    1308          96 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1309          48 :                 guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, ctx->guard_interval,
    1310          24 :                                                  ctx->dif_pi_format);
    1311          24 :         } else {
    1312          72 :                 memcpy(dst, src, ctx->guard_interval);
    1313             :         }
    1314             : 
    1315          96 :         _dif_generate(dst + ctx->guard_interval, guard, offset_blocks, ctx);
    1316             : 
    1317          96 :         _dif_sgl_advance(src_sgl, ctx->block_size);
    1318          96 :         _dif_sgl_advance(dst_sgl, ctx->block_size);
    1319          96 : }
    1320             : 
    1321             : static void
    1322          16 : dif_overwrite_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1323             :                    uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1324             : {
    1325             :         uint32_t offset_blocks;
    1326             : 
    1327         112 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1328          96 :                 _dif_overwrite_copy(src_sgl, dst_sgl, offset_blocks, ctx);
    1329          96 :         }
    1330          16 : }
    1331             : 
    1332             : static void
    1333           8 : _dif_overwrite_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1334             :                           uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
    1335             : {
    1336           8 :         uint64_t guard = 0;
    1337           8 :         struct spdk_dif dif = {};
    1338             : 
    1339           8 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1340          16 :                 guard = _dif_generate_guard_copy_split(ctx->guard_seed, dst_sgl, src_sgl,
    1341           8 :                                                        ctx->guard_interval, ctx->dif_pi_format);
    1342           8 :         } else {
    1343           0 :                 _data_copy_split(dst_sgl, src_sgl, ctx->guard_interval);
    1344             :         }
    1345             : 
    1346           8 :         _dif_sgl_advance(src_sgl, ctx->block_size - ctx->guard_interval);
    1347             : 
    1348           8 :         _dif_generate(&dif, guard, offset_blocks, ctx);
    1349           8 :         dif_store_split(dst_sgl, &dif, ctx);
    1350           8 : }
    1351             : 
    1352             : static void
    1353           2 : dif_overwrite_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1354             :                          uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1355             : {
    1356             :         uint32_t offset_blocks;
    1357             : 
    1358          10 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1359           8 :                 _dif_overwrite_copy_split(src_sgl, dst_sgl, offset_blocks, ctx);
    1360           8 :         }
    1361           2 : }
    1362             : 
    1363             : static void
    1364           0 : dif_disable_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1365             :                  uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1366             : {
    1367           0 :         _data_copy_split(dst_sgl, src_sgl, ctx->block_size * num_blocks);
    1368           0 : }
    1369             : 
    1370             : static int
    1371          18 : _spdk_dif_overwrite_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1372             :                          uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1373             : {
    1374          18 :         if (!_dif_sgl_is_valid(src_sgl, ctx->block_size * num_blocks) ||
    1375          18 :             !_dif_sgl_is_valid(dst_sgl, ctx->block_size * num_blocks)) {
    1376           0 :                 SPDK_ERRLOG("Size of iovec arrays are not valid.\n");
    1377           0 :                 return -EINVAL;
    1378             :         }
    1379             : 
    1380          18 :         if (_dif_is_disabled(ctx->dif_type)) {
    1381           0 :                 dif_disable_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1382           0 :                 return 0;
    1383             :         }
    1384             : 
    1385          18 :         if (_dif_sgl_is_bytes_multiple(src_sgl, ctx->block_size) &&
    1386          16 :             _dif_sgl_is_bytes_multiple(dst_sgl, ctx->block_size)) {
    1387          16 :                 dif_overwrite_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1388          16 :         } else {
    1389           2 :                 dif_overwrite_copy_split(src_sgl, dst_sgl, num_blocks, ctx);
    1390             :         }
    1391             : 
    1392          18 :         return 0;
    1393          18 : }
    1394             : 
    1395             : int
    1396         108 : spdk_dif_generate_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iovs,
    1397             :                        int bounce_iovcnt, uint32_t num_blocks,
    1398             :                        const struct spdk_dif_ctx *ctx)
    1399             : {
    1400             :         struct _dif_sgl src_sgl, dst_sgl;
    1401             : 
    1402         108 :         _dif_sgl_init(&src_sgl, iovs, iovcnt);
    1403         108 :         _dif_sgl_init(&dst_sgl, bounce_iovs, bounce_iovcnt);
    1404             : 
    1405         108 :         if (!(ctx->dif_flags & SPDK_DIF_FLAGS_NVME_PRACT) ||
    1406          18 :             ctx->md_size == _dif_size(ctx->dif_pi_format)) {
    1407          90 :                 return _spdk_dif_insert_copy(&src_sgl, &dst_sgl, num_blocks, ctx);
    1408             :         } else {
    1409          18 :                 return _spdk_dif_overwrite_copy(&src_sgl, &dst_sgl, num_blocks, ctx);
    1410             :         }
    1411         108 : }
    1412             : 
    1413             : static int
    1414         420 : _dif_strip_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1415             :                 uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
    1416             :                 struct spdk_dif_error *err_blk)
    1417             : {
    1418             :         uint32_t data_block_size;
    1419             :         uint8_t *src, *dst;
    1420             :         int rc;
    1421         420 :         uint64_t guard = 0;
    1422             : 
    1423         420 :         data_block_size = ctx->block_size - ctx->md_size;
    1424             : 
    1425         420 :         _dif_sgl_get_buf(src_sgl, &src, NULL);
    1426         420 :         _dif_sgl_get_buf(dst_sgl, &dst, NULL);
    1427             : 
    1428         420 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1429         540 :                 guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, data_block_size,
    1430         270 :                                                  ctx->dif_pi_format);
    1431         540 :                 guard = _dif_generate_guard(guard, src + data_block_size,
    1432         270 :                                             ctx->guard_interval - data_block_size, ctx->dif_pi_format);
    1433         270 :         } else {
    1434         150 :                 memcpy(dst, src, data_block_size);
    1435             :         }
    1436             : 
    1437         420 :         rc = _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
    1438         420 :         if (rc != 0) {
    1439          24 :                 return rc;
    1440             :         }
    1441             : 
    1442         396 :         _dif_sgl_advance(src_sgl, ctx->block_size);
    1443         396 :         _dif_sgl_advance(dst_sgl, data_block_size);
    1444             : 
    1445         396 :         return 0;
    1446         420 : }
    1447             : 
    1448             : static int
    1449          56 : dif_strip_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1450             :                uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1451             :                struct spdk_dif_error *err_blk)
    1452             : {
    1453             :         uint32_t offset_blocks;
    1454             :         int rc;
    1455             : 
    1456         452 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1457         420 :                 rc = _dif_strip_copy(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
    1458         420 :                 if (rc != 0) {
    1459          24 :                         return rc;
    1460             :                 }
    1461         396 :         }
    1462             : 
    1463          32 :         return 0;
    1464          56 : }
    1465             : 
    1466             : static int
    1467          43 : _dif_strip_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1468             :                       uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
    1469             :                       struct spdk_dif_error *err_blk)
    1470             : {
    1471             :         uint32_t data_block_size;
    1472          43 :         uint64_t guard = 0;
    1473          43 :         struct spdk_dif dif = {};
    1474             : 
    1475          43 :         data_block_size = ctx->block_size - ctx->md_size;
    1476             : 
    1477          43 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1478          86 :                 guard = _dif_generate_guard_copy_split(ctx->guard_seed, dst_sgl, src_sgl,
    1479          43 :                                                        data_block_size, ctx->dif_pi_format);
    1480          86 :                 guard = dif_generate_guard_split(guard, src_sgl, data_block_size,
    1481          43 :                                                  ctx->guard_interval - data_block_size, ctx);
    1482          43 :         } else {
    1483           0 :                 _data_copy_split(dst_sgl, src_sgl, data_block_size);
    1484           0 :                 _dif_sgl_advance(src_sgl, ctx->guard_interval - data_block_size);
    1485             :         }
    1486             : 
    1487          43 :         dif_load_split(src_sgl, &dif, ctx);
    1488             : 
    1489          43 :         return _dif_verify(&dif, guard, offset_blocks, ctx, err_blk);
    1490             : }
    1491             : 
    1492             : static int
    1493          31 : dif_strip_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1494             :                      uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1495             :                      struct spdk_dif_error *err_blk)
    1496             : {
    1497             :         uint32_t offset_blocks;
    1498             :         int rc;
    1499             : 
    1500          50 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1501          43 :                 rc = _dif_strip_copy_split(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
    1502          43 :                 if (rc != 0) {
    1503          24 :                         return rc;
    1504             :                 }
    1505          19 :         }
    1506             : 
    1507           7 :         return 0;
    1508          31 : }
    1509             : 
    1510             : static void
    1511          12 : _dif_disable_strip_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1512             :                         const struct spdk_dif_ctx *ctx)
    1513             : {
    1514          12 :         uint32_t offset = 0, src_len, dst_len, buf_len, data_block_size;
    1515             :         uint8_t *src, *dst;
    1516             : 
    1517          12 :         data_block_size = ctx->block_size - ctx->md_size;
    1518             : 
    1519          32 :         while (offset < data_block_size) {
    1520          20 :                 _dif_sgl_get_buf(src_sgl, &src, &src_len);
    1521          20 :                 _dif_sgl_get_buf(dst_sgl, &dst, &dst_len);
    1522          20 :                 buf_len = spdk_min(src_len, dst_len);
    1523          20 :                 buf_len = spdk_min(buf_len, data_block_size - offset);
    1524             : 
    1525          20 :                 memcpy(dst, src, buf_len);
    1526             : 
    1527          20 :                 _dif_sgl_advance(src_sgl, buf_len);
    1528          20 :                 _dif_sgl_advance(dst_sgl, buf_len);
    1529          20 :                 offset += buf_len;
    1530             :         }
    1531             : 
    1532          12 :         _dif_sgl_advance(src_sgl, ctx->md_size);
    1533          12 : }
    1534             : 
    1535             : static void
    1536           3 : dif_disable_strip_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1537             :                        uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1538             : {
    1539             :         uint32_t offset_blocks;
    1540             : 
    1541          15 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1542          12 :                 _dif_disable_strip_copy(src_sgl, dst_sgl, ctx);
    1543          12 :         }
    1544           3 : }
    1545             : 
    1546             : static int
    1547          90 : _spdk_dif_strip_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1548             :                      uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1549             :                      struct spdk_dif_error *err_blk)
    1550             : {
    1551             :         uint32_t data_block_size;
    1552             : 
    1553          90 :         data_block_size = ctx->block_size - ctx->md_size;
    1554             : 
    1555          90 :         if (!_dif_sgl_is_valid(dst_sgl, data_block_size * num_blocks) ||
    1556          90 :             !_dif_sgl_is_valid(src_sgl, ctx->block_size * num_blocks)) {
    1557           0 :                 SPDK_ERRLOG("Size of iovec arrays are not valid\n");
    1558           0 :                 return -EINVAL;
    1559             :         }
    1560             : 
    1561          90 :         if (_dif_is_disabled(ctx->dif_type)) {
    1562           3 :                 dif_disable_strip_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1563           3 :                 return 0;
    1564             :         }
    1565             : 
    1566          87 :         if (_dif_sgl_is_bytes_multiple(dst_sgl, data_block_size) &&
    1567          56 :             _dif_sgl_is_bytes_multiple(src_sgl, ctx->block_size)) {
    1568          56 :                 return dif_strip_copy(src_sgl, dst_sgl, num_blocks, ctx, err_blk);
    1569             :         } else {
    1570          31 :                 return dif_strip_copy_split(src_sgl, dst_sgl, num_blocks, ctx, err_blk);
    1571             :         }
    1572          90 : }
    1573             : 
    1574             : static int
    1575          96 : _dif_verify_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1576             :                  uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
    1577             :                  struct spdk_dif_error *err_blk)
    1578             : {
    1579             :         uint8_t *src, *dst;
    1580             :         int rc;
    1581          96 :         uint64_t guard = 0;
    1582             : 
    1583          96 :         _dif_sgl_get_buf(src_sgl, &src, NULL);
    1584          96 :         _dif_sgl_get_buf(dst_sgl, &dst, NULL);
    1585             : 
    1586          96 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1587          48 :                 guard = _dif_generate_guard_copy(ctx->guard_seed, dst, src, ctx->guard_interval,
    1588          24 :                                                  ctx->dif_pi_format);
    1589          24 :         } else {
    1590          72 :                 memcpy(dst, src, ctx->guard_interval);
    1591             :         }
    1592             : 
    1593          96 :         rc = _dif_verify(src + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
    1594          96 :         if (rc != 0) {
    1595           0 :                 return rc;
    1596             :         }
    1597             : 
    1598          96 :         _dif_sgl_advance(src_sgl, ctx->block_size);
    1599          96 :         _dif_sgl_advance(dst_sgl, ctx->block_size);
    1600             : 
    1601          96 :         return 0;
    1602          96 : }
    1603             : 
    1604             : static int
    1605          16 : dif_verify_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1606             :                 uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1607             :                 struct spdk_dif_error *err_blk)
    1608             : {
    1609             :         uint32_t offset_blocks;
    1610             :         int rc;
    1611             : 
    1612         112 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1613          96 :                 rc = _dif_verify_copy(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
    1614          96 :                 if (rc != 0) {
    1615           0 :                         return rc;
    1616             :                 }
    1617          96 :         }
    1618             : 
    1619          16 :         return 0;
    1620          16 : }
    1621             : 
    1622             : static int
    1623           8 : _dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1624             :                        uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
    1625             :                        struct spdk_dif_error *err_blk)
    1626             : {
    1627           8 :         uint64_t guard = 0;
    1628           8 :         struct spdk_dif dif = {};
    1629             : 
    1630           8 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1631          16 :                 guard = _dif_generate_guard_copy_split(ctx->guard_seed, dst_sgl, src_sgl,
    1632           8 :                                                        ctx->guard_interval, ctx->dif_pi_format);
    1633           8 :         } else {
    1634           0 :                 _data_copy_split(dst_sgl, src_sgl, ctx->guard_interval);
    1635             :         }
    1636             : 
    1637           8 :         dif_load_split(src_sgl, &dif, ctx);
    1638           8 :         _dif_sgl_advance(dst_sgl, ctx->block_size - ctx->guard_interval);
    1639             : 
    1640           8 :         return _dif_verify(&dif, guard, offset_blocks, ctx, err_blk);
    1641             : }
    1642             : 
    1643             : static int
    1644           2 : dif_verify_copy_split(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1645             :                       uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1646             :                       struct spdk_dif_error *err_blk)
    1647             : {
    1648             :         uint32_t offset_blocks;
    1649             :         int rc;
    1650             : 
    1651          10 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1652           8 :                 rc = _dif_verify_copy_split(src_sgl, dst_sgl, offset_blocks, ctx, err_blk);
    1653           8 :                 if (rc != 0) {
    1654           0 :                         return rc;
    1655             :                 }
    1656           8 :         }
    1657             : 
    1658           2 :         return 0;
    1659           2 : }
    1660             : 
    1661             : static int
    1662          18 : _spdk_dif_verify_copy(struct _dif_sgl *src_sgl, struct _dif_sgl *dst_sgl,
    1663             :                       uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1664             :                       struct spdk_dif_error *err_blk)
    1665             : {
    1666          18 :         if (!_dif_sgl_is_valid(dst_sgl, ctx->block_size * num_blocks) ||
    1667          18 :             !_dif_sgl_is_valid(src_sgl, ctx->block_size * num_blocks)) {
    1668           0 :                 SPDK_ERRLOG("Size of iovec arrays are not valid\n");
    1669           0 :                 return -EINVAL;
    1670             :         }
    1671             : 
    1672          18 :         if (_dif_is_disabled(ctx->dif_type)) {
    1673           0 :                 dif_disable_copy(src_sgl, dst_sgl, num_blocks, ctx);
    1674           0 :                 return 0;
    1675             :         }
    1676             : 
    1677          18 :         if (_dif_sgl_is_bytes_multiple(dst_sgl, ctx->block_size) &&
    1678          16 :             _dif_sgl_is_bytes_multiple(src_sgl, ctx->block_size)) {
    1679          16 :                 return dif_verify_copy(src_sgl, dst_sgl, num_blocks, ctx, err_blk);
    1680             :         } else {
    1681           2 :                 return dif_verify_copy_split(src_sgl, dst_sgl, num_blocks, ctx, err_blk);
    1682             :         }
    1683          18 : }
    1684             : 
    1685             : int
    1686         108 : spdk_dif_verify_copy(struct iovec *iovs, int iovcnt, struct iovec *bounce_iovs,
    1687             :                      int bounce_iovcnt, uint32_t num_blocks,
    1688             :                      const struct spdk_dif_ctx *ctx,
    1689             :                      struct spdk_dif_error *err_blk)
    1690             : {
    1691             :         struct _dif_sgl src_sgl, dst_sgl;
    1692             : 
    1693         108 :         _dif_sgl_init(&src_sgl, bounce_iovs, bounce_iovcnt);
    1694         108 :         _dif_sgl_init(&dst_sgl, iovs, iovcnt);
    1695             : 
    1696         108 :         if (!(ctx->dif_flags & SPDK_DIF_FLAGS_NVME_PRACT) ||
    1697          18 :             ctx->md_size == _dif_size(ctx->dif_pi_format)) {
    1698          90 :                 return _spdk_dif_strip_copy(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
    1699             :         } else {
    1700          18 :                 return _spdk_dif_verify_copy(&src_sgl, &dst_sgl, num_blocks, ctx, err_blk);
    1701             :         }
    1702         108 : }
    1703             : 
    1704             : static void
    1705         240 : _bit_flip(uint8_t *buf, uint32_t flip_bit)
    1706             : {
    1707             :         uint8_t byte;
    1708             : 
    1709         240 :         byte = *buf;
    1710         240 :         byte ^= 1 << flip_bit;
    1711         240 :         *buf = byte;
    1712         240 : }
    1713             : 
    1714             : static int
    1715         240 : _dif_inject_error(struct _dif_sgl *sgl,
    1716             :                   uint32_t block_size, uint32_t num_blocks,
    1717             :                   uint32_t inject_offset_blocks,
    1718             :                   uint32_t inject_offset_bytes,
    1719             :                   uint32_t inject_offset_bits)
    1720             : {
    1721             :         uint32_t offset_in_block, buf_len;
    1722             :         uint8_t *buf;
    1723             : 
    1724         240 :         _dif_sgl_advance(sgl, block_size * inject_offset_blocks);
    1725             : 
    1726         240 :         offset_in_block = 0;
    1727             : 
    1728         316 :         while (offset_in_block < block_size) {
    1729         316 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
    1730         316 :                 buf_len = spdk_min(buf_len, block_size - offset_in_block);
    1731             : 
    1732         316 :                 if (inject_offset_bytes >= offset_in_block &&
    1733         316 :                     inject_offset_bytes < offset_in_block + buf_len) {
    1734         240 :                         buf += inject_offset_bytes - offset_in_block;
    1735         240 :                         _bit_flip(buf, inject_offset_bits);
    1736         240 :                         return 0;
    1737             :                 }
    1738             : 
    1739          76 :                 _dif_sgl_advance(sgl, buf_len);
    1740          76 :                 offset_in_block += buf_len;
    1741             :         }
    1742             : 
    1743           0 :         return -1;
    1744         240 : }
    1745             : 
    1746             : static int
    1747         240 : dif_inject_error(struct _dif_sgl *sgl, uint32_t block_size, uint32_t num_blocks,
    1748             :                  uint32_t start_inject_bytes, uint32_t inject_range_bytes,
    1749             :                  uint32_t *inject_offset)
    1750             : {
    1751             :         uint32_t inject_offset_blocks, inject_offset_bytes, inject_offset_bits;
    1752             :         uint32_t offset_blocks;
    1753             :         int rc;
    1754             : 
    1755         240 :         srand(time(0));
    1756             : 
    1757         240 :         inject_offset_blocks = rand() % num_blocks;
    1758         240 :         inject_offset_bytes = start_inject_bytes + (rand() % inject_range_bytes);
    1759         240 :         inject_offset_bits = rand() % 8;
    1760             : 
    1761         672 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1762         672 :                 if (offset_blocks == inject_offset_blocks) {
    1763         480 :                         rc = _dif_inject_error(sgl, block_size, num_blocks,
    1764         240 :                                                inject_offset_blocks,
    1765         240 :                                                inject_offset_bytes,
    1766         240 :                                                inject_offset_bits);
    1767         240 :                         if (rc == 0) {
    1768         240 :                                 *inject_offset = inject_offset_blocks;
    1769         240 :                         }
    1770         240 :                         return rc;
    1771             :                 }
    1772         432 :         }
    1773             : 
    1774           0 :         return -1;
    1775         240 : }
    1776             : 
    1777             : int
    1778         192 : spdk_dif_inject_error(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
    1779             :                       const struct spdk_dif_ctx *ctx, uint32_t inject_flags,
    1780             :                       uint32_t *inject_offset)
    1781             : {
    1782             :         struct _dif_sgl sgl;
    1783             :         int rc;
    1784             : 
    1785         192 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    1786             : 
    1787         192 :         if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
    1788           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    1789           0 :                 return -EINVAL;
    1790             :         }
    1791             : 
    1792         192 :         if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
    1793          96 :                 rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
    1794          48 :                                       ctx->guard_interval + _dif_reftag_offset(ctx->dif_pi_format),
    1795          48 :                                       _dif_reftag_size(ctx->dif_pi_format),
    1796          48 :                                       inject_offset);
    1797          48 :                 if (rc != 0) {
    1798           0 :                         SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
    1799           0 :                         return rc;
    1800             :                 }
    1801          48 :         }
    1802             : 
    1803         192 :         if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
    1804          96 :                 rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
    1805          48 :                                       ctx->guard_interval + _dif_apptag_offset(ctx->dif_pi_format),
    1806          48 :                                       _dif_apptag_size(),
    1807          48 :                                       inject_offset);
    1808          48 :                 if (rc != 0) {
    1809           0 :                         SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
    1810           0 :                         return rc;
    1811             :                 }
    1812          48 :         }
    1813         192 :         if (inject_flags & SPDK_DIF_GUARD_ERROR) {
    1814          96 :                 rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
    1815          48 :                                       ctx->guard_interval,
    1816          48 :                                       _dif_guard_size(ctx->dif_pi_format),
    1817          48 :                                       inject_offset);
    1818          48 :                 if (rc != 0) {
    1819           0 :                         SPDK_ERRLOG("Failed to inject error to Guard.\n");
    1820           0 :                         return rc;
    1821             :                 }
    1822          48 :         }
    1823             : 
    1824         192 :         if (inject_flags & SPDK_DIF_DATA_ERROR) {
    1825             :                 /* If the DIF information is contained within the last 8/16 bytes of
    1826             :                  * metadata (depending on the PI format), then the CRC covers all metadata
    1827             :                  * bytes up to but excluding the last 8/16 bytes. But error injection does not
    1828             :                  * cover these metadata because classification is not determined yet.
    1829             :                  *
    1830             :                  * Note: Error injection to data block is expected to be detected as
    1831             :                  * guard error.
    1832             :                  */
    1833          96 :                 rc = dif_inject_error(&sgl, ctx->block_size, num_blocks,
    1834             :                                       0,
    1835          48 :                                       ctx->block_size - ctx->md_size,
    1836          48 :                                       inject_offset);
    1837          48 :                 if (rc != 0) {
    1838           0 :                         SPDK_ERRLOG("Failed to inject error to data block.\n");
    1839           0 :                         return rc;
    1840             :                 }
    1841          48 :         }
    1842             : 
    1843         192 :         return 0;
    1844         192 : }
    1845             : 
    1846             : static void
    1847          62 : dix_generate(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    1848             :              uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1849             : {
    1850          62 :         uint32_t offset_blocks = 0;
    1851             :         uint8_t *data_buf, *md_buf;
    1852             :         uint64_t guard;
    1853             : 
    1854         814 :         while (offset_blocks < num_blocks) {
    1855         752 :                 _dif_sgl_get_buf(data_sgl, &data_buf, NULL);
    1856         752 :                 _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
    1857             : 
    1858         752 :                 guard = 0;
    1859         752 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1860        1276 :                         guard = _dif_generate_guard(ctx->guard_seed, data_buf, ctx->block_size,
    1861         638 :                                                     ctx->dif_pi_format);
    1862        1276 :                         guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
    1863         638 :                                                     ctx->dif_pi_format);
    1864         638 :                 }
    1865             : 
    1866         752 :                 _dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
    1867             : 
    1868         752 :                 _dif_sgl_advance(data_sgl, ctx->block_size);
    1869         752 :                 _dif_sgl_advance(md_sgl, ctx->md_size);
    1870         752 :                 offset_blocks++;
    1871             :         }
    1872          62 : }
    1873             : 
    1874             : static void
    1875          75 : _dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    1876             :                     uint32_t offset_blocks, const struct spdk_dif_ctx *ctx)
    1877             : {
    1878             :         uint32_t offset_in_block, data_buf_len;
    1879             :         uint8_t *data_buf, *md_buf;
    1880          75 :         uint64_t guard = 0;
    1881             : 
    1882          75 :         _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
    1883             : 
    1884          75 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1885          75 :                 guard = ctx->guard_seed;
    1886          75 :         }
    1887          75 :         offset_in_block = 0;
    1888             : 
    1889         231 :         while (offset_in_block < ctx->block_size) {
    1890         156 :                 _dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
    1891         156 :                 data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
    1892             : 
    1893         156 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1894         312 :                         guard = _dif_generate_guard(guard, data_buf, data_buf_len,
    1895         156 :                                                     ctx->dif_pi_format);
    1896         156 :                 }
    1897             : 
    1898         156 :                 _dif_sgl_advance(data_sgl, data_buf_len);
    1899         156 :                 offset_in_block += data_buf_len;
    1900             :         }
    1901             : 
    1902          75 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1903         150 :                 guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
    1904          75 :                                             ctx->dif_pi_format);
    1905          75 :         }
    1906             : 
    1907          75 :         _dif_sgl_advance(md_sgl, ctx->md_size);
    1908             : 
    1909          75 :         _dif_generate(md_buf + ctx->guard_interval, guard, offset_blocks, ctx);
    1910          75 : }
    1911             : 
    1912             : static void
    1913          33 : dix_generate_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    1914             :                    uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1915             : {
    1916             :         uint32_t offset_blocks;
    1917             : 
    1918         108 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    1919          75 :                 _dix_generate_split(data_sgl, md_sgl, offset_blocks, ctx);
    1920          75 :         }
    1921          33 : }
    1922             : 
    1923             : int
    1924          95 : spdk_dix_generate(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
    1925             :                   uint32_t num_blocks, const struct spdk_dif_ctx *ctx)
    1926             : {
    1927             :         struct _dif_sgl data_sgl, md_sgl;
    1928             : 
    1929          95 :         _dif_sgl_init(&data_sgl, iovs, iovcnt);
    1930          95 :         _dif_sgl_init(&md_sgl, md_iov, 1);
    1931             : 
    1932          95 :         if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
    1933          95 :             !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
    1934           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    1935           0 :                 return -EINVAL;
    1936             :         }
    1937             : 
    1938          95 :         if (_dif_is_disabled(ctx->dif_type)) {
    1939           0 :                 return 0;
    1940             :         }
    1941             : 
    1942          95 :         if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
    1943          62 :                 dix_generate(&data_sgl, &md_sgl, num_blocks, ctx);
    1944          62 :         } else {
    1945          33 :                 dix_generate_split(&data_sgl, &md_sgl, num_blocks, ctx);
    1946             :         }
    1947             : 
    1948          95 :         return 0;
    1949          95 : }
    1950             : 
    1951             : static int
    1952          67 : dix_verify(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    1953             :            uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    1954             :            struct spdk_dif_error *err_blk)
    1955             : {
    1956          67 :         uint32_t offset_blocks = 0;
    1957             :         uint8_t *data_buf, *md_buf;
    1958             :         uint64_t guard;
    1959             :         int rc;
    1960             : 
    1961         947 :         while (offset_blocks < num_blocks) {
    1962         904 :                 _dif_sgl_get_buf(data_sgl, &data_buf, NULL);
    1963         904 :                 _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
    1964             : 
    1965         904 :                 guard = 0;
    1966         904 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1967        1580 :                         guard = _dif_generate_guard(ctx->guard_seed, data_buf, ctx->block_size,
    1968         790 :                                                     ctx->dif_pi_format);
    1969        1580 :                         guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
    1970         790 :                                                     ctx->dif_pi_format);
    1971         790 :                 }
    1972             : 
    1973         904 :                 rc = _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
    1974         904 :                 if (rc != 0) {
    1975          24 :                         return rc;
    1976             :                 }
    1977             : 
    1978         880 :                 _dif_sgl_advance(data_sgl, ctx->block_size);
    1979         880 :                 _dif_sgl_advance(md_sgl, ctx->md_size);
    1980         880 :                 offset_blocks++;
    1981             :         }
    1982             : 
    1983          43 :         return 0;
    1984          67 : }
    1985             : 
    1986             : static int
    1987          51 : _dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    1988             :                   uint32_t offset_blocks, const struct spdk_dif_ctx *ctx,
    1989             :                   struct spdk_dif_error *err_blk)
    1990             : {
    1991             :         uint32_t offset_in_block, data_buf_len;
    1992             :         uint8_t *data_buf, *md_buf;
    1993          51 :         uint64_t guard = 0;
    1994             : 
    1995          51 :         _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
    1996             : 
    1997          51 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    1998          51 :                 guard = ctx->guard_seed;
    1999          51 :         }
    2000          51 :         offset_in_block = 0;
    2001             : 
    2002         159 :         while (offset_in_block < ctx->block_size) {
    2003         108 :                 _dif_sgl_get_buf(data_sgl, &data_buf, &data_buf_len);
    2004         108 :                 data_buf_len = spdk_min(data_buf_len, ctx->block_size - offset_in_block);
    2005             : 
    2006         108 :                 if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2007         216 :                         guard = _dif_generate_guard(guard, data_buf, data_buf_len,
    2008         108 :                                                     ctx->dif_pi_format);
    2009         108 :                 }
    2010             : 
    2011         108 :                 _dif_sgl_advance(data_sgl, data_buf_len);
    2012         108 :                 offset_in_block += data_buf_len;
    2013             :         }
    2014             : 
    2015          51 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2016         102 :                 guard = _dif_generate_guard(guard, md_buf, ctx->guard_interval,
    2017          51 :                                             ctx->dif_pi_format);
    2018          51 :         }
    2019             : 
    2020          51 :         _dif_sgl_advance(md_sgl, ctx->md_size);
    2021             : 
    2022          51 :         return _dif_verify(md_buf + ctx->guard_interval, guard, offset_blocks, ctx, err_blk);
    2023             : }
    2024             : 
    2025             : static int
    2026          33 : dix_verify_split(struct _dif_sgl *data_sgl, struct _dif_sgl *md_sgl,
    2027             :                  uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    2028             :                  struct spdk_dif_error *err_blk)
    2029             : {
    2030             :         uint32_t offset_blocks;
    2031             :         int rc;
    2032             : 
    2033          60 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    2034          51 :                 rc = _dix_verify_split(data_sgl, md_sgl, offset_blocks, ctx, err_blk);
    2035          51 :                 if (rc != 0) {
    2036          24 :                         return rc;
    2037             :                 }
    2038          27 :         }
    2039             : 
    2040           9 :         return 0;
    2041          33 : }
    2042             : 
    2043             : int
    2044         100 : spdk_dix_verify(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
    2045             :                 uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    2046             :                 struct spdk_dif_error *err_blk)
    2047             : {
    2048             :         struct _dif_sgl data_sgl, md_sgl;
    2049             : 
    2050         100 :         if (md_iov->iov_base == NULL) {
    2051           0 :                 SPDK_ERRLOG("Metadata buffer is NULL.\n");
    2052           0 :                 return -EINVAL;
    2053             :         }
    2054             : 
    2055         100 :         _dif_sgl_init(&data_sgl, iovs, iovcnt);
    2056         100 :         _dif_sgl_init(&md_sgl, md_iov, 1);
    2057             : 
    2058         100 :         if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
    2059         100 :             !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
    2060           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    2061           0 :                 return -EINVAL;
    2062             :         }
    2063             : 
    2064         100 :         if (_dif_is_disabled(ctx->dif_type)) {
    2065           0 :                 return 0;
    2066             :         }
    2067             : 
    2068         100 :         if (_dif_sgl_is_bytes_multiple(&data_sgl, ctx->block_size)) {
    2069          67 :                 return dix_verify(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
    2070             :         } else {
    2071          33 :                 return dix_verify_split(&data_sgl, &md_sgl, num_blocks, ctx, err_blk);
    2072             :         }
    2073         100 : }
    2074             : 
    2075             : int
    2076          48 : spdk_dix_inject_error(struct iovec *iovs, int iovcnt, struct iovec *md_iov,
    2077             :                       uint32_t num_blocks, const struct spdk_dif_ctx *ctx,
    2078             :                       uint32_t inject_flags, uint32_t *inject_offset)
    2079             : {
    2080             :         struct _dif_sgl data_sgl, md_sgl;
    2081             :         int rc;
    2082             : 
    2083          48 :         _dif_sgl_init(&data_sgl, iovs, iovcnt);
    2084          48 :         _dif_sgl_init(&md_sgl, md_iov, 1);
    2085             : 
    2086          48 :         if (!_dif_sgl_is_valid(&data_sgl, ctx->block_size * num_blocks) ||
    2087          48 :             !_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
    2088           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    2089           0 :                 return -EINVAL;
    2090             :         }
    2091             : 
    2092          48 :         if (inject_flags & SPDK_DIF_REFTAG_ERROR) {
    2093          24 :                 rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
    2094          12 :                                       ctx->guard_interval + _dif_reftag_offset(ctx->dif_pi_format),
    2095          12 :                                       _dif_reftag_size(ctx->dif_pi_format),
    2096          12 :                                       inject_offset);
    2097          12 :                 if (rc != 0) {
    2098           0 :                         SPDK_ERRLOG("Failed to inject error to Reference Tag.\n");
    2099           0 :                         return rc;
    2100             :                 }
    2101          12 :         }
    2102             : 
    2103          48 :         if (inject_flags & SPDK_DIF_APPTAG_ERROR) {
    2104          24 :                 rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
    2105          12 :                                       ctx->guard_interval + _dif_apptag_offset(ctx->dif_pi_format),
    2106          12 :                                       _dif_apptag_size(),
    2107          12 :                                       inject_offset);
    2108          12 :                 if (rc != 0) {
    2109           0 :                         SPDK_ERRLOG("Failed to inject error to Application Tag.\n");
    2110           0 :                         return rc;
    2111             :                 }
    2112          12 :         }
    2113             : 
    2114          48 :         if (inject_flags & SPDK_DIF_GUARD_ERROR) {
    2115          24 :                 rc = dif_inject_error(&md_sgl, ctx->md_size, num_blocks,
    2116          12 :                                       ctx->guard_interval,
    2117          12 :                                       _dif_guard_size(ctx->dif_pi_format),
    2118          12 :                                       inject_offset);
    2119          12 :                 if (rc != 0) {
    2120           0 :                         SPDK_ERRLOG("Failed to inject error to Guard.\n");
    2121           0 :                         return rc;
    2122             :                 }
    2123          12 :         }
    2124             : 
    2125          48 :         if (inject_flags & SPDK_DIF_DATA_ERROR) {
    2126             :                 /* Note: Error injection to data block is expected to be detected
    2127             :                  * as guard error.
    2128             :                  */
    2129          24 :                 rc = dif_inject_error(&data_sgl, ctx->block_size, num_blocks,
    2130             :                                       0,
    2131          12 :                                       ctx->block_size,
    2132          12 :                                       inject_offset);
    2133          12 :                 if (rc != 0) {
    2134           0 :                         SPDK_ERRLOG("Failed to inject error to Guard.\n");
    2135           0 :                         return rc;
    2136             :                 }
    2137          12 :         }
    2138             : 
    2139          48 :         return 0;
    2140          48 : }
    2141             : 
    2142             : static uint32_t
    2143         369 : _to_next_boundary(uint32_t offset, uint32_t boundary)
    2144             : {
    2145         369 :         return boundary - (offset % boundary);
    2146             : }
    2147             : 
    2148             : static uint32_t
    2149         237 : _to_size_with_md(uint32_t size, uint32_t data_block_size, uint32_t block_size)
    2150             : {
    2151         237 :         return (size / data_block_size) * block_size + (size % data_block_size);
    2152             : }
    2153             : 
    2154             : int
    2155          38 : spdk_dif_set_md_interleave_iovs(struct iovec *iovs, int iovcnt,
    2156             :                                 struct iovec *buf_iovs, int buf_iovcnt,
    2157             :                                 uint32_t data_offset, uint32_t data_len,
    2158             :                                 uint32_t *_mapped_len,
    2159             :                                 const struct spdk_dif_ctx *ctx)
    2160             : {
    2161             :         uint32_t data_block_size, data_unalign, buf_len, buf_offset, len;
    2162             :         struct _dif_sgl dif_sgl;
    2163             :         struct _dif_sgl buf_sgl;
    2164             : 
    2165          38 :         if (iovs == NULL || iovcnt == 0 || buf_iovs == NULL || buf_iovcnt == 0) {
    2166           0 :                 return -EINVAL;
    2167             :         }
    2168             : 
    2169          38 :         data_block_size = ctx->block_size - ctx->md_size;
    2170             : 
    2171          38 :         data_unalign = ctx->data_offset % data_block_size;
    2172             : 
    2173          76 :         buf_len = _to_size_with_md(data_unalign + data_offset + data_len, data_block_size,
    2174          38 :                                    ctx->block_size);
    2175          38 :         buf_len -= data_unalign;
    2176             : 
    2177          38 :         _dif_sgl_init(&dif_sgl, iovs, iovcnt);
    2178          38 :         _dif_sgl_init(&buf_sgl, buf_iovs, buf_iovcnt);
    2179             : 
    2180          38 :         if (!_dif_sgl_is_valid(&buf_sgl, buf_len)) {
    2181           1 :                 SPDK_ERRLOG("Buffer overflow will occur.\n");
    2182           1 :                 return -ERANGE;
    2183             :         }
    2184             : 
    2185          37 :         buf_offset = _to_size_with_md(data_unalign + data_offset, data_block_size, ctx->block_size);
    2186          37 :         buf_offset -= data_unalign;
    2187             : 
    2188          37 :         _dif_sgl_advance(&buf_sgl, buf_offset);
    2189             : 
    2190         121 :         while (data_len != 0) {
    2191         104 :                 len = spdk_min(data_len, _to_next_boundary(ctx->data_offset + data_offset, data_block_size));
    2192         104 :                 if (!_dif_sgl_append_split(&dif_sgl, &buf_sgl, len)) {
    2193          20 :                         break;
    2194             :                 }
    2195          84 :                 _dif_sgl_advance(&buf_sgl, ctx->md_size);
    2196          84 :                 data_offset += len;
    2197          84 :                 data_len -= len;
    2198             :         }
    2199             : 
    2200          37 :         if (_mapped_len != NULL) {
    2201          37 :                 *_mapped_len = dif_sgl.total_size;
    2202          37 :         }
    2203             : 
    2204          37 :         return iovcnt - dif_sgl.iovcnt;
    2205          38 : }
    2206             : 
    2207             : static int
    2208          67 : _dif_sgl_setup_stream(struct _dif_sgl *sgl, uint32_t *_buf_offset, uint32_t *_buf_len,
    2209             :                       uint32_t data_offset, uint32_t data_len,
    2210             :                       const struct spdk_dif_ctx *ctx)
    2211             : {
    2212             :         uint32_t data_block_size, data_unalign, buf_len, buf_offset;
    2213             : 
    2214          67 :         data_block_size = ctx->block_size - ctx->md_size;
    2215             : 
    2216          67 :         data_unalign = ctx->data_offset % data_block_size;
    2217             : 
    2218             :         /* If the last data block is complete, DIF of the data block is
    2219             :          * inserted or verified in this turn.
    2220             :          */
    2221         134 :         buf_len = _to_size_with_md(data_unalign + data_offset + data_len, data_block_size,
    2222          67 :                                    ctx->block_size);
    2223          67 :         buf_len -= data_unalign;
    2224             : 
    2225          67 :         if (!_dif_sgl_is_valid(sgl, buf_len)) {
    2226           3 :                 return -ERANGE;
    2227             :         }
    2228             : 
    2229          64 :         buf_offset = _to_size_with_md(data_unalign + data_offset, data_block_size, ctx->block_size);
    2230          64 :         buf_offset -= data_unalign;
    2231             : 
    2232          64 :         _dif_sgl_advance(sgl, buf_offset);
    2233          64 :         buf_len -= buf_offset;
    2234             : 
    2235          64 :         buf_offset += data_unalign;
    2236             : 
    2237          64 :         *_buf_offset = buf_offset;
    2238          64 :         *_buf_len = buf_len;
    2239             : 
    2240          64 :         return 0;
    2241          67 : }
    2242             : 
    2243             : int
    2244          49 : spdk_dif_generate_stream(struct iovec *iovs, int iovcnt,
    2245             :                          uint32_t data_offset, uint32_t data_len,
    2246             :                          struct spdk_dif_ctx *ctx)
    2247             : {
    2248          49 :         uint32_t buf_len = 0, buf_offset = 0;
    2249             :         uint32_t len, offset_in_block, offset_blocks;
    2250          49 :         uint64_t guard = 0;
    2251             :         struct _dif_sgl sgl;
    2252             :         int rc;
    2253             : 
    2254          49 :         if (iovs == NULL || iovcnt == 0) {
    2255           0 :                 return -EINVAL;
    2256             :         }
    2257             : 
    2258          49 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2259          49 :                 guard = ctx->last_guard;
    2260          49 :         }
    2261             : 
    2262          49 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    2263             : 
    2264          49 :         rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
    2265          49 :         if (rc != 0) {
    2266           3 :                 return rc;
    2267             :         }
    2268             : 
    2269         122 :         while (buf_len != 0) {
    2270          76 :                 len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
    2271          76 :                 offset_in_block = buf_offset % ctx->block_size;
    2272          76 :                 offset_blocks = buf_offset / ctx->block_size;
    2273             : 
    2274          76 :                 guard = _dif_generate_split(&sgl, offset_in_block, len, guard, offset_blocks, ctx);
    2275             : 
    2276          76 :                 buf_len -= len;
    2277          76 :                 buf_offset += len;
    2278             :         }
    2279             : 
    2280          46 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2281          46 :                 ctx->last_guard = guard;
    2282          46 :         }
    2283             : 
    2284          46 :         return 0;
    2285          49 : }
    2286             : 
    2287             : int
    2288           9 : spdk_dif_verify_stream(struct iovec *iovs, int iovcnt,
    2289             :                        uint32_t data_offset, uint32_t data_len,
    2290             :                        struct spdk_dif_ctx *ctx,
    2291             :                        struct spdk_dif_error *err_blk)
    2292             : {
    2293           9 :         uint32_t buf_len = 0, buf_offset = 0;
    2294             :         uint32_t len, offset_in_block, offset_blocks;
    2295           9 :         uint64_t guard = 0;
    2296             :         struct _dif_sgl sgl;
    2297           9 :         int rc = 0;
    2298             : 
    2299           9 :         if (iovs == NULL || iovcnt == 0) {
    2300           0 :                 return -EINVAL;
    2301             :         }
    2302             : 
    2303           9 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2304           9 :                 guard = ctx->last_guard;
    2305           9 :         }
    2306             : 
    2307           9 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    2308             : 
    2309           9 :         rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
    2310           9 :         if (rc != 0) {
    2311           0 :                 return rc;
    2312             :         }
    2313             : 
    2314          27 :         while (buf_len != 0) {
    2315          18 :                 len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
    2316          18 :                 offset_in_block = buf_offset % ctx->block_size;
    2317          18 :                 offset_blocks = buf_offset / ctx->block_size;
    2318             : 
    2319          36 :                 rc = _dif_verify_split(&sgl, offset_in_block, len, &guard, offset_blocks,
    2320          18 :                                        ctx, err_blk);
    2321          18 :                 if (rc != 0) {
    2322           0 :                         goto error;
    2323             :                 }
    2324             : 
    2325          18 :                 buf_len -= len;
    2326          18 :                 buf_offset += len;
    2327             :         }
    2328             : 
    2329          18 :         if (ctx->dif_flags & SPDK_DIF_FLAGS_GUARD_CHECK) {
    2330           9 :                 ctx->last_guard = guard;
    2331           9 :         }
    2332             : error:
    2333           9 :         return rc;
    2334           9 : }
    2335             : 
    2336             : int
    2337           9 : spdk_dif_update_crc32c_stream(struct iovec *iovs, int iovcnt,
    2338             :                               uint32_t data_offset, uint32_t data_len,
    2339             :                               uint32_t *_crc32c, const struct spdk_dif_ctx *ctx)
    2340             : {
    2341           9 :         uint32_t buf_len = 0, buf_offset = 0, len, offset_in_block;
    2342             :         uint32_t crc32c;
    2343             :         struct _dif_sgl sgl;
    2344             :         int rc;
    2345             : 
    2346           9 :         if (iovs == NULL || iovcnt == 0) {
    2347           0 :                 return -EINVAL;
    2348             :         }
    2349             : 
    2350           9 :         crc32c = *_crc32c;
    2351           9 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    2352             : 
    2353           9 :         rc = _dif_sgl_setup_stream(&sgl, &buf_offset, &buf_len, data_offset, data_len, ctx);
    2354           9 :         if (rc != 0) {
    2355           0 :                 return rc;
    2356             :         }
    2357             : 
    2358          27 :         while (buf_len != 0) {
    2359          18 :                 len = spdk_min(buf_len, _to_next_boundary(buf_offset, ctx->block_size));
    2360          18 :                 offset_in_block = buf_offset % ctx->block_size;
    2361             : 
    2362          18 :                 crc32c = _dif_update_crc32c_split(&sgl, offset_in_block, len, crc32c, ctx);
    2363             : 
    2364          18 :                 buf_len -= len;
    2365          18 :                 buf_offset += len;
    2366             :         }
    2367             : 
    2368           9 :         *_crc32c = crc32c;
    2369             : 
    2370           9 :         return 0;
    2371           9 : }
    2372             : 
    2373             : void
    2374          10 : spdk_dif_get_range_with_md(uint32_t data_offset, uint32_t data_len,
    2375             :                            uint32_t *_buf_offset, uint32_t *_buf_len,
    2376             :                            const struct spdk_dif_ctx *ctx)
    2377             : {
    2378             :         uint32_t data_block_size, data_unalign, buf_offset, buf_len;
    2379             : 
    2380          10 :         if (!ctx->md_interleave) {
    2381           0 :                 buf_offset = data_offset;
    2382           0 :                 buf_len = data_len;
    2383           0 :         } else {
    2384          10 :                 data_block_size = ctx->block_size - ctx->md_size;
    2385             : 
    2386          10 :                 data_unalign = data_offset % data_block_size;
    2387             : 
    2388          10 :                 buf_offset = _to_size_with_md(data_offset, data_block_size, ctx->block_size);
    2389          20 :                 buf_len = _to_size_with_md(data_unalign + data_len, data_block_size, ctx->block_size) -
    2390          10 :                           data_unalign;
    2391             :         }
    2392             : 
    2393          10 :         if (_buf_offset != NULL) {
    2394          10 :                 *_buf_offset = buf_offset;
    2395          10 :         }
    2396             : 
    2397          10 :         if (_buf_len != NULL) {
    2398          10 :                 *_buf_len = buf_len;
    2399          10 :         }
    2400          10 : }
    2401             : 
    2402             : uint32_t
    2403          11 : spdk_dif_get_length_with_md(uint32_t data_len, const struct spdk_dif_ctx *ctx)
    2404             : {
    2405             :         uint32_t data_block_size;
    2406             : 
    2407          11 :         if (!ctx->md_interleave) {
    2408           0 :                 return data_len;
    2409             :         } else {
    2410          11 :                 data_block_size = ctx->block_size - ctx->md_size;
    2411             : 
    2412          11 :                 return _to_size_with_md(data_len, data_block_size, ctx->block_size);
    2413             :         }
    2414          11 : }
    2415             : 
    2416             : static int
    2417          72 : _dif_remap_ref_tag(struct _dif_sgl *sgl, uint32_t offset_blocks,
    2418             :                    const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk,
    2419             :                    bool check_ref_tag)
    2420             : {
    2421             :         uint32_t offset, buf_len;
    2422          72 :         uint64_t expected = 0, remapped;
    2423             :         uint8_t *buf;
    2424             :         struct _dif_sgl tmp_sgl;
    2425             :         struct spdk_dif dif;
    2426             : 
    2427             :         /* Fast forward to DIF field. */
    2428          72 :         _dif_sgl_advance(sgl, ctx->guard_interval);
    2429          72 :         _dif_sgl_copy(&tmp_sgl, sgl);
    2430             : 
    2431             :         /* Copy the split DIF field to the temporary DIF buffer */
    2432          72 :         offset = 0;
    2433         162 :         while (offset < _dif_size(ctx->dif_pi_format)) {
    2434          90 :                 _dif_sgl_get_buf(sgl, &buf, &buf_len);
    2435          90 :                 buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
    2436             : 
    2437          90 :                 memcpy((uint8_t *)&dif + offset, buf, buf_len);
    2438             : 
    2439          90 :                 _dif_sgl_advance(sgl, buf_len);
    2440          90 :                 offset += buf_len;
    2441             :         }
    2442             : 
    2443          72 :         if (_dif_ignore(&dif, ctx)) {
    2444           0 :                 goto end;
    2445             :         }
    2446             : 
    2447             :         /* For type 1 and 2, the Reference Tag is incremented for each
    2448             :          * subsequent logical block. For type 3, the Reference Tag
    2449             :          * remains the same as the initial Reference Tag.
    2450             :          */
    2451          72 :         if (ctx->dif_type != SPDK_DIF_TYPE3) {
    2452          72 :                 expected = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
    2453          72 :                 remapped = ctx->remapped_init_ref_tag + ctx->ref_tag_offset + offset_blocks;
    2454          72 :         } else {
    2455           0 :                 remapped = ctx->remapped_init_ref_tag;
    2456             :         }
    2457             : 
    2458             :         /* Verify the stored Reference Tag. */
    2459          72 :         if (check_ref_tag && !_dif_reftag_check(&dif, ctx, expected, offset_blocks, err_blk)) {
    2460           0 :                 return -1;
    2461             :         }
    2462             : 
    2463             :         /* Update the stored Reference Tag to the remapped one. */
    2464          72 :         _dif_set_reftag(&dif, remapped, ctx->dif_pi_format);
    2465             : 
    2466          72 :         offset = 0;
    2467         162 :         while (offset < _dif_size(ctx->dif_pi_format)) {
    2468          90 :                 _dif_sgl_get_buf(&tmp_sgl, &buf, &buf_len);
    2469          90 :                 buf_len = spdk_min(buf_len, _dif_size(ctx->dif_pi_format) - offset);
    2470             : 
    2471          90 :                 memcpy(buf, (uint8_t *)&dif + offset, buf_len);
    2472             : 
    2473          90 :                 _dif_sgl_advance(&tmp_sgl, buf_len);
    2474          90 :                 offset += buf_len;
    2475             :         }
    2476             : 
    2477             : end:
    2478          72 :         _dif_sgl_advance(sgl, ctx->block_size - ctx->guard_interval - _dif_size(ctx->dif_pi_format));
    2479             : 
    2480          72 :         return 0;
    2481          72 : }
    2482             : 
    2483             : int
    2484          12 : spdk_dif_remap_ref_tag(struct iovec *iovs, int iovcnt, uint32_t num_blocks,
    2485             :                        const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk,
    2486             :                        bool check_ref_tag)
    2487             : {
    2488             :         struct _dif_sgl sgl;
    2489             :         uint32_t offset_blocks;
    2490             :         int rc;
    2491             : 
    2492          12 :         _dif_sgl_init(&sgl, iovs, iovcnt);
    2493             : 
    2494          12 :         if (!_dif_sgl_is_valid(&sgl, ctx->block_size * num_blocks)) {
    2495           0 :                 SPDK_ERRLOG("Size of iovec array is not valid.\n");
    2496           0 :                 return -EINVAL;
    2497             :         }
    2498             : 
    2499          12 :         if (_dif_is_disabled(ctx->dif_type)) {
    2500           0 :                 return 0;
    2501             :         }
    2502             : 
    2503          12 :         if (!(ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK)) {
    2504           0 :                 return 0;
    2505             :         }
    2506             : 
    2507          84 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    2508          72 :                 rc = _dif_remap_ref_tag(&sgl, offset_blocks, ctx, err_blk, check_ref_tag);
    2509          72 :                 if (rc != 0) {
    2510           0 :                         return rc;
    2511             :                 }
    2512          72 :         }
    2513             : 
    2514          12 :         return 0;
    2515          12 : }
    2516             : 
    2517             : static int
    2518         200 : _dix_remap_ref_tag(struct _dif_sgl *md_sgl, uint32_t offset_blocks,
    2519             :                    const struct spdk_dif_ctx *ctx, struct spdk_dif_error *err_blk,
    2520             :                    bool check_ref_tag)
    2521             : {
    2522         200 :         uint64_t expected = 0, remapped;
    2523             :         uint8_t *md_buf;
    2524             :         struct spdk_dif *dif;
    2525             : 
    2526         200 :         _dif_sgl_get_buf(md_sgl, &md_buf, NULL);
    2527             : 
    2528         200 :         dif = (struct spdk_dif *)(md_buf + ctx->guard_interval);
    2529             : 
    2530         200 :         if (_dif_ignore(dif, ctx)) {
    2531           0 :                 goto end;
    2532             :         }
    2533             : 
    2534             :         /* For type 1 and 2, the Reference Tag is incremented for each
    2535             :          * subsequent logical block. For type 3, the Reference Tag
    2536             :          * remains the same as the initialReference Tag.
    2537             :          */
    2538         200 :         if (ctx->dif_type != SPDK_DIF_TYPE3) {
    2539         200 :                 expected = ctx->init_ref_tag + ctx->ref_tag_offset + offset_blocks;
    2540         200 :                 remapped = ctx->remapped_init_ref_tag + ctx->ref_tag_offset + offset_blocks;
    2541         200 :         } else {
    2542           0 :                 remapped = ctx->remapped_init_ref_tag;
    2543             :         }
    2544             : 
    2545             :         /* Verify the stored Reference Tag. */
    2546         200 :         if (check_ref_tag && !_dif_reftag_check(dif, ctx, expected, offset_blocks, err_blk)) {
    2547           0 :                 return -1;
    2548             :         }
    2549             : 
    2550             :         /* Update the stored Reference Tag to the remapped one. */
    2551         200 :         _dif_set_reftag(dif, remapped, ctx->dif_pi_format);
    2552             : 
    2553             : end:
    2554         200 :         _dif_sgl_advance(md_sgl, ctx->md_size);
    2555             : 
    2556         200 :         return 0;
    2557         200 : }
    2558             : 
    2559             : int
    2560          12 : spdk_dix_remap_ref_tag(struct iovec *md_iov, uint32_t num_blocks,
    2561             :                        const struct spdk_dif_ctx *ctx,
    2562             :                        struct spdk_dif_error *err_blk,
    2563             :                        bool check_ref_tag)
    2564             : {
    2565             :         struct _dif_sgl md_sgl;
    2566             :         uint32_t offset_blocks;
    2567             :         int rc;
    2568             : 
    2569          12 :         _dif_sgl_init(&md_sgl, md_iov, 1);
    2570             : 
    2571          12 :         if (!_dif_sgl_is_valid(&md_sgl, ctx->md_size * num_blocks)) {
    2572           0 :                 SPDK_ERRLOG("Size of metadata iovec array is not valid.\n");
    2573           0 :                 return -EINVAL;
    2574             :         }
    2575             : 
    2576          12 :         if (_dif_is_disabled(ctx->dif_type)) {
    2577           0 :                 return 0;
    2578             :         }
    2579             : 
    2580          12 :         if (!(ctx->dif_flags & SPDK_DIF_FLAGS_REFTAG_CHECK)) {
    2581           0 :                 return 0;
    2582             :         }
    2583             : 
    2584         212 :         for (offset_blocks = 0; offset_blocks < num_blocks; offset_blocks++) {
    2585         200 :                 rc = _dix_remap_ref_tag(&md_sgl, offset_blocks, ctx, err_blk, check_ref_tag);
    2586         200 :                 if (rc != 0) {
    2587           0 :                         return rc;
    2588             :                 }
    2589         200 :         }
    2590             : 
    2591          12 :         return 0;
    2592          12 : }

Generated by: LCOV version 1.15