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

Generated by: LCOV version 1.15