forked from vitalif/vitastor
Compare commits
157 Commits
master
...
rm-left-on
Author | SHA1 | Date | |
---|---|---|---|
0f964d62db | |||
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 | |||
d8313e939a | |||
3e92c3f082 | |||
82b9f4c52d | |||
2bdf415eb3 |
@@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(vitastor)
|
||||
|
||||
set(VERSION "0.6.17")
|
||||
set(VERSION "0.8.3")
|
||||
|
||||
add_subdirectory(src)
|
||||
|
@@ -58,6 +58,7 @@ Vitastor поддерживает QEMU-драйвер, протоколы NBD и
|
||||
- [Метаданные образов в etcd](docs/config/inode.ru.md)
|
||||
- Использование
|
||||
- [vitastor-cli](docs/usage/cli.ru.md) (консольный интерфейс)
|
||||
- [vitastor-disk](docs/usage/disk.ru.md) (управление дисками)
|
||||
- [fio](docs/usage/fio.ru.md) для тестов производительности
|
||||
- [NBD](docs/usage/nbd.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 или более поздней версии. Так сделано в целях
|
||||
совместимости с таким ПО, как QEMU и fio.
|
||||
|
||||
Вы можете найти полный текст 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.txt](VNPL-1.1.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)
|
||||
- Usage
|
||||
- [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
|
||||
- [NBD](docs/usage/nbd.en.md) for kernel mounts
|
||||
- [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.
|
||||
|
||||
"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
|
||||
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
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
|
@@ -18,15 +18,19 @@ ENV CSI_ENDPOINT=""
|
||||
|
||||
RUN apt-get update && \
|
||||
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 "APT::Install-Recommends false;" > /etc/apt/apt.conf) && \
|
||||
apt-get update && \
|
||||
apt-get install -y e2fsprogs xfsprogs vitastor kmod && \
|
||||
apt-get install -y e2fsprogs xfsprogs kmod && \
|
||||
apt-get clean && \
|
||||
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
||||
|
||||
COPY --from=build /app/vitastor-csi /bin/
|
||||
|
||||
RUN (echo deb http://vitastor.io/debian 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"]
|
||||
|
@@ -1,4 +1,4 @@
|
||||
VERSION ?= v0.6.17
|
||||
VERSION ?= v0.8.3
|
||||
|
||||
all: build push
|
||||
|
||||
|
@@ -49,7 +49,7 @@ spec:
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: vitalif/vitastor-csi:v0.6.17
|
||||
image: vitalif/vitastor-csi:v0.8.3
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
@@ -102,7 +102,7 @@ spec:
|
||||
- "--health-port=9898"
|
||||
env:
|
||||
- name: CSI_ENDPOINT
|
||||
value: unix://csi/csi.sock
|
||||
value: unix:///csi/csi.sock
|
||||
volumeMounts:
|
||||
- mountPath: /csi
|
||||
name: socket-dir
|
||||
|
@@ -116,7 +116,7 @@ spec:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
image: vitalif/vitastor-csi:v0.6.17
|
||||
image: vitalif/vitastor-csi:v0.8.3
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
@@ -5,7 +5,7 @@ package vitastor
|
||||
|
||||
const (
|
||||
vitastorCSIDriverName = "csi.vitastor.io"
|
||||
vitastorCSIDriverVersion = "0.6.17"
|
||||
vitastorCSIDriverVersion = "0.8.3"
|
||||
)
|
||||
|
||||
// Config struct fills the parameters of request or user input
|
||||
|
16
debian/changelog
vendored
16
debian/changelog
vendored
@@ -1,4 +1,18 @@
|
||||
vitastor (0.6.17-1) unstable; urgency=medium
|
||||
vitastor (0.8.3-1) unstable; urgency=medium
|
||||
|
||||
* Bugfixes
|
||||
|
||||
-- Vitaliy Filippov <vitalif@yourcmc.ru> Fri, 03 Jun 2022 02:09:44 +0300
|
||||
|
||||
vitastor (0.8.3-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
|
||||
* Bugfixes
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@@ -2,7 +2,7 @@ Source: vitastor
|
||||
Section: admin
|
||||
Priority: optional
|
||||
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
|
||||
Homepage: https://vitastor.io/
|
||||
Rules-Requires-Root: no
|
||||
@@ -18,7 +18,7 @@ Description: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
Package: vitastor-osd
|
||||
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
|
||||
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-nfs
|
||||
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/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-disk
|
||||
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 build-dep 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
|
||||
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/; \
|
||||
cd /root/fio-build/; \
|
||||
rm -rf /root/fio-build/*; \
|
||||
@@ -33,8 +34,8 @@ RUN set -e -x; \
|
||||
mkdir -p /root/packages/vitastor-$REL; \
|
||||
rm -rf /root/packages/vitastor-$REL/*; \
|
||||
cd /root/packages/vitastor-$REL; \
|
||||
cp -r /root/vitastor vitastor-0.6.17; \
|
||||
cd vitastor-0.6.17; \
|
||||
cp -r /root/vitastor vitastor-0.8.3; \
|
||||
cd vitastor-0.8.3; \
|
||||
ln -s /root/fio-build/fio-*/ ./fio; \
|
||||
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; \
|
||||
@@ -47,8 +48,8 @@ RUN set -e -x; \
|
||||
rm -rf a b; \
|
||||
echo "dep:fio=$FIO" > debian/fio_version; \
|
||||
cd /root/packages/vitastor-$REL; \
|
||||
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.6.17.orig.tar.xz vitastor-0.6.17; \
|
||||
cd vitastor-0.6.17; \
|
||||
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.8.3.orig.tar.xz vitastor-0.8.3; \
|
||||
cd vitastor-0.8.3; \
|
||||
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
||||
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
|
||||
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)
|
||||
- [bitmap_granularity](#bitmap_granularity)
|
||||
- [immediate_commit](#immediate_commit)
|
||||
- [client_dirty_limit](#client_dirty_limit)
|
||||
|
||||
## block_size
|
||||
|
||||
- Type: integer
|
||||
- Default: 131072
|
||||
|
||||
Size of objects (data blocks) into which all physical and virtual drives are
|
||||
subdivided in Vitastor. One of current main settings in Vitastor, affects
|
||||
memory usage, write amplification and I/O load distribution effectiveness.
|
||||
Size of objects (data blocks) into which all physical and virtual drives
|
||||
(within a pool) are subdivided in Vitastor. One of current main settings
|
||||
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,
|
||||
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
||||
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
|
||||
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
|
||||
(bitmap_granularity) bytes.
|
||||
|
||||
This parameter can't be changed after OSD initialization without losing
|
||||
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.
|
||||
Can't be smaller than the OSD data device sector.
|
||||
|
||||
## 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
|
||||
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
|
||||
OSD command line or configuration. Setting it to "all" or "small" requires
|
||||
enabling disable_journal_fsync and disable_meta_fsync, setting it to "all"
|
||||
also requires enabling disable_data_fsync.
|
||||
Setting this parameter to "all" or "small" in OSD parameters requires enabling
|
||||
disable_journal_fsync and disable_meta_fsync, setting it to "all" also requires
|
||||
enabling disable_data_fsync.
|
||||
|
||||
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
||||
SSDs with supercapacitor-based power loss protection (nonvolatile
|
||||
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
|
||||
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 с разными значениями данных параметров (например, SSD и гибридные SSD+HDD
|
||||
OSD) могут сосуществовать в одном кластере Vitastor в разных пулах. Один пул
|
||||
может включать только OSD с одинаковыми настройками этих параметров.
|
||||
|
||||
Данные параметры, отличаясь от значения по умолчанию, должны также быть заданы
|
||||
в etcd, чтобы клиенты могли узнать их значение, либо в глобальной конфигурации
|
||||
/vitastor/config/global, либо в настройках пулов. Настройки пула переопределяют
|
||||
глобальное значение. Если значение в настройках пула не будет соответствовать
|
||||
конфигурации OSD, OSD откажется запускать PG данного пула.
|
||||
|
||||
- [block_size](#block_size)
|
||||
- [bitmap_granularity](#bitmap_granularity)
|
||||
- [immediate_commit](#immediate_commit)
|
||||
- [client_dirty_limit](#client_dirty_limit)
|
||||
|
||||
## block_size
|
||||
|
||||
@@ -20,24 +29,15 @@
|
||||
- Значение по умолчанию: 131072
|
||||
|
||||
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
||||
диски в Vitastor. Одна из ключевых на данный момент настроек, влияет на
|
||||
потребление памяти, объём избыточной записи (write amplification) и
|
||||
эффективность распределения нагрузки по OSD.
|
||||
диски в Vitastor (в рамках каждого пула). Одна из ключевых на данный момент
|
||||
настроек, влияет на потребление памяти, объём избыточной записи (write
|
||||
amplification) и эффективность распределения нагрузки по OSD.
|
||||
|
||||
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
||||
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
||||
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
||||
среднем увеличит WA.
|
||||
|
||||
OSD с разными размерами блока (например, SSD и SSD+HDD OSD) на данный
|
||||
момент могут сосуществовать в рамках одного etcd только в виде двух независимых
|
||||
кластеров Vitastor с разными etcd_prefix.
|
||||
|
||||
Также размер блока нельзя менять после инициализации OSD без потери данных.
|
||||
|
||||
Если вы меняете размер блока, обязательно прописывайте его в etcd в
|
||||
/vitastor/config/global, дабы все клиенты его знали.
|
||||
|
||||
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
||||
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
||||
стандартном 128 КБ блоке.
|
||||
@@ -52,13 +52,7 @@ OSD с разными размерами блока (например, SSD и SS
|
||||
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
||||
по 2 бита на каждые (bitmap_granularity) байт.
|
||||
|
||||
Данный параметр нельзя менять после инициализации OSD без потери данных.
|
||||
Также он фиксирован для всего кластера Vitastor, т.е. разные значения
|
||||
не могут сосуществовать в одном кластере.
|
||||
|
||||
Клиенты ДОЛЖНЫ знать правильное значение этого параметра, так что если вы
|
||||
его меняете, обязательно прописывайте изменённое значение в etcd в ключ
|
||||
/vitastor/config/global.
|
||||
Не может быть меньше размера сектора дисков данных OSD.
|
||||
|
||||
## immediate_commit
|
||||
|
||||
@@ -108,8 +102,7 @@ HDD-дисках с внутренним SSD или "медиа" кэшем - н
|
||||
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
||||
указано в спецификациях).
|
||||
|
||||
Данный параметр нужно указывать и в etcd в /vitastor/config/global, и в
|
||||
командной строке или конфигурации OSD. Значения "all" и "small" требуют
|
||||
Указание "all" или "small" в настройках / командной строке OSD требует
|
||||
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
||||
требует включения disable_data_fsync.
|
||||
|
||||
@@ -119,16 +112,3 @@ immediate_commit в значение "all", если вы используете
|
||||
такие SSD для всех журналов, но не для данных - можете установить параметр
|
||||
в "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_keepalive_timeout](#etcd_keepalive_timeout)
|
||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||
- [client_dirty_limit](#client_dirty_limit)
|
||||
|
||||
## 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
|
||||
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_keepalive_timeout](#etcd_keepalive_timeout)
|
||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||
- [client_dirty_limit](#client_dirty_limit)
|
||||
|
||||
## tcp_header_buffer_size
|
||||
|
||||
@@ -222,3 +223,16 @@ etcd_report_interval, чтобы keepalive гарантированно рабо
|
||||
- Значение по умолчанию: 30
|
||||
|
||||
Интервал проверки живости вебсокет-подключений к 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_writes](#autosync_writes)
|
||||
- [recovery_queue_depth](#recovery_queue_depth)
|
||||
- [recovery_pg_switch](#recovery_pg_switch)
|
||||
- [recovery_sync_batch](#recovery_sync_batch)
|
||||
- [readonly](#readonly)
|
||||
- [no_recovery](#no_recovery)
|
||||
- [no_rebalance](#no_rebalance)
|
||||
- [print_stats_interval](#print_stats_interval)
|
||||
- [slow_log_interval](#slow_log_interval)
|
||||
- [inode_vanish_time](#inode_vanish_time)
|
||||
- [max_write_iodepth](#max_write_iodepth)
|
||||
- [min_flusher_count](#min_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
|
||||
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
|
||||
|
||||
- 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
|
||||
"slow".
|
||||
|
||||
## inode_vanish_time
|
||||
|
||||
- Type: seconds
|
||||
- Default: 60
|
||||
|
||||
Number of seconds after which a deleted inode is removed from OSD statistics.
|
||||
|
||||
## max_write_iodepth
|
||||
|
||||
- Type: integer
|
||||
|
@@ -18,12 +18,14 @@
|
||||
- [autosync_interval](#autosync_interval)
|
||||
- [autosync_writes](#autosync_writes)
|
||||
- [recovery_queue_depth](#recovery_queue_depth)
|
||||
- [recovery_pg_switch](#recovery_pg_switch)
|
||||
- [recovery_sync_batch](#recovery_sync_batch)
|
||||
- [readonly](#readonly)
|
||||
- [no_recovery](#no_recovery)
|
||||
- [no_rebalance](#no_rebalance)
|
||||
- [print_stats_interval](#print_stats_interval)
|
||||
- [slow_log_interval](#slow_log_interval)
|
||||
- [inode_vanish_time](#inode_vanish_time)
|
||||
- [max_write_iodepth](#max_write_iodepth)
|
||||
- [min_flusher_count](#min_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
|
||||
|
||||
- Тип: целое число
|
||||
@@ -169,6 +182,13 @@ OSD.
|
||||
медленных или зависших операций, если таковые имеются. Также время, при
|
||||
превышении которого операция считается "медленной".
|
||||
|
||||
## inode_vanish_time
|
||||
|
||||
- Тип: секунды
|
||||
- Значение по умолчанию: 60
|
||||
|
||||
Число секунд, через которое удалённые инод удаляется и из статистики OSD.
|
||||
|
||||
## max_write_iodepth
|
||||
|
||||
- Тип: целое число
|
||||
|
@@ -33,6 +33,9 @@ Parameters:
|
||||
- [pg_count](#pg_count)
|
||||
- [failure_domain](#failure_domain)
|
||||
- [max_osd_combinations](#max_osd_combinations)
|
||||
- [block_size](#block_size)
|
||||
- [bitmap_granularity](#bitmap_granularity)
|
||||
- [immediate_commit](#immediate_commit)
|
||||
- [pg_stripe_size](#pg_stripe_size)
|
||||
- [root_node](#root_node)
|
||||
- [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>`
|
||||
in JSON format `{"<key>":<value>}`.
|
||||
|
||||
As of now, there is only one setting:
|
||||
As of now, two settings are supported:
|
||||
|
||||
## 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
|
||||
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
|
||||
|
||||
## name
|
||||
@@ -106,9 +118,12 @@ Pool name.
|
||||
|
||||
- Type: string
|
||||
- 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
|
||||
|
||||
@@ -183,6 +198,43 @@ number of combinations to generate when optimising PG placement.
|
||||
|
||||
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
|
||||
|
||||
- Type: integer
|
||||
@@ -243,7 +295,7 @@ of the OSDs containing a data chunk for a PG.
|
||||
{
|
||||
"2": {
|
||||
"name":"ecpool",
|
||||
"scheme":"jerasure",
|
||||
"scheme":"ec",
|
||||
"pg_size":3,
|
||||
"parity_chunks":1,
|
||||
"pg_minsize":2,
|
||||
|
@@ -32,6 +32,9 @@
|
||||
- [pg_count](#pg_count)
|
||||
- [failure_domain](#failure_domain)
|
||||
- [max_osd_combinations](#max_osd_combinations)
|
||||
- [block_size](#block_size)
|
||||
- [bitmap_granularity](#bitmap_granularity)
|
||||
- [immediate_commit](#immediate_commit)
|
||||
- [pg_stripe_size](#pg_stripe_size)
|
||||
- [root_node](#root_node)
|
||||
- [osd_tags](#osd_tags)
|
||||
@@ -78,7 +81,10 @@
|
||||
Настройки отдельных OSD задаются в ключах etcd `/vitastor/config/osd/<number>`
|
||||
в JSON-формате `{"<key>":<value>}`.
|
||||
|
||||
На данный момент поддерживается одна настройка:
|
||||
На данный момент поддерживаются две настройки:
|
||||
|
||||
- [reweight](#reweight)
|
||||
- [tags](#tags)
|
||||
|
||||
## reweight
|
||||
|
||||
@@ -93,6 +99,15 @@
|
||||
хранении данных вообще. Вы можете установить reweight в 0, чтобы убрать
|
||||
все данные с OSD.
|
||||
|
||||
## tags
|
||||
|
||||
- Тип: строка или массив строк
|
||||
|
||||
Задаёт тег или набор тегов для данного OSD. Теги можно использовать, чтобы
|
||||
делить OSD на множества и потом размещать пул только на части OSD, а не на
|
||||
всех. Можно, например, пометить SSD OSD тегом "ssd", а HDD тегом "hdd", в
|
||||
этом смысле теги работают аналогично классам устройств.
|
||||
|
||||
# Параметры
|
||||
|
||||
## name
|
||||
@@ -106,9 +121,13 @@
|
||||
|
||||
- Тип: строка
|
||||
- Обязательный
|
||||
- Возможные значения: "replicated", "xor" или "jerasure"
|
||||
- Возможные значения: "replicated", "xor", "ec" или "jerasure"
|
||||
|
||||
Схема избыточности, используемая в данном пуле.
|
||||
Схема избыточности, используемая в данном пуле. "jerasure" - синоним для "ec",
|
||||
в обеих схемах используются коды Рида-Соломона-Вандермонда, реализованные на
|
||||
основе библиотек ISA-L или jerasure. Быстрая реализация на основе ISA-L
|
||||
используется автоматически, когда доступна, в противном случае используется
|
||||
более медленная jerasure-версия.
|
||||
|
||||
## 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
|
||||
|
||||
- Тип: целое число
|
||||
- По умолчанию: 0
|
||||
|
||||
Данный параметр задаёт размер полосы "нарезки" образов на PG. Размер полосы не может
|
||||
быть меньше, чем [block_size](layout-cluster.ru.md#block_size), умноженный на
|
||||
быть меньше, чем [block_size](#block_size), умноженный на
|
||||
(pg_size - parity_chunks) для EC-пулов или 1 для реплицированных пулов. То же
|
||||
значение используется по умолчанию.
|
||||
|
||||
@@ -242,7 +299,7 @@ PG в Vitastor эферемерны, то есть вы можете менят
|
||||
{
|
||||
"2": {
|
||||
"name":"ecpool",
|
||||
"scheme":"jerasure",
|
||||
"scheme":"ec",
|
||||
"pg_size":3,
|
||||
"parity_chunks":1,
|
||||
"pg_minsize":2,
|
||||
|
@@ -2,3 +2,13 @@
|
||||
|
||||
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.
|
||||
|
||||
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 с разными значениями данных параметров (например, SSD и гибридные SSD+HDD
|
||||
OSD) могут сосуществовать в одном кластере Vitastor в разных пулах. Один пул
|
||||
может включать только OSD с одинаковыми настройками этих параметров.
|
||||
|
||||
Данные параметры, отличаясь от значения по умолчанию, должны также быть заданы
|
||||
в etcd, чтобы клиенты могли узнать их значение, либо в глобальной конфигурации
|
||||
/vitastor/config/global, либо в настройках пулов. Настройки пула переопределяют
|
||||
глобальное значение. Если значение в настройках пула не будет соответствовать
|
||||
конфигурации OSD, OSD откажется запускать PG данного пула.
|
||||
|
@@ -2,46 +2,28 @@
|
||||
type: int
|
||||
default: 131072
|
||||
info: |
|
||||
Size of objects (data blocks) into which all physical and virtual drives are
|
||||
subdivided in Vitastor. One of current main settings in Vitastor, affects
|
||||
memory usage, write amplification and I/O load distribution effectiveness.
|
||||
Size of objects (data blocks) into which all physical and virtual drives
|
||||
(within a pool) are subdivided in Vitastor. One of current main settings
|
||||
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,
|
||||
it's possible to use 4 MB for SSD too - it will lower memory usage, but
|
||||
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
|
||||
544 MB per 1 TB of used disk space with the default 128 KB block size.
|
||||
info_ru: |
|
||||
Размер объектов (блоков данных), на которые делятся физические и виртуальные
|
||||
диски в Vitastor. Одна из ключевых на данный момент настроек, влияет на
|
||||
потребление памяти, объём избыточной записи (write amplification) и
|
||||
эффективность распределения нагрузки по OSD.
|
||||
диски в Vitastor (в рамках каждого пула). Одна из ключевых на данный момент
|
||||
настроек, влияет на потребление памяти, объём избыточной записи (write
|
||||
amplification) и эффективность распределения нагрузки по OSD.
|
||||
|
||||
Рекомендуемые по умолчанию размеры блока - 128 килобайт для SSD и 4
|
||||
мегабайта для HDD. В принципе, для SSD можно тоже использовать 4 мегабайта,
|
||||
это понизит использование памяти, но ухудшит распределение нагрузки и в
|
||||
среднем увеличит WA.
|
||||
|
||||
OSD с разными размерами блока (например, SSD и SSD+HDD OSD) на данный
|
||||
момент могут сосуществовать в рамках одного etcd только в виде двух независимых
|
||||
кластеров Vitastor с разными etcd_prefix.
|
||||
|
||||
Также размер блока нельзя менять после инициализации OSD без потери данных.
|
||||
|
||||
Если вы меняете размер блока, обязательно прописывайте его в etcd в
|
||||
/vitastor/config/global, дабы все клиенты его знали.
|
||||
|
||||
Потребление памяти OSD составляет примерно (РАЗМЕР / БЛОК * 68 байт),
|
||||
т.е. примерно 544 МБ памяти на 1 ТБ занятого места на диске при
|
||||
стандартном 128 КБ блоке.
|
||||
@@ -54,25 +36,14 @@
|
||||
an allocation bitmap for each object containing 2 bits per each
|
||||
(bitmap_granularity) bytes.
|
||||
|
||||
This parameter can't be changed after OSD initialization without losing
|
||||
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.
|
||||
Can't be smaller than the OSD data device sector.
|
||||
info_ru: |
|
||||
Требуемое выравнивание записи на виртуальные диски (размер их "сектора").
|
||||
Должен быть кратен disk_alignment. Называется гранулярностью битовой карты
|
||||
потому, что Vitastor хранит битовую карту для каждого объекта, содержащую
|
||||
по 2 бита на каждые (bitmap_granularity) байт.
|
||||
|
||||
Данный параметр нельзя менять после инициализации OSD без потери данных.
|
||||
Также он фиксирован для всего кластера Vitastor, т.е. разные значения
|
||||
не могут сосуществовать в одном кластере.
|
||||
|
||||
Клиенты ДОЛЖНЫ знать правильное значение этого параметра, так что если вы
|
||||
его меняете, обязательно прописывайте изменённое значение в etcd в ключ
|
||||
/vitastor/config/global.
|
||||
Не может быть меньше размера сектора дисков данных OSD.
|
||||
- name: immediate_commit
|
||||
type: string
|
||||
default: false
|
||||
@@ -114,10 +85,9 @@
|
||||
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).
|
||||
|
||||
This parameter must be set both in etcd in /vitastor/config/global and in
|
||||
OSD command line or configuration. Setting it to "all" or "small" requires
|
||||
enabling disable_journal_fsync and disable_meta_fsync, setting it to "all"
|
||||
also requires enabling disable_data_fsync.
|
||||
Setting this parameter to "all" or "small" in OSD parameters requires enabling
|
||||
disable_journal_fsync and disable_meta_fsync, setting it to "all" also requires
|
||||
enabling disable_data_fsync.
|
||||
|
||||
TLDR: For optimal performance, set immediate_commit to "all" if you only use
|
||||
SSDs with supercapacitor-based power loss protection (nonvolatile
|
||||
@@ -168,8 +138,7 @@
|
||||
многих дисках Seagate EXOS (у них есть внутренний SSD-кэш, хотя это и не
|
||||
указано в спецификациях).
|
||||
|
||||
Данный параметр нужно указывать и в etcd в /vitastor/config/global, и в
|
||||
командной строке или конфигурации OSD. Значения "all" и "small" требуют
|
||||
Указание "all" или "small" в настройках / командной строке OSD требует
|
||||
включения disable_journal_fsync и disable_meta_fsync, значение "all" также
|
||||
требует включения disable_data_fsync.
|
||||
|
||||
@@ -179,22 +148,3 @@
|
||||
такие SSD для всех журналов, но не для данных - можете установить параметр
|
||||
в "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.
|
||||
info_ru: |
|
||||
Интервал проверки живости вебсокет-подключений к 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
|
||||
type: int
|
||||
default: 16
|
||||
@@ -158,6 +172,13 @@
|
||||
Временной интервал, с которым 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
|
||||
type: int
|
||||
default: 128
|
||||
|
@@ -6,10 +6,10 @@
|
||||
|
||||
# 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
|
||||
(buster for 6.4, bullseye for 7.1)
|
||||
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
||||
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
|
||||
- Define storage in `/etc/pve/storage.cfg` (see below)
|
||||
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
||||
@@ -35,5 +35,5 @@ vitastor: vitastor
|
||||
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.
|
||||
|
@@ -6,10 +6,10 @@
|
||||
|
||||
# Proxmox
|
||||
|
||||
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4 и 7.1):
|
||||
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-7.3):
|
||||
|
||||
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox
|
||||
(buster для 6.4, bullseye для 7.1)
|
||||
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
||||
buster для 6.4, bullseye для 7.3, pve7.1 для 7.1, pve7.2 для 7.2
|
||||
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
||||
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
||||
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||
@@ -35,5 +35,5 @@ vitastor: 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`.
|
||||
|
@@ -15,7 +15,8 @@
|
||||
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
|
||||
designated initializers support from C++20
|
||||
- CMake
|
||||
- liburing, jerasure headers
|
||||
- liburing, jerasure headers and libraries
|
||||
- ISA-L, libibverbs headers and libraries (optional)
|
||||
- tcmalloc (google-perftools-dev)
|
||||
|
||||
## Basic instructions
|
||||
|
@@ -15,7 +15,8 @@
|
||||
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
|
||||
назначенных инициализаторов (designated initializers) из C++20
|
||||
- CMake
|
||||
- Заголовки liburing, jerasure
|
||||
- Заголовки и библиотеки liburing, jerasure
|
||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs
|
||||
- tcmalloc (google-perftools-dev)
|
||||
|
||||
## Базовая инструкция
|
||||
|
@@ -127,7 +127,7 @@
|
||||
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
||||
с 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
|
||||
|
@@ -33,5 +33,5 @@ Vitastor Network Public License 1.1, основанная на GNU GPL 3.0 с д
|
||||
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
|
||||
совместимости с таким ПО, как QEMU и fio.
|
||||
|
||||
Вы можете найти полный текст 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.txt](../../VNPL-1.1.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
|
||||
- [Performance](../performance/comparison1.en.md) ;-D
|
||||
- [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)
|
||||
- Automatic data distribution over OSDs, with support for:
|
||||
- Mathematical optimization for better uniformity and less data movement
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
- [Debian and CentOS packages](../installation/packages.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
|
||||
- [Native QEMU driver](../usage/qemu.en.md)
|
||||
- [Loadable fio engine for benchmarks](../usage/fio.en.md)
|
||||
@@ -47,7 +48,6 @@
|
||||
|
||||
The following features are planned for the future:
|
||||
|
||||
- Better OSD creation and auto-start tools
|
||||
- Other administrative tools
|
||||
- Web GUI
|
||||
- OpenNebula plugin
|
||||
|
@@ -15,7 +15,7 @@
|
||||
- Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа
|
||||
- [Производительность](../comparison1.ru.md) ;-D
|
||||
- [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок
|
||||
Рида-Соломона на основе библиотеки jerasure с любым числом дисков данных и чётности в группе
|
||||
Рида-Соломона на основе библиотек jerasure и ISA-L с любым числом дисков данных и чётности в группе
|
||||
- Конфигурация через простые человекочитаемые JSON-структуры в etcd
|
||||
- Автоматическое распределение данных по OSD, с поддержкой:
|
||||
- Математической оптимизации для лучшей равномерности распределения и минимизации перемещений данных
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
- [Пакеты для Debian и CentOS](../installation/packages.ru.md)
|
||||
- [Консольный интерфейс управления образами (vitastor-cli)](../usage/cli.ru.md)
|
||||
- [Инструмент управления дисками (vitastor-disk)](docs/usage/disk.ru.md)
|
||||
- Общая пользовательская клиентская библиотека для работы с кластером
|
||||
- [Драйвер диска для QEMU](../usage/qemu.ru.md)
|
||||
- [Драйвер диска для утилиты тестирования производительности fio](../usage/fio.ru.md)
|
||||
@@ -47,7 +48,6 @@
|
||||
|
||||
## Планы развития
|
||||
|
||||
- Более корректные скрипты разметки дисков и автоматического запуска OSD
|
||||
- Другие инструменты администрирования
|
||||
- Web-интерфейс
|
||||
- Плагин для OpenNebula
|
||||
|
@@ -26,9 +26,14 @@
|
||||
## Configure monitors
|
||||
|
||||
On the monitor hosts:
|
||||
- Edit variables at the top of `/usr/lib/vitastor/mon/make-units.sh` to desired values.
|
||||
- Create systemd units for the monitor and etcd: `/usr/lib/vitastor/mon/make-units.sh`
|
||||
- Start etcd and monitors: `systemctl start etcd vitastor-mon`
|
||||
- Put identical etcd_address into `/etc/vitastor/vitastor.conf`. Example:
|
||||
```
|
||||
{
|
||||
"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
|
||||
|
||||
@@ -40,11 +45,9 @@ On the monitor hosts:
|
||||
}
|
||||
```
|
||||
- Initialize OSDs:
|
||||
- Simplest, SSD-only: `/usr/lib/vitastor/mon/make-osd.sh /dev/disk/by-partuuid/XXX [/dev/disk/by-partuuid/YYY ...]`
|
||||
**Warning!** This very simple script by default makes units for server-grade SSDs with write-through cache!
|
||||
If it's not your case, you MUST remove disable_data_fsync and immediate_commit from systemd units.
|
||||
- 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.
|
||||
- SSD-only: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`
|
||||
- Hybrid, SSD+HDD: `vitastor-disk prepare --hybrid /dev/sdXXX [/dev/sdYYY ...]`.
|
||||
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
|
||||
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.
|
||||
@@ -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"}}'
|
||||
```
|
||||
|
||||
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",
|
||||
"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.
|
||||
|
@@ -26,16 +26,14 @@
|
||||
## Настройте мониторы
|
||||
|
||||
На хостах, выделенных под мониторы:
|
||||
- Пропишите нужные вам значения в файле `/usr/lib/vitastor/mon/make-units.sh`
|
||||
- Создайте юниты 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 в `/etc/vitastor/vitastor.conf`. Например:
|
||||
```
|
||||
{
|
||||
"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_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
|
||||
}
|
||||
```
|
||||
- Инициализируйте сервисы etcd, запустив `/usr/lib/vitastor/mon/make-etcd`
|
||||
- Запустите etcd и мониторы: `systemctl enable --now etcd vitastor-mon`
|
||||
|
||||
## Настройте OSD
|
||||
|
||||
@@ -47,12 +45,10 @@
|
||||
}
|
||||
```
|
||||
- Инициализуйте OSD:
|
||||
- SSD: `/usr/lib/vitastor/make-osd.sh /dev/disk/by-partuuid/XXX [/dev/disk/by-partuuid/YYY ...]`. \
|
||||
**Внимание!** Скрипт по умолчанию рассчитан на то, что у вас диски с конденсаторами и отключённым
|
||||
кэшем! Если это не так, из юнитов systemd нужно убрать строчки disable_data_fsync и immediate_commit!
|
||||
- Гибридные, HDD+SSD: `/usr/lib/vitastor/mon/make-osd-hybrid.js /dev/sda /dev/sdb ...` - передайте
|
||||
все ваши SSD и HDD скрипту в командной строке подряд, скрипт автоматически выделит разделы под
|
||||
журналы на SSD и данные на HDD. Скрипт пропускает HDD, на которых уже есть разделы
|
||||
- SSD: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`
|
||||
- Гибридные, SSD+HDD: `vitastor-disk prepare --hybrid /dev/sdXXX [/dev/sdYYY ...]`.
|
||||
Передайте все ваши SSD и HDD скрипту в командной строке подряд, скрипт автоматически выделит
|
||||
разделы под журналы на SSD и данные на HDD. Скрипт пропускает HDD, на которых уже есть разделы
|
||||
или вообще какие-то данные, поэтому если диски непустые, сначала очистите их с помощью
|
||||
`wipefs -a`. SSD с таблицей разделов не пропускаются, но так как скрипт создаёт новые разделы
|
||||
для журналов, на SSD должно быть доступно свободное нераспределённое место.
|
||||
@@ -75,7 +71,7 @@ etcdctl --endpoints=... put /vitastor/config/pools '{"1":{"name":"testpool",
|
||||
|
||||
```
|
||||
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 должны запустить их.
|
||||
|
@@ -20,7 +20,7 @@ It supports the following commands:
|
||||
- [rm-data](#rm-data)
|
||||
- [merge-data](#merge-data)
|
||||
- [alloc-osd](#alloc-osd)
|
||||
- [simple-offsets](#simple-offsets)
|
||||
- [rm-osd](#rm-osd)
|
||||
|
||||
Global options:
|
||||
|
||||
@@ -38,9 +38,9 @@ Global options:
|
||||
|
||||
`vitastor-cli status`
|
||||
|
||||
Показать состояние кластера.
|
||||
Show cluster status.
|
||||
|
||||
Пример вывода:
|
||||
Example output:
|
||||
|
||||
```
|
||||
cluster:
|
||||
@@ -65,9 +65,9 @@ Global options:
|
||||
|
||||
`vitastor-cli df`
|
||||
|
||||
Показать список пулов и занятое место.
|
||||
Show pool space statistics.
|
||||
|
||||
Пример вывода:
|
||||
Example output:
|
||||
|
||||
```
|
||||
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
||||
@@ -76,27 +76,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" эффективность равна нулю, так как все OSD выключены.
|
||||
In the example above, "kaveri" pool has "zero" efficiency because all its OSD are down.
|
||||
|
||||
## ls
|
||||
|
||||
`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 или имени)
|
||||
-l|--long Также выводить статистику занятого места и ввода-вывода
|
||||
--del Также выводить статистику операций удаления
|
||||
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||
-r|--reverse Сортировать в обратном порядке
|
||||
-n|--count N Показывать только первые N записей
|
||||
-p|--pool POOL Filter images by pool ID or name
|
||||
-l|--long Also report allocated size and I/O statistics
|
||||
--del Also include delete operation statistics
|
||||
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||
-r|--reverse Sort in descending order
|
||||
-n|--count N Only list first N items
|
||||
```
|
||||
|
||||
Пример вывода:
|
||||
Example output:
|
||||
|
||||
```
|
||||
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
||||
@@ -113,94 +112,78 @@ 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>`
|
||||
|
||||
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
|
||||
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
|
||||
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
|
||||
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
|
||||
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 snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
||||
```
|
||||
|
||||
Создать снимок образа `<name>` (можно использовать любую форму команды). Снимок можно создавать без остановки
|
||||
клиентов, если пишущий клиент максимум 1.
|
||||
Create a snapshot of image `<name>` (either form can be used). May be used live if only a single writer is active.
|
||||
|
||||
## modify
|
||||
|
||||
`vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]`
|
||||
|
||||
Изменить размер, имя образа или флаг "только для чтения". Снимать флаг "только для чтения"
|
||||
и уменьшать размер образов, у которых есть дочерние клоны, без `--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.
|
||||
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
|
||||
|
||||
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
||||
|
||||
Удалить образ `<from>` или все слои от `<from>` до `<to>` (`<to>` должен быть дочерним
|
||||
образом `<from>`), одновременно меняя родительские образы их клонов (если таковые есть).
|
||||
|
||||
`--writers-stopped` позволяет чуть более эффективно удалять образы в частом случае, когда
|
||||
у удаляемой цепочки есть только один дочерний образ, содержащий небольшой объём данных.
|
||||
В этом случае дочерний образ вливается в родительский и удаляется, а родительский
|
||||
переименовывается в дочерний.
|
||||
|
||||
В других случаях родительские слои вливаются в дочерние.
|
||||
Remove `<from>` or all layers between `<from>` and `<to>` (`<to>` must be a child of `<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:
|
||||
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
|
||||
|
||||
`vitastor-cli flatten <layer>`
|
||||
|
||||
Сделай образ `<layer>` плоским, то есть, скопировать в него данные и разорвать его
|
||||
соединение с родительскими.
|
||||
Flatten a layer, i.e. merge data and detach it from parents.
|
||||
|
||||
## rm-data
|
||||
|
||||
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||
|
||||
Удалить данные инода, не меняя метаданные образов.
|
||||
Remove inode data without changing metadata.
|
||||
|
||||
```
|
||||
--wait-list Сначала запросить полный листинг объектов, а потом начать удалять.
|
||||
Требует больше памяти, но позволяет правильно печатать прогресс удаления.
|
||||
--min-offset Удалять только данные, начиная с заданного смещения.
|
||||
--wait-list Retrieve full objects listings before starting to remove objects.
|
||||
Requires more memory, but allows to show correct removal progress.
|
||||
--min-offset Purge only data starting with specified offset.
|
||||
```
|
||||
|
||||
## merge-data
|
||||
|
||||
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
||||
|
||||
Слить данные слоёв, не меняя метаданные. Вливает данные из слоёв от `<from>` до `<to>`
|
||||
в целевой образ `<target>`. `<to>` должен быть дочерним образом `<from>`, а `<target>`
|
||||
должен быть одним из слоёв между `<from>` и `<to>`, включая сами `<from>` и `<to>`.
|
||||
Merge layer data without changing metadata. Merge `<from>`..`<to>` to `<target>`.
|
||||
`<to>` must be a child of `<from>` and `<target>` may be one of the layers between
|
||||
`<from>` and `<to>`, including `<from>` and `<to>`.
|
||||
|
||||
## alloc-osd
|
||||
|
||||
`vitastor-cli alloc-osd`
|
||||
|
||||
Атомарно выделить новый номер OSD и зарезервировать его, создав в etcd пустой
|
||||
ключ `/osd/stats/<n>`.
|
||||
Allocate a new OSD number and reserve it by creating empty `/osd/stats/<n>` key.
|
||||
|
||||
## 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`.
|
||||
|
||||
```
|
||||
--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
|
||||
```
|
||||
With `--dry-run` only checks if deletion is possible without data loss and
|
||||
redundancy degradation.
|
||||
|
@@ -21,7 +21,7 @@ vitastor-cli - интерфейс командной строки для адм
|
||||
- [rm-data](#rm-data)
|
||||
- [merge-data](#merge-data)
|
||||
- [alloc-osd](#alloc-osd)
|
||||
- [simple-offsets](#simple-offsets)
|
||||
- [rm-osd](#rm-osd)
|
||||
|
||||
Глобальные опции:
|
||||
|
||||
@@ -39,9 +39,9 @@ vitastor-cli - интерфейс командной строки для адм
|
||||
|
||||
`vitastor-cli status`
|
||||
|
||||
Show cluster status.
|
||||
Показать состояние кластера.
|
||||
|
||||
Example output:
|
||||
Пример вывода:
|
||||
|
||||
```
|
||||
cluster:
|
||||
@@ -66,9 +66,9 @@ Example output:
|
||||
|
||||
`vitastor-cli df`
|
||||
|
||||
Show pool space statistics.
|
||||
Показать список пулов и занятое место.
|
||||
|
||||
Example output:
|
||||
Пример вывода:
|
||||
|
||||
```
|
||||
NAME SCHEME PGS TOTAL USED AVAILABLE USED% EFFICIENCY
|
||||
@@ -77,26 +77,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%
|
||||
```
|
||||
|
||||
In the example above, "kaveri" pool has "zero" efficiency because all its OSD are down.
|
||||
В примере у пула "kaveri" эффективность равна нулю, так как все OSD выключены.
|
||||
|
||||
## ls
|
||||
|
||||
`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
|
||||
-l|--long Also report allocated size and I/O statistics
|
||||
--del Also include delete operation statistics
|
||||
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||
-r|--reverse Sort in descending order
|
||||
-n|--count N Only list first N items
|
||||
-p|--pool POOL Фильтровать образы по пулу (ID или имени)
|
||||
-l|--long Также выводить статистику занятого места и ввода-вывода
|
||||
--del Также выводить статистику операций удаления
|
||||
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||
-r|--reverse Сортировать в обратном порядке
|
||||
-n|--count N Показывать только первые N записей
|
||||
```
|
||||
|
||||
Example output:
|
||||
Пример вывода:
|
||||
|
||||
```
|
||||
NAME POOL SIZE USED READ IOPS QUEUE LAT WRITE IOPS QUEUE LAT FLAGS PARENT
|
||||
@@ -113,85 +114,87 @@ 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>`
|
||||
|
||||
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
|
||||
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.
|
||||
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
|
||||
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
|
||||
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
|
||||
|
||||
```
|
||||
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
||||
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.
|
||||
|
||||
## modify
|
||||
|
||||
`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.
|
||||
You should resize file system in the image, if present, before shrinking it.
|
||||
Изменить размер, имя образа или флаг "только для чтения". Снимать флаг "только для чтения"
|
||||
и уменьшать размер образов, у которых есть дочерние клоны, без `--force` нельзя.
|
||||
|
||||
Если новый размер меньше старого, "лишние" данные будут удалены, поэтому перед уменьшением
|
||||
образа сначала уменьшите файловую систему в нём.
|
||||
|
||||
```
|
||||
-f|--force Proceed with shrinking or setting readwrite flag even if the image has children.
|
||||
-f|--force Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
||||
```
|
||||
|
||||
## rm
|
||||
|
||||
`vitastor-cli rm <from> [<to>] [--writers-stopped]`
|
||||
|
||||
Remove `<from>` or all layers between `<from>` and `<to>` (`<to>` must be a child of `<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:
|
||||
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.
|
||||
Удалить образ `<from>` или все слои от `<from>` до `<to>` (`<to>` должен быть дочерним
|
||||
образом `<from>`), одновременно меняя родительские образы их клонов (если таковые есть).
|
||||
|
||||
`--writers-stopped` позволяет чуть более эффективно удалять образы в частом случае, когда
|
||||
у удаляемой цепочки есть только один дочерний образ, содержащий небольшой объём данных.
|
||||
В этом случае дочерний образ вливается в родительский и удаляется, а родительский
|
||||
переименовывается в дочерний.
|
||||
|
||||
В других случаях родительские слои вливаются в дочерние.
|
||||
|
||||
## flatten
|
||||
|
||||
`vitastor-cli flatten <layer>`
|
||||
|
||||
Flatten a layer, i.e. merge data and detach it from parents.
|
||||
Сделай образ `<layer>` плоским, то есть, скопировать в него данные и разорвать его
|
||||
соединение с родительскими.
|
||||
|
||||
## rm-data
|
||||
|
||||
`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.
|
||||
Requires more memory, but allows to show correct removal progress.
|
||||
--min-offset Purge only data starting with specified offset.
|
||||
--wait-list Сначала запросить полный листинг объектов, а потом начать удалять.
|
||||
Требует больше памяти, но позволяет правильно печатать прогресс удаления.
|
||||
--min-offset Удалять только данные, начиная с заданного смещения.
|
||||
```
|
||||
|
||||
## merge-data
|
||||
|
||||
`vitastor-cli merge-data <from> <to> [--target <target>]`
|
||||
|
||||
Merge layer data without changing metadata. Merge `<from>`..`<to>` to `<target>`.
|
||||
`<to>` must be a child of `<from>` and `<target>` may be one of the layers between
|
||||
`<from>` and `<to>`, including `<from>` and `<to>`.
|
||||
Слить данные слоёв, не меняя метаданные. Вливает данные из слоёв от `<from>` до `<to>`
|
||||
в целевой образ `<target>`. `<to>` должен быть дочерним образом `<from>`, а `<target>`
|
||||
должен быть одним из слоёв между `<from>` и `<to>`, включая сами `<from>` и `<to>`.
|
||||
|
||||
## 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`.
|
||||
|
||||
```
|
||||
--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
|
||||
```
|
||||
С опцией `--dry-run` только проверяет, возможно ли удаление без потери данных и деградации
|
||||
избыточности.
|
||||
|
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
|
||||
```
|
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)
|
||||
{
|
||||
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)
|
||||
|
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=local.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')
|
||||
{
|
||||
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]');
|
||||
process.exit();
|
||||
}
|
||||
|
91
mon/mon.js
91
mon/mon.js
@@ -105,6 +105,7 @@ const etcd_tree = {
|
||||
no_rebalance: false,
|
||||
print_stats_interval: 3,
|
||||
slow_log_interval: 10,
|
||||
inode_vanish_time: 60,
|
||||
osd_memlock: false,
|
||||
// blockstore - fixed in superblock
|
||||
block_size,
|
||||
@@ -147,16 +148,21 @@ const etcd_tree = {
|
||||
/* pools: {
|
||||
<id>: {
|
||||
name: 'testpool',
|
||||
// jerasure uses Reed-Solomon-Vandermonde codes
|
||||
scheme: 'replicated' | 'xor' | 'jerasure',
|
||||
// 'ec' uses Reed-Solomon-Vandermonde codes, 'jerasure' is an alias for 'ec'
|
||||
scheme: 'replicated' | 'xor' | 'ec' | 'jerasure',
|
||||
pg_size: 3,
|
||||
pg_minsize: 2,
|
||||
// number of parity chunks, required for jerasure
|
||||
// number of parity chunks, required for EC
|
||||
parity_chunks?: 1,
|
||||
pg_count: 100,
|
||||
failure_domain: 'host',
|
||||
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',
|
||||
// restrict pool to OSDs having all of these tags
|
||||
osd_tags?: 'nvme' | [ 'nvme', ... ],
|
||||
@@ -322,6 +328,13 @@ const etcd_tree = {
|
||||
misplaced: uint64_t,
|
||||
degraded: uint64_t,
|
||||
incomplete: uint64_t,
|
||||
},
|
||||
object_bytes: {
|
||||
total: uint64_t,
|
||||
clean: uint64_t,
|
||||
misplaced: uint64_t,
|
||||
degraded: uint64_t,
|
||||
incomplete: uint64_t,
|
||||
}, */
|
||||
},
|
||||
history: {
|
||||
@@ -650,12 +663,15 @@ class Mon
|
||||
async save_last_clean()
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
new_clean_pgs.items[pool_id] = (this.state.history.last_clean_pgs.items||{})[pool_id];
|
||||
const pool_cfg = this.state.config.pools[pool_id];
|
||||
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++)
|
||||
{
|
||||
@@ -664,17 +680,18 @@ class Mon
|
||||
!(this.state.pg.state[pool_id][pg_num].state instanceof Array))
|
||||
{
|
||||
// Unclean
|
||||
return;
|
||||
continue next_pool;
|
||||
}
|
||||
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')
|
||||
{
|
||||
// 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', {
|
||||
success: [ { requestPut: {
|
||||
key: b64(this.etcd_prefix+'/history/last_clean_pgs'),
|
||||
@@ -1013,14 +1030,15 @@ class Mon
|
||||
console.log('Pool ID '+pool_id+' is invalid');
|
||||
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)
|
||||
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;
|
||||
}
|
||||
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)
|
||||
console.log('Pool '+pool_id+' has invalid pg_size');
|
||||
@@ -1039,7 +1057,8 @@ class Mon
|
||||
console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)');
|
||||
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)
|
||||
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
||||
@@ -1153,6 +1172,10 @@ class Mon
|
||||
{
|
||||
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, [], []);
|
||||
}
|
||||
}
|
||||
@@ -1355,16 +1378,14 @@ class Mon
|
||||
// This is required for multiple change events to trigger at most 1 recheck in 1s
|
||||
schedule_recheck()
|
||||
{
|
||||
if (this.recheck_timer)
|
||||
if (!this.recheck_timer)
|
||||
{
|
||||
clearTimeout(this.recheck_timer);
|
||||
this.recheck_timer = null;
|
||||
this.recheck_timer = setTimeout(() =>
|
||||
{
|
||||
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)
|
||||
@@ -1431,8 +1452,24 @@ class Mon
|
||||
sum_object_counts()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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])
|
||||
{
|
||||
const st = this.state.pg.stats[pool_id][pg_num];
|
||||
@@ -1443,12 +1480,13 @@ class Mon
|
||||
if (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)
|
||||
@@ -1561,7 +1599,7 @@ class Mon
|
||||
{
|
||||
const txn = [];
|
||||
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 inode_stats = this.sum_inode_stats(
|
||||
this.prev_stats ? this.prev_stats.inode_stats : null,
|
||||
@@ -1569,6 +1607,7 @@ class Mon
|
||||
);
|
||||
this.prev_stats = { timestamp, ...stats, inode_stats };
|
||||
stats.object_counts = object_counts;
|
||||
stats.object_bytes = object_bytes;
|
||||
stats = this.serialize_bigints(stats);
|
||||
inode_stats = this.serialize_bigints(inode_stats);
|
||||
txn.push({ requestPut: { key: b64(this.etcd_prefix+'/stats'), value: b64(JSON.stringify(stats)) } });
|
||||
@@ -1656,6 +1695,7 @@ class Mon
|
||||
// Do not clear these to null
|
||||
kv.value = kv.value || {};
|
||||
}
|
||||
const old = cur[key_parts[key_parts.length-1]];
|
||||
cur[key_parts[key_parts.length-1]] = kv.value;
|
||||
if (key === 'config/global')
|
||||
{
|
||||
@@ -1680,7 +1720,12 @@ class Mon
|
||||
}
|
||||
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.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
|
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
||||
from cinder.volume import driver
|
||||
from cinder.volume import volume_utils
|
||||
|
||||
VERSION = '0.6.17'
|
||||
VERSION = '0.8.3'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -464,7 +464,7 @@ class VitastorDriver(driver.CloneableImageVD,
|
||||
vol_name = utils.convert_str(volume.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:
|
||||
raise exception.SnapshotNotFound(snapshot_id = snap_name)
|
||||
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])
|
||||
|
659
patches/libvirt-6.0-vitastor.diff
Normal file
659
patches/libvirt-6.0-vitastor.diff
Normal file
@@ -0,0 +1,659 @@
|
||||
commit 7f01510ef207940b07fac4f5fc8b9f1580b443aa
|
||||
Author: Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||
Date: Sun Jun 27 12:52:40 2021 +0300
|
||||
|
||||
Add Vitastor support
|
||||
|
||||
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
|
||||
index aa50eac..082b4f8 100644
|
||||
--- a/docs/schemas/domaincommon.rng
|
||||
+++ b/docs/schemas/domaincommon.rng
|
||||
@@ -1766,6 +1766,35 @@
|
||||
</element>
|
||||
</define>
|
||||
|
||||
+ <define name="diskSourceNetworkProtocolVitastor">
|
||||
+ <element name="source">
|
||||
+ <interleave>
|
||||
+ <attribute name="protocol">
|
||||
+ <value>vitastor</value>
|
||||
+ </attribute>
|
||||
+ <ref name="diskSourceCommon"/>
|
||||
+ <optional>
|
||||
+ <attribute name="name"/>
|
||||
+ </optional>
|
||||
+ <optional>
|
||||
+ <attribute name="query"/>
|
||||
+ </optional>
|
||||
+ <zeroOrMore>
|
||||
+ <ref name="diskSourceNetworkHost"/>
|
||||
+ </zeroOrMore>
|
||||
+ <optional>
|
||||
+ <element name="config">
|
||||
+ <attribute name="file">
|
||||
+ <ref name="absFilePath"/>
|
||||
+ </attribute>
|
||||
+ <empty/>
|
||||
+ </element>
|
||||
+ </optional>
|
||||
+ <empty/>
|
||||
+ </interleave>
|
||||
+ </element>
|
||||
+ </define>
|
||||
+
|
||||
<define name="diskSourceNetworkProtocolISCSI">
|
||||
<element name="source">
|
||||
<attribute name="protocol">
|
||||
@@ -1891,6 +1920,7 @@
|
||||
<ref name="diskSourceNetworkProtocolHTTP"/>
|
||||
<ref name="diskSourceNetworkProtocolSimple"/>
|
||||
<ref name="diskSourceNetworkProtocolVxHS"/>
|
||||
+ <ref name="diskSourceNetworkProtocolVitastor"/>
|
||||
</choice>
|
||||
</define>
|
||||
|
||||
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h
|
||||
index 4bf2b5f..dbc011b 100644
|
||||
--- a/include/libvirt/libvirt-storage.h
|
||||
+++ b/include/libvirt/libvirt-storage.h
|
||||
@@ -245,6 +245,7 @@ typedef enum {
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17,
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE = 1 << 18,
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT = 1 << 19,
|
||||
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR = 1 << 20,
|
||||
} virConnectListAllStoragePoolsFlags;
|
||||
|
||||
int virConnectListAllStoragePools(virConnectPtr conn,
|
||||
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
|
||||
index 222bb8c..2c30c55 100644
|
||||
--- a/src/conf/domain_conf.c
|
||||
+++ b/src/conf/domain_conf.c
|
||||
@@ -5114,8 +5114,7 @@ virDomainDiskDefPostParse(virDomainDiskD
|
||||
const virDomainDef *def,
|
||||
virDomainXMLOptionPtr xmlopt)
|
||||
{
|
||||
- /* internal snapshots and config files are currently supported
|
||||
- * only with rbd: */
|
||||
+ /* internal snapshots are currently supported only with rbd: */
|
||||
if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_NETWORK &&
|
||||
disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
|
||||
if (disk->src->snapshot) {
|
||||
@@ -5124,11 +5123,15 @@ virDomainDiskDefPostParse(virDomainDiskD
|
||||
"only with 'rbd' disks"));
|
||||
return -1;
|
||||
}
|
||||
-
|
||||
+ }
|
||||
+ /* config files are currently supported only with rbd and vitastor: */
|
||||
+ if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_NETWORK &&
|
||||
+ disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD &&
|
||||
+ disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
|
||||
if (disk->src->configFile) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("<config> element is currently supported "
|
||||
- "only with 'rbd' disks"));
|
||||
+ "only with 'rbd' and 'vitastor' disks"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -9258,6 +9261,10 @@ virDomainDiskSourceNetworkParse(xmlNodeP
|
||||
return -1;
|
||||
}
|
||||
|
||||
+ if (src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
|
||||
+ src->relPath = virXMLPropString(node, "query");
|
||||
+ }
|
||||
+
|
||||
if ((haveTLS = virXMLPropString(node, "tls")) &&
|
||||
(src->haveTLS = virTristateBoolTypeFromString(haveTLS)) <= 0) {
|
||||
virReportError(VIR_ERR_XML_ERROR,
|
||||
@@ -9303,6 +9310,10 @@ virDomainDiskSourceNetworkParse(xmlNodeP
|
||||
/* config file currently only works with remote disks */
|
||||
src->configFile = virXPathString("string(./config/@file)", ctxt);
|
||||
|
||||
+ if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
|
||||
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS)
|
||||
+ src->query = virXMLPropString(node, "query");
|
||||
+
|
||||
if (virDomainStorageNetworkParseHosts(node, &src->hosts, &src->nhosts) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -24141,6 +24152,10 @@ virDomainDiskSourceFormatNetwork(virBuff
|
||||
|
||||
virBufferEscapeString(attrBuf, " name='%s'", path ? path : src->path);
|
||||
|
||||
+ if (src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR && src->relPath != NULL) {
|
||||
+ virBufferEscapeString(attrBuf, " query='%s'", src->relPath);
|
||||
+ }
|
||||
+
|
||||
if (src->haveTLS != VIR_TRISTATE_BOOL_ABSENT &&
|
||||
!(flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE &&
|
||||
src->tlsFromConfig))
|
||||
@@ -31402,6 +31417,7 @@ virDomainDiskTranslateSourcePool(virDomainDiskDefPtr def)
|
||||
|
||||
case VIR_STORAGE_POOL_MPATH:
|
||||
case VIR_STORAGE_POOL_RBD:
|
||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||
case VIR_STORAGE_POOL_SHEEPDOG:
|
||||
case VIR_STORAGE_POOL_GLUSTER:
|
||||
case VIR_STORAGE_POOL_LAST:
|
||||
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
|
||||
index 55db7a9..7cbe937 100644
|
||||
--- a/src/conf/storage_conf.c
|
||||
+++ b/src/conf/storage_conf.c
|
||||
@@ -59,7 +59,7 @@ VIR_ENUM_IMPL(virStoragePool,
|
||||
"logical", "disk", "iscsi",
|
||||
"iscsi-direct", "scsi", "mpath",
|
||||
"rbd", "sheepdog", "gluster",
|
||||
- "zfs", "vstorage",
|
||||
+ "zfs", "vstorage", "vitastor",
|
||||
);
|
||||
|
||||
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
|
||||
@@ -248,6 +248,18 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
|
||||
.formatToString = virStorageFileFormatTypeToString,
|
||||
}
|
||||
},
|
||||
+ {.poolType = VIR_STORAGE_POOL_VITASTOR,
|
||||
+ .poolOptions = {
|
||||
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
||||
+ VIR_STORAGE_POOL_SOURCE_NETWORK |
|
||||
+ VIR_STORAGE_POOL_SOURCE_NAME),
|
||||
+ },
|
||||
+ .volOptions = {
|
||||
+ .defaultFormat = VIR_STORAGE_FILE_RAW,
|
||||
+ .formatFromString = virStorageVolumeFormatFromString,
|
||||
+ .formatToString = virStorageFileFormatTypeToString,
|
||||
+ }
|
||||
+ },
|
||||
{.poolType = VIR_STORAGE_POOL_SHEEPDOG,
|
||||
.poolOptions = {
|
||||
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
||||
@@ -550,6 +562,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
|
||||
_("element 'name' is mandatory for RBD pool"));
|
||||
goto cleanup;
|
||||
}
|
||||
+ if (pool_type == VIR_STORAGE_POOL_VITASTOR && source->name == NULL) {
|
||||
+ virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
+ _("element 'name' is mandatory for Vitastor pool"));
|
||||
+ return -1;
|
||||
+ }
|
||||
|
||||
if (options->formatFromString) {
|
||||
char *format = virXPathString("string(./format/@type)", ctxt);
|
||||
@@ -1173,6 +1190,7 @@ virStoragePoolDefFormatBuf(virBufferPtr buf,
|
||||
/* RBD, Sheepdog, Gluster and Iscsi-direct devices are not local block devs nor
|
||||
* files, so they don't have a target */
|
||||
if (def->type != VIR_STORAGE_POOL_RBD &&
|
||||
+ def->type != VIR_STORAGE_POOL_VITASTOR &&
|
||||
def->type != VIR_STORAGE_POOL_SHEEPDOG &&
|
||||
def->type != VIR_STORAGE_POOL_GLUSTER &&
|
||||
def->type != VIR_STORAGE_POOL_ISCSI_DIRECT) {
|
||||
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
|
||||
index dc0aa2a..ed4983d 100644
|
||||
--- a/src/conf/storage_conf.h
|
||||
+++ b/src/conf/storage_conf.h
|
||||
@@ -110,6 +110,7 @@ typedef enum {
|
||||
VIR_STORAGE_POOL_GLUSTER, /* Gluster device */
|
||||
VIR_STORAGE_POOL_ZFS, /* ZFS */
|
||||
VIR_STORAGE_POOL_VSTORAGE, /* Virtuozzo Storage */
|
||||
+ VIR_STORAGE_POOL_VITASTOR, /* Vitastor */
|
||||
|
||||
VIR_STORAGE_POOL_LAST,
|
||||
} virStoragePoolType;
|
||||
@@ -466,6 +467,7 @@ VIR_ENUM_DECL(virStoragePartedFs)
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \
|
||||
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR | \
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \
|
||||
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS | \
|
||||
diff --git a/src/conf/virstorageobj.c b/src/conf/virstorageobj.c
|
||||
index 6ea6a97..3ba45b9 100644
|
||||
--- a/src/conf/virstorageobj.c
|
||||
+++ b/src/conf/virstorageobj.c
|
||||
@@ -1493,6 +1493,7 @@ virStoragePoolObjSourceFindDuplicateCb(const void *payload,
|
||||
return 1;
|
||||
break;
|
||||
|
||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||
case VIR_STORAGE_POOL_RBD:
|
||||
case VIR_STORAGE_POOL_LAST:
|
||||
break;
|
||||
@@ -1994,6 +1995,8 @@ virStoragePoolObjMatch(virStoragePoolObjPtr obj,
|
||||
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
|
||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
|
||||
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
|
||||
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR) &&
|
||||
+ (obj->def->type == VIR_STORAGE_POOL_VITASTOR)) ||
|
||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
|
||||
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
|
||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
|
||||
diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c
|
||||
index 2ea3e94..d5d2273 100644
|
||||
--- a/src/libvirt-storage.c
|
||||
+++ b/src/libvirt-storage.c
|
||||
@@ -92,6 +92,7 @@ virStoragePoolGetConnect(virStoragePoolPtr pool)
|
||||
* VIR_CONNECT_LIST_STORAGE_POOLS_SCSI
|
||||
* VIR_CONNECT_LIST_STORAGE_POOLS_MPATH
|
||||
* VIR_CONNECT_LIST_STORAGE_POOLS_RBD
|
||||
+ * VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR
|
||||
* VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG
|
||||
*
|
||||
* Returns the number of storage pools found or -1 and sets @pools to
|
||||
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
|
||||
index 73e988a..ab7bb81 100644
|
||||
--- a/src/libxl/libxl_conf.c
|
||||
+++ b/src/libxl/libxl_conf.c
|
||||
@@ -888,6 +888,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSourcePtr src,
|
||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||
virReportError(VIR_ERR_NO_SUPPORT,
|
||||
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
|
||||
index 17b93d0..c5a0084 100644
|
||||
--- a/src/libxl/xen_xl.c
|
||||
+++ b/src/libxl/xen_xl.c
|
||||
@@ -1601,6 +1601,7 @@ xenFormatXLDiskSrcNet(virStorageSourcePtr src)
|
||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||
virReportError(VIR_ERR_NO_SUPPORT,
|
||||
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
|
||||
index cbf0aa4..f0ca9e7 100644
|
||||
--- a/src/qemu/qemu_block.c
|
||||
+++ b/src/qemu/qemu_block.c
|
||||
@@ -869,6 +869,42 @@ qemuBlockStorageSourceGetRBDProps(virStorageSourcePtr src)
|
||||
}
|
||||
|
||||
|
||||
+static virJSONValuePtr
|
||||
+qemuBlockStorageSourceGetVitastorProps(virStorageSource *src)
|
||||
+{
|
||||
+ virJSONValuePtr ret = NULL;
|
||||
+ virStorageNetHostDefPtr host;
|
||||
+ size_t i;
|
||||
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
+ char *etcd = NULL;
|
||||
+
|
||||
+ for (i = 0; i < src->nhosts; i++) {
|
||||
+ host = src->hosts + i;
|
||||
+ if ((virStorageNetHostTransport)host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ virBufferAsprintf(&buf, i > 0 ? ",%s:%u" : "%s:%u", host->name, host->port);
|
||||
+ }
|
||||
+ if (src->nhosts > 0) {
|
||||
+ etcd = virBufferContentAndReset(&buf);
|
||||
+ }
|
||||
+
|
||||
+ if (virJSONValueObjectCreate(&ret,
|
||||
+ "s:driver", "vitastor",
|
||||
+ "S:etcd-host", etcd,
|
||||
+ "S:etcd-prefix", src->relPath,
|
||||
+ "S:config-path", src->configFile,
|
||||
+ "s:image", src->path,
|
||||
+ NULL) < 0)
|
||||
+ goto cleanup;
|
||||
+
|
||||
+cleanup:
|
||||
+ VIR_FREE(etcd);
|
||||
+ virBufferFreeAndReset(&buf);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+
|
||||
static virJSONValuePtr
|
||||
qemuBlockStorageSourceGetSheepdogProps(virStorageSourcePtr src)
|
||||
{
|
||||
@@ -1130,6 +1166,11 @@ qemuBlockStorageSourceGetBackendProps(virStorageSourcePtr src,
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
+ if (!(fileprops = qemuBlockStorageSourceGetVitastorProps(src)))
|
||||
+ return NULL;
|
||||
+ break;
|
||||
+
|
||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||
if (!(fileprops = qemuBlockStorageSourceGetSheepdogProps(src)))
|
||||
return NULL;
|
||||
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
|
||||
index 822d5f8..abec34e 100644
|
||||
--- a/src/qemu/qemu_command.c
|
||||
+++ b/src/qemu/qemu_command.c
|
||||
@@ -1078,6 +1078,43 @@ qemuBuildNetworkDriveStr(virStorageSourcePtr src,
|
||||
ret = virBufferContentAndReset(&buf);
|
||||
break;
|
||||
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
+ if (strchr(src->path, ':')) {
|
||||
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
+ _("':' not allowed in Vitastor source volume name '%s'"),
|
||||
+ src->path);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ virBufferStrcat(&buf, "vitastor:image=", src->path, NULL);
|
||||
+
|
||||
+ if (src->nhosts > 0) {
|
||||
+ virBufferAddLit(&buf, ":etcd-host=");
|
||||
+ for (i = 0; i < src->nhosts; i++) {
|
||||
+ if (i)
|
||||
+ virBufferAddLit(&buf, ",");
|
||||
+
|
||||
+ /* assume host containing : is ipv6 */
|
||||
+ if (strchr(src->hosts[i].name, ':'))
|
||||
+ virBufferEscape(&buf, '\\', ":", "[%s]",
|
||||
+ src->hosts[i].name);
|
||||
+ else
|
||||
+ virBufferAsprintf(&buf, "%s", src->hosts[i].name);
|
||||
+
|
||||
+ if (src->hosts[i].port)
|
||||
+ virBufferAsprintf(&buf, "\\:%u", src->hosts[i].port);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (src->configFile)
|
||||
+ virBufferEscape(&buf, '\\', ":", ":config-path=%s", src->configFile);
|
||||
+
|
||||
+ if (src->relPath)
|
||||
+ virBufferEscape(&buf, '\\', ":", ":etcd-prefix=%s", src->relPath);
|
||||
+
|
||||
+ ret = virBufferContentAndReset(&buf);
|
||||
+ break;
|
||||
+
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("VxHS protocol does not support URI syntax"));
|
||||
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
|
||||
index ec6b340..f399efa 100644
|
||||
--- a/src/qemu/qemu_domain.c
|
||||
+++ b/src/qemu/qemu_domain.c
|
||||
@@ -6862,6 +6862,16 @@ qemuDomainValidateStorageSource(virStora
|
||||
return -1;
|
||||
}
|
||||
|
||||
+ if (src->query &&
|
||||
+ (actualType != VIR_STORAGE_TYPE_NETWORK ||
|
||||
+ (src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTPS &&
|
||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP &&
|
||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR))) {
|
||||
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
+ _("query is supported only with HTTP(S) protocols"));
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13836,6 +13846,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSourcePtr src,
|
||||
break;
|
||||
|
||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
|
||||
index 1d96170..2d24396 100644
|
||||
--- a/src/qemu/qemu_driver.c
|
||||
+++ b/src/qemu/qemu_driver.c
|
||||
@@ -14841,6 +14841,7 @@ qemuDomainSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDefPtr snapdi
|
||||
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("external inactive snapshots are not supported on "
|
||||
@@ -14925,6 +14926,7 @@ qemuDomainSnapshotPrepareDiskExternalActive(virDomainSnapshotDiskDefPtr snapdisk
|
||||
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("external active snapshots are not supported on "
|
||||
@@ -15054,6 +15056,7 @@ qemuDomainSnapshotPrepareDiskInternal(virDomainDiskDefPtr disk,
|
||||
case VIR_STORAGE_NET_PROTOCOL_TFTP:
|
||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("internal inactive snapshots are not supported on "
|
||||
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
|
||||
index 4a13e90..33301c7 100644
|
||||
--- a/src/storage/storage_driver.c
|
||||
+++ b/src/storage/storage_driver.c
|
||||
@@ -1641,6 +1641,7 @@ storageVolLookupByPathCallback(virStoragePoolObjPtr obj,
|
||||
case VIR_STORAGE_POOL_RBD:
|
||||
case VIR_STORAGE_POOL_SHEEPDOG:
|
||||
case VIR_STORAGE_POOL_ZFS:
|
||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||
case VIR_STORAGE_POOL_LAST:
|
||||
ignore_value(VIR_STRDUP(stable_path, data->path));
|
||||
break;
|
||||
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
|
||||
index 29c4c86..a27ad94 100644
|
||||
--- a/src/test/test_driver.c
|
||||
+++ b/src/test/test_driver.c
|
||||
@@ -7086,6 +7086,7 @@ testStorageVolumeTypeForPool(int pooltype)
|
||||
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
||||
case VIR_STORAGE_POOL_GLUSTER:
|
||||
case VIR_STORAGE_POOL_RBD:
|
||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||
return VIR_STORAGE_VOL_NETWORK;
|
||||
case VIR_STORAGE_POOL_LOGICAL:
|
||||
case VIR_STORAGE_POOL_DISK:
|
||||
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
|
||||
index 0d3c2af..edb7f9e 100644
|
||||
--- a/src/util/virstoragefile.c
|
||||
+++ b/src/util/virstoragefile.c
|
||||
@@ -90,6 +90,7 @@ VIR_ENUM_IMPL(virStorageNetProtocol,
|
||||
"tftp",
|
||||
"ssh",
|
||||
"vxhs",
|
||||
+ "vitastor",
|
||||
);
|
||||
|
||||
VIR_ENUM_IMPL(virStorageNetHostTransport,
|
||||
@@ -2927,6 +2928,73 @@ virStorageSourceParseRBDColonString(cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+virStorageSourceParseVitastorColonString(const char *colonstr,
|
||||
+ virStorageSourcePtr src)
|
||||
+{
|
||||
+ char *p, *e, *next;
|
||||
+ g_autofree char *options = NULL;
|
||||
+
|
||||
+ /* optionally skip the "vitastor:" prefix if provided */
|
||||
+ if (STRPREFIX(colonstr, "vitastor:"))
|
||||
+ colonstr += strlen("vitastor:");
|
||||
+
|
||||
+ options = g_strdup(colonstr);
|
||||
+
|
||||
+ p = options;
|
||||
+ while (*p) {
|
||||
+ /* find : delimiter or end of string */
|
||||
+ for (e = p; *e && *e != ':'; ++e) {
|
||||
+ if (*e == '\\') {
|
||||
+ e++;
|
||||
+ if (*e == '\0')
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (*e == '\0') {
|
||||
+ next = e; /* last kv pair */
|
||||
+ } else {
|
||||
+ next = e + 1;
|
||||
+ *e = '\0';
|
||||
+ }
|
||||
+
|
||||
+ if (STRPREFIX(p, "image=")) {
|
||||
+ src->path = g_strdup(p + strlen("image="));
|
||||
+ } else if (STRPREFIX(p, "etcd-prefix=")) {
|
||||
+ src->query = g_strdup(p + strlen("etcd-prefix="));
|
||||
+ } else if (STRPREFIX(p, "config-path=")) {
|
||||
+ src->configFile = g_strdup(p + strlen("config-path="));
|
||||
+ } else if (STRPREFIX(p, "etcd-host=")) {
|
||||
+ char *h, *sep;
|
||||
+
|
||||
+ h = p + strlen("etcd-host=");
|
||||
+ while (h < e) {
|
||||
+ for (sep = h; sep < e; ++sep) {
|
||||
+ if (*sep == '\\' && (sep[1] == ',' ||
|
||||
+ sep[1] == ';' ||
|
||||
+ sep[1] == ' ')) {
|
||||
+ *sep = '\0';
|
||||
+ sep += 2;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (virStorageSourceRBDAddHost(src, h) < 0)
|
||||
+ return -1;
|
||||
+
|
||||
+ h = sep;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ p = next;
|
||||
+ }
|
||||
+
|
||||
+ if (!src->path) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
static int
|
||||
virStorageSourceParseNBDColonString(const char *nbdstr,
|
||||
@@ -3022,6 +3090,11 @@ virStorageSourceParseBackingColon(virSto
|
||||
return -1;
|
||||
break;
|
||||
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
+ if (virStorageSourceParseVitastorColonString(path, src) < 0)
|
||||
+ return -1;
|
||||
+ break;
|
||||
+
|
||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||
@@ -3507,6 +3580,54 @@ virStorageSourceParseBackingJSONRBD(virS
|
||||
}
|
||||
|
||||
static int
|
||||
+virStorageSourceParseBackingJSONVitastor(virStorageSourcePtr src,
|
||||
+ virJSONValuePtr json,
|
||||
+ const char *jsonstr G_GNUC_UNUSED,
|
||||
+ int opaque G_GNUC_UNUSED)
|
||||
+{
|
||||
+ const char *filename;
|
||||
+ const char *image = virJSONValueObjectGetString(json, "image");
|
||||
+ const char *conf = virJSONValueObjectGetString(json, "config-path");
|
||||
+ const char *etcd_prefix = virJSONValueObjectGetString(json, "etcd-prefix");
|
||||
+ virJSONValuePtr servers = virJSONValueObjectGetArray(json, "server");
|
||||
+ size_t nservers;
|
||||
+ size_t i;
|
||||
+
|
||||
+ src->type = VIR_STORAGE_TYPE_NETWORK;
|
||||
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VITASTOR;
|
||||
+
|
||||
+ /* legacy syntax passed via 'filename' option */
|
||||
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
|
||||
+ return virStorageSourceParseVitastorColonString(filename, src);
|
||||
+
|
||||
+ if (!image) {
|
||||
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||
+ _("missing image name in Vitastor backing volume "
|
||||
+ "JSON specification"));
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ src->path = g_strdup(image);
|
||||
+ src->configFile = g_strdup(conf);
|
||||
+ src->query = g_strdup(etcd_prefix);
|
||||
+
|
||||
+ if (servers) {
|
||||
+ nservers = virJSONValueArraySize(servers);
|
||||
+
|
||||
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
|
||||
+ src->nhosts = nservers;
|
||||
+
|
||||
+ for (i = 0; i < nservers; i++) {
|
||||
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
|
||||
+ virJSONValueArrayGet(servers, i)) < 0)
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src,
|
||||
virJSONValuePtr json,
|
||||
int opaque G_GNUC_UNUSED)
|
||||
@@ -3578,6 +3699,7 @@ static const struct virStorageSourceJSON
|
||||
{"sheepdog", virStorageSourceParseBackingJSONSheepdog, 0},
|
||||
{"ssh", virStorageSourceParseBackingJSONSSH, 0},
|
||||
{"rbd", virStorageSourceParseBackingJSONRBD, 0},
|
||||
+ {"vitastor", virStorageSourceParseBackingJSONVitastor, 0},
|
||||
{"raw", virStorageSourceParseBackingJSONRaw, 0},
|
||||
{"vxhs", virStorageSourceParseBackingJSONVxHS, 0},
|
||||
};
|
||||
@@ -4364,6 +4486,7 @@ virStorageSourceNetworkDefaultPort(virSt
|
||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||
return 24007;
|
||||
|
||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||
/* we don't provide a default for RBD */
|
||||
return 0;
|
||||
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
|
||||
index 1d6161a..8d83bf3 100644
|
||||
--- a/src/util/virstoragefile.h
|
||||
+++ b/src/util/virstoragefile.h
|
||||
@@ -134,6 +134,7 @@ typedef enum {
|
||||
VIR_STORAGE_NET_PROTOCOL_TFTP,
|
||||
VIR_STORAGE_NET_PROTOCOL_SSH,
|
||||
VIR_STORAGE_NET_PROTOCOL_VXHS,
|
||||
+ VIR_STORAGE_NET_PROTOCOL_VITASTOR,
|
||||
|
||||
VIR_STORAGE_NET_PROTOCOL_LAST
|
||||
} virStorageNetProtocol;
|
||||
@@ -265,6 +266,7 @@ struct _virStorageSource {
|
||||
char *snapshot; /* for storage systems supporting internal snapshots */
|
||||
char *configFile; /* some storage systems use config file as part of
|
||||
the source definition */
|
||||
+ char *query; /* query string for HTTP based protocols */
|
||||
size_t nhosts;
|
||||
virStorageNetHostDefPtr hosts;
|
||||
virStorageSourcePoolDefPtr srcpool;
|
||||
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
|
||||
index 70ca39b..9caef51 100644
|
||||
--- a/tools/virsh-pool.c
|
||||
+++ b/tools/virsh-pool.c
|
||||
@@ -1219,6 +1219,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
|
||||
case VIR_STORAGE_POOL_VSTORAGE:
|
||||
flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE;
|
||||
break;
|
||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||
+ flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR;
|
||||
+ break;
|
||||
case VIR_STORAGE_POOL_LAST:
|
||||
break;
|
||||
}
|
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 '--- /dev/null' >> $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
|
||||
fi
|
||||
done
|
||||
|
@@ -25,4 +25,4 @@ rm 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"; }'`
|
||||
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
||||
tar --transform 's#^#vitastor-0.6.17/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.6.17$(rpm --eval '%dist').tar.gz *
|
||||
tar --transform 's#^#vitastor-0.8.3/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.8.3$(rpm --eval '%dist').tar.gz *
|
||||
|
@@ -58,7 +58,7 @@
|
||||
+BuildRequires: gperftools-devel
|
||||
+BuildRequires: libusbx-devel >= 1.0.21
|
||||
%if %{have_usbredir}
|
||||
BuildRequires: usbredir-devel >= 0.7.1
|
||||
BuildRequires: usbredir-devel >= 0.8.2
|
||||
%endif
|
||||
@@ -856,12 +861,13 @@ BuildRequires: virglrenderer-devel
|
||||
# For smartcard NSS support
|
||||
|
@@ -9,7 +9,8 @@ WORKDIR /root
|
||||
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 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 rpm --nomd5 -i fio*.src.rpm
|
||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||
@@ -34,7 +35,7 @@ ADD . /root/vitastor
|
||||
RUN set -e; \
|
||||
cd /root/vitastor/rpm; \
|
||||
sh build-tarball.sh; \
|
||||
cp /root/vitastor-0.6.17.el7.tar.gz ~/rpmbuild/SOURCES; \
|
||||
cp /root/vitastor-0.8.3.el7.tar.gz ~/rpmbuild/SOURCES; \
|
||||
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||
cd ~/rpmbuild/SPECS/; \
|
||||
rpmbuild -ba vitastor.spec; \
|
||||
|
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 0.6.17
|
||||
Version: 0.8.3
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-0.6.17.el7.tar.gz
|
||||
Source0: vitastor-0.8.3.el7.tar.gz
|
||||
|
||||
BuildRequires: liburing-devel >= 0.6
|
||||
BuildRequires: gperftools-devel
|
||||
@@ -13,6 +13,7 @@ BuildRequires: devtoolset-9-gcc-c++
|
||||
BuildRequires: rh-nodejs12
|
||||
BuildRequires: rh-nodejs12-npm
|
||||
BuildRequires: jerasure-devel
|
||||
BuildRequires: libisa-l-devel
|
||||
BuildRequires: gf-complete-devel
|
||||
BuildRequires: libibverbs-devel
|
||||
BuildRequires: cmake
|
||||
@@ -32,8 +33,11 @@ size with configurable redundancy (replication or erasure codes/XOR).
|
||||
%package -n vitastor-osd
|
||||
Summary: Vitastor - OSD
|
||||
Requires: libJerasure2
|
||||
Requires: libisa-l
|
||||
Requires: liburing >= 0.6
|
||||
Requires: vitastor-client = %{version}-%{release}
|
||||
Requires: util-linux
|
||||
Requires: parted
|
||||
|
||||
|
||||
%description -n vitastor-osd
|
||||
@@ -100,8 +104,11 @@ cd mon
|
||||
npm install
|
||||
cd ..
|
||||
mkdir -p %buildroot/usr/lib/vitastor
|
||||
cp mon/make-osd.sh %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
|
||||
@@ -110,11 +117,29 @@ cp -r mon %buildroot/usr/lib/vitastor
|
||||
|
||||
%files -n vitastor-osd
|
||||
%_bindir/vitastor-osd
|
||||
%_bindir/vitastor-disk
|
||||
%_bindir/vitastor-dump-journal
|
||||
/lib/systemd/system/vitastor-osd@.service
|
||||
/lib/systemd/system/vitastor.target
|
||||
/lib/udev/rules.d/90-vitastor.rules
|
||||
|
||||
|
||||
%pre -n vitastor-osd
|
||||
groupadd -r -f vitastor 2>/dev/null ||:
|
||||
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||
mkdir -p /etc/vitastor
|
||||
|
||||
|
||||
%files -n vitastor-mon
|
||||
/usr/lib/vitastor/mon
|
||||
/lib/systemd/system/vitastor-mon.service
|
||||
|
||||
|
||||
%pre -n vitastor-mon
|
||||
groupadd -r -f vitastor 2>/dev/null ||:
|
||||
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||
mkdir -p /etc/vitastor
|
||||
|
||||
|
||||
%files -n vitastor-client
|
||||
@@ -125,7 +150,6 @@ cp -r mon %buildroot/usr/lib/vitastor
|
||||
%_bindir/vita
|
||||
%_libdir/libvitastor_blk.so*
|
||||
%_libdir/libvitastor_client.so*
|
||||
/usr/lib/vitastor/make-osd.sh
|
||||
|
||||
|
||||
%files -n vitastor-client-devel
|
||||
|
@@ -6,10 +6,12 @@ FROM centos:8
|
||||
WORKDIR /root
|
||||
|
||||
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 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 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 rpm --nomd5 -i fio*.src.rpm
|
||||
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
|
||||
@@ -33,7 +35,7 @@ ADD . /root/vitastor
|
||||
RUN set -e; \
|
||||
cd /root/vitastor/rpm; \
|
||||
sh build-tarball.sh; \
|
||||
cp /root/vitastor-0.6.17.el8.tar.gz ~/rpmbuild/SOURCES; \
|
||||
cp /root/vitastor-0.8.3.el8.tar.gz ~/rpmbuild/SOURCES; \
|
||||
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||
cd ~/rpmbuild/SPECS/; \
|
||||
rpmbuild -ba vitastor.spec; \
|
||||
|
@@ -1,17 +1,18 @@
|
||||
Name: vitastor
|
||||
Version: 0.6.17
|
||||
Version: 0.8.3
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-0.6.17.el8.tar.gz
|
||||
Source0: vitastor-0.8.3.el8.tar.gz
|
||||
|
||||
BuildRequires: liburing-devel >= 0.6
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-toolset-9-gcc-c++
|
||||
BuildRequires: nodejs >= 10
|
||||
BuildRequires: jerasure-devel
|
||||
BuildRequires: libisa-l-devel
|
||||
BuildRequires: gf-complete-devel
|
||||
BuildRequires: libibverbs-devel
|
||||
BuildRequires: cmake
|
||||
@@ -31,8 +32,11 @@ size with configurable redundancy (replication or erasure codes/XOR).
|
||||
%package -n vitastor-osd
|
||||
Summary: Vitastor - OSD
|
||||
Requires: libJerasure2
|
||||
Requires: libisa-l
|
||||
Requires: liburing >= 0.6
|
||||
Requires: vitastor-client = %{version}-%{release}
|
||||
Requires: util-linux
|
||||
Requires: parted
|
||||
|
||||
|
||||
%description -n vitastor-osd
|
||||
@@ -97,8 +101,11 @@ cd mon
|
||||
npm install
|
||||
cd ..
|
||||
mkdir -p %buildroot/usr/lib/vitastor
|
||||
cp mon/make-osd.sh %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
|
||||
@@ -107,11 +114,29 @@ cp -r mon %buildroot/usr/lib/vitastor
|
||||
|
||||
%files -n vitastor-osd
|
||||
%_bindir/vitastor-osd
|
||||
%_bindir/vitastor-disk
|
||||
%_bindir/vitastor-dump-journal
|
||||
/lib/systemd/system/vitastor-osd@.service
|
||||
/lib/systemd/system/vitastor.target
|
||||
/lib/udev/rules.d/90-vitastor.rules
|
||||
|
||||
|
||||
%pre -n vitastor-osd
|
||||
groupadd -r -f vitastor 2>/dev/null ||:
|
||||
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||
install -o vitastor -g vitastor -d /var/log/vitastor
|
||||
mkdir -p /etc/vitastor
|
||||
|
||||
|
||||
%files -n vitastor-mon
|
||||
/usr/lib/vitastor/mon
|
||||
/lib/systemd/system/vitastor-mon.service
|
||||
|
||||
|
||||
%pre -n vitastor-mon
|
||||
groupadd -r -f vitastor 2>/dev/null ||:
|
||||
useradd -r -g vitastor -s /sbin/nologin -c "Vitastor daemons" -M -d /nonexistent vitastor 2>/dev/null ||:
|
||||
mkdir -p /etc/vitastor
|
||||
|
||||
|
||||
%files -n vitastor-client
|
||||
@@ -122,7 +147,6 @@ cp -r mon %buildroot/usr/lib/vitastor
|
||||
%_bindir/vita
|
||||
%_libdir/libvitastor_blk.so*
|
||||
%_libdir/libvitastor_client.so*
|
||||
/usr/lib/vitastor/make-osd.sh
|
||||
|
||||
|
||||
%files -n vitastor-client-devel
|
||||
|
@@ -15,7 +15,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
add_definitions(-DVERSION="0.6.17")
|
||||
add_definitions(-DVERSION="0.8.3")
|
||||
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
||||
if (${WITH_ASAN})
|
||||
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
||||
@@ -50,6 +50,10 @@ pkg_check_modules(IBVERBS libibverbs)
|
||||
if (IBVERBS_LIBRARIES)
|
||||
add_definitions(-DWITH_RDMA)
|
||||
endif (IBVERBS_LIBRARIES)
|
||||
pkg_check_modules(ISAL libisal)
|
||||
if (ISAL_LIBRARIES)
|
||||
add_definitions(-DWITH_ISAL)
|
||||
endif (ISAL_LIBRARIES)
|
||||
|
||||
include_directories(
|
||||
../
|
||||
@@ -60,7 +64,7 @@ include_directories(
|
||||
|
||||
# libvitastor_blk.so
|
||||
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
|
||||
)
|
||||
target_link_libraries(vitastor_blk
|
||||
@@ -90,7 +94,7 @@ endif (IBVERBS_LIBRARIES)
|
||||
add_library(vitastor_common STATIC
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -104,6 +108,7 @@ target_link_libraries(vitastor-osd
|
||||
vitastor_common
|
||||
vitastor_blk
|
||||
Jerasure
|
||||
${ISAL_LIBRARIES}
|
||||
${IBVERBS_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -126,7 +131,6 @@ add_library(vitastor_client SHARED
|
||||
vitastor_c.cpp
|
||||
cli_common.cpp
|
||||
cli_alloc_osd.cpp
|
||||
cli_simple_offsets.cpp
|
||||
cli_status.cpp
|
||||
cli_df.cpp
|
||||
cli_ls.cpp
|
||||
@@ -136,6 +140,7 @@ add_library(vitastor_client SHARED
|
||||
cli_merge.cpp
|
||||
cli_rm_data.cpp
|
||||
cli_rm.cpp
|
||||
cli_rm_osd.cpp
|
||||
)
|
||||
set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "vitastor_c.h")
|
||||
target_link_libraries(vitastor_client
|
||||
@@ -188,9 +193,15 @@ target_link_libraries(vitastor-cli
|
||||
)
|
||||
configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
||||
|
||||
# vitastor-dump-journal
|
||||
add_executable(vitastor-dump-journal
|
||||
dump_journal.cpp crc32c.c
|
||||
# vitastor-disk
|
||||
add_executable(vitastor-disk
|
||||
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})
|
||||
@@ -224,8 +235,15 @@ add_executable(osd_test osd_test.cpp rw_blocking.cpp addr_util.cpp)
|
||||
target_link_libraries(osd_test tcmalloc_minimal)
|
||||
|
||||
# osd_rmw_test
|
||||
# FIXME: Move to tests
|
||||
add_executable(osd_rmw_test 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)
|
||||
|
||||
if (ISAL_LIBRARIES)
|
||||
add_executable(osd_rmw_test_je 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)
|
||||
endif (ISAL_LIBRARIES)
|
||||
|
||||
# stub_uring_osd
|
||||
add_executable(stub_uring_osd
|
||||
@@ -253,6 +271,14 @@ target_link_libraries(test_cas
|
||||
vitastor_client
|
||||
)
|
||||
|
||||
# test_crc32
|
||||
add_executable(test_crc32
|
||||
test_crc32.cpp
|
||||
)
|
||||
target_link_libraries(test_crc32
|
||||
vitastor_blk
|
||||
)
|
||||
|
||||
# test_cluster_client
|
||||
add_executable(test_cluster_client
|
||||
test_cluster_client.cpp
|
||||
@@ -270,7 +296,8 @@ target_include_directories(test_cluster_client PUBLIC ${CMAKE_SOURCE_DIR}/src/mo
|
||||
|
||||
### 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}/vita)
|
||||
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 <map>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
#include "object_id.h"
|
||||
@@ -19,15 +18,19 @@
|
||||
#include "timerfd_manager.h"
|
||||
|
||||
// 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
|
||||
#define MEM_ALIGNMENT 4096
|
||||
#endif
|
||||
|
||||
// Default block size is 128 KB, current allowed range is 4K - 128M
|
||||
#define DEFAULT_ORDER 17
|
||||
#define MIN_BLOCK_SIZE 4*1024
|
||||
#define MAX_BLOCK_SIZE 128*1024*1024
|
||||
#define DEFAULT_DATA_BLOCK_ORDER 17
|
||||
#define MIN_DATA_BLOCK_SIZE 4*1024
|
||||
#define MAX_DATA_BLOCK_SIZE 128*1024*1024
|
||||
#define DEFAULT_BITMAP_GRANULARITY 4096
|
||||
|
||||
#define BS_OP_MIN 1
|
||||
@@ -151,7 +154,7 @@ struct blockstore_op_t
|
||||
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;
|
||||
|
||||
@@ -189,7 +192,6 @@ public:
|
||||
// Print diagnostics to stdout
|
||||
void dump_diagnostics();
|
||||
|
||||
// FIXME rename to object_size
|
||||
uint32_t get_block_size();
|
||||
uint64_t get_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;
|
||||
syncing_flushers = 0;
|
||||
// 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_counter = 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];
|
||||
for (int i = 0; i < max_flusher_count; i++)
|
||||
{
|
||||
@@ -35,24 +35,14 @@ journal_flusher_co::journal_flusher_co()
|
||||
{
|
||||
bs->live = true;
|
||||
if (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 :-("
|
||||
);
|
||||
}
|
||||
bs->disk_error_abort("read operation during flush", data->res, data->iov.iov_len);
|
||||
wait_count--;
|
||||
};
|
||||
simple_callback_w = [this](ring_data_t* data)
|
||||
{
|
||||
bs->live = true;
|
||||
if (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"
|
||||
);
|
||||
}
|
||||
bs->disk_error_abort("write operation during flush", data->res, data->iov.iov_len);
|
||||
wait_count--;
|
||||
};
|
||||
}
|
||||
@@ -87,7 +77,7 @@ void journal_flusher_t::loop()
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -172,7 +162,8 @@ void journal_flusher_t::mark_trim_possible()
|
||||
if (trim_wanted > 0)
|
||||
{
|
||||
dequeuing = true;
|
||||
journal_trim_counter++;
|
||||
if (!journal_trim_counter)
|
||||
journal_trim_counter = journal_trim_interval;
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
}
|
||||
@@ -306,6 +297,8 @@ bool journal_flusher_co::loop()
|
||||
goto resume_20;
|
||||
else if (wait_state == 21)
|
||||
goto resume_21;
|
||||
else if (wait_state == 22)
|
||||
goto resume_22;
|
||||
resume_0:
|
||||
if (flusher->flush_queue.size() < flusher->min_flusher_count && !flusher->trim_wanted ||
|
||||
!flusher->flush_queue.size() || !flusher->dequeuing)
|
||||
@@ -486,31 +479,38 @@ resume_1:
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
// 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
|
||||
? (uint8_t*)meta_new.buf + meta_new.pos*bs->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*)meta_new.buf + meta_new.pos*bs->dsk.clean_entry_size + sizeof(clean_disk_entry)
|
||||
: (uint8_t*)bs->clean_bitmap + (clean_loc >> bs->dsk.block_order)*(2*bs->dsk.clean_entry_bitmap_size));
|
||||
if (clean_init_bitmap)
|
||||
{
|
||||
memset(new_clean_bitmap, 0, bs->clean_entry_bitmap_size);
|
||||
bitmap_set(new_clean_bitmap, clean_bitmap_offset, clean_bitmap_len, bs->bitmap_granularity);
|
||||
memset(new_clean_bitmap, 0, bs->dsk.clean_entry_bitmap_size);
|
||||
bitmap_set(new_clean_bitmap, clean_bitmap_offset, clean_bitmap_len, bs->dsk.bitmap_granularity);
|
||||
}
|
||||
}
|
||||
for (it = v.begin(); it != v.end(); it++)
|
||||
{
|
||||
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);
|
||||
data->iov = (struct iovec){ it->buf, (size_t)it->len };
|
||||
data->callback = simple_callback_w;
|
||||
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 for data writes before fsyncing it
|
||||
resume_22:
|
||||
if (wait_count > 0)
|
||||
{
|
||||
wait_state = 22;
|
||||
return false;
|
||||
}
|
||||
// Sync data before writing metadata
|
||||
resume_16:
|
||||
resume_17:
|
||||
@@ -521,7 +521,7 @@ resume_1:
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
// metadata sector is still being read or data is still being written, wait for it
|
||||
@@ -536,35 +536,35 @@ resume_1:
|
||||
return false;
|
||||
}
|
||||
// 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);
|
||||
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;
|
||||
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++;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
// 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
exit(1);
|
||||
}
|
||||
@@ -572,20 +572,20 @@ resume_1:
|
||||
new_entry->version = cur.version;
|
||||
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
|
||||
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;
|
||||
memcpy((uint8_t*)(new_entry+1) + bs->clean_entry_bitmap_size, bmp_ptr, bs->clean_entry_bitmap_size);
|
||||
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->dsk.clean_entry_bitmap_size, bmp_ptr, bs->dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
}
|
||||
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;
|
||||
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++;
|
||||
resume_7:
|
||||
@@ -615,7 +615,12 @@ resume_1:
|
||||
}
|
||||
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();
|
||||
// And sync metadata (in batches - not per each operation!)
|
||||
@@ -669,9 +674,9 @@ resume_1:
|
||||
.version = JOURNAL_VERSION,
|
||||
};
|
||||
((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;
|
||||
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++;
|
||||
resume_13:
|
||||
if (wait_count > 0)
|
||||
@@ -682,7 +687,7 @@ resume_1:
|
||||
if (!bs->disable_journal_fsync)
|
||||
{
|
||||
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->callback = simple_callback_w;
|
||||
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_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++;
|
||||
if (bs->journal.inmemory)
|
||||
{
|
||||
// Take it from memory
|
||||
memcpy(it->buf, (uint8_t*)bs->journal.buffer + submit_offset, submit_len);
|
||||
// Take it from memory, don't copy it
|
||||
it->buf = (uint8_t*)bs->journal.buffer + submit_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read it from disk
|
||||
it->buf = memalign_or_die(MEM_ALIGNMENT, submit_len);
|
||||
await_sqe(0);
|
||||
data->iov = (struct iovec){ it->buf, (size_t)submit_len };
|
||||
data->callback = simple_callback_r;
|
||||
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++;
|
||||
}
|
||||
@@ -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,
|
||||
// so I'll avoid it as long as I can.
|
||||
wr.submitted = false;
|
||||
wr.sector = ((meta_loc >> bs->block_order) / (bs->meta_block_size / bs->clean_entry_size)) * bs->meta_block_size;
|
||||
wr.pos = ((meta_loc >> bs->block_order) % (bs->meta_block_size / bs->clean_entry_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->dsk.block_order) % (bs->dsk.meta_block_size / bs->dsk.clean_entry_size));
|
||||
if (bs->inmemory_meta)
|
||||
{
|
||||
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())
|
||||
{
|
||||
// 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){
|
||||
.offset = wr.sector,
|
||||
.len = bs->meta_block_size,
|
||||
.len = bs->dsk.meta_block_size,
|
||||
.state = 0, // 0 = not read yet
|
||||
.buf = wr.buf,
|
||||
.usage_count = 1,
|
||||
}).first;
|
||||
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;
|
||||
wr.submitted = true;
|
||||
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++;
|
||||
}
|
||||
@@ -867,11 +873,11 @@ void journal_flusher_co::update_clean_db()
|
||||
{
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
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,
|
||||
clean_loc >> bs->block_order);
|
||||
clean_loc >> bs->dsk.block_order);
|
||||
#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);
|
||||
if (has_delete)
|
||||
@@ -880,10 +886,10 @@ void journal_flusher_co::update_clean_db()
|
||||
clean_db.erase(clean_it);
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
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);
|
||||
#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;
|
||||
}
|
||||
else
|
||||
@@ -932,7 +938,7 @@ bool journal_flusher_co::fsync_batch(bool fsync_meta, int wait_base)
|
||||
await_sqe(0);
|
||||
data->iov = { 0 };
|
||||
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;
|
||||
wait_count++;
|
||||
resume_2:
|
||||
|
@@ -11,25 +11,19 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_t *
|
||||
ring_consumer.loop = [this]() { loop(); };
|
||||
ringloop->register_consumer(&ring_consumer);
|
||||
initialized = 0;
|
||||
data_fd = meta_fd = journal.fd = -1;
|
||||
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
|
||||
{
|
||||
open_data();
|
||||
open_meta();
|
||||
open_journal();
|
||||
dsk.open_data();
|
||||
dsk.open_meta();
|
||||
dsk.open_journal();
|
||||
calc_lengths();
|
||||
data_alloc = new allocator(block_count);
|
||||
data_alloc = new allocator(dsk.block_count);
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
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);
|
||||
dsk.close_all();
|
||||
throw;
|
||||
}
|
||||
flusher = new journal_flusher_t(this);
|
||||
@@ -41,12 +35,7 @@ blockstore_impl_t::~blockstore_impl_t()
|
||||
delete flusher;
|
||||
free(zero_object);
|
||||
ringloop->unregister_consumer(&ring_consumer);
|
||||
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);
|
||||
dsk.close_all();
|
||||
if (metadata_buffer)
|
||||
free(metadata_buffer);
|
||||
if (clean_bitmap)
|
||||
@@ -118,7 +107,7 @@ void blockstore_impl_t::loop()
|
||||
// has_writes == 0 - no writes before the current queue item
|
||||
// has_writes == 1 - some writes in progress
|
||||
// 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++)
|
||||
{
|
||||
auto op = submit_queue[op_idx];
|
||||
@@ -199,16 +188,12 @@ void blockstore_impl_t::loop()
|
||||
else if (op->opcode == BS_OP_LIST)
|
||||
{
|
||||
// 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
|
||||
if (single_tick_list_limit <= 0 || done_lists < single_tick_list_limit)
|
||||
{
|
||||
process_list(op);
|
||||
done_lists++;
|
||||
wr_st = 2;
|
||||
}
|
||||
process_list(op);
|
||||
wr_st = 2;
|
||||
}
|
||||
if (wr_st == 2)
|
||||
{
|
||||
submit_queue[op_idx] = NULL;
|
||||
new_idx--;
|
||||
}
|
||||
if (wr_st == 0)
|
||||
@@ -317,17 +302,6 @@ void blockstore_impl_t::check_wait(blockstore_op_t *op)
|
||||
// do not submit
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
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
|
||||
return;
|
||||
}
|
||||
@@ -343,9 +317,9 @@ void blockstore_impl_t::enqueue_op(blockstore_op_t *op)
|
||||
{
|
||||
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->offset >= block_size ||
|
||||
op->len > block_size-op->offset ||
|
||||
(op->len % disk_alignment)
|
||||
op->offset >= dsk.data_block_size ||
|
||||
op->len > dsk.data_block_size-op->offset ||
|
||||
(op->len % dsk.disk_alignment)
|
||||
)) ||
|
||||
readonly && op->opcode != BS_OP_READ && op->opcode != BS_OP_LIST)
|
||||
{
|
||||
@@ -477,7 +451,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||
uint64_t min_inode = op->oid.inode;
|
||||
uint64_t max_inode = op->version;
|
||||
// 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;
|
||||
FINISH_OP(op);
|
||||
@@ -609,7 +583,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||
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
|
||||
if (!replace_stable(dirty_it->first.oid, dirty_it->first.version, 0, clean_stable_count, stable))
|
||||
@@ -698,3 +672,16 @@ void blockstore_impl_t::dump_diagnostics()
|
||||
journal.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
|
||||
|
||||
#include "blockstore.h"
|
||||
#include "blockstore_disk.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <new>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "cpp-btree/btree_map.h"
|
||||
|
||||
@@ -90,13 +92,13 @@
|
||||
#include "blockstore_journal.h"
|
||||
|
||||
// "VITAstor"
|
||||
#define BLOCKSTORE_META_MAGIC 0x726F747341544956l
|
||||
#define BLOCKSTORE_META_VERSION 1
|
||||
#define BLOCKSTORE_META_MAGIC_V1 0x726F747341544956l
|
||||
#define BLOCKSTORE_META_VERSION_V1 1
|
||||
|
||||
// metadata header (superblock)
|
||||
// FIXME: After adding the OSD superblock, add a key to metadata
|
||||
// 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 magic;
|
||||
@@ -158,12 +160,11 @@ struct __attribute__((__packed__)) dirty_entry
|
||||
#define WAIT_JOURNAL 3
|
||||
// Suspend operation until the next journal sector buffer is free
|
||||
#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
|
||||
{
|
||||
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)
|
||||
@@ -217,23 +218,10 @@ struct pool_shard_settings_t
|
||||
|
||||
class blockstore_impl_t
|
||||
{
|
||||
blockstore_disk_t dsk;
|
||||
|
||||
/******* 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;
|
||||
// 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
|
||||
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"
|
||||
@@ -252,8 +240,6 @@ class blockstore_impl_t
|
||||
int throttle_target_parallelism = 1;
|
||||
// Minimum difference in microseconds between target and real execution times to throttle the response
|
||||
int throttle_threshold_us = 50;
|
||||
// Maximum number of LIST operations to be processed between
|
||||
int single_tick_list_limit = 1;
|
||||
/******* END OF OPTIONS *******/
|
||||
|
||||
struct ring_consumer_t ring_consumer;
|
||||
@@ -268,16 +254,6 @@ class blockstore_impl_t
|
||||
allocator *data_alloc = NULL;
|
||||
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;
|
||||
|
||||
struct journal_t journal;
|
||||
@@ -314,6 +290,7 @@ class blockstore_impl_t
|
||||
// Journaling
|
||||
void prepare_journal_sector_write(int sector, blockstore_op_t *op);
|
||||
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
|
||||
int initialized;
|
||||
@@ -326,7 +303,7 @@ class blockstore_impl_t
|
||||
// Read
|
||||
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,
|
||||
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,
|
||||
uint32_t item_state, uint64_t item_version);
|
||||
void handle_read_event(ring_data_t *data, blockstore_op_t *op);
|
||||
@@ -394,9 +371,9 @@ public:
|
||||
// Print diagnostics to stdout
|
||||
void dump_diagnostics();
|
||||
|
||||
inline uint32_t get_block_size() { return block_size; }
|
||||
inline uint64_t get_block_count() { return block_count; }
|
||||
inline uint32_t get_block_size() { return dsk.data_block_size; }
|
||||
inline uint64_t get_block_count() { return dsk.block_count; }
|
||||
inline uint64_t get_free_block_count() { return data_alloc->get_free_count(); }
|
||||
inline uint32_t get_bitmap_granularity() { return disk_alignment; }
|
||||
inline uint64_t get_journal_size() { return journal.len; }
|
||||
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
|
||||
inline uint64_t get_journal_size() { return dsk.journal_len; }
|
||||
};
|
||||
|
@@ -3,6 +3,11 @@
|
||||
|
||||
#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() \
|
||||
sqe = bs->get_sqe();\
|
||||
if (!sqe)\
|
||||
@@ -22,32 +27,33 @@ blockstore_init_meta::blockstore_init_meta(blockstore_impl_t *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)
|
||||
{
|
||||
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)
|
||||
);
|
||||
}
|
||||
prev_done = data->res > 0 ? submitted : 0;
|
||||
done_len = data->res;
|
||||
done_pos = metadata_read;
|
||||
metadata_read += data->res;
|
||||
submitted = 0;
|
||||
if (buf_num >= 0)
|
||||
{
|
||||
bufs[buf_num].state = (bufs[buf_num].state == INIT_META_READING
|
||||
? INIT_META_READ_DONE
|
||||
: INIT_META_EMPTY);
|
||||
}
|
||||
submitted--;
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
|
||||
int blockstore_init_meta::loop()
|
||||
{
|
||||
if (wait_state == 1)
|
||||
goto resume_1;
|
||||
else if (wait_state == 2)
|
||||
goto resume_2;
|
||||
else if (wait_state == 3)
|
||||
goto resume_3;
|
||||
else if (wait_state == 4)
|
||||
goto resume_4;
|
||||
if (wait_state == 1) goto resume_1;
|
||||
else if (wait_state == 2) goto resume_2;
|
||||
else if (wait_state == 3) goto resume_3;
|
||||
else if (wait_state == 4) goto resume_4;
|
||||
else if (wait_state == 5) goto resume_5;
|
||||
else if (wait_state == 6) goto resume_6;
|
||||
printf("Reading blockstore metadata\n");
|
||||
if (bs->inmemory_meta)
|
||||
metadata_buffer = bs->metadata_buffer;
|
||||
@@ -57,27 +63,27 @@ int blockstore_init_meta::loop()
|
||||
throw std::runtime_error("Failed to allocate metadata read buffer");
|
||||
// Read superblock
|
||||
GET_SQE();
|
||||
data->iov = { metadata_buffer, bs->meta_block_size };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
||||
my_uring_prep_readv(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset);
|
||||
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);
|
||||
bs->ringloop->submit();
|
||||
submitted = 1;
|
||||
submitted++;
|
||||
resume_1:
|
||||
if (submitted)
|
||||
if (submitted > 0)
|
||||
{
|
||||
wait_state = 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->magic = BLOCKSTORE_META_MAGIC;
|
||||
hdr->version = BLOCKSTORE_META_VERSION;
|
||||
hdr->meta_block_size = bs->meta_block_size;
|
||||
hdr->data_block_size = bs->block_size;
|
||||
hdr->bitmap_granularity = bs->bitmap_granularity;
|
||||
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
||||
hdr->version = BLOCKSTORE_META_VERSION_V1;
|
||||
hdr->meta_block_size = bs->dsk.meta_block_size;
|
||||
hdr->data_block_size = bs->dsk.data_block_size;
|
||||
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
|
||||
}
|
||||
if (bs->readonly)
|
||||
{
|
||||
@@ -87,11 +93,11 @@ resume_1:
|
||||
{
|
||||
printf("Initializing metadata area\n");
|
||||
GET_SQE();
|
||||
data->iov = (struct iovec){ metadata_buffer, bs->meta_block_size };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data); };
|
||||
my_uring_prep_writev(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset);
|
||||
data->iov = (struct iovec){ 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);
|
||||
bs->ringloop->submit();
|
||||
submitted = 1;
|
||||
submitted++;
|
||||
resume_3:
|
||||
if (submitted > 0)
|
||||
{
|
||||
@@ -103,10 +109,10 @@ resume_1:
|
||||
}
|
||||
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 ||
|
||||
hdr->magic != BLOCKSTORE_META_MAGIC ||
|
||||
hdr->version != BLOCKSTORE_META_VERSION)
|
||||
hdr->magic != BLOCKSTORE_META_MAGIC_V1 ||
|
||||
hdr->version != BLOCKSTORE_META_VERSION_V1)
|
||||
{
|
||||
printf(
|
||||
"Metadata is corrupt or old version.\n"
|
||||
@@ -115,80 +121,132 @@ resume_1:
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
if (hdr->meta_block_size != bs->meta_block_size ||
|
||||
hdr->data_block_size != bs->block_size ||
|
||||
hdr->bitmap_granularity != bs->bitmap_granularity)
|
||||
if (hdr->meta_block_size != bs->dsk.meta_block_size ||
|
||||
hdr->data_block_size != bs->dsk.data_block_size ||
|
||||
hdr->bitmap_granularity != bs->dsk.bitmap_granularity)
|
||||
{
|
||||
printf(
|
||||
"Configuration stored in metadata superblock"
|
||||
" (meta_block_size=%u, data_block_size=%u, bitmap_granularity=%u)"
|
||||
" differs from OSD configuration (%lu/%u/%lu).\n",
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Skip superblock
|
||||
bs->meta_offset += bs->meta_block_size;
|
||||
bs->meta_len -= bs->meta_block_size;
|
||||
prev_done = 0;
|
||||
done_len = 0;
|
||||
done_pos = 0;
|
||||
metadata_read = 0;
|
||||
md_offset = bs->dsk.meta_block_size;
|
||||
next_offset = md_offset;
|
||||
entries_per_block = bs->dsk.meta_block_size / bs->dsk.clean_entry_size;
|
||||
// Read the rest of the metadata
|
||||
while (1)
|
||||
resume_2:
|
||||
if (next_offset < bs->dsk.meta_len && submitted == 0)
|
||||
{
|
||||
resume_2:
|
||||
if (submitted)
|
||||
// Submit one read
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
wait_state = 2;
|
||||
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
|
||||
if (!bufs[i].state)
|
||||
{
|
||||
// Fill metadata with zeroes
|
||||
memset(data->iov.iov_base, 0, data->iov.iov_len);
|
||||
my_uring_prep_writev(sqe, bs->meta_fd, &data->iov, 1, bs->meta_offset + metadata_read);
|
||||
bufs[i].buf = (uint8_t*)metadata_buffer + (bs->inmemory_meta
|
||||
? next_offset-md_offset
|
||||
: 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
|
||||
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)
|
||||
{
|
||||
free(metadata_buffer);
|
||||
@@ -197,10 +255,10 @@ resume_1:
|
||||
if (zero_on_init && !bs->disable_meta_fsync)
|
||||
{
|
||||
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->callback = [this](ring_data_t *data) { handle_event(data); };
|
||||
submitted = 1;
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||
submitted++;
|
||||
bs->ringloop->submit();
|
||||
resume_4:
|
||||
if (submitted > 0)
|
||||
@@ -212,14 +270,18 @@ resume_1:
|
||||
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);
|
||||
if (!bs->inmemory_meta && bs->clean_entry_bitmap_size)
|
||||
clean_disk_entry *entry = (clean_disk_entry*)(buf + i*bs->dsk.clean_entry_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)
|
||||
{
|
||||
@@ -230,17 +292,39 @@ void blockstore_init_meta::handle_entries(void* entries, unsigned count, int blo
|
||||
if (clean_it != clean_db.end())
|
||||
{
|
||||
// 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
|
||||
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,
|
||||
done_cnt+i);
|
||||
#endif
|
||||
bs->data_alloc->set(clean_it->second.location >> block_order, false);
|
||||
bs->data_alloc->set(old_clean_loc, false);
|
||||
}
|
||||
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++;
|
||||
#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);
|
||||
clean_db[entry->oid] = (struct clean_entry){
|
||||
.version = entry->version,
|
||||
.location = (done_cnt+i) << block_order,
|
||||
.location = (done_cnt+i) << bs->dsk.block_order,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// here we also have to zero out the entry
|
||||
updated = true;
|
||||
memset(entry, 0, bs->dsk.clean_entry_size);
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
printf("Old clean entry %lu: %lx:%lx v%lu\n", done_cnt+i, entry->oid.inode, entry->oid.stripe, entry->version);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
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->iov = { submitted_buf, bs->journal.block_size };
|
||||
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();
|
||||
wait_count = 1;
|
||||
resume_1:
|
||||
@@ -367,7 +455,7 @@ resume_1:
|
||||
GET_SQE();
|
||||
data->iov = (struct iovec){ submitted_buf, 2*bs->journal.block_size };
|
||||
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++;
|
||||
bs->ringloop->submit();
|
||||
resume_6:
|
||||
@@ -379,7 +467,7 @@ resume_1:
|
||||
if (!bs->disable_journal_fsync)
|
||||
{
|
||||
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->callback = simple_callback;
|
||||
wait_count++;
|
||||
@@ -448,7 +536,7 @@ resume_1:
|
||||
end - journal_pos < JOURNAL_BUFFER_SIZE ? end - journal_pos : JOURNAL_BUFFER_SIZE,
|
||||
};
|
||||
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();
|
||||
}
|
||||
while (done.size() > 0)
|
||||
@@ -463,7 +551,7 @@ resume_1:
|
||||
GET_SQE();
|
||||
data->iov = { init_write_buf, bs->journal.block_size };
|
||||
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++;
|
||||
bs->ringloop->submit();
|
||||
resume_7:
|
||||
@@ -477,7 +565,7 @@ resume_1:
|
||||
GET_SQE();
|
||||
data->iov = { 0 };
|
||||
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++;
|
||||
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.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;
|
||||
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_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
|
||||
{
|
||||
@@ -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
|
||||
// a patched map with dynamic entry size, but not the btree_map,
|
||||
// because it doesn't keep iterators valid all the time.
|
||||
bmp = malloc_or_die(bs->clean_entry_bitmap_size);
|
||||
memcpy(bmp, bmp_from, bs->clean_entry_bitmap_size);
|
||||
bmp = malloc_or_die(bs->dsk.clean_entry_bitmap_size);
|
||||
memcpy(bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
bs->dirty_db.emplace(ov, (dirty_entry){
|
||||
.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(
|
||||
"je_big_write%s oid=%lx:%lx ver=%lu loc=%lu\n",
|
||||
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
|
||||
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_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
|
||||
{
|
||||
@@ -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
|
||||
// a patched map with dynamic entry size, but not the btree_map,
|
||||
// because it doesn't keep iterators valid all the time.
|
||||
bmp = malloc_or_die(bs->clean_entry_bitmap_size);
|
||||
memcpy(bmp, bmp_from, bs->clean_entry_bitmap_size);
|
||||
bmp = malloc_or_die(bs->dsk.clean_entry_bitmap_size);
|
||||
memcpy(bmp, bmp_from, bs->dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
auto dirty_it = bs->dirty_db.emplace(ov, (dirty_entry){
|
||||
.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,
|
||||
.bitmap = bmp,
|
||||
}).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
|
||||
// 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
|
||||
printf(
|
||||
"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
|
||||
);
|
||||
#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]++;
|
||||
#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;
|
||||
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);
|
||||
// Remove it from the flusher's queue, too
|
||||
|
@@ -3,20 +3,32 @@
|
||||
|
||||
#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
|
||||
{
|
||||
blockstore_impl_t *bs;
|
||||
int wait_state = 0;
|
||||
bool zero_on_init = false;
|
||||
void *metadata_buffer = NULL;
|
||||
uint64_t metadata_read = 0;
|
||||
int prev = 0, prev_done = 0, done_len = 0, submitted = 0;
|
||||
uint64_t done_cnt = 0, done_pos = 0;
|
||||
uint64_t entries_loaded = 0;
|
||||
blockstore_init_meta_buf bufs[2] = {};
|
||||
int submitted = 0;
|
||||
struct io_uring_sqe *sqe;
|
||||
struct ring_data_t *data;
|
||||
void handle_entries(void *entries, unsigned count, int block_order);
|
||||
void handle_event(ring_data_t *data);
|
||||
uint64_t md_offset = 0;
|
||||
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:
|
||||
blockstore_init_meta(blockstore_impl_t *bs);
|
||||
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); };
|
||||
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;
|
||||
@@ -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)
|
||||
{
|
||||
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
||||
throw std::runtime_error(
|
||||
"journal write failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
||||
);
|
||||
disk_error_abort("journal write", data->res, data->iov.iov_len);
|
||||
}
|
||||
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)
|
||||
|
@@ -10,11 +10,13 @@
|
||||
#define JOURNAL_MAGIC 0x4A33
|
||||
#define JOURNAL_VERSION 1
|
||||
#define JOURNAL_BUFFER_SIZE 4*1024*1024
|
||||
#define JOURNAL_ENTRY_HEADER_SIZE 16
|
||||
|
||||
// We reserve some extra space for future stabilize requests during writes
|
||||
// FIXME: This value should be dynamic i.e. Blockstore ideally shouldn't allow
|
||||
// writing more than can be stabilized afterwards
|
||||
#define JOURNAL_STABILIZE_RESERVATION 65536
|
||||
#define JOURNAL_INSTANT_RESERVATION 131072
|
||||
|
||||
// Journal entries
|
||||
// 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
|
||||
{
|
||||
int fd;
|
||||
uint64_t device_size;
|
||||
bool inmemory = false;
|
||||
bool flush_journal = false;
|
||||
void *buffer = NULL;
|
||||
|
@@ -4,23 +4,10 @@
|
||||
#include <sys/file.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)
|
||||
{
|
||||
// Common disk options
|
||||
dsk.parse_config(config);
|
||||
// Parse
|
||||
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;
|
||||
}
|
||||
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")
|
||||
{
|
||||
// Only flush journal and exit
|
||||
@@ -56,24 +39,11 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
||||
immediate_commit = IMMEDIATE_SMALL;
|
||||
}
|
||||
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";
|
||||
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.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";
|
||||
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);
|
||||
if (!max_flusher_count)
|
||||
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_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
||||
// 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)
|
||||
{
|
||||
max_flusher_count = 256;
|
||||
@@ -105,62 +67,6 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
||||
{
|
||||
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)
|
||||
{
|
||||
journal.sector_count = 32;
|
||||
@@ -169,11 +75,11 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
||||
{
|
||||
metadata_buf_size = 4*1024*1024;
|
||||
}
|
||||
if (meta_device == "")
|
||||
if (dsk.meta_device == dsk.data_device)
|
||||
{
|
||||
disable_meta_fsync = disable_data_fsync;
|
||||
}
|
||||
if (journal_device == "")
|
||||
if (dsk.journal_device == dsk.meta_device)
|
||||
{
|
||||
disable_journal_fsync = disable_meta_fsync;
|
||||
}
|
||||
@@ -202,238 +108,46 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
|
||||
throttle_threshold_us = 50;
|
||||
}
|
||||
// init some fields
|
||||
clean_entry_bitmap_size = block_size / bitmap_granularity / 8;
|
||||
clean_entry_size = sizeof(clean_disk_entry) + 2*clean_entry_bitmap_size;
|
||||
journal.block_size = journal_block_size;
|
||||
journal.next_free = journal_block_size;
|
||||
journal.used_start = journal_block_size;
|
||||
journal.block_size = dsk.journal_block_size;
|
||||
journal.next_free = dsk.journal_block_size;
|
||||
journal.used_start = dsk.journal_block_size;
|
||||
// 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()
|
||||
{
|
||||
// data
|
||||
data_len = data_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 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");
|
||||
}
|
||||
dsk.calc_lengths();
|
||||
journal.len = dsk.journal_len;
|
||||
journal.block_size = dsk.journal_block_size;
|
||||
journal.offset = dsk.journal_offset;
|
||||
if (inmemory_meta)
|
||||
{
|
||||
metadata_buffer = memalign(MEM_ALIGNMENT, meta_len);
|
||||
metadata_buffer = memalign(MEM_ALIGNMENT, dsk.meta_len);
|
||||
if (!metadata_buffer)
|
||||
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)
|
||||
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)
|
||||
{
|
||||
journal.buffer = memalign(MEM_ALIGNMENT, journal.len);
|
||||
if (!journal.buffer)
|
||||
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
|
||||
{
|
||||
throw std::runtime_error(name+" is neither a file nor a block device");
|
||||
}
|
||||
}
|
||||
|
||||
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_buf = (uint8_t*)memalign(MEM_ALIGNMENT, journal.sector_count * dsk.journal_block_size);
|
||||
if (!journal.sector_buf)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
journal.sector_info = (journal_sector_info_t*)calloc(journal.sector_count, sizeof(journal_sector_info_t));
|
||||
if (!journal.sector_info)
|
||||
{
|
||||
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++;
|
||||
my_uring_prep_readv(
|
||||
sqe,
|
||||
IS_JOURNAL(item_state) ? journal.fd : data_fd,
|
||||
IS_JOURNAL(item_state) ? dsk.journal_fd : dsk.data_fd,
|
||||
&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); };
|
||||
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
|
||||
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;
|
||||
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 = {
|
||||
.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);
|
||||
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 *clean_entry_bitmap;
|
||||
uint64_t meta_loc = block_loc >> block_order;
|
||||
uint64_t meta_loc = block_loc >> dsk.block_order;
|
||||
if (inmemory_meta)
|
||||
{
|
||||
uint64_t sector = (meta_loc / (meta_block_size / clean_entry_size)) * meta_block_size;
|
||||
uint64_t pos = (meta_loc % (meta_block_size / clean_entry_size));
|
||||
clean_entry_bitmap = ((uint8_t*)metadata_buffer + sector + pos*clean_entry_size + sizeof(clean_disk_entry) + offset);
|
||||
uint64_t sector = (meta_loc / (dsk.meta_block_size / dsk.clean_entry_size)) * dsk.meta_block_size;
|
||||
uint64_t pos = (meta_loc % (dsk.meta_block_size / dsk.clean_entry_size));
|
||||
clean_entry_bitmap = ((uint8_t*)metadata_buffer + sector + pos*dsk.clean_entry_size + sizeof(clean_disk_entry) + offset);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
||||
while (dirty_it->first.oid == read_op->oid)
|
||||
{
|
||||
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 (!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;
|
||||
if (read_op->bitmap)
|
||||
{
|
||||
void *bmp_ptr = (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||
memcpy(read_op->bitmap, bmp_ptr, clean_entry_bitmap_size);
|
||||
void *bmp_ptr = (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||
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,
|
||||
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
|
||||
PRIV(read_op)->read_vec.clear();
|
||||
@@ -171,22 +174,23 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
||||
dirty_it--;
|
||||
}
|
||||
}
|
||||
if (clean_it != clean_db.end())
|
||||
if (clean_found)
|
||||
{
|
||||
if (!result_version)
|
||||
{
|
||||
result_version = clean_it->second.version;
|
||||
if (read_op->bitmap)
|
||||
{
|
||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
||||
memcpy(read_op->bitmap, bmp_ptr, 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, dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
}
|
||||
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
|
||||
PRIV(read_op)->read_vec.clear();
|
||||
@@ -196,7 +200,7 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
||||
else
|
||||
{
|
||||
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 (!(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)
|
||||
{
|
||||
// fill with zeroes
|
||||
assert(fulfill_read(read_op, fulfilled, bmp_start * bitmap_granularity,
|
||||
bmp_end * bitmap_granularity, (BS_ST_DELETE | BS_ST_STABLE), 0, 0));
|
||||
assert(fulfill_read(read_op, fulfilled, bmp_start * dsk.bitmap_granularity,
|
||||
bmp_end * dsk.bitmap_granularity, (BS_ST_DELETE | BS_ST_STABLE), 0, 0, 0));
|
||||
}
|
||||
bmp_start = bmp_end;
|
||||
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 (!fulfill_read(read_op, fulfilled, bmp_start * bitmap_granularity,
|
||||
bmp_end * bitmap_granularity, (BS_ST_BIG_WRITE | BS_ST_STABLE), 0,
|
||||
clean_it->second.location + bmp_start * bitmap_granularity))
|
||||
if (!fulfill_read(read_op, fulfilled, bmp_start * dsk.bitmap_granularity,
|
||||
bmp_end * dsk.bitmap_granularity, (BS_ST_BIG_WRITE | BS_ST_STABLE), 0,
|
||||
clean_it->second.location + bmp_start * dsk.bitmap_granularity, 0))
|
||||
{
|
||||
// need to wait. undo added requests, don't dequeue op
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
read_op->version = result_version;
|
||||
@@ -249,6 +253,15 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *read_op)
|
||||
FINISH_OP(read_op);
|
||||
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;
|
||||
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 (!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)
|
||||
op->retval = op->len;
|
||||
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;
|
||||
if (bitmap)
|
||||
{
|
||||
void *bmp_ptr = (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||
memcpy(bitmap, bmp_ptr, clean_entry_bitmap_size);
|
||||
void *bmp_ptr = (dsk.clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap);
|
||||
memcpy(bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
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;
|
||||
if (bitmap)
|
||||
{
|
||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
||||
memcpy(bitmap, bmp_ptr, clean_entry_bitmap_size);
|
||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, dsk.clean_entry_bitmap_size);
|
||||
memcpy(bitmap, bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (result_version)
|
||||
*result_version = 0;
|
||||
if (bitmap)
|
||||
memset(bitmap, 0, clean_entry_bitmap_size);
|
||||
memset(bitmap, 0, dsk.clean_entry_bitmap_size);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
@@ -112,7 +112,7 @@ resume_2:
|
||||
if (!disable_journal_fsync)
|
||||
{
|
||||
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->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;
|
||||
@@ -127,7 +127,6 @@ resume_4:
|
||||
{
|
||||
mark_rolled_back(*v);
|
||||
}
|
||||
flusher->mark_trim_possible();
|
||||
// Acknowledge op
|
||||
op->retval = 0;
|
||||
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)
|
||||
{
|
||||
#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);
|
||||
#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
|
||||
printf(
|
||||
"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)
|
||||
{
|
||||
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);
|
||||
dirty_it->second.bitmap = NULL;
|
||||
|
@@ -137,7 +137,7 @@ resume_2:
|
||||
if (!disable_journal_fsync)
|
||||
{
|
||||
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->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;
|
||||
@@ -195,12 +195,16 @@ void blockstore_impl_t::mark_stable(const obj_ver_id & v, bool forget_dirty)
|
||||
}
|
||||
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))
|
||||
{
|
||||
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) ||
|
||||
|
@@ -60,7 +60,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
||||
if (!disable_data_fsync)
|
||||
{
|
||||
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->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;
|
||||
@@ -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
|
||||
blockstore_journal_check_t space_check(this);
|
||||
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;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
||||
int s = 0;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
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;
|
||||
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->len = dirty_entry.len;
|
||||
je->location = dirty_entry.location;
|
||||
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*)
|
||||
? dirty_entry.bitmap : &dirty_entry.bitmap), clean_entry_bitmap_size);
|
||||
memcpy((void*)(je+1), (dsk.clean_entry_bitmap_size > sizeof(void*)
|
||||
? dirty_entry.bitmap : &dirty_entry.bitmap), dsk.clean_entry_bitmap_size);
|
||||
je->crc32 = je_crc32((journal_entry*)je);
|
||||
journal.crc32_last = je->crc32;
|
||||
it++;
|
||||
@@ -132,7 +132,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
||||
if (!disable_journal_fsync)
|
||||
{
|
||||
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->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;
|
||||
|
@@ -10,9 +10,9 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
||||
bool wait_big = false, wait_del = false;
|
||||
void *bmp = NULL;
|
||||
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)
|
||||
{
|
||||
@@ -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);
|
||||
if (!is_del && !deleted)
|
||||
{
|
||||
if (clean_entry_bitmap_size > sizeof(void*))
|
||||
memcpy(bmp, dirty_it->second.bitmap, clean_entry_bitmap_size);
|
||||
if (dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||
memcpy(bmp, dirty_it->second.bitmap, dsk.clean_entry_bitmap_size);
|
||||
else
|
||||
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;
|
||||
if (!is_del)
|
||||
{
|
||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, clean_entry_bitmap_size);
|
||||
memcpy((clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp), bmp_ptr, clean_entry_bitmap_size);
|
||||
void *bmp_ptr = get_clean_entry_bitmap(clean_it->second.location, dsk.clean_entry_bitmap_size);
|
||||
memcpy((dsk.clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp), bmp_ptr, dsk.clean_entry_bitmap_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -89,15 +89,18 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
if (!is_del && clean_entry_bitmap_size > sizeof(void*))
|
||||
if (!is_del && dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||
{
|
||||
free(bmp);
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
printf("Write %lx:%lx v%lu offset=%u len=%u\n", op->oid.inode, op->oid.stripe, op->version, op->offset, op->len);
|
||||
#endif
|
||||
// FIXME No strict need to add it into dirty_db here, it's just left
|
||||
// from the previous implementation where reads waited for writes
|
||||
// No strict need to add it into dirty_db here except maybe for listings to return
|
||||
// correct data when there are inflight operations in the queue
|
||||
uint32_t state;
|
||||
if (is_del)
|
||||
state = BS_ST_DELETE | BS_ST_IN_FLIGHT;
|
||||
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)
|
||||
clock_gettime(CLOCK_REALTIME, &PRIV(op)->tv_begin);
|
||||
if (wait_del)
|
||||
@@ -136,10 +139,10 @@ bool blockstore_impl_t::enqueue_write(blockstore_op_t *op)
|
||||
if (op->bitmap)
|
||||
{
|
||||
// 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);
|
||||
uint32_t bit = op->offset/bitmap_granularity;
|
||||
uint32_t bits_left = op->len/bitmap_granularity;
|
||||
while (!(bit % 8) && bits_left > 8)
|
||||
uint8_t *bmp_ptr = (uint8_t*)(dsk.clean_entry_bitmap_size > sizeof(void*) ? bmp : &bmp);
|
||||
uint32_t bit = op->offset/dsk.bitmap_granularity;
|
||||
uint32_t bits_left = op->len/dsk.bitmap_granularity;
|
||||
while (!(bit % 8) && bits_left >= 8)
|
||||
{
|
||||
// Copy bytes
|
||||
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)
|
||||
{
|
||||
if (clean_entry_bitmap_size > sizeof(void*))
|
||||
if (dsk.clean_entry_bitmap_size > sizeof(void*))
|
||||
free(dirty_it->second.bitmap);
|
||||
dirty_db.erase(dirty_it++);
|
||||
}
|
||||
bool found = false;
|
||||
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;
|
||||
}
|
||||
else if (found && other_op->oid == op->oid &&
|
||||
(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);
|
||||
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;
|
||||
}
|
||||
@@ -260,18 +271,28 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
if (loc == UINT64_MAX)
|
||||
{
|
||||
// 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);
|
||||
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);
|
||||
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;
|
||||
#ifdef BLOCKSTORE_DEBUG
|
||||
printf(
|
||||
@@ -280,9 +301,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
);
|
||||
#endif
|
||||
data_alloc->set(loc, true);
|
||||
uint64_t stripe_offset = (op->offset % bitmap_granularity);
|
||||
uint64_t stripe_end = (op->offset + op->len) % bitmap_granularity;
|
||||
// Zero fill up to bitmap_granularity
|
||||
uint64_t stripe_offset = (op->offset % dsk.bitmap_granularity);
|
||||
uint64_t stripe_end = (op->offset + op->len) % dsk.bitmap_granularity;
|
||||
// Zero fill up to dsk.bitmap_granularity
|
||||
int vcnt = 0;
|
||||
if (stripe_offset)
|
||||
{
|
||||
@@ -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 };
|
||||
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 };
|
||||
}
|
||||
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); };
|
||||
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)->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);
|
||||
if (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,
|
||||
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;
|
||||
}
|
||||
@@ -329,7 +351,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
BS_SUBMIT_CHECK_SQES(
|
||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||
(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)
|
||||
);
|
||||
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); };
|
||||
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);
|
||||
}
|
||||
@@ -349,7 +371,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
// Then pre-fill 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,
|
||||
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;
|
||||
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
|
||||
// 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->version = op->version;
|
||||
je->offset = op->offset;
|
||||
je->len = op->len;
|
||||
je->data_offset = journal.next_free;
|
||||
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);
|
||||
journal.crc32_last = je->crc32;
|
||||
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->callback = cb;
|
||||
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++;
|
||||
}
|
||||
@@ -400,7 +422,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
journal.next_free += op->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)
|
||||
{
|
||||
@@ -432,15 +454,22 @@ int blockstore_impl_t::continue_write(blockstore_op_t *op)
|
||||
resume_2:
|
||||
// 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){
|
||||
.oid = op->oid,
|
||||
.version = op->version,
|
||||
});
|
||||
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, 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;
|
||||
journal.used_sectors[journal.sector_info[journal.cur_sector].offset]++;
|
||||
@@ -456,7 +485,7 @@ resume_2:
|
||||
je->offset = op->offset;
|
||||
je->len = op->len;
|
||||
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);
|
||||
journal.crc32_last = je->crc32;
|
||||
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)
|
||||
{
|
||||
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
||||
throw std::runtime_error(
|
||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
||||
);
|
||||
disk_error_abort("data write", data->res, data->iov.iov_len);
|
||||
}
|
||||
PRIV(op)->pending_ops--;
|
||||
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());
|
||||
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;
|
||||
}
|
||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||
BS_SUBMIT_CHECK_SQES(
|
||||
(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
|
||||
);
|
||||
if (write_iodepth >= max_write_iodepth)
|
||||
@@ -645,7 +671,7 @@ int blockstore_impl_t::dequeue_del(blockstore_op_t *op)
|
||||
// Prepare journal sector write
|
||||
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)
|
||||
{
|
||||
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 "cluster_client.h"
|
||||
#include "pg_states.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
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[])
|
||||
{
|
||||
@@ -25,42 +101,47 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
||||
cfg["progress"] = "1";
|
||||
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";
|
||||
}
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
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";
|
||||
}
|
||||
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";
|
||||
}
|
||||
else if (args[i][0] == '-' && args[i][1] == '-')
|
||||
{
|
||||
const char *opt = args[i]+2;
|
||||
cfg[opt] = i == narg-1 || !strcmp(opt, "json") || !strcmp(opt, "wait-list") ||
|
||||
!strcmp(opt, "long") || !strcmp(opt, "del") || !strcmp(opt, "no-color") ||
|
||||
cfg[opt] = i == narg-1 || !strcmp(opt, "json") ||
|
||||
!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, "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];
|
||||
}
|
||||
else
|
||||
@@ -76,98 +157,17 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
||||
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;
|
||||
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)
|
||||
{
|
||||
cli_result_t result;
|
||||
cli_result_t result = {};
|
||||
p->parse_config(cfg);
|
||||
json11::Json::array cmd = cfg["command"].array_items();
|
||||
cfg.erase("command");
|
||||
@@ -235,6 +235,16 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
||||
// Delete inode data
|
||||
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")
|
||||
{
|
||||
// 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
|
||||
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
|
||||
{
|
||||
result = { .err = EINVAL, .text = "unknown command: "+cmd[0].string_value() };
|
||||
|
@@ -39,12 +39,13 @@ public:
|
||||
ring_loop_t *ringloop = NULL;
|
||||
epoll_manager_t *epmgr = NULL;
|
||||
cluster_client_t *cli = NULL;
|
||||
bool no_recovery = false, no_rebalance = false, readonly = false;
|
||||
|
||||
int waiting = 0;
|
||||
cli_result_t etcd_err;
|
||||
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);
|
||||
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_flatten(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 &)> simple_offsets(json11::Json cfg);
|
||||
|
||||
// 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);
|
||||
@@ -72,12 +73,8 @@ public:
|
||||
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 format_size(uint64_t size, bool nobytes = false);
|
||||
|
||||
std::string format_lat(uint64_t lat);
|
||||
|
||||
std::string format_q(double depth);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <ctype.h>
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
#include "cluster_client.h"
|
||||
#include "cli.h"
|
||||
|
||||
@@ -100,9 +100,20 @@ inode_config_t* cli_tool_t::get_inode_cfg(const std::string & name)
|
||||
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();
|
||||
iodepth = cfg["iodepth"].uint64_value();
|
||||
if (!iodepth)
|
||||
@@ -112,7 +123,7 @@ void cli_tool_t::parse_config(json11::Json cfg)
|
||||
parallel_osds = 4;
|
||||
log_level = cfg["log_level"].int64_value();
|
||||
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
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <ctype.h>
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
// Create an image, snapshot or clone
|
||||
//
|
||||
@@ -276,7 +276,8 @@ resume_4:
|
||||
new_id = 1+INODE_NO_POOL(kv.value.uint64_value());
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
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() != "")
|
||||
{
|
||||
@@ -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();
|
||||
if (cfg["size"].string_value() != "")
|
||||
{
|
||||
image_creator->size = parse_size(cfg["size"].string_value());
|
||||
if (image_creator->size == UINT64_MAX)
|
||||
bool ok;
|
||||
image_creator->size = parse_size(cfg["size"].string_value(), &ok);
|
||||
if (!ok)
|
||||
{
|
||||
return [size = cfg["size"].string_value()](cli_result_t & result)
|
||||
{
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
// List pools with space statistics
|
||||
struct pool_lister_t
|
||||
@@ -127,7 +127,7 @@ resume_1:
|
||||
pool_stats[pool_cfg.id] = json11::Json::object {
|
||||
{ "name", pool_cfg.name },
|
||||
{ "pg_count", pool_cfg.pg_count },
|
||||
{ "scheme", pool_cfg.scheme == POOL_SCHEME_REPLICATED ? "replicated" : "jerasure" },
|
||||
{ "scheme", pool_cfg.scheme == POOL_SCHEME_REPLICATED ? "replicated" : "ec" },
|
||||
{ "scheme_name", pool_cfg.scheme == POOL_SCHEME_REPLICATED
|
||||
? std::to_string(pool_cfg.pg_size)+"/"+std::to_string(pool_cfg.pg_minsize)
|
||||
: "EC "+std::to_string(pool_cfg.pg_size-pool_cfg.parity_chunks)+"+"+std::to_string(pool_cfg.parity_chunks) },
|
||||
|
@@ -133,7 +133,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_flatten(json11::Json cfg)
|
||||
auto flattener = new snap_flattener_t();
|
||||
flattener->parent = this;
|
||||
flattener->target_name = cfg["image"].string_value();
|
||||
flattener->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||
flattener->fsync_interval = cfg["fsync_interval"].uint64_value();
|
||||
if (!flattener->fsync_interval)
|
||||
flattener->fsync_interval = 128;
|
||||
if (!cfg["cas"].is_null())
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <algorithm>
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
// List existing images
|
||||
//
|
||||
@@ -446,33 +446,6 @@ std::string print_table(json11::Json items, json11::Json header, bool use_esc)
|
||||
return str;
|
||||
}
|
||||
|
||||
static uint64_t size_thresh[] = { (uint64_t)1024*1024*1024*1024, (uint64_t)1024*1024*1024, (uint64_t)1024*1024, 1024, 0 };
|
||||
static uint64_t size_thresh_d[] = { (uint64_t)1000000000000, (uint64_t)1000000000, (uint64_t)1000000, (uint64_t)1000, 0 };
|
||||
static const int size_thresh_n = sizeof(size_thresh)/sizeof(size_thresh[0]);
|
||||
static const char *size_unit = "TGMKB";
|
||||
|
||||
std::string format_size(uint64_t size, bool nobytes)
|
||||
{
|
||||
uint64_t *thr = nobytes ? size_thresh_d : size_thresh;
|
||||
char buf[256];
|
||||
for (int i = 0; i < size_thresh_n; i++)
|
||||
{
|
||||
if (size >= thr[i] || i >= size_thresh_n-1)
|
||||
{
|
||||
double value = thr[i] ? (double)size/thr[i] : size;
|
||||
int l = snprintf(buf, sizeof(buf), "%.1f", value);
|
||||
assert(l < sizeof(buf)-2);
|
||||
if (buf[l-1] == '0')
|
||||
l -= 2;
|
||||
buf[l] = i == size_thresh_n-1 && nobytes ? 0 : ' ';
|
||||
buf[l+1] = i == size_thresh_n-1 && nobytes ? 0 : size_unit[i];
|
||||
buf[l+2] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string format_lat(uint64_t lat)
|
||||
{
|
||||
char buf[256];
|
||||
|
@@ -47,6 +47,7 @@ struct snap_merger_t
|
||||
int state = 0;
|
||||
int lists_todo = 0;
|
||||
uint64_t target_block_size = 0;
|
||||
uint32_t target_bitmap_granularity = 0;
|
||||
btree::safe_btree_set<uint64_t> merge_offsets;
|
||||
btree::safe_btree_set<uint64_t>::iterator oit;
|
||||
std::map<inode_t, std::vector<uint64_t>> layer_lists;
|
||||
@@ -101,7 +102,7 @@ struct snap_merger_t
|
||||
std::vector<inode_t> chain_list;
|
||||
inode_config_t *cur = to_cfg;
|
||||
chain_list.push_back(cur->num);
|
||||
layer_block_size[cur->num] = get_block_size(cur->num);
|
||||
layer_block_size[cur->num] = get_block_size(cur->num, NULL);
|
||||
while (cur->parent_id != from_cfg->num &&
|
||||
cur->parent_id != to_cfg->num &&
|
||||
cur->parent_id != 0)
|
||||
@@ -124,7 +125,7 @@ struct snap_merger_t
|
||||
}
|
||||
cur = &it->second;
|
||||
chain_list.push_back(cur->num);
|
||||
layer_block_size[cur->num] = get_block_size(cur->num);
|
||||
layer_block_size[cur->num] = get_block_size(cur->num, NULL);
|
||||
}
|
||||
if (cur->parent_id != from_cfg->num)
|
||||
{
|
||||
@@ -133,7 +134,7 @@ struct snap_merger_t
|
||||
return;
|
||||
}
|
||||
chain_list.push_back(from_cfg->num);
|
||||
layer_block_size[from_cfg->num] = get_block_size(from_cfg->num);
|
||||
layer_block_size[from_cfg->num] = get_block_size(from_cfg->num, NULL);
|
||||
int i = chain_list.size()-1;
|
||||
for (inode_t item: chain_list)
|
||||
{
|
||||
@@ -204,14 +205,16 @@ struct snap_merger_t
|
||||
use_cas ? " online (with CAS)" : "", INODE_NO_POOL(target), INODE_POOL(target)
|
||||
);
|
||||
}
|
||||
target_block_size = get_block_size(target);
|
||||
target_block_size = get_block_size(target, &target_bitmap_granularity);
|
||||
}
|
||||
|
||||
uint64_t get_block_size(inode_t inode)
|
||||
uint64_t get_block_size(inode_t inode, uint32_t *bitmap_granularity)
|
||||
{
|
||||
auto & pool_cfg = parent->cli->st_cli.pool_config.at(INODE_POOL(inode));
|
||||
uint64_t pg_data_size = (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
|
||||
return parent->cli->get_bs_block_size() * pg_data_size;
|
||||
if (bitmap_granularity)
|
||||
*bitmap_granularity = pool_cfg.bitmap_granularity;
|
||||
return pool_cfg.data_block_size * pg_data_size;
|
||||
}
|
||||
|
||||
void continue_merge_reent()
|
||||
@@ -409,7 +412,7 @@ struct snap_merger_t
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t bitmap_bytes = target_block_size/parent->cli->get_bs_bitmap_granularity()/8;
|
||||
uint64_t bitmap_bytes = target_block_size/target_bitmap_granularity/8;
|
||||
int i;
|
||||
for (i = 0; i < bitmap_bytes; i++)
|
||||
{
|
||||
@@ -469,7 +472,7 @@ struct snap_merger_t
|
||||
{
|
||||
// Write each non-empty range using an individual operation
|
||||
// FIXME: Allow to use single write with "holes" (OSDs don't allow it yet)
|
||||
uint32_t gran = parent->cli->get_bs_bitmap_granularity();
|
||||
uint32_t gran = target_bitmap_granularity;
|
||||
uint64_t bitmap_size = target_block_size / gran;
|
||||
while (rwo->end < bitmap_size && !rwo->error_code)
|
||||
{
|
||||
@@ -628,8 +631,8 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_merge(json11::Json cfg)
|
||||
merger->from_name = cfg["from"].string_value();
|
||||
merger->to_name = cfg["to"].string_value();
|
||||
merger->target_name = cfg["target"].string_value();
|
||||
merger->delete_source = cfg["delete-source"].string_value() != "";
|
||||
merger->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||
merger->delete_source = cfg["delete_source"].string_value() != "";
|
||||
merger->fsync_interval = cfg["fsync_interval"].uint64_value();
|
||||
if (!merger->fsync_interval)
|
||||
merger->fsync_interval = 128;
|
||||
if (!cfg["cas"].is_null())
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
// Rename, resize image (and purge extra data on shrink) or change its readonly status
|
||||
struct image_changer_t
|
||||
@@ -236,7 +236,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
|
||||
changer->force = cfg["force"].bool_value();
|
||||
changer->set_readonly = cfg["readonly"].bool_value();
|
||||
changer->set_readwrite = cfg["readwrite"].bool_value();
|
||||
changer->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||
changer->fsync_interval = cfg["fsync_interval"].uint64_value();
|
||||
if (!changer->fsync_interval)
|
||||
changer->fsync_interval = 128;
|
||||
// FIXME Check that the image doesn't have children when shrinking
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <fcntl.h>
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
#include "base64.h"
|
||||
#include "str_util.h"
|
||||
|
||||
// Remove layer(s): similar to merge, but alters metadata and processes multiple merge targets
|
||||
//
|
||||
@@ -639,7 +639,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_rm(json11::Json cfg)
|
||||
snap_remover->parent = this;
|
||||
snap_remover->from_name = cfg["from"].string_value();
|
||||
snap_remover->to_name = cfg["to"].string_value();
|
||||
snap_remover->fsync_interval = cfg["fsync-interval"].uint64_value();
|
||||
snap_remover->fsync_interval = cfg["fsync_interval"].uint64_value();
|
||||
if (!snap_remover->fsync_interval)
|
||||
snap_remover->fsync_interval = 128;
|
||||
if (!cfg["cas"].is_null())
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user