Compare commits
188 Commits
v0.7.0
...
csi-use-vi
Author | SHA1 | Date | |
---|---|---|---|
2b4e0de397 | |||
726c6d3470 | |||
2389b49a16 | |||
fe1ee67b05 | |||
c775a52a7d | |||
e307dd13ed | |||
a7f63f7c29 | |||
1e307069bc | |||
c3e80abad7 | |||
138ffe4032 | |||
8139a34e97 | |||
4ab630b44d | |||
2c8241b7db | |||
36a7dd3671 | |||
936122bbcf | |||
1a1ba0d1e7 | |||
3d09c9cec7 | |||
3d08a1ad6c | |||
499881d81c | |||
aba93b951b | |||
d125fb1f30 | |||
9d3fd72298 | |||
8b552a01f9 | |||
0385b2f9e8 | |||
749c837045 | |||
98001d845b | |||
c96bcae74b | |||
9f4e34a8cc | |||
81fc8bb94c | |||
bc465c16de | |||
8763e9211c | |||
9e1a80bd17 | |||
3e280f2f08 | |||
fe87b4076b | |||
a38957c1a7 | |||
137309cf29 | |||
373f9d0387 | |||
c4516ea971 | |||
91065c80fc | |||
0f6b946add | |||
465cbf0b2f | |||
41add50e4e | |||
02e7be7dc9 | |||
73940adf07 | |||
e950c024d3 | |||
71d6d9f868 | |||
a4dfa519af | |||
37a6aff2fa | |||
67019f5b02 | |||
0593e5c21c | |||
998e24adf8 | |||
d7bd36dc32 | |||
cf5c562800 | |||
629200b0cc | |||
3589ccec22 | |||
8d55a1e780 | |||
65f6b3a4eb | |||
fd216eac77 | |||
61fca7c426 | |||
1c29ed80b9 | |||
68f3fb795e | |||
fa90f287da | |||
795020674d | |||
8e12285629 | |||
b9b50ab4cc | |||
0d8625f92d | |||
2f3c2c5140 | |||
4ebdd02b0f | |||
bf6fdc4141 | |||
c2244331e6 | |||
3de57e87b1 | |||
2d4cc688b2 | |||
31bd1ec145 | |||
c08d1f2dfe | |||
1d80bcc8d0 | |||
5ef8bed75f | |||
8669998e5e | |||
b457327e77 | |||
f7fa9d5e34 | |||
49b88b01f9 | |||
71688bcb59 | |||
552e207d2b | |||
5464821fa5 | |||
6917a32ca8 | |||
f8722a8bd5 | |||
9c2f69c9fa | |||
1a93e3f33a | |||
3f35744052 | |||
66f14ac019 | |||
1364009931 | |||
d7e30b8353 | |||
cb437913d3 | |||
472bce58ab | |||
7a71e7ef01 | |||
c71e5e7bbd | |||
8fdf30b21f | |||
238037ae31 | |||
09a8864686 | |||
6e6f6ecbb0 | |||
9491f81419 | |||
44c2b30167 | |||
bf8a0581cd | |||
5953942042 | |||
a276a1f737 | |||
cc24e5796e | |||
6e26732e6a | |||
b4edc79449 | |||
5f26887d32 | |||
11ec9ad874 | |||
83bb6598dc | |||
150f369346 | |||
8d9a5fde15 | |||
9ccc607ab9 | |||
8972878c77 | |||
2a1da88253 | |||
2f13f347b0 | |||
9453db0e99 | |||
a828a1233d | |||
9481456dfe | |||
bd11db5d0a | |||
68ebe5993a | |||
eecbfb66ce | |||
a537db8909 | |||
54ef2c389f | |||
153c73574a | |||
d83580bd68 | |||
29b40aba93 | |||
a52f2b0e8f | |||
1407db9c08 | |||
c0d5e83fb8 | |||
40d8d65188 | |||
a16263e88c | |||
e62bab1b39 | |||
cb4e3a118d | |||
b1e39b5dea | |||
1170319431 | |||
2e0a2221eb | |||
5a10d135f3 | |||
4c9aaa8a86 | |||
ae99ee6266 | |||
5af75f7d78 | |||
7dc6f10ea1 | |||
6fde9950d6 | |||
76dd0fdcea | |||
5acc19bbd5 | |||
d5ca4e1f90 | |||
67e04f789f | |||
837407a84c | |||
1fe5908899 | |||
dcc6d546be | |||
85fa389557 | |||
dfa433c63b | |||
cf487c95aa | |||
b10656ca09 | |||
ea632367e9 | |||
4d777c6729 | |||
0c404c5074 | |||
dfd80626bd | |||
30907852c2 | |||
078ed5b116 | |||
73a363bf92 | |||
b0e86ca643 | |||
8800afb649 | |||
c10c90f620 | |||
e20cdd13b6 | |||
d29b5d2d04 | |||
65b0e8e940 | |||
bce357e2a5 | |||
0876ca09cd | |||
dac12d8a4c | |||
1eec4407ab | |||
![]() |
3b7c6dcac2 | ||
342517d126 | |||
675bc12a13 | |||
101592bbff | |||
be4087d9d2 | |||
404e43dd2d | |||
87613ed590 | |||
2a2e914ef9 | |||
0cdc9292c8 | |||
3e1b03bb5c | |||
36e851505a | |||
1efbbb0c36 | |||
088dd15449 | |||
4a531d7b8b | |||
a0cae4c180 | |||
c4eb46600d | |||
21b306e25f |
@@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 2.8)
|
|||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
set(VERSION "0.7.0")
|
set(VERSION "0.8.5")
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
@@ -58,6 +58,7 @@ Vitastor поддерживает QEMU-драйвер, протоколы NBD и
|
|||||||
- [Метаданные образов в etcd](docs/config/inode.ru.md)
|
- [Метаданные образов в etcd](docs/config/inode.ru.md)
|
||||||
- Использование
|
- Использование
|
||||||
- [vitastor-cli](docs/usage/cli.ru.md) (консольный интерфейс)
|
- [vitastor-cli](docs/usage/cli.ru.md) (консольный интерфейс)
|
||||||
|
- [vitastor-disk](docs/usage/disk.ru.md) (управление дисками)
|
||||||
- [fio](docs/usage/fio.ru.md) для тестов производительности
|
- [fio](docs/usage/fio.ru.md) для тестов производительности
|
||||||
- [NBD](docs/usage/nbd.ru.md) для монтирования ядром
|
- [NBD](docs/usage/nbd.ru.md) для монтирования ядром
|
||||||
- [QEMU и qemu-img](docs/usage/qemu.ru.md)
|
- [QEMU и qemu-img](docs/usage/qemu.ru.md)
|
||||||
@@ -96,5 +97,5 @@ Vitastor Network Public License 1.1, основанная на GNU GPL 3.0 с д
|
|||||||
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
|
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
|
||||||
совместимости с таким ПО, как QEMU и fio.
|
совместимости с таким ПО, как QEMU и fio.
|
||||||
|
|
||||||
Вы можете найти полный текст VNPL 1.1 в файле [VNPL-1.1.txt](VNPL-1.1.txt),
|
Вы можете найти полный текст VNPL 1.1 на английском языке в файле [VNPL-1.1.txt](VNPL-1.1.txt),
|
||||||
а GPL 2.0 в файле [GPL-2.0.txt](GPL-2.0.txt).
|
VNPL 1.1 на русском языке в файле [VNPL-1.1-RU.txt](VNPL-1.1-RU.txt), а GPL 2.0 в файле [GPL-2.0.txt](GPL-2.0.txt).
|
||||||
|
@@ -58,6 +58,7 @@ Read more details below in the documentation.
|
|||||||
- [Image metadata in etcd](docs/config/inode.en.md)
|
- [Image metadata in etcd](docs/config/inode.en.md)
|
||||||
- Usage
|
- Usage
|
||||||
- [vitastor-cli](docs/usage/cli.en.md) (command-line interface)
|
- [vitastor-cli](docs/usage/cli.en.md) (command-line interface)
|
||||||
|
- [vitastor-disk](docs/usage/disk.en.md) (disk management tool)
|
||||||
- [fio](docs/usage/fio.en.md) for benchmarks
|
- [fio](docs/usage/fio.en.md) for benchmarks
|
||||||
- [NBD](docs/usage/nbd.en.md) for kernel mounts
|
- [NBD](docs/usage/nbd.en.md) for kernel mounts
|
||||||
- [QEMU and qemu-img](docs/usage/qemu.en.md)
|
- [QEMU and qemu-img](docs/usage/qemu.en.md)
|
||||||
|
680
VNPL-1.1-RU.txt
Normal file
680
VNPL-1.1-RU.txt
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
СЕТЕВАЯ ПУБЛИЧНАЯ ЛИЦЕНЗИЯ VITASTOR
|
||||||
|
VITASTOR NETWORK PUBLIC LICENSE
|
||||||
|
Версия 1.1, от 6 февраля 2021
|
||||||
|
|
||||||
|
Автор лицензии: Виталий Филиппов <vitalif@yourcmc.ru>, 2021 год
|
||||||
|
Каждый имеет право копировать и распространять точные копии этой
|
||||||
|
лицензии, но без внесения изменений.
|
||||||
|
|
||||||
|
ПРЕАМБУЛА
|
||||||
|
|
||||||
|
Сетевая Публичная Лицензия Vitastor - это свободная "копилефт" лицензия для
|
||||||
|
для программного обеспечения (ПО) и других видов произведений, специально
|
||||||
|
разработанная, чтобы гарантировать кооперацию с сообществом при разработке
|
||||||
|
сетевых приложений.
|
||||||
|
|
||||||
|
Большинство лицензий на программное обеспечение и другие произведения
|
||||||
|
спроектированы так, чтобы лишить Вас свободы делиться ими и изменять их.
|
||||||
|
Сетевая Публичная Лицензия Vitastor, напротив, разработана с целью
|
||||||
|
гарантировать Ваше право распространять и вносить изменения во все версии
|
||||||
|
программного обеспечения -- для уверенности, что ПО останется свободным для
|
||||||
|
всех пользователей.
|
||||||
|
|
||||||
|
Когда мы говорим о свободном ПО, мы имеем в виду свободу использования, а не
|
||||||
|
бесплатность. Свободные лицензии, такие, как Сетевая Публичная Лицензия
|
||||||
|
Vitastor, составлены для того, чтобы убедиться, что у Вас есть право
|
||||||
|
распространять копии свободного ПО (и взимать плату за них, если Вы хотите),
|
||||||
|
что Вы получаете исходные тексты или можете получить их, если захотите, что Вы
|
||||||
|
можете изменять программное обеспечение или использовать его части в новых
|
||||||
|
свободных программах, и что Вы знаете о своем праве делать всё это.
|
||||||
|
|
||||||
|
Разработчики, использующие Сетевую Публичную Лицензию Vitastor, гарантируют
|
||||||
|
Ваши права при помощи следующих мер: (1) закрепляют авторское право на
|
||||||
|
программное обеспечение, и (2) предлагают Вам принять условия настоящей
|
||||||
|
Лицензии, закрепляющей Ваше право на создание копий, распространение и (или)
|
||||||
|
модификацию программного обеспечения.
|
||||||
|
|
||||||
|
Еще одно преимущество защиты свободы всех пользователей заключается в том,
|
||||||
|
что улучшения, сделанные в разных версиях программы, при их широком
|
||||||
|
распространении становятся доступными для использования другими разработчиками.
|
||||||
|
Многие разработчики программного обеспечения воодушевляются этим
|
||||||
|
сотрудничеством и пользуются его преимуществами. Однако, если программное
|
||||||
|
обеспечение используется на сетевых серверах, данный результат не всегда
|
||||||
|
достигается. Генеральная публичная лицензия GNU разрешает создание измененных
|
||||||
|
версий и предоставление неограниченного доступа к ним, не делая общедоступным
|
||||||
|
их исходный текст. Даже генеральная публичная лицензия GNU Affero разрешает
|
||||||
|
использование модифицированной версии свободной программы в закрытой среде, где
|
||||||
|
внешние пользователи взаимодействуют с ней только через закрытый промежуточный
|
||||||
|
интерфейс (прокси), опять же, без открытия в свободный публичный доступ как
|
||||||
|
самой программы, так и прокси.
|
||||||
|
|
||||||
|
Сетевая Публичная Лицензия Vitastor разработана специально, чтобы
|
||||||
|
гарантировать, что в таких случаях и модифицированная версия программы, и
|
||||||
|
прокси останутся доступными сообществу. Для этого лицензия требует от
|
||||||
|
операторов сетевых серверов предоставлять исходный код оригинальной программы,
|
||||||
|
а также всех других программ, взаимодействующих с ней на их серверах,
|
||||||
|
пользователям этих серверов, на условиях свободных лицензий. Таким образом,
|
||||||
|
публичное использование изменённой версии ПО на сервере, прямо или косвенно
|
||||||
|
доступном пользователям, даёт пользователям доступ к исходным кодам изменённой
|
||||||
|
версии.
|
||||||
|
|
||||||
|
Детальные определения используемых терминов и описание условий копирования,
|
||||||
|
распространения и внесения изменений приведены ниже.
|
||||||
|
|
||||||
|
ТЕРМИНЫ И УСЛОВИЯ
|
||||||
|
|
||||||
|
0. Определения.
|
||||||
|
|
||||||
|
"Настоящая Лицензия" -- версия 1.1 Сетевой Публичной Лицензии Vitastor.
|
||||||
|
|
||||||
|
Под "Авторским правом" понимаются все законы, сходные с авторско-правовыми,
|
||||||
|
которые применяются к любым видам работ, например, к топологиям микросхем.
|
||||||
|
|
||||||
|
Термином "Программа" обозначается любое охраноспособное произведение,
|
||||||
|
используемое в соответствии с настоящей Лицензией. Лицензиат именуется "Вы".
|
||||||
|
"Лицензиаты" и "получатели" могут быть как физическими лицами, так и
|
||||||
|
организациями.
|
||||||
|
|
||||||
|
"Внесение изменений" в произведение означает копирование или адаптацию
|
||||||
|
произведения целиком или в части, способом, требующим разрешения
|
||||||
|
правообладателя, за исключением изготовления его точной копии. Получившееся
|
||||||
|
произведение называется "измененной версией" предыдущего произведения или
|
||||||
|
произведением, "основанным на" более ранней работе.
|
||||||
|
|
||||||
|
Термином "Лицензионное произведение" обозначается неизмененная Программа или
|
||||||
|
произведение, основанное на Программе.
|
||||||
|
|
||||||
|
"Распространение" произведения означает совершение с ним действий, которые
|
||||||
|
при отсутствии разрешения сделают Вас прямо или косвенно ответственным за
|
||||||
|
нарушение действующего закона об авторском праве, за исключением запуска на
|
||||||
|
компьютере или изменения копии, созданной в личных целях. Распространение
|
||||||
|
включает в себя копирование, раздачу копий (с изменениями или без них),
|
||||||
|
доведение до всеобщего сведения, а в некоторых странах -- и другие действия.
|
||||||
|
|
||||||
|
"Передача" произведения означает любой вид распространения, который позволяет
|
||||||
|
другим лицам создавать или получать копии произведения. Обычное взаимодействие
|
||||||
|
с пользователем через компьютерную сеть без создания копии передачей не
|
||||||
|
является.
|
||||||
|
|
||||||
|
Интерактивный интерфейс пользователя должен отображать "Информация об
|
||||||
|
авторском праве", достаточную для того, чтобы (1) обеспечить отображение
|
||||||
|
соответствующего уведомления об авторских правах и (2) сообщить пользователю
|
||||||
|
о том, что ему не предоставляются никакие гарантии на произведение (за
|
||||||
|
исключением явным образом предоставленных гарантий), о том, что лицензиаты
|
||||||
|
могут передавать произведение на условиях, описанных в настоящей Лицензии,
|
||||||
|
а также о том, как ознакомиться с текстом настоящей Лицензии. Если интерфейс
|
||||||
|
предоставляет собой список пользовательских команд или настроек, наподобие
|
||||||
|
меню, это требование считается выполненным при наличии явно выделенного
|
||||||
|
пункта в таком меню.
|
||||||
|
|
||||||
|
1. Исходный текст.
|
||||||
|
|
||||||
|
Под "Исходным текстом" понимается произведение в форме, которая более всего
|
||||||
|
подходит для внесения в него изменений. "Объектным кодом" называется
|
||||||
|
произведение в любой иной форме.
|
||||||
|
|
||||||
|
"Стандартный интерфейс" -- интерфейс, который либо является общепринятым
|
||||||
|
стандартом, введенным общепризнанным органом по стандартизации, либо, в случае
|
||||||
|
интерфейсов, характерных для конкретного языка программирования -- тот,
|
||||||
|
который широко используется разработчиками, пишущими программы на этом языке.
|
||||||
|
|
||||||
|
"Системные библиотеки" исполняемого произведения включают в себя то, что не
|
||||||
|
относится к произведению в целом и при этом (a) входит в обычный комплект
|
||||||
|
Основного компонента, но при этом не является его частью и (b) служит только
|
||||||
|
для обеспечения работы с этим Основным компонентом или для реализации
|
||||||
|
Стандартного интерфейса, для которого существует общедоступная реализация,
|
||||||
|
опубликованная в виде исходного текста. "Основным компонентом" в данном
|
||||||
|
контексте назван главный существенный компонент (ядро, оконная система и т.д.)
|
||||||
|
определенной операционной системы (если она используется), под управлением
|
||||||
|
которой функционирует исполняемое произведение, либо компилятор, используемый
|
||||||
|
для создания произведения или интерпретатор объектного кода, используемый для
|
||||||
|
его запуска.
|
||||||
|
|
||||||
|
"Полный исходный текст" для произведения в форме объектного кода -- весь
|
||||||
|
исходный текст, необходимый для создания, установки и (для исполняемого
|
||||||
|
произведения) функционирования объектного кода, а также модификации
|
||||||
|
произведения, включая сценарии, контролирующие эти действия. Однако он не
|
||||||
|
включает в себя Системные библиотеки, необходимые для функционирования
|
||||||
|
произведения, инструменты общего назначения или общедоступные свободные
|
||||||
|
программы, которые используются в неизменном виде для выполнения этих
|
||||||
|
действий, но не являются частью произведения. Полный исходный текст включает
|
||||||
|
в себя, например, файлы описания интерфейса, прилагаемые к файлам исходного
|
||||||
|
текста произведения, а также исходные тексты общих библиотек и динамически
|
||||||
|
связанных подпрограмм, которые требуются для функционирования произведения
|
||||||
|
и разработаны специально для этого, например, для прямой передачи данных
|
||||||
|
или управления потоками между этими подпрограммами и другими частями
|
||||||
|
произведения. Полный исходный текст не включает в себя то, что пользователи
|
||||||
|
могут сгенерировать автоматически из других частей Полного исходного текста.
|
||||||
|
Полным исходным текстом для произведения в форме исходных текстов является
|
||||||
|
само это произведение.
|
||||||
|
|
||||||
|
2. Основные права.
|
||||||
|
|
||||||
|
Все права, предоставленные на основании настоящей Лицензии, действуют в
|
||||||
|
течение срока действия авторских прав на Программу и не могут быть отозваны
|
||||||
|
при условии, что сформулированные в ней условия соблюдены. Настоящая Лицензия
|
||||||
|
однозначно подтверждает Ваши неограниченные права на запуск неизмененной
|
||||||
|
Программы. Настоящая Лицензия распространяется на результаты функционирования
|
||||||
|
Лицензионного произведения только в том случае, если они, учитывая их
|
||||||
|
содержание, сами являются частью Лицензионного произведения. Настоящая
|
||||||
|
Лицензия подтверждает Ваши права на свободное использование произведения
|
||||||
|
или другие аналогичные полномочия, предусмотренные действующим
|
||||||
|
законодательством об авторском праве.
|
||||||
|
|
||||||
|
Если Вы не осуществляете обычную передачу Лицензионного произведения, то
|
||||||
|
можете как угодно создавать, запускать и распространять его копии до тех пор,
|
||||||
|
пока ваша Лицензия сохраняет силу. Вы можете передавать Лицензионные
|
||||||
|
произведения третьим лицам исключительно для того, чтобы они внесли в них
|
||||||
|
изменения для Вас или предоставили Вам возможность их запуска, при условии,
|
||||||
|
что Вы соглашаетесь с условиями настоящей Лицензии при передаче всех
|
||||||
|
материалов, авторскими правами на которые Вы не обладаете. Лица, создающие
|
||||||
|
или запускающие Лицензионные произведения для Вас, должны делать это
|
||||||
|
исключительно от Вашего имени, под Вашим руководством и контролем, на
|
||||||
|
условиях, которые запрещают им создание без Вашей санкции каких-либо копий
|
||||||
|
материалов, на которые Вы обладаете авторским правом.
|
||||||
|
|
||||||
|
Любая другая передача разрешается исключительно при соблюдении описанных
|
||||||
|
ниже условий. Сублицензирование не допускается; раздел 10 делает его не нужным.
|
||||||
|
|
||||||
|
3. Защита прав пользователей от законов, запрещающих обход технических средств.
|
||||||
|
|
||||||
|
Ни одно Лицензионное произведение не должно считаться содержащим эффективные
|
||||||
|
технические средства, удовлетворяющие требованиям любого действующего закона,
|
||||||
|
принятого для исполнения обязательств, предусмотренных статьей 11 Договора ВОИС
|
||||||
|
по авторскому праву от 20 декабря 1996 года или аналогичных законов,
|
||||||
|
запрещающих или ограничивающих обход таких технических средств.
|
||||||
|
|
||||||
|
При передаче Лицензионного произведения Вы отказываетесь от всех
|
||||||
|
предоставляемых законом полномочий по запрету обхода технических средств,
|
||||||
|
используемых авторами в связи с осуществлением их прав, признавая, что такой
|
||||||
|
обход находится в рамках осуществления прав на использование Лицензионного
|
||||||
|
произведения, предоставленных настоящей Лицензией; также Вы отказываетесь
|
||||||
|
от любых попыток ограничить функционирование произведения или внесение в него
|
||||||
|
изменений, направленных на реализацию предоставленных Вам законом прав на
|
||||||
|
запрет пользователю обхода технических средств.
|
||||||
|
|
||||||
|
4. Передача неизмененных копий.
|
||||||
|
|
||||||
|
Вы можете передавать точные копии исходных текстов Программы в том виде,
|
||||||
|
в котором Вы их получили, на любом носителе, при условии, что Вы прилагаете
|
||||||
|
к каждой копии соответствующее уведомление об авторских правах способом,
|
||||||
|
обеспечивающим ознакомление с ним пользователя; сохраняете все уведомления
|
||||||
|
о том, что к тексту применима настоящая Лицензия и любые ограничения,
|
||||||
|
добавленные в соответствии с разделом 7; сохраняете все уведомления об
|
||||||
|
отсутствии каких-либо гарантий; предоставляете всем получателям вместе с
|
||||||
|
Программой копию настоящей Лицензии.
|
||||||
|
|
||||||
|
Вы можете установить любую цену за каждую копию, которую Вы передаете,
|
||||||
|
или распространять копии бесплатно; также Вы можете предложить поддержку
|
||||||
|
или гарантию за отдельную плату.
|
||||||
|
|
||||||
|
5. Передача измененных исходных текстов.
|
||||||
|
|
||||||
|
Вы можете передавать исходный текст произведения, основанного на Программе,
|
||||||
|
или изменений, необходимых для того, чтобы получить его из Программы, на
|
||||||
|
условиях, описанных в разделе 4, при соблюдении следующих условий:
|
||||||
|
|
||||||
|
а) Произведение должно содержать уведомления о произведенных Вами
|
||||||
|
изменениях с указанием их даты, сделанные способом, обеспечивающим
|
||||||
|
ознакомление с ними пользователя.
|
||||||
|
|
||||||
|
b) Произведение должно содержать уведомление о том, что оно
|
||||||
|
распространяется на условиях настоящей Лицензии, а также об условиях,
|
||||||
|
добавленных в соответствии с разделом 7, сделанное способом,
|
||||||
|
обеспечивающим ознакомление с ним пользователя. Данное требование имеет
|
||||||
|
приоритет над требованиями раздела 4 "оставлять нетронутыми все
|
||||||
|
уведомления".
|
||||||
|
|
||||||
|
c) Вы должны передать на условиях настоящей Лицензии всю работу целиком
|
||||||
|
любому лицу, которое приобретает копию. Таким образом, настоящая Лицензия
|
||||||
|
вместе с любыми применимыми условиями раздела 7 будет применяться к
|
||||||
|
произведению в целом и всем его частям, независимо от их комплектности.
|
||||||
|
Настоящая Лицензия не дает права на лицензирование произведения на любых
|
||||||
|
других условиях, но это не лишает законной силы такое разрешение, если Вы
|
||||||
|
получили его отдельно.
|
||||||
|
|
||||||
|
d) Если произведение имеет интерактивные пользовательские интерфейсы,
|
||||||
|
каждый из них должен отображать Информацию об авторском праве; однако,
|
||||||
|
если Программа имеет пользовательские интерфейсы, которые не отображают
|
||||||
|
информацию об авторском праве, от Вашего произведения этого также не
|
||||||
|
требуется.
|
||||||
|
|
||||||
|
Включение Лицензионного произведения в подборку на разделе хранения данных
|
||||||
|
или на носителе, на котором распространяется произведение, вместе с другими
|
||||||
|
отдельными самостоятельными произведениями, которые по своей природе не
|
||||||
|
являются переработкой Лицензионного произведения и не объединены с ним,
|
||||||
|
например, в программный комплекс, называется "набором", если авторские права
|
||||||
|
на подборку не используются для ограничения доступа к ней или законных прав
|
||||||
|
её пользователей сверх того, что предусматривают лицензии на отдельные
|
||||||
|
произведения. Включение Лицензионного произведения в набор не влечет применения
|
||||||
|
положений настоящей Лицензии к остальным его частям.
|
||||||
|
|
||||||
|
6. Передача произведения в формах, не относящихся к исходному тексту.
|
||||||
|
|
||||||
|
Вы можете передавать Лицензионное произведение в виде объектного кода в
|
||||||
|
соответствии с положениями разделов 4 и 5, при условии, что Вы также передаете
|
||||||
|
машиночитаемый Полный исходный текст в соответствии с условиями настоящей
|
||||||
|
Лицензии, одним из следующих способов:
|
||||||
|
|
||||||
|
а) Передавая объектный код или содержащий его материальный продукт (включая
|
||||||
|
распределенный материальный носитель), с приложением Полного исходного
|
||||||
|
текста наматериальном носителе, обычно используемом для обмена программным
|
||||||
|
обеспечением.
|
||||||
|
|
||||||
|
b) Передавая объектный код или содержащий его материальный продукт (включая
|
||||||
|
носитель, на котором распространяется произведение), с письменным
|
||||||
|
предложением, действительным в течение не менее трех лет либо до тех пор,
|
||||||
|
пока Вы предоставляете запасные части или поддержку для данного продукта,
|
||||||
|
о передаче любому обладателю объектного кода (1) копии Полного исходного
|
||||||
|
текста для всего программного обеспечения, содержащегося в продукте, на
|
||||||
|
которое распространяется действие настоящей Лицензии, на физическом
|
||||||
|
носителе, обычно используемом для обмена программным обеспечением, по цене,
|
||||||
|
не превышающей разумных затрат на передачу копии, или (2) доступа к Полному
|
||||||
|
исходному тексту с возможностью его копирования с сетевого сервера без
|
||||||
|
взимания платы.
|
||||||
|
|
||||||
|
с) Передавая отдельные копии объектного кода с письменной копией предложения
|
||||||
|
о предоставлении Полного исходного текста. Этот вариант допускается только
|
||||||
|
в отдельных случаях при распространении без извлечения прибыли, и только
|
||||||
|
если Вы получили объектный код с таким предложением в соответствии
|
||||||
|
с пунктом 6b.
|
||||||
|
|
||||||
|
d) Передавая объектный код посредством предоставления доступа к нему по
|
||||||
|
определенному адресу (бесплатно или за дополнительную плату), и предлагая
|
||||||
|
эквивалентный доступ к Полному исходному тексту таким же способом по тому же
|
||||||
|
адресу без какой-либо дополнительной оплаты. От Вас не требуется принуждать
|
||||||
|
получателей копировать Полный исходный текст вместе с объектным кодом. Если
|
||||||
|
объектный код размещен на сетевом сервере, Полный исходный текст может
|
||||||
|
находиться на другом сервере (управляемом Вами или третьим лицом), который
|
||||||
|
предоставляет аналогичную возможность копирования; при этом Вы должны четко
|
||||||
|
указать рядом с объектным кодом способ получения Полного исходного текста.
|
||||||
|
Независимо от того, на каком сервере расположен Полный исходный текст, Вы
|
||||||
|
обязаны убедиться в том, что он будет распространяться в течение времени,
|
||||||
|
необходимого для соблюдения этих требований.
|
||||||
|
|
||||||
|
e) Передавая объектный код с использованием одноранговой (пиринговой) сети,
|
||||||
|
при условии информирования других пользователей сети о том, где можно
|
||||||
|
бесплатно получить объектный код и Полный исходный текст произведения
|
||||||
|
способом, описанным в пункте 6d.
|
||||||
|
|
||||||
|
Не нужно включать в передаваемый объектный код его отделимые части, исходные
|
||||||
|
тексты которых не входят в состав Полного исходного текста, такие как Системные
|
||||||
|
библиотеки.
|
||||||
|
|
||||||
|
"Потребительский товар" это либо (1) "товар, предназначенный для личных нужд",
|
||||||
|
под которым понимается любое материальное личное имущество, которое обычно
|
||||||
|
используется для личных, семейных или домашних целей, или (2) что-либо
|
||||||
|
спроектированное или продающееся для использования в жилище. При определении
|
||||||
|
того, предназначен ли товар для личных нужд, сомнения должны толковаться в
|
||||||
|
пользу положительного ответа на этот вопрос. Применительно к конкретному
|
||||||
|
товару, используемому конкретным пользователем, под выражением "обычно
|
||||||
|
используется" имеется в виду способ, которым данный вид товаров преимущественно
|
||||||
|
или как правило используется, независимо от статуса конкретного пользователя
|
||||||
|
или способа, которым конкретный пользователь использует, предполагает или
|
||||||
|
будет использовать товар. Товар относится к предназначенным для личных нужд
|
||||||
|
независимо от того, насколько часто он используется в коммерческой
|
||||||
|
деятельности, промышленности или иной сфере, не относящейся к личным нуждам,
|
||||||
|
за исключением случая, когда использование в этой сфере представляет собой
|
||||||
|
единственный основной способ использования такого товара.
|
||||||
|
|
||||||
|
"Информация, необходимая для установки" Потребительского товара -- любые
|
||||||
|
методы, процедуры, сведения, необходимые для авторизации, или другая
|
||||||
|
информация, необходимая для установки и запуска в Потребительском товаре
|
||||||
|
измененных версий Лицензионного произведения, полученных при изменении
|
||||||
|
Полного исходного текста. Данная информация должна быть достаточной для
|
||||||
|
того, чтобы обеспечить возможность внесения в исходный текст изменений,
|
||||||
|
не приводящих к ограничению или нарушению его дальнейшей работоспособности.
|
||||||
|
|
||||||
|
Если вместе с Потребительским товаром или специально для использования
|
||||||
|
в нём Вы передаете произведение в виде объектного кода на условиях, описанных
|
||||||
|
в данном разделе, и такая передача является частью сделки, по которой право
|
||||||
|
владения и пользования Потребительским товаром переходит к получателю
|
||||||
|
пожизненно или на определенный срок (независимо от признаков сделки), Полный
|
||||||
|
исходный текст, передаваемый согласно данному разделу, должен сопровождаться
|
||||||
|
Информацией, необходимой для установки. Но это требование не применяется,
|
||||||
|
если ни Вы, ни какое-либо третье лицо не сохраняет за собой возможности
|
||||||
|
установки измененного объектного кода на Потребительский товар (например,
|
||||||
|
произведение было установлено в постоянную память).
|
||||||
|
|
||||||
|
Требование о предоставлении Информации, необходимой для установки, не
|
||||||
|
включает в себя требование продолжения оказания услуг по поддержке,
|
||||||
|
предоставления гарантии или обновлений для произведения, которое было изменено
|
||||||
|
или установлено получателем, либо для Потребительского товара, в котором оно
|
||||||
|
было изменено или на который оно было установлено. В доступе к сети может быть
|
||||||
|
отказано, если само внесение изменений существенно и негативно влияет на
|
||||||
|
работу сети, нарушает правила обмена данными или не поддерживает протоколы для
|
||||||
|
обмена данными по сети.
|
||||||
|
|
||||||
|
Передаваемый в соответствии с данным разделом Полный исходный текст и
|
||||||
|
предоставленная Информация, необходимая для установки, должны быть записаны в
|
||||||
|
формате, который имеет общедоступное описание (и общедоступную реализацию,
|
||||||
|
опубликованную в форме исходного текста) и не должны требовать никаких
|
||||||
|
специальных паролей или ключей для распаковки, чтения или копирования.
|
||||||
|
|
||||||
|
7. Дополнительные условия.
|
||||||
|
|
||||||
|
"Дополнительными разрешениями" называются условия, которые дополняют условия
|
||||||
|
настоящей Лицензии, делая исключения из одного или нескольких её положений.
|
||||||
|
Дополнительные разрешения, которые применимы ко всей Программе, должны
|
||||||
|
рассматриваться как часть настоящей Лицензии, в той степени, в которой они
|
||||||
|
соответствуют действующему законодательству. Если дополнительные разрешения
|
||||||
|
применяются только к части Программы, эта часть может быть использована отдельно
|
||||||
|
на измененных условиях, но вся Программа продолжает использоваться на условиях
|
||||||
|
настоящей Лицензии без учета дополнительных разрешений.
|
||||||
|
|
||||||
|
Когда Вы передаете копию Лицензионного произведения, Вы можете по своему
|
||||||
|
усмотрению исключить любые дополнительные разрешения, примененные к этой копии
|
||||||
|
или к любой её части. (Для дополнительных разрешений может быть заявлено
|
||||||
|
требование об их удалении в определенных случаях, когда Вы вносите изменения в
|
||||||
|
произведение.) Вы можете добавлять дополнительные разрешения к добавленным Вами
|
||||||
|
в Лицензионное произведение материалам, на которые Вы обладаете авторскими
|
||||||
|
правами или правом выдачи соответствующего разрешения.
|
||||||
|
|
||||||
|
Независимо от любых других положений настоящей Лицензии, Вы можете дополнить
|
||||||
|
следующими условиями положения настоящей Лицензии в отношении материала,
|
||||||
|
добавленного к Лицензионному произведению (если это разрешено обладателями
|
||||||
|
авторских прав на материал):
|
||||||
|
|
||||||
|
a) отказом от гарантий или ограничением ответственности, отличающимися от
|
||||||
|
тех, что описаны в разделах 15 и 16 настоящей Лицензии; либо
|
||||||
|
|
||||||
|
b) требованием сохранения соответствующей информации о правах или об
|
||||||
|
авторстве материала, или включения её в Информацию об авторском праве,
|
||||||
|
отображаемую содержащим его произведением; либо
|
||||||
|
|
||||||
|
c) запретом на искажение информации об источнике происхождения материала
|
||||||
|
или требованием того, чтобы измененные версии такого материала содержали
|
||||||
|
корректную отметку об отличиях от исходной версии; либо
|
||||||
|
|
||||||
|
d) ограничением использования в целях рекламы имен лицензиаров или авторов
|
||||||
|
материала; либо
|
||||||
|
|
||||||
|
e) отказом от предоставления прав на использование в качестве товарных
|
||||||
|
знаков некоторых торговых наименований, товарных знаков или знаков
|
||||||
|
обслуживания; либо
|
||||||
|
|
||||||
|
f) требованием от каждого, кто по договору передает материал (или его
|
||||||
|
измененные версии), предоставления компенсации лицензиарам и авторам
|
||||||
|
материала в виде принятия на себя любой ответственности, которую этот
|
||||||
|
договор налагает на лицензиаров и авторов.
|
||||||
|
|
||||||
|
Все остальные ограничительные дополнительные условия считаются "дополнительными
|
||||||
|
запретами" по смыслу раздела 10. Если программа, которую Вы получили, или любая
|
||||||
|
её часть содержит уведомление о том, что наряду с настоящей Лицензией её
|
||||||
|
использование регулируется условием, относящимся к дополнительным запретам, Вы
|
||||||
|
можете удалить такое условие. Если лицензия содержит дополнительный запрет, но
|
||||||
|
допускает лицензирование на измененных условиях или передачу в соответствии с
|
||||||
|
настоящей Лицензией, Вы можете добавить к Лицензионному произведению материал,
|
||||||
|
используемый на условиях такой лицензии, в том случае, если дополнительный
|
||||||
|
запрет не сохраняется при таком изменении условий лицензии или передаче.
|
||||||
|
|
||||||
|
Если Вы добавляете условия для использования Лицензионного произведения в
|
||||||
|
соответствии с настоящим разделом, Вы должны поместить в соответствующих файлах
|
||||||
|
исходного текста уведомление о том, что к этим файлам применяются дополнительные
|
||||||
|
условия, или указание на то, как ознакомиться с соответствующими условиями.
|
||||||
|
|
||||||
|
Дополнительные разрешающие или ограничивающие условия могут быть сформулированы
|
||||||
|
в виде отдельной лицензии или зафиксированы как исключения; вышеуказанные
|
||||||
|
требования применяются в любом случае.
|
||||||
|
|
||||||
|
8. Прекращение действия.
|
||||||
|
|
||||||
|
Вы не можете распространять Лицензионное произведение или вносить в него
|
||||||
|
изменения на условиях, отличающихся от явно оговоренных в настоящей Лицензии.
|
||||||
|
Любая попытка распространения или внесения изменений на иных условиях является
|
||||||
|
ничтожной и автоматически прекращает Ваши права, полученные по настоящей
|
||||||
|
Лицензии (включая лицензию на любые патенты, предоставленные согласно третьему
|
||||||
|
пункту раздела 11).
|
||||||
|
|
||||||
|
Тем не менее если Вы прекращаете нарушение настоящей Лицензии, Ваши права,
|
||||||
|
полученные от конкретного правообладателя, восстанавливаются (а) временно, до
|
||||||
|
тех пор пока правообладатель явно и окончательно не прекратит действие Ваших
|
||||||
|
прав, и (б) навсегда, если правообладатель не уведомит Вас о нарушении с помощью
|
||||||
|
надлежащих средств в течение 60 дней после прекращения нарушений.
|
||||||
|
|
||||||
|
Кроме того, Ваши права, полученные от конкретного правообладателя,
|
||||||
|
восстанавливаются навсегда, если правообладатель впервые любым подходящим
|
||||||
|
способом уведомляет Вас о нарушении настоящей Лицензии на свое произведение (для
|
||||||
|
любого произведения) и Вы устраняете нарушение в течение 30 дней после получения
|
||||||
|
уведомления.
|
||||||
|
|
||||||
|
Прекращение Ваших прав, описанное в настоящем разделе, не прекращает действие
|
||||||
|
лицензий лиц, которые получили от Вас копии произведения или права,
|
||||||
|
предоставляемые настоящей Лицензией. Если Ваши права были прекращены навсегда и
|
||||||
|
не восстановлены, Вы не можете вновь получить право на тот же материал на
|
||||||
|
условиях, описанных в разделе 10.
|
||||||
|
|
||||||
|
9. Акцепт не требуется для получения копий.
|
||||||
|
|
||||||
|
Вы не обязаны принимать условия настоящей Лицензии для того, чтобы получить или
|
||||||
|
запустить копию Программы. Случайное распространение Лицензионного произведения,
|
||||||
|
происходящее вследствие использования одноранговой (пиринговой) сети для
|
||||||
|
получения его копии, также не требует принятия этих условий. Тем не менее только
|
||||||
|
настоящая Лицензия дает Вам право распространять или изменять любое Лицензионное
|
||||||
|
произведение. Если Вы не приняли условия настоящей Лицензии, такие действия
|
||||||
|
будут нарушением авторского права. Поэтому изменяя или распространяя
|
||||||
|
Лицензионное произведение, Вы выражаете согласие с условиями настоящей Лицензии.
|
||||||
|
|
||||||
|
10. Автоматическое получение прав последующими получателями.
|
||||||
|
|
||||||
|
Каждый раз, когда Вы передаете Лицензионное произведение, получатель
|
||||||
|
автоматически получает от его лицензиара право запускать, изменять и
|
||||||
|
распространять это произведение при условии соблюдения настоящей Лицензии. Вы не
|
||||||
|
несете ответственности за соблюдение третьими лицами условий настоящей Лицензии.
|
||||||
|
|
||||||
|
"Реорганизацией" называются действия, в результате которых передается управление
|
||||||
|
организацией или значительная часть её активов, а также происходит разделение
|
||||||
|
или слияние организаций. Если распространение Лицензионного произведения
|
||||||
|
является результатом реорганизации, каждая из сторон сделки, получающая копию
|
||||||
|
произведения, также получает все права на произведение, которые предшествующее
|
||||||
|
юридическое лицо имело или могло предоставить согласно предыдущему абзацу, а
|
||||||
|
также право на владение Полным исходным текстом произведения от предшественника,
|
||||||
|
осуществляемое в его интересах, если предшественник владеет им или может
|
||||||
|
получить его при разумных усилиях.
|
||||||
|
|
||||||
|
Вы не можете налагать каких-либо дополнительных ограничений на осуществление
|
||||||
|
прав, предоставленных или подтвержденных в соответствии с настоящей Лицензией.
|
||||||
|
Например, Вы не можете ставить осуществление прав, предоставленных по настоящей
|
||||||
|
Лицензии, в зависимость от оплаты отчислений, роялти или других сборов; также Вы
|
||||||
|
не можете инициировать судебный процесс (включая встречный иск или заявление
|
||||||
|
встречного требования в судебном процессе) о нарушении любых патентных прав при
|
||||||
|
создании, использовании, продаже, предложении продажи, импорте Программы или
|
||||||
|
любой её части.
|
||||||
|
|
||||||
|
11. Патенты.
|
||||||
|
|
||||||
|
"Инвестором" называется правообладатель, разрешающий использование Программы
|
||||||
|
либо произведения, на котором основана Программа, на условиях настоящей
|
||||||
|
Лицензии. Произведение, лицензированное таким образом, называется "версией со
|
||||||
|
вкладом" инвестора.
|
||||||
|
|
||||||
|
"Неотъемлемые патентные претензии" инвестора -- все патентные права,
|
||||||
|
принадлежащие инвестору или контролируемые им в настоящее время либо
|
||||||
|
приобретенные в будущем, которые могут быть нарушены созданием, использованием
|
||||||
|
или продажей версии со вкладом, допускаемыми настоящей Лицензией; они не
|
||||||
|
включают в себя права, которые будут нарушены исключительно вследствие будущих
|
||||||
|
изменений версии со вкладом. Для целей данного определения под "контролем"
|
||||||
|
понимается право выдавать патентные сублицензии способами, не нарушающими
|
||||||
|
требований настоящей Лицензии.
|
||||||
|
|
||||||
|
Каждый инвестор предоставляет Вам неисключительную безвозмездную лицензию на
|
||||||
|
патент, действующую во всем мире, соответствующую неотъемлемым патентным
|
||||||
|
претензиям инвестора, на создание, использование, продажу, предложение для
|
||||||
|
продажи, импорт, а также запуск, внесение изменений и распространение всего, что
|
||||||
|
входит в состав версии со вкладом.
|
||||||
|
|
||||||
|
В следующих трех абзацах "лицензией на патент" называется любое явно выраженное
|
||||||
|
вовне согласие или обязательство не применять патент (например, выдача
|
||||||
|
разрешения на использование запатентованного объекта или обещание не подавать в
|
||||||
|
суд за нарушение патента). "Выдать" кому-то такую лицензию на патент означает
|
||||||
|
заключить такое соглашение или обязаться не применять патент против него.
|
||||||
|
|
||||||
|
Если Вы передаете Лицензионное произведение, сознательно основываясь на лицензии
|
||||||
|
на патент, в то время как Полный исходный текст произведения невозможно
|
||||||
|
бесплатно скопировать с общедоступного сервера или другим не вызывающим
|
||||||
|
затруднений способом, Вы должны либо (1) обеспечить возможность такого доступа к
|
||||||
|
Полному исходному тексту, либо (2) отказаться от прав, предоставленных по
|
||||||
|
лицензии на патент для данного произведения, либо (3) принять меры по передаче
|
||||||
|
лицензии на патент последующим получателям произведения, в соответствии с
|
||||||
|
требованиями настоящей Лицензии. "Сознательно основываясь" означает, что Вы
|
||||||
|
знаете, что при отсутствии лицензии на патент передача Вами Лицензионного
|
||||||
|
произведения в определенной стране или использование получателем переданного ему
|
||||||
|
Вами Лицензионного произведения в этой стране нарушит один или несколько
|
||||||
|
определенных патентов этой страны, срок действия которых не истек.
|
||||||
|
|
||||||
|
Если в соответствии или в связи с единичной сделкой либо соглашением Вы
|
||||||
|
передаете или делаете заказ на распространение Лицензионного произведения, и
|
||||||
|
предоставляете определенным лицам, получающим Лицензионное произведение,
|
||||||
|
лицензию на патент, разрешающую им использовать, распространять, вносить
|
||||||
|
изменения или передавать конкретные экземпляры Лицензионного произведения,
|
||||||
|
права, которые Вы предоставляете по лицензии на патент, автоматически переходят
|
||||||
|
ко всем получателям Лицензионного произведения и произведений, созданных на его
|
||||||
|
основе.
|
||||||
|
|
||||||
|
Патентная лицензия называется "дискриминирующей", если она не покрывает,
|
||||||
|
запрещает осуществление или содержит в качестве условия отказ от применения
|
||||||
|
одного или нескольких прав, предоставленных настоящей Лицензией. Вы не можете
|
||||||
|
передавать Лицензионное произведение, если Вы являетесь участником договора с
|
||||||
|
третьим лицом, осуществляющим распространение программного обеспечения, в
|
||||||
|
соответствии с которым Вы делаете в пользу третьего лица выплаты, размер которых
|
||||||
|
зависит от масштабов Вашей деятельности по передаче произведения, и в
|
||||||
|
соответствии с которым любое третье лицо, получающее от Вас Лицензионное
|
||||||
|
произведение, делает это на условиях дискриминирующей патентной лицензии (а)
|
||||||
|
которая зависит от количества копий Лицензионного произведения, переданных Вами
|
||||||
|
(или копий, сделанных с этих копий), или (b) которая используется
|
||||||
|
преимущественно в конкретных товарах или подборках, содержащих Лицензионное
|
||||||
|
произведение, или в связи с ними, в том случае, если Вы заключили данный договор
|
||||||
|
или получили лицензию на патент после 28 марта 2007 года.
|
||||||
|
|
||||||
|
Ничто в настоящей Лицензии не должно толковаться как исключение или ограничение
|
||||||
|
любого предполагаемого права или других способов противодействия нарушениям,
|
||||||
|
которые во всем остальном могут быть доступны для Вас в соответствии с
|
||||||
|
применимым патентным правом.
|
||||||
|
|
||||||
|
12. Запрет отказывать в свободе другим.
|
||||||
|
|
||||||
|
Если на Вас наложены обязанности (будь то по решению суда, договору или иным
|
||||||
|
способом), которые противоречат условиям настоящей Лицензии, это не освобождает
|
||||||
|
Вас от соблюдения её условий. Если Вы не можете передать Лицензионное
|
||||||
|
произведение так, чтобы одновременно выполнять Ваши обязательства по настоящей
|
||||||
|
Лицензии и любые другие относящиеся к делу обязательства, то Вы не можете
|
||||||
|
передавать его вообще. Например, если Вы согласны с условием, обязывающими Вас
|
||||||
|
производить сбор отчислений за дальнейшую передачу от тех, кому Вы передаете
|
||||||
|
Программу, то для того, чтобы соблюсти это условие и выполнить требования
|
||||||
|
настоящей Лицензии, Вы должны полностью воздержаться от передачи Программы.
|
||||||
|
|
||||||
|
13. Удаленное сетевое взаимодействие.
|
||||||
|
|
||||||
|
Под "Прокси-программой" понимается отдельная программа, специально
|
||||||
|
разработанная для использования совместно с Лицензионным произведением,
|
||||||
|
и взаимодействующая с ним прямо или косвенно через любой вид программного
|
||||||
|
интерфейса, компьютерную сеть, имитацию такой сети, или, в свою очередь,
|
||||||
|
через другую Прокси-программу.
|
||||||
|
|
||||||
|
Независимо от любых других положений настоящей Лицензии, если вы
|
||||||
|
предоставляете любому пользователю возможность взаимодействовать с Лицензионным
|
||||||
|
произведением через компьютерную сеть, имитацию такой сети, или через любое
|
||||||
|
количество "Прокси-программ", вы должны в явной форме предложить этому
|
||||||
|
пользователю возможность получить Полный исходный текст Лицензионного
|
||||||
|
произведения и всех Прокси-программ путём предоставления доступа к нему
|
||||||
|
с сетевого сервера без взимания платы, посредством стандартных или
|
||||||
|
традиционных способов, используемых для копирования программного обеспечения.
|
||||||
|
Полный исходный текст Лицензионного произведения должен предоставляться
|
||||||
|
пользователю на условиях настоящей Лицензии, а Полный исходный текст
|
||||||
|
Прокси-программ должен предоставляться пользователю либо на условиях настоящей
|
||||||
|
Лицензии, либо на условиях одной из свободных лицензий, совместимых с
|
||||||
|
Генеральной публичной Лицензией GNU, перечисленных Фондом Свободного
|
||||||
|
Программного Обеспечения в списке под названием "Лицензии свободных программ,
|
||||||
|
совместимые с GPL".
|
||||||
|
|
||||||
|
14. Пересмотренные редакции настоящей Лицензии.
|
||||||
|
|
||||||
|
Автор настоящей Лицензии время от времени может публиковать пересмотренные
|
||||||
|
и (или) новые редакции Сетевой Публичной Лицензии Vitastor. Они будут аналогичны
|
||||||
|
по смыслу настоящей редакции, но могут отличаться от нее в деталях, направленных
|
||||||
|
на решение новых проблем или регулирование новых отношений.
|
||||||
|
|
||||||
|
Каждой редакции присваивается собственный номер. Если для Программы указано,
|
||||||
|
что к ней применима определенная редакция Сетевой Публичной Лицензии Vitastor
|
||||||
|
"или любая более поздняя редакция", у Вас есть возможность использовать термины
|
||||||
|
и условия, содержащиеся в редакции с указанным номером или любой более поздней
|
||||||
|
редакции, опубликованной автором настоящей Лицензии. Если для Программы не
|
||||||
|
указан номер редакции Сетевой Публичной Лицензии Vitastor, Вы можете выбрать
|
||||||
|
любую редакцию, опубликованную автором настоящей Лицензии.
|
||||||
|
|
||||||
|
Более поздние редакции Лицензии могут дать Вам дополнительные или принципиально
|
||||||
|
иные права. Тем не менее в результате Вашего выбора более поздней редакции на
|
||||||
|
автора или правообладателя не возлагается никаких дополнительных обязанностей.
|
||||||
|
|
||||||
|
15. Отказ от гарантий.
|
||||||
|
|
||||||
|
НА ПРОГРАММУ НЕ ПРЕДОСТАВЛЯЕТСЯ НИКАКИХ ГАРАНТИЙ ЗА ИСКЛЮЧЕНИЕМ ПРЕДУСМОТРЕННЫХ
|
||||||
|
ДЕЙСТВУЮЩИМ ЗАКОНОДАТЕЛЬСТВОМ. ЕСЛИ ИНОЕ НЕ УКАЗАНО В ПИСЬМЕННОЙ ФОРМЕ,
|
||||||
|
ПРАВООБЛАДАТЕЛИ И (ИЛИ) ТРЕТЬИ ЛИЦА ПРЕДОСТАВЛЯЮТ ПРОГРАММУ "КАК ЕСТЬ", БЕЗ
|
||||||
|
КАКИХ-ЛИБО ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ ГАРАНТИЙ, ВКЛЮЧАЯ ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ
|
||||||
|
КОНКРЕТНЫХ ЦЕЛЕЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. ВЕСЬ РИСК, СВЯЗАННЫЙ С КАЧЕСТВОМ И
|
||||||
|
ПРОИЗВОДИТЕЛЬНОСТЬЮ ПРОГРАММЫ, ВОЗЛАГАЕТСЯ НА ВАС. ЕСЛИ В ПРОГРАММЕ БУДУТ
|
||||||
|
ВЫЯВЛЕНЫ НЕДОСТАТКИ, ВЫ ПРИНИМАЕТЕ НА СЕБЯ СТОИМОСТЬ ВСЕГО НЕОБХОДИМОГО
|
||||||
|
ОБСЛУЖИВАНИЯ, РЕМОНТА ИЛИ ИСПРАВЛЕНИЯ.
|
||||||
|
|
||||||
|
16. Ограничение ответственности.
|
||||||
|
|
||||||
|
ЕСЛИ ИНОЕ НЕ ПРЕДУСМОТРЕНО ДЕЙСТВУЮЩИМ ЗАКОНОДАТЕЛЬСТВОМ ИЛИ СОГЛАШЕНИЕМ СТОРОН,
|
||||||
|
ЗАКЛЮЧЕННЫМ В ПИСЬМЕННОЙ ФОРМЕ, ПРАВООБЛАДАТЕЛЬ ИЛИ ИНОЕ ЛИЦО, КОТОРОЕ ВНОСИТ
|
||||||
|
ИЗМЕНЕНИЯ В ПРОГРАММУ И (ИЛИ) ПЕРЕДАЕТ ЕЁ НА УСЛОВИЯХ, СФОРМУЛИРОВАННЫХ ВЫШЕ, НЕ
|
||||||
|
МОЖЕТ НЕСТИ ОТВЕТСТВЕННОСТЬ ПЕРЕД ВАМИ ЗА ПРИЧИНЕННЫЙ УЩЕРБ, ВКЛЮЧАЯ УЩЕРБ
|
||||||
|
ОБЩЕГО ЛИБО КОНКРЕТНОГО ХАРАКТЕРА, ПРИЧИНЕННЫЙ СЛУЧАЙНО ИЛИ ЯВЛЯЮЩИЙСЯ
|
||||||
|
СЛЕДСТВИЕМ ИСПОЛЬЗОВАНИЯ ПРОГРАММЫ ЛИБО НЕВОЗМОЖНОСТИ ЕЁ ИСПОЛЬЗОВАНИЯ (В ТОМ
|
||||||
|
ЧИСЛЕ ЗА УНИЧТОЖЕНИЕ ИЛИ МОДИФИКАЦИЮ ИНФОРМАЦИИ, ЛИБО УБЫТКИ, ПОНЕСЕННЫЕ ВАМИ
|
||||||
|
ИЛИ ТРЕТЬИМИ ЛИЦАМИ, ЛИБО СБОИ ПРОГРАММЫ ПРИ ВЗАИМОДЕЙСТВИИ С ДРУГИМ ПРОГРАММНЫМ
|
||||||
|
ОБЕСПЕЧЕНИЕМ), В ТОМ ЧИСЛЕ И В СЛУЧАЯХ, КОГДА ПРАВООБЛАДАТЕЛЬ ИЛИ ТРЕТЬЕ ЛИЦО
|
||||||
|
ПРЕДУПРЕЖДЕНЫ О ВОЗМОЖНОСТИ ПРИЧИНЕНИЯ ТАКИХ УБЫТКОВ.
|
||||||
|
|
||||||
|
17. Толкование разделов 15 и 16.
|
||||||
|
|
||||||
|
Если отказ от гарантии и ограничение ответственности, представленные выше, по
|
||||||
|
закону не могут быть применены в соответствии с их условиями, суды,
|
||||||
|
рассматривающие спор, должны применить действующий закон, который в наибольшей
|
||||||
|
степени предусматривает абсолютный отказ от всей гражданской ответственности в
|
||||||
|
связи с Программой, за исключением случаев, когда гарантия или принятие на себя
|
||||||
|
ответственности за копию программы предоставляется за плату.
|
||||||
|
|
||||||
|
КОНЕЦ ОПРЕДЕЛЕНИЙ И УСЛОВИЙ
|
||||||
|
|
||||||
|
Порядок применения условий Лицензии к Вашим программам
|
||||||
|
|
||||||
|
Если Вы разрабатываете новую программу и хотите, чтобы её использование принесло
|
||||||
|
максимальную пользу обществу, наилучший способ достичь этого -- сделать её
|
||||||
|
свободной, чтобы все могли распространять и изменять её на условиях настоящей
|
||||||
|
Лицензии.
|
||||||
|
|
||||||
|
Для этого сделайте так, чтобы программа содержала в себе описанные ниже
|
||||||
|
уведомления. Самым надежным способом это сделать является включение их в начало
|
||||||
|
каждого файла исходного текста, чтобы наиболее эффективным образом сообщить об
|
||||||
|
отсутствии гарантий; каждый файл должен иметь по меньшей мере одну строку с
|
||||||
|
оповещением об авторских правах и указанием на то, где находится полный текст
|
||||||
|
уведомлений.
|
||||||
|
|
||||||
|
<Строка с названием Программы и информацией о её назначении.>
|
||||||
|
Copyright © <год выпуска программы в свет> <имя автора>
|
||||||
|
|
||||||
|
Эта программа является свободным программным обеспечением: Вы можете
|
||||||
|
распространять её и (или) изменять, соблюдая условия Сетевой Публичной
|
||||||
|
Лицензии Vitastor, опубликованной автором Vitastor, либо редакции 1.1
|
||||||
|
Лицензии, либо (на Ваше усмотрение) любой редакции, выпущенной позже.
|
||||||
|
|
||||||
|
Эта программа распространяется в расчете на то, что она окажется полезной,
|
||||||
|
но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, включая подразумеваемую гарантию КАЧЕСТВА либо
|
||||||
|
ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Ознакомьтесь с Сетевой Публичной
|
||||||
|
Лицензией Vitastor для получения более подробной информации.
|
||||||
|
|
||||||
|
Также добавьте информацию о том, как связаться с Вами посредством электронной
|
||||||
|
или обычной почты.
|
||||||
|
|
||||||
|
Если ваша программа взаимодействует с пользователями удаленно через
|
||||||
|
компьютерную сеть, Вы также должны убедиться, что обеспечили её пользователям
|
||||||
|
возможность получить её исходные тексты. Например, если Ваша программа является
|
||||||
|
веб-приложением, её интерфейс может отображать ссылку "Исходные коды", которая
|
||||||
|
указывает на архив с текстом. Существует много способов, которыми Вы можете
|
||||||
|
распространять исходные тексты, для разных программ подходят разные решения;
|
||||||
|
ознакомьтесь с разделом 13 для того, чтобы узнать конкретные требования.
|
@@ -61,7 +61,7 @@ modification follow.
|
|||||||
|
|
||||||
0. Definitions.
|
0. Definitions.
|
||||||
|
|
||||||
"This License" refers to version 1 of the Vitastor Network Public License.
|
"This License" refers to version 1.1 of the Vitastor Network Public License.
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
works, such as semiconductor masks.
|
works, such as semiconductor masks.
|
||||||
@@ -629,7 +629,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the Vitastor Network Public License as published by
|
it under the terms of the Vitastor Network Public License as published by
|
||||||
the Vitastor Author, either version 1 of the License, or
|
the Vitastor Author, either version 1.1 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
|
@@ -18,15 +18,19 @@ ENV CSI_ENDPOINT=""
|
|||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y wget && \
|
apt-get install -y wget && \
|
||||||
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
|
||||||
(echo deb http://vitastor.io/debian buster main > /etc/apt/sources.list.d/vitastor.list) && \
|
|
||||||
(echo deb http://deb.debian.org/debian buster-backports main > /etc/apt/sources.list.d/backports.list) && \
|
(echo deb http://deb.debian.org/debian buster-backports main > /etc/apt/sources.list.d/backports.list) && \
|
||||||
(echo "APT::Install-Recommends false;" > /etc/apt/apt.conf) && \
|
(echo "APT::Install-Recommends false;" > /etc/apt/apt.conf) && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y e2fsprogs xfsprogs vitastor kmod && \
|
apt-get install -y e2fsprogs xfsprogs kmod && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
||||||
|
|
||||||
COPY --from=build /app/vitastor-csi /bin/
|
COPY --from=build /app/vitastor-csi /bin/
|
||||||
|
|
||||||
|
RUN (echo deb http://vitastor.io/debian buster main > /etc/apt/sources.list.d/vitastor.list) && \
|
||||||
|
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y vitastor-client && \
|
||||||
|
apt-get clean
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/vitastor-csi"]
|
ENTRYPOINT ["/bin/vitastor-csi"]
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
VERSION ?= v0.7.0
|
VERSION ?= v0.8.5
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ spec:
|
|||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
allowPrivilegeEscalation: true
|
allowPrivilegeEscalation: true
|
||||||
image: vitalif/vitastor-csi:v0.7.0
|
image: vitalif/vitastor-csi:v0.8.5
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
@@ -102,7 +102,7 @@ spec:
|
|||||||
- "--health-port=9898"
|
- "--health-port=9898"
|
||||||
env:
|
env:
|
||||||
- name: CSI_ENDPOINT
|
- name: CSI_ENDPOINT
|
||||||
value: unix://csi/csi.sock
|
value: unix:///csi/csi.sock
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /csi
|
- mountPath: /csi
|
||||||
name: socket-dir
|
name: socket-dir
|
||||||
|
@@ -116,7 +116,7 @@ spec:
|
|||||||
privileged: true
|
privileged: true
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
image: vitalif/vitastor-csi:v0.7.0
|
image: vitalif/vitastor-csi:v0.8.5
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
@@ -5,7 +5,7 @@ package vitastor
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
vitastorCSIDriverName = "csi.vitastor.io"
|
vitastorCSIDriverName = "csi.vitastor.io"
|
||||||
vitastorCSIDriverVersion = "0.7.0"
|
vitastorCSIDriverVersion = "0.8.5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct fills the parameters of request or user input
|
// Config struct fills the parameters of request or user input
|
||||||
|
@@ -10,7 +10,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -21,8 +20,6 @@ import (
|
|||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"go.etcd.io/etcd/clientv3"
|
|
||||||
|
|
||||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -114,6 +111,34 @@ func GetConnectionParams(params map[string]string) (map[string]string, []string,
|
|||||||
return ctxVars, etcdUrl, etcdPrefix
|
return ctxVars, etcdUrl, etcdPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func invokeCLI(ctxVars map[string]string, args []string) ([]byte, error)
|
||||||
|
{
|
||||||
|
if (ctxVars["etcdUrl"] != "")
|
||||||
|
{
|
||||||
|
args = append(args, "--etcd_address", ctxVars["etcdUrl"])
|
||||||
|
}
|
||||||
|
if (ctxVars["etcdPrefix"] != "")
|
||||||
|
{
|
||||||
|
args = append(args, "--etcd_prefix", ctxVars["etcdPrefix"])
|
||||||
|
}
|
||||||
|
if (ctxVars["configPath"] != "")
|
||||||
|
{
|
||||||
|
args = append(args, "--config_path", ctxVars["configPath"])
|
||||||
|
}
|
||||||
|
c := exec.Command("/usr/bin/vitastor-cli", args...)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
c.Stdout = &stdout
|
||||||
|
c.Stderr = &stderr
|
||||||
|
err := c.Run()
|
||||||
|
stderrStr := string(stderr.Bytes())
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("vitastor-cli %s failed: %s, status %s\n", strings.Join(args, " "), stderrStr, err)
|
||||||
|
return nil, status.Error(codes.Internal, stderrStr+" (status "+err.Error()+")")
|
||||||
|
}
|
||||||
|
return stdout.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create the volume
|
// Create the volume
|
||||||
func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error)
|
func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error)
|
||||||
{
|
{
|
||||||
@@ -146,128 +171,41 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
|||||||
volSize = ((capRange.GetRequiredBytes() + MB - 1) / MB) * MB
|
volSize = ((capRange.GetRequiredBytes() + MB - 1) / MB) * MB
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: The following should PROBABLY be implemented externally in a management tool
|
ctxVars, etcdUrl, _ := GetConnectionParams(req.Parameters)
|
||||||
|
|
||||||
ctxVars, etcdUrl, etcdPrefix := GetConnectionParams(req.Parameters)
|
|
||||||
if (len(etcdUrl) == 0)
|
if (len(etcdUrl) == 0)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.InvalidArgument, "no etcdUrl in storage class configuration and no etcd_address in vitastor.conf")
|
return nil, status.Error(codes.InvalidArgument, "no etcdUrl in storage class configuration and no etcd_address in vitastor.conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to etcd
|
// Create image using vitastor-cli
|
||||||
cli, err := clientv3.New(clientv3.Config{
|
_, err := invokeCLI(ctxVars, []string{ "create", volName, "-s", string(volSize), "--pool", string(poolId) })
|
||||||
DialTimeout: ETCD_TIMEOUT,
|
|
||||||
Endpoints: etcdUrl,
|
|
||||||
})
|
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "failed to connect to etcd at "+strings.Join(etcdUrl, ",")+": "+err.Error())
|
if (strings.Index(err.Error(), "already exists") > 0)
|
||||||
}
|
|
||||||
defer cli.Close()
|
|
||||||
|
|
||||||
var imageId uint64 = 0
|
|
||||||
for
|
|
||||||
{
|
|
||||||
// Check if the image exists
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
resp, err := cli.Get(ctx, etcdPrefix+"/index/image/"+volName)
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "failed to read key from etcd: "+err.Error())
|
stat, err := invokeCLI(ctxVars, []string{ "ls", "--json", volName })
|
||||||
}
|
|
||||||
if (len(resp.Kvs) > 0)
|
|
||||||
{
|
|
||||||
kv := resp.Kvs[0]
|
|
||||||
var v InodeIndex
|
|
||||||
err := json.Unmarshal(kv.Value, &v)
|
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "invalid /index/image/"+volName+" key in etcd: "+err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
poolId = v.PoolId
|
var inodeCfg []InodeConfig
|
||||||
imageId = v.Id
|
err = json.Unmarshal(stat, &inodeCfg)
|
||||||
inodeCfgKey := fmt.Sprintf("/config/inode/%d/%d", poolId, imageId)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
resp, err := cli.Get(ctx, etcdPrefix+inodeCfgKey)
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "failed to read key from etcd: "+err.Error())
|
return nil, status.Error(codes.Internal, "Invalid JSON in vitastor-cli ls: "+err.Error())
|
||||||
}
|
}
|
||||||
if (len(resp.Kvs) == 0)
|
if (len(inodeCfg) == 0)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "missing "+inodeCfgKey+" key in etcd")
|
return nil, status.Error(codes.Internal, "vitastor-cli create said that image already exists, but ls can't find it")
|
||||||
}
|
}
|
||||||
var inodeCfg InodeConfig
|
if (inodeCfg[0].Size < uint64(volSize))
|
||||||
err = json.Unmarshal(resp.Kvs[0].Value, &inodeCfg)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "invalid "+inodeCfgKey+" key in etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
if (inodeCfg.Size < uint64(volSize))
|
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "image "+volName+" is already created, but size is less than expected")
|
return nil, status.Error(codes.Internal, "image "+volName+" is already created, but size is less than expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Find a free ID
|
return nil, err
|
||||||
// Create image metadata in a transaction verifying that the image doesn't exist yet AND ID is still free
|
|
||||||
maxIdKey := fmt.Sprintf("%s/index/maxid/%d", etcdPrefix, poolId)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
resp, err := cli.Get(ctx, maxIdKey)
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to read key from etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
var modRev int64
|
|
||||||
var nextId uint64
|
|
||||||
if (len(resp.Kvs) > 0)
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
nextId, err = strconv.ParseUint(string(resp.Kvs[0].Value), 10, 64)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, maxIdKey+" contains invalid ID")
|
|
||||||
}
|
|
||||||
modRev = resp.Kvs[0].ModRevision
|
|
||||||
nextId++
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nextId = 1
|
|
||||||
}
|
|
||||||
inodeIdxJson, _ := json.Marshal(InodeIndex{
|
|
||||||
Id: nextId,
|
|
||||||
PoolId: poolId,
|
|
||||||
})
|
|
||||||
inodeCfgJson, _ := json.Marshal(InodeConfig{
|
|
||||||
Name: volName,
|
|
||||||
Size: uint64(volSize),
|
|
||||||
})
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
txnResp, err := cli.Txn(ctx).If(
|
|
||||||
clientv3.Compare(clientv3.ModRevision(fmt.Sprintf("%s/index/maxid/%d", etcdPrefix, poolId)), "=", modRev),
|
|
||||||
clientv3.Compare(clientv3.CreateRevision(fmt.Sprintf("%s/index/image/%s", etcdPrefix, volName)), "=", 0),
|
|
||||||
clientv3.Compare(clientv3.CreateRevision(fmt.Sprintf("%s/config/inode/%d/%d", etcdPrefix, poolId, nextId)), "=", 0),
|
|
||||||
).Then(
|
|
||||||
clientv3.OpPut(fmt.Sprintf("%s/index/maxid/%d", etcdPrefix, poolId), fmt.Sprintf("%d", nextId)),
|
|
||||||
clientv3.OpPut(fmt.Sprintf("%s/index/image/%s", etcdPrefix, volName), string(inodeIdxJson)),
|
|
||||||
clientv3.OpPut(fmt.Sprintf("%s/config/inode/%d/%d", etcdPrefix, poolId, nextId), string(inodeCfgJson)),
|
|
||||||
).Commit()
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to commit transaction in etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
if (txnResp.Succeeded)
|
|
||||||
{
|
|
||||||
imageId = nextId
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Start over if the transaction fails
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,97 +237,12 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
|||||||
}
|
}
|
||||||
volName := ctxVars["name"]
|
volName := ctxVars["name"]
|
||||||
|
|
||||||
_, etcdUrl, etcdPrefix := GetConnectionParams(ctxVars)
|
ctxVars, _, _ = GetConnectionParams(ctxVars)
|
||||||
if (len(etcdUrl) == 0)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "no etcdUrl in storage class configuration and no etcd_address in vitastor.conf")
|
|
||||||
}
|
|
||||||
|
|
||||||
cli, err := clientv3.New(clientv3.Config{
|
_, err = invokeCLI(ctxVars, []string{ "rm", volName })
|
||||||
DialTimeout: ETCD_TIMEOUT,
|
|
||||||
Endpoints: etcdUrl,
|
|
||||||
})
|
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "failed to connect to etcd at "+strings.Join(etcdUrl, ",")+": "+err.Error())
|
return nil, err
|
||||||
}
|
|
||||||
defer cli.Close()
|
|
||||||
|
|
||||||
// Find inode by name
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
resp, err := cli.Get(ctx, etcdPrefix+"/index/image/"+volName)
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to read key from etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
if (len(resp.Kvs) == 0)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.NotFound, "volume "+volName+" does not exist")
|
|
||||||
}
|
|
||||||
var idx InodeIndex
|
|
||||||
err = json.Unmarshal(resp.Kvs[0].Value, &idx)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "invalid /index/image/"+volName+" key in etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get inode config
|
|
||||||
inodeCfgKey := fmt.Sprintf("%s/config/inode/%d/%d", etcdPrefix, idx.PoolId, idx.Id)
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
resp, err = cli.Get(ctx, inodeCfgKey)
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to read key from etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
if (len(resp.Kvs) == 0)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.NotFound, "volume "+volName+" does not exist")
|
|
||||||
}
|
|
||||||
var inodeCfg InodeConfig
|
|
||||||
err = json.Unmarshal(resp.Kvs[0].Value, &inodeCfg)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "invalid "+inodeCfgKey+" key in etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete inode data by invoking vitastor-cli
|
|
||||||
args := []string{
|
|
||||||
"rm-data", "--etcd_address", strings.Join(etcdUrl, ","),
|
|
||||||
"--pool", fmt.Sprintf("%d", idx.PoolId),
|
|
||||||
"--inode", fmt.Sprintf("%d", idx.Id),
|
|
||||||
}
|
|
||||||
if (ctxVars["configPath"] != "")
|
|
||||||
{
|
|
||||||
args = append(args, "--config_path", ctxVars["configPath"])
|
|
||||||
}
|
|
||||||
c := exec.Command("/usr/bin/vitastor-cli", args...)
|
|
||||||
var stderr bytes.Buffer
|
|
||||||
c.Stdout = nil
|
|
||||||
c.Stderr = &stderr
|
|
||||||
err = c.Run()
|
|
||||||
stderrStr := string(stderr.Bytes())
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("vitastor-cli rm-data failed: %s, status %s\n", stderrStr, err)
|
|
||||||
return nil, status.Error(codes.Internal, stderrStr+" (status "+err.Error()+")")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete inode config in etcd
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), ETCD_TIMEOUT)
|
|
||||||
txnResp, err := cli.Txn(ctx).Then(
|
|
||||||
clientv3.OpDelete(fmt.Sprintf("%s/index/image/%s", etcdPrefix, volName)),
|
|
||||||
clientv3.OpDelete(fmt.Sprintf("%s/config/inode/%d/%d", etcdPrefix, idx.PoolId, idx.Id)),
|
|
||||||
).Commit()
|
|
||||||
cancel()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to delete keys in etcd: "+err.Error())
|
|
||||||
}
|
|
||||||
if (!txnResp.Succeeded)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "failed to delete keys in etcd: transaction failed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &csi.DeleteVolumeResponse{}, nil
|
return &csi.DeleteVolumeResponse{}, nil
|
||||||
|
16
debian/changelog
vendored
16
debian/changelog
vendored
@@ -1,4 +1,18 @@
|
|||||||
vitastor (0.7.0-1) unstable; urgency=medium
|
vitastor (0.8.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Bugfixes
|
||||||
|
|
||||||
|
-- Vitaliy Filippov <vitalif@yourcmc.ru> Fri, 03 Jun 2022 02:09:44 +0300
|
||||||
|
|
||||||
|
vitastor (0.8.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Implement NFS proxy
|
||||||
|
* Add documentation
|
||||||
|
* Bugfixes
|
||||||
|
|
||||||
|
-- Vitaliy Filippov <vitalif@yourcmc.ru> Sun, 29 May 2022 23:39:13 +0300
|
||||||
|
|
||||||
|
vitastor (0.6.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
* RDMA support
|
* RDMA support
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
4
debian/control
vendored
4
debian/control
vendored
@@ -2,7 +2,7 @@ Source: vitastor
|
|||||||
Section: admin
|
Section: admin
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||||
Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev
|
Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev, libisal-dev
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://vitastor.io/
|
Homepage: https://vitastor.io/
|
||||||
Rules-Requires-Root: no
|
Rules-Requires-Root: no
|
||||||
@@ -18,7 +18,7 @@ Description: Vitastor, a fast software-defined clustered block storage
|
|||||||
|
|
||||||
Package: vitastor-osd
|
Package: vitastor-osd
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, vitastor-client (= ${binary:Version})
|
Depends: ${shlibs:Depends}, ${misc:Depends}, vitastor-client (= ${binary:Version}), fdisk, util-linux, parted
|
||||||
Description: Vitastor, a fast software-defined clustered block storage - object storage daemon
|
Description: Vitastor, a fast software-defined clustered block storage - object storage daemon
|
||||||
Vitastor object storage daemon, i.e. server program that stores data.
|
Vitastor object storage daemon, i.e. server program that stores data.
|
||||||
|
|
||||||
|
11
debian/libisal.pc
vendored
Normal file
11
debian/libisal.pc
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
prefix=/usr
|
||||||
|
exec_prefix=${prefix}
|
||||||
|
libdir=${prefix}/lib/x86_64-linux-gnu
|
||||||
|
includedir=${prefix}/include
|
||||||
|
|
||||||
|
Name: libisal
|
||||||
|
Description: Library for storage systems
|
||||||
|
Version: 2.30.0
|
||||||
|
Libs: -L${libdir} -lisal
|
||||||
|
Libs.private:
|
||||||
|
Cflags: -I${includedir}
|
2
debian/pve-storage-vitastor.install
vendored
2
debian/pve-storage-vitastor.install
vendored
@@ -1 +1 @@
|
|||||||
patches/PVE_VitastorPlugin.pm usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm
|
patches/VitastorPlugin.pm usr/share/perl5/PVE/Storage/Custom/
|
||||||
|
1
debian/vitastor-client.install
vendored
1
debian/vitastor-client.install
vendored
@@ -4,4 +4,3 @@ usr/bin/vitastor-rm
|
|||||||
usr/bin/vitastor-nbd
|
usr/bin/vitastor-nbd
|
||||||
usr/bin/vitastor-nfs
|
usr/bin/vitastor-nfs
|
||||||
usr/lib/*/libvitastor*.so*
|
usr/lib/*/libvitastor*.so*
|
||||||
mon/make-osd.sh /usr/lib/vitastor
|
|
||||||
|
1
debian/vitastor-mon.install
vendored
1
debian/vitastor-mon.install
vendored
@@ -1 +1,2 @@
|
|||||||
mon usr/lib/vitastor
|
mon usr/lib/vitastor
|
||||||
|
mon/vitastor-mon.service /lib/systemd/system
|
||||||
|
9
debian/vitastor-mon.postinst
vendored
Normal file
9
debian/vitastor-mon.postinst
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
addgroup --system --quiet vitastor
|
||||||
|
adduser --system --quiet --ingroup vitastor --no-create-home --home /nonexistent vitastor
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
fi
|
4
debian/vitastor-osd.install
vendored
4
debian/vitastor-osd.install
vendored
@@ -1,2 +1,6 @@
|
|||||||
usr/bin/vitastor-osd
|
usr/bin/vitastor-osd
|
||||||
|
usr/bin/vitastor-disk
|
||||||
usr/bin/vitastor-dump-journal
|
usr/bin/vitastor-dump-journal
|
||||||
|
mon/vitastor-osd@.service /lib/systemd/system
|
||||||
|
mon/vitastor.target /lib/systemd/system
|
||||||
|
mon/90-vitastor.rules /lib/udev/rules.d
|
||||||
|
10
debian/vitastor-osd.postinst
vendored
Normal file
10
debian/vitastor-osd.postinst
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
addgroup --system --quiet vitastor
|
||||||
|
adduser --system --quiet --ingroup vitastor --no-create-home --home /nonexistent vitastor
|
||||||
|
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
fi
|
11
debian/vitastor.Dockerfile
vendored
11
debian/vitastor.Dockerfile
vendored
@@ -22,10 +22,11 @@ RUN apt-get update
|
|||||||
RUN apt-get -y install fio liburing1 liburing-dev libgoogle-perftools-dev devscripts
|
RUN apt-get -y install fio liburing1 liburing-dev libgoogle-perftools-dev devscripts
|
||||||
RUN apt-get -y build-dep fio
|
RUN apt-get -y build-dep fio
|
||||||
RUN apt-get --download-only source fio
|
RUN apt-get --download-only source fio
|
||||||
RUN apt-get update && apt-get -y install libjerasure-dev cmake libibverbs-dev
|
RUN apt-get update && apt-get -y install libjerasure-dev cmake libibverbs-dev libisal-dev
|
||||||
|
|
||||||
ADD . /root/vitastor
|
ADD . /root/vitastor
|
||||||
RUN set -e -x; \
|
RUN set -e -x; \
|
||||||
|
[ -e /usr/lib/x86_64-linux-gnu/pkgconfig/libisal.pc ] || cp /root/vitastor/debian/libisal.pc /usr/lib/x86_64-linux-gnu/pkgconfig; \
|
||||||
mkdir -p /root/fio-build/; \
|
mkdir -p /root/fio-build/; \
|
||||||
cd /root/fio-build/; \
|
cd /root/fio-build/; \
|
||||||
rm -rf /root/fio-build/*; \
|
rm -rf /root/fio-build/*; \
|
||||||
@@ -33,8 +34,8 @@ RUN set -e -x; \
|
|||||||
mkdir -p /root/packages/vitastor-$REL; \
|
mkdir -p /root/packages/vitastor-$REL; \
|
||||||
rm -rf /root/packages/vitastor-$REL/*; \
|
rm -rf /root/packages/vitastor-$REL/*; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
cp -r /root/vitastor vitastor-0.7.0; \
|
cp -r /root/vitastor vitastor-0.8.5; \
|
||||||
cd vitastor-0.7.0; \
|
cd vitastor-0.8.5; \
|
||||||
ln -s /root/fio-build/fio-*/ ./fio; \
|
ln -s /root/fio-build/fio-*/ ./fio; \
|
||||||
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
||||||
@@ -47,8 +48,8 @@ RUN set -e -x; \
|
|||||||
rm -rf a b; \
|
rm -rf a b; \
|
||||||
echo "dep:fio=$FIO" > debian/fio_version; \
|
echo "dep:fio=$FIO" > debian/fio_version; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.7.0.orig.tar.xz vitastor-0.7.0; \
|
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.8.5.orig.tar.xz vitastor-0.8.5; \
|
||||||
cd vitastor-0.7.0; \
|
cd vitastor-0.8.5; \
|
||||||
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
||||||
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
||||||
|
@@ -9,34 +9,34 @@
|
|||||||
These parameters apply to clients and OSDs, are fixed at the moment of OSD drive
|
These parameters apply to clients and OSDs, are fixed at the moment of OSD drive
|
||||||
initialization and can't be changed after it without losing data.
|
initialization and can't be changed after it without losing data.
|
||||||
|
|
||||||
|
OSDs with different values of these parameters (for example, SSD and SSD+HDD
|
||||||
|
OSDs) can coexist in one Vitastor cluster within different pools. Each pool can
|
||||||
|
only include OSDs with identical settings of these parameters.
|
||||||
|
|
||||||
|
These parameters, when set to a non-default value, must also be specified in
|
||||||
|
etcd for clients to be aware of their values, either in /vitastor/config/global
|
||||||
|
or in pool configuration. Pool configuration overrides the global setting.
|
||||||
|
If the value for a pool in etcd doesn't match on-disk OSD configuration, the
|
||||||
|
OSD will refuse to start PGs of that pool.
|
||||||
|
|
||||||
- [block_size](#block_size)
|
- [block_size](#block_size)
|
||||||
- [bitmap_granularity](#bitmap_granularity)
|
- [bitmap_granularity](#bitmap_granularity)
|
||||||
- [immediate_commit](#immediate_commit)
|
- [immediate_commit](#immediate_commit)
|
||||||
- [client_dirty_limit](#client_dirty_limit)
|
|
||||||
|
|
||||||
## block_size
|
## block_size
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
- Default: 131072
|
- Default: 131072
|
||||||
|
|
||||||
Size of objects (data blocks) into which all physical and virtual drives are
|
Size of objects (data blocks) into which all physical and virtual drives
|
||||||
subdivided in Vitastor. One of current main settings in Vitastor, affects
|
(within a pool) are subdivided in Vitastor. One of current main settings
|
||||||
memory usage, write amplification and I/O load distribution effectiveness.
|
in Vitastor, affects memory usage, write amplification and I/O load
|
||||||
|
distribution effectiveness.
|
||||||
|
|
||||||
Recommended default block size is 128 KB for SSD and 4 MB for HDD. In fact,
|
Recommended default block size is 128 KB for SSD and 4 MB for HDD. In fact,
|
||||||
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
||||||
may increase average WA and reduce linear performance.
|
may increase average WA and reduce linear performance.
|
||||||
|
|
||||||
OSDs with different block sizes (for example, SSD and SSD+HDD OSDs) can
|
|
||||||
currently coexist in one etcd instance only within separate Vitastor
|
|
||||||
clusters with different etcd_prefix'es.
|
|
||||||
|
|
||||||
Also block size can't be changed after OSD initialization without losing
|
|
||||||
data.
|
|
||||||
|
|
||||||
You must always specify block_size in etcd in /vitastor/config/global if
|
|
||||||
you change it so all clients can know about it.
|
|
||||||
|
|
||||||
OSD memory usage is roughly (SIZE / BLOCK * 68 bytes) which is roughly
|
OSD memory usage is roughly (SIZE / BLOCK * 68 bytes) which is roughly
|
||||||
544 MB per 1 TB of used disk space with the default 128 KB block size.
|
544 MB per 1 TB of used disk space with the default 128 KB block size.
|
||||||
|
|
||||||
@@ -50,12 +50,7 @@ of disk_alignment. It's called bitmap granularity because Vitastor tracks
|
|||||||
an allocation bitmap for each object containing 2 bits per each
|
an allocation bitmap for each object containing 2 bits per each
|
||||||
(bitmap_granularity) bytes.
|
(bitmap_granularity) bytes.
|
||||||
|
|
||||||
This parameter can't be changed after OSD initialization without losing
|
Can't be smaller than the OSD data device sector.
|
||||||
data. Also it's fixed for the whole Vitastor cluster i.e. two different
|
|
||||||
values can't be used in a single Vitastor cluster.
|
|
||||||
|
|
||||||
Clients MUST be aware of this parameter value, so put it into etcd key
|
|
||||||
/vitastor/config/global if you change it for any reason.
|
|
||||||
|
|
||||||
## immediate_commit
|
## immediate_commit
|
||||||
|
|
||||||
@@ -99,26 +94,12 @@ unsafe to change by hand). The same may apply to newer HDDs with internal
|
|||||||
SSD cache or "media-cache" - for example, a lot of Seagate EXOS drives have
|
SSD cache or "media-cache" - for example, a lot of Seagate EXOS drives have
|
||||||
it (they have internal SSD cache even though it's not stated in datasheets).
|
it (they have internal SSD cache even though it's not stated in datasheets).
|
||||||
|
|
||||||
This parameter must be set both in etcd in /vitastor/config/global and in
|
Setting this parameter to "all" or "small" in OSD parameters requires enabling
|
||||||
OSD command line or configuration. Setting it to "all" or "small" requires
|
disable_journal_fsync and disable_meta_fsync, setting it to "all" also requires
|
||||||
enabling disable_journal_fsync and disable_meta_fsync, setting it to "all"
|
enabling disable_data_fsync.
|
||||||
also requires enabling disable_data_fsync.
|
|
||||||
|
|
||||||
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
||||||
SSDs with supercapacitor-based power loss protection (nonvolatile
|
SSDs with supercapacitor-based power loss protection (nonvolatile
|
||||||
write-through cache) for both data and journals in the whole Vitastor
|
write-through cache) for both data and journals in the whole Vitastor
|
||||||
cluster. Set it to "small" if you only use such SSDs for journals. Leave
|
cluster. Set it to "small" if you only use such SSDs for journals. Leave
|
||||||
empty if your drives have write-back cache.
|
empty if your drives have write-back cache.
|
||||||
|
|
||||||
## client_dirty_limit
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 33554432
|
|
||||||
|
|
||||||
Without immediate_commit=all this parameter sets the limit of "dirty"
|
|
||||||
(not committed by fsync) data allowed by the client before forcing an
|
|
||||||
additional fsync and committing the data. Also note that the client always
|
|
||||||
holds a copy of uncommitted data in memory so this setting also affects
|
|
||||||
RAM usage of clients.
|
|
||||||
|
|
||||||
This parameter doesn't affect OSDs themselves.
|
|
||||||
|
@@ -9,10 +9,19 @@
|
|||||||
Данные параметры используются клиентами и OSD, задаются в момент инициализации
|
Данные параметры используются клиентами и OSD, задаются в момент инициализации
|
||||||
диска OSD и не могут быть изменены после этого без потери данных.
|
диска OSD и не могут быть изменены после этого без потери данных.
|
||||||
|
|
||||||
|
OSD с разными значениями данных параметров (например, SSD и гибридные SSD+HDD
|
||||||
|
OSD) могут сосуществовать в одном кластере Vitastor в разных пулах. Один пул
|
||||||
|
может включать только OSD с одинаковыми настройками этих параметров.
|
||||||
|
|
||||||
|
Данные параметры, отличаясь от значения по умолчанию, должны также быть заданы
|
||||||
|
в etcd, чтобы клиенты могли узнать их значение, либо в глобальной конфигурации
|
||||||
|
/vitastor/config/global, либо в настройках пулов. Настройки пула переопределяют
|
||||||
|
глобальное значение. Если значение в настройках пула не будет соответствовать
|
||||||
|
конфигурации OSD, OSD откажется запускать PG данного пула.
|
||||||
|
|
||||||
- [block_size](#block_size)
|
- [block_size](#block_size)
|
||||||
- [bitmap_granularity](#bitmap_granularity)
|
- [bitmap_granularity](#bitmap_granularity)
|
||||||
- [immediate_commit](#immediate_commit)
|
- [immediate_commit](#immediate_commit)
|
||||||
- [client_dirty_limit](#client_dirty_limit)
|
|
||||||
|
|
||||||
## block_size
|
## block_size
|
||||||
|
|
||||||
@@ -20,24 +29,15 @@
|
|||||||
- Значение по умолчанию: 131072
|
- Значение по умолчанию: 131072
|
||||||
|
|
||||||
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
||||||
диски в Vitastor. Одна из ключевых на данный момент настроек, влияет на
|
диски в Vitastor (в рамках каждого пула). Одна из ключевых на данный момент
|
||||||
потребление памяти, объём избыточной записи (write amplification) и
|
настроек, влияет на потребление памяти, объём избыточной записи (write
|
||||||
эффективность распределения нагрузки по OSD.
|
amplification) и эффективность распределения нагрузки по OSD.
|
||||||
|
|
||||||
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
||||||
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
||||||
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
||||||
среднем увеличит WA.
|
среднем увеличит WA.
|
||||||
|
|
||||||
OSD с разными размерами блока (например, SSD и SSD+HDD OSD) на данный
|
|
||||||
момент могут сосуществовать в рамках одного etcd только в виде двух независимых
|
|
||||||
кластеров Vitastor с разными etcd_prefix.
|
|
||||||
|
|
||||||
Также размер блока нельзя менять после инициализации OSD без потери данных.
|
|
||||||
|
|
||||||
Если вы меняете размер блока, обязательно прописывайте его в etcd в
|
|
||||||
/vitastor/config/global, дабы все клиенты его знали.
|
|
||||||
|
|
||||||
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
||||||
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
||||||
стандартном 128 КБ блоке.
|
стандартном 128 КБ блоке.
|
||||||
@@ -52,13 +52,7 @@ OSD с разными размерами блока (например, SSD и SS
|
|||||||
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
||||||
по 2 бита на каждые (bitmap_granularity) байт.
|
по 2 бита на каждые (bitmap_granularity) байт.
|
||||||
|
|
||||||
Данный параметр нельзя менять после инициализации OSD без потери данных.
|
Не может быть меньше размера сектора дисков данных OSD.
|
||||||
Также он фиксирован для всего кластера Vitastor, т.е. разные значения
|
|
||||||
не могут сосуществовать в одном кластере.
|
|
||||||
|
|
||||||
Клиенты ДОЛЖНЫ знать правильное значение этого параметра, так что если вы
|
|
||||||
его меняете, обязательно прописывайте изменённое значение в etcd в ключ
|
|
||||||
/vitastor/config/global.
|
|
||||||
|
|
||||||
## immediate_commit
|
## immediate_commit
|
||||||
|
|
||||||
@@ -108,8 +102,7 @@ HDD-дисках с внутренним SSD или "медиа" кэшем - н
|
|||||||
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
||||||
указано в спецификациях).
|
указано в спецификациях).
|
||||||
|
|
||||||
Данный параметр нужно указывать и в etcd в /vitastor/config/global, и в
|
Указание "all" или "small" в настройках / командной строке OSD требует
|
||||||
командной строке или конфигурации OSD. Значения "all" и "small" требуют
|
|
||||||
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
||||||
требует включения disable_data_fsync.
|
требует включения disable_data_fsync.
|
||||||
|
|
||||||
@@ -119,16 +112,3 @@ immediate_commit в значение "all", если вы используете
|
|||||||
такие SSD для всех журналов, но не для данных - можете установить параметр
|
такие SSD для всех журналов, но не для данных - можете установить параметр
|
||||||
в "small". Если и какие-то из дисков журналов имеют волатильный кэш записи -
|
в "small". Если и какие-то из дисков журналов имеют волатильный кэш записи -
|
||||||
оставьте параметр пустым.
|
оставьте параметр пустым.
|
||||||
|
|
||||||
## client_dirty_limit
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 33554432
|
|
||||||
|
|
||||||
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
|
||||||
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
|
||||||
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
|
||||||
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
|
||||||
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
|
||||||
|
|
||||||
Параметр не влияет на сами OSD.
|
|
||||||
|
@@ -29,6 +29,7 @@ between clients, OSDs and etcd.
|
|||||||
- [etcd_slow_timeout](#etcd_slow_timeout)
|
- [etcd_slow_timeout](#etcd_slow_timeout)
|
||||||
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
||||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||||
|
- [client_dirty_limit](#client_dirty_limit)
|
||||||
|
|
||||||
## tcp_header_buffer_size
|
## tcp_header_buffer_size
|
||||||
|
|
||||||
@@ -212,3 +213,16 @@ etcd_report_interval to guarantee that keepalive actually works.
|
|||||||
|
|
||||||
etcd websocket ping interval required to keep the connection alive and
|
etcd websocket ping interval required to keep the connection alive and
|
||||||
detect disconnections quickly.
|
detect disconnections quickly.
|
||||||
|
|
||||||
|
## client_dirty_limit
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 33554432
|
||||||
|
|
||||||
|
Without immediate_commit=all this parameter sets the limit of "dirty"
|
||||||
|
(not committed by fsync) data allowed by the client before forcing an
|
||||||
|
additional fsync and committing the data. Also note that the client always
|
||||||
|
holds a copy of uncommitted data in memory so this setting also affects
|
||||||
|
RAM usage of clients.
|
||||||
|
|
||||||
|
This parameter doesn't affect OSDs themselves.
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
- [etcd_slow_timeout](#etcd_slow_timeout)
|
- [etcd_slow_timeout](#etcd_slow_timeout)
|
||||||
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
||||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||||
|
- [client_dirty_limit](#client_dirty_limit)
|
||||||
|
|
||||||
## tcp_header_buffer_size
|
## tcp_header_buffer_size
|
||||||
|
|
||||||
@@ -222,3 +223,16 @@ etcd_report_interval, чтобы keepalive гарантированно рабо
|
|||||||
- Значение по умолчанию: 30
|
- Значение по умолчанию: 30
|
||||||
|
|
||||||
Интервал проверки живости вебсокет-подключений к etcd.
|
Интервал проверки живости вебсокет-подключений к etcd.
|
||||||
|
|
||||||
|
## client_dirty_limit
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 33554432
|
||||||
|
|
||||||
|
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
||||||
|
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
||||||
|
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
||||||
|
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
||||||
|
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
||||||
|
|
||||||
|
Параметр не влияет на сами OSD.
|
||||||
|
@@ -17,12 +17,14 @@ initialization and can be changed with an OSD restart.
|
|||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [recovery_queue_depth](#recovery_queue_depth)
|
||||||
|
- [recovery_pg_switch](#recovery_pg_switch)
|
||||||
- [recovery_sync_batch](#recovery_sync_batch)
|
- [recovery_sync_batch](#recovery_sync_batch)
|
||||||
- [readonly](#readonly)
|
- [readonly](#readonly)
|
||||||
- [no_recovery](#no_recovery)
|
- [no_recovery](#no_recovery)
|
||||||
- [no_rebalance](#no_rebalance)
|
- [no_rebalance](#no_rebalance)
|
||||||
- [print_stats_interval](#print_stats_interval)
|
- [print_stats_interval](#print_stats_interval)
|
||||||
- [slow_log_interval](#slow_log_interval)
|
- [slow_log_interval](#slow_log_interval)
|
||||||
|
- [inode_vanish_time](#inode_vanish_time)
|
||||||
- [max_write_iodepth](#max_write_iodepth)
|
- [max_write_iodepth](#max_write_iodepth)
|
||||||
- [min_flusher_count](#min_flusher_count)
|
- [min_flusher_count](#min_flusher_count)
|
||||||
- [max_flusher_count](#max_flusher_count)
|
- [max_flusher_count](#max_flusher_count)
|
||||||
@@ -114,6 +116,16 @@ Maximum recovery operations per one primary OSD at any given moment of time.
|
|||||||
Currently it's the only parameter available to tune the speed or recovery
|
Currently it's the only parameter available to tune the speed or recovery
|
||||||
and rebalancing, but it's planned to implement more.
|
and rebalancing, but it's planned to implement more.
|
||||||
|
|
||||||
|
## recovery_pg_switch
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 128
|
||||||
|
|
||||||
|
Number of recovery operations before switching to recovery of the next PG.
|
||||||
|
The idea is to mix all PGs during recovery for more even space and load
|
||||||
|
distribution but still benefit from recovery queue depth greater than 1.
|
||||||
|
Degraded PGs are anyway scanned first.
|
||||||
|
|
||||||
## recovery_sync_batch
|
## recovery_sync_batch
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
@@ -163,6 +175,13 @@ Time interval at which OSDs dump slow or stuck operations on stdout, if
|
|||||||
they're any. Also it's the time after which an operation is considered
|
they're any. Also it's the time after which an operation is considered
|
||||||
"slow".
|
"slow".
|
||||||
|
|
||||||
|
## inode_vanish_time
|
||||||
|
|
||||||
|
- Type: seconds
|
||||||
|
- Default: 60
|
||||||
|
|
||||||
|
Number of seconds after which a deleted inode is removed from OSD statistics.
|
||||||
|
|
||||||
## max_write_iodepth
|
## max_write_iodepth
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
|
@@ -18,12 +18,14 @@
|
|||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [recovery_queue_depth](#recovery_queue_depth)
|
||||||
|
- [recovery_pg_switch](#recovery_pg_switch)
|
||||||
- [recovery_sync_batch](#recovery_sync_batch)
|
- [recovery_sync_batch](#recovery_sync_batch)
|
||||||
- [readonly](#readonly)
|
- [readonly](#readonly)
|
||||||
- [no_recovery](#no_recovery)
|
- [no_recovery](#no_recovery)
|
||||||
- [no_rebalance](#no_rebalance)
|
- [no_rebalance](#no_rebalance)
|
||||||
- [print_stats_interval](#print_stats_interval)
|
- [print_stats_interval](#print_stats_interval)
|
||||||
- [slow_log_interval](#slow_log_interval)
|
- [slow_log_interval](#slow_log_interval)
|
||||||
|
- [inode_vanish_time](#inode_vanish_time)
|
||||||
- [max_write_iodepth](#max_write_iodepth)
|
- [max_write_iodepth](#max_write_iodepth)
|
||||||
- [min_flusher_count](#min_flusher_count)
|
- [min_flusher_count](#min_flusher_count)
|
||||||
- [max_flusher_count](#max_flusher_count)
|
- [max_flusher_count](#max_flusher_count)
|
||||||
@@ -118,6 +120,17 @@ OSD, чтобы успевать очищать журнал - без них OSD
|
|||||||
для ускорения или замедления восстановления и перебалансировки данных, но
|
для ускорения или замедления восстановления и перебалансировки данных, но
|
||||||
в планах реализация других параметров.
|
в планах реализация других параметров.
|
||||||
|
|
||||||
|
## recovery_pg_switch
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 128
|
||||||
|
|
||||||
|
Число операций восстановления перед переключением на восстановление другой PG.
|
||||||
|
Идея заключается в том, чтобы восстанавливать все PG одновременно для более
|
||||||
|
равномерного распределения места и нагрузки, но при этом всё равно выигрывать
|
||||||
|
от глубины очереди восстановления, большей, чем 1. Деградированные PG в любом
|
||||||
|
случае сканируются первыми.
|
||||||
|
|
||||||
## recovery_sync_batch
|
## recovery_sync_batch
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
@@ -169,6 +182,13 @@ OSD.
|
|||||||
медленных или зависших операций, если таковые имеются. Также время, при
|
медленных или зависших операций, если таковые имеются. Также время, при
|
||||||
превышении которого операция считается "медленной".
|
превышении которого операция считается "медленной".
|
||||||
|
|
||||||
|
## inode_vanish_time
|
||||||
|
|
||||||
|
- Тип: секунды
|
||||||
|
- Значение по умолчанию: 60
|
||||||
|
|
||||||
|
Число секунд, через которое удалённые инод удаляется и из статистики OSD.
|
||||||
|
|
||||||
## max_write_iodepth
|
## max_write_iodepth
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
|
@@ -33,6 +33,9 @@ Parameters:
|
|||||||
- [pg_count](#pg_count)
|
- [pg_count](#pg_count)
|
||||||
- [failure_domain](#failure_domain)
|
- [failure_domain](#failure_domain)
|
||||||
- [max_osd_combinations](#max_osd_combinations)
|
- [max_osd_combinations](#max_osd_combinations)
|
||||||
|
- [block_size](#block_size)
|
||||||
|
- [bitmap_granularity](#bitmap_granularity)
|
||||||
|
- [immediate_commit](#immediate_commit)
|
||||||
- [pg_stripe_size](#pg_stripe_size)
|
- [pg_stripe_size](#pg_stripe_size)
|
||||||
- [root_node](#root_node)
|
- [root_node](#root_node)
|
||||||
- [osd_tags](#osd_tags)
|
- [osd_tags](#osd_tags)
|
||||||
@@ -79,7 +82,7 @@ Parent node reference is required for intermediate tree nodes.
|
|||||||
Separate OSD settings are set in etc keys `/vitastor/config/osd/<number>`
|
Separate OSD settings are set in etc keys `/vitastor/config/osd/<number>`
|
||||||
in JSON format `{"<key>":<value>}`.
|
in JSON format `{"<key>":<value>}`.
|
||||||
|
|
||||||
As of now, there is only one setting:
|
As of now, two settings are supported:
|
||||||
|
|
||||||
## reweight
|
## reweight
|
||||||
|
|
||||||
@@ -93,6 +96,15 @@ This means an OSD configured with reweight lower than 1 receives less PGs than
|
|||||||
it normally would. An OSD with reweight = 0 won't store any data. You can set
|
it normally would. An OSD with reweight = 0 won't store any data. You can set
|
||||||
reweight to 0 to trigger rebalance and remove all data from an OSD.
|
reweight to 0 to trigger rebalance and remove all data from an OSD.
|
||||||
|
|
||||||
|
## tags
|
||||||
|
|
||||||
|
- Type: string or array of strings
|
||||||
|
|
||||||
|
Sets tag or multiple tags for this OSD. Tags can be used to group OSDs into
|
||||||
|
subsets and then use a specific subset for pool instead of all OSDs.
|
||||||
|
For example you can mark SSD OSDs with tag "ssd" and HDD OSDs with "hdd" and
|
||||||
|
such tags will work as device classes.
|
||||||
|
|
||||||
# Pool parameters
|
# Pool parameters
|
||||||
|
|
||||||
## name
|
## name
|
||||||
@@ -106,9 +118,12 @@ Pool name.
|
|||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
- Required
|
- Required
|
||||||
- One of: "replicated", "xor" or "jerasure"
|
- One of: "replicated", "xor", "ec" or "jerasure"
|
||||||
|
|
||||||
Redundancy scheme used for data in this pool.
|
Redundancy scheme used for data in this pool. "jerasure" is an alias for "ec",
|
||||||
|
both use Reed-Solomon-Vandermonde codes based on ISA-L or jerasure libraries.
|
||||||
|
Fast ISA-L based implementation is used automatically when it's available,
|
||||||
|
slower jerasure version is used otherwise.
|
||||||
|
|
||||||
## pg_size
|
## pg_size
|
||||||
|
|
||||||
@@ -183,6 +198,43 @@ number of combinations to generate when optimising PG placement.
|
|||||||
|
|
||||||
This parameter usually doesn't require to be changed.
|
This parameter usually doesn't require to be changed.
|
||||||
|
|
||||||
|
## block_size
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 131072
|
||||||
|
|
||||||
|
Block size for this pool. The value from /vitastor/config/global is used when
|
||||||
|
unspecified. If your cluster has OSDs with different block sizes then pool must
|
||||||
|
be restricted by [osd_tags](#osd_tags) to only include OSDs with matching block
|
||||||
|
size.
|
||||||
|
|
||||||
|
Read more about this parameter in [Cluster-Wide Disk Layout Parameters](layout-cluster.en.md#block_size).
|
||||||
|
|
||||||
|
## bitmap_granularity
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 4096
|
||||||
|
|
||||||
|
"Sector" size of virtual disks in this pool. The value from
|
||||||
|
/vitastor/config/global is used when unspecified. Similar to block_size, the
|
||||||
|
pool must be restricted by [osd_tags](#osd_tags) to only include OSDs with
|
||||||
|
matching bitmap_granularity.
|
||||||
|
|
||||||
|
Read more about this parameter in [Cluster-Wide Disk Layout Parameters](layout-cluster.en.md#bitmap_granularity).
|
||||||
|
|
||||||
|
## immediate_commit
|
||||||
|
|
||||||
|
- Type: string, one of "all", "small" and "none"
|
||||||
|
- Default: none
|
||||||
|
|
||||||
|
Immediate commit setting for this pool. The value from /vitastor/config/global
|
||||||
|
is used when unspecified. Similar to block_size, the pool must be restricted by
|
||||||
|
[osd_tags](#osd_tags) to only include OSDs with compatible immediate_commit.
|
||||||
|
Compatible means that a pool with non-immediate commit will work with OSDs with
|
||||||
|
immediate commit enabled, but not vice versa.
|
||||||
|
|
||||||
|
Read more about this parameter in [Cluster-Wide Disk Layout Parameters](layout-cluster.en.md#immediate_commit).
|
||||||
|
|
||||||
## pg_stripe_size
|
## pg_stripe_size
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
@@ -243,7 +295,7 @@ of the OSDs containing a data chunk for a PG.
|
|||||||
{
|
{
|
||||||
"2": {
|
"2": {
|
||||||
"name":"ecpool",
|
"name":"ecpool",
|
||||||
"scheme":"jerasure",
|
"scheme":"ec",
|
||||||
"pg_size":3,
|
"pg_size":3,
|
||||||
"parity_chunks":1,
|
"parity_chunks":1,
|
||||||
"pg_minsize":2,
|
"pg_minsize":2,
|
||||||
|
@@ -32,6 +32,9 @@
|
|||||||
- [pg_count](#pg_count)
|
- [pg_count](#pg_count)
|
||||||
- [failure_domain](#failure_domain)
|
- [failure_domain](#failure_domain)
|
||||||
- [max_osd_combinations](#max_osd_combinations)
|
- [max_osd_combinations](#max_osd_combinations)
|
||||||
|
- [block_size](#block_size)
|
||||||
|
- [bitmap_granularity](#bitmap_granularity)
|
||||||
|
- [immediate_commit](#immediate_commit)
|
||||||
- [pg_stripe_size](#pg_stripe_size)
|
- [pg_stripe_size](#pg_stripe_size)
|
||||||
- [root_node](#root_node)
|
- [root_node](#root_node)
|
||||||
- [osd_tags](#osd_tags)
|
- [osd_tags](#osd_tags)
|
||||||
@@ -78,7 +81,10 @@
|
|||||||
Настройки отдельных OSD задаются в ключах etcd `/vitastor/config/osd/<number>`
|
Настройки отдельных OSD задаются в ключах etcd `/vitastor/config/osd/<number>`
|
||||||
в JSON-формате `{"<key>":<value>}`.
|
в JSON-формате `{"<key>":<value>}`.
|
||||||
|
|
||||||
На данный момент поддерживается одна настройка:
|
На данный момент поддерживаются две настройки:
|
||||||
|
|
||||||
|
- [reweight](#reweight)
|
||||||
|
- [tags](#tags)
|
||||||
|
|
||||||
## reweight
|
## reweight
|
||||||
|
|
||||||
@@ -93,6 +99,15 @@
|
|||||||
хранении данных вообще. Вы можете установить reweight в 0, чтобы убрать
|
хранении данных вообще. Вы можете установить reweight в 0, чтобы убрать
|
||||||
все данные с OSD.
|
все данные с OSD.
|
||||||
|
|
||||||
|
## tags
|
||||||
|
|
||||||
|
- Тип: строка или массив строк
|
||||||
|
|
||||||
|
Задаёт тег или набор тегов для данного OSD. Теги можно использовать, чтобы
|
||||||
|
делить OSD на множества и потом размещать пул только на части OSD, а не на
|
||||||
|
всех. Можно, например, пометить SSD OSD тегом "ssd", а HDD тегом "hdd", в
|
||||||
|
этом смысле теги работают аналогично классам устройств.
|
||||||
|
|
||||||
# Параметры
|
# Параметры
|
||||||
|
|
||||||
## name
|
## name
|
||||||
@@ -106,9 +121,13 @@
|
|||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
- Обязательный
|
- Обязательный
|
||||||
- Возможные значения: "replicated", "xor" или "jerasure"
|
- Возможные значения: "replicated", "xor", "ec" или "jerasure"
|
||||||
|
|
||||||
Схема избыточности, используемая в данном пуле.
|
Схема избыточности, используемая в данном пуле. "jerasure" - синоним для "ec",
|
||||||
|
в обеих схемах используются коды Рида-Соломона-Вандермонда, реализованные на
|
||||||
|
основе библиотек ISA-L или jerasure. Быстрая реализация на основе ISA-L
|
||||||
|
используется автоматически, когда доступна, в противном случае используется
|
||||||
|
более медленная jerasure-версия.
|
||||||
|
|
||||||
## pg_size
|
## pg_size
|
||||||
|
|
||||||
@@ -181,13 +200,51 @@ PG в Vitastor эферемерны, то есть вы можете менят
|
|||||||
|
|
||||||
Обычно данный параметр не требует изменений.
|
Обычно данный параметр не требует изменений.
|
||||||
|
|
||||||
|
## block_size
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- По умолчанию: 131072
|
||||||
|
|
||||||
|
Размер блока для данного пула. Если не задан, используется значение из
|
||||||
|
/vitastor/config/global. Если в вашем кластере есть OSD с разными размерами
|
||||||
|
блока, пул должен быть ограничен только OSD, блок которых равен блоку пула,
|
||||||
|
с помощью [osd_tags](#osd_tags).
|
||||||
|
|
||||||
|
О самом параметре читайте в разделе [Дисковые параметры уровня кластера](layout-cluster.ru.md#block_size).
|
||||||
|
|
||||||
|
## bitmap_granularity
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- По умолчанию: 4096
|
||||||
|
|
||||||
|
Размер "сектора" виртуальных дисков в данном пуле. Если не задан, используется
|
||||||
|
значение из /vitastor/config/global. Аналогично block_size, пул должен быть
|
||||||
|
ограничен OSD со значением bitmap_granularity, равным значению пула, с помощью
|
||||||
|
[osd_tags](#osd_tags).
|
||||||
|
|
||||||
|
О самом параметре читайте в разделе [Дисковые параметры уровня кластера](layout-cluster.ru.md#bitmap_granularity).
|
||||||
|
|
||||||
|
## immediate_commit
|
||||||
|
|
||||||
|
- Тип: строка "all", "small" или "none"
|
||||||
|
- По умолчанию: none
|
||||||
|
|
||||||
|
Настройка мгновенного коммита для данного пула. Если не задана, используется
|
||||||
|
значение из /vitastor/config/global. Аналогично block_size, пул должен быть
|
||||||
|
ограничен OSD со значением bitmap_granularity, совместимым со значением пула, с
|
||||||
|
помощью [osd_tags](#osd_tags). Совместимость означает, что пул с отключенным
|
||||||
|
мгновенным коммитом может работать на OSD с включённым мгновенным коммитом, но
|
||||||
|
не наоборот.
|
||||||
|
|
||||||
|
О самом параметре читайте в разделе [Дисковые параметры уровня кластера](layout-cluster.ru.md#immediate_commit).
|
||||||
|
|
||||||
## pg_stripe_size
|
## pg_stripe_size
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
- По умолчанию: 0
|
- По умолчанию: 0
|
||||||
|
|
||||||
Данный параметр задаёт размер полосы "нарезки" образов на PG. Размер полосы не может
|
Данный параметр задаёт размер полосы "нарезки" образов на PG. Размер полосы не может
|
||||||
быть меньше, чем [block_size](layout-cluster.ru.md#block_size), умноженный на
|
быть меньше, чем [block_size](#block_size), умноженный на
|
||||||
(pg_size - parity_chunks) для EC-пулов или 1 для реплицированных пулов. То же
|
(pg_size - parity_chunks) для EC-пулов или 1 для реплицированных пулов. То же
|
||||||
значение используется по умолчанию.
|
значение используется по умолчанию.
|
||||||
|
|
||||||
@@ -242,7 +299,7 @@ PG в Vitastor эферемерны, то есть вы можете менят
|
|||||||
{
|
{
|
||||||
"2": {
|
"2": {
|
||||||
"name":"ecpool",
|
"name":"ecpool",
|
||||||
"scheme":"jerasure",
|
"scheme":"ec",
|
||||||
"pg_size":3,
|
"pg_size":3,
|
||||||
"parity_chunks":1,
|
"parity_chunks":1,
|
||||||
"pg_minsize":2,
|
"pg_minsize":2,
|
||||||
|
@@ -2,3 +2,13 @@
|
|||||||
|
|
||||||
These parameters apply to clients and OSDs, are fixed at the moment of OSD drive
|
These parameters apply to clients and OSDs, are fixed at the moment of OSD drive
|
||||||
initialization and can't be changed after it without losing data.
|
initialization and can't be changed after it without losing data.
|
||||||
|
|
||||||
|
OSDs with different values of these parameters (for example, SSD and SSD+HDD
|
||||||
|
OSDs) can coexist in one Vitastor cluster within different pools. Each pool can
|
||||||
|
only include OSDs with identical settings of these parameters.
|
||||||
|
|
||||||
|
These parameters, when set to a non-default value, must also be specified in
|
||||||
|
etcd for clients to be aware of their values, either in /vitastor/config/global
|
||||||
|
or in pool configuration. Pool configuration overrides the global setting.
|
||||||
|
If the value for a pool in etcd doesn't match on-disk OSD configuration, the
|
||||||
|
OSD will refuse to start PGs of that pool.
|
||||||
|
@@ -2,3 +2,13 @@
|
|||||||
|
|
||||||
Данные параметры используются клиентами и OSD, задаются в момент инициализации
|
Данные параметры используются клиентами и OSD, задаются в момент инициализации
|
||||||
диска OSD и не могут быть изменены после этого без потери данных.
|
диска OSD и не могут быть изменены после этого без потери данных.
|
||||||
|
|
||||||
|
OSD с разными значениями данных параметров (например, SSD и гибридные SSD+HDD
|
||||||
|
OSD) могут сосуществовать в одном кластере Vitastor в разных пулах. Один пул
|
||||||
|
может включать только OSD с одинаковыми настройками этих параметров.
|
||||||
|
|
||||||
|
Данные параметры, отличаясь от значения по умолчанию, должны также быть заданы
|
||||||
|
в etcd, чтобы клиенты могли узнать их значение, либо в глобальной конфигурации
|
||||||
|
/vitastor/config/global, либо в настройках пулов. Настройки пула переопределяют
|
||||||
|
глобальное значение. Если значение в настройках пула не будет соответствовать
|
||||||
|
конфигурации OSD, OSD откажется запускать PG данного пула.
|
||||||
|
@@ -2,46 +2,28 @@
|
|||||||
type: int
|
type: int
|
||||||
default: 131072
|
default: 131072
|
||||||
info: |
|
info: |
|
||||||
Size of objects (data blocks) into which all physical and virtual drives are
|
Size of objects (data blocks) into which all physical and virtual drives
|
||||||
subdivided in Vitastor. One of current main settings in Vitastor, affects
|
(within a pool) are subdivided in Vitastor. One of current main settings
|
||||||
memory usage, write amplification and I/O load distribution effectiveness.
|
in Vitastor, affects memory usage, write amplification and I/O load
|
||||||
|
distribution effectiveness.
|
||||||
|
|
||||||
Recommended default block size is 128 KB for SSD and 4 MB for HDD. In fact,
|
Recommended default block size is 128 KB for SSD and 4 MB for HDD. In fact,
|
||||||
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
||||||
may increase average WA and reduce linear performance.
|
may increase average WA and reduce linear performance.
|
||||||
|
|
||||||
OSDs with different block sizes (for example, SSD and SSD+HDD OSDs) can
|
|
||||||
currently coexist in one etcd instance only within separate Vitastor
|
|
||||||
clusters with different etcd_prefix'es.
|
|
||||||
|
|
||||||
Also block size can't be changed after OSD initialization without losing
|
|
||||||
data.
|
|
||||||
|
|
||||||
You must always specify block_size in etcd in /vitastor/config/global if
|
|
||||||
you change it so all clients can know about it.
|
|
||||||
|
|
||||||
OSD memory usage is roughly (SIZE / BLOCK * 68 bytes) which is roughly
|
OSD memory usage is roughly (SIZE / BLOCK * 68 bytes) which is roughly
|
||||||
544 MB per 1 TB of used disk space with the default 128 KB block size.
|
544 MB per 1 TB of used disk space with the default 128 KB block size.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
||||||
диски в Vitastor. Одна из ключевых на данный момент настроек, влияет на
|
диски в Vitastor (в рамках каждого пула). Одна из ключевых на данный момент
|
||||||
потребление памяти, объём избыточной записи (write amplification) и
|
настроек, влияет на потребление памяти, объём избыточной записи (write
|
||||||
эффективность распределения нагрузки по OSD.
|
amplification) и эффективность распределения нагрузки по OSD.
|
||||||
|
|
||||||
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
||||||
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
||||||
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
||||||
среднем увеличит WA.
|
среднем увеличит WA.
|
||||||
|
|
||||||
OSD с разными размерами блока (например, SSD и SSD+HDD OSD) на данный
|
|
||||||
момент могут сосуществовать в рамках одного etcd только в виде двух независимых
|
|
||||||
кластеров Vitastor с разными etcd_prefix.
|
|
||||||
|
|
||||||
Также размер блока нельзя менять после инициализации OSD без потери данных.
|
|
||||||
|
|
||||||
Если вы меняете размер блока, обязательно прописывайте его в etcd в
|
|
||||||
/vitastor/config/global, дабы все клиенты его знали.
|
|
||||||
|
|
||||||
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
||||||
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
||||||
стандартном 128 КБ блоке.
|
стандартном 128 КБ блоке.
|
||||||
@@ -54,25 +36,14 @@
|
|||||||
an allocation bitmap for each object containing 2 bits per each
|
an allocation bitmap for each object containing 2 bits per each
|
||||||
(bitmap_granularity) bytes.
|
(bitmap_granularity) bytes.
|
||||||
|
|
||||||
This parameter can't be changed after OSD initialization without losing
|
Can't be smaller than the OSD data device sector.
|
||||||
data. Also it's fixed for the whole Vitastor cluster i.e. two different
|
|
||||||
values can't be used in a single Vitastor cluster.
|
|
||||||
|
|
||||||
Clients MUST be aware of this parameter value, so put it into etcd key
|
|
||||||
/vitastor/config/global if you change it for any reason.
|
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Требуемое выравнивание записи на виртуальные диски (размер их "сектора").
|
Требуемое выравнивание записи на виртуальные диски (размер их "сектора").
|
||||||
Должен быть кратен disk_alignment. Называется гранулярностью битовой карты
|
Должен быть кратен disk_alignment. Называется гранулярностью битовой карты
|
||||||
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
||||||
по 2 бита на каждые (bitmap_granularity) байт.
|
по 2 бита на каждые (bitmap_granularity) байт.
|
||||||
|
|
||||||
Данный параметр нельзя менять после инициализации OSD без потери данных.
|
Не может быть меньше размера сектора дисков данных OSD.
|
||||||
Также он фиксирован для всего кластера Vitastor, т.е. разные значения
|
|
||||||
не могут сосуществовать в одном кластере.
|
|
||||||
|
|
||||||
Клиенты ДОЛЖНЫ знать правильное значение этого параметра, так что если вы
|
|
||||||
его меняете, обязательно прописывайте изменённое значение в etcd в ключ
|
|
||||||
/vitastor/config/global.
|
|
||||||
- name: immediate_commit
|
- name: immediate_commit
|
||||||
type: string
|
type: string
|
||||||
default: false
|
default: false
|
||||||
@@ -114,10 +85,9 @@
|
|||||||
SSD cache or "media-cache" - for example, a lot of Seagate EXOS drives have
|
SSD cache or "media-cache" - for example, a lot of Seagate EXOS drives have
|
||||||
it (they have internal SSD cache even though it's not stated in datasheets).
|
it (they have internal SSD cache even though it's not stated in datasheets).
|
||||||
|
|
||||||
This parameter must be set both in etcd in /vitastor/config/global and in
|
Setting this parameter to "all" or "small" in OSD parameters requires enabling
|
||||||
OSD command line or configuration. Setting it to "all" or "small" requires
|
disable_journal_fsync and disable_meta_fsync, setting it to "all" also requires
|
||||||
enabling disable_journal_fsync and disable_meta_fsync, setting it to "all"
|
enabling disable_data_fsync.
|
||||||
also requires enabling disable_data_fsync.
|
|
||||||
|
|
||||||
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
||||||
SSDs with supercapacitor-based power loss protection (nonvolatile
|
SSDs with supercapacitor-based power loss protection (nonvolatile
|
||||||
@@ -168,8 +138,7 @@
|
|||||||
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
||||||
указано в спецификациях).
|
указано в спецификациях).
|
||||||
|
|
||||||
Данный параметр нужно указывать и в etcd в /vitastor/config/global, и в
|
Указание "all" или "small" в настройках / командной строке OSD требует
|
||||||
командной строке или конфигурации OSD. Значения "all" и "small" требуют
|
|
||||||
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
||||||
требует включения disable_data_fsync.
|
требует включения disable_data_fsync.
|
||||||
|
|
||||||
@@ -179,22 +148,3 @@
|
|||||||
такие SSD для всех журналов, но не для данных - можете установить параметр
|
такие SSD для всех журналов, но не для данных - можете установить параметр
|
||||||
в "small". Если и какие-то из дисков журналов имеют волатильный кэш записи -
|
в "small". Если и какие-то из дисков журналов имеют волатильный кэш записи -
|
||||||
оставьте параметр пустым.
|
оставьте параметр пустым.
|
||||||
- name: client_dirty_limit
|
|
||||||
type: int
|
|
||||||
default: 33554432
|
|
||||||
info: |
|
|
||||||
Without immediate_commit=all this parameter sets the limit of "dirty"
|
|
||||||
(not committed by fsync) data allowed by the client before forcing an
|
|
||||||
additional fsync and committing the data. Also note that the client always
|
|
||||||
holds a copy of uncommitted data in memory so this setting also affects
|
|
||||||
RAM usage of clients.
|
|
||||||
|
|
||||||
This parameter doesn't affect OSDs themselves.
|
|
||||||
info_ru: |
|
|
||||||
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
|
||||||
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
|
||||||
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
|
||||||
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
|
||||||
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
|
||||||
|
|
||||||
Параметр не влияет на сами OSD.
|
|
||||||
|
@@ -223,3 +223,22 @@
|
|||||||
detect disconnections quickly.
|
detect disconnections quickly.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Интервал проверки живости вебсокет-подключений к etcd.
|
Интервал проверки живости вебсокет-подключений к etcd.
|
||||||
|
- name: client_dirty_limit
|
||||||
|
type: int
|
||||||
|
default: 33554432
|
||||||
|
info: |
|
||||||
|
Without immediate_commit=all this parameter sets the limit of "dirty"
|
||||||
|
(not committed by fsync) data allowed by the client before forcing an
|
||||||
|
additional fsync and committing the data. Also note that the client always
|
||||||
|
holds a copy of uncommitted data in memory so this setting also affects
|
||||||
|
RAM usage of clients.
|
||||||
|
|
||||||
|
This parameter doesn't affect OSDs themselves.
|
||||||
|
info_ru: |
|
||||||
|
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
||||||
|
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
||||||
|
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
||||||
|
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
||||||
|
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
||||||
|
|
||||||
|
Параметр не влияет на сами OSD.
|
||||||
|
@@ -102,6 +102,20 @@
|
|||||||
момент времени. На данный момент единственный параметр, который можно менять
|
момент времени. На данный момент единственный параметр, который можно менять
|
||||||
для ускорения или замедления восстановления и перебалансировки данных, но
|
для ускорения или замедления восстановления и перебалансировки данных, но
|
||||||
в планах реализация других параметров.
|
в планах реализация других параметров.
|
||||||
|
- name: recovery_pg_switch
|
||||||
|
type: int
|
||||||
|
default: 128
|
||||||
|
info: |
|
||||||
|
Number of recovery operations before switching to recovery of the next PG.
|
||||||
|
The idea is to mix all PGs during recovery for more even space and load
|
||||||
|
distribution but still benefit from recovery queue depth greater than 1.
|
||||||
|
Degraded PGs are anyway scanned first.
|
||||||
|
info_ru: |
|
||||||
|
Число операций восстановления перед переключением на восстановление другой PG.
|
||||||
|
Идея заключается в том, чтобы восстанавливать все PG одновременно для более
|
||||||
|
равномерного распределения места и нагрузки, но при этом всё равно выигрывать
|
||||||
|
от глубины очереди восстановления, большей, чем 1. Деградированные PG в любом
|
||||||
|
случае сканируются первыми.
|
||||||
- name: recovery_sync_batch
|
- name: recovery_sync_batch
|
||||||
type: int
|
type: int
|
||||||
default: 16
|
default: 16
|
||||||
@@ -158,6 +172,13 @@
|
|||||||
Временной интервал, с которым OSD выводят в стандартный вывод список
|
Временной интервал, с которым OSD выводят в стандартный вывод список
|
||||||
медленных или зависших операций, если таковые имеются. Также время, при
|
медленных или зависших операций, если таковые имеются. Также время, при
|
||||||
превышении которого операция считается "медленной".
|
превышении которого операция считается "медленной".
|
||||||
|
- name: inode_vanish_time
|
||||||
|
type: sec
|
||||||
|
default: 60
|
||||||
|
info: |
|
||||||
|
Number of seconds after which a deleted inode is removed from OSD statistics.
|
||||||
|
info_ru: |
|
||||||
|
Число секунд, через которое удалённые инод удаляется и из статистики OSD.
|
||||||
- name: max_write_iodepth
|
- name: max_write_iodepth
|
||||||
type: int
|
type: int
|
||||||
default: 128
|
default: 128
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
## Debian
|
## Debian
|
||||||
|
|
||||||
- Trust Vitastor package signing key:
|
- Trust Vitastor package signing key:
|
||||||
`wget -q -O - https://vitastor.io/debian/pubkey | sudo apt-key add -`
|
`wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg`
|
||||||
- Add Vitastor package repository to your /etc/apt/sources.list:
|
- Add Vitastor package repository to your /etc/apt/sources.list:
|
||||||
- Debian 11 (Bullseye/Sid): `deb https://vitastor.io/debian bullseye main`
|
- Debian 11 (Bullseye/Sid): `deb https://vitastor.io/debian bullseye main`
|
||||||
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
- Add Vitastor package repository:
|
- Add Vitastor package repository:
|
||||||
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm`
|
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
|
||||||
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm`
|
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
||||||
- Enable EPEL: `yum/dnf install epel-release`
|
- Enable EPEL: `yum/dnf install epel-release`
|
||||||
- Enable additional CentOS repositories:
|
- Enable additional CentOS repositories:
|
||||||
- CentOS 7: `yum install centos-release-scl`
|
- CentOS 7: `yum install centos-release-scl`
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
## Debian
|
## Debian
|
||||||
|
|
||||||
- Добавьте ключ репозитория Vitastor:
|
- Добавьте ключ репозитория Vitastor:
|
||||||
`wget -q -O - https://vitastor.io/debian/pubkey | sudo apt-key add -`
|
`wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg`
|
||||||
- Добавьте репозиторий Vitastor в /etc/apt/sources.list:
|
- Добавьте репозиторий Vitastor в /etc/apt/sources.list:
|
||||||
- Debian 11 (Bullseye/Sid): `deb https://vitastor.io/debian bullseye main`
|
- Debian 11 (Bullseye/Sid): `deb https://vitastor.io/debian bullseye main`
|
||||||
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
- Добавьте в систему репозиторий Vitastor:
|
- Добавьте в систему репозиторий Vitastor:
|
||||||
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm`
|
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
|
||||||
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm`
|
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
||||||
- Включите EPEL: `yum/dnf install epel-release`
|
- Включите EPEL: `yum/dnf install epel-release`
|
||||||
- Включите дополнительные репозитории CentOS:
|
- Включите дополнительные репозитории CentOS:
|
||||||
- CentOS 7: `yum install centos-release-scl`
|
- CentOS 7: `yum install centos-release-scl`
|
||||||
|
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
# Proxmox VE
|
# Proxmox VE
|
||||||
|
|
||||||
To enable Vitastor support in Proxmox Virtual Environment (6.4 and 7.1 are supported):
|
To enable Vitastor support in Proxmox Virtual Environment (6.4-7.3 are supported):
|
||||||
|
|
||||||
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts
|
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
||||||
(buster for 6.4, bullseye for 7.1)
|
buster for 6.4, bullseye for 7.3, pve7.1 for 7.1, pve7.2 for 7.2
|
||||||
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
||||||
- Define storage in `/etc/pve/storage.cfg` (see below)
|
- Define storage in `/etc/pve/storage.cfg` (see below)
|
||||||
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
||||||
@@ -35,5 +35,5 @@ vitastor: vitastor
|
|||||||
vitastor_nbd 0
|
vitastor_nbd 0
|
||||||
```
|
```
|
||||||
|
|
||||||
\* Note: you can also manually copy [patches/PVE_VitastorPlugin.pm](patches/PVE_VitastorPlugin.pm) to Proxmox hosts
|
\* Note: you can also manually copy [patches/VitastorPlugin.pm](patches/VitastorPlugin.pm) to Proxmox hosts
|
||||||
as `/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm` instead of installing pve-storage-vitastor.
|
as `/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm` instead of installing pve-storage-vitastor.
|
||||||
|
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
# Proxmox
|
# Proxmox
|
||||||
|
|
||||||
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4 и 7.1):
|
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-7.3):
|
||||||
|
|
||||||
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox
|
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
||||||
(buster для 6.4, bullseye для 7.1)
|
buster для 6.4, bullseye для 7.3, pve7.1 для 7.1, pve7.2 для 7.2
|
||||||
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
||||||
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
||||||
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||||
@@ -35,5 +35,5 @@ vitastor: vitastor
|
|||||||
```
|
```
|
||||||
|
|
||||||
\* Примечание: вместо установки пакета pve-storage-vitastor вы можете вручную скопировать файл
|
\* Примечание: вместо установки пакета pve-storage-vitastor вы можете вручную скопировать файл
|
||||||
[patches/PVE_VitastorPlugin.pm](patches/PVE_VitastorPlugin.pm) на хосты Proxmox как
|
[patches/VitastorPlugin.pm](patches/VitastorPlugin.pm) на хосты Proxmox как
|
||||||
`/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm`.
|
`/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm`.
|
||||||
|
@@ -15,7 +15,8 @@
|
|||||||
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
|
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
|
||||||
designated initializers support from C++20
|
designated initializers support from C++20
|
||||||
- CMake
|
- CMake
|
||||||
- liburing, jerasure headers
|
- liburing, jerasure headers and libraries
|
||||||
|
- ISA-L, libibverbs headers and libraries (optional)
|
||||||
- tcmalloc (google-perftools-dev)
|
- tcmalloc (google-perftools-dev)
|
||||||
|
|
||||||
## Basic instructions
|
## Basic instructions
|
||||||
|
@@ -15,7 +15,8 @@
|
|||||||
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
|
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
|
||||||
назначенных инициализаторов (designated initializers) из C++20
|
назначенных инициализаторов (designated initializers) из C++20
|
||||||
- CMake
|
- CMake
|
||||||
- Заголовки liburing, jerasure
|
- Заголовки и библиотеки liburing, jerasure
|
||||||
|
- Опционально - заголовки и библиотеки ISA-L, libibverbs
|
||||||
- tcmalloc (google-perftools-dev)
|
- tcmalloc (google-perftools-dev)
|
||||||
|
|
||||||
## Базовая инструкция
|
## Базовая инструкция
|
||||||
|
@@ -127,7 +127,7 @@
|
|||||||
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
||||||
с OSD повторяют из памяти. Скопированные в память данные удаляются при успешном fsync,
|
с OSD повторяют из памяти. Скопированные в память данные удаляются при успешном fsync,
|
||||||
а чтобы хранение этих данных не приводило к чрезмерному потреблению памяти, клиенты
|
а чтобы хранение этих данных не приводило к чрезмерному потреблению памяти, клиенты
|
||||||
автоматически выполняют fsync каждые [client_dirty_limit](../config/layout-cluster.ru.md#client_dirty_limit)
|
автоматически выполняют fsync каждые [client_dirty_limit](../config/network.ru.md#client_dirty_limit)
|
||||||
записанных байт.
|
записанных байт.
|
||||||
|
|
||||||
## Схожесть с Ceph
|
## Схожесть с Ceph
|
||||||
|
@@ -33,5 +33,5 @@ Vitastor Network Public License 1.1, основанная на GNU GPL 3.0 с д
|
|||||||
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
|
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
|
||||||
совместимости с таким ПО, как QEMU и fio.
|
совместимости с таким ПО, как QEMU и fio.
|
||||||
|
|
||||||
Вы можете найти полный текст VNPL 1.1 в файле [VNPL-1.1.txt](../../VNPL-1.1.txt),
|
Вы можете найти полный текст VNPL 1.1 на английском языке в файле [VNPL-1.1.txt](../../VNPL-1.1.txt),
|
||||||
а GPL 2.0 в файле [GPL-2.0.txt](../../GPL-2.0.txt).
|
VNPL 1.1 на русском языке в файле [VNPL-1.1-RU.txt](../../VNPL-1.1-RU.txt), а GPL 2.0 в файле [GPL-2.0.txt](../../GPL-2.0.txt).
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
- Basic part: highly-available block storage with symmetric clustering and no SPOF
|
- Basic part: highly-available block storage with symmetric clustering and no SPOF
|
||||||
- [Performance](../performance/comparison1.en.md) ;-D
|
- [Performance](../performance/comparison1.en.md) ;-D
|
||||||
- [Multiple redundancy schemes](../config/pool.en.md#scheme): Replication, XOR n+1, Reed-Solomon erasure codes
|
- [Multiple redundancy schemes](../config/pool.en.md#scheme): Replication, XOR n+1, Reed-Solomon erasure codes
|
||||||
based on jerasure library with any number of data and parity drives in a group
|
based on jerasure and ISA-L libraries with any number of data and parity drives in a group
|
||||||
- Configuration via simple JSON data structures in etcd (parameters, pools and images)
|
- Configuration via simple JSON data structures in etcd (parameters, pools and images)
|
||||||
- Automatic data distribution over OSDs, with support for:
|
- Automatic data distribution over OSDs, with support for:
|
||||||
- Mathematical optimization for better uniformity and less data movement
|
- Mathematical optimization for better uniformity and less data movement
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
- [Debian and CentOS packages](../installation/packages.en.md)
|
- [Debian and CentOS packages](../installation/packages.en.md)
|
||||||
- [Image management CLI (vitastor-cli)](../usage/cli.en.md)
|
- [Image management CLI (vitastor-cli)](../usage/cli.en.md)
|
||||||
|
- [Disk management CLI (vitastor-disk)](docs/usage/disk.en.md)
|
||||||
- Generic user-space client library
|
- Generic user-space client library
|
||||||
- [Native QEMU driver](../usage/qemu.en.md)
|
- [Native QEMU driver](../usage/qemu.en.md)
|
||||||
- [Loadable fio engine for benchmarks](../usage/fio.en.md)
|
- [Loadable fio engine for benchmarks](../usage/fio.en.md)
|
||||||
@@ -47,7 +48,6 @@
|
|||||||
|
|
||||||
The following features are planned for the future:
|
The following features are planned for the future:
|
||||||
|
|
||||||
- Better OSD creation and auto-start tools
|
|
||||||
- Other administrative tools
|
- Other administrative tools
|
||||||
- Web GUI
|
- Web GUI
|
||||||
- OpenNebula plugin
|
- OpenNebula plugin
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
- Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа
|
- Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа
|
||||||
- [Производительность](../comparison1.ru.md) ;-D
|
- [Производительность](../comparison1.ru.md) ;-D
|
||||||
- [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок
|
- [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок
|
||||||
Рида-Соломона на основе библиотеки jerasure с любым числом дисков данных и чётности в группе
|
Рида-Соломона на основе библиотек jerasure и ISA-L с любым числом дисков данных и чётности в группе
|
||||||
- Конфигурация через простые человекочитаемые JSON-структуры в etcd
|
- Конфигурация через простые человекочитаемые JSON-структуры в etcd
|
||||||
- Автоматическое распределение данных по OSD, с поддержкой:
|
- Автоматическое распределение данных по OSD, с поддержкой:
|
||||||
- Математической оптимизации для лучшей равномерности распределения и минимизации перемещений данных
|
- Математической оптимизации для лучшей равномерности распределения и минимизации перемещений данных
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
- [Пакеты для Debian и CentOS](../installation/packages.ru.md)
|
- [Пакеты для Debian и CentOS](../installation/packages.ru.md)
|
||||||
- [Консольный интерфейс управления образами (vitastor-cli)](../usage/cli.ru.md)
|
- [Консольный интерфейс управления образами (vitastor-cli)](../usage/cli.ru.md)
|
||||||
|
- [Инструмент управления дисками (vitastor-disk)](docs/usage/disk.ru.md)
|
||||||
- Общая пользовательская клиентская библиотека для работы с кластером
|
- Общая пользовательская клиентская библиотека для работы с кластером
|
||||||
- [Драйвер диска для QEMU](../usage/qemu.ru.md)
|
- [Драйвер диска для QEMU](../usage/qemu.ru.md)
|
||||||
- [Драйвер диска для утилиты тестирования производительности fio](../usage/fio.ru.md)
|
- [Драйвер диска для утилиты тестирования производительности fio](../usage/fio.ru.md)
|
||||||
@@ -47,7 +48,6 @@
|
|||||||
|
|
||||||
## Планы развития
|
## Планы развития
|
||||||
|
|
||||||
- Более корректные скрипты разметки дисков и автоматического запуска OSD
|
|
||||||
- Другие инструменты администрирования
|
- Другие инструменты администрирования
|
||||||
- Web-интерфейс
|
- Web-интерфейс
|
||||||
- Плагин для OpenNebula
|
- Плагин для OpenNebula
|
||||||
|
@@ -26,9 +26,14 @@
|
|||||||
## Configure monitors
|
## Configure monitors
|
||||||
|
|
||||||
On the monitor hosts:
|
On the monitor hosts:
|
||||||
- Edit variables at the top of `/usr/lib/vitastor/mon/make-units.sh` to desired values.
|
- Put identical etcd_address into `/etc/vitastor/vitastor.conf`. Example:
|
||||||
- Create systemd units for the monitor and etcd: `/usr/lib/vitastor/mon/make-units.sh`
|
```
|
||||||
- Start etcd and monitors: `systemctl start etcd vitastor-mon`
|
{
|
||||||
|
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Create systemd units for etcd by running: `/usr/lib/vitastor/mon/make-etcd`
|
||||||
|
- Start etcd and monitors: `systemctl enable --now etcd vitastor-mon`
|
||||||
|
|
||||||
## Configure OSDs
|
## Configure OSDs
|
||||||
|
|
||||||
@@ -40,11 +45,9 @@ On the monitor hosts:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
- Initialize OSDs:
|
- Initialize OSDs:
|
||||||
- Simplest, SSD-only: `/usr/lib/vitastor/mon/make-osd.sh /dev/disk/by-partuuid/XXX [/dev/disk/by-partuuid/YYY ...]`
|
- SSD-only: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`
|
||||||
**Warning!** This very simple script by default makes units for server-grade SSDs with write-through cache!
|
- Hybrid, SSD+HDD: `vitastor-disk prepare --hybrid /dev/sdXXX [/dev/sdYYY ...]`.
|
||||||
If it's not your case, you MUST remove disable_data_fsync and immediate_commit from systemd units.
|
Pass all your devices (HDD and SSD) to this script — it will partition disks and initialize journals on its own.
|
||||||
- Hybrid, HDD+SSD: `/usr/lib/vitastor/mon/make-osd-hybrid.js /dev/sda /dev/sdb ...` — pass all your
|
|
||||||
devices (HDD and SSD) to this script — it will partition disks and initialize journals on its own.
|
|
||||||
This script skips HDDs which are already partitioned so if you want to use non-empty disks for
|
This script skips HDDs which are already partitioned so if you want to use non-empty disks for
|
||||||
Vitastor you should first wipe them with `wipefs -a`. SSDs with GPT partition table are not skipped,
|
Vitastor you should first wipe them with `wipefs -a`. SSDs with GPT partition table are not skipped,
|
||||||
but some free unpartitioned space must be available because the script creates new partitions for journals.
|
but some free unpartitioned space must be available because the script creates new partitions for journals.
|
||||||
@@ -63,11 +66,11 @@ etcdctl --endpoints=... put /vitastor/config/pools '{"1":{"name":"testpool",
|
|||||||
"scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":256,"failure_domain":"host"}}'
|
"scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":256,"failure_domain":"host"}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
For jerasure pools the configuration should look like the following:
|
For EC pools the configuration should look like the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
|
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
|
||||||
"scheme":"jerasure","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}`
|
"scheme":"ec","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
After you do this, one of the monitors will configure PGs and OSDs will start them.
|
After you do this, one of the monitors will configure PGs and OSDs will start them.
|
||||||
|
@@ -26,16 +26,14 @@
|
|||||||
## Настройте мониторы
|
## Настройте мониторы
|
||||||
|
|
||||||
На хостах, выделенных под мониторы:
|
На хостах, выделенных под мониторы:
|
||||||
- Пропишите нужные вам значения в файле `/usr/lib/vitastor/mon/make-units.sh`
|
- Пропишите одинаковые etcd_address в `/etc/vitastor/vitastor.conf`. Например:
|
||||||
- Создайте юниты systemd для etcd и мониторов: `/usr/lib/vitastor/mon/make-units.sh`
|
|
||||||
- Запустите etcd и мониторы: `systemctl start etcd vitastor-mon`
|
|
||||||
- Пропишите etcd_address и osd_network в `/etc/vitastor/vitastor.conf`. Например:
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"],
|
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
|
||||||
"osd_network": "10.200.1.0/24"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
- Инициализируйте сервисы etcd, запустив `/usr/lib/vitastor/mon/make-etcd`
|
||||||
|
- Запустите etcd и мониторы: `systemctl enable --now etcd vitastor-mon`
|
||||||
|
|
||||||
## Настройте OSD
|
## Настройте OSD
|
||||||
|
|
||||||
@@ -47,12 +45,10 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
- Инициализуйте OSD:
|
- Инициализуйте OSD:
|
||||||
- SSD: `/usr/lib/vitastor/make-osd.sh /dev/disk/by-partuuid/XXX [/dev/disk/by-partuuid/YYY ...]`. \
|
- SSD: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`
|
||||||
**Внимание!** Скрипт по умолчанию рассчитан на то, что у вас диски с конденсаторами и отключённым
|
- Гибридные, SSD+HDD: `vitastor-disk prepare --hybrid /dev/sdXXX [/dev/sdYYY ...]`.
|
||||||
кэшем! Если это не так, из юнитов systemd нужно убрать строчки disable_data_fsync и immediate_commit!
|
Передайте все ваши SSD и HDD скрипту в командной строке подряд, скрипт автоматически выделит
|
||||||
- Гибридные, HDD+SSD: `/usr/lib/vitastor/mon/make-osd-hybrid.js /dev/sda /dev/sdb ...` - передайте
|
разделы под журналы на SSD и данные на HDD. Скрипт пропускает HDD, на которых уже есть разделы
|
||||||
все ваши SSD и HDD скрипту в командной строке подряд, скрипт автоматически выделит разделы под
|
|
||||||
журналы на SSD и данные на HDD. Скрипт пропускает HDD, на которых уже есть разделы
|
|
||||||
или вообще какие-то данные, поэтому если диски непустые, сначала очистите их с помощью
|
или вообще какие-то данные, поэтому если диски непустые, сначала очистите их с помощью
|
||||||
`wipefs -a`. SSD с таблицей разделов не пропускаются, но так как скрипт создаёт новые разделы
|
`wipefs -a`. SSD с таблицей разделов не пропускаются, но так как скрипт создаёт новые разделы
|
||||||
для журналов, на SSD должно быть доступно свободное нераспределённое место.
|
для журналов, на SSD должно быть доступно свободное нераспределённое место.
|
||||||
@@ -75,7 +71,7 @@ etcdctl --endpoints=... put /vitastor/config/pools '{"1":{"name":"testpool",
|
|||||||
|
|
||||||
```
|
```
|
||||||
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
|
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
|
||||||
"scheme":"jerasure","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}`
|
"scheme":"ec","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
После этого один из мониторов должен сконфигурировать PG, а OSD должны запустить их.
|
После этого один из мониторов должен сконфигурировать PG, а OSD должны запустить их.
|
||||||
|
@@ -14,13 +14,14 @@ It supports the following commands:
|
|||||||
- [df](#df)
|
- [df](#df)
|
||||||
- [ls](#ls)
|
- [ls](#ls)
|
||||||
- [create](#create)
|
- [create](#create)
|
||||||
|
- [snap-create](#create)
|
||||||
- [modify](#modify)
|
- [modify](#modify)
|
||||||
- [rm](#rm)
|
- [rm](#rm)
|
||||||
- [flatten](#flatten)
|
- [flatten](#flatten)
|
||||||
- [rm-data](#rm-data)
|
- [rm-data](#rm-data)
|
||||||
- [merge-data](#merge-data)
|
- [merge-data](#merge-data)
|
||||||
- [alloc-osd](#alloc-osd)
|
- [alloc-osd](#alloc-osd)
|
||||||
- [simple-offsets](#simple-offsets)
|
- [rm-osd](#rm-osd)
|
||||||
|
|
||||||
Global options:
|
Global options:
|
||||||
|
|
||||||
@@ -38,9 +39,9 @@ Global options:
|
|||||||
|
|
||||||
`vitastor-cli status`
|
`vitastor-cli status`
|
||||||
|
|
||||||
Показать состояние кластера.
|
Show cluster status.
|
||||||
|
|
||||||
Пример вывода:
|
Example output:
|
||||||
|
|
||||||
```
|
```
|
||||||
cluster:
|
cluster:
|
||||||
@@ -65,9 +66,9 @@ Global options:
|
|||||||
|
|
||||||
`vitastor-cli df`
|
`vitastor-cli df`
|
||||||
|
|
||||||
Показать список пулов и занятое место.
|
Show pool space statistics.
|
||||||
|
|
||||||
Пример вывода:
|
Example output:
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
||||||
@@ -76,27 +77,26 @@ size1 1/1 32 199.9 G 10 G 121.5 G 39.23% 100%
|
|||||||
kaveri 2/1 32 0 B 10 G 0 B 100% 0%
|
kaveri 2/1 32 0 B 10 G 0 B 100% 0%
|
||||||
```
|
```
|
||||||
|
|
||||||
В примере у пула "kaveri" эффективность равна нулю, так как все OSD выключены.
|
In the example above, "kaveri" pool has "zero" efficiency because all its OSD are down.
|
||||||
|
|
||||||
## ls
|
## ls
|
||||||
|
|
||||||
`vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
`vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
||||||
|
|
||||||
Показать список образов, если переданы шаблоны `<glob>`, то только с именами,
|
List images (only matching `<glob>` pattern(s) if passed).
|
||||||
соответствующими этим шаблонам (стандартные ФС-шаблоны с * и ?).
|
|
||||||
|
|
||||||
Опции:
|
Options:
|
||||||
|
|
||||||
```
|
```
|
||||||
-p|--pool POOL Фильтровать образы по пулу (ID или имени)
|
-p|--pool POOL Filter images by pool ID or name
|
||||||
-l|--long Также выводить статистику занятого места и ввода-вывода
|
-l|--long Also report allocated size and I/O statistics
|
||||||
--del Также выводить статистику операций удаления
|
--del Also include delete operation statistics
|
||||||
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||||
-r|--reverse Сортировать в обратном порядке
|
-r|--reverse Sort in descending order
|
||||||
-n|--count N Показывать только первые N записей
|
-n|--count N Only list first N items
|
||||||
```
|
```
|
||||||
|
|
||||||
Пример вывода:
|
Example output:
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
||||||
@@ -113,94 +113,80 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
|
|||||||
|
|
||||||
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
||||||
|
|
||||||
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
|
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
|
||||||
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
|
a copy-on-write image clone is created. Parent must be a snapshot (readonly image).
|
||||||
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
|
Pool must be specified if there is more than one pool.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
||||||
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
||||||
```
|
```
|
||||||
|
|
||||||
Создать снимок образа `<name>` (можно использовать любую форму команды). Снимок можно создавать без остановки
|
Create a snapshot of image `<name>` (either form can be used). May be used live if only a single writer is active.
|
||||||
клиентов, если пишущий клиент максимум 1.
|
|
||||||
|
See also about [how to export snapshots](qemu.en.md#exporting-snapshots).
|
||||||
|
|
||||||
## modify
|
## modify
|
||||||
|
|
||||||
`vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]`
|
`vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]`
|
||||||
|
|
||||||
Изменить размер, имя образа или флаг "только для чтения". Снимать флаг "только для чтения"
|
Rename, resize image or change its readonly status. Images with children can't be made read-write.
|
||||||
и уменьшать размер образов, у которых есть дочерние клоны, без `--force` нельзя.
|
If the new size is smaller than the old size, extra data will be purged.
|
||||||
|
You should resize file system in the image, if present, before shrinking it.
|
||||||
Если новый размер меньше старого, "лишние" данные будут удалены, поэтому перед уменьшением
|
|
||||||
образа сначала уменьшите файловую систему в нём.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
-f|--force Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
-f|--force Proceed with shrinking or setting readwrite flag even if the image has children.
|
||||||
```
|
```
|
||||||
|
|
||||||
## rm
|
## rm
|
||||||
|
|
||||||
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
||||||
|
|
||||||
Удалить образ `<from>` или все слои от `<from>` до `<to>` (`<to>` должен быть дочерним
|
Remove `<from>` or all layers between `<from>` and `<to>` (`<to>` must be a child of `<from>`),
|
||||||
образом `<from>`), одновременно меняя родительские образы их клонов (если таковые есть).
|
rebasing all their children accordingly. --writers-stopped allows merging to be a bit
|
||||||
|
more effective in case of a single 'slim' read-write child and 'fat' removed parent:
|
||||||
`--writers-stopped` позволяет чуть более эффективно удалять образы в частом случае, когда
|
the child is merged into parent and parent is renamed to child in that case.
|
||||||
у удаляемой цепочки есть только один дочерний образ, содержащий небольшой объём данных.
|
In other cases parent layers are always merged into children.
|
||||||
В этом случае дочерний образ вливается в родительский и удаляется, а родительский
|
|
||||||
переименовывается в дочерний.
|
|
||||||
|
|
||||||
В других случаях родительские слои вливаются в дочерние.
|
|
||||||
|
|
||||||
## flatten
|
## flatten
|
||||||
|
|
||||||
`vitastor-cli flatten <layer>`
|
`vitastor-cli flatten <layer>`
|
||||||
|
|
||||||
Сделай образ `<layer>` плоским, то есть, скопировать в него данные и разорвать его
|
Flatten a layer, i.e. merge data and detach it from parents.
|
||||||
соединение с родительскими.
|
|
||||||
|
|
||||||
## rm-data
|
## rm-data
|
||||||
|
|
||||||
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||||
|
|
||||||
Удалить данные инода, не меняя метаданные образов.
|
Remove inode data without changing metadata.
|
||||||
|
|
||||||
```
|
```
|
||||||
--wait-list Сначала запросить полный листинг объектов, а потом начать удалять.
|
--wait-list Retrieve full objects listings before starting to remove objects.
|
||||||
Требует больше памяти, но позволяет правильно печатать прогресс удаления.
|
Requires more memory, but allows to show correct removal progress.
|
||||||
--min-offset Удалять только данные, начиная с заданного смещения.
|
--min-offset Purge only data starting with specified offset.
|
||||||
```
|
```
|
||||||
|
|
||||||
## merge-data
|
## merge-data
|
||||||
|
|
||||||
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
||||||
|
|
||||||
Слить данные слоёв, не меняя метаданные. Вливает данные из слоёв от `<from>` до `<to>`
|
Merge layer data without changing metadata. Merge `<from>`..`<to>` to `<target>`.
|
||||||
в целевой образ `<target>`. `<to>` должен быть дочерним образом `<from>`, а `<target>`
|
`<to>` must be a child of `<from>` and `<target>` may be one of the layers between
|
||||||
должен быть одним из слоёв между `<from>` и `<to>`, включая сами `<from>` и `<to>`.
|
`<from>` and `<to>`, including `<from>` and `<to>`.
|
||||||
|
|
||||||
## alloc-osd
|
## alloc-osd
|
||||||
|
|
||||||
`vitastor-cli alloc-osd`
|
`vitastor-cli alloc-osd`
|
||||||
|
|
||||||
Атомарно выделить новый номер OSD и зарезервировать его, создав в etcd пустой
|
Allocate a new OSD number and reserve it by creating empty `/osd/stats/<n>` key.
|
||||||
ключ `/osd/stats/<n>`.
|
|
||||||
|
|
||||||
## simple-offsets
|
## rm-osd
|
||||||
|
|
||||||
`vitastor-cli simple-offsets <device>`
|
`vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]`
|
||||||
|
|
||||||
Рассчитать смещения для простого и тупого создания OSD на диске (без суперблока).
|
Remove metadata and configuration for specified OSD(s) from etcd.
|
||||||
|
|
||||||
Опции (см. также [Дисковые параметры уровня кластера](../config/layout-cluster.ru.md)):
|
Refuses to remove OSDs with data without `--force` and `--allow-data-loss`.
|
||||||
|
|
||||||
```
|
With `--dry-run` only checks if deletion is possible without data loss and
|
||||||
--object_size 128k Размер блока хранилища
|
redundancy degradation.
|
||||||
--bitmap_granularity 4k Гранулярность битовых карт
|
|
||||||
--journal_size 32M Размер журнала
|
|
||||||
--device_block_size 4k Размер блока устройства
|
|
||||||
--journal_offset 0 Смещение журнала
|
|
||||||
--device_size 0 Размер устройства
|
|
||||||
--format text Формат результата: json, options, env или text
|
|
||||||
```
|
|
||||||
|
@@ -15,13 +15,14 @@ vitastor-cli - интерфейс командной строки для адм
|
|||||||
- [df](#df)
|
- [df](#df)
|
||||||
- [ls](#ls)
|
- [ls](#ls)
|
||||||
- [create](#create)
|
- [create](#create)
|
||||||
|
- [snap-create](#create)
|
||||||
- [modify](#modify)
|
- [modify](#modify)
|
||||||
- [rm](#rm)
|
- [rm](#rm)
|
||||||
- [flatten](#flatten)
|
- [flatten](#flatten)
|
||||||
- [rm-data](#rm-data)
|
- [rm-data](#rm-data)
|
||||||
- [merge-data](#merge-data)
|
- [merge-data](#merge-data)
|
||||||
- [alloc-osd](#alloc-osd)
|
- [alloc-osd](#alloc-osd)
|
||||||
- [simple-offsets](#simple-offsets)
|
- [rm-osd](#rm-osd)
|
||||||
|
|
||||||
Глобальные опции:
|
Глобальные опции:
|
||||||
|
|
||||||
@@ -39,9 +40,9 @@ vitastor-cli - интерфейс командной строки для адм
|
|||||||
|
|
||||||
`vitastor-cli status`
|
`vitastor-cli status`
|
||||||
|
|
||||||
Show cluster status.
|
Показать состояние кластера.
|
||||||
|
|
||||||
Example output:
|
Пример вывода:
|
||||||
|
|
||||||
```
|
```
|
||||||
cluster:
|
cluster:
|
||||||
@@ -66,9 +67,9 @@ Example output:
|
|||||||
|
|
||||||
`vitastor-cli df`
|
`vitastor-cli df`
|
||||||
|
|
||||||
Show pool space statistics.
|
Показать список пулов и занятое место.
|
||||||
|
|
||||||
Example output:
|
Пример вывода:
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
||||||
@@ -77,26 +78,27 @@ size1 1/1 32 199.9 G 10 G 121.5 G 39.23% 100%
|
|||||||
kaveri 2/1 32 0 B 10 G 0 B 100% 0%
|
kaveri 2/1 32 0 B 10 G 0 B 100% 0%
|
||||||
```
|
```
|
||||||
|
|
||||||
In the example above, "kaveri" pool has "zero" efficiency because all its OSD are down.
|
В примере у пула "kaveri" эффективность равна нулю, так как все OSD выключены.
|
||||||
|
|
||||||
## ls
|
## ls
|
||||||
|
|
||||||
`vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
`vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
||||||
|
|
||||||
List images (only matching `<glob>` pattern(s) if passed).
|
Показать список образов, если переданы шаблоны `<glob>`, то только с именами,
|
||||||
|
соответствующими этим шаблонам (стандартные ФС-шаблоны с * и ?).
|
||||||
|
|
||||||
Options:
|
Опции:
|
||||||
|
|
||||||
```
|
```
|
||||||
-p|--pool POOL Filter images by pool ID or name
|
-p|--pool POOL Фильтровать образы по пулу (ID или имени)
|
||||||
-l|--long Also report allocated size and I/O statistics
|
-l|--long Также выводить статистику занятого места и ввода-вывода
|
||||||
--del Also include delete operation statistics
|
--del Также выводить статистику операций удаления
|
||||||
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||||
-r|--reverse Sort in descending order
|
-r|--reverse Сортировать в обратном порядке
|
||||||
-n|--count N Only list first N items
|
-n|--count N Показывать только первые N записей
|
||||||
```
|
```
|
||||||
|
|
||||||
Example output:
|
Пример вывода:
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
||||||
@@ -113,85 +115,89 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
|
|||||||
|
|
||||||
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
||||||
|
|
||||||
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
|
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
|
||||||
a copy-on-write image clone is created. Parent must be a snapshot (readonly image).
|
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
|
||||||
Pool must be specified if there is more than one pool.
|
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
||||||
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a snapshot of image `<name>` (either form can be used). May be used live if only a single writer is active.
|
Создать снимок образа `<name>` (можно использовать любую форму команды). Снимок можно создавать без остановки
|
||||||
|
клиентов, если пишущий клиент максимум 1.
|
||||||
|
|
||||||
|
Смотрите также информацию о том, [как экспортировать снимки](qemu.ru.md#экспорт-снимков).
|
||||||
|
|
||||||
## modify
|
## modify
|
||||||
|
|
||||||
`vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]`
|
`vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]`
|
||||||
|
|
||||||
Rename, resize image or change its readonly status. Images with children can't be made read-write.
|
Изменить размер, имя образа или флаг "только для чтения". Снимать флаг "только для чтения"
|
||||||
If the new size is smaller than the old size, extra data will be purged.
|
и уменьшать размер образов, у которых есть дочерние клоны, без `--force` нельзя.
|
||||||
You should resize file system in the image, if present, before shrinking it.
|
|
||||||
|
Если новый размер меньше старого, "лишние" данные будут удалены, поэтому перед уменьшением
|
||||||
|
образа сначала уменьшите файловую систему в нём.
|
||||||
|
|
||||||
```
|
```
|
||||||
-f|--force Proceed with shrinking or setting readwrite flag even if the image has children.
|
-f|--force Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
||||||
```
|
```
|
||||||
|
|
||||||
## rm
|
## rm
|
||||||
|
|
||||||
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
||||||
|
|
||||||
Remove `<from>` or all layers between `<from>` and `<to>` (`<to>` must be a child of `<from>`),
|
Удалить образ `<from>` или все слои от `<from>` до `<to>` (`<to>` должен быть дочерним
|
||||||
rebasing all their children accordingly. --writers-stopped allows merging to be a bit
|
образом `<from>`), одновременно меняя родительские образы их клонов (если таковые есть).
|
||||||
more effective in case of a single 'slim' read-write child and 'fat' removed parent:
|
|
||||||
the child is merged into parent and parent is renamed to child in that case.
|
`--writers-stopped` позволяет чуть более эффективно удалять образы в частом случае, когда
|
||||||
In other cases parent layers are always merged into children.
|
у удаляемой цепочки есть только один дочерний образ, содержащий небольшой объём данных.
|
||||||
|
В этом случае дочерний образ вливается в родительский и удаляется, а родительский
|
||||||
|
переименовывается в дочерний.
|
||||||
|
|
||||||
|
В других случаях родительские слои вливаются в дочерние.
|
||||||
|
|
||||||
## flatten
|
## flatten
|
||||||
|
|
||||||
`vitastor-cli flatten <layer>`
|
`vitastor-cli flatten <layer>`
|
||||||
|
|
||||||
Flatten a layer, i.e. merge data and detach it from parents.
|
Сделай образ `<layer>` плоским, то есть, скопировать в него данные и разорвать его
|
||||||
|
соединение с родительскими.
|
||||||
|
|
||||||
## rm-data
|
## rm-data
|
||||||
|
|
||||||
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||||
|
|
||||||
Remove inode data without changing metadata.
|
Удалить данные инода, не меняя метаданные образов.
|
||||||
|
|
||||||
```
|
```
|
||||||
--wait-list Retrieve full objects listings before starting to remove objects.
|
--wait-list Сначала запросить полный листинг объектов, а потом начать удалять.
|
||||||
Requires more memory, but allows to show correct removal progress.
|
Требует больше памяти, но позволяет правильно печатать прогресс удаления.
|
||||||
--min-offset Purge only data starting with specified offset.
|
--min-offset Удалять только данные, начиная с заданного смещения.
|
||||||
```
|
```
|
||||||
|
|
||||||
## merge-data
|
## merge-data
|
||||||
|
|
||||||
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
||||||
|
|
||||||
Merge layer data without changing metadata. Merge `<from>`..`<to>` to `<target>`.
|
Слить данные слоёв, не меняя метаданные. Вливает данные из слоёв от `<from>` до `<to>`
|
||||||
`<to>` must be a child of `<from>` and `<target>` may be one of the layers between
|
в целевой образ `<target>`. `<to>` должен быть дочерним образом `<from>`, а `<target>`
|
||||||
`<from>` and `<to>`, including `<from>` and `<to>`.
|
должен быть одним из слоёв между `<from>` и `<to>`, включая сами `<from>` и `<to>`.
|
||||||
|
|
||||||
## alloc-osd
|
## alloc-osd
|
||||||
|
|
||||||
`vitastor-cli alloc-osd`
|
`vitastor-cli alloc-osd`
|
||||||
|
|
||||||
Allocate a new OSD number and reserve it by creating empty `/osd/stats/<n>` key.
|
Атомарно выделить новый номер OSD и зарезервировать его, создав в etcd пустой
|
||||||
|
ключ `/osd/stats/<n>`.
|
||||||
|
|
||||||
## simple-offsets
|
## rm-osd
|
||||||
|
|
||||||
`vitastor-cli simple-offsets <device>`
|
`vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]`
|
||||||
|
|
||||||
Calculate offsets for simple&stupid (no superblock) OSD deployment.
|
Удалить метаданные и конфигурацию для заданных OSD из etcd.
|
||||||
|
|
||||||
Options (see also [Cluster-Wide Disk Layout Parameters](../config/layout-cluster.en.md)):
|
Отказывается удалять OSD с данными без опций `--force` и `--allow-data-loss`.
|
||||||
|
|
||||||
```
|
С опцией `--dry-run` только проверяет, возможно ли удаление без потери данных и деградации
|
||||||
--object_size 128k Set blockstore block size
|
избыточности.
|
||||||
--bitmap_granularity 4k Set bitmap granularity
|
|
||||||
--journal_size 32M Set journal size
|
|
||||||
--device_block_size 4k Set device block size
|
|
||||||
--journal_offset 0 Set journal offset
|
|
||||||
--device_size 0 Set device size
|
|
||||||
--format text Result format: json, options, env, or text
|
|
||||||
```
|
|
||||||
|
257
docs/usage/disk.en.md
Normal file
257
docs/usage/disk.en.md
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
[Documentation](../../README.md#documentation) → Usage → Disk Tool
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Читать на русском](disk.ru.md)
|
||||||
|
|
||||||
|
# Disk management tool
|
||||||
|
|
||||||
|
vitastor-disk is a command-line tool for physical Vitastor disk management.
|
||||||
|
|
||||||
|
It supports the following commands:
|
||||||
|
|
||||||
|
- [prepare](#prepare)
|
||||||
|
- [upgrade-simple](#upgrade-simple)
|
||||||
|
- [resize](#resize)
|
||||||
|
- [start/stop/restart/enable/disable](#start/stop/restart/enable/disable)
|
||||||
|
- [purge](#purge)
|
||||||
|
- [read-sb](#read-sb)
|
||||||
|
- [write-sb](#write-sb)
|
||||||
|
- [udev](#udev)
|
||||||
|
- [exec-osd](#exec-osd)
|
||||||
|
- [pre-exec](#pre-exec)
|
||||||
|
- Debugging:
|
||||||
|
- [dump-journal](#dump-journal)
|
||||||
|
- [write-journal](#write-journal)
|
||||||
|
- [dump-meta](#dump-meta)
|
||||||
|
- [write-meta](#write-meta)
|
||||||
|
- [simple-offsets](#simple-offsets)
|
||||||
|
|
||||||
|
## prepare
|
||||||
|
|
||||||
|
`vitastor-disk prepare [OPTIONS] [devices...]`
|
||||||
|
|
||||||
|
Initialize disk(s) for Vitastor OSD(s).
|
||||||
|
|
||||||
|
There are two modes of this command. In the first mode, you pass `<devices>` which
|
||||||
|
must be raw disks (not partitions). They are partitioned automatically and OSDs
|
||||||
|
are initialized on all of them.
|
||||||
|
|
||||||
|
In the second mode, you omit `<devices>` and pass `--data_device`, `--journal_device`
|
||||||
|
and/or `--meta_device` which must be already existing partitions identified by their
|
||||||
|
GPT partition UUIDs. In this case a single OSD is created.
|
||||||
|
|
||||||
|
Requires `vitastor-cli`, `wipefs`, `sfdisk` and `partprobe` (from parted) utilities.
|
||||||
|
|
||||||
|
Options (automatic mode):
|
||||||
|
|
||||||
|
```
|
||||||
|
--osd_per_disk <N>
|
||||||
|
Create <N> OSDs on each disk (default 1)
|
||||||
|
--hybrid
|
||||||
|
Prepare hybrid (HDD+SSD) OSDs using provided devices. SSDs will be used for
|
||||||
|
journals and metadata, HDDs will be used for data. Partitions for journals and
|
||||||
|
metadata will be created automatically. Whether disks are SSD or HDD is decided
|
||||||
|
by the `/sys/block/.../queue/rotational` flag. In hybrid mode, default object
|
||||||
|
size is 1 MB instead of 128 KB, default journal size is 1 GB instead of 32 MB,
|
||||||
|
and throttle_small_writes is enabled by default.
|
||||||
|
--disable_data_fsync auto
|
||||||
|
Disable data device cache and fsync (1/yes/true = on, default auto)
|
||||||
|
--disable_meta_fsync auto
|
||||||
|
Disable metadata/journal device cache and fsync (default auto)
|
||||||
|
--meta_reserve 2x,1G
|
||||||
|
New metadata partitions in --hybrid mode are created larger than actual
|
||||||
|
metadata size to ease possible future extension. The default is to allocate
|
||||||
|
2 times more space and at least 1G. Use this option to override.
|
||||||
|
--max_other 10%
|
||||||
|
Use disks for OSD data even if they already have non-Vitastor partitions,
|
||||||
|
but only if these take up no more than this percent of disk space.
|
||||||
|
```
|
||||||
|
|
||||||
|
Options (single-device mode):
|
||||||
|
|
||||||
|
```
|
||||||
|
--data_device <DEV> Use partition <DEV> for data
|
||||||
|
--meta_device <DEV> Use partition <DEV> for metadata (optional)
|
||||||
|
--journal_device <DEV> Use partition <DEV> for journal (optional)
|
||||||
|
--disable_data_fsync 0 Disable data device cache and fsync (default off)
|
||||||
|
--disable_meta_fsync 0 Disable metadata device cache and fsync (default off)
|
||||||
|
--disable_journal_fsync 0 Disable journal device cache and fsync (default off)
|
||||||
|
--force Bypass partition safety checks (for emptiness and so on)
|
||||||
|
```
|
||||||
|
|
||||||
|
Options (both modes):
|
||||||
|
|
||||||
|
```
|
||||||
|
--journal_size 1G/32M Set journal size (area or partition size)
|
||||||
|
--block_size 1M/128k Set blockstore object size
|
||||||
|
--bitmap_granularity 4k Set bitmap granularity
|
||||||
|
--data_device_block 4k Override data device block size
|
||||||
|
--meta_device_block 4k Override metadata device block size
|
||||||
|
--journal_device_block 4k Override journal device block size
|
||||||
|
```
|
||||||
|
|
||||||
|
[immediate_commit](../config/layout-cluster.en.md#immediate_commit) setting is
|
||||||
|
automatically derived from "disable fsync" options. It's set to "all" when fsync
|
||||||
|
is disabled on all devices, and to "small" if fsync is only disabled on journal device.
|
||||||
|
|
||||||
|
When data/meta/journal fsyncs are disabled, the OSD startup script automatically
|
||||||
|
checks the device cache status on start and tries to disable cache for SATA/SAS disks.
|
||||||
|
If it doesn't succeed it issues a warning in the system log.
|
||||||
|
|
||||||
|
You can also pass other OSD options here as arguments and they'll be persisted
|
||||||
|
to the superblock: max_write_iodepth, max_write_iodepth, min_flusher_count,
|
||||||
|
max_flusher_count, inmemory_metadata, inmemory_journal, journal_sector_buffer_count,
|
||||||
|
journal_no_same_sector_overwrites, throttle_small_writes, throttle_target_iops,
|
||||||
|
throttle_target_mbs, throttle_target_parallelism, throttle_threshold_us.
|
||||||
|
See [Runtime OSD Parameters](../config/osd.en.md) for details.
|
||||||
|
|
||||||
|
## upgrade-simple
|
||||||
|
|
||||||
|
`vitastor-disk upgrade-simple <UNIT_FILE|OSD_NUMBER>`
|
||||||
|
|
||||||
|
Upgrade an OSD created by old (0.7.1 and older) `make-osd.sh` or `make-osd-hybrid.js` scripts.
|
||||||
|
|
||||||
|
Adds superblocks to OSD devices, disables old `vitastor-osdN` unit and replaces it with `vitastor-osd@N`.
|
||||||
|
Can be invoked with an osd number of with a path to systemd service file `UNIT_FILE` which
|
||||||
|
must be `/etc/systemd/system/vitastor-osd<OSD_NUMBER>.service`.
|
||||||
|
|
||||||
|
Note that the procedure isn't atomic and may ruin OSD data in case of an interrupt,
|
||||||
|
so don't upgrade all your OSDs in parallel.
|
||||||
|
|
||||||
|
Requires the `sfdisk` utility.
|
||||||
|
|
||||||
|
## resize
|
||||||
|
|
||||||
|
`vitastor-disk resize <ALL_OSD_PARAMETERS> <NEW_LAYOUT> [--iodepth 32]`
|
||||||
|
|
||||||
|
Resize data area and/or rewrite/move journal and metadata.
|
||||||
|
|
||||||
|
`ALL_OSD_PARAMETERS` must include all (at least all disk-related)
|
||||||
|
parameters from OSD command line (i.e. from systemd unit or superblock).
|
||||||
|
|
||||||
|
`NEW_LAYOUT` may include new disk layout parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
--new_data_offset SIZE resize data area so it starts at SIZE
|
||||||
|
--new_data_len SIZE resize data area to SIZE bytes
|
||||||
|
--new_meta_device PATH use PATH for new metadata
|
||||||
|
--new_meta_offset SIZE make new metadata area start at SIZE
|
||||||
|
--new_meta_len SIZE make new metadata area SIZE bytes long
|
||||||
|
--new_journal_device PATH use PATH for new journal
|
||||||
|
--new_journal_offset SIZE make new journal area start at SIZE
|
||||||
|
--new_journal_len SIZE make new journal area SIZE bytes long
|
||||||
|
```
|
||||||
|
|
||||||
|
SIZE may include k/m/g/t suffixes. If any of the new layout parameter
|
||||||
|
options are not specified, old values will be used.
|
||||||
|
|
||||||
|
## start/stop/restart/enable/disable
|
||||||
|
|
||||||
|
`vitastor-disk start|stop|restart|enable|disable [--now] <device> [device2 device3 ...]`
|
||||||
|
|
||||||
|
Manipulate Vitastor OSDs using systemd by their device paths.
|
||||||
|
|
||||||
|
Commands are passed to `systemctl` with `vitastor-osd@<num>` units as arguments.
|
||||||
|
|
||||||
|
When `--now` is added to enable/disable, OSDs are also immediately started/stopped.
|
||||||
|
|
||||||
|
## purge
|
||||||
|
|
||||||
|
`vitastor-disk purge [--force] [--allow-data-loss] <device> [device2 device3 ...]`
|
||||||
|
|
||||||
|
Purge Vitastor OSD(s) on specified device(s). Uses `vitastor-cli rm-osd` to check
|
||||||
|
if deletion is possible without data loss and to actually remove metadata from etcd.
|
||||||
|
`--force` and `--allow-data-loss` options may be used to ignore safety check results.
|
||||||
|
|
||||||
|
Requires `vitastor-cli`, `sfdisk` and `partprobe` (from parted) utilities.
|
||||||
|
|
||||||
|
## read-sb
|
||||||
|
|
||||||
|
`vitastor-disk read-sb [--force] <device>`
|
||||||
|
|
||||||
|
Try to read Vitastor OSD superblock from `<device>` and print it in JSON format.
|
||||||
|
`--force` allows to ignore validation errors.
|
||||||
|
|
||||||
|
## write-sb
|
||||||
|
|
||||||
|
`vitastor-disk write-sb <device>`
|
||||||
|
|
||||||
|
Read JSON from STDIN and write it into Vitastor OSD superblock on `<device>`.
|
||||||
|
|
||||||
|
## udev
|
||||||
|
|
||||||
|
`vitastor-disk udev <device>`
|
||||||
|
|
||||||
|
Try to read Vitastor OSD superblock from `<device>` and print variables for udev.
|
||||||
|
|
||||||
|
## exec-osd
|
||||||
|
|
||||||
|
`vitastor-disk exec-osd <device>`
|
||||||
|
|
||||||
|
Read Vitastor OSD superblock from `<device>` and start the OSD with parameters from it.
|
||||||
|
|
||||||
|
Intended for use from startup scripts (i.e. from systemd units).
|
||||||
|
|
||||||
|
## pre-exec
|
||||||
|
|
||||||
|
`vitastor-disk pre-exec <device>`
|
||||||
|
|
||||||
|
Read Vitastor OSD superblock from `<device>` and perform pre-start checks for the OSD.
|
||||||
|
|
||||||
|
For now, this only checks that device cache is in write-through mode if fsync is disabled.
|
||||||
|
|
||||||
|
Intended for use from startup scripts (i.e. from systemd units).
|
||||||
|
|
||||||
|
## dump-journal
|
||||||
|
|
||||||
|
`vitastor-disk dump-journal [OPTIONS] <journal_file> <journal_block_size> <offset> <size>`
|
||||||
|
|
||||||
|
Dump journal in human-readable or JSON (if `--json` is specified) format.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
```
|
||||||
|
--all Scan the whole journal area for entries and dump them, even outdated ones
|
||||||
|
--json Dump journal in JSON format
|
||||||
|
--format entries (Default) Dump actual journal entries as an array, without data
|
||||||
|
--format data Same as "entries", but also include small write data
|
||||||
|
--format blocks Dump as an array of journal blocks each containing array of entries
|
||||||
|
```
|
||||||
|
|
||||||
|
## write-journal
|
||||||
|
|
||||||
|
`vitastor-disk write-journal <journal_file> <journal_block_size> <bitmap_size> <offset> <size>`
|
||||||
|
|
||||||
|
Write journal from JSON taken from standard input in the same format as produced by
|
||||||
|
`dump-journal --json --format data`.
|
||||||
|
|
||||||
|
## dump-meta
|
||||||
|
|
||||||
|
`vitastor-disk dump-meta <meta_file> <meta_block_size> <offset> <size>`
|
||||||
|
|
||||||
|
Dump metadata in JSON format.
|
||||||
|
|
||||||
|
## write-meta
|
||||||
|
|
||||||
|
`vitastor-disk write-meta <meta_file> <offset> <size>`
|
||||||
|
|
||||||
|
Write metadata from JSON taken from standard input in the same format as produced by `dump-meta`.
|
||||||
|
|
||||||
|
## simple-offsets
|
||||||
|
|
||||||
|
`vitastor-disk simple-offsets <device>`
|
||||||
|
|
||||||
|
Calculate offsets for old simple&stupid (no superblock) OSD deployment.
|
||||||
|
|
||||||
|
Options (see also [Cluster-Wide Disk Layout Parameters](../config/layout-cluster.en.md)):
|
||||||
|
|
||||||
|
```
|
||||||
|
--object_size 128k Set blockstore block size
|
||||||
|
--bitmap_granularity 4k Set bitmap granularity
|
||||||
|
--journal_size 32M Set journal size
|
||||||
|
--device_block_size 4k Set device block size
|
||||||
|
--journal_offset 0 Set journal offset
|
||||||
|
--device_size 0 Set device size
|
||||||
|
--format text Result format: json, options, env, or text
|
||||||
|
```
|
262
docs/usage/disk.ru.md
Normal file
262
docs/usage/disk.ru.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
[Документация](../../README-ru.md#документация) → Использование → Управление дисками
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Read in English](disk.en.md)
|
||||||
|
|
||||||
|
# Инструмент управления дисками
|
||||||
|
|
||||||
|
vitastor-disk - инструмент командной строки для управления дисками Vitastor OSD.
|
||||||
|
|
||||||
|
Поддерживаются следующие команды:
|
||||||
|
|
||||||
|
- [prepare](#prepare)
|
||||||
|
- [upgrade-simple](#upgrade-simple)
|
||||||
|
- [resize](#resize)
|
||||||
|
- [start/stop/restart/enable/disable](#start/stop/restart/enable/disable)
|
||||||
|
- [purge](#purge)
|
||||||
|
- [read-sb](#read-sb)
|
||||||
|
- [write-sb](#write-sb)
|
||||||
|
- [udev](#udev)
|
||||||
|
- [exec-osd](#exec-osd)
|
||||||
|
- [pre-exec](#pre-exec)
|
||||||
|
- Для отладки:
|
||||||
|
- [dump-journal](#dump-journal)
|
||||||
|
- [write-journal](#write-journal)
|
||||||
|
- [dump-meta](#dump-meta)
|
||||||
|
- [write-meta](#write-meta)
|
||||||
|
- [simple-offsets](#simple-offsets)
|
||||||
|
|
||||||
|
## prepare
|
||||||
|
|
||||||
|
`vitastor-disk prepare [OPTIONS] [devices...]`
|
||||||
|
|
||||||
|
Подготовить диск(и) для OSD Vitastor.
|
||||||
|
|
||||||
|
У команды есть 2 режима. В первом режиме вы указываете список устройств `<devices>`,
|
||||||
|
которые должны быть целыми дисками (не разделами). На них автоматически создаются
|
||||||
|
разделы и инициализируются OSD.
|
||||||
|
|
||||||
|
Во втором режиме вместо списка устройств вы указываете пути к отдельным устройствам
|
||||||
|
`--data_device`, `--journal_device` и/или `--meta_device`, которые должны быть
|
||||||
|
уже существующими GPT-разделами. В этом случае инициализируется ровно один OSD.
|
||||||
|
|
||||||
|
Команде требуются утилиты `vitastor-cli`, `wipefs`, `sfdisk` и `partprobe` (из состава parted).
|
||||||
|
|
||||||
|
Опции для автоматического режима:
|
||||||
|
|
||||||
|
```
|
||||||
|
--osd_per_disk <N>
|
||||||
|
Создавать по несколько (<N>) OSD на каждом диске (по умолчанию 1)
|
||||||
|
--hybrid
|
||||||
|
Инициализировать гибридные (HDD+SSD) OSD на указанных дисках. SSD будут
|
||||||
|
использованы для журналов и метаданных, а HDD - для данных. Разделы для журналов
|
||||||
|
и метаданных будут созданы автоматически. Является ли диск SSD или HDD, определяется
|
||||||
|
по флагу `/sys/block/.../queue/rotational`. В гибридном режиме по умолчанию
|
||||||
|
используется размер объекта 1 МБ вместо 128 КБ, размер журнала 1 ГБ вместо 32 МБ
|
||||||
|
и включённый throttle_small_writes.
|
||||||
|
--disable_data_fsync auto
|
||||||
|
Отключать кэш и fsync-и для устройств данных. (1/yes/true = да, по умолчанию автоопределение)
|
||||||
|
--disable_meta_fsync auto
|
||||||
|
Отключать кэш и fsync-и для журналов и метаданных (по умолчанию автоопределение)
|
||||||
|
--meta_reserve 2x,1G
|
||||||
|
В гибридном режиме для метаданных выделяется больше места, чем нужно на самом
|
||||||
|
деле, чтобы оставить запас под будущее расширение. По умолчанию выделяется
|
||||||
|
в 2 раза больше места, и не менее 1 ГБ. Чтобы изменить это поведение,
|
||||||
|
воспользуйтесь данной опцией.
|
||||||
|
--max_other 10%
|
||||||
|
Использовать диски под данные OSD, даже если на них уже есть не-Vitastor-овые
|
||||||
|
разделы, но только в случае, если они занимают не более данного процента диска.
|
||||||
|
```
|
||||||
|
|
||||||
|
Опции для режима одного OSD:
|
||||||
|
|
||||||
|
```
|
||||||
|
--data_device <DEV> Использовать раздел <DEV> для данных
|
||||||
|
--meta_device <DEV> Использовать раздел <DEV> для метаданных (опционально)
|
||||||
|
--journal_device <DEV> Использовать раздел <DEV> для журнала (опционально)
|
||||||
|
--disable_data_fsync 0 Отключить кэш и fsync устройства данных (по умолчанию нет)
|
||||||
|
--disable_meta_fsync 0 Отключить кэш и fsync метаданных (по умолчанию нет)
|
||||||
|
--disable_journal_fsync 0 Отключить кэш и fsync журнала (по умолчанию нет)
|
||||||
|
--force Пропустить проверки разделов (на пустоту и т.п.)
|
||||||
|
```
|
||||||
|
|
||||||
|
Опции для обоих режимов:
|
||||||
|
|
||||||
|
```
|
||||||
|
--journal_size 1G/32M Задать размер журнала (области или раздела журнала)
|
||||||
|
--block_size 1M/128k Задать размер объекта хранилища
|
||||||
|
--bitmap_granularity 4k Задать гранулярность битовых карт
|
||||||
|
--data_device_block 4k Задать размер блока устройства данных
|
||||||
|
--meta_device_block 4k Задать размер блока метаданных
|
||||||
|
--journal_device_block 4k Задать размер блока журнала
|
||||||
|
```
|
||||||
|
|
||||||
|
Настройка [immediate_commit](../config/layout-cluster.ru.md#immediate_commit)
|
||||||
|
автоматически выводится из опций отключения кэша - она устанавливается в "all", если кэш
|
||||||
|
отключён на всех устройствах, и в "small", если он отключён только на устройстве журнала.
|
||||||
|
|
||||||
|
Когда fsync данных/метаданных/журнала отключён, скрипты запуска OSD автоматически
|
||||||
|
проверяют состояние кэша диска и стараются его отключить для SATA/SAS дисков. Если
|
||||||
|
это не удаётся, в системный журнал выводится предупреждение.
|
||||||
|
|
||||||
|
Вы можете передать данной команде и некоторые другие опции OSD в качестве аргументов
|
||||||
|
и они тоже будут сохранены в суперблок: max_write_iodepth, max_write_iodepth, min_flusher_count,
|
||||||
|
max_flusher_count, inmemory_metadata, inmemory_journal, journal_sector_buffer_count,
|
||||||
|
journal_no_same_sector_overwrites, throttle_small_writes, throttle_target_iops,
|
||||||
|
throttle_target_mbs, throttle_target_parallelism, throttle_threshold_us.
|
||||||
|
Читайте об этих параметрах подробнее в разделе [Изменяемые параметры OSD](../config/osd.ru.md).
|
||||||
|
|
||||||
|
## upgrade-simple
|
||||||
|
|
||||||
|
`vitastor-disk upgrade-simple <UNIT_FILE|OSD_NUMBER>`
|
||||||
|
|
||||||
|
Обновить OSD, созданный старыми (0.7.1 и старее) скриптами `make-osd.sh` и `make-osd-hybrid.js`.
|
||||||
|
|
||||||
|
Добавляет суперблок на разделы OSD, отключает старый сервис `vitastor-osdN` и заменяет его на `vitastor-osd@N`.
|
||||||
|
|
||||||
|
Можно вызывать, указывая либо номер OSD, либо путь к файлу сервиса `UNIT_FILE`, но он обязан
|
||||||
|
иметь вид `/etc/systemd/system/vitastor-osd<OSD_NUMBER>.service`.
|
||||||
|
|
||||||
|
Имейте в виду, что процедура обновления не атомарна и при прерывании может уничтожить данные OSD,
|
||||||
|
так что обновляйте ваши OSD по очереди.
|
||||||
|
|
||||||
|
Команде требуется утилита `sfdisk`.
|
||||||
|
|
||||||
|
## resize
|
||||||
|
|
||||||
|
`vitastor-disk resize <ALL_OSD_PARAMETERS> <NEW_LAYOUT> [--iodepth 32]`
|
||||||
|
|
||||||
|
Изменить размер области данных и/или переместить журнал и метаданные.
|
||||||
|
|
||||||
|
В `ALL_OSD_PARAMETERS` нужно указать все относящиеся к диску параметры OSD
|
||||||
|
из суперблока OSD или из файла сервиса systemd (в старых версиях).
|
||||||
|
|
||||||
|
В `NEW_LAYOUT` нужно указать новые параметры расположения данных:
|
||||||
|
|
||||||
|
```
|
||||||
|
--new_data_offset РАЗМЕР сдвинуть начало области данных на РАЗМЕР байт
|
||||||
|
--new_data_len РАЗМЕР изменить размер области данных до РАЗМЕР байт
|
||||||
|
--new_meta_device ПУТЬ использовать ПУТЬ как новое устройство метаданных
|
||||||
|
--new_meta_offset РАЗМЕР разместить новые метаданные по смещению РАЗМЕР байт
|
||||||
|
--new_meta_len РАЗМЕР сделать новые метаданные размером РАЗМЕР байт
|
||||||
|
--new_journal_device ПУТЬ использовать ПУТЬ как новое устройство журнала
|
||||||
|
--new_journal_offset РАЗМЕР разместить новый журнал по смещению РАЗМЕР байт
|
||||||
|
--new_journal_len РАЗМЕР сделать новый журнал размером РАЗМЕР байт
|
||||||
|
```
|
||||||
|
|
||||||
|
РАЗМЕР может быть указан с суффиксами k/m/g/t. Если любой из новых параметров
|
||||||
|
расположения не указан, он принимается равным старому значению.
|
||||||
|
|
||||||
|
## start/stop/restart/enable/disable
|
||||||
|
|
||||||
|
`vitastor-disk start|stop|restart|enable|disable [--now] <device> [device2 device3 ...]`
|
||||||
|
|
||||||
|
Команды управления OSD по путям дисков через systemd.
|
||||||
|
|
||||||
|
Команды транслируются `systemctl` с сервисами `vitastor-osd@<num>` в виде аргументов.
|
||||||
|
|
||||||
|
Когда к командам включения/выключения добавляется параметр `--now`, OSD также сразу
|
||||||
|
запускаются/останавливаются.
|
||||||
|
|
||||||
|
## purge
|
||||||
|
|
||||||
|
`vitastor-disk purge [--force] [--allow-data-loss] <device> [device2 device3 ...]`
|
||||||
|
|
||||||
|
Удалить OSD на заданном диске/дисках. Использует `vitastor-cli rm-osd` для проверки
|
||||||
|
возможности удаления без потери данных и для удаления OSD из etcd. Опции `--force`
|
||||||
|
и `--allow-data-loss` служат для обхода данной защиты в случае необходимости.
|
||||||
|
|
||||||
|
Команде требуются утилиты `vitastor-cli`, `sfdisk` и `partprobe` (из состава parted).
|
||||||
|
|
||||||
|
## read-sb
|
||||||
|
|
||||||
|
`vitastor-disk read-sb [--force] <device>`
|
||||||
|
|
||||||
|
Прочитать суперблок OSD с диска `<device>` и вывести его в формате JSON.
|
||||||
|
|
||||||
|
Опция `--force` позволяет читать суперблок, даже если он считается некорректным
|
||||||
|
из-за ошибок валидации.
|
||||||
|
|
||||||
|
## write-sb
|
||||||
|
|
||||||
|
`vitastor-disk write-sb <device>`
|
||||||
|
|
||||||
|
Прочитать JSON со стандартного ввода и записать его в суперблок OSD на диск `<device>`.
|
||||||
|
|
||||||
|
## udev
|
||||||
|
|
||||||
|
`vitastor-disk udev <device>`
|
||||||
|
|
||||||
|
Прочитать суперблок OSD с диска `<device>` и вывести переменные для udev.
|
||||||
|
|
||||||
|
## exec-osd
|
||||||
|
|
||||||
|
`vitastor-disk exec-osd <device>`
|
||||||
|
|
||||||
|
Прочитать суперблок OSD с диска `<device>` и запустить исполняемый файл OSD с параметрами оттуда.
|
||||||
|
|
||||||
|
Команда предназначена для использования из скриптов запуска (например, из сервисов systemd).
|
||||||
|
|
||||||
|
## pre-exec
|
||||||
|
|
||||||
|
`vitastor-disk pre-exec <device>`
|
||||||
|
|
||||||
|
Прочитать суперблок OSD с диска `<device>` и провести проверки OSD перед запуском.
|
||||||
|
|
||||||
|
На данный момент только отключает кэш диска или проверяет, что он отключён, если в параметрах
|
||||||
|
OSD отключены fsync-и.
|
||||||
|
|
||||||
|
Команда предназначена для использования из скриптов запуска (например, из сервисов systemd).
|
||||||
|
|
||||||
|
## dump-journal
|
||||||
|
|
||||||
|
`vitastor-disk dump-journal [OPTIONS] <journal_file> <journal_block_size> <offset> <size>`
|
||||||
|
|
||||||
|
Вывести журнал в человекочитаемом или в JSON (с опцией `--json`) виде.
|
||||||
|
|
||||||
|
Опции:
|
||||||
|
|
||||||
|
```
|
||||||
|
--all Просканировать всю область журнала и вывести даже старые записи
|
||||||
|
--json Вывести журнал в формате JSON
|
||||||
|
--format entries (По умолчанию) Вывести только актуальные записи журнала без данных
|
||||||
|
--format data Вывести только актуальные записи журнала с данными
|
||||||
|
--format blocks Вывести массив блоков журнала, а в каждом массив актуальных записей без данных
|
||||||
|
```
|
||||||
|
|
||||||
|
## write-journal
|
||||||
|
|
||||||
|
`vitastor-disk write-journal <journal_file> <journal_block_size> <bitmap_size> <offset> <size>`
|
||||||
|
|
||||||
|
Записать журнал из JSON со стандартного ввода в формате, аналогичном `dump-journal --json --format data`.
|
||||||
|
|
||||||
|
## dump-meta
|
||||||
|
|
||||||
|
`vitastor-disk dump-meta <meta_file> <meta_block_size> <offset> <size>`
|
||||||
|
|
||||||
|
Вывести метаданные в формате JSON.
|
||||||
|
|
||||||
|
## write-meta
|
||||||
|
|
||||||
|
`vitastor-disk write-meta <meta_file> <offset> <size>`
|
||||||
|
|
||||||
|
Записать метаданные из JSON со стандартного ввода в формате, аналогичном `dump-meta`.
|
||||||
|
|
||||||
|
## simple-offsets
|
||||||
|
|
||||||
|
`vitastor-disk simple-offsets <device>`
|
||||||
|
|
||||||
|
Рассчитать смещения для старого ("простого и тупого") создания OSD на диске (без суперблока).
|
||||||
|
|
||||||
|
Опции (см. также [Дисковые параметры уровня кластера](../config/layout-cluster.ru.md)):
|
||||||
|
|
||||||
|
```
|
||||||
|
--object_size 128k Размер блока хранилища
|
||||||
|
--bitmap_granularity 4k Гранулярность битовых карт
|
||||||
|
--journal_size 32M Размер журнала
|
||||||
|
--device_block_size 4k Размер блока устройства
|
||||||
|
--journal_offset 0 Смещение журнала
|
||||||
|
--device_size 0 Размер устройства
|
||||||
|
--format text Формат результата: json, options, env или text
|
||||||
|
```
|
@@ -46,3 +46,40 @@ qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:etcd_host=192.168.7
|
|||||||
|
|
||||||
You can also specify `:pool=<POOL>:inode=<INODE>:size=<SIZE>` instead of `:image=<IMAGE>`
|
You can also specify `:pool=<POOL>:inode=<INODE>:size=<SIZE>` instead of `:image=<IMAGE>`
|
||||||
if you don't want to use inode metadata.
|
if you don't want to use inode metadata.
|
||||||
|
|
||||||
|
### Exporting snapshots
|
||||||
|
|
||||||
|
Starting with 0.8.4, you can also export individual layers (snapshot diffs) using `qemu-img`.
|
||||||
|
|
||||||
|
Suppose you have an image `testimg` and a snapshot `testimg@0` created with `vitastor-cli snap-create testimg@0`.
|
||||||
|
|
||||||
|
Then you can export the `testimg@0` snapshot and the data written to `testimg` after creating
|
||||||
|
the snapshot separately using the following commands (key points are using `skip-parents=1` and
|
||||||
|
`-B backing_file` option):
|
||||||
|
|
||||||
|
```
|
||||||
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg@0' \
|
||||||
|
-O qcow2 testimg_0.qcow2
|
||||||
|
|
||||||
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg:skip-parents=1' \
|
||||||
|
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
||||||
|
```
|
||||||
|
|
||||||
|
In fact, with `cluster_size=4k` any QCOW2 file can be used instead `-B testimg_0.qcow2`, even an empty one.
|
||||||
|
|
||||||
|
QCOW2 `cluster_size=4k` option is required if you want `testimg.qcow2` to contain only the data
|
||||||
|
overwritten **exactly** in the child layer. With the default 64 KB QCOW2 cluster size you'll
|
||||||
|
get a bit of extra data from parent layers, i.e. a 4 KB overwrite will result in `testimg.qcow2`
|
||||||
|
containing 64 KB of data. And this extra data will be taken by `qemu-img` from the file passed
|
||||||
|
in `-B` option, so you really need 4 KB cluster if you use an empty image in `-B`.
|
||||||
|
|
||||||
|
After this procedure you'll get two chained QCOW2 images. To detach `testimg.qcow2` from
|
||||||
|
its parent, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
qemu-img rebase -u -b '' testimg.qcow2
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be used for backups. Just note that exporting an image that is currently being written to
|
||||||
|
is of course unsafe and doesn't produce a consistent result, so only export snapshots if you do this
|
||||||
|
on a live VM.
|
||||||
|
@@ -50,3 +50,40 @@ qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:etcd_host=10.115.0.
|
|||||||
|
|
||||||
Если вы не хотите обращаться к образу по имени, вместо `:image=<IMAGE>` можно указать номер пула, номер инода и размер:
|
Если вы не хотите обращаться к образу по имени, вместо `:image=<IMAGE>` можно указать номер пула, номер инода и размер:
|
||||||
`:pool=<POOL>:inode=<INODE>:size=<SIZE>`.
|
`:pool=<POOL>:inode=<INODE>:size=<SIZE>`.
|
||||||
|
|
||||||
|
### Экспорт снимков
|
||||||
|
|
||||||
|
Начиная с 0.8.4 вы можете экспортировать отдельные слои (изменения в снимках) с помощью `qemu-img`.
|
||||||
|
|
||||||
|
Допустим, что у вас есть образ `testimg` и его снимок `testimg@0`, созданный с помощью `vitastor-cli snap-create testimg@0`.
|
||||||
|
|
||||||
|
Тогда вы можете выгрузить снимок `testimg@0` и данные, изменённые в `testimg` после создания снимка, отдельно,
|
||||||
|
с помощью следующих команд (ключевые моменты - использование `skip-parents=1` и опции `-B backing_file.qcow2`):
|
||||||
|
|
||||||
|
```
|
||||||
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg@0' \
|
||||||
|
-O qcow2 testimg_0.qcow2
|
||||||
|
|
||||||
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg:skip-parents=1' \
|
||||||
|
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
||||||
|
```
|
||||||
|
|
||||||
|
На самом деле, с `cluster_size=4k` вместо `-B testimg_0.qcow2` можно использовать любой qcow2-файл,
|
||||||
|
даже пустой.
|
||||||
|
|
||||||
|
Опция QCOW2 `cluster_size=4k` нужна, если вы хотите, чтобы `testimg.qcow2` содержал **в точности**
|
||||||
|
данные, перезаписанные в дочернем слое. С размером кластера QCOW2 по умолчанию, составляющим 64 КБ,
|
||||||
|
вы получите немного "лишних" данных из родительских слоёв - перезапись 4 КБ будет приводить к тому,
|
||||||
|
что в `testimg.qcow2` будет появляться 64 КБ данных. Причём "лишние" данные qemu-img будет брать
|
||||||
|
как раз из файла, указанного в опции `-B`, так что если там указан пустой образ, кластер обязан быть 4 КБ.
|
||||||
|
|
||||||
|
После данной процедуры вы получите два QCOW2-образа, связанных в цепочку. Чтобы "отцепить" образ
|
||||||
|
`testimg.qcow2` от базового, выполните:
|
||||||
|
|
||||||
|
```
|
||||||
|
qemu-img rebase -u -b '' testimg.qcow2
|
||||||
|
```
|
||||||
|
|
||||||
|
Это можно использовать для резервного копирования. Только помните, что экспортировать образ, в который
|
||||||
|
в то же время идёт запись, небезопасно - результат чтения не будет целостным. Так что если вы работаете
|
||||||
|
с активными виртуальными машинами, экспортируйте только их снимки, но не сам образ.
|
||||||
|
2
json11
2
json11
Submodule json11 updated: 52a3af664f...fd37016cf8
7
mon/90-vitastor.rules
Normal file
7
mon/90-vitastor.rules
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
SUBSYSTEM=="block", ENV{ID_PART_ENTRY_TYPE}=="e7009fac-a5a1-4d72-af72-53de13059903", \
|
||||||
|
OWNER="vitastor", GROUP="vitastor", \
|
||||||
|
IMPORT{program}="/usr/bin/vitastor-disk udev $devnode", \
|
||||||
|
SYMLINK+="vitastor/$env{VITASTOR_ALIAS}"
|
||||||
|
|
||||||
|
ENV{VITASTOR_OSD_NUM}!="", ACTION=="add", RUN{program}+="/usr/bin/systemctl enable --now vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
||||||
|
ENV{VITASTOR_OSD_NUM}!="", ACTION=="remove", RUN{program}+="/usr/bin/systemctl disable --now vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
@@ -21,7 +21,7 @@ function add_pg_history(new_pg_history, new_pg, prev_pgs, prev_pg_history, old_p
|
|||||||
{
|
{
|
||||||
for (const pg of oh.osd_sets)
|
for (const pg of oh.osd_sets)
|
||||||
{
|
{
|
||||||
nh.osd_sets[pg.join(' ')] = pg;
|
nh.osd_sets[pg.join(' ')] = pg.map(osd_num => Number(osd_num));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oh && oh.all_peers && oh.all_peers.length)
|
if (oh && oh.all_peers && oh.all_peers.length)
|
||||||
|
@@ -550,8 +550,8 @@ function random_combinations(osd_tree, pg_size, count, ordered)
|
|||||||
seed ^= seed << 5;
|
seed ^= seed << 5;
|
||||||
return seed + 2147483648;
|
return seed + 2147483648;
|
||||||
};
|
};
|
||||||
const hosts = Object.keys(osd_tree).sort();
|
|
||||||
const osds = Object.keys(osd_tree).reduce((a, c) => { a[c] = Object.keys(osd_tree[c]).sort(); return a; }, {});
|
const osds = Object.keys(osd_tree).reduce((a, c) => { a[c] = Object.keys(osd_tree[c]).sort(); return a; }, {});
|
||||||
|
const hosts = Object.keys(osd_tree).sort().filter(h => osds[h].length > 0);
|
||||||
const r = {};
|
const r = {};
|
||||||
// Generate random combinations including each OSD at least once
|
// Generate random combinations including each OSD at least once
|
||||||
for (let h = 0; h < hosts.length; h++)
|
for (let h = 0; h < hosts.length; h++)
|
||||||
|
110
mon/make-etcd
Executable file
110
mon/make-etcd
Executable file
@@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/node
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
run().catch(e => { console.error(e); process.exit(1); });
|
||||||
|
|
||||||
|
async function run()
|
||||||
|
{
|
||||||
|
const config_path = process.argv[2] || '/etc/vitastor/vitastor.conf';
|
||||||
|
if (config_path == '-h' || config_path == '--help')
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (fs.existsSync("/etc/systemd/system/etcd.service"))
|
||||||
|
{
|
||||||
|
console.log("/etc/systemd/system/etcd.service already exists");
|
||||||
|
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 => (''+s).replace(/^https?:\/\/\[?|\]?(:\d+)?(\/.*)?$/g, '').toLowerCase());
|
||||||
|
const num = select_local_etcd(etcds);
|
||||||
|
if (num < 0)
|
||||||
|
{
|
||||||
|
console.log('No matching IPs in etcd_address from '+config_path);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
const etcd_cluster = etcds.map((e, i) => `etcd${i}=http://${e}:2380`).join(',');
|
||||||
|
await system(`mkdir -p /var/lib/etcd${num}.etcd`);
|
||||||
|
fs.writeFileSync(
|
||||||
|
"/etc/systemd/system/etcd.service",
|
||||||
|
`[Unit]
|
||||||
|
Description=etcd for vitastor
|
||||||
|
After=network-online.target local-fs.target time-sync.target
|
||||||
|
Wants=network-online.target local-fs.target time-sync.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
ExecStart=/usr/local/bin/etcd -name etcd${num} --data-dir /var/lib/etcd${num}.etcd \\
|
||||||
|
--advertise-client-urls http://${etcds[num]}:2379 --listen-client-urls http://${etcds[num]}:2379 \\
|
||||||
|
--initial-advertise-peer-urls http://${etcds[num]}:2380 --listen-peer-urls http://${etcds[num]}: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${num}.etcd
|
||||||
|
ExecStartPre=+chown -R etcd /var/lib/etcd${num}.etcd
|
||||||
|
User=etcd
|
||||||
|
PrivateTmp=false
|
||||||
|
TasksMax=infinity
|
||||||
|
Restart=always
|
||||||
|
StartLimitInterval=0
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
`);
|
||||||
|
await system(`useradd etcd`);
|
||||||
|
await system(`systemctl daemon-reload`);
|
||||||
|
await system(`systemctl enable etcd`);
|
||||||
|
await system(`systemctl start etcd`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function select_local_etcd(etcds)
|
||||||
|
{
|
||||||
|
const ifaces = os.networkInterfaces();
|
||||||
|
for (const ifname in ifaces)
|
||||||
|
for (const iface of ifaces[ifname])
|
||||||
|
for (let i = 0; i < etcds.length; i++)
|
||||||
|
if (etcds[i] == iface.address.toLowerCase())
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function system(cmd)
|
||||||
|
{
|
||||||
|
const cp = child_process.spawn(cmd, { shell: true, stdio: [ 0, 1, 2 ] });
|
||||||
|
let finish_cb;
|
||||||
|
cp.on('exit', () => finish_cb && finish_cb());
|
||||||
|
if (cp.exitCode == null)
|
||||||
|
await new Promise(ok => finish_cb = ok);
|
||||||
|
return cp.exitCode;
|
||||||
|
}
|
@@ -1,414 +0,0 @@
|
|||||||
#!/usr/bin/nodejs
|
|
||||||
// systemd unit generator for hybrid (HDD+SSD) vitastor OSDs
|
|
||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
// License: VNPL-1.1
|
|
||||||
|
|
||||||
// USAGE: nodejs make-osd-hybrid.js [--disable_ssd_cache 0] [--disable_hdd_cache 0] /dev/sda /dev/sdb /dev/sdc /dev/sdd ...
|
|
||||||
// I.e. - just pass all HDDs and SSDs mixed, the script will decide where
|
|
||||||
// to put journals on its own
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const fsp = fs.promises;
|
|
||||||
const child_process = require('child_process');
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
debug: 1,
|
|
||||||
journal_size: 1024*1024*1024,
|
|
||||||
min_meta_size: 1024*1024*1024,
|
|
||||||
object_size: 1024*1024,
|
|
||||||
bitmap_granularity: 4096,
|
|
||||||
device_block_size: 4096,
|
|
||||||
disable_ssd_cache: 1,
|
|
||||||
disable_hdd_cache: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
run().catch(console.fatal);
|
|
||||||
|
|
||||||
async function run()
|
|
||||||
{
|
|
||||||
const device_list = parse_options();
|
|
||||||
await system_or_die("mkdir -p /var/log/vitastor; chown vitastor /var/log/vitastor");
|
|
||||||
// Collect devices
|
|
||||||
const all_devices = await collect_devices(device_list);
|
|
||||||
const ssds = all_devices.filter(d => d.ssd);
|
|
||||||
const hdds = all_devices.filter(d => !d.ssd);
|
|
||||||
// Collect existing OSD units
|
|
||||||
const osd_units = await collect_osd_units();
|
|
||||||
// Count assigned HDD journals and unallocated space for each SSD
|
|
||||||
await check_journal_count(ssds, osd_units);
|
|
||||||
// Create new OSDs
|
|
||||||
await create_new_hybrid_osds(hdds, ssds, osd_units);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse_options()
|
|
||||||
{
|
|
||||||
const devices = [];
|
|
||||||
const opt = {};
|
|
||||||
for (let i = 2; i < process.argv.length; i++)
|
|
||||||
{
|
|
||||||
const arg = process.argv[i];
|
|
||||||
if (arg == '--help' || arg == '-h')
|
|
||||||
{
|
|
||||||
opt.help = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (arg.substr(0, 2) == '--')
|
|
||||||
opt[arg.substr(2)] = process.argv[++i];
|
|
||||||
else
|
|
||||||
devices.push(arg);
|
|
||||||
}
|
|
||||||
if (opt.help || !devices.length)
|
|
||||||
{
|
|
||||||
console.log(
|
|
||||||
'Prepare hybrid (HDD+SSD) Vitastor OSDs\n'+
|
|
||||||
'(c) Vitaliy Filippov, 2019+, license: VNPL-1.1\n\n'+
|
|
||||||
'USAGE: nodejs make-osd-hybrid.js [OPTIONS] /dev/sda /dev/sdb /dev/sdc ...\n'+
|
|
||||||
'Just pass all your SSDs and HDDs in any order, the script will distribute OSDs for you.\n\n'+
|
|
||||||
'OPTIONS (with defaults):\n'+
|
|
||||||
Object.keys(options).map(k => ` --${k} ${options[k]}`).join('\n')
|
|
||||||
);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
for (const k in opt)
|
|
||||||
options[k] = opt[k];
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect devices
|
|
||||||
async function collect_devices(devices_to_check)
|
|
||||||
{
|
|
||||||
const devices = [];
|
|
||||||
for (const dev of devices_to_check)
|
|
||||||
{
|
|
||||||
if (dev.substr(0, 5) != '/dev/')
|
|
||||||
{
|
|
||||||
console.log(`${dev} does not start with /dev/, skipping`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!await file_exists('/sys/block/'+dev.substr(5)))
|
|
||||||
{
|
|
||||||
console.log(`${dev} is a partition, skipping`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Check if the device is an SSD
|
|
||||||
const rot = '/sys/block/'+dev.substr(5)+'/queue/rotational';
|
|
||||||
if (!await file_exists(rot))
|
|
||||||
{
|
|
||||||
console.log(`${dev} does not have ${rot} to check whether it's an SSD, skipping`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const ssd = !parseInt(await fsp.readFile(rot, { encoding: 'utf-8' }));
|
|
||||||
// Check if the device has partition table
|
|
||||||
let [ has_partition_table, parts ] = await system(`sfdisk --dump ${dev} --json`);
|
|
||||||
if (has_partition_table != 0)
|
|
||||||
{
|
|
||||||
// Check if the device has any data
|
|
||||||
const [ has_data, out ] = await system(`blkid ${dev}`);
|
|
||||||
if (has_data == 0)
|
|
||||||
{
|
|
||||||
console.log(`${dev} contains data, skipping:\n ${out.trim().replace(/\n/g, '\n ')}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parts = parts ? JSON.parse(parts).partitiontable : null;
|
|
||||||
if (parts && parts.label != 'gpt')
|
|
||||||
{
|
|
||||||
console.log(`${dev} contains "${parts.label}" partition table, only GPT is supported, skipping`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
devices.push({
|
|
||||||
path: dev,
|
|
||||||
ssd,
|
|
||||||
parts,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect existing OSD units
|
|
||||||
async function collect_osd_units()
|
|
||||||
{
|
|
||||||
const units = [];
|
|
||||||
for (const unit of (await system("ls /etc/systemd/system/vitastor-osd*.service"))[1].trim().split('\n'))
|
|
||||||
{
|
|
||||||
if (!unit)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let cmd = /^ExecStart\s*=\s*(([^\n]*\\\n)*[^\n]*)/.exec(await fsp.readFile(unit, { encoding: 'utf-8' }));
|
|
||||||
if (!cmd)
|
|
||||||
{
|
|
||||||
console.log('ExecStart= not found in '+unit+', skipping')
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let kv = {}, key;
|
|
||||||
cmd = cmd[1].replace(/^bash\s+-c\s+'/, '')
|
|
||||||
.replace(/>>\s*\S+2>\s*&1\s*'$/, '')
|
|
||||||
.replace(/\s*\\\n\s*/g, ' ')
|
|
||||||
.replace(/([^\s']+)|'([^']+)'/g, (m, m1, m2) =>
|
|
||||||
{
|
|
||||||
m1 = m1||m2;
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
if (m1.substr(0, 2) != '--')
|
|
||||||
{
|
|
||||||
console.log('Strange command line in '+unit+', stopping');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
key = m1.substr(2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
kv[key] = m1;
|
|
||||||
key = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
units.push(kv);
|
|
||||||
}
|
|
||||||
return units;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count assigned HDD journals and unallocated space for each SSD
|
|
||||||
async function check_journal_count(ssds, osd_units)
|
|
||||||
{
|
|
||||||
const units_by_journal = osd_units.reduce((a, c) =>
|
|
||||||
{
|
|
||||||
if (c.journal_device)
|
|
||||||
a[c.journal_device] = c;
|
|
||||||
return a;
|
|
||||||
}, {});
|
|
||||||
for (const dev of ssds)
|
|
||||||
{
|
|
||||||
dev.journals = 0;
|
|
||||||
if (dev.parts)
|
|
||||||
{
|
|
||||||
for (const part of dev.parts.partitions)
|
|
||||||
{
|
|
||||||
if (part.uuid && units_by_journal['/dev/disk/by-partuuid/'+part.uuid.toLowerCase()])
|
|
||||||
{
|
|
||||||
dev.journals++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dev.free = free_from_parttable(dev.parts);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dev.free = parseInt(await system_or_die("blockdev --getsize64 "+dev.path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function create_new_hybrid_osds(hdds, ssds, osd_units)
|
|
||||||
{
|
|
||||||
const units_by_disk = osd_units.reduce((a, c) => { a[c.data_device] = c; return a; }, {});
|
|
||||||
for (const dev of hdds)
|
|
||||||
{
|
|
||||||
if (!dev.parts)
|
|
||||||
{
|
|
||||||
// HDD is not partitioned yet, create a single partition
|
|
||||||
// + is the "default value" for sfdisk
|
|
||||||
await system_or_die('sfdisk '+dev.path, 'label: gpt\n\n+ +\n');
|
|
||||||
dev.parts = JSON.parse(await system_or_die('sfdisk --dump '+dev.path+' --json')).partitiontable;
|
|
||||||
}
|
|
||||||
if (dev.parts.partitions.length != 1)
|
|
||||||
{
|
|
||||||
console.log(dev.path+' has more than 1 partition, skipping');
|
|
||||||
}
|
|
||||||
else if ((dev.parts.partitions[0].start + dev.parts.partitions[0].size) != (1 + dev.parts.lastlba))
|
|
||||||
{
|
|
||||||
console.log(dev.path+'1 is not a whole-disk partition, skipping');
|
|
||||||
}
|
|
||||||
else if (!dev.parts.partitions[0].uuid)
|
|
||||||
{
|
|
||||||
console.log(dev.parts.partitions[0].node+' does not have UUID. Please repartition '+dev.path+' with GPT');
|
|
||||||
}
|
|
||||||
else if (!units_by_disk['/dev/disk/by-partuuid/'+dev.parts.partitions[0].uuid.toLowerCase()])
|
|
||||||
{
|
|
||||||
await create_hybrid_osd(dev, ssds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function create_hybrid_osd(dev, ssds)
|
|
||||||
{
|
|
||||||
// Create a new OSD
|
|
||||||
// Calculate metadata size
|
|
||||||
const data_device = '/dev/disk/by-partuuid/'+dev.parts.partitions[0].uuid.toLowerCase();
|
|
||||||
const data_size = dev.parts.partitions[0].size * dev.parts.sectorsize;
|
|
||||||
const meta_entry_size = 24 + 2*options.object_size/options.bitmap_granularity/8;
|
|
||||||
const entries_per_block = Math.floor(options.device_block_size / meta_entry_size);
|
|
||||||
const object_count = Math.floor(data_size / options.object_size);
|
|
||||||
let meta_size = Math.ceil(1 + object_count / entries_per_block) * options.device_block_size;
|
|
||||||
// Leave some extra space for future metadata formats and round metadata area size to multiples of 1 MB
|
|
||||||
meta_size = 2*meta_size;
|
|
||||||
meta_size = Math.ceil(meta_size/1024/1024) * 1024*1024;
|
|
||||||
if (meta_size < options.min_meta_size)
|
|
||||||
meta_size = options.min_meta_size;
|
|
||||||
let journal_size = Math.ceil(options.journal_size/1024/1024) * 1024*1024;
|
|
||||||
// Pick an SSD for journal, balancing the number of journals across SSDs
|
|
||||||
let selected_ssd;
|
|
||||||
for (const ssd of ssds)
|
|
||||||
if (ssd.free >= (meta_size+journal_size) && (!selected_ssd || selected_ssd.journals > ssd.journals))
|
|
||||||
selected_ssd = ssd;
|
|
||||||
if (!selected_ssd)
|
|
||||||
{
|
|
||||||
console.error('Could not find free space for SSD journal and metadata for '+dev.path);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
// Allocate an OSD number
|
|
||||||
const osd_num = (await system_or_die("vitastor-cli alloc-osd")).trim();
|
|
||||||
if (!osd_num)
|
|
||||||
{
|
|
||||||
console.error('Failed to run vitastor-cli alloc-osd');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
console.log('Creating OSD '+osd_num+' on '+dev.path+' (HDD) with journal and metadata on '+selected_ssd.path+' (SSD)');
|
|
||||||
// Add two partitions: journal and metadata
|
|
||||||
const new_parts = await add_partitions(selected_ssd, [ journal_size, meta_size ]);
|
|
||||||
selected_ssd.journals++;
|
|
||||||
const journal_device = '/dev/disk/by-partuuid/'+new_parts[0].uuid.toLowerCase();
|
|
||||||
const meta_device = '/dev/disk/by-partuuid/'+new_parts[1].uuid.toLowerCase();
|
|
||||||
// Wait until the device symlinks appear
|
|
||||||
while (!await file_exists(journal_device))
|
|
||||||
{
|
|
||||||
await new Promise(ok => setTimeout(ok, 100));
|
|
||||||
}
|
|
||||||
while (!await file_exists(meta_device))
|
|
||||||
{
|
|
||||||
await new Promise(ok => setTimeout(ok, 100));
|
|
||||||
}
|
|
||||||
// Zero out metadata and journal
|
|
||||||
await system_or_die("dd if=/dev/zero of="+journal_device+" bs=1M count="+(journal_size/1024/1024)+" oflag=direct");
|
|
||||||
await system_or_die("dd if=/dev/zero of="+meta_device+" bs=1M count="+(meta_size/1024/1024)+" oflag=direct");
|
|
||||||
// Create unit file for the OSD
|
|
||||||
const has_scsi_cache_type = options.disable_ssd_cache &&
|
|
||||||
(await system("ls /sys/block/"+selected_ssd.path.substr(5)+"/device/scsi_disk/*/cache_type"))[0] == 0;
|
|
||||||
const write_through = options.disable_ssd_cache && (
|
|
||||||
has_scsi_cache_type || selected_ssd.path.substr(5, 4) == 'nvme'
|
|
||||||
&& (await system_or_die("/sys/block/"+selected_ssd.path.substr(5)+"/queue/write_cache")).trim() == "write through");
|
|
||||||
await fsp.writeFile('/etc/systemd/system/vitastor-osd'+osd_num+'.service',
|
|
||||||
`[Unit]
|
|
||||||
Description=Vitastor object storage daemon osd.${osd_num}
|
|
||||||
After=network-online.target local-fs.target time-sync.target
|
|
||||||
Wants=network-online.target local-fs.target time-sync.target
|
|
||||||
PartOf=vitastor.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
LimitNOFILE=1048576
|
|
||||||
LimitNPROC=1048576
|
|
||||||
LimitMEMLOCK=infinity
|
|
||||||
ExecStart=bash -c '/usr/bin/vitastor-osd \\
|
|
||||||
--osd_num ${osd_num} ${write_through
|
|
||||||
? "--disable_meta_fsync 1 --disable_journal_fsync 1 --immediate_commit "+(options.disable_hdd_cache ? "all" : "small")
|
|
||||||
: ""} \\
|
|
||||||
--throttle_small_writes 1 \\
|
|
||||||
--disk_alignment ${options.device_block_size} \\
|
|
||||||
--journal_block_size ${options.device_block_size} \\
|
|
||||||
--meta_block_size ${options.device_block_size} \\
|
|
||||||
--journal_no_same_sector_overwrites true \\
|
|
||||||
--journal_sector_buffer_count 1024 \\
|
|
||||||
--block_size ${options.object_size} \\
|
|
||||||
--data_device ${data_device} \\
|
|
||||||
--journal_device ${journal_device} \\
|
|
||||||
--meta_device ${meta_device} >>/var/log/vitastor/osd${osd_num}.log 2>&1'
|
|
||||||
WorkingDirectory=/
|
|
||||||
ExecStartPre=+chown vitastor:vitastor ${data_device}
|
|
||||||
ExecStartPre=+chown vitastor:vitastor ${journal_device}
|
|
||||||
ExecStartPre=+chown vitastor:vitastor ${meta_device}${
|
|
||||||
has_scsi_cache_type
|
|
||||||
? "\nExecStartPre=+bash -c 'D=$$$(readlink "+journal_device+"); echo write through > $$$(dirname /sys/block/*/$$\${D##*/})/device/scsi_disk/*/cache_type'"
|
|
||||||
: ""}${
|
|
||||||
options.disable_hdd_cache
|
|
||||||
? "\nExecStartPre=+bash -c 'D=$$$(readlink "+data_device+"); echo write through > $$$(dirname /sys/block/*/$$\${D##*/})/device/scsi_disk/*/cache_type'"
|
|
||||||
: ""}
|
|
||||||
User=vitastor
|
|
||||||
PrivateTmp=false
|
|
||||||
TasksMax=infinity
|
|
||||||
Restart=always
|
|
||||||
StartLimitInterval=0
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=vitastor.target
|
|
||||||
`);
|
|
||||||
await system_or_die("systemctl enable vitastor-osd"+osd_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function add_partitions(dev, sizes)
|
|
||||||
{
|
|
||||||
let script = 'label: gpt\n\n';
|
|
||||||
if (dev.parts)
|
|
||||||
{
|
|
||||||
// Old partitions
|
|
||||||
for (const part of dev.parts.partitions)
|
|
||||||
{
|
|
||||||
script += part.node+': '+Object.keys(part).map(k => k == 'node' ? '' : k+'='+part[k]).filter(k => k).join(', ')+'\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// New partitions
|
|
||||||
for (const size of sizes)
|
|
||||||
{
|
|
||||||
script += '+ '+Math.ceil(size/1024)+'KiB\n';
|
|
||||||
}
|
|
||||||
await system_or_die('sfdisk '+dev.path, script);
|
|
||||||
// Get new partition table and find the new partition
|
|
||||||
const newpt = JSON.parse(await system_or_die('sfdisk --dump '+dev.path+' --json')).partitiontable;
|
|
||||||
const old_nodes = dev.parts ? dev.parts.partitions.reduce((a, c) => { a[c.uuid] = true; return a; }, {}) : {};
|
|
||||||
const new_nodes = newpt.partitions.filter(part => !old_nodes[part.uuid]);
|
|
||||||
if (new_nodes.length != sizes.length)
|
|
||||||
{
|
|
||||||
console.error('Failed to partition '+dev.path+': new partitions not found in table');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
dev.parts = newpt;
|
|
||||||
dev.free = free_from_parttable(newpt);
|
|
||||||
return new_nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function free_from_parttable(pt)
|
|
||||||
{
|
|
||||||
let free = pt.lastlba + 1 - pt.firstlba;
|
|
||||||
for (const part of pt.partitions)
|
|
||||||
{
|
|
||||||
free -= part.size;
|
|
||||||
}
|
|
||||||
free *= pt.sectorsize;
|
|
||||||
return free;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function system_or_die(cmd, input = '')
|
|
||||||
{
|
|
||||||
let [ exitcode, stdout, stderr ] = await system(cmd, input);
|
|
||||||
if (exitcode != 0)
|
|
||||||
{
|
|
||||||
console.error(cmd+' failed: '+stderr);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
return stdout;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function system(cmd, input = '')
|
|
||||||
{
|
|
||||||
if (options.debug)
|
|
||||||
{
|
|
||||||
process.stderr.write('+ '+cmd+(input ? " <<EOF\n"+input.replace(/\s*$/, '\n')+"EOF" : '')+'\n');
|
|
||||||
}
|
|
||||||
const cp = child_process.spawn(cmd, { shell: true });
|
|
||||||
let stdout = '', stderr = '', finish_cb;
|
|
||||||
cp.stdout.on('data', buf => stdout += buf.toString());
|
|
||||||
cp.stderr.on('data', buf => stderr += buf.toString());
|
|
||||||
cp.on('exit', () => finish_cb && finish_cb());
|
|
||||||
cp.stdin.write(input);
|
|
||||||
cp.stdin.end();
|
|
||||||
if (cp.exitCode == null)
|
|
||||||
{
|
|
||||||
await new Promise(ok => finish_cb = ok);
|
|
||||||
}
|
|
||||||
return [ cp.exitCode, stdout, stderr ];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function file_exists(filename)
|
|
||||||
{
|
|
||||||
return new Promise((ok, no) => fs.access(filename, fs.constants.R_OK, err => ok(!err)));
|
|
||||||
}
|
|
@@ -1,66 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Very simple systemd unit generator for vitastor-osd services
|
|
||||||
# Not the final solution yet, mostly for tests
|
|
||||||
# Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
# License: MIT
|
|
||||||
|
|
||||||
# USAGE:
|
|
||||||
# 1) Put etcd_address and osd_network into /etc/vitastor/vitastor.conf. Example:
|
|
||||||
# {
|
|
||||||
# "etcd_address":["http://10.200.1.10:2379/v3","http://10.200.1.11:2379/v3","http://10.200.1.12:2379/v3"],
|
|
||||||
# "osd_network":"10.200.1.0/24"
|
|
||||||
# }
|
|
||||||
# 2) Run ./make-osd.sh /dev/disk/by-partuuid/xxx [ /dev/disk/by-partuuid/yyy]...
|
|
||||||
|
|
||||||
set -e -x
|
|
||||||
|
|
||||||
# Create OSDs on all passed devices
|
|
||||||
for DEV in $*; do
|
|
||||||
|
|
||||||
OSD_NUM=$(vitastor-cli alloc-osd)
|
|
||||||
|
|
||||||
echo Creating OSD $OSD_NUM on $DEV
|
|
||||||
|
|
||||||
OPT=$(vitastor-cli simple-offsets --format options $DEV | tr '\n' ' ')
|
|
||||||
META=$(vitastor-cli simple-offsets --format json $DEV | jq .data_offset)
|
|
||||||
dd if=/dev/zero of=$DEV bs=1048576 count=$(((META+1048575)/1048576)) oflag=direct
|
|
||||||
|
|
||||||
mkdir -p /var/log/vitastor
|
|
||||||
id vitastor &>/dev/null || useradd vitastor
|
|
||||||
chown vitastor /var/log/vitastor
|
|
||||||
|
|
||||||
cat >/etc/systemd/system/vitastor-osd$OSD_NUM.service <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=Vitastor object storage daemon osd.$OSD_NUM
|
|
||||||
After=network-online.target local-fs.target time-sync.target
|
|
||||||
Wants=network-online.target local-fs.target time-sync.target
|
|
||||||
PartOf=vitastor.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
LimitNOFILE=1048576
|
|
||||||
LimitNPROC=1048576
|
|
||||||
LimitMEMLOCK=infinity
|
|
||||||
ExecStart=bash -c '/usr/bin/vitastor-osd \\
|
|
||||||
--osd_num $OSD_NUM \\
|
|
||||||
--disable_data_fsync 1 \\
|
|
||||||
--immediate_commit all \\
|
|
||||||
--disk_alignment 4096 --journal_block_size 4096 --meta_block_size 4096 \\
|
|
||||||
--journal_no_same_sector_overwrites true \\
|
|
||||||
--journal_sector_buffer_count 1024 \\
|
|
||||||
$OPT >>/var/log/vitastor/osd$OSD_NUM.log 2>&1'
|
|
||||||
WorkingDirectory=/
|
|
||||||
ExecStartPre=+chown vitastor:vitastor $DEV
|
|
||||||
User=vitastor
|
|
||||||
PrivateTmp=false
|
|
||||||
TasksMax=infinity
|
|
||||||
Restart=always
|
|
||||||
StartLimitInterval=0
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=vitastor.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
systemctl enable vitastor-osd$OSD_NUM
|
|
||||||
|
|
||||||
done
|
|
@@ -1,86 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Very simple systemd unit generator for etcd & vitastor-mon services
|
|
||||||
# Not the final solution yet, mostly for tests
|
|
||||||
# Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
# License: MIT
|
|
||||||
|
|
||||||
# USAGE: ./make-units.sh
|
|
||||||
|
|
||||||
IP_SUBSTR="10.200.1."
|
|
||||||
ETCD_HOSTS="etcd0=http://10.200.1.10:2380,etcd1=http://10.200.1.11:2380,etcd2=http://10.200.1.12:2380"
|
|
||||||
|
|
||||||
# determine IP
|
|
||||||
IP=`ip -json a s | jq -r '.[].addr_info[] | select(.local | startswith("'$IP_SUBSTR'")) | .local'`
|
|
||||||
[ "$IP" != "" ] || exit 1
|
|
||||||
ETCD_NUM=${ETCD_HOSTS/$IP*/}
|
|
||||||
[ "$ETCD_NUM" != "$ETCD_HOSTS" ] || exit 1
|
|
||||||
ETCD_NUM=$(echo $ETCD_NUM | tr -d -c , | wc -c)
|
|
||||||
|
|
||||||
# etcd
|
|
||||||
useradd etcd
|
|
||||||
|
|
||||||
mkdir -p /var/lib/etcd$ETCD_NUM.etcd
|
|
||||||
cat >/etc/systemd/system/etcd.service <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=etcd for vitastor
|
|
||||||
After=network-online.target local-fs.target time-sync.target
|
|
||||||
Wants=network-online.target local-fs.target time-sync.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/local/bin/etcd -name etcd$ETCD_NUM --data-dir /var/lib/etcd$ETCD_NUM.etcd \\
|
|
||||||
--advertise-client-urls http://$IP:2379 --listen-client-urls http://$IP:2379 \\
|
|
||||||
--initial-advertise-peer-urls http://$IP:2380 --listen-peer-urls http://$IP:2380 \\
|
|
||||||
--initial-cluster-token vitastor-etcd-1 --initial-cluster $ETCD_HOSTS \\
|
|
||||||
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \\
|
|
||||||
--auto-compaction-retention=10 --auto-compaction-mode=revision
|
|
||||||
WorkingDirectory=/var/lib/etcd$ETCD_NUM.etcd
|
|
||||||
ExecStartPre=+chown -R etcd /var/lib/etcd$ETCD_NUM.etcd
|
|
||||||
User=etcd
|
|
||||||
PrivateTmp=false
|
|
||||||
TasksMax=infinity
|
|
||||||
Restart=always
|
|
||||||
StartLimitInterval=0
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=local.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable etcd
|
|
||||||
systemctl start etcd
|
|
||||||
|
|
||||||
useradd vitastor
|
|
||||||
chmod 755 /root
|
|
||||||
|
|
||||||
# Vitastor target
|
|
||||||
cat >/etc/systemd/system/vitastor.target <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=vitastor target
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Monitor unit
|
|
||||||
ETCD_MON=$(echo $ETCD_HOSTS | perl -pe 's/:2380/:2379/g; s/etcd\d*=//g;')
|
|
||||||
cat >/etc/systemd/system/vitastor-mon.service <<EOF
|
|
||||||
[Unit]
|
|
||||||
Description=Vitastor monitor
|
|
||||||
After=network-online.target local-fs.target time-sync.target
|
|
||||||
Wants=network-online.target local-fs.target time-sync.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
ExecStart=node /usr/lib/vitastor/mon/mon-main.js --etcd_url '$ETCD_MON' --etcd_prefix '/vitastor' --etcd_start_timeout 5
|
|
||||||
WorkingDirectory=/
|
|
||||||
User=vitastor
|
|
||||||
PrivateTmp=false
|
|
||||||
TasksMax=infinity
|
|
||||||
Restart=always
|
|
||||||
StartLimitInterval=0
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=vitastor.target
|
|
||||||
EOF
|
|
@@ -12,7 +12,7 @@ for (let i = 2; i < process.argv.length; i++)
|
|||||||
if (process.argv[i] === '-h' || process.argv[i] === '--help')
|
if (process.argv[i] === '-h' || process.argv[i] === '--help')
|
||||||
{
|
{
|
||||||
console.error('USAGE: '+process.argv[0]+' '+process.argv[1]+' [--verbose 1]'+
|
console.error('USAGE: '+process.argv[0]+' '+process.argv[1]+' [--verbose 1]'+
|
||||||
' [--etcd_address "http://127.0.0.1:2379,..."] [--config_file /etc/vitastor/vitastor.conf]'+
|
' [--etcd_address "http://127.0.0.1:2379,..."] [--config_path /etc/vitastor/vitastor.conf]'+
|
||||||
' [--etcd_prefix "/vitastor"] [--etcd_start_timeout 5]');
|
' [--etcd_prefix "/vitastor"] [--etcd_start_timeout 5]');
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
110
mon/mon.js
110
mon/mon.js
@@ -70,9 +70,9 @@ const etcd_tree = {
|
|||||||
rdma_gid_index: 0,
|
rdma_gid_index: 0,
|
||||||
rdma_mtu: 4096,
|
rdma_mtu: 4096,
|
||||||
rdma_max_sge: 128,
|
rdma_max_sge: 128,
|
||||||
rdma_max_send: 32,
|
rdma_max_send: 64,
|
||||||
rdma_max_recv: 8,
|
rdma_max_recv: 128,
|
||||||
rdma_max_msg: 1048576,
|
rdma_max_msg: 132096,
|
||||||
log_level: 0,
|
log_level: 0,
|
||||||
block_size: 131072,
|
block_size: 131072,
|
||||||
disk_alignment: 4096,
|
disk_alignment: 4096,
|
||||||
@@ -105,7 +105,12 @@ const etcd_tree = {
|
|||||||
no_rebalance: false,
|
no_rebalance: false,
|
||||||
print_stats_interval: 3,
|
print_stats_interval: 3,
|
||||||
slow_log_interval: 10,
|
slow_log_interval: 10,
|
||||||
|
inode_vanish_time: 60,
|
||||||
osd_memlock: false,
|
osd_memlock: false,
|
||||||
|
scrub_interval: '30d', // 1s/1m/1h/1d
|
||||||
|
scrub_queue_depth: 1,
|
||||||
|
scrub_sleep: 0, // milliseconds
|
||||||
|
scrub_list_limit: 1000, // objects to list on one scrub iteration
|
||||||
// blockstore - fixed in superblock
|
// blockstore - fixed in superblock
|
||||||
block_size,
|
block_size,
|
||||||
disk_alignment,
|
disk_alignment,
|
||||||
@@ -147,21 +152,28 @@ const etcd_tree = {
|
|||||||
/* pools: {
|
/* pools: {
|
||||||
<id>: {
|
<id>: {
|
||||||
name: 'testpool',
|
name: 'testpool',
|
||||||
// jerasure uses Reed-Solomon-Vandermonde codes
|
// 'ec' uses Reed-Solomon-Vandermonde codes, 'jerasure' is an alias for 'ec'
|
||||||
scheme: 'replicated' | 'xor' | 'jerasure',
|
scheme: 'replicated' | 'xor' | 'ec' | 'jerasure',
|
||||||
pg_size: 3,
|
pg_size: 3,
|
||||||
pg_minsize: 2,
|
pg_minsize: 2,
|
||||||
// number of parity chunks, required for jerasure
|
// number of parity chunks, required for EC
|
||||||
parity_chunks?: 1,
|
parity_chunks?: 1,
|
||||||
pg_count: 100,
|
pg_count: 100,
|
||||||
failure_domain: 'host',
|
failure_domain: 'host',
|
||||||
max_osd_combinations: 10000,
|
max_osd_combinations: 10000,
|
||||||
pg_stripe_size: 4194304,
|
// block_size, bitmap_granularity, immediate_commit must match all OSDs used in that pool
|
||||||
|
block_size: 131072,
|
||||||
|
bitmap_granularity: 4096,
|
||||||
|
// 'all'/'small'/'none', same as in OSD options
|
||||||
|
immediate_commit: 'none',
|
||||||
|
pg_stripe_size: 0,
|
||||||
root_node?: 'rack1',
|
root_node?: 'rack1',
|
||||||
// restrict pool to OSDs having all of these tags
|
// restrict pool to OSDs having all of these tags
|
||||||
osd_tags?: 'nvme' | [ 'nvme', ... ],
|
osd_tags?: 'nvme' | [ 'nvme', ... ],
|
||||||
// prefer to put primary on OSD with these tags
|
// prefer to put primary on OSD with these tags
|
||||||
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
|
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
|
||||||
|
// scrub interval
|
||||||
|
scrub_interval?: '30d',
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}, */
|
}, */
|
||||||
@@ -255,9 +267,9 @@ const etcd_tree = {
|
|||||||
/* <pool_id>: {
|
/* <pool_id>: {
|
||||||
<pg_id>: {
|
<pg_id>: {
|
||||||
primary: osd_num_t,
|
primary: osd_num_t,
|
||||||
state: ("starting"|"peering"|"peered"|"incomplete"|"active"|"repeering"|"stopping"|"offline"|
|
state: ("starting"|"peering"|"incomplete"|"active"|"repeering"|"stopping"|"offline"|
|
||||||
"degraded"|"has_incomplete"|"has_degraded"|"has_misplaced"|"has_unclean"|
|
"degraded"|"has_corrupted"|"has_incomplete"|"has_degraded"|"has_misplaced"|"has_unclean"|
|
||||||
"has_invalid"|"left_on_dead")[],
|
"has_invalid"|"left_on_dead"|"scrubbing")[],
|
||||||
}
|
}
|
||||||
}, */
|
}, */
|
||||||
},
|
},
|
||||||
@@ -279,6 +291,7 @@ const etcd_tree = {
|
|||||||
osd_sets: osd_num_t[][],
|
osd_sets: osd_num_t[][],
|
||||||
all_peers: osd_num_t[],
|
all_peers: osd_num_t[],
|
||||||
epoch: uint64_t,
|
epoch: uint64_t,
|
||||||
|
scrub_ts: uint64_t,
|
||||||
},
|
},
|
||||||
}, */
|
}, */
|
||||||
},
|
},
|
||||||
@@ -322,6 +335,13 @@ const etcd_tree = {
|
|||||||
misplaced: uint64_t,
|
misplaced: uint64_t,
|
||||||
degraded: uint64_t,
|
degraded: uint64_t,
|
||||||
incomplete: uint64_t,
|
incomplete: uint64_t,
|
||||||
|
},
|
||||||
|
object_bytes: {
|
||||||
|
total: uint64_t,
|
||||||
|
clean: uint64_t,
|
||||||
|
misplaced: uint64_t,
|
||||||
|
degraded: uint64_t,
|
||||||
|
incomplete: uint64_t,
|
||||||
}, */
|
}, */
|
||||||
},
|
},
|
||||||
history: {
|
history: {
|
||||||
@@ -650,12 +670,15 @@ class Mon
|
|||||||
async save_last_clean()
|
async save_last_clean()
|
||||||
{
|
{
|
||||||
// last_clean_pgs is used to avoid extra data move when observing a series of changes in the cluster
|
// last_clean_pgs is used to avoid extra data move when observing a series of changes in the cluster
|
||||||
|
const new_clean_pgs = { items: {} };
|
||||||
|
next_pool:
|
||||||
for (const pool_id in this.state.config.pools)
|
for (const pool_id in this.state.config.pools)
|
||||||
{
|
{
|
||||||
|
new_clean_pgs.items[pool_id] = (this.state.history.last_clean_pgs.items||{})[pool_id];
|
||||||
const pool_cfg = this.state.config.pools[pool_id];
|
const pool_cfg = this.state.config.pools[pool_id];
|
||||||
if (!this.validate_pool_cfg(pool_id, pool_cfg, false))
|
if (!this.validate_pool_cfg(pool_id, pool_cfg, false))
|
||||||
{
|
{
|
||||||
continue;
|
continue next_pool;
|
||||||
}
|
}
|
||||||
for (let pg_num = 1; pg_num <= pool_cfg.pg_count; pg_num++)
|
for (let pg_num = 1; pg_num <= pool_cfg.pg_count; pg_num++)
|
||||||
{
|
{
|
||||||
@@ -664,17 +687,18 @@ class Mon
|
|||||||
!(this.state.pg.state[pool_id][pg_num].state instanceof Array))
|
!(this.state.pg.state[pool_id][pg_num].state instanceof Array))
|
||||||
{
|
{
|
||||||
// Unclean
|
// Unclean
|
||||||
return;
|
continue next_pool;
|
||||||
}
|
}
|
||||||
let st = this.state.pg.state[pool_id][pg_num].state.join(',');
|
let st = this.state.pg.state[pool_id][pg_num].state.join(',');
|
||||||
if (st != 'active' && st != 'active,left_on_dead' && st != 'left_on_dead,active')
|
if (st != 'active' && st != 'active,left_on_dead' && st != 'left_on_dead,active')
|
||||||
{
|
{
|
||||||
// Unclean
|
// Unclean
|
||||||
return;
|
continue next_pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
new_clean_pgs.items[pool_id] = this.state.config.pgs.items[pool_id];
|
||||||
}
|
}
|
||||||
this.state.history.last_clean_pgs = JSON.parse(JSON.stringify(this.state.config.pgs));
|
this.state.history.last_clean_pgs = new_clean_pgs;
|
||||||
await this.etcd_call('/kv/txn', {
|
await this.etcd_call('/kv/txn', {
|
||||||
success: [ { requestPut: {
|
success: [ { requestPut: {
|
||||||
key: b64(this.etcd_prefix+'/history/last_clean_pgs'),
|
key: b64(this.etcd_prefix+'/history/last_clean_pgs'),
|
||||||
@@ -1013,14 +1037,15 @@ class Mon
|
|||||||
console.log('Pool ID '+pool_id+' is invalid');
|
console.log('Pool ID '+pool_id+' is invalid');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (pool_cfg.scheme !== 'xor' && pool_cfg.scheme !== 'replicated' && pool_cfg.scheme !== 'jerasure')
|
if (pool_cfg.scheme !== 'xor' && pool_cfg.scheme !== 'replicated' &&
|
||||||
|
pool_cfg.scheme !== 'ec' && pool_cfg.scheme !== 'jerasure')
|
||||||
{
|
{
|
||||||
if (warn)
|
if (warn)
|
||||||
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated" and "jerasure" required)');
|
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
|
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
|
||||||
(pool_cfg.scheme === 'xor' || pool_cfg.scheme == 'jerasure') && pool_cfg.pg_size < 3)
|
pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
|
||||||
{
|
{
|
||||||
if (warn)
|
if (warn)
|
||||||
console.log('Pool '+pool_id+' has invalid pg_size');
|
console.log('Pool '+pool_id+' has invalid pg_size');
|
||||||
@@ -1039,7 +1064,8 @@ class Mon
|
|||||||
console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)');
|
console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (pool_cfg.scheme === 'jerasure' && (pool_cfg.parity_chunks < 1 || pool_cfg.parity_chunks > pool_cfg.pg_size-2))
|
if ((pool_cfg.scheme === 'ec' || pool_cfg.scheme === 'jerasure') &&
|
||||||
|
(pool_cfg.parity_chunks < 1 || pool_cfg.parity_chunks > pool_cfg.pg_size-2))
|
||||||
{
|
{
|
||||||
if (warn)
|
if (warn)
|
||||||
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
||||||
@@ -1153,6 +1179,10 @@ class Mon
|
|||||||
{
|
{
|
||||||
prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set;
|
prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set;
|
||||||
}
|
}
|
||||||
|
// Also delete pool statistics
|
||||||
|
etcd_request.success.push({ requestDeleteRange: {
|
||||||
|
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
|
||||||
|
} });
|
||||||
this.save_new_pgs_txn(etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []);
|
this.save_new_pgs_txn(etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1355,16 +1385,14 @@ class Mon
|
|||||||
// This is required for multiple change events to trigger at most 1 recheck in 1s
|
// This is required for multiple change events to trigger at most 1 recheck in 1s
|
||||||
schedule_recheck()
|
schedule_recheck()
|
||||||
{
|
{
|
||||||
if (this.recheck_timer)
|
if (!this.recheck_timer)
|
||||||
{
|
{
|
||||||
clearTimeout(this.recheck_timer);
|
this.recheck_timer = setTimeout(() =>
|
||||||
this.recheck_timer = null;
|
{
|
||||||
|
this.recheck_timer = null;
|
||||||
|
this.recheck_pgs().catch(this.die);
|
||||||
|
}, this.config.mon_change_timeout || 1000);
|
||||||
}
|
}
|
||||||
this.recheck_timer = setTimeout(() =>
|
|
||||||
{
|
|
||||||
this.recheck_timer = null;
|
|
||||||
this.recheck_pgs().catch(this.die);
|
|
||||||
}, this.config.mon_change_timeout || 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sum_op_stats(timestamp, prev_stats)
|
sum_op_stats(timestamp, prev_stats)
|
||||||
@@ -1431,8 +1459,24 @@ class Mon
|
|||||||
sum_object_counts()
|
sum_object_counts()
|
||||||
{
|
{
|
||||||
const object_counts = { object: 0n, clean: 0n, misplaced: 0n, degraded: 0n, incomplete: 0n };
|
const object_counts = { object: 0n, clean: 0n, misplaced: 0n, degraded: 0n, incomplete: 0n };
|
||||||
|
const object_bytes = { object: 0n, clean: 0n, misplaced: 0n, degraded: 0n, incomplete: 0n };
|
||||||
for (const pool_id in this.state.pg.stats)
|
for (const pool_id in this.state.pg.stats)
|
||||||
{
|
{
|
||||||
|
let object_size = 0;
|
||||||
|
for (const osd_num of this.state.pg.stats[pool_id].write_osd_set||[])
|
||||||
|
{
|
||||||
|
if (osd_num && this.state.osd.stats[osd_num] && this.state.osd.stats[osd_num].block_size)
|
||||||
|
{
|
||||||
|
object_size = this.state.osd.stats[osd_num].block_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!object_size)
|
||||||
|
{
|
||||||
|
object_size = (this.state.config.pools[pool_id]||{}).block_size ||
|
||||||
|
this.config.block_size || 131072;
|
||||||
|
}
|
||||||
|
object_size = BigInt(object_size);
|
||||||
for (const pg_num in this.state.pg.stats[pool_id])
|
for (const pg_num in this.state.pg.stats[pool_id])
|
||||||
{
|
{
|
||||||
const st = this.state.pg.stats[pool_id][pg_num];
|
const st = this.state.pg.stats[pool_id][pg_num];
|
||||||
@@ -1443,12 +1487,13 @@ class Mon
|
|||||||
if (st[k+'_count'])
|
if (st[k+'_count'])
|
||||||
{
|
{
|
||||||
object_counts[k] += BigInt(st[k+'_count']);
|
object_counts[k] += BigInt(st[k+'_count']);
|
||||||
|
object_bytes[k] += BigInt(st[k+'_count']) * object_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return object_counts;
|
return { object_counts, object_bytes };
|
||||||
}
|
}
|
||||||
|
|
||||||
sum_inode_stats(prev_stats, timestamp, prev_timestamp)
|
sum_inode_stats(prev_stats, timestamp, prev_timestamp)
|
||||||
@@ -1561,7 +1606,7 @@ class Mon
|
|||||||
{
|
{
|
||||||
const txn = [];
|
const txn = [];
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const object_counts = this.sum_object_counts();
|
const { object_counts, object_bytes } = this.sum_object_counts();
|
||||||
let stats = this.sum_op_stats(timestamp, this.prev_stats);
|
let stats = this.sum_op_stats(timestamp, this.prev_stats);
|
||||||
let inode_stats = this.sum_inode_stats(
|
let inode_stats = this.sum_inode_stats(
|
||||||
this.prev_stats ? this.prev_stats.inode_stats : null,
|
this.prev_stats ? this.prev_stats.inode_stats : null,
|
||||||
@@ -1569,6 +1614,7 @@ class Mon
|
|||||||
);
|
);
|
||||||
this.prev_stats = { timestamp, ...stats, inode_stats };
|
this.prev_stats = { timestamp, ...stats, inode_stats };
|
||||||
stats.object_counts = object_counts;
|
stats.object_counts = object_counts;
|
||||||
|
stats.object_bytes = object_bytes;
|
||||||
stats = this.serialize_bigints(stats);
|
stats = this.serialize_bigints(stats);
|
||||||
inode_stats = this.serialize_bigints(inode_stats);
|
inode_stats = this.serialize_bigints(inode_stats);
|
||||||
txn.push({ requestPut: { key: b64(this.etcd_prefix+'/stats'), value: b64(JSON.stringify(stats)) } });
|
txn.push({ requestPut: { key: b64(this.etcd_prefix+'/stats'), value: b64(JSON.stringify(stats)) } });
|
||||||
@@ -1656,6 +1702,7 @@ class Mon
|
|||||||
// Do not clear these to null
|
// Do not clear these to null
|
||||||
kv.value = kv.value || {};
|
kv.value = kv.value || {};
|
||||||
}
|
}
|
||||||
|
const old = cur[key_parts[key_parts.length-1]];
|
||||||
cur[key_parts[key_parts.length-1]] = kv.value;
|
cur[key_parts[key_parts.length-1]] = kv.value;
|
||||||
if (key === 'config/global')
|
if (key === 'config/global')
|
||||||
{
|
{
|
||||||
@@ -1680,7 +1727,12 @@ class Mon
|
|||||||
}
|
}
|
||||||
else if (key_parts[0] === 'osd' && key_parts[1] === 'stats')
|
else if (key_parts[0] === 'osd' && key_parts[1] === 'stats')
|
||||||
{
|
{
|
||||||
// Recheck PGs <osd_out_time> later
|
// Recheck OSD tree on OSD addition/deletion
|
||||||
|
if ((!old) != (!kv.value) || old && kv.value && old.size != kv.value.size)
|
||||||
|
{
|
||||||
|
this.schedule_recheck();
|
||||||
|
}
|
||||||
|
// Recheck PGs <osd_out_time> after last OSD statistics report
|
||||||
this.schedule_next_recheck_at(
|
this.schedule_next_recheck_at(
|
||||||
!this.state.osd.stats[key[2]] ? 0 : this.state.osd.stats[key[2]].time+this.config.osd_out_time
|
!this.state.osd.stats[key[2]] ? 0 : this.state.osd.stats[key[2]].time+this.config.osd_out_time
|
||||||
);
|
);
|
||||||
|
18
mon/vitastor-mon.service
Normal file
18
mon/vitastor-mon.service
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Vitastor monitor
|
||||||
|
After=network-online.target local-fs.target time-sync.target
|
||||||
|
Wants=network-online.target local-fs.target time-sync.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
ExecStart=node /usr/lib/vitastor/mon/mon-main.js
|
||||||
|
WorkingDirectory=/
|
||||||
|
User=vitastor
|
||||||
|
PrivateTmp=false
|
||||||
|
TasksMax=infinity
|
||||||
|
Restart=always
|
||||||
|
StartLimitInterval=0
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=vitastor.target
|
22
mon/vitastor-osd@.service
Normal file
22
mon/vitastor-osd@.service
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Vitastor object storage daemon osd.%i
|
||||||
|
After=network-online.target local-fs.target time-sync.target
|
||||||
|
Wants=network-online.target local-fs.target time-sync.target
|
||||||
|
PartOf=vitastor.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
LimitNOFILE=1048576
|
||||||
|
LimitNPROC=1048576
|
||||||
|
LimitMEMLOCK=infinity
|
||||||
|
ExecStart=bash -c 'exec vitastor-disk exec-osd /dev/vitastor/osd%i-data >>/var/log/vitastor/osd%i.log 2>&1'
|
||||||
|
ExecStartPre=+vitastor-disk pre-exec /dev/vitastor/osd%i-data
|
||||||
|
WorkingDirectory=/
|
||||||
|
User=vitastor
|
||||||
|
PrivateTmp=false
|
||||||
|
TasksMax=infinity
|
||||||
|
Restart=always
|
||||||
|
StartLimitInterval=0
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=vitastor.target
|
4
mon/vitastor.target
Normal file
4
mon/vitastor.target
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=vitastor target
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
@@ -16,6 +16,11 @@ use PVE::Tools qw(run_command);
|
|||||||
|
|
||||||
use base qw(PVE::Storage::Plugin);
|
use base qw(PVE::Storage::Plugin);
|
||||||
|
|
||||||
|
if (@PVE::Storage::Plugin::SHARED_STORAGE)
|
||||||
|
{
|
||||||
|
push @PVE::Storage::Plugin::SHARED_STORAGE, 'vitastor';
|
||||||
|
}
|
||||||
|
|
||||||
sub api
|
sub api
|
||||||
{
|
{
|
||||||
# Trick it :)
|
# Trick it :)
|
||||||
@@ -133,9 +138,11 @@ sub properties
|
|||||||
sub options
|
sub options
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
shared => { optional => 1 },
|
||||||
|
content => { optional => 1 },
|
||||||
nodes => { optional => 1 },
|
nodes => { optional => 1 },
|
||||||
disable => { optional => 1 },
|
disable => { optional => 1 },
|
||||||
vitastor_etcd_address => { optional => 1},
|
vitastor_etcd_address => { optional => 1 },
|
||||||
vitastor_etcd_prefix => { optional => 1 },
|
vitastor_etcd_prefix => { optional => 1 },
|
||||||
vitastor_config_path => { optional => 1 },
|
vitastor_config_path => { optional => 1 },
|
||||||
vitastor_prefix => { optional => 1 },
|
vitastor_prefix => { optional => 1 },
|
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
|||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
from cinder.volume import volume_utils
|
from cinder.volume import volume_utils
|
||||||
|
|
||||||
VERSION = '0.7.0'
|
VERSION = '0.8.5'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -464,7 +464,7 @@ class VitastorDriver(driver.CloneableImageVD,
|
|||||||
vol_name = utils.convert_str(volume.name)
|
vol_name = utils.convert_str(volume.name)
|
||||||
snap_name = utils.convert_str(snapshot.name)
|
snap_name = utils.convert_str(snapshot.name)
|
||||||
|
|
||||||
snap = self._get_image(vol_name+'@'+snap_name)
|
snap = self._get_image('volume-'+snapshot.volume_id+'@'+snap_name)
|
||||||
if not snap:
|
if not snap:
|
||||||
raise exception.SnapshotNotFound(snapshot_id = snap_name)
|
raise exception.SnapshotNotFound(snapshot_id = snap_name)
|
||||||
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])
|
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])
|
||||||
|
169
patches/pve-qemu-6.2-vitastor.patch
Normal file
169
patches/pve-qemu-6.2-vitastor.patch
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
Index: qemu/block/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/block/meson.build
|
||||||
|
+++ qemu/block/meson.build
|
||||||
|
@@ -91,6 +91,7 @@ foreach m : [
|
||||||
|
[libnfs, 'nfs', files('nfs.c')],
|
||||||
|
[libssh, 'ssh', files('ssh.c')],
|
||||||
|
[rbd, 'rbd', files('rbd.c')],
|
||||||
|
+ [vitastor, 'vitastor', files('vitastor.c')],
|
||||||
|
]
|
||||||
|
if m[0].found()
|
||||||
|
module_ss = ss.source_set()
|
||||||
|
Index: qemu/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/meson.build
|
||||||
|
+++ qemu/meson.build
|
||||||
|
@@ -838,6 +838,26 @@ if not get_option('rbd').auto() or have_
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
+vitastor = not_found
|
||||||
|
+if not get_option('vitastor').auto() or have_block
|
||||||
|
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
|
||||||
|
+ required: get_option('vitastor'), kwargs: static_kwargs)
|
||||||
|
+ if libvitastor_client.found()
|
||||||
|
+ if cc.links('''
|
||||||
|
+ #include <vitastor_c.h>
|
||||||
|
+ int main(void) {
|
||||||
|
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ return 0;
|
||||||
|
+ }''', dependencies: libvitastor_client)
|
||||||
|
+ vitastor = declare_dependency(dependencies: libvitastor_client)
|
||||||
|
+ elif get_option('vitastor').enabled()
|
||||||
|
+ error('could not link libvitastor_client')
|
||||||
|
+ else
|
||||||
|
+ warning('could not link libvitastor_client, disabling')
|
||||||
|
+ endif
|
||||||
|
+ endif
|
||||||
|
+endif
|
||||||
|
+
|
||||||
|
glusterfs = not_found
|
||||||
|
glusterfs_ftruncate_has_stat = false
|
||||||
|
glusterfs_iocb_has_stat = false
|
||||||
|
@@ -1459,6 +1479,7 @@ config_host_data.set('CONFIG_LINUX_AIO',
|
||||||
|
config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found())
|
||||||
|
config_host_data.set('CONFIG_LIBPMEM', libpmem.found())
|
||||||
|
config_host_data.set('CONFIG_RBD', rbd.found())
|
||||||
|
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
|
||||||
|
config_host_data.set('CONFIG_SDL', sdl.found())
|
||||||
|
config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found())
|
||||||
|
config_host_data.set('CONFIG_SECCOMP', seccomp.found())
|
||||||
|
@@ -3424,6 +3445,7 @@ if spice_protocol.found()
|
||||||
|
summary_info += {' spice server support': spice}
|
||||||
|
endif
|
||||||
|
summary_info += {'rbd support': rbd}
|
||||||
|
+summary_info += {'vitastor support': vitastor}
|
||||||
|
summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')}
|
||||||
|
summary_info += {'smartcard support': cacard}
|
||||||
|
summary_info += {'U2F support': u2f}
|
||||||
|
Index: qemu/meson_options.txt
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/meson_options.txt
|
||||||
|
+++ qemu/meson_options.txt
|
||||||
|
@@ -121,6 +121,8 @@ option('lzo', type : 'feature', value :
|
||||||
|
description: 'lzo compression support')
|
||||||
|
option('rbd', type : 'feature', value : 'auto',
|
||||||
|
description: 'Ceph block device driver')
|
||||||
|
+option('vitastor', type : 'feature', value : 'auto',
|
||||||
|
+ description: 'Vitastor block device driver')
|
||||||
|
option('gtk', type : 'feature', value : 'auto',
|
||||||
|
description: 'GTK+ user interface')
|
||||||
|
option('sdl', type : 'feature', value : 'auto',
|
||||||
|
Index: qemu/qapi/block-core.json
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/qapi/block-core.json
|
||||||
|
+++ qemu/qapi/block-core.json
|
||||||
|
@@ -3179,7 +3179,7 @@
|
||||||
|
'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||||
|
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
|
||||||
|
'pbs',
|
||||||
|
- 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsFile:
|
||||||
|
@@ -4125,6 +4125,28 @@
|
||||||
|
'*server': ['InetSocketAddressBase'] } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific block device options for vitastor
|
||||||
|
+#
|
||||||
|
+# @image: Image name
|
||||||
|
+# @inode: Inode number
|
||||||
|
+# @pool: Pool ID
|
||||||
|
+# @size: Desired image size in bytes
|
||||||
|
+# @config-path: Path to Vitastor configuration
|
||||||
|
+# @etcd-host: etcd connection address(es)
|
||||||
|
+# @etcd-prefix: etcd key/value prefix
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'data': { '*inode': 'uint64',
|
||||||
|
+ '*pool': 'uint64',
|
||||||
|
+ '*size': 'uint64',
|
||||||
|
+ '*image': 'str',
|
||||||
|
+ '*config-path': 'str',
|
||||||
|
+ '*etcd-host': 'str',
|
||||||
|
+ '*etcd-prefix': 'str' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @ReplicationMode:
|
||||||
|
#
|
||||||
|
# An enumeration of replication modes.
|
||||||
|
@@ -4520,6 +4542,7 @@
|
||||||
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||||
|
+ 'vitastor': 'BlockdevOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||||
|
'vpc': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vvfat': 'BlockdevOptionsVVFAT'
|
||||||
|
@@ -4910,6 +4933,17 @@
|
||||||
|
'*encrypt' : 'RbdEncryptionCreateOptions' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevCreateOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific image creation options for Vitastor.
|
||||||
|
+#
|
||||||
|
+# @size: Size of the virtual disk in bytes
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
||||||
|
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'size': 'size' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
@@ -5108,6 +5142,7 @@
|
||||||
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevCreateOptionsVmdk',
|
||||||
|
'vpc': 'BlockdevCreateOptionsVpc'
|
||||||
|
} }
|
||||||
|
Index: qemu/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
+++ qemu/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
@@ -31,7 +31,7 @@
|
||||||
|
--with-git=meson \
|
||||||
|
--with-git-submodules=update \
|
||||||
|
--target-list="x86_64-softmmu" \
|
||||||
|
---block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
||||||
|
+--block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,vitastor,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
||||||
|
--audio-drv-list="" \
|
||||||
|
--block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \
|
||||||
|
--with-coroutine=ucontext \
|
||||||
|
@@ -183,6 +183,7 @@
|
||||||
|
--enable-opengl \
|
||||||
|
--enable-pie \
|
||||||
|
--enable-rbd \
|
||||||
|
+--enable-vitastor \
|
||||||
|
--enable-rdma \
|
||||||
|
--enable-seccomp \
|
||||||
|
--enable-snappy \
|
169
patches/pve-qemu-7.1-vitastor.patch
Normal file
169
patches/pve-qemu-7.1-vitastor.patch
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
Index: qemu/block/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/block/meson.build
|
||||||
|
+++ qemu/block/meson.build
|
||||||
|
@@ -111,6 +111,7 @@ foreach m : [
|
||||||
|
[libnfs, 'nfs', files('nfs.c')],
|
||||||
|
[libssh, 'ssh', files('ssh.c')],
|
||||||
|
[rbd, 'rbd', files('rbd.c')],
|
||||||
|
+ [vitastor, 'vitastor', files('vitastor.c')],
|
||||||
|
]
|
||||||
|
if m[0].found()
|
||||||
|
module_ss = ss.source_set()
|
||||||
|
Index: qemu/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/meson.build
|
||||||
|
+++ qemu/meson.build
|
||||||
|
@@ -967,6 +967,26 @@ if not get_option('rbd').auto() or have_
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
+vitastor = not_found
|
||||||
|
+if not get_option('vitastor').auto() or have_block
|
||||||
|
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
|
||||||
|
+ required: get_option('vitastor'), kwargs: static_kwargs)
|
||||||
|
+ if libvitastor_client.found()
|
||||||
|
+ if cc.links('''
|
||||||
|
+ #include <vitastor_c.h>
|
||||||
|
+ int main(void) {
|
||||||
|
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ return 0;
|
||||||
|
+ }''', dependencies: libvitastor_client)
|
||||||
|
+ vitastor = declare_dependency(dependencies: libvitastor_client)
|
||||||
|
+ elif get_option('vitastor').enabled()
|
||||||
|
+ error('could not link libvitastor_client')
|
||||||
|
+ else
|
||||||
|
+ warning('could not link libvitastor_client, disabling')
|
||||||
|
+ endif
|
||||||
|
+ endif
|
||||||
|
+endif
|
||||||
|
+
|
||||||
|
glusterfs = not_found
|
||||||
|
glusterfs_ftruncate_has_stat = false
|
||||||
|
glusterfs_iocb_has_stat = false
|
||||||
|
@@ -1802,6 +1822,7 @@ config_host_data.set('CONFIG_NUMA', numa
|
||||||
|
config_host_data.set('CONFIG_OPENGL', opengl.found())
|
||||||
|
config_host_data.set('CONFIG_PROFILER', get_option('profiler'))
|
||||||
|
config_host_data.set('CONFIG_RBD', rbd.found())
|
||||||
|
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
|
||||||
|
config_host_data.set('CONFIG_RDMA', rdma.found())
|
||||||
|
config_host_data.set('CONFIG_SDL', sdl.found())
|
||||||
|
config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found())
|
||||||
|
@@ -3965,6 +3986,7 @@ if spice_protocol.found()
|
||||||
|
summary_info += {' spice server support': spice}
|
||||||
|
endif
|
||||||
|
summary_info += {'rbd support': rbd}
|
||||||
|
+summary_info += {'vitastor support': vitastor}
|
||||||
|
summary_info += {'smartcard support': cacard}
|
||||||
|
summary_info += {'U2F support': u2f}
|
||||||
|
summary_info += {'libusb': libusb}
|
||||||
|
Index: qemu/meson_options.txt
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/meson_options.txt
|
||||||
|
+++ qemu/meson_options.txt
|
||||||
|
@@ -167,6 +167,8 @@ option('lzo', type : 'feature', value :
|
||||||
|
description: 'lzo compression support')
|
||||||
|
option('rbd', type : 'feature', value : 'auto',
|
||||||
|
description: 'Ceph block device driver')
|
||||||
|
+option('vitastor', type : 'feature', value : 'auto',
|
||||||
|
+ description: 'Vitastor block device driver')
|
||||||
|
option('opengl', type : 'feature', value : 'auto',
|
||||||
|
description: 'OpenGL support')
|
||||||
|
option('rdma', type : 'feature', value : 'auto',
|
||||||
|
Index: qemu/qapi/block-core.json
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/qapi/block-core.json
|
||||||
|
+++ qemu/qapi/block-core.json
|
||||||
|
@@ -3209,7 +3209,7 @@
|
||||||
|
'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||||
|
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
|
||||||
|
'pbs',
|
||||||
|
- 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsFile:
|
||||||
|
@@ -4149,6 +4149,28 @@
|
||||||
|
'*server': ['InetSocketAddressBase'] } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific block device options for vitastor
|
||||||
|
+#
|
||||||
|
+# @image: Image name
|
||||||
|
+# @inode: Inode number
|
||||||
|
+# @pool: Pool ID
|
||||||
|
+# @size: Desired image size in bytes
|
||||||
|
+# @config-path: Path to Vitastor configuration
|
||||||
|
+# @etcd-host: etcd connection address(es)
|
||||||
|
+# @etcd-prefix: etcd key/value prefix
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'data': { '*inode': 'uint64',
|
||||||
|
+ '*pool': 'uint64',
|
||||||
|
+ '*size': 'uint64',
|
||||||
|
+ '*image': 'str',
|
||||||
|
+ '*config-path': 'str',
|
||||||
|
+ '*etcd-host': 'str',
|
||||||
|
+ '*etcd-prefix': 'str' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @ReplicationMode:
|
||||||
|
#
|
||||||
|
# An enumeration of replication modes.
|
||||||
|
@@ -4593,6 +4615,7 @@
|
||||||
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||||
|
+ 'vitastor': 'BlockdevOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||||
|
'vpc': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vvfat': 'BlockdevOptionsVVFAT'
|
||||||
|
@@ -4985,6 +5008,17 @@
|
||||||
|
'*encrypt' : 'RbdEncryptionCreateOptions' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevCreateOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific image creation options for Vitastor.
|
||||||
|
+#
|
||||||
|
+# @size: Size of the virtual disk in bytes
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
||||||
|
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'size': 'size' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
@@ -5182,6 +5216,7 @@
|
||||||
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevCreateOptionsVmdk',
|
||||||
|
'vpc': 'BlockdevCreateOptionsVpc'
|
||||||
|
} }
|
||||||
|
Index: qemu/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
===================================================================
|
||||||
|
--- qemu.orig/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
+++ qemu/scripts/ci/org.centos/stream/8/x86_64/configure
|
||||||
|
@@ -31,7 +31,7 @@
|
||||||
|
--with-git=meson \
|
||||||
|
--with-git-submodules=update \
|
||||||
|
--target-list="x86_64-softmmu" \
|
||||||
|
---block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
||||||
|
+--block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,vitastor,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
||||||
|
--audio-drv-list="" \
|
||||||
|
--block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \
|
||||||
|
--with-coroutine=ucontext \
|
||||||
|
@@ -179,6 +179,7 @@
|
||||||
|
--enable-opengl \
|
||||||
|
--enable-pie \
|
||||||
|
--enable-rbd \
|
||||||
|
+--enable-vitastor \
|
||||||
|
--enable-rdma \
|
||||||
|
--enable-seccomp \
|
||||||
|
--enable-snappy \
|
@@ -9,7 +9,7 @@ for i in "$DIR"/qemu-*-vitastor.patch "$DIR"/pve-qemu-*-vitastor.patch; do
|
|||||||
echo '===================================================================' >> $i
|
echo '===================================================================' >> $i
|
||||||
echo '--- /dev/null' >> $i
|
echo '--- /dev/null' >> $i
|
||||||
echo '+++ a/block/vitastor.c' >> $i
|
echo '+++ a/block/vitastor.c' >> $i
|
||||||
echo '@@ -0,0 +1,'$(wc -l "$DIR"/../src/qemu_driver.c)' @@' >> $i
|
echo '@@ -0,0 +1,'$(wc -l "$DIR"/../src/qemu_driver.c | cut -d ' ' -f 1)' @@' >> $i
|
||||||
cat "$DIR"/../src/qemu_driver.c | sed 's/^/+/' >> $i
|
cat "$DIR"/../src/qemu_driver.c | sed 's/^/+/' >> $i
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@@ -25,4 +25,4 @@ rm fio
|
|||||||
mv fio-copy fio
|
mv fio-copy fio
|
||||||
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
||||||
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
||||||
tar --transform 's#^#vitastor-0.7.0/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.7.0$(rpm --eval '%dist').tar.gz *
|
tar --transform 's#^#vitastor-0.8.5/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.8.5$(rpm --eval '%dist').tar.gz *
|
||||||
|
@@ -58,7 +58,7 @@
|
|||||||
+BuildRequires: gperftools-devel
|
+BuildRequires: gperftools-devel
|
||||||
+BuildRequires: libusbx-devel >= 1.0.21
|
+BuildRequires: libusbx-devel >= 1.0.21
|
||||||
%if %{have_usbredir}
|
%if %{have_usbredir}
|
||||||
BuildRequires: usbredir-devel >= 0.7.1
|
BuildRequires: usbredir-devel >= 0.8.2
|
||||||
%endif
|
%endif
|
||||||
@@ -856,12 +861,13 @@ BuildRequires: virglrenderer-devel
|
@@ -856,12 +861,13 @@ BuildRequires: virglrenderer-devel
|
||||||
# For smartcard NSS support
|
# For smartcard NSS support
|
||||||
|
@@ -9,7 +9,8 @@ WORKDIR /root
|
|||||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||||
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
|
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
|
||||||
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm
|
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm
|
||||||
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel fio rh-nodejs12 jerasure-devel gf-complete-devel rdma-core-devel
|
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel \
|
||||||
|
fio rh-nodejs12 jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel
|
||||||
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
|
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||||
@@ -34,7 +35,7 @@ ADD . /root/vitastor
|
|||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-0.7.0.el7.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-0.8.5.el7.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 0.7.0
|
Version: 0.8.5
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-0.7.0.el7.tar.gz
|
Source0: vitastor-0.8.5.el7.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
@@ -13,6 +13,7 @@ BuildRequires: devtoolset-9-gcc-c++
|
|||||||
BuildRequires: rh-nodejs12
|
BuildRequires: rh-nodejs12
|
||||||
BuildRequires: rh-nodejs12-npm
|
BuildRequires: rh-nodejs12-npm
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: libisa-l-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: libibverbs-devel
|
BuildRequires: libibverbs-devel
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
@@ -32,8 +33,12 @@ size with configurable redundancy (replication or erasure codes/XOR).
|
|||||||
%package -n vitastor-osd
|
%package -n vitastor-osd
|
||||||
Summary: Vitastor - OSD
|
Summary: Vitastor - OSD
|
||||||
Requires: libJerasure2
|
Requires: libJerasure2
|
||||||
|
Requires: libisa-l
|
||||||
Requires: liburing >= 0.6
|
Requires: liburing >= 0.6
|
||||||
|
Requires: liburing < 2
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
Requires: util-linux
|
||||||
|
Requires: parted
|
||||||
|
|
||||||
|
|
||||||
%description -n vitastor-osd
|
%description -n vitastor-osd
|
||||||
@@ -55,6 +60,7 @@ scheduling cluster-level operations.
|
|||||||
%package -n vitastor-client
|
%package -n vitastor-client
|
||||||
Summary: Vitastor - client
|
Summary: Vitastor - client
|
||||||
Requires: liburing >= 0.6
|
Requires: liburing >= 0.6
|
||||||
|
Requires: liburing < 2
|
||||||
|
|
||||||
|
|
||||||
%description -n vitastor-client
|
%description -n vitastor-client
|
||||||
@@ -100,8 +106,11 @@ cd mon
|
|||||||
npm install
|
npm install
|
||||||
cd ..
|
cd ..
|
||||||
mkdir -p %buildroot/usr/lib/vitastor
|
mkdir -p %buildroot/usr/lib/vitastor
|
||||||
cp mon/make-osd.sh %buildroot/usr/lib/vitastor
|
|
||||||
cp -r mon %buildroot/usr/lib/vitastor
|
cp -r mon %buildroot/usr/lib/vitastor
|
||||||
|
mkdir -p %buildroot/lib/systemd/system
|
||||||
|
cp mon/vitastor.target mon/vitastor-mon.service mon/vitastor-osd@.service %buildroot/lib/systemd/system
|
||||||
|
mkdir -p %buildroot/lib/udev/rules.d
|
||||||
|
cp mon/90-vitastor.rules %buildroot/lib/udev/rules.d
|
||||||
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
@@ -110,11 +119,29 @@ cp -r mon %buildroot/usr/lib/vitastor
|
|||||||
|
|
||||||
%files -n vitastor-osd
|
%files -n vitastor-osd
|
||||||
%_bindir/vitastor-osd
|
%_bindir/vitastor-osd
|
||||||
|
%_bindir/vitastor-disk
|
||||||
%_bindir/vitastor-dump-journal
|
%_bindir/vitastor-dump-journal
|
||||||
|
/lib/systemd/system/vitastor-osd@.service
|
||||||
|
/lib/systemd/system/vitastor.target
|
||||||
|
/lib/udev/rules.d/90-vitastor.rules
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-osd
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-mon
|
%files -n vitastor-mon
|
||||||
/usr/lib/vitastor/mon
|
/usr/lib/vitastor/mon
|
||||||
|
/lib/systemd/system/vitastor-mon.service
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-mon
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-client
|
%files -n vitastor-client
|
||||||
@@ -125,7 +152,6 @@ cp -r mon %buildroot/usr/lib/vitastor
|
|||||||
%_bindir/vita
|
%_bindir/vita
|
||||||
%_libdir/libvitastor_blk.so*
|
%_libdir/libvitastor_blk.so*
|
||||||
%_libdir/libvitastor_client.so*
|
%_libdir/libvitastor_client.so*
|
||||||
/usr/lib/vitastor/make-osd.sh
|
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-client-devel
|
%files -n vitastor-client-devel
|
||||||
|
@@ -6,10 +6,12 @@ FROM centos:8
|
|||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
|
||||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||||
|
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/!baseurl=http://vault.centos.org/!' /etc/yum.repos.d/*.repo
|
||||||
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
|
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
|
||||||
|
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/$basearch/$avdir/!; s!^baseurl=.*Source/.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/Source/advanced-virtualization/!' /etc/yum.repos.d/CentOS-Advanced-Virtualization.repo
|
||||||
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm
|
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm
|
||||||
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
|
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
|
||||||
fio nodejs rpm-build jerasure-devel gf-complete-devel libibverbs-devel libarchive cmake
|
fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel libibverbs-devel libarchive cmake
|
||||||
RUN dnf download --source fio
|
RUN dnf download --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
|
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
|
||||||
@@ -33,7 +35,7 @@ ADD . /root/vitastor
|
|||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-0.7.0.el8.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-0.8.5.el8.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
@@ -1,17 +1,18 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 0.7.0
|
Version: 0.8.5
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-0.7.0.el8.tar.gz
|
Source0: vitastor-0.8.5.el8.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-toolset-9-gcc-c++
|
BuildRequires: gcc-toolset-9-gcc-c++
|
||||||
BuildRequires: nodejs >= 10
|
BuildRequires: nodejs >= 10
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: libisa-l-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: libibverbs-devel
|
BuildRequires: libibverbs-devel
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
@@ -31,8 +32,12 @@ size with configurable redundancy (replication or erasure codes/XOR).
|
|||||||
%package -n vitastor-osd
|
%package -n vitastor-osd
|
||||||
Summary: Vitastor - OSD
|
Summary: Vitastor - OSD
|
||||||
Requires: libJerasure2
|
Requires: libJerasure2
|
||||||
|
Requires: libisa-l
|
||||||
Requires: liburing >= 0.6
|
Requires: liburing >= 0.6
|
||||||
|
Requires: liburing < 2
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
Requires: util-linux
|
||||||
|
Requires: parted
|
||||||
|
|
||||||
|
|
||||||
%description -n vitastor-osd
|
%description -n vitastor-osd
|
||||||
@@ -53,6 +58,7 @@ scheduling cluster-level operations.
|
|||||||
%package -n vitastor-client
|
%package -n vitastor-client
|
||||||
Summary: Vitastor - client
|
Summary: Vitastor - client
|
||||||
Requires: liburing >= 0.6
|
Requires: liburing >= 0.6
|
||||||
|
Requires: liburing < 2
|
||||||
|
|
||||||
|
|
||||||
%description -n vitastor-client
|
%description -n vitastor-client
|
||||||
@@ -97,8 +103,11 @@ cd mon
|
|||||||
npm install
|
npm install
|
||||||
cd ..
|
cd ..
|
||||||
mkdir -p %buildroot/usr/lib/vitastor
|
mkdir -p %buildroot/usr/lib/vitastor
|
||||||
cp mon/make-osd.sh %buildroot/usr/lib/vitastor
|
|
||||||
cp -r mon %buildroot/usr/lib/vitastor
|
cp -r mon %buildroot/usr/lib/vitastor
|
||||||
|
mkdir -p %buildroot/lib/systemd/system
|
||||||
|
cp mon/vitastor.target mon/vitastor-mon.service mon/vitastor-osd@.service %buildroot/lib/systemd/system
|
||||||
|
mkdir -p %buildroot/lib/udev/rules.d
|
||||||
|
cp mon/90-vitastor.rules %buildroot/lib/udev/rules.d
|
||||||
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
@@ -107,11 +116,29 @@ cp -r mon %buildroot/usr/lib/vitastor
|
|||||||
|
|
||||||
%files -n vitastor-osd
|
%files -n vitastor-osd
|
||||||
%_bindir/vitastor-osd
|
%_bindir/vitastor-osd
|
||||||
|
%_bindir/vitastor-disk
|
||||||
%_bindir/vitastor-dump-journal
|
%_bindir/vitastor-dump-journal
|
||||||
|
/lib/systemd/system/vitastor-osd@.service
|
||||||
|
/lib/systemd/system/vitastor.target
|
||||||
|
/lib/udev/rules.d/90-vitastor.rules
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-osd
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-mon
|
%files -n vitastor-mon
|
||||||
/usr/lib/vitastor/mon
|
/usr/lib/vitastor/mon
|
||||||
|
/lib/systemd/system/vitastor-mon.service
|
||||||
|
|
||||||
|
|
||||||
|
%pre -n vitastor-mon
|
||||||
|
groupadd -r -f vitastor 2>/dev/null ||:
|
||||||
|
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||||
|
mkdir -p /etc/vitastor
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-client
|
%files -n vitastor-client
|
||||||
@@ -122,7 +149,6 @@ cp -r mon %buildroot/usr/lib/vitastor
|
|||||||
%_bindir/vita
|
%_bindir/vita
|
||||||
%_libdir/libvitastor_blk.so*
|
%_libdir/libvitastor_blk.so*
|
||||||
%_libdir/libvitastor_client.so*
|
%_libdir/libvitastor_client.so*
|
||||||
/usr/lib/vitastor/make-osd.sh
|
|
||||||
|
|
||||||
|
|
||||||
%files -n vitastor-client-devel
|
%files -n vitastor-client-devel
|
||||||
|
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.8)
|
|||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
set(WITH_QEMU false CACHE BOOL "Build QEMU driver inside Vitastor source tree")
|
set(WITH_QEMU false CACHE BOOL "Build QEMU driver inside Vitastor source tree")
|
||||||
set(WITH_FIO true CACHE BOOL "Build FIO driver")
|
set(WITH_FIO true CACHE BOOL "Build FIO driver")
|
||||||
@@ -15,7 +16,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
|||||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_definitions(-DVERSION="0.7.0")
|
add_definitions(-DVERSION="0.8.5")
|
||||||
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
||||||
if (${WITH_ASAN})
|
if (${WITH_ASAN})
|
||||||
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
@@ -50,6 +51,18 @@ pkg_check_modules(IBVERBS libibverbs)
|
|||||||
if (IBVERBS_LIBRARIES)
|
if (IBVERBS_LIBRARIES)
|
||||||
add_definitions(-DWITH_RDMA)
|
add_definitions(-DWITH_RDMA)
|
||||||
endif (IBVERBS_LIBRARIES)
|
endif (IBVERBS_LIBRARIES)
|
||||||
|
pkg_check_modules(ISAL libisal)
|
||||||
|
if (ISAL_LIBRARIES)
|
||||||
|
add_definitions(-DWITH_ISAL)
|
||||||
|
endif (ISAL_LIBRARIES)
|
||||||
|
|
||||||
|
add_custom_target(build_tests)
|
||||||
|
add_custom_target(test
|
||||||
|
COMMAND
|
||||||
|
echo leak:tcmalloc > ${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt &&
|
||||||
|
env LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt ${CMAKE_CTEST_COMMAND}
|
||||||
|
)
|
||||||
|
add_dependencies(test build_tests)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
../
|
../
|
||||||
@@ -60,7 +73,7 @@ include_directories(
|
|||||||
|
|
||||||
# libvitastor_blk.so
|
# libvitastor_blk.so
|
||||||
add_library(vitastor_blk SHARED
|
add_library(vitastor_blk SHARED
|
||||||
allocator.cpp blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp blockstore_journal.cpp blockstore_read.cpp
|
allocator.cpp blockstore.cpp blockstore_impl.cpp blockstore_disk.cpp blockstore_init.cpp blockstore_open.cpp blockstore_journal.cpp blockstore_read.cpp
|
||||||
blockstore_write.cpp blockstore_sync.cpp blockstore_stable.cpp blockstore_rollback.cpp blockstore_flush.cpp crc32c.c ringloop.cpp
|
blockstore_write.cpp blockstore_sync.cpp blockstore_stable.cpp blockstore_rollback.cpp blockstore_flush.cpp crc32c.c ringloop.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor_blk
|
target_link_libraries(vitastor_blk
|
||||||
@@ -90,7 +103,7 @@ endif (IBVERBS_LIBRARIES)
|
|||||||
add_library(vitastor_common STATIC
|
add_library(vitastor_common STATIC
|
||||||
epoll_manager.cpp etcd_state_client.cpp messenger.cpp addr_util.cpp
|
epoll_manager.cpp etcd_state_client.cpp messenger.cpp addr_util.cpp
|
||||||
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ringloop.cpp ../json11/json11.cpp
|
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ringloop.cpp ../json11/json11.cpp
|
||||||
http_client.cpp osd_ops.cpp pg_states.cpp timerfd_manager.cpp base64.cpp ${MSGR_RDMA}
|
http_client.cpp osd_ops.cpp pg_states.cpp timerfd_manager.cpp str_util.cpp ${MSGR_RDMA}
|
||||||
)
|
)
|
||||||
target_compile_options(vitastor_common PUBLIC -fPIC)
|
target_compile_options(vitastor_common PUBLIC -fPIC)
|
||||||
|
|
||||||
@@ -98,12 +111,13 @@ target_compile_options(vitastor_common PUBLIC -fPIC)
|
|||||||
add_executable(vitastor-osd
|
add_executable(vitastor-osd
|
||||||
osd_main.cpp osd.cpp osd_secondary.cpp osd_peering.cpp osd_flush.cpp osd_peering_pg.cpp
|
osd_main.cpp osd.cpp osd_secondary.cpp osd_peering.cpp osd_flush.cpp osd_peering_pg.cpp
|
||||||
osd_primary.cpp osd_primary_chain.cpp osd_primary_sync.cpp osd_primary_write.cpp osd_primary_subops.cpp
|
osd_primary.cpp osd_primary_chain.cpp osd_primary_sync.cpp osd_primary_write.cpp osd_primary_subops.cpp
|
||||||
osd_cluster.cpp osd_rmw.cpp
|
osd_cluster.cpp osd_rmw.cpp osd_scrub.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor-osd
|
target_link_libraries(vitastor-osd
|
||||||
vitastor_common
|
vitastor_common
|
||||||
vitastor_blk
|
vitastor_blk
|
||||||
Jerasure
|
Jerasure
|
||||||
|
${ISAL_LIBRARIES}
|
||||||
${IBVERBS_LIBRARIES}
|
${IBVERBS_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,7 +140,6 @@ add_library(vitastor_client SHARED
|
|||||||
vitastor_c.cpp
|
vitastor_c.cpp
|
||||||
cli_common.cpp
|
cli_common.cpp
|
||||||
cli_alloc_osd.cpp
|
cli_alloc_osd.cpp
|
||||||
cli_simple_offsets.cpp
|
|
||||||
cli_status.cpp
|
cli_status.cpp
|
||||||
cli_df.cpp
|
cli_df.cpp
|
||||||
cli_ls.cpp
|
cli_ls.cpp
|
||||||
@@ -136,11 +149,11 @@ add_library(vitastor_client SHARED
|
|||||||
cli_merge.cpp
|
cli_merge.cpp
|
||||||
cli_rm_data.cpp
|
cli_rm_data.cpp
|
||||||
cli_rm.cpp
|
cli_rm.cpp
|
||||||
|
cli_rm_osd.cpp
|
||||||
)
|
)
|
||||||
set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "vitastor_c.h")
|
set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "vitastor_c.h")
|
||||||
target_link_libraries(vitastor_client
|
target_link_libraries(vitastor_client
|
||||||
vitastor_common
|
vitastor_common
|
||||||
tcmalloc_minimal
|
|
||||||
${LIBURING_LIBRARIES}
|
${LIBURING_LIBRARIES}
|
||||||
${IBVERBS_LIBRARIES}
|
${IBVERBS_LIBRARIES}
|
||||||
)
|
)
|
||||||
@@ -188,9 +201,15 @@ target_link_libraries(vitastor-cli
|
|||||||
)
|
)
|
||||||
configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
||||||
|
|
||||||
# vitastor-dump-journal
|
# vitastor-disk
|
||||||
add_executable(vitastor-dump-journal
|
add_executable(vitastor-disk
|
||||||
dump_journal.cpp crc32c.c
|
disk_tool.cpp disk_simple_offsets.cpp
|
||||||
|
disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp disk_tool_udev.cpp disk_tool_utils.cpp disk_tool_upgrade.cpp
|
||||||
|
crc32c.c str_util.cpp ../json11/json11.cpp rw_blocking.cpp allocator.cpp ringloop.cpp blockstore_disk.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(vitastor-disk
|
||||||
|
tcmalloc_minimal
|
||||||
|
${LIBURING_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (${WITH_QEMU})
|
if (${WITH_QEMU})
|
||||||
@@ -224,8 +243,18 @@ add_executable(osd_test osd_test.cpp rw_blocking.cpp addr_util.cpp)
|
|||||||
target_link_libraries(osd_test tcmalloc_minimal)
|
target_link_libraries(osd_test tcmalloc_minimal)
|
||||||
|
|
||||||
# osd_rmw_test
|
# osd_rmw_test
|
||||||
add_executable(osd_rmw_test osd_rmw_test.cpp allocator.cpp)
|
add_executable(osd_rmw_test EXCLUDE_FROM_ALL osd_rmw_test.cpp allocator.cpp)
|
||||||
target_link_libraries(osd_rmw_test Jerasure tcmalloc_minimal)
|
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 allocator.cpp)
|
||||||
|
target_compile_definitions(osd_rmw_test_je PUBLIC -DNO_ISAL)
|
||||||
|
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)
|
||||||
|
|
||||||
# stub_uring_osd
|
# stub_uring_osd
|
||||||
add_executable(stub_uring_osd
|
add_executable(stub_uring_osd
|
||||||
@@ -239,11 +268,15 @@ target_link_libraries(stub_uring_osd
|
|||||||
)
|
)
|
||||||
|
|
||||||
# osd_peering_pg_test
|
# osd_peering_pg_test
|
||||||
add_executable(osd_peering_pg_test osd_peering_pg_test.cpp osd_peering_pg.cpp)
|
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)
|
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)
|
||||||
|
|
||||||
# test_allocator
|
# test_allocator
|
||||||
add_executable(test_allocator test_allocator.cpp allocator.cpp)
|
add_executable(test_allocator EXCLUDE_FROM_ALL test_allocator.cpp allocator.cpp)
|
||||||
|
add_dependencies(build_tests test_allocator)
|
||||||
|
add_test(NAME test_allocator COMMAND test_allocator)
|
||||||
|
|
||||||
# test_cas
|
# test_cas
|
||||||
add_executable(test_cas
|
add_executable(test_cas
|
||||||
@@ -253,14 +286,25 @@ target_link_libraries(test_cas
|
|||||||
vitastor_client
|
vitastor_client
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# test_crc32
|
||||||
|
add_executable(test_crc32
|
||||||
|
test_crc32.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(test_crc32
|
||||||
|
vitastor_blk
|
||||||
|
)
|
||||||
|
|
||||||
# test_cluster_client
|
# test_cluster_client
|
||||||
add_executable(test_cluster_client
|
add_executable(test_cluster_client
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
test_cluster_client.cpp
|
test_cluster_client.cpp
|
||||||
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp msgr_op.cpp mock/messenger.cpp msgr_stop.cpp
|
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp msgr_op.cpp mock/messenger.cpp msgr_stop.cpp
|
||||||
etcd_state_client.cpp timerfd_manager.cpp ../json11/json11.cpp
|
etcd_state_client.cpp timerfd_manager.cpp str_util.cpp ../json11/json11.cpp
|
||||||
)
|
)
|
||||||
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
||||||
target_include_directories(test_cluster_client PUBLIC ${CMAKE_SOURCE_DIR}/src/mock)
|
target_include_directories(test_cluster_client PUBLIC ${CMAKE_SOURCE_DIR}/src/mock)
|
||||||
|
add_dependencies(build_tests test_cluster_client)
|
||||||
|
add_test(NAME test_cluster_client COMMAND test_cluster_client)
|
||||||
|
|
||||||
## test_blockstore, test_shit
|
## test_blockstore, test_shit
|
||||||
#add_executable(test_blockstore test_blockstore.cpp)
|
#add_executable(test_blockstore test_blockstore.cpp)
|
||||||
@@ -270,7 +314,8 @@ target_include_directories(test_cluster_client PUBLIC ${CMAKE_SOURCE_DIR}/src/mo
|
|||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
install(TARGETS vitastor-osd vitastor-dump-journal vitastor-nbd vitastor-nfs vitastor-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS vitastor-osd vitastor-disk vitastor-nbd vitastor-nfs vitastor-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
install_symlink(vitastor-disk ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-dump-journal)
|
||||||
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
|
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
|
||||||
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
|
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
|
||||||
install(
|
install(
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
|
||||||
|
|
||||||
#include "base64.h"
|
|
||||||
|
|
||||||
std::string base64_encode(const std::string &in)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
unsigned val = 0;
|
|
||||||
int valb = -6;
|
|
||||||
for (unsigned char c: in)
|
|
||||||
{
|
|
||||||
val = (val << 8) + c;
|
|
||||||
valb += 8;
|
|
||||||
while (valb >= 0)
|
|
||||||
{
|
|
||||||
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val>>valb) & 0x3F]);
|
|
||||||
valb -= 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (valb > -6)
|
|
||||||
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val<<8)>>(valb+8)) & 0x3F]);
|
|
||||||
while (out.size() % 4)
|
|
||||||
out.push_back('=');
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char T[256] = { 0 };
|
|
||||||
|
|
||||||
std::string base64_decode(const std::string &in)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
if (T[0] == 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
T[i] = -1;
|
|
||||||
for (int i = 0; i < 64; i++)
|
|
||||||
T[(unsigned char)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i])] = i;
|
|
||||||
}
|
|
||||||
unsigned val = 0;
|
|
||||||
int valb = -8;
|
|
||||||
for (unsigned char c: in)
|
|
||||||
{
|
|
||||||
if (T[c] == -1)
|
|
||||||
break;
|
|
||||||
val = (val<<6) + T[c];
|
|
||||||
valb += 6;
|
|
||||||
if (valb >= 0)
|
|
||||||
{
|
|
||||||
out.push_back(char((val >> valb) & 0xFF));
|
|
||||||
valb -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
std::string base64_encode(const std::string &in);
|
|
||||||
std::string base64_decode(const std::string &in);
|
|
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "object_id.h"
|
#include "object_id.h"
|
||||||
@@ -19,15 +18,19 @@
|
|||||||
#include "timerfd_manager.h"
|
#include "timerfd_manager.h"
|
||||||
|
|
||||||
// Memory alignment for direct I/O (usually 512 bytes)
|
// Memory alignment for direct I/O (usually 512 bytes)
|
||||||
// All other alignments must be a multiple of this one
|
#ifndef DIRECT_IO_ALIGNMENT
|
||||||
|
#define DIRECT_IO_ALIGNMENT 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Memory allocation alignment (page size is usually optimal)
|
||||||
#ifndef MEM_ALIGNMENT
|
#ifndef MEM_ALIGNMENT
|
||||||
#define MEM_ALIGNMENT 4096
|
#define MEM_ALIGNMENT 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Default block size is 128 KB, current allowed range is 4K - 128M
|
// Default block size is 128 KB, current allowed range is 4K - 128M
|
||||||
#define DEFAULT_ORDER 17
|
#define DEFAULT_DATA_BLOCK_ORDER 17
|
||||||
#define MIN_BLOCK_SIZE 4*1024
|
#define MIN_DATA_BLOCK_SIZE 4*1024
|
||||||
#define MAX_BLOCK_SIZE 128*1024*1024
|
#define MAX_DATA_BLOCK_SIZE 128*1024*1024
|
||||||
#define DEFAULT_BITMAP_GRANULARITY 4096
|
#define DEFAULT_BITMAP_GRANULARITY 4096
|
||||||
|
|
||||||
#define BS_OP_MIN 1
|
#define BS_OP_MIN 1
|
||||||
@@ -119,11 +122,14 @@ Output:
|
|||||||
Get a list of all objects in this Blockstore.
|
Get a list of all objects in this Blockstore.
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
- oid.stripe = PG alignment
|
- pg_alignment = PG alignment
|
||||||
- len = PG count or 0 to list all objects
|
- pg_count = PG count or 0 to list all objects
|
||||||
- offset = PG number
|
- pg_number = PG number
|
||||||
- oid.inode = min inode number or 0 to list all inodes
|
- list_stable_limit = max number of clean objects in the reply
|
||||||
- version = max inode number or 0 to list all inodes
|
it's guaranteed that dirty objects are returned from the same interval,
|
||||||
|
i.e. from (min_oid .. min(max_oid, max(returned stable OIDs)))
|
||||||
|
- min_oid = min inode/stripe or 0 to list all objects
|
||||||
|
- max_oid = max inode/stripe or 0 to list all objects
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
- retval = total obj_ver_id count
|
- retval = total obj_ver_id count
|
||||||
@@ -140,10 +146,27 @@ struct blockstore_op_t
|
|||||||
uint64_t opcode;
|
uint64_t opcode;
|
||||||
// finish callback
|
// finish callback
|
||||||
std::function<void (blockstore_op_t*)> callback;
|
std::function<void (blockstore_op_t*)> callback;
|
||||||
object_id oid;
|
union
|
||||||
uint64_t version;
|
{
|
||||||
uint32_t offset;
|
// R/W
|
||||||
uint32_t len;
|
struct
|
||||||
|
{
|
||||||
|
object_id oid;
|
||||||
|
uint64_t version;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t len;
|
||||||
|
};
|
||||||
|
// List
|
||||||
|
struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
object_id min_oid;
|
||||||
|
object_id max_oid;
|
||||||
|
uint32_t pg_alignment;
|
||||||
|
uint32_t pg_count;
|
||||||
|
uint32_t pg_number;
|
||||||
|
uint32_t list_stable_limit;
|
||||||
|
};
|
||||||
|
};
|
||||||
void *buf;
|
void *buf;
|
||||||
void *bitmap;
|
void *bitmap;
|
||||||
int retval;
|
int retval;
|
||||||
@@ -151,7 +174,7 @@ struct blockstore_op_t
|
|||||||
uint8_t private_data[BS_OP_PRIVATE_DATA_SIZE];
|
uint8_t private_data[BS_OP_PRIVATE_DATA_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<std::string, std::string> blockstore_config_t;
|
typedef std::map<std::string, std::string> blockstore_config_t;
|
||||||
|
|
||||||
class blockstore_impl_t;
|
class blockstore_impl_t;
|
||||||
|
|
||||||
@@ -189,7 +212,6 @@ public:
|
|||||||
// Print diagnostics to stdout
|
// Print diagnostics to stdout
|
||||||
void dump_diagnostics();
|
void dump_diagnostics();
|
||||||
|
|
||||||
// FIXME rename to object_size
|
|
||||||
uint32_t get_block_size();
|
uint32_t get_block_size();
|
||||||
uint64_t get_block_count();
|
uint64_t get_block_count();
|
||||||
uint64_t get_free_block_count();
|
uint64_t get_free_block_count();
|
||||||
|
323
src/blockstore_disk.cpp
Normal file
323
src/blockstore_disk.cpp
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "blockstore_impl.h"
|
||||||
|
#include "blockstore_disk.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
|
||||||
|
static uint32_t is_power_of_two(uint64_t value)
|
||||||
|
{
|
||||||
|
uint32_t l = 0;
|
||||||
|
while (value > 1)
|
||||||
|
{
|
||||||
|
if (value & 1)
|
||||||
|
{
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
value = value >> 1;
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config)
|
||||||
|
{
|
||||||
|
// Parse
|
||||||
|
if (config["disable_device_lock"] == "true" || config["disable_device_lock"] == "1" || config["disable_device_lock"] == "yes")
|
||||||
|
{
|
||||||
|
disable_flock = true;
|
||||||
|
}
|
||||||
|
cfg_journal_size = parse_size(config["journal_size"]);
|
||||||
|
data_device = config["data_device"];
|
||||||
|
data_offset = parse_size(config["data_offset"]);
|
||||||
|
cfg_data_size = parse_size(config["data_size"]);
|
||||||
|
meta_device = config["meta_device"];
|
||||||
|
meta_offset = parse_size(config["meta_offset"]);
|
||||||
|
data_block_size = parse_size(config["block_size"]);
|
||||||
|
journal_device = config["journal_device"];
|
||||||
|
journal_offset = parse_size(config["journal_offset"]);
|
||||||
|
disk_alignment = strtoull(config["disk_alignment"].c_str(), NULL, 10);
|
||||||
|
journal_block_size = strtoull(config["journal_block_size"].c_str(), NULL, 10);
|
||||||
|
meta_block_size = strtoull(config["meta_block_size"].c_str(), NULL, 10);
|
||||||
|
bitmap_granularity = strtoull(config["bitmap_granularity"].c_str(), NULL, 10);
|
||||||
|
// Validate
|
||||||
|
if (!data_block_size)
|
||||||
|
{
|
||||||
|
data_block_size = (1 << DEFAULT_DATA_BLOCK_ORDER);
|
||||||
|
}
|
||||||
|
if ((block_order = is_power_of_two(data_block_size)) >= 64 || data_block_size < MIN_DATA_BLOCK_SIZE || data_block_size >= MAX_DATA_BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Bad block size");
|
||||||
|
}
|
||||||
|
if (!disk_alignment)
|
||||||
|
{
|
||||||
|
disk_alignment = 4096;
|
||||||
|
}
|
||||||
|
else if (disk_alignment % DIRECT_IO_ALIGNMENT)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("disk_alignment must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
|
||||||
|
}
|
||||||
|
if (!journal_block_size)
|
||||||
|
{
|
||||||
|
journal_block_size = 4096;
|
||||||
|
}
|
||||||
|
else if (journal_block_size % DIRECT_IO_ALIGNMENT)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("journal_block_size must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
|
||||||
|
}
|
||||||
|
if (!meta_block_size)
|
||||||
|
{
|
||||||
|
meta_block_size = 4096;
|
||||||
|
}
|
||||||
|
else if (meta_block_size % DIRECT_IO_ALIGNMENT)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("meta_block_size must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
|
||||||
|
}
|
||||||
|
if (data_offset % disk_alignment)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("data_offset must be a multiple of disk_alignment = "+std::to_string(disk_alignment));
|
||||||
|
}
|
||||||
|
if (!bitmap_granularity)
|
||||||
|
{
|
||||||
|
bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
||||||
|
}
|
||||||
|
else if (bitmap_granularity % disk_alignment)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Sparse write tracking granularity must be a multiple of disk_alignment = "+std::to_string(disk_alignment));
|
||||||
|
}
|
||||||
|
if (data_block_size % bitmap_granularity)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Block size must be a multiple of sparse write tracking granularity");
|
||||||
|
}
|
||||||
|
if (meta_device == "")
|
||||||
|
{
|
||||||
|
meta_device = data_device;
|
||||||
|
}
|
||||||
|
if (journal_device == "")
|
||||||
|
{
|
||||||
|
journal_device = meta_device;
|
||||||
|
}
|
||||||
|
if (meta_offset % meta_block_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("meta_offset must be a multiple of meta_block_size = "+std::to_string(meta_block_size));
|
||||||
|
}
|
||||||
|
if (journal_offset % journal_block_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("journal_offset must be a multiple of journal_block_size = "+std::to_string(journal_block_size));
|
||||||
|
}
|
||||||
|
clean_entry_bitmap_size = data_block_size / bitmap_granularity / 8;
|
||||||
|
clean_entry_size = sizeof(clean_disk_entry) + 2*clean_entry_bitmap_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::calc_lengths(bool skip_meta_check)
|
||||||
|
{
|
||||||
|
// data
|
||||||
|
data_len = data_device_size - data_offset;
|
||||||
|
if (data_fd == meta_fd && data_offset < meta_offset)
|
||||||
|
{
|
||||||
|
data_len = meta_offset - data_offset;
|
||||||
|
}
|
||||||
|
if (data_fd == journal_fd && data_offset < journal_offset)
|
||||||
|
{
|
||||||
|
data_len = data_len < journal_offset-data_offset
|
||||||
|
? data_len : journal_offset-data_offset;
|
||||||
|
}
|
||||||
|
if (cfg_data_size != 0)
|
||||||
|
{
|
||||||
|
if (data_len < cfg_data_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Data area ("+std::to_string(data_len)+
|
||||||
|
" bytes) is smaller than configured size ("+std::to_string(cfg_data_size)+" bytes)");
|
||||||
|
}
|
||||||
|
data_len = cfg_data_size;
|
||||||
|
}
|
||||||
|
// meta
|
||||||
|
uint64_t meta_area_size = (meta_fd == data_fd ? data_device_size : meta_device_size) - meta_offset;
|
||||||
|
if (meta_fd == data_fd && meta_offset <= data_offset)
|
||||||
|
{
|
||||||
|
meta_area_size = data_offset - meta_offset;
|
||||||
|
}
|
||||||
|
if (meta_fd == journal_fd && meta_offset <= journal_offset)
|
||||||
|
{
|
||||||
|
meta_area_size = meta_area_size < journal_offset-meta_offset
|
||||||
|
? meta_area_size : journal_offset-meta_offset;
|
||||||
|
}
|
||||||
|
// journal
|
||||||
|
journal_len = (journal_fd == data_fd ? data_device_size : (journal_fd == meta_fd ? meta_device_size : journal_device_size)) - journal_offset;
|
||||||
|
if (journal_fd == data_fd && journal_offset <= data_offset)
|
||||||
|
{
|
||||||
|
journal_len = data_offset - journal_offset;
|
||||||
|
}
|
||||||
|
if (journal_fd == meta_fd && journal_offset <= meta_offset)
|
||||||
|
{
|
||||||
|
journal_len = journal_len < meta_offset-journal_offset
|
||||||
|
? journal_len : meta_offset-journal_offset;
|
||||||
|
}
|
||||||
|
// required metadata size
|
||||||
|
block_count = data_len / data_block_size;
|
||||||
|
meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size) / (meta_block_size / clean_entry_size)) * meta_block_size;
|
||||||
|
if (!skip_meta_check && meta_area_size < meta_len)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Metadata area is too small, need at least "+std::to_string(meta_len)+" bytes");
|
||||||
|
}
|
||||||
|
// requested journal size
|
||||||
|
if (!skip_meta_check && cfg_journal_size > journal_len)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Requested journal_size is too large");
|
||||||
|
}
|
||||||
|
else if (cfg_journal_size > 0)
|
||||||
|
{
|
||||||
|
journal_len = cfg_journal_size;
|
||||||
|
}
|
||||||
|
if (journal_len < MIN_JOURNAL_SIZE)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Journal is too small, need at least "+std::to_string(MIN_JOURNAL_SIZE)+" bytes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Move to utils
|
||||||
|
static void check_size(int fd, uint64_t *size, uint64_t *sectsize, std::string name)
|
||||||
|
{
|
||||||
|
int sect;
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to stat "+name);
|
||||||
|
}
|
||||||
|
if (S_ISREG(st.st_mode))
|
||||||
|
{
|
||||||
|
*size = st.st_size;
|
||||||
|
if (sectsize)
|
||||||
|
{
|
||||||
|
*sectsize = st.st_blksize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (S_ISBLK(st.st_mode))
|
||||||
|
{
|
||||||
|
if (ioctl(fd, BLKGETSIZE64, size) < 0 ||
|
||||||
|
ioctl(fd, BLKSSZGET, §) < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get "+name+" size or block size: "+strerror(errno));
|
||||||
|
}
|
||||||
|
if (sectsize)
|
||||||
|
{
|
||||||
|
*sectsize = sect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error(name+" is neither a file nor a block device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::open_data()
|
||||||
|
{
|
||||||
|
data_fd = open(data_device.c_str(), O_DIRECT|O_RDWR);
|
||||||
|
if (data_fd == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to open data device "+data_device+": "+std::string(strerror(errno)));
|
||||||
|
}
|
||||||
|
check_size(data_fd, &data_device_size, &data_device_sect, "data device");
|
||||||
|
if (disk_alignment % data_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"disk_alignment ("+std::to_string(disk_alignment)+
|
||||||
|
") is not a multiple of data device sector size ("+std::to_string(data_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data_offset >= data_device_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("data_offset exceeds device size = "+std::to_string(data_device_size));
|
||||||
|
}
|
||||||
|
if (!disable_flock && flock(data_fd, LOCK_EX|LOCK_NB) != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Failed to lock data device: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::open_meta()
|
||||||
|
{
|
||||||
|
if (meta_device != data_device)
|
||||||
|
{
|
||||||
|
meta_fd = open(meta_device.c_str(), O_DIRECT|O_RDWR);
|
||||||
|
if (meta_fd == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to open metadata device "+meta_device+": "+std::string(strerror(errno)));
|
||||||
|
}
|
||||||
|
check_size(meta_fd, &meta_device_size, &meta_device_sect, "metadata device");
|
||||||
|
if (meta_offset >= meta_device_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(meta_device_size));
|
||||||
|
}
|
||||||
|
if (!disable_flock && flock(meta_fd, LOCK_EX|LOCK_NB) != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Failed to lock metadata device: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
meta_fd = data_fd;
|
||||||
|
meta_device_sect = data_device_sect;
|
||||||
|
meta_device_size = 0;
|
||||||
|
if (meta_offset >= data_device_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(data_device_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (meta_block_size % meta_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"meta_block_size ("+std::to_string(meta_block_size)+
|
||||||
|
") is not a multiple of data device sector size ("+std::to_string(meta_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::open_journal()
|
||||||
|
{
|
||||||
|
if (journal_device != meta_device)
|
||||||
|
{
|
||||||
|
journal_fd = open(journal_device.c_str(), O_DIRECT|O_RDWR);
|
||||||
|
if (journal_fd == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to open journal device "+journal_device+": "+std::string(strerror(errno)));
|
||||||
|
}
|
||||||
|
check_size(journal_fd, &journal_device_size, &journal_device_sect, "journal device");
|
||||||
|
if (!disable_flock && flock(journal_fd, LOCK_EX|LOCK_NB) != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Failed to lock journal device: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
journal_fd = meta_fd;
|
||||||
|
journal_device_sect = meta_device_sect;
|
||||||
|
journal_device_size = 0;
|
||||||
|
if (journal_offset >= data_device_size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("journal_offset exceeds device size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (journal_block_size % journal_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"journal_block_size ("+std::to_string(journal_block_size)+
|
||||||
|
") is not a multiple of journal device sector size ("+std::to_string(journal_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_disk_t::close_all()
|
||||||
|
{
|
||||||
|
if (data_fd >= 0)
|
||||||
|
close(data_fd);
|
||||||
|
if (meta_fd >= 0 && meta_fd != data_fd)
|
||||||
|
close(meta_fd);
|
||||||
|
if (journal_fd >= 0 && journal_fd != meta_fd)
|
||||||
|
close(journal_fd);
|
||||||
|
data_fd = meta_fd = journal_fd = -1;
|
||||||
|
}
|
42
src/blockstore_disk.h
Normal file
42
src/blockstore_disk.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
struct blockstore_disk_t
|
||||||
|
{
|
||||||
|
std::string data_device, meta_device, journal_device;
|
||||||
|
uint32_t data_block_size;
|
||||||
|
uint64_t cfg_journal_size, cfg_data_size;
|
||||||
|
// Required write alignment and journal/metadata/data areas' location alignment
|
||||||
|
uint32_t disk_alignment = 4096;
|
||||||
|
// Journal block size - minimum_io_size of the journal device is the best choice
|
||||||
|
uint64_t journal_block_size = 4096;
|
||||||
|
// Metadata block size - minimum_io_size of the metadata device is the best choice
|
||||||
|
uint64_t meta_block_size = 4096;
|
||||||
|
// Sparse write tracking granularity. 4 KB is a good choice. Must be a multiple of disk_alignment
|
||||||
|
uint64_t bitmap_granularity = 4096;
|
||||||
|
// By default, Blockstore locks all opened devices exclusively. This option can be used to disable locking
|
||||||
|
bool disable_flock = false;
|
||||||
|
|
||||||
|
int meta_fd = -1, data_fd = -1, journal_fd = -1;
|
||||||
|
uint64_t meta_offset, meta_device_sect, meta_device_size, meta_len;
|
||||||
|
uint64_t data_offset, data_device_sect, data_device_size, data_len;
|
||||||
|
uint64_t journal_offset, journal_device_sect, journal_device_size, journal_len;
|
||||||
|
|
||||||
|
uint32_t block_order;
|
||||||
|
uint64_t block_count;
|
||||||
|
uint32_t clean_entry_bitmap_size = 0, clean_entry_size = 0;
|
||||||
|
|
||||||
|
void parse_config(std::map<std::string, std::string> & config);
|
||||||
|
void open_data();
|
||||||
|
void open_meta();
|
||||||
|
void open_journal();
|
||||||
|
void calc_lengths(bool skip_meta_check = false);
|
||||||
|
void close_all();
|
||||||
|
};
|
@@ -15,11 +15,11 @@ journal_flusher_t::journal_flusher_t(blockstore_impl_t *bs)
|
|||||||
active_flushers = 0;
|
active_flushers = 0;
|
||||||
syncing_flushers = 0;
|
syncing_flushers = 0;
|
||||||
// FIXME: allow to configure flusher_start_threshold and journal_trim_interval
|
// FIXME: allow to configure flusher_start_threshold and journal_trim_interval
|
||||||
flusher_start_threshold = bs->journal_block_size / sizeof(journal_entry_stable);
|
flusher_start_threshold = bs->dsk.journal_block_size / sizeof(journal_entry_stable);
|
||||||
journal_trim_interval = 512;
|
journal_trim_interval = 512;
|
||||||
journal_trim_counter = bs->journal.flush_journal ? 1 : 0;
|
journal_trim_counter = bs->journal.flush_journal ? 1 : 0;
|
||||||
trim_wanted = bs->journal.flush_journal ? 1 : 0;
|
trim_wanted = bs->journal.flush_journal ? 1 : 0;
|
||||||
journal_superblock = bs->journal.inmemory ? bs->journal.buffer : memalign_or_die(MEM_ALIGNMENT, bs->journal_block_size);
|
journal_superblock = bs->journal.inmemory ? bs->journal.buffer : memalign_or_die(MEM_ALIGNMENT, bs->dsk.journal_block_size);
|
||||||
co = new journal_flusher_co[max_flusher_count];
|
co = new journal_flusher_co[max_flusher_count];
|
||||||
for (int i = 0; i < max_flusher_count; i++)
|
for (int i = 0; i < max_flusher_count; i++)
|
||||||
{
|
{
|
||||||
@@ -35,24 +35,14 @@ journal_flusher_co::journal_flusher_co()
|
|||||||
{
|
{
|
||||||
bs->live = true;
|
bs->live = true;
|
||||||
if (data->res != data->iov.iov_len)
|
if (data->res != data->iov.iov_len)
|
||||||
{
|
bs->disk_error_abort("read operation during flush", data->res, data->iov.iov_len);
|
||||||
throw std::runtime_error(
|
|
||||||
"data read operation failed during flush ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). can't continue, sorry :-("
|
|
||||||
);
|
|
||||||
}
|
|
||||||
wait_count--;
|
wait_count--;
|
||||||
};
|
};
|
||||||
simple_callback_w = [this](ring_data_t* data)
|
simple_callback_w = [this](ring_data_t* data)
|
||||||
{
|
{
|
||||||
bs->live = true;
|
bs->live = true;
|
||||||
if (data->res != data->iov.iov_len)
|
if (data->res != data->iov.iov_len)
|
||||||
{
|
bs->disk_error_abort("write operation during flush", data->res, data->iov.iov_len);
|
||||||
throw std::runtime_error(
|
|
||||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). state "+std::to_string(wait_state)+". in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
wait_count--;
|
wait_count--;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -87,7 +77,7 @@ void journal_flusher_t::loop()
|
|||||||
cur_flusher_count--;
|
cur_flusher_count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; (active_flushers > 0 || dequeuing) && i < cur_flusher_count; i++)
|
for (int i = 0; (active_flushers > 0 || dequeuing || trim_wanted > 0) && i < cur_flusher_count; i++)
|
||||||
co[i].loop();
|
co[i].loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +162,8 @@ void journal_flusher_t::mark_trim_possible()
|
|||||||
if (trim_wanted > 0)
|
if (trim_wanted > 0)
|
||||||
{
|
{
|
||||||
dequeuing = true;
|
dequeuing = true;
|
||||||
journal_trim_counter++;
|
if (!journal_trim_counter)
|
||||||
|
journal_trim_counter = journal_trim_interval;
|
||||||
bs->ringloop->wakeup();
|
bs->ringloop->wakeup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,6 +297,8 @@ bool journal_flusher_co::loop()
|
|||||||
goto resume_20;
|
goto resume_20;
|
||||||
else if (wait_state == 21)
|
else if (wait_state == 21)
|
||||||
goto resume_21;
|
goto resume_21;
|
||||||
|
else if (wait_state == 22)
|
||||||
|
goto resume_22;
|
||||||
resume_0:
|
resume_0:
|
||||||
if (flusher->flush_queue.size() < flusher->min_flusher_count && !flusher->trim_wanted ||
|
if (flusher->flush_queue.size() < flusher->min_flusher_count && !flusher->trim_wanted ||
|
||||||
!flusher->flush_queue.size() || !flusher->dequeuing)
|
!flusher->flush_queue.size() || !flusher->dequeuing)
|
||||||
@@ -486,31 +479,38 @@ resume_1:
|
|||||||
bs->ringloop->wakeup();
|
bs->ringloop->wakeup();
|
||||||
}
|
}
|
||||||
// Reads completed, submit writes and set bitmap bits
|
// Reads completed, submit writes and set bitmap bits
|
||||||
if (bs->clean_entry_bitmap_size)
|
if (bs->dsk.clean_entry_bitmap_size)
|
||||||
{
|
{
|
||||||
new_clean_bitmap = (bs->inmemory_meta
|
new_clean_bitmap = (bs->inmemory_meta
|
||||||
? (uint8_t*)meta_new.buf + meta_new.pos*bs->clean_entry_size + sizeof(clean_disk_entry)
|
? (uint8_t*)meta_new.buf + meta_new.pos*bs->dsk.clean_entry_size + sizeof(clean_disk_entry)
|
||||||
: (uint8_t*)bs->clean_bitmap + (clean_loc >> bs->block_order)*(2*bs->clean_entry_bitmap_size));
|
: (uint8_t*)bs->clean_bitmap + (clean_loc >> bs->dsk.block_order)*(2*bs->dsk.clean_entry_bitmap_size));
|
||||||
if (clean_init_bitmap)
|
if (clean_init_bitmap)
|
||||||
{
|
{
|
||||||
memset(new_clean_bitmap, 0, bs->clean_entry_bitmap_size);
|
memset(new_clean_bitmap, 0, bs->dsk.clean_entry_bitmap_size);
|
||||||
bitmap_set(new_clean_bitmap, clean_bitmap_offset, clean_bitmap_len, bs->bitmap_granularity);
|
bitmap_set(new_clean_bitmap, clean_bitmap_offset, clean_bitmap_len, bs->dsk.bitmap_granularity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (it = v.begin(); it != v.end(); it++)
|
for (it = v.begin(); it != v.end(); it++)
|
||||||
{
|
{
|
||||||
if (new_clean_bitmap)
|
if (new_clean_bitmap)
|
||||||
{
|
{
|
||||||
bitmap_set(new_clean_bitmap, it->offset, it->len, bs->bitmap_granularity);
|
bitmap_set(new_clean_bitmap, it->offset, it->len, bs->dsk.bitmap_granularity);
|
||||||
}
|
}
|
||||||
await_sqe(4);
|
await_sqe(4);
|
||||||
data->iov = (struct iovec){ it->buf, (size_t)it->len };
|
data->iov = (struct iovec){ it->buf, (size_t)it->len };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe, bs->data_fd, &data->iov, 1, bs->data_offset + clean_loc + it->offset
|
sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + it->offset
|
||||||
);
|
);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
}
|
}
|
||||||
|
// Wait for data writes before fsyncing it
|
||||||
|
resume_22:
|
||||||
|
if (wait_count > 0)
|
||||||
|
{
|
||||||
|
wait_state = 22;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Sync data before writing metadata
|
// Sync data before writing metadata
|
||||||
resume_16:
|
resume_16:
|
||||||
resume_17:
|
resume_17:
|
||||||
@@ -521,7 +521,7 @@ resume_1:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resume_5:
|
resume_5:
|
||||||
// And metadata writes, but only after data writes complete
|
// Submit metadata writes, but only when data is written and fsynced
|
||||||
if (!bs->inmemory_meta && meta_new.it->second.state == 0 || wait_count > 0)
|
if (!bs->inmemory_meta && meta_new.it->second.state == 0 || wait_count > 0)
|
||||||
{
|
{
|
||||||
// metadata sector is still being read or data is still being written, wait for it
|
// metadata sector is still being read or data is still being written, wait for it
|
||||||
@@ -536,35 +536,35 @@ resume_1:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// zero out old metadata entry
|
// zero out old metadata entry
|
||||||
memset((uint8_t*)meta_old.buf + meta_old.pos*bs->clean_entry_size, 0, bs->clean_entry_size);
|
memset((uint8_t*)meta_old.buf + meta_old.pos*bs->dsk.clean_entry_size, 0, bs->dsk.clean_entry_size);
|
||||||
await_sqe(15);
|
await_sqe(15);
|
||||||
data->iov = (struct iovec){ meta_old.buf, bs->meta_block_size };
|
data->iov = (struct iovec){ meta_old.buf, bs->dsk.meta_block_size };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + meta_old.sector
|
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_old.sector
|
||||||
);
|
);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
}
|
}
|
||||||
if (has_delete)
|
if (has_delete)
|
||||||
{
|
{
|
||||||
clean_disk_entry *new_entry = (clean_disk_entry*)((uint8_t*)meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
clean_disk_entry *new_entry = (clean_disk_entry*)((uint8_t*)meta_new.buf + meta_new.pos*bs->dsk.clean_entry_size);
|
||||||
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
||||||
{
|
{
|
||||||
printf("Fatal error (metadata corruption or bug): tried to delete metadata entry %lu (%lx:%lx v%lu) while deleting %lx:%lx\n",
|
printf("Fatal error (metadata corruption or bug): tried to delete metadata entry %lu (%lx:%lx v%lu) while deleting %lx:%lx\n",
|
||||||
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe,
|
clean_loc >> bs->dsk.block_order, new_entry->oid.inode, new_entry->oid.stripe,
|
||||||
new_entry->version, cur.oid.inode, cur.oid.stripe);
|
new_entry->version, cur.oid.inode, cur.oid.stripe);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
// zero out new metadata entry
|
// zero out new metadata entry
|
||||||
memset((uint8_t*)meta_new.buf + meta_new.pos*bs->clean_entry_size, 0, bs->clean_entry_size);
|
memset((uint8_t*)meta_new.buf + meta_new.pos*bs->dsk.clean_entry_size, 0, bs->dsk.clean_entry_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
clean_disk_entry *new_entry = (clean_disk_entry*)((uint8_t*)meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
clean_disk_entry *new_entry = (clean_disk_entry*)((uint8_t*)meta_new.buf + meta_new.pos*bs->dsk.clean_entry_size);
|
||||||
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
||||||
{
|
{
|
||||||
printf("Fatal error (metadata corruption or bug): tried to overwrite non-zero metadata entry %lu (%lx:%lx v%lu) with %lx:%lx v%lu\n",
|
printf("Fatal error (metadata corruption or bug): tried to overwrite non-zero metadata entry %lu (%lx:%lx v%lu) with %lx:%lx v%lu\n",
|
||||||
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe, new_entry->version,
|
clean_loc >> bs->dsk.block_order, new_entry->oid.inode, new_entry->oid.stripe, new_entry->version,
|
||||||
cur.oid.inode, cur.oid.stripe, cur.version);
|
cur.oid.inode, cur.oid.stripe, cur.version);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -572,20 +572,20 @@ resume_1:
|
|||||||
new_entry->version = cur.version;
|
new_entry->version = cur.version;
|
||||||
if (!bs->inmemory_meta)
|
if (!bs->inmemory_meta)
|
||||||
{
|
{
|
||||||
memcpy(&new_entry->bitmap, new_clean_bitmap, bs->clean_entry_bitmap_size);
|
memcpy(&new_entry->bitmap, new_clean_bitmap, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
// copy latest external bitmap/attributes
|
// copy latest external bitmap/attributes
|
||||||
if (bs->clean_entry_bitmap_size)
|
if (bs->dsk.clean_entry_bitmap_size)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = bs->clean_entry_bitmap_size > sizeof(void*) ? dirty_end->second.bitmap : &dirty_end->second.bitmap;
|
void *bmp_ptr = bs->dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_end->second.bitmap : &dirty_end->second.bitmap;
|
||||||
memcpy((uint8_t*)(new_entry+1) + bs->clean_entry_bitmap_size, bmp_ptr, bs->clean_entry_bitmap_size);
|
memcpy((uint8_t*)(new_entry+1) + bs->dsk.clean_entry_bitmap_size, bmp_ptr, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await_sqe(6);
|
await_sqe(6);
|
||||||
data->iov = (struct iovec){ meta_new.buf, bs->meta_block_size };
|
data->iov = (struct iovec){ meta_new.buf, bs->dsk.meta_block_size };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + meta_new.sector
|
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_new.sector
|
||||||
);
|
);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
resume_7:
|
resume_7:
|
||||||
@@ -615,7 +615,12 @@ resume_1:
|
|||||||
}
|
}
|
||||||
for (it = v.begin(); it != v.end(); it++)
|
for (it = v.begin(); it != v.end(); it++)
|
||||||
{
|
{
|
||||||
free(it->buf);
|
// Free it if it's not taken from the journal
|
||||||
|
if (it->buf && (!bs->journal.inmemory || it->buf < bs->journal.buffer ||
|
||||||
|
it->buf >= (uint8_t*)bs->journal.buffer + bs->journal.len))
|
||||||
|
{
|
||||||
|
free(it->buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
v.clear();
|
v.clear();
|
||||||
// And sync metadata (in batches - not per each operation!)
|
// And sync metadata (in batches - not per each operation!)
|
||||||
@@ -669,9 +674,9 @@ resume_1:
|
|||||||
.version = JOURNAL_VERSION,
|
.version = JOURNAL_VERSION,
|
||||||
};
|
};
|
||||||
((journal_entry_start*)flusher->journal_superblock)->crc32 = je_crc32((journal_entry*)flusher->journal_superblock);
|
((journal_entry_start*)flusher->journal_superblock)->crc32 = je_crc32((journal_entry*)flusher->journal_superblock);
|
||||||
data->iov = (struct iovec){ flusher->journal_superblock, bs->journal_block_size };
|
data->iov = (struct iovec){ flusher->journal_superblock, bs->dsk.journal_block_size };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
my_uring_prep_writev(sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset);
|
my_uring_prep_writev(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
resume_13:
|
resume_13:
|
||||||
if (wait_count > 0)
|
if (wait_count > 0)
|
||||||
@@ -682,7 +687,7 @@ resume_1:
|
|||||||
if (!bs->disable_journal_fsync)
|
if (!bs->disable_journal_fsync)
|
||||||
{
|
{
|
||||||
await_sqe(20);
|
await_sqe(20);
|
||||||
my_uring_prep_fsync(sqe, bs->journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, bs->dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
resume_21:
|
resume_21:
|
||||||
@@ -760,21 +765,22 @@ bool journal_flusher_co::scan_dirty(int wait_base)
|
|||||||
{
|
{
|
||||||
submit_offset = dirty_it->second.location + offset - dirty_it->second.offset;
|
submit_offset = dirty_it->second.location + offset - dirty_it->second.offset;
|
||||||
submit_len = it == v.end() || it->offset >= end_offset ? end_offset-offset : it->offset-offset;
|
submit_len = it == v.end() || it->offset >= end_offset ? end_offset-offset : it->offset-offset;
|
||||||
it = v.insert(it, (copy_buffer_t){ .offset = offset, .len = submit_len, .buf = memalign_or_die(MEM_ALIGNMENT, submit_len) });
|
it = v.insert(it, (copy_buffer_t){ .offset = offset, .len = submit_len });
|
||||||
copy_count++;
|
copy_count++;
|
||||||
if (bs->journal.inmemory)
|
if (bs->journal.inmemory)
|
||||||
{
|
{
|
||||||
// Take it from memory
|
// Take it from memory, don't copy it
|
||||||
memcpy(it->buf, (uint8_t*)bs->journal.buffer + submit_offset, submit_len);
|
it->buf = (uint8_t*)bs->journal.buffer + submit_offset;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Read it from disk
|
// Read it from disk
|
||||||
|
it->buf = memalign_or_die(MEM_ALIGNMENT, submit_len);
|
||||||
await_sqe(0);
|
await_sqe(0);
|
||||||
data->iov = (struct iovec){ it->buf, (size_t)submit_len };
|
data->iov = (struct iovec){ it->buf, (size_t)submit_len };
|
||||||
data->callback = simple_callback_r;
|
data->callback = simple_callback_r;
|
||||||
my_uring_prep_readv(
|
my_uring_prep_readv(
|
||||||
sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset + submit_offset
|
sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset + submit_offset
|
||||||
);
|
);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
}
|
}
|
||||||
@@ -825,8 +831,8 @@ bool journal_flusher_co::modify_meta_read(uint64_t meta_loc, flusher_meta_write_
|
|||||||
// And yet another option is to use LSM trees for metadata, but it sophisticates everything a lot,
|
// And yet another option is to use LSM trees for metadata, but it sophisticates everything a lot,
|
||||||
// so I'll avoid it as long as I can.
|
// so I'll avoid it as long as I can.
|
||||||
wr.submitted = false;
|
wr.submitted = false;
|
||||||
wr.sector = ((meta_loc >> bs->block_order) / (bs->meta_block_size / bs->clean_entry_size)) * bs->meta_block_size;
|
wr.sector = ((meta_loc >> bs->dsk.block_order) / (bs->dsk.meta_block_size / bs->dsk.clean_entry_size)) * bs->dsk.meta_block_size;
|
||||||
wr.pos = ((meta_loc >> bs->block_order) % (bs->meta_block_size / bs->clean_entry_size));
|
wr.pos = ((meta_loc >> bs->dsk.block_order) % (bs->dsk.meta_block_size / bs->dsk.clean_entry_size));
|
||||||
if (bs->inmemory_meta)
|
if (bs->inmemory_meta)
|
||||||
{
|
{
|
||||||
wr.buf = (uint8_t*)bs->metadata_buffer + wr.sector;
|
wr.buf = (uint8_t*)bs->metadata_buffer + wr.sector;
|
||||||
@@ -836,20 +842,20 @@ bool journal_flusher_co::modify_meta_read(uint64_t meta_loc, flusher_meta_write_
|
|||||||
if (wr.it == flusher->meta_sectors.end())
|
if (wr.it == flusher->meta_sectors.end())
|
||||||
{
|
{
|
||||||
// Not in memory yet, read it
|
// Not in memory yet, read it
|
||||||
wr.buf = memalign_or_die(MEM_ALIGNMENT, bs->meta_block_size);
|
wr.buf = memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
|
||||||
wr.it = flusher->meta_sectors.emplace(wr.sector, (meta_sector_t){
|
wr.it = flusher->meta_sectors.emplace(wr.sector, (meta_sector_t){
|
||||||
.offset = wr.sector,
|
.offset = wr.sector,
|
||||||
.len = bs->meta_block_size,
|
.len = bs->dsk.meta_block_size,
|
||||||
.state = 0, // 0 = not read yet
|
.state = 0, // 0 = not read yet
|
||||||
.buf = wr.buf,
|
.buf = wr.buf,
|
||||||
.usage_count = 1,
|
.usage_count = 1,
|
||||||
}).first;
|
}).first;
|
||||||
await_sqe(0);
|
await_sqe(0);
|
||||||
data->iov = (struct iovec){ wr.it->second.buf, bs->meta_block_size };
|
data->iov = (struct iovec){ wr.it->second.buf, bs->dsk.meta_block_size };
|
||||||
data->callback = simple_callback_r;
|
data->callback = simple_callback_r;
|
||||||
wr.submitted = true;
|
wr.submitted = true;
|
||||||
my_uring_prep_readv(
|
my_uring_prep_readv(
|
||||||
sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + wr.sector
|
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + wr.sector
|
||||||
);
|
);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
}
|
}
|
||||||
@@ -867,11 +873,11 @@ void journal_flusher_co::update_clean_db()
|
|||||||
{
|
{
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Free block %lu from %lx:%lx v%lu (new location is %lu)\n",
|
printf("Free block %lu from %lx:%lx v%lu (new location is %lu)\n",
|
||||||
old_clean_loc >> bs->block_order,
|
old_clean_loc >> bs->dsk.block_order,
|
||||||
cur.oid.inode, cur.oid.stripe, cur.version,
|
cur.oid.inode, cur.oid.stripe, cur.version,
|
||||||
clean_loc >> bs->block_order);
|
clean_loc >> bs->dsk.block_order);
|
||||||
#endif
|
#endif
|
||||||
bs->data_alloc->set(old_clean_loc >> bs->block_order, false);
|
bs->data_alloc->set(old_clean_loc >> bs->dsk.block_order, false);
|
||||||
}
|
}
|
||||||
auto & clean_db = bs->clean_db_shard(cur.oid);
|
auto & clean_db = bs->clean_db_shard(cur.oid);
|
||||||
if (has_delete)
|
if (has_delete)
|
||||||
@@ -880,10 +886,10 @@ void journal_flusher_co::update_clean_db()
|
|||||||
clean_db.erase(clean_it);
|
clean_db.erase(clean_it);
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Free block %lu from %lx:%lx v%lu (delete)\n",
|
printf("Free block %lu from %lx:%lx v%lu (delete)\n",
|
||||||
clean_loc >> bs->block_order,
|
clean_loc >> bs->dsk.block_order,
|
||||||
cur.oid.inode, cur.oid.stripe, cur.version);
|
cur.oid.inode, cur.oid.stripe, cur.version);
|
||||||
#endif
|
#endif
|
||||||
bs->data_alloc->set(clean_loc >> bs->block_order, false);
|
bs->data_alloc->set(clean_loc >> bs->dsk.block_order, false);
|
||||||
clean_loc = UINT64_MAX;
|
clean_loc = UINT64_MAX;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -932,7 +938,7 @@ bool journal_flusher_co::fsync_batch(bool fsync_meta, int wait_base)
|
|||||||
await_sqe(0);
|
await_sqe(0);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
my_uring_prep_fsync(sqe, fsync_meta ? bs->meta_fd : bs->data_fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, fsync_meta ? bs->dsk.meta_fd : bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||||
cur_sync->state = 1;
|
cur_sync->state = 1;
|
||||||
wait_count++;
|
wait_count++;
|
||||||
resume_2:
|
resume_2:
|
||||||
|
@@ -11,25 +11,19 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_t *
|
|||||||
ring_consumer.loop = [this]() { loop(); };
|
ring_consumer.loop = [this]() { loop(); };
|
||||||
ringloop->register_consumer(&ring_consumer);
|
ringloop->register_consumer(&ring_consumer);
|
||||||
initialized = 0;
|
initialized = 0;
|
||||||
data_fd = meta_fd = journal.fd = -1;
|
|
||||||
parse_config(config);
|
parse_config(config);
|
||||||
zero_object = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, block_size);
|
zero_object = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, dsk.data_block_size);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
open_data();
|
dsk.open_data();
|
||||||
open_meta();
|
dsk.open_meta();
|
||||||
open_journal();
|
dsk.open_journal();
|
||||||
calc_lengths();
|
calc_lengths();
|
||||||
data_alloc = new allocator(block_count);
|
data_alloc = new allocator(dsk.block_count);
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
if (data_fd >= 0)
|
dsk.close_all();
|
||||||
close(data_fd);
|
|
||||||
if (meta_fd >= 0 && meta_fd != data_fd)
|
|
||||||
close(meta_fd);
|
|
||||||
if (journal.fd >= 0 && journal.fd != meta_fd)
|
|
||||||
close(journal.fd);
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
flusher = new journal_flusher_t(this);
|
flusher = new journal_flusher_t(this);
|
||||||
@@ -41,12 +35,7 @@ blockstore_impl_t::~blockstore_impl_t()
|
|||||||
delete flusher;
|
delete flusher;
|
||||||
free(zero_object);
|
free(zero_object);
|
||||||
ringloop->unregister_consumer(&ring_consumer);
|
ringloop->unregister_consumer(&ring_consumer);
|
||||||
if (data_fd >= 0)
|
dsk.close_all();
|
||||||
close(data_fd);
|
|
||||||
if (meta_fd >= 0 && meta_fd != data_fd)
|
|
||||||
close(meta_fd);
|
|
||||||
if (journal.fd >= 0 && journal.fd != meta_fd)
|
|
||||||
close(journal.fd);
|
|
||||||
if (metadata_buffer)
|
if (metadata_buffer)
|
||||||
free(metadata_buffer);
|
free(metadata_buffer);
|
||||||
if (clean_bitmap)
|
if (clean_bitmap)
|
||||||
@@ -118,7 +107,7 @@ void blockstore_impl_t::loop()
|
|||||||
// has_writes == 0 - no writes before the current queue item
|
// has_writes == 0 - no writes before the current queue item
|
||||||
// has_writes == 1 - some writes in progress
|
// has_writes == 1 - some writes in progress
|
||||||
// has_writes == 2 - tried to submit some writes, but failed
|
// has_writes == 2 - tried to submit some writes, but failed
|
||||||
int has_writes = 0, op_idx = 0, new_idx = 0, done_lists = 0;
|
int has_writes = 0, op_idx = 0, new_idx = 0;
|
||||||
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
||||||
{
|
{
|
||||||
auto op = submit_queue[op_idx];
|
auto op = submit_queue[op_idx];
|
||||||
@@ -199,16 +188,12 @@ void blockstore_impl_t::loop()
|
|||||||
else if (op->opcode == BS_OP_LIST)
|
else if (op->opcode == BS_OP_LIST)
|
||||||
{
|
{
|
||||||
// LIST doesn't have to be blocked by previous modifications
|
// LIST doesn't have to be blocked by previous modifications
|
||||||
// But don't do a lot of LISTs at once, because they're blocking and potentially slow
|
process_list(op);
|
||||||
if (single_tick_list_limit <= 0 || done_lists < single_tick_list_limit)
|
wr_st = 2;
|
||||||
{
|
|
||||||
process_list(op);
|
|
||||||
done_lists++;
|
|
||||||
wr_st = 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (wr_st == 2)
|
if (wr_st == 2)
|
||||||
{
|
{
|
||||||
|
submit_queue[op_idx] = NULL;
|
||||||
new_idx--;
|
new_idx--;
|
||||||
}
|
}
|
||||||
if (wr_st == 0)
|
if (wr_st == 0)
|
||||||
@@ -317,17 +302,6 @@ void blockstore_impl_t::check_wait(blockstore_op_t *op)
|
|||||||
// do not submit
|
// do not submit
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Still waiting for a journal buffer\n");
|
printf("Still waiting for a journal buffer\n");
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PRIV(op)->wait_for = 0;
|
|
||||||
}
|
|
||||||
else if (PRIV(op)->wait_for == WAIT_FREE)
|
|
||||||
{
|
|
||||||
if (!data_alloc->get_free_count() && flusher->is_active())
|
|
||||||
{
|
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
|
||||||
printf("Still waiting for free space on the data device\n");
|
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -343,15 +317,15 @@ void blockstore_impl_t::enqueue_op(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
if (op->opcode < BS_OP_MIN || op->opcode > BS_OP_MAX ||
|
if (op->opcode < BS_OP_MIN || op->opcode > BS_OP_MAX ||
|
||||||
((op->opcode == BS_OP_READ || op->opcode == BS_OP_WRITE || op->opcode == BS_OP_WRITE_STABLE) && (
|
((op->opcode == BS_OP_READ || op->opcode == BS_OP_WRITE || op->opcode == BS_OP_WRITE_STABLE) && (
|
||||||
op->offset >= block_size ||
|
op->offset >= dsk.data_block_size ||
|
||||||
op->len > block_size-op->offset ||
|
op->len > dsk.data_block_size-op->offset ||
|
||||||
(op->len % disk_alignment)
|
(op->len % dsk.disk_alignment)
|
||||||
)) ||
|
)) ||
|
||||||
readonly && op->opcode != BS_OP_READ && op->opcode != BS_OP_LIST)
|
readonly && op->opcode != BS_OP_READ && op->opcode != BS_OP_LIST)
|
||||||
{
|
{
|
||||||
// Basic verification not passed
|
// Basic verification not passed
|
||||||
op->retval = -EINVAL;
|
op->retval = -EINVAL;
|
||||||
std::function<void (blockstore_op_t*)>(op->callback)(op);
|
ringloop->set_immediate([op]() { std::function<void (blockstore_op_t*)>(op->callback)(op); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (op->opcode == BS_OP_SYNC_STAB_ALL)
|
if (op->opcode == BS_OP_SYNC_STAB_ALL)
|
||||||
@@ -394,7 +368,7 @@ void blockstore_impl_t::enqueue_op(blockstore_op_t *op)
|
|||||||
}
|
}
|
||||||
if ((op->opcode == BS_OP_WRITE || op->opcode == BS_OP_WRITE_STABLE || op->opcode == BS_OP_DELETE) && !enqueue_write(op))
|
if ((op->opcode == BS_OP_WRITE || op->opcode == BS_OP_WRITE_STABLE || op->opcode == BS_OP_DELETE) && !enqueue_write(op))
|
||||||
{
|
{
|
||||||
std::function<void (blockstore_op_t*)>(op->callback)(op);
|
ringloop->set_immediate([op]() { std::function<void (blockstore_op_t*)>(op->callback)(op); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Call constructor without allocating memory. We'll call destructor before returning op back
|
// Call constructor without allocating memory. We'll call destructor before returning op back
|
||||||
@@ -471,13 +445,13 @@ void blockstore_impl_t::reshard_clean_db(pool_id_t pool, uint32_t pg_count, uint
|
|||||||
|
|
||||||
void blockstore_impl_t::process_list(blockstore_op_t *op)
|
void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
uint32_t list_pg = op->offset+1;
|
uint32_t list_pg = op->pg_number+1;
|
||||||
uint32_t pg_count = op->len;
|
uint32_t pg_count = op->pg_count;
|
||||||
uint64_t pg_stripe_size = op->oid.stripe;
|
uint64_t pg_stripe_size = op->pg_alignment;
|
||||||
uint64_t min_inode = op->oid.inode;
|
uint64_t min_inode = op->min_oid.inode;
|
||||||
uint64_t max_inode = op->version;
|
uint64_t max_inode = op->max_oid.inode;
|
||||||
// Check PG
|
// Check PG
|
||||||
if (pg_count != 0 && (pg_stripe_size < MIN_BLOCK_SIZE || list_pg > pg_count))
|
if (pg_count != 0 && (pg_stripe_size < MIN_DATA_BLOCK_SIZE || list_pg > pg_count))
|
||||||
{
|
{
|
||||||
op->retval = -EINVAL;
|
op->retval = -EINVAL;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
@@ -522,7 +496,13 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
stable_alloc += clean_db.size();
|
stable_alloc += clean_db.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
if (op->list_stable_limit > 0)
|
||||||
|
{
|
||||||
|
stable_alloc = op->list_stable_limit;
|
||||||
|
if (stable_alloc > 1024*1024)
|
||||||
|
stable_alloc = 1024*1024;
|
||||||
|
}
|
||||||
|
if (stable_alloc < 32768)
|
||||||
{
|
{
|
||||||
stable_alloc = 32768;
|
stable_alloc = 32768;
|
||||||
}
|
}
|
||||||
@@ -533,22 +513,21 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto max_oid = op->max_oid;
|
||||||
|
bool limited = false;
|
||||||
for (auto shard_it = clean_db_shards.lower_bound(first_shard);
|
for (auto shard_it = clean_db_shards.lower_bound(first_shard);
|
||||||
shard_it != clean_db_shards.end() && shard_it->first <= last_shard;
|
shard_it != clean_db_shards.end() && shard_it->first <= last_shard;
|
||||||
shard_it++)
|
shard_it++)
|
||||||
{
|
{
|
||||||
auto & clean_db = shard_it->second;
|
auto & clean_db = shard_it->second;
|
||||||
auto clean_it = clean_db.begin(), clean_end = clean_db.end();
|
auto clean_it = clean_db.begin(), clean_end = clean_db.end();
|
||||||
if ((min_inode != 0 || max_inode != 0) && min_inode <= max_inode)
|
if (op->min_oid.inode != 0 || op->min_oid.stripe != 0)
|
||||||
{
|
{
|
||||||
clean_it = clean_db.lower_bound({
|
clean_it = clean_db.lower_bound(op->min_oid);
|
||||||
.inode = min_inode,
|
}
|
||||||
.stripe = 0,
|
if ((max_oid.inode != 0 || max_oid.stripe != 0) && !(max_oid < op->min_oid))
|
||||||
});
|
{
|
||||||
clean_end = clean_db.upper_bound({
|
clean_end = clean_db.upper_bound(max_oid);
|
||||||
.inode = max_inode,
|
|
||||||
.stripe = UINT64_MAX,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
for (; clean_it != clean_end; clean_it++)
|
for (; clean_it != clean_end; clean_it++)
|
||||||
{
|
{
|
||||||
@@ -567,11 +546,24 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
.oid = clean_it->first,
|
.oid = clean_it->first,
|
||||||
.version = clean_it->second.version,
|
.version = clean_it->second.version,
|
||||||
};
|
};
|
||||||
|
if (op->list_stable_limit > 0 && !limited && stable_count >= op->list_stable_limit)
|
||||||
|
{
|
||||||
|
limited = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op->list_stable_limit > 0 && first_shard != last_shard)
|
||||||
|
{
|
||||||
|
// To maintain the order, we have to include objects in the same range from other shards
|
||||||
|
std::sort(stable, stable+stable_count);
|
||||||
|
if (stable_count > op->list_stable_limit)
|
||||||
|
stable_count = op->list_stable_limit;
|
||||||
|
max_oid = stable[stable_count-1].oid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (first_shard != last_shard)
|
if (op->list_stable_limit == 0 && first_shard != last_shard)
|
||||||
{
|
{
|
||||||
// If that's not a per-PG listing, sort clean entries
|
// If that's not a per-PG listing, sort clean entries (already sorted if list_stable_limit != 0)
|
||||||
std::sort(stable, stable+stable_count);
|
std::sort(stable, stable+stable_count);
|
||||||
}
|
}
|
||||||
int clean_stable_count = stable_count;
|
int clean_stable_count = stable_count;
|
||||||
@@ -580,20 +572,17 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
obj_ver_id *unstable = NULL;
|
obj_ver_id *unstable = NULL;
|
||||||
{
|
{
|
||||||
auto dirty_it = dirty_db.begin(), dirty_end = dirty_db.end();
|
auto dirty_it = dirty_db.begin(), dirty_end = dirty_db.end();
|
||||||
if ((min_inode != 0 || max_inode != 0) && min_inode <= max_inode)
|
if (op->min_oid.inode != 0 || op->min_oid.stripe != 0)
|
||||||
{
|
{
|
||||||
dirty_it = dirty_db.lower_bound({
|
dirty_it = dirty_db.lower_bound({
|
||||||
.oid = {
|
.oid = op->min_oid,
|
||||||
.inode = min_inode,
|
|
||||||
.stripe = 0,
|
|
||||||
},
|
|
||||||
.version = 0,
|
.version = 0,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if ((max_oid.inode != 0 || max_oid.stripe != 0) && !(max_oid < op->min_oid))
|
||||||
|
{
|
||||||
dirty_end = dirty_db.upper_bound({
|
dirty_end = dirty_db.upper_bound({
|
||||||
.oid = {
|
.oid = max_oid,
|
||||||
.inode = max_inode,
|
|
||||||
.stripe = UINT64_MAX,
|
|
||||||
},
|
|
||||||
.version = UINT64_MAX,
|
.version = UINT64_MAX,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -609,7 +598,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
replace_stable(dirty_it->first.oid, 0, clean_stable_count, stable_count, stable);
|
replace_stable(dirty_it->first.oid, 0, clean_stable_count, stable_count, stable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (IS_STABLE(dirty_it->second.state))
|
else if (IS_STABLE(dirty_it->second.state) || (dirty_it->second.state & BS_ST_INSTANT))
|
||||||
{
|
{
|
||||||
// First try to replace a clean stable version in the first part of the list
|
// First try to replace a clean stable version in the first part of the list
|
||||||
if (!replace_stable(dirty_it->first.oid, dirty_it->first.version, 0, clean_stable_count, stable))
|
if (!replace_stable(dirty_it->first.oid, dirty_it->first.version, 0, clean_stable_count, stable))
|
||||||
@@ -698,3 +687,16 @@ void blockstore_impl_t::dump_diagnostics()
|
|||||||
journal.dump_diagnostics();
|
journal.dump_diagnostics();
|
||||||
flusher->dump_diagnostics();
|
flusher->dump_diagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blockstore_impl_t::disk_error_abort(const char *op, int retval, int expected)
|
||||||
|
{
|
||||||
|
if (retval == -EAGAIN)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "EAGAIN error received from a disk %s during flush."
|
||||||
|
" It must never happen with io_uring and indicates a kernel bug."
|
||||||
|
" Please upgrade your kernel. Aborting.\n", op);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Disk %s failed: result is %d, expected %d. Can't continue, sorry :-(\n", op, retval, expected);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "blockstore.h"
|
#include "blockstore.h"
|
||||||
|
#include "blockstore_disk.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "cpp-btree/btree_map.h"
|
#include "cpp-btree/btree_map.h"
|
||||||
|
|
||||||
@@ -90,13 +92,13 @@
|
|||||||
#include "blockstore_journal.h"
|
#include "blockstore_journal.h"
|
||||||
|
|
||||||
// "VITAstor"
|
// "VITAstor"
|
||||||
#define BLOCKSTORE_META_MAGIC 0x726F747341544956l
|
#define BLOCKSTORE_META_MAGIC_V1 0x726F747341544956l
|
||||||
#define BLOCKSTORE_META_VERSION 1
|
#define BLOCKSTORE_META_VERSION_V1 1
|
||||||
|
|
||||||
// metadata header (superblock)
|
// metadata header (superblock)
|
||||||
// FIXME: After adding the OSD superblock, add a key to metadata
|
// FIXME: After adding the OSD superblock, add a key to metadata
|
||||||
// and journal headers to check if they belong to the same OSD
|
// and journal headers to check if they belong to the same OSD
|
||||||
struct __attribute__((__packed__)) blockstore_meta_header_t
|
struct __attribute__((__packed__)) blockstore_meta_header_v1_t
|
||||||
{
|
{
|
||||||
uint64_t zero;
|
uint64_t zero;
|
||||||
uint64_t magic;
|
uint64_t magic;
|
||||||
@@ -158,12 +160,11 @@ struct __attribute__((__packed__)) dirty_entry
|
|||||||
#define WAIT_JOURNAL 3
|
#define WAIT_JOURNAL 3
|
||||||
// Suspend operation until the next journal sector buffer is free
|
// Suspend operation until the next journal sector buffer is free
|
||||||
#define WAIT_JOURNAL_BUFFER 4
|
#define WAIT_JOURNAL_BUFFER 4
|
||||||
// Suspend operation until there is some free space on the data device
|
|
||||||
#define WAIT_FREE 5
|
|
||||||
|
|
||||||
struct fulfill_read_t
|
struct fulfill_read_t
|
||||||
{
|
{
|
||||||
uint64_t offset, len;
|
uint64_t offset, len;
|
||||||
|
uint64_t journal_sector; // sector+1 if used and !journal.inmemory, otherwise 0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PRIV(op) ((blockstore_op_private_t*)(op)->private_data)
|
#define PRIV(op) ((blockstore_op_private_t*)(op)->private_data)
|
||||||
@@ -217,23 +218,10 @@ struct pool_shard_settings_t
|
|||||||
|
|
||||||
class blockstore_impl_t
|
class blockstore_impl_t
|
||||||
{
|
{
|
||||||
|
blockstore_disk_t dsk;
|
||||||
|
|
||||||
/******* OPTIONS *******/
|
/******* OPTIONS *******/
|
||||||
std::string data_device, meta_device, journal_device;
|
|
||||||
uint32_t block_size;
|
|
||||||
uint64_t meta_offset;
|
|
||||||
uint64_t data_offset;
|
|
||||||
uint64_t cfg_journal_size, cfg_data_size;
|
|
||||||
// Required write alignment and journal/metadata/data areas' location alignment
|
|
||||||
uint32_t disk_alignment = 4096;
|
|
||||||
// Journal block size - minimum_io_size of the journal device is the best choice
|
|
||||||
uint64_t journal_block_size = 4096;
|
|
||||||
// Metadata block size - minimum_io_size of the metadata device is the best choice
|
|
||||||
uint64_t meta_block_size = 4096;
|
|
||||||
// Sparse write tracking granularity. 4 KB is a good choice. Must be a multiple of disk_alignment
|
|
||||||
uint64_t bitmap_granularity = 4096;
|
|
||||||
bool readonly = false;
|
bool readonly = false;
|
||||||
// By default, Blockstore locks all opened devices exclusively. This option can be used to disable locking
|
|
||||||
bool disable_flock = false;
|
|
||||||
// It is safe to disable fsync() if drive write cache is writethrough
|
// It is safe to disable fsync() if drive write cache is writethrough
|
||||||
bool disable_data_fsync = false, disable_meta_fsync = false, disable_journal_fsync = false;
|
bool disable_data_fsync = false, disable_meta_fsync = false, disable_journal_fsync = false;
|
||||||
// Enable if you want every operation to be executed with an "implicit fsync"
|
// Enable if you want every operation to be executed with an "implicit fsync"
|
||||||
@@ -252,8 +240,6 @@ class blockstore_impl_t
|
|||||||
int throttle_target_parallelism = 1;
|
int throttle_target_parallelism = 1;
|
||||||
// Minimum difference in microseconds between target and real execution times to throttle the response
|
// Minimum difference in microseconds between target and real execution times to throttle the response
|
||||||
int throttle_threshold_us = 50;
|
int throttle_threshold_us = 50;
|
||||||
// Maximum number of LIST operations to be processed between
|
|
||||||
int single_tick_list_limit = 1;
|
|
||||||
/******* END OF OPTIONS *******/
|
/******* END OF OPTIONS *******/
|
||||||
|
|
||||||
struct ring_consumer_t ring_consumer;
|
struct ring_consumer_t ring_consumer;
|
||||||
@@ -268,16 +254,6 @@ class blockstore_impl_t
|
|||||||
allocator *data_alloc = NULL;
|
allocator *data_alloc = NULL;
|
||||||
uint8_t *zero_object;
|
uint8_t *zero_object;
|
||||||
|
|
||||||
uint32_t block_order;
|
|
||||||
uint64_t block_count;
|
|
||||||
uint32_t clean_entry_bitmap_size = 0, clean_entry_size = 0;
|
|
||||||
|
|
||||||
int meta_fd;
|
|
||||||
int data_fd;
|
|
||||||
uint64_t meta_size, meta_area, meta_len;
|
|
||||||
uint64_t data_size, data_len;
|
|
||||||
uint64_t data_device_sect, meta_device_sect, journal_device_sect;
|
|
||||||
|
|
||||||
void *metadata_buffer = NULL;
|
void *metadata_buffer = NULL;
|
||||||
|
|
||||||
struct journal_t journal;
|
struct journal_t journal;
|
||||||
@@ -314,6 +290,7 @@ class blockstore_impl_t
|
|||||||
// Journaling
|
// Journaling
|
||||||
void prepare_journal_sector_write(int sector, blockstore_op_t *op);
|
void prepare_journal_sector_write(int sector, blockstore_op_t *op);
|
||||||
void handle_journal_write(ring_data_t *data, uint64_t flush_id);
|
void handle_journal_write(ring_data_t *data, uint64_t flush_id);
|
||||||
|
void disk_error_abort(const char *op, int retval, int expected);
|
||||||
|
|
||||||
// Asynchronous init
|
// Asynchronous init
|
||||||
int initialized;
|
int initialized;
|
||||||
@@ -326,7 +303,7 @@ class blockstore_impl_t
|
|||||||
// Read
|
// Read
|
||||||
int dequeue_read(blockstore_op_t *read_op);
|
int dequeue_read(blockstore_op_t *read_op);
|
||||||
int fulfill_read(blockstore_op_t *read_op, uint64_t &fulfilled, uint32_t item_start, uint32_t item_end,
|
int fulfill_read(blockstore_op_t *read_op, uint64_t &fulfilled, uint32_t item_start, uint32_t item_end,
|
||||||
uint32_t item_state, uint64_t item_version, uint64_t item_location);
|
uint32_t item_state, uint64_t item_version, uint64_t item_location, uint64_t journal_sector);
|
||||||
int fulfill_read_push(blockstore_op_t *op, void *buf, uint64_t offset, uint64_t len,
|
int fulfill_read_push(blockstore_op_t *op, void *buf, uint64_t offset, uint64_t len,
|
||||||
uint32_t item_state, uint64_t item_version);
|
uint32_t item_state, uint64_t item_version);
|
||||||
void handle_read_event(ring_data_t *data, blockstore_op_t *op);
|
void handle_read_event(ring_data_t *data, blockstore_op_t *op);
|
||||||
@@ -394,9 +371,9 @@ public:
|
|||||||
// Print diagnostics to stdout
|
// Print diagnostics to stdout
|
||||||
void dump_diagnostics();
|
void dump_diagnostics();
|
||||||
|
|
||||||
inline uint32_t get_block_size() { return block_size; }
|
inline uint32_t get_block_size() { return dsk.data_block_size; }
|
||||||
inline uint64_t get_block_count() { return block_count; }
|
inline uint64_t get_block_count() { return dsk.block_count; }
|
||||||
inline uint64_t get_free_block_count() { return data_alloc->get_free_count(); }
|
inline uint64_t get_free_block_count() { return data_alloc->get_free_count(); }
|
||||||
inline uint32_t get_bitmap_granularity() { return disk_alignment; }
|
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
|
||||||
inline uint64_t get_journal_size() { return journal.len; }
|
inline uint64_t get_journal_size() { return dsk.journal_len; }
|
||||||
};
|
};
|
||||||
|
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
#include "blockstore_impl.h"
|
#include "blockstore_impl.h"
|
||||||
|
|
||||||
|
#define INIT_META_EMPTY 0
|
||||||
|
#define INIT_META_READING 1
|
||||||
|
#define INIT_META_READ_DONE 2
|
||||||
|
#define INIT_META_WRITING 3
|
||||||
|
|
||||||
#define GET_SQE() \
|
#define GET_SQE() \
|
||||||
sqe = bs->get_sqe();\
|
sqe = bs->get_sqe();\
|
||||||
if (!sqe)\
|
if (!sqe)\
|
||||||
@@ -22,32 +27,33 @@ blockstore_init_meta::blockstore_init_meta(blockstore_impl_t *bs)
|
|||||||
this->bs = bs;
|
this->bs = bs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_init_meta::handle_event(ring_data_t *data)
|
void blockstore_init_meta::handle_event(ring_data_t *data, int buf_num)
|
||||||
{
|
{
|
||||||
if (data->res < 0)
|
if (data->res < 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string("read metadata failed at offset ") + std::to_string(metadata_read) +
|
std::string("read metadata failed at offset ") + std::to_string(bufs[buf_num].offset) +
|
||||||
std::string(": ") + strerror(-data->res)
|
std::string(": ") + strerror(-data->res)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
prev_done = data->res > 0 ? submitted : 0;
|
if (buf_num >= 0)
|
||||||
done_len = data->res;
|
{
|
||||||
done_pos = metadata_read;
|
bufs[buf_num].state = (bufs[buf_num].state == INIT_META_READING
|
||||||
metadata_read += data->res;
|
? INIT_META_READ_DONE
|
||||||
submitted = 0;
|
: INIT_META_EMPTY);
|
||||||
|
}
|
||||||
|
submitted--;
|
||||||
|
bs->ringloop->wakeup();
|
||||||
}
|
}
|
||||||
|
|
||||||
int blockstore_init_meta::loop()
|
int blockstore_init_meta::loop()
|
||||||
{
|
{
|
||||||
if (wait_state == 1)
|
if (wait_state == 1) goto resume_1;
|
||||||
goto resume_1;
|
else if (wait_state == 2) goto resume_2;
|
||||||
else if (wait_state == 2)
|
else if (wait_state == 3) goto resume_3;
|
||||||
goto resume_2;
|
else if (wait_state == 4) goto resume_4;
|
||||||
else if (wait_state == 3)
|
else if (wait_state == 5) goto resume_5;
|
||||||
goto resume_3;
|
else if (wait_state == 6) goto resume_6;
|
||||||
else if (wait_state == 4)
|
|
||||||
goto resume_4;
|
|
||||||
printf("Reading blockstore metadata\n");
|
printf("Reading blockstore metadata\n");
|
||||||
if (bs->inmemory_meta)
|
if (bs->inmemory_meta)
|
||||||
metadata_buffer = bs->metadata_buffer;
|
metadata_buffer = bs->metadata_buffer;
|
||||||
@@ -57,27 +63,27 @@ int blockstore_init_meta::loop()
|
|||||||
throw std::runtime_error("Failed to allocate metadata read buffer");
|
throw std::runtime_error("Failed to allocate metadata read buffer");
|
||||||
// Read superblock
|
// Read superblock
|
||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = { metadata_buffer, bs->meta_block_size };
|
data->iov = { metadata_buffer, bs->dsk.meta_block_size };
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||||
my_uring_prep_readv(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset);
|
my_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
submitted = 1;
|
submitted++;
|
||||||
resume_1:
|
resume_1:
|
||||||
if (submitted)
|
if (submitted > 0)
|
||||||
{
|
{
|
||||||
wait_state = 1;
|
wait_state = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (iszero((uint64_t*)metadata_buffer, bs->meta_block_size / sizeof(uint64_t)))
|
if (iszero((uint64_t*)metadata_buffer, bs->dsk.meta_block_size / sizeof(uint64_t)))
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
blockstore_meta_header_t *hdr = (blockstore_meta_header_t *)metadata_buffer;
|
blockstore_meta_header_v1_t *hdr = (blockstore_meta_header_v1_t *)metadata_buffer;
|
||||||
hdr->zero = 0;
|
hdr->zero = 0;
|
||||||
hdr->magic = BLOCKSTORE_META_MAGIC;
|
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
||||||
hdr->version = BLOCKSTORE_META_VERSION;
|
hdr->version = BLOCKSTORE_META_VERSION_V1;
|
||||||
hdr->meta_block_size = bs->meta_block_size;
|
hdr->meta_block_size = bs->dsk.meta_block_size;
|
||||||
hdr->data_block_size = bs->block_size;
|
hdr->data_block_size = bs->dsk.data_block_size;
|
||||||
hdr->bitmap_granularity = bs->bitmap_granularity;
|
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
|
||||||
}
|
}
|
||||||
if (bs->readonly)
|
if (bs->readonly)
|
||||||
{
|
{
|
||||||
@@ -87,11 +93,11 @@ resume_1:
|
|||||||
{
|
{
|
||||||
printf("Initializing metadata area\n");
|
printf("Initializing metadata area\n");
|
||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = (struct iovec){ metadata_buffer, bs->meta_block_size };
|
data->iov = (struct iovec){ metadata_buffer, bs->dsk.meta_block_size };
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||||
my_uring_prep_writev(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset);
|
my_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
submitted = 1;
|
submitted++;
|
||||||
resume_3:
|
resume_3:
|
||||||
if (submitted > 0)
|
if (submitted > 0)
|
||||||
{
|
{
|
||||||
@@ -103,10 +109,10 @@ resume_1:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blockstore_meta_header_t *hdr = (blockstore_meta_header_t *)metadata_buffer;
|
blockstore_meta_header_v1_t *hdr = (blockstore_meta_header_v1_t *)metadata_buffer;
|
||||||
if (hdr->zero != 0 ||
|
if (hdr->zero != 0 ||
|
||||||
hdr->magic != BLOCKSTORE_META_MAGIC ||
|
hdr->magic != BLOCKSTORE_META_MAGIC_V1 ||
|
||||||
hdr->version != BLOCKSTORE_META_VERSION)
|
hdr->version != BLOCKSTORE_META_VERSION_V1)
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Metadata is corrupt or old version.\n"
|
"Metadata is corrupt or old version.\n"
|
||||||
@@ -115,80 +121,132 @@ resume_1:
|
|||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (hdr->meta_block_size != bs->meta_block_size ||
|
if (hdr->meta_block_size != bs->dsk.meta_block_size ||
|
||||||
hdr->data_block_size != bs->block_size ||
|
hdr->data_block_size != bs->dsk.data_block_size ||
|
||||||
hdr->bitmap_granularity != bs->bitmap_granularity)
|
hdr->bitmap_granularity != bs->dsk.bitmap_granularity)
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
"Configuration stored in metadata superblock"
|
"Configuration stored in metadata superblock"
|
||||||
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u)"
|
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u)"
|
||||||
" differs from OSD configuration (%lu/%u/%lu).\n",
|
" differs from OSD configuration (%lu/%u/%lu).\n",
|
||||||
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity,
|
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity,
|
||||||
bs->meta_block_size, bs->block_size, bs->bitmap_granularity
|
bs->dsk.meta_block_size, bs->dsk.data_block_size, bs->dsk.bitmap_granularity
|
||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Skip superblock
|
// Skip superblock
|
||||||
bs->meta_offset += bs->meta_block_size;
|
md_offset = bs->dsk.meta_block_size;
|
||||||
bs->meta_len -= bs->meta_block_size;
|
next_offset = md_offset;
|
||||||
prev_done = 0;
|
entries_per_block = bs->dsk.meta_block_size / bs->dsk.clean_entry_size;
|
||||||
done_len = 0;
|
|
||||||
done_pos = 0;
|
|
||||||
metadata_read = 0;
|
|
||||||
// Read the rest of the metadata
|
// Read the rest of the metadata
|
||||||
while (1)
|
resume_2:
|
||||||
|
if (next_offset < bs->dsk.meta_len && submitted == 0)
|
||||||
{
|
{
|
||||||
resume_2:
|
// Submit one read
|
||||||
if (submitted)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
wait_state = 2;
|
if (!bufs[i].state)
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (metadata_read < bs->meta_len)
|
|
||||||
{
|
|
||||||
GET_SQE();
|
|
||||||
data->iov = {
|
|
||||||
(uint8_t*)metadata_buffer + (bs->inmemory_meta
|
|
||||||
? metadata_read
|
|
||||||
: (prev == 1 ? bs->metadata_buf_size : 0)),
|
|
||||||
bs->meta_len - metadata_read > bs->metadata_buf_size ? bs->metadata_buf_size : bs->meta_len - metadata_read,
|
|
||||||
};
|
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
|
||||||
if (!zero_on_init)
|
|
||||||
my_uring_prep_readv(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + metadata_read);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Fill metadata with zeroes
|
bufs[i].buf = (uint8_t*)metadata_buffer + (bs->inmemory_meta
|
||||||
memset(data->iov.iov_base, 0, data->iov.iov_len);
|
? next_offset-md_offset
|
||||||
my_uring_prep_writev(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + metadata_read);
|
: i*bs->metadata_buf_size);
|
||||||
|
bufs[i].offset = next_offset;
|
||||||
|
bufs[i].size = bs->dsk.meta_len-next_offset > bs->metadata_buf_size
|
||||||
|
? bs->metadata_buf_size : bs->dsk.meta_len-next_offset;
|
||||||
|
bufs[i].state = INIT_META_READING;
|
||||||
|
submitted++;
|
||||||
|
next_offset += bufs[i].size;
|
||||||
|
GET_SQE();
|
||||||
|
data->iov = { bufs[i].buf, bufs[i].size };
|
||||||
|
data->callback = [this, i](ring_data_t *data) { handle_event(data, i); };
|
||||||
|
if (!zero_on_init)
|
||||||
|
my_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fill metadata with zeroes
|
||||||
|
memset(data->iov.iov_base, 0, data->iov.iov_len);
|
||||||
|
my_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
|
||||||
|
}
|
||||||
|
bs->ringloop->submit();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
bs->ringloop->submit();
|
|
||||||
submitted = (prev == 1 ? 2 : 1);
|
|
||||||
prev = submitted;
|
|
||||||
}
|
|
||||||
if (prev_done)
|
|
||||||
{
|
|
||||||
void *done_buf = bs->inmemory_meta
|
|
||||||
? ((uint8_t*)metadata_buffer + done_pos)
|
|
||||||
: ((uint8_t*)metadata_buffer + (prev_done == 2 ? bs->metadata_buf_size : 0));
|
|
||||||
unsigned count = bs->meta_block_size / bs->clean_entry_size;
|
|
||||||
for (int sector = 0; sector < done_len; sector += bs->meta_block_size)
|
|
||||||
{
|
|
||||||
// handle <count> entries
|
|
||||||
handle_entries((uint8_t*)done_buf + sector, count, bs->block_order);
|
|
||||||
done_cnt += count;
|
|
||||||
}
|
|
||||||
prev_done = 0;
|
|
||||||
done_len = 0;
|
|
||||||
}
|
|
||||||
if (!submitted)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (bufs[i].state == INIT_META_READ_DONE)
|
||||||
|
{
|
||||||
|
// Handle result
|
||||||
|
bool changed = false;
|
||||||
|
for (uint64_t sector = 0; sector < bufs[i].size; sector += bs->dsk.meta_block_size)
|
||||||
|
{
|
||||||
|
// handle <count> entries
|
||||||
|
if (handle_meta_block(bufs[i].buf + sector, entries_per_block,
|
||||||
|
((bufs[i].offset + sector - md_offset) / bs->dsk.meta_block_size) * entries_per_block))
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed && !bs->inmemory_meta && !bs->readonly)
|
||||||
|
{
|
||||||
|
// write the modified buffer back
|
||||||
|
GET_SQE();
|
||||||
|
data->iov = { bufs[i].buf, bufs[i].size };
|
||||||
|
data->callback = [this, i](ring_data_t *data) { handle_event(data, i); };
|
||||||
|
my_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
|
||||||
|
bufs[i].state = INIT_META_WRITING;
|
||||||
|
submitted++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bufs[i].state = 0;
|
||||||
|
}
|
||||||
|
bs->ringloop->wakeup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (submitted > 0)
|
||||||
|
{
|
||||||
|
wait_state = 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (entries_to_zero.size() && !bs->inmemory_meta && !bs->readonly)
|
||||||
|
{
|
||||||
|
// we have to zero out additional entries
|
||||||
|
for (i = 0; i < entries_to_zero.size(); )
|
||||||
|
{
|
||||||
|
next_offset = entries_to_zero[i]/entries_per_block;
|
||||||
|
for (j = i; j < entries_to_zero.size() && entries_to_zero[j]/entries_per_block == next_offset; j++) {}
|
||||||
|
GET_SQE();
|
||||||
|
data->iov = { metadata_buffer, bs->dsk.meta_block_size };
|
||||||
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||||
|
my_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + (1+next_offset)*bs->dsk.meta_block_size);
|
||||||
|
submitted++;
|
||||||
|
resume_5:
|
||||||
|
if (submitted > 0)
|
||||||
|
{
|
||||||
|
wait_state = 5;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (; i < j; i++)
|
||||||
|
{
|
||||||
|
uint64_t pos = (entries_to_zero[i] % entries_per_block);
|
||||||
|
memset((uint8_t*)metadata_buffer + pos*bs->dsk.clean_entry_size, 0, bs->dsk.clean_entry_size);
|
||||||
|
}
|
||||||
|
GET_SQE();
|
||||||
|
data->iov = { metadata_buffer, bs->dsk.meta_block_size };
|
||||||
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||||
|
my_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + (1+next_offset)*bs->dsk.meta_block_size);
|
||||||
|
submitted++;
|
||||||
|
resume_6:
|
||||||
|
if (submitted > 0)
|
||||||
|
{
|
||||||
|
wait_state = 6;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries_to_zero.clear();
|
||||||
|
}
|
||||||
// metadata read finished
|
// metadata read finished
|
||||||
printf("Metadata entries loaded: %lu, free blocks: %lu / %lu\n", entries_loaded, bs->data_alloc->get_free_count(), bs->block_count);
|
printf("Metadata entries loaded: %lu, free blocks: %lu / %lu\n", entries_loaded, bs->data_alloc->get_free_count(), bs->dsk.block_count);
|
||||||
if (!bs->inmemory_meta)
|
if (!bs->inmemory_meta)
|
||||||
{
|
{
|
||||||
free(metadata_buffer);
|
free(metadata_buffer);
|
||||||
@@ -197,10 +255,10 @@ resume_1:
|
|||||||
if (zero_on_init && !bs->disable_meta_fsync)
|
if (zero_on_init && !bs->disable_meta_fsync)
|
||||||
{
|
{
|
||||||
GET_SQE();
|
GET_SQE();
|
||||||
my_uring_prep_fsync(sqe, bs->meta_fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||||
submitted = 1;
|
submitted++;
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
resume_4:
|
resume_4:
|
||||||
if (submitted > 0)
|
if (submitted > 0)
|
||||||
@@ -212,14 +270,18 @@ resume_1:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_init_meta::handle_entries(void* entries, unsigned count, int block_order)
|
bool blockstore_init_meta::handle_meta_block(uint8_t *buf, uint64_t entries_per_block, uint64_t done_cnt)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < count; i++)
|
bool updated = false;
|
||||||
|
uint64_t max_i = entries_per_block;
|
||||||
|
if (max_i > bs->dsk.block_count-done_cnt)
|
||||||
|
max_i = bs->dsk.block_count-done_cnt;
|
||||||
|
for (uint64_t i = 0; i < max_i; i++)
|
||||||
{
|
{
|
||||||
clean_disk_entry *entry = (clean_disk_entry*)((uint8_t*)entries + i*bs->clean_entry_size);
|
clean_disk_entry *entry = (clean_disk_entry*)(buf + i*bs->dsk.clean_entry_size);
|
||||||
if (!bs->inmemory_meta && bs->clean_entry_bitmap_size)
|
if (!bs->inmemory_meta && bs->dsk.clean_entry_bitmap_size)
|
||||||
{
|
{
|
||||||
memcpy(bs->clean_bitmap + (done_cnt+i)*2*bs->clean_entry_bitmap_size, &entry->bitmap, 2*bs->clean_entry_bitmap_size);
|
memcpy(bs->clean_bitmap + (done_cnt+i)*2*bs->dsk.clean_entry_bitmap_size, &entry->bitmap, 2*bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
if (entry->oid.inode > 0)
|
if (entry->oid.inode > 0)
|
||||||
{
|
{
|
||||||
@@ -230,17 +292,39 @@ void blockstore_init_meta::handle_entries(void* entries, unsigned count, int blo
|
|||||||
if (clean_it != clean_db.end())
|
if (clean_it != clean_db.end())
|
||||||
{
|
{
|
||||||
// free the previous block
|
// free the previous block
|
||||||
|
// here we have to zero out the previous entry because otherwise we'll hit
|
||||||
|
// "tried to overwrite non-zero metadata entry" later
|
||||||
|
uint64_t old_clean_loc = clean_it->second.location >> bs->dsk.block_order;
|
||||||
|
if (bs->inmemory_meta)
|
||||||
|
{
|
||||||
|
uint64_t sector = (old_clean_loc / entries_per_block) * bs->dsk.meta_block_size;
|
||||||
|
uint64_t pos = (old_clean_loc % entries_per_block);
|
||||||
|
clean_disk_entry *old_entry = (clean_disk_entry*)((uint8_t*)bs->metadata_buffer + sector + pos*bs->dsk.clean_entry_size);
|
||||||
|
memset(old_entry, 0, bs->dsk.clean_entry_size);
|
||||||
|
}
|
||||||
|
else if (old_clean_loc >= done_cnt)
|
||||||
|
{
|
||||||
|
updated = true;
|
||||||
|
uint64_t sector = ((old_clean_loc - done_cnt) / entries_per_block) * bs->dsk.meta_block_size;
|
||||||
|
uint64_t pos = (old_clean_loc % entries_per_block);
|
||||||
|
clean_disk_entry *old_entry = (clean_disk_entry*)(buf + sector + pos*bs->dsk.clean_entry_size);
|
||||||
|
memset(old_entry, 0, bs->dsk.clean_entry_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entries_to_zero.push_back(clean_it->second.location >> bs->dsk.block_order);
|
||||||
|
}
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Free block %lu from %lx:%lx v%lu (new location is %lu)\n",
|
printf("Free block %lu from %lx:%lx v%lu (new location is %lu)\n",
|
||||||
clean_it->second.location >> block_order,
|
old_clean_loc,
|
||||||
clean_it->first.inode, clean_it->first.stripe, clean_it->second.version,
|
clean_it->first.inode, clean_it->first.stripe, clean_it->second.version,
|
||||||
done_cnt+i);
|
done_cnt+i);
|
||||||
#endif
|
#endif
|
||||||
bs->data_alloc->set(clean_it->second.location >> block_order, false);
|
bs->data_alloc->set(old_clean_loc, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bs->inode_space_stats[entry->oid.inode] += bs->block_size;
|
bs->inode_space_stats[entry->oid.inode] += bs->dsk.data_block_size;
|
||||||
}
|
}
|
||||||
entries_loaded++;
|
entries_loaded++;
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
@@ -249,17 +333,21 @@ void blockstore_init_meta::handle_entries(void* entries, unsigned count, int blo
|
|||||||
bs->data_alloc->set(done_cnt+i, true);
|
bs->data_alloc->set(done_cnt+i, true);
|
||||||
clean_db[entry->oid] = (struct clean_entry){
|
clean_db[entry->oid] = (struct clean_entry){
|
||||||
.version = entry->version,
|
.version = entry->version,
|
||||||
.location = (done_cnt+i) << block_order,
|
.location = (done_cnt+i) << bs->dsk.block_order,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// here we also have to zero out the entry
|
||||||
|
updated = true;
|
||||||
|
memset(entry, 0, bs->dsk.clean_entry_size);
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Old clean entry %lu: %lx:%lx v%lu\n", done_cnt+i, entry->oid.inode, entry->oid.stripe, entry->version);
|
printf("Old clean entry %lu: %lx:%lx v%lu\n", done_cnt+i, entry->oid.inode, entry->oid.stripe, entry->version);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockstore_init_journal::blockstore_init_journal(blockstore_impl_t *bs)
|
blockstore_init_journal::blockstore_init_journal(blockstore_impl_t *bs)
|
||||||
@@ -328,7 +416,7 @@ int blockstore_init_journal::loop()
|
|||||||
data = ((ring_data_t*)sqe->user_data);
|
data = ((ring_data_t*)sqe->user_data);
|
||||||
data->iov = { submitted_buf, bs->journal.block_size };
|
data->iov = { submitted_buf, bs->journal.block_size };
|
||||||
data->callback = simple_callback;
|
data->callback = simple_callback;
|
||||||
my_uring_prep_readv(sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset);
|
my_uring_prep_readv(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset);
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
wait_count = 1;
|
wait_count = 1;
|
||||||
resume_1:
|
resume_1:
|
||||||
@@ -367,7 +455,7 @@ resume_1:
|
|||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = (struct iovec){ submitted_buf, 2*bs->journal.block_size };
|
data->iov = (struct iovec){ submitted_buf, 2*bs->journal.block_size };
|
||||||
data->callback = simple_callback;
|
data->callback = simple_callback;
|
||||||
my_uring_prep_writev(sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset);
|
my_uring_prep_writev(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
resume_6:
|
resume_6:
|
||||||
@@ -379,7 +467,7 @@ resume_1:
|
|||||||
if (!bs->disable_journal_fsync)
|
if (!bs->disable_journal_fsync)
|
||||||
{
|
{
|
||||||
GET_SQE();
|
GET_SQE();
|
||||||
my_uring_prep_fsync(sqe, bs->journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, bs->dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = simple_callback;
|
data->callback = simple_callback;
|
||||||
wait_count++;
|
wait_count++;
|
||||||
@@ -448,7 +536,7 @@ resume_1:
|
|||||||
end - journal_pos < JOURNAL_BUFFER_SIZE ? end - journal_pos : JOURNAL_BUFFER_SIZE,
|
end - journal_pos < JOURNAL_BUFFER_SIZE ? end - journal_pos : JOURNAL_BUFFER_SIZE,
|
||||||
};
|
};
|
||||||
data->callback = [this](ring_data_t *data1) { handle_event(data1); };
|
data->callback = [this](ring_data_t *data1) { handle_event(data1); };
|
||||||
my_uring_prep_readv(sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset + journal_pos);
|
my_uring_prep_readv(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset + journal_pos);
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
}
|
}
|
||||||
while (done.size() > 0)
|
while (done.size() > 0)
|
||||||
@@ -463,7 +551,7 @@ resume_1:
|
|||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = { init_write_buf, bs->journal.block_size };
|
data->iov = { init_write_buf, bs->journal.block_size };
|
||||||
data->callback = simple_callback;
|
data->callback = simple_callback;
|
||||||
my_uring_prep_writev(sqe, bs->journal.fd, &data->iov, 1, bs->journal.offset + init_write_sector);
|
my_uring_prep_writev(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->journal.offset + init_write_sector);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
resume_7:
|
resume_7:
|
||||||
@@ -477,7 +565,7 @@ resume_1:
|
|||||||
GET_SQE();
|
GET_SQE();
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = simple_callback;
|
data->callback = simple_callback;
|
||||||
my_uring_prep_fsync(sqe, bs->journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, bs->dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
}
|
}
|
||||||
@@ -544,7 +632,7 @@ resume_1:
|
|||||||
? bs->journal.len-bs->journal.block_size - (bs->journal.next_free-bs->journal.used_start)
|
? bs->journal.len-bs->journal.block_size - (bs->journal.next_free-bs->journal.used_start)
|
||||||
: bs->journal.used_start - bs->journal.next_free),
|
: bs->journal.used_start - bs->journal.next_free),
|
||||||
bs->journal.used_start, bs->journal.next_free,
|
bs->journal.used_start, bs->journal.next_free,
|
||||||
bs->data_alloc->get_free_count(), bs->block_count
|
bs->data_alloc->get_free_count(), bs->dsk.block_count
|
||||||
);
|
);
|
||||||
bs->journal.crc32_last = crc32_last;
|
bs->journal.crc32_last = crc32_last;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -669,9 +757,9 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
};
|
};
|
||||||
void *bmp = NULL;
|
void *bmp = NULL;
|
||||||
void *bmp_from = (uint8_t*)je + sizeof(journal_entry_small_write);
|
void *bmp_from = (uint8_t*)je + sizeof(journal_entry_small_write);
|
||||||
if (bs->clean_entry_bitmap_size <= sizeof(void*))
|
if (bs->dsk.clean_entry_bitmap_size <= sizeof(void*))
|
||||||
{
|
{
|
||||||
memcpy(&bmp, bmp_from, bs->clean_entry_bitmap_size);
|
memcpy(&bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -679,8 +767,8 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
// allocations for entry bitmaps. This can only be fixed by using
|
// allocations for entry bitmaps. This can only be fixed by using
|
||||||
// a patched map with dynamic entry size, but not the btree_map,
|
// a patched map with dynamic entry size, but not the btree_map,
|
||||||
// because it doesn't keep iterators valid all the time.
|
// because it doesn't keep iterators valid all the time.
|
||||||
bmp = malloc_or_die(bs->clean_entry_bitmap_size);
|
bmp = malloc_or_die(bs->dsk.clean_entry_bitmap_size);
|
||||||
memcpy(bmp, bmp_from, bs->clean_entry_bitmap_size);
|
memcpy(bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
bs->dirty_db.emplace(ov, (dirty_entry){
|
bs->dirty_db.emplace(ov, (dirty_entry){
|
||||||
.state = (BS_ST_SMALL_WRITE | BS_ST_SYNCED),
|
.state = (BS_ST_SMALL_WRITE | BS_ST_SYNCED),
|
||||||
@@ -712,7 +800,7 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
printf(
|
printf(
|
||||||
"je_big_write%s oid=%lx:%lx ver=%lu loc=%lu\n",
|
"je_big_write%s oid=%lx:%lx ver=%lu loc=%lu\n",
|
||||||
je->type == JE_BIG_WRITE_INSTANT ? "_instant" : "",
|
je->type == JE_BIG_WRITE_INSTANT ? "_instant" : "",
|
||||||
je->big_write.oid.inode, je->big_write.oid.stripe, je->big_write.version, je->big_write.location >> bs->block_order
|
je->big_write.oid.inode, je->big_write.oid.stripe, je->big_write.version, je->big_write.location >> bs->dsk.block_order
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
auto dirty_it = bs->dirty_db.upper_bound((obj_ver_id){
|
auto dirty_it = bs->dirty_db.upper_bound((obj_ver_id){
|
||||||
@@ -750,9 +838,9 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
};
|
};
|
||||||
void *bmp = NULL;
|
void *bmp = NULL;
|
||||||
void *bmp_from = (uint8_t*)je + sizeof(journal_entry_big_write);
|
void *bmp_from = (uint8_t*)je + sizeof(journal_entry_big_write);
|
||||||
if (bs->clean_entry_bitmap_size <= sizeof(void*))
|
if (bs->dsk.clean_entry_bitmap_size <= sizeof(void*))
|
||||||
{
|
{
|
||||||
memcpy(&bmp, bmp_from, bs->clean_entry_bitmap_size);
|
memcpy(&bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -760,8 +848,8 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
// allocations for entry bitmaps. This can only be fixed by using
|
// allocations for entry bitmaps. This can only be fixed by using
|
||||||
// a patched map with dynamic entry size, but not the btree_map,
|
// a patched map with dynamic entry size, but not the btree_map,
|
||||||
// because it doesn't keep iterators valid all the time.
|
// because it doesn't keep iterators valid all the time.
|
||||||
bmp = malloc_or_die(bs->clean_entry_bitmap_size);
|
bmp = malloc_or_die(bs->dsk.clean_entry_bitmap_size);
|
||||||
memcpy(bmp, bmp_from, bs->clean_entry_bitmap_size);
|
memcpy(bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
auto dirty_it = bs->dirty_db.emplace(ov, (dirty_entry){
|
auto dirty_it = bs->dirty_db.emplace(ov, (dirty_entry){
|
||||||
.state = (BS_ST_BIG_WRITE | BS_ST_SYNCED),
|
.state = (BS_ST_BIG_WRITE | BS_ST_SYNCED),
|
||||||
@@ -772,7 +860,7 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
.journal_sector = proc_pos,
|
.journal_sector = proc_pos,
|
||||||
.bitmap = bmp,
|
.bitmap = bmp,
|
||||||
}).first;
|
}).first;
|
||||||
if (bs->data_alloc->get(je->big_write.location >> bs->block_order))
|
if (bs->data_alloc->get(je->big_write.location >> bs->dsk.block_order))
|
||||||
{
|
{
|
||||||
// This is probably a big_write that's already flushed and freed, but it may
|
// This is probably a big_write that's already flushed and freed, but it may
|
||||||
// also indicate a bug. So we remember such entries and recheck them afterwards.
|
// also indicate a bug. So we remember such entries and recheck them afterwards.
|
||||||
@@ -785,11 +873,11 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
|||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf(
|
printf(
|
||||||
"Allocate block (journal) %lu: %lx:%lx v%lu\n",
|
"Allocate block (journal) %lu: %lx:%lx v%lu\n",
|
||||||
je->big_write.location >> bs->block_order,
|
je->big_write.location >> bs->dsk.block_order,
|
||||||
ov.oid.inode, ov.oid.stripe, ov.version
|
ov.oid.inode, ov.oid.stripe, ov.version
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
bs->data_alloc->set(je->big_write.location >> bs->block_order, true);
|
bs->data_alloc->set(je->big_write.location >> bs->dsk.block_order, true);
|
||||||
}
|
}
|
||||||
bs->journal.used_sectors[proc_pos]++;
|
bs->journal.used_sectors[proc_pos]++;
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
@@ -912,7 +1000,11 @@ void blockstore_init_journal::erase_dirty_object(blockstore_dirty_db_t::iterator
|
|||||||
? clean_it->second.location : UINT64_MAX;
|
? clean_it->second.location : UINT64_MAX;
|
||||||
if (exists && clean_loc == UINT64_MAX)
|
if (exists && clean_loc == UINT64_MAX)
|
||||||
{
|
{
|
||||||
bs->inode_space_stats[oid.inode] -= bs->block_size;
|
auto & sp = bs->inode_space_stats[oid.inode];
|
||||||
|
if (sp > bs->dsk.data_block_size)
|
||||||
|
sp -= bs->dsk.data_block_size;
|
||||||
|
else
|
||||||
|
bs->inode_space_stats.erase(oid.inode);
|
||||||
}
|
}
|
||||||
bs->erase_dirty(dirty_it, dirty_end, clean_loc);
|
bs->erase_dirty(dirty_it, dirty_end, clean_loc);
|
||||||
// Remove it from the flusher's queue, too
|
// Remove it from the flusher's queue, too
|
||||||
|
@@ -3,20 +3,32 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
struct blockstore_init_meta_buf
|
||||||
|
{
|
||||||
|
uint8_t *buf = NULL;
|
||||||
|
uint64_t size = 0;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
int state = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class blockstore_init_meta
|
class blockstore_init_meta
|
||||||
{
|
{
|
||||||
blockstore_impl_t *bs;
|
blockstore_impl_t *bs;
|
||||||
int wait_state = 0;
|
int wait_state = 0;
|
||||||
bool zero_on_init = false;
|
bool zero_on_init = false;
|
||||||
void *metadata_buffer = NULL;
|
void *metadata_buffer = NULL;
|
||||||
uint64_t metadata_read = 0;
|
blockstore_init_meta_buf bufs[2] = {};
|
||||||
int prev = 0, prev_done = 0, done_len = 0, submitted = 0;
|
int submitted = 0;
|
||||||
uint64_t done_cnt = 0, done_pos = 0;
|
|
||||||
uint64_t entries_loaded = 0;
|
|
||||||
struct io_uring_sqe *sqe;
|
struct io_uring_sqe *sqe;
|
||||||
struct ring_data_t *data;
|
struct ring_data_t *data;
|
||||||
void handle_entries(void *entries, unsigned count, int block_order);
|
uint64_t md_offset = 0;
|
||||||
void handle_event(ring_data_t *data);
|
uint64_t next_offset = 0;
|
||||||
|
uint64_t entries_loaded = 0;
|
||||||
|
unsigned entries_per_block = 0;
|
||||||
|
int i = 0, j = 0;
|
||||||
|
std::vector<uint64_t> entries_to_zero;
|
||||||
|
bool handle_meta_block(uint8_t *buf, uint64_t count, uint64_t done_cnt);
|
||||||
|
void handle_event(ring_data_t *data, int buf_num);
|
||||||
public:
|
public:
|
||||||
blockstore_init_meta(blockstore_impl_t *bs);
|
blockstore_init_meta(blockstore_impl_t *bs);
|
||||||
int loop();
|
int loop();
|
||||||
|
@@ -175,7 +175,7 @@ void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_
|
|||||||
};
|
};
|
||||||
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe, journal.fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
journal.sector_info[cur_sector].dirty = false;
|
journal.sector_info[cur_sector].dirty = false;
|
||||||
@@ -198,10 +198,7 @@ void blockstore_impl_t::handle_journal_write(ring_data_t *data, uint64_t flush_i
|
|||||||
if (data->res != data->iov.iov_len)
|
if (data->res != data->iov.iov_len)
|
||||||
{
|
{
|
||||||
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
||||||
throw std::runtime_error(
|
disk_error_abort("journal write", data->res, data->iov.iov_len);
|
||||||
"journal write failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
auto fl_it = journal.flushing_ops.upper_bound((pending_journaling_t){ .flush_id = flush_id });
|
auto fl_it = journal.flushing_ops.upper_bound((pending_journaling_t){ .flush_id = flush_id });
|
||||||
if (fl_it != journal.flushing_ops.end() && fl_it->flush_id == flush_id)
|
if (fl_it != journal.flushing_ops.end() && fl_it->flush_id == flush_id)
|
||||||
|
@@ -10,11 +10,13 @@
|
|||||||
#define JOURNAL_MAGIC 0x4A33
|
#define JOURNAL_MAGIC 0x4A33
|
||||||
#define JOURNAL_VERSION 1
|
#define JOURNAL_VERSION 1
|
||||||
#define JOURNAL_BUFFER_SIZE 4*1024*1024
|
#define JOURNAL_BUFFER_SIZE 4*1024*1024
|
||||||
|
#define JOURNAL_ENTRY_HEADER_SIZE 16
|
||||||
|
|
||||||
// We reserve some extra space for future stabilize requests during writes
|
// We reserve some extra space for future stabilize requests during writes
|
||||||
// FIXME: This value should be dynamic i.e. Blockstore ideally shouldn't allow
|
// FIXME: This value should be dynamic i.e. Blockstore ideally shouldn't allow
|
||||||
// writing more than can be stabilized afterwards
|
// writing more than can be stabilized afterwards
|
||||||
#define JOURNAL_STABILIZE_RESERVATION 65536
|
#define JOURNAL_STABILIZE_RESERVATION 65536
|
||||||
|
#define JOURNAL_INSTANT_RESERVATION 131072
|
||||||
|
|
||||||
// Journal entries
|
// Journal entries
|
||||||
// Journal entries are linked to each other by their crc32 value
|
// Journal entries are linked to each other by their crc32 value
|
||||||
@@ -164,7 +166,6 @@ inline bool operator < (const pending_journaling_t & a, const pending_journaling
|
|||||||
struct journal_t
|
struct journal_t
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
uint64_t device_size;
|
|
||||||
bool inmemory = false;
|
bool inmemory = false;
|
||||||
bool flush_journal = false;
|
bool flush_journal = false;
|
||||||
void *buffer = NULL;
|
void *buffer = NULL;
|
||||||
|
@@ -4,23 +4,10 @@
|
|||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include "blockstore_impl.h"
|
#include "blockstore_impl.h"
|
||||||
|
|
||||||
static uint32_t is_power_of_two(uint64_t value)
|
|
||||||
{
|
|
||||||
uint32_t l = 0;
|
|
||||||
while (value > 1)
|
|
||||||
{
|
|
||||||
if (value & 1)
|
|
||||||
{
|
|
||||||
return 64;
|
|
||||||
}
|
|
||||||
value = value >> 1;
|
|
||||||
l++;
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
||||||
{
|
{
|
||||||
|
// Common disk options
|
||||||
|
dsk.parse_config(config);
|
||||||
// Parse
|
// Parse
|
||||||
if (config["readonly"] == "true" || config["readonly"] == "1" || config["readonly"] == "yes")
|
if (config["readonly"] == "true" || config["readonly"] == "1" || config["readonly"] == "yes")
|
||||||
{
|
{
|
||||||
@@ -38,10 +25,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
{
|
{
|
||||||
disable_journal_fsync = true;
|
disable_journal_fsync = true;
|
||||||
}
|
}
|
||||||
if (config["disable_device_lock"] == "true" || config["disable_device_lock"] == "1" || config["disable_device_lock"] == "yes")
|
|
||||||
{
|
|
||||||
disable_flock = true;
|
|
||||||
}
|
|
||||||
if (config["flush_journal"] == "true" || config["flush_journal"] == "1" || config["flush_journal"] == "yes")
|
if (config["flush_journal"] == "true" || config["flush_journal"] == "1" || config["flush_journal"] == "yes")
|
||||||
{
|
{
|
||||||
// Only flush journal and exit
|
// Only flush journal and exit
|
||||||
@@ -56,24 +39,11 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
immediate_commit = IMMEDIATE_SMALL;
|
immediate_commit = IMMEDIATE_SMALL;
|
||||||
}
|
}
|
||||||
metadata_buf_size = strtoull(config["meta_buf_size"].c_str(), NULL, 10);
|
metadata_buf_size = strtoull(config["meta_buf_size"].c_str(), NULL, 10);
|
||||||
cfg_journal_size = strtoull(config["journal_size"].c_str(), NULL, 10);
|
|
||||||
data_device = config["data_device"];
|
|
||||||
data_offset = strtoull(config["data_offset"].c_str(), NULL, 10);
|
|
||||||
cfg_data_size = strtoull(config["data_size"].c_str(), NULL, 10);
|
|
||||||
meta_device = config["meta_device"];
|
|
||||||
meta_offset = strtoull(config["meta_offset"].c_str(), NULL, 10);
|
|
||||||
block_size = strtoull(config["block_size"].c_str(), NULL, 10);
|
|
||||||
inmemory_meta = config["inmemory_metadata"] != "false";
|
inmemory_meta = config["inmemory_metadata"] != "false";
|
||||||
journal_device = config["journal_device"];
|
|
||||||
journal.offset = strtoull(config["journal_offset"].c_str(), NULL, 10);
|
|
||||||
journal.sector_count = strtoull(config["journal_sector_buffer_count"].c_str(), NULL, 10);
|
journal.sector_count = strtoull(config["journal_sector_buffer_count"].c_str(), NULL, 10);
|
||||||
journal.no_same_sector_overwrites = config["journal_no_same_sector_overwrites"] == "true" ||
|
journal.no_same_sector_overwrites = config["journal_no_same_sector_overwrites"] == "true" ||
|
||||||
config["journal_no_same_sector_overwrites"] == "1" || config["journal_no_same_sector_overwrites"] == "yes";
|
config["journal_no_same_sector_overwrites"] == "1" || config["journal_no_same_sector_overwrites"] == "yes";
|
||||||
journal.inmemory = config["inmemory_journal"] != "false";
|
journal.inmemory = config["inmemory_journal"] != "false";
|
||||||
disk_alignment = strtoull(config["disk_alignment"].c_str(), NULL, 10);
|
|
||||||
journal_block_size = strtoull(config["journal_block_size"].c_str(), NULL, 10);
|
|
||||||
meta_block_size = strtoull(config["meta_block_size"].c_str(), NULL, 10);
|
|
||||||
bitmap_granularity = strtoull(config["bitmap_granularity"].c_str(), NULL, 10);
|
|
||||||
max_flusher_count = strtoull(config["max_flusher_count"].c_str(), NULL, 10);
|
max_flusher_count = strtoull(config["max_flusher_count"].c_str(), NULL, 10);
|
||||||
if (!max_flusher_count)
|
if (!max_flusher_count)
|
||||||
max_flusher_count = strtoull(config["flusher_count"].c_str(), NULL, 10);
|
max_flusher_count = strtoull(config["flusher_count"].c_str(), NULL, 10);
|
||||||
@@ -85,14 +55,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
|
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
|
||||||
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
||||||
// Validate
|
// Validate
|
||||||
if (!block_size)
|
|
||||||
{
|
|
||||||
block_size = (1 << DEFAULT_ORDER);
|
|
||||||
}
|
|
||||||
if ((block_order = is_power_of_two(block_size)) >= 64 || block_size < MIN_BLOCK_SIZE || block_size >= MAX_BLOCK_SIZE)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Bad block size");
|
|
||||||
}
|
|
||||||
if (!max_flusher_count)
|
if (!max_flusher_count)
|
||||||
{
|
{
|
||||||
max_flusher_count = 256;
|
max_flusher_count = 256;
|
||||||
@@ -105,62 +67,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
{
|
{
|
||||||
max_write_iodepth = 128;
|
max_write_iodepth = 128;
|
||||||
}
|
}
|
||||||
if (!disk_alignment)
|
|
||||||
{
|
|
||||||
disk_alignment = 4096;
|
|
||||||
}
|
|
||||||
else if (disk_alignment % MEM_ALIGNMENT)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("disk_alignment must be a multiple of "+std::to_string(MEM_ALIGNMENT));
|
|
||||||
}
|
|
||||||
if (!journal_block_size)
|
|
||||||
{
|
|
||||||
journal_block_size = 4096;
|
|
||||||
}
|
|
||||||
else if (journal_block_size % MEM_ALIGNMENT)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("journal_block_size must be a multiple of "+std::to_string(MEM_ALIGNMENT));
|
|
||||||
}
|
|
||||||
if (!meta_block_size)
|
|
||||||
{
|
|
||||||
meta_block_size = 4096;
|
|
||||||
}
|
|
||||||
else if (meta_block_size % MEM_ALIGNMENT)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("meta_block_size must be a multiple of "+std::to_string(MEM_ALIGNMENT));
|
|
||||||
}
|
|
||||||
if (data_offset % disk_alignment)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("data_offset must be a multiple of disk_alignment = "+std::to_string(disk_alignment));
|
|
||||||
}
|
|
||||||
if (!bitmap_granularity)
|
|
||||||
{
|
|
||||||
bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
|
||||||
}
|
|
||||||
else if (bitmap_granularity % disk_alignment)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Sparse write tracking granularity must be a multiple of disk_alignment = "+std::to_string(disk_alignment));
|
|
||||||
}
|
|
||||||
if (block_size % bitmap_granularity)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Block size must be a multiple of sparse write tracking granularity");
|
|
||||||
}
|
|
||||||
if (journal_device == meta_device || meta_device == "" && journal_device == data_device)
|
|
||||||
{
|
|
||||||
journal_device = "";
|
|
||||||
}
|
|
||||||
if (meta_device == data_device)
|
|
||||||
{
|
|
||||||
meta_device = "";
|
|
||||||
}
|
|
||||||
if (meta_offset % meta_block_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("meta_offset must be a multiple of meta_block_size = "+std::to_string(meta_block_size));
|
|
||||||
}
|
|
||||||
if (journal.offset % journal_block_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("journal_offset must be a multiple of journal_block_size = "+std::to_string(journal_block_size));
|
|
||||||
}
|
|
||||||
if (journal.sector_count < 2)
|
if (journal.sector_count < 2)
|
||||||
{
|
{
|
||||||
journal.sector_count = 32;
|
journal.sector_count = 32;
|
||||||
@@ -169,11 +75,11 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
{
|
{
|
||||||
metadata_buf_size = 4*1024*1024;
|
metadata_buf_size = 4*1024*1024;
|
||||||
}
|
}
|
||||||
if (meta_device == "")
|
if (dsk.meta_device == dsk.data_device)
|
||||||
{
|
{
|
||||||
disable_meta_fsync = disable_data_fsync;
|
disable_meta_fsync = disable_data_fsync;
|
||||||
}
|
}
|
||||||
if (journal_device == "")
|
if (dsk.journal_device == dsk.meta_device)
|
||||||
{
|
{
|
||||||
disable_journal_fsync = disable_meta_fsync;
|
disable_journal_fsync = disable_meta_fsync;
|
||||||
}
|
}
|
||||||
@@ -202,238 +108,46 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
|||||||
throttle_threshold_us = 50;
|
throttle_threshold_us = 50;
|
||||||
}
|
}
|
||||||
// init some fields
|
// init some fields
|
||||||
clean_entry_bitmap_size = block_size / bitmap_granularity / 8;
|
journal.block_size = dsk.journal_block_size;
|
||||||
clean_entry_size = sizeof(clean_disk_entry) + 2*clean_entry_bitmap_size;
|
journal.next_free = dsk.journal_block_size;
|
||||||
journal.block_size = journal_block_size;
|
journal.used_start = dsk.journal_block_size;
|
||||||
journal.next_free = journal_block_size;
|
|
||||||
journal.used_start = journal_block_size;
|
|
||||||
// no free space because sector is initially unmapped
|
// no free space because sector is initially unmapped
|
||||||
journal.in_sector_pos = journal_block_size;
|
journal.in_sector_pos = dsk.journal_block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::calc_lengths()
|
void blockstore_impl_t::calc_lengths()
|
||||||
{
|
{
|
||||||
// data
|
dsk.calc_lengths();
|
||||||
data_len = data_size - data_offset;
|
journal.len = dsk.journal_len;
|
||||||
if (data_fd == meta_fd && data_offset < meta_offset)
|
journal.block_size = dsk.journal_block_size;
|
||||||
{
|
journal.offset = dsk.journal_offset;
|
||||||
data_len = meta_offset - data_offset;
|
|
||||||
}
|
|
||||||
if (data_fd == journal.fd && data_offset < journal.offset)
|
|
||||||
{
|
|
||||||
data_len = data_len < journal.offset-data_offset
|
|
||||||
? data_len : journal.offset-data_offset;
|
|
||||||
}
|
|
||||||
if (cfg_data_size != 0)
|
|
||||||
{
|
|
||||||
if (data_len < cfg_data_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Data area ("+std::to_string(data_len)+
|
|
||||||
" bytes) is less than configured size ("+std::to_string(cfg_data_size)+" bytes)");
|
|
||||||
}
|
|
||||||
data_len = cfg_data_size;
|
|
||||||
}
|
|
||||||
// meta
|
|
||||||
meta_area = (meta_fd == data_fd ? data_size : meta_size) - meta_offset;
|
|
||||||
if (meta_fd == data_fd && meta_offset <= data_offset)
|
|
||||||
{
|
|
||||||
meta_area = data_offset - meta_offset;
|
|
||||||
}
|
|
||||||
if (meta_fd == journal.fd && meta_offset <= journal.offset)
|
|
||||||
{
|
|
||||||
meta_area = meta_area < journal.offset-meta_offset
|
|
||||||
? meta_area : journal.offset-meta_offset;
|
|
||||||
}
|
|
||||||
// journal
|
|
||||||
journal.len = (journal.fd == data_fd ? data_size : (journal.fd == meta_fd ? meta_size : journal.device_size)) - journal.offset;
|
|
||||||
if (journal.fd == data_fd && journal.offset <= data_offset)
|
|
||||||
{
|
|
||||||
journal.len = data_offset - journal.offset;
|
|
||||||
}
|
|
||||||
if (journal.fd == meta_fd && journal.offset <= meta_offset)
|
|
||||||
{
|
|
||||||
journal.len = journal.len < meta_offset-journal.offset
|
|
||||||
? journal.len : meta_offset-journal.offset;
|
|
||||||
}
|
|
||||||
// required metadata size
|
|
||||||
block_count = data_len / block_size;
|
|
||||||
meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size) / (meta_block_size / clean_entry_size)) * meta_block_size;
|
|
||||||
if (meta_area < meta_len)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Metadata area is too small, need at least "+std::to_string(meta_len)+" bytes");
|
|
||||||
}
|
|
||||||
if (inmemory_meta)
|
if (inmemory_meta)
|
||||||
{
|
{
|
||||||
metadata_buffer = memalign(MEM_ALIGNMENT, meta_len);
|
metadata_buffer = memalign(MEM_ALIGNMENT, dsk.meta_len);
|
||||||
if (!metadata_buffer)
|
if (!metadata_buffer)
|
||||||
throw std::runtime_error("Failed to allocate memory for the metadata");
|
throw std::runtime_error("Failed to allocate memory for the metadata");
|
||||||
}
|
}
|
||||||
else if (clean_entry_bitmap_size)
|
else if (dsk.clean_entry_bitmap_size)
|
||||||
{
|
{
|
||||||
clean_bitmap = (uint8_t*)malloc(block_count * 2*clean_entry_bitmap_size);
|
clean_bitmap = (uint8_t*)malloc(dsk.block_count * 2*dsk.clean_entry_bitmap_size);
|
||||||
if (!clean_bitmap)
|
if (!clean_bitmap)
|
||||||
throw std::runtime_error("Failed to allocate memory for the metadata sparse write bitmap");
|
throw std::runtime_error("Failed to allocate memory for the metadata sparse write bitmap");
|
||||||
}
|
}
|
||||||
// requested journal size
|
|
||||||
if (cfg_journal_size > journal.len)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Requested journal_size is too large");
|
|
||||||
}
|
|
||||||
else if (cfg_journal_size > 0)
|
|
||||||
{
|
|
||||||
journal.len = cfg_journal_size;
|
|
||||||
}
|
|
||||||
if (journal.len < MIN_JOURNAL_SIZE)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Journal is too small, need at least "+std::to_string(MIN_JOURNAL_SIZE)+" bytes");
|
|
||||||
}
|
|
||||||
if (journal.inmemory)
|
if (journal.inmemory)
|
||||||
{
|
{
|
||||||
journal.buffer = memalign(MEM_ALIGNMENT, journal.len);
|
journal.buffer = memalign(MEM_ALIGNMENT, journal.len);
|
||||||
if (!journal.buffer)
|
if (!journal.buffer)
|
||||||
throw std::runtime_error("Failed to allocate memory for journal");
|
throw std::runtime_error("Failed to allocate memory for journal");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void check_size(int fd, uint64_t *size, uint64_t *sectsize, std::string name)
|
|
||||||
{
|
|
||||||
int sect;
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fd, &st) < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to stat "+name);
|
|
||||||
}
|
|
||||||
if (S_ISREG(st.st_mode))
|
|
||||||
{
|
|
||||||
*size = st.st_size;
|
|
||||||
if (sectsize)
|
|
||||||
{
|
|
||||||
*sectsize = st.st_blksize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (S_ISBLK(st.st_mode))
|
|
||||||
{
|
|
||||||
if (ioctl(fd, BLKGETSIZE64, size) < 0 ||
|
|
||||||
ioctl(fd, BLKSSZGET, §) < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to get "+name+" size or block size: "+strerror(errno));
|
|
||||||
}
|
|
||||||
if (sectsize)
|
|
||||||
{
|
|
||||||
*sectsize = sect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error(name+" is neither a file nor a block device");
|
journal.sector_buf = (uint8_t*)memalign(MEM_ALIGNMENT, journal.sector_count * dsk.journal_block_size);
|
||||||
}
|
if (!journal.sector_buf)
|
||||||
}
|
throw std::bad_alloc();
|
||||||
|
|
||||||
void blockstore_impl_t::open_data()
|
|
||||||
{
|
|
||||||
data_fd = open(data_device.c_str(), O_DIRECT|O_RDWR);
|
|
||||||
if (data_fd == -1)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to open data device");
|
|
||||||
}
|
|
||||||
check_size(data_fd, &data_size, &data_device_sect, "data device");
|
|
||||||
if (disk_alignment % data_device_sect)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"disk_alignment ("+std::to_string(disk_alignment)+
|
|
||||||
") is not a multiple of data device sector size ("+std::to_string(data_device_sect)+")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (data_offset >= data_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("data_offset exceeds device size = "+std::to_string(data_size));
|
|
||||||
}
|
|
||||||
if (!disable_flock && flock(data_fd, LOCK_EX|LOCK_NB) != 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Failed to lock data device: ") + strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::open_meta()
|
|
||||||
{
|
|
||||||
if (meta_device != "")
|
|
||||||
{
|
|
||||||
meta_offset = 0;
|
|
||||||
meta_fd = open(meta_device.c_str(), O_DIRECT|O_RDWR);
|
|
||||||
if (meta_fd == -1)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to open metadata device");
|
|
||||||
}
|
|
||||||
check_size(meta_fd, &meta_size, &meta_device_sect, "metadata device");
|
|
||||||
if (meta_offset >= meta_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(meta_size));
|
|
||||||
}
|
|
||||||
if (!disable_flock && flock(meta_fd, LOCK_EX|LOCK_NB) != 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Failed to lock metadata device: ") + strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
meta_fd = data_fd;
|
|
||||||
meta_device_sect = data_device_sect;
|
|
||||||
meta_size = 0;
|
|
||||||
if (meta_offset >= data_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(data_size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (meta_block_size % meta_device_sect)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"meta_block_size ("+std::to_string(meta_block_size)+
|
|
||||||
") is not a multiple of data device sector size ("+std::to_string(meta_device_sect)+")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::open_journal()
|
|
||||||
{
|
|
||||||
if (journal_device != "")
|
|
||||||
{
|
|
||||||
journal.fd = open(journal_device.c_str(), O_DIRECT|O_RDWR);
|
|
||||||
if (journal.fd == -1)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to open journal device");
|
|
||||||
}
|
|
||||||
check_size(journal.fd, &journal.device_size, &journal_device_sect, "journal device");
|
|
||||||
if (!disable_flock && flock(journal.fd, LOCK_EX|LOCK_NB) != 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("Failed to lock journal device: ") + strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
journal.fd = meta_fd;
|
|
||||||
journal_device_sect = meta_device_sect;
|
|
||||||
journal.device_size = 0;
|
|
||||||
if (journal.offset >= data_size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("journal_offset exceeds device size");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
journal.sector_info = (journal_sector_info_t*)calloc(journal.sector_count, sizeof(journal_sector_info_t));
|
journal.sector_info = (journal_sector_info_t*)calloc(journal.sector_count, sizeof(journal_sector_info_t));
|
||||||
if (!journal.sector_info)
|
if (!journal.sector_info)
|
||||||
{
|
{
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
if (!journal.inmemory)
|
|
||||||
{
|
|
||||||
journal.sector_buf = (uint8_t*)memalign(MEM_ALIGNMENT, journal.sector_count * journal_block_size);
|
|
||||||
if (!journal.sector_buf)
|
|
||||||
throw std::bad_alloc();
|
|
||||||
}
|
|
||||||
if (journal_block_size % journal_device_sect)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"journal_block_size ("+std::to_string(journal_block_size)+
|
|
||||||
") is not a multiple of journal device sector size ("+std::to_string(journal_device_sect)+")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -32,9 +32,9 @@ int blockstore_impl_t::fulfill_read_push(blockstore_op_t *op, void *buf, uint64_
|
|||||||
PRIV(op)->pending_ops++;
|
PRIV(op)->pending_ops++;
|
||||||
my_uring_prep_readv(
|
my_uring_prep_readv(
|
||||||
sqe,
|
sqe,
|
||||||
IS_JOURNAL(item_state) ? journal.fd : data_fd,
|
IS_JOURNAL(item_state) ? dsk.journal_fd : dsk.data_fd,
|
||||||
&data->iov, 1,
|
&data->iov, 1,
|
||||||
(IS_JOURNAL(item_state) ? journal.offset : data_offset) + offset
|
(IS_JOURNAL(item_state) ? dsk.journal_offset : dsk.data_offset) + offset
|
||||||
);
|
);
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_read_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_read_event(data, op); };
|
||||||
return 1;
|
return 1;
|
||||||
@@ -42,7 +42,7 @@ int blockstore_impl_t::fulfill_read_push(blockstore_op_t *op, void *buf, uint64_
|
|||||||
|
|
||||||
// FIXME I've seen a bug here so I want some tests
|
// FIXME I've seen a bug here so I want some tests
|
||||||
int blockstore_impl_t::fulfill_read(blockstore_op_t *read_op, uint64_t &fulfilled, uint32_t item_start, uint32_t item_end,
|
int blockstore_impl_t::fulfill_read(blockstore_op_t *read_op, uint64_t &fulfilled, uint32_t item_start, uint32_t item_end,
|
||||||
uint32_t item_state, uint64_t item_version, uint64_t item_location)
|
uint32_t item_state, uint64_t item_version, uint64_t item_location, uint64_t journal_sector)
|
||||||
{
|
{
|
||||||
uint32_t cur_start = item_start;
|
uint32_t cur_start = item_start;
|
||||||
if (cur_start < read_op->offset + read_op->len && item_end > read_op->offset)
|
if (cur_start < read_op->offset + read_op->len && item_end > read_op->offset)
|
||||||
@@ -72,6 +72,7 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *read_op, uint64_t &fulfille
|
|||||||
fulfill_read_t el = {
|
fulfill_read_t el = {
|
||||||
.offset = cur_start,
|
.offset = cur_start,
|
||||||
.len = it == PRIV(read_op)->read_vec.end() || it->offset >= item_end ? item_end-cur_start : it->offset-cur_start,
|
.len = it == PRIV(read_op)->read_vec.end() || it->offset >= item_end ? item_end-cur_start : it->offset-cur_start,
|
||||||
|
.journal_sector = journal_sector,
|
||||||
};
|
};
|
||||||
it = PRIV(read_op)->read_vec.insert(it, el);
|
it = PRIV(read_op)->read_vec.insert(it, el);
|
||||||
if (!fulfill_read_push(read_op,
|
if (!fulfill_read_push(read_op,
|
||||||
@@ -97,15 +98,15 @@ endwhile:
|
|||||||
uint8_t* blockstore_impl_t::get_clean_entry_bitmap(uint64_t block_loc, int offset)
|
uint8_t* blockstore_impl_t::get_clean_entry_bitmap(uint64_t block_loc, int offset)
|
||||||
{
|
{
|
||||||
uint8_t *clean_entry_bitmap;
|
uint8_t *clean_entry_bitmap;
|
||||||
uint64_t meta_loc = block_loc >> block_order;
|
uint64_t meta_loc = block_loc >> dsk.block_order;
|
||||||
if (inmemory_meta)
|
if (inmemory_meta)
|
||||||
{
|
{
|
||||||
uint64_t sector = (meta_loc / (meta_block_size / clean_entry_size)) * meta_block_size;
|
uint64_t sector = (meta_loc / (dsk.meta_block_size / dsk.clean_entry_size)) * dsk.meta_block_size;
|
||||||
uint64_t pos = (meta_loc % (meta_block_size / clean_entry_size));
|
uint64_t pos = (meta_loc % (dsk.meta_block_size / dsk.clean_entry_size));
|
||||||
clean_entry_bitmap = ((uint8_t*)metadata_buffer + sector + pos*clean_entry_size + sizeof(clean_disk_entry) + offset);
|
clean_entry_bitmap = ((uint8_t*)metadata_buffer + sector + pos*dsk.clean_entry_size + sizeof(clean_disk_entry) + offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
clean_entry_bitmap = (uint8_t*)(clean_bitmap + meta_loc*2*clean_entry_bitmap_size + offset);
|
clean_entry_bitmap = (uint8_t*)(clean_bitmap + meta_loc*2*dsk.clean_entry_bitmap_size + offset);
|
||||||
return clean_entry_bitmap;
|
return clean_entry_bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
while (dirty_it->first.oid == read_op->oid)
|
while (dirty_it->first.oid == read_op->oid)
|
||||||
{
|
{
|
||||||
dirty_entry& dirty = dirty_it->second;
|
dirty_entry& dirty = dirty_it->second;
|
||||||
bool version_ok = read_op->version >= dirty_it->first.version;
|
bool version_ok = !IS_IN_FLIGHT(dirty.state) && read_op->version >= dirty_it->first.version;
|
||||||
if (IS_SYNCED(dirty.state))
|
if (IS_SYNCED(dirty.state))
|
||||||
{
|
{
|
||||||
if (!version_ok && read_op->version != 0)
|
if (!version_ok && read_op->version != 0)
|
||||||
@@ -152,12 +153,14 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
result_version = dirty_it->first.version;
|
result_version = dirty_it->first.version;
|
||||||
if (read_op->bitmap)
|
if (read_op->bitmap)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
void *bmp_ptr = (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||||
memcpy(read_op->bitmap, bmp_ptr, clean_entry_bitmap_size);
|
memcpy(read_op->bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If inmemory_journal is false, journal trim will have to wait until the read is completed
|
||||||
if (!fulfill_read(read_op, fulfilled, dirty.offset, dirty.offset + dirty.len,
|
if (!fulfill_read(read_op, fulfilled, dirty.offset, dirty.offset + dirty.len,
|
||||||
dirty.state, dirty_it->first.version, dirty.location + (IS_JOURNAL(dirty.state) ? 0 : dirty.offset)))
|
dirty.state, dirty_it->first.version, dirty.location + (IS_JOURNAL(dirty.state) ? 0 : dirty.offset),
|
||||||
|
(IS_JOURNAL(dirty.state) ? dirty.journal_sector+1 : 0)))
|
||||||
{
|
{
|
||||||
// need to wait. undo added requests, don't dequeue op
|
// need to wait. undo added requests, don't dequeue op
|
||||||
PRIV(read_op)->read_vec.clear();
|
PRIV(read_op)->read_vec.clear();
|
||||||
@@ -171,22 +174,23 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
dirty_it--;
|
dirty_it--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clean_it != clean_db.end())
|
if (clean_found)
|
||||||
{
|
{
|
||||||
if (!result_version)
|
if (!result_version)
|
||||||
{
|
{
|
||||||
result_version = clean_it->second.version;
|
result_version = clean_it->second.version;
|
||||||
if (read_op->bitmap)
|
if (read_op->bitmap)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, dsk.clean_entry_bitmap_size);
|
||||||
memcpy(read_op->bitmap, bmp_ptr, clean_entry_bitmap_size);
|
memcpy(read_op->bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fulfilled < read_op->len)
|
if (fulfilled < read_op->len)
|
||||||
{
|
{
|
||||||
if (!clean_entry_bitmap_size)
|
if (!dsk.clean_entry_bitmap_size)
|
||||||
{
|
{
|
||||||
if (!fulfill_read(read_op, fulfilled, 0, block_size, (BS_ST_BIG_WRITE | BS_ST_STABLE), 0, clean_it->second.location))
|
if (!fulfill_read(read_op, fulfilled, 0, dsk.data_block_size,
|
||||||
|
(BS_ST_BIG_WRITE | BS_ST_STABLE), 0, clean_it->second.location, 0))
|
||||||
{
|
{
|
||||||
// need to wait. undo added requests, don't dequeue op
|
// need to wait. undo added requests, don't dequeue op
|
||||||
PRIV(read_op)->read_vec.clear();
|
PRIV(read_op)->read_vec.clear();
|
||||||
@@ -196,7 +200,7 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t *clean_entry_bitmap = get_clean_entry_bitmap(clean_it->second.location, 0);
|
uint8_t *clean_entry_bitmap = get_clean_entry_bitmap(clean_it->second.location, 0);
|
||||||
uint64_t bmp_start = 0, bmp_end = 0, bmp_size = block_size/bitmap_granularity;
|
uint64_t bmp_start = 0, bmp_end = 0, bmp_size = dsk.data_block_size/dsk.bitmap_granularity;
|
||||||
while (bmp_start < bmp_size)
|
while (bmp_start < bmp_size)
|
||||||
{
|
{
|
||||||
while (!(clean_entry_bitmap[bmp_end >> 3] & (1 << (bmp_end & 0x7))) && bmp_end < bmp_size)
|
while (!(clean_entry_bitmap[bmp_end >> 3] & (1 << (bmp_end & 0x7))) && bmp_end < bmp_size)
|
||||||
@@ -206,8 +210,8 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
if (bmp_end > bmp_start)
|
if (bmp_end > bmp_start)
|
||||||
{
|
{
|
||||||
// fill with zeroes
|
// fill with zeroes
|
||||||
assert(fulfill_read(read_op, fulfilled, bmp_start * bitmap_granularity,
|
assert(fulfill_read(read_op, fulfilled, bmp_start * dsk.bitmap_granularity,
|
||||||
bmp_end * bitmap_granularity, (BS_ST_DELETE | BS_ST_STABLE), 0, 0));
|
bmp_end * dsk.bitmap_granularity, (BS_ST_DELETE | BS_ST_STABLE), 0, 0, 0));
|
||||||
}
|
}
|
||||||
bmp_start = bmp_end;
|
bmp_start = bmp_end;
|
||||||
while (clean_entry_bitmap[bmp_end >> 3] & (1 << (bmp_end & 0x7)) && bmp_end < bmp_size)
|
while (clean_entry_bitmap[bmp_end >> 3] & (1 << (bmp_end & 0x7)) && bmp_end < bmp_size)
|
||||||
@@ -216,9 +220,9 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
}
|
}
|
||||||
if (bmp_end > bmp_start)
|
if (bmp_end > bmp_start)
|
||||||
{
|
{
|
||||||
if (!fulfill_read(read_op, fulfilled, bmp_start * bitmap_granularity,
|
if (!fulfill_read(read_op, fulfilled, bmp_start * dsk.bitmap_granularity,
|
||||||
bmp_end * bitmap_granularity, (BS_ST_BIG_WRITE | BS_ST_STABLE), 0,
|
bmp_end * dsk.bitmap_granularity, (BS_ST_BIG_WRITE | BS_ST_STABLE), 0,
|
||||||
clean_it->second.location + bmp_start * bitmap_granularity))
|
clean_it->second.location + bmp_start * dsk.bitmap_granularity, 0))
|
||||||
{
|
{
|
||||||
// need to wait. undo added requests, don't dequeue op
|
// need to wait. undo added requests, don't dequeue op
|
||||||
PRIV(read_op)->read_vec.clear();
|
PRIV(read_op)->read_vec.clear();
|
||||||
@@ -233,7 +237,7 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
else if (fulfilled < read_op->len)
|
else if (fulfilled < read_op->len)
|
||||||
{
|
{
|
||||||
// fill remaining parts with zeroes
|
// fill remaining parts with zeroes
|
||||||
assert(fulfill_read(read_op, fulfilled, 0, block_size, (BS_ST_DELETE | BS_ST_STABLE), 0, 0));
|
assert(fulfill_read(read_op, fulfilled, 0, dsk.data_block_size, (BS_ST_DELETE | BS_ST_STABLE), 0, 0, 0));
|
||||||
}
|
}
|
||||||
assert(fulfilled == read_op->len);
|
assert(fulfilled == read_op->len);
|
||||||
read_op->version = result_version;
|
read_op->version = result_version;
|
||||||
@@ -249,6 +253,15 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
|||||||
FINISH_OP(read_op);
|
FINISH_OP(read_op);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
if (!journal.inmemory)
|
||||||
|
{
|
||||||
|
// Journal trim has to wait until the read is completed - record journal sector usage
|
||||||
|
for (auto & rv: PRIV(read_op)->read_vec)
|
||||||
|
{
|
||||||
|
if (rv.journal_sector)
|
||||||
|
journal.used_sectors[rv.journal_sector-1]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
read_op->retval = 0;
|
read_op->retval = 0;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -264,6 +277,22 @@ void blockstore_impl_t::handle_read_event(ring_data_t *data, blockstore_op_t *op
|
|||||||
}
|
}
|
||||||
if (PRIV(op)->pending_ops == 0)
|
if (PRIV(op)->pending_ops == 0)
|
||||||
{
|
{
|
||||||
|
if (!journal.inmemory)
|
||||||
|
{
|
||||||
|
// Release journal sector usage
|
||||||
|
for (auto & rv: PRIV(op)->read_vec)
|
||||||
|
{
|
||||||
|
if (rv.journal_sector)
|
||||||
|
{
|
||||||
|
auto used = --journal.used_sectors[rv.journal_sector-1];
|
||||||
|
if (used == 0)
|
||||||
|
{
|
||||||
|
journal.used_sectors.erase(rv.journal_sector-1);
|
||||||
|
flusher->mark_trim_possible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (op->retval == 0)
|
if (op->retval == 0)
|
||||||
op->retval = op->len;
|
op->retval = op->len;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
@@ -288,8 +317,8 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
|
|||||||
*result_version = dirty_it->first.version;
|
*result_version = dirty_it->first.version;
|
||||||
if (bitmap)
|
if (bitmap)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
void *bmp_ptr = (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||||
memcpy(bitmap, bmp_ptr, clean_entry_bitmap_size);
|
memcpy(bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -306,14 +335,14 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
|
|||||||
*result_version = clean_it->second.version;
|
*result_version = clean_it->second.version;
|
||||||
if (bitmap)
|
if (bitmap)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, dsk.clean_entry_bitmap_size);
|
||||||
memcpy(bitmap, bmp_ptr, clean_entry_bitmap_size);
|
memcpy(bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (result_version)
|
if (result_version)
|
||||||
*result_version = 0;
|
*result_version = 0;
|
||||||
if (bitmap)
|
if (bitmap)
|
||||||
memset(bitmap, 0, clean_entry_bitmap_size);
|
memset(bitmap, 0, dsk.clean_entry_bitmap_size);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
@@ -112,7 +112,7 @@ resume_2:
|
|||||||
if (!disable_journal_fsync)
|
if (!disable_journal_fsync)
|
||||||
{
|
{
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
@@ -127,7 +127,6 @@ resume_4:
|
|||||||
{
|
{
|
||||||
mark_rolled_back(*v);
|
mark_rolled_back(*v);
|
||||||
}
|
}
|
||||||
flusher->mark_trim_possible();
|
|
||||||
// Acknowledge op
|
// Acknowledge op
|
||||||
op->retval = 0;
|
op->retval = 0;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
@@ -217,12 +216,12 @@ void blockstore_impl_t::erase_dirty(blockstore_dirty_db_t::iterator dirty_start,
|
|||||||
dirty_it->second.location != UINT64_MAX)
|
dirty_it->second.location != UINT64_MAX)
|
||||||
{
|
{
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf("Free block %lu from %lx:%lx v%lu\n", dirty_it->second.location >> block_order,
|
printf("Free block %lu from %lx:%lx v%lu\n", dirty_it->second.location >> dsk.block_order,
|
||||||
dirty_it->first.oid.inode, dirty_it->first.oid.stripe, dirty_it->first.version);
|
dirty_it->first.oid.inode, dirty_it->first.oid.stripe, dirty_it->first.version);
|
||||||
#endif
|
#endif
|
||||||
data_alloc->set(dirty_it->second.location >> block_order, false);
|
data_alloc->set(dirty_it->second.location >> dsk.block_order, false);
|
||||||
}
|
}
|
||||||
int used = --journal.used_sectors[dirty_it->second.journal_sector];
|
auto used = --journal.used_sectors[dirty_it->second.journal_sector];
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf(
|
printf(
|
||||||
"remove usage of journal offset %08lx by %lx:%lx v%lu (%d refs)\n", dirty_it->second.journal_sector,
|
"remove usage of journal offset %08lx by %lx:%lx v%lu (%d refs)\n", dirty_it->second.journal_sector,
|
||||||
@@ -232,8 +231,9 @@ void blockstore_impl_t::erase_dirty(blockstore_dirty_db_t::iterator dirty_start,
|
|||||||
if (used == 0)
|
if (used == 0)
|
||||||
{
|
{
|
||||||
journal.used_sectors.erase(dirty_it->second.journal_sector);
|
journal.used_sectors.erase(dirty_it->second.journal_sector);
|
||||||
|
flusher->mark_trim_possible();
|
||||||
}
|
}
|
||||||
if (clean_entry_bitmap_size > sizeof(void*))
|
if (dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||||
{
|
{
|
||||||
free(dirty_it->second.bitmap);
|
free(dirty_it->second.bitmap);
|
||||||
dirty_it->second.bitmap = NULL;
|
dirty_it->second.bitmap = NULL;
|
||||||
|
@@ -137,7 +137,7 @@ resume_2:
|
|||||||
if (!disable_journal_fsync)
|
if (!disable_journal_fsync)
|
||||||
{
|
{
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
@@ -195,12 +195,16 @@ void blockstore_impl_t::mark_stable(const obj_ver_id & v, bool forget_dirty)
|
|||||||
}
|
}
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
inode_space_stats[dirty_it->first.oid.inode] += block_size;
|
inode_space_stats[dirty_it->first.oid.inode] += dsk.data_block_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (IS_DELETE(dirty_it->second.state))
|
else if (IS_DELETE(dirty_it->second.state))
|
||||||
{
|
{
|
||||||
inode_space_stats[dirty_it->first.oid.inode] -= block_size;
|
auto & sp = inode_space_stats[dirty_it->first.oid.inode];
|
||||||
|
if (sp > dsk.data_block_size)
|
||||||
|
sp -= dsk.data_block_size;
|
||||||
|
else
|
||||||
|
inode_space_stats.erase(dirty_it->first.oid.inode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (forget_dirty && (IS_BIG_WRITE(dirty_it->second.state) ||
|
if (forget_dirty && (IS_BIG_WRITE(dirty_it->second.state) ||
|
||||||
|
@@ -60,7 +60,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
if (!disable_data_fsync)
|
if (!disable_data_fsync)
|
||||||
{
|
{
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, data_fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
@@ -79,7 +79,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
// Check space in the journal and journal memory buffers
|
// Check space in the journal and journal memory buffers
|
||||||
blockstore_journal_check_t space_check(this);
|
blockstore_journal_check_t space_check(this);
|
||||||
if (!space_check.check_available(op, PRIV(op)->sync_big_writes.size(),
|
if (!space_check.check_available(op, PRIV(op)->sync_big_writes.size(),
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size, JOURNAL_STABILIZE_RESERVATION))
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size, JOURNAL_STABILIZE_RESERVATION))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
int s = 0;
|
int s = 0;
|
||||||
while (it != PRIV(op)->sync_big_writes.end())
|
while (it != PRIV(op)->sync_big_writes.end())
|
||||||
{
|
{
|
||||||
if (!journal.entry_fits(sizeof(journal_entry_big_write) + clean_entry_bitmap_size) &&
|
if (!journal.entry_fits(sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal.cur_sector, op);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
@@ -99,7 +99,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
auto & dirty_entry = dirty_db.at(*it);
|
auto & dirty_entry = dirty_db.at(*it);
|
||||||
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
||||||
journal, (dirty_entry.state & BS_ST_INSTANT) ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
journal, (dirty_entry.state & BS_ST_INSTANT) ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size
|
||||||
);
|
);
|
||||||
dirty_entry.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
dirty_entry.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
||||||
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
||||||
@@ -115,8 +115,8 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
je->offset = dirty_entry.offset;
|
je->offset = dirty_entry.offset;
|
||||||
je->len = dirty_entry.len;
|
je->len = dirty_entry.len;
|
||||||
je->location = dirty_entry.location;
|
je->location = dirty_entry.location;
|
||||||
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*)
|
memcpy((void*)(je+1), (dsk.clean_entry_bitmap_size > sizeof(void*)
|
||||||
? dirty_entry.bitmap : &dirty_entry.bitmap), clean_entry_bitmap_size);
|
? dirty_entry.bitmap : &dirty_entry.bitmap), dsk.clean_entry_bitmap_size);
|
||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
it++;
|
it++;
|
||||||
@@ -132,7 +132,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
if (!disable_journal_fsync)
|
if (!disable_journal_fsync)
|
||||||
{
|
{
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, dsk.journal_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
|
@@ -10,9 +10,9 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
bool wait_big = false, wait_del = false;
|
bool wait_big = false, wait_del = false;
|
||||||
void *bmp = NULL;
|
void *bmp = NULL;
|
||||||
uint64_t version = 1;
|
uint64_t version = 1;
|
||||||
if (!is_del && clean_entry_bitmap_size > sizeof(void*))
|
if (!is_del && dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||||
{
|
{
|
||||||
bmp = calloc_or_die(1, clean_entry_bitmap_size);
|
bmp = calloc_or_die(1, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
if (dirty_db.size() > 0)
|
if (dirty_db.size() > 0)
|
||||||
{
|
{
|
||||||
@@ -32,8 +32,8 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
: ((dirty_it->second.state & BS_ST_WORKFLOW_MASK) == BS_ST_WAIT_BIG);
|
: ((dirty_it->second.state & BS_ST_WORKFLOW_MASK) == BS_ST_WAIT_BIG);
|
||||||
if (!is_del && !deleted)
|
if (!is_del && !deleted)
|
||||||
{
|
{
|
||||||
if (clean_entry_bitmap_size > sizeof(void*))
|
if (dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||||
memcpy(bmp, dirty_it->second.bitmap, clean_entry_bitmap_size);
|
memcpy(bmp, dirty_it->second.bitmap, dsk.clean_entry_bitmap_size);
|
||||||
else
|
else
|
||||||
bmp = dirty_it->second.bitmap;
|
bmp = dirty_it->second.bitmap;
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,8 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
version = clean_it->second.version + 1;
|
version = clean_it->second.version + 1;
|
||||||
if (!is_del)
|
if (!is_del)
|
||||||
{
|
{
|
||||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, dsk.clean_entry_bitmap_size);
|
||||||
memcpy((clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp), bmp_ptr, clean_entry_bitmap_size);
|
memcpy((dsk.clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp), bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -89,15 +89,18 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Invalid version requested
|
// Invalid version requested
|
||||||
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
|
printf("Write %lx:%lx v%lu requested, but we already have v%lu\n", op->oid.inode, op->oid.stripe, op->version, version);
|
||||||
|
#endif
|
||||||
op->retval = -EEXIST;
|
op->retval = -EEXIST;
|
||||||
if (!is_del && clean_entry_bitmap_size > sizeof(void*))
|
if (!is_del && dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||||
{
|
{
|
||||||
free(bmp);
|
free(bmp);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wait_big && !is_del && !deleted && op->len < block_size &&
|
if (wait_big && !is_del && !deleted && op->len < dsk.data_block_size &&
|
||||||
immediate_commit != IMMEDIATE_ALL)
|
immediate_commit != IMMEDIATE_ALL)
|
||||||
{
|
{
|
||||||
// Issue an additional sync so that the previous big write can reach the journal
|
// Issue an additional sync so that the previous big write can reach the journal
|
||||||
@@ -115,14 +118,14 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
else if (!wait_del)
|
else if (!wait_del)
|
||||||
printf("Write %lx:%lx v%lu offset=%u len=%u\n", op->oid.inode, op->oid.stripe, op->version, op->offset, op->len);
|
printf("Write %lx:%lx v%lu offset=%u len=%u\n", op->oid.inode, op->oid.stripe, op->version, op->offset, op->len);
|
||||||
#endif
|
#endif
|
||||||
// FIXME No strict need to add it into dirty_db here, it's just left
|
// No strict need to add it into dirty_db here except maybe for listings to return
|
||||||
// from the previous implementation where reads waited for writes
|
// correct data when there are inflight operations in the queue
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
if (is_del)
|
if (is_del)
|
||||||
state = BS_ST_DELETE | BS_ST_IN_FLIGHT;
|
state = BS_ST_DELETE | BS_ST_IN_FLIGHT;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state = (op->len == block_size || deleted ? BS_ST_BIG_WRITE : BS_ST_SMALL_WRITE);
|
state = (op->len == dsk.data_block_size || deleted ? BS_ST_BIG_WRITE : BS_ST_SMALL_WRITE);
|
||||||
if (state == BS_ST_SMALL_WRITE && throttle_small_writes)
|
if (state == BS_ST_SMALL_WRITE && throttle_small_writes)
|
||||||
clock_gettime(CLOCK_REALTIME, &PRIV(op)->tv_begin);
|
clock_gettime(CLOCK_REALTIME, &PRIV(op)->tv_begin);
|
||||||
if (wait_del)
|
if (wait_del)
|
||||||
@@ -136,10 +139,10 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
|||||||
if (op->bitmap)
|
if (op->bitmap)
|
||||||
{
|
{
|
||||||
// Only allow to overwrite part of the object bitmap respective to the write's offset/len
|
// Only allow to overwrite part of the object bitmap respective to the write's offset/len
|
||||||
uint8_t *bmp_ptr = (uint8_t*)(clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp);
|
uint8_t *bmp_ptr = (uint8_t*)(dsk.clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp);
|
||||||
uint32_t bit = op->offset/bitmap_granularity;
|
uint32_t bit = op->offset/dsk.bitmap_granularity;
|
||||||
uint32_t bits_left = op->len/bitmap_granularity;
|
uint32_t bits_left = op->len/dsk.bitmap_granularity;
|
||||||
while (!(bit % 8) && bits_left > 8)
|
while (!(bit % 8) && bits_left >= 8)
|
||||||
{
|
{
|
||||||
// Copy bytes
|
// Copy bytes
|
||||||
bmp_ptr[bit/8] = ((uint8_t*)op->bitmap)[bit/8];
|
bmp_ptr[bit/8] = ((uint8_t*)op->bitmap)[bit/8];
|
||||||
@@ -175,15 +178,22 @@ void blockstore_impl_t::cancel_all_writes(blockstore_op_t *op, blockstore_dirty_
|
|||||||
{
|
{
|
||||||
while (dirty_it != dirty_db.end() && dirty_it->first.oid == op->oid)
|
while (dirty_it != dirty_db.end() && dirty_it->first.oid == op->oid)
|
||||||
{
|
{
|
||||||
if (clean_entry_bitmap_size > sizeof(void*))
|
if (dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||||
free(dirty_it->second.bitmap);
|
free(dirty_it->second.bitmap);
|
||||||
dirty_db.erase(dirty_it++);
|
dirty_db.erase(dirty_it++);
|
||||||
}
|
}
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto other_op: submit_queue)
|
for (auto other_op: submit_queue)
|
||||||
{
|
{
|
||||||
if (!found && other_op == op)
|
if (!other_op)
|
||||||
|
{
|
||||||
|
// freed operations during submitting are zeroed
|
||||||
|
}
|
||||||
|
else if (other_op == op)
|
||||||
|
{
|
||||||
|
// <op> may be present in queue multiple times due to moving operations in submit_queue
|
||||||
found = true;
|
found = true;
|
||||||
|
}
|
||||||
else if (found && other_op->oid == op->oid &&
|
else if (found && other_op->oid == op->oid &&
|
||||||
(other_op->opcode == BS_OP_WRITE || other_op->opcode == BS_OP_WRITE_STABLE))
|
(other_op->opcode == BS_OP_WRITE || other_op->opcode == BS_OP_WRITE_STABLE))
|
||||||
{
|
{
|
||||||
@@ -251,7 +261,8 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
blockstore_journal_check_t space_check(this);
|
blockstore_journal_check_t space_check(this);
|
||||||
if (!space_check.check_available(op, unsynced_big_write_count + 1,
|
if (!space_check.check_available(op, unsynced_big_write_count + 1,
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size, JOURNAL_STABILIZE_RESERVATION))
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size,
|
||||||
|
(dirty_it->second.state & BS_ST_INSTANT) ? JOURNAL_INSTANT_RESERVATION : JOURNAL_STABILIZE_RESERVATION))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -260,18 +271,28 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
if (loc == UINT64_MAX)
|
if (loc == UINT64_MAX)
|
||||||
{
|
{
|
||||||
// no space
|
// no space
|
||||||
if (flusher->is_active())
|
|
||||||
{
|
|
||||||
// hope that some space will be available after flush
|
|
||||||
PRIV(op)->wait_for = WAIT_FREE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
cancel_all_writes(op, dirty_it, -ENOSPC);
|
cancel_all_writes(op, dirty_it, -ENOSPC);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
if (inmemory_meta)
|
||||||
|
{
|
||||||
|
// Check once more that metadata entry is zeroed (the reverse means a bug or corruption)
|
||||||
|
uint64_t sector = (loc / (dsk.meta_block_size / dsk.clean_entry_size)) * dsk.meta_block_size;
|
||||||
|
uint64_t pos = (loc % (dsk.meta_block_size / dsk.clean_entry_size));
|
||||||
|
clean_disk_entry *entry = (clean_disk_entry*)((uint8_t*)metadata_buffer + sector + pos*dsk.clean_entry_size);
|
||||||
|
if (entry->oid.inode || entry->oid.stripe || entry->version)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Fatal error (metadata corruption or bug): tried to write object %lx:%lx v%lu"
|
||||||
|
" over a non-zero metadata entry %lu with %lx:%lx v%lu\n", op->oid.inode,
|
||||||
|
op->oid.stripe, op->version, loc, entry->oid.inode, entry->oid.stripe, entry->version
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
write_iodepth++;
|
write_iodepth++;
|
||||||
dirty_it->second.location = loc << block_order;
|
dirty_it->second.location = loc << dsk.block_order;
|
||||||
dirty_it->second.state = (dirty_it->second.state & ~BS_ST_WORKFLOW_MASK) | BS_ST_SUBMITTED;
|
dirty_it->second.state = (dirty_it->second.state & ~BS_ST_WORKFLOW_MASK) | BS_ST_SUBMITTED;
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
printf(
|
printf(
|
||||||
@@ -280,9 +301,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
data_alloc->set(loc, true);
|
data_alloc->set(loc, true);
|
||||||
uint64_t stripe_offset = (op->offset % bitmap_granularity);
|
uint64_t stripe_offset = (op->offset % dsk.bitmap_granularity);
|
||||||
uint64_t stripe_end = (op->offset + op->len) % bitmap_granularity;
|
uint64_t stripe_end = (op->offset + op->len) % dsk.bitmap_granularity;
|
||||||
// Zero fill up to bitmap_granularity
|
// Zero fill up to dsk.bitmap_granularity
|
||||||
int vcnt = 0;
|
int vcnt = 0;
|
||||||
if (stripe_offset)
|
if (stripe_offset)
|
||||||
{
|
{
|
||||||
@@ -291,13 +312,13 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ op->buf, op->len };
|
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ op->buf, op->len };
|
||||||
if (stripe_end)
|
if (stripe_end)
|
||||||
{
|
{
|
||||||
stripe_end = bitmap_granularity - stripe_end;
|
stripe_end = dsk.bitmap_granularity - stripe_end;
|
||||||
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ zero_object, stripe_end };
|
PRIV(op)->iov_zerofill[vcnt++] = (struct iovec){ zero_object, stripe_end };
|
||||||
}
|
}
|
||||||
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback
|
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe, data_fd, PRIV(op)->iov_zerofill, vcnt, data_offset + (loc << block_order) + op->offset - stripe_offset
|
sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + (loc << dsk.block_order) + op->offset - stripe_offset
|
||||||
);
|
);
|
||||||
PRIV(op)->pending_ops = 1;
|
PRIV(op)->pending_ops = 1;
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
@@ -319,9 +340,10 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
blockstore_journal_check_t space_check(this);
|
blockstore_journal_check_t space_check(this);
|
||||||
if (unsynced_big_write_count &&
|
if (unsynced_big_write_count &&
|
||||||
!space_check.check_available(op, unsynced_big_write_count,
|
!space_check.check_available(op, unsynced_big_write_count,
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size, 0)
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size, 0)
|
||||||
|| !space_check.check_available(op, 1,
|
|| !space_check.check_available(op, 1,
|
||||||
sizeof(journal_entry_small_write) + clean_entry_bitmap_size, op->len + JOURNAL_STABILIZE_RESERVATION))
|
sizeof(journal_entry_small_write) + dsk.clean_entry_bitmap_size,
|
||||||
|
op->len + ((dirty_it->second.state & BS_ST_INSTANT) ? JOURNAL_INSTANT_RESERVATION : JOURNAL_STABILIZE_RESERVATION)))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -329,7 +351,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
BS_SUBMIT_CHECK_SQES(
|
BS_SUBMIT_CHECK_SQES(
|
||||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||||
(immediate_commit != IMMEDIATE_NONE ||
|
(immediate_commit != IMMEDIATE_NONE ||
|
||||||
!journal.entry_fits(sizeof(journal_entry_small_write) + clean_entry_bitmap_size) ? 1 : 0) +
|
!journal.entry_fits(sizeof(journal_entry_small_write) + dsk.clean_entry_bitmap_size) ? 1 : 0) +
|
||||||
(op->len > 0 ? 1 : 0)
|
(op->len > 0 ? 1 : 0)
|
||||||
);
|
);
|
||||||
write_iodepth++;
|
write_iodepth++;
|
||||||
@@ -337,7 +359,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
auto cb = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
auto cb = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
if (immediate_commit == IMMEDIATE_NONE)
|
if (immediate_commit == IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
if (!journal.entry_fits(sizeof(journal_entry_small_write) + clean_entry_bitmap_size))
|
if (!journal.entry_fits(sizeof(journal_entry_small_write) + dsk.clean_entry_bitmap_size))
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal.cur_sector, op);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
}
|
}
|
||||||
@@ -349,7 +371,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
// Then pre-fill journal entry
|
// Then pre-fill journal entry
|
||||||
journal_entry_small_write *je = (journal_entry_small_write*)prefill_single_journal_entry(
|
journal_entry_small_write *je = (journal_entry_small_write*)prefill_single_journal_entry(
|
||||||
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE,
|
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE,
|
||||||
sizeof(journal_entry_small_write) + clean_entry_bitmap_size
|
sizeof(journal_entry_small_write) + dsk.clean_entry_bitmap_size
|
||||||
);
|
);
|
||||||
dirty_it->second.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
dirty_it->second.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
||||||
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
||||||
@@ -361,14 +383,14 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
// Figure out where data will be
|
// Figure out where data will be
|
||||||
journal.next_free = (journal.next_free + op->len) <= journal.len ? journal.next_free : journal_block_size;
|
journal.next_free = (journal.next_free + op->len) <= journal.len ? journal.next_free : dsk.journal_block_size;
|
||||||
je->oid = op->oid;
|
je->oid = op->oid;
|
||||||
je->version = op->version;
|
je->version = op->version;
|
||||||
je->offset = op->offset;
|
je->offset = op->offset;
|
||||||
je->len = op->len;
|
je->len = op->len;
|
||||||
je->data_offset = journal.next_free;
|
je->data_offset = journal.next_free;
|
||||||
je->crc32_data = crc32c(0, op->buf, op->len);
|
je->crc32_data = crc32c(0, op->buf, op->len);
|
||||||
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), clean_entry_bitmap_size);
|
memcpy((void*)(je+1), (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), dsk.clean_entry_bitmap_size);
|
||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
if (immediate_commit != IMMEDIATE_NONE)
|
if (immediate_commit != IMMEDIATE_NONE)
|
||||||
@@ -387,7 +409,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
data2->iov = (struct iovec){ op->buf, op->len };
|
data2->iov = (struct iovec){ op->buf, op->len };
|
||||||
data2->callback = cb;
|
data2->callback = cb;
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
sqe2, journal.fd, &data2->iov, 1, journal.offset + journal.next_free
|
sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free
|
||||||
);
|
);
|
||||||
PRIV(op)->pending_ops++;
|
PRIV(op)->pending_ops++;
|
||||||
}
|
}
|
||||||
@@ -400,7 +422,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
journal.next_free += op->len;
|
journal.next_free += op->len;
|
||||||
if (journal.next_free >= journal.len)
|
if (journal.next_free >= journal.len)
|
||||||
{
|
{
|
||||||
journal.next_free = journal_block_size;
|
journal.next_free = dsk.journal_block_size;
|
||||||
}
|
}
|
||||||
if (!PRIV(op)->pending_ops)
|
if (!PRIV(op)->pending_ops)
|
||||||
{
|
{
|
||||||
@@ -432,15 +454,22 @@ int blockstore_impl_t::continue_write(blockstore_op_t *op)
|
|||||||
resume_2:
|
resume_2:
|
||||||
// Only for the immediate_commit mode: prepare and submit big_write journal entry
|
// Only for the immediate_commit mode: prepare and submit big_write journal entry
|
||||||
{
|
{
|
||||||
BS_SUBMIT_CHECK_SQES(1);
|
|
||||||
auto dirty_it = dirty_db.find((obj_ver_id){
|
auto dirty_it = dirty_db.find((obj_ver_id){
|
||||||
.oid = op->oid,
|
.oid = op->oid,
|
||||||
.version = op->version,
|
.version = op->version,
|
||||||
});
|
});
|
||||||
assert(dirty_it != dirty_db.end());
|
assert(dirty_it != dirty_db.end());
|
||||||
|
blockstore_journal_check_t space_check(this);
|
||||||
|
if (!space_check.check_available(op, 1,
|
||||||
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size,
|
||||||
|
((dirty_it->second.state & BS_ST_INSTANT) ? JOURNAL_INSTANT_RESERVATION : JOURNAL_STABILIZE_RESERVATION)))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
||||||
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size
|
||||||
);
|
);
|
||||||
dirty_it->second.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
dirty_it->second.journal_sector = journal.sector_info[journal.cur_sector].offset;
|
||||||
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
||||||
@@ -456,7 +485,7 @@ resume_2:
|
|||||||
je->offset = op->offset;
|
je->offset = op->offset;
|
||||||
je->len = op->len;
|
je->len = op->len;
|
||||||
je->location = dirty_it->second.location;
|
je->location = dirty_it->second.location;
|
||||||
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), clean_entry_bitmap_size);
|
memcpy((void*)(je+1), (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), dsk.clean_entry_bitmap_size);
|
||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
prepare_journal_sector_write(journal.cur_sector, op);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
@@ -569,10 +598,7 @@ void blockstore_impl_t::handle_write_event(ring_data_t *data, blockstore_op_t *o
|
|||||||
if (data->res != data->iov.iov_len)
|
if (data->res != data->iov.iov_len)
|
||||||
{
|
{
|
||||||
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
||||||
throw std::runtime_error(
|
disk_error_abort("data write", data->res, data->iov.iov_len);
|
||||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PRIV(op)->pending_ops--;
|
PRIV(op)->pending_ops--;
|
||||||
assert(PRIV(op)->pending_ops >= 0);
|
assert(PRIV(op)->pending_ops >= 0);
|
||||||
@@ -627,14 +653,14 @@ int blockstore_impl_t::dequeue_del(blockstore_op_t *op)
|
|||||||
});
|
});
|
||||||
assert(dirty_it != dirty_db.end());
|
assert(dirty_it != dirty_db.end());
|
||||||
blockstore_journal_check_t space_check(this);
|
blockstore_journal_check_t space_check(this);
|
||||||
if (!space_check.check_available(op, 1, sizeof(journal_entry_del), JOURNAL_STABILIZE_RESERVATION))
|
if (!space_check.check_available(op, 1, sizeof(journal_entry_del), JOURNAL_INSTANT_RESERVATION))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||||
BS_SUBMIT_CHECK_SQES(
|
BS_SUBMIT_CHECK_SQES(
|
||||||
(immediate_commit != IMMEDIATE_NONE ||
|
(immediate_commit != IMMEDIATE_NONE ||
|
||||||
(journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
(dsk.journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty) ? 1 : 0
|
journal.sector_info[journal.cur_sector].dirty) ? 1 : 0
|
||||||
);
|
);
|
||||||
if (write_iodepth >= max_write_iodepth)
|
if (write_iodepth >= max_write_iodepth)
|
||||||
@@ -645,7 +671,7 @@ int blockstore_impl_t::dequeue_del(blockstore_op_t *op)
|
|||||||
// Prepare journal sector write
|
// Prepare journal sector write
|
||||||
if (immediate_commit == IMMEDIATE_NONE)
|
if (immediate_commit == IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
if ((journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
if ((dsk.journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal.cur_sector, op);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
|
217
src/cli.cpp
217
src/cli.cpp
@@ -12,11 +12,87 @@
|
|||||||
#include "epoll_manager.h"
|
#include "epoll_manager.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
#include "base64.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
static const char *exe_name = NULL;
|
static const char *exe_name = NULL;
|
||||||
|
|
||||||
static void help();
|
static const char* help_text =
|
||||||
|
"Vitastor command-line tool\n"
|
||||||
|
"(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n"
|
||||||
|
"\n"
|
||||||
|
"COMMANDS:\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli status\n"
|
||||||
|
" Show cluster status\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli df\n"
|
||||||
|
" Show pool space statistics\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]\n"
|
||||||
|
" List images (only matching <glob> patterns if passed).\n"
|
||||||
|
" -p|--pool POOL Filter images by pool ID or name\n"
|
||||||
|
" -l|--long Also report allocated size and I/O statistics\n"
|
||||||
|
" --del Also include delete operation statistics\n"
|
||||||
|
" --sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)\n"
|
||||||
|
" -r|--reverse Sort in descending order\n"
|
||||||
|
" -n|--count N Only list first N items\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
|
||||||
|
" Create an image. You may use K/M/G/T suffixes for <size>. If --parent is specified,\n"
|
||||||
|
" a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n"
|
||||||
|
" Pool must be specified if there is more than one pool.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>\n"
|
||||||
|
"vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
|
||||||
|
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]\n"
|
||||||
|
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
||||||
|
" If the new size is smaller than the old size, extra data will be purged.\n"
|
||||||
|
" You should resize file system in the image, if present, before shrinking it.\n"
|
||||||
|
" -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli rm <from> [<to>] [--writers-stopped]\n"
|
||||||
|
" Remove <from> or all layers between <from> and <to> (<to> must be a child of <from>),\n"
|
||||||
|
" rebasing all their children accordingly. --writers-stopped allows merging to be a bit\n"
|
||||||
|
" more effective in case of a single 'slim' read-write child and 'fat' removed parent:\n"
|
||||||
|
" the child is merged into parent and parent is renamed to child in that case.\n"
|
||||||
|
" In other cases parent layers are always merged into children.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli flatten <layer>\n"
|
||||||
|
" Flatten a layer, i.e. merge data and detach it from parents.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]\n"
|
||||||
|
" Remove inode data without changing metadata.\n"
|
||||||
|
" --wait-list Retrieve full objects listings before starting to remove objects.\n"
|
||||||
|
" Requires more memory, but allows to show correct removal progress.\n"
|
||||||
|
" --min-offset Purge only data starting with specified offset.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli merge-data <from> <to> [--target <target>]\n"
|
||||||
|
" Merge layer data without changing metadata. Merge <from>..<to> to <target>.\n"
|
||||||
|
" <to> must be a child of <from> and <target> may be one of the layers between\n"
|
||||||
|
" <from> and <to>, including <from> and <to>.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli alloc-osd\n"
|
||||||
|
" Allocate a new OSD number and reserve it by creating empty /osd/stats/<n> key.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]\n"
|
||||||
|
" Remove metadata and configuration for specified OSD(s) from etcd.\n"
|
||||||
|
" Refuses to remove OSDs with data without --force and --allow-data-loss.\n"
|
||||||
|
" With --dry-run only checks if deletion is possible without data loss and\n"
|
||||||
|
" redundancy degradation.\n"
|
||||||
|
"\n"
|
||||||
|
"Use vitastor-cli --help <command> for command details or vitastor-cli --help --all for all details.\n"
|
||||||
|
"\n"
|
||||||
|
"GLOBAL OPTIONS:\n"
|
||||||
|
" --etcd_address <etcd_address>\n"
|
||||||
|
" --iodepth N Send N operations in parallel to each OSD when possible (default 32)\n"
|
||||||
|
" --parallel_osds M Work with M osds in parallel when possible (default 4)\n"
|
||||||
|
" --progress 1|0 Report progress (default 1)\n"
|
||||||
|
" --cas 1|0 Use CAS writes for flatten, merge, rm (default is decide automatically)\n"
|
||||||
|
" --no-color Disable colored output\n"
|
||||||
|
" --json JSON output\n"
|
||||||
|
;
|
||||||
|
|
||||||
static json11::Json::object parse_args(int narg, const char *args[])
|
static json11::Json::object parse_args(int narg, const char *args[])
|
||||||
{
|
{
|
||||||
@@ -25,42 +101,47 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
|||||||
cfg["progress"] = "1";
|
cfg["progress"] = "1";
|
||||||
for (int i = 1; i < narg; i++)
|
for (int i = 1; i < narg; i++)
|
||||||
{
|
{
|
||||||
if (!strcmp(args[i], "-h") || !strcmp(args[i], "--help"))
|
if (args[i][0] == '-' && args[i][1] == 'h' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
help();
|
cfg["help"] = "1";
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 'l')
|
else if (args[i][0] == '-' && args[i][1] == 'l' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["long"] = "1";
|
cfg["long"] = "1";
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 'n')
|
else if (args[i][0] == '-' && args[i][1] == 'n' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["count"] = args[++i];
|
cfg["count"] = args[++i];
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 'p')
|
else if (args[i][0] == '-' && args[i][1] == 'p' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["pool"] = args[++i];
|
cfg["pool"] = args[++i];
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 's')
|
else if (args[i][0] == '-' && args[i][1] == 's' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["size"] = args[++i];
|
cfg["size"] = args[++i];
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 'r')
|
else if (args[i][0] == '-' && args[i][1] == 'r' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["reverse"] = "1";
|
cfg["reverse"] = "1";
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == 'f')
|
else if (args[i][0] == '-' && args[i][1] == 'f' && args[i][2] == 0)
|
||||||
{
|
{
|
||||||
cfg["force"] = "1";
|
cfg["force"] = "1";
|
||||||
}
|
}
|
||||||
else if (args[i][0] == '-' && args[i][1] == '-')
|
else if (args[i][0] == '-' && args[i][1] == '-')
|
||||||
{
|
{
|
||||||
const char *opt = args[i]+2;
|
const char *opt = args[i]+2;
|
||||||
cfg[opt] = i == narg-1 || !strcmp(opt, "json") || !strcmp(opt, "wait-list") ||
|
cfg[opt] = i == narg-1 || !strcmp(opt, "json") ||
|
||||||
!strcmp(opt, "long") || !strcmp(opt, "del") || !strcmp(opt, "no-color") ||
|
!strcmp(opt, "wait-list") || !strcmp(opt, "wait_list") ||
|
||||||
|
!strcmp(opt, "long") || !strcmp(opt, "del") ||
|
||||||
|
!strcmp(opt, "no-color") || !strcmp(opt, "no_color") ||
|
||||||
!strcmp(opt, "readonly") || !strcmp(opt, "readwrite") ||
|
!strcmp(opt, "readonly") || !strcmp(opt, "readwrite") ||
|
||||||
!strcmp(opt, "force") || !strcmp(opt, "reverse") ||
|
!strcmp(opt, "force") || !strcmp(opt, "reverse") ||
|
||||||
!strcmp(opt, "writers-stopped") && strcmp("1", args[i+1]) != 0
|
!strcmp(opt, "allow-data-loss") || !strcmp(opt, "allow_data_loss") ||
|
||||||
|
!strcmp(opt, "dry-run") || !strcmp(opt, "dry_run") ||
|
||||||
|
!strcmp(opt, "help") || !strcmp(opt, "all") ||
|
||||||
|
(!strcmp(opt, "writers-stopped") || !strcmp(opt, "writers_stopped")) && strcmp("1", args[i+1]) != 0
|
||||||
? "1" : args[++i];
|
? "1" : args[++i];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -76,98 +157,17 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
|||||||
cmd.push_back("rm-data");
|
cmd.push_back("rm-data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!cmd.size() || cfg["help"].bool_value())
|
||||||
|
{
|
||||||
|
print_help(help_text, "vitastor-cli", cmd.size() ? cmd[0].string_value() : "", cfg["all"].bool_value());
|
||||||
|
}
|
||||||
cfg["command"] = cmd;
|
cfg["command"] = cmd;
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help()
|
|
||||||
{
|
|
||||||
printf(
|
|
||||||
"Vitastor command-line tool\n"
|
|
||||||
"(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n"
|
|
||||||
"\n"
|
|
||||||
"USAGE:\n"
|
|
||||||
"%s status\n"
|
|
||||||
" Show cluster status\n"
|
|
||||||
"\n"
|
|
||||||
"%s df\n"
|
|
||||||
" Show pool space statistics\n"
|
|
||||||
"\n"
|
|
||||||
"%s ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]\n"
|
|
||||||
" List images (only matching <glob> patterns if passed).\n"
|
|
||||||
" -p|--pool POOL Filter images by pool ID or name\n"
|
|
||||||
" -l|--long Also report allocated size and I/O statistics\n"
|
|
||||||
" --del Also include delete operation statistics\n"
|
|
||||||
" --sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)\n"
|
|
||||||
" -r|--reverse Sort in descending order\n"
|
|
||||||
" -n|--count N Only list first N items\n"
|
|
||||||
"\n"
|
|
||||||
"%s create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
|
|
||||||
" Create an image. You may use K/M/G/T suffixes for <size>. If --parent is specified,\n"
|
|
||||||
" a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n"
|
|
||||||
" Pool must be specified if there is more than one pool.\n"
|
|
||||||
"\n"
|
|
||||||
"%s create --snapshot <snapshot> [-p|--pool <id|name>] <image>\n"
|
|
||||||
"%s snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
|
|
||||||
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
|
|
||||||
"\n"
|
|
||||||
"%s modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]\n"
|
|
||||||
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
|
||||||
" If the new size is smaller than the old size, extra data will be purged.\n"
|
|
||||||
" You should resize file system in the image, if present, before shrinking it.\n"
|
|
||||||
" -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n"
|
|
||||||
"\n"
|
|
||||||
"%s rm <from> [<to>] [--writers-stopped]\n"
|
|
||||||
" Remove <from> or all layers between <from> and <to> (<to> must be a child of <from>),\n"
|
|
||||||
" rebasing all their children accordingly. --writers-stopped allows merging to be a bit\n"
|
|
||||||
" more effective in case of a single 'slim' read-write child and 'fat' removed parent:\n"
|
|
||||||
" the child is merged into parent and parent is renamed to child in that case.\n"
|
|
||||||
" In other cases parent layers are always merged into children.\n"
|
|
||||||
"\n"
|
|
||||||
"%s flatten <layer>\n"
|
|
||||||
" Flatten a layer, i.e. merge data and detach it from parents.\n"
|
|
||||||
"\n"
|
|
||||||
"%s rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]\n"
|
|
||||||
" Remove inode data without changing metadata.\n"
|
|
||||||
" --wait-list Retrieve full objects listings before starting to remove objects.\n"
|
|
||||||
" Requires more memory, but allows to show correct removal progress.\n"
|
|
||||||
" --min-offset Purge only data starting with specified offset.\n"
|
|
||||||
"\n"
|
|
||||||
"%s merge-data <from> <to> [--target <target>]\n"
|
|
||||||
" Merge layer data without changing metadata. Merge <from>..<to> to <target>.\n"
|
|
||||||
" <to> must be a child of <from> and <target> may be one of the layers between\n"
|
|
||||||
" <from> and <to>, including <from> and <to>.\n"
|
|
||||||
"\n"
|
|
||||||
"%s alloc-osd\n"
|
|
||||||
" Allocate a new OSD number and reserve it by creating empty /osd/stats/<n> key.\n"
|
|
||||||
"%s simple-offsets <device>\n"
|
|
||||||
" Calculate offsets for simple&stupid (no superblock) OSD deployment. Options:\n"
|
|
||||||
" --object_size 128k Set blockstore block size\n"
|
|
||||||
" --bitmap_granularity 4k Set bitmap granularity\n"
|
|
||||||
" --journal_size 16M Set journal size\n"
|
|
||||||
" --device_block_size 4k Set device block size\n"
|
|
||||||
" --journal_offset 0 Set journal offset\n"
|
|
||||||
" --device_size 0 Set device size\n"
|
|
||||||
" --format text Result format: json, options, env, or text\n"
|
|
||||||
"\n"
|
|
||||||
"GLOBAL OPTIONS:\n"
|
|
||||||
" --etcd_address <etcd_address>\n"
|
|
||||||
" --iodepth N Send N operations in parallel to each OSD when possible (default 32)\n"
|
|
||||||
" --parallel_osds M Work with M osds in parallel when possible (default 4)\n"
|
|
||||||
" --progress 1|0 Report progress (default 1)\n"
|
|
||||||
" --cas 1|0 Use CAS writes for flatten, merge, rm (default is decide automatically)\n"
|
|
||||||
" --no-color Disable colored output\n"
|
|
||||||
" --json JSON output\n"
|
|
||||||
,
|
|
||||||
exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name,
|
|
||||||
exe_name, exe_name, exe_name, exe_name, exe_name, exe_name
|
|
||||||
);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int run(cli_tool_t *p, json11::Json::object cfg)
|
static int run(cli_tool_t *p, json11::Json::object cfg)
|
||||||
{
|
{
|
||||||
cli_result_t result;
|
cli_result_t result = {};
|
||||||
p->parse_config(cfg);
|
p->parse_config(cfg);
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
cfg.erase("command");
|
cfg.erase("command");
|
||||||
@@ -235,6 +235,16 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
// Delete inode data
|
// Delete inode data
|
||||||
action_cb = p->start_rm_data(cfg);
|
action_cb = p->start_rm_data(cfg);
|
||||||
}
|
}
|
||||||
|
else if (cmd[0] == "rm-osd")
|
||||||
|
{
|
||||||
|
// Delete OSD metadata from etcd
|
||||||
|
if (cmd.size() > 1)
|
||||||
|
{
|
||||||
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
|
cfg["osd_id"] = cmd;
|
||||||
|
}
|
||||||
|
action_cb = p->start_rm_osd(cfg);
|
||||||
|
}
|
||||||
else if (cmd[0] == "merge-data")
|
else if (cmd[0] == "merge-data")
|
||||||
{
|
{
|
||||||
// Merge layer data without affecting metadata
|
// Merge layer data without affecting metadata
|
||||||
@@ -271,15 +281,6 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
// Allocate a new OSD number
|
// Allocate a new OSD number
|
||||||
action_cb = p->start_alloc_osd(cfg);
|
action_cb = p->start_alloc_osd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "simple-offsets")
|
|
||||||
{
|
|
||||||
// Calculate offsets for simple & stupid OSD deployment without superblock
|
|
||||||
if (cmd.size() > 1)
|
|
||||||
{
|
|
||||||
cfg["device"] = cmd[1];
|
|
||||||
}
|
|
||||||
action_cb = p->simple_offsets(cfg);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = { .err = EINVAL, .text = "unknown command: "+cmd[0].string_value() };
|
result = { .err = EINVAL, .text = "unknown command: "+cmd[0].string_value() };
|
||||||
|
@@ -39,12 +39,13 @@ public:
|
|||||||
ring_loop_t *ringloop = NULL;
|
ring_loop_t *ringloop = NULL;
|
||||||
epoll_manager_t *epmgr = NULL;
|
epoll_manager_t *epmgr = NULL;
|
||||||
cluster_client_t *cli = NULL;
|
cluster_client_t *cli = NULL;
|
||||||
|
bool no_recovery = false, no_rebalance = false, readonly = false;
|
||||||
|
|
||||||
int waiting = 0;
|
int waiting = 0;
|
||||||
cli_result_t etcd_err;
|
cli_result_t etcd_err;
|
||||||
json11::Json etcd_result;
|
json11::Json etcd_result;
|
||||||
|
|
||||||
void parse_config(json11::Json cfg);
|
void parse_config(json11::Json::object & cfg);
|
||||||
|
|
||||||
void change_parent(inode_t cur, inode_t new_parent, cli_result_t *result);
|
void change_parent(inode_t cur, inode_t new_parent, cli_result_t *result);
|
||||||
inode_config_t* get_inode_cfg(const std::string & name);
|
inode_config_t* get_inode_cfg(const std::string & name);
|
||||||
@@ -63,8 +64,8 @@ public:
|
|||||||
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_rm(json11::Json);
|
std::function<bool(cli_result_t &)> start_rm(json11::Json);
|
||||||
|
std::function<bool(cli_result_t &)> start_rm_osd(json11::Json cfg);
|
||||||
std::function<bool(cli_result_t &)> start_alloc_osd(json11::Json cfg);
|
std::function<bool(cli_result_t &)> start_alloc_osd(json11::Json cfg);
|
||||||
std::function<bool(cli_result_t &)> simple_offsets(json11::Json cfg);
|
|
||||||
|
|
||||||
// Should be called like loop_and_wait(start_status(), <completion callback>)
|
// Should be called like loop_and_wait(start_status(), <completion callback>)
|
||||||
void loop_and_wait(std::function<bool(cli_result_t &)> loop_cb, std::function<void(const cli_result_t &)> complete_cb);
|
void loop_and_wait(std::function<bool(cli_result_t &)> loop_cb, std::function<void(const cli_result_t &)> complete_cb);
|
||||||
@@ -72,12 +73,8 @@ public:
|
|||||||
void etcd_txn(json11::Json txn);
|
void etcd_txn(json11::Json txn);
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t parse_size(std::string size_str);
|
|
||||||
|
|
||||||
std::string print_table(json11::Json items, json11::Json header, bool use_esc);
|
std::string print_table(json11::Json items, json11::Json header, bool use_esc);
|
||||||
|
|
||||||
std::string format_size(uint64_t size, bool nobytes = false);
|
|
||||||
|
|
||||||
std::string format_lat(uint64_t lat);
|
std::string format_lat(uint64_t lat);
|
||||||
|
|
||||||
std::string format_q(double depth);
|
std::string format_q(double depth);
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "base64.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
#include "base64.h"
|
#include "str_util.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
|
||||||
@@ -100,9 +100,20 @@ inode_config_t* cli_tool_t::get_inode_cfg(const std::string & name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_tool_t::parse_config(json11::Json cfg)
|
void cli_tool_t::parse_config(json11::Json::object & cfg)
|
||||||
{
|
{
|
||||||
color = !cfg["no-color"].bool_value();
|
for (auto kv_it = cfg.begin(); kv_it != cfg.end();)
|
||||||
|
{
|
||||||
|
// Translate all options with - to _
|
||||||
|
if (kv_it->first.find("-") != std::string::npos)
|
||||||
|
{
|
||||||
|
cfg[str_replace(kv_it->first, "-", "_")] = kv_it->second;
|
||||||
|
cfg.erase(kv_it++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
kv_it++;
|
||||||
|
}
|
||||||
|
color = !cfg["no_color"].bool_value();
|
||||||
json_output = cfg["json"].bool_value();
|
json_output = cfg["json"].bool_value();
|
||||||
iodepth = cfg["iodepth"].uint64_value();
|
iodepth = cfg["iodepth"].uint64_value();
|
||||||
if (!iodepth)
|
if (!iodepth)
|
||||||
@@ -112,7 +123,7 @@ void cli_tool_t::parse_config(json11::Json cfg)
|
|||||||
parallel_osds = 4;
|
parallel_osds = 4;
|
||||||
log_level = cfg["log_level"].int64_value();
|
log_level = cfg["log_level"].int64_value();
|
||||||
progress = cfg["progress"].uint64_value() ? true : false;
|
progress = cfg["progress"].uint64_value() ? true : false;
|
||||||
list_first = cfg["wait-list"].uint64_value() ? true : false;
|
list_first = cfg["wait_list"].uint64_value() ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cli_result_looper_t
|
struct cli_result_looper_t
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "base64.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
// Create an image, snapshot or clone
|
// Create an image, snapshot or clone
|
||||||
//
|
//
|
||||||
@@ -276,7 +276,8 @@ resume_4:
|
|||||||
new_id = 1+INODE_NO_POOL(kv.value.uint64_value());
|
new_id = 1+INODE_NO_POOL(kv.value.uint64_value());
|
||||||
max_id_mod_rev = kv.mod_revision;
|
max_id_mod_rev = kv.mod_revision;
|
||||||
}
|
}
|
||||||
auto ino_it = parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(new_pool_id, 0));
|
// Also check existing inodes - for the case when some inodes are created without changing /index/maxid
|
||||||
|
auto ino_it = parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(new_pool_id+1, 0));
|
||||||
if (ino_it != parent->cli->st_cli.inode_config.begin())
|
if (ino_it != parent->cli->st_cli.inode_config.begin())
|
||||||
{
|
{
|
||||||
ino_it--;
|
ino_it--;
|
||||||
@@ -506,34 +507,6 @@ resume_3:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t parse_size(std::string size_str)
|
|
||||||
{
|
|
||||||
if (!size_str.length())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint64_t mul = 1;
|
|
||||||
char type_char = tolower(size_str[size_str.length()-1]);
|
|
||||||
if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't')
|
|
||||||
{
|
|
||||||
if (type_char == 'k')
|
|
||||||
mul = (uint64_t)1<<10;
|
|
||||||
else if (type_char == 'm')
|
|
||||||
mul = (uint64_t)1<<20;
|
|
||||||
else if (type_char == 'g')
|
|
||||||
mul = (uint64_t)1<<30;
|
|
||||||
else /*if (type_char == 't')*/
|
|
||||||
mul = (uint64_t)1<<40;
|
|
||||||
size_str = size_str.substr(0, size_str.length()-1);
|
|
||||||
}
|
|
||||||
uint64_t size = json11::Json(size_str).uint64_value() * mul;
|
|
||||||
if (size == 0 && size_str != "0" && (size_str != "" || mul != 1))
|
|
||||||
{
|
|
||||||
return UINT64_MAX;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
||||||
{
|
{
|
||||||
auto image_creator = new image_creator_t();
|
auto image_creator = new image_creator_t();
|
||||||
@@ -544,7 +517,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
|||||||
image_creator->force_size = cfg["force_size"].bool_value();
|
image_creator->force_size = cfg["force_size"].bool_value();
|
||||||
if (cfg["image_meta"].is_object())
|
if (cfg["image_meta"].is_object())
|
||||||
{
|
{
|
||||||
image_creator->new_meta = cfg["image-meta"];
|
image_creator->new_meta = cfg["image_meta"];
|
||||||
}
|
}
|
||||||
if (cfg["snapshot"].string_value() != "")
|
if (cfg["snapshot"].string_value() != "")
|
||||||
{
|
{
|
||||||
@@ -553,8 +526,9 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
|||||||
image_creator->new_parent = cfg["parent"].string_value();
|
image_creator->new_parent = cfg["parent"].string_value();
|
||||||
if (cfg["size"].string_value() != "")
|
if (cfg["size"].string_value() != "")
|
||||||
{
|
{
|
||||||
image_creator->size = parse_size(cfg["size"].string_value());
|
bool ok;
|
||||||
if (image_creator->size == UINT64_MAX)
|
image_creator->size = parse_size(cfg["size"].string_value(), &ok);
|
||||||
|
if (!ok)
|
||||||
{
|
{
|
||||||
return [size = cfg["size"].string_value()](cli_result_t & result)
|
return [size = cfg["size"].string_value()](cli_result_t & result)
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user