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: 137 155 88.4 %
Date: 2024-07-15 10:41:47 Functions: 18 20 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 35 52 67.3 %

           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                 :        720 : ut_memset4(void *dst, uint32_t pat, size_t len)
      45                 :            : {
      46                 :        720 :         uint32_t *vals = dst;
      47                 :            : 
      48         [ -  + ]:        720 :         assert((len % 4) == 0);
      49         [ +  + ]:      42480 :         for (size_t i = 0; i < (len / 4); i++) {
      50                 :      41760 :                 vals[i] = pat;
      51                 :            :         }
      52                 :        720 : }
      53                 :            : 
      54                 :            : static void
      55                 :      63720 : ut_memset8(void *dst, uint64_t pat, size_t len)
      56                 :            : {
      57                 :      63720 :         uint64_t *vals = dst;
      58                 :            : 
      59         [ -  + ]:      63720 :         assert((len % 8) == 0);
      60         [ +  + ]:    6173930 :         for (size_t i = 0; i < (len / 8); i++) {
      61                 :    6110210 :                 vals[i] = pat;
      62                 :            :         }
      63                 :      63720 : }
      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                 :        456 : 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         [ -  + ]:        456 :         memset(opts, 0, sizeof(*opts));
     100                 :        456 :         opts->magic = UT_ESNAP_OPTS_MAGIC;
     101                 :        456 :         opts->block_size = block_size;
     102                 :        456 :         opts->num_blocks = num_blocks;
     103                 :        456 :         opts->destroyed = destroyed;
     104                 :        456 :         spdk_strcpy_pad(opts->name, name, sizeof(opts->name) - 1, '\0');
     105                 :        456 : }
     106                 :            : 
     107                 :            : static struct spdk_io_channel *
     108                 :        264 : ut_esnap_create_channel(struct spdk_bs_dev *dev)
     109                 :            : {
     110                 :            :         struct spdk_io_channel *ch;
     111                 :            : 
     112                 :        264 :         ch = spdk_get_io_channel(dev);
     113         [ -  + ]:        264 :         if (ch == NULL) {
     114                 :          0 :                 return NULL;
     115                 :            :         }
     116                 :            : 
     117                 :        264 :         return ch;
     118                 :            : }
     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                 :      41544 : 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                 :      41544 :         union ut_word   *words = buf;
     143                 :            :         uint32_t        off, i, j, lba;
     144                 :            : 
     145                 :      41544 :         j = 0;
     146         [ +  + ]:     113760 :         for (off = start_byte; off < start_byte + buf_sz; off += esnap_blksz) {
     147         [ -  + ]:      72216 :                 lba = off / esnap_blksz;
     148         [ +  + ]:    6898200 :                 for (i = 0; i < esnap_blksz / sizeof(*words); i++) {
     149   [ +  -  -  + ]:    6825980 :                         if (words[j].f.blob_id != id || words[j].f.lba != lba) {
     150                 :          0 :                                 return false;
     151                 :            :                         }
     152                 :    6825980 :                         j++;
     153                 :            :                 }
     154                 :            :         }
     155                 :      41544 :         return true;
     156                 :            : }
     157                 :            : 
     158                 :            : static void
     159                 :      13608 : 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                 :      13608 :         struct ut_esnap_dev     *ut_dev = (struct ut_esnap_dev *)bs_dev;
     163                 :      13608 :         struct ut_esnap_channel *ut_ch = spdk_io_channel_get_ctx(channel);
     164                 :      13608 :         const uint32_t          block_size = ut_dev->ut_opts.block_size;
     165                 :            :         union ut_word           word;
     166                 :            :         uint64_t                cur;
     167                 :            : 
     168                 :            :         /* The channel passed in must be associated with this bs_dev. */
     169                 :      13608 :         CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
     170                 :      13608 :         CU_ASSERT(spdk_get_thread() == ut_ch->thread);
     171                 :            : 
     172         [ -  + ]:      13608 :         SPDK_CU_ASSERT_FATAL(sizeof(word) == 8);
     173         [ -  + ]:      13608 :         SPDK_CU_ASSERT_FATAL(lba + lba_count <= UINT32_MAX);
     174                 :            : 
     175                 :      13608 :         word.f.blob_id = ut_dev->blob_id & 0xffffffff;
     176         [ +  + ]:      67344 :         for (cur = 0; cur < lba_count; cur++) {
     177                 :      53736 :                 word.f.lba = lba + cur;
     178                 :      53736 :                 ut_memset8(payload + cur * block_size, word.num, block_size);
     179                 :            :         }
     180                 :      13608 :         ut_ch->blocks_read += lba_count;
     181                 :            : 
     182                 :      13608 :         cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, 0);
     183                 :      13608 : }
     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                 :            : }
     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                 :        576 : ut_esnap_is_zeroes(struct spdk_bs_dev *dev, uint64_t lba, uint64_t lba_count)
     221                 :            : {
     222                 :        576 :         return false;
     223                 :            : }
     224                 :            : 
     225                 :            : static bool
     226                 :        672 : ut_esnap_is_range_valid(struct spdk_bs_dev *dev, uint64_t lba, uint64_t lba_count)
     227                 :            : {
     228                 :        672 :         struct ut_esnap_dev     *ut_dev = (struct ut_esnap_dev *)dev;
     229                 :            : 
     230         [ +  + ]:        672 :         if (lba >= ut_dev->ut_opts.num_blocks) {
     231                 :         96 :                 return false;
     232         [ -  + ]:        576 :         } else if (lba + lba_count > ut_dev->ut_opts.num_blocks) {
     233                 :            :                 /* bdevs used for esnaps must currently be an exact multiple of the
     234                 :            :                  * blobstore cluster size (see spdk_lvol_create_esnap_clone()), but if that
     235                 :            :                  * ever changes this code here needs to be updated to account for it. */
     236                 :          0 :                 SPDK_ERRLOG("Entire range must be within the bs_dev bounds for CoW.\n"
     237                 :            :                             "lba(lba_count): %lu(%lu), num_blks: %lu\n", lba, lba_count, ut_dev->ut_opts.num_blocks);
     238                 :          0 :                 assert(false);
     239                 :            :                 return false;
     240                 :            :         }
     241                 :            : 
     242                 :        576 :         return true;
     243                 :            : }
     244                 :            : 
     245                 :            : static int
     246                 :        264 : ut_esnap_io_channel_create(void *io_device, void *ctx)
     247                 :            : {
     248                 :        264 :         struct ut_esnap_dev     *ut_dev = io_device;
     249                 :        264 :         struct ut_esnap_channel *ut_ch = ctx;
     250                 :            : 
     251                 :        264 :         ut_ch->dev = ut_dev;
     252                 :        264 :         ut_ch->thread = spdk_get_thread();
     253                 :        264 :         ut_ch->blocks_read = 0;
     254                 :            : 
     255                 :        264 :         ut_dev->num_channels++;
     256                 :            : 
     257                 :        264 :         return 0;
     258                 :            : }
     259                 :            : 
     260                 :            : static void
     261                 :        264 : ut_esnap_io_channel_destroy(void *io_device, void *ctx)
     262                 :            : {
     263                 :        264 :         struct ut_esnap_dev     *ut_dev = io_device;
     264                 :        264 :         struct ut_esnap_channel *ut_ch = ctx;
     265                 :            : 
     266                 :        264 :         CU_ASSERT(ut_ch->thread == spdk_get_thread());
     267                 :            : 
     268                 :        264 :         CU_ASSERT(ut_dev->num_channels > 0);
     269                 :        264 :         ut_dev->num_channels--;
     270                 :            : 
     271                 :        264 :         return;
     272                 :            : }
     273                 :            : 
     274                 :            : static void
     275                 :        720 : ut_esnap_dev_free(void *io_device)
     276                 :            : {
     277                 :        720 :         struct ut_esnap_dev     *ut_dev = io_device;
     278                 :            : 
     279         [ +  + ]:        720 :         if (ut_dev->ut_opts.destroyed != NULL) {
     280                 :        192 :                 *ut_dev->ut_opts.destroyed = true;
     281                 :            :         }
     282                 :            : 
     283                 :        720 :         CU_ASSERT(ut_dev->num_channels == 0);
     284                 :            : 
     285                 :        720 :         ut_memset4(ut_dev, 0xdeadf1ea, sizeof(*ut_dev));
     286                 :        720 :         free(ut_dev);
     287                 :        720 : }
     288                 :            : 
     289                 :            : static void
     290                 :        720 : ut_esnap_destroy(struct spdk_bs_dev *bs_dev)
     291                 :            : {
     292                 :        720 :         spdk_io_device_unregister(bs_dev, ut_esnap_dev_free);
     293                 :        720 : }
     294                 :            : 
     295                 :            : static bool
     296                 :          0 : ut_esnap_translate_lba(struct spdk_bs_dev *dev, uint64_t lba, uint64_t *base_lba)
     297                 :            : {
     298                 :          0 :         *base_lba = lba;
     299                 :          0 :         return true;
     300                 :            : }
     301                 :            : 
     302                 :            : static struct spdk_bs_dev *
     303                 :        720 : ut_esnap_dev_alloc(const struct ut_esnap_opts *opts)
     304                 :            : {
     305                 :            :         struct ut_esnap_dev     *ut_dev;
     306                 :            :         struct spdk_bs_dev      *bs_dev;
     307                 :            : 
     308         [ -  + ]:        720 :         assert(opts->magic == UT_ESNAP_OPTS_MAGIC);
     309                 :            : 
     310                 :        720 :         ut_dev = calloc(1, sizeof(*ut_dev));
     311         [ -  + ]:        720 :         if (ut_dev == NULL) {
     312                 :          0 :                 return NULL;
     313                 :            :         }
     314                 :            : 
     315                 :        720 :         ut_dev->ut_opts = *opts;
     316                 :        720 :         bs_dev = &ut_dev->bs_dev;
     317                 :            : 
     318                 :        720 :         bs_dev->blocklen = opts->block_size;
     319                 :        720 :         bs_dev->blockcnt = opts->num_blocks;
     320                 :            : 
     321                 :        720 :         bs_dev->create_channel = ut_esnap_create_channel;
     322                 :        720 :         bs_dev->destroy_channel = ut_esnap_destroy_channel;
     323                 :        720 :         bs_dev->destroy = ut_esnap_destroy;
     324                 :        720 :         bs_dev->read = ut_esnap_read;
     325                 :        720 :         bs_dev->readv = ut_esnap_readv;
     326                 :        720 :         bs_dev->readv_ext = ut_esnap_readv_ext;
     327                 :        720 :         bs_dev->is_zeroes = ut_esnap_is_zeroes;
     328                 :        720 :         bs_dev->is_range_valid = ut_esnap_is_range_valid;
     329                 :        720 :         bs_dev->translate_lba = ut_esnap_translate_lba;
     330                 :            : 
     331                 :        720 :         spdk_io_device_register(ut_dev, ut_esnap_io_channel_create, ut_esnap_io_channel_destroy,
     332                 :        720 :                                 sizeof(struct ut_esnap_channel), opts->name);
     333                 :            : 
     334                 :        720 :         return bs_dev;
     335                 :            : }
     336                 :            : 
     337                 :            : static int
     338                 :        672 : ut_esnap_create(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                 :        672 :         struct spdk_bs_dev      *bs_dev = NULL;
     342                 :            : 
     343                 :            :         /* With any blobstore that will use bs_ctx or blob_ctx, wrap this function and pass NULL as
     344                 :            :          * bs_ctx and blob_ctx. */
     345                 :        672 :         CU_ASSERT(bs_ctx == NULL);
     346                 :        672 :         CU_ASSERT(bs_ctx == NULL);
     347                 :            : 
     348         [ -  + ]:        672 :         SPDK_CU_ASSERT_FATAL(id != NULL);
     349         [ -  + ]:        672 :         SPDK_CU_ASSERT_FATAL(sizeof(struct ut_esnap_opts) == id_len);
     350                 :            : 
     351                 :        672 :         bs_dev = ut_esnap_dev_alloc(id);
     352         [ -  + ]:        672 :         SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
     353                 :            : 
     354                 :        672 :         *bs_devp = bs_dev;
     355                 :        672 :         return 0;
     356                 :            : }
     357                 :            : 
     358                 :            : static int
     359                 :         72 : ut_esnap_create_with_count(void *bs_ctx, void *blob_ctx, struct spdk_blob *blob,
     360                 :            :                            const void *id, uint32_t id_len, struct spdk_bs_dev **bs_devp)
     361                 :            : {
     362                 :         72 :         uint32_t *bs_ctx_count = bs_ctx;
     363                 :         72 :         uint32_t *blob_ctx_count = blob_ctx;
     364                 :            : 
     365         [ -  + ]:         72 :         SPDK_CU_ASSERT_FATAL(bs_ctx != NULL);
     366                 :            : 
     367                 :         72 :         (*bs_ctx_count)++;
     368                 :            : 
     369                 :            :         /*
     370                 :            :          * blob_ctx can be non-NULL when spdk_bs_open_blob() is used. Opens that come via
     371                 :            :          * spdk_bs_load(), spdk_bs_open_blob(), and those that come via spdk_bs_open_blob_ext() with
     372                 :            :          * NULL opts->esnap_ctx will have blob_ctx == NULL.
     373                 :            :          */
     374         [ +  + ]:         72 :         if (blob_ctx_count != NULL) {
     375                 :         24 :                 (*blob_ctx_count)++;
     376                 :            :         }
     377                 :            : 
     378                 :         72 :         return ut_esnap_create(NULL, NULL, blob, id, id_len, bs_devp);
     379                 :            : }
     380                 :            : 
     381                 :            : static struct ut_esnap_channel *
     382                 :        120 : ut_esnap_get_io_channel(struct spdk_io_channel *ch, spdk_blob_id blob_id)
     383                 :            : {
     384                 :        120 :         struct spdk_bs_channel  *bs_channel = spdk_io_channel_get_ctx(ch);
     385                 :        120 :         struct blob_esnap_channel       find = {};
     386                 :            :         struct blob_esnap_channel       *esnap_channel;
     387                 :            : 
     388                 :        120 :         find.blob_id = blob_id;
     389                 :        120 :         esnap_channel = RB_FIND(blob_esnap_channel_tree, &bs_channel->esnap_channels, &find);
     390         [ +  + ]:        120 :         if (esnap_channel == NULL) {
     391                 :         72 :                 return NULL;
     392                 :            :         }
     393                 :            : 
     394                 :         48 :         return spdk_io_channel_get_ctx(esnap_channel->channel);
     395                 :            : }

Generated by: LCOV version 1.14