LCOV - code coverage report
Current view: top level - lib/util - dif.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 1344 1462 91.9 %
Date: 2024-12-05 04:35:15 Functions: 105 108 97.2 %

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

Generated by: LCOV version 1.15