Compare commits

..
Author SHA1 Message Date
Vitaliy Filippov ec10a25905 Use 32-bit big write location (OK for up to 512 TB OSDs) 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 923c41b928 Do not store offset & len in big_writes 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 8f179eb9a6 Fix object crc32c calculation 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 172b106d99 Add entry_type to heap_object_t too
This is required to:
1) later inline the last "big_write" entry into the object to slightly reduce memory usage
2) eliminate an ugly hack where entry type is determined by its size
3) make the storage scheme extensible i.e. when adding new entry types
2025-11-23 19:08:24 +03:00
Vitaliy Filippov 59a51363d0 Rename flags to entry_type 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 65d5d69ae3 Use robin_hood::unordered_flat_map - it has 1 byte overhead instead of 8 byte 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 5d100b4e35 Revert try_get 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 7a894efcb7 WIP dump/load heap 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 3bad0f6fa8 Add missing list_stable_limit support 2025-11-23 19:08:24 +03:00
Vitaliy Filippov f215c791c8 Fix checksum validation in !inmemory_journal mode 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 0a3260672b Fix checksum padding during read 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 171b339eb3 Remove unused bs->buffer_area + buffer calc 2025-11-23 19:08:24 +03:00
Vitaliy Filippov ac331fa77b Add missing memset zero_object to 0 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 915be747d6 Mark in-memory data reads as SKIP_CSUM 2025-11-23 19:08:24 +03:00
Vitaliy Filippov b14fbbf5cc Add missing calc_crc32c after updating block checksums 2025-11-23 19:08:24 +03:00
Vitaliy Filippov e379fc6df4 Fix checksum padding during flush 2025-11-23 19:08:24 +03:00
Vitaliy Filippov eec2f2b799 Fix free_read_buffers 2025-11-23 19:08:24 +03:00
Vitaliy Filippov d85e1dc204 Fix assert(region_marker) at the end of the block in find_block_run() 2025-11-23 19:08:24 +03:00
Vitaliy Filippov c2edcf63e1 Fix assert(is_buffer_area_free) with size=0 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 3a8f0f53de Add a copy of wyhash 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 9b70bfc2e4 Fix test build with isa-l 2025-11-23 19:08:24 +03:00
Vitaliy Filippov d1678c67d9 Remove assert !region_marker & FREE 2025-11-23 19:08:24 +03:00
Vitaliy Filippov ad308ed92a Use wyhash 2025-11-23 19:08:24 +03:00
Vitaliy Filippov b0bf45c26f Change emhashes 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 2a550d4d13 unordered_map mvcc 2025-11-23 19:08:24 +03:00
Vitaliy Filippov d4d3d34a84 Fix bad resharding due to the lack of iteration order in a hashmap 2025-11-23 19:08:24 +03:00
Vitaliy Filippov b8bec78879 Use emhash::try_get 2025-11-23 19:08:24 +03:00
Vitaliy Filippov fa77a321ab Use emhash hashmap (2x faster) 2025-11-23 19:08:24 +03:00
Vitaliy Filippov cfce09be03 Use unordered_maps for object-block index 2025-11-23 19:08:24 +03:00
Vitaliy Filippov ed3332ea83 B-tree is slower... 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 0a94c437e2 Unordered_map for pool settings 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 3bb514321f Fix read fio bench 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 1a4c6746c4 Fix vitastor-disk prepare and param validation 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 3040bcfbfa Fix skipping of corrupted objects, fix use_buffer_area with zero size 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 08e16ba985 Support heap format in simple-offsets 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 9afa3f9a13 Return all unstable versions in listing 2025-11-23 19:08:24 +03:00
Vitaliy Filippov bbf5063b25 Actually fsync after stabilize 2025-11-23 19:08:24 +03:00
Vitaliy Filippov f45c88d38c Move test & build_test to top-level cmakelists 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 8fa0638347 Add missing request_trim 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 0c2e0b8a49 Use multilist_index_t instead of multiple bitmap allocators 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 36ccf63922 Implement another multilist-style allocator for metadata blocks 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 4bc1d09fbd Fix a bug with unstable_big over unstable_small 2025-11-23 19:08:24 +03:00
Vitaliy Filippov dc31650110 Limit the number of unstable versions per object 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 598e1ed1db Integrate moving objects 2025-11-23 19:08:24 +03:00
Vitaliy Filippov a49c176423 Support moving objects between blocks 2025-11-23 19:08:24 +03:00
Vitaliy Filippov ecd26ebac3 Fix zero-length writes 2025-11-23 19:08:24 +03:00
Vitaliy Filippov d0690091c1 Fix op_stable slowdowns 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 9369b643a1 Fix a bs_read bug 2025-11-23 19:08:24 +03:00
Vitaliy Filippov ed2a886ab4 Fix test dependencies 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 98bdcfbff9 Add read_blocks() API 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 3cce10d9c7 Fix block checksum calculation in write_journal for the old blockstore version 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 8762ae07a6 Remove block_order parameter 2025-11-23 19:08:24 +03:00
Vitaliy Filippov b2b30201c4 Extract (flags & BS_HEAP_TYPE) into a function 2025-11-23 19:08:24 +03:00
Vitaliy Filippov a912420424 Make dump-journal --format data default 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 27c250e4da Add fio options 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 0b28ec8fc9 Fix loading for out-of-order lsns 2025-11-23 19:08:24 +03:00
Vitaliy Filippov b9b2f8cec1 Add include for older gcc 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 4581084540 Fix vitastor-disk build (with old metadata and journal formats) 2025-11-23 19:08:24 +03:00
Vitaliy Filippov 4dfb80e6a1 Collapse intent_writes on other write types too 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 1aff129f99 Fix buffer overflow in test_heap 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 2282ed0345 Add a test for parallel reads with block checksums 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 4b95fe4919 Remove cancel_all_writes
Not needed because a) parallel writes to the same object are forbidden
b) subsequent writes don't depend on previous ones anyway.
2025-11-23 19:08:23 +03:00
Vitaliy Filippov ad6354a537 Add a test for "perfect_csum_update" mode 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 346d20ce29 Add 2 tests for intent writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov eb9633f21f Add a test with fsync 2025-11-23 19:08:23 +03:00
Vitaliy Filippov f86bed106c Implement buffered disk_mock_t mode, extract ringloop_mock.cpp 2025-11-23 19:08:23 +03:00
Vitaliy Filippov cc2975efe6 unaligned_intent does not need special handling anymore 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 27ef64cef4 Add an option for global coverage 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 7f7d79fdc9 Fix some blockstore bugs discovered by the mocked test! 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 7b94969869 Add a basic mocked blockstore test 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 92a0942cd6 Add mocks for blockstore integration tests: timerfd, ring_loop_mock_t and disk_mock_t 2025-11-23 19:08:23 +03:00
Vitaliy Filippov c5c332cb9a Test calc_checksums 2025-11-23 19:08:23 +03:00
Vitaliy Filippov cedbedc1e5 Check 2/3 blocks in test_recheck 2025-11-23 19:08:23 +03:00
Vitaliy Filippov b28c67ba5f Fix intent writes with padded checksums 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 26fb08cc02 Fix reads from intent writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 186f37e286 Disable punching block checksums and allow to enable it with a parameter 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 8c3b83ff74 Process big_writes as intents to avoid fsyncs 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 5fe474573e Fix collapsing intent-over-intent checksums 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 89644692ac Allow 1 intent_write over big_write in fsync mode 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 8581988c2c Block lists by previous writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov fb83ebbfff Allow multiple writes with the same version 2025-11-23 19:08:23 +03:00
Vitaliy Filippov abf741048a Add tests for intent writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov cb056b4413 Call finish_load after async recheck 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 43c4510a29 More tests for incorrect data cases 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 2ba34bfd75 Do not block writes on previous writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 481005b062 Add atomic_write_size parameter 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 6464dae129 Use ui32 for block sizes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov e0d60dd49f Remove extra unneeded read_entry-s 2025-11-23 19:08:23 +03:00
Vitaliy Filippov abeb8b73fa Do not recheck data location on intent-write 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 15d47a2695 Remove FIXMEs 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 661c45cbb5 Fsync after stabilizing 2025-11-23 19:08:23 +03:00
Vitaliy Filippov bff3d35c85 Fsync & update metadata when block checksums are enabled 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 18a2858c62 Return new_lsn from erase and rollback 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 999d0e2961 Allow to cancel compaction for unfinished writes 2025-11-23 19:08:23 +03:00
Vitaliy Filippov df1ab722dd Fix space allocation & compaction on start 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 40a4952592 Use inflight_lsn iterators 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 298a5ec499 Free space on overwrites correctly 2025-11-23 19:08:23 +03:00
Vitaliy Filippov dd28f2f963 Use fsynced_lsn in flusher 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 41256ea460 Correctly track fsynced_lsn when fsyncs are enabled 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 229c19dee4 Extract multilist_alloc_t 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 1ccb241eef Check for overlaps during blockstore loading 2025-11-23 19:08:23 +03:00
Vitaliy Filippov b8245f86eb Fix multilist_alloc_t bug, move verify and print to lib 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 31f7cbc19a Fix pending_ops 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 20226200a4 Fix repeating cur_oid 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 48f8ae8e30 Mark overwritten heap_writes as immediately compacted 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 38faa21966 Move "ack write" debug message 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 2afb16cd19 Fsync data on trim_lsn, not when writing compacted data 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 908ce74500 Batch big_write data fsyncs 2025-11-23 19:08:23 +03:00
Vitaliy Filippov 98da2cb6e0 Use the same "inflight" queue to track compaction 2025-11-23 19:08:22 +03:00
Vitaliy Filippov 0fd557fe95 Use new LSNs on stabilize 2025-11-23 19:07:43 +03:00
Vitaliy Filippov a52dd0dfb5 Assign new LSN on stabilize 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 66847653aa WIP Only save MVCC copy when overwriting an object 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 393f9d6b6f Do not use wr_offset 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 9a87452d23 Prevent compaction of incomplete object writes 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 385973f777 Experimental INTENT_WRITE write mode with WA=2 instead of 3 2025-11-23 19:07:43 +03:00
Vitaliy Filippov b958f7f119 Add test_compact_block 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 4d649b1726 Remove alloc_buffer_area 2025-11-23 19:07:43 +03:00
Vitaliy Filippov bf180f5292 Two more unordered_maps 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 6ee1093837 Remove sync_to_repeat map and use simpler repeating 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 1747cd6a95 Use a sequence of bitmap_allocs for metadata instead of std::sets... 2025-11-23 19:07:43 +03:00
Vitaliy Filippov ec9eba63af It seems tcmalloc is actually slower, disable it 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 86dbd99268 Use single add_used_space call instead of unmark+mark allocated_block 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 7ad3001afd Remove compact_queue_lsn map 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 5222c2724f Implement a really crazy "multi-linked-list" allocator for buffered data 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 622cc55924 Use linked list heap in blockstore code
WIP, still slower than the old version :-E
2025-11-23 19:07:43 +03:00
Vitaliy Filippov e2fa675ad0 Use linked lists in heap to avoid excessive memory copying 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 388b5f19a1 v1 (old) store fixes for merged version 2025-11-23 19:07:43 +03:00
Vitaliy Filippov 19a386e4b3 Integrate "heap" metadata storage into blockstore 2025-11-23 19:07:42 +03:00
Vitaliy Filippov 9264ca96a4 "Heap" metadata storage scheme 2025-11-23 15:18:01 +03:00
246 changed files with 6573 additions and 11795 deletions
+7 -8
View File
@@ -1,29 +1,28 @@
FROM node:16-bookworm
FROM node:16-bullseye
WORKDIR /root
ADD ./docker/etc/apt/trusted.gpg.d /etc/apt/trusted.gpg.d
ADD ./docker/vitastor.gpg /etc/apt/trusted.gpg.d
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list; \
echo 'deb http://vitastor.io/debian bookworm main' >> /etc/apt/sources.list; \
RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list; \
echo 'deb http://vitastor.io/debian bullseye main' >> /etc/apt/sources.list; \
echo >> /etc/apt/preferences; \
echo 'Package: *' >> /etc/apt/preferences; \
echo 'Pin: release n=bookworm-backports' >> /etc/apt/preferences; \
echo 'Pin: release a=bullseye-backports' >> /etc/apt/preferences; \
echo 'Pin-Priority: 500' >> /etc/apt/preferences; \
echo >> /etc/apt/preferences; \
echo 'Package: *' >> /etc/apt/preferences; \
echo 'Pin: origin "vitastor.io"' >> /etc/apt/preferences; \
echo 'Pin-Priority: 1000' >> /etc/apt/preferences; \
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/debian.sources; \
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
RUN apt-get update
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan8 \
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan5 \
libgoogle-perftools-dev devscripts libjerasure-dev cmake libibverbs-dev libisal-dev
RUN apt-get -y build-dep fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted
RUN apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
RUN set -ex; \
+3 -813
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 build_tests test
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 test
npm_lint:
runs-on: ubuntu-latest
@@ -306,78 +306,6 @@ jobs:
echo ""
done
test_old_interrupted_rebalance:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 /root/vitastor/tests/test_interrupted_rebalance.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_interrupted_rebalance_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_interrupted_rebalance.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_interrupted_rebalance_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_interrupted_rebalance.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_interrupted_rebalance_ec_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 SCHEME=ec IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_interrupted_rebalance.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_create_halfhost:
runs-on: ubuntu-latest
needs: build
@@ -414,24 +342,6 @@ jobs:
echo ""
done
test_level_placement:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_level_placement.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_snapshot:
runs-on: ubuntu-latest
needs: build
@@ -468,42 +378,6 @@ jobs:
echo ""
done
test_old_snapshot:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_snapshot.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_snapshot_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_snapshot.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_minsize_1:
runs-on: ubuntu-latest
needs: build
@@ -540,24 +414,6 @@ jobs:
echo ""
done
test_old_move_reappear:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_move_reappear.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_degraded:
runs-on: ubuntu-latest
needs: build
@@ -648,42 +504,6 @@ jobs:
echo ""
done
test_old_snapshot_chain:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_snapshot_chain.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_snapshot_chain_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_snapshot_chain.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_snapshot_down:
runs-on: ubuntu-latest
needs: build
@@ -720,78 +540,6 @@ jobs:
echo ""
done
test_old_snapshot_down:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_snapshot_down.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_snapshot_down_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_snapshot_down.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_kv_stress:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_kv_stress.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_kv_stress_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_kv_stress.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_splitbrain:
runs-on: ubuntu-latest
needs: build
@@ -882,78 +630,6 @@ jobs:
echo ""
done
test_old_rebalance_verify:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 /root/vitastor/tests/test_rebalance_verify.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_rebalance_verify_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_rebalance_verify.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_rebalance_verify_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_rebalance_verify.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_rebalance_verify_ec_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: OLD=1 SCHEME=ec IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_rebalance_verify.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_dd:
runs-on: ubuntu-latest
needs: build
@@ -1062,7 +738,7 @@ jobs:
echo ""
done
test_old_write:
test_write_no_same:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
@@ -1070,61 +746,7 @@ jobs:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_write.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_write_xor:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=xor /root/vitastor/tests/test_write.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_write_old_iothreads:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=old_iothreads OLD=1 GLOBAL_CONFIG=',"client_iothread_count":4' /root/vitastor/tests/test_write.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_write_no_same:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_write_no_same.sh
run: /root/vitastor/tests/test_write_no_same.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
@@ -1206,78 +828,6 @@ jobs:
echo ""
done
test_checksum:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_checksum.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_checksum:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_checksum.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_corrupt_all:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_corrupt_all.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_corrupt_all:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_corrupt_all.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_reweight_half:
runs-on: ubuntu-latest
needs: build
@@ -1476,42 +1026,6 @@ jobs:
echo ""
done
test_old_resize:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_resize.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_resize_auto:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_resize_auto.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_osd_tags:
runs-on: ubuntu-latest
needs: build
@@ -1602,78 +1116,6 @@ jobs:
echo ""
done
test_old_enospc:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_enospc.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_enospc_xor:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=xor /root/vitastor/tests/test_enospc.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_enospc_imm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_enospc.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_enospc_imm_xor:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 IMMEDIATE_COMMIT=1 SCHEME=xor /root/vitastor/tests/test_enospc.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_scrub:
runs-on: ubuntu-latest
needs: build
@@ -1782,240 +1224,6 @@ jobs:
echo ""
done
test_old_scrub:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_scrub_zero_osd_2:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 ZERO_OSD=2 /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_scrub_xor:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=xor /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_scrub_pg_size_3:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 PG_SIZE=3 /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_scrub_pg_size_6_pg_minsize_4_osd_count_6_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 PG_SIZE=6 PG_MINSIZE=4 OSD_COUNT=6 SCHEME=ec /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_scrub_ec:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 SCHEME=ec /root/vitastor/tests/test_scrub.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_partwr_csum:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_partwr_csum.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_32k_dmj:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_32k_dmj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_metadata false --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_32k_dj:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_32k_dj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_32k:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_32k OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_4k_dmj:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_4k_dmj OLD=1 OSD_ARGS="--data_csum_type crc32c --inmemory_metadata false --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_4k_dj:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_4k_dj OLD=1 OSD_ARGS="--data_csum_type crc32c --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_heal_old_csum_4k:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=old_csum_4k OLD=1 OSD_ARGS="--data_csum_type crc32c" OFFSET_ARGS=$OSD_ARGS /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_nfs:
runs-on: ubuntu-latest
needs: build
@@ -2034,21 +1242,3 @@ jobs:
echo ""
done
test_nfs_unaligned_append:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_nfs_unaligned_append.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
-4
View File
@@ -38,10 +38,6 @@ for my $line (<>)
{
$test_name .= '_antietcd';
}
elsif ($1 eq 'OLD')
{
$test_name =~ s/^test_/test_old_/s;
}
else
{
$test_name .= '_'.lc($1).'_'.$2;
+7 -7
View File
@@ -1,20 +1,20 @@
cmake_minimum_required(VERSION 2.8...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
set(VITASTOR_VERSION "3.0.12")
set(VITASTOR_VERSION "2.4.3")
include(CTest)
add_custom_target(build_tests)
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"
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_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)
+2 -6
View File
@@ -26,15 +26,11 @@ Vitastor поддерживает QEMU-драйвер, протоколы UBLK,
## Презентации и записи докладов
- KuberConf'2025: [видео](https://vitastor.io/presentation/kuberconf.webm)
- Highload'2025: [видео](https://vitastor.io/presentation/hl2025/hl2025.webm),
[на youtube](https://www.youtube.com/watch?v=0R8MLjFtz7g), презентация
([на русском](https://vitastor.io/presentation/hl2025/), [на английском](https://vitastor.io/presentation/hl2025/en.html))
- Highload'2022: презентация ([на русском](https://vitastor.io/presentation/highload/highload.html)),
[видео](https://vitastor.io/presentation/highload/talk.webm)
- DevOpsConf'2021: презентация ([на русском](https://vitastor.io/presentation/devopsconf/devopsconf.html),
[на английском](https://vitastor.io/presentation/devopsconf/devopsconf_en.html)),
[видео](https://vitastor.io/presentation/devopsconf/talk.webm)
- Highload'2022: презентация ([на русском](https://vitastor.io/presentation/highload/highload.html)),
[видео](https://vitastor.io/presentation/highload/talk.webm)
## Документация
+2 -6
View File
@@ -26,15 +26,11 @@ Read more details in the documentation. You can start from here: [Quick Start](d
## Talks and presentations
- KuberConf'2025: [video](https://vitastor.io/presentation/kuberconf.webm)
- Highload'2025: [video](https://vitastor.io/presentation/hl2025/hl2025.webm),
[youtube](https://www.youtube.com/watch?v=0R8MLjFtz7g), presentation
([in Russian](https://vitastor.io/presentation/hl2025/), [in English](https://vitastor.io/presentation/hl2025/en.html))
- Highload'2022: presentation ([in Russian](https://vitastor.io/presentation/highload/highload.html)),
[video](https://vitastor.io/presentation/highload/talk.webm)
- DevOpsConf'2021: presentation ([in Russian](https://vitastor.io/presentation/devopsconf/devopsconf.html),
[in English](https://vitastor.io/presentation/devopsconf/devopsconf_en.html)),
[video](https://vitastor.io/presentation/devopsconf/talk.webm)
- Highload'2022: presentation ([in Russian](https://vitastor.io/presentation/highload/highload.html)),
[video](https://vitastor.io/presentation/highload/talk.webm)
## Documentation
+7 -7
View File
@@ -1,5 +1,5 @@
# Compile stage
FROM golang:trixie AS build
FROM golang:bookworm AS build
ADD go.sum go.mod /app/
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go mod download -x
@@ -9,7 +9,7 @@ RUN perl -i -e '$/ = undef; while(<>) { s/\n\s*(\{\s*\n)/$1\n/g; s/\}(\s*\n\s*)e
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vitastor-csi
# Final stage
FROM debian:trixie
FROM debian:bookworm
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
LABEL description="Vitastor CSI Driver"
@@ -25,20 +25,20 @@ RUN apt-get update && \
# NFS mount dependencies
nfs-common netbase \
# dependencies of qemu-storage-daemon
libaio1t64 libc6 libfuse3-4 libglib2.0-0t64 libgmp10 libgnutls30t64 \
libhogweed6t64 libnettle8t64 libnuma1 libselinux1 liburing2 libzstd1 zlib1g && \
libnuma1 liburing2 libglib2.0-0 libfuse3-3 libaio1 libzstd1 libnettle8 \
libgmp10 libhogweed6 libp11-kit0 libidn2-0 libunistring2 libtasn1-6 libpcre2-8-0 libffi8 && \
apt-get clean && \
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
COPY --from=build /app/vitastor-csi /bin/
RUN (echo deb http://vitastor.io/debian trixie main > /etc/apt/sources.list.d/vitastor.list) && \
RUN (echo deb http://vitastor.io/debian bookworm main > /etc/apt/sources.list.d/vitastor.list) && \
((echo 'Package: *'; echo 'Pin: origin "vitastor.io"'; echo 'Pin-Priority: 1000') > /etc/apt/preferences.d/vitastor.pref) && \
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
apt-get update && \
apt-get install -y vitastor-client ibverbs-providers && \
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.2%2Bds-2%2Bvitastor1/qemu-utils_10.0.2%2Bds-2%2Bvitastor1_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.2%2Bds-2%2Bvitastor1/qemu-block-extra_10.0.2%2Bds-2%2Bvitastor1_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-bookworm-9.2.2%2Bds-1%2Bvitastor4/qemu-utils_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-bookworm-9.2.2%2Bds-1%2Bvitastor4/qemu-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
dpkg -x qemu-utils*.deb tmp1 && \
dpkg -x qemu-block-extra*.deb tmp1 && \
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
+4 -4
View File
@@ -1,5 +1,5 @@
# Compile stage
FROM golang:trixie AS build
FROM golang:bookworm AS build
ADD go.sum go.mod /app/
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go mod download -x
@@ -9,7 +9,7 @@ RUN perl -i -e '$/ = undef; while(<>) { s/\n\s*(\{\s*\n)/$1\n/g; s/\}(\s*\n\s*)e
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vitastor-csi
# Final stage
FROM debian:trixie
FROM debian:bookworm
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
LABEL description="Vitastor CSI Driver"
@@ -36,8 +36,8 @@ ADD deb /deb
RUN apt-get update && \
apt-get -y install /deb/vitastor-client_*.deb && \
wget https://vitastor.io/archive/qemu/qemu-trixie-9.2.2%2Bds-1%2Bvitastor4/qemu-utils_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-trixie-9.2.2%2Bds-1%2Bvitastor4/qemu-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-bookworm-9.2.2%2Bds-1%2Bvitastor4/qemu-utils_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
wget https://vitastor.io/archive/qemu/qemu-bookworm-9.2.2%2Bds-1%2Bvitastor4/qemu-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
dpkg -x qemu-utils*.deb tmp1 && \
dpkg -x qemu-block-extra*.deb tmp1 && \
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.12
VITASTOR_VERSION ?= v2.4.3
all: build push
+1 -1
View File
@@ -49,7 +49,7 @@ spec:
capabilities:
add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v3.0.12
image: vitalif/vitastor-csi:v2.4.3
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.12
image: vitalif/vitastor-csi:v2.4.3
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.12"
vitastorCSIDriverVersion = "2.4.3"
)
// Config struct fills the parameters of request or user input
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
# 26.04 Resolute Raccoon
docker build --build-arg DISTRO=ubuntu --build-arg REL=resolute -t vitastor-buildenv:resolute -f vitastor-buildenv.Dockerfile .
docker run -it --rm -e REL=resolute -v `dirname $0`/../:/root/vitastor vitastor-buildenv:resolute /root/vitastor/debian/vitastor-build.sh
+1 -1
View File
@@ -1,4 +1,4 @@
vitastor (3.0.12-1) unstable; urgency=medium
vitastor (2.4.3-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" || "$REL" = "resolute" ) && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
if [[ "$REL" = "trixie" && -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,6 +1,6 @@
# Build Docker image with Vitastor packages
FROM debian:trixie
FROM debian:bookworm
ADD etc/apt /etc/apt/
RUN apt-get update && apt-get -y install vitastor ibverbs-providers udev systemd qemu-system-x86 qemu-system-common qemu-block-extra qemu-utils jq nfs-common && apt-get clean
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.12
VITASTOR_VERSION ?= v2.4.3
all: build push
+1 -1
View File
@@ -1,3 +1,3 @@
Package: *
Pin: release n=trixie-backports
Pin: release n=bookworm-backports
Pin-Priority: 500
+2 -2
View File
@@ -1,2 +1,2 @@
deb http://vitastor.io/debian trixie main
#deb http://http.debian.net/debian/ trixie-backports main
deb http://vitastor.io/debian bookworm main
deb http://http.debian.net/debian/ bookworm-backports main
@@ -7,7 +7,7 @@ PartOf=vitastor.target
[Service]
Restart=always
EnvironmentFile=/etc/vitastor/docker.conf
ExecStart=bash -c 'docker run --rm -i -v /etc/vitastor:/etc/vitastor -v /dev:/dev -v /run:/run -e SYSTEMD_IN_CHROOT=0 \
ExecStart=bash -c 'docker run --rm -i -v /etc/vitastor:/etc/vitastor -v /dev:/dev -v /run:/run \
--security-opt seccomp=unconfined --privileged --pid=host --log-driver none --network host --name vitastor vitastor:$VITASTOR_VERSION \
sleep.sh'
ExecStartPost=udevadm trigger
+1 -1
View File
@@ -4,7 +4,7 @@
#
# Desired Vitastor version
VITASTOR_VERSION=v3.0.12
VITASTOR_VERSION=v2.4.3
# Additional arguments for all containers
# For example, you may want to specify a custom logging driver here
+3 -2
View File
@@ -2,7 +2,8 @@
set -e
cp -urv /etc/systemd/system/vitastor* /host-etc/systemd/system/
cp -urv /etc/udev/rules.d /host-etc/udev/
cp -urv /etc/default /host-etc/
cp -urv /etc/systemd /host-etc/
cp -urv /etc/udev /host-etc/
cp -urnv /etc/vitastor /host-etc/
cp -urnv /opt/scripts/* /host-bin/
-16
View File
@@ -9,7 +9,6 @@
These parameters apply to OSDs, are fixed at the moment of OSD drive
initialization and can't be changed after it without losing data.
- [meta_format](#meta_format)
- [data_device](#data_device)
- [meta_device](#meta_device)
- [journal_device](#journal_device)
@@ -28,21 +27,6 @@ initialization and can't be changed after it without losing data.
- [data_csum_type](#data_csum_type)
- [csum_block_size](#csum_block_size)
## meta_format
- Type: integer
- Default: 3
OSD store implementation version and on-disk metadata format.
Three versions are currently supported: 3, 2 and 1.
- 3 the new log-structured store, it's overall faster, has lower Write
Amplification, which may be even close to 1 (i.e. almost no extra writes)
if your SSDs support atomic writes (see [atomic_write_size](osd.en.md#atomic_write_size)).
- 2 is the old stable store from Vitastor 0.9-2.x.
- 1 is the same old store but with a legacy metadata format from Vitastor
versions to up 0.8.x, without any support for checksums.
## data_device
- Type: string
-18
View File
@@ -10,7 +10,6 @@
дисковые параметры, задаются в момент инициализации дисков OSD и не могут быть
изменены после этого без потери данных.
- [meta_format](#meta_format)
- [data_device](#data_device)
- [meta_device](#meta_device)
- [journal_device](#journal_device)
@@ -29,23 +28,6 @@
- [data_csum_type](#data_csum_type)
- [csum_block_size](#csum_block_size)
## meta_format
- Тип: целое число
- Значение по умолчанию: 3
Версия реализации дискового хранилища OSD и дискового формата метаданных.
Поддерживаются три версии: 3, 2 и 1.
- 3 - новое лог-структурированное хранилище, в целом более быстрое, со
сниженным фактором амплификации записи, который может составлять около 1
(то есть, практически без лишней служебной записи), если ваши SSD
поддерживают атомарную запись (см. [atomic_write_size](osd.ru.md#atomic_write_size)).
- 2 - старое стабильное хранилище из версий Vitastor 0.9-2.x.
- 1 - то же самое стабильное хранилище, но с ещё более старым форматом
метаданных из версий Vitastor до 0.8.x, без какой-либо поддержки
контрольных сумм.
## data_device
- Тип: строка
+28 -17
View File
@@ -22,6 +22,7 @@ between clients, OSDs and etcd.
- [rdma_max_msg](#rdma_max_msg)
- [rdma_max_recv](#rdma_max_recv)
- [rdma_max_send](#rdma_max_send)
- [rdma_odp](#rdma_odp)
- [peer_connect_interval](#peer_connect_interval)
- [peer_connect_timeout](#peer_connect_timeout)
- [osd_idle_timeout](#osd_idle_timeout)
@@ -101,6 +102,11 @@ found or if `osd_network` is not specified. Auto-selection is also
unsupported with old libibverbs < v32, like in Debian 10 Buster or
CentOS 7.
Vitastor supports all adapters, even ones without ODP support, like
Mellanox ConnectX-3 and non-Mellanox cards. Versions up to Vitastor
1.2.0 required ODP which is only present in Mellanox ConnectX >= 4.
See also [rdma_odp](#rdma_odp).
Run `ibv_devinfo -v` as root to list available RDMA devices and their
features.
@@ -110,23 +116,6 @@ the manual of your network vendor for details about setting up the switch
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
Vitastor supports all adapters, even ones without ODP (On-Demand Paging)
support, like Mellanox ConnectX-3 and non-Mellanox cards. ODP is only present
in Mellanox ConnectX >= 4 adapters and allows to skip memory registration
for RDMA and thus, in theory, avoid memory copying.
Versions up to Vitastor 1.2.0 required ODP, then it was disabled by default,
but it was still supported up to 3.0.3. Now ODP support is removed because it
actually only hurts performance: an example 3-node cluster with 8 NVMe in each
node and 2*25 GBit/s ConnectX-6 RDMA network pushed 3950000 read iops without
ODP, but only 239000 iops with ODP.
This happens because Mellanox ODP implementation seems to be based on
message retransmissions when the adapter doesn't know about the buffer yet -
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
which is generally slow in RDMA/RoCE networks. Here's a presentation about
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
## rdma_port_num
- Type: integer
@@ -198,6 +187,28 @@ less than `rdma_max_recv` so the receiving side doesn't run out of buffers.
Doesn't affect memory usage - additional memory isn't allocated for send
operations.
## rdma_odp
- Type: boolean
- Default: false
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
copying during sending. One would think this should improve performance, but
**in reality** RDMA performance with ODP is **drastically** worse. Example
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
This happens because Mellanox ODP implementation seems to be based on
message retransmissions when the adapter doesn't know about the buffer yet -
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
which is generally slow in RDMA/RoCE networks. Here's a presentation about
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
ODP support is retained in the code just in case a good ODP implementation
appears one day.
## peer_connect_interval
- Type: seconds
+30 -18
View File
@@ -22,6 +22,7 @@
- [rdma_max_msg](#rdma_max_msg)
- [rdma_max_recv](#rdma_max_recv)
- [rdma_max_send](#rdma_max_send)
- [rdma_odp](#rdma_odp)
- [peer_connect_interval](#peer_connect_interval)
- [peer_connect_timeout](#peer_connect_timeout)
- [osd_idle_timeout](#osd_idle_timeout)
@@ -100,6 +101,12 @@ RoCEv1/RoCEv2, и даже позволяет полностью отключи
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
Vitastor поддерживает все модели адаптеров, включая те, у которых
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
картами производства не Mellanox. Версии Vitastor до 1.2.0 включительно
требовали ODP, который есть только на Mellanox ConnectX 4 и более новых.
См. также [rdma_odp](#rdma_odp).
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
список доступных RDMA-устройств, их параметры и возможности.
@@ -110,24 +117,6 @@ libibverbs < v32, например в Debian 10 Buster или CentOS 7.
подразумевает настройку сети без потерь на основе PFC (Priority Flow
Control) и ECN (Explicit Congestion Notification).
Vitastor поддерживает все модели адаптеров, включая те, у которых нет
поддержки ODP (On-Demand Paging), например, ConnectX-3 и карты производства
не Mellanox. Функция ODP доступна только на адаптерах Mellanox ConnectX-4 и
более новых и позволяет не регистрировать память для её использования RDMA-картой,
благодаря чему в теории можно избежать лишних копирований памяти.
Версии Vitastor до 1.2.0 включительно требовали ODP, потом функция был отключена
по умолчанию, но поддерживалась вплоть до версии 3.0.3. Сейчас поддержка ODP
полностью удалена, так как на самом деле она только портит производительность:
например, на 3-узловом кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на
чтение с RDMA без ODP удаётся снять 3950000 iops, а с ODP - всего 239000 iops.
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
основана на повторной передаче сообщений, когда карте не известен буфер -
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
## rdma_port_num
- Тип: целое число
@@ -203,6 +192,29 @@ OSD в любом случае согласовывают реальное зн
Не влияет на потребление памяти - дополнительная память на операции отправки
не выделяется.
## rdma_odp
- Тип: булево (да/нет)
- Значение по умолчанию: false
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
не регистрировать память для её использования RDMA-картой. Благодаря этому
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
улучшать производительность - но **по факту** получается так, что
производительность только ухудшается, причём сильно. Пример - на 3-узловом
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
основана на повторной передаче сообщений, когда карте не известен буфер -
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
Возможность использования ODP сохранена в коде на случай, если вдруг в один
прекрасный день появится хорошая реализация ODP.
## peer_connect_interval
- Тип: секунды
+7 -101
View File
@@ -38,7 +38,6 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [journal_io](#journal_io)
- [journal_sector_buffer_count](#journal_sector_buffer_count)
- [journal_no_same_sector_overwrites](#journal_no_same_sector_overwrites)
- [skip_corrupted_meta_entries](#skip_corrupted_meta_entries)
- [throttle_small_writes](#throttle_small_writes)
- [throttle_target_iops](#throttle_target_iops)
- [throttle_target_mbs](#throttle_target_mbs)
@@ -66,11 +65,6 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [allow_net_split](#allow_net_split)
- [enable_pg_locks](#enable_pg_locks)
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
- [atomic_write_size](#atomic_write_size)
- [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
@@ -281,19 +275,13 @@ Maximum number of journal flushers (see above min_flusher_count).
- Type: boolean
- Default: true
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2).
This parameter makes Vitastor keep a copy of metadata area in memory as it is
on disk, in addition to the metadata database. When the option is enabled, every
metadata entry is effectively stored in RAM twice. It's required for good performance
because it allows to avoid additional read-modify-write cycles during metadata
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB
of data. You can turn the option off to reduce memory usage by this value, but
it will reduce performance.
For the new store ([meta_format](layout-osd.en.md#meta_format) 3), the option
may be changed in the future to support operation without loading full metadata
database in memory.
This parameter makes Vitastor always keep metadata area of the block device
in memory. It's required for good performance because it allows to avoid
additional read-modify-write cycles during metadata modifications. Metadata
area size is currently roughly 224 MB per 1 TB of data. You can turn it off
to reduce memory usage by this value, but it will hurt performance. This
restriction is likely to be removed in the future along with the upgrade
of the metadata storage scheme.
## inmemory_journal
@@ -372,8 +360,6 @@ blocks. The only situation when you should increase it to a larger value
is when you enable journal_no_same_sector_overwrites. In this case set
it to, for example, 1024.
Not applicable to the new store ([meta_format](layout-osd.en.md#meta_format) 3).
## journal_no_same_sector_overwrites
- Type: boolean
@@ -387,17 +373,6 @@ journal after writing it instead of possibly overwriting it the second time.
Most (99%) other SSDs don't need this option.
Not applicable to the new store ([meta_format](layout-osd.en.md#meta_format) 3).
## skip_corrupted_meta_entries
- Type: boolean
- Default: false
Only for the new store ([meta_format](layout-osd.en.md#meta_format) 3).
Allow OSD to start when some metadata entries or blocks are corrupted by
skipping them. Should be only used as an emergency measure.
## throttle_small_writes
- Type: boolean
@@ -691,72 +666,3 @@ Use this parameter to enable or disable this function for all pools.
- Default: 100
Retry interval for failed PG lock attempts.
## atomic_write_size
- Type: integer
- Default: 4096
Maximum data device atomic write size allowed for OSD to use.
Atomic writes allow to reduce the Write Amplification factor with the new store
([meta_format](layout-osd.en.md#meta_format)=3) to almost 1 (i.e. almost no extra writes)
with replicated pools and reach the best possible write performance.
Default value is auto-detected during OSD initialization from
`/sys/block/xx/queue/atomic_write_max_bytes` or assumed to be 4096 bytes
because all known disks support 4 KB atomic writes. Auto-detection is only used for
NVMe disks because SAS disks require the explicit WRITE ATOMIC command which requires
RWF_ATOMIC (see below [#use_atomic_flag]) but that flag works incorrectly in current
Linux versions.
You can also check if your NVMe drives support atomic writes by running
the command `nvme id-ctrl /dev/nvme0n1 | grep awupf`. If the reported value,
plus 1, multiplied by the currently selected block size of the NVMe,
is more than 4 KB, then the new store can utilize it for better performance.
The only drives known to support it currently are [Micron and Kioxia](../intro/quickstart.en.md).
Atomic writes allow to skip double data writes in replicated pools, thus
reducing Write Amplification and improving write performance up to 2 times.
## use_atomic_flag
- Type: boolean
This option controls whether Vitastor OSDs use RWF_ATOMIC write flag with atomic writes.
This flag is supported since Linux 6.11 and adds some safety to atomic writes - the kernel
guarantees to not fragment write requests with it and also to check them against the actual
device atomic write capabilities.
However, the option is disabled by default because the flag is currently UNUSABLE - Linux
incorrectly requires writes with that flag to be of power-of-2 length and length-aligned.
I.e., for example, 12 KB writes and not-8-KB aligned 8 KB writes are forbidden by the kernel,
even though the NVMe specification allows them.
For NVMe disks with `scheduler=none` writes aren't fragmented anyway so it's not a big deal.
However, you can rebuild your kernel with [this patch](../../patches/linux-fix-atomic-write-checks.diff)
and turn this option on. It will make your atomic writes a bit safer.
## pg_reshard_chunk_size
- Type: integer
- Default: 100000
Pool PG count change is a CPU-intensive operation because OSDs store the full object database
in memory and have to move all entries between old and new PGs. Thus it's performed in chunks,
with pauses between chunks to prevent blocking OSD's event loop and other clients' operations.
This option sets the maximum number of object is a chunk. Moving 100k objects usually takes
50-100ms. Chunk size equal to 0 means unlimited.
## pg_reshard_chunk_pause_ms
- Type: milliseconds
- 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 -108
View File
@@ -39,7 +39,6 @@
- [journal_io](#journal_io)
- [journal_sector_buffer_count](#journal_sector_buffer_count)
- [journal_no_same_sector_overwrites](#journal_no_same_sector_overwrites)
- [skip_corrupted_meta_entries](#skip_corrupted_meta_entries)
- [throttle_small_writes](#throttle_small_writes)
- [throttle_target_iops](#throttle_target_iops)
- [throttle_target_mbs](#throttle_target_mbs)
@@ -67,11 +66,6 @@
- [allow_net_split](#allow_net_split)
- [enable_pg_locks](#enable_pg_locks)
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
- [atomic_write_size](#atomic_write_size)
- [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
@@ -289,19 +283,13 @@ Flusher - это микро-поток (корутина), которая коп
- Тип: булево (да/нет)
- Значение по умолчанию: true
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2).
Данный параметр заставляет Vitastor всегда держать копию области метаданных
в памяти в том же виде, как она лежит на диске, в дополнение к БД метаданных.
То есть, с включённой опцией каждая запись метаданных хранится в памяти дважды.
Это нужно, чтобы избегать дополнительных операций чтения с диска при записи.
Размер области метаданных в старом хранилище составляет примерно 224 МБ на
1 ТБ данных. Вы можете отключить опцию, чтобы снизить потребление памяти
примерно на эту величину, но при этом также снизится и производительность.
Для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3) опция,
возможно, будет переработана в будущем для поддержки работы без полной
загрузки метаданных в памяти.
Данный параметр заставляет Vitastor всегда держать область метаданных диска
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
при записи. Размер области метаданных на данный момент составляет примерно
224 МБ на 1 ТБ данных. При включении потребление памяти снизится примерно
на эту величину, но при этом также снизится и производительность. В будущем,
после обновления схемы хранения метаданных, это ограничение, скорее всего,
будет ликвидировано.
## inmemory_journal
@@ -384,8 +372,6 @@ fsync небезопасным даже с режимом "directsync".
нужно менять - это если вы включаете journal_no_same_sector_overwrites. В
этом случае установите данный параметр, например, в 1024.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
## journal_no_same_sector_overwrites
- Тип: булево (да/нет)
@@ -401,18 +387,6 @@ fsync небезопасным даже с режимом "directsync".
Почти все другие SSD (99% моделей) не требуют данной опции.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
## skip_corrupted_meta_entries
- Тип: булево (да/нет)
- Значение по умолчанию: false
Только для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3).
Разрешить OSD запускаться, даже если часть блоков или записей метаданных
повреждена, пропуская их. Опция предназначена для использования только в
целях аварийного восстановления.
## throttle_small_writes
- Тип: булево (да/нет)
@@ -725,78 +699,3 @@ pg_minsize OSD во время переключений, что может по
- Значение по умолчанию: 100
Интервал повтора неудачных попыток блокировки PG.
## atomic_write_size
- Тип: целое число
- Значение по умолчанию: 4096
Максимальный размер атомарной записи на диск данных, который OSD разрешено использовать.
Поддержка атомарной записи позволяет снизить мультипликатор записи (Write Amplification)
на диск с новым хранилищем ([meta_format](layout-osd.ru.md#meta_format)=3)
практически до 1 (то есть, почти до нулевого объёма лишней записи) в реплицированных
пулах и достигнуть наилучшей возможной производительности записи.
Значение по умолчанию авто-определяется во время инициализации OSD из
`/sys/block/xx/queue/atomic_write_max_bytes` либо принимается равным 4096,
так как все известные диски поддерживают атомарную запись 4 КБ блоков.
Автоопределение применяется только для NVMe-дисков, так как SAS диски требуют
использования отдельной команды WRITE ATOMIC, а для неё нужен флаг RWF_ATOMIC
(см. ниже [#use_atomic_flag]), а он в текущих версиях Linux работает некорректно.
Вы также можете проверить, поддерживают ли ваши NVMe-диски атомарную запись,
с помощью команды `nvme id-ctrl /dev/nvme0n1 | grep awupf`. Если значение awupf
плюс 1, умноженное на текущий выбранный размер блока NVMe-диска, больше 4 КБ,
то новое хранилище может использовать атомарные записи для достижения лучшей
производительности. Единственные известные диски, которые поддерживают это сейчас -
[Micron и Kioxia](../intro/quickstart.ru.md).
Атомарная запись позволяет не использовать двойную запись данных (в журнал и на
устройство данных) в реплицированных пулах и таким образом снижает амплификацию
записи (объём служебной записи на диск) и улучшает производительность записи
вплоть до 2-х кратного прироста.
## use_atomic_flag
- Тип: булево (да/нет)
Данная опция контролирует использование Vitastor OSD флага RWF_ATOMIC при атомарной записи
блоков. Этот флаг поддерживается, начиная с версии ядра Linux 6.11 и добавляет немного корректности
атомарным записям - ядро гарантирует отсутствие фрагментации запросов записи с этим флагом и
проверяет их на соответствие реальным возможностям устройства.
Однако, данная опция по умолчанию отключена, так как флаг в текущих версиях Linux работает
абсолютно НЕКОРРЕКТНО - при нём Linux требует, чтобы запросы записи имели длину, равную
степени двойки и были выровнены на эту длину. То есть, например, 12 КБ запросы записи, а также
8 КБ запросы записи по не-кратному 8 КБ смещению запрещаются ядром, хотя спецификация NVMe их
разрешает.
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
## pg_reshard_chunk_size
- Тип: целое число
- Значение по умолчанию: 100000
Изменение числа PG в пуле заметно загружает процессор, так как OSD хранят полную базу данных
объектов в памяти и им приходится перемещать все записи объектов между старыми и новыми PG.
Поэтому изменение применяется порциями, с паузами между порциями, чтобы не блокировать обработку
событий OSD и операции остальных клиентов. Данная опция задаёт максимальное число объектов
в порции. Перемещение 100 тысяч объектов (значение по умолчанию) обычно занимает порядка
50-100 миллисекунд. Значение опции 0 отключает лимит размера порции.
## pg_reshard_chunk_pause_ms
- Тип: миллисекунды
- Значение по умолчанию: 100
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
## gc_on_start
- Тип: булево (да/нет)
Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
-25
View File
@@ -1,28 +1,3 @@
- name: meta_format
type: int
default: 3
info: |
OSD store implementation version and on-disk metadata format.
Three versions are currently supported: 3, 2 and 1.
- 3 the new log-structured store, it's overall faster, has lower Write
Amplification, which may be even close to 1 (i.e. almost no extra writes)
if your SSDs support atomic writes (see [atomic_write_size](osd.en.md#atomic_write_size)).
- 2 is the old stable store from Vitastor 0.9-2.x.
- 1 is the same old store but with a legacy metadata format from Vitastor
versions to up 0.8.x, without any support for checksums.
info_ru: |
Версия реализации дискового хранилища OSD и дискового формата метаданных.
Поддерживаются три версии: 3, 2 и 1.
- 3 - новое лог-структурированное хранилище, в целом более быстрое, со
сниженным фактором амплификации записи, который может составлять около 1
(то есть, практически без лишней служебной записи), если ваши SSD
поддерживают атомарную запись (см. [atomic_write_size](osd.ru.md#atomic_write_size)).
- 2 - старое стабильное хранилище из версий Vitastor 0.9-2.x.
- 1 - то же самое стабильное хранилище, но с ещё более старым форматом
метаданных из версий Vitastor до 0.8.x, без какой-либо поддержки
контрольных сумм.
- name: data_device
type: string
info: |
+50 -35
View File
@@ -84,6 +84,11 @@
unsupported with old libibverbs < v32, like in Debian 10 Buster or
CentOS 7.
Vitastor supports all adapters, even ones without ODP support, like
Mellanox ConnectX-3 and non-Mellanox cards. Versions up to Vitastor
1.2.0 required ODP which is only present in Mellanox ConnectX >= 4.
See also [rdma_odp](#rdma_odp).
Run `ibv_devinfo -v` as root to list available RDMA devices and their
features.
@@ -92,23 +97,6 @@
the manual of your network vendor for details about setting up the switch
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
Vitastor supports all adapters, even ones without ODP (On-Demand Paging)
support, like Mellanox ConnectX-3 and non-Mellanox cards. ODP is only present
in Mellanox ConnectX >= 4 adapters and allows to skip memory registration
for RDMA and thus, in theory, avoid memory copying.
Versions up to Vitastor 1.2.0 required ODP, then it was disabled by default,
but it was still supported up to 3.0.3. Now ODP support is removed because it
actually only hurts performance: an example 3-node cluster with 8 NVMe in each
node and 2*25 GBit/s ConnectX-6 RDMA network pushed 3950000 read iops without
ODP, but only 239000 iops with ODP.
This happens because Mellanox ODP implementation seems to be based on
message retransmissions when the adapter doesn't know about the buffer yet -
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
which is generally slow in RDMA/RoCE networks. Here's a presentation about
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
info_ru: |
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
Если не указано, Vitastor попробует найти RoCE-устройство, соответствующее
@@ -117,6 +105,12 @@
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
Vitastor поддерживает все модели адаптеров, включая те, у которых
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
картами производства не Mellanox. Версии Vitastor до 1.2.0 включительно
требовали ODP, который есть только на Mellanox ConnectX 4 и более новых.
См. также [rdma_odp](#rdma_odp).
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
список доступных RDMA-устройств, их параметры и возможности.
@@ -126,24 +120,6 @@
коммутатора для RoCEv2 ищите в документации производителя. Обычно это
подразумевает настройку сети без потерь на основе PFC (Priority Flow
Control) и ECN (Explicit Congestion Notification).
Vitastor поддерживает все модели адаптеров, включая те, у которых нет
поддержки ODP (On-Demand Paging), например, ConnectX-3 и карты производства
не Mellanox. Функция ODP доступна только на адаптерах Mellanox ConnectX-4 и
более новых и позволяет не регистрировать память для её использования RDMA-картой,
благодаря чему в теории можно избежать лишних копирований памяти.
Версии Vitastor до 1.2.0 включительно требовали ODP, потом функция был отключена
по умолчанию, но поддерживалась вплоть до версии 3.0.3. Сейчас поддержка ODP
полностью удалена, так как на самом деле она только портит производительность:
например, на 3-узловом кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на
чтение с RDMA без ODP удаётся снять 3950000 iops, а с ODP - всего 239000 iops.
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
основана на повторной передаче сообщений, когда карте не известен буфер -
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
- name: rdma_port_num
type: int
info: |
@@ -242,6 +218,45 @@
у принимающей стороны в процессе работы не заканчивались буферы на приём.
Не влияет на потребление памяти - дополнительная память на операции отправки
не выделяется.
- name: rdma_odp
type: bool
default: false
online: false
info: |
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
copying during sending. One would think this should improve performance, but
**in reality** RDMA performance with ODP is **drastically** worse. Example
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
This happens because Mellanox ODP implementation seems to be based on
message retransmissions when the adapter doesn't know about the buffer yet -
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
which is generally slow in RDMA/RoCE networks. Here's a presentation about
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
ODP support is retained in the code just in case a good ODP implementation
appears one day.
info_ru: |
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
не регистрировать память для её использования RDMA-картой. Благодаря этому
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
улучшать производительность - но **по факту** получается так, что
производительность только ухудшается, причём сильно. Пример - на 3-узловом
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
основана на повторной передаче сообщений, когда карте не известен буфер -
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
Возможность использования ODP сохранена в коде на случай, если вдруг в один
прекрасный день появится хорошая реализация ODP.
- name: peer_connect_interval
type: sec
min: 1
+14 -155
View File
@@ -253,33 +253,21 @@
type: bool
default: true
info: |
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2).
This parameter makes Vitastor keep a copy of metadata area in memory as it is
on disk, in addition to the metadata database. When the option is enabled, every
metadata entry is effectively stored in RAM twice. It's required for good performance
because it allows to avoid additional read-modify-write cycles during metadata
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB
of data. You can turn the option off to reduce memory usage by this value, but
it will reduce performance.
For the new store ([meta_format](layout-osd.en.md#meta_format) 3), the option
may be changed in the future to support operation without loading full metadata
database in memory.
This parameter makes Vitastor always keep metadata area of the block device
in memory. It's required for good performance because it allows to avoid
additional read-modify-write cycles during metadata modifications. Metadata
area size is currently roughly 224 MB per 1 TB of data. You can turn it off
to reduce memory usage by this value, but it will hurt performance. This
restriction is likely to be removed in the future along with the upgrade
of the metadata storage scheme.
info_ru: |
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2).
Данный параметр заставляет Vitastor всегда держать копию области метаданных
в памяти в том же виде, как она лежит на диске, в дополнение к БД метаданных.
То есть, с включённой опцией каждая запись метаданных хранится в памяти дважды.
Это нужно, чтобы избегать дополнительных операций чтения с диска при записи.
Размер области метаданных в старом хранилище составляет примерно 224 МБ на
1 ТБ данных. Вы можете отключить опцию, чтобы снизить потребление памяти
примерно на эту величину, но при этом также снизится и производительность.
Для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3) опция,
возможно, будет переработана в будущем для поддержки работы без полной
загрузки метаданных в памяти.
Данный параметр заставляет Vitastor всегда держать область метаданных диска
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
при записи. Размер области метаданных на данный момент составляет примерно
224 МБ на 1 ТБ данных. При включении потребление памяти снизится примерно
на эту величину, но при этом также снизится и производительность. В будущем,
после обновления схемы хранения метаданных, это ограничение, скорее всего,
будет ликвидировано.
- name: inmemory_journal
type: bool
default: true
@@ -398,15 +386,11 @@
blocks. The only situation when you should increase it to a larger value
is when you enable journal_no_same_sector_overwrites. In this case set
it to, for example, 1024.
Not applicable to the new store ([meta_format](layout-osd.en.md#meta_format) 3).
info_ru: |
Максимальное число буферов, разрешённых для использования под записываемые
в журнал блоки метаданных. Единственная ситуация, в которой этот параметр
нужно менять - это если вы включаете journal_no_same_sector_overwrites. В
этом случае установите данный параметр, например, в 1024.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
- name: journal_no_same_sector_overwrites
type: bool
default: false
@@ -418,8 +402,6 @@
journal after writing it instead of possibly overwriting it the second time.
Most (99%) other SSDs don't need this option.
Not applicable to the new store ([meta_format](layout-osd.en.md#meta_format) 3).
info_ru: |
Включайте данную опцию для SSD вроде Intel D3-S4510 и D3-S4610, которые
ОЧЕНЬ не любят, когда ПО перезаписывает один и тот же сектор несколько раз
@@ -430,20 +412,6 @@
самого сектора.
Почти все другие SSD (99% моделей) не требуют данной опции.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
- name: skip_corrupted_meta_entries
type: bool
default: false
info: |
Only for the new store ([meta_format](layout-osd.en.md#meta_format) 3).
Allow OSD to start when some metadata entries or blocks are corrupted by
skipping them. Should be only used as an emergency measure.
info_ru: |
Только для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3).
Разрешить OSD запускаться, даже если часть блоков или записей метаданных
повреждена, пропуская их. Опция предназначена для использования только в
целях аварийного восстановления.
- name: throttle_small_writes
type: bool
default: false
@@ -833,112 +801,3 @@
default: 100
info: Retry interval for failed PG lock attempts.
info_ru: Интервал повтора неудачных попыток блокировки PG.
- name: atomic_write_size
type: int
default: 4096
info: |
Maximum data device atomic write size allowed for OSD to use.
Atomic writes allow to reduce the Write Amplification factor with the new store
([meta_format](layout-osd.en.md#meta_format)=3) to almost 1 (i.e. almost no extra writes)
with replicated pools and reach the best possible write performance.
Default value is auto-detected during OSD initialization from
`/sys/block/xx/queue/atomic_write_max_bytes` or assumed to be 4096 bytes
because all known disks support 4 KB atomic writes. Auto-detection is only used for
NVMe disks because SAS disks require the explicit WRITE ATOMIC command which requires
RWF_ATOMIC (see below [#use_atomic_flag]) but that flag works incorrectly in current
Linux versions.
You can also check if your NVMe drives support atomic writes by running
the command `nvme id-ctrl /dev/nvme0n1 | grep awupf`. If the reported value,
plus 1, multiplied by the currently selected block size of the NVMe,
is more than 4 KB, then the new store can utilize it for better performance.
The only drives known to support it currently are [Micron and Kioxia](../intro/quickstart.en.md).
Atomic writes allow to skip double data writes in replicated pools, thus
reducing Write Amplification and improving write performance up to 2 times.
info_ru: |
Максимальный размер атомарной записи на диск данных, который OSD разрешено использовать.
Поддержка атомарной записи позволяет снизить мультипликатор записи (Write Amplification)
на диск с новым хранилищем ([meta_format](layout-osd.ru.md#meta_format)=3)
практически до 1 (то есть, почти до нулевого объёма лишней записи) в реплицированных
пулах и достигнуть наилучшей возможной производительности записи.
Значение по умолчанию авто-определяется во время инициализации OSD из
`/sys/block/xx/queue/atomic_write_max_bytes` либо принимается равным 4096,
так как все известные диски поддерживают атомарную запись 4 КБ блоков.
Автоопределение применяется только для NVMe-дисков, так как SAS диски требуют
использования отдельной команды WRITE ATOMIC, а для неё нужен флаг RWF_ATOMIC
(см. ниже [#use_atomic_flag]), а он в текущих версиях Linux работает некорректно.
Вы также можете проверить, поддерживают ли ваши NVMe-диски атомарную запись,
с помощью команды `nvme id-ctrl /dev/nvme0n1 | grep awupf`. Если значение awupf
плюс 1, умноженное на текущий выбранный размер блока NVMe-диска, больше 4 КБ,
то новое хранилище может использовать атомарные записи для достижения лучшей
производительности. Единственные известные диски, которые поддерживают это сейчас -
[Micron и Kioxia](../intro/quickstart.ru.md).
Атомарная запись позволяет не использовать двойную запись данных (в журнал и на
устройство данных) в реплицированных пулах и таким образом снижает амплификацию
записи (объём служебной записи на диск) и улучшает производительность записи
вплоть до 2-х кратного прироста.
- name: use_atomic_flag
type: bool
info: |
This option controls whether Vitastor OSDs use RWF_ATOMIC write flag with atomic writes.
This flag is supported since Linux 6.11 and adds some safety to atomic writes - the kernel
guarantees to not fragment write requests with it and also to check them against the actual
device atomic write capabilities.
However, the option is disabled by default because the flag is currently UNUSABLE - Linux
incorrectly requires writes with that flag to be of power-of-2 length and length-aligned.
I.e., for example, 12 KB writes and not-8-KB aligned 8 KB writes are forbidden by the kernel,
even though the NVMe specification allows them.
For NVMe disks with `scheduler=none` writes aren't fragmented anyway so it's not a big deal.
However, you can rebuild your kernel with [this patch](../../patches/linux-fix-atomic-write-checks.diff)
and turn this option on. It will make your atomic writes a bit safer.
info_ru: |
Данная опция контролирует использование Vitastor OSD флага RWF_ATOMIC при атомарной записи
блоков. Этот флаг поддерживается, начиная с версии ядра Linux 6.11 и добавляет немного корректности
атомарным записям - ядро гарантирует отсутствие фрагментации запросов записи с этим флагом и
проверяет их на соответствие реальным возможностям устройства.
Однако, данная опция по умолчанию отключена, так как флаг в текущих версиях Linux работает
абсолютно НЕКОРРЕКТНО - при нём Linux требует, чтобы запросы записи имели длину, равную
степени двойки и были выровнены на эту длину. То есть, например, 12 КБ запросы записи, а также
8 КБ запросы записи по не-кратному 8 КБ смещению запрещаются ядром, хотя спецификация NVMe их
разрешает.
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
- name: pg_reshard_chunk_size
type: int
default: 100000
info: |
Pool PG count change is a CPU-intensive operation because OSDs store the full object database
in memory and have to move all entries between old and new PGs. Thus it's performed in chunks,
with pauses between chunks to prevent blocking OSD's event loop and other clients' operations.
This option sets the maximum number of object is a chunk. Moving 100k objects usually takes
50-100ms. Chunk size equal to 0 means unlimited.
info_ru: |
Изменение числа PG в пуле заметно загружает процессор, так как OSD хранят полную базу данных
объектов в памяти и им приходится перемещать все записи объектов между старыми и новыми PG.
Поэтому изменение применяется порциями, с паузами между порциями, чтобы не блокировать обработку
событий OSD и операции остальных клиентов. Данная опция задаёт максимальное число объектов
в порции. Перемещение 100 тысяч объектов (значение по умолчанию) обычно занимает порядка
50-100 миллисекунд. Значение опции 0 отключает лимит размера порции.
- name: pg_reshard_chunk_pause_ms
type: ms
default: 100
info: |
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.
+3 -27
View File
@@ -26,37 +26,13 @@ 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.12`
`docker pull vitalif/vitastor:v2.4.3`
2. Install scripts to the host system: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v2.4.3 install.sh`
3. Reload udev rules: \
`udevadm control --reload-rules`
4. Enable the vitastor-host service: \
`systemctl enable --now vitastor-host`
After these steps, you can return to [Quick Start](../intro/quickstart.en.md).
## Podman
If you use Podman, run the following commands as root before installing Vitastor containers:
```
ln -s podman /usr/bin/docker
mkdir -p /etc/systemd/system/systemd-udevd.service.d
cat >/etc/systemd/system/systemd-udevd.service.d/override.conf <<EOF
[Service]
CapabilityBoundingSet=~
SystemCallFilter=@mount capset
EOF
systemctl daemon-reload
systemctl restart systemd-udevd
```
Without it, udev fails to do calls into a Podman container and Vitastor disk detection doesn't work.
And you can return to [Quick Start](../intro/quickstart.en.md).
## Upgrading Containers
+2 -27
View File
@@ -25,39 +25,14 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
Инструкция по установке максимально простая.
1. Скачайте Docker-образ желаемой версии: \
`docker pull vitalif/vitastor:v3.0.12`
`docker pull vitalif/vitastor:v2.4.3`
2. Установите скрипты в хост-систему командой: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v2.4.3 install.sh`
3. Перезагрузите правила udev: \
`udevadm control --reload-rules`
4. Включите сервис vitastor-host: \
`systemctl enable --now vitastor-host`
После этого вы можете возвращаться к разделу [Быстрый старт](../intro/quickstart.ru.md).
## Podman
Если вы используете Podman, перед установкой контейнеров Vitastor выполните следующие
команды от имени суперпользователя:
```
ln -s podman /usr/bin/docker
mkdir -p /etc/systemd/system/systemd-udevd.service.d
cat >/etc/systemd/system/systemd-udevd.service.d/override.conf <<EOF
[Service]
CapabilityBoundingSet=~
SystemCallFilter=@mount capset
EOF
systemctl daemon-reload
systemctl restart systemd-udevd
```
Без этих настроек udev не может делать вызовы внутрь Podman-контейнеров и определение дисков Vitastor не работает.
## Обновление контейнеров
Сначала обязательно проверьте раздел [Обновление Vitastor](../usage/admin.ru.md#обновление-vitastor),
+1 -4
View File
@@ -17,7 +17,6 @@
- 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`:
@@ -34,17 +33,15 @@
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
- AlmaLinux 9 and other RHEL 9 clones (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
- AlmaLinux 10 and other RHEL 10 clones: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
- Enable EPEL: `yum/dnf install epel-release`
- Enable additional CentOS repositories:
- CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization`
- RHEL 9/10 clones: not required
- RHEL 9 clones: not required
- Enable elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
- RHEL 9 clones: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
- RHEL 10 clones: not required
- Install packages: `yum/dnf install vitastor lpsolve etcd kernel-ml qemu-kvm`
## Installation requirements
+1 -4
View File
@@ -17,7 +17,6 @@
- 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`:
@@ -34,17 +33,15 @@
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
- AlmaLinux 9 и другие клоны RHEL 9 (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
- AlmaLinux 10 и другие клоны RHEL 10: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
- Включите EPEL: `yum/dnf install epel-release`
- Включите дополнительные репозитории CentOS:
- CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization`
- Клоны RHEL 9/10: не нужно
- Клоны RHEL 9: не нужно
- Включите elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
- Клоны RHEL 9: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
- Клоны RHEL 10: не нужно
- Установите пакеты: `yum/dnf install vitastor lpsolve etcd kernel-ml qemu-kvm`
## Установочные требования
+1 -1
View File
@@ -6,7 +6,7 @@
# Proxmox VE
To enable Vitastor support in Proxmox Virtual Environment (6.4-9.x are supported):
To enable Vitastor support in Proxmox Virtual Environment (6.4-8.x are supported):
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
trixie for 9.0+, bookworm for 8.1+, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1, buster for 6.4
+1 -1
View File
@@ -6,7 +6,7 @@
# Proxmox VE
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-9.x):
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-8.x):
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
trixie для 9.0+, bookworm для 8.1+, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1, buster для 6.4
+2 -1
View File
@@ -16,7 +16,8 @@
designated initializers support from C++20
- CMake
- jerasure headers and libraries
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
- ISA-L, libibverbs and librdmacm headers and libraries (optional)
- tcmalloc (google-perftools-dev)
## Basic instructions
+2 -1
View File
@@ -16,7 +16,8 @@
назначенных инициализаторов (designated initializers) из C++20
- CMake
- Заголовки и библиотеки jerasure
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm
- tcmalloc (google-perftools-dev)
## Базовая инструкция
-2
View File
@@ -14,8 +14,6 @@
- Basic part: highly-available block storage with symmetric clustering and no SPOF
- [Performance](../performance/bench2.en.md) ;-D
- [NVMe atomic write support](../config/osd.en.md#atomic_write_size) for reducing the amount
of "extra" disk writes to almost zero (Write Amplification = 1)
- [Multiple redundancy schemes](../config/pool.en.md#scheme): Replication, XOR n+1, Reed-Solomon erasure codes
based on jerasure and ISA-L libraries with any number of data and parity drives in a group
- Configuration via simple JSON data structures in etcd (parameters, pools and images)
-2
View File
@@ -14,8 +14,6 @@
- Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа
- [Производительность](../performance/bench2.ru.md) ;-D
- [Поддержка атомарной записи NVMe](../config/osd.ru.md#atomic_write_size) для снижения объёма
служебной записи практически до нуля (Write Amplification = 1)
- [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок
Рида-Соломона на основе библиотек jerasure и ISA-L с любым числом дисков данных и чётности в группе
- Конфигурация через простые человекочитаемые JSON-структуры в etcd
+6 -9
View File
@@ -18,10 +18,9 @@
## Preparation
- Get some SATA or NVMe SSDs with capacitors (server-grade drives). The best performance
is achieved with Micron or Kioxia NVMes with atomic write support (see below). You can use desktop
SSDs with lazy fsync, but prepare for inferior single-thread latency. Read more about
capacitors [here](../config/layout-cluster.en.md#immediate_commit).
- Get some SATA or NVMe SSDs with capacitors (server-grade drives). You can use desktop SSDs
with lazy fsync, but prepare for inferior single-thread latency. Read more about capacitors
[here](../config/layout-cluster.en.md#immediate_commit).
- If you want to use HDDs, get modern HDDs with Media Cache or SSD Cache: HGST Ultrastar,
Toshiba MG, Seagate EXOS or something similar. If your drives don't have such cache then
you also need small SSDs for journal and metadata (even 2 GB per 1 TB of HDD space is enough).
@@ -31,11 +30,9 @@
## Recommended drives
- NVMe with atomic write support (ideal!): Micron 7450/7500/7550, Kioxia CD6/CD7/CD8/CD9
- Other NVMe: Micron 9100/9200/9300/9400/9550, Micron 7300, Samsung PM983/PM9A3, Samsung PM1723/1735/1743,
Intel DC-P3700/P4500/P4600, Intel/Solidigm D5-P4320/P5530, Intel/Solidigm D7-P5500/P5600, Solidigm D7-PS1010/PS1030/P5810,
Intel Optane, Kingston DC1000B/DC1500M, Kioxia CD6/CD7/CD8/CD9
- SATA SSD: Micron 5100/5200/5300/5400, Samsung PM863/PM883/PM893, Intel/Solidigm D3-S4510/4520/4610/4620, Kingston DC500M
- SATA SSD: Micron 5100/5200/5300/5400, Samsung PM863/PM883/PM893, Intel D3-S4510/4520/4610/4620, Kingston DC500M
- NVMe: Micron 9100/9200/9300/9400, Micron 7300/7450, Samsung PM983/PM9A3, Samsung PM1723/1735/1743,
Intel DC-P3700/P4500/P4600, Intel D5-P4320/P5530, Intel D7-P5500/P5600, Intel Optane, Kingston DC1000B/DC1500M
- HDD: HGST Ultrastar, Toshiba MG, Seagate EXOS
## Configure monitors
+5 -8
View File
@@ -18,9 +18,8 @@
## Подготовка
- Возьмите серверы с SSD (SATA или NVMe), желательно с конденсаторами (серверные SSD). Наилучшая
производительность достигается на дисках Micron и Kioxia с поддержкой атомарной записи (см. ниже).
Можно использовать и десктопные SSD, включив режим отложенного fsync, но производительность будет хуже.
- Возьмите серверы с SSD (SATA или NVMe), желательно с конденсаторами (серверные SSD). Можно
использовать и десктопные SSD, включив режим отложенного fsync, но производительность будет хуже.
О конденсаторах читайте [здесь](../config/layout-cluster.ru.md#immediate_commit).
- Если хотите использовать HDD, берите современные модели с Media или SSD кэшем - HGST Ultrastar,
Toshiba MG, Seagate EXOS или что-то похожее. Если такого кэша у ваших дисков нет,
@@ -31,11 +30,9 @@
## Рекомендуемые диски
- NVMe с поддержкой атомарной записи (идеально!): Micron 7450/7500/7550, Kioxia CD6/CD7/CD8/CD9
- Другие NVMe: Micron 9100/9200/9300/9400/9550, Micron 7300, Samsung PM983/PM9A3, Samsung PM1723/1735/1743,
Intel DC-P3700/P4500/P4600, Intel/Solidigm D5-P4320/P5530, Intel/Solidigm D7-P5500/P5600, Solidigm D7-PS1010/PS1030/P5810,
Intel Optane, Kingston DC1000B/DC1500M, Kioxia CD6/CD7/CD8/CD9
- SATA SSD: Micron 5100/5200/5300/5400, Samsung PM863/PM883/PM893, Intel/Solidigm D3-S4510/4520/4610/4620, Kingston DC500M
- SATA SSD: Micron 5100/5200/5300/5400, Samsung PM863/PM883/PM893, Intel D3-S4510/4520/4610/4620, Kingston DC500M
- NVMe: Micron 9100/9200/9300/9400, Micron 7300/7450, Samsung PM983/PM9A3, Samsung PM1723/1735/1743,
Intel DC-P3700/P4500/P4600, Intel D5-P4320/P5530, Intel D7-P5500/P5600, Intel Optane, Kingston DC1000B/DC1500M
- HDD: HGST Ultrastar, Toshiba MG, Seagate EXOS
## Настройте мониторы
+20 -28
View File
@@ -14,7 +14,7 @@ Replicated setups:
- Linear read: `min(total network bandwidth, sum(disk read MB/s))`.
- Linear write: `min(total network bandwidth, sum(disk write MB/s / number of replicas))`.
- Saturated parallel read iops: `min(total network bandwidth, sum(disk read iops))`.
- Saturated parallel write iops: `min(total network bandwidth / number of replicas, sum(disk write iops / number of replicas / write amplification))`.
- Saturated parallel write iops: `min(total network bandwidth / number of replicas, sum(disk write iops / number of replicas / (write amplification = 4)))`.
EC/XOR setups (EC N+K):
- Single-threaded (T1Q1) read latency: 1.5 network roundtrips + 1 disk read.
@@ -26,36 +26,28 @@ EC/XOR setups (EC N+K):
- Linear read: `min(total network bandwidth, sum(disk read MB/s))`.
- Linear write: `min(total network bandwidth, sum(disk write MB/s * N/(N+K)))`.
- Saturated parallel read iops: `min(total network bandwidth, sum(disk read iops))`.
- Saturated parallel write iops: roughly `total iops / (N+K) / WA`. More exactly:
- With the new store: `min(total network bandwidth * N/(N+K), sum(disk randrw iops / (2 + N-1 + K*2)))`,
with random read/write mix corresponding to `(N-1)/(2 + N-1 + K*2)*100 % reads`.
- For example, with EC 2+1 it is: `(20% randrw iops) / 5`.
- With EC 6+3 it is: `(38% randrw iops) / 13`.
- With the old store: `min(total network bandwidth * N/(N+K), sum(disk randrw iops / (3 + N-1 + K*3)))`,
with random read/write mix corresponding to `(N-1)/(3 + N-1 + K*3)*100 % reads`.
- For example, with EC 2+1 it is: `(14% randrw iops) / 7`.
- With EC 6+3 it is: `(30% randrw iops) / 17`.
- Saturated parallel write iops: roughly `total iops / (N+K) / WA`. More exactly,
`min(total network bandwidth * N/(N+K), sum(disk randrw iops / (N*4 + K*5 + 1)))` with
random read/write mix corresponding to `(N-1)/(N*4 + K*5 + 1)*100 % reads`.
- For example, with EC 2+1 it is: `(7% randrw iops) / 14`.
- With EC 6+3 it is: `(12.5% randrw iops) / 40`.
Write Amplification factor:
- For the new store and for 4 KB writes: WA is always 1 unless you set [atomic_write_size](../config/osd.en.md#atomic_write_size) to 0 manually.
- For the new store and for 8-124 KB writes: WA is 1 if you use NVMe drives with atomic write support, or roughly 2 if you use other drives.
- For the old store, WA is roughly `(2 * write size + 4 KB) / (write size)`. So, for 4 KB writes it's 3, and for 8-124 KB writes it's closer to 2.
- For both the new and the old store and for writes of [block_size](../config/layout-cluster.en.md#block_size): WA is almost 1.
Write amplification for 4 KB blocks is usually 3-5 in Vitastor:
1. Journal block write
2. Journal data write
3. Metadata block write
4. Another journal block write for EC/XOR setups
5. Data block write
Write Amplification consists of:
- For the new store:
- Buffer block write if non-atomic
- Data block write
- Metadata write(s) (amortized)
- For the old store:
- Journal block write (amortized)
- Journal data write
- Metadata block write
- Another journal block write for EC/XOR setups (amortized)
- Data block write
If you manage to get an SSD which handles 512 byte blocks well (Optane?) you may
lower 1, 3 and 4 to 512 bytes (1/8 of data size) and get WA as low as 2.375.
Other possibilities to reduce WA would be to use SSDs with internal 512-byte blocks
or NVDIMM, but both options seem unavailable on the market at the moment.
Implemented NVDIMM support can basically eliminate WA at all - all extra writes will
go to DRAM memory. But this requires a test cluster with NVDIMM - please contact me
if you want to provide me with such cluster for tests.
Lazy fsync also reduces WA for parallel workloads because journal blocks are only
written when they fill up or fsync is requested.
## In Practice
+20 -27
View File
@@ -27,36 +27,29 @@
- Линейное чтение: сумма МБ/с чтения всех дисков, либо общая производительность сети, если в сеть упрётся раньше.
- Линейная запись: сумма МБ/с записи всех дисков * N/(N+K), либо производительность сети * N / (N+K), если в сеть упрётся раньше.
- Параллельное случайное мелкое чтение: сумма IOPS чтения всех дисков либо производительность сети, если в сеть упрётся раньше.
- Параллельная случайная мелкая запись: грубо `(сумма IOPS / (N+K) / WA)`.
Либо `производительность сети * N/(N+K)`, если в сеть упрётся раньше. Если точнее, то:
- С новым хранилищем: сумма смешанного IOPS всех дисков при `(N-1)/(2 + N-1 + K*2)*100 %` чтения, делённая на `(2 + N-1 + K*2)`.
- Например, при EC 2+1 это: `(сумма IOPS при 20% чтения) / 5`.
- При EC 6+3 это: `(сумма IOPS при 38% чтения) / 13`.
- Со старым хранилищем: сумма смешанного IOPS всех дисков при `(N-1)/(3 + N-1 + K*3)*100 %` чтения, делённая на `(3 + N-1 + K*3)`.
- Например, при EC 2+1 это: `(сумма IOPS при 14% чтения) / 7`.
- При EC 6+3 это: `(сумма IOPS при 30% чтения) / 17`.
- Параллельная случайная мелкая запись: грубо `(сумма IOPS / (N+K) / WA)`. Если точнее, то:
сумма смешанного IOPS всех дисков при `(N-1)/(N*4 + K*5 + 1)*100 %` чтения, делённая на `(N*4 + K*5 + 1)`.
Либо, производительность сети * N/(N+K), если в сеть упрётся раньше.
- Например, при EC 2+1 это: `(сумма IOPS при 7% чтения) / 14`.
- При EC 6+3 это: `(сумма IOPS при 12.5% чтения) / 40`.
WA (Write Amplification, мультипликатор записи):
- С новым хранилищем для 4 КБ записи: WA всегда примерно 1, если только вы не установите [atomic_write_size](../config/osd.ru.md#atomic_write_size) вручную в 0.
- С новым хранилищем и большими записями (8-124 КБ): WA примерно 1, если вы используете NVMe-диски с поддержкой атомарной записи,
или примерно 2, если вы используете другие диски.
- Со старым хранилищем, WA примерно `(2 * размер записи + 4 КБ) / (размер записи)`. То есть, для 4 КБ записи WA=3, а для 8-124 КБ WA ближе к 2.
- И с новым, и со старым хранилищем и для записи размером [block_size](../config/layout-cluster.ru.md#block_size): WA примерно равен 1.
WA (мультипликатор записи) для 4 КБ блоков в Vitastor обычно составляет 3-5:
1. Запись метаданных в журнал
2. Запись блока данных в журнал
3. Запись метаданных в БД
4. Ещё одна запись метаданных в журнал при использовании EC
5. Запись блока данных на диск данных
Мультипликатор записи состоит из:
- С новым хранилищем:
- Запись блока буфера, если диски без поддержки атомарной записи
- Запись блока данных
- Запись(-и) блоков метаданных (амортизированные)
- Со старым хранилищем:
- Запись блока журнала (амортизированная)
- Запись данных в журнал
- Запись блока метаданных
- Ещё одна запись блока журнала для EC/XOR пулов (амортизированная)
- Запись блока данных
Если вы найдёте SSD, хорошо работающий с 512-байтными блоками данных (Optane?),
то 1, 3 и 4 можно снизить до 512 байт (1/8 от размера данных) и получить WA всего 2.375.
Другими потенциальными возможностями снижения WA могли бы быть SSD с внутренним 512-байтным блоком
либо NVDIMM, но и то, и другое сейчас выглядит недоступным на рынке.
Если реализовать поддержку NVDIMM, то WA можно, условно говоря, ликвидировать вообще - все
дополнительные операции записи смогут обслуживаться DRAM памятью. Но для этого необходим
тестовый кластер с NVDIMM - пишите, если готовы предоставить такой для тестов.
Кроме того, WA снижается при использовании отложенного/ленивого сброса при параллельной
нагрузке, т.к. блоки журнала записываются на диск только когда они заполняются или явным
образом запрашивается fsync.
## На практике
-12
View File
@@ -231,18 +231,6 @@ Upgrading from <= 0.5.x to >= 0.6.x is not supported.
Downgrade are also allowed freely, except the following specific instructions:
### 3.x -> 2.x
Versions 3.0.0 and newer contain two store implementations - an old one and a new
one, unsupported in 2.x and previous versions. So you should check your OSD store
versions before downgrading to 2.x with the following command:
`vitastor-disk read-sb /dev/vitastor/osdXX-data | jq -r .meta_format`
If it prints 3 then OSD uses the new store and you can't downgrade it to 2.x.
If it prints 2 or nothing then OSD uses the old store and the downgrade is allowed.
### 1.8.0 to 1.7.1
Before downgrading from version >= 1.8.0 to version <= 1.7.1
-12
View File
@@ -228,18 +228,6 @@ done
Откат (понижение версии) тоже свободно разрешён, кроме указанных ниже случаев:
### 3.x -> 2.x
Версии 3.0.0 и более новые содержат две реализации хранилища - старую и новую, не
поддерживаемую в 2.x и предыдущих версиях. Таким образом, перед откатом на 2.x вам
следует проверить, какая версия хранилища используется вашими OSD - командой:
`vitastor-disk read-sb /dev/vitastor/osdXX-data | jq -r .meta_format`
Если выводится 3, это новое хранилище и откатить такой OSD до 2.x нельзя.
Если выводится 2 или не выводится ничего, это старое хранилище и откат разрешён.
### 1.8.0 -> 1.7.1
Перед понижением версии с >= 1.8.0 до <= 1.7.1 вы должны скопировать ключ
-5
View File
@@ -51,9 +51,6 @@ Options (automatic mode):
```
--osd_per_disk <N>
Create <N> OSDs on each disk (default 1)
--meta_format 3
Metadata store version. 3 is the new log-structured store, 2 is the stable store
from Vitastor 0.9-2.x, 1 is the legacy store from Vitastor 0.6-0.8.
--hybrid
Prepare hybrid (HDD+SSD, NVMe+SATA or etc) OSDs using provided devices. By default,
any passed SSDs will be used for journals and metadata, HDDs will be used for data,
@@ -95,8 +92,6 @@ Options (single-device mode):
Options (both modes):
```
--tags tag1,tag2 Set new OSD tag(s)
--weight <number> Set new OSD weight (between 0 to 1)
--journal_size 1G/32M Set journal size (area or partition size)
--block_size 1M/128k Set blockstore object size
--bitmap_granularity 4k Set bitmap granularity
-5
View File
@@ -50,9 +50,6 @@ vitastor-disk - инструмент командной строки для уп
```
--osd_per_disk <N>
Создавать по несколько (<N>) OSD на каждом диске (по умолчанию 1)
--meta_format 3
Версия хранилища метаданных. 3 - новое лог-структурированное хранилище,
2 - стабильное хранилище из Vitastor 0.9-2.x, 1 - старое хранилище из Vitastor 0.6-0.8.
--hybrid
Инициализировать гибридные (HDD+SSD, NVMe+SATA и т.п.) OSD на указанных дисках.
По умолчанию, SSD будут использованы для журналов и метаданных, а HDD - для данных,
@@ -96,8 +93,6 @@ vitastor-disk - инструмент командной строки для уп
Опции для обоих режимов:
```
--tags tag1,tag2 Задать теги для новых OSD
--weight <number> Задать вес для новых OSD (от 0 до 1)
--journal_size 1G/32M Задать размер журнала (области или раздела журнала)
--block_size 1M/128k Задать размер объекта хранилища
--bitmap_granularity 4k Задать гранулярность битовых карт
-1
View File
@@ -262,4 +262,3 @@ 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,4 +274,3 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+1 -1
Submodule json11 updated: edcd85b8bd...fd37016cf8
+1 -1
View File
@@ -16,7 +16,7 @@ async function create_http_server(cfg, handler)
};
if (cfg.mon_https_ca)
{
tls.ca = await fsp.readFile(cfg.mon_https_ca);
tls.mon_https_ca = await fsp.readFile(cfg.mon_https_ca);
}
if (cfg.mon_https_client_auth)
{
+5 -8
View File
@@ -10,19 +10,16 @@ const NO_OSD = 'Z';
async function lp_solve(text)
{
const cp = child_process.spawn('lp_solve');
let stdout = '', stderr = '', finish_cb, finished = 0;
let stdout = '', stderr = '', finish_cb;
cp.stdout.on('data', buf => stdout += buf.toString());
cp.stderr.on('data', buf => stderr += buf.toString());
cp.stdout.on('end', () => finish_cb());
cp.stderr.on('end', () => finish_cb());
cp.on('exit', () => finish_cb && finish_cb());
cp.stdin.write(text);
cp.stdin.end();
await new Promise(ok => (finish_cb = () =>
if (cp.exitCode == null)
{
finished++;
if (finished == 2)
ok();
}));
await new Promise(ok => finish_cb = ok);
}
if (!stdout.trim())
{
return null;
+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] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
pg_history[pg-1] = 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();
}, (this.next_recheck_at-now)*1000);
}, now-this.next_recheck_at);
}
}
+1 -1
View File
@@ -87,7 +87,7 @@ function make_hier_tree(global_config, tree)
tree[''] = { children: [] };
for (const node_id in tree)
{
if (node_id === '')
if (node_id === '' || !(tree[node_id].children||[]).length && (tree[node_id].size||0) <= 0)
{
continue;
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor-mon",
"version": "3.0.12",
"version": "2.4.3",
"description": "Vitastor SDS monitor service",
"main": "mon-main.js",
"scripts": {
@@ -9,7 +9,7 @@
"author": "Vitaliy Filippov",
"license": "UNLICENSED",
"dependencies": {
"antietcd": "^1.2.4",
"antietcd": "^1.1.3",
"sprintf-js": "^1.1.2",
"ws": "^7.2.5"
},
+4 -17
View File
@@ -9,6 +9,7 @@ const LPOptimizer = require('./lp_optimizer/lp_optimizer.js');
const { scale_pg_count } = require('./pg_utils.js');
const { make_hier_tree, filter_osds_by_root_node,
filter_osds_by_tags, filter_osds_by_block_layout, get_affinity_osds } = require('./osd_tree.js');
const { select_murmur3 } = require('./lp_optimizer/murmur3.js');
function pick_primary(pool_id, pg_num, pool_config, osd_set, up_osds, aff_osds)
{
@@ -38,7 +39,7 @@ function pick_primary(pool_id, pg_num, pool_config, osd_set, up_osds, aff_osds)
{
return 0;
}
return alive_set[pg_num % alive_set.length];
return alive_set[select_murmur3(alive_set.length, osd_num => pool_id+'/'+pg_num+'/'+osd_num)];
}
function recheck_primary(state, global_config, up_osds, osd_tree)
@@ -52,7 +53,6 @@ function recheck_primary(state, global_config, up_osds, osd_tree)
continue;
}
const aff_osds = get_affinity_osds(pool_cfg, up_osds, osd_tree);
let paused = false;
for (let pg_num = 1; pg_num <= pool_cfg.pg_count; pg_num++)
{
if (!state.pg.config.items[pool_id])
@@ -75,19 +75,6 @@ function recheck_primary(state, global_config, up_osds, osd_tree)
);
new_pg_config.items[pool_id][pg_num].primary = new_primary;
}
paused = paused || !!pg_cfg.pause;
}
}
if (paused)
{
if (!new_pg_config)
{
new_pg_config = JSON.parse(JSON.stringify(state.pg.config));
}
console.log(`Resuming paused pool ${pool_id}`);
for (const pg in new_pg_config.items[pool_id])
{
delete new_pg_config.items[pool_id][pg].pause;
}
}
}
@@ -192,10 +179,10 @@ async function generate_pool_pgs(state, global_config, pool_id, osd_tree, levels
const rules = use_rules ? get_pg_rules(pool_id, pool_cfg, global_config.placement_levels) : null;
const folded = fold_failure_domains(Object.values(pool_tree), use_rules ? rules : [ [ [ pool_cfg.failure_domain ] ] ]);
// FIXME: Remove/merge make_hier_tree() step somewhere, however it's needed to remove empty nodes
const folded_tree = make_hier_tree(global_config, folded.nodes.reduce((a, c) => { a[c.id] = c; return a; }, {}));
const folded_tree = make_hier_tree(global_config, folded.nodes);
const old_pg_count = prev_pgs.length;
const optimize_cfg = {
osd_weights: folded.nodes.reduce((a, c) => { if (/^\d+$/.exec(c.id) && c.size != null) { a[c.id] = c.size||0; } return a; }, {}),
osd_weights: folded.nodes.reduce((a, c) => { if (Number(c.id)) { a[c.id] = c.size; } return a; }, {}),
combinator: use_rules
// new algorithm:
? new RuleCombinator(folded_tree, rules, pool_cfg.max_osd_combinations)
+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] = JSON.parse(JSON.stringify(merged_history[1]));
new_pg_history[i] = { ...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,7 +37,6 @@ 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.12",
"version": "2.4.3",
"description": "Low-level native bindings to Vitastor client library",
"main": "index.js",
"keywords": [
+232 -30
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.12'
VITASTOR_VERSION = '2.4.3'
LOG = logging.getLogger(__name__)
@@ -275,7 +275,7 @@ class VitastorDriver(driver.CloneableImageVD,
LOG.exception('error getting vitastor pool stats: '+str(e))
self._stats = stats
def get_volume_stats(self, refresh=False):
"""Get volume stats.
If 'refresh' is True, run update the stats first.
@@ -291,14 +291,6 @@ class VitastorDriver(driver.CloneableImageVD,
else:
return (1 + resp['kvs'][0]['value'], resp['kvs'][0]['mod_revision'])
def _cli(self, descr, *args):
args = [ 'vitastor-cli', *args, *(self._vitastor_args()) ]
try:
self._execute(*args)
except processutils.ProcessExecutionError as exc:
LOG.error("Failed to "+descr+": "+exc)
raise exception.VolumeBackendAPIException(data = exc.stderr)
def create_volume(self, volume):
"""Creates a logical volume."""
@@ -310,7 +302,7 @@ class VitastorDriver(driver.CloneableImageVD,
LOG.debug("creating volume '%s'", vol_name)
self._cli('create volume', 'create', vol_name, '--size', size)
self._create_image(vol_name, { 'size': size })
if volume.encryption_key_id:
self._create_encrypted_volume(volume, volume.obj_context)
@@ -354,7 +346,7 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = utils.convert_str(snapshot.name)
if snap_name.find('@') >= 0 or snap_name.find('/') >= 0:
raise exception.VolumeBackendAPIException(data = '@ and / are forbidden in volume and snapshot names')
self._cli('create snapshot', 'snap-create', vol_name+'@'+snap_name)
self._create_snapshot(vol_name, vol_name+'@'+snap_name)
def snapshot_revert_use_temp_snapshot(self):
"""Disable the use of a temporary snapshot on revert."""
@@ -367,8 +359,21 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = utils.convert_str(snapshot.name)
# Delete the image and recreate it from the snapshot
self._cli('delete image', 'rm', vol_name)
self._cli('recreate image', 'create', '--parent', vol_name+'@'+snap_name, vol_name)
args = [ 'vitastor-cli', 'rm', vol_name, *(self._vitastor_args()) ]
try:
self._execute(*args)
except processutils.ProcessExecutionError as exc:
LOG.error("Failed to delete image "+vol_name+": "+exc)
raise exception.VolumeBackendAPIException(data = exc.stderr)
args = [
'vitastor-cli', 'create', '--parent', vol_name+'@'+snap_name,
vol_name, *(self._vitastor_args())
]
try:
self._execute(*args)
except processutils.ProcessExecutionError as exc:
LOG.error("Failed to recreate image "+vol_name+" from "+vol_name+"@"+snap_name+": "+exc)
raise exception.VolumeBackendAPIException(data = exc.stderr)
def delete_snapshot(self, snapshot):
"""Deletes a snapshot."""
@@ -376,7 +381,15 @@ class VitastorDriver(driver.CloneableImageVD,
vol_name = utils.convert_str(snapshot.volume_name)
snap_name = utils.convert_str(snapshot.name)
self._cli('remove snapshot', 'rm', vol_name+'@'+snap_name)
args = [
'vitastor-cli', 'rm', vol_name+'@'+snap_name,
*(self._vitastor_args())
]
try:
self._execute(*args)
except processutils.ProcessExecutionError as exc:
LOG.error("Failed to remove snapshot "+vol_name+'@'+snap_name+": "+exc)
raise exception.VolumeBackendAPIException(data = exc.stderr)
def _child_count(self, parents):
children = 0
@@ -414,7 +427,13 @@ class VitastorDriver(driver.CloneableImageVD,
if src_vref.admin_metadata.get('readonly') == 'True':
# source volume is a volume-image cache entry or other readonly volume
# clone without intermediate snapshot
self._cli('create clone', 'create', '--parent', src_name, '--size', size, dest_name)
src = self._get_image(src_name)
LOG.debug("creating image '%s' from '%s'", dest_name, src_name)
new_cfg = self._create_image(dest_name, {
'size': size,
'parent_id': src['idx']['id'],
'parent_pool_id': src['idx']['pool_id'],
})
return {}
clone_snap = "%s@%s.clone_snap" % (src_name, dest_name)
@@ -427,12 +446,15 @@ class VitastorDriver(driver.CloneableImageVD,
clone_snap = dest_name
make_img = False
LOG.debug("creating snapshot '%s'", clone_snap)
self._cli('create base snapshot', 'snap-create', '--allow-existing', '1', clone_snap)
LOG.debug("creating layer '%s' under '%s'", clone_snap, src_name)
new_cfg = self._create_snapshot(src_name, clone_snap, True)
if make_img:
# Then create a clone from it
self._cli('create clone', 'create', '--parent', clone_snap, '--size', size, dest_name)
new_cfg = self._create_image(dest_name, {
'size': size,
'parent_id': new_cfg['parent_id'],
'parent_pool_id': new_cfg['parent_pool_id'],
})
return {}
@@ -442,8 +464,7 @@ class VitastorDriver(driver.CloneableImageVD,
vol_name = utils.convert_str(volume.name)
snap_name = utils.convert_str(snapshot.name)
src_snap = 'volume-'+snapshot.volume_id+'@'+snap_name
snap = self._get_image(src_snap)
snap = self._get_image('volume-'+snapshot.volume_id+'@'+snap_name)
if not snap:
raise exception.SnapshotNotFound(snapshot_id = snap_name)
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])
@@ -452,8 +473,12 @@ class VitastorDriver(driver.CloneableImageVD,
size = snap['cfg']['size']
if int(volume.size):
size = int(volume.size) * units.Gi
new_cfg = self._create_image(vol_name, {
'size': size,
'parent_id': snap['idx']['id'],
'parent_pool_id': snap['idx']['pool_id'],
})
self._cli('create clone', 'create', vol_name, '--size', size, '--parent', src_snap)
return {}
def _vitastor_args(self):
@@ -480,7 +505,49 @@ class VitastorDriver(driver.CloneableImageVD,
"""Deletes a logical volume."""
vol_name = utils.convert_str(volume.name)
self._cli('delete volume', 'rm', '--matching', vol_name, vol_name+'@*', '--progress', '0')
# Find the volume and all its snapshots
range_end = b'index/image/' + vol_name.encode('utf-8')
range_end = range_end[0 : len(range_end)-1] + six.int2byte(range_end[len(range_end)-1] + 1)
resp = self._etcd_txn({ 'success': [
{ 'request_range': { 'key': 'index/image/'+vol_name, 'range_end': range_end } },
] })
if len(resp['responses'][0]['kvs']) == 0:
# already deleted
LOG.info("volume %s no longer exists in backend", vol_name)
return
layers = resp['responses'][0]['kvs']
layer_ids = {}
for kv in layers:
inode_id = int(kv['value']['id'])
pool_id = int(kv['value']['pool_id'])
inode_pool_id = (pool_id << 48) | (inode_id & 0xffffffffffff)
layer_ids[inode_pool_id] = True
# Check if the volume has clones and raise 'busy' if so
children = self._child_count(layer_ids)
if children > 0:
raise exception.VolumeIsBusy(volume_name = vol_name)
# Clear data
for kv in layers:
args = [
'vitastor-cli', 'rm-data', '--pool', str(kv['value']['pool_id']),
'--inode', str(kv['value']['id']), '--progress', '0',
*(self._vitastor_args())
]
try:
self._execute(*args)
except processutils.ProcessExecutionError as exc:
LOG.error("Failed to remove layer "+kv['key']+": "+exc)
raise exception.VolumeBackendAPIException(data = exc.stderr)
# Delete all layers from etcd
requests = []
for kv in layers:
requests.append({ 'request_delete_range': { 'key': kv['key'] } })
requests.append({ 'request_delete_range': { 'key': 'config/inode/'+str(kv['value']['pool_id'])+'/'+str(kv['value']['id']) } })
self._etcd_txn({ 'success': requests })
def retype(self, context, volume, new_type, diff, host):
"""Change extra type specifications for a volume."""
@@ -500,6 +567,98 @@ class VitastorDriver(driver.CloneableImageVD,
"""Removes an export for a logical volume."""
pass
def _create_image(self, vol_name, cfg):
pool_s = str(self.cfg['pool_id'])
image_id = 0
while image_id == 0:
# check if the image already exists and find a free ID
resp = self._etcd_txn({ 'success': [
{ 'request_range': { 'key': 'index/image/'+vol_name } },
{ 'request_range': { 'key': 'index/maxid/'+pool_s } },
] })
if len(resp['responses'][0]['kvs']) > 0:
# already exists
raise exception.VolumeBackendAPIException(data = 'Volume '+vol_name+' already exists')
image_id, id_mod = self._next_id(resp['responses'][1])
# try to create the image
resp = self._etcd_txn({ 'compare': [
{ 'target': 'MOD', 'mod_revision': id_mod, 'key': 'index/maxid/'+pool_s },
{ 'target': 'VERSION', 'version': 0, 'key': 'index/image/'+vol_name },
{ 'target': 'VERSION', 'version': 0, 'key': 'config/inode/'+pool_s+'/'+str(image_id) },
], 'success': [
{ 'request_put': { 'key': 'index/maxid/'+pool_s, 'value': image_id } },
{ 'request_put': { 'key': 'index/image/'+vol_name, 'value': json.dumps({
'id': image_id, 'pool_id': self.cfg['pool_id']
}) } },
{ 'request_put': { 'key': 'config/inode/'+pool_s+'/'+str(image_id), 'value': json.dumps({
**cfg, 'name': vol_name,
}) } },
] })
if not resp.get('succeeded'):
# repeat
image_id = 0
def _create_snapshot(self, vol_name, snap_vol_name, allow_existing = False):
while True:
# check if the image already exists and snapshot doesn't
resp = self._etcd_txn({ 'success': [
{ 'request_range': { 'key': 'index/image/'+vol_name } },
{ 'request_range': { 'key': 'index/image/'+snap_vol_name } },
] })
if len(resp['responses'][0]['kvs']) == 0:
raise exception.VolumeBackendAPIException(data = 'Volume '+vol_name+' does not exist')
if len(resp['responses'][1]['kvs']) > 0:
if allow_existing:
snap_idx = resp['responses'][1]['kvs'][0]['value']
resp = self._etcd_txn({ 'success': [
{ 'request_range': { 'key': 'config/inode/'+str(snap_idx['pool_id'])+'/'+str(snap_idx['id']) } },
] })
if len(resp['responses'][0]['kvs']) == 0:
raise exception.VolumeBackendAPIException(data =
'Volume '+snap_vol_name+' is already indexed, but does not exist'
)
return resp['responses'][0]['kvs'][0]['value']
raise exception.VolumeBackendAPIException(
data = 'Volume '+snap_vol_name+' already exists'
)
vol_idx = resp['responses'][0]['kvs'][0]['value']
vol_idx_mod = resp['responses'][0]['kvs'][0]['mod_revision']
# get image inode config and find a new ID
resp = self._etcd_txn({ 'success': [
{ 'request_range': { 'key': 'config/inode/'+str(vol_idx['pool_id'])+'/'+str(vol_idx['id']) } },
{ 'request_range': { 'key': 'index/maxid/'+str(self.cfg['pool_id']) } },
] })
if len(resp['responses'][0]['kvs']) == 0:
raise exception.VolumeBackendAPIException(data = 'Volume '+vol_name+' does not exist')
vol_cfg = resp['responses'][0]['kvs'][0]['value']
vol_mod = resp['responses'][0]['kvs'][0]['mod_revision']
new_id, id_mod = self._next_id(resp['responses'][1])
# try to redirect image to the new inode
new_cfg = {
**vol_cfg, 'name': vol_name, 'parent_id': vol_idx['id'], 'parent_pool_id': vol_idx['pool_id']
}
resp = self._etcd_txn({ 'compare': [
{ 'target': 'MOD', 'mod_revision': vol_idx_mod, 'key': 'index/image/'+vol_name },
{ 'target': 'MOD', 'mod_revision': vol_mod, 'key': 'config/inode/'+str(vol_idx['pool_id'])+'/'+str(vol_idx['id']) },
{ 'target': 'MOD', 'mod_revision': id_mod, 'key': 'index/maxid/'+str(self.cfg['pool_id']) },
{ 'target': 'VERSION', 'version': 0, 'key': 'index/image/'+snap_vol_name },
{ 'target': 'VERSION', 'version': 0, 'key': 'config/inode/'+str(self.cfg['pool_id'])+'/'+str(new_id) },
], 'success': [
{ 'request_put': { 'key': 'index/maxid/'+str(self.cfg['pool_id']), 'value': new_id } },
{ 'request_put': { 'key': 'index/image/'+vol_name, 'value': json.dumps({
'id': new_id, 'pool_id': self.cfg['pool_id']
}) } },
{ 'request_put': { 'key': 'config/inode/'+str(self.cfg['pool_id'])+'/'+str(new_id), 'value': json.dumps(new_cfg) } },
{ 'request_put': { 'key': 'index/image/'+snap_vol_name, 'value': json.dumps({
'id': vol_idx['id'], 'pool_id': vol_idx['pool_id']
}) } },
{ 'request_put': { 'key': 'config/inode/'+str(vol_idx['pool_id'])+'/'+str(vol_idx['id']), 'value': json.dumps({
**vol_cfg, 'name': snap_vol_name, 'readonly': True
}) } }
] })
if resp.get('succeeded'):
return new_cfg
def initialize_connection(self, volume, connector):
data = {
'driver_volume_type': 'vitastor',
@@ -538,9 +697,13 @@ class VitastorDriver(driver.CloneableImageVD,
size = int(volume.size) * units.Gi
dest_name = utils.convert_str(volume.name)
# Find or create the base snapshot
self._cli('create base snapshot', 'create', '--allow-existing', '1', base_vol.name+'@.clone_snap')
snap_cfg = self._create_snapshot(base_vol.name, base_vol.name+'@.clone_snap', True)
# Then create a clone from it
self._cli('create clone', 'create', dest_name, '--size', size, '--parent', base_vol.name+'@.clone_snap')
new_cfg = self._create_image(dest_name, {
'size': size,
'parent_id': snap_cfg['parent_id'],
'parent_pool_id': snap_cfg['parent_pool_id'],
})
return ({}, True)
return ({}, False)
@@ -607,8 +770,26 @@ class VitastorDriver(driver.CloneableImageVD,
def extend_volume(self, volume, new_size):
"""Extend an existing volume."""
vol_name = utils.convert_str(volume.name)
size = int(new_size) * units.Gi
self._cli('extend volume', 'modify', vol_name, '--resize', new_size)
while True:
vol = self._get_image(vol_name)
if not vol:
raise exception.VolumeBackendAPIException(data = 'Volume '+vol_name+' does not exist')
# change size
size = int(new_size) * units.Gi
if size == vol['cfg']['size']:
break
resp = self._etcd_txn({ 'compare': [ {
'target': 'MOD',
'mod_revision': vol['cfg_mod'],
'key': 'config/inode/'+str(vol['idx']['pool_id'])+'/'+str(vol['idx']['id']),
} ], 'success': [
{ 'request_put': {
'key': 'config/inode/'+str(vol['idx']['pool_id'])+'/'+str(vol['idx']['id']),
'value': json.dumps({ **vol['cfg'], 'size': size }),
} },
] })
if resp.get('succeeded'):
break
LOG.debug(
"Extend volume from %(old_size)s GB to %(new_size)s GB.",
{'old_size': volume.size, 'new_size': new_size}
@@ -681,7 +862,28 @@ class VitastorDriver(driver.CloneableImageVD,
"""
from_name = self._get_existing_name(existing_ref)
to_name = utils.convert_str(volume.name)
self._cli('rename', 'modify', from_name, '--rename', to_name)
self._rename(from_name, to_name)
def _rename(self, from_name, to_name):
while True:
vol = self._get_image(from_name)
if not vol:
raise exception.VolumeBackendAPIException(data = 'Volume '+from_name+' does not exist')
to = self._get_image(to_name)
if to:
raise exception.VolumeBackendAPIException(data = 'Volume '+to_name+' already exists')
resp = self._etcd_txn({ 'compare': [
{ 'target': 'MOD', 'mod_revision': vol['idx_mod'], 'key': 'index/image/'+vol['cfg']['name'] },
{ 'target': 'MOD', 'mod_revision': vol['cfg_mod'], 'key': 'config/inode/'+str(vol['idx']['pool_id'])+'/'+str(vol['idx']['id']) },
{ 'target': 'VERSION', 'version': 0, 'key': 'index/image/'+to_name },
], 'success': [
{ 'request_delete_range': { 'key': 'index/image/'+vol['cfg']['name'] } },
{ 'request_put': { 'key': 'index/image/'+to_name, 'value': json.dumps(vol['idx']) } },
{ 'request_put': { 'key': 'config/inode/'+str(vol['idx']['pool_id'])+'/'+str(vol['idx']['id']),
'value': json.dumps({ **vol['cfg'], 'name': to_name }) } },
] })
if resp.get('succeeded'):
break
def unmanage(self, volume):
pass
@@ -754,7 +956,7 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = self._get_existing_name(existing_ref)
from_name = vol_name+'@'+snap_name
to_name = vol_name+'@'+utils.convert_str(snapshot.name)
self._cli('rename', 'modify', from_name, '--rename', to_name)
self._rename(from_name, to_name)
def unmanage_snapshot(self, snapshot):
"""Removes the specified snapshot from Cinder management."""
-637
View File
@@ -1,637 +0,0 @@
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;
}
@@ -1,39 +0,0 @@
From 98d3f68a40130c438854f61db6025f9e9b099cb6 Mon Sep 17 00:00:00 2001
From: Vitaliy Filippov <vitalifster@gmail.com>
Date: Sat, 20 Dec 2025 14:44:35 +0300
Subject: [PATCH] Do not require atomic writes to be power of 2 sized and
aligned on length boundary
It contradicts NVMe specification where alignment is only required when atomic
write boundary (NABSPF/NABO) is set and highly limits usage of NVMe atomic writes
Signed-off-by: Vitaliy Filippov <vitalifster@gmail.com>
---
fs/read_write.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/fs/read_write.c b/fs/read_write.c
index 833bae068770..5467d710108d 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1802,17 +1802,9 @@ int generic_file_rw_checks(struct file *file_in, struct file *file_out)
int generic_atomic_write_valid(struct kiocb *iocb, struct iov_iter *iter)
{
- size_t len = iov_iter_count(iter);
-
if (!iter_is_ubuf(iter))
return -EINVAL;
- if (!is_power_of_2(len))
- return -EINVAL;
-
- if (!IS_ALIGNED(iocb->ki_pos, len))
- return -EINVAL;
-
if (!(iocb->ki_flags & IOCB_DIRECT))
return -EOPNOTSUPP;
--
2.51.0
+28 -171
View File
@@ -1,172 +1,29 @@
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
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
}
+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 ;;
-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;
-172
View File
@@ -1,172 +0,0 @@
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 ;;
+1 -1
View File
@@ -21,7 +21,7 @@ rpmbuild -bp fio.spec
cd $VITASTOR
VER=$(grep ^Version: rpm/vitastor-$REL.spec | awk '{print $2}')
rm -rf fio
ln -s $(ls -d ~/rpmbuild/BUILD/fio*/ | grep -v SPECPARTS) fio
ln -s ~/rpmbuild/BUILD/fio*/ fio
sh copy-fio-includes.sh
rm fio
mv fio-copy fio
-17
View File
@@ -1,17 +0,0 @@
# Build packages for AlmaLinux 10 inside a container
# cd ..
# docker pull --platform=linux/amd64/v2 quay.io/almalinuxorg/almalinux:10
# docker build -t vitastor-buildenv:el10 -f rpm/vitastor-el10.Dockerfile .
# docker run -i --rm -v ./:/root/vitastor vitastor-buildenv:el10 /root/vitastor/rpm/vitastor-build.sh
FROM quay.io/almalinuxorg/almalinux:10
WORKDIR /root
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
RUN dnf -y install epel-release dnf-plugins-core
RUN dnf -y install https://vitastor.io/rpms/centos/10/vitastor-release-1.0-1.el10.noarch.rpm
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel gf-complete-devel rdma-core-devel cmake libnl3-devel
RUN dnf download --source fio
RUN rpm --nomd5 -i fio*.src.rpm
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
-198
View File
@@ -1,198 +0,0 @@
Name: vitastor
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.12.el10.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
BuildRequires: nodejs >= 10
BuildRequires: jerasure-devel
BuildRequires: isa-l-devel
BuildRequires: gf-complete-devel
BuildRequires: rdma-core-devel
BuildRequires: cmake
BuildRequires: libnl3-devel
Requires: vitastor-osd = %{version}-%{release}
Requires: vitastor-mon = %{version}-%{release}
Requires: vitastor-client = %{version}-%{release}
Requires: vitastor-client-devel = %{version}-%{release}
Requires: vitastor-fio = %{version}-%{release}
%description
Vitastor is a small, simple and fast clustered block storage (storage for VM drives),
architecturally similar to Ceph which means strong consistency, primary-replication,
symmetric clustering and automatic data distribution over any number of drives of any
size with configurable redundancy (replication or erasure codes/XOR).
%package -n vitastor-osd
Summary: Vitastor - OSD
Requires: vitastor-client = %{version}-%{release}
Requires: util-linux
Requires: parted
%description -n vitastor-osd
Vitastor object storage daemon, i.e. server program that stores data.
%package -n vitastor-mon
Summary: Vitastor - monitor
Requires: nodejs >= 10
Requires: lpsolve
%description -n vitastor-mon
Vitastor monitor, i.e. server program responsible for watching cluster state and
scheduling cluster-level operations.
%package -n vitastor-client
Summary: Vitastor - client
%description -n vitastor-client
Vitastor client library and command-line interface.
%package -n vitastor-client-devel
Summary: Vitastor - development files
Group: Development/Libraries
Requires: vitastor-client = %{version}-%{release}
%description -n vitastor-client-devel
Vitastor library headers for development.
%package -n vitastor-fio
Summary: Vitastor - fio drivers
Group: Development/Libraries
Requires: vitastor-client = %{version}-%{release}
Requires: fio = 3.36-5.el10
%description -n vitastor-fio
Vitastor fio drivers for benchmarking.
%package -n vitastor-opennebula
Summary: Vitastor for OpenNebula
Group: Development/Libraries
Requires: vitastor-client
Requires: jq
Requires: python3-lxml
Requires: patch
Requires: qemu-kvm-block-vitastor
%description -n vitastor-opennebula
Vitastor storage plugin for OpenNebula.
%prep
%setup -q
%build
%cmake
%cmake_build
%install
rm -rf $RPM_BUILD_ROOT
%cmake_install
cd mon
npm install --production
cd ..
mkdir -p %buildroot/usr/lib/vitastor
cp -r mon %buildroot/usr/lib/vitastor
mv %buildroot/usr/lib/vitastor/mon/scripts/make-etcd %buildroot/usr/lib/vitastor/mon/
mkdir -p %buildroot/lib/systemd/system
cp mon/scripts/vitastor.target mon/scripts/vitastor-mon.service mon/scripts/vitastor-osd@.service %buildroot/lib/systemd/system
mkdir -p %buildroot/lib/udev/rules.d
cp mon/scripts/90-vitastor.rules %buildroot/lib/udev/rules.d
mkdir -p %buildroot/var/lib/one
cp -r opennebula/remotes %buildroot/var/lib/one
cp opennebula/install.sh %buildroot/var/lib/one/remotes/datastore/vitastor/
mkdir -p %buildroot/etc/
cp -r opennebula/sudoers.d %buildroot/etc/
%files
%doc GPL-2.0.txt VNPL-1.1.txt README.md README-ru.md
%files -n vitastor-osd
%_bindir/vitastor-osd
%_bindir/vitastor-disk
%_bindir/vitastor-dump-journal
/lib/systemd/system/vitastor-osd@.service
/lib/systemd/system/vitastor.target
/lib/udev/rules.d/90-vitastor.rules
%pre -n vitastor-osd
groupadd -r -f vitastor 2>/dev/null ||:
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
install -o vitastor -g vitastor -d /var/log/vitastor
mkdir -p /etc/vitastor
%files -n vitastor-mon
/usr/lib/vitastor/mon
/lib/systemd/system/vitastor-mon.service
%pre -n vitastor-mon
groupadd -r -f vitastor 2>/dev/null ||:
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
mkdir -p /etc/vitastor
mkdir -p /var/lib/vitastor
chown vitastor:vitastor /var/lib/vitastor
%files -n vitastor-client
%_bindir/vitastor-nbd
%_bindir/vitastor-ublk
%_bindir/vitastor-nfs
%_bindir/vitastor-cli
%_bindir/vitastor-rm
%_bindir/vitastor-kv
%_bindir/vitastor-kv-stress
%_bindir/vita
%_libdir/libvitastor_client.so*
%_libdir/libvitastor_kv.so*
%files -n vitastor-client-devel
%_includedir/vitastor_c.h
%_includedir/vitastor_kv.h
%_libdir/pkgconfig
%files -n vitastor-fio
%_libdir/libfio_vitastor.so
%_libdir/libfio_vitastor_blk.so
%_libdir/libfio_vitastor_sec.so
%files -n vitastor-opennebula
/var/lib/one
/etc/sudoers.d/opennebula-vitastor
%triggerin -n vitastor-opennebula -- opennebula
[ $2 = 0 ] || exit 0
/var/lib/one/remotes/datastore/vitastor/install.sh
# Turn off the brp-python-bytecompile script
%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g')
%changelog
+3 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.12
Version: 2.4.3
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.12.el7.tar.gz
Source0: vitastor-2.4.3.el7.tar.gz
BuildRequires: gperftools-devel
BuildRequires: devtoolset-9-gcc-c++
@@ -171,6 +171,7 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-kv
%_bindir/vitastor-kv-stress
%_bindir/vita
%_libdir/libvitastor_blk.so*
%_libdir/libvitastor_client.so*
%_libdir/libvitastor_kv.so*
+3 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.12
Version: 2.4.3
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.12.el8.tar.gz
Source0: vitastor-2.4.3.el8.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++
@@ -168,6 +168,7 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-kv
%_bindir/vitastor-kv-stress
%_bindir/vita
%_libdir/libvitastor_blk.so*
%_libdir/libvitastor_client.so*
%_libdir/libvitastor_kv.so*
+3 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.12
Version: 2.4.3
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.12.el9.tar.gz
Source0: vitastor-2.4.3.el9.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
@@ -165,6 +165,7 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-kv
%_bindir/vitastor-kv-stress
%_bindir/vita
%_libdir/libvitastor_blk.so*
%_libdir/libvitastor_client.so*
%_libdir/libvitastor_kv.so*
+3 -2
View File
@@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 2.8...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
include(GNUInstallDirs)
include(CTest)
include(CheckIncludeFile)
find_package(PkgConfig)
@@ -20,7 +21,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
endif()
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
add_definitions(-DVITASTOR_VERSION="3.0.12")
add_definitions(-DVITASTOR_VERSION="2.4.3")
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...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
-10
View File
@@ -183,11 +183,6 @@ public:
// Update configuration
virtual void parse_config(blockstore_config_t & config) = 0;
// Reshard database for a pool in chunks
// MUST be called only when nobody makes any modifications to the DB for this pool
virtual void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit) = 0;
virtual bool reshard_continue(void *reshard_state, uint64_t chunk_limit) = 0;
// Event loop
virtual void loop() = 0;
@@ -228,9 +223,4 @@ 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;
};
+18 -35
View File
@@ -48,12 +48,11 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
disk_alignment = parse_size(config["disk_alignment"]);
journal_block_size = parse_size(config["journal_block_size"]);
meta_block_size = parse_size(config["meta_block_size"]);
meta_block_target_free_space = parse_size(config["meta_block_target_free_space"]);
bitmap_granularity = parse_size(config["bitmap_granularity"]);
meta_format = stoull_full(config["meta_format"]);
atomic_write_size = (config.find("atomic_write_size") != config.end()
? parse_size(config["atomic_write_size"]) : 4096);
use_atomic_flag = config.find("use_atomic_flag") != config.end() &&
(config["use_atomic_flag"] == "true" || config["use_atomic_flag"] == "1" || config["use_atomic_flag"] == "yes");
if (config.find("data_io") == config.end() &&
config.find("meta_io") == config.end() &&
config.find("journal_io") == config.end())
@@ -94,9 +93,6 @@ 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;
@@ -158,6 +154,14 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
{
throw std::runtime_error("meta_block_size must not exceed "+std::to_string(MAX_DATA_BLOCK_SIZE));
}
if (!meta_block_target_free_space)
{
meta_block_target_free_space = 800;
}
if (meta_block_target_free_space >= meta_block_size)
{
throw std::runtime_error("meta_block_target_free_space must not exceed "+std::to_string(meta_block_size));
}
if (data_offset % disk_alignment)
{
throw std::runtime_error("data_offset must be a multiple of disk_alignment = "+std::to_string(disk_alignment));
@@ -174,10 +178,6 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
{
throw std::runtime_error("Data block size must be a multiple of sparse write tracking granularity");
}
if (data_block_size / bitmap_granularity < 8)
{
throw std::runtime_error("Data block size must be at least bitmap_granularity*8");
}
if (!data_csum_type)
{
csum_block_size = 0;
@@ -210,6 +210,10 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
{
throw std::runtime_error("journal_offset must be a multiple of journal_block_size = "+std::to_string(journal_block_size));
}
if (!meta_format)
{
meta_format = BLOCKSTORE_META_FORMAT_HEAP;
}
if (meta_device == data_device)
{
disable_meta_fsync = disable_data_fsync;
@@ -220,7 +224,7 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
}
}
void blockstore_disk_t::calc_lengths(bool skip_meta_check)
void blockstore_disk_t::calc_lengths()
{
// data
data_len = data_device_size - data_offset;
@@ -266,13 +270,13 @@ void blockstore_disk_t::calc_lengths(bool skip_meta_check)
}
// required metadata size
block_count = data_len / data_block_size;
clean_entry_bitmap_size = (data_block_size / bitmap_granularity + 7) / 8;
clean_entry_bitmap_size = data_block_size / bitmap_granularity / 8;
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
recalc:
if (meta_format == BLOCKSTORE_META_FORMAT_HEAP)
{
uint32_t entries_per_block = meta_block_size / (sizeof(heap_big_write_t) + clean_dyn_size);
uint32_t entries_per_block = ((meta_block_size-meta_block_target_free_space) /
(sizeof(heap_object_t) + sizeof(heap_write_t) + clean_dyn_size));
min_meta_len = (block_count+entries_per_block-1) / entries_per_block * meta_block_size;
}
else if (meta_format == BLOCKSTORE_META_FORMAT_V1)
@@ -280,32 +284,11 @@ recalc:
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + 2*clean_entry_bitmap_size;
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size)
/ (meta_block_size / clean_entry_size)) * meta_block_size;
if (!skip_meta_check && meta_area_size < min_meta_len)
{
too_small:
throw std::runtime_error("Metadata area is too small, need at least "+std::to_string(min_meta_len)+
" bytes, have only "+std::to_string(meta_area_size)+" bytes");
}
}
else if (meta_format == BLOCKSTORE_META_FORMAT_V2 || !meta_format)
else if (meta_format == BLOCKSTORE_META_FORMAT_V2)
{
meta_format = BLOCKSTORE_META_FORMAT_V2;
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + clean_dyn_size + 4 /*entry_csum*/;
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size) / (meta_block_size / clean_entry_size)) * meta_block_size;
if (!skip_meta_check && meta_area_size < min_meta_len)
{
if (!data_csum_type)
{
printf("Warning: Using old metadata format without checksums because the new format"
" doesn't fit into provided area (%ju bytes required, %ju bytes available)\n", min_meta_len, meta_area_size);
meta_format = BLOCKSTORE_META_FORMAT_V1;
goto recalc;
}
else
{
goto too_small;
}
}
}
else
{
+9 -14
View File
@@ -26,18 +26,18 @@ class allocator_t;
struct blockstore_disk_t
{
std::string data_device, meta_device, journal_device;
uint64_t data_block_size;
uint32_t data_block_size;
uint64_t cfg_journal_size, cfg_data_size;
// Required write alignment and journal/metadata/data areas' location alignment
uint32_t disk_alignment = 4096;
// Journal block size - minimum_io_size of the journal device is the best choice
uint64_t journal_block_size = 4096;
uint32_t journal_block_size = 4096;
// Metadata block size - minimum_io_size of the metadata device is the best choice
uint64_t meta_block_size = 4096;
uint32_t meta_block_size = 4096;
// Atomic write size of the data block device
uint32_t atomic_write_size = 4096;
// Whether we should set RWF_ATOMIC on atomic writes
bool use_atomic_flag = false;
// Target free space in metadata blocks
uint32_t meta_block_target_free_space = 800;
// Sparse write tracking granularity. 4 KB is a good choice. Must be a multiple of disk_alignment
uint32_t bitmap_granularity = 4096;
// Data checksum type, BLOCKSTORE_CSUM_NONE or BLOCKSTORE_CSUM_CRC32C
@@ -57,18 +57,13 @@ 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;
int meta_fd = -1, data_fd = -1, journal_fd = -1;
uint64_t meta_offset = 0, meta_device_sect = 0, meta_device_size = 0, meta_area_size = 0, min_meta_len = 0;
uint64_t data_offset = 0, data_device_sect = 0, data_device_size = 0, data_len = 0;
uint64_t journal_offset = 0, journal_device_sect = 0, journal_device_size = 0, journal_len = 0;
uint64_t meta_format = 0;
uint64_t meta_offset, meta_device_sect, meta_device_size, meta_area_size, min_meta_len, meta_format = 0;
uint64_t data_offset, data_device_sect, data_device_size, data_len;
uint64_t journal_offset, journal_device_sect, journal_device_size, journal_len;
uint64_t block_count = 0;
uint32_t clean_entry_bitmap_size = 0;
@@ -80,7 +75,7 @@ struct blockstore_disk_t
void open_data();
void open_meta();
void open_journal();
void calc_lengths(bool skip_meta_check = false);
void calc_lengths();
void check_lengths();
void close_all();
int trim_data(std::function<bool(uint64_t)> is_free);
+216 -325
View File
@@ -18,18 +18,12 @@ journal_flusher_t::journal_flusher_t(blockstore_impl_t *bs)
this->cur_flusher_count = bs->min_flusher_count;
this->target_flusher_count = bs->min_flusher_count;
active_flushers = 0;
advance_lsn_counter = 0;
co = new journal_flusher_co[max_flusher_count];
for (int i = 0; i < max_flusher_count; i++)
{
co[i].co_id = i;
co[i].bs = bs;
co[i].new_bmp = (uint8_t*)malloc_or_die(3*bs->dsk.clean_entry_bitmap_size);
co[i].new_ext_bmp = co[i].new_bmp + bs->dsk.clean_entry_bitmap_size;
co[i].punch_bmp = co[i].new_bmp + 2*bs->dsk.clean_entry_bitmap_size;
if (bs->dsk.csum_block_size > 0)
{
co[i].new_csums = (uint8_t*)malloc_or_die(bs->dsk.data_block_size / bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
}
co[i].flusher = this;
}
}
@@ -60,18 +54,6 @@ journal_flusher_t::~journal_flusher_t()
journal_flusher_co::~journal_flusher_co()
{
if (new_csums)
{
free(new_csums);
new_csums = NULL;
}
if (new_bmp)
{
free(new_bmp);
new_bmp = NULL;
}
new_ext_bmp = NULL;
punch_bmp = NULL;
free_buffers();
}
@@ -80,6 +62,11 @@ int journal_flusher_t::get_syncing_buffer()
return syncing_buffer;
}
uint64_t journal_flusher_t::get_compact_counter()
{
return compact_counter;
}
bool journal_flusher_t::is_active()
{
return active_flushers > 0;
@@ -99,10 +86,9 @@ void journal_flusher_t::release_trim()
void journal_flusher_t::dump_diagnostics()
{
printf(
"Compaction queue: %u items + %u future, data: %ju/%ju blocks used, buffer: %ju/%ju bytes used, meta: %ju/%ju bytes used, %u/%ju blocks nearfull\n",
bs->heap->get_compact_queue_size(), bs->heap->get_to_compact_count(),
"Compaction queue: %u items, data: %ju/%ju blocks used, meta: %ju/%ju bytes used, %u/%ju blocks nearfull\n",
bs->heap->get_to_compact_count(),
bs->heap->get_data_used_space()/bs->dsk.data_block_size, bs->dsk.block_count,
bs->heap->get_buffer_area_used_space(), bs->dsk.journal_len,
bs->heap->get_meta_used_space(), bs->heap->get_meta_total_space(),
bs->heap->get_meta_nearfull_blocks(), bs->dsk.meta_area_size/bs->dsk.meta_block_size-1
);
@@ -127,12 +113,8 @@ void journal_flusher_t::loop()
}
}
int prev_active = active_flushers;
for (int i = 0; (active_flushers > 0 || force_start > 0 ||
bs->heap->get_compact_queue_size() > bs->flusher_start_threshold ||
i == 0 && bs->intent_write_counter >= bs->journal_trim_interval) && i < cur_flusher_count; i++)
{
for (int i = 0; (active_flushers > 0 || force_start > 0 || bs->heap->get_to_compact_count() > bs->flusher_start_threshold) && i < cur_flusher_count; i++)
co[i].loop();
}
if (prev_active && !active_flushers && force_start > 0)
bs->ringloop->wakeup();
}
@@ -177,35 +159,19 @@ bool journal_flusher_co::loop()
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);
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:
resume_19:
if (!fsync_buffer(17))
return false;
resume_20:
resume_21:
if (!trim_lsn(20))
return false;
}
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))
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync))
{
// When under pressure, do an additional fsync to force entries to be marked compactable
flusher->active_flushers++;
resume_14:
resume_15:
resume_16:
if (!fsync_buffer(14))
resume_21:
resume_22:
res = fsync_buffer(21);
if (!res)
{
return false;
}
@@ -214,105 +180,62 @@ resume_16:
}
if (res == ENOENT)
{
if (co_id == 0 && flusher->force_start > 0)
{
flusher->active_flushers++;
resume_16:
resume_17:
resume_18:
resume_19:
resume_20:
if (!trim_lsn(16))
return false;
flusher->active_flushers--;
}
cur_oid = {};
wait_state = 0;
return true;
}
if (flusher->flushing.find(cur_oid) != flusher->flushing.end())
for (int i = 0; i < flusher->cur_flusher_count; i++)
{
for (int i = 0; i < flusher->cur_flusher_count; i++)
if (i != co_id && flusher->co[i].cur_oid == cur_oid)
{
if (i != co_id && flusher->co[i].cur_oid == cur_oid)
{
// Already flushing it
flusher->co[i].should_repeat = true;
goto resume_0;
}
// Already flushing it
flusher->co[i].should_repeat = true;
goto resume_0;
}
assert(false);
}
flusher->flushing.insert(cur_oid);
resume_1:
wait_state = 1;
should_repeat = false;
cur_obj = bs->heap->lock_and_read_entry(cur_oid);
cur_obj = bs->heap->lock_and_read_entry(cur_oid, copy_id);
if (!cur_obj)
{
// Object does not exist
flusher->flushing.erase(cur_oid);
goto resume_0;
}
cur_version = cur_obj->get_writes()->version;
// Find the range to compact
compact_lsn = bs->heap->get_fsynced_lsn();
bs->heap->get_compact_range(cur_obj, compact_lsn, &begin_wr, &end_wr);
if (!begin_wr)
{
// Nothing to flush
bs->heap->unlock_entry(cur_oid, copy_id);
goto resume_0;
}
assert(!end_wr->next() && end_wr->entry_type == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE));
clean_loc = end_wr->big_location(bs->heap);
if (bs->log_level > 10)
printf("Compacting %jx:%jx l%ju .. l%ju (last l%ju)\n", cur_oid.inode, cur_oid.stripe, end_wr->lsn, begin_wr->lsn, compact_lsn);
flusher->active_flushers++;
// Scan versions to flush
free_buffers();
copy_count = 0;
fsynced_lsn = bs->heap->get_fsynced_lsn();
bitmap_copied = false;
memset(new_bmp, 0, bs->dsk.clean_entry_bitmap_size);
csum_copy.clear();
compact_info = bs->heap->iterate_compaction(cur_obj, fsynced_lsn, flusher->force_start, [&](heap_entry_t *wr)
for (auto wr = begin_wr; wr != end_wr; wr = wr->next())
{
if (!bitmap_copied)
{
memcpy(new_ext_bmp, wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
bitmap_copied = true;
}
bitmap_set(new_bmp, wr->small().offset, wr->small().len, bs->dsk.bitmap_granularity);
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
{
csum_copy.push_back(wr);
}
if (wr->type() == BS_HEAP_SMALL_WRITE ||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
{
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 (!compact_info.compact_lsn)
{
// Flushing is aborted
flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
goto resume_0;
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size);
copy_count++;
}
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)
{
memcpy(new_ext_bmp, compact_info.clean_wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
bitmap_copied = true;
}
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
{
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
for (size_t i = csum_copy.size(); i > 0; i--)
{
auto wr = csum_copy[i-1];
memcpy(new_csums + wr->small().offset/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF),
wr->get_checksums(bs->heap), wr->small().len/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF));
}
csum_copy.clear();
}
clean_loc = compact_info.clean_wr->big_location(bs->heap);
overwrite_start = overwrite_end = 0;
if (read_vec.size() > 0)
{
@@ -331,6 +254,7 @@ resume_1:
}
// Read buffered data
cur_obj = NULL;
begin_wr = end_wr = NULL;
resume_2:
resume_3:
if (!read_buffered(2))
@@ -348,135 +272,107 @@ resume_3:
flusher->wanting_meta_fsync--;
}
res = check_and_punch_checksums();
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--;
goto resume_0;
}
if (res == EBUSY)
{
resume_4:
modified_block = UINT32_MAX;
res = bs->heap->punch_holes(compact_info.clean_wr, punch_bmp, new_csums, &modified_block);
if (res == ENOENT)
{
// Abort compaction
goto abort_compact;
}
if (res == EAGAIN)
{
// Retry, block is busy
wait_state = 4;
return false;
}
assert(res == 0);
resume_5:
resume_6:
if (!write_meta_block(5))
if (!write_meta_block(4))
{
return false;
}
resume_6:
resume_7:
resume_8:
resume_9:
if (!fsync_meta(7))
if (!fsync_meta(6))
{
return false;
}
res = 0;
}
else if (res == ENOENT || res == EDOM)
{
// Abort compaction
flusher->active_flushers--;
goto resume_0;
}
assert(res == 0);
// Submit data writes
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_COALESCED) ||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
{
assert(read_vec[i].buf);
await_sqe(10);
await_sqe(9);
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++;
}
}
resume_11:
resume_10:
if (wait_count > 0)
{
wait_state = 11;
wait_state = 10;
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);
bs->heap->unlock_entry(cur_oid, copy_id);
// Mark the object compacted, but don't free and remove small_writes
// We'll free and remove them only when trimming
// The only thing we modify here are big_write block checksums if >4k block is used
cur_obj = bs->heap->read_entry(cur_oid);
cur_obj = bs->heap->read_entry(cur_oid, &modified_block);
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;
}
res = bs->heap->add_compact(cur_obj, compact_info.compact_version, compact_info.compact_lsn, clean_loc,
compact_info.do_delete, &modified_block, new_bmp, new_ext_bmp, new_csums);
if (res == EBUSY)
if (read_to_fill_incomplete)
{
// Abort compaction, object is already overwritten by something else
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
assert(res == 0);
resume_12:
resume_13:
if (!write_meta_block(12))
{
return false;
resume_23:
resume_24:
if (!write_meta_block(23))
{
return false;
}
// Recheck the object because it could be invalidated again
cur_obj = bs->heap->read_entry(cur_oid, &modified_block);
if (!cur_obj)
{
// Abort compaction
goto resume_0;
}
}
bs->heap->mark_object_compacted(cur_obj, compact_lsn);
// Done
if (bs->log_level > 10)
{
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_info.compact_lsn, copy_count);
}
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_lsn, copy_count);
flusher->compact_counter++;
flusher->active_flushers--;
// Advance compacted_lsn every <journal_trim_interval> objects
if (co_id == 0 && !((++flusher->advance_lsn_counter) % bs->journal_trim_interval))
{
flusher->advance_lsn_counter = 0;
resume_11:
resume_12:
resume_13:
resume_14:
resume_15:
if (!trim_lsn(11))
return false;
}
if (should_repeat)
{
// Flush the same object again
goto resume_1;
}
flusher->flushing.erase(cur_oid);
// All done
goto resume_0;
}
@@ -513,11 +409,11 @@ void journal_flusher_co::fill_partial_checksum_blocks()
.copy_flags = COPY_BUF_DATA | copy_flags,
.offset = blk_begin,
.len = blk_end - blk_begin,
.disk_loc = clean_loc,
.disk_loc = end_wr->big_location(bs->heap),
.disk_offset = blk_begin,
.disk_len = blk_end - blk_begin,
.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, blk_end - blk_begin),
.wr = compact_info.clean_wr,
.wr_lsn = end_wr->lsn,
});
}
auto & vec = read_vec[read_vec.size()-1];
@@ -555,28 +451,26 @@ int journal_flusher_co::check_and_punch_checksums()
return 0;
}
// Verify data checksums
cur_obj = bs->heap->read_entry(cur_oid);
if (!cur_obj)
{
// Object is deleted, abort compaction
return ENOENT;
}
cur_obj = bs->heap->read_locked_entry(cur_oid, copy_id);
bool csum_ok = true;
for (int i = 0; i < read_vec.size(); i++)
{
auto & vec = read_vec[i];
if (!(vec.copy_flags & (COPY_BUF_COALESCED|COPY_BUF_ZERO|COPY_BUF_SKIP_CSUM)))
{
uint32_t *csums = (uint32_t*)(vec.wr->get_checksums(bs->heap)
heap_write_t *wr = cur_obj->get_writes();
while (wr && wr->lsn != vec.wr_lsn)
wr = wr->next();
assert(wr);
uint32_t *csums = (uint32_t*)(wr->get_checksums(bs->heap)
+ (vec.disk_offset/bs->dsk.csum_block_size)*(bs->dsk.data_csum_type & 0xFF)
- ((vec.wr->type() == BS_HEAP_BIG_WRITE || vec.wr->type() == BS_HEAP_BIG_INTENT)
? 0 : (vec.wr->small().offset/bs->dsk.csum_block_size)*(bs->dsk.data_csum_type & 0xFF)));
- ((wr->type() == BS_HEAP_BIG_WRITE) ? 0 : (wr->small().offset/bs->dsk.csum_block_size)*(bs->dsk.data_csum_type & 0xFF)));
bs->heap->calc_block_checksums(
csums, vec.buf, vec.wr->get_int_bitmap(bs->heap), vec.disk_offset, vec.disk_offset+vec.disk_len, false,
csums, vec.buf, wr->get_int_bitmap(bs->heap), vec.disk_offset, vec.disk_offset+vec.disk_len, false,
[&](uint32_t mismatch_pos, uint32_t expected_csum, uint32_t real_csum)
{
printf("Checksum mismatch during compaction in object %jx:%jx v%ju, offset 0x%x in %s area at offset 0x%jx: got %08x, expected %08x\n",
cur_oid.inode, cur_oid.stripe, vec.wr->version, mismatch_pos,
cur_oid.inode, cur_oid.stripe, wr->version, mismatch_pos,
(vec.copy_flags & COPY_BUF_JOURNAL ? "buffer" : "data"),
vec.disk_loc+vec.disk_offset, real_csum, expected_csum);
csum_ok = false;
@@ -595,8 +489,20 @@ int journal_flusher_co::check_and_punch_checksums()
// Nothing to do
return 0;
}
memcpy(punch_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
cur_obj = bs->heap->read_entry(cur_oid, &modified_block);
if (!cur_obj)
{
// Object is deleted, abort compaction
return ENOENT;
}
bs->heap->get_compact_range(cur_obj, compact_lsn, &begin_wr, &end_wr);
if (!begin_wr)
{
// Object is overwritten, abort compaction
return ENOENT;
}
uint8_t *bmp = end_wr->get_int_bitmap(bs->heap);
uint8_t *csums = end_wr->get_checksums(bs->heap);
// Clear bits
for (auto & vec: read_vec)
{
@@ -607,7 +513,7 @@ int journal_flusher_co::check_and_punch_checksums()
if (!(vec.copy_flags & COPY_BUF_COALESCED) &&
((vec.offset % bs->dsk.csum_block_size) || (vec.len % bs->dsk.csum_block_size)))
{
bitmap_clear(punch_bmp, vec.offset, vec.len, bs->dsk.bitmap_granularity);
bitmap_clear(bmp, vec.offset, vec.len, bs->dsk.bitmap_granularity);
}
}
// Update partial block checksums
@@ -616,20 +522,42 @@ int journal_flusher_co::check_and_punch_checksums()
if (vec.copy_flags & COPY_BUF_CSUM_FILL)
{
uint32_t csum_off = vec.offset/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF);
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
bs->heap->calc_block_checksums((uint32_t*)(csums+csum_off), vec.buf, bmp, vec.offset, vec.offset+vec.len, true, NULL);
}
}
// Modified, we should punch_holes and then write the block to disk
cur_obj->crc32c = cur_obj->calc_crc32c();
if (res == ENOENT)
{
// Object is deleted, abort compaction
return ENOENT;
}
// Modified, we should write the block to disk
assert(!res);
return EBUSY;
}
bool journal_flusher_co::calc_block_checksums()
{
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity || !read_vec.size())
{
return true;
}
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
bs->heap->get_compact_range(cur_obj, compact_lsn, &begin_wr, &end_wr);
if (!begin_wr)
{
// Object is overwritten, abort compaction
return false;
}
uint8_t *bmp = end_wr->get_int_bitmap(bs->heap);
uint8_t *csums = end_wr->get_checksums(bs->heap);
// Set bits
for (auto & vec: read_vec)
{
if (!(vec.copy_flags & (COPY_BUF_COALESCED|COPY_BUF_CSUM_FILL)))
{
bitmap_set(bmp, vec.offset, vec.len, bs->dsk.bitmap_granularity);
}
}
// Update block checksums
size_t i = 0;
while (i < read_vec.size() && !(read_vec[i].copy_flags & COPY_BUF_CSUM_FILL))
@@ -648,7 +576,7 @@ bool journal_flusher_co::calc_block_checksums()
assert(!(end % bs->dsk.csum_block_size));
uint32_t csum_off = start/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF);
bs->heap->calc_block_checksums(
(uint32_t*)(new_csums+csum_off), new_bmp, start, end,
(uint32_t*)(csums+csum_off), bmp, start, end,
[&](uint32_t start, uint32_t & len)
{
// O(n^2) search, may be fixed later :-p
@@ -665,6 +593,7 @@ bool journal_flusher_co::calc_block_checksums()
}, true, NULL
);
}
cur_obj->crc32c = cur_obj->calc_crc32c();
return true;
}
@@ -674,15 +603,13 @@ bool journal_flusher_co::write_meta_block(int wait_base)
goto resume_0;
else if (wait_state == wait_base+1)
goto resume_1;
resume_0:
if (bs->ringloop->space_left() < 1)
{
wait_state = wait_base+0;
return 0;
}
bs->prepare_meta_block_write(modified_block);
await_sqe(0);
data->iov = (struct iovec){ bs->heap->get_meta_block(modified_block), (size_t)bs->dsk.meta_block_size };
data->callback = simple_callback_w;
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + (modified_block+1)*bs->dsk.meta_block_size);
wait_count++;
resume_1:
if (bs->meta_block_is_pending(modified_block))
if (wait_count > 0)
{
wait_state = wait_base+1;
return false;
@@ -732,77 +659,16 @@ 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;
else if (wait_state == wait_base+1) goto resume_1;
else if (wait_state == wait_base+2) goto resume_2;
resume_0:
if (bs->dsk.disable_meta_fsync)
{
return true;
}
resume_0:
if (flusher->wanting_meta_fsync || flusher->fsyncing_meta > 0)
{
wait_state = wait_base;
@@ -827,30 +693,69 @@ resume_2:
return true;
}
bool journal_flusher_co::fsync_buffer(int wait_base)
int journal_flusher_co::fsync_buffer(int wait_base)
{
if (wait_state == wait_base) goto resume_0;
else if (wait_state == wait_base+1) goto resume_1;
if (bs->dsk.disable_journal_fsync && bs->dsk.disable_meta_fsync && bs->dsk.disable_data_fsync || !bs->unsynced_big_write_count && !bs->unsynced_small_write_count)
{
return 1;
}
if (flusher->syncing_buffer)
{
return 0;
}
flusher->active_flushers++;
flusher->syncing_buffer++;
resume_0:
assert(!wait_count);
compact_lsn = bs->heap->get_completed_lsn();
if (!bs->submit_fsyncs(wait_count))
{
wait_state = wait_base+0;
return 0;
}
resume_1:
if (wait_count > 0)
{
wait_state = wait_base+1;
return 0;
}
bs->heap->mark_lsn_fsynced(compact_lsn);
flusher->active_flushers--;
flusher->syncing_buffer--;
return 2;
}
bool journal_flusher_co::trim_lsn(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;
if (!bs->has_unsynced())
else if (wait_state == wait_base+3) goto resume_3;
else if (wait_state == wait_base+4) goto resume_4;
compact_lsn = bs->heap->get_compacted_lsn();
if (((blockstore_meta_header_v3_t*)bs->meta_superblock)->compacted_lsn == compact_lsn)
{
return true;
}
resume_0:
if (flusher->syncing_buffer)
{
wait_state = wait_base+0;
return false;
}
flusher->active_flushers++;
flusher->syncing_buffer++;
resume_1:
assert(!wait_count);
fsynced_lsn = bs->heap->get_completed_lsn();
if (!bs->submit_fsyncs(wait_count))
if (!bs->dsk.disable_meta_fsync)
{
wait_state = wait_base+1;
return false;
await_sqe(0);
data->iov = { 0 };
data->callback = simple_callback_w;
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
wait_count++;
}
if (!bs->dsk.disable_data_fsync && bs->dsk.data_fd != bs->dsk.meta_fd)
{
await_sqe(1);
data->iov = { 0 };
data->callback = simple_callback_w;
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
wait_count++;
}
resume_2:
if (wait_count > 0)
@@ -858,37 +763,23 @@ resume_2:
wait_state = wait_base+2;
return false;
}
bs->heap->mark_lsn_fsynced(fsynced_lsn);
flusher->active_flushers--;
flusher->syncing_buffer--;
return true;
}
bool journal_flusher_co::trim_lsn(int wait_base)
{
if (wait_state == wait_base) goto resume_0;
else if (wait_state == wait_base+1) goto resume_1;
fsynced_lsn = bs->heap->get_fsynced_lsn();
if (((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn == fsynced_lsn)
{
return true;
}
flusher->active_flushers++;
((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn = fsynced_lsn;
((blockstore_meta_header_v3_t*)bs->meta_superblock)->compacted_lsn = compact_lsn;
((blockstore_meta_header_v3_t*)bs->meta_superblock)->set_crc32c();
await_sqe(0);
await_sqe(3);
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
data->callback = simple_callback_w;
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
// Update superblock with datasync
sqe->rw_flags = RWF_DSYNC;
wait_count++;
resume_1:
resume_4:
if (wait_count > 0)
{
wait_state = wait_base+1;
wait_state = wait_base+4;
return false;
}
bs->heap->mark_lsn_trimmed(compact_lsn);
flusher->compact_counter++;
flusher->active_flushers--;
return true;
}
+15 -26
View File
@@ -6,7 +6,7 @@ struct copy_buffer_t
uint32_t copy_flags;
uint64_t offset, len, disk_loc, disk_offset, disk_len;
uint8_t *buf;
heap_entry_t *wr;
uint64_t wr_lsn;
};
struct meta_sector_t
@@ -25,15 +25,6 @@ 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
@@ -45,29 +36,27 @@ class journal_flusher_co
int wait_state, wait_count;
struct io_uring_sqe *sqe;
struct ring_data_t *data;
uint8_t *new_csums = NULL;
uint8_t *new_bmp = NULL;
uint8_t *punch_bmp = NULL;
uint8_t *new_ext_bmp = NULL;
std::function<void(ring_data_t*)> simple_callback_r, simple_callback_w;
object_id cur_oid;
heap_entry_t *cur_obj;
uint64_t fsynced_lsn;
heap_compact_t compact_info;
uint64_t clean_loc;
uint64_t copy_id;
uint64_t compact_lsn;
uint64_t cur_version;
heap_object_t *cur_obj;
heap_write_t *begin_wr, *end_wr;
uint32_t modified_block;
bool bitmap_copied;
bool should_repeat;
std::vector<copy_buffer_t> read_vec;
std::vector<heap_entry_t*> csum_copy;
uint32_t overwrite_start, overwrite_end;
uint32_t big_start, big_end;
int i, res;
bool read_to_fill_incomplete;
int copy_count;
std::list<flusher_data_sync_t>::iterator cur_sync;
uint64_t clean_loc;
flusher_meta_write_t meta_old, meta_new;
bool do_repeat = false;
friend class journal_flusher_t;
@@ -78,10 +67,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);
int fsync_buffer(int wait_base);
bool trim_lsn(int wait_base);
public:
journal_flusher_co();
@@ -98,9 +85,10 @@ class journal_flusher_t
blockstore_impl_t *bs;
friend class journal_flusher_co;
robin_hood::unordered_flat_set<object_id> flushing;
int advance_lsn_counter = 0;
uint64_t compact_counter = 0;
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;
@@ -110,6 +98,7 @@ public:
~journal_flusher_t();
void loop();
int get_syncing_buffer();
uint64_t get_compact_counter();
bool is_active();
void request_trim();
void release_trim();
File diff suppressed because it is too large Load Diff
+202 -216
View File
@@ -1,4 +1,4 @@
// Metadata storage version 3 ("lsm heap")
// Metadata storage version 3 ("heap")
// Copyright (c) Vitaliy Filippov, 2025+
// License: VNPL-1.1 (see README.md for details)
@@ -19,332 +19,326 @@ struct pool_shard_settings_t
{
uint32_t pg_count;
uint32_t pg_stripe_size;
uint32_t no_inode_stats;
};
#define BS_HEAP_TYPE 0x07
#define BS_HEAP_BIG_WRITE 1
#define BS_HEAP_TYPE 7
#define BS_HEAP_OBJECT 1
#define BS_HEAP_SMALL_WRITE 2
#define BS_HEAP_INTENT_WRITE 3
#define BS_HEAP_BIG_INTENT 4
#define BS_HEAP_DELETE 5
#define BS_HEAP_COMMIT 6
#define BS_HEAP_ROLLBACK 7
#define BS_HEAP_STABLE 0x40
#define BS_HEAP_GARBAGE 0x80
#define BS_HEAP_BIG_WRITE 3
#define BS_HEAP_TOMBSTONE 4
#define BS_HEAP_INTENT_WRITE 5
#define BS_HEAP_STABLE 8
class blockstore_heap_t;
struct heap_small_write_t;
struct heap_big_write_t;
struct heap_big_intent_t;
struct __attribute__((__packed__)) heap_entry_t
struct __attribute__((__packed__)) heap_small_write_t
{
uint16_t size;
uint16_t entry_type;
uint32_t crc32c;
int16_t next_pos;
uint8_t flags;
uint64_t lsn;
uint64_t inode;
uint64_t stripe;
uint64_t version;
uint64_t location;
uint32_t offset;
uint32_t len;
};
struct __attribute__((__packed__)) heap_big_write_t
{
uint16_t size;
int16_t next_pos;
uint8_t flags;
uint64_t lsn;
uint64_t version;
uint32_t block_num;
};
struct __attribute__((__packed__)) heap_tombstone_t
{
uint16_t size;
int16_t next_pos;
uint8_t flags;
uint64_t lsn;
uint64_t version;
};
struct __attribute__((__packed__)) heap_write_t
{
// size should have top bit cleared
uint16_t size = 0;
int16_t next_pos = 0;
uint8_t entry_type = 0; // BS_HEAP_*
uint64_t lsn = 0;
uint64_t version = 0;
// uint8_t[] external_bitmap
// uint8_t[] internal_bitmap
// uint32_t[] checksums
heap_write_t *next();
inline uint8_t type() const { return (entry_type & BS_HEAP_TYPE); }
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() const;
void set_garbage();
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);
uint32_t get_csum_size(blockstore_heap_t *heap);
bool needs_recheck(blockstore_heap_t *heap);
bool needs_compact(blockstore_heap_t *heap);
bool is_compacted(uint64_t compacted_lsn);
bool can_be_collapsed(blockstore_heap_t *heap);
bool is_allowed_before_compacted(uint64_t compacted_lsn, bool is_last_entry);
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
uint8_t *get_checksums(blockstore_heap_t *heap);
uint32_t *get_checksum(blockstore_heap_t *heap);
uint64_t big_location(blockstore_heap_t *heap);
void set_big_location(blockstore_heap_t *heap, uint64_t location);
};
struct __attribute__((__packed__)) heap_object_t
{
// size should have top bit cleared
uint16_t size = 0;
// linked list of write entries...
// newest entries are stored first to simplify scanning
int16_t write_pos = 0;
uint8_t entry_type = 0; // BS_HEAP_*
uint32_t crc32c = 0;
uint64_t inode = 0;
uint64_t stripe = 0;
heap_write_t *get_writes();
uint32_t calc_crc32c();
};
struct __attribute__((__packed__)) heap_small_write_t
struct heap_object_lsn_t
{
heap_entry_t hdr;
uint64_t location;
uint32_t offset;
uint32_t len;
// Also includes 1 bitmap and 1 crc32c after the bitmap if checksums are disabled
object_id oid;
uint64_t lsn;
};
struct __attribute__((__packed__)) heap_big_write_t
inline bool operator < (const heap_object_lsn_t & a, const heap_object_lsn_t & b)
{
heap_entry_t hdr;
return a.oid < b.oid || a.oid == b.oid && a.lsn < b.lsn;
}
uint32_t block_num;
struct tmp_compact_item_t
{
object_id oid;
uint64_t lsn;
bool compact;
};
struct __attribute__((__packed__)) heap_big_intent_t
struct heap_mvcc_copy_id_t
{
heap_entry_t hdr;
uint32_t block_num;
uint32_t offset;
uint32_t len;
// Also includes 2 bitmaps and 1 crc32c if checksums are disabled
object_id oid;
uint64_t copy_id;
};
struct __attribute__((__packed__)) heap_list_item_t
inline bool operator == (const heap_mvcc_copy_id_t & a, const heap_mvcc_copy_id_t & b)
{
heap_list_item_t *prev;
heap_list_item_t *next;
uint32_t block_num;
heap_entry_t entry;
return a.oid.inode == b.oid.inode && a.oid.stripe == b.oid.stripe && a.copy_id == b.copy_id;
}
namespace std
{
template<> struct hash<heap_mvcc_copy_id_t>
{
inline size_t operator()(const heap_mvcc_copy_id_t &s) const
{
size_t seed = std::hash<object_id>()(s.oid);
// Copy-pasted from spp::hash_combine()
seed ^= (s.copy_id + 0xc6a4a7935bd1e995 + (seed << 6) + (seed >> 2));
return seed;
}
};
};
struct heap_object_mvcc_t
{
uint32_t readers = 0;
heap_entry_t *garbage_entry = NULL;
heap_object_t *entry_copy = NULL;
};
struct heap_block_info_t
struct __attribute__((__packed__)) heap_block_info_t
{
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 = false;
std::vector<heap_list_item_t*> entries;
uint32_t used_space = 0;
uint32_t free_pos = 0;
uint8_t *data = NULL;
};
struct heap_inflight_lsn_t
{
object_id oid;
uint64_t flags;
heap_entry_t *wr;
};
struct heap_compact_t
struct heap_refqi_t
{
uint64_t compact_lsn, compact_version;
heap_entry_t *clean_wr;
bool do_delete;
};
struct heap_reshard_state_t;
struct heap_li_hash
{
size_t operator()(const heap_list_item_t* li) const noexcept
{
return robin_hood::hash_int(li->entry.stripe);
}
};
struct heap_li_equal
{
constexpr bool operator()(const heap_list_item_t* a, const heap_list_item_t* b) const noexcept
{
return a->entry.stripe == b->entry.stripe;
}
};
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;
uint64_t lsn;
uint64_t inode;
uint64_t location;
uint32_t len;
bool is_data;
};
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,
robin_hood::unordered_flat_map<inode_t, void*, i64hash_t>, i64hash_t>;
using heap_mvcc_map_t = robin_hood::unordered_flat_map<object_id, heap_object_mvcc_t>;
robin_hood::unordered_flat_map<inode_t, robin_hood::unordered_flat_map<uint64_t, uint64_t, i64hash_t, std::equal_to<uint64_t>, 88>, i64hash_t>, i64hash_t>;
using heap_mvcc_map_t = robin_hood::unordered_flat_map<heap_mvcc_copy_id_t, heap_object_mvcc_t>;
class blockstore_heap_t
{
friend struct heap_entry_t;
friend class heap_write_t;
friend class heap_object_t;
blockstore_disk_t *dsk = NULL;
uint8_t* buffer_area = NULL;
bool abort_on_corruption = false;
bool abort_on_overlap = true;
int log_level = 0;
const uint32_t meta_block_count = 0;
const uint32_t max_entry_size = 0;
const uint32_t meta_block_count = 0;
uint32_t target_block_free_space = 800;
uint64_t next_lsn = 0;
robin_hood::unordered_flat_map<pool_id_t, pool_shard_settings_t> pool_shard_settings;
// PG => inode => stripe => block number
heap_block_index_t block_index;
std::vector<heap_block_info_t> block_info;
allocator_t *data_alloc = NULL;
multilist_index_t *meta_alloc = NULL;
uint32_t meta_nearfull_blocks = 0;
uint32_t meta_alloc_count = 0;
uint64_t meta_used_space = 0;
multilist_alloc_t *buffer_alloc = NULL;
heap_mvcc_map_t object_mvcc;
std::unordered_map<uint64_t, uint32_t> mvcc_data_refs;
std::unordered_map<uint64_t, uint32_t> mvcc_buffer_refs;
std::map<uint64_t, uint64_t> inode_space_stats;
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;
// LSN queue: inflight (writing) -> completed [-> fsynced]
// LSN queue: inflight (writing) -> completed [-> fsynced] -> compactable -> compacted [-> fsynced] -> trimmed and removed
std::deque<heap_inflight_lsn_t> inflight_lsn;
uint32_t to_compact_count = 0;
uint64_t compacted_count = 0;
uint32_t inflight_overwrite_count = 0;
uint64_t first_inflight_lsn = 0;
uint64_t completed_lsn = 0;
uint64_t fsynced_lsn = 0;
std::deque<object_id> compact_queue;
uint64_t compacted_lsn = 0;
uint64_t next_compact_lsn = 0;
std::deque<heap_refqi_t> overwrite_ref_queue;
bool marked_used_blocks = false;
bool recheck_queue_filled = false;
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;
std::vector<tmp_compact_item_t> tmp_compact_queue;
std::deque<object_id> recheck_queue;
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;
int recheck_queue_depth = 0;
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);
const uint32_t max_write_entry_size;
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
void defragment_block(uint32_t block_num);
uint32_t find_block_run(heap_block_info_t & block, uint32_t space);
uint32_t find_block_space(uint32_t block_num, uint32_t space);
uint32_t block_has_compactable(uint8_t *data);
uint32_t compact_object_to(heap_object_t *obj, uint64_t lsn, uint8_t *new_csums, bool do_free);
void copy_full_object(uint8_t *dst, heap_object_t *obj);
bool mvcc_save_copy(heap_object_t *obj);
bool mvcc_check_tracking(object_id oid);
void free_mvcc(heap_mvcc_map_t::iterator mvcc_it);
void allocate_block(heap_block_info_t & inf);
int allocate_new_object(object_id oid, uint32_t full_object_size, uint32_t *modified_block, heap_object_t **new_obj);
int add_object(object_id oid, heap_write_t *wr, uint32_t *modified_block);
void mark_overwritten(uint64_t over_lsn, uint64_t inode, heap_write_t *wr, heap_write_t *end_wr, bool tracking_active);
int update_object(uint32_t block_num, heap_object_t *obj, heap_write_t *wr, uint32_t *modified_block, uint32_t *moved_from_block);
void init_erase(uint32_t block_num, heap_object_t *obj);
void erase_object(uint32_t block_num, heap_object_t *obj, uint64_t lsn, bool tracking_active);
void reindex_block(uint32_t block_num, heap_object_t *from_obj);
void erase_block_index(inode_t inode, uint64_t stripe);
void deref_data(uint64_t inode, uint64_t location, bool free_at_0);
void deref_buffer(uint64_t inode, uint64_t location, uint32_t len, bool free_at_0);
void deref_overwrites(uint64_t lsn);
void free_object_space(inode_t inode, heap_write_t *from, heap_write_t *to, int mode = 0);
void add_used_space(uint32_t block_num, int32_t used_delta);
void push_inflight_lsn(object_id oid, uint64_t lsn, uint64_t flags);
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_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);
uint32_t meta_alloc_pos(const heap_block_info_t & inf);
void modify_alloc(uint32_t block_num, std::function<void(heap_block_info_t &)> change_cb);
void mark_garbage_up_to(heap_entry_t *wr);
void mark_garbage(uint32_t block_num, heap_entry_t *prev_wr, uint32_t used_big);
void push_inflight_lsn(uint64_t lsn, heap_entry_t *wr, uint64_t flags);
void mark_completed_lsns(uint64_t mod_lsn);
void apply_inflight(heap_inflight_lsn_t & inflight);
public:
blockstore_heap_t(blockstore_disk_t *dsk, uint8_t *buffer_area, int log_level = 0);
~blockstore_heap_t();
void start_load(uint64_t completed_lsn);
// load data from the disk, returns EDOM on corruption
int read_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf, bool allow_corrupted,
std::function<void(uint32_t block_num, heap_entry_t* wr)> handle_write,
std::function<void(uint32_t, uint32_t, uint8_t*)> handle_block);
int load_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf,
bool allow_corrupted, uint64_t &entries_loaded);
// finish loading - should be called after load_blocks
// set initially compacted lsn - should be done before loading
void set_compacted_lsn(uint64_t compacted_lsn);
uint64_t get_compacted_lsn();
// load data from the disk, returns count of loaded write entries
void read_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf,
std::function<void(heap_object_t*)> handle_object, std::function<void(uint32_t, uint32_t, uint8_t*)> handle_block);
uint64_t load_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf);
// finish loading
void finish_load();
// get blocks which are modified during loading and should be written to the disk
// before finishing initialization if not R/O
std::vector<uint32_t> get_recheck_modified_blocks();
// recheck small write data after reading the database from disk
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> read_buffer, int queue_depth);
int finish_recheck();
// initialize metadata area (fill it with empty data)
// returns 0 when done, EAGAIN when the caller has to wait more
int initialize();
// read from the metadata area
// returns 0 when done, EAGAIN when the caller has to wait more
int read();
// reshard database according to the pool's PG count
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
bool reshard_continue(void* reshard_state, uint64_t chunk_limit);
bool reshard_check(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size);
void set_no_inode_stats(const std::vector<uint64_t> & pool_ids);
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
void reshard(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size);
// read an object entry and lock it against removal
// in the future, may become asynchronous
heap_entry_t *lock_and_read_entry(object_id oid);
heap_object_t *lock_and_read_entry(object_id oid, uint64_t & copy_id);
// re-read a locked object entry with the given lsn (pointer may be invalidated)
heap_object_t *read_locked_entry(object_id oid, uint64_t copy_id);
// read an object entry without locking it
heap_entry_t *read_entry(object_id oid);
heap_object_t *read_entry(object_id oid, uint32_t *block_num_ptr, bool for_update = false);
// unlock an entry
bool unlock_entry(object_id oid);
bool unlock_entry(object_id oid, uint64_t copy_id);
// set or verify checksums in a write request
bool calc_checksums(heap_entry_t *wr, uint8_t *data, bool set, uint32_t offset = UINT32_MAX, uint32_t len = UINT32_MAX);
bool calc_checksums(heap_write_t *wr, uint8_t *data, bool set, uint32_t offset = 0, uint32_t len = 0);
// set or verify raw block checksums
bool calc_block_checksums(uint32_t *block_csums, uint8_t *data, uint8_t *bitmap, uint32_t start, uint32_t end,
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
bool calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap,
uint32_t start, uint32_t end, std::function<uint8_t*(uint32_t start, uint32_t & len)> next,
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
// adds a small_write or intent_write entry to an object
// copy an object as is
int copy_object(heap_object_t *obj, uint32_t *modified_block);
// auto-compacts the object, then adds a write entry to it and to the compaction queue
// return 0 if OK, or maybe ENOSPC
int add_small_write(object_id oid, heap_entry_t **obj_ptr, uint16_t type, uint64_t version,
uint32_t offset, uint32_t len, uint64_t location, uint8_t *bitmap, uint8_t *data, uint32_t *modified_block);
// adds a big_write (overwrite) entry to an object
int add_big_write(object_id oid, heap_entry_t *old_head, bool stable, uint64_t version,
uint32_t offset, uint32_t len, uint64_t location, uint8_t *bitmap, uint8_t *data, uint32_t *modified_block);
// adds a "redirecting" big_intent entry to an object (same as big_write, used to avoid fsync on desktop SSDs)
int add_redirect_intent(object_id oid, heap_entry_t **obj_ptr, uint64_t version,
uint32_t offset, uint32_t len, uint64_t location, uint8_t *bitmap, uint8_t *data, uint32_t *modified_block);
// adds a big_intent (atomic partial modification) entry to an object
int add_big_intent(object_id oid, heap_entry_t **obj_ptr, uint64_t version,
uint32_t offset, uint32_t len, uint8_t *bitmap, uint8_t *data, uint8_t *checksums, uint32_t *modified_block);
// adds a compacted up to <version> entry to an object
int add_compact(heap_entry_t *obj, uint64_t compact_version, uint64_t compact_lsn, uint64_t compact_location,
bool do_delete, uint32_t *modified_block, uint8_t *new_int_bitmap, uint8_t *new_ext_bitmap, uint8_t *new_csums);
// "punch holes" in a big_entry
int punch_holes(heap_entry_t *wr, uint8_t *new_bitmap, uint8_t *new_csums, uint32_t *modified_block);
int post_write(object_id oid, heap_write_t *wr, uint32_t *modified_block, uint32_t *moved_from_block);
int post_write(uint32_t & block_num, object_id oid, heap_object_t *obj, heap_write_t *wr, uint32_t *moved_from_block);
// stabilize an unstable object version
// return 0 if OK, ENOENT if not exists
int add_commit(heap_entry_t *obj, uint64_t version, uint32_t *modified_block);
int post_stabilize(object_id oid, uint64_t version, uint32_t *modified_block, uint64_t *new_lsn, uint64_t *new_to_lsn);
// rollback an unstable object version
// return 0 if OK, ENOENT if not exists, EBUSY if already stable
int add_rollback(heap_entry_t *obj, uint64_t version, uint32_t *modified_block);
int post_rollback(object_id oid, uint64_t version, uint64_t *new_lsn, uint32_t *modified_block);
// forget an object
// return error code
int add_delete(heap_entry_t *obj, uint32_t *modified_block);
int post_delete(object_id oid, uint64_t *new_lsn, uint32_t *modified_block);
int post_delete(uint32_t block_num, heap_object_t *obj, uint64_t *new_lsn);
// get the next object to compact
// guaranteed to return objects in min lsn order
// returns 0 if OK, ENOENT if nothing to compact
int get_next_compact(object_id & oid);
void iterate_with_stable(heap_entry_t *obj, uint64_t max_lsn, std::function<bool(heap_entry_t*, bool stable)> cb);
// iterate compactable entries
heap_compact_t iterate_compaction(heap_entry_t *obj, uint64_t fsynced_lsn, bool under_pressure,
std::function<void(heap_entry_t*)> small_wr_cb);
// iterate all objects
void iterate_objects(std::function<void(heap_entry_t*, uint32_t block_num)> cb);
// get the range of an object eligible for compaction
void get_compact_range(heap_object_t *obj, uint64_t max_lsn, heap_write_t **begin_wr, heap_write_t **end_wr);
// mark an object as compacted up to the given lsn
int compact_object(object_id oid, uint64_t lsn, uint8_t *new_csums);
// retrieve object listing from a PG
int list_objects(uint32_t pg_num, object_id min_oid, object_id max_oid,
obj_ver_id **result_list, size_t *stable_count, size_t *unstable_count);
// set a block number for a new object and returns error status: 0, EAGAIN or ENOSPC
int get_block_for_new_object(uint32_t & out_block_num, uint32_t size = 0);
// inflight write tracking
void start_block_write(uint32_t block_num);
void complete_block_write(uint32_t block_num);
void complete_lsn_write(uint64_t lsn);
bool is_lsn_completed(uint64_t lsn);
void mark_lsn_completed(uint64_t lsn);
void mark_lsn_fsynced(uint64_t lsn);
void mark_lsn_compacted(uint64_t lsn, bool allow_undone = false);
void mark_object_compacted(heap_object_t *obj, uint64_t max_lsn);
void mark_lsn_trimmed(uint64_t lsn);
uint64_t get_completed_lsn();
uint64_t get_fsynced_lsn();
void mark_lsn_fsynced(uint64_t lsn);
// data device block allocator functions
uint64_t find_free_data();
@@ -360,8 +354,7 @@ public:
uint64_t get_buffer_area_used_space();
// get metadata block data buffer and used space
void get_meta_block(uint32_t block_num, uint8_t *buffer);
void fill_block_empty_space(uint8_t *buffer, uint32_t pos);
uint8_t *get_meta_block(uint32_t block_num);
uint32_t get_meta_block_used_space(uint32_t block_num);
// get space usage statistics
@@ -370,21 +363,14 @@ public:
uint64_t get_meta_total_space();
uint64_t get_meta_used_space();
uint32_t get_meta_nearfull_blocks();
uint32_t get_inflight_queue_size();
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);
heap_entry_t *prev(heap_entry_t *wr);
uint32_t get_simple_entry_size();
uint32_t get_big_entry_size();
uint32_t get_big_intent_entry_size();
uint32_t get_small_entry_size(uint32_t offset, uint32_t len);
uint32_t get_csum_size(heap_entry_t *wr);
uint32_t get_csum_size(uint32_t entry_type, uint32_t offset = 0, uint32_t len = 0);
// get maximum size for a temporary heap_write_t buffer
uint32_t get_max_write_entry_size();
// only for tests
void set_abort_on_corruption(bool fail);
void set_abort_on_overlap(bool fail);
};
+17 -40
View File
@@ -23,22 +23,26 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_i *
dsk.open_meta();
dsk.open_journal();
dsk.calc_lengths();
dsk.check_lengths();
zero_object = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.data_block_size);
}
catch (std::exception & e)
{
dsk.close_all();
throw;
}
memset(zero_object, 0, dsk.data_block_size);
meta_superblock = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size);
memset(meta_superblock, 0, dsk.meta_block_size);
}
void blockstore_impl_t::init()
{
flusher = new journal_flusher_t(this);
if (dsk.inmemory_journal)
{
buffer_area = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.journal_len);
}
heap = new blockstore_heap_t(&dsk, buffer_area, log_level);
ringloop->wakeup();
}
blockstore_impl_t::~blockstore_impl_t()
@@ -51,6 +55,8 @@ blockstore_impl_t::~blockstore_impl_t()
free(buffer_area);
if (meta_superblock)
free(meta_superblock);
if (zero_object)
free(zero_object);
ringloop->unregister_consumer(&ring_consumer);
dsk.close_all();
}
@@ -101,7 +107,6 @@ 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];
@@ -139,13 +144,7 @@ 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
if (!has_unfinished_sync)
{
wr_st = continue_sync(op);
has_unfinished_sync = (wr_st != 2);
}
else
wr_st = 0;
wr_st = continue_sync(op);
}
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
{
@@ -161,7 +160,9 @@ void blockstore_impl_t::loop()
wr_st = 2;
}
else
{
wr_st = 0;
}
}
if (wr_st == 2)
{
@@ -191,14 +192,6 @@ void blockstore_impl_t::loop()
{
flusher->loop();
}
for (auto & block_num: pending_modified_blocks)
{
auto & mb = modified_blocks[block_num];
heap->get_meta_block(block_num, mb.buf);
heap->start_block_write(block_num);
mb.sent = true;
}
pending_modified_blocks.clear();
int ret = ringloop->submit();
if (ret < 0)
{
@@ -221,7 +214,7 @@ bool blockstore_impl_t::is_safe_to_stop()
{
return false;
}
if (has_unsynced())
if (unsynced_big_write_count > 0 || unsynced_small_write_count > 0)
{
if (!readonly && !stop_sync_submitted)
{
@@ -257,7 +250,7 @@ void blockstore_impl_t::check_wait(blockstore_op_t *op)
}
else if (PRIV(op)->wait_for == WAIT_COMPACTION)
{
if (heap->get_compacted_count() <= PRIV(op)->wait_detail)
if (flusher->get_compact_counter() <= PRIV(op)->wait_detail)
{
// do not submit
#ifdef BLOCKSTORE_DEBUG
@@ -280,8 +273,7 @@ void blockstore_impl_t::enqueue_op(blockstore_op_t *op)
((op->opcode == BS_OP_READ || op->opcode == BS_OP_WRITE || op->opcode == BS_OP_WRITE_STABLE) && (
op->offset >= dsk.data_block_size ||
op->len > dsk.data_block_size-op->offset ||
(op->offset % dsk.bitmap_granularity) ||
(op->len % dsk.bitmap_granularity)
(op->len % dsk.disk_alignment)
)) ||
readonly && op->opcode != BS_OP_READ && op->opcode != BS_OP_LIST)
{
@@ -328,13 +320,9 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
FINISH_OP(op);
return;
}
// Check if the DB is sharded correctly
if (!heap->reshard_check(INODE_POOL(min_inode), pg_count, pg_stripe_size))
{
op->retval = -EAGAIN;
FINISH_OP(op);
return;
}
// Check if the DB needs resharding
// (we don't know about PGs from the beginning, we only create "shards" here)
heap->reshard(INODE_POOL(min_inode), pg_count, pg_stripe_size);
obj_ver_id *result = NULL;
size_t stable_count = 0, unstable_count = 0;
int res = heap->list_objects(list_pg, op->min_oid, op->max_oid, &result, &stable_count, &unstable_count);
@@ -357,7 +345,6 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
void blockstore_impl_t::set_no_inode_stats(const std::vector<uint64_t> & pool_ids)
{
heap->set_no_inode_stats(pool_ids);
}
void blockstore_impl_t::dump_diagnostics()
@@ -401,13 +388,3 @@ std::string blockstore_impl_t::get_op_diag(blockstore_op_t *op)
snprintf(buf, sizeof(buf), "state=%d", priv->op_state);
return std::string(buf);
}
void* blockstore_impl_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
{
return heap->reshard_start(pool, pg_count, pg_stripe_size, chunk_limit);
}
bool blockstore_impl_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
{
return heap->reshard_continue(reshard_state, chunk_limit);
}
+30 -36
View File
@@ -29,6 +29,18 @@ class blockstore_impl_t;
//#define BLOCKSTORE_DEBUG
// - Sync must be submitted after previous writes/deletes (not before!)
// - Reads may be submitted in parallel with writes/deletes because we use MVCC
// - Writes may be submitted in any order, because they don't overlap. Each write
// goes into a new location - either on the journal device or on the data device
// - Stable (stabilize) must be submitted after sync of that object is completed
// It's even OK to return an error to the caller if that object is not synced yet
// - compacted_lsn should be moved forward only after all versions are moved to the main storage
// - If an operation can not be submitted because the ring is full
// we should stop submission of other operations. Otherwise some "scatter" reads
// may end up blocked for a long time.
// Otherwise, the submission order is free.
#include "blockstore_init.h"
#include "blockstore_flush.h"
@@ -41,30 +53,25 @@ struct blockstore_op_private_t
int pending_ops;
int op_state;
// Write, sync, stabilize
uint32_t modified_block, modified_block2;
// Read, write, sync, stabilize
uint64_t lsn;
// Read
std::vector<copy_buffer_t> read_vec;
// Read, write
uint64_t lsn;
// Write
uint64_t location;
uint32_t write_type;
bool is_big;
// Stabilize, rollback
int stab_pos;
// Write
timespec tv_begin;
};
// Stabilize
uint64_t to_lsn;
struct bs_modified_block_t
{
bool sent;
uint8_t *buf;
// Write
struct iovec iov_zerofill[3];
timespec tv_begin;
};
class blockstore_impl_t: public blockstore_i
@@ -78,7 +85,6 @@ public:
// Suitable only for server SSDs with capacitors, requires disabled data and journal fsyncs
int immediate_commit = IMMEDIATE_NONE;
bool inmemory_meta = false;
bool skip_corrupted_meta_entries = false;
uint32_t meta_write_recheck_parallelism = 0;
// Maximum and minimum flusher count
unsigned max_flusher_count = 0, min_flusher_count = 0;
@@ -109,16 +115,13 @@ public:
uint8_t* meta_superblock = NULL;
uint8_t *buffer_area = NULL;
std::vector<blockstore_op_t*> submit_queue;
int unsynced_data_write_count = 0, unsynced_buffer_write_count = 0, unsynced_meta_write_count = 0;
int unsynced_big_write_count = 0, unsynced_small_write_count = 0, unsynced_meta_write_count = 0;
int unsynced_queued_ops = 0;
std::vector<uint32_t> pending_modified_blocks;
robin_hood::unordered_flat_map<uint32_t, bs_modified_block_t> modified_blocks;
uint8_t *zero_object = NULL;
journal_flusher_t *flusher;
int write_iodepth = 0;
int inflight_big = 0;
int intent_write_counter = 0;
bool fsyncing_data = false;
bool live = false, queue_stall = false;
@@ -143,17 +146,18 @@ public:
int metadata_buf_size;
blockstore_init_meta* metadata_init_reader;
void init();
void check_wait(blockstore_op_t *op);
void init_op(blockstore_op_t *op);
// Read
int dequeue_read(blockstore_op_t *op);
int fulfill_read(blockstore_op_t *op);
uint32_t prepare_read(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum);
uint32_t prepare_read_with_bitmaps(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum);
uint32_t prepare_read(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end);
uint32_t prepare_read_with_bitmaps(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end);
uint32_t prepare_read_zero(std::vector<copy_buffer_t> & read_vec, uint32_t start, uint32_t end);
uint32_t prepare_read_simple(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum);
void prepare_disk_read(std::vector<copy_buffer_t> & read_vec, int pos, heap_entry_t *obj, heap_entry_t *wr,
uint32_t prepare_read_simple(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end);
void prepare_disk_read(std::vector<copy_buffer_t> & read_vec, int pos, heap_object_t *obj, heap_write_t *wr,
uint32_t blk_start, uint32_t blk_end, uint32_t start, uint32_t end, uint32_t copy_flags);
void find_holes(std::vector<copy_buffer_t> & read_vec, uint32_t item_start, uint32_t item_end,
std::function<void(int&, uint32_t, uint32_t)> callback);
@@ -163,10 +167,9 @@ public:
// Write
bool enqueue_write(blockstore_op_t *op);
void prepare_meta_block_write(uint32_t modified_block);
bool meta_block_is_pending(uint32_t modified_block);
bool intent_write_allowed(blockstore_op_t *op, heap_entry_t *obj);
void prepare_meta_block_write(blockstore_op_t *op, uint64_t modified_block, io_uring_sqe *sqe = NULL);
int dequeue_write(blockstore_op_t *op);
int make_big_write(blockstore_op_t *op, uint32_t offset, uint32_t len, uint32_t *modified_block, uint32_t *moved_from_block);
int continue_write(blockstore_op_t *op);
void handle_write_event(ring_data_t *data, blockstore_op_t *op);
@@ -174,7 +177,6 @@ public:
int continue_sync(blockstore_op_t *op);
bool submit_fsyncs(int & wait_count);
int do_sync(blockstore_op_t *op, int base_state);
bool has_unsynced();
// Stabilize
int dequeue_stable(blockstore_op_t *op);
@@ -190,9 +192,6 @@ public:
void parse_config(blockstore_config_t & config);
void parse_config(blockstore_config_t & config, bool init);
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
bool reshard_continue(void *reshard_state, uint64_t chunk_limit);
// Event loop
void loop();
@@ -227,11 +226,6 @@ public:
inline uint32_t get_block_size() { return dsk.data_block_size; }
inline uint64_t get_block_count() { return dsk.block_count; }
uint64_t get_free_block_count();
inline uint32_t get_bitmap_granularity() { return dsk.bitmap_granularity; }
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
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(); }
};
+49 -115
View File
@@ -51,8 +51,6 @@ int blockstore_init_meta::loop()
else if (wait_state == 5) goto resume_5;
else if (wait_state == 6) goto resume_6;
else if (wait_state == 7) goto resume_7;
else if (wait_state == 8) goto resume_8;
else if (wait_state == 9) goto resume_9;
metadata_buffer = memalign(MEM_ALIGNMENT, 2*bs->metadata_buf_size);
if (!metadata_buffer)
throw std::runtime_error("Failed to allocate metadata read buffer");
@@ -72,6 +70,7 @@ resume_1:
}
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
{
bs->dsk.check_lengths();
{
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
hdr->zero = 0;
@@ -80,15 +79,12 @@ resume_1:
hdr->meta_block_size = bs->dsk.meta_block_size;
hdr->data_block_size = bs->dsk.data_block_size;
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
hdr->compacted_lsn = 0;
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_V2)
{
hdr->data_csum_type = bs->dsk.data_csum_type;
hdr->csum_block_size = bs->dsk.csum_block_size;
}
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_HEAP)
{
hdr->meta_area_size = bs->dsk.meta_area_size;
}
hdr->set_crc32c();
}
if (bs->readonly)
@@ -139,30 +135,28 @@ resume_1:
hdr->data_block_size != bs->dsk.data_block_size ||
hdr->bitmap_granularity != bs->dsk.bitmap_granularity ||
hdr->data_csum_type != bs->dsk.data_csum_type ||
hdr->csum_block_size != bs->dsk.csum_block_size ||
hdr->meta_area_size != bs->dsk.meta_area_size)
hdr->csum_block_size != bs->dsk.csum_block_size)
{
printf(
"Configuration stored in metadata superblock"
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u, data_csum_type=%u, csum_block_size=%u, meta_area_size=%ju)"
" differs from OSD configuration (%ju/%ju/%u, %u/%u, %ju).\n",
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u, data_csum_type=%u, csum_block_size=%u)"
" differs from OSD configuration (%u/%u/%u, %u/%u).\n",
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity,
hdr->data_csum_type, hdr->csum_block_size, hdr->meta_area_size,
hdr->data_csum_type, hdr->csum_block_size,
bs->dsk.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity,
bs->dsk.data_csum_type, bs->dsk.csum_block_size, bs->dsk.meta_area_size
bs->dsk.data_csum_type, bs->dsk.csum_block_size
);
exit(1);
}
uint32_t csum = hdr->header_csum;
hdr->header_csum = 0;
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
if (bs->dsk.meta_format != hdr->version)
{
printf("Metadata header is corrupt (checksum mismatch).\n");
exit(1);
bs->dsk.meta_format = hdr->version;
bs->dsk.calc_lengths();
}
hdr->header_csum = csum;
bs->dsk.check_lengths();
}
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
bs->init();
bs->heap->set_compacted_lsn(((blockstore_meta_header_v3_t *)bs->meta_superblock)->compacted_lsn);
if (bs->dsk.inmemory_journal)
{
// Read buffer area
@@ -216,10 +210,8 @@ resume_4:
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
else
{
// Fill metadata with empty block pattern
memset(bufs[i].buf, 0, bufs[i].size);
for (uint64_t o = 0; o < bufs[i].size; o += bs->dsk.meta_block_size)
bs->heap->fill_block_empty_space(bufs[i].buf + o, 0);
// Fill metadata with zeroes
memset(data->iov.iov_base, 0, data->iov.iov_len);
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
}
bs->ringloop->submit();
@@ -232,11 +224,7 @@ resume_4:
if (bufs[i].state == INIT_META_READ_DONE)
{
// Handle result
uint64_t loaded = 0;
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
if (r != 0)
exit(1);
entries_loaded += loaded;
entries_loaded += bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf);
bufs[i].state = 0;
bs->ringloop->wakeup();
}
@@ -247,89 +235,8 @@ resume_4:
return 1;
}
// metadata read finished
bs->heap->finish_load();
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
// asynchronous recheck
resume_6:
wait_state = 6;
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, uint8_t *buf, std::function<void()> cb)
{
if (!buf)
{
wait_state = 7;
bs->ringloop->wakeup();
return;
}
GET_SQE();
data->iov = (iovec){ buf, len };
data->callback = [offset, cb](ring_data_t *data)
{
if (data->res < 0)
{
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", offset, data->res);
exit(1);
}
cb();
};
io_uring_prep_readv(sqe, (is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
(is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + offset);
bs->ringloop->submit();
}, bs->meta_write_recheck_parallelism);
return 1;
resume_7:
if (bs->heap->finish_recheck() != 0)
{
exit(1);
}
recheck_mod = bs->heap->get_recheck_modified_blocks();
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++)
{
resume_8:
if (wait_count >= bs->meta_write_recheck_parallelism || !(sqe = bs->get_sqe()))
{
bs->ringloop->submit();
wait_state = 8;
return 1;
}
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*)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)
{
wait_count--;
free(buf);
if (data->res != bs->dsk.meta_block_size)
{
throw std::runtime_error(
"write metadata failed at offset " + std::to_string(block_offset) + ": " + strerror(-data->res)
);
}
};
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, block_offset);
wait_count++;
}
resume_9:
if (wait_count > 0)
{
bs->ringloop->submit();
wait_state = 9;
return 1;
}
free(metadata_buffer);
metadata_buffer = NULL;
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
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);
@@ -345,9 +252,36 @@ resume_9:
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());
// asynchronous recheck
resume_6:
wait_state = 6;
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, uint8_t *buf, std::function<void()> cb)
{
if (!buf)
{
wait_state = 7;
bs->ringloop->wakeup();
return;
}
GET_SQE();
data->iov = (iovec){ buf, len };
data->callback = [this, offset, cb](ring_data_t *data)
{
if (data->res < 0)
{
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", offset, data->res);
exit(1);
}
cb();
};
io_uring_prep_readv(sqe, (is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
(is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + offset);
bs->ringloop->submit();
}, bs->meta_write_recheck_parallelism);
return 1;
resume_7:
bs->heap->finish_load();
free(metadata_buffer);
metadata_buffer = NULL;
return 0;
}
-2
View File
@@ -15,7 +15,6 @@ class blockstore_init_meta
{
blockstore_impl_t *bs;
int wait_state = 0;
int wait_count = 0;
bool zero_on_init = false;
void *metadata_buffer = NULL;
blockstore_init_meta_buf bufs[2] = {};
@@ -26,7 +25,6 @@ class blockstore_init_meta
uint64_t next_offset = 0;
uint64_t last_read_offset = 0;
uint64_t entries_loaded = 0;
std::vector<uint32_t> recheck_mod;
int i = 0, j = 0;
bool handle_meta_block(uint8_t *buf, uint64_t count, uint64_t done_cnt);
void handle_event(ring_data_t *data, int buf_num);
-11
View File
@@ -1,6 +1,3 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#pragma once
#define BS_SUBMIT_CHECK_SQES(n) \
@@ -51,11 +48,3 @@
#define COPY_BUF_COALESCED 0x10
#define COPY_BUF_PADDED 0x20
#define COPY_BUF_SKIP_CSUM 0x40
#ifndef RWF_ATOMIC
#define RWF_ATOMIC 0x40
#endif
#ifndef RWF_DSYNC
#define RWF_DSYNC 0x02
#endif
+1 -6
View File
@@ -28,7 +28,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
perfect_csum_update = config["perfect_csum_update"] == "true" || config["perfect_csum_update"] == "1" || config["perfect_csum_update"] == "yes";
skip_corrupted_meta_entries = config["skip_corrupted_meta_entries"] == "true" || config["skip_corrupted_meta_entries"] == "1" || config["skip_corrupted_meta_entries"] == "yes";
if (config["autosync_writes"] != "")
{
autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10);
@@ -43,7 +42,7 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
}
if (!journal_trim_interval)
{
journal_trim_interval = 4096;
journal_trim_interval = 1024;
}
if (!flusher_start_threshold)
{
@@ -97,10 +96,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
{
metadata_buf_size = 4*1024*1024;
}
if (metadata_buf_size % dsk.meta_block_size)
{
throw std::runtime_error("metadata_buf_size should be a multiple of meta_block_size");
}
if (!meta_write_recheck_parallelism)
{
meta_write_recheck_parallelism = 16;
+65 -135
View File
@@ -7,7 +7,7 @@
int blockstore_impl_t::dequeue_read(blockstore_op_t *op)
{
heap_entry_t *obj = heap->lock_and_read_entry(op->oid);
heap_object_t *obj = heap->lock_and_read_entry(op->oid, PRIV(op)->lsn);
if (!obj)
{
op->version = 0;
@@ -17,44 +17,16 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *op)
}
uint32_t fulfilled = 0;
PRIV(op)->pending_ops = 0;
PRIV(op)->lsn = 0;
auto & rv = PRIV(op)->read_vec;
uint64_t result_version = 0;
bool found = false;
uint32_t skip_csum = 0;
uint32_t blk_start = op->offset, blk_end = op->offset+op->len;
bool need_skip = dsk.csum_block_size > dsk.bitmap_granularity && !perfect_csum_update;
if (need_skip)
for (auto wr = obj->get_writes(); wr; wr = wr->next())
{
PRIV(op)->lsn = obj->lsn;
blk_start = op->offset - op->offset%dsk.csum_block_size;
blk_end = op->offset + op->len;
if (blk_end % dsk.csum_block_size)
blk_end += dsk.csum_block_size - (blk_end % dsk.csum_block_size);
}
bool need_wait = false;
heap->iterate_with_stable(obj, obj->lsn, [&](heap_entry_t *wr, bool stable)
{
if (wr->type() == BS_HEAP_DELETE)
if (op->version < wr->version)
{
return false;
continue;
}
if (!heap->is_lsn_completed(wr->lsn))
{
if (wr->type() == BS_HEAP_BIG_INTENT && wr->big_intent().offset < blk_end && wr->big_intent().offset+wr->big_intent().len > blk_start ||
wr->type() == BS_HEAP_INTENT_WRITE && wr->small().offset < blk_end && wr->small().offset+wr->small().len > blk_start)
{
// Wait until intent write is completed
need_wait = true;
return false;
}
else if (wr->type() == BS_HEAP_SMALL_WRITE && wr->small().offset < blk_end && wr->small().offset+wr->small().len > blk_start)
{
// Skip entry and read the previous one
return true;
}
}
if (op->version >= wr->version && !found)
if (!found)
{
found = true;
result_version = wr->version;
@@ -63,39 +35,18 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *op)
memcpy(op->bitmap, wr->get_ext_bitmap(heap), dsk.clean_entry_bitmap_size);
}
}
if (op->version >= wr->version)
fulfilled += prepare_read(PRIV(op)->read_vec, obj, wr, op->offset, op->offset+op->len);
if (fulfilled == op->len ||
wr->type() == BS_HEAP_BIG_WRITE ||
wr->type() == BS_HEAP_TOMBSTONE)
{
fulfilled += prepare_read(PRIV(op)->read_vec, obj, wr, op->offset, op->offset+op->len,
wr->type() != BS_HEAP_SMALL_WRITE ? skip_csum : 0);
if (fulfilled == op->len ||
wr->type() == BS_HEAP_BIG_WRITE ||
wr->type() == BS_HEAP_BIG_INTENT ||
wr->type() == BS_HEAP_DELETE)
{
return false;
}
break;
}
if (need_skip && wr->type() == BS_HEAP_SMALL_WRITE &&
wr->small().offset < blk_end && wr->small().offset+wr->small().len > blk_start)
{
// Small write may mutate big write checksums during flush
skip_csum = COPY_BUF_SKIP_CSUM;
}
return true;
});
if (need_wait)
{
undo_wait:
// Need to wait. undo added requests, unlock lsn
heap->unlock_entry(op->oid);
free_read_buffers(rv);
rv.clear();
return 0;
}
if (!found)
{
// May happen if there are entries but all of them are > requested version
heap->unlock_entry(op->oid);
heap->unlock_entry(op->oid, PRIV(op)->lsn);
op->version = 0;
op->retval = -ENOENT;
FINISH_OP(op);
@@ -104,13 +55,17 @@ undo_wait:
assert(fulfilled == op->len);
if (!fulfill_read(op))
{
goto undo_wait;
// Need to wait. undo added requests, unlock lsn
heap->unlock_entry(op->oid, PRIV(op)->lsn);
free_read_buffers(rv);
rv.clear();
return 0;
}
op->version = result_version;
if (!PRIV(op)->pending_ops)
{
// everything is fulfilled from memory
heap->unlock_entry(op->oid);
heap->unlock_entry(op->oid, PRIV(op)->lsn);
op->retval = op->len;
free_read_buffers(rv);
FINISH_OP(op);
@@ -153,20 +108,20 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *op)
return 1;
}
uint32_t blockstore_impl_t::prepare_read(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum)
uint32_t blockstore_impl_t::prepare_read(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end)
{
if (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT)
if (wr->type() == BS_HEAP_BIG_WRITE)
{
return prepare_read_with_bitmaps(read_vec, obj, wr, start, end, skip_csum);
return prepare_read_with_bitmaps(read_vec, obj, wr, start, end);
}
if (wr->type() == BS_HEAP_DELETE)
if (wr->type() == BS_HEAP_TOMBSTONE)
{
return prepare_read_zero(read_vec, start, end);
}
return prepare_read_simple(read_vec, obj, wr, start, end, skip_csum);
return prepare_read_simple(read_vec, obj, wr, start, end);
}
uint32_t blockstore_impl_t::prepare_read_with_bitmaps(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum)
uint32_t blockstore_impl_t::prepare_read_with_bitmaps(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end)
{
// BIG_WRITEs contain a bitmap and we have to handle its holes
uint32_t res = 0;
@@ -189,7 +144,7 @@ uint32_t blockstore_impl_t::prepare_read_with_bitmaps(std::vector<copy_buffer_t>
}
if (bmp_end > bmp_start)
{
res += prepare_read_simple(read_vec, obj, wr, bmp_start * dsk.bitmap_granularity, bmp_end * dsk.bitmap_granularity, skip_csum);
res += prepare_read_simple(read_vec, obj, wr, bmp_start * dsk.bitmap_granularity, bmp_end * dsk.bitmap_granularity);
bmp_start = bmp_end;
}
}
@@ -211,7 +166,7 @@ uint32_t blockstore_impl_t::prepare_read_zero(std::vector<copy_buffer_t> & read_
return res;
}
uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & read_vec, heap_entry_t *obj, heap_entry_t *wr, uint32_t start, uint32_t end, uint32_t skip_csum)
uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & read_vec, heap_object_t *obj, heap_write_t *wr, uint32_t start, uint32_t end)
{
uint32_t res = 0;
if (wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE)
@@ -235,7 +190,7 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
.disk_offset = start,
.disk_len = end-start,
.buf = buffer_area + wr->small().location + start - wr->small().offset,
.wr = wr,
.wr_lsn = wr->lsn,
});
}
else if (dsk.csum_block_size <= dsk.bitmap_granularity)
@@ -254,6 +209,14 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
blk_start = blk_start < wr->small().offset ? wr->small().offset : blk_start;
blk_end = blk_end > wr->small().offset+wr->small().len ? wr->small().offset+wr->small().len : blk_end;
}
uint32_t skip_csum = 0;
if (!perfect_csum_update && wr->type() == BS_HEAP_BIG_WRITE)
{
for (auto owr = obj->get_writes(); owr && owr != wr; owr = owr->next())
if ((owr->type() == BS_HEAP_INTENT_WRITE || owr->type() == BS_HEAP_SMALL_WRITE) &&
owr->small().offset < blk_end && owr->small().offset+owr->small().len > blk_start)
skip_csum = COPY_BUF_SKIP_CSUM;
}
if ((blk_end-1)/dsk.csum_block_size == blk_start/dsk.csum_block_size ||
blk_end/dsk.csum_block_size == blk_start/dsk.csum_block_size+1 && blk_end != end && blk_start != start ||
blk_end == end && blk_start == start)
@@ -280,37 +243,21 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
return res;
}
void blockstore_impl_t::prepare_disk_read(std::vector<copy_buffer_t> & read_vec, int pos, heap_entry_t *obj, heap_entry_t *wr,
void blockstore_impl_t::prepare_disk_read(std::vector<copy_buffer_t> & read_vec, int pos, heap_object_t *obj, heap_write_t *wr,
uint32_t blk_start, uint32_t blk_end, uint32_t start, uint32_t end, uint32_t copy_flags)
{
uint64_t loc = 0;
if (wr->type() == BS_HEAP_INTENT_WRITE)
{
heap_entry_t *big_wr = wr;
while (big_wr && big_wr->type() == BS_HEAP_INTENT_WRITE)
{
big_wr = heap->prev(big_wr);
}
assert(big_wr->type() == BS_HEAP_BIG_WRITE || big_wr->type() == BS_HEAP_BIG_INTENT);
loc = big_wr->big_location(heap);
}
else if (wr->type() == BS_HEAP_SMALL_WRITE)
{
loc = wr->small().location-wr->small().offset;
}
else
{
assert(wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT);
loc = wr->big_location(heap);
}
// Only one INTENT_WRITE is allowed at a time
assert(wr->type() != BS_HEAP_INTENT_WRITE || wr->next()->type() == BS_HEAP_BIG_WRITE);
copy_buffer_t vec = {
.copy_flags = (wr->type() == BS_HEAP_SMALL_WRITE ? COPY_BUF_JOURNAL : COPY_BUF_DATA) | copy_flags,
.offset = start,
.len = end-start,
.disk_loc = loc,
.disk_loc = (wr->type() == BS_HEAP_INTENT_WRITE ? wr->next()->big_location(heap)
: (wr->type() == BS_HEAP_SMALL_WRITE ? wr->small().location-wr->small().offset
: wr->big_location(heap))),
.disk_offset = blk_start,
.disk_len = blk_end - blk_start,
.wr = wr,
.wr_lsn = wr->lsn,
};
if (blk_start != start || blk_end != end)
{
@@ -318,7 +265,7 @@ void blockstore_impl_t::prepare_disk_read(std::vector<copy_buffer_t> & read_vec,
vec.copy_flags |= COPY_BUF_PADDED;
if (pos > 0 && read_vec.size() >= pos &&
read_vec[pos-1].copy_flags == vec.copy_flags &&
read_vec[pos-1].wr == vec.wr &&
read_vec[pos-1].wr_lsn == vec.wr_lsn &&
read_vec[pos-1].disk_offset <= vec.disk_offset &&
read_vec[pos-1].disk_offset+read_vec[pos-1].disk_len >= blk_end)
{
@@ -402,7 +349,7 @@ void blockstore_impl_t::handle_read_event(ring_data_t *data, blockstore_op_t *op
op->retval = -EDOM;
else if (op->retval == 0)
op->retval = op->len;
heap->unlock_entry(op->oid);
heap->unlock_entry(op->oid, PRIV(op)->lsn);
free_read_buffers(PRIV(op)->read_vec);
FINISH_OP(op);
}
@@ -410,15 +357,7 @@ void blockstore_impl_t::handle_read_event(ring_data_t *data, blockstore_op_t *op
bool blockstore_impl_t::verify_read_checksums(blockstore_op_t *op)
{
bool skip_all = false;
if (PRIV(op)->lsn)
{
heap_entry_t *obj = heap->read_entry(op->oid);
if (obj->lsn != PRIV(op)->lsn) // check top lsn
{
skip_all = true;
}
}
heap_object_t *obj = heap->read_locked_entry(op->oid, PRIV(op)->lsn);
auto & rv = PRIV(op)->read_vec;
for (auto & vec: rv)
{
@@ -426,22 +365,23 @@ bool blockstore_impl_t::verify_read_checksums(blockstore_op_t *op)
continue;
if (vec.copy_flags & COPY_BUF_PADDED)
memcpy(op->buf + vec.offset - op->offset, vec.buf + vec.offset - vec.disk_offset, vec.len);
if (vec.copy_flags & (COPY_BUF_COALESCED|COPY_BUF_SKIP_CSUM) ||
skip_all && vec.wr->type() != BS_HEAP_SMALL_WRITE)
if (vec.copy_flags & (COPY_BUF_COALESCED|COPY_BUF_SKIP_CSUM))
continue;
heap_write_t *wr = obj->get_writes();
while (wr && wr->lsn != vec.wr_lsn)
wr = wr->next();
assert(wr);
uint8_t *buf = vec.buf ? vec.buf : (op->buf + vec.offset - op->offset);
uint32_t *csums = (uint32_t*)(vec.wr->get_checksums(heap)
uint32_t *csums = (uint32_t*)(wr->get_checksums(heap)
+ (vec.disk_offset/dsk.csum_block_size)*(dsk.data_csum_type & 0xFF)
- ((vec.wr->type() == BS_HEAP_BIG_WRITE || vec.wr->type() == BS_HEAP_BIG_INTENT)
? 0 : (vec.wr->small().offset/dsk.csum_block_size)*(dsk.data_csum_type & 0xFF)));
if (!heap->calc_block_checksums(csums, buf, vec.wr->get_int_bitmap(heap),
- ((wr->type() == BS_HEAP_BIG_WRITE) ? 0 : (wr->small().offset/dsk.csum_block_size)*(dsk.data_csum_type & 0xFF)));
if (!heap->calc_block_checksums(csums, buf, wr->get_int_bitmap(heap),
vec.disk_offset, vec.disk_offset+vec.disk_len, false, [&](uint32_t mismatch_pos, uint32_t expected_csum, uint32_t real_csum)
{
printf(
"Checksum mismatch in object %jx:%jx v%ju, offset 0x%x in %s area at offset 0x%jx during read %x+%x: %08x expected vs %08x actual\n",
"Checksum mismatch in object %jx:%jx v%ju, offset 0x%x in %s area at offset 0x%jx: %08x expected vs %08x actual\n",
op->oid.inode, op->oid.stripe, op->version, mismatch_pos,
(vec.copy_flags & COPY_BUF_JOURNAL) ? "buffer" : "data", vec.disk_loc + vec.disk_offset,
op->offset, op->len,
expected_csum, real_csum
);
}))
@@ -454,33 +394,23 @@ bool blockstore_impl_t::verify_read_checksums(blockstore_op_t *op)
int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void *bitmap, uint64_t *result_version)
{
heap_entry_t *obj = heap->read_entry(oid);
heap_object_t *obj = heap->read_entry(oid, NULL);
if (obj)
{
bool found = false;
heap->iterate_with_stable(obj, obj->lsn, [&](heap_entry_t *wr, bool stable)
for (auto wr = obj->get_writes(); wr; wr = wr->next())
{
if (target_version >= wr->version)
if (target_version < wr->version)
{
if (wr->type() == BS_HEAP_DELETE)
{
return false;
}
found = true;
if (result_version)
{
*result_version = wr->version;
}
if (bitmap)
{
memcpy(bitmap, wr->get_ext_bitmap(heap), dsk.clean_entry_bitmap_size);
}
return false;
continue;
}
if (result_version)
{
*result_version = wr->version;
}
if (bitmap)
{
memcpy(bitmap, wr->get_ext_bitmap(heap), dsk.clean_entry_bitmap_size);
}
return true;
});
if (found)
{
return 0;
}
}
+55 -64
View File
@@ -13,82 +13,73 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
else if (priv->op_state == 2) goto resume_2;
else if (priv->op_state == 3) goto resume_3;
else if (priv->op_state == 4) goto resume_4;
else if (priv->op_state == 5) goto resume_5;
assert(!priv->op_state);
// Modify in-memory state and assign contiguous LSNs
priv->stab_pos = 0;
priv->lsn = priv->to_lsn = 0;
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++)
while (priv->stab_pos < op->len)
{
uint32_t modified_block = 0;
uint64_t new_lsn = 0;
uint64_t new_to_lsn = 0;
int res = op->opcode == BS_OP_STABLE
? heap->post_stabilize(v[priv->stab_pos].oid, v[priv->stab_pos].version, &modified_block, &new_lsn, &new_to_lsn)
: heap->post_rollback(v[priv->stab_pos].oid, v[priv->stab_pos].version, &new_lsn, &modified_block);
if (res != 0)
{
auto obj = heap->read_entry(v[priv->stab_pos].oid);
if (!obj)
assert(res == ENOENT || res == EBUSY);
op->retval = -res;
}
if (new_lsn)
{
assert(priv->lsn == 0 || priv->to_lsn == new_lsn-1);
if (!priv->lsn)
priv->lsn = new_lsn;
priv->to_lsn = op->opcode == BS_OP_STABLE ? new_to_lsn : new_lsn;
}
priv->stab_pos++;
}
// Submit metadata writes
priv->stab_pos = 0;
resume_1:
priv->op_state = 1;
while (priv->stab_pos < op->len)
{
uint32_t block_num = 0;
heap_object_t *obj = heap->read_entry(v[priv->stab_pos].oid, &block_num);
if (obj)
{
io_uring_sqe *sqe = get_sqe();
if (!sqe)
{
op->retval = -ENOENT;
FINISH_OP(op);
return 2;
}
int res = op->opcode == BS_OP_STABLE
? heap->add_commit(obj, v[priv->stab_pos].version, &priv->modified_block2)
: heap->add_rollback(obj, v[priv->stab_pos].version, &priv->modified_block2);
if (res == EBUSY)
{
op->retval = -EBUSY;
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())
{
// no space
op->retval = -ENOSPC;
FINISH_OP(op);
return 2;
}
if (priv->modified_block2 != UINT32_MAX)
{
priv->stab_pos--;
goto resume_1;
}
priv->wait_for = WAIT_COMPACTION;
priv->wait_detail = heap->get_compacted_count();
flusher->request_trim();
if (priv->pending_ops > 0)
return 1;
priv->wait_detail = 1;
priv->wait_for = WAIT_SQE;
return 0;
}
assert(res == 0);
}
resume_1:
if (priv->modified_block != UINT32_MAX && priv->modified_block2 != priv->modified_block)
{
BS_SUBMIT_CHECK_SQES(1);
prepare_meta_block_write(priv->modified_block);
resume_2:
if (meta_block_is_pending(priv->modified_block))
{
priv->op_state = 2;
return 1;
}
}
priv->modified_block = priv->modified_block2;
if (priv->stab_pos == op->len-1 && priv->modified_block2 != UINT32_MAX)
{
priv->modified_block2 = UINT32_MAX;
goto resume_1;
prepare_meta_block_write(op, block_num, sqe);
}
priv->stab_pos++;
}
if (priv->pending_ops > 0)
{
priv->op_state = 1;
return 1;
}
// Mark writes as completed to allow compaction
for (uint64_t lsn = priv->lsn; lsn <= priv->to_lsn; lsn++)
{
heap->mark_lsn_completed(lsn);
}
unsynced_meta_write_count++;
// Fsync, just because our semantics imply that commit (stabilize) is immediately fsynced
priv->op_state = 3;
priv->op_state = 2;
resume_2:
resume_3:
resume_4:
resume_5:
int res = do_sync(op, 3);
int res = do_sync(op, 2);
if (res != 2)
{
return res;
+10 -20
View File
@@ -9,7 +9,6 @@ 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)
@@ -19,19 +18,11 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
return res;
}
bool blockstore_impl_t::has_unsynced()
{
bool data = (!dsk.disable_data_fsync && unsynced_data_write_count);
bool buffer = (!dsk.disable_journal_fsync && unsynced_buffer_write_count);
bool meta = (!dsk.disable_meta_fsync && unsynced_meta_write_count);
return data || buffer || meta;
}
bool blockstore_impl_t::submit_fsyncs(int & wait_count)
{
int n = (unsynced_meta_write_count > 0 && !dsk.disable_meta_fsync) +
(unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync && dsk.journal_fd != dsk.meta_fd) +
(unsynced_data_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd);
int n = ((unsynced_small_write_count > 0 || unsynced_big_write_count > 0 || unsynced_meta_write_count > 0) && !dsk.disable_meta_fsync) +
(unsynced_small_write_count > 0 && !dsk.disable_journal_fsync && dsk.journal_fd != dsk.meta_fd) +
(unsynced_big_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd);
if (ringloop->space_left() < n)
{
return false;
@@ -49,7 +40,7 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
if (!wait_count)
ringloop->wakeup();
};
if (unsynced_meta_write_count > 0 && !dsk.disable_meta_fsync)
if ((unsynced_small_write_count > 0 || unsynced_big_write_count > 0 || unsynced_meta_write_count > 0) && !dsk.disable_meta_fsync)
{
// fsync meta
io_uring_sqe *sqe = get_sqe();
@@ -60,7 +51,7 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
data->callback = cb;
wait_count++;
}
if (unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync && dsk.meta_fd != dsk.journal_fd)
if (unsynced_small_write_count > 0 && !dsk.disable_journal_fsync && dsk.meta_fd != dsk.journal_fd)
{
// fsync buffer
io_uring_sqe *sqe = get_sqe();
@@ -71,7 +62,7 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
data->callback = cb;
wait_count++;
}
if (unsynced_data_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd)
if (unsynced_big_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd)
{
// fsync data
io_uring_sqe *sqe = get_sqe();
@@ -82,8 +73,8 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
data->callback = cb;
wait_count++;
}
unsynced_data_write_count = 0;
unsynced_buffer_write_count = 0;
unsynced_big_write_count = 0;
unsynced_small_write_count = 0;
unsynced_meta_write_count = 0;
return true;
}
@@ -99,13 +90,12 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
// Wait for flusher-initiated sync
return 0;
}
if (!has_unsynced())
if (dsk.disable_journal_fsync && dsk.disable_meta_fsync && dsk.disable_data_fsync || !unsynced_big_write_count && !unsynced_small_write_count)
{
// We can return immediately because sync only syncs previous writes
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
unsynced_big_write_count = unsynced_small_write_count = unsynced_meta_write_count = 0;
return 2;
}
assert(!PRIV(op)->lsn);
PRIV(op)->lsn = heap->get_completed_lsn();
if (!submit_fsyncs(PRIV(op)->pending_ops))
{
+191 -225
View File
@@ -5,110 +5,29 @@
#include "blockstore_internal.h"
#include "allocator.h"
#define _REDIRECT_INTENT 0x101
bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
{
clock_gettime(CLOCK_REALTIME, &PRIV(op)->tv_begin);
return true;
}
void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
void blockstore_impl_t::prepare_meta_block_write(blockstore_op_t *op, uint64_t modified_block, io_uring_sqe *sqe)
{
if (modified_blocks.find(modified_block) != modified_blocks.end())
return;
io_uring_sqe *sqe = get_sqe();
assert(sqe != NULL);
if (!sqe)
{
sqe = get_sqe();
assert(sqe != NULL);
}
ring_data_t *data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size);
data->iov = (struct iovec){ buf, (size_t)dsk.meta_block_size };
data->callback = [this, modified_block](ring_data_t *data)
{
live = true;
if (data->res != data->iov.iov_len)
{
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
disk_error_abort("data write", data->res, data->iov.iov_len);
}
auto it = modified_blocks.find(modified_block);
assert(it != modified_blocks.end());
free(it->second.buf);
modified_blocks.erase(it);
heap->complete_block_write(modified_block);
ringloop->wakeup();
};
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
data->iov = (struct iovec){ heap->get_meta_block(modified_block), (size_t)dsk.meta_block_size };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
PRIV(op)->pending_ops++;
io_uring_prep_writev(
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + ((uint64_t)modified_block+1)*dsk.meta_block_size
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + (modified_block+1)*dsk.meta_block_size
);
unsynced_meta_write_count++;
pending_modified_blocks.push_back(modified_block);
modified_blocks[modified_block] = { .sent = false, .buf = buf };
}
bool blockstore_impl_t::meta_block_is_pending(uint32_t modified_block)
{
auto mb_it = modified_blocks.find(modified_block);
return mb_it != modified_blocks.end();
}
bool blockstore_impl_t::intent_write_allowed(blockstore_op_t *op, heap_entry_t *obj)
{
// Parallel writes to the same object are forbidden so "one intent at a time" is fulfilled automatically
// Intent writes are disabled when metadata fsync is enabled
if (!dsk.disable_meta_fsync)
{
return false;
}
// Intent writes are only for replication
if (op->opcode != BS_OP_WRITE_STABLE)
{
return false;
}
// Operation size should be less than or equal to atomic write size
if (!op->len || op->len > dsk.atomic_write_size)
{
return false;
}
// Intent-writes are disabled if "absolutely correct during compaction" checksum validation algorithm is enabled
// We could also do RMW here when perfect_csum_update is enabled, but it's unclear if we need it
if (perfect_csum_update && dsk.csum_block_size > dsk.bitmap_granularity &&
((op->offset % dsk.csum_block_size) || (op->len % dsk.csum_block_size)))
{
return false;
}
bool ok = true;
heap->iterate_with_stable(obj, obj->lsn, [&](heap_entry_t *wr, bool stable)
{
// Intent writes are not allowed over buffered writes
auto t = wr->type();
if (t == BS_HEAP_SMALL_WRITE)
{
ok = false;
return false;
}
// Intent writes are not allowed over unstable writes
if (!stable)
{
ok = false;
return false;
}
// Intent writes are not allowed over unfinished intent writes
if ((t == BS_HEAP_INTENT_WRITE || t == BS_HEAP_BIG_INTENT) && wr->lsn > heap->get_fsynced_lsn())
{
ok = false;
return false;
}
// Intent writes are allowed over BIG_WRITEs even with fsyncs because BIG_WRITE is always counted as fsynced
if (t == BS_HEAP_BIG_WRITE || t == BS_HEAP_BIG_INTENT)
{
return false;
}
return true;
});
return ok;
}
// First step of the write algorithm: dequeue operation and submit initial write(s)
int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
{
if (PRIV(op)->op_state)
@@ -119,42 +38,40 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
{
return 0;
}
PRIV(op)->modified_block = UINT32_MAX;
PRIV(op)->write_type = 0;
heap_entry_t *obj = heap->read_entry(op->oid);
PRIV(op)->is_big = false;
uint32_t modified_block = UINT32_MAX, moved_from_block = UINT32_MAX;
heap_object_t *obj = heap->read_entry(op->oid, &modified_block);
if (op->opcode == BS_OP_DELETE)
{
// Delete
if (!obj || obj->type() == BS_HEAP_DELETE)
if (!obj)
{
// Already deleted
op->retval = 0;
FINISH_OP(op);
return 2;
}
PRIV(op)->write_type = BS_HEAP_DELETE;
BS_SUBMIT_CHECK_SQES(1);
int res = heap->add_delete(obj, &PRIV(op)->modified_block);
if (res == ENOSPC)
goto enospc;
int res = heap->post_delete(modified_block, obj, &PRIV(op)->lsn);
assert(res == 0);
prepare_meta_block_write(PRIV(op)->modified_block);
PRIV(op)->pending_ops++;
prepare_meta_block_write(op, modified_block);
PRIV(op)->op_state = 5;
write_iodepth++;
}
// FIXME: Allow to do initial writes as buffered, not redirected
// FIXME: Allow to do direct writes over holes
else if (!obj || obj->type() == BS_HEAP_DELETE || op->offset == 0 && op->len == dsk.data_block_size)
else if (!obj || obj->get_writes()->type() == BS_HEAP_TOMBSTONE ||
op->offset == 0 && op->len == dsk.data_block_size)
{
// Big (redirect) write
PRIV(op)->write_type = dsk.disable_data_fsync || op->opcode != BS_OP_WRITE_STABLE ? BS_HEAP_BIG_WRITE : _REDIRECT_INTENT;
BS_SUBMIT_CHECK_SQES(1);
PRIV(op)->location = heap->find_free_data();
if (PRIV(op)->location == UINT64_MAX)
PRIV(op)->is_big = true;
uint32_t tmp_block;
uint64_t loc = heap->find_free_data();
if (loc == UINT64_MAX ||
!obj && heap->get_block_for_new_object(tmp_block) != 0)
{
enospc:
if (!heap->get_to_compact_count())
if (!heap->get_inflight_queue_size())
{
// no space
op->retval = -ENOSPC;
@@ -162,11 +79,11 @@ enospc:
return 2;
}
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = heap->get_compacted_count();
PRIV(op)->wait_detail = flusher->get_compact_counter();
flusher->request_trim();
return 0;
}
uint64_t loc = PRIV(op)->location;
PRIV(op)->location = loc;
#ifdef BLOCKSTORE_DEBUG
printf(
"Allocate offset %ju for %jx:%jx v%ju\n",
@@ -174,61 +91,103 @@ enospc:
);
#endif
heap->use_data(op->oid.inode, PRIV(op)->location);
if (!dsk.disable_data_fsync && dsk.disable_meta_fsync)
{
// Do big_write as an INTENT to avoid data fsync
int res = make_big_write(op, 0, 0, &modified_block, &moved_from_block);
assert(res == 0);
if (moved_from_block != UINT32_MAX)
prepare_meta_block_write(op, moved_from_block);
obj = heap->read_entry(op->oid, &modified_block);
heap->mark_lsn_completed(PRIV(op)->lsn);
goto process_intent;
}
io_uring_sqe *sqe = get_sqe();
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++;
if (PRIV(op)->write_type == BS_HEAP_BIG_WRITE)
uint64_t stripe_offset = (op->offset % dsk.bitmap_granularity);
uint64_t stripe_end = (op->offset + op->len) % dsk.bitmap_granularity;
// Zero fill up to dsk.bitmap_granularity
int vcnt = 0;
if (stripe_offset)
{
PRIV(op)->op_state = 1;
inflight_big++;
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ zero_object, (size_t)stripe_offset };
}
else
PRIV(op)->op_state = 3;
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ op->buf, op->len };
if (stripe_end)
{
stripe_end = dsk.bitmap_granularity - stripe_end;
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ zero_object, (size_t)stripe_end };
}
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 + op->offset - stripe_offset
);
PRIV(op)->pending_ops++;
PRIV(op)->op_state = 1;
write_iodepth++;
inflight_big++;
}
else if (intent_write_allowed(op, obj))
// Only one INTENT_WRITE is allowed at a time, but in fact,
// parallel writes to the same object are forbidden anyway
else if (op->len > 0 && op->len <= dsk.atomic_write_size &&
// Intent-writes are disabled if "absolutely correct during compaction" checksum validation algorithm is enabled
// We could also do RMW here when perfect_csum_update is enabled, but it's unclear if we need it
(!perfect_csum_update || dsk.csum_block_size <= dsk.bitmap_granularity ||
!(op->offset % dsk.csum_block_size) &&
!(op->len % dsk.csum_block_size) &&
(obj->get_writes()->entry_type != (BS_HEAP_INTENT_WRITE|BS_HEAP_STABLE) ||
obj->get_writes()->can_be_collapsed(heap))) &&
// One intent-write is allowed even with fsyncs because BIG_WRITE is always counted as fsynced
dsk.disable_meta_fsync &&
(op->opcode == BS_OP_WRITE_STABLE &&
(obj->get_writes()->entry_type == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE) ||
obj->get_writes()->entry_type == (BS_HEAP_INTENT_WRITE|BS_HEAP_STABLE) && dsk.disable_data_fsync) ||
op->opcode == BS_OP_WRITE && obj->get_writes()->entry_type == BS_HEAP_BIG_WRITE))
{
// Direct intent-write
BS_SUBMIT_CHECK_SQES(1);
int res = 0;
if (dsk.csum_block_size <= dsk.bitmap_granularity &&
(obj->entry_type == (BS_HEAP_BIG_INTENT|BS_HEAP_STABLE) ||
obj->entry_type == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE)))
if (obj->get_writes()->type() == BS_HEAP_BIG_WRITE)
{
// Even more simplified BIG_INTENT writes
// FIXME: Support RMW mode for csum_block_size > bitmap_granularity
PRIV(op)->write_type = BS_HEAP_BIG_INTENT;
PRIV(op)->location = obj->big_location(heap);
res = heap->add_big_intent(op->oid, &obj, op->version, op->offset, op->len, op->bitmap,
(uint8_t*)op->buf, NULL, &PRIV(op)->modified_block);
if (res == ENOSPC)
goto enospc;
assert(res == 0);
PRIV(op)->lsn = obj->lsn;
PRIV(op)->location = obj->get_writes()->big_location(heap);
}
else
{
PRIV(op)->write_type = BS_HEAP_INTENT_WRITE;
auto wr = obj;
while (wr && (wr->type() == BS_HEAP_INTENT_WRITE || wr->type() == BS_HEAP_COMMIT || wr->type() == BS_HEAP_ROLLBACK))
{
wr = heap->prev(wr);
}
assert(wr && (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT));
PRIV(op)->location = wr->big_location(heap);
res = heap->add_small_write(op->oid, &obj, (BS_HEAP_INTENT_WRITE | (op->opcode == BS_OP_WRITE_STABLE ? BS_HEAP_STABLE : 0)),
op->version, op->offset, op->len, 0, op->bitmap, (uint8_t*)op->buf, &PRIV(op)->modified_block);
if (res == ENOSPC)
goto enospc;
assert(res == 0);
PRIV(op)->lsn = obj->lsn;
assert(obj->get_writes()->next()->type() == BS_HEAP_BIG_WRITE);
PRIV(op)->location = obj->get_writes()->next()->big_location(heap);
}
prepare_meta_block_write(PRIV(op)->modified_block);
PRIV(op)->pending_ops++;
process_intent:
uint8_t wr_buf[heap->get_max_write_entry_size()];
heap_write_t *wr = (heap_write_t*)wr_buf;
wr->version = op->version;
wr->entry_type = BS_HEAP_INTENT_WRITE | (op->opcode == BS_OP_WRITE_STABLE ? BS_HEAP_STABLE : 0);
wr->small().offset = op->offset;
wr->small().len = op->len;
wr->small().location = 0;
if (op->bitmap)
memcpy(wr->get_ext_bitmap(heap), op->bitmap, dsk.clean_entry_bitmap_size);
heap->calc_checksums(wr, (uint8_t*)op->buf, true);
int res = heap->post_write(modified_block, op->oid, obj, wr, &moved_from_block);
if (res == EAGAIN)
{
assert(heap->get_inflight_queue_size());
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = flusher->get_compact_counter();
flusher->request_trim();
return 0;
}
else if (res == ENOSPC)
{
// no space
op->retval = -ENOSPC;
FINISH_OP(op);
return 2;
}
assert(res == 0);
PRIV(op)->lsn = wr->lsn;
if (moved_from_block != UINT32_MAX)
prepare_meta_block_write(op, moved_from_block);
prepare_meta_block_write(op, modified_block);
PRIV(op)->op_state = 9;
write_iodepth++;
}
@@ -236,29 +195,53 @@ enospc:
{
// Small (buffered) overwrite
// First check if there is free buffer space
PRIV(op)->write_type = BS_HEAP_SMALL_WRITE;
uint64_t loc = !op->len ? 0 : heap->find_free_buffer_area(op->len);
if (loc == UINT64_MAX)
{
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = heap->get_compacted_count();
PRIV(op)->wait_detail = flusher->get_compact_counter();
flusher->request_trim();
return 0;
}
// There is sufficient space. Check SQE(s)
BS_SUBMIT_CHECK_SQES(1 + (op->len > 0 ? 1 : 0));
int res = heap->add_small_write(op->oid, &obj, (BS_HEAP_SMALL_WRITE | (op->opcode == BS_OP_WRITE_STABLE ? BS_HEAP_STABLE : 0)),
op->version, op->offset, op->len, loc, op->bitmap, (uint8_t*)op->buf, &PRIV(op)->modified_block);
if (res == ENOSPC)
goto enospc;
uint8_t wr_buf[heap->get_max_write_entry_size()];
heap_write_t *wr = (heap_write_t*)wr_buf;
wr->version = op->version;
wr->entry_type = BS_HEAP_SMALL_WRITE | (op->opcode == BS_OP_WRITE_STABLE ? BS_HEAP_STABLE : 0);
wr->small().offset = op->offset;
wr->small().len = op->len;
wr->small().location = loc;
PRIV(op)->location = loc;
if (op->bitmap)
memcpy(wr->get_ext_bitmap(heap), op->bitmap, dsk.clean_entry_bitmap_size);
heap->calc_checksums(wr, (uint8_t*)op->buf, true);
int res = heap->post_write(modified_block, op->oid, obj, wr, &moved_from_block);
if (res == EAGAIN)
{
assert(heap->get_inflight_queue_size());
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = flusher->get_compact_counter();
flusher->request_trim();
return 0;
}
else if (res == ENOSPC)
{
// no space
op->retval = -ENOSPC;
FINISH_OP(op);
return 2;
}
assert(res == 0);
PRIV(op)->lsn = obj->lsn;
prepare_meta_block_write(PRIV(op)->modified_block);
PRIV(op)->pending_ops++;
PRIV(op)->lsn = wr->lsn;
if (op->len)
heap->use_buffer_area(op->oid.inode, loc, op->len);
if (moved_from_block != UINT32_MAX)
prepare_meta_block_write(op, moved_from_block);
prepare_meta_block_write(op, modified_block);
if (op->len > 0)
{
// Prepare buffered data write
heap->use_buffer_area(op->oid.inode, loc, op->len);
if (dsk.inmemory_journal)
{
memcpy((uint8_t*)buffer_area + loc, op->buf, op->len);
@@ -266,7 +249,6 @@ 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++;
}
@@ -281,6 +263,26 @@ enospc:
return 1;
}
int blockstore_impl_t::make_big_write(blockstore_op_t *op, uint32_t offset, uint32_t len, uint32_t *modified_block, uint32_t *moved_from_block)
{
uint8_t wr_buf[heap->get_max_write_entry_size()];
heap_write_t *wr = (heap_write_t*)wr_buf;
wr->entry_type = BS_HEAP_BIG_WRITE | (op->opcode == BS_OP_WRITE_STABLE ? BS_HEAP_STABLE : 0);
wr->version = op->version;
wr->set_big_location(heap, PRIV(op)->location);
if (op->bitmap)
memcpy(wr->get_ext_bitmap(heap), op->bitmap, dsk.clean_entry_bitmap_size);
memset(wr->get_int_bitmap(heap), 0, dsk.clean_entry_bitmap_size);
bitmap_set(wr->get_int_bitmap(heap), offset, len, dsk.bitmap_granularity);
heap->calc_checksums(wr, (uint8_t*)op->buf, true, offset, len);
int res = heap->post_write(op->oid, wr, modified_block, moved_from_block);
if (res != 0)
return res;
assert(res == 0);
PRIV(op)->lsn = wr->lsn;
return 0;
}
int blockstore_impl_t::continue_write(blockstore_op_t *op)
{
int op_state = PRIV(op)->op_state;
@@ -303,12 +305,6 @@ again:
{
// In progress
assert(op_state < 10);
if (PRIV(op)->modified_block != UINT32_MAX &&
!meta_block_is_pending(PRIV(op)->modified_block))
{
PRIV(op)->pending_ops--;
PRIV(op)->modified_block = UINT32_MAX;
}
if (PRIV(op)->pending_ops > 0)
return 1;
op_state++;
@@ -317,6 +313,7 @@ again:
resume_2:
// We must fsync all big writes to avoid complex write workflows
// It's OK for all HDDs and for server SSDs, but slightly worse for desktop SSDs
// The other way is to add another type of MVCC to blockstore_heap: "forward" MVCC :)
inflight_big--;
if (!dsk.disable_data_fsync)
{
@@ -352,44 +349,33 @@ resume_12:
}
resume_4:
{
BS_SUBMIT_CHECK_SQES(1);
auto obj = heap->read_entry(op->oid);
int res = 0;
if (PRIV(op)->write_type == _REDIRECT_INTENT)
uint32_t modified_block = UINT32_MAX, moved_from_block = UINT32_MAX;
int res = make_big_write(op, op->offset, op->len, &modified_block, &moved_from_block);
if (res == EAGAIN)
{
res = heap->add_redirect_intent(op->oid, &obj, op->version, op->offset, op->len,
PRIV(op)->location, op->bitmap, (uint8_t*)op->buf, &PRIV(op)->modified_block);
}
else
{
res = heap->add_big_write(op->oid, obj, op->opcode == BS_OP_WRITE_STABLE,
op->version, op->offset, op->len, PRIV(op)->location, op->bitmap, (uint8_t*)op->buf, &PRIV(op)->modified_block);
}
if (res == ENOSPC)
{
if (!heap->get_to_compact_count())
{
// no space
heap->free_data(op->oid.inode, PRIV(op)->location);
write_iodepth--;
op->retval = -ENOSPC;
FINISH_OP(op);
return 2;
}
assert(heap->get_inflight_queue_size());
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = heap->get_compacted_count();
PRIV(op)->wait_detail = flusher->get_compact_counter();
flusher->request_trim();
return 0;
return 1;
}
assert(res == 0);
prepare_meta_block_write(PRIV(op)->modified_block);
PRIV(op)->pending_ops++;
else if (res == ENOSPC)
{
heap->free_data(op->oid.inode, PRIV(op)->location);
write_iodepth--;
op->retval = -ENOSPC;
FINISH_OP(op);
return 2;
}
if (moved_from_block != UINT32_MAX)
prepare_meta_block_write(op, moved_from_block);
prepare_meta_block_write(op, modified_block);
PRIV(op)->op_state = 5;
return 1;
}
resume_6:
// Apply throttling to not fill the journal too quickly for the SSD+HDD case
if (PRIV(op)->write_type == BS_HEAP_SMALL_WRITE && throttle_small_writes)
// Apply throttling to not fill the journal too fast for the SSD+HDD case
if (!PRIV(op)->is_big && throttle_small_writes)
{
// Apply throttling
timespec tv_end;
@@ -409,12 +395,11 @@ resume_6:
if (ref_us > exec_us + throttle_threshold_us)
{
// Pause reply
PRIV(op)->pending_ops++;
PRIV(op)->op_state = 7;
// Remember that the timer can in theory be called right here
tfd->set_timer_us(ref_us-exec_us, false, [this, op](int timer_id)
{
PRIV(op)->pending_ops--;
PRIV(op)->op_state = 8;
ringloop->wakeup();
});
return 1;
@@ -426,27 +411,11 @@ resume_8:
printf("Ack write %jx:%jx v%ju\n", op->oid.inode, op->oid.stripe, op->version);
#endif
op->retval = op->len;
if (PRIV(op)->write_type == BS_HEAP_BIG_WRITE)
{
unsynced_data_write_count++;
}
else if (PRIV(op)->write_type == BS_HEAP_SMALL_WRITE)
{
unsynced_buffer_write_count++;
heap->complete_lsn_write(PRIV(op)->lsn);
}
else if (PRIV(op)->write_type == BS_HEAP_BIG_INTENT ||
PRIV(op)->write_type == BS_HEAP_INTENT_WRITE)
{
unsynced_data_write_count++;
intent_write_counter++;
heap->complete_lsn_write(PRIV(op)->lsn);
}
else if (PRIV(op)->write_type == _REDIRECT_INTENT)
{
unsynced_data_write_count++;
intent_write_counter++;
}
heap->mark_lsn_completed(PRIV(op)->lsn);
if (PRIV(op)->is_big)
unsynced_big_write_count++;
else
unsynced_small_write_count++;
write_iodepth--;
FINISH_OP(op);
return 2;
@@ -456,10 +425,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;
PRIV(op)->pending_ops++;
PRIV(op)->op_state = 7;
return 1;
+12 -68
View File
@@ -28,7 +28,6 @@
#include "epoll_manager.h"
#include "malloc_or_die.h"
#include "json11/json11.hpp"
#include "../util/robin_hood.h"
#include "fio_headers.h"
struct bs_data
@@ -38,15 +37,11 @@ struct bs_data
ring_loop_t *ringloop;
/* The list of completed io_u structs. */
std::vector<io_u*> completed;
robin_hood::unordered_flat_map<uint64_t, int> inflight_oids;
std::vector<io_u*> postponed;
int op_n = 0, inflight = 0;
bool ec = false;
bool imm = true;
bool last_sync = false;
bool trace = false;
uint8_t *bitmap = NULL;
uint32_t block_size = 0;
};
struct bs_options
@@ -142,7 +137,6 @@ static void bs_cleanup(struct thread_data *td)
delete bsd->bs;
delete bsd->epmgr;
delete bsd->ringloop;
free(bsd->bitmap);
delete bsd;
}
}
@@ -166,12 +160,9 @@ static int bs_init(struct thread_data *td)
config[p.first] = p.second.dump();
}
}
bsd->bitmap = (uint8_t*)malloc_or_die(MAX_DATA_BLOCK_SIZE/512/8);
memset(bsd->bitmap, 0, MAX_DATA_BLOCK_SIZE/512/8);
bsd->ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
bsd->epmgr = new epoll_manager_t(bsd->ringloop);
bsd->bs = blockstore_i::create(config, bsd->ringloop, bsd->epmgr->tfd);
bsd->block_size = bsd->bs->get_block_size();
bsd->imm = config.find("immediate_commit") == config.end() ||
config["immediate_commit"] == "all";
while (1)
@@ -186,33 +177,8 @@ static int bs_init(struct thread_data *td)
return 0;
}
static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool force);
static void _bs_retry(struct bs_data *bsd, uint64_t offset)
{
// Retry postponed ops
auto inflight_it = bsd->inflight_oids.find(offset / bsd->block_size);
assert(inflight_it != bsd->inflight_oids.end());
inflight_it->second--;
if (inflight_it->second > 0)
{
for (size_t i = 0; i < bsd->postponed.size(); i++)
{
auto oio = bsd->postponed[i];
if (oio->offset/bsd->block_size == offset/bsd->block_size)
{
bsd->postponed.erase(bsd->postponed.begin()+i);
_bs_queue((thread_data*)oio->engine_data, oio, true);
break;
}
}
}
else
bsd->inflight_oids.erase(inflight_it);
}
/* Begin read or write request. */
static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool force)
static enum fio_q_status bs_queue(struct thread_data *td, struct io_u *io)
{
bs_data *bsd = (bs_data*)td->io_ops_data;
if (io->ddir == DDIR_SYNC && bsd->last_sync)
@@ -222,22 +188,10 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
fio_ro_check(td, io);
io->engine_data = td;
io->engine_data = bsd;
if (io->ddir == DDIR_WRITE || io->ddir == DDIR_READ)
assert(io->xfer_buflen <= bsd->block_size);
uint64_t stripe = io->offset / bsd->block_size;
if (!force && io->ddir == DDIR_WRITE)
{
auto & inflight = bsd->inflight_oids[stripe];
inflight++;
if (inflight > 1)
{
bsd->postponed.push_back(io);
return FIO_Q_QUEUED;
}
}
assert(io->xfer_buflen <= bsd->bs->get_block_size());
blockstore_op_t *op = new blockstore_op_t;
op->callback = NULL;
@@ -249,16 +203,15 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
op->buf = (uint8_t*)io->xfer_buf;
op->oid = {
.inode = 1,
.stripe = stripe,
.stripe = io->offset / bsd->bs->get_block_size(),
};
op->version = UINT64_MAX; // last unstable
op->offset = io->offset % bsd->block_size;
op->offset = io->offset % bsd->bs->get_block_size();
op->len = io->xfer_buflen;
op->bitmap = bsd->bitmap;
op->callback = [io, n = bsd->op_n](blockstore_op_t *op)
{
io->error = op->retval < 0 ? -op->retval : 0;
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
bs_data *bsd = (bs_data*)io->engine_data;
bsd->inflight--;
bsd->completed.push_back(io);
if (bsd->trace)
@@ -271,17 +224,16 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
op->buf = (uint8_t*)io->xfer_buf;
op->oid = {
.inode = 1,
.stripe = stripe,
.stripe = io->offset / bsd->bs->get_block_size(),
};
op->version = 0; // assign automatically
op->offset = io->offset % bsd->block_size;
op->offset = io->offset % bsd->bs->get_block_size();
op->len = io->xfer_buflen;
op->bitmap = bsd->bitmap;
if (bsd->ec)
{
op->callback = [io, n = bsd->op_n](blockstore_op_t *op)
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
bs_data *bsd = (bs_data*)io->engine_data;
if (bsd->trace)
printf("--- OP_WRITE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
if (op->retval < 0)
@@ -289,7 +241,6 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
io->error = op->retval < 0 ? -op->retval : 0;
bsd->inflight--;
bsd->completed.push_back(io);
_bs_retry(bsd, io->offset);
delete op;
}
else
@@ -303,13 +254,12 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
stab_op->len = 1;
stab_op->callback = [io, n](blockstore_op_t *op)
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
bs_data *bsd = (bs_data*)io->engine_data;
if (bsd->trace)
printf("--- OP_STABLE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
io->error = op->retval < 0 ? -op->retval : 0;
bsd->inflight--;
bsd->completed.push_back(io);
_bs_retry(bsd, io->offset);
delete op;
};
bsd->bs->enqueue_op(stab_op);
@@ -321,13 +271,12 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
{
op->callback = [io, n = bsd->op_n](blockstore_op_t *op)
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
bs_data *bsd = (bs_data*)io->engine_data;
if (bsd->trace)
printf("--- OP_WRITE_STABLE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
io->error = op->retval < 0 ? -op->retval : 0;
bsd->inflight--;
bsd->completed.push_back(io);
_bs_retry(bsd, io->offset);
delete op;
};
}
@@ -337,7 +286,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
op->opcode = BS_OP_SYNC;
op->callback = [io, n = bsd->op_n](blockstore_op_t *op)
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
bs_data *bsd = (bs_data*)io->engine_data;
io->error = op->retval < 0 ? -op->retval : 0;
bsd->completed.push_back(io);
bsd->inflight--;
@@ -365,11 +314,6 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
return FIO_Q_QUEUED;
}
static enum fio_q_status bs_queue(struct thread_data *td, struct io_u *io)
{
return _bs_queue(td, io, false);
}
static int bs_getevents(struct thread_data *td, unsigned int min, unsigned int max, const struct timespec *t)
{
bs_data *bsd = (bs_data*)td->io_ops_data;
-5
View File
@@ -300,11 +300,6 @@ uint32_t multilist_index_t::find(uint32_t wanted_used)
return heads[wanted_used];
}
uint32_t multilist_index_t::next(uint32_t pos)
{
return nexts[pos];
}
void multilist_index_t::change(uint32_t pos, uint32_t old_used, uint32_t new_used)
{
if (new_used == old_used)
-1
View File
@@ -32,7 +32,6 @@ struct multilist_index_t
// used should be always < max_used
multilist_index_t(uint32_t count, uint32_t max_used, uint32_t init_used);
uint32_t find(uint32_t wanted_used);
uint32_t next(uint32_t pos);
void change(uint32_t pos, uint32_t old_used, uint32_t new_used);
void print();
};
+2 -3
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 je->size < 4 ? 0 : crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
return crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
}
// "VITAstor"
@@ -185,8 +185,7 @@ struct __attribute__((__packed__)) blockstore_meta_header_v3_t
uint32_t data_csum_type;
uint32_t csum_block_size;
uint32_t header_csum;
uint64_t meta_area_size;
uint64_t completed_lsn;
uint64_t compacted_lsn;
void set_crc32c();
};
-2
View File
@@ -520,7 +520,6 @@ 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
);
@@ -750,7 +749,6 @@ 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
);
+16 -89
View File
@@ -407,77 +407,32 @@ blockstore_clean_db_t& blockstore_impl_t::clean_db_shard(object_id oid)
return clean_db_shards[(pool_id << (64-POOL_ID_BITS)) | pg_num];
}
struct bs_reshard_state_t
void blockstore_impl_t::reshard_clean_db(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size)
{
int state = 0;
uint64_t pool_id = 0;
uint32_t pg_count = 0;
uint32_t pg_stripe_size = 0;
uint64_t chunk_size = 0;
std::map<pool_pg_id_t, blockstore_clean_db_t> old_shards;
uint64_t pool_id = (uint64_t)pool;
std::map<pool_pg_id_t, blockstore_clean_db_t> new_shards;
std::map<pool_pg_id_t, blockstore_clean_db_t>::iterator sh_it;
blockstore_clean_db_t::iterator obj_it;
};
void* blockstore_impl_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
{
auto & settings = clean_db_settings[pool];
if (settings.pg_count == pg_count && settings.pg_stripe_size == pg_stripe_size)
{
return NULL;
}
bs_reshard_state_t *st = new bs_reshard_state_t;
st->state = 0;
st->pool_id = pool;
st->pg_count = pg_count;
st->pg_stripe_size = pg_stripe_size;
auto sh_it = clean_db_shards.lower_bound((st->pool_id << (64-POOL_ID_BITS)));
auto sh_it = clean_db_shards.lower_bound((pool_id << (64-POOL_ID_BITS)));
while (sh_it != clean_db_shards.end() &&
(sh_it->first >> (64-POOL_ID_BITS)) == st->pool_id)
(sh_it->first >> (64-POOL_ID_BITS)) == pool_id)
{
st->old_shards[sh_it->first] = std::move(sh_it->second);
for (auto & pair: sh_it->second)
{
// like map_to_pg()
uint64_t pg_num = (pair.first.stripe / pg_stripe_size) % pg_count + 1;
uint64_t shard_id = (pool_id << (64-POOL_ID_BITS)) | pg_num;
new_shards[shard_id][pair.first] = pair.second;
}
clean_db_shards.erase(sh_it++);
}
bool finished = reshard_continue(st, chunk_limit);
return finished ? NULL : st;
}
bool blockstore_impl_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
{
bs_reshard_state_t *st = (bs_reshard_state_t*)reshard_state;
uint64_t chunk_size = 0;
if (st->state == 1)
goto resume_1;
for (st->sh_it = st->old_shards.begin(); st->sh_it != st->old_shards.end(); )
{
for (st->obj_it = st->sh_it->second.begin(); st->obj_it != st->sh_it->second.end(); st->obj_it++)
{
if (chunk_limit > 0 && chunk_size >= chunk_limit)
{
st->state = 1;
return false;
}
resume_1:
// like map_to_pg()
uint64_t pg_num = (st->obj_it->first.stripe / st->pg_stripe_size) % st->pg_count + 1;
uint64_t shard_id = (st->pool_id << (64-POOL_ID_BITS)) | pg_num;
st->new_shards[shard_id][st->obj_it->first] = st->obj_it->second;
chunk_size++;
}
st->old_shards.erase(st->sh_it++);
}
for (auto sh_it = st->new_shards.begin(); sh_it != st->new_shards.end(); sh_it++)
for (sh_it = new_shards.begin(); sh_it != new_shards.end(); sh_it++)
{
auto & to = clean_db_shards[sh_it->first];
to.swap(sh_it->second);
}
clean_db_settings[st->pool_id] = (pool_shard_settings_t){
.pg_count = st->pg_count,
.pg_stripe_size = st->pg_stripe_size,
clean_db_settings[pool_id] = (pool_shard_settings_t){
.pg_count = pg_count,
.pg_stripe_size = pg_stripe_size,
};
delete st;
return true;
}
void blockstore_impl_t::process_list(blockstore_op_t *op)
@@ -510,10 +465,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
sh_it->second.pg_count != pg_count ||
sh_it->second.pg_stripe_size != pg_stripe_size)
{
// Sharding mismatch
op->retval = -EAGAIN;
FINISH_OP(op);
return;
reshard_clean_db(pool_id, pg_count, pg_stripe_size);
}
first_shard = last_shard = ((uint64_t)pool_id << (64-POOL_ID_BITS)) | list_pg;
}
@@ -855,29 +807,4 @@ 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
+1 -8
View File
@@ -202,6 +202,7 @@ class blockstore_impl_t: public blockstore_i
uint8_t* get_clean_entry_bitmap(uint64_t block_loc, int offset);
blockstore_clean_db_t& clean_db_shard(object_id oid);
void reshard_clean_db(pool_id_t pool_id, uint32_t pg_count, uint32_t pg_stripe_size);
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
// Journaling
@@ -287,10 +288,6 @@ public:
void parse_config(blockstore_config_t & config);
void parse_config(blockstore_config_t & config, bool init);
// Reshard database for a pool
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
bool reshard_continue(void *reshard_state, uint64_t chunk_limit);
// Event loop
void loop();
@@ -332,10 +329,6 @@ 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 -1
View File
@@ -189,7 +189,7 @@ resume_1:
printf(
"Configuration stored in metadata superblock"
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u, data_csum_type=%u, csum_block_size=%u)"
" differs from OSD configuration (%ju/%ju/%u, %u/%u).\n",
" differs from OSD configuration (%u/%u/%u, %u/%u).\n",
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity,
hdr->data_csum_type, hdr->csum_block_size,
bs->dsk.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity,

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