LCOV - code coverage report
Current view: top level - spdk/test/unit/lib/blob/blob.c - esnap_dev.c (source / functions) Hit Total Coverage
Test: Combined Lines: 153 169 90.5 %
Date: 2024-11-20 16:36:23 Functions: 17 19 89.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 158 330 47.9 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
       3                 :            :  */
       4                 :            : #include "spdk/stdinc.h"
       5                 :            : 
       6                 :            : #include "spdk_internal/cunit.h"
       7                 :            : #include "spdk/blob.h"
       8                 :            : 
       9                 :            : /*
      10                 :            :  * This creates a bs_dev that does not depend on a bdev. Typical use without assertions looks like:
      11                 :            :  *
      12                 :            :  *      struct spdk_bs_dev      *dev;
      13                 :            :  *      struct spdk_bs_opts     bs_opts;
      14                 :            :  *      struct spdk_blob_opts   blob_opts;
      15                 :            :  *      struct ut_snap_opts     esnap_opts;
      16                 :            :  *      struct spdk_io_channel  *bs_chan;
      17                 :            :  *      bool                    destroyed = false;
      18                 :            :  *
      19                 :            :  *   Create the blobstore with external snapshot support.
      20                 :            :  *      dev = init_dev();
      21                 :            :  *      memset(g_dev_buffer, 0, DEV_BUFFER_SIZE);
      22                 :            :  *      spdk_bs_opts_init(&bs_opts, sizeof(bs_opts));
      23                 :            :  *      bs_opts.esnap_bs_dev_create = ut_esnap_create;
      24                 :            :  *
      25                 :            :  *   Create an esnap clone blob.
      26                 :            :  *      ut_esnap_opts_init(512, 2048, "name", &destroyed, &esnap_opts);
      27                 :            :  *      blob_opts.esnap_id = &esnap_opts;
      28                 :            :  *      blob_opts.esnap_id_len = sizeof(esnap_opts);
      29                 :            :  *      opts.num_clusters = 4;
      30                 :            :  *      blob = ut_blob_create_and_open(bs, &opts);
      31                 :            :  *
      32                 :            :  *   Do stuff like you would with any other blob.
      33                 :            :  *      bs_chan = spdk_bs_alloc_io_channel(bs);
      34                 :            :  *      ...
      35                 :            :  *
      36                 :            :  *   You can check the value of destroyed to verify that spdk_blob_close() led to the
      37                 :            :  *   destruction of the bs_dev created during spdk_blob_open().
      38                 :            :  *      spdk_blob_close(blob, blob_op_complete, NULL);
      39                 :            :  *      poll_threads();
      40                 :            :  *      CU_ASSERT(destroyed);
      41                 :            :  */
      42                 :            : 
      43                 :            : static void
      44                 :        576 : ut_memset4(void *dst, uint32_t pat, size_t len)
      45                 :            : {
      46                 :        576 :         uint32_t *vals = dst;
      47                 :            : 
      48   [ +  +  +  -  :        576 :         assert((len % 4) == 0);
                   #  # ]
      49   [ +  +  +  + ]:      32832 :         for (size_t i = 0; i < (len / 4); i++) {
      50   [ +  -  +  - ]:      32256 :                 vals[i] = pat;
      51                 :       5376 :         }
      52                 :        576 : }
      53                 :            : 
      54                 :            : static void
      55                 :      51264 : ut_memset8(void *dst, uint64_t pat, size_t len)
      56                 :            : {
      57                 :      51264 :         uint64_t *vals = dst;
      58                 :            : 
      59   [ +  +  +  -  :      51264 :         assert((len % 8) == 0);
                   #  # ]
      60   [ +  +  +  + ]:    5364288 :         for (size_t i = 0; i < (len / 8); i++) {
      61   [ +  -  +  - ]:    5313024 :                 vals[i] = pat;
      62                 :     885504 :         }
      63                 :      51264 : }
      64                 :            : 
      65                 :            : #define UT_ESNAP_OPTS_MAGIC     0xbadf1ea5
      66                 :            : struct ut_esnap_opts {
      67                 :            :         /*
      68                 :            :          * This structure gets stored in an xattr. The magic number is used to give some assurance
      69                 :            :          * that we got the right thing before trying to use the other fields.
      70                 :            :          */
      71                 :            :         uint32_t        magic;
      72                 :            :         uint32_t        block_size;
      73                 :            :         uint64_t        num_blocks;
      74                 :            :         /*
      75                 :            :          * If non-NULL, referenced address will be set to true when the device is fully destroyed.
      76                 :            :          * This address must remain valid for the life of the blob, even across blobstore reload.
      77                 :            :          */
      78                 :            :         bool            *destroyed;
      79                 :            :         char            name[32];
      80                 :            : };
      81                 :            : 
      82                 :            : struct ut_esnap_dev {
      83                 :            :         struct spdk_bs_dev      bs_dev;
      84                 :            :         struct ut_esnap_opts    ut_opts;
      85                 :            :         spdk_blob_id            blob_id;
      86                 :            :         uint32_t                num_channels;
      87                 :            : };
      88                 :            : 
      89                 :            : struct ut_esnap_channel {
      90                 :            :         struct ut_esnap_dev     *dev;
      91                 :            :         struct spdk_thread      *thread;
      92                 :            :         uint64_t                blocks_read;
      93                 :            : };
      94                 :            : 
      95                 :            : static void
      96                 :        336 : ut_esnap_opts_init(uint32_t block_size, uint32_t num_blocks, const char *name, bool *destroyed,
      97                 :            :                    struct ut_esnap_opts *opts)
      98                 :            : {
      99         [ +  + ]:        336 :         memset(opts, 0, sizeof(*opts));
     100   [ +  -  +  - ]:        336 :         opts->magic = UT_ESNAP_OPTS_MAGIC;
     101   [ +  -  +  - ]:        336 :         opts->block_size = block_size;
     102   [ +  -  +  - ]:        336 :         opts->num_blocks = num_blocks;
     103   [ +  -  +  - ]:        336 :         opts->destroyed = destroyed;
     104         [ +  - ]:        336 :         spdk_strcpy_pad(opts->name, name, sizeof(opts->name) - 1, '\0');
     105                 :        336 : }
     106                 :            : 
     107                 :            : static struct spdk_io_channel *
     108                 :        240 : ut_esnap_create_channel(struct spdk_bs_dev *dev)
     109                 :            : {
     110                 :         40 :         struct spdk_io_channel *ch;
     111                 :            : 
     112                 :        240 :         ch = spdk_get_io_channel(dev);
     113         [ -  + ]:        240 :         if (ch == NULL) {
     114                 :          0 :                 return NULL;
     115                 :            :         }
     116                 :            : 
     117                 :        240 :         return ch;
     118                 :         40 : }
     119                 :            : 
     120                 :            : static void
     121                 :         72 : ut_esnap_destroy_channel(struct spdk_bs_dev *dev, struct spdk_io_channel *channel)
     122                 :            : {
     123                 :         72 :         spdk_put_io_channel(channel);
     124                 :         72 : }
     125                 :            : 
     126                 :            : /*
     127                 :            :  * When reading, each block is filled with 64-bit values made up of the least significant 32 bits of
     128                 :            :  * the blob ID and the lba.
     129                 :            :  */
     130                 :            : union ut_word {
     131                 :            :         uint64_t        num;
     132                 :            :         struct {
     133                 :            :                 uint32_t        blob_id;
     134                 :            :                 uint32_t        lba;
     135                 :            :         } f;
     136                 :            : };
     137                 :            : 
     138                 :            : static bool
     139                 :      23160 : ut_esnap_content_is_correct(void *buf, uint32_t buf_sz, uint32_t id,
     140                 :            :                             uint32_t start_byte, uint32_t esnap_blksz)
     141                 :            : {
     142                 :      23160 :         union ut_word   *words = buf;
     143                 :       3860 :         uint32_t        off, i, j, lba;
     144                 :            : 
     145                 :      23160 :         j = 0;
     146         [ +  + ]:      76992 :         for (off = start_byte; off < start_byte + buf_sz; off += esnap_blksz) {
     147         [ -  + ]:      53832 :                 lba = off / esnap_blksz;
     148   [ +  +  +  + ]:    5703240 :                 for (i = 0; i < esnap_blksz / sizeof(*words); i++) {
     149   [ +  -  +  +  :    5649408 :                         if (words[j].f.blob_id != id || words[j].f.lba != lba) {
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  -  
                      + ]
     150                 :          0 :                                 return false;
     151                 :            :                         }
     152                 :    5649408 :                         j++;
     153                 :     941568 :                 }
     154                 :       8972 :         }
     155                 :      23160 :         return true;
     156                 :       3860 : }
     157                 :            : 
     158                 :            : static void
     159                 :      13248 : ut_esnap_read(struct spdk_bs_dev *bs_dev, struct spdk_io_channel *channel, void *payload,
     160                 :            :               uint64_t lba, uint32_t lba_count, struct spdk_bs_dev_cb_args *cb_args)
     161                 :            : {
     162                 :      13248 :         struct ut_esnap_dev     *ut_dev = (struct ut_esnap_dev *)bs_dev;
     163                 :      13248 :         struct ut_esnap_channel *ut_ch = spdk_io_channel_get_ctx(channel);
     164   [ +  -  +  -  :      13248 :         const uint32_t          block_size = ut_dev->ut_opts.block_size;
                   +  - ]
     165                 :       2208 :         union ut_word           word;
     166                 :       2208 :         uint64_t                cur;
     167                 :            : 
     168                 :            :         /* The channel passed in must be associated with this bs_dev. */
     169   [ +  -  +  -  :      13248 :         CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
                   +  - ]
     170   [ +  -  +  - ]:      13248 :         CU_ASSERT(spdk_get_thread() == ut_ch->thread);
     171                 :            : 
     172   [ +  +  #  # ]:      13248 :         SPDK_CU_ASSERT_FATAL(sizeof(word) == 8);
     173   [ +  +  #  # ]:      13248 :         SPDK_CU_ASSERT_FATAL(lba + lba_count <= UINT32_MAX);
     174                 :            : 
     175   [ +  -  +  - ]:      13248 :         word.f.blob_id = ut_dev->blob_id & 0xffffffff;
     176         [ +  + ]:      60672 :         for (cur = 0; cur < lba_count; cur++) {
     177         [ +  - ]:      47424 :                 word.f.lba = lba + cur;
     178                 :      47424 :                 ut_memset8(payload + cur * block_size, word.num, block_size);
     179                 :       7904 :         }
     180   [ +  -  +  - ]:      13248 :         ut_ch->blocks_read += lba_count;
     181                 :            : 
     182   [ +  -  +  -  :      13248 :         cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, 0);
          -  +  +  -  +  
          -  +  -  +  -  
                   +  - ]
     183                 :      13248 : }
     184                 :            : 
     185                 :            : static void
     186                 :       8256 : ut_esnap_readv(struct spdk_bs_dev *bs_dev, struct spdk_io_channel *channel,
     187                 :            :                struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count,
     188                 :            :                struct spdk_bs_dev_cb_args *cb_args)
     189                 :            : {
     190                 :       8256 :         struct ut_esnap_channel *ut_ch = spdk_io_channel_get_ctx(channel);
     191                 :            : 
     192                 :            :         /* The channel passed in must be associated with this bs_dev. */
     193   [ +  -  +  -  :       8256 :         CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
                   +  - ]
     194   [ +  -  +  - ]:       8256 :         CU_ASSERT(spdk_get_thread() == ut_ch->thread);
     195                 :            : 
     196         [ -  + ]:       8256 :         if (iovcnt != 1) {
     197                 :          0 :                 CU_ASSERT(false);
     198   [ #  #  #  #  :          0 :                 cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, -ENOTSUP);
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     199                 :          0 :                 return;
     200                 :            :         }
     201   [ +  -  +  - ]:       8256 :         ut_esnap_read(bs_dev, channel, iov->iov_base, lba, lba_count, cb_args);
     202         [ -  + ]:       1376 : }
     203                 :            : 
     204                 :            : static void
     205                 :          0 : ut_esnap_readv_ext(struct spdk_bs_dev *bs_dev, struct spdk_io_channel *channel,
     206                 :            :                    struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count,
     207                 :            :                    struct spdk_bs_dev_cb_args *cb_args, struct spdk_blob_ext_io_opts *io_opts)
     208                 :            : {
     209                 :          0 :         struct ut_esnap_channel *ut_ch = spdk_io_channel_get_ctx(channel);
     210                 :            : 
     211                 :            :         /* The channel passed in must be associated with this bs_dev. */
     212   [ #  #  #  #  :          0 :         CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
                   #  # ]
     213   [ #  #  #  # ]:          0 :         CU_ASSERT(spdk_get_thread() == ut_ch->thread);
     214                 :            : 
     215                 :          0 :         CU_ASSERT(false);
     216   [ #  #  #  #  :          0 :         cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, -ENOTSUP);
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     217                 :          0 : }
     218                 :            : 
     219                 :            : static bool
     220                 :        480 : ut_esnap_is_zeroes(struct spdk_bs_dev *dev, uint64_t lba, uint64_t lba_count)
     221                 :            : {
     222                 :        480 :         return false;
     223                 :            : }
     224                 :            : 
     225                 :            : static int
     226                 :        240 : ut_esnap_io_channel_create(void *io_device, void *ctx)
     227                 :            : {
     228                 :        240 :         struct ut_esnap_dev     *ut_dev = io_device;
     229                 :        240 :         struct ut_esnap_channel *ut_ch = ctx;
     230                 :            : 
     231   [ +  -  +  - ]:        240 :         ut_ch->dev = ut_dev;
     232   [ +  -  +  - ]:        240 :         ut_ch->thread = spdk_get_thread();
     233   [ +  -  +  - ]:        240 :         ut_ch->blocks_read = 0;
     234                 :            : 
     235         [ +  - ]:        240 :         ut_dev->num_channels++;
     236                 :            : 
     237                 :        240 :         return 0;
     238                 :         40 : }
     239                 :            : 
     240                 :            : static void
     241                 :        240 : ut_esnap_io_channel_destroy(void *io_device, void *ctx)
     242                 :            : {
     243                 :        240 :         struct ut_esnap_dev     *ut_dev = io_device;
     244                 :        240 :         struct ut_esnap_channel *ut_ch = ctx;
     245                 :            : 
     246   [ +  -  +  - ]:        240 :         CU_ASSERT(ut_ch->thread == spdk_get_thread());
     247                 :            : 
     248   [ +  -  +  - ]:        240 :         CU_ASSERT(ut_dev->num_channels > 0);
     249         [ +  - ]:        240 :         ut_dev->num_channels--;
     250                 :            : 
     251                 :        200 :         return;
     252                 :         40 : }
     253                 :            : 
     254                 :            : static void
     255                 :        576 : ut_esnap_dev_free(void *io_device)
     256                 :            : {
     257                 :        576 :         struct ut_esnap_dev     *ut_dev = io_device;
     258                 :            : 
     259   [ +  +  +  -  :        576 :         if (ut_dev->ut_opts.destroyed != NULL) {
             +  -  +  + ]
     260   [ +  -  +  -  :        192 :                 *ut_dev->ut_opts.destroyed = true;
             +  -  +  - ]
     261                 :         32 :         }
     262                 :            : 
     263   [ +  -  +  - ]:        576 :         CU_ASSERT(ut_dev->num_channels == 0);
     264                 :            : 
     265                 :        576 :         ut_memset4(ut_dev, 0xdeadf1ea, sizeof(*ut_dev));
     266                 :        576 :         free(ut_dev);
     267                 :        576 : }
     268                 :            : 
     269                 :            : static void
     270                 :        576 : ut_esnap_destroy(struct spdk_bs_dev *bs_dev)
     271                 :            : {
     272                 :        576 :         spdk_io_device_unregister(bs_dev, ut_esnap_dev_free);
     273                 :        576 : }
     274                 :            : 
     275                 :            : static bool
     276                 :          0 : ut_esnap_translate_lba(struct spdk_bs_dev *dev, uint64_t lba, uint64_t *base_lba)
     277                 :            : {
     278         [ #  # ]:          0 :         *base_lba = lba;
     279                 :          0 :         return true;
     280                 :            : }
     281                 :            : 
     282                 :            : static struct spdk_bs_dev *
     283                 :        576 : ut_esnap_dev_alloc(const struct ut_esnap_opts *opts)
     284                 :            : {
     285                 :         96 :         struct ut_esnap_dev     *ut_dev;
     286                 :         96 :         struct spdk_bs_dev      *bs_dev;
     287                 :            : 
     288   [ +  +  +  -  :        576 :         assert(opts->magic == UT_ESNAP_OPTS_MAGIC);
             +  -  #  # ]
     289                 :            : 
     290                 :        576 :         ut_dev = calloc(1, sizeof(*ut_dev));
     291         [ +  + ]:        576 :         if (ut_dev == NULL) {
     292                 :          0 :                 return NULL;
     293                 :            :         }
     294                 :            : 
     295         [ +  - ]:        576 :         ut_dev->ut_opts = *opts;
     296         [ +  - ]:        576 :         bs_dev = &ut_dev->bs_dev;
     297                 :            : 
     298   [ +  -  +  -  :        576 :         bs_dev->blocklen = opts->block_size;
             +  -  +  - ]
     299   [ +  -  +  -  :        576 :         bs_dev->blockcnt = opts->num_blocks;
             +  -  +  - ]
     300                 :            : 
     301   [ +  -  +  - ]:        576 :         bs_dev->create_channel = ut_esnap_create_channel;
     302   [ +  -  +  - ]:        576 :         bs_dev->destroy_channel = ut_esnap_destroy_channel;
     303   [ +  -  +  - ]:        576 :         bs_dev->destroy = ut_esnap_destroy;
     304   [ +  -  +  - ]:        576 :         bs_dev->read = ut_esnap_read;
     305   [ +  -  +  - ]:        576 :         bs_dev->readv = ut_esnap_readv;
     306   [ +  -  +  - ]:        576 :         bs_dev->readv_ext = ut_esnap_readv_ext;
     307   [ +  -  +  - ]:        576 :         bs_dev->is_zeroes = ut_esnap_is_zeroes;
     308   [ +  -  +  - ]:        576 :         bs_dev->translate_lba = ut_esnap_translate_lba;
     309                 :            : 
     310                 :        576 :         spdk_io_device_register(ut_dev, ut_esnap_io_channel_create, ut_esnap_io_channel_destroy,
     311         [ +  - ]:        576 :                                 sizeof(struct ut_esnap_channel), opts->name);
     312                 :            : 
     313                 :        576 :         return bs_dev;
     314                 :         96 : }
     315                 :            : 
     316                 :            : static int
     317                 :        528 : ut_esnap_create(void *bs_ctx, void *blob_ctx, struct spdk_blob *blob,
     318                 :            :                 const void *id, uint32_t id_len, struct spdk_bs_dev **bs_devp)
     319                 :            : {
     320                 :        528 :         struct spdk_bs_dev      *bs_dev = NULL;
     321                 :            : 
     322                 :            :         /* With any blobstore that will use bs_ctx or blob_ctx, wrap this function and pass NULL as
     323                 :            :          * bs_ctx and blob_ctx. */
     324                 :        528 :         CU_ASSERT(bs_ctx == NULL);
     325                 :        528 :         CU_ASSERT(bs_ctx == NULL);
     326                 :            : 
     327   [ +  +  #  # ]:        528 :         SPDK_CU_ASSERT_FATAL(id != NULL);
     328   [ +  +  #  # ]:        528 :         SPDK_CU_ASSERT_FATAL(sizeof(struct ut_esnap_opts) == id_len);
     329                 :            : 
     330                 :        528 :         bs_dev = ut_esnap_dev_alloc(id);
     331   [ +  +  #  # ]:        528 :         SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
     332                 :            : 
     333         [ +  - ]:        528 :         *bs_devp = bs_dev;
     334                 :        528 :         return 0;
     335                 :         88 : }
     336                 :            : 
     337                 :            : static int
     338                 :         72 : ut_esnap_create_with_count(void *bs_ctx, void *blob_ctx, struct spdk_blob *blob,
     339                 :            :                            const void *id, uint32_t id_len, struct spdk_bs_dev **bs_devp)
     340                 :            : {
     341                 :         72 :         uint32_t *bs_ctx_count = bs_ctx;
     342                 :         72 :         uint32_t *blob_ctx_count = blob_ctx;
     343                 :            : 
     344   [ +  +  #  # ]:         72 :         SPDK_CU_ASSERT_FATAL(bs_ctx != NULL);
     345                 :            : 
     346                 :         72 :         (*bs_ctx_count)++;
     347                 :            : 
     348                 :            :         /*
     349                 :            :          * blob_ctx can be non-NULL when spdk_bs_open_blob() is used. Opens that come via
     350                 :            :          * spdk_bs_load(), spdk_bs_open_blob(), and those that come via spdk_bs_open_blob_ext() with
     351                 :            :          * NULL opts->esnap_ctx will have blob_ctx == NULL.
     352                 :            :          */
     353         [ +  + ]:         72 :         if (blob_ctx_count != NULL) {
     354                 :         24 :                 (*blob_ctx_count)++;
     355                 :          4 :         }
     356                 :            : 
     357                 :         84 :         return ut_esnap_create(NULL, NULL, blob, id, id_len, bs_devp);
     358                 :         12 : }
     359                 :            : 
     360                 :            : static struct ut_esnap_channel *
     361                 :        120 : ut_esnap_get_io_channel(struct spdk_io_channel *ch, spdk_blob_id blob_id)
     362                 :            : {
     363                 :        120 :         struct spdk_bs_channel  *bs_channel = spdk_io_channel_get_ctx(ch);
     364                 :        120 :         struct blob_esnap_channel       find = {};
     365                 :         20 :         struct blob_esnap_channel       *esnap_channel;
     366                 :            : 
     367         [ +  - ]:        120 :         find.blob_id = blob_id;
     368         [ +  - ]:        120 :         esnap_channel = RB_FIND(blob_esnap_channel_tree, &bs_channel->esnap_channels, &find);
     369         [ +  + ]:        120 :         if (esnap_channel == NULL) {
     370                 :         72 :                 return NULL;
     371                 :            :         }
     372                 :            : 
     373   [ +  -  +  - ]:         48 :         return spdk_io_channel_get_ctx(esnap_channel->channel);
     374                 :         20 : }

Generated by: LCOV version 1.15