LCOV - code coverage report
Current view: top level - lib/util - dif.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 964 1058 91.1 %
Date: 2024-08-12 04:47:02 Functions: 84 84 100.0 %

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

Generated by: LCOV version 1.15