Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2018 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "queue_internal.h"
10 : :
11 : : #include "spdk/reduce.h"
12 : : #include "spdk/env.h"
13 : : #include "spdk/string.h"
14 : : #include "spdk/bit_array.h"
15 : : #include "spdk/util.h"
16 : : #include "spdk/log.h"
17 : : #include "spdk/memory.h"
18 : : #include "spdk/tree.h"
19 : : #include "spdk/queue.h"
20 : :
21 : : #include "libpmem.h"
22 : :
23 : : /* Always round up the size of the PM region to the nearest cacheline. */
24 : : #define REDUCE_PM_SIZE_ALIGNMENT 64
25 : :
26 : : /* Offset into the backing device where the persistent memory file's path is stored. */
27 : : #define REDUCE_BACKING_DEV_PATH_OFFSET 4096
28 : :
29 : : #define REDUCE_EMPTY_MAP_ENTRY -1ULL
30 : :
31 : : #define REDUCE_NUM_VOL_REQUESTS 256
32 : :
33 : : /* Structure written to offset 0 of both the pm file and the backing device. */
34 : : struct spdk_reduce_vol_superblock {
35 : : uint8_t signature[8];
36 : : struct spdk_reduce_vol_params params;
37 : : uint8_t reserved[3983];
38 : : };
39 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_reduce_vol_superblock) == 4096, "size incorrect");
40 : :
41 : : #define SPDK_REDUCE_SIGNATURE "SPDKREDU"
42 : : /* null terminator counts one */
43 : : SPDK_STATIC_ASSERT(sizeof(SPDK_REDUCE_SIGNATURE) - 1 ==
44 : : SPDK_SIZEOF_MEMBER(struct spdk_reduce_vol_superblock, signature), "size incorrect");
45 : :
46 : : #define REDUCE_PATH_MAX 4096
47 : :
48 : : #define REDUCE_ZERO_BUF_SIZE 0x100000
49 : :
50 : : /* REDUCE_META_CACHE_BLOCK_NUM should not less than 2*REDUCE_NUM_VOL_REQUESTS */
51 : : #define REDUCE_META_CACHE_BLOCK_NUM (4 * REDUCE_NUM_VOL_REQUESTS)
52 : : #define REDUCE_META_CACHE_RECLAIM_RATIO 20
53 : : #define REDUCE_META_INIT_BUF_SIZE (256 << 10) /* 256KB */
54 : :
55 : : /**
56 : : * Describes a persistent memory file used to hold metadata associated with a
57 : : * compressed volume.
58 : : */
59 : : struct spdk_reduce_pm_file {
60 : : char path[REDUCE_PATH_MAX];
61 : : void *pm_buf;
62 : : int pm_is_pmem;
63 : : uint64_t size;
64 : : };
65 : :
66 : : #define REDUCE_IO_READV 1
67 : : #define REDUCE_IO_WRITEV 2
68 : : #define REDUCE_IO_UNMAP 3
69 : :
70 : : struct spdk_reduce_chunk_map {
71 : : uint32_t compressed_size;
72 : : uint32_t reserved;
73 : : uint64_t io_unit_index[0];
74 : : };
75 : :
76 : : struct spdk_reduce_vol_request {
77 : : /**
78 : : * Scratch buffer used for uncompressed chunk. This is used for:
79 : : * 1) source buffer for compression operations
80 : : * 2) destination buffer for decompression operations
81 : : * 3) data buffer when writing uncompressed chunk to disk
82 : : * 4) data buffer when reading uncompressed chunk from disk
83 : : */
84 : : uint8_t *decomp_buf;
85 : : struct iovec *decomp_buf_iov;
86 : :
87 : : /**
88 : : * These are used to construct the iovecs that are sent to
89 : : * the decomp engine, they point to a mix of the scratch buffer
90 : : * and user buffer
91 : : */
92 : : struct iovec decomp_iov[REDUCE_MAX_IOVECS + 2];
93 : : int decomp_iovcnt;
94 : :
95 : : /**
96 : : * Scratch buffer used for compressed chunk. This is used for:
97 : : * 1) destination buffer for compression operations
98 : : * 2) source buffer for decompression operations
99 : : * 3) data buffer when writing compressed chunk to disk
100 : : * 4) data buffer when reading compressed chunk from disk
101 : : */
102 : : uint8_t *comp_buf;
103 : : struct iovec *comp_buf_iov;
104 : : struct iovec *iov;
105 : : bool rmw;
106 : : struct spdk_reduce_vol *vol;
107 : : int type;
108 : : int reduce_errno;
109 : : int iovcnt;
110 : : int num_backing_ops;
111 : : uint32_t num_io_units;
112 : : struct spdk_reduce_backing_io *backing_io;
113 : : bool chunk_is_compressed;
114 : : bool copy_after_decompress;
115 : : uint64_t offset;
116 : : uint64_t logical_map_index;
117 : : uint64_t length;
118 : : uint64_t chunk_map_index;
119 : : /* record the old chunkmap idx when rmw */
120 : : uint64_t read_chunk_map_index;
121 : : struct spdk_reduce_chunk_map *chunk;
122 : : /* save the new chunkinfo to the pre-allocated chunk mem,
123 : : * then memcpy from here to the metablock which used to submit backend
124 : : */
125 : : struct spdk_reduce_chunk_map *prealloc_chunk;
126 : : spdk_reduce_vol_op_complete cb_fn;
127 : : void *cb_arg;
128 : : TAILQ_ENTRY(spdk_reduce_vol_request) tailq;
129 : : RB_ENTRY(spdk_reduce_vol_request) rbnode;
130 : : struct spdk_reduce_vol_cb_args backing_cb_args;
131 : : };
132 : :
133 : : struct spdk_reduce_vol {
134 : : struct spdk_reduce_vol_params params;
135 : : struct spdk_reduce_vol_info info;
136 : : uint32_t backing_io_units_per_chunk;
137 : : uint32_t backing_lba_per_io_unit;
138 : : uint32_t logical_blocks_per_chunk;
139 : : struct spdk_reduce_pm_file pm_file;
140 : : struct spdk_reduce_backing_dev *backing_dev;
141 : : struct spdk_reduce_vol_superblock *backing_super;
142 : : struct spdk_reduce_vol_superblock *pm_super;
143 : : uint64_t *pm_logical_map;
144 : : uint64_t *pm_chunk_maps;
145 : :
146 : : struct spdk_bit_array *allocated_chunk_maps;
147 : : /* The starting position when looking for a block from allocated_chunk_maps */
148 : : uint64_t find_chunk_offset;
149 : : /* Cache free chunks to speed up lookup of free chunk. */
150 : : struct reduce_queue free_chunks_queue;
151 : : struct spdk_bit_array *allocated_backing_io_units;
152 : : /* The starting position when looking for a block from allocated_backing_io_units */
153 : : uint64_t find_block_offset;
154 : : /* Cache free blocks for backing bdev to speed up lookup of free backing blocks. */
155 : : struct reduce_queue free_backing_blocks_queue;
156 : :
157 : : struct spdk_reduce_vol_request *request_mem;
158 : : TAILQ_HEAD(, spdk_reduce_vol_request) free_requests;
159 : : RB_HEAD(executing_req_tree, spdk_reduce_vol_request) executing_requests;
160 : : TAILQ_HEAD(, spdk_reduce_vol_request) queued_requests;
161 : :
162 : : /* Single contiguous buffer used for all request buffers for this volume. */
163 : : uint8_t *buf_mem;
164 : : struct iovec *buf_iov_mem;
165 : : /* Single contiguous buffer used for backing io buffers for this volume. */
166 : : uint8_t *buf_backing_io_mem;
167 : :
168 : : /* the count of meta cache block we prepare */
169 : : uint32_t metablocks_num;
170 : : /* A pointer of many 4k DMA mem from huge page, It has metablocks_num * 4K Bytes */
171 : : uint8_t *metablock_buf;
172 : : /* A pointer of many mblk describtion struct mem. The count of these struct is equal to metablocks num */
173 : : struct reduce_metablock_extent *metablock_extent_mem;
174 : : TAILQ_HEAD(, reduce_metablock_extent) metablocks_free;
175 : : RB_HEAD(metablock_cache_tree, reduce_metablock_extent) metablocks_caching;
176 : : TAILQ_HEAD(, reduce_metablock_extent) metablocks_lru;
177 : :
178 : : /* use independent mem for meta opetation backingio */
179 : : struct reduce_meta_request *meta_request_buf;
180 : : TAILQ_HEAD(, reduce_meta_request) free_meta_request;
181 : : };
182 : :
183 : : struct reduce_metablock_extent {
184 : : uint32_t mblk_sn;
185 : : /* pointer to 4K mem */
186 : : struct iovec iov;
187 : : /* the node to attach free list or lru list */
188 : : TAILQ_ENTRY(reduce_metablock_extent) tailq;
189 : : /* the node to attach read/write tree */
190 : : RB_ENTRY(reduce_metablock_extent) rbnode;
191 : :
192 : : /* the write_meta_req cnt that this one metadata write operation catching */
193 : : uint32_t batch_write_cnt;
194 : : /* the refcnt should be both pending_to_supply_read and pending_to_write elem count */
195 : : uint32_t refcnt;
196 : : /* when multi io to access metablock, callback them by the tailq */
197 : : TAILQ_HEAD(, reduce_meta_request) pending_to_supply_read;
198 : : TAILQ_HEAD(, reduce_meta_request) pending_to_write;
199 : : };
200 : :
201 : :
202 : : /* We need 2 iovs during load - one for the superblock, another for the path */
203 : : #define LOAD_IOV_COUNT 2
204 : :
205 : : struct reduce_mblk_init_ctx {
206 : : /* unit: bytes */
207 : : void *meta_init_buf;
208 : : size_t buf_len;
209 : : /* unit: backing blocklen */
210 : : uint64_t off_need_init;
211 : : uint64_t len_need_init;
212 : : uint64_t off_need_init_next;
213 : : uint32_t maxlen_per_io;
214 : : };
215 : :
216 : : struct reduce_init_load_ctx {
217 : : struct spdk_reduce_vol *vol;
218 : : struct spdk_reduce_vol_cb_args backing_cb_args;
219 : : spdk_reduce_vol_op_with_handle_complete cb_fn;
220 : : void *cb_arg;
221 : : struct iovec iov[LOAD_IOV_COUNT];
222 : : void *path;
223 : : struct spdk_reduce_backing_io *backing_io;
224 : : struct reduce_mblk_init_ctx mblk_init_ctx;
225 : : };
226 : :
227 : : struct meta_builtin_readv_ctx {
228 : : struct spdk_reduce_vol *vol;
229 : : struct iovec *iov;
230 : : int iovcnt;
231 : : uint64_t offset;
232 : : uint64_t length;
233 : :
234 : : spdk_reduce_vol_op_complete cb_fn;
235 : : void *cb_arg;
236 : : };
237 : :
238 : : typedef void (*reduce_request_fn)(void *_req, int reduce_errno);
239 : :
240 : : struct meta_builtin_read_chunk_ctx {
241 : : struct spdk_reduce_vol *vol;
242 : : struct spdk_reduce_vol_request *req;
243 : : reduce_request_fn next_fn;
244 : : };
245 : :
246 : : struct meta_builtin_request_ctx {
247 : : struct spdk_reduce_vol_request *req;
248 : : void *logicalmap_meta_req;
249 : : };
250 : :
251 : : struct meta_builtin_load_ctx {
252 : : struct reduce_init_load_ctx *load_ctx;
253 : : };
254 : :
255 : : /* meta read/write operations are async, should save the context before meta opetations */
256 : : union reduce_meta_restore_ctx_container {
257 : : struct meta_builtin_readv_ctx readv_ctx;
258 : : struct meta_builtin_read_chunk_ctx read_chunk_ctx;
259 : : struct meta_builtin_request_ctx req_ctx;
260 : : struct meta_builtin_load_ctx load_ctx;
261 : : };
262 : :
263 : : struct reduce_meta_request {
264 : : /* the node to attach free_meta_backingio or pending list of struct reduce_metablock_extent */
265 : : TAILQ_ENTRY(reduce_meta_request) tailq;
266 : :
267 : : struct spdk_reduce_vol *vol;
268 : : /* if need or may need to update meta, set the is_write flag */
269 : : bool is_write;
270 : : /* when we want to update meta, should do read-modify-write about the metablock.
271 : : * 1. set the supply_read flag, and do read.
272 : : * 2. read callback, modify the elem on metablock, and reset the supply_read flag. send do write.
273 : : * 3. write callback, we can judge the meta stage according the supply_read flag.
274 : : */
275 : : bool supply_read;
276 : : enum spdk_reduce_meta_type mtype;
277 : : uint32_t mblk_sn;
278 : : uint32_t elem_sn_on_mblk;
279 : : /* A pointer to caching meta block */
280 : : struct reduce_metablock_extent *mblk_extent;
281 : : union reduce_meta_restore_ctx_container restore_ctx;
282 : :
283 : : struct spdk_reduce_vol_cb_args cb_args;
284 : : struct spdk_reduce_backing_io backing_io;
285 : : };
286 : :
287 : : static void _start_readv_request(struct spdk_reduce_vol_request *req);
288 : : static void _start_writev_request(struct spdk_reduce_vol_request *req);
289 : : static uint8_t *g_zero_buf;
290 : : static int g_vol_count = 0;
291 : :
292 : : /*
293 : : * Allocate extra metadata chunks and corresponding backing io units to account for
294 : : * outstanding IO in worst case scenario where logical map is completely allocated
295 : : * and no data can be compressed. We need extra chunks in this case to handle
296 : : * in-flight writes since reduce never writes data in place.
297 : : */
298 : : #define REDUCE_NUM_EXTRA_CHUNKS 128
299 : :
300 : : static void
301 : 769964 : _reduce_persist(struct spdk_reduce_vol *vol, const void *addr, size_t len)
302 : : {
303 [ - + ]: 769964 : if (vol->pm_file.pm_is_pmem) {
304 : 0 : pmem_persist(addr, len);
305 : : } else {
306 : 769964 : pmem_msync(addr, len);
307 : : }
308 : 769964 : }
309 : :
310 : : static uint64_t
311 : 24 : _get_pm_logical_map_size(uint64_t vol_size, uint64_t chunk_size)
312 : : {
313 : : uint64_t chunks_in_logical_map, logical_map_size;
314 : :
315 [ - + ]: 24 : chunks_in_logical_map = vol_size / chunk_size;
316 : 24 : logical_map_size = chunks_in_logical_map * sizeof(uint64_t);
317 : :
318 : : /* Round up to next cacheline. */
319 : 24 : return spdk_divide_round_up(logical_map_size, REDUCE_PM_SIZE_ALIGNMENT) *
320 : : REDUCE_PM_SIZE_ALIGNMENT;
321 : : }
322 : :
323 : : static uint64_t
324 : 1257856 : _get_total_chunks(uint64_t vol_size, uint64_t chunk_size)
325 : : {
326 : : uint64_t num_chunks;
327 : :
328 [ - + ]: 1257856 : num_chunks = vol_size / chunk_size;
329 : 1257856 : num_chunks += REDUCE_NUM_EXTRA_CHUNKS;
330 : :
331 : 1257856 : return num_chunks;
332 : : }
333 : :
334 : : static inline uint32_t
335 : 2133710 : _reduce_vol_get_chunk_struct_size(uint64_t backing_io_units_per_chunk)
336 : : {
337 : 2133710 : return sizeof(struct spdk_reduce_chunk_map) + sizeof(uint64_t) * backing_io_units_per_chunk;
338 : : }
339 : :
340 : : static uint64_t
341 : 12 : _get_pm_total_chunks_size(uint64_t vol_size, uint64_t chunk_size, uint64_t backing_io_unit_size)
342 : : {
343 : : uint64_t io_units_per_chunk, num_chunks, total_chunks_size;
344 : :
345 : 12 : num_chunks = _get_total_chunks(vol_size, chunk_size);
346 [ - + ]: 12 : io_units_per_chunk = chunk_size / backing_io_unit_size;
347 : :
348 : 12 : total_chunks_size = num_chunks * _reduce_vol_get_chunk_struct_size(io_units_per_chunk);
349 : :
350 : 12 : return spdk_divide_round_up(total_chunks_size, REDUCE_PM_SIZE_ALIGNMENT) *
351 : : REDUCE_PM_SIZE_ALIGNMENT;
352 : : }
353 : :
354 : : static struct spdk_reduce_chunk_map *
355 : 1257822 : _reduce_vol_get_chunk_map(struct spdk_reduce_vol *vol, uint64_t chunk_map_index)
356 : : {
357 : : uintptr_t chunk_map_addr;
358 : :
359 [ - + ]: 1257822 : assert(chunk_map_index < _get_total_chunks(vol->params.vol_size, vol->params.chunk_size));
360 : :
361 : 1257822 : chunk_map_addr = (uintptr_t)vol->pm_chunk_maps;
362 : 1257822 : chunk_map_addr += chunk_map_index *
363 : 1257822 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
364 : :
365 : 1257822 : return (struct spdk_reduce_chunk_map *)chunk_map_addr;
366 : : }
367 : :
368 : : static int
369 : 22 : _validate_vol_params(struct spdk_reduce_vol_params *params)
370 : : {
371 [ - + ]: 22 : if (params->vol_size > 0) {
372 : : /**
373 : : * User does not pass in the vol size - it gets calculated by libreduce from
374 : : * values in this structure plus the size of the backing device.
375 : : */
376 : 0 : return -EINVAL;
377 : : }
378 : :
379 [ + - + - ]: 22 : if (params->chunk_size == 0 || params->backing_io_unit_size == 0 ||
380 [ - + ]: 22 : params->logical_block_size == 0) {
381 : 0 : return -EINVAL;
382 : : }
383 : :
384 : : /* Chunk size must be an even multiple of the backing io unit size. */
385 [ - + - + ]: 22 : if ((params->chunk_size % params->backing_io_unit_size) != 0) {
386 : 0 : return -EINVAL;
387 : : }
388 : :
389 : : /* Chunk size must be an even multiple of the logical block size. */
390 [ - + - + ]: 22 : if ((params->chunk_size % params->logical_block_size) != 0) {
391 : 0 : return -1;
392 : : }
393 : :
394 : 22 : return 0;
395 : : }
396 : :
397 : : static uint64_t
398 : 22 : _get_vol_size(uint64_t chunk_size, uint64_t backing_dev_size)
399 : : {
400 : : uint64_t num_chunks;
401 : :
402 [ - + ]: 22 : num_chunks = backing_dev_size / chunk_size;
403 [ - + ]: 22 : if (num_chunks <= REDUCE_NUM_EXTRA_CHUNKS) {
404 : 0 : return 0;
405 : : }
406 : :
407 : 22 : num_chunks -= REDUCE_NUM_EXTRA_CHUNKS;
408 : 22 : return num_chunks * chunk_size;
409 : : }
410 : :
411 : : static uint64_t
412 : 12 : _get_pm_file_size(struct spdk_reduce_vol_params *params)
413 : : {
414 : : uint64_t total_pm_size;
415 : :
416 : 12 : total_pm_size = sizeof(struct spdk_reduce_vol_superblock);
417 : 12 : total_pm_size += _get_pm_logical_map_size(params->vol_size, params->chunk_size);
418 : 12 : total_pm_size += _get_pm_total_chunks_size(params->vol_size, params->chunk_size,
419 : 12 : params->backing_io_unit_size);
420 : 12 : return total_pm_size;
421 : : }
422 : :
423 : : const struct spdk_uuid *
424 : 0 : spdk_reduce_vol_get_uuid(struct spdk_reduce_vol *vol)
425 : : {
426 : 0 : return &vol->params.uuid;
427 : : }
428 : :
429 : : static uint32_t
430 : 1423438 : _meta_get_mblksn(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype,
431 : : uint64_t elem_sn)
432 : : {
433 : : uint32_t mblksn;
434 : :
435 : 1423438 : mblksn = params->meta_region_desc[mtype].offset + elem_sn /
436 [ - + ]: 1423438 : params->meta_region_desc[mtype].elems_per_mblk;
437 [ - + ]: 1423438 : assert(mblksn < params->meta_region_desc[mtype].offset + params->meta_region_desc[mtype].length);
438 : :
439 : 1423438 : return mblksn;
440 : : }
441 : :
442 : : static inline uint32_t
443 : 1423438 : _meta_get_elem_sn_on_mblk(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype,
444 : : uint64_t elem_sn)
445 : : {
446 [ - + ]: 1423438 : return elem_sn % params->meta_region_desc[mtype].elems_per_mblk;
447 : : }
448 : :
449 : : static inline void *
450 : 1423438 : _reduce_get_meta_elem_addr(struct reduce_meta_request *meta_req)
451 : : {
452 : 2846876 : return meta_req->mblk_extent->iov.iov_base + meta_req->elem_sn_on_mblk *
453 : 1423438 : meta_req->vol->params.meta_region_desc[meta_req->mtype].size_per_elem;
454 : : }
455 : :
456 : : static void
457 : 12 : _initialize_vol_pm_pointers(struct spdk_reduce_vol *vol)
458 : : {
459 : : uint64_t logical_map_size;
460 : :
461 : : /* Superblock is at the beginning of the pm file. */
462 : 12 : vol->pm_super = (struct spdk_reduce_vol_superblock *)vol->pm_file.pm_buf;
463 : :
464 : : /* Logical map immediately follows the super block. */
465 : 12 : vol->pm_logical_map = (uint64_t *)(vol->pm_super + 1);
466 : :
467 : : /* Chunks maps follow the logical map. */
468 : 12 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size, vol->params.chunk_size);
469 : 12 : vol->pm_chunk_maps = (uint64_t *)((uint8_t *)vol->pm_logical_map + logical_map_size);
470 : 12 : }
471 : :
472 : :
473 : : static inline bool
474 : 15272 : _addr_crosses_huge_page(const void *addr, size_t *size)
475 : : {
476 : : size_t _size;
477 : : uint64_t rc;
478 : :
479 [ - + ]: 15272 : assert(size);
480 : :
481 : 15272 : _size = *size;
482 : 15272 : rc = spdk_vtophys(addr, size);
483 : :
484 [ + - - + ]: 15272 : return rc == SPDK_VTOPHYS_ERROR || _size != *size;
485 : : }
486 : :
487 : : static inline int
488 : 11264 : _set_buffer(uint8_t **vol_buffer, uint8_t **_addr, uint8_t *addr_range, size_t buffer_size)
489 : : {
490 : : uint8_t *addr;
491 : 11264 : size_t size_tmp = buffer_size;
492 : :
493 : 11264 : addr = *_addr;
494 : :
495 : : /* Verify that addr + buffer_size doesn't cross huge page boundary */
496 [ - + ]: 11264 : if (_addr_crosses_huge_page(addr, &size_tmp)) {
497 : : /* Memory start is aligned on 2MiB, so buffer should be located at the end of the page.
498 : : * Skip remaining bytes and continue from the beginning of the next page */
499 : 0 : addr += size_tmp;
500 : : }
501 : :
502 [ - + ]: 11264 : if (addr + buffer_size > addr_range) {
503 : 0 : SPDK_ERRLOG("Vol buffer %p out of range %p\n", addr, addr_range);
504 : 0 : return -ERANGE;
505 : : }
506 : :
507 : 11264 : *vol_buffer = addr;
508 : 11264 : *_addr = addr + buffer_size;
509 : :
510 : 11264 : return 0;
511 : : }
512 : :
513 : : static inline uint64_t
514 : 394041 : _reduce_meta_backingio_get_lba(struct reduce_meta_request *meta_req)
515 : : {
516 [ - + ]: 394041 : return meta_req->mblk_sn * REDUCE_META_BLKSZ / meta_req->vol->backing_dev->blocklen;
517 : : }
518 : :
519 : : static inline uint64_t
520 : 394041 : _reduce_meta_backingio_get_lba_count(struct reduce_meta_request *meta_req)
521 : : {
522 [ - + ]: 394041 : return REDUCE_META_BLKSZ / meta_req->vol->backing_dev->blocklen;
523 : : }
524 : :
525 : : static void
526 : 394041 : _reduce_meta_block_rw_backing(struct reduce_meta_request *meta_req,
527 : : enum spdk_reduce_backing_io_type io_type)
528 : : {
529 : 394041 : struct spdk_reduce_vol *vol = meta_req->vol;
530 : 394041 : struct spdk_reduce_backing_io *backing_io = &meta_req->backing_io;
531 : :
532 : 394041 : backing_io->dev = vol->backing_dev;
533 : 394041 : backing_io->iov = &meta_req->mblk_extent->iov;
534 : 394041 : backing_io->iovcnt = 1;
535 : 394041 : backing_io->lba = _reduce_meta_backingio_get_lba(meta_req);
536 : 394041 : backing_io->lba_count = _reduce_meta_backingio_get_lba_count(meta_req);
537 : 394041 : backing_io->backing_io_type = io_type;
538 : 394041 : vol->backing_dev->submit_backing_io(backing_io);
539 : 394041 : }
540 : :
541 : : static int
542 : 6294010 : metablock_cmp(struct reduce_metablock_extent *mblk1, struct reduce_metablock_extent *mblk2)
543 : : {
544 [ + + ]: 10588555 : return (mblk1->mblk_sn < mblk2->mblk_sn ? -1 : mblk1->mblk_sn >
545 : 4294545 : mblk2->mblk_sn);
546 : : }
547 [ + + + + : 6295892 : RB_GENERATE_STATIC(metablock_cache_tree, reduce_metablock_extent, rbnode, metablock_cmp);
+ + + + +
+ + + + -
- + - + -
+ - + - +
+ + + + -
+ - - - -
- - - - -
- + + + +
+ + + + #
# # # # #
# # # # #
# # # ]
548 : :
549 : : static inline void
550 : 1423438 : _metablock_lru_update(struct spdk_reduce_vol *vol, struct reduce_metablock_extent *mblkext,
551 : : bool new_add)
552 : : {
553 [ + + ]: 1423438 : if (!new_add) {
554 [ + + ]: 1423043 : TAILQ_REMOVE(&vol->metablocks_lru, mblkext, tailq);
555 : : }
556 : 1423438 : TAILQ_INSERT_TAIL(&vol->metablocks_lru, mblkext, tailq);
557 : 1423438 : }
558 : :
559 : : static inline void
560 : 0 : _metablock_lru_delete(struct spdk_reduce_vol *vol, struct reduce_metablock_extent *mblkext)
561 : : {
562 : 0 : RB_REMOVE(metablock_cache_tree, &vol->metablocks_caching, mblkext);
563 [ # # ]: 0 : TAILQ_REMOVE(&vol->metablocks_lru, mblkext, tailq);
564 : 0 : TAILQ_INSERT_TAIL(&vol->metablocks_free, mblkext, tailq);
565 : 0 : }
566 : :
567 : : /* When the read process is complete, call this function to trigger
568 : : * the callback function registered in the metadata request on pending_to_supply_read.
569 : : * There will be metadata write requests and metadata read requests in the queue.
570 : : *
571 : : * When reading requests on pending_to_supply_read are callbacked, meta_req is released after the callback is completed.
572 : : * When the write request on pending_to_supply_read is callbacked, the metadata cache block data will be updated
573 : : * and the write request will be moved to the pending_to_write queue.
574 : : *
575 : : * After pending_to_supply_read is empty, if pending_to_write is not empty, one metadata write is triggered,
576 : : * and the metadata in multiple write requests is modified and solidify at one time.
577 : : */
578 : : static void
579 : 1423438 : _reduce_metablock_cache_read_done_update(struct reduce_meta_request *meta_req, int32_t error)
580 : : {
581 : 1423438 : struct reduce_metablock_extent *mblkext = meta_req->mblk_extent;
582 : : struct reduce_meta_request *meta_req_next, *meta_req_write;
583 : 1423438 : struct spdk_reduce_vol *vol = meta_req->vol;
584 : :
585 [ + + ]: 1423438 : TAILQ_REMOVE(&mblkext->pending_to_supply_read, meta_req, tailq);
586 [ - + + + : 1423438 : if (meta_req->is_write && !meta_req->supply_read) {
- + + + ]
587 : 606192 : TAILQ_INSERT_TAIL(&mblkext->pending_to_write, meta_req, tailq);
588 : 606192 : mblkext->batch_write_cnt++;
589 : : } else {
590 : 817246 : mblkext->refcnt--;
591 : 817246 : TAILQ_INSERT_TAIL(&vol->free_meta_request, meta_req, tailq);
592 : : }
593 : :
594 : : /* check whether collected all modify region */
595 : 1423438 : meta_req_next = TAILQ_FIRST(&mblkext->pending_to_supply_read);
596 [ + + ]: 1423438 : if (meta_req_next) {
597 [ - + - + ]: 676665 : SPDK_INFOLOG(reduce,
598 : : "metareq:%p mblkext %p mblksn %u ref %u. triger next readreq cpcb %p, error %d\n",
599 : : meta_req, mblkext,
600 : : mblkext->mblk_sn, mblkext->refcnt, meta_req_next, error);
601 [ - + ]: 676665 : assert(mblkext->refcnt);
602 : 676665 : meta_req_next->backing_io.backing_cb_args->cb_fn(meta_req_next->backing_io.backing_cb_args->cb_arg,
603 : : error);
604 : : } else {
605 : : /* we have callback all supply read metareq, start write to backend */
606 : 746773 : meta_req_write = TAILQ_FIRST(&mblkext->pending_to_write);
607 [ + + ]: 746773 : if (meta_req_write) {
608 [ - + - + ]: 393646 : SPDK_INFOLOG(reduce,
609 : : "metareq:%p mblkext %p mblksn %u ref %u. collect write req cnt %u, triger writereq do write %p\n",
610 : : meta_req, mblkext, mblkext->mblk_sn, mblkext->refcnt, mblkext->batch_write_cnt, meta_req_write);
611 [ - + ]: 393646 : assert(mblkext->refcnt);
612 [ - + ]: 393646 : assert(mblkext->batch_write_cnt);
613 [ - + ]: 393646 : assert(error == 0);
614 : 393646 : _reduce_meta_block_rw_backing(meta_req_write, SPDK_REDUCE_BACKING_IO_WRITE);
615 : : } else {
616 [ - + ]: 353127 : if (error != 0) {
617 : : /* This cache block has experienced abnormal reads or writes. Release it */
618 [ # # ]: 0 : assert(mblkext->refcnt == 0);
619 [ # # ]: 0 : assert(mblkext->batch_write_cnt == 0);
620 : 0 : _metablock_lru_delete(vol, mblkext);
621 : : }
622 : : }
623 : : }
624 : 1423438 : }
625 : :
626 : : /* After meta block has written done, callback meta_req in pending_to_write one by one.
627 : : * Of course, during the process of metadata asynchronous disks, there will appear metadata read and write requests,
628 : : * but they will be in pending_to_supply_read.
629 : : * Because the metadata has just been written to backend, the metadata cache block in memory is the same as that on backend,
630 : : * and the requests on the queue can be called back one by one.
631 : : */
632 : : static void
633 : 606192 : _reduce_metablock_cache_write_done_update(struct reduce_meta_request *meta_req, int32_t error)
634 : : {
635 : 606192 : struct reduce_metablock_extent *mblkext = meta_req->mblk_extent;
636 : : struct reduce_meta_request *meta_req_next;
637 : 606192 : struct spdk_reduce_vol *vol = meta_req->vol;
638 : :
639 : 606192 : mblkext->refcnt--;
640 : 606192 : mblkext->batch_write_cnt--;
641 : :
642 [ + + ]: 606192 : TAILQ_REMOVE(&mblkext->pending_to_write, meta_req, tailq);
643 : 606192 : TAILQ_INSERT_TAIL(&vol->free_meta_request, meta_req, tailq);
644 : :
645 : : /* trigger next */
646 : 606192 : meta_req_next = TAILQ_FIRST(&mblkext->pending_to_write);
647 [ + + ]: 606192 : if (meta_req_next) {
648 [ - + - + ]: 212546 : SPDK_INFOLOG(reduce,
649 : : "metareq:%p mblkext %p mblksn %u ref %u. write done to triger next writereq cpcb %p, error %d\n",
650 : : meta_req, mblkext,
651 : : mblkext->mblk_sn, mblkext->refcnt, meta_req_next, error);
652 [ - + ]: 212546 : assert(mblkext->refcnt);
653 [ - + ]: 212546 : assert(mblkext->batch_write_cnt);
654 : 212546 : meta_req_next->backing_io.backing_cb_args->cb_fn(meta_req_next->backing_io.backing_cb_args->cb_arg,
655 : : error);
656 : : } else {
657 : : /* when we async write to backend, there maybe new read come in, callback them */
658 [ - + ]: 393646 : assert(mblkext->batch_write_cnt == 0);
659 : 393646 : meta_req_next = TAILQ_FIRST(&mblkext->pending_to_supply_read);
660 [ + + ]: 393646 : if (meta_req_next) {
661 [ - + - + ]: 307984 : SPDK_INFOLOG(reduce,
662 : : "metareq:%p mblkext %p mblksn %u ref %u. triger next readreq cpcb %p, error %d\n",
663 : : meta_req, mblkext,
664 : : mblkext->mblk_sn, mblkext->refcnt, meta_req_next, error);
665 [ - + ]: 307984 : assert(mblkext->refcnt);
666 : 307984 : meta_req_next->backing_io.backing_cb_args->cb_fn(meta_req_next->backing_io.backing_cb_args->cb_arg,
667 : : error);
668 : : } else {
669 [ - + ]: 85662 : if (error != 0) {
670 : : /* This cache block has experienced abnormal reads or writes. Release it */
671 [ # # ]: 0 : assert(mblkext->refcnt == 0);
672 [ # # ]: 0 : assert(mblkext->batch_write_cnt == 0);
673 : 0 : _metablock_lru_delete(vol, mblkext);
674 : : }
675 : : }
676 : : }
677 : 606192 : }
678 : :
679 : : static uint32_t
680 : 0 : _metablock_lru_reclaim_batch(struct spdk_reduce_vol *vol)
681 : : {
682 : : struct reduce_metablock_extent *mblkext, *mblkext_next;
683 : 0 : uint32_t reclaim_num_expected = vol->metablocks_num * REDUCE_META_CACHE_RECLAIM_RATIO / 100;
684 : 0 : uint32_t reclaim_count = 0;
685 : :
686 [ # # ]: 0 : TAILQ_FOREACH_SAFE(mblkext, &vol->metablocks_lru, tailq, mblkext_next) {
687 [ # # # # ]: 0 : if (mblkext->refcnt == 0 && reclaim_count < reclaim_num_expected) {
688 [ # # ]: 0 : assert(TAILQ_EMPTY(&mblkext->pending_to_supply_read));
689 [ # # ]: 0 : assert(TAILQ_EMPTY(&mblkext->pending_to_write));
690 : 0 : _metablock_lru_delete(vol, mblkext);
691 : 0 : reclaim_count++;
692 : : } else {
693 : : break;
694 : : }
695 : : }
696 [ # # # # ]: 0 : SPDK_INFOLOG(reduce, "reclaim count %u\n", reclaim_count);
697 : :
698 : 0 : return reclaim_count;
699 : : }
700 : :
701 : : /* The entry of meta block operation */
702 : : static void
703 : 1423438 : _reduce_metablock_cache_read(struct reduce_meta_request *meta_req)
704 : : {
705 : 1423438 : struct reduce_metablock_extent *mblkext_res = NULL;
706 : : struct reduce_metablock_extent mblkext_find;
707 : 1423438 : struct spdk_reduce_vol *vol = meta_req->vol;
708 : :
709 : 1423438 : mblkext_find.mblk_sn = meta_req->mblk_sn;
710 : 1423438 : mblkext_res = RB_FIND(metablock_cache_tree, &vol->metablocks_caching, &mblkext_find);
711 : :
712 [ + + ]: 1423438 : if (mblkext_res) {
713 : 1423043 : meta_req->mblk_extent = mblkext_res;
714 : 1423043 : TAILQ_INSERT_TAIL(&mblkext_res->pending_to_supply_read, meta_req, tailq);
715 : 1423043 : mblkext_res->refcnt++;
716 : 1423043 : _metablock_lru_update(vol, mblkext_res, false);
717 [ + + ]: 1423043 : if (mblkext_res->refcnt == 1) {
718 : : /* metablock in cache, just sync cb */
719 [ - + - + ]: 438394 : SPDK_INFOLOG(reduce, "metareq %p mblkext %p mblksn %u ref %u. direct get from cache.\n", meta_req,
720 : : mblkext_res,
721 : : mblkext_res->mblk_sn, mblkext_res->refcnt);
722 : 438394 : meta_req->backing_io.backing_cb_args->cb_fn(meta_req->backing_io.backing_cb_args->cb_arg, 0);
723 : : } else {
724 : : /* pending wait cb */
725 [ - + - + ]: 984649 : SPDK_INFOLOG(reduce, "metareq %p mblkext %p mblksn %u ref %u. wait callback.\n", meta_req,
726 : : mblkext_res,
727 : : mblkext_res->mblk_sn, mblkext_res->refcnt);
728 : : }
729 : : } else {
730 : 395 : mblkext_res = TAILQ_FIRST(&vol->metablocks_free);
731 [ - + ]: 395 : if (!mblkext_res) {
732 : 0 : _metablock_lru_reclaim_batch(vol);
733 : 0 : mblkext_res = TAILQ_FIRST(&vol->metablocks_free);
734 [ # # ]: 0 : assert(mblkext_res);
735 : : }
736 [ + - ]: 395 : TAILQ_REMOVE(&vol->metablocks_free, mblkext_res, tailq);
737 [ - + ]: 395 : assert(mblkext_res->refcnt == 0);
738 [ - + ]: 395 : assert(mblkext_res->batch_write_cnt == 0);
739 : :
740 : 395 : mblkext_res->mblk_sn = meta_req->mblk_sn;
741 : 395 : RB_INSERT(metablock_cache_tree, &vol->metablocks_caching, mblkext_res);
742 : 395 : _metablock_lru_update(vol, mblkext_res, true);
743 : :
744 : 395 : TAILQ_INSERT_TAIL(&mblkext_res->pending_to_supply_read, meta_req, tailq);
745 : 395 : mblkext_res->refcnt++;
746 : 395 : meta_req->mblk_extent = mblkext_res;
747 : :
748 [ - + - + ]: 395 : SPDK_INFOLOG(reduce, "metareq %p mblkext %p mtype %u mblksn %u ref %u. add to cache\n", meta_req,
749 : : mblkext_res, meta_req->mtype,
750 : : mblkext_res->mblk_sn, mblkext_res->refcnt);
751 : 395 : _reduce_meta_block_rw_backing(meta_req, SPDK_REDUCE_BACKING_IO_READ);
752 : : }
753 : 1423438 : }
754 : :
755 : : static int
756 : 22 : _allocate_vol_requests(struct spdk_reduce_vol *vol)
757 : : {
758 : : struct spdk_reduce_vol_request *req;
759 : 22 : struct spdk_reduce_backing_dev *backing_dev = vol->backing_dev;
760 : : uint32_t reqs_in_2mb_page, huge_pages_needed, request_memlen;
761 : : uint8_t *buffer, *buffer_end;
762 : 22 : int i = 0;
763 : 22 : int rc = 0;
764 : :
765 : : /* It is needed to allocate comp and decomp buffers so that they do not cross physical
766 : : * page boundaries. Assume that the system uses default 2MiB pages and chunk_size is not
767 : : * necessarily power of 2
768 : : * Allocate 2x since we need buffers for both read/write and compress/decompress
769 : : * intermediate buffers. */
770 [ - + ]: 22 : reqs_in_2mb_page = VALUE_2MB / (vol->params.chunk_size * 2);
771 [ - + ]: 22 : if (!reqs_in_2mb_page) {
772 : 0 : return -EINVAL;
773 : : }
774 [ - + ]: 22 : huge_pages_needed = SPDK_CEIL_DIV(REDUCE_NUM_VOL_REQUESTS, reqs_in_2mb_page);
775 : :
776 : 22 : vol->buf_mem = spdk_dma_malloc(VALUE_2MB * huge_pages_needed, VALUE_2MB, NULL);
777 [ - + ]: 22 : if (vol->buf_mem == NULL) {
778 : 0 : return -ENOMEM;
779 : : }
780 : :
781 : 22 : request_memlen = sizeof(*req);
782 [ + + ]: 22 : if (vol->params.meta_builtin) {
783 : 10 : request_memlen = sizeof(*req) + _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
784 : : }
785 : 22 : vol->request_mem = calloc(REDUCE_NUM_VOL_REQUESTS, request_memlen);
786 [ - + ]: 22 : if (vol->request_mem == NULL) {
787 : 0 : spdk_free(vol->buf_mem);
788 : 0 : vol->buf_mem = NULL;
789 : 0 : return -ENOMEM;
790 : : }
791 : :
792 : : /* Allocate 2x since we need iovs for both read/write and compress/decompress intermediate
793 : : * buffers.
794 : : */
795 : 22 : vol->buf_iov_mem = calloc(REDUCE_NUM_VOL_REQUESTS,
796 : 22 : 2 * sizeof(struct iovec) * vol->backing_io_units_per_chunk);
797 [ - + ]: 22 : if (vol->buf_iov_mem == NULL) {
798 : 0 : free(vol->request_mem);
799 : 0 : spdk_free(vol->buf_mem);
800 : 0 : vol->request_mem = NULL;
801 : 0 : vol->buf_mem = NULL;
802 : 0 : return -ENOMEM;
803 : : }
804 : :
805 : 22 : vol->buf_backing_io_mem = calloc(REDUCE_NUM_VOL_REQUESTS, (sizeof(struct spdk_reduce_backing_io) +
806 : 22 : backing_dev->user_ctx_size) * vol->backing_io_units_per_chunk);
807 [ - + ]: 22 : if (vol->buf_backing_io_mem == NULL) {
808 : 0 : free(vol->request_mem);
809 : 0 : free(vol->buf_iov_mem);
810 : 0 : spdk_free(vol->buf_mem);
811 : 0 : vol->request_mem = NULL;
812 : 0 : vol->buf_iov_mem = NULL;
813 : 0 : vol->buf_mem = NULL;
814 : 0 : return -ENOMEM;
815 : : }
816 : :
817 : 22 : buffer = vol->buf_mem;
818 : 22 : buffer_end = buffer + VALUE_2MB * huge_pages_needed;
819 : :
820 [ + + ]: 5654 : for (i = 0; i < REDUCE_NUM_VOL_REQUESTS; i++) {
821 : 5632 : req = (void *)vol->request_mem + i * request_memlen;
822 [ + + ]: 5632 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
823 [ + + ]: 5632 : if (vol->params.meta_builtin) {
824 : 2560 : req->prealloc_chunk = (void *)req + sizeof(*req);
825 : : }
826 : 11264 : req->backing_io = (struct spdk_reduce_backing_io *)(vol->buf_backing_io_mem + i *
827 : 5632 : (sizeof(struct spdk_reduce_backing_io) + backing_dev->user_ctx_size) *
828 : 5632 : vol->backing_io_units_per_chunk);
829 : :
830 : 5632 : req->decomp_buf_iov = &vol->buf_iov_mem[(2 * i) * vol->backing_io_units_per_chunk];
831 : 5632 : req->comp_buf_iov = &vol->buf_iov_mem[(2 * i + 1) * vol->backing_io_units_per_chunk];
832 : :
833 : 5632 : rc = _set_buffer(&req->comp_buf, &buffer, buffer_end, vol->params.chunk_size);
834 [ - + ]: 5632 : if (rc) {
835 : 0 : SPDK_ERRLOG("Failed to set comp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
836 : : vol->buf_mem, buffer_end);
837 : 0 : break;
838 : : }
839 : 5632 : rc = _set_buffer(&req->decomp_buf, &buffer, buffer_end, vol->params.chunk_size);
840 [ - + ]: 5632 : if (rc) {
841 : 0 : SPDK_ERRLOG("Failed to set decomp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
842 : : vol->buf_mem, buffer_end);
843 : 0 : break;
844 : : }
845 : : }
846 : :
847 [ - + ]: 22 : if (rc) {
848 : 0 : free(vol->buf_backing_io_mem);
849 : 0 : free(vol->buf_iov_mem);
850 : 0 : free(vol->request_mem);
851 : 0 : spdk_free(vol->buf_mem);
852 : 0 : vol->buf_mem = NULL;
853 : 0 : vol->buf_backing_io_mem = NULL;
854 : 0 : vol->buf_iov_mem = NULL;
855 : 0 : vol->request_mem = NULL;
856 : : }
857 : :
858 : 22 : return rc;
859 : : }
860 : :
861 : : const struct spdk_reduce_vol_info *
862 : 22 : spdk_reduce_vol_get_info(const struct spdk_reduce_vol *vol)
863 : : {
864 : 22 : return &vol->info;
865 : : }
866 : :
867 : : static void
868 : 515 : _init_load_cleanup(struct spdk_reduce_vol *vol, struct reduce_init_load_ctx *ctx)
869 : : {
870 [ + + ]: 515 : if (ctx != NULL) {
871 : 471 : spdk_free(ctx->path);
872 : 471 : spdk_free(ctx->mblk_init_ctx.meta_init_buf);
873 : 471 : free(ctx->backing_io);
874 : 471 : free(ctx);
875 : : }
876 : :
877 [ + + ]: 515 : if (vol != NULL) {
878 [ + + ]: 471 : if (vol->pm_file.pm_buf != NULL) {
879 : 12 : pmem_unmap(vol->pm_file.pm_buf, vol->pm_file.size);
880 : : }
881 : :
882 : 471 : spdk_free(vol->backing_super);
883 : 471 : spdk_bit_array_free(&vol->allocated_chunk_maps);
884 : 471 : spdk_bit_array_free(&vol->allocated_backing_io_units);
885 : 471 : free(vol->request_mem);
886 : 471 : free(vol->buf_backing_io_mem);
887 : 471 : free(vol->buf_iov_mem);
888 : 471 : spdk_free(vol->buf_mem);
889 : 471 : spdk_free(vol->metablock_buf);
890 : 471 : free(vol->metablock_extent_mem);
891 : 471 : free(vol->meta_request_buf);
892 : 471 : free(vol);
893 : : }
894 : 515 : }
895 : :
896 : : static int
897 : 471 : _alloc_zero_buff(void)
898 : : {
899 : 471 : int rc = 0;
900 : :
901 : : /* The zero buffer is shared between all volumes and just used
902 : : * for reads so allocate one global instance here if not already
903 : : * allocated when another vol init'd or loaded.
904 : : */
905 [ + + ]: 471 : if (g_vol_count++ == 0) {
906 : 77 : g_zero_buf = spdk_zmalloc(REDUCE_ZERO_BUF_SIZE,
907 : : 64, NULL, SPDK_ENV_LCORE_ID_ANY,
908 : : SPDK_MALLOC_DMA);
909 [ - + ]: 77 : if (g_zero_buf == NULL) {
910 : 0 : g_vol_count--;
911 : 0 : rc = -ENOMEM;
912 : : }
913 : : }
914 : 471 : return rc;
915 : : }
916 : :
917 : : static int
918 : 10 : _allocate_vol_metablock_and_metaio(struct spdk_reduce_vol *vol)
919 : : {
920 : : uint32_t mblk_in_2mb_page, total_mblk, total_metareq, huge_pages_needed, backing_io_struct_size, i;
921 : : struct reduce_metablock_extent *mblk_extent;
922 : : struct reduce_meta_request *meta_request;
923 : 10 : int rc = 0;
924 : :
925 : 10 : mblk_in_2mb_page = VALUE_2MB / REDUCE_META_BLKSZ;
926 [ - + ]: 10 : huge_pages_needed = SPDK_CEIL_DIV(REDUCE_META_CACHE_BLOCK_NUM, mblk_in_2mb_page);
927 : 10 : total_mblk = huge_pages_needed * mblk_in_2mb_page;
928 : 10 : vol->metablocks_num = total_mblk;
929 : :
930 : : /* alloc metablock mem */
931 : 10 : vol->metablock_buf = spdk_dma_malloc(VALUE_2MB * huge_pages_needed, VALUE_2MB, NULL);
932 [ - + ]: 10 : if (vol->metablock_buf == NULL) {
933 : 0 : rc = -ENOMEM;
934 : 0 : goto err;
935 : : }
936 : :
937 : : /* alloc metablock control struct */
938 : 10 : vol->metablock_extent_mem = calloc(total_mblk, sizeof(*vol->metablock_extent_mem));
939 [ - + ]: 10 : if (vol->request_mem == NULL) {
940 : 0 : rc = -ENOMEM;
941 : 0 : goto err;
942 : : }
943 [ + + ]: 10250 : for (i = 0; i < total_mblk; i++) {
944 : 10240 : mblk_extent = &vol->metablock_extent_mem[i];
945 : 10240 : mblk_extent->iov.iov_base = vol->metablock_buf + i * REDUCE_META_BLKSZ;
946 : 10240 : mblk_extent->iov.iov_len = REDUCE_META_BLKSZ;
947 : 10240 : TAILQ_INIT(&mblk_extent->pending_to_supply_read);
948 : 10240 : TAILQ_INIT(&mblk_extent->pending_to_write);
949 [ + + ]: 10240 : TAILQ_INSERT_HEAD(&vol->metablocks_free, mblk_extent, tailq);
950 : : }
951 : :
952 : : /* alloc reduce_meta_request struct mem
953 : : * Use these struct memory to record the ctx before meta read/write operetion, wait complete callback to restore them.
954 : : */
955 : 10 : backing_io_struct_size = sizeof(struct reduce_meta_request) + vol->backing_dev->user_ctx_size;
956 : : /* every vol date request may need 2 meta req for logical_map and chunk_map */
957 : 10 : total_metareq = 2 * REDUCE_NUM_VOL_REQUESTS;
958 : 10 : vol->meta_request_buf = (struct reduce_meta_request *)calloc(total_metareq, backing_io_struct_size);
959 [ - + ]: 10 : if (vol->meta_request_buf == NULL) {
960 : 0 : rc = -ENOMEM;
961 : 0 : goto err;
962 : : }
963 [ + + ]: 5130 : for (i = 0; i < total_metareq; i++) {
964 : 5120 : meta_request = (struct reduce_meta_request *)((uint8_t *)vol->meta_request_buf + i *
965 : : backing_io_struct_size);
966 : 5120 : meta_request->backing_io.backing_cb_args = &meta_request->cb_args;
967 [ + + ]: 5120 : TAILQ_INSERT_HEAD(&vol->free_meta_request, meta_request, tailq);
968 : : }
969 : :
970 : 10 : return 0;
971 : 0 : err:
972 : 0 : spdk_free(vol->metablock_buf);
973 : 0 : free(vol->metablock_extent_mem);
974 : 0 : free(vol->meta_request_buf);
975 : 0 : vol->metablock_buf = NULL;
976 : 0 : vol->metablock_extent_mem = NULL;
977 : 0 : vol->meta_request_buf = NULL;
978 : :
979 : 0 : return rc;
980 : : }
981 : :
982 : : static void
983 : 22 : _init_write_super_cpl(void *cb_arg, int reduce_errno)
984 : : {
985 : 22 : struct reduce_init_load_ctx *init_ctx = cb_arg;
986 : 22 : int rc = 0;
987 : :
988 [ - + ]: 22 : if (reduce_errno != 0) {
989 : 0 : rc = reduce_errno;
990 : 0 : goto err;
991 : : }
992 : :
993 : 22 : rc = _allocate_vol_requests(init_ctx->vol);
994 [ - + ]: 22 : if (rc != 0) {
995 : 0 : goto err;
996 : : }
997 : :
998 [ + + ]: 22 : if (init_ctx->vol->params.meta_builtin) {
999 : 10 : rc = _allocate_vol_metablock_and_metaio(init_ctx->vol);
1000 [ - + ]: 10 : if (rc != 0) {
1001 : 0 : goto err;
1002 : : }
1003 : : }
1004 : :
1005 : 22 : rc = _alloc_zero_buff();
1006 [ - + ]: 22 : if (rc != 0) {
1007 : 0 : goto err;
1008 : : }
1009 : :
1010 : 22 : init_ctx->cb_fn(init_ctx->cb_arg, init_ctx->vol, rc);
1011 : : /* Only clean up the ctx - the vol has been passed to the application
1012 : : * for use now that initialization was successful.
1013 : : */
1014 : 22 : _init_load_cleanup(NULL, init_ctx);
1015 : :
1016 : 22 : return;
1017 : 0 : err:
1018 [ # # # # ]: 0 : if (unlink(init_ctx->path)) {
1019 : 0 : SPDK_ERRLOG("%s could not be unlinked: %s\n",
1020 : : (char *)init_ctx->path, spdk_strerror(errno));
1021 : : }
1022 : :
1023 : 0 : init_ctx->cb_fn(init_ctx->cb_arg, NULL, rc);
1024 : 0 : _init_load_cleanup(init_ctx->vol, init_ctx);
1025 : : }
1026 : :
1027 : : static void
1028 : 22 : _init_write_path_cpl(void *cb_arg, int reduce_errno)
1029 : : {
1030 : 22 : struct reduce_init_load_ctx *init_ctx = cb_arg;
1031 : 22 : struct spdk_reduce_vol *vol = init_ctx->vol;
1032 : 22 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
1033 : :
1034 [ - + ]: 22 : if (reduce_errno != 0) {
1035 : 0 : _init_write_super_cpl(cb_arg, reduce_errno);
1036 : 0 : return;
1037 : : }
1038 : :
1039 : 22 : init_ctx->iov[0].iov_base = vol->backing_super;
1040 : 22 : init_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
1041 : 22 : init_ctx->backing_cb_args.cb_fn = _init_write_super_cpl;
1042 : 22 : init_ctx->backing_cb_args.cb_arg = init_ctx;
1043 : :
1044 : 22 : backing_io->dev = vol->backing_dev;
1045 : 22 : backing_io->iov = init_ctx->iov;
1046 : 22 : backing_io->iovcnt = 1;
1047 : 22 : backing_io->lba = 0;
1048 [ - + ]: 22 : backing_io->lba_count = sizeof(*vol->backing_super) / vol->backing_dev->blocklen;
1049 : 22 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
1050 : 22 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1051 : :
1052 : 22 : vol->backing_dev->submit_backing_io(backing_io);
1053 : : }
1054 : :
1055 : : static void
1056 : 22 : _init_builtin_meta_region_cpl(void *cb_arg, int reduce_errno)
1057 : : {
1058 : 22 : struct reduce_init_load_ctx *init_ctx = cb_arg;
1059 : 22 : struct spdk_reduce_vol *vol = init_ctx->vol;
1060 : 22 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
1061 : :
1062 [ - + ]: 22 : if (reduce_errno != 0) {
1063 : 0 : _init_write_path_cpl(cb_arg, reduce_errno);
1064 : 0 : return;
1065 : : }
1066 : :
1067 : 22 : init_ctx->iov[0].iov_base = init_ctx->path;
1068 : 22 : init_ctx->iov[0].iov_len = REDUCE_PATH_MAX;
1069 : 22 : init_ctx->backing_cb_args.cb_fn = _init_write_path_cpl;
1070 : 22 : init_ctx->backing_cb_args.cb_arg = init_ctx;
1071 : : /* Write path to offset 4K on backing device - just after where the super
1072 : : * block will be written. We wait until this is committed before writing the
1073 : : * super block to guarantee we don't get the super block written without the
1074 : : * the path if the system crashed in the middle of a write operation.
1075 : : */
1076 : 22 : backing_io->dev = vol->backing_dev;
1077 : 22 : backing_io->iov = init_ctx->iov;
1078 : 22 : backing_io->iovcnt = 1;
1079 [ - + ]: 22 : backing_io->lba = REDUCE_BACKING_DEV_PATH_OFFSET / vol->backing_dev->blocklen;
1080 [ - + ]: 22 : backing_io->lba_count = REDUCE_PATH_MAX / vol->backing_dev->blocklen;
1081 : 22 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
1082 : 22 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1083 : :
1084 : 22 : vol->backing_dev->submit_backing_io(backing_io);
1085 : : }
1086 : :
1087 : : static void
1088 : 30 : _init_builtin_meta_region(void *cb_arg, int reduce_errno)
1089 : : {
1090 : 30 : struct reduce_init_load_ctx *init_ctx = cb_arg;
1091 : 30 : struct spdk_reduce_vol *vol = init_ctx->vol;
1092 : 30 : struct reduce_mblk_init_ctx *ctx = &init_ctx->mblk_init_ctx;
1093 : 30 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
1094 : : uint64_t offset, len;
1095 : :
1096 [ + - + + ]: 30 : if (reduce_errno || ctx->off_need_init_next == ctx->off_need_init + ctx->len_need_init) {
1097 : 10 : _init_builtin_meta_region_cpl(cb_arg, reduce_errno);
1098 : 10 : return;
1099 : : }
1100 : :
1101 : 20 : offset = ctx->off_need_init_next;
1102 : 20 : len = spdk_min(ctx->maxlen_per_io,
1103 : : ctx->off_need_init + ctx->len_need_init - ctx->off_need_init_next);
1104 : 20 : ctx->off_need_init_next = offset + len;
1105 : :
1106 : 20 : init_ctx->iov[0].iov_base = ctx->meta_init_buf;
1107 : 20 : init_ctx->iov[0].iov_len = len * vol->backing_dev->blocklen;
1108 : 20 : init_ctx->backing_cb_args.cb_fn = _init_builtin_meta_region;
1109 : 20 : init_ctx->backing_cb_args.cb_arg = init_ctx;
1110 : :
1111 : 20 : backing_io->dev = vol->backing_dev;
1112 : 20 : backing_io->iov = init_ctx->iov;
1113 : 20 : backing_io->iovcnt = 1;
1114 : 20 : backing_io->lba = offset;
1115 : 20 : backing_io->lba_count = len;
1116 : 20 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
1117 : 20 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1118 : :
1119 : 20 : vol->backing_dev->submit_backing_io(backing_io);
1120 : : }
1121 : :
1122 : : static void
1123 : 10 : _init_builtin_meta_region_ctx_init(struct reduce_init_load_ctx *init_ctx)
1124 : : {
1125 : 10 : struct reduce_mblk_init_ctx *mblk_init_ctx = &init_ctx->mblk_init_ctx;
1126 : 10 : struct spdk_reduce_vol *vol = init_ctx->vol;
1127 : :
1128 : 20 : mblk_init_ctx->off_need_init = vol->params.meta_region_desc[REDUCE_MTYPE_SB].offset /
1129 : 10 : REDUCE_META_BLKSZ *
1130 : 10 : vol->backing_dev->blocklen;
1131 [ - + ]: 20 : mblk_init_ctx->len_need_init = vol->params.data_region_off / vol->backing_dev->blocklen -
1132 : 10 : mblk_init_ctx->off_need_init;
1133 : 10 : mblk_init_ctx->off_need_init_next = mblk_init_ctx->off_need_init;
1134 [ - + ]: 10 : mblk_init_ctx->maxlen_per_io = mblk_init_ctx->buf_len / vol->backing_dev->blocklen;
1135 : 10 : }
1136 : :
1137 : : static int
1138 : 22 : _allocate_bit_arrays(struct spdk_reduce_vol *vol)
1139 : : {
1140 : : uint64_t total_chunks, total_backing_io_units;
1141 : : uint32_t i, num_metadata_io_units;
1142 : :
1143 : 22 : total_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
1144 [ + + ]: 22 : if (vol->params.meta_builtin) {
1145 [ - + ]: 10 : total_chunks += vol->params.data_region_off / vol->params.chunk_size;
1146 : : }
1147 : 22 : vol->allocated_chunk_maps = spdk_bit_array_create(total_chunks);
1148 : 22 : vol->find_chunk_offset = 0;
1149 [ - + ]: 22 : total_backing_io_units = total_chunks * (vol->params.chunk_size / vol->params.backing_io_unit_size);
1150 : 22 : vol->allocated_backing_io_units = spdk_bit_array_create(total_backing_io_units);
1151 : 22 : vol->find_block_offset = 0;
1152 : :
1153 [ + - - + ]: 22 : if (vol->allocated_chunk_maps == NULL || vol->allocated_backing_io_units == NULL) {
1154 : 0 : return -ENOMEM;
1155 : : }
1156 : :
1157 : : /* Set backing io unit bits associated with metadata. */
1158 [ + + ]: 22 : if (vol->params.meta_builtin) {
1159 [ - + ]: 10 : num_metadata_io_units = vol->params.data_region_off / vol->params.backing_io_unit_size;
1160 : : } else {
1161 : 12 : num_metadata_io_units = (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
1162 [ - + ]: 12 : vol->params.backing_io_unit_size;
1163 : : }
1164 [ + + ]: 826 : for (i = 0; i < num_metadata_io_units; i++) {
1165 : 804 : spdk_bit_array_set(vol->allocated_backing_io_units, i);
1166 : 804 : vol->info.allocated_io_units++;
1167 : : }
1168 : :
1169 : 22 : return 0;
1170 : : }
1171 : :
1172 : : static int
1173 : 10480605 : overlap_cmp(struct spdk_reduce_vol_request *req1, struct spdk_reduce_vol_request *req2)
1174 : : {
1175 [ + + ]: 18227903 : return (req1->logical_map_index < req2->logical_map_index ? -1 : req1->logical_map_index >
1176 : 7747298 : req2->logical_map_index);
1177 : : }
1178 [ + + + + : 18815433 : RB_GENERATE_STATIC(executing_req_tree, spdk_reduce_vol_request, rbnode, overlap_cmp);
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ + + + +
+ + + ]
1179 : :
1180 : : static inline int
1181 : 44 : _check_meta_builtin(const char *path)
1182 : : {
1183 [ - + ]: 44 : return strlen(path) == strlen(REDUCE_META_BUILTIN) &&
1184 [ + + - + : 44 : memcmp(path, REDUCE_META_BUILTIN, sizeof(REDUCE_META_BUILTIN)) == 0 ? 1 : 0;
+ - ]
1185 : : }
1186 : :
1187 : : static inline uint32_t
1188 : 40 : _meta_get_elem_struct_size(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype)
1189 : : {
1190 : 40 : uint32_t size = 0;
1191 : :
1192 [ + + + + : 40 : switch (mtype) {
- ]
1193 : 10 : case REDUCE_MTYPE_SB:
1194 : 10 : size = sizeof(struct spdk_reduce_vol_superblock);
1195 : 10 : break;
1196 : 10 : case REDUCE_MTYPE_PATH:
1197 : 10 : size = REDUCE_PATH_MAX;
1198 : 10 : break;
1199 : 10 : case REDUCE_MTYPE_LOGICAL_MAP:
1200 : 10 : size = sizeof(uint64_t);
1201 : 10 : break;
1202 : 10 : case REDUCE_MTYPE_CHUNK_MAP:
1203 [ - + ]: 10 : size = _reduce_vol_get_chunk_struct_size(params->chunk_size / params->backing_io_unit_size);
1204 : 10 : break;
1205 : 0 : default:
1206 : 0 : break;
1207 : : }
1208 : :
1209 : 40 : return size;
1210 : : }
1211 : :
1212 : : static inline uint64_t
1213 : 40 : _meta_get_elem_count(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype,
1214 : : uint64_t backing_dev_size)
1215 : : {
1216 : 40 : uint64_t count = 0;
1217 : :
1218 [ + + - ]: 40 : switch (mtype) {
1219 : 20 : case REDUCE_MTYPE_SB:
1220 : : case REDUCE_MTYPE_PATH:
1221 : 20 : count = 1;
1222 : 20 : break;
1223 : 20 : case REDUCE_MTYPE_LOGICAL_MAP:
1224 : : case REDUCE_MTYPE_CHUNK_MAP:
1225 [ - + ]: 20 : count = backing_dev_size / params->chunk_size;
1226 : 20 : break;
1227 : 0 : default:
1228 : 0 : assert(0);
1229 : : }
1230 : :
1231 : 40 : return count;
1232 : : }
1233 : :
1234 : :
1235 : : static uint64_t
1236 : 10 : _meta_builtin_get_vol_size(uint64_t backing_dev_size, struct spdk_reduce_vol_params *params)
1237 : : {
1238 : : uint64_t num_chunks;
1239 : :
1240 [ - + ]: 10 : num_chunks = (backing_dev_size - params->data_region_off) / params->chunk_size;
1241 : 10 : num_chunks -= REDUCE_NUM_EXTRA_CHUNKS;
1242 : :
1243 : 10 : return num_chunks * params->chunk_size;
1244 : : }
1245 : :
1246 : : /* Calc the metaregion offset and length according backing dev size,
1247 : : * then datasize = backing dev size - metasize.
1248 : : * The data region that 'logical_map' and 'chunkmap' can represent is a little more than the true data region.
1249 : : * It is neccessary to set the bits on the allocated bitarray of allocated_backing_io_units which represent meta region.
1250 : : */
1251 : : static void
1252 : 10 : _init_builtin_meta_region_params(struct spdk_reduce_vol_params *params, uint64_t backing_dev_size)
1253 : : {
1254 : : /* tmpoff unit is REDUCE_META_BLKSZ */
1255 : 10 : uint64_t tmpoff = 0;
1256 : : uint32_t mtype;
1257 : 10 : struct spdk_reduce_meta_desc *meta_region_desc = params->meta_region_desc;
1258 : :
1259 [ + + ]: 50 : for (mtype = REDUCE_MTYPE_SB; mtype < REDUCE_MTYPE_NR; mtype++) {
1260 : : /* Although size_per_elem and elems_per_mblk could calc at runtime,
1261 : : * calc it at init time could avoid calc it every time when edit a new mblk,
1262 : : * especially at I/O path.
1263 : : */
1264 : 40 : meta_region_desc[mtype].size_per_elem = _meta_get_elem_struct_size(params, mtype);
1265 [ - + ]: 40 : meta_region_desc[mtype].elems_per_mblk = REDUCE_META_BLKSZ / meta_region_desc[mtype].size_per_elem;
1266 : 40 : meta_region_desc[mtype].offset = tmpoff;
1267 : 40 : meta_region_desc[mtype].length = spdk_divide_round_up(_meta_get_elem_count(params, mtype,
1268 : 40 : backing_dev_size), meta_region_desc[mtype].elems_per_mblk);
1269 : 40 : tmpoff += meta_region_desc[mtype].length;
1270 : : }
1271 : :
1272 : : /* data */
1273 : 10 : params->data_region_off = tmpoff * REDUCE_META_BLKSZ;
1274 : 10 : params->vol_size = _meta_builtin_get_vol_size(backing_dev_size, params);
1275 : 10 : }
1276 : :
1277 : : static int
1278 : 10 : _alloc_meta_buf(struct spdk_reduce_vol *vol, struct reduce_init_load_ctx *init_ctx)
1279 : : {
1280 : 10 : struct reduce_mblk_init_ctx *mblk_init_ctx = &init_ctx->mblk_init_ctx;
1281 : :
1282 : 10 : mblk_init_ctx->buf_len = SPDK_ALIGN_FLOOR(REDUCE_META_INIT_BUF_SIZE, REDUCE_META_BLKSZ);
1283 : 10 : mblk_init_ctx->meta_init_buf = spdk_malloc(mblk_init_ctx->buf_len,
1284 : : 64, NULL, SPDK_ENV_LCORE_ID_ANY,
1285 : : SPDK_MALLOC_DMA);
1286 [ - + ]: 10 : if (mblk_init_ctx->meta_init_buf == NULL) {
1287 : 0 : return -ENOMEM;
1288 : : }
1289 [ - + ]: 10 : memset(mblk_init_ctx->meta_init_buf, 0xff, mblk_init_ctx->buf_len);
1290 : :
1291 : 10 : return 0;
1292 : : }
1293 : :
1294 : : void
1295 : 22 : spdk_reduce_vol_init(struct spdk_reduce_vol_params *params,
1296 : : struct spdk_reduce_backing_dev *backing_dev,
1297 : : const char *pm_file_dir,
1298 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
1299 : : {
1300 : : struct spdk_reduce_vol *vol;
1301 : : struct reduce_init_load_ctx *init_ctx;
1302 : : struct spdk_reduce_backing_io *backing_io;
1303 : : uint64_t backing_dev_size;
1304 : : size_t mapped_len;
1305 : 22 : int dir_len = 0, max_dir_len, rc;
1306 : :
1307 : 22 : rc = _validate_vol_params(params);
1308 [ - + ]: 22 : if (rc != 0) {
1309 : 0 : SPDK_ERRLOG("invalid vol params\n");
1310 : 0 : cb_fn(cb_arg, NULL, rc);
1311 : 0 : return;
1312 : : }
1313 : :
1314 : 22 : backing_dev_size = backing_dev->blockcnt * backing_dev->blocklen;
1315 : 22 : params->vol_size = _get_vol_size(params->chunk_size, backing_dev_size);
1316 [ - + ]: 22 : if (params->vol_size == 0) {
1317 : 0 : SPDK_ERRLOG("backing device is too small\n");
1318 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1319 : 0 : return;
1320 : : }
1321 : :
1322 [ - + ]: 22 : if (backing_dev->submit_backing_io == NULL) {
1323 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
1324 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1325 : 0 : return;
1326 : : }
1327 : :
1328 [ + - ]: 22 : if (spdk_uuid_is_null(¶ms->uuid)) {
1329 : 22 : spdk_uuid_generate(¶ms->uuid);
1330 : : }
1331 : :
1332 [ + + ]: 22 : if (!_check_meta_builtin(pm_file_dir)) {
1333 : : /* Use pmem to host metadata */
1334 : 12 : params->meta_builtin = false;
1335 : : /* We need to append a path separator and the UUID to the supplied
1336 : : * path.
1337 : : */
1338 : 12 : max_dir_len = REDUCE_PATH_MAX - SPDK_UUID_STRING_LEN - 1;
1339 [ - + ]: 12 : dir_len = strnlen(pm_file_dir, max_dir_len);
1340 : : /* Strip trailing slash if the user provided one - we will add it back
1341 : : * later when appending the filename.
1342 : : */
1343 [ - + ]: 12 : if (pm_file_dir[dir_len - 1] == '/') {
1344 : 0 : dir_len--;
1345 : : }
1346 [ - + ]: 12 : if (dir_len == max_dir_len) {
1347 : 0 : SPDK_ERRLOG("pm_file_dir (%s) too long\n", pm_file_dir);
1348 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1349 : 0 : return;
1350 : : }
1351 : : } else {
1352 : : /* Use backing dev space to hold metadata */
1353 : 10 : params->meta_builtin = true;
1354 : : }
1355 : :
1356 : 22 : vol = calloc(1, sizeof(*vol));
1357 [ - + ]: 22 : if (vol == NULL) {
1358 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1359 : 0 : return;
1360 : : }
1361 : :
1362 : 22 : TAILQ_INIT(&vol->free_requests);
1363 : 22 : RB_INIT(&vol->executing_requests);
1364 : 22 : TAILQ_INIT(&vol->queued_requests);
1365 : 22 : queue_init(&vol->free_chunks_queue);
1366 : 22 : queue_init(&vol->free_backing_blocks_queue);
1367 : 22 : TAILQ_INIT(&vol->metablocks_free);
1368 : 22 : TAILQ_INIT(&vol->metablocks_lru);
1369 : 22 : RB_INIT(&vol->metablocks_caching);
1370 : 22 : TAILQ_INIT(&vol->free_meta_request);
1371 : :
1372 : 22 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 0, NULL,
1373 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1374 [ - + ]: 22 : if (vol->backing_super == NULL) {
1375 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1376 : 0 : _init_load_cleanup(vol, NULL);
1377 : 0 : return;
1378 : : }
1379 : :
1380 : 22 : init_ctx = calloc(1, sizeof(*init_ctx));
1381 [ - + ]: 22 : if (init_ctx == NULL) {
1382 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1383 : 0 : _init_load_cleanup(vol, NULL);
1384 : 0 : return;
1385 : : }
1386 : :
1387 : 22 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1388 [ - + ]: 22 : if (backing_io == NULL) {
1389 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1390 : 0 : _init_load_cleanup(vol, init_ctx);
1391 : 0 : return;
1392 : : }
1393 : 22 : init_ctx->backing_io = backing_io;
1394 : :
1395 : 22 : init_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 0, NULL,
1396 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1397 [ - + ]: 22 : if (init_ctx->path == NULL) {
1398 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1399 : 0 : _init_load_cleanup(vol, init_ctx);
1400 : 0 : return;
1401 : : }
1402 : :
1403 [ - + ]: 22 : vol->backing_io_units_per_chunk = params->chunk_size / params->backing_io_unit_size;
1404 [ - + ]: 22 : vol->logical_blocks_per_chunk = params->chunk_size / params->logical_block_size;
1405 [ - + ]: 22 : vol->backing_lba_per_io_unit = params->backing_io_unit_size / backing_dev->blocklen;
1406 : 22 : vol->backing_dev = backing_dev;
1407 : :
1408 : : /* meta in pmem: init metadata in pmemdir.
1409 : : meta in backing_bdev: split total region to meta region and data region.
1410 : : */
1411 [ + + ]: 22 : if (params->meta_builtin == false) {
1412 [ - + ]: 12 : assert(dir_len != 0);
1413 [ - + - + ]: 12 : memcpy(vol->pm_file.path, pm_file_dir, dir_len);
1414 : 12 : vol->pm_file.path[dir_len] = '/';
1415 : 12 : spdk_uuid_fmt_lower(&vol->pm_file.path[dir_len + 1], SPDK_UUID_STRING_LEN,
1416 : 12 : ¶ms->uuid);
1417 : 12 : vol->pm_file.size = _get_pm_file_size(params);
1418 : 12 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, vol->pm_file.size,
1419 : : PMEM_FILE_CREATE | PMEM_FILE_EXCL, 0600,
1420 : : &mapped_len, &vol->pm_file.pm_is_pmem);
1421 [ - + ]: 12 : if (vol->pm_file.pm_buf == NULL) {
1422 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n",
1423 : : vol->pm_file.path, strerror(errno));
1424 : 0 : cb_fn(cb_arg, NULL, -errno);
1425 : 0 : _init_load_cleanup(vol, init_ctx);
1426 : 0 : return;
1427 : : }
1428 : :
1429 [ - + ]: 12 : if (vol->pm_file.size != mapped_len) {
1430 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
1431 : : vol->pm_file.size, mapped_len);
1432 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1433 : 0 : _init_load_cleanup(vol, init_ctx);
1434 : 0 : return;
1435 : : }
1436 : :
1437 [ - + - + ]: 12 : memcpy(&vol->params, params, sizeof(*params));
1438 : 12 : _initialize_vol_pm_pointers(vol);
1439 : : } else {
1440 : : /* metadata region on backingbdev
1441 : : |0 |4096 |8192... |
1442 : : |sb |mdpath |logical_map... |chunk_map...
1443 : : the order of initailization: pmpath, normalmeta(logical_map, chunk_map), sb
1444 : : */
1445 : 10 : _init_builtin_meta_region_params(params, backing_dev_size);
1446 [ - + - + ]: 10 : memcpy(&vol->params, params, sizeof(*params));
1447 : : }
1448 : :
1449 [ - + ]: 22 : memcpy(vol->backing_super->signature, SPDK_REDUCE_SIGNATURE,
1450 : : sizeof(vol->backing_super->signature));
1451 [ - + - + ]: 22 : memcpy(&vol->backing_super->params, params, sizeof(*params));
1452 : :
1453 [ + + ]: 22 : if (params->meta_builtin == false) {
1454 [ - + - + ]: 12 : memcpy(vol->pm_super, vol->backing_super, sizeof(*vol->backing_super));
1455 : : /* Writing 0xFF's is equivalent of filling it all with SPDK_EMPTY_MAP_ENTRY.
1456 : : * Note that this writes 0xFF to not just the logical map but the chunk maps as well.
1457 : : */
1458 [ - + ]: 12 : memset(vol->pm_logical_map, 0xFF, vol->pm_file.size - sizeof(*vol->backing_super));
1459 : 12 : _reduce_persist(vol, vol->pm_file.pm_buf, vol->pm_file.size);
1460 : : }
1461 : :
1462 : 22 : rc = _allocate_bit_arrays(vol);
1463 [ - + ]: 22 : if (rc != 0) {
1464 : 0 : cb_fn(cb_arg, NULL, rc);
1465 : 0 : _init_load_cleanup(vol, init_ctx);
1466 : 0 : return;
1467 : : }
1468 : :
1469 : 22 : init_ctx->vol = vol;
1470 : 22 : init_ctx->cb_fn = cb_fn;
1471 : 22 : init_ctx->cb_arg = cb_arg;
1472 : :
1473 [ + + ]: 22 : if (params->meta_builtin) {
1474 [ - + ]: 10 : memcpy(init_ctx->path, REDUCE_META_BUILTIN, sizeof(REDUCE_META_BUILTIN));
1475 : 10 : rc = _alloc_meta_buf(vol, init_ctx);
1476 [ - + ]: 10 : if (rc != 0) {
1477 : 0 : cb_fn(cb_arg, NULL, rc);
1478 : 0 : _init_load_cleanup(vol, init_ctx);
1479 : 0 : return;
1480 : : }
1481 : 10 : _init_builtin_meta_region_ctx_init(init_ctx);
1482 : 10 : _init_builtin_meta_region(init_ctx, 0);
1483 : : } else {
1484 [ - + - + ]: 12 : memcpy(init_ctx->path, vol->pm_file.path, REDUCE_PATH_MAX);
1485 : 12 : _init_builtin_meta_region_cpl(init_ctx, 0);
1486 : : }
1487 : : }
1488 : :
1489 : : static struct reduce_meta_request *
1490 : 1423438 : _get_init_meta_req(struct spdk_reduce_vol *vol, bool is_write,
1491 : : enum spdk_reduce_meta_type mtype, uint32_t mblk_sn, uint32_t elemsn_on_mblk,
1492 : : spdk_reduce_dev_cpl cb_fn)
1493 : : {
1494 : : struct reduce_meta_request *meta_req;
1495 : 1423438 : meta_req = TAILQ_FIRST(&vol->free_meta_request);
1496 [ - + ]: 1423438 : if (meta_req == NULL) {
1497 : : /* should not happend, because we have allocated enough metateq. 2 metareq for each vol req */
1498 : 0 : assert(0);
1499 : : return meta_req;
1500 : : }
1501 [ + - ]: 1423438 : TAILQ_REMOVE(&vol->free_meta_request, meta_req, tailq);
1502 : 1423438 : meta_req->vol = vol;
1503 : 1423438 : meta_req->is_write = is_write;
1504 : 1423438 : meta_req->supply_read = is_write ? 1 : 0;
1505 : 1423438 : meta_req->mtype = mtype;
1506 : 1423438 : meta_req->mblk_sn = mblk_sn;
1507 : 1423438 : meta_req->elem_sn_on_mblk = elemsn_on_mblk;
1508 : 1423438 : meta_req->cb_args.cb_fn = cb_fn;
1509 : 1423438 : meta_req->cb_args.cb_arg = meta_req;
1510 : :
1511 : 1423438 : return meta_req;
1512 : : }
1513 : :
1514 : : static void _load_allocated_backing_io_unit(struct reduce_init_load_ctx *load_ctx, uint32_t mblksn);
1515 : :
1516 : : /* restore the vol->allocated_backing_io_units by meta blocks */
1517 : : static void
1518 : 0 : _load_allocated_backing_io_unit_cb(void *cb_arg, int reduce_errno)
1519 : : {
1520 : 0 : struct reduce_meta_request *meta_req = cb_arg;
1521 : 0 : struct reduce_init_load_ctx *load_ctx = meta_req->restore_ctx.load_ctx.load_ctx;
1522 : 0 : struct spdk_reduce_vol *vol = meta_req->vol;
1523 : 0 : struct spdk_reduce_meta_desc *mdesc = &vol->params.meta_region_desc[REDUCE_MTYPE_CHUNK_MAP];
1524 : : void *chunk_array;
1525 : 0 : struct spdk_reduce_chunk_map *chunk = NULL;
1526 : : /* chunk_off chunk_count describes the location of the chunkmap contained in this metadata block
1527 : : * among all chunkmaps in the reduce volume. Just like offset and length.
1528 : : */
1529 : : uint32_t chunk_off, chunk_count, chunk_size, chunk_idx, next_mblksn, chunkmap_idx, i, io_unit;
1530 : : uint64_t io_unit_index;
1531 : :
1532 [ # # ]: 0 : if (reduce_errno != 0) {
1533 : 0 : goto error;
1534 : : }
1535 : :
1536 : 0 : chunk_size = mdesc->size_per_elem;
1537 : 0 : chunk_off = (meta_req->mblk_sn - mdesc->offset) * mdesc->elems_per_mblk;
1538 : 0 : chunk_count = mdesc->elems_per_mblk;
1539 : :
1540 : 0 : chunk_array = _reduce_get_meta_elem_addr(meta_req);
1541 [ # # ]: 0 : for (i = 0; i < chunk_count; i++) {
1542 : 0 : chunk_idx = chunk_off + i;
1543 : : /* one chunkmap metablock contains lots of chunkmap, skip unused chunks */
1544 [ # # ]: 0 : if (0 == spdk_bit_array_get(vol->allocated_chunk_maps, chunk_idx)) {
1545 : 0 : continue;
1546 : : }
1547 : 0 : chunk = (struct spdk_reduce_chunk_map *)(chunk_array + i * chunk_size);
1548 [ # # ]: 0 : for (io_unit = 0; io_unit < vol->backing_io_units_per_chunk; io_unit++) {
1549 : 0 : io_unit_index = chunk->io_unit_index[io_unit];
1550 [ # # ]: 0 : if (io_unit_index == REDUCE_EMPTY_MAP_ENTRY) {
1551 : 0 : continue;
1552 : : }
1553 : 0 : spdk_bit_array_set(vol->allocated_backing_io_units, io_unit_index);
1554 : : }
1555 : : }
1556 : : /* we only read the chunkmap mblks which contain used chunks */
1557 [ # # ]: 0 : assert(chunk);
1558 : 0 : _reduce_metablock_cache_read_done_update(meta_req, reduce_errno);
1559 : :
1560 : 0 : chunkmap_idx = spdk_bit_array_find_first_set(vol->allocated_chunk_maps, chunk_off + chunk_count);
1561 [ # # ]: 0 : if (chunkmap_idx == UINT32_MAX) {
1562 : 0 : SPDK_NOTICELOG("vol:%p io unit bitarray load finish\n", vol);
1563 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1564 : 0 : _init_load_cleanup(NULL, load_ctx);
1565 : 0 : return;
1566 : : }
1567 : :
1568 : : /* load next chunkmap mblks */
1569 : 0 : next_mblksn = _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, chunkmap_idx);
1570 : 0 : _load_allocated_backing_io_unit(load_ctx, next_mblksn);
1571 : 0 : return;
1572 : :
1573 : 0 : error:
1574 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, NULL, reduce_errno);
1575 : 0 : _init_load_cleanup(vol, load_ctx);
1576 : : }
1577 : :
1578 : : static void
1579 : 0 : _load_allocated_backing_io_unit(struct reduce_init_load_ctx *load_ctx, uint32_t mblksn)
1580 : : {
1581 : 0 : struct spdk_reduce_vol *vol = load_ctx->vol;
1582 : : struct reduce_meta_request *meta_req;
1583 : : uint32_t next_mblksn, chunkmap_idx;
1584 : :
1585 [ # # ]: 0 : if (mblksn == UINT32_MAX) {
1586 : 0 : chunkmap_idx = spdk_bit_array_find_first_set(vol->allocated_chunk_maps, 0);
1587 [ # # ]: 0 : if (chunkmap_idx == UINT32_MAX) {
1588 : : /* means the compress volume is empty */
1589 : 0 : SPDK_NOTICELOG("vol:%p backing io units don't need load\n", vol);
1590 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1591 : 0 : _init_load_cleanup(NULL, load_ctx);
1592 : 0 : return;
1593 : : }
1594 : 0 : next_mblksn = _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, chunkmap_idx);
1595 : : } else {
1596 : 0 : next_mblksn = mblksn;
1597 : : }
1598 : :
1599 : 0 : meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_CHUNK_MAP,
1600 : : next_mblksn, 0,
1601 : : _load_allocated_backing_io_unit_cb);
1602 : 0 : meta_req->restore_ctx.load_ctx.load_ctx = load_ctx;
1603 : 0 : _reduce_metablock_cache_read(meta_req);
1604 : : }
1605 : :
1606 : : static void _load_allocated_chunk_map_by_md(struct reduce_init_load_ctx *load_ctx, uint32_t mblksn);
1607 : :
1608 : : /* restore the vol->allocated_chunk_maps by meta blocks */
1609 : : static void
1610 : 0 : _load_allocated_chunk_map_cb(void *cb_arg, int reduce_errno)
1611 : : {
1612 : 0 : struct reduce_meta_request *meta_req = cb_arg;
1613 : 0 : struct reduce_init_load_ctx *load_ctx = meta_req->restore_ctx.load_ctx.load_ctx;
1614 : 0 : struct spdk_reduce_vol *vol = meta_req->vol;
1615 : 0 : struct spdk_reduce_meta_desc *mdesc = &vol->params.meta_region_desc[REDUCE_MTYPE_LOGICAL_MAP];
1616 : : uint64_t *logical_map_array, logical_map;
1617 : : uint32_t next_mblksn;
1618 : :
1619 [ # # ]: 0 : if (reduce_errno != 0) {
1620 : 0 : goto error;
1621 : : }
1622 : :
1623 : 0 : logical_map_array = _reduce_get_meta_elem_addr(meta_req);
1624 [ # # ]: 0 : for (int i = 0; i < mdesc->elems_per_mblk; i++) {
1625 : 0 : logical_map = logical_map_array[i];
1626 [ # # ]: 0 : if (logical_map == REDUCE_EMPTY_MAP_ENTRY) {
1627 : 0 : continue;
1628 : : }
1629 : 0 : spdk_bit_array_set(vol->allocated_chunk_maps, logical_map);
1630 : : }
1631 : :
1632 : 0 : next_mblksn = meta_req->mblk_sn + 1;
1633 : 0 : _reduce_metablock_cache_read_done_update(meta_req, reduce_errno);
1634 : :
1635 [ # # ]: 0 : if (next_mblksn == mdesc->offset + mdesc->length) {
1636 : 0 : SPDK_NOTICELOG("vol:%p logical map bitarray load finish\n", vol);
1637 : 0 : _load_allocated_backing_io_unit(load_ctx, UINT32_MAX);
1638 : 0 : return;
1639 : : }
1640 : :
1641 : 0 : _load_allocated_chunk_map_by_md(load_ctx, next_mblksn);
1642 : 0 : return;
1643 : :
1644 : 0 : error:
1645 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, NULL, reduce_errno);
1646 : 0 : _init_load_cleanup(vol, load_ctx);
1647 : : }
1648 : :
1649 : : static void
1650 : 0 : _load_allocated_chunk_map_by_md(struct reduce_init_load_ctx *load_ctx, uint32_t mblksn)
1651 : : {
1652 : 0 : struct spdk_reduce_vol *vol = load_ctx->vol;
1653 : 0 : struct spdk_reduce_meta_desc *md_desc = &vol->params.meta_region_desc[REDUCE_MTYPE_LOGICAL_MAP];
1654 : : struct reduce_meta_request *meta_req;
1655 [ # # ]: 0 : uint32_t next_mblksn = mblksn == UINT32_MAX ? md_desc->offset : mblksn;
1656 : :
1657 : 0 : meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_LOGICAL_MAP,
1658 : : next_mblksn, 0,
1659 : : _load_allocated_chunk_map_cb);
1660 : 0 : meta_req->restore_ctx.load_ctx.load_ctx = load_ctx;
1661 : 0 : _reduce_metablock_cache_read(meta_req);
1662 : 0 : }
1663 : :
1664 : : static void destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno);
1665 : :
1666 : : static void
1667 : 449 : _load_read_super_and_path_cpl(void *cb_arg, int reduce_errno)
1668 : : {
1669 : 449 : struct reduce_init_load_ctx *load_ctx = cb_arg;
1670 : 449 : struct spdk_reduce_vol *vol = load_ctx->vol;
1671 : : uint64_t backing_dev_size;
1672 : : uint64_t i, num_chunks, logical_map_index;
1673 : : struct spdk_reduce_chunk_map *chunk;
1674 : : size_t mapped_len;
1675 : : uint32_t j;
1676 : : int rc;
1677 : :
1678 [ - + ]: 449 : if (reduce_errno != 0) {
1679 : 0 : rc = reduce_errno;
1680 : 0 : goto error;
1681 : : }
1682 : :
1683 : 449 : rc = _alloc_zero_buff();
1684 [ - + ]: 449 : if (rc) {
1685 : 0 : goto error;
1686 : : }
1687 : :
1688 [ - + + + ]: 449 : if (memcmp(vol->backing_super->signature,
1689 : : SPDK_REDUCE_SIGNATURE,
1690 : : sizeof(vol->backing_super->signature)) != 0) {
1691 : : /* This backing device isn't a libreduce backing device. */
1692 : 427 : rc = -EILSEQ;
1693 : 427 : goto error;
1694 : : }
1695 : :
1696 : : /* If the cb_fn is destroy_load_cb, it means we are wanting to destroy this compress bdev.
1697 : : * So don't bother getting the volume ready to use - invoke the callback immediately
1698 : : * so destroy_load_cb can delete the metadata off of the block device and delete the
1699 : : * persistent memory file if it exists.
1700 : : */
1701 [ - + - + ]: 22 : memcpy(vol->pm_file.path, load_ctx->path, sizeof(vol->pm_file.path));
1702 [ + - ]: 22 : if (load_ctx->cb_fn == (*destroy_load_cb)) {
1703 : 22 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1704 : 22 : _init_load_cleanup(NULL, load_ctx);
1705 : 22 : return;
1706 : : }
1707 : :
1708 [ # # ]: 0 : assert(vol->backing_super->params.meta_builtin == _check_meta_builtin(vol->pm_file.path));
1709 : :
1710 [ # # # # ]: 0 : memcpy(&vol->params, &vol->backing_super->params, sizeof(vol->params));
1711 [ # # ]: 0 : vol->backing_io_units_per_chunk = vol->params.chunk_size / vol->params.backing_io_unit_size;
1712 [ # # ]: 0 : vol->logical_blocks_per_chunk = vol->params.chunk_size / vol->params.logical_block_size;
1713 [ # # ]: 0 : vol->backing_lba_per_io_unit = vol->params.backing_io_unit_size / vol->backing_dev->blocklen;
1714 : :
1715 : 0 : rc = _allocate_bit_arrays(vol);
1716 [ # # ]: 0 : if (rc != 0) {
1717 : 0 : goto error;
1718 : : }
1719 : :
1720 : 0 : backing_dev_size = vol->backing_dev->blockcnt * vol->backing_dev->blocklen;
1721 [ # # ]: 0 : if (_get_vol_size(vol->params.chunk_size, backing_dev_size) < vol->params.vol_size) {
1722 : 0 : SPDK_ERRLOG("backing device size %" PRIi64 " smaller than expected\n",
1723 : : backing_dev_size);
1724 : 0 : rc = -EILSEQ;
1725 : 0 : goto error;
1726 : : }
1727 : :
1728 : 0 : rc = _allocate_vol_requests(vol);
1729 [ # # ]: 0 : if (rc != 0) {
1730 : 0 : goto error;
1731 : : }
1732 : :
1733 [ # # ]: 0 : if (vol->params.meta_builtin) {
1734 : 0 : rc = _allocate_vol_metablock_and_metaio(load_ctx->vol);
1735 [ # # ]: 0 : if (rc != 0) {
1736 : 0 : goto error;
1737 : : }
1738 : 0 : _load_allocated_chunk_map_by_md(load_ctx, UINT32_MAX);
1739 : 0 : return;
1740 : : }
1741 : :
1742 : 0 : vol->pm_file.size = _get_pm_file_size(&vol->params);
1743 : 0 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, 0, 0, 0, &mapped_len,
1744 : : &vol->pm_file.pm_is_pmem);
1745 [ # # ]: 0 : if (vol->pm_file.pm_buf == NULL) {
1746 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n", vol->pm_file.path, strerror(errno));
1747 : 0 : rc = -errno;
1748 : 0 : goto error;
1749 : : }
1750 : :
1751 [ # # ]: 0 : if (vol->pm_file.size != mapped_len) {
1752 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
1753 : : vol->pm_file.size, mapped_len);
1754 : 0 : rc = -ENOMEM;
1755 : 0 : goto error;
1756 : : }
1757 : :
1758 : 0 : _initialize_vol_pm_pointers(vol);
1759 : :
1760 [ # # ]: 0 : num_chunks = vol->params.vol_size / vol->params.chunk_size;
1761 [ # # ]: 0 : for (i = 0; i < num_chunks; i++) {
1762 : 0 : logical_map_index = vol->pm_logical_map[i];
1763 [ # # ]: 0 : if (logical_map_index == REDUCE_EMPTY_MAP_ENTRY) {
1764 : 0 : continue;
1765 : : }
1766 : 0 : spdk_bit_array_set(vol->allocated_chunk_maps, logical_map_index);
1767 : 0 : chunk = _reduce_vol_get_chunk_map(vol, logical_map_index);
1768 [ # # ]: 0 : for (j = 0; j < vol->backing_io_units_per_chunk; j++) {
1769 [ # # ]: 0 : if (chunk->io_unit_index[j] != REDUCE_EMPTY_MAP_ENTRY) {
1770 : 0 : spdk_bit_array_set(vol->allocated_backing_io_units, chunk->io_unit_index[j]);
1771 : 0 : vol->info.allocated_io_units++;
1772 : : }
1773 : : }
1774 : : }
1775 : :
1776 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1777 : : /* Only clean up the ctx - the vol has been passed to the application
1778 : : * for use now that volume load was successful.
1779 : : */
1780 : 0 : _init_load_cleanup(NULL, load_ctx);
1781 : 0 : return;
1782 : :
1783 : 427 : error:
1784 : 427 : load_ctx->cb_fn(load_ctx->cb_arg, NULL, rc);
1785 : 427 : _init_load_cleanup(vol, load_ctx);
1786 : : }
1787 : :
1788 : : void
1789 : 449 : spdk_reduce_vol_load(struct spdk_reduce_backing_dev *backing_dev,
1790 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
1791 : : {
1792 : : struct spdk_reduce_vol *vol;
1793 : : struct reduce_init_load_ctx *load_ctx;
1794 : : struct spdk_reduce_backing_io *backing_io;
1795 : :
1796 [ - + ]: 449 : if (backing_dev->submit_backing_io == NULL) {
1797 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
1798 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1799 : 0 : return;
1800 : : }
1801 : :
1802 : 449 : vol = calloc(1, sizeof(*vol));
1803 [ - + ]: 449 : if (vol == NULL) {
1804 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1805 : 0 : return;
1806 : : }
1807 : :
1808 : 449 : TAILQ_INIT(&vol->free_requests);
1809 : 449 : RB_INIT(&vol->executing_requests);
1810 : 449 : TAILQ_INIT(&vol->queued_requests);
1811 : 449 : queue_init(&vol->free_chunks_queue);
1812 : 449 : queue_init(&vol->free_backing_blocks_queue);
1813 : 449 : TAILQ_INIT(&vol->metablocks_free);
1814 : 449 : TAILQ_INIT(&vol->metablocks_lru);
1815 : 449 : RB_INIT(&vol->metablocks_caching);
1816 : 449 : TAILQ_INIT(&vol->free_meta_request);
1817 : :
1818 : 449 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 64, NULL,
1819 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1820 [ - + ]: 449 : if (vol->backing_super == NULL) {
1821 : 0 : _init_load_cleanup(vol, NULL);
1822 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1823 : 0 : return;
1824 : : }
1825 : :
1826 : 449 : vol->backing_dev = backing_dev;
1827 : :
1828 : 449 : load_ctx = calloc(1, sizeof(*load_ctx));
1829 [ - + ]: 449 : if (load_ctx == NULL) {
1830 : 0 : _init_load_cleanup(vol, NULL);
1831 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1832 : 0 : return;
1833 : : }
1834 : :
1835 : 449 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1836 [ - + ]: 449 : if (backing_io == NULL) {
1837 : 0 : _init_load_cleanup(vol, load_ctx);
1838 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1839 : 0 : return;
1840 : : }
1841 : :
1842 : 449 : load_ctx->backing_io = backing_io;
1843 : :
1844 : 449 : load_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 64, NULL,
1845 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1846 [ - + ]: 449 : if (load_ctx->path == NULL) {
1847 : 0 : _init_load_cleanup(vol, load_ctx);
1848 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1849 : 0 : return;
1850 : : }
1851 : :
1852 : 449 : load_ctx->vol = vol;
1853 : 449 : load_ctx->cb_fn = cb_fn;
1854 : 449 : load_ctx->cb_arg = cb_arg;
1855 : :
1856 : 449 : load_ctx->iov[0].iov_base = vol->backing_super;
1857 : 449 : load_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
1858 : 449 : load_ctx->iov[1].iov_base = load_ctx->path;
1859 : 449 : load_ctx->iov[1].iov_len = REDUCE_PATH_MAX;
1860 : 449 : backing_io->dev = vol->backing_dev;
1861 : 449 : backing_io->iov = load_ctx->iov;
1862 : 449 : backing_io->iovcnt = LOAD_IOV_COUNT;
1863 : 449 : backing_io->lba = 0;
1864 : 449 : backing_io->lba_count = (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
1865 [ - + ]: 449 : vol->backing_dev->blocklen;
1866 : 449 : backing_io->backing_cb_args = &load_ctx->backing_cb_args;
1867 : 449 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
1868 : :
1869 : 449 : load_ctx->backing_cb_args.cb_fn = _load_read_super_and_path_cpl;
1870 : 449 : load_ctx->backing_cb_args.cb_arg = load_ctx;
1871 : 449 : vol->backing_dev->submit_backing_io(backing_io);
1872 : : }
1873 : :
1874 : : void
1875 : 44 : spdk_reduce_vol_unload(struct spdk_reduce_vol *vol,
1876 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1877 : : {
1878 [ - + ]: 44 : if (vol == NULL) {
1879 : : /* This indicates a programming error. */
1880 : 0 : assert(false);
1881 : : cb_fn(cb_arg, -EINVAL);
1882 : : return;
1883 : : }
1884 : :
1885 [ - + ]: 44 : if (--g_vol_count == 0) {
1886 : 0 : spdk_free(g_zero_buf);
1887 : : }
1888 [ - + ]: 44 : assert(g_vol_count >= 0);
1889 : 44 : _init_load_cleanup(vol, NULL);
1890 : 44 : cb_fn(cb_arg, 0);
1891 : : }
1892 : :
1893 : : struct reduce_destroy_ctx {
1894 : : spdk_reduce_vol_op_complete cb_fn;
1895 : : void *cb_arg;
1896 : : struct spdk_reduce_vol *vol;
1897 : : struct spdk_reduce_vol_superblock *super;
1898 : : struct iovec iov;
1899 : : struct spdk_reduce_vol_cb_args backing_cb_args;
1900 : : int reduce_errno;
1901 : : char pm_path[REDUCE_PATH_MAX];
1902 : : struct spdk_reduce_backing_io *backing_io;
1903 : : };
1904 : :
1905 : : static void
1906 : 22 : destroy_unload_cpl(void *cb_arg, int reduce_errno)
1907 : : {
1908 : 22 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1909 : :
1910 [ + - ]: 22 : if (destroy_ctx->reduce_errno == 0) {
1911 [ + + - + : 22 : if (!_check_meta_builtin(destroy_ctx->pm_path) && unlink(destroy_ctx->pm_path)) {
- + ]
1912 : 0 : SPDK_ERRLOG("%s could not be unlinked: %s\n",
1913 : : destroy_ctx->pm_path, strerror(errno));
1914 : : }
1915 : : }
1916 : :
1917 : : /* Even if the unload somehow failed, we still pass the destroy_ctx
1918 : : * reduce_errno since that indicates whether or not the volume was
1919 : : * actually destroyed.
1920 : : */
1921 : 22 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, destroy_ctx->reduce_errno);
1922 : 22 : spdk_free(destroy_ctx->super);
1923 : 22 : free(destroy_ctx->backing_io);
1924 : 22 : free(destroy_ctx);
1925 : 22 : }
1926 : :
1927 : : static void
1928 : 22 : _destroy_zero_super_cpl(void *cb_arg, int reduce_errno)
1929 : : {
1930 : 22 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1931 : 22 : struct spdk_reduce_vol *vol = destroy_ctx->vol;
1932 : :
1933 : 22 : destroy_ctx->reduce_errno = reduce_errno;
1934 : 22 : spdk_reduce_vol_unload(vol, destroy_unload_cpl, destroy_ctx);
1935 : 22 : }
1936 : :
1937 : : static void
1938 : 22 : destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
1939 : : {
1940 : 22 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1941 : 22 : struct spdk_reduce_backing_io *backing_io = destroy_ctx->backing_io;
1942 : :
1943 [ - + ]: 22 : if (reduce_errno != 0) {
1944 : 0 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, reduce_errno);
1945 : 0 : spdk_free(destroy_ctx->super);
1946 : 0 : free(destroy_ctx);
1947 : 0 : return;
1948 : : }
1949 : :
1950 : 22 : destroy_ctx->vol = vol;
1951 [ - + - + ]: 22 : memcpy(destroy_ctx->pm_path, vol->pm_file.path, sizeof(destroy_ctx->pm_path));
1952 : 22 : destroy_ctx->iov.iov_base = destroy_ctx->super;
1953 : 22 : destroy_ctx->iov.iov_len = sizeof(*destroy_ctx->super);
1954 : 22 : destroy_ctx->backing_cb_args.cb_fn = _destroy_zero_super_cpl;
1955 : 22 : destroy_ctx->backing_cb_args.cb_arg = destroy_ctx;
1956 : :
1957 : 22 : backing_io->dev = vol->backing_dev;
1958 : 22 : backing_io->iov = &destroy_ctx->iov;
1959 : 22 : backing_io->iovcnt = 1;
1960 : 22 : backing_io->lba = 0;
1961 [ - + ]: 22 : backing_io->lba_count = sizeof(*destroy_ctx->super) / vol->backing_dev->blocklen;
1962 : 22 : backing_io->backing_cb_args = &destroy_ctx->backing_cb_args;
1963 : 22 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1964 : :
1965 : 22 : vol->backing_dev->submit_backing_io(backing_io);
1966 : : }
1967 : :
1968 : : void
1969 : 22 : spdk_reduce_vol_destroy(struct spdk_reduce_backing_dev *backing_dev,
1970 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1971 : : {
1972 : : struct reduce_destroy_ctx *destroy_ctx;
1973 : : struct spdk_reduce_backing_io *backing_io;
1974 : :
1975 : 22 : destroy_ctx = calloc(1, sizeof(*destroy_ctx));
1976 [ - + ]: 22 : if (destroy_ctx == NULL) {
1977 : 0 : cb_fn(cb_arg, -ENOMEM);
1978 : 0 : return;
1979 : : }
1980 : :
1981 : 22 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1982 [ - + ]: 22 : if (backing_io == NULL) {
1983 : 0 : free(destroy_ctx);
1984 : 0 : cb_fn(cb_arg, -ENOMEM);
1985 : 0 : return;
1986 : : }
1987 : :
1988 : 22 : destroy_ctx->backing_io = backing_io;
1989 : :
1990 : 22 : destroy_ctx->super = spdk_zmalloc(sizeof(*destroy_ctx->super), 64, NULL,
1991 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1992 [ - + ]: 22 : if (destroy_ctx->super == NULL) {
1993 : 0 : free(destroy_ctx);
1994 : 0 : free(backing_io);
1995 : 0 : cb_fn(cb_arg, -ENOMEM);
1996 : 0 : return;
1997 : : }
1998 : 22 : destroy_ctx->cb_fn = cb_fn;
1999 : 22 : destroy_ctx->cb_arg = cb_arg;
2000 : 22 : spdk_reduce_vol_load(backing_dev, destroy_load_cb, destroy_ctx);
2001 : : }
2002 : :
2003 : : static bool
2004 : 1503266 : _request_spans_chunk_boundary(struct spdk_reduce_vol *vol, uint64_t offset, uint64_t length)
2005 : : {
2006 : : uint64_t start_chunk, end_chunk;
2007 : :
2008 [ - + ]: 1503266 : start_chunk = offset / vol->logical_blocks_per_chunk;
2009 [ - + ]: 1503266 : end_chunk = (offset + length - 1) / vol->logical_blocks_per_chunk;
2010 : :
2011 : 1503266 : return (start_chunk != end_chunk);
2012 : : }
2013 : :
2014 : : static void _start_unmap_request_full_chunk(void *ctx);
2015 : :
2016 : : static void
2017 : 1112554 : _reduce_vol_complete_req(struct spdk_reduce_vol_request *req, int reduce_errno)
2018 : : {
2019 : : struct spdk_reduce_vol_request *next_req;
2020 : 1112554 : struct spdk_reduce_vol *vol = req->vol;
2021 : :
2022 : 1112554 : req->cb_fn(req->cb_arg, reduce_errno);
2023 : 1112554 : RB_REMOVE(executing_req_tree, &vol->executing_requests, req);
2024 : :
2025 [ + + ]: 12773844 : TAILQ_FOREACH(next_req, &vol->queued_requests, tailq) {
2026 [ + + ]: 12313498 : if (next_req->logical_map_index == req->logical_map_index) {
2027 [ + + ]: 652208 : TAILQ_REMOVE(&vol->queued_requests, next_req, tailq);
2028 [ + + ]: 652208 : if (next_req->type == REDUCE_IO_READV) {
2029 : 284292 : _start_readv_request(next_req);
2030 [ + - ]: 367916 : } else if (next_req->type == REDUCE_IO_WRITEV) {
2031 : 367916 : _start_writev_request(next_req);
2032 : : } else {
2033 [ # # ]: 0 : assert(next_req->type == REDUCE_IO_UNMAP);
2034 : 0 : _start_unmap_request_full_chunk(next_req);
2035 : : }
2036 : 652208 : break;
2037 : : }
2038 : : }
2039 : :
2040 [ + - ]: 1112554 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
2041 : 1112554 : }
2042 : :
2043 : : static void
2044 : 524938 : _reduce_vol_reset_chunk(struct spdk_reduce_vol *vol, uint64_t chunk_map_index,
2045 : : struct spdk_reduce_chunk_map *chunkaddr)
2046 : : {
2047 : : struct spdk_reduce_chunk_map *chunk;
2048 : : uint64_t index;
2049 : : bool success;
2050 : : uint32_t i;
2051 : :
2052 [ - + ]: 524938 : assert(chunk_map_index != REDUCE_EMPTY_MAP_ENTRY);
2053 [ + + ]: 524938 : if (chunkaddr == NULL) {
2054 : 341878 : chunk = _reduce_vol_get_chunk_map(vol, chunk_map_index);
2055 : : } else {
2056 : 183060 : chunk = chunkaddr;
2057 : : }
2058 [ - + - + ]: 524938 : SPDK_INFOLOG(reduce, "release %lu. %lu %lu %lu %lu\n",
2059 : : chunk_map_index, chunk->io_unit_index[0], chunk->io_unit_index[1], chunk->io_unit_index[2],
2060 : : chunk->io_unit_index[3]);
2061 [ + - ]: 1049876 : for (i = 0; i < vol->backing_io_units_per_chunk; i++) {
2062 : 1049876 : index = chunk->io_unit_index[i];
2063 [ + + ]: 1049876 : if (index == REDUCE_EMPTY_MAP_ENTRY) {
2064 : 524938 : break;
2065 : : }
2066 [ - + ]: 524938 : assert(spdk_bit_array_get(vol->allocated_backing_io_units,
2067 : : index) == true);
2068 : 524938 : spdk_bit_array_clear(vol->allocated_backing_io_units, index);
2069 : 524938 : vol->info.allocated_io_units--;
2070 : 524938 : success = queue_enqueue(&vol->free_backing_blocks_queue, index);
2071 [ + + + + ]: 524938 : if (!success && index < vol->find_block_offset) {
2072 : 17 : vol->find_block_offset = index;
2073 : : }
2074 : 524938 : chunk->io_unit_index[i] = REDUCE_EMPTY_MAP_ENTRY;
2075 : : }
2076 : 524938 : success = queue_enqueue(&vol->free_chunks_queue, chunk_map_index);
2077 [ + + + + ]: 524938 : if (!success && chunk_map_index < vol->find_chunk_offset) {
2078 : 17 : vol->find_chunk_offset = chunk_map_index;
2079 : : }
2080 : 524938 : spdk_bit_array_clear(vol->allocated_chunk_maps, chunk_map_index);
2081 : 524938 : }
2082 : :
2083 : : static inline void
2084 : 606192 : convert_supply_read_to_write(struct reduce_meta_request *meta_req)
2085 : : {
2086 [ - + + - : 606192 : assert(meta_req->is_write && meta_req->supply_read);
- + + - ]
2087 : 606192 : meta_req->supply_read = false;
2088 : 606192 : }
2089 : :
2090 : : static void
2091 : 0 : _write_done_mapinfo_update_error_process(struct reduce_meta_request *meta_req, int error)
2092 : : {
2093 : 0 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.req_ctx.req;
2094 : : char uuid_str[SPDK_UUID_STRING_LEN];
2095 : :
2096 : : /* update chunk map or logical fail, we need to release the bit from
2097 : : * allocated_chunk_maps and allocated_backing_io_units where we want to write.
2098 : : */
2099 : 0 : _reduce_vol_reset_chunk(req->vol, req->chunk_map_index, req->chunk);
2100 : 0 : spdk_uuid_fmt_lower(uuid_str, sizeof(uuid_str), spdk_reduce_vol_get_uuid(req->vol));
2101 : 0 : SPDK_ERRLOG("%s write update new mapping error %d, release chunkmap idx: %lu, backing io unit: %lu %lu %lu %lu\n",
2102 : : uuid_str, error, req->chunk_map_index,
2103 : : req->chunk->io_unit_index[0], req->chunk->io_unit_index[1], req->chunk->io_unit_index[2],
2104 : : req->chunk->io_unit_index[3]);
2105 : 0 : req->reduce_errno = error;
2106 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2107 [ # # # # : 0 : if (meta_req->supply_read || !meta_req->is_write) {
# # # # ]
2108 : 0 : _reduce_metablock_cache_read_done_update(meta_req, error);
2109 : : } else {
2110 : 0 : _reduce_metablock_cache_write_done_update(meta_req, error);
2111 : : }
2112 : 0 : }
2113 : :
2114 : : static void
2115 : 423132 : _write_write_done_update_logical_map(void *_meta_req, int error)
2116 : : {
2117 : 423132 : struct reduce_meta_request *meta_req = _meta_req;
2118 : 423132 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.req_ctx.req;
2119 : : uint64_t *logical_map;
2120 : :
2121 [ - + ]: 423132 : if (error != 0) {
2122 : 0 : _write_done_mapinfo_update_error_process(meta_req, error);
2123 : 0 : return;
2124 : : }
2125 : :
2126 [ - + + + ]: 423132 : if (meta_req->supply_read) {
2127 : 211566 : logical_map = (uint64_t *)_reduce_get_meta_elem_addr(meta_req);
2128 : 211566 : *logical_map = req->chunk_map_index;
2129 : 211566 : convert_supply_read_to_write(meta_req);
2130 : 211566 : _reduce_metablock_cache_read_done_update(meta_req, error);
2131 : : } else {
2132 : 211566 : _reduce_metablock_cache_write_done_update(meta_req, error);
2133 : :
2134 : 211566 : _reduce_vol_complete_req(req, error);
2135 : : }
2136 : : }
2137 : :
2138 : : static void
2139 : 423132 : _write_write_done_update_chunk_map(void *_meta_req, int error)
2140 : : {
2141 : 423132 : struct reduce_meta_request *meta_req = _meta_req;
2142 : 423132 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.req_ctx.req;
2143 : 423132 : struct spdk_reduce_vol *vol = req->vol;
2144 : : struct spdk_reduce_chunk_map *chunk;
2145 : :
2146 [ - + ]: 423132 : if (error != 0) {
2147 : 0 : _write_done_mapinfo_update_error_process(meta_req, error);
2148 : 0 : return;
2149 : : }
2150 : :
2151 [ - + + + ]: 423132 : if (meta_req->supply_read) {
2152 : 211566 : chunk = (struct spdk_reduce_chunk_map *)_reduce_get_meta_elem_addr(meta_req);
2153 [ - + - + ]: 211566 : memcpy(chunk, req->chunk, _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk));
2154 : 211566 : convert_supply_read_to_write(meta_req);
2155 : 211566 : _reduce_metablock_cache_read_done_update(meta_req, error);
2156 : : } else {
2157 : 211566 : _reduce_metablock_cache_write_done_update(meta_req, error);
2158 : 211566 : meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_LOGICAL_MAP,
2159 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
2160 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
2161 : : _write_write_done_update_logical_map);
2162 : 211566 : meta_req->restore_ctx.req_ctx.req = req;
2163 : 211566 : _reduce_metablock_cache_read(meta_req);
2164 : : }
2165 : : }
2166 : :
2167 : : static void
2168 : 366120 : _write_write_done_clear_old_chunkmap(void *_meta_req, int error)
2169 : : {
2170 : 366120 : struct reduce_meta_request *meta_req = _meta_req;
2171 : 366120 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.req_ctx.req;
2172 : 366120 : struct spdk_reduce_vol *vol = req->vol;
2173 : : struct spdk_reduce_chunk_map *chunk_addr;
2174 : :
2175 [ - + ]: 366120 : if (error != 0) {
2176 : : /* If clear the old chunkmap meta fail, we don't consider it will success when update new chunk map.
2177 : : * There is no necessary to continue the rest of process.
2178 : : * We also don't release old chunkmap and old logical map to protect the data when we read chunkmap failed.
2179 : : * So just return error.
2180 : : */
2181 : 0 : _write_done_mapinfo_update_error_process(meta_req, error);
2182 : 0 : return;
2183 : : }
2184 : :
2185 : : /* read the meta elem */
2186 [ - + + + ]: 366120 : if (meta_req->supply_read) {
2187 : 183060 : chunk_addr = (struct spdk_reduce_chunk_map *)_reduce_get_meta_elem_addr(meta_req);
2188 : 183060 : _reduce_vol_reset_chunk(req->vol, req->read_chunk_map_index, chunk_addr);
2189 : :
2190 : : /* meta data write backing */
2191 : 183060 : convert_supply_read_to_write(meta_req);
2192 : 183060 : _reduce_metablock_cache_read_done_update(meta_req, error);
2193 : : } else {
2194 : 183060 : _reduce_metablock_cache_write_done_update(meta_req, error);
2195 : :
2196 : : /* set the new chunk map */
2197 : 183060 : meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_CHUNK_MAP,
2198 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2199 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2200 : : _write_write_done_update_chunk_map);
2201 : 183060 : meta_req->restore_ctx.req_ctx.req = req;
2202 : 183060 : _reduce_metablock_cache_read(meta_req);
2203 : :
2204 : 183060 : return;
2205 : : }
2206 : : }
2207 : :
2208 : : static void
2209 : 596542 : _write_write_done(void *_req, int reduce_errno)
2210 : : {
2211 : 596542 : struct spdk_reduce_vol_request *req = _req;
2212 : 596542 : struct spdk_reduce_vol *vol = req->vol;
2213 : : uint64_t old_chunk_map_index;
2214 : : struct reduce_meta_request *meta_req;
2215 : :
2216 [ - + ]: 596542 : if (reduce_errno != 0) {
2217 : 0 : req->reduce_errno = reduce_errno;
2218 : : }
2219 : :
2220 [ - + ]: 596542 : assert(req->num_backing_ops > 0);
2221 [ - + ]: 596542 : if (--req->num_backing_ops > 0) {
2222 : 0 : return;
2223 : : }
2224 : :
2225 [ - + ]: 596542 : if (req->reduce_errno != 0) {
2226 [ # # ]: 0 : if (!vol->params.meta_builtin) {
2227 : 0 : _reduce_vol_reset_chunk(vol, req->chunk_map_index, NULL);
2228 : : }
2229 : : /* for meta_builtin == true, we don't need handle the case, because we haven't change the metadata yet */
2230 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2231 : 0 : return;
2232 : : }
2233 : :
2234 [ + + ]: 596542 : if (vol->params.meta_builtin) {
2235 [ + + ]: 211566 : if (req->read_chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
2236 : 183060 : meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_CHUNK_MAP,
2237 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->read_chunk_map_index),
2238 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->read_chunk_map_index),
2239 : : _write_write_done_clear_old_chunkmap);
2240 : 183060 : meta_req->restore_ctx.req_ctx.req = req;
2241 : 183060 : _reduce_metablock_cache_read(meta_req);
2242 : 183060 : return;
2243 : : } else {
2244 : : /* set the new chunk map */
2245 : 28506 : meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_CHUNK_MAP,
2246 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2247 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2248 : : _write_write_done_update_chunk_map);
2249 : 28506 : meta_req->restore_ctx.req_ctx.req = req;
2250 : 28506 : _reduce_metablock_cache_read(meta_req);
2251 : 28506 : return;
2252 : : }
2253 : : }
2254 : :
2255 : 384976 : old_chunk_map_index = vol->pm_logical_map[req->logical_map_index];
2256 [ + + ]: 384976 : if (old_chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
2257 : 341878 : _reduce_vol_reset_chunk(vol, old_chunk_map_index, NULL);
2258 : : }
2259 : :
2260 : : /*
2261 : : * We don't need to persist the clearing of the old chunk map here. The old chunk map
2262 : : * becomes invalid after we update the logical map, since the old chunk map will no
2263 : : * longer have a reference to it in the logical map.
2264 : : */
2265 : :
2266 : : /* Persist the new chunk map. This must be persisted before we update the logical map. */
2267 : 384976 : _reduce_persist(vol, req->chunk,
2268 : 384976 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk));
2269 : :
2270 : 384976 : vol->pm_logical_map[req->logical_map_index] = req->chunk_map_index;
2271 : :
2272 : 384976 : _reduce_persist(vol, &vol->pm_logical_map[req->logical_map_index], sizeof(uint64_t));
2273 : :
2274 : 384976 : _reduce_vol_complete_req(req, 0);
2275 : : }
2276 : :
2277 : : static struct spdk_reduce_backing_io *
2278 : 1406824 : _reduce_vol_req_get_backing_io(struct spdk_reduce_vol_request *req, uint32_t index)
2279 : : {
2280 : 1406824 : struct spdk_reduce_backing_dev *backing_dev = req->vol->backing_dev;
2281 : : struct spdk_reduce_backing_io *backing_io;
2282 : :
2283 : 1406824 : backing_io = (struct spdk_reduce_backing_io *)((uint8_t *)req->backing_io +
2284 : 1406824 : (sizeof(*backing_io) + backing_dev->user_ctx_size) * index);
2285 : :
2286 : 1406824 : return backing_io;
2287 : :
2288 : : }
2289 : :
2290 : : struct reduce_merged_io_desc {
2291 : : uint64_t io_unit_index;
2292 : : uint32_t num_io_units;
2293 : : };
2294 : :
2295 : : static void
2296 : 0 : _issue_backing_ops_without_merge(struct spdk_reduce_vol_request *req, struct spdk_reduce_vol *vol,
2297 : : reduce_request_fn next_fn, bool is_write)
2298 : : {
2299 : : struct iovec *iov;
2300 : : struct spdk_reduce_backing_io *backing_io;
2301 : : uint8_t *buf;
2302 : : uint32_t i;
2303 : :
2304 [ # # # # ]: 0 : if (req->chunk_is_compressed) {
2305 : 0 : iov = req->comp_buf_iov;
2306 : 0 : buf = req->comp_buf;
2307 : : } else {
2308 : 0 : iov = req->decomp_buf_iov;
2309 : 0 : buf = req->decomp_buf;
2310 : : }
2311 : :
2312 : 0 : req->num_backing_ops = req->num_io_units;
2313 : 0 : req->backing_cb_args.cb_fn = next_fn;
2314 : 0 : req->backing_cb_args.cb_arg = req;
2315 [ # # ]: 0 : for (i = 0; i < req->num_io_units; i++) {
2316 : 0 : backing_io = _reduce_vol_req_get_backing_io(req, i);
2317 : 0 : iov[i].iov_base = buf + i * vol->params.backing_io_unit_size;
2318 : 0 : iov[i].iov_len = vol->params.backing_io_unit_size;
2319 : 0 : backing_io->dev = vol->backing_dev;
2320 : 0 : backing_io->iov = &iov[i];
2321 : 0 : backing_io->iovcnt = 1;
2322 : 0 : backing_io->lba = req->chunk->io_unit_index[i] * vol->backing_lba_per_io_unit;
2323 : 0 : backing_io->lba_count = vol->backing_lba_per_io_unit;
2324 : 0 : backing_io->backing_cb_args = &req->backing_cb_args;
2325 [ # # ]: 0 : if (is_write) {
2326 : 0 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
2327 : : } else {
2328 : 0 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
2329 : : }
2330 : 0 : vol->backing_dev->submit_backing_io(backing_io);
2331 : : }
2332 : 0 : }
2333 : :
2334 : : static void
2335 : 1406824 : _issue_backing_ops(struct spdk_reduce_vol_request *req, struct spdk_reduce_vol *vol,
2336 : : reduce_request_fn next_fn, bool is_write)
2337 : : {
2338 : : struct iovec *iov;
2339 : : struct spdk_reduce_backing_io *backing_io;
2340 : : struct reduce_merged_io_desc merged_io_desc[4];
2341 : : uint8_t *buf;
2342 : 1406824 : bool merge = false;
2343 : 1406824 : uint32_t num_io = 0;
2344 : 1406824 : uint32_t io_unit_counts = 0;
2345 : 1406824 : uint32_t merged_io_idx = 0;
2346 : : uint32_t i;
2347 : :
2348 : : /* The merged_io_desc value is defined here to contain four elements,
2349 : : * and the chunk size must be four times the maximum of the io unit.
2350 : : * if chunk size is too big, don't merge IO.
2351 : : */
2352 [ - + ]: 1406824 : if (vol->backing_io_units_per_chunk > 4) {
2353 : 0 : _issue_backing_ops_without_merge(req, vol, next_fn, is_write);
2354 : 0 : return;
2355 : : }
2356 : :
2357 [ - + + - ]: 1406824 : if (req->chunk_is_compressed) {
2358 : 1406824 : iov = req->comp_buf_iov;
2359 : 1406824 : buf = req->comp_buf;
2360 : : } else {
2361 : 0 : iov = req->decomp_buf_iov;
2362 : 0 : buf = req->decomp_buf;
2363 : : }
2364 : :
2365 [ + - ]: 1406824 : for (i = 0; i < req->num_io_units; i++) {
2366 [ + - ]: 1406824 : if (!merge) {
2367 : 1406824 : merged_io_desc[merged_io_idx].io_unit_index = req->chunk->io_unit_index[i];
2368 : 1406824 : merged_io_desc[merged_io_idx].num_io_units = 1;
2369 : 1406824 : num_io++;
2370 : : }
2371 : :
2372 [ + - ]: 1406824 : if (i + 1 == req->num_io_units) {
2373 : 1406824 : break;
2374 : : }
2375 : :
2376 [ # # ]: 0 : if (req->chunk->io_unit_index[i] + 1 == req->chunk->io_unit_index[i + 1]) {
2377 : 0 : merged_io_desc[merged_io_idx].num_io_units += 1;
2378 : 0 : merge = true;
2379 : 0 : continue;
2380 : : }
2381 : 0 : merge = false;
2382 : 0 : merged_io_idx++;
2383 : : }
2384 : :
2385 : 1406824 : req->num_backing_ops = num_io;
2386 : 1406824 : req->backing_cb_args.cb_fn = next_fn;
2387 : 1406824 : req->backing_cb_args.cb_arg = req;
2388 [ + + ]: 2813648 : for (i = 0; i < num_io; i++) {
2389 : 1406824 : backing_io = _reduce_vol_req_get_backing_io(req, i);
2390 : 1406824 : iov[i].iov_base = buf + io_unit_counts * vol->params.backing_io_unit_size;
2391 : 1406824 : iov[i].iov_len = vol->params.backing_io_unit_size * merged_io_desc[i].num_io_units;
2392 : 1406824 : backing_io->dev = vol->backing_dev;
2393 : 1406824 : backing_io->iov = &iov[i];
2394 : 1406824 : backing_io->iovcnt = 1;
2395 : 1406824 : backing_io->lba = merged_io_desc[i].io_unit_index * vol->backing_lba_per_io_unit;
2396 : 1406824 : backing_io->lba_count = vol->backing_lba_per_io_unit * merged_io_desc[i].num_io_units;
2397 : 1406824 : backing_io->backing_cb_args = &req->backing_cb_args;
2398 [ + + ]: 1406824 : if (is_write) {
2399 : 596542 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
2400 : : } else {
2401 : 810282 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
2402 : : }
2403 : 1406824 : vol->backing_dev->submit_backing_io(backing_io);
2404 : :
2405 : : /* Collects the number of processed I/O. */
2406 : 1406824 : io_unit_counts += merged_io_desc[i].num_io_units;
2407 : : }
2408 : : }
2409 : :
2410 : : static void
2411 : 596542 : _reduce_vol_write_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn,
2412 : : uint32_t compressed_size)
2413 : : {
2414 : 596542 : struct spdk_reduce_vol *vol = req->vol;
2415 : : uint32_t i;
2416 : 596542 : uint64_t chunk_offset, remainder, free_index, total_len = 0;
2417 : : uint8_t *buf;
2418 : : bool success;
2419 : : int j;
2420 : :
2421 : 596542 : success = queue_dequeue(&vol->free_chunks_queue, &free_index);
2422 [ + + ]: 596542 : if (success) {
2423 : 524171 : req->chunk_map_index = free_index;
2424 : : } else {
2425 : 72371 : req->chunk_map_index = spdk_bit_array_find_first_clear(vol->allocated_chunk_maps,
2426 : 72371 : vol->find_chunk_offset);
2427 : 72371 : vol->find_chunk_offset = req->chunk_map_index + 1;
2428 : : }
2429 : :
2430 : : /* TODO: fail if no chunk map found - but really this should not happen if we
2431 : : * size the number of requests similarly to number of extra chunk maps
2432 : : */
2433 [ - + ]: 596542 : assert(req->chunk_map_index != REDUCE_EMPTY_MAP_ENTRY);
2434 : 596542 : spdk_bit_array_set(vol->allocated_chunk_maps, req->chunk_map_index);
2435 : :
2436 [ + + ]: 596542 : if (vol->params.meta_builtin) {
2437 : : /* when use builtin meta, need the chunk memory from heap */
2438 : 211566 : req->chunk = req->prealloc_chunk;
2439 [ - + ]: 211566 : memset(req->chunk->io_unit_index, 0XFF, sizeof(uint64_t) * vol->backing_io_units_per_chunk);
2440 : : } else {
2441 : 384976 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
2442 : : }
2443 : 596542 : req->num_io_units = spdk_divide_round_up(compressed_size,
2444 : 596542 : vol->params.backing_io_unit_size);
2445 : 596542 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
2446 : 596542 : req->chunk->compressed_size =
2447 [ - + + - ]: 596542 : req->chunk_is_compressed ? compressed_size : vol->params.chunk_size;
2448 : :
2449 : : /* if the chunk is uncompressed we need to copy the data from the host buffers. */
2450 [ - + - + ]: 596542 : if (req->chunk_is_compressed == false) {
2451 [ # # ]: 0 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2452 : 0 : buf = req->decomp_buf;
2453 : 0 : total_len = chunk_offset * vol->params.logical_block_size;
2454 : :
2455 : : /* zero any offset into chunk */
2456 [ # # # # : 0 : if (req->rmw == false && chunk_offset) {
# # ]
2457 [ # # ]: 0 : memset(buf, 0, total_len);
2458 : : }
2459 : 0 : buf += total_len;
2460 : :
2461 : : /* copy the data */
2462 [ # # ]: 0 : for (j = 0; j < req->iovcnt; j++) {
2463 [ # # # # ]: 0 : memcpy(buf, req->iov[j].iov_base, req->iov[j].iov_len);
2464 : 0 : buf += req->iov[j].iov_len;
2465 : 0 : total_len += req->iov[j].iov_len;
2466 : : }
2467 : :
2468 : : /* zero any remainder */
2469 : 0 : remainder = vol->params.chunk_size - total_len;
2470 : 0 : total_len += remainder;
2471 [ # # # # : 0 : if (req->rmw == false && remainder) {
# # ]
2472 [ # # ]: 0 : memset(buf, 0, remainder);
2473 : : }
2474 [ # # ]: 0 : assert(total_len == vol->params.chunk_size);
2475 : : }
2476 : :
2477 [ + + ]: 1193084 : for (i = 0; i < req->num_io_units; i++) {
2478 : 596542 : success = queue_dequeue(&vol->free_backing_blocks_queue, &free_index);
2479 [ + + ]: 596542 : if (success) {
2480 : 524171 : req->chunk->io_unit_index[i] = free_index;
2481 : : } else {
2482 : 72371 : req->chunk->io_unit_index[i] = spdk_bit_array_find_first_clear(vol->allocated_backing_io_units,
2483 : 72371 : vol->find_block_offset);
2484 : 72371 : vol->find_block_offset = req->chunk->io_unit_index[i] + 1;
2485 : : }
2486 : : /* TODO: fail if no backing block found - but really this should also not
2487 : : * happen (see comment above).
2488 : : */
2489 [ - + ]: 596542 : assert(req->chunk->io_unit_index[i] != REDUCE_EMPTY_MAP_ENTRY);
2490 : 596542 : spdk_bit_array_set(vol->allocated_backing_io_units, req->chunk->io_unit_index[i]);
2491 : 596542 : vol->info.allocated_io_units++;
2492 : : }
2493 [ - + - + ]: 596542 : SPDK_INFOLOG(reduce, "datareq %p, use %lu. %lu %lu %lu %lu\n", req,
2494 : : req->chunk_map_index, req->chunk->io_unit_index[0], req->chunk->io_unit_index[1],
2495 : : req->chunk->io_unit_index[2], req->chunk->io_unit_index[3]);
2496 : 596542 : _issue_backing_ops(req, vol, next_fn, true /* write */);
2497 : 596542 : }
2498 : :
2499 : : static void
2500 : 596542 : _write_compress_done(void *_req, int reduce_errno)
2501 : : {
2502 : 596542 : struct spdk_reduce_vol_request *req = _req;
2503 : :
2504 : : /* Negative reduce_errno indicates failure for compression operations.
2505 : : * Just write the uncompressed data instead. Force this to happen
2506 : : * by just passing the full chunk size to _reduce_vol_write_chunk.
2507 : : * When it sees the data couldn't be compressed, it will just write
2508 : : * the uncompressed buffer to disk.
2509 : : */
2510 [ - + ]: 596542 : if (reduce_errno < 0) {
2511 : 0 : req->backing_cb_args.output_size = req->vol->params.chunk_size;
2512 : : }
2513 : :
2514 : 596542 : _reduce_vol_write_chunk(req, _write_write_done, req->backing_cb_args.output_size);
2515 : 596542 : }
2516 : :
2517 : : static void
2518 : 596542 : _reduce_vol_compress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
2519 : : {
2520 : 596542 : struct spdk_reduce_vol *vol = req->vol;
2521 : :
2522 : 596542 : req->backing_cb_args.cb_fn = next_fn;
2523 : 596542 : req->backing_cb_args.cb_arg = req;
2524 : 596542 : req->comp_buf_iov[0].iov_base = req->comp_buf;
2525 : 596542 : req->comp_buf_iov[0].iov_len = vol->params.chunk_size;
2526 : 1193084 : vol->backing_dev->compress(vol->backing_dev,
2527 : 596542 : req->decomp_iov, req->decomp_iovcnt, req->comp_buf_iov, 1,
2528 : : &req->backing_cb_args);
2529 : 596542 : }
2530 : :
2531 : : static void
2532 : 523826 : _reduce_vol_decompress_chunk_scratch(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
2533 : : {
2534 : 523826 : struct spdk_reduce_vol *vol = req->vol;
2535 : :
2536 : 523826 : req->backing_cb_args.cb_fn = next_fn;
2537 : 523826 : req->backing_cb_args.cb_arg = req;
2538 : 523826 : req->comp_buf_iov[0].iov_base = req->comp_buf;
2539 : 523826 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
2540 : 523826 : req->decomp_buf_iov[0].iov_base = req->decomp_buf;
2541 : 523826 : req->decomp_buf_iov[0].iov_len = vol->params.chunk_size;
2542 : 523826 : vol->backing_dev->decompress(vol->backing_dev,
2543 : : req->comp_buf_iov, 1, req->decomp_buf_iov, 1,
2544 : : &req->backing_cb_args);
2545 : 523826 : }
2546 : :
2547 : : static void
2548 : 286456 : _reduce_vol_decompress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
2549 : : {
2550 : 286456 : struct spdk_reduce_vol *vol = req->vol;
2551 : 286456 : uint64_t chunk_offset, remainder = 0;
2552 : 286456 : uint64_t ttl_len = 0;
2553 : : size_t iov_len;
2554 : : int i;
2555 : :
2556 : 286456 : req->decomp_iovcnt = 0;
2557 [ - + ]: 286456 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2558 : :
2559 : : /* If backing device doesn't support SGL output then we should copy the result of decompression to user's buffer
2560 : : * if at least one of the conditions below is true:
2561 : : * 1. User's buffer is fragmented
2562 : : * 2. Length of the user's buffer is less than the chunk
2563 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
2564 : 286456 : iov_len = req->iov[0].iov_len;
2565 [ - + + - : 572904 : req->copy_after_decompress = !vol->backing_dev->sgl_out && (req->iovcnt > 1 ||
+ + ]
2566 [ + + - + ]: 288452 : req->iov[0].iov_len < vol->params.chunk_size ||
2567 : 2004 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len));
2568 [ - + + + ]: 286456 : if (req->copy_after_decompress) {
2569 : 284452 : req->decomp_iov[0].iov_base = req->decomp_buf;
2570 : 284452 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
2571 : 284452 : req->decomp_iovcnt = 1;
2572 : 284452 : goto decompress;
2573 : : }
2574 : :
2575 [ - + ]: 2004 : if (chunk_offset) {
2576 : : /* first iov point to our scratch buffer for any offset into the chunk */
2577 : 0 : req->decomp_iov[0].iov_base = req->decomp_buf;
2578 : 0 : req->decomp_iov[0].iov_len = chunk_offset * vol->params.logical_block_size;
2579 : 0 : ttl_len += req->decomp_iov[0].iov_len;
2580 : 0 : req->decomp_iovcnt = 1;
2581 : : }
2582 : :
2583 : : /* now the user data iov, direct to the user buffer */
2584 [ + + ]: 4008 : for (i = 0; i < req->iovcnt; i++) {
2585 : 2004 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
2586 : 2004 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
2587 : 2004 : ttl_len += req->decomp_iov[i + req->decomp_iovcnt].iov_len;
2588 : : }
2589 : 2004 : req->decomp_iovcnt += req->iovcnt;
2590 : :
2591 : : /* send the rest of the chunk to our scratch buffer */
2592 : 2004 : remainder = vol->params.chunk_size - ttl_len;
2593 [ - + ]: 2004 : if (remainder) {
2594 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = req->decomp_buf + ttl_len;
2595 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
2596 : 0 : ttl_len += req->decomp_iov[req->decomp_iovcnt].iov_len;
2597 : 0 : req->decomp_iovcnt++;
2598 : : }
2599 [ - + ]: 2004 : assert(ttl_len == vol->params.chunk_size);
2600 : :
2601 : 286456 : decompress:
2602 [ - + + + : 286456 : assert(!req->copy_after_decompress || (req->copy_after_decompress && req->decomp_iovcnt == 1));
- + + - +
- ]
2603 : 286456 : req->backing_cb_args.cb_fn = next_fn;
2604 : 286456 : req->backing_cb_args.cb_arg = req;
2605 : 286456 : req->comp_buf_iov[0].iov_base = req->comp_buf;
2606 : 286456 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
2607 : 572912 : vol->backing_dev->decompress(vol->backing_dev,
2608 : 286456 : req->comp_buf_iov, 1, req->decomp_iov, req->decomp_iovcnt,
2609 : : &req->backing_cb_args);
2610 : 286456 : }
2611 : :
2612 : : static inline void
2613 : 594538 : _prepare_compress_chunk_copy_user_buffers(struct spdk_reduce_vol_request *req, bool zero_paddings)
2614 : : {
2615 : 594538 : struct spdk_reduce_vol *vol = req->vol;
2616 : 594538 : uint64_t chunk_offset, ttl_len = 0;
2617 : 594538 : uint64_t remainder = 0;
2618 : 594538 : char *copy_offset = NULL;
2619 : 594538 : uint32_t lbsize = vol->params.logical_block_size;
2620 : : int i;
2621 : :
2622 : 594538 : req->decomp_iov[0].iov_base = req->decomp_buf;
2623 : 594538 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
2624 : 594538 : req->decomp_iovcnt = 1;
2625 : 594538 : copy_offset = req->decomp_iov[0].iov_base;
2626 [ - + ]: 594538 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2627 : :
2628 [ + + ]: 594538 : if (chunk_offset) {
2629 : 368348 : ttl_len += chunk_offset * lbsize;
2630 : : /* copy_offset already points to the correct buffer if zero_paddings=false */
2631 [ + + ]: 368348 : if (zero_paddings) {
2632 [ - + ]: 1102 : memset(copy_offset, 0, ttl_len);
2633 : : }
2634 : 368348 : copy_offset += ttl_len;
2635 : : }
2636 : :
2637 : : /* now the user data iov, direct from the user buffer */
2638 [ + + ]: 1189196 : for (i = 0; i < req->iovcnt; i++) {
2639 [ - + - + ]: 594658 : memcpy(copy_offset, req->iov[i].iov_base, req->iov[i].iov_len);
2640 : 594658 : copy_offset += req->iov[i].iov_len;
2641 : 594658 : ttl_len += req->iov[i].iov_len;
2642 : : }
2643 : :
2644 : 594538 : remainder = vol->params.chunk_size - ttl_len;
2645 [ + + ]: 594538 : if (remainder) {
2646 : : /* copy_offset already points to the correct buffer if zero_paddings=false */
2647 [ + + ]: 368393 : if (zero_paddings) {
2648 [ - + ]: 69610 : memset(copy_offset, 0, remainder);
2649 : : }
2650 : 368393 : ttl_len += remainder;
2651 : : }
2652 : :
2653 [ - + ]: 594538 : assert(ttl_len == req->vol->params.chunk_size);
2654 : 594538 : }
2655 : :
2656 : : /* This function can be called when we are compressing a new data or in case of read-modify-write
2657 : : * In the first case possible paddings should be filled with zeroes, in the second case the paddings
2658 : : * should point to already read and decompressed buffer */
2659 : : static inline void
2660 : 596542 : _prepare_compress_chunk(struct spdk_reduce_vol_request *req, bool zero_paddings)
2661 : : {
2662 : 596542 : struct spdk_reduce_vol *vol = req->vol;
2663 [ + + ]: 596542 : char *padding_buffer = zero_paddings ? g_zero_buf : req->decomp_buf;
2664 : 596542 : uint64_t chunk_offset, ttl_len = 0;
2665 : 596542 : uint64_t remainder = 0;
2666 : 596542 : uint32_t lbsize = vol->params.logical_block_size;
2667 : : size_t iov_len;
2668 : : int i;
2669 : :
2670 : : /* If backing device doesn't support SGL input then we should copy user's buffer into decomp_buf
2671 : : * if at least one of the conditions below is true:
2672 : : * 1. User's buffer is fragmented
2673 : : * 2. Length of the user's buffer is less than the chunk
2674 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
2675 : 596542 : iov_len = req->iov[0].iov_len;
2676 [ - + + - : 596542 : if (!vol->backing_dev->sgl_in && (req->iovcnt > 1 ||
+ + ]
2677 [ + + - + ]: 598538 : req->iov[0].iov_len < vol->params.chunk_size ||
2678 : 2004 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len))) {
2679 : 594538 : _prepare_compress_chunk_copy_user_buffers(req, zero_paddings);
2680 : 594538 : return;
2681 : : }
2682 : :
2683 : 2004 : req->decomp_iovcnt = 0;
2684 [ - + ]: 2004 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2685 : :
2686 [ - + ]: 2004 : if (chunk_offset != 0) {
2687 : 0 : ttl_len += chunk_offset * lbsize;
2688 : 0 : req->decomp_iov[0].iov_base = padding_buffer;
2689 : 0 : req->decomp_iov[0].iov_len = ttl_len;
2690 : 0 : req->decomp_iovcnt = 1;
2691 : : }
2692 : :
2693 : : /* now the user data iov, direct from the user buffer */
2694 [ + + ]: 4008 : for (i = 0; i < req->iovcnt; i++) {
2695 : 2004 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
2696 : 2004 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
2697 : 2004 : ttl_len += req->iov[i].iov_len;
2698 : : }
2699 : 2004 : req->decomp_iovcnt += req->iovcnt;
2700 : :
2701 : 2004 : remainder = vol->params.chunk_size - ttl_len;
2702 [ - + ]: 2004 : if (remainder) {
2703 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = padding_buffer + ttl_len;
2704 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
2705 : 0 : req->decomp_iovcnt++;
2706 : 0 : ttl_len += remainder;
2707 : : }
2708 [ - + ]: 2004 : assert(ttl_len == req->vol->params.chunk_size);
2709 : : }
2710 : :
2711 : : static void
2712 : 523826 : _write_decompress_done(void *_req, int reduce_errno)
2713 : : {
2714 : 523826 : struct spdk_reduce_vol_request *req = _req;
2715 : :
2716 : : /* Negative reduce_errno indicates failure for compression operations. */
2717 [ - + ]: 523826 : if (reduce_errno < 0) {
2718 : 0 : _reduce_vol_complete_req(req, reduce_errno);
2719 : 0 : return;
2720 : : }
2721 : :
2722 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
2723 : : * represents the output_size.
2724 : : */
2725 [ - + ]: 523826 : if (req->backing_cb_args.output_size != req->vol->params.chunk_size) {
2726 : 0 : _reduce_vol_complete_req(req, -EIO);
2727 : 0 : return;
2728 : : }
2729 : :
2730 : 523826 : _prepare_compress_chunk(req, false);
2731 : 523826 : _reduce_vol_compress_chunk(req, _write_compress_done);
2732 : : }
2733 : :
2734 : : static void
2735 : 523826 : _write_read_done(void *_req, int reduce_errno)
2736 : : {
2737 : 523826 : struct spdk_reduce_vol_request *req = _req;
2738 : :
2739 [ - + ]: 523826 : if (reduce_errno != 0) {
2740 : 0 : req->reduce_errno = reduce_errno;
2741 : : }
2742 : :
2743 [ - + ]: 523826 : assert(req->num_backing_ops > 0);
2744 [ - + ]: 523826 : if (--req->num_backing_ops > 0) {
2745 : 0 : return;
2746 : : }
2747 : :
2748 [ - + ]: 523826 : if (req->reduce_errno != 0) {
2749 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2750 : 0 : return;
2751 : : }
2752 : :
2753 [ - + + - ]: 523826 : if (req->chunk_is_compressed) {
2754 : 523826 : _reduce_vol_decompress_chunk_scratch(req, _write_decompress_done);
2755 : : } else {
2756 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
2757 : :
2758 : 0 : _write_decompress_done(req, 0);
2759 : : }
2760 : : }
2761 : :
2762 : : static void
2763 : 286456 : _read_decompress_done(void *_req, int reduce_errno)
2764 : : {
2765 : 286456 : struct spdk_reduce_vol_request *req = _req;
2766 : 286456 : struct spdk_reduce_vol *vol = req->vol;
2767 : :
2768 : : /* Negative reduce_errno indicates failure for compression operations. */
2769 [ - + ]: 286456 : if (reduce_errno < 0) {
2770 : 0 : _reduce_vol_complete_req(req, reduce_errno);
2771 : 0 : return;
2772 : : }
2773 : :
2774 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
2775 : : * represents the output_size.
2776 : : */
2777 [ - + ]: 286456 : if (req->backing_cb_args.output_size != vol->params.chunk_size) {
2778 : 0 : _reduce_vol_complete_req(req, -EIO);
2779 : 0 : return;
2780 : : }
2781 : :
2782 [ - + + + ]: 286456 : if (req->copy_after_decompress) {
2783 [ - + ]: 284452 : uint64_t chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2784 : 284452 : char *decomp_buffer = (char *)req->decomp_buf + chunk_offset * vol->params.logical_block_size;
2785 : : int i;
2786 : :
2787 [ + + ]: 569024 : for (i = 0; i < req->iovcnt; i++) {
2788 [ - + - + ]: 284572 : memcpy(req->iov[i].iov_base, decomp_buffer, req->iov[i].iov_len);
2789 : 284572 : decomp_buffer += req->iov[i].iov_len;
2790 [ - + ]: 284572 : assert(decomp_buffer <= (char *)req->decomp_buf + vol->params.chunk_size);
2791 : : }
2792 : : }
2793 : :
2794 : 286456 : _reduce_vol_complete_req(req, 0);
2795 : : }
2796 : :
2797 : : static void
2798 : 286456 : _read_read_done(void *_req, int reduce_errno)
2799 : : {
2800 : 286456 : struct spdk_reduce_vol_request *req = _req;
2801 : :
2802 [ - + ]: 286456 : if (reduce_errno != 0) {
2803 : 0 : req->reduce_errno = reduce_errno;
2804 : : }
2805 : :
2806 [ - + ]: 286456 : assert(req->num_backing_ops > 0);
2807 [ - + ]: 286456 : if (--req->num_backing_ops > 0) {
2808 : 0 : return;
2809 : : }
2810 : :
2811 [ - + ]: 286456 : if (req->reduce_errno != 0) {
2812 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2813 : 0 : return;
2814 : : }
2815 : :
2816 [ - + + - ]: 286456 : if (req->chunk_is_compressed) {
2817 : 286456 : _reduce_vol_decompress_chunk(req, _read_decompress_done);
2818 : : } else {
2819 : :
2820 : : /* If the chunk was compressed, the data would have been sent to the
2821 : : * host buffers by the decompression operation, if not we need to memcpy
2822 : : * from req->decomp_buf.
2823 : : */
2824 : 0 : req->copy_after_decompress = true;
2825 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
2826 : :
2827 : 0 : _read_decompress_done(req, 0);
2828 : : }
2829 : : }
2830 : :
2831 : : static void
2832 : 279314 : _reduce_meta_read_chunk_map_for_read_chunk(void *_meta_req, int error)
2833 : : {
2834 : 279314 : struct reduce_meta_request *meta_req = _meta_req;
2835 : 279314 : struct spdk_reduce_vol *vol = meta_req->restore_ctx.read_chunk_ctx.vol;
2836 : 279314 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.read_chunk_ctx.req;
2837 : 279314 : reduce_request_fn next_fn = meta_req->restore_ctx.read_chunk_ctx.next_fn;
2838 : :
2839 [ - + ]: 279314 : if (error != 0) {
2840 : 0 : next_fn(req, error);
2841 : 0 : _reduce_metablock_cache_read_done_update(meta_req, error);
2842 : 0 : return;
2843 : : }
2844 : :
2845 : : /* copy the elem memory from metablock */
2846 : 279314 : req->chunk = req->prealloc_chunk;
2847 [ - + - + ]: 279314 : memcpy(req->chunk, _reduce_get_meta_elem_addr(meta_req),
2848 : 279314 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk));
2849 : 279314 : _reduce_metablock_cache_read_done_update(meta_req, error);
2850 : :
2851 [ - + ]: 279314 : assert(req->chunk->compressed_size != UINT32_MAX);
2852 : 279314 : req->num_io_units = spdk_divide_round_up(req->chunk->compressed_size,
2853 : 279314 : vol->params.backing_io_unit_size);
2854 : 279314 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
2855 : :
2856 : 279314 : _issue_backing_ops(req, vol, next_fn, false /* read */);
2857 : : }
2858 : :
2859 : : static void
2860 : 96850 : _reduce_meta_read_logical_map_for_read_chunk(void *_meta_req, int error)
2861 : : {
2862 : 96850 : struct reduce_meta_request *meta_req = _meta_req, *chunkmap_meta_req;
2863 : 96850 : struct spdk_reduce_vol *vol = meta_req->restore_ctx.read_chunk_ctx.vol;
2864 : 96850 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.read_chunk_ctx.req;
2865 : 96850 : reduce_request_fn next_fn = meta_req->restore_ctx.read_chunk_ctx.next_fn;
2866 : :
2867 [ - + ]: 96850 : if (error != 0) {
2868 : 0 : next_fn(req, error);
2869 : 0 : _reduce_metablock_cache_read_done_update(meta_req, error);
2870 : 0 : return;
2871 : : }
2872 : :
2873 : : /* read the meta elem */
2874 : 96850 : req->chunk_map_index = *(uint64_t *)_reduce_get_meta_elem_addr(meta_req);
2875 : 96850 : _reduce_metablock_cache_read_done_update(meta_req, error);
2876 : :
2877 : : /* check chunk allocated */
2878 [ + + ]: 96850 : if (req->chunk_map_index == REDUCE_EMPTY_MAP_ENTRY) {
2879 [ + + ]: 80 : for (int i = 0; i < req->iovcnt; i++) {
2880 [ - + ]: 40 : memset(req->iov[i].iov_base, 0, req->iov[i].iov_len);
2881 : : }
2882 : 40 : _reduce_vol_complete_req(req, 0);
2883 : 40 : return;
2884 : : }
2885 : :
2886 : : /* read chunkmap */
2887 : 96810 : chunkmap_meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_CHUNK_MAP,
2888 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2889 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2890 : : _reduce_meta_read_chunk_map_for_read_chunk);
2891 : 96810 : chunkmap_meta_req->restore_ctx.read_chunk_ctx.vol = vol;
2892 : 96810 : chunkmap_meta_req->restore_ctx.read_chunk_ctx.req = req;
2893 : 96810 : chunkmap_meta_req->restore_ctx.read_chunk_ctx.next_fn = next_fn;
2894 : 96810 : _reduce_metablock_cache_read(chunkmap_meta_req);
2895 : :
2896 : 96810 : return;
2897 : : }
2898 : :
2899 : : static void
2900 : 810322 : _reduce_vol_read_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
2901 : : {
2902 : 810322 : struct spdk_reduce_vol *vol = req->vol;
2903 : 810322 : struct reduce_meta_request *meta_req = NULL;
2904 : :
2905 [ + + ]: 810322 : if (vol->params.meta_builtin) {
2906 [ + + ]: 279354 : if (next_fn == _write_read_done) {
2907 : 182504 : meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_CHUNK_MAP,
2908 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2909 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
2910 : : _reduce_meta_read_chunk_map_for_read_chunk);
2911 [ + - ]: 96850 : } else if (next_fn == _read_read_done) {
2912 : 96850 : meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_LOGICAL_MAP,
2913 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
2914 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
2915 : : _reduce_meta_read_logical_map_for_read_chunk);
2916 : : } else {
2917 : 0 : assert(0);
2918 : : }
2919 : 279354 : meta_req->restore_ctx.read_chunk_ctx.vol = vol;
2920 : 279354 : meta_req->restore_ctx.read_chunk_ctx.req = req;
2921 : 279354 : meta_req->restore_ctx.read_chunk_ctx.next_fn = next_fn;
2922 : 279354 : _reduce_metablock_cache_read(meta_req);
2923 : :
2924 : 279354 : return;
2925 : : }
2926 : :
2927 : 530968 : req->chunk_map_index = vol->pm_logical_map[req->logical_map_index];
2928 [ - + ]: 530968 : assert(req->chunk_map_index != REDUCE_EMPTY_MAP_ENTRY);
2929 : :
2930 : 530968 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
2931 : 530968 : req->num_io_units = spdk_divide_round_up(req->chunk->compressed_size,
2932 : 530968 : vol->params.backing_io_unit_size);
2933 : 530968 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
2934 : :
2935 : 530968 : _issue_backing_ops(req, vol, next_fn, false /* read */);
2936 : : }
2937 : :
2938 : : static bool
2939 : 883086 : _iov_array_is_valid(struct spdk_reduce_vol *vol, struct iovec *iov, int iovcnt,
2940 : : uint64_t length)
2941 : : {
2942 : 883086 : uint64_t size = 0;
2943 : : int i;
2944 : :
2945 [ - + ]: 883086 : if (iovcnt > REDUCE_MAX_IOVECS) {
2946 : 0 : return false;
2947 : : }
2948 : :
2949 [ + + ]: 1766412 : for (i = 0; i < iovcnt; i++) {
2950 : 883326 : size += iov[i].iov_len;
2951 : : }
2952 : :
2953 : 883086 : return size == (length * vol->params.logical_block_size);
2954 : : }
2955 : :
2956 : : static bool
2957 : 1503266 : _check_overlap(struct spdk_reduce_vol *vol, uint64_t logical_map_index)
2958 : : {
2959 : : struct spdk_reduce_vol_request req;
2960 : :
2961 : 1503266 : req.logical_map_index = logical_map_index;
2962 : :
2963 : 1503266 : return (NULL != RB_FIND(executing_req_tree, &vol->executing_requests, &req));
2964 : : }
2965 : :
2966 : : static void
2967 : 286496 : _start_readv_request(struct spdk_reduce_vol_request *req)
2968 : : {
2969 : 286496 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
2970 : 286496 : _reduce_vol_read_chunk(req, _read_read_done);
2971 : 286496 : }
2972 : :
2973 : : void
2974 : 286544 : spdk_reduce_vol_readv(struct spdk_reduce_vol *vol,
2975 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
2976 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2977 : : {
2978 : : struct spdk_reduce_vol_request *req;
2979 : : uint64_t logical_map_index;
2980 : : bool overlapped;
2981 : : int i;
2982 : :
2983 [ - + ]: 286544 : if (length == 0) {
2984 : 0 : cb_fn(cb_arg, 0);
2985 : 0 : return;
2986 : : }
2987 : :
2988 [ - + ]: 286544 : if (_request_spans_chunk_boundary(vol, offset, length)) {
2989 : 0 : cb_fn(cb_arg, -EINVAL);
2990 : 0 : return;
2991 : : }
2992 : :
2993 [ - + ]: 286544 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
2994 : 0 : cb_fn(cb_arg, -EINVAL);
2995 : 0 : return;
2996 : : }
2997 : :
2998 [ - + ]: 286544 : logical_map_index = offset / vol->logical_blocks_per_chunk;
2999 : 286544 : overlapped = _check_overlap(vol, logical_map_index);
3000 : :
3001 : : /* If we use builtin metadata, chunk allocated detection needs async read backend.
3002 : : * and get the metadata async and check the chunk allocated status, maybe late,
3003 : : * there may be another write about the chunk.
3004 : : * So we need insert the overlap tree, then check the chunk allocated status.
3005 : : */
3006 [ + + ]: 286544 : if (!vol->params.meta_builtin) {
3007 [ + + + + ]: 189694 : if (!overlapped && vol->pm_logical_map[logical_map_index] == REDUCE_EMPTY_MAP_ENTRY) {
3008 : : /*
3009 : : * This chunk hasn't been allocated. So treat the data as all
3010 : : * zeroes for this chunk - do the memset and immediately complete
3011 : : * the operation.
3012 : : */
3013 [ + + ]: 96 : for (i = 0; i < iovcnt; i++) {
3014 [ - + ]: 48 : memset(iov[i].iov_base, 0, iov[i].iov_len);
3015 : : }
3016 : 48 : cb_fn(cb_arg, 0);
3017 : 48 : return;
3018 : : }
3019 : : }
3020 : :
3021 : 286496 : req = TAILQ_FIRST(&vol->free_requests);
3022 [ - + ]: 286496 : if (req == NULL) {
3023 : 0 : cb_fn(cb_arg, -ENOMEM);
3024 : 0 : return;
3025 : : }
3026 : :
3027 [ + - ]: 286496 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
3028 : 286496 : req->type = REDUCE_IO_READV;
3029 : 286496 : req->vol = vol;
3030 : 286496 : req->iov = iov;
3031 : 286496 : req->iovcnt = iovcnt;
3032 : 286496 : req->offset = offset;
3033 : 286496 : req->logical_map_index = logical_map_index;
3034 : 286496 : req->length = length;
3035 : 286496 : req->copy_after_decompress = false;
3036 : 286496 : req->cb_fn = cb_fn;
3037 : 286496 : req->cb_arg = cb_arg;
3038 : 286496 : req->reduce_errno = 0;
3039 : :
3040 [ + + ]: 286496 : if (!overlapped) {
3041 : 2204 : _start_readv_request(req);
3042 : : } else {
3043 : 284292 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
3044 : : }
3045 : : }
3046 : :
3047 : : static void
3048 : 211566 : _reduce_meta_read_logical_map_for_writev(void *_meta_req, int error)
3049 : : {
3050 : 211566 : struct reduce_meta_request *meta_req = _meta_req;
3051 : 211566 : struct spdk_reduce_vol_request *req = meta_req->restore_ctx.req_ctx.req;
3052 : 211566 : struct spdk_reduce_vol *vol = req->vol;
3053 : :
3054 [ - + ]: 211566 : if (error != 0) {
3055 : 0 : req->reduce_errno = error;
3056 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
3057 : 0 : return;
3058 : : }
3059 : :
3060 : : /* read the meta elem */
3061 : 211566 : req->chunk_map_index = *(uint64_t *)_reduce_get_meta_elem_addr(meta_req);
3062 : 211566 : req->read_chunk_map_index = req->chunk_map_index;
3063 : 211566 : _reduce_metablock_cache_read_done_update(meta_req, error);
3064 : :
3065 : : /* restore */
3066 [ + + ]: 211566 : if (req->chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
3067 [ + + ]: 183060 : if ((req->length * vol->params.logical_block_size) < vol->params.chunk_size) {
3068 : : /* Read old chunk, then overwrite with data from this write
3069 : : * operation.
3070 : : */
3071 : 182504 : req->rmw = true;
3072 : 182504 : _reduce_vol_read_chunk(req, _write_read_done);
3073 : 182504 : return;
3074 : : }
3075 : : }
3076 : :
3077 : 29062 : req->rmw = false;
3078 : :
3079 : 29062 : _prepare_compress_chunk(req, true);
3080 : 29062 : _reduce_vol_compress_chunk(req, _write_compress_done);
3081 : : }
3082 : :
3083 : : static void
3084 : 596542 : _start_writev_request(struct spdk_reduce_vol_request *req)
3085 : : {
3086 : 596542 : struct spdk_reduce_vol *vol = req->vol;
3087 : : struct reduce_meta_request *meta_req;
3088 : :
3089 : 596542 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
3090 : :
3091 [ + + ]: 596542 : if (vol->params.meta_builtin) {
3092 : 211566 : meta_req = _get_init_meta_req(vol, false, REDUCE_MTYPE_LOGICAL_MAP,
3093 : : _meta_get_mblksn(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
3094 : : _meta_get_elem_sn_on_mblk(&vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
3095 : : _reduce_meta_read_logical_map_for_writev);
3096 : 211566 : meta_req->restore_ctx.req_ctx.req = req;
3097 : :
3098 : 211566 : _reduce_metablock_cache_read(meta_req);
3099 : 211566 : return;
3100 : : }
3101 : :
3102 [ + + ]: 384976 : if (vol->pm_logical_map[req->logical_map_index] != REDUCE_EMPTY_MAP_ENTRY) {
3103 [ + + ]: 341878 : if ((req->length * vol->params.logical_block_size) < vol->params.chunk_size) {
3104 : : /* Read old chunk, then overwrite with data from this write
3105 : : * operation.
3106 : : */
3107 : 341322 : req->rmw = true;
3108 : 341322 : _reduce_vol_read_chunk(req, _write_read_done);
3109 : 341322 : return;
3110 : : }
3111 : : }
3112 : :
3113 : 43654 : req->rmw = false;
3114 : :
3115 : 43654 : _prepare_compress_chunk(req, true);
3116 : 43654 : _reduce_vol_compress_chunk(req, _write_compress_done);
3117 : : }
3118 : :
3119 : : void
3120 : 596542 : spdk_reduce_vol_writev(struct spdk_reduce_vol *vol,
3121 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
3122 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
3123 : : {
3124 : : struct spdk_reduce_vol_request *req;
3125 : : uint64_t logical_map_index;
3126 : : bool overlapped;
3127 : :
3128 [ - + ]: 596542 : if (length == 0) {
3129 : 0 : cb_fn(cb_arg, 0);
3130 : 0 : return;
3131 : : }
3132 : :
3133 [ - + ]: 596542 : if (_request_spans_chunk_boundary(vol, offset, length)) {
3134 : 0 : cb_fn(cb_arg, -EINVAL);
3135 : 0 : return;
3136 : : }
3137 : :
3138 [ - + ]: 596542 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
3139 : 0 : cb_fn(cb_arg, -EINVAL);
3140 : 0 : return;
3141 : : }
3142 : :
3143 [ - + ]: 596542 : logical_map_index = offset / vol->logical_blocks_per_chunk;
3144 : 596542 : overlapped = _check_overlap(vol, logical_map_index);
3145 : :
3146 : 596542 : req = TAILQ_FIRST(&vol->free_requests);
3147 [ - + ]: 596542 : if (req == NULL) {
3148 : 0 : cb_fn(cb_arg, -ENOMEM);
3149 : 0 : return;
3150 : : }
3151 : :
3152 [ + - ]: 596542 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
3153 : 596542 : req->type = REDUCE_IO_WRITEV;
3154 : 596542 : req->vol = vol;
3155 : 596542 : req->iov = iov;
3156 : 596542 : req->iovcnt = iovcnt;
3157 : 596542 : req->offset = offset;
3158 : 596542 : req->logical_map_index = logical_map_index;
3159 : 596542 : req->length = length;
3160 : 596542 : req->copy_after_decompress = false;
3161 : 596542 : req->cb_fn = cb_fn;
3162 : 596542 : req->cb_arg = cb_arg;
3163 : 596542 : req->reduce_errno = 0;
3164 : :
3165 [ + + ]: 596542 : if (!overlapped) {
3166 : 228626 : _start_writev_request(req);
3167 : : } else {
3168 : 367916 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
3169 : : }
3170 : : }
3171 : :
3172 : : static void
3173 : 0 : _unmap_chunkmap_update_error_process(struct reduce_meta_request *chunk_map_meta_req, int error)
3174 : : {
3175 : 0 : struct reduce_meta_request *logical_map_meta_req =
3176 : : chunk_map_meta_req->restore_ctx.req_ctx.logicalmap_meta_req;
3177 : 0 : struct spdk_reduce_vol_request *req = chunk_map_meta_req->restore_ctx.req_ctx.req;
3178 : :
3179 [ # # # # : 0 : if (chunk_map_meta_req->supply_read || !chunk_map_meta_req->is_write) {
# # # # ]
3180 : 0 : _reduce_metablock_cache_read_done_update(chunk_map_meta_req, error);
3181 : : } else {
3182 : 0 : _reduce_metablock_cache_write_done_update(chunk_map_meta_req, error);
3183 : : }
3184 : 0 : _reduce_metablock_cache_read_done_update(logical_map_meta_req, error);
3185 : 0 : _reduce_vol_complete_req(req, error);
3186 : 0 : }
3187 : :
3188 : : static void
3189 : 0 : _reduce_meta_read_chunk_map_for_unmap_full_chunk(void *cb_arg, int reduce_errno)
3190 : : {
3191 : 0 : struct reduce_meta_request *chunk_map_meta_req = cb_arg;
3192 : 0 : struct spdk_reduce_vol_request *req = chunk_map_meta_req->restore_ctx.req_ctx.req;
3193 : 0 : struct spdk_reduce_vol *vol = req->vol;
3194 : 0 : struct reduce_meta_request *logical_map_meta_req =
3195 : : chunk_map_meta_req->restore_ctx.req_ctx.logicalmap_meta_req;
3196 : : struct spdk_reduce_chunk_map *chunk_map;
3197 : : uint64_t *logical_map;
3198 : :
3199 [ # # # # ]: 0 : if (chunk_map_meta_req->supply_read) {
3200 [ # # ]: 0 : if (reduce_errno != 0) {
3201 : 0 : _unmap_chunkmap_update_error_process(chunk_map_meta_req, reduce_errno);
3202 : 0 : return;
3203 : : }
3204 : :
3205 : 0 : chunk_map = (struct spdk_reduce_chunk_map *)_reduce_get_meta_elem_addr(chunk_map_meta_req);
3206 : 0 : _reduce_vol_reset_chunk(vol, req->chunk_map_index, chunk_map);
3207 : 0 : convert_supply_read_to_write(chunk_map_meta_req);
3208 : 0 : _reduce_metablock_cache_read_done_update(chunk_map_meta_req, reduce_errno);
3209 : : } else {
3210 [ # # ]: 0 : if (reduce_errno != 0) {
3211 : 0 : _unmap_chunkmap_update_error_process(chunk_map_meta_req, reduce_errno);
3212 : 0 : return;
3213 : : }
3214 : 0 : _reduce_metablock_cache_write_done_update(chunk_map_meta_req, reduce_errno);
3215 : :
3216 : : /* clear logical map */
3217 : 0 : logical_map = (uint64_t *)_reduce_get_meta_elem_addr(logical_map_meta_req);
3218 : 0 : *logical_map = REDUCE_EMPTY_MAP_ENTRY;
3219 : 0 : convert_supply_read_to_write(logical_map_meta_req);
3220 : 0 : _reduce_metablock_cache_read_done_update(logical_map_meta_req, reduce_errno);
3221 : : }
3222 : : }
3223 : :
3224 : : static void
3225 : 229516 : _reduce_meta_read_logical_map_for_unmap_full_chunk(void *cb_arg, int reduce_errno)
3226 : : {
3227 : 229516 : struct reduce_meta_request *logical_map_meta_req = cb_arg, *chunk_map_meta_req;
3228 : 229516 : struct spdk_reduce_vol_request *req = logical_map_meta_req->restore_ctx.req_ctx.req;
3229 : :
3230 [ - + + - ]: 229516 : if (logical_map_meta_req->supply_read) {
3231 : : /* the entry process of unmap process */
3232 [ - + ]: 229516 : if (reduce_errno != 0) {
3233 : 0 : _reduce_metablock_cache_read_done_update(logical_map_meta_req, reduce_errno);
3234 : 0 : _reduce_vol_complete_req(req, reduce_errno);
3235 : 0 : return;
3236 : : }
3237 : :
3238 : 229516 : req->chunk_map_index = *(uint64_t *)_reduce_get_meta_elem_addr(logical_map_meta_req);
3239 [ + - ]: 229516 : if (req->chunk_map_index == REDUCE_EMPTY_MAP_ENTRY) {
3240 : 229516 : _reduce_metablock_cache_read_done_update(logical_map_meta_req, reduce_errno);
3241 : 229516 : _reduce_vol_complete_req(req, reduce_errno);
3242 : 229516 : return;
3243 : : }
3244 : :
3245 : : /* get_chunk_map */
3246 : 0 : chunk_map_meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_CHUNK_MAP,
3247 : 0 : _meta_get_mblksn(&req->vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
3248 : 0 : _meta_get_elem_sn_on_mblk(&req->vol->params, REDUCE_MTYPE_CHUNK_MAP, req->chunk_map_index),
3249 : : _reduce_meta_read_chunk_map_for_unmap_full_chunk);
3250 : 0 : chunk_map_meta_req->restore_ctx.req_ctx.req = req;
3251 : 0 : chunk_map_meta_req->restore_ctx.req_ctx.logicalmap_meta_req = logical_map_meta_req;
3252 : 0 : _reduce_metablock_cache_read(chunk_map_meta_req);
3253 : : } else {
3254 : : /* the final process of unmap process */
3255 : 0 : _reduce_metablock_cache_write_done_update(logical_map_meta_req, reduce_errno);
3256 : 0 : _reduce_vol_complete_req(req, reduce_errno);
3257 : : }
3258 : : }
3259 : :
3260 : : static void
3261 : 229516 : _reduce_meta_unmap_full_chunk(void *ctx)
3262 : : {
3263 : 229516 : struct spdk_reduce_vol_request *req = ctx;
3264 : : struct reduce_meta_request *logical_map_meta_req;
3265 : :
3266 : : /* alloc write meta_req, if we find the logical map is EMPTY, don't need write */
3267 : 688548 : logical_map_meta_req = _get_init_meta_req(req->vol, true, REDUCE_MTYPE_LOGICAL_MAP,
3268 : 229516 : _meta_get_mblksn(&req->vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
3269 : 229516 : _meta_get_elem_sn_on_mblk(&req->vol->params, REDUCE_MTYPE_LOGICAL_MAP, req->logical_map_index),
3270 : : _reduce_meta_read_logical_map_for_unmap_full_chunk);
3271 : 229516 : logical_map_meta_req->restore_ctx.req_ctx.req = req;
3272 : 229516 : _reduce_metablock_cache_read(logical_map_meta_req);
3273 : 229516 : }
3274 : :
3275 : : static void
3276 : 229516 : _start_unmap_request_full_chunk(void *ctx)
3277 : : {
3278 : 229516 : struct spdk_reduce_vol_request *req = ctx;
3279 : 229516 : struct spdk_reduce_vol *vol = req->vol;
3280 : : uint64_t chunk_map_index;
3281 : :
3282 : 229516 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
3283 : :
3284 [ + - ]: 229516 : if (vol->params.meta_builtin) {
3285 : 229516 : _reduce_meta_unmap_full_chunk(ctx);
3286 : 229516 : return;
3287 : : }
3288 : :
3289 : 0 : chunk_map_index = vol->pm_logical_map[req->logical_map_index];
3290 [ # # ]: 0 : if (chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
3291 : 0 : _reduce_vol_reset_chunk(vol, chunk_map_index, NULL);
3292 : 0 : vol->pm_logical_map[req->logical_map_index] = REDUCE_EMPTY_MAP_ENTRY;
3293 : 0 : _reduce_persist(vol, &vol->pm_logical_map[req->logical_map_index], sizeof(uint64_t));
3294 : : }
3295 : 0 : _reduce_vol_complete_req(req, 0);
3296 : : }
3297 : :
3298 : : static void
3299 : 620180 : _reduce_vol_unmap_full_chunk(struct spdk_reduce_vol *vol,
3300 : : uint64_t offset, uint64_t length,
3301 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
3302 : : {
3303 : : struct spdk_reduce_vol_request *req;
3304 : : uint64_t logical_map_index;
3305 : : bool overlapped;
3306 : :
3307 [ - + ]: 620180 : if (_request_spans_chunk_boundary(vol, offset, length)) {
3308 : 0 : cb_fn(cb_arg, -EINVAL);
3309 : 0 : return;
3310 : : }
3311 : :
3312 [ - + ]: 620180 : logical_map_index = offset / vol->logical_blocks_per_chunk;
3313 : 620180 : overlapped = _check_overlap(vol, logical_map_index);
3314 : :
3315 [ + + ]: 620180 : if (!vol->params.meta_builtin) {
3316 [ + - + - ]: 390664 : if (!overlapped && vol->pm_logical_map[logical_map_index] == REDUCE_EMPTY_MAP_ENTRY) {
3317 : : /*
3318 : : * This chunk hasn't been allocated. Nothing needs to be done.
3319 : : */
3320 : 390664 : cb_fn(cb_arg, 0);
3321 : 390664 : return;
3322 : : }
3323 : : }
3324 : :
3325 : 229516 : req = TAILQ_FIRST(&vol->free_requests);
3326 [ - + ]: 229516 : if (req == NULL) {
3327 : 0 : cb_fn(cb_arg, -ENOMEM);
3328 : 0 : return;
3329 : : }
3330 : :
3331 [ + - ]: 229516 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
3332 : 229516 : req->type = REDUCE_IO_UNMAP;
3333 : 229516 : req->vol = vol;
3334 : 229516 : req->iov = NULL;
3335 : 229516 : req->iovcnt = 0;
3336 : 229516 : req->offset = offset;
3337 : 229516 : req->logical_map_index = logical_map_index;
3338 : 229516 : req->length = length;
3339 : 229516 : req->copy_after_decompress = false;
3340 : 229516 : req->cb_fn = cb_fn;
3341 : 229516 : req->cb_arg = cb_arg;
3342 : 229516 : req->reduce_errno = 0;
3343 : :
3344 [ + - ]: 229516 : if (!overlapped) {
3345 : 229516 : _start_unmap_request_full_chunk(req);
3346 : : } else {
3347 : 0 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
3348 : : }
3349 : : }
3350 : :
3351 : : struct unmap_partial_chunk_ctx {
3352 : : struct spdk_reduce_vol *vol;
3353 : : struct iovec iov;
3354 : : spdk_reduce_vol_op_complete cb_fn;
3355 : : void *cb_arg;
3356 : : };
3357 : :
3358 : : static void
3359 : 310090 : _reduce_unmap_partial_chunk_complete(void *_ctx, int reduce_errno)
3360 : : {
3361 : 310090 : struct unmap_partial_chunk_ctx *ctx = _ctx;
3362 : :
3363 : 310090 : ctx->cb_fn(ctx->cb_arg, reduce_errno);
3364 : 310090 : free(ctx);
3365 : 310090 : }
3366 : :
3367 : : static void
3368 : 310090 : _reduce_vol_unmap_partial_chunk(struct spdk_reduce_vol *vol, uint64_t offset, uint64_t length,
3369 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
3370 : : {
3371 : : struct unmap_partial_chunk_ctx *ctx;
3372 : :
3373 : 310090 : ctx = calloc(1, sizeof(struct unmap_partial_chunk_ctx));
3374 [ - + ]: 310090 : if (ctx == NULL) {
3375 : 0 : cb_fn(cb_arg, -ENOMEM);
3376 : 0 : return;
3377 : : }
3378 : :
3379 : 310090 : ctx->vol = vol;
3380 : 310090 : ctx->iov.iov_base = g_zero_buf;
3381 : 310090 : ctx->iov.iov_len = length * vol->params.logical_block_size;
3382 : 310090 : ctx->cb_fn = cb_fn;
3383 : 310090 : ctx->cb_arg = cb_arg;
3384 : :
3385 : 310090 : spdk_reduce_vol_writev(vol, &ctx->iov, 1, offset, length, _reduce_unmap_partial_chunk_complete,
3386 : : ctx);
3387 : : }
3388 : :
3389 : : void
3390 : 930270 : spdk_reduce_vol_unmap(struct spdk_reduce_vol *vol,
3391 : : uint64_t offset, uint64_t length,
3392 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
3393 : : {
3394 [ + + ]: 930270 : if (length < vol->logical_blocks_per_chunk) {
3395 : 310090 : _reduce_vol_unmap_partial_chunk(vol, offset, length, cb_fn, cb_arg);
3396 [ + - ]: 620180 : } else if (length == vol->logical_blocks_per_chunk) {
3397 : 620180 : _reduce_vol_unmap_full_chunk(vol, offset, length, cb_fn, cb_arg);
3398 : : } else {
3399 : 0 : cb_fn(cb_arg, -EINVAL);
3400 : : }
3401 : 930270 : }
3402 : :
3403 : : const struct spdk_reduce_vol_params *
3404 : 310090 : spdk_reduce_vol_get_params(struct spdk_reduce_vol *vol)
3405 : : {
3406 : 310090 : return &vol->params;
3407 : : }
3408 : :
3409 : : const char *
3410 : 22 : spdk_reduce_vol_get_pm_path(const struct spdk_reduce_vol *vol)
3411 : : {
3412 : 22 : return vol->pm_file.path;
3413 : : }
3414 : :
3415 : : void
3416 : 0 : spdk_reduce_vol_print_info(struct spdk_reduce_vol *vol)
3417 : : {
3418 : : uint64_t logical_map_size, num_chunks, ttl_chunk_sz;
3419 : : uint32_t struct_size;
3420 : : uint64_t chunk_map_size;
3421 : :
3422 : 0 : SPDK_NOTICELOG("vol info:\n");
3423 : 0 : SPDK_NOTICELOG("\tvol->params.backing_io_unit_size = 0x%x\n", vol->params.backing_io_unit_size);
3424 : 0 : SPDK_NOTICELOG("\tvol->params.logical_block_size = 0x%x\n", vol->params.logical_block_size);
3425 : 0 : SPDK_NOTICELOG("\tvol->params.chunk_size = 0x%x\n", vol->params.chunk_size);
3426 : 0 : SPDK_NOTICELOG("\tvol->params.vol_size = 0x%" PRIx64 "\n", vol->params.vol_size);
3427 : 0 : num_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
3428 : 0 : SPDK_NOTICELOG("\ttotal chunks (including extra) = 0x%" PRIx64 "\n", num_chunks);
3429 [ # # ]: 0 : SPDK_NOTICELOG("\ttotal chunks (excluding extra) = 0x%" PRIx64 "\n",
3430 : : vol->params.vol_size / vol->params.chunk_size);
3431 : 0 : ttl_chunk_sz = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
3432 : 0 : vol->params.backing_io_unit_size);
3433 : 0 : SPDK_NOTICELOG("\ttotal_chunks_size = 0x%" PRIx64 "\n", ttl_chunk_sz);
3434 : 0 : struct_size = _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
3435 : 0 : SPDK_NOTICELOG("\tchunk_struct_size = 0x%x\n", struct_size);
3436 : :
3437 : 0 : SPDK_NOTICELOG("pmem info:\n");
3438 : 0 : SPDK_NOTICELOG("\tvol->pm_file.size = 0x%" PRIx64 "\n", vol->pm_file.size);
3439 : 0 : SPDK_NOTICELOG("\tvol->pm_file.pm_buf = %p\n", (void *)vol->pm_file.pm_buf);
3440 : 0 : SPDK_NOTICELOG("\tvol->pm_super = %p\n", (void *)vol->pm_super);
3441 : 0 : SPDK_NOTICELOG("\tvol->pm_logical_map = %p\n", (void *)vol->pm_logical_map);
3442 : 0 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size,
3443 : 0 : vol->params.chunk_size);
3444 : 0 : SPDK_NOTICELOG("\tlogical_map_size = 0x%" PRIx64 "\n", logical_map_size);
3445 : 0 : SPDK_NOTICELOG("\tvol->pm_chunk_maps = %p\n", (void *)vol->pm_chunk_maps);
3446 : 0 : chunk_map_size = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
3447 : 0 : vol->params.backing_io_unit_size);
3448 : 0 : SPDK_NOTICELOG("\tchunk_map_size = 0x%" PRIx64 "\n", chunk_map_size);
3449 : 0 : }
3450 : :
3451 : 107 : SPDK_LOG_REGISTER_COMPONENT(reduce)
|