Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09df74cfac | ||
|
|
2ef3f012c9 | ||
|
|
7175d99c64 | ||
|
|
230a26772e | ||
|
|
637684c579 | ||
|
|
c541dd422f | ||
|
|
22c39561cf | ||
|
|
d4c67b4879 | ||
|
|
d1b7167861 | ||
|
|
33b561b73a | ||
|
|
a95a60c600 | ||
|
|
b3bc815354 | ||
|
|
42fc45d6da | ||
|
|
3fbe2e7f8a | ||
|
|
310c512b43 | ||
|
|
fd3e3b4ef0 | ||
|
|
93cf69f89f | ||
|
|
a8dd8bf06c | ||
|
|
7e23b57014 | ||
|
|
b2427836a1 | ||
|
|
2ed6760447 | ||
|
|
a1d215ea2f | ||
|
|
4fa442a5de | ||
|
|
3a057ed5af | ||
|
|
0ad042b28b | ||
|
|
4d75c6c8f9 | ||
|
|
9ec18f0aa1 | ||
|
|
facaa2cc6a | ||
|
|
d2ac8e3827 | ||
|
|
d6dacc67db | ||
|
|
cbe51595cb | ||
|
|
6bd0830ab8 | ||
|
|
796e82f34d | ||
|
|
db39283970 | ||
|
|
9f8c686321 | ||
|
|
2618e559d1 | ||
|
|
037d2bc162 | ||
|
|
f0b64adb32 | ||
|
|
f06d64d879 | ||
|
|
d164499a1c | ||
|
|
725e9aa8ae | ||
|
|
0715feffa1 | ||
|
|
e9f37e8dc3 | ||
|
|
4fbe4b5654 | ||
|
|
9fb645693b | ||
|
|
9e47828383 | ||
|
|
ac2ce48cb2 | ||
|
|
9cc2beed95 | ||
|
|
fb1c870f5c | ||
|
|
2d616d8058 | ||
|
|
3f7f6f442b | ||
|
|
7e7b95eeb4 | ||
|
|
dd588a0783 | ||
|
|
028a6cab68 | ||
|
|
d75334ddf0 | ||
|
|
bf0875128e | ||
|
|
9a6a7b7f75 | ||
|
|
c4c17ee6fb | ||
|
|
2b801a7ffa | ||
|
|
233d2b2a09 | ||
|
|
8380d4c6a6 | ||
|
|
73f9c7293f | ||
|
|
1c66c3e5ba | ||
|
|
eddfa93c18 | ||
|
|
ddd755a0e6 | ||
|
|
819f5b7ec9 | ||
|
|
34d0a6d9b1 |
@@ -1,28 +1,29 @@
|
|||||||
FROM node:16-bullseye
|
FROM node:16-bookworm
|
||||||
|
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
|
||||||
ADD ./docker/vitastor.gpg /etc/apt/trusted.gpg.d
|
ADD ./docker/etc/apt/trusted.gpg.d /etc/apt/trusted.gpg.d
|
||||||
|
|
||||||
RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list; \
|
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list; \
|
||||||
echo 'deb http://vitastor.io/debian bullseye main' >> /etc/apt/sources.list; \
|
echo 'deb http://vitastor.io/debian bookworm 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 a=bullseye-backports' >> /etc/apt/preferences; \
|
echo 'Pin: release n=bookworm-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 libasan5 \
|
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan8 \
|
||||||
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
|
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev
|
||||||
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; \
|
||||||
|
|||||||
@@ -1206,6 +1206,78 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_checksum:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_checksum.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_old_checksum:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: OLD=1 /root/vitastor/tests/test_checksum.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_corrupt_all:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_corrupt_all.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_old_corrupt_all:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: OLD=1 /root/vitastor/tests/test_corrupt_all.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_reweight_half:
|
test_reweight_half:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -1818,6 +1890,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_old_partwr_csum:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: OLD=1 /root/vitastor/tests/test_partwr_csum.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_heal_old_csum_32k_dmj:
|
test_heal_old_csum_32k_dmj:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12)
|
|||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
set(VITASTOR_VERSION "3.0.1")
|
set(VITASTOR_VERSION "3.0.4")
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -1,5 +1,5 @@
|
|||||||
# Compile stage
|
# Compile stage
|
||||||
FROM golang:bookworm AS build
|
FROM golang:trixie 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:bookworm
|
FROM debian:trixie
|
||||||
|
|
||||||
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
|
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
|
||||||
LABEL description="Vitastor CSI Driver"
|
LABEL description="Vitastor CSI Driver"
|
||||||
@@ -25,20 +25,20 @@ 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
|
||||||
libnuma1 liburing2 libglib2.0-0 libfuse3-3 libaio1 libzstd1 libnettle8 \
|
libaio1t64 libc6 libfuse3-4 libglib2.0-0t64 libgmp10 libgnutls30t64 \
|
||||||
libgmp10 libhogweed6 libp11-kit0 libidn2-0 libunistring2 libtasn1-6 libpcre2-8-0 libffi8 && \
|
libhogweed6t64 libnettle8t64 libnuma1 libselinux1 liburing2 libzstd1 zlib1g && \
|
||||||
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)
|
||||||
|
|
||||||
COPY --from=build /app/vitastor-csi /bin/
|
COPY --from=build /app/vitastor-csi /bin/
|
||||||
|
|
||||||
RUN (echo deb http://vitastor.io/debian bookworm main > /etc/apt/sources.list.d/vitastor.list) && \
|
RUN (echo deb http://vitastor.io/debian trixie main > /etc/apt/sources.list.d/vitastor.list) && \
|
||||||
((echo 'Package: *'; echo 'Pin: origin "vitastor.io"'; echo 'Pin-Priority: 1000') > /etc/apt/preferences.d/vitastor.pref) && \
|
((echo 'Package: *'; echo 'Pin: origin "vitastor.io"'; echo 'Pin-Priority: 1000') > /etc/apt/preferences.d/vitastor.pref) && \
|
||||||
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y vitastor-client ibverbs-providers && \
|
apt-get install -y vitastor-client ibverbs-providers && \
|
||||||
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-10.0.2%2Bds-2%2Bvitastor1/qemu-utils_10.0.2%2Bds-2%2Bvitastor1_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 && \
|
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.2%2Bds-2%2Bvitastor1/qemu-block-extra_10.0.2%2Bds-2%2Bvitastor1_amd64.deb && \
|
||||||
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,5 +1,5 @@
|
|||||||
# Compile stage
|
# Compile stage
|
||||||
FROM golang:bookworm AS build
|
FROM golang:trixie 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:bookworm
|
FROM debian:trixie
|
||||||
|
|
||||||
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-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-utils_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-bookworm-9.2.2%2Bds-1%2Bvitastor4/qemu-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
|
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 && \
|
||||||
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
@@ -1,4 +1,4 @@
|
|||||||
VITASTOR_VERSION ?= v3.0.1
|
VITASTOR_VERSION ?= v3.0.4
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ spec:
|
|||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
allowPrivilegeEscalation: true
|
allowPrivilegeEscalation: true
|
||||||
image: vitalif/vitastor-csi:v3.0.1
|
image: vitalif/vitastor-csi:v3.0.4
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ spec:
|
|||||||
privileged: true
|
privileged: true
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
image: vitalif/vitastor-csi:v3.0.1
|
image: vitalif/vitastor-csi:v3.0.4
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ package vitastor
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
vitastorCSIDriverName = "csi.vitastor.io"
|
vitastorCSIDriverName = "csi.vitastor.io"
|
||||||
vitastorCSIDriverVersion = "3.0.1"
|
vitastorCSIDriverVersion = "3.0.4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct fills the parameters of request or user input
|
// Config struct fills the parameters of request or user input
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
vitastor (3.0.1-1) unstable; urgency=medium
|
vitastor (3.0.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Build Docker image with Vitastor packages
|
# Build Docker image with Vitastor packages
|
||||||
|
|
||||||
FROM debian:bookworm
|
FROM debian:trixie
|
||||||
|
|
||||||
ADD etc/apt /etc/apt/
|
ADD etc/apt /etc/apt/
|
||||||
RUN apt-get update && apt-get -y install vitastor ibverbs-providers udev systemd qemu-system-x86 qemu-system-common qemu-block-extra qemu-utils jq nfs-common && apt-get clean
|
RUN apt-get update && apt-get -y install vitastor ibverbs-providers udev systemd qemu-system-x86 qemu-system-common qemu-block-extra qemu-utils jq nfs-common && apt-get clean
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
VITASTOR_VERSION ?= v3.0.1
|
VITASTOR_VERSION ?= v3.0.4
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
Package: *
|
Package: *
|
||||||
Pin: release n=bookworm-backports
|
Pin: release n=trixie-backports
|
||||||
Pin-Priority: 500
|
Pin-Priority: 500
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
deb http://vitastor.io/debian bookworm main
|
deb http://vitastor.io/debian trixie main
|
||||||
deb http://http.debian.net/debian/ bookworm-backports main
|
#deb http://http.debian.net/debian/ trixie-backports main
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Desired Vitastor version
|
# Desired Vitastor version
|
||||||
VITASTOR_VERSION=v3.0.1
|
VITASTOR_VERSION=v3.0.4
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
+17
-28
@@ -22,7 +22,6 @@ between clients, OSDs and etcd.
|
|||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
- [rdma_odp](#rdma_odp)
|
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
@@ -102,11 +101,6 @@ found or if `osd_network` is not specified. Auto-selection is also
|
|||||||
unsupported with old libibverbs < v32, like in Debian 10 Buster or
|
unsupported with old libibverbs < v32, like in Debian 10 Buster or
|
||||||
CentOS 7.
|
CentOS 7.
|
||||||
|
|
||||||
Vitastor supports all adapters, even ones without ODP support, like
|
|
||||||
Mellanox ConnectX-3 and non-Mellanox cards. Versions up to Vitastor
|
|
||||||
1.2.0 required ODP which is only present in Mellanox ConnectX >= 4.
|
|
||||||
See also [rdma_odp](#rdma_odp).
|
|
||||||
|
|
||||||
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
||||||
features.
|
features.
|
||||||
|
|
||||||
@@ -116,6 +110,23 @@ the manual of your network vendor for details about setting up the switch
|
|||||||
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
|
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
|
||||||
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
||||||
|
|
||||||
|
Vitastor supports all adapters, even ones without ODP (On-Demand Paging)
|
||||||
|
support, like Mellanox ConnectX-3 and non-Mellanox cards. ODP is only present
|
||||||
|
in Mellanox ConnectX >= 4 adapters and allows to skip memory registration
|
||||||
|
for RDMA and thus, in theory, avoid memory copying.
|
||||||
|
|
||||||
|
Versions up to Vitastor 1.2.0 required ODP, then it was disabled by default,
|
||||||
|
but it was still supported up to 3.0.3. Now ODP support is removed because it
|
||||||
|
actually only hurts performance: an example 3-node cluster with 8 NVMe in each
|
||||||
|
node and 2*25 GBit/s ConnectX-6 RDMA network pushed 3950000 read iops without
|
||||||
|
ODP, but only 239000 iops with ODP.
|
||||||
|
|
||||||
|
This happens because Mellanox ODP implementation seems to be based on
|
||||||
|
message retransmissions when the adapter doesn't know about the buffer yet -
|
||||||
|
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
||||||
|
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
||||||
|
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
## rdma_port_num
|
## rdma_port_num
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
@@ -187,28 +198,6 @@ less than `rdma_max_recv` so the receiving side doesn't run out of buffers.
|
|||||||
Doesn't affect memory usage - additional memory isn't allocated for send
|
Doesn't affect memory usage - additional memory isn't allocated for send
|
||||||
operations.
|
operations.
|
||||||
|
|
||||||
## rdma_odp
|
|
||||||
|
|
||||||
- Type: boolean
|
|
||||||
- Default: false
|
|
||||||
|
|
||||||
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
|
||||||
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
|
||||||
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
|
||||||
copying during sending. One would think this should improve performance, but
|
|
||||||
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
|
||||||
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
|
||||||
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
|
||||||
|
|
||||||
This happens because Mellanox ODP implementation seems to be based on
|
|
||||||
message retransmissions when the adapter doesn't know about the buffer yet -
|
|
||||||
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
|
||||||
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
|
||||||
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
ODP support is retained in the code just in case a good ODP implementation
|
|
||||||
appears one day.
|
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
|
|||||||
+18
-30
@@ -22,7 +22,6 @@
|
|||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
- [rdma_odp](#rdma_odp)
|
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
@@ -101,12 +100,6 @@ RoCEv1/RoCEv2, и даже позволяет полностью отключи
|
|||||||
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
|
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
|
||||||
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
|
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
|
||||||
|
|
||||||
Vitastor поддерживает все модели адаптеров, включая те, у которых
|
|
||||||
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
|
||||||
картами производства не Mellanox. Версии Vitastor до 1.2.0 включительно
|
|
||||||
требовали ODP, который есть только на Mellanox ConnectX 4 и более новых.
|
|
||||||
См. также [rdma_odp](#rdma_odp).
|
|
||||||
|
|
||||||
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
||||||
список доступных RDMA-устройств, их параметры и возможности.
|
список доступных RDMA-устройств, их параметры и возможности.
|
||||||
|
|
||||||
@@ -117,6 +110,24 @@ Vitastor поддерживает все модели адаптеров, вкл
|
|||||||
подразумевает настройку сети без потерь на основе PFC (Priority Flow
|
подразумевает настройку сети без потерь на основе PFC (Priority Flow
|
||||||
Control) и ECN (Explicit Congestion Notification).
|
Control) и ECN (Explicit Congestion Notification).
|
||||||
|
|
||||||
|
Vitastor поддерживает все модели адаптеров, включая те, у которых нет
|
||||||
|
поддержки ODP (On-Demand Paging), например, ConnectX-3 и карты производства
|
||||||
|
не Mellanox. Функция ODP доступна только на адаптерах Mellanox ConnectX-4 и
|
||||||
|
более новых и позволяет не регистрировать память для её использования RDMA-картой,
|
||||||
|
благодаря чему в теории можно избежать лишних копирований памяти.
|
||||||
|
|
||||||
|
Версии Vitastor до 1.2.0 включительно требовали ODP, потом функция был отключена
|
||||||
|
по умолчанию, но поддерживалась вплоть до версии 3.0.3. Сейчас поддержка ODP
|
||||||
|
полностью удалена, так как на самом деле она только портит производительность:
|
||||||
|
например, на 3-узловом кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на
|
||||||
|
чтение с RDMA без ODP удаётся снять 3950000 iops, а с ODP - всего 239000 iops.
|
||||||
|
|
||||||
|
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
||||||
|
основана на повторной передаче сообщений, когда карте не известен буфер -
|
||||||
|
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
||||||
|
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
||||||
|
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
|
|
||||||
## rdma_port_num
|
## rdma_port_num
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
@@ -192,29 +203,6 @@ OSD в любом случае согласовывают реальное зн
|
|||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
|
|
||||||
## rdma_odp
|
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
|
||||||
- Значение по умолчанию: false
|
|
||||||
|
|
||||||
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
|
||||||
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
|
||||||
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
|
||||||
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
|
||||||
улучшать производительность - но **по факту** получается так, что
|
|
||||||
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
|
||||||
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
|
||||||
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
|
||||||
|
|
||||||
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
|
||||||
основана на повторной передаче сообщений, когда карте не известен буфер -
|
|
||||||
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
|
||||||
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
|
||||||
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
|
||||||
прекрасный день появится хорошая реализация ODP.
|
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
|
|||||||
+47
-7
@@ -38,6 +38,7 @@ 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)
|
||||||
@@ -67,6 +68,8 @@ with an OSD restart or, for some of them, even without restarting by updating co
|
|||||||
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
|
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
|
||||||
- [atomic_write_size](#atomic_write_size)
|
- [atomic_write_size](#atomic_write_size)
|
||||||
- [use_atomic_flag](#use_atomic_flag)
|
- [use_atomic_flag](#use_atomic_flag)
|
||||||
|
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
|
||||||
|
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
|
||||||
|
|
||||||
## bind_address
|
## bind_address
|
||||||
|
|
||||||
@@ -277,13 +280,19 @@ Maximum number of journal flushers (see above min_flusher_count).
|
|||||||
- Type: boolean
|
- Type: boolean
|
||||||
- Default: true
|
- Default: true
|
||||||
|
|
||||||
This parameter makes Vitastor always keep metadata area of the block device
|
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2).
|
||||||
in memory. It's required for good performance because it allows to avoid
|
|
||||||
additional read-modify-write cycles during metadata modifications. Metadata
|
This parameter makes Vitastor keep a copy of metadata area in memory as it is
|
||||||
area size is currently roughly 224 MB per 1 TB of data. You can turn it off
|
on disk, in addition to the metadata database. When the option is enabled, every
|
||||||
to reduce memory usage by this value, but it will hurt performance. This
|
metadata entry is effectively stored in RAM twice. It's required for good performance
|
||||||
restriction is likely to be removed in the future along with the upgrade
|
because it allows to avoid additional read-modify-write cycles during metadata
|
||||||
of the metadata storage scheme.
|
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB
|
||||||
|
of data. You can turn the option off to reduce memory usage by this value, but
|
||||||
|
it will reduce performance.
|
||||||
|
|
||||||
|
For the new store ([meta_format](layout-osd.en.md#meta_format) 3), the option
|
||||||
|
may be changed in the future to support operation without loading full metadata
|
||||||
|
database in memory.
|
||||||
|
|
||||||
## inmemory_journal
|
## inmemory_journal
|
||||||
|
|
||||||
@@ -362,6 +371,8 @@ 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
|
||||||
@@ -375,6 +386,17 @@ 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
|
||||||
@@ -713,3 +735,21 @@ even though the NVMe specification allows them.
|
|||||||
For NVMe disks with `scheduler=none` writes aren't fragmented anyway so it's not a big deal.
|
For NVMe disks with `scheduler=none` writes aren't fragmented anyway so it's not a big deal.
|
||||||
However, you can rebuild your kernel with [this patch](../../patches/linux-fix-atomic-write-checks.diff)
|
However, you can rebuild your kernel with [this patch](../../patches/linux-fix-atomic-write-checks.diff)
|
||||||
and turn this option on. It will make your atomic writes a bit safer.
|
and turn this option on. It will make your atomic writes a bit safer.
|
||||||
|
|
||||||
|
## pg_reshard_chunk_size
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 100000
|
||||||
|
|
||||||
|
Pool PG count change is a CPU-intensive operation because OSDs store the full object database
|
||||||
|
in memory and have to move all entries between old and new PGs. Thus it's performed in chunks,
|
||||||
|
with pauses between chunks to prevent blocking OSD's event loop and other clients' operations.
|
||||||
|
This option sets the maximum number of object is a chunk. Moving 100k objects usually takes
|
||||||
|
50-100ms. Chunk size equal to 0 means unlimited.
|
||||||
|
|
||||||
|
## pg_reshard_chunk_pause_ms
|
||||||
|
|
||||||
|
- Type: milliseconds
|
||||||
|
- Default: 100
|
||||||
|
|
||||||
|
This option sets the interval between handling two PG count change chunks.
|
||||||
|
|||||||
+49
-7
@@ -39,6 +39,7 @@
|
|||||||
- [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)
|
||||||
@@ -68,6 +69,8 @@
|
|||||||
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
|
- [pg_lock_retry_interval_ms](#pg_lock_retry_interval_ms)
|
||||||
- [atomic_write_size](#atomic_write_size)
|
- [atomic_write_size](#atomic_write_size)
|
||||||
- [use_atomic_flag](#use_atomic_flag)
|
- [use_atomic_flag](#use_atomic_flag)
|
||||||
|
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
|
||||||
|
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
|
||||||
|
|
||||||
## bind_address
|
## bind_address
|
||||||
|
|
||||||
@@ -285,13 +288,19 @@ Flusher - это микро-поток (корутина), которая коп
|
|||||||
- Тип: булево (да/нет)
|
- Тип: булево (да/нет)
|
||||||
- Значение по умолчанию: true
|
- Значение по умолчанию: true
|
||||||
|
|
||||||
Данный параметр заставляет Vitastor всегда держать область метаданных диска
|
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2).
|
||||||
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
|
|
||||||
при записи. Размер области метаданных на данный момент составляет примерно
|
Данный параметр заставляет Vitastor всегда держать копию области метаданных
|
||||||
224 МБ на 1 ТБ данных. При включении потребление памяти снизится примерно
|
в памяти в том же виде, как она лежит на диске, в дополнение к БД метаданных.
|
||||||
на эту величину, но при этом также снизится и производительность. В будущем,
|
То есть, с включённой опцией каждая запись метаданных хранится в памяти дважды.
|
||||||
после обновления схемы хранения метаданных, это ограничение, скорее всего,
|
Это нужно, чтобы избегать дополнительных операций чтения с диска при записи.
|
||||||
будет ликвидировано.
|
Размер области метаданных в старом хранилище составляет примерно 224 МБ на
|
||||||
|
1 ТБ данных. Вы можете отключить опцию, чтобы снизить потребление памяти
|
||||||
|
примерно на эту величину, но при этом также снизится и производительность.
|
||||||
|
|
||||||
|
Для нового хранилища ([meta_format](layout-osd.en.md#meta_format) 3) опция,
|
||||||
|
возможно, будет переработана в будущем для поддержки работы без полной
|
||||||
|
загрузки метаданных в памяти.
|
||||||
|
|
||||||
## inmemory_journal
|
## inmemory_journal
|
||||||
|
|
||||||
@@ -374,6 +383,8 @@ 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
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
- Тип: булево (да/нет)
|
||||||
@@ -389,6 +400,18 @@ 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
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
- Тип: булево (да/нет)
|
||||||
@@ -751,3 +774,22 @@ pg_minsize OSD во время переключений, что может по
|
|||||||
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
|
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
|
||||||
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
|
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
|
||||||
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
|
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
|
||||||
|
|
||||||
|
## pg_reshard_chunk_size
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 100000
|
||||||
|
|
||||||
|
Изменение числа PG в пуле заметно загружает процессор, так как OSD хранят полную базу данных
|
||||||
|
объектов в памяти и им приходится перемещать все записи объектов между старыми и новыми PG.
|
||||||
|
Поэтому изменение применяется порциями, с паузами между порциями, чтобы не блокировать обработку
|
||||||
|
событий OSD и операции остальных клиентов. Данная опция задаёт максимальное число объектов
|
||||||
|
в порции. Перемещение 100 тысяч объектов (значение по умолчанию) обычно занимает порядка
|
||||||
|
50-100 миллисекунд. Значение опции 0 отключает лимит размера порции.
|
||||||
|
|
||||||
|
## pg_reshard_chunk_pause_ms
|
||||||
|
|
||||||
|
- Тип: миллисекунды
|
||||||
|
- Значение по умолчанию: 100
|
||||||
|
|
||||||
|
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
|
||||||
|
|||||||
+35
-50
@@ -84,11 +84,6 @@
|
|||||||
unsupported with old libibverbs < v32, like in Debian 10 Buster or
|
unsupported with old libibverbs < v32, like in Debian 10 Buster or
|
||||||
CentOS 7.
|
CentOS 7.
|
||||||
|
|
||||||
Vitastor supports all adapters, even ones without ODP support, like
|
|
||||||
Mellanox ConnectX-3 and non-Mellanox cards. Versions up to Vitastor
|
|
||||||
1.2.0 required ODP which is only present in Mellanox ConnectX >= 4.
|
|
||||||
See also [rdma_odp](#rdma_odp).
|
|
||||||
|
|
||||||
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
||||||
features.
|
features.
|
||||||
|
|
||||||
@@ -97,6 +92,23 @@
|
|||||||
the manual of your network vendor for details about setting up the switch
|
the manual of your network vendor for details about setting up the switch
|
||||||
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
|
for RoCEv2 correctly. Usually it means setting up Lossless Ethernet with
|
||||||
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
||||||
|
|
||||||
|
Vitastor supports all adapters, even ones without ODP (On-Demand Paging)
|
||||||
|
support, like Mellanox ConnectX-3 and non-Mellanox cards. ODP is only present
|
||||||
|
in Mellanox ConnectX >= 4 adapters and allows to skip memory registration
|
||||||
|
for RDMA and thus, in theory, avoid memory copying.
|
||||||
|
|
||||||
|
Versions up to Vitastor 1.2.0 required ODP, then it was disabled by default,
|
||||||
|
but it was still supported up to 3.0.3. Now ODP support is removed because it
|
||||||
|
actually only hurts performance: an example 3-node cluster with 8 NVMe in each
|
||||||
|
node and 2*25 GBit/s ConnectX-6 RDMA network pushed 3950000 read iops without
|
||||||
|
ODP, but only 239000 iops with ODP.
|
||||||
|
|
||||||
|
This happens because Mellanox ODP implementation seems to be based on
|
||||||
|
message retransmissions when the adapter doesn't know about the buffer yet -
|
||||||
|
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
||||||
|
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
||||||
|
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
||||||
Если не указано, Vitastor попробует найти RoCE-устройство, соответствующее
|
Если не указано, Vitastor попробует найти RoCE-устройство, соответствующее
|
||||||
@@ -105,12 +117,6 @@
|
|||||||
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
|
не задана. Также автовыбор не поддерживается со старыми версиями библиотеки
|
||||||
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
|
libibverbs < v32, например в Debian 10 Buster или CentOS 7.
|
||||||
|
|
||||||
Vitastor поддерживает все модели адаптеров, включая те, у которых
|
|
||||||
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
|
||||||
картами производства не Mellanox. Версии Vitastor до 1.2.0 включительно
|
|
||||||
требовали ODP, который есть только на Mellanox ConnectX 4 и более новых.
|
|
||||||
См. также [rdma_odp](#rdma_odp).
|
|
||||||
|
|
||||||
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
||||||
список доступных RDMA-устройств, их параметры и возможности.
|
список доступных RDMA-устройств, их параметры и возможности.
|
||||||
|
|
||||||
@@ -120,6 +126,24 @@
|
|||||||
коммутатора для RoCEv2 ищите в документации производителя. Обычно это
|
коммутатора для RoCEv2 ищите в документации производителя. Обычно это
|
||||||
подразумевает настройку сети без потерь на основе PFC (Priority Flow
|
подразумевает настройку сети без потерь на основе PFC (Priority Flow
|
||||||
Control) и ECN (Explicit Congestion Notification).
|
Control) и ECN (Explicit Congestion Notification).
|
||||||
|
|
||||||
|
Vitastor поддерживает все модели адаптеров, включая те, у которых нет
|
||||||
|
поддержки ODP (On-Demand Paging), например, ConnectX-3 и карты производства
|
||||||
|
не Mellanox. Функция ODP доступна только на адаптерах Mellanox ConnectX-4 и
|
||||||
|
более новых и позволяет не регистрировать память для её использования RDMA-картой,
|
||||||
|
благодаря чему в теории можно избежать лишних копирований памяти.
|
||||||
|
|
||||||
|
Версии Vitastor до 1.2.0 включительно требовали ODP, потом функция был отключена
|
||||||
|
по умолчанию, но поддерживалась вплоть до версии 3.0.3. Сейчас поддержка ODP
|
||||||
|
полностью удалена, так как на самом деле она только портит производительность:
|
||||||
|
например, на 3-узловом кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на
|
||||||
|
чтение с RDMA без ODP удаётся снять 3950000 iops, а с ODP - всего 239000 iops.
|
||||||
|
|
||||||
|
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
||||||
|
основана на повторной передаче сообщений, когда карте не известен буфер -
|
||||||
|
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
||||||
|
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
||||||
|
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
||||||
- name: rdma_port_num
|
- name: rdma_port_num
|
||||||
type: int
|
type: int
|
||||||
info: |
|
info: |
|
||||||
@@ -218,45 +242,6 @@
|
|||||||
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
- name: rdma_odp
|
|
||||||
type: bool
|
|
||||||
default: false
|
|
||||||
online: false
|
|
||||||
info: |
|
|
||||||
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
|
||||||
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
|
||||||
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
|
||||||
copying during sending. One would think this should improve performance, but
|
|
||||||
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
|
||||||
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
|
||||||
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
|
||||||
|
|
||||||
This happens because Mellanox ODP implementation seems to be based on
|
|
||||||
message retransmissions when the adapter doesn't know about the buffer yet -
|
|
||||||
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
|
||||||
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
|
||||||
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
ODP support is retained in the code just in case a good ODP implementation
|
|
||||||
appears one day.
|
|
||||||
info_ru: |
|
|
||||||
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
|
||||||
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
|
||||||
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
|
||||||
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
|
||||||
улучшать производительность - но **по факту** получается так, что
|
|
||||||
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
|
||||||
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
|
||||||
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
|
||||||
|
|
||||||
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
|
||||||
основана на повторной передаче сообщений, когда карте не известен буфер -
|
|
||||||
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
|
||||||
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
|
||||||
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
|
||||||
прекрасный день появится хорошая реализация ODP.
|
|
||||||
- name: peer_connect_interval
|
- name: peer_connect_interval
|
||||||
type: sec
|
type: sec
|
||||||
min: 1
|
min: 1
|
||||||
|
|||||||
+69
-14
@@ -253,21 +253,33 @@
|
|||||||
type: bool
|
type: bool
|
||||||
default: true
|
default: true
|
||||||
info: |
|
info: |
|
||||||
This parameter makes Vitastor always keep metadata area of the block device
|
Only for the old store ([meta_format](layout-osd.en.md#meta_format) 2).
|
||||||
in memory. It's required for good performance because it allows to avoid
|
|
||||||
additional read-modify-write cycles during metadata modifications. Metadata
|
This parameter makes Vitastor keep a copy of metadata area in memory as it is
|
||||||
area size is currently roughly 224 MB per 1 TB of data. You can turn it off
|
on disk, in addition to the metadata database. When the option is enabled, every
|
||||||
to reduce memory usage by this value, but it will hurt performance. This
|
metadata entry is effectively stored in RAM twice. It's required for good performance
|
||||||
restriction is likely to be removed in the future along with the upgrade
|
because it allows to avoid additional read-modify-write cycles during metadata
|
||||||
of the metadata storage scheme.
|
modifications. Metadata area size with the old store is roughly 224 MB per 1 TB
|
||||||
|
of data. You can turn the option off to reduce memory usage by this value, but
|
||||||
|
it will reduce performance.
|
||||||
|
|
||||||
|
For the new store ([meta_format](layout-osd.en.md#meta_format) 3), the option
|
||||||
|
may be changed in the future to support operation without loading full metadata
|
||||||
|
database in memory.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Данный параметр заставляет Vitastor всегда держать область метаданных диска
|
Только для старого хранилища ([meta_format](layout-osd.en.md#meta_format) 2).
|
||||||
в памяти. Это нужно, чтобы избегать дополнительных операций чтения с диска
|
|
||||||
при записи. Размер области метаданных на данный момент составляет примерно
|
Данный параметр заставляет 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
|
||||||
@@ -386,11 +398,15 @@
|
|||||||
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
|
||||||
@@ -402,6 +418,8 @@
|
|||||||
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, которые
|
||||||
ОЧЕНЬ не любят, когда ПО перезаписывает один и тот же сектор несколько раз
|
ОЧЕНЬ не любят, когда ПО перезаписывает один и тот же сектор несколько раз
|
||||||
@@ -412,6 +430,20 @@
|
|||||||
самого сектора.
|
самого сектора.
|
||||||
|
|
||||||
Почти все другие 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
|
||||||
@@ -883,3 +915,26 @@
|
|||||||
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
|
Для NVMe-дисков с `scheduler=none` запросы записи и так не фрагментируются, так что это не так
|
||||||
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
|
уж и важно, однако вы можете пересобрать своё ядро с [этим патчем](../../patches/linux-fix-atomic-write-checks.diff)
|
||||||
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
|
и включить данную опцию. Это сделает вашу атомарную запись капельку безопаснее.
|
||||||
|
- name: pg_reshard_chunk_size
|
||||||
|
type: int
|
||||||
|
default: 100000
|
||||||
|
info: |
|
||||||
|
Pool PG count change is a CPU-intensive operation because OSDs store the full object database
|
||||||
|
in memory and have to move all entries between old and new PGs. Thus it's performed in chunks,
|
||||||
|
with pauses between chunks to prevent blocking OSD's event loop and other clients' operations.
|
||||||
|
This option sets the maximum number of object is a chunk. Moving 100k objects usually takes
|
||||||
|
50-100ms. Chunk size equal to 0 means unlimited.
|
||||||
|
info_ru: |
|
||||||
|
Изменение числа PG в пуле заметно загружает процессор, так как OSD хранят полную базу данных
|
||||||
|
объектов в памяти и им приходится перемещать все записи объектов между старыми и новыми PG.
|
||||||
|
Поэтому изменение применяется порциями, с паузами между порциями, чтобы не блокировать обработку
|
||||||
|
событий OSD и операции остальных клиентов. Данная опция задаёт максимальное число объектов
|
||||||
|
в порции. Перемещение 100 тысяч объектов (значение по умолчанию) обычно занимает порядка
|
||||||
|
50-100 миллисекунд. Значение опции 0 отключает лимит размера порции.
|
||||||
|
- name: pg_reshard_chunk_pause_ms
|
||||||
|
type: ms
|
||||||
|
default: 100
|
||||||
|
info: |
|
||||||
|
This option sets the interval between handling two PG count change chunks.
|
||||||
|
info_ru: |
|
||||||
|
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ 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.1`
|
`docker pull vitalif/vitastor:v3.0.4`
|
||||||
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.1 install.sh`
|
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.4 install.sh`
|
||||||
3. Reload udev rules: \
|
3. Reload udev rules: \
|
||||||
`udevadm control --reload-rules`
|
`udevadm control --reload-rules`
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
|
|||||||
Инструкция по установке максимально простая.
|
Инструкция по установке максимально простая.
|
||||||
|
|
||||||
1. Скачайте Docker-образ желаемой версии: \
|
1. Скачайте Docker-образ желаемой версии: \
|
||||||
`docker pull vitalif/vitastor:v3.0.1`
|
`docker pull vitalif/vitastor:v3.0.4`
|
||||||
2. Установите скрипты в хост-систему командой: \
|
2. Установите скрипты в хост-систему командой: \
|
||||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.1 install.sh`
|
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.4 install.sh`
|
||||||
3. Перезагрузите правила udev: \
|
3. Перезагрузите правила udev: \
|
||||||
`udevadm control --reload-rules`
|
`udevadm control --reload-rules`
|
||||||
|
|
||||||
|
|||||||
@@ -33,15 +33,17 @@
|
|||||||
- 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 clones: not required
|
- RHEL 9/10 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
|
||||||
|
|||||||
@@ -33,15 +33,17 @@
|
|||||||
- 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: не нужно
|
- Клоны RHEL 9/10: не нужно
|
||||||
- Включите 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`
|
||||||
|
|
||||||
## Установочные требования
|
## Установочные требования
|
||||||
|
|||||||
@@ -10,16 +10,19 @@ const NO_OSD = 'Z';
|
|||||||
async function lp_solve(text)
|
async function lp_solve(text)
|
||||||
{
|
{
|
||||||
const cp = child_process.spawn('lp_solve');
|
const cp = child_process.spawn('lp_solve');
|
||||||
let stdout = '', stderr = '', finish_cb;
|
let stdout = '', stderr = '', finish_cb, finished = 0;
|
||||||
cp.stdout.on('data', buf => stdout += buf.toString());
|
cp.stdout.on('data', buf => stdout += buf.toString());
|
||||||
cp.stderr.on('data', buf => stderr += buf.toString());
|
cp.stderr.on('data', buf => stderr += buf.toString());
|
||||||
cp.on('exit', () => finish_cb && finish_cb());
|
cp.stdout.on('end', () => finish_cb());
|
||||||
|
cp.stderr.on('end', () => finish_cb());
|
||||||
cp.stdin.write(text);
|
cp.stdin.write(text);
|
||||||
cp.stdin.end();
|
cp.stdin.end();
|
||||||
if (cp.exitCode == null)
|
await new Promise(ok => (finish_cb = () =>
|
||||||
{
|
{
|
||||||
await new Promise(ok => finish_cb = ok);
|
finished++;
|
||||||
}
|
if (finished == 2)
|
||||||
|
ok();
|
||||||
|
}));
|
||||||
if (!stdout.trim())
|
if (!stdout.trim())
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vitastor-mon",
|
"name": "vitastor-mon",
|
||||||
"version": "3.0.1",
|
"version": "3.0.4",
|
||||||
"description": "Vitastor SDS monitor service",
|
"description": "Vitastor SDS monitor service",
|
||||||
"main": "mon-main.js",
|
"main": "mon-main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"author": "Vitaliy Filippov",
|
"author": "Vitaliy Filippov",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antietcd": "^1.1.3",
|
"antietcd": "^1.2.4",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"ws": "^7.2.5"
|
"ws": "^7.2.5"
|
||||||
},
|
},
|
||||||
|
|||||||
+15
-1
@@ -52,6 +52,7 @@ function recheck_primary(state, global_config, up_osds, osd_tree)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const aff_osds = get_affinity_osds(pool_cfg, up_osds, osd_tree);
|
const aff_osds = get_affinity_osds(pool_cfg, up_osds, osd_tree);
|
||||||
|
let paused = false;
|
||||||
for (let pg_num = 1; pg_num <= pool_cfg.pg_count; pg_num++)
|
for (let pg_num = 1; pg_num <= pool_cfg.pg_count; pg_num++)
|
||||||
{
|
{
|
||||||
if (!state.pg.config.items[pool_id])
|
if (!state.pg.config.items[pool_id])
|
||||||
@@ -74,6 +75,19 @@ function recheck_primary(state, global_config, up_osds, osd_tree)
|
|||||||
);
|
);
|
||||||
new_pg_config.items[pool_id][pg_num].primary = new_primary;
|
new_pg_config.items[pool_id][pg_num].primary = new_primary;
|
||||||
}
|
}
|
||||||
|
paused = paused || !!pg_cfg.pause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paused)
|
||||||
|
{
|
||||||
|
if (!new_pg_config)
|
||||||
|
{
|
||||||
|
new_pg_config = JSON.parse(JSON.stringify(state.pg.config));
|
||||||
|
}
|
||||||
|
console.log(`Resuming paused pool ${pool_id}`);
|
||||||
|
for (const pg in new_pg_config.items[pool_id])
|
||||||
|
{
|
||||||
|
delete new_pg_config.items[pool_id][pg].pause;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +195,7 @@ async function generate_pool_pgs(state, global_config, pool_id, osd_tree, levels
|
|||||||
const folded_tree = make_hier_tree(global_config, folded.nodes.reduce((a, c) => { a[c.id] = c; return a; }, {}));
|
const folded_tree = make_hier_tree(global_config, folded.nodes.reduce((a, c) => { a[c.id] = c; return a; }, {}));
|
||||||
const old_pg_count = prev_pgs.length;
|
const old_pg_count = prev_pgs.length;
|
||||||
const optimize_cfg = {
|
const optimize_cfg = {
|
||||||
osd_weights: folded.nodes.reduce((a, c) => { if (Number(c.id)) { a[c.id] = c.size; } return a; }, {}),
|
osd_weights: folded.nodes.reduce((a, c) => { if (/^\d+$/.exec(c.id) && c.size != null) { a[c.id] = c.size||0; } return a; }, {}),
|
||||||
combinator: use_rules
|
combinator: use_rules
|
||||||
// new algorithm:
|
// new algorithm:
|
||||||
? new RuleCombinator(folded_tree, rules, pool_cfg.max_osd_combinations)
|
? new RuleCombinator(folded_tree, rules, pool_cfg.max_osd_combinations)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vitastor",
|
"name": "vitastor",
|
||||||
"version": "3.0.1",
|
"version": "3.0.4",
|
||||||
"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": [
|
||||||
|
|||||||
@@ -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.1'
|
VITASTOR_VERSION = '3.0.4'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ rpmbuild -bp fio.spec
|
|||||||
cd $VITASTOR
|
cd $VITASTOR
|
||||||
VER=$(grep ^Version: rpm/vitastor-$REL.spec | awk '{print $2}')
|
VER=$(grep ^Version: rpm/vitastor-$REL.spec | awk '{print $2}')
|
||||||
rm -rf fio
|
rm -rf fio
|
||||||
ln -s ~/rpmbuild/BUILD/fio*/ fio
|
ln -s $(ls -d ~/rpmbuild/BUILD/fio*/ | grep -v SPECPARTS) fio
|
||||||
sh copy-fio-includes.sh
|
sh copy-fio-includes.sh
|
||||||
rm fio
|
rm fio
|
||||||
mv fio-copy fio
|
mv fio-copy fio
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Build packages for AlmaLinux 10 inside a container
|
||||||
|
# cd ..
|
||||||
|
# docker pull --platform=linux/amd64/v2 quay.io/almalinuxorg/almalinux:10
|
||||||
|
# docker build -t vitastor-buildenv:el10 -f rpm/vitastor-el10.Dockerfile .
|
||||||
|
# docker run -i --rm -v ./:/root/vitastor vitastor-buildenv:el10 /root/vitastor/rpm/vitastor-build.sh
|
||||||
|
|
||||||
|
FROM quay.io/almalinuxorg/almalinux:10
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
|
||||||
|
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
|
||||||
|
RUN dnf -y install epel-release dnf-plugins-core
|
||||||
|
RUN dnf -y install https://vitastor.io/rpms/centos/10/vitastor-release-1.0-1.el10.noarch.rpm
|
||||||
|
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel gf-complete-devel rdma-core-devel cmake libnl3-devel
|
||||||
|
RUN dnf download --source fio
|
||||||
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
|
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
Name: vitastor
|
||||||
|
Version: 3.0.4
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
|
License: Vitastor Network Public License 1.1
|
||||||
|
URL: https://vitastor.io/
|
||||||
|
Source0: vitastor-3.0.4.el10.tar.gz
|
||||||
|
|
||||||
|
BuildRequires: gperftools-devel
|
||||||
|
BuildRequires: gcc-c++
|
||||||
|
BuildRequires: nodejs >= 10
|
||||||
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: isa-l-devel
|
||||||
|
BuildRequires: gf-complete-devel
|
||||||
|
BuildRequires: rdma-core-devel
|
||||||
|
BuildRequires: cmake
|
||||||
|
BuildRequires: libnl3-devel
|
||||||
|
Requires: vitastor-osd = %{version}-%{release}
|
||||||
|
Requires: vitastor-mon = %{version}-%{release}
|
||||||
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
Requires: vitastor-client-devel = %{version}-%{release}
|
||||||
|
Requires: vitastor-fio = %{version}-%{release}
|
||||||
|
|
||||||
|
%description
|
||||||
|
Vitastor is a small, simple and fast clustered block storage (storage for VM drives),
|
||||||
|
architecturally similar to Ceph which means strong consistency, primary-replication,
|
||||||
|
symmetric clustering and automatic data distribution over any number of drives of any
|
||||||
|
size with configurable redundancy (replication or erasure codes/XOR).
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-osd
|
||||||
|
Summary: Vitastor - OSD
|
||||||
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
Requires: util-linux
|
||||||
|
Requires: parted
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-osd
|
||||||
|
Vitastor object storage daemon, i.e. server program that stores data.
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-mon
|
||||||
|
Summary: Vitastor - monitor
|
||||||
|
Requires: nodejs >= 10
|
||||||
|
Requires: lpsolve
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-mon
|
||||||
|
Vitastor monitor, i.e. server program responsible for watching cluster state and
|
||||||
|
scheduling cluster-level operations.
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-client
|
||||||
|
Summary: Vitastor - client
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-client
|
||||||
|
Vitastor client library and command-line interface.
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-client-devel
|
||||||
|
Summary: Vitastor - development files
|
||||||
|
Group: Development/Libraries
|
||||||
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-client-devel
|
||||||
|
Vitastor library headers for development.
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-fio
|
||||||
|
Summary: Vitastor - fio drivers
|
||||||
|
Group: Development/Libraries
|
||||||
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
Requires: fio = 3.36-5.el10
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-fio
|
||||||
|
Vitastor fio drivers for benchmarking.
|
||||||
|
|
||||||
|
|
||||||
|
%package -n vitastor-opennebula
|
||||||
|
Summary: Vitastor for OpenNebula
|
||||||
|
Group: Development/Libraries
|
||||||
|
Requires: vitastor-client
|
||||||
|
Requires: jq
|
||||||
|
Requires: python3-lxml
|
||||||
|
Requires: patch
|
||||||
|
Requires: qemu-kvm-block-vitastor
|
||||||
|
|
||||||
|
|
||||||
|
%description -n vitastor-opennebula
|
||||||
|
Vitastor storage plugin for OpenNebula.
|
||||||
|
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q
|
||||||
|
|
||||||
|
|
||||||
|
%build
|
||||||
|
%cmake
|
||||||
|
%cmake_build
|
||||||
|
|
||||||
|
|
||||||
|
%install
|
||||||
|
rm -rf $RPM_BUILD_ROOT
|
||||||
|
%cmake_install
|
||||||
|
cd mon
|
||||||
|
npm install --production
|
||||||
|
cd ..
|
||||||
|
mkdir -p %buildroot/usr/lib/vitastor
|
||||||
|
cp -r mon %buildroot/usr/lib/vitastor
|
||||||
|
mv %buildroot/usr/lib/vitastor/mon/scripts/make-etcd %buildroot/usr/lib/vitastor/mon/
|
||||||
|
mkdir -p %buildroot/lib/systemd/system
|
||||||
|
cp mon/scripts/vitastor.target mon/scripts/vitastor-mon.service mon/scripts/vitastor-osd@.service %buildroot/lib/systemd/system
|
||||||
|
mkdir -p %buildroot/lib/udev/rules.d
|
||||||
|
cp mon/scripts/90-vitastor.rules %buildroot/lib/udev/rules.d
|
||||||
|
mkdir -p %buildroot/var/lib/one
|
||||||
|
cp -r opennebula/remotes %buildroot/var/lib/one
|
||||||
|
cp opennebula/install.sh %buildroot/var/lib/one/remotes/datastore/vitastor/
|
||||||
|
mkdir -p %buildroot/etc/
|
||||||
|
cp -r opennebula/sudoers.d %buildroot/etc/
|
||||||
|
|
||||||
|
|
||||||
|
%files
|
||||||
|
%doc GPL-2.0.txt VNPL-1.1.txt README.md README-ru.md
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-osd
|
||||||
|
%_bindir/vitastor-osd
|
||||||
|
%_bindir/vitastor-disk
|
||||||
|
%_bindir/vitastor-dump-journal
|
||||||
|
/lib/systemd/system/vitastor-osd@.service
|
||||||
|
/lib/systemd/system/vitastor.target
|
||||||
|
/lib/udev/rules.d/90-vitastor.rules
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-osd
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-mon
|
||||||
|
/usr/lib/vitastor/mon
|
||||||
|
/lib/systemd/system/vitastor-mon.service
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-mon
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
mkdir -p /var/lib/vitastor
|
||||||
|
chown vitastor:vitastor /var/lib/vitastor
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-client
|
||||||
|
%_bindir/vitastor-nbd
|
||||||
|
%_bindir/vitastor-ublk
|
||||||
|
%_bindir/vitastor-nfs
|
||||||
|
%_bindir/vitastor-cli
|
||||||
|
%_bindir/vitastor-rm
|
||||||
|
%_bindir/vitastor-kv
|
||||||
|
%_bindir/vitastor-kv-stress
|
||||||
|
%_bindir/vita
|
||||||
|
%_libdir/libvitastor_client.so*
|
||||||
|
%_libdir/libvitastor_kv.so*
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-client-devel
|
||||||
|
%_includedir/vitastor_c.h
|
||||||
|
%_includedir/vitastor_kv.h
|
||||||
|
%_libdir/pkgconfig
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-fio
|
||||||
|
%_libdir/libfio_vitastor.so
|
||||||
|
%_libdir/libfio_vitastor_blk.so
|
||||||
|
%_libdir/libfio_vitastor_sec.so
|
||||||
|
|
||||||
|
|
||||||
|
%files -n vitastor-opennebula
|
||||||
|
/var/lib/one
|
||||||
|
/etc/sudoers.d/opennebula-vitastor
|
||||||
|
|
||||||
|
|
||||||
|
%triggerin -n vitastor-opennebula -- opennebula
|
||||||
|
[ $2 = 0 ] || exit 0
|
||||||
|
/var/lib/one/remotes/datastore/vitastor/install.sh
|
||||||
|
|
||||||
|
|
||||||
|
# Turn off the brp-python-bytecompile script
|
||||||
|
%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g')
|
||||||
|
|
||||||
|
|
||||||
|
%changelog
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.1
|
Version: 3.0.4
|
||||||
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.1.el7.tar.gz
|
Source0: vitastor-3.0.4.el7.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: devtoolset-9-gcc-c++
|
BuildRequires: devtoolset-9-gcc-c++
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.1
|
Version: 3.0.4
|
||||||
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.1.el8.tar.gz
|
Source0: vitastor-3.0.4.el8.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-toolset-9-gcc-c++
|
BuildRequires: gcc-toolset-9-gcc-c++
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.1
|
Version: 3.0.4
|
||||||
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.1.el9.tar.gz
|
Source0: vitastor-3.0.4.el9.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
|
|||||||
+1
-1
@@ -21,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.1")
|
add_definitions(-DVITASTOR_VERSION="3.0.4")
|
||||||
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})
|
||||||
|
|||||||
@@ -183,6 +183,11 @@ public:
|
|||||||
// Update configuration
|
// Update configuration
|
||||||
virtual void parse_config(blockstore_config_t & config) = 0;
|
virtual void parse_config(blockstore_config_t & config) = 0;
|
||||||
|
|
||||||
|
// Reshard database for a pool in chunks
|
||||||
|
// MUST be called only when nobody makes any modifications to the DB for this pool
|
||||||
|
virtual void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit) = 0;
|
||||||
|
virtual bool reshard_continue(void *reshard_state, uint64_t chunk_limit) = 0;
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
virtual void loop() = 0;
|
virtual void loop() = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,10 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Data block size must be a multiple of sparse write tracking granularity");
|
throw std::runtime_error("Data block size must be a multiple of sparse write tracking granularity");
|
||||||
}
|
}
|
||||||
|
if (data_block_size / bitmap_granularity < 8)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Data block size must be at least bitmap_granularity*8");
|
||||||
|
}
|
||||||
if (!data_csum_type)
|
if (!data_csum_type)
|
||||||
{
|
{
|
||||||
csum_block_size = 0;
|
csum_block_size = 0;
|
||||||
@@ -259,7 +263,7 @@ void blockstore_disk_t::calc_lengths(bool skip_meta_check)
|
|||||||
}
|
}
|
||||||
// required metadata size
|
// required metadata size
|
||||||
block_count = data_len / data_block_size;
|
block_count = data_len / data_block_size;
|
||||||
clean_entry_bitmap_size = data_block_size / bitmap_granularity / 8;
|
clean_entry_bitmap_size = (data_block_size / bitmap_granularity + 7) / 8;
|
||||||
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
|
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
|
||||||
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
|
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
|
||||||
recalc:
|
recalc:
|
||||||
|
|||||||
@@ -58,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;
|
||||||
bool do_repeat = false;
|
|
||||||
|
|
||||||
friend class journal_flusher_t;
|
friend class journal_flusher_t;
|
||||||
|
|
||||||
|
|||||||
+265
-134
@@ -29,6 +29,15 @@
|
|||||||
#define IMAP_MALLOC_LOW_BITS ((size_t)0x0F)
|
#define IMAP_MALLOC_LOW_BITS ((size_t)0x0F)
|
||||||
#define IMAP_MAX_LOW 16
|
#define IMAP_MAX_LOW 16
|
||||||
|
|
||||||
|
void inode_map_put(void* & inode_idx, heap_list_item_t* li);
|
||||||
|
void inode_map_get(void *inode_idx, heap_inode_map_t::iterator & li_it, heap_list_item_t* & li, uint64_t stripe);
|
||||||
|
void inode_map_free(void* inode_idx);
|
||||||
|
bool inode_map_is_big(void* & inode_idx);
|
||||||
|
void inode_map_iterate(void* & inode_idx, std::function<void(heap_list_item_t*)> cb);
|
||||||
|
void inode_map_replace(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* new_li);
|
||||||
|
void inode_map_erase(robin_hood::unordered_flat_map<inode_t, void*, i64hash_t> & pg_idx, void* & inode_idx,
|
||||||
|
const heap_inode_map_t::iterator & li_it, heap_list_item_t* li);
|
||||||
|
|
||||||
static inline heap_list_item_t *list_item(heap_entry_t *wr)
|
static inline heap_list_item_t *list_item(heap_entry_t *wr)
|
||||||
{
|
{
|
||||||
return (heap_list_item_t*)((uint8_t*)wr - offsetof(struct heap_list_item_t, entry));
|
return (heap_list_item_t*)((uint8_t*)wr - offsetof(struct heap_list_item_t, entry));
|
||||||
@@ -298,6 +307,7 @@ corrupted_block:
|
|||||||
if (allow_corrupted)
|
if (allow_corrupted)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Metadata block is corrupted, skipping\n");
|
fprintf(stderr, "Metadata block is corrupted, skipping\n");
|
||||||
|
recheck_modified_blocks.insert(block_num);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -322,7 +332,19 @@ corrupted_block:
|
|||||||
block_num, block_offset, wr->size, sizeof(heap_entry_t));
|
block_num, block_offset, wr->size, sizeof(heap_entry_t));
|
||||||
goto corrupted_block;
|
goto corrupted_block;
|
||||||
}
|
}
|
||||||
wr->entry_type &= ~BS_HEAP_GARBAGE;
|
if (wr->is_garbage())
|
||||||
|
{
|
||||||
|
// Garbage collection is only performed when writing new entries into the block
|
||||||
|
// because it needs a fake LSN and modified blocks require consecutive modified LSNs
|
||||||
|
// That's why garbage entries may persist on disk
|
||||||
|
if (log_level > 5)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Notice: skipping garbage entry %jx:%jx v%ju l%ju in metadata block %u at %u\n",
|
||||||
|
wr->inode, wr->stripe, wr->version, wr->lsn, block_num, block_offset);
|
||||||
|
}
|
||||||
|
block_offset += wr->size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if ((wr->entry_type & BS_HEAP_TYPE) < BS_HEAP_BIG_WRITE ||
|
if ((wr->entry_type & BS_HEAP_TYPE) < BS_HEAP_BIG_WRITE ||
|
||||||
(wr->entry_type & BS_HEAP_TYPE) > BS_HEAP_ROLLBACK ||
|
(wr->entry_type & BS_HEAP_TYPE) > BS_HEAP_ROLLBACK ||
|
||||||
(wr->entry_type & ~(BS_HEAP_TYPE|BS_HEAP_STABLE)) ||
|
(wr->entry_type & ~(BS_HEAP_TYPE|BS_HEAP_STABLE)) ||
|
||||||
@@ -336,6 +358,7 @@ corrupted_object:
|
|||||||
if (allow_corrupted)
|
if (allow_corrupted)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Entry is corrupted, skipping\n");
|
fprintf(stderr, "Entry is corrupted, skipping\n");
|
||||||
|
recheck_modified_blocks.insert(block_num);
|
||||||
block_offset += wr->size;
|
block_offset += wr->size;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -351,7 +374,7 @@ corrupted_object:
|
|||||||
{
|
{
|
||||||
// Small writes require accessing offset & len to calculate correct length,
|
// Small writes require accessing offset & len to calculate correct length,
|
||||||
// so require at least sizeof(heap_small_write_t) for them
|
// so require at least sizeof(heap_small_write_t) for them
|
||||||
fprintf(stderr, "Error: entry %jx:%jx v%ju has invalid size in metadata block %u at %u (%u < min %zu bytes). Metadata is corrupted, aborting\n",
|
fprintf(stderr, "Error: entry %jx:%jx v%ju has invalid size in metadata block %u at %u (%u < min %zu bytes)\n",
|
||||||
wr->inode, wr->stripe, wr->version, block_num, block_offset, wr->size, sizeof(heap_small_write_t));
|
wr->inode, wr->stripe, wr->version, block_num, block_offset, wr->size, sizeof(heap_small_write_t));
|
||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
}
|
}
|
||||||
@@ -365,8 +388,8 @@ corrupted_object:
|
|||||||
uint32_t expected_crc32c = wr->calc_crc32c();
|
uint32_t expected_crc32c = wr->calc_crc32c();
|
||||||
if (wr->crc32c != expected_crc32c)
|
if (wr->crc32c != expected_crc32c)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: entry %jx:%jx v%ju in metadata block %u at %u is corrupt (crc32c mismatch: expected %08x, got %08x). Metadata is corrupted, aborting\n",
|
fprintf(stderr, "Error: entry %jx:%jx v%ju l%ju in metadata block %u at %u is corrupt (crc32c mismatch: expected %08x, got %08x). ",
|
||||||
wr->inode, wr->stripe, wr->version,
|
wr->inode, wr->stripe, wr->version, wr->lsn,
|
||||||
block_num, block_offset, expected_crc32c, wr->crc32c);
|
block_num, block_offset, expected_crc32c, wr->crc32c);
|
||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
}
|
}
|
||||||
@@ -376,7 +399,7 @@ corrupted_object:
|
|||||||
wr->small().offset % dsk->bitmap_granularity ||
|
wr->small().offset % dsk->bitmap_granularity ||
|
||||||
wr->small().len % dsk->bitmap_granularity))
|
wr->small().len % dsk->bitmap_granularity))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: %s entry %jx:%jx v%ju has invalid offset/length: %u/%u. Metadata is incompatible with current parameters, aborting\n",
|
fprintf(stderr, "Error: %s entry %jx:%jx v%ju has invalid offset/length: %u/%u. Metadata is incompatible with current parameters. ",
|
||||||
wr->type() == BS_HEAP_SMALL_WRITE ? "small_write" : "intent_write",
|
wr->type() == BS_HEAP_SMALL_WRITE ? "small_write" : "intent_write",
|
||||||
wr->inode, wr->stripe, wr->version, wr->small().offset, wr->small().len);
|
wr->inode, wr->stripe, wr->version, wr->small().offset, wr->small().len);
|
||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
@@ -386,7 +409,7 @@ corrupted_object:
|
|||||||
wr->big_intent().offset % dsk->bitmap_granularity ||
|
wr->big_intent().offset % dsk->bitmap_granularity ||
|
||||||
wr->big_intent().len % dsk->bitmap_granularity))
|
wr->big_intent().len % dsk->bitmap_granularity))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: big_intent entry %jx:%jx v%ju has invalid offset/length: %u/%u. Metadata is incompatible with current parameters, aborting\n",
|
fprintf(stderr, "Error: big_intent entry %jx:%jx v%ju has invalid offset/length: %u/%u. Metadata is incompatible with current parameters. ",
|
||||||
wr->inode, wr->stripe, wr->version, wr->big_intent().offset, wr->big_intent().len);
|
wr->inode, wr->stripe, wr->version, wr->big_intent().offset, wr->big_intent().len);
|
||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
}
|
}
|
||||||
@@ -413,7 +436,7 @@ int blockstore_heap_t::load_blocks(uint64_t disk_offset, uint64_t size, uint8_t
|
|||||||
next_lsn = wr->lsn;
|
next_lsn = wr->lsn;
|
||||||
}
|
}
|
||||||
entries_loaded++;
|
entries_loaded++;
|
||||||
insert_list_item(li);
|
loaded_list_items.push_back(li);
|
||||||
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
||||||
{
|
{
|
||||||
if (!inf.entries.size())
|
if (!inf.entries.size())
|
||||||
@@ -449,19 +472,22 @@ bool blockstore_heap_t::validate_object(heap_entry_t *obj)
|
|||||||
next_wr = wr;
|
next_wr = wr;
|
||||||
if (wr->type() == BS_HEAP_ROLLBACK)
|
if (wr->type() == BS_HEAP_ROLLBACK)
|
||||||
{
|
{
|
||||||
if (commit_wr && wr->version > commit_wr->version)
|
|
||||||
{
|
|
||||||
// rollback may not come before commit with a smaller version
|
|
||||||
fprintf(stderr, "Error: rollback entry %jx:%jx v%ju l%ju comes before a commit entry v%ju l%ju\n",
|
|
||||||
wr->inode, wr->stripe, wr->version, wr->lsn, commit_wr->version, commit_wr->lsn);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rollback_wr = wr;
|
rollback_wr = wr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (wr->type() == BS_HEAP_COMMIT)
|
if (wr->type() == BS_HEAP_COMMIT)
|
||||||
{
|
{
|
||||||
commit_wr = wr;
|
if (commit_wr && wr->version > commit_wr->version)
|
||||||
|
{
|
||||||
|
// commit may not come before commit with a smaller version
|
||||||
|
fprintf(stderr, "Error: commit entry %jx:%jx v%ju l%ju comes before a commit entry v%ju l%ju\n",
|
||||||
|
wr->inode, wr->stripe, wr->version, wr->lsn, commit_wr->version, commit_wr->lsn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!commit_wr)
|
||||||
|
{
|
||||||
|
commit_wr = wr;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (wr->entry_type & BS_HEAP_STABLE)
|
if (wr->entry_type & BS_HEAP_STABLE)
|
||||||
@@ -514,6 +540,23 @@ bool blockstore_heap_t::validate_object(heap_entry_t *obj)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blockstore_heap_t::finish_load()
|
||||||
|
{
|
||||||
|
if (loaded_list_items.size())
|
||||||
|
{
|
||||||
|
// Sort everything and load in correct order
|
||||||
|
std::sort(loaded_list_items.begin(), loaded_list_items.end(), [this](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||||
|
{
|
||||||
|
return a->entry.lsn < b->entry.lsn;
|
||||||
|
});
|
||||||
|
for (auto & li: loaded_list_items)
|
||||||
|
{
|
||||||
|
insert_list_item(li);
|
||||||
|
}
|
||||||
|
loaded_list_items.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::fill_recheck_queue()
|
void blockstore_heap_t::fill_recheck_queue()
|
||||||
{
|
{
|
||||||
for (auto & pgp: block_index)
|
for (auto & pgp: block_index)
|
||||||
@@ -658,7 +701,8 @@ void blockstore_heap_t::recheck_buffer(heap_entry_t *cwr, uint8_t *buf)
|
|||||||
else if (!calc_checksums(cwr, buf, false))
|
else if (!calc_checksums(cwr, buf, false))
|
||||||
{
|
{
|
||||||
// write entry is invalid, erase it and mark newer entries with garbage bit
|
// write entry is invalid, erase it and mark newer entries with garbage bit
|
||||||
auto & inode_idx = block_index[get_pg_id(cwr->inode, cwr->stripe)][cwr->inode];
|
auto & pg_idx = block_index[get_pg_id(cwr->inode, cwr->stripe)];
|
||||||
|
auto & inode_idx = pg_idx[cwr->inode];
|
||||||
heap_inode_map_t::iterator li_it;
|
heap_inode_map_t::iterator li_it;
|
||||||
heap_list_item_t *li = NULL;
|
heap_list_item_t *li = NULL;
|
||||||
inode_map_get(inode_idx, li_it, li, cwr->stripe);
|
inode_map_get(inode_idx, li_it, li, cwr->stripe);
|
||||||
@@ -684,7 +728,7 @@ void blockstore_heap_t::recheck_buffer(heap_entry_t *cwr, uint8_t *buf)
|
|||||||
{
|
{
|
||||||
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n",
|
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n",
|
||||||
cwr->inode, cwr->stripe);
|
cwr->inode, cwr->stripe);
|
||||||
inode_map_erase(inode_idx, li_it, li);
|
inode_map_erase(pg_idx, inode_idx, li_it, li);
|
||||||
}
|
}
|
||||||
free_entry(li);
|
free_entry(li);
|
||||||
}
|
}
|
||||||
@@ -699,6 +743,7 @@ bool blockstore_heap_t::recheck_small_writes(std::function<void(bool is_data, ui
|
|||||||
}
|
}
|
||||||
if (!recheck_queue_filled)
|
if (!recheck_queue_filled)
|
||||||
{
|
{
|
||||||
|
finish_load();
|
||||||
fill_recheck_queue();
|
fill_recheck_queue();
|
||||||
recheck_queue_filled = true;
|
recheck_queue_filled = true;
|
||||||
}
|
}
|
||||||
@@ -790,7 +835,7 @@ std::vector<uint32_t> blockstore_heap_t::get_recheck_modified_blocks()
|
|||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
int blockstore_heap_t::finish_load(bool allow_corrupted)
|
int blockstore_heap_t::finish_recheck()
|
||||||
{
|
{
|
||||||
if (!marked_used_blocks)
|
if (!marked_used_blocks)
|
||||||
{
|
{
|
||||||
@@ -827,7 +872,6 @@ bool blockstore_heap_t::calc_checksums(heap_entry_t *wr, uint8_t *data, bool set
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
uint32_t len = 0;
|
|
||||||
if (wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE)
|
if (wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE)
|
||||||
len = wr->small().len;
|
len = wr->small().len;
|
||||||
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||||
@@ -844,13 +888,14 @@ bool blockstore_heap_t::calc_checksums(heap_entry_t *wr, uint8_t *data, bool set
|
|||||||
}
|
}
|
||||||
if (wr->type() == BS_HEAP_BIG_WRITE)
|
if (wr->type() == BS_HEAP_BIG_WRITE)
|
||||||
{
|
{
|
||||||
|
assert(offset != UINT32_MAX && len != UINT32_MAX);
|
||||||
return calc_block_checksums((uint32_t*)(wr->get_checksums(this) + offset/dsk->csum_block_size * (dsk->data_csum_type & 0xFF)),
|
return calc_block_checksums((uint32_t*)(wr->get_checksums(this) + offset/dsk->csum_block_size * (dsk->data_csum_type & 0xFF)),
|
||||||
data, wr->get_int_bitmap(this), offset, offset+len, set, NULL);
|
data, wr->get_int_bitmap(this), offset, offset+len, set, NULL);
|
||||||
}
|
}
|
||||||
if (wr->type() == BS_HEAP_BIG_INTENT)
|
if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||||
{
|
{
|
||||||
auto & bi = wr->big_intent();
|
auto & bi = wr->big_intent();
|
||||||
return calc_block_checksums((uint32_t*)(wr->get_checksums(this) + offset/dsk->csum_block_size * (dsk->data_csum_type & 0xFF)),
|
return calc_block_checksums((uint32_t*)(wr->get_checksums(this) + bi.offset/dsk->csum_block_size * (dsk->data_csum_type & 0xFF)),
|
||||||
data, wr->get_int_bitmap(this), bi.offset, bi.offset+bi.len, set, NULL);
|
data, wr->get_int_bitmap(this), bi.offset, bi.offset+bi.len, set, NULL);
|
||||||
}
|
}
|
||||||
assert(wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE);
|
assert(wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE);
|
||||||
@@ -943,44 +988,128 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bit
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::reshard(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size)
|
struct heap_reshard_state_t
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
uint64_t pool_id = 0;
|
||||||
|
uint32_t old_pg_count = 0;
|
||||||
|
uint32_t pg_count = 0;
|
||||||
|
uint32_t pg_stripe_size = 0;
|
||||||
|
uint64_t chunk_size = 0;
|
||||||
|
heap_block_index_t new_shards;
|
||||||
|
heap_block_index_t old_shards;
|
||||||
|
heap_block_index_t::iterator sh_it;
|
||||||
|
robin_hood::unordered_flat_map<inode_t, void*, i64hash_t>::iterator inode_it;
|
||||||
|
heap_inode_map_t *stripe_map = NULL;
|
||||||
|
heap_inode_map_t::iterator stripe_it;
|
||||||
|
|
||||||
|
void add(heap_list_item_t *li);
|
||||||
|
bool run(uint64_t chunk_limit);
|
||||||
|
};
|
||||||
|
|
||||||
|
void heap_reshard_state_t::add(heap_list_item_t *li)
|
||||||
|
{
|
||||||
|
// like map_to_pg()
|
||||||
|
uint64_t pg_num = (li->entry.stripe / pg_stripe_size) % pg_count + 1;
|
||||||
|
uint64_t shard_id = (pool_id << (64-POOL_ID_BITS)) | pg_num;
|
||||||
|
inode_map_put(new_shards[shard_id][li->entry.inode], li);
|
||||||
|
chunk_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool heap_reshard_state_t::run(uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
chunk_size = 0;
|
||||||
|
if (state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
else if (state == 2)
|
||||||
|
goto resume_2;
|
||||||
|
sh_it = old_shards.begin();
|
||||||
|
for (; sh_it != old_shards.end(); sh_it++)
|
||||||
|
{
|
||||||
|
inode_it = sh_it->second.begin();
|
||||||
|
for (; inode_it != sh_it->second.end(); inode_it++)
|
||||||
|
{
|
||||||
|
if (!inode_map_is_big(inode_it->second))
|
||||||
|
{
|
||||||
|
if (chunk_limit > 0 && chunk_size >= chunk_limit)
|
||||||
|
{
|
||||||
|
state = 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resume_1:
|
||||||
|
inode_map_iterate(inode_it->second, [&](heap_list_item_t *li) { add(li); });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stripe_map = (heap_inode_map_t*)inode_it->second;
|
||||||
|
stripe_it = stripe_map->begin();
|
||||||
|
for (; stripe_it != stripe_map->end(); stripe_it++)
|
||||||
|
{
|
||||||
|
if (chunk_limit > 0 && chunk_size >= chunk_limit)
|
||||||
|
{
|
||||||
|
state = 2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resume_2:
|
||||||
|
add(*stripe_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inode_map_free(inode_it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* blockstore_heap_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
|
||||||
{
|
{
|
||||||
auto & pool_settings = pool_shard_settings[pool];
|
auto & pool_settings = pool_shard_settings[pool];
|
||||||
if (pool_settings.pg_count == pg_count && pool_settings.pg_stripe_size == pg_stripe_size)
|
if (pool_settings.pg_count == pg_count && pool_settings.pg_stripe_size == pg_stripe_size)
|
||||||
{
|
{
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
uint32_t old_pg_count = !pool_settings.pg_count ? 1 : pool_settings.pg_count;
|
heap_reshard_state_t *st = new heap_reshard_state_t;
|
||||||
uint64_t pool_id = (uint64_t)pool;
|
st->pool_id = (uint64_t)pool;
|
||||||
heap_block_index_t new_shards;
|
st->pg_count = pg_count;
|
||||||
for (uint32_t pg_num = 0; pg_num <= old_pg_count; pg_num++)
|
st->pg_stripe_size = pg_stripe_size;
|
||||||
|
st->old_pg_count = !pool_settings.pg_count ? 1 : pool_settings.pg_count;
|
||||||
|
for (uint32_t pg_num = 0; pg_num <= st->old_pg_count; pg_num++)
|
||||||
{
|
{
|
||||||
auto sh_it = block_index.find((pool_id << (64-POOL_ID_BITS)) | pg_num);
|
auto sh_it = block_index.find((st->pool_id << (64-POOL_ID_BITS)) | pg_num);
|
||||||
if (sh_it == block_index.end())
|
if (sh_it != block_index.end())
|
||||||
{
|
{
|
||||||
continue;
|
st->old_shards[pg_num] = std::move(sh_it->second);
|
||||||
|
block_index.erase(sh_it);
|
||||||
}
|
}
|
||||||
for (auto & inode_pair: sh_it->second)
|
|
||||||
{
|
|
||||||
inode_map_iterate(inode_pair.second, [&](heap_list_item_t *li)
|
|
||||||
{
|
|
||||||
// like map_to_pg()
|
|
||||||
uint64_t pg_num = (li->entry.stripe / pg_stripe_size) % pg_count + 1;
|
|
||||||
uint64_t shard_id = (pool_id << (64-POOL_ID_BITS)) | pg_num;
|
|
||||||
inode_map_put(new_shards[shard_id][li->entry.inode], li);
|
|
||||||
});
|
|
||||||
inode_map_free(inode_pair.second);
|
|
||||||
}
|
|
||||||
block_index.erase(sh_it);
|
|
||||||
}
|
}
|
||||||
for (auto sh_it = new_shards.begin(); sh_it != new_shards.end(); sh_it++)
|
bool finished = reshard_continue(st, chunk_limit);
|
||||||
|
return finished ? NULL : st;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_heap_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
heap_reshard_state_t *st = (heap_reshard_state_t*)reshard_state;
|
||||||
|
if (!st->run(chunk_limit))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto sh_it = st->new_shards.begin(); sh_it != st->new_shards.end(); sh_it++)
|
||||||
{
|
{
|
||||||
block_index[sh_it->first] = std::move(sh_it->second);
|
block_index[sh_it->first] = std::move(sh_it->second);
|
||||||
}
|
}
|
||||||
pool_settings = (pool_shard_settings_t){
|
pool_shard_settings[st->pool_id] = (pool_shard_settings_t){
|
||||||
.pg_count = pg_count,
|
.pg_count = st->pg_count,
|
||||||
.pg_stripe_size = pg_stripe_size,
|
.pg_stripe_size = st->pg_stripe_size,
|
||||||
};
|
};
|
||||||
|
delete st;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_heap_t::reshard_check(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size)
|
||||||
|
{
|
||||||
|
auto set_it = pool_shard_settings.find(pool);
|
||||||
|
return (set_it != pool_shard_settings.end() &&
|
||||||
|
set_it->second.pg_count == pg_count &&
|
||||||
|
set_it->second.pg_stripe_size == pg_stripe_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_entry_t *blockstore_heap_t::lock_and_read_entry(object_id oid)
|
heap_entry_t *blockstore_heap_t::lock_and_read_entry(object_id oid)
|
||||||
@@ -995,27 +1124,6 @@ heap_entry_t *blockstore_heap_t::lock_and_read_entry(object_id oid)
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_entry_t *blockstore_heap_t::read_locked_entry(object_id oid, uint64_t lsn)
|
|
||||||
{
|
|
||||||
auto obj = read_entry(oid);
|
|
||||||
assert(obj);
|
|
||||||
for (auto wr = obj; wr; wr = prev(wr))
|
|
||||||
{
|
|
||||||
if (wr->is_overwrite())
|
|
||||||
{
|
|
||||||
if (lsn == wr->lsn)
|
|
||||||
{
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
obj = prev(wr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool blockstore_heap_t::unlock_entry(object_id oid)
|
bool blockstore_heap_t::unlock_entry(object_id oid)
|
||||||
{
|
{
|
||||||
auto mvcc_it = object_mvcc.find(oid);
|
auto mvcc_it = object_mvcc.find(oid);
|
||||||
@@ -1052,6 +1160,35 @@ heap_entry_t *blockstore_heap_t::read_entry(object_id oid)
|
|||||||
return &li->entry;
|
return &li->entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blockstore_heap_t::gc_block(heap_block_info_t & inf)
|
||||||
|
{
|
||||||
|
if (inf.has_garbage)
|
||||||
|
{
|
||||||
|
size_t i = 0, j = 0;
|
||||||
|
for (; i < inf.entries.size(); i++)
|
||||||
|
{
|
||||||
|
if (inf.entries[i]->entry.is_garbage())
|
||||||
|
{
|
||||||
|
// old entry invalidated by a newer one, mark it as freeable on block write
|
||||||
|
// assign a 'virtual' LSN to track GC completion
|
||||||
|
assert(!inf.mod_lsn_to || inf.mod_lsn_to == next_lsn);
|
||||||
|
uint64_t gc_lsn = ++next_lsn;
|
||||||
|
inf.mod_lsn = inf.mod_lsn ? inf.mod_lsn : gc_lsn;
|
||||||
|
inf.mod_lsn_to = gc_lsn;
|
||||||
|
push_inflight_lsn(gc_lsn, &inf.entries[i]->entry, HEAP_INFLIGHT_GC);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (j != i)
|
||||||
|
inf.entries[j] = inf.entries[i];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inf.entries.resize(j);
|
||||||
|
inf.has_garbage = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int blockstore_heap_t::allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free)
|
int blockstore_heap_t::allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free)
|
||||||
{
|
{
|
||||||
if (last_allocated_block != UINT32_MAX)
|
if (last_allocated_block != UINT32_MAX)
|
||||||
@@ -1118,31 +1255,7 @@ int blockstore_heap_t::allocate_entry(uint32_t entry_size, uint32_t *block_num,
|
|||||||
}
|
}
|
||||||
// Write into the same block
|
// Write into the same block
|
||||||
auto & inf = block_info.at(last_allocated_block);
|
auto & inf = block_info.at(last_allocated_block);
|
||||||
if (inf.has_garbage)
|
gc_block(inf);
|
||||||
{
|
|
||||||
size_t i = 0, j = 0;
|
|
||||||
for (; i < inf.entries.size(); i++)
|
|
||||||
{
|
|
||||||
if (inf.entries[i]->entry.is_garbage())
|
|
||||||
{
|
|
||||||
// old entry invalidated by a newer one, mark it as freeable on block write
|
|
||||||
// assign a 'virtual' LSN to track GC completion
|
|
||||||
assert(!inf.mod_lsn_to || inf.mod_lsn_to == next_lsn);
|
|
||||||
uint64_t gc_lsn = ++next_lsn;
|
|
||||||
inf.mod_lsn = inf.mod_lsn ? inf.mod_lsn : gc_lsn;
|
|
||||||
inf.mod_lsn_to = gc_lsn;
|
|
||||||
push_inflight_lsn(gc_lsn, &inf.entries[i]->entry, HEAP_INFLIGHT_GC);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (j != i)
|
|
||||||
inf.entries[j] = inf.entries[i];
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inf.entries.resize(j);
|
|
||||||
inf.has_garbage = false;
|
|
||||||
}
|
|
||||||
*block_num = last_allocated_block;
|
*block_num = last_allocated_block;
|
||||||
modify_alloc(last_allocated_block, [&](heap_block_info_t & inf)
|
modify_alloc(last_allocated_block, [&](heap_block_info_t & inf)
|
||||||
{
|
{
|
||||||
@@ -1314,7 +1427,7 @@ int blockstore_heap_t::add_redirect_intent(object_id oid, heap_entry_t **obj_ptr
|
|||||||
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
||||||
if (dsk->data_csum_type)
|
if (dsk->data_csum_type)
|
||||||
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
||||||
calc_checksums(wr, (uint8_t*)data, true, offset, len);
|
calc_checksums(wr, (uint8_t*)data, true);
|
||||||
*obj_ptr = wr;
|
*obj_ptr = wr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1353,11 +1466,11 @@ int blockstore_heap_t::add_big_intent(object_id oid, heap_entry_t **obj_ptr, uin
|
|||||||
if (dsk->data_csum_type)
|
if (dsk->data_csum_type)
|
||||||
{
|
{
|
||||||
if (checksums)
|
if (checksums)
|
||||||
memcpy(wr->get_checksums(this), checksums, dsk->clean_entry_bitmap_size);
|
memcpy(wr->get_checksums(this), checksums, get_csum_size(wr));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(wr->get_checksums(this), obj->get_checksums(this), dsk->clean_entry_bitmap_size);
|
memcpy(wr->get_checksums(this), obj->get_checksums(this), get_csum_size(wr));
|
||||||
calc_checksums(wr, (uint8_t*)data, true, offset, len);
|
calc_checksums(wr, (uint8_t*)data, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1478,7 +1591,7 @@ int blockstore_heap_t::add_commit(heap_entry_t *obj, uint64_t version, uint32_t
|
|||||||
}
|
}
|
||||||
if (!uncommitted)
|
if (!uncommitted)
|
||||||
{
|
{
|
||||||
return EBUSY;
|
return 0;
|
||||||
}
|
}
|
||||||
return add_simple(obj, version, modified_block, BS_HEAP_COMMIT);
|
return add_simple(obj, version, modified_block, BS_HEAP_COMMIT);
|
||||||
}
|
}
|
||||||
@@ -1488,23 +1601,32 @@ int blockstore_heap_t::add_rollback(heap_entry_t *obj, uint64_t version, uint32_
|
|||||||
heap_entry_t *wr = obj;
|
heap_entry_t *wr = obj;
|
||||||
bool found_uncommitted = false;
|
bool found_uncommitted = false;
|
||||||
uint64_t commit_version = 0;
|
uint64_t commit_version = 0;
|
||||||
while (wr && !wr->is_overwrite())
|
uint64_t rollback_version = UINT64_MAX;
|
||||||
|
while (wr)
|
||||||
{
|
{
|
||||||
if (wr->type() == BS_HEAP_ROLLBACK)
|
if (wr->type() == BS_HEAP_ROLLBACK)
|
||||||
{
|
{
|
||||||
auto rollback_version = wr->version;
|
if (wr->version <= version)
|
||||||
wr = prev(wr);
|
|
||||||
while (wr->version > rollback_version)
|
|
||||||
{
|
{
|
||||||
assert(!(wr->entry_type & BS_HEAP_STABLE));
|
// All previous writes are already rolled back, stop
|
||||||
wr = prev(wr);
|
break;
|
||||||
}
|
}
|
||||||
|
rollback_version = wr->version;
|
||||||
|
wr = prev(wr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (wr->type() == BS_HEAP_COMMIT)
|
if (wr->type() == BS_HEAP_COMMIT)
|
||||||
{
|
{
|
||||||
if (commit_version < wr->version)
|
if (commit_version < wr->version)
|
||||||
|
{
|
||||||
commit_version = wr->version;
|
commit_version = wr->version;
|
||||||
|
}
|
||||||
|
wr = prev(wr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (wr->version > rollback_version)
|
||||||
|
{
|
||||||
|
// Already rolled back, skip
|
||||||
wr = prev(wr);
|
wr = prev(wr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1515,14 +1637,10 @@ int blockstore_heap_t::add_rollback(heap_entry_t *obj, uint64_t version, uint32_
|
|||||||
{
|
{
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
}
|
}
|
||||||
else if (wr->version == version)
|
else
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (wr->version < version)
|
|
||||||
{
|
|
||||||
return ENOENT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (wr->version > version)
|
else if (wr->version > version)
|
||||||
{
|
{
|
||||||
@@ -1703,9 +1821,9 @@ void blockstore_heap_t::iterate_with_stable(heap_entry_t *obj, uint64_t max_lsn,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 1) 1 2 3 ROLLBACK(2) COMMIT(3) -> impossible
|
// 1) 1 2 3 ROLLBACK(2) COMMIT(3) -> 3 is unstable
|
||||||
// 2) 1 2 3 4 ROLLBACK(3) COMMIT(2) -> OK
|
// 2) 1 2 3 4 ROLLBACK(3) COMMIT(2) -> OK
|
||||||
// 3) 1 2 3 ROLLBACK(2) 3 COMMIT(3) -> first 3 shouldn't be treated as stable
|
// 3) 1 2 3 ROLLBACK(2) 3 COMMIT(3) -> first 3 is unstable
|
||||||
// 4) 1 2 3 COMMIT(3) ROLLBACK(2) -> impossible
|
// 4) 1 2 3 COMMIT(3) ROLLBACK(2) -> impossible
|
||||||
// I.e. a rollback always has version >= previous commit
|
// I.e. a rollback always has version >= previous commit
|
||||||
// 5) 1 2 3 4 5 ROLLBACK(4) 5 ROLLBACK(3)
|
// 5) 1 2 3 4 5 ROLLBACK(4) 5 ROLLBACK(3)
|
||||||
@@ -1862,24 +1980,21 @@ int blockstore_heap_t::list_objects(uint32_t pg_num, object_id min_oid, object_i
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t stable_version = 0;
|
uint64_t stable_version = 0;
|
||||||
auto first_wr = obj;
|
iterate_with_stable(obj, UINT64_MAX, [&](heap_entry_t* wr, bool stable)
|
||||||
for (auto wr = first_wr; wr; wr = prev(wr))
|
|
||||||
{
|
{
|
||||||
if ((wr->entry_type & BS_HEAP_STABLE) || wr->type() == BS_HEAP_COMMIT || wr->type() == BS_HEAP_ROLLBACK)
|
if (stable)
|
||||||
{
|
{
|
||||||
stable_version = wr->version;
|
stable_version = wr->version;
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
if (unstable_size >= unstable_alloc)
|
||||||
{
|
{
|
||||||
if (unstable_size >= unstable_alloc)
|
unstable_alloc = (!unstable_alloc ? 128 : unstable_alloc*2);
|
||||||
{
|
unstable = (obj_ver_id*)realloc_or_die(unstable, sizeof(obj_ver_id) * unstable_alloc);
|
||||||
unstable_alloc = (!unstable_alloc ? 128 : unstable_alloc*2);
|
|
||||||
unstable = (obj_ver_id*)realloc_or_die(unstable, sizeof(obj_ver_id) * unstable_alloc);
|
|
||||||
}
|
|
||||||
unstable[unstable_size++] = (obj_ver_id){ .oid = oid, .version = wr->version };
|
|
||||||
}
|
}
|
||||||
}
|
unstable[unstable_size++] = (obj_ver_id){ .oid = oid, .version = wr->version };
|
||||||
|
return true;
|
||||||
|
});
|
||||||
if (stable_version)
|
if (stable_version)
|
||||||
{
|
{
|
||||||
if (res_size >= res_alloc)
|
if (res_size >= res_alloc)
|
||||||
@@ -1941,7 +2056,13 @@ void blockstore_heap_t::free_data(inode_t inode, uint64_t location)
|
|||||||
inode = (INODE_POOL(inode) << POOL_ID_BITS);
|
inode = (INODE_POOL(inode) << POOL_ID_BITS);
|
||||||
assert(data_alloc->get(location / dsk->data_block_size));
|
assert(data_alloc->get(location / dsk->data_block_size));
|
||||||
data_alloc->set(location / dsk->data_block_size, false);
|
data_alloc->set(location / dsk->data_block_size, false);
|
||||||
inode_space_stats[inode] -= dsk->data_block_size;
|
auto sp_it = inode_space_stats.find(inode);
|
||||||
|
if (sp_it != inode_space_stats.end())
|
||||||
|
{
|
||||||
|
sp_it->second -= dsk->data_block_size;
|
||||||
|
if (sp_it->second == 0)
|
||||||
|
inode_space_stats.erase(sp_it);
|
||||||
|
}
|
||||||
data_used_space -= dsk->data_block_size;
|
data_used_space -= dsk->data_block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2171,11 +2292,12 @@ void blockstore_heap_t::apply_inflight(heap_inflight_lsn_t & inflight)
|
|||||||
if (!next)
|
if (!next)
|
||||||
{
|
{
|
||||||
assert(!prev);
|
assert(!prev);
|
||||||
auto & inode_idx = block_index[get_pg_id(wr->inode, wr->stripe)][wr->inode];
|
auto & pg_idx = block_index[get_pg_id(wr->inode, wr->stripe)];
|
||||||
|
auto & inode_idx = pg_idx[wr->inode];
|
||||||
heap_inode_map_t::iterator li_it;
|
heap_inode_map_t::iterator li_it;
|
||||||
heap_list_item_t *old_li = NULL;
|
heap_list_item_t *old_li = NULL;
|
||||||
inode_map_get(inode_idx, li_it, old_li, wr->stripe);
|
inode_map_get(inode_idx, li_it, old_li, wr->stripe);
|
||||||
inode_map_erase(inode_idx, li_it, old_li);
|
inode_map_erase(pg_idx, inode_idx, li_it, old_li);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2223,7 +2345,7 @@ void blockstore_heap_t::set_no_inode_stats(const std::vector<uint64_t> & pool_id
|
|||||||
{
|
{
|
||||||
// Recalculate if changed
|
// Recalculate if changed
|
||||||
if (ps.second.no_inode_stats == 2 || ps.second.no_inode_stats == 1)
|
if (ps.second.no_inode_stats == 2 || ps.second.no_inode_stats == 1)
|
||||||
recalc_inode_space_stats(ps.first, ps.second.no_inode_stats == 1);
|
recalc_inode_space_stats(ps.first, ps.second.no_inode_stats == 2);
|
||||||
ps.second.no_inode_stats &= 1;
|
ps.second.no_inode_stats &= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2234,8 +2356,8 @@ void blockstore_heap_t::recalc_inode_space_stats(uint64_t pool_id, bool per_inod
|
|||||||
auto sp_begin = inode_space_stats.lower_bound((pool_id << (64-POOL_ID_BITS)));
|
auto sp_begin = inode_space_stats.lower_bound((pool_id << (64-POOL_ID_BITS)));
|
||||||
auto sp_end = inode_space_stats.lower_bound(((pool_id+1) << (64-POOL_ID_BITS)));
|
auto sp_end = inode_space_stats.lower_bound(((pool_id+1) << (64-POOL_ID_BITS)));
|
||||||
inode_space_stats.erase(sp_begin, sp_end);
|
inode_space_stats.erase(sp_begin, sp_end);
|
||||||
uint32_t pg_count = ps.pg_count ? ps.pg_count : 1;
|
uint32_t pg_count = ps.pg_count;
|
||||||
for (uint32_t pg_num = 1; pg_num <= pg_count; pg_num++)
|
for (uint32_t pg_num = pg_count ? 1 : 0; pg_num <= pg_count; pg_num++)
|
||||||
{
|
{
|
||||||
auto & pg_idx = block_index[(pool_id << (64-POOL_ID_BITS)) | pg_num];
|
auto & pg_idx = block_index[(pool_id << (64-POOL_ID_BITS)) | pg_num];
|
||||||
for (auto & ip: pg_idx)
|
for (auto & ip: pg_idx)
|
||||||
@@ -2268,12 +2390,15 @@ void blockstore_heap_t::recalc_inode_space_stats(uint64_t pool_id, bool per_inod
|
|||||||
// This is some really crazy shit but it seems to work well :)
|
// This is some really crazy shit but it seems to work well :)
|
||||||
// At the same time it has almost zero overhead and works just as fast for fat inodes.
|
// At the same time it has almost zero overhead and works just as fast for fat inodes.
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_get(void *inode_idx, heap_inode_map_t::iterator & li_it, heap_list_item_t* & li, uint64_t stripe)
|
void inode_map_get(void *inode_idx, heap_inode_map_t::iterator & li_it, heap_list_item_t* & li, uint64_t stripe)
|
||||||
{
|
{
|
||||||
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
if (!map_n)
|
if (!map_n)
|
||||||
{
|
{
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||||
li_it = ((heap_inode_map_t*)inode_idx)->find(list_item_key(&stripe));
|
li_it = ((heap_inode_map_t*)inode_idx)->find(list_item_key(&stripe));
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
li = li_it != ((heap_inode_map_t*)inode_idx)->end() ? *li_it : NULL;
|
li = li_it != ((heap_inode_map_t*)inode_idx)->end() ? *li_it : NULL;
|
||||||
}
|
}
|
||||||
else if (map_n == 1)
|
else if (map_n == 1)
|
||||||
@@ -2295,7 +2420,7 @@ void blockstore_heap_t::inode_map_get(void *inode_idx, heap_inode_map_t::iterato
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_free(void* inode_idx)
|
void inode_map_free(void* inode_idx)
|
||||||
{
|
{
|
||||||
size_t n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
size_t n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
if (!n)
|
if (!n)
|
||||||
@@ -2308,7 +2433,12 @@ void blockstore_heap_t::inode_map_free(void* inode_idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_iterate(void* & inode_idx, std::function<void(heap_list_item_t*)> cb)
|
bool inode_map_is_big(void* & inode_idx)
|
||||||
|
{
|
||||||
|
return !((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inode_map_iterate(void* & inode_idx, std::function<void(heap_list_item_t*)> cb)
|
||||||
{
|
{
|
||||||
size_t n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
size_t n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
if (!n)
|
if (!n)
|
||||||
@@ -2335,7 +2465,7 @@ void blockstore_heap_t::inode_map_iterate(void* & inode_idx, std::function<void(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_put(void* & inode_idx, heap_list_item_t* li)
|
void inode_map_put(void* & inode_idx, heap_list_item_t* li)
|
||||||
{
|
{
|
||||||
if (!inode_idx)
|
if (!inode_idx)
|
||||||
{
|
{
|
||||||
@@ -2404,7 +2534,7 @@ void blockstore_heap_t::inode_map_put(void* & inode_idx, heap_list_item_t* li)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_replace(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* new_li)
|
void inode_map_replace(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* new_li)
|
||||||
{
|
{
|
||||||
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
if (!map_n)
|
if (!map_n)
|
||||||
@@ -2430,7 +2560,8 @@ void blockstore_heap_t::inode_map_replace(void* & inode_idx, const heap_inode_ma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_heap_t::inode_map_erase(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* li)
|
void inode_map_erase(robin_hood::unordered_flat_map<inode_t, void*, i64hash_t> & pg_idx, void* & inode_idx,
|
||||||
|
const heap_inode_map_t::iterator & li_it, heap_list_item_t* li)
|
||||||
{
|
{
|
||||||
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
size_t map_n = ((size_t)inode_idx & IMAP_MALLOC_LOW_BITS);
|
||||||
if (!map_n)
|
if (!map_n)
|
||||||
@@ -2455,7 +2586,7 @@ void blockstore_heap_t::inode_map_erase(void* & inode_idx, const heap_inode_map_
|
|||||||
else if (map_n == 1)
|
else if (map_n == 1)
|
||||||
{
|
{
|
||||||
// Erase
|
// Erase
|
||||||
block_index[get_pg_id(li->entry.inode, li->entry.stripe)].erase(li->entry.inode);
|
pg_idx.erase(li->entry.inode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ struct heap_compact_t
|
|||||||
bool do_delete;
|
bool do_delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct heap_reshard_state_t;
|
||||||
|
|
||||||
struct heap_li_hash
|
struct heap_li_hash
|
||||||
{
|
{
|
||||||
size_t operator()(const heap_list_item_t* li) const noexcept
|
size_t operator()(const heap_list_item_t* li) const noexcept
|
||||||
@@ -161,7 +163,7 @@ using heap_mvcc_map_t = robin_hood::unordered_flat_map<object_id, heap_object_mv
|
|||||||
|
|
||||||
class blockstore_heap_t
|
class blockstore_heap_t
|
||||||
{
|
{
|
||||||
friend class heap_entry_t;
|
friend struct heap_entry_t;
|
||||||
|
|
||||||
blockstore_disk_t *dsk = NULL;
|
blockstore_disk_t *dsk = NULL;
|
||||||
uint8_t* buffer_area = NULL;
|
uint8_t* buffer_area = NULL;
|
||||||
@@ -198,6 +200,7 @@ 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*> 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;
|
||||||
int recheck_in_progress = 0;
|
int recheck_in_progress = 0;
|
||||||
@@ -205,20 +208,15 @@ class blockstore_heap_t
|
|||||||
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;
|
||||||
int recheck_queue_depth = 0;
|
int recheck_queue_depth = 0;
|
||||||
|
|
||||||
void inode_map_put(void* & inode_idx, heap_list_item_t* li);
|
|
||||||
void inode_map_get(void *inode_idx, heap_inode_map_t::iterator & li_it, heap_list_item_t* & li, uint64_t stripe);
|
|
||||||
void inode_map_free(void* inode_idx);
|
|
||||||
void inode_map_iterate(void* & inode_idx, std::function<void(heap_list_item_t*)> cb);
|
|
||||||
void inode_map_replace(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* new_li);
|
|
||||||
void inode_map_erase(void* & inode_idx, const heap_inode_map_t::iterator & li_it, heap_list_item_t* li);
|
|
||||||
|
|
||||||
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();
|
||||||
int mark_used_blocks();
|
int mark_used_blocks();
|
||||||
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 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_item(heap_list_item_t *li);
|
void insert_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,
|
||||||
@@ -241,28 +239,29 @@ public:
|
|||||||
std::function<void(uint32_t, uint32_t, uint8_t*)> handle_block);
|
std::function<void(uint32_t, uint32_t, uint8_t*)> handle_block);
|
||||||
int load_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf,
|
int load_blocks(uint64_t disk_offset, uint64_t size, uint8_t *buf,
|
||||||
bool allow_corrupted, uint64_t &entries_loaded);
|
bool allow_corrupted, uint64_t &entries_loaded);
|
||||||
// finish loading
|
// finish loading - should be called after load_blocks
|
||||||
int finish_load(bool allow_corrupted = false);
|
void finish_load();
|
||||||
// get blocks which are modified during loading and should be written to the disk
|
// get blocks which are modified during loading and should be written to the disk
|
||||||
// before finishing initialization if not R/O
|
// before finishing initialization if not R/O
|
||||||
std::vector<uint32_t> get_recheck_modified_blocks();
|
std::vector<uint32_t> get_recheck_modified_blocks();
|
||||||
// recheck small write data after reading the database from disk
|
// recheck small write data after reading the database from disk
|
||||||
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> read_buffer, int queue_depth);
|
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> read_buffer, int queue_depth);
|
||||||
|
int finish_recheck();
|
||||||
// reshard database according to the pool's PG count
|
// reshard database according to the pool's PG count
|
||||||
void reshard(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size);
|
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
|
||||||
|
bool reshard_continue(void* reshard_state, uint64_t chunk_limit);
|
||||||
|
bool reshard_check(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size);
|
||||||
void set_no_inode_stats(const std::vector<uint64_t> & pool_ids);
|
void set_no_inode_stats(const std::vector<uint64_t> & pool_ids);
|
||||||
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
|
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
|
||||||
// read an object entry and lock it against removal
|
// read an object entry and lock it against removal
|
||||||
// in the future, may become asynchronous
|
// in the future, may become asynchronous
|
||||||
heap_entry_t *lock_and_read_entry(object_id oid);
|
heap_entry_t *lock_and_read_entry(object_id oid);
|
||||||
// re-read a locked object entry with the given lsn (pointer may be invalidated)
|
|
||||||
heap_entry_t *read_locked_entry(object_id oid, uint64_t lsn);
|
|
||||||
// read an object entry without locking it
|
// read an object entry without locking it
|
||||||
heap_entry_t *read_entry(object_id oid);
|
heap_entry_t *read_entry(object_id oid);
|
||||||
// unlock an entry
|
// unlock an entry
|
||||||
bool unlock_entry(object_id oid);
|
bool unlock_entry(object_id oid);
|
||||||
// set or verify checksums in a write request
|
// set or verify checksums in a write request
|
||||||
bool calc_checksums(heap_entry_t *wr, uint8_t *data, bool set, uint32_t offset = 0, uint32_t len = 0);
|
bool calc_checksums(heap_entry_t *wr, uint8_t *data, bool set, uint32_t offset = UINT32_MAX, uint32_t len = UINT32_MAX);
|
||||||
// set or verify raw block checksums
|
// set or verify raw block checksums
|
||||||
bool calc_block_checksums(uint32_t *block_csums, uint8_t *data, uint8_t *bitmap, uint32_t start, uint32_t end,
|
bool calc_block_checksums(uint32_t *block_csums, uint8_t *data, uint8_t *bitmap, uint32_t start, uint32_t end,
|
||||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
|
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_i *
|
|||||||
dsk.open_meta();
|
dsk.open_meta();
|
||||||
dsk.open_journal();
|
dsk.open_journal();
|
||||||
dsk.calc_lengths();
|
dsk.calc_lengths();
|
||||||
|
dsk.check_lengths();
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
@@ -31,16 +32,13 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_i *
|
|||||||
}
|
}
|
||||||
meta_superblock = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size);
|
meta_superblock = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.meta_block_size);
|
||||||
memset(meta_superblock, 0, dsk.meta_block_size);
|
memset(meta_superblock, 0, dsk.meta_block_size);
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::init()
|
|
||||||
{
|
|
||||||
flusher = new journal_flusher_t(this);
|
flusher = new journal_flusher_t(this);
|
||||||
if (dsk.inmemory_journal)
|
if (dsk.inmemory_journal)
|
||||||
{
|
{
|
||||||
buffer_area = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.journal_len);
|
buffer_area = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.journal_len);
|
||||||
}
|
}
|
||||||
heap = new blockstore_heap_t(&dsk, buffer_area, log_level);
|
heap = new blockstore_heap_t(&dsk, buffer_area, log_level);
|
||||||
|
ringloop->wakeup();
|
||||||
}
|
}
|
||||||
|
|
||||||
blockstore_impl_t::~blockstore_impl_t()
|
blockstore_impl_t::~blockstore_impl_t()
|
||||||
@@ -325,9 +323,13 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check if the DB needs resharding
|
// Check if the DB is sharded correctly
|
||||||
// (we don't know about PGs from the beginning, we only create "shards" here)
|
if (!heap->reshard_check(INODE_POOL(min_inode), pg_count, pg_stripe_size))
|
||||||
heap->reshard(INODE_POOL(min_inode), pg_count, pg_stripe_size);
|
{
|
||||||
|
op->retval = -EAGAIN;
|
||||||
|
FINISH_OP(op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
obj_ver_id *result = NULL;
|
obj_ver_id *result = NULL;
|
||||||
size_t stable_count = 0, unstable_count = 0;
|
size_t stable_count = 0, unstable_count = 0;
|
||||||
int res = heap->list_objects(list_pg, op->min_oid, op->max_oid, &result, &stable_count, &unstable_count);
|
int res = heap->list_objects(list_pg, op->min_oid, op->max_oid, &result, &stable_count, &unstable_count);
|
||||||
@@ -394,3 +396,13 @@ std::string blockstore_impl_t::get_op_diag(blockstore_op_t *op)
|
|||||||
snprintf(buf, sizeof(buf), "state=%d", priv->op_state);
|
snprintf(buf, sizeof(buf), "state=%d", priv->op_state);
|
||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* blockstore_impl_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
return heap->reshard_start(pool, pg_count, pg_stripe_size, chunk_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_impl_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
return heap->reshard_continue(reshard_state, chunk_limit);
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ 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;
|
||||||
@@ -142,7 +143,6 @@ public:
|
|||||||
int metadata_buf_size;
|
int metadata_buf_size;
|
||||||
blockstore_init_meta* metadata_init_reader;
|
blockstore_init_meta* metadata_init_reader;
|
||||||
|
|
||||||
void init();
|
|
||||||
void check_wait(blockstore_op_t *op);
|
void check_wait(blockstore_op_t *op);
|
||||||
void init_op(blockstore_op_t *op);
|
void init_op(blockstore_op_t *op);
|
||||||
|
|
||||||
@@ -190,6 +190,9 @@ public:
|
|||||||
void parse_config(blockstore_config_t & config);
|
void parse_config(blockstore_config_t & config);
|
||||||
void parse_config(blockstore_config_t & config, bool init);
|
void parse_config(blockstore_config_t & config, bool init);
|
||||||
|
|
||||||
|
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
|
||||||
|
bool reshard_continue(void *reshard_state, uint64_t chunk_limit);
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ resume_1:
|
|||||||
}
|
}
|
||||||
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
|
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
|
||||||
{
|
{
|
||||||
bs->dsk.check_lengths();
|
|
||||||
{
|
{
|
||||||
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
|
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
|
||||||
hdr->zero = 0;
|
hdr->zero = 0;
|
||||||
@@ -141,7 +140,7 @@ resume_1:
|
|||||||
hdr->bitmap_granularity != bs->dsk.bitmap_granularity ||
|
hdr->bitmap_granularity != bs->dsk.bitmap_granularity ||
|
||||||
hdr->data_csum_type != bs->dsk.data_csum_type ||
|
hdr->data_csum_type != bs->dsk.data_csum_type ||
|
||||||
hdr->csum_block_size != bs->dsk.csum_block_size ||
|
hdr->csum_block_size != bs->dsk.csum_block_size ||
|
||||||
hdr->meta_area_size > bs->dsk.meta_area_size)
|
hdr->meta_area_size != bs->dsk.meta_area_size)
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Configuration stored in metadata superblock"
|
"Configuration stored in metadata superblock"
|
||||||
@@ -154,15 +153,7 @@ resume_1:
|
|||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
bs->dsk.meta_area_size = hdr->meta_area_size;
|
|
||||||
if (bs->dsk.meta_format != hdr->version)
|
|
||||||
{
|
|
||||||
bs->dsk.meta_format = hdr->version;
|
|
||||||
bs->dsk.calc_lengths();
|
|
||||||
}
|
|
||||||
bs->dsk.check_lengths();
|
|
||||||
}
|
}
|
||||||
bs->init();
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -234,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, false, loaded);
|
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
entries_loaded += loaded;
|
entries_loaded += loaded;
|
||||||
@@ -248,6 +239,7 @@ resume_4:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// metadata read finished
|
// metadata read finished
|
||||||
|
bs->heap->finish_load();
|
||||||
printf("Metadata entries loaded: %ju, used blocks: %ju / %ju\n", entries_loaded, bs->heap->get_data_used_space() / bs->dsk.data_block_size, bs->dsk.block_count);
|
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)
|
if (zero_on_init && !bs->dsk.disable_meta_fsync)
|
||||||
{
|
{
|
||||||
@@ -278,7 +270,7 @@ resume_6:
|
|||||||
}
|
}
|
||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = (iovec){ buf, len };
|
data->iov = (iovec){ buf, len };
|
||||||
data->callback = [this, offset, cb](ring_data_t *data)
|
data->callback = [offset, cb](ring_data_t *data)
|
||||||
{
|
{
|
||||||
if (data->res < 0)
|
if (data->res < 0)
|
||||||
{
|
{
|
||||||
@@ -293,7 +285,7 @@ resume_6:
|
|||||||
}, bs->meta_write_recheck_parallelism);
|
}, bs->meta_write_recheck_parallelism);
|
||||||
return 1;
|
return 1;
|
||||||
resume_7:
|
resume_7:
|
||||||
if (bs->heap->finish_load() != 0)
|
if (bs->heap->finish_recheck() != 0)
|
||||||
{
|
{
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ 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);
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
|||||||
}
|
}
|
||||||
assert(res == 0);
|
assert(res == 0);
|
||||||
}
|
}
|
||||||
|
resume_1:
|
||||||
if (priv->modified_block != UINT32_MAX && priv->modified_block2 != priv->modified_block)
|
if (priv->modified_block != UINT32_MAX && priv->modified_block2 != priv->modified_block)
|
||||||
{
|
{
|
||||||
resume_1:
|
|
||||||
BS_SUBMIT_CHECK_SQES(1);
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
prepare_meta_block_write(priv->modified_block);
|
prepare_meta_block_write(priv->modified_block);
|
||||||
resume_2:
|
resume_2:
|
||||||
|
|||||||
@@ -404,11 +404,12 @@ 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)->op_state = 8;
|
PRIV(op)->pending_ops--;
|
||||||
ringloop->wakeup();
|
ringloop->wakeup();
|
||||||
});
|
});
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
+65
-17
@@ -407,32 +407,77 @@ blockstore_clean_db_t& blockstore_impl_t::clean_db_shard(object_id oid)
|
|||||||
return clean_db_shards[(pool_id << (64-POOL_ID_BITS)) | pg_num];
|
return clean_db_shards[(pool_id << (64-POOL_ID_BITS)) | pg_num];
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::reshard_clean_db(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size)
|
struct bs_reshard_state_t
|
||||||
{
|
{
|
||||||
uint64_t pool_id = (uint64_t)pool;
|
int state = 0;
|
||||||
|
uint64_t pool_id = 0;
|
||||||
|
uint32_t pg_count = 0;
|
||||||
|
uint32_t pg_stripe_size = 0;
|
||||||
|
uint64_t chunk_size = 0;
|
||||||
|
std::map<pool_pg_id_t, blockstore_clean_db_t> old_shards;
|
||||||
std::map<pool_pg_id_t, blockstore_clean_db_t> new_shards;
|
std::map<pool_pg_id_t, blockstore_clean_db_t> new_shards;
|
||||||
auto sh_it = clean_db_shards.lower_bound((pool_id << (64-POOL_ID_BITS)));
|
std::map<pool_pg_id_t, blockstore_clean_db_t>::iterator sh_it;
|
||||||
while (sh_it != clean_db_shards.end() &&
|
blockstore_clean_db_t::iterator obj_it;
|
||||||
(sh_it->first >> (64-POOL_ID_BITS)) == pool_id)
|
};
|
||||||
|
|
||||||
|
void* blockstore_impl_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
auto & settings = clean_db_settings[pool];
|
||||||
|
if (settings.pg_count == pg_count && settings.pg_stripe_size == pg_stripe_size)
|
||||||
{
|
{
|
||||||
for (auto & pair: sh_it->second)
|
return NULL;
|
||||||
{
|
}
|
||||||
// like map_to_pg()
|
bs_reshard_state_t *st = new bs_reshard_state_t;
|
||||||
uint64_t pg_num = (pair.first.stripe / pg_stripe_size) % pg_count + 1;
|
st->state = 0;
|
||||||
uint64_t shard_id = (pool_id << (64-POOL_ID_BITS)) | pg_num;
|
st->pool_id = pool;
|
||||||
new_shards[shard_id][pair.first] = pair.second;
|
st->pg_count = pg_count;
|
||||||
}
|
st->pg_stripe_size = pg_stripe_size;
|
||||||
|
auto sh_it = clean_db_shards.lower_bound((st->pool_id << (64-POOL_ID_BITS)));
|
||||||
|
while (sh_it != clean_db_shards.end() &&
|
||||||
|
(sh_it->first >> (64-POOL_ID_BITS)) == st->pool_id)
|
||||||
|
{
|
||||||
|
st->old_shards[sh_it->first] = std::move(sh_it->second);
|
||||||
clean_db_shards.erase(sh_it++);
|
clean_db_shards.erase(sh_it++);
|
||||||
}
|
}
|
||||||
for (sh_it = new_shards.begin(); sh_it != new_shards.end(); sh_it++)
|
bool finished = reshard_continue(st, chunk_limit);
|
||||||
|
return finished ? NULL : st;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_impl_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
bs_reshard_state_t *st = (bs_reshard_state_t*)reshard_state;
|
||||||
|
uint64_t chunk_size = 0;
|
||||||
|
if (st->state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
for (st->sh_it = st->old_shards.begin(); st->sh_it != st->old_shards.end(); )
|
||||||
|
{
|
||||||
|
for (st->obj_it = st->sh_it->second.begin(); st->obj_it != st->sh_it->second.end(); st->obj_it++)
|
||||||
|
{
|
||||||
|
if (chunk_limit > 0 && chunk_size >= chunk_limit)
|
||||||
|
{
|
||||||
|
st->state = 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resume_1:
|
||||||
|
// like map_to_pg()
|
||||||
|
uint64_t pg_num = (st->obj_it->first.stripe / st->pg_stripe_size) % st->pg_count + 1;
|
||||||
|
uint64_t shard_id = (st->pool_id << (64-POOL_ID_BITS)) | pg_num;
|
||||||
|
st->new_shards[shard_id][st->obj_it->first] = st->obj_it->second;
|
||||||
|
chunk_size++;
|
||||||
|
}
|
||||||
|
st->old_shards.erase(st->sh_it++);
|
||||||
|
}
|
||||||
|
for (auto sh_it = st->new_shards.begin(); sh_it != st->new_shards.end(); sh_it++)
|
||||||
{
|
{
|
||||||
auto & to = clean_db_shards[sh_it->first];
|
auto & to = clean_db_shards[sh_it->first];
|
||||||
to.swap(sh_it->second);
|
to.swap(sh_it->second);
|
||||||
}
|
}
|
||||||
clean_db_settings[pool_id] = (pool_shard_settings_t){
|
clean_db_settings[st->pool_id] = (pool_shard_settings_t){
|
||||||
.pg_count = pg_count,
|
.pg_count = st->pg_count,
|
||||||
.pg_stripe_size = pg_stripe_size,
|
.pg_stripe_size = st->pg_stripe_size,
|
||||||
};
|
};
|
||||||
|
delete st;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::process_list(blockstore_op_t *op)
|
void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
@@ -465,7 +510,10 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
sh_it->second.pg_count != pg_count ||
|
sh_it->second.pg_count != pg_count ||
|
||||||
sh_it->second.pg_stripe_size != pg_stripe_size)
|
sh_it->second.pg_stripe_size != pg_stripe_size)
|
||||||
{
|
{
|
||||||
reshard_clean_db(pool_id, pg_count, pg_stripe_size);
|
// Sharding mismatch
|
||||||
|
op->retval = -EAGAIN;
|
||||||
|
FINISH_OP(op);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
first_shard = last_shard = ((uint64_t)pool_id << (64-POOL_ID_BITS)) | list_pg;
|
first_shard = last_shard = ((uint64_t)pool_id << (64-POOL_ID_BITS)) | list_pg;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ class blockstore_impl_t: public blockstore_i
|
|||||||
uint8_t* get_clean_entry_bitmap(uint64_t block_loc, int offset);
|
uint8_t* get_clean_entry_bitmap(uint64_t block_loc, int offset);
|
||||||
|
|
||||||
blockstore_clean_db_t& clean_db_shard(object_id oid);
|
blockstore_clean_db_t& clean_db_shard(object_id oid);
|
||||||
void reshard_clean_db(pool_id_t pool_id, uint32_t pg_count, uint32_t pg_stripe_size);
|
|
||||||
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
|
void recalc_inode_space_stats(uint64_t pool_id, bool per_inode);
|
||||||
|
|
||||||
// Journaling
|
// Journaling
|
||||||
@@ -288,6 +287,10 @@ public:
|
|||||||
void parse_config(blockstore_config_t & config);
|
void parse_config(blockstore_config_t & config);
|
||||||
void parse_config(blockstore_config_t & config, bool init);
|
void parse_config(blockstore_config_t & config, bool init);
|
||||||
|
|
||||||
|
// Reshard database for a pool
|
||||||
|
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
|
||||||
|
bool reshard_continue(void *reshard_state, uint64_t chunk_limit);
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
uint32_t end = (op->offset+op->len-1) / dsk.csum_block_size;
|
uint32_t end = (op->offset+op->len-1) / dsk.csum_block_size;
|
||||||
auto fn = state & BS_ST_BIG_WRITE ? crc32c_pad : crc32c_nopad;
|
auto fn = state & BS_ST_BIG_WRITE ? crc32c_pad : crc32c_nopad;
|
||||||
if (start == end)
|
if (start == end)
|
||||||
data_csums[0] = fn(0, op->buf, op->len, op->offset - start*dsk.csum_block_size, end*dsk.csum_block_size - (op->offset+op->len));
|
data_csums[0] = fn(0, op->buf, op->len, op->offset - start*dsk.csum_block_size, (end+1)*dsk.csum_block_size - (op->offset+op->len));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// First block
|
// First block
|
||||||
|
|||||||
@@ -898,7 +898,7 @@ void cluster_client_t::execute_cas(cluster_op_t *op)
|
|||||||
.opcode = OSD_OP_SYNC,
|
.opcode = OSD_OP_SYNC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.callback = [this, op](osd_op_t *part)
|
.callback = [op](osd_op_t *part)
|
||||||
{
|
{
|
||||||
if (part->reply.hdr.retval != 0)
|
if (part->reply.hdr.retval != 0)
|
||||||
{
|
{
|
||||||
@@ -1257,7 +1257,7 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
|
|||||||
{
|
{
|
||||||
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
|
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
|
||||||
op->part_bitmaps = (uint8_t*)op->bitmap_buf + object_bitmap_size;
|
op->part_bitmaps = (uint8_t*)op->bitmap_buf + object_bitmap_size;
|
||||||
memset(op->bitmap_buf+op->bitmap_buf_size, 0, bitmap_mem-op->bitmap_buf_size);
|
memset((uint8_t*)op->bitmap_buf+op->bitmap_buf_size, 0, bitmap_mem-op->bitmap_buf_size);
|
||||||
op->bitmap_buf_size = bitmap_mem;
|
op->bitmap_buf_size = bitmap_mem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -414,7 +414,10 @@ 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"}}
|
||||||
if (etcd_watches_initialised == ETCD_TOTAL_WATCHES && !data["result"]["header"]["revision"].is_null())
|
// 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() &&
|
||||||
|
!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,
|
||||||
@@ -568,9 +571,19 @@ void etcd_state_client_t::start_ws_keepalive()
|
|||||||
|
|
||||||
void etcd_state_client_t::load_global_config()
|
void etcd_state_client_t::load_global_config()
|
||||||
{
|
{
|
||||||
etcd_call("/kv/range", json11::Json::object {
|
json11::Json::object req = { { "success", json11::Json::array {
|
||||||
{ "key", base64_encode(etcd_prefix+"/config/global") }
|
json11::Json::object {
|
||||||
}, etcd_quick_timeout, max_etcd_attempts, 0, [this](std::string err, json11::Json data)
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(etcd_prefix+"/config/global") },
|
||||||
|
} }
|
||||||
|
},
|
||||||
|
json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(etcd_prefix+"/config/pools") },
|
||||||
|
} }
|
||||||
|
},
|
||||||
|
} } };
|
||||||
|
etcd_txn(req, etcd_quick_timeout, max_etcd_attempts, 0, [this](std::string err, json11::Json data)
|
||||||
{
|
{
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
@@ -588,10 +601,12 @@ void etcd_state_client_t::load_global_config()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
json11::Json config_kv = data["responses"][0]["response_range"]["kvs"][0];
|
||||||
|
json11::Json pools_kv = data["responses"][1]["response_range"]["kvs"][0];
|
||||||
json11::Json::object global_config;
|
json11::Json::object global_config;
|
||||||
if (data["kvs"].array_items().size() > 0)
|
if (!config_kv.is_null())
|
||||||
{
|
{
|
||||||
auto kv = parse_etcd_kv(data["kvs"][0]);
|
auto kv = parse_etcd_kv(config_kv);
|
||||||
if (kv.value.is_object())
|
if (kv.value.is_object())
|
||||||
{
|
{
|
||||||
global_config = kv.value.object_items();
|
global_config = kv.value.object_items();
|
||||||
@@ -608,6 +623,11 @@ void etcd_state_client_t::load_global_config()
|
|||||||
global_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
global_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
||||||
}
|
}
|
||||||
global_immediate_commit = parse_immediate_commit(global_config["immediate_commit"].string_value(), IMMEDIATE_ALL);
|
global_immediate_commit = parse_immediate_commit(global_config["immediate_commit"].string_value(), IMMEDIATE_ALL);
|
||||||
|
if (!pools_kv.is_null())
|
||||||
|
{
|
||||||
|
auto kv = parse_etcd_kv(pools_kv);
|
||||||
|
parse_state(kv);
|
||||||
|
}
|
||||||
on_load_config_hook(global_config);
|
on_load_config_hook(global_config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -945,8 +965,12 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
|||||||
if (pc.pg_stripe_size < min_stripe_size)
|
if (pc.pg_stripe_size < min_stripe_size)
|
||||||
pc.pg_stripe_size = min_stripe_size;
|
pc.pg_stripe_size = min_stripe_size;
|
||||||
// Save
|
// Save
|
||||||
pc.real_pg_count = this->pool_config[pool_id].real_pg_count;
|
auto & old_pc = this->pool_config[pool_id];
|
||||||
std::swap(pc.pg_config, this->pool_config[pool_id].pg_config);
|
pc.real_pg_count = old_pc.real_pg_count;
|
||||||
|
pc.applied_pg_count = old_pc.applied_pg_count;
|
||||||
|
pc.applied_pg_stripe_size = old_pc.applied_pg_stripe_size;
|
||||||
|
pc.reshard_state = old_pc.reshard_state;
|
||||||
|
std::swap(pc.pg_config, old_pc.pg_config);
|
||||||
std::swap(this->pool_config[pool_id], pc);
|
std::swap(this->pool_config[pool_id], pc);
|
||||||
auto & parsed_cfg = this->pool_config[pool_id];
|
auto & parsed_cfg = this->pool_config[pool_id];
|
||||||
parsed_cfg.exists = true;
|
parsed_cfg.exists = true;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
#include "osd_id.h"
|
#include "object_id.h"
|
||||||
#include "timerfd_manager.h"
|
#include "timerfd_manager.h"
|
||||||
|
|
||||||
#define ETCD_CONFIG_WATCH_ID 1
|
#define ETCD_CONFIG_WATCH_ID 1
|
||||||
@@ -68,6 +68,11 @@ struct pool_config_t
|
|||||||
std::string used_for_app;
|
std::string used_for_app;
|
||||||
int backfillfull = 0;
|
int backfillfull = 0;
|
||||||
int local_reads = 0;
|
int local_reads = 0;
|
||||||
|
|
||||||
|
// runtime data, used only by OSD:
|
||||||
|
uint64_t applied_pg_count = 0;
|
||||||
|
uint64_t applied_pg_stripe_size = 0;
|
||||||
|
void *reshard_state = NULL;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct inode_config_t
|
struct inode_config_t
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ void osd_messenger_t::init()
|
|||||||
rdma_contexts = msgr_rdma_context_t::create_all(
|
rdma_contexts = msgr_rdma_context_t::create_all(
|
||||||
osd_num && osd_cluster_network_masks.size() ? osd_cluster_network_masks : osd_network_masks,
|
osd_num && osd_cluster_network_masks.size() ? osd_cluster_network_masks : osd_network_masks,
|
||||||
rdma_device != "" ? rdma_device.c_str() : NULL,
|
rdma_device != "" ? rdma_device.c_str() : NULL,
|
||||||
rdma_port_num, rdma_gid_index, rdma_mtu, rdma_odp, log_level
|
rdma_port_num, rdma_gid_index, rdma_mtu, log_level
|
||||||
);
|
);
|
||||||
if (!rdma_contexts.size())
|
if (!rdma_contexts.size())
|
||||||
{
|
{
|
||||||
@@ -322,7 +322,6 @@ void osd_messenger_t::parse_config(const json11::Json & config)
|
|||||||
this->rdma_max_msg = config["rdma_max_msg"].uint64_value();
|
this->rdma_max_msg = config["rdma_max_msg"].uint64_value();
|
||||||
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;
|
||||||
this->rdma_odp = config["rdma_odp"].bool_value();
|
|
||||||
#endif
|
#endif
|
||||||
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();
|
||||||
|
|||||||
@@ -200,7 +200,6 @@ protected:
|
|||||||
std::vector<msgr_rdma_context_t *> rdma_contexts;
|
std::vector<msgr_rdma_context_t *> rdma_contexts;
|
||||||
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;
|
||||||
bool rdma_odp = false;
|
|
||||||
rdma_event_channel *rdmacm_evch = NULL;
|
rdma_event_channel *rdmacm_evch = NULL;
|
||||||
std::map<rdma_cm_id*, osd_client_t*> rdmacm_connections;
|
std::map<rdma_cm_id*, osd_client_t*> rdmacm_connections;
|
||||||
std::map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
|
std::map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
|
||||||
@@ -210,7 +209,7 @@ protected:
|
|||||||
std::vector<int> read_ready_clients;
|
std::vector<int> read_ready_clients;
|
||||||
std::vector<int> 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::vector<osd_op_t*> set_immediate_ops;
|
std::deque<osd_op_t*> set_immediate_ops;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
timerfd_manager_t *tfd = NULL;
|
timerfd_manager_t *tfd = NULL;
|
||||||
@@ -287,8 +286,6 @@ protected:
|
|||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
void try_send_rdma(osd_client_t *cl);
|
void try_send_rdma(osd_client_t *cl);
|
||||||
void try_send_rdma_odp(osd_client_t *cl);
|
|
||||||
void try_send_rdma_nodp(osd_client_t *cl);
|
|
||||||
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);
|
||||||
|
|||||||
+20
-110
@@ -59,8 +59,6 @@ msgr_rdma_context_t::~msgr_rdma_context_t()
|
|||||||
ibv_destroy_cq(cq);
|
ibv_destroy_cq(cq);
|
||||||
if (channel)
|
if (channel)
|
||||||
ibv_destroy_comp_channel(channel);
|
ibv_destroy_comp_channel(channel);
|
||||||
if (mr)
|
|
||||||
ibv_dereg_mr(mr);
|
|
||||||
if (pd)
|
if (pd)
|
||||||
ibv_dealloc_pd(pd);
|
ibv_dealloc_pd(pd);
|
||||||
if (context && !is_cm)
|
if (context && !is_cm)
|
||||||
@@ -182,7 +180,7 @@ static int match_port_gid(const std::vector<addr_mask_t> & osd_network_masks, ib
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vector<addr_mask_t> & osd_network_masks,
|
std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vector<addr_mask_t> & osd_network_masks,
|
||||||
const char *sel_dev_name, int sel_port_num, int sel_gid_index, uint32_t sel_mtu, bool odp, int log_level)
|
const char *sel_dev_name, int sel_port_num, int sel_gid_index, uint32_t sel_mtu, int log_level)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
std::vector<msgr_rdma_context_t*> ret;
|
std::vector<msgr_rdma_context_t*> ret;
|
||||||
@@ -271,7 +269,7 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
|
|||||||
{
|
{
|
||||||
if (log_level > 0)
|
if (log_level > 0)
|
||||||
log_rdma_dev_port_gid(dev, port_num, best_gid_idx, port_mtu, best_gidx);
|
log_rdma_dev_port_gid(dev, port_num, best_gid_idx, port_mtu, best_gidx);
|
||||||
auto ctx = msgr_rdma_context_t::create(dev, portinfo, port_num, best_gid_idx, port_mtu, odp, log_level);
|
auto ctx = msgr_rdma_context_t::create(dev, portinfo, port_num, best_gid_idx, port_mtu, log_level);
|
||||||
if (ctx)
|
if (ctx)
|
||||||
{
|
{
|
||||||
ctx->net_mask = osd_network_masks[net_num];
|
ctx->net_mask = osd_network_masks[net_num];
|
||||||
@@ -291,7 +289,7 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
|
|||||||
log_rdma_dev_port_gid(dev, port_num, best_gid_idx, port_mtu, gidx);
|
log_rdma_dev_port_gid(dev, port_num, best_gid_idx, port_mtu, gidx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
auto ctx = msgr_rdma_context_t::create(dev, portinfo, port_num, best_gid_idx, port_mtu, odp, log_level);
|
auto ctx = msgr_rdma_context_t::create(dev, portinfo, port_num, best_gid_idx, port_mtu, log_level);
|
||||||
if (ctx)
|
if (ctx)
|
||||||
ret.push_back(ctx);
|
ret.push_back(ctx);
|
||||||
}
|
}
|
||||||
@@ -306,7 +304,7 @@ cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
msgr_rdma_context_t *msgr_rdma_context_t::create(ibv_device *dev, ibv_port_attr & portinfo, int ib_port, int gid_index, uint32_t mtu, bool odp, int log_level)
|
msgr_rdma_context_t *msgr_rdma_context_t::create(ibv_device *dev, ibv_port_attr & portinfo, int ib_port, int gid_index, uint32_t mtu, int log_level)
|
||||||
{
|
{
|
||||||
msgr_rdma_context_t *ctx = new msgr_rdma_context_t();
|
msgr_rdma_context_t *ctx = new msgr_rdma_context_t();
|
||||||
ibv_context *context = ibv_open_device(dev);
|
ibv_context *context = ibv_open_device(dev);
|
||||||
@@ -346,30 +344,6 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(ibv_device *dev, ibv_port_attr
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->odp = odp;
|
|
||||||
if (ctx->odp)
|
|
||||||
{
|
|
||||||
if (!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT) ||
|
|
||||||
!(ctx->attrx.odp_caps.general_caps & IBV_ODP_SUPPORT_IMPLICIT) ||
|
|
||||||
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_SEND) ||
|
|
||||||
!(ctx->attrx.odp_caps.per_transport_caps.rc_odp_caps & IBV_ODP_SUPPORT_RECV))
|
|
||||||
{
|
|
||||||
ctx->odp = false;
|
|
||||||
if (log_level > 0)
|
|
||||||
fprintf(stderr, "The RDMA device isn't implicit ODP (On-Demand Paging) capable, disabling it\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->odp)
|
|
||||||
{
|
|
||||||
ctx->mr = ibv_reg_mr(ctx->pd, NULL, SIZE_MAX, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_ON_DEMAND);
|
|
||||||
if (!ctx->mr)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Couldn't register RDMA memory region\n");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->channel = ibv_create_comp_channel(ctx->context);
|
ctx->channel = ibv_create_comp_channel(ctx->context);
|
||||||
if (!ctx->channel)
|
if (!ctx->channel)
|
||||||
{
|
{
|
||||||
@@ -388,8 +362,6 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(ibv_device *dev, ibv_port_attr
|
|||||||
return ctx;
|
return ctx;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (context)
|
|
||||||
ibv_close_device(context);
|
|
||||||
delete ctx;
|
delete ctx;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -605,52 +577,7 @@ static int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
|
|||||||
return total_dst_len-dst_len;
|
return total_dst_len-dst_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::try_send_rdma_odp(osd_client_t *cl)
|
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||||
{
|
|
||||||
auto rc = cl->rdma_conn;
|
|
||||||
if (!cl->send_list.size() || rc->cur_send >= rc->max_send)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint64_t op_size = 0, op_sge = 0;
|
|
||||||
ibv_sge sge[rc->max_sge];
|
|
||||||
while (rc->send_pos < cl->send_list.size())
|
|
||||||
{
|
|
||||||
iovec & iov = cl->send_list[rc->send_pos];
|
|
||||||
if (op_size >= rc->max_msg || op_sge >= rc->max_sge)
|
|
||||||
{
|
|
||||||
rc->send_sizes.push_back(op_size);
|
|
||||||
try_send_rdma_wr(cl, sge, op_sge);
|
|
||||||
op_sge = 0;
|
|
||||||
op_size = 0;
|
|
||||||
if (rc->cur_send >= rc->max_send)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint32_t len = (uint32_t)(op_size+iov.iov_len-rc->send_buf_pos < rc->max_msg
|
|
||||||
? iov.iov_len-rc->send_buf_pos : rc->max_msg-op_size);
|
|
||||||
sge[op_sge++] = {
|
|
||||||
.addr = (uintptr_t)((uint8_t*)iov.iov_base+rc->send_buf_pos),
|
|
||||||
.length = len,
|
|
||||||
.lkey = rc->ctx->mr->lkey,
|
|
||||||
};
|
|
||||||
op_size += len;
|
|
||||||
rc->send_buf_pos += len;
|
|
||||||
if (rc->send_buf_pos >= iov.iov_len)
|
|
||||||
{
|
|
||||||
rc->send_pos++;
|
|
||||||
rc->send_buf_pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (op_sge > 0)
|
|
||||||
{
|
|
||||||
rc->send_sizes.push_back(op_size);
|
|
||||||
try_send_rdma_wr(cl, sge, op_sge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void osd_messenger_t::try_send_rdma_nodp(osd_client_t *cl)
|
|
||||||
{
|
{
|
||||||
auto rc = cl->rdma_conn;
|
auto rc = cl->rdma_conn;
|
||||||
if (!rc->send_out_size)
|
if (!rc->send_out_size)
|
||||||
@@ -658,14 +585,11 @@ void osd_messenger_t::try_send_rdma_nodp(osd_client_t *cl)
|
|||||||
// Allocate send ring buffer, if not yet
|
// Allocate send ring buffer, if not yet
|
||||||
rc->send_out_size = rc->max_msg*rdma_max_send;
|
rc->send_out_size = rc->max_msg*rdma_max_send;
|
||||||
rc->send_out.buf = (uint8_t*)malloc_or_die(rc->send_out_size);
|
rc->send_out.buf = (uint8_t*)malloc_or_die(rc->send_out_size);
|
||||||
if (!rc->ctx->odp)
|
rc->send_out.mr = ibv_reg_mr(rc->ctx->pd, rc->send_out.buf, rc->send_out_size, 0);
|
||||||
|
if (!rc->send_out.mr)
|
||||||
{
|
{
|
||||||
rc->send_out.mr = ibv_reg_mr(rc->ctx->pd, rc->send_out.buf, rc->send_out_size, 0);
|
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
||||||
if (!rc->send_out.mr)
|
exit(1);
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Copy data into the buffer and send it
|
// Copy data into the buffer and send it
|
||||||
@@ -690,7 +614,7 @@ void osd_messenger_t::try_send_rdma_nodp(osd_client_t *cl)
|
|||||||
ibv_sge sge = {
|
ibv_sge sge = {
|
||||||
.addr = (uintptr_t)dst,
|
.addr = (uintptr_t)dst,
|
||||||
.length = (uint32_t)copied,
|
.length = (uint32_t)copied,
|
||||||
.lkey = rc->ctx->odp ? rc->ctx->mr->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);
|
rc->send_sizes.push_back(copied);
|
||||||
@@ -698,20 +622,12 @@ void osd_messenger_t::try_send_rdma_nodp(osd_client_t *cl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
|
||||||
{
|
|
||||||
if (cl->rdma_conn->ctx->odp)
|
|
||||||
try_send_rdma_odp(cl);
|
|
||||||
else
|
|
||||||
try_send_rdma_nodp(cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
|
static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
|
||||||
{
|
{
|
||||||
ibv_sge sge = {
|
ibv_sge sge = {
|
||||||
.addr = (uintptr_t)buf,
|
.addr = (uintptr_t)buf,
|
||||||
.length = (uint32_t)cl->rdma_conn->max_msg,
|
.length = (uint32_t)cl->rdma_conn->max_msg,
|
||||||
.lkey = cl->rdma_conn->ctx->odp ? cl->rdma_conn->ctx->mr->lkey : cl->rdma_conn->recv_buf.mr->lkey,
|
.lkey = cl->rdma_conn->recv_buf.mr->lkey,
|
||||||
};
|
};
|
||||||
ibv_recv_wr *bad_wr = NULL;
|
ibv_recv_wr *bad_wr = NULL;
|
||||||
ibv_recv_wr wr = {
|
ibv_recv_wr wr = {
|
||||||
@@ -733,14 +649,11 @@ bool osd_messenger_t::init_recv_rdma(osd_client_t *cl)
|
|||||||
auto rc = cl->rdma_conn;
|
auto rc = cl->rdma_conn;
|
||||||
assert(!rc->recv_buf.buf);
|
assert(!rc->recv_buf.buf);
|
||||||
rc->recv_buf.buf = (uint8_t*)malloc_or_die(rc->max_msg * rc->max_recv);
|
rc->recv_buf.buf = (uint8_t*)malloc_or_die(rc->max_msg * rc->max_recv);
|
||||||
if (!rc->ctx->odp)
|
rc->recv_buf.mr = ibv_reg_mr(rc->ctx->pd, rc->recv_buf.buf, rc->max_msg * rc->max_recv, IBV_ACCESS_LOCAL_WRITE);
|
||||||
|
if (!rc->recv_buf.mr)
|
||||||
{
|
{
|
||||||
rc->recv_buf.mr = ibv_reg_mr(rc->ctx->pd, rc->recv_buf.buf, rc->max_msg * rc->max_recv, IBV_ACCESS_LOCAL_WRITE);
|
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
||||||
if (!rc->recv_buf.mr)
|
exit(1);
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to register RDMA memory region: %s\n", strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < rc->max_recv; i++)
|
for (uint32_t i = 0; i < rc->max_recv; i++)
|
||||||
{
|
{
|
||||||
@@ -816,14 +729,11 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
|||||||
rc->cur_send--;
|
rc->cur_send--;
|
||||||
uint64_t sent_size = rc->send_sizes.at(0);
|
uint64_t sent_size = rc->send_sizes.at(0);
|
||||||
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
|
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
|
||||||
if (!rdma_context->odp)
|
rc->send_done_pos += sent_size;
|
||||||
{
|
rc->send_out_full = false;
|
||||||
rc->send_done_pos += sent_size;
|
if (rc->send_done_pos == rc->send_out_size)
|
||||||
rc->send_out_full = false;
|
rc->send_done_pos = 0;
|
||||||
if (rc->send_done_pos == rc->send_out_size)
|
assert(rc->send_done_pos < rc->send_out_size);
|
||||||
rc->send_done_pos = 0;
|
|
||||||
assert(rc->send_done_pos < rc->send_out_size);
|
|
||||||
}
|
|
||||||
int send_pos = 0, send_buf_pos = 0;
|
int send_pos = 0, send_buf_pos = 0;
|
||||||
while (sent_size > 0)
|
while (sent_size > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ struct msgr_rdma_context_t
|
|||||||
ibv_context *context = NULL;
|
ibv_context *context = NULL;
|
||||||
ibv_device_attr_ex attrx;
|
ibv_device_attr_ex attrx;
|
||||||
ibv_pd *pd = NULL;
|
ibv_pd *pd = NULL;
|
||||||
bool odp = false;
|
|
||||||
ibv_mr *mr = NULL;
|
|
||||||
ibv_comp_channel *channel = NULL;
|
ibv_comp_channel *channel = NULL;
|
||||||
ibv_cq *cq = NULL;
|
ibv_cq *cq = NULL;
|
||||||
ibv_port_attr portinfo;
|
ibv_port_attr portinfo;
|
||||||
@@ -43,9 +41,9 @@ struct msgr_rdma_context_t
|
|||||||
int cm_refs = 0;
|
int cm_refs = 0;
|
||||||
|
|
||||||
static std::vector<msgr_rdma_context_t*> create_all(const std::vector<addr_mask_t> & osd_network_masks,
|
static std::vector<msgr_rdma_context_t*> create_all(const std::vector<addr_mask_t> & osd_network_masks,
|
||||||
const char *sel_dev_name, int sel_port_num, int sel_gid_index, uint32_t sel_mtu, bool odp, int log_level);
|
const char *sel_dev_name, int sel_port_num, int sel_gid_index, uint32_t sel_mtu, int log_level);
|
||||||
static msgr_rdma_context_t *create(ibv_device *dev, ibv_port_attr & portinfo,
|
static msgr_rdma_context_t *create(ibv_device *dev, ibv_port_attr & portinfo,
|
||||||
int ib_port, int gid_index, uint32_t mtu, bool odp, int log_level);
|
int ib_port, int gid_index, uint32_t mtu, int log_level);
|
||||||
static msgr_rdma_context_t* create_cm(ibv_context *ctx);
|
static msgr_rdma_context_t* create_cm(ibv_context *ctx);
|
||||||
bool reserve_cqe(int n);
|
bool reserve_cqe(int n);
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ msgr_rdma_context_t* msgr_rdma_context_t::create_cm(ibv_context *ctx)
|
|||||||
delete rdma_context;
|
delete rdma_context;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rdma_context->odp = false;
|
|
||||||
rdma_context->channel = ibv_create_comp_channel(rdma_context->context);
|
rdma_context->channel = ibv_create_comp_channel(rdma_context->context);
|
||||||
if (!rdma_context->channel)
|
if (!rdma_context->channel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ void osd_messenger_t::read_requests()
|
|||||||
}
|
}
|
||||||
if (!sqe)
|
if (!sqe)
|
||||||
{
|
{
|
||||||
|
cl->refs--;
|
||||||
cl->read_msg.msg_iovlen = 0;
|
cl->read_msg.msg_iovlen = 0;
|
||||||
read_ready_clients.erase(read_ready_clients.begin(), read_ready_clients.begin() + i);
|
read_ready_clients.erase(read_ready_clients.begin(), read_ready_clients.begin() + i);
|
||||||
return;
|
return;
|
||||||
@@ -159,8 +160,10 @@ void osd_messenger_t::clear_immediate_ops(int peer_fd)
|
|||||||
|
|
||||||
void osd_messenger_t::handle_immediate_ops()
|
void osd_messenger_t::handle_immediate_ops()
|
||||||
{
|
{
|
||||||
for (auto op: set_immediate_ops)
|
while (set_immediate_ops.size())
|
||||||
{
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
exec_op(op);
|
exec_op(op);
|
||||||
@@ -171,7 +174,6 @@ 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, void *curbuf, int remain)
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
clear_immediate_ops(peer_fd);
|
||||||
if (log_level > 0)
|
if (log_level > 0)
|
||||||
{
|
{
|
||||||
if (cl->osd_num)
|
if (cl->osd_num)
|
||||||
@@ -85,6 +86,16 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
osd_peer_fds.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__
|
#ifndef __MOCK__
|
||||||
// Then remove FD from the eventloop so we don't accidentally read something
|
// Then remove FD from the eventloop so we don't accidentally read something
|
||||||
tfd->set_fd_handler(peer_fd, false, NULL);
|
tfd->set_fd_handler(peer_fd, false, NULL);
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ 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)
|
||||||
@@ -61,6 +70,21 @@ 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>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#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
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
|||||||
|
|
||||||
Name: Vitastor
|
Name: Vitastor
|
||||||
Description: Vitastor client library
|
Description: Vitastor client library
|
||||||
Version: 3.0.1
|
Version: 3.0.4
|
||||||
Libs: -L${libdir} -lvitastor_client
|
Libs: -L${libdir} -lvitastor_client
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
|||||||
cfg["progress"] = "1";
|
cfg["progress"] = "1";
|
||||||
for (int i = 1; i < narg; i++)
|
for (int i = 1; i < narg; i++)
|
||||||
{
|
{
|
||||||
bool argHasValue = (!(i == narg-1) && (args[i+1][0] != '-'));
|
bool argHasValue = (i < narg-1);
|
||||||
if (args[i][0] == '-' && args[i][1] == 'h' && args[i][2] == 0)
|
if (args[i][0] == '-' && args[i][1] == 'h' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["help"] = "1";
|
cfg["help"] = "1";
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
#include "object_id.h"
|
#include "object_id.h"
|
||||||
#include "osd_id.h"
|
|
||||||
#include "ringloop.h"
|
#include "ringloop.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -442,7 +442,7 @@ struct cli_dd_t
|
|||||||
}
|
}
|
||||||
delete cur_read;
|
delete cur_read;
|
||||||
}
|
}
|
||||||
else if (!is_zero(read_op->bitmap_buf, read_op->len/iinfo.in_granularity/8))
|
else if (!is_zero(read_op->bitmap_buf, (read_op->len/iinfo.in_granularity+7)/8))
|
||||||
{
|
{
|
||||||
vitastor_read(cur_read);
|
vitastor_read(cur_read);
|
||||||
}
|
}
|
||||||
@@ -890,12 +890,12 @@ resume_2:
|
|||||||
clock_gettime(CLOCK_REALTIME, &tv_begin);
|
clock_gettime(CLOCK_REALTIME, &tv_begin);
|
||||||
tv_progress = tv_begin;
|
tv_progress = tv_begin;
|
||||||
resume_3:
|
resume_3:
|
||||||
while ((ignore_errors || !copy_error) && (!in_eof || read_buffers.size() || in_waiting > 0 || out_waiting > 0))
|
while ((ignore_errors || !copy_error) && (!in_eof || read_buffers.size() || in_waiting > 0 || out_waiting > 0 || short_writes.size()))
|
||||||
{
|
{
|
||||||
print_progress(false);
|
print_progress(false);
|
||||||
while ((ignore_errors || !copy_error) &&
|
while ((ignore_errors || !copy_error) &&
|
||||||
(!in_eof && in_waiting < in_iodepth && read_buffers.size() < out_iodepth ||
|
(!in_eof && in_waiting < in_iodepth && read_buffers.size() < out_iodepth ||
|
||||||
read_buffers.size() && out_waiting < out_iodepth))
|
(read_buffers.size() || short_writes.size()) && out_waiting < out_iodepth))
|
||||||
{
|
{
|
||||||
if (!in_eof && in_waiting < in_iodepth && read_buffers.size() < out_iodepth)
|
if (!in_eof && in_waiting < in_iodepth && read_buffers.size() < out_iodepth)
|
||||||
{
|
{
|
||||||
@@ -904,7 +904,7 @@ resume_3:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (read_buffers.size() && out_waiting < out_iodepth)
|
if ((read_buffers.size() || short_writes.size()) && out_waiting < out_iodepth)
|
||||||
{
|
{
|
||||||
if (!add_write_op())
|
if (!add_write_op())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ int disk_tool_t::trim_data(std::string device)
|
|||||||
fprintf(stderr, "Reading metadata\n");
|
fprintf(stderr, "Reading metadata\n");
|
||||||
data_alloc = new allocator_t(dsk.block_count);
|
data_alloc = new allocator_t(dsk.block_count);
|
||||||
r = process_meta(
|
r = process_meta(
|
||||||
[this](blockstore_meta_header_v3_t *hdr) {},
|
[](blockstore_meta_header_v3_t *hdr) {},
|
||||||
[this](blockstore_heap_t *heap, heap_entry_t *obj, uint32_t meta_block_num)
|
[this](blockstore_heap_t *heap, heap_entry_t *obj, uint32_t meta_block_num)
|
||||||
{
|
{
|
||||||
for (auto wr = obj; wr; wr = heap->prev(wr))
|
for (auto wr = obj; wr; wr = heap->prev(wr))
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "disk_tool.h"
|
#include "disk_tool.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "osd_id.h"
|
|
||||||
#include "json_util.h"
|
#include "json_util.h"
|
||||||
#include "malloc_or_die.h"
|
#include "malloc_or_die.h"
|
||||||
|
|
||||||
@@ -109,6 +108,7 @@ close_error:
|
|||||||
r = heap->load_blocks(meta_pos-dsk.meta_block_size, read_len, data, true, entries_loaded);
|
r = heap->load_blocks(meta_pos-dsk.meta_block_size, read_len, data, true, entries_loaded);
|
||||||
meta_pos += read_len;
|
meta_pos += read_len;
|
||||||
}
|
}
|
||||||
|
heap->finish_load();
|
||||||
heap->iterate_objects([&](heap_entry_t* obj, uint32_t meta_block_num)
|
heap->iterate_objects([&](heap_entry_t* obj, uint32_t meta_block_num)
|
||||||
{
|
{
|
||||||
obj_fn(heap, obj, meta_block_num);
|
obj_fn(heap, obj, meta_block_num);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "disk_tool.h"
|
#include "disk_tool.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
#include "json_util.h"
|
#include "json_util.h"
|
||||||
#include "osd_id.h"
|
|
||||||
|
|
||||||
void disk_tool_t::parse_meta_reserve()
|
void disk_tool_t::parse_meta_reserve()
|
||||||
{
|
{
|
||||||
@@ -150,9 +149,12 @@ int disk_tool_t::prepare_one(std::map<std::string, std::string> options, int is_
|
|||||||
{
|
{
|
||||||
if (options["block_size"] == "")
|
if (options["block_size"] == "")
|
||||||
options["block_size"] = "1M";
|
options["block_size"] = "1M";
|
||||||
|
if (is_hybrid && options["atomic_write_size"] == "")
|
||||||
|
options["atomic_write_size"] = "0";
|
||||||
if (is_hybrid && options["throttle_small_writes"] == "")
|
if (is_hybrid && options["throttle_small_writes"] == "")
|
||||||
options["throttle_small_writes"] = "1";
|
options["throttle_small_writes"] = "1";
|
||||||
if (!is_hybrid && options.find("data_csum_type") != options.end() && options.at("data_csum_type") != "")
|
if (!is_hybrid && options.find("data_csum_type") != options.end() && options.at("data_csum_type") != "" &&
|
||||||
|
options["csum_block_size"] == "")
|
||||||
options["csum_block_size"] = "32k";
|
options["csum_block_size"] = "32k";
|
||||||
}
|
}
|
||||||
else if (!json_is_true(options["disable_data_fsync"]))
|
else if (!json_is_true(options["disable_data_fsync"]))
|
||||||
|
|||||||
@@ -140,7 +140,10 @@ uint32_t disk_tool_t::write_osd_superblock(std::string device, json11::Json para
|
|||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
free(buf);
|
free(buf);
|
||||||
shell_exec({ "udevadm", "trigger", "--settle", device }, "", NULL, NULL);
|
if (!test_mode)
|
||||||
|
{
|
||||||
|
shell_exec({ "udevadm", "trigger", "--settle", device }, "", NULL, NULL);
|
||||||
|
}
|
||||||
return sb_size;
|
return sb_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ resume_1:
|
|||||||
}
|
}
|
||||||
if (st->ientry["type"].string_value() != "file" &&
|
if (st->ientry["type"].string_value() != "file" &&
|
||||||
st->ientry["type"].string_value() != "" &&
|
st->ientry["type"].string_value() != "" &&
|
||||||
!st->set_attrs["size"].is_null())
|
st->set_attrs.find("size") != st->set_attrs.end())
|
||||||
{
|
{
|
||||||
auto cb = std::move(st->cb);
|
auto cb = std::move(st->cb);
|
||||||
cb(-EINVAL);
|
cb(-EINVAL);
|
||||||
@@ -96,8 +96,13 @@ resume_1:
|
|||||||
nfs_kv_continue_setattr(st, 2);
|
nfs_kv_continue_setattr(st, 2);
|
||||||
}, [st](int res, const std::string & cas_value)
|
}, [st](int res, const std::string & cas_value)
|
||||||
{
|
{
|
||||||
|
if ((res == 0 || res == -ENOENT && st->ino == KV_ROOT_INODE) && cas_value == st->ientry_text)
|
||||||
|
{
|
||||||
|
st->cas_res = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
st->cas_res = res;
|
st->cas_res = res;
|
||||||
return (res == 0 || res == -ENOENT && st->ino == KV_ROOT_INODE) && cas_value == st->ientry_text;
|
return false;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
resume_2:
|
resume_2:
|
||||||
|
|||||||
+44
-19
@@ -31,25 +31,6 @@ osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
|
|||||||
// FIXME: Use timerfd_interval based directly on io_uring
|
// FIXME: Use timerfd_interval based directly on io_uring
|
||||||
this->tfd = epmgr->tfd;
|
this->tfd = epmgr->tfd;
|
||||||
|
|
||||||
if (!json_is_true(this->config["disable_blockstore"]))
|
|
||||||
{
|
|
||||||
auto bs_cfg = json_to_string_map(this->config);
|
|
||||||
this->bs = blockstore_i::create(bs_cfg, ringloop, tfd);
|
|
||||||
// Wait for blockstore initialisation before actually starting OSD logic
|
|
||||||
// to prevent peering timeouts during restart with filled databases
|
|
||||||
while (!bs->is_started())
|
|
||||||
{
|
|
||||||
ringloop->loop();
|
|
||||||
if (bs->is_started())
|
|
||||||
break;
|
|
||||||
ringloop->wait();
|
|
||||||
}
|
|
||||||
// Autosync based on the number of unstable writes to prevent stalls due to insufficient journal space
|
|
||||||
uint64_t max_autosync = bs->get_journal_size() / bs->get_block_size() / 2;
|
|
||||||
if (autosync_writes > max_autosync)
|
|
||||||
autosync_writes = max_autosync;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json_is_true(this->config["osd_memlock"]))
|
if (json_is_true(this->config["osd_memlock"]))
|
||||||
{
|
{
|
||||||
// Lock all OSD memory if requested
|
// Lock all OSD memory if requested
|
||||||
@@ -117,6 +98,7 @@ osd_t::~osd_t()
|
|||||||
autosync_timer_id = -1;
|
autosync_timer_id = -1;
|
||||||
}
|
}
|
||||||
ringloop->unregister_consumer(&consumer);
|
ringloop->unregister_consumer(&consumer);
|
||||||
|
ringloop->unregister_consumer(&init_consumer);
|
||||||
delete epmgr;
|
delete epmgr;
|
||||||
if (bs)
|
if (bs)
|
||||||
delete bs;
|
delete bs;
|
||||||
@@ -131,6 +113,43 @@ osd_t::~osd_t()
|
|||||||
free(zero_buffer);
|
free(zero_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osd_t::init_blockstore(std::function<void()> on_init)
|
||||||
|
{
|
||||||
|
if (!json_is_true(this->config["disable_blockstore"]))
|
||||||
|
{
|
||||||
|
auto bs_cfg = json_to_string_map(this->config);
|
||||||
|
this->bs = blockstore_i::create(bs_cfg, ringloop, tfd);
|
||||||
|
// Pre-configure pool PG shards
|
||||||
|
for (auto & pool_item: st_cli.pool_config)
|
||||||
|
{
|
||||||
|
auto st = bs->reshard_start(pool_item.first, pool_item.second.pg_count, pool_item.second.pg_stripe_size, 0);
|
||||||
|
assert(!st);
|
||||||
|
}
|
||||||
|
// Autosync based on the number of unstable writes to prevent stalls due to insufficient journal space
|
||||||
|
uint64_t max_autosync = bs->get_journal_size() / bs->get_block_size() / 2;
|
||||||
|
if (autosync_writes > max_autosync)
|
||||||
|
autosync_writes = max_autosync;
|
||||||
|
if (on_init)
|
||||||
|
{
|
||||||
|
init_consumer.loop = [this, on_init]()
|
||||||
|
{
|
||||||
|
// Wait for blockstore initialisation before actually starting OSD logic
|
||||||
|
// to prevent peering timeouts during restart with filled databases
|
||||||
|
if (bs->is_started())
|
||||||
|
{
|
||||||
|
ringloop->set_immediate([this, on_init] { init_consumer.loop = NULL; on_init(); });
|
||||||
|
ringloop->unregister_consumer(&init_consumer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ringloop->register_consumer(&init_consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (on_init)
|
||||||
|
{
|
||||||
|
on_init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void osd_t::parse_config(bool init)
|
void osd_t::parse_config(bool init)
|
||||||
{
|
{
|
||||||
config = msgr.merge_configs(cli_config, file_config, etcd_global_config, etcd_osd_config);
|
config = msgr.merge_configs(cli_config, file_config, etcd_global_config, etcd_osd_config);
|
||||||
@@ -292,6 +311,12 @@ void osd_t::parse_config(bool init)
|
|||||||
scrub_list_limit = config["scrub_list_limit"].uint64_value();
|
scrub_list_limit = config["scrub_list_limit"].uint64_value();
|
||||||
if (!scrub_list_limit)
|
if (!scrub_list_limit)
|
||||||
scrub_list_limit = 262144;
|
scrub_list_limit = 262144;
|
||||||
|
pg_reshard_chunk_size = config["pg_reshard_chunk_size"].uint64_value();
|
||||||
|
if (!pg_reshard_chunk_size)
|
||||||
|
pg_reshard_chunk_size = 100000;
|
||||||
|
pg_reshard_chunk_pause_ms = config["pg_reshard_chunk_pause_ms"].uint64_value();
|
||||||
|
if (!pg_reshard_chunk_pause_ms)
|
||||||
|
pg_reshard_chunk_pause_ms = 100;
|
||||||
if (!old_auto_scrub && auto_scrub)
|
if (!old_auto_scrub && auto_scrub)
|
||||||
{
|
{
|
||||||
// Schedule scrubbing
|
// Schedule scrubbing
|
||||||
|
|||||||
+15
-8
@@ -148,6 +148,8 @@ class osd_t
|
|||||||
bool enable_pg_locks = false;
|
bool enable_pg_locks = false;
|
||||||
bool pg_locks_localize_only = false;
|
bool pg_locks_localize_only = false;
|
||||||
uint64_t pg_lock_retry_interval_ms = 100;
|
uint64_t pg_lock_retry_interval_ms = 100;
|
||||||
|
uint64_t pg_reshard_chunk_size = 100000;
|
||||||
|
uint64_t pg_reshard_chunk_pause_ms = 100;
|
||||||
|
|
||||||
// cluster state
|
// cluster state
|
||||||
|
|
||||||
@@ -158,6 +160,7 @@ class osd_t
|
|||||||
json11::Json self_state;
|
json11::Json self_state;
|
||||||
bool loading_peer_config = false;
|
bool loading_peer_config = false;
|
||||||
std::set<pool_pg_num_t> pg_state_dirty;
|
std::set<pool_pg_num_t> pg_state_dirty;
|
||||||
|
bool etcd_global_config_loaded = false;
|
||||||
bool pg_config_applied = false;
|
bool pg_config_applied = false;
|
||||||
bool etcd_reporting_pg_state = false;
|
bool etcd_reporting_pg_state = false;
|
||||||
bool etcd_reporting_stats = false;
|
bool etcd_reporting_stats = false;
|
||||||
@@ -168,10 +171,11 @@ class osd_t
|
|||||||
// peers and PGs
|
// peers and PGs
|
||||||
|
|
||||||
std::map<pool_pg_num_t, osd_pg_lock_t> pg_locks;
|
std::map<pool_pg_num_t, osd_pg_lock_t> pg_locks;
|
||||||
std::map<pool_id_t, pg_num_t> pg_counts;
|
|
||||||
std::map<pool_pg_num_t, pg_t> pgs;
|
std::map<pool_pg_num_t, pg_t> pgs;
|
||||||
std::set<pool_pg_num_t> dirty_pgs;
|
std::set<pool_pg_num_t> dirty_pgs;
|
||||||
std::set<osd_num_t> dirty_osds;
|
std::set<osd_num_t> dirty_osds;
|
||||||
|
std::vector<pool_id_t> reshard_pools;
|
||||||
|
int reshard_timer_id = -1;
|
||||||
int copies_to_delete_after_sync_count = 0;
|
int copies_to_delete_after_sync_count = 0;
|
||||||
uint64_t misplaced_objects = 0, degraded_objects = 0, incomplete_objects = 0, inconsistent_objects = 0, corrupted_objects = 0;
|
uint64_t misplaced_objects = 0, degraded_objects = 0, incomplete_objects = 0, inconsistent_objects = 0, corrupted_objects = 0;
|
||||||
int peering_state = 0;
|
int peering_state = 0;
|
||||||
@@ -206,7 +210,7 @@ class osd_t
|
|||||||
void *zero_buffer = NULL;
|
void *zero_buffer = NULL;
|
||||||
uint64_t zero_buffer_size = 0;
|
uint64_t zero_buffer_size = 0;
|
||||||
uint32_t bs_block_size, bs_bitmap_granularity, clean_entry_bitmap_size;
|
uint32_t bs_block_size, bs_bitmap_granularity, clean_entry_bitmap_size;
|
||||||
ring_loop_t *ringloop;
|
ring_loop_t *ringloop = NULL;
|
||||||
timerfd_manager_t *tfd = NULL;
|
timerfd_manager_t *tfd = NULL;
|
||||||
epoll_manager_t *epmgr = NULL;
|
epoll_manager_t *epmgr = NULL;
|
||||||
|
|
||||||
@@ -218,6 +222,7 @@ class osd_t
|
|||||||
int rdmacm_port = 0;
|
int rdmacm_port = 0;
|
||||||
#endif
|
#endif
|
||||||
ring_consumer_t consumer;
|
ring_consumer_t consumer;
|
||||||
|
ring_consumer_t init_consumer;
|
||||||
|
|
||||||
// op statistics
|
// op statistics
|
||||||
osd_op_stats_t prev_stats, prev_report_stats;
|
osd_op_stats_t prev_stats, prev_report_stats;
|
||||||
@@ -241,6 +246,7 @@ class osd_t
|
|||||||
|
|
||||||
// cluster connection
|
// cluster connection
|
||||||
void parse_config(bool init);
|
void parse_config(bool init);
|
||||||
|
void init_blockstore(std::function<void()> on_init);
|
||||||
void init_cluster();
|
void init_cluster();
|
||||||
void on_change_osd_state_hook(osd_num_t peer_osd);
|
void on_change_osd_state_hook(osd_num_t peer_osd);
|
||||||
void on_change_backfillfull_hook(pool_id_t pool_id);
|
void on_change_backfillfull_hook(pool_id_t pool_id);
|
||||||
@@ -268,6 +274,7 @@ class osd_t
|
|||||||
void apply_no_inode_stats();
|
void apply_no_inode_stats();
|
||||||
void apply_pg_count();
|
void apply_pg_count();
|
||||||
void apply_pg_config();
|
void apply_pg_config();
|
||||||
|
void reshard_continue();
|
||||||
|
|
||||||
// event loop, socket read/write
|
// event loop, socket read/write
|
||||||
void loop();
|
void loop();
|
||||||
@@ -317,7 +324,7 @@ class osd_t
|
|||||||
void finish_op(osd_op_t *cur_op, int retval);
|
void finish_op(osd_op_t *cur_op, int retval);
|
||||||
|
|
||||||
// secondary ops
|
// secondary ops
|
||||||
bool sec_check_pg_lock(osd_num_t primary_osd, const object_id &oid);
|
bool sec_check_pg_lock(osd_num_t primary_osd, const object_id & oid, uint32_t flags);
|
||||||
void exec_show_config(osd_op_t *cur_op);
|
void exec_show_config(osd_op_t *cur_op);
|
||||||
void exec_secondary(osd_op_t *cur_op);
|
void exec_secondary(osd_op_t *cur_op);
|
||||||
void exec_secondary_real(osd_op_t *cur_op);
|
void exec_secondary_real(osd_op_t *cur_op);
|
||||||
@@ -376,12 +383,12 @@ class osd_t
|
|||||||
int submit_bitmap_subops(osd_op_t *cur_op, pg_t & pg);
|
int submit_bitmap_subops(osd_op_t *cur_op, pg_t & pg);
|
||||||
int read_bitmaps(osd_op_t *cur_op, pg_t *pg, int base_state);
|
int read_bitmaps(osd_op_t *cur_op, pg_t *pg, int base_state);
|
||||||
|
|
||||||
inline pg_num_t map_to_pg(object_id oid, uint64_t pg_stripe_size)
|
inline pg_num_t map_to_pg(object_id oid)
|
||||||
{
|
{
|
||||||
uint64_t pg_count = pg_counts[INODE_POOL(oid.inode)];
|
auto pool_it = st_cli.pool_config.find(INODE_POOL(oid.inode));
|
||||||
if (!pg_count)
|
if (pool_it == st_cli.pool_config.end())
|
||||||
pg_count = 1;
|
return 1;
|
||||||
return (oid.stripe / pg_stripe_size) % pg_count + 1;
|
return (oid.stripe / pool_it->second.applied_pg_stripe_size) % pool_it->second.applied_pg_count + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
+114
-24
@@ -18,6 +18,7 @@ void osd_t::init_cluster()
|
|||||||
{
|
{
|
||||||
if (!st_cli.address_count())
|
if (!st_cli.address_count())
|
||||||
{
|
{
|
||||||
|
init_blockstore(NULL);
|
||||||
if (run_primary)
|
if (run_primary)
|
||||||
{
|
{
|
||||||
// Test version of clustering code with 1 pool, 1 PG and 2 peers
|
// Test version of clustering code with 1 pool, 1 PG and 2 peers
|
||||||
@@ -54,9 +55,10 @@ void osd_t::init_cluster()
|
|||||||
.pg_minsize = 2,
|
.pg_minsize = 2,
|
||||||
.pg_count = 1,
|
.pg_count = 1,
|
||||||
.real_pg_count = 1,
|
.real_pg_count = 1,
|
||||||
|
.applied_pg_count = 1,
|
||||||
|
.applied_pg_stripe_size = bs_block_size*2,
|
||||||
};
|
};
|
||||||
report_pg_state(pgs[{ 1, 1 }]);
|
report_pg_state(pgs[{ 1, 1 }]);
|
||||||
pg_counts[1] = 1;
|
|
||||||
}
|
}
|
||||||
bind_socket();
|
bind_socket();
|
||||||
}
|
}
|
||||||
@@ -422,7 +424,10 @@ void osd_t::on_change_osd_state_hook(osd_num_t peer_osd)
|
|||||||
|
|
||||||
void osd_t::on_change_pool_config_hook()
|
void osd_t::on_change_pool_config_hook()
|
||||||
{
|
{
|
||||||
apply_pg_locks_localize_only();
|
if (etcd_global_config_loaded)
|
||||||
|
{
|
||||||
|
apply_pg_locks_localize_only();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_t::apply_pg_locks_localize_only()
|
void osd_t::apply_pg_locks_localize_only()
|
||||||
@@ -484,9 +489,13 @@ void osd_t::on_load_config_hook(json11::Json::object & global_config)
|
|||||||
{
|
{
|
||||||
etcd_global_config = global_config;
|
etcd_global_config = global_config;
|
||||||
parse_config(true);
|
parse_config(true);
|
||||||
bind_socket();
|
|
||||||
acquire_lease();
|
|
||||||
st_cli.on_load_config_hook = [this](json11::Json::object & cfg) { on_reload_config_hook(cfg); };
|
st_cli.on_load_config_hook = [this](json11::Json::object & cfg) { on_reload_config_hook(cfg); };
|
||||||
|
etcd_global_config_loaded = true;
|
||||||
|
init_blockstore([this]()
|
||||||
|
{
|
||||||
|
bind_socket();
|
||||||
|
acquire_lease();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_t::on_reload_config_hook(json11::Json::object & global_config)
|
void osd_t::on_reload_config_hook(json11::Json::object & global_config)
|
||||||
@@ -719,8 +728,13 @@ void osd_t::apply_pg_count()
|
|||||||
{
|
{
|
||||||
for (auto & pool_item: st_cli.pool_config)
|
for (auto & pool_item: st_cli.pool_config)
|
||||||
{
|
{
|
||||||
if (pool_item.second.real_pg_count != 0 &&
|
auto & pool_cfg = pool_item.second;
|
||||||
pool_item.second.real_pg_count != pg_counts[pool_item.first])
|
if (pool_cfg.real_pg_count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pool_cfg.real_pg_count != pool_cfg.applied_pg_count ||
|
||||||
|
pool_cfg.pg_stripe_size != pool_cfg.applied_pg_stripe_size)
|
||||||
{
|
{
|
||||||
// Check that all pool PGs are offline. It is not allowed to change PG count when any PGs are online
|
// Check that all pool PGs are offline. It is not allowed to change PG count when any PGs are online
|
||||||
// The external tool must wait for all PGs to come down before changing PG count
|
// The external tool must wait for all PGs to come down before changing PG count
|
||||||
@@ -742,17 +756,87 @@ void osd_t::apply_pg_count()
|
|||||||
}
|
}
|
||||||
if (still_active_primary > 0 || still_active_secondary > 0)
|
if (still_active_primary > 0 || still_active_secondary > 0)
|
||||||
{
|
{
|
||||||
printf(
|
if (pool_cfg.real_pg_count != pool_cfg.applied_pg_count)
|
||||||
"[OSD %ju] PG count change detected for pool %u (new is %ju, old is %u),"
|
{
|
||||||
" but %u PG(s) are still active as primary and %u as secondary. This is not allowed. Exiting\n",
|
printf(
|
||||||
this->osd_num, pool_item.first, pool_item.second.real_pg_count, pg_counts[pool_item.first],
|
"[OSD %ju] PG count change detected for pool %u (new is %ju, old is %ju),"
|
||||||
still_active_primary, still_active_secondary
|
" but %u PG(s) are still active as primary and %u as secondary. This is not allowed. Exiting\n",
|
||||||
);
|
this->osd_num, pool_item.first, pool_cfg.real_pg_count, pool_cfg.applied_pg_count,
|
||||||
|
still_active_primary, still_active_secondary
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"[OSD %ju] PG stripe change detected for pool %u (new is %ju, old is %ju),"
|
||||||
|
" but %u PG(s) are still active as primary and %u as secondary. This is not allowed. Exiting\n",
|
||||||
|
this->osd_num, pool_item.first, pool_cfg.pg_stripe_size, pool_cfg.applied_pg_stripe_size,
|
||||||
|
still_active_primary, still_active_secondary
|
||||||
|
);
|
||||||
|
}
|
||||||
force_stop(1);
|
force_stop(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
pool_cfg.applied_pg_count = pool_cfg.real_pg_count;
|
||||||
|
pool_cfg.applied_pg_stripe_size = pool_cfg.pg_stripe_size;
|
||||||
|
if (bs && !pool_cfg.reshard_state)
|
||||||
|
{
|
||||||
|
pool_cfg.reshard_state = bs->reshard_start(pool_item.first, pool_cfg.real_pg_count, pool_cfg.pg_stripe_size, pg_reshard_chunk_size);
|
||||||
|
if (pool_cfg.reshard_state)
|
||||||
|
{
|
||||||
|
reshard_pools.push_back(pool_item.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this->pg_counts[pool_item.first] = pool_item.second.real_pg_count;
|
}
|
||||||
|
if (reshard_pools.size() && reshard_timer_id < 0)
|
||||||
|
{
|
||||||
|
reshard_timer_id = tfd->set_timer(pg_reshard_chunk_pause_ms, false, [this](int)
|
||||||
|
{
|
||||||
|
reshard_continue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_t::reshard_continue()
|
||||||
|
{
|
||||||
|
again:
|
||||||
|
auto pool_id = reshard_pools[0];
|
||||||
|
auto pool_it = st_cli.pool_config.find(pool_id);
|
||||||
|
if (pool_it == st_cli.pool_config.end() || !pool_it->second.reshard_state)
|
||||||
|
{
|
||||||
|
reshard_pools.erase(reshard_pools.begin());
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
auto & pool_cfg = pool_it->second;
|
||||||
|
bool done = bs->reshard_continue(pool_cfg.reshard_state, pg_reshard_chunk_size);
|
||||||
|
if (done &&
|
||||||
|
(pool_cfg.real_pg_count != pool_cfg.applied_pg_count ||
|
||||||
|
pool_cfg.pg_stripe_size != pool_cfg.applied_pg_stripe_size))
|
||||||
|
{
|
||||||
|
// PG count changed again, reshard again
|
||||||
|
pool_cfg.applied_pg_count = pool_cfg.real_pg_count;
|
||||||
|
pool_cfg.applied_pg_stripe_size = pool_cfg.pg_stripe_size;
|
||||||
|
pool_cfg.reshard_state = bs->reshard_start(pool_id, pool_cfg.real_pg_count, pool_cfg.pg_stripe_size, pg_reshard_chunk_size);
|
||||||
|
done = !pool_cfg.reshard_state;
|
||||||
|
}
|
||||||
|
if (done)
|
||||||
|
{
|
||||||
|
// Pool is resharded
|
||||||
|
pool_cfg.reshard_state = NULL;
|
||||||
|
reshard_pools.erase(reshard_pools.begin());
|
||||||
|
apply_pg_config();
|
||||||
|
}
|
||||||
|
if (reshard_pools.size())
|
||||||
|
{
|
||||||
|
reshard_timer_id = tfd->set_timer(pg_reshard_chunk_pause_ms, false, [this](int)
|
||||||
|
{
|
||||||
|
reshard_continue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reshard_timer_id = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -761,9 +845,15 @@ void osd_t::apply_pg_config()
|
|||||||
bool all_applied = true;
|
bool all_applied = true;
|
||||||
for (auto & pool_item: st_cli.pool_config)
|
for (auto & pool_item: st_cli.pool_config)
|
||||||
{
|
{
|
||||||
|
auto & pool_cfg = pool_item.second;
|
||||||
|
if (pool_cfg.reshard_state)
|
||||||
|
{
|
||||||
|
// Can't apply anything for pools being resharded
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bool warned_block_size = false;
|
bool warned_block_size = false;
|
||||||
auto pool_id = pool_item.first;
|
auto pool_id = pool_item.first;
|
||||||
for (auto & kv: pool_item.second.pg_config)
|
for (auto & kv: pool_cfg.pg_config)
|
||||||
{
|
{
|
||||||
pg_num_t pg_num = kv.first;
|
pg_num_t pg_num = kv.first;
|
||||||
auto & pg_cfg = kv.second;
|
auto & pg_cfg = kv.second;
|
||||||
@@ -772,8 +862,8 @@ void osd_t::apply_pg_config()
|
|||||||
auto pg_it = this->pgs.find({ .pool_id = pool_id, .pg_num = pg_num });
|
auto pg_it = this->pgs.find({ .pool_id = pool_id, .pg_num = pg_num });
|
||||||
bool currently_taken = pg_it != this->pgs.end() && pg_it->second.state != PG_OFFLINE;
|
bool currently_taken = pg_it != this->pgs.end() && pg_it->second.state != PG_OFFLINE;
|
||||||
// Check pool block size and bitmap granularity
|
// Check pool block size and bitmap granularity
|
||||||
if (take && this->bs_block_size != pool_item.second.data_block_size ||
|
if (take && this->bs_block_size != pool_cfg.data_block_size ||
|
||||||
this->bs_bitmap_granularity != pool_item.second.bitmap_granularity)
|
this->bs_bitmap_granularity != pool_cfg.bitmap_granularity)
|
||||||
{
|
{
|
||||||
if (!warned_block_size)
|
if (!warned_block_size)
|
||||||
{
|
{
|
||||||
@@ -781,7 +871,7 @@ void osd_t::apply_pg_config()
|
|||||||
"[OSD %ju] My block_size and bitmap_granularity are %u/%u"
|
"[OSD %ju] My block_size and bitmap_granularity are %u/%u"
|
||||||
", but pool %u has %u/%u. Refusing to start PGs of this pool\n",
|
", but pool %u has %u/%u. Refusing to start PGs of this pool\n",
|
||||||
this->osd_num, bs_block_size, bs_bitmap_granularity,
|
this->osd_num, bs_block_size, bs_bitmap_granularity,
|
||||||
pool_id, pool_item.second.data_block_size, pool_item.second.bitmap_granularity
|
pool_id, pool_cfg.data_block_size, pool_cfg.bitmap_granularity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
warned_block_size = true;
|
warned_block_size = true;
|
||||||
@@ -874,12 +964,12 @@ void osd_t::apply_pg_config()
|
|||||||
}
|
}
|
||||||
auto & pg = this->pgs[{ .pool_id = pool_id, .pg_num = pg_num }];
|
auto & pg = this->pgs[{ .pool_id = pool_id, .pg_num = pg_num }];
|
||||||
pg.state = pg_cfg.cur_primary == this->osd_num ? PG_PEERING : PG_STARTING;
|
pg.state = pg_cfg.cur_primary == this->osd_num ? PG_PEERING : PG_STARTING;
|
||||||
pg.scheme = pool_item.second.scheme;
|
pg.scheme = pool_cfg.scheme;
|
||||||
pg.pg_cursize = 0;
|
pg.pg_cursize = 0;
|
||||||
pg.pg_size = pool_item.second.pg_size;
|
pg.pg_size = pool_cfg.pg_size;
|
||||||
pg.pg_minsize = pool_item.second.pg_minsize;
|
pg.pg_minsize = pool_cfg.pg_minsize;
|
||||||
pg.pg_data_size = pool_item.second.scheme == POOL_SCHEME_REPLICATED
|
pg.pg_data_size = pool_cfg.scheme == POOL_SCHEME_REPLICATED
|
||||||
? 1 : pool_item.second.pg_size - pool_item.second.parity_chunks;
|
? 1 : pool_cfg.pg_size - pool_cfg.parity_chunks;
|
||||||
pg.pool_id = pool_id;
|
pg.pool_id = pool_id;
|
||||||
pg.pg_num = pg_num;
|
pg.pg_num = pg_num;
|
||||||
pg.reported_epoch = pg_cfg.epoch;
|
pg.reported_epoch = pg_cfg.epoch;
|
||||||
@@ -888,8 +978,8 @@ void osd_t::apply_pg_config()
|
|||||||
pg.next_scrub = pg_cfg.next_scrub;
|
pg.next_scrub = pg_cfg.next_scrub;
|
||||||
pg.target_set = pg_cfg.target_set;
|
pg.target_set = pg_cfg.target_set;
|
||||||
pg.disable_pg_locks = pg_locks_localize_only &&
|
pg.disable_pg_locks = pg_locks_localize_only &&
|
||||||
(pool_item.second.scheme != POOL_SCHEME_REPLICATED ||
|
(pool_cfg.scheme != POOL_SCHEME_REPLICATED ||
|
||||||
pool_item.second.local_reads == POOL_LOCAL_READ_PRIMARY);
|
pool_cfg.local_reads == POOL_LOCAL_READ_PRIMARY);
|
||||||
if (pg.scheme == POOL_SCHEME_EC)
|
if (pg.scheme == POOL_SCHEME_EC)
|
||||||
{
|
{
|
||||||
use_ec(pg.pg_size, pg.pg_data_size, true);
|
use_ec(pg.pg_size, pg.pg_data_size, true);
|
||||||
|
|||||||
@@ -318,8 +318,7 @@ void osd_t::submit_recovery_op(osd_recovery_op_t *op)
|
|||||||
// EPIPE is totally harmless (peer is gone), others like EIO/EDOM may be not
|
// EPIPE is totally harmless (peer is gone), others like EIO/EDOM may be not
|
||||||
printf(
|
printf(
|
||||||
"[PG %u/%u] Recovery operation failed with object %jx:%jx: error %jd\n",
|
"[PG %u/%u] Recovery operation failed with object %jx:%jx: error %jd\n",
|
||||||
INODE_POOL(op->oid.inode),
|
INODE_POOL(op->oid.inode), map_to_pg(op->oid),
|
||||||
map_to_pg(op->oid, st_cli.pool_config.at(INODE_POOL(op->oid.inode)).pg_stripe_size),
|
|
||||||
op->oid.inode, op->oid.stripe, op->osd_op->reply.hdr.retval
|
op->oid.inode, op->oid.stripe, op->osd_op->reply.hdr.retval
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "object_id.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -159,11 +159,10 @@ void osd_t::reset_pg(pg_t & pg)
|
|||||||
cancel_primary_write(p.second);
|
cancel_primary_write(p.second);
|
||||||
}
|
}
|
||||||
pg.write_queue.clear();
|
pg.write_queue.clear();
|
||||||
uint64_t pg_stripe_size = st_cli.pool_config[pg.pool_id].pg_stripe_size;
|
|
||||||
for (auto it = unstable_writes.begin(); it != unstable_writes.end(); )
|
for (auto it = unstable_writes.begin(); it != unstable_writes.end(); )
|
||||||
{
|
{
|
||||||
// Forget this PG's unstable writes
|
// Forget this PG's unstable writes
|
||||||
if (INODE_POOL(it->first.oid.inode) == pg.pool_id && map_to_pg(it->first.oid, pg_stripe_size) == pg.pg_num)
|
if (INODE_POOL(it->first.oid.inode) == pg.pool_id && map_to_pg(it->first.oid) == pg.pg_num)
|
||||||
unstable_writes.erase(it++);
|
unstable_writes.erase(it++);
|
||||||
else
|
else
|
||||||
it++;
|
it++;
|
||||||
@@ -524,6 +523,7 @@ void osd_t::relock_pg(pg_t & pg)
|
|||||||
|
|
||||||
void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
|
void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
|
||||||
{
|
{
|
||||||
|
auto & pool_cfg = st_cli.pool_config.at(ps->pool_id);
|
||||||
if (role_osd == this->osd_num)
|
if (role_osd == this->osd_num)
|
||||||
{
|
{
|
||||||
// Self
|
// Self
|
||||||
@@ -533,11 +533,11 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
|
|||||||
clock_gettime(CLOCK_REALTIME, &op->tv_begin);
|
clock_gettime(CLOCK_REALTIME, &op->tv_begin);
|
||||||
op->bs_op = new blockstore_op_t();
|
op->bs_op = new blockstore_op_t();
|
||||||
op->bs_op->opcode = BS_OP_LIST;
|
op->bs_op->opcode = BS_OP_LIST;
|
||||||
op->bs_op->pg_alignment = st_cli.pool_config[ps->pool_id].pg_stripe_size;
|
op->bs_op->pg_alignment = pool_cfg.applied_pg_stripe_size;
|
||||||
op->bs_op->min_oid.inode = ((uint64_t)ps->pool_id << (64 - POOL_ID_BITS));
|
op->bs_op->min_oid.inode = ((uint64_t)ps->pool_id << (64 - POOL_ID_BITS));
|
||||||
op->bs_op->max_oid.inode = ((uint64_t)(ps->pool_id+1) << (64 - POOL_ID_BITS)) - 1;
|
op->bs_op->max_oid.inode = ((uint64_t)(ps->pool_id+1) << (64 - POOL_ID_BITS)) - 1;
|
||||||
op->bs_op->max_oid.stripe = UINT64_MAX;
|
op->bs_op->max_oid.stripe = UINT64_MAX;
|
||||||
op->bs_op->pg_count = pg_counts[ps->pool_id];
|
op->bs_op->pg_count = pool_cfg.applied_pg_count;
|
||||||
op->bs_op->pg_number = ps->pg_num-1;
|
op->bs_op->pg_number = ps->pg_num-1;
|
||||||
op->bs_op->callback = [this, ps, op, role_osd](blockstore_op_t *bs_op)
|
op->bs_op->callback = [this, ps, op, role_osd](blockstore_op_t *bs_op)
|
||||||
{
|
{
|
||||||
@@ -584,8 +584,8 @@ void osd_t::submit_list_subop(osd_num_t role_osd, pg_peering_state_t *ps)
|
|||||||
.opcode = OSD_OP_SEC_LIST,
|
.opcode = OSD_OP_SEC_LIST,
|
||||||
},
|
},
|
||||||
.list_pg = ps->pg_num,
|
.list_pg = ps->pg_num,
|
||||||
.pg_count = pg_counts[ps->pool_id],
|
.pg_count = (uint32_t)pool_cfg.applied_pg_count,
|
||||||
.pg_stripe_size = st_cli.pool_config[ps->pool_id].pg_stripe_size,
|
.pg_stripe_size = pool_cfg.applied_pg_stripe_size,
|
||||||
.min_inode = ((uint64_t)(ps->pool_id) << (64 - POOL_ID_BITS)),
|
.min_inode = ((uint64_t)(ps->pool_id) << (64 - POOL_ID_BITS)),
|
||||||
.max_inode = ((uint64_t)(ps->pool_id+1) << (64 - POOL_ID_BITS)) - 1,
|
.max_inode = ((uint64_t)(ps->pool_id+1) << (64 - POOL_ID_BITS)) - 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -334,36 +334,44 @@ pg_osd_set_state_t* pg_t::add_object_to_state(const object_id oid, const uint64_
|
|||||||
if (it == state_dict.end())
|
if (it == state_dict.end())
|
||||||
{
|
{
|
||||||
std::vector<osd_num_t> read_target;
|
std::vector<osd_num_t> read_target;
|
||||||
|
bool found = false;
|
||||||
|
uint32_t bad_mask = (LOC_OUTDATED | LOC_CORRUPTED);
|
||||||
|
retry:
|
||||||
if (scheme == POOL_SCHEME_REPLICATED)
|
if (scheme == POOL_SCHEME_REPLICATED)
|
||||||
{
|
{
|
||||||
for (auto & o: osd_set)
|
for (auto & o: osd_set)
|
||||||
{
|
{
|
||||||
if (!(o.loc_bad & (LOC_OUTDATED | LOC_CORRUPTED)))
|
if (!(o.loc_bad & bad_mask))
|
||||||
{
|
{
|
||||||
read_target.push_back(o.osd_num);
|
read_target.push_back(o.osd_num);
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (read_target.size() < pg_size)
|
if (read_target.size() < pg_size)
|
||||||
{
|
{
|
||||||
// FIXME: This is because we then use .data() and assume it's at least <pg_size> long
|
// FIXME: This is because we then use .data() and assume it's at least <pg_size> long
|
||||||
read_target.push_back(0);
|
read_target.resize(pg_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
read_target.resize(pg_size);
|
read_target.resize(pg_size);
|
||||||
for (int i = 0; i < pg_size; i++)
|
|
||||||
{
|
|
||||||
read_target[i] = 0;
|
|
||||||
}
|
|
||||||
for (auto & o: osd_set)
|
for (auto & o: osd_set)
|
||||||
{
|
{
|
||||||
if (!(o.loc_bad & (LOC_OUTDATED | LOC_CORRUPTED)))
|
if (!(o.loc_bad & bad_mask))
|
||||||
{
|
{
|
||||||
read_target[o.role] = o.osd_num;
|
read_target[o.role] = o.osd_num;
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found && (bad_mask & LOC_CORRUPTED))
|
||||||
|
{
|
||||||
|
// Allow to try reading corrupted copies in rare cases when the object is corrupted on all OSDs
|
||||||
|
bad_mask = LOC_OUTDATED;
|
||||||
|
read_target.clear();
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
state_dict[osd_set] = {
|
state_dict[osd_set] = {
|
||||||
.read_target = read_target,
|
.read_target = read_target,
|
||||||
.osd_set = osd_set,
|
.osd_set = osd_set,
|
||||||
|
|||||||
+21
-8
@@ -35,7 +35,14 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
|
|||||||
// oid.stripe = starting offset of the parity stripe
|
// oid.stripe = starting offset of the parity stripe
|
||||||
.stripe = (cur_op->req.rw.offset/pg_block_size)*pg_block_size,
|
.stripe = (cur_op->req.rw.offset/pg_block_size)*pg_block_size,
|
||||||
};
|
};
|
||||||
pg_num_t pg_num = (oid.stripe/pool_cfg.pg_stripe_size) % pg_counts[pool_id] + 1; // like map_to_pg()
|
auto pg_count = pool_cfg.applied_pg_count;
|
||||||
|
if (!pg_count)
|
||||||
|
{
|
||||||
|
// Pool config is not loaded yet
|
||||||
|
finish_op(cur_op, -EPIPE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pg_num_t pg_num = (oid.stripe/pool_cfg.applied_pg_stripe_size) % pg_count + 1; // like map_to_pg()
|
||||||
auto pg_it = pgs.find({ .pool_id = pool_id, .pg_num = pg_num });
|
auto pg_it = pgs.find({ .pool_id = pool_id, .pg_num = pg_num });
|
||||||
if (pg_it == pgs.end() || pg_it->second.state == PG_OFFLINE)
|
if (pg_it == pgs.end() || pg_it->second.state == PG_OFFLINE)
|
||||||
{
|
{
|
||||||
@@ -264,7 +271,6 @@ resume_0:
|
|||||||
}
|
}
|
||||||
cur_op->buf = alloc_read_buffer(op_data->stripes, pg ? pg->pg_data_size : 1, 0);
|
cur_op->buf = alloc_read_buffer(op_data->stripes, pg ? pg->pg_data_size : 1, 0);
|
||||||
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
||||||
op_data->st = 1;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -277,11 +283,14 @@ resume_0:
|
|||||||
op_data->degraded = 1;
|
op_data->degraded = 1;
|
||||||
cur_op->buf = alloc_read_buffer(op_data->stripes, pg->pg_size, 0);
|
cur_op->buf = alloc_read_buffer(op_data->stripes, pg->pg_size, 0);
|
||||||
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
||||||
op_data->st = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resume_1:
|
resume_1:
|
||||||
return;
|
if (op_data->n_subops > 0)
|
||||||
|
{
|
||||||
|
op_data->st = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
resume_2:
|
resume_2:
|
||||||
if (op_data->errors > 0)
|
if (op_data->errors > 0)
|
||||||
{
|
{
|
||||||
@@ -289,8 +298,9 @@ resume_2:
|
|||||||
{
|
{
|
||||||
// I/O or checksum error
|
// I/O or checksum error
|
||||||
// FIXME: ref = true ideally... because new_state != state is not necessarily true if it's freed and recreated
|
// FIXME: ref = true ideally... because new_state != state is not necessarily true if it's freed and recreated
|
||||||
op_data->object_state = mark_object_corrupted(*pg, op_data->oid, op_data->object_state, op_data->stripes, false);
|
auto new_object_state = mark_object_corrupted(*pg, op_data->oid, op_data->object_state, op_data->stripes, false);
|
||||||
goto resume_0;
|
if (new_object_state != op_data->object_state)
|
||||||
|
goto resume_0;
|
||||||
}
|
}
|
||||||
finish_op(cur_op, op_data->errcode);
|
finish_op(cur_op, op_data->errcode);
|
||||||
return;
|
return;
|
||||||
@@ -729,8 +739,11 @@ resume_1:
|
|||||||
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op);
|
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op);
|
||||||
op_data->prev_set = NULL;
|
op_data->prev_set = NULL;
|
||||||
resume_2:
|
resume_2:
|
||||||
op_data->st = 2;
|
if (op_data->n_subops > 0)
|
||||||
return;
|
{
|
||||||
|
op_data->st = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
resume_3:
|
resume_3:
|
||||||
if (op_data->errors > 0)
|
if (op_data->errors > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -134,11 +134,17 @@ void osd_t::submit_primary_subops(int submit_type, uint64_t op_version, const ui
|
|||||||
n_subops = 1;
|
n_subops = 1;
|
||||||
else
|
else
|
||||||
zero_read = -1;
|
zero_read = -1;
|
||||||
osd_op_t *subops = new osd_op_t[n_subops];
|
|
||||||
op_data->fact_ver = 0;
|
op_data->fact_ver = 0;
|
||||||
op_data->done = op_data->errors = op_data->drops = op_data->errcode = 0;
|
op_data->done = op_data->errors = op_data->drops = op_data->errcode = 0;
|
||||||
op_data->n_subops = n_subops;
|
op_data->n_subops = n_subops;
|
||||||
op_data->subops = subops;
|
if (!n_subops)
|
||||||
|
{
|
||||||
|
op_data->errcode = -EIO;
|
||||||
|
op_data->subops = NULL;
|
||||||
|
op_data->errors = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
op_data->subops = new osd_op_t[n_subops];
|
||||||
int sent = submit_primary_subop_batch(submit_type, op_data->oid.inode, op_version, op_data->stripes, osd_set, cur_op, 0, zero_read);
|
int sent = submit_primary_subop_batch(submit_type, op_data->oid.inode, op_version, op_data->stripes, osd_set, cur_op, 0, zero_read);
|
||||||
assert(sent == n_subops);
|
assert(sent == n_subops);
|
||||||
}
|
}
|
||||||
@@ -404,8 +410,9 @@ void osd_t::handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op)
|
|||||||
if (op_data->fact_ver != 0 && op_data->fact_ver != version)
|
if (op_data->fact_ver != 0 && op_data->fact_ver != version)
|
||||||
{
|
{
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr, "different fact_versions returned from %s %jx:%jx subops: %ju vs %ju\n",
|
stderr, "different fact_versions returned from %s %jx:%jx subops for a %s op: %ju vs %ju\n",
|
||||||
osd_op_names[opcode], subop->req.sec_rw.oid.inode, subop->req.sec_rw.oid.stripe, version, op_data->fact_ver
|
osd_op_names[opcode], subop->req.sec_rw.oid.inode, subop->req.sec_rw.oid.stripe,
|
||||||
|
osd_op_names[cur_op->req.hdr.opcode], version, op_data->fact_ver
|
||||||
);
|
);
|
||||||
retval = -ERANGE;
|
retval = -ERANGE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ resume_6:
|
|||||||
auto & w = op_data->unstable_writes[unstable_osd.start + i];
|
auto & w = op_data->unstable_writes[unstable_osd.start + i];
|
||||||
pool_pg_num_t wpg = {
|
pool_pg_num_t wpg = {
|
||||||
.pool_id = INODE_POOL(w.oid.inode),
|
.pool_id = INODE_POOL(w.oid.inode),
|
||||||
.pg_num = map_to_pg(w.oid, st_cli.pool_config.at(INODE_POOL(w.oid.inode)).pg_stripe_size),
|
.pg_num = map_to_pg(w.oid),
|
||||||
};
|
};
|
||||||
if (pgs.at(wpg).state & PG_ACTIVE)
|
if (pgs.at(wpg).state & PG_ACTIVE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,57 +108,31 @@ retry_1:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Read required blocks
|
// Read required blocks
|
||||||
{
|
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op);
|
||||||
if (op_data->object_state && (op_data->object_state->state & OBJ_INCOMPLETE))
|
|
||||||
{
|
|
||||||
// Allow to read version number (just version number!) from corrupted chunks
|
|
||||||
// to allow full overwrite of a corrupted object
|
|
||||||
bool found = false;
|
|
||||||
for (int role = 0; role < pg.pg_size; role++)
|
|
||||||
{
|
|
||||||
if (op_data->prev_set[role] != 0 || op_data->stripes[role].read_end > op_data->stripes[role].read_start)
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
osd_num_t corrupted_target[pg.pg_size];
|
|
||||||
for (int role = 0; role < pg.pg_size; role++)
|
|
||||||
{
|
|
||||||
corrupted_target[role] = 0;
|
|
||||||
}
|
|
||||||
for (auto & loc: op_data->object_state->osd_set)
|
|
||||||
{
|
|
||||||
if (!(loc.loc_bad & LOC_OUTDATED) && !corrupted_target[loc.role])
|
|
||||||
{
|
|
||||||
corrupted_target[loc.role] = loc.osd_num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, corrupted_target, cur_op);
|
|
||||||
goto resume_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op);
|
|
||||||
}
|
|
||||||
resume_2:
|
resume_2:
|
||||||
op_data->st = 2;
|
if (op_data->n_subops > 0)
|
||||||
return;
|
{
|
||||||
|
op_data->st = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
resume_3:
|
resume_3:
|
||||||
if (op_data->errors > 0)
|
if (op_data->errors > 0)
|
||||||
{
|
{
|
||||||
if (op_data->errcode == -EIO || op_data->errcode == -EDOM)
|
if (op_data->errcode == -EIO || op_data->errcode == -EDOM)
|
||||||
{
|
{
|
||||||
// Mark object corrupted and retry
|
// Mark object corrupted and retry
|
||||||
op_data->object_state = mark_object_corrupted(pg, op_data->oid, op_data->object_state, op_data->stripes, true);
|
pg_osd_set_state_t *new_object_state = mark_object_corrupted(pg, op_data->oid, op_data->object_state, op_data->stripes, true);
|
||||||
op_data->prev_set = op_data->object_state ? op_data->object_state->read_target.data() : pg.cur_set.data();
|
if (new_object_state != op_data->object_state)
|
||||||
if (cur_op->rmw_buf)
|
|
||||||
{
|
{
|
||||||
free(cur_op->rmw_buf);
|
op_data->object_state = new_object_state;
|
||||||
cur_op->rmw_buf = NULL;
|
op_data->prev_set = op_data->object_state ? op_data->object_state->read_target.data() : pg.cur_set.data();
|
||||||
|
if (cur_op->rmw_buf)
|
||||||
|
{
|
||||||
|
free(cur_op->rmw_buf);
|
||||||
|
cur_op->rmw_buf = NULL;
|
||||||
|
}
|
||||||
|
goto retry_1;
|
||||||
}
|
}
|
||||||
goto retry_1;
|
|
||||||
}
|
}
|
||||||
deref_object_state(pg, &op_data->object_state, true);
|
deref_object_state(pg, &op_data->object_state, true);
|
||||||
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "object_id.h"
|
#include "object_id.h"
|
||||||
#include "osd_id.h"
|
|
||||||
|
|
||||||
struct buf_len_t
|
struct buf_len_t
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ void osd_t::scrub_list(pool_pg_num_t pg_id, osd_num_t role_osd, object_id min_oi
|
|||||||
{
|
{
|
||||||
pool_id_t pool_id = pg_id.pool_id;
|
pool_id_t pool_id = pg_id.pool_id;
|
||||||
pg_num_t pg_num = pg_id.pg_num;
|
pg_num_t pg_num = pg_id.pg_num;
|
||||||
|
auto & pool_cfg = st_cli.pool_config.at(pool_id);
|
||||||
assert(!scrub_list_op);
|
assert(!scrub_list_op);
|
||||||
if (role_osd == this->osd_num)
|
if (role_osd == this->osd_num)
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ void osd_t::scrub_list(pool_pg_num_t pg_id, osd_num_t role_osd, object_id min_oi
|
|||||||
clock_gettime(CLOCK_REALTIME, &op->tv_begin);
|
clock_gettime(CLOCK_REALTIME, &op->tv_begin);
|
||||||
op->bs_op = new blockstore_op_t();
|
op->bs_op = new blockstore_op_t();
|
||||||
op->bs_op->opcode = BS_OP_LIST;
|
op->bs_op->opcode = BS_OP_LIST;
|
||||||
op->bs_op->pg_alignment = st_cli.pool_config[pool_id].pg_stripe_size;
|
op->bs_op->pg_alignment = pool_cfg.applied_pg_stripe_size;
|
||||||
if (min_oid.inode != 0 || min_oid.stripe != 0)
|
if (min_oid.inode != 0 || min_oid.stripe != 0)
|
||||||
op->bs_op->min_oid = min_oid;
|
op->bs_op->min_oid = min_oid;
|
||||||
else
|
else
|
||||||
@@ -30,7 +31,7 @@ void osd_t::scrub_list(pool_pg_num_t pg_id, osd_num_t role_osd, object_id min_oi
|
|||||||
op->bs_op->max_oid.inode = ((uint64_t)(pool_id+1) << (64 - POOL_ID_BITS)) - 1;
|
op->bs_op->max_oid.inode = ((uint64_t)(pool_id+1) << (64 - POOL_ID_BITS)) - 1;
|
||||||
op->bs_op->max_oid.stripe = UINT64_MAX;
|
op->bs_op->max_oid.stripe = UINT64_MAX;
|
||||||
op->bs_op->list_stable_limit = scrub_list_limit;
|
op->bs_op->list_stable_limit = scrub_list_limit;
|
||||||
op->bs_op->pg_count = pg_counts[pool_id];
|
op->bs_op->pg_count = pool_cfg.applied_pg_count;
|
||||||
op->bs_op->pg_number = pg_num-1;
|
op->bs_op->pg_number = pg_num-1;
|
||||||
op->bs_op->callback = [this, op](blockstore_op_t *bs_op)
|
op->bs_op->callback = [this, op](blockstore_op_t *bs_op)
|
||||||
{
|
{
|
||||||
@@ -68,8 +69,8 @@ void osd_t::scrub_list(pool_pg_num_t pg_id, osd_num_t role_osd, object_id min_oi
|
|||||||
.opcode = OSD_OP_SEC_LIST,
|
.opcode = OSD_OP_SEC_LIST,
|
||||||
},
|
},
|
||||||
.list_pg = pg_num,
|
.list_pg = pg_num,
|
||||||
.pg_count = pg_counts[pool_id],
|
.pg_count = (uint32_t)pool_cfg.applied_pg_count,
|
||||||
.pg_stripe_size = st_cli.pool_config[pool_id].pg_stripe_size,
|
.pg_stripe_size = pool_cfg.applied_pg_stripe_size,
|
||||||
.min_inode = min_oid.inode ? min_oid.inode : ((uint64_t)(pool_id) << (64 - POOL_ID_BITS)),
|
.min_inode = min_oid.inode ? min_oid.inode : ((uint64_t)(pool_id) << (64 - POOL_ID_BITS)),
|
||||||
.max_inode = ((uint64_t)(pool_id+1) << (64 - POOL_ID_BITS)) - 1,
|
.max_inode = ((uint64_t)(pool_id+1) << (64 - POOL_ID_BITS)) - 1,
|
||||||
.min_stripe = min_oid.stripe,
|
.min_stripe = min_oid.stripe,
|
||||||
@@ -249,7 +250,7 @@ void osd_t::submit_scrub_op(object_id oid)
|
|||||||
printf(
|
printf(
|
||||||
"Scrub failed with object %jx:%jx (PG %u/%u): error %jd\n",
|
"Scrub failed with object %jx:%jx (PG %u/%u): error %jd\n",
|
||||||
oid.inode, oid.stripe, INODE_POOL(oid.inode),
|
oid.inode, oid.stripe, INODE_POOL(oid.inode),
|
||||||
map_to_pg(oid, st_cli.pool_config.at(INODE_POOL(oid.inode)).pg_stripe_size),
|
map_to_pg(oid),
|
||||||
osd_op->reply.hdr.retval
|
osd_op->reply.hdr.retval
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-14
@@ -79,12 +79,8 @@ void osd_t::exec_secondary(osd_op_t *op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool osd_t::sec_check_pg_lock(osd_num_t primary_osd, const object_id &oid)
|
bool osd_t::sec_check_pg_lock(osd_num_t primary_osd, const object_id &oid, uint32_t flags)
|
||||||
{
|
{
|
||||||
if (!enable_pg_locks)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
pool_id_t pool_id = INODE_POOL(oid.inode);
|
pool_id_t pool_id = INODE_POOL(oid.inode);
|
||||||
auto pool_cfg_it = st_cli.pool_config.find(pool_id);
|
auto pool_cfg_it = st_cli.pool_config.find(pool_id);
|
||||||
if (pool_cfg_it == st_cli.pool_config.end())
|
if (pool_cfg_it == st_cli.pool_config.end())
|
||||||
@@ -92,11 +88,19 @@ bool osd_t::sec_check_pg_lock(osd_num_t primary_osd, const object_id &oid)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto & pool_cfg = pool_cfg_it->second;
|
auto & pool_cfg = pool_cfg_it->second;
|
||||||
|
if (pool_cfg.reshard_state)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!enable_pg_locks || (flags & OSD_OP_IGNORE_PG_LOCK))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (pg_locks_localize_only && (pool_cfg.scheme != POOL_SCHEME_REPLICATED || pool_cfg.local_reads == POOL_LOCAL_READ_PRIMARY))
|
if (pg_locks_localize_only && (pool_cfg.scheme != POOL_SCHEME_REPLICATED || pool_cfg.local_reads == POOL_LOCAL_READ_PRIMARY))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto ppg = (pool_pg_num_t){ .pool_id = pool_id, .pg_num = map_to_pg(oid, pool_cfg_it->second.pg_stripe_size) };
|
auto ppg = (pool_pg_num_t){ .pool_id = pool_id, .pg_num = map_to_pg(oid) };
|
||||||
auto pg_it = pgs.find(ppg);
|
auto pg_it = pgs.find(ppg);
|
||||||
if (pg_it != pgs.end() && pg_it->second.state != PG_OFFLINE)
|
if (pg_it != pgs.end() && pg_it->second.state != PG_OFFLINE)
|
||||||
{
|
{
|
||||||
@@ -140,8 +144,7 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
|||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_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_WRITE_STABLE)
|
||||||
{
|
{
|
||||||
if (!(cur_op->req.sec_rw.flags & OSD_OP_IGNORE_PG_LOCK) &&
|
if (!sec_check_pg_lock(cl->in_osd_num, cur_op->req.sec_rw.oid, cur_op->req.sec_rw.flags))
|
||||||
!sec_check_pg_lock(cl->in_osd_num, cur_op->req.sec_rw.oid))
|
|
||||||
{
|
{
|
||||||
cur_op->bs_op->retval = -EPIPE;
|
cur_op->bs_op->retval = -EPIPE;
|
||||||
secondary_op_callback(cur_op);
|
secondary_op_callback(cur_op);
|
||||||
@@ -169,8 +172,7 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
|||||||
}
|
}
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_DELETE)
|
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_DELETE)
|
||||||
{
|
{
|
||||||
if (!(cur_op->req.sec_del.flags & OSD_OP_IGNORE_PG_LOCK) &&
|
if (!sec_check_pg_lock(cl->in_osd_num, cur_op->req.sec_del.oid, cur_op->req.sec_del.flags))
|
||||||
!sec_check_pg_lock(cl->in_osd_num, cur_op->req.sec_del.oid))
|
|
||||||
{
|
{
|
||||||
cur_op->bs_op->retval = -EPIPE;
|
cur_op->bs_op->retval = -EPIPE;
|
||||||
secondary_op_callback(cur_op);
|
secondary_op_callback(cur_op);
|
||||||
@@ -190,11 +192,11 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
|||||||
#ifdef OSD_STUB
|
#ifdef OSD_STUB
|
||||||
cur_op->bs_op->retval = 0;
|
cur_op->bs_op->retval = 0;
|
||||||
#endif
|
#endif
|
||||||
if (enable_pg_locks && !(cur_op->req.sec_stab.flags & OSD_OP_IGNORE_PG_LOCK))
|
if (enable_pg_locks)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < cur_op->bs_op->len; i++)
|
for (int i = 0; i < cur_op->bs_op->len; i++)
|
||||||
{
|
{
|
||||||
if (!sec_check_pg_lock(cl->in_osd_num, ((obj_ver_id*)cur_op->buf)[i].oid))
|
if (!sec_check_pg_lock(cl->in_osd_num, ((obj_ver_id*)cur_op->buf)[i].oid, cur_op->req.sec_stab.flags))
|
||||||
{
|
{
|
||||||
cur_op->bs_op->retval = -EPIPE;
|
cur_op->bs_op->retval = -EPIPE;
|
||||||
secondary_op_callback(cur_op);
|
secondary_op_callback(cur_op);
|
||||||
@@ -213,6 +215,14 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
|||||||
secondary_op_callback(cur_op);
|
secondary_op_callback(cur_op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto pool_id = INODE_POOL(cur_op->bs_op->min_oid.inode);
|
||||||
|
if (pool_id && !sec_check_pg_lock(0, (object_id){ .inode = cur_op->bs_op->min_oid.inode }, OSD_OP_IGNORE_PG_LOCK))
|
||||||
|
{
|
||||||
|
// Check resharding state of the pool
|
||||||
|
cur_op->bs_op->retval = -EPIPE;
|
||||||
|
secondary_op_callback(cur_op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
cur_op->bs_op->pg_alignment = cur_op->req.sec_list.pg_stripe_size;
|
cur_op->bs_op->pg_alignment = cur_op->req.sec_list.pg_stripe_size;
|
||||||
cur_op->bs_op->pg_count = cur_op->req.sec_list.pg_count;
|
cur_op->bs_op->pg_count = cur_op->req.sec_list.pg_count;
|
||||||
cur_op->bs_op->pg_number = cur_op->req.sec_list.list_pg - 1;
|
cur_op->bs_op->pg_number = cur_op->req.sec_list.list_pg - 1;
|
||||||
@@ -248,8 +258,7 @@ void osd_t::exec_sec_read_bmp(osd_op_t *cur_op)
|
|||||||
void *cur_buf = reply_buf;
|
void *cur_buf = reply_buf;
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
if (!sec_check_pg_lock(cl->in_osd_num, ov[i].oid) &&
|
if (!sec_check_pg_lock(cl->in_osd_num, ov[i].oid, cur_op->req.sec_read_bmp.flags))
|
||||||
!(cur_op->req.sec_read_bmp.flags & OSD_OP_IGNORE_PG_LOCK))
|
|
||||||
{
|
{
|
||||||
free(reply_buf);
|
free(reply_buf);
|
||||||
cur_op->bs_op->retval = -EPIPE;
|
cur_op->bs_op->retval = -EPIPE;
|
||||||
|
|||||||
@@ -57,3 +57,7 @@ json11::Json::object osd_messenger_t::merge_configs(const json11::Json::object &
|
|||||||
{
|
{
|
||||||
return cli_config;
|
return cli_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::clear_immediate_ops(int peer_fd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ void disk_mock_t::erase_buffers(uint64_t begin, uint64_t end)
|
|||||||
{
|
{
|
||||||
// Cut beginning & end & stop
|
// Cut beginning & end & stop
|
||||||
uint8_t *ce = (uint8_t*)malloc_or_die(be-end);
|
uint8_t *ce = (uint8_t*)malloc_or_die(be-end);
|
||||||
memcpy(ce, it->second.iov_base + (end-bs), be-end);
|
memcpy(ce, (uint8_t*)it->second.iov_base + (end-bs), be-end);
|
||||||
uint8_t *cs = (uint8_t*)realloc(it->second.iov_base, begin-bs);
|
uint8_t *cs = (uint8_t*)realloc(it->second.iov_base, begin-bs);
|
||||||
if (!cs)
|
if (!cs)
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
@@ -221,7 +221,7 @@ void disk_mock_t::erase_buffers(uint64_t begin, uint64_t end)
|
|||||||
// Cut end & stop
|
// Cut end & stop
|
||||||
assert(be > end);
|
assert(be > end);
|
||||||
uint8_t *ce = (uint8_t*)malloc_or_die(be-end);
|
uint8_t *ce = (uint8_t*)malloc_or_die(be-end);
|
||||||
memcpy(ce, it->second.iov_base + (end-bs), be-end);
|
memcpy(ce, (uint8_t*)it->second.iov_base + (end-bs), be-end);
|
||||||
buffers[be] = (iovec){ .iov_base = ce, .iov_len = be-end };
|
buffers[be] = (iovec){ .iov_base = ce, .iov_len = be-end };
|
||||||
buffers.erase(it);
|
buffers.erase(it);
|
||||||
break;
|
break;
|
||||||
@@ -308,7 +308,7 @@ void disk_mock_t::read_item(uint8_t *to, uint64_t offset, uint64_t len)
|
|||||||
last = offset;
|
last = offset;
|
||||||
}
|
}
|
||||||
uint64_t cur_end = be < offset+len ? be : offset+len;
|
uint64_t cur_end = be < offset+len ? be : offset+len;
|
||||||
memcpy(to+last-offset, it->second.iov_base+last-bs, cur_end-last);
|
memcpy(to+last-offset, (uint8_t*)it->second.iov_base+last-bs, cur_end-last);
|
||||||
last = be;
|
last = be;
|
||||||
}
|
}
|
||||||
if (last < offset+len)
|
if (last < offset+len)
|
||||||
|
|||||||
+943
-33
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "crc32c.h"
|
#include "crc32c.h"
|
||||||
|
|
||||||
#ifdef WITH_ISAL
|
#ifdef WITH_ISAL
|
||||||
@@ -394,6 +395,7 @@ static uint8_t zero_page[4096] = {};
|
|||||||
|
|
||||||
uint32_t crc32c_pad(uint32_t prev_crc, const void *buf, size_t len, size_t left_pad, size_t right_pad)
|
uint32_t crc32c_pad(uint32_t prev_crc, const void *buf, size_t len, size_t left_pad, size_t right_pad)
|
||||||
{
|
{
|
||||||
|
assert(left_pad < 0x10000000 && right_pad < 0x10000000);
|
||||||
uint32_t r = prev_crc;
|
uint32_t r = prev_crc;
|
||||||
while (left_pad >= 4096)
|
while (left_pad >= 4096)
|
||||||
{
|
{
|
||||||
|
|||||||
+10
-2
@@ -65,7 +65,7 @@ start_etcd()
|
|||||||
--max-txn-ops=100000 --auto-compaction-retention=10 --auto-compaction-mode=revision &>./testdata/etcd$i.log &
|
--max-txn-ops=100000 --auto-compaction-retention=10 --auto-compaction-mode=revision &>./testdata/etcd$i.log &
|
||||||
eval ETCD${i}_PID=$!
|
eval ETCD${i}_PID=$!
|
||||||
else
|
else
|
||||||
node mon/mon-main.js $MON_PARAMS --antietcd_port $((ETCD_PORT+2*i-2)) --etcd_address $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 >>./testdata/mon$i.log 2>&1 &
|
node mon/mon-main.js $MON_PARAMS --antietcd_port $((ETCD_PORT+2*i-2)) --verbose 1 >>./testdata/mon$i.log 2>&1 &
|
||||||
eval ETCD${i}_PID=$!
|
eval ETCD${i}_PID=$!
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -108,12 +108,20 @@ wait_condition()
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VITASTOR_CFG='"etcd_address":"'$ETCD_URL'"'
|
||||||
|
echo "{$VITASTOR_CFG}" > ./testdata/vitastor.conf
|
||||||
|
VITASTOR_CFG=./testdata/vitastor.conf
|
||||||
|
VITASTOR_CLI="build/src/cmd/vitastor-cli --config_path $VITASTOR_CFG"
|
||||||
|
# Preload build/src/client/libfio_vitastor.so so libasan detects all symbols
|
||||||
|
VITASTOR_FIO="env LD_PRELOAD=build/src/client/libfio_vitastor.so fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -conf $VITASTOR_CFG"
|
||||||
|
OSD_ARGS="$OSD_ARGS --config_path $VITASTOR_CFG"
|
||||||
|
MON_PARAMS="$MON_PARAMS --config_path $VITASTOR_CFG"
|
||||||
|
|
||||||
if [[ -n "$ANTIETCD" ]]; then
|
if [[ -n "$ANTIETCD" ]]; then
|
||||||
ETCDCTL="node mon/node_modules/.bin/anticli -e $ETCD_URL"
|
ETCDCTL="node mon/node_modules/.bin/anticli -e $ETCD_URL"
|
||||||
MON_PARAMS="--use_antietcd 1 --antietcd_data_dir ./testdata --antietcd_persist_interval 500 $MON_PARAMS"
|
MON_PARAMS="--use_antietcd 1 --antietcd_data_dir ./testdata --antietcd_persist_interval 500 $MON_PARAMS"
|
||||||
else
|
else
|
||||||
ETCDCTL="${ETCD}ctl --endpoints=$ETCD_URL --dial-timeout=5s --command-timeout=10s"
|
ETCDCTL="${ETCD}ctl --endpoints=$ETCD_URL --dial-timeout=5s --command-timeout=10s"
|
||||||
MON_PARAMS="$MON_PARAMS"
|
|
||||||
start_etcd_cluster
|
start_etcd_cluster
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ if [[ -n "$ANTIETCD" ]]; then
|
|||||||
start_etcd $i
|
start_etcd $i
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
node mon/mon-main.js $MON_PARAMS --etcd_address $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 >>./testdata/mon.log 2>&1 &
|
node mon/mon-main.js $MON_PARAMS --verbose 1 >>./testdata/mon.log 2>&1 &
|
||||||
MON_PID=$!
|
MON_PID=$!
|
||||||
fi
|
fi
|
||||||
wait_etcd
|
wait_etcd
|
||||||
@@ -40,7 +40,7 @@ start_osd_on()
|
|||||||
{
|
{
|
||||||
local i=$1
|
local i=$1
|
||||||
local dev=$2
|
local dev=$2
|
||||||
build/src/osd/vitastor-osd --osd_num $i --bind_address $ETCD_IP $NO_SAME $OSD_ARGS --etcd_address $ETCD_URL \
|
build/src/osd/vitastor-osd --osd_num $i --bind_address $ETCD_IP $NO_SAME $OSD_ARGS \
|
||||||
$(build/src/disk_tool/vitastor-disk simple-offsets --format options $OFFSET_ARGS $dev $OFFSET_ARGS 2>/dev/null) \
|
$(build/src/disk_tool/vitastor-disk simple-offsets --format options $OFFSET_ARGS $dev $OFFSET_ARGS 2>/dev/null) \
|
||||||
>>./testdata/osd$i.log 2>&1 &
|
>>./testdata/osd$i.log 2>&1 &
|
||||||
eval OSD${i}_PID=$!
|
eval OSD${i}_PID=$!
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ TEST_NAME=local_read POOLCFG='"local_reads":"random",' ./test_heal.sh
|
|||||||
SCHEME=ec ./test_heal.sh
|
SCHEME=ec ./test_heal.sh
|
||||||
ANTIETCD=1 ./test_heal.sh
|
ANTIETCD=1 ./test_heal.sh
|
||||||
|
|
||||||
|
./test_checksum.sh
|
||||||
|
OLD=1 ./test_checksum.sh
|
||||||
|
./test_corrupt_all.sh
|
||||||
|
OLD=1 ./test_corrupt_all.sh
|
||||||
|
|
||||||
./test_reweight_half.sh
|
./test_reweight_half.sh
|
||||||
./test_snapshot_pool2.sh
|
./test_snapshot_pool2.sh
|
||||||
./test_snapshot_read_bitmap.sh
|
./test_snapshot_read_bitmap.sh
|
||||||
@@ -138,6 +143,7 @@ OLD=1 SCHEME=xor ./test_scrub.sh
|
|||||||
OLD=1 PG_SIZE=3 ./test_scrub.sh
|
OLD=1 PG_SIZE=3 ./test_scrub.sh
|
||||||
OLD=1 PG_SIZE=6 PG_MINSIZE=4 OSD_COUNT=6 SCHEME=ec ./test_scrub.sh
|
OLD=1 PG_SIZE=6 PG_MINSIZE=4 OSD_COUNT=6 SCHEME=ec ./test_scrub.sh
|
||||||
OLD=1 SCHEME=ec ./test_scrub.sh
|
OLD=1 SCHEME=ec ./test_scrub.sh
|
||||||
|
OLD=1 ./test_partwr_csum.sh
|
||||||
|
|
||||||
TEST_NAME=old_csum_32k_dmj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_metadata false --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS ./test_heal.sh
|
TEST_NAME=old_csum_32k_dmj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_metadata false --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS ./test_heal.sh
|
||||||
TEST_NAME=old_csum_32k_dj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS ./test_heal.sh
|
TEST_NAME=old_csum_32k_dj OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS ./test_heal.sh
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ PG_COUNT=2048
|
|||||||
GLOBAL_CONFIG=',"osd_out_time":1'
|
GLOBAL_CONFIG=',"osd_out_time":1'
|
||||||
. `dirname $0`/run_3osds.sh
|
. `dirname $0`/run_3osds.sh
|
||||||
|
|
||||||
LD_PRELOAD="build/src/client/libfio_vitastor.so" \
|
$VITASTOR_FIO -bs=4M -direct=1 -iodepth=1 -end_fsync=1 \
|
||||||
fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -bs=4M -direct=1 -iodepth=1 -end_fsync=1 \
|
-rw=write -pool=1 -inode=1 -size=128M -cluster_log_level=10
|
||||||
-rw=write -etcd=$ETCD_URL -pool=1 -inode=1 -size=128M -cluster_log_level=10
|
|
||||||
|
|
||||||
start_osd 4
|
start_osd 4
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ wait_finish_rebalance 60
|
|||||||
sleep 1
|
sleep 1
|
||||||
kill -9 $OSD4_PID
|
kill -9 $OSD4_PID
|
||||||
sleep 1
|
sleep 1
|
||||||
build/src/cmd/vitastor-cli --etcd_address $ETCD_URL rm-osd --force 4
|
$VITASTOR_CLI rm-osd --force 4
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
. `dirname $0`/run_3osds.sh
|
. `dirname $0`/run_3osds.sh
|
||||||
|
|
||||||
build/src/test/test_cas --pool_id 1 --inode_id 1 --etcd_address $ETCD_URL
|
build/src/test/test_cas --pool_id 1 --inode_id 1 --config_path $VITASTOR_CFG
|
||||||
|
|
||||||
format_green OK
|
format_green OK
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
GLOBAL_CONFIG=',"pg_reshard_chunk_size":100'
|
||||||
OSD_COUNT=${OSD_COUNT:-6}
|
OSD_COUNT=${OSD_COUNT:-6}
|
||||||
PG_COUNT=16
|
PG_COUNT=16
|
||||||
|
|
||||||
@@ -7,9 +8,8 @@ PG_COUNT=16
|
|||||||
|
|
||||||
NOBJ=$(((128*8+PG_DATA_SIZE-1)/PG_DATA_SIZE))
|
NOBJ=$(((128*8+PG_DATA_SIZE-1)/PG_DATA_SIZE))
|
||||||
|
|
||||||
LD_PRELOAD="build/src/client/libfio_vitastor.so" \
|
$VITASTOR_FIO -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write \
|
||||||
fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write \
|
-pool=1 -inode=2 -size=128M -cluster_log_level=10
|
||||||
-etcd=$ETCD_URL -pool=1 -inode=2 -size=128M -cluster_log_level=10
|
|
||||||
|
|
||||||
try_change()
|
try_change()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ GLOBAL_CONFIG=',"client_retry_interval":1000'
|
|||||||
|
|
||||||
. `dirname $0`/run_3osds.sh
|
. `dirname $0`/run_3osds.sh
|
||||||
|
|
||||||
LD_PRELOAD="build/src/client/libfio_vitastor.so" \
|
$VITASTOR_FIO -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write -pool=1 -inode=2 -size=128M
|
||||||
fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write \
|
|
||||||
-etcd=$ETCD_URL -pool=1 -inode=2 -size=128M
|
|
||||||
|
|
||||||
LD_PRELOAD="build/src/client/libfio_vitastor.so" \
|
$VITASTOR_FIO -bs=4k -direct=1 -iodepth=4 -rw=randrw -pool=1 -inode=2 -size=128M -loops=100 \
|
||||||
fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -bs=4k -direct=1 -iodepth=4 -rw=randrw \
|
-cluster_log_level=3 -runtime=60 &>./testdata/fio.log &
|
||||||
-etcd=$ETCD_URL -pool=1 -inode=2 -size=128M -loops=100 -cluster_log_level=3 -runtime=60 &>./testdata/fio.log &
|
|
||||||
FIO_PID=$!
|
FIO_PID=$!
|
||||||
|
|
||||||
try_change()
|
try_change()
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ IMG_SIZE=128
|
|||||||
$ETCDCTL put /vitastor/config/inode/1/1 '{"name":"testimg","size":'$((IMG_SIZE*1024*1024))'}'
|
$ETCDCTL put /vitastor/config/inode/1/1 '{"name":"testimg","size":'$((IMG_SIZE*1024*1024))'}'
|
||||||
|
|
||||||
# Write
|
# Write
|
||||||
LD_PRELOAD="build/src/client/libfio_vitastor.so" \
|
$VITASTOR_FIO -bs=1M -direct=1 -iodepth=4 \
|
||||||
fio -thread -name=test -ioengine=build/src/client/libfio_vitastor.so -bs=1M -direct=1 -iodepth=4 \
|
-mirror_file=./testdata/bin/mirror.bin -end_fsync=1 -rw=write -image=testimg -runtime=10
|
||||||
-mirror_file=./testdata/bin/mirror.bin -end_fsync=1 -rw=write -etcd=$ETCD_URL -image=testimg -runtime=10
|
|
||||||
|
|
||||||
# Intentionally corrupt OSD data and restart it
|
# Intentionally corrupt OSD data and restart it
|
||||||
kill $OSD1_PID
|
kill $OSD1_PID
|
||||||
@@ -29,7 +28,7 @@ wait_up 10
|
|||||||
|
|
||||||
# Read everything back
|
# Read everything back
|
||||||
qemu-img convert -S 4096 -p \
|
qemu-img convert -S 4096 -p \
|
||||||
-f raw "vitastor:etcd_host=127.0.0.1\:$ETCD_PORT/v3:image=testimg" \
|
-f raw "vitastor:config_path=$VITASTOR_CFG:image=testimg" \
|
||||||
-O raw ./testdata/bin/read.bin
|
-O raw ./testdata/bin/read.bin
|
||||||
|
|
||||||
diff ./testdata/bin/read.bin ./testdata/bin/mirror.bin
|
diff ./testdata/bin/read.bin ./testdata/bin/mirror.bin
|
||||||
|
|||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
SCHEME=replicated
|
||||||
|
PG_SIZE=2
|
||||||
|
OSD_COUNT=2
|
||||||
|
IMG_SIZE=128
|
||||||
|
OSD_ARGS="--data_csum_type crc32c --csum_block_size 4k --inmemory_journal false --journal_trim_interval $((IMG_SIZE*8)) $OSD_ARGS"
|
||||||
|
OFFSET_ARGS="--data_csum_type crc32c --csum_block_size 4k $OFFSET_ARGS"
|
||||||
|
GLOBAL_CONFIG=',"client_eio_retry_interval":0'
|
||||||
|
. `dirname $0`/run_3osds.sh
|
||||||
|
check_qemu
|
||||||
|
|
||||||
|
$ETCDCTL put /vitastor/config/inode/1/1 '{"name":"testimg","size":'$((IMG_SIZE*1024*1024))'}'
|
||||||
|
|
||||||
|
# Write
|
||||||
|
$VITASTOR_FIO -bs=1M -direct=1 -iodepth=4 -end_fsync=1 -rw=write -image=testimg -runtime=10
|
||||||
|
#$VITASTOR_FIO -bs=4k -direct=1 -iodepth=16 -end_fsync=1 -rw=randwrite -image=testimg -number_ios=10000
|
||||||
|
|
||||||
|
# Intentionally corrupt OSD data and restart both of them
|
||||||
|
kill $OSD1_PID $OSD2_PID
|
||||||
|
data_offset=$(build/src/disk_tool/vitastor-disk simple-offsets ./testdata/bin/test_osd1.bin $OFFSET_ARGS | grep data_offset | awk '{print $2}')
|
||||||
|
truncate -s $data_offset ./testdata/bin/test_osd1.bin
|
||||||
|
dd if=/dev/zero of=./testdata/bin/test_osd1.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
|
||||||
|
truncate -s $data_offset ./testdata/bin/test_osd2.bin
|
||||||
|
dd if=/dev/zero of=./testdata/bin/test_osd2.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
|
||||||
|
start_osd 1
|
||||||
|
start_osd 2
|
||||||
|
|
||||||
|
# Wait until start
|
||||||
|
wait_up 10
|
||||||
|
|
||||||
|
# Trigger scrub
|
||||||
|
$ETCDCTL put /vitastor/pg/history/1/1 `$ETCDCTL get --print-value-only /vitastor/pg/history/1/1 | jq -s -c '(.[0] // {}) + {"next_scrub":1}'`
|
||||||
|
|
||||||
|
# Wait for scrub to finish
|
||||||
|
wait_condition 300 "$ETCDCTL get --prefix /vitastor/pg/history/ --print-value-only | jq -s -e '([ .[] | select(.next_scrub == 0 or .next_scrub == null) ] | length) == $PG_COUNT'" Scrubbing
|
||||||
|
|
||||||
|
# Verify that ALL objects are now corrupted+incomplete
|
||||||
|
$VITASTOR_CLI describe --json &>./testdata/describe.json
|
||||||
|
$VITASTOR_CLI describe --json | jq -e '[ .[] | select(.corrupted) ] | length == '$((IMG_SIZE * 8 * PG_SIZE))
|
||||||
|
|
||||||
|
# Check that we can remove or overwrite them
|
||||||
|
$VITASTOR_FIO -bs=4M -direct=1 -iodepth=1 -end_fsync=1 -rw=write -offset=$((IMG_SIZE/2))M -image=testimg -runtime=10
|
||||||
|
$VITASTOR_CLI rm-data --pool 1 --inode 1
|
||||||
|
|
||||||
|
format_green OK
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
. `dirname $0`/common.sh
|
. `dirname $0`/common.sh
|
||||||
|
|
||||||
node mon/mon-main.js $MON_PARAMS --etcd_address $ETCD_URL --etcd_prefix "/vitastor" >>./testdata/mon.log 2>&1 &
|
node mon/mon-main.js $MON_PARAMS >>./testdata/mon.log 2>&1 &
|
||||||
MON_PID=$!
|
MON_PID=$!
|
||||||
wait_etcd
|
wait_etcd
|
||||||
|
|
||||||
@@ -28,8 +28,8 @@ $ETCDCTL put /vitastor/osd/stats/3 '{"host":"host2","size":1073741824,"time":"'$
|
|||||||
$ETCDCTL put /vitastor/osd/stats/4 '{"host":"host2","size":1073741824,"time":"'$TIME'"}'
|
$ETCDCTL put /vitastor/osd/stats/4 '{"host":"host2","size":1073741824,"time":"'$TIME'"}'
|
||||||
$ETCDCTL put /vitastor/osd/stats/5 '{"host":"host3","size":1073741824,"time":"'$TIME'"}'
|
$ETCDCTL put /vitastor/osd/stats/5 '{"host":"host3","size":1073741824,"time":"'$TIME'"}'
|
||||||
$ETCDCTL put /vitastor/osd/stats/6 '{"host":"host3","size":1073741824,"time":"'$TIME'"}'
|
$ETCDCTL put /vitastor/osd/stats/6 '{"host":"host3","size":1073741824,"time":"'$TIME'"}'
|
||||||
build/src/cmd/vitastor-cli --etcd_address $ETCD_URL osd-tree
|
$VITASTOR_CLI osd-tree
|
||||||
# check that it doesn't fail
|
# check that it doesn't fail
|
||||||
build/src/cmd/vitastor-cli --etcd_address $ETCD_URL create-pool testpool --ec 2+1 -n 32
|
$VITASTOR_CLI create-pool testpool --ec 2+1 -n 32
|
||||||
|
|
||||||
format_green OK
|
format_green OK
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user