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