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_INIT_BUF_SIZE (256 << 10) /* 256KB */
53 : :
54 : : /**
55 : : * Describes a persistent memory file used to hold metadata associated with a
56 : : * compressed volume.
57 : : */
58 : : struct spdk_reduce_pm_file {
59 : : char path[REDUCE_PATH_MAX];
60 : : void *pm_buf;
61 : : int pm_is_pmem;
62 : : uint64_t size;
63 : : };
64 : :
65 : : #define REDUCE_IO_READV 1
66 : : #define REDUCE_IO_WRITEV 2
67 : : #define REDUCE_IO_UNMAP 3
68 : :
69 : : struct spdk_reduce_chunk_map {
70 : : uint32_t compressed_size;
71 : : uint32_t reserved;
72 : : uint64_t io_unit_index[0];
73 : : };
74 : :
75 : : struct spdk_reduce_vol_request {
76 : : /**
77 : : * Scratch buffer used for uncompressed chunk. This is used for:
78 : : * 1) source buffer for compression operations
79 : : * 2) destination buffer for decompression operations
80 : : * 3) data buffer when writing uncompressed chunk to disk
81 : : * 4) data buffer when reading uncompressed chunk from disk
82 : : */
83 : : uint8_t *decomp_buf;
84 : : struct iovec *decomp_buf_iov;
85 : :
86 : : /**
87 : : * These are used to construct the iovecs that are sent to
88 : : * the decomp engine, they point to a mix of the scratch buffer
89 : : * and user buffer
90 : : */
91 : : struct iovec decomp_iov[REDUCE_MAX_IOVECS + 2];
92 : : int decomp_iovcnt;
93 : :
94 : : /**
95 : : * Scratch buffer used for compressed chunk. This is used for:
96 : : * 1) destination buffer for compression operations
97 : : * 2) source buffer for decompression operations
98 : : * 3) data buffer when writing compressed chunk to disk
99 : : * 4) data buffer when reading compressed chunk from disk
100 : : */
101 : : uint8_t *comp_buf;
102 : : struct iovec *comp_buf_iov;
103 : : struct iovec *iov;
104 : : bool rmw;
105 : : struct spdk_reduce_vol *vol;
106 : : int type;
107 : : int reduce_errno;
108 : : int iovcnt;
109 : : int num_backing_ops;
110 : : uint32_t num_io_units;
111 : : struct spdk_reduce_backing_io *backing_io;
112 : : bool chunk_is_compressed;
113 : : bool copy_after_decompress;
114 : : uint64_t offset;
115 : : uint64_t logical_map_index;
116 : : uint64_t length;
117 : : uint64_t chunk_map_index;
118 : : struct spdk_reduce_chunk_map *chunk;
119 : : /* save the new chunkinfo to the pre-allocated chunk mem,
120 : : * then memcpy from here to the metablock which used to submit backend
121 : : */
122 : : struct spdk_reduce_chunk_map *prealloc_chunk;
123 : : spdk_reduce_vol_op_complete cb_fn;
124 : : void *cb_arg;
125 : : TAILQ_ENTRY(spdk_reduce_vol_request) tailq;
126 : : RB_ENTRY(spdk_reduce_vol_request) rbnode;
127 : : struct spdk_reduce_vol_cb_args backing_cb_args;
128 : : };
129 : :
130 : : struct spdk_reduce_vol {
131 : : struct spdk_reduce_vol_params params;
132 : : struct spdk_reduce_vol_info info;
133 : : uint32_t backing_io_units_per_chunk;
134 : : uint32_t backing_lba_per_io_unit;
135 : : uint32_t logical_blocks_per_chunk;
136 : : struct spdk_reduce_pm_file pm_file;
137 : : struct spdk_reduce_backing_dev *backing_dev;
138 : : struct spdk_reduce_vol_superblock *backing_super;
139 : : struct spdk_reduce_vol_superblock *pm_super;
140 : : uint64_t *pm_logical_map;
141 : : uint64_t *pm_chunk_maps;
142 : :
143 : : struct spdk_bit_array *allocated_chunk_maps;
144 : : /* The starting position when looking for a block from allocated_chunk_maps */
145 : : uint64_t find_chunk_offset;
146 : : /* Cache free chunks to speed up lookup of free chunk. */
147 : : struct reduce_queue free_chunks_queue;
148 : : struct spdk_bit_array *allocated_backing_io_units;
149 : : /* The starting position when looking for a block from allocated_backing_io_units */
150 : : uint64_t find_block_offset;
151 : : /* Cache free blocks for backing bdev to speed up lookup of free backing blocks. */
152 : : struct reduce_queue free_backing_blocks_queue;
153 : :
154 : : struct spdk_reduce_vol_request *request_mem;
155 : : TAILQ_HEAD(, spdk_reduce_vol_request) free_requests;
156 : : RB_HEAD(executing_req_tree, spdk_reduce_vol_request) executing_requests;
157 : : TAILQ_HEAD(, spdk_reduce_vol_request) queued_requests;
158 : :
159 : : /* Single contiguous buffer used for all request buffers for this volume. */
160 : : uint8_t *buf_mem;
161 : : struct iovec *buf_iov_mem;
162 : : /* Single contiguous buffer used for backing io buffers for this volume. */
163 : : uint8_t *buf_backing_io_mem;
164 : :
165 : : /* the count of meta cache block we prepare */
166 : : uint32_t metablocks_num;
167 : : /* A pointer of many 4k DMA mem from huge page, It has metablocks_num * 4K Bytes */
168 : : uint8_t *metablock_buf;
169 : : /* A pointer of many mblk describtion struct mem. The count of these struct is equal to metablocks num */
170 : : struct reduce_metablock_extent *metablock_extent_mem;
171 : : TAILQ_HEAD(, reduce_metablock_extent) metablocks_free;
172 : : RB_HEAD(metablock_cache_tree, reduce_metablock_extent) metablocks_caching;
173 : : TAILQ_HEAD(, reduce_metablock_extent) metablocks_lru;
174 : :
175 : : /* use independent mem for meta opetation backingio */
176 : : struct reduce_meta_request *meta_request_buf;
177 : : TAILQ_HEAD(, reduce_meta_request) free_meta_request;
178 : : };
179 : :
180 : : struct reduce_metablock_extent {
181 : : uint32_t mblk_sn;
182 : : /* pointer to 4K mem */
183 : : struct iovec iov;
184 : : /* the node to attach free list or lru list */
185 : : TAILQ_ENTRY(reduce_metablock_extent) tailq;
186 : : /* the node to attach read/write tree */
187 : : RB_ENTRY(reduce_metablock_extent) rbnode;
188 : :
189 : : /* the write_meta_req cnt that this one metadata write operation catching */
190 : : uint32_t batch_write_cnt;
191 : : /* the refcnt should be both pending_to_supply_read and pending_to_write elem count */
192 : : uint32_t refcnt;
193 : : /* when multi io to access metablock, callback them by the tailq */
194 : : TAILQ_HEAD(, reduce_meta_request) pending_to_supply_read;
195 : : TAILQ_HEAD(, reduce_meta_request) pending_to_write;
196 : : };
197 : :
198 : : struct reduce_meta_request {
199 : : /* the node to attach free_meta_backingio or pending list of struct reduce_metablock_extent */
200 : : TAILQ_ENTRY(reduce_meta_request) tailq;
201 : :
202 : : struct spdk_reduce_vol *vol;
203 : : /* if need or may need to update meta, set the is_write flag */
204 : : bool is_write;
205 : : /* when we want to update meta, should do read-modify-write about the metablock.
206 : : * 1. set the supply_read flag, and do read.
207 : : * 2. read callback, modify the elem on metablock, and reset the supply_read flag. send do write.
208 : : * 3. write callback, we can judge the meta stage according the supply_read flag.
209 : : */
210 : : bool supply_read;
211 : : enum spdk_reduce_meta_type mtype;
212 : : uint32_t mblk_sn;
213 : : uint32_t elem_sn_on_mblk;
214 : : /* A pointer to caching meta block */
215 : : struct reduce_metablock_extent *mblk_extent;
216 : :
217 : : struct spdk_reduce_vol_cb_args cb_args;
218 : : struct spdk_reduce_backing_io backing_io;
219 : : };
220 : :
221 : : static void _start_readv_request(struct spdk_reduce_vol_request *req);
222 : : static void _start_writev_request(struct spdk_reduce_vol_request *req);
223 : : static uint8_t *g_zero_buf;
224 : : static int g_vol_count = 0;
225 : :
226 : : /*
227 : : * Allocate extra metadata chunks and corresponding backing io units to account for
228 : : * outstanding IO in worst case scenario where logical map is completely allocated
229 : : * and no data can be compressed. We need extra chunks in this case to handle
230 : : * in-flight writes since reduce never writes data in place.
231 : : */
232 : : #define REDUCE_NUM_EXTRA_CHUNKS 128
233 : :
234 : : static void
235 : 993304 : _reduce_persist(struct spdk_reduce_vol *vol, const void *addr, size_t len)
236 : : {
237 [ - + ]: 993304 : if (vol->pm_file.pm_is_pmem) {
238 : 0 : pmem_persist(addr, len);
239 : : } else {
240 : 993304 : pmem_msync(addr, len);
241 : : }
242 : 993304 : }
243 : :
244 : : static uint64_t
245 : 40 : _get_pm_logical_map_size(uint64_t vol_size, uint64_t chunk_size)
246 : : {
247 : : uint64_t chunks_in_logical_map, logical_map_size;
248 : :
249 [ - + ]: 40 : chunks_in_logical_map = vol_size / chunk_size;
250 : 40 : logical_map_size = chunks_in_logical_map * sizeof(uint64_t);
251 : :
252 : : /* Round up to next cacheline. */
253 : 40 : return spdk_divide_round_up(logical_map_size, REDUCE_PM_SIZE_ALIGNMENT) *
254 : : REDUCE_PM_SIZE_ALIGNMENT;
255 : : }
256 : :
257 : : static uint64_t
258 : 1505506 : _get_total_chunks(uint64_t vol_size, uint64_t chunk_size)
259 : : {
260 : : uint64_t num_chunks;
261 : :
262 [ - + ]: 1505506 : num_chunks = vol_size / chunk_size;
263 : 1505506 : num_chunks += REDUCE_NUM_EXTRA_CHUNKS;
264 : :
265 : 1505506 : return num_chunks;
266 : : }
267 : :
268 : : static inline uint32_t
269 : 2002128 : _reduce_vol_get_chunk_struct_size(uint64_t backing_io_units_per_chunk)
270 : : {
271 : 2002128 : return sizeof(struct spdk_reduce_chunk_map) + sizeof(uint64_t) * backing_io_units_per_chunk;
272 : : }
273 : :
274 : : static uint64_t
275 : 20 : _get_pm_total_chunks_size(uint64_t vol_size, uint64_t chunk_size, uint64_t backing_io_unit_size)
276 : : {
277 : : uint64_t io_units_per_chunk, num_chunks, total_chunks_size;
278 : :
279 : 20 : num_chunks = _get_total_chunks(vol_size, chunk_size);
280 [ - + ]: 20 : io_units_per_chunk = chunk_size / backing_io_unit_size;
281 : :
282 : 20 : total_chunks_size = num_chunks * _reduce_vol_get_chunk_struct_size(io_units_per_chunk);
283 : :
284 : 20 : return spdk_divide_round_up(total_chunks_size, REDUCE_PM_SIZE_ALIGNMENT) *
285 : : REDUCE_PM_SIZE_ALIGNMENT;
286 : : }
287 : :
288 : : static struct spdk_reduce_chunk_map *
289 : 1505466 : _reduce_vol_get_chunk_map(struct spdk_reduce_vol *vol, uint64_t chunk_map_index)
290 : : {
291 : : uintptr_t chunk_map_addr;
292 : :
293 [ - + ]: 1505466 : assert(chunk_map_index < _get_total_chunks(vol->params.vol_size, vol->params.chunk_size));
294 : :
295 : 1505466 : chunk_map_addr = (uintptr_t)vol->pm_chunk_maps;
296 : 1505466 : chunk_map_addr += chunk_map_index *
297 : 1505466 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
298 : :
299 : 1505466 : return (struct spdk_reduce_chunk_map *)chunk_map_addr;
300 : : }
301 : :
302 : : static int
303 : 20 : _validate_vol_params(struct spdk_reduce_vol_params *params)
304 : : {
305 [ - + ]: 20 : if (params->vol_size > 0) {
306 : : /**
307 : : * User does not pass in the vol size - it gets calculated by libreduce from
308 : : * values in this structure plus the size of the backing device.
309 : : */
310 : 0 : return -EINVAL;
311 : : }
312 : :
313 [ + - + - ]: 20 : if (params->chunk_size == 0 || params->backing_io_unit_size == 0 ||
314 [ - + ]: 20 : params->logical_block_size == 0) {
315 : 0 : return -EINVAL;
316 : : }
317 : :
318 : : /* Chunk size must be an even multiple of the backing io unit size. */
319 [ - + - + ]: 20 : if ((params->chunk_size % params->backing_io_unit_size) != 0) {
320 : 0 : return -EINVAL;
321 : : }
322 : :
323 : : /* Chunk size must be an even multiple of the logical block size. */
324 [ - + - + ]: 20 : if ((params->chunk_size % params->logical_block_size) != 0) {
325 : 0 : return -1;
326 : : }
327 : :
328 : 20 : return 0;
329 : : }
330 : :
331 : : static uint64_t
332 : 20 : _get_vol_size(uint64_t chunk_size, uint64_t backing_dev_size)
333 : : {
334 : : uint64_t num_chunks;
335 : :
336 [ - + ]: 20 : num_chunks = backing_dev_size / chunk_size;
337 [ - + ]: 20 : if (num_chunks <= REDUCE_NUM_EXTRA_CHUNKS) {
338 : 0 : return 0;
339 : : }
340 : :
341 : 20 : num_chunks -= REDUCE_NUM_EXTRA_CHUNKS;
342 : 20 : return num_chunks * chunk_size;
343 : : }
344 : :
345 : : static uint64_t
346 : 20 : _get_pm_file_size(struct spdk_reduce_vol_params *params)
347 : : {
348 : : uint64_t total_pm_size;
349 : :
350 : 20 : total_pm_size = sizeof(struct spdk_reduce_vol_superblock);
351 : 20 : total_pm_size += _get_pm_logical_map_size(params->vol_size, params->chunk_size);
352 : 20 : total_pm_size += _get_pm_total_chunks_size(params->vol_size, params->chunk_size,
353 : 20 : params->backing_io_unit_size);
354 : 20 : return total_pm_size;
355 : : }
356 : :
357 : : const struct spdk_uuid *
358 : 0 : spdk_reduce_vol_get_uuid(struct spdk_reduce_vol *vol)
359 : : {
360 : 0 : return &vol->params.uuid;
361 : : }
362 : :
363 : : static void
364 : 20 : _initialize_vol_pm_pointers(struct spdk_reduce_vol *vol)
365 : : {
366 : : uint64_t logical_map_size;
367 : :
368 : : /* Superblock is at the beginning of the pm file. */
369 : 20 : vol->pm_super = (struct spdk_reduce_vol_superblock *)vol->pm_file.pm_buf;
370 : :
371 : : /* Logical map immediately follows the super block. */
372 : 20 : vol->pm_logical_map = (uint64_t *)(vol->pm_super + 1);
373 : :
374 : : /* Chunks maps follow the logical map. */
375 : 20 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size, vol->params.chunk_size);
376 : 20 : vol->pm_chunk_maps = (uint64_t *)((uint8_t *)vol->pm_logical_map + logical_map_size);
377 : 20 : }
378 : :
379 : : struct reduce_mblk_init_ctx {
380 : : /* unit: bytes */
381 : : void *meta_init_buf;
382 : : size_t buf_len;
383 : : /* unit: backing blocklen */
384 : : uint64_t off_need_init;
385 : : uint64_t len_need_init;
386 : : uint64_t off_need_init_next;
387 : : uint32_t maxlen_per_io;
388 : : };
389 : :
390 : : /* We need 2 iovs during load - one for the superblock, another for the path */
391 : : #define LOAD_IOV_COUNT 2
392 : :
393 : : struct reduce_init_load_ctx {
394 : : struct spdk_reduce_vol *vol;
395 : : struct spdk_reduce_vol_cb_args backing_cb_args;
396 : : spdk_reduce_vol_op_with_handle_complete cb_fn;
397 : : void *cb_arg;
398 : : struct iovec iov[LOAD_IOV_COUNT];
399 : : void *path;
400 : : struct spdk_reduce_backing_io *backing_io;
401 : : struct reduce_mblk_init_ctx mblk_init_ctx;
402 : : };
403 : :
404 : : static inline bool
405 : 12244 : _addr_crosses_huge_page(const void *addr, size_t *size)
406 : : {
407 : : size_t _size;
408 : : uint64_t rc;
409 : :
410 [ - + ]: 12244 : assert(size);
411 : :
412 : 12244 : _size = *size;
413 : 12244 : rc = spdk_vtophys(addr, size);
414 : :
415 [ + - - + ]: 12244 : return rc == SPDK_VTOPHYS_ERROR || _size != *size;
416 : : }
417 : :
418 : : static inline int
419 : 10240 : _set_buffer(uint8_t **vol_buffer, uint8_t **_addr, uint8_t *addr_range, size_t buffer_size)
420 : : {
421 : : uint8_t *addr;
422 : 10240 : size_t size_tmp = buffer_size;
423 : :
424 : 10240 : addr = *_addr;
425 : :
426 : : /* Verify that addr + buffer_size doesn't cross huge page boundary */
427 [ - + ]: 10240 : if (_addr_crosses_huge_page(addr, &size_tmp)) {
428 : : /* Memory start is aligned on 2MiB, so buffer should be located at the end of the page.
429 : : * Skip remaining bytes and continue from the beginning of the next page */
430 : 0 : addr += size_tmp;
431 : : }
432 : :
433 [ - + ]: 10240 : if (addr + buffer_size > addr_range) {
434 : 0 : SPDK_ERRLOG("Vol buffer %p out of range %p\n", addr, addr_range);
435 : 0 : return -ERANGE;
436 : : }
437 : :
438 : 10240 : *vol_buffer = addr;
439 : 10240 : *_addr = addr + buffer_size;
440 : :
441 : 10240 : return 0;
442 : : }
443 : :
444 : : static int
445 : 20 : _allocate_vol_requests(struct spdk_reduce_vol *vol)
446 : : {
447 : : struct spdk_reduce_vol_request *req;
448 : 20 : struct spdk_reduce_backing_dev *backing_dev = vol->backing_dev;
449 : : uint32_t reqs_in_2mb_page, huge_pages_needed, request_memlen;
450 : : uint8_t *buffer, *buffer_end;
451 : 20 : int i = 0;
452 : 20 : int rc = 0;
453 : :
454 : : /* It is needed to allocate comp and decomp buffers so that they do not cross physical
455 : : * page boundaries. Assume that the system uses default 2MiB pages and chunk_size is not
456 : : * necessarily power of 2
457 : : * Allocate 2x since we need buffers for both read/write and compress/decompress
458 : : * intermediate buffers. */
459 [ - + ]: 20 : reqs_in_2mb_page = VALUE_2MB / (vol->params.chunk_size * 2);
460 [ - + ]: 20 : if (!reqs_in_2mb_page) {
461 : 0 : return -EINVAL;
462 : : }
463 [ - + ]: 20 : huge_pages_needed = SPDK_CEIL_DIV(REDUCE_NUM_VOL_REQUESTS, reqs_in_2mb_page);
464 : :
465 : 20 : vol->buf_mem = spdk_dma_malloc(VALUE_2MB * huge_pages_needed, VALUE_2MB, NULL);
466 [ - + ]: 20 : if (vol->buf_mem == NULL) {
467 : 0 : return -ENOMEM;
468 : : }
469 : :
470 : 20 : request_memlen = sizeof(*req);
471 [ - + ]: 20 : if (vol->params.meta_builtin) {
472 : 0 : request_memlen = sizeof(*req) + _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
473 : : }
474 : 20 : vol->request_mem = calloc(REDUCE_NUM_VOL_REQUESTS, request_memlen);
475 [ - + ]: 20 : if (vol->request_mem == NULL) {
476 : 0 : spdk_free(vol->buf_mem);
477 : 0 : vol->buf_mem = NULL;
478 : 0 : return -ENOMEM;
479 : : }
480 : :
481 : : /* Allocate 2x since we need iovs for both read/write and compress/decompress intermediate
482 : : * buffers.
483 : : */
484 : 20 : vol->buf_iov_mem = calloc(REDUCE_NUM_VOL_REQUESTS,
485 : 20 : 2 * sizeof(struct iovec) * vol->backing_io_units_per_chunk);
486 [ - + ]: 20 : if (vol->buf_iov_mem == NULL) {
487 : 0 : free(vol->request_mem);
488 : 0 : spdk_free(vol->buf_mem);
489 : 0 : vol->request_mem = NULL;
490 : 0 : vol->buf_mem = NULL;
491 : 0 : return -ENOMEM;
492 : : }
493 : :
494 : 20 : vol->buf_backing_io_mem = calloc(REDUCE_NUM_VOL_REQUESTS, (sizeof(struct spdk_reduce_backing_io) +
495 : 20 : backing_dev->user_ctx_size) * vol->backing_io_units_per_chunk);
496 [ - + ]: 20 : if (vol->buf_backing_io_mem == NULL) {
497 : 0 : free(vol->request_mem);
498 : 0 : free(vol->buf_iov_mem);
499 : 0 : spdk_free(vol->buf_mem);
500 : 0 : vol->request_mem = NULL;
501 : 0 : vol->buf_iov_mem = NULL;
502 : 0 : vol->buf_mem = NULL;
503 : 0 : return -ENOMEM;
504 : : }
505 : :
506 : 20 : buffer = vol->buf_mem;
507 : 20 : buffer_end = buffer + VALUE_2MB * huge_pages_needed;
508 : :
509 [ + + ]: 5140 : for (i = 0; i < REDUCE_NUM_VOL_REQUESTS; i++) {
510 : 5120 : req = (void *)vol->request_mem + i * request_memlen;
511 [ + + ]: 5120 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
512 [ - + ]: 5120 : if (vol->params.meta_builtin) {
513 : 0 : req->prealloc_chunk = (void *)req + sizeof(*req);
514 : : }
515 : 10240 : req->backing_io = (struct spdk_reduce_backing_io *)(vol->buf_backing_io_mem + i *
516 : 5120 : (sizeof(struct spdk_reduce_backing_io) + backing_dev->user_ctx_size) *
517 : 5120 : vol->backing_io_units_per_chunk);
518 : :
519 : 5120 : req->decomp_buf_iov = &vol->buf_iov_mem[(2 * i) * vol->backing_io_units_per_chunk];
520 : 5120 : req->comp_buf_iov = &vol->buf_iov_mem[(2 * i + 1) * vol->backing_io_units_per_chunk];
521 : :
522 : 5120 : rc = _set_buffer(&req->comp_buf, &buffer, buffer_end, vol->params.chunk_size);
523 [ - + ]: 5120 : if (rc) {
524 : 0 : SPDK_ERRLOG("Failed to set comp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
525 : : vol->buf_mem, buffer_end);
526 : 0 : break;
527 : : }
528 : 5120 : rc = _set_buffer(&req->decomp_buf, &buffer, buffer_end, vol->params.chunk_size);
529 [ - + ]: 5120 : if (rc) {
530 : 0 : SPDK_ERRLOG("Failed to set decomp buffer for req idx %u, addr %p, start %p, end %p\n", i, buffer,
531 : : vol->buf_mem, buffer_end);
532 : 0 : break;
533 : : }
534 : : }
535 : :
536 [ - + ]: 20 : if (rc) {
537 : 0 : free(vol->buf_backing_io_mem);
538 : 0 : free(vol->buf_iov_mem);
539 : 0 : free(vol->request_mem);
540 : 0 : spdk_free(vol->buf_mem);
541 : 0 : vol->buf_mem = NULL;
542 : 0 : vol->buf_backing_io_mem = NULL;
543 : 0 : vol->buf_iov_mem = NULL;
544 : 0 : vol->request_mem = NULL;
545 : : }
546 : :
547 : 20 : return rc;
548 : : }
549 : :
550 : : const struct spdk_reduce_vol_info *
551 : 20 : spdk_reduce_vol_get_info(const struct spdk_reduce_vol *vol)
552 : : {
553 : 20 : return &vol->info;
554 : : }
555 : :
556 : : static void
557 : 504 : _init_load_cleanup(struct spdk_reduce_vol *vol, struct reduce_init_load_ctx *ctx)
558 : : {
559 [ + + ]: 504 : if (ctx != NULL) {
560 : 464 : spdk_free(ctx->path);
561 : 464 : spdk_free(ctx->mblk_init_ctx.meta_init_buf);
562 : 464 : free(ctx->backing_io);
563 : 464 : free(ctx);
564 : : }
565 : :
566 [ + + ]: 504 : if (vol != NULL) {
567 [ + + ]: 464 : if (vol->pm_file.pm_buf != NULL) {
568 : 20 : pmem_unmap(vol->pm_file.pm_buf, vol->pm_file.size);
569 : : }
570 : :
571 : 464 : spdk_free(vol->backing_super);
572 : 464 : spdk_bit_array_free(&vol->allocated_chunk_maps);
573 : 464 : spdk_bit_array_free(&vol->allocated_backing_io_units);
574 : 464 : free(vol->request_mem);
575 : 464 : free(vol->buf_backing_io_mem);
576 : 464 : free(vol->buf_iov_mem);
577 : 464 : spdk_free(vol->buf_mem);
578 : 464 : spdk_free(vol->metablock_buf);
579 : 464 : free(vol->metablock_extent_mem);
580 : 464 : free(vol->meta_request_buf);
581 : 464 : free(vol);
582 : : }
583 : 504 : }
584 : :
585 : : static int
586 : 464 : _alloc_zero_buff(void)
587 : : {
588 : 464 : int rc = 0;
589 : :
590 : : /* The zero buffer is shared between all volumes and just used
591 : : * for reads so allocate one global instance here if not already
592 : : * allocated when another vol init'd or loaded.
593 : : */
594 [ + + ]: 464 : if (g_vol_count++ == 0) {
595 : 76 : g_zero_buf = spdk_zmalloc(REDUCE_ZERO_BUF_SIZE,
596 : : 64, NULL, SPDK_ENV_LCORE_ID_ANY,
597 : : SPDK_MALLOC_DMA);
598 [ - + ]: 76 : if (g_zero_buf == NULL) {
599 : 0 : g_vol_count--;
600 : 0 : rc = -ENOMEM;
601 : : }
602 : : }
603 : 464 : return rc;
604 : : }
605 : :
606 : : static int
607 : 0 : _allocate_vol_metablock_and_metaio(struct spdk_reduce_vol *vol)
608 : : {
609 : : uint32_t mblk_in_2mb_page, total_mblk, total_metareq, huge_pages_needed, backing_io_struct_size, i;
610 : : struct reduce_metablock_extent *mblk_extent;
611 : : struct reduce_meta_request *meta_request;
612 : 0 : int rc = 0;
613 : :
614 : 0 : mblk_in_2mb_page = VALUE_2MB / REDUCE_META_BLKSZ;
615 [ # # ]: 0 : huge_pages_needed = SPDK_CEIL_DIV(REDUCE_META_CACHE_BLOCK_NUM, mblk_in_2mb_page);
616 : 0 : total_mblk = huge_pages_needed * mblk_in_2mb_page;
617 : 0 : vol->metablocks_num = total_mblk;
618 : :
619 : : /* alloc metablock mem */
620 : 0 : vol->metablock_buf = spdk_dma_malloc(VALUE_2MB * huge_pages_needed, VALUE_2MB, NULL);
621 [ # # ]: 0 : if (vol->metablock_buf == NULL) {
622 : 0 : rc = -ENOMEM;
623 : 0 : goto err;
624 : : }
625 : :
626 : : /* alloc metablock control struct */
627 : 0 : vol->metablock_extent_mem = calloc(total_mblk, sizeof(*vol->metablock_extent_mem));
628 [ # # ]: 0 : if (vol->request_mem == NULL) {
629 : 0 : rc = -ENOMEM;
630 : 0 : goto err;
631 : : }
632 [ # # ]: 0 : for (i = 0; i < total_mblk; i++) {
633 : 0 : mblk_extent = &vol->metablock_extent_mem[i];
634 : 0 : mblk_extent->iov.iov_base = vol->metablock_buf + i * REDUCE_META_BLKSZ;
635 : 0 : mblk_extent->iov.iov_len = REDUCE_META_BLKSZ;
636 : 0 : TAILQ_INIT(&mblk_extent->pending_to_supply_read);
637 : 0 : TAILQ_INIT(&mblk_extent->pending_to_write);
638 [ # # ]: 0 : TAILQ_INSERT_HEAD(&vol->metablocks_free, mblk_extent, tailq);
639 : : }
640 : :
641 : : /* alloc reduce_meta_request struct mem
642 : : * Use these struct memory to record the ctx before meta read/write operetion, wait complete callback to restore them.
643 : : */
644 : 0 : backing_io_struct_size = sizeof(struct reduce_meta_request) + vol->backing_dev->user_ctx_size;
645 : : /* every vol date request may need 2 meta req for logical_map and chunk_map */
646 : 0 : total_metareq = 2 * REDUCE_NUM_VOL_REQUESTS;
647 : 0 : vol->meta_request_buf = (struct reduce_meta_request *)calloc(total_metareq, backing_io_struct_size);
648 [ # # ]: 0 : if (vol->meta_request_buf == NULL) {
649 : 0 : rc = -ENOMEM;
650 : 0 : goto err;
651 : : }
652 [ # # ]: 0 : for (i = 0; i < total_metareq; i++) {
653 : 0 : meta_request = (struct reduce_meta_request *)((uint8_t *)vol->meta_request_buf + i *
654 : : backing_io_struct_size);
655 : 0 : meta_request->backing_io.backing_cb_args = &meta_request->cb_args;
656 [ # # ]: 0 : TAILQ_INSERT_HEAD(&vol->free_meta_request, meta_request, tailq);
657 : : }
658 : :
659 : 0 : return 0;
660 : 0 : err:
661 : 0 : spdk_free(vol->metablock_buf);
662 : 0 : free(vol->metablock_extent_mem);
663 : 0 : free(vol->meta_request_buf);
664 : 0 : vol->metablock_buf = NULL;
665 : 0 : vol->metablock_extent_mem = NULL;
666 : 0 : vol->meta_request_buf = NULL;
667 : :
668 : 0 : return rc;
669 : : }
670 : :
671 : : static void
672 : 20 : _init_write_super_cpl(void *cb_arg, int reduce_errno)
673 : : {
674 : 20 : struct reduce_init_load_ctx *init_ctx = cb_arg;
675 : 20 : int rc = 0;
676 : :
677 [ - + ]: 20 : if (reduce_errno != 0) {
678 : 0 : rc = reduce_errno;
679 : 0 : goto err;
680 : : }
681 : :
682 : 20 : rc = _allocate_vol_requests(init_ctx->vol);
683 [ - + ]: 20 : if (rc != 0) {
684 : 0 : goto err;
685 : : }
686 : :
687 [ - + ]: 20 : if (init_ctx->vol->params.meta_builtin) {
688 : 0 : rc = _allocate_vol_metablock_and_metaio(init_ctx->vol);
689 [ # # ]: 0 : if (rc != 0) {
690 : 0 : goto err;
691 : : }
692 : : }
693 : :
694 : 20 : rc = _alloc_zero_buff();
695 [ - + ]: 20 : if (rc != 0) {
696 : 0 : goto err;
697 : : }
698 : :
699 : 20 : init_ctx->cb_fn(init_ctx->cb_arg, init_ctx->vol, rc);
700 : : /* Only clean up the ctx - the vol has been passed to the application
701 : : * for use now that initialization was successful.
702 : : */
703 : 20 : _init_load_cleanup(NULL, init_ctx);
704 : :
705 : 20 : return;
706 : 0 : err:
707 [ # # # # ]: 0 : if (unlink(init_ctx->path)) {
708 : 0 : SPDK_ERRLOG("%s could not be unlinked: %s\n",
709 : : (char *)init_ctx->path, spdk_strerror(errno));
710 : : }
711 : :
712 : 0 : init_ctx->cb_fn(init_ctx->cb_arg, NULL, rc);
713 : 0 : _init_load_cleanup(init_ctx->vol, init_ctx);
714 : : }
715 : :
716 : : static void
717 : 20 : _init_write_path_cpl(void *cb_arg, int reduce_errno)
718 : : {
719 : 20 : struct reduce_init_load_ctx *init_ctx = cb_arg;
720 : 20 : struct spdk_reduce_vol *vol = init_ctx->vol;
721 : 20 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
722 : :
723 [ - + ]: 20 : if (reduce_errno != 0) {
724 : 0 : _init_write_super_cpl(cb_arg, reduce_errno);
725 : 0 : return;
726 : : }
727 : :
728 : 20 : init_ctx->iov[0].iov_base = vol->backing_super;
729 : 20 : init_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
730 : 20 : init_ctx->backing_cb_args.cb_fn = _init_write_super_cpl;
731 : 20 : init_ctx->backing_cb_args.cb_arg = init_ctx;
732 : :
733 : 20 : backing_io->dev = vol->backing_dev;
734 : 20 : backing_io->iov = init_ctx->iov;
735 : 20 : backing_io->iovcnt = 1;
736 : 20 : backing_io->lba = 0;
737 [ - + ]: 20 : backing_io->lba_count = sizeof(*vol->backing_super) / vol->backing_dev->blocklen;
738 : 20 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
739 : 20 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
740 : :
741 : 20 : vol->backing_dev->submit_backing_io(backing_io);
742 : : }
743 : :
744 : : static void
745 : 20 : _init_builtin_meta_region_cpl(void *cb_arg, int reduce_errno)
746 : : {
747 : 20 : struct reduce_init_load_ctx *init_ctx = cb_arg;
748 : 20 : struct spdk_reduce_vol *vol = init_ctx->vol;
749 : 20 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
750 : :
751 [ - + ]: 20 : if (reduce_errno != 0) {
752 : 0 : _init_write_path_cpl(cb_arg, reduce_errno);
753 : 0 : return;
754 : : }
755 : :
756 : 20 : init_ctx->iov[0].iov_base = init_ctx->path;
757 : 20 : init_ctx->iov[0].iov_len = REDUCE_PATH_MAX;
758 : 20 : init_ctx->backing_cb_args.cb_fn = _init_write_path_cpl;
759 : 20 : init_ctx->backing_cb_args.cb_arg = init_ctx;
760 : : /* Write path to offset 4K on backing device - just after where the super
761 : : * block will be written. We wait until this is committed before writing the
762 : : * super block to guarantee we don't get the super block written without the
763 : : * the path if the system crashed in the middle of a write operation.
764 : : */
765 : 20 : backing_io->dev = vol->backing_dev;
766 : 20 : backing_io->iov = init_ctx->iov;
767 : 20 : backing_io->iovcnt = 1;
768 [ - + ]: 20 : backing_io->lba = REDUCE_BACKING_DEV_PATH_OFFSET / vol->backing_dev->blocklen;
769 [ - + ]: 20 : backing_io->lba_count = REDUCE_PATH_MAX / vol->backing_dev->blocklen;
770 : 20 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
771 : 20 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
772 : :
773 : 20 : vol->backing_dev->submit_backing_io(backing_io);
774 : : }
775 : :
776 : : static void
777 : 0 : _init_builtin_meta_region(void *cb_arg, int reduce_errno)
778 : : {
779 : 0 : struct reduce_init_load_ctx *init_ctx = cb_arg;
780 : 0 : struct spdk_reduce_vol *vol = init_ctx->vol;
781 : 0 : struct reduce_mblk_init_ctx *ctx = &init_ctx->mblk_init_ctx;
782 : 0 : struct spdk_reduce_backing_io *backing_io = init_ctx->backing_io;
783 : : uint64_t offset, len;
784 : :
785 [ # # # # ]: 0 : if (reduce_errno || ctx->off_need_init_next == ctx->off_need_init + ctx->len_need_init) {
786 : 0 : _init_builtin_meta_region_cpl(cb_arg, reduce_errno);
787 : 0 : return;
788 : : }
789 : :
790 : 0 : offset = ctx->off_need_init_next;
791 : 0 : len = spdk_min(ctx->maxlen_per_io,
792 : : ctx->off_need_init + ctx->len_need_init - ctx->off_need_init_next);
793 : 0 : ctx->off_need_init_next = offset + len;
794 : :
795 : 0 : init_ctx->iov[0].iov_base = ctx->meta_init_buf;
796 : 0 : init_ctx->iov[0].iov_len = len * vol->backing_dev->blocklen;
797 : 0 : init_ctx->backing_cb_args.cb_fn = _init_builtin_meta_region;
798 : 0 : init_ctx->backing_cb_args.cb_arg = init_ctx;
799 : :
800 : 0 : backing_io->dev = vol->backing_dev;
801 : 0 : backing_io->iov = init_ctx->iov;
802 : 0 : backing_io->iovcnt = 1;
803 : 0 : backing_io->lba = offset;
804 : 0 : backing_io->lba_count = len;
805 : 0 : backing_io->backing_cb_args = &init_ctx->backing_cb_args;
806 : 0 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
807 : :
808 : 0 : vol->backing_dev->submit_backing_io(backing_io);
809 : : }
810 : :
811 : : static void
812 : 0 : _init_builtin_meta_region_ctx_init(struct reduce_init_load_ctx *init_ctx)
813 : : {
814 : 0 : struct reduce_mblk_init_ctx *mblk_init_ctx = &init_ctx->mblk_init_ctx;
815 : 0 : struct spdk_reduce_vol *vol = init_ctx->vol;
816 : :
817 : 0 : mblk_init_ctx->off_need_init = vol->params.meta_region_desc[REDUCE_MTYPE_SB].offset /
818 : 0 : REDUCE_META_BLKSZ *
819 : 0 : vol->backing_dev->blocklen;
820 [ # # ]: 0 : mblk_init_ctx->len_need_init = vol->params.data_region_off / vol->backing_dev->blocklen -
821 : 0 : mblk_init_ctx->off_need_init;
822 : 0 : mblk_init_ctx->off_need_init_next = mblk_init_ctx->off_need_init;
823 [ # # ]: 0 : mblk_init_ctx->maxlen_per_io = mblk_init_ctx->buf_len / vol->backing_dev->blocklen;
824 : 0 : }
825 : :
826 : : static int
827 : 20 : _allocate_bit_arrays(struct spdk_reduce_vol *vol)
828 : : {
829 : : uint64_t total_chunks, total_backing_io_units;
830 : : uint32_t i, num_metadata_io_units;
831 : :
832 : 20 : total_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
833 [ - + ]: 20 : if (vol->params.meta_builtin) {
834 [ # # ]: 0 : total_chunks += vol->params.data_region_off / vol->params.chunk_size;
835 : : }
836 : 20 : vol->allocated_chunk_maps = spdk_bit_array_create(total_chunks);
837 : 20 : vol->find_chunk_offset = 0;
838 [ - + ]: 20 : total_backing_io_units = total_chunks * (vol->params.chunk_size / vol->params.backing_io_unit_size);
839 : 20 : vol->allocated_backing_io_units = spdk_bit_array_create(total_backing_io_units);
840 : 20 : vol->find_block_offset = 0;
841 : :
842 [ + - - + ]: 20 : if (vol->allocated_chunk_maps == NULL || vol->allocated_backing_io_units == NULL) {
843 : 0 : return -ENOMEM;
844 : : }
845 : :
846 : : /* Set backing io unit bits associated with metadata. */
847 [ - + ]: 20 : if (vol->params.meta_builtin) {
848 [ # # ]: 0 : num_metadata_io_units = vol->params.data_region_off / vol->params.backing_io_unit_size;
849 : : } else {
850 : 20 : num_metadata_io_units = (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
851 [ - + ]: 20 : vol->params.backing_io_unit_size;
852 : : }
853 [ + + ]: 60 : for (i = 0; i < num_metadata_io_units; i++) {
854 : 40 : spdk_bit_array_set(vol->allocated_backing_io_units, i);
855 : 40 : vol->info.allocated_io_units++;
856 : : }
857 : :
858 : 20 : return 0;
859 : : }
860 : :
861 : : static int
862 : 7676412 : overlap_cmp(struct spdk_reduce_vol_request *req1, struct spdk_reduce_vol_request *req2)
863 : : {
864 [ + + ]: 13690312 : return (req1->logical_map_index < req2->logical_map_index ? -1 : req1->logical_map_index >
865 : 6013900 : req2->logical_map_index);
866 : : }
867 [ + + + + : 15209923 : RB_GENERATE_STATIC(executing_req_tree, spdk_reduce_vol_request, rbnode, overlap_cmp);
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ + + + +
+ + + ]
868 : :
869 : : static inline int
870 : 20 : _check_meta_builtin(const char *path)
871 : : {
872 [ - + ]: 20 : return strlen(path) == strlen(REDUCE_META_BUILTIN) &&
873 [ - + - - : 20 : memcmp(path, REDUCE_META_BUILTIN, sizeof(REDUCE_META_BUILTIN)) == 0 ? 1 : 0;
- - ]
874 : : }
875 : :
876 : : static inline uint32_t
877 : 0 : _meta_get_elem_struct_size(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype)
878 : : {
879 : 0 : uint32_t size = 0;
880 : :
881 [ # # # # : 0 : switch (mtype) {
# ]
882 : 0 : case REDUCE_MTYPE_SB:
883 : 0 : size = sizeof(struct spdk_reduce_vol_superblock);
884 : 0 : break;
885 : 0 : case REDUCE_MTYPE_PATH:
886 : 0 : size = REDUCE_PATH_MAX;
887 : 0 : break;
888 : 0 : case REDUCE_MTYPE_LOGICAL_MAP:
889 : 0 : size = sizeof(uint64_t);
890 : 0 : break;
891 : 0 : case REDUCE_MTYPE_CHUNK_MAP:
892 [ # # ]: 0 : size = _reduce_vol_get_chunk_struct_size(params->chunk_size / params->backing_io_unit_size);
893 : 0 : break;
894 : 0 : default:
895 : 0 : break;
896 : : }
897 : :
898 : 0 : return size;
899 : : }
900 : :
901 : : static inline uint64_t
902 : 0 : _meta_get_elem_count(struct spdk_reduce_vol_params *params, enum spdk_reduce_meta_type mtype,
903 : : uint64_t backing_dev_size)
904 : : {
905 : 0 : uint64_t count = 0;
906 : :
907 [ # # # ]: 0 : switch (mtype) {
908 : 0 : case REDUCE_MTYPE_SB:
909 : : case REDUCE_MTYPE_PATH:
910 : 0 : count = 1;
911 : 0 : break;
912 : 0 : case REDUCE_MTYPE_LOGICAL_MAP:
913 : : case REDUCE_MTYPE_CHUNK_MAP:
914 [ # # ]: 0 : count = backing_dev_size / params->chunk_size;
915 : 0 : break;
916 : 0 : default:
917 : 0 : assert(0);
918 : : }
919 : :
920 : 0 : return count;
921 : : }
922 : :
923 : :
924 : : static uint64_t
925 : 0 : _meta_builtin_get_vol_size(uint64_t backing_dev_size, struct spdk_reduce_vol_params *params)
926 : : {
927 : : uint64_t num_chunks;
928 : :
929 [ # # ]: 0 : num_chunks = (backing_dev_size - params->data_region_off) / params->chunk_size;
930 : 0 : num_chunks -= REDUCE_NUM_EXTRA_CHUNKS;
931 : :
932 : 0 : return num_chunks * params->chunk_size;
933 : : }
934 : :
935 : : /* Calc the metaregion offset and length according backing dev size,
936 : : * then datasize = backing dev size - metasize.
937 : : * The data region that 'logical_map' and 'chunkmap' can represent is a little more than the true data region.
938 : : * It is neccessary to set the bits on the allocated bitarray of allocated_backing_io_units which represent meta region.
939 : : */
940 : : static void
941 : 0 : _init_builtin_meta_region_params(struct spdk_reduce_vol_params *params, uint64_t backing_dev_size)
942 : : {
943 : : /* tmpoff unit is REDUCE_META_BLKSZ */
944 : 0 : uint64_t tmpoff = 0;
945 : : uint32_t mtype;
946 : 0 : struct spdk_reduce_meta_desc *meta_region_desc = params->meta_region_desc;
947 : :
948 [ # # ]: 0 : for (mtype = REDUCE_MTYPE_SB; mtype < REDUCE_MTYPE_NR; mtype++) {
949 : : /* Although size_per_elem and elems_per_mblk could calc at runtime,
950 : : * calc it at init time could avoid calc it every time when edit a new mblk,
951 : : * especially at I/O path.
952 : : */
953 : 0 : meta_region_desc[mtype].size_per_elem = _meta_get_elem_struct_size(params, mtype);
954 [ # # ]: 0 : meta_region_desc[mtype].elems_per_mblk = REDUCE_META_BLKSZ / meta_region_desc[mtype].size_per_elem;
955 : 0 : meta_region_desc[mtype].offset = tmpoff;
956 : 0 : meta_region_desc[mtype].length = spdk_divide_round_up(_meta_get_elem_count(params, mtype,
957 : 0 : backing_dev_size), meta_region_desc[mtype].elems_per_mblk);
958 : 0 : tmpoff += meta_region_desc[mtype].length;
959 : : }
960 : :
961 : : /* data */
962 : 0 : params->data_region_off = tmpoff * REDUCE_META_BLKSZ;
963 : 0 : params->vol_size = _meta_builtin_get_vol_size(backing_dev_size, params);
964 : 0 : }
965 : :
966 : : static int
967 : 0 : _alloc_meta_buf(struct spdk_reduce_vol *vol, struct reduce_init_load_ctx *init_ctx)
968 : : {
969 : 0 : struct reduce_mblk_init_ctx *mblk_init_ctx = &init_ctx->mblk_init_ctx;
970 : :
971 : 0 : mblk_init_ctx->buf_len = SPDK_ALIGN_FLOOR(REDUCE_META_INIT_BUF_SIZE, REDUCE_META_BLKSZ);
972 : 0 : mblk_init_ctx->meta_init_buf = spdk_malloc(mblk_init_ctx->buf_len,
973 : : 64, NULL, SPDK_ENV_LCORE_ID_ANY,
974 : : SPDK_MALLOC_DMA);
975 [ # # ]: 0 : if (mblk_init_ctx->meta_init_buf == NULL) {
976 : 0 : return -ENOMEM;
977 : : }
978 [ # # ]: 0 : memset(mblk_init_ctx->meta_init_buf, 0xff, mblk_init_ctx->buf_len);
979 : :
980 : 0 : return 0;
981 : : }
982 : :
983 : : void
984 : 20 : spdk_reduce_vol_init(struct spdk_reduce_vol_params *params,
985 : : struct spdk_reduce_backing_dev *backing_dev,
986 : : const char *pm_file_dir,
987 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
988 : : {
989 : : struct spdk_reduce_vol *vol;
990 : : struct reduce_init_load_ctx *init_ctx;
991 : : struct spdk_reduce_backing_io *backing_io;
992 : : uint64_t backing_dev_size;
993 : : size_t mapped_len;
994 : 20 : int dir_len = 0, max_dir_len, rc;
995 : :
996 : 20 : rc = _validate_vol_params(params);
997 [ - + ]: 20 : if (rc != 0) {
998 : 0 : SPDK_ERRLOG("invalid vol params\n");
999 : 0 : cb_fn(cb_arg, NULL, rc);
1000 : 0 : return;
1001 : : }
1002 : :
1003 : 20 : backing_dev_size = backing_dev->blockcnt * backing_dev->blocklen;
1004 : 20 : params->vol_size = _get_vol_size(params->chunk_size, backing_dev_size);
1005 [ - + ]: 20 : if (params->vol_size == 0) {
1006 : 0 : SPDK_ERRLOG("backing device is too small\n");
1007 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1008 : 0 : return;
1009 : : }
1010 : :
1011 [ - + ]: 20 : if (backing_dev->submit_backing_io == NULL) {
1012 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
1013 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1014 : 0 : return;
1015 : : }
1016 : :
1017 [ + - ]: 20 : if (spdk_uuid_is_null(¶ms->uuid)) {
1018 : 20 : spdk_uuid_generate(¶ms->uuid);
1019 : : }
1020 : :
1021 [ + - ]: 20 : if (!_check_meta_builtin(pm_file_dir)) {
1022 : : /* Use pmem to host metadata */
1023 : 20 : params->meta_builtin = false;
1024 : : /* We need to append a path separator and the UUID to the supplied
1025 : : * path.
1026 : : */
1027 : 20 : max_dir_len = REDUCE_PATH_MAX - SPDK_UUID_STRING_LEN - 1;
1028 [ - + ]: 20 : dir_len = strnlen(pm_file_dir, max_dir_len);
1029 : : /* Strip trailing slash if the user provided one - we will add it back
1030 : : * later when appending the filename.
1031 : : */
1032 [ - + ]: 20 : if (pm_file_dir[dir_len - 1] == '/') {
1033 : 0 : dir_len--;
1034 : : }
1035 [ - + ]: 20 : if (dir_len == max_dir_len) {
1036 : 0 : SPDK_ERRLOG("pm_file_dir (%s) too long\n", pm_file_dir);
1037 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1038 : 0 : return;
1039 : : }
1040 : : } else {
1041 : : /* Use backing dev space to hold metadata */
1042 : 0 : params->meta_builtin = true;
1043 : : }
1044 : :
1045 : 20 : vol = calloc(1, sizeof(*vol));
1046 [ - + ]: 20 : if (vol == NULL) {
1047 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1048 : 0 : return;
1049 : : }
1050 : :
1051 : 20 : TAILQ_INIT(&vol->free_requests);
1052 : 20 : RB_INIT(&vol->executing_requests);
1053 : 20 : TAILQ_INIT(&vol->queued_requests);
1054 : 20 : queue_init(&vol->free_chunks_queue);
1055 : 20 : queue_init(&vol->free_backing_blocks_queue);
1056 : 20 : TAILQ_INIT(&vol->metablocks_free);
1057 : 20 : TAILQ_INIT(&vol->metablocks_lru);
1058 : 20 : RB_INIT(&vol->metablocks_caching);
1059 : 20 : TAILQ_INIT(&vol->free_meta_request);
1060 : :
1061 : 20 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 0, NULL,
1062 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1063 [ - + ]: 20 : if (vol->backing_super == NULL) {
1064 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1065 : 0 : _init_load_cleanup(vol, NULL);
1066 : 0 : return;
1067 : : }
1068 : :
1069 : 20 : init_ctx = calloc(1, sizeof(*init_ctx));
1070 [ - + ]: 20 : if (init_ctx == NULL) {
1071 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1072 : 0 : _init_load_cleanup(vol, NULL);
1073 : 0 : return;
1074 : : }
1075 : :
1076 : 20 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1077 [ - + ]: 20 : if (backing_io == NULL) {
1078 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1079 : 0 : _init_load_cleanup(vol, init_ctx);
1080 : 0 : return;
1081 : : }
1082 : 20 : init_ctx->backing_io = backing_io;
1083 : :
1084 : 20 : init_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 0, NULL,
1085 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1086 [ - + ]: 20 : if (init_ctx->path == NULL) {
1087 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1088 : 0 : _init_load_cleanup(vol, init_ctx);
1089 : 0 : return;
1090 : : }
1091 : :
1092 [ - + ]: 20 : vol->backing_io_units_per_chunk = params->chunk_size / params->backing_io_unit_size;
1093 [ - + ]: 20 : vol->logical_blocks_per_chunk = params->chunk_size / params->logical_block_size;
1094 [ - + ]: 20 : vol->backing_lba_per_io_unit = params->backing_io_unit_size / backing_dev->blocklen;
1095 : 20 : vol->backing_dev = backing_dev;
1096 : :
1097 : : /* meta in pmem: init metadata in pmemdir.
1098 : : meta in backing_bdev: split total region to meta region and data region.
1099 : : */
1100 [ + - ]: 20 : if (params->meta_builtin == false) {
1101 [ - + ]: 20 : assert(dir_len != 0);
1102 [ - + - + ]: 20 : memcpy(vol->pm_file.path, pm_file_dir, dir_len);
1103 : 20 : vol->pm_file.path[dir_len] = '/';
1104 : 20 : spdk_uuid_fmt_lower(&vol->pm_file.path[dir_len + 1], SPDK_UUID_STRING_LEN,
1105 : 20 : ¶ms->uuid);
1106 : 20 : vol->pm_file.size = _get_pm_file_size(params);
1107 : 20 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, vol->pm_file.size,
1108 : : PMEM_FILE_CREATE | PMEM_FILE_EXCL, 0600,
1109 : : &mapped_len, &vol->pm_file.pm_is_pmem);
1110 [ - + ]: 20 : if (vol->pm_file.pm_buf == NULL) {
1111 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n",
1112 : : vol->pm_file.path, strerror(errno));
1113 : 0 : cb_fn(cb_arg, NULL, -errno);
1114 : 0 : _init_load_cleanup(vol, init_ctx);
1115 : 0 : return;
1116 : : }
1117 : :
1118 [ - + ]: 20 : if (vol->pm_file.size != mapped_len) {
1119 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
1120 : : vol->pm_file.size, mapped_len);
1121 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1122 : 0 : _init_load_cleanup(vol, init_ctx);
1123 : 0 : return;
1124 : : }
1125 : :
1126 [ - + - + ]: 20 : memcpy(&vol->params, params, sizeof(*params));
1127 : 20 : _initialize_vol_pm_pointers(vol);
1128 : : } else {
1129 : : /* metadata region on backingbdev
1130 : : |0 |4096 |8192... |
1131 : : |sb |mdpath |logical_map... |chunk_map...
1132 : : the order of initailization: pmpath, normalmeta(logical_map, chunk_map), sb
1133 : : */
1134 : 0 : _init_builtin_meta_region_params(params, backing_dev_size);
1135 [ # # # # ]: 0 : memcpy(&vol->params, params, sizeof(*params));
1136 : : }
1137 : :
1138 [ - + ]: 20 : memcpy(vol->backing_super->signature, SPDK_REDUCE_SIGNATURE,
1139 : : sizeof(vol->backing_super->signature));
1140 [ - + - + ]: 20 : memcpy(&vol->backing_super->params, params, sizeof(*params));
1141 : :
1142 [ + - ]: 20 : if (params->meta_builtin == false) {
1143 [ - + - + ]: 20 : memcpy(vol->pm_super, vol->backing_super, sizeof(*vol->backing_super));
1144 : : /* Writing 0xFF's is equivalent of filling it all with SPDK_EMPTY_MAP_ENTRY.
1145 : : * Note that this writes 0xFF to not just the logical map but the chunk maps as well.
1146 : : */
1147 [ - + ]: 20 : memset(vol->pm_logical_map, 0xFF, vol->pm_file.size - sizeof(*vol->backing_super));
1148 : 20 : _reduce_persist(vol, vol->pm_file.pm_buf, vol->pm_file.size);
1149 : : }
1150 : :
1151 : 20 : rc = _allocate_bit_arrays(vol);
1152 [ - + ]: 20 : if (rc != 0) {
1153 : 0 : cb_fn(cb_arg, NULL, rc);
1154 : 0 : _init_load_cleanup(vol, init_ctx);
1155 : 0 : return;
1156 : : }
1157 : :
1158 : 20 : init_ctx->vol = vol;
1159 : 20 : init_ctx->cb_fn = cb_fn;
1160 : 20 : init_ctx->cb_arg = cb_arg;
1161 : :
1162 [ - + ]: 20 : if (params->meta_builtin) {
1163 [ # # ]: 0 : memcpy(init_ctx->path, REDUCE_META_BUILTIN, sizeof(REDUCE_META_BUILTIN));
1164 : 0 : rc = _alloc_meta_buf(vol, init_ctx);
1165 [ # # ]: 0 : if (rc != 0) {
1166 : 0 : cb_fn(cb_arg, NULL, rc);
1167 : 0 : _init_load_cleanup(vol, init_ctx);
1168 : 0 : return;
1169 : : }
1170 : 0 : _init_builtin_meta_region_ctx_init(init_ctx);
1171 : 0 : _init_builtin_meta_region(init_ctx, 0);
1172 : : } else {
1173 [ - + - + ]: 20 : memcpy(init_ctx->path, vol->pm_file.path, REDUCE_PATH_MAX);
1174 : 20 : _init_builtin_meta_region_cpl(init_ctx, 0);
1175 : : }
1176 : : }
1177 : :
1178 : : static void destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno);
1179 : :
1180 : : static void
1181 : 444 : _load_read_super_and_path_cpl(void *cb_arg, int reduce_errno)
1182 : : {
1183 : 444 : struct reduce_init_load_ctx *load_ctx = cb_arg;
1184 : 444 : struct spdk_reduce_vol *vol = load_ctx->vol;
1185 : : uint64_t backing_dev_size;
1186 : : uint64_t i, num_chunks, logical_map_index;
1187 : : struct spdk_reduce_chunk_map *chunk;
1188 : : size_t mapped_len;
1189 : : uint32_t j;
1190 : : int rc;
1191 : :
1192 [ - + ]: 444 : if (reduce_errno != 0) {
1193 : 0 : rc = reduce_errno;
1194 : 0 : goto error;
1195 : : }
1196 : :
1197 : 444 : rc = _alloc_zero_buff();
1198 [ - + ]: 444 : if (rc) {
1199 : 0 : goto error;
1200 : : }
1201 : :
1202 [ - + + + ]: 444 : if (memcmp(vol->backing_super->signature,
1203 : : SPDK_REDUCE_SIGNATURE,
1204 : : sizeof(vol->backing_super->signature)) != 0) {
1205 : : /* This backing device isn't a libreduce backing device. */
1206 : 424 : rc = -EILSEQ;
1207 : 424 : goto error;
1208 : : }
1209 : :
1210 : : /* If the cb_fn is destroy_load_cb, it means we are wanting to destroy this compress bdev.
1211 : : * So don't bother getting the volume ready to use - invoke the callback immediately
1212 : : * so destroy_load_cb can delete the metadata off of the block device and delete the
1213 : : * persistent memory file if it exists.
1214 : : */
1215 [ - + - + ]: 20 : memcpy(vol->pm_file.path, load_ctx->path, sizeof(vol->pm_file.path));
1216 [ + - ]: 20 : if (load_ctx->cb_fn == (*destroy_load_cb)) {
1217 : 20 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1218 : 20 : _init_load_cleanup(NULL, load_ctx);
1219 : 20 : return;
1220 : : }
1221 : :
1222 [ # # # # ]: 0 : memcpy(&vol->params, &vol->backing_super->params, sizeof(vol->params));
1223 [ # # ]: 0 : vol->backing_io_units_per_chunk = vol->params.chunk_size / vol->params.backing_io_unit_size;
1224 [ # # ]: 0 : vol->logical_blocks_per_chunk = vol->params.chunk_size / vol->params.logical_block_size;
1225 [ # # ]: 0 : vol->backing_lba_per_io_unit = vol->params.backing_io_unit_size / vol->backing_dev->blocklen;
1226 : :
1227 : 0 : rc = _allocate_bit_arrays(vol);
1228 [ # # ]: 0 : if (rc != 0) {
1229 : 0 : goto error;
1230 : : }
1231 : :
1232 : 0 : backing_dev_size = vol->backing_dev->blockcnt * vol->backing_dev->blocklen;
1233 [ # # ]: 0 : if (_get_vol_size(vol->params.chunk_size, backing_dev_size) < vol->params.vol_size) {
1234 : 0 : SPDK_ERRLOG("backing device size %" PRIi64 " smaller than expected\n",
1235 : : backing_dev_size);
1236 : 0 : rc = -EILSEQ;
1237 : 0 : goto error;
1238 : : }
1239 : :
1240 : 0 : vol->pm_file.size = _get_pm_file_size(&vol->params);
1241 : 0 : vol->pm_file.pm_buf = pmem_map_file(vol->pm_file.path, 0, 0, 0, &mapped_len,
1242 : : &vol->pm_file.pm_is_pmem);
1243 [ # # ]: 0 : if (vol->pm_file.pm_buf == NULL) {
1244 : 0 : SPDK_ERRLOG("could not pmem_map_file(%s): %s\n", vol->pm_file.path, strerror(errno));
1245 : 0 : rc = -errno;
1246 : 0 : goto error;
1247 : : }
1248 : :
1249 [ # # ]: 0 : if (vol->pm_file.size != mapped_len) {
1250 : 0 : SPDK_ERRLOG("could not map entire pmem file (size=%" PRIu64 " mapped=%" PRIu64 ")\n",
1251 : : vol->pm_file.size, mapped_len);
1252 : 0 : rc = -ENOMEM;
1253 : 0 : goto error;
1254 : : }
1255 : :
1256 : 0 : rc = _allocate_vol_requests(vol);
1257 [ # # ]: 0 : if (rc != 0) {
1258 : 0 : goto error;
1259 : : }
1260 : :
1261 : 0 : _initialize_vol_pm_pointers(vol);
1262 : :
1263 [ # # ]: 0 : num_chunks = vol->params.vol_size / vol->params.chunk_size;
1264 [ # # ]: 0 : for (i = 0; i < num_chunks; i++) {
1265 : 0 : logical_map_index = vol->pm_logical_map[i];
1266 [ # # ]: 0 : if (logical_map_index == REDUCE_EMPTY_MAP_ENTRY) {
1267 : 0 : continue;
1268 : : }
1269 : 0 : spdk_bit_array_set(vol->allocated_chunk_maps, logical_map_index);
1270 : 0 : chunk = _reduce_vol_get_chunk_map(vol, logical_map_index);
1271 [ # # ]: 0 : for (j = 0; j < vol->backing_io_units_per_chunk; j++) {
1272 [ # # ]: 0 : if (chunk->io_unit_index[j] != REDUCE_EMPTY_MAP_ENTRY) {
1273 : 0 : spdk_bit_array_set(vol->allocated_backing_io_units, chunk->io_unit_index[j]);
1274 : 0 : vol->info.allocated_io_units++;
1275 : : }
1276 : : }
1277 : : }
1278 : :
1279 : 0 : load_ctx->cb_fn(load_ctx->cb_arg, vol, 0);
1280 : : /* Only clean up the ctx - the vol has been passed to the application
1281 : : * for use now that volume load was successful.
1282 : : */
1283 : 0 : _init_load_cleanup(NULL, load_ctx);
1284 : 0 : return;
1285 : :
1286 : 424 : error:
1287 : 424 : load_ctx->cb_fn(load_ctx->cb_arg, NULL, rc);
1288 : 424 : _init_load_cleanup(vol, load_ctx);
1289 : : }
1290 : :
1291 : : void
1292 : 444 : spdk_reduce_vol_load(struct spdk_reduce_backing_dev *backing_dev,
1293 : : spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg)
1294 : : {
1295 : : struct spdk_reduce_vol *vol;
1296 : : struct reduce_init_load_ctx *load_ctx;
1297 : : struct spdk_reduce_backing_io *backing_io;
1298 : :
1299 [ - + ]: 444 : if (backing_dev->submit_backing_io == NULL) {
1300 : 0 : SPDK_ERRLOG("backing_dev function pointer not specified\n");
1301 : 0 : cb_fn(cb_arg, NULL, -EINVAL);
1302 : 0 : return;
1303 : : }
1304 : :
1305 : 444 : vol = calloc(1, sizeof(*vol));
1306 [ - + ]: 444 : if (vol == NULL) {
1307 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1308 : 0 : return;
1309 : : }
1310 : :
1311 : 444 : TAILQ_INIT(&vol->free_requests);
1312 : 444 : RB_INIT(&vol->executing_requests);
1313 : 444 : TAILQ_INIT(&vol->queued_requests);
1314 : 444 : queue_init(&vol->free_chunks_queue);
1315 : 444 : queue_init(&vol->free_backing_blocks_queue);
1316 : :
1317 : 444 : vol->backing_super = spdk_zmalloc(sizeof(*vol->backing_super), 64, NULL,
1318 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1319 [ - + ]: 444 : if (vol->backing_super == NULL) {
1320 : 0 : _init_load_cleanup(vol, NULL);
1321 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1322 : 0 : return;
1323 : : }
1324 : :
1325 : 444 : vol->backing_dev = backing_dev;
1326 : :
1327 : 444 : load_ctx = calloc(1, sizeof(*load_ctx));
1328 [ - + ]: 444 : if (load_ctx == NULL) {
1329 : 0 : _init_load_cleanup(vol, NULL);
1330 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1331 : 0 : return;
1332 : : }
1333 : :
1334 : 444 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1335 [ - + ]: 444 : if (backing_io == NULL) {
1336 : 0 : _init_load_cleanup(vol, load_ctx);
1337 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1338 : 0 : return;
1339 : : }
1340 : :
1341 : 444 : load_ctx->backing_io = backing_io;
1342 : :
1343 : 444 : load_ctx->path = spdk_zmalloc(REDUCE_PATH_MAX, 64, NULL,
1344 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1345 [ - + ]: 444 : if (load_ctx->path == NULL) {
1346 : 0 : _init_load_cleanup(vol, load_ctx);
1347 : 0 : cb_fn(cb_arg, NULL, -ENOMEM);
1348 : 0 : return;
1349 : : }
1350 : :
1351 : 444 : load_ctx->vol = vol;
1352 : 444 : load_ctx->cb_fn = cb_fn;
1353 : 444 : load_ctx->cb_arg = cb_arg;
1354 : :
1355 : 444 : load_ctx->iov[0].iov_base = vol->backing_super;
1356 : 444 : load_ctx->iov[0].iov_len = sizeof(*vol->backing_super);
1357 : 444 : load_ctx->iov[1].iov_base = load_ctx->path;
1358 : 444 : load_ctx->iov[1].iov_len = REDUCE_PATH_MAX;
1359 : 444 : backing_io->dev = vol->backing_dev;
1360 : 444 : backing_io->iov = load_ctx->iov;
1361 : 444 : backing_io->iovcnt = LOAD_IOV_COUNT;
1362 : 444 : backing_io->lba = 0;
1363 : 444 : backing_io->lba_count = (sizeof(*vol->backing_super) + REDUCE_PATH_MAX) /
1364 [ - + ]: 444 : vol->backing_dev->blocklen;
1365 : 444 : backing_io->backing_cb_args = &load_ctx->backing_cb_args;
1366 : 444 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
1367 : :
1368 : 444 : load_ctx->backing_cb_args.cb_fn = _load_read_super_and_path_cpl;
1369 : 444 : load_ctx->backing_cb_args.cb_arg = load_ctx;
1370 : 444 : vol->backing_dev->submit_backing_io(backing_io);
1371 : : }
1372 : :
1373 : : void
1374 : 40 : spdk_reduce_vol_unload(struct spdk_reduce_vol *vol,
1375 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1376 : : {
1377 [ - + ]: 40 : if (vol == NULL) {
1378 : : /* This indicates a programming error. */
1379 : 0 : assert(false);
1380 : : cb_fn(cb_arg, -EINVAL);
1381 : : return;
1382 : : }
1383 : :
1384 [ - + ]: 40 : if (--g_vol_count == 0) {
1385 : 0 : spdk_free(g_zero_buf);
1386 : : }
1387 [ - + ]: 40 : assert(g_vol_count >= 0);
1388 : 40 : _init_load_cleanup(vol, NULL);
1389 : 40 : cb_fn(cb_arg, 0);
1390 : : }
1391 : :
1392 : : struct reduce_destroy_ctx {
1393 : : spdk_reduce_vol_op_complete cb_fn;
1394 : : void *cb_arg;
1395 : : struct spdk_reduce_vol *vol;
1396 : : struct spdk_reduce_vol_superblock *super;
1397 : : struct iovec iov;
1398 : : struct spdk_reduce_vol_cb_args backing_cb_args;
1399 : : int reduce_errno;
1400 : : char pm_path[REDUCE_PATH_MAX];
1401 : : struct spdk_reduce_backing_io *backing_io;
1402 : : };
1403 : :
1404 : : static void
1405 : 20 : destroy_unload_cpl(void *cb_arg, int reduce_errno)
1406 : : {
1407 : 20 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1408 : :
1409 [ + - ]: 20 : if (destroy_ctx->reduce_errno == 0) {
1410 [ - + - + ]: 20 : if (unlink(destroy_ctx->pm_path)) {
1411 : 0 : SPDK_ERRLOG("%s could not be unlinked: %s\n",
1412 : : destroy_ctx->pm_path, strerror(errno));
1413 : : }
1414 : : }
1415 : :
1416 : : /* Even if the unload somehow failed, we still pass the destroy_ctx
1417 : : * reduce_errno since that indicates whether or not the volume was
1418 : : * actually destroyed.
1419 : : */
1420 : 20 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, destroy_ctx->reduce_errno);
1421 : 20 : spdk_free(destroy_ctx->super);
1422 : 20 : free(destroy_ctx->backing_io);
1423 : 20 : free(destroy_ctx);
1424 : 20 : }
1425 : :
1426 : : static void
1427 : 20 : _destroy_zero_super_cpl(void *cb_arg, int reduce_errno)
1428 : : {
1429 : 20 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1430 : 20 : struct spdk_reduce_vol *vol = destroy_ctx->vol;
1431 : :
1432 : 20 : destroy_ctx->reduce_errno = reduce_errno;
1433 : 20 : spdk_reduce_vol_unload(vol, destroy_unload_cpl, destroy_ctx);
1434 : 20 : }
1435 : :
1436 : : static void
1437 : 20 : destroy_load_cb(void *cb_arg, struct spdk_reduce_vol *vol, int reduce_errno)
1438 : : {
1439 : 20 : struct reduce_destroy_ctx *destroy_ctx = cb_arg;
1440 : 20 : struct spdk_reduce_backing_io *backing_io = destroy_ctx->backing_io;
1441 : :
1442 [ - + ]: 20 : if (reduce_errno != 0) {
1443 : 0 : destroy_ctx->cb_fn(destroy_ctx->cb_arg, reduce_errno);
1444 : 0 : spdk_free(destroy_ctx->super);
1445 : 0 : free(destroy_ctx);
1446 : 0 : return;
1447 : : }
1448 : :
1449 : 20 : destroy_ctx->vol = vol;
1450 [ - + - + ]: 20 : memcpy(destroy_ctx->pm_path, vol->pm_file.path, sizeof(destroy_ctx->pm_path));
1451 : 20 : destroy_ctx->iov.iov_base = destroy_ctx->super;
1452 : 20 : destroy_ctx->iov.iov_len = sizeof(*destroy_ctx->super);
1453 : 20 : destroy_ctx->backing_cb_args.cb_fn = _destroy_zero_super_cpl;
1454 : 20 : destroy_ctx->backing_cb_args.cb_arg = destroy_ctx;
1455 : :
1456 : 20 : backing_io->dev = vol->backing_dev;
1457 : 20 : backing_io->iov = &destroy_ctx->iov;
1458 : 20 : backing_io->iovcnt = 1;
1459 : 20 : backing_io->lba = 0;
1460 [ - + ]: 20 : backing_io->lba_count = sizeof(*destroy_ctx->super) / vol->backing_dev->blocklen;
1461 : 20 : backing_io->backing_cb_args = &destroy_ctx->backing_cb_args;
1462 : 20 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1463 : :
1464 : 20 : vol->backing_dev->submit_backing_io(backing_io);
1465 : : }
1466 : :
1467 : : void
1468 : 20 : spdk_reduce_vol_destroy(struct spdk_reduce_backing_dev *backing_dev,
1469 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
1470 : : {
1471 : : struct reduce_destroy_ctx *destroy_ctx;
1472 : : struct spdk_reduce_backing_io *backing_io;
1473 : :
1474 : 20 : destroy_ctx = calloc(1, sizeof(*destroy_ctx));
1475 [ - + ]: 20 : if (destroy_ctx == NULL) {
1476 : 0 : cb_fn(cb_arg, -ENOMEM);
1477 : 0 : return;
1478 : : }
1479 : :
1480 : 20 : backing_io = calloc(1, sizeof(*backing_io) + backing_dev->user_ctx_size);
1481 [ - + ]: 20 : if (backing_io == NULL) {
1482 : 0 : free(destroy_ctx);
1483 : 0 : cb_fn(cb_arg, -ENOMEM);
1484 : 0 : return;
1485 : : }
1486 : :
1487 : 20 : destroy_ctx->backing_io = backing_io;
1488 : :
1489 : 20 : destroy_ctx->super = spdk_zmalloc(sizeof(*destroy_ctx->super), 64, NULL,
1490 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
1491 [ - + ]: 20 : if (destroy_ctx->super == NULL) {
1492 : 0 : free(destroy_ctx);
1493 : 0 : free(backing_io);
1494 : 0 : cb_fn(cb_arg, -ENOMEM);
1495 : 0 : return;
1496 : : }
1497 : 20 : destroy_ctx->cb_fn = cb_fn;
1498 : 20 : destroy_ctx->cb_arg = cb_arg;
1499 : 20 : spdk_reduce_vol_load(backing_dev, destroy_load_cb, destroy_ctx);
1500 : : }
1501 : :
1502 : : static bool
1503 : 3400690 : _request_spans_chunk_boundary(struct spdk_reduce_vol *vol, uint64_t offset, uint64_t length)
1504 : : {
1505 : : uint64_t start_chunk, end_chunk;
1506 : :
1507 [ - + ]: 3400690 : start_chunk = offset / vol->logical_blocks_per_chunk;
1508 [ - + ]: 3400690 : end_chunk = (offset + length - 1) / vol->logical_blocks_per_chunk;
1509 : :
1510 : 3400690 : return (start_chunk != end_chunk);
1511 : : }
1512 : :
1513 : : typedef void (*reduce_request_fn)(void *_req, int reduce_errno);
1514 : : static void _start_unmap_request_full_chunk(void *ctx);
1515 : :
1516 : : static void
1517 : 629956 : _reduce_vol_complete_req(struct spdk_reduce_vol_request *req, int reduce_errno)
1518 : : {
1519 : : struct spdk_reduce_vol_request *next_req;
1520 : 629956 : struct spdk_reduce_vol *vol = req->vol;
1521 : :
1522 : 629956 : req->cb_fn(req->cb_arg, reduce_errno);
1523 : 629956 : RB_REMOVE(executing_req_tree, &vol->executing_requests, req);
1524 : :
1525 [ + + ]: 7042319 : TAILQ_FOREACH(next_req, &vol->queued_requests, tailq) {
1526 [ + + ]: 6795091 : if (next_req->logical_map_index == req->logical_map_index) {
1527 [ + + ]: 382728 : TAILQ_REMOVE(&vol->queued_requests, next_req, tailq);
1528 [ + + ]: 382728 : if (next_req->type == REDUCE_IO_READV) {
1529 : 130195 : _start_readv_request(next_req);
1530 [ + - ]: 252533 : } else if (next_req->type == REDUCE_IO_WRITEV) {
1531 : 252533 : _start_writev_request(next_req);
1532 : : } else {
1533 [ # # ]: 0 : assert(next_req->type == REDUCE_IO_UNMAP);
1534 : 0 : _start_unmap_request_full_chunk(next_req);
1535 : : }
1536 : 382728 : break;
1537 : : }
1538 : : }
1539 : :
1540 [ + - ]: 629956 : TAILQ_INSERT_HEAD(&vol->free_requests, req, tailq);
1541 : 629956 : }
1542 : :
1543 : : static void
1544 : 438033 : _reduce_vol_reset_chunk(struct spdk_reduce_vol *vol, uint64_t chunk_map_index)
1545 : : {
1546 : : struct spdk_reduce_chunk_map *chunk;
1547 : : uint64_t index;
1548 : : bool success;
1549 : : uint32_t i;
1550 : :
1551 : 438033 : chunk = _reduce_vol_get_chunk_map(vol, chunk_map_index);
1552 [ + - ]: 876066 : for (i = 0; i < vol->backing_io_units_per_chunk; i++) {
1553 : 876066 : index = chunk->io_unit_index[i];
1554 [ + + ]: 876066 : if (index == REDUCE_EMPTY_MAP_ENTRY) {
1555 : 438033 : break;
1556 : : }
1557 [ - + ]: 438033 : assert(spdk_bit_array_get(vol->allocated_backing_io_units,
1558 : : index) == true);
1559 : 438033 : spdk_bit_array_clear(vol->allocated_backing_io_units, index);
1560 : 438033 : vol->info.allocated_io_units--;
1561 : 438033 : success = queue_enqueue(&vol->free_backing_blocks_queue, index);
1562 [ + + + + ]: 438033 : if (!success && index < vol->find_block_offset) {
1563 : 25 : vol->find_block_offset = index;
1564 : : }
1565 : 438033 : chunk->io_unit_index[i] = REDUCE_EMPTY_MAP_ENTRY;
1566 : : }
1567 : 438033 : success = queue_enqueue(&vol->free_chunks_queue, chunk_map_index);
1568 [ + + + + ]: 438033 : if (!success && chunk_map_index < vol->find_chunk_offset) {
1569 : 25 : vol->find_chunk_offset = chunk_map_index;
1570 : : }
1571 : 438033 : spdk_bit_array_clear(vol->allocated_chunk_maps, chunk_map_index);
1572 : 438033 : }
1573 : :
1574 : : static void
1575 : 496642 : _write_write_done(void *_req, int reduce_errno)
1576 : : {
1577 : 496642 : struct spdk_reduce_vol_request *req = _req;
1578 : 496642 : struct spdk_reduce_vol *vol = req->vol;
1579 : : uint64_t old_chunk_map_index;
1580 : :
1581 [ - + ]: 496642 : if (reduce_errno != 0) {
1582 : 0 : req->reduce_errno = reduce_errno;
1583 : : }
1584 : :
1585 [ - + ]: 496642 : assert(req->num_backing_ops > 0);
1586 [ - + ]: 496642 : if (--req->num_backing_ops > 0) {
1587 : 0 : return;
1588 : : }
1589 : :
1590 [ - + ]: 496642 : if (req->reduce_errno != 0) {
1591 : 0 : _reduce_vol_reset_chunk(vol, req->chunk_map_index);
1592 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
1593 : 0 : return;
1594 : : }
1595 : :
1596 : 496642 : old_chunk_map_index = vol->pm_logical_map[req->logical_map_index];
1597 [ + + ]: 496642 : if (old_chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
1598 : 438033 : _reduce_vol_reset_chunk(vol, old_chunk_map_index);
1599 : : }
1600 : :
1601 : : /*
1602 : : * We don't need to persist the clearing of the old chunk map here. The old chunk map
1603 : : * becomes invalid after we update the logical map, since the old chunk map will no
1604 : : * longer have a reference to it in the logical map.
1605 : : */
1606 : :
1607 : : /* Persist the new chunk map. This must be persisted before we update the logical map. */
1608 : 496642 : _reduce_persist(vol, req->chunk,
1609 : 496642 : _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk));
1610 : :
1611 : 496642 : vol->pm_logical_map[req->logical_map_index] = req->chunk_map_index;
1612 : :
1613 : 496642 : _reduce_persist(vol, &vol->pm_logical_map[req->logical_map_index], sizeof(uint64_t));
1614 : :
1615 : 496642 : _reduce_vol_complete_req(req, 0);
1616 : : }
1617 : :
1618 : : static struct spdk_reduce_backing_io *
1619 : 1067433 : _reduce_vol_req_get_backing_io(struct spdk_reduce_vol_request *req, uint32_t index)
1620 : : {
1621 : 1067433 : struct spdk_reduce_backing_dev *backing_dev = req->vol->backing_dev;
1622 : : struct spdk_reduce_backing_io *backing_io;
1623 : :
1624 : 1067433 : backing_io = (struct spdk_reduce_backing_io *)((uint8_t *)req->backing_io +
1625 : 1067433 : (sizeof(*backing_io) + backing_dev->user_ctx_size) * index);
1626 : :
1627 : 1067433 : return backing_io;
1628 : :
1629 : : }
1630 : :
1631 : : struct reduce_merged_io_desc {
1632 : : uint64_t io_unit_index;
1633 : : uint32_t num_io_units;
1634 : : };
1635 : :
1636 : : static void
1637 : 0 : _issue_backing_ops_without_merge(struct spdk_reduce_vol_request *req, struct spdk_reduce_vol *vol,
1638 : : reduce_request_fn next_fn, bool is_write)
1639 : : {
1640 : : struct iovec *iov;
1641 : : struct spdk_reduce_backing_io *backing_io;
1642 : : uint8_t *buf;
1643 : : uint32_t i;
1644 : :
1645 [ # # # # ]: 0 : if (req->chunk_is_compressed) {
1646 : 0 : iov = req->comp_buf_iov;
1647 : 0 : buf = req->comp_buf;
1648 : : } else {
1649 : 0 : iov = req->decomp_buf_iov;
1650 : 0 : buf = req->decomp_buf;
1651 : : }
1652 : :
1653 : 0 : req->num_backing_ops = req->num_io_units;
1654 : 0 : req->backing_cb_args.cb_fn = next_fn;
1655 : 0 : req->backing_cb_args.cb_arg = req;
1656 [ # # ]: 0 : for (i = 0; i < req->num_io_units; i++) {
1657 : 0 : backing_io = _reduce_vol_req_get_backing_io(req, i);
1658 : 0 : iov[i].iov_base = buf + i * vol->params.backing_io_unit_size;
1659 : 0 : iov[i].iov_len = vol->params.backing_io_unit_size;
1660 : 0 : backing_io->dev = vol->backing_dev;
1661 : 0 : backing_io->iov = &iov[i];
1662 : 0 : backing_io->iovcnt = 1;
1663 : 0 : backing_io->lba = req->chunk->io_unit_index[i] * vol->backing_lba_per_io_unit;
1664 : 0 : backing_io->lba_count = vol->backing_lba_per_io_unit;
1665 : 0 : backing_io->backing_cb_args = &req->backing_cb_args;
1666 [ # # ]: 0 : if (is_write) {
1667 : 0 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1668 : : } else {
1669 : 0 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
1670 : : }
1671 : 0 : vol->backing_dev->submit_backing_io(backing_io);
1672 : : }
1673 : 0 : }
1674 : :
1675 : : static void
1676 : 1067433 : _issue_backing_ops(struct spdk_reduce_vol_request *req, struct spdk_reduce_vol *vol,
1677 : : reduce_request_fn next_fn, bool is_write)
1678 : : {
1679 : : struct iovec *iov;
1680 : : struct spdk_reduce_backing_io *backing_io;
1681 : : struct reduce_merged_io_desc merged_io_desc[4];
1682 : : uint8_t *buf;
1683 : 1067433 : bool merge = false;
1684 : 1067433 : uint32_t num_io = 0;
1685 : 1067433 : uint32_t io_unit_counts = 0;
1686 : 1067433 : uint32_t merged_io_idx = 0;
1687 : : uint32_t i;
1688 : :
1689 : : /* The merged_io_desc value is defined here to contain four elements,
1690 : : * and the chunk size must be four times the maximum of the io unit.
1691 : : * if chunk size is too big, don't merge IO.
1692 : : */
1693 [ - + ]: 1067433 : if (vol->backing_io_units_per_chunk > 4) {
1694 : 0 : _issue_backing_ops_without_merge(req, vol, next_fn, is_write);
1695 : 0 : return;
1696 : : }
1697 : :
1698 [ - + + - ]: 1067433 : if (req->chunk_is_compressed) {
1699 : 1067433 : iov = req->comp_buf_iov;
1700 : 1067433 : buf = req->comp_buf;
1701 : : } else {
1702 : 0 : iov = req->decomp_buf_iov;
1703 : 0 : buf = req->decomp_buf;
1704 : : }
1705 : :
1706 [ + - ]: 1067433 : for (i = 0; i < req->num_io_units; i++) {
1707 [ + - ]: 1067433 : if (!merge) {
1708 : 1067433 : merged_io_desc[merged_io_idx].io_unit_index = req->chunk->io_unit_index[i];
1709 : 1067433 : merged_io_desc[merged_io_idx].num_io_units = 1;
1710 : 1067433 : num_io++;
1711 : : }
1712 : :
1713 [ + - ]: 1067433 : if (i + 1 == req->num_io_units) {
1714 : 1067433 : break;
1715 : : }
1716 : :
1717 [ # # ]: 0 : if (req->chunk->io_unit_index[i] + 1 == req->chunk->io_unit_index[i + 1]) {
1718 : 0 : merged_io_desc[merged_io_idx].num_io_units += 1;
1719 : 0 : merge = true;
1720 : 0 : continue;
1721 : : }
1722 : 0 : merge = false;
1723 : 0 : merged_io_idx++;
1724 : : }
1725 : :
1726 : 1067433 : req->num_backing_ops = num_io;
1727 : 1067433 : req->backing_cb_args.cb_fn = next_fn;
1728 : 1067433 : req->backing_cb_args.cb_arg = req;
1729 [ + + ]: 2134866 : for (i = 0; i < num_io; i++) {
1730 : 1067433 : backing_io = _reduce_vol_req_get_backing_io(req, i);
1731 : 1067433 : iov[i].iov_base = buf + io_unit_counts * vol->params.backing_io_unit_size;
1732 : 1067433 : iov[i].iov_len = vol->params.backing_io_unit_size * merged_io_desc[i].num_io_units;
1733 : 1067433 : backing_io->dev = vol->backing_dev;
1734 : 1067433 : backing_io->iov = &iov[i];
1735 : 1067433 : backing_io->iovcnt = 1;
1736 : 1067433 : backing_io->lba = merged_io_desc[i].io_unit_index * vol->backing_lba_per_io_unit;
1737 : 1067433 : backing_io->lba_count = vol->backing_lba_per_io_unit * merged_io_desc[i].num_io_units;
1738 : 1067433 : backing_io->backing_cb_args = &req->backing_cb_args;
1739 [ + + ]: 1067433 : if (is_write) {
1740 : 496642 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_WRITE;
1741 : : } else {
1742 : 570791 : backing_io->backing_io_type = SPDK_REDUCE_BACKING_IO_READ;
1743 : : }
1744 : 1067433 : vol->backing_dev->submit_backing_io(backing_io);
1745 : :
1746 : : /* Collects the number of processed I/O. */
1747 : 1067433 : io_unit_counts += merged_io_desc[i].num_io_units;
1748 : : }
1749 : : }
1750 : :
1751 : : static void
1752 : 496642 : _reduce_vol_write_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn,
1753 : : uint32_t compressed_size)
1754 : : {
1755 : 496642 : struct spdk_reduce_vol *vol = req->vol;
1756 : : uint32_t i;
1757 : 496642 : uint64_t chunk_offset, remainder, free_index, total_len = 0;
1758 : : uint8_t *buf;
1759 : : bool success;
1760 : : int j;
1761 : :
1762 : 496642 : success = queue_dequeue(&vol->free_chunks_queue, &free_index);
1763 [ + + ]: 496642 : if (success) {
1764 : 437402 : req->chunk_map_index = free_index;
1765 : : } else {
1766 : 59240 : req->chunk_map_index = spdk_bit_array_find_first_clear(vol->allocated_chunk_maps,
1767 : 59240 : vol->find_chunk_offset);
1768 : 59240 : vol->find_chunk_offset = req->chunk_map_index + 1;
1769 : : }
1770 : :
1771 : : /* TODO: fail if no chunk map found - but really this should not happen if we
1772 : : * size the number of requests similarly to number of extra chunk maps
1773 : : */
1774 [ - + ]: 496642 : assert(req->chunk_map_index != REDUCE_EMPTY_MAP_ENTRY);
1775 : 496642 : spdk_bit_array_set(vol->allocated_chunk_maps, req->chunk_map_index);
1776 : :
1777 : 496642 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
1778 : 496642 : req->num_io_units = spdk_divide_round_up(compressed_size,
1779 : 496642 : vol->params.backing_io_unit_size);
1780 : 496642 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
1781 : 496642 : req->chunk->compressed_size =
1782 [ - + + - ]: 496642 : req->chunk_is_compressed ? compressed_size : vol->params.chunk_size;
1783 : :
1784 : : /* if the chunk is uncompressed we need to copy the data from the host buffers. */
1785 [ - + - + ]: 496642 : if (req->chunk_is_compressed == false) {
1786 [ # # ]: 0 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1787 : 0 : buf = req->decomp_buf;
1788 : 0 : total_len = chunk_offset * vol->params.logical_block_size;
1789 : :
1790 : : /* zero any offset into chunk */
1791 [ # # # # : 0 : if (req->rmw == false && chunk_offset) {
# # ]
1792 [ # # ]: 0 : memset(buf, 0, total_len);
1793 : : }
1794 : 0 : buf += total_len;
1795 : :
1796 : : /* copy the data */
1797 [ # # ]: 0 : for (j = 0; j < req->iovcnt; j++) {
1798 [ # # # # ]: 0 : memcpy(buf, req->iov[j].iov_base, req->iov[j].iov_len);
1799 : 0 : buf += req->iov[j].iov_len;
1800 : 0 : total_len += req->iov[j].iov_len;
1801 : : }
1802 : :
1803 : : /* zero any remainder */
1804 : 0 : remainder = vol->params.chunk_size - total_len;
1805 : 0 : total_len += remainder;
1806 [ # # # # : 0 : if (req->rmw == false && remainder) {
# # ]
1807 [ # # ]: 0 : memset(buf, 0, remainder);
1808 : : }
1809 [ # # ]: 0 : assert(total_len == vol->params.chunk_size);
1810 : : }
1811 : :
1812 [ + + ]: 993284 : for (i = 0; i < req->num_io_units; i++) {
1813 : 496642 : success = queue_dequeue(&vol->free_backing_blocks_queue, &free_index);
1814 [ + + ]: 496642 : if (success) {
1815 : 437402 : req->chunk->io_unit_index[i] = free_index;
1816 : : } else {
1817 : 59240 : req->chunk->io_unit_index[i] = spdk_bit_array_find_first_clear(vol->allocated_backing_io_units,
1818 : 59240 : vol->find_block_offset);
1819 : 59240 : vol->find_block_offset = req->chunk->io_unit_index[i] + 1;
1820 : : }
1821 : : /* TODO: fail if no backing block found - but really this should also not
1822 : : * happen (see comment above).
1823 : : */
1824 [ - + ]: 496642 : assert(req->chunk->io_unit_index[i] != REDUCE_EMPTY_MAP_ENTRY);
1825 : 496642 : spdk_bit_array_set(vol->allocated_backing_io_units, req->chunk->io_unit_index[i]);
1826 : 496642 : vol->info.allocated_io_units++;
1827 : : }
1828 : :
1829 : 496642 : _issue_backing_ops(req, vol, next_fn, true /* write */);
1830 : 496642 : }
1831 : :
1832 : : static void
1833 : 496642 : _write_compress_done(void *_req, int reduce_errno)
1834 : : {
1835 : 496642 : struct spdk_reduce_vol_request *req = _req;
1836 : :
1837 : : /* Negative reduce_errno indicates failure for compression operations.
1838 : : * Just write the uncompressed data instead. Force this to happen
1839 : : * by just passing the full chunk size to _reduce_vol_write_chunk.
1840 : : * When it sees the data couldn't be compressed, it will just write
1841 : : * the uncompressed buffer to disk.
1842 : : */
1843 [ - + ]: 496642 : if (reduce_errno < 0) {
1844 : 0 : req->backing_cb_args.output_size = req->vol->params.chunk_size;
1845 : : }
1846 : :
1847 : 496642 : _reduce_vol_write_chunk(req, _write_write_done, req->backing_cb_args.output_size);
1848 : 496642 : }
1849 : :
1850 : : static void
1851 : 496642 : _reduce_vol_compress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1852 : : {
1853 : 496642 : struct spdk_reduce_vol *vol = req->vol;
1854 : :
1855 : 496642 : req->backing_cb_args.cb_fn = next_fn;
1856 : 496642 : req->backing_cb_args.cb_arg = req;
1857 : 496642 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1858 : 496642 : req->comp_buf_iov[0].iov_len = vol->params.chunk_size;
1859 : 993284 : vol->backing_dev->compress(vol->backing_dev,
1860 : 496642 : req->decomp_iov, req->decomp_iovcnt, req->comp_buf_iov, 1,
1861 : : &req->backing_cb_args);
1862 : 496642 : }
1863 : :
1864 : : static void
1865 : 437477 : _reduce_vol_decompress_chunk_scratch(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1866 : : {
1867 : 437477 : struct spdk_reduce_vol *vol = req->vol;
1868 : :
1869 : 437477 : req->backing_cb_args.cb_fn = next_fn;
1870 : 437477 : req->backing_cb_args.cb_arg = req;
1871 : 437477 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1872 : 437477 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
1873 : 437477 : req->decomp_buf_iov[0].iov_base = req->decomp_buf;
1874 : 437477 : req->decomp_buf_iov[0].iov_len = vol->params.chunk_size;
1875 : 437477 : vol->backing_dev->decompress(vol->backing_dev,
1876 : : req->comp_buf_iov, 1, req->decomp_buf_iov, 1,
1877 : : &req->backing_cb_args);
1878 : 437477 : }
1879 : :
1880 : : static void
1881 : 133314 : _reduce_vol_decompress_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
1882 : : {
1883 : 133314 : struct spdk_reduce_vol *vol = req->vol;
1884 : 133314 : uint64_t chunk_offset, remainder = 0;
1885 : 133314 : uint64_t ttl_len = 0;
1886 : : size_t iov_len;
1887 : : int i;
1888 : :
1889 : 133314 : req->decomp_iovcnt = 0;
1890 [ - + ]: 133314 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1891 : :
1892 : : /* If backing device doesn't support SGL output then we should copy the result of decompression to user's buffer
1893 : : * if at least one of the conditions below is true:
1894 : : * 1. User's buffer is fragmented
1895 : : * 2. Length of the user's buffer is less than the chunk
1896 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
1897 : 133314 : iov_len = req->iov[0].iov_len;
1898 [ - + + - : 266624 : req->copy_after_decompress = !vol->backing_dev->sgl_out && (req->iovcnt > 1 ||
+ + ]
1899 [ + + - + ]: 134312 : req->iov[0].iov_len < vol->params.chunk_size ||
1900 : 1002 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len));
1901 [ - + + + ]: 133314 : if (req->copy_after_decompress) {
1902 : 132312 : req->decomp_iov[0].iov_base = req->decomp_buf;
1903 : 132312 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
1904 : 132312 : req->decomp_iovcnt = 1;
1905 : 132312 : goto decompress;
1906 : : }
1907 : :
1908 [ - + ]: 1002 : if (chunk_offset) {
1909 : : /* first iov point to our scratch buffer for any offset into the chunk */
1910 : 0 : req->decomp_iov[0].iov_base = req->decomp_buf;
1911 : 0 : req->decomp_iov[0].iov_len = chunk_offset * vol->params.logical_block_size;
1912 : 0 : ttl_len += req->decomp_iov[0].iov_len;
1913 : 0 : req->decomp_iovcnt = 1;
1914 : : }
1915 : :
1916 : : /* now the user data iov, direct to the user buffer */
1917 [ + + ]: 2004 : for (i = 0; i < req->iovcnt; i++) {
1918 : 1002 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
1919 : 1002 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
1920 : 1002 : ttl_len += req->decomp_iov[i + req->decomp_iovcnt].iov_len;
1921 : : }
1922 : 1002 : req->decomp_iovcnt += req->iovcnt;
1923 : :
1924 : : /* send the rest of the chunk to our scratch buffer */
1925 : 1002 : remainder = vol->params.chunk_size - ttl_len;
1926 [ - + ]: 1002 : if (remainder) {
1927 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = req->decomp_buf + ttl_len;
1928 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
1929 : 0 : ttl_len += req->decomp_iov[req->decomp_iovcnt].iov_len;
1930 : 0 : req->decomp_iovcnt++;
1931 : : }
1932 [ - + ]: 1002 : assert(ttl_len == vol->params.chunk_size);
1933 : :
1934 : 133314 : decompress:
1935 [ - + + + : 133314 : assert(!req->copy_after_decompress || (req->copy_after_decompress && req->decomp_iovcnt == 1));
- + + - +
- ]
1936 : 133314 : req->backing_cb_args.cb_fn = next_fn;
1937 : 133314 : req->backing_cb_args.cb_arg = req;
1938 : 133314 : req->comp_buf_iov[0].iov_base = req->comp_buf;
1939 : 133314 : req->comp_buf_iov[0].iov_len = req->chunk->compressed_size;
1940 : 266628 : vol->backing_dev->decompress(vol->backing_dev,
1941 : 133314 : req->comp_buf_iov, 1, req->decomp_iov, req->decomp_iovcnt,
1942 : : &req->backing_cb_args);
1943 : 133314 : }
1944 : :
1945 : : static inline void
1946 : 495640 : _prepare_compress_chunk_copy_user_buffers(struct spdk_reduce_vol_request *req, bool zero_paddings)
1947 : : {
1948 : 495640 : struct spdk_reduce_vol *vol = req->vol;
1949 : 495640 : uint64_t chunk_offset, ttl_len = 0;
1950 : 495640 : uint64_t remainder = 0;
1951 : 495640 : char *copy_offset = NULL;
1952 : 495640 : uint32_t lbsize = vol->params.logical_block_size;
1953 : : int i;
1954 : :
1955 : 495640 : req->decomp_iov[0].iov_base = req->decomp_buf;
1956 : 495640 : req->decomp_iov[0].iov_len = vol->params.chunk_size;
1957 : 495640 : req->decomp_iovcnt = 1;
1958 : 495640 : copy_offset = req->decomp_iov[0].iov_base;
1959 [ - + ]: 495640 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
1960 : :
1961 [ + + ]: 495640 : if (chunk_offset) {
1962 : 280874 : ttl_len += chunk_offset * lbsize;
1963 : : /* copy_offset already points to the correct buffer if zero_paddings=false */
1964 [ + + ]: 280874 : if (zero_paddings) {
1965 [ - + ]: 7360 : memset(copy_offset, 0, ttl_len);
1966 : : }
1967 : 280874 : copy_offset += ttl_len;
1968 : : }
1969 : :
1970 : : /* now the user data iov, direct from the user buffer */
1971 [ + + ]: 991340 : for (i = 0; i < req->iovcnt; i++) {
1972 [ - + - + ]: 495700 : memcpy(copy_offset, req->iov[i].iov_base, req->iov[i].iov_len);
1973 : 495700 : copy_offset += req->iov[i].iov_len;
1974 : 495700 : ttl_len += req->iov[i].iov_len;
1975 : : }
1976 : :
1977 : 495640 : remainder = vol->params.chunk_size - ttl_len;
1978 [ + + ]: 495640 : if (remainder) {
1979 : : /* copy_offset already points to the correct buffer if zero_paddings=false */
1980 [ + + ]: 280909 : if (zero_paddings) {
1981 [ - + ]: 50803 : memset(copy_offset, 0, remainder);
1982 : : }
1983 : 280909 : ttl_len += remainder;
1984 : : }
1985 : :
1986 [ - + ]: 495640 : assert(ttl_len == req->vol->params.chunk_size);
1987 : 495640 : }
1988 : :
1989 : : /* This function can be called when we are compressing a new data or in case of read-modify-write
1990 : : * In the first case possible paddings should be filled with zeroes, in the second case the paddings
1991 : : * should point to already read and decompressed buffer */
1992 : : static inline void
1993 : 496642 : _prepare_compress_chunk(struct spdk_reduce_vol_request *req, bool zero_paddings)
1994 : : {
1995 : 496642 : struct spdk_reduce_vol *vol = req->vol;
1996 [ + + ]: 496642 : char *padding_buffer = zero_paddings ? g_zero_buf : req->decomp_buf;
1997 : 496642 : uint64_t chunk_offset, ttl_len = 0;
1998 : 496642 : uint64_t remainder = 0;
1999 : 496642 : uint32_t lbsize = vol->params.logical_block_size;
2000 : : size_t iov_len;
2001 : : int i;
2002 : :
2003 : : /* If backing device doesn't support SGL input then we should copy user's buffer into decomp_buf
2004 : : * if at least one of the conditions below is true:
2005 : : * 1. User's buffer is fragmented
2006 : : * 2. Length of the user's buffer is less than the chunk
2007 : : * 3. User's buffer is contig, equals chunk_size but crosses huge page boundary */
2008 : 496642 : iov_len = req->iov[0].iov_len;
2009 [ - + + - : 496642 : if (!vol->backing_dev->sgl_in && (req->iovcnt > 1 ||
+ + ]
2010 [ + + - + ]: 497640 : req->iov[0].iov_len < vol->params.chunk_size ||
2011 : 1002 : _addr_crosses_huge_page(req->iov[0].iov_base, &iov_len))) {
2012 : 495640 : _prepare_compress_chunk_copy_user_buffers(req, zero_paddings);
2013 : 495640 : return;
2014 : : }
2015 : :
2016 : 1002 : req->decomp_iovcnt = 0;
2017 [ - + ]: 1002 : chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2018 : :
2019 [ - + ]: 1002 : if (chunk_offset != 0) {
2020 : 0 : ttl_len += chunk_offset * lbsize;
2021 : 0 : req->decomp_iov[0].iov_base = padding_buffer;
2022 : 0 : req->decomp_iov[0].iov_len = ttl_len;
2023 : 0 : req->decomp_iovcnt = 1;
2024 : : }
2025 : :
2026 : : /* now the user data iov, direct from the user buffer */
2027 [ + + ]: 2004 : for (i = 0; i < req->iovcnt; i++) {
2028 : 1002 : req->decomp_iov[i + req->decomp_iovcnt].iov_base = req->iov[i].iov_base;
2029 : 1002 : req->decomp_iov[i + req->decomp_iovcnt].iov_len = req->iov[i].iov_len;
2030 : 1002 : ttl_len += req->iov[i].iov_len;
2031 : : }
2032 : 1002 : req->decomp_iovcnt += req->iovcnt;
2033 : :
2034 : 1002 : remainder = vol->params.chunk_size - ttl_len;
2035 [ - + ]: 1002 : if (remainder) {
2036 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_base = padding_buffer + ttl_len;
2037 : 0 : req->decomp_iov[req->decomp_iovcnt].iov_len = remainder;
2038 : 0 : req->decomp_iovcnt++;
2039 : 0 : ttl_len += remainder;
2040 : : }
2041 [ - + ]: 1002 : assert(ttl_len == req->vol->params.chunk_size);
2042 : : }
2043 : :
2044 : : static void
2045 : 437477 : _write_decompress_done(void *_req, int reduce_errno)
2046 : : {
2047 : 437477 : struct spdk_reduce_vol_request *req = _req;
2048 : :
2049 : : /* Negative reduce_errno indicates failure for compression operations. */
2050 [ - + ]: 437477 : if (reduce_errno < 0) {
2051 : 0 : _reduce_vol_complete_req(req, reduce_errno);
2052 : 0 : return;
2053 : : }
2054 : :
2055 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
2056 : : * represents the output_size.
2057 : : */
2058 [ - + ]: 437477 : if (req->backing_cb_args.output_size != req->vol->params.chunk_size) {
2059 : 0 : _reduce_vol_complete_req(req, -EIO);
2060 : 0 : return;
2061 : : }
2062 : :
2063 : 437477 : _prepare_compress_chunk(req, false);
2064 : 437477 : _reduce_vol_compress_chunk(req, _write_compress_done);
2065 : : }
2066 : :
2067 : : static void
2068 : 437477 : _write_read_done(void *_req, int reduce_errno)
2069 : : {
2070 : 437477 : struct spdk_reduce_vol_request *req = _req;
2071 : :
2072 [ - + ]: 437477 : if (reduce_errno != 0) {
2073 : 0 : req->reduce_errno = reduce_errno;
2074 : : }
2075 : :
2076 [ - + ]: 437477 : assert(req->num_backing_ops > 0);
2077 [ - + ]: 437477 : if (--req->num_backing_ops > 0) {
2078 : 0 : return;
2079 : : }
2080 : :
2081 [ - + ]: 437477 : if (req->reduce_errno != 0) {
2082 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2083 : 0 : return;
2084 : : }
2085 : :
2086 [ - + + - ]: 437477 : if (req->chunk_is_compressed) {
2087 : 437477 : _reduce_vol_decompress_chunk_scratch(req, _write_decompress_done);
2088 : : } else {
2089 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
2090 : :
2091 : 0 : _write_decompress_done(req, 0);
2092 : : }
2093 : : }
2094 : :
2095 : : static void
2096 : 133314 : _read_decompress_done(void *_req, int reduce_errno)
2097 : : {
2098 : 133314 : struct spdk_reduce_vol_request *req = _req;
2099 : 133314 : struct spdk_reduce_vol *vol = req->vol;
2100 : :
2101 : : /* Negative reduce_errno indicates failure for compression operations. */
2102 [ - + ]: 133314 : if (reduce_errno < 0) {
2103 : 0 : _reduce_vol_complete_req(req, reduce_errno);
2104 : 0 : return;
2105 : : }
2106 : :
2107 : : /* Positive reduce_errno indicates that the output size field in the backing_cb_args
2108 : : * represents the output_size.
2109 : : */
2110 [ - + ]: 133314 : if (req->backing_cb_args.output_size != vol->params.chunk_size) {
2111 : 0 : _reduce_vol_complete_req(req, -EIO);
2112 : 0 : return;
2113 : : }
2114 : :
2115 [ - + + + ]: 133314 : if (req->copy_after_decompress) {
2116 [ - + ]: 132312 : uint64_t chunk_offset = req->offset % vol->logical_blocks_per_chunk;
2117 : 132312 : char *decomp_buffer = (char *)req->decomp_buf + chunk_offset * vol->params.logical_block_size;
2118 : : int i;
2119 : :
2120 [ + + ]: 264684 : for (i = 0; i < req->iovcnt; i++) {
2121 [ - + - + ]: 132372 : memcpy(req->iov[i].iov_base, decomp_buffer, req->iov[i].iov_len);
2122 : 132372 : decomp_buffer += req->iov[i].iov_len;
2123 [ - + ]: 132372 : assert(decomp_buffer <= (char *)req->decomp_buf + vol->params.chunk_size);
2124 : : }
2125 : : }
2126 : :
2127 : 133314 : _reduce_vol_complete_req(req, 0);
2128 : : }
2129 : :
2130 : : static void
2131 : 133314 : _read_read_done(void *_req, int reduce_errno)
2132 : : {
2133 : 133314 : struct spdk_reduce_vol_request *req = _req;
2134 : :
2135 [ - + ]: 133314 : if (reduce_errno != 0) {
2136 : 0 : req->reduce_errno = reduce_errno;
2137 : : }
2138 : :
2139 [ - + ]: 133314 : assert(req->num_backing_ops > 0);
2140 [ - + ]: 133314 : if (--req->num_backing_ops > 0) {
2141 : 0 : return;
2142 : : }
2143 : :
2144 [ - + ]: 133314 : if (req->reduce_errno != 0) {
2145 : 0 : _reduce_vol_complete_req(req, req->reduce_errno);
2146 : 0 : return;
2147 : : }
2148 : :
2149 [ - + + - ]: 133314 : if (req->chunk_is_compressed) {
2150 : 133314 : _reduce_vol_decompress_chunk(req, _read_decompress_done);
2151 : : } else {
2152 : :
2153 : : /* If the chunk was compressed, the data would have been sent to the
2154 : : * host buffers by the decompression operation, if not we need to memcpy
2155 : : * from req->decomp_buf.
2156 : : */
2157 : 0 : req->copy_after_decompress = true;
2158 : 0 : req->backing_cb_args.output_size = req->chunk->compressed_size;
2159 : :
2160 : 0 : _read_decompress_done(req, 0);
2161 : : }
2162 : : }
2163 : :
2164 : : static void
2165 : 570791 : _reduce_vol_read_chunk(struct spdk_reduce_vol_request *req, reduce_request_fn next_fn)
2166 : : {
2167 : 570791 : struct spdk_reduce_vol *vol = req->vol;
2168 : :
2169 : 570791 : req->chunk_map_index = vol->pm_logical_map[req->logical_map_index];
2170 [ - + ]: 570791 : assert(req->chunk_map_index != REDUCE_EMPTY_MAP_ENTRY);
2171 : :
2172 : 570791 : req->chunk = _reduce_vol_get_chunk_map(vol, req->chunk_map_index);
2173 : 570791 : req->num_io_units = spdk_divide_round_up(req->chunk->compressed_size,
2174 : 570791 : vol->params.backing_io_unit_size);
2175 : 570791 : req->chunk_is_compressed = (req->num_io_units != vol->backing_io_units_per_chunk);
2176 : :
2177 : 570791 : _issue_backing_ops(req, vol, next_fn, false /* read */);
2178 : 570791 : }
2179 : :
2180 : : static bool
2181 : 630036 : _iov_array_is_valid(struct spdk_reduce_vol *vol, struct iovec *iov, int iovcnt,
2182 : : uint64_t length)
2183 : : {
2184 : 630036 : uint64_t size = 0;
2185 : : int i;
2186 : :
2187 [ - + ]: 630036 : if (iovcnt > REDUCE_MAX_IOVECS) {
2188 : 0 : return false;
2189 : : }
2190 : :
2191 [ + + ]: 1260192 : for (i = 0; i < iovcnt; i++) {
2192 : 630156 : size += iov[i].iov_len;
2193 : : }
2194 : :
2195 : 630036 : return size == (length * vol->params.logical_block_size);
2196 : : }
2197 : :
2198 : : static bool
2199 : 3400690 : _check_overlap(struct spdk_reduce_vol *vol, uint64_t logical_map_index)
2200 : : {
2201 : : struct spdk_reduce_vol_request req;
2202 : :
2203 : 3400690 : req.logical_map_index = logical_map_index;
2204 : :
2205 : 3400690 : return (NULL != RB_FIND(executing_req_tree, &vol->executing_requests, &req));
2206 : : }
2207 : :
2208 : : static void
2209 : 133314 : _start_readv_request(struct spdk_reduce_vol_request *req)
2210 : : {
2211 : 133314 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
2212 : 133314 : _reduce_vol_read_chunk(req, _read_read_done);
2213 : 133314 : }
2214 : :
2215 : : void
2216 : 133394 : spdk_reduce_vol_readv(struct spdk_reduce_vol *vol,
2217 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
2218 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2219 : : {
2220 : : struct spdk_reduce_vol_request *req;
2221 : : uint64_t logical_map_index;
2222 : : bool overlapped;
2223 : : int i;
2224 : :
2225 [ - + ]: 133394 : if (length == 0) {
2226 : 0 : cb_fn(cb_arg, 0);
2227 : 0 : return;
2228 : : }
2229 : :
2230 [ - + ]: 133394 : if (_request_spans_chunk_boundary(vol, offset, length)) {
2231 : 0 : cb_fn(cb_arg, -EINVAL);
2232 : 0 : return;
2233 : : }
2234 : :
2235 [ - + ]: 133394 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
2236 : 0 : cb_fn(cb_arg, -EINVAL);
2237 : 0 : return;
2238 : : }
2239 : :
2240 [ - + ]: 133394 : logical_map_index = offset / vol->logical_blocks_per_chunk;
2241 : 133394 : overlapped = _check_overlap(vol, logical_map_index);
2242 : :
2243 [ + + + + ]: 133394 : if (!overlapped && vol->pm_logical_map[logical_map_index] == REDUCE_EMPTY_MAP_ENTRY) {
2244 : : /*
2245 : : * This chunk hasn't been allocated. So treat the data as all
2246 : : * zeroes for this chunk - do the memset and immediately complete
2247 : : * the operation.
2248 : : */
2249 [ + + ]: 160 : for (i = 0; i < iovcnt; i++) {
2250 [ - + ]: 80 : memset(iov[i].iov_base, 0, iov[i].iov_len);
2251 : : }
2252 : 80 : cb_fn(cb_arg, 0);
2253 : 80 : return;
2254 : : }
2255 : :
2256 : 133314 : req = TAILQ_FIRST(&vol->free_requests);
2257 [ - + ]: 133314 : if (req == NULL) {
2258 : 0 : cb_fn(cb_arg, -ENOMEM);
2259 : 0 : return;
2260 : : }
2261 : :
2262 [ + - ]: 133314 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
2263 : 133314 : req->type = REDUCE_IO_READV;
2264 : 133314 : req->vol = vol;
2265 : 133314 : req->iov = iov;
2266 : 133314 : req->iovcnt = iovcnt;
2267 : 133314 : req->offset = offset;
2268 : 133314 : req->logical_map_index = logical_map_index;
2269 : 133314 : req->length = length;
2270 : 133314 : req->copy_after_decompress = false;
2271 : 133314 : req->cb_fn = cb_fn;
2272 : 133314 : req->cb_arg = cb_arg;
2273 : 133314 : req->reduce_errno = 0;
2274 : :
2275 [ + + ]: 133314 : if (!overlapped) {
2276 : 3119 : _start_readv_request(req);
2277 : : } else {
2278 : 130195 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
2279 : : }
2280 : : }
2281 : :
2282 : : static void
2283 : 496642 : _start_writev_request(struct spdk_reduce_vol_request *req)
2284 : : {
2285 : 496642 : struct spdk_reduce_vol *vol = req->vol;
2286 : :
2287 : 496642 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
2288 [ + + ]: 496642 : if (vol->pm_logical_map[req->logical_map_index] != REDUCE_EMPTY_MAP_ENTRY) {
2289 [ + + ]: 438033 : if ((req->length * vol->params.logical_block_size) < vol->params.chunk_size) {
2290 : : /* Read old chunk, then overwrite with data from this write
2291 : : * operation.
2292 : : */
2293 : 437477 : req->rmw = true;
2294 : 437477 : _reduce_vol_read_chunk(req, _write_read_done);
2295 : 437477 : return;
2296 : : }
2297 : : }
2298 : :
2299 : 59165 : req->rmw = false;
2300 : :
2301 : 59165 : _prepare_compress_chunk(req, true);
2302 : 59165 : _reduce_vol_compress_chunk(req, _write_compress_done);
2303 : : }
2304 : :
2305 : : void
2306 : 496642 : spdk_reduce_vol_writev(struct spdk_reduce_vol *vol,
2307 : : struct iovec *iov, int iovcnt, uint64_t offset, uint64_t length,
2308 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2309 : : {
2310 : : struct spdk_reduce_vol_request *req;
2311 : : uint64_t logical_map_index;
2312 : : bool overlapped;
2313 : :
2314 [ - + ]: 496642 : if (length == 0) {
2315 : 0 : cb_fn(cb_arg, 0);
2316 : 0 : return;
2317 : : }
2318 : :
2319 [ - + ]: 496642 : if (_request_spans_chunk_boundary(vol, offset, length)) {
2320 : 0 : cb_fn(cb_arg, -EINVAL);
2321 : 0 : return;
2322 : : }
2323 : :
2324 [ - + ]: 496642 : if (!_iov_array_is_valid(vol, iov, iovcnt, length)) {
2325 : 0 : cb_fn(cb_arg, -EINVAL);
2326 : 0 : return;
2327 : : }
2328 : :
2329 [ - + ]: 496642 : logical_map_index = offset / vol->logical_blocks_per_chunk;
2330 : 496642 : overlapped = _check_overlap(vol, logical_map_index);
2331 : :
2332 : 496642 : req = TAILQ_FIRST(&vol->free_requests);
2333 [ - + ]: 496642 : if (req == NULL) {
2334 : 0 : cb_fn(cb_arg, -ENOMEM);
2335 : 0 : return;
2336 : : }
2337 : :
2338 [ + - ]: 496642 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
2339 : 496642 : req->type = REDUCE_IO_WRITEV;
2340 : 496642 : req->vol = vol;
2341 : 496642 : req->iov = iov;
2342 : 496642 : req->iovcnt = iovcnt;
2343 : 496642 : req->offset = offset;
2344 : 496642 : req->logical_map_index = logical_map_index;
2345 : 496642 : req->length = length;
2346 : 496642 : req->copy_after_decompress = false;
2347 : 496642 : req->cb_fn = cb_fn;
2348 : 496642 : req->cb_arg = cb_arg;
2349 : 496642 : req->reduce_errno = 0;
2350 : :
2351 [ + + ]: 496642 : if (!overlapped) {
2352 : 244109 : _start_writev_request(req);
2353 : : } else {
2354 : 252533 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
2355 : : }
2356 : : }
2357 : :
2358 : : static void
2359 : 0 : _start_unmap_request_full_chunk(void *ctx)
2360 : : {
2361 : 0 : struct spdk_reduce_vol_request *req = ctx;
2362 : 0 : struct spdk_reduce_vol *vol = req->vol;
2363 : : uint64_t chunk_map_index;
2364 : :
2365 : 0 : RB_INSERT(executing_req_tree, &req->vol->executing_requests, req);
2366 : :
2367 : 0 : chunk_map_index = vol->pm_logical_map[req->logical_map_index];
2368 [ # # ]: 0 : if (chunk_map_index != REDUCE_EMPTY_MAP_ENTRY) {
2369 : 0 : _reduce_vol_reset_chunk(vol, chunk_map_index);
2370 : 0 : vol->pm_logical_map[req->logical_map_index] = REDUCE_EMPTY_MAP_ENTRY;
2371 : 0 : _reduce_persist(vol, &vol->pm_logical_map[req->logical_map_index], sizeof(uint64_t));
2372 : : }
2373 : 0 : _reduce_vol_complete_req(req, 0);
2374 : 0 : }
2375 : :
2376 : : static void
2377 : 2770654 : _reduce_vol_unmap_full_chunk(struct spdk_reduce_vol *vol,
2378 : : uint64_t offset, uint64_t length,
2379 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2380 : : {
2381 : : struct spdk_reduce_vol_request *req;
2382 : : uint64_t logical_map_index;
2383 : : bool overlapped;
2384 : :
2385 [ - + ]: 2770654 : if (_request_spans_chunk_boundary(vol, offset, length)) {
2386 : 0 : cb_fn(cb_arg, -EINVAL);
2387 : 0 : return;
2388 : : }
2389 : :
2390 [ - + ]: 2770654 : logical_map_index = offset / vol->logical_blocks_per_chunk;
2391 : 2770654 : overlapped = _check_overlap(vol, logical_map_index);
2392 : :
2393 [ + - + - ]: 2770654 : if (!overlapped && vol->pm_logical_map[logical_map_index] == REDUCE_EMPTY_MAP_ENTRY) {
2394 : : /*
2395 : : * This chunk hasn't been allocated. Nothing needs to be done.
2396 : : */
2397 : 2770654 : cb_fn(cb_arg, 0);
2398 : 2770654 : return;
2399 : : }
2400 : :
2401 : 0 : req = TAILQ_FIRST(&vol->free_requests);
2402 [ # # ]: 0 : if (req == NULL) {
2403 : 0 : cb_fn(cb_arg, -ENOMEM);
2404 : 0 : return;
2405 : : }
2406 : :
2407 [ # # ]: 0 : TAILQ_REMOVE(&vol->free_requests, req, tailq);
2408 : 0 : req->type = REDUCE_IO_UNMAP;
2409 : 0 : req->vol = vol;
2410 : 0 : req->iov = NULL;
2411 : 0 : req->iovcnt = 0;
2412 : 0 : req->offset = offset;
2413 : 0 : req->logical_map_index = logical_map_index;
2414 : 0 : req->length = length;
2415 : 0 : req->copy_after_decompress = false;
2416 : 0 : req->cb_fn = cb_fn;
2417 : 0 : req->cb_arg = cb_arg;
2418 : 0 : req->reduce_errno = 0;
2419 : :
2420 [ # # ]: 0 : if (!overlapped) {
2421 : 0 : _start_unmap_request_full_chunk(req);
2422 : : } else {
2423 : 0 : TAILQ_INSERT_TAIL(&vol->queued_requests, req, tailq);
2424 : : }
2425 : : }
2426 : :
2427 : : struct unmap_partial_chunk_ctx {
2428 : : struct spdk_reduce_vol *vol;
2429 : : struct iovec iov;
2430 : : spdk_reduce_vol_op_complete cb_fn;
2431 : : void *cb_arg;
2432 : : };
2433 : :
2434 : : static void
2435 : 363330 : _reduce_unmap_partial_chunk_complete(void *_ctx, int reduce_errno)
2436 : : {
2437 : 363330 : struct unmap_partial_chunk_ctx *ctx = _ctx;
2438 : :
2439 : 363330 : ctx->cb_fn(ctx->cb_arg, reduce_errno);
2440 : 363330 : free(ctx);
2441 : 363330 : }
2442 : :
2443 : : static void
2444 : 363330 : _reduce_vol_unmap_partial_chunk(struct spdk_reduce_vol *vol, uint64_t offset, uint64_t length,
2445 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2446 : : {
2447 : : struct unmap_partial_chunk_ctx *ctx;
2448 : :
2449 : 363330 : ctx = calloc(1, sizeof(struct unmap_partial_chunk_ctx));
2450 [ - + ]: 363330 : if (ctx == NULL) {
2451 : 0 : cb_fn(cb_arg, -ENOMEM);
2452 : 0 : return;
2453 : : }
2454 : :
2455 : 363330 : ctx->vol = vol;
2456 : 363330 : ctx->iov.iov_base = g_zero_buf;
2457 : 363330 : ctx->iov.iov_len = length * vol->params.logical_block_size;
2458 : 363330 : ctx->cb_fn = cb_fn;
2459 : 363330 : ctx->cb_arg = cb_arg;
2460 : :
2461 : 363330 : spdk_reduce_vol_writev(vol, &ctx->iov, 1, offset, length, _reduce_unmap_partial_chunk_complete,
2462 : : ctx);
2463 : : }
2464 : :
2465 : : void
2466 : 3133984 : spdk_reduce_vol_unmap(struct spdk_reduce_vol *vol,
2467 : : uint64_t offset, uint64_t length,
2468 : : spdk_reduce_vol_op_complete cb_fn, void *cb_arg)
2469 : : {
2470 [ + + ]: 3133984 : if (length < vol->logical_blocks_per_chunk) {
2471 : 363330 : _reduce_vol_unmap_partial_chunk(vol, offset, length, cb_fn, cb_arg);
2472 [ + - ]: 2770654 : } else if (length == vol->logical_blocks_per_chunk) {
2473 : 2770654 : _reduce_vol_unmap_full_chunk(vol, offset, length, cb_fn, cb_arg);
2474 : : } else {
2475 : 0 : cb_fn(cb_arg, -EINVAL);
2476 : : }
2477 : 3133984 : }
2478 : :
2479 : : const struct spdk_reduce_vol_params *
2480 : 1989110 : spdk_reduce_vol_get_params(struct spdk_reduce_vol *vol)
2481 : : {
2482 : 1989110 : return &vol->params;
2483 : : }
2484 : :
2485 : : const char *
2486 : 20 : spdk_reduce_vol_get_pm_path(const struct spdk_reduce_vol *vol)
2487 : : {
2488 : 20 : return vol->pm_file.path;
2489 : : }
2490 : :
2491 : : void
2492 : 0 : spdk_reduce_vol_print_info(struct spdk_reduce_vol *vol)
2493 : : {
2494 : : uint64_t logical_map_size, num_chunks, ttl_chunk_sz;
2495 : : uint32_t struct_size;
2496 : : uint64_t chunk_map_size;
2497 : :
2498 : 0 : SPDK_NOTICELOG("vol info:\n");
2499 : 0 : SPDK_NOTICELOG("\tvol->params.backing_io_unit_size = 0x%x\n", vol->params.backing_io_unit_size);
2500 : 0 : SPDK_NOTICELOG("\tvol->params.logical_block_size = 0x%x\n", vol->params.logical_block_size);
2501 : 0 : SPDK_NOTICELOG("\tvol->params.chunk_size = 0x%x\n", vol->params.chunk_size);
2502 : 0 : SPDK_NOTICELOG("\tvol->params.vol_size = 0x%" PRIx64 "\n", vol->params.vol_size);
2503 : 0 : num_chunks = _get_total_chunks(vol->params.vol_size, vol->params.chunk_size);
2504 : 0 : SPDK_NOTICELOG("\ttotal chunks (including extra) = 0x%" PRIx64 "\n", num_chunks);
2505 [ # # ]: 0 : SPDK_NOTICELOG("\ttotal chunks (excluding extra) = 0x%" PRIx64 "\n",
2506 : : vol->params.vol_size / vol->params.chunk_size);
2507 : 0 : ttl_chunk_sz = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
2508 : 0 : vol->params.backing_io_unit_size);
2509 : 0 : SPDK_NOTICELOG("\ttotal_chunks_size = 0x%" PRIx64 "\n", ttl_chunk_sz);
2510 : 0 : struct_size = _reduce_vol_get_chunk_struct_size(vol->backing_io_units_per_chunk);
2511 : 0 : SPDK_NOTICELOG("\tchunk_struct_size = 0x%x\n", struct_size);
2512 : :
2513 : 0 : SPDK_NOTICELOG("pmem info:\n");
2514 : 0 : SPDK_NOTICELOG("\tvol->pm_file.size = 0x%" PRIx64 "\n", vol->pm_file.size);
2515 : 0 : SPDK_NOTICELOG("\tvol->pm_file.pm_buf = %p\n", (void *)vol->pm_file.pm_buf);
2516 : 0 : SPDK_NOTICELOG("\tvol->pm_super = %p\n", (void *)vol->pm_super);
2517 : 0 : SPDK_NOTICELOG("\tvol->pm_logical_map = %p\n", (void *)vol->pm_logical_map);
2518 : 0 : logical_map_size = _get_pm_logical_map_size(vol->params.vol_size,
2519 : 0 : vol->params.chunk_size);
2520 : 0 : SPDK_NOTICELOG("\tlogical_map_size = 0x%" PRIx64 "\n", logical_map_size);
2521 : 0 : SPDK_NOTICELOG("\tvol->pm_chunk_maps = %p\n", (void *)vol->pm_chunk_maps);
2522 : 0 : chunk_map_size = _get_pm_total_chunks_size(vol->params.vol_size, vol->params.chunk_size,
2523 : 0 : vol->params.backing_io_unit_size);
2524 : 0 : SPDK_NOTICELOG("\tchunk_map_size = 0x%" PRIx64 "\n", chunk_map_size);
2525 : 0 : }
2526 : :
2527 : 106 : SPDK_LOG_REGISTER_COMPONENT(reduce)
|