Compare commits

..
Author SHA1 Message Date
Vitaliy Filippov ee0bae573f Remove n_subops=0 (TODO also needs OSD unit tests) 2026-05-21 13:31:32 +03:00
Vitaliy Filippov 27be4ee2fa Followup to mark_partial_write fix - also free subops in other branch to prevent assert on ENOSPC 2026-05-20 22:04:28 +03:00
Vitaliy Filippov 00517e2bac Fix header checksum after new store write-meta 2026-05-19 12:30:21 +03:00
Vitaliy Filippov e0a2615cbc Fix test build 2026-05-19 02:27:03 +03:00
Vitaliy Filippov 63fe3c323a Release 3.0.12
Important fixes (except the new store):

- Fixed a possible use-after-free in the OSD during error handling of initial
  commit/rollback of objects in EC pools.
- Fixed a possible free of an invalid pointer in the OSD during read errors
  from snapshot/clone chains in EC pools.
- Fixed possibly incorrect handling of commit/rollback operations in EC pools
  during pool PG count changes.
- Fixed the inverted fsync enable parameter in the ublk driver (fsync was not
  enabled on pools without immediate_commit).
- Added the raw-ls command for debugging purposes to find object versions in
  the cluster using listing operations.

New store fixes:

- Improved startup speed by using LSN-based sorting only for objects with a
  large number of intermediate versions.
- Added skip_double_claim option as a temporary workaround to fix the rare OSD
  startup error with the "double claimed block" message, observed by several
  users. This option does not affect data integrity.
- Fixed incorrect rechecking of small writes during startup, which in theory
  could lead to duplicate small write object entries on the OSD.
- Fixed fsync operation for disks with a writeback cache (without capacitors):
  - Fixed incorrect semantics of consecutive fsyncs (next fsync was not blocked
    by the previous one).
  - Added fsync when copying small writes from the buffer to the data device
    (somehow forgotten during initial development).
  - Added fsync after the initial garbage collection during OSD startup.
  - Fixed incorrect cast of LSN from uint64 to uint32, breaking fsync when
    reaching LSN 2^32.
- Added missing verification of the metadata header checksum during startup.
- Fixed incorrect updating of object checksums in perfect_csum_update=true mode.
- Fixed a possible OSD crash with "assertion failed" when processing a malformed
  EC STABILIZE operation.
- Fixed the accounting of active compactor coroutines.
- Removed broken and untested new->old store conversion support.

Minor issues fixed:

- Incorrect accounting of OSD local operation statistics in replicated pools.
- Missing non-zero exitcodes on vitastor-disk resize command errors.
- Missing reset of the list of inconsistent objects during PG restarts.
- Theoretically possible hangs of various OSD operations when working with
  completely corrupted objects (without a single available copy), and possibly
  in some other very rare situations.
- Incorrect fsyncs when deleting objects from pools without immediate_commit
  (on disks with a writeback cache), which previously could leave garbage when
  deleting misplaced objects.
- Possible crash/memory corruption of the NFS server during a targeted attack
  on NFS-RDMA.
- Possibly incorrect handling of ENOSPC/EIO write errors in replicated pools,
  leading to inability to retry the write later.
- Possible crash instead of a clean error exit when starting an OSD with the
  old storage engine on a disk with corrupted journal data.
- Shallow copying of PG configuration in the monitor, however, not related to
  actual bugs.
- Incorrect checking of allocated blocks in the QEMU driver in an unused code
  branch (without the BDRV_WANT_ZERO flag).
- Possible memory leak on read errors of corrupted objects.
- Possible incorrect PG states when corrupted objects are detected.
- Possible failure to mark all "bad" copies of an object during scrubs without
  checksums and with a large number of replicas (> 4).
- Incorrect checksum calculation in the old storage engine when
  bitmap_granularity < 4096 (a practically unused configuration).
- Theoretically possible OSD crash in rare cases during a scrub and simultaneous
  object recovery.
- Theoretically possible OSD crash when handling PING operation errors.
- Slightly suboptimal logic for reusing the RDMA send buffer.
- Possible memory leak when canceling an already running scrub via no_scrub.
- Possible memory corruption when a client (e.g., QEMU code) passes invalid
  buffers and the writeback cache is enabled.
- Potentially incorrect search for corrupted parts of EC objects (inability
  to find a "good" combination) during a scrub with checksums disabled.
- Possible additional memory usage on the OSD side when handling failed reads
  from snapshots (not a leak however - the memory was freed upon client
  disconnection).
- Potential sudden write slowdown at certain pg epoch values due to incorrect
  epoch update logic in etcd.
2026-05-18 02:24:22 +03:00
Vitaliy Filippov a88465df05 Fix build under older gcc 2026-05-18 01:17:56 +03:00
Vitaliy Filippov ad24be717a Wrap delete rdma_conn into destroy_rdma_conn 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 648e3b12f0 Replace public/protected ifdef with a friend class 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a675993c74 Remove unused osd_test.cpp and mock/ringloop.h, move epmgr out of osd_t 2026-05-18 01:17:56 +03:00
Vitaliy Filippov c9dfd0f67d Fix possible buffer overflow in NFS-RDMA 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 84919a10a9 Extract msgr_iothread to separate .{cpp,h} 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 51ae4d6e24 Remove extra assert in heap after reworking recheck 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 572b20fedc Fix mark_partial_write for replicas (TODO: try to write an OSD unit test for it) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4e2724b28f Remove duplicate subop submit/fail code 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 768b1675f8 Do not use ISA-L in test_crc32c 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 38fa722725 Remove the rest of tcmalloc from build 2026-05-18 01:17:56 +03:00
Vitaliy Filippov e56d83fb7f Remove heap->v1 conversion code (untested, unused and probably buggy) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov ff95a85875 Add bound check to je_crc32 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 98203568a8 Handle ENOENT from add_commit 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 89df98ee08 Deep copy pg_history and prev_pgs before mutation 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 0007a831b6 Add return to handle_flush_op when stopping the PG 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 40517c335f Fix absolute bmp_end 2026-05-18 01:17:56 +03:00
Vitaliy Filippov c9f7308b6a Wrap this submit_ into n_subops > 0 too 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 85c7e3bde0 Fix inverted writeback in ublk 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4fb55b3535 Rework recheck_small_writes to free multiple bad entries more correctly and not use garbage as "already freed" flag 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 912aca11a3 Improve test_recheck to check all possible combinations 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 7b454bd16c Do not try to free snapshot_bitmaps 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a0c8be46a4 Speedup blockstore_heap loading by postponing only objects with a lot of versions 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 53b4329fac Add missing active_flushers-- 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a7f41c4a12 Add assert to reconstruct_stripes_ec 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 5d78057ac3 Fix cur_op->buf memory leak on EIO/EDOM reads(?) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 8efc5a353f Fix pg_state_bits overwriting PG_HAS_CORRUPTED 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 603b26b896 Add a FIXME about delete buffering 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a3b0fe0deb Fix op stats not tracked for OP_WRITE_STABLE 2026-05-18 01:17:56 +03:00
Vitaliy Filippov f504e356d5 Fix schedule_next_recheck_at 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4ed17b7070 Sync metadata after writing out recheck_modified_blocks 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 1fd2819724 Fix marking only first OSD from non-majority group as corrupted during scrub
Note: Could be tested with 5 replicas - 3 good and 2 corrupted if we had OSD unit tests
2026-05-18 01:17:56 +03:00
Vitaliy Filippov dcdabbc1ec Fix vitastor-disk resize error return codes missed 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 625d5b7b9e Fix punch_holes not updating entry crc (bug when perfect_csum_update==true) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 9e507fd333 Fix typo in crc32_pad
Didn't affect anything though because it was only skipping < 4096 byte remainders
2026-05-18 01:17:56 +03:00
Vitaliy Filippov c2b5118127 Add data device fsyncs during compaction (forgotten during initial heap store dev) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4b926e2223 Add a test for missing data device fsyncs 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a5d9a6996a Do not try to increment ref_count if recheck_state is null 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 0ee03e7172 Add reply == request opcode check (also just in case) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 88b7d9afcd Expect to receive bitmap only for successfull response 2026-05-18 01:17:56 +03:00
Vitaliy Filippov f271c8450c Block sync by all previous syncs 2026-05-11 16:33:49 +03:00
Vitaliy Filippov f78d7d4efc Do not capture client pointer in ping lambda 2026-05-11 16:30:01 +03:00
Vitaliy Filippov fdaf7c88ff Fix RDMA send buffer wraparound - could only lead to slower sending but not to corruption because the bug was canceled by send_out_pos >= send_done_pos check 2026-05-11 16:28:03 +03:00
Vitaliy Filippov 2fb6eb0c30 Free scrub_cur_list.buf when turning on no_scrub (potential memory leak) 2026-05-11 16:15:48 +03:00
Vitaliy Filippov 36d2b56208 Deep copy merged_history, also just in case 2026-05-11 16:14:03 +03:00
Vitaliy Filippov 14b22f2ba9 Retry even fatal errors for OP_SYNC (no real-world issue, just in case) 2026-05-11 16:08:14 +03:00
Vitaliy Filippov fe8b1fe0cc Add assert in cluster_client writeback to prevent overflow when incorrect operation data is passed 2026-05-11 16:06:19 +03:00
Vitaliy Filippov 1ec963e468 Fix ec_find_good for misplaced objects with extra corrupted chunks 2026-05-11 15:57:21 +03:00
Vitaliy Filippov 5100f822d8 Wrap all submit_primary_xxx_subops waits with if (n_subops) > 0 2026-05-11 15:17:50 +03:00
Vitaliy Filippov 7432494e88 Remove _LARGEFILE64_SOURCE warnings 2026-05-11 14:59:20 +03:00
Vitaliy Filippov d0c0f3ea39 Do not ignore cur_primary change on PG state validation errors 2026-05-11 14:59:20 +03:00
Vitaliy Filippov f61190f31d Fix send_list/outbox desync on failed SEC_READ_BMP (possible memory leak) 2026-05-11 14:59:11 +03:00
Vitaliy Filippov 3dc0ab5c33 Clear inconsistent_objects on repeer 2026-05-11 14:59:08 +03:00
Vitaliy Filippov de96efed2f Fix 2^(64-PG_EPOCH_BITS) - 1 check 2026-05-11 14:59:08 +03:00
Vitaliy Filippov 87a5230798 Add forgotten header_csum check to the new store 2026-05-11 14:59:08 +03:00
Vitaliy Filippov 0c5e6d4346 Do not use modified_block(ui32) to store lsn(ui64) 2026-05-11 14:59:08 +03:00
Vitaliy Filippov b278087410 Fix assert in blockstore_heap 2026-05-11 13:01:03 +03:00
Vitaliy Filippov a8e821b13b Mark OSDs as dirty during deletes more correctly 2026-05-11 12:56:23 +03:00
Vitaliy Filippov caa70317fa Add a workaround for "double claim" bug with the new store 2026-05-10 17:00:36 +03:00
Vitaliy Filippov b8eaaabfe4 Add raw-ls command for troubleshooting 2026-05-10 17:00:36 +03:00
Vitaliy Filippov e4d80c415e Always check pool resharding state also for stabilize/rollback 2026-05-10 13:22:02 +03:00
Vitaliy Filippov 553191c3ff Add asserts to the new store too O:-) 2026-05-10 11:39:54 +03:00
Vitaliy Filippov ab385252b5 Add offset asserts before each write 2026-05-10 10:56:32 +03:00
Vitaliy Filippov 041185c673 Release 3.0.11
A single hotfix:

- Fix missing error handling in EC snapshot/cloned image reads potentially
  leading to corrupted reads during OSD/PG restarts
2026-05-09 18:49:36 +03:00
Vitaliy Filippov b03ac80a57 Fix missing error handling in chained read sec_read_bmp handling 2026-05-07 15:01:49 +03:00
Vitaliy Filippov 2ba56074f9 Run copy_part_bitmaps only after all subops succeed (slightly more correct) 2026-05-07 15:01:40 +03:00
Vitaliy Filippov 4acfe149cb Release 3.0.10
Important bug fixes (new store):
- Fix OSDs possibly refusing to start with "write metadata failed at offset xxx: Invalid argument"
  (fix buffer alignment during initial garbage collection)
- Rollback change from 3.0.4 - on-disk garbage entries are not skipped on start again. This change
  doesn't have any impact normally, but OSDs originally running 3.0.0-3.0.2 and then upgraded
  to 3.0.9 may hit a bug where 3.0.9 refuses to start due to entries marked as garbage too
  early and flushed to disk in 3.0.0-3.0.2.

Other changes:
- Auto-select the only RDMA device/port if there is only one
- Rollback one 3.0.9 change - there was no actual use-after-free :)
  (the problem was only relevant to an unstable development version)
- Fix `vitastor-nfs --trace` option
- Fix an unintended 1 second sleep in vitastor-cli rm-data
- Fix inode statistics not being cleared for a deleted pool
- Fix print to stdout in client
2026-04-27 13:53:48 +03:00
Vitaliy Filippov 008ed5b269 Rollback 25ecca7625 - there was no actual use-after-free :) 2026-04-22 01:35:38 +03:00
Vitaliy Filippov 4fffe0f032 Fix vitastor-nfs trace option 2026-04-20 21:51:47 +03:00
Vitaliy Filippov a76d5ccc0d Wakeup callers in rm-data 2026-04-17 13:53:32 +03:00
Vitaliy Filippov 8ed1e180e0 Clear inode_stats in mon 2026-04-14 02:44:46 +03:00
Vitaliy Filippov 8832fc3b14 Fix print to stdout in client 2026-04-14 02:43:50 +03:00
Vitaliy Filippov 0134934c99 Do not skip garbage entries on start (rollback change from 3.0.4) 2026-04-11 12:05:02 +03:00
Vitaliy Filippov 2e36f292bd Fix buffer alignment during metadata clearing on init 2026-04-10 21:56:12 +03:00
Vitaliy Filippov bcc6419760 Auto-select the only RDMA device/port if there is only one 2026-04-08 15:46:10 +03:00
Vitaliy Filippov dd5941b9a4 Release 3.0.9
- Fix broken garbage collection in the new store leading to very high memory usage
- Make the new store do full garbage collection on OSD startup by default (can be turned off with a new gc_on_start parameter)
- Make startup logging with the new store more informative, allow to log amount of garbage with log_level > 1
- Fix clients reading from snapshots incorrectly on retries (for example when OSDs were restarted during read)
- Fix a possible "send-after-free" in OSD which could probably also affect snapshot reads
- Fix OSD crashing when handling a read_bitmap operation from a deleted object with the new store
2026-04-04 18:38:40 +03:00
Vitaliy Filippov 4005b88865 Do not use node_type 2026-04-04 17:37:47 +03:00
Vitaliy Filippov 280b5cd675 Add gc_on_start docs 2026-04-04 17:19:32 +03:00
Vitaliy Filippov e5c505eaf4 Write 1 entry to blocks to collect garbage 2026-04-03 18:08:12 +00:00
Vitaliy Filippov c1d244d4f0 Fix init metadata entry loading logging 2026-04-03 18:08:12 +00:00
Vitaliy Filippov 9b264a212f Do full metadata GC on start (new store) 2026-04-03 18:08:12 +00:00
Vitaliy Filippov ff7f5cb4f4 Report live & garbage entry count / memory with log_level > 1 2026-04-02 23:45:13 +00:00
Vitaliy Filippov 25ecca7625 Fix use-after-free in OSD ops (ooops) 2026-04-02 13:48:17 +00:00
Vitaliy Filippov 99c4244004 Try to write into blocks with more garbage first 2026-04-02 13:48:17 +00:00
Vitaliy Filippov 9949b9fb4e Fix incorrect reads from snapshots on retries 2026-03-31 02:06:53 +03:00
Vitaliy Filippov e6881ad1d5 Fix read_bitmap from deleted objects with the new store 2026-03-31 01:59:53 +03:00
Vitaliy Filippov b30635b932 Release 3.0.8
- Surrogate increment-only peer IDs are now used instead of file descriptor numbers,
  and client cleanup sequence is reworked in another attempt to fix a possible PG hang
  in 'peering' states. The history of this bug is still unclear and it still hasn't been
  reproduced in tests, but several users have reported it since 3.0.4 (only with enabled RDMA).
- Fix a minor bug where an OSD could skip reporting misplaced PG state when moving
  an object in non-immediate_commit mode.
- Add Ubuntu 26.04 (Resolute Raccoon) support and patches for qemu 10.2 and libvirt 12.0
2026-03-30 02:09:02 +03:00
Vitaliy Filippov 0c1154833c Add Ubuntu 26.04 support 2026-03-30 02:06:10 +03:00
Vitaliy Filippov c227bb05b6 Fix incorrect patch contents for qemu 10.1, add patch for qemu 10.2 2026-03-30 02:06:10 +03:00
Vitaliy Filippov dd85315f22 Update CMake supported versions 2026-03-30 02:06:10 +03:00
Vitaliy Filippov 47d2f4e0be Add patch for libvirt 12.0 2026-03-30 02:06:10 +03:00
Vitaliy Filippov 2a5028d17f Use robin_hood_map in messenger 2026-03-30 02:06:10 +03:00
Vitaliy Filippov 07915c2881 Use surrogate peer IDs instead of FDs 2026-03-30 02:06:10 +03:00
Vitaliy Filippov 79141eb383 Fix commented out //this->pg_state_dirty.insert 2026-03-28 20:08:52 +03:00
Vitaliy Filippov f7cbb6ed56 Remove FD from the event loop in destroy_client, not in stop_client 2026-03-28 14:18:20 +03:00
Vitaliy Filippov 8f8172db99 clear_immediate_ops is now done by stop_client 2026-03-26 01:08:23 +03:00
Vitaliy Filippov 94be147e80 Clear received_ops - they are already freed 2026-03-26 01:08:02 +03:00
123 changed files with 3625 additions and 1724 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
# leak sanitizer sometimes crashes
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 test
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 build_tests test
npm_lint:
runs-on: ubuntu-latest
+7 -7
View File
@@ -1,20 +1,20 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
set(VITASTOR_VERSION "3.0.6")
set(VITASTOR_VERSION "3.0.12")
include(CTest)
add_custom_target(build_tests)
add_custom_target(test
COMMAND
echo leak:tcmalloc > ${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt &&
env LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt ${CMAKE_CTEST_COMMAND}
set_property(TEST PROPERTY ENVIRONMENT LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt)
add_test(gen_lsan_suppress
${CMAKE_COMMAND} -E echo leak:tcmalloc > "${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt"
)
set_tests_properties(gen_lsan_suppress PROPERTIES FIXTURES_SETUP f_lsan_suppress)
set_property(TEST PROPERTY FIXTURES_REQUIRED f_lsan_suppress)
# make -j16 -C ../../build test_heap && ../../build/src/test/test_heap
# make -j16 -C ../../build test_heap && rm -f $(find ../../build -name '*.gcda') && ctest -V -T test -T coverage -R heap --test-dir ../../build && (cd ../../build; gcovr -f ../src --html --html-nested -o coverage/index.html; cd ../src/test)
# make -j16 -C ../../build test_blockstore && rm -f $(find ../../build -name '*.gcda') && ctest -V -T test -T coverage -R blockstore --test-dir ../../build && (cd ../../build; gcovr -f ../src --html --html-nested -o coverage/index.html; cd ../src/test)
# kcov --include-path=../../../src ../../kcov ./test_blockstore
add_dependencies(test build_tests)
add_subdirectory(src)
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.6
VITASTOR_VERSION ?= v3.0.12
all: build push
+1 -1
View File
@@ -49,7 +49,7 @@ spec:
capabilities:
add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v3.0.6
image: vitalif/vitastor-csi:v3.0.12
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -121,7 +121,7 @@ spec:
privileged: true
capabilities:
add: ["SYS_ADMIN"]
image: vitalif/vitastor-csi:v3.0.6
image: vitalif/vitastor-csi:v3.0.12
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -5,7 +5,7 @@ package vitastor
const (
vitastorCSIDriverName = "csi.vitastor.io"
vitastorCSIDriverVersion = "3.0.6"
vitastorCSIDriverVersion = "3.0.12"
)
// Config struct fills the parameters of request or user input
+1 -1
View File
@@ -1,4 +1,4 @@
vitastor (3.0.6-1) unstable; urgency=medium
vitastor (3.0.12-1) unstable; urgency=medium
* Bugfixes
+1 -1
View File
@@ -44,7 +44,7 @@ curl -s https://git.yourcmc.ru/vitalif/antietcd/archive/master.tar.gz | tar -zx
curl -s https://git.yourcmc.ru/vitalif/tinyraft/archive/master.tar.gz | tar -zx
cd /root/vitastor/packages/vitastor-$REL
if [[ "$REL" = "trixie" && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
if [[ ( "$REL" = "trixie" || "$REL" = "resolute" ) && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
# Fucking shit, archives differ between bookworm (xz 5.4.1) and trixie (xz 5.8.1)
cp ../vitastor-bookworm/vitastor_$VER.orig.tar.xz .
else
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.6
VITASTOR_VERSION ?= v3.0.12
all: build push
+1 -1
View File
@@ -4,7 +4,7 @@
#
# Desired Vitastor version
VITASTOR_VERSION=v3.0.6
VITASTOR_VERSION=v3.0.12
# Additional arguments for all containers
# For example, you may want to specify a custom logging driver here
+7
View File
@@ -70,6 +70,7 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -753,3 +754,9 @@ This option sets the maximum number of object is a chunk. Moving 100k objects us
- Default: 100
This option sets the interval between handling two PG count change chunks.
## gc_on_start
- Type: boolean
Forcibly clean all garbage entries in the new store on every OSD restart.
+7
View File
@@ -71,6 +71,7 @@
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -793,3 +794,9 @@ pg_minsize OSD во время переключений, что может по
- Значение по умолчанию: 100
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
## gc_on_start
- Тип: булево (да/нет)
Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
+4
View File
@@ -938,3 +938,7 @@
This option sets the interval between handling two PG count change chunks.
info_ru: |
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
- name: gc_on_start
type: bool
info: Forcibly clean all garbage entries in the new store on every OSD restart.
info_ru: Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
+2 -2
View File
@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
The instruction is very simple.
1. Download a Docker image of the desired version: \
`docker pull vitalif/vitastor:v3.0.6`
`docker pull vitalif/vitastor:v3.0.12`
2. Install scripts to the host system: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.6 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
3. Reload udev rules: \
`udevadm control --reload-rules`
4. Enable the vitastor-host service: \
+2 -2
View File
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
Инструкция по установке максимально простая.
1. Скачайте Docker-образ желаемой версии: \
`docker pull vitalif/vitastor:v3.0.6`
`docker pull vitalif/vitastor:v3.0.12`
2. Установите скрипты в хост-систему командой: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.6 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
3. Перезагрузите правила udev: \
`udevadm control --reload-rules`
4. Включите сервис vitastor-host: \
+1
View File
@@ -17,6 +17,7 @@
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
- Add `-oldstable` to bookworm/bullseye/buster in this line to install the last
stable version from 0.9.x branch instead of 1.x
- To always prefer vitastor-patched QEMU and Libvirt versions, add the following to `/etc/apt/preferences`:
+1
View File
@@ -17,6 +17,7 @@
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
- Добавьте `-oldstable` к слову bookworm/bullseye/buster в этой строке, чтобы
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
- Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`:
+1 -2
View File
@@ -16,8 +16,7 @@
designated initializers support from C++20
- CMake
- jerasure headers and libraries
- ISA-L, libibverbs and librdmacm headers and libraries (optional)
- tcmalloc (google-perftools-dev)
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
## Basic instructions
+1 -2
View File
@@ -16,8 +16,7 @@
назначенных инициализаторов (designated initializers) из C++20
- CMake
- Заголовки и библиотеки jerasure
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm
- tcmalloc (google-perftools-dev)
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
## Базовая инструкция
+1
View File
@@ -262,3 +262,4 @@ Options:
| `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |
+1
View File
@@ -274,3 +274,4 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+1 -1
Submodule json11 updated: fd37016cf8...edcd85b8bd
+2 -2
View File
@@ -627,7 +627,7 @@ class Mon
if (this.state.pg.history[pool_id] &&
this.state.pg.history[pool_id][pg])
{
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
pg_history[pg-1] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
}
}
const real_prev_pgs = [];
@@ -719,7 +719,7 @@ class Mon
this.next_recheck_timer = null;
this.next_recheck_at = 0;
this.schedule_recheck();
}, now-this.next_recheck_at);
}, (this.next_recheck_at-now)*1000);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor-mon",
"version": "3.0.6",
"version": "3.0.12",
"description": "Vitastor SDS monitor service",
"main": "mon-main.js",
"scripts": {
+2 -2
View File
@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
finish_pg_history(merged_history[1]);
for (let i = 0; i < new_pg_count; i++)
{
new_pg_history[i] = { ...merged_history[1] };
new_pg_history[i] = JSON.parse(JSON.stringify(merged_history[1]));
}
}
// Mark history keys for removed PGs as removed
@@ -102,7 +102,7 @@ function scale_pg_count(prev_pgs, new_pg_count)
{
for (let i = prev_pgs.length; i < new_pg_count; i++)
{
prev_pgs[i] = prev_pgs[i % prev_pgs.length];
prev_pgs[i] = [ ...prev_pgs[i % prev_pgs.length] ];
}
}
else if (prev_pgs.length > new_pg_count)
+1
View File
@@ -37,6 +37,7 @@ function derive_osd_stats(st, prev, prev_diff)
const n = c.count - BigInt(pr && pr.count||0);
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
}
diff.inode_stats = {};
for (const pool_id in st.inode_stats||{})
{
diff.inode_stats[pool_id] = {};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor",
"version": "3.0.6",
"version": "3.0.12",
"description": "Low-level native bindings to Vitastor client library",
"main": "index.js",
"keywords": [
+1 -1
View File
@@ -50,7 +50,7 @@ from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume import volume_utils
VITASTOR_VERSION = '3.0.6'
VITASTOR_VERSION = '3.0.12'
LOG = logging.getLogger(__name__)
+637
View File
@@ -0,0 +1,637 @@
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h
index aaad4a3da1..5f5daa8341 100644
--- a/include/libvirt/libvirt-storage.h
+++ b/include/libvirt/libvirt-storage.h
@@ -326,6 +326,7 @@ typedef enum {
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17, /* (Since: 1.2.8) */
VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE = 1 << 18, /* (Since: 3.1.0) */
VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT = 1 << 19, /* (Since: 5.6.0) */
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR = 1 << 20, /* (Since: 5.0.0) */
} virConnectListAllStoragePoolsFlags;
int virConnectListAllStoragePools(virConnectPtr conn,
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9ca5c2450c..cc52f00c0c 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7453,7 +7453,8 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
src->configFile = virXPathString("string(./config/@file)", ctxt);
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
- src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS)
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR)
src->query = virXMLPropString(node, "query");
if (virDomainStorageNetworkParseHosts(node, ctxt, &src->hosts, &src->nhosts) < 0)
@@ -32187,6 +32188,7 @@ virDomainStorageSourceTranslateSourcePool(virStorageSource *src,
case VIR_STORAGE_POOL_MPATH:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_LAST:
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index 7346a61731..83e94d762e 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -520,6 +520,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
case VIR_STORAGE_NET_PROTOCOL_RBD:
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
@@ -592,7 +593,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
}
}
- /* internal snapshots and config files are currently supported only with rbd: */
+ /* internal snapshots are currently supported only with rbd: */
if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
if (src->snapshot) {
@@ -600,10 +601,14 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
_("<snapshot> element is currently supported only with 'rbd' disks"));
return -1;
}
-
+ }
+ /* config files are currently supported only with rbd and vitastor: */
+ if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
if (src->configFile) {
virReportError(VIR_ERR_XML_ERROR, "%s",
- _("<config> element is currently supported only with 'rbd' disks"));
+ _("<config> element is currently supported only with 'rbd' and 'vitastor' disks"));
return -1;
}
}
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 114dd3f96f..c71f9a3277 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -2093,6 +2093,35 @@
</element>
</define>
+ <define name="diskSourceNetworkProtocolVitastor">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <value>vitastor</value>
+ </attribute>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <optional>
+ <attribute name="query"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="diskSourceNetworkHost"/>
+ </zeroOrMore>
+ <optional>
+ <element name="config">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <empty/>
+ </interleave>
+ </element>
+ </define>
+
<define name="diskSourceNetworkProtocolISCSI">
<element name="source">
<attribute name="protocol">
@@ -2443,6 +2472,7 @@
<ref name="diskSourceNetworkProtocolSimple"/>
<ref name="diskSourceNetworkProtocolVxHS"/>
<ref name="diskSourceNetworkProtocolNFS"/>
+ <ref name="diskSourceNetworkProtocolVitastor"/>
</choice>
</define>
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 1dc9365bf2..a8a736be81 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -56,7 +56,7 @@ VIR_ENUM_IMPL(virStoragePool,
"logical", "disk", "iscsi",
"iscsi-direct", "scsi", "mpath",
"rbd", "sheepdog", "gluster",
- "zfs", "vstorage",
+ "zfs", "vstorage", "vitastor",
);
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
@@ -242,6 +242,18 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatToString = virStorageFileFormatTypeToString,
}
},
+ {.poolType = VIR_STORAGE_POOL_VITASTOR,
+ .poolOptions = {
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
+ VIR_STORAGE_POOL_SOURCE_NETWORK |
+ VIR_STORAGE_POOL_SOURCE_NAME),
+ },
+ .volOptions = {
+ .defaultFormat = VIR_STORAGE_FILE_RAW,
+ .formatFromString = virStorageVolumeFormatFromString,
+ .formatToString = virStorageFileFormatTypeToString,
+ }
+ },
{.poolType = VIR_STORAGE_POOL_SHEEPDOG,
.poolOptions = {
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
@@ -538,6 +550,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
_("element 'name' is mandatory for RBD pool"));
return -1;
}
+ if (pool_type == VIR_STORAGE_POOL_VITASTOR && source->name == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("element 'name' is mandatory for Vitastor pool"));
+ return -1;
+ }
if (options->formatFromString) {
g_autofree char *format = NULL;
@@ -1127,6 +1144,7 @@ virStoragePoolDefFormatBuf(virBuffer *buf,
/* RBD, Sheepdog, Gluster and Iscsi-direct devices are not local block devs nor
* files, so they don't have a target */
if (def->type != VIR_STORAGE_POOL_RBD &&
+ def->type != VIR_STORAGE_POOL_VITASTOR &&
def->type != VIR_STORAGE_POOL_SHEEPDOG &&
def->type != VIR_STORAGE_POOL_GLUSTER &&
def->type != VIR_STORAGE_POOL_ISCSI_DIRECT) {
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index fc67957cfe..720c07ef74 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -103,6 +103,7 @@ typedef enum {
VIR_STORAGE_POOL_GLUSTER, /* Gluster device */
VIR_STORAGE_POOL_ZFS, /* ZFS */
VIR_STORAGE_POOL_VSTORAGE, /* Virtuozzo Storage */
+ VIR_STORAGE_POOL_VITASTOR, /* Vitastor */
VIR_STORAGE_POOL_LAST,
} virStoragePoolType;
@@ -454,6 +455,7 @@ VIR_ENUM_DECL(virStoragePartedFs);
VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \
VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR | \
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \
VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS | \
diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c
index d7b9bdfecb..38aefd0dd4 100644
--- a/src/conf/storage_source_conf.c
+++ b/src/conf/storage_source_conf.c
@@ -90,6 +90,7 @@ VIR_ENUM_IMPL(virStorageNetProtocol,
"ssh",
"vxhs",
"nfs",
+ "vitastor",
);
@@ -1317,6 +1318,7 @@ virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol)
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
return 24007;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_RBD:
/* we don't provide a default for RBD */
return 0;
diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h
index 22c35d420d..f1e32ea83d 100644
--- a/src/conf/storage_source_conf.h
+++ b/src/conf/storage_source_conf.h
@@ -131,6 +131,7 @@ typedef enum {
VIR_STORAGE_NET_PROTOCOL_SSH,
VIR_STORAGE_NET_PROTOCOL_VXHS,
VIR_STORAGE_NET_PROTOCOL_NFS,
+ VIR_STORAGE_NET_PROTOCOL_VITASTOR,
VIR_STORAGE_NET_PROTOCOL_LAST
} virStorageNetProtocol;
diff --git a/src/conf/virstorageobj.c b/src/conf/virstorageobj.c
index 59fa5da372..4739167f5f 100644
--- a/src/conf/virstorageobj.c
+++ b/src/conf/virstorageobj.c
@@ -1438,6 +1438,7 @@ virStoragePoolObjSourceFindDuplicateCb(const void *payload,
return 1;
break;
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_RBD:
case VIR_STORAGE_POOL_LAST:
@@ -1921,6 +1922,8 @@ virStoragePoolObjMatch(virStoragePoolObj *obj,
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR) &&
+ (obj->def->type == VIR_STORAGE_POOL_VITASTOR)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c
index db7660aac4..561df34709 100644
--- a/src/libvirt-storage.c
+++ b/src/libvirt-storage.c
@@ -94,6 +94,7 @@ virStoragePoolGetConnect(virStoragePoolPtr pool)
* VIR_CONNECT_LIST_STORAGE_POOLS_SCSI
* VIR_CONNECT_LIST_STORAGE_POOLS_MPATH
* VIR_CONNECT_LIST_STORAGE_POOLS_RBD
+ * VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR
* VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG
* VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER
* VIR_CONNECT_LIST_STORAGE_POOLS_ZFS
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
index 2b988157fa..9d0eb47b25 100644
--- a/src/libxl/libxl_conf.c
+++ b/src/libxl/libxl_conf.c
@@ -1069,6 +1069,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src,
case VIR_STORAGE_NET_PROTOCOL_SSH:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_NO_SUPPORT,
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
index e72e7d7f44..8482c21805 100644
--- a/src/libxl/xen_xl.c
+++ b/src/libxl/xen_xl.c
@@ -1461,6 +1461,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src)
case VIR_STORAGE_NET_PROTOCOL_SSH:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_NO_SUPPORT,
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 9b43279797..459d8e8a65 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -743,6 +743,38 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
}
+static virJSONValue *
+qemuBlockStorageSourceGetVitastorProps(virStorageSource *src)
+{
+ virJSONValue *ret = NULL;
+ virStorageNetHostDef *host;
+ size_t i;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+ g_autofree char *etcd = NULL;
+
+ for (i = 0; i < src->nhosts; i++) {
+ host = src->hosts + i;
+ if ((virStorageNetHostTransport)host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
+ return NULL;
+ }
+ virBufferAsprintf(&buf, i > 0 ? ",%s:%u" : "%s:%u", host->name, host->port);
+ }
+ if (src->nhosts > 0) {
+ etcd = virBufferContentAndReset(&buf);
+ }
+
+ if (virJSONValueObjectAdd(&ret,
+ "S:etcd-host", etcd,
+ "S:etcd-prefix", src->query,
+ "S:config-path", src->configFile,
+ "s:image", src->path,
+ NULL) < 0)
+ return NULL;
+
+ return ret;
+}
+
+
static virJSONValue *
qemuBlockStorageSourceGetSshProps(virStorageSource *src)
{
@@ -1094,6 +1126,12 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
return NULL;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ driver = "vitastor";
+ if (!(fileprops = qemuBlockStorageSourceGetVitastorProps(src)))
+ return NULL;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SSH:
driver = "ssh";
if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
@@ -1997,6 +2035,7 @@ qemuBlockGetBackingStoreString(virStorageSource *src,
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
case VIR_STORAGE_NET_PROTOCOL_SSH:
@@ -2377,6 +2416,12 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
return -1;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ driver = "vitastor";
+ if (!(location = qemuBlockStorageSourceGetVitastorProps(src)))
+ return -1;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SSH:
if (srcPriv->nbdkitProcess) {
/* disk creation not yet supported with nbdkit, and even if it
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index ac56fc7cb4..9e407b4aab 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -4677,7 +4677,8 @@ qemuDomainValidateStorageSource(virStorageSource *src,
if (src->query &&
(actualType != VIR_STORAGE_TYPE_NETWORK ||
(src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTPS &&
- src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP))) {
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("query is supported only with HTTP(S) protocols"));
return -1;
@@ -9103,6 +9104,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSource *src,
break;
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index e738afffc3..37d64f469b 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -665,6 +665,7 @@ qemuSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDef *snapdisk,
case VIR_STORAGE_NET_PROTOCOL_NONE:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
@@ -893,6 +894,7 @@ qemuSnapshotPrepareDiskInternal(virDomainDiskDef *disk,
case VIR_STORAGE_NET_PROTOCOL_NONE:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index e19e032427..59f91f4710 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1626,6 +1626,7 @@ storageVolLookupByPathCallback(virStoragePoolObj *obj,
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_ZFS:
case VIR_STORAGE_POOL_LAST:
diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c
index 821378883c..2211f6891b 100644
--- a/src/storage_file/storage_source_backingstore.c
+++ b/src/storage_file/storage_source_backingstore.c
@@ -264,6 +264,75 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
}
+static int
+virStorageSourceParseVitastorColonString(const char *colonstr,
+ virStorageSource *src)
+{
+ char *p, *e, *next;
+ g_autofree char *options = NULL;
+
+ /* optionally skip the "vitastor:" prefix if provided */
+ if (STRPREFIX(colonstr, "vitastor:"))
+ colonstr += strlen("vitastor:");
+
+ options = g_strdup(colonstr);
+
+ p = options;
+ while (*p) {
+ /* find : delimiter or end of string */
+ for (e = p; *e && *e != ':'; ++e) {
+ if (*e == '\\') {
+ e++;
+ if (*e == '\0')
+ break;
+ }
+ }
+ if (*e == '\0') {
+ next = e; /* last kv pair */
+ } else {
+ next = e + 1;
+ *e = '\0';
+ }
+
+ if (STRPREFIX(p, "image=")) {
+ src->path = g_strdup(p + strlen("image="));
+ } else if (STRPREFIX(p, "etcd-prefix=")) {
+ src->query = g_strdup(p + strlen("etcd-prefix="));
+ } else if (STRPREFIX(p, "config-path=")) {
+ src->configFile = g_strdup(p + strlen("config-path="));
+ } else if (STRPREFIX(p, "etcd-host=")) {
+ char *h, *sep;
+
+ h = p + strlen("etcd-host=");
+ while (h < e) {
+ for (sep = h; sep < e; ++sep) {
+ if (*sep == '\\' && (sep[1] == ',' ||
+ sep[1] == ';' ||
+ sep[1] == ' ')) {
+ *sep = '\0';
+ sep += 2;
+ break;
+ }
+ }
+
+ if (virStorageSourceRBDAddHost(src, h) < 0)
+ return -1;
+
+ h = sep;
+ }
+ }
+
+ p = next;
+ }
+
+ if (!src->path) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int
virStorageSourceParseNBDColonString(const char *nbdstr,
virStorageSource *src)
@@ -379,6 +448,11 @@ virStorageSourceParseBackingColon(virStorageSource *src,
return -1;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ if (virStorageSourceParseVitastorColonString(path, src) < 0)
+ return -1;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
@@ -953,6 +1027,54 @@ virStorageSourceParseBackingJSONRBD(virStorageSource *src,
return 0;
}
+static int
+virStorageSourceParseBackingJSONVitastor(virStorageSource *src,
+ virJSONValue *json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *filename;
+ const char *image = virJSONValueObjectGetString(json, "image");
+ const char *conf = virJSONValueObjectGetString(json, "config-path");
+ const char *etcd_prefix = virJSONValueObjectGetString(json, "etcd-prefix");
+ virJSONValue *servers = virJSONValueObjectGetArray(json, "server");
+ size_t nservers;
+ size_t i;
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VITASTOR;
+
+ /* legacy syntax passed via 'filename' option */
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
+ return virStorageSourceParseVitastorColonString(filename, src);
+
+ if (!image) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing image name in Vitastor backing volume "
+ "JSON specification"));
+ return -1;
+ }
+
+ src->path = g_strdup(image);
+ src->configFile = g_strdup(conf);
+ src->query = g_strdup(etcd_prefix);
+
+ if (servers) {
+ nservers = virJSONValueArraySize(servers);
+
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
+ src->nhosts = nservers;
+
+ for (i = 0; i < nservers; i++) {
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
+ virJSONValueArrayGet(servers, i)) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int
virStorageSourceParseBackingJSONRaw(virStorageSource *src,
virJSONValue *json,
@@ -1130,6 +1252,7 @@ static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
{"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
{"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
{"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
+ {"vitastor", false, virStorageSourceParseBackingJSONVitastor, 0},
{"raw", true, virStorageSourceParseBackingJSONRaw, 0},
{"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
{"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index 1165689de7..bba846351c 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -7345,6 +7345,7 @@ testStorageVolumeTypeForPool(int pooltype)
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
return VIR_STORAGE_VOL_NETWORK;
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
diff --git a/tests/storagepoolcapsschemadata/poolcaps-fs.xml b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
index eee75af746..8bd0a57bdd 100644
--- a/tests/storagepoolcapsschemadata/poolcaps-fs.xml
+++ b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
@@ -204,4 +204,11 @@
</enum>
</volOptions>
</pool>
+ <pool type='vitastor' supported='no'>
+ <volOptions>
+ <defaultFormat type='raw'/>
+ <enum name='targetFormatType'>
+ </enum>
+ </volOptions>
+ </pool>
</storagepoolCapabilities>
diff --git a/tests/storagepoolcapsschemadata/poolcaps-full.xml b/tests/storagepoolcapsschemadata/poolcaps-full.xml
index 805950a937..852df0de16 100644
--- a/tests/storagepoolcapsschemadata/poolcaps-full.xml
+++ b/tests/storagepoolcapsschemadata/poolcaps-full.xml
@@ -204,4 +204,11 @@
</enum>
</volOptions>
</pool>
+ <pool type='vitastor' supported='yes'>
+ <volOptions>
+ <defaultFormat type='raw'/>
+ <enum name='targetFormatType'>
+ </enum>
+ </volOptions>
+ </pool>
</storagepoolCapabilities>
diff --git a/tests/storagepoolxml2argvtest.c b/tests/storagepoolxml2argvtest.c
index d5c2531ab8..b19308ac38 100644
--- a/tests/storagepoolxml2argvtest.c
+++ b/tests/storagepoolxml2argvtest.c
@@ -57,6 +57,7 @@ testCompareXMLToArgvFiles(bool shouldFail,
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_ZFS:
case VIR_STORAGE_POOL_VSTORAGE:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_LAST:
default:
VIR_TEST_DEBUG("pool type '%s' has no xml2argv test", defTypeStr);
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
index 2010ef1356..072e2ff9e8 100644
--- a/tools/virsh-pool.c
+++ b/tools/virsh-pool.c
@@ -1187,6 +1187,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
case VIR_STORAGE_POOL_VSTORAGE:
flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE;
break;
+ case VIR_STORAGE_POOL_VITASTOR:
+ flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR;
+ break;
case VIR_STORAGE_POOL_LAST:
break;
}
+171 -28
View File
@@ -1,29 +1,172 @@
diff --git a/src/client/qemu_driver.c b/src/client/qemu_driver.c
index d8356dab..5f4cd50d 100644
--- a/src/client/qemu_driver.c
+++ b/src/client/qemu_driver.c
@@ -974,14 +974,21 @@ static void vitastor_co_read_bitmap_cb(void *opaque, long retval, uint8_t *bitma
#endif
}
diff --git a/block/meson.build b/block/meson.build
index 34b1b2a306..24ca0f1e52 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -114,6 +114,7 @@ foreach m : [
[libnfs, 'nfs', files('nfs.c')],
[libssh, 'ssh', files('ssh.c')],
[rbd, 'rbd', files('rbd.c')],
+ [vitastor, 'vitastor', files('vitastor.c')],
]
if m[0].found()
module_ss = ss.source_set()
diff --git a/meson.build b/meson.build
index 50c774a195..e5c7a3a4b1 100644
--- a/meson.build
+++ b/meson.build
@@ -1652,6 +1652,26 @@ if not get_option('rbd').auto() or have_block
endif
endif
-static int coroutine_fn vitastor_co_block_status(
- BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
- int64_t *pnum, int64_t *map, BlockDriverState **file)
+static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
+#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ unsigned int mode,
+#else
+ bool want_zero,
+#endif
+ int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file)
{
// Allocated => return BDRV_BLOCK_DATA|BDRV_BLOCK_OFFSET_VALID
// Not allocated => return 0
// Error => return -errno
// Set pnum to length of the extent, `*map` = `offset`, `*file` = `bs`
+#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ int want_zero = (mode == BDRV_WANT_PRECISE);
+#endif
VitastorRPC task;
VitastorClient *client = bs->opaque;
uint64_t inode = client->watch ? vitastor_c_inode_get_num(client->watch) : client->inode;
+vitastor = not_found
+if not get_option('vitastor').auto() or have_block
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
+ required: get_option('vitastor'))
+ if libvitastor_client.found()
+ if cc.links('''
+ #include <vitastor_c.h>
+ int main(void) {
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ return 0;
+ }''', dependencies: libvitastor_client)
+ vitastor = declare_dependency(dependencies: libvitastor_client)
+ elif get_option('vitastor').enabled()
+ error('could not link libvitastor_client')
+ else
+ warning('could not link libvitastor_client, disabling')
+ endif
+ endif
+endif
+
glusterfs = not_found
glusterfs_ftruncate_has_stat = false
glusterfs_iocb_has_stat = false
@@ -2547,6 +2567,7 @@ endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
@@ -4972,6 +4993,7 @@ summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
+summary_info += {'vitastor support': vitastor}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
diff --git a/meson_options.txt b/meson_options.txt
index fff1521e58..f0844c0e00 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -202,6 +202,8 @@ option('pvg', type: 'feature', value: 'auto',
description: 'macOS paravirtualized graphics support')
option('rbd', type : 'feature', value : 'auto',
description: 'Ceph block device driver')
+option('vitastor', type : 'feature', value : 'auto',
+ description: 'Vitastor block device driver')
option('opengl', type : 'feature', value : 'auto',
description: 'OpenGL support')
option('rdma', type : 'feature', value : 'auto',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index dc6eb4ae23..d043f4340e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3280,7 +3280,7 @@
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
'raw', 'rbd',
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
- 'ssh', 'throttle', 'vdi', 'vhdx',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -4363,6 +4363,28 @@
'*key-secret': 'str',
'*server': ['InetSocketAddressBase'] } }
+##
+# @BlockdevOptionsVitastor:
+#
+# Driver specific block device options for vitastor
+#
+# @image: Image name
+# @inode: Inode number
+# @pool: Pool ID
+# @size: Desired image size in bytes
+# @config-path: Path to Vitastor configuration
+# @etcd-host: etcd connection address(es)
+# @etcd-prefix: etcd key/value prefix
+##
+{ 'struct': 'BlockdevOptionsVitastor',
+ 'data': { '*inode': 'uint64',
+ '*pool': 'uint64',
+ '*size': 'uint64',
+ '*image': 'str',
+ '*config-path': 'str',
+ '*etcd-host': 'str',
+ '*etcd-prefix': 'str' } }
+
##
# @ReplicationMode:
#
@@ -4831,6 +4853,7 @@
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vitastor': 'BlockdevOptionsVitastor',
'virtio-blk-vfio-pci':
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
'if': 'CONFIG_BLKIO' },
@@ -5304,6 +5327,20 @@
'*cluster-size' : 'size',
'*encrypt' : 'RbdEncryptionCreateOptions' } }
+##
+# @BlockdevCreateOptionsVitastor:
+#
+# Driver specific image creation options for Vitastor.
+#
+# @location: Where to store the new image file. This location cannot
+# point to a snapshot.
+#
+# @size: Size of the virtual disk in bytes
+##
+{ 'struct': 'BlockdevCreateOptionsVitastor',
+ 'data': { 'location': 'BlockdevOptionsVitastor',
+ 'size': 'size' } }
+
##
# @BlockdevVmdkSubformat:
#
@@ -5526,6 +5563,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 0ebe6bc52a..2c37ad3892 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -175,6 +175,7 @@ meson_options_help() {
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
printf "%s\n" ' qpl Query Processing Library support'
printf "%s\n" ' rbd Ceph block device driver'
+ printf "%s\n" ' vitastor Vitastor block device driver'
printf "%s\n" ' rdma Enable RDMA-based migration'
printf "%s\n" ' replication replication support'
printf "%s\n" ' rust Rust support'
@@ -459,6 +460,8 @@ _meson_option_parse() {
--disable-qpl) printf "%s" -Dqpl=disabled ;;
--enable-rbd) printf "%s" -Drbd=enabled ;;
--disable-rbd) printf "%s" -Drbd=disabled ;;
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
--enable-rdma) printf "%s" -Drdma=enabled ;;
--disable-rdma) printf "%s" -Drdma=disabled ;;
--enable-relocatable) printf "%s" -Drelocatable=true ;;
+172
View File
@@ -0,0 +1,172 @@
diff --git a/block/meson.build b/block/meson.build
index 34b1b2a306..24ca0f1e52 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -114,6 +114,7 @@ foreach m : [
[libnfs, 'nfs', files('nfs.c')],
[libssh, 'ssh', files('ssh.c')],
[rbd, 'rbd', files('rbd.c')],
+ [vitastor, 'vitastor', files('vitastor.c')],
]
if m[0].found()
module_ss = ss.source_set()
diff --git a/meson.build b/meson.build
index d9293294d8..776a5becc6 100644
--- a/meson.build
+++ b/meson.build
@@ -1665,6 +1665,26 @@ if not get_option('rbd').auto() or have_block
endif
endif
+vitastor = not_found
+if not get_option('vitastor').auto() or have_block
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
+ required: get_option('vitastor'))
+ if libvitastor_client.found()
+ if cc.links('''
+ #include <vitastor_c.h>
+ int main(void) {
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ return 0;
+ }''', dependencies: libvitastor_client)
+ vitastor = declare_dependency(dependencies: libvitastor_client)
+ elif get_option('vitastor').enabled()
+ error('could not link libvitastor_client')
+ else
+ warning('could not link libvitastor_client, disabling')
+ endif
+ endif
+endif
+
glusterfs = not_found
glusterfs_ftruncate_has_stat = false
glusterfs_iocb_has_stat = false
@@ -2509,6 +2529,7 @@ endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
@@ -4948,6 +4969,7 @@ summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
+summary_info += {'vitastor support': vitastor}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
diff --git a/meson_options.txt b/meson_options.txt
index 2836156257..148086cc6f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -206,6 +206,8 @@ option('pvg', type: 'feature', value: 'auto',
description: 'macOS paravirtualized graphics support')
option('rbd', type : 'feature', value : 'auto',
description: 'Ceph block device driver')
+option('vitastor', type : 'feature', value : 'auto',
+ description: 'Vitastor block device driver')
option('opengl', type : 'feature', value : 'auto',
description: 'OpenGL support')
option('rdma', type : 'feature', value : 'auto',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b82af74256..f25a6f5ce8 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3351,7 +3351,7 @@
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
'raw', 'rbd',
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
- 'ssh', 'throttle', 'vdi', 'vhdx',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -4434,6 +4434,28 @@
'*key-secret': 'str',
'*server': ['InetSocketAddressBase'] } }
+##
+# @BlockdevOptionsVitastor:
+#
+# Driver specific block device options for vitastor
+#
+# @image: Image name
+# @inode: Inode number
+# @pool: Pool ID
+# @size: Desired image size in bytes
+# @config-path: Path to Vitastor configuration
+# @etcd-host: etcd connection address(es)
+# @etcd-prefix: etcd key/value prefix
+##
+{ 'struct': 'BlockdevOptionsVitastor',
+ 'data': { '*inode': 'uint64',
+ '*pool': 'uint64',
+ '*size': 'uint64',
+ '*image': 'str',
+ '*config-path': 'str',
+ '*etcd-host': 'str',
+ '*etcd-prefix': 'str' } }
+
##
# @ReplicationMode:
#
@@ -4902,6 +4924,7 @@
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vitastor': 'BlockdevOptionsVitastor',
'virtio-blk-vfio-pci':
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
'if': 'CONFIG_BLKIO' },
@@ -5376,6 +5399,20 @@
'*cluster-size' : 'size',
'*encrypt' : 'RbdEncryptionCreateOptions' } }
+##
+# @BlockdevCreateOptionsVitastor:
+#
+# Driver specific image creation options for Vitastor.
+#
+# @location: Where to store the new image file. This location cannot
+# point to a snapshot.
+#
+# @size: Size of the virtual disk in bytes
+##
+{ 'struct': 'BlockdevCreateOptionsVitastor',
+ 'data': { 'location': 'BlockdevOptionsVitastor',
+ 'size': 'size' } }
+
##
# @BlockdevVmdkSubformat:
#
@@ -5598,6 +5635,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 3d0d132344..65ee8c855e 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -177,6 +177,7 @@ meson_options_help() {
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
printf "%s\n" ' qpl Query Processing Library support'
printf "%s\n" ' rbd Ceph block device driver'
+ printf "%s\n" ' vitastor Vitastor block device driver'
printf "%s\n" ' rdma Enable RDMA-based migration'
printf "%s\n" ' replication replication support'
printf "%s\n" ' rust Rust support'
@@ -464,6 +465,8 @@ _meson_option_parse() {
--disable-qpl) printf "%s" -Dqpl=disabled ;;
--enable-rbd) printf "%s" -Drbd=enabled ;;
--disable-rbd) printf "%s" -Drbd=disabled ;;
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
--enable-rdma) printf "%s" -Drdma=enabled ;;
--disable-rdma) printf "%s" -Drdma=disabled ;;
--enable-relocatable) printf "%s" -Drelocatable=true ;;
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.6
Version: 3.0.12
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.6.el10.tar.gz
Source0: vitastor-3.0.12.el10.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.6
Version: 3.0.12
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.6.el7.tar.gz
Source0: vitastor-3.0.12.el7.tar.gz
BuildRequires: gperftools-devel
BuildRequires: devtoolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.6
Version: 3.0.12
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.6.el8.tar.gz
Source0: vitastor-3.0.12.el8.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.6
Version: 3.0.12
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.6.el9.tar.gz
Source0: vitastor-3.0.12.el9.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
+2 -3
View File
@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
include(GNUInstallDirs)
include(CTest)
include(CheckIncludeFile)
find_package(PkgConfig)
@@ -21,7 +20,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
endif()
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
add_definitions(-DVITASTOR_VERSION="3.0.6")
add_definitions(-DVITASTOR_VERSION="3.0.12")
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
add_link_options(-fno-omit-frame-pointer)
if (${WITH_ASAN})
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
+5
View File
@@ -228,4 +228,9 @@ public:
virtual uint64_t get_journal_size() = 0;
virtual uint32_t get_bitmap_granularity() = 0;
virtual uint64_t get_live_entries() = 0;
virtual uint64_t get_live_memory() = 0;
virtual uint64_t get_garbage_entries() = 0;
virtual uint64_t get_garbage_memory() = 0;
};
+3
View File
@@ -94,6 +94,9 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
csum_block_size = parse_size(config["csum_block_size"]);
discard_on_start = config.find("discard_on_start") != config.end() &&
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
gc_on_start = config.find("gc_on_start") == config.end() ||
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
skip_double_claim = (config["skip_double_claim"] == "true" || config["skip_double_claim"] == "1" || config["skip_double_claim"] == "yes");
min_discard_size = parse_size(config["min_discard_size"]);
if (!min_discard_size)
min_discard_size = 1024*1024;
+4
View File
@@ -57,6 +57,10 @@ struct blockstore_disk_t
bool inmemory_journal = true;
// Data discard granularity and minimum size (for the sake of performance)
bool discard_on_start = false;
// GC on start (new store)
bool gc_on_start = true;
// Skip double claim conflicts on start (new store, temporary until the bug is found)
bool skip_double_claim = false;
uint64_t min_discard_size = 1024*1024;
uint64_t discard_granularity = 0;
+112 -18
View File
@@ -174,14 +174,18 @@ bool journal_flusher_co::loop()
else if (wait_state == 19) goto resume_19;
else if (wait_state == 20) goto resume_20;
else if (wait_state == 21) goto resume_21;
else if (wait_state == 22) goto resume_22;
else if (wait_state == 23) goto resume_23;
else if (wait_state == 24) goto resume_24;
else if (wait_state == 25) goto resume_25;
resume_0:
wait_state = 0;
wait_count = 0;
cur_oid = {};
res = bs->heap->get_next_compact(cur_oid);
// Advance fsynced_lsn every <journal_trim_interval> intent writes
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0)
{
// Advance fsynced_lsn every <journal_trim_interval> intent writes
bs->intent_write_counter = 0;
resume_17:
resume_18:
@@ -196,6 +200,7 @@ resume_21:
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
{
// When under pressure, do an additional fsync to force entries to be marked compactable
flusher->active_flushers++;
resume_14:
resume_15:
@@ -259,11 +264,9 @@ resume_1:
if (wr->type() == BS_HEAP_SMALL_WRITE ||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
{
auto res = bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
? COPY_BUF_SKIP_CSUM : 0);
if (res > 0)
copy_count++;
}
});
if (!compact_info.compact_lsn)
@@ -273,6 +276,25 @@ resume_1:
bs->heap->unlock_entry(cur_oid);
goto resume_0;
}
flusher->active_flushers++;
for (i = 0; i < read_vec.size(); i++)
{
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
{
copy_count++;
}
}
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
init_fsync_data();
}
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
if (!bitmap_copied)
{
@@ -291,13 +313,6 @@ resume_1:
csum_copy.clear();
}
clean_loc = compact_info.clean_wr->big_location(bs->heap);
flusher->active_flushers++;
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
overwrite_start = overwrite_end = 0;
if (read_vec.size() > 0)
{
@@ -336,6 +351,13 @@ resume_3:
if (res == ENOENT || res == EDOM)
{
// Abort compaction
abort_compact:
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
cur_sync->member_count--;
if (cur_sync->member_count > 0)
bs->ringloop->wakeup();
}
flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--;
@@ -349,10 +371,7 @@ resume_4:
if (res == ENOENT)
{
// Abort compaction
flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--;
goto resume_0;
goto abort_compact;
}
if (res == EAGAIN)
{
@@ -381,14 +400,14 @@ resume_9:
for (i = 0; i < read_vec.size(); i++)
{
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
!(read_vec[i].copy_flags & COPY_BUF_COALESCED) ||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
{
assert(read_vec[i].buf);
await_sqe(10);
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
data->callback = simple_callback_w;
assert(clean_loc + read_vec[i].offset + data->iov.iov_len <= bs->dsk.block_count*bs->dsk.data_block_size);
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
wait_count++;
}
@@ -399,6 +418,17 @@ resume_11:
wait_state = 11;
return false;
}
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
resume_22:
resume_23:
resume_24:
resume_25:
if (!fsync_data(22))
{
return false;
}
}
// Lock is only needed to prevent freeing the big_write because we overwrite it...
bs->heap->unlock_entry(cur_oid);
// Mark the object compacted, but don't free and remove small_writes
@@ -408,12 +438,14 @@ resume_11:
if (!cur_obj)
{
// Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
if (!calc_block_checksums())
{
// Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
@@ -422,6 +454,7 @@ resume_11:
if (res == EBUSY)
{
// Abort compaction, object is already overwritten by something else
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
@@ -586,7 +619,7 @@ int journal_flusher_co::check_and_punch_checksums()
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
}
}
// Modified, we should add_punch_holes and then write the block to disk
// Modified, we should punch_holes and then write the block to disk
return EBUSY;
}
@@ -699,6 +732,67 @@ resume_1:
return true;
}
void journal_flusher_co::init_fsync_data()
{
cur_sync = flusher->data_syncs.begin();
if (cur_sync == flusher->data_syncs.end() || cur_sync->ready_count > 0)
{
cur_sync = flusher->data_syncs.emplace(cur_sync);
}
cur_sync->member_count++;
}
bool journal_flusher_co::fsync_data(int wait_base)
{
if (wait_state == wait_base)
goto resume_0;
else if (wait_state == wait_base+1)
goto resume_1;
else if (wait_state == wait_base+2)
goto resume_2;
else if (wait_state == wait_base+3)
goto resume_3;
cur_sync->ready_count++;
resume_0:
if (cur_sync->ready_count < cur_sync->member_count)
{
wait_state = wait_base;
return false;
}
if (!cur_sync->sent)
{
// Sync batch is ready. Do it.
await_sqe(1);
data->iov = { 0 };
data->callback = simple_callback_w;
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
cur_sync->sent = true;
wait_count++;
resume_2:
if (wait_count > 0)
{
wait_state = wait_base+2;
return false;
}
cur_sync->done = true;
// Wake up other flushers
bs->ringloop->wakeup();
}
resume_3:
if (!cur_sync->done)
{
wait_state = wait_base+3;
return false;
}
cur_sync->done_count++;
if (cur_sync->done_count >= cur_sync->member_count)
{
flusher->data_syncs.erase(cur_sync);
cur_sync = flusher->data_syncs.end();
}
return true;
}
bool journal_flusher_co::fsync_meta(int wait_base)
{
if (wait_state == wait_base) goto resume_0;
+13
View File
@@ -25,6 +25,15 @@ struct flusher_meta_write_t
std::map<uint64_t, meta_sector_t>::iterator it;
};
struct flusher_data_sync_t
{
int member_count = 0;
int ready_count = 0;
int done_count = 0;
bool sent = false;
bool done = false;
};
class journal_flusher_t;
// Journal flusher coroutine
@@ -58,6 +67,7 @@ class journal_flusher_co
int i, res;
bool read_to_fill_incomplete;
int copy_count;
std::list<flusher_data_sync_t>::iterator cur_sync;
friend class journal_flusher_t;
@@ -68,6 +78,8 @@ class journal_flusher_co
bool calc_block_checksums();
bool write_meta_block(int wait_base);
bool read_buffered(int wait_base);
void init_fsync_data();
bool fsync_data(int wait_base);
bool fsync_meta(int wait_base);
bool fsync_buffer(int wait_base);
bool trim_lsn(int wait_base);
@@ -88,6 +100,7 @@ class journal_flusher_t
robin_hood::unordered_flat_set<object_id> flushing;
int active_flushers = 0;
std::list<flusher_data_sync_t> data_syncs;
int wanting_meta_fsync = 0;
bool fsyncing_meta = false;
int syncing_buffer = 0;
File diff suppressed because it is too large Load Diff
+41 -9
View File
@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
bool is_garbage();
bool is_garbage() const;
void set_garbage();
bool is_overwrite();
bool is_compactable();
bool is_before(heap_entry_t *other);
bool is_overwrite() const;
bool is_compactable() const;
bool is_before(const heap_entry_t *other) const;
uint32_t get_size(blockstore_heap_t *heap);
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
@@ -117,10 +117,13 @@ struct heap_object_mvcc_t
struct heap_block_info_t
{
uint32_t used_space = 0;
struct __attribute__((__packed__))
{
uint32_t used_space = 0;
uint32_t garbage_space = 0;
};
uint64_t mod_lsn = 0, mod_lsn_to = 0; // only 1 block write of LSN sequence is allowed at a moment
bool is_writing: 1;
bool has_garbage: 1;
bool is_writing = false;
std::vector<heap_list_item_t*> entries;
};
@@ -155,6 +158,16 @@ struct heap_li_equal
}
};
struct heap_recheck_state_t
{
heap_entry_t *obj = NULL;
heap_entry_t *next_wr = NULL;
size_t total_reads = 0;
size_t sent_reads = 0;
size_t checked_reads = 0;
heap_entry_t *bad_wr = NULL;
};
using i64hash_t = robin_hood::hash<uint64_t>;
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
@@ -184,6 +197,11 @@ class blockstore_heap_t
uint64_t buffer_area_used_space = 0;
uint64_t data_used_space = 0;
uint64_t live_entries = 0;
uint64_t live_memory = 0;
uint64_t garbage_entries = 0;
uint64_t garbage_memory = 0;
uint64_t next_lsn = 0;
uint32_t last_allocated_block = UINT32_MAX;
heap_mvcc_map_t object_mvcc;
@@ -200,9 +218,11 @@ class blockstore_heap_t
bool marked_used_blocks = false;
bool recheck_queue_filled = false;
std::vector<heap_list_item_t*> loaded_list_items;
std::vector<heap_list_item_t*> postponed_items;
std::set<uint32_t> recheck_modified_blocks;
std::deque<heap_entry_t*> recheck_queue;
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
size_t recheck_pending_reads = 0;
int recheck_in_progress = 0;
bool in_recheck = false;
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
@@ -211,14 +231,22 @@ class blockstore_heap_t
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
bool validate_object(heap_entry_t *obj);
void fill_recheck_queue();
void recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr);
void recheck_start_reads(heap_recheck_state_t *st);
int mark_used_blocks();
void init_free_bad_entry(heap_entry_t *wr);
void init_erase_bad_entry(heap_list_item_t *li);
bool init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li);
void recheck_full_gc();
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
void defragment_block(uint32_t block_num);
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
void gc_block(heap_block_info_t & inf);
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free);
void insert_list_item(heap_list_item_t *li);
void insert_list_items(heap_list_item_t** v, size_t count, bool postpone);
void remove_list_item(heap_list_item_t *li);
void unlink_list_item(heap_list_item_t *li);
int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free,
bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
@@ -345,6 +373,10 @@ public:
uint32_t get_compact_queue_size();
uint32_t get_to_compact_count();
uint64_t get_compacted_count();
uint64_t get_live_entries();
uint64_t get_live_memory();
uint64_t get_garbage_entries();
uint64_t get_garbage_memory();
uint64_t entry_pos(uint32_t block_num, uint32_t offset);
heap_entry_t *entry_from_pos(uint64_t entry_pos, bool allow_unallocated = false);
+8 -3
View File
@@ -101,6 +101,7 @@ void blockstore_impl_t::loop()
unsigned initial_ring_space = ringloop->space_left();
int op_idx = 0, new_idx = 0;
bool has_unfinished_writes = false;
bool has_unfinished_sync = false;
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
{
auto op = submit_queue[op_idx];
@@ -138,7 +139,13 @@ void blockstore_impl_t::loop()
else if (op->opcode == BS_OP_SYNC)
{
// syncs only completed writes, so doesn't have to be blocked by anything
wr_st = continue_sync(op);
if (!has_unfinished_sync)
{
wr_st = continue_sync(op);
has_unfinished_sync = (wr_st != 2);
}
else
wr_st = 0;
}
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
{
@@ -154,9 +161,7 @@ void blockstore_impl_t::loop()
wr_st = 2;
}
else
{
wr_st = 0;
}
}
if (wr_st == 2)
{
+5
View File
@@ -229,4 +229,9 @@ public:
uint64_t get_free_block_count();
inline uint32_t get_bitmap_granularity() { return dsk.bitmap_granularity; }
inline uint64_t get_journal_size() { return dsk.journal_len; }
inline uint64_t get_live_entries() { return heap->get_live_entries(); }
inline uint64_t get_live_memory() { return heap->get_live_memory(); }
inline uint64_t get_garbage_entries() { return heap->get_garbage_entries(); }
inline uint64_t get_garbage_memory() { return heap->get_garbage_memory(); }
};
+35 -18
View File
@@ -153,6 +153,14 @@ resume_1:
);
exit(1);
}
uint32_t csum = hdr->header_csum;
hdr->header_csum = 0;
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
{
printf("Metadata header is corrupt (checksum mismatch).\n");
exit(1);
}
hdr->header_csum = csum;
}
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
if (bs->dsk.inmemory_journal)
@@ -240,23 +248,7 @@ resume_4:
}
// metadata read finished
bs->heap->finish_load();
printf("Metadata entries loaded: %ju, used blocks: %ju / %ju\n", entries_loaded, bs->heap->get_data_used_space() / bs->dsk.data_block_size, bs->dsk.block_count);
if (zero_on_init && !bs->dsk.disable_meta_fsync)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
// asynchronous recheck
resume_6:
wait_state = 6;
@@ -293,6 +285,11 @@ resume_7:
if (bs->readonly)
{
recheck_mod.clear();
printf("Actual metadata entries: %ju\n", bs->heap->get_live_entries());
}
else
{
printf("Actual metadata entries: %ju, clearing garbage in %zu metadata blocks\n", bs->heap->get_live_entries(), recheck_mod.size());
}
for (i = 0; i < recheck_mod.size(); i++)
{
@@ -306,7 +303,7 @@ resume_8:
uint32_t block_num = recheck_mod[i];
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data)
@@ -332,5 +329,25 @@ resume_9:
}
free(metadata_buffer);
metadata_buffer = NULL;
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
format_size(bs->heap->get_data_used_space()).c_str(),
format_size(bs->dsk.block_count * bs->dsk.data_block_size).c_str());
return 0;
}
+4
View File
@@ -462,6 +462,10 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
{
if (target_version >= wr->version)
{
if (wr->type() == BS_HEAP_DELETE)
{
return false;
}
found = true;
if (result_version)
{
+7
View File
@@ -16,6 +16,7 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
else if (priv->op_state == 5) goto resume_5;
assert(!priv->op_state);
op->retval = 0;
PRIV(op)->lsn = 0;
priv->modified_block = priv->modified_block2 = UINT32_MAX;
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
{
@@ -36,6 +37,12 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
FINISH_OP(op);
return 2;
}
if (res == ENOENT)
{
op->retval = -ENOENT;
FINISH_OP(op);
return 2;
}
if (res == ENOSPC)
{
if (!heap->get_to_compact_count())
+4 -2
View File
@@ -9,6 +9,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
if (!PRIV(op)->op_state)
{
op->retval = 0;
PRIV(op)->lsn = 0;
}
int res = do_sync(op, 0);
if (res == 2)
@@ -104,7 +105,8 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
return 2;
}
PRIV(op)->modified_block = heap->get_completed_lsn();
assert(!PRIV(op)->lsn);
PRIV(op)->lsn = heap->get_completed_lsn();
if (!submit_fsyncs(PRIV(op)->pending_ops))
{
PRIV(op)->wait_detail = 1;
@@ -118,6 +120,6 @@ resume_1:
return 1;
}
resume_2:
heap->mark_lsn_fsynced(PRIV(op)->modified_block);
heap->mark_lsn_fsynced(PRIV(op)->lsn);
return 2;
}
+4
View File
@@ -37,6 +37,7 @@ void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
heap->complete_block_write(modified_block);
ringloop->wakeup();
};
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
io_uring_prep_writev(
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + ((uint64_t)modified_block+1)*dsk.meta_block_size
);
@@ -177,6 +178,7 @@ enospc:
ring_data_t *data = ((ring_data_t*)sqe->user_data);
data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(loc+op->offset+op->len <= dsk.block_count*dsk.data_block_size);
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + loc + op->offset);
PRIV(op)->pending_ops++;
write_iodepth++;
@@ -264,6 +266,7 @@ enospc:
BS_SUBMIT_GET_SQE(sqe2, data2);
data2->iov = (struct iovec){ op->buf, op->len };
data2->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(loc+op->len <= dsk.journal_len);
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, dsk.journal_offset + loc);
PRIV(op)->pending_ops++;
}
@@ -453,6 +456,7 @@ resume_10:
BS_SUBMIT_GET_SQE(sqe, data);
data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(PRIV(op)->location + op->offset <= dsk.block_count*dsk.data_block_size);
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + PRIV(op)->location + op->offset);
if (dsk.use_atomic_flag)
sqe->rw_flags = RWF_ATOMIC;
+1 -1
View File
@@ -141,7 +141,7 @@ struct __attribute__((__packed__)) journal_entry
inline uint32_t je_crc32(journal_entry *je)
{
// 0x48674bc7 = crc32(4 zero bytes)
return crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
return je->size < 4 ? 0 : crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
}
// "VITAstor"
+2
View File
@@ -520,6 +520,7 @@ resume_2:
await_sqe(15);
data->iov = (struct iovec){ it->buf, (size_t)it->len };
data->callback = simple_callback_w;
assert(clean_loc+it->offset+it->len <= bs->dsk.block_count*bs->dsk.data_block_size);
io_uring_prep_writev(
sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + it->offset
);
@@ -749,6 +750,7 @@ bool journal_flusher_co::write_meta_block(flusher_meta_write_t & meta_block, int
await_sqe(0);
data->iov = (struct iovec){ meta_block.buf, (size_t)bs->dsk.meta_block_size };
data->callback = simple_callback_w;
assert(bs->dsk.meta_block_size + meta_block.sector + bs->dsk.meta_block_size <= bs->dsk.meta_area_size);
io_uring_prep_writev(
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_block.sector
);
+25
View File
@@ -855,4 +855,29 @@ std::string blockstore_impl_t::get_op_diag(blockstore_op_t *op)
return std::string(buf);
}
uint64_t blockstore_impl_t::get_live_entries()
{
return used_blocks;
}
uint64_t blockstore_impl_t::get_live_memory()
{
uint64_t used = 0;
for (auto & kv: clean_db_shards)
{
used += kv.second.size() * sizeof(blockstore_clean_db_t::value_type);
}
return used;
}
uint64_t blockstore_impl_t::get_garbage_entries()
{
return dirty_db.size();
}
uint64_t blockstore_impl_t::get_garbage_memory()
{
return (sizeof(obj_ver_id) + sizeof(dirty_entry) + 32) * dirty_db.size();
}
} // namespace v1
+4
View File
@@ -332,6 +332,10 @@ public:
inline uint64_t get_free_block_count() { return dsk.block_count - used_blocks; }
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
inline uint64_t get_journal_size() { return dsk.journal_len; }
uint64_t get_live_entries();
uint64_t get_live_memory();
uint64_t get_garbage_entries();
uint64_t get_garbage_memory();
};
} // namespace v1
+1
View File
@@ -193,6 +193,7 @@ void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_
(size_t)journal.block_size
};
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
assert(journal.sector_info[cur_sector].offset+journal.block_size <= dsk.journal_len);
io_uring_prep_writev(
sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
);
+5 -6
View File
@@ -368,9 +368,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
}
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
io_uring_prep_writev(
sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + (loc * dsk.data_block_size) + op->offset - stripe_offset
);
const uint64_t write_offset = (loc * dsk.data_block_size) + op->offset - stripe_offset;
assert(write_offset+op->len+stripe_offset+stripe_end <= dsk.block_count*dsk.data_block_size);
io_uring_prep_writev(sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + write_offset);
PRIV(op)->pending_ops = 1;
if (!(dirty_it->second.state & BS_ST_INSTANT))
{
@@ -495,9 +495,8 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
.op = op,
});
data2->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
io_uring_prep_writev(
sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free
);
assert(journal.next_free+op->len <= dsk.journal_len);
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free);
PRIV(op)->pending_ops++;
}
else
+3 -5
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
@@ -12,7 +12,7 @@ if (RDMACM_LIBRARIES)
set(MSGR_RDMACM "msgr_rdmacm.cpp")
endif (RDMACM_LIBRARIES)
add_library(vitastor_common STATIC
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
)
@@ -52,9 +52,6 @@ if (${WITH_FIO})
../util/rw_blocking.cpp
../util/addr_util.cpp
)
target_link_libraries(fio_vitastor_sec
tcmalloc_minimal
)
endif (${WITH_FIO})
# vitastor-nbd
@@ -101,6 +98,7 @@ add_executable(test_cluster_client
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp
)
target_link_libraries(test_cluster_client ${LIBURING_LIBRARIES})
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
add_dependencies(build_tests test_cluster_client)
+32 -32
View File
@@ -27,7 +27,7 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
msgr.ringloop = ringloop;
msgr.repeer_pgs = [this](osd_num_t peer_osd)
{
if (msgr.osd_peer_fds.find(peer_osd) != msgr.osd_peer_fds.end())
if (msgr.osd_peers.find(peer_osd) != msgr.osd_peers.end())
{
// peer_osd just connected
continue_ops();
@@ -47,8 +47,8 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
msgr.exec_op = [this](osd_op_t *op)
{
// Garbage in
fprintf(stderr, "Incoming garbage from peer %d\n", op->peer_fd);
msgr.stop_client(op->peer_fd);
fprintf(stderr, "Can't handle incoming operation from client %lu\n", op->client_id);
msgr.stop_client(op->client_id);
delete op;
};
msgr.parse_config(config);
@@ -156,7 +156,7 @@ void cluster_client_t::continue_raw_ops(osd_num_t peer_osd)
{
auto op = it->second;
op->op_type = OSD_OP_OUT;
op->peer_fd = msgr.osd_peer_fds.at(peer_osd);
op->client_id = msgr.osd_peers.at(peer_osd)->client_id;
msgr.outbox_push(op);
raw_ops.erase(it++);
}
@@ -590,7 +590,7 @@ void cluster_client_t::on_change_pool_config_hook()
{
if (log_level > 2 && pg_counts[pool_item.first])
{
printf("Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
fprintf(stderr, "Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
pg_counts[pool_item.first], pool_item.second.real_pg_count);
}
// At this point, all pool operations should have been suspended
@@ -871,13 +871,13 @@ void cluster_client_t::execute_cas(cluster_op_t *op)
if (op->retval != expected && op->retval >= 0)
op->retval = -EIO;
op->retval = op->retval == -EPIPE ? -EINTR : op->retval;
auto peer_it = msgr.osd_peer_fds.find(op->parts[0].osd_num);
auto peer_it = msgr.osd_peers.find(op->parts[0].osd_num);
if (op->retval != 0 || (op->flags & OP_IMMEDIATE_COMMIT))
{
auto cb = std::move(op->callback);
cb(op);
}
else if (peer_it == msgr.osd_peer_fds.end())
else if (peer_it == msgr.osd_peers.end())
{
// Care must be taken to make sure that the client doesn't reconnect to the OSD
// before executing the previously completed operation callback (!)
@@ -888,10 +888,10 @@ void cluster_client_t::execute_cas(cluster_op_t *op)
else
{
// CAS writes have a built-in sync
auto peer_fd = peer_it->second;
osd_client_t *cl = peer_it->second;
*part = (osd_op_t){
.op_type = OSD_OP_OUT,
.peer_fd = peer_fd,
.client_id = cl->client_id,
.req = {
.hdr = {
.magic = SECONDARY_OSD_OP_MAGIC,
@@ -1004,11 +1004,11 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
void cluster_client_t::execute_raw(osd_num_t osd_num, osd_op_t *op)
{
auto fd_it = msgr.osd_peer_fds.find(osd_num);
if (fd_it != msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(osd_num);
if (peer_it != msgr.osd_peers.end())
{
op->op_type = OSD_OP_OUT;
op->peer_fd = fd_it->second;
op->client_id = peer_it->second->client_id;
msgr.outbox_push(op);
}
else
@@ -1119,6 +1119,13 @@ resume_2:
// Finished successfully
// Even if the PG count has changed in meanwhile we treat it as success
// because if some operations were invalid for the new PG count we'd get errors
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
// Copy part bitmaps only after finishing all part reads
for (auto & part: op->parts)
if ((part.flags & (PART_SENT|PART_DONE|PART_VALID)) == (PART_SENT|PART_DONE|PART_VALID))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
// Check parent inode
@@ -1164,7 +1171,7 @@ resume_2:
erase_op(op);
return 1;
}
else if (op->retval != 0 && !(op->flags & OP_FLUSH_BUFFER) &&
else if (op->retval != 0 && op->opcode != OSD_OP_SYNC && !(op->flags & OP_FLUSH_BUFFER) &&
op->retval != -EPIPE && (op->retval != -EIO || !client_eio_retry_interval) && (op->retval != -ENOSPC || !client_retry_enospc))
{
// Fatal error (neither -EPIPE, -EIO nor -ENOSPC)
@@ -1401,10 +1408,10 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
primary_osd = nearest_osd;
}
part->osd_num = primary_osd;
auto peer_it = msgr.osd_peer_fds.find(primary_osd);
if (peer_it != msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(primary_osd);
if (peer_it != msgr.osd_peers.end())
{
int peer_fd = peer_it->second;
osd_client_t *cl = peer_it->second;
part->flags |= PART_SENT|PART_VALID;
op->inflight_count++;
uint64_t pg_bitmap_size = (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8) * (
@@ -1419,7 +1426,7 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
}
part->op = (osd_op_t){
.op_type = OSD_OP_OUT,
.peer_fd = peer_fd,
.client_id = cl->client_id,
.req = { .rw = {
.header = {
.magic = SECONDARY_OSD_OP_MAGIC,
@@ -1468,8 +1475,8 @@ int cluster_client_t::continue_sync(cluster_op_t *op)
for (auto do_it = dirty_osds.begin(); do_it != dirty_osds.end(); )
{
osd_num_t sync_osd = *do_it;
auto peer_it = msgr.osd_peer_fds.find(sync_osd);
if (peer_it == msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(sync_osd);
if (peer_it == msgr.osd_peers.end())
dirty_osds.erase(do_it++);
else
do_it++;
@@ -1522,12 +1529,12 @@ resume_1:
void cluster_client_t::send_sync(cluster_op_t *op, cluster_op_part_t *part)
{
auto peer_fd = msgr.osd_peer_fds.at(part->osd_num);
osd_client_t *cl = msgr.osd_peers.at(part->osd_num);
part->flags |= PART_SENT;
op->inflight_count++;
part->op = (osd_op_t){
.op_type = OSD_OP_OUT,
.peer_fd = peer_fd,
.client_id = cl->client_id,
.req = {
.hdr = {
.magic = SECONDARY_OSD_OP_MAGIC,
@@ -1567,10 +1574,10 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
// Error priority: EIO > ENOSPC > ETIMEDOUT > EPIPE
op->retval = part->op.reply.hdr.retval;
}
int stop_fd = -1;
uint64_t stop_client_id = 0;
if (op->retval != -EINTR && op->retval != -EIO && op->retval != -ENOSPC)
{
stop_fd = part->op.peer_fd;
stop_client_id = part->op.client_id;
if (op->retval != -EPIPE || log_level > 0)
{
fprintf(
@@ -1597,9 +1604,9 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
op->retry_after = op->retval != -EPIPE ? client_eio_retry_interval : client_retry_interval;
}
reset_retry_timer(op->retry_after);
if (stop_fd >= 0)
if (stop_client_id)
{
msgr.stop_client(stop_fd);
msgr.stop_client(stop_client_id);
}
op->inflight_count--;
if (op->inflight_count == 0 && !op->retry_after)
@@ -1630,13 +1637,6 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
}
if (op->inflight_count == 0 && !op->retry_after)
{
// Copy part bitmaps only after finishing all part reads
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
for (auto & part: op->parts)
if (part.flags == (PART_SENT|PART_VALID|PART_DONE))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_SYNC)
continue_sync(op);
else
+1 -9
View File
@@ -83,9 +83,6 @@ class writeback_cache_t;
// FIXME: Split into public and private interfaces
class __attribute__((visibility("default"))) cluster_client_t
{
#ifdef __MOCK__
public:
#endif
timerfd_manager_t *tfd = NULL;
ring_loop_t *ringloop = NULL;
@@ -155,15 +152,9 @@ public:
void list_inode(inode_t inode, uint64_t min_offset, uint64_t max_offset, int max_parallel_pgs, std::function<void(
int status, int pgs_left, pg_num_t pg_num, std::set<object_id>&& objects)> pg_callback);
//inline uint32_t get_bs_bitmap_granularity() { return st_cli.global_bitmap_granularity; }
//inline uint64_t get_bs_block_size() { return st_cli.global_block_size; }
#ifndef __MOCK__
protected:
#endif
void continue_ops(int time_passed = 0);
protected:
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
@@ -204,4 +195,5 @@ protected:
osd_num_t select_nearest_osd(const std::vector<osd_num_t> & osds);
friend class writeback_cache_t;
friend class cluster_client_test_t;
};
+2 -2
View File
@@ -295,7 +295,7 @@ int cluster_client_t::start_pg_listing(inode_list_pg_t *pg)
bool conn = true;
for (osd_num_t peer_osd: all_peers)
{
if (msgr.osd_peer_fds.find(peer_osd) == msgr.osd_peer_fds.end())
if (msgr.osd_peers.find(peer_osd) == msgr.osd_peers.end())
{
// Initiate connection
if (st_cli.peer_states[peer_osd].is_null())
@@ -340,7 +340,7 @@ void cluster_client_t::send_list(inode_list_osd_t *cur_list)
osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT;
// Already checked that it exists above, but anyway
op->peer_fd = msgr.osd_peer_fds.at(cur_list->osd_num);
op->client_id = msgr.osd_peers.at(cur_list->osd_num)->client_id;
op->req = (osd_any_op_t){
.sec_list = {
.header = {
+9 -3
View File
@@ -88,6 +88,11 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
// ...or just save it for writeback if write buffering is enabled
if (op->len == 0)
{
// FIXME: OSD_OP_DELETEs are currently only sent by vitastor-cli rm/rm-data and
// actually have len=0, because delete is actually a delete of the full object
// containing the requested offset, not a "punch hole" operation. But here, writeback
// cache assumes it IS a "punch hole" operation. I should select one of these
// approaches and fix everything accordingly when I decide to implement TRIM.
return;
}
auto dirty_it = find_dirty(op->inode, op->offset);
@@ -244,12 +249,13 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
writeback_queue_size--;
}
}
if (!is_del)
if (!is_del && op->len > 0)
{
uint64_t pos = 0, len = op->len, iov_idx = 0;
while (len > 0 && iov_idx < op->iov.count)
while (iov_idx < op->iov.count)
{
auto & iov = op->iov.buf[iov_idx];
assert(pos + iov.iov_len <= len);
memcpy(buf + pos, iov.iov_base, iov.iov_len);
pos += iov.iov_len;
iov_idx++;
@@ -443,7 +449,7 @@ void writeback_cache_t::start_writebacks(cluster_client_t *cli, int count)
started++;
assert(writeback_queue_size > 0);
writeback_queue_size--;
writeback_bytes -= off - from_it->first.stripe;
writeback_bytes -= (is_del ? 0 : off - from_it->first.stripe);
assert(writeback_queue_size > 0 || !writeback_bytes);
flush_buffers(cli, from_it, to_it);
}
-2
View File
@@ -1185,7 +1185,6 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
if (i >= pg_state_bit_count)
{
fprintf(stderr, "Unexpected pool %u PG %u state keyword in etcd: %s\n", pool_id, pg_num, e.dump().c_str());
return;
}
}
if (!cur_primary || !value["state"].is_array() || !state ||
@@ -1194,7 +1193,6 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
(state & PG_INCOMPLETE) && state != PG_INCOMPLETE && state != (PG_INCOMPLETE|PG_HAS_INVALID))
{
fprintf(stderr, "Unexpected pool %u PG %u state in etcd: primary=%ju, state=%s\n", pool_id, pg_num, cur_primary, value["state"].dump().c_str());
return;
}
pg_cfg.cur_primary = cur_primary;
pg_cfg.cur_state = state;
+70 -168
View File
@@ -15,106 +15,6 @@
#include "msgr_rdma.h"
#endif
#include <sys/poll.h>
msgr_iothread_t::msgr_iothread_t():
ring(RINGLOOP_DEFAULT_SIZE, true),
thread(&msgr_iothread_t::run, this)
{
eventfd = ring.register_eventfd();
if (eventfd < 0)
{
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
}
}
msgr_iothread_t::~msgr_iothread_t()
{
stop();
}
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
{
mu.lock();
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
if (queue.size() == 1)
{
cond.notify_all();
}
mu.unlock();
}
void msgr_iothread_t::stop()
{
mu.lock();
if (stopped)
{
mu.unlock();
return;
}
stopped = true;
if (outer_loop_data)
{
outer_loop_data->callback = [](ring_data_t*){};
}
cond.notify_all();
close(eventfd);
mu.unlock();
thread.join();
}
void msgr_iothread_t::add_to_ringloop(ring_loop_t *outer_loop)
{
assert(!this->outer_loop || this->outer_loop == outer_loop);
io_uring_sqe *sqe = outer_loop->get_sqe();
assert(sqe != NULL);
this->outer_loop = outer_loop;
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
outer_loop_data->callback = [this](ring_data_t *data)
{
if (data->res < 0)
{
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
}
outer_loop_data = NULL;
if (stopped)
{
return;
}
add_to_ringloop(this->outer_loop);
ring.loop();
};
}
void msgr_iothread_t::run()
{
while (true)
{
{
std::unique_lock<std::mutex> lk(mu);
while (!stopped && !queue.size())
cond.wait(lk);
if (stopped)
return;
int i = 0;
for (; i < queue.size(); i++)
{
io_uring_sqe *sqe = ring.get_sqe();
if (!sqe)
break;
ring_data_t *data = ((ring_data_t*)sqe->user_data);
*data = std::move(queue[i].data);
*sqe = queue[i].sqe;
sqe->user_data = (uint64_t)data;
}
queue.erase(queue.begin(), queue.begin()+i);
}
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
ring.submit();
}
}
void osd_messenger_t::init()
{
#ifdef WITH_RDMACM
@@ -173,21 +73,17 @@ void osd_messenger_t::init()
}
if (ringloop && iothread_count > 0)
{
for (int i = 0; i < iothread_count; i++)
{
auto iot = new msgr_iothread_t();
iothreads.push_back(iot);
iot->add_to_ringloop(ringloop);
}
init_iothreads();
}
keepalive_timer_id = tfd->set_timer(1000, true, [this](int)
{
std::vector<uint64_t> clients_to_stop;
std::vector<osd_op_t*> ops_to_send;
auto cl_it = clients.begin();
while (cl_it != clients.end())
{
auto cl = cl_it->second;
cl_it++;
auto peer_fd = cl->peer_fd;
if (!cl->osd_num && !cl->in_osd_num || cl->peer_state != PEER_CONNECTED && cl->peer_state != PEER_RDMA)
{
// Do not run keepalive on regular clients
@@ -199,10 +95,9 @@ void osd_messenger_t::init()
if (!cl->ping_time_remaining)
{
// Ping timed out, stop the client
fprintf(stderr, "Ping timed out for OSD %ju (client %d), disconnecting peer\n", cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->peer_fd);
stop_client(peer_fd, true);
// Restart iterator because it may be invalidated
cl_it = clients.upper_bound(peer_fd);
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n",
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
clients_to_stop.push_back(cl->client_id);
}
}
else if (cl->idle_time_remaining > 0)
@@ -213,37 +108,36 @@ void osd_messenger_t::init()
// Connection is idle for <osd_idle_time>, send ping
osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT;
op->peer_fd = cl->peer_fd;
op->client_id = cl->client_id;
op->req = (osd_any_op_t){
.hdr = {
.magic = SECONDARY_OSD_OP_MAGIC,
.opcode = OSD_OP_PING,
},
};
op->callback = [this, cl](osd_op_t *op)
op->callback = [this](osd_op_t *op)
{
auto cl_it = clients.find(op->peer_fd);
if (cl_it == clients.end() || cl_it->second != cl)
auto cl_it = clients.find(op->client_id);
if (cl_it == clients.end())
{
// client is already dropped
delete op;
return;
}
int fail_fd = (op->reply.hdr.retval != 0 ? op->peer_fd : -1);
auto cl = cl_it->second;
uint64_t fail_client_id = (op->reply.hdr.retval != 0 ? op->client_id : 0);
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
cl->ping_time_remaining = 0;
delete op;
if (fail_fd >= 0)
if (fail_client_id)
{
fprintf(stderr, "Ping failed for OSD %ju (client %d), disconnecting peer\n", fail_osd_num, fail_fd);
stop_client(fail_fd, true);
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, fail_client_id);
stop_client(fail_client_id);
}
};
cl->ping_time_remaining = osd_ping_timeout;
cl->idle_time_remaining = osd_idle_timeout;
outbox_push(op);
// Restart iterator because it may be invalidated
cl_it = clients.upper_bound(peer_fd);
ops_to_send.push_back(op);
}
}
else
@@ -251,6 +145,14 @@ void osd_messenger_t::init()
cl->idle_time_remaining = osd_idle_timeout;
}
}
for (uint64_t client_id: clients_to_stop)
{
stop_client(client_id);
}
for (osd_op_t *op: ops_to_send)
{
outbox_push(op);
}
});
}
@@ -263,16 +165,9 @@ osd_messenger_t::~osd_messenger_t()
}
while (clients.size() > 0)
{
stop_client(clients.begin()->first, true, true);
}
if (iothreads.size())
{
for (auto iot: iothreads)
{
delete iot;
}
iothreads.clear();
stop_client(clients.begin()->first, true);
}
destroy_iothreads();
#ifdef WITH_RDMA
for (auto rdma_context: rdma_contexts)
{
@@ -440,7 +335,7 @@ void osd_messenger_t::try_connect_peer(uint64_t peer_osd)
{
return;
}
if (osd_peer_fds.find(peer_osd) != osd_peer_fds.end())
if (osd_peers.find(peer_osd) != osd_peers.end())
{
wanted_peers.erase(peer_osd);
return;
@@ -467,20 +362,20 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
#ifdef WITH_RDMACM
if (disable_tcp)
{
on_connect_peer(peer_osd, -EINVAL);
on_connect_peer(peer_osd, -EINVAL, 0);
return;
}
#endif
struct sockaddr_storage addr;
if (!string_to_addr(peer_host, 0, peer_port, &addr))
{
on_connect_peer(peer_osd, -EINVAL);
on_connect_peer(peer_osd, -EINVAL, 0);
return;
}
int peer_fd = socket(addr.ss_family, SOCK_STREAM, 0);
if (peer_fd < 0)
{
on_connect_peer(peer_osd, -errno);
on_connect_peer(peer_osd, -errno, 0);
return;
}
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
@@ -488,21 +383,25 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
if (r < 0 && errno != EINPROGRESS)
{
close(peer_fd);
on_connect_peer(peer_osd, -errno);
on_connect_peer(peer_osd, -errno, 0);
return;
}
clients[peer_fd] = new osd_client_t();
const uint64_t client_id = next_client_id++;
osd_client_t *cl = new osd_client_t();
if (log_level > 0)
{
fprintf(stderr, "Connecting to OSD %ju at %s:%d (client %d)\n", peer_osd, peer_host, peer_port, peer_fd);
fprintf(stderr, "Connecting to OSD %ju at %s:%d (client %ju, FD %d)\n", peer_osd, peer_host, peer_port, client_id, peer_fd);
}
clients[peer_fd]->peer_addr = addr;
clients[peer_fd]->peer_port = peer_port;
clients[peer_fd]->peer_fd = peer_fd;
clients[peer_fd]->peer_state = PEER_CONNECTING;
clients[peer_fd]->connect_timeout_id = -1;
clients[peer_fd]->osd_num = peer_osd;
clients[peer_fd]->in_buf = malloc_or_die(receive_buffer_size);
cl->client_id = client_id;
cl->peer_addr = addr;
cl->peer_port = peer_port;
cl->peer_fd = peer_fd;
cl->peer_state = PEER_CONNECTING;
cl->connect_timeout_id = -1;
cl->osd_num = peer_osd;
cl->in_buf = malloc_or_die(receive_buffer_size);
clients[client_id] = cl;
clients_by_fd[peer_fd] = cl;
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
{
// Either OUT (connected) or HUP
@@ -510,11 +409,11 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
});
if (peer_connect_timeout > 0)
{
clients[peer_fd]->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, peer_fd](int timer_id)
cl->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, client_id](int timer_id)
{
osd_num_t peer_osd = clients.at(peer_fd)->osd_num;
stop_client(peer_fd, true);
on_connect_peer(peer_osd, -EPIPE);
osd_num_t peer_osd = clients.at(client_id)->osd_num;
stop_client(client_id);
on_connect_peer(peer_osd, -EPIPE, 0);
return;
});
}
@@ -522,7 +421,7 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
void osd_messenger_t::handle_connect_epoll(int peer_fd)
{
auto cl = clients[peer_fd];
auto cl = clients_by_fd.at(peer_fd);
if (cl->connect_timeout_id >= 0)
{
tfd->clear_timer(cl->connect_timeout_id);
@@ -537,8 +436,8 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
}
if (result != 0)
{
stop_client(peer_fd, true);
on_connect_peer(peer_osd, -result);
stop_client(cl->client_id);
on_connect_peer(peer_osd, -result, 0);
return;
}
int one = 1;
@@ -555,23 +454,23 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
{
// Mark client as ready (i.e. some data is available)
auto cl = clients_by_fd.at(peer_fd);
if (epoll_events & EPOLLRDHUP)
{
// Stop client
if (log_level > 0)
{
fprintf(stderr, "[OSD %ju] client %d disconnected\n", this->osd_num, peer_fd);
fprintf(stderr, "[OSD %ju] client %ju disconnected\n", this->osd_num, cl->client_id);
}
stop_client(peer_fd, true);
stop_client(cl->client_id);
}
else if (epoll_events & EPOLLIN)
{
// Mark client as ready (i.e. some data is available)
auto cl = clients[peer_fd];
cl->read_ready++;
if (cl->read_ready == 1)
{
read_ready_clients.push_back(cl->peer_fd);
read_ready_clients.push_back(cl->client_id);
if (ringloop)
ringloop->wakeup();
else
@@ -580,13 +479,13 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
}
}
void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int peer_fd)
void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t client_id)
{
auto & wp = wanted_peers.at(peer_osd);
wp.connecting = false;
if (peer_fd < 0)
if (errcode < 0)
{
fprintf(stderr, "Failed to connect to peer OSD %ju address %s port %d: %s\n", peer_osd, wp.cur_addr.c_str(), wp.cur_port, strerror(-peer_fd));
fprintf(stderr, "Failed to connect to peer OSD %ju address %s port %d: %s\n", peer_osd, wp.cur_addr.c_str(), wp.cur_port, strerror(-errcode));
if (wp.address_changed)
{
wp.address_changed = false;
@@ -613,7 +512,7 @@ void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int peer_fd)
}
if (log_level > 0)
{
fprintf(stderr, "[OSD %ju] Connected with peer OSD %ju (client %d)\n", osd_num, peer_osd, peer_fd);
fprintf(stderr, "[OSD %ju] Connected with peer OSD %ju (client %ju)\n", osd_num, peer_osd, client_id);
}
wanted_peers.erase(peer_osd);
repeer_pgs(peer_osd);
@@ -623,7 +522,7 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
{
osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT;
op->peer_fd = cl->peer_fd;
op->client_id = cl->client_id;
op->req = (osd_any_op_t){
.show_conf = {
.header = {
@@ -647,7 +546,7 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
if (!selected_ctx)
{
if (log_level > 0)
fprintf(stderr, "No RDMA context for OSD %ju connection (peer %d), using only TCP\n", cl->osd_num, cl->peer_fd);
fprintf(stderr, "No RDMA context for OSD %ju connection (client %ju), using only TCP\n", cl->osd_num, cl->client_id);
}
else
{
@@ -708,8 +607,8 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
if (err)
{
osd_num_t peer_osd = cl->osd_num;
stop_client(op->peer_fd);
on_connect_peer(peer_osd, -EINVAL);
stop_client(op->client_id);
on_connect_peer(peer_osd, -EINVAL, 0);
delete op;
return;
}
@@ -744,8 +643,8 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
}
}
#endif
osd_peer_fds[cl->osd_num] = cl->peer_fd;
on_connect_peer(cl->osd_num, cl->peer_fd);
osd_peers[cl->osd_num] = cl;
on_connect_peer(cl->osd_num, 0, cl->client_id);
delete op;
};
outbox_push(op);
@@ -760,13 +659,16 @@ void osd_messenger_t::accept_connections(int listen_fd)
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
{
assert(peer_fd != 0);
fprintf(stderr, "[OSD %ju] new client %d: connection from %s\n", this->osd_num, peer_fd,
const uint64_t client_id = next_client_id++;
fprintf(stderr, "[OSD %ju] new client %ju (FD %d): connection from %s\n", this->osd_num, client_id, peer_fd,
addr_to_string(addr).c_str());
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
int one = 1;
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
auto cl = new osd_client_t();
clients[peer_fd] = cl;
cl->client_id = client_id;
clients[cl->client_id] = cl;
clients_by_fd[peer_fd] = cl;
cl->is_incoming = true;
cl->peer_addr = addr;
cl->peer_addr = addr;
+20 -49
View File
@@ -12,6 +12,7 @@
#include <deque>
#include <vector>
#include "../util/robin_hood.h"
#include "malloc_or_die.h"
#include "json11/json11.hpp"
#include "msgr_op.h"
@@ -50,6 +51,7 @@ struct msgr_rdma_context_t;
struct osd_client_t
{
uint64_t client_id = 0;
int refs = 0;
sockaddr_storage peer_addr = {};
@@ -85,7 +87,7 @@ struct osd_client_t
std::vector<osd_op_t*> received_ops;
// Outbound operations
std::map<uint64_t, osd_op_t*> sent_ops;
robin_hood::unordered_flat_map<uint64_t, osd_op_t*> sent_ops;
uint64_t send_op_id = 0;
// PGs dirtied by this client's primary-writes
@@ -127,43 +129,7 @@ struct osd_op_stats_t
uint64_t subop_stat_count[OSD_OP_MAX+1] = { 0 };
};
#include <mutex>
#include <condition_variable>
#include <thread>
#ifdef __MOCK__
class msgr_iothread_t;
#else
struct iothread_sqe_t
{
io_uring_sqe sqe;
ring_data_t data;
};
class msgr_iothread_t
{
protected:
ring_loop_t ring;
ring_loop_t *outer_loop = NULL;
ring_data_t *outer_loop_data = NULL;
int eventfd = -1;
bool stopped = false;
std::mutex mu;
std::condition_variable cond;
std::vector<iothread_sqe_t> queue;
std::thread thread;
void run();
public:
msgr_iothread_t();
~msgr_iothread_t();
void add_sqe(io_uring_sqe & sqe);
void stop();
void add_to_ringloop(ring_loop_t *outer_loop);
};
#endif
#ifdef WITH_RDMA
struct rdma_event_channel;
@@ -201,25 +167,27 @@ protected:
uint64_t rdma_max_sge = 0, rdma_max_send = 0, rdma_max_recv = 0;
uint64_t rdma_max_msg = 0;
rdma_event_channel *rdmacm_evch = NULL;
std::map<rdma_cm_id*, osd_client_t*> rdmacm_connections;
std::map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
robin_hood::unordered_flat_map<rdma_cm_id*, osd_client_t*> rdmacm_connections;
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
#endif
std::vector<msgr_iothread_t*> iothreads;
std::vector<int> read_ready_clients;
std::vector<int> write_ready_clients;
std::vector<uint64_t> read_ready_clients;
std::vector<uint64_t> write_ready_clients;
// We don't use ringloop->set_immediate here because we may have no ringloop in client :)
std::deque<osd_op_t*> set_immediate_ops;
public:
timerfd_manager_t *tfd = NULL;
ring_loop_t *ringloop = NULL;
ring_loop_i *ringloop = NULL;
bool has_sendmsg_zc = false;
// osd_num_t is only for logging and asserts
uint64_t next_client_id = 1;
osd_num_t osd_num;
std::map<int, osd_client_t*> clients;
std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
std::map<uint64_t, int> osd_peer_fds;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> clients;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> osd_peers;
robin_hood::unordered_flat_map<int, osd_client_t*> clients_by_fd;
robin_hood::unordered_flat_map<osd_num_t, osd_wanted_peer_t> wanted_peers;
std::vector<std::string> osd_networks;
std::vector<addr_mask_t> osd_network_masks;
std::vector<std::string> osd_cluster_networks;
@@ -230,9 +198,11 @@ public:
osd_op_stats_t stats, recovery_stats;
void init();
void init_iothreads();
void parse_config(const json11::Json & config);
void connect_peer(uint64_t osd_num, json11::Json peer_state);
void stop_client(int peer_fd, bool force = false, bool force_delete = false);
void stop_client(uint64_t client_id, bool force_delete = false);
void destroy_client(osd_client_t *cl);
void outbox_push(osd_op_t *cur_op);
std::function<void(osd_op_t*)> exec_op;
std::function<void(osd_num_t)> repeer_pgs;
@@ -241,6 +211,7 @@ public:
void read_requests();
void send_replies();
void accept_connections(int listen_fd);
void destroy_iothreads();
~osd_messenger_t();
static json11::Json::object read_config(const json11::Json & config);
@@ -251,7 +222,7 @@ public:
#ifdef WITH_RDMA
bool is_rdma_enabled();
bool connect_rdma(int peer_fd, std::string rdma_address, uint64_t client_max_msg);
bool connect_rdma(uint64_t client_id, std::string rdma_address, uint64_t client_max_msg);
#endif
#ifdef WITH_RDMACM
bool is_use_rdmacm();
@@ -267,7 +238,7 @@ protected:
void try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_host, int peer_port);
void handle_peer_epoll(int peer_fd, int epoll_events);
void handle_connect_epoll(int peer_fd);
void on_connect_peer(osd_num_t peer_osd, int peer_fd);
void on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t client_id);
void check_peer_config(osd_client_t *cl);
void cancel_osd_ops(osd_client_t *cl);
void cancel_op(osd_op_t *op);
@@ -282,13 +253,13 @@ protected:
bool handle_reply_hdr(osd_client_t *cl);
void handle_reply_ready(osd_op_t *op);
void handle_immediate_ops();
void clear_immediate_ops(int peer_fd);
#ifdef WITH_RDMA
void try_send_rdma(osd_client_t *cl);
bool init_recv_rdma(osd_client_t *cl);
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
void destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn);
#endif
#ifdef WITH_RDMACM
void handle_rdmacm_events();
+129
View File
@@ -0,0 +1,129 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <stdexcept>
#include <sys/poll.h>
#include <unistd.h>
#include "messenger.h"
#include "msgr_iothread.h"
msgr_iothread_t::msgr_iothread_t():
ring(RINGLOOP_DEFAULT_SIZE, true),
thread(&msgr_iothread_t::run, this)
{
eventfd = ring.register_eventfd();
if (eventfd < 0)
{
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
}
}
msgr_iothread_t::~msgr_iothread_t()
{
stop();
}
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
{
mu.lock();
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
if (queue.size() == 1)
{
cond.notify_all();
}
mu.unlock();
}
void msgr_iothread_t::stop()
{
mu.lock();
if (stopped)
{
mu.unlock();
return;
}
stopped = true;
if (outer_loop_data)
{
outer_loop_data->callback = [](ring_data_t*){};
}
cond.notify_all();
close(eventfd);
mu.unlock();
thread.join();
}
void msgr_iothread_t::add_to_ringloop(ring_loop_i *outer_loop)
{
assert(!this->outer_loop || this->outer_loop == outer_loop);
io_uring_sqe *sqe = outer_loop->get_sqe();
assert(sqe != NULL);
this->outer_loop = outer_loop;
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
outer_loop_data->callback = [this](ring_data_t *data)
{
if (data->res < 0)
{
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
}
outer_loop_data = NULL;
if (stopped)
{
return;
}
add_to_ringloop(this->outer_loop);
ring.loop();
};
}
void msgr_iothread_t::run()
{
while (true)
{
{
std::unique_lock<std::mutex> lk(mu);
while (!stopped && !queue.size())
cond.wait(lk);
if (stopped)
return;
int i = 0;
for (; i < queue.size(); i++)
{
io_uring_sqe *sqe = ring.get_sqe();
if (!sqe)
break;
ring_data_t *data = ((ring_data_t*)sqe->user_data);
*data = std::move(queue[i].data);
*sqe = queue[i].sqe;
sqe->user_data = (uint64_t)data;
}
queue.erase(queue.begin(), queue.begin()+i);
}
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
ring.submit();
}
}
void osd_messenger_t::init_iothreads()
{
for (int i = 0; i < iothread_count; i++)
{
auto iot = new msgr_iothread_t();
iothreads.push_back(iot);
iot->add_to_ringloop(ringloop);
}
}
void osd_messenger_t::destroy_iothreads()
{
if (iothreads.size())
{
for (auto iot: iothreads)
{
delete iot;
}
iothreads.clear();
}
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <mutex>
#include <condition_variable>
#include <thread>
#include "ringloop.h"
struct iothread_sqe_t
{
io_uring_sqe sqe;
ring_data_t data;
};
class msgr_iothread_t
{
protected:
ring_loop_t ring;
ring_loop_i *outer_loop = NULL;
ring_data_t *outer_loop_data = NULL;
int eventfd = -1;
bool stopped = false;
std::mutex mu;
std::condition_variable cond;
std::vector<iothread_sqe_t> queue;
std::thread thread;
void run();
public:
msgr_iothread_t();
~msgr_iothread_t();
void add_sqe(io_uring_sqe & sqe);
void stop();
void add_to_ringloop(ring_loop_i *outer_loop);
};
+2 -1
View File
@@ -156,7 +156,8 @@ struct __attribute__((visibility("default"))) osd_op_t
{
timespec tv_begin = { 0 }, tv_end = { 0 };
uint64_t op_type = OSD_OP_IN;
int peer_fd;
uint64_t client_id = 0;
osd_num_t osd_num = 0;
osd_any_op_t req;
osd_any_reply_t reply;
blockstore_op_t *bs_op = NULL;
+41 -16
View File
@@ -187,6 +187,8 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
ibv_device **raw_dev_list = NULL;
ibv_device **dev_list = NULL;
ibv_device *single_list[2] = {};
int up_ports = 0;
int single_port_num = 0;
raw_dev_list = dev_list = ibv_get_device_list(NULL);
if (!dev_list || !*dev_list)
@@ -221,6 +223,7 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
dev_list = single_list;
}
retry:
for (int i = 0; dev_list[i]; ++i)
{
auto dev = dev_list[i];
@@ -258,6 +261,9 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index);
continue;
}
up_ports++;
single_port_num = port_num;
single_list[0] = dev;
uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu);
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
if (sel_gid_index < 0)
@@ -298,6 +304,14 @@ cleanup_dev:
ibv_close_device(context);
}
if (!ret.size() && up_ports == 1 && dev_list != single_list)
{
// Auto-select the only available device/port if there is only one
dev_list = single_list;
sel_port_num = single_port_num;
goto retry;
}
cleanup:
if (raw_dev_list)
ibv_free_device_list(raw_dev_list);
@@ -493,7 +507,7 @@ int msgr_rdma_connection_t::connect(msgr_rdma_address_t *dest)
return 0;
}
bool osd_messenger_t::connect_rdma(int peer_fd, std::string rdma_address, uint64_t client_max_msg)
bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address, uint64_t client_max_msg)
{
// Try to connect to the peer using RDMA
msgr_rdma_address_t addr;
@@ -503,12 +517,12 @@ bool osd_messenger_t::connect_rdma(int peer_fd, std::string rdma_address, uint64
{
client_max_msg = rdma_max_msg;
}
auto cl = clients.at(peer_fd);
auto cl = clients.at(client_id);
msgr_rdma_context_t *selected_ctx = choose_rdma_context(cl);
if (!selected_ctx)
{
if (log_level > 0)
fprintf(stderr, "No RDMA context for peer %d, using only TCP\n", cl->peer_fd);
fprintf(stderr, "No RDMA context for peer %ju, using only TCP\n", client_id);
return false;
}
msgr_rdma_connection_t *rdma_conn = msgr_rdma_connection_t::create(selected_ctx, rdma_max_send, rdma_max_recv, rdma_max_sge, client_max_msg);
@@ -519,14 +533,13 @@ bool osd_messenger_t::connect_rdma(int peer_fd, std::string rdma_address, uint64
{
delete rdma_conn;
fprintf(
stderr, "Failed to connect RDMA queue pair to %s (client %d)\n",
addr.to_string().c_str(), peer_fd
stderr, "Failed to connect RDMA queue pair to %s (client %ju)\n",
addr.to_string().c_str(), client_id
);
}
else
{
// Remember connection, but switch to RDMA only after sending the configuration response
auto cl = clients.at(peer_fd);
cl->rdma_conn = rdma_conn;
cl->peer_state = PEER_RDMA_CONNECTING;
return true;
@@ -540,7 +553,7 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
{
ibv_send_wr *bad_wr = NULL;
ibv_send_wr wr = {
.wr_id = (uint64_t)(cl->peer_fd*2+1),
.wr_id = cl->client_id,
.sg_list = sge,
.num_sge = op_sge,
.opcode = IBV_WR_SEND,
@@ -599,7 +612,9 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send)
{
dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos;
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
dst_len = (rc->send_out_pos >= rc->send_done_pos
? rc->send_out_size-rc->send_out_pos
: rc->send_done_pos-rc->send_out_pos);
if (dst_len > rc->max_msg)
dst_len = rc->max_msg;
copied = try_send_rdma_copy(cl, dst, dst_len);
@@ -609,7 +624,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
if (rc->send_out_pos == rc->send_out_size)
rc->send_out_pos = 0;
assert(rc->send_out_pos < rc->send_out_size);
if (rc->send_out_pos >= rc->send_done_pos)
if (rc->send_out_pos == rc->send_done_pos)
rc->send_out_full = true;
ibv_sge sge = {
.addr = (uintptr_t)dst,
@@ -631,7 +646,7 @@ static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
};
ibv_recv_wr *bad_wr = NULL;
ibv_recv_wr wr = {
.wr_id = (uint64_t)(cl->peer_fd*2),
.wr_id = cl->client_id,
.sg_list = &sge,
.num_sge = 1,
};
@@ -688,8 +703,8 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
event_count = ibv_poll_cq(rdma_context->cq, RDMA_EVENTS_AT_ONCE, wc);
for (int i = 0; i < event_count; i++)
{
int client_id = wc[i].wr_id >> 1;
bool is_send = wc[i].wr_id & 1;
uint64_t client_id = wc[i].wr_id;
bool is_send = wc[i].opcode == IBV_WC_SEND;
auto cl_it = clients.find(client_id);
if (cl_it == clients.end())
{
@@ -703,14 +718,13 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
auto rc = cl->rdma_conn;
if (wc[i].status != IBV_WC_SUCCESS)
{
fprintf(stderr, "RDMA work request failed for client %d", client_id);
fprintf(stderr, "RDMA work request failed for client %ju", client_id);
if (cl->osd_num)
{
fprintf(stderr, " (OSD %ju)", cl->osd_num);
}
fprintf(stderr, " with status: %s, stopping client\n", ibv_wc_status_str(wc[i].status));
stop_client(client_id);
clear_immediate_ops(client_id);
continue;
}
if (!is_send)
@@ -721,8 +735,6 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
rc->cur_recv--;
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
{
// handle_read_buffer may stop the client
clear_immediate_ops(client_id);
continue;
}
try_recv_rdma_wr(cl, rc->recv_buffers[rc->next_recv_buf]);
@@ -782,3 +794,16 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
} while (event_count > 0);
handle_immediate_ops();
}
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
{
if (rdma_conn->cmid)
{
auto rdma_it = rdmacm_connections.find(rdma_conn->cmid);
if (rdma_it != rdmacm_connections.end() && rdma_it->second->rdma_conn == rdma_conn)
{
rdmacm_connections.erase(rdma_it);
}
}
delete rdma_conn;
}
+12 -32
View File
@@ -11,7 +11,7 @@
struct rdmacm_connecting_t
{
rdma_cm_id *cmid = NULL;
int peer_fd = -1;
uint64_t client_id = 0;
osd_num_t peer_osd = 0;
std::string addr;
sockaddr_storage parsed_addr = {};
@@ -117,9 +117,9 @@ void osd_messenger_t::handle_rdmacm_events()
auto cli_it = rdmacm_connections.find(ev->id);
if (cli_it != rdmacm_connections.end())
{
fprintf(stderr, "Received %s event for peer %d, closing connection\n",
event_type_name, cli_it->second->peer_fd);
stop_client(cli_it->second->peer_fd);
fprintf(stderr, "Received %s event for client %ju, closing connection\n",
event_type_name, cli_it->second->client_id);
stop_client(cli_it->second->client_id);
}
else if (rdmacm_connecting.find(ev->id) != rdmacm_connecting.end())
{
@@ -265,14 +265,6 @@ msgr_rdma_context_t* osd_messenger_t::rdmacm_create_qp(rdma_cm_id *cmid)
void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev)
{
// Make a fake FD (FIXME: do not use FDs for identifying clients!)
int fake_fd = socket(AF_INET, SOCK_STREAM, 0);
if (fake_fd < 0)
{
fprintf(stderr, "Failed to allocate a fake socket for RDMA-CM client: %s (code %d)\n", strerror(errno), errno);
rdma_destroy_id(ev->id);
return;
}
auto rdma_context = rdmacm_create_qp(ev->id);
if (!rdma_context)
{
@@ -297,12 +289,12 @@ void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev)
// Wait for RDMA_CM_ESTABLISHED, and enable the connection only after it
auto conn = new rdmacm_connecting_t;
conn->cmid = ev->id;
conn->peer_fd = fake_fd;
conn->client_id = next_client_id++;
conn->parsed_addr = *(sockaddr_storage*)rdma_get_peer_addr(ev->id);
conn->rdma_context = rdma_context;
rdmacm_set_conn_timeout(conn);
rdmacm_connecting[ev->id] = conn;
fprintf(stderr, "[OSD %ju] new client %d: connection from %s via RDMA-CM\n", this->osd_num, conn->peer_fd,
fprintf(stderr, "[OSD %ju] new client %ju: connection from %s via RDMA-CM\n", this->osd_num, conn->client_id,
addr_to_string(conn->parsed_addr).c_str());
}
@@ -332,8 +324,6 @@ void osd_messenger_t::rdmacm_on_connect_peer_error(rdma_cm_id *cmid, int res)
auto peer_osd = conn->peer_osd;
if (conn->timeout_id >= 0)
tfd->clear_timer(conn->timeout_id);
if (conn->peer_fd >= 0)
close(conn->peer_fd);
if (conn->rdma_context)
conn->rdma_context->reserve_cqe(-rdma_max_send-rdma_max_recv);
if (conn->cmid)
@@ -354,7 +344,7 @@ void osd_messenger_t::rdmacm_on_connect_peer_error(rdma_cm_id *cmid, int res)
else
{
// TCP is disabled
on_connect_peer(peer_osd, res == 0 ? -EINVAL : (res > 0 ? -res : res));
on_connect_peer(peer_osd, res == 0 ? -EINVAL : (res > 0 ? -res : res), 0);
}
}
}
@@ -365,7 +355,7 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
if (!string_to_addr(addr, false, rdmacm_port, &sa))
{
fprintf(stderr, "Address %s is invalid\n", addr.c_str());
on_connect_peer(peer_osd, -EINVAL);
on_connect_peer(peer_osd, -EINVAL, 0);
return;
}
rdma_cm_id *cmid = NULL;
@@ -376,17 +366,7 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
if (!disable_tcp)
try_connect_peer_tcp(peer_osd, addr.c_str(), fallback_tcp_port);
else
on_connect_peer(peer_osd, res);
return;
}
// Make a fake FD (FIXME: do not use FDs for identifying clients!)
int fake_fd = socket(AF_INET, SOCK_STREAM, 0);
if (fake_fd < 0)
{
int res = -errno;
rdma_destroy_id(cmid);
// Can't create socket, pointless to try TCP
on_connect_peer(peer_osd, res);
on_connect_peer(peer_osd, res, 0);
return;
}
if (log_level > 0)
@@ -394,7 +374,7 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
auto conn = new rdmacm_connecting_t;
rdmacm_connecting[cmid] = conn;
conn->cmid = cmid;
conn->peer_fd = fake_fd;
conn->client_id = next_client_id++;
conn->peer_osd = peer_osd;
conn->addr = addr;
conn->parsed_addr = sa;
@@ -511,13 +491,13 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
auto cl = new osd_client_t();
cl->peer_addr = conn->parsed_addr;
cl->peer_port = conn->rdmacm_port;
cl->peer_fd = conn->peer_fd;
cl->client_id = conn->client_id;
cl->peer_state = PEER_RDMA;
cl->connect_timeout_id = -1;
cl->osd_num = peer_osd;
cl->in_buf = malloc_or_die(receive_buffer_size);
cl->rdma_conn = rc;
clients[conn->peer_fd] = cl;
clients[conn->client_id] = cl;
if (conn->timeout_id >= 0)
{
tfd->clear_timer(conn->timeout_id);
+29 -47
View File
@@ -2,15 +2,16 @@
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include "messenger.h"
#include "msgr_iothread.h"
void osd_messenger_t::read_requests()
{
for (int i = 0; i < read_ready_clients.size(); i++)
{
int peer_fd = read_ready_clients[i];
auto cl_it = clients.find(peer_fd);
uint64_t client_id = read_ready_clients[i];
auto cl_it = clients.find(client_id);
if (cl_it == clients.end() || !cl_it->second || cl_it->second->read_msg.msg_iovlen ||
cl_it->second->peer_state == PEER_RDMA || cl_it->second->peer_state == PEER_RDMA_CONNECTING)
cl_it->second->peer_state != PEER_CONNECTED)
{
continue;
}
@@ -32,7 +33,7 @@ void osd_messenger_t::read_requests()
cl->refs++;
if (ringloop && !use_sync_send_recv)
{
auto iothread = iothreads.size() ? iothreads[peer_fd % iothreads.size()] : NULL;
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL;
io_uring_sqe sqe_local;
ring_data_t data_local;
io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe());
@@ -50,7 +51,7 @@ void osd_messenger_t::read_requests()
}
ring_data_t* data = ((ring_data_t*)sqe->user_data);
data->callback = [this, cl](ring_data_t *data) { handle_read(data->res, cl); };
io_uring_prep_recvmsg(sqe, peer_fd, &cl->read_msg, 0);
io_uring_prep_recvmsg(sqe, cl->peer_fd, &cl->read_msg, 0);
if (iothread)
{
iothread->add_sqe(sqe_local);
@@ -58,7 +59,7 @@ void osd_messenger_t::read_requests()
}
else
{
int result = recvmsg(peer_fd, &cl->read_msg, 0);
int result = recvmsg(cl->peer_fd, &cl->read_msg, 0);
if (result < 0)
{
result = -errno;
@@ -73,7 +74,6 @@ void osd_messenger_t::read_requests()
bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
{
bool ret = false;
int peer_fd = cl->peer_fd;
cl->read_msg.msg_iovlen = 0;
cl->refs--;
if (cl->peer_state == PEER_RDMA)
@@ -84,7 +84,7 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
{
if (cl->refs <= 0)
{
delete cl;
destroy_client(cl);
}
return false;
}
@@ -93,20 +93,20 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
// this is a client socket, so don't panic on error. just disconnect it
if (result != 0)
{
fprintf(stderr, "Client %d socket read error: %d (%s). Disconnecting client\n", cl->peer_fd, -result, strerror(-result));
fprintf(stderr, "Client %ju socket read error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result));
}
stop_client(cl->peer_fd);
stop_client(cl->client_id);
return false;
}
if (result == -EAGAIN || result == -EINTR || result < cl->read_iov.iov_len)
{
cl->read_ready--;
if (cl->read_ready > 0)
read_ready_clients.push_back(cl->peer_fd);
read_ready_clients.push_back(cl->client_id);
}
else
{
read_ready_clients.push_back(cl->peer_fd);
read_ready_clients.push_back(cl->client_id);
}
if (result > 0)
{
@@ -114,7 +114,6 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
{
if (!handle_read_buffer(cl, cl->in_buf, result))
{
clear_immediate_ops(peer_fd);
handle_immediate_ops();
return false;
}
@@ -128,7 +127,6 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
{
if (!handle_finished_read(cl))
{
clear_immediate_ops(peer_fd);
handle_immediate_ops();
return false;
}
@@ -143,26 +141,6 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
return ret;
}
void osd_messenger_t::clear_immediate_ops(int peer_fd)
{
size_t i = 0, j = 0;
while (i < set_immediate_ops.size())
{
if (set_immediate_ops[i]->peer_fd == peer_fd && set_immediate_ops[i]->op_type == OSD_OP_IN)
{
delete set_immediate_ops[i];
}
else
{
if (i != j)
set_immediate_ops[j] = set_immediate_ops[i];
j++;
}
i++;
}
set_immediate_ops.resize(j);
}
void osd_messenger_t::handle_immediate_ops()
{
while (set_immediate_ops.size())
@@ -171,7 +149,11 @@ void osd_messenger_t::handle_immediate_ops()
set_immediate_ops.pop_front();
if (op->op_type == OSD_OP_IN)
{
exec_op(op);
auto cl_it = clients.find(op->client_id);
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_STOPPED)
exec_op(op);
else
delete op;
}
else
{
@@ -189,7 +171,7 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, void *curbuf, int rem
if (!cl->read_op)
{
cl->read_op = new osd_op_t;
cl->read_op->peer_fd = cl->peer_fd;
cl->read_op->client_id = cl->client_id;
cl->read_op->op_type = OSD_OP_IN;
cl->recv_list.push_back(cl->read_op->req.buf, OSD_PACKET_SIZE);
cl->read_remaining = OSD_PACKET_SIZE;
@@ -243,8 +225,8 @@ bool osd_messenger_t::handle_finished_read(osd_client_t *cl)
{
if (cl->read_op->req.hdr.id != cl->read_op_id)
{
fprintf(stderr, "Warning: operation sequencing is broken on client %d: expected num %ju, got %ju, stopping client\n", cl->peer_fd, cl->read_op_id, cl->read_op->req.hdr.id);
stop_client(cl->peer_fd);
fprintf(stderr, "Warning: operation sequencing is broken on client %ju: expected num %ju, got %ju, stopping client\n", cl->client_id, cl->read_op_id, cl->read_op->req.hdr.id);
stop_client(cl->client_id);
return false;
}
cl->read_op_id++;
@@ -253,8 +235,8 @@ bool osd_messenger_t::handle_finished_read(osd_client_t *cl)
}
else
{
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from %d\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->peer_fd);
stop_client(cl->peer_fd);
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from client %ju\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->client_id);
stop_client(cl->client_id);
return false;
}
}
@@ -367,11 +349,11 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
{
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id);
if (req_it == cl->sent_ops.end())
if (req_it == cl->sent_ops.end() || req_it->second->req.hdr.opcode != cl->read_op->req.hdr.opcode)
{
// Command out of sync. Drop connection
fprintf(stderr, "Client %d command out of sync: id %ju\n", cl->peer_fd, cl->read_op->req.hdr.id);
stop_client(cl->peer_fd);
fprintf(stderr, "Client %ju command out of sync: id %ju\n", cl->client_id, cl->read_op->req.hdr.id);
stop_client(cl->client_id);
return false;
}
osd_op_t *op = req_it->second;
@@ -385,13 +367,13 @@ bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
if (op->reply.hdr.retval >= 0 && (op->reply.hdr.retval != expected_size || bmp_len > op->bitmap_len))
{
// Check reply length to not overflow the buffer
fprintf(stderr, "Client %d read reply of different length: expected %u+%u, got %jd+%u\n",
cl->peer_fd, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
fprintf(stderr, "Client %ju read reply of different length: expected %u+%u, got %jd+%u\n",
cl->client_id, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
cl->sent_ops[op->req.hdr.id] = op;
stop_client(cl->peer_fd);
stop_client(cl->client_id);
return false;
}
if (bmp_len > 0)
if (op->reply.hdr.retval >= 0 && bmp_len > 0)
{
assert(op->bitmap);
cl->recv_list.push_back(op->bitmap, bmp_len);
+34 -27
View File
@@ -6,11 +6,18 @@
#include <sys/epoll.h>
#include "messenger.h"
#include "msgr_iothread.h"
void osd_messenger_t::outbox_push(osd_op_t *cur_op)
{
assert(cur_op->peer_fd);
osd_client_t *cl = clients.at(cur_op->peer_fd);
assert(cur_op->client_id);
auto cl_it = clients.find(cur_op->client_id);
if (cl_it == clients.end() || cl_it->second->peer_state == PEER_STOPPED)
{
delete cur_op;
return;
}
osd_client_t *cl = cl_it->second;
if (cur_op->op_type == OSD_OP_OUT)
{
clock_gettime(CLOCK_REALTIME, &cur_op->tv_begin);
@@ -18,8 +25,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
}
else
{
// Check that operation actually belongs to this client
// FIXME: Review if this is still needed
// Remove the operation from received op list
bool found = false;
for (auto it = cl->received_ops.begin(); it != cl->received_ops.end(); it++)
{
@@ -30,11 +36,8 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
break;
}
}
if (!found)
{
delete cur_op;
return;
}
// Can't be not found because client IDs are unique
assert(found);
}
auto & to_send_list = cl->write_msg.msg_iovlen ? cl->next_send_list : cl->send_list;
auto & to_outbox = cl->write_msg.msg_iovlen ? cl->next_outbox : cl->outbox;
@@ -97,10 +100,15 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{
if (cur_op->op_type == OSD_OP_IN && cur_op->reply.hdr.retval > 0)
{
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->reply.hdr.retval });
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
else if (cur_op->op_type == OSD_OP_OUT && cur_op->req.sec_read_bmp.len > 0)
{
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->req.sec_read_bmp.len });
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
}
if (cur_op->op_type == OSD_OP_IN)
{
@@ -126,7 +134,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
if ((cl->write_msg.msg_iovlen > 0 || !try_send(cl)) && (cl->write_state == 0))
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cur_op->peer_fd);
write_ready_clients.push_back(cur_op->client_id);
}
ringloop->wakeup();
}
@@ -183,15 +191,14 @@ void osd_messenger_t::measure_exec(osd_op_t *cur_op)
bool osd_messenger_t::try_send(osd_client_t *cl)
{
int peer_fd = cl->peer_fd;
if (!cl->send_list.size() || cl->write_msg.msg_iovlen > 0)
if (!cl->send_list.size() || cl->write_msg.msg_iovlen > 0 || cl->peer_state == PEER_STOPPED || cl->peer_fd < 0)
{
return true;
}
assert(cl->peer_state != PEER_RDMA);
if (ringloop && !use_sync_send_recv)
{
auto iothread = iothreads.size() ? iothreads[peer_fd % iothreads.size()] : NULL;
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL;
io_uring_sqe sqe_local;
ring_data_t data_local;
io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe());
@@ -218,11 +225,11 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
}
if (use_zc)
{
io_uring_prep_sendmsg_zc(sqe, peer_fd, &cl->write_msg, MSG_WAITALL);
io_uring_prep_sendmsg_zc(sqe, cl->peer_fd, &cl->write_msg, MSG_WAITALL);
}
else
{
io_uring_prep_sendmsg(sqe, peer_fd, &cl->write_msg, MSG_WAITALL);
io_uring_prep_sendmsg(sqe, cl->peer_fd, &cl->write_msg, MSG_WAITALL);
}
if (iothread)
{
@@ -234,7 +241,7 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
cl->write_msg.msg_iov = cl->send_list.data();
cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
cl->refs++;
int result = sendmsg(peer_fd, &cl->write_msg, MSG_NOSIGNAL);
int result = sendmsg(cl->peer_fd, &cl->write_msg, MSG_NOSIGNAL);
if (result < 0)
{
result = -errno;
@@ -249,8 +256,8 @@ void osd_messenger_t::send_replies()
{
for (int i = 0; i < write_ready_clients.size(); i++)
{
int peer_fd = write_ready_clients[i];
auto cl_it = clients.find(peer_fd);
uint64_t client_id = write_ready_clients[i];
auto cl_it = clients.find(client_id);
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_RDMA && !try_send(cl_it->second))
{
write_ready_clients.erase(write_ready_clients.begin(), write_ready_clients.begin() + i);
@@ -274,15 +281,15 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
{
if (cl->refs <= 0)
{
delete cl;
destroy_client(cl);
}
return;
}
if (result < 0 && result != -EAGAIN && result != -EINTR)
{
// this is a client socket, so don't panic. just disconnect it
fprintf(stderr, "Client %d socket write error: %d (%s). Disconnecting client\n", cl->peer_fd, -result, strerror(-result));
stop_client(cl->peer_fd);
fprintf(stderr, "Client %ju socket write error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result));
stop_client(cl->client_id);
return;
}
if (result >= 0)
@@ -326,9 +333,9 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
int expected = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
if (done != expected)
{
fprintf(stderr, "Client %d socket write error: expected to send "
"%d iovecs with MSG_WAITALL but sent %d. Disconnecting client\n", cl->peer_fd, expected, done);
stop_client(cl->peer_fd);
fprintf(stderr, "Client %ju socket write error: expected to send "
"%d iovecs with MSG_WAITALL but sent %d. Disconnecting client\n", cl->client_id, expected, done);
stop_client(cl->client_id);
return;
}
cl->zc_free_list.push_back(NULL); // end marker
@@ -352,7 +359,7 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
// FIXME: Ignore pings during RDMA state transition
if (log_level > 0)
{
fprintf(stderr, "Successfully connected with client %d using RDMA\n", cl->peer_fd);
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id);
}
cl->peer_state = PEER_RDMA;
// Add the initial receive request
@@ -362,6 +369,6 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
}
if (cl->write_state != 0)
{
write_ready_clients.push_back(cl->peer_fd);
write_ready_clients.push_back(cl->client_id);
}
}
+61 -60
View File
@@ -5,9 +5,6 @@
#include <assert.h>
#include "messenger.h"
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
void osd_client_t::cancel_ops()
{
@@ -43,34 +40,40 @@ void osd_op_t::cancel()
}
}
void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
// force_delete means stop the client anyway, even if there are refs to it in the event loop.
// the flag should be used in the destructor.
// why? - because yes, we could close the FD first and let it fail all requests in the event loop,
// but in that case it can be quickly reopened and we can get old failed responses for the new FD.
void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
{
assert(peer_fd != 0);
auto it = clients.find(peer_fd);
if (it == clients.end())
auto it = clients.find(client_id);
if (!client_id || it == clients.end())
{
return;
}
osd_client_t *cl = it->second;
// FIXME: This 'force' flag is probably an ugly reenterability hack - check its logic and maybe remove it
if (cl->peer_state == PEER_CONNECTING && !force || cl->peer_state == PEER_STOPPED)
if (cl->peer_state == PEER_STOPPED)
{
if (force_delete)
{
destroy_client(cl);
}
return;
}
clear_immediate_ops(peer_fd);
cl->received_ops.clear();
if (log_level > 0)
{
if (cl->osd_num)
{
fprintf(stderr, "[OSD %ju] Stopping client %d (OSD peer %ju)\n", osd_num, peer_fd, cl->osd_num);
fprintf(stderr, "[OSD %ju] Stopping client %ju (OSD peer %ju)\n", osd_num, client_id, cl->osd_num);
}
else if (cl->in_osd_num)
{
fprintf(stderr, "[OSD %ju] Stopping client %d (incoming OSD peer %ju)\n", osd_num, peer_fd, cl->in_osd_num);
fprintf(stderr, "[OSD %ju] Stopping client %ju (incoming OSD peer %ju)\n", osd_num, client_id, cl->in_osd_num);
}
else
{
fprintf(stderr, "[OSD %ju] Stopping client %d (regular client)\n", osd_num, peer_fd);
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id);
}
}
// First set state to STOPPED so another stop_client() call doesn't try to free it again
@@ -79,48 +82,18 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
cl->peer_state = PEER_STOPPED;
if (cl->osd_num)
{
auto osd_it = osd_peer_fds.find(cl->osd_num);
if (osd_it != osd_peer_fds.end() && osd_it->second == cl->peer_fd)
auto osd_it = osd_peers.find(cl->osd_num);
if (osd_it != osd_peers.end() && osd_it->second == cl)
{
// ...and forget OSD peer
osd_peer_fds.erase(osd_it);
osd_peers.erase(osd_it);
}
}
#ifdef WITH_RDMA
if (cl->rdma_conn && cl->rdma_conn->cmid)
{
auto rdma_it = rdmacm_connections.find(cl->rdma_conn->cmid);
if (rdma_it != rdmacm_connections.end() && rdma_it->second == cl)
{
rdmacm_connections.erase(rdma_it);
}
}
#endif
#ifndef __MOCK__
// Then remove FD from the eventloop so we don't accidentally read something
tfd->set_fd_handler(peer_fd, false, NULL);
if (cl->connect_timeout_id >= 0)
{
tfd->clear_timer(cl->connect_timeout_id);
cl->connect_timeout_id = -1;
}
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
{
if (*rit == peer_fd)
{
read_ready_clients.erase(rit);
break;
}
}
for (auto wit = write_ready_clients.begin(); wit != write_ready_clients.end(); wit++)
{
if (*wit == peer_fd)
{
write_ready_clients.erase(wit);
break;
}
}
#endif
if (cl->in_osd_num && break_pg_locks)
{
// Break PG locks
@@ -134,19 +107,56 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
// so do not repeer on it.
repeer_pgs(cl->osd_num);
}
// Find the item again because it can be invalidated at this point
it = clients.find(peer_fd);
if (it != clients.end())
if (cl->peer_fd >= 0)
{
clients.erase(it);
int r = shutdown(cl->peer_fd, SHUT_RDWR);
if (r != 0 && errno != ENOTCONN)
{
fprintf(stderr, "[OSD %ju] failed to shutdown a socket: %s (code %d)\n", osd_num, strerror(errno), errno);
}
}
cl->refs--;
if (cl->refs <= 0 || force_delete)
{
delete cl;
destroy_client(cl);
}
}
void osd_messenger_t::destroy_client(osd_client_t *cl)
{
// Find the item again because it can be invalidated at this point
clients.erase(cl->client_id);
if (cl->peer_fd >= 0)
{
tfd->set_fd_handler(cl->peer_fd, false, NULL);
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
{
if (*rit == cl->client_id)
{
read_ready_clients.erase(rit);
break;
}
}
for (auto wit = write_ready_clients.begin(); wit != write_ready_clients.end(); wit++)
{
if (*wit == cl->client_id)
{
write_ready_clients.erase(wit);
break;
}
}
clients_by_fd.erase(cl->peer_fd);
}
#ifdef WITH_RDMA
if (cl->rdma_conn)
{
destroy_rdma_conn(cl->rdma_conn);
cl->rdma_conn = NULL;
}
#endif
delete cl;
}
osd_client_t::~osd_client_t()
{
free(in_buf);
@@ -176,13 +186,4 @@ osd_client_t::~osd_client_t()
delete op;
}
}
#ifndef __MOCK__
#ifdef WITH_RDMA
if (rdma_conn)
{
delete rdma_conn;
rdma_conn = NULL;
}
#endif
#endif
}
+1 -1
View File
@@ -1049,7 +1049,7 @@ static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
{
// Get larger allocated extents, possibly with false positives
uint64_t bmp_pos = (offset-task.offset) / task.bitmap_granularity;
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity - bmp_pos;
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity;
while (bmp_pos < bmp_end)
{
if (!(bmp_pos & 7) && bmp_end >= bmp_pos+8)
+1 -1
View File
@@ -282,7 +282,7 @@ help:
exit(1);
}
}
const bool writeback = cli->get_immediate_commit(inode);
const bool writeback = !cli->get_immediate_commit(inode);
auto pool_it = cli->st_cli.pool_config.find(INODE_POOL(inode ? inode : watch->cfg.num));
if (pool_it == cli->st_cli.pool_config.end())
{
+1 -1
View File
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: Vitastor
Description: Vitastor client library
Version: 3.0.6
Version: 3.0.12
Libs: -L${libdir} -lvitastor_client
Cflags: -I${includedir}
+2 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
@@ -27,6 +27,7 @@ add_library(vitastor_cli STATIC
cli_pool_ls.cpp
cli_pool_modify.cpp
cli_pool_rm.cpp
cli_raw_ls.cpp
)
target_compile_options(vitastor_cli PUBLIC -fPIC)
+10
View File
@@ -126,6 +126,11 @@ static const char* help_text =
" --min-offset, --max-offset\n"
" Restrict listing to specific offsets inside inodes.\n"
"\n"
"vitastor-cli raw-ls [OPTIONS]\n"
" Find object(s) in the cluster using raw secondary listing operations. Options:\n"
" [--min_inode NUM] [--max_inode NUM] [--offset NUM] [--pg_num NUM] [--pg_count COUNT]\n"
" [--pg_stripe_size NUM] [--osds 1,2,3,...]\n"
"\n"
"vitastor-cli fix [--objects <objects>] [--bad-osds <osds>] [--part <part>] [--check no]\n"
" Fix inconsistent objects in the cluster by deleting some copies.\n"
" --objects <objects>\n"
@@ -459,6 +464,11 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
// Describe unclean objects
action_cb = p->start_describe(cfg);
}
else if (cmd[0] == "raw-ls")
{
// Run raw listings
action_cb = p->start_raw_ls(cfg);
}
else if (cmd[0] == "fix")
{
// Fix inconsistent objects (by deleting some copies)
+1
View File
@@ -62,6 +62,7 @@ public:
std::function<bool(cli_result_t &)> start_fix(json11::Json);
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
std::function<bool(cli_result_t &)> start_ls(json11::Json);
std::function<bool(cli_result_t &)> start_raw_ls(json11::Json cfg);
std::function<bool(cli_result_t &)> start_merge(json11::Json);
std::function<bool(cli_result_t &)> start_modify(json11::Json);
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
+212
View File
@@ -0,0 +1,212 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#include "cli_fix.h"
#include "cluster_client.h"
#include "pg_states.h"
#include "str_util.h"
#include "json_util.h"
std::vector<uint64_t> parse_uint64_list(json11::Json val);
// Find object(s) in the cluster using raw secondary listing operations.
// Only for troubleshooting.
struct cli_raw_ls_t
{
json11::Json cfg;
pool_id_t pool_id = 0;
pg_num_t pg_num = 0;
pg_num_t pg_count = 0;
uint32_t pg_stripe_size = 0;
std::set<uint64_t> osds;
std::vector<osd_num_t> osd_list;
uint64_t min_inode = 0, max_inode = 0, min_offset = 0, max_offset = 0;
bool offset_set = false;
cli_tool_t *parent = NULL;
int state = 0;
cli_result_t result;
bool first = true;
size_t osd_pos = 0;
bool is_done()
{
return state == 100;
}
void loop()
{
if (state == 1)
goto resume_1;
if (state == 100)
return;
pool_id = cfg["pool_id"].uint64_value();
pg_num = (pg_num_t)cfg["pg_num"].uint64_value();
pg_count = (pg_num_t)cfg["pg_count"].uint64_value();
pg_stripe_size = cfg["pg_stripe_size"].uint64_value();
if (!pool_id)
{
result = (cli_result_t){ .err = EINVAL, .text = "Pool ID is required" };
state = 100;
return;
}
if (cfg["min_inode"].is_null() && cfg["max_inode"].is_null())
{
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), 0);
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), UINT64_MAX);
}
else
{
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["min_inode"].as_string()));
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["max_inode"].as_string()));
}
if (cfg["offset"].is_null())
{
if (!pg_num)
{
result = (cli_result_t){ .err = EINVAL, .text = "PG number is required if offset is not specified" };
state = 100;
return;
}
min_offset = 0;
max_offset = UINT64_MAX;
}
else
{
min_offset = max_offset = stoull_full(cfg["offset"].as_string());
offset_set = true;
}
for (auto osd_num: parse_uint64_list(cfg["osds"]))
{
osds.insert(osd_num);
}
if (!pg_count || !pg_stripe_size || !osds.size())
{
auto pool_it = parent->cli->st_cli.pool_config.find(pool_id);
if (pool_it == parent->cli->st_cli.pool_config.end())
{
result = (cli_result_t){ .err = EINVAL, .text = "pg_count, pg_stripe_size and osds are required if the pool does not exist" };
state = 100;
return;
}
if (!pg_count)
{
pg_count = pool_it->second.real_pg_count;
}
if (!pg_stripe_size)
{
pg_stripe_size = pool_it->second.pg_stripe_size;
}
if (!osds.size())
{
for (auto & pgp: pool_it->second.pg_config)
{
for (osd_num_t pg_osd: pgp.second.target_set)
osds.insert(pg_osd);
for (osd_num_t pg_osd: pgp.second.all_peers)
osds.insert(pg_osd);
for (auto & hist_item: pgp.second.target_history)
for (auto pg_osd: hist_item)
osds.insert(pg_osd);
}
osds.erase(0);
}
}
if (offset_set)
{
min_offset = (min_offset / pg_stripe_size) * pg_stripe_size;
max_offset = ((max_offset / pg_stripe_size) * pg_stripe_size) | STRIPE_MASK;
pg_num = (min_offset/pg_stripe_size) % pg_count + 1; // like map_to_pg()
fprintf(stderr, "Selected PG %u\n", pg_num);
}
parent->cli->init_msgr();
osd_list = std::vector<osd_num_t>(osds.begin(), osds.end());
osd_pos = 0;
state = 1;
resume_1:
for (; osd_pos < osd_list.size() && parent->waiting < parent->parallel_osds; osd_pos++)
{
uint64_t osd_num = osd_list[osd_pos];
if (parent->cli->st_cli.peer_states[osd_num].is_null())
{
fprintf(stderr, "OSD %ju is unavailable, skipping\n", osd_num);
continue;
}
osd_op_t *op = new osd_op_t;
op->req = (osd_any_op_t){
.sec_list = {
.header = {
.magic = SECONDARY_OSD_OP_MAGIC,
.opcode = OSD_OP_SEC_LIST,
},
.list_pg = pg_num,
.pg_count = pg_count,
.pg_stripe_size = pg_stripe_size,
.min_inode = min_inode,
.max_inode = max_inode,
.min_stripe = min_offset,
.max_stripe = max_offset,
},
};
op->callback = [this, osd_num](osd_op_t *op)
{
if (op->reply.hdr.retval < 0)
{
fprintf(stderr, "OSD %ju listing failed: retval=%jd\n", osd_num, op->reply.hdr.retval);
}
else
{
for (uint64_t i = 0; i < op->reply.hdr.retval; i++)
{
auto & ov = ((obj_ver_id*)op->buf)[i];
if (parent->json_output)
{
printf("%s{\"osd\":%ju,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"version\":%ju,\"stable\":%s}",
first ? "" : ",\n", osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
i < op->reply.sec_list.stable_count ? "true" : "false");
first = false;
}
else
{
printf("OSD %ju - %jx:%jx v%ju%s\n",
osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
i < op->reply.sec_list.stable_count ? " stable" : "");
}
}
}
parent->waiting--;
loop();
delete op;
};
parent->waiting++;
parent->cli->execute_raw(osd_num, op);
}
if (parent->waiting > 0)
{
return;
}
if (!first)
{
printf("\n");
}
state = 100;
}
};
std::function<bool(cli_result_t &)> cli_tool_t::start_raw_ls(json11::Json cfg)
{
auto raw_ls = new cli_raw_ls_t();
raw_ls->parent = this;
raw_ls->cfg = cfg;
return [raw_ls](cli_result_t & result)
{
raw_ls->loop();
if (raw_ls->is_done())
{
result = raw_ls->result;
delete raw_ls;
return true;
}
return false;
};
}
+2
View File
@@ -286,6 +286,8 @@ struct rm_inode_t
.data = data,
};
}
// Wakeup callers (otherwise they wake up only on next loop())
parent->ringloop->wakeup();
}
in_continue = false;
}
+1 -2
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
@@ -11,7 +11,6 @@ add_executable(vitastor-disk
../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp
)
target_link_libraries(vitastor-disk
tcmalloc_minimal
${LIBURING_LIBRARIES}
${ISAL_LIBRARIES}
)
+3
View File
@@ -427,6 +427,9 @@ int main(int argc, char *argv[])
self.dsk.open_journal();
self.dsk.calc_lengths();
self.dsk.close_all();
self.new_meta_device = self.dsk.meta_device;
self.new_meta_offset = self.dsk.meta_offset;
self.new_meta_len = self.dsk.meta_area_size;
}
std::string json_err;
json11::Json meta = json11::Json::parse(read_all_fd(0), json_err);
-3
View File
@@ -52,7 +52,6 @@ struct disk_tool_t
bool all = false, json = false, now = false;
bool dump_with_blocks = false, dump_with_data = false;
bool dump_as_old = false;
bool skip_obsolete = false;
int log_level = 1;
double meta_reserve_multiple = 2;
uint64_t meta_reserve_min_size = (uint64_t)1024*1024*1024;
@@ -138,8 +137,6 @@ struct disk_tool_t
int resize_write_new_journal();
void remap_big_write(heap_entry_t *wr);
void remap_small_write(heap_entry_t *wr);
void fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr);
void fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr);
int resize_rebuild_meta();
int resize_write_new_meta();
void free_new_meta();
+2 -1
View File
@@ -589,6 +589,7 @@ int disk_tool_t::write_json_meta(json11::Json meta)
int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
{
assert(new_meta_len >= sizeof(blockstore_meta_header_v3_t));
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
memset(new_meta_buf, 0, new_meta_len);
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
@@ -606,9 +607,9 @@ int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
: (meta["data_csum_type"].string_value() == "crc32c"
? BLOCKSTORE_CSUM_CRC32C
: BLOCKSTORE_CSUM_NONE);
new_meta_hdr->meta_area_size = new_meta_len;
new_meta_hdr->csum_block_size = meta["csum_block_size"].uint64_value();
new_meta_hdr->header_csum = crc32c(0, new_meta_hdr, sizeof(blockstore_meta_header_v3_t));
new_meta_hdr->meta_area_size = new_meta_len;
new_clean_entry_bitmap_size = (new_meta_hdr->data_block_size / new_meta_hdr->bitmap_granularity + 7) / 8;
new_clean_entry_size = 0;
new_entries_per_block = 0;
+19 -136
View File
@@ -111,7 +111,7 @@ int disk_tool_t::raw_resize()
fprintf(stderr, "Done\n");
ret:
free_new_meta();
return 0;
return r;
}
int disk_tool_t::resize_parse_params()
@@ -154,9 +154,6 @@ int disk_tool_t::resize_parse_params()
? parse_size(options["new_journal_offset"]) : dsk.journal_offset;
new_journal_len = options.find("new_journal_len") != options.end()
? parse_size(options["new_journal_len"]) : dsk.journal_len;
new_meta_format = options.find("new_meta_format") != options.end()
? stoull_full(options["new_meta_format"]) : 0;
skip_obsolete = options.find("skip_obsolete") != options.end();
if (new_data_len+new_data_offset > dsk.data_device_size)
new_data_len = dsk.data_device_size-new_data_offset;
if (new_meta_device == dsk.data_device && new_data_offset < new_meta_offset &&
@@ -205,10 +202,7 @@ void disk_tool_t::resize_init(blockstore_meta_header_v3_t *hdr)
{
dsk.meta_format = hdr->version;
}
if (new_meta_format == 0)
{
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
}
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
dsk.calc_lengths();
if (((new_data_offset-dsk.data_offset) % dsk.data_block_size))
{
@@ -563,66 +557,6 @@ void disk_tool_t::remap_small_write(heap_entry_t *wr)
}
}
void disk_tool_t::fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr)
{
uint64_t block_num = big_wr->big().block_num;
clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size +
dsk.meta_block_size*(block_num / new_entries_per_block) +
new_clean_entry_size*(block_num % new_entries_per_block));
new_entry->oid = (object_id){ .inode = big_wr->inode, .stripe = big_wr->stripe };
new_entry->version = big_wr->version;
memcpy(new_entry->bitmap, big_wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
memcpy(new_entry->bitmap + new_clean_entry_bitmap_size, big_wr->get_int_bitmap(heap), new_clean_entry_bitmap_size);
memcpy(new_entry->bitmap + 2*new_clean_entry_bitmap_size, big_wr->get_checksums(heap), new_data_csum_size);
uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4);
*new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4);
}
void disk_tool_t::fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr)
{
assert(wr->type() == BS_HEAP_SMALL_WRITE ||
wr->type() == BS_HEAP_BIG_WRITE ||
wr->type() == BS_HEAP_BIG_INTENT);
uint32_t je_size = ((wr->entry_type & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE
? sizeof(journal_entry_small_write) + dsk.dirty_dyn_size(wr->small().offset, wr->small().len)
: sizeof(journal_entry_big_write) + dsk.dirty_dyn_size(0, dsk.data_block_size));
choose_journal_block(je_size);
journal_entry *je = (journal_entry*)(new_journal_ptr + new_journal_in_pos);
je->magic = JOURNAL_MAGIC;
je->type = (wr->entry_type & BS_HEAP_STABLE) ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE;
je->size = je_size;
je->crc32_prev = new_crc32_prev;
je->small_write.oid = (object_id){ .inode = wr->inode, .stripe = wr->stripe };
je->small_write.version = wr->version;
if (wr->type() == BS_HEAP_SMALL_WRITE)
{
je->small_write.offset = wr->small().offset;
je->small_write.len = wr->small().len;
je->small_write.data_offset = new_journal_data-new_journal_buf;
if (je->small_write.data_offset + je->small_write.len > new_journal_len)
{
fprintf(stderr, "Error: live entries don't fit to the new journal\n");
exit(1);
}
memcpy(new_journal_data, buffer_area+wr->small().location, je->small_write.len);
new_journal_data += je->small_write.len;
if (dsk.data_csum_type == 0 && wr->get_checksum(heap))
je->small_write.crc32_data = *wr->get_checksum(heap);
}
else
{
je->big_write.location = wr->big_location(heap);
}
memcpy((uint8_t*)je + je->size, wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
if (dsk.data_csum_type != 0 && wr->get_checksums(heap))
{
memcpy((uint8_t*)je + je->size + new_clean_entry_bitmap_size, wr->get_checksums(heap), heap->get_csum_size(wr));
}
je->crc32 = je_crc32(je);
new_journal_in_pos += je->size;
new_crc32_prev = je->crc32;
}
int disk_tool_t::resize_rebuild_meta()
{
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
@@ -630,13 +564,12 @@ int disk_tool_t::resize_rebuild_meta()
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
uint64_t new_meta_pos = dsk.meta_block_size;
uint64_t next_lsn = 0;
std::vector<heap_entry_t*> writes;
int r = process_meta(
[&](blockstore_meta_header_v3_t *hdr)
{
new_meta_hdr->zero = 0;
new_meta_hdr->magic = BLOCKSTORE_META_MAGIC_V1;
new_meta_hdr->version = new_meta_format == 0 ? BLOCKSTORE_META_FORMAT_HEAP : new_meta_format;
new_meta_hdr->version = new_meta_format;
new_meta_hdr->meta_block_size = dsk.meta_block_size;
new_meta_hdr->data_block_size = dsk.data_block_size;
new_meta_hdr->bitmap_granularity = dsk.bitmap_granularity ? dsk.bitmap_granularity : 4096;
@@ -654,18 +587,16 @@ int disk_tool_t::resize_rebuild_meta()
},
[&](blockstore_heap_t *heap, heap_entry_t *obj, uint32_t meta_block_num)
{
assert(new_meta_format == BLOCKSTORE_META_FORMAT_HEAP);
if (!obj)
{
// Finish
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
while (new_meta_pos < new_meta_len)
{
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
while (new_meta_pos < new_meta_len)
{
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
new_meta_pos += dsk.meta_block_size;
}
new_meta_pos += dsk.meta_block_size;
}
return;
}
@@ -679,72 +610,24 @@ int disk_tool_t::resize_rebuild_meta()
{
remap_small_write(wr);
}
else if (wr->type() != BS_HEAP_DELETE && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP)
// New -> New
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
{
fprintf(stderr, "Object %jx:%jx can't be converted to the old format because it contains an entry of type 0x%x%s\n",
wr->inode, wr->stripe, wr->entry_type,
(wr->type() == BS_HEAP_INTENT_WRITE ? " (intent_write)" : ""));
exit(1);
}
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
{
// New -> New
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
if (new_meta_pos >= new_meta_len)
{
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
if (new_meta_pos >= new_meta_len)
{
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
exit(1);
}
}
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
new_meta_pos += wr->size;
if (skip_obsolete && wr->type() == BS_HEAP_BIG_WRITE && stable)
{
// Skip older writes
return false;
}
}
else
{
// New -> Old
if (wr->type() == BS_HEAP_DELETE && stable)
{
// Object is deleted, skip it
return false;
}
if (wr->type() == BS_HEAP_BIG_WRITE && stable)
{
fill_old_clean_entry(heap, wr);
return false;
}
else
{
writes.push_back(wr);
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
exit(1);
}
}
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
new_meta_pos += wr->size;
return true;
};
if (new_meta_format != BLOCKSTORE_META_FORMAT_HEAP || skip_obsolete)
for (auto wr = obj; wr; wr = heap->prev(wr))
{
heap->iterate_with_stable(obj, obj->lsn, handle_write);
}
else
{
for (auto wr = obj; wr; wr = heap->prev(wr))
{
handle_write(wr, false);
}
}
if (writes.size())
{
for (size_t i = writes.size(); i > 0; i--)
{
fill_old_journal_entry(heap, writes[i-1]);
}
writes.clear();
handle_write(wr, false);
}
},
[&](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap)
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
+2 -1
View File
@@ -121,6 +121,7 @@ static const char* help_text =
" --logfile <FILE> log to the specified file\n"
" --enforce 1 enforce permissions at the server side (default is disabled)\n"
" --foreground 1 stay in foreground, do not daemonize\n"
" --trace trace all NFS requests\n"
"\n"
"NFS proxy is stateless if you use immediate_commit=all in your cluster and if\n"
"you do not use client_enable_writeback=true, so you can freely use multiple\n"
@@ -158,7 +159,7 @@ json11::Json::object nfs_proxy_t::parse_args(int narg, const char *args[])
{
const char *opt = args[i]+2;
cfg[str_replace(opt, "-", "_")] = !strcmp(opt, "json") || !strcmp(opt, "block") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") || !strcmp(opt, "trace") ||
!strcmp(opt, "include-empty") || !strcmp(opt, "no-rm") || i == narg-1 ? "1" : args[++i];
}
else
+5 -5
View File
@@ -586,8 +586,8 @@ send_again:
}
// Estimate reply WR count, create WR and SGE arrays
xdr_write_chunk *reply_chunk = rop->in_rdma_msg.rdma_body.rdma_msg.rdma_reply;
int reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + (reply_chunk ? reply_chunk_wr_count : 0);
uint32_t reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + reply_chunk_wr_count;
if (wr_count > ctx->max_send_wr)
{
fprintf(stderr, "Reply fragmentation (%u) exceeds max_send_wr (%u), sending ERR_CHUNK\n", wr_count, ctx->max_send_wr);
@@ -622,7 +622,7 @@ chunk_error:
{
size_t reply_chunk_len = 0;
size_t left = msg_size;
for (uint32_t i = 0; i < reply_chunk->target.target_len; i++)
for (uint32_t i = 0; i < reply_chunk_wr_count; i++)
{
reply_chunk_len += reply_chunk->target.target_val[i].length;
if (reply_chunk->target.target_val[i].length > left)
@@ -653,7 +653,7 @@ chunk_error:
}
ibv_sge sges[wr_count];
ibv_send_wr wrs[wr_count];
int wr_pos = 0;
uint32_t wr_pos = 0;
// Use a buffer from rdma_malloc for the reply
assert(!rop->buffer);
rop->buffer = rdma_malloc_alloc(conn_dev->alloc, hdr_size+msg_size);
@@ -685,7 +685,7 @@ chunk_error:
if (reply_chunk)
{
size_t pos = hdr_size;
for (uint32_t i = 0; i < reply_chunk->target.target_len && pos < msg_size; i++)
for (uint32_t i = 0; i < reply_chunk_wr_count && pos < msg_size; i++)
{
uint32_t len = (reply_chunk->target.target_val[i].length < msg_size-pos
? reply_chunk->target.target_val[i].length : msg_size-pos);
+3 -4
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
@@ -19,20 +19,19 @@ target_link_libraries(vitastor-osd
# osd_rmw_test
add_executable(osd_rmw_test EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES} tcmalloc_minimal)
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES})
add_dependencies(build_tests osd_rmw_test)
add_test(NAME osd_rmw_test COMMAND osd_rmw_test)
if (ISAL_LIBRARIES)
add_executable(osd_rmw_test_je EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
target_compile_definitions(osd_rmw_test_je PUBLIC -DNO_ISAL)
target_link_libraries(osd_rmw_test_je Jerasure tcmalloc_minimal)
target_link_libraries(osd_rmw_test_je Jerasure)
add_dependencies(build_tests osd_rmw_test_je)
add_test(NAME osd_rmw_test_jerasure COMMAND osd_rmw_test_je)
endif (ISAL_LIBRARIES)
# osd_peering_pg_test
add_executable(osd_peering_pg_test EXCLUDE_FROM_ALL osd_peering_pg_test.cpp osd_peering_pg.cpp)
target_link_libraries(osd_peering_pg_test tcmalloc_minimal)
add_dependencies(build_tests osd_peering_pg_test)
add_test(NAME osd_peering_pg_test COMMAND osd_peering_pg_test)
+10 -9
View File
@@ -15,22 +15,19 @@
#include "str_util.h"
#include "json_util.h"
osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
osd_t::osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd)
{
zero_buffer_size = 1<<20;
zero_buffer = malloc_or_die(zero_buffer_size);
memset(zero_buffer, 0, zero_buffer_size);
this->ringloop = ringloop;
this->tfd = tfd;
this->cli_config = config.object_items();
this->file_config = msgr.read_config(this->cli_config);
parse_config(true);
epmgr = new epoll_manager_t(ringloop);
// FIXME: Use timerfd_interval based directly on io_uring
this->tfd = epmgr->tfd;
if (json_is_true(this->config["osd_memlock"]))
{
// Lock all OSD memory if requested
@@ -99,7 +96,6 @@ osd_t::~osd_t()
}
ringloop->unregister_consumer(&consumer);
ringloop->unregister_consumer(&init_consumer);
delete epmgr;
if (bs)
delete bs;
#ifdef WITH_RDMACM
@@ -398,7 +394,7 @@ void osd_t::bind_socket()
{
int listen_fd = create_and_bind_socket(bind_address, listening_port ? listening_port : bind_port, listen_backlog, &listening_port);
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events)
tfd->set_fd_handler(listen_fd, false, [this](int fd, int events)
{
msgr.accept_connections(fd);
});
@@ -529,6 +525,11 @@ void osd_t::exec_op(osd_op_t *cur_op)
void osd_t::print_stats()
{
if (bs && log_level > 1)
{
printf("[OSD %ju] Live entries: %ju (%ju bytes), garbage entries: %ju (%ju bytes)\n", osd_num,
bs->get_live_entries(), bs->get_live_memory(), bs->get_garbage_entries(), bs->get_garbage_memory());
}
for (int i = OSD_OP_MIN; i <= OSD_OP_MAX; i++)
{
if (msgr.stats.op_stat_count[i] != prev_stats.op_stat_count[i] && i != OSD_OP_PING)
@@ -616,11 +617,11 @@ void osd_t::print_slow()
bufprintf("[OSD %ju] Slow op %jx", osd_num, (uint64_t)op);
if (kv.second->osd_num)
{
bufprintf(" from peer OSD %ju (client %d)", kv.second->osd_num, kv.second->peer_fd);
bufprintf(" from peer OSD %ju (client %ju)", kv.second->osd_num, kv.second->client_id);
}
else
{
bufprintf(" from client %d", kv.second->peer_fd);
bufprintf(" from client %ju", kv.second->client_id);
}
bufprintf(": %s id=%ju", osd_op_names[op->req.hdr.opcode], op->req.hdr.id);
if (op->req.hdr.opcode == OSD_OP_SEC_READ || op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
+6 -8
View File
@@ -19,7 +19,6 @@
#include "blockstore.h"
#include "ringloop.h"
#include "timerfd_manager.h"
#include "epoll_manager.h"
#include "osd_peering_pg.h"
#include "messenger.h"
#include "etcd_state_client.h"
@@ -210,9 +209,8 @@ class osd_t
void *zero_buffer = NULL;
uint64_t zero_buffer_size = 0;
uint32_t bs_block_size, bs_bitmap_granularity, clean_entry_bitmap_size;
ring_loop_t *ringloop = NULL;
ring_loop_i *ringloop = NULL;
timerfd_manager_t *tfd = NULL;
epoll_manager_t *epmgr = NULL;
int listening_port = 0;
std::vector<std::string> bind_addresses;
@@ -347,13 +345,12 @@ class osd_t
bool check_write_queue(osd_op_t *cur_op, pg_t & pg);
pg_osd_set_state_t* add_object_to_set(pg_t & pg, const object_id oid, const pg_osd_set_t & osd_set,
uint64_t old_pg_state, int log_at_level);
void remove_object_from_state(object_id & oid, pg_osd_set_state_t **object_state, pg_t &pg, bool report = true);
bool remove_object_from_state(object_id & oid, pg_osd_set_state_t **object_state, pg_t &pg, bool report = true);
pg_osd_set_state_t *mark_object(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state, bool ref,
std::function<int(pg_osd_set_t & new_set)> calc_set);
pg_osd_set_state_t *mark_object_corrupted(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
osd_rmw_stripe_t *stripes, bool ref);
pg_osd_set_state_t *mark_partial_write(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
osd_rmw_stripe_t *stripes, bool ref);
pg_osd_set_state_t *mark_partial_write(pg_t & pg, osd_op_t *cur_op);
void deref_object_state(pg_t & pg, pg_osd_set_state_t **object_state, bool deref);
bool remember_unstable_write(osd_op_t *cur_op, pg_t & pg, pg_osd_set_t & loc_set, int base_state);
void handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op);
@@ -366,9 +363,10 @@ class osd_t
osd_rmw_stripe_t *stripes, const uint64_t* osd_set, osd_op_t *cur_op, int subop_idx, int zero_read);
void submit_primary_subop(osd_op_t *cur_op, osd_op_t *subop,
osd_rmw_stripe_t *si, bool wr, inode_t inode, uint64_t op_version);
bool submit_to_osd(osd_op_t *subop, osd_num_t osd_num);
void submit_primary_del_subops(osd_op_t *cur_op, uint64_t *cur_set, uint64_t set_size, pg_osd_set_t & loc_set);
void submit_primary_del_batch(osd_op_t *cur_op, obj_ver_osd_t *chunks_to_delete, int chunks_to_delete_count);
int submit_primary_sync_subops(osd_op_t *cur_op);
void submit_primary_sync_subops(osd_op_t *cur_op);
void submit_primary_stab_subops(osd_op_t *cur_op);
void submit_primary_rollback_subops(osd_op_t *cur_op, const uint64_t* osd_set);
@@ -392,7 +390,7 @@ class osd_t
}
public:
osd_t(const json11::Json & config, ring_loop_t *ringloop);
osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd);
~osd_t();
void force_stop(int exitcode);
bool shutdown();
+1 -1
View File
@@ -991,7 +991,7 @@ void osd_t::apply_pg_config()
// Add peers
for (auto pg_osd: all_peers)
{
if (pg_osd != this->osd_num && msgr.osd_peer_fds.find(pg_osd) == msgr.osd_peer_fds.end())
if (pg_osd != this->osd_num && msgr.osd_peers.find(pg_osd) == msgr.osd_peers.end())
{
msgr.connect_peer(pg_osd, st_cli.peer_states[pg_osd]);
}
+7 -14
View File
@@ -4,6 +4,7 @@
#include "osd.h"
#define FLUSH_BATCH 512
#define SELF_CLIENT 0
void osd_t::submit_pg_flush_ops(pg_t & pg)
{
@@ -91,11 +92,12 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
else
{
printf("Error while doing flush on OSD %ju: %d (%s)\n", osd_num, retval, strerror(-retval));
auto fd_it = msgr.osd_peer_fds.find(peer_osd);
if (fd_it != msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(peer_osd);
if (peer_it != msgr.osd_peers.end())
{
// Will repeer/stop this PG
msgr.stop_client(fd_it->second);
msgr.stop_client(peer_it->second->client_id);
return;
}
}
}
@@ -212,17 +214,8 @@ bool osd_t::submit_flush_op(pool_id_t pool_id, pg_num_t pg_num, pg_flush_batch_t
handle_flush_op(op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK, pool_id, pg_num, fb, peer_osd, op->reply.hdr.retval);
delete op;
};
auto peer_fd_it = msgr.osd_peer_fds.find(peer_osd);
if (peer_fd_it != msgr.osd_peer_fds.end())
if (!submit_to_osd(op, peer_osd))
{
op->peer_fd = peer_fd_it->second;
msgr.outbox_push(op);
}
else
{
// Fail it immediately
op->reply.hdr.retval = -EPIPE;
op->callback(op);
return false;
}
}
@@ -307,7 +300,7 @@ void osd_t::submit_recovery_op(osd_recovery_op_t *op)
{
printf("Submitting recovery operation for %jx:%jx (%s)\n", op->oid.inode, op->oid.stripe, op->degraded ? "degraded" : "misplaced");
}
op->osd_op->peer_fd = -1;
op->osd_op->client_id = SELF_CLIENT;
op->osd_op->callback = [this, op](osd_op_t *osd_op)
{
ringloop->set_immediate([this, op]()
+4 -1
View File
@@ -1,6 +1,7 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#include "epoll_manager.h"
#include "osd.h"
#include <sys/prctl.h>
@@ -63,13 +64,15 @@ int main(int narg, char *args[])
signal(SIGINT, handle_sigint);
signal(SIGTERM, handle_sigint);
ring_loop_t *ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
osd = new osd_t(config, ringloop);
epoll_manager_t *epmgr = new epoll_manager_t(ringloop);
osd = new osd_t(config, ringloop, epmgr->tfd);
while (1)
{
ringloop->loop();
ringloop->wait();
}
delete osd;
delete epmgr;
delete ringloop;
return 0;
}
+23 -21
View File
@@ -9,7 +9,7 @@
#include "str_util.h"
#include "osd.h"
#define SELF_FD -1
#define SELF_CLIENT 0
// Peering loop
void osd_t::handle_peers()
@@ -140,10 +140,12 @@ void osd_t::reset_pg(pg_t & pg)
copies_to_delete_after_sync_count -= pg.copies_to_delete_after_sync.size();
pg.copies_to_delete_after_sync.clear();
corrupted_objects -= pg.corrupted_count;
inconsistent_objects -= pg.inconsistent_objects.size();
incomplete_objects -= pg.incomplete_objects.size();
misplaced_objects -= pg.misplaced_objects.size();
degraded_objects -= pg.degraded_objects.size();
pg.corrupted_count = 0;
pg.inconsistent_objects.clear();
pg.incomplete_objects.clear();
pg.misplaced_objects.clear();
pg.degraded_objects.clear();
@@ -175,17 +177,17 @@ void osd_t::drop_dirty_pg_connections(pool_pg_num_t pg)
{
if (immediate_commit != IMMEDIATE_ALL)
{
std::vector<int> to_stop;
std::vector<uint64_t> to_stop;
for (auto & cp: msgr.clients)
{
if (cp.second->dirty_pgs.find(pg) != cp.second->dirty_pgs.end())
{
to_stop.push_back(cp.first);
to_stop.push_back(cp.second->client_id);
}
}
for (auto peer_fd: to_stop)
for (auto client_id: to_stop)
{
msgr.stop_client(peer_fd);
msgr.stop_client(client_id);
}
}
}
@@ -203,7 +205,7 @@ void osd_t::start_pg_peering(pg_t & pg)
for (auto pg_osd: pg.all_peers)
{
if (pg_osd != this->osd_num &&
msgr.osd_peer_fds.find(pg_osd) == msgr.osd_peer_fds.end())
msgr.osd_peers.find(pg_osd) == msgr.osd_peers.end())
{
if (msgr.wanted_peers.find(pg_osd) == msgr.wanted_peers.end())
msgr.connect_peer(pg_osd, st_cli.peer_states[pg_osd]);
@@ -224,7 +226,7 @@ void osd_t::start_pg_peering(pg_t & pg)
for (int role = 0; role < pg.target_set.size(); role++)
{
pg.cur_set[role] = pg.target_set[role] == this->osd_num ||
msgr.osd_peer_fds.find(pg.target_set[role]) != msgr.osd_peer_fds.end() ? pg.target_set[role] : 0;
msgr.osd_peers.find(pg.target_set[role]) != msgr.osd_peers.end() ? pg.target_set[role] : 0;
if (pg.cur_set[role] != 0)
{
pg.pg_cursize++;
@@ -246,7 +248,7 @@ void osd_t::start_pg_peering(pg_t & pg)
std::set<osd_num_t> dead_peers;
for (auto pg_osd: pg.all_peers)
{
if (pg_osd == this->osd_num || msgr.osd_peer_fds.find(pg_osd) != msgr.osd_peer_fds.end())
if (pg_osd == this->osd_num || msgr.osd_peers.find(pg_osd) != msgr.osd_peers.end())
cur_peers.insert(pg_osd);
else
dead_peers.insert(pg_osd);
@@ -266,7 +268,7 @@ void osd_t::start_pg_peering(pg_t & pg)
{
nonzero++;
if (history_osd == this->osd_num ||
msgr.osd_peer_fds.find(history_osd) != msgr.osd_peer_fds.end())
msgr.osd_peers.find(history_osd) != msgr.osd_peers.end())
{
found++;
}
@@ -351,6 +353,7 @@ bool osd_t::continue_pg_peering(pg_t & pg)
pg.calc_object_states(log_level);
report_pg_state(pg);
schedule_scrub(pg);
inconsistent_objects += pg.inconsistent_objects.size();
incomplete_objects += pg.incomplete_objects.size();
misplaced_objects += pg.misplaced_objects.size();
// FIXME: degraded objects may currently include misplaced, too! Report them separately?
@@ -435,8 +438,8 @@ void osd_t::relock_pg(pg_t & pg)
bool unlock_peer = (i >= relock_osd_count);
uint64_t new_state = unlock_peer ? 0 : pg.state;
auto peer_osd = diff_osds[i];
auto peer_fd_it = msgr.osd_peer_fds.find(peer_osd);
if (peer_fd_it == msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(peer_osd);
if (peer_it == msgr.osd_peers.end())
{
if (unlock_peer)
{
@@ -446,8 +449,7 @@ void osd_t::relock_pg(pg_t & pg)
}
continue;
}
int peer_fd = peer_fd_it->second;
auto cl = msgr.clients.at(peer_fd);
auto cl = peer_it->second;
if (!cl->enable_pg_locks)
{
// Peer does not support locking - just instantly remember the lock as successful
@@ -458,7 +460,7 @@ void osd_t::relock_pg(pg_t & pg)
pg.inflight_locks++;
osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT;
op->peer_fd = peer_fd;
op->client_id = cl->client_id;
op->req = (osd_any_op_t){
.sec_lock = {
.header = {
@@ -529,7 +531,7 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
// Self
osd_op_t *op = new osd_op_t();
op->op_type = 0;
op->peer_fd = SELF_FD;
op->client_id = SELF_CLIENT;
clock_gettime(CLOCK_REALTIME, &op->tv_begin);
op->bs_op = new blockstore_op_t();
op->bs_op->opcode = BS_OP_LIST;
@@ -567,8 +569,8 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
}
else
{
auto role_fd_it = msgr.osd_peer_fds.find(role_osd);
if (role_fd_it == msgr.osd_peer_fds.end())
auto peer_it = msgr.osd_peers.find(role_osd);
if (peer_it == msgr.osd_peers.end())
{
printf("Failed to get object list from OSD %ju because it is disconnected\n", role_osd);
return;
@@ -576,7 +578,7 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
// Peer
osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT;
op->peer_fd = role_fd_it->second;
op->client_id = peer_it->second->client_id;
op->req = (osd_any_op_t){
.sec_list = {
.header = {
@@ -595,10 +597,10 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
if (op->reply.hdr.retval < 0)
{
printf("Failed to get object list from OSD %ju (retval=%jd), disconnecting peer\n", role_osd, op->reply.hdr.retval);
int fail_fd = op->peer_fd;
uint64_t fail_client_id = op->client_id;
ps->list_ops.erase(role_osd);
delete op;
msgr.stop_client(fail_fd);
msgr.stop_client(fail_client_id);
return;
}
printf(
@@ -622,7 +624,7 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
void osd_t::discard_list_subop(osd_op_t *list_op)
{
if (list_op->peer_fd == SELF_FD)
if (list_op->client_id == SELF_CLIENT)
{
// Self
list_op->bs_op->callback = [list_op](blockstore_op_t *bs_op)
+2
View File
@@ -1,7 +1,9 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#include "malloc_or_die.h"
#include "osd_peering_pg.h"

Some files were not shown because too many files have changed in this diff Show More