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

Generated by: LCOV version 1.15