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