Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32721046b8 | ||
|
|
f8de201625 | ||
|
|
e7f30c4939 | ||
|
|
8e441f4173 | ||
|
|
80a05c052a | ||
|
|
a972c13571 | ||
|
|
9372df4beb | ||
|
|
24c27386b6 | ||
|
|
394de5e9aa | ||
|
|
be61e76df0 | ||
|
|
5cad126987 | ||
|
|
afa50a05b8 | ||
|
|
9ff833178e | ||
|
|
b152baa733 | ||
|
|
8dbbf4097a | ||
|
|
17cb875e10 | ||
|
|
03e15338e5 | ||
|
|
dccc549e33 | ||
|
|
440a8bc33a | ||
|
|
78a21542da | ||
|
|
75ba9509ad | ||
|
|
8b58fe9808 | ||
|
|
331f345a78 | ||
|
|
d0a4ac914b | ||
|
|
e42b812323 | ||
|
|
ac5c753735 | ||
|
|
f9eb497105 | ||
|
|
3489c99640 | ||
|
|
a21dcd4480 | ||
|
|
340849248b | ||
|
|
ee462c4375 | ||
|
|
0053a0d825 | ||
|
|
cdeecead8d | ||
|
|
c0e34b19c9 | ||
|
|
e8abe99205 | ||
|
|
8225b37f53 | ||
|
|
62b29b03c5 | ||
|
|
ae2f570b58 | ||
|
|
d7f854ab3e | ||
|
|
29640d1aac | ||
|
|
66a016f8a1 | ||
|
|
34d01f44db | ||
|
|
3956982112 | ||
|
|
713b17e574 | ||
|
|
4efa48c9ca | ||
|
|
26d5e1ed00 | ||
|
|
9ba3444cfa | ||
|
|
96c0001127 | ||
|
|
56a14cc0bd | ||
|
|
6ca1b9eca9 | ||
|
|
876dbd730a | ||
|
|
4398ee0377 | ||
|
|
2c673a90bd | ||
|
|
a8371498e1 | ||
|
|
2a093cbfb5 | ||
|
|
094a181491 | ||
|
|
fe30783135 | ||
|
|
e9a6c58418 |
@@ -720,24 +720,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_snapshot_chain_enc_gcm:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||
steps:
|
||||
- name: Run test
|
||||
id: test
|
||||
timeout-minutes: 3
|
||||
run: TEST_NAME=enc_gcm ENCRYPTED=1 VITASTOR_CFG=',"proto_checksums":"gcm"' /root/vitastor/tests/test_snapshot_chain.sh
|
||||
- name: Print logs
|
||||
if: always() && steps.test.outcome == 'failure'
|
||||
run: |
|
||||
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||
echo "-------- $i --------"
|
||||
cat $i
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_old_snapshot_chain:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8...3.30)
|
||||
|
||||
project(vitastor)
|
||||
|
||||
set(VITASTOR_VERSION "3.0.12")
|
||||
set(VITASTOR_VERSION "3.0.10")
|
||||
|
||||
include(CTest)
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VITASTOR_VERSION ?= v3.0.12
|
||||
VITASTOR_VERSION ?= v3.0.10
|
||||
|
||||
all: build push
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ spec:
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: vitalif/vitastor-csi:v3.0.12
|
||||
image: vitalif/vitastor-csi:v3.0.10
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
||||
@@ -121,7 +121,7 @@ spec:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
image: vitalif/vitastor-csi:v3.0.12
|
||||
image: vitalif/vitastor-csi:v3.0.10
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ package vitastor
|
||||
|
||||
const (
|
||||
vitastorCSIDriverName = "csi.vitastor.io"
|
||||
vitastorCSIDriverVersion = "3.0.12"
|
||||
vitastorCSIDriverVersion = "3.0.10"
|
||||
)
|
||||
|
||||
// Config struct fills the parameters of request or user input
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
vitastor (3.0.12-1) unstable; urgency=medium
|
||||
vitastor (3.0.10-1) unstable; urgency=medium
|
||||
|
||||
* Bugfixes
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VITASTOR_VERSION ?= v3.0.12
|
||||
VITASTOR_VERSION ?= v3.0.10
|
||||
|
||||
all: build push
|
||||
|
||||
|
||||
@@ -12,7 +12,12 @@ EnvironmentFile=/etc/vitastor/etcd.conf
|
||||
SyslogIdentifier=etcd
|
||||
ExecStart=bash -c 'docker run --rm -i -v /var/lib/vitastor/etcd:/data \
|
||||
--log-driver none --network host $CONTAINER_OPTIONS --name vitastor-etcd \
|
||||
$ETCD_IMAGE /usr/local/bin/etcd --data-dir /data
|
||||
$ETCD_IMAGE /usr/local/bin/etcd --name "$ETCD_NAME" --data-dir /data \
|
||||
--snapshot-count 10000 --advertise-client-urls http://$ETCD_IP:2379 --listen-client-urls http://$ETCD_IP:2379 \
|
||||
--initial-advertise-peer-urls http://$ETCD_IP:2380 --listen-peer-urls http://$ETCD_IP:2380 \
|
||||
--initial-cluster-token vitastor-etcd-1 --initial-cluster "$ETCD_INITIAL_CLUSTER" \
|
||||
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \
|
||||
--auto-compaction-retention=10 --auto-compaction-mode=revision'
|
||||
ExecStop=docker stop vitastor-etcd
|
||||
Restart=always
|
||||
StartLimitInterval=0
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
# Desired Vitastor version
|
||||
VITASTOR_VERSION=v3.0.12
|
||||
VITASTOR_VERSION=v3.0.10
|
||||
|
||||
# Additional arguments for all containers
|
||||
# For example, you may want to specify a custom logging driver here
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
ETCD_IMAGE=quay.io/coreos/etcd:v3.5.18
|
||||
ETCD_NAME=""
|
||||
ETCD_IP=""
|
||||
ETCD_INITIAL_CLUSTER=""
|
||||
|
||||
@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
|
||||
The instruction is very simple.
|
||||
|
||||
1. Download a Docker image of the desired version: \
|
||||
`docker pull vitalif/vitastor:v3.0.12`
|
||||
`docker pull vitalif/vitastor:v3.0.10`
|
||||
2. Install scripts to the host system: \
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.10 install.sh`
|
||||
3. Reload udev rules: \
|
||||
`udevadm control --reload-rules`
|
||||
4. Enable the vitastor-host service: \
|
||||
|
||||
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
|
||||
Инструкция по установке максимально простая.
|
||||
|
||||
1. Скачайте Docker-образ желаемой версии: \
|
||||
`docker pull vitalif/vitastor:v3.0.12`
|
||||
`docker pull vitalif/vitastor:v3.0.10`
|
||||
2. Установите скрипты в хост-систему командой: \
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.10 install.sh`
|
||||
3. Перезагрузите правила udev: \
|
||||
`udevadm control --reload-rules`
|
||||
4. Включите сервис vitastor-host: \
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- CMake
|
||||
- jerasure, c-ares headers and libraries
|
||||
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
|
||||
- tcmalloc (google-perftools-dev)
|
||||
|
||||
## Basic instructions
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- CMake
|
||||
- Заголовки и библиотеки jerasure, c-ares
|
||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
|
||||
- tcmalloc (google-perftools-dev)
|
||||
|
||||
## Базовая инструкция
|
||||
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
[Документация](../../README-ru.md#документация) → Безопасность
|
||||
|
||||
-----
|
||||
|
||||
[Read in English](security.en.md)
|
||||
|
||||
# Оглавление
|
||||
|
||||
⚠️ Предупреждение: детальное описание настроек безопасности достаточно длинное.
|
||||
|
||||
Если не боитесь - читайте [Подробное описание](#подробное-описание).
|
||||
|
||||
Если хотите просто быстро настроить Vitastor с шифрованием - читайте начало статьи.
|
||||
|
||||
- [Быстрая настройка](#быстрая-настройка)
|
||||
-
|
||||
|
||||
# Быстрая настройка
|
||||
|
||||
|
||||
|
||||
# Пользовательские сценарии
|
||||
|
||||
Зачем всё это нужно вам?
|
||||
|
||||
|
||||
|
||||
# Подробное описание
|
||||
|
||||
Начиная с версии 3.1.0, в Vitastor есть следующие функции:
|
||||
1. Шифрование соединений с etcd (TLS)
|
||||
2. Шифрование соединений с OSD (AES-GCM) - по выбору либо только заголовков, либо и заголовков, и данных
|
||||
3. Сквозное шифрование данных образов (AES-XTS)
|
||||
4. Хранения ключей шифрования AES-XTS во внешнем Vault
|
||||
5. Контрольных сумм данных на транспортном уровне с секретной "солью"
|
||||
6. Аутентификация с помощью TLS (X.509) сертификатов и закрытых ключей
|
||||
7. Разграничение прав доступа клиентов к данным etcd
|
||||
8. Разграничение прав доступа клиентов к данным самих образов (на стороне OSD)
|
||||
|
||||
По умолчанию шифрование, аутентификация и авторизация отключены, но, начиная с 3.1.0,
|
||||
используются контрольные суммы данных на транспортном уровне (`proto_checksums=payload`).
|
||||
|
||||
## Шифрование соединений с etcd (TLS)
|
||||
|
||||
Варианты настройки:
|
||||
- Без шифрования (http)
|
||||
- С шифрованием (https)
|
||||
- С клиентским сертификатом, но при выключенной авторизации (`use_auth=false`) - используется
|
||||
отдельный сертификат и ключ: `etcd_client_cert`, `etcd_client_key`
|
||||
- С клиентским сертификатом, при включённой аутентификации на уровне OSD - используется общий
|
||||
сертификат и ключ: для OSD - `osd_cert` и `osd_pkey`, для клиентов - `cert` и `pkey`
|
||||
|
||||
## Шифрование соединений с OSD (AES-GCM)
|
||||
|
||||
Варианты настройки:
|
||||
- Без шифрования и без контрольных сумм: `proto_checksums=none`.
|
||||
- Без шифрования, с контрольными суммами данных: `proto_checksums=payload` (можно не указывать,
|
||||
т.к. это значение по умолчанию). При этом контрольные суммы можно отключить на стороне
|
||||
клиента либо использовать более старые версии клиента, не поддерживающие контрольные суммы.
|
||||
Если нужно запретить подключение клиентов без контрольных сумм, можно использовать опцию
|
||||
`force_proto_checksums=payload`.
|
||||
- С шифрованием заголовков и контрольными суммами данных: активируется при установленных опциях
|
||||
`cert`, `pkey`, `osd_ca` на стороне клиента и `osd_cert`, `osd_pkey`, `osd_ca`, `client_ca`
|
||||
на стороне OSD, при `proto_checksums=payload`. При этом по умолчанию запрещается
|
||||
отключение контрольных сумм на уровне клиента, то есть используется `force_proto_checksums=payload`.
|
||||
- С полным шифрованием всего трафика: аналогично прошлому варианту, но с `proto_checksums=gcm`.
|
||||
Клиенту при этом по умолчанию разрешается понизить уровень защиты до контрольных сумм, но
|
||||
это тоже можно запретить через `force_proto_checksums=gcm`. Данный вариант не является рекомендуемым,
|
||||
так как добавлен в первую очередь для возможной поддержки небезопасных (публичных) сетей и
|
||||
больше всего снижает производительность. В частности, если одновременно использовать полное
|
||||
шифрование трафика и сквозное шифрование образов AES-XTS, то данные будут шифроваться дважды.
|
||||
|
||||
Для шифрования используется алгоритм AES-256-GCM и собственный упрощённый протокол согласования
|
||||
ключей, полностью аналогичный TLS 1.3 ECDHE.
|
||||
|
||||
## Сквозное шифрование данных образов (AES-XTS)
|
||||
|
||||
Клиент Vitastor поддерживает шифрование данных каждого образа своим ключом. В этом случае на OSD
|
||||
уходят уже зашифрованные данные и сами OSD не видят настоящее содержимое образов. Разные ключи
|
||||
в том числе могут иметь разные снимки или клоны одного и того же образа. Например, можно сделать
|
||||
базовый образ ВМ (условный Debian Linux) нешифрованным, но наследовать от него шифрованные образы
|
||||
клиентских ВМ.
|
||||
|
||||
Ключи шифрования образов могут храниться либо в etcd, либо во внешнем Vault. Во втором случае
|
||||
в etcd хранятся только ID ключей, а Vitastor вообще не имеет доступа к данным образов. Для
|
||||
использования Vault нужно создать образ с опцией `--enc_key vault:ID`, а в конфигурации указать
|
||||
опции:
|
||||
- vault_url
|
||||
- vault_ca
|
||||
- vault_client_cert
|
||||
- vault_client_key
|
||||
|
||||
Ещё раз повторимся, что если AES-XTS используется с полным шифрованием трафика (`proto_checksums=gcm`),
|
||||
то данные образов шифруются дважды - сначала AES-XTS, а потом AES-GCM. Можете использовать,
|
||||
только если вы совсем параноик :-).
|
||||
|
||||
## Производительность шифрования
|
||||
|
||||
У вас может возникнуть вопрос - а как быстро всё это прекрасное шифрование работает?
|
||||
|
||||
Ответ - скорость сильно зависит от процессора. Складывается она из нескольких вещей:
|
||||
|
||||
-
|
||||
|
||||
TODO: vitastor-cli bench.
|
||||
|
||||
## Аутентификация по сертификатам
|
||||
|
||||
При включённом шифровании клиенты, OSD и мониторы Vitastor аутентифицируются по сертификатам
|
||||
как при соединениях с etcd (Antietcd), так и с OSD.
|
||||
|
||||
Для OSD и мониторов должны использоваться отдельные сертификаты - либо самоподписанные, либо
|
||||
подписанные отдельными CA (`osd_ca` и `mon_ca`). При этом все OSD могут использовать один и
|
||||
тот же сертификат и все мониторы тоже могут использовать один и тот же сертификат, так как
|
||||
привилегии разных OSD или разных мониторов ничем не отличаются (теоретически можно было бы
|
||||
сделать разграничение сертификатов OSD по пулам, но пока что такой необходимости не было).
|
||||
|
||||
Также сертификат монитора может быть вообще не нужен, если Antietcd встраивается в сам монитор.
|
||||
В этом случае монитор и так имеет доступ ко всем данным etcd прямо в памяти.
|
||||
|
||||
Каждый клиент должен иметь свой сертификат, подписанный общим корневым сертификатом
|
||||
для клиентов (`client_ca`). Common Name сертификата должно равняться имени пользователя.
|
||||
|
||||
## Модель прав доступа
|
||||
|
||||
Привилегии пользователей хранятся в данных etcd в ключах `/vitastor/config/user/<имя>`.
|
||||
|
||||
У пользователя есть 2 свойства:
|
||||
- Тип:
|
||||
- Клиент (`type=client` или не указано) - может читать и модифицировать только явным образом
|
||||
разрешённые образы.
|
||||
- Администратор (`type=admin`) - может читать и модифицировать все образы, а также администрировать
|
||||
кластер: смотреть общую статистику и состояние, создавать и удалять OSD и так далее.
|
||||
- Список имён групп, членом которых пользователь является.
|
||||
|
||||
У образов есть 3 свойства:
|
||||
- Владелец (owner) - имя пользователя, которому разрешено и читать, и менять образ
|
||||
- Группа владельцев (owner_group) - имя группы владельцев
|
||||
- Группа читатетей (reader_group) - имя группы пользователей, которым разрешено читать образ
|
||||
|
||||
У пулов есть 1 свойство:
|
||||
- Группа создателей (creator_group) - имя группы пользователей, которым разрешено создавать образы в пуле
|
||||
|
||||
## Права доступа к данным etcd
|
||||
|
||||
Привилегии реализуются через Antietcd во всех режимах работы. Если используется etcd, то
|
||||
Antietcd выступает в роли фильтрующего прокси, при этом он может быть встроен в монитор
|
||||
Vitastor или запущен отдельно. В этом случае etcd должен разрешать входящие подключения
|
||||
только от Antietcd, а все остальные компоненты должны соединяться с Antietcd.
|
||||
|
||||
Если же используется Antietcd, то привилегии реализуются в нём самом.
|
||||
|
||||
Если используется встроенный в монитор Antietcd, то привилегии включаются либо параметром
|
||||
`use_auth: true`, либо, если этот параметр не указан - включается автоматически, если задан
|
||||
любой из параметров `client_ca`, `osd_ca`, `mon_ca`. При этом монитор требует указания
|
||||
параметров `client_ca` и `osd_ca`, а если не используется режим проксирования в etcd -
|
||||
также `antietcd_server_ca`, чтобы Antietcd мог отличать кластерные соединения от клиентских.
|
||||
|
||||
Если используется отдельно стоящий Antietcd, привилегии нужно включать явным образом.
|
||||
|
||||
Встроенные привилегии etcd не поддерживаются по причине их многочисленных недоработок:
|
||||
- Аутентификация по сертификатам не работает в REST интерфейсе etcd,
|
||||
- Привилегии хранятся отдельно от k/v и не могут участвовать в транзакциях,
|
||||
- Менять привилегии может только администратор (root)
|
||||
- Нет поддержки фильтрации ответов чтения по привилегиям.
|
||||
|
||||
Подробный список привилегий на ключи в etcd [смотрите ниже](#привилегии-etcd).
|
||||
|
||||
## Права доступа к данным OSD
|
||||
|
||||
Регулируется опцией `use_auth`, либо, если она не указана, включается автоматически,
|
||||
если используется шифрование, то есть, если заданы опции `osd_ca` и `client_ca`.
|
||||
|
||||
OSD аутентифицирует клиентов по сертификатам и разрешает каждому клиенту только
|
||||
то, что ему разрешено согласно модели прав доступа.
|
||||
|
||||
Подробный список разрешаемых OSD операций [смотрите ниже](#привилегии-osd).
|
||||
|
||||
## Права доступа к API
|
||||
|
||||
[vitastor-cli serve](../usage/cli.ru.md#serve) также поддерживает клиентскую
|
||||
аутентификацию по сертификатам. Принимаются только сертификаты, подписанные
|
||||
`client_ca`. В качестве серверного сертификата используется отдельный сертификат
|
||||
`server_cert` с ключом `server_key`.
|
||||
|
||||
При этом для корректной работы `vitastor-cli serve` он сам должен использовать
|
||||
для доступа в Vitastor сертификат (`cert`+`pkey`) пользователя с правами
|
||||
администратора (`type=admin`).
|
||||
|
||||
Обычным клиентам при доступе к API разрешаются только API-операции с образами,
|
||||
доступными им либо на чтение (для чтения), либо на запись (для модификации).
|
||||
Все остальные API-вызовы разрешаются только для администраторов.
|
||||
|
||||
Подробный список разрешаемых API операций [смотрите ниже](#привилегии-api).
|
||||
|
||||
## Привилегии etcd
|
||||
|
||||
Ниже все названия ключей приведены без общего префикса `/vitastor`.
|
||||
|
||||
Разрешённые операции с ключами в Antietcd для клиентов (`type=client`):
|
||||
- Только чтение:
|
||||
- Разрешено всегда:
|
||||
- `/config/global`
|
||||
- `/config/node_placement`
|
||||
- `/config/pools`
|
||||
- `/pg/config`
|
||||
- `/osd/state/*`
|
||||
- `/pg/state/*`
|
||||
- `/index/maxid/*`
|
||||
- Для образов, которые [может читать пользователь](#модель-прав-доступа):
|
||||
- `/config/inode/*`
|
||||
- `/index/image/*`
|
||||
- `/inode/stats/*`
|
||||
- Чтение и запись:
|
||||
- Для пулов, в которых может создавать образы пользователь:
|
||||
- `/index/maxid/*`
|
||||
- Для образов, которыми владеет пользователь:
|
||||
- `/config/inode/*`
|
||||
- `/index/image/*`
|
||||
|
||||
Разрешённые операции с ключами в Antietcd для администраторов (`type=admin`):
|
||||
- Чтение:
|
||||
- `/stats`
|
||||
- `/mon/*`
|
||||
- `/pg/*`
|
||||
- `/pgstats/*`
|
||||
- `/inode/stats/*`
|
||||
- `/pool/stats/*`
|
||||
- Чтение и запись:
|
||||
- `/config/*`
|
||||
- `/osd/*`
|
||||
- `/index/*`
|
||||
- `/pg/history/*`
|
||||
|
||||
Разрешённые операции с ключами в etcd для OSD:
|
||||
- Чтение:
|
||||
- `/pg/config`
|
||||
- `/config/*`
|
||||
- Чтение и запись:
|
||||
- `/osd/*`
|
||||
- `/pg/state/*`
|
||||
- `/pg/history/*`
|
||||
- `/pgstats/*`
|
||||
|
||||
Разрешённые операции с ключами в etcd для мониторов:
|
||||
- Чтение:
|
||||
- `/config/*`
|
||||
- `/osd/*`
|
||||
- `/pgstats/*`
|
||||
- Чтение и запись:
|
||||
- `/pg/config`
|
||||
- `/stats`
|
||||
- `/history/last_clean_pgs`
|
||||
- `/mon/*`
|
||||
- `/pg/history/*`
|
||||
- `/inode/stats/*`
|
||||
- `/pool/stats/*`
|
||||
|
||||
## Привилегии OSD
|
||||
|
||||
Клиентские операции:
|
||||
- READ - разрешено для образов, доступных пользователю на чтение.
|
||||
- WRITE, DELETE, SCRUB - разрешены для образов, доступных пользователю на запись.
|
||||
- SYNC - операция не связана с образом и разрешена всегда.
|
||||
- DESCRIBE - операция разрешена только для администраторов (используются командами
|
||||
`vitastor-cli describe` и `fix`).
|
||||
- PING - операция разрешена всегда.
|
||||
- SHOW_CONFIG - операция разрешена всегда, однако если в ней клиент представляется
|
||||
как OSD, то проверяется, что он использует сертификат, подписанный `osd_ca`.
|
||||
- SEC_LIST (листинг) - разрешена другим OSD и администраторам с любыми параметрами,
|
||||
а обычным клиентам разрешена только для запросов, ограниченных образом, доступным
|
||||
пользователю на чтение.
|
||||
|
||||
Кластерные операции - разрешаются только другим OSD:
|
||||
- SEC_READ
|
||||
- SEC_WRITE
|
||||
- SEC_WRITE_STABLE
|
||||
- SEC_SYNC
|
||||
- SEC_STABILIZE
|
||||
- SEC_ROLLBACK
|
||||
- SEC_DELETE
|
||||
- SEC_READ_BMP
|
||||
- SEC_LOCK
|
||||
|
||||
## Привилегии API
|
||||
|
||||
Клиентам (пользователям с `type=client`) разрешаются операции:
|
||||
- image/list - для образов, которые пользователь может читать.
|
||||
- image/create - для пулов, в которых пользователю разрешено создавать образы, либо
|
||||
для создания снимков образов, которыми пользователь владеет.
|
||||
- image/delete, image/flatten, image/modify - для образов, которыми пользователь владеет.
|
||||
|
||||
Все остальные операции разрешаются только администраторам (`type=admin`).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Таким образом, доступны следующие варианты настройки:
|
||||
|
||||
### Mon в роли Etcd proxy
|
||||
|
||||
Mon
|
||||
- use_antietcd: true
|
||||
- etcd_proxy = {
|
||||
urls: [],
|
||||
cert = <antietcd.pem>,
|
||||
key,
|
||||
ca = <etcd.pem>,
|
||||
}
|
||||
- antietcd_cert = antietcd.pem
|
||||
- antietcd_key
|
||||
|
||||
etcd
|
||||
--client-cert-auth --cert-file=etcd.pem --key-file=etcd.key --trusted-ca-file=antietcd.pem \
|
||||
--peer-client-cert-auth --peer-cert-file=etcd.pem --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.pem
|
||||
|
||||
### Mon с отдельным Antietcd Proxy
|
||||
|
||||
Mon
|
||||
- use_antietcd: false
|
||||
- etcd_ca = antietcd.pem
|
||||
|
||||
Antietcd
|
||||
--client_cert_auth 1 --auth_filter vitastor_auth_filter.js --etcd_proxy url1,url2,... \
|
||||
--cert antietcd.pem --key antietcd.key --ca client_ca.pem --osd_ca osd_ca.pem \
|
||||
--etcd_cert antietcd.pem --etcd_key antietcd.key --etcd_ca etcd.pem
|
||||
|
||||
etcd
|
||||
--client-cert-auth --cert-file=etcd.pem --key-file=etcd.key --trusted-ca-file=antietcd.pem \
|
||||
--peer-client-cert-auth --peer-cert-file=etcd.pem --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.pem
|
||||
|
||||
### Mon со встроенным Antietcd
|
||||
|
||||
Mon
|
||||
- use_antietcd: true
|
||||
- use_auth: true
|
||||
- antietcd_cert = antietcd.pem
|
||||
- antietcd_key
|
||||
|
||||
### Отдельный Antietcd
|
||||
|
||||
Mon
|
||||
- use_antietcd: false
|
||||
- etcd_ca = antietcd.pem
|
||||
|
||||
Antietcd
|
||||
--client_cert_auth 1 --auth_filter vitastor_auth_filter.js
|
||||
|
||||
## Варианты настройки
|
||||
|
||||
### Настройка по умолчанию
|
||||
|
||||
Используются только контрольные суммы данных на транспортном уровне. Соединения с etcd не шифруются.
|
||||
Аутентификация и авторизация не используется, любой клиент имеет доступ ко всем данным кластера.
|
||||
|
||||
Аналог настройки:
|
||||
- proto_checksums: payload
|
||||
|
||||
### Полная защита
|
||||
|
||||
Везде
|
||||
- osd_ca
|
||||
- client_ca
|
||||
- etcd_ca = antietcd.pem
|
||||
|
||||
OSD
|
||||
- osd_cert
|
||||
- osd_pkey
|
||||
|
||||
Клиент
|
||||
- cert
|
||||
- pkey
|
||||
|
||||
### Только защита etcd
|
||||
|
||||
- etcd_ca
|
||||
- etcd_cert
|
||||
- etcd_key
|
||||
|
||||
### antietcd и только защита antietcd
|
||||
|
||||
- etcd_ca
|
||||
- etcd_cert
|
||||
- etcd_key
|
||||
- use_antietcd: true
|
||||
- antietcd_cert = etcd_ca
|
||||
- antietcd_key
|
||||
- antietcd_ca = etcd_cert
|
||||
|
||||
### Полное шифрование протокола, включая данные
|
||||
|
||||
Внимание: если включить этот вариант защиты и при этом
|
||||
|
||||
### Только контрольные суммы на транспортном уровне, без шифрования
|
||||
|
||||
## Настройка Vault/OpenBao
|
||||
|
||||
openssl req -days 3650 -x509 -addext basicConstraints=critical,CA:TRUE,pathlen:1 --addext subjectAltName=DNS:vault \
|
||||
-new -newkey rsa:4096 -nodes -keyout vault.key -out vault.crt
|
||||
|
||||
bao status -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200
|
||||
|
||||
bao operator init -n 1 -t 1 -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200
|
||||
|
||||
bao operator unseal -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200
|
||||
|
||||
bao auth enable -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200 cert
|
||||
|
||||
bao secrets enable -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200 -path=secret kv-v1
|
||||
|
||||
bao kv put -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200 secret/vitastor/testimg3 key=$(openssl rand -hex 64)
|
||||
|
||||
cat >testimg3.policy <<EOF
|
||||
path "/secret/vitastor/testimg3" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
EOF
|
||||
|
||||
bao policy write -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200 testimg3 testimg3.policy
|
||||
|
||||
bao write -ca-cert /etc/openbao/tls/vault.crt -address=https://vault:8200 auth/cert/certs/testimg3 certificate=@testimg3.crt display_name=testimg3 token_ttl=24h token_policies=testimg3
|
||||
|
||||
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key --json '{}' https://vault:8200/v1/auth/cert/login
|
||||
|
||||
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key -H 'X-Vault-Token: s.Qkrm78BeK7Rqdz5MA3eJZNbu' https://vault:8200/v1/secret/vitastor/testimg3
|
||||
+14
-44
@@ -3,7 +3,6 @@
|
||||
|
||||
const AntiEtcd = require('antietcd');
|
||||
|
||||
const vitastor_auth_filter = require('./vitastor_auth_filter.js');
|
||||
const vitastor_persist_filter = require('./vitastor_persist_filter.js');
|
||||
const { b64, local_ips } = require('./utils.js');
|
||||
|
||||
@@ -28,66 +27,37 @@ class AntiEtcdAdapter
|
||||
is_local['::'] = true;
|
||||
is_local[''] = true;
|
||||
// split :, 3 -> <schema>:<//ip>:<port>
|
||||
const cluster_local = cluster.map(s =>
|
||||
{
|
||||
const m = /^https?:\/\/(?:\[(.*)\]|([^\[\:]+))(?::(\d+))?$/.exec(s);
|
||||
return [ m[2] || m[1], m[3] || 2379 ];
|
||||
});
|
||||
const selected = cluster_local.filter(ip => is_local[ip[0]] && (!cfg_port || ip[1] == cfg_port));
|
||||
const selected = cluster.map(s => s.split(':', 3)).filter(ip => is_local[ip[1].substr(2)] && (!cfg_port || ip[2] == cfg_port));
|
||||
if (selected.length > 1)
|
||||
{
|
||||
console.error('More than 1 etcd_address matches local IPs: '+(selected.join(', '))+', please specify port');
|
||||
console.error('More than 1 etcd_address matches local IPs, please specify port');
|
||||
process.exit(1);
|
||||
}
|
||||
else if (selected.length == 1)
|
||||
{
|
||||
const antietcd_config = {
|
||||
ip: selected[0][0],
|
||||
port: selected[0][1],
|
||||
ip: selected[0][1].substr(2),
|
||||
port: selected[0][2],
|
||||
cert: config.antietcd_cert,
|
||||
key: config.antietcd_key,
|
||||
ca: config.antietcd_ca,
|
||||
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][1]+'.json.gz'),
|
||||
ca: config.etcd_ca,
|
||||
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][2]+'.json.gz'),
|
||||
persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }),
|
||||
node_id: selected[0][0]+':'+selected[0][1], // node_id = ip:port
|
||||
node_id: selected[0][1].substr(2)+':'+selected[0][2], // node_id = ip:port
|
||||
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c.replace(/^(https?:\/\/)/, '')] = c; return a; }, {})),
|
||||
cluster_key: (config.etcd_prefix || '/vitastor'),
|
||||
stale_read: 1,
|
||||
log_level: 1,
|
||||
};
|
||||
if (config.etcd_proxy)
|
||||
{
|
||||
// Monitor may use the builtin etcd_proxy mode
|
||||
if (!config.etcd_proxy.urls)
|
||||
{
|
||||
console.error('etcd_proxy.urls are empty');
|
||||
process.exit(1);
|
||||
}
|
||||
antietcd_config.etcd_proxy = config.etcd_proxy.urls;
|
||||
antietcd_config.etcd_cert = config.etcd_proxy.cert;
|
||||
antietcd_config.etcd_key = config.etcd_proxy.key;
|
||||
antietcd_config.etcd_ca = config.etcd_proxy.ca;
|
||||
delete antietcd_config.data;
|
||||
delete antietcd_config.persist_filter;
|
||||
delete antietcd_config.cluster;
|
||||
delete antietcd_config.cluster_key;
|
||||
}
|
||||
const use_auth = config.use_auth || config.use_auth == null && config.client_ca;
|
||||
if (use_auth)
|
||||
if (config.use_auth)
|
||||
{
|
||||
antietcd_config.client_cert_auth = true;
|
||||
antietcd_config.auth_filter = vitastor_auth_filter;
|
||||
antietcd_config.ca = config.client_ca;
|
||||
antietcd_config.osd_ca = config.osd_ca;
|
||||
antietcd_config.mon_ca = config.mon_ca;
|
||||
if (!config.etcd_proxy)
|
||||
antietcd_config.auth_filter = require('./vitastor_auth_filter.js');
|
||||
antietcd_config.peer_ca = config.antietcd_server_ca;
|
||||
if (!config.antietcd_server_ca || config.antietcd_server_ca == config.etcd_ca)
|
||||
{
|
||||
antietcd_config.peer_ca = config.antietcd_server_ca;
|
||||
if (!config.antietcd_server_ca || config.antietcd_server_ca == config.client_ca)
|
||||
{
|
||||
console.error('Secure setup requires separate antietcd_server_ca (for signing antietcd server certificates) and client_ca (for signing client certificates)');
|
||||
process.exit(1);
|
||||
}
|
||||
console.error('Secure setup requires separate antietcd_server_ca (for signing antietcd server certificates) and etcd_ca (for signing client certificates)');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
for (const key in config)
|
||||
@@ -214,7 +184,7 @@ class AntiEtcdAdapter
|
||||
await new Promise(ok => setTimeout(ok, timeout-(Date.now()-prev)));
|
||||
}
|
||||
prev = Date.now();
|
||||
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { user_type: 'mon' });
|
||||
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { username: 'root' });
|
||||
if (res.error)
|
||||
{
|
||||
console.error('Failed to query antietcd '+path+' (retry '+retry+'/'+retries+'): '+res.error);
|
||||
|
||||
+2
-2
@@ -627,7 +627,7 @@ class Mon
|
||||
if (this.state.pg.history[pool_id] &&
|
||||
this.state.pg.history[pool_id][pg])
|
||||
{
|
||||
pg_history[pg-1] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
|
||||
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
|
||||
}
|
||||
}
|
||||
const real_prev_pgs = [];
|
||||
@@ -719,7 +719,7 @@ class Mon
|
||||
this.next_recheck_timer = null;
|
||||
this.next_recheck_at = 0;
|
||||
this.schedule_recheck();
|
||||
}, (this.next_recheck_at-now)*1000);
|
||||
}, now-this.next_recheck_at);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vitastor-mon",
|
||||
"version": "3.0.12",
|
||||
"version": "3.0.10",
|
||||
"description": "Vitastor SDS monitor service",
|
||||
"main": "mon-main.js",
|
||||
"scripts": {
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
||||
finish_pg_history(merged_history[1]);
|
||||
for (let i = 0; i < new_pg_count; i++)
|
||||
{
|
||||
new_pg_history[i] = JSON.parse(JSON.stringify(merged_history[1]));
|
||||
new_pg_history[i] = { ...merged_history[1] };
|
||||
}
|
||||
}
|
||||
// Mark history keys for removed PGs as removed
|
||||
@@ -102,7 +102,7 @@ function scale_pg_count(prev_pgs, new_pg_count)
|
||||
{
|
||||
for (let i = prev_pgs.length; i < new_pg_count; i++)
|
||||
{
|
||||
prev_pgs[i] = [ ...prev_pgs[i % prev_pgs.length] ];
|
||||
prev_pgs[i] = prev_pgs[i % prev_pgs.length];
|
||||
}
|
||||
}
|
||||
else if (prev_pgs.length > new_pg_count)
|
||||
|
||||
+44
-327
@@ -1,278 +1,38 @@
|
||||
#!/usr/bin/node
|
||||
// Simple Vitastor etcd / antietcd / TLS configurator
|
||||
// Simple systemd unit generator for etcd
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: MIT
|
||||
|
||||
// USAGE:
|
||||
// 1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes
|
||||
// 2) Run ./make-etcd.js. It will create the etcd service on one of specified IPs
|
||||
|
||||
const child_process = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const readline = require('readline');
|
||||
|
||||
run().catch(e => { console.error(e); process.exit(1); });
|
||||
|
||||
const help_text = `Initialize a Vitastor cluster (etcd, vitastor.conf and TLS certificates)
|
||||
(c) Vitaliy Filippov, 2019+ (MIT)
|
||||
|
||||
USAGE:
|
||||
1) Create a minimal vitastor.conf with etcd_address, osd_network and (optionally) use_auth.
|
||||
Example: {"etcd_address":["http://10.0.0.10:2379","http://10.0.0.11:2379","http://10.0.0.12:2379"],"use_auth":false,"osd_network":"10.0.0.0/24"}
|
||||
Or: {"etcd_address":["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],"use_auth":true,"osd_network":"10.0.0.0/24"}
|
||||
2) Run: ${process.argv[1]} [./vitastor.conf]
|
||||
You can run it on etcd/monitor nodes or on an external node.
|
||||
It configures etcd, generates TLS certificates (on the first or external node), copies them
|
||||
to other etcd/monitor nodes, and updates vitastor.conf with TLS options.
|
||||
3) If you have OSD-only nodes, run:
|
||||
${process.argv[1]} --copy-to-osd-node NODE_NAME ./vitastor.conf
|
||||
It copies vitastor.conf and required TLS certificates to that node.
|
||||
|
||||
OPTIONS:
|
||||
--gen-certs
|
||||
force certificate generation even if it's not the first node
|
||||
--no-certs
|
||||
disable certificate generation
|
||||
--no-copy
|
||||
do not copy initial certificates to other nodes
|
||||
--copy-to-osd-node NODE[,NODE2,...]
|
||||
copy vitastor.conf and TLS certificates required for OSDs to NODES using scp
|
||||
--copy-to-mon-node NODE[,NODE2,...]
|
||||
copy vitastor.conf and TLS certificates required for monitor and etcd to NODES using scp
|
||||
--copy-to-client-node NODE[,NODE2,...]
|
||||
copy vitastor.conf and TLS certificates required for clients to NODES using scp
|
||||
`;
|
||||
|
||||
async function run()
|
||||
{
|
||||
let config_path = '/etc/vitastor/vitastor.conf';
|
||||
let config_dir = '/etc/vitastor/';
|
||||
let gen_certs = 'auto';
|
||||
let copy_initial = true;
|
||||
let copy_to_osd =
|
||||
for (let i = 2; i < process.argv.length; i++)
|
||||
const config_path = process.argv[2] || '/etc/vitastor/vitastor.conf';
|
||||
if (config_path == '-h' || config_path == '--help')
|
||||
{
|
||||
if (arg == '-h' || arg == '--help')
|
||||
{
|
||||
console.log(help_text);
|
||||
process.exit(0);
|
||||
}
|
||||
else if (arg == '--only-certs')
|
||||
{
|
||||
i++;
|
||||
gen_certs =
|
||||
}
|
||||
else if (arg == '--copy')
|
||||
{
|
||||
i++;
|
||||
copy = process.argv[i];
|
||||
if (copy !== 'ask' && copy !== 'yes' && copy !== 'no')
|
||||
{
|
||||
console.error('--copy should be "ask", "yes" or "no"');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
else if (arg[0] == '-')
|
||||
{
|
||||
console.error('Unknown option: '+arg[0]);
|
||||
process.exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
config_path = arg;
|
||||
}
|
||||
console.log(
|
||||
'Initialize systemd etcd service for Vitastor\n'+
|
||||
'(c) Vitaliy Filippov, 2019+ (MIT)\n'+
|
||||
'\n'+
|
||||
'USAGE:\n'+
|
||||
'1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes\n'+
|
||||
'2) Run '+process.argv[1]+' [config_path]\n'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
if (!fs.existsSync(config_path))
|
||||
{
|
||||
console.log(config_path+' is missing');
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(fs.readFileSync(config_path, { encoding: 'utf-8' }));
|
||||
if (!config.etcd_address)
|
||||
{
|
||||
console.log("etcd_address is missing in "+config_path);
|
||||
process.exit(1);
|
||||
}
|
||||
const etcds = (config.etcd_address instanceof Array ? config.etcd_address : (''+config.etcd_address).split(/,/))
|
||||
.map(s => /^(https?):\/\/(\[[^\]]+\]|[^\[\]\:\/]+)(?::(\d+))?/.exec(s.toLowerCase()))
|
||||
.filter(s => s)
|
||||
.map(s => ({
|
||||
scheme: s[1],
|
||||
addr: s[2].indexOf(':') && s[2][0] != '[' ? '['+s[2]+']' : s[2],
|
||||
ip: s[2][0] == '[' ? s[2].substr(1, s[2].length-2) : s[2],
|
||||
port: s[3],
|
||||
}));
|
||||
const tls = etcds.filter(e => e.scheme === 'https').length > 0;
|
||||
const use_auth = tls && config.use_auth;
|
||||
const num = select_local_etcd(etcds);
|
||||
if (tls)
|
||||
{
|
||||
if (gen_certs === 'yes')
|
||||
{
|
||||
gen_certs = true;
|
||||
console.log('Certificate generation is requested explicitly, generating');
|
||||
}
|
||||
else if (gen_certs === 'no')
|
||||
{
|
||||
gen_certs = false;
|
||||
console.log('Certificate generation is disabled explicitly, skipping');
|
||||
}
|
||||
else if (num < 0)
|
||||
{
|
||||
gen_certs = true;
|
||||
console.log('No matching IPs in etcd_address from '+config_path+', only generating certificates');
|
||||
}
|
||||
else if (fs.existsSync("/etc/vitastor/etcd.crt"))
|
||||
{
|
||||
gen_certs = false;
|
||||
console.log('/etc/vitastor/etcd.crt already exists, assuming certificates are already generated');
|
||||
}
|
||||
else if (num === 0)
|
||||
{
|
||||
gen_certs = true;
|
||||
console.log('This is monitor node 1, generating certificates');
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('This is monitor node '+(num+1)+', /etc/vitastor/etcd.crt does not exist, please copy certificates to this node');
|
||||
process.exit(1);
|
||||
}
|
||||
if (gen_certs)
|
||||
{
|
||||
if (copy === 'ask')
|
||||
copy = await ask_copy('Copy certificates and vitastor.conf to other nodes after generation?');
|
||||
copy = (copy === 'y' || copy === 'yes');
|
||||
await make_certs(dir, copy);
|
||||
}
|
||||
await write_auth_config(config, config_path);
|
||||
}
|
||||
if (num < 0)
|
||||
{
|
||||
console.log('No matching IPs in etcd_address from '+config_path);
|
||||
process.exit(tls && gen_certs ? 0 : 1);
|
||||
}
|
||||
await configure_etcd();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
async function ask_copy(question)
|
||||
{
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
prompt: '> ',
|
||||
});
|
||||
let copy;
|
||||
while (true)
|
||||
{
|
||||
copy = await new Promise(ok => rl.question(question, ok));
|
||||
if (copy != 'y' && copy != 'n' && copy != 'yes' && copy != 'no')
|
||||
console.log('Please type "yes" or "no"');
|
||||
else
|
||||
break;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
async function make_certs(dir, copy)
|
||||
{
|
||||
console.log(`-----
|
||||
Generating certificates in ${dir}
|
||||
-----
|
||||
`);
|
||||
await make_ca("/O=Vitastor etcd CA", dir+"etcd_ca");
|
||||
await make_signed("/CN=Vitastor etcd", dir+"etcd", dir+"etcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
|
||||
if (use_auth)
|
||||
{
|
||||
await make_ca("/O=Vitastor Antietcd CA", dir+"antietcd_ca");
|
||||
await make_signed("/CN=Vitastor Antietcd", dir+"antietcd", dir+"antietcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
|
||||
await make_ca("/CN=Vitastor OSD", dir+"osd");
|
||||
await make_ca("/O=Vitastor Client CA", dir+"client_ca");
|
||||
await make_signed("/CN=admin", dir+"admin", dir+"client_ca");
|
||||
}
|
||||
if (use_auth)
|
||||
{
|
||||
console.log(`-----
|
||||
Certificates generated, commands to copy them:
|
||||
- Monitor+OSD node:
|
||||
cd ${dir} && scp antietcd_ca.crt antietcd.crt antietcd.key osd.crt osd.key client_ca.crt etcd_ca.crt etcd.crt etcd.key root@NODE:/etc/vitastor/
|
||||
- Monitor node:
|
||||
cd ${dir} && scp antietcd_ca.crt antietcd.crt antietcd.key osd.crt client_ca.crt etcd_ca.crt etcd.crt etcd.key root@NODE:/etc/vitastor/
|
||||
- OSD node:
|
||||
cd ${dir} && scp antietcd_ca.crt osd.crt osd.key client_ca.crt root@NODE:/etc/vitastor/
|
||||
-----
|
||||
`);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log(`-----
|
||||
Certificates generated, commands to copy them:
|
||||
- Monitor node:
|
||||
cd ${dir} && scp etcd_ca.crt etcd.crt etcd.key root@NODE:/etc/vitastor/
|
||||
-----
|
||||
`);
|
||||
}
|
||||
if (copy)
|
||||
{
|
||||
const to_copy = use_auth
|
||||
? [ "antietcd_ca.crt", "antietcd.crt", "antietcd.key", "osd.crt", "osd.key", "client_ca.crt", "etcd_ca.crt", "etcd.crt", "etcd.key" ]
|
||||
: [ "etcd_ca.crt", "etcd.crt", "etcd.key" ];
|
||||
for (const node of etcds)
|
||||
{
|
||||
await system("scp "+dir+to_copy.join(" "+dir)+" root@"+node.ip+"/etc/vitastor/");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
console.warn('Certificates generated in /etc/vitastor, please copy them to other nodes');
|
||||
}
|
||||
}
|
||||
|
||||
async function write_auth_config(config, config_path)
|
||||
{
|
||||
const auth = {};
|
||||
if (use_auth)
|
||||
{
|
||||
auth["use_antietcd"] = true;
|
||||
auth["etcd_proxy"] = {
|
||||
urls: etcds.map(e => e.ip+':2381'),
|
||||
cert: "/etc/vitastor/antietcd.crt",
|
||||
key: "/etc/vitastor/antietcd.key",
|
||||
ca: "/etc/vitastor/etcd_ca.crt",
|
||||
};
|
||||
auth["antietcd_cert"] = "/etc/vitastor/antietcd.crt";
|
||||
auth["antietcd_key"] = "/etc/vitastor/antietcd.key";
|
||||
auth["etcd_ca"] = "/etc/vitastor/antietcd_ca.crt";
|
||||
auth["osd_cert"] = "/etc/vitastor/osd.crt";
|
||||
auth["osd_pkey"] = "/etc/vitastor/osd.key";
|
||||
auth["osd_ca"] = "/etc/vitastor/osd.crt";
|
||||
auth["client_ca"] = "/etc/vitastor/client_ca.crt";
|
||||
auth["cert"] = "/etc/vitastor/admin.crt";
|
||||
auth["pkey"] = "/etc/vitastor/admin.key";
|
||||
}
|
||||
else
|
||||
{
|
||||
auth["etcd_ca"] = "/etc/vitastor/etcd.crt";
|
||||
}
|
||||
for (const k in auth)
|
||||
{
|
||||
if ((k in config) && JSON.stringify(auth[k]) != JSON.stringify(config[k]))
|
||||
{
|
||||
// Auth options already overridden with non-default
|
||||
console.log(k+" is already overridden in "+config_path+", skipping config update");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const k in auth)
|
||||
{
|
||||
config[k] = auth[k];
|
||||
}
|
||||
console.log(`-----
|
||||
Updating ${config_path}
|
||||
-----
|
||||
`);
|
||||
fs.writeFileSync(config_path, JSON.stringify(config, 0, 4));
|
||||
}
|
||||
|
||||
async configure_etcd()
|
||||
{
|
||||
const in_docker = fs.existsSync("/etc/vitastor/etcd.conf") &&
|
||||
fs.existsSync("/etc/vitastor/docker.conf");
|
||||
if (!in_docker && fs.existsSync("/etc/systemd/system/vitastor-etcd.service"))
|
||||
@@ -285,49 +45,30 @@ async configure_etcd()
|
||||
console.log("/etc/systemd/system/etcd.service already exists");
|
||||
process.exit(1);
|
||||
}
|
||||
const etcd_url = etcds[num].scheme + '://' + etcds[num].addr;
|
||||
const options = {
|
||||
name: 'etcd'+etcds[num].ip.replace(/[^0-9a-z_]/ig, '_'),
|
||||
advertise_client_urls: etcd_url+':'+(use_auth ? 2381 : 2379),
|
||||
listen_client_urls: etcd_url+':'+(use_auth ? 2381 : 2379),
|
||||
initial_advertise_peer_urls: etcd_url+':2380',
|
||||
listen_peer_urls: etcd_url+':2380',
|
||||
initial_cluster_token: 'vitastor-etcd-1',
|
||||
initial_cluster_state: 'new',
|
||||
initial_cluster: etcds.map(e => `etcd${e.ip.replace(/[^0-9a-z_]/ig, '_')}=${e.scheme}://${e.addr}:2380`).join(','),
|
||||
snapshot_count: 10000,
|
||||
max_txn_ops: 100000,
|
||||
max_request_bytes: 104857600,
|
||||
auto_compaction_retention: 10,
|
||||
auto_compaction_mode: 'revision',
|
||||
};
|
||||
if (tls)
|
||||
const config = JSON.parse(fs.readFileSync(config_path, { encoding: 'utf-8' }));
|
||||
if (!config.etcd_address)
|
||||
{
|
||||
options['cert_file'] = '/etc/vitastor/etcd.crt';
|
||||
options['key_file'] = '/etc/vitastor/etcd.key';
|
||||
if (use_auth)
|
||||
{
|
||||
options['client_cert_auth'] = '1';
|
||||
options['trusted_ca_file'] = '/etc/vitastor/antietcd.crt';
|
||||
}
|
||||
options['peer_cert_file'] = '/etc/vitastor/etcd.crt';
|
||||
options['peer_key_file'] = '/etc/vitastor/etcd.key';
|
||||
if (use_auth)
|
||||
{
|
||||
options['peer_client_cert_auth'] = '1';
|
||||
options['peer_trusted_ca_file'] = '/etc/vitastor/etcd.crt';
|
||||
}
|
||||
console.log("etcd_address is missing in "+config_path);
|
||||
process.exit(1);
|
||||
}
|
||||
let etcd_conf = fs.existsSync("/etc/vitastor/etcd.conf")
|
||||
? fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' })
|
||||
: "";
|
||||
for (const k in options)
|
||||
const etcds = (config.etcd_address instanceof Array ? config.etcd_address : (''+config.etcd_address).split(/,/))
|
||||
.map(s => (''+s).replace(/^https?:\/\/|(:\d+)?(\/.*)?$/g, '').replace(/^\[(.*)\]$/, '$1').toLowerCase());
|
||||
const num = select_local_etcd(etcds);
|
||||
if (num < 0)
|
||||
{
|
||||
etcd_conf = replace_env(etcd_conf, 'ETCD_'+k.toUpperCase().replace(/-/, '_'), options[k]);
|
||||
console.log('No matching IPs in etcd_address from '+config_path);
|
||||
process.exit(0);
|
||||
}
|
||||
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
|
||||
const etcd_url = 'http://' + (etcds[num].indexOf(':') >= 0 ? '['+etcds[num]+']' : etcds[num]);
|
||||
const etcd_name = 'etcd'+etcds[num].replace(/[^0-9a-z_]/ig, '_');
|
||||
const etcd_cluster = etcds.map(e => `etcd${e.replace(/[^0-9a-z_]/ig, '_')}=http://${e.indexOf(':') >= 0 ? '['+e+']' : e}:2380`).join(',');
|
||||
if (in_docker)
|
||||
{
|
||||
let etcd_conf = fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' });
|
||||
etcd_conf = replace_env(etcd_conf, 'ETCD_NAME', etcd_name);
|
||||
etcd_conf = replace_env(etcd_conf, 'ETCD_IP', etcds[num]);
|
||||
etcd_conf = replace_env(etcd_conf, 'ETCD_INITIAL_CLUSTER', etcd_cluster);
|
||||
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
|
||||
console.log('etcd for Vitastor configured. Run `systemctl enable --now vitastor-etcd` to start etcd');
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -342,8 +83,12 @@ Wants=network-online.target local-fs.target time-sync.target
|
||||
[Service]
|
||||
Restart=always
|
||||
Environment=GOGC=50
|
||||
EnvironmentFile=/etc/vitastor/etcd.conf
|
||||
ExecStart=etcd --data-dir /var/lib/etcd/vitastor
|
||||
ExecStart=etcd --name ${etcd_name} --data-dir /var/lib/etcd/vitastor \\
|
||||
--snapshot-count 10000 --advertise-client-urls ${etcd_url}:2379 --listen-client-urls ${etcd_url}:2379 \\
|
||||
--initial-advertise-peer-urls ${etcd_url}:2380 --listen-peer-urls ${etcd_url}:2380 \\
|
||||
--initial-cluster-token vitastor-etcd-1 --initial-cluster ${etcd_cluster} \\
|
||||
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \\
|
||||
--auto-compaction-retention=10 --auto-compaction-mode=revision
|
||||
WorkingDirectory=/var/lib/etcd/vitastor
|
||||
ExecStartPre=+chown -R etcd /var/lib/etcd/vitastor
|
||||
User=etcd
|
||||
@@ -361,6 +106,7 @@ WantedBy=multi-user.target
|
||||
// Disable distribution etcd unit and enable our one
|
||||
await system(`systemctl disable --now etcd`);
|
||||
await system(`systemctl enable --now vitastor-etcd`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function replace_env(text, key, value)
|
||||
@@ -373,29 +119,16 @@ function replace_env(text, key, value)
|
||||
function select_local_etcd(etcds)
|
||||
{
|
||||
const ifaces = os.networkInterfaces();
|
||||
const local = {};
|
||||
for (const ifname in ifaces)
|
||||
{
|
||||
for (const iface of ifaces[ifname])
|
||||
{
|
||||
const addr = iface.address;
|
||||
if (iface.family == 'IPv6')
|
||||
local[addr.toLowerCase()] = local['['+addr.toLowerCase()+']'] = true;
|
||||
else
|
||||
local[addr] = true;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < etcds.length; i++)
|
||||
{
|
||||
if (local[etcds[i].addr])
|
||||
return i;
|
||||
}
|
||||
for (let i = 0; i < etcds.length; i++)
|
||||
if (etcds[i] == iface.address.toLowerCase())
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
async function system(cmd)
|
||||
{
|
||||
console.log('Running '+cmd);
|
||||
const cp = child_process.spawn(cmd, { shell: true, stdio: [ 0, 1, 2 ] });
|
||||
let finish_cb;
|
||||
cp.on('exit', () => finish_cb && finish_cb());
|
||||
@@ -403,19 +136,3 @@ async function system(cmd)
|
||||
await new Promise(ok => finish_cb = ok);
|
||||
return cp.exitCode;
|
||||
}
|
||||
|
||||
async function make_ca(subj, filename)
|
||||
{
|
||||
if (await system("openssl req -days 3650 -x509 -subj '"+subj+"' -addext basicConstraints=critical,CA:TRUE,pathlen:1"+
|
||||
" -new -newkey rsa:4096 -nodes -keyout "+filename+".key -out "+filename+".crt"))
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function make_signed(subj, f, ca, san)
|
||||
{
|
||||
if (await system(`openssl req -subj '${subj}' ${san ? "-addext 'subjectAltName="+san+"'" : ""} -nodes -new -keyout ${f}.key -out ${f}.csr`))
|
||||
process.exit(1);
|
||||
if (await system(`openssl x509 -req -days 3650 -CA ${ca}.crt -CAkey ${ca}.key -CAcreateserial -in ${f}.csr -out ${f}.crt`))
|
||||
process.exit(1);
|
||||
fs.unlinkSync(f+".csr");
|
||||
}
|
||||
|
||||
+31
-99
@@ -6,19 +6,16 @@
|
||||
// 1. Users.
|
||||
// Stored in /vitastor/config/user/<username>.
|
||||
// Has 2 properties:
|
||||
// - type, one of: admin, client.
|
||||
// admin has full access to all images and also to cluster config.
|
||||
// client has r/w access to owned images and r/o access to images with reader_group.
|
||||
// - type, one of: osd, mon, admin, client.
|
||||
// osd, mon types should be used by OSDs/monitors.
|
||||
// admin should be used for administrative access from vitastor-cli.
|
||||
// client should be used for regular clients.
|
||||
// - groups, a list of group names the user is included in.
|
||||
// 2. Images.
|
||||
// Stored in /vitastor/config/inode/<pool>/<inode>. Has the following properties:
|
||||
// - owner (user name)
|
||||
// - owner_group (group name)
|
||||
// - reader_group
|
||||
// 3. Certificates.
|
||||
// - osd, mon use their own trusted certificates.
|
||||
|
||||
const { X509Certificate } = require('node:crypto');
|
||||
|
||||
const static_perms = {
|
||||
invalid: {
|
||||
@@ -27,7 +24,7 @@ const static_perms = {
|
||||
},
|
||||
osd: {
|
||||
keys: { '/pg/config': false },
|
||||
prefixes: { '/config/': false, '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
|
||||
prefixes: { '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
|
||||
},
|
||||
mon: {
|
||||
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
|
||||
@@ -45,15 +42,15 @@ const static_perms = {
|
||||
},
|
||||
client: {
|
||||
keys: { '/config/global': false, '/config/node_placement': false, '/config/pools': false, '/pg/config': false },
|
||||
prefixes: { '/osd/state/': false, '/pg/state/': false, '/index/maxid/': false },
|
||||
prefixes: { '/osd/stats/': false, '/pg/state/': false, '/index/maxid/': false },
|
||||
},
|
||||
};
|
||||
|
||||
const api_perms = {
|
||||
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
|
||||
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
|
||||
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
||||
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
||||
admin: { maintenance_status: true },
|
||||
client: { maintenance_status: true },
|
||||
client: {},
|
||||
};
|
||||
|
||||
class VitastorAuthFilter
|
||||
@@ -66,43 +63,6 @@ class VitastorAuthFilter
|
||||
this.prefix_parts = this.prefix.split('/');
|
||||
}
|
||||
|
||||
async init()
|
||||
{
|
||||
if (!this.cfg.cert || !this.cfg.key || !this.cfg.osd_ca || !this.cfg.etcd_proxy && !this.cfg.peer_ca || !this.cfg.client_cert_auth)
|
||||
{
|
||||
throw new Error('Authenticated Vitastor setups require enabled client_cert_auth, cert, key'+
|
||||
' and separate ca (client CA), osd_ca'+(this.cfg.etcd_proxy ? '' : ', peer_ca')+' and optionally mon_ca');
|
||||
}
|
||||
this.osd_ca = await this.antietcd.readPEM(this.cfg.osd_ca);
|
||||
this.osd_ca_obj = new X509Certificate(this.osd_ca);
|
||||
this.antietcd.tls.ca.push(this.osd_ca);
|
||||
if (this.cfg.mon_ca)
|
||||
{
|
||||
this.mon_ca = await this.antietcd.readPEM(this.cfg.mon_ca);
|
||||
this.mon_ca_obj = new X509Certificate(this.mon_ca_obj);
|
||||
this.antietcd.tls.ca.push(this.mon_ca);
|
||||
}
|
||||
}
|
||||
|
||||
init_context(context, clientCert)
|
||||
{
|
||||
let cert = clientCert;
|
||||
while (cert)
|
||||
{
|
||||
if (cert.fingerprint256 == this.osd_ca_obj.fingerprint256)
|
||||
{
|
||||
context.user_type = 'osd';
|
||||
break;
|
||||
}
|
||||
if (this.mon_ca_obj && cert.fingerprint256 == this.mon_ca_obj.fingerprint256)
|
||||
{
|
||||
context.user_type = 'mon';
|
||||
break;
|
||||
}
|
||||
cert = cert.issuerCertificate;
|
||||
}
|
||||
}
|
||||
|
||||
_get(path, decode)
|
||||
{
|
||||
let cur = this.antietcd.etctree.state;
|
||||
@@ -303,18 +263,6 @@ class VitastorAuthFilter
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (key.substr(0, 13) == '/inode/stats/')
|
||||
{
|
||||
const [ pool_id, id ] = key.substr(13).split('/');
|
||||
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', pool_id, id ], true);
|
||||
if (inode && (inode.reader_group && userInfo.groups[inode.reader_group] ||
|
||||
inode.owner_group && userInfo.groups[inode.owner_group] ||
|
||||
inode.owner === userInfo.name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -401,31 +349,19 @@ class VitastorAuthFilter
|
||||
return true;
|
||||
}
|
||||
|
||||
_get_user(context)
|
||||
_get_user(username)
|
||||
{
|
||||
if (context.user_type === 'osd' || context.user_type === 'mon')
|
||||
if (!username)
|
||||
{
|
||||
return {
|
||||
name: context.user_type,
|
||||
type: context.user_type,
|
||||
perms: static_perms[context.user_type],
|
||||
};
|
||||
return null;
|
||||
}
|
||||
if (!context.username)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', context.username ], true);
|
||||
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
||||
if (!userInfo)
|
||||
{
|
||||
userInfo = { type: 'client' };
|
||||
}
|
||||
else if (userInfo.type !== 'client' && userInfo.type !== 'admin')
|
||||
{
|
||||
userInfo.type = 'client';
|
||||
}
|
||||
userInfo.perms = static_perms[userInfo.type] || static_perms['invalid'];
|
||||
userInfo.name = context.username;
|
||||
userInfo.name = username;
|
||||
if (userInfo.groups instanceof Array)
|
||||
{
|
||||
userInfo.groups = userInfo.groups.reduce((a, c) => { a[c] = true; return a; }, {});
|
||||
@@ -437,27 +373,23 @@ class VitastorAuthFilter
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
filter_api(context, api/*, data*/)
|
||||
filter_api(username, api/*, data*/)
|
||||
{
|
||||
let type = 'client';
|
||||
if (context.user_type === 'osd' || context.user_type === 'mon')
|
||||
if (username === 'root')
|
||||
{
|
||||
type = context.user_type;
|
||||
return true;
|
||||
}
|
||||
else if (context.username)
|
||||
{
|
||||
const userInfo = this._get([ ...this.prefix_parts, 'config', 'user', context.username ], true);
|
||||
if (userInfo && userInfo.type === 'admin')
|
||||
{
|
||||
type = 'admin';
|
||||
}
|
||||
}
|
||||
return api_perms[type] && api_perms[type][api];
|
||||
const userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
||||
return userInfo && api_perms[userInfo.type] && api_perms[userInfo.type][api];
|
||||
}
|
||||
|
||||
filter_txn(context, txn)
|
||||
filter_txn(username, txn)
|
||||
{
|
||||
const userInfo = this._get_user(context);
|
||||
if (username === 'root')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const userInfo = this._get_user(username);
|
||||
if (!userInfo)
|
||||
{
|
||||
return null;
|
||||
@@ -493,13 +425,13 @@ class VitastorAuthFilter
|
||||
return txn;
|
||||
}
|
||||
|
||||
filter_txn_response(context, txn, res)
|
||||
filter_txn_response(username, txn, res)
|
||||
{
|
||||
if (!res.responses)
|
||||
if (!res.responses || username === 'root')
|
||||
{
|
||||
return;
|
||||
}
|
||||
const userInfo = this._get_user(context);
|
||||
const userInfo = this._get_user(username);
|
||||
if (!userInfo)
|
||||
{
|
||||
for (const resp of res.responses)
|
||||
@@ -520,13 +452,13 @@ class VitastorAuthFilter
|
||||
}
|
||||
}
|
||||
|
||||
filter_watch_message(context, msg)
|
||||
filter_watch_message(username, msg)
|
||||
{
|
||||
if (!msg.result || !msg.result.events)
|
||||
if (!msg.result || !msg.result.events || username === 'root')
|
||||
{
|
||||
return;
|
||||
}
|
||||
const userInfo = this._get_user(context);
|
||||
const userInfo = this._get_user(username);
|
||||
if (!userInfo)
|
||||
{
|
||||
msg.result.events = [];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vitastor",
|
||||
"version": "3.0.12",
|
||||
"version": "3.0.10",
|
||||
"description": "Low-level native bindings to Vitastor client library",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
|
||||
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
||||
from cinder.volume import driver
|
||||
from cinder.volume import volume_utils
|
||||
|
||||
VITASTOR_VERSION = '3.0.12'
|
||||
VITASTOR_VERSION = '3.0.10'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.12
|
||||
Version: 3.0.10
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.12.el10.tar.gz
|
||||
Source0: vitastor-3.0.10.el10.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.12
|
||||
Version: 3.0.10
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.12.el7.tar.gz
|
||||
Source0: vitastor-3.0.10.el7.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: devtoolset-9-gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.12
|
||||
Version: 3.0.10
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.12.el8.tar.gz
|
||||
Source0: vitastor-3.0.10.el8.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-toolset-9-gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.12
|
||||
Version: 3.0.10
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.12.el9.tar.gz
|
||||
Source0: vitastor-3.0.10.el9.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-c++
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||
endif()
|
||||
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
|
||||
|
||||
add_definitions(-DVITASTOR_VERSION="3.0.12")
|
||||
add_definitions(-DVITASTOR_VERSION="3.0.10")
|
||||
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
||||
add_link_options(-fno-omit-frame-pointer)
|
||||
if (${WITH_ASAN})
|
||||
|
||||
@@ -100,7 +100,6 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
||||
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
|
||||
gc_on_start = config.find("gc_on_start") == config.end() ||
|
||||
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
|
||||
skip_double_claim = (config["skip_double_claim"] == "true" || config["skip_double_claim"] == "1" || config["skip_double_claim"] == "yes");
|
||||
min_discard_size = parse_size(config["min_discard_size"]);
|
||||
if (!min_discard_size)
|
||||
min_discard_size = 1024*1024;
|
||||
|
||||
@@ -60,8 +60,6 @@ struct blockstore_disk_t
|
||||
bool discard_on_start = false;
|
||||
// GC on start (new store)
|
||||
bool gc_on_start = true;
|
||||
// Skip double claim conflicts on start (new store, temporary until the bug is found)
|
||||
bool skip_double_claim = false;
|
||||
uint64_t min_discard_size = 1024*1024;
|
||||
uint64_t discard_granularity = 0;
|
||||
|
||||
|
||||
@@ -174,18 +174,14 @@ bool journal_flusher_co::loop()
|
||||
else if (wait_state == 19) goto resume_19;
|
||||
else if (wait_state == 20) goto resume_20;
|
||||
else if (wait_state == 21) goto resume_21;
|
||||
else if (wait_state == 22) goto resume_22;
|
||||
else if (wait_state == 23) goto resume_23;
|
||||
else if (wait_state == 24) goto resume_24;
|
||||
else if (wait_state == 25) goto resume_25;
|
||||
resume_0:
|
||||
wait_state = 0;
|
||||
wait_count = 0;
|
||||
cur_oid = {};
|
||||
res = bs->heap->get_next_compact(cur_oid);
|
||||
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
||||
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0)
|
||||
{
|
||||
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
||||
bs->intent_write_counter = 0;
|
||||
resume_17:
|
||||
resume_18:
|
||||
@@ -200,7 +196,6 @@ resume_21:
|
||||
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
|
||||
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
|
||||
{
|
||||
// When under pressure, do an additional fsync to force entries to be marked compactable
|
||||
flusher->active_flushers++;
|
||||
resume_14:
|
||||
resume_15:
|
||||
@@ -264,9 +259,11 @@ resume_1:
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE ||
|
||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
|
||||
{
|
||||
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
||||
auto res = bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
|
||||
? COPY_BUF_SKIP_CSUM : 0);
|
||||
if (res > 0)
|
||||
copy_count++;
|
||||
}
|
||||
});
|
||||
if (!compact_info.compact_lsn)
|
||||
@@ -276,25 +273,6 @@ resume_1:
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
flusher->active_flushers++;
|
||||
for (i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||
{
|
||||
copy_count++;
|
||||
}
|
||||
}
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
init_fsync_data();
|
||||
}
|
||||
if (bs->log_level > 10)
|
||||
{
|
||||
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
||||
compact_info.clean_wr->version, compact_info.compact_version,
|
||||
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
||||
}
|
||||
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
||||
if (!bitmap_copied)
|
||||
{
|
||||
@@ -313,6 +291,13 @@ resume_1:
|
||||
csum_copy.clear();
|
||||
}
|
||||
clean_loc = compact_info.clean_wr->big_location(bs->heap);
|
||||
flusher->active_flushers++;
|
||||
if (bs->log_level > 10)
|
||||
{
|
||||
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
||||
compact_info.clean_wr->version, compact_info.compact_version,
|
||||
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
||||
}
|
||||
overwrite_start = overwrite_end = 0;
|
||||
if (read_vec.size() > 0)
|
||||
{
|
||||
@@ -351,13 +336,6 @@ resume_3:
|
||||
if (res == ENOENT || res == EDOM)
|
||||
{
|
||||
// Abort compaction
|
||||
abort_compact:
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
cur_sync->member_count--;
|
||||
if (cur_sync->member_count > 0)
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
flusher->flushing.erase(cur_oid);
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
flusher->active_flushers--;
|
||||
@@ -371,7 +349,10 @@ resume_4:
|
||||
if (res == ENOENT)
|
||||
{
|
||||
// Abort compaction
|
||||
goto abort_compact;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
flusher->active_flushers--;
|
||||
goto resume_0;
|
||||
}
|
||||
if (res == EAGAIN)
|
||||
{
|
||||
@@ -400,14 +381,14 @@ resume_9:
|
||||
for (i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED) ||
|
||||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
|
||||
{
|
||||
assert(read_vec[i].buf);
|
||||
await_sqe(10);
|
||||
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
|
||||
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
|
||||
data->callback = simple_callback_w;
|
||||
assert(clean_loc + read_vec[i].offset + data->iov.iov_len <= bs->dsk.block_count*bs->dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
|
||||
wait_count++;
|
||||
}
|
||||
@@ -418,17 +399,6 @@ resume_11:
|
||||
wait_state = 11;
|
||||
return false;
|
||||
}
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
resume_22:
|
||||
resume_23:
|
||||
resume_24:
|
||||
resume_25:
|
||||
if (!fsync_data(22))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Lock is only needed to prevent freeing the big_write because we overwrite it...
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
// Mark the object compacted, but don't free and remove small_writes
|
||||
@@ -438,14 +408,12 @@ resume_25:
|
||||
if (!cur_obj)
|
||||
{
|
||||
// Abort compaction
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
if (!calc_block_checksums())
|
||||
{
|
||||
// Abort compaction
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
@@ -454,7 +422,6 @@ resume_25:
|
||||
if (res == EBUSY)
|
||||
{
|
||||
// Abort compaction, object is already overwritten by something else
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
@@ -619,7 +586,7 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
|
||||
}
|
||||
}
|
||||
// Modified, we should punch_holes and then write the block to disk
|
||||
// Modified, we should add_punch_holes and then write the block to disk
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
@@ -732,67 +699,6 @@ resume_1:
|
||||
return true;
|
||||
}
|
||||
|
||||
void journal_flusher_co::init_fsync_data()
|
||||
{
|
||||
cur_sync = flusher->data_syncs.begin();
|
||||
if (cur_sync == flusher->data_syncs.end() || cur_sync->ready_count > 0)
|
||||
{
|
||||
cur_sync = flusher->data_syncs.emplace(cur_sync);
|
||||
}
|
||||
cur_sync->member_count++;
|
||||
}
|
||||
|
||||
bool journal_flusher_co::fsync_data(int wait_base)
|
||||
{
|
||||
if (wait_state == wait_base)
|
||||
goto resume_0;
|
||||
else if (wait_state == wait_base+1)
|
||||
goto resume_1;
|
||||
else if (wait_state == wait_base+2)
|
||||
goto resume_2;
|
||||
else if (wait_state == wait_base+3)
|
||||
goto resume_3;
|
||||
cur_sync->ready_count++;
|
||||
resume_0:
|
||||
if (cur_sync->ready_count < cur_sync->member_count)
|
||||
{
|
||||
wait_state = wait_base;
|
||||
return false;
|
||||
}
|
||||
if (!cur_sync->sent)
|
||||
{
|
||||
// Sync batch is ready. Do it.
|
||||
await_sqe(1);
|
||||
data->iov = { 0 };
|
||||
data->callback = simple_callback_w;
|
||||
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||
cur_sync->sent = true;
|
||||
wait_count++;
|
||||
resume_2:
|
||||
if (wait_count > 0)
|
||||
{
|
||||
wait_state = wait_base+2;
|
||||
return false;
|
||||
}
|
||||
cur_sync->done = true;
|
||||
// Wake up other flushers
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
resume_3:
|
||||
if (!cur_sync->done)
|
||||
{
|
||||
wait_state = wait_base+3;
|
||||
return false;
|
||||
}
|
||||
cur_sync->done_count++;
|
||||
if (cur_sync->done_count >= cur_sync->member_count)
|
||||
{
|
||||
flusher->data_syncs.erase(cur_sync);
|
||||
cur_sync = flusher->data_syncs.end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool journal_flusher_co::fsync_meta(int wait_base)
|
||||
{
|
||||
if (wait_state == wait_base) goto resume_0;
|
||||
|
||||
@@ -25,15 +25,6 @@ struct flusher_meta_write_t
|
||||
std::map<uint64_t, meta_sector_t>::iterator it;
|
||||
};
|
||||
|
||||
struct flusher_data_sync_t
|
||||
{
|
||||
int member_count = 0;
|
||||
int ready_count = 0;
|
||||
int done_count = 0;
|
||||
bool sent = false;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
class journal_flusher_t;
|
||||
|
||||
// Journal flusher coroutine
|
||||
@@ -67,7 +58,6 @@ class journal_flusher_co
|
||||
int i, res;
|
||||
bool read_to_fill_incomplete;
|
||||
int copy_count;
|
||||
std::list<flusher_data_sync_t>::iterator cur_sync;
|
||||
|
||||
friend class journal_flusher_t;
|
||||
|
||||
@@ -78,8 +68,6 @@ class journal_flusher_co
|
||||
bool calc_block_checksums();
|
||||
bool write_meta_block(int wait_base);
|
||||
bool read_buffered(int wait_base);
|
||||
void init_fsync_data();
|
||||
bool fsync_data(int wait_base);
|
||||
bool fsync_meta(int wait_base);
|
||||
bool fsync_buffer(int wait_base);
|
||||
bool trim_lsn(int wait_base);
|
||||
@@ -100,7 +88,6 @@ class journal_flusher_t
|
||||
|
||||
robin_hood::unordered_flat_set<object_id> flushing;
|
||||
int active_flushers = 0;
|
||||
std::list<flusher_data_sync_t> data_syncs;
|
||||
int wanting_meta_fsync = 0;
|
||||
bool fsyncing_meta = false;
|
||||
int syncing_buffer = 0;
|
||||
|
||||
+180
-335
@@ -29,7 +29,6 @@
|
||||
|
||||
#define IMAP_MALLOC_LOW_BITS ((size_t)0x0F)
|
||||
#define IMAP_MAX_LOW 16
|
||||
#define POSTPONE_INSERT_COUNT 10
|
||||
|
||||
#define list_item_overhead(a) (((a) + sizeof(heap_list_item_t) - sizeof(heap_entry_t) + sizeof(void*) + 15) & ~15)
|
||||
|
||||
@@ -128,26 +127,26 @@ uint32_t heap_entry_t::get_size(blockstore_heap_t *heap)
|
||||
return heap->get_simple_entry_size();
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_overwrite() const
|
||||
bool heap_entry_t::is_overwrite()
|
||||
{
|
||||
return ((entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_BIG_INTENT|BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_DELETE|BS_HEAP_STABLE));
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_compactable() const
|
||||
bool heap_entry_t::is_compactable()
|
||||
{
|
||||
return !is_overwrite() && (entry_type & BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == BS_HEAP_COMMIT ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == BS_HEAP_ROLLBACK;
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_before(const heap_entry_t *other) const
|
||||
bool heap_entry_t::is_before(heap_entry_t *other)
|
||||
{
|
||||
return lsn < other->lsn || lsn == other->lsn && !is_overwrite() && other->is_overwrite();
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_garbage() const
|
||||
bool heap_entry_t::is_garbage()
|
||||
{
|
||||
return (entry_type & BS_HEAP_GARBAGE);
|
||||
}
|
||||
@@ -419,13 +418,6 @@ corrupted_object:
|
||||
wr->inode, wr->stripe, wr->version, wr->big_intent().offset, wr->big_intent().len);
|
||||
goto corrupted_object;
|
||||
}
|
||||
if ((wr->type() == BS_HEAP_BIG_INTENT || wr->type() == BS_HEAP_BIG_WRITE) &&
|
||||
wr->big().block_num >= dsk->block_count)
|
||||
{
|
||||
fprintf(stderr, "Error: big_write or big_intent entry %jx:%jx v%ju block_num is too large: %u > %lu. Metadata is incompatible with current parameters. ",
|
||||
wr->inode, wr->stripe, wr->version, wr->big_intent().block_num, dsk->block_count);
|
||||
goto corrupted_object;
|
||||
}
|
||||
handle_write(block_num, wr);
|
||||
block_offset += wr->size;
|
||||
}
|
||||
@@ -452,7 +444,7 @@ int blockstore_heap_t::load_blocks(uint64_t disk_offset, uint64_t size, uint8_t
|
||||
next_lsn = wr->lsn;
|
||||
}
|
||||
entries_loaded++;
|
||||
insert_list_items(&li, 1, true);
|
||||
loaded_list_items.push_back(li);
|
||||
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
if (!inf.entries.size())
|
||||
@@ -558,26 +550,18 @@ bool blockstore_heap_t::validate_object(heap_entry_t *obj)
|
||||
|
||||
void blockstore_heap_t::finish_load()
|
||||
{
|
||||
if (postponed_items.size())
|
||||
if (loaded_list_items.size())
|
||||
{
|
||||
// Sort "postponed" items and load in batches
|
||||
std::sort(postponed_items.begin(), postponed_items.end(), [this](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||
// 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.inode < b->entry.inode || a->entry.inode == b->entry.inode &&
|
||||
(a->entry.stripe < b->entry.stripe || a->entry.stripe == b->entry.stripe &&
|
||||
!a->entry.is_before(&b->entry)); // object ASC, lsn DESC
|
||||
return a->entry.lsn < b->entry.lsn;
|
||||
});
|
||||
size_t s = 0, e, n = postponed_items.size();
|
||||
for (e = 1; e <= n; e++)
|
||||
for (auto & li: loaded_list_items)
|
||||
{
|
||||
if (e >= n || postponed_items[e]->entry.inode != postponed_items[s]->entry.inode &&
|
||||
postponed_items[e]->entry.stripe != postponed_items[s]->entry.stripe)
|
||||
{
|
||||
insert_list_items(postponed_items.data()+s, e-s, false);
|
||||
s = e;
|
||||
}
|
||||
insert_list_item(li);
|
||||
}
|
||||
postponed_items.clear();
|
||||
loaded_list_items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,11 +574,26 @@ void blockstore_heap_t::fill_recheck_queue()
|
||||
inode_map_iterate(ip.second, [&](heap_list_item_t *li)
|
||||
{
|
||||
auto obj = &li->entry;
|
||||
// Recheck only the latest intent_write (if after completed_lsn) or a series of small_writes
|
||||
if ((obj->type() == BS_HEAP_INTENT_WRITE || obj->type() == BS_HEAP_BIG_INTENT)
|
||||
&& obj->lsn > completed_lsn || obj->type() == BS_HEAP_SMALL_WRITE)
|
||||
// Add object to recheck queue
|
||||
if (obj->type() == BS_HEAP_INTENT_WRITE || obj->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
recheck_queue.push_back(obj);
|
||||
// Recheck only the latest intent_write
|
||||
if (obj->lsn > completed_lsn)
|
||||
{
|
||||
// Do not recheck if it's already marked as completed in the superblock
|
||||
recheck_queue.push_back(obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Or recheck a series of small_writes
|
||||
for (auto wr = obj; wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
{
|
||||
if (wr->small().len > 0)
|
||||
{
|
||||
recheck_queue.push_back(wr);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -604,11 +603,6 @@ void blockstore_heap_t::fill_recheck_queue()
|
||||
int blockstore_heap_t::mark_used_blocks()
|
||||
{
|
||||
int res = 0;
|
||||
std::vector<heap_list_item_t*> used_by;
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
used_by.resize(dsk->block_count);
|
||||
}
|
||||
for (auto & pgp: block_index)
|
||||
{
|
||||
for (auto & ip: pgp.second)
|
||||
@@ -662,34 +656,10 @@ int blockstore_heap_t::mark_used_blocks()
|
||||
{
|
||||
if (is_data_used(wr->big_location(this)))
|
||||
{
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
// There is a BUG currently:
|
||||
// Sometimes (under unknown conditions) deletion entries are removed from the disk
|
||||
// earlier than previous big_writes.
|
||||
// Until it's fixed, we provide a way to ignore such objects on start.
|
||||
auto prev_li = used_by[wr->big().block_num];
|
||||
assert(prev_li);
|
||||
// Newer LSN must be trusted. Remove the older object.
|
||||
fprintf(stderr, "Block %u is double-claimed by entries %jx:%jx l%ju and %jx:%jx l%ju\n",
|
||||
wr->big().block_num, prev_li->entry.inode, prev_li->entry.stripe, prev_li->entry.lsn, wr->inode, wr->stripe, wr->lsn);
|
||||
if (init_erase_double_claim(prev_li, li))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: double-claimed data block %u, second time by %jx:%jx l%ju\n",
|
||||
wr->big().block_num, wr->inode, wr->stripe, wr->lsn);
|
||||
res = EDOM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
// Record the object which uses the data block
|
||||
used_by[wr->big().block_num] = li;
|
||||
fprintf(stderr, "Error: double-claimed data block %u, second time by %jx:%jx l%ju\n",
|
||||
wr->big().block_num, wr->inode, wr->stripe, wr->lsn);
|
||||
res = EDOM;
|
||||
return;
|
||||
}
|
||||
use_data(wr->inode, wr->big_location(this));
|
||||
}
|
||||
@@ -713,116 +683,6 @@ int blockstore_heap_t::mark_used_blocks()
|
||||
return res;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::init_free_bad_entry(heap_entry_t *wr)
|
||||
{
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
free_buffer_area(wr->inode, wr->small().location, wr->small().len);
|
||||
}
|
||||
else if (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
free_data(wr->inode, wr->big_location(this));
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::init_erase_bad_entry(heap_list_item_t *li)
|
||||
{
|
||||
modify_alloc(li->block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
for (size_t i = 0; i < inf.entries.size(); i++)
|
||||
{
|
||||
if (inf.entries[i] == li)
|
||||
{
|
||||
inf.entries.erase(inf.entries.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inf.used_space -= li->entry.size;
|
||||
inf.garbage_space -= (li->entry.is_garbage() ? li->entry.size : 0);
|
||||
});
|
||||
recheck_modified_blocks.insert(li->block_num);
|
||||
unlink_list_item(li);
|
||||
}
|
||||
|
||||
bool blockstore_heap_t::init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li)
|
||||
{
|
||||
bool erase_prev = false;
|
||||
bool erase_cur = false;
|
||||
if (prev_li->entry.lsn < cur_li->entry.lsn)
|
||||
{
|
||||
erase_prev = true;
|
||||
auto latest_li = prev_li;
|
||||
while (latest_li->next)
|
||||
{
|
||||
latest_li = latest_li->next;
|
||||
}
|
||||
if (latest_li->entry.lsn >= cur_li->entry.lsn)
|
||||
{
|
||||
// LSN ranges intersect, erase both
|
||||
erase_cur = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
erase_cur = true;
|
||||
auto latest_li = cur_li;
|
||||
while (latest_li->next)
|
||||
{
|
||||
latest_li = latest_li->next;
|
||||
}
|
||||
if ((latest_li->entry.inode != prev_li->entry.inode ||
|
||||
latest_li->entry.stripe != prev_li->entry.stripe) &&
|
||||
latest_li->entry.lsn >= prev_li->entry.lsn)
|
||||
{
|
||||
// LSN ranges intersect, erase both
|
||||
erase_prev = true;
|
||||
}
|
||||
}
|
||||
if (erase_prev)
|
||||
{
|
||||
fprintf(stderr, "Erasing object %jx:%jx due to double-claim\n", prev_li->entry.inode, prev_li->entry.stripe);
|
||||
auto erase_li = prev_li;
|
||||
while (erase_li->next)
|
||||
{
|
||||
erase_li = erase_li->next;
|
||||
}
|
||||
bool overwritten = false;
|
||||
while (erase_li)
|
||||
{
|
||||
auto prev_erase_li = erase_li->prev;
|
||||
if (!overwritten)
|
||||
{
|
||||
init_free_bad_entry(&erase_li->entry);
|
||||
overwritten = erase_li->entry.is_overwrite();
|
||||
}
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = prev_erase_li;
|
||||
}
|
||||
}
|
||||
if (erase_cur)
|
||||
{
|
||||
fprintf(stderr, "Erasing object %jx:%jx due to double-claim\n", cur_li->entry.inode, cur_li->entry.stripe);
|
||||
auto erase_li = cur_li->next;
|
||||
while (erase_li)
|
||||
{
|
||||
// Only newer entries are marked as used
|
||||
auto next_erase_li = erase_li->next;
|
||||
init_free_bad_entry(&erase_li->entry);
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = next_erase_li;
|
||||
}
|
||||
erase_li = cur_li;
|
||||
// Older ones are not
|
||||
while (erase_li)
|
||||
{
|
||||
auto prev_erase_li = erase_li->prev;
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = prev_erase_li;
|
||||
}
|
||||
}
|
||||
return erase_cur;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_full_gc()
|
||||
{
|
||||
uint32_t block_num = 0;
|
||||
@@ -863,98 +723,80 @@ void blockstore_heap_t::recheck_full_gc()
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr)
|
||||
void blockstore_heap_t::recheck_buffer(heap_entry_t *cwr, uint8_t *buf)
|
||||
{
|
||||
// write entry is invalid, erase it and all newer entries
|
||||
int bad_count = 1;
|
||||
for (auto wr = obj; wr && wr != bad_wr; wr = prev(wr))
|
||||
auto free_entry = [&](heap_list_item_t *li)
|
||||
{
|
||||
bad_count++;
|
||||
}
|
||||
auto prev_wr = prev(bad_wr);
|
||||
if (prev_wr)
|
||||
{
|
||||
fprintf(stderr, "Notice: %u unfinished %s to %jx:%jx v%ju since good lsn %ju, rolling back\n",
|
||||
bad_count, bad_count > 1 ? "writes" : "write", obj->inode, obj->stripe, obj->version, prev_wr->lsn);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n", obj->inode, obj->stripe);
|
||||
}
|
||||
auto li = list_item(obj);
|
||||
while (li && prev_wr != &li->entry)
|
||||
{
|
||||
auto prev = li->prev;
|
||||
assert(li->entry.type() == bad_wr->type());
|
||||
init_erase_bad_entry(li);
|
||||
li = prev;
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_start_reads(heap_recheck_state_t *st)
|
||||
{
|
||||
if (st->sent_reads >= st->total_reads)
|
||||
return;
|
||||
while (recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
auto wr = st->next_wr;
|
||||
st->next_wr = prev(st->next_wr);
|
||||
uint64_t loc = 0, len = 0;
|
||||
bool from_data = false;
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
uint32_t block_num = li->block_num;
|
||||
auto wr_size = li->entry.size;
|
||||
if (li->entry.is_garbage())
|
||||
{
|
||||
loc = wr->small().location;
|
||||
len = wr->small().len;
|
||||
garbage_entries--;
|
||||
garbage_memory -= list_item_overhead(wr_size);
|
||||
}
|
||||
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||
live_entries--;
|
||||
live_memory -= list_item_overhead(wr_size);
|
||||
free(li);
|
||||
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
auto & bi = wr->big_intent();
|
||||
loc = (uint64_t)bi.block_num * dsk->data_block_size + bi.offset;
|
||||
len = bi.len;
|
||||
from_data = true;
|
||||
inf.used_space -= wr_size;
|
||||
bool found = false;
|
||||
for (auto it = inf.entries.begin(); it != inf.entries.end(); it++)
|
||||
{
|
||||
if (*it == li)
|
||||
{
|
||||
found = true;
|
||||
inf.entries.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
});
|
||||
recheck_modified_blocks.insert(block_num);
|
||||
};
|
||||
if (cwr->is_garbage())
|
||||
{
|
||||
// already freed after rechecking one of the previous small_write entries
|
||||
free_entry(list_item(cwr));
|
||||
}
|
||||
else if (!calc_checksums(cwr, buf, false))
|
||||
{
|
||||
// write entry is invalid, erase it and mark newer entries with garbage bit
|
||||
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_list_item_t *li = NULL;
|
||||
inode_map_get(inode_idx, li_it, li, cwr->stripe);
|
||||
int rolled_back = 1;
|
||||
while (li && cwr != &li->entry)
|
||||
{
|
||||
assert(li->entry.entry_type == cwr->entry_type);
|
||||
auto prev = li->prev;
|
||||
li->next = li->prev = NULL;
|
||||
if (!li->entry.is_garbage())
|
||||
{
|
||||
garbage_entries++;
|
||||
garbage_memory += list_item_overhead(li->entry.size);
|
||||
li->entry.set_garbage();
|
||||
}
|
||||
li = prev;
|
||||
rolled_back++;
|
||||
}
|
||||
assert(li);
|
||||
if (li->prev)
|
||||
{
|
||||
fprintf(stderr, "Notice: %u unfinished %s to %jx:%jx v%ju since lsn %ju, rolling back\n",
|
||||
rolled_back, rolled_back > 1 ? "writes" : "write", cwr->inode, cwr->stripe, li->prev->entry.version, li->entry.lsn);
|
||||
inode_map_replace(inode_idx, li_it, li->prev);
|
||||
li->prev->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(wr->type() == BS_HEAP_INTENT_WRITE);
|
||||
auto prev_wr = prev(wr);
|
||||
while (prev_wr && prev_wr->entry_type == wr->entry_type)
|
||||
{
|
||||
// Skip other intent_writes
|
||||
prev_wr = prev(prev_wr);
|
||||
}
|
||||
if (!prev_wr || prev_wr->entry_type != (BS_HEAP_BIG_WRITE | (wr->entry_type & BS_HEAP_STABLE)) &&
|
||||
prev_wr->entry_type != (BS_HEAP_BIG_INTENT | (wr->entry_type & BS_HEAP_STABLE)))
|
||||
{
|
||||
fprintf(stderr, "Error: intent_write entry %jx:%jx v%ju l%ju is not written over a big_write\n",
|
||||
wr->inode, wr->stripe, wr->version, wr->lsn);
|
||||
exit(1);
|
||||
}
|
||||
loc = wr->small().offset + prev_wr->big_location(this);
|
||||
len = wr->small().len;
|
||||
from_data = true;
|
||||
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n",
|
||||
cwr->inode, cwr->stripe);
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, li);
|
||||
}
|
||||
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, len);
|
||||
st->sent_reads++;
|
||||
recheck_in_progress++;
|
||||
recheck_pending_reads--;
|
||||
bool is_last = st->sent_reads >= st->total_reads;
|
||||
recheck_cb(from_data, loc, len, buf, [this, st, wr, buf]()
|
||||
{
|
||||
st->checked_reads++;
|
||||
if (!calc_checksums(wr, buf, false))
|
||||
st->bad_wr = !st->bad_wr || st->bad_wr->lsn > wr->lsn ? wr : st->bad_wr;
|
||||
if (st->checked_reads >= st->total_reads)
|
||||
{
|
||||
if (st->bad_wr)
|
||||
recheck_drop_entries(st->obj, st->bad_wr);
|
||||
recheck_states.erase(st->obj);
|
||||
}
|
||||
free(buf);
|
||||
recheck_in_progress--;
|
||||
recheck_small_writes(NULL, 0);
|
||||
});
|
||||
if (is_last)
|
||||
break;
|
||||
free_entry(li);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,47 +819,70 @@ bool blockstore_heap_t::recheck_small_writes(std::function<void(bool is_data, ui
|
||||
recheck_queue_depth = queue_depth;
|
||||
}
|
||||
in_recheck = true;
|
||||
while (recheck_pending_reads > 0 && recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
for (auto & sp: recheck_states)
|
||||
recheck_start_reads(&sp.second);
|
||||
}
|
||||
while (recheck_queue.size() > 0 && recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
heap_entry_t *obj = recheck_queue.front();
|
||||
heap_entry_t *wr = recheck_queue.front();
|
||||
recheck_queue.pop_front();
|
||||
if (obj->type() == BS_HEAP_SMALL_WRITE && buffer_area)
|
||||
bool from_data = false;
|
||||
uint64_t loc = 0;
|
||||
uint32_t len = 0;
|
||||
if (wr->type() == BS_HEAP_INTENT_WRITE)
|
||||
{
|
||||
// Check this object synchronously
|
||||
heap_entry_t *bad_wr = NULL;
|
||||
for (auto wr = obj; wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
auto prev_wr = prev(wr);
|
||||
while (prev_wr && prev_wr->entry_type == wr->entry_type)
|
||||
{
|
||||
fprintf(stderr, "Notice: rechecking %jx:%jx l%ju - %u bytes at %ju in buffer area\n",
|
||||
wr->inode, wr->stripe, wr->lsn, wr->small().len, wr->small().location);
|
||||
if (!calc_checksums(wr, buffer_area + wr->small().location, false))
|
||||
bad_wr = wr;
|
||||
// Skip other intent_writes
|
||||
prev_wr = prev(prev_wr);
|
||||
}
|
||||
if (bad_wr)
|
||||
recheck_drop_entries(obj, bad_wr);
|
||||
if (!prev_wr || prev_wr->entry_type != (BS_HEAP_BIG_WRITE | (wr->entry_type & BS_HEAP_STABLE)) &&
|
||||
prev_wr->entry_type != (BS_HEAP_BIG_INTENT | (wr->entry_type & BS_HEAP_STABLE)))
|
||||
{
|
||||
fprintf(stderr, "Error: intent_write entry %jx:%jx v%ju l%ju is not written over a big_write\n",
|
||||
wr->inode, wr->stripe, wr->version, wr->lsn);
|
||||
exit(1);
|
||||
}
|
||||
loc = wr->small().offset + prev_wr->big_location(this);
|
||||
len = wr->small().len;
|
||||
from_data = true;
|
||||
}
|
||||
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
auto & bi = wr->big_intent();
|
||||
loc = (uint64_t)bi.block_num * dsk->data_block_size + bi.offset;
|
||||
len = bi.len;
|
||||
from_data = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recheck will be asynchronous. Create state and start it
|
||||
auto & st = recheck_states[obj];
|
||||
st.obj = obj;
|
||||
st.next_wr = obj;
|
||||
st.total_reads = 1;
|
||||
if (obj->type() == BS_HEAP_SMALL_WRITE)
|
||||
for (auto wr = prev(obj); wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
st.total_reads++;
|
||||
recheck_pending_reads += st.total_reads;
|
||||
recheck_start_reads(&st);
|
||||
assert(wr->type() == BS_HEAP_SMALL_WRITE);
|
||||
loc = wr->small().location;
|
||||
len = wr->small().len;
|
||||
}
|
||||
if (log_level > 5)
|
||||
{
|
||||
fprintf(stderr, "Notice: rechecking %jx:%jx l%ju - %u bytes at %ju in %s area\n",
|
||||
wr->inode, wr->stripe, wr->lsn, len, loc, from_data ? "data" : "buffer");
|
||||
}
|
||||
if (!from_data && buffer_area)
|
||||
{
|
||||
recheck_buffer(wr, buffer_area+loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
recheck_in_progress++;
|
||||
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, len);
|
||||
recheck_cb(from_data, loc, len, buf, [this, wr, buf]()
|
||||
{
|
||||
recheck_buffer(wr, buf);
|
||||
free(buf);
|
||||
recheck_in_progress--;
|
||||
recheck_small_writes(NULL, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
in_recheck = false;
|
||||
if (!recheck_queue.size() && !recheck_in_progress)
|
||||
{
|
||||
assert(!recheck_states.size());
|
||||
auto cb = std::move(recheck_cb);
|
||||
recheck_queue_depth = 0;
|
||||
if (cb)
|
||||
@@ -1539,51 +1404,43 @@ int blockstore_heap_t::allocate_entry(uint32_t entry_size, uint32_t *block_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::insert_list_items(heap_list_item_t** v, size_t count, bool postpone)
|
||||
void blockstore_heap_t::insert_list_item(heap_list_item_t *li)
|
||||
{
|
||||
auto wr = &v[0]->entry;
|
||||
auto & inode_idx = block_index[get_pg_id(wr->inode, wr->stripe)][wr->inode];
|
||||
auto & inode_idx = block_index[get_pg_id(li->entry.inode, li->entry.stripe)][li->entry.inode];
|
||||
heap_inode_map_t::iterator li_it;
|
||||
heap_list_item_t *old_head = NULL;
|
||||
if (inode_idx)
|
||||
inode_map_get(inode_idx, li_it, old_head, wr->stripe);
|
||||
heap_list_item_t *next_li = NULL;
|
||||
heap_list_item_t *prev_li = old_head;
|
||||
int skips = 0;
|
||||
// Merge entry array and inode_idx linked list (both sorted in newest first order)
|
||||
for (size_t i = 0; i < count; i++)
|
||||
inode_map_get(inode_idx, li_it, old_head, li->entry.stripe);
|
||||
if (old_head && !old_head->entry.is_before(&li->entry))
|
||||
{
|
||||
// BIG_WRITE may be inserted into the middle of the sequence during compaction
|
||||
// and it overrides SMALL_WRITEs and COMMITs with the same LSN
|
||||
// However, all entries of other types (say DELETE) override previous ones
|
||||
auto li = v[i];
|
||||
auto next_li = old_head;
|
||||
auto prev_li = old_head->prev;
|
||||
while (prev_li && !prev_li->entry.is_before(&li->entry))
|
||||
{
|
||||
next_li = prev_li;
|
||||
prev_li = prev_li->prev;
|
||||
skips++;
|
||||
}
|
||||
if (postpone && skips > POSTPONE_INSERT_COUNT)
|
||||
{
|
||||
postponed_items.push_back(li);
|
||||
return;
|
||||
}
|
||||
if (next_li == NULL)
|
||||
{
|
||||
// Replace the latest entry pointer
|
||||
if (old_head)
|
||||
inode_map_replace(inode_idx, li_it, li);
|
||||
else
|
||||
inode_map_put(inode_idx, li);
|
||||
}
|
||||
// Insert <li> between <next_li> and <prev_li>
|
||||
li->next = next_li;
|
||||
if (next_li)
|
||||
next_li->prev = li;
|
||||
li->prev = prev_li;
|
||||
if (prev_li)
|
||||
prev_li->next = li;
|
||||
next_li = li;
|
||||
next_li->prev = li;
|
||||
li->next = next_li;
|
||||
}
|
||||
else
|
||||
{
|
||||
li->prev = old_head;
|
||||
li->next = NULL;
|
||||
if (old_head)
|
||||
{
|
||||
old_head->next = li;
|
||||
inode_map_replace(inode_idx, li_it, li);
|
||||
}
|
||||
else
|
||||
inode_map_put(inode_idx, li);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1615,7 +1472,7 @@ int blockstore_heap_t::add_entry(uint32_t wr_size, uint32_t *modified_block,
|
||||
(explicit_complete ? HEAP_INFLIGHT_EXPLICIT : 0) |
|
||||
(new_wr->is_overwrite() ? HEAP_INFLIGHT_COMPACTED : 0) |
|
||||
(new_wr->is_compactable() ? HEAP_INFLIGHT_COMPACTABLE : 0));
|
||||
insert_list_items(&li, 1, false);
|
||||
insert_list_item(li);
|
||||
li->block_num = block_num;
|
||||
new_wr->size = wr_size;
|
||||
new_wr->checksum = new_wr->calc_checksum(this);
|
||||
@@ -1811,7 +1668,6 @@ int blockstore_heap_t::punch_holes(heap_entry_t *wr, uint8_t *new_bitmap, uint8_
|
||||
*modified_block = block_num;
|
||||
memcpy(wr->get_int_bitmap(this), new_bitmap, dsk->clean_entry_bitmap_size);
|
||||
memcpy(wr->get_checksums(this), new_csums, dsk->data_block_size/dsk->csum_block_size*(dsk->data_csum_type & 0xFF));
|
||||
wr->checksum = wr->calc_checksum(dsk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2610,22 +2466,6 @@ void blockstore_heap_t::apply_inflight(heap_inflight_lsn_t & inflight)
|
||||
}
|
||||
|
||||
void blockstore_heap_t::remove_list_item(heap_list_item_t *li)
|
||||
{
|
||||
if (!li->next)
|
||||
{
|
||||
// The last freed entry must be a deletion
|
||||
assert(!li->prev);
|
||||
assert((li->entry.entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_DELETE|BS_HEAP_STABLE));
|
||||
}
|
||||
else if (!li->prev && li->next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE))
|
||||
{
|
||||
// free BS_HEAP_DELETEs when all previous entries are also freed
|
||||
mark_garbage(li->next->block_num, &li->next->entry, UINT32_MAX);
|
||||
}
|
||||
unlink_list_item(li);
|
||||
}
|
||||
|
||||
void blockstore_heap_t::unlink_list_item(heap_list_item_t *li)
|
||||
{
|
||||
auto prev = li->prev;
|
||||
auto next = li->next;
|
||||
@@ -2635,20 +2475,25 @@ void blockstore_heap_t::unlink_list_item(heap_list_item_t *li)
|
||||
}
|
||||
if (!next)
|
||||
{
|
||||
// The last freed entry must be a deletion
|
||||
assert(!prev);
|
||||
auto wr = &li->entry;
|
||||
assert(wr->entry_type == BS_HEAP_DELETE|BS_HEAP_STABLE);
|
||||
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_list_item_t *old_li = NULL;
|
||||
inode_map_get(inode_idx, li_it, old_li, wr->stripe);
|
||||
if (!prev)
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, old_li);
|
||||
else
|
||||
inode_map_replace(inode_idx, li_it, prev);
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, old_li);
|
||||
}
|
||||
else
|
||||
{
|
||||
next->prev = prev;
|
||||
if (!prev && next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE))
|
||||
{
|
||||
// free BS_HEAP_DELETEs when all previous entries are also freed
|
||||
mark_garbage(next->block_num, &next->entry, UINT32_MAX);
|
||||
}
|
||||
}
|
||||
if (li->entry.is_garbage())
|
||||
{
|
||||
|
||||
@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
|
||||
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
|
||||
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
|
||||
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
|
||||
bool is_garbage() const;
|
||||
bool is_garbage();
|
||||
void set_garbage();
|
||||
bool is_overwrite() const;
|
||||
bool is_compactable() const;
|
||||
bool is_before(const heap_entry_t *other) const;
|
||||
bool is_overwrite();
|
||||
bool is_compactable();
|
||||
bool is_before(heap_entry_t *other);
|
||||
uint32_t get_size(blockstore_heap_t *heap);
|
||||
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
|
||||
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
|
||||
@@ -159,16 +159,6 @@ struct heap_li_equal
|
||||
}
|
||||
};
|
||||
|
||||
struct heap_recheck_state_t
|
||||
{
|
||||
heap_entry_t *obj = NULL;
|
||||
heap_entry_t *next_wr = NULL;
|
||||
size_t total_reads = 0;
|
||||
size_t sent_reads = 0;
|
||||
size_t checked_reads = 0;
|
||||
heap_entry_t *bad_wr = NULL;
|
||||
};
|
||||
|
||||
using i64hash_t = robin_hood::hash<uint64_t>;
|
||||
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
|
||||
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
|
||||
@@ -219,11 +209,9 @@ class blockstore_heap_t
|
||||
|
||||
bool marked_used_blocks = false;
|
||||
bool recheck_queue_filled = false;
|
||||
std::vector<heap_list_item_t*> postponed_items;
|
||||
std::vector<heap_list_item_t*> loaded_list_items;
|
||||
std::set<uint32_t> recheck_modified_blocks;
|
||||
std::deque<heap_entry_t*> recheck_queue;
|
||||
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
|
||||
size_t recheck_pending_reads = 0;
|
||||
int recheck_in_progress = 0;
|
||||
bool in_recheck = false;
|
||||
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
|
||||
@@ -232,12 +220,7 @@ class blockstore_heap_t
|
||||
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
|
||||
bool validate_object(heap_entry_t *obj);
|
||||
void fill_recheck_queue();
|
||||
void recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr);
|
||||
void recheck_start_reads(heap_recheck_state_t *st);
|
||||
int mark_used_blocks();
|
||||
void init_free_bad_entry(heap_entry_t *wr);
|
||||
void init_erase_bad_entry(heap_list_item_t *li);
|
||||
bool init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li);
|
||||
void recheck_full_gc();
|
||||
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
|
||||
void defragment_block(uint32_t block_num);
|
||||
@@ -245,9 +228,8 @@ class blockstore_heap_t
|
||||
|
||||
void gc_block(heap_block_info_t & inf);
|
||||
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free);
|
||||
void insert_list_items(heap_list_item_t** v, size_t count, bool postpone);
|
||||
void insert_list_item(heap_list_item_t *li);
|
||||
void remove_list_item(heap_list_item_t *li);
|
||||
void unlink_list_item(heap_list_item_t *li);
|
||||
int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free,
|
||||
bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
|
||||
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
|
||||
|
||||
@@ -101,7 +101,6 @@ void blockstore_impl_t::loop()
|
||||
unsigned initial_ring_space = ringloop->space_left();
|
||||
int op_idx = 0, new_idx = 0;
|
||||
bool has_unfinished_writes = false;
|
||||
bool has_unfinished_sync = false;
|
||||
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
||||
{
|
||||
auto op = submit_queue[op_idx];
|
||||
@@ -139,13 +138,7 @@ void blockstore_impl_t::loop()
|
||||
else if (op->opcode == BS_OP_SYNC)
|
||||
{
|
||||
// syncs only completed writes, so doesn't have to be blocked by anything
|
||||
if (!has_unfinished_sync)
|
||||
{
|
||||
wr_st = continue_sync(op);
|
||||
has_unfinished_sync = (wr_st != 2);
|
||||
}
|
||||
else
|
||||
wr_st = 0;
|
||||
wr_st = continue_sync(op);
|
||||
}
|
||||
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
|
||||
{
|
||||
@@ -161,7 +154,9 @@ void blockstore_impl_t::loop()
|
||||
wr_st = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
wr_st = 0;
|
||||
}
|
||||
}
|
||||
if (wr_st == 2)
|
||||
{
|
||||
|
||||
@@ -153,14 +153,6 @@ resume_1:
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
uint32_t csum = hdr->header_csum;
|
||||
hdr->header_csum = 0;
|
||||
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
|
||||
{
|
||||
printf("Metadata header is corrupt (checksum mismatch).\n");
|
||||
exit(1);
|
||||
}
|
||||
hdr->header_csum = csum;
|
||||
}
|
||||
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
|
||||
if (bs->dsk.inmemory_journal)
|
||||
@@ -249,6 +241,22 @@ resume_4:
|
||||
// metadata read finished
|
||||
bs->heap->finish_load();
|
||||
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
|
||||
if (zero_on_init && !bs->dsk.disable_meta_fsync)
|
||||
{
|
||||
GET_SQE();
|
||||
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||
last_read_offset = 0;
|
||||
data->iov = { 0 };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||
submitted++;
|
||||
bs->ringloop->submit();
|
||||
resume_5:
|
||||
if (submitted > 0)
|
||||
{
|
||||
wait_state = 5;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// asynchronous recheck
|
||||
resume_6:
|
||||
wait_state = 6;
|
||||
@@ -329,22 +337,6 @@ resume_9:
|
||||
}
|
||||
free(metadata_buffer);
|
||||
metadata_buffer = NULL;
|
||||
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
|
||||
{
|
||||
GET_SQE();
|
||||
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||
last_read_offset = 0;
|
||||
data->iov = { 0 };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||
submitted++;
|
||||
bs->ringloop->submit();
|
||||
resume_5:
|
||||
if (submitted > 0)
|
||||
{
|
||||
wait_state = 5;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
|
||||
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
|
||||
format_size(bs->heap->get_data_used_space()).c_str(),
|
||||
|
||||
@@ -16,7 +16,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
||||
else if (priv->op_state == 5) goto resume_5;
|
||||
assert(!priv->op_state);
|
||||
op->retval = 0;
|
||||
PRIV(op)->lsn = 0;
|
||||
priv->modified_block = priv->modified_block2 = UINT32_MAX;
|
||||
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
|
||||
{
|
||||
@@ -37,12 +36,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
||||
FINISH_OP(op);
|
||||
return 2;
|
||||
}
|
||||
if (res == ENOENT)
|
||||
{
|
||||
op->retval = -ENOENT;
|
||||
FINISH_OP(op);
|
||||
return 2;
|
||||
}
|
||||
if (res == ENOSPC)
|
||||
{
|
||||
if (!heap->get_to_compact_count())
|
||||
|
||||
@@ -9,7 +9,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||
if (!PRIV(op)->op_state)
|
||||
{
|
||||
op->retval = 0;
|
||||
PRIV(op)->lsn = 0;
|
||||
}
|
||||
int res = do_sync(op, 0);
|
||||
if (res == 2)
|
||||
@@ -105,8 +104,7 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
|
||||
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
|
||||
return 2;
|
||||
}
|
||||
assert(!PRIV(op)->lsn);
|
||||
PRIV(op)->lsn = heap->get_completed_lsn();
|
||||
PRIV(op)->modified_block = heap->get_completed_lsn();
|
||||
if (!submit_fsyncs(PRIV(op)->pending_ops))
|
||||
{
|
||||
PRIV(op)->wait_detail = 1;
|
||||
@@ -120,6 +118,6 @@ resume_1:
|
||||
return 1;
|
||||
}
|
||||
resume_2:
|
||||
heap->mark_lsn_fsynced(PRIV(op)->lsn);
|
||||
heap->mark_lsn_fsynced(PRIV(op)->modified_block);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
|
||||
heap->complete_block_write(modified_block);
|
||||
ringloop->wakeup();
|
||||
};
|
||||
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + ((uint64_t)modified_block+1)*dsk.meta_block_size
|
||||
);
|
||||
@@ -178,7 +177,6 @@ enospc:
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
data->iov = (struct iovec){ op->buf, op->len };
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(loc+op->offset+op->len <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + loc + op->offset);
|
||||
PRIV(op)->pending_ops++;
|
||||
write_iodepth++;
|
||||
@@ -266,7 +264,6 @@ enospc:
|
||||
BS_SUBMIT_GET_SQE(sqe2, data2);
|
||||
data2->iov = (struct iovec){ op->buf, op->len };
|
||||
data2->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(loc+op->len <= dsk.journal_len);
|
||||
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, dsk.journal_offset + loc);
|
||||
PRIV(op)->pending_ops++;
|
||||
}
|
||||
@@ -456,7 +453,6 @@ resume_10:
|
||||
BS_SUBMIT_GET_SQE(sqe, data);
|
||||
data->iov = (struct iovec){ op->buf, op->len };
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(PRIV(op)->location + op->offset <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + PRIV(op)->location + op->offset);
|
||||
if (dsk.use_atomic_flag)
|
||||
sqe->rw_flags = RWF_ATOMIC;
|
||||
|
||||
@@ -141,7 +141,7 @@ struct __attribute__((__packed__)) journal_entry
|
||||
inline uint32_t je_crc32(journal_entry *je)
|
||||
{
|
||||
// 0x48674bc7 = crc32(4 zero bytes)
|
||||
return je->size < 4 ? 0 : crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
|
||||
return crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
|
||||
}
|
||||
|
||||
// "VITAstor"
|
||||
|
||||
@@ -520,7 +520,6 @@ resume_2:
|
||||
await_sqe(15);
|
||||
data->iov = (struct iovec){ it->buf, (size_t)it->len };
|
||||
data->callback = simple_callback_w;
|
||||
assert(clean_loc+it->offset+it->len <= bs->dsk.block_count*bs->dsk.data_block_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + it->offset
|
||||
);
|
||||
@@ -750,7 +749,6 @@ bool journal_flusher_co::write_meta_block(flusher_meta_write_t & meta_block, int
|
||||
await_sqe(0);
|
||||
data->iov = (struct iovec){ meta_block.buf, (size_t)bs->dsk.meta_block_size };
|
||||
data->callback = simple_callback_w;
|
||||
assert(bs->dsk.meta_block_size + meta_block.sector + bs->dsk.meta_block_size <= bs->dsk.meta_area_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_block.sector
|
||||
);
|
||||
|
||||
@@ -193,7 +193,6 @@ void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_
|
||||
(size_t)journal.block_size
|
||||
};
|
||||
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||
assert(journal.sector_info[cur_sector].offset+journal.block_size <= dsk.journal_len);
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
||||
);
|
||||
|
||||
@@ -368,9 +368,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
}
|
||||
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
const uint64_t write_offset = (loc * dsk.data_block_size) + op->offset - stripe_offset;
|
||||
assert(write_offset+op->len+stripe_offset+stripe_end <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + write_offset);
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + (loc * dsk.data_block_size) + op->offset - stripe_offset
|
||||
);
|
||||
PRIV(op)->pending_ops = 1;
|
||||
if (!(dirty_it->second.state & BS_ST_INSTANT))
|
||||
{
|
||||
@@ -495,8 +495,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
.op = op,
|
||||
});
|
||||
data2->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||
assert(journal.next_free+op->len <= dsk.journal_len);
|
||||
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free);
|
||||
io_uring_prep_writev(
|
||||
sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free
|
||||
);
|
||||
PRIV(op)->pending_ops++;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -12,7 +12,7 @@ if (RDMACM_LIBRARIES)
|
||||
set(MSGR_RDMACM "msgr_rdmacm.cpp")
|
||||
endif (RDMACM_LIBRARIES)
|
||||
add_library(vitastor_common STATIC
|
||||
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp
|
||||
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp
|
||||
msgr_encrypt.cpp msgr_handshake.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
|
||||
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
|
||||
)
|
||||
@@ -55,6 +55,9 @@ if (${WITH_FIO})
|
||||
../util/rw_blocking.cpp
|
||||
../util/addr_util.cpp
|
||||
)
|
||||
target_link_libraries(fio_vitastor_sec
|
||||
tcmalloc_minimal
|
||||
)
|
||||
endif (${WITH_FIO})
|
||||
|
||||
# vitastor-nbd
|
||||
@@ -101,7 +104,7 @@ add_executable(test_cluster_client
|
||||
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp cluster_client_icache.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp msgr_encrypt.cpp
|
||||
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp ../../json11/json11.cpp
|
||||
)
|
||||
target_link_libraries(test_cluster_client ${LIBURING_LIBRARIES} ${OPENSSL_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
|
||||
target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
|
||||
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
||||
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
|
||||
add_dependencies(build_tests test_cluster_client)
|
||||
|
||||
@@ -1162,13 +1162,6 @@ resume_2:
|
||||
// Finished successfully
|
||||
// Even if the PG count has changed in meanwhile we treat it as success
|
||||
// because if some operations were invalid for the new PG count we'd get errors
|
||||
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
|
||||
{
|
||||
// Copy part bitmaps only after finishing all part reads
|
||||
for (auto & part: op->parts)
|
||||
if ((part.flags & (PART_SENT|PART_DONE|PART_VALID)) == (PART_SENT|PART_DONE|PART_VALID))
|
||||
copy_part_bitmap(op, &part);
|
||||
}
|
||||
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
|
||||
{
|
||||
uint64_t next_inode = 0;
|
||||
@@ -1216,7 +1209,7 @@ resume_2:
|
||||
erase_op(op);
|
||||
return 1;
|
||||
}
|
||||
else if (op->retval != 0 && op->opcode != OSD_OP_SYNC && !(op->flags & OP_FLUSH_BUFFER) &&
|
||||
else if (op->retval != 0 && !(op->flags & OP_FLUSH_BUFFER) &&
|
||||
op->retval != -EPIPE && (op->retval != -EIO || !client_eio_retry_interval) && (op->retval != -ENOSPC || !client_retry_enospc))
|
||||
{
|
||||
// Fatal error (neither -EPIPE, -EIO nor -ENOSPC)
|
||||
@@ -1683,6 +1676,13 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
|
||||
}
|
||||
if (op->inflight_count == 0 && !op->retry_after)
|
||||
{
|
||||
// Copy part bitmaps only after finishing all part reads
|
||||
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
|
||||
{
|
||||
for (auto & part: op->parts)
|
||||
if ((part.flags & (PART_SENT|PART_VALID|PART_DONE)) == (PART_SENT|PART_VALID|PART_DONE))
|
||||
copy_part_bitmap(op, &part);
|
||||
}
|
||||
if (op->opcode == OSD_OP_SYNC)
|
||||
continue_sync(op);
|
||||
else
|
||||
|
||||
@@ -104,6 +104,9 @@ struct vault_load_key_t
|
||||
// FIXME: Split into public and private interfaces
|
||||
class __attribute__((visibility("default"))) cluster_client_t
|
||||
{
|
||||
#ifdef __MOCK__
|
||||
public:
|
||||
#endif
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
|
||||
@@ -197,7 +200,12 @@ public:
|
||||
void list_inode(inode_t inode, uint64_t min_offset, uint64_t max_offset, int max_parallel_pgs, std::function<void(
|
||||
int status, int pgs_left, pg_num_t pg_num, std::set<object_id>&& objects)> pg_callback);
|
||||
|
||||
//inline uint32_t get_bs_bitmap_granularity() { return st_cli.global_bitmap_granularity; }
|
||||
//inline uint64_t get_bs_block_size() { return st_cli.global_block_size; }
|
||||
|
||||
#ifndef __MOCK__
|
||||
protected:
|
||||
#endif
|
||||
void continue_ops(int time_passed = 0);
|
||||
|
||||
std::shared_ptr<inode_cache_t> inode_cache_get(inode_t ino);
|
||||
@@ -207,6 +215,7 @@ protected:
|
||||
void vault_destroy();
|
||||
void vault_parse_secret(const std::string & key_id, const std::string & err, json11::Json data);
|
||||
|
||||
protected:
|
||||
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
|
||||
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
|
||||
|
||||
@@ -249,5 +258,4 @@ protected:
|
||||
osd_num_t select_nearest_osd(const std::vector<osd_num_t> & osds);
|
||||
|
||||
friend class writeback_cache_t;
|
||||
friend class cluster_client_test_t;
|
||||
};
|
||||
|
||||
@@ -88,11 +88,6 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
|
||||
// ...or just save it for writeback if write buffering is enabled
|
||||
if (op->len == 0)
|
||||
{
|
||||
// FIXME: OSD_OP_DELETEs are currently only sent by vitastor-cli rm/rm-data and
|
||||
// actually have len=0, because delete is actually a delete of the full object
|
||||
// containing the requested offset, not a "punch hole" operation. But here, writeback
|
||||
// cache assumes it IS a "punch hole" operation. I should select one of these
|
||||
// approaches and fix everything accordingly when I decide to implement TRIM.
|
||||
return;
|
||||
}
|
||||
auto dirty_it = find_dirty(op->inode, op->offset);
|
||||
@@ -249,13 +244,12 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
|
||||
writeback_queue_size--;
|
||||
}
|
||||
}
|
||||
if (!is_del && op->len > 0)
|
||||
if (!is_del)
|
||||
{
|
||||
uint64_t pos = 0, len = op->len, iov_idx = 0;
|
||||
while (iov_idx < op->iov.count)
|
||||
while (len > 0 && iov_idx < op->iov.count)
|
||||
{
|
||||
auto & iov = op->iov.buf[iov_idx];
|
||||
assert(pos + iov.iov_len <= len);
|
||||
memcpy(buf + pos, iov.iov_base, iov.iov_len);
|
||||
pos += iov.iov_len;
|
||||
iov_idx++;
|
||||
@@ -449,7 +443,7 @@ void writeback_cache_t::start_writebacks(cluster_client_t *cli, int count)
|
||||
started++;
|
||||
assert(writeback_queue_size > 0);
|
||||
writeback_queue_size--;
|
||||
writeback_bytes -= (is_del ? 0 : off - from_it->first.stripe);
|
||||
writeback_bytes -= off - from_it->first.stripe;
|
||||
assert(writeback_queue_size > 0 || !writeback_bytes);
|
||||
flush_buffers(cli, from_it, to_it);
|
||||
}
|
||||
|
||||
@@ -291,12 +291,7 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
|
||||
add_etcd_url(ea.string_value());
|
||||
}
|
||||
}
|
||||
if (this->etcd_client_cert != "")
|
||||
{
|
||||
this->etcd_client_cert = config["etcd_client_cert"].string_value();
|
||||
this->etcd_client_key = config["etcd_client_key"].string_value();
|
||||
}
|
||||
else if (this->osd_num)
|
||||
if (this->osd_num)
|
||||
{
|
||||
this->etcd_client_cert = config["osd_cert"].string_value();
|
||||
this->etcd_client_key = config["osd_pkey"].string_value();
|
||||
@@ -306,6 +301,11 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
|
||||
this->etcd_client_cert = config["cert"].string_value();
|
||||
this->etcd_client_key = config["pkey"].string_value();
|
||||
}
|
||||
if (this->etcd_client_cert == "")
|
||||
{
|
||||
this->etcd_client_cert = config["etcd_client_cert"].string_value();
|
||||
this->etcd_client_key = config["etcd_client_key"].string_value();
|
||||
}
|
||||
this->etcd_ca = config["etcd_ca"].string_value();
|
||||
this->etcd_prefix = config["etcd_prefix"].string_value();
|
||||
if (this->etcd_prefix == "")
|
||||
@@ -775,8 +775,12 @@ void etcd_state_client_t::load_pgs()
|
||||
json11::Json::array txn = {
|
||||
json11::Json::object {
|
||||
{ "request_range", json11::Json::object {
|
||||
{ "key", base64_encode(etcd_prefix+"/config/") },
|
||||
{ "range_end", base64_encode(etcd_prefix+"/config0") },
|
||||
{ "key", base64_encode(etcd_prefix+"/config/pools") },
|
||||
} }
|
||||
},
|
||||
json11::Json::object {
|
||||
{ "request_range", json11::Json::object {
|
||||
{ "key", base64_encode(etcd_prefix+"/config/pgs") },
|
||||
} }
|
||||
},
|
||||
json11::Json::object {
|
||||
@@ -784,6 +788,12 @@ void etcd_state_client_t::load_pgs()
|
||||
{ "key", base64_encode(etcd_prefix+"/pg/config") },
|
||||
} }
|
||||
},
|
||||
json11::Json::object {
|
||||
{ "request_range", json11::Json::object {
|
||||
{ "key", base64_encode(etcd_prefix+"/config/inode/") },
|
||||
{ "range_end", base64_encode(etcd_prefix+"/config/inode0") },
|
||||
} }
|
||||
},
|
||||
json11::Json::object {
|
||||
{ "request_range", json11::Json::object {
|
||||
{ "key", base64_encode(etcd_prefix+"/pg/history/") },
|
||||
@@ -1298,6 +1308,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
if (i >= pg_state_bit_count)
|
||||
{
|
||||
fprintf(stderr, "Unexpected pool %u PG %u state keyword in etcd: %s\n", pool_id, pg_num, e.dump().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!cur_primary || !value["state"].is_array() || !state ||
|
||||
@@ -1306,6 +1317,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
(state & PG_INCOMPLETE) && state != PG_INCOMPLETE && state != (PG_INCOMPLETE|PG_HAS_INVALID))
|
||||
{
|
||||
fprintf(stderr, "Unexpected pool %u PG %u state in etcd: primary=%ju, state=%s\n", pool_id, pg_num, cur_primary, value["state"].dump().c_str());
|
||||
return;
|
||||
}
|
||||
pg_cfg.cur_primary = cur_primary;
|
||||
pg_cfg.cur_state = state;
|
||||
@@ -1413,7 +1425,9 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
inf = std::make_shared<user_info_t>();
|
||||
inf->name = name;
|
||||
}
|
||||
inf->type = value["type"] == "admin" ? user_type_t::ADMIN : user_type_t::CLIENT;
|
||||
inf->type = value["type"] == "admin" ? user_type_t::ADMIN :
|
||||
(value["type"] == "mon" ? user_type_t::MON :
|
||||
(value["type"] == "osd" ? user_type_t::OSD : user_type_t::CLIENT));
|
||||
inf->groups.clear();
|
||||
for (auto & group: value["groups"].array_items())
|
||||
{
|
||||
|
||||
@@ -115,6 +115,8 @@ enum class user_type_t
|
||||
{
|
||||
CLIENT = 0,
|
||||
ADMIN = 1,
|
||||
MON = 2,
|
||||
OSD = 3,
|
||||
};
|
||||
|
||||
struct user_perm_t
|
||||
|
||||
+190
-55
@@ -16,6 +16,106 @@
|
||||
#include "msgr_rdma.h"
|
||||
#endif
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
msgr_iothread_t::msgr_iothread_t():
|
||||
ring(RINGLOOP_DEFAULT_SIZE, true),
|
||||
thread(&msgr_iothread_t::run, this)
|
||||
{
|
||||
eventfd = ring.register_eventfd();
|
||||
if (eventfd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
|
||||
}
|
||||
}
|
||||
|
||||
msgr_iothread_t::~msgr_iothread_t()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
|
||||
{
|
||||
mu.lock();
|
||||
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
cond.notify_all();
|
||||
}
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::stop()
|
||||
{
|
||||
mu.lock();
|
||||
if (stopped)
|
||||
{
|
||||
mu.unlock();
|
||||
return;
|
||||
}
|
||||
stopped = true;
|
||||
if (outer_loop_data)
|
||||
{
|
||||
outer_loop_data->callback = [](ring_data_t*){};
|
||||
}
|
||||
cond.notify_all();
|
||||
close(eventfd);
|
||||
mu.unlock();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_to_ringloop(ring_loop_t *outer_loop)
|
||||
{
|
||||
assert(!this->outer_loop || this->outer_loop == outer_loop);
|
||||
io_uring_sqe *sqe = outer_loop->get_sqe();
|
||||
assert(sqe != NULL);
|
||||
this->outer_loop = outer_loop;
|
||||
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
|
||||
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
|
||||
outer_loop_data->callback = [this](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
|
||||
}
|
||||
outer_loop_data = NULL;
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_to_ringloop(this->outer_loop);
|
||||
ring.loop();
|
||||
};
|
||||
}
|
||||
|
||||
void msgr_iothread_t::run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mu);
|
||||
while (!stopped && !queue.size())
|
||||
cond.wait(lk);
|
||||
if (stopped)
|
||||
return;
|
||||
int i = 0;
|
||||
for (; i < queue.size(); i++)
|
||||
{
|
||||
io_uring_sqe *sqe = ring.get_sqe();
|
||||
if (!sqe)
|
||||
break;
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
*data = std::move(queue[i].data);
|
||||
*sqe = queue[i].sqe;
|
||||
sqe->user_data = (uint64_t)data;
|
||||
}
|
||||
queue.erase(queue.begin(), queue.begin()+i);
|
||||
}
|
||||
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
|
||||
ring.submit();
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::init()
|
||||
{
|
||||
init_tls();
|
||||
@@ -75,7 +175,12 @@ void osd_messenger_t::init()
|
||||
}
|
||||
if (ringloop && iothread_count > 0)
|
||||
{
|
||||
init_iothreads();
|
||||
for (int i = 0; i < iothread_count; i++)
|
||||
{
|
||||
auto iot = new msgr_iothread_t();
|
||||
iothreads.push_back(iot);
|
||||
iot->add_to_ringloop(ringloop);
|
||||
}
|
||||
}
|
||||
keepalive_timer_id = tfd->set_timer(1000, true, [this](int)
|
||||
{
|
||||
@@ -86,9 +191,12 @@ void osd_messenger_t::init()
|
||||
{
|
||||
auto cl = cl_it->second;
|
||||
cl_it++;
|
||||
if (!cl->osd_num && !cl->in_osd_num || cl->peer_state != PEER_CONNECTED && cl->peer_state != PEER_RDMA)
|
||||
if (!cl->osd_num && !cl->in_osd_num ||
|
||||
cl->peer_state != PEER_CONNECTED &&
|
||||
cl->peer_state != PEER_RDMA_CONNECTING_OUT &&
|
||||
cl->peer_state != PEER_RDMA)
|
||||
{
|
||||
// Do not run keepalive on regular clients
|
||||
// Do not run keepalive on regular and unconnected clients
|
||||
continue;
|
||||
}
|
||||
if (cl->ping_time_remaining > 0)
|
||||
@@ -96,10 +204,21 @@ void osd_messenger_t::init()
|
||||
cl->ping_time_remaining--;
|
||||
if (!cl->ping_time_remaining)
|
||||
{
|
||||
// Ping timed out, stop the client
|
||||
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n",
|
||||
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
|
||||
clients_to_stop.push_back(cl->client_id);
|
||||
// Ping timed out
|
||||
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT && cl->peer_fd >= 0)
|
||||
{
|
||||
fprintf(stderr, "Ping timed out for OSD %ju over RDMA (client %ju), switching back to TCP\n",
|
||||
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
|
||||
delete cl->rdma_conn;
|
||||
cl->rdma_conn = NULL;
|
||||
cl->peer_state = PEER_CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n",
|
||||
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
|
||||
clients_to_stop.push_back(cl->client_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cl->idle_time_remaining > 0)
|
||||
@@ -127,14 +246,24 @@ void osd_messenger_t::init()
|
||||
return;
|
||||
}
|
||||
auto cl = cl_it->second;
|
||||
uint64_t fail_client_id = (op->reply.hdr.retval != 0 ? op->client_id : 0);
|
||||
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
|
||||
bool failed = (op->reply.hdr.retval != 0);
|
||||
cl->ping_time_remaining = 0;
|
||||
delete op;
|
||||
if (fail_client_id)
|
||||
if (failed)
|
||||
{
|
||||
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, fail_client_id);
|
||||
stop_client(fail_client_id);
|
||||
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
|
||||
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT && cl->peer_fd >= 0)
|
||||
{
|
||||
fprintf(stderr, "Ping failed for OSD %ju over RDMA (client %ju), switching back to TCP\n", fail_osd_num, cl->client_id);
|
||||
delete cl->rdma_conn;
|
||||
cl->rdma_conn = NULL;
|
||||
cl->peer_state = PEER_CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, cl->client_id);
|
||||
stop_client(cl->client_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
cl->ping_time_remaining = osd_ping_timeout;
|
||||
@@ -169,7 +298,14 @@ osd_messenger_t::~osd_messenger_t()
|
||||
{
|
||||
stop_client(clients.begin()->first, true);
|
||||
}
|
||||
destroy_iothreads();
|
||||
if (iothreads.size())
|
||||
{
|
||||
for (auto iot: iothreads)
|
||||
{
|
||||
delete iot;
|
||||
}
|
||||
iothreads.clear();
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
for (auto rdma_context: rdma_contexts)
|
||||
{
|
||||
@@ -197,34 +333,19 @@ osd_messenger_t::~osd_messenger_t()
|
||||
destroy_tls();
|
||||
}
|
||||
|
||||
static int parse_proto_checksums(const json11::Json & val, int default_value)
|
||||
{
|
||||
if (val.is_string())
|
||||
{
|
||||
const auto & str = val.string_value();
|
||||
if (str == "full")
|
||||
return MSGR_CSUM_FULL;
|
||||
else if (str == "payload")
|
||||
return MSGR_CSUM_PAYLOAD;
|
||||
else if (str == "gcm")
|
||||
return MSGR_CSUM_GCM;
|
||||
else if (str == "none")
|
||||
return 0;
|
||||
else if (str == "")
|
||||
return default_value;
|
||||
}
|
||||
else if (val.is_null())
|
||||
return default_value;
|
||||
fprintf(stderr, "proto_checksums should be \"full\", \"payload\", \"gcm\", \"none\""
|
||||
", \"\" or null (default), but it is: %s\n", val.dump().c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void osd_messenger_t::parse_config(const json11::Json & config, bool init)
|
||||
{
|
||||
this->max_cipher_pool_size = config["max_cipher_pool_size"].uint64_value();
|
||||
if (!this->max_cipher_pool_size)
|
||||
this->max_cipher_pool_size = 256;
|
||||
if (config["proto_checksums"].is_null())
|
||||
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
|
||||
else if (config["proto_checksums"].is_bool())
|
||||
this->use_proto_checksums = config["proto_checksums"].bool_value() ? MSGR_CSUM_FULL : 0;
|
||||
else if (config["proto_checksums"].string_value() != "")
|
||||
this->use_proto_checksums = config["proto_checksums"].string_value() == "full" ? MSGR_CSUM_FULL : MSGR_CSUM_PAYLOAD;
|
||||
else
|
||||
this->use_proto_checksums = 0;
|
||||
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
|
||||
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
|
||||
this->receive_buffer_size = 65536;
|
||||
@@ -290,8 +411,6 @@ void osd_messenger_t::parse_config(const json11::Json & config, bool init)
|
||||
osd_tls_ca = config["osd_ca"].string_value();
|
||||
client_tls_ca = config["client_ca"].string_value();
|
||||
}
|
||||
this->use_proto_checksums = parse_proto_checksums(config["proto_checksums"], MSGR_CSUM_PAYLOAD);
|
||||
this->force_proto_checksums = parse_proto_checksums(config["force_proto_checksums"], tls_cert != "" ? MSGR_CSUM_PAYLOAD : 0);
|
||||
if (!osd_num)
|
||||
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
|
||||
else
|
||||
@@ -514,6 +633,17 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
|
||||
if (epoll_events & EPOLLRDHUP)
|
||||
{
|
||||
// Stop client
|
||||
if (cl->rdma_close_tcp && (cl->peer_state == PEER_RDMA ||
|
||||
cl->peer_state == PEER_RDMA_CONNECTING_IN ||
|
||||
cl->peer_state == PEER_RDMA_CONNECTING_OUT))
|
||||
{
|
||||
// It's allowed to stop the TCP socket during RDMA transition
|
||||
tfd->set_fd_handler(cl->peer_fd, false, NULL);
|
||||
clients_by_fd.erase(cl->peer_fd);
|
||||
close(cl->peer_fd);
|
||||
cl->peer_fd = -1;
|
||||
return;
|
||||
}
|
||||
if (log_level > 0)
|
||||
{
|
||||
fprintf(stderr, "[OSD %ju] client %ju disconnected\n", this->osd_num, cl->client_id);
|
||||
@@ -593,7 +723,10 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
||||
// Inform that we're OSD <osd_num>
|
||||
payload["osd_num"] = osd_num;
|
||||
}
|
||||
auto features = json11::Json::object{ { "check_sequencing", true } };
|
||||
auto features = json11::Json::object{
|
||||
{ "check_sequencing", true },
|
||||
{ "rdma_close_tcp", true },
|
||||
};
|
||||
if (use_proto_checksums)
|
||||
{
|
||||
features["proto_checksums"] = use_proto_checksums;
|
||||
@@ -670,21 +803,6 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
||||
err = !check_config_hook(cl, config);
|
||||
}
|
||||
}
|
||||
if (!err && use_proto_checksums)
|
||||
{
|
||||
auto peer_csums = config["features"]["proto_checksums"].uint64_value();
|
||||
if (peer_csums == MSGR_CSUM_GCM && use_proto_checksums == MSGR_CSUM_GCM && cl->gcm_enabled)
|
||||
cl->proto_csum_status = MSGR_CSUM_GCM;
|
||||
else if (peer_csums == MSGR_CSUM_FULL && use_proto_checksums == MSGR_CSUM_FULL)
|
||||
cl->proto_csum_status = MSGR_CSUM_FULL;
|
||||
else if (peer_csums && use_proto_checksums)
|
||||
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
|
||||
if (cl->proto_csum_status < force_proto_checksums)
|
||||
{
|
||||
fprintf(stderr, "Error: OSD %ju use_proto_checksums security level is lower than force_proto_checksums\n", cl->osd_num);
|
||||
err = true;
|
||||
}
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
osd_num_t peer_osd = cl->osd_num;
|
||||
@@ -693,7 +811,19 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
||||
delete op;
|
||||
return;
|
||||
}
|
||||
if (use_proto_checksums)
|
||||
{
|
||||
auto peer_csums = config["features"]["proto_checksums"].uint64_value();
|
||||
if (peer_csums == MSGR_CSUM_FULL && use_proto_checksums == MSGR_CSUM_FULL)
|
||||
cl->proto_csum_status = MSGR_CSUM_FULL;
|
||||
else if (peer_csums && use_proto_checksums)
|
||||
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (config["features"]["rdma_close_tcp"].bool_value())
|
||||
{
|
||||
cl->rdma_close_tcp = true;
|
||||
}
|
||||
if (!use_rdmacm && cl->rdma_conn && config["rdma_address"].is_string())
|
||||
{
|
||||
msgr_rdma_address_t addr;
|
||||
@@ -718,9 +848,14 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
||||
{
|
||||
fprintf(stderr, "Connected to OSD %ju using RDMA\n", cl->osd_num);
|
||||
}
|
||||
cl->peer_state = PEER_RDMA;
|
||||
cl->peer_state = (cl->rdma_close_tcp ? PEER_RDMA_CONNECTING_OUT : PEER_RDMA);
|
||||
// Add the initial receive request
|
||||
init_recv_rdma(cl);
|
||||
// Check the connection by using a ping
|
||||
cl->ping_time_remaining = osd_ping_timeout;
|
||||
cl->idle_time_remaining = 0;
|
||||
delete op;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+46
-10
@@ -35,14 +35,14 @@
|
||||
|
||||
#define PEER_CONNECTING 1
|
||||
#define PEER_CONNECTED 2
|
||||
#define PEER_RDMA_CONNECTING 3
|
||||
#define PEER_RDMA 4
|
||||
#define PEER_STOPPED 5
|
||||
#define PEER_RDMA_CONNECTING_IN 3
|
||||
#define PEER_RDMA_CONNECTING_OUT 4
|
||||
#define PEER_RDMA 5
|
||||
#define PEER_STOPPED 6
|
||||
|
||||
#define MSGR_CSUM_PAYLOAD 1
|
||||
#define MSGR_CSUM_FULL 2
|
||||
#define MSGR_CSUM_GCM 4
|
||||
#define MSGR_CSUM_NEG 8
|
||||
#define MSGR_CSUM_NEG 4
|
||||
|
||||
#define VITASTOR_CONFIG_PATH "/etc/vitastor/vitastor.conf"
|
||||
|
||||
@@ -93,6 +93,9 @@ struct osd_client_t
|
||||
msgr_rdma_connection_t *rdma_conn = NULL;
|
||||
#endif
|
||||
|
||||
uint8_t *ssl_out_buf = NULL;
|
||||
size_t ssl_out_buf_size = 0, ssl_out_buf_cap = 0;
|
||||
|
||||
bool gcm_enabled = false;
|
||||
msgr_handshake_i *hs = NULL;
|
||||
msgr_handshake_result_t hs_result;
|
||||
@@ -124,6 +127,7 @@ struct osd_client_t
|
||||
std::vector<int> recv_flags;
|
||||
uint64_t read_op_id = 1;
|
||||
bool check_sequencing = false;
|
||||
bool rdma_close_tcp = false;
|
||||
bool enable_pg_locks = false;
|
||||
op_aes_xts_decrypt_t *xts_dec_ctx = NULL;
|
||||
size_t read_op_inline_decrypt_pos = 0;
|
||||
@@ -183,7 +187,43 @@ struct osd_op_stats_t
|
||||
uint64_t subop_stat_count[OSD_OP_MAX+1] = { 0 };
|
||||
};
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __MOCK__
|
||||
class msgr_iothread_t;
|
||||
#else
|
||||
struct iothread_sqe_t
|
||||
{
|
||||
io_uring_sqe sqe;
|
||||
ring_data_t data;
|
||||
};
|
||||
|
||||
class msgr_iothread_t
|
||||
{
|
||||
protected:
|
||||
ring_loop_t ring;
|
||||
ring_loop_t *outer_loop = NULL;
|
||||
ring_data_t *outer_loop_data = NULL;
|
||||
int eventfd = -1;
|
||||
bool stopped = false;
|
||||
std::mutex mu;
|
||||
std::condition_variable cond;
|
||||
std::vector<iothread_sqe_t> queue;
|
||||
std::thread thread;
|
||||
|
||||
void run();
|
||||
public:
|
||||
|
||||
msgr_iothread_t();
|
||||
~msgr_iothread_t();
|
||||
|
||||
void add_sqe(io_uring_sqe & sqe);
|
||||
void stop();
|
||||
void add_to_ringloop(ring_loop_t *outer_loop);
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef WITH_RDMA
|
||||
struct rdma_event_channel;
|
||||
@@ -269,7 +309,7 @@ protected:
|
||||
|
||||
public:
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
ring_loop_i *ringloop = NULL;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
bool has_sendmsg_zc = false;
|
||||
uint64_t next_client_id = 1;
|
||||
// osd_num = 0 for client messenger, osd_num > 0 for OSD messenger
|
||||
@@ -288,12 +328,10 @@ public:
|
||||
std::vector<std::string> all_osd_networks;
|
||||
std::vector<addr_mask_t> all_osd_network_masks;
|
||||
int use_proto_checksums = 0;
|
||||
int force_proto_checksums = 0;
|
||||
// op statistics
|
||||
osd_op_stats_t stats, recovery_stats;
|
||||
|
||||
void init();
|
||||
void init_iothreads();
|
||||
void parse_config(const json11::Json & config, bool init);
|
||||
void connect_peer(uint64_t osd_num, json11::Json peer_state);
|
||||
void stop_client(uint64_t client_id, bool force_delete = false);
|
||||
@@ -307,7 +345,6 @@ public:
|
||||
void read_requests();
|
||||
void send_replies();
|
||||
void accept_connections(int listen_fd);
|
||||
void destroy_iothreads();
|
||||
~osd_messenger_t();
|
||||
|
||||
static json11::Json::object read_config(const json11::Json & config);
|
||||
@@ -371,7 +408,6 @@ protected:
|
||||
bool init_recv_rdma(osd_client_t *cl);
|
||||
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
|
||||
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
|
||||
void destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn);
|
||||
#endif
|
||||
#ifdef WITH_RDMACM
|
||||
void handle_rdmacm_events();
|
||||
|
||||
@@ -44,10 +44,9 @@ op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void op_aes_xts_encrypt_t::start(osd_client_t *cl, uint8_t *key, uint64_t start_offset, size_t block_size)
|
||||
void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t block_size)
|
||||
{
|
||||
assert(!encrypted);
|
||||
this->cl = cl;
|
||||
this->start_offset = start_offset;
|
||||
this->key = key;
|
||||
this->block_size = block_size;
|
||||
@@ -92,28 +91,6 @@ void op_aes_xts_encrypt_t::encrypt_block(uint8_t *in, uint8_t *out)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void copy_or_gcm(osd_client_t *cl, uint8_t *out, uint8_t *in, size_t n)
|
||||
{
|
||||
if (cl->proto_csum_status != MSGR_CSUM_GCM)
|
||||
memcpy(out, in, n);
|
||||
else
|
||||
{
|
||||
#ifdef WITH_ISAL_CRYPTO
|
||||
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, out, in, n);
|
||||
assert(!r);
|
||||
#else
|
||||
int actual_out;
|
||||
if (EVP_EncryptUpdate(cl->enc_ctx, out, &actual_out, in, n) != 1)
|
||||
{
|
||||
fprintf(stderr, "EncryptUpdate error: ");
|
||||
ERR_print_errors_fp(stderr);
|
||||
abort();
|
||||
}
|
||||
assert(actual_out == n);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
|
||||
{
|
||||
// Fucking AES-XTS implementations (all of them) don't have streaming support,
|
||||
@@ -127,7 +104,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
assert(tmp);
|
||||
if (max_out > block_size - tmp_pos)
|
||||
max_out = block_size - tmp_pos;
|
||||
copy_or_gcm(cl, out, tmp + tmp_pos, max_out);
|
||||
memcpy(out, tmp + tmp_pos, max_out);
|
||||
done_out += max_out;
|
||||
tmp_pos += max_out;
|
||||
if (tmp_pos >= block_size)
|
||||
@@ -160,7 +137,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
memcpy(tmp + offset%block_size, in, max_in);
|
||||
encrypt_block(tmp, tmp);
|
||||
encrypted = true;
|
||||
copy_or_gcm(cl, out, tmp, max_out);
|
||||
memcpy(out, tmp, max_out);
|
||||
tmp_pos = max_out;
|
||||
done_in += max_in-1;
|
||||
offset += max_in;
|
||||
@@ -170,8 +147,6 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
{
|
||||
// Full block - simplest case
|
||||
encrypt_block(in, out);
|
||||
if (cl->proto_csum_status == MSGR_CSUM_GCM)
|
||||
copy_or_gcm(cl, out, out, block_size);
|
||||
done_in += block_size;
|
||||
offset += block_size;
|
||||
done_out += block_size;
|
||||
@@ -183,8 +158,6 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
max_in = block_size - offset%block_size;
|
||||
memcpy(tmp + offset%block_size, in, max_in);
|
||||
encrypt_block(tmp, out);
|
||||
if (cl->proto_csum_status == MSGR_CSUM_GCM)
|
||||
copy_or_gcm(cl, out, out, block_size);
|
||||
done_in += max_in;
|
||||
offset += max_in;
|
||||
done_out += block_size;
|
||||
@@ -223,10 +196,9 @@ op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void op_aes_xts_decrypt_t::start(osd_client_t *cl, uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
|
||||
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
|
||||
{
|
||||
assert(!decrypted);
|
||||
this->cl = cl;
|
||||
this->start_offset = start_offset;
|
||||
this->key_chain = key_chain;
|
||||
this->chain_size = chain_size;
|
||||
@@ -297,23 +269,6 @@ void op_aes_xts_decrypt_t::decrypt_block(uint8_t *in, uint8_t *out)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void gcm_dec(osd_client_t *cl, uint8_t *out, uint8_t *in, size_t n)
|
||||
{
|
||||
#ifdef WITH_ISAL_CRYPTO
|
||||
int r = isal_aes_gcm_dec_256_update(&cl->peer_key_isal, cl->dec_ctx, out, in, n);
|
||||
assert(!r);
|
||||
#else
|
||||
int actual_out;
|
||||
if (EVP_DecryptUpdate(cl->dec_ctx, out, &actual_out, in, n) != 1)
|
||||
{
|
||||
fprintf(stderr, "DecryptUpdate error: ");
|
||||
ERR_print_errors_fp(stderr);
|
||||
abort();
|
||||
}
|
||||
assert(actual_out == n);
|
||||
#endif
|
||||
}
|
||||
|
||||
// out may be NULL, in this case all input is still decrypted to calculate checksums,
|
||||
// but part of it is skipped and not copied to out
|
||||
void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
|
||||
@@ -361,8 +316,6 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
}
|
||||
max_in = block_size - offset%block_size;
|
||||
memcpy(tmp + offset%block_size, in, max_in);
|
||||
if (cl->proto_csum_status == MSGR_CSUM_GCM)
|
||||
gcm_dec(cl, tmp, tmp, block_size);
|
||||
decrypt_block(tmp, tmp);
|
||||
decrypted = true;
|
||||
if (out)
|
||||
@@ -375,18 +328,7 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
else if (!(offset%block_size))
|
||||
{
|
||||
// Full block - simplest case
|
||||
if (cl->proto_csum_status == MSGR_CSUM_GCM)
|
||||
{
|
||||
if (!tmp)
|
||||
{
|
||||
tmp = (uint8_t*)malloc_or_die(block_size);
|
||||
tmp_size = block_size;
|
||||
}
|
||||
gcm_dec(cl, tmp, in, block_size);
|
||||
if (out)
|
||||
decrypt_block(tmp, out);
|
||||
}
|
||||
else
|
||||
if (out)
|
||||
decrypt_block(in, out);
|
||||
done_in += block_size;
|
||||
offset += block_size;
|
||||
@@ -399,8 +341,6 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
|
||||
max_in = block_size - offset%block_size;
|
||||
memcpy(tmp + offset%block_size, in, max_in);
|
||||
assert(out);
|
||||
if (cl->proto_csum_status == MSGR_CSUM_GCM)
|
||||
gcm_dec(cl, tmp, tmp, block_size);
|
||||
decrypt_block(tmp, out);
|
||||
done_in += max_in;
|
||||
offset += max_in;
|
||||
@@ -425,7 +365,7 @@ void osd_messenger_t::op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf,
|
||||
else
|
||||
cl->xts_enc_ctx = new op_aes_xts_encrypt_t();
|
||||
assert(cl->write_op->enc->key_chain[0]);
|
||||
cl->xts_enc_ctx->start(cl, cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
|
||||
cl->xts_enc_ctx->start(cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
|
||||
}
|
||||
while (done_plain < plain_len && done_enc < enc_len)
|
||||
{
|
||||
@@ -471,7 +411,7 @@ void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
|
||||
cl->xts_dec_ctx = new op_aes_xts_decrypt_t();
|
||||
auto & enc = cl->read_op->enc;
|
||||
assert(cl->read_op->req.hdr.opcode == OSD_OP_READ);
|
||||
cl->xts_dec_ctx->start(cl, enc->key_chain, enc->chain_size,
|
||||
cl->xts_dec_ctx->start(enc->key_chain, enc->chain_size,
|
||||
(cl->read_op->req.rw.flags & OSD_OP_RETURN_CHAIN) ? (uint8_t*)cl->read_op->bitmap + enc->read_chain_bitmap_pos : 0,
|
||||
cl->read_op->req.rw.offset, enc->bitmap_granularity);
|
||||
}
|
||||
@@ -621,7 +561,7 @@ void osd_messenger_t::init_tls_client(osd_client_t *cl)
|
||||
cl->gcm_enabled = true;
|
||||
cl->hs = hs_ctx->create();
|
||||
cl->hs->init(cl->is_incoming);
|
||||
if (cl->hs->out_size())
|
||||
if (cl->hs->get_out().size())
|
||||
{
|
||||
if (cl->write_state == 0)
|
||||
{
|
||||
|
||||
@@ -12,14 +12,11 @@
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
struct osd_client_t;
|
||||
|
||||
class op_aes_xts_encrypt_t
|
||||
{
|
||||
#ifndef WITH_ISAL_CRYPTO
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
#endif
|
||||
osd_client_t *cl = NULL;
|
||||
uint64_t start_offset = 0;
|
||||
uint8_t *key = NULL;
|
||||
size_t offset = 0;
|
||||
@@ -35,7 +32,7 @@ public:
|
||||
op_aes_xts_encrypt_t();
|
||||
~op_aes_xts_encrypt_t();
|
||||
|
||||
void start(osd_client_t *cl, uint8_t *key, uint64_t start_offset, size_t block_size);
|
||||
void start(uint8_t *key, uint64_t start_offset, size_t block_size);
|
||||
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
|
||||
};
|
||||
|
||||
@@ -46,7 +43,6 @@ class op_aes_xts_decrypt_t
|
||||
#ifndef WITH_ISAL_CRYPTO
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
#endif
|
||||
osd_client_t *cl = NULL;
|
||||
uint64_t start_offset = 0;
|
||||
uint8_t **key_chain = NULL;
|
||||
size_t chain_size = 0;
|
||||
@@ -65,7 +61,7 @@ public:
|
||||
op_aes_xts_decrypt_t();
|
||||
~op_aes_xts_decrypt_t();
|
||||
|
||||
void start(osd_client_t *cl, uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
|
||||
void start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
|
||||
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "msgr_handshake.h"
|
||||
#include "malloc_or_die.h"
|
||||
#include "openssl_util.h"
|
||||
#include "str_util.h"
|
||||
|
||||
@@ -85,8 +84,7 @@ class msgr_handshake_t: public msgr_handshake_i
|
||||
|
||||
std::vector<uint8_t> full_handshake;
|
||||
std::vector<uint8_t> in_buf;
|
||||
uint8_t *out_buf = NULL;
|
||||
size_t out_buf_size = 0;
|
||||
std::vector<uint8_t> out_buf;
|
||||
std::string error;
|
||||
|
||||
int state = 0;
|
||||
@@ -127,10 +125,7 @@ public:
|
||||
bool init(bool server_mode) override;
|
||||
ssize_t handle(uint8_t* in_buf, size_t in_size) override;
|
||||
bool done() override;
|
||||
uint8_t *get_out() override;
|
||||
size_t out_size() override;
|
||||
void eat_out(size_t n) override;
|
||||
void reset_out() override;
|
||||
std::vector<uint8_t>& get_out() override;
|
||||
msgr_handshake_result_t get_result() override;
|
||||
std::string get_error() override;
|
||||
};
|
||||
@@ -252,8 +247,6 @@ bool msgr_handshake_ctx_t::derive_kdf(const uint8_t* insecret, size_t insecret_l
|
||||
|
||||
msgr_handshake_t::~msgr_handshake_t()
|
||||
{
|
||||
if (out_buf)
|
||||
free(out_buf);
|
||||
if (peer_cert)
|
||||
X509_free(peer_cert);
|
||||
if (ec_key)
|
||||
@@ -295,12 +288,6 @@ static void copy_to(std::vector<uint8_t> & buf, const void* src, uint32_t len)
|
||||
memcpy(buf.data() + old_size, src, len);
|
||||
}
|
||||
|
||||
static void copy_to_raw(uint8_t* & buf, const void* src, size_t len)
|
||||
{
|
||||
memcpy(buf, src, len);
|
||||
buf += len;
|
||||
}
|
||||
|
||||
static void copy_to_with_len(std::vector<uint8_t> & buf, const void* src, uint32_t len)
|
||||
{
|
||||
copy_to(buf, &len, sizeof(len));
|
||||
@@ -455,10 +442,9 @@ bool msgr_handshake_t::make_client_init()
|
||||
size_t key_len = EVP_PKEY_get1_encoded_public_key(ec_key, &key);
|
||||
if (!key_len)
|
||||
return on_error("EVP_PKEY_get1_encoded_public_key: ");
|
||||
const size_t old_out_size = out_buf_size;
|
||||
out_buf_size += key_len + sizeof(msgr_handshake_hdr_t);
|
||||
out_buf = (uint8_t*)realloc_or_die(out_buf, out_buf_size);
|
||||
uint8_t *buf = out_buf + old_out_size;
|
||||
const size_t old_len = out_buf.size();
|
||||
out_buf.resize(out_buf.size() + key_len + sizeof(msgr_handshake_hdr_t));
|
||||
uint8_t *buf = out_buf.data() + old_len;
|
||||
msgr_handshake_hdr_t *hdr = (msgr_handshake_hdr_t *)buf;
|
||||
hdr->msg_len = key_len + sizeof(msgr_handshake_hdr_t);
|
||||
hdr->magic = MSGR_HS_MAGIC;
|
||||
@@ -499,12 +485,9 @@ bool msgr_handshake_t::make_server_reply()
|
||||
}
|
||||
// Construct message
|
||||
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + 4 + key_len + encrypt_data.size();
|
||||
out_buf = (uint8_t*)realloc_or_die(out_buf, (out_buf_size += hdr.msg_len));
|
||||
uint8_t *cur = out_buf + out_buf_size - hdr.msg_len;
|
||||
copy_to_raw(cur, &hdr, sizeof(hdr));
|
||||
copy_to_raw(cur, &key_len, 4);
|
||||
copy_to_raw(cur, key, key_len);
|
||||
copy_to_raw(cur, encrypt_data.data(), encrypt_data.size());
|
||||
copy_to(out_buf, &hdr, sizeof(hdr));
|
||||
copy_to_with_len(out_buf, key, key_len);
|
||||
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
|
||||
OPENSSL_free(key);
|
||||
return true;
|
||||
}
|
||||
@@ -527,10 +510,8 @@ bool msgr_handshake_t::make_client_reply()
|
||||
return false;
|
||||
// Construct message
|
||||
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + encrypt_data.size();
|
||||
out_buf = (uint8_t*)realloc_or_die(out_buf, (out_buf_size += hdr.msg_len));
|
||||
uint8_t *cur = out_buf + out_buf_size - hdr.msg_len;
|
||||
copy_to_raw(cur, &hdr, sizeof(hdr));
|
||||
copy_to_raw(cur, encrypt_data.data(), encrypt_data.size());
|
||||
copy_to(out_buf, &hdr, sizeof(hdr));
|
||||
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -761,37 +742,11 @@ bool msgr_handshake_t::done()
|
||||
return (state == MSGR_HS_DONE);
|
||||
}
|
||||
|
||||
uint8_t *msgr_handshake_t::get_out()
|
||||
std::vector<uint8_t>& msgr_handshake_t::get_out()
|
||||
{
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
size_t msgr_handshake_t::out_size()
|
||||
{
|
||||
return out_buf_size;
|
||||
}
|
||||
|
||||
void msgr_handshake_t::eat_out(size_t n)
|
||||
{
|
||||
if (n >= out_buf_size)
|
||||
{
|
||||
free(out_buf);
|
||||
out_buf = NULL;
|
||||
out_buf_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(out_buf, out_buf + n, out_buf_size - n);
|
||||
out_buf_size -= n;
|
||||
}
|
||||
}
|
||||
|
||||
void msgr_handshake_t::reset_out()
|
||||
{
|
||||
out_buf = NULL;
|
||||
out_buf_size = 0;
|
||||
}
|
||||
|
||||
msgr_handshake_result_t msgr_handshake_t::get_result()
|
||||
{
|
||||
if (state != MSGR_HS_DONE)
|
||||
|
||||
@@ -35,10 +35,7 @@ public:
|
||||
virtual bool init(bool server_mode) = 0;
|
||||
virtual ssize_t handle(uint8_t* in_buf, size_t in_size) = 0;
|
||||
virtual bool done() = 0;
|
||||
virtual uint8_t *get_out() = 0;
|
||||
virtual size_t out_size() = 0;
|
||||
virtual void eat_out(size_t n) = 0;
|
||||
virtual void reset_out() = 0;
|
||||
virtual std::vector<uint8_t> & get_out() = 0;
|
||||
virtual msgr_handshake_result_t get_result() = 0;
|
||||
virtual std::string get_error() = 0;
|
||||
};
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
|
||||
msgr_iothread_t::msgr_iothread_t():
|
||||
ring(RINGLOOP_DEFAULT_SIZE, true),
|
||||
thread(&msgr_iothread_t::run, this)
|
||||
{
|
||||
eventfd = ring.register_eventfd();
|
||||
if (eventfd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
|
||||
}
|
||||
}
|
||||
|
||||
msgr_iothread_t::~msgr_iothread_t()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
|
||||
{
|
||||
mu.lock();
|
||||
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
cond.notify_all();
|
||||
}
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::stop()
|
||||
{
|
||||
mu.lock();
|
||||
if (stopped)
|
||||
{
|
||||
mu.unlock();
|
||||
return;
|
||||
}
|
||||
stopped = true;
|
||||
if (outer_loop_data)
|
||||
{
|
||||
outer_loop_data->callback = [](ring_data_t*){};
|
||||
}
|
||||
cond.notify_all();
|
||||
close(eventfd);
|
||||
mu.unlock();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_to_ringloop(ring_loop_i *outer_loop)
|
||||
{
|
||||
assert(!this->outer_loop || this->outer_loop == outer_loop);
|
||||
io_uring_sqe *sqe = outer_loop->get_sqe();
|
||||
assert(sqe != NULL);
|
||||
this->outer_loop = outer_loop;
|
||||
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
|
||||
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
|
||||
outer_loop_data->callback = [this](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
|
||||
}
|
||||
outer_loop_data = NULL;
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_to_ringloop(this->outer_loop);
|
||||
ring.loop();
|
||||
};
|
||||
}
|
||||
|
||||
void msgr_iothread_t::run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mu);
|
||||
while (!stopped && !queue.size())
|
||||
cond.wait(lk);
|
||||
if (stopped)
|
||||
return;
|
||||
int i = 0;
|
||||
for (; i < queue.size(); i++)
|
||||
{
|
||||
io_uring_sqe *sqe = ring.get_sqe();
|
||||
if (!sqe)
|
||||
break;
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
*data = std::move(queue[i].data);
|
||||
*sqe = queue[i].sqe;
|
||||
sqe->user_data = (uint64_t)data;
|
||||
}
|
||||
queue.erase(queue.begin(), queue.begin()+i);
|
||||
}
|
||||
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
|
||||
ring.submit();
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::init_iothreads()
|
||||
{
|
||||
for (int i = 0; i < iothread_count; i++)
|
||||
{
|
||||
auto iot = new msgr_iothread_t();
|
||||
iothreads.push_back(iot);
|
||||
iot->add_to_ringloop(ringloop);
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_iothreads()
|
||||
{
|
||||
if (iothreads.size())
|
||||
{
|
||||
for (auto iot: iothreads)
|
||||
{
|
||||
delete iot;
|
||||
}
|
||||
iothreads.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "ringloop.h"
|
||||
|
||||
struct iothread_sqe_t
|
||||
{
|
||||
io_uring_sqe sqe;
|
||||
ring_data_t data;
|
||||
};
|
||||
|
||||
class msgr_iothread_t
|
||||
{
|
||||
protected:
|
||||
ring_loop_t ring;
|
||||
ring_loop_i *outer_loop = NULL;
|
||||
ring_data_t *outer_loop_data = NULL;
|
||||
int eventfd = -1;
|
||||
bool stopped = false;
|
||||
std::mutex mu;
|
||||
std::condition_variable cond;
|
||||
std::vector<iothread_sqe_t> queue;
|
||||
std::thread thread;
|
||||
|
||||
void run();
|
||||
public:
|
||||
|
||||
msgr_iothread_t();
|
||||
~msgr_iothread_t();
|
||||
|
||||
void add_sqe(io_uring_sqe & sqe);
|
||||
void stop();
|
||||
void add_to_ringloop(ring_loop_i *outer_loop);
|
||||
};
|
||||
@@ -23,6 +23,10 @@ osd_op_t::~osd_op_t()
|
||||
// So we don't reuse it, but free it every time
|
||||
free(buf);
|
||||
}
|
||||
if (enc_buf)
|
||||
{
|
||||
free(enc_buf);
|
||||
}
|
||||
}
|
||||
|
||||
bool osd_op_t::is_recovery_related()
|
||||
|
||||
@@ -174,7 +174,6 @@ struct __attribute__((visibility("default"))) osd_op_t
|
||||
timespec tv_begin = { 0 }, tv_end = { 0 };
|
||||
uint64_t op_type = OSD_OP_IN;
|
||||
uint64_t client_id = 0;
|
||||
osd_num_t osd_num = 0;
|
||||
osd_any_op_t req;
|
||||
osd_any_reply_t reply;
|
||||
blockstore_op_t *bs_op = NULL;
|
||||
@@ -186,6 +185,7 @@ struct __attribute__((visibility("default"))) osd_op_t
|
||||
uint8_t *bitmap_buf = NULL;
|
||||
void *rmw_buf = NULL;
|
||||
std::shared_ptr<osd_op_enc_t> enc;
|
||||
uint8_t *enc_buf = NULL;
|
||||
uint64_t csum = 0; // network layer checksum
|
||||
osd_primary_op_data_t* op_data = NULL;
|
||||
std::function<void(osd_op_t*)> callback;
|
||||
|
||||
+34
-24
@@ -541,7 +541,7 @@ bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address,
|
||||
{
|
||||
// Remember connection, but switch to RDMA only after sending the configuration response
|
||||
cl->rdma_conn = rdma_conn;
|
||||
cl->peer_state = PEER_RDMA_CONNECTING;
|
||||
cl->peer_state = PEER_RDMA_CONNECTING_IN;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -590,9 +590,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||
while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send)
|
||||
{
|
||||
dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos;
|
||||
dst_len = (rc->send_out_pos >= rc->send_done_pos
|
||||
? rc->send_out_size-rc->send_out_pos
|
||||
: rc->send_done_pos-rc->send_out_pos);
|
||||
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
|
||||
if (dst_len > rc->max_msg)
|
||||
dst_len = rc->max_msg;
|
||||
copied = copy_ops_to(cl, dst, dst_len);
|
||||
@@ -607,7 +605,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||
if (rc->send_out_pos == rc->send_out_size)
|
||||
rc->send_out_pos = 0;
|
||||
assert(rc->send_out_pos < rc->send_out_size);
|
||||
if (rc->send_out_pos == rc->send_done_pos)
|
||||
if (rc->send_out_pos >= rc->send_done_pos)
|
||||
rc->send_out_full = true;
|
||||
ibv_sge sge = {
|
||||
.addr = (uintptr_t)dst,
|
||||
@@ -645,6 +643,7 @@ static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
|
||||
|
||||
bool osd_messenger_t::init_recv_rdma(osd_client_t *cl)
|
||||
{
|
||||
return true;
|
||||
auto rc = cl->rdma_conn;
|
||||
assert(!rc->recv_buf.buf);
|
||||
rc->recv_buf.buf = (uint8_t*)malloc_or_die(rc->max_msg * rc->max_recv);
|
||||
@@ -700,9 +699,14 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
||||
continue;
|
||||
}
|
||||
auto rc = cl->rdma_conn;
|
||||
if (!rc)
|
||||
{
|
||||
// Connection destroyed (fallback to TCP)
|
||||
continue;
|
||||
}
|
||||
if (wc[i].status != IBV_WC_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "RDMA work request failed for client %ju", client_id);
|
||||
fprintf(stderr, "RDMA work request (%s) failed for client %ju", is_send ? "send" : "recv", client_id);
|
||||
if (cl->osd_num)
|
||||
{
|
||||
fprintf(stderr, " (OSD %ju)", cl->osd_num);
|
||||
@@ -713,6 +717,28 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
||||
}
|
||||
if (!is_send)
|
||||
{
|
||||
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT)
|
||||
{
|
||||
// First message received over RDMA - TCP socket can be closed now
|
||||
if (log_level > 0)
|
||||
{
|
||||
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id);
|
||||
}
|
||||
cl->peer_state = PEER_RDMA;
|
||||
if (cl->peer_fd >= 0 && cl->rdma_close_tcp)
|
||||
{
|
||||
// TCP socket is not needed anymore
|
||||
tfd->set_fd_handler(cl->peer_fd, false, NULL);
|
||||
clients_by_fd.erase(cl->peer_fd);
|
||||
close(cl->peer_fd);
|
||||
cl->peer_fd = -1;
|
||||
}
|
||||
if (cl->osd_num)
|
||||
{
|
||||
osd_peers[cl->osd_num] = cl;
|
||||
on_connect_peer(cl->osd_num, 0, cl->client_id);
|
||||
}
|
||||
}
|
||||
rc->cur_recv--;
|
||||
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
|
||||
{
|
||||
@@ -732,12 +758,9 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
||||
if (rc->send_done_pos == rc->send_out_size)
|
||||
rc->send_done_pos = 0;
|
||||
assert(rc->send_done_pos < rc->send_out_size);
|
||||
while (osd_op_t *op = cl->send_free_ops.front())
|
||||
while (cl->send_free_ops.front())
|
||||
{
|
||||
if (!((size_t)op & 7))
|
||||
delete op;
|
||||
else
|
||||
free((void*)((size_t)op & ~(size_t)7));
|
||||
delete cl->send_free_ops.front();
|
||||
cl->send_free_ops.pop_front();
|
||||
}
|
||||
cl->send_free_ops.pop_front();
|
||||
@@ -751,16 +774,3 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
||||
}
|
||||
} while (event_count > 0);
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
|
||||
{
|
||||
if (rdma_conn->cmid)
|
||||
{
|
||||
auto rdma_it = rdmacm_connections.find(rdma_conn->cmid);
|
||||
if (rdma_it != rdmacm_connections.end() && rdma_it->second->rdma_conn == rdma_conn)
|
||||
{
|
||||
rdmacm_connections.erase(rdma_it);
|
||||
}
|
||||
}
|
||||
delete rdma_conn;
|
||||
}
|
||||
|
||||
+56
-31
@@ -4,7 +4,6 @@
|
||||
#define _XOPEN_SOURCE
|
||||
#include <limits.h>
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
#include "openssl_util.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
@@ -170,17 +169,41 @@ public:
|
||||
if (done >= bufsize)
|
||||
return false;
|
||||
size_t n = dst_len-from;
|
||||
if (n > bufsize-done)
|
||||
n = bufsize-done;
|
||||
if (flags & RDR_XTS)
|
||||
if (!(flags & RDR_GCM))
|
||||
{
|
||||
msgr->op_decrypted_copy_buf(cl, curbuf, bufsize, dst, dst_len, from, done);
|
||||
n = 0;
|
||||
if (n > bufsize-done)
|
||||
n = bufsize-done;
|
||||
if (flags & RDR_XTS)
|
||||
{
|
||||
msgr->op_decrypted_copy_buf(cl, curbuf, bufsize, dst, dst_len, from, done);
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cl->read_csum_state && !(flags & RDR_NO_CSUM))
|
||||
{
|
||||
// data may be skipped if dst == NULL but checksum is still calculated
|
||||
XXH3_64bits_update(cl->read_csum_state, curbuf+done, n);
|
||||
}
|
||||
// Here, dst == NULL is allowed
|
||||
if (dst != NULL)
|
||||
memcpy(dst+from, curbuf+done, n);
|
||||
done += n;
|
||||
}
|
||||
cl->read_op_pos += n;
|
||||
from += n;
|
||||
if (from < dst_len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (flags & RDR_GCM)
|
||||
else
|
||||
{
|
||||
// Here, dst == NULL is not allowed
|
||||
assert(dst != NULL);
|
||||
size_t n = dst_len-from;
|
||||
if (n > bufsize-done)
|
||||
n = bufsize-done;
|
||||
#ifdef WITH_ISAL_CRYPTO
|
||||
int r = isal_aes_gcm_dec_256_update(&cl->peer_key_isal, cl->dec_ctx, dst+from, curbuf+done, n);
|
||||
assert(!r);
|
||||
@@ -199,24 +222,12 @@ public:
|
||||
XXH3_64bits_update(cl->read_csum_state, dst+from, n);
|
||||
}
|
||||
done += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cl->read_csum_state && !(flags & RDR_NO_CSUM))
|
||||
from += n;
|
||||
cl->read_op_pos += n;
|
||||
if (from < dst_len)
|
||||
{
|
||||
// data may be skipped if dst == NULL but checksum is still calculated
|
||||
XXH3_64bits_update(cl->read_csum_state, curbuf+done, n);
|
||||
return false;
|
||||
}
|
||||
// Here, dst == NULL is allowed
|
||||
if (dst != NULL)
|
||||
memcpy(dst+from, curbuf+done, n);
|
||||
done += n;
|
||||
}
|
||||
cl->read_op_pos += n;
|
||||
from += n;
|
||||
if (from < dst_len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
from = 0;
|
||||
return true;
|
||||
@@ -385,6 +396,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WITH_RDMA
|
||||
#include "msgr_rdma.h"
|
||||
#endif
|
||||
|
||||
void osd_messenger_t::read_requests()
|
||||
{
|
||||
for (int i = 0; i < read_ready_clients.size(); i++)
|
||||
@@ -392,7 +407,8 @@ void osd_messenger_t::read_requests()
|
||||
uint64_t client_id = read_ready_clients[i];
|
||||
auto cl_it = clients.find(client_id);
|
||||
if (cl_it == clients.end() || !cl_it->second || cl_it->second->read_msg.msg_iovlen ||
|
||||
cl_it->second->peer_state != PEER_CONNECTED)
|
||||
cl_it->second->peer_state == PEER_CONNECTING ||
|
||||
cl_it->second->peer_state == PEER_STOPPED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -473,6 +489,15 @@ void osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (cl->peer_state == PEER_RDMA_CONNECTING_IN || cl->peer_state == PEER_RDMA_CONNECTING_OUT)
|
||||
{
|
||||
// Data received over TCP, abort RDMA connection
|
||||
fprintf(stderr, "Data received for RDMA client %ju over TCP, falling back to TCP\n", cl->client_id);
|
||||
delete cl->rdma_conn;
|
||||
cl->rdma_conn = NULL;
|
||||
cl->peer_state = PEER_CONNECTED;
|
||||
assert(cl->peer_fd >= 0);
|
||||
}
|
||||
if (cl->peer_state == PEER_STOPPED)
|
||||
{
|
||||
if (cl->refs <= 0)
|
||||
@@ -593,7 +618,7 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size
|
||||
}
|
||||
curbuf += done;
|
||||
bufsize -= done;
|
||||
if (cl->hs->out_size())
|
||||
if (cl->hs->get_out().size())
|
||||
{
|
||||
if (cl->write_state == 0)
|
||||
{
|
||||
@@ -601,7 +626,7 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size
|
||||
write_ready_clients.push_back(cl->client_id);
|
||||
}
|
||||
}
|
||||
if (cl->hs->done() && !cl->hs->out_size())
|
||||
if (cl->hs->done() && !cl->hs->get_out().size())
|
||||
{
|
||||
// Delete hs when done and nothing to send
|
||||
delete cl->hs;
|
||||
@@ -853,7 +878,7 @@ bool osd_messenger_t::allocate_reply_buffers(osd_client_t *cl, osd_op_t *op)
|
||||
cl->client_id, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
|
||||
return false;
|
||||
}
|
||||
if (op->reply.hdr.retval >= 0 && bmp_len > 0)
|
||||
if (bmp_len > 0)
|
||||
{
|
||||
assert(op->bitmap);
|
||||
cl->read_op_size += bmp_len;
|
||||
@@ -921,7 +946,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr)
|
||||
{
|
||||
if (!rdr.read((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, RDR_GCM))
|
||||
return false;
|
||||
if (!rdr.read((uint8_t*)op->buf, op->req.sec_rw.len, cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0))
|
||||
if (!rdr.read((uint8_t*)op->buf, op->req.sec_rw.len, 0))
|
||||
return false;
|
||||
}
|
||||
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||
@@ -937,7 +962,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr)
|
||||
}
|
||||
else if (op->req.hdr.opcode == OSD_OP_WRITE)
|
||||
{
|
||||
if (!rdr.read((uint8_t*)op->buf, op->req.rw.len, cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0))
|
||||
if (!rdr.read((uint8_t*)op->buf, op->req.rw.len, 0))
|
||||
return false;
|
||||
}
|
||||
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
|
||||
@@ -961,7 +986,7 @@ switched_type:
|
||||
if (op->reply.hdr.retval > 0)
|
||||
{
|
||||
for (int i = 0; i < op->iov.count; i++)
|
||||
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0)))
|
||||
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, 0))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -975,7 +1000,7 @@ switched_type:
|
||||
if (op->reply.hdr.retval > 0)
|
||||
{
|
||||
for (int i = 0; i < op->iov.count; i++)
|
||||
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (op->enc ? RDR_XTS : 0) | (cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0)))
|
||||
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (op->enc ? RDR_XTS : 0)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+150
-109
@@ -6,7 +6,6 @@
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -15,8 +14,6 @@
|
||||
#define WR_XTS 2
|
||||
#define WR_NO_CSUM 4
|
||||
|
||||
#define GCM_TMP_BUF_SIZE 4096
|
||||
|
||||
class msgr_op_writer_t
|
||||
{
|
||||
public:
|
||||
@@ -182,11 +179,28 @@ public:
|
||||
from -= src_len;
|
||||
return true;
|
||||
}
|
||||
if (flags & WR_XTS)
|
||||
if (!(flags & WR_GCM))
|
||||
{
|
||||
msgr->op_encrypted_copy_buf(cl, curbuf, bufsize, src, src_len, from, done);
|
||||
if (flags & WR_XTS)
|
||||
{
|
||||
msgr->op_encrypted_copy_buf(cl, curbuf, bufsize, src, src_len, from, done);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n = src_len-from;
|
||||
if (n > bufsize-done)
|
||||
n = bufsize-done;
|
||||
if (!n)
|
||||
return false;
|
||||
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
|
||||
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||
memcpy(curbuf+done, src+from, n);
|
||||
done += n;
|
||||
cl->write_op_pos += n;
|
||||
from += n;
|
||||
}
|
||||
}
|
||||
else if (flags & WR_GCM)
|
||||
else
|
||||
{
|
||||
size_t n = src_len-from;
|
||||
if (n > bufsize-done)
|
||||
@@ -212,20 +226,6 @@ public:
|
||||
cl->write_op_pos += n;
|
||||
from += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n = src_len-from;
|
||||
if (n > bufsize-done)
|
||||
n = bufsize-done;
|
||||
if (!n)
|
||||
return false;
|
||||
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
|
||||
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||
memcpy(curbuf+done, src+from, n);
|
||||
done += n;
|
||||
cl->write_op_pos += n;
|
||||
from += n;
|
||||
}
|
||||
if (from < src_len)
|
||||
return false;
|
||||
from = 0;
|
||||
@@ -296,49 +296,63 @@ class get_op_writer_t: public msgr_op_writer_t
|
||||
osd_client_t* cl;
|
||||
size_t from;
|
||||
size_t done;
|
||||
size_t op_enc;
|
||||
size_t enc_size;
|
||||
size_t done_enc;
|
||||
uint8_t *enc_buf;
|
||||
|
||||
public:
|
||||
get_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t*, size_t):
|
||||
msgr(msgr), cl(cl), from(cl->write_op_pos), done(0), enc_size(0), done_enc(0), enc_buf(NULL)
|
||||
msgr(msgr), cl(cl), from(cl->write_op_pos), done(0), enc_size(0), done_enc(0)
|
||||
{
|
||||
}
|
||||
|
||||
static void ssl_extend_buf(osd_client_t *cl, size_t more = 0)
|
||||
{
|
||||
size_t min_cap = cl->ssl_out_buf_size*2;
|
||||
if (min_cap < cl->ssl_out_buf_size+more)
|
||||
min_cap = cl->ssl_out_buf_size+more;
|
||||
if (min_cap < 16384)
|
||||
min_cap = 16384;
|
||||
if (cl->ssl_out_buf_cap < min_cap)
|
||||
{
|
||||
uintptr_t old_buf = (uintptr_t)cl->ssl_out_buf;
|
||||
uintptr_t old_end = old_buf + cl->ssl_out_buf_cap;
|
||||
cl->ssl_out_buf = (uint8_t*)realloc_or_die(cl->ssl_out_buf, min_cap);
|
||||
cl->ssl_out_buf_cap = min_cap;
|
||||
for (auto & iov: cl->send_list)
|
||||
{
|
||||
if ((uintptr_t)iov.iov_base >= old_buf && (uintptr_t)iov.iov_base < old_end)
|
||||
iov.iov_base = cl->ssl_out_buf + ((uintptr_t)iov.iov_base - old_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void send_out_buf(osd_client_t *cl, size_t n)
|
||||
{
|
||||
if (cl->send_list.size() > 0)
|
||||
{
|
||||
iovec& last = cl->send_list.back();
|
||||
if (last.iov_base+last.iov_len == cl->ssl_out_buf+cl->ssl_out_buf_size)
|
||||
{
|
||||
last.iov_len += n;
|
||||
cl->ssl_out_buf_size += n;
|
||||
return;
|
||||
}
|
||||
}
|
||||
cl->send_list.push_back((iovec){ .iov_base = cl->ssl_out_buf+cl->ssl_out_buf_size, .iov_len = n });
|
||||
cl->ssl_out_buf_size += n;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
op_enc = 0;
|
||||
from = cl->write_op_pos;
|
||||
enc_size = 0;
|
||||
done_enc = 0;
|
||||
if (cl->gcm_enabled)
|
||||
{
|
||||
gcm_op_writer_t::init_ctx(msgr, cl);
|
||||
}
|
||||
}
|
||||
|
||||
void extend_tmp(size_t n)
|
||||
{
|
||||
if (!enc_buf || done_enc + n > enc_size)
|
||||
{
|
||||
enc_size = n < GCM_TMP_BUF_SIZE ? GCM_TMP_BUF_SIZE : n;
|
||||
enc_buf = (uint8_t*)malloc_or_die(enc_size);
|
||||
done_enc = 0;
|
||||
assert(!((size_t)enc_buf & 7));
|
||||
cl->send_free_ops.push_back((osd_op_t*)((size_t)enc_buf | 1));
|
||||
}
|
||||
}
|
||||
|
||||
void send_tmp(size_t n)
|
||||
{
|
||||
if (cl->send_list.size() && cl->send_list.back().iov_base == (enc_buf + done_enc))
|
||||
cl->send_list.back().iov_len += n;
|
||||
else
|
||||
cl->send_list.push_back((iovec){ .iov_base = enc_buf + done_enc, .iov_len = n });
|
||||
done += n;
|
||||
done_enc += n;
|
||||
}
|
||||
|
||||
bool write(uint8_t *src, size_t src_len, int flags) override
|
||||
{
|
||||
if (from >= src_len)
|
||||
@@ -347,54 +361,63 @@ public:
|
||||
from -= src_len;
|
||||
return true;
|
||||
}
|
||||
if (cl->send_list.size() >= IOV_MAX-1)
|
||||
if (cl->send_list.size() >= IOV_MAX)
|
||||
{
|
||||
// Make sure tag always fits
|
||||
return false;
|
||||
}
|
||||
if (flags & WR_GCM)
|
||||
{
|
||||
if (cl->gcm_enabled)
|
||||
{
|
||||
// Encrypt data to client's temporary output buffer (all at once)
|
||||
size_t n = src_len-from;
|
||||
ssl_extend_buf(cl, n);
|
||||
#ifdef WITH_ISAL_CRYPTO
|
||||
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, cl->ssl_out_buf+cl->ssl_out_buf_size, src+from, n);
|
||||
assert(!r);
|
||||
#else
|
||||
int actual_out;
|
||||
if (EVP_EncryptUpdate(cl->enc_ctx, cl->ssl_out_buf+cl->ssl_out_buf_size, &actual_out, src+from, n) != 1)
|
||||
{
|
||||
fprintf(stderr, "EncryptUpdate error: ");
|
||||
ERR_print_errors_fp(stderr);
|
||||
abort();
|
||||
}
|
||||
assert(actual_out == n);
|
||||
#endif
|
||||
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
|
||||
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||
send_out_buf(cl, n);
|
||||
done += n;
|
||||
cl->write_op_pos += n;
|
||||
from += n;
|
||||
if (from < src_len)
|
||||
return false;
|
||||
from = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (flags & WR_XTS)
|
||||
{
|
||||
// Allocate a temporary buffer and encrypt data to it
|
||||
if (!op_enc)
|
||||
if (!cl->write_op->enc_buf)
|
||||
{
|
||||
if (cl->send_list.size() >= IOV_MAX-1)
|
||||
{
|
||||
// Make sure that 1 encrypted buffer and 1 checksum fits
|
||||
return false;
|
||||
}
|
||||
// No way except than to allocate a temporary buffer and encrypt data to it
|
||||
assert(cl->write_op->req.hdr.opcode == OSD_OP_WRITE);
|
||||
op_enc = cl->write_op->req.rw.len - from + (from % 16);
|
||||
assert(op_enc > 0);
|
||||
extend_tmp(op_enc);
|
||||
enc_size = cl->write_op->req.rw.len - from + (from % 16);
|
||||
assert(enc_size > 0);
|
||||
cl->write_op->enc_buf = (uint8_t*)malloc_or_die(enc_size);
|
||||
cl->send_list.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = enc_size });
|
||||
done += enc_size;
|
||||
}
|
||||
size_t new_done = done_enc;
|
||||
msgr->op_encrypted_copy_buf(cl, enc_buf, enc_size, src, src_len, from, new_done);
|
||||
send_tmp(new_done-done_enc);
|
||||
assert(enc_size > 0);
|
||||
msgr->op_encrypted_copy_buf(cl, cl->write_op->enc_buf, enc_size, src, src_len, from, done_enc);
|
||||
assert(from == src_len);
|
||||
}
|
||||
else if ((flags & WR_GCM) && cl->gcm_enabled)
|
||||
{
|
||||
// Allocate a temporary buffer and encrypt data to it
|
||||
size_t n = src_len-from;
|
||||
extend_tmp(n);
|
||||
#ifdef WITH_ISAL_CRYPTO
|
||||
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, enc_buf+done_enc, src+from, n);
|
||||
assert(!r);
|
||||
#else
|
||||
int actual_out;
|
||||
if (EVP_EncryptUpdate(cl->enc_ctx, enc_buf+done_enc, &actual_out, src+from, n) != 1)
|
||||
{
|
||||
fprintf(stderr, "EncryptUpdate error: ");
|
||||
ERR_print_errors_fp(stderr);
|
||||
abort();
|
||||
}
|
||||
assert(actual_out == n);
|
||||
#endif
|
||||
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
|
||||
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||
send_tmp(n);
|
||||
cl->write_op_pos += n;
|
||||
from += n;
|
||||
if (from < src_len)
|
||||
return false;
|
||||
from = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
|
||||
@@ -411,10 +434,13 @@ public:
|
||||
{
|
||||
if (cl->enc_ctx)
|
||||
{
|
||||
if (cl->send_list.size() >= IOV_MAX)
|
||||
return false;
|
||||
// Tag is 16 bytes
|
||||
extend_tmp(16);
|
||||
gcm_op_writer_t::write_tag_to(msgr, cl, enc_buf + done_enc);
|
||||
send_tmp(16);
|
||||
ssl_extend_buf(cl, 16);
|
||||
gcm_op_writer_t::write_tag_to(msgr, cl, cl->ssl_out_buf+cl->ssl_out_buf_size);
|
||||
send_out_buf(cl, 16);
|
||||
done += 16;
|
||||
gcm_op_writer_t::free_ctx(msgr, cl);
|
||||
}
|
||||
return true;
|
||||
@@ -461,7 +487,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
||||
}
|
||||
cl->write_ops.push_back(cur_op);
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->peer_state == PEER_RDMA)
|
||||
if (cl->peer_state == PEER_RDMA || cl->peer_state == PEER_RDMA_CONNECTING_OUT)
|
||||
{
|
||||
try_send_rdma(cl);
|
||||
return;
|
||||
@@ -549,15 +575,14 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
|
||||
if (cl->hs)
|
||||
{
|
||||
// Send handshake message
|
||||
if (cl->hs->out_size())
|
||||
if (cl->hs->get_out().size())
|
||||
{
|
||||
uint8_t *out = cl->hs->get_out();
|
||||
cl->send_list.push_back((iovec){ .iov_base = out, .iov_len = cl->hs->out_size() });
|
||||
assert(!((size_t)out & 7));
|
||||
cl->send_free_ops.push_back((osd_op_t*)((size_t)out | 1));
|
||||
cl->hs->reset_out();
|
||||
get_op_writer_t::ssl_extend_buf(cl, cl->hs->get_out().size());
|
||||
memcpy(cl->ssl_out_buf+cl->ssl_out_buf_size, cl->hs->get_out().data(), cl->hs->get_out().size());
|
||||
get_op_writer_t::send_out_buf(cl, cl->hs->get_out().size());
|
||||
cl->hs->get_out().clear();
|
||||
}
|
||||
if (!cl->hs->out_size() && cl->hs->done())
|
||||
if (!cl->hs->get_out().size() && cl->hs->done())
|
||||
{
|
||||
delete cl->hs;
|
||||
cl->hs = NULL;
|
||||
@@ -644,13 +669,13 @@ size_t osd_messenger_t::copy_ops_to(osd_client_t *cl, uint8_t *dst, size_t dst_l
|
||||
{
|
||||
// Send handshake message
|
||||
size_t n = 0;
|
||||
if (cl->hs->out_size())
|
||||
if (cl->hs->get_out().size())
|
||||
{
|
||||
n = cl->hs->out_size() < dst_len ? cl->hs->out_size() : dst_len;
|
||||
memcpy(dst, cl->hs->get_out(), n);
|
||||
cl->hs->eat_out(n);
|
||||
n = cl->hs->get_out().size() < dst_len ? cl->hs->get_out().size() : dst_len;
|
||||
memcpy(dst, cl->hs->get_out().data(), n);
|
||||
cl->hs->get_out().erase(cl->hs->get_out().begin(), cl->hs->get_out().begin() + n);
|
||||
}
|
||||
if (!cl->hs->out_size() && cl->hs->done())
|
||||
if (!cl->hs->get_out().size() && cl->hs->done())
|
||||
{
|
||||
delete cl->hs;
|
||||
cl->hs = NULL;
|
||||
@@ -773,14 +798,24 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
||||
for (auto op: cl->send_free_ops)
|
||||
{
|
||||
if (more)
|
||||
{
|
||||
assert(!((size_t)op & 7));
|
||||
cl->zc_free_list.push_back(op);
|
||||
else if (!((size_t)op & 7))
|
||||
delete op;
|
||||
}
|
||||
else
|
||||
free((void*)((size_t)op & ~(size_t)7));
|
||||
delete op;
|
||||
}
|
||||
if (more)
|
||||
{
|
||||
if (cl->ssl_out_buf_size)
|
||||
{
|
||||
cl->zc_free_list.push_back((osd_op_t*)((size_t)cl->ssl_out_buf | 1));
|
||||
cl->ssl_out_buf = NULL;
|
||||
cl->ssl_out_buf_cap = 0;
|
||||
}
|
||||
cl->zc_free_list.push_back(NULL); // end marker
|
||||
}
|
||||
cl->ssl_out_buf_size = 0;
|
||||
cl->send_free_ops.clear();
|
||||
cl->write_state = 0;
|
||||
if (cl->write_op || cl->write_ops.size())
|
||||
@@ -791,14 +826,20 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
||||
cl->proto_csum_status = cl->proto_csum_status & (~MSGR_CSUM_NEG);
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->rdma_conn && !cl->write_op && !cl->write_ops.size() && cl->peer_state == PEER_RDMA_CONNECTING)
|
||||
if (cl->rdma_conn && cl->peer_state == PEER_RDMA_CONNECTING_IN && !cl->write_op && !cl->write_ops.size())
|
||||
{
|
||||
// FIXME: Ignore pings during RDMA state transition
|
||||
if (log_level > 0)
|
||||
if (cl->rdma_close_tcp)
|
||||
{
|
||||
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id);
|
||||
cl->peer_state = PEER_RDMA_CONNECTING_OUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log_level > 0)
|
||||
{
|
||||
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id);
|
||||
}
|
||||
cl->peer_state = PEER_RDMA;
|
||||
}
|
||||
cl->peer_state = PEER_RDMA;
|
||||
// Add the initial receive request
|
||||
init_recv_rdma(cl);
|
||||
}
|
||||
@@ -888,7 +929,7 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr)
|
||||
for (int i = 0; i < cl->write_op->iov.count; i++)
|
||||
{
|
||||
auto & iov = cl->write_op->iov.buf[i];
|
||||
if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, (op->enc ? WR_XTS : 0) | (cl->proto_csum_status == MSGR_CSUM_GCM ? WR_GCM : 0)))
|
||||
if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, (op->enc ? WR_XTS : 0)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-11
@@ -6,6 +6,9 @@
|
||||
|
||||
#include "messenger.h"
|
||||
#include "../util/xxh_x86dispatch.h"
|
||||
#ifdef WITH_RDMA
|
||||
#include "msgr_rdma.h"
|
||||
#endif
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
@@ -108,11 +111,23 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
|
||||
osd_peers.erase(osd_it);
|
||||
}
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->rdma_conn && cl->rdma_conn->cmid)
|
||||
{
|
||||
auto rdma_it = rdmacm_connections.find(cl->rdma_conn->cmid);
|
||||
if (rdma_it != rdmacm_connections.end() && rdma_it->second == cl)
|
||||
{
|
||||
rdmacm_connections.erase(rdma_it);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifndef __MOCK__
|
||||
if (cl->connect_timeout_id >= 0)
|
||||
{
|
||||
tfd->clear_timer(cl->connect_timeout_id);
|
||||
cl->connect_timeout_id = -1;
|
||||
}
|
||||
#endif
|
||||
if (cl->in_osd_num && break_pg_locks)
|
||||
{
|
||||
// Break PG locks
|
||||
@@ -147,7 +162,9 @@ void osd_messenger_t::destroy_client(osd_client_t *cl)
|
||||
clients.erase(cl->client_id);
|
||||
if (cl->peer_fd >= 0)
|
||||
{
|
||||
#ifndef __MOCK__
|
||||
tfd->set_fd_handler(cl->peer_fd, false, NULL);
|
||||
#endif
|
||||
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
|
||||
{
|
||||
if (*rit == cl->client_id)
|
||||
@@ -166,13 +183,6 @@ void osd_messenger_t::destroy_client(osd_client_t *cl)
|
||||
}
|
||||
clients_by_fd.erase(cl->peer_fd);
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->rdma_conn)
|
||||
{
|
||||
destroy_rdma_conn(cl->rdma_conn);
|
||||
cl->rdma_conn = NULL;
|
||||
}
|
||||
#endif
|
||||
delete cl;
|
||||
}
|
||||
|
||||
@@ -202,10 +212,7 @@ osd_client_t::~osd_client_t()
|
||||
{
|
||||
if (op)
|
||||
{
|
||||
if (!((size_t)op & 7))
|
||||
delete op;
|
||||
else
|
||||
free((void*)((size_t)op & ~(size_t)7));
|
||||
delete op;
|
||||
}
|
||||
}
|
||||
for (osd_op_t *op: zc_free_list)
|
||||
@@ -218,6 +225,15 @@ osd_client_t::~osd_client_t()
|
||||
free((void*)((size_t)op & ~(size_t)7));
|
||||
}
|
||||
}
|
||||
#ifndef __MOCK__
|
||||
#ifdef WITH_RDMA
|
||||
if (rdma_conn)
|
||||
{
|
||||
delete rdma_conn;
|
||||
rdma_conn = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
if (read_csum_state)
|
||||
{
|
||||
XXH3_freeState(read_csum_state);
|
||||
@@ -246,6 +262,11 @@ osd_client_t::~osd_client_t()
|
||||
#endif
|
||||
dec_ctx = NULL;
|
||||
}
|
||||
if (ssl_out_buf)
|
||||
{
|
||||
free(ssl_out_buf);
|
||||
ssl_out_buf = NULL;
|
||||
}
|
||||
if (hs)
|
||||
{
|
||||
delete hs;
|
||||
|
||||
@@ -1049,7 +1049,7 @@ static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
|
||||
{
|
||||
// Get larger allocated extents, possibly with false positives
|
||||
uint64_t bmp_pos = (offset-task.offset) / task.bitmap_granularity;
|
||||
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity;
|
||||
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity - bmp_pos;
|
||||
while (bmp_pos < bmp_end)
|
||||
{
|
||||
if (!(bmp_pos & 7) && bmp_end >= bmp_pos+8)
|
||||
|
||||
@@ -282,7 +282,7 @@ help:
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
const bool writeback = !cli->get_immediate_commit(inode);
|
||||
const bool writeback = cli->get_immediate_commit(inode);
|
||||
auto pool_it = cli->st_cli.pool_config.find(INODE_POOL(inode ? inode : watch->cfg.num));
|
||||
if (pool_it == cli->st_cli.pool_config.end())
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: Vitastor
|
||||
Description: Vitastor client library
|
||||
Version: 3.0.12
|
||||
Version: 3.0.10
|
||||
Libs: -L${libdir} -lvitastor_client
|
||||
Cflags: -I${includedir}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ add_library(vitastor_cli STATIC
|
||||
cli_pool_ls.cpp
|
||||
cli_pool_modify.cpp
|
||||
cli_pool_rm.cpp
|
||||
cli_raw_ls.cpp
|
||||
cli_serve.cpp
|
||||
cli_status.cpp
|
||||
cli_user_ls.cpp
|
||||
|
||||
@@ -144,11 +144,6 @@ static const char* help_text =
|
||||
" --min-offset, --max-offset\n"
|
||||
" Restrict listing to specific offsets inside inodes.\n"
|
||||
"\n"
|
||||
"vitastor-cli raw-ls [OPTIONS]\n"
|
||||
" Find object(s) in the cluster using raw secondary listing operations. Options:\n"
|
||||
" [--min_inode NUM] [--max_inode NUM] [--offset NUM] [--pg_num NUM] [--pg_count COUNT]\n"
|
||||
" [--pg_stripe_size NUM] [--osds 1,2,3,...]\n"
|
||||
"\n"
|
||||
"vitastor-cli fix [--objects <objects>] [--bad-osds <osds>] [--part <part>] [--check no]\n"
|
||||
" Fix inconsistent objects in the cluster by deleting some copies.\n"
|
||||
" --objects <objects>\n"
|
||||
@@ -514,11 +509,6 @@ std::function<bool(cli_result_t &)> cli_tool_t::start(json11::Json::object cfg,
|
||||
// Describe unclean objects
|
||||
action_cb = start_describe(cfg);
|
||||
}
|
||||
else if (cmd[0] == "raw-ls")
|
||||
{
|
||||
// Run raw listings
|
||||
action_cb = start_raw_ls(cfg);
|
||||
}
|
||||
else if (cmd[0] == "fix")
|
||||
{
|
||||
// Fix inconsistent objects (by deleting some copies)
|
||||
|
||||
@@ -73,7 +73,6 @@ public:
|
||||
std::function<bool(cli_result_t &)> start_fix(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_ls(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_raw_ls(json11::Json cfg);
|
||||
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
|
||||
|
||||
@@ -95,11 +95,7 @@ struct image_changer_t
|
||||
(!set_readonly || cfg.readonly) &&
|
||||
(!set_deleted || cfg.deleted == new_deleted) &&
|
||||
(!new_size && !force_size || cfg.size == new_size || cfg.size >= new_size && inc_size) &&
|
||||
(new_name == "" || new_name == image_name) &&
|
||||
(!new_owner.is_string() || new_owner.string_value() == cfg.owner) &&
|
||||
(!new_owner_group.is_string() || new_owner_group.string_value() == cfg.owner_group) &&
|
||||
(!new_reader_group.is_string() || new_reader_group.string_value() == cfg.reader_group) &&
|
||||
!set_key)
|
||||
(new_name == "" || new_name == image_name))
|
||||
{
|
||||
result = (cli_result_t){ .err = 0, .text = "No change", .data = parent->format_image(cfg) };
|
||||
state = 100;
|
||||
|
||||
@@ -31,7 +31,7 @@ struct cli_modify_user_t
|
||||
goto resume_1;
|
||||
else if (state == 2)
|
||||
goto resume_2;
|
||||
if (user_type != "client" && user_type != "admin")
|
||||
if (user_type != "client" && user_type != "admin" && user_type != "mon" && user_type != "osd")
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Unknown user type: "+user_type };
|
||||
state = 100;
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "cli_fix.h"
|
||||
#include "cluster_client.h"
|
||||
#include "pg_states.h"
|
||||
#include "str_util.h"
|
||||
#include "json_util.h"
|
||||
|
||||
std::vector<uint64_t> parse_uint64_list(json11::Json val);
|
||||
|
||||
// Find object(s) in the cluster using raw secondary listing operations.
|
||||
// Only for troubleshooting.
|
||||
struct cli_raw_ls_t
|
||||
{
|
||||
json11::Json cfg;
|
||||
pool_id_t pool_id = 0;
|
||||
pg_num_t pg_num = 0;
|
||||
pg_num_t pg_count = 0;
|
||||
uint32_t pg_stripe_size = 0;
|
||||
std::set<uint64_t> osds;
|
||||
std::vector<osd_num_t> osd_list;
|
||||
uint64_t min_inode = 0, max_inode = 0, min_offset = 0, max_offset = 0;
|
||||
bool offset_set = false;
|
||||
|
||||
cli_tool_t *parent = NULL;
|
||||
int state = 0;
|
||||
cli_result_t result;
|
||||
bool first = true;
|
||||
size_t osd_pos = 0;
|
||||
|
||||
bool is_done()
|
||||
{
|
||||
return state == 100;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (state == 1)
|
||||
goto resume_1;
|
||||
if (state == 100)
|
||||
return;
|
||||
pool_id = cfg["pool_id"].uint64_value();
|
||||
pg_num = (pg_num_t)cfg["pg_num"].uint64_value();
|
||||
pg_count = (pg_num_t)cfg["pg_count"].uint64_value();
|
||||
pg_stripe_size = cfg["pg_stripe_size"].uint64_value();
|
||||
if (!pool_id)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Pool ID is required" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (cfg["min_inode"].is_null() && cfg["max_inode"].is_null())
|
||||
{
|
||||
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), 0);
|
||||
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), UINT64_MAX);
|
||||
}
|
||||
else
|
||||
{
|
||||
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["min_inode"].as_string()));
|
||||
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["max_inode"].as_string()));
|
||||
}
|
||||
if (cfg["offset"].is_null())
|
||||
{
|
||||
if (!pg_num)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "PG number is required if offset is not specified" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
min_offset = 0;
|
||||
max_offset = UINT64_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_offset = max_offset = stoull_full(cfg["offset"].as_string());
|
||||
offset_set = true;
|
||||
}
|
||||
for (auto osd_num: parse_uint64_list(cfg["osds"]))
|
||||
{
|
||||
osds.insert(osd_num);
|
||||
}
|
||||
if (!pg_count || !pg_stripe_size || !osds.size())
|
||||
{
|
||||
auto pool_it = parent->cli->st_cli.pool_config.find(pool_id);
|
||||
if (pool_it == parent->cli->st_cli.pool_config.end())
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "pg_count, pg_stripe_size and osds are required if the pool does not exist" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (!pg_count)
|
||||
{
|
||||
pg_count = pool_it->second.real_pg_count;
|
||||
}
|
||||
if (!pg_stripe_size)
|
||||
{
|
||||
pg_stripe_size = pool_it->second.pg_stripe_size;
|
||||
}
|
||||
if (!osds.size())
|
||||
{
|
||||
for (auto & pgp: pool_it->second.pg_config)
|
||||
{
|
||||
for (osd_num_t pg_osd: pgp.second.target_set)
|
||||
osds.insert(pg_osd);
|
||||
for (osd_num_t pg_osd: pgp.second.all_peers)
|
||||
osds.insert(pg_osd);
|
||||
for (auto & hist_item: pgp.second.target_history)
|
||||
for (auto pg_osd: hist_item)
|
||||
osds.insert(pg_osd);
|
||||
}
|
||||
osds.erase(0);
|
||||
}
|
||||
}
|
||||
if (offset_set)
|
||||
{
|
||||
min_offset = (min_offset / pg_stripe_size) * pg_stripe_size;
|
||||
max_offset = ((max_offset / pg_stripe_size) * pg_stripe_size) | STRIPE_MASK;
|
||||
pg_num = (min_offset/pg_stripe_size) % pg_count + 1; // like map_to_pg()
|
||||
fprintf(stderr, "Selected PG %u\n", pg_num);
|
||||
}
|
||||
parent->cli->init_msgr();
|
||||
osd_list = std::vector<osd_num_t>(osds.begin(), osds.end());
|
||||
osd_pos = 0;
|
||||
state = 1;
|
||||
resume_1:
|
||||
for (; osd_pos < osd_list.size() && parent->waiting < parent->parallel_osds; osd_pos++)
|
||||
{
|
||||
uint64_t osd_num = osd_list[osd_pos];
|
||||
if (parent->cli->st_cli.peer_states[osd_num].is_null())
|
||||
{
|
||||
fprintf(stderr, "OSD %ju is unavailable, skipping\n", osd_num);
|
||||
continue;
|
||||
}
|
||||
osd_op_t *op = new osd_op_t;
|
||||
op->req = (osd_any_op_t){
|
||||
.sec_list = {
|
||||
.header = {
|
||||
.magic = SECONDARY_OSD_OP_MAGIC,
|
||||
.opcode = OSD_OP_SEC_LIST,
|
||||
},
|
||||
.list_pg = pg_num,
|
||||
.pg_count = pg_count,
|
||||
.pg_stripe_size = pg_stripe_size,
|
||||
.min_inode = min_inode,
|
||||
.max_inode = max_inode,
|
||||
.min_stripe = min_offset,
|
||||
.max_stripe = max_offset,
|
||||
},
|
||||
};
|
||||
op->callback = [this, osd_num](osd_op_t *op)
|
||||
{
|
||||
if (op->reply.hdr.retval < 0)
|
||||
{
|
||||
fprintf(stderr, "OSD %ju listing failed: retval=%jd\n", osd_num, op->reply.hdr.retval);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint64_t i = 0; i < op->reply.hdr.retval; i++)
|
||||
{
|
||||
auto & ov = ((obj_ver_id*)op->buf)[i];
|
||||
if (parent->json_output)
|
||||
{
|
||||
printf("%s{\"osd\":%ju,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"version\":%ju,\"stable\":%s}",
|
||||
first ? "" : ",\n", osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
|
||||
i < op->reply.sec_list.stable_count ? "true" : "false");
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OSD %ju - %jx:%jx v%ju%s\n",
|
||||
osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
|
||||
i < op->reply.sec_list.stable_count ? " stable" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
parent->waiting--;
|
||||
loop();
|
||||
delete op;
|
||||
};
|
||||
parent->waiting++;
|
||||
parent->cli->execute_raw(osd_num, op);
|
||||
}
|
||||
if (parent->waiting > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!first)
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
state = 100;
|
||||
}
|
||||
};
|
||||
|
||||
std::function<bool(cli_result_t &)> cli_tool_t::start_raw_ls(json11::Json cfg)
|
||||
{
|
||||
auto raw_ls = new cli_raw_ls_t();
|
||||
raw_ls->parent = this;
|
||||
raw_ls->cfg = cfg;
|
||||
return [raw_ls](cli_result_t & result)
|
||||
{
|
||||
raw_ls->loop();
|
||||
if (raw_ls->is_done())
|
||||
{
|
||||
result = raw_ls->result;
|
||||
delete raw_ls;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -946,7 +946,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string", "description": "User name" },
|
||||
"type": { "type": "string", "enum": [ "admin", "client" ], "description": "User type" },
|
||||
"type": { "type": "string", "enum": [ "osd", "mon", "admin", "client" ], "description": "User type" },
|
||||
"groups": { "type": "array", "items": { "type": "string" }, "description": "User group names" }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ add_executable(vitastor-disk
|
||||
../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp
|
||||
)
|
||||
target_link_libraries(vitastor-disk
|
||||
tcmalloc_minimal
|
||||
${LIBURING_LIBRARIES}
|
||||
${ISAL_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -427,9 +427,6 @@ int main(int argc, char *argv[])
|
||||
self.dsk.open_journal();
|
||||
self.dsk.calc_lengths();
|
||||
self.dsk.close_all();
|
||||
self.new_meta_device = self.dsk.meta_device;
|
||||
self.new_meta_offset = self.dsk.meta_offset;
|
||||
self.new_meta_len = self.dsk.meta_area_size;
|
||||
}
|
||||
std::string json_err;
|
||||
json11::Json meta = json11::Json::parse(read_all_fd(0), json_err);
|
||||
|
||||
@@ -52,6 +52,7 @@ struct disk_tool_t
|
||||
bool all = false, json = false, now = false;
|
||||
bool dump_with_blocks = false, dump_with_data = false;
|
||||
bool dump_as_old = false;
|
||||
bool skip_obsolete = false;
|
||||
int log_level = 1;
|
||||
double meta_reserve_multiple = 2;
|
||||
uint64_t meta_reserve_min_size = (uint64_t)1024*1024*1024;
|
||||
@@ -137,6 +138,8 @@ struct disk_tool_t
|
||||
int resize_write_new_journal();
|
||||
void remap_big_write(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||
void remap_small_write(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||
void fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr);
|
||||
void fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||
int resize_rebuild_meta();
|
||||
int resize_write_new_meta();
|
||||
void free_new_meta();
|
||||
|
||||
@@ -590,7 +590,6 @@ int disk_tool_t::write_json_meta(json11::Json meta)
|
||||
|
||||
int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
|
||||
{
|
||||
assert(new_meta_len >= sizeof(blockstore_meta_header_v3_t));
|
||||
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
|
||||
memset(new_meta_buf, 0, new_meta_len);
|
||||
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
|
||||
@@ -608,9 +607,9 @@ int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
|
||||
: (meta["data_csum_type"].string_value() == "crc32c"
|
||||
? BLOCKSTORE_CSUM_CRC32C
|
||||
: BLOCKSTORE_CSUM_NONE);
|
||||
new_meta_hdr->meta_area_size = new_meta_len;
|
||||
new_meta_hdr->csum_block_size = meta["csum_block_size"].uint64_value();
|
||||
new_meta_hdr->header_csum = crc32c(0, new_meta_hdr, sizeof(blockstore_meta_header_v3_t));
|
||||
new_meta_hdr->meta_area_size = new_meta_len;
|
||||
new_clean_entry_bitmap_size = (new_meta_hdr->data_block_size / new_meta_hdr->bitmap_granularity + 7) / 8;
|
||||
new_clean_entry_size = 0;
|
||||
new_entries_per_block = 0;
|
||||
|
||||
@@ -111,7 +111,7 @@ int disk_tool_t::raw_resize()
|
||||
fprintf(stderr, "Done\n");
|
||||
ret:
|
||||
free_new_meta();
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int disk_tool_t::resize_parse_params()
|
||||
@@ -154,6 +154,9 @@ int disk_tool_t::resize_parse_params()
|
||||
? parse_size(options["new_journal_offset"]) : dsk.journal_offset;
|
||||
new_journal_len = options.find("new_journal_len") != options.end()
|
||||
? parse_size(options["new_journal_len"]) : dsk.journal_len;
|
||||
new_meta_format = options.find("new_meta_format") != options.end()
|
||||
? stoull_full(options["new_meta_format"]) : 0;
|
||||
skip_obsolete = options.find("skip_obsolete") != options.end();
|
||||
if (new_data_len+new_data_offset > dsk.data_device_size)
|
||||
new_data_len = dsk.data_device_size-new_data_offset;
|
||||
if (new_meta_device == dsk.data_device && new_data_offset < new_meta_offset &&
|
||||
@@ -202,7 +205,10 @@ void disk_tool_t::resize_init(blockstore_meta_header_v3_t *hdr)
|
||||
{
|
||||
dsk.meta_format = hdr->version;
|
||||
}
|
||||
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
|
||||
if (new_meta_format == 0)
|
||||
{
|
||||
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
|
||||
}
|
||||
dsk.calc_lengths();
|
||||
if (((new_data_offset-dsk.data_offset) % dsk.data_block_size))
|
||||
{
|
||||
@@ -557,6 +563,66 @@ void disk_tool_t::remap_small_write(blockstore_heap_t *heap, heap_entry_t *wr)
|
||||
}
|
||||
}
|
||||
|
||||
void disk_tool_t::fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr)
|
||||
{
|
||||
uint64_t block_num = big_wr->big().block_num;
|
||||
clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size +
|
||||
dsk.meta_block_size*(block_num / new_entries_per_block) +
|
||||
new_clean_entry_size*(block_num % new_entries_per_block));
|
||||
new_entry->oid = (object_id){ .inode = big_wr->inode, .stripe = big_wr->stripe };
|
||||
new_entry->version = big_wr->version;
|
||||
memcpy(new_entry->bitmap, big_wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
memcpy(new_entry->bitmap + new_clean_entry_bitmap_size, big_wr->get_int_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
memcpy(new_entry->bitmap + 2*new_clean_entry_bitmap_size, big_wr->get_checksums(heap), new_data_csum_size);
|
||||
uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4);
|
||||
*new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4);
|
||||
}
|
||||
|
||||
void disk_tool_t::fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr)
|
||||
{
|
||||
assert(wr->type() == BS_HEAP_SMALL_WRITE ||
|
||||
wr->type() == BS_HEAP_BIG_WRITE ||
|
||||
wr->type() == BS_HEAP_BIG_INTENT);
|
||||
uint32_t je_size = ((wr->entry_type & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE
|
||||
? sizeof(journal_entry_small_write) + dsk.dirty_dyn_size(wr->small().offset, wr->small().len)
|
||||
: sizeof(journal_entry_big_write) + dsk.dirty_dyn_size(0, dsk.data_block_size));
|
||||
choose_journal_block(je_size);
|
||||
journal_entry *je = (journal_entry*)(new_journal_ptr + new_journal_in_pos);
|
||||
je->magic = JOURNAL_MAGIC;
|
||||
je->type = (wr->entry_type & BS_HEAP_STABLE) ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE;
|
||||
je->size = je_size;
|
||||
je->crc32_prev = new_crc32_prev;
|
||||
je->small_write.oid = (object_id){ .inode = wr->inode, .stripe = wr->stripe };
|
||||
je->small_write.version = wr->version;
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
je->small_write.offset = wr->small().offset;
|
||||
je->small_write.len = wr->small().len;
|
||||
je->small_write.data_offset = new_journal_data-new_journal_buf;
|
||||
if (je->small_write.data_offset + je->small_write.len > new_journal_len)
|
||||
{
|
||||
fprintf(stderr, "Error: live entries don't fit to the new journal\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(new_journal_data, buffer_area+wr->small().location, je->small_write.len);
|
||||
new_journal_data += je->small_write.len;
|
||||
if (dsk.data_csum_type == 0 && wr->get_checksum(heap))
|
||||
je->small_write.crc32_data = *wr->get_checksum(heap);
|
||||
}
|
||||
else
|
||||
{
|
||||
je->big_write.location = wr->big_location(heap);
|
||||
}
|
||||
memcpy((uint8_t*)je + je->size, wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
if (dsk.data_csum_type != 0 && wr->get_checksums(heap))
|
||||
{
|
||||
memcpy((uint8_t*)je + je->size + new_clean_entry_bitmap_size, wr->get_checksums(heap), heap->get_csum_size(wr));
|
||||
}
|
||||
je->crc32 = je_crc32(je);
|
||||
new_journal_in_pos += je->size;
|
||||
new_crc32_prev = je->crc32;
|
||||
}
|
||||
|
||||
int disk_tool_t::resize_rebuild_meta()
|
||||
{
|
||||
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
|
||||
@@ -564,12 +630,13 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
|
||||
uint64_t new_meta_pos = dsk.meta_block_size;
|
||||
uint64_t next_lsn = 0;
|
||||
std::vector<heap_entry_t*> writes;
|
||||
int r = process_meta(
|
||||
[&](blockstore_meta_header_v3_t *hdr)
|
||||
{
|
||||
new_meta_hdr->zero = 0;
|
||||
new_meta_hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
||||
new_meta_hdr->version = new_meta_format;
|
||||
new_meta_hdr->version = new_meta_format == 0 ? BLOCKSTORE_META_FORMAT_HEAP : new_meta_format;
|
||||
new_meta_hdr->meta_block_size = dsk.meta_block_size;
|
||||
new_meta_hdr->data_block_size = dsk.data_block_size;
|
||||
new_meta_hdr->bitmap_granularity = dsk.bitmap_granularity ? dsk.bitmap_granularity : 4096;
|
||||
@@ -587,16 +654,18 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
},
|
||||
[&](blockstore_heap_t *heap, heap_entry_t *obj, uint32_t meta_block_num)
|
||||
{
|
||||
assert(new_meta_format == BLOCKSTORE_META_FORMAT_HEAP);
|
||||
if (!obj)
|
||||
{
|
||||
// Finish
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
while (new_meta_pos < new_meta_len)
|
||||
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos += dsk.meta_block_size;
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
while (new_meta_pos < new_meta_len)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos += dsk.meta_block_size;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -610,24 +679,72 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
{
|
||||
remap_small_write(heap, wr);
|
||||
}
|
||||
// New -> New
|
||||
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
|
||||
else if (wr->type() != BS_HEAP_DELETE && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
if (new_meta_pos >= new_meta_len)
|
||||
fprintf(stderr, "Object %jx:%jx can't be converted to the old format because it contains an entry of type 0x%x%s\n",
|
||||
wr->inode, wr->stripe, wr->entry_type,
|
||||
(wr->type() == BS_HEAP_INTENT_WRITE ? " (intent_write)" : ""));
|
||||
exit(1);
|
||||
}
|
||||
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
||||
{
|
||||
// New -> New
|
||||
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
|
||||
{
|
||||
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
|
||||
exit(1);
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
if (new_meta_pos >= new_meta_len)
|
||||
{
|
||||
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
|
||||
new_meta_pos += wr->size;
|
||||
if (skip_obsolete && wr->type() == BS_HEAP_BIG_WRITE && stable)
|
||||
{
|
||||
// Skip older writes
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// New -> Old
|
||||
if (wr->type() == BS_HEAP_DELETE && stable)
|
||||
{
|
||||
// Object is deleted, skip it
|
||||
return false;
|
||||
}
|
||||
if (wr->type() == BS_HEAP_BIG_WRITE && stable)
|
||||
{
|
||||
fill_old_clean_entry(heap, wr);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
writes.push_back(wr);
|
||||
}
|
||||
}
|
||||
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
|
||||
new_meta_pos += wr->size;
|
||||
return true;
|
||||
};
|
||||
for (auto wr = obj; wr; wr = heap->prev(wr))
|
||||
if (new_meta_format != BLOCKSTORE_META_FORMAT_HEAP || skip_obsolete)
|
||||
{
|
||||
handle_write(wr, false);
|
||||
heap->iterate_with_stable(obj, obj->lsn, handle_write);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto wr = obj; wr; wr = heap->prev(wr))
|
||||
{
|
||||
handle_write(wr, false);
|
||||
}
|
||||
}
|
||||
if (writes.size())
|
||||
{
|
||||
for (size_t i = writes.size(); i > 0; i--)
|
||||
{
|
||||
fill_old_journal_entry(heap, writes[i-1]);
|
||||
}
|
||||
writes.clear();
|
||||
}
|
||||
},
|
||||
[&](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap)
|
||||
|
||||
@@ -586,8 +586,8 @@ send_again:
|
||||
}
|
||||
// Estimate reply WR count, create WR and SGE arrays
|
||||
xdr_write_chunk *reply_chunk = rop->in_rdma_msg.rdma_body.rdma_msg.rdma_reply;
|
||||
uint32_t reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
|
||||
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + reply_chunk_wr_count;
|
||||
int reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
|
||||
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + (reply_chunk ? reply_chunk_wr_count : 0);
|
||||
if (wr_count > ctx->max_send_wr)
|
||||
{
|
||||
fprintf(stderr, "Reply fragmentation (%u) exceeds max_send_wr (%u), sending ERR_CHUNK\n", wr_count, ctx->max_send_wr);
|
||||
@@ -622,7 +622,7 @@ chunk_error:
|
||||
{
|
||||
size_t reply_chunk_len = 0;
|
||||
size_t left = msg_size;
|
||||
for (uint32_t i = 0; i < reply_chunk_wr_count; i++)
|
||||
for (uint32_t i = 0; i < reply_chunk->target.target_len; i++)
|
||||
{
|
||||
reply_chunk_len += reply_chunk->target.target_val[i].length;
|
||||
if (reply_chunk->target.target_val[i].length > left)
|
||||
@@ -653,7 +653,7 @@ chunk_error:
|
||||
}
|
||||
ibv_sge sges[wr_count];
|
||||
ibv_send_wr wrs[wr_count];
|
||||
uint32_t wr_pos = 0;
|
||||
int wr_pos = 0;
|
||||
// Use a buffer from rdma_malloc for the reply
|
||||
assert(!rop->buffer);
|
||||
rop->buffer = rdma_malloc_alloc(conn_dev->alloc, hdr_size+msg_size);
|
||||
@@ -685,7 +685,7 @@ chunk_error:
|
||||
if (reply_chunk)
|
||||
{
|
||||
size_t pos = hdr_size;
|
||||
for (uint32_t i = 0; i < reply_chunk_wr_count && pos < msg_size; i++)
|
||||
for (uint32_t i = 0; i < reply_chunk->target.target_len && pos < msg_size; i++)
|
||||
{
|
||||
uint32_t len = (reply_chunk->target.target_val[i].length < msg_size-pos
|
||||
? reply_chunk->target.target_val[i].length : msg_size-pos);
|
||||
|
||||
@@ -20,19 +20,20 @@ target_link_libraries(vitastor-osd
|
||||
|
||||
# osd_rmw_test
|
||||
add_executable(osd_rmw_test EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
|
||||
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES})
|
||||
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES} tcmalloc_minimal)
|
||||
add_dependencies(build_tests osd_rmw_test)
|
||||
add_test(NAME osd_rmw_test COMMAND osd_rmw_test)
|
||||
|
||||
if (ISAL_LIBRARIES)
|
||||
add_executable(osd_rmw_test_je EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
|
||||
target_compile_definitions(osd_rmw_test_je PUBLIC -DNO_ISAL)
|
||||
target_link_libraries(osd_rmw_test_je Jerasure)
|
||||
target_link_libraries(osd_rmw_test_je Jerasure tcmalloc_minimal)
|
||||
add_dependencies(build_tests osd_rmw_test_je)
|
||||
add_test(NAME osd_rmw_test_jerasure COMMAND osd_rmw_test_je)
|
||||
endif (ISAL_LIBRARIES)
|
||||
|
||||
# osd_peering_pg_test
|
||||
add_executable(osd_peering_pg_test EXCLUDE_FROM_ALL osd_peering_pg_test.cpp osd_peering_pg.cpp)
|
||||
target_link_libraries(osd_peering_pg_test tcmalloc_minimal)
|
||||
add_dependencies(build_tests osd_peering_pg_test)
|
||||
add_test(NAME osd_peering_pg_test COMMAND osd_peering_pg_test)
|
||||
|
||||
+7
-3
@@ -16,19 +16,22 @@
|
||||
#include "json_util.h"
|
||||
#include "openssl_util.h"
|
||||
|
||||
osd_t::osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd)
|
||||
osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
|
||||
{
|
||||
zero_buffer_size = 1<<20;
|
||||
zero_buffer = malloc_or_die(zero_buffer_size);
|
||||
memset(zero_buffer, 0, zero_buffer_size);
|
||||
|
||||
this->ringloop = ringloop;
|
||||
this->tfd = tfd;
|
||||
|
||||
this->cli_config = config.object_items();
|
||||
this->file_config = msgr.read_config(this->cli_config);
|
||||
parse_config(true);
|
||||
|
||||
epmgr = new epoll_manager_t(ringloop);
|
||||
// FIXME: Use timerfd_interval based directly on io_uring
|
||||
this->tfd = epmgr->tfd;
|
||||
|
||||
if (json_is_true(this->config["osd_memlock"]))
|
||||
{
|
||||
// Lock all OSD memory if requested
|
||||
@@ -102,6 +105,7 @@ osd_t::~osd_t()
|
||||
}
|
||||
ringloop->unregister_consumer(&consumer);
|
||||
ringloop->unregister_consumer(&init_consumer);
|
||||
delete epmgr;
|
||||
if (bs)
|
||||
delete bs;
|
||||
#ifdef WITH_RDMACM
|
||||
@@ -408,7 +412,7 @@ void osd_t::bind_socket()
|
||||
{
|
||||
int listen_fd = create_and_bind_socket(bind_address, listening_port ? listening_port : bind_port, listen_backlog, &listening_port);
|
||||
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
tfd->set_fd_handler(listen_fd, false, [this](int fd, int events)
|
||||
epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events)
|
||||
{
|
||||
msgr.accept_connections(fd);
|
||||
});
|
||||
|
||||
+7
-5
@@ -19,6 +19,7 @@
|
||||
#include "blockstore.h"
|
||||
#include "ringloop.h"
|
||||
#include "timerfd_manager.h"
|
||||
#include "epoll_manager.h"
|
||||
#include "osd_peering_pg.h"
|
||||
#include "messenger.h"
|
||||
#include "etcd_state_client.h"
|
||||
@@ -212,8 +213,9 @@ class osd_t
|
||||
void *zero_buffer = NULL;
|
||||
uint64_t zero_buffer_size = 0;
|
||||
uint32_t bs_block_size, bs_bitmap_granularity, clean_entry_bitmap_size;
|
||||
ring_loop_i *ringloop = NULL;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
epoll_manager_t *epmgr = NULL;
|
||||
|
||||
int listening_port = 0;
|
||||
std::vector<std::string> bind_addresses;
|
||||
@@ -353,7 +355,8 @@ class osd_t
|
||||
std::function<int(pg_osd_set_t & new_set)> calc_set);
|
||||
pg_osd_set_state_t *mark_object_corrupted(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref);
|
||||
pg_osd_set_state_t *mark_partial_write(pg_t & pg, osd_op_t *cur_op);
|
||||
pg_osd_set_state_t *mark_partial_write(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref);
|
||||
void deref_object_state(pg_t & pg, pg_osd_set_state_t **object_state, bool deref);
|
||||
bool remember_unstable_write(osd_op_t *cur_op, pg_t & pg, pg_osd_set_t & loc_set, int base_state);
|
||||
void handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op);
|
||||
@@ -366,10 +369,9 @@ class osd_t
|
||||
osd_rmw_stripe_t *stripes, const uint64_t* osd_set, osd_op_t *cur_op, int subop_idx, int zero_read);
|
||||
void submit_primary_subop(osd_op_t *cur_op, osd_op_t *subop,
|
||||
osd_rmw_stripe_t *si, bool wr, inode_t inode, uint64_t op_version);
|
||||
bool submit_to_osd(osd_op_t *subop, osd_num_t osd_num);
|
||||
void submit_primary_del_subops(osd_op_t *cur_op, uint64_t *cur_set, uint64_t set_size, pg_osd_set_t & loc_set);
|
||||
void submit_primary_del_batch(osd_op_t *cur_op, obj_ver_osd_t *chunks_to_delete, int chunks_to_delete_count);
|
||||
void submit_primary_sync_subops(osd_op_t *cur_op);
|
||||
int submit_primary_sync_subops(osd_op_t *cur_op);
|
||||
void submit_primary_stab_subops(osd_op_t *cur_op);
|
||||
void submit_primary_rollback_subops(osd_op_t *cur_op, const uint64_t* osd_set);
|
||||
|
||||
@@ -393,7 +395,7 @@ class osd_t
|
||||
}
|
||||
|
||||
public:
|
||||
osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd);
|
||||
osd_t(const json11::Json & config, ring_loop_t *ringloop);
|
||||
~osd_t();
|
||||
void force_stop(int exitcode);
|
||||
bool shutdown();
|
||||
|
||||
@@ -744,7 +744,7 @@ void osd_t::apply_pg_count()
|
||||
for (auto & pool_item: st_cli.pool_config)
|
||||
{
|
||||
auto & pool_cfg = pool_item.second;
|
||||
if (pool_cfg.real_pg_count == 0 || !pool_cfg.exists)
|
||||
if (pool_cfg.real_pg_count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
+10
-2
@@ -98,7 +98,6 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
|
||||
{
|
||||
// Will repeer/stop this PG
|
||||
msgr.stop_client(peer_it->second->client_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,8 +214,17 @@ bool osd_t::submit_flush_op(pool_id_t pool_id, pg_num_t pg_num, pg_flush_batch_t
|
||||
handle_flush_op(op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK, pool_id, pg_num, fb, peer_osd, op->reply.hdr.retval);
|
||||
delete op;
|
||||
};
|
||||
if (!submit_to_osd(op, peer_osd))
|
||||
auto peer_it = msgr.osd_peers.find(peer_osd);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
op->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
op->reply.hdr.retval = -EPIPE;
|
||||
op->callback(op);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "epoll_manager.h"
|
||||
#include "osd.h"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
@@ -64,15 +63,13 @@ int main(int narg, char *args[])
|
||||
signal(SIGINT, handle_sigint);
|
||||
signal(SIGTERM, handle_sigint);
|
||||
ring_loop_t *ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
|
||||
epoll_manager_t *epmgr = new epoll_manager_t(ringloop);
|
||||
osd = new osd_t(config, ringloop, epmgr->tfd);
|
||||
osd = new osd_t(config, ringloop);
|
||||
while (1)
|
||||
{
|
||||
ringloop->loop();
|
||||
ringloop->wait();
|
||||
}
|
||||
delete osd;
|
||||
delete epmgr;
|
||||
delete ringloop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -138,12 +138,10 @@ void osd_t::reset_pg(pg_t & pg)
|
||||
copies_to_delete_after_sync_count -= pg.copies_to_delete_after_sync.size();
|
||||
pg.copies_to_delete_after_sync.clear();
|
||||
corrupted_objects -= pg.corrupted_count;
|
||||
inconsistent_objects -= pg.inconsistent_objects.size();
|
||||
incomplete_objects -= pg.incomplete_objects.size();
|
||||
misplaced_objects -= pg.misplaced_objects.size();
|
||||
degraded_objects -= pg.degraded_objects.size();
|
||||
pg.corrupted_count = 0;
|
||||
pg.inconsistent_objects.clear();
|
||||
pg.incomplete_objects.clear();
|
||||
pg.misplaced_objects.clear();
|
||||
pg.degraded_objects.clear();
|
||||
@@ -351,7 +349,6 @@ bool osd_t::continue_pg_peering(pg_t & pg)
|
||||
pg.calc_object_states(log_level);
|
||||
report_pg_state(pg);
|
||||
schedule_scrub(pg);
|
||||
inconsistent_objects += pg.inconsistent_objects.size();
|
||||
incomplete_objects += pg.incomplete_objects.size();
|
||||
misplaced_objects += pg.misplaced_objects.size();
|
||||
// FIXME: degraded objects may currently include misplaced, too! Report them separately?
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
#include "malloc_or_die.h"
|
||||
#include "osd_peering_pg.h"
|
||||
|
||||
+22
-41
@@ -297,7 +297,6 @@ resume_0:
|
||||
finish_op(cur_op, -EIO);
|
||||
return;
|
||||
}
|
||||
assert(!cur_op->buf);
|
||||
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);
|
||||
}
|
||||
@@ -310,7 +309,6 @@ resume_0:
|
||||
}
|
||||
// Submit reads
|
||||
op_data->degraded = 1;
|
||||
assert(!cur_op->buf);
|
||||
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);
|
||||
}
|
||||
@@ -330,11 +328,7 @@ resume_2:
|
||||
// FIXME: ref = true ideally... because new_state != state is not necessarily true if it's freed and recreated
|
||||
auto new_object_state = mark_object_corrupted(*pg, op_data->oid, op_data->object_state, op_data->stripes, false);
|
||||
if (new_object_state != op_data->object_state)
|
||||
{
|
||||
free(cur_op->buf);
|
||||
cur_op->buf = NULL;
|
||||
goto resume_0;
|
||||
}
|
||||
}
|
||||
finish_op(cur_op, op_data->errcode);
|
||||
return;
|
||||
@@ -462,33 +456,24 @@ pg_osd_set_state_t *osd_t::mark_object_corrupted(pg_t & pg, object_id oid,
|
||||
}
|
||||
|
||||
// Mark the object as partially updated (probably due to a ENOSPC)
|
||||
pg_osd_set_state_t *osd_t::mark_partial_write(pg_t & pg, osd_op_t *cur_op)
|
||||
pg_osd_set_state_t *osd_t::mark_partial_write(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref)
|
||||
{
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
return mark_object(pg, op_data->oid, op_data->object_state, true, [&](pg_osd_set_t & new_set)
|
||||
return mark_object(pg, oid, prev_object_state, ref, [stripes](pg_osd_set_t & new_set)
|
||||
{
|
||||
// Mark object chunk(s) as outdated
|
||||
int changes = 0;
|
||||
for (auto & chunk: new_set)
|
||||
for (auto chunk_it = new_set.begin(); chunk_it != new_set.end(); )
|
||||
{
|
||||
if (chunk.loc_bad != LOC_OUTDATED)
|
||||
auto & chunk = *chunk_it;
|
||||
if (stripes[chunk.role].osd_num == chunk.osd_num &&
|
||||
stripes[chunk.role].read_error &&
|
||||
chunk.loc_bad != LOC_OUTDATED)
|
||||
{
|
||||
bool success = false;
|
||||
for (int i = 0; i < op_data->n_subops; i++)
|
||||
{
|
||||
if (op_data->subops[i].osd_num == chunk.osd_num &&
|
||||
op_data->subops[i].reply.hdr.retval == op_data->subops[i].req.sec_rw.len)
|
||||
{
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
changes++;
|
||||
chunk.loc_bad = LOC_OUTDATED;
|
||||
}
|
||||
changes++;
|
||||
chunk.loc_bad = LOC_OUTDATED;
|
||||
}
|
||||
chunk_it++;
|
||||
}
|
||||
return changes;
|
||||
});
|
||||
@@ -564,19 +549,19 @@ pg_osd_set_state_t* osd_t::add_object_to_set(pg_t & pg, const object_id oid, con
|
||||
{
|
||||
this->incomplete_objects++;
|
||||
obj_state |= OBJ_INCOMPLETE;
|
||||
pg_state_bits |= PG_HAS_INCOMPLETE;
|
||||
pg_state_bits = PG_HAS_INCOMPLETE;
|
||||
}
|
||||
else if (n_roles < pg.pg_cursize)
|
||||
{
|
||||
this->degraded_objects++;
|
||||
obj_state |= OBJ_DEGRADED;
|
||||
pg_state_bits |= PG_HAS_DEGRADED;
|
||||
pg_state_bits = PG_HAS_DEGRADED;
|
||||
}
|
||||
else if (n_misplaced > 0 || n_outdated > 0)
|
||||
{
|
||||
this->misplaced_objects++;
|
||||
obj_state |= OBJ_MISPLACED;
|
||||
pg_state_bits |= PG_HAS_MISPLACED;
|
||||
pg_state_bits = PG_HAS_MISPLACED;
|
||||
}
|
||||
if (this->log_level >= log_at_level)
|
||||
{
|
||||
@@ -656,8 +641,7 @@ bool osd_t::remove_object_from_state(object_id & oid, pg_osd_set_state_t **objec
|
||||
get_object_osd_set(pg, oid, &recheck_state);
|
||||
if (recheck_state != *object_state)
|
||||
{
|
||||
if (recheck_state)
|
||||
recheck_state->ref_count++;
|
||||
recheck_state->ref_count++;
|
||||
(*object_state)->ref_count--;
|
||||
*object_state = recheck_state;
|
||||
return false;
|
||||
@@ -812,11 +796,8 @@ resume_3:
|
||||
op_data->fact_ver++;
|
||||
submit_primary_del_subops(cur_op, NULL, 0, op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set);
|
||||
resume_4:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 4;
|
||||
return;
|
||||
}
|
||||
op_data->st = 4;
|
||||
return;
|
||||
resume_5:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -826,11 +807,6 @@ resume_5:
|
||||
}
|
||||
// Remove version override
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
// Mark PG and OSDs as dirty
|
||||
for (auto & chunk: (op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set))
|
||||
{
|
||||
this->dirty_osds.insert(chunk.osd_num);
|
||||
}
|
||||
// Adjust PG stats after "instant stabilize", because we need object_state above
|
||||
if (!op_data->object_state)
|
||||
{
|
||||
@@ -841,6 +817,11 @@ resume_5:
|
||||
remove_object_from_state(op_data->oid, &op_data->object_state, pg);
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
}
|
||||
// Mark PG and OSDs as dirty
|
||||
for (auto & chunk: (op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set))
|
||||
{
|
||||
this->dirty_osds.insert(chunk.osd_num);
|
||||
}
|
||||
for (auto cl_it = msgr.clients.find(cur_op->client_id); cl_it != msgr.clients.end(); )
|
||||
{
|
||||
cl_it->second->dirty_pgs.insert({ .pool_id = pg.pool_id, .pg_num = pg.pg_num });
|
||||
|
||||
@@ -82,7 +82,6 @@ int osd_t::read_bitmaps(osd_op_t *cur_op, pg_t *pg, int base_state)
|
||||
goto resume_0;
|
||||
else if (op_data->st == base_state+1)
|
||||
goto resume_1;
|
||||
assert(op_data->st < base_state);
|
||||
if (!pg || pg->state == PG_ACTIVE && pg->scheme == POOL_SCHEME_REPLICATED)
|
||||
{
|
||||
// Happy path for clean replicated PGs (all bitmaps are available locally)
|
||||
@@ -113,12 +112,6 @@ resume_0:
|
||||
return 1;
|
||||
}
|
||||
resume_1:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
// Failure
|
||||
finish_op(cur_op, op_data->errcode);
|
||||
return -1;
|
||||
}
|
||||
if (pg->scheme != POOL_SCHEME_REPLICATED)
|
||||
{
|
||||
for (int chain_num = 0; chain_num < op_data->chain_size; chain_num++)
|
||||
@@ -190,6 +183,7 @@ int osd_t::collect_bitmap_requests(osd_op_t *cur_op, pg_t & pg, std::vector<bitm
|
||||
memcpy(local_stripes, op_data->stripes, sizeof(osd_rmw_stripe_t) * pg.pg_size);
|
||||
if (extend_missing_stripes(local_stripes, cur_set, pg.pg_data_size, pg.pg_size) < 0)
|
||||
{
|
||||
free(op_data->snapshot_bitmaps);
|
||||
return -1;
|
||||
}
|
||||
int need_at_least = 0;
|
||||
@@ -318,7 +312,19 @@ int osd_t::submit_bitmap_subops(osd_op_t *cur_op, pg_t & pg)
|
||||
}
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
submit_to_osd(subop, subop_osd_num);
|
||||
auto peer_it = msgr.osd_peers.find(subop_osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
subop_idx++;
|
||||
}
|
||||
prev = i+1;
|
||||
@@ -617,7 +623,7 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
|
||||
{
|
||||
if (cur > prev)
|
||||
{
|
||||
// Send buffer in parts to avoid copying
|
||||
// Send buffer in parts to avoid copying
|
||||
if (!prev_set)
|
||||
{
|
||||
while ((cur-prev) > zero_buffer_size/bs_bitmap_granularity)
|
||||
|
||||
@@ -268,30 +268,22 @@ void osd_t::submit_primary_subop(osd_op_t *cur_op, osd_op_t *subop,
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
submit_to_osd(subop, si->osd_num);
|
||||
auto peer_it = msgr.osd_peers.find(si->osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool osd_t::submit_to_osd(osd_op_t *subop, osd_num_t osd_num)
|
||||
{
|
||||
subop->osd_num = osd_num;
|
||||
auto peer_it = msgr.osd_peers.find(osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t bs_op_to_osd_op[] = {
|
||||
0,
|
||||
OSD_OP_SEC_READ, // BS_OP_READ = 1
|
||||
@@ -365,7 +357,7 @@ void osd_t::add_bs_subop_stats(osd_op_t *subop, bool recovery_related)
|
||||
uint64_t opcode = bs_op_to_osd_op[subop->bs_op->opcode];
|
||||
timespec tv_end;
|
||||
clock_gettime(CLOCK_REALTIME, &tv_end);
|
||||
uint64_t len = (opcode == OSD_OP_SEC_READ || opcode == OSD_OP_SEC_WRITE || opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||
uint64_t len = (opcode == OSD_OP_SEC_READ || opcode == OSD_OP_SEC_WRITE)
|
||||
? subop->bs_op->len : 0;
|
||||
msgr.inc_op_stats(msgr.stats, opcode, subop->tv_begin, tv_end, len);
|
||||
if (recovery_related)
|
||||
@@ -488,11 +480,8 @@ void osd_t::handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op)
|
||||
}
|
||||
if ((op_data->errors + op_data->done) >= op_data->n_subops)
|
||||
{
|
||||
if (!op_data->errors || !op_data->done || opcode != OSD_OP_SEC_WRITE && opcode != OSD_OP_SEC_WRITE_STABLE)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
}
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
op_data->st++;
|
||||
if (cur_op->req.hdr.opcode == OSD_OP_READ)
|
||||
{
|
||||
@@ -625,82 +614,86 @@ void osd_t::submit_primary_del_batch(osd_op_t *cur_op, obj_ver_osd_t *chunks_to_
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
submit_to_osd(&subops[i], chunk.osd_num);
|
||||
auto peer_it = msgr.osd_peers.find(chunk.osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subops[i].client_id = 0;
|
||||
subops[i].reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop = &subops[i]]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void osd_t::submit_primary_sync_subops(osd_op_t *cur_op)
|
||||
int osd_t::submit_primary_sync_subops(osd_op_t *cur_op)
|
||||
{
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
int n_osds = op_data->dirty_osd_count;
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->done = op_data->errors = op_data->errcode = 0;
|
||||
op_data->n_subops = n_osds;
|
||||
if (op_data->n_subops <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->subops = subops;
|
||||
robin_hood::unordered_flat_map<uint64_t, osd_client_t*>::iterator peer_it;
|
||||
int subop_idx = 0;
|
||||
for (int i = 0; i < n_osds; i++)
|
||||
{
|
||||
osd_num_t sync_osd = op_data->dirty_osds[i];
|
||||
osd_op_t *subop = &subops[subop_idx];
|
||||
if (sync_osd == this->osd_num)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, &subop->tv_begin);
|
||||
subop->op_type = (uint64_t)cur_op;
|
||||
subop->bs_op = new blockstore_op_t({
|
||||
clock_gettime(CLOCK_REALTIME, &subops[i].tv_begin);
|
||||
subops[i].op_type = (uint64_t)cur_op;
|
||||
subops[i].bs_op = new blockstore_op_t({
|
||||
.opcode = BS_OP_SYNC,
|
||||
.callback = [subop, this](blockstore_op_t *bs_subop)
|
||||
.callback = [subop = &subops[i], this](blockstore_op_t *bs_subop)
|
||||
{
|
||||
handle_primary_bs_subop(subop);
|
||||
},
|
||||
});
|
||||
bs->enqueue_op(subop->bs_op);
|
||||
subop_idx++;
|
||||
bs->enqueue_op(subops[i].bs_op);
|
||||
}
|
||||
else if ((peer_it = msgr.osd_peers.find(sync_osd)) != msgr.osd_peers.end())
|
||||
{
|
||||
subop->op_type = OSD_OP_OUT;
|
||||
subop->osd_num = sync_osd;
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
subop->req = (osd_any_op_t){ .sec_sync = {
|
||||
subops[i].op_type = OSD_OP_OUT;
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
subops[i].req = (osd_any_op_t){ .sec_sync = {
|
||||
.header = {
|
||||
.magic = SECONDARY_OSD_OP_MAGIC,
|
||||
.opcode = OSD_OP_SEC_SYNC,
|
||||
},
|
||||
.flags = cur_op->client_id == SELF_CLIENT && cur_op->req.hdr.opcode != OSD_OP_SCRUB ? OSD_OP_RECOVERY_RELATED : 0,
|
||||
} };
|
||||
subop->callback = [cur_op, this](osd_op_t *subop)
|
||||
subops[i].callback = [cur_op, this](osd_op_t *subop)
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
msgr.outbox_push(subop);
|
||||
subop_idx++;
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
op_data->done++;
|
||||
}
|
||||
}
|
||||
op_data->n_subops = subop_idx;
|
||||
if (subop_idx <= 0)
|
||||
if (op_data->done >= op_data->n_subops)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void osd_t::submit_primary_stab_subops(osd_op_t *cur_op)
|
||||
{
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
int n_osds = op_data->unstable_write_osds->size();
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->done = op_data->errors = op_data->errcode = 0;
|
||||
op_data->n_subops = n_osds;
|
||||
if (op_data->n_subops <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->subops = subops;
|
||||
for (int i = 0; i < n_osds; i++)
|
||||
{
|
||||
@@ -738,7 +731,19 @@ void osd_t::submit_primary_stab_subops(osd_op_t *cur_op)
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
submit_to_osd(&subops[i], stab_osd.osd_num);
|
||||
auto peer_it = msgr.osd_peers.find(stab_osd.osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subops[i].client_id = 0;
|
||||
subops[i].reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop = &subops[i]]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -824,7 +829,8 @@ void osd_t::submit_primary_rollback_subops(osd_op_t *cur_op, const uint64_t* osd
|
||||
op_data->oid.inode, op_data->oid.stripe | role, op_data->target_ver-1
|
||||
);
|
||||
#endif
|
||||
submit_to_osd(subop, osd_set[role]);
|
||||
subop->client_id = msgr.osd_peers.at(osd_set[role])->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -137,13 +137,13 @@ resume_2:
|
||||
if (immediate_commit != IMMEDIATE_ALL)
|
||||
{
|
||||
// SYNC
|
||||
submit_primary_sync_subops(cur_op);
|
||||
resume_3:
|
||||
if (op_data->n_subops > 0)
|
||||
if (!submit_primary_sync_subops(cur_op))
|
||||
{
|
||||
op_data->st = 3;
|
||||
return;
|
||||
goto resume_4;
|
||||
}
|
||||
resume_3:
|
||||
op_data->st = 3;
|
||||
return;
|
||||
resume_4:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -155,11 +155,8 @@ resume_4:
|
||||
// Stabilize version sets, if any
|
||||
submit_primary_stab_subops(cur_op);
|
||||
resume_5:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 5;
|
||||
return;
|
||||
}
|
||||
op_data->st = 5;
|
||||
return;
|
||||
}
|
||||
resume_6:
|
||||
if (op_data->errors > 0)
|
||||
@@ -204,11 +201,8 @@ resume_6:
|
||||
// Actually delete copies which we wanted to delete
|
||||
submit_primary_del_batch(cur_op, op_data->copies_to_delete, op_data->copies_to_delete_count);
|
||||
resume_7:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 7;
|
||||
return;
|
||||
}
|
||||
op_data->st = 7;
|
||||
return;
|
||||
resume_8:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
|
||||
@@ -188,7 +188,7 @@ resume_3:
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((op_data->fact_ver & (((uint64_t)1 << (64-PG_EPOCH_BITS)) - 1)) == (((uint64_t)1 << (64-PG_EPOCH_BITS)) - 1))
|
||||
if ((op_data->fact_ver & ((uint64_t)1 << (64-PG_EPOCH_BITS) - 1)) == ((uint64_t)1 << (64-PG_EPOCH_BITS) - 1))
|
||||
{
|
||||
assert(pg.epoch != (((uint64_t)1 << PG_EPOCH_BITS)-1));
|
||||
pg.epoch++;
|
||||
@@ -231,11 +231,8 @@ resume_10:
|
||||
}
|
||||
submit_primary_subops(SUBMIT_WRITE, op_data->target_ver, pg.cur_set.data(), cur_op);
|
||||
resume_4:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 4;
|
||||
return;
|
||||
}
|
||||
op_data->st = 4;
|
||||
return;
|
||||
resume_5:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -250,11 +247,8 @@ resume_5:
|
||||
{
|
||||
submit_primary_rollback_subops(cur_op, pg.cur_set.data());
|
||||
resume_11:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 11;
|
||||
return;
|
||||
}
|
||||
op_data->st = 11;
|
||||
return;
|
||||
resume_12:
|
||||
// Ignore ROLLBACK errors - submit_primary_subops will drop the connection if it fails
|
||||
delete[] op_data->unstable_writes;
|
||||
@@ -263,16 +257,11 @@ resume_12:
|
||||
else
|
||||
{
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
mark_partial_write(pg, cur_op);
|
||||
mark_partial_write(pg, op_data->oid, op_data->object_state, op_data->stripes, true);
|
||||
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (op_data->subops)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
}
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
||||
@@ -498,11 +487,8 @@ immediate:
|
||||
}
|
||||
submit_primary_stab_subops(cur_op);
|
||||
resume_6:
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 6;
|
||||
return false;
|
||||
}
|
||||
op_data->st = 6;
|
||||
return false;
|
||||
resume_7:
|
||||
// FIXME: Free those in the destructor?
|
||||
delete op_data->unstable_write_osds;
|
||||
|
||||
+2
-3
@@ -417,7 +417,6 @@ void reconstruct_stripes_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsi
|
||||
data_ptrs[orig++] = (uint8_t*)stripes[other].read_buf + (read_start - stripes[other].read_start);
|
||||
}
|
||||
}
|
||||
assert(orig == pg_minsize);
|
||||
ec_encode_data(
|
||||
read_end-read_start, pg_minsize, wanted, dectable + wanted_base*item_size*pg_minsize,
|
||||
data_ptrs, data_ptrs + pg_minsize
|
||||
@@ -1282,7 +1281,7 @@ std::vector<int> ec_find_good(osd_rmw_stripe_t *stripes, int stripe_count, int p
|
||||
std::vector<std::vector<int>> live_variants(pg_size);
|
||||
int eq_to[stripe_count];
|
||||
int live_roles = 0, live_total = 0;
|
||||
for (int i = 0; i < stripe_count; i++)
|
||||
for (int i = 0; i < pg_size; i++)
|
||||
{
|
||||
eq_to[i] = i;
|
||||
}
|
||||
@@ -1382,7 +1381,7 @@ std::vector<int> ec_find_good(osd_rmw_stripe_t *stripes, int stripe_count, int p
|
||||
for (i = 0; i < pg_minsize; i++)
|
||||
{
|
||||
subvar[i]++;
|
||||
if (subvar[i] < live_variants[comb_to_subset[combination[i]]].size())
|
||||
if (subvar[i] < live_variants[combination[i]].size())
|
||||
break;
|
||||
subvar[i] = 0;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ void test_recover_22_d2();
|
||||
void test_ec43_error_bruteforce();
|
||||
void test_recover_53_d5();
|
||||
void test_recover_22();
|
||||
void test_ec_find_good_multi_chunks();
|
||||
|
||||
int main(int narg, char *args[])
|
||||
{
|
||||
@@ -70,7 +69,6 @@ int main(int narg, char *args[])
|
||||
test_recover_22_d2();
|
||||
// Error bruteforce
|
||||
test_ec43_error_bruteforce();
|
||||
test_ec_find_good_multi_chunks();
|
||||
// Test 19
|
||||
test_recover_53_d5();
|
||||
// Test 20
|
||||
@@ -1347,54 +1345,3 @@ void test_recover_22()
|
||||
free(write_buf);
|
||||
use_ec(4, 2, false);
|
||||
}
|
||||
|
||||
void test_ec_find_good_multi_chunks()
|
||||
{
|
||||
use_ec(7, 4, true);
|
||||
osd_num_t osd_set[7] = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
osd_rmw_stripe_t stripes[15] = {};
|
||||
split_stripes(4, 4096, 0, 4096 * 4, stripes);
|
||||
uint8_t *write_buf = (uint8_t*)malloc_or_die(4096 * 15);
|
||||
set_pattern(write_buf+0*4096, 4096, PATTERN0);
|
||||
set_pattern(write_buf+1*4096, 4096, PATTERN1);
|
||||
set_pattern(write_buf+2*4096, 4096, PATTERN2);
|
||||
set_pattern(write_buf+3*4096, 4096, PATTERN3);
|
||||
uint8_t *rmw_buf = (uint8_t*)calc_rmw(write_buf, stripes, osd_set, 7, 4, 7, osd_set, 4096, 0);
|
||||
calc_rmw_parity_ec(stripes, 7, 4, osd_set, osd_set, 4096, 0);
|
||||
check_pattern(stripes[4].write_buf, 4096, PATTERN0^PATTERN1^PATTERN2^PATTERN3);
|
||||
check_pattern(stripes[5].write_buf, 4096, 0xfcee568ba36371ac); // 2nd EC chunk
|
||||
check_pattern(stripes[6].write_buf, 4096, 0x139274739ae6f387); // 3rd EC chunk
|
||||
memcpy(write_buf+4*4096, stripes[4].write_buf, 4096);
|
||||
memcpy(write_buf+5*4096, stripes[5].write_buf, 4096);
|
||||
memcpy(write_buf+6*4096, stripes[6].write_buf, 4096);
|
||||
// Make 15 chunks
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
stripes[i].read_start = 0;
|
||||
stripes[i].read_end = 4096;
|
||||
stripes[i].read_buf = write_buf+i*4096;
|
||||
stripes[i].write_buf = NULL;
|
||||
stripes[i].role = i;
|
||||
stripes[i].osd_num = i+1;
|
||||
}
|
||||
for (int i = 7; i < 14; i++)
|
||||
{
|
||||
stripes[i].role = i % 7;
|
||||
memcpy(write_buf+i*4096, write_buf + (i%7)*4096, 4096);
|
||||
}
|
||||
stripes[14].role = 6;
|
||||
memcpy(write_buf+14*4096, write_buf + 6*4096, 4096);
|
||||
// Corrupt chunks 0-6 and chunk 13, make role 0 absent
|
||||
stripes[0].read_error = true;
|
||||
stripes[7].read_error = true;
|
||||
for (int i = 0; i < 7; i++)
|
||||
memset(write_buf+i*4096, i*10+1, 4096);
|
||||
memset(write_buf+13*4096, 8*10+1, 4096);
|
||||
// Find errors
|
||||
auto res = ec_find_good(stripes, 15, 7, 4, false, 4096, 0, 100, true);
|
||||
assert_eq_vec(res, std::vector<int>({8, 9, 10, 11, 12, 14}));
|
||||
// Done
|
||||
free(rmw_buf);
|
||||
free(write_buf);
|
||||
use_ec(7, 4, false);
|
||||
}
|
||||
|
||||
@@ -287,8 +287,6 @@ bool osd_t::continue_scrub()
|
||||
if (no_scrub)
|
||||
{
|
||||
// Return false = no more scrub work to do
|
||||
if (scrub_cur_list.buf)
|
||||
free(scrub_cur_list.buf);
|
||||
scrub_cur_list = {};
|
||||
scrub_last_pg = {};
|
||||
scrub_nearest_ts = 0;
|
||||
@@ -503,7 +501,7 @@ void osd_t::scrub_check_results(osd_op_t *cur_op)
|
||||
{
|
||||
unknown = true;
|
||||
}
|
||||
if (eq_to[role] != -1 && votes[eq_to[role]] > 0 && votes[eq_to[role]] < votes[best])
|
||||
if (votes[role] > 0 && votes[role] < votes[best])
|
||||
{
|
||||
printf(
|
||||
"[PG %u/%u] Object %jx:%jx v%ju copy on OSD %ju doesn't match %d other copies%s\n",
|
||||
|
||||
+20
-19
@@ -217,13 +217,16 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
#ifdef OSD_STUB
|
||||
cur_op->bs_op->retval = 0;
|
||||
#endif
|
||||
for (int i = 0; i < cur_op->bs_op->len; i++)
|
||||
if (enable_pg_locks)
|
||||
{
|
||||
if (!sec_check_pg_lock(cl->in_osd_num, ((obj_ver_id*)cur_op->buf)[i].oid, cur_op->req.sec_stab.flags))
|
||||
for (int i = 0; i < cur_op->bs_op->len; i++)
|
||||
{
|
||||
cur_op->bs_op->retval = -EPIPE;
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
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;
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,8 +240,8 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
}
|
||||
auto pool_id = INODE_POOL(cur_op->req.sec_list.min_inode);
|
||||
if (!pool_id || !sec_check_pg_lock(0, (object_id){ .inode = cur_op->req.sec_list.min_inode }, OSD_OP_IGNORE_PG_LOCK))
|
||||
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;
|
||||
@@ -251,7 +254,7 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
cur_op->bs_op->min_oid.inode = cur_op->req.sec_list.min_inode;
|
||||
cur_op->bs_op->min_oid.stripe = cur_op->req.sec_list.min_stripe;
|
||||
cur_op->bs_op->max_oid.inode = cur_op->req.sec_list.max_inode;
|
||||
if (cur_op->req.sec_list.max_inode)
|
||||
if (cur_op->req.sec_list.max_inode && cur_op->req.sec_list.max_stripe != UINT64_MAX)
|
||||
{
|
||||
cur_op->bs_op->max_oid.stripe = cur_op->req.sec_list.max_stripe
|
||||
? cur_op->req.sec_list.max_stripe : UINT64_MAX;
|
||||
@@ -370,26 +373,24 @@ void osd_t::exec_show_config(osd_op_t *cur_op)
|
||||
cl->read_op_id = cur_op->req.hdr.id + 1;
|
||||
}
|
||||
auto features = json11::Json::object{ { "pg_locks", true } };
|
||||
#ifdef WITH_RDMA
|
||||
if (msgr.is_rdma_enabled() && req_json["features"]["rdma_close_tcp"].bool_value())
|
||||
{
|
||||
cl->rdma_close_tcp = true;
|
||||
features["rdma_close_tcp"] = true;
|
||||
}
|
||||
#endif
|
||||
if (msgr.use_proto_checksums)
|
||||
{
|
||||
auto peer_csums = req_json["features"]["proto_checksums"].uint64_value();
|
||||
if (peer_csums == MSGR_CSUM_FULL || peer_csums == MSGR_CSUM_PAYLOAD ||
|
||||
cl->gcm_enabled && peer_csums == MSGR_CSUM_GCM)
|
||||
if (peer_csums == MSGR_CSUM_FULL || peer_csums == MSGR_CSUM_PAYLOAD)
|
||||
{
|
||||
if (msgr.use_proto_checksums == MSGR_CSUM_GCM && peer_csums == MSGR_CSUM_GCM)
|
||||
cl->proto_csum_status = MSGR_CSUM_GCM|MSGR_CSUM_NEG;
|
||||
else if (msgr.use_proto_checksums == MSGR_CSUM_FULL && peer_csums == MSGR_CSUM_FULL)
|
||||
if (msgr.use_proto_checksums == MSGR_CSUM_FULL && peer_csums == MSGR_CSUM_FULL)
|
||||
cl->proto_csum_status = MSGR_CSUM_FULL|MSGR_CSUM_NEG;
|
||||
else
|
||||
cl->proto_csum_status = MSGR_CSUM_PAYLOAD|MSGR_CSUM_NEG;
|
||||
features["proto_checksums"] = msgr.use_proto_checksums;
|
||||
}
|
||||
if (peer_csums < msgr.force_proto_checksums)
|
||||
{
|
||||
fprintf(stderr, "Error: Client %ju use_proto_checksums security level is lower than force_proto_checksums\n", cl->client_id);
|
||||
msgr.stop_client(cl->client_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Expose sensitive configuration values so peers can check them
|
||||
json11::Json::object wire_config = json11::Json::object {
|
||||
|
||||
@@ -4,9 +4,13 @@ project(vitastor)
|
||||
|
||||
### Test stubs
|
||||
|
||||
# stub_osd, stub_bench
|
||||
# stub_osd, stub_bench, osd_test
|
||||
add_executable(stub_osd stub_osd.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(stub_osd tcmalloc_minimal)
|
||||
add_executable(stub_bench stub_bench.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(stub_bench tcmalloc_minimal)
|
||||
add_executable(osd_test osd_test.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(osd_test tcmalloc_minimal)
|
||||
|
||||
# bindiff
|
||||
add_executable(bindiff
|
||||
@@ -15,7 +19,6 @@ add_executable(bindiff
|
||||
|
||||
# stub_uring_osd
|
||||
add_executable(stub_uring_osd
|
||||
EXCLUDE_FROM_ALL
|
||||
stub_uring_osd.cpp
|
||||
)
|
||||
target_link_libraries(stub_uring_osd
|
||||
@@ -23,6 +26,7 @@ target_link_libraries(stub_uring_osd
|
||||
${LIBURING_LIBRARIES}
|
||||
${IBVERBS_LIBRARIES}
|
||||
${RDMACM_LIBRARIES}
|
||||
tcmalloc_minimal
|
||||
)
|
||||
|
||||
# test_allocator
|
||||
@@ -62,6 +66,9 @@ target_link_libraries(test_cas
|
||||
add_executable(test_crc32
|
||||
test_crc32.cpp
|
||||
)
|
||||
target_link_libraries(test_crc32
|
||||
vitastor_blk ${ISAL_LIBRARIES}
|
||||
)
|
||||
|
||||
# test_blockstore
|
||||
add_executable(test_blockstore EXCLUDE_FROM_ALL test_blockstore.cpp ringloop_mock.cpp)
|
||||
|
||||
@@ -57,7 +57,3 @@ json11::Json::object osd_messenger_t::merge_configs(const json11::Json::object &
|
||||
{
|
||||
return cli_config;
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct ring_consumer_t
|
||||
{
|
||||
std::function<void(void)> loop;
|
||||
};
|
||||
|
||||
class ring_loop_t
|
||||
{
|
||||
public:
|
||||
void register_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
}
|
||||
void unregister_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
}
|
||||
void submit()
|
||||
{
|
||||
}
|
||||
void wait()
|
||||
{
|
||||
}
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user