Compare commits

..
Author SHA1 Message Date
Vitaliy Filippov 6467ebb806 WIP Add AES-XTS client encryption support 2026-02-22 02:48:34 +03:00
Vitaliy Filippov 8c2f0b2c93 Rework msgr send/receive to allow encryption support 2026-02-21 12:31:39 +03:00
Vitaliy Filippov 50db154bb3 Update antietcd to 1.2.4 2026-02-20 21:21:03 +03:00
Vitaliy Filippov 4c53fcdf39 Move fromhexstr() to str_util 2026-02-20 21:18:29 +03:00
Vitaliy Filippov 467d51a48c Add openapi description 2026-02-20 21:18:29 +03:00
Vitaliy Filippov 44fa7c189f Slightly fix API return and input types 2026-02-20 21:18:29 +03:00
Vitaliy Filippov 1502474e20 Implement vitastor-cli serve command to serve simple HTTP API 2026-02-20 21:18:29 +03:00
Vitaliy Filippov 8d93a97374 Implement HTTP server support O_o 2026-02-19 12:38:16 +03:00
Vitaliy Filippov 79f9631f03 Update antietcd to 1.2.3 2026-02-19 12:38:16 +03:00
Vitaliy Filippov 6024b3b2a5 Rename http_response_t to http_message_t 2026-02-19 12:38:16 +03:00
Vitaliy Filippov 069841d899 Extract common HTTP context 2026-02-19 12:38:16 +03:00
Vitaliy Filippov 59ccb73298 Support xxhash 32-bit checksums (data_csum_type=xxh3_32) 2026-02-19 12:38:16 +03:00
Vitaliy Filippov 7add655916 Detect block checksums using csum_block_size, not data_csum_type 2026-02-18 01:08:05 +03:00
Vitaliy Filippov bf1382a5ad Add client certificate support 2026-02-18 01:08:05 +03:00
Vitaliy Filippov b8558e163d Do not re-initialize TLS context every connection 2026-02-18 01:08:05 +03:00
Vitaliy Filippov 3669258078 Add https support to antietcd 2026-02-18 01:08:05 +03:00
Vitaliy Filippov 2a06a3e903 Implement etcd SSL support via OpenSSL
Maybe I should remove all of this and use libwebsockets :)
2026-02-18 01:08:05 +03:00
185 changed files with 13397 additions and 4686 deletions
+7 -8
View File
@@ -1,29 +1,28 @@
FROM node:16-bookworm FROM node:16-bullseye
WORKDIR /root 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; \ RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list; \
echo 'deb http://vitastor.io/debian bookworm main' >> /etc/apt/sources.list; \ echo 'deb http://vitastor.io/debian bullseye main' >> /etc/apt/sources.list; \
echo >> /etc/apt/preferences; \ echo >> /etc/apt/preferences; \
echo 'Package: *' >> /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 'Pin-Priority: 500' >> /etc/apt/preferences; \
echo >> /etc/apt/preferences; \ echo >> /etc/apt/preferences; \
echo 'Package: *' >> /etc/apt/preferences; \ echo 'Package: *' >> /etc/apt/preferences; \
echo 'Pin: origin "vitastor.io"' >> /etc/apt/preferences; \ echo 'Pin: origin "vitastor.io"' >> /etc/apt/preferences; \
echo 'Pin-Priority: 1000' >> /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; \ 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-Recommends false;' >> /etc/apt/apt.conf; \
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
RUN apt-get update 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 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 -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 apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
RUN set -ex; \ RUN set -ex; \
+73 -1
View File
@@ -63,7 +63,7 @@ jobs:
container: ${{env.TEST_IMAGE}}:${{github.sha}} container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps: steps:
# leak sanitizer sometimes crashes # 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: npm_lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -234,6 +234,60 @@ jobs:
echo "" echo ""
done done
test_etcd_fail_https:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: ETCD_SCHEME=https /root/vitastor/tests/test_etcd_fail.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_etcd_fail_https_antietcd:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: ETCD_SCHEME=https ANTIETCD=1 /root/vitastor/tests/test_etcd_fail.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_https:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: ETCD_SCHEME=https /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_interrupted_rebalance: test_interrupted_rebalance:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
@@ -1224,6 +1278,24 @@ jobs:
echo "" echo ""
done done
test_checksum_xxhash:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=xxhash OSD_ARGS="--data_csum_type xxh3_32" /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: test_old_checksum:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
+4
View File
@@ -38,6 +38,10 @@ for my $line (<>)
{ {
$test_name .= '_antietcd'; $test_name .= '_antietcd';
} }
elsif ($1 eq 'ETCD_SCHEME' && $2 eq 'https')
{
$test_name .= '_https';
}
elsif ($1 eq 'OLD') elsif ($1 eq 'OLD')
{ {
$test_name =~ s/^test_/test_old_/s; $test_name =~ s/^test_/test_old_/s;
+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) project(vitastor)
set(VITASTOR_VERSION "3.0.12") set(VITASTOR_VERSION "3.0.3")
include(CTest) include(CTest)
add_custom_target(build_tests) add_custom_target(build_tests)
set_property(TEST PROPERTY ENVIRONMENT LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt) add_custom_target(test
add_test(gen_lsan_suppress COMMAND
${CMAKE_COMMAND} -E echo leak:tcmalloc > "${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt" 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 && ../../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_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) # 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 # kcov --include-path=../../../src ../../kcov ./test_blockstore
add_dependencies(test build_tests)
add_subdirectory(src) add_subdirectory(src)
+2 -2
View File
@@ -25,8 +25,8 @@ RUN apt-get update && \
# NFS mount dependencies # NFS mount dependencies
nfs-common netbase \ nfs-common netbase \
# dependencies of qemu-storage-daemon # dependencies of qemu-storage-daemon
libaio1t64 libc6 libfuse3-4 libglib2.0-0t64 libgmp10 libgnutls30t64 \ libnuma1 liburing2 libglib2.0-0 libfuse3-3 libaio1 libzstd1 libnettle8 \
libhogweed6t64 libnettle8t64 libnuma1 libselinux1 liburing2 libzstd1 zlib1g && \ libgmp10 libhogweed6 libp11-kit0 libidn2-0 libunistring2 libtasn1-6 libpcre2-8-0 libffi8 && \
apt-get clean && \ apt-get clean && \
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf) (echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
+4 -4
View File
@@ -1,5 +1,5 @@
# Compile stage # Compile stage
FROM golang:trixie AS build FROM golang:bookworm AS build
ADD go.sum go.mod /app/ ADD go.sum go.mod /app/
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go mod download -x 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 CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vitastor-csi
# Final stage # Final stage
FROM debian:trixie FROM debian:bookworm
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>" LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
LABEL description="Vitastor CSI Driver" LABEL description="Vitastor CSI Driver"
@@ -36,8 +36,8 @@ ADD deb /deb
RUN apt-get update && \ RUN apt-get update && \
apt-get -y install /deb/vitastor-client_*.deb && \ 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-bookworm-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-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
dpkg -x qemu-utils*.deb tmp1 && \ dpkg -x qemu-utils*.deb tmp1 && \
dpkg -x qemu-block-extra*.deb tmp1 && \ dpkg -x qemu-block-extra*.deb tmp1 && \
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \ 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 ?= v3.0.3
all: build push all: build push
+1 -1
View File
@@ -49,7 +49,7 @@ spec:
capabilities: capabilities:
add: ["SYS_ADMIN"] add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v3.0.12 image: vitalif/vitastor-csi:v3.0.3
args: args:
- "--node=$(NODE_ID)" - "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)" - "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -121,7 +121,7 @@ spec:
privileged: true privileged: true
capabilities: capabilities:
add: ["SYS_ADMIN"] add: ["SYS_ADMIN"]
image: vitalif/vitastor-csi:v3.0.12 image: vitalif/vitastor-csi:v3.0.3
args: args:
- "--node=$(NODE_ID)" - "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)" - "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -5,7 +5,7 @@ package vitastor
const ( const (
vitastorCSIDriverName = "csi.vitastor.io" vitastorCSIDriverName = "csi.vitastor.io"
vitastorCSIDriverVersion = "3.0.12" vitastorCSIDriverVersion = "3.0.3"
) )
// Config struct fills the parameters of request or user input // 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 (3.0.3-1) unstable; urgency=medium
* Bugfixes * 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 curl -s https://git.yourcmc.ru/vitalif/tinyraft/archive/master.tar.gz | tar -zx
cd /root/vitastor/packages/vitastor-$REL 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) # Fucking shit, archives differ between bookworm (xz 5.4.1) and trixie (xz 5.8.1)
cp ../vitastor-bookworm/vitastor_$VER.orig.tar.xz . cp ../vitastor-bookworm/vitastor_$VER.orig.tar.xz .
else else
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.12 VITASTOR_VERSION ?= v3.0.3
all: build push all: build push
+1 -1
View File
@@ -1,3 +1,3 @@
Package: * Package: *
Pin: release n=trixie-backports Pin: release n=bookworm-backports
Pin-Priority: 500 Pin-Priority: 500
+2 -2
View File
@@ -1,2 +1,2 @@
deb http://vitastor.io/debian trixie main deb http://vitastor.io/debian bookworm main
#deb http://http.debian.net/debian/ trixie-backports main deb http://http.debian.net/debian/ bookworm-backports main
@@ -7,7 +7,7 @@ PartOf=vitastor.target
[Service] [Service]
Restart=always Restart=always
EnvironmentFile=/etc/vitastor/docker.conf 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 \ --security-opt seccomp=unconfined --privileged --pid=host --log-driver none --network host --name vitastor vitastor:$VITASTOR_VERSION \
sleep.sh' sleep.sh'
ExecStartPost=udevadm trigger ExecStartPost=udevadm trigger
+1 -1
View File
@@ -4,7 +4,7 @@
# #
# Desired Vitastor version # Desired Vitastor version
VITASTOR_VERSION=v3.0.12 VITASTOR_VERSION=v3.0.3
# Additional arguments for all containers # Additional arguments for all containers
# For example, you may want to specify a custom logging driver here # For example, you may want to specify a custom logging driver here
+3 -2
View File
@@ -2,7 +2,8 @@
set -e set -e
cp -urv /etc/systemd/system/vitastor* /host-etc/systemd/system/ cp -urv /etc/default /host-etc/
cp -urv /etc/udev/rules.d /host-etc/udev/ cp -urv /etc/systemd /host-etc/
cp -urv /etc/udev /host-etc/
cp -urnv /etc/vitastor /host-etc/ cp -urnv /etc/vitastor /host-etc/
cp -urnv /opt/scripts/* /host-bin/ cp -urnv /opt/scripts/* /host-bin/
+7 -34
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_io](#journal_io)
- [journal_sector_buffer_count](#journal_sector_buffer_count) - [journal_sector_buffer_count](#journal_sector_buffer_count)
- [journal_no_same_sector_overwrites](#journal_no_same_sector_overwrites) - [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_small_writes](#throttle_small_writes)
- [throttle_target_iops](#throttle_target_iops) - [throttle_target_iops](#throttle_target_iops)
- [throttle_target_mbs](#throttle_target_mbs) - [throttle_target_mbs](#throttle_target_mbs)
@@ -70,7 +69,6 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [use_atomic_flag](#use_atomic_flag) - [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size) - [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms) - [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address ## bind_address
@@ -281,19 +279,13 @@ Maximum number of journal flushers (see above min_flusher_count).
- Type: boolean - Type: boolean
- Default: true - Default: true
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2). 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
This parameter makes Vitastor keep a copy of metadata area in memory as it is additional read-modify-write cycles during metadata modifications. Metadata
on disk, in addition to the metadata database. When the option is enabled, every area size is currently roughly 224 MB per 1 TB of data. You can turn it off
metadata entry is effectively stored in RAM twice. It's required for good performance to reduce memory usage by this value, but it will hurt performance. This
because it allows to avoid additional read-modify-write cycles during metadata restriction is likely to be removed in the future along with the upgrade
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB of the metadata storage scheme.
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.
## inmemory_journal ## inmemory_journal
@@ -372,8 +364,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 is when you enable journal_no_same_sector_overwrites. In this case set
it to, for example, 1024. 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 ## journal_no_same_sector_overwrites
- Type: boolean - Type: boolean
@@ -387,17 +377,6 @@ journal after writing it instead of possibly overwriting it the second time.
Most (99%) other SSDs don't need this option. 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 ## throttle_small_writes
- Type: boolean - Type: boolean
@@ -754,9 +733,3 @@ This option sets the maximum number of object is a chunk. Moving 100k objects us
- Default: 100 - Default: 100
This option sets the interval between handling two PG count change chunks. 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 -35
View File
@@ -39,7 +39,6 @@
- [journal_io](#journal_io) - [journal_io](#journal_io)
- [journal_sector_buffer_count](#journal_sector_buffer_count) - [journal_sector_buffer_count](#journal_sector_buffer_count)
- [journal_no_same_sector_overwrites](#journal_no_same_sector_overwrites) - [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_small_writes](#throttle_small_writes)
- [throttle_target_iops](#throttle_target_iops) - [throttle_target_iops](#throttle_target_iops)
- [throttle_target_mbs](#throttle_target_mbs) - [throttle_target_mbs](#throttle_target_mbs)
@@ -71,7 +70,6 @@
- [use_atomic_flag](#use_atomic_flag) - [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size) - [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms) - [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address ## bind_address
@@ -289,19 +287,13 @@ Flusher - это микро-поток (корутина), которая коп
- Тип: булево (да/нет) - Тип: булево (да/нет)
- Значение по умолчанию: true - Значение по умолчанию: true
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2). Данный параметр заставляет Vitastor всегда держать область метаданных диска
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
Данный параметр заставляет Vitastor всегда держать копию области метаданных при записи. Размер области метаданных на данный момент составляет примерно
в памяти в том же виде, как она лежит на диске, в дополнение к БД метаданных. 224 МБ на 1 ТБ данных. При включении потребление памяти снизится примерно
То есть, с включённой опцией каждая запись метаданных хранится в памяти дважды. на эту величину, но при этом также снизится и производительность. В будущем,
Это нужно, чтобы избегать дополнительных операций чтения с диска при записи. после обновления схемы хранения метаданных, это ограничение, скорее всего,
Размер области метаданных в старом хранилище составляет примерно 224 МБ на будет ликвидировано.
1 ТБ данных. Вы можете отключить опцию, чтобы снизить потребление памяти
примерно на эту величину, но при этом также снизится и производительность.
Для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3) опция,
возможно, будет переработана в будущем для поддержки работы без полной
загрузки метаданных в памяти.
## inmemory_journal ## inmemory_journal
@@ -384,8 +376,6 @@ fsync небезопасным даже с режимом "directsync".
нужно менять - это если вы включаете journal_no_same_sector_overwrites. В нужно менять - это если вы включаете journal_no_same_sector_overwrites. В
этом случае установите данный параметр, например, в 1024. этом случае установите данный параметр, например, в 1024.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
## journal_no_same_sector_overwrites ## journal_no_same_sector_overwrites
- Тип: булево (да/нет) - Тип: булево (да/нет)
@@ -401,18 +391,6 @@ fsync небезопасным даже с режимом "directsync".
Почти все другие SSD (99% моделей) не требуют данной опции. Почти все другие 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 ## throttle_small_writes
- Тип: булево (да/нет) - Тип: булево (да/нет)
@@ -794,9 +772,3 @@ pg_minsize OSD во время переключений, что может по
- Значение по умолчанию: 100 - Значение по умолчанию: 100
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов. Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
## gc_on_start
- Тип: булево (да/нет)
Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
+14 -50
View File
@@ -253,33 +253,21 @@
type: bool type: bool
default: true default: true
info: | info: |
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2). 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
This parameter makes Vitastor keep a copy of metadata area in memory as it is additional read-modify-write cycles during metadata modifications. Metadata
on disk, in addition to the metadata database. When the option is enabled, every area size is currently roughly 224 MB per 1 TB of data. You can turn it off
metadata entry is effectively stored in RAM twice. It's required for good performance to reduce memory usage by this value, but it will hurt performance. This
because it allows to avoid additional read-modify-write cycles during metadata restriction is likely to be removed in the future along with the upgrade
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB of the metadata storage scheme.
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.
info_ru: | info_ru: |
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2). Данный параметр заставляет Vitastor всегда держать область метаданных диска
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
Данный параметр заставляет Vitastor всегда держать копию области метаданных при записи. Размер области метаданных на данный момент составляет примерно
в памяти в том же виде, как она лежит на диске, в дополнение к БД метаданных. 224 МБ на 1 ТБ данных. При включении потребление памяти снизится примерно
То есть, с включённой опцией каждая запись метаданных хранится в памяти дважды. на эту величину, но при этом также снизится и производительность. В будущем,
Это нужно, чтобы избегать дополнительных операций чтения с диска при записи. после обновления схемы хранения метаданных, это ограничение, скорее всего,
Размер области метаданных в старом хранилище составляет примерно 224 МБ на будет ликвидировано.
1 ТБ данных. Вы можете отключить опцию, чтобы снизить потребление памяти
примерно на эту величину, но при этом также снизится и производительность.
Для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3) опция,
возможно, будет переработана в будущем для поддержки работы без полной
загрузки метаданных в памяти.
- name: inmemory_journal - name: inmemory_journal
type: bool type: bool
default: true default: true
@@ -398,15 +386,11 @@
blocks. The only situation when you should increase it to a larger value 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 is when you enable journal_no_same_sector_overwrites. In this case set
it to, for example, 1024. it to, for example, 1024.
Not applicable to the new store ([meta_format](layout-osd.en.md#meta_format) 3).
info_ru: | info_ru: |
Максимальное число буферов, разрешённых для использования под записываемые Максимальное число буферов, разрешённых для использования под записываемые
в журнал блоки метаданных. Единственная ситуация, в которой этот параметр в журнал блоки метаданных. Единственная ситуация, в которой этот параметр
нужно менять - это если вы включаете journal_no_same_sector_overwrites. В нужно менять - это если вы включаете journal_no_same_sector_overwrites. В
этом случае установите данный параметр, например, в 1024. этом случае установите данный параметр, например, в 1024.
Неприменимо к новому хранилищу ([meta_format](layout-osd.en.md#meta_format) 3).
- name: journal_no_same_sector_overwrites - name: journal_no_same_sector_overwrites
type: bool type: bool
default: false default: false
@@ -418,8 +402,6 @@
journal after writing it instead of possibly overwriting it the second time. journal after writing it instead of possibly overwriting it the second time.
Most (99%) other SSDs don't need this option. 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: | info_ru: |
Включайте данную опцию для SSD вроде Intel D3-S4510 и D3-S4610, которые Включайте данную опцию для SSD вроде Intel D3-S4510 и D3-S4610, которые
ОЧЕНЬ не любят, когда ПО перезаписывает один и тот же сектор несколько раз ОЧЕНЬ не любят, когда ПО перезаписывает один и тот же сектор несколько раз
@@ -430,20 +412,6 @@
самого сектора. самого сектора.
Почти все другие SSD (99% моделей) не требуют данной опции. Почти все другие 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 - name: throttle_small_writes
type: bool type: bool
default: false default: false
@@ -938,7 +906,3 @@
This option sets the interval between handling two PG count change chunks. This option sets the interval between handling two PG count change chunks.
info_ru: | info_ru: |
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов. Данная опция задаёт интервал между обработкой двух порций изменения числа 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. The instruction is very simple.
1. Download a Docker image of the desired version: \ 1. Download a Docker image of the desired version: \
`docker pull vitalif/vitastor:v3.0.12` `docker pull vitalif/vitastor:v3.0.3`
2. Install scripts to the host system: \ 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:v3.0.3 install.sh`
3. Reload udev rules: \ 3. Reload udev rules: \
`udevadm control --reload-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). And 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.
## Upgrading Containers ## Upgrading Containers
+2 -27
View File
@@ -25,39 +25,14 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
Инструкция по установке максимально простая. Инструкция по установке максимально простая.
1. Скачайте Docker-образ желаемой версии: \ 1. Скачайте Docker-образ желаемой версии: \
`docker pull vitalif/vitastor:v3.0.12` `docker pull vitalif/vitastor:v3.0.3`
2. Установите скрипты в хост-систему командой: \ 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:v3.0.3 install.sh`
3. Перезагрузите правила udev: \ 3. Перезагрузите правила udev: \
`udevadm control --reload-rules` `udevadm control --reload-rules`
4. Включите сервис vitastor-host: \
`systemctl enable --now vitastor-host`
После этого вы можете возвращаться к разделу [Быстрый старт](../intro/quickstart.ru.md). После этого вы можете возвращаться к разделу [Быстрый старт](../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), Сначала обязательно проверьте раздел [Обновление Vitastor](../usage/admin.ru.md#обновление-vitastor),
+1 -4
View File
@@ -17,7 +17,6 @@
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main` - Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main` - Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble 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 - Add `-oldstable` to bookworm/bullseye/buster in this line to install the last
stable version from 0.9.x branch instead of 1.x 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`: - 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 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` - 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 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 EPEL: `yum/dnf install epel-release`
- Enable additional CentOS repositories: - Enable additional CentOS repositories:
- CentOS 7: `yum install centos-release-scl` - CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization` - CentOS 8: `dnf install centos-release-advanced-virtualization`
- RHEL 9/10 clones: not required - RHEL 9 clones: not required
- Enable elrepo-kernel: - Enable elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm` - 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` - 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 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` - Install packages: `yum/dnf install vitastor lpsolve etcd kernel-ml qemu-kvm`
## Installation requirements ## Installation requirements
+1 -4
View File
@@ -17,7 +17,6 @@
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main` - Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main` - Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble 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 в этой строке, чтобы - Добавьте `-oldstable` к слову bookworm/bullseye/buster в этой строке, чтобы
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
- Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`: - Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`:
@@ -34,17 +33,15 @@
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm` - 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` - 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 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` - Включите EPEL: `yum/dnf install epel-release`
- Включите дополнительные репозитории CentOS: - Включите дополнительные репозитории CentOS:
- CentOS 7: `yum install centos-release-scl` - CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization` - CentOS 8: `dnf install centos-release-advanced-virtualization`
- Клоны RHEL 9/10: не нужно - Клоны RHEL 9: не нужно
- Включите elrepo-kernel: - Включите elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm` - 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` - 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 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` - Установите пакеты: `yum/dnf install vitastor lpsolve etcd kernel-ml qemu-kvm`
## Установочные требования ## Установочные требования
+2 -1
View File
@@ -16,7 +16,8 @@
designated initializers support from C++20 designated initializers support from C++20
- CMake - CMake
- jerasure headers and libraries - 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 ## Basic instructions
+2 -1
View File
@@ -16,7 +16,8 @@
назначенных инициализаторов (designated initializers) из C++20 назначенных инициализаторов (designated initializers) из C++20
- CMake - CMake
- Заголовки и библиотеки jerasure - Заголовки и библиотеки jerasure
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3 - Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm
- tcmalloc (google-perftools-dev)
## Базовая инструкция ## Базовая инструкция
-1
View File
@@ -262,4 +262,3 @@ Options:
| `--logfile <FILE>` | log to the specified file | | `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) | | `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize | | `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |
-1
View File
@@ -274,4 +274,3 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл | | `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) | | `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска | | `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+1 -1
Submodule json11 updated: edcd85b8bd...fd37016cf8
+11 -7
View File
@@ -18,7 +18,7 @@ class AntiEtcdAdapter
cluster = cluster ? (''+(cluster||'')).split(/,+/) : []; cluster = cluster ? (''+(cluster||'')).split(/,+/) : [];
cluster = Object.keys(cluster.reduce((a, url) => cluster = Object.keys(cluster.reduce((a, url) =>
{ {
a[url.toLowerCase().replace(/^(https?:\/\/)/, '').replace(/\/.*$/, '')] = true; a[url.toLowerCase().replace(/^(https?:\/\/)?(.*?)(\/.*)?$/, (m, m1, m2) => (m1||'http://')+m2)] = true;
return a; return a;
}, {})); }, {}));
const cfg_port = config.antietcd_port; const cfg_port = config.antietcd_port;
@@ -26,7 +26,8 @@ class AntiEtcdAdapter
is_local['0.0.0.0'] = true; is_local['0.0.0.0'] = true;
is_local['::'] = true; is_local['::'] = true;
is_local[''] = true; is_local[''] = true;
const selected = cluster.map(s => s.split(':', 2)).filter(ip => is_local[ip[0]] && (!cfg_port || ip[1] == cfg_port)); // split :, 3 -> <schema>:<//ip>:<port>
const selected = cluster.map(s => s.split(':', 3)).filter(ip => is_local[ip[1].substr(2)] && (!cfg_port || ip[2] == cfg_port));
if (selected.length > 1) if (selected.length > 1)
{ {
console.error('More than 1 etcd_address matches local IPs, please specify port'); console.error('More than 1 etcd_address matches local IPs, please specify port');
@@ -35,12 +36,15 @@ class AntiEtcdAdapter
else if (selected.length == 1) else if (selected.length == 1)
{ {
const antietcd_config = { const antietcd_config = {
ip: selected[0][0], ip: selected[0][1].substr(2),
port: selected[0][1], port: selected[0][2],
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][1]+'.json.gz'), cert: config.antietcd_cert,
key: config.antietcd_key,
ca: config.etcd_ca,
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][2]+'.json.gz'),
persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }), persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }),
node_id: selected[0][0]+':'+selected[0][1], // node_id = ip:port node_id: selected[0][1].substr(2)+':'+selected[0][2], // node_id = ip:port
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c] = "http://"+c; return a; }, {})), cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c.replace(/^(https?:\/\/)/, '')] = c; return a; }, {})),
cluster_key: (config.etcd_prefix || '/vitastor'), cluster_key: (config.etcd_prefix || '/vitastor'),
stale_read: 1, stale_read: 1,
log_level: 1, log_level: 1,
+15 -6
View File
@@ -1,7 +1,9 @@
// Copyright (c) Vitaliy Filippov, 2019+ // Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details) // License: VNPL-1.1 (see README.md for details)
const fs = require('fs');
const http = require('http'); const http = require('http');
const https = require('https');
const WebSocket = require('ws'); const WebSocket = require('ws');
const { b64, local_ips } = require('./utils.js'); const { b64, local_ips } = require('./utils.js');
@@ -15,11 +17,18 @@ class EtcdAdapter
this.ws = null; this.ws = null;
this.ws_alive = false; this.ws_alive = false;
this.ws_keepalive_timer = null; this.ws_keepalive_timer = null;
this.opts = {};
} }
parse_config(config) parse_config(config)
{ {
this.parse_etcd_addresses(config.etcd_address||config.etcd_url); this.parse_etcd_addresses(config.etcd_address||config.etcd_url);
if (config.etcd_client_cert)
this.opts.cert = fs.readFileSync(config.etcd_client_cert, { encoding: 'utf-8' });
if (config.etcd_client_key)
this.opts.key = fs.readFileSync(config.etcd_client_key, { encoding: 'utf-8' });
if (config.etcd_ca)
this.opts.ca = fs.readFileSync(config.etcd_ca, { encoding: 'utf-8' });
} }
parse_etcd_addresses(addrs) parse_etcd_addresses(addrs)
@@ -39,7 +48,7 @@ class EtcdAdapter
for (let url of addrs) for (let url of addrs)
{ {
let scheme = 'http'; let scheme = 'http';
url = url.trim().replace(/^(https?):\/\//, (m, m1) => { scheme = m1; return ''; }); url = url.trim().replace(/^(https?):\/\//i, (m, m1) => { scheme = m1.toLowerCase(); return ''; });
const slash = url.indexOf('/'); const slash = url.indexOf('/');
const colon = url.indexOf(':'); const colon = url.indexOf(':');
const is_local = is_local_ip[colon >= 0 ? url.substr(0, colon) : (slash >= 0 ? url.substr(0, slash) : url)]; const is_local = is_local_ip[colon >= 0 ? url.substr(0, colon) : (slash >= 0 ? url.substr(0, slash) : url)];
@@ -130,7 +139,7 @@ class EtcdAdapter
} }
ok(false); ok(false);
}, this.mon.config.etcd_mon_timeout); }, this.mon.config.etcd_mon_timeout);
this.ws = new WebSocket(base+'/watch'); this.ws = new WebSocket(base+'/watch', this.opts);
this.ws_used_url = cur_addr; this.ws_used_url = cur_addr;
const fail = () => const fail = () =>
{ {
@@ -272,7 +281,7 @@ class EtcdAdapter
{ {
throw new Error(MON_STOPPED); throw new Error(MON_STOPPED);
} }
const res = await POST(base+path, body, timeout); const res = await POST(base+path, body, timeout, this.opts);
if (this.mon.stopped) if (this.mon.stopped)
{ {
throw new Error(MON_STOPPED); throw new Error(MON_STOPPED);
@@ -298,7 +307,7 @@ class EtcdAdapter
} }
} }
function POST(url, body, timeout) function POST(url, body, timeout, opts)
{ {
return new Promise(ok => return new Promise(ok =>
{ {
@@ -310,10 +319,10 @@ function POST(url, body, timeout)
req = null; req = null;
ok({ error: 'timeout' }); ok({ error: 'timeout' });
}, timeout) : null; }, timeout) : null;
let req = http.request(url, { method: 'POST', headers: { let req = (url.substr(0, 5) == 'https' ? https : http).request(url, { method: 'POST', headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Content-Length': body_text.length, 'Content-Length': body_text.length,
} }, (res) => }, ...(opts||{}) }, (res) =>
{ {
if (!req) if (!req)
{ {
+4 -1
View File
@@ -45,7 +45,10 @@ const etcd_tree = {
config_path: "/etc/vitastor/vitastor.conf", config_path: "/etc/vitastor/vitastor.conf",
etcd_prefix: "/vitastor", etcd_prefix: "/vitastor",
// etcd connection - configurable online // etcd connection - configurable online
etcd_address: "10.0.115.10:2379/v3", etcd_address: "http://10.0.115.10:2379/v3",
etcd_client_cert: "",
etcd_client_key: "",
etcd_ca: "",
// mon // mon
etcd_mon_ttl: 5, // min: 1 etcd_mon_ttl: 5, // min: 1
etcd_mon_timeout: 1000, // ms. min: 0 etcd_mon_timeout: 1000, // ms. min: 0
+1 -1
View File
@@ -16,7 +16,7 @@ async function create_http_server(cfg, handler)
}; };
if (cfg.mon_https_ca) 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) if (cfg.mon_https_client_auth)
{ {
+2 -2
View File
@@ -627,7 +627,7 @@ class Mon
if (this.state.pg.history[pool_id] && if (this.state.pg.history[pool_id] &&
this.state.pg.history[pool_id][pg]) 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 = []; const real_prev_pgs = [];
@@ -719,7 +719,7 @@ class Mon
this.next_recheck_timer = null; this.next_recheck_timer = null;
this.next_recheck_at = 0; this.next_recheck_at = 0;
this.schedule_recheck(); this.schedule_recheck();
}, (this.next_recheck_at-now)*1000); }, now-this.next_recheck_at);
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "vitastor-mon", "name": "vitastor-mon",
"version": "3.0.12", "version": "3.0.3",
"description": "Vitastor SDS monitor service", "description": "Vitastor SDS monitor service",
"main": "mon-main.js", "main": "mon-main.js",
"scripts": { "scripts": {
+2 -2
View File
@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
finish_pg_history(merged_history[1]); finish_pg_history(merged_history[1]);
for (let i = 0; i < new_pg_count; i++) 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 // 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++) 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) 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); 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.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||{}) for (const pool_id in st.inode_stats||{})
{ {
diff.inode_stats[pool_id] = {}; diff.inode_stats[pool_id] = {};
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "vitastor", "name": "vitastor",
"version": "3.0.12", "version": "3.0.3",
"description": "Low-level native bindings to Vitastor client library", "description": "Low-level native bindings to Vitastor client library",
"main": "index.js", "main": "index.js",
"keywords": [ "keywords": [
+232 -30
View File
@@ -50,7 +50,7 @@ from cinder.volume import configuration
from cinder.volume import driver from cinder.volume import driver
from cinder.volume import volume_utils from cinder.volume import volume_utils
VITASTOR_VERSION = '3.0.12' VITASTOR_VERSION = '3.0.3'
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -275,7 +275,7 @@ class VitastorDriver(driver.CloneableImageVD,
LOG.exception('error getting vitastor pool stats: '+str(e)) LOG.exception('error getting vitastor pool stats: '+str(e))
self._stats = stats self._stats = stats
def get_volume_stats(self, refresh=False): def get_volume_stats(self, refresh=False):
"""Get volume stats. """Get volume stats.
If 'refresh' is True, run update the stats first. If 'refresh' is True, run update the stats first.
@@ -291,14 +291,6 @@ class VitastorDriver(driver.CloneableImageVD,
else: else:
return (1 + resp['kvs'][0]['value'], resp['kvs'][0]['mod_revision']) 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): def create_volume(self, volume):
"""Creates a logical volume.""" """Creates a logical volume."""
@@ -310,7 +302,7 @@ class VitastorDriver(driver.CloneableImageVD,
LOG.debug("creating volume '%s'", vol_name) 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: if volume.encryption_key_id:
self._create_encrypted_volume(volume, volume.obj_context) self._create_encrypted_volume(volume, volume.obj_context)
@@ -354,7 +346,7 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = utils.convert_str(snapshot.name) snap_name = utils.convert_str(snapshot.name)
if snap_name.find('@') >= 0 or snap_name.find('/') >= 0: if snap_name.find('@') >= 0 or snap_name.find('/') >= 0:
raise exception.VolumeBackendAPIException(data = '@ and / are forbidden in volume and snapshot names') 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): def snapshot_revert_use_temp_snapshot(self):
"""Disable the use of a temporary snapshot on revert.""" """Disable the use of a temporary snapshot on revert."""
@@ -367,8 +359,21 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = utils.convert_str(snapshot.name) snap_name = utils.convert_str(snapshot.name)
# Delete the image and recreate it from the snapshot # Delete the image and recreate it from the snapshot
self._cli('delete image', 'rm', vol_name) args = [ 'vitastor-cli', 'rm', vol_name, *(self._vitastor_args()) ]
self._cli('recreate image', 'create', '--parent', vol_name+'@'+snap_name, vol_name) 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): def delete_snapshot(self, snapshot):
"""Deletes a snapshot.""" """Deletes a snapshot."""
@@ -376,7 +381,15 @@ class VitastorDriver(driver.CloneableImageVD,
vol_name = utils.convert_str(snapshot.volume_name) vol_name = utils.convert_str(snapshot.volume_name)
snap_name = utils.convert_str(snapshot.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): def _child_count(self, parents):
children = 0 children = 0
@@ -414,7 +427,13 @@ class VitastorDriver(driver.CloneableImageVD,
if src_vref.admin_metadata.get('readonly') == 'True': if src_vref.admin_metadata.get('readonly') == 'True':
# source volume is a volume-image cache entry or other readonly volume # source volume is a volume-image cache entry or other readonly volume
# clone without intermediate snapshot # 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 {} return {}
clone_snap = "%s@%s.clone_snap" % (src_name, dest_name) clone_snap = "%s@%s.clone_snap" % (src_name, dest_name)
@@ -427,12 +446,15 @@ class VitastorDriver(driver.CloneableImageVD,
clone_snap = dest_name clone_snap = dest_name
make_img = False make_img = False
LOG.debug("creating snapshot '%s'", clone_snap) LOG.debug("creating layer '%s' under '%s'", clone_snap, src_name)
self._cli('create base snapshot', 'snap-create', '--allow-existing', '1', clone_snap) new_cfg = self._create_snapshot(src_name, clone_snap, True)
if make_img: if make_img:
# Then create a clone from it # 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 {} return {}
@@ -442,8 +464,7 @@ class VitastorDriver(driver.CloneableImageVD,
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
snap_name = utils.convert_str(snapshot.name) snap_name = utils.convert_str(snapshot.name)
src_snap = 'volume-'+snapshot.volume_id+'@'+snap_name snap = self._get_image('volume-'+snapshot.volume_id+'@'+snap_name)
snap = self._get_image(src_snap)
if not snap: if not snap:
raise exception.SnapshotNotFound(snapshot_id = snap_name) raise exception.SnapshotNotFound(snapshot_id = snap_name)
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id']) snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])
@@ -452,8 +473,12 @@ class VitastorDriver(driver.CloneableImageVD,
size = snap['cfg']['size'] size = snap['cfg']['size']
if int(volume.size): if int(volume.size):
size = int(volume.size) * units.Gi 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 {} return {}
def _vitastor_args(self): def _vitastor_args(self):
@@ -480,7 +505,49 @@ class VitastorDriver(driver.CloneableImageVD,
"""Deletes a logical volume.""" """Deletes a logical volume."""
vol_name = utils.convert_str(volume.name) 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): def retype(self, context, volume, new_type, diff, host):
"""Change extra type specifications for a volume.""" """Change extra type specifications for a volume."""
@@ -500,6 +567,98 @@ class VitastorDriver(driver.CloneableImageVD,
"""Removes an export for a logical volume.""" """Removes an export for a logical volume."""
pass 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): def initialize_connection(self, volume, connector):
data = { data = {
'driver_volume_type': 'vitastor', 'driver_volume_type': 'vitastor',
@@ -538,9 +697,13 @@ class VitastorDriver(driver.CloneableImageVD,
size = int(volume.size) * units.Gi size = int(volume.size) * units.Gi
dest_name = utils.convert_str(volume.name) dest_name = utils.convert_str(volume.name)
# Find or create the base snapshot # 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 # 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 ({}, True)
return ({}, False) return ({}, False)
@@ -607,8 +770,26 @@ class VitastorDriver(driver.CloneableImageVD,
def extend_volume(self, volume, new_size): def extend_volume(self, volume, new_size):
"""Extend an existing volume.""" """Extend an existing volume."""
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
size = int(new_size) * units.Gi while True:
self._cli('extend volume', 'modify', vol_name, '--resize', new_size) 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( LOG.debug(
"Extend volume from %(old_size)s GB to %(new_size)s GB.", "Extend volume from %(old_size)s GB to %(new_size)s GB.",
{'old_size': volume.size, 'new_size': new_size} {'old_size': volume.size, 'new_size': new_size}
@@ -681,7 +862,28 @@ class VitastorDriver(driver.CloneableImageVD,
""" """
from_name = self._get_existing_name(existing_ref) from_name = self._get_existing_name(existing_ref)
to_name = utils.convert_str(volume.name) 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): def unmanage(self, volume):
pass pass
@@ -754,7 +956,7 @@ class VitastorDriver(driver.CloneableImageVD,
snap_name = self._get_existing_name(existing_ref) snap_name = self._get_existing_name(existing_ref)
from_name = vol_name+'@'+snap_name from_name = vol_name+'@'+snap_name
to_name = vol_name+'@'+utils.convert_str(snapshot.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): def unmanage_snapshot(self, snapshot):
"""Removes the specified snapshot from Cinder management.""" """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;
}
+28 -171
View File
@@ -1,172 +1,29 @@
diff --git a/block/meson.build b/block/meson.build diff --git a/src/client/qemu_driver.c b/src/client/qemu_driver.c
index 34b1b2a306..24ca0f1e52 100644 index d8356dab..5f4cd50d 100644
--- a/block/meson.build --- a/src/client/qemu_driver.c
+++ b/block/meson.build +++ b/src/client/qemu_driver.c
@@ -114,6 +114,7 @@ foreach m : [ @@ -974,14 +974,21 @@ static void vitastor_co_read_bitmap_cb(void *opaque, long retval, uint8_t *bitma
[libnfs, 'nfs', files('nfs.c')], #endif
[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
+vitastor = not_found -static int coroutine_fn vitastor_co_block_status(
+if not get_option('vitastor').auto() or have_block - BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'], - int64_t *pnum, int64_t *map, BlockDriverState **file)
+ required: get_option('vitastor')) +static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
+ if libvitastor_client.found() +#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ if cc.links(''' + unsigned int mode,
+ #include <vitastor_c.h> +#else
+ int main(void) { + bool want_zero,
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +#endif
+ return 0; + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file)
+ }''', dependencies: libvitastor_client) {
+ vitastor = declare_dependency(dependencies: libvitastor_client) // Allocated => return BDRV_BLOCK_DATA|BDRV_BLOCK_OFFSET_VALID
+ elif get_option('vitastor').enabled() // Not allocated => return 0
+ error('could not link libvitastor_client') // Error => return -errno
+ else // Set pnum to length of the extent, `*map` = `offset`, `*file` = `bs`
+ warning('could not link libvitastor_client, disabling') +#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ endif + int want_zero = (mode == BDRV_WANT_PRECISE);
+ endif +#endif
+endif VitastorRPC task;
+ VitastorClient *client = bs->opaque;
glusterfs = not_found uint64_t inode = client->watch ? vitastor_c_inode_get_num(client->watch) : client->inode;
glusterfs_ftruncate_has_stat = false
glusterfs_iocb_has_stat = false
@@ -2547,6 +2567,7 @@ endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
@@ -4972,6 +4993,7 @@ summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
+summary_info += {'vitastor support': vitastor}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
diff --git a/meson_options.txt b/meson_options.txt
index fff1521e58..f0844c0e00 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -202,6 +202,8 @@ option('pvg', type: 'feature', value: 'auto',
description: 'macOS paravirtualized graphics support')
option('rbd', type : 'feature', value : 'auto',
description: 'Ceph block device driver')
+option('vitastor', type : 'feature', value : 'auto',
+ description: 'Vitastor block device driver')
option('opengl', type : 'feature', value : 'auto',
description: 'OpenGL support')
option('rdma', type : 'feature', value : 'auto',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index dc6eb4ae23..d043f4340e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3280,7 +3280,7 @@
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
'raw', 'rbd',
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
- 'ssh', 'throttle', 'vdi', 'vhdx',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -4363,6 +4363,28 @@
'*key-secret': 'str',
'*server': ['InetSocketAddressBase'] } }
+##
+# @BlockdevOptionsVitastor:
+#
+# Driver specific block device options for vitastor
+#
+# @image: Image name
+# @inode: Inode number
+# @pool: Pool ID
+# @size: Desired image size in bytes
+# @config-path: Path to Vitastor configuration
+# @etcd-host: etcd connection address(es)
+# @etcd-prefix: etcd key/value prefix
+##
+{ 'struct': 'BlockdevOptionsVitastor',
+ 'data': { '*inode': 'uint64',
+ '*pool': 'uint64',
+ '*size': 'uint64',
+ '*image': 'str',
+ '*config-path': 'str',
+ '*etcd-host': 'str',
+ '*etcd-prefix': 'str' } }
+
##
# @ReplicationMode:
#
@@ -4831,6 +4853,7 @@
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vitastor': 'BlockdevOptionsVitastor',
'virtio-blk-vfio-pci':
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
'if': 'CONFIG_BLKIO' },
@@ -5304,6 +5327,20 @@
'*cluster-size' : 'size',
'*encrypt' : 'RbdEncryptionCreateOptions' } }
+##
+# @BlockdevCreateOptionsVitastor:
+#
+# Driver specific image creation options for Vitastor.
+#
+# @location: Where to store the new image file. This location cannot
+# point to a snapshot.
+#
+# @size: Size of the virtual disk in bytes
+##
+{ 'struct': 'BlockdevCreateOptionsVitastor',
+ 'data': { 'location': 'BlockdevOptionsVitastor',
+ 'size': 'size' } }
+
##
# @BlockdevVmdkSubformat:
#
@@ -5526,6 +5563,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 0ebe6bc52a..2c37ad3892 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -175,6 +175,7 @@ meson_options_help() {
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
printf "%s\n" ' qpl Query Processing Library support'
printf "%s\n" ' rbd Ceph block device driver'
+ printf "%s\n" ' vitastor Vitastor block device driver'
printf "%s\n" ' rdma Enable RDMA-based migration'
printf "%s\n" ' replication replication support'
printf "%s\n" ' rust Rust support'
@@ -459,6 +460,8 @@ _meson_option_parse() {
--disable-qpl) printf "%s" -Dqpl=disabled ;;
--enable-rbd) printf "%s" -Drbd=enabled ;;
--disable-rbd) printf "%s" -Drbd=disabled ;;
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
--enable-rdma) printf "%s" -Drdma=enabled ;;
--disable-rdma) printf "%s" -Drdma=disabled ;;
--enable-relocatable) printf "%s" -Drelocatable=true ;;
-172
View File
@@ -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 ;;
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor Name: vitastor
Version: 3.0.12 Version: 3.0.3
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-3.0.12.el10.tar.gz Source0: vitastor-3.0.3.el10.tar.gz
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
BuildRequires: gcc-c++ BuildRequires: gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor Name: vitastor
Version: 3.0.12 Version: 3.0.3
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-3.0.12.el7.tar.gz Source0: vitastor-3.0.3.el7.tar.gz
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
BuildRequires: devtoolset-9-gcc-c++ BuildRequires: devtoolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor Name: vitastor
Version: 3.0.12 Version: 3.0.3
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-3.0.12.el8.tar.gz Source0: vitastor-3.0.3.el8.tar.gz
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++ BuildRequires: gcc-toolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor Name: vitastor
Version: 3.0.12 Version: 3.0.3
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-3.0.12.el9.tar.gz Source0: vitastor-3.0.3.el9.tar.gz
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
BuildRequires: gcc-c++ BuildRequires: gcc-c++
+8 -2
View File
@@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 2.8...3.30) cmake_minimum_required(VERSION 2.8.12)
project(vitastor) project(vitastor)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CTest)
include(CheckIncludeFile) include(CheckIncludeFile)
find_package(PkgConfig) find_package(PkgConfig)
@@ -20,7 +21,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
endif() endif()
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage") set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
add_definitions(-DVITASTOR_VERSION="3.0.12") add_definitions(-DVITASTOR_VERSION="3.0.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_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) add_link_options(-fno-omit-frame-pointer)
if (${WITH_ASAN}) if (${WITH_ASAN})
@@ -74,6 +75,11 @@ if (RDMACM_LIBRARIES)
add_definitions(-DWITH_RDMACM) add_definitions(-DWITH_RDMACM)
endif (RDMACM_LIBRARIES) endif (RDMACM_LIBRARIES)
find_package(OpenSSL)
if (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
endif (OPENSSL_FOUND)
if (${WITH_SYSTEM_LIBURING}) if (${WITH_SYSTEM_LIBURING})
pkg_check_modules(LIBURING REQUIRED liburing>=2.10) pkg_check_modules(LIBURING REQUIRED liburing>=2.10)
include_directories(${LIBURING_INCLUDE_DIRS}) include_directories(${LIBURING_INCLUDE_DIRS})
+2 -2
View File
@@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 2.8...3.30) cmake_minimum_required(VERSION 2.8.12)
project(vitastor) project(vitastor)
# libvitastor_blk.a # libvitastor_blk.a
add_library(vitastor_blk STATIC add_library(vitastor_blk STATIC
../util/allocator.cpp ../util/crc32c.c ../util/ringloop.cpp ../util/allocator.cpp ../util/crc32c.c ../util/xxhash.c ../util/ringloop.cpp
multilist.cpp blockstore_heap.cpp blockstore_disk.cpp multilist.cpp blockstore_heap.cpp blockstore_disk.cpp
blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp
blockstore_flush.cpp blockstore_read.cpp blockstore_stable.cpp blockstore_sync.cpp blockstore_write.cpp blockstore_flush.cpp blockstore_read.cpp blockstore_stable.cpp blockstore_sync.cpp blockstore_write.cpp
-5
View File
@@ -228,9 +228,4 @@ public:
virtual uint64_t get_journal_size() = 0; virtual uint64_t get_journal_size() = 0;
virtual uint32_t get_bitmap_granularity() = 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;
}; };
+8 -5
View File
@@ -83,20 +83,21 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
{ {
data_csum_type = BLOCKSTORE_CSUM_CRC32C; data_csum_type = BLOCKSTORE_CSUM_CRC32C;
} }
else if (config["data_csum_type"] == "xxh3_32")
{
data_csum_type = BLOCKSTORE_CSUM_XXH3_32;
}
else if (config["data_csum_type"] == "" || config["data_csum_type"] == "none") else if (config["data_csum_type"] == "" || config["data_csum_type"] == "none")
{ {
data_csum_type = BLOCKSTORE_CSUM_NONE; data_csum_type = BLOCKSTORE_CSUM_NONE;
} }
else else
{ {
throw std::runtime_error("data_csum_type="+config["data_csum_type"]+" is unsupported, only \"crc32c\" and \"none\" are supported"); throw std::runtime_error("data_csum_type="+config["data_csum_type"]+" is unsupported, only \"crc32c\", \"xxh3_32\" and \"none\" are supported");
} }
csum_block_size = parse_size(config["csum_block_size"]); csum_block_size = parse_size(config["csum_block_size"]);
discard_on_start = config.find("discard_on_start") != config.end() && 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"); (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"]); min_discard_size = parse_size(config["min_discard_size"]);
if (!min_discard_size) if (!min_discard_size)
min_discard_size = 1024*1024; min_discard_size = 1024*1024;
@@ -176,7 +177,9 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
} }
if (data_block_size / bitmap_granularity < 8) if (data_block_size / bitmap_granularity < 8)
{ {
throw std::runtime_error("Data block size must be at least bitmap_granularity*8"); fprintf(stderr, "Warning: block_size (%u) / bitmap_granularity (%u) = %u bits. "
"Consider using larger block_size or bitmap_granularity for better performance.\n",
data_block_size, bitmap_granularity, data_block_size / bitmap_granularity);
} }
if (!data_csum_type) if (!data_csum_type)
{ {
+4 -7
View File
@@ -16,6 +16,7 @@
#define BLOCKSTORE_CSUM_NONE 0 #define BLOCKSTORE_CSUM_NONE 0
// Lower byte of checksum type is its length // Lower byte of checksum type is its length
#define BLOCKSTORE_CSUM_CRC32C 0x104 #define BLOCKSTORE_CSUM_CRC32C 0x104
#define BLOCKSTORE_CSUM_XXH3_32 0x204
#define MOCK_DATA_FD 1000 #define MOCK_DATA_FD 1000
#define MOCK_META_FD 1001 #define MOCK_META_FD 1001
@@ -26,14 +27,14 @@ class allocator_t;
struct blockstore_disk_t struct blockstore_disk_t
{ {
std::string data_device, meta_device, journal_device; 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; uint64_t cfg_journal_size, cfg_data_size;
// Required write alignment and journal/metadata/data areas' location alignment // Required write alignment and journal/metadata/data areas' location alignment
uint32_t disk_alignment = 4096; uint32_t disk_alignment = 4096;
// Journal block size - minimum_io_size of the journal device is the best choice // 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 // 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 // Atomic write size of the data block device
uint32_t atomic_write_size = 4096; uint32_t atomic_write_size = 4096;
// Whether we should set RWF_ATOMIC on atomic writes // Whether we should set RWF_ATOMIC on atomic writes
@@ -57,10 +58,6 @@ struct blockstore_disk_t
bool inmemory_journal = true; bool inmemory_journal = true;
// Data discard granularity and minimum size (for the sake of performance) // Data discard granularity and minimum size (for the sake of performance)
bool discard_on_start = false; 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 min_discard_size = 1024*1024;
uint64_t discard_granularity = 0; uint64_t discard_granularity = 0;
+18 -112
View File
@@ -174,18 +174,14 @@ bool journal_flusher_co::loop()
else if (wait_state == 19) goto resume_19; else if (wait_state == 19) goto resume_19;
else if (wait_state == 20) goto resume_20; else if (wait_state == 20) goto resume_20;
else if (wait_state == 21) goto resume_21; else if (wait_state == 21) goto resume_21;
else if (wait_state == 22) goto resume_22;
else if (wait_state == 23) goto resume_23;
else if (wait_state == 24) goto resume_24;
else if (wait_state == 25) goto resume_25;
resume_0: resume_0:
wait_state = 0; wait_state = 0;
wait_count = 0; wait_count = 0;
cur_oid = {}; cur_oid = {};
res = bs->heap->get_next_compact(cur_oid); res = bs->heap->get_next_compact(cur_oid);
// Advance fsynced_lsn every <journal_trim_interval> intent writes
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0) 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; bs->intent_write_counter = 0;
resume_17: resume_17:
resume_18: resume_18:
@@ -200,7 +196,6 @@ resume_21:
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 && 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 || !bs->dsk.disable_data_fsync))
{ {
// When under pressure, do an additional fsync to force entries to be marked compactable
flusher->active_flushers++; flusher->active_flushers++;
resume_14: resume_14:
resume_15: resume_15:
@@ -264,9 +259,11 @@ resume_1:
if (wr->type() == BS_HEAP_SMALL_WRITE || if (wr->type() == BS_HEAP_SMALL_WRITE ||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity) 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, auto res = 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 wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
? COPY_BUF_SKIP_CSUM : 0); ? COPY_BUF_SKIP_CSUM : 0);
if (res > 0)
copy_count++;
} }
}); });
if (!compact_info.compact_lsn) if (!compact_info.compact_lsn)
@@ -276,25 +273,6 @@ resume_1:
bs->heap->unlock_entry(cur_oid); bs->heap->unlock_entry(cur_oid);
goto resume_0; goto resume_0;
} }
flusher->active_flushers++;
for (i = 0; i < read_vec.size(); i++)
{
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
{
copy_count++;
}
}
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
init_fsync_data();
}
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size); mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
if (!bitmap_copied) if (!bitmap_copied)
{ {
@@ -313,6 +291,13 @@ resume_1:
csum_copy.clear(); csum_copy.clear();
} }
clean_loc = compact_info.clean_wr->big_location(bs->heap); clean_loc = compact_info.clean_wr->big_location(bs->heap);
flusher->active_flushers++;
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
overwrite_start = overwrite_end = 0; overwrite_start = overwrite_end = 0;
if (read_vec.size() > 0) if (read_vec.size() > 0)
{ {
@@ -351,13 +336,6 @@ resume_3:
if (res == ENOENT || res == EDOM) if (res == ENOENT || res == EDOM)
{ {
// Abort compaction // 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); flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid); bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--; flusher->active_flushers--;
@@ -371,7 +349,10 @@ resume_4:
if (res == ENOENT) if (res == ENOENT)
{ {
// Abort compaction // Abort compaction
goto abort_compact; flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--;
goto resume_0;
} }
if (res == EAGAIN) if (res == EAGAIN)
{ {
@@ -400,14 +381,14 @@ resume_9:
for (i = 0; i < read_vec.size(); i++) for (i = 0; i < read_vec.size(); i++)
{ {
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) && 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); assert(read_vec[i].buf);
await_sqe(10); await_sqe(10);
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED 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 }; ? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
data->callback = simple_callback_w; 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); io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
wait_count++; wait_count++;
} }
@@ -418,17 +399,6 @@ resume_11:
wait_state = 11; wait_state = 11;
return false; 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... // 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);
// Mark the object compacted, but don't free and remove small_writes // Mark the object compacted, but don't free and remove small_writes
@@ -438,14 +408,12 @@ resume_25:
if (!cur_obj) if (!cur_obj)
{ {
// Abort compaction // Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid); flusher->flushing.erase(cur_oid);
goto resume_0; goto resume_0;
} }
if (!calc_block_checksums()) if (!calc_block_checksums())
{ {
// Abort compaction // Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid); flusher->flushing.erase(cur_oid);
goto resume_0; goto resume_0;
} }
@@ -454,7 +422,6 @@ resume_25:
if (res == EBUSY) if (res == EBUSY)
{ {
// Abort compaction, object is already overwritten by something else // Abort compaction, object is already overwritten by something else
flusher->active_flushers--;
flusher->flushing.erase(cur_oid); flusher->flushing.erase(cur_oid);
goto resume_0; goto resume_0;
} }
@@ -619,7 +586,7 @@ int journal_flusher_co::check_and_punch_checksums()
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL); bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
} }
} }
// Modified, we should punch_holes and then write the block to disk // Modified, we should add_punch_holes and then write the block to disk
return EBUSY; return EBUSY;
} }
@@ -732,67 +699,6 @@ resume_1:
return true; 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) bool journal_flusher_co::fsync_meta(int wait_base)
{ {
if (wait_state == wait_base) goto resume_0; if (wait_state == wait_base) goto resume_0;
-13
View File
@@ -25,15 +25,6 @@ struct flusher_meta_write_t
std::map<uint64_t, meta_sector_t>::iterator it; 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; class journal_flusher_t;
// Journal flusher coroutine // Journal flusher coroutine
@@ -67,7 +58,6 @@ class journal_flusher_co
int i, res; int i, res;
bool read_to_fill_incomplete; bool read_to_fill_incomplete;
int copy_count; int copy_count;
std::list<flusher_data_sync_t>::iterator cur_sync;
friend class journal_flusher_t; friend class journal_flusher_t;
@@ -78,8 +68,6 @@ class journal_flusher_co
bool calc_block_checksums(); bool calc_block_checksums();
bool write_meta_block(int wait_base); bool write_meta_block(int wait_base);
bool read_buffered(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_meta(int wait_base);
bool fsync_buffer(int wait_base); bool fsync_buffer(int wait_base);
bool trim_lsn(int wait_base); bool trim_lsn(int wait_base);
@@ -100,7 +88,6 @@ class journal_flusher_t
robin_hood::unordered_flat_set<object_id> flushing; robin_hood::unordered_flat_set<object_id> flushing;
int active_flushers = 0; int active_flushers = 0;
std::list<flusher_data_sync_t> data_syncs;
int wanting_meta_fsync = 0; int wanting_meta_fsync = 0;
bool fsyncing_meta = false; bool fsyncing_meta = false;
int syncing_buffer = 0; int syncing_buffer = 0;
File diff suppressed because it is too large Load Diff
+14 -45
View File
@@ -43,7 +43,7 @@ struct __attribute__((__packed__)) heap_entry_t
{ {
uint16_t size; uint16_t size;
uint16_t entry_type; uint16_t entry_type;
uint32_t crc32c; uint32_t checksum;
uint64_t lsn; uint64_t lsn;
uint64_t inode; uint64_t inode;
uint64_t stripe; uint64_t stripe;
@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; } 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_write_t& big() { return *(heap_big_write_t*)this; }
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; } inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
bool is_garbage() const; bool is_garbage();
void set_garbage(); void set_garbage();
bool is_overwrite() const; bool is_overwrite();
bool is_compactable() const; bool is_compactable();
bool is_before(const heap_entry_t *other) const; bool is_before(heap_entry_t *other);
uint32_t get_size(blockstore_heap_t *heap); uint32_t get_size(blockstore_heap_t *heap);
uint8_t *get_ext_bitmap(blockstore_heap_t *heap); uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
uint8_t *get_int_bitmap(blockstore_heap_t *heap); uint8_t *get_int_bitmap(blockstore_heap_t *heap);
@@ -69,7 +69,8 @@ struct __attribute__((__packed__)) heap_entry_t
uint32_t *get_checksum(blockstore_heap_t *heap); uint32_t *get_checksum(blockstore_heap_t *heap);
uint64_t big_location(blockstore_heap_t *heap); uint64_t big_location(blockstore_heap_t *heap);
void set_big_location(blockstore_heap_t *heap, uint64_t location); void set_big_location(blockstore_heap_t *heap, uint64_t location);
uint32_t calc_crc32c(); uint32_t calc_checksum(blockstore_heap_t *heap);
uint32_t calc_checksum(blockstore_disk_t *dsk);
}; };
struct __attribute__((__packed__)) heap_small_write_t struct __attribute__((__packed__)) heap_small_write_t
@@ -80,7 +81,7 @@ struct __attribute__((__packed__)) heap_small_write_t
uint32_t offset; uint32_t offset;
uint32_t len; uint32_t len;
// Also includes 1 bitmap and 1 crc32c after the bitmap if checksums are disabled // Also includes 1 bitmap and 1 checksum after the bitmap if block checksums are disabled
}; };
struct __attribute__((__packed__)) heap_big_write_t struct __attribute__((__packed__)) heap_big_write_t
@@ -98,7 +99,7 @@ struct __attribute__((__packed__)) heap_big_intent_t
uint32_t offset; uint32_t offset;
uint32_t len; uint32_t len;
// Also includes 2 bitmaps and 1 crc32c if checksums are disabled // Also includes 2 bitmaps and 1 checksums if block checksums are disabled
}; };
struct __attribute__((__packed__)) heap_list_item_t struct __attribute__((__packed__)) heap_list_item_t
@@ -117,13 +118,10 @@ struct heap_object_mvcc_t
struct heap_block_info_t struct heap_block_info_t
{ {
struct __attribute__((__packed__)) uint32_t used_space = 0;
{
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 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; bool is_writing: 1;
bool has_garbage: 1;
std::vector<heap_list_item_t*> entries; std::vector<heap_list_item_t*> entries;
}; };
@@ -158,16 +156,6 @@ struct heap_li_equal
} }
}; };
struct heap_recheck_state_t
{
heap_entry_t *obj = NULL;
heap_entry_t *next_wr = NULL;
size_t total_reads = 0;
size_t sent_reads = 0;
size_t checked_reads = 0;
heap_entry_t *bad_wr = NULL;
};
using i64hash_t = robin_hood::hash<uint64_t>; using 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_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, using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
@@ -197,11 +185,6 @@ class blockstore_heap_t
uint64_t buffer_area_used_space = 0; uint64_t buffer_area_used_space = 0;
uint64_t data_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; uint64_t next_lsn = 0;
uint32_t last_allocated_block = UINT32_MAX; uint32_t last_allocated_block = UINT32_MAX;
heap_mvcc_map_t object_mvcc; heap_mvcc_map_t object_mvcc;
@@ -218,11 +201,9 @@ class blockstore_heap_t
bool marked_used_blocks = false; bool marked_used_blocks = false;
bool recheck_queue_filled = false; bool recheck_queue_filled = false;
std::vector<heap_list_item_t*> postponed_items; std::vector<heap_list_item_t*> loaded_list_items;
std::set<uint32_t> recheck_modified_blocks; std::set<uint32_t> recheck_modified_blocks;
std::deque<heap_entry_t*> recheck_queue; std::deque<heap_entry_t*> recheck_queue;
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
size_t recheck_pending_reads = 0;
int recheck_in_progress = 0; int recheck_in_progress = 0;
bool in_recheck = false; bool in_recheck = false;
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb; std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
@@ -231,22 +212,14 @@ class blockstore_heap_t
uint64_t get_pg_id(inode_t inode, uint64_t stripe); uint64_t get_pg_id(inode_t inode, uint64_t stripe);
bool validate_object(heap_entry_t *obj); bool validate_object(heap_entry_t *obj);
void fill_recheck_queue(); 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(); 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 recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
void defragment_block(uint32_t block_num); void defragment_block(uint32_t block_num);
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li); void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
void gc_block(heap_block_info_t & inf); void gc_block(heap_block_info_t & inf);
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free); 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 insert_list_item(heap_list_item_t *li);
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, 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); 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); int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
@@ -373,10 +346,6 @@ public:
uint32_t get_compact_queue_size(); uint32_t get_compact_queue_size();
uint32_t get_to_compact_count(); uint32_t get_to_compact_count();
uint64_t get_compacted_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); 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 *entry_from_pos(uint64_t entry_pos, bool allow_unallocated = false);
+4 -9
View File
@@ -101,7 +101,6 @@ void blockstore_impl_t::loop()
unsigned initial_ring_space = ringloop->space_left(); unsigned initial_ring_space = ringloop->space_left();
int op_idx = 0, new_idx = 0; int op_idx = 0, new_idx = 0;
bool has_unfinished_writes = false; bool has_unfinished_writes = false;
bool has_unfinished_sync = false;
for (; op_idx < submit_queue.size(); op_idx++, new_idx++) for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
{ {
auto op = submit_queue[op_idx]; auto op = submit_queue[op_idx];
@@ -139,13 +138,7 @@ void blockstore_impl_t::loop()
else if (op->opcode == BS_OP_SYNC) else if (op->opcode == BS_OP_SYNC)
{ {
// syncs only completed writes, so doesn't have to be blocked by anything // syncs only completed writes, so doesn't have to be blocked by anything
if (!has_unfinished_sync) wr_st = continue_sync(op);
{
wr_st = continue_sync(op);
has_unfinished_sync = (wr_st != 2);
}
else
wr_st = 0;
} }
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK) else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
{ {
@@ -161,7 +154,9 @@ void blockstore_impl_t::loop()
wr_st = 2; wr_st = 2;
} }
else else
{
wr_st = 0; wr_st = 0;
}
} }
if (wr_st == 2) if (wr_st == 2)
{ {
@@ -198,12 +193,12 @@ void blockstore_impl_t::loop()
heap->start_block_write(block_num); heap->start_block_write(block_num);
mb.sent = true; mb.sent = true;
} }
pending_modified_blocks.clear();
int ret = ringloop->submit(); int ret = ringloop->submit();
if (ret < 0) if (ret < 0)
{ {
throw std::runtime_error(std::string("io_uring_submit: ") + strerror(-ret)); throw std::runtime_error(std::string("io_uring_submit: ") + strerror(-ret));
} }
pending_modified_blocks.clear();
if ((initial_ring_space - ringloop->space_left()) > 0) if ((initial_ring_space - ringloop->space_left()) > 0)
{ {
live = true; live = true;
-6
View File
@@ -78,7 +78,6 @@ public:
// Suitable only for server SSDs with capacitors, requires disabled data and journal fsyncs // Suitable only for server SSDs with capacitors, requires disabled data and journal fsyncs
int immediate_commit = IMMEDIATE_NONE; int immediate_commit = IMMEDIATE_NONE;
bool inmemory_meta = false; bool inmemory_meta = false;
bool skip_corrupted_meta_entries = false;
uint32_t meta_write_recheck_parallelism = 0; uint32_t meta_write_recheck_parallelism = 0;
// Maximum and minimum flusher count // Maximum and minimum flusher count
unsigned max_flusher_count = 0, min_flusher_count = 0; unsigned max_flusher_count = 0, min_flusher_count = 0;
@@ -229,9 +228,4 @@ public:
uint64_t get_free_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.bitmap_granularity; }
inline uint64_t get_journal_size() { return dsk.journal_len; } 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(); }
}; };
+20 -37
View File
@@ -145,7 +145,7 @@ resume_1:
printf( printf(
"Configuration stored in metadata superblock" "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)" " (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", " differs from OSD configuration (%u/%u/%u, %u/%u, %ju).\n",
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity, 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, hdr->meta_area_size,
bs->dsk.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity, bs->dsk.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity,
@@ -153,14 +153,6 @@ resume_1:
); );
exit(1); exit(1);
} }
uint32_t csum = hdr->header_csum;
hdr->header_csum = 0;
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
{
printf("Metadata header is corrupt (checksum mismatch).\n");
exit(1);
}
hdr->header_csum = csum;
} }
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn); bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
if (bs->dsk.inmemory_journal) if (bs->dsk.inmemory_journal)
@@ -233,7 +225,7 @@ resume_4:
{ {
// Handle result // Handle result
uint64_t loaded = 0; 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); int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, false, loaded);
if (r != 0) if (r != 0)
exit(1); exit(1);
entries_loaded += loaded; entries_loaded += loaded;
@@ -248,7 +240,23 @@ resume_4:
} }
// metadata read finished // metadata read finished
bs->heap->finish_load(); bs->heap->finish_load();
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded); printf("Metadata entries loaded: %ju, used blocks: %ju / %ju\n", entries_loaded, bs->heap->get_data_used_space() / bs->dsk.data_block_size, bs->dsk.block_count);
if (zero_on_init && !bs->dsk.disable_meta_fsync)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
// asynchronous recheck // asynchronous recheck
resume_6: resume_6:
wait_state = 6; wait_state = 6;
@@ -285,11 +293,6 @@ resume_7:
if (bs->readonly) if (bs->readonly)
{ {
recheck_mod.clear(); 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++) for (i = 0; i < recheck_mod.size(); i++)
{ {
@@ -303,7 +306,7 @@ resume_8:
uint32_t block_num = recheck_mod[i]; 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; 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); data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size); uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf); bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size }; data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data) data->callback = [this, buf, block_offset](ring_data_t *data)
@@ -329,25 +332,5 @@ resume_9:
} }
free(metadata_buffer); free(metadata_buffer);
metadata_buffer = NULL; metadata_buffer = NULL;
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
format_size(bs->heap->get_data_used_space()).c_str(),
format_size(bs->dsk.block_count * bs->dsk.data_block_size).c_str());
return 0; return 0;
} }
-1
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_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
throttle_threshold_us = strtoull(config["throttle_threshold_us"].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"; 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"] != "") if (config["autosync_writes"] != "")
{ {
autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10); autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10);
-4
View File
@@ -462,10 +462,6 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
{ {
if (target_version >= wr->version) if (target_version >= wr->version)
{ {
if (wr->type() == BS_HEAP_DELETE)
{
return false;
}
found = true; found = true;
if (result_version) if (result_version)
{ {
-7
View File
@@ -16,7 +16,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
else if (priv->op_state == 5) goto resume_5; else if (priv->op_state == 5) goto resume_5;
assert(!priv->op_state); assert(!priv->op_state);
op->retval = 0; op->retval = 0;
PRIV(op)->lsn = 0;
priv->modified_block = priv->modified_block2 = UINT32_MAX; priv->modified_block = priv->modified_block2 = UINT32_MAX;
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++) for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
{ {
@@ -37,12 +36,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
FINISH_OP(op); FINISH_OP(op);
return 2; return 2;
} }
if (res == ENOENT)
{
op->retval = -ENOENT;
FINISH_OP(op);
return 2;
}
if (res == ENOSPC) if (res == ENOSPC)
{ {
if (!heap->get_to_compact_count()) if (!heap->get_to_compact_count())
+2 -4
View File
@@ -9,7 +9,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
if (!PRIV(op)->op_state) if (!PRIV(op)->op_state)
{ {
op->retval = 0; op->retval = 0;
PRIV(op)->lsn = 0;
} }
int res = do_sync(op, 0); int res = do_sync(op, 0);
if (res == 2) if (res == 2)
@@ -105,8 +104,7 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0; unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
return 2; return 2;
} }
assert(!PRIV(op)->lsn); PRIV(op)->modified_block = heap->get_completed_lsn();
PRIV(op)->lsn = heap->get_completed_lsn();
if (!submit_fsyncs(PRIV(op)->pending_ops)) if (!submit_fsyncs(PRIV(op)->pending_ops))
{ {
PRIV(op)->wait_detail = 1; PRIV(op)->wait_detail = 1;
@@ -120,6 +118,6 @@ resume_1:
return 1; return 1;
} }
resume_2: resume_2:
heap->mark_lsn_fsynced(PRIV(op)->lsn); heap->mark_lsn_fsynced(PRIV(op)->modified_block);
return 2; return 2;
} }
+7 -14
View File
@@ -22,24 +22,21 @@ void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
ring_data_t *data = ((ring_data_t*)sqe->user_data); ring_data_t *data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size); 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->iov = (struct iovec){ buf, (size_t)dsk.meta_block_size };
data->callback = [this, modified_block](ring_data_t *data) data->callback = [this, modified_block, buf](ring_data_t *data)
{ {
free(buf);
live = true; live = true;
if (data->res != data->iov.iov_len) if (data->res != data->iov.iov_len)
{ {
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die // 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); disk_error_abort("data write", data->res, data->iov.iov_len);
} }
auto it = modified_blocks.find(modified_block); modified_blocks.erase(modified_block);
assert(it != modified_blocks.end());
free(it->second.buf);
modified_blocks.erase(it);
heap->complete_block_write(modified_block); heap->complete_block_write(modified_block);
ringloop->wakeup(); ringloop->wakeup();
}; };
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
io_uring_prep_writev( 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++; unsynced_meta_write_count++;
pending_modified_blocks.push_back(modified_block); pending_modified_blocks.push_back(modified_block);
@@ -178,7 +175,6 @@ enospc:
ring_data_t *data = ((ring_data_t*)sqe->user_data); ring_data_t *data = ((ring_data_t*)sqe->user_data);
data->iov = (struct iovec){ op->buf, op->len }; data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); }; 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); io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + loc + op->offset);
PRIV(op)->pending_ops++; PRIV(op)->pending_ops++;
write_iodepth++; write_iodepth++;
@@ -253,12 +249,13 @@ enospc:
goto enospc; goto enospc;
assert(res == 0); assert(res == 0);
PRIV(op)->lsn = obj->lsn; PRIV(op)->lsn = obj->lsn;
if (op->len)
heap->use_buffer_area(op->oid.inode, loc, op->len);
prepare_meta_block_write(PRIV(op)->modified_block); prepare_meta_block_write(PRIV(op)->modified_block);
PRIV(op)->pending_ops++; PRIV(op)->pending_ops++;
if (op->len > 0) if (op->len > 0)
{ {
// Prepare buffered data write // Prepare buffered data write
heap->use_buffer_area(op->oid.inode, loc, op->len);
if (dsk.inmemory_journal) if (dsk.inmemory_journal)
{ {
memcpy((uint8_t*)buffer_area + loc, op->buf, op->len); memcpy((uint8_t*)buffer_area + loc, op->buf, op->len);
@@ -266,7 +263,6 @@ enospc:
BS_SUBMIT_GET_SQE(sqe2, data2); BS_SUBMIT_GET_SQE(sqe2, data2);
data2->iov = (struct iovec){ op->buf, op->len }; data2->iov = (struct iovec){ op->buf, op->len };
data2->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); }; 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); io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, dsk.journal_offset + loc);
PRIV(op)->pending_ops++; PRIV(op)->pending_ops++;
} }
@@ -352,7 +348,6 @@ resume_12:
} }
resume_4: resume_4:
{ {
BS_SUBMIT_CHECK_SQES(1);
auto obj = heap->read_entry(op->oid); auto obj = heap->read_entry(op->oid);
int res = 0; int res = 0;
if (PRIV(op)->write_type == _REDIRECT_INTENT) if (PRIV(op)->write_type == _REDIRECT_INTENT)
@@ -409,12 +404,11 @@ resume_6:
if (ref_us > exec_us + throttle_threshold_us) if (ref_us > exec_us + throttle_threshold_us)
{ {
// Pause reply // Pause reply
PRIV(op)->pending_ops++;
PRIV(op)->op_state = 7; PRIV(op)->op_state = 7;
// Remember that the timer can in theory be called right here // 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) 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(); ringloop->wakeup();
}); });
return 1; return 1;
@@ -456,7 +450,6 @@ resume_10:
BS_SUBMIT_GET_SQE(sqe, data); BS_SUBMIT_GET_SQE(sqe, data);
data->iov = (struct iovec){ op->buf, op->len }; data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); }; 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); io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + PRIV(op)->location + op->offset);
if (dsk.use_atomic_flag) if (dsk.use_atomic_flag)
sqe->rw_flags = RWF_ATOMIC; sqe->rw_flags = RWF_ATOMIC;
+1 -1
View File
@@ -141,7 +141,7 @@ struct __attribute__((__packed__)) journal_entry
inline uint32_t je_crc32(journal_entry *je) inline uint32_t je_crc32(journal_entry *je)
{ {
// 0x48674bc7 = crc32(4 zero bytes) // 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" // "VITAstor"
-2
View File
@@ -520,7 +520,6 @@ resume_2:
await_sqe(15); await_sqe(15);
data->iov = (struct iovec){ it->buf, (size_t)it->len }; data->iov = (struct iovec){ it->buf, (size_t)it->len };
data->callback = simple_callback_w; 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( io_uring_prep_writev(
sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + it->offset 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); await_sqe(0);
data->iov = (struct iovec){ meta_block.buf, (size_t)bs->dsk.meta_block_size }; data->iov = (struct iovec){ meta_block.buf, (size_t)bs->dsk.meta_block_size };
data->callback = simple_callback_w; 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( io_uring_prep_writev(
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_block.sector sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_block.sector
); );
-25
View File
@@ -855,29 +855,4 @@ std::string blockstore_impl_t::get_op_diag(blockstore_op_t *op)
return std::string(buf); 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 } // namespace v1
-4
View File
@@ -332,10 +332,6 @@ public:
inline uint64_t get_free_block_count() { return dsk.block_count - used_blocks; } inline uint64_t get_free_block_count() { return dsk.block_count - used_blocks; }
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; } inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
inline uint64_t get_journal_size() { return dsk.journal_len; } 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 } // namespace v1
+1 -1
View File
@@ -189,7 +189,7 @@ resume_1:
printf( printf(
"Configuration stored in metadata superblock" "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_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->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity,
hdr->data_csum_type, hdr->csum_block_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.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity,
-1
View File
@@ -193,7 +193,6 @@ void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_
(size_t)journal.block_size (size_t)journal.block_size
}; };
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); }; data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
assert(journal.sector_info[cur_sector].offset+journal.block_size <= dsk.journal_len);
io_uring_prep_writev( io_uring_prep_writev(
sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
); );
+2 -1
View File
@@ -620,7 +620,8 @@ bool blockstore_impl_t::fulfill_clean_read(blockstore_op_t *read_op, uint64_t &
else if (from_journal) else if (from_journal)
{ {
// Don't scan bitmap - journal writes don't have holes (internal bitmap)! // Don't scan bitmap - journal writes don't have holes (internal bitmap)!
uint8_t *csum = !dsk.csum_block_size ? 0 : (clean_entry_bitmap + dsk.clean_entry_bitmap_size); uint8_t *csum = !dsk.csum_block_size ? 0 : (clean_entry_bitmap + dsk.clean_entry_bitmap_size +
item_start/dsk.csum_block_size*(dsk.data_csum_type & 0xFF));
if (!fulfill_read(read_op, fulfilled, item_start, item_end, if (!fulfill_read(read_op, fulfilled, item_start, item_end,
(BS_ST_BIG_WRITE | BS_ST_STABLE), 0, clean_loc + item_start, 0, csum, dyn_data)) (BS_ST_BIG_WRITE | BS_ST_STABLE), 0, clean_loc + item_start, 0, csum, dyn_data))
{ {
+6 -5
View File
@@ -368,9 +368,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
} }
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback data->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); }; data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
const uint64_t write_offset = (loc * dsk.data_block_size) + op->offset - stripe_offset; io_uring_prep_writev(
assert(write_offset+op->len+stripe_offset+stripe_end <= dsk.block_count*dsk.data_block_size); sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + (loc * dsk.data_block_size) + op->offset - stripe_offset
io_uring_prep_writev(sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + write_offset); );
PRIV(op)->pending_ops = 1; PRIV(op)->pending_ops = 1;
if (!(dirty_it->second.state & BS_ST_INSTANT)) if (!(dirty_it->second.state & BS_ST_INSTANT))
{ {
@@ -495,8 +495,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
.op = op, .op = op,
}); });
data2->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); }; data2->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
assert(journal.next_free+op->len <= dsk.journal_len); io_uring_prep_writev(
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free); sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free
);
PRIV(op)->pending_ops++; PRIV(op)->pending_ops++;
} }
else else
+10 -6
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8...3.30) cmake_minimum_required(VERSION 2.8.12)
project(vitastor) project(vitastor)
@@ -12,11 +12,11 @@ if (RDMACM_LIBRARIES)
set(MSGR_RDMACM "msgr_rdmacm.cpp") set(MSGR_RDMACM "msgr_rdmacm.cpp")
endif (RDMACM_LIBRARIES) endif (RDMACM_LIBRARIES)
add_library(vitastor_common STATIC add_library(vitastor_common STATIC
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp ../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp msgr_encrypt.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM} http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
) )
target_link_libraries(vitastor_common pthread) target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES})
target_compile_options(vitastor_common PUBLIC -fPIC) target_compile_options(vitastor_common PUBLIC -fPIC)
# libvitastor_client.so # libvitastor_client.so
@@ -33,6 +33,7 @@ target_link_libraries(vitastor_client
${LIBURING_LIBRARIES} ${LIBURING_LIBRARIES}
${IBVERBS_LIBRARIES} ${IBVERBS_LIBRARIES}
${RDMACM_LIBRARIES} ${RDMACM_LIBRARIES}
${OPENSSL_LIBRARIES}
) )
set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0) set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
configure_file(vitastor.pc.in vitastor.pc @ONLY) configure_file(vitastor.pc.in vitastor.pc @ONLY)
@@ -52,6 +53,9 @@ if (${WITH_FIO})
../util/rw_blocking.cpp ../util/rw_blocking.cpp
../util/addr_util.cpp ../util/addr_util.cpp
) )
target_link_libraries(fio_vitastor_sec
tcmalloc_minimal
)
endif (${WITH_FIO}) endif (${WITH_FIO})
# vitastor-nbd # vitastor-nbd
@@ -95,10 +99,10 @@ endif (${WITH_QEMU})
add_executable(test_cluster_client add_executable(test_cluster_client
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
../test/test_cluster_client.cpp ../test/test_cluster_client.cpp
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp msgr_encrypt.cpp
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp
) )
target_link_libraries(test_cluster_client ${LIBURING_LIBRARIES}) target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES})
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__) target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock) target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
add_dependencies(build_tests test_cluster_client) add_dependencies(build_tests test_cluster_client)
+47 -34
View File
@@ -27,7 +27,7 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
msgr.ringloop = ringloop; msgr.ringloop = ringloop;
msgr.repeer_pgs = [this](osd_num_t peer_osd) msgr.repeer_pgs = [this](osd_num_t peer_osd)
{ {
if (msgr.osd_peers.find(peer_osd) != msgr.osd_peers.end()) if (msgr.osd_peer_fds.find(peer_osd) != msgr.osd_peer_fds.end())
{ {
// peer_osd just connected // peer_osd just connected
continue_ops(); continue_ops();
@@ -47,8 +47,8 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
msgr.exec_op = [this](osd_op_t *op) msgr.exec_op = [this](osd_op_t *op)
{ {
// Garbage in // Garbage in
fprintf(stderr, "Can't handle incoming operation from client %lu\n", op->client_id); fprintf(stderr, "Incoming garbage from peer %d\n", op->peer_fd);
msgr.stop_client(op->client_id); msgr.stop_client(op->peer_fd);
delete op; delete op;
}; };
msgr.parse_config(config); msgr.parse_config(config);
@@ -156,7 +156,7 @@ void cluster_client_t::continue_raw_ops(osd_num_t peer_osd)
{ {
auto op = it->second; auto op = it->second;
op->op_type = OSD_OP_OUT; op->op_type = OSD_OP_OUT;
op->client_id = msgr.osd_peers.at(peer_osd)->client_id; op->peer_fd = msgr.osd_peer_fds.at(peer_osd);
msgr.outbox_push(op); msgr.outbox_push(op);
raw_ops.erase(it++); raw_ops.erase(it++);
} }
@@ -590,7 +590,7 @@ void cluster_client_t::on_change_pool_config_hook()
{ {
if (log_level > 2 && pg_counts[pool_item.first]) if (log_level > 2 && pg_counts[pool_item.first])
{ {
fprintf(stderr, "Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(), printf("Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
pg_counts[pool_item.first], pool_item.second.real_pg_count); pg_counts[pool_item.first], pool_item.second.real_pg_count);
} }
// At this point, all pool operations should have been suspended // At this point, all pool operations should have been suspended
@@ -871,13 +871,13 @@ void cluster_client_t::execute_cas(cluster_op_t *op)
if (op->retval != expected && op->retval >= 0) if (op->retval != expected && op->retval >= 0)
op->retval = -EIO; op->retval = -EIO;
op->retval = op->retval == -EPIPE ? -EINTR : op->retval; op->retval = op->retval == -EPIPE ? -EINTR : op->retval;
auto peer_it = msgr.osd_peers.find(op->parts[0].osd_num); auto peer_it = msgr.osd_peer_fds.find(op->parts[0].osd_num);
if (op->retval != 0 || (op->flags & OP_IMMEDIATE_COMMIT)) if (op->retval != 0 || (op->flags & OP_IMMEDIATE_COMMIT))
{ {
auto cb = std::move(op->callback); auto cb = std::move(op->callback);
cb(op); cb(op);
} }
else if (peer_it == msgr.osd_peers.end()) else if (peer_it == msgr.osd_peer_fds.end())
{ {
// Care must be taken to make sure that the client doesn't reconnect to the OSD // Care must be taken to make sure that the client doesn't reconnect to the OSD
// before executing the previously completed operation callback (!) // before executing the previously completed operation callback (!)
@@ -888,10 +888,10 @@ void cluster_client_t::execute_cas(cluster_op_t *op)
else else
{ {
// CAS writes have a built-in sync // CAS writes have a built-in sync
osd_client_t *cl = peer_it->second; auto peer_fd = peer_it->second;
*part = (osd_op_t){ *part = (osd_op_t){
.op_type = OSD_OP_OUT, .op_type = OSD_OP_OUT,
.client_id = cl->client_id, .peer_fd = peer_fd,
.req = { .req = {
.hdr = { .hdr = {
.magic = SECONDARY_OSD_OP_MAGIC, .magic = SECONDARY_OSD_OP_MAGIC,
@@ -958,9 +958,22 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
{ {
op->flags |= OP_IMMEDIATE_COMMIT; op->flags |= OP_IMMEDIATE_COMMIT;
} }
auto ino_it = st_cli.inode_config.find(op->inode);
if (ino_it != st_cli.inode_config.end() && ino_it->second.enc)
{
// FIXME: Rework client API by adding open/close and cache inode information in the "FD"
op->enc = ino_it->second.enc;
if (!op->enc->bitmap_granularity)
{
op->enc->bitmap_granularity = pool_it->second.bitmap_granularity;
}
}
else
{
op->enc.reset();
}
if ((op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE) && !(op->flags & OSD_OP_IGNORE_READONLY)) if ((op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE) && !(op->flags & OSD_OP_IGNORE_READONLY))
{ {
auto ino_it = st_cli.inode_config.find(op->inode);
if (ino_it != st_cli.inode_config.end() && ino_it->second.readonly) if (ino_it != st_cli.inode_config.end() && ino_it->second.readonly)
{ {
op->retval = -EROFS; op->retval = -EROFS;
@@ -972,7 +985,6 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
op->deoptimise_snapshot = false; op->deoptimise_snapshot = false;
if (enable_writeback && (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)) if (enable_writeback && (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP))
{ {
auto ino_it = st_cli.inode_config.find(op->inode);
if (ino_it != st_cli.inode_config.end()) if (ino_it != st_cli.inode_config.end())
{ {
int chain_size = 0; int chain_size = 0;
@@ -1004,11 +1016,11 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
void cluster_client_t::execute_raw(osd_num_t osd_num, osd_op_t *op) void cluster_client_t::execute_raw(osd_num_t osd_num, osd_op_t *op)
{ {
auto peer_it = msgr.osd_peers.find(osd_num); auto fd_it = msgr.osd_peer_fds.find(osd_num);
if (peer_it != msgr.osd_peers.end()) if (fd_it != msgr.osd_peer_fds.end())
{ {
op->op_type = OSD_OP_OUT; op->op_type = OSD_OP_OUT;
op->client_id = peer_it->second->client_id; op->peer_fd = fd_it->second;
msgr.outbox_push(op); msgr.outbox_push(op);
} }
else else
@@ -1119,13 +1131,6 @@ resume_2:
// Finished successfully // Finished successfully
// Even if the PG count has changed in meanwhile we treat it as success // Even if the PG count has changed in meanwhile we treat it as success
// because if some operations were invalid for the new PG count we'd get errors // because if some operations were invalid for the new PG count we'd get errors
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
// Copy part bitmaps only after finishing all part reads
for (auto & part: op->parts)
if ((part.flags & (PART_SENT|PART_DONE|PART_VALID)) == (PART_SENT|PART_DONE|PART_VALID))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP) if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{ {
// Check parent inode // Check parent inode
@@ -1171,7 +1176,7 @@ resume_2:
erase_op(op); erase_op(op);
return 1; return 1;
} }
else if (op->retval != 0 && op->opcode != OSD_OP_SYNC && !(op->flags & OP_FLUSH_BUFFER) && else if (op->retval != 0 && !(op->flags & OP_FLUSH_BUFFER) &&
op->retval != -EPIPE && (op->retval != -EIO || !client_eio_retry_interval) && (op->retval != -ENOSPC || !client_retry_enospc)) op->retval != -EPIPE && (op->retval != -EIO || !client_eio_retry_interval) && (op->retval != -ENOSPC || !client_retry_enospc))
{ {
// Fatal error (neither -EPIPE, -EIO nor -ENOSPC) // Fatal error (neither -EPIPE, -EIO nor -ENOSPC)
@@ -1408,10 +1413,10 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
primary_osd = nearest_osd; primary_osd = nearest_osd;
} }
part->osd_num = primary_osd; part->osd_num = primary_osd;
auto peer_it = msgr.osd_peers.find(primary_osd); auto peer_it = msgr.osd_peer_fds.find(primary_osd);
if (peer_it != msgr.osd_peers.end()) if (peer_it != msgr.osd_peer_fds.end())
{ {
osd_client_t *cl = peer_it->second; int peer_fd = peer_it->second;
part->flags |= PART_SENT|PART_VALID; part->flags |= PART_SENT|PART_VALID;
op->inflight_count++; op->inflight_count++;
uint64_t pg_bitmap_size = (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8) * ( uint64_t pg_bitmap_size = (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8) * (
@@ -1426,7 +1431,7 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
} }
part->op = (osd_op_t){ part->op = (osd_op_t){
.op_type = OSD_OP_OUT, .op_type = OSD_OP_OUT,
.client_id = cl->client_id, .peer_fd = peer_fd,
.req = { .rw = { .req = { .rw = {
.header = { .header = {
.magic = SECONDARY_OSD_OP_MAGIC, .magic = SECONDARY_OSD_OP_MAGIC,
@@ -1442,6 +1447,7 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
? (uint8_t*)op->part_bitmaps + pg_bitmap_size*i : NULL), ? (uint8_t*)op->part_bitmaps + pg_bitmap_size*i : NULL),
.bitmap_len = (unsigned)(op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP .bitmap_len = (unsigned)(op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP
? pg_bitmap_size : 0), ? pg_bitmap_size : 0),
.enc = op->enc,
.callback = cb ? cb : [this, part](osd_op_t *op_part) .callback = cb ? cb : [this, part](osd_op_t *op_part)
{ {
handle_op_part(part); handle_op_part(part);
@@ -1475,8 +1481,8 @@ int cluster_client_t::continue_sync(cluster_op_t *op)
for (auto do_it = dirty_osds.begin(); do_it != dirty_osds.end(); ) for (auto do_it = dirty_osds.begin(); do_it != dirty_osds.end(); )
{ {
osd_num_t sync_osd = *do_it; osd_num_t sync_osd = *do_it;
auto peer_it = msgr.osd_peers.find(sync_osd); auto peer_it = msgr.osd_peer_fds.find(sync_osd);
if (peer_it == msgr.osd_peers.end()) if (peer_it == msgr.osd_peer_fds.end())
dirty_osds.erase(do_it++); dirty_osds.erase(do_it++);
else else
do_it++; do_it++;
@@ -1529,12 +1535,12 @@ resume_1:
void cluster_client_t::send_sync(cluster_op_t *op, cluster_op_part_t *part) void cluster_client_t::send_sync(cluster_op_t *op, cluster_op_part_t *part)
{ {
osd_client_t *cl = msgr.osd_peers.at(part->osd_num); auto peer_fd = msgr.osd_peer_fds.at(part->osd_num);
part->flags |= PART_SENT; part->flags |= PART_SENT;
op->inflight_count++; op->inflight_count++;
part->op = (osd_op_t){ part->op = (osd_op_t){
.op_type = OSD_OP_OUT, .op_type = OSD_OP_OUT,
.client_id = cl->client_id, .peer_fd = peer_fd,
.req = { .req = {
.hdr = { .hdr = {
.magic = SECONDARY_OSD_OP_MAGIC, .magic = SECONDARY_OSD_OP_MAGIC,
@@ -1574,10 +1580,10 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
// Error priority: EIO > ENOSPC > ETIMEDOUT > EPIPE // Error priority: EIO > ENOSPC > ETIMEDOUT > EPIPE
op->retval = part->op.reply.hdr.retval; op->retval = part->op.reply.hdr.retval;
} }
uint64_t stop_client_id = 0; int stop_fd = -1;
if (op->retval != -EINTR && op->retval != -EIO && op->retval != -ENOSPC) if (op->retval != -EINTR && op->retval != -EIO && op->retval != -ENOSPC)
{ {
stop_client_id = part->op.client_id; stop_fd = part->op.peer_fd;
if (op->retval != -EPIPE || log_level > 0) if (op->retval != -EPIPE || log_level > 0)
{ {
fprintf( fprintf(
@@ -1604,9 +1610,9 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
op->retry_after = op->retval != -EPIPE ? client_eio_retry_interval : client_retry_interval; op->retry_after = op->retval != -EPIPE ? client_eio_retry_interval : client_retry_interval;
} }
reset_retry_timer(op->retry_after); reset_retry_timer(op->retry_after);
if (stop_client_id) if (stop_fd >= 0)
{ {
msgr.stop_client(stop_client_id); msgr.stop_client(stop_fd);
} }
op->inflight_count--; op->inflight_count--;
if (op->inflight_count == 0 && !op->retry_after) if (op->inflight_count == 0 && !op->retry_after)
@@ -1637,6 +1643,13 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
} }
if (op->inflight_count == 0 && !op->retry_after) if (op->inflight_count == 0 && !op->retry_after)
{ {
// Copy part bitmaps only after finishing all part reads
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
for (auto & part: op->parts)
if (part.flags == (PART_SENT|PART_VALID|PART_DONE))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_SYNC) if (op->opcode == OSD_OP_SYNC)
continue_sync(op); continue_sync(op);
else else
+10 -1
View File
@@ -71,6 +71,7 @@ protected:
cluster_op_t *prev = NULL, *next = NULL; cluster_op_t *prev = NULL, *next = NULL;
int prev_wait = 0; int prev_wait = 0;
uint64_t flush_id = 0; uint64_t flush_id = 0;
std::shared_ptr<inode_enc_t> enc;
friend class cluster_client_t; friend class cluster_client_t;
friend class writeback_cache_t; friend class writeback_cache_t;
}; };
@@ -83,6 +84,9 @@ class writeback_cache_t;
// FIXME: Split into public and private interfaces // FIXME: Split into public and private interfaces
class __attribute__((visibility("default"))) cluster_client_t class __attribute__((visibility("default"))) cluster_client_t
{ {
#ifdef __MOCK__
public:
#endif
timerfd_manager_t *tfd = NULL; timerfd_manager_t *tfd = NULL;
ring_loop_t *ringloop = NULL; ring_loop_t *ringloop = NULL;
@@ -152,9 +156,15 @@ public:
void list_inode(inode_t inode, uint64_t min_offset, uint64_t max_offset, int max_parallel_pgs, std::function<void( void list_inode(inode_t inode, uint64_t min_offset, uint64_t max_offset, int max_parallel_pgs, std::function<void(
int status, int pgs_left, pg_num_t pg_num, std::set<object_id>&& objects)> pg_callback); int status, int pgs_left, pg_num_t pg_num, std::set<object_id>&& objects)> pg_callback);
//inline uint32_t get_bs_bitmap_granularity() { return st_cli.global_bitmap_granularity; }
//inline uint64_t get_bs_block_size() { return st_cli.global_block_size; }
#ifndef __MOCK__
protected: protected:
#endif
void continue_ops(int time_passed = 0); void continue_ops(int time_passed = 0);
protected:
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd); bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num); bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
@@ -195,5 +205,4 @@ protected:
osd_num_t select_nearest_osd(const std::vector<osd_num_t> & osds); osd_num_t select_nearest_osd(const std::vector<osd_num_t> & osds);
friend class writeback_cache_t; friend class writeback_cache_t;
friend class cluster_client_test_t;
}; };
+2 -2
View File
@@ -295,7 +295,7 @@ int cluster_client_t::start_pg_listing(inode_list_pg_t *pg)
bool conn = true; bool conn = true;
for (osd_num_t peer_osd: all_peers) for (osd_num_t peer_osd: all_peers)
{ {
if (msgr.osd_peers.find(peer_osd) == msgr.osd_peers.end()) if (msgr.osd_peer_fds.find(peer_osd) == msgr.osd_peer_fds.end())
{ {
// Initiate connection // Initiate connection
if (st_cli.peer_states[peer_osd].is_null()) if (st_cli.peer_states[peer_osd].is_null())
@@ -340,7 +340,7 @@ void cluster_client_t::send_list(inode_list_osd_t *cur_list)
osd_op_t *op = new osd_op_t(); osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT; op->op_type = OSD_OP_OUT;
// Already checked that it exists above, but anyway // Already checked that it exists above, but anyway
op->client_id = msgr.osd_peers.at(cur_list->osd_num)->client_id; op->peer_fd = msgr.osd_peer_fds.at(cur_list->osd_num);
op->req = (osd_any_op_t){ op->req = (osd_any_op_t){
.sec_list = { .sec_list = {
.header = { .header = {
+3 -9
View File
@@ -88,11 +88,6 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
// ...or just save it for writeback if write buffering is enabled // ...or just save it for writeback if write buffering is enabled
if (op->len == 0) if (op->len == 0)
{ {
// FIXME: OSD_OP_DELETEs are currently only sent by vitastor-cli rm/rm-data and
// actually have len=0, because delete is actually a delete of the full object
// containing the requested offset, not a "punch hole" operation. But here, writeback
// cache assumes it IS a "punch hole" operation. I should select one of these
// approaches and fix everything accordingly when I decide to implement TRIM.
return; return;
} }
auto dirty_it = find_dirty(op->inode, op->offset); auto dirty_it = find_dirty(op->inode, op->offset);
@@ -249,13 +244,12 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
writeback_queue_size--; writeback_queue_size--;
} }
} }
if (!is_del && op->len > 0) if (!is_del)
{ {
uint64_t pos = 0, len = op->len, iov_idx = 0; uint64_t pos = 0, len = op->len, iov_idx = 0;
while (iov_idx < op->iov.count) while (len > 0 && iov_idx < op->iov.count)
{ {
auto & iov = op->iov.buf[iov_idx]; auto & iov = op->iov.buf[iov_idx];
assert(pos + iov.iov_len <= len);
memcpy(buf + pos, iov.iov_base, iov.iov_len); memcpy(buf + pos, iov.iov_base, iov.iov_len);
pos += iov.iov_len; pos += iov.iov_len;
iov_idx++; iov_idx++;
@@ -449,7 +443,7 @@ void writeback_cache_t::start_writebacks(cluster_client_t *cli, int count)
started++; started++;
assert(writeback_queue_size > 0); assert(writeback_queue_size > 0);
writeback_queue_size--; writeback_queue_size--;
writeback_bytes -= (is_del ? 0 : off - from_it->first.stripe); writeback_bytes -= off - from_it->first.stripe;
assert(writeback_queue_size > 0 || !writeback_bytes); assert(writeback_queue_size > 0 || !writeback_bytes);
flush_buffers(cli, from_it, to_it); flush_buffers(cli, from_it, to_it);
} }
+62 -35
View File
@@ -22,14 +22,19 @@ etcd_state_client_t::~etcd_state_client_t()
stop_ws_keepalive(); stop_ws_keepalive();
if (etcd_watch_ws) if (etcd_watch_ws)
{ {
http_close(etcd_watch_ws); http_destroy(etcd_watch_ws);
etcd_watch_ws = NULL; etcd_watch_ws = NULL;
} }
if (keepalive_client) if (keepalive_client)
{ {
http_close(keepalive_client); http_destroy(keepalive_client);
keepalive_client = NULL; keepalive_client = NULL;
} }
if (http_ctx)
{
http_context_destroy(http_ctx);
http_ctx = NULL;
}
#endif #endif
if (load_pgs_timer_id >= 0) if (load_pgs_timer_id >= 0)
{ {
@@ -72,10 +77,27 @@ std::vector<std::string> etcd_state_client_t::get_addresses()
return addrs; return addrs;
} }
http_context_t *etcd_state_client_t::get_http_ctx()
{
if (!http_ctx)
{
std::string error;
http_ctx = http_context_init(etcd_client_cert, etcd_client_key, etcd_ca, true, error);
if (!http_ctx)
{
fprintf(stderr, "Failed to initialize HTTP context: %s\n", error.c_str());
exit(1);
}
}
return http_ctx;
}
void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload,
int timeout, std::function<void(std::string, json11::Json)> callback) int timeout, std::function<void(std::string, json11::Json)> callback)
{ {
std::string etcd_api_path; std::string etcd_api_path;
bool ssl = etcd_address.substr(0, 8) == "https://";
etcd_address = etcd_address.substr(ssl ? 8 : 7);
int pos = etcd_address.find('/'); int pos = etcd_address.find('/');
if (pos >= 0) if (pos >= 0)
{ {
@@ -89,16 +111,16 @@ void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::strin
"Content-Length: "+std::to_string(req.size())+"\r\n" "Content-Length: "+std::to_string(req.size())+"\r\n"
"Connection: close\r\n" "Connection: close\r\n"
"\r\n"+req; "\r\n"+req;
auto http_cli = http_init(tfd); auto http_cli = http_init(tfd, get_http_ctx());
auto cb = [http_cli, callback](const http_response_t *response) auto cb = [http_cli, callback](http_message_t *response)
{ {
std::string err; std::string err;
json11::Json data; json11::Json data;
response->parse_json_response(err, data); response->parse_json_response(err, data);
callback(err, data); callback(err, data);
http_close(http_cli); http_destroy(http_cli);
}; };
http_request(http_cli, etcd_address, req, { .timeout = timeout }, cb); http_request(http_cli, etcd_address, req, { .timeout = timeout, .ssl = ssl }, cb);
} }
void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int timeout, void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int timeout,
@@ -112,6 +134,8 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t
pick_next_etcd(); pick_next_etcd();
std::string etcd_address = selected_etcd_address; std::string etcd_address = selected_etcd_address;
std::string etcd_api_path; std::string etcd_api_path;
bool ssl = etcd_address.substr(0, 8) == "https://";
etcd_address = etcd_address.substr(ssl ? 8 : 7);
int pos = etcd_address.find('/'); int pos = etcd_address.find('/');
if (pos >= 0) if (pos >= 0)
{ {
@@ -128,7 +152,7 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t
"\r\n"+req; "\r\n"+req;
retries--; retries--;
auto cb = [this, api, payload, timeout, retries, interval, callback, auto cb = [this, api, payload, timeout, retries, interval, callback,
cur_addr = selected_etcd_address](const http_response_t *response) cur_addr = selected_etcd_address](http_message_t *response)
{ {
std::string err; std::string err;
json11::Json data; json11::Json data;
@@ -164,22 +188,21 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t
callback(err, data); callback(err, data);
}; };
if (!keepalive_client) if (!keepalive_client)
{ keepalive_client = http_init(tfd, get_http_ctx());
keepalive_client = http_init(tfd); http_request(keepalive_client, etcd_address, req, { .timeout = timeout, .keepalive = true, .ssl = ssl }, cb);
}
http_request(keepalive_client, etcd_address, req, { .timeout = timeout, .keepalive = true }, cb);
} }
void etcd_state_client_t::add_etcd_url(std::string addr) void etcd_state_client_t::add_etcd_url(std::string addr)
{ {
if (addr.length() > 0) if (addr.length() > 0)
{ {
bool ssl = false;
if (strtolower(addr.substr(0, 7)) == "http://") if (strtolower(addr.substr(0, 7)) == "http://")
addr = addr.substr(7); addr = addr.substr(7);
else if (strtolower(addr.substr(0, 8)) == "https://") else if (strtolower(addr.substr(0, 8)) == "https://")
{ {
fprintf(stderr, "HTTPS is unsupported for etcd. Either use plain HTTP or setup a local proxy for etcd interaction\n"); addr = addr.substr(8);
exit(1); ssl = true;
} }
if (!local_ips.size()) if (!local_ips.size())
local_ips = getifaddr_list(std::vector<addr_mask_t>(), true); local_ips = getifaddr_list(std::vector<addr_mask_t>(), true);
@@ -194,6 +217,7 @@ void etcd_state_client_t::add_etcd_url(std::string addr)
check_addr = addr; check_addr = addr;
if (pos == std::string::npos) if (pos == std::string::npos)
addr += "/v3"; addr += "/v3";
addr = (ssl ? "https://" : "http://") + addr;
bool local = false; bool local = false;
int i; int i;
for (i = 0; i < local_ips.size(); i++) for (i = 0; i < local_ips.size(); i++)
@@ -239,6 +263,9 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
add_etcd_url(ea.string_value()); add_etcd_url(ea.string_value());
} }
} }
this->etcd_client_cert = config["etcd_client_cert"].string_value();
this->etcd_client_key = config["etcd_client_key"].string_value();
this->etcd_ca = config["etcd_ca"].string_value();
this->etcd_prefix = config["etcd_prefix"].string_value(); this->etcd_prefix = config["etcd_prefix"].string_value();
if (this->etcd_prefix == "") if (this->etcd_prefix == "")
{ {
@@ -331,6 +358,8 @@ void etcd_state_client_t::start_etcd_watcher()
pick_next_etcd(); pick_next_etcd();
std::string etcd_address = selected_etcd_address; std::string etcd_address = selected_etcd_address;
std::string etcd_api_path; std::string etcd_api_path;
bool ssl = etcd_address.substr(0, 8) == "https://";
etcd_address = etcd_address.substr(ssl ? 8 : 7);
int pos = etcd_address.find('/'); int pos = etcd_address.find('/');
if (pos >= 0) if (pos >= 0)
{ {
@@ -339,18 +368,17 @@ void etcd_state_client_t::start_etcd_watcher()
} }
etcd_watches_initialised = 0; etcd_watches_initialised = 0;
ws_alive = 1; ws_alive = 1;
if (etcd_watch_ws)
{
http_close(etcd_watch_ws);
etcd_watch_ws = NULL;
}
if (this->log_level > 1) if (this->log_level > 1)
{ {
fprintf(stderr, "Trying to connect to etcd websocket at %s, watch from revision %ju/%ju/%ju\n", etcd_address.c_str(), fprintf(stderr, "Trying to connect to etcd websocket at %s, watch from revision %ju/%ju/%ju\n", etcd_address.c_str(),
etcd_watch_revision_config, etcd_watch_revision_osd, etcd_watch_revision_pg); etcd_watch_revision_config, etcd_watch_revision_osd, etcd_watch_revision_pg);
} }
etcd_watch_ws = open_websocket(tfd, etcd_address, etcd_api_path+"/watch", etcd_slow_timeout, if (!etcd_watch_ws)
[this, cur_addr = selected_etcd_address](const http_response_t *msg) etcd_watch_ws = http_init(tfd, get_http_ctx());
else
http_close(etcd_watch_ws);
open_websocket(etcd_watch_ws, etcd_address, etcd_api_path+"/watch", { .timeout = etcd_slow_timeout, .ssl = ssl },
[this, cur_addr = selected_etcd_address](http_message_t *msg)
{ {
if (msg->body.length()) if (msg->body.length())
{ {
@@ -393,7 +421,6 @@ void etcd_state_client_t::start_etcd_watcher()
fprintf(stderr, "Revisions before %ju were compacted by etcd, reloading state\n", fprintf(stderr, "Revisions before %ju were compacted by etcd, reloading state\n",
data["result"]["compact_revision"].uint64_value()); data["result"]["compact_revision"].uint64_value());
http_close(etcd_watch_ws); http_close(etcd_watch_ws);
etcd_watch_ws = NULL;
etcd_watch_revision_config = etcd_watch_revision_osd = etcd_watch_revision_pg = 0; etcd_watch_revision_config = etcd_watch_revision_osd = etcd_watch_revision_pg = 0;
on_reload_hook(); on_reload_hook();
} }
@@ -414,10 +441,7 @@ void etcd_state_client_t::start_etcd_watcher()
} }
// Save revision only if it's present in the message - because sometimes etcd sends something without a header, like: // Save revision only if it's present in the message - because sometimes etcd sends something without a header, like:
// {"error": {"grpc_code": 14, "http_code": 503, "http_status": "Service Unavailable", "message": "error reading from server: EOF"}} // {"error": {"grpc_code": 14, "http_code": 503, "http_status": "Service Unavailable", "message": "error reading from server: EOF"}}
// Also don't save revision from the initial created: true messages because they always contain the latest revision if (etcd_watches_initialised == ETCD_TOTAL_WATCHES && !data["result"]["header"]["revision"].is_null())
if (etcd_watches_initialised == ETCD_TOTAL_WATCHES &&
!data["result"]["header"]["revision"].is_null() &&
!data["result"]["created"].bool_value())
{ {
// Restart watchers from the same revision number as in the last received message, // Restart watchers from the same revision number as in the last received message,
// not from the next one to protect against revision being split into multiple messages, // not from the next one to protect against revision being split into multiple messages,
@@ -470,11 +494,6 @@ void etcd_state_client_t::start_etcd_watcher()
fprintf(stderr, "Disconnected from etcd %s\n", cur_addr.c_str()); fprintf(stderr, "Disconnected from etcd %s\n", cur_addr.c_str());
if (cur_addr == selected_etcd_address) if (cur_addr == selected_etcd_address)
selected_etcd_address = ""; selected_etcd_address = "";
if (etcd_watch_ws)
{
http_close(etcd_watch_ws);
etcd_watch_ws = NULL;
}
if (etcd_watches_initialised == 0) if (etcd_watches_initialised == 0)
{ {
// Connection not established, retry in <etcd_quick_timeout> // Connection not established, retry in <etcd_quick_timeout>
@@ -551,11 +570,6 @@ void etcd_state_client_t::start_ws_keepalive()
{ {
fprintf(stderr, "Websocket ping failed, disconnecting from etcd %s\n", selected_etcd_address.c_str()); fprintf(stderr, "Websocket ping failed, disconnecting from etcd %s\n", selected_etcd_address.c_str());
} }
if (etcd_watch_ws)
{
http_close(etcd_watch_ws);
etcd_watch_ws = NULL;
}
start_etcd_watcher(); start_etcd_watcher();
} }
else else
@@ -1185,6 +1199,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
if (i >= pg_state_bit_count) if (i >= pg_state_bit_count)
{ {
fprintf(stderr, "Unexpected pool %u PG %u state keyword in etcd: %s\n", pool_id, pg_num, e.dump().c_str()); fprintf(stderr, "Unexpected pool %u PG %u state keyword in etcd: %s\n", pool_id, pg_num, e.dump().c_str());
return;
} }
} }
if (!cur_primary || !value["state"].is_array() || !state || if (!cur_primary || !value["state"].is_array() || !state ||
@@ -1193,6 +1208,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
(state & PG_INCOMPLETE) && state != PG_INCOMPLETE && state != (PG_INCOMPLETE|PG_HAS_INVALID)) (state & PG_INCOMPLETE) && state != PG_INCOMPLETE && state != (PG_INCOMPLETE|PG_HAS_INVALID))
{ {
fprintf(stderr, "Unexpected pool %u PG %u state in etcd: primary=%ju, state=%s\n", pool_id, pg_num, cur_primary, value["state"].dump().c_str()); fprintf(stderr, "Unexpected pool %u PG %u state in etcd: primary=%ju, state=%s\n", pool_id, pg_num, cur_primary, value["state"].dump().c_str());
return;
} }
pg_cfg.cur_primary = cur_primary; pg_cfg.cur_primary = cur_primary;
pg_cfg.cur_state = state; pg_cfg.cur_state = state;
@@ -1280,6 +1296,16 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
else else
parent_inode_num |= parent_pool_id << (64-POOL_ID_BITS); parent_inode_num |= parent_pool_id << (64-POOL_ID_BITS);
} }
std::shared_ptr<inode_enc_t> enc;
if (!value["enc_key"].string_value().empty())
{
std::vector<uint8_t> k = hexdecode(value["enc_key"].string_value());
if (k.size() == 512/8)
{
enc = std::make_shared<inode_enc_t>();
enc->key = std::move(k);
}
}
insert_inode_config((inode_config_t){ insert_inode_config((inode_config_t){
.num = inode_num, .num = inode_num,
.name = value["name"].string_value(), .name = value["name"].string_value(),
@@ -1287,6 +1313,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
.parent_id = parent_inode_num, .parent_id = parent_inode_num,
.readonly = value["readonly"].bool_value(), .readonly = value["readonly"].bool_value(),
.deleted = value["deleted"].bool_value(), .deleted = value["deleted"].bool_value(),
.enc = enc,
.meta = value["meta"], .meta = value["meta"],
.mod_revision = kv.mod_revision, .mod_revision = kv.mod_revision,
}); });
+17 -1
View File
@@ -4,9 +4,10 @@
#pragma once #pragma once
#include <set> #include <set>
#include <memory>
#include "json11/json11.hpp" #include "json11/json11.hpp"
#include "object_id.h" #include "osd_id.h"
#include "timerfd_manager.h" #include "timerfd_manager.h"
#define ETCD_CONFIG_WATCH_ID 1 #define ETCD_CONFIG_WATCH_ID 1
@@ -75,6 +76,14 @@ struct pool_config_t
void *reshard_state = NULL; void *reshard_state = NULL;
}; };
struct inode_enc_t
{
int refs = 0;
std::vector<uint8_t> key;
// FIXME It may also contain snapshot chain and key information
uint32_t bitmap_granularity = 0;
};
struct inode_config_t struct inode_config_t
{ {
uint64_t num = 0; uint64_t num = 0;
@@ -83,6 +92,7 @@ struct inode_config_t
inode_t parent_id = 0; inode_t parent_id = 0;
bool readonly = false; bool readonly = false;
bool deleted = false; bool deleted = false;
std::shared_ptr<inode_enc_t> enc;
// Arbitrary metadata // Arbitrary metadata
json11::Json meta; json11::Json meta;
// Change revision of the metadata in etcd // Change revision of the metadata in etcd
@@ -96,6 +106,7 @@ struct inode_watch_t
}; };
struct http_co_t; struct http_co_t;
struct http_context_t;
struct __attribute__((visibility("default"))) etcd_state_client_t struct __attribute__((visibility("default"))) etcd_state_client_t
{ {
@@ -125,9 +136,13 @@ public:
uint32_t global_immediate_commit = IMMEDIATE_NONE; uint32_t global_immediate_commit = IMMEDIATE_NONE;
std::string etcd_prefix; std::string etcd_prefix;
std::string etcd_client_cert;
std::string etcd_client_key;
std::string etcd_ca;
int log_level = 0; int log_level = 0;
timerfd_manager_t *tfd = NULL; timerfd_manager_t *tfd = NULL;
http_context_t *http_ctx = NULL;
http_co_t *etcd_watch_ws = NULL, *keepalive_client = NULL; http_co_t *etcd_watch_ws = NULL, *keepalive_client = NULL;
int etcd_watches_initialised = 0; int etcd_watches_initialised = 0;
uint64_t etcd_watch_revision_config = 0; uint64_t etcd_watch_revision_config = 0;
@@ -160,6 +175,7 @@ public:
json11::Json::object serialize_inode_cfg(inode_config_t *cfg); json11::Json::object serialize_inode_cfg(inode_config_t *cfg);
etcd_kv_t parse_etcd_kv(const json11::Json & kv_json); etcd_kv_t parse_etcd_kv(const json11::Json & kv_json);
std::vector<std::string> get_addresses(); std::vector<std::string> get_addresses();
http_context_t *get_http_ctx();
void etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function<void(std::string, json11::Json)> callback); void etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function<void(std::string, json11::Json)> callback);
void etcd_call(std::string api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback); void etcd_call(std::string api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
void etcd_txn(json11::Json txn, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback); void etcd_txn(json11::Json txn, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
+433 -68
View File
@@ -10,9 +10,17 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <stdexcept> #include <stdexcept>
#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#endif
#include "addr_util.h" #include "addr_util.h"
#include "str_util.h" #include "str_util.h"
#include "json_util.h" #include "json_util.h"
@@ -24,14 +32,51 @@
static std::string ws_format_frame(int type, uint64_t size); static std::string ws_format_frame(int type, uint64_t size);
static bool ws_parse_frame(std::string & buf, uint8_t & type, std::string & res); static bool ws_parse_frame(std::string & buf, uint8_t & type, std::string & res);
static void parse_http_headers(std::string & res, http_response_t *parsed); static void parse_http_headers(std::string & res, http_message_t *parsed, bool is_request);
struct http_context_t
{
std::string ssl_cert;
std::string ssl_key;
std::string ssl_ca;
#ifdef WITH_OPENSSL
SSL_CTX *ssl_ctx = NULL;
#endif
~http_context_t()
{
#ifdef WITH_OPENSSL
if (ssl_ctx)
{
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
}
#endif
}
};
struct http_call_t
{
std::string host;
std::string request;
http_options_t options;
std::function<void(http_message_t *)> cb;
};
struct http_co_t struct http_co_t
{ {
http_context_t *ctx = NULL;
#ifdef WITH_OPENSSL
SSL *ssl_cli = NULL;
BIO *ssl_bio = NULL;
#endif
timerfd_manager_t *tfd; timerfd_manager_t *tfd;
std::function<void(const http_response_t*)> response_callback; std::function<void(http_message_t*)> response_callback;
int request_timeout = 0; int request_timeout = 0;
bool ssl = false;
std::string host; std::string host;
std::string request; std::string request;
std::string ws_outbox; std::string ws_outbox;
@@ -39,7 +84,7 @@ struct http_co_t
bool want_streaming; bool want_streaming;
bool keepalive; bool keepalive;
std::vector<std::function<void()>> keepalive_queue; std::vector<http_call_t> keepalive_queue;
int state = 0; int state = 0;
std::string connected_host; std::string connected_host;
@@ -47,10 +92,10 @@ struct http_co_t
int timeout_id = -1; int timeout_id = -1;
int epoll_events = 0; int epoll_events = 0;
int sent = 0; int sent = 0;
std::vector<char> rbuf; std::vector<uint8_t> rbuf;
iovec read_iov, send_iov; iovec read_iov, send_iov;
msghdr read_msg = { 0 }, send_msg = { 0 }; msghdr read_msg = { 0 }, send_msg = { 0 };
http_response_t parsed; http_message_t parsed;
uint64_t target_response_size = 0; uint64_t target_response_size = 0;
int onstack = 0; int onstack = 0;
@@ -70,9 +115,14 @@ struct http_co_t
void submit_read(bool check_timeout); void submit_read(bool check_timeout);
void submit_send(); void submit_send();
bool handle_read(); bool handle_read();
#ifdef WITH_OPENSSL
bool do_ssl_handshake(bool init_send);
void on_ssl_error(int res);
#endif
void post_message(uint8_t type, const std::string & msg); void post_message(uint8_t type, const std::string & msg);
void reply(const std::string & msg);
void send_request(const std::string & host, const std::string & request, void send_request(const std::string & host, const std::string & request,
const http_options_t & options, std::function<void(const http_response_t *response)> response_callback); const http_options_t & options, std::function<void(http_message_t *response)> response_callback);
}; };
#define HTTP_CO_CLOSED 0 #define HTTP_CO_CLOSED 0
@@ -83,20 +133,64 @@ struct http_co_t
#define HTTP_CO_WEBSOCKET 5 #define HTTP_CO_WEBSOCKET 5
#define HTTP_CO_CHUNKED 6 #define HTTP_CO_CHUNKED 6
#define HTTP_CO_KEEPALIVE 7 #define HTTP_CO_KEEPALIVE 7
#define HTTP_CO_SERVER 8
#define HTTP_CO_REQ_HDR_RECEIVED 9
#define HTTP_CO_REQUEST_RECEIVED 10
#define DEFAULT_TIMEOUT 5000 #define DEFAULT_TIMEOUT 5000
http_co_t *http_init(timerfd_manager_t *tfd) http_context_t* http_context_init(const std::string & ssl_cert, const std::string & ssl_key,
const std::string & ssl_ca, bool verify_peer, std::string & error)
{
http_context_t *ctx = new http_context_t;
#ifdef WITH_OPENSSL
SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
ctx->ssl_cert = ssl_cert;
ctx->ssl_key = ssl_key;
ctx->ssl_ca = ssl_ca;
ctx->ssl_ctx = ssl_ctx;
if (!ssl_ctx)
goto init_err;
SSL_CTX_set_verify(ssl_ctx, verify_peer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
if (!SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION))
goto init_err;
if ((ssl_ca != "")
? !SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca.c_str(), NULL)
: !SSL_CTX_set_default_verify_paths(ssl_ctx))
goto init_err;
if (ssl_cert != "" && ssl_key != "" &&
(!SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert.c_str(), SSL_FILETYPE_PEM) ||
!SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key.c_str(), SSL_FILETYPE_PEM)))
goto init_err;
#endif
return ctx;
init_err:
error = std::string("openssl initialization failed: ")+ERR_error_string(ERR_get_error(), NULL);
delete ctx;
return NULL;
}
void http_context_destroy(http_context_t *ctx)
{
delete ctx;
}
http_co_t *http_init(timerfd_manager_t *tfd, http_context_t *ctx)
{ {
http_co_t *handler = new http_co_t(); http_co_t *handler = new http_co_t();
handler->tfd = tfd; handler->tfd = tfd;
handler->state = HTTP_CO_CLOSED; handler->state = HTTP_CO_CLOSED;
handler->ctx = ctx;
return handler; return handler;
} }
http_co_t* open_websocket(timerfd_manager_t *tfd, const std::string & host, const std::string & path, void open_websocket(http_co_t *handler, const std::string & host, const std::string & path,
int timeout, std::function<void(const http_response_t *msg)> response_callback) const http_options_t & options, std::function<void(http_message_t *msg)> response_callback)
{ {
if (handler->state == HTTP_CO_KEEPALIVE && (handler->connected_host != host || handler->ssl != options.ssl))
handler->close_connection();
if (handler->state != HTTP_CO_KEEPALIVE && handler->state != HTTP_CO_CLOSED)
throw std::runtime_error("Attempt to open websocket on a keepalive stream");
std::string request = "GET "+path+" HTTP/1.1\r\n" std::string request = "GET "+path+" HTTP/1.1\r\n"
"Host: "+host+"\r\n" "Host: "+host+"\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
@@ -104,29 +198,54 @@ http_co_t* open_websocket(timerfd_manager_t *tfd, const std::string & host, cons
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Version: 13\r\n"
"\r\n"; "\r\n";
http_co_t *handler = new http_co_t();
handler->tfd = tfd;
handler->state = HTTP_CO_CLOSED;
handler->host = host; handler->host = host;
handler->request_timeout = timeout < 0 ? -1 : (timeout == 0 ? DEFAULT_TIMEOUT : timeout); handler->request_timeout = options.timeout < 0 ? -1 : (options.timeout == 0 ? DEFAULT_TIMEOUT : options.timeout);
handler->want_streaming = false; handler->want_streaming = false;
handler->keepalive = false; handler->keepalive = false;
handler->ssl = options.ssl;
handler->request = request; handler->request = request;
handler->response_callback = response_callback; handler->response_callback = response_callback;
handler->ws_outbox = "";
handler->response = "";
handler->sent = 0;
handler->parsed = {};
handler->start_ws_connection(); handler->start_ws_connection();
return handler;
} }
void http_request(http_co_t *handler, const std::string & host, const std::string & request, void http_request(http_co_t *handler, const std::string & host, const std::string & request,
const http_options_t & options, std::function<void(const http_response_t *response)> response_callback) const http_options_t & options, std::function<void(http_message_t *response)> response_callback)
{ {
handler->send_request(host, request, options, response_callback); handler->send_request(host, request, options, response_callback);
} }
void http_serve(http_co_t *handler, int peer_fd, const http_options_t & options, std::function<void(http_message_t *msg)> request_callback)
{
if (handler->state != HTTP_CO_SERVER || handler->peer_fd != peer_fd)
handler->close_connection();
handler->host = "";
handler->request_timeout = options.timeout < 0 ? -1 : (options.timeout == 0 ? DEFAULT_TIMEOUT : options.timeout);
handler->want_streaming = false;
handler->keepalive = false;
handler->ssl = options.ssl;
handler->request = "";
handler->response_callback = request_callback;
handler->ws_outbox = "";
handler->response = "";
handler->sent = 0;
handler->parsed = {};
handler->peer_fd = peer_fd;
handler->state = HTTP_CO_SERVER;
handler->tfd->set_fd_handler(peer_fd, false, [handler](int peer_fd, int epoll_events)
{
handler->epoll_events |= epoll_events;
handler->handle_events();
});
}
void http_co_t::run_cb_and_clear() void http_co_t::run_cb_and_clear()
{ {
parsed.eof = true; parsed.eof = true;
std::function<void(const http_response_t*)> cb; std::function<void(http_message_t*)> cb;
cb.swap(response_callback); cb.swap(response_callback);
// Call callback after clearing it because otherwise we may hit reenterability problems // Call callback after clearing it because otherwise we may hit reenterability problems
if (cb != NULL) if (cb != NULL)
@@ -135,7 +254,7 @@ void http_co_t::run_cb_and_clear()
} }
void http_co_t::send_request(const std::string & host, const std::string & request, void http_co_t::send_request(const std::string & host, const std::string & request,
const http_options_t & options, std::function<void(const http_response_t *response)> response_callback) const http_options_t & options, std::function<void(http_message_t *response)> response_callback)
{ {
stackin(); stackin();
if (state == HTTP_CO_WEBSOCKET) if (state == HTTP_CO_WEBSOCKET)
@@ -145,20 +264,18 @@ void http_co_t::send_request(const std::string & host, const std::string & reque
} }
else if (state != HTTP_CO_KEEPALIVE && state != HTTP_CO_CLOSED) else if (state != HTTP_CO_KEEPALIVE && state != HTTP_CO_CLOSED)
{ {
keepalive_queue.push_back([this, host, request, options, response_callback]() keepalive_queue.emplace_back((http_call_t){ host, request, options, std::move(response_callback) });
{
this->send_request(host, request, options, response_callback);
});
stackout(); stackout();
return; return;
} }
if (state == HTTP_CO_KEEPALIVE && connected_host != host) if (state == HTTP_CO_KEEPALIVE && (connected_host != host || ssl != options.ssl))
{ {
close_connection(); close_connection();
} }
this->request_timeout = options.timeout < 0 ? 0 : (options.timeout == 0 ? DEFAULT_TIMEOUT : options.timeout); this->request_timeout = options.timeout < 0 ? 0 : (options.timeout == 0 ? DEFAULT_TIMEOUT : options.timeout);
this->want_streaming = options.want_streaming; this->want_streaming = options.want_streaming;
this->keepalive = options.keepalive; this->keepalive = options.keepalive;
this->ssl = options.ssl;
this->host = host; this->host = host;
this->request = request; this->request = request;
this->response = ""; this->response = "";
@@ -190,7 +307,7 @@ void http_co_t::send_request(const std::string & host, const std::string & reque
else else
{ {
close_connection(); close_connection();
parsed = { .error = "HTTP request timed out" }; parsed = { .error = "HTTP request timed out", .status_code = ETIMEDOUT };
run_cb_and_clear(); run_cb_and_clear();
} }
stackout(); stackout();
@@ -204,6 +321,11 @@ void http_post_message(http_co_t *handler, uint8_t type, const std::string & msg
handler->post_message(type, msg); handler->post_message(type, msg);
} }
void http_reply(http_co_t *handler, const std::string & reply)
{
handler->reply(reply);
}
void http_co_t::post_message(uint8_t type, const std::string & msg) void http_co_t::post_message(uint8_t type, const std::string & msg)
{ {
stackin(); stackin();
@@ -213,7 +335,8 @@ void http_co_t::post_message(uint8_t type, const std::string & msg)
request += msg; request += msg;
submit_send(); submit_send();
} }
else if (state == HTTP_CO_KEEPALIVE || state == HTTP_CO_CHUNKED) else if (state == HTTP_CO_KEEPALIVE || state == HTTP_CO_CHUNKED ||
state == HTTP_CO_SERVER || state == HTTP_CO_REQ_HDR_RECEIVED || state == HTTP_CO_REQUEST_RECEIVED)
{ {
throw std::runtime_error("Attempt to send websocket message on a regular HTTP connection"); throw std::runtime_error("Attempt to send websocket message on a regular HTTP connection");
} }
@@ -225,12 +348,29 @@ void http_co_t::post_message(uint8_t type, const std::string & msg)
stackout(); stackout();
} }
void http_close(http_co_t *handler) void http_co_t::reply(const std::string & reply)
{
stackin();
if (state != HTTP_CO_REQUEST_RECEIVED)
{
throw std::runtime_error("Attempt to send HTTP response in invalid connection state");
}
request += reply;
submit_send();
stackout();
}
void http_destroy(http_co_t *handler)
{ {
handler->end(); handler->end();
} }
void http_response_t::parse_json_response(std::string & error, json11::Json & r) const void http_close(http_co_t *handler)
{
handler->close_connection();
}
void http_message_t::parse_json_response(std::string & error, json11::Json & r) const
{ {
if (this->error != "") if (this->error != "")
{ {
@@ -277,6 +417,15 @@ void http_co_t::close_connection()
close(peer_fd); close(peer_fd);
peer_fd = -1; peer_fd = -1;
} }
#ifdef WITH_OPENSSL
if (ssl_cli)
{
// Frees client and bios at once
SSL_free(ssl_cli);
ssl_cli = NULL;
}
ssl_bio = NULL;
#endif
state = HTTP_CO_CLOSED; state = HTTP_CO_CLOSED;
connected_host = ""; connected_host = "";
response = ""; response = "";
@@ -295,7 +444,7 @@ void http_co_t::start_ws_connection()
if (state != HTTP_CO_WEBSOCKET) if (state != HTTP_CO_WEBSOCKET)
{ {
close_connection(); close_connection();
parsed = { .error = "Websocket connection timed out" }; parsed = { .error = "Websocket connection timed out", .status_code = ETIMEDOUT };
run_cb_and_clear(); run_cb_and_clear();
} }
stackout(); stackout();
@@ -311,7 +460,7 @@ void http_co_t::start_connection()
if (!string_to_addr(host.c_str(), 1, 80, &addr)) if (!string_to_addr(host.c_str(), 1, 80, &addr))
{ {
close_connection(); close_connection();
parsed = { .error = "Invalid address: "+host }; parsed = { .error = "Invalid address: "+host, .status_code = EINVAL };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return; return;
@@ -320,19 +469,56 @@ void http_co_t::start_connection()
if (peer_fd < 0) if (peer_fd < 0)
{ {
close_connection(); close_connection();
parsed = { .error = std::string("socket: ")+strerror(errno) }; parsed = { .error = std::string("socket: ")+strerror(errno), .status_code = errno };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return; return;
} }
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK); fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
epoll_events = 0; epoll_events = 0;
#ifdef WITH_OPENSSL
// https://wiki.openssl.org/index.php/Hostname_validation
if (ssl)
{
if (!ctx)
goto init_err;
ssl_bio = BIO_new(BIO_s_socket());
if (!ssl_bio)
goto init_err;
if (!BIO_set_fd(ssl_bio, peer_fd, BIO_NOCLOSE))
goto init_err;
ssl_cli = SSL_new(ctx->ssl_ctx);
if (!ssl_cli)
goto init_err;
SSL_set_bio(ssl_cli, ssl_bio, ssl_bio);
if (!SSL_set_tlsext_host_name(ssl_cli, host.c_str()))
{
init_err:
if (ssl_cli)
{
SSL_free(ssl_cli);
ssl_cli = NULL;
}
else if (ssl_bio)
{
BIO_free(ssl_bio);
ssl_bio = NULL;
}
parsed = { .error = std::string("openssl initialization failed: ")+ERR_error_string(ERR_get_error(), NULL) };
response_callback(&parsed);
response_callback = NULL;
stackout();
return;
}
SSL_set_connect_state(ssl_cli);
}
#endif
// Finally call connect // Finally call connect
int r = ::connect(peer_fd, (sockaddr*)&addr, sizeof(addr)); int r = ::connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
if (r < 0 && errno != EINPROGRESS) if (r < 0 && errno != EINPROGRESS)
{ {
close_connection(); close_connection();
parsed = { .error = std::string("connect: ")+strerror(errno) }; parsed = { .error = std::string("connect: ")+strerror(errno), .status_code = errno };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return; return;
@@ -367,6 +553,8 @@ void http_co_t::handle_events()
{ {
if (state == HTTP_CO_HEADERS_RECEIVED) if (state == HTTP_CO_HEADERS_RECEIVED)
std::swap(parsed.body, response); std::swap(parsed.body, response);
else if (state == HTTP_CO_SERVER || state == HTTP_CO_REQ_HDR_RECEIVED || state == HTTP_CO_REQUEST_RECEIVED)
parsed = { .error = "client has disconnected normally" };
close_connection(); close_connection();
run_cb_and_clear(); run_cb_and_clear();
break; break;
@@ -388,7 +576,7 @@ void http_co_t::handle_connect_result()
if (result != 0) if (result != 0)
{ {
close_connection(); close_connection();
parsed = { .error = std::string("connect: ")+strerror(result) }; parsed = { .error = std::string("connect: ")+strerror(result), .status_code = result };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return; return;
@@ -408,18 +596,44 @@ void http_co_t::handle_connect_result()
void http_co_t::submit_send() void http_co_t::submit_send()
{ {
stackin(); stackin();
int res; ssize_t res = 0;
again: again:
if (sent < request.size()) if (sent < request.size())
{ {
send_iov = (iovec){ .iov_base = (void*)(request.c_str()+sent), .iov_len = request.size()-sent }; send_iov = (iovec){ .iov_base = (void*)(request.data()+sent), .iov_len = request.size()-sent };
send_msg.msg_iov = &send_iov; #ifdef WITH_OPENSSL
send_msg.msg_iovlen = 1; if (!ssl)
res = sendmsg(peer_fd, &send_msg, MSG_NOSIGNAL); #endif
if (res < 0)
{ {
res = -errno; send_msg.msg_iov = &send_iov;
send_msg.msg_iovlen = 1;
res = sendmsg(peer_fd, &send_msg, MSG_NOSIGNAL);
if (res < 0)
res = -errno;
} }
#ifdef WITH_OPENSSL
else
{
if (!do_ssl_handshake(false))
goto out;
int ok = SSL_write_ex(ssl_cli, send_iov.iov_base, send_iov.iov_len, (size_t*)&res);
if (!ok)
{
res = SSL_get_error(ssl_cli, ok);
if (res == SSL_ERROR_WANT_WRITE || res == 0)
res = 0;
else if (res == SSL_ERROR_WANT_READ)
goto out;
else if (res == SSL_ERROR_SYSCALL)
res = -errno;
else
{
on_ssl_error(res);
goto out;
}
}
}
#endif
if (res == -EAGAIN || res == -EINTR) if (res == -EAGAIN || res == -EINTR)
{ {
res = 0; res = 0;
@@ -427,13 +641,33 @@ again:
else if (res < 0) else if (res < 0)
{ {
close_connection(); close_connection();
parsed = { .error = std::string("sendmsg: ")+strerror(errno) }; parsed = { .error = std::string("sendmsg: ")+strerror(errno), .status_code = errno };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return; return;
} }
sent += res; sent += res;
if (state == HTTP_CO_SENDING_REQUEST) if (state == HTTP_CO_REQUEST_RECEIVED)
{
if (sent >= request.size())
{
if (!keepalive)
{
close_connection();
parsed = { .error = "connection is not keep-alive" };
run_cb_and_clear();
stackout();
return;
}
state = HTTP_CO_SERVER;
request = "";
sent = 0;
}
else
goto again;
handle_read();
}
else if (state == HTTP_CO_SENDING_REQUEST)
{ {
if (sent >= request.size()) if (sent >= request.size())
state = HTTP_CO_REQUEST_SENT; state = HTTP_CO_REQUEST_SENT;
@@ -447,26 +681,53 @@ again:
goto again; goto again;
} }
} }
out:
stackout(); stackout();
} }
void http_co_t::submit_read(bool check_timeout) void http_co_t::submit_read(bool check_timeout)
{ {
stackin(); stackin();
int res; ssize_t res = 0;
again: again:
if (rbuf.size() != READ_BUFFER_SIZE) if (rbuf.size() != READ_BUFFER_SIZE)
{ {
rbuf.resize(READ_BUFFER_SIZE); rbuf.resize(READ_BUFFER_SIZE);
} }
read_iov = { .iov_base = rbuf.data(), .iov_len = READ_BUFFER_SIZE }; read_iov = { .iov_base = rbuf.data(), .iov_len = READ_BUFFER_SIZE };
read_msg.msg_iov = &read_iov; #ifdef WITH_OPENSSL
read_msg.msg_iovlen = 1; if (!ssl)
res = recvmsg(peer_fd, &read_msg, 0); #endif
if (res < 0)
{ {
res = -errno; read_msg.msg_iov = &read_iov;
read_msg.msg_iovlen = 1;
res = recvmsg(peer_fd, &read_msg, 0);
if (res < 0)
res = -errno;
} }
#ifdef WITH_OPENSSL
else
{
if (!do_ssl_handshake(true))
goto out;
int ok = SSL_read_ex(ssl_cli, read_iov.iov_base, read_iov.iov_len, (size_t*)&res);
if (!ok)
{
res = SSL_get_error(ssl_cli, ok);
if (res == SSL_ERROR_WANT_READ)
res = -EAGAIN;
else if (res == SSL_ERROR_SYSCALL)
res = -errno;
else if (res == SSL_ERROR_ZERO_RETURN)
res = 0;
else
{
on_ssl_error(res);
goto out;
}
}
}
#endif
if (res == -EAGAIN || res == -EINTR) if (res == -EAGAIN || res == -EINTR)
{ {
if (check_timeout) if (check_timeout)
@@ -477,7 +738,7 @@ again:
{ {
// Timeout happened and there is no data to read // Timeout happened and there is no data to read
close_connection(); close_connection();
parsed = { .error = "HTTP request timed out" }; parsed = { .error = "HTTP request timed out", .status_code = ETIMEDOUT };
run_cb_and_clear(); run_cb_and_clear();
} }
} }
@@ -492,23 +753,76 @@ again:
epoll_events = epoll_events & ~EPOLLIN; epoll_events = epoll_events & ~EPOLLIN;
if (state == HTTP_CO_HEADERS_RECEIVED) if (state == HTTP_CO_HEADERS_RECEIVED)
std::swap(parsed.body, response); std::swap(parsed.body, response);
close_connection();
if (res < 0) if (res < 0)
parsed = { .error = std::string("recvmsg: ")+strerror(-res) }; parsed = { .error = std::string("recvmsg: ")+strerror(-res), .status_code = (int)-res };
else if (state == HTTP_CO_SERVER || state == HTTP_CO_REQ_HDR_RECEIVED || state == HTTP_CO_REQUEST_RECEIVED)
parsed = { .error = "client has disconnected normally" };
close_connection();
run_cb_and_clear(); run_cb_and_clear();
} }
else else
{ {
response += std::string(rbuf.data(), res); response += std::string((char*)rbuf.data(), res);
handle_read(); handle_read();
} }
out:
stackout(); stackout();
} }
#ifdef WITH_OPENSSL
void http_co_t::on_ssl_error(int res)
{
close_connection();
if (res == SSL_ERROR_ZERO_RETURN)
{
// Client closed the connection
parsed = { .error = "peer closed the SSL connection" };
}
else
parsed = { .error = std::string("SSL error: ")+ERR_error_string(ERR_get_error(), NULL), .status_code = EIO };
run_cb_and_clear();
}
bool http_co_t::do_ssl_handshake(bool init_send)
{
if (SSL_is_init_finished(ssl_cli))
return true;
int r;
while (1)
{
r = SSL_do_handshake(ssl_cli);
if (r > 0)
{
// OK
if (init_send)
submit_send();
return true;
}
r = SSL_get_error(ssl_cli, r);
if (r == SSL_ERROR_WANT_READ)
{
break;
}
else
{
int errcode = ERR_get_error();
parsed = { .error = ERR_error_string(errcode, NULL), .status_code = EIO };
close_connection();
run_cb_and_clear();
return false;
}
}
return false;
}
#endif
bool http_co_t::handle_read() bool http_co_t::handle_read()
{ {
stackin(); stackin();
if (state == HTTP_CO_REQUEST_SENT) if (state == HTTP_CO_REQUEST_RECEIVED)
{
}
else if (state == HTTP_CO_REQUEST_SENT)
{ {
int pos = response.find("\r\n\r\n"); int pos = response.find("\r\n\r\n");
if (pos >= 0) if (pos >= 0)
@@ -520,7 +834,7 @@ bool http_co_t::handle_read()
timeout_id = -1; timeout_id = -1;
} }
state = HTTP_CO_HEADERS_RECEIVED; state = HTTP_CO_HEADERS_RECEIVED;
parse_http_headers(response, &parsed); parse_http_headers(response, &parsed, false);
if (parsed.status_code == 101 && if (parsed.status_code == 101 &&
parsed.headers.find("sec-websocket-accept") != parsed.headers.end() && parsed.headers.find("sec-websocket-accept") != parsed.headers.end() &&
parsed.headers["upgrade"] == "websocket" && parsed.headers["upgrade"] == "websocket" &&
@@ -544,7 +858,7 @@ bool http_co_t::handle_read()
{ {
// Sorry, unsupported response // Sorry, unsupported response
close_connection(); close_connection();
parsed = { .error = "Response has neither Connection: close, nor Transfer-Encoding: chunked nor Content-Length headers" }; parsed = { .error = "Response has neither Connection: close, nor Transfer-Encoding: chunked nor Content-Length headers", .status_code = EINVAL };
run_cb_and_clear(); run_cb_and_clear();
stackout(); stackout();
return false; return false;
@@ -556,14 +870,57 @@ bool http_co_t::handle_read()
} }
} }
} }
if (state == HTTP_CO_HEADERS_RECEIVED && target_response_size > 0 && response.size() >= target_response_size) else if (state == HTTP_CO_SERVER)
{
int pos = response.find("\r\n\r\n");
if (pos >= 0)
{
if (timeout_id >= 0)
{
// Timeout is cleared when headers are received
tfd->clear_timer(timeout_id);
timeout_id = -1;
}
state = HTTP_CO_REQ_HDR_RECEIVED;
parse_http_headers(response, &parsed, true);
auto conn_it = parsed.headers.find("connection");
keepalive = (conn_it != parsed.headers.end() && conn_it->second == "keep-alive");
auto enc_it = parsed.headers.find("transfer-encoding");
if (enc_it != parsed.headers.end())
{
// Sorry, unsupported request
close_connection();
parsed = { .error = "Chunked requests are not supported", .status_code = EINVAL };
run_cb_and_clear();
stackout();
return false;
}
auto len_it = parsed.headers.find("content-length");
target_response_size = stoull_full(len_it != parsed.headers.end() ? len_it->second : "");
if (!target_response_size)
{
state = HTTP_CO_REQUEST_RECEIVED;
response_callback(&parsed);
}
}
}
if ((state == HTTP_CO_HEADERS_RECEIVED || state == HTTP_CO_REQ_HDR_RECEIVED) &&
target_response_size > 0 && response.size() >= target_response_size)
{ {
std::swap(parsed.body, response); std::swap(parsed.body, response);
if (!keepalive) if (state == HTTP_CO_REQ_HDR_RECEIVED)
close_connection(); {
state = HTTP_CO_REQUEST_RECEIVED;
response_callback(&parsed);
}
else else
state = HTTP_CO_KEEPALIVE; {
run_cb_and_clear(); if (!keepalive)
close_connection();
else
state = HTTP_CO_KEEPALIVE;
run_cb_and_clear();
}
} }
else if (state == HTTP_CO_CHUNKED && response.size() > 0) else if (state == HTTP_CO_CHUNKED && response.size() > 0)
{ {
@@ -626,26 +983,34 @@ void http_co_t::next_request()
{ {
if (keepalive_queue.size() > 0) if (keepalive_queue.size() > 0)
{ {
auto next = keepalive_queue[0]; auto next = std::move(keepalive_queue[0]);
keepalive_queue.erase(keepalive_queue.begin(), keepalive_queue.begin()+1); keepalive_queue.erase(keepalive_queue.begin());
next(); send_request(next.host, next.request, next.options, next.cb);
} }
} }
static void parse_http_headers(std::string & res, http_response_t *parsed) static void parse_http_headers(std::string & res, http_message_t *parsed, bool is_request)
{ {
int pos = res.find("\r\n"); int pos = res.find("\r\n");
pos = pos < 0 ? res.length() : pos+2; pos = pos < 0 ? res.length() : pos+2;
std::string status_line = res.substr(0, pos); std::string status_line = res.substr(0, pos);
int http_version; int http_version;
char *status_text = NULL; char *status_text = NULL;
sscanf(status_line.c_str(), "HTTP/1.%d %d %ms", &http_version, &parsed->status_code, &status_text); if (!is_request)
if (status_text)
{ {
parsed->status_line = status_text; sscanf(status_line.c_str(), "HTTP/1.%d %d %ms", &http_version, &parsed->status_code, &status_text);
// %ms = allocate a buffer if (status_text)
free(status_text); {
status_text = NULL; parsed->status_line = status_text;
// %ms = allocate a buffer
free(status_text);
status_text = NULL;
}
}
else
{
// Should be GET/POST / HTTP/1.1
parsed->status_line = status_line;
} }
int prev = pos; int prev = pos;
while ((pos = res.find("\r\n", prev)) >= prev) while ((pos = res.find("\r\n", prev)) >= prev)
+19 -5
View File
@@ -17,14 +17,19 @@
class timerfd_manager_t; class timerfd_manager_t;
#pragma GCC visibility push(default)
struct http_options_t struct http_options_t
{ {
int timeout; int timeout;
bool want_streaming; bool want_streaming;
bool keepalive; bool keepalive;
bool ssl;
}; };
struct http_response_t struct http_context_t;
struct http_message_t
{ {
std::string error; std::string error;
@@ -41,10 +46,19 @@ struct http_response_t
// Opened websocket or keepalive HTTP connection // Opened websocket or keepalive HTTP connection
struct http_co_t; struct http_co_t;
http_co_t* http_init(timerfd_manager_t *tfd); http_context_t* http_context_init(const std::string & ssl_cert, const std::string & ssl_key,
http_co_t* open_websocket(timerfd_manager_t *tfd, const std::string & host, const std::string & path, const std::string & ssl_ca, bool verify_peer, std::string & error);
int timeout, std::function<void(const http_response_t *msg)> on_message); void http_context_destroy(http_context_t *ctx);
http_co_t* http_init(timerfd_manager_t *tfd, http_context_t *ctx = NULL);
void open_websocket(http_co_t *handler, const std::string & host, const std::string & path,
const http_options_t & options, std::function<void(http_message_t *msg)> on_message);
void http_request(http_co_t *handler, const std::string & host, const std::string & request, void http_request(http_co_t *handler, const std::string & host, const std::string & request,
const http_options_t & options, std::function<void(const http_response_t *response)> response_callback); const http_options_t & options, std::function<void(http_message_t *response)> response_callback);
void http_post_message(http_co_t *handler, uint8_t type, const std::string & msg); void http_post_message(http_co_t *handler, uint8_t type, const std::string & msg);
void http_serve(http_co_t *handler, int peer_fd, const http_options_t & options,
std::function<void(http_message_t *msg)> request_callback);
void http_reply(http_co_t *handler, const std::string & reply);
void http_close(http_co_t *co); void http_close(http_co_t *co);
void http_destroy(http_co_t *co);
#pragma GCC visibility pop
+190 -71
View File
@@ -15,6 +15,106 @@
#include "msgr_rdma.h" #include "msgr_rdma.h"
#endif #endif
#include <sys/poll.h>
msgr_iothread_t::msgr_iothread_t():
ring(RINGLOOP_DEFAULT_SIZE, true),
thread(&msgr_iothread_t::run, this)
{
eventfd = ring.register_eventfd();
if (eventfd < 0)
{
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
}
}
msgr_iothread_t::~msgr_iothread_t()
{
stop();
}
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
{
mu.lock();
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
if (queue.size() == 1)
{
cond.notify_all();
}
mu.unlock();
}
void msgr_iothread_t::stop()
{
mu.lock();
if (stopped)
{
mu.unlock();
return;
}
stopped = true;
if (outer_loop_data)
{
outer_loop_data->callback = [](ring_data_t*){};
}
cond.notify_all();
close(eventfd);
mu.unlock();
thread.join();
}
void msgr_iothread_t::add_to_ringloop(ring_loop_t *outer_loop)
{
assert(!this->outer_loop || this->outer_loop == outer_loop);
io_uring_sqe *sqe = outer_loop->get_sqe();
assert(sqe != NULL);
this->outer_loop = outer_loop;
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
outer_loop_data->callback = [this](ring_data_t *data)
{
if (data->res < 0)
{
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
}
outer_loop_data = NULL;
if (stopped)
{
return;
}
add_to_ringloop(this->outer_loop);
ring.loop();
};
}
void msgr_iothread_t::run()
{
while (true)
{
{
std::unique_lock<std::mutex> lk(mu);
while (!stopped && !queue.size())
cond.wait(lk);
if (stopped)
return;
int i = 0;
for (; i < queue.size(); i++)
{
io_uring_sqe *sqe = ring.get_sqe();
if (!sqe)
break;
ring_data_t *data = ((ring_data_t*)sqe->user_data);
*data = std::move(queue[i].data);
*sqe = queue[i].sqe;
sqe->user_data = (uint64_t)data;
}
queue.erase(queue.begin(), queue.begin()+i);
}
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
ring.submit();
}
}
void osd_messenger_t::init() void osd_messenger_t::init()
{ {
#ifdef WITH_RDMACM #ifdef WITH_RDMACM
@@ -73,17 +173,21 @@ void osd_messenger_t::init()
} }
if (ringloop && iothread_count > 0) if (ringloop && iothread_count > 0)
{ {
init_iothreads(); for (int i = 0; i < iothread_count; i++)
{
auto iot = new msgr_iothread_t();
iothreads.push_back(iot);
iot->add_to_ringloop(ringloop);
}
} }
keepalive_timer_id = tfd->set_timer(1000, true, [this](int) keepalive_timer_id = tfd->set_timer(1000, true, [this](int)
{ {
std::vector<uint64_t> clients_to_stop;
std::vector<osd_op_t*> ops_to_send;
auto cl_it = clients.begin(); auto cl_it = clients.begin();
while (cl_it != clients.end()) while (cl_it != clients.end())
{ {
auto cl = cl_it->second; auto cl = cl_it->second;
cl_it++; cl_it++;
auto peer_fd = cl->peer_fd;
if (!cl->osd_num && !cl->in_osd_num || cl->peer_state != PEER_CONNECTED && cl->peer_state != PEER_RDMA) if (!cl->osd_num && !cl->in_osd_num || cl->peer_state != PEER_CONNECTED && cl->peer_state != PEER_RDMA)
{ {
// Do not run keepalive on regular clients // Do not run keepalive on regular clients
@@ -95,9 +199,10 @@ void osd_messenger_t::init()
if (!cl->ping_time_remaining) if (!cl->ping_time_remaining)
{ {
// Ping timed out, stop the client // Ping timed out, stop the client
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n", fprintf(stderr, "Ping timed out for OSD %ju (client %d), disconnecting peer\n", cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->peer_fd);
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id); stop_client(peer_fd, true);
clients_to_stop.push_back(cl->client_id); // Restart iterator because it may be invalidated
cl_it = clients.upper_bound(peer_fd);
} }
} }
else if (cl->idle_time_remaining > 0) else if (cl->idle_time_remaining > 0)
@@ -108,36 +213,37 @@ void osd_messenger_t::init()
// Connection is idle for <osd_idle_time>, send ping // Connection is idle for <osd_idle_time>, send ping
osd_op_t *op = new osd_op_t(); osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT; op->op_type = OSD_OP_OUT;
op->client_id = cl->client_id; op->peer_fd = cl->peer_fd;
op->req = (osd_any_op_t){ op->req = (osd_any_op_t){
.hdr = { .hdr = {
.magic = SECONDARY_OSD_OP_MAGIC, .magic = SECONDARY_OSD_OP_MAGIC,
.opcode = OSD_OP_PING, .opcode = OSD_OP_PING,
}, },
}; };
op->callback = [this](osd_op_t *op) op->callback = [this, cl](osd_op_t *op)
{ {
auto cl_it = clients.find(op->client_id); auto cl_it = clients.find(op->peer_fd);
if (cl_it == clients.end()) if (cl_it == clients.end() || cl_it->second != cl)
{ {
// client is already dropped // client is already dropped
delete op; delete op;
return; return;
} }
auto cl = cl_it->second; int fail_fd = (op->reply.hdr.retval != 0 ? op->peer_fd : -1);
uint64_t fail_client_id = (op->reply.hdr.retval != 0 ? op->client_id : 0);
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num; auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
cl->ping_time_remaining = 0; cl->ping_time_remaining = 0;
delete op; delete op;
if (fail_client_id) if (fail_fd >= 0)
{ {
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, fail_client_id); fprintf(stderr, "Ping failed for OSD %ju (client %d), disconnecting peer\n", fail_osd_num, fail_fd);
stop_client(fail_client_id); stop_client(fail_fd, true);
} }
}; };
cl->ping_time_remaining = osd_ping_timeout; cl->ping_time_remaining = osd_ping_timeout;
cl->idle_time_remaining = osd_idle_timeout; cl->idle_time_remaining = osd_idle_timeout;
ops_to_send.push_back(op); outbox_push(op);
// Restart iterator because it may be invalidated
cl_it = clients.upper_bound(peer_fd);
} }
} }
else else
@@ -145,14 +251,6 @@ void osd_messenger_t::init()
cl->idle_time_remaining = osd_idle_timeout; cl->idle_time_remaining = osd_idle_timeout;
} }
} }
for (uint64_t client_id: clients_to_stop)
{
stop_client(client_id);
}
for (osd_op_t *op: ops_to_send)
{
outbox_push(op);
}
}); });
} }
@@ -165,9 +263,16 @@ osd_messenger_t::~osd_messenger_t()
} }
while (clients.size() > 0) while (clients.size() > 0)
{ {
stop_client(clients.begin()->first, true); stop_client(clients.begin()->first, true, true);
}
if (iothreads.size())
{
for (auto iot: iothreads)
{
delete iot;
}
iothreads.clear();
} }
destroy_iothreads();
#ifdef WITH_RDMA #ifdef WITH_RDMA
for (auto rdma_context: rdma_contexts) for (auto rdma_context: rdma_contexts)
{ {
@@ -184,6 +289,16 @@ osd_messenger_t::~osd_messenger_t()
rdmacm_evch = NULL; rdmacm_evch = NULL;
} }
#endif #endif
#ifdef WITH_OPENSSL
for (auto encrypt_ctx: encrypt_ctx_pool)
{
destroy_aes_xts_encrypt(encrypt_ctx);
}
for (auto decrypt_ctx: decrypt_ctx_pool)
{
destroy_aes_xts_decrypt(decrypt_ctx);
}
#endif
} }
void osd_messenger_t::parse_config(const json11::Json & config) void osd_messenger_t::parse_config(const json11::Json & config)
@@ -218,6 +333,9 @@ void osd_messenger_t::parse_config(const json11::Json & config)
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024) if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
this->rdma_max_msg = 129*1024; this->rdma_max_msg = 129*1024;
#endif #endif
this->max_aes_xts_pool_size = config["max_aes_xts_pool_size"].uint64_value();
if (!this->max_aes_xts_pool_size)
this->max_aes_xts_pool_size = 256;
if (!osd_num) if (!osd_num)
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value(); this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
else else
@@ -335,7 +453,7 @@ void osd_messenger_t::try_connect_peer(uint64_t peer_osd)
{ {
return; return;
} }
if (osd_peers.find(peer_osd) != osd_peers.end()) if (osd_peer_fds.find(peer_osd) != osd_peer_fds.end())
{ {
wanted_peers.erase(peer_osd); wanted_peers.erase(peer_osd);
return; return;
@@ -362,20 +480,20 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
#ifdef WITH_RDMACM #ifdef WITH_RDMACM
if (disable_tcp) if (disable_tcp)
{ {
on_connect_peer(peer_osd, -EINVAL, 0); on_connect_peer(peer_osd, -EINVAL);
return; return;
} }
#endif #endif
struct sockaddr_storage addr; struct sockaddr_storage addr;
if (!string_to_addr(peer_host, 0, peer_port, &addr)) if (!string_to_addr(peer_host, 0, peer_port, &addr))
{ {
on_connect_peer(peer_osd, -EINVAL, 0); on_connect_peer(peer_osd, -EINVAL);
return; return;
} }
int peer_fd = socket(addr.ss_family, SOCK_STREAM, 0); int peer_fd = socket(addr.ss_family, SOCK_STREAM, 0);
if (peer_fd < 0) if (peer_fd < 0)
{ {
on_connect_peer(peer_osd, -errno, 0); on_connect_peer(peer_osd, -errno);
return; return;
} }
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK); fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
@@ -383,25 +501,21 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
if (r < 0 && errno != EINPROGRESS) if (r < 0 && errno != EINPROGRESS)
{ {
close(peer_fd); close(peer_fd);
on_connect_peer(peer_osd, -errno, 0); on_connect_peer(peer_osd, -errno);
return; return;
} }
const uint64_t client_id = next_client_id++; clients[peer_fd] = new osd_client_t();
osd_client_t *cl = new osd_client_t();
if (log_level > 0) if (log_level > 0)
{ {
fprintf(stderr, "Connecting to OSD %ju at %s:%d (client %ju, FD %d)\n", peer_osd, peer_host, peer_port, client_id, peer_fd); fprintf(stderr, "Connecting to OSD %ju at %s:%d (client %d)\n", peer_osd, peer_host, peer_port, peer_fd);
} }
cl->client_id = client_id; clients[peer_fd]->peer_addr = addr;
cl->peer_addr = addr; clients[peer_fd]->peer_port = peer_port;
cl->peer_port = peer_port; clients[peer_fd]->peer_fd = peer_fd;
cl->peer_fd = peer_fd; clients[peer_fd]->peer_state = PEER_CONNECTING;
cl->peer_state = PEER_CONNECTING; clients[peer_fd]->connect_timeout_id = -1;
cl->connect_timeout_id = -1; clients[peer_fd]->osd_num = peer_osd;
cl->osd_num = peer_osd; clients[peer_fd]->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
cl->in_buf = malloc_or_die(receive_buffer_size);
clients[client_id] = cl;
clients_by_fd[peer_fd] = cl;
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events) tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
{ {
// Either OUT (connected) or HUP // Either OUT (connected) or HUP
@@ -409,11 +523,11 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
}); });
if (peer_connect_timeout > 0) if (peer_connect_timeout > 0)
{ {
cl->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, client_id](int timer_id) clients[peer_fd]->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, peer_fd](int timer_id)
{ {
osd_num_t peer_osd = clients.at(client_id)->osd_num; osd_num_t peer_osd = clients.at(peer_fd)->osd_num;
stop_client(client_id); stop_client(peer_fd, true);
on_connect_peer(peer_osd, -EPIPE, 0); on_connect_peer(peer_osd, -EPIPE);
return; return;
}); });
} }
@@ -421,7 +535,7 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
void osd_messenger_t::handle_connect_epoll(int peer_fd) void osd_messenger_t::handle_connect_epoll(int peer_fd)
{ {
auto cl = clients_by_fd.at(peer_fd); auto cl = clients[peer_fd];
if (cl->connect_timeout_id >= 0) if (cl->connect_timeout_id >= 0)
{ {
tfd->clear_timer(cl->connect_timeout_id); tfd->clear_timer(cl->connect_timeout_id);
@@ -436,8 +550,8 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
} }
if (result != 0) if (result != 0)
{ {
stop_client(cl->client_id); stop_client(peer_fd, true);
on_connect_peer(peer_osd, -result, 0); on_connect_peer(peer_osd, -result);
return; return;
} }
int one = 1; int one = 1;
@@ -454,23 +568,23 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events) void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
{ {
// Mark client as ready (i.e. some data is available) // Mark client as ready (i.e. some data is available)
auto cl = clients_by_fd.at(peer_fd);
if (epoll_events & EPOLLRDHUP) if (epoll_events & EPOLLRDHUP)
{ {
// Stop client // Stop client
if (log_level > 0) if (log_level > 0)
{ {
fprintf(stderr, "[OSD %ju] client %ju disconnected\n", this->osd_num, cl->client_id); fprintf(stderr, "[OSD %ju] client %d disconnected\n", this->osd_num, peer_fd);
} }
stop_client(cl->client_id); stop_client(peer_fd, true);
} }
else if (epoll_events & EPOLLIN) else if (epoll_events & EPOLLIN)
{ {
// Mark client as ready (i.e. some data is available) // Mark client as ready (i.e. some data is available)
auto cl = clients[peer_fd];
cl->read_ready++; cl->read_ready++;
if (cl->read_ready == 1) if (cl->read_ready == 1)
{ {
read_ready_clients.push_back(cl->client_id); read_ready_clients.push_back(cl->peer_fd);
if (ringloop) if (ringloop)
ringloop->wakeup(); ringloop->wakeup();
else else
@@ -479,13 +593,13 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
} }
} }
void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t client_id) void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int peer_fd)
{ {
auto & wp = wanted_peers.at(peer_osd); auto & wp = wanted_peers.at(peer_osd);
wp.connecting = false; wp.connecting = false;
if (errcode < 0) if (peer_fd < 0)
{ {
fprintf(stderr, "Failed to connect to peer OSD %ju address %s port %d: %s\n", peer_osd, wp.cur_addr.c_str(), wp.cur_port, strerror(-errcode)); fprintf(stderr, "Failed to connect to peer OSD %ju address %s port %d: %s\n", peer_osd, wp.cur_addr.c_str(), wp.cur_port, strerror(-peer_fd));
if (wp.address_changed) if (wp.address_changed)
{ {
wp.address_changed = false; wp.address_changed = false;
@@ -512,7 +626,7 @@ void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t
} }
if (log_level > 0) if (log_level > 0)
{ {
fprintf(stderr, "[OSD %ju] Connected with peer OSD %ju (client %ju)\n", osd_num, peer_osd, client_id); fprintf(stderr, "[OSD %ju] Connected with peer OSD %ju (client %d)\n", osd_num, peer_osd, peer_fd);
} }
wanted_peers.erase(peer_osd); wanted_peers.erase(peer_osd);
repeer_pgs(peer_osd); repeer_pgs(peer_osd);
@@ -522,7 +636,7 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
{ {
osd_op_t *op = new osd_op_t(); osd_op_t *op = new osd_op_t();
op->op_type = OSD_OP_OUT; op->op_type = OSD_OP_OUT;
op->client_id = cl->client_id; op->peer_fd = cl->peer_fd;
op->req = (osd_any_op_t){ op->req = (osd_any_op_t){
.show_conf = { .show_conf = {
.header = { .header = {
@@ -546,7 +660,7 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
if (!selected_ctx) if (!selected_ctx)
{ {
if (log_level > 0) if (log_level > 0)
fprintf(stderr, "No RDMA context for OSD %ju connection (client %ju), using only TCP\n", cl->osd_num, cl->client_id); fprintf(stderr, "No RDMA context for OSD %ju connection (peer %d), using only TCP\n", cl->osd_num, cl->peer_fd);
} }
else else
{ {
@@ -607,8 +721,8 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
if (err) if (err)
{ {
osd_num_t peer_osd = cl->osd_num; osd_num_t peer_osd = cl->osd_num;
stop_client(op->client_id); stop_client(op->peer_fd);
on_connect_peer(peer_osd, -EINVAL, 0); on_connect_peer(peer_osd, -EINVAL);
delete op; delete op;
return; return;
} }
@@ -638,13 +752,21 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
fprintf(stderr, "Connected to OSD %ju using RDMA\n", cl->osd_num); fprintf(stderr, "Connected to OSD %ju using RDMA\n", cl->osd_num);
} }
cl->peer_state = PEER_RDMA; cl->peer_state = PEER_RDMA;
tfd->set_fd_handler(cl->peer_fd, false, [this](int peer_fd, int epoll_events)
{
// Do not miss the disconnection!
if (epoll_events & EPOLLRDHUP)
{
handle_peer_epoll(peer_fd, epoll_events);
}
});
// Add the initial receive request // Add the initial receive request
init_recv_rdma(cl); init_recv_rdma(cl);
} }
} }
#endif #endif
osd_peers[cl->osd_num] = cl; osd_peer_fds[cl->osd_num] = cl->peer_fd;
on_connect_peer(cl->osd_num, 0, cl->client_id); on_connect_peer(cl->osd_num, cl->peer_fd);
delete op; delete op;
}; };
outbox_push(op); outbox_push(op);
@@ -659,23 +781,20 @@ void osd_messenger_t::accept_connections(int listen_fd)
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0) while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
{ {
assert(peer_fd != 0); assert(peer_fd != 0);
const uint64_t client_id = next_client_id++; fprintf(stderr, "[OSD %ju] new client %d: connection from %s\n", this->osd_num, peer_fd,
fprintf(stderr, "[OSD %ju] new client %ju (FD %d): connection from %s\n", this->osd_num, client_id, peer_fd,
addr_to_string(addr).c_str()); addr_to_string(addr).c_str());
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK); fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
int one = 1; int one = 1;
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
auto cl = new osd_client_t(); auto cl = new osd_client_t();
cl->client_id = client_id; clients[peer_fd] = cl;
clients[cl->client_id] = cl;
clients_by_fd[peer_fd] = cl;
cl->is_incoming = true; cl->is_incoming = true;
cl->peer_addr = addr; cl->peer_addr = addr;
cl->peer_addr = addr; cl->peer_addr = addr;
cl->peer_port = ntohs(((sockaddr_in*)&addr)->sin_port); cl->peer_port = ntohs(((sockaddr_in*)&addr)->sin_port);
cl->peer_fd = peer_fd; cl->peer_fd = peer_fd;
cl->peer_state = PEER_CONNECTED; cl->peer_state = PEER_CONNECTED;
cl->in_buf = malloc_or_die(receive_buffer_size); cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
// Add FD to epoll // Add FD to epoll
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events) tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
{ {
+94 -32
View File
@@ -12,7 +12,6 @@
#include <deque> #include <deque>
#include <vector> #include <vector>
#include "../util/robin_hood.h"
#include "malloc_or_die.h" #include "malloc_or_die.h"
#include "json11/json11.hpp" #include "json11/json11.hpp"
#include "msgr_op.h" #include "msgr_op.h"
@@ -35,9 +34,6 @@
#define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024 #define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024
#define MSGR_SENDP_HDR 1
#define MSGR_SENDP_FREE 2
struct msgr_sendp_t struct msgr_sendp_t
{ {
osd_op_t *op; osd_op_t *op;
@@ -49,9 +45,15 @@ struct msgr_rdma_connection_t;
struct msgr_rdma_context_t; struct msgr_rdma_context_t;
#endif #endif
#ifdef WITH_OPENSSL
struct op_aes_xts_encrypt_t;
struct op_aes_xts_decrypt_t;
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
#endif
struct osd_client_t struct osd_client_t
{ {
uint64_t client_id = 0;
int refs = 0; int refs = 0;
sockaddr_storage peer_addr = {}; sockaddr_storage peer_addr = {};
@@ -65,20 +67,23 @@ struct osd_client_t
osd_num_t in_osd_num = 0; osd_num_t in_osd_num = 0;
bool is_incoming = false; bool is_incoming = false;
void *in_buf = NULL; uint8_t *in_buf = NULL;
#ifdef WITH_RDMA #ifdef WITH_RDMA
msgr_rdma_connection_t *rdma_conn = NULL; msgr_rdma_connection_t *rdma_conn = NULL;
#endif #endif
// Read state // Read state
op_aes_xts_decrypt_t *decrypt_ctx = NULL;
int read_ready = 0; int read_ready = 0;
osd_op_t *read_op = NULL; osd_op_t *read_op = NULL;
size_t read_op_size = 0;
size_t read_op_pos = 0;
size_t read_op_inline_decrypt_pos = 0;
iovec read_iov = { 0 }; iovec read_iov = { 0 };
msghdr read_msg = { 0 }; msghdr read_msg = { 0 };
int read_remaining = 0; std::vector<iovec> recv_list;
int read_state = 0; size_t recv_list_size = 0;
osd_op_buf_list_t recv_list;
uint64_t read_op_id = 1; uint64_t read_op_id = 1;
bool check_sequencing = false; bool check_sequencing = false;
bool enable_pg_locks = false; bool enable_pg_locks = false;
@@ -87,17 +92,22 @@ struct osd_client_t
std::vector<osd_op_t*> received_ops; std::vector<osd_op_t*> received_ops;
// Outbound operations // Outbound operations
robin_hood::unordered_flat_map<uint64_t, osd_op_t*> sent_ops; std::map<uint64_t, osd_op_t*> sent_ops;
uint64_t send_op_id = 0; uint64_t send_op_id = 0;
// PGs dirtied by this client's primary-writes // PGs dirtied by this client's primary-writes
std::set<pool_pg_num_t> dirty_pgs; std::set<pool_pg_num_t> dirty_pgs;
// Write state // Write state
op_aes_xts_encrypt_t *encrypt_ctx = NULL;
std::deque<osd_op_t *> write_ops;
osd_op_t *write_op = NULL;
size_t write_op_pos = 0;
msghdr write_msg = { 0 }; msghdr write_msg = { 0 };
int write_state = 0; int write_state = 0;
std::vector<iovec> send_list, next_send_list; std::vector<iovec> send_list;
std::vector<msgr_sendp_t> outbox, next_outbox; size_t send_list_size = 0;
std::deque<osd_op_t*> send_free_ops;
std::vector<osd_op_t*> zc_free_list; std::vector<osd_op_t*> zc_free_list;
~osd_client_t(); ~osd_client_t();
@@ -129,7 +139,43 @@ struct osd_op_stats_t
uint64_t subop_stat_count[OSD_OP_MAX+1] = { 0 }; uint64_t subop_stat_count[OSD_OP_MAX+1] = { 0 };
}; };
#include <mutex>
#include <condition_variable>
#include <thread>
#ifdef __MOCK__
class msgr_iothread_t; class msgr_iothread_t;
#else
struct iothread_sqe_t
{
io_uring_sqe sqe;
ring_data_t data;
};
class msgr_iothread_t
{
protected:
ring_loop_t ring;
ring_loop_t *outer_loop = NULL;
ring_data_t *outer_loop_data = NULL;
int eventfd = -1;
bool stopped = false;
std::mutex mu;
std::condition_variable cond;
std::vector<iothread_sqe_t> queue;
std::thread thread;
void run();
public:
msgr_iothread_t();
~msgr_iothread_t();
void add_sqe(io_uring_sqe & sqe);
void stop();
void add_to_ringloop(ring_loop_t *outer_loop);
};
#endif
#ifdef WITH_RDMA #ifdef WITH_RDMA
struct rdma_event_channel; struct rdma_event_channel;
@@ -154,6 +200,7 @@ protected:
bool use_sync_send_recv = false; bool use_sync_send_recv = false;
int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE; int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE;
int iothread_count = 0; int iothread_count = 0;
int max_aes_xts_pool_size = 256;
#ifdef WITH_RDMA #ifdef WITH_RDMA
bool use_rdma = true; bool use_rdma = true;
@@ -167,27 +214,30 @@ protected:
uint64_t rdma_max_sge = 0, rdma_max_send = 0, rdma_max_recv = 0; uint64_t rdma_max_sge = 0, rdma_max_send = 0, rdma_max_recv = 0;
uint64_t rdma_max_msg = 0; uint64_t rdma_max_msg = 0;
rdma_event_channel *rdmacm_evch = NULL; rdma_event_channel *rdmacm_evch = NULL;
robin_hood::unordered_flat_map<rdma_cm_id*, osd_client_t*> rdmacm_connections; std::map<rdma_cm_id*, osd_client_t*> rdmacm_connections;
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting; std::map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
#endif #endif
std::vector<msgr_iothread_t*> iothreads; std::vector<msgr_iothread_t*> iothreads;
std::vector<uint64_t> read_ready_clients; std::vector<int> read_ready_clients;
std::vector<uint64_t> write_ready_clients; std::vector<int> write_ready_clients;
// We don't use ringloop->set_immediate here because we may have no ringloop in client :) // We don't use ringloop->set_immediate here because we may have no ringloop in client :)
std::deque<osd_op_t*> set_immediate_ops; std::vector<osd_op_t*> set_immediate_ops;
#ifdef WITH_OPENSSL
std::vector<op_aes_xts_encrypt_t*> encrypt_ctx_pool;
std::vector<op_aes_xts_decrypt_t*> decrypt_ctx_pool;
#endif
public: public:
timerfd_manager_t *tfd = NULL; timerfd_manager_t *tfd = NULL;
ring_loop_i *ringloop = NULL; ring_loop_t *ringloop = NULL;
bool has_sendmsg_zc = false; bool has_sendmsg_zc = false;
// osd_num_t is only for logging and asserts // osd_num_t is only for logging and asserts
uint64_t next_client_id = 1;
osd_num_t osd_num; osd_num_t osd_num;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> clients; std::map<int, osd_client_t*> clients;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> osd_peers; std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
robin_hood::unordered_flat_map<int, osd_client_t*> clients_by_fd; std::map<uint64_t, int> osd_peer_fds;
robin_hood::unordered_flat_map<osd_num_t, osd_wanted_peer_t> wanted_peers;
std::vector<std::string> osd_networks; std::vector<std::string> osd_networks;
std::vector<addr_mask_t> osd_network_masks; std::vector<addr_mask_t> osd_network_masks;
std::vector<std::string> osd_cluster_networks; std::vector<std::string> osd_cluster_networks;
@@ -198,11 +248,9 @@ public:
osd_op_stats_t stats, recovery_stats; osd_op_stats_t stats, recovery_stats;
void init(); void init();
void init_iothreads();
void parse_config(const json11::Json & config); void parse_config(const json11::Json & config);
void connect_peer(uint64_t osd_num, json11::Json peer_state); void connect_peer(uint64_t osd_num, json11::Json peer_state);
void stop_client(uint64_t client_id, bool force_delete = false); void stop_client(int peer_fd, bool force = false, bool force_delete = false);
void destroy_client(osd_client_t *cl);
void outbox_push(osd_op_t *cur_op); void outbox_push(osd_op_t *cur_op);
std::function<void(osd_op_t*)> exec_op; std::function<void(osd_op_t*)> exec_op;
std::function<void(osd_num_t)> repeer_pgs; std::function<void(osd_num_t)> repeer_pgs;
@@ -211,7 +259,6 @@ public:
void read_requests(); void read_requests();
void send_replies(); void send_replies();
void accept_connections(int listen_fd); void accept_connections(int listen_fd);
void destroy_iothreads();
~osd_messenger_t(); ~osd_messenger_t();
static json11::Json::object read_config(const json11::Json & config); static json11::Json::object read_config(const json11::Json & config);
@@ -222,7 +269,7 @@ public:
#ifdef WITH_RDMA #ifdef WITH_RDMA
bool is_rdma_enabled(); bool is_rdma_enabled();
bool connect_rdma(uint64_t client_id, std::string rdma_address, uint64_t client_max_msg); bool connect_rdma(int peer_fd, std::string rdma_address, uint64_t client_max_msg);
#endif #endif
#ifdef WITH_RDMACM #ifdef WITH_RDMACM
bool is_use_rdmacm(); bool is_use_rdmacm();
@@ -238,28 +285,43 @@ protected:
void try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_host, int peer_port); void try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_host, int peer_port);
void handle_peer_epoll(int peer_fd, int epoll_events); void handle_peer_epoll(int peer_fd, int epoll_events);
void handle_connect_epoll(int peer_fd); void handle_connect_epoll(int peer_fd);
void on_connect_peer(osd_num_t peer_osd, int errcode, uint64_t client_id); void on_connect_peer(osd_num_t peer_osd, int peer_fd);
void check_peer_config(osd_client_t *cl); void check_peer_config(osd_client_t *cl);
void cancel_osd_ops(osd_client_t *cl); void cancel_osd_ops(osd_client_t *cl);
void cancel_op(osd_op_t *op); void cancel_op(osd_op_t *op);
bool try_send(osd_client_t *cl); bool try_send(osd_client_t *cl);
void handle_send(int result, bool prev, bool more, osd_client_t *cl); void handle_send(int result, bool prev, bool more, osd_client_t *cl);
bool op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
size_t op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
void handle_read(int result, osd_client_t *cl);
bool handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
bool handle_hdr(osd_client_t *cl);
bool allocate_op_buffers(osd_client_t *cl);
bool allocate_reply_buffers(osd_client_t *cl, osd_op_t *op);
size_t op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done);
bool op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
void op_decrypt_start(osd_client_t* cl);
void op_decrypt_inline(osd_client_t* cl);
void op_decrypt_free(osd_client_t* cl);
size_t op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst);
void handle_finished_op(osd_client_t *cl);
bool handle_read(int result, osd_client_t *cl);
bool handle_read_buffer(osd_client_t *cl, void *curbuf, int remain);
bool handle_finished_read(osd_client_t *cl); bool handle_finished_read(osd_client_t *cl);
void handle_op_hdr(osd_client_t *cl); void handle_op_hdr(osd_client_t *cl);
bool handle_reply_hdr(osd_client_t *cl); bool handle_reply_hdr(osd_client_t *cl);
void handle_reply_ready(osd_op_t *op); void handle_reply_ready(osd_op_t *op);
void handle_immediate_ops(); void handle_immediate_ops();
void clear_immediate_ops(int peer_fd);
#ifdef WITH_RDMA #ifdef WITH_RDMA
void try_send_rdma(osd_client_t *cl); void try_send_rdma(osd_client_t *cl);
int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len);
bool init_recv_rdma(osd_client_t *cl); bool init_recv_rdma(osd_client_t *cl);
void handle_rdma_events(msgr_rdma_context_t *rdma_context); void handle_rdma_events(msgr_rdma_context_t *rdma_context);
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl); msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
void destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn);
#endif #endif
#ifdef WITH_RDMACM #ifdef WITH_RDMACM
void handle_rdmacm_events(); void handle_rdmacm_events();
+328
View File
@@ -0,0 +1,328 @@
// Copyright (c) Vitaliy Filippov, 2026+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#define _XOPEN_SOURCE
#include <limits.h>
#include <assert.h>
#include "etcd_state_client.h"
#include "messenger.h"
#include "msgr_encrypt.h"
// FIXME Fuck, no streaming...
op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
{
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
abort();
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_xts(), NULL, NULL, NULL) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
}
op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
{
EVP_CIPHER_CTX_free(ctx);
}
void op_aes_xts_encrypt_t::start(const uint8_t *key, uint64_t start_offset, size_t block_size)
{
this->start_offset = start_offset;
this->key = key;
this->block_size = block_size;
this->offset = 0;
}
void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
{
if (max_in > block_size - offset%block_size)
max_in = block_size - offset%block_size;
size_t insize = max_in;
size_t outsize = ((offset+insize)/16 - offset/16) * 16;
if (outsize > max_out)
{
// encrypt is used to send data through temporary buffer(s),
// so we don't care to support fragmenting output into < 16 b parts
insize = (max_out < 16 ? 0 : (max_out & ~15) - offset%16);
outsize = ((offset+insize)/16 - offset/16) * 16;
}
assert(insize <= max_in);
assert(outsize <= max_out);
if (!(offset % block_size))
{
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset + offset;
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
}
int actual_out = 0;
if (EVP_EncryptUpdate(ctx, out, &actual_out, in, insize) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == outsize);
done_in += insize;
done_out += outsize;
offset += insize;
}
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx)
{
delete encrypt_ctx;
}
op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
{
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
abort();
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_xts(), NULL, NULL, NULL) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
}
op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
{
EVP_CIPHER_CTX_free(ctx);
}
void op_aes_xts_decrypt_t::start(const uint8_t *key, uint64_t start_offset, size_t block_size)
{
this->start_offset = start_offset;
this->key = key;
this->block_size = block_size;
this->in_offset = 0;
this->tmp_pos = 16;
}
void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
{
if (max_in > block_size - in_offset%block_size)
max_in = block_size - in_offset%block_size;
int actual_out = 0;
// Write previously buffered block to support small output buffers
if (tmp_pos < 16)
{
size_t tmp_size = 16-tmp_pos;
if (tmp_size > max_out)
tmp_size = max_out;
memcpy(out, tmp_buf+tmp_pos, tmp_size);
tmp_pos += tmp_size;
done_out += tmp_size;
out += tmp_size;
max_out -= tmp_size;
if (!max_out)
return;
assert(tmp_pos == 16);
}
if (!(in_offset % block_size))
{
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset+in_offset;
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
}
size_t insize = max_in;
size_t outsize = ((in_offset+insize)/16 - in_offset/16) * 16;
if (outsize > max_out)
{
if (max_out < 16)
{
// We can only decrypt a partial block
insize = 16 - in_offset%16;
if (EVP_DecryptUpdate(ctx, tmp_buf, &actual_out, in, insize) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == 16);
in_offset += insize;
in += insize;
max_in -= insize;
tmp_pos = 0;
return;
}
// Otherwise, we can decrypt at least some data into <out> directly
insize = (max_out & ~15) - (in_offset % 16);
outsize = (max_out & ~15);
assert(insize < max_in);
}
if (EVP_DecryptUpdate(ctx, out, &actual_out, in, insize) != 1)
{
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == outsize);
in_offset += insize;
done_in += insize;
done_out += actual_out;
}
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx)
{
delete decrypt_ctx;
}
bool osd_messenger_t::op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
{
auto op = cl->write_op;
auto & op_pos = cl->write_op_pos;
assert(op->req.hdr.opcode == OSD_OP_WRITE);
if (!from)
{
if (!cl->encrypt_ctx)
{
if (encrypt_ctx_pool.size())
{
cl->encrypt_ctx = encrypt_ctx_pool.back();
encrypt_ctx_pool.pop_back();
}
else
cl->encrypt_ctx = new op_aes_xts_encrypt_t();
}
assert(op->enc->key.size() == 512/8);
cl->encrypt_ctx->start(op->enc->key.data(), op->req.rw.offset, op->enc->bitmap_granularity);
}
for (int i = 0; i < op->iov.count; i++)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len)
{
size_t done_in = 0;
size_t done_out = 0;
cl->encrypt_ctx->update(plain+from, plain_len-from, enc_buf+done, enc_len-done, done_in, done_out);
done += done_out;
op_pos += done_in;
from += done_in;
if (!done_in)
return false;
}
from -= plain_len;
}
if (cl->encrypt_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->encrypt_ctx;
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
}
return true;
}
bool osd_messenger_t::op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
{
op_decrypt_start(cl);
auto op = cl->read_op;
auto & op_pos = cl->read_op_pos;
assert(op->req.hdr.opcode == OSD_OP_READ);
uint64_t offset = from;
for (int i = 0; i < op->iov.count; i++)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len)
{
size_t done_in = 0;
size_t done_out = 0;
cl->decrypt_ctx->update(enc_buf+done, enc_len-done, plain+from, plain_len-from, done_in, done_out);
done += done_in;
offset += done_in;
op_pos += done_out;
from += done_out;
if (!done_in)
return false;
}
from -= plain_len;
}
op_decrypt_free(cl);
return true;
}
void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
{
if (!cl->decrypt_ctx)
{
if (decrypt_ctx_pool.size())
{
cl->decrypt_ctx = decrypt_ctx_pool.back();
decrypt_ctx_pool.pop_back();
}
else
cl->decrypt_ctx = new op_aes_xts_decrypt_t();
assert(cl->read_op->enc->key.size() == 512/8);
cl->decrypt_ctx->start(cl->read_op->enc->key.data(), cl->read_op->req.rw.offset, cl->read_op->enc->bitmap_granularity);
}
}
void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
{
op_decrypt_start(cl);
osd_op_t *op = cl->read_op;
size_t from_in = cl->read_op_inline_decrypt_pos - OSD_PACKET_SIZE - op->reply.rw.bitmap_len;
int i = 0;
while (i < op->iov.count && from_in >= op->iov.buf[i].iov_len)
{
from_in -= op->iov.buf[i].iov_len;
i++;
}
size_t from_out = from_in;
int j = i;
while (i < op->iov.count && j < op->iov.count)
{
uint8_t *in = (uint8_t*)op->iov.buf[i].iov_base + from_in;
size_t in_len = op->iov.buf[i].iov_len - from_in;
uint8_t *out = (uint8_t*)op->iov.buf[j].iov_base + from_out;
size_t out_len = op->iov.buf[j].iov_len - from_out;
size_t done_in = 0;
size_t done_out = 0;
cl->decrypt_ctx->update(in, in_len, out, out_len, done_in, done_out);
if (done_in >= in_len)
{
i++;
from_in = 0;
}
else
from_in += done_in;
if (done_out >= out_len)
{
j++;
from_out = 0;
}
else
from_out += done_out;
}
assert(j >= op->iov.count);
op_decrypt_free(cl);
}
void osd_messenger_t::op_decrypt_free(osd_client_t* cl)
{
if (cl->decrypt_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->decrypt_ctx;
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
}
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) Vitaliy Filippov, 2026+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <stdint.h>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
struct op_aes_xts_encrypt_t
{
EVP_CIPHER_CTX *ctx = NULL;
uint64_t start_offset = 0;
const uint8_t *key = NULL;
size_t offset = 0;
size_t block_size = 0;
op_aes_xts_encrypt_t();
~op_aes_xts_encrypt_t();
void start(const uint8_t *key, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
struct op_aes_xts_decrypt_t
{
EVP_CIPHER_CTX *ctx = NULL;
uint64_t start_offset = 0;
const uint8_t *key = NULL;
uint8_t tmp_buf[16];
size_t tmp_pos = 16;
size_t in_offset = 0;
size_t block_size = 0;
op_aes_xts_decrypt_t();
~op_aes_xts_decrypt_t();
void start(const uint8_t *key, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
-129
View File
@@ -1,129 +0,0 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <stdexcept>
#include <sys/poll.h>
#include <unistd.h>
#include "messenger.h"
#include "msgr_iothread.h"
msgr_iothread_t::msgr_iothread_t():
ring(RINGLOOP_DEFAULT_SIZE, true),
thread(&msgr_iothread_t::run, this)
{
eventfd = ring.register_eventfd();
if (eventfd < 0)
{
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
}
}
msgr_iothread_t::~msgr_iothread_t()
{
stop();
}
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
{
mu.lock();
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
if (queue.size() == 1)
{
cond.notify_all();
}
mu.unlock();
}
void msgr_iothread_t::stop()
{
mu.lock();
if (stopped)
{
mu.unlock();
return;
}
stopped = true;
if (outer_loop_data)
{
outer_loop_data->callback = [](ring_data_t*){};
}
cond.notify_all();
close(eventfd);
mu.unlock();
thread.join();
}
void msgr_iothread_t::add_to_ringloop(ring_loop_i *outer_loop)
{
assert(!this->outer_loop || this->outer_loop == outer_loop);
io_uring_sqe *sqe = outer_loop->get_sqe();
assert(sqe != NULL);
this->outer_loop = outer_loop;
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
outer_loop_data->callback = [this](ring_data_t *data)
{
if (data->res < 0)
{
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
}
outer_loop_data = NULL;
if (stopped)
{
return;
}
add_to_ringloop(this->outer_loop);
ring.loop();
};
}
void msgr_iothread_t::run()
{
while (true)
{
{
std::unique_lock<std::mutex> lk(mu);
while (!stopped && !queue.size())
cond.wait(lk);
if (stopped)
return;
int i = 0;
for (; i < queue.size(); i++)
{
io_uring_sqe *sqe = ring.get_sqe();
if (!sqe)
break;
ring_data_t *data = ((ring_data_t*)sqe->user_data);
*data = std::move(queue[i].data);
*sqe = queue[i].sqe;
sqe->user_data = (uint64_t)data;
}
queue.erase(queue.begin(), queue.begin()+i);
}
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
ring.submit();
}
}
void osd_messenger_t::init_iothreads()
{
for (int i = 0; i < iothread_count; i++)
{
auto iot = new msgr_iothread_t();
iothreads.push_back(iot);
iot->add_to_ringloop(ringloop);
}
}
void osd_messenger_t::destroy_iothreads()
{
if (iothreads.size())
{
for (auto iot: iothreads)
{
delete iot;
}
iothreads.clear();
}
}
-38
View File
@@ -1,38 +0,0 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <mutex>
#include <condition_variable>
#include <thread>
#include "ringloop.h"
struct iothread_sqe_t
{
io_uring_sqe sqe;
ring_data_t data;
};
class msgr_iothread_t
{
protected:
ring_loop_t ring;
ring_loop_i *outer_loop = NULL;
ring_data_t *outer_loop_data = NULL;
int eventfd = -1;
bool stopped = false;
std::mutex mu;
std::condition_variable cond;
std::vector<iothread_sqe_t> queue;
std::thread thread;
void run();
public:
msgr_iothread_t();
~msgr_iothread_t();
void add_sqe(io_uring_sqe & sqe);
void stop();
void add_to_ringloop(ring_loop_i *outer_loop);
};
+4
View File
@@ -23,6 +23,10 @@ osd_op_t::~osd_op_t()
// So we don't reuse it, but free it every time // So we don't reuse it, but free it every time
free(buf); free(buf);
} }
if (enc_buf)
{
free(enc_buf);
}
} }
bool osd_op_t::is_recovery_related() bool osd_op_t::is_recovery_related()
+7 -2
View File
@@ -3,6 +3,8 @@
#pragma once #pragma once
#include <memory>
#include <sys/uio.h> #include <sys/uio.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@@ -152,12 +154,13 @@ struct blockstore_op_t;
struct osd_primary_op_data_t; struct osd_primary_op_data_t;
struct inode_enc_t;
struct __attribute__((visibility("default"))) osd_op_t struct __attribute__((visibility("default"))) osd_op_t
{ {
timespec tv_begin = { 0 }, tv_end = { 0 }; timespec tv_begin = { 0 }, tv_end = { 0 };
uint64_t op_type = OSD_OP_IN; uint64_t op_type = OSD_OP_IN;
uint64_t client_id = 0; int peer_fd;
osd_num_t osd_num = 0;
osd_any_op_t req; osd_any_op_t req;
osd_any_reply_t reply; osd_any_reply_t reply;
blockstore_op_t *bs_op = NULL; blockstore_op_t *bs_op = NULL;
@@ -168,6 +171,8 @@ struct __attribute__((visibility("default"))) osd_op_t
unsigned bmp_data = 0; unsigned bmp_data = 0;
void *bitmap_buf = NULL; void *bitmap_buf = NULL;
void *rmw_buf = NULL; void *rmw_buf = NULL;
std::shared_ptr<inode_enc_t> enc;
uint8_t *enc_buf = NULL;
osd_primary_op_data_t* op_data = NULL; osd_primary_op_data_t* op_data = NULL;
std::function<void(osd_op_t*)> callback; std::function<void(osd_op_t*)> callback;
+40 -97
View File
@@ -187,8 +187,6 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
ibv_device **raw_dev_list = NULL; ibv_device **raw_dev_list = NULL;
ibv_device **dev_list = NULL; ibv_device **dev_list = NULL;
ibv_device *single_list[2] = {}; ibv_device *single_list[2] = {};
int up_ports = 0;
int single_port_num = 0;
raw_dev_list = dev_list = ibv_get_device_list(NULL); raw_dev_list = dev_list = ibv_get_device_list(NULL);
if (!dev_list || !*dev_list) if (!dev_list || !*dev_list)
@@ -223,7 +221,6 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
dev_list = single_list; dev_list = single_list;
} }
retry:
for (int i = 0; dev_list[i]; ++i) for (int i = 0; dev_list[i]; ++i)
{ {
auto dev = dev_list[i]; auto dev = dev_list[i];
@@ -261,9 +258,6 @@ retry:
fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index); fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index);
continue; continue;
} }
up_ports++;
single_port_num = port_num;
single_list[0] = dev;
uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu); uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu);
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT #ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
if (sel_gid_index < 0) if (sel_gid_index < 0)
@@ -304,14 +298,6 @@ cleanup_dev:
ibv_close_device(context); ibv_close_device(context);
} }
if (!ret.size() && up_ports == 1 && dev_list != single_list)
{
// Auto-select the only available device/port if there is only one
dev_list = single_list;
sel_port_num = single_port_num;
goto retry;
}
cleanup: cleanup:
if (raw_dev_list) if (raw_dev_list)
ibv_free_device_list(raw_dev_list); ibv_free_device_list(raw_dev_list);
@@ -507,7 +493,7 @@ int msgr_rdma_connection_t::connect(msgr_rdma_address_t *dest)
return 0; return 0;
} }
bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address, uint64_t client_max_msg) bool osd_messenger_t::connect_rdma(int peer_fd, std::string rdma_address, uint64_t client_max_msg)
{ {
// Try to connect to the peer using RDMA // Try to connect to the peer using RDMA
msgr_rdma_address_t addr; msgr_rdma_address_t addr;
@@ -517,12 +503,12 @@ bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address,
{ {
client_max_msg = rdma_max_msg; client_max_msg = rdma_max_msg;
} }
auto cl = clients.at(client_id); auto cl = clients.at(peer_fd);
msgr_rdma_context_t *selected_ctx = choose_rdma_context(cl); msgr_rdma_context_t *selected_ctx = choose_rdma_context(cl);
if (!selected_ctx) if (!selected_ctx)
{ {
if (log_level > 0) if (log_level > 0)
fprintf(stderr, "No RDMA context for peer %ju, using only TCP\n", client_id); fprintf(stderr, "No RDMA context for peer %d, using only TCP\n", cl->peer_fd);
return false; return false;
} }
msgr_rdma_connection_t *rdma_conn = msgr_rdma_connection_t::create(selected_ctx, rdma_max_send, rdma_max_recv, rdma_max_sge, client_max_msg); msgr_rdma_connection_t *rdma_conn = msgr_rdma_connection_t::create(selected_ctx, rdma_max_send, rdma_max_recv, rdma_max_sge, client_max_msg);
@@ -533,13 +519,14 @@ bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address,
{ {
delete rdma_conn; delete rdma_conn;
fprintf( fprintf(
stderr, "Failed to connect RDMA queue pair to %s (client %ju)\n", stderr, "Failed to connect RDMA queue pair to %s (client %d)\n",
addr.to_string().c_str(), client_id addr.to_string().c_str(), peer_fd
); );
} }
else else
{ {
// Remember connection, but switch to RDMA only after sending the configuration response // Remember connection, but switch to RDMA only after sending the configuration response
auto cl = clients.at(peer_fd);
cl->rdma_conn = rdma_conn; cl->rdma_conn = rdma_conn;
cl->peer_state = PEER_RDMA_CONNECTING; cl->peer_state = PEER_RDMA_CONNECTING;
return true; return true;
@@ -553,7 +540,7 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
{ {
ibv_send_wr *bad_wr = NULL; ibv_send_wr *bad_wr = NULL;
ibv_send_wr wr = { ibv_send_wr wr = {
.wr_id = cl->client_id, .wr_id = (uint64_t)(cl->peer_fd*2+1),
.sg_list = sge, .sg_list = sge,
.num_sge = op_sge, .num_sge = op_sge,
.opcode = IBV_WR_SEND, .opcode = IBV_WR_SEND,
@@ -568,23 +555,28 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
cl->rdma_conn->cur_send++; cl->rdma_conn->cur_send++;
} }
static int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len) int osd_messenger_t::try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
{ {
auto rc = cl->rdma_conn;
int total_dst_len = dst_len; int total_dst_len = dst_len;
while (dst_len > 0 && rc->send_pos < cl->send_list.size()) while (dst_len > 0 && cl->write_ops.size())
{ {
iovec & iov = cl->send_list[rc->send_pos]; if (!cl->write_op)
uint32_t len = (uint32_t)(iov.iov_len-rc->send_buf_pos < dst_len
? iov.iov_len-rc->send_buf_pos : dst_len);
memcpy(dst, (uint8_t*)iov.iov_base+rc->send_buf_pos, len);
dst += len;
dst_len -= len;
rc->send_buf_pos += len;
if (rc->send_buf_pos >= iov.iov_len)
{ {
rc->send_pos++; cl->write_op = cl->write_ops.front();
rc->send_buf_pos = 0; cl->write_ops.pop_front();
}
osd_op_t *op = cl->write_op;
size_t copied = op_copy_to(cl, dst, dst_len);
if (!copied)
{
break;
}
dst += copied;
dst_len -= copied;
if (!cl->write_op && op->op_type == OSD_OP_IN)
{
// this is a reply, free the op after sending it
cl->send_free_ops.push_back(op);
} }
} }
return total_dst_len-dst_len; return total_dst_len-dst_len;
@@ -612,9 +604,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send) while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send)
{ {
dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos; dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos;
dst_len = (rc->send_out_pos >= rc->send_done_pos dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
? rc->send_out_size-rc->send_out_pos
: rc->send_done_pos-rc->send_out_pos);
if (dst_len > rc->max_msg) if (dst_len > rc->max_msg)
dst_len = rc->max_msg; dst_len = rc->max_msg;
copied = try_send_rdma_copy(cl, dst, dst_len); copied = try_send_rdma_copy(cl, dst, dst_len);
@@ -624,7 +614,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
if (rc->send_out_pos == rc->send_out_size) if (rc->send_out_pos == rc->send_out_size)
rc->send_out_pos = 0; rc->send_out_pos = 0;
assert(rc->send_out_pos < rc->send_out_size); assert(rc->send_out_pos < rc->send_out_size);
if (rc->send_out_pos == rc->send_done_pos) if (rc->send_out_pos >= rc->send_done_pos)
rc->send_out_full = true; rc->send_out_full = true;
ibv_sge sge = { ibv_sge sge = {
.addr = (uintptr_t)dst, .addr = (uintptr_t)dst,
@@ -632,7 +622,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
.lkey = rc->send_out.mr->lkey, .lkey = rc->send_out.mr->lkey,
}; };
try_send_rdma_wr(cl, &sge, 1); try_send_rdma_wr(cl, &sge, 1);
rc->send_sizes.push_back(copied); cl->send_free_ops.push_back(NULL); // end marker
} }
} }
} }
@@ -646,7 +636,7 @@ static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
}; };
ibv_recv_wr *bad_wr = NULL; ibv_recv_wr *bad_wr = NULL;
ibv_recv_wr wr = { ibv_recv_wr wr = {
.wr_id = cl->client_id, .wr_id = (uint64_t)(cl->peer_fd*2),
.sg_list = &sge, .sg_list = &sge,
.num_sge = 1, .num_sge = 1,
}; };
@@ -703,28 +693,25 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
event_count = ibv_poll_cq(rdma_context->cq, RDMA_EVENTS_AT_ONCE, wc); event_count = ibv_poll_cq(rdma_context->cq, RDMA_EVENTS_AT_ONCE, wc);
for (int i = 0; i < event_count; i++) for (int i = 0; i < event_count; i++)
{ {
uint64_t client_id = wc[i].wr_id; int client_id = wc[i].wr_id >> 1;
bool is_send = wc[i].opcode == IBV_WC_SEND; bool is_send = wc[i].wr_id & 1;
auto cl_it = clients.find(client_id); auto cl_it = clients.find(client_id);
if (cl_it == clients.end()) if (cl_it == clients.end())
{ {
continue; continue;
} }
osd_client_t *cl = cl_it->second; osd_client_t *cl = cl_it->second;
if (cl->peer_state == PEER_STOPPED)
{
continue;
}
auto rc = cl->rdma_conn; auto rc = cl->rdma_conn;
if (wc[i].status != IBV_WC_SUCCESS) if (wc[i].status != IBV_WC_SUCCESS)
{ {
fprintf(stderr, "RDMA work request failed for client %ju", client_id); fprintf(stderr, "RDMA work request failed for client %d", client_id);
if (cl->osd_num) if (cl->osd_num)
{ {
fprintf(stderr, " (OSD %ju)", cl->osd_num); fprintf(stderr, " (OSD %ju)", cl->osd_num);
} }
fprintf(stderr, " with status: %s, stopping client\n", ibv_wc_status_str(wc[i].status)); fprintf(stderr, " with status: %s, stopping client\n", ibv_wc_status_str(wc[i].status));
stop_client(client_id); stop_client(client_id);
clear_immediate_ops(client_id);
continue; continue;
} }
if (!is_send) if (!is_send)
@@ -735,6 +722,8 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
rc->cur_recv--; rc->cur_recv--;
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len)) if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
{ {
// handle_read_buffer may stop the client
clear_immediate_ops(client_id);
continue; continue;
} }
try_recv_rdma_wr(cl, rc->recv_buffers[rc->next_recv_buf]); try_recv_rdma_wr(cl, rc->recv_buffers[rc->next_recv_buf]);
@@ -743,67 +732,21 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
else else
{ {
rc->cur_send--; rc->cur_send--;
uint64_t sent_size = rc->send_sizes.at(0); uint64_t sent_size = wc[i].byte_len;
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
rc->send_done_pos += sent_size; rc->send_done_pos += sent_size;
rc->send_out_full = false; rc->send_out_full = false;
if (rc->send_done_pos == rc->send_out_size) if (rc->send_done_pos == rc->send_out_size)
rc->send_done_pos = 0; rc->send_done_pos = 0;
assert(rc->send_done_pos < rc->send_out_size); assert(rc->send_done_pos < rc->send_out_size);
int send_pos = 0, send_buf_pos = 0; while (cl->send_free_ops.front())
while (sent_size > 0)
{ {
if (sent_size >= cl->send_list.at(send_pos).iov_len) delete cl->send_free_ops.front();
{ cl->send_free_ops.pop_front();
sent_size -= cl->send_list[send_pos].iov_len;
send_pos++;
}
else
{
send_buf_pos = sent_size;
sent_size = 0;
}
}
assert(rc->send_pos >= send_pos);
if (rc->send_pos == send_pos)
{
rc->send_buf_pos -= send_buf_pos;
}
rc->send_pos -= send_pos;
for (int i = 0; i < send_pos; i++)
{
if (cl->outbox[i].flags & MSGR_SENDP_FREE)
{
// Reply fully sent
delete cl->outbox[i].op;
}
}
if (send_pos > 0)
{
cl->send_list.erase(cl->send_list.begin(), cl->send_list.begin()+send_pos);
cl->outbox.erase(cl->outbox.begin(), cl->outbox.begin()+send_pos);
}
if (send_buf_pos > 0)
{
cl->send_list[0].iov_base = (uint8_t*)cl->send_list[0].iov_base + send_buf_pos;
cl->send_list[0].iov_len -= send_buf_pos;
} }
cl->send_free_ops.pop_front();
try_send_rdma(cl); try_send_rdma(cl);
} }
} }
} while (event_count > 0); } while (event_count > 0);
handle_immediate_ops(); handle_immediate_ops();
} }
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
{
if (rdma_conn->cmid)
{
auto rdma_it = rdmacm_connections.find(rdma_conn->cmid);
if (rdma_it != rdmacm_connections.end() && rdma_it->second->rdma_conn == rdma_conn)
{
rdmacm_connections.erase(rdma_it);
}
}
delete rdma_conn;
}
+3 -2
View File
@@ -10,6 +10,8 @@
#include <vector> #include <vector>
#include "addr_util.h" #include "addr_util.h"
struct osd_op_t;
struct msgr_rdma_address_t struct msgr_rdma_address_t
{ {
ibv_gid gid; ibv_gid gid;
@@ -72,9 +74,8 @@ struct msgr_rdma_connection_t
int cur_send = 0, cur_recv = 0; int cur_send = 0, cur_recv = 0;
int send_pos = 0, send_buf_pos = 0; int send_pos = 0, send_buf_pos = 0;
int next_recv_buf = 0; int next_recv_buf = 0;
std::vector<void*> recv_buffers; std::vector<uint8_t*> recv_buffers;
msgr_rdma_buf_t recv_buf; msgr_rdma_buf_t recv_buf;
std::vector<uint64_t> send_sizes;
msgr_rdma_buf_t send_out; msgr_rdma_buf_t send_out;
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0; int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
bool send_out_full = false; bool send_out_full = false;
+33 -13
View File
@@ -11,7 +11,7 @@
struct rdmacm_connecting_t struct rdmacm_connecting_t
{ {
rdma_cm_id *cmid = NULL; rdma_cm_id *cmid = NULL;
uint64_t client_id = 0; int peer_fd = -1;
osd_num_t peer_osd = 0; osd_num_t peer_osd = 0;
std::string addr; std::string addr;
sockaddr_storage parsed_addr = {}; sockaddr_storage parsed_addr = {};
@@ -117,9 +117,9 @@ void osd_messenger_t::handle_rdmacm_events()
auto cli_it = rdmacm_connections.find(ev->id); auto cli_it = rdmacm_connections.find(ev->id);
if (cli_it != rdmacm_connections.end()) if (cli_it != rdmacm_connections.end())
{ {
fprintf(stderr, "Received %s event for client %ju, closing connection\n", fprintf(stderr, "Received %s event for peer %d, closing connection\n",
event_type_name, cli_it->second->client_id); event_type_name, cli_it->second->peer_fd);
stop_client(cli_it->second->client_id); stop_client(cli_it->second->peer_fd);
} }
else if (rdmacm_connecting.find(ev->id) != rdmacm_connecting.end()) else if (rdmacm_connecting.find(ev->id) != rdmacm_connecting.end())
{ {
@@ -265,6 +265,14 @@ msgr_rdma_context_t* osd_messenger_t::rdmacm_create_qp(rdma_cm_id *cmid)
void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev) void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev)
{ {
// Make a fake FD (FIXME: do not use FDs for identifying clients!)
int fake_fd = socket(AF_INET, SOCK_STREAM, 0);
if (fake_fd < 0)
{
fprintf(stderr, "Failed to allocate a fake socket for RDMA-CM client: %s (code %d)\n", strerror(errno), errno);
rdma_destroy_id(ev->id);
return;
}
auto rdma_context = rdmacm_create_qp(ev->id); auto rdma_context = rdmacm_create_qp(ev->id);
if (!rdma_context) if (!rdma_context)
{ {
@@ -289,12 +297,12 @@ void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev)
// Wait for RDMA_CM_ESTABLISHED, and enable the connection only after it // Wait for RDMA_CM_ESTABLISHED, and enable the connection only after it
auto conn = new rdmacm_connecting_t; auto conn = new rdmacm_connecting_t;
conn->cmid = ev->id; conn->cmid = ev->id;
conn->client_id = next_client_id++; conn->peer_fd = fake_fd;
conn->parsed_addr = *(sockaddr_storage*)rdma_get_peer_addr(ev->id); conn->parsed_addr = *(sockaddr_storage*)rdma_get_peer_addr(ev->id);
conn->rdma_context = rdma_context; conn->rdma_context = rdma_context;
rdmacm_set_conn_timeout(conn); rdmacm_set_conn_timeout(conn);
rdmacm_connecting[ev->id] = conn; rdmacm_connecting[ev->id] = conn;
fprintf(stderr, "[OSD %ju] new client %ju: connection from %s via RDMA-CM\n", this->osd_num, conn->client_id, fprintf(stderr, "[OSD %ju] new client %d: connection from %s via RDMA-CM\n", this->osd_num, conn->peer_fd,
addr_to_string(conn->parsed_addr).c_str()); addr_to_string(conn->parsed_addr).c_str());
} }
@@ -324,6 +332,8 @@ void osd_messenger_t::rdmacm_on_connect_peer_error(rdma_cm_id *cmid, int res)
auto peer_osd = conn->peer_osd; auto peer_osd = conn->peer_osd;
if (conn->timeout_id >= 0) if (conn->timeout_id >= 0)
tfd->clear_timer(conn->timeout_id); tfd->clear_timer(conn->timeout_id);
if (conn->peer_fd >= 0)
close(conn->peer_fd);
if (conn->rdma_context) if (conn->rdma_context)
conn->rdma_context->reserve_cqe(-rdma_max_send-rdma_max_recv); conn->rdma_context->reserve_cqe(-rdma_max_send-rdma_max_recv);
if (conn->cmid) if (conn->cmid)
@@ -344,7 +354,7 @@ void osd_messenger_t::rdmacm_on_connect_peer_error(rdma_cm_id *cmid, int res)
else else
{ {
// TCP is disabled // TCP is disabled
on_connect_peer(peer_osd, res == 0 ? -EINVAL : (res > 0 ? -res : res), 0); on_connect_peer(peer_osd, res == 0 ? -EINVAL : (res > 0 ? -res : res));
} }
} }
} }
@@ -355,7 +365,7 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
if (!string_to_addr(addr, false, rdmacm_port, &sa)) if (!string_to_addr(addr, false, rdmacm_port, &sa))
{ {
fprintf(stderr, "Address %s is invalid\n", addr.c_str()); fprintf(stderr, "Address %s is invalid\n", addr.c_str());
on_connect_peer(peer_osd, -EINVAL, 0); on_connect_peer(peer_osd, -EINVAL);
return; return;
} }
rdma_cm_id *cmid = NULL; rdma_cm_id *cmid = NULL;
@@ -366,7 +376,17 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
if (!disable_tcp) if (!disable_tcp)
try_connect_peer_tcp(peer_osd, addr.c_str(), fallback_tcp_port); try_connect_peer_tcp(peer_osd, addr.c_str(), fallback_tcp_port);
else else
on_connect_peer(peer_osd, res, 0); on_connect_peer(peer_osd, res);
return;
}
// Make a fake FD (FIXME: do not use FDs for identifying clients!)
int fake_fd = socket(AF_INET, SOCK_STREAM, 0);
if (fake_fd < 0)
{
int res = -errno;
rdma_destroy_id(cmid);
// Can't create socket, pointless to try TCP
on_connect_peer(peer_osd, res);
return; return;
} }
if (log_level > 0) if (log_level > 0)
@@ -374,7 +394,7 @@ void osd_messenger_t::rdmacm_try_connect_peer(uint64_t peer_osd, const std::stri
auto conn = new rdmacm_connecting_t; auto conn = new rdmacm_connecting_t;
rdmacm_connecting[cmid] = conn; rdmacm_connecting[cmid] = conn;
conn->cmid = cmid; conn->cmid = cmid;
conn->client_id = next_client_id++; conn->peer_fd = fake_fd;
conn->peer_osd = peer_osd; conn->peer_osd = peer_osd;
conn->addr = addr; conn->addr = addr;
conn->parsed_addr = sa; conn->parsed_addr = sa;
@@ -491,13 +511,13 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
auto cl = new osd_client_t(); auto cl = new osd_client_t();
cl->peer_addr = conn->parsed_addr; cl->peer_addr = conn->parsed_addr;
cl->peer_port = conn->rdmacm_port; cl->peer_port = conn->rdmacm_port;
cl->client_id = conn->client_id; cl->peer_fd = conn->peer_fd;
cl->peer_state = PEER_RDMA; cl->peer_state = PEER_RDMA;
cl->connect_timeout_id = -1; cl->connect_timeout_id = -1;
cl->osd_num = peer_osd; cl->osd_num = peer_osd;
cl->in_buf = malloc_or_die(receive_buffer_size); cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
cl->rdma_conn = rc; cl->rdma_conn = rc;
clients[conn->client_id] = cl; clients[conn->peer_fd] = cl;
if (conn->timeout_id >= 0) if (conn->timeout_id >= 0)
{ {
tfd->clear_timer(conn->timeout_id); tfd->clear_timer(conn->timeout_id);
+423 -250
View File
@@ -1,22 +1,26 @@
// Copyright (c) Vitaliy Filippov, 2019+ // Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details) // License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#define _XOPEN_SOURCE
#include <limits.h>
#include "messenger.h" #include "messenger.h"
#include "msgr_iothread.h"
void osd_messenger_t::read_requests() void osd_messenger_t::read_requests()
{ {
for (int i = 0; i < read_ready_clients.size(); i++) for (int i = 0; i < read_ready_clients.size(); i++)
{ {
uint64_t client_id = read_ready_clients[i]; int peer_fd = read_ready_clients[i];
auto cl_it = clients.find(client_id); auto cl_it = clients.find(peer_fd);
if (cl_it == clients.end() || !cl_it->second || cl_it->second->read_msg.msg_iovlen || if (cl_it == clients.end() || !cl_it->second || cl_it->second->read_msg.msg_iovlen)
cl_it->second->peer_state != PEER_CONNECTED)
{ {
continue; continue;
} }
auto cl = cl_it->second; auto cl = cl_it->second;
if (cl->read_remaining < receive_buffer_size) if (cl->read_op && cl->read_op_size-(cl->read_op_pos-OSD_PACKET_SIZE) >= receive_buffer_size)
{
op_get_read_buffers(cl, cl->recv_list);
}
if (!cl->recv_list.size())
{ {
cl->read_iov.iov_base = cl->in_buf; cl->read_iov.iov_base = cl->in_buf;
cl->read_iov.iov_len = receive_buffer_size; cl->read_iov.iov_len = receive_buffer_size;
@@ -26,14 +30,15 @@ void osd_messenger_t::read_requests()
else else
{ {
cl->read_iov.iov_base = 0; cl->read_iov.iov_base = 0;
cl->read_iov.iov_len = cl->read_remaining; cl->read_iov.iov_len = 0;
cl->read_msg.msg_iov = cl->recv_list.get_iovec(); cl->read_msg.msg_iov = cl->recv_list.data();
cl->read_msg.msg_iovlen = cl->recv_list.get_size(); cl->read_msg.msg_iovlen = cl->recv_list.size();
} }
assert(!cl->read_op || cl->read_op_pos < OSD_PACKET_SIZE || cl->read_op_size >= (cl->read_op_pos-OSD_PACKET_SIZE));
cl->refs++; cl->refs++;
if (ringloop && !use_sync_send_recv) if (ringloop && !use_sync_send_recv)
{ {
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL; auto iothread = iothreads.size() ? iothreads[peer_fd % iothreads.size()] : NULL;
io_uring_sqe sqe_local; io_uring_sqe sqe_local;
ring_data_t data_local; ring_data_t data_local;
io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe()); io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe());
@@ -51,7 +56,7 @@ void osd_messenger_t::read_requests()
} }
ring_data_t* data = ((ring_data_t*)sqe->user_data); ring_data_t* data = ((ring_data_t*)sqe->user_data);
data->callback = [this, cl](ring_data_t *data) { handle_read(data->res, cl); }; data->callback = [this, cl](ring_data_t *data) { handle_read(data->res, cl); };
io_uring_prep_recvmsg(sqe, cl->peer_fd, &cl->read_msg, 0); io_uring_prep_recvmsg(sqe, peer_fd, &cl->read_msg, cl->recv_list.size() ? MSG_WAITALL : 0);
if (iothread) if (iothread)
{ {
iothread->add_sqe(sqe_local); iothread->add_sqe(sqe_local);
@@ -59,7 +64,7 @@ void osd_messenger_t::read_requests()
} }
else else
{ {
int result = recvmsg(cl->peer_fd, &cl->read_msg, 0); int result = recvmsg(peer_fd, &cl->read_msg, 0);
if (result < 0) if (result < 0)
{ {
result = -errno; result = -errno;
@@ -71,89 +76,110 @@ void osd_messenger_t::read_requests()
read_ready_clients.clear(); read_ready_clients.clear();
} }
bool osd_messenger_t::handle_read(int result, osd_client_t *cl) void osd_messenger_t::handle_read(int result, osd_client_t *cl)
{ {
bool ret = false; int peer_fd = cl->peer_fd;
cl->read_msg.msg_iovlen = 0;
cl->refs--; cl->refs--;
if (cl->peer_state == PEER_RDMA)
{
return true;
}
if (cl->peer_state == PEER_STOPPED) if (cl->peer_state == PEER_STOPPED)
{ {
if (cl->refs <= 0) if (cl->refs <= 0)
{ {
destroy_client(cl); delete cl;
} }
return false; return;
} }
if (result <= 0 && result != -EAGAIN && result != -EINTR) if (result <= 0 && result != -EAGAIN && result != -EINTR)
{ {
// this is a client socket, so don't panic on error. just disconnect it // this is a client socket, so don't panic on error. just disconnect it
if (result != 0) if (result != 0)
{ {
fprintf(stderr, "Client %ju socket read error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result)); fprintf(stderr, "Client %d socket read error: %d (%s). Disconnecting client\n", cl->peer_fd, -result, strerror(-result));
} }
stop_client(cl->client_id); stop_client(cl->peer_fd);
return false; return;
}
if (result == -EAGAIN || result == -EINTR || result < cl->read_iov.iov_len)
{
cl->read_ready--;
if (cl->read_ready > 0)
read_ready_clients.push_back(cl->client_id);
}
else
{
read_ready_clients.push_back(cl->client_id);
} }
bool full_read = false;
if (result > 0) if (result > 0)
{ {
if (cl->read_iov.iov_base == cl->in_buf) if (cl->read_iov.iov_base == cl->in_buf)
{ {
full_read = result >= cl->read_iov.iov_len;
if (!handle_read_buffer(cl, cl->in_buf, result)) if (!handle_read_buffer(cl, cl->in_buf, result))
{ {
clear_immediate_ops(peer_fd);
handle_immediate_ops(); handle_immediate_ops();
return false; return;
} }
} }
else else
{ {
// Reset OSD ping state
cl->ping_time_remaining = 0;
cl->idle_time_remaining = osd_idle_timeout;
// Long data // Long data
cl->read_remaining -= result; size_t i = 0;
cl->recv_list.eat(result); while (i < cl->recv_list.size() && result >= cl->recv_list[i].iov_len)
if (cl->recv_list.done >= cl->recv_list.count)
{ {
if (!handle_finished_read(cl)) result -= cl->recv_list[i].iov_len;
{ i++;
handle_immediate_ops(); }
return false; if (i < cl->recv_list.size())
} {
cl->recv_list[i].iov_base += result;
cl->recv_list[i].iov_len -= result;
}
else
{
full_read = true;
}
cl->recv_list.erase(cl->recv_list.begin(), cl->recv_list.begin()+i);
if (!cl->recv_list.size())
{
handle_finished_op(cl);
} }
} }
if (result >= cl->read_iov.iov_len) }
{ cl->read_msg.msg_iovlen = 0;
ret = true; if (result == -EAGAIN || result == -EINTR || !full_read)
} {
cl->read_ready--;
if (cl->read_ready > 0)
read_ready_clients.push_back(cl->peer_fd);
}
else
{
read_ready_clients.push_back(cl->peer_fd);
} }
handle_immediate_ops(); handle_immediate_ops();
return ret; }
void osd_messenger_t::clear_immediate_ops(int peer_fd)
{
size_t i = 0, j = 0;
while (i < set_immediate_ops.size())
{
if (set_immediate_ops[i]->peer_fd == peer_fd && set_immediate_ops[i]->op_type == OSD_OP_IN)
{
delete set_immediate_ops[i];
}
else
{
if (i != j)
set_immediate_ops[j] = set_immediate_ops[i];
j++;
}
i++;
}
set_immediate_ops.resize(j);
} }
void osd_messenger_t::handle_immediate_ops() void osd_messenger_t::handle_immediate_ops()
{ {
while (set_immediate_ops.size()) for (auto op: set_immediate_ops)
{ {
auto op = set_immediate_ops.front();
set_immediate_ops.pop_front();
if (op->op_type == OSD_OP_IN) if (op->op_type == OSD_OP_IN)
{ {
auto cl_it = clients.find(op->client_id); exec_op(op);
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_STOPPED)
exec_op(op);
else
delete op;
} }
else else
{ {
@@ -161,115 +187,98 @@ void osd_messenger_t::handle_immediate_ops()
std::function<void(osd_op_t*)>(op->callback)(op); std::function<void(osd_op_t*)>(op->callback)(op);
} }
} }
set_immediate_ops.clear();
} }
bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, void *curbuf, int remain) bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize)
{
// Compose operation(s) from the buffer
while (remain > 0)
{
if (!cl->read_op)
{
cl->read_op = new osd_op_t;
cl->read_op->client_id = cl->client_id;
cl->read_op->op_type = OSD_OP_IN;
cl->recv_list.push_back(cl->read_op->req.buf, OSD_PACKET_SIZE);
cl->read_remaining = OSD_PACKET_SIZE;
cl->read_state = CL_READ_HDR;
}
while (cl->recv_list.done < cl->recv_list.count && remain > 0)
{
iovec* cur = cl->recv_list.get_iovec();
if (cur->iov_len > remain)
{
memcpy(cur->iov_base, curbuf, remain);
cl->read_remaining -= remain;
cur->iov_len -= remain;
cur->iov_base = (uint8_t*)cur->iov_base + remain;
remain = 0;
}
else
{
memcpy(cur->iov_base, curbuf, cur->iov_len);
curbuf = (uint8_t*)curbuf + cur->iov_len;
cl->read_remaining -= cur->iov_len;
remain -= cur->iov_len;
cur->iov_len = 0;
cl->recv_list.done++;
}
}
if (cl->recv_list.done >= cl->recv_list.count)
{
if (!handle_finished_read(cl))
{
return false;
}
}
}
return true;
}
bool osd_messenger_t::handle_finished_read(osd_client_t *cl)
{ {
// Reset OSD ping state // Reset OSD ping state
cl->ping_time_remaining = 0; cl->ping_time_remaining = 0;
cl->idle_time_remaining = osd_idle_timeout; cl->idle_time_remaining = osd_idle_timeout;
cl->recv_list.reset(); // Compose operation(s) from the buffer
if (cl->read_state == CL_READ_HDR) size_t done = 0;
while (done < bufsize)
{ {
if (cl->read_op->req.hdr.magic == SECONDARY_OSD_REPLY_MAGIC) if (!cl->read_op)
return handle_reply_hdr(cl);
else if (cl->read_op->req.hdr.magic == SECONDARY_OSD_OP_MAGIC)
{ {
if (cl->check_sequencing) cl->read_op = new osd_op_t;
cl->read_op->peer_fd = cl->peer_fd;
cl->read_op->op_type = OSD_OP_IN;
cl->read_op_pos = 0;
cl->read_op_size = 0;
cl->read_op_inline_decrypt_pos = (size_t)-1;
}
if (cl->read_op_pos < OSD_PACKET_SIZE)
{
int len = OSD_PACKET_SIZE - cl->read_op_pos;
if (len > bufsize-done)
len = bufsize-done;
memcpy(cl->read_op->req.buf + cl->read_op_pos, curbuf+done, len);
done += len;
cl->read_op_pos += len;
if (cl->read_op_pos < OSD_PACKET_SIZE)
return true;
if (!handle_hdr(cl))
{ {
if (cl->read_op->req.hdr.id != cl->read_op_id) stop_client(cl->peer_fd);
{ return false;
fprintf(stderr, "Warning: operation sequencing is broken on client %ju: expected num %ju, got %ju, stopping client\n", cl->client_id, cl->read_op_id, cl->read_op->req.hdr.id);
stop_client(cl->client_id);
return false;
}
cl->read_op_id++;
} }
handle_op_hdr(cl);
} }
else op_copy_from(cl, curbuf, bufsize, done);
{
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from client %ju\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->client_id);
stop_client(cl->client_id);
return false;
}
}
else if (cl->read_state == CL_READ_DATA)
{
// Operation is ready
cl->received_ops.push_back(cl->read_op);
set_immediate_ops.push_back(cl->read_op);
cl->read_op = NULL;
cl->read_state = 0;
}
else if (cl->read_state == CL_READ_REPLY_DATA)
{
// Reply is ready
handle_reply_ready(cl->read_op);
cl->read_op = NULL;
cl->read_state = 0;
}
else
{
assert(0);
} }
return true; return true;
} }
void osd_messenger_t::handle_op_hdr(osd_client_t *cl) bool osd_messenger_t::handle_hdr(osd_client_t *cl)
{
if (cl->read_op->req.hdr.magic == SECONDARY_OSD_REPLY_MAGIC)
{
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id);
if (req_it == cl->sent_ops.end())
{
// Command out of sync. Drop connection
fprintf(stderr, "Client %d command out of sync: id %ju\n", cl->peer_fd, cl->read_op->req.hdr.id);
return false;
}
osd_op_t *op = req_it->second;
memcpy(op->reply.buf, cl->read_op->req.buf, OSD_PACKET_SIZE);
if (!allocate_reply_buffers(cl, op))
{
return false;
}
cl->sent_ops.erase(req_it);
delete cl->read_op;
cl->read_op = op;
}
else if (cl->read_op->req.hdr.magic == SECONDARY_OSD_OP_MAGIC)
{
if (cl->check_sequencing)
{
if (cl->read_op->req.hdr.id != cl->read_op_id)
{
fprintf(stderr, "Warning: operation sequencing is broken on client %d: expected num %ju, got %ju, stopping client\n", cl->peer_fd, cl->read_op_id, cl->read_op->req.hdr.id);
return false;
}
cl->read_op_id++;
}
if (!allocate_op_buffers(cl))
{
return false;
}
}
else
{
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from %d\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->peer_fd);
return false;
}
return true;
}
bool osd_messenger_t::allocate_op_buffers(osd_client_t *cl)
{ {
osd_op_t *cur_op = cl->read_op; osd_op_t *cur_op = cl->read_op;
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ) cl->read_op_size = 0;
{ if (cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
cl->read_remaining = 0;
}
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
{ {
if (cur_op->req.sec_rw.attr_len > 0) if (cur_op->req.sec_rw.attr_len > 0)
@@ -278,14 +287,12 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
cur_op->bitmap = cur_op->rmw_buf = malloc_or_die(cur_op->req.sec_rw.attr_len); cur_op->bitmap = cur_op->rmw_buf = malloc_or_die(cur_op->req.sec_rw.attr_len);
else else
cur_op->bitmap = &cur_op->bmp_data; cur_op->bitmap = &cur_op->bmp_data;
cl->recv_list.push_back(cur_op->bitmap, cur_op->req.sec_rw.attr_len);
} }
if (cur_op->req.sec_rw.len > 0) if (cur_op->req.sec_rw.len > 0)
{ {
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_rw.len); cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_rw.len);
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_rw.len);
} }
cl->read_remaining = cur_op->req.sec_rw.len + cur_op->req.sec_rw.attr_len; cl->read_op_size = cur_op->req.sec_rw.len + cur_op->req.sec_rw.attr_len;
} }
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE || else if (cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK) cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
@@ -293,27 +300,24 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
if (cur_op->req.sec_stab.len > 0) if (cur_op->req.sec_stab.len > 0)
{ {
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_stab.len); cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_stab.len);
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_stab.len);
} }
cl->read_remaining = cur_op->req.sec_stab.len; cl->read_op_size = cur_op->req.sec_stab.len;
} }
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP) else if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{ {
if (cur_op->req.sec_read_bmp.len > 0) if (cur_op->req.sec_read_bmp.len > 0)
{ {
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_read_bmp.len); cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_read_bmp.len);
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_read_bmp.len);
} }
cl->read_remaining = cur_op->req.sec_read_bmp.len; cl->read_op_size = cur_op->req.sec_read_bmp.len;
} }
else if (cur_op->req.hdr.opcode == OSD_OP_WRITE) else if (cur_op->req.hdr.opcode == OSD_OP_WRITE)
{ {
if (cur_op->req.rw.len > 0) if (cur_op->req.rw.len > 0)
{ {
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.rw.len); cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.rw.len);
cl->recv_list.push_back(cur_op->buf, cur_op->req.rw.len);
} }
cl->read_remaining = cur_op->req.rw.len; cl->read_op_size = cur_op->req.rw.len;
} }
else if (cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG) else if (cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
{ {
@@ -321,44 +325,15 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
{ {
cur_op->buf = malloc_or_die(cur_op->req.show_conf.json_len+1); cur_op->buf = malloc_or_die(cur_op->req.show_conf.json_len+1);
((uint8_t*)cur_op->buf)[cur_op->req.show_conf.json_len] = 0; ((uint8_t*)cur_op->buf)[cur_op->req.show_conf.json_len] = 0;
cl->recv_list.push_back(cur_op->buf, cur_op->req.show_conf.json_len);
} }
cl->read_remaining = cur_op->req.show_conf.json_len; cl->read_op_size = cur_op->req.show_conf.json_len;
}
/*else if (cur_op->req.hdr.opcode == OSD_OP_READ ||
cur_op->req.hdr.opcode == OSD_OP_SCRUB ||
cur_op->req.hdr.opcode == OSD_OP_DESCRIBE)
{
cl->read_remaining = 0;
}*/
if (cl->read_remaining > 0)
{
// Read data
cl->read_state = CL_READ_DATA;
}
else
{
// Operation is ready
cl->received_ops.push_back(cur_op);
set_immediate_ops.push_back(cur_op);
cl->read_op = NULL;
cl->read_state = 0;
} }
return true;
} }
bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl) bool osd_messenger_t::allocate_reply_buffers(osd_client_t *cl, osd_op_t *op)
{ {
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id); cl->read_op_size = 0;
if (req_it == cl->sent_ops.end() || req_it->second->req.hdr.opcode != cl->read_op->req.hdr.opcode)
{
// Command out of sync. Drop connection
fprintf(stderr, "Client %ju command out of sync: id %ju\n", cl->client_id, cl->read_op->req.hdr.id);
stop_client(cl->client_id);
return false;
}
osd_op_t *op = req_it->second;
memcpy(op->reply.buf, cl->read_op->req.buf, OSD_PACKET_SIZE);
cl->sent_ops.erase(req_it);
if (op->reply.hdr.opcode == OSD_OP_SEC_READ || op->reply.hdr.opcode == OSD_OP_READ) if (op->reply.hdr.opcode == OSD_OP_SEC_READ || op->reply.hdr.opcode == OSD_OP_READ)
{ {
// Read data. In this case we assume that the buffer is preallocated by the caller (!) // Read data. In this case we assume that the buffer is preallocated by the caller (!)
@@ -367,99 +342,297 @@ bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
if (op->reply.hdr.retval >= 0 && (op->reply.hdr.retval != expected_size || bmp_len > op->bitmap_len)) if (op->reply.hdr.retval >= 0 && (op->reply.hdr.retval != expected_size || bmp_len > op->bitmap_len))
{ {
// Check reply length to not overflow the buffer // Check reply length to not overflow the buffer
fprintf(stderr, "Client %ju read reply of different length: expected %u+%u, got %jd+%u\n", fprintf(stderr, "Client %d read reply of different length: expected %u+%u, got %jd+%u\n",
cl->client_id, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len); cl->peer_fd, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
cl->sent_ops[op->req.hdr.id] = op;
stop_client(cl->client_id);
return false; return false;
} }
if (op->reply.hdr.retval >= 0 && bmp_len > 0) if (bmp_len > 0)
{ {
assert(op->bitmap); assert(op->bitmap);
cl->recv_list.push_back(op->bitmap, bmp_len); cl->read_op_size += bmp_len;
cl->read_remaining += bmp_len;
} }
if (op->reply.hdr.retval > 0) if (op->reply.hdr.retval > 0)
{ {
assert(op->iov.count > 0); assert(op->iov.count > 0);
cl->recv_list.append(op->iov); cl->read_op_size += op->reply.hdr.retval;
cl->read_remaining += op->reply.hdr.retval;
} }
if (cl->read_remaining == 0)
{
goto reuse;
}
delete cl->read_op;
cl->read_op = op;
cl->read_state = CL_READ_REPLY_DATA;
} }
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0) else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
{ {
assert(!op->iov.count); assert(!op->iov.count);
delete cl->read_op; cl->read_op_size = sizeof(obj_ver_id) * op->reply.hdr.retval;
cl->read_op = op; op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_op_size);
cl->read_state = CL_READ_REPLY_DATA;
cl->read_remaining = sizeof(obj_ver_id) * op->reply.hdr.retval;
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_remaining);
cl->recv_list.push_back(op->buf, cl->read_remaining);
} }
else if (op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP && op->reply.hdr.retval > 0) else if (op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP && op->reply.hdr.retval > 0)
{ {
assert(!op->iov.count); assert(!op->iov.count);
delete cl->read_op; cl->read_op_size = op->reply.hdr.retval;
cl->read_op = op;
cl->read_state = CL_READ_REPLY_DATA;
cl->read_remaining = op->reply.hdr.retval;
free(op->buf); free(op->buf);
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_remaining); op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_op_size);
cl->recv_list.push_back(op->buf, cl->read_remaining);
} }
else if (op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG && op->reply.hdr.retval > 0) else if (op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG && op->reply.hdr.retval > 0)
{ {
delete cl->read_op; cl->read_op_size = op->reply.hdr.retval;
cl->read_op = op;
cl->read_state = CL_READ_REPLY_DATA;
cl->read_remaining = op->reply.hdr.retval;
free(op->buf); free(op->buf);
op->buf = malloc_or_die(op->reply.hdr.retval); op->buf = malloc_or_die(op->reply.hdr.retval);
cl->recv_list.push_back(op->buf, op->reply.hdr.retval);
} }
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0) else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
{ {
delete cl->read_op; cl->read_op_size = op->reply.describe.result_bytes;
cl->read_op = op;
cl->read_state = CL_READ_REPLY_DATA;
cl->read_remaining = op->reply.describe.result_bytes;
free(op->buf); free(op->buf);
op->buf = malloc_or_die(op->reply.describe.result_bytes); op->buf = malloc_or_die(op->reply.describe.result_bytes);
cl->recv_list.push_back(op->buf, op->reply.describe.result_bytes);
}
else
{
reuse:
// It's fine to reuse cl->read_op for the next reply
handle_reply_ready(op);
cl->recv_list.push_back(cl->read_op->req.buf, OSD_PACKET_SIZE);
cl->read_remaining = OSD_PACKET_SIZE;
cl->read_state = CL_READ_HDR;
} }
return true; return true;
} }
void osd_messenger_t::handle_reply_ready(osd_op_t *op) size_t osd_messenger_t::op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done)
{ {
// Measure subop latency osd_op_t *op = cl->read_op;
timespec tv_end; size_t from = cl->read_op_pos-OSD_PACKET_SIZE;
clock_gettime(CLOCK_REALTIME, &tv_end); auto op_read_buf = [&](uint8_t *dst, size_t dst_len)
stats.subop_stat_count[op->req.hdr.opcode]++;
if (!stats.subop_stat_count[op->req.hdr.opcode])
{ {
stats.subop_stat_count[op->req.hdr.opcode]++; if (from < dst_len)
stats.subop_stat_sum[op->req.hdr.opcode] = 0; {
size_t n = dst_len-from;
if (n > src_len-done)
n = src_len-done;
memcpy(dst+from, src+done, n);
done += n;
cl->read_op_pos += n;
from += n;
if (from < dst_len)
return false;
from = 0;
}
else
from -= dst_len;
return true;
};
if (op->op_type == OSD_OP_IN)
{
if (op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len))
return done;
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_rw.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_stab.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_read_bmp.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_WRITE)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.rw.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.show_conf.json_len))
return done;
}
} }
stats.subop_stat_sum[op->req.hdr.opcode] += ( else
(tv_end.tv_sec - op->tv_begin.tv_sec)*1000000 + {
(tv_end.tv_nsec - op->tv_begin.tv_nsec)/1000 if (op->reply.hdr.opcode == OSD_OP_SEC_READ)
); {
set_immediate_ops.push_back(op); if (op->reply.sec_rw.attr_len > 0)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len))
return done;
}
if (op->reply.hdr.retval > 0)
{
for (int i = 0; i < op->iov.count; i++)
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
return done;
}
}
else if (op->reply.hdr.opcode == OSD_OP_READ)
{
if (op->reply.rw.bitmap_len > 0)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.rw.bitmap_len))
return done;
}
if (op->reply.hdr.retval > 0)
{
if (op->enc)
{
if (!op_decrypted_copy_data_from(cl, src, src_len, from, done))
return done;
}
else
{
for (int i = 0; i < op->iov.count; i++)
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
return done;
}
}
}
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
{
if (!op_read_buf((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval))
return done;
}
else if ((op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP ||
op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG) && op->reply.hdr.retval > 0)
{
if (!op_read_buf((uint8_t*)op->buf, op->reply.hdr.retval))
return done;
}
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
{
if (!op_read_buf((uint8_t*)op->buf, op->reply.describe.result_bytes))
return done;
}
}
handle_finished_op(cl);
return done;
}
size_t osd_messenger_t::op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst)
{
osd_op_t *op = cl->read_op;
size_t from = cl->read_op_pos-OSD_PACKET_SIZE;
size_t done = 0;
auto op_read_buf = [&](uint8_t *dst, size_t dst_len)
{
if (lst.size() >= IOV_MAX)
return false;
if (from < dst_len)
{
lst.push_back((iovec){ .iov_base = dst+from, .iov_len = dst_len-from });
cl->read_op_pos += dst_len-from;
done += dst_len-from;
from = 0;
}
else
from -= dst_len;
return true;
};
if (op->op_type == OSD_OP_IN)
{
if (op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len))
return done;
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_rw.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_stab.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_read_bmp.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_WRITE)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.rw.len))
return done;
}
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
{
if (!op_read_buf((uint8_t*)op->buf, op->req.show_conf.json_len))
return done;
}
}
else
{
if (op->reply.hdr.opcode == OSD_OP_SEC_READ)
{
if (op->reply.sec_rw.attr_len > 0)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len))
return done;
}
if (op->reply.hdr.retval > 0)
{
for (int i = 0; i < op->iov.count; i++)
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
return done;
}
}
else if (op->reply.hdr.opcode == OSD_OP_READ)
{
if (op->reply.rw.bitmap_len > 0)
{
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.rw.bitmap_len))
return done;
}
if (op->reply.hdr.retval > 0)
{
if (op->enc)
cl->read_op_inline_decrypt_pos = cl->read_op_pos;
for (int i = 0; i < op->iov.count; i++)
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
return done;
}
}
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
{
if (!op_read_buf((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval))
return done;
}
else if ((op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP ||
op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG) && op->reply.hdr.retval > 0)
{
if (!op_read_buf((uint8_t*)op->buf, op->reply.hdr.retval))
return done;
}
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
{
if (!op_read_buf((uint8_t*)op->buf, op->reply.describe.result_bytes))
return done;
}
}
return done;
}
void osd_messenger_t::handle_finished_op(osd_client_t *cl)
{
osd_op_t *op = cl->read_op;
if (op->op_type == OSD_OP_IN)
{
// Operation is ready
cl->received_ops.push_back(op);
}
else
{
// Inline decryption
if (cl->read_op_inline_decrypt_pos != (size_t)-1)
{
op_decrypt_inline(cl);
cl->read_op_inline_decrypt_pos = (size_t)-1;
}
// Measure subop (outbound op) latency
timespec tv_end;
clock_gettime(CLOCK_REALTIME, &tv_end);
stats.subop_stat_count[op->req.hdr.opcode]++;
if (!stats.subop_stat_count[op->req.hdr.opcode])
{
stats.subop_stat_count[op->req.hdr.opcode]++;
stats.subop_stat_sum[op->req.hdr.opcode] = 0;
}
stats.subop_stat_sum[op->req.hdr.opcode] += (
(tv_end.tv_sec - op->tv_begin.tv_sec)*1000000 +
(tv_end.tv_nsec - op->tv_begin.tv_nsec)/1000
);
}
set_immediate_ops.push_back(op);
cl->read_op = NULL;
} }
+230 -152
View File
@@ -6,26 +6,21 @@
#include <sys/epoll.h> #include <sys/epoll.h>
#include "messenger.h" #include "messenger.h"
#include "msgr_iothread.h"
void osd_messenger_t::outbox_push(osd_op_t *cur_op) void osd_messenger_t::outbox_push(osd_op_t *cur_op)
{ {
assert(cur_op->client_id); assert(cur_op->peer_fd);
auto cl_it = clients.find(cur_op->client_id); osd_client_t *cl = clients.at(cur_op->peer_fd);
if (cl_it == clients.end() || cl_it->second->peer_state == PEER_STOPPED)
{
delete cur_op;
return;
}
osd_client_t *cl = cl_it->second;
if (cur_op->op_type == OSD_OP_OUT) if (cur_op->op_type == OSD_OP_OUT)
{ {
clock_gettime(CLOCK_REALTIME, &cur_op->tv_begin); clock_gettime(CLOCK_REALTIME, &cur_op->tv_begin);
cur_op->req.hdr.id = ++cl->send_op_id; cur_op->req.hdr.id = ++cl->send_op_id;
cl->sent_ops[cur_op->req.hdr.id] = cur_op;
} }
else else
{ {
// Remove the operation from received op list // Check that operation actually belongs to this client
// FIXME: Review if this is still needed
bool found = false; bool found = false;
for (auto it = cl->received_ops.begin(); it != cl->received_ops.end(); it++) for (auto it = cl->received_ops.begin(); it != cl->received_ops.end(); it++)
{ {
@@ -36,84 +31,14 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
break; break;
} }
} }
// Can't be not found because client IDs are unique if (!found)
assert(found); {
} delete cur_op;
auto & to_send_list = cl->write_msg.msg_iovlen ? cl->next_send_list : cl->send_list; return;
auto & to_outbox = cl->write_msg.msg_iovlen ? cl->next_outbox : cl->outbox; }
if (cur_op->op_type == OSD_OP_IN)
{
measure_exec(cur_op); measure_exec(cur_op);
to_send_list.push_back((iovec){ .iov_base = cur_op->reply.buf, .iov_len = OSD_PACKET_SIZE });
}
else
{
to_send_list.push_back((iovec){ .iov_base = cur_op->req.buf, .iov_len = OSD_PACKET_SIZE });
cl->sent_ops[cur_op->req.hdr.id] = cur_op;
}
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = MSGR_SENDP_HDR });
// Bitmap
if (cur_op->op_type == OSD_OP_IN &&
cur_op->req.hdr.opcode == OSD_OP_SEC_READ &&
cur_op->reply.sec_rw.attr_len > 0)
{
to_send_list.push_back((iovec){
.iov_base = cur_op->bitmap,
.iov_len = cur_op->reply.sec_rw.attr_len,
});
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
else if (cur_op->op_type == OSD_OP_OUT &&
(cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE || cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) &&
cur_op->req.sec_rw.attr_len > 0)
{
to_send_list.push_back((iovec){
.iov_base = cur_op->bitmap,
.iov_len = cur_op->req.sec_rw.attr_len,
});
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
// Operation data
if ((cur_op->op_type == OSD_OP_IN
? (cur_op->req.hdr.opcode == OSD_OP_READ ||
cur_op->req.hdr.opcode == OSD_OP_SEC_READ ||
cur_op->req.hdr.opcode == OSD_OP_SEC_LIST ||
cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG ||
cur_op->req.hdr.opcode == OSD_OP_DESCRIBE)
: (cur_op->req.hdr.opcode == OSD_OP_WRITE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK ||
cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)) && cur_op->iov.count > 0)
{
for (int i = 0; i < cur_op->iov.count; i++)
{
if (cur_op->iov.buf[i].iov_len > 0)
{
assert(cur_op->iov.buf[i].iov_base);
to_send_list.push_back(cur_op->iov.buf[i]);
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
}
}
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{
if (cur_op->op_type == OSD_OP_IN && cur_op->reply.hdr.retval > 0)
{
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->reply.hdr.retval });
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
else if (cur_op->op_type == OSD_OP_OUT && cur_op->req.sec_read_bmp.len > 0)
{
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->req.sec_read_bmp.len });
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
}
}
if (cur_op->op_type == OSD_OP_IN)
{
to_outbox[to_outbox.size()-1].flags |= MSGR_SENDP_FREE;
} }
cl->write_ops.push_back(cur_op);
#ifdef WITH_RDMA #ifdef WITH_RDMA
if (cl->peer_state == PEER_RDMA) if (cl->peer_state == PEER_RDMA)
{ {
@@ -124,7 +49,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
if (!ringloop) if (!ringloop)
{ {
// FIXME: It's worse because it doesn't allow batching // FIXME: It's worse because it doesn't allow batching
while (cl->outbox.size()) while (cl->write_ops.size())
{ {
try_send(cl); try_send(cl);
} }
@@ -134,7 +59,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
if ((cl->write_msg.msg_iovlen > 0 || !try_send(cl)) && (cl->write_state == 0)) if ((cl->write_msg.msg_iovlen > 0 || !try_send(cl)) && (cl->write_state == 0))
{ {
cl->write_state = CL_WRITE_READY; cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cur_op->client_id); write_ready_clients.push_back(cur_op->peer_fd);
} }
ringloop->wakeup(); ringloop->wakeup();
} }
@@ -191,14 +116,29 @@ void osd_messenger_t::measure_exec(osd_op_t *cur_op)
bool osd_messenger_t::try_send(osd_client_t *cl) bool osd_messenger_t::try_send(osd_client_t *cl)
{ {
if (!cl->send_list.size() || cl->write_msg.msg_iovlen > 0 || cl->peer_state == PEER_STOPPED || cl->peer_fd < 0) int peer_fd = cl->peer_fd;
if (!cl->write_op && !cl->write_ops.size() || cl->write_msg.msg_iovlen > 0)
{ {
return true; return true;
} }
assert(cl->peer_state != PEER_RDMA); assert(cl->peer_state != PEER_RDMA);
while ((cl->write_op || cl->write_ops.size()) && cl->send_list.size() < IOV_MAX)
{
if (!cl->write_op)
{
cl->write_op = cl->write_ops.front();
cl->write_ops.pop_front();
}
osd_op_t *op = cl->write_op;
op_get_write_buffers(cl, cl->send_list);
if (!cl->write_op && op->op_type == OSD_OP_IN)
{
cl->send_free_ops.push_back(op);
}
}
if (ringloop && !use_sync_send_recv) if (ringloop && !use_sync_send_recv)
{ {
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL; auto iothread = iothreads.size() ? iothreads[peer_fd % iothreads.size()] : NULL;
io_uring_sqe sqe_local; io_uring_sqe sqe_local;
ring_data_t data_local; ring_data_t data_local;
io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe()); io_uring_sqe* sqe = (iothread ? &sqe_local : ringloop->get_sqe());
@@ -208,28 +148,32 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
data_local = {}; data_local = {};
} }
if (!sqe) if (!sqe)
{
return false; return false;
}
cl->send_list_size = 0;
for (auto & iov: cl->send_list)
{
cl->send_list_size += iov.iov_len;
}
cl->write_msg.msg_iov = cl->send_list.data(); cl->write_msg.msg_iov = cl->send_list.data();
cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX; cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
cl->refs++; cl->refs++;
ring_data_t* data = ((ring_data_t*)sqe->user_data); ring_data_t* data = ((ring_data_t*)sqe->user_data);
data->callback = [this, cl](ring_data_t *data) { handle_send(data->res, data->prev, data->more, cl); }; data->callback = [this, cl](ring_data_t *data) { handle_send(data->res, data->prev, data->more, cl); };
bool use_zc = has_sendmsg_zc && min_zerocopy_send_size >= 0; bool use_zc = has_sendmsg_zc && min_zerocopy_send_size >= 0;
if (use_zc && min_zerocopy_send_size > 0) if (use_zc && min_zerocopy_send_size > 0 &&
cl->send_list_size/cl->write_msg.msg_iovlen < min_zerocopy_send_size)
{ {
size_t avg_size = 0; use_zc = false;
for (size_t i = 0; i < cl->write_msg.msg_iovlen; i++)
avg_size += cl->write_msg.msg_iov[i].iov_len;
if (avg_size/cl->write_msg.msg_iovlen < min_zerocopy_send_size)
use_zc = false;
} }
if (use_zc) if (use_zc)
{ {
io_uring_prep_sendmsg_zc(sqe, cl->peer_fd, &cl->write_msg, MSG_WAITALL); io_uring_prep_sendmsg_zc(sqe, peer_fd, &cl->write_msg, MSG_WAITALL);
} }
else else
{ {
io_uring_prep_sendmsg(sqe, cl->peer_fd, &cl->write_msg, MSG_WAITALL); io_uring_prep_sendmsg(sqe, peer_fd, &cl->write_msg, MSG_WAITALL);
} }
if (iothread) if (iothread)
{ {
@@ -241,7 +185,7 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
cl->write_msg.msg_iov = cl->send_list.data(); cl->write_msg.msg_iov = cl->send_list.data();
cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX; cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
cl->refs++; cl->refs++;
int result = sendmsg(cl->peer_fd, &cl->write_msg, MSG_NOSIGNAL); int result = sendmsg(peer_fd, &cl->write_msg, MSG_NOSIGNAL);
if (result < 0) if (result < 0)
{ {
result = -errno; result = -errno;
@@ -256,9 +200,9 @@ void osd_messenger_t::send_replies()
{ {
for (int i = 0; i < write_ready_clients.size(); i++) for (int i = 0; i < write_ready_clients.size(); i++)
{ {
uint64_t client_id = write_ready_clients[i]; int peer_fd = write_ready_clients[i];
auto cl_it = clients.find(client_id); auto cl_it = clients.find(peer_fd);
if (cl_it != clients.end() && cl_it->second->peer_state != PEER_RDMA && !try_send(cl_it->second)) if (cl_it != clients.end() && !try_send(cl_it->second))
{ {
write_ready_clients.erase(write_ready_clients.begin(), write_ready_clients.begin() + i); write_ready_clients.erase(write_ready_clients.begin(), write_ready_clients.begin() + i);
return; return;
@@ -272,6 +216,7 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
if (!prev) if (!prev)
{ {
cl->write_msg.msg_iovlen = 0; cl->write_msg.msg_iovlen = 0;
cl->send_list.clear();
} }
if (!more) if (!more)
{ {
@@ -281,15 +226,15 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
{ {
if (cl->refs <= 0) if (cl->refs <= 0)
{ {
destroy_client(cl); delete cl;
} }
return; return;
} }
if (result < 0 && result != -EAGAIN && result != -EINTR) if (result < 0 && result != -EAGAIN && result != -EINTR)
{ {
// this is a client socket, so don't panic. just disconnect it // this is a client socket, so don't panic. just disconnect it
fprintf(stderr, "Client %ju socket write error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result)); fprintf(stderr, "Client %d socket write error: %d (%s). Disconnecting client\n", cl->peer_fd, -result, strerror(-result));
stop_client(cl->client_id); stop_client(cl->peer_fd);
return; return;
} }
if (result >= 0) if (result >= 0)
@@ -304,64 +249,42 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
cl->zc_free_list.erase(cl->zc_free_list.begin(), cl->zc_free_list.begin()+i+1); cl->zc_free_list.erase(cl->zc_free_list.begin(), cl->zc_free_list.begin()+i+1);
return; return;
} }
int done = 0; if (cl->send_list_size > result)
while (result > 0 && done < cl->send_list.size())
{ {
iovec & iov = cl->send_list[done]; fprintf(stderr, "Client %d socket write error: expected to send "
if (iov.iov_len <= result) "%zu bytes with MSG_WAITALL but sent %u. Disconnecting client\n", cl->peer_fd, cl->send_list_size, result);
{ stop_client(cl->peer_fd);
if (cl->outbox[done].flags & MSGR_SENDP_FREE) return;
{ }
// Reply fully sent for (auto op: cl->send_free_ops)
if (more) {
cl->zc_free_list.push_back(cl->outbox[done].op); if (more)
else cl->zc_free_list.push_back(op);
delete cl->outbox[done].op;
}
result -= iov.iov_len;
done++;
}
else else
{ delete op;
iov.iov_len -= result;
iov.iov_base = (uint8_t*)iov.iov_base + result;
break;
}
} }
if (more) if (more)
{
int expected = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
if (done != expected)
{
fprintf(stderr, "Client %ju socket write error: expected to send "
"%d iovecs with MSG_WAITALL but sent %d. Disconnecting client\n", cl->client_id, expected, done);
stop_client(cl->client_id);
return;
}
cl->zc_free_list.push_back(NULL); // end marker cl->zc_free_list.push_back(NULL); // end marker
} cl->send_free_ops.clear();
if (done > 0) cl->write_state = cl->write_op || cl->write_ops.size() ? CL_WRITE_READY : 0;
{
cl->send_list.erase(cl->send_list.begin(), cl->send_list.begin()+done);
cl->outbox.erase(cl->outbox.begin(), cl->outbox.begin()+done);
}
if (cl->next_send_list.size())
{
cl->send_list.insert(cl->send_list.end(), cl->next_send_list.begin(), cl->next_send_list.end());
cl->outbox.insert(cl->outbox.end(), cl->next_outbox.begin(), cl->next_outbox.end());
cl->next_send_list.clear();
cl->next_outbox.clear();
}
cl->write_state = cl->outbox.size() > 0 ? CL_WRITE_READY : 0;
#ifdef WITH_RDMA #ifdef WITH_RDMA
if (cl->rdma_conn && !cl->outbox.size() && cl->peer_state == PEER_RDMA_CONNECTING) if (cl->rdma_conn && !cl->write_op && !cl->write_ops.size() && cl->peer_state == PEER_RDMA_CONNECTING)
{ {
// FIXME: Do something better than just forgetting the FD
// FIXME: Ignore pings during RDMA state transition // FIXME: Ignore pings during RDMA state transition
if (log_level > 0) if (log_level > 0)
{ {
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id); fprintf(stderr, "Successfully connected with client %d using RDMA\n", cl->peer_fd);
} }
cl->peer_state = PEER_RDMA; cl->peer_state = PEER_RDMA;
tfd->set_fd_handler(cl->peer_fd, false, [this](int peer_fd, int epoll_events)
{
// Do not miss the disconnection!
if (epoll_events & EPOLLRDHUP)
{
handle_peer_epoll(peer_fd, epoll_events);
}
});
// Add the initial receive request // Add the initial receive request
init_recv_rdma(cl); init_recv_rdma(cl);
} }
@@ -369,6 +292,161 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
} }
if (cl->write_state != 0) if (cl->write_state != 0)
{ {
write_ready_clients.push_back(cl->client_id); write_ready_clients.push_back(cl->peer_fd);
} }
} }
static inline bool op_write_headers(osd_op_t *op, std::function<bool(uint8_t*, size_t)> op_write_buf)
{
// Header
if (!op_write_buf((op->op_type == OSD_OP_IN ? op->reply.buf : op->req.buf), OSD_PACKET_SIZE))
return false;
// Bitmap
if (op->op_type == OSD_OP_IN &&
op->req.hdr.opcode == OSD_OP_SEC_READ &&
op->reply.sec_rw.attr_len > 0)
{
if (!op_write_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len))
return false;
}
else if (op->op_type == OSD_OP_OUT &&
(op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) &&
op->req.sec_rw.attr_len > 0)
{
if (!op_write_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len))
return false;
}
if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
{
if (op->op_type == OSD_OP_IN && op->reply.hdr.retval > 0)
{
if (!op_write_buf((uint8_t*)op->buf, (size_t)op->reply.hdr.retval))
return false;
}
else if (op->op_type == OSD_OP_OUT && op->req.sec_read_bmp.len > 0)
{
if (!op_write_buf((uint8_t*)op->buf, (size_t)op->req.sec_read_bmp.len))
return false;
}
}
return true;
}
static inline bool op_has_data(osd_op_t *op)
{
return (op->op_type == OSD_OP_IN
? (op->req.hdr.opcode == OSD_OP_READ ||
op->req.hdr.opcode == OSD_OP_SEC_READ ||
op->req.hdr.opcode == OSD_OP_SEC_LIST ||
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG ||
op->req.hdr.opcode == OSD_OP_DESCRIBE)
: (op->req.hdr.opcode == OSD_OP_WRITE ||
op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE ||
op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK ||
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)) && op->iov.count > 0;
}
size_t osd_messenger_t::op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len)
{
size_t done = 0;
size_t from = cl->write_op_pos;
auto op_write_buf = [&](uint8_t *src, size_t src_len)
{
if (from < src_len)
{
size_t n = src_len-from;
if (n > dst_len-done)
n = dst_len-done;
memcpy(dst+done, src+from, n);
done += n;
cl->write_op_pos += n;
from += n;
if (from < src_len)
return false;
from = 0;
}
else
from -= src_len;
return true;
};
if (!op_write_headers(cl->write_op, op_write_buf))
{
return done;
}
// Operation data
if (op_has_data(cl->write_op))
{
if (cl->write_op->enc)
{
if (!op_encrypted_copy_data_to(cl, dst, dst_len, from, done))
{
return done;
}
}
else
{
for (int i = 0; i < cl->write_op->iov.count; i++)
{
if (!op_write_buf((uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len))
return done;
}
}
}
cl->write_op = NULL;
cl->write_op_pos = 0;
return done;
}
void osd_messenger_t::op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst)
{
size_t from = cl->write_op_pos;
auto op_write_buf = [&](uint8_t *src, size_t src_len)
{
if (lst.size() >= IOV_MAX)
return false;
if (from < src_len)
{
lst.push_back((iovec){ .iov_base = src+from, .iov_len = src_len-from });
cl->write_op_pos += src_len-from;
from = 0;
}
else
from -= src_len;
return true;
};
if (!op_write_headers(cl->write_op, op_write_buf))
{
return;
}
// Operation data
if (op_has_data(cl->write_op))
{
if (cl->write_op->enc)
{
if (lst.size() >= IOV_MAX)
return;
// No way except to allocate a temporary buffer and encrypt data to it
assert(cl->write_op->req.hdr.opcode == OSD_OP_WRITE);
size_t remsize = cl->write_op->req.rw.len - from + (from % 16);
assert(remsize > 0);
assert(!cl->write_op->enc_buf);
cl->write_op->enc_buf = (uint8_t*)malloc_or_die(remsize);
size_t done = 0;
bool end = op_encrypted_copy_data_to(cl, cl->write_op->enc_buf, remsize, from, done);
assert(end);
lst.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = remsize });
}
else
{
for (int i = 0; i < cl->write_op->iov.count; i++)
{
if (!op_write_buf((uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len))
return;
}
}
}
cl->write_op = NULL;
cl->write_op_pos = 0;
}
+82 -61
View File
@@ -5,6 +5,9 @@
#include <assert.h> #include <assert.h>
#include "messenger.h" #include "messenger.h"
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
void osd_client_t::cancel_ops() void osd_client_t::cancel_ops()
{ {
@@ -40,60 +43,99 @@ void osd_op_t::cancel()
} }
} }
// force_delete means stop the client anyway, even if there are refs to it in the event loop. void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
// the flag should be used in the destructor.
// why? - because yes, we could close the FD first and let it fail all requests in the event loop,
// but in that case it can be quickly reopened and we can get old failed responses for the new FD.
void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
{ {
auto it = clients.find(client_id); assert(peer_fd != 0);
if (!client_id || it == clients.end()) auto it = clients.find(peer_fd);
if (it == clients.end())
{ {
return; return;
} }
osd_client_t *cl = it->second; osd_client_t *cl = it->second;
if (cl->peer_state == PEER_STOPPED) // FIXME: This 'force' flag is probably an ugly reenterability hack - check its logic and maybe remove it
if (cl->peer_state == PEER_CONNECTING && !force || cl->peer_state == PEER_STOPPED)
{ {
if (force_delete)
{
destroy_client(cl);
}
return; return;
} }
cl->received_ops.clear();
if (log_level > 0) if (log_level > 0)
{ {
if (cl->osd_num) if (cl->osd_num)
{ {
fprintf(stderr, "[OSD %ju] Stopping client %ju (OSD peer %ju)\n", osd_num, client_id, cl->osd_num); fprintf(stderr, "[OSD %ju] Stopping client %d (OSD peer %ju)\n", osd_num, peer_fd, cl->osd_num);
} }
else if (cl->in_osd_num) else if (cl->in_osd_num)
{ {
fprintf(stderr, "[OSD %ju] Stopping client %ju (incoming OSD peer %ju)\n", osd_num, client_id, cl->in_osd_num); fprintf(stderr, "[OSD %ju] Stopping client %d (incoming OSD peer %ju)\n", osd_num, peer_fd, cl->in_osd_num);
} }
else else
{ {
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id); fprintf(stderr, "[OSD %ju] Stopping client %d (regular client)\n", osd_num, peer_fd);
} }
} }
if (cl->encrypt_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_encrypt(cl->encrypt_ctx);
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
}
if (cl->decrypt_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_decrypt(cl->decrypt_ctx);
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
}
// First set state to STOPPED so another stop_client() call doesn't try to free it again // First set state to STOPPED so another stop_client() call doesn't try to free it again
cl->refs++; cl->refs++;
int prev_state = cl->peer_state; int prev_state = cl->peer_state;
cl->peer_state = PEER_STOPPED; cl->peer_state = PEER_STOPPED;
if (cl->osd_num) if (cl->osd_num)
{ {
auto osd_it = osd_peers.find(cl->osd_num); auto osd_it = osd_peer_fds.find(cl->osd_num);
if (osd_it != osd_peers.end() && osd_it->second == cl) if (osd_it != osd_peer_fds.end() && osd_it->second == cl->peer_fd)
{ {
// ...and forget OSD peer // ...and forget OSD peer
osd_peers.erase(osd_it); osd_peer_fds.erase(osd_it);
} }
} }
#ifdef WITH_RDMA
if (cl->rdma_conn && cl->rdma_conn->cmid)
{
auto rdma_it = rdmacm_connections.find(cl->rdma_conn->cmid);
if (rdma_it != rdmacm_connections.end() && rdma_it->second == cl)
{
rdmacm_connections.erase(rdma_it);
}
}
#endif
#ifndef __MOCK__
// Then remove FD from the eventloop so we don't accidentally read something
tfd->set_fd_handler(peer_fd, false, NULL);
if (cl->connect_timeout_id >= 0) if (cl->connect_timeout_id >= 0)
{ {
tfd->clear_timer(cl->connect_timeout_id); tfd->clear_timer(cl->connect_timeout_id);
cl->connect_timeout_id = -1; cl->connect_timeout_id = -1;
} }
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
{
if (*rit == peer_fd)
{
read_ready_clients.erase(rit);
break;
}
}
for (auto wit = write_ready_clients.begin(); wit != write_ready_clients.end(); wit++)
{
if (*wit == peer_fd)
{
write_ready_clients.erase(wit);
break;
}
}
#endif
if (cl->in_osd_num && break_pg_locks) if (cl->in_osd_num && break_pg_locks)
{ {
// Break PG locks // Break PG locks
@@ -107,56 +149,19 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
// so do not repeer on it. // so do not repeer on it.
repeer_pgs(cl->osd_num); repeer_pgs(cl->osd_num);
} }
if (cl->peer_fd >= 0) // Find the item again because it can be invalidated at this point
it = clients.find(peer_fd);
if (it != clients.end())
{ {
int r = shutdown(cl->peer_fd, SHUT_RDWR); clients.erase(it);
if (r != 0 && errno != ENOTCONN)
{
fprintf(stderr, "[OSD %ju] failed to shutdown a socket: %s (code %d)\n", osd_num, strerror(errno), errno);
}
} }
cl->refs--; cl->refs--;
if (cl->refs <= 0 || force_delete) if (cl->refs <= 0 || force_delete)
{ {
destroy_client(cl); delete cl;
} }
} }
void osd_messenger_t::destroy_client(osd_client_t *cl)
{
// Find the item again because it can be invalidated at this point
clients.erase(cl->client_id);
if (cl->peer_fd >= 0)
{
tfd->set_fd_handler(cl->peer_fd, false, NULL);
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
{
if (*rit == cl->client_id)
{
read_ready_clients.erase(rit);
break;
}
}
for (auto wit = write_ready_clients.begin(); wit != write_ready_clients.end(); wit++)
{
if (*wit == cl->client_id)
{
write_ready_clients.erase(wit);
break;
}
}
clients_by_fd.erase(cl->peer_fd);
}
#ifdef WITH_RDMA
if (cl->rdma_conn)
{
destroy_rdma_conn(cl->rdma_conn);
cl->rdma_conn = NULL;
}
#endif
delete cl;
}
osd_client_t::~osd_client_t() osd_client_t::~osd_client_t()
{ {
free(in_buf); free(in_buf);
@@ -179,6 +184,13 @@ osd_client_t::~osd_client_t()
} }
// Cancel outbound ops // Cancel outbound ops
cancel_ops(); cancel_ops();
for (osd_op_t *op: send_free_ops)
{
if (op)
{
delete op;
}
}
for (osd_op_t *op: zc_free_list) for (osd_op_t *op: zc_free_list)
{ {
if (op) if (op)
@@ -186,4 +198,13 @@ osd_client_t::~osd_client_t()
delete op; delete op;
} }
} }
#ifndef __MOCK__
#ifdef WITH_RDMA
if (rdma_conn)
{
delete rdma_conn;
rdma_conn = NULL;
}
#endif
#endif
} }
-24
View File
@@ -20,15 +20,6 @@ typedef uint64_t inode_t;
// Pool ID is 16 bits long // Pool ID is 16 bits long
typedef uint32_t pool_id_t; typedef uint32_t pool_id_t;
typedef uint64_t osd_num_t;
typedef uint32_t pg_num_t;
struct pool_pg_num_t
{
pool_id_t pool_id;
pg_num_t pg_num;
};
// 16 bytes per object/stripe id // 16 bytes per object/stripe id
// stripe = (start of the parity stripe + peer role) // stripe = (start of the parity stripe + peer role)
// i.e. for example (256KB + one of 0,1,2) // i.e. for example (256KB + one of 0,1,2)
@@ -70,21 +61,6 @@ inline bool operator < (const obj_ver_id & a, const obj_ver_id & b)
return a.oid < b.oid || a.oid == b.oid && a.version < b.version; return a.oid < b.oid || a.oid == b.oid && a.version < b.version;
} }
inline bool operator < (const pool_pg_num_t & a, const pool_pg_num_t & b)
{
return a.pool_id < b.pool_id || a.pool_id == b.pool_id && a.pg_num < b.pg_num;
}
inline bool operator == (const pool_pg_num_t & a, const pool_pg_num_t & b)
{
return a.pool_id == b.pool_id && a.pg_num == b.pg_num;
}
inline bool operator != (const pool_pg_num_t & a, const pool_pg_num_t & b)
{
return a.pool_id != b.pool_id || a.pg_num != b.pg_num;
}
namespace std namespace std
{ {
template<> struct hash<object_id> template<> struct hash<object_id>
+1
View File
@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "object_id.h" #include "object_id.h"
#include "osd_id.h"
// Magic numbers // Magic numbers
#define SECONDARY_OSD_OP_MAGIC 0x2bd7b10325434553l #define SECONDARY_OSD_OP_MAGIC 0x2bd7b10325434553l
+1 -1
View File
@@ -1049,7 +1049,7 @@ static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
{ {
// Get larger allocated extents, possibly with false positives // Get larger allocated extents, possibly with false positives
uint64_t bmp_pos = (offset-task.offset) / task.bitmap_granularity; uint64_t bmp_pos = (offset-task.offset) / task.bitmap_granularity;
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity; uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity - bmp_pos;
while (bmp_pos < bmp_end) while (bmp_pos < bmp_end)
{ {
if (!(bmp_pos & 7) && bmp_end >= bmp_pos+8) if (!(bmp_pos & 7) && bmp_end >= bmp_pos+8)

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