LCOV - code coverage report
Current view: top level - lib/util - dif.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 1101 1204 91.4 %
Date: 2024-11-20 11:31:17 Functions: 95 96 99.0 %

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

Generated by: LCOV version 1.15