LCOV - code coverage report
Current view: top level - lib/util - dif.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 997 1099 90.7 %
Date: 2024-11-05 10:06:02 Functions: 93 94 98.9 %

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

Generated by: LCOV version 1.15