xref: /spdk/test/unit/lib/blob/blob.c/esnap_dev.c (revision 60982c759db49b4f4579f16e3b24df0725ba4b94)
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 ut_memset4(void *dst, uint32_t pat, size_t len)
45 {
46 	uint32_t *vals = dst;
47 
48 	assert((len % 4) == 0);
49 	for (size_t i = 0; i < (len / 4); i++) {
50 		vals[i] = pat;
51 	}
52 }
53 
54 static void
55 ut_memset8(void *dst, uint64_t pat, size_t len)
56 {
57 	uint64_t *vals = dst;
58 
59 	assert((len % 8) == 0);
60 	for (size_t i = 0; i < (len / 8); i++) {
61 		vals[i] = pat;
62 	}
63 }
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 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 	memset(opts, 0, sizeof(*opts));
100 	opts->magic = UT_ESNAP_OPTS_MAGIC;
101 	opts->block_size = block_size;
102 	opts->num_blocks = num_blocks;
103 	opts->destroyed = destroyed;
104 	spdk_strcpy_pad(opts->name, name, sizeof(opts->name) - 1, '\0');
105 }
106 
107 static struct spdk_io_channel *
108 ut_esnap_create_channel(struct spdk_bs_dev *dev)
109 {
110 	struct spdk_io_channel *ch;
111 
112 	ch = spdk_get_io_channel(dev);
113 	if (ch == NULL) {
114 		return NULL;
115 	}
116 
117 	return ch;
118 }
119 
120 static void
121 ut_esnap_destroy_channel(struct spdk_bs_dev *dev, struct spdk_io_channel *channel)
122 {
123 	spdk_put_io_channel(channel);
124 }
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 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 	union ut_word	*words = buf;
143 	uint32_t	off, i, j, lba;
144 
145 	j = 0;
146 	for (off = start_byte; off < start_byte + buf_sz; off += esnap_blksz) {
147 		lba = off / esnap_blksz;
148 		for (i = 0; i < esnap_blksz / sizeof(*words); i++) {
149 			if (words[j].f.blob_id != id || words[j].f.lba != lba) {
150 				return false;
151 			}
152 			j++;
153 		}
154 	}
155 	return true;
156 }
157 
158 static void
159 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 	struct ut_esnap_dev	*ut_dev = (struct ut_esnap_dev *)bs_dev;
163 	struct ut_esnap_channel	*ut_ch = spdk_io_channel_get_ctx(channel);
164 	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 	CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
170 	CU_ASSERT(spdk_get_thread() == ut_ch->thread);
171 
172 	SPDK_CU_ASSERT_FATAL(sizeof(word) == 8);
173 	SPDK_CU_ASSERT_FATAL(lba + lba_count <= UINT32_MAX);
174 
175 	word.f.blob_id = ut_dev->blob_id & 0xffffffff;
176 	for (cur = 0; cur < lba_count; cur++) {
177 		word.f.lba = lba + cur;
178 		ut_memset8(payload + cur * block_size, word.num, block_size);
179 	}
180 	ut_ch->blocks_read += lba_count;
181 
182 	cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, 0);
183 }
184 
185 static void
186 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 	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 	CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
194 	CU_ASSERT(spdk_get_thread() == ut_ch->thread);
195 
196 	if (iovcnt != 1) {
197 		CU_ASSERT(false);
198 		cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, -ENOTSUP);
199 		return;
200 	}
201 	ut_esnap_read(bs_dev, channel, iov->iov_base, lba, lba_count, cb_args);
202 }
203 
204 static void
205 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 	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 	CU_ASSERT(&ut_ch->dev->bs_dev == bs_dev);
213 	CU_ASSERT(spdk_get_thread() == ut_ch->thread);
214 
215 	CU_ASSERT(false);
216 	cb_args->cb_fn(cb_args->channel, cb_args->cb_arg, -ENOTSUP);
217 }
218 
219 static bool
220 ut_esnap_is_zeroes(struct spdk_bs_dev *dev, uint64_t lba, uint64_t lba_count)
221 {
222 	return false;
223 }
224 
225 static int
226 ut_esnap_io_channel_create(void *io_device, void *ctx)
227 {
228 	struct ut_esnap_dev	*ut_dev = io_device;
229 	struct ut_esnap_channel	*ut_ch = ctx;
230 
231 	ut_ch->dev = ut_dev;
232 	ut_ch->thread = spdk_get_thread();
233 	ut_ch->blocks_read = 0;
234 
235 	ut_dev->num_channels++;
236 
237 	return 0;
238 }
239 
240 static void
241 ut_esnap_io_channel_destroy(void *io_device, void *ctx)
242 {
243 	struct ut_esnap_dev	*ut_dev = io_device;
244 	struct ut_esnap_channel	*ut_ch = ctx;
245 
246 	CU_ASSERT(ut_ch->thread == spdk_get_thread());
247 
248 	CU_ASSERT(ut_dev->num_channels > 0);
249 	ut_dev->num_channels--;
250 
251 	return;
252 }
253 
254 static void
255 ut_esnap_dev_free(void *io_device)
256 {
257 	struct ut_esnap_dev	*ut_dev = io_device;
258 
259 	if (ut_dev->ut_opts.destroyed != NULL) {
260 		*ut_dev->ut_opts.destroyed = true;
261 	}
262 
263 	CU_ASSERT(ut_dev->num_channels == 0);
264 
265 	ut_memset4(ut_dev, 0xdeadf1ea, sizeof(*ut_dev));
266 	free(ut_dev);
267 }
268 
269 static void
270 ut_esnap_destroy(struct spdk_bs_dev *bs_dev)
271 {
272 	spdk_io_device_unregister(bs_dev, ut_esnap_dev_free);
273 }
274 
275 static bool
276 ut_esnap_translate_lba(struct spdk_bs_dev *dev, uint64_t lba, uint64_t *base_lba)
277 {
278 	*base_lba = lba;
279 	return true;
280 }
281 
282 static struct spdk_bs_dev *
283 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 	assert(opts->magic == UT_ESNAP_OPTS_MAGIC);
289 
290 	ut_dev = calloc(1, sizeof(*ut_dev));
291 	if (ut_dev == NULL) {
292 		return NULL;
293 	}
294 
295 	ut_dev->ut_opts = *opts;
296 	bs_dev = &ut_dev->bs_dev;
297 
298 	bs_dev->blocklen = opts->block_size;
299 	bs_dev->blockcnt = opts->num_blocks;
300 
301 	bs_dev->create_channel = ut_esnap_create_channel;
302 	bs_dev->destroy_channel = ut_esnap_destroy_channel;
303 	bs_dev->destroy = ut_esnap_destroy;
304 	bs_dev->read = ut_esnap_read;
305 	bs_dev->readv = ut_esnap_readv;
306 	bs_dev->readv_ext = ut_esnap_readv_ext;
307 	bs_dev->is_zeroes = ut_esnap_is_zeroes;
308 	bs_dev->translate_lba = ut_esnap_translate_lba;
309 
310 	spdk_io_device_register(ut_dev, ut_esnap_io_channel_create, ut_esnap_io_channel_destroy,
311 				sizeof(struct ut_esnap_channel), opts->name);
312 
313 	return bs_dev;
314 }
315 
316 static int
317 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 	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 	CU_ASSERT(bs_ctx == NULL);
325 	CU_ASSERT(bs_ctx == NULL);
326 
327 	SPDK_CU_ASSERT_FATAL(id != NULL);
328 	SPDK_CU_ASSERT_FATAL(sizeof(struct ut_esnap_opts) == id_len);
329 
330 	bs_dev = ut_esnap_dev_alloc(id);
331 	SPDK_CU_ASSERT_FATAL(bs_dev != NULL);
332 
333 	*bs_devp = bs_dev;
334 	return 0;
335 }
336 
337 static int
338 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 	uint32_t *bs_ctx_count = bs_ctx;
342 	uint32_t *blob_ctx_count = blob_ctx;
343 
344 	SPDK_CU_ASSERT_FATAL(bs_ctx != NULL);
345 
346 	(*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 	if (blob_ctx_count != NULL) {
354 		(*blob_ctx_count)++;
355 	}
356 
357 	return ut_esnap_create(NULL, NULL, blob, id, id_len, bs_devp);
358 }
359 
360 static struct ut_esnap_channel *
361 ut_esnap_get_io_channel(struct spdk_io_channel *ch, spdk_blob_id blob_id)
362 {
363 	struct spdk_bs_channel	*bs_channel = spdk_io_channel_get_ctx(ch);
364 	struct blob_esnap_channel	find = {};
365 	struct blob_esnap_channel	*esnap_channel;
366 
367 	find.blob_id = blob_id;
368 	esnap_channel = RB_FIND(blob_esnap_channel_tree, &bs_channel->esnap_channels, &find);
369 	if (esnap_channel == NULL) {
370 		return NULL;
371 	}
372 
373 	return spdk_io_channel_get_ctx(esnap_channel->channel);
374 }
375