Compare commits

..

24 Commits

Author SHA1 Message Date
Vitaliy Filippov d29b5d2d04 Add Russian translation of VNPL-1.1 2022-06-24 01:34:25 +03:00
Vitaliy Filippov 65b0e8e940 Fix typo in VNPL-1.1 2022-06-24 01:34:25 +03:00
Vitaliy Filippov bce357e2a5 Do not read all metadata into memory when dumping 2022-06-13 01:26:30 +03:00
Vitaliy Filippov 0876ca09cd Fix dumper includes and print format 2022-06-11 00:30:44 +03:00
Vitaliy Filippov dac12d8a4c Implement metadata dump tool 2022-06-10 18:50:09 +03:00
Vitaliy Filippov 1eec4407ab Fix inode creation when /index/maxid is out of sync 2022-06-06 16:35:51 +03:00
huy 3b7c6dcac2 Fix volume creation from snapshots in Cinder driver 2022-06-06 15:46:13 +03:00
Vitaliy Filippov 342517d126 Fix typo 2022-06-05 00:45:02 +03:00
Vitaliy Filippov 675bc12a13 Add extern "C" for systems like Gentoo which miss it in jerasure includes 2022-06-05 00:33:38 +03:00
Vitaliy Filippov 101592bbff Release 0.7.1
- Add ISA-L erasure code implementation, now used automatically instead of jerasure when available
- Fix listings sending too many parallel requests to OSDs
- Fix rm-data crashing with --wait-list
- Remove empty inodes from statistics and `ls` output, after <inode_vanish_time> seconds after deletion
- Make monitor delete pool statistics when the pool is deleted and thus remove them from `df` output
- Log multiple etcd addresses in OSD logs correctly
- Fix true/false parsing in json configs like no_recovery/no_rebalance
- Show no_recovery, no_rebalance, readonly flags in status
2022-06-05 00:07:24 +03:00
Vitaliy Filippov be4087d9d2 Add a FIXME to test_interrupted_rebalance 2022-06-05 00:06:56 +03:00
Vitaliy Filippov 404e43dd2d Note that ISA-L does not need to be enabled separately 2022-06-04 22:58:02 +03:00
Vitaliy Filippov 87613ed590 Add ISA-L into RPM specs 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 2a2e914ef9 Show no_recovery, no_rebalance and readonly flags in status 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 0cdc9292c8 Fix true/false parsing in json configs like no_recovery/no_rebalance 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 3e1b03bb5c Show all etcd addresses in the "reporting to..." message 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 36e851505a Make monitor delete pool statistics when the pool is deleted 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 1efbbb0c36 Make deleted inodes vanish from statistics after 60 seconds 2022-06-04 13:27:06 +03:00
Vitaliy Filippov 088dd15449 Exclude empty inodes from stats 2022-06-04 00:18:17 +03:00
Vitaliy Filippov 4a531d7b8b Fix listings sending too many parallel requests to OSDs, fix rm-data crashing with --wait-list 2022-06-03 23:36:37 +03:00
Vitaliy Filippov a0cae4c180 Rename "jerasure" to "ec" in pool configuration, function names, fix documentation and Debian build scripts
Old pool configurations with "jerasure" also remain supported as an alias for "ec"
2022-06-03 15:40:00 +03:00
Vitaliy Filippov c4eb46600d Merge run_3osds and run_7osds scripts 2022-06-03 01:56:36 +03:00
Vitaliy Filippov 21b306e25f Add ISA-L support 2022-06-02 01:47:33 +03:00
Vitaliy Filippov d8313e939a Release 0.7.0
- Add documentation! :-) in Russian and English
- Implement an NFS proxy for file-based access emulation to Vitastor
  images for non-QEMU based hypervisors like VMWare, as a better way
  than iSCSI
- Implement "primary affinity tags"
- Add a patch for libvirt 6.0
- Fix free_down_raw in cli status
- Fix a rare bug where OSDs could drop unrelated connections on errors
2022-05-29 23:39:53 +03:00
86 changed files with 1558 additions and 870 deletions

View File

@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 2.8)
project(vitastor) project(vitastor)
set(VERSION "0.6.17") set(VERSION "0.7.1")
add_subdirectory(src) add_subdirectory(src)

View File

@ -96,5 +96,5 @@ Vitastor Network Public License 1.1, основанная на GNU GPL 3.0 с д
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
совместимости с таким ПО, как QEMU и fio. совместимости с таким ПО, как QEMU и fio.
Вы можете найти полный текст VNPL 1.1 в файле [VNPL-1.1.txt](VNPL-1.1.txt), Вы можете найти полный текст VNPL 1.1 на английском языке в файле [VNPL-1.1.txt](VNPL-1.1.txt),
а GPL 2.0 в файле [GPL-2.0.txt](GPL-2.0.txt). VNPL 1.1 на русском языке в файле [VNPL-1.1-RU.txt](VNPL-1.1-RU.txt), а GPL 2.0 в файле [GPL-2.0.txt](GPL-2.0.txt).

680
VNPL-1.1-RU.txt Normal file
View 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 для того, чтобы узнать конкретные требования.

View File

@ -61,7 +61,7 @@ modification follow.
0. Definitions. 0. Definitions.
"This License" refers to version 1 of the Vitastor Network Public License. "This License" refers to version 1.1 of the Vitastor Network Public License.
"Copyright" also means copyright-like laws that apply to other kinds of "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks. works, such as semiconductor masks.
@ -629,7 +629,7 @@ the "copyright" line and a pointer to where the full notice is found.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the Vitastor Network Public License as published by it under the terms of the Vitastor Network Public License as published by
the Vitastor Author, either version 1 of the License, or the Vitastor Author, either version 1.1 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,

View File

@ -1,4 +1,4 @@
VERSION ?= v0.6.17 VERSION ?= v0.7.1
all: build push all: build push

View File

@ -49,7 +49,7 @@ spec:
capabilities: capabilities:
add: ["SYS_ADMIN"] add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v0.6.17 image: vitalif/vitastor-csi:v0.7.1
args: args:
- "--node=$(NODE_ID)" - "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)" - "--endpoint=$(CSI_ENDPOINT)"

View File

@ -116,7 +116,7 @@ spec:
privileged: true privileged: true
capabilities: capabilities:
add: ["SYS_ADMIN"] add: ["SYS_ADMIN"]
image: vitalif/vitastor-csi:v0.6.17 image: vitalif/vitastor-csi:v0.7.1
args: args:
- "--node=$(NODE_ID)" - "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)" - "--endpoint=$(CSI_ENDPOINT)"

View File

@ -5,7 +5,7 @@ package vitastor
const ( const (
vitastorCSIDriverName = "csi.vitastor.io" vitastorCSIDriverName = "csi.vitastor.io"
vitastorCSIDriverVersion = "0.6.17" vitastorCSIDriverVersion = "0.7.1"
) )
// Config struct fills the parameters of request or user input // Config struct fills the parameters of request or user input

16
debian/changelog vendored
View File

@ -1,4 +1,18 @@
vitastor (0.6.17-1) unstable; urgency=medium vitastor (0.7.1-1) unstable; urgency=medium
* Bugfixes
-- Vitaliy Filippov <vitalif@yourcmc.ru> Fri, 03 Jun 2022 02:09:44 +0300
vitastor (0.7.1-1) unstable; urgency=medium
* Implement NFS proxy
* Add documentation
* Bugfixes
-- Vitaliy Filippov <vitalif@yourcmc.ru> Sun, 29 May 2022 23:39:13 +0300
vitastor (0.6.3-1) unstable; urgency=medium
* RDMA support * RDMA support
* Bugfixes * Bugfixes

2
debian/control vendored
View File

@ -2,7 +2,7 @@ Source: vitastor
Section: admin Section: admin
Priority: optional Priority: optional
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru> Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev, libisal-dev
Standards-Version: 4.5.0 Standards-Version: 4.5.0
Homepage: https://vitastor.io/ Homepage: https://vitastor.io/
Rules-Requires-Root: no Rules-Requires-Root: no

11
debian/libisal.pc vendored Normal file
View 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}

View File

@ -1,2 +1,3 @@
usr/bin/vitastor-osd usr/bin/vitastor-osd
usr/bin/vitastor-dump-journal usr/bin/vitastor-dump-journal
usr/bin/vitastor-dump-meta

View File

@ -22,10 +22,11 @@ RUN apt-get update
RUN apt-get -y install fio liburing1 liburing-dev libgoogle-perftools-dev devscripts RUN apt-get -y install fio liburing1 liburing-dev libgoogle-perftools-dev devscripts
RUN apt-get -y build-dep fio RUN apt-get -y build-dep fio
RUN apt-get --download-only source fio RUN apt-get --download-only source fio
RUN apt-get update && apt-get -y install libjerasure-dev cmake libibverbs-dev RUN apt-get update && apt-get -y install libjerasure-dev cmake libibverbs-dev libisal-dev
ADD . /root/vitastor ADD . /root/vitastor
RUN set -e -x; \ RUN set -e -x; \
[ -e /usr/lib/x86_64-linux-gnu/pkgconfig/libisal.pc ] || cp /root/vitastor/debian/libisal.pc /usr/lib/x86_64-linux-gnu/pkgconfig; \
mkdir -p /root/fio-build/; \ mkdir -p /root/fio-build/; \
cd /root/fio-build/; \ cd /root/fio-build/; \
rm -rf /root/fio-build/*; \ rm -rf /root/fio-build/*; \
@ -33,8 +34,8 @@ RUN set -e -x; \
mkdir -p /root/packages/vitastor-$REL; \ mkdir -p /root/packages/vitastor-$REL; \
rm -rf /root/packages/vitastor-$REL/*; \ rm -rf /root/packages/vitastor-$REL/*; \
cd /root/packages/vitastor-$REL; \ cd /root/packages/vitastor-$REL; \
cp -r /root/vitastor vitastor-0.6.17; \ cp -r /root/vitastor vitastor-0.7.1; \
cd vitastor-0.6.17; \ cd vitastor-0.7.1; \
ln -s /root/fio-build/fio-*/ ./fio; \ ln -s /root/fio-build/fio-*/ ./fio; \
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \ FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \ ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
@ -47,8 +48,8 @@ RUN set -e -x; \
rm -rf a b; \ rm -rf a b; \
echo "dep:fio=$FIO" > debian/fio_version; \ echo "dep:fio=$FIO" > debian/fio_version; \
cd /root/packages/vitastor-$REL; \ cd /root/packages/vitastor-$REL; \
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.6.17.orig.tar.xz vitastor-0.6.17; \ tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.7.1.orig.tar.xz vitastor-0.7.1; \
cd vitastor-0.6.17; \ cd vitastor-0.7.1; \
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \ V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \ DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \ DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \

View File

@ -23,6 +23,7 @@ initialization and can be changed with an OSD restart.
- [no_rebalance](#no_rebalance) - [no_rebalance](#no_rebalance)
- [print_stats_interval](#print_stats_interval) - [print_stats_interval](#print_stats_interval)
- [slow_log_interval](#slow_log_interval) - [slow_log_interval](#slow_log_interval)
- [inode_vanish_time](#inode_vanish_time)
- [max_write_iodepth](#max_write_iodepth) - [max_write_iodepth](#max_write_iodepth)
- [min_flusher_count](#min_flusher_count) - [min_flusher_count](#min_flusher_count)
- [max_flusher_count](#max_flusher_count) - [max_flusher_count](#max_flusher_count)
@ -163,6 +164,13 @@ Time interval at which OSDs dump slow or stuck operations on stdout, if
they're any. Also it's the time after which an operation is considered they're any. Also it's the time after which an operation is considered
"slow". "slow".
## inode_vanish_time
- Type: seconds
- Default: 60
Number of seconds after which a deleted inode is removed from OSD statistics.
## max_write_iodepth ## max_write_iodepth
- Type: integer - Type: integer

View File

@ -24,6 +24,7 @@
- [no_rebalance](#no_rebalance) - [no_rebalance](#no_rebalance)
- [print_stats_interval](#print_stats_interval) - [print_stats_interval](#print_stats_interval)
- [slow_log_interval](#slow_log_interval) - [slow_log_interval](#slow_log_interval)
- [inode_vanish_time](#inode_vanish_time)
- [max_write_iodepth](#max_write_iodepth) - [max_write_iodepth](#max_write_iodepth)
- [min_flusher_count](#min_flusher_count) - [min_flusher_count](#min_flusher_count)
- [max_flusher_count](#max_flusher_count) - [max_flusher_count](#max_flusher_count)
@ -169,6 +170,13 @@ OSD.
медленных или зависших операций, если таковые имеются. Также время, при медленных или зависших операций, если таковые имеются. Также время, при
превышении которого операция считается "медленной". превышении которого операция считается "медленной".
## inode_vanish_time
- Тип: секунды
- Значение по умолчанию: 60
Число секунд, через которое удалённые инод удаляется и из статистики OSD.
## max_write_iodepth ## max_write_iodepth
- Тип: целое число - Тип: целое число

View File

@ -106,9 +106,12 @@ Pool name.
- Type: string - Type: string
- Required - Required
- One of: "replicated", "xor" or "jerasure" - One of: "replicated", "xor", "ec" or "jerasure"
Redundancy scheme used for data in this pool. Redundancy scheme used for data in this pool. "jerasure" is an alias for "ec",
both use Reed-Solomon-Vandermonde codes based on ISA-L or jerasure libraries.
Fast ISA-L based implementation is used automatically when it's available,
slower jerasure version is used otherwise.
## pg_size ## pg_size
@ -243,7 +246,7 @@ of the OSDs containing a data chunk for a PG.
{ {
"2": { "2": {
"name":"ecpool", "name":"ecpool",
"scheme":"jerasure", "scheme":"ec",
"pg_size":3, "pg_size":3,
"parity_chunks":1, "parity_chunks":1,
"pg_minsize":2, "pg_minsize":2,

View File

@ -106,9 +106,13 @@
- Тип: строка - Тип: строка
- Обязательный - Обязательный
- Возможные значения: "replicated", "xor" или "jerasure" - Возможные значения: "replicated", "xor", "ec" или "jerasure"
Схема избыточности, используемая в данном пуле. Схема избыточности, используемая в данном пуле. "jerasure" - синоним для "ec",
в обеих схемах используются коды Рида-Соломона-Вандермонда, реализованные на
основе библиотек ISA-L или jerasure. Быстрая реализация на основе ISA-L
используется автоматически, когда доступна, в противном случае используется
более медленная jerasure-версия.
## pg_size ## pg_size
@ -242,7 +246,7 @@ PG в Vitastor эферемерны, то есть вы можете менят
{ {
"2": { "2": {
"name":"ecpool", "name":"ecpool",
"scheme":"jerasure", "scheme":"ec",
"pg_size":3, "pg_size":3,
"parity_chunks":1, "parity_chunks":1,
"pg_minsize":2, "pg_minsize":2,

View File

@ -158,6 +158,13 @@
Временной интервал, с которым OSD выводят в стандартный вывод список Временной интервал, с которым OSD выводят в стандартный вывод список
медленных или зависших операций, если таковые имеются. Также время, при медленных или зависших операций, если таковые имеются. Также время, при
превышении которого операция считается "медленной". превышении которого операция считается "медленной".
- name: inode_vanish_time
type: sec
default: 60
info: |
Number of seconds after which a deleted inode is removed from OSD statistics.
info_ru: |
Число секунд, через которое удалённые инод удаляется и из статистики OSD.
- name: max_write_iodepth - name: max_write_iodepth
type: int type: int
default: 128 default: 128

View File

@ -15,7 +15,8 @@
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus - gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
designated initializers support from C++20 designated initializers support from C++20
- CMake - CMake
- liburing, jerasure headers - liburing, jerasure headers and libraries
- ISA-L, libibverbs headers and libraries (optional)
- tcmalloc (google-perftools-dev) - tcmalloc (google-perftools-dev)
## Basic instructions ## Basic instructions

View File

@ -15,7 +15,8 @@
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс - gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
назначенных инициализаторов (designated initializers) из C++20 назначенных инициализаторов (designated initializers) из C++20
- CMake - CMake
- Заголовки liburing, jerasure - Заголовки и библиотеки liburing, jerasure
- Опционально - заголовки и библиотеки ISA-L, libibverbs
- tcmalloc (google-perftools-dev) - tcmalloc (google-perftools-dev)
## Базовая инструкция ## Базовая инструкция

View File

@ -33,5 +33,5 @@ Vitastor Network Public License 1.1, основанная на GNU GPL 3.0 с д
и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях и также на условиях GNU GPL 2.0 или более поздней версии. Так сделано в целях
совместимости с таким ПО, как QEMU и fio. совместимости с таким ПО, как QEMU и fio.
Вы можете найти полный текст VNPL 1.1 в файле [VNPL-1.1.txt](../../VNPL-1.1.txt), Вы можете найти полный текст VNPL 1.1 на английском языке в файле [VNPL-1.1.txt](../../VNPL-1.1.txt),
а GPL 2.0 в файле [GPL-2.0.txt](../../GPL-2.0.txt). VNPL 1.1 на русском языке в файле [VNPL-1.1-RU.txt](../../VNPL-1.1-RU.txt), а GPL 2.0 в файле [GPL-2.0.txt](../../GPL-2.0.txt).

View File

@ -15,7 +15,7 @@
- Basic part: highly-available block storage with symmetric clustering and no SPOF - Basic part: highly-available block storage with symmetric clustering and no SPOF
- [Performance](../performance/comparison1.en.md) ;-D - [Performance](../performance/comparison1.en.md) ;-D
- [Multiple redundancy schemes](../config/pool.en.md#scheme): Replication, XOR n+1, Reed-Solomon erasure codes - [Multiple redundancy schemes](../config/pool.en.md#scheme): Replication, XOR n+1, Reed-Solomon erasure codes
based on jerasure library with any number of data and parity drives in a group based on jerasure and ISA-L libraries with any number of data and parity drives in a group
- Configuration via simple JSON data structures in etcd (parameters, pools and images) - Configuration via simple JSON data structures in etcd (parameters, pools and images)
- Automatic data distribution over OSDs, with support for: - Automatic data distribution over OSDs, with support for:
- Mathematical optimization for better uniformity and less data movement - Mathematical optimization for better uniformity and less data movement

View File

@ -15,7 +15,7 @@
- Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа - Базовая часть - надёжное кластерное блочное хранилище без единой точки отказа
- [Производительность](../comparison1.ru.md) ;-D - [Производительность](../comparison1.ru.md) ;-D
- [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок - [Несколько схем отказоустойчивости](../config/pool.ru.md#scheme): репликация, XOR n+1 (1 диск чётности), коды коррекции ошибок
Рида-Соломона на основе библиотеки jerasure с любым числом дисков данных и чётности в группе Рида-Соломона на основе библиотек jerasure и ISA-L с любым числом дисков данных и чётности в группе
- Конфигурация через простые человекочитаемые JSON-структуры в etcd - Конфигурация через простые человекочитаемые JSON-структуры в etcd
- Автоматическое распределение данных по OSD, с поддержкой: - Автоматическое распределение данных по OSD, с поддержкой:
- Математической оптимизации для лучшей равномерности распределения и минимизации перемещений данных - Математической оптимизации для лучшей равномерности распределения и минимизации перемещений данных

View File

@ -63,11 +63,11 @@ etcdctl --endpoints=... put /vitastor/config/pools '{"1":{"name":"testpool",
"scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":256,"failure_domain":"host"}}' "scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":256,"failure_domain":"host"}}'
``` ```
For jerasure pools the configuration should look like the following: For EC pools the configuration should look like the following:
``` ```
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool", etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
"scheme":"jerasure","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}` "scheme":"ec","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}`
``` ```
After you do this, one of the monitors will configure PGs and OSDs will start them. After you do this, one of the monitors will configure PGs and OSDs will start them.

View File

@ -75,7 +75,7 @@ etcdctl --endpoints=... put /vitastor/config/pools '{"1":{"name":"testpool",
``` ```
etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool", etcdctl --endpoints=... put /vitastor/config/pools '{"2":{"name":"ecpool",
"scheme":"jerasure","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}` "scheme":"ec","pg_size":4,"parity_chunks":2,"pg_minsize":2,"pg_count":256,"failure_domain":"host"}`
``` ```
После этого один из мониторов должен сконфигурировать PG, а OSD должны запустить их. После этого один из мониторов должен сконфигурировать PG, а OSD должны запустить их.

View File

@ -10,25 +10,18 @@ function add_pg_history(new_pg_history, new_pg, prev_pgs, prev_pg_history, old_p
if (!new_pg_history[new_pg]) if (!new_pg_history[new_pg])
{ {
new_pg_history[new_pg] = { new_pg_history[new_pg] = {
osd_set_epochs: {}, osd_sets: {},
all_peers: {}, all_peers: {},
epoch: 0, epoch: 0,
}; };
} }
const nh = new_pg_history[new_pg], oh = prev_pg_history[old_pg]; const nh = new_pg_history[new_pg], oh = prev_pg_history[old_pg];
nh.osd_set_epochs[prev_pgs[old_pg].join(' ')] = { osd_set: prev_pgs[old_pg] }; nh.osd_sets[prev_pgs[old_pg].join(' ')] = prev_pgs[old_pg];
if (oh && oh.osd_sets && oh.osd_sets.length) if (oh && oh.osd_sets && oh.osd_sets.length)
{ {
for (const pg of oh.osd_sets) for (const pg of oh.osd_sets)
{ {
nh.osd_set_epochs[pg.join(' ')] = { osd_set: pg }; nh.osd_sets[pg.join(' ')] = pg;
}
}
if (oh && oh.osd_set_epochs && oh.osd_set_epochs.length)
{
for (const pg of oh.osd_set_epochs)
{
nh.osd_set_epochs[pg.osd_set.join(' ')] = { osd_set: pg.osd_set };
} }
} }
if (oh && oh.all_peers && oh.all_peers.length) if (oh && oh.all_peers && oh.all_peers.length)
@ -46,7 +39,7 @@ function add_pg_history(new_pg_history, new_pg, prev_pgs, prev_pg_history, old_p
function finish_pg_history(merged_history) function finish_pg_history(merged_history)
{ {
merged_history.osd_set_epochs = Object.values(merged_history.osd_set_epochs); merged_history.osd_sets = Object.values(merged_history.osd_sets);
merged_history.all_peers = Object.values(merged_history.all_peers); merged_history.all_peers = Object.values(merged_history.all_peers);
} }

View File

@ -105,6 +105,7 @@ const etcd_tree = {
no_rebalance: false, no_rebalance: false,
print_stats_interval: 3, print_stats_interval: 3,
slow_log_interval: 10, slow_log_interval: 10,
inode_vanish_time: 60,
osd_memlock: false, osd_memlock: false,
// blockstore - fixed in superblock // blockstore - fixed in superblock
block_size, block_size,
@ -147,11 +148,11 @@ const etcd_tree = {
/* pools: { /* pools: {
<id>: { <id>: {
name: 'testpool', name: 'testpool',
// jerasure uses Reed-Solomon-Vandermonde codes // 'ec' uses Reed-Solomon-Vandermonde codes, 'jerasure' is an alias for 'ec'
scheme: 'replicated' | 'xor' | 'jerasure', scheme: 'replicated' | 'xor' | 'ec' | 'jerasure',
pg_size: 3, pg_size: 3,
pg_minsize: 2, pg_minsize: 2,
// number of parity chunks, required for jerasure // number of parity chunks, required for EC
parity_chunks?: 1, parity_chunks?: 1,
pg_count: 100, pg_count: 100,
failure_domain: 'host', failure_domain: 'host',
@ -276,12 +277,7 @@ const etcd_tree = {
history: { history: {
/* <pool_id>: { /* <pool_id>: {
<pg_id>: { <pg_id>: {
osd_set_epochs: { osd_sets: osd_num_t[][],
osd_set: osd_num_t[],
min_epoch: uint64_t,
max_epoch: uint64_t,
}[],
osd_sets: osd_num_t[][], // outdated
all_peers: osd_num_t[], all_peers: osd_num_t[],
epoch: uint64_t, epoch: uint64_t,
}, },
@ -952,6 +948,18 @@ class Mon
osd_set, osd_set,
primary: this.pick_primary(pool_id, osd_set, up_osds, aff_osds), primary: this.pick_primary(pool_id, osd_set, up_osds, aff_osds),
}; };
if (prev_pgs[i] && prev_pgs[i].join(' ') != osd_set.join(' ') &&
prev_pgs[i].filter(osd_num => osd_num).length > 0)
{
pg_history[i] = pg_history[i] || {};
pg_history[i].osd_sets = pg_history[i].osd_sets || [];
pg_history[i].osd_sets.push(prev_pgs[i]);
}
if (pg_history[i] && pg_history[i].osd_sets)
{
pg_history[i].osd_sets = Object.values(pg_history[i].osd_sets
.reduce((a, c) => { a[c.join(' ')] = c; return a; }, {}));
}
}); });
for (let i = 0; i < new_pgs.length || i < prev_pgs.length; i++) for (let i = 0; i < new_pgs.length || i < prev_pgs.length; i++)
{ {
@ -1006,14 +1014,15 @@ class Mon
console.log('Pool ID '+pool_id+' is invalid'); console.log('Pool ID '+pool_id+' is invalid');
return false; return false;
} }
if (pool_cfg.scheme !== 'xor' && pool_cfg.scheme !== 'replicated' && pool_cfg.scheme !== 'jerasure') if (pool_cfg.scheme !== 'xor' && pool_cfg.scheme !== 'replicated' &&
pool_cfg.scheme !== 'ec' && pool_cfg.scheme !== 'jerasure')
{ {
if (warn) if (warn)
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated" and "jerasure" required)'); console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
return false; return false;
} }
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 || if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
(pool_cfg.scheme === 'xor' || pool_cfg.scheme == 'jerasure') && pool_cfg.pg_size < 3) pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
{ {
if (warn) if (warn)
console.log('Pool '+pool_id+' has invalid pg_size'); console.log('Pool '+pool_id+' has invalid pg_size');
@ -1032,7 +1041,8 @@ class Mon
console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)'); console.log('Pool '+pool_id+' has invalid parity_chunks (must be 1)');
return false; return false;
} }
if (pool_cfg.scheme === 'jerasure' && (pool_cfg.parity_chunks < 1 || pool_cfg.parity_chunks > pool_cfg.pg_size-2)) if ((pool_cfg.scheme === 'ec' || pool_cfg.scheme === 'jerasure') &&
(pool_cfg.parity_chunks < 1 || pool_cfg.parity_chunks > pool_cfg.pg_size-2))
{ {
if (warn) if (warn)
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)'); console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
@ -1146,6 +1156,10 @@ class Mon
{ {
prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set; prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set;
} }
// Also delete pool statistics
etcd_request.success.push({ requestDeleteRange: {
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
} });
this.save_new_pgs_txn(etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []); this.save_new_pgs_txn(etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []);
} }
} }

View File

@ -50,7 +50,7 @@ from cinder.volume import configuration
from cinder.volume import driver from cinder.volume import driver
from cinder.volume import volume_utils from cinder.volume import volume_utils
VERSION = '0.6.17' VERSION = '0.7.1'
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -464,7 +464,7 @@ class VitastorDriver(driver.CloneableImageVD,
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
snap_name = utils.convert_str(snapshot.name) snap_name = utils.convert_str(snapshot.name)
snap = self._get_image(vol_name+'@'+snap_name) snap = self._get_image('volume-'+snapshot.volume_id+'@'+snap_name)
if not snap: if not snap:
raise exception.SnapshotNotFound(snapshot_id = snap_name) raise exception.SnapshotNotFound(snapshot_id = snap_name)
snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id']) snap_inode_id = int(resp['responses'][0]['kvs'][0]['value']['id'])

View File

@ -25,4 +25,4 @@ rm fio
mv fio-copy fio mv fio-copy fio
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'` FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
tar --transform 's#^#vitastor-0.6.17/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.6.17$(rpm --eval '%dist').tar.gz * tar --transform 's#^#vitastor-0.7.1/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.7.1$(rpm --eval '%dist').tar.gz *

View File

@ -9,7 +9,8 @@ WORKDIR /root
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel fio rh-nodejs12 jerasure-devel gf-complete-devel rdma-core-devel RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel \
fio rh-nodejs12 jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
RUN rpm --nomd5 -i fio*.src.rpm RUN rpm --nomd5 -i fio*.src.rpm
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
@ -34,7 +35,7 @@ ADD . /root/vitastor
RUN set -e; \ RUN set -e; \
cd /root/vitastor/rpm; \ cd /root/vitastor/rpm; \
sh build-tarball.sh; \ sh build-tarball.sh; \
cp /root/vitastor-0.6.17.el7.tar.gz ~/rpmbuild/SOURCES; \ cp /root/vitastor-0.7.1.el7.tar.gz ~/rpmbuild/SOURCES; \
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \ cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
cd ~/rpmbuild/SPECS/; \ cd ~/rpmbuild/SPECS/; \
rpmbuild -ba vitastor.spec; \ rpmbuild -ba vitastor.spec; \

View File

@ -1,11 +1,11 @@
Name: vitastor Name: vitastor
Version: 0.6.17 Version: 0.7.1
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-0.6.17.el7.tar.gz Source0: vitastor-0.7.1.el7.tar.gz
BuildRequires: liburing-devel >= 0.6 BuildRequires: liburing-devel >= 0.6
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
@ -13,6 +13,7 @@ BuildRequires: devtoolset-9-gcc-c++
BuildRequires: rh-nodejs12 BuildRequires: rh-nodejs12
BuildRequires: rh-nodejs12-npm BuildRequires: rh-nodejs12-npm
BuildRequires: jerasure-devel BuildRequires: jerasure-devel
BuildRequires: libisa-l-devel
BuildRequires: gf-complete-devel BuildRequires: gf-complete-devel
BuildRequires: libibverbs-devel BuildRequires: libibverbs-devel
BuildRequires: cmake BuildRequires: cmake
@ -32,6 +33,7 @@ size with configurable redundancy (replication or erasure codes/XOR).
%package -n vitastor-osd %package -n vitastor-osd
Summary: Vitastor - OSD Summary: Vitastor - OSD
Requires: libJerasure2 Requires: libJerasure2
Requires: libisa-l
Requires: liburing >= 0.6 Requires: liburing >= 0.6
Requires: vitastor-client = %{version}-%{release} Requires: vitastor-client = %{version}-%{release}
@ -111,6 +113,7 @@ cp -r mon %buildroot/usr/lib/vitastor
%files -n vitastor-osd %files -n vitastor-osd
%_bindir/vitastor-osd %_bindir/vitastor-osd
%_bindir/vitastor-dump-journal %_bindir/vitastor-dump-journal
%_bindir/vitastor-dump-meta
%files -n vitastor-mon %files -n vitastor-mon

View File

@ -6,10 +6,12 @@ FROM centos:8
WORKDIR /root WORKDIR /root
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/!baseurl=http://vault.centos.org/!' /etc/yum.repos.d/*.repo
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/$basearch/$avdir/!; s!^baseurl=.*Source/.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/Source/advanced-virtualization/!' /etc/yum.repos.d/CentOS-Advanced-Virtualization.repo
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \ RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
fio nodejs rpm-build jerasure-devel gf-complete-devel libibverbs-devel libarchive cmake fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel libibverbs-devel libarchive cmake
RUN dnf download --source fio RUN dnf download --source fio
RUN rpm --nomd5 -i fio*.src.rpm RUN rpm --nomd5 -i fio*.src.rpm
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
@ -33,7 +35,7 @@ ADD . /root/vitastor
RUN set -e; \ RUN set -e; \
cd /root/vitastor/rpm; \ cd /root/vitastor/rpm; \
sh build-tarball.sh; \ sh build-tarball.sh; \
cp /root/vitastor-0.6.17.el8.tar.gz ~/rpmbuild/SOURCES; \ cp /root/vitastor-0.7.1.el8.tar.gz ~/rpmbuild/SOURCES; \
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \ cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
cd ~/rpmbuild/SPECS/; \ cd ~/rpmbuild/SPECS/; \
rpmbuild -ba vitastor.spec; \ rpmbuild -ba vitastor.spec; \

View File

@ -1,17 +1,18 @@
Name: vitastor Name: vitastor
Version: 0.6.17 Version: 0.7.1
Release: 1%{?dist} Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1 License: Vitastor Network Public License 1.1
URL: https://vitastor.io/ URL: https://vitastor.io/
Source0: vitastor-0.6.17.el8.tar.gz Source0: vitastor-0.7.1.el8.tar.gz
BuildRequires: liburing-devel >= 0.6 BuildRequires: liburing-devel >= 0.6
BuildRequires: gperftools-devel BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++ BuildRequires: gcc-toolset-9-gcc-c++
BuildRequires: nodejs >= 10 BuildRequires: nodejs >= 10
BuildRequires: jerasure-devel BuildRequires: jerasure-devel
BuildRequires: libisa-l-devel
BuildRequires: gf-complete-devel BuildRequires: gf-complete-devel
BuildRequires: libibverbs-devel BuildRequires: libibverbs-devel
BuildRequires: cmake BuildRequires: cmake
@ -31,6 +32,7 @@ size with configurable redundancy (replication or erasure codes/XOR).
%package -n vitastor-osd %package -n vitastor-osd
Summary: Vitastor - OSD Summary: Vitastor - OSD
Requires: libJerasure2 Requires: libJerasure2
Requires: libisa-l
Requires: liburing >= 0.6 Requires: liburing >= 0.6
Requires: vitastor-client = %{version}-%{release} Requires: vitastor-client = %{version}-%{release}
@ -108,6 +110,7 @@ cp -r mon %buildroot/usr/lib/vitastor
%files -n vitastor-osd %files -n vitastor-osd
%_bindir/vitastor-osd %_bindir/vitastor-osd
%_bindir/vitastor-dump-journal %_bindir/vitastor-dump-journal
%_bindir/vitastor-dump-meta
%files -n vitastor-mon %files -n vitastor-mon

View File

@ -15,7 +15,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
endif() endif()
add_definitions(-DVERSION="0.6.17") add_definitions(-DVERSION="0.7.1")
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src) add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
if (${WITH_ASAN}) if (${WITH_ASAN})
add_definitions(-fsanitize=address -fno-omit-frame-pointer) add_definitions(-fsanitize=address -fno-omit-frame-pointer)
@ -50,6 +50,10 @@ pkg_check_modules(IBVERBS libibverbs)
if (IBVERBS_LIBRARIES) if (IBVERBS_LIBRARIES)
add_definitions(-DWITH_RDMA) add_definitions(-DWITH_RDMA)
endif (IBVERBS_LIBRARIES) endif (IBVERBS_LIBRARIES)
pkg_check_modules(ISAL libisal)
if (ISAL_LIBRARIES)
add_definitions(-DWITH_ISAL)
endif (ISAL_LIBRARIES)
include_directories( include_directories(
../ ../
@ -104,6 +108,7 @@ target_link_libraries(vitastor-osd
vitastor_common vitastor_common
vitastor_blk vitastor_blk
Jerasure Jerasure
${ISAL_LIBRARIES}
${IBVERBS_LIBRARIES} ${IBVERBS_LIBRARIES}
) )
@ -193,6 +198,11 @@ add_executable(vitastor-dump-journal
dump_journal.cpp crc32c.c dump_journal.cpp crc32c.c
) )
# vitastor-dump-meta
add_executable(vitastor-dump-meta
dump_meta.cpp rw_blocking.cpp
)
if (${WITH_QEMU}) if (${WITH_QEMU})
# qemu_driver.so # qemu_driver.so
add_library(qemu_vitastor SHARED add_library(qemu_vitastor SHARED
@ -225,7 +235,7 @@ target_link_libraries(osd_test tcmalloc_minimal)
# osd_rmw_test # osd_rmw_test
add_executable(osd_rmw_test osd_rmw_test.cpp allocator.cpp) add_executable(osd_rmw_test 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)
# stub_uring_osd # stub_uring_osd
add_executable(stub_uring_osd add_executable(stub_uring_osd
@ -270,7 +280,7 @@ target_include_directories(test_cluster_client PUBLIC ${CMAKE_SOURCE_DIR}/src/mo
### Install ### Install
install(TARGETS vitastor-osd vitastor-dump-journal vitastor-nbd vitastor-nfs vitastor-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS vitastor-osd vitastor-dump-journal vitastor-dump-meta vitastor-nbd vitastor-nfs vitastor-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm) install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita) install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
install( install(

View File

@ -19,7 +19,11 @@
#include "timerfd_manager.h" #include "timerfd_manager.h"
// Memory alignment for direct I/O (usually 512 bytes) // Memory alignment for direct I/O (usually 512 bytes)
// All other alignments must be a multiple of this one #ifndef DIRECT_IO_ALIGNMENT
#define DIRECT_IO_ALIGNMENT 512
#endif
// Memory allocation alignment (page size is usually optimal)
#ifndef MEM_ALIGNMENT #ifndef MEM_ALIGNMENT
#define MEM_ALIGNMENT 4096 #define MEM_ALIGNMENT 4096
#endif #endif

View File

@ -90,13 +90,13 @@
#include "blockstore_journal.h" #include "blockstore_journal.h"
// "VITAstor" // "VITAstor"
#define BLOCKSTORE_META_MAGIC 0x726F747341544956l #define BLOCKSTORE_META_MAGIC_V1 0x726F747341544956l
#define BLOCKSTORE_META_VERSION 1 #define BLOCKSTORE_META_VERSION_V1 1
// metadata header (superblock) // metadata header (superblock)
// FIXME: After adding the OSD superblock, add a key to metadata // FIXME: After adding the OSD superblock, add a key to metadata
// and journal headers to check if they belong to the same OSD // and journal headers to check if they belong to the same OSD
struct __attribute__((__packed__)) blockstore_meta_header_t struct __attribute__((__packed__)) blockstore_meta_header_v1_t
{ {
uint64_t zero; uint64_t zero;
uint64_t magic; uint64_t magic;

View File

@ -71,10 +71,10 @@ resume_1:
if (iszero((uint64_t*)metadata_buffer, bs->meta_block_size / sizeof(uint64_t))) if (iszero((uint64_t*)metadata_buffer, bs->meta_block_size / sizeof(uint64_t)))
{ {
{ {
blockstore_meta_header_t *hdr = (blockstore_meta_header_t *)metadata_buffer; blockstore_meta_header_v1_t *hdr = (blockstore_meta_header_v1_t *)metadata_buffer;
hdr->zero = 0; hdr->zero = 0;
hdr->magic = BLOCKSTORE_META_MAGIC; hdr->magic = BLOCKSTORE_META_MAGIC_V1;
hdr->version = BLOCKSTORE_META_VERSION; hdr->version = BLOCKSTORE_META_VERSION_V1;
hdr->meta_block_size = bs->meta_block_size; hdr->meta_block_size = bs->meta_block_size;
hdr->data_block_size = bs->block_size; hdr->data_block_size = bs->block_size;
hdr->bitmap_granularity = bs->bitmap_granularity; hdr->bitmap_granularity = bs->bitmap_granularity;
@ -103,10 +103,10 @@ resume_1:
} }
else else
{ {
blockstore_meta_header_t *hdr = (blockstore_meta_header_t *)metadata_buffer; blockstore_meta_header_v1_t *hdr = (blockstore_meta_header_v1_t *)metadata_buffer;
if (hdr->zero != 0 || if (hdr->zero != 0 ||
hdr->magic != BLOCKSTORE_META_MAGIC || hdr->magic != BLOCKSTORE_META_MAGIC_V1 ||
hdr->version != BLOCKSTORE_META_VERSION) hdr->version != BLOCKSTORE_META_VERSION_V1)
{ {
printf( printf(
"Metadata is corrupt or old version.\n" "Metadata is corrupt or old version.\n"
@ -912,7 +912,11 @@ void blockstore_init_journal::erase_dirty_object(blockstore_dirty_db_t::iterator
? clean_it->second.location : UINT64_MAX; ? clean_it->second.location : UINT64_MAX;
if (exists && clean_loc == UINT64_MAX) if (exists && clean_loc == UINT64_MAX)
{ {
bs->inode_space_stats[oid.inode] -= bs->block_size; auto & sp = bs->inode_space_stats[oid.inode];
if (sp > bs->block_size)
sp -= bs->block_size;
else
bs->inode_space_stats.erase(oid.inode);
} }
bs->erase_dirty(dirty_it, dirty_end, clean_loc); bs->erase_dirty(dirty_it, dirty_end, clean_loc);
// Remove it from the flusher's queue, too // Remove it from the flusher's queue, too

View File

@ -109,25 +109,25 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config)
{ {
disk_alignment = 4096; disk_alignment = 4096;
} }
else if (disk_alignment % MEM_ALIGNMENT) else if (disk_alignment % DIRECT_IO_ALIGNMENT)
{ {
throw std::runtime_error("disk_alignment must be a multiple of "+std::to_string(MEM_ALIGNMENT)); throw std::runtime_error("disk_alignment must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
} }
if (!journal_block_size) if (!journal_block_size)
{ {
journal_block_size = 4096; journal_block_size = 4096;
} }
else if (journal_block_size % MEM_ALIGNMENT) else if (journal_block_size % DIRECT_IO_ALIGNMENT)
{ {
throw std::runtime_error("journal_block_size must be a multiple of "+std::to_string(MEM_ALIGNMENT)); throw std::runtime_error("journal_block_size must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
} }
if (!meta_block_size) if (!meta_block_size)
{ {
meta_block_size = 4096; meta_block_size = 4096;
} }
else if (meta_block_size % MEM_ALIGNMENT) else if (meta_block_size % DIRECT_IO_ALIGNMENT)
{ {
throw std::runtime_error("meta_block_size must be a multiple of "+std::to_string(MEM_ALIGNMENT)); throw std::runtime_error("meta_block_size must be a multiple of "+std::to_string(DIRECT_IO_ALIGNMENT));
} }
if (data_offset % disk_alignment) if (data_offset % disk_alignment)
{ {

View File

@ -200,7 +200,11 @@ void blockstore_impl_t::mark_stable(const obj_ver_id & v, bool forget_dirty)
} }
else if (IS_DELETE(dirty_it->second.state)) else if (IS_DELETE(dirty_it->second.state))
{ {
inode_space_stats[dirty_it->first.oid.inode] -= block_size; auto & sp = inode_space_stats[dirty_it->first.oid.inode];
if (sp > block_size)
sp -= block_size;
else
inode_space_stats.erase(dirty_it->first.oid.inode);
} }
} }
if (forget_dirty && (IS_BIG_WRITE(dirty_it->second.state) || if (forget_dirty && (IS_BIG_WRITE(dirty_it->second.state) ||

View File

@ -39,6 +39,7 @@ public:
ring_loop_t *ringloop = NULL; ring_loop_t *ringloop = NULL;
epoll_manager_t *epmgr = NULL; epoll_manager_t *epmgr = NULL;
cluster_client_t *cli = NULL; cluster_client_t *cli = NULL;
bool no_recovery = false, no_rebalance = false, readonly = false;
int waiting = 0; int waiting = 0;
cli_result_t etcd_err; cli_result_t etcd_err;

View File

@ -276,7 +276,8 @@ resume_4:
new_id = 1+INODE_NO_POOL(kv.value.uint64_value()); new_id = 1+INODE_NO_POOL(kv.value.uint64_value());
max_id_mod_rev = kv.mod_revision; max_id_mod_rev = kv.mod_revision;
} }
auto ino_it = parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(new_pool_id, 0)); // Also check existing inodes - for the case when some inodes are created without changing /index/maxid
auto ino_it = parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(new_pool_id+1, 0));
if (ino_it != parent->cli->st_cli.inode_config.begin()) if (ino_it != parent->cli->st_cli.inode_config.begin())
{ {
ino_it--; ino_it--;

View File

@ -127,7 +127,7 @@ resume_1:
pool_stats[pool_cfg.id] = json11::Json::object { pool_stats[pool_cfg.id] = json11::Json::object {
{ "name", pool_cfg.name }, { "name", pool_cfg.name },
{ "pg_count", pool_cfg.pg_count }, { "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 { "scheme_name", pool_cfg.scheme == POOL_SCHEME_REPLICATED
? std::to_string(pool_cfg.pg_size)+"/"+std::to_string(pool_cfg.pg_minsize) ? 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) }, : "EC "+std::to_string(pool_cfg.pg_size-pool_cfg.parity_chunks)+"+"+std::to_string(pool_cfg.parity_chunks) },

View File

@ -64,8 +64,9 @@ struct rm_inode_t
} }
rm->obj_pos = rm->objects.begin(); rm->obj_pos = rm->objects.begin();
lists.push_back(rm); lists.push_back(rm);
if (parent->list_first) if (parent->list_first && !(status & INODE_LIST_DONE))
{ {
// The listing object is dead when DONE => don't call next()
parent->cli->list_inode_next(lister, 1); parent->cli->list_inode_next(lister, 1);
} }
if (status & INODE_LIST_DONE) if (status & INODE_LIST_DONE)

View File

@ -14,6 +14,11 @@
std::function<bool(cli_result_t &)> cli_tool_t::simple_offsets(json11::Json cfg) std::function<bool(cli_result_t &)> cli_tool_t::simple_offsets(json11::Json cfg)
{ {
std::string device = cfg["device"].string_value(); std::string device = cfg["device"].string_value();
if (device == "")
{
fprintf(stderr, "Device path is missing\n");
exit(1);
}
uint64_t object_size = parse_size(cfg["object_size"].string_value()); uint64_t object_size = parse_size(cfg["object_size"].string_value());
uint64_t bitmap_granularity = parse_size(cfg["bitmap_granularity"].string_value()); uint64_t bitmap_granularity = parse_size(cfg["bitmap_granularity"].string_value());
uint64_t journal_size = parse_size(cfg["journal_size"].string_value()); uint64_t journal_size = parse_size(cfg["journal_size"].string_value());

View File

@ -5,6 +5,7 @@
#include "cluster_client.h" #include "cluster_client.h"
#include "base64.h" #include "base64.h"
#include "pg_states.h" #include "pg_states.h"
#include "http_client.h"
// Print cluster status: // Print cluster status:
// etcd, mon, osd states // etcd, mon, osd states
@ -207,6 +208,9 @@ resume_2:
obj_n = agg_stats["object_counts"]["incomplete"].uint64_value(); obj_n = agg_stats["object_counts"]["incomplete"].uint64_value();
if (obj_n > 0) if (obj_n > 0)
more_states += ", "+format_size(obj_n*object_size)+" incomplete"; more_states += ", "+format_size(obj_n*object_size)+" incomplete";
bool readonly = json_is_true(parent->cli->merged_config["readonly"]);
bool no_recovery = json_is_true(parent->cli->merged_config["no_recovery"]);
bool no_rebalance = json_is_true(parent->cli->merged_config["no_rebalance"]);
std::string recovery_io; std::string recovery_io;
{ {
uint64_t deg_bps = agg_stats["recovery_stats"]["degraded"]["bps"].uint64_value(); uint64_t deg_bps = agg_stats["recovery_stats"]["degraded"]["bps"].uint64_value();
@ -214,9 +218,19 @@ resume_2:
uint64_t misp_bps = agg_stats["recovery_stats"]["misplaced"]["bps"].uint64_value(); uint64_t misp_bps = agg_stats["recovery_stats"]["misplaced"]["bps"].uint64_value();
uint64_t misp_iops = agg_stats["recovery_stats"]["misplaced"]["iops"].uint64_value(); uint64_t misp_iops = agg_stats["recovery_stats"]["misplaced"]["iops"].uint64_value();
if (deg_iops > 0 || deg_bps > 0) if (deg_iops > 0 || deg_bps > 0)
recovery_io += " recovery: "+format_size(deg_bps)+"/s, "+format_size(deg_iops, true)+" op/s\n"; {
recovery_io += " recovery: "+std::string(no_recovery ? "disabled, " : "")+
format_size(deg_bps)+"/s, "+format_size(deg_iops, true)+" op/s\n";
}
else if (no_recovery)
recovery_io += " recovery: disabled\n";
if (misp_iops > 0 || misp_bps > 0) if (misp_iops > 0 || misp_bps > 0)
recovery_io += " rebalance: "+format_size(misp_bps)+"/s, "+format_size(misp_iops, true)+" op/s\n"; {
recovery_io += " rebalance: "+std::string(no_rebalance ? "disabled, " : "")+
format_size(misp_bps)+"/s, "+format_size(misp_iops, true)+" op/s\n";
}
else if (no_rebalance)
recovery_io += " rebalance: disabled\n";
} }
if (parent->json_output) if (parent->json_output)
{ {
@ -233,6 +247,9 @@ resume_2:
{ "free_raw", free_raw }, { "free_raw", free_raw },
{ "down_raw", down_raw }, { "down_raw", down_raw },
{ "free_down_raw", free_down_raw }, { "free_down_raw", free_down_raw },
{ "readonly", readonly },
{ "no_recovery", no_recovery },
{ "no_rebalance", no_rebalance },
{ "clean_data", agg_stats["object_counts"]["clean"].uint64_value() * object_size }, { "clean_data", agg_stats["object_counts"]["clean"].uint64_value() * object_size },
{ "misplaced_data", agg_stats["object_counts"]["misplaced"].uint64_value() * object_size }, { "misplaced_data", agg_stats["object_counts"]["misplaced"].uint64_value() * object_size },
{ "degraded_data", agg_stats["object_counts"]["degraded"].uint64_value() * object_size }, { "degraded_data", agg_stats["object_counts"]["degraded"].uint64_value() * object_size },
@ -259,7 +276,7 @@ resume_2:
" pools: %d / %d active\n" " pools: %d / %d active\n"
" pgs: %s\n" " pgs: %s\n"
" \n" " \n"
" io:\n" " io%s:\n"
" client:%s %s/s rd, %s op/s rd, %s/s wr, %s op/s wr\n" " client:%s %s/s rd, %s op/s rd, %s/s wr, %s op/s wr\n"
"%s", "%s",
etcd_alive, etcd_states.size(), format_size(etcd_db_size).c_str(), etcd_alive, etcd_states.size(), format_size(etcd_db_size).c_str(),
@ -272,6 +289,7 @@ resume_2:
format_size(agg_stats["object_counts"]["clean"].uint64_value() * object_size).c_str(), more_states.c_str(), format_size(agg_stats["object_counts"]["clean"].uint64_value() * object_size).c_str(), more_states.c_str(),
pools_active, pool_count, pools_active, pool_count,
pgs_by_state_str.c_str(), pgs_by_state_str.c_str(),
readonly ? " (read-only mode)" : "",
recovery_io.size() > 0 ? " " : "", recovery_io.size() > 0 ? " " : "",
format_size(agg_stats["op_stats"]["primary_read"]["bps"].uint64_value()).c_str(), format_size(agg_stats["op_stats"]["primary_read"]["bps"].uint64_value()).c_str(),
format_size(agg_stats["op_stats"]["primary_read"]["iops"].uint64_value(), true).c_str(), format_size(agg_stats["op_stats"]["primary_read"]["iops"].uint64_value(), true).c_str(),

View File

@ -279,6 +279,11 @@ static uint32_t is_power_of_two(uint64_t value)
void cluster_client_t::on_load_config_hook(json11::Json::object & config) void cluster_client_t::on_load_config_hook(json11::Json::object & config)
{ {
this->merged_config = config;
for (auto & kv: this->config.object_items())
{
this->merged_config[kv.first] = kv.second;
}
bs_block_size = config["block_size"].uint64_value(); bs_block_size = config["block_size"].uint64_value();
bs_bitmap_granularity = config["bitmap_granularity"].uint64_value(); bs_bitmap_granularity = config["bitmap_granularity"].uint64_value();
if (!bs_block_size) if (!bs_block_size)

View File

@ -111,6 +111,7 @@ public:
etcd_state_client_t st_cli; etcd_state_client_t st_cli;
osd_messenger_t msgr; osd_messenger_t msgr;
json11::Json config; json11::Json config;
json11::Json::object merged_config;
cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd, json11::Json & config); cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd, json11::Json & config);
~cluster_client_t(); ~cluster_client_t();

View File

@ -95,7 +95,7 @@ inode_list_t* cluster_client_t::list_inode_start(inode_t inode,
} }
for (auto & hist_item: pg.target_history) for (auto & hist_item: pg.target_history)
{ {
for (auto pg_osd: hist_item.osd_set) for (auto pg_osd: hist_item)
{ {
if (pg_osd != 0) if (pg_osd != 0)
{ {
@ -104,8 +104,6 @@ inode_list_t* cluster_client_t::list_inode_start(inode_t inode,
} }
} }
for (osd_num_t peer_osd: all_peers) for (osd_num_t peer_osd: all_peers)
{
if (st_cli.peer_states.find(peer_osd) != st_cli.peer_states.end())
{ {
r->list_osds.push_back((inode_list_osd_t){ r->list_osds.push_back((inode_list_osd_t){
.pg = r, .pg = r,
@ -114,7 +112,6 @@ inode_list_t* cluster_client_t::list_inode_start(inode_t inode,
}); });
} }
} }
}
else else
{ {
// Clean // Clean
@ -156,16 +153,6 @@ void cluster_client_t::continue_listing(inode_list_t *lst)
{ {
if (lst->done_pgs >= lst->pgs.size()) if (lst->done_pgs >= lst->pgs.size())
{ {
// All done
for (int i = 0; i < lists.size(); i++)
{
if (lists[i] == lst)
{
lists.erase(lists.begin()+i, lists.begin()+i+1);
break;
}
}
delete lst;
return; return;
} }
if (lst->want <= 0) if (lst->want <= 0)
@ -181,7 +168,7 @@ void cluster_client_t::continue_listing(inode_list_t *lst)
send_list(&lst->pgs[i]->list_osds[j]); send_list(&lst->pgs[i]->list_osds[j]);
if (lst->want <= 0) if (lst->want <= 0)
{ {
break; return;
} }
} }
} }
@ -271,6 +258,24 @@ void cluster_client_t::send_list(inode_list_osd_t *cur_list)
lst->callback(lst, std::move(pg->objects), pg->pg_num, pg->cur_primary, status); lst->callback(lst, std::move(pg->objects), pg->pg_num, pg->cur_primary, status);
lst->pgs[pg->pos] = NULL; lst->pgs[pg->pos] = NULL;
delete pg; delete pg;
if (lst->done_pgs >= lst->pgs.size())
{
// All done
for (int i = 0; i < lists.size(); i++)
{
if (lists[i] == lst)
{
lists.erase(lists.begin()+i, lists.begin()+i+1);
break;
}
}
delete lst;
return;
}
}
else
{
lst->want++;
} }
continue_listing(lst); continue_listing(lst);
}; };

View File

@ -52,7 +52,7 @@ int main(int argc, char *argv[])
self.journal_block = strtoul(argv[b+1], NULL, 10); self.journal_block = strtoul(argv[b+1], NULL, 10);
self.journal_offset = strtoull(argv[b+2], NULL, 10); self.journal_offset = strtoull(argv[b+2], NULL, 10);
self.journal_len = strtoull(argv[b+3], NULL, 10); self.journal_len = strtoull(argv[b+3], NULL, 10);
if (self.journal_block < MEM_ALIGNMENT || (self.journal_block % MEM_ALIGNMENT) || if (self.journal_block < DIRECT_IO_ALIGNMENT || (self.journal_block % DIRECT_IO_ALIGNMENT) ||
self.journal_block > 128*1024) self.journal_block > 128*1024)
{ {
printf("Invalid journal block size\n"); printf("Invalid journal block size\n");

169
src/dump_meta.cpp Normal file
View File

@ -0,0 +1,169 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include "blockstore_impl.h"
#include "osd_id.h"
#include "rw_blocking.h"
struct meta_dumper_t
{
char *meta_device;
uint32_t meta_block_size;
uint64_t meta_offset;
uint64_t meta_len;
uint64_t meta_pos;
int fd;
int dump();
};
int main(int argc, char *argv[])
{
meta_dumper_t self = { 0 };
int b = 1;
if (argc < b+4)
{
printf("USAGE: %s <meta_file> <meta_block_size> <offset> <size>\n", argv[0]);
return 1;
}
self.meta_device = argv[b];
self.meta_block_size = strtoul(argv[b+1], NULL, 10);
self.meta_offset = strtoull(argv[b+2], NULL, 10);
self.meta_len = strtoull(argv[b+3], NULL, 10);
return self.dump();
}
int meta_dumper_t::dump()
{
if (this->meta_block_size % DIRECT_IO_ALIGNMENT)
{
printf("Invalid metadata block size\n");
return 1;
}
this->fd = open(this->meta_device, O_DIRECT|O_RDONLY);
if (this->fd == -1)
{
printf("Failed to open metadata device\n");
return 1;
}
int buf_size = 1024*1024;
if (buf_size % this->meta_block_size)
buf_size = 8*this->meta_block_size;
if (buf_size > this->meta_len)
buf_size = this->meta_len;
void *data = memalign_or_die(MEM_ALIGNMENT, buf_size);
lseek64(this->fd, this->meta_offset, 0);
read_blocking(this->fd, data, buf_size);
// Check superblock
blockstore_meta_header_v1_t *hdr = (blockstore_meta_header_v1_t *)data;
if (hdr->zero == 0 &&
hdr->magic == BLOCKSTORE_META_MAGIC_V1 &&
hdr->version == BLOCKSTORE_META_VERSION_V1)
{
// Vitastor 0.6-0.7 - static array of clean_disk_entry with bitmaps
if (hdr->meta_block_size != this->meta_block_size)
{
printf("Using block size of %u bytes based on information from the superblock\n", hdr->meta_block_size);
this->meta_block_size = hdr->meta_block_size;
if (buf_size % this->meta_block_size)
{
buf_size = 8*this->meta_block_size;
free(data);
data = memalign_or_die(MEM_ALIGNMENT, buf_size);
}
}
this->meta_offset += this->meta_block_size;
this->meta_len -= this->meta_block_size;
uint64_t clean_entry_bitmap_size = hdr->data_block_size / hdr->bitmap_granularity / 8;
uint64_t clean_entry_size = sizeof(clean_disk_entry) + 2*clean_entry_bitmap_size;
uint64_t block_num = 0;
printf(
"{\"version\":\"0.6\",\"meta_block_size\":%u,\"data_block_size\":%u,\"bitmap_granularity\":%u,\"entries\":[\n",
hdr->meta_block_size, hdr->data_block_size, hdr->bitmap_granularity
);
bool first = true;
lseek64(this->fd, this->meta_offset, 0);
while (this->meta_pos < this->meta_len)
{
uint64_t read_len = buf_size < this->meta_len-this->meta_pos ? buf_size : this->meta_len-this->meta_pos;
read_blocking(this->fd, data, read_len);
this->meta_pos += read_len;
for (uint64_t blk = 0; blk < read_len; blk += this->meta_block_size)
{
for (uint64_t ioff = 0; ioff < this->meta_block_size-clean_entry_size; ioff += clean_entry_size, block_num++)
{
clean_disk_entry *entry = (clean_disk_entry*)(data + blk + ioff);
if (entry->oid.inode)
{
printf(
#define ENTRY_FMT "{\"block\":%lu,\"pool\":%u,\"inode\":%lu,\"stripe\":%lu,\"version\":%lu,\"bitmap\":\""
(first ? ENTRY_FMT : (",\n" ENTRY_FMT)),
#undef ENTRY_FMT
block_num, INODE_POOL(entry->oid.inode), INODE_NO_POOL(entry->oid.inode),
entry->oid.stripe, entry->version
);
first = false;
for (uint64_t i = 0; i < clean_entry_bitmap_size; i++)
{
printf("%02x", entry->bitmap[i]);
}
printf("\",\"ext_bitmap\":\"");
for (uint64_t i = 0; i < clean_entry_bitmap_size; i++)
{
printf("%02x", entry->bitmap[clean_entry_bitmap_size + i]);
}
printf("\"}");
}
}
}
}
printf("\n]}\n");
}
else
{
// Vitastor 0.4-0.5 - static array of clean_disk_entry
uint64_t clean_entry_size = sizeof(clean_disk_entry);
uint64_t block_num = 0;
printf("{\"version\":\"0.5\",\"meta_block_size\":%u,\"entries\":[\n", this->meta_block_size);
bool first = true;
while (this->meta_pos < this->meta_len)
{
uint64_t read_len = buf_size < this->meta_len-this->meta_pos ? buf_size : this->meta_len-this->meta_pos;
read_blocking(this->fd, data, read_len);
this->meta_pos += read_len;
for (uint64_t blk = 0; blk < read_len; blk += this->meta_block_size)
{
for (uint64_t ioff = 0; ioff < this->meta_block_size-clean_entry_size; ioff += clean_entry_size, block_num++)
{
clean_disk_entry *entry = (clean_disk_entry*)(data + blk + ioff);
if (entry->oid.inode)
{
printf(
#define ENTRY_FMT "{\"block\":%lu,\"pool\":%u,\"inode\":%lu,\"stripe\":%lu,\"version\":%lu}"
(first ? ENTRY_FMT : (",\n" ENTRY_FMT)),
#undef ENTRY_FMT
block_num, INODE_POOL(entry->oid.inode), INODE_NO_POOL(entry->oid.inode),
entry->oid.stripe, entry->version
);
first = false;
}
}
}
}
printf("\n]}\n");
}
free(data);
close(this->fd);
return 0;
}

View File

@ -673,18 +673,18 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
pc.scheme = POOL_SCHEME_REPLICATED; pc.scheme = POOL_SCHEME_REPLICATED;
else if (pool_item.second["scheme"] == "xor") else if (pool_item.second["scheme"] == "xor")
pc.scheme = POOL_SCHEME_XOR; pc.scheme = POOL_SCHEME_XOR;
else if (pool_item.second["scheme"] == "jerasure") else if (pool_item.second["scheme"] == "ec" || pool_item.second["scheme"] == "jerasure")
pc.scheme = POOL_SCHEME_JERASURE; pc.scheme = POOL_SCHEME_EC;
else else
{ {
fprintf(stderr, "Pool %u has invalid coding scheme (one of \"xor\", \"replicated\" or \"jerasure\" required), skipping pool\n", pool_id); fprintf(stderr, "Pool %u has invalid coding scheme (one of \"xor\", \"replicated\", \"ec\" or \"jerasure\" required), skipping pool\n", pool_id);
continue; continue;
} }
// PG Size // PG Size
pc.pg_size = pool_item.second["pg_size"].uint64_value(); pc.pg_size = pool_item.second["pg_size"].uint64_value();
if (pc.pg_size < 1 || if (pc.pg_size < 1 ||
pool_item.second["pg_size"].uint64_value() < 3 && pool_item.second["pg_size"].uint64_value() < 3 &&
(pc.scheme == POOL_SCHEME_XOR || pc.scheme == POOL_SCHEME_JERASURE) || (pc.scheme == POOL_SCHEME_XOR || pc.scheme == POOL_SCHEME_EC) ||
pool_item.second["pg_size"].uint64_value() > 256) pool_item.second["pg_size"].uint64_value() > 256)
{ {
fprintf(stderr, "Pool %u has invalid pg_size, skipping pool\n", pool_id); fprintf(stderr, "Pool %u has invalid pg_size, skipping pool\n", pool_id);
@ -701,7 +701,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
} }
pc.parity_chunks = 1; pc.parity_chunks = 1;
} }
if (pc.scheme == POOL_SCHEME_JERASURE && if (pc.scheme == POOL_SCHEME_EC &&
(pc.parity_chunks < 1 || pc.parity_chunks > pc.pg_size-2)) (pc.parity_chunks < 1 || pc.parity_chunks > pc.pg_size-2))
{ {
fprintf(stderr, "Pool %u has invalid parity_chunks (must be between 1 and pg_size-2), skipping pool\n", pool_id); fprintf(stderr, "Pool %u has invalid parity_chunks (must be between 1 and pg_size-2), skipping pool\n", pool_id);
@ -710,7 +710,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
// PG MinSize // PG MinSize
pc.pg_minsize = pool_item.second["pg_minsize"].uint64_value(); pc.pg_minsize = pool_item.second["pg_minsize"].uint64_value();
if (pc.pg_minsize < 1 || pc.pg_minsize > pc.pg_size || if (pc.pg_minsize < 1 || pc.pg_minsize > pc.pg_size ||
(pc.scheme == POOL_SCHEME_XOR || pc.scheme == POOL_SCHEME_JERASURE) && (pc.scheme == POOL_SCHEME_XOR || pc.scheme == POOL_SCHEME_EC) &&
pc.pg_minsize < (pc.pg_size-pc.parity_chunks)) pc.pg_minsize < (pc.pg_size-pc.parity_chunks))
{ {
fprintf(stderr, "Pool %u has invalid pg_minsize, skipping pool\n", pool_id); fprintf(stderr, "Pool %u has invalid pg_minsize, skipping pool\n", pool_id);
@ -845,28 +845,8 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
{ {
history_set.push_back(pg_osd.uint64_value()); history_set.push_back(pg_osd.uint64_value());
} }
pg_cfg.target_history.push_back((pg_history_set_t){ .osd_set = history_set });
}
// Newer format with epochs
for (auto hist_item: value["osd_set_epochs"].array_items())
{
pg_history_set_t history_set;
history_set.min_epoch = hist_item["min_epoch"].uint64_value();
history_set.max_epoch = hist_item["max_epoch"].uint64_value();
if (history_set.max_epoch < history_set.min_epoch)
{
history_set.max_epoch = 0;
history_set.min_epoch = 0;
}
for (auto pg_osd: hist_item["osd_set"].array_items())
{
history_set.osd_set.push_back(pg_osd.uint64_value());
}
if (history_set.max_epoch || history_set.osd_set.size())
{
pg_cfg.target_history.push_back(history_set); pg_cfg.target_history.push_back(history_set);
} }
}
// Include these additional OSDs when peering the PG // Include these additional OSDs when peering the PG
for (auto pg_osd: value["all_peers"].array_items()) for (auto pg_osd: value["all_peers"].array_items())
{ {

View File

@ -26,7 +26,7 @@ struct pg_config_t
bool exists; bool exists;
osd_num_t primary; osd_num_t primary;
std::vector<osd_num_t> target_set; std::vector<osd_num_t> target_set;
std::vector<pg_history_set_t> target_history; std::vector<std::vector<osd_num_t>> target_history;
std::vector<osd_num_t> all_peers; std::vector<osd_num_t> all_peers;
bool pause; bool pause;
osd_num_t cur_primary; osd_num_t cur_primary;

View File

@ -758,3 +758,21 @@ static std::string trim(const std::string & in)
int end = in.find_last_not_of(" \n\r\t"); int end = in.find_last_not_of(" \n\r\t");
return in.substr(begin, end+1-begin); return in.substr(begin, end+1-begin);
} }
bool json_is_true(const json11::Json & val)
{
if (val.is_string())
return val == "true" || val == "yes" || val == "1";
return val.bool_value();
}
bool json_is_false(const json11::Json & val)
{
if (val.is_string())
return val.string_value() == "false" || val.string_value() == "no" || val.string_value() == "0";
if (val.is_number())
return val.number_value() == 0;
if (val.is_bool())
return !val.bool_value();
return false;
}

View File

@ -52,3 +52,6 @@ void http_close(http_co_t *co);
// Utils // Utils
uint64_t stoull_full(const std::string & str, int base = 10); uint64_t stoull_full(const std::string & str, int base = 10);
std::string strtolower(const std::string & in); std::string strtolower(const std::string & in);
// FIXME: move to json11
bool json_is_true(const json11::Json & val);
bool json_is_false(const json11::Json & val);

View File

@ -92,10 +92,7 @@ void nfs_proxy_t::run(json11::Json cfg)
if (bind_address == "") if (bind_address == "")
bind_address = "0.0.0.0"; bind_address = "0.0.0.0";
default_pool = cfg["pool"].as_string(); default_pool = cfg["pool"].as_string();
portmap_enabled = cfg.object_items().find("portmap") == cfg.object_items().end() || portmap_enabled = !json_is_false(cfg["portmap"]);
cfg["portmap"].uint64_value() ||
cfg["portmap"].string_value() == "yes" ||
cfg["portmap"].string_value() == "true";
nfs_port = cfg["port"].uint64_value() & 0xffff; nfs_port = cfg["port"].uint64_value() & 0xffff;
if (!nfs_port) if (!nfs_port)
nfs_port = 2049; nfs_port = 2049;

View File

@ -54,7 +54,7 @@ osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
autosync_writes = max_autosync; autosync_writes = max_autosync;
} }
if (this->config["osd_memlock"] == "true" || this->config["osd_memlock"] == "1" || this->config["osd_memlock"] == "yes") if (json_is_true(this->config["osd_memlock"]))
{ {
// Lock all OSD memory if requested // Lock all OSD memory if requested
if (mlockall(MCL_CURRENT|MCL_FUTURE if (mlockall(MCL_CURRENT|MCL_FUTURE
@ -127,11 +127,11 @@ void osd_t::parse_config(const json11::Json & config)
etcd_report_interval = config["etcd_report_interval"].uint64_value(); etcd_report_interval = config["etcd_report_interval"].uint64_value();
if (etcd_report_interval <= 0) if (etcd_report_interval <= 0)
etcd_report_interval = 5; etcd_report_interval = 5;
readonly = config["readonly"] == "true" || config["readonly"] == "1" || config["readonly"] == "yes"; readonly = json_is_true(config["readonly"]);
run_primary = config["run_primary"] != "false" && config["run_primary"] != "0" && config["run_primary"] != "no"; run_primary = !json_is_false(config["run_primary"]);
no_rebalance = config["no_rebalance"] == "true" || config["no_rebalance"] == "1" || config["no_rebalance"] == "yes"; no_rebalance = json_is_true(config["no_rebalance"]);
no_recovery = config["no_recovery"] == "true" || config["no_recovery"] == "1" || config["no_recovery"] == "yes"; no_recovery = json_is_true(config["no_recovery"]);
allow_test_ops = config["allow_test_ops"] == "true" || config["allow_test_ops"] == "1" || config["allow_test_ops"] == "yes"; allow_test_ops = json_is_true(config["allow_test_ops"]);
if (config["immediate_commit"] == "all") if (config["immediate_commit"] == "all")
immediate_commit = IMMEDIATE_ALL; immediate_commit = IMMEDIATE_ALL;
else if (config["immediate_commit"] == "small") else if (config["immediate_commit"] == "small")
@ -168,6 +168,9 @@ void osd_t::parse_config(const json11::Json & config)
slow_log_interval = config["slow_log_interval"].uint64_value(); slow_log_interval = config["slow_log_interval"].uint64_value();
if (!slow_log_interval) if (!slow_log_interval)
slow_log_interval = 10; slow_log_interval = 10;
inode_vanish_time = config["inode_vanish_time"].uint64_value();
if (!inode_vanish_time)
inode_vanish_time = 60;
} }
void osd_t::bind_socket() void osd_t::bind_socket()

View File

@ -113,6 +113,7 @@ class osd_t
int autosync_writes = DEFAULT_AUTOSYNC_WRITES; int autosync_writes = DEFAULT_AUTOSYNC_WRITES;
int recovery_queue_depth = DEFAULT_RECOVERY_QUEUE; int recovery_queue_depth = DEFAULT_RECOVERY_QUEUE;
int recovery_sync_batch = DEFAULT_RECOVERY_BATCH; int recovery_sync_batch = DEFAULT_RECOVERY_BATCH;
int inode_vanish_time = 60;
int log_level = 0; int log_level = 0;
// cluster state // cluster state
@ -165,6 +166,7 @@ class osd_t
// op statistics // op statistics
osd_op_stats_t prev_stats; osd_op_stats_t prev_stats;
std::map<uint64_t, inode_stats_t> inode_stats; std::map<uint64_t, inode_stats_t> inode_stats;
std::map<uint64_t, timespec> vanishing_inodes;
const char* recovery_stat_names[2] = { "degraded", "misplaced" }; const char* recovery_stat_names[2] = { "degraded", "misplaced" };
uint64_t recovery_stat_count[2][2] = {}; uint64_t recovery_stat_count[2][2] = {};
uint64_t recovery_stat_bytes[2][2] = {}; uint64_t recovery_stat_bytes[2][2] = {};

View File

@ -186,7 +186,8 @@ void osd_t::report_statistics()
json11::Json::object inode_space; json11::Json::object inode_space;
json11::Json::object last_stat; json11::Json::object last_stat;
pool_id_t last_pool = 0; pool_id_t last_pool = 0;
for (auto kv: bs->get_inode_space_stats()) auto & bs_inode_space = bs->get_inode_space_stats();
for (auto kv: bs_inode_space)
{ {
pool_id_t pool_id = INODE_POOL(kv.first); pool_id_t pool_id = INODE_POOL(kv.first);
uint64_t only_inode_num = INODE_NO_POOL(kv.first); uint64_t only_inode_num = INODE_NO_POOL(kv.first);
@ -204,8 +205,26 @@ void osd_t::report_statistics()
last_stat = json11::Json::object(); last_stat = json11::Json::object();
last_pool = 0; last_pool = 0;
json11::Json::object inode_ops; json11::Json::object inode_ops;
for (auto kv: inode_stats) timespec tv_now;
for (auto st_it = inode_stats.begin(); st_it != inode_stats.end(); )
{ {
auto & kv = *st_it;
if (!bs_inode_space[kv.first])
{
// Is it an empty inode?
if (!tv_now.tv_sec)
clock_gettime(CLOCK_REALTIME, &tv_now);
auto & tv_van = vanishing_inodes[kv.first];
if (!tv_van.tv_sec)
tv_van = tv_now;
else if (tv_van.tv_sec < tv_now.tv_sec-inode_vanish_time)
{
// Inode vanished <inode_vanish_time> seconds ago, remove it from stats
vanishing_inodes.erase(kv.first);
inode_stats.erase(st_it++);
continue;
}
}
pool_id_t pool_id = INODE_POOL(kv.first); pool_id_t pool_id = INODE_POOL(kv.first);
uint64_t only_inode_num = (kv.first & (((uint64_t)1 << (64-POOL_ID_BITS)) - 1)); uint64_t only_inode_num = (kv.first & (((uint64_t)1 << (64-POOL_ID_BITS)) - 1));
if (!last_pool || pool_id != last_pool) if (!last_pool || pool_id != last_pool)
@ -232,6 +251,7 @@ void osd_t::report_statistics()
{ "bytes", kv.second.op_bytes[INODE_STATS_DELETE] }, { "bytes", kv.second.op_bytes[INODE_STATS_DELETE] },
} }, } },
}; };
st_it++;
} }
if (last_pool) if (last_pool)
inode_ops[std::to_string(last_pool)] = last_stat; inode_ops[std::to_string(last_pool)] = last_stat;
@ -333,9 +353,6 @@ void osd_t::on_change_pg_history_hook(pool_id_t pool_id, pg_num_t pg_num)
{ {
oid = op.first; oid = op.first;
first = false; first = false;
if (op.second->req.hdr.opcode == OSD_OP_DELETE)
continue_primary_del(op.second);
else
continue_primary_write(op.second); continue_primary_write(op.second);
} }
} }
@ -373,7 +390,11 @@ void osd_t::acquire_lease()
etcd_lease_id = data["ID"].string_value(); etcd_lease_id = data["ID"].string_value();
create_osd_state(); create_osd_state();
}); });
printf("[OSD %lu] reporting to etcd at %s every %d seconds\n", this->osd_num, config["etcd_address"].string_value().c_str(), etcd_report_interval); printf(
"[OSD %lu] reporting to etcd at %s every %d seconds\n", this->osd_num,
(config["etcd_address"].is_string() ? config["etcd_address"].string_value() : config["etcd_address"].dump()).c_str(),
etcd_report_interval
);
tfd->set_timer(etcd_report_interval*1000, true, [this](int timer_id) tfd->set_timer(etcd_report_interval*1000, true, [this](int timer_id)
{ {
renew_lease(); renew_lease();
@ -611,7 +632,7 @@ void osd_t::apply_pg_config()
} }
for (auto & hist_item: pg_cfg.target_history) for (auto & hist_item: pg_cfg.target_history)
{ {
for (auto pg_osd: hist_item.osd_set) for (auto pg_osd: hist_item)
{ {
if (pg_osd != 0) if (pg_osd != 0)
{ {
@ -679,9 +700,9 @@ void osd_t::apply_pg_config()
.all_peers = std::vector<osd_num_t>(all_peers.begin(), all_peers.end()), .all_peers = std::vector<osd_num_t>(all_peers.begin(), all_peers.end()),
.target_set = pg_cfg.target_set, .target_set = pg_cfg.target_set,
}; };
if (pg.scheme == POOL_SCHEME_JERASURE) if (pg.scheme == POOL_SCHEME_EC)
{ {
use_jerasure(pg.pg_size, pg.pg_data_size, true); use_ec(pg.pg_size, pg.pg_data_size, true);
} }
this->pg_state_dirty.insert({ .pool_id = pool_id, .pg_num = pg_num }); this->pg_state_dirty.insert({ .pool_id = pool_id, .pg_num = pg_num });
pg.print_state(); pg.print_state();
@ -802,40 +823,11 @@ void osd_t::report_pg_states()
// Prevent race conditions (for the case when the monitor is updating this key at the same time) // Prevent race conditions (for the case when the monitor is updating this key at the same time)
pg.history_changed = false; pg.history_changed = false;
std::string history_key = base64_encode(st_cli.etcd_prefix+"/pg/history/"+std::to_string(pg.pool_id)+"/"+std::to_string(pg.pg_num)); std::string history_key = base64_encode(st_cli.etcd_prefix+"/pg/history/"+std::to_string(pg.pool_id)+"/"+std::to_string(pg.pg_num));
json11::Json::array target_history;
for (auto & pgh: pg.target_history)
{
target_history.push_back(json11::Json::object {
{ "osd_set", pgh.osd_set },
{ "min_epoch", pgh.min_epoch },
{ "max_epoch", pgh.max_epoch },
});
}
std::vector<osd_num_t> all_peers;
for (auto peer_osd: pg.all_peers)
{
bool found = false;
for (auto target_peer: pg.target_set)
{
if (target_peer == peer_osd)
{
found = true;
break;
}
}
if (!found)
{
all_peers.push_back(peer_osd);
}
}
json11::Json::object history_value = { json11::Json::object history_value = {
{ "epoch", pg.epoch }, { "epoch", pg.epoch },
{ "osd_set_epochs", target_history }, { "all_peers", pg.all_peers },
{ "osd_sets", pg.target_history },
}; };
if (all_peers.size())
{
history_value["all_peers"] = all_peers;
}
checks.push_back(json11::Json::object { checks.push_back(json11::Json::object {
{ "target", "MOD" }, { "target", "MOD" },
{ "key", history_key }, { "key", history_key },
@ -922,9 +914,9 @@ void osd_t::report_pg_states()
{ {
// Forget offline PGs after reporting their state // Forget offline PGs after reporting their state
// (if the state wasn't changed again) // (if the state wasn't changed again)
if (pg_it->second.scheme == POOL_SCHEME_JERASURE) if (pg_it->second.scheme == POOL_SCHEME_EC)
{ {
use_jerasure(pg_it->second.pg_size, pg_it->second.pg_data_size, false); use_ec(pg_it->second.pg_size, pg_it->second.pg_data_size, false);
} }
this->pgs.erase(pg_it); this->pgs.erase(pg_it);
} }

View File

@ -268,25 +268,6 @@ bool osd_t::pick_next_recovery(osd_recovery_op_t &op)
void osd_t::submit_recovery_op(osd_recovery_op_t *op) void osd_t::submit_recovery_op(osd_recovery_op_t *op)
{ {
// Check if the object is deleted
bool is_deleted = false;
pool_id_t pool_id = INODE_POOL(op->oid.inode);
auto pool_cfg_it = st_cli.pool_config.find(pool_id);
if (pool_cfg_it != st_cli.pool_config.end())
{
pg_num_t pg_num = (op->oid.stripe/pool_cfg_it->second.pg_stripe_size) % pg_counts[pool_id] + 1; // like map_to_pg()
auto pg_it = pgs.find({ .pool_id = pool_id, .pg_num = pg_num });
if (pg_it != pgs.end())
{
pg_osd_set_state_t *object_state;
get_object_osd_set(pg_it->second, op->oid, pg_it->second.cur_set.data(), &object_state);
if (object_state && (object_state->state & OBJ_DELETED))
{
// Object is deleted, but not from all OSDs - delete remaining copies
is_deleted = true;
}
}
}
op->osd_op = new osd_op_t(); op->osd_op = new osd_op_t();
op->osd_op->op_type = OSD_OP_OUT; op->osd_op->op_type = OSD_OP_OUT;
op->osd_op->req = (osd_any_op_t){ op->osd_op->req = (osd_any_op_t){
@ -294,7 +275,7 @@ void osd_t::submit_recovery_op(osd_recovery_op_t *op)
.header = { .header = {
.magic = SECONDARY_OSD_OP_MAGIC, .magic = SECONDARY_OSD_OP_MAGIC,
.id = 1, .id = 1,
.opcode = (uint64_t)(is_deleted ? OSD_OP_DELETE : OSD_OP_WRITE), .opcode = OSD_OP_WRITE,
}, },
.inode = op->oid.inode, .inode = op->oid.inode,
.offset = op->oid.stripe, .offset = op->oid.stripe,

View File

@ -3,11 +3,9 @@
#pragma once #pragma once
#include <vector>
#define POOL_SCHEME_REPLICATED 1 #define POOL_SCHEME_REPLICATED 1
#define POOL_SCHEME_XOR 2 #define POOL_SCHEME_XOR 2
#define POOL_SCHEME_JERASURE 3 #define POOL_SCHEME_EC 3
#define POOL_ID_MAX 0x10000 #define POOL_ID_MAX 0x10000
#define POOL_ID_BITS 16 #define POOL_ID_BITS 16
#define INODE_POOL(inode) (pool_id_t)((inode) >> (64 - POOL_ID_BITS)) #define INODE_POOL(inode) (pool_id_t)((inode) >> (64 - POOL_ID_BITS))
@ -30,9 +28,3 @@ inline bool operator < (const pool_pg_num_t & a, const pool_pg_num_t & b)
{ {
return a.pool_id < b.pool_id || a.pool_id == b.pool_id && a.pg_num < b.pg_num; return a.pool_id < b.pool_id || a.pool_id == b.pool_id && a.pg_num < b.pg_num;
} }
struct pg_history_set_t
{
std::vector<osd_num_t> osd_set;
uint64_t min_epoch, max_epoch;
};

View File

@ -30,13 +30,19 @@
#define OSD_OP_PING 15 #define OSD_OP_PING 15
#define OSD_OP_SEC_READ_BMP 16 #define OSD_OP_SEC_READ_BMP 16
#define OSD_OP_MAX 16 #define OSD_OP_MAX 16
// Alignment & limit for read/write operations
#ifndef MEM_ALIGNMENT
#define MEM_ALIGNMENT 512
#endif
#define OSD_RW_MAX 64*1024*1024 #define OSD_RW_MAX 64*1024*1024
#define OSD_PROTOCOL_VERSION 1 #define OSD_PROTOCOL_VERSION 1
// Memory alignment for direct I/O (usually 512 bytes)
#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
// common request and reply headers // common request and reply headers
struct __attribute__((__packed__)) osd_op_header_t struct __attribute__((__packed__)) osd_op_header_t
{ {

View File

@ -228,7 +228,7 @@ void osd_t::start_pg_peering(pg_t & pg)
for (auto & history_set: pg.target_history) for (auto & history_set: pg.target_history)
{ {
bool found = true; bool found = true;
for (auto history_osd: history_set.osd_set) for (auto history_osd: history_set)
{ {
if (history_osd != 0) if (history_osd != 0)
{ {
@ -539,51 +539,28 @@ void osd_t::finish_stop_pg(pg_t & pg)
report_pg_state(pg); report_pg_state(pg);
} }
static int count_nonzero_osds(const std::vector<osd_num_t> & v)
{
int n = 0;
for (auto & osd_num: v)
{
if (osd_num != 0)
{
n++;
}
}
return n;
}
void osd_t::report_pg_state(pg_t & pg) void osd_t::report_pg_state(pg_t & pg)
{ {
pg.print_state(); pg.print_state();
this->pg_state_dirty.insert({ .pool_id = pg.pool_id, .pg_num = pg.pg_num }); this->pg_state_dirty.insert({ .pool_id = pg.pool_id, .pg_num = pg.pg_num });
if ((pg.state == PG_ACTIVE || pg.state == (PG_ACTIVE|PG_LEFT_ON_DEAD)) && if (pg.state == PG_ACTIVE && (pg.target_history.size() > 0 || pg.all_peers.size() > pg.target_set.size()))
(pg.target_history.size() != 1 ||
pg.target_history[0].osd_set != pg.target_set ||
pg.target_history[0].min_epoch != 0 ||
pg.target_history[0].max_epoch != pg.epoch ||
pg.all_peers.size() > count_nonzero_osds(pg.target_set)))
{ {
// Clear history of active+clean PGs // Clear history of active+clean PGs
pg.history_changed = true; pg.history_changed = true;
pg.target_history.clear(); pg.target_history.clear();
pg.target_history.push_back((pg_history_set_t){ pg.all_peers = pg.target_set;
.osd_set = pg.cur_set, pg.cur_peers = pg.target_set;
.min_epoch = 0,
.max_epoch = pg.epoch,
});
if (pg.state == PG_ACTIVE)
{
pg.all_peers.clear();
for (auto pg_osd: pg.target_set)
{
if (pg_osd)
pg.all_peers.push_back(pg_osd);
} }
} else if (pg.state == (PG_ACTIVE|PG_LEFT_ON_DEAD))
else
{ {
// Clear history of active+left_on_dead PGs, but leave dead OSDs in all_peers // Clear history of active+left_on_dead PGs, but leave dead OSDs in all_peers
std::set<osd_num_t> dead_peers(pg.all_peers.begin(), pg.all_peers.end()); pg.history_changed = true;
pg.target_history.clear();
std::set<osd_num_t> dead_peers;
for (auto pg_osd: pg.all_peers)
{
dead_peers.insert(pg_osd);
}
for (auto pg_osd: pg.cur_peers) for (auto pg_osd: pg.cur_peers)
{ {
dead_peers.erase(pg_osd); dead_peers.erase(pg_osd);
@ -591,64 +568,20 @@ void osd_t::report_pg_state(pg_t & pg)
for (auto pg_osd: pg.target_set) for (auto pg_osd: pg.target_set)
{ {
if (pg_osd) if (pg_osd)
{
dead_peers.insert(pg_osd); dead_peers.insert(pg_osd);
} }
}
pg.all_peers.clear(); pg.all_peers.clear();
pg.all_peers.insert(pg.all_peers.begin(), dead_peers.begin(), dead_peers.end()); pg.all_peers.insert(pg.all_peers.begin(), dead_peers.begin(), dead_peers.end());
}
pg.cur_peers.clear(); pg.cur_peers.clear();
for (auto pg_osd: pg.target_set) for (auto pg_osd: pg.target_set)
{ {
if (pg_osd) if (pg_osd)
{
pg.cur_peers.push_back(pg_osd); pg.cur_peers.push_back(pg_osd);
} }
} }
if (pg.history_changed)
{
bool epoch_already_reported = false;
int max_epoch_pos = -1;
for (int i = pg.target_history.size()-1; i >= 0; i--)
{
if (pg.target_history[i].min_epoch > pg.epoch)
{
printf("[PG %u/%u] Invalid PG history: there is an entry with min_epoch (%lu) > current epoch (%lu)\n",
pg.pool_id, pg.pg_num, pg.target_history[i].min_epoch, pg.epoch);
force_stop(1);
return;
}
if (max_epoch_pos < 0 || pg.target_history[i].max_epoch > pg.target_history[max_epoch_pos].max_epoch)
{
max_epoch_pos = i;
}
if (pg.target_history[i].min_epoch <= pg.epoch &&
pg.target_history[i].max_epoch >= pg.epoch)
{
if (pg.target_history[i].osd_set != pg.cur_set)
{
printf("[PG %u/%u] Invalid target_history: epoch %lu has another OSD set already registered\n", pg.pool_id, pg.pg_num, pg.epoch);
force_stop(1);
return;
}
// Already reported
epoch_already_reported = true;
break;
}
}
if (!epoch_already_reported)
{
if (max_epoch_pos >= 0 && pg.target_history[max_epoch_pos].osd_set == pg.cur_set)
{
pg.target_history[max_epoch_pos].max_epoch = pg.epoch;
}
else
{
pg.target_history.push_back((pg_history_set_t){
.osd_set = pg.cur_set,
.min_epoch = pg.epoch,
.max_epoch = pg.epoch,
});
}
}
} }
if (pg.state == PG_OFFLINE && !this->pg_config_applied) if (pg.state == PG_OFFLINE && !this->pg_config_applied)
{ {

View File

@ -52,7 +52,6 @@ struct pg_obj_state_check_t
void walk(); void walk();
void start_object(); void start_object();
void recheck_version_osd_set();
void handle_version(); void handle_version();
void finish_object(); void finish_object();
}; };
@ -85,19 +84,27 @@ void pg_obj_state_check_t::walk()
pg->state = PG_INCOMPLETE | PG_HAS_INVALID; pg->state = PG_INCOMPLETE | PG_HAS_INVALID;
return; return;
} }
// Activate PG
if (pg->pg_cursize < pg->pg_size) if (pg->pg_cursize < pg->pg_size)
{ {
// History will be reported on first write // Report PG history and activate
pg->state |= PG_DEGRADED | PG_PEERED; pg->state |= PG_DEGRADED | PG_PEERED;
std::vector<osd_num_t> history_set;
for (auto peer_osd: pg->cur_set)
{
if (peer_osd != 0)
{
history_set.push_back(peer_osd);
}
}
pg->target_history.push_back(history_set);
pg->history_changed = true;
} }
else else
{ {
// Just activate
pg->state |= PG_ACTIVE; pg->state |= PG_ACTIVE;
// Clear history
pg->history_changed = true;
} }
if (pg->cur_peers.size() < pg->all_peers.size()) if (pg->state == PG_ACTIVE && pg->cur_peers.size() < pg->all_peers.size())
{ {
pg->state |= PG_LEFT_ON_DEAD; pg->state |= PG_LEFT_ON_DEAD;
} }
@ -114,82 +121,13 @@ void pg_obj_state_check_t::start_object()
n_unstable = n_invalid = 0; n_unstable = n_invalid = 0;
} }
void pg_obj_state_check_t::recheck_version_osd_set()
{
uint64_t epoch = (last_ver >> (64-PG_EPOCH_BITS));
if (!pg->epoch_sizes_differ && n_copies >= pg->pg_size)
{
// Enough copies
return;
}
auto epoch_it = pg->target_by_epoch.lower_bound(epoch);
if (epoch_it == pg->target_by_epoch.end() || epoch_it->second.min_epoch > epoch)
{
// Epoch info not found
return;
}
if (pg->epoch_sizes_differ && n_copies >= epoch_it->second.osd_set.size())
{
// For the (unlikely) case of PG size change - enough copies
return;
}
// Recheck version against the OSD set corresponding to epoch if it's known
if (epoch_it != pg->target_by_epoch.end() && epoch_it->second.min_epoch <= epoch)
{
for (int j = 0; j < epoch_it->second.osd_set.size(); j++)
{
osd_num_t cur_osd = epoch_it->second.osd_set[j];
bool found = false;
for (int i = ver_start; i < ver_end; i++)
{
if (cur_osd == list[i].osd_num)
{
found = true;
break;
}
}
if (!found)
{
// Check if a newer version is present on the same OSD and masks the older one
// It happens for overwritten replicas in the following case:
// Version 1 is present on OSD 1,2,3
// Client tries to write Version 2
// OSD 3 succeeds to write Version 2, others don't. OSD 3 crashes, then starts again
// OSD 1 sees: version 1 on OSD 1,2 and version 2 on OSD 3
// (version 1 on OSD 3 is already masked/removed)
// Version 1 is not present on a full set, but it must not be removed
if (replicated)
{
for (int i = obj_start; i < ver_start; i++)
{
if (cur_osd == list[i].osd_num)
{
found = true;
break;
}
}
}
if (!found)
{
// Object is missing from one of the OSDs of that set.
// This means it's deleted or moved and we can safely drop this version.
target_ver = 0;
break;
}
}
}
}
}
void pg_obj_state_check_t::handle_version() void pg_obj_state_check_t::handle_version()
{ {
if (!target_ver && last_ver != list[list_pos].version && (n_stable > 0 || n_roles >= pg->pg_data_size)) if (!target_ver && last_ver != list[list_pos].version && (n_stable > 0 || n_roles >= pg->pg_data_size))
{ {
// Version is either stable or recoverable // Version is either stable or recoverable
ver_end = list_pos;
target_ver = last_ver; target_ver = last_ver;
// Skip versions that are not present on any of OSDs for the corresponding PG epoch ver_end = list_pos;
recheck_version_osd_set();
} }
if (!target_ver) if (!target_ver)
{ {
@ -253,8 +191,6 @@ void pg_obj_state_check_t::finish_object()
// Version is either stable or recoverable // Version is either stable or recoverable
target_ver = last_ver; target_ver = last_ver;
ver_end = list_pos; ver_end = list_pos;
// Skip versions that are not present on any of OSDs for the corresponding PG epoch
recheck_version_osd_set();
} }
obj_end = list_pos; obj_end = list_pos;
// Remember the decision // Remember the decision
@ -308,23 +244,11 @@ void pg_obj_state_check_t::finish_object()
} }
} }
} }
if (!target_ver && (n_unstable >= obj_end-obj_start)) if (!target_ver)
{ {
return; return;
} }
if (!target_ver) if (!replicated && n_roles < pg->pg_data_size)
{
// Object is present, but should not be :) i.e. it's a deleted object that reappeared
if (log_level > 1)
{
printf("Object is deleted: %lx:%lx version=%lu/%lu\n", oid.inode, oid.stripe, target_ver, max_ver);
}
state = OBJ_DELETED;
pg->state = pg->state | PG_HAS_MISPLACED;
// To record all versions as outdated:
ver_end = obj_start;
}
else if (!replicated && n_roles < pg->pg_data_size)
{ {
if (log_level > 1) if (log_level > 1)
{ {
@ -352,7 +276,7 @@ void pg_obj_state_check_t::finish_object()
pg->state = pg->state | PG_HAS_MISPLACED; pg->state = pg->state | PG_HAS_MISPLACED;
} }
if (log_level > 1 && (state & (OBJ_INCOMPLETE | OBJ_DEGRADED)) || if (log_level > 1 && (state & (OBJ_INCOMPLETE | OBJ_DEGRADED)) ||
log_level > 2 && (state & (OBJ_MISPLACED | OBJ_DELETED))) log_level > 2 && (state & OBJ_MISPLACED))
{ {
for (int i = obj_start; i < obj_end; i++) for (int i = obj_start; i < obj_end; i++)
{ {
@ -361,9 +285,9 @@ void pg_obj_state_check_t::finish_object()
} }
} }
pg->total_count++; pg->total_count++;
osd_set.clear(); if (state != 0 || ver_end < obj_end)
if (target_ver != 0 && (state != 0 || ver_end < obj_end))
{ {
osd_set.clear();
for (int i = ver_start; i < ver_end; i++) for (int i = ver_start; i < ver_end; i++)
{ {
osd_set.push_back((pg_obj_loc_t){ osd_set.push_back((pg_obj_loc_t){
@ -386,8 +310,7 @@ void pg_obj_state_check_t::finish_object()
break; break;
} }
} }
if (j >= osd_set.size() && ((state & OBJ_DELETED) || if (j >= osd_set.size() && pg->cur_set[list[i].oid.stripe & STRIPE_MASK] != list[i].osd_num)
pg->cur_set[list[i].oid.stripe & STRIPE_MASK] != list[i].osd_num))
{ {
osd_set.push_back((pg_obj_loc_t){ osd_set.push_back((pg_obj_loc_t){
.role = (list[i].oid.stripe & STRIPE_MASK), .role = (list[i].oid.stripe & STRIPE_MASK),
@ -402,11 +325,7 @@ void pg_obj_state_check_t::finish_object()
} }
} }
} }
if (state & OBJ_DELETED) if (target_ver < max_ver)
{
pg->ver_override[oid] = max_ver;
}
else if (target_ver < max_ver)
{ {
pg->ver_override[oid] = target_ver; pg->ver_override[oid] = target_ver;
} }
@ -460,7 +379,6 @@ void pg_obj_state_check_t::finish_object()
} }
else else
{ {
assert(it->second.state == state);
it->second.object_count++; it->second.object_count++;
} }
if (state & OBJ_INCOMPLETE) if (state & OBJ_INCOMPLETE)
@ -481,34 +399,6 @@ void pg_obj_state_check_t::finish_object()
// FIXME: Write at least some tests for this function // FIXME: Write at least some tests for this function
void pg_t::calc_object_states(int log_level) void pg_t::calc_object_states(int log_level)
{ {
// Calculate intersections of target_history with cur_peers
for (auto & history_item: target_history)
{
if (history_item.max_epoch)
{
pg_history_set_t & set_copy = target_by_epoch[history_item.max_epoch];
set_copy.min_epoch = history_item.min_epoch;
set_copy.max_epoch = history_item.max_epoch;
for (int i = 0; i < history_item.osd_set.size(); i++)
{
if (history_item.osd_set[i] != 0)
{
for (int j = 0; j < cur_set.size(); j++)
{
if (cur_set[j] == history_item.osd_set[i])
{
set_copy.osd_set.push_back(history_item.osd_set[i]);
break;
}
}
}
}
if (set_copy.osd_set.size() != pg_size)
{
epoch_sizes_differ = true;
}
}
}
// Copy all object lists into one array // Copy all object lists into one array
pg_obj_state_check_t st; pg_obj_state_check_t st;
st.log_level = log_level; st.log_level = log_level;
@ -545,18 +435,10 @@ void pg_t::calc_object_states(int log_level)
std::sort(st.list.begin(), st.list.end()); std::sort(st.list.begin(), st.list.end());
// Walk over it and check object states // Walk over it and check object states
st.walk(); st.walk();
target_by_epoch.clear(); if (this->state & (PG_DEGRADED|PG_LEFT_ON_DEAD))
if (this->state != PG_ACTIVE)
{ {
assert(epoch != (((uint64_t)1 << PG_EPOCH_BITS)-1)); assert(epoch != (((uint64_t)1 << PG_EPOCH_BITS)-1));
epoch++; epoch++;
for (auto & pgh: target_history)
{
if (epoch <= pgh.max_epoch)
{
epoch = pgh.max_epoch+1;
}
}
} }
} }

View File

@ -89,9 +89,7 @@ struct pg_t
// epoch number - should increase with each non-clean activation of the PG // epoch number - should increase with each non-clean activation of the PG
uint64_t epoch = 0, reported_epoch = 0; uint64_t epoch = 0, reported_epoch = 0;
// target history and all potential peers // target history and all potential peers
std::vector<pg_history_set_t> target_history; std::vector<std::vector<osd_num_t>> target_history;
std::map<uint64_t, pg_history_set_t> target_by_epoch;
bool epoch_sizes_differ = false;
std::vector<osd_num_t> all_peers; std::vector<osd_num_t> all_peers;
bool history_changed = false; bool history_changed = false;
// peer list from the last peering event // peer list from the last peering event

View File

@ -199,21 +199,6 @@ void osd_t::continue_primary_read(osd_op_t *cur_op)
{ {
// PG may be degraded or have misplaced objects // PG may be degraded or have misplaced objects
op_data->prev_set = get_object_osd_set(pg, op_data->oid, pg.cur_set.data(), &op_data->object_state); op_data->prev_set = get_object_osd_set(pg, op_data->oid, pg.cur_set.data(), &op_data->object_state);
if (op_data->object_state && (op_data->object_state->state & OBJ_DELETED))
{
// Object is deleted, just return zeroes
cur_op->reply.rw.version = 0;
cur_op->reply.rw.bitmap_len = op_data->pg_data_size * clean_entry_bitmap_size;
uint64_t zero_len = cur_op->reply.rw.bitmap_len + cur_op->req.rw.len;
while (zero_len >= 0)
{
uint64_t cur_zero_len = zero_buffer_size > zero_len ? zero_len : zero_buffer_size;
cur_op->iov.push_back(zero_buffer, cur_zero_len);
zero_len -= cur_zero_len;
}
finish_op(cur_op, cur_op->req.rw.len);
return;
}
} }
if (pg.state == PG_ACTIVE || op_data->scheme == POOL_SCHEME_REPLICATED) if (pg.state == PG_ACTIVE || op_data->scheme == POOL_SCHEME_REPLICATED)
{ {
@ -256,9 +241,9 @@ resume_2:
{ {
reconstruct_stripes_xor(stripes, op_data->pg_size, clean_entry_bitmap_size); reconstruct_stripes_xor(stripes, op_data->pg_size, clean_entry_bitmap_size);
} }
else if (op_data->scheme == POOL_SCHEME_JERASURE) else if (op_data->scheme == POOL_SCHEME_EC)
{ {
reconstruct_stripes_jerasure(stripes, op_data->pg_size, op_data->pg_data_size, clean_entry_bitmap_size); reconstruct_stripes_ec(stripes, op_data->pg_size, op_data->pg_data_size, clean_entry_bitmap_size);
} }
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len); cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
for (int role = 0; role < op_data->pg_size; role++) for (int role = 0; role < op_data->pg_size; role++)
@ -305,7 +290,7 @@ void osd_t::remove_object_from_state(object_id & oid, pg_osd_set_state_t *object
report_pg_state(pg); report_pg_state(pg);
} }
} }
else if (object_state->state & (OBJ_MISPLACED | OBJ_DELETED)) else if (object_state->state & OBJ_MISPLACED)
{ {
this->misplaced_objects--; this->misplaced_objects--;
pg.misplaced_objects.erase(oid); pg.misplaced_objects.erase(oid);
@ -344,6 +329,12 @@ void osd_t::continue_primary_del(osd_op_t *cur_op)
else if (op_data->st == 4) goto resume_4; else if (op_data->st == 4) goto resume_4;
else if (op_data->st == 5) goto resume_5; else if (op_data->st == 5) goto resume_5;
assert(op_data->st == 0); assert(op_data->st == 0);
// Delete is forbidden even in active PGs if they're also degraded or have previous dead OSDs
if (pg.state & (PG_DEGRADED | PG_LEFT_ON_DEAD))
{
finish_op(cur_op, -EBUSY);
return;
}
if (!check_write_queue(cur_op, pg)) if (!check_write_queue(cur_op, pg))
{ {
return; return;
@ -351,18 +342,11 @@ void osd_t::continue_primary_del(osd_op_t *cur_op)
resume_1: resume_1:
// Determine which OSDs contain this object and delete it // Determine which OSDs contain this object and delete it
op_data->prev_set = get_object_osd_set(pg, op_data->oid, pg.cur_set.data(), &op_data->object_state); op_data->prev_set = get_object_osd_set(pg, op_data->oid, pg.cur_set.data(), &op_data->object_state);
if (op_data->object_state && (op_data->object_state->state & OBJ_DELETED))
{
op_data->fact_ver = pg.ver_override[op_data->oid];
}
else
{
// Submit 1 read to determine the actual version number // Submit 1 read to determine the actual version number
submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op); submit_primary_subops(SUBMIT_RMW_READ, UINT64_MAX, op_data->prev_set, cur_op);
resume_2: resume_2:
op_data->st = 2; op_data->st = 2;
return; return;
}
resume_3: resume_3:
if (op_data->errors > 0) if (op_data->errors > 0)
{ {

View File

@ -110,9 +110,9 @@ resume_1:
{ {
reconstruct_stripes_xor(local_stripes, pg.pg_size, clean_entry_bitmap_size); reconstruct_stripes_xor(local_stripes, pg.pg_size, clean_entry_bitmap_size);
} }
else if (pg.scheme == POOL_SCHEME_JERASURE) else if (pg.scheme == POOL_SCHEME_EC)
{ {
reconstruct_stripes_jerasure(local_stripes, pg.pg_size, pg.pg_data_size, clean_entry_bitmap_size); reconstruct_stripes_ec(local_stripes, pg.pg_size, pg.pg_data_size, clean_entry_bitmap_size);
} }
break; break;
} }
@ -133,12 +133,6 @@ int osd_t::collect_bitmap_requests(osd_op_t *cur_op, pg_t & pg, std::vector<bitm
uint64_t target_version = vo_it != pg.ver_override.end() ? vo_it->second : UINT64_MAX; uint64_t target_version = vo_it != pg.ver_override.end() ? vo_it->second : UINT64_MAX;
pg_osd_set_state_t *object_state; pg_osd_set_state_t *object_state;
uint64_t* cur_set = get_object_osd_set(pg, cur_oid, pg.cur_set.data(), &object_state); uint64_t* cur_set = get_object_osd_set(pg, cur_oid, pg.cur_set.data(), &object_state);
if (object_state && (object_state->state & OBJ_DELETED))
{
// Object is deleted, zero out the bitmap
memset((uint8_t*)op_data->snapshot_bitmaps + chain_num*clean_entry_bitmap_size, 0, clean_entry_bitmap_size);
continue;
}
if (pg.scheme == POOL_SCHEME_REPLICATED) if (pg.scheme == POOL_SCHEME_REPLICATED)
{ {
osd_num_t read_target = 0; osd_num_t read_target = 0;
@ -512,9 +506,9 @@ void osd_t::send_chained_read_results(pg_t & pg, osd_op_t *cur_op)
{ {
reconstruct_stripes_xor(stripes, pg.pg_size, clean_entry_bitmap_size); reconstruct_stripes_xor(stripes, pg.pg_size, clean_entry_bitmap_size);
} }
else if (op_data->scheme == POOL_SCHEME_JERASURE) else if (op_data->scheme == POOL_SCHEME_EC)
{ {
reconstruct_stripes_jerasure(stripes, pg.pg_size, pg.pg_data_size, clean_entry_bitmap_size); reconstruct_stripes_ec(stripes, pg.pg_size, pg.pg_data_size, clean_entry_bitmap_size);
} }
} }
} }

View File

@ -116,19 +116,17 @@ void osd_t::submit_primary_subops(int submit_type, uint64_t op_version, const ui
if (osd_set[role] != 0 && (wr || !rep && stripes[role].read_end != 0)) if (osd_set[role] != 0 && (wr || !rep && stripes[role].read_end != 0))
n_subops++; n_subops++;
} }
if (!n_subops && (submit_type == SUBMIT_RMW_READ || rep) && zero_read >= 0) if (!n_subops && (submit_type == SUBMIT_RMW_READ || rep))
n_subops = 1; n_subops = 1;
else else
zero_read = -1; zero_read = -1;
osd_op_t *subops = new osd_op_t[n_subops];
op_data->fact_ver = 0; op_data->fact_ver = 0;
op_data->done = op_data->errors = 0; op_data->done = op_data->errors = 0;
op_data->n_subops = n_subops; op_data->n_subops = n_subops;
if (n_subops > 0) op_data->subops = subops;
{
op_data->subops = new osd_op_t[n_subops];
int sent = submit_primary_subop_batch(submit_type, op_data->oid.inode, op_version, op_data->stripes, osd_set, cur_op, 0, zero_read); int sent = submit_primary_subop_batch(submit_type, op_data->oid.inode, op_version, op_data->stripes, osd_set, cur_op, 0, zero_read);
assert(sent == n_subops); assert(sent == n_subops);
}
} }
int osd_t::submit_primary_subop_batch(int submit_type, inode_t inode, uint64_t op_version, int osd_t::submit_primary_subop_batch(int submit_type, inode_t inode, uint64_t op_version,

View File

@ -132,9 +132,9 @@ resume_3:
{ {
calc_rmw_parity_xor(op_data->stripes, pg.pg_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size); calc_rmw_parity_xor(op_data->stripes, pg.pg_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size);
} }
else if (pg.scheme == POOL_SCHEME_JERASURE) else if (pg.scheme == POOL_SCHEME_EC)
{ {
calc_rmw_parity_jerasure(op_data->stripes, pg.pg_size, op_data->pg_data_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size); calc_rmw_parity_ec(op_data->stripes, pg.pg_size, op_data->pg_data_size, op_data->prev_set, pg.cur_set.data(), bs_block_size, clean_entry_bitmap_size);
} }
} }
// Send writes // Send writes
@ -154,9 +154,10 @@ resume_3:
if (pg.epoch > pg.reported_epoch) if (pg.epoch > pg.reported_epoch)
{ {
// Report newer epoch before writing // Report newer epoch before writing
// FIXME: We don't have to report all changed PG states here // FIXME: We may report only one PG state here...
this->pg_state_dirty.insert({ .pool_id = pg.pool_id, .pg_num = pg.pg_num });
pg.history_changed = true; pg.history_changed = true;
report_pg_state(pg); report_pg_states();
resume_10: resume_10:
if (pg.epoch > pg.reported_epoch) if (pg.epoch > pg.reported_epoch)
{ {

View File

@ -4,8 +4,13 @@
#include <stdexcept> #include <stdexcept>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <jerasure/reed_sol.h> extern "C" {
#include <reed_sol.h>
#include <jerasure.h> #include <jerasure.h>
#ifdef WITH_ISAL
#include <isa-l/erasure_code.h>
#endif
}
#include <map> #include <map>
#include "allocator.h" #include "allocator.h"
#include "xor.h" #include "xor.h"
@ -147,13 +152,14 @@ inline bool operator < (const reed_sol_erased_t &a, const reed_sol_erased_t &b)
struct reed_sol_matrix_t struct reed_sol_matrix_t
{ {
int refs = 0; int refs = 0;
int *data; int *je_data;
std::map<reed_sol_erased_t, int*> decodings; uint8_t *isal_data;
std::map<reed_sol_erased_t, void*> decodings;
}; };
std::map<uint64_t, reed_sol_matrix_t> matrices; static std::map<uint64_t, reed_sol_matrix_t> matrices;
void use_jerasure(int pg_size, int pg_minsize, bool use) void use_ec(int pg_size, int pg_minsize, bool use)
{ {
uint64_t key = (uint64_t)pg_size | ((uint64_t)pg_minsize) << 32; uint64_t key = (uint64_t)pg_size | ((uint64_t)pg_minsize) << 32;
auto rs_it = matrices.find(key); auto rs_it = matrices.find(key);
@ -164,19 +170,33 @@ void use_jerasure(int pg_size, int pg_minsize, bool use)
return; return;
} }
int *matrix = reed_sol_vandermonde_coding_matrix(pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W); int *matrix = reed_sol_vandermonde_coding_matrix(pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W);
uint8_t *isal_table = NULL;
#ifdef WITH_ISAL
uint8_t *isal_matrix = (uint8_t*)malloc_or_die(pg_minsize*(pg_size-pg_minsize));
for (int i = 0; i < pg_minsize*(pg_size-pg_minsize); i++)
{
isal_matrix[i] = matrix[i];
}
isal_table = (uint8_t*)malloc_or_die(pg_minsize*(pg_size-pg_minsize)*32);
ec_init_tables(pg_minsize, pg_size-pg_minsize, isal_matrix, isal_table);
free(isal_matrix);
#endif
matrices[key] = (reed_sol_matrix_t){ matrices[key] = (reed_sol_matrix_t){
.refs = 0, .refs = 0,
.data = matrix, .je_data = matrix,
.isal_data = isal_table,
}; };
rs_it = matrices.find(key); rs_it = matrices.find(key);
} }
rs_it->second.refs += (!use ? -1 : 1); rs_it->second.refs += (!use ? -1 : 1);
if (rs_it->second.refs <= 0) if (rs_it->second.refs <= 0)
{ {
free(rs_it->second.data); free(rs_it->second.je_data);
if (rs_it->second.isal_data)
free(rs_it->second.isal_data);
for (auto dec_it = rs_it->second.decodings.begin(); dec_it != rs_it->second.decodings.end();) for (auto dec_it = rs_it->second.decodings.begin(); dec_it != rs_it->second.decodings.end();)
{ {
int *data = dec_it->second; void *data = dec_it->second;
rs_it->second.decodings.erase(dec_it++); rs_it->second.decodings.erase(dec_it++);
free(data); free(data);
} }
@ -184,7 +204,7 @@ void use_jerasure(int pg_size, int pg_minsize, bool use)
} }
} }
reed_sol_matrix_t* get_jerasure_matrix(int pg_size, int pg_minsize) static reed_sol_matrix_t* get_ec_matrix(int pg_size, int pg_minsize)
{ {
uint64_t key = (uint64_t)pg_size | ((uint64_t)pg_minsize) << 32; uint64_t key = (uint64_t)pg_size | ((uint64_t)pg_minsize) << 32;
auto rs_it = matrices.find(key); auto rs_it = matrices.find(key);
@ -199,7 +219,7 @@ reed_sol_matrix_t* get_jerasure_matrix(int pg_size, int pg_minsize)
// we don't need it. also it makes an extra allocation of int *erased on every call and doesn't cache // we don't need it. also it makes an extra allocation of int *erased on every call and doesn't cache
// the decoding matrix. // the decoding matrix.
// all these flaws are fixed in this function: // all these flaws are fixed in this function:
int* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize) static void* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize)
{ {
int edd = 0; int edd = 0;
int erased[pg_size]; int erased[pg_size];
@ -210,16 +230,57 @@ int* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size, int pg
edd++; edd++;
if (edd == 0) if (edd == 0)
return NULL; return NULL;
reed_sol_matrix_t *matrix = get_jerasure_matrix(pg_size, pg_minsize); reed_sol_matrix_t *matrix = get_ec_matrix(pg_size, pg_minsize);
auto dec_it = matrix->decodings.find((reed_sol_erased_t){ .data = erased, .size = pg_size }); auto dec_it = matrix->decodings.find((reed_sol_erased_t){ .data = erased, .size = pg_size });
if (dec_it == matrix->decodings.end()) if (dec_it == matrix->decodings.end())
{ {
#ifdef WITH_ISAL
int smrow = 0;
uint8_t *submatrix = (uint8_t*)malloc_or_die(pg_minsize*pg_minsize*2);
for (int i = 0; i < pg_size; i++)
{
if (!erased[i])
{
if (i < pg_minsize)
{
for (int j = 0; j < pg_minsize; j++)
submatrix[smrow*pg_minsize + j] = j == i;
}
else
{
for (int j = 0; j < pg_minsize; j++)
submatrix[smrow*pg_minsize + j] = (uint8_t)matrix->je_data[(i-pg_minsize)*pg_minsize + j];
}
smrow++;
}
}
if (smrow < pg_minsize)
{
free(submatrix);
throw std::runtime_error("failed to make an invertible submatrix");
}
gf_invert_matrix(submatrix, submatrix + pg_minsize*pg_minsize, pg_minsize);
smrow = 0;
for (int i = 0; i < pg_minsize; i++)
{
if (erased[i])
{
memcpy(submatrix + pg_minsize*smrow, submatrix + (pg_minsize+i)*pg_minsize, pg_minsize);
smrow++;
}
}
uint8_t *rectable = (uint8_t*)malloc_or_die(32*smrow*pg_minsize + pg_size*sizeof(int));
ec_init_tables(pg_minsize, smrow, submatrix, rectable);
free(submatrix);
int *erased_copy = (int*)(rectable + 32*smrow*pg_minsize);
memcpy(erased_copy, erased, pg_size*sizeof(int));
matrix->decodings.emplace((reed_sol_erased_t){ .data = erased_copy, .size = pg_size }, rectable);
return rectable;
#else
int *dm_ids = (int*)malloc_or_die(sizeof(int)*(pg_minsize + pg_minsize*pg_minsize + pg_size)); int *dm_ids = (int*)malloc_or_die(sizeof(int)*(pg_minsize + pg_minsize*pg_minsize + pg_size));
int *decoding_matrix = dm_ids + pg_minsize; int *decoding_matrix = dm_ids + pg_minsize;
if (!dm_ids)
throw std::bad_alloc();
// we always use row_k_ones=1 and w=8 (OSD_JERASURE_W) // we always use row_k_ones=1 and w=8 (OSD_JERASURE_W)
if (jerasure_make_decoding_matrix(pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->data, erased, decoding_matrix, dm_ids) < 0) if (jerasure_make_decoding_matrix(pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->je_data, erased, decoding_matrix, dm_ids) < 0)
{ {
free(dm_ids); free(dm_ids);
throw std::runtime_error("jerasure_make_decoding_matrix() failed"); throw std::runtime_error("jerasure_make_decoding_matrix() failed");
@ -228,13 +289,64 @@ int* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size, int pg
memcpy(erased_copy, erased, pg_size*sizeof(int)); memcpy(erased_copy, erased, pg_size*sizeof(int));
matrix->decodings.emplace((reed_sol_erased_t){ .data = erased_copy, .size = pg_size }, dm_ids); matrix->decodings.emplace((reed_sol_erased_t){ .data = erased_copy, .size = pg_size }, dm_ids);
return dm_ids; return dm_ids;
#endif
} }
return dec_it->second; return dec_it->second;
} }
void reconstruct_stripes_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, uint32_t bitmap_size) #ifdef WITH_ISAL
void reconstruct_stripes_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, uint32_t bitmap_size)
{ {
int *dm_ids = get_jerasure_decoding_matrix(stripes, pg_size, pg_minsize); uint8_t *dectable = (uint8_t*)get_jerasure_decoding_matrix(stripes, pg_size, pg_minsize);
if (!dectable)
{
return;
}
uint8_t *data_ptrs[pg_size];
int wanted_base = 0, wanted = 0;
uint64_t read_start = 0, read_end = 0;
auto recover_seq = [&]()
{
int orig = 0;
for (int other = 0; other < pg_size; other++)
{
if (stripes[other].read_end != 0 && !stripes[other].missing)
{
assert(stripes[other].read_start <= read_start);
assert(stripes[other].read_end >= read_end);
data_ptrs[orig++] = (uint8_t*)stripes[other].read_buf + (read_start - stripes[other].read_start);
}
}
ec_encode_data(
read_end-read_start, pg_minsize, wanted, dectable + wanted_base*32*pg_minsize,
data_ptrs, data_ptrs + pg_minsize
);
wanted_base += wanted;
wanted = 0;
};
for (int role = 0; role < pg_minsize; role++)
{
if (stripes[role].read_end != 0 && stripes[role].missing)
{
if (read_end && (stripes[role].read_start != read_start ||
stripes[role].read_end != read_end))
{
recover_seq();
}
read_start = stripes[role].read_start;
read_end = stripes[role].read_end;
data_ptrs[pg_minsize + (wanted++)] = (uint8_t*)stripes[role].read_buf;
}
}
if (wanted > 0)
{
recover_seq();
}
}
#else
void reconstruct_stripes_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, uint32_t bitmap_size)
{
int *dm_ids = (int*)get_jerasure_decoding_matrix(stripes, pg_size, pg_minsize);
if (!dm_ids) if (!dm_ids)
{ {
return; return;
@ -242,7 +354,9 @@ void reconstruct_stripes_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg
int *decoding_matrix = dm_ids + pg_minsize; int *decoding_matrix = dm_ids + pg_minsize;
char *data_ptrs[pg_size]; char *data_ptrs[pg_size];
for (int role = 0; role < pg_size; role++) for (int role = 0; role < pg_size; role++)
{
data_ptrs[role] = NULL; data_ptrs[role] = NULL;
}
for (int role = 0; role < pg_minsize; role++) for (int role = 0; role < pg_minsize; role++)
{ {
if (stripes[role].read_end != 0 && stripes[role].missing) if (stripes[role].read_end != 0 && stripes[role].missing)
@ -279,6 +393,7 @@ void reconstruct_stripes_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg
} }
} }
} }
#endif
int extend_missing_stripes(osd_rmw_stripe_t *stripes, osd_num_t *osd_set, int pg_minsize, int pg_size) int extend_missing_stripes(osd_rmw_stripe_t *stripes, osd_num_t *osd_set, int pg_minsize, int pg_size)
{ {
@ -679,12 +794,12 @@ void calc_rmw_parity_xor(osd_rmw_stripe_t *stripes, int pg_size, uint64_t *read_
calc_rmw_parity_copy_parity(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, start, end); calc_rmw_parity_copy_parity(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, start, end);
} }
void calc_rmw_parity_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, void calc_rmw_parity_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize,
uint64_t *read_osd_set, uint64_t *write_osd_set, uint32_t chunk_size, uint32_t bitmap_size) uint64_t *read_osd_set, uint64_t *write_osd_set, uint32_t chunk_size, uint32_t bitmap_size)
{ {
uint32_t bitmap_granularity = bitmap_size > 0 ? chunk_size / bitmap_size / 8 : 0; uint32_t bitmap_granularity = bitmap_size > 0 ? chunk_size / bitmap_size / 8 : 0;
reed_sol_matrix_t *matrix = get_jerasure_matrix(pg_size, pg_minsize); reed_sol_matrix_t *matrix = get_ec_matrix(pg_size, pg_minsize);
reconstruct_stripes_jerasure(stripes, pg_size, pg_minsize, bitmap_size); reconstruct_stripes_ec(stripes, pg_size, pg_minsize, bitmap_size);
uint32_t start = 0, end = 0; uint32_t start = 0, end = 0;
calc_rmw_parity_copy_mod(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, bitmap_granularity, start, end); calc_rmw_parity_copy_mod(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, bitmap_granularity, start, end);
if (end != 0) if (end != 0)
@ -741,20 +856,34 @@ void calc_rmw_parity_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg_min
curbuf[i]++; curbuf[i]++;
} }
} }
#ifdef WITH_ISAL
ec_encode_data(
next_end-pos, pg_minsize, pg_size-pg_minsize, matrix->isal_data,
(uint8_t**)data_ptrs, (uint8_t**)data_ptrs+pg_minsize
);
#else
jerasure_matrix_encode( jerasure_matrix_encode(
pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->data, pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->je_data,
(char**)data_ptrs, (char**)data_ptrs+pg_minsize, next_end-pos (char**)data_ptrs, (char**)data_ptrs+pg_minsize, next_end-pos
); );
#endif
pos = next_end; pos = next_end;
} }
for (int i = 0; i < pg_size; i++) for (int i = 0; i < pg_size; i++)
{ {
data_ptrs[i] = stripes[i].bmp_buf; data_ptrs[i] = stripes[i].bmp_buf;
} }
#ifdef WITH_ISAL
ec_encode_data(
bitmap_size, pg_minsize, pg_size-pg_minsize, matrix->isal_data,
(uint8_t**)data_ptrs, (uint8_t**)data_ptrs+pg_minsize
);
#else
jerasure_matrix_encode( jerasure_matrix_encode(
pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->data, pg_minsize, pg_size-pg_minsize, OSD_JERASURE_W, matrix->je_data,
(char**)data_ptrs, (char**)data_ptrs+pg_minsize, bitmap_size (char**)data_ptrs, (char**)data_ptrs+pg_minsize, bitmap_size
); );
#endif
} }
} }
calc_rmw_parity_copy_parity(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, start, end); calc_rmw_parity_copy_parity(stripes, pg_size, pg_minsize, read_osd_set, write_osd_set, chunk_size, start, end);

View File

@ -7,6 +7,7 @@
#include "object_id.h" #include "object_id.h"
#include "osd_id.h" #include "osd_id.h"
// Memory allocation alignment (page size is usually optimal)
#ifndef MEM_ALIGNMENT #ifndef MEM_ALIGNMENT
#define MEM_ALIGNMENT 4096 #define MEM_ALIGNMENT 4096
#endif #endif
@ -44,9 +45,9 @@ void* calc_rmw(void *request_buf, osd_rmw_stripe_t *stripes, uint64_t *read_osd_
void calc_rmw_parity_xor(osd_rmw_stripe_t *stripes, int pg_size, uint64_t *read_osd_set, uint64_t *write_osd_set, void calc_rmw_parity_xor(osd_rmw_stripe_t *stripes, int pg_size, uint64_t *read_osd_set, uint64_t *write_osd_set,
uint32_t chunk_size, uint32_t bitmap_size); uint32_t chunk_size, uint32_t bitmap_size);
void use_jerasure(int pg_size, int pg_minsize, bool use); void use_ec(int pg_size, int pg_minsize, bool use);
void reconstruct_stripes_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, uint32_t bitmap_size); void reconstruct_stripes_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, uint32_t bitmap_size);
void calc_rmw_parity_jerasure(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize, void calc_rmw_parity_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsize,
uint64_t *read_osd_set, uint64_t *write_osd_set, uint32_t chunk_size, uint32_t bitmap_size); uint64_t *read_osd_set, uint64_t *write_osd_set, uint32_t chunk_size, uint32_t bitmap_size);

View File

@ -587,14 +587,14 @@ void test12()
input buffer: [ write0, write1 ], input buffer: [ write0, write1 ],
rmw buffer: [ write2, write3, read0, read1 ], rmw buffer: [ write2, write3, read0, read1 ],
} }
then, after calc_rmw_parity_jerasure(): all the same then, after calc_rmw_parity_ec(): all the same
then simulate read with read_osd_set=[0,0,3,4] and check read0,read1 buffers then simulate read with read_osd_set=[0,0,3,4] and check read0,read1 buffers
***/ ***/
void test13() void test13()
{ {
use_jerasure(4, 2, true); use_ec(4, 2, true);
osd_num_t osd_set[4] = { 1, 2, 0, 0 }; osd_num_t osd_set[4] = { 1, 2, 0, 0 };
osd_num_t write_osd_set[4] = { 1, 2, 3, 4 }; osd_num_t write_osd_set[4] = { 1, 2, 3, 4 };
osd_rmw_stripe_t stripes[4] = {}; osd_rmw_stripe_t stripes[4] = {};
@ -628,7 +628,7 @@ void test13()
set_pattern(write_buf, 8192, PATTERN3); set_pattern(write_buf, 8192, PATTERN3);
set_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1); set_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1);
set_pattern(stripes[1].read_buf, 128*1024-4096, PATTERN2); set_pattern(stripes[1].read_buf, 128*1024-4096, PATTERN2);
calc_rmw_parity_jerasure(stripes, 4, 2, osd_set, write_osd_set, 128*1024, 0); calc_rmw_parity_ec(stripes, 4, 2, osd_set, write_osd_set, 128*1024, 0);
assert(stripes[0].write_start == 128*1024-4096 && stripes[0].write_end == 128*1024); assert(stripes[0].write_start == 128*1024-4096 && stripes[0].write_end == 128*1024);
assert(stripes[1].write_start == 0 && stripes[1].write_end == 4096); assert(stripes[1].write_start == 0 && stripes[1].write_end == 4096);
assert(stripes[2].write_start == 0 && stripes[2].write_end == 128*1024); assert(stripes[2].write_start == 0 && stripes[2].write_end == 128*1024);
@ -663,7 +663,7 @@ void test13()
assert(stripes[3].read_buf == (uint8_t*)read_buf+3*128*1024); assert(stripes[3].read_buf == (uint8_t*)read_buf+3*128*1024);
memcpy((uint8_t*)read_buf+2*128*1024, rmw_buf, 128*1024); memcpy((uint8_t*)read_buf+2*128*1024, rmw_buf, 128*1024);
memcpy((uint8_t*)read_buf+3*128*1024, (uint8_t*)rmw_buf+128*1024, 128*1024); memcpy((uint8_t*)read_buf+3*128*1024, (uint8_t*)rmw_buf+128*1024, 128*1024);
reconstruct_stripes_jerasure(stripes, 4, 2, 0); reconstruct_stripes_ec(stripes, 4, 2, 0);
check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1); check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1);
check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3); check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3);
check_pattern(stripes[1].read_buf, 4096, PATTERN3); check_pattern(stripes[1].read_buf, 4096, PATTERN3);
@ -694,14 +694,14 @@ void test13()
assert(stripes[3].read_buf == (uint8_t*)read_buf+2*128*1024); assert(stripes[3].read_buf == (uint8_t*)read_buf+2*128*1024);
memcpy((uint8_t*)read_buf+128*1024, rmw_buf, 128*1024); memcpy((uint8_t*)read_buf+128*1024, rmw_buf, 128*1024);
memcpy((uint8_t*)read_buf+2*128*1024, (uint8_t*)rmw_buf+128*1024, 128*1024); memcpy((uint8_t*)read_buf+2*128*1024, (uint8_t*)rmw_buf+128*1024, 128*1024);
reconstruct_stripes_jerasure(stripes, 4, 2, 0); reconstruct_stripes_ec(stripes, 4, 2, 0);
check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1); check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1);
check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3); check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3);
free(read_buf); free(read_buf);
// Huh done // Huh done
free(rmw_buf); free(rmw_buf);
free(write_buf); free(write_buf);
use_jerasure(4, 2, false); use_ec(4, 2, false);
} }
/*** /***
@ -714,7 +714,7 @@ void test13()
input buffer: [ write0, write1 ], input buffer: [ write0, write1 ],
rmw buffer: [ write2, read0, read1 ], rmw buffer: [ write2, read0, read1 ],
} }
then, after calc_rmw_parity_jerasure(): all the same then, after calc_rmw_parity_ec(): all the same
then simulate read with read_osd_set=[0,2,3] and check read0 buffer then simulate read with read_osd_set=[0,2,3] and check read0 buffer
***/ ***/
@ -722,7 +722,7 @@ void test13()
void test14() void test14()
{ {
const int bmp = 4; const int bmp = 4;
use_jerasure(3, 2, true); use_ec(3, 2, true);
osd_num_t osd_set[3] = { 1, 2, 0 }; osd_num_t osd_set[3] = { 1, 2, 0 };
osd_num_t write_osd_set[3] = { 1, 2, 3 }; osd_num_t write_osd_set[3] = { 1, 2, 3 };
osd_rmw_stripe_t stripes[3] = {}; osd_rmw_stripe_t stripes[3] = {};
@ -757,7 +757,7 @@ void test14()
memset(stripes[0].bmp_buf, 0, bmp); memset(stripes[0].bmp_buf, 0, bmp);
memset(stripes[1].bmp_buf, 0, bmp); memset(stripes[1].bmp_buf, 0, bmp);
memset(stripes[2].bmp_buf, 0, bmp); memset(stripes[2].bmp_buf, 0, bmp);
calc_rmw_parity_jerasure(stripes, 3, 2, osd_set, write_osd_set, 128*1024, bmp); calc_rmw_parity_ec(stripes, 3, 2, osd_set, write_osd_set, 128*1024, bmp);
assert(*(uint32_t*)stripes[0].bmp_buf == 0x80000000); assert(*(uint32_t*)stripes[0].bmp_buf == 0x80000000);
assert(*(uint32_t*)stripes[1].bmp_buf == 0x00000001); assert(*(uint32_t*)stripes[1].bmp_buf == 0x00000001);
assert(*(uint32_t*)stripes[2].bmp_buf == 0x80000001); // jerasure 2+1 is still just XOR assert(*(uint32_t*)stripes[2].bmp_buf == 0x80000001); // jerasure 2+1 is still just XOR
@ -793,12 +793,12 @@ void test14()
set_pattern(stripes[1].read_buf, 4096, PATTERN3); set_pattern(stripes[1].read_buf, 4096, PATTERN3);
set_pattern(stripes[1].read_buf+4096, 128*1024-4096, PATTERN2); set_pattern(stripes[1].read_buf+4096, 128*1024-4096, PATTERN2);
memcpy(stripes[2].read_buf, rmw_buf, 128*1024); memcpy(stripes[2].read_buf, rmw_buf, 128*1024);
reconstruct_stripes_jerasure(stripes, 3, 2, bmp); reconstruct_stripes_ec(stripes, 3, 2, bmp);
check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1); check_pattern(stripes[0].read_buf, 128*1024-4096, PATTERN1);
check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3); check_pattern(stripes[0].read_buf+128*1024-4096, 4096, PATTERN3);
free(read_buf); free(read_buf);
// Huh done // Huh done
free(rmw_buf); free(rmw_buf);
free(write_buf); free(write_buf);
use_jerasure(3, 2, false); use_ec(3, 2, false);
} }

View File

@ -34,7 +34,6 @@
#define OBJ_DEGRADED 0x02 #define OBJ_DEGRADED 0x02
#define OBJ_INCOMPLETE 0x04 #define OBJ_INCOMPLETE 0x04
#define OBJ_MISPLACED 0x08 #define OBJ_MISPLACED 0x08
#define OBJ_DELETED 0x10
#define OBJ_NEEDS_STABLE 0x10000 #define OBJ_NEEDS_STABLE 0x10000
#define OBJ_NEEDS_ROLLBACK 0x20000 #define OBJ_NEEDS_ROLLBACK 0x20000

View File

@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: Vitastor Name: Vitastor
Description: Vitastor client library Description: Vitastor client library
Version: 0.6.17 Version: 0.7.1
Libs: -L${libdir} -lvitastor_client Libs: -L${libdir} -lvitastor_client
Cflags: -I${includedir} Cflags: -I${includedir}

View File

@ -1,18 +1,39 @@
#!/bin/bash -ex #!/bin/bash
. `dirname $0`/common.sh . `dirname $0`/common.sh
OSD_SIZE=${OSD_SIZE:-1024} OSD_SIZE=${OSD_SIZE:-1024}
PG_COUNT=${PG_COUNT:-1} PG_COUNT=${PG_COUNT:-1}
PG_SIZE=${PG_SIZE:-3} # OSD_COUNT
PG_MINSIZE=${PG_MINSIZE:-2} SCHEME=${SCHEME:-replicated}
OSD_COUNT=${OSD_COUNT:-3} # OSD_ARGS
SCHEME=${SCHEME:-ec} # PG_SIZE
# PG_MINSIZE
if [ "$SCHEME" = "ec" ]; then
OSD_COUNT=${OSD_COUNT:-5}
else
OSD_COUNT=${OSD_COUNT:-3}
fi
if [ "$IMMEDIATE_COMMIT" != "" ]; then
NO_SAME="--journal_no_same_sector_overwrites true --journal_sector_buffer_count 1024 --disable_data_fsync 1 --immediate_commit all --log_level 1"
$ETCDCTL put /vitastor/config/global '{"recovery_queue_depth":1,"osd_out_time":1,"immediate_commit":"all"}'
else
NO_SAME="--journal_sector_buffer_count 1024 --log_level 1"
$ETCDCTL put /vitastor/config/global '{"recovery_queue_depth":1,"osd_out_time":1}'
fi
start_osd()
{
local i=$1
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $NO_SAME $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log &
eval OSD${i}_PID=$!
}
for i in $(seq 1 $OSD_COUNT); do for i in $(seq 1 $OSD_COUNT); do
dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1)) dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log & start_osd $i
eval OSD${i}_PID=$!
done done
cd mon cd mon
@ -21,43 +42,62 @@ cd ..
node mon/mon-main.js --etcd_url $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 &>./testdata/mon.log & node mon/mon-main.js --etcd_url $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 &>./testdata/mon.log &
MON_PID=$! MON_PID=$!
if [ -n "$GLOBAL_CONF" ]; then if [ "$SCHEME" = "ec" ]; then
$ETCDCTL put /vitastor/config/global "$GLOBAL_CONF" PG_SIZE=${PG_SIZE:-5}
fi PG_MINSIZE=${PG_MINSIZE:-3}
PG_DATA_SIZE=$PG_MINSIZE
if [ "$SCHEME" = "replicated" ]; then POOLCFG='"scheme":"ec","parity_chunks":'$((PG_SIZE-PG_MINSIZE))
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"pg_count":'$PG_COUNT',"failure_domain":"osd"}}' elif [ "$SCHEME" = "xor" ]; then
PG_SIZE=${PG_SIZE:-3}
PG_MINSIZE=${PG_MINSIZE:-2}
PG_DATA_SIZE=$PG_MINSIZE
POOLCFG='"scheme":"xor","parity_chunks":'$((PG_SIZE-PG_MINSIZE))
else else
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"xor","pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"parity_chunks":1,"pg_count":'$PG_COUNT',"failure_domain":"osd"}}' PG_SIZE=${PG_SIZE:-2}
PG_MINSIZE=${PG_MINSIZE:-2}
PG_DATA_SIZE=1
POOLCFG='"scheme":"replicated"'
fi
POOLCFG='"name":"testpool","failure_domain":"osd",'$POOLCFG
$ETCDCTL put /vitastor/config/pools '{"1":{'$POOLCFG',"pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"pg_count":'$PG_COUNT'}}'
sleep 2
if ! ($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | select(. > 0)) | length == '$PG_SIZE') | length) == '$PG_COUNT); then
format_error "FAILED: $PG_COUNT PGS NOT CONFIGURED"
fi fi
wait_up() if ! ($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == '$PG_COUNT); then
format_error "FAILED: $PG_COUNT PGS NOT UP"
fi
try_reweight()
{ {
local sec=$1 osd=$1
local i=0 w=$2
local configured=0 $ETCDCTL put /vitastor/config/osd/$osd '{"reweight":'$w'}'
sleep 3
}
wait_finish_rebalance()
{
sec=$1
i=0
while [[ $i -lt $sec ]]; do while [[ $i -lt $sec ]]; do
if $ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(. | length) != 0 and ([ .[0].items["1"][] | ($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == 32') && \
select(((.osd_set | select(. != 0) | sort | unique) | length) == '$PG_SIZE') ] | length) == '$PG_COUNT; then
configured=1
if $ETCDCTL get /vitastor/pg/state/1/ --prefix --print-value-only | jq -s -e '[ .[] | select(.state == ["active"]) ] | length == '$PG_COUNT; then
break break
fi if [ $i -eq 60 ]; then
format_error "Rebalance couldn't finish in $sec seconds"
fi fi
sleep 1 sleep 1
i=$((i+1)) i=$((i+1))
if [ $i -eq $sec ]; then
if [[ $configured -ne 0 ]]; then
format_error "FAILED: $PG_COUNT PG(s) NOT CONFIGURED"
fi
format_error "FAILED: $PG_COUNT PG(s) NOT UP"
fi
done done
} }
wait_up 60 check_qemu()
{
if ! cmp build/src/block-vitastor.so /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so; then if ! cmp build/src/block-vitastor.so /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so; then
sudo rm -f /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so sudo rm -f /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so
sudo ln -s "$(realpath .)/build/src/block-vitastor.so" /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so sudo ln -s "$(realpath .)/build/src/block-vitastor.so" /usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so
fi fi
}

View File

@ -1,74 +0,0 @@
#!/bin/bash
. `dirname $0`/common.sh
if [ "$IMMEDIATE_COMMIT" != "" ]; then
NO_SAME="--journal_no_same_sector_overwrites true --journal_sector_buffer_count 1024 --disable_data_fsync 1 --immediate_commit all --log_level 1"
$ETCDCTL put /vitastor/config/global '{"recovery_queue_depth":1,"osd_out_time":1,"immediate_commit":"all"}'
else
NO_SAME="--journal_sector_buffer_count 1024 --log_level 1"
$ETCDCTL put /vitastor/config/global '{"recovery_queue_depth":1,"osd_out_time":1}'
fi
start_osd()
{
local i=$1
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $NO_SAME $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log &
eval OSD${i}_PID=$!
}
OSD_SIZE=1024
OSD_COUNT=7
OSD_ARGS=
for i in $(seq 1 $OSD_COUNT); do
dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
start_osd $i
done
cd mon
npm install
cd ..
node mon/mon-main.js --etcd_url $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 &>./testdata/mon.log &
MON_PID=$!
if [ "$EC" != "" ]; then
PG_SIZE=3
POOLCFG='"scheme":"xor","pg_size":3,"pg_minsize":2,"parity_chunks":1'
else
PG_SIZE=${PG_SIZE:-2}
POOLCFG='"scheme":"replicated","pg_size":'$PG_SIZE',"pg_minsize":2'
fi
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool",'$POOLCFG',"pg_count":32,"failure_domain":"osd"}}'
sleep 2
if ! ($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | select(. > 0)) | length == '$PG_SIZE') | length) == 32'); then
format_error "FAILED: 32 PGS NOT CONFIGURED"
fi
if ! ($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == 32'); then
format_error "FAILED: 32 PGS NOT UP"
fi
try_reweight()
{
osd=$1
w=$2
$ETCDCTL put /vitastor/config/osd/$osd '{"reweight":'$w'}'
sleep 3
}
wait_finish_rebalance()
{
sec=$1
i=0
while [[ $i -lt $sec ]]; do
($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == 32') && \
break
sleep 1
i=$((i+1))
if [ $i -eq $sec ]; then
format_error "Rebalance couldn't finish in $sec seconds"
fi
done
}

View File

@ -8,11 +8,11 @@ cd $(dirname $0)
./test_cas.sh ./test_cas.sh
./test_change_pg_count.sh ./test_change_pg_count.sh
EC=1 ./test_change_pg_count.sh SCHEME=ec ./test_change_pg_count.sh
./test_change_pg_size.sh ./test_change_pg_size.sh
./test_degraded_delete.sh ./test_create_nomaxid.sh
./test_etcd_fail.sh ./test_etcd_fail.sh
@ -20,8 +20,8 @@ EC=1 ./test_change_pg_count.sh
./test_interrupted_rebalance.sh ./test_interrupted_rebalance.sh
IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh
EC=1 ./test_interrupted_rebalance.sh SCHEME=ec ./test_interrupted_rebalance.sh
EC=1 IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh SCHEME=ec IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh
./test_minsize_1.sh ./test_minsize_1.sh
@ -29,17 +29,17 @@ EC=1 IMMEDIATE_COMMIT=1 ./test_interrupted_rebalance.sh
./test_rebalance_verify.sh ./test_rebalance_verify.sh
IMMEDIATE_COMMIT=1 ./test_rebalance_verify.sh IMMEDIATE_COMMIT=1 ./test_rebalance_verify.sh
EC=1 ./test_rebalance_verify.sh SCHEME=ec ./test_rebalance_verify.sh
EC=1 IMMEDIATE_COMMIT=1 ./test_rebalance_verify.sh SCHEME=ec IMMEDIATE_COMMIT=1 ./test_rebalance_verify.sh
./test_rm.sh ./test_rm.sh
./test_snapshot.sh ./test_snapshot.sh
SCHEME=replicated ./test_snapshot.sh SCHEME=ec ./test_snapshot.sh
./test_splitbrain.sh ./test_splitbrain.sh
./test_write.sh ./test_write.sh
SCHEME=replicated ./test_write.sh SCHEME=xor ./test_write.sh
./test_write_no_same.sh ./test_write_no_same.sh

View File

@ -1,41 +1,11 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/common.sh OSD_COUNT=${OSD_COUNT:-6}
PG_COUNT=16
if [ "$EC" != "" ]; then . `dirname $0`/run_3osds.sh
POOLCFG='"scheme":"xor","pg_size":3,"pg_minsize":2,"parity_chunks":1'
NOBJ=512
else
POOLCFG='"scheme":"replicated","pg_size":2,"pg_minsize":2'
NOBJ=1024
fi
OSD_SIZE=1024 NOBJ=$(((128*8+PG_DATA_SIZE-1)/PG_DATA_SIZE))
OSD_COUNT=6
OSD_ARGS=
for i in $(seq 1 $OSD_COUNT); do
dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log &
eval OSD${i}_PID=$!
done
cd mon
npm install
cd ..
node mon/mon-main.js --etcd_url $ETCD_URL --etcd_prefix "/vitastor" --verbose 1 &>./testdata/mon.log &
MON_PID=$!
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool",'$POOLCFG',"pg_count":16,"failure_domain":"osd"}}'
sleep 2
if ! ($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | select(. > 0)) | length == 2) | length) == 16'); then
format_error "FAILED: 16 PGS NOT CONFIGURED"
fi
if ! ($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == 16'); then
format_error "FAILED: 16 PGS NOT UP"
fi
LD_PRELOAD="build/src/libfio_vitastor.so" \ LD_PRELOAD="build/src/libfio_vitastor.so" \
fio -thread -name=test -ioengine=build/src/libfio_vitastor.so -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write \ fio -thread -name=test -ioengine=build/src/libfio_vitastor.so -bs=4M -direct=1 -iodepth=1 -fsync=1 -rw=write \
@ -49,7 +19,7 @@ try_change()
echo --- Change PG count to $n --- >>testdata/osd$i.log echo --- Change PG count to $n --- >>testdata/osd$i.log
done done
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool",'$POOLCFG',"pg_count":'$n',"failure_domain":"osd"}}' $ETCDCTL put /vitastor/config/pools '{"1":{'$POOLCFG',"pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"pg_count":'$n'}}'
for i in {1..10}; do for i in {1..10}; do
($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | select(. > 0)) | length == 2) | length) == '$n) && \ ($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | select(. > 0)) | length == 2) | length) == '$n) && \

View File

@ -1,40 +1,16 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/common.sh PG_COUNT=16
SCHEME=${SCHEME:-replicated}
OSD_SIZE=1024 . `dirname $0`/run_3osds.sh
OSD_COUNT=3
OSD_ARGS=
for i in $(seq 1 $OSD_COUNT); do
dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log &
eval OSD${i}_PID=$!
done
cd mon
npm install
cd ..
node mon/mon-main.js --etcd_url $ETCD_URL --etcd_prefix "/vitastor" &>./testdata/mon.log &
MON_PID=$!
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":3,"pg_minsize":2,"pg_count":16,"failure_domain":"osd"}}'
sleep 2
if ! ($ETCDCTL get /vitastor/config/pgs --print-value-only | jq -s -e '(.[0].items["1"] | map((.osd_set | sort) == ["1","2","3"]) | length) == 16'); then
format_error "FAILED: 16 PGS NOT CONFIGURED"
fi
if ! ($ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '([ .[] | select(.state == ["active"]) ] | length) == 16'); then
format_error "FAILED: 16 PGS NOT UP"
fi
try_change() try_change()
{ {
n=$1 n=$1
s=$2 s=$2
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":'$s',"pg_minsize":2,"pg_count":'$n',"failure_domain":"osd"}}' $ETCDCTL put /vitastor/config/pools '{"1":{'$POOLCFG',"pg_size":'$s',"pg_minsize":'$PG_MINSIZE',"pg_count":'$n'}}'
for i in {1..10}; do for i in {1..10}; do
($ETCDCTL get /vitastor/config/pgs --print-value-only |\ ($ETCDCTL get /vitastor/config/pgs --print-value-only |\

21
tests/test_create_nomaxid.sh Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash -ex
# Test vitastor-cli create when /index/maxid is out of sync
. `dirname $0`/run_3osds.sh
$ETCDCTL put /vitastor/config/inode/1/120 '{"name":"testimg","size":'$((1024*1024*1024))'}'
build/src/vitastor-cli create --etcd_address $ETCD_URL -s 1G testimg2
t=$($ETCDCTL get --print-value-only /vitastor/config/inode/1/121 | jq -r .name)
if [[ "$t" != "testimg2" ]]; then
format_error "testimg2 should've been created as inode 121"
fi
t=$($ETCDCTL get --print-value-only /vitastor/index/maxid/1)
if [[ "$t" != 121 ]]; then
format_error "/index/maxid should've been set to 121"
fi
format_green OK

View File

@ -1,131 +0,0 @@
#!/bin/bash -ex
# Run 3 OSDs
. `dirname $0`/run_3osds.sh
# Write inodes 1 and 2
LD_PRELOAD="build/src/libfio_vitastor.so" \
fio -thread -name=test -ioengine=build/src/libfio_vitastor.so -bs=1M -direct=1 -iodepth=4 \
-rw=write -etcd=$ETCD_URL -pool=1 -inode=1 -size=128M -runtime=10
LD_PRELOAD="build/src/libfio_vitastor.so" \
fio -thread -name=test -ioengine=build/src/libfio_vitastor.so -bs=1M -direct=1 -iodepth=4 \
-rw=write -etcd=$ETCD_URL -pool=1 -inode=2 -size=128M -runtime=10
LD_PRELOAD="build/src/libfio_vitastor.so" \
fio -thread -name=test -ioengine=build/src/libfio_vitastor.so -bs=4k -direct=1 -iodepth=16 \
-rw=randwrite -etcd=$ETCD_URL -pool=1 -inode=1 -size=128M -runtime=10 &>/dev/null &
sleep 5
# Stop OSD 1
kill -INT $OSD1_PID
sleep 2
# Remove inode 2
build/src/vitastor-cli rm-data --etcd_address $ETCD_URL --pool 1 --inode 2
# Run 3 more OSDs and move PG to 4,5,6
for i in $(seq 4 6); do
dd if=/dev/zero of=./testdata/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
build/src/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd$i.bin 2>/dev/null) &>./testdata/osd$i.log &
eval OSD${i}_PID=$!
done
$ETCDCTL put /vitastor/config/osd/1 '{"reweight":0}'
$ETCDCTL put /vitastor/config/osd/2 '{"reweight":0}'
$ETCDCTL put /vitastor/config/osd/3 '{"reweight":0}'
# Wait for rebalance to finish
wait_finish_rebalance()
{
local sec=$1
local st=$2
local i=0
while [[ $i -lt $sec ]]; do
if $ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e \
'([ .[] | select(.state == ['$st'] and (.peers | contains([1]) | not) and (.peers | contains([2,3]) | not)) ] | length) == '$PG_COUNT; then
break
fi
sleep 1
i=$((i+1))
if [ $i -eq $sec ]; then
format_error "Rebalance couldn't finish in $sec seconds"
fi
done
}
wait_finish_rebalance 60 '"active","left_on_dead"'
# Stop OSD 2,3
kill -INT $OSD2_PID
kill -INT $OSD3_PID
sleep 2
# Verify that PGs are still active
if ! ($ETCDCTL get /vitastor/pg/state/1/ --prefix --print-value-only | jq -s -e '[ .[] | select(.state == ["active","left_on_dead"]) ] | length == '$PG_COUNT); then
format_error "FAILED: $PG_COUNT PG(s) NOT UP"
fi
# Start OSD 1
build/src/vitastor-osd --osd_num 1 --bind_address 127.0.0.1 $OSD_ARGS --etcd_address $ETCD_URL $(build/src/vitastor-cli simple-offsets --format options ./testdata/test_osd1.bin 2>/dev/null) &>./testdata/osd1.log &
OSD1_PID=$!
# Verify that inode 2 is removed and inode 1 is in place
wait_repeer_1()
{
local sec=$1
local i=0
while [[ $i -lt $sec ]]; do
if grep -q 'Repeer because of OSD 1' testdata/osd4.log testdata/osd5.log testdata/osd6.log; then
break
fi
sleep 1
i=$((i+1))
if [ $i -eq $sec ]; then
format_error "OSD 4/5/6 do not peer with older OSD 1"
fi
done
}
wait_repeer_1 15
wait_finish_rebalance 15 '"active"'
if [ "$SCHEME" = "replicated" ]; then
NOBJ=1024
else
NOBJ=$((1024/(PG_SIZE-1)))
fi
if ! ($ETCDCTL get /vitastor/pg/stats/1/1 --print-value-only | jq -s -e '.[0].object_count == '$NOBJ); then
format_error "FAILED: PG SHOULD CONTAIN EXACTLY 128 MB OF DATA, BUT IT DOESN'T"
fi
qemu-img convert -S 4096 -p \
-f raw "vitastor:etcd_host=127.0.0.1\:$ETCD_PORT/v3:pool=1:inode=1:size=4096" \
-O raw ./testdata/inode1.bin
qemu-img convert -S 4096 -p \
-f raw "vitastor:etcd_host=127.0.0.1\:$ETCD_PORT/v3:pool=1:inode=2:size="$((128*1024*1024)) \
-O raw ./testdata/inode2.bin
if (dd if=/dev/zero bs=4096 count=1 | diff - ./testdata/inode1.bin); then
format_error "FAILED: INODE 1 SEEMS LOST"
fi
if ! (dd if=/dev/zero bs=1M count=128 | diff - ./testdata/inode2.bin); then
format_error "FAILED: INODE 2 SEEMS RESTORED"
fi
format_green OK

View File

@ -3,8 +3,10 @@
# Kill OSDs while writing # Kill OSDs while writing
PG_SIZE=3 PG_SIZE=3
OSD_COUNT=7
. `dirname $0`/run_7osds.sh PG_COUNT=32
. `dirname $0`/run_3osds.sh
check_qemu
IMG_SIZE=960 IMG_SIZE=960

View File

@ -1,6 +1,8 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/run_7osds.sh OSD_COUNT=7
PG_COUNT=32
. `dirname $0`/run_3osds.sh
IMG_SIZE=960 IMG_SIZE=960
@ -32,13 +34,14 @@ try_reweight 5 1
wait_finish_rebalance 60 wait_finish_rebalance 60
# Check that PGs never had degraded objects ! # Check that PGs never had degraded objects !
if grep has_degraded ./testdata/mon.log; then # FIXME: In fact, the test doesn't guarantee it because PGs aren't always peered only with full prior OSD sets :-(
format_error "Some copies of objects were lost during interrupted rebalancings" #if grep has_degraded ./testdata/mon.log; then
fi # format_error "Some copies of objects were lost during interrupted rebalancings"
#fi
# Check that no objects are lost ! # Check that no objects are lost !
nobj=`$ETCDCTL get --prefix '/vitastor/pg/stats' --print-value-only | jq -s '[ .[].object_count ] | reduce .[] as $num (0; .+$num)'` nobj=`$ETCDCTL get --prefix '/vitastor/pg/stats' --print-value-only | jq -s '[ .[].object_count ] | reduce .[] as $num (0; .+$num)'`
if [ "$nobj" -ne $((IMG_SIZE*8)) ]; then if [ "$nobj" -ne $((IMG_SIZE*8/PG_DATA_SIZE)) ]; then
format_error "Data lost after multiple interrupted rebalancings" format_error "Data lost after multiple interrupted rebalancings"
fi fi

View File

@ -1,6 +1,8 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/run_7osds.sh OSD_COUNT=7
PG_COUNT=32
. `dirname $0`/run_3osds.sh
IMG_SIZE=256 IMG_SIZE=256

View File

@ -1,6 +1,7 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/run_3osds.sh . `dirname $0`/run_3osds.sh
check_qemu
# Test basic write and snapshot # Test basic write and snapshot

View File

@ -3,6 +3,7 @@
OSD_SIZE=2048 OSD_SIZE=2048
. `dirname $0`/run_3osds.sh . `dirname $0`/run_3osds.sh
check_qemu
$ETCDCTL put /vitastor/config/inode/1/1 '{"name":"debian9","size":'$((2048*1024*1024))'}' $ETCDCTL put /vitastor/config/inode/1/1 '{"name":"debian9","size":'$((2048*1024*1024))'}'

View File

@ -1,6 +1,7 @@
#!/bin/bash -ex #!/bin/bash -ex
. `dirname $0`/run_3osds.sh . `dirname $0`/run_3osds.sh
check_qemu
#LD_PRELOAD=libasan.so.5 \ #LD_PRELOAD=libasan.so.5 \
# fio -thread -name=test -ioengine=build/src/libfio_vitastor_sec.so -bs=4k -fsync=128 `$ETCDCTL get /vitastor/osd/state/1 --print-value-only | jq -r '"-host="+.addresses[0]+" -port="+(.port|tostring)'` -rw=write -size=32M # fio -thread -name=test -ioengine=build/src/libfio_vitastor_sec.so -bs=4k -fsync=128 `$ETCDCTL get /vitastor/osd/state/1 --print-value-only | jq -r '"-host="+.addresses[0]+" -port="+(.port|tostring)'` -rw=write -size=32M