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: 130 146 89.0 %
Date: 2024-07-11 13:28:34 Functions: 17 19 89.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 32 48 66.7 %

           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                 :            :         }
      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                 :            :         }
      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                 :            :         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                 :            : }
     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                 :            :         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                 :            :                 }
     154                 :            :         }
     155                 :      23160 :         return true;
     156                 :            : }
     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                 :            :         union ut_word           word;
     166                 :            :         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                 :            :         }
     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                 :            : }
     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                 :            : }
     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                 :        240 :         return;
     252                 :            : }
     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                 :            :         }
     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                 :            :         struct ut_esnap_dev     *ut_dev;
     286                 :            :         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                 :            : }
     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                 :            : }
     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                 :            :         }
     356                 :            : 
     357                 :         72 :         return ut_esnap_create(NULL, NULL, blob, id, id_len, bs_devp);
     358                 :            : }
     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                 :            :         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                 :            : }

Generated by: LCOV version 1.14