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