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