Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee0bae573f | ||
|
|
27be4ee2fa | ||
|
|
00517e2bac | ||
|
|
e0a2615cbc | ||
|
|
63fe3c323a | ||
|
|
a88465df05 | ||
|
|
ad24be717a | ||
|
|
648e3b12f0 | ||
|
|
a675993c74 | ||
|
|
c9dfd0f67d | ||
|
|
84919a10a9 | ||
|
|
51ae4d6e24 | ||
|
|
572b20fedc | ||
|
|
4e2724b28f | ||
|
|
768b1675f8 | ||
|
|
38fa722725 | ||
|
|
e56d83fb7f | ||
|
|
ff95a85875 | ||
|
|
98203568a8 | ||
|
|
89df98ee08 | ||
|
|
0007a831b6 | ||
|
|
40517c335f | ||
|
|
c9f7308b6a | ||
|
|
85c7e3bde0 | ||
|
|
4fb55b3535 | ||
|
|
912aca11a3 | ||
|
|
7b454bd16c | ||
|
|
a0c8be46a4 | ||
|
|
53b4329fac | ||
|
|
a7f41c4a12 | ||
|
|
5d78057ac3 | ||
|
|
8efc5a353f | ||
|
|
603b26b896 | ||
|
|
a3b0fe0deb | ||
|
|
f504e356d5 | ||
|
|
4ed17b7070 | ||
|
|
1fd2819724 | ||
|
|
dcdabbc1ec | ||
|
|
625d5b7b9e | ||
|
|
9e507fd333 | ||
|
|
c2b5118127 | ||
|
|
4b926e2223 | ||
|
|
a5d9a6996a | ||
|
|
0ee03e7172 | ||
|
|
88b7d9afcd | ||
|
|
f271c8450c | ||
|
|
f78d7d4efc | ||
|
|
fdaf7c88ff | ||
|
|
2fb6eb0c30 | ||
|
|
36d2b56208 | ||
|
|
14b22f2ba9 | ||
|
|
fe8b1fe0cc | ||
|
|
1ec963e468 | ||
|
|
5100f822d8 | ||
|
|
7432494e88 | ||
|
|
d0c0f3ea39 | ||
|
|
f61190f31d | ||
|
|
3dc0ab5c33 | ||
|
|
de96efed2f | ||
|
|
87a5230798 | ||
|
|
0c5e6d4346 | ||
|
|
b278087410 | ||
|
|
a8e821b13b | ||
|
|
caa70317fa | ||
|
|
b8eaaabfe4 | ||
|
|
e4d80c415e | ||
|
|
553191c3ff | ||
|
|
ab385252b5 |
+1
-1
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8...3.30)
|
||||
|
||||
project(vitastor)
|
||||
|
||||
set(VITASTOR_VERSION "3.0.11")
|
||||
set(VITASTOR_VERSION "3.0.12")
|
||||
|
||||
include(CTest)
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VITASTOR_VERSION ?= v3.0.11
|
||||
VITASTOR_VERSION ?= v3.0.12
|
||||
|
||||
all: build push
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ spec:
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
allowPrivilegeEscalation: true
|
||||
image: vitalif/vitastor-csi:v3.0.11
|
||||
image: vitalif/vitastor-csi:v3.0.12
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
||||
@@ -121,7 +121,7 @@ spec:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
image: vitalif/vitastor-csi:v3.0.11
|
||||
image: vitalif/vitastor-csi:v3.0.12
|
||||
args:
|
||||
- "--node=$(NODE_ID)"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ package vitastor
|
||||
|
||||
const (
|
||||
vitastorCSIDriverName = "csi.vitastor.io"
|
||||
vitastorCSIDriverVersion = "3.0.11"
|
||||
vitastorCSIDriverVersion = "3.0.12"
|
||||
)
|
||||
|
||||
// Config struct fills the parameters of request or user input
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
vitastor (3.0.11-1) unstable; urgency=medium
|
||||
vitastor (3.0.12-1) unstable; urgency=medium
|
||||
|
||||
* Bugfixes
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
VITASTOR_VERSION ?= v3.0.11
|
||||
VITASTOR_VERSION ?= v3.0.12
|
||||
|
||||
all: build push
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
# Desired Vitastor version
|
||||
VITASTOR_VERSION=v3.0.11
|
||||
VITASTOR_VERSION=v3.0.12
|
||||
|
||||
# Additional arguments for all containers
|
||||
# For example, you may want to specify a custom logging driver here
|
||||
|
||||
@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
|
||||
The instruction is very simple.
|
||||
|
||||
1. Download a Docker image of the desired version: \
|
||||
`docker pull vitalif/vitastor:v3.0.11`
|
||||
`docker pull vitalif/vitastor:v3.0.12`
|
||||
2. Install scripts to the host system: \
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.11 install.sh`
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
|
||||
3. Reload udev rules: \
|
||||
`udevadm control --reload-rules`
|
||||
4. Enable the vitastor-host service: \
|
||||
|
||||
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
|
||||
Инструкция по установке максимально простая.
|
||||
|
||||
1. Скачайте Docker-образ желаемой версии: \
|
||||
`docker pull vitalif/vitastor:v3.0.11`
|
||||
`docker pull vitalif/vitastor:v3.0.12`
|
||||
2. Установите скрипты в хост-систему командой: \
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.11 install.sh`
|
||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.12 install.sh`
|
||||
3. Перезагрузите правила udev: \
|
||||
`udevadm control --reload-rules`
|
||||
4. Включите сервис vitastor-host: \
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
designated initializers support from C++20
|
||||
- CMake
|
||||
- jerasure headers and libraries
|
||||
- ISA-L, libibverbs and librdmacm headers and libraries (optional)
|
||||
- tcmalloc (google-perftools-dev)
|
||||
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
|
||||
|
||||
## Basic instructions
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
назначенных инициализаторов (designated initializers) из C++20
|
||||
- CMake
|
||||
- Заголовки и библиотеки jerasure
|
||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm
|
||||
- tcmalloc (google-perftools-dev)
|
||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
|
||||
|
||||
## Базовая инструкция
|
||||
|
||||
|
||||
+2
-2
@@ -627,7 +627,7 @@ class Mon
|
||||
if (this.state.pg.history[pool_id] &&
|
||||
this.state.pg.history[pool_id][pg])
|
||||
{
|
||||
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
|
||||
pg_history[pg-1] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
|
||||
}
|
||||
}
|
||||
const real_prev_pgs = [];
|
||||
@@ -719,7 +719,7 @@ class Mon
|
||||
this.next_recheck_timer = null;
|
||||
this.next_recheck_at = 0;
|
||||
this.schedule_recheck();
|
||||
}, now-this.next_recheck_at);
|
||||
}, (this.next_recheck_at-now)*1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vitastor-mon",
|
||||
"version": "3.0.11",
|
||||
"version": "3.0.12",
|
||||
"description": "Vitastor SDS monitor service",
|
||||
"main": "mon-main.js",
|
||||
"scripts": {
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
||||
finish_pg_history(merged_history[1]);
|
||||
for (let i = 0; i < new_pg_count; i++)
|
||||
{
|
||||
new_pg_history[i] = { ...merged_history[1] };
|
||||
new_pg_history[i] = JSON.parse(JSON.stringify(merged_history[1]));
|
||||
}
|
||||
}
|
||||
// Mark history keys for removed PGs as removed
|
||||
@@ -102,7 +102,7 @@ function scale_pg_count(prev_pgs, new_pg_count)
|
||||
{
|
||||
for (let i = prev_pgs.length; i < new_pg_count; i++)
|
||||
{
|
||||
prev_pgs[i] = prev_pgs[i % prev_pgs.length];
|
||||
prev_pgs[i] = [ ...prev_pgs[i % prev_pgs.length] ];
|
||||
}
|
||||
}
|
||||
else if (prev_pgs.length > new_pg_count)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vitastor",
|
||||
"version": "3.0.11",
|
||||
"version": "3.0.12",
|
||||
"description": "Low-level native bindings to Vitastor client library",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
|
||||
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
||||
from cinder.volume import driver
|
||||
from cinder.volume import volume_utils
|
||||
|
||||
VITASTOR_VERSION = '3.0.11'
|
||||
VITASTOR_VERSION = '3.0.12'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.11
|
||||
Version: 3.0.12
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.11.el10.tar.gz
|
||||
Source0: vitastor-3.0.12.el10.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.11
|
||||
Version: 3.0.12
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.11.el7.tar.gz
|
||||
Source0: vitastor-3.0.12.el7.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: devtoolset-9-gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.11
|
||||
Version: 3.0.12
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.11.el8.tar.gz
|
||||
Source0: vitastor-3.0.12.el8.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-toolset-9-gcc-c++
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Name: vitastor
|
||||
Version: 3.0.11
|
||||
Version: 3.0.12
|
||||
Release: 1%{?dist}
|
||||
Summary: Vitastor, a fast software-defined clustered block storage
|
||||
|
||||
License: Vitastor Network Public License 1.1
|
||||
URL: https://vitastor.io/
|
||||
Source0: vitastor-3.0.11.el9.tar.gz
|
||||
Source0: vitastor-3.0.12.el9.tar.gz
|
||||
|
||||
BuildRequires: gperftools-devel
|
||||
BuildRequires: gcc-c++
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||
endif()
|
||||
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
|
||||
|
||||
add_definitions(-DVITASTOR_VERSION="3.0.11")
|
||||
add_definitions(-DVITASTOR_VERSION="3.0.12")
|
||||
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
||||
add_link_options(-fno-omit-frame-pointer)
|
||||
if (${WITH_ASAN})
|
||||
|
||||
@@ -96,6 +96,7 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
||||
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
|
||||
gc_on_start = config.find("gc_on_start") == config.end() ||
|
||||
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
|
||||
skip_double_claim = (config["skip_double_claim"] == "true" || config["skip_double_claim"] == "1" || config["skip_double_claim"] == "yes");
|
||||
min_discard_size = parse_size(config["min_discard_size"]);
|
||||
if (!min_discard_size)
|
||||
min_discard_size = 1024*1024;
|
||||
|
||||
@@ -59,6 +59,8 @@ struct blockstore_disk_t
|
||||
bool discard_on_start = false;
|
||||
// GC on start (new store)
|
||||
bool gc_on_start = true;
|
||||
// Skip double claim conflicts on start (new store, temporary until the bug is found)
|
||||
bool skip_double_claim = false;
|
||||
uint64_t min_discard_size = 1024*1024;
|
||||
uint64_t discard_granularity = 0;
|
||||
|
||||
|
||||
@@ -174,14 +174,18 @@ bool journal_flusher_co::loop()
|
||||
else if (wait_state == 19) goto resume_19;
|
||||
else if (wait_state == 20) goto resume_20;
|
||||
else if (wait_state == 21) goto resume_21;
|
||||
else if (wait_state == 22) goto resume_22;
|
||||
else if (wait_state == 23) goto resume_23;
|
||||
else if (wait_state == 24) goto resume_24;
|
||||
else if (wait_state == 25) goto resume_25;
|
||||
resume_0:
|
||||
wait_state = 0;
|
||||
wait_count = 0;
|
||||
cur_oid = {};
|
||||
res = bs->heap->get_next_compact(cur_oid);
|
||||
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
||||
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0)
|
||||
{
|
||||
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
||||
bs->intent_write_counter = 0;
|
||||
resume_17:
|
||||
resume_18:
|
||||
@@ -196,6 +200,7 @@ resume_21:
|
||||
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
|
||||
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
|
||||
{
|
||||
// When under pressure, do an additional fsync to force entries to be marked compactable
|
||||
flusher->active_flushers++;
|
||||
resume_14:
|
||||
resume_15:
|
||||
@@ -259,11 +264,9 @@ resume_1:
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE ||
|
||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
|
||||
{
|
||||
auto res = bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
||||
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
|
||||
? COPY_BUF_SKIP_CSUM : 0);
|
||||
if (res > 0)
|
||||
copy_count++;
|
||||
}
|
||||
});
|
||||
if (!compact_info.compact_lsn)
|
||||
@@ -273,6 +276,25 @@ resume_1:
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
flusher->active_flushers++;
|
||||
for (i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||
{
|
||||
copy_count++;
|
||||
}
|
||||
}
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
init_fsync_data();
|
||||
}
|
||||
if (bs->log_level > 10)
|
||||
{
|
||||
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
||||
compact_info.clean_wr->version, compact_info.compact_version,
|
||||
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
||||
}
|
||||
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
||||
if (!bitmap_copied)
|
||||
{
|
||||
@@ -291,13 +313,6 @@ resume_1:
|
||||
csum_copy.clear();
|
||||
}
|
||||
clean_loc = compact_info.clean_wr->big_location(bs->heap);
|
||||
flusher->active_flushers++;
|
||||
if (bs->log_level > 10)
|
||||
{
|
||||
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
||||
compact_info.clean_wr->version, compact_info.compact_version,
|
||||
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
||||
}
|
||||
overwrite_start = overwrite_end = 0;
|
||||
if (read_vec.size() > 0)
|
||||
{
|
||||
@@ -336,6 +351,13 @@ resume_3:
|
||||
if (res == ENOENT || res == EDOM)
|
||||
{
|
||||
// Abort compaction
|
||||
abort_compact:
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
cur_sync->member_count--;
|
||||
if (cur_sync->member_count > 0)
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
flusher->flushing.erase(cur_oid);
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
flusher->active_flushers--;
|
||||
@@ -349,10 +371,7 @@ resume_4:
|
||||
if (res == ENOENT)
|
||||
{
|
||||
// Abort compaction
|
||||
flusher->flushing.erase(cur_oid);
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
flusher->active_flushers--;
|
||||
goto resume_0;
|
||||
goto abort_compact;
|
||||
}
|
||||
if (res == EAGAIN)
|
||||
{
|
||||
@@ -381,14 +400,14 @@ resume_9:
|
||||
for (i = 0; i < read_vec.size(); i++)
|
||||
{
|
||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED) ||
|
||||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
|
||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||
{
|
||||
assert(read_vec[i].buf);
|
||||
await_sqe(10);
|
||||
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
|
||||
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
|
||||
data->callback = simple_callback_w;
|
||||
assert(clean_loc + read_vec[i].offset + data->iov.iov_len <= bs->dsk.block_count*bs->dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
|
||||
wait_count++;
|
||||
}
|
||||
@@ -399,6 +418,17 @@ resume_11:
|
||||
wait_state = 11;
|
||||
return false;
|
||||
}
|
||||
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||
{
|
||||
resume_22:
|
||||
resume_23:
|
||||
resume_24:
|
||||
resume_25:
|
||||
if (!fsync_data(22))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Lock is only needed to prevent freeing the big_write because we overwrite it...
|
||||
bs->heap->unlock_entry(cur_oid);
|
||||
// Mark the object compacted, but don't free and remove small_writes
|
||||
@@ -408,12 +438,14 @@ resume_11:
|
||||
if (!cur_obj)
|
||||
{
|
||||
// Abort compaction
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
if (!calc_block_checksums())
|
||||
{
|
||||
// Abort compaction
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
@@ -422,6 +454,7 @@ resume_11:
|
||||
if (res == EBUSY)
|
||||
{
|
||||
// Abort compaction, object is already overwritten by something else
|
||||
flusher->active_flushers--;
|
||||
flusher->flushing.erase(cur_oid);
|
||||
goto resume_0;
|
||||
}
|
||||
@@ -586,7 +619,7 @@ int journal_flusher_co::check_and_punch_checksums()
|
||||
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
|
||||
}
|
||||
}
|
||||
// Modified, we should add_punch_holes and then write the block to disk
|
||||
// Modified, we should punch_holes and then write the block to disk
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
@@ -699,6 +732,67 @@ resume_1:
|
||||
return true;
|
||||
}
|
||||
|
||||
void journal_flusher_co::init_fsync_data()
|
||||
{
|
||||
cur_sync = flusher->data_syncs.begin();
|
||||
if (cur_sync == flusher->data_syncs.end() || cur_sync->ready_count > 0)
|
||||
{
|
||||
cur_sync = flusher->data_syncs.emplace(cur_sync);
|
||||
}
|
||||
cur_sync->member_count++;
|
||||
}
|
||||
|
||||
bool journal_flusher_co::fsync_data(int wait_base)
|
||||
{
|
||||
if (wait_state == wait_base)
|
||||
goto resume_0;
|
||||
else if (wait_state == wait_base+1)
|
||||
goto resume_1;
|
||||
else if (wait_state == wait_base+2)
|
||||
goto resume_2;
|
||||
else if (wait_state == wait_base+3)
|
||||
goto resume_3;
|
||||
cur_sync->ready_count++;
|
||||
resume_0:
|
||||
if (cur_sync->ready_count < cur_sync->member_count)
|
||||
{
|
||||
wait_state = wait_base;
|
||||
return false;
|
||||
}
|
||||
if (!cur_sync->sent)
|
||||
{
|
||||
// Sync batch is ready. Do it.
|
||||
await_sqe(1);
|
||||
data->iov = { 0 };
|
||||
data->callback = simple_callback_w;
|
||||
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||
cur_sync->sent = true;
|
||||
wait_count++;
|
||||
resume_2:
|
||||
if (wait_count > 0)
|
||||
{
|
||||
wait_state = wait_base+2;
|
||||
return false;
|
||||
}
|
||||
cur_sync->done = true;
|
||||
// Wake up other flushers
|
||||
bs->ringloop->wakeup();
|
||||
}
|
||||
resume_3:
|
||||
if (!cur_sync->done)
|
||||
{
|
||||
wait_state = wait_base+3;
|
||||
return false;
|
||||
}
|
||||
cur_sync->done_count++;
|
||||
if (cur_sync->done_count >= cur_sync->member_count)
|
||||
{
|
||||
flusher->data_syncs.erase(cur_sync);
|
||||
cur_sync = flusher->data_syncs.end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool journal_flusher_co::fsync_meta(int wait_base)
|
||||
{
|
||||
if (wait_state == wait_base) goto resume_0;
|
||||
|
||||
@@ -25,6 +25,15 @@ struct flusher_meta_write_t
|
||||
std::map<uint64_t, meta_sector_t>::iterator it;
|
||||
};
|
||||
|
||||
struct flusher_data_sync_t
|
||||
{
|
||||
int member_count = 0;
|
||||
int ready_count = 0;
|
||||
int done_count = 0;
|
||||
bool sent = false;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
class journal_flusher_t;
|
||||
|
||||
// Journal flusher coroutine
|
||||
@@ -58,6 +67,7 @@ class journal_flusher_co
|
||||
int i, res;
|
||||
bool read_to_fill_incomplete;
|
||||
int copy_count;
|
||||
std::list<flusher_data_sync_t>::iterator cur_sync;
|
||||
|
||||
friend class journal_flusher_t;
|
||||
|
||||
@@ -68,6 +78,8 @@ class journal_flusher_co
|
||||
bool calc_block_checksums();
|
||||
bool write_meta_block(int wait_base);
|
||||
bool read_buffered(int wait_base);
|
||||
void init_fsync_data();
|
||||
bool fsync_data(int wait_base);
|
||||
bool fsync_meta(int wait_base);
|
||||
bool fsync_buffer(int wait_base);
|
||||
bool trim_lsn(int wait_base);
|
||||
@@ -88,6 +100,7 @@ class journal_flusher_t
|
||||
|
||||
robin_hood::unordered_flat_set<object_id> flushing;
|
||||
int active_flushers = 0;
|
||||
std::list<flusher_data_sync_t> data_syncs;
|
||||
int wanting_meta_fsync = 0;
|
||||
bool fsyncing_meta = false;
|
||||
int syncing_buffer = 0;
|
||||
|
||||
+333
-178
@@ -28,6 +28,7 @@
|
||||
|
||||
#define IMAP_MALLOC_LOW_BITS ((size_t)0x0F)
|
||||
#define IMAP_MAX_LOW 16
|
||||
#define POSTPONE_INSERT_COUNT 10
|
||||
|
||||
#define list_item_overhead(a) (((a) + sizeof(heap_list_item_t) - sizeof(heap_entry_t) + sizeof(void*) + 15) & ~15)
|
||||
|
||||
@@ -126,26 +127,26 @@ uint32_t heap_entry_t::get_size(blockstore_heap_t *heap)
|
||||
return heap->get_simple_entry_size();
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_overwrite()
|
||||
bool heap_entry_t::is_overwrite() const
|
||||
{
|
||||
return ((entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_BIG_WRITE|BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_BIG_INTENT|BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_DELETE|BS_HEAP_STABLE));
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_compactable()
|
||||
bool heap_entry_t::is_compactable() const
|
||||
{
|
||||
return !is_overwrite() && (entry_type & BS_HEAP_STABLE) ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == BS_HEAP_COMMIT ||
|
||||
(entry_type & ~BS_HEAP_GARBAGE) == BS_HEAP_ROLLBACK;
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_before(heap_entry_t *other)
|
||||
bool heap_entry_t::is_before(const heap_entry_t *other) const
|
||||
{
|
||||
return lsn < other->lsn || lsn == other->lsn && !is_overwrite() && other->is_overwrite();
|
||||
}
|
||||
|
||||
bool heap_entry_t::is_garbage()
|
||||
bool heap_entry_t::is_garbage() const
|
||||
{
|
||||
return (entry_type & BS_HEAP_GARBAGE);
|
||||
}
|
||||
@@ -408,6 +409,13 @@ corrupted_object:
|
||||
wr->inode, wr->stripe, wr->version, wr->big_intent().offset, wr->big_intent().len);
|
||||
goto corrupted_object;
|
||||
}
|
||||
if ((wr->type() == BS_HEAP_BIG_INTENT || wr->type() == BS_HEAP_BIG_WRITE) &&
|
||||
wr->big().block_num >= dsk->block_count)
|
||||
{
|
||||
fprintf(stderr, "Error: big_write or big_intent entry %jx:%jx v%ju block_num is too large: %u > %lu. Metadata is incompatible with current parameters. ",
|
||||
wr->inode, wr->stripe, wr->version, wr->big_intent().block_num, dsk->block_count);
|
||||
goto corrupted_object;
|
||||
}
|
||||
handle_write(block_num, wr);
|
||||
block_offset += wr->size;
|
||||
}
|
||||
@@ -434,7 +442,7 @@ int blockstore_heap_t::load_blocks(uint64_t disk_offset, uint64_t size, uint8_t
|
||||
next_lsn = wr->lsn;
|
||||
}
|
||||
entries_loaded++;
|
||||
loaded_list_items.push_back(li);
|
||||
insert_list_items(&li, 1, true);
|
||||
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
if (!inf.entries.size())
|
||||
@@ -540,18 +548,26 @@ bool blockstore_heap_t::validate_object(heap_entry_t *obj)
|
||||
|
||||
void blockstore_heap_t::finish_load()
|
||||
{
|
||||
if (loaded_list_items.size())
|
||||
if (postponed_items.size())
|
||||
{
|
||||
// Sort everything and load in correct order
|
||||
std::sort(loaded_list_items.begin(), loaded_list_items.end(), [this](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||
// Sort "postponed" items and load in batches
|
||||
std::sort(postponed_items.begin(), postponed_items.end(), [this](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||
{
|
||||
return a->entry.lsn < b->entry.lsn;
|
||||
return a->entry.inode < b->entry.inode || a->entry.inode == b->entry.inode &&
|
||||
(a->entry.stripe < b->entry.stripe || a->entry.stripe == b->entry.stripe &&
|
||||
!a->entry.is_before(&b->entry)); // object ASC, lsn DESC
|
||||
});
|
||||
for (auto & li: loaded_list_items)
|
||||
size_t s = 0, e, n = postponed_items.size();
|
||||
for (e = 1; e <= n; e++)
|
||||
{
|
||||
insert_list_item(li);
|
||||
if (e >= n || postponed_items[e]->entry.inode != postponed_items[s]->entry.inode &&
|
||||
postponed_items[e]->entry.stripe != postponed_items[s]->entry.stripe)
|
||||
{
|
||||
insert_list_items(postponed_items.data()+s, e-s, false);
|
||||
s = e;
|
||||
}
|
||||
}
|
||||
loaded_list_items.clear();
|
||||
postponed_items.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,26 +580,11 @@ void blockstore_heap_t::fill_recheck_queue()
|
||||
inode_map_iterate(ip.second, [&](heap_list_item_t *li)
|
||||
{
|
||||
auto obj = &li->entry;
|
||||
// Add object to recheck queue
|
||||
if (obj->type() == BS_HEAP_INTENT_WRITE || obj->type() == BS_HEAP_BIG_INTENT)
|
||||
// Recheck only the latest intent_write (if after completed_lsn) or a series of small_writes
|
||||
if ((obj->type() == BS_HEAP_INTENT_WRITE || obj->type() == BS_HEAP_BIG_INTENT)
|
||||
&& obj->lsn > completed_lsn || obj->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
// Recheck only the latest intent_write
|
||||
if (obj->lsn > completed_lsn)
|
||||
{
|
||||
// Do not recheck if it's already marked as completed in the superblock
|
||||
recheck_queue.push_back(obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Or recheck a series of small_writes
|
||||
for (auto wr = obj; wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
{
|
||||
if (wr->small().len > 0)
|
||||
{
|
||||
recheck_queue.push_back(wr);
|
||||
}
|
||||
}
|
||||
recheck_queue.push_back(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -593,6 +594,11 @@ void blockstore_heap_t::fill_recheck_queue()
|
||||
int blockstore_heap_t::mark_used_blocks()
|
||||
{
|
||||
int res = 0;
|
||||
std::vector<heap_list_item_t*> used_by;
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
used_by.resize(dsk->block_count);
|
||||
}
|
||||
for (auto & pgp: block_index)
|
||||
{
|
||||
for (auto & ip: pgp.second)
|
||||
@@ -646,10 +652,34 @@ int blockstore_heap_t::mark_used_blocks()
|
||||
{
|
||||
if (is_data_used(wr->big_location(this)))
|
||||
{
|
||||
fprintf(stderr, "Error: double-claimed data block %u, second time by %jx:%jx l%ju\n",
|
||||
wr->big().block_num, wr->inode, wr->stripe, wr->lsn);
|
||||
res = EDOM;
|
||||
return;
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
// There is a BUG currently:
|
||||
// Sometimes (under unknown conditions) deletion entries are removed from the disk
|
||||
// earlier than previous big_writes.
|
||||
// Until it's fixed, we provide a way to ignore such objects on start.
|
||||
auto prev_li = used_by[wr->big().block_num];
|
||||
assert(prev_li);
|
||||
// Newer LSN must be trusted. Remove the older object.
|
||||
fprintf(stderr, "Block %u is double-claimed by entries %jx:%jx l%ju and %jx:%jx l%ju\n",
|
||||
wr->big().block_num, prev_li->entry.inode, prev_li->entry.stripe, prev_li->entry.lsn, wr->inode, wr->stripe, wr->lsn);
|
||||
if (init_erase_double_claim(prev_li, li))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: double-claimed data block %u, second time by %jx:%jx l%ju\n",
|
||||
wr->big().block_num, wr->inode, wr->stripe, wr->lsn);
|
||||
res = EDOM;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (dsk->skip_double_claim)
|
||||
{
|
||||
// Record the object which uses the data block
|
||||
used_by[wr->big().block_num] = li;
|
||||
}
|
||||
use_data(wr->inode, wr->big_location(this));
|
||||
}
|
||||
@@ -673,6 +703,116 @@ int blockstore_heap_t::mark_used_blocks()
|
||||
return res;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::init_free_bad_entry(heap_entry_t *wr)
|
||||
{
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
free_buffer_area(wr->inode, wr->small().location, wr->small().len);
|
||||
}
|
||||
else if (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
free_data(wr->inode, wr->big_location(this));
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::init_erase_bad_entry(heap_list_item_t *li)
|
||||
{
|
||||
modify_alloc(li->block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
for (size_t i = 0; i < inf.entries.size(); i++)
|
||||
{
|
||||
if (inf.entries[i] == li)
|
||||
{
|
||||
inf.entries.erase(inf.entries.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
inf.used_space -= li->entry.size;
|
||||
inf.garbage_space -= (li->entry.is_garbage() ? li->entry.size : 0);
|
||||
});
|
||||
recheck_modified_blocks.insert(li->block_num);
|
||||
unlink_list_item(li);
|
||||
}
|
||||
|
||||
bool blockstore_heap_t::init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li)
|
||||
{
|
||||
bool erase_prev = false;
|
||||
bool erase_cur = false;
|
||||
if (prev_li->entry.lsn < cur_li->entry.lsn)
|
||||
{
|
||||
erase_prev = true;
|
||||
auto latest_li = prev_li;
|
||||
while (latest_li->next)
|
||||
{
|
||||
latest_li = latest_li->next;
|
||||
}
|
||||
if (latest_li->entry.lsn >= cur_li->entry.lsn)
|
||||
{
|
||||
// LSN ranges intersect, erase both
|
||||
erase_cur = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
erase_cur = true;
|
||||
auto latest_li = cur_li;
|
||||
while (latest_li->next)
|
||||
{
|
||||
latest_li = latest_li->next;
|
||||
}
|
||||
if ((latest_li->entry.inode != prev_li->entry.inode ||
|
||||
latest_li->entry.stripe != prev_li->entry.stripe) &&
|
||||
latest_li->entry.lsn >= prev_li->entry.lsn)
|
||||
{
|
||||
// LSN ranges intersect, erase both
|
||||
erase_prev = true;
|
||||
}
|
||||
}
|
||||
if (erase_prev)
|
||||
{
|
||||
fprintf(stderr, "Erasing object %jx:%jx due to double-claim\n", prev_li->entry.inode, prev_li->entry.stripe);
|
||||
auto erase_li = prev_li;
|
||||
while (erase_li->next)
|
||||
{
|
||||
erase_li = erase_li->next;
|
||||
}
|
||||
bool overwritten = false;
|
||||
while (erase_li)
|
||||
{
|
||||
auto prev_erase_li = erase_li->prev;
|
||||
if (!overwritten)
|
||||
{
|
||||
init_free_bad_entry(&erase_li->entry);
|
||||
overwritten = erase_li->entry.is_overwrite();
|
||||
}
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = prev_erase_li;
|
||||
}
|
||||
}
|
||||
if (erase_cur)
|
||||
{
|
||||
fprintf(stderr, "Erasing object %jx:%jx due to double-claim\n", cur_li->entry.inode, cur_li->entry.stripe);
|
||||
auto erase_li = cur_li->next;
|
||||
while (erase_li)
|
||||
{
|
||||
// Only newer entries are marked as used
|
||||
auto next_erase_li = erase_li->next;
|
||||
init_free_bad_entry(&erase_li->entry);
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = next_erase_li;
|
||||
}
|
||||
erase_li = cur_li;
|
||||
// Older ones are not
|
||||
while (erase_li)
|
||||
{
|
||||
auto prev_erase_li = erase_li->prev;
|
||||
init_erase_bad_entry(erase_li);
|
||||
erase_li = prev_erase_li;
|
||||
}
|
||||
}
|
||||
return erase_cur;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_full_gc()
|
||||
{
|
||||
uint32_t block_num = 0;
|
||||
@@ -713,80 +853,98 @@ void blockstore_heap_t::recheck_full_gc()
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_buffer(heap_entry_t *cwr, uint8_t *buf)
|
||||
void blockstore_heap_t::recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr)
|
||||
{
|
||||
auto free_entry = [&](heap_list_item_t *li)
|
||||
// write entry is invalid, erase it and all newer entries
|
||||
int bad_count = 1;
|
||||
for (auto wr = obj; wr && wr != bad_wr; wr = prev(wr))
|
||||
{
|
||||
uint32_t block_num = li->block_num;
|
||||
auto wr_size = li->entry.size;
|
||||
if (li->entry.is_garbage())
|
||||
{
|
||||
garbage_entries--;
|
||||
garbage_memory -= list_item_overhead(wr_size);
|
||||
}
|
||||
live_entries--;
|
||||
live_memory -= list_item_overhead(wr_size);
|
||||
free(li);
|
||||
modify_alloc(block_num, [&](heap_block_info_t & inf)
|
||||
{
|
||||
inf.used_space -= wr_size;
|
||||
bool found = false;
|
||||
for (auto it = inf.entries.begin(); it != inf.entries.end(); it++)
|
||||
{
|
||||
if (*it == li)
|
||||
{
|
||||
found = true;
|
||||
inf.entries.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
});
|
||||
recheck_modified_blocks.insert(block_num);
|
||||
};
|
||||
if (cwr->is_garbage())
|
||||
{
|
||||
// already freed after rechecking one of the previous small_write entries
|
||||
free_entry(list_item(cwr));
|
||||
bad_count++;
|
||||
}
|
||||
else if (!calc_checksums(cwr, buf, false))
|
||||
auto prev_wr = prev(bad_wr);
|
||||
if (prev_wr)
|
||||
{
|
||||
// write entry is invalid, erase it and mark newer entries with garbage bit
|
||||
auto & pg_idx = block_index[get_pg_id(cwr->inode, cwr->stripe)];
|
||||
auto & inode_idx = pg_idx[cwr->inode];
|
||||
heap_inode_map_t::iterator li_it;
|
||||
heap_list_item_t *li = NULL;
|
||||
inode_map_get(inode_idx, li_it, li, cwr->stripe);
|
||||
int rolled_back = 1;
|
||||
while (li && cwr != &li->entry)
|
||||
fprintf(stderr, "Notice: %u unfinished %s to %jx:%jx v%ju since good lsn %ju, rolling back\n",
|
||||
bad_count, bad_count > 1 ? "writes" : "write", obj->inode, obj->stripe, obj->version, prev_wr->lsn);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n", obj->inode, obj->stripe);
|
||||
}
|
||||
auto li = list_item(obj);
|
||||
while (li && prev_wr != &li->entry)
|
||||
{
|
||||
auto prev = li->prev;
|
||||
assert(li->entry.type() == bad_wr->type());
|
||||
init_erase_bad_entry(li);
|
||||
li = prev;
|
||||
}
|
||||
}
|
||||
|
||||
void blockstore_heap_t::recheck_start_reads(heap_recheck_state_t *st)
|
||||
{
|
||||
if (st->sent_reads >= st->total_reads)
|
||||
return;
|
||||
while (recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
auto wr = st->next_wr;
|
||||
st->next_wr = prev(st->next_wr);
|
||||
uint64_t loc = 0, len = 0;
|
||||
bool from_data = false;
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
assert(li->entry.entry_type == cwr->entry_type);
|
||||
auto prev = li->prev;
|
||||
li->next = li->prev = NULL;
|
||||
if (!li->entry.is_garbage())
|
||||
{
|
||||
garbage_entries++;
|
||||
garbage_memory += list_item_overhead(li->entry.size);
|
||||
li->entry.set_garbage();
|
||||
}
|
||||
li = prev;
|
||||
rolled_back++;
|
||||
loc = wr->small().location;
|
||||
len = wr->small().len;
|
||||
}
|
||||
assert(li);
|
||||
if (li->prev)
|
||||
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
fprintf(stderr, "Notice: %u unfinished %s to %jx:%jx v%ju since lsn %ju, rolling back\n",
|
||||
rolled_back, rolled_back > 1 ? "writes" : "write", cwr->inode, cwr->stripe, li->prev->entry.version, li->entry.lsn);
|
||||
inode_map_replace(inode_idx, li_it, li->prev);
|
||||
li->prev->next = NULL;
|
||||
auto & bi = wr->big_intent();
|
||||
loc = (uint64_t)bi.block_num * dsk->data_block_size + bi.offset;
|
||||
len = bi.len;
|
||||
from_data = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Notice: the whole object %jx:%jx only has unfinished writes, rolling back\n",
|
||||
cwr->inode, cwr->stripe);
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, li);
|
||||
assert(wr->type() == BS_HEAP_INTENT_WRITE);
|
||||
auto prev_wr = prev(wr);
|
||||
while (prev_wr && prev_wr->entry_type == wr->entry_type)
|
||||
{
|
||||
// Skip other intent_writes
|
||||
prev_wr = prev(prev_wr);
|
||||
}
|
||||
if (!prev_wr || prev_wr->entry_type != (BS_HEAP_BIG_WRITE | (wr->entry_type & BS_HEAP_STABLE)) &&
|
||||
prev_wr->entry_type != (BS_HEAP_BIG_INTENT | (wr->entry_type & BS_HEAP_STABLE)))
|
||||
{
|
||||
fprintf(stderr, "Error: intent_write entry %jx:%jx v%ju l%ju is not written over a big_write\n",
|
||||
wr->inode, wr->stripe, wr->version, wr->lsn);
|
||||
exit(1);
|
||||
}
|
||||
loc = wr->small().offset + prev_wr->big_location(this);
|
||||
len = wr->small().len;
|
||||
from_data = true;
|
||||
}
|
||||
free_entry(li);
|
||||
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, len);
|
||||
st->sent_reads++;
|
||||
recheck_in_progress++;
|
||||
recheck_pending_reads--;
|
||||
bool is_last = st->sent_reads >= st->total_reads;
|
||||
recheck_cb(from_data, loc, len, buf, [this, st, wr, buf]()
|
||||
{
|
||||
st->checked_reads++;
|
||||
if (!calc_checksums(wr, buf, false))
|
||||
st->bad_wr = !st->bad_wr || st->bad_wr->lsn > wr->lsn ? wr : st->bad_wr;
|
||||
if (st->checked_reads >= st->total_reads)
|
||||
{
|
||||
if (st->bad_wr)
|
||||
recheck_drop_entries(st->obj, st->bad_wr);
|
||||
recheck_states.erase(st->obj);
|
||||
}
|
||||
free(buf);
|
||||
recheck_in_progress--;
|
||||
recheck_small_writes(NULL, 0);
|
||||
});
|
||||
if (is_last)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,70 +967,47 @@ bool blockstore_heap_t::recheck_small_writes(std::function<void(bool is_data, ui
|
||||
recheck_queue_depth = queue_depth;
|
||||
}
|
||||
in_recheck = true;
|
||||
while (recheck_pending_reads > 0 && recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
for (auto & sp: recheck_states)
|
||||
recheck_start_reads(&sp.second);
|
||||
}
|
||||
while (recheck_queue.size() > 0 && recheck_in_progress < recheck_queue_depth)
|
||||
{
|
||||
heap_entry_t *wr = recheck_queue.front();
|
||||
heap_entry_t *obj = recheck_queue.front();
|
||||
recheck_queue.pop_front();
|
||||
bool from_data = false;
|
||||
uint64_t loc = 0;
|
||||
uint32_t len = 0;
|
||||
if (wr->type() == BS_HEAP_INTENT_WRITE)
|
||||
if (obj->type() == BS_HEAP_SMALL_WRITE && buffer_area)
|
||||
{
|
||||
auto prev_wr = prev(wr);
|
||||
while (prev_wr && prev_wr->entry_type == wr->entry_type)
|
||||
// Check this object synchronously
|
||||
heap_entry_t *bad_wr = NULL;
|
||||
for (auto wr = obj; wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
{
|
||||
// Skip other intent_writes
|
||||
prev_wr = prev(prev_wr);
|
||||
fprintf(stderr, "Notice: rechecking %jx:%jx l%ju - %u bytes at %ju in buffer area\n",
|
||||
wr->inode, wr->stripe, wr->lsn, wr->small().len, wr->small().location);
|
||||
if (!calc_checksums(wr, buffer_area + wr->small().location, false))
|
||||
bad_wr = wr;
|
||||
}
|
||||
if (!prev_wr || prev_wr->entry_type != (BS_HEAP_BIG_WRITE | (wr->entry_type & BS_HEAP_STABLE)) &&
|
||||
prev_wr->entry_type != (BS_HEAP_BIG_INTENT | (wr->entry_type & BS_HEAP_STABLE)))
|
||||
{
|
||||
fprintf(stderr, "Error: intent_write entry %jx:%jx v%ju l%ju is not written over a big_write\n",
|
||||
wr->inode, wr->stripe, wr->version, wr->lsn);
|
||||
exit(1);
|
||||
}
|
||||
loc = wr->small().offset + prev_wr->big_location(this);
|
||||
len = wr->small().len;
|
||||
from_data = true;
|
||||
}
|
||||
else if (wr->type() == BS_HEAP_BIG_INTENT)
|
||||
{
|
||||
auto & bi = wr->big_intent();
|
||||
loc = (uint64_t)bi.block_num * dsk->data_block_size + bi.offset;
|
||||
len = bi.len;
|
||||
from_data = true;
|
||||
if (bad_wr)
|
||||
recheck_drop_entries(obj, bad_wr);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(wr->type() == BS_HEAP_SMALL_WRITE);
|
||||
loc = wr->small().location;
|
||||
len = wr->small().len;
|
||||
}
|
||||
if (log_level > 5)
|
||||
{
|
||||
fprintf(stderr, "Notice: rechecking %jx:%jx l%ju - %u bytes at %ju in %s area\n",
|
||||
wr->inode, wr->stripe, wr->lsn, len, loc, from_data ? "data" : "buffer");
|
||||
}
|
||||
if (!from_data && buffer_area)
|
||||
{
|
||||
recheck_buffer(wr, buffer_area+loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
recheck_in_progress++;
|
||||
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, len);
|
||||
recheck_cb(from_data, loc, len, buf, [this, wr, buf]()
|
||||
{
|
||||
recheck_buffer(wr, buf);
|
||||
free(buf);
|
||||
recheck_in_progress--;
|
||||
recheck_small_writes(NULL, 0);
|
||||
});
|
||||
// Recheck will be asynchronous. Create state and start it
|
||||
auto & st = recheck_states[obj];
|
||||
st.obj = obj;
|
||||
st.next_wr = obj;
|
||||
st.total_reads = 1;
|
||||
if (obj->type() == BS_HEAP_SMALL_WRITE)
|
||||
for (auto wr = prev(obj); wr && wr->type() == BS_HEAP_SMALL_WRITE; wr = prev(wr))
|
||||
st.total_reads++;
|
||||
recheck_pending_reads += st.total_reads;
|
||||
recheck_start_reads(&st);
|
||||
}
|
||||
}
|
||||
in_recheck = false;
|
||||
if (!recheck_queue.size() && !recheck_in_progress)
|
||||
{
|
||||
assert(!recheck_states.size());
|
||||
auto cb = std::move(recheck_cb);
|
||||
recheck_queue_depth = 0;
|
||||
if (cb)
|
||||
@@ -1328,43 +1463,51 @@ int blockstore_heap_t::allocate_entry(uint32_t entry_size, uint32_t *block_num,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blockstore_heap_t::insert_list_item(heap_list_item_t *li)
|
||||
void blockstore_heap_t::insert_list_items(heap_list_item_t** v, size_t count, bool postpone)
|
||||
{
|
||||
auto & inode_idx = block_index[get_pg_id(li->entry.inode, li->entry.stripe)][li->entry.inode];
|
||||
auto wr = &v[0]->entry;
|
||||
auto & inode_idx = block_index[get_pg_id(wr->inode, wr->stripe)][wr->inode];
|
||||
heap_inode_map_t::iterator li_it;
|
||||
heap_list_item_t *old_head = NULL;
|
||||
if (inode_idx)
|
||||
inode_map_get(inode_idx, li_it, old_head, li->entry.stripe);
|
||||
if (old_head && !old_head->entry.is_before(&li->entry))
|
||||
inode_map_get(inode_idx, li_it, old_head, wr->stripe);
|
||||
heap_list_item_t *next_li = NULL;
|
||||
heap_list_item_t *prev_li = old_head;
|
||||
int skips = 0;
|
||||
// Merge entry array and inode_idx linked list (both sorted in newest first order)
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
// BIG_WRITE may be inserted into the middle of the sequence during compaction
|
||||
// and it overrides SMALL_WRITEs and COMMITs with the same LSN
|
||||
// However, all entries of other types (say DELETE) override previous ones
|
||||
auto next_li = old_head;
|
||||
auto prev_li = old_head->prev;
|
||||
auto li = v[i];
|
||||
while (prev_li && !prev_li->entry.is_before(&li->entry))
|
||||
{
|
||||
next_li = prev_li;
|
||||
prev_li = prev_li->prev;
|
||||
skips++;
|
||||
}
|
||||
if (postpone && skips > POSTPONE_INSERT_COUNT)
|
||||
{
|
||||
postponed_items.push_back(li);
|
||||
return;
|
||||
}
|
||||
if (next_li == NULL)
|
||||
{
|
||||
// Replace the latest entry pointer
|
||||
if (old_head)
|
||||
inode_map_replace(inode_idx, li_it, li);
|
||||
else
|
||||
inode_map_put(inode_idx, li);
|
||||
}
|
||||
// Insert <li> between <next_li> and <prev_li>
|
||||
li->next = next_li;
|
||||
if (next_li)
|
||||
next_li->prev = li;
|
||||
li->prev = prev_li;
|
||||
if (prev_li)
|
||||
prev_li->next = li;
|
||||
next_li->prev = li;
|
||||
li->next = next_li;
|
||||
}
|
||||
else
|
||||
{
|
||||
li->prev = old_head;
|
||||
li->next = NULL;
|
||||
if (old_head)
|
||||
{
|
||||
old_head->next = li;
|
||||
inode_map_replace(inode_idx, li_it, li);
|
||||
}
|
||||
else
|
||||
inode_map_put(inode_idx, li);
|
||||
next_li = li;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1396,7 +1539,7 @@ int blockstore_heap_t::add_entry(uint32_t wr_size, uint32_t *modified_block,
|
||||
(explicit_complete ? HEAP_INFLIGHT_EXPLICIT : 0) |
|
||||
(new_wr->is_overwrite() ? HEAP_INFLIGHT_COMPACTED : 0) |
|
||||
(new_wr->is_compactable() ? HEAP_INFLIGHT_COMPACTABLE : 0));
|
||||
insert_list_item(li);
|
||||
insert_list_items(&li, 1, false);
|
||||
li->block_num = block_num;
|
||||
new_wr->size = wr_size;
|
||||
new_wr->crc32c = new_wr->calc_crc32c();
|
||||
@@ -1592,6 +1735,7 @@ int blockstore_heap_t::punch_holes(heap_entry_t *wr, uint8_t *new_bitmap, uint8_
|
||||
*modified_block = block_num;
|
||||
memcpy(wr->get_int_bitmap(this), new_bitmap, dsk->clean_entry_bitmap_size);
|
||||
memcpy(wr->get_checksums(this), new_csums, dsk->data_block_size/dsk->csum_block_size*(dsk->data_csum_type & 0xFF));
|
||||
wr->crc32c = wr->calc_crc32c();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2390,6 +2534,22 @@ void blockstore_heap_t::apply_inflight(heap_inflight_lsn_t & inflight)
|
||||
}
|
||||
|
||||
void blockstore_heap_t::remove_list_item(heap_list_item_t *li)
|
||||
{
|
||||
if (!li->next)
|
||||
{
|
||||
// The last freed entry must be a deletion
|
||||
assert(!li->prev);
|
||||
assert((li->entry.entry_type & ~BS_HEAP_GARBAGE) == (BS_HEAP_DELETE|BS_HEAP_STABLE));
|
||||
}
|
||||
else if (!li->prev && li->next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE))
|
||||
{
|
||||
// free BS_HEAP_DELETEs when all previous entries are also freed
|
||||
mark_garbage(li->next->block_num, &li->next->entry, UINT32_MAX);
|
||||
}
|
||||
unlink_list_item(li);
|
||||
}
|
||||
|
||||
void blockstore_heap_t::unlink_list_item(heap_list_item_t *li)
|
||||
{
|
||||
auto prev = li->prev;
|
||||
auto next = li->next;
|
||||
@@ -2399,25 +2559,20 @@ void blockstore_heap_t::remove_list_item(heap_list_item_t *li)
|
||||
}
|
||||
if (!next)
|
||||
{
|
||||
// The last freed entry must be a deletion
|
||||
assert(!prev);
|
||||
auto wr = &li->entry;
|
||||
assert(wr->entry_type == BS_HEAP_DELETE|BS_HEAP_STABLE);
|
||||
auto & pg_idx = block_index[get_pg_id(wr->inode, wr->stripe)];
|
||||
auto & inode_idx = pg_idx[wr->inode];
|
||||
heap_inode_map_t::iterator li_it;
|
||||
heap_list_item_t *old_li = NULL;
|
||||
inode_map_get(inode_idx, li_it, old_li, wr->stripe);
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, old_li);
|
||||
if (!prev)
|
||||
inode_map_erase(pg_idx, inode_idx, li_it, old_li);
|
||||
else
|
||||
inode_map_replace(inode_idx, li_it, prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
next->prev = prev;
|
||||
if (!prev && next->entry.entry_type == (BS_HEAP_DELETE|BS_HEAP_STABLE))
|
||||
{
|
||||
// free BS_HEAP_DELETEs when all previous entries are also freed
|
||||
mark_garbage(next->block_num, &next->entry, UINT32_MAX);
|
||||
}
|
||||
}
|
||||
if (li->entry.is_garbage())
|
||||
{
|
||||
|
||||
@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
|
||||
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
|
||||
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
|
||||
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
|
||||
bool is_garbage();
|
||||
bool is_garbage() const;
|
||||
void set_garbage();
|
||||
bool is_overwrite();
|
||||
bool is_compactable();
|
||||
bool is_before(heap_entry_t *other);
|
||||
bool is_overwrite() const;
|
||||
bool is_compactable() const;
|
||||
bool is_before(const heap_entry_t *other) const;
|
||||
uint32_t get_size(blockstore_heap_t *heap);
|
||||
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
|
||||
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
|
||||
@@ -158,6 +158,16 @@ struct heap_li_equal
|
||||
}
|
||||
};
|
||||
|
||||
struct heap_recheck_state_t
|
||||
{
|
||||
heap_entry_t *obj = NULL;
|
||||
heap_entry_t *next_wr = NULL;
|
||||
size_t total_reads = 0;
|
||||
size_t sent_reads = 0;
|
||||
size_t checked_reads = 0;
|
||||
heap_entry_t *bad_wr = NULL;
|
||||
};
|
||||
|
||||
using i64hash_t = robin_hood::hash<uint64_t>;
|
||||
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
|
||||
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
|
||||
@@ -208,9 +218,11 @@ class blockstore_heap_t
|
||||
|
||||
bool marked_used_blocks = false;
|
||||
bool recheck_queue_filled = false;
|
||||
std::vector<heap_list_item_t*> loaded_list_items;
|
||||
std::vector<heap_list_item_t*> postponed_items;
|
||||
std::set<uint32_t> recheck_modified_blocks;
|
||||
std::deque<heap_entry_t*> recheck_queue;
|
||||
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
|
||||
size_t recheck_pending_reads = 0;
|
||||
int recheck_in_progress = 0;
|
||||
bool in_recheck = false;
|
||||
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
|
||||
@@ -219,7 +231,12 @@ class blockstore_heap_t
|
||||
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
|
||||
bool validate_object(heap_entry_t *obj);
|
||||
void fill_recheck_queue();
|
||||
void recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr);
|
||||
void recheck_start_reads(heap_recheck_state_t *st);
|
||||
int mark_used_blocks();
|
||||
void init_free_bad_entry(heap_entry_t *wr);
|
||||
void init_erase_bad_entry(heap_list_item_t *li);
|
||||
bool init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li);
|
||||
void recheck_full_gc();
|
||||
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
|
||||
void defragment_block(uint32_t block_num);
|
||||
@@ -227,8 +244,9 @@ class blockstore_heap_t
|
||||
|
||||
void gc_block(heap_block_info_t & inf);
|
||||
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free);
|
||||
void insert_list_item(heap_list_item_t *li);
|
||||
void insert_list_items(heap_list_item_t** v, size_t count, bool postpone);
|
||||
void remove_list_item(heap_list_item_t *li);
|
||||
void unlink_list_item(heap_list_item_t *li);
|
||||
int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free,
|
||||
bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
|
||||
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
|
||||
|
||||
@@ -101,6 +101,7 @@ void blockstore_impl_t::loop()
|
||||
unsigned initial_ring_space = ringloop->space_left();
|
||||
int op_idx = 0, new_idx = 0;
|
||||
bool has_unfinished_writes = false;
|
||||
bool has_unfinished_sync = false;
|
||||
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
||||
{
|
||||
auto op = submit_queue[op_idx];
|
||||
@@ -138,7 +139,13 @@ void blockstore_impl_t::loop()
|
||||
else if (op->opcode == BS_OP_SYNC)
|
||||
{
|
||||
// syncs only completed writes, so doesn't have to be blocked by anything
|
||||
wr_st = continue_sync(op);
|
||||
if (!has_unfinished_sync)
|
||||
{
|
||||
wr_st = continue_sync(op);
|
||||
has_unfinished_sync = (wr_st != 2);
|
||||
}
|
||||
else
|
||||
wr_st = 0;
|
||||
}
|
||||
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
|
||||
{
|
||||
@@ -154,9 +161,7 @@ void blockstore_impl_t::loop()
|
||||
wr_st = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
wr_st = 0;
|
||||
}
|
||||
}
|
||||
if (wr_st == 2)
|
||||
{
|
||||
|
||||
@@ -153,6 +153,14 @@ resume_1:
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
uint32_t csum = hdr->header_csum;
|
||||
hdr->header_csum = 0;
|
||||
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
|
||||
{
|
||||
printf("Metadata header is corrupt (checksum mismatch).\n");
|
||||
exit(1);
|
||||
}
|
||||
hdr->header_csum = csum;
|
||||
}
|
||||
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
|
||||
if (bs->dsk.inmemory_journal)
|
||||
@@ -241,22 +249,6 @@ resume_4:
|
||||
// metadata read finished
|
||||
bs->heap->finish_load();
|
||||
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
|
||||
if (zero_on_init && !bs->dsk.disable_meta_fsync)
|
||||
{
|
||||
GET_SQE();
|
||||
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||
last_read_offset = 0;
|
||||
data->iov = { 0 };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||
submitted++;
|
||||
bs->ringloop->submit();
|
||||
resume_5:
|
||||
if (submitted > 0)
|
||||
{
|
||||
wait_state = 5;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// asynchronous recheck
|
||||
resume_6:
|
||||
wait_state = 6;
|
||||
@@ -337,6 +329,22 @@ resume_9:
|
||||
}
|
||||
free(metadata_buffer);
|
||||
metadata_buffer = NULL;
|
||||
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
|
||||
{
|
||||
GET_SQE();
|
||||
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||
last_read_offset = 0;
|
||||
data->iov = { 0 };
|
||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
||||
submitted++;
|
||||
bs->ringloop->submit();
|
||||
resume_5:
|
||||
if (submitted > 0)
|
||||
{
|
||||
wait_state = 5;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
|
||||
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
|
||||
format_size(bs->heap->get_data_used_space()).c_str(),
|
||||
|
||||
@@ -16,6 +16,7 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
||||
else if (priv->op_state == 5) goto resume_5;
|
||||
assert(!priv->op_state);
|
||||
op->retval = 0;
|
||||
PRIV(op)->lsn = 0;
|
||||
priv->modified_block = priv->modified_block2 = UINT32_MAX;
|
||||
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
|
||||
{
|
||||
@@ -36,6 +37,12 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
||||
FINISH_OP(op);
|
||||
return 2;
|
||||
}
|
||||
if (res == ENOENT)
|
||||
{
|
||||
op->retval = -ENOENT;
|
||||
FINISH_OP(op);
|
||||
return 2;
|
||||
}
|
||||
if (res == ENOSPC)
|
||||
{
|
||||
if (!heap->get_to_compact_count())
|
||||
|
||||
@@ -9,6 +9,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||
if (!PRIV(op)->op_state)
|
||||
{
|
||||
op->retval = 0;
|
||||
PRIV(op)->lsn = 0;
|
||||
}
|
||||
int res = do_sync(op, 0);
|
||||
if (res == 2)
|
||||
@@ -104,7 +105,8 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
|
||||
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
|
||||
return 2;
|
||||
}
|
||||
PRIV(op)->modified_block = heap->get_completed_lsn();
|
||||
assert(!PRIV(op)->lsn);
|
||||
PRIV(op)->lsn = heap->get_completed_lsn();
|
||||
if (!submit_fsyncs(PRIV(op)->pending_ops))
|
||||
{
|
||||
PRIV(op)->wait_detail = 1;
|
||||
@@ -118,6 +120,6 @@ resume_1:
|
||||
return 1;
|
||||
}
|
||||
resume_2:
|
||||
heap->mark_lsn_fsynced(PRIV(op)->modified_block);
|
||||
heap->mark_lsn_fsynced(PRIV(op)->lsn);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
|
||||
heap->complete_block_write(modified_block);
|
||||
ringloop->wakeup();
|
||||
};
|
||||
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + ((uint64_t)modified_block+1)*dsk.meta_block_size
|
||||
);
|
||||
@@ -177,6 +178,7 @@ enospc:
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
data->iov = (struct iovec){ op->buf, op->len };
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(loc+op->offset+op->len <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + loc + op->offset);
|
||||
PRIV(op)->pending_ops++;
|
||||
write_iodepth++;
|
||||
@@ -264,6 +266,7 @@ enospc:
|
||||
BS_SUBMIT_GET_SQE(sqe2, data2);
|
||||
data2->iov = (struct iovec){ op->buf, op->len };
|
||||
data2->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(loc+op->len <= dsk.journal_len);
|
||||
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, dsk.journal_offset + loc);
|
||||
PRIV(op)->pending_ops++;
|
||||
}
|
||||
@@ -453,6 +456,7 @@ resume_10:
|
||||
BS_SUBMIT_GET_SQE(sqe, data);
|
||||
data->iov = (struct iovec){ op->buf, op->len };
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
assert(PRIV(op)->location + op->offset <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + PRIV(op)->location + op->offset);
|
||||
if (dsk.use_atomic_flag)
|
||||
sqe->rw_flags = RWF_ATOMIC;
|
||||
|
||||
@@ -141,7 +141,7 @@ struct __attribute__((__packed__)) journal_entry
|
||||
inline uint32_t je_crc32(journal_entry *je)
|
||||
{
|
||||
// 0x48674bc7 = crc32(4 zero bytes)
|
||||
return crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
|
||||
return je->size < 4 ? 0 : crc32c(0x48674bc7, ((uint8_t*)je)+4, je->size-4);
|
||||
}
|
||||
|
||||
// "VITAstor"
|
||||
|
||||
@@ -520,6 +520,7 @@ resume_2:
|
||||
await_sqe(15);
|
||||
data->iov = (struct iovec){ it->buf, (size_t)it->len };
|
||||
data->callback = simple_callback_w;
|
||||
assert(clean_loc+it->offset+it->len <= bs->dsk.block_count*bs->dsk.data_block_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + it->offset
|
||||
);
|
||||
@@ -749,6 +750,7 @@ bool journal_flusher_co::write_meta_block(flusher_meta_write_t & meta_block, int
|
||||
await_sqe(0);
|
||||
data->iov = (struct iovec){ meta_block.buf, (size_t)bs->dsk.meta_block_size };
|
||||
data->callback = simple_callback_w;
|
||||
assert(bs->dsk.meta_block_size + meta_block.sector + bs->dsk.meta_block_size <= bs->dsk.meta_area_size);
|
||||
io_uring_prep_writev(
|
||||
sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bs->dsk.meta_block_size + meta_block.sector
|
||||
);
|
||||
|
||||
@@ -193,6 +193,7 @@ void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_
|
||||
(size_t)journal.block_size
|
||||
};
|
||||
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||
assert(journal.sector_info[cur_sector].offset+journal.block_size <= dsk.journal_len);
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.journal_fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
||||
);
|
||||
|
||||
@@ -368,9 +368,9 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
}
|
||||
data->iov.iov_len = op->len + stripe_offset + stripe_end; // to check it in the callback
|
||||
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||
io_uring_prep_writev(
|
||||
sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + (loc * dsk.data_block_size) + op->offset - stripe_offset
|
||||
);
|
||||
const uint64_t write_offset = (loc * dsk.data_block_size) + op->offset - stripe_offset;
|
||||
assert(write_offset+op->len+stripe_offset+stripe_end <= dsk.block_count*dsk.data_block_size);
|
||||
io_uring_prep_writev(sqe, dsk.data_fd, PRIV(op)->iov_zerofill, vcnt, dsk.data_offset + write_offset);
|
||||
PRIV(op)->pending_ops = 1;
|
||||
if (!(dirty_it->second.state & BS_ST_INSTANT))
|
||||
{
|
||||
@@ -495,9 +495,8 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
||||
.op = op,
|
||||
});
|
||||
data2->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||
io_uring_prep_writev(
|
||||
sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free
|
||||
);
|
||||
assert(journal.next_free+op->len <= dsk.journal_len);
|
||||
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, journal.offset + journal.next_free);
|
||||
PRIV(op)->pending_ops++;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -12,7 +12,7 @@ if (RDMACM_LIBRARIES)
|
||||
set(MSGR_RDMACM "msgr_rdmacm.cpp")
|
||||
endif (RDMACM_LIBRARIES)
|
||||
add_library(vitastor_common STATIC
|
||||
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp
|
||||
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp msgr_iothread.cpp ../util/addr_util.cpp
|
||||
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
|
||||
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
|
||||
)
|
||||
@@ -52,9 +52,6 @@ if (${WITH_FIO})
|
||||
../util/rw_blocking.cpp
|
||||
../util/addr_util.cpp
|
||||
)
|
||||
target_link_libraries(fio_vitastor_sec
|
||||
tcmalloc_minimal
|
||||
)
|
||||
endif (${WITH_FIO})
|
||||
|
||||
# vitastor-nbd
|
||||
@@ -101,6 +98,7 @@ add_executable(test_cluster_client
|
||||
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp
|
||||
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp
|
||||
)
|
||||
target_link_libraries(test_cluster_client ${LIBURING_LIBRARIES})
|
||||
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
||||
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
|
||||
add_dependencies(build_tests test_cluster_client)
|
||||
|
||||
@@ -1171,7 +1171,7 @@ resume_2:
|
||||
erase_op(op);
|
||||
return 1;
|
||||
}
|
||||
else if (op->retval != 0 && !(op->flags & OP_FLUSH_BUFFER) &&
|
||||
else if (op->retval != 0 && op->opcode != OSD_OP_SYNC && !(op->flags & OP_FLUSH_BUFFER) &&
|
||||
op->retval != -EPIPE && (op->retval != -EIO || !client_eio_retry_interval) && (op->retval != -ENOSPC || !client_retry_enospc))
|
||||
{
|
||||
// Fatal error (neither -EPIPE, -EIO nor -ENOSPC)
|
||||
|
||||
@@ -83,9 +83,6 @@ class writeback_cache_t;
|
||||
// FIXME: Split into public and private interfaces
|
||||
class __attribute__((visibility("default"))) cluster_client_t
|
||||
{
|
||||
#ifdef __MOCK__
|
||||
public:
|
||||
#endif
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
|
||||
@@ -155,15 +152,9 @@ public:
|
||||
void list_inode(inode_t inode, uint64_t min_offset, uint64_t max_offset, int max_parallel_pgs, std::function<void(
|
||||
int status, int pgs_left, pg_num_t pg_num, std::set<object_id>&& objects)> pg_callback);
|
||||
|
||||
//inline uint32_t get_bs_bitmap_granularity() { return st_cli.global_bitmap_granularity; }
|
||||
//inline uint64_t get_bs_block_size() { return st_cli.global_block_size; }
|
||||
|
||||
#ifndef __MOCK__
|
||||
protected:
|
||||
#endif
|
||||
void continue_ops(int time_passed = 0);
|
||||
|
||||
protected:
|
||||
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
|
||||
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
|
||||
|
||||
@@ -204,4 +195,5 @@ protected:
|
||||
osd_num_t select_nearest_osd(const std::vector<osd_num_t> & osds);
|
||||
|
||||
friend class writeback_cache_t;
|
||||
friend class cluster_client_test_t;
|
||||
};
|
||||
|
||||
@@ -88,6 +88,11 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
|
||||
// ...or just save it for writeback if write buffering is enabled
|
||||
if (op->len == 0)
|
||||
{
|
||||
// FIXME: OSD_OP_DELETEs are currently only sent by vitastor-cli rm/rm-data and
|
||||
// actually have len=0, because delete is actually a delete of the full object
|
||||
// containing the requested offset, not a "punch hole" operation. But here, writeback
|
||||
// cache assumes it IS a "punch hole" operation. I should select one of these
|
||||
// approaches and fix everything accordingly when I decide to implement TRIM.
|
||||
return;
|
||||
}
|
||||
auto dirty_it = find_dirty(op->inode, op->offset);
|
||||
@@ -244,12 +249,13 @@ void writeback_cache_t::copy_write(cluster_op_t *op, int state, uint64_t new_flu
|
||||
writeback_queue_size--;
|
||||
}
|
||||
}
|
||||
if (!is_del)
|
||||
if (!is_del && op->len > 0)
|
||||
{
|
||||
uint64_t pos = 0, len = op->len, iov_idx = 0;
|
||||
while (len > 0 && iov_idx < op->iov.count)
|
||||
while (iov_idx < op->iov.count)
|
||||
{
|
||||
auto & iov = op->iov.buf[iov_idx];
|
||||
assert(pos + iov.iov_len <= len);
|
||||
memcpy(buf + pos, iov.iov_base, iov.iov_len);
|
||||
pos += iov.iov_len;
|
||||
iov_idx++;
|
||||
@@ -443,7 +449,7 @@ void writeback_cache_t::start_writebacks(cluster_client_t *cli, int count)
|
||||
started++;
|
||||
assert(writeback_queue_size > 0);
|
||||
writeback_queue_size--;
|
||||
writeback_bytes -= off - from_it->first.stripe;
|
||||
writeback_bytes -= (is_del ? 0 : off - from_it->first.stripe);
|
||||
assert(writeback_queue_size > 0 || !writeback_bytes);
|
||||
flush_buffers(cli, from_it, to_it);
|
||||
}
|
||||
|
||||
@@ -1185,7 +1185,6 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
if (i >= pg_state_bit_count)
|
||||
{
|
||||
fprintf(stderr, "Unexpected pool %u PG %u state keyword in etcd: %s\n", pool_id, pg_num, e.dump().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!cur_primary || !value["state"].is_array() || !state ||
|
||||
@@ -1194,7 +1193,6 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
(state & PG_INCOMPLETE) && state != PG_INCOMPLETE && state != (PG_INCOMPLETE|PG_HAS_INVALID))
|
||||
{
|
||||
fprintf(stderr, "Unexpected pool %u PG %u state in etcd: primary=%ju, state=%s\n", pool_id, pg_num, cur_primary, value["state"].dump().c_str());
|
||||
return;
|
||||
}
|
||||
pg_cfg.cur_primary = cur_primary;
|
||||
pg_cfg.cur_state = state;
|
||||
|
||||
+4
-115
@@ -15,106 +15,6 @@
|
||||
#include "msgr_rdma.h"
|
||||
#endif
|
||||
|
||||
#include <sys/poll.h>
|
||||
|
||||
msgr_iothread_t::msgr_iothread_t():
|
||||
ring(RINGLOOP_DEFAULT_SIZE, true),
|
||||
thread(&msgr_iothread_t::run, this)
|
||||
{
|
||||
eventfd = ring.register_eventfd();
|
||||
if (eventfd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
|
||||
}
|
||||
}
|
||||
|
||||
msgr_iothread_t::~msgr_iothread_t()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
|
||||
{
|
||||
mu.lock();
|
||||
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
cond.notify_all();
|
||||
}
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::stop()
|
||||
{
|
||||
mu.lock();
|
||||
if (stopped)
|
||||
{
|
||||
mu.unlock();
|
||||
return;
|
||||
}
|
||||
stopped = true;
|
||||
if (outer_loop_data)
|
||||
{
|
||||
outer_loop_data->callback = [](ring_data_t*){};
|
||||
}
|
||||
cond.notify_all();
|
||||
close(eventfd);
|
||||
mu.unlock();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_to_ringloop(ring_loop_t *outer_loop)
|
||||
{
|
||||
assert(!this->outer_loop || this->outer_loop == outer_loop);
|
||||
io_uring_sqe *sqe = outer_loop->get_sqe();
|
||||
assert(sqe != NULL);
|
||||
this->outer_loop = outer_loop;
|
||||
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
|
||||
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
|
||||
outer_loop_data->callback = [this](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
|
||||
}
|
||||
outer_loop_data = NULL;
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_to_ringloop(this->outer_loop);
|
||||
ring.loop();
|
||||
};
|
||||
}
|
||||
|
||||
void msgr_iothread_t::run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mu);
|
||||
while (!stopped && !queue.size())
|
||||
cond.wait(lk);
|
||||
if (stopped)
|
||||
return;
|
||||
int i = 0;
|
||||
for (; i < queue.size(); i++)
|
||||
{
|
||||
io_uring_sqe *sqe = ring.get_sqe();
|
||||
if (!sqe)
|
||||
break;
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
*data = std::move(queue[i].data);
|
||||
*sqe = queue[i].sqe;
|
||||
sqe->user_data = (uint64_t)data;
|
||||
}
|
||||
queue.erase(queue.begin(), queue.begin()+i);
|
||||
}
|
||||
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
|
||||
ring.submit();
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::init()
|
||||
{
|
||||
#ifdef WITH_RDMACM
|
||||
@@ -173,12 +73,7 @@ void osd_messenger_t::init()
|
||||
}
|
||||
if (ringloop && iothread_count > 0)
|
||||
{
|
||||
for (int i = 0; i < iothread_count; i++)
|
||||
{
|
||||
auto iot = new msgr_iothread_t();
|
||||
iothreads.push_back(iot);
|
||||
iot->add_to_ringloop(ringloop);
|
||||
}
|
||||
init_iothreads();
|
||||
}
|
||||
keepalive_timer_id = tfd->set_timer(1000, true, [this](int)
|
||||
{
|
||||
@@ -220,7 +115,7 @@ void osd_messenger_t::init()
|
||||
.opcode = OSD_OP_PING,
|
||||
},
|
||||
};
|
||||
op->callback = [this, cl](osd_op_t *op)
|
||||
op->callback = [this](osd_op_t *op)
|
||||
{
|
||||
auto cl_it = clients.find(op->client_id);
|
||||
if (cl_it == clients.end())
|
||||
@@ -229,6 +124,7 @@ void osd_messenger_t::init()
|
||||
delete op;
|
||||
return;
|
||||
}
|
||||
auto cl = cl_it->second;
|
||||
uint64_t fail_client_id = (op->reply.hdr.retval != 0 ? op->client_id : 0);
|
||||
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
|
||||
cl->ping_time_remaining = 0;
|
||||
@@ -271,14 +167,7 @@ osd_messenger_t::~osd_messenger_t()
|
||||
{
|
||||
stop_client(clients.begin()->first, true);
|
||||
}
|
||||
if (iothreads.size())
|
||||
{
|
||||
for (auto iot: iothreads)
|
||||
{
|
||||
delete iot;
|
||||
}
|
||||
iothreads.clear();
|
||||
}
|
||||
destroy_iothreads();
|
||||
#ifdef WITH_RDMA
|
||||
for (auto rdma_context: rdma_contexts)
|
||||
{
|
||||
|
||||
+4
-37
@@ -129,43 +129,7 @@ struct osd_op_stats_t
|
||||
uint64_t subop_stat_count[OSD_OP_MAX+1] = { 0 };
|
||||
};
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __MOCK__
|
||||
class msgr_iothread_t;
|
||||
#else
|
||||
struct iothread_sqe_t
|
||||
{
|
||||
io_uring_sqe sqe;
|
||||
ring_data_t data;
|
||||
};
|
||||
|
||||
class msgr_iothread_t
|
||||
{
|
||||
protected:
|
||||
ring_loop_t ring;
|
||||
ring_loop_t *outer_loop = NULL;
|
||||
ring_data_t *outer_loop_data = NULL;
|
||||
int eventfd = -1;
|
||||
bool stopped = false;
|
||||
std::mutex mu;
|
||||
std::condition_variable cond;
|
||||
std::vector<iothread_sqe_t> queue;
|
||||
std::thread thread;
|
||||
|
||||
void run();
|
||||
public:
|
||||
|
||||
msgr_iothread_t();
|
||||
~msgr_iothread_t();
|
||||
|
||||
void add_sqe(io_uring_sqe & sqe);
|
||||
void stop();
|
||||
void add_to_ringloop(ring_loop_t *outer_loop);
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef WITH_RDMA
|
||||
struct rdma_event_channel;
|
||||
@@ -215,7 +179,7 @@ protected:
|
||||
|
||||
public:
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
ring_loop_i *ringloop = NULL;
|
||||
bool has_sendmsg_zc = false;
|
||||
// osd_num_t is only for logging and asserts
|
||||
uint64_t next_client_id = 1;
|
||||
@@ -234,6 +198,7 @@ public:
|
||||
osd_op_stats_t stats, recovery_stats;
|
||||
|
||||
void init();
|
||||
void init_iothreads();
|
||||
void parse_config(const json11::Json & config);
|
||||
void connect_peer(uint64_t osd_num, json11::Json peer_state);
|
||||
void stop_client(uint64_t client_id, bool force_delete = false);
|
||||
@@ -246,6 +211,7 @@ public:
|
||||
void read_requests();
|
||||
void send_replies();
|
||||
void accept_connections(int listen_fd);
|
||||
void destroy_iothreads();
|
||||
~osd_messenger_t();
|
||||
|
||||
static json11::Json::object read_config(const json11::Json & config);
|
||||
@@ -293,6 +259,7 @@ protected:
|
||||
bool init_recv_rdma(osd_client_t *cl);
|
||||
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
|
||||
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
|
||||
void destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn);
|
||||
#endif
|
||||
#ifdef WITH_RDMACM
|
||||
void handle_rdmacm_events();
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
|
||||
msgr_iothread_t::msgr_iothread_t():
|
||||
ring(RINGLOOP_DEFAULT_SIZE, true),
|
||||
thread(&msgr_iothread_t::run, this)
|
||||
{
|
||||
eventfd = ring.register_eventfd();
|
||||
if (eventfd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("failed to register eventfd: ") + strerror(-eventfd));
|
||||
}
|
||||
}
|
||||
|
||||
msgr_iothread_t::~msgr_iothread_t()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_sqe(io_uring_sqe & sqe)
|
||||
{
|
||||
mu.lock();
|
||||
queue.push_back((iothread_sqe_t){ .sqe = sqe, .data = std::move(*(ring_data_t*)sqe.user_data) });
|
||||
if (queue.size() == 1)
|
||||
{
|
||||
cond.notify_all();
|
||||
}
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::stop()
|
||||
{
|
||||
mu.lock();
|
||||
if (stopped)
|
||||
{
|
||||
mu.unlock();
|
||||
return;
|
||||
}
|
||||
stopped = true;
|
||||
if (outer_loop_data)
|
||||
{
|
||||
outer_loop_data->callback = [](ring_data_t*){};
|
||||
}
|
||||
cond.notify_all();
|
||||
close(eventfd);
|
||||
mu.unlock();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void msgr_iothread_t::add_to_ringloop(ring_loop_i *outer_loop)
|
||||
{
|
||||
assert(!this->outer_loop || this->outer_loop == outer_loop);
|
||||
io_uring_sqe *sqe = outer_loop->get_sqe();
|
||||
assert(sqe != NULL);
|
||||
this->outer_loop = outer_loop;
|
||||
this->outer_loop_data = ((ring_data_t*)sqe->user_data);
|
||||
io_uring_prep_poll_add(sqe, eventfd, POLLIN);
|
||||
outer_loop_data->callback = [this](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("eventfd poll failed: ") + strerror(-data->res));
|
||||
}
|
||||
outer_loop_data = NULL;
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_to_ringloop(this->outer_loop);
|
||||
ring.loop();
|
||||
};
|
||||
}
|
||||
|
||||
void msgr_iothread_t::run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mu);
|
||||
while (!stopped && !queue.size())
|
||||
cond.wait(lk);
|
||||
if (stopped)
|
||||
return;
|
||||
int i = 0;
|
||||
for (; i < queue.size(); i++)
|
||||
{
|
||||
io_uring_sqe *sqe = ring.get_sqe();
|
||||
if (!sqe)
|
||||
break;
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
*data = std::move(queue[i].data);
|
||||
*sqe = queue[i].sqe;
|
||||
sqe->user_data = (uint64_t)data;
|
||||
}
|
||||
queue.erase(queue.begin(), queue.begin()+i);
|
||||
}
|
||||
// We only want to offload sendmsg/recvmsg. Callbacks will be called in main thread
|
||||
ring.submit();
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::init_iothreads()
|
||||
{
|
||||
for (int i = 0; i < iothread_count; i++)
|
||||
{
|
||||
auto iot = new msgr_iothread_t();
|
||||
iothreads.push_back(iot);
|
||||
iot->add_to_ringloop(ringloop);
|
||||
}
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_iothreads()
|
||||
{
|
||||
if (iothreads.size())
|
||||
{
|
||||
for (auto iot: iothreads)
|
||||
{
|
||||
delete iot;
|
||||
}
|
||||
iothreads.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
#include "ringloop.h"
|
||||
|
||||
struct iothread_sqe_t
|
||||
{
|
||||
io_uring_sqe sqe;
|
||||
ring_data_t data;
|
||||
};
|
||||
|
||||
class msgr_iothread_t
|
||||
{
|
||||
protected:
|
||||
ring_loop_t ring;
|
||||
ring_loop_i *outer_loop = NULL;
|
||||
ring_data_t *outer_loop_data = NULL;
|
||||
int eventfd = -1;
|
||||
bool stopped = false;
|
||||
std::mutex mu;
|
||||
std::condition_variable cond;
|
||||
std::vector<iothread_sqe_t> queue;
|
||||
std::thread thread;
|
||||
|
||||
void run();
|
||||
public:
|
||||
|
||||
msgr_iothread_t();
|
||||
~msgr_iothread_t();
|
||||
|
||||
void add_sqe(io_uring_sqe & sqe);
|
||||
void stop();
|
||||
void add_to_ringloop(ring_loop_i *outer_loop);
|
||||
};
|
||||
@@ -157,6 +157,7 @@ struct __attribute__((visibility("default"))) osd_op_t
|
||||
timespec tv_begin = { 0 }, tv_end = { 0 };
|
||||
uint64_t op_type = OSD_OP_IN;
|
||||
uint64_t client_id = 0;
|
||||
osd_num_t osd_num = 0;
|
||||
osd_any_op_t req;
|
||||
osd_any_reply_t reply;
|
||||
blockstore_op_t *bs_op = NULL;
|
||||
|
||||
@@ -612,7 +612,9 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||
while (!rc->send_out_full && copied > 0 && rc->cur_send < rc->max_send)
|
||||
{
|
||||
dst = (uint8_t*)rc->send_out.buf + rc->send_out_pos;
|
||||
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
|
||||
dst_len = (rc->send_out_pos >= rc->send_done_pos
|
||||
? rc->send_out_size-rc->send_out_pos
|
||||
: rc->send_done_pos-rc->send_out_pos);
|
||||
if (dst_len > rc->max_msg)
|
||||
dst_len = rc->max_msg;
|
||||
copied = try_send_rdma_copy(cl, dst, dst_len);
|
||||
@@ -622,7 +624,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
||||
if (rc->send_out_pos == rc->send_out_size)
|
||||
rc->send_out_pos = 0;
|
||||
assert(rc->send_out_pos < rc->send_out_size);
|
||||
if (rc->send_out_pos >= rc->send_done_pos)
|
||||
if (rc->send_out_pos == rc->send_done_pos)
|
||||
rc->send_out_full = true;
|
||||
ibv_sge sge = {
|
||||
.addr = (uintptr_t)dst,
|
||||
@@ -792,3 +794,16 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
||||
} while (event_count > 0);
|
||||
handle_immediate_ops();
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
|
||||
{
|
||||
if (rdma_conn->cmid)
|
||||
{
|
||||
auto rdma_it = rdmacm_connections.find(rdma_conn->cmid);
|
||||
if (rdma_it != rdmacm_connections.end() && rdma_it->second->rdma_conn == rdma_conn)
|
||||
{
|
||||
rdmacm_connections.erase(rdma_it);
|
||||
}
|
||||
}
|
||||
delete rdma_conn;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
|
||||
void osd_messenger_t::read_requests()
|
||||
{
|
||||
@@ -348,7 +349,7 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
|
||||
bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
|
||||
{
|
||||
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id);
|
||||
if (req_it == cl->sent_ops.end())
|
||||
if (req_it == cl->sent_ops.end() || req_it->second->req.hdr.opcode != cl->read_op->req.hdr.opcode)
|
||||
{
|
||||
// Command out of sync. Drop connection
|
||||
fprintf(stderr, "Client %ju command out of sync: id %ju\n", cl->client_id, cl->read_op->req.hdr.id);
|
||||
@@ -372,7 +373,7 @@ bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
|
||||
stop_client(cl->client_id);
|
||||
return false;
|
||||
}
|
||||
if (bmp_len > 0)
|
||||
if (op->reply.hdr.retval >= 0 && bmp_len > 0)
|
||||
{
|
||||
assert(op->bitmap);
|
||||
cl->recv_list.push_back(op->bitmap, bmp_len);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "messenger.h"
|
||||
#include "msgr_iothread.h"
|
||||
|
||||
void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
||||
{
|
||||
@@ -99,10 +100,15 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
||||
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
||||
{
|
||||
if (cur_op->op_type == OSD_OP_IN && cur_op->reply.hdr.retval > 0)
|
||||
{
|
||||
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->reply.hdr.retval });
|
||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
||||
}
|
||||
else if (cur_op->op_type == OSD_OP_OUT && cur_op->req.sec_read_bmp.len > 0)
|
||||
{
|
||||
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->req.sec_read_bmp.len });
|
||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
||||
}
|
||||
}
|
||||
if (cur_op->op_type == OSD_OP_IN)
|
||||
{
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "messenger.h"
|
||||
#ifdef WITH_RDMA
|
||||
#include "msgr_rdma.h"
|
||||
#endif
|
||||
|
||||
void osd_client_t::cancel_ops()
|
||||
{
|
||||
@@ -92,23 +89,11 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
|
||||
osd_peers.erase(osd_it);
|
||||
}
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->rdma_conn && cl->rdma_conn->cmid)
|
||||
{
|
||||
auto rdma_it = rdmacm_connections.find(cl->rdma_conn->cmid);
|
||||
if (rdma_it != rdmacm_connections.end() && rdma_it->second == cl)
|
||||
{
|
||||
rdmacm_connections.erase(rdma_it);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifndef __MOCK__
|
||||
if (cl->connect_timeout_id >= 0)
|
||||
{
|
||||
tfd->clear_timer(cl->connect_timeout_id);
|
||||
cl->connect_timeout_id = -1;
|
||||
}
|
||||
#endif
|
||||
if (cl->in_osd_num && break_pg_locks)
|
||||
{
|
||||
// Break PG locks
|
||||
@@ -143,9 +128,7 @@ void osd_messenger_t::destroy_client(osd_client_t *cl)
|
||||
clients.erase(cl->client_id);
|
||||
if (cl->peer_fd >= 0)
|
||||
{
|
||||
#ifndef __MOCK__
|
||||
tfd->set_fd_handler(cl->peer_fd, false, NULL);
|
||||
#endif
|
||||
for (auto rit = read_ready_clients.begin(); rit != read_ready_clients.end(); rit++)
|
||||
{
|
||||
if (*rit == cl->client_id)
|
||||
@@ -164,6 +147,13 @@ void osd_messenger_t::destroy_client(osd_client_t *cl)
|
||||
}
|
||||
clients_by_fd.erase(cl->peer_fd);
|
||||
}
|
||||
#ifdef WITH_RDMA
|
||||
if (cl->rdma_conn)
|
||||
{
|
||||
destroy_rdma_conn(cl->rdma_conn);
|
||||
cl->rdma_conn = NULL;
|
||||
}
|
||||
#endif
|
||||
delete cl;
|
||||
}
|
||||
|
||||
@@ -196,13 +186,4 @@ osd_client_t::~osd_client_t()
|
||||
delete op;
|
||||
}
|
||||
}
|
||||
#ifndef __MOCK__
|
||||
#ifdef WITH_RDMA
|
||||
if (rdma_conn)
|
||||
{
|
||||
delete rdma_conn;
|
||||
rdma_conn = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1049,7 +1049,7 @@ static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
|
||||
{
|
||||
// Get larger allocated extents, possibly with false positives
|
||||
uint64_t bmp_pos = (offset-task.offset) / task.bitmap_granularity;
|
||||
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity - bmp_pos;
|
||||
uint64_t bmp_end = (offset+bytes-task.offset) / task.bitmap_granularity;
|
||||
while (bmp_pos < bmp_end)
|
||||
{
|
||||
if (!(bmp_pos & 7) && bmp_end >= bmp_pos+8)
|
||||
|
||||
@@ -282,7 +282,7 @@ help:
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
const bool writeback = cli->get_immediate_commit(inode);
|
||||
const bool writeback = !cli->get_immediate_commit(inode);
|
||||
auto pool_it = cli->st_cli.pool_config.find(INODE_POOL(inode ? inode : watch->cfg.num));
|
||||
if (pool_it == cli->st_cli.pool_config.end())
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: Vitastor
|
||||
Description: Vitastor client library
|
||||
Version: 3.0.11
|
||||
Version: 3.0.12
|
||||
Libs: -L${libdir} -lvitastor_client
|
||||
Cflags: -I${includedir}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ add_library(vitastor_cli STATIC
|
||||
cli_pool_ls.cpp
|
||||
cli_pool_modify.cpp
|
||||
cli_pool_rm.cpp
|
||||
cli_raw_ls.cpp
|
||||
)
|
||||
target_compile_options(vitastor_cli PUBLIC -fPIC)
|
||||
|
||||
|
||||
@@ -126,6 +126,11 @@ static const char* help_text =
|
||||
" --min-offset, --max-offset\n"
|
||||
" Restrict listing to specific offsets inside inodes.\n"
|
||||
"\n"
|
||||
"vitastor-cli raw-ls [OPTIONS]\n"
|
||||
" Find object(s) in the cluster using raw secondary listing operations. Options:\n"
|
||||
" [--min_inode NUM] [--max_inode NUM] [--offset NUM] [--pg_num NUM] [--pg_count COUNT]\n"
|
||||
" [--pg_stripe_size NUM] [--osds 1,2,3,...]\n"
|
||||
"\n"
|
||||
"vitastor-cli fix [--objects <objects>] [--bad-osds <osds>] [--part <part>] [--check no]\n"
|
||||
" Fix inconsistent objects in the cluster by deleting some copies.\n"
|
||||
" --objects <objects>\n"
|
||||
@@ -459,6 +464,11 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
||||
// Describe unclean objects
|
||||
action_cb = p->start_describe(cfg);
|
||||
}
|
||||
else if (cmd[0] == "raw-ls")
|
||||
{
|
||||
// Run raw listings
|
||||
action_cb = p->start_raw_ls(cfg);
|
||||
}
|
||||
else if (cmd[0] == "fix")
|
||||
{
|
||||
// Fix inconsistent objects (by deleting some copies)
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
std::function<bool(cli_result_t &)> start_fix(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_ls(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_raw_ls(json11::Json cfg);
|
||||
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
||||
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "cli_fix.h"
|
||||
#include "cluster_client.h"
|
||||
#include "pg_states.h"
|
||||
#include "str_util.h"
|
||||
#include "json_util.h"
|
||||
|
||||
std::vector<uint64_t> parse_uint64_list(json11::Json val);
|
||||
|
||||
// Find object(s) in the cluster using raw secondary listing operations.
|
||||
// Only for troubleshooting.
|
||||
struct cli_raw_ls_t
|
||||
{
|
||||
json11::Json cfg;
|
||||
pool_id_t pool_id = 0;
|
||||
pg_num_t pg_num = 0;
|
||||
pg_num_t pg_count = 0;
|
||||
uint32_t pg_stripe_size = 0;
|
||||
std::set<uint64_t> osds;
|
||||
std::vector<osd_num_t> osd_list;
|
||||
uint64_t min_inode = 0, max_inode = 0, min_offset = 0, max_offset = 0;
|
||||
bool offset_set = false;
|
||||
|
||||
cli_tool_t *parent = NULL;
|
||||
int state = 0;
|
||||
cli_result_t result;
|
||||
bool first = true;
|
||||
size_t osd_pos = 0;
|
||||
|
||||
bool is_done()
|
||||
{
|
||||
return state == 100;
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (state == 1)
|
||||
goto resume_1;
|
||||
if (state == 100)
|
||||
return;
|
||||
pool_id = cfg["pool_id"].uint64_value();
|
||||
pg_num = (pg_num_t)cfg["pg_num"].uint64_value();
|
||||
pg_count = (pg_num_t)cfg["pg_count"].uint64_value();
|
||||
pg_stripe_size = cfg["pg_stripe_size"].uint64_value();
|
||||
if (!pool_id)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Pool ID is required" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (cfg["min_inode"].is_null() && cfg["max_inode"].is_null())
|
||||
{
|
||||
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), 0);
|
||||
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), UINT64_MAX);
|
||||
}
|
||||
else
|
||||
{
|
||||
min_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["min_inode"].as_string()));
|
||||
max_inode = INODE_WITH_POOL(cfg["pool_id"].uint64_value(), stoull_full(cfg["max_inode"].as_string()));
|
||||
}
|
||||
if (cfg["offset"].is_null())
|
||||
{
|
||||
if (!pg_num)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "PG number is required if offset is not specified" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
min_offset = 0;
|
||||
max_offset = UINT64_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_offset = max_offset = stoull_full(cfg["offset"].as_string());
|
||||
offset_set = true;
|
||||
}
|
||||
for (auto osd_num: parse_uint64_list(cfg["osds"]))
|
||||
{
|
||||
osds.insert(osd_num);
|
||||
}
|
||||
if (!pg_count || !pg_stripe_size || !osds.size())
|
||||
{
|
||||
auto pool_it = parent->cli->st_cli.pool_config.find(pool_id);
|
||||
if (pool_it == parent->cli->st_cli.pool_config.end())
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "pg_count, pg_stripe_size and osds are required if the pool does not exist" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (!pg_count)
|
||||
{
|
||||
pg_count = pool_it->second.real_pg_count;
|
||||
}
|
||||
if (!pg_stripe_size)
|
||||
{
|
||||
pg_stripe_size = pool_it->second.pg_stripe_size;
|
||||
}
|
||||
if (!osds.size())
|
||||
{
|
||||
for (auto & pgp: pool_it->second.pg_config)
|
||||
{
|
||||
for (osd_num_t pg_osd: pgp.second.target_set)
|
||||
osds.insert(pg_osd);
|
||||
for (osd_num_t pg_osd: pgp.second.all_peers)
|
||||
osds.insert(pg_osd);
|
||||
for (auto & hist_item: pgp.second.target_history)
|
||||
for (auto pg_osd: hist_item)
|
||||
osds.insert(pg_osd);
|
||||
}
|
||||
osds.erase(0);
|
||||
}
|
||||
}
|
||||
if (offset_set)
|
||||
{
|
||||
min_offset = (min_offset / pg_stripe_size) * pg_stripe_size;
|
||||
max_offset = ((max_offset / pg_stripe_size) * pg_stripe_size) | STRIPE_MASK;
|
||||
pg_num = (min_offset/pg_stripe_size) % pg_count + 1; // like map_to_pg()
|
||||
fprintf(stderr, "Selected PG %u\n", pg_num);
|
||||
}
|
||||
parent->cli->init_msgr();
|
||||
osd_list = std::vector<osd_num_t>(osds.begin(), osds.end());
|
||||
osd_pos = 0;
|
||||
state = 1;
|
||||
resume_1:
|
||||
for (; osd_pos < osd_list.size() && parent->waiting < parent->parallel_osds; osd_pos++)
|
||||
{
|
||||
uint64_t osd_num = osd_list[osd_pos];
|
||||
if (parent->cli->st_cli.peer_states[osd_num].is_null())
|
||||
{
|
||||
fprintf(stderr, "OSD %ju is unavailable, skipping\n", osd_num);
|
||||
continue;
|
||||
}
|
||||
osd_op_t *op = new osd_op_t;
|
||||
op->req = (osd_any_op_t){
|
||||
.sec_list = {
|
||||
.header = {
|
||||
.magic = SECONDARY_OSD_OP_MAGIC,
|
||||
.opcode = OSD_OP_SEC_LIST,
|
||||
},
|
||||
.list_pg = pg_num,
|
||||
.pg_count = pg_count,
|
||||
.pg_stripe_size = pg_stripe_size,
|
||||
.min_inode = min_inode,
|
||||
.max_inode = max_inode,
|
||||
.min_stripe = min_offset,
|
||||
.max_stripe = max_offset,
|
||||
},
|
||||
};
|
||||
op->callback = [this, osd_num](osd_op_t *op)
|
||||
{
|
||||
if (op->reply.hdr.retval < 0)
|
||||
{
|
||||
fprintf(stderr, "OSD %ju listing failed: retval=%jd\n", osd_num, op->reply.hdr.retval);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint64_t i = 0; i < op->reply.hdr.retval; i++)
|
||||
{
|
||||
auto & ov = ((obj_ver_id*)op->buf)[i];
|
||||
if (parent->json_output)
|
||||
{
|
||||
printf("%s{\"osd\":%ju,\"inode\":\"0x%jx\",\"stripe\":\"0x%jx\",\"version\":%ju,\"stable\":%s}",
|
||||
first ? "" : ",\n", osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
|
||||
i < op->reply.sec_list.stable_count ? "true" : "false");
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OSD %ju - %jx:%jx v%ju%s\n",
|
||||
osd_num, ov.oid.inode, ov.oid.stripe, ov.version,
|
||||
i < op->reply.sec_list.stable_count ? " stable" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
parent->waiting--;
|
||||
loop();
|
||||
delete op;
|
||||
};
|
||||
parent->waiting++;
|
||||
parent->cli->execute_raw(osd_num, op);
|
||||
}
|
||||
if (parent->waiting > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!first)
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
state = 100;
|
||||
}
|
||||
};
|
||||
|
||||
std::function<bool(cli_result_t &)> cli_tool_t::start_raw_ls(json11::Json cfg)
|
||||
{
|
||||
auto raw_ls = new cli_raw_ls_t();
|
||||
raw_ls->parent = this;
|
||||
raw_ls->cfg = cfg;
|
||||
return [raw_ls](cli_result_t & result)
|
||||
{
|
||||
raw_ls->loop();
|
||||
if (raw_ls->is_done())
|
||||
{
|
||||
result = raw_ls->result;
|
||||
delete raw_ls;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -11,7 +11,6 @@ add_executable(vitastor-disk
|
||||
../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp
|
||||
)
|
||||
target_link_libraries(vitastor-disk
|
||||
tcmalloc_minimal
|
||||
${LIBURING_LIBRARIES}
|
||||
${ISAL_LIBRARIES}
|
||||
)
|
||||
|
||||
@@ -427,6 +427,9 @@ int main(int argc, char *argv[])
|
||||
self.dsk.open_journal();
|
||||
self.dsk.calc_lengths();
|
||||
self.dsk.close_all();
|
||||
self.new_meta_device = self.dsk.meta_device;
|
||||
self.new_meta_offset = self.dsk.meta_offset;
|
||||
self.new_meta_len = self.dsk.meta_area_size;
|
||||
}
|
||||
std::string json_err;
|
||||
json11::Json meta = json11::Json::parse(read_all_fd(0), json_err);
|
||||
|
||||
@@ -52,7 +52,6 @@ struct disk_tool_t
|
||||
bool all = false, json = false, now = false;
|
||||
bool dump_with_blocks = false, dump_with_data = false;
|
||||
bool dump_as_old = false;
|
||||
bool skip_obsolete = false;
|
||||
int log_level = 1;
|
||||
double meta_reserve_multiple = 2;
|
||||
uint64_t meta_reserve_min_size = (uint64_t)1024*1024*1024;
|
||||
@@ -138,8 +137,6 @@ struct disk_tool_t
|
||||
int resize_write_new_journal();
|
||||
void remap_big_write(heap_entry_t *wr);
|
||||
void remap_small_write(heap_entry_t *wr);
|
||||
void fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr);
|
||||
void fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||
int resize_rebuild_meta();
|
||||
int resize_write_new_meta();
|
||||
void free_new_meta();
|
||||
|
||||
@@ -589,6 +589,7 @@ int disk_tool_t::write_json_meta(json11::Json meta)
|
||||
|
||||
int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
|
||||
{
|
||||
assert(new_meta_len >= sizeof(blockstore_meta_header_v3_t));
|
||||
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
|
||||
memset(new_meta_buf, 0, new_meta_len);
|
||||
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
|
||||
@@ -606,9 +607,9 @@ int disk_tool_t::write_json_heap(json11::Json meta, json11::Json journal)
|
||||
: (meta["data_csum_type"].string_value() == "crc32c"
|
||||
? BLOCKSTORE_CSUM_CRC32C
|
||||
: BLOCKSTORE_CSUM_NONE);
|
||||
new_meta_hdr->meta_area_size = new_meta_len;
|
||||
new_meta_hdr->csum_block_size = meta["csum_block_size"].uint64_value();
|
||||
new_meta_hdr->header_csum = crc32c(0, new_meta_hdr, sizeof(blockstore_meta_header_v3_t));
|
||||
new_meta_hdr->meta_area_size = new_meta_len;
|
||||
new_clean_entry_bitmap_size = (new_meta_hdr->data_block_size / new_meta_hdr->bitmap_granularity + 7) / 8;
|
||||
new_clean_entry_size = 0;
|
||||
new_entries_per_block = 0;
|
||||
|
||||
@@ -111,7 +111,7 @@ int disk_tool_t::raw_resize()
|
||||
fprintf(stderr, "Done\n");
|
||||
ret:
|
||||
free_new_meta();
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
int disk_tool_t::resize_parse_params()
|
||||
@@ -154,9 +154,6 @@ int disk_tool_t::resize_parse_params()
|
||||
? parse_size(options["new_journal_offset"]) : dsk.journal_offset;
|
||||
new_journal_len = options.find("new_journal_len") != options.end()
|
||||
? parse_size(options["new_journal_len"]) : dsk.journal_len;
|
||||
new_meta_format = options.find("new_meta_format") != options.end()
|
||||
? stoull_full(options["new_meta_format"]) : 0;
|
||||
skip_obsolete = options.find("skip_obsolete") != options.end();
|
||||
if (new_data_len+new_data_offset > dsk.data_device_size)
|
||||
new_data_len = dsk.data_device_size-new_data_offset;
|
||||
if (new_meta_device == dsk.data_device && new_data_offset < new_meta_offset &&
|
||||
@@ -205,10 +202,7 @@ void disk_tool_t::resize_init(blockstore_meta_header_v3_t *hdr)
|
||||
{
|
||||
dsk.meta_format = hdr->version;
|
||||
}
|
||||
if (new_meta_format == 0)
|
||||
{
|
||||
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
|
||||
}
|
||||
new_meta_format = hdr && hdr->version == BLOCKSTORE_META_FORMAT_HEAP ? BLOCKSTORE_META_FORMAT_HEAP : BLOCKSTORE_META_FORMAT_V2;
|
||||
dsk.calc_lengths();
|
||||
if (((new_data_offset-dsk.data_offset) % dsk.data_block_size))
|
||||
{
|
||||
@@ -563,66 +557,6 @@ void disk_tool_t::remap_small_write(heap_entry_t *wr)
|
||||
}
|
||||
}
|
||||
|
||||
void disk_tool_t::fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr)
|
||||
{
|
||||
uint64_t block_num = big_wr->big().block_num;
|
||||
clean_disk_entry *new_entry = (clean_disk_entry*)(new_meta_buf + dsk.meta_block_size +
|
||||
dsk.meta_block_size*(block_num / new_entries_per_block) +
|
||||
new_clean_entry_size*(block_num % new_entries_per_block));
|
||||
new_entry->oid = (object_id){ .inode = big_wr->inode, .stripe = big_wr->stripe };
|
||||
new_entry->version = big_wr->version;
|
||||
memcpy(new_entry->bitmap, big_wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
memcpy(new_entry->bitmap + new_clean_entry_bitmap_size, big_wr->get_int_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
memcpy(new_entry->bitmap + 2*new_clean_entry_bitmap_size, big_wr->get_checksums(heap), new_data_csum_size);
|
||||
uint32_t *new_entry_csum = (uint32_t*)(((uint8_t*)new_entry) + new_clean_entry_size - 4);
|
||||
*new_entry_csum = crc32c(0, new_entry, new_clean_entry_size - 4);
|
||||
}
|
||||
|
||||
void disk_tool_t::fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr)
|
||||
{
|
||||
assert(wr->type() == BS_HEAP_SMALL_WRITE ||
|
||||
wr->type() == BS_HEAP_BIG_WRITE ||
|
||||
wr->type() == BS_HEAP_BIG_INTENT);
|
||||
uint32_t je_size = ((wr->entry_type & BS_HEAP_TYPE) == BS_HEAP_SMALL_WRITE
|
||||
? sizeof(journal_entry_small_write) + dsk.dirty_dyn_size(wr->small().offset, wr->small().len)
|
||||
: sizeof(journal_entry_big_write) + dsk.dirty_dyn_size(0, dsk.data_block_size));
|
||||
choose_journal_block(je_size);
|
||||
journal_entry *je = (journal_entry*)(new_journal_ptr + new_journal_in_pos);
|
||||
je->magic = JOURNAL_MAGIC;
|
||||
je->type = (wr->entry_type & BS_HEAP_STABLE) ? JE_SMALL_WRITE_INSTANT : JE_SMALL_WRITE;
|
||||
je->size = je_size;
|
||||
je->crc32_prev = new_crc32_prev;
|
||||
je->small_write.oid = (object_id){ .inode = wr->inode, .stripe = wr->stripe };
|
||||
je->small_write.version = wr->version;
|
||||
if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||
{
|
||||
je->small_write.offset = wr->small().offset;
|
||||
je->small_write.len = wr->small().len;
|
||||
je->small_write.data_offset = new_journal_data-new_journal_buf;
|
||||
if (je->small_write.data_offset + je->small_write.len > new_journal_len)
|
||||
{
|
||||
fprintf(stderr, "Error: live entries don't fit to the new journal\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(new_journal_data, buffer_area+wr->small().location, je->small_write.len);
|
||||
new_journal_data += je->small_write.len;
|
||||
if (dsk.data_csum_type == 0 && wr->get_checksum(heap))
|
||||
je->small_write.crc32_data = *wr->get_checksum(heap);
|
||||
}
|
||||
else
|
||||
{
|
||||
je->big_write.location = wr->big_location(heap);
|
||||
}
|
||||
memcpy((uint8_t*)je + je->size, wr->get_ext_bitmap(heap), new_clean_entry_bitmap_size);
|
||||
if (dsk.data_csum_type != 0 && wr->get_checksums(heap))
|
||||
{
|
||||
memcpy((uint8_t*)je + je->size + new_clean_entry_bitmap_size, wr->get_checksums(heap), heap->get_csum_size(wr));
|
||||
}
|
||||
je->crc32 = je_crc32(je);
|
||||
new_journal_in_pos += je->size;
|
||||
new_crc32_prev = je->crc32;
|
||||
}
|
||||
|
||||
int disk_tool_t::resize_rebuild_meta()
|
||||
{
|
||||
new_meta_buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, new_meta_len);
|
||||
@@ -630,13 +564,12 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
new_meta_hdr = (blockstore_meta_header_v3_t *)new_meta_buf;
|
||||
uint64_t new_meta_pos = dsk.meta_block_size;
|
||||
uint64_t next_lsn = 0;
|
||||
std::vector<heap_entry_t*> writes;
|
||||
int r = process_meta(
|
||||
[&](blockstore_meta_header_v3_t *hdr)
|
||||
{
|
||||
new_meta_hdr->zero = 0;
|
||||
new_meta_hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
||||
new_meta_hdr->version = new_meta_format == 0 ? BLOCKSTORE_META_FORMAT_HEAP : new_meta_format;
|
||||
new_meta_hdr->version = new_meta_format;
|
||||
new_meta_hdr->meta_block_size = dsk.meta_block_size;
|
||||
new_meta_hdr->data_block_size = dsk.data_block_size;
|
||||
new_meta_hdr->bitmap_granularity = dsk.bitmap_granularity ? dsk.bitmap_granularity : 4096;
|
||||
@@ -654,18 +587,16 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
},
|
||||
[&](blockstore_heap_t *heap, heap_entry_t *obj, uint32_t meta_block_num)
|
||||
{
|
||||
assert(new_meta_format == BLOCKSTORE_META_FORMAT_HEAP);
|
||||
if (!obj)
|
||||
{
|
||||
// Finish
|
||||
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
while (new_meta_pos < new_meta_len)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
while (new_meta_pos < new_meta_len)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos += dsk.meta_block_size;
|
||||
}
|
||||
new_meta_pos += dsk.meta_block_size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -679,72 +610,24 @@ int disk_tool_t::resize_rebuild_meta()
|
||||
{
|
||||
remap_small_write(wr);
|
||||
}
|
||||
else if (wr->type() != BS_HEAP_DELETE && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP)
|
||||
// New -> New
|
||||
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
|
||||
{
|
||||
fprintf(stderr, "Object %jx:%jx can't be converted to the old format because it contains an entry of type 0x%x%s\n",
|
||||
wr->inode, wr->stripe, wr->entry_type,
|
||||
(wr->type() == BS_HEAP_INTENT_WRITE ? " (intent_write)" : ""));
|
||||
exit(1);
|
||||
}
|
||||
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
||||
{
|
||||
// New -> New
|
||||
if ((new_meta_pos % dsk.meta_block_size) + wr->size > dsk.meta_block_size)
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
if (new_meta_pos >= new_meta_len)
|
||||
{
|
||||
heap->fill_block_empty_space(new_meta_buf, new_meta_pos);
|
||||
new_meta_pos = (new_meta_pos/dsk.meta_block_size + 1) * dsk.meta_block_size;
|
||||
if (new_meta_pos >= new_meta_len)
|
||||
{
|
||||
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
|
||||
new_meta_pos += wr->size;
|
||||
if (skip_obsolete && wr->type() == BS_HEAP_BIG_WRITE && stable)
|
||||
{
|
||||
// Skip older writes
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// New -> Old
|
||||
if (wr->type() == BS_HEAP_DELETE && stable)
|
||||
{
|
||||
// Object is deleted, skip it
|
||||
return false;
|
||||
}
|
||||
if (wr->type() == BS_HEAP_BIG_WRITE && stable)
|
||||
{
|
||||
fill_old_clean_entry(heap, wr);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
writes.push_back(wr);
|
||||
fprintf(stderr, "New metadata doesn't fit into the provided area\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
memcpy(new_meta_buf + new_meta_pos, wr, wr->size);
|
||||
new_meta_pos += wr->size;
|
||||
return true;
|
||||
};
|
||||
if (new_meta_format != BLOCKSTORE_META_FORMAT_HEAP || skip_obsolete)
|
||||
for (auto wr = obj; wr; wr = heap->prev(wr))
|
||||
{
|
||||
heap->iterate_with_stable(obj, obj->lsn, handle_write);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto wr = obj; wr; wr = heap->prev(wr))
|
||||
{
|
||||
handle_write(wr, false);
|
||||
}
|
||||
}
|
||||
if (writes.size())
|
||||
{
|
||||
for (size_t i = writes.size(); i > 0; i--)
|
||||
{
|
||||
fill_old_journal_entry(heap, writes[i-1]);
|
||||
}
|
||||
writes.clear();
|
||||
handle_write(wr, false);
|
||||
}
|
||||
},
|
||||
[&](uint64_t block_num, clean_disk_entry *entry, uint8_t *bitmap)
|
||||
|
||||
@@ -586,8 +586,8 @@ send_again:
|
||||
}
|
||||
// Estimate reply WR count, create WR and SGE arrays
|
||||
xdr_write_chunk *reply_chunk = rop->in_rdma_msg.rdma_body.rdma_msg.rdma_reply;
|
||||
int reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
|
||||
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + (reply_chunk ? reply_chunk_wr_count : 0);
|
||||
uint32_t reply_chunk_wr_count = (reply_chunk ? reply_chunk->target.target_len : 0);
|
||||
uint32_t wr_count = 1 + (chunk_iov ? 1 : 0) + reply_chunk_wr_count;
|
||||
if (wr_count > ctx->max_send_wr)
|
||||
{
|
||||
fprintf(stderr, "Reply fragmentation (%u) exceeds max_send_wr (%u), sending ERR_CHUNK\n", wr_count, ctx->max_send_wr);
|
||||
@@ -622,7 +622,7 @@ chunk_error:
|
||||
{
|
||||
size_t reply_chunk_len = 0;
|
||||
size_t left = msg_size;
|
||||
for (uint32_t i = 0; i < reply_chunk->target.target_len; i++)
|
||||
for (uint32_t i = 0; i < reply_chunk_wr_count; i++)
|
||||
{
|
||||
reply_chunk_len += reply_chunk->target.target_val[i].length;
|
||||
if (reply_chunk->target.target_val[i].length > left)
|
||||
@@ -653,7 +653,7 @@ chunk_error:
|
||||
}
|
||||
ibv_sge sges[wr_count];
|
||||
ibv_send_wr wrs[wr_count];
|
||||
int wr_pos = 0;
|
||||
uint32_t wr_pos = 0;
|
||||
// Use a buffer from rdma_malloc for the reply
|
||||
assert(!rop->buffer);
|
||||
rop->buffer = rdma_malloc_alloc(conn_dev->alloc, hdr_size+msg_size);
|
||||
@@ -685,7 +685,7 @@ chunk_error:
|
||||
if (reply_chunk)
|
||||
{
|
||||
size_t pos = hdr_size;
|
||||
for (uint32_t i = 0; i < reply_chunk->target.target_len && pos < msg_size; i++)
|
||||
for (uint32_t i = 0; i < reply_chunk_wr_count && pos < msg_size; i++)
|
||||
{
|
||||
uint32_t len = (reply_chunk->target.target_val[i].length < msg_size-pos
|
||||
? reply_chunk->target.target_val[i].length : msg_size-pos);
|
||||
|
||||
@@ -19,20 +19,19 @@ target_link_libraries(vitastor-osd
|
||||
|
||||
# osd_rmw_test
|
||||
add_executable(osd_rmw_test EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
|
||||
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES} tcmalloc_minimal)
|
||||
target_link_libraries(osd_rmw_test Jerasure ${ISAL_LIBRARIES})
|
||||
add_dependencies(build_tests osd_rmw_test)
|
||||
add_test(NAME osd_rmw_test COMMAND osd_rmw_test)
|
||||
|
||||
if (ISAL_LIBRARIES)
|
||||
add_executable(osd_rmw_test_je EXCLUDE_FROM_ALL osd_rmw_test.cpp ../util/allocator.cpp)
|
||||
target_compile_definitions(osd_rmw_test_je PUBLIC -DNO_ISAL)
|
||||
target_link_libraries(osd_rmw_test_je Jerasure tcmalloc_minimal)
|
||||
target_link_libraries(osd_rmw_test_je Jerasure)
|
||||
add_dependencies(build_tests osd_rmw_test_je)
|
||||
add_test(NAME osd_rmw_test_jerasure COMMAND osd_rmw_test_je)
|
||||
endif (ISAL_LIBRARIES)
|
||||
|
||||
# osd_peering_pg_test
|
||||
add_executable(osd_peering_pg_test EXCLUDE_FROM_ALL osd_peering_pg_test.cpp osd_peering_pg.cpp)
|
||||
target_link_libraries(osd_peering_pg_test tcmalloc_minimal)
|
||||
add_dependencies(build_tests osd_peering_pg_test)
|
||||
add_test(NAME osd_peering_pg_test COMMAND osd_peering_pg_test)
|
||||
|
||||
+3
-7
@@ -15,22 +15,19 @@
|
||||
#include "str_util.h"
|
||||
#include "json_util.h"
|
||||
|
||||
osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
|
||||
osd_t::osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd)
|
||||
{
|
||||
zero_buffer_size = 1<<20;
|
||||
zero_buffer = malloc_or_die(zero_buffer_size);
|
||||
memset(zero_buffer, 0, zero_buffer_size);
|
||||
|
||||
this->ringloop = ringloop;
|
||||
this->tfd = tfd;
|
||||
|
||||
this->cli_config = config.object_items();
|
||||
this->file_config = msgr.read_config(this->cli_config);
|
||||
parse_config(true);
|
||||
|
||||
epmgr = new epoll_manager_t(ringloop);
|
||||
// FIXME: Use timerfd_interval based directly on io_uring
|
||||
this->tfd = epmgr->tfd;
|
||||
|
||||
if (json_is_true(this->config["osd_memlock"]))
|
||||
{
|
||||
// Lock all OSD memory if requested
|
||||
@@ -99,7 +96,6 @@ osd_t::~osd_t()
|
||||
}
|
||||
ringloop->unregister_consumer(&consumer);
|
||||
ringloop->unregister_consumer(&init_consumer);
|
||||
delete epmgr;
|
||||
if (bs)
|
||||
delete bs;
|
||||
#ifdef WITH_RDMACM
|
||||
@@ -398,7 +394,7 @@ void osd_t::bind_socket()
|
||||
{
|
||||
int listen_fd = create_and_bind_socket(bind_address, listening_port ? listening_port : bind_port, listen_backlog, &listening_port);
|
||||
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events)
|
||||
tfd->set_fd_handler(listen_fd, false, [this](int fd, int events)
|
||||
{
|
||||
msgr.accept_connections(fd);
|
||||
});
|
||||
|
||||
+5
-7
@@ -19,7 +19,6 @@
|
||||
#include "blockstore.h"
|
||||
#include "ringloop.h"
|
||||
#include "timerfd_manager.h"
|
||||
#include "epoll_manager.h"
|
||||
#include "osd_peering_pg.h"
|
||||
#include "messenger.h"
|
||||
#include "etcd_state_client.h"
|
||||
@@ -210,9 +209,8 @@ class osd_t
|
||||
void *zero_buffer = NULL;
|
||||
uint64_t zero_buffer_size = 0;
|
||||
uint32_t bs_block_size, bs_bitmap_granularity, clean_entry_bitmap_size;
|
||||
ring_loop_t *ringloop = NULL;
|
||||
ring_loop_i *ringloop = NULL;
|
||||
timerfd_manager_t *tfd = NULL;
|
||||
epoll_manager_t *epmgr = NULL;
|
||||
|
||||
int listening_port = 0;
|
||||
std::vector<std::string> bind_addresses;
|
||||
@@ -352,8 +350,7 @@ class osd_t
|
||||
std::function<int(pg_osd_set_t & new_set)> calc_set);
|
||||
pg_osd_set_state_t *mark_object_corrupted(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref);
|
||||
pg_osd_set_state_t *mark_partial_write(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref);
|
||||
pg_osd_set_state_t *mark_partial_write(pg_t & pg, osd_op_t *cur_op);
|
||||
void deref_object_state(pg_t & pg, pg_osd_set_state_t **object_state, bool deref);
|
||||
bool remember_unstable_write(osd_op_t *cur_op, pg_t & pg, pg_osd_set_t & loc_set, int base_state);
|
||||
void handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op);
|
||||
@@ -366,9 +363,10 @@ class osd_t
|
||||
osd_rmw_stripe_t *stripes, const uint64_t* osd_set, osd_op_t *cur_op, int subop_idx, int zero_read);
|
||||
void submit_primary_subop(osd_op_t *cur_op, osd_op_t *subop,
|
||||
osd_rmw_stripe_t *si, bool wr, inode_t inode, uint64_t op_version);
|
||||
bool submit_to_osd(osd_op_t *subop, osd_num_t osd_num);
|
||||
void submit_primary_del_subops(osd_op_t *cur_op, uint64_t *cur_set, uint64_t set_size, pg_osd_set_t & loc_set);
|
||||
void submit_primary_del_batch(osd_op_t *cur_op, obj_ver_osd_t *chunks_to_delete, int chunks_to_delete_count);
|
||||
int submit_primary_sync_subops(osd_op_t *cur_op);
|
||||
void submit_primary_sync_subops(osd_op_t *cur_op);
|
||||
void submit_primary_stab_subops(osd_op_t *cur_op);
|
||||
void submit_primary_rollback_subops(osd_op_t *cur_op, const uint64_t* osd_set);
|
||||
|
||||
@@ -392,7 +390,7 @@ class osd_t
|
||||
}
|
||||
|
||||
public:
|
||||
osd_t(const json11::Json & config, ring_loop_t *ringloop);
|
||||
osd_t(const json11::Json & config, ring_loop_i *ringloop, timerfd_manager_t *tfd);
|
||||
~osd_t();
|
||||
void force_stop(int exitcode);
|
||||
bool shutdown();
|
||||
|
||||
+2
-10
@@ -97,6 +97,7 @@ void osd_t::handle_flush_op(bool rollback, pool_id_t pool_id, pg_num_t pg_num, p
|
||||
{
|
||||
// Will repeer/stop this PG
|
||||
msgr.stop_client(peer_it->second->client_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,17 +214,8 @@ bool osd_t::submit_flush_op(pool_id_t pool_id, pg_num_t pg_num, pg_flush_batch_t
|
||||
handle_flush_op(op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK, pool_id, pg_num, fb, peer_osd, op->reply.hdr.retval);
|
||||
delete op;
|
||||
};
|
||||
auto peer_it = msgr.osd_peers.find(peer_osd);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
if (!submit_to_osd(op, peer_osd))
|
||||
{
|
||||
op->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(op);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
op->reply.hdr.retval = -EPIPE;
|
||||
op->callback(op);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include "epoll_manager.h"
|
||||
#include "osd.h"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
@@ -63,13 +64,15 @@ int main(int narg, char *args[])
|
||||
signal(SIGINT, handle_sigint);
|
||||
signal(SIGTERM, handle_sigint);
|
||||
ring_loop_t *ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
|
||||
osd = new osd_t(config, ringloop);
|
||||
epoll_manager_t *epmgr = new epoll_manager_t(ringloop);
|
||||
osd = new osd_t(config, ringloop, epmgr->tfd);
|
||||
while (1)
|
||||
{
|
||||
ringloop->loop();
|
||||
ringloop->wait();
|
||||
}
|
||||
delete osd;
|
||||
delete epmgr;
|
||||
delete ringloop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -140,10 +140,12 @@ void osd_t::reset_pg(pg_t & pg)
|
||||
copies_to_delete_after_sync_count -= pg.copies_to_delete_after_sync.size();
|
||||
pg.copies_to_delete_after_sync.clear();
|
||||
corrupted_objects -= pg.corrupted_count;
|
||||
inconsistent_objects -= pg.inconsistent_objects.size();
|
||||
incomplete_objects -= pg.incomplete_objects.size();
|
||||
misplaced_objects -= pg.misplaced_objects.size();
|
||||
degraded_objects -= pg.degraded_objects.size();
|
||||
pg.corrupted_count = 0;
|
||||
pg.inconsistent_objects.clear();
|
||||
pg.incomplete_objects.clear();
|
||||
pg.misplaced_objects.clear();
|
||||
pg.degraded_objects.clear();
|
||||
@@ -351,6 +353,7 @@ bool osd_t::continue_pg_peering(pg_t & pg)
|
||||
pg.calc_object_states(log_level);
|
||||
report_pg_state(pg);
|
||||
schedule_scrub(pg);
|
||||
inconsistent_objects += pg.inconsistent_objects.size();
|
||||
incomplete_objects += pg.incomplete_objects.size();
|
||||
misplaced_objects += pg.misplaced_objects.size();
|
||||
// FIXME: degraded objects may currently include misplaced, too! Report them separately?
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
#include "malloc_or_die.h"
|
||||
#include "osd_peering_pg.h"
|
||||
|
||||
+41
-22
@@ -269,6 +269,7 @@ resume_0:
|
||||
finish_op(cur_op, -EIO);
|
||||
return;
|
||||
}
|
||||
assert(!cur_op->buf);
|
||||
cur_op->buf = alloc_read_buffer(op_data->stripes, pg ? pg->pg_data_size : 1, 0);
|
||||
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
||||
}
|
||||
@@ -281,6 +282,7 @@ resume_0:
|
||||
}
|
||||
// Submit reads
|
||||
op_data->degraded = 1;
|
||||
assert(!cur_op->buf);
|
||||
cur_op->buf = alloc_read_buffer(op_data->stripes, pg->pg_size, 0);
|
||||
submit_primary_subops(SUBMIT_RMW_READ, op_data->target_ver, op_data->prev_set, cur_op);
|
||||
}
|
||||
@@ -300,7 +302,11 @@ resume_2:
|
||||
// FIXME: ref = true ideally... because new_state != state is not necessarily true if it's freed and recreated
|
||||
auto new_object_state = mark_object_corrupted(*pg, op_data->oid, op_data->object_state, op_data->stripes, false);
|
||||
if (new_object_state != op_data->object_state)
|
||||
{
|
||||
free(cur_op->buf);
|
||||
cur_op->buf = NULL;
|
||||
goto resume_0;
|
||||
}
|
||||
}
|
||||
finish_op(cur_op, op_data->errcode);
|
||||
return;
|
||||
@@ -429,24 +435,33 @@ pg_osd_set_state_t *osd_t::mark_object_corrupted(pg_t & pg, object_id oid,
|
||||
}
|
||||
|
||||
// Mark the object as partially updated (probably due to a ENOSPC)
|
||||
pg_osd_set_state_t *osd_t::mark_partial_write(pg_t & pg, object_id oid, pg_osd_set_state_t *prev_object_state,
|
||||
osd_rmw_stripe_t *stripes, bool ref)
|
||||
pg_osd_set_state_t *osd_t::mark_partial_write(pg_t & pg, osd_op_t *cur_op)
|
||||
{
|
||||
return mark_object(pg, oid, prev_object_state, ref, [stripes](pg_osd_set_t & new_set)
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
return mark_object(pg, op_data->oid, op_data->object_state, true, [&](pg_osd_set_t & new_set)
|
||||
{
|
||||
// Mark object chunk(s) as outdated
|
||||
int changes = 0;
|
||||
for (auto chunk_it = new_set.begin(); chunk_it != new_set.end(); )
|
||||
for (auto & chunk: new_set)
|
||||
{
|
||||
auto & chunk = *chunk_it;
|
||||
if (stripes[chunk.role].osd_num == chunk.osd_num &&
|
||||
stripes[chunk.role].read_error &&
|
||||
chunk.loc_bad != LOC_OUTDATED)
|
||||
if (chunk.loc_bad != LOC_OUTDATED)
|
||||
{
|
||||
changes++;
|
||||
chunk.loc_bad = LOC_OUTDATED;
|
||||
bool success = false;
|
||||
for (int i = 0; i < op_data->n_subops; i++)
|
||||
{
|
||||
if (op_data->subops[i].osd_num == chunk.osd_num &&
|
||||
op_data->subops[i].reply.hdr.retval == op_data->subops[i].req.sec_rw.len)
|
||||
{
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
changes++;
|
||||
chunk.loc_bad = LOC_OUTDATED;
|
||||
}
|
||||
}
|
||||
chunk_it++;
|
||||
}
|
||||
return changes;
|
||||
});
|
||||
@@ -522,19 +537,19 @@ pg_osd_set_state_t* osd_t::add_object_to_set(pg_t & pg, const object_id oid, con
|
||||
{
|
||||
this->incomplete_objects++;
|
||||
obj_state |= OBJ_INCOMPLETE;
|
||||
pg_state_bits = PG_HAS_INCOMPLETE;
|
||||
pg_state_bits |= PG_HAS_INCOMPLETE;
|
||||
}
|
||||
else if (n_roles < pg.pg_cursize)
|
||||
{
|
||||
this->degraded_objects++;
|
||||
obj_state |= OBJ_DEGRADED;
|
||||
pg_state_bits = PG_HAS_DEGRADED;
|
||||
pg_state_bits |= PG_HAS_DEGRADED;
|
||||
}
|
||||
else if (n_misplaced > 0 || n_outdated > 0)
|
||||
{
|
||||
this->misplaced_objects++;
|
||||
obj_state |= OBJ_MISPLACED;
|
||||
pg_state_bits = PG_HAS_MISPLACED;
|
||||
pg_state_bits |= PG_HAS_MISPLACED;
|
||||
}
|
||||
if (this->log_level >= log_at_level)
|
||||
{
|
||||
@@ -614,7 +629,8 @@ bool osd_t::remove_object_from_state(object_id & oid, pg_osd_set_state_t **objec
|
||||
get_object_osd_set(pg, oid, &recheck_state);
|
||||
if (recheck_state != *object_state)
|
||||
{
|
||||
recheck_state->ref_count++;
|
||||
if (recheck_state)
|
||||
recheck_state->ref_count++;
|
||||
(*object_state)->ref_count--;
|
||||
*object_state = recheck_state;
|
||||
return false;
|
||||
@@ -769,8 +785,11 @@ resume_3:
|
||||
op_data->fact_ver++;
|
||||
submit_primary_del_subops(cur_op, NULL, 0, op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set);
|
||||
resume_4:
|
||||
op_data->st = 4;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 4;
|
||||
return;
|
||||
}
|
||||
resume_5:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -780,6 +799,11 @@ resume_5:
|
||||
}
|
||||
// Remove version override
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
// Mark PG and OSDs as dirty
|
||||
for (auto & chunk: (op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set))
|
||||
{
|
||||
this->dirty_osds.insert(chunk.osd_num);
|
||||
}
|
||||
// Adjust PG stats after "instant stabilize", because we need object_state above
|
||||
if (!op_data->object_state)
|
||||
{
|
||||
@@ -790,11 +814,6 @@ resume_5:
|
||||
remove_object_from_state(op_data->oid, &op_data->object_state, pg);
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
}
|
||||
// Mark PG and OSDs as dirty
|
||||
for (auto & chunk: (op_data->object_state ? op_data->object_state->osd_set : pg.cur_loc_set))
|
||||
{
|
||||
this->dirty_osds.insert(chunk.osd_num);
|
||||
}
|
||||
for (auto cl_it = msgr.clients.find(cur_op->client_id); cl_it != msgr.clients.end(); )
|
||||
{
|
||||
cl_it->second->dirty_pgs.insert({ .pool_id = pg.pool_id, .pg_num = pg.pg_num });
|
||||
|
||||
@@ -190,7 +190,6 @@ int osd_t::collect_bitmap_requests(osd_op_t *cur_op, pg_t & pg, std::vector<bitm
|
||||
memcpy(local_stripes, op_data->stripes, sizeof(osd_rmw_stripe_t) * pg.pg_size);
|
||||
if (extend_missing_stripes(local_stripes, cur_set, pg.pg_data_size, pg.pg_size) < 0)
|
||||
{
|
||||
free(op_data->snapshot_bitmaps);
|
||||
return -1;
|
||||
}
|
||||
int need_at_least = 0;
|
||||
@@ -319,19 +318,7 @@ int osd_t::submit_bitmap_subops(osd_op_t *cur_op, pg_t & pg)
|
||||
}
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
auto peer_it = msgr.osd_peers.find(subop_osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
submit_to_osd(subop, subop_osd_num);
|
||||
subop_idx++;
|
||||
}
|
||||
prev = i+1;
|
||||
|
||||
@@ -270,22 +270,30 @@ void osd_t::submit_primary_subop(osd_op_t *cur_op, osd_op_t *subop,
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
auto peer_it = msgr.osd_peers.find(si->osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
submit_to_osd(subop, si->osd_num);
|
||||
}
|
||||
}
|
||||
|
||||
bool osd_t::submit_to_osd(osd_op_t *subop, osd_num_t osd_num)
|
||||
{
|
||||
subop->osd_num = osd_num;
|
||||
auto peer_it = msgr.osd_peers.find(osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subop->client_id = 0;
|
||||
subop->reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t bs_op_to_osd_op[] = {
|
||||
0,
|
||||
OSD_OP_SEC_READ, // BS_OP_READ = 1
|
||||
@@ -359,7 +367,7 @@ void osd_t::add_bs_subop_stats(osd_op_t *subop, bool recovery_related)
|
||||
uint64_t opcode = bs_op_to_osd_op[subop->bs_op->opcode];
|
||||
timespec tv_end;
|
||||
clock_gettime(CLOCK_REALTIME, &tv_end);
|
||||
uint64_t len = (opcode == OSD_OP_SEC_READ || opcode == OSD_OP_SEC_WRITE)
|
||||
uint64_t len = (opcode == OSD_OP_SEC_READ || opcode == OSD_OP_SEC_WRITE || opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||
? subop->bs_op->len : 0;
|
||||
msgr.inc_op_stats(msgr.stats, opcode, subop->tv_begin, tv_end, len);
|
||||
if (recovery_related)
|
||||
@@ -482,8 +490,11 @@ void osd_t::handle_primary_subop(osd_op_t *subop, osd_op_t *cur_op)
|
||||
}
|
||||
if ((op_data->errors + op_data->done) >= op_data->n_subops)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
if (!op_data->errors || !op_data->done || opcode != OSD_OP_SEC_WRITE && opcode != OSD_OP_SEC_WRITE_STABLE)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
}
|
||||
op_data->st++;
|
||||
if (cur_op->req.hdr.opcode == OSD_OP_READ)
|
||||
{
|
||||
@@ -616,86 +627,82 @@ void osd_t::submit_primary_del_batch(osd_op_t *cur_op, obj_ver_osd_t *chunks_to_
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
auto peer_it = msgr.osd_peers.find(chunk.osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subops[i].client_id = 0;
|
||||
subops[i].reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop = &subops[i]]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
submit_to_osd(&subops[i], chunk.osd_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int osd_t::submit_primary_sync_subops(osd_op_t *cur_op)
|
||||
void osd_t::submit_primary_sync_subops(osd_op_t *cur_op)
|
||||
{
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
int n_osds = op_data->dirty_osd_count;
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->done = op_data->errors = op_data->errcode = 0;
|
||||
op_data->n_subops = n_osds;
|
||||
if (op_data->n_subops <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->subops = subops;
|
||||
robin_hood::unordered_flat_map<uint64_t, osd_client_t*>::iterator peer_it;
|
||||
int subop_idx = 0;
|
||||
for (int i = 0; i < n_osds; i++)
|
||||
{
|
||||
osd_num_t sync_osd = op_data->dirty_osds[i];
|
||||
osd_op_t *subop = &subops[subop_idx];
|
||||
if (sync_osd == this->osd_num)
|
||||
{
|
||||
clock_gettime(CLOCK_REALTIME, &subops[i].tv_begin);
|
||||
subops[i].op_type = (uint64_t)cur_op;
|
||||
subops[i].bs_op = new blockstore_op_t({
|
||||
clock_gettime(CLOCK_REALTIME, &subop->tv_begin);
|
||||
subop->op_type = (uint64_t)cur_op;
|
||||
subop->bs_op = new blockstore_op_t({
|
||||
.opcode = BS_OP_SYNC,
|
||||
.callback = [subop = &subops[i], this](blockstore_op_t *bs_subop)
|
||||
.callback = [subop, this](blockstore_op_t *bs_subop)
|
||||
{
|
||||
handle_primary_bs_subop(subop);
|
||||
},
|
||||
});
|
||||
bs->enqueue_op(subops[i].bs_op);
|
||||
bs->enqueue_op(subop->bs_op);
|
||||
subop_idx++;
|
||||
}
|
||||
else if ((peer_it = msgr.osd_peers.find(sync_osd)) != msgr.osd_peers.end())
|
||||
{
|
||||
subops[i].op_type = OSD_OP_OUT;
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
subops[i].req = (osd_any_op_t){ .sec_sync = {
|
||||
subop->op_type = OSD_OP_OUT;
|
||||
subop->osd_num = sync_osd;
|
||||
subop->client_id = peer_it->second->client_id;
|
||||
subop->req = (osd_any_op_t){ .sec_sync = {
|
||||
.header = {
|
||||
.magic = SECONDARY_OSD_OP_MAGIC,
|
||||
.opcode = OSD_OP_SEC_SYNC,
|
||||
},
|
||||
.flags = cur_op->client_id == SELF_CLIENT && cur_op->req.hdr.opcode != OSD_OP_SCRUB ? OSD_OP_RECOVERY_RELATED : 0,
|
||||
} };
|
||||
subops[i].callback = [cur_op, this](osd_op_t *subop)
|
||||
subop->callback = [cur_op, this](osd_op_t *subop)
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
op_data->done++;
|
||||
msgr.outbox_push(subop);
|
||||
subop_idx++;
|
||||
}
|
||||
}
|
||||
if (op_data->done >= op_data->n_subops)
|
||||
op_data->n_subops = subop_idx;
|
||||
if (subop_idx <= 0)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void osd_t::submit_primary_stab_subops(osd_op_t *cur_op)
|
||||
{
|
||||
osd_primary_op_data_t *op_data = cur_op->op_data;
|
||||
int n_osds = op_data->unstable_write_osds->size();
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->done = op_data->errors = op_data->errcode = 0;
|
||||
op_data->n_subops = n_osds;
|
||||
if (op_data->n_subops <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
osd_op_t *subops = new osd_op_t[n_osds];
|
||||
op_data->subops = subops;
|
||||
for (int i = 0; i < n_osds; i++)
|
||||
{
|
||||
@@ -733,19 +740,7 @@ void osd_t::submit_primary_stab_subops(osd_op_t *cur_op)
|
||||
{
|
||||
handle_primary_subop(subop, cur_op);
|
||||
};
|
||||
auto peer_it = msgr.osd_peers.find(stab_osd.osd_num);
|
||||
if (peer_it != msgr.osd_peers.end())
|
||||
{
|
||||
subops[i].client_id = peer_it->second->client_id;
|
||||
msgr.outbox_push(&subops[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fail it immediately
|
||||
subops[i].client_id = 0;
|
||||
subops[i].reply.hdr.retval = -EPIPE;
|
||||
ringloop->set_immediate([subop = &subops[i]]() { std::function<void(osd_op_t*)>(subop->callback)(subop); });
|
||||
}
|
||||
submit_to_osd(&subops[i], stab_osd.osd_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -831,8 +826,7 @@ void osd_t::submit_primary_rollback_subops(osd_op_t *cur_op, const uint64_t* osd
|
||||
op_data->oid.inode, op_data->oid.stripe | role, op_data->target_ver-1
|
||||
);
|
||||
#endif
|
||||
subop->client_id = msgr.osd_peers.at(osd_set[role])->client_id;
|
||||
msgr.outbox_push(subop);
|
||||
submit_to_osd(subop, osd_set[role]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -127,13 +127,13 @@ resume_2:
|
||||
if (immediate_commit != IMMEDIATE_ALL)
|
||||
{
|
||||
// SYNC
|
||||
if (!submit_primary_sync_subops(cur_op))
|
||||
{
|
||||
goto resume_4;
|
||||
}
|
||||
submit_primary_sync_subops(cur_op);
|
||||
resume_3:
|
||||
op_data->st = 3;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 3;
|
||||
return;
|
||||
}
|
||||
resume_4:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -145,8 +145,11 @@ resume_4:
|
||||
// Stabilize version sets, if any
|
||||
submit_primary_stab_subops(cur_op);
|
||||
resume_5:
|
||||
op_data->st = 5;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 5;
|
||||
return;
|
||||
}
|
||||
}
|
||||
resume_6:
|
||||
if (op_data->errors > 0)
|
||||
@@ -191,8 +194,11 @@ resume_6:
|
||||
// Actually delete copies which we wanted to delete
|
||||
submit_primary_del_batch(cur_op, op_data->copies_to_delete, op_data->copies_to_delete_count);
|
||||
resume_7:
|
||||
op_data->st = 7;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 7;
|
||||
return;
|
||||
}
|
||||
resume_8:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
|
||||
@@ -188,7 +188,7 @@ resume_3:
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((op_data->fact_ver & ((uint64_t)1 << (64-PG_EPOCH_BITS) - 1)) == ((uint64_t)1 << (64-PG_EPOCH_BITS) - 1))
|
||||
if ((op_data->fact_ver & (((uint64_t)1 << (64-PG_EPOCH_BITS)) - 1)) == (((uint64_t)1 << (64-PG_EPOCH_BITS)) - 1))
|
||||
{
|
||||
assert(pg.epoch != (((uint64_t)1 << PG_EPOCH_BITS)-1));
|
||||
pg.epoch++;
|
||||
@@ -231,8 +231,11 @@ resume_10:
|
||||
}
|
||||
submit_primary_subops(SUBMIT_WRITE, op_data->target_ver, pg.cur_set.data(), cur_op);
|
||||
resume_4:
|
||||
op_data->st = 4;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 4;
|
||||
return;
|
||||
}
|
||||
resume_5:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -247,8 +250,11 @@ resume_5:
|
||||
{
|
||||
submit_primary_rollback_subops(cur_op, pg.cur_set.data());
|
||||
resume_11:
|
||||
op_data->st = 11;
|
||||
return;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 11;
|
||||
return;
|
||||
}
|
||||
resume_12:
|
||||
// Ignore ROLLBACK errors - submit_primary_subops will drop the connection if it fails
|
||||
delete[] op_data->unstable_writes;
|
||||
@@ -257,11 +263,21 @@ resume_12:
|
||||
else
|
||||
{
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
mark_partial_write(pg, op_data->oid, op_data->object_state, op_data->stripes, true);
|
||||
mark_partial_write(pg, cur_op);
|
||||
if (op_data->subops)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
}
|
||||
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (op_data->subops)
|
||||
{
|
||||
delete[] op_data->subops;
|
||||
op_data->subops = NULL;
|
||||
}
|
||||
pg.ver_override.erase(op_data->oid);
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
pg_cancel_write_queue(pg, cur_op, op_data->oid, op_data->errcode);
|
||||
@@ -323,7 +339,6 @@ resume_12:
|
||||
}
|
||||
resume_6:
|
||||
resume_7:
|
||||
op_data->n_subops = 0;
|
||||
if (!remember_unstable_write(cur_op, pg, pg.cur_loc_set, 6))
|
||||
{
|
||||
return;
|
||||
@@ -359,16 +374,21 @@ resume_7:
|
||||
);
|
||||
recovery_stat[recovery_type].usec += usec;
|
||||
}
|
||||
if (immediate_commit == IMMEDIATE_ALL)
|
||||
if (immediate_commit != IMMEDIATE_ALL)
|
||||
{
|
||||
// Do not delete anything, it will be deleted as part of copies_to_delete_after_sync
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
submit_primary_del_subops(cur_op, pg.cur_set.data(), pg.pg_size, op_data->object_state->osd_set);
|
||||
}
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
deref_object_state(pg, &op_data->object_state, true);
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
resume_8:
|
||||
op_data->st = 8;
|
||||
return;
|
||||
op_data->st = 8;
|
||||
return;
|
||||
}
|
||||
resume_9:
|
||||
if (op_data->errors > 0)
|
||||
{
|
||||
@@ -487,8 +507,11 @@ immediate:
|
||||
}
|
||||
submit_primary_stab_subops(cur_op);
|
||||
resume_6:
|
||||
op_data->st = 6;
|
||||
return false;
|
||||
if (op_data->n_subops > 0)
|
||||
{
|
||||
op_data->st = 6;
|
||||
return false;
|
||||
}
|
||||
resume_7:
|
||||
// FIXME: Free those in the destructor?
|
||||
delete op_data->unstable_write_osds;
|
||||
|
||||
+3
-2
@@ -417,6 +417,7 @@ void reconstruct_stripes_ec(osd_rmw_stripe_t *stripes, int pg_size, int pg_minsi
|
||||
data_ptrs[orig++] = (uint8_t*)stripes[other].read_buf + (read_start - stripes[other].read_start);
|
||||
}
|
||||
}
|
||||
assert(orig == pg_minsize);
|
||||
ec_encode_data(
|
||||
read_end-read_start, pg_minsize, wanted, dectable + wanted_base*item_size*pg_minsize,
|
||||
data_ptrs, data_ptrs + pg_minsize
|
||||
@@ -1281,7 +1282,7 @@ std::vector<int> ec_find_good(osd_rmw_stripe_t *stripes, int stripe_count, int p
|
||||
std::vector<std::vector<int>> live_variants(pg_size);
|
||||
int eq_to[stripe_count];
|
||||
int live_roles = 0, live_total = 0;
|
||||
for (int i = 0; i < pg_size; i++)
|
||||
for (int i = 0; i < stripe_count; i++)
|
||||
{
|
||||
eq_to[i] = i;
|
||||
}
|
||||
@@ -1381,7 +1382,7 @@ std::vector<int> ec_find_good(osd_rmw_stripe_t *stripes, int stripe_count, int p
|
||||
for (i = 0; i < pg_minsize; i++)
|
||||
{
|
||||
subvar[i]++;
|
||||
if (subvar[i] < live_variants[combination[i]].size())
|
||||
if (subvar[i] < live_variants[comb_to_subset[combination[i]]].size())
|
||||
break;
|
||||
subvar[i] = 0;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ void test_recover_22_d2();
|
||||
void test_ec43_error_bruteforce();
|
||||
void test_recover_53_d5();
|
||||
void test_recover_22();
|
||||
void test_ec_find_good_multi_chunks();
|
||||
|
||||
int main(int narg, char *args[])
|
||||
{
|
||||
@@ -69,6 +70,7 @@ int main(int narg, char *args[])
|
||||
test_recover_22_d2();
|
||||
// Error bruteforce
|
||||
test_ec43_error_bruteforce();
|
||||
test_ec_find_good_multi_chunks();
|
||||
// Test 19
|
||||
test_recover_53_d5();
|
||||
// Test 20
|
||||
@@ -1345,3 +1347,54 @@ void test_recover_22()
|
||||
free(write_buf);
|
||||
use_ec(4, 2, false);
|
||||
}
|
||||
|
||||
void test_ec_find_good_multi_chunks()
|
||||
{
|
||||
use_ec(7, 4, true);
|
||||
osd_num_t osd_set[7] = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
osd_rmw_stripe_t stripes[15] = {};
|
||||
split_stripes(4, 4096, 0, 4096 * 4, stripes);
|
||||
uint8_t *write_buf = (uint8_t*)malloc_or_die(4096 * 15);
|
||||
set_pattern(write_buf+0*4096, 4096, PATTERN0);
|
||||
set_pattern(write_buf+1*4096, 4096, PATTERN1);
|
||||
set_pattern(write_buf+2*4096, 4096, PATTERN2);
|
||||
set_pattern(write_buf+3*4096, 4096, PATTERN3);
|
||||
uint8_t *rmw_buf = (uint8_t*)calc_rmw(write_buf, stripes, osd_set, 7, 4, 7, osd_set, 4096, 0);
|
||||
calc_rmw_parity_ec(stripes, 7, 4, osd_set, osd_set, 4096, 0);
|
||||
check_pattern(stripes[4].write_buf, 4096, PATTERN0^PATTERN1^PATTERN2^PATTERN3);
|
||||
check_pattern(stripes[5].write_buf, 4096, 0xfcee568ba36371ac); // 2nd EC chunk
|
||||
check_pattern(stripes[6].write_buf, 4096, 0x139274739ae6f387); // 3rd EC chunk
|
||||
memcpy(write_buf+4*4096, stripes[4].write_buf, 4096);
|
||||
memcpy(write_buf+5*4096, stripes[5].write_buf, 4096);
|
||||
memcpy(write_buf+6*4096, stripes[6].write_buf, 4096);
|
||||
// Make 15 chunks
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
stripes[i].read_start = 0;
|
||||
stripes[i].read_end = 4096;
|
||||
stripes[i].read_buf = write_buf+i*4096;
|
||||
stripes[i].write_buf = NULL;
|
||||
stripes[i].role = i;
|
||||
stripes[i].osd_num = i+1;
|
||||
}
|
||||
for (int i = 7; i < 14; i++)
|
||||
{
|
||||
stripes[i].role = i % 7;
|
||||
memcpy(write_buf+i*4096, write_buf + (i%7)*4096, 4096);
|
||||
}
|
||||
stripes[14].role = 6;
|
||||
memcpy(write_buf+14*4096, write_buf + 6*4096, 4096);
|
||||
// Corrupt chunks 0-6 and chunk 13, make role 0 absent
|
||||
stripes[0].read_error = true;
|
||||
stripes[7].read_error = true;
|
||||
for (int i = 0; i < 7; i++)
|
||||
memset(write_buf+i*4096, i*10+1, 4096);
|
||||
memset(write_buf+13*4096, 8*10+1, 4096);
|
||||
// Find errors
|
||||
auto res = ec_find_good(stripes, 15, 7, 4, false, 4096, 0, 100, true);
|
||||
assert_eq_vec(res, std::vector<int>({8, 9, 10, 11, 12, 14}));
|
||||
// Done
|
||||
free(rmw_buf);
|
||||
free(write_buf);
|
||||
use_ec(7, 4, false);
|
||||
}
|
||||
|
||||
@@ -289,6 +289,8 @@ bool osd_t::continue_scrub()
|
||||
if (no_scrub)
|
||||
{
|
||||
// Return false = no more scrub work to do
|
||||
if (scrub_cur_list.buf)
|
||||
free(scrub_cur_list.buf);
|
||||
scrub_cur_list = {};
|
||||
scrub_last_pg = {};
|
||||
scrub_nearest_ts = 0;
|
||||
@@ -503,7 +505,7 @@ void osd_t::scrub_check_results(osd_op_t *cur_op)
|
||||
{
|
||||
unknown = true;
|
||||
}
|
||||
if (votes[role] > 0 && votes[role] < votes[best])
|
||||
if (eq_to[role] != -1 && votes[eq_to[role]] > 0 && votes[eq_to[role]] < votes[best])
|
||||
{
|
||||
printf(
|
||||
"[PG %u/%u] Object %jx:%jx v%ju copy on OSD %ju doesn't match %d other copies%s\n",
|
||||
|
||||
@@ -192,16 +192,13 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
#ifdef OSD_STUB
|
||||
cur_op->bs_op->retval = 0;
|
||||
#endif
|
||||
if (enable_pg_locks)
|
||||
for (int i = 0; i < cur_op->bs_op->len; i++)
|
||||
{
|
||||
for (int i = 0; i < cur_op->bs_op->len; i++)
|
||||
if (!sec_check_pg_lock(cl->in_osd_num, ((obj_ver_id*)cur_op->buf)[i].oid, cur_op->req.sec_stab.flags))
|
||||
{
|
||||
if (!sec_check_pg_lock(cl->in_osd_num, ((obj_ver_id*)cur_op->buf)[i].oid, cur_op->req.sec_stab.flags))
|
||||
{
|
||||
cur_op->bs_op->retval = -EPIPE;
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
}
|
||||
cur_op->bs_op->retval = -EPIPE;
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,8 +212,8 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
secondary_op_callback(cur_op);
|
||||
return;
|
||||
}
|
||||
auto pool_id = INODE_POOL(cur_op->bs_op->min_oid.inode);
|
||||
if (pool_id && !sec_check_pg_lock(0, (object_id){ .inode = cur_op->bs_op->min_oid.inode }, OSD_OP_IGNORE_PG_LOCK))
|
||||
auto pool_id = INODE_POOL(cur_op->req.sec_list.min_inode);
|
||||
if (!pool_id || !sec_check_pg_lock(0, (object_id){ .inode = cur_op->req.sec_list.min_inode }, OSD_OP_IGNORE_PG_LOCK))
|
||||
{
|
||||
// Check resharding state of the pool
|
||||
cur_op->bs_op->retval = -EPIPE;
|
||||
@@ -229,7 +226,7 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
|
||||
cur_op->bs_op->min_oid.inode = cur_op->req.sec_list.min_inode;
|
||||
cur_op->bs_op->min_oid.stripe = cur_op->req.sec_list.min_stripe;
|
||||
cur_op->bs_op->max_oid.inode = cur_op->req.sec_list.max_inode;
|
||||
if (cur_op->req.sec_list.max_inode && cur_op->req.sec_list.max_stripe != UINT64_MAX)
|
||||
if (cur_op->req.sec_list.max_inode)
|
||||
{
|
||||
cur_op->bs_op->max_oid.stripe = cur_op->req.sec_list.max_stripe
|
||||
? cur_op->req.sec_list.max_stripe : UINT64_MAX;
|
||||
|
||||
@@ -4,13 +4,9 @@ project(vitastor)
|
||||
|
||||
### Test stubs
|
||||
|
||||
# stub_osd, stub_bench, osd_test
|
||||
# stub_osd, stub_bench
|
||||
add_executable(stub_osd stub_osd.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(stub_osd tcmalloc_minimal)
|
||||
add_executable(stub_bench stub_bench.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(stub_bench tcmalloc_minimal)
|
||||
add_executable(osd_test osd_test.cpp ../util/rw_blocking.cpp ../util/addr_util.cpp)
|
||||
target_link_libraries(osd_test tcmalloc_minimal)
|
||||
|
||||
# bindiff
|
||||
add_executable(bindiff
|
||||
@@ -19,6 +15,7 @@ add_executable(bindiff
|
||||
|
||||
# stub_uring_osd
|
||||
add_executable(stub_uring_osd
|
||||
EXCLUDE_FROM_ALL
|
||||
stub_uring_osd.cpp
|
||||
)
|
||||
target_link_libraries(stub_uring_osd
|
||||
@@ -26,7 +23,6 @@ target_link_libraries(stub_uring_osd
|
||||
${LIBURING_LIBRARIES}
|
||||
${IBVERBS_LIBRARIES}
|
||||
${RDMACM_LIBRARIES}
|
||||
tcmalloc_minimal
|
||||
)
|
||||
|
||||
# test_allocator
|
||||
|
||||
@@ -57,3 +57,7 @@ json11::Json::object osd_messenger_t::merge_configs(const json11::Json::object &
|
||||
{
|
||||
return cli_config;
|
||||
}
|
||||
|
||||
void osd_messenger_t::destroy_rdma_conn(msgr_rdma_connection_t *rdma_conn)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
struct ring_consumer_t
|
||||
{
|
||||
std::function<void(void)> loop;
|
||||
};
|
||||
|
||||
class ring_loop_t
|
||||
{
|
||||
public:
|
||||
void register_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
}
|
||||
void unregister_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
}
|
||||
void submit()
|
||||
{
|
||||
}
|
||||
void wait()
|
||||
{
|
||||
}
|
||||
void loop()
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -1,389 +0,0 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "malloc_or_die.h"
|
||||
#include "addr_util.h"
|
||||
#include "osd_ops.h"
|
||||
#include "rw_blocking.h"
|
||||
#include "test_pattern.h"
|
||||
|
||||
int connect_osd(const char *osd_address, int osd_port);
|
||||
|
||||
uint64_t test_read(int connect_fd, uint64_t inode, uint64_t stripe, uint64_t version, uint64_t offset, uint64_t len);
|
||||
|
||||
uint64_t test_write(int connect_fd, uint64_t inode, uint64_t stripe, uint64_t version, uint64_t pattern);
|
||||
|
||||
void* test_primary_read(int connect_fd, uint64_t inode, uint64_t offset, uint64_t len);
|
||||
|
||||
void test_primary_write(int connect_fd, uint64_t inode, uint64_t offset, uint64_t len, uint64_t pattern);
|
||||
|
||||
void test_primary_sync(int connect_fd);
|
||||
|
||||
void test_sync_stab_all(int connect_fd);
|
||||
|
||||
void test_list_stab(int connect_fd);
|
||||
|
||||
int main0(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
// Prepare data for cluster read
|
||||
connect_fd = connect_osd("127.0.0.1", 11203);
|
||||
test_write(connect_fd, 2, 0, 1, PATTERN0);
|
||||
close(connect_fd);
|
||||
connect_fd = connect_osd("127.0.0.1", 11204);
|
||||
test_write(connect_fd, 2, 1, 1, PATTERN1);
|
||||
close(connect_fd);
|
||||
connect_fd = connect_osd("127.0.0.1", 11205);
|
||||
test_write(connect_fd, 2, 2, 1, PATTERN0^PATTERN1);
|
||||
close(connect_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main1(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
void *data;
|
||||
// Cluster read
|
||||
connect_fd = connect_osd("127.0.0.1", 11203);
|
||||
data = test_primary_read(connect_fd, 2, 0, 128*1024);
|
||||
if (data)
|
||||
{
|
||||
check_pattern(data, 128*1024, PATTERN0);
|
||||
printf("inode=2 0-128K OK\n");
|
||||
free(data);
|
||||
}
|
||||
data = test_primary_read(connect_fd, 2, 0, 256*1024);
|
||||
if (data)
|
||||
{
|
||||
check_pattern(data, 128*1024, PATTERN0);
|
||||
check_pattern(data+128*1024, 128*1024, PATTERN1);
|
||||
printf("inode=2 0-256K OK\n");
|
||||
free(data);
|
||||
}
|
||||
close(connect_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main2(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
// Cluster write (sync not implemented yet)
|
||||
connect_fd = connect_osd("127.0.0.1", 11203);
|
||||
test_primary_write(connect_fd, 2, 0, 128*1024, PATTERN0);
|
||||
test_primary_write(connect_fd, 2, 128*1024, 128*1024, PATTERN1);
|
||||
test_sync_stab_all(connect_fd);
|
||||
close(connect_fd);
|
||||
connect_fd = connect_osd("127.0.0.1", 11204);
|
||||
if (connect_fd >= 0)
|
||||
{
|
||||
test_sync_stab_all(connect_fd);
|
||||
close(connect_fd);
|
||||
}
|
||||
connect_fd = connect_osd("127.0.0.1", 11205);
|
||||
if (connect_fd >= 0)
|
||||
{
|
||||
test_sync_stab_all(connect_fd);
|
||||
close(connect_fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main3(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
connect_fd = connect_osd("127.0.0.1", 11203);
|
||||
test_list_stab(connect_fd);
|
||||
close(connect_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main4(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
// Cluster write (sync not implemented yet)
|
||||
connect_fd = connect_osd("127.0.0.1", 11203);
|
||||
test_primary_write(connect_fd, 2, 0, 128*1024, PATTERN0);
|
||||
test_primary_write(connect_fd, 2, 128*1024, 128*1024, PATTERN1);
|
||||
test_primary_sync(connect_fd);
|
||||
close(connect_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int narg, char *args[])
|
||||
{
|
||||
int connect_fd;
|
||||
connect_fd = connect_osd("192.168.7.2", 43051);
|
||||
test_read(connect_fd, 1, 1039663104, UINT64_MAX, 0, 128*1024);
|
||||
close(connect_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connect_osd(const char *osd_address, int osd_port)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
if (!string_to_addr(osd_address, 0, osd_port, &addr))
|
||||
{
|
||||
fprintf(stderr, "server address: %s is not valid\n", osd_address);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int connect_fd = socket(addr.ss_family, SOCK_STREAM, 0);
|
||||
if (connect_fd < 0)
|
||||
{
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
if (connect(connect_fd, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
perror("connect");
|
||||
close(connect_fd);
|
||||
return -1;
|
||||
}
|
||||
int one = 1;
|
||||
setsockopt(connect_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||
|
||||
return connect_fd;
|
||||
}
|
||||
|
||||
bool check_reply(int r, osd_any_op_t & op, osd_any_reply_t & reply, int expected)
|
||||
{
|
||||
if (r != OSD_PACKET_SIZE)
|
||||
{
|
||||
printf("read failed\n");
|
||||
return false;
|
||||
}
|
||||
if (reply.hdr.magic != SECONDARY_OSD_REPLY_MAGIC ||
|
||||
reply.hdr.id != op.hdr.id || reply.hdr.opcode != op.hdr.opcode)
|
||||
{
|
||||
printf("bad reply: magic, id or opcode does not match request\n");
|
||||
return false;
|
||||
}
|
||||
if (expected >= 0 && reply.hdr.retval != expected)
|
||||
{
|
||||
printf("operation failed, retval=%jd\n", reply.hdr.retval);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t test_read(int connect_fd, uint64_t inode, uint64_t stripe, uint64_t version, uint64_t offset, uint64_t len)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_SEC_READ;
|
||||
op.sec_rw.oid = {
|
||||
.inode = inode,
|
||||
.stripe = stripe,
|
||||
};
|
||||
op.sec_rw.version = version;
|
||||
op.sec_rw.offset = offset;
|
||||
op.sec_rw.len = len;
|
||||
void *data = memalign_or_die(MEM_ALIGNMENT, op.sec_rw.len);
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
if (!check_reply(r, op, reply, op.sec_rw.len))
|
||||
{
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
r = read_blocking(connect_fd, data, len);
|
||||
if (r != len)
|
||||
{
|
||||
free(data);
|
||||
perror("read data");
|
||||
return 0;
|
||||
}
|
||||
free(data);
|
||||
printf("Read %jx:%jx v%ju = v%ju\n", inode, stripe, version, reply.sec_rw.version);
|
||||
op.hdr.opcode = OSD_OP_SEC_LIST;
|
||||
op.sec_list.list_pg = 1;
|
||||
op.sec_list.pg_count = 1;
|
||||
op.sec_list.pg_stripe_size = 4*1024*1024;
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
if (reply.hdr.retval < 0 || !check_reply(r, op, reply, reply.hdr.retval))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
data = memalign_or_die(MEM_ALIGNMENT, sizeof(obj_ver_id)*reply.hdr.retval);
|
||||
r = read_blocking(connect_fd, data, sizeof(obj_ver_id)*reply.hdr.retval);
|
||||
if (r != sizeof(obj_ver_id)*reply.hdr.retval)
|
||||
{
|
||||
free(data);
|
||||
perror("read data");
|
||||
return 0;
|
||||
}
|
||||
obj_ver_id *ov = (obj_ver_id*)data;
|
||||
for (int i = 0; i < reply.hdr.retval; i++)
|
||||
{
|
||||
if (ov[i].oid.inode == inode && (ov[i].oid.stripe & ~(4096-1)) == (stripe & ~(4096-1)))
|
||||
{
|
||||
printf("list: %jx:%jx v%ju stable=%d\n", ov[i].oid.inode, ov[i].oid.stripe, ov[i].version, i < reply.sec_list.stable_count ? 1 : 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t test_write(int connect_fd, uint64_t inode, uint64_t stripe, uint64_t version, uint64_t pattern)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_SEC_WRITE;
|
||||
op.sec_rw.oid = {
|
||||
.inode = inode,
|
||||
.stripe = stripe,
|
||||
};
|
||||
op.sec_rw.version = version;
|
||||
op.sec_rw.offset = 0;
|
||||
op.sec_rw.len = 128*1024;
|
||||
void *data = memalign_or_die(MEM_ALIGNMENT, op.sec_rw.len);
|
||||
for (int i = 0; i < (op.sec_rw.len)/sizeof(uint64_t); i++)
|
||||
((uint64_t*)data)[i] = pattern;
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
write_blocking(connect_fd, data, op.sec_rw.len);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
if (!check_reply(r, op, reply, op.sec_rw.len))
|
||||
{
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
version = reply.sec_rw.version;
|
||||
op.hdr.opcode = OSD_OP_TEST_SYNC_STAB_ALL;
|
||||
op.hdr.id = 2;
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
if (!check_reply(r, op, reply, 0))
|
||||
{
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
free(data);
|
||||
return version;
|
||||
}
|
||||
|
||||
void* test_primary_read(int connect_fd, uint64_t inode, uint64_t offset, uint64_t len)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_READ;
|
||||
op.rw.inode = inode;
|
||||
op.rw.offset = offset;
|
||||
op.rw.len = len;
|
||||
void *data = memalign_or_die(MEM_ALIGNMENT, len);
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
if (!check_reply(r, op, reply, len))
|
||||
{
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
r = read_blocking(connect_fd, data, len);
|
||||
if (r != len)
|
||||
{
|
||||
free(data);
|
||||
perror("read data");
|
||||
return NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void test_primary_write(int connect_fd, uint64_t inode, uint64_t offset, uint64_t len, uint64_t pattern)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_WRITE;
|
||||
op.rw.inode = inode;
|
||||
op.rw.offset = offset;
|
||||
op.rw.len = len;
|
||||
void *data = memalign_or_die(MEM_ALIGNMENT, len);
|
||||
set_pattern(data, len, pattern);
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
write_blocking(connect_fd, data, len);
|
||||
free(data);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
assert(check_reply(r, op, reply, len));
|
||||
}
|
||||
|
||||
void test_primary_sync(int connect_fd)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_SYNC;
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
assert(check_reply(r, op, reply, 0));
|
||||
}
|
||||
|
||||
void test_sync_stab_all(int connect_fd)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_TEST_SYNC_STAB_ALL;
|
||||
write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
assert(check_reply(r, op, reply, 0));
|
||||
}
|
||||
|
||||
void test_list_stab(int connect_fd)
|
||||
{
|
||||
osd_any_op_t op;
|
||||
osd_any_reply_t reply;
|
||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||
op.hdr.id = 1;
|
||||
op.hdr.opcode = OSD_OP_SEC_LIST;
|
||||
op.sec_list.pg_count = 0;
|
||||
assert(write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE) == OSD_PACKET_SIZE);
|
||||
int r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
assert(check_reply(r, op, reply, -1));
|
||||
int total_count = reply.hdr.retval;
|
||||
int stable_count = reply.sec_list.stable_count;
|
||||
obj_ver_id *data = (obj_ver_id*)malloc_or_die(total_count * sizeof(obj_ver_id));
|
||||
assert(data);
|
||||
assert(read_blocking(connect_fd, data, total_count * sizeof(obj_ver_id)) == (total_count * sizeof(obj_ver_id)));
|
||||
int last_start = stable_count;
|
||||
for (int i = stable_count; i <= total_count; i++)
|
||||
{
|
||||
// Stabilize in portions of 32 entries
|
||||
if (i - last_start >= 32 || i == total_count)
|
||||
{
|
||||
op.hdr.opcode = OSD_OP_SEC_STABILIZE;
|
||||
op.sec_stab.len = sizeof(obj_ver_id) * (i - last_start);
|
||||
assert(write_blocking(connect_fd, op.buf, OSD_PACKET_SIZE) == OSD_PACKET_SIZE);
|
||||
assert(write_blocking(connect_fd, data + last_start, op.sec_stab.len) == op.sec_stab.len);
|
||||
r = read_blocking(connect_fd, reply.buf, OSD_PACKET_SIZE);
|
||||
assert(check_reply(r, op, reply, 0));
|
||||
last_start = i;
|
||||
}
|
||||
}
|
||||
obj_ver_id *data2 = (obj_ver_id*)malloc_or_die(sizeof(obj_ver_id) * 32);
|
||||
assert(data2);
|
||||
free(data2);
|
||||
free(data);
|
||||
}
|
||||
@@ -166,8 +166,9 @@ void ring_loop_mock_t::mark_completed(ring_data_t *data)
|
||||
wakeup();
|
||||
}
|
||||
|
||||
disk_mock_t::disk_mock_t(size_t size, bool buffered)
|
||||
disk_mock_t::disk_mock_t(const std::string & name, size_t size, bool buffered)
|
||||
{
|
||||
this->name = name;
|
||||
this->size = size;
|
||||
this->data = (uint8_t*)malloc_or_die(size);
|
||||
this->buffered = buffered;
|
||||
@@ -193,6 +194,7 @@ void disk_mock_t::erase_buffers(uint64_t begin, uint64_t end)
|
||||
if (bs >= begin && be <= end)
|
||||
{
|
||||
// Remove the whole buffer
|
||||
free(it->second.iov_base);
|
||||
buffers.erase(it++);
|
||||
}
|
||||
else if (bs < begin && be > end)
|
||||
@@ -222,6 +224,7 @@ void disk_mock_t::erase_buffers(uint64_t begin, uint64_t end)
|
||||
assert(be > end);
|
||||
uint8_t *ce = (uint8_t*)malloc_or_die(be-end);
|
||||
memcpy(ce, (uint8_t*)it->second.iov_base + (end-bs), be-end);
|
||||
free(it->second.iov_base);
|
||||
buffers[be] = (iovec){ .iov_base = ce, .iov_len = be-end };
|
||||
buffers.erase(it);
|
||||
break;
|
||||
@@ -242,7 +245,7 @@ void disk_mock_t::discard_buffers(bool all, uint32_t seed)
|
||||
if (all)
|
||||
{
|
||||
if (trace)
|
||||
printf("disk: discard all buffers (%zu)\n", buffers.size());
|
||||
printf("%s: discard all buffers (%zu)\n", name.c_str(), buffers.size());
|
||||
for (auto & b: buffers)
|
||||
free(b.second.iov_base);
|
||||
buffers.clear();
|
||||
@@ -250,7 +253,7 @@ void disk_mock_t::discard_buffers(bool all, uint32_t seed)
|
||||
else
|
||||
{
|
||||
if (trace)
|
||||
printf("disk: discard random buffers seed=%u\n", seed);
|
||||
printf("%s: discard random buffers seed=%u\n", name.c_str(), seed);
|
||||
std::mt19937 rnd(seed);
|
||||
for (auto it = buffers.begin(); it != buffers.end(); )
|
||||
{
|
||||
@@ -279,7 +282,7 @@ ssize_t disk_mock_t::copy_from_sqe(io_uring_sqe *sqe, uint8_t *to, uint64_t base
|
||||
}
|
||||
size_t cur = (off + v[i].iov_len > size ? size-off : v[i].iov_len);
|
||||
if (trace)
|
||||
printf("disk: write %zu+%zu from %jx\n", off, cur, (uint64_t)v[i].iov_base);
|
||||
printf("%s: write %zu+%zu from %jx\n", name.c_str(), off, cur, (uint64_t)v[i].iov_base);
|
||||
memcpy(to + off - base_offset, v[i].iov_base, cur);
|
||||
off += v[i].iov_len;
|
||||
}
|
||||
@@ -332,7 +335,7 @@ bool disk_mock_t::submit(io_uring_sqe *sqe)
|
||||
{
|
||||
size_t cur = (off + v[i].iov_len > size ? size-off : v[i].iov_len);
|
||||
if (trace)
|
||||
printf("disk: read %zu+%zu to %jx\n", off, cur, (uint64_t)v[i].iov_base);
|
||||
printf("%s: read %zu+%zu to %jx\n", name.c_str(), off, cur, (uint64_t)v[i].iov_base);
|
||||
if (buffers.size())
|
||||
read_item((uint8_t*)v[i].iov_base, off, cur);
|
||||
else
|
||||
@@ -374,7 +377,7 @@ bool disk_mock_t::submit(io_uring_sqe *sqe)
|
||||
else if (sqe->opcode == IORING_OP_FSYNC)
|
||||
{
|
||||
if (trace)
|
||||
printf("disk: fsync\n");
|
||||
printf("%s: fsync\n", name.c_str());
|
||||
if (buffers.size())
|
||||
{
|
||||
for (auto & b: buffers)
|
||||
|
||||
@@ -45,6 +45,7 @@ class disk_mock_t
|
||||
{
|
||||
uint8_t *data = NULL;
|
||||
std::map<uint64_t, iovec> buffers;
|
||||
std::string name;
|
||||
size_t size = 0;
|
||||
bool buffered = false;
|
||||
|
||||
@@ -53,7 +54,7 @@ class disk_mock_t
|
||||
void read_item(uint8_t *to, uint64_t offset, uint64_t len);
|
||||
public:
|
||||
bool trace = false;
|
||||
disk_mock_t(size_t size, bool buffered);
|
||||
disk_mock_t(const std::string & name, size_t size, bool buffered);
|
||||
~disk_mock_t();
|
||||
void clear(size_t offset, size_t len);
|
||||
void discard_buffers(bool all, uint32_t seed);
|
||||
|
||||
@@ -106,13 +106,13 @@ struct bs_test_t
|
||||
}
|
||||
if (!data_disk)
|
||||
{
|
||||
data_disk = new disk_mock_t(parse_size(config["data_device_size"]), config["disable_data_fsync"] != "1");
|
||||
data_disk = new disk_mock_t("data disk", parse_size(config["data_device_size"]), config["disable_data_fsync"] != "1");
|
||||
data_disk->clear(0, parse_size(config["data_offset"]));
|
||||
}
|
||||
uint64_t meta_size = parse_size(config["meta_device_size"]);
|
||||
if (meta_size && !meta_disk)
|
||||
{
|
||||
meta_disk = new disk_mock_t(meta_size, config["disable_meta_fsync"] != "1");
|
||||
meta_disk = new disk_mock_t("meta disk", meta_size, config["disable_meta_fsync"] != "1");
|
||||
meta_disk->clear(0, meta_size);
|
||||
}
|
||||
if (!bs)
|
||||
@@ -251,9 +251,9 @@ static void test_fsync(bool separate_meta)
|
||||
test.meta_disk->trace = 1;
|
||||
|
||||
// Write
|
||||
printf("writing\n");
|
||||
printf("writing 16K+4K v1\n");
|
||||
blockstore_op_t op;
|
||||
op.opcode = BS_OP_WRITE;
|
||||
op.opcode = BS_OP_WRITE_STABLE;
|
||||
op.oid = { .inode = 1, .stripe = 0 };
|
||||
op.version = 1;
|
||||
op.offset = 16384;
|
||||
@@ -313,6 +313,54 @@ static void test_fsync(bool separate_meta)
|
||||
assert(memcmp(op2.buf+16*1024, op.buf, 4*1024) == 0);
|
||||
assert(is_zero(op2.buf+20*1024, 108*1024));
|
||||
|
||||
// Check fsync during compaction - do a small write
|
||||
printf("writing 20K+4K v2\n");
|
||||
op.opcode = BS_OP_WRITE_STABLE;
|
||||
op.oid = { .inode = 1, .stripe = 0 };
|
||||
op.version = 2;
|
||||
op.offset = 20*1024;
|
||||
op.len = 4096;
|
||||
memset(op.buf, 0xab, 4096);
|
||||
test.exec_op(&op);
|
||||
assert(op.retval == op.len);
|
||||
|
||||
op.opcode = BS_OP_SYNC;
|
||||
test.exec_op(&op);
|
||||
assert(op.retval == 0);
|
||||
|
||||
// Check it by a read op
|
||||
op2.version = UINT64_MAX;
|
||||
test.exec_op(&op2);
|
||||
assert(op2.retval == op2.len);
|
||||
assert(is_zero(op2.buf, 16*1024));
|
||||
assert(memcheck(op2.buf+16*1024, 0xaa, 4*1024));
|
||||
assert(memcheck(op2.buf+20*1024, 0xab, 4*1024));
|
||||
assert(is_zero(op2.buf+24*1024, 104*1024));
|
||||
|
||||
// Trigger & wait compaction
|
||||
test.bs->flusher->dump_diagnostics();
|
||||
test.bs->flusher->request_trim();
|
||||
while (test.bs->heap->get_compact_queue_size())
|
||||
test.ringloop->loop();
|
||||
while (test.bs->flusher->is_active())
|
||||
test.ringloop->loop();
|
||||
test.bs->flusher->release_trim();
|
||||
// Check that compaction succeeded
|
||||
assert(!test.bs->heap->get_to_compact_count());
|
||||
|
||||
// Restart and check data again
|
||||
test.destroy_bs();
|
||||
test.data_disk->discard_buffers(true, 0);
|
||||
test.init();
|
||||
|
||||
op2.version = UINT64_MAX;
|
||||
test.exec_op(&op2);
|
||||
assert(op2.retval == op2.len);
|
||||
assert(is_zero(op2.buf, 16*1024));
|
||||
assert(memcheck(op2.buf+16*1024, 0xaa, 4*1024));
|
||||
assert(memcheck(op2.buf+20*1024, 0xab, 4*1024)); // <- would be lost without data device fsync
|
||||
assert(is_zero(op2.buf+24*1024, 104*1024));
|
||||
|
||||
free(op.buf);
|
||||
free(op2.buf);
|
||||
}
|
||||
@@ -434,6 +482,84 @@ static void test_padded_csum_intent(bool perfect)
|
||||
free(op2.buf);
|
||||
}
|
||||
|
||||
static void test_perfect_csum_interrupted()
|
||||
{
|
||||
printf("\n-- test_perfect_csum_interrupted\n");
|
||||
|
||||
bs_test_t test;
|
||||
test.default_cfg();
|
||||
test.config["csum_block_size"] = "16384";
|
||||
test.config["perfect_csum_update"] = "1";
|
||||
test.config["disable_meta_fsync"] = "1";
|
||||
test.config["meta_device"] = "./test_meta.bin";
|
||||
test.config["meta_device_size"] = "33554432";
|
||||
test.config["meta_device_sect"] = "4096";
|
||||
test.config["data_offset"] = "0";
|
||||
test.init();
|
||||
|
||||
// Write
|
||||
printf("writing\n");
|
||||
blockstore_op_t op;
|
||||
op.opcode = BS_OP_WRITE_STABLE;
|
||||
op.oid = { .inode = 1, .stripe = 0 };
|
||||
op.version = 1;
|
||||
op.offset = 16*1024;
|
||||
op.len = 12*1024;
|
||||
op.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, 12*1024);
|
||||
memset(op.buf, 0xaa, 12*1024);
|
||||
test.exec_op(&op);
|
||||
assert(op.retval == op.len);
|
||||
|
||||
// Write again
|
||||
printf("writing (small)\n");
|
||||
op.version = 2;
|
||||
op.offset = 20*1024;
|
||||
op.len = 4*1024;
|
||||
memset(op.buf, 0xbb, 4096);
|
||||
test.exec_op(&op);
|
||||
assert(op.retval == op.len);
|
||||
|
||||
// Trigger & block compaction after punch_holes
|
||||
bool modified = false;
|
||||
test.sqe_handler = [&](io_uring_sqe *sqe)
|
||||
{
|
||||
if (sqe->fd == MOCK_META_FD && sqe->opcode == IORING_OP_WRITEV)
|
||||
{
|
||||
bool ok = test.meta_disk->submit(sqe);
|
||||
assert(ok);
|
||||
free(((ring_data_t*)sqe->user_data)->iov.iov_base);
|
||||
modified = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
test.bs->flusher->request_trim();
|
||||
while (!modified)
|
||||
test.ringloop->loop();
|
||||
test.destroy_bs();
|
||||
test.init();
|
||||
|
||||
// Read and check
|
||||
printf("rechecking reloaded\n");
|
||||
blockstore_op_t op2;
|
||||
op2.opcode = BS_OP_READ;
|
||||
op2.oid = { .inode = 1, .stripe = 0 };
|
||||
op2.version = UINT64_MAX;
|
||||
op2.offset = 0;
|
||||
op2.len = 128*1024;
|
||||
op2.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, 128*1024);
|
||||
test.exec_op(&op2);
|
||||
assert(op2.retval == op2.len);
|
||||
assert(memcheck(op2.buf, 0, 16*1024));
|
||||
assert(memcheck(op2.buf+16*1024, 0xaa, 4*1024));
|
||||
assert(memcheck(op2.buf+20*1024, 0xbb, 4*1024));
|
||||
assert(memcheck(op2.buf+24*1024, 0xaa, 4*1024));
|
||||
assert(memcheck(op2.buf+28*1024, 0, 100*1024));
|
||||
|
||||
free(op.buf);
|
||||
free(op2.buf);
|
||||
}
|
||||
|
||||
static void test_padded_csum_parallel_read(bool perfect, uint32_t offset)
|
||||
{
|
||||
printf("\n-- test_padded_csum_parallel_read%s offset=%u\n", perfect ? " perfect_csum_update" : "", offset);
|
||||
@@ -526,6 +652,7 @@ int main(int narg, char *args[])
|
||||
test_intent_over_unstable();
|
||||
test_padded_csum_intent(false);
|
||||
test_padded_csum_intent(true);
|
||||
test_perfect_csum_interrupted();
|
||||
test_padded_csum_parallel_read(false, 8192);
|
||||
test_padded_csum_parallel_read(true, 8192);
|
||||
test_padded_csum_parallel_read(false, 16384);
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
#include <assert.h>
|
||||
#include "cluster_client_impl.h"
|
||||
|
||||
class cluster_client_test_t
|
||||
{
|
||||
public:
|
||||
static void continue_ops(cluster_client_t *cli)
|
||||
{
|
||||
cli->continue_ops(cli->client_retry_interval);
|
||||
}
|
||||
};
|
||||
|
||||
void configure_single_pg_pool(cluster_client_t *cli)
|
||||
{
|
||||
cli->st_cli.parse_state((etcd_kv_t){
|
||||
@@ -260,7 +269,7 @@ void test1()
|
||||
|
||||
pretend_disconnected(cli, 1);
|
||||
pretend_connected(cli, 1);
|
||||
cli->continue_ops(cli->client_retry_interval);
|
||||
cluster_client_test_t::continue_ops(cli);
|
||||
|
||||
// Check replay
|
||||
{
|
||||
@@ -323,7 +332,7 @@ void test1()
|
||||
pretend_op_completed(cli, find_op(cli, 1, OSD_OP_WRITE, 0, 0x1000), -EPIPE);
|
||||
check_disconnected(cli, 1);
|
||||
pretend_connected(cli, 1);
|
||||
cli->continue_ops(cli->client_retry_interval);
|
||||
cluster_client_test_t::continue_ops(cli);
|
||||
check_op_count(cli, 1, 1);
|
||||
can_complete(r1);
|
||||
pretend_op_completed(cli, find_op(cli, 1, OSD_OP_WRITE, 0, 0x1000), 0);
|
||||
@@ -342,7 +351,7 @@ void test1()
|
||||
check_completed(r1);
|
||||
check_disconnected(cli, 1);
|
||||
pretend_connected(cli, 1);
|
||||
cli->continue_ops(cli->client_retry_interval);
|
||||
cluster_client_test_t::continue_ops(cli);
|
||||
check_op_count(cli, 1, 1);
|
||||
pretend_op_completed(cli, find_op(cli, 1, OSD_OP_WRITE, 0, 0x1000), 0);
|
||||
check_op_count(cli, 1, 1);
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#undef WITH_ISAL
|
||||
#include "malloc_or_die.h"
|
||||
#include "errno.h"
|
||||
#include "crc32c.h"
|
||||
#include "crc32c.c"
|
||||
|
||||
int main(int narg, char *args[])
|
||||
{
|
||||
|
||||
+511
-45
@@ -20,7 +20,7 @@ static int count_writes(blockstore_heap_t & heap, heap_entry_t *obj)
|
||||
return n;
|
||||
}
|
||||
|
||||
#define FREE_SPACE_BIT 0x8000
|
||||
#define BS_HEAP_FREE_SPACE 0xAB8F
|
||||
#define GARBAGE_BIT ((uint64_t)1 << 63)
|
||||
|
||||
bool check_used_space(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint32_t block_num)
|
||||
@@ -30,10 +30,14 @@ bool check_used_space(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint32_
|
||||
uint8_t *data = buf;
|
||||
uint8_t *end = data+dsk.meta_block_size;
|
||||
uint32_t used = 0;
|
||||
while (data < end)
|
||||
while (data <= end-4)
|
||||
{
|
||||
heap_entry_t *wr = ((heap_entry_t*)data);
|
||||
if (!(wr->size & FREE_SPACE_BIT) && !wr->is_garbage())
|
||||
if (wr->entry_type == BS_HEAP_FREE_SPACE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!wr->is_garbage())
|
||||
{
|
||||
used += wr->size;
|
||||
}
|
||||
@@ -41,7 +45,7 @@ bool check_used_space(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint32_
|
||||
{
|
||||
break;
|
||||
}
|
||||
data += (wr->size & ~FREE_SPACE_BIT);
|
||||
data += wr->size;
|
||||
}
|
||||
free(buf);
|
||||
return used == heap.get_meta_block_used_space(block_num);
|
||||
@@ -72,6 +76,38 @@ void _test_big_write(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint64_t
|
||||
heap.complete_block_write(mblock);
|
||||
}
|
||||
|
||||
void _test_big_intent(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint64_t inode, uint64_t stripe, uint64_t version,
|
||||
bool stable, uint32_t offset, uint32_t len, uint8_t *data, uint32_t expected_mblock = 0)
|
||||
{
|
||||
uint32_t mblock = 999999;
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, inode), .stripe = stripe };
|
||||
uint8_t ext_bitmap[dsk.clean_entry_bitmap_size];
|
||||
memset(ext_bitmap, 0x8e, dsk.clean_entry_bitmap_size);
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
int res = heap.add_big_intent(oid, &obj, version, offset, len, ext_bitmap, data, NULL, &mblock);
|
||||
assert(res == 0);
|
||||
assert(mblock == 0);
|
||||
heap.start_block_write(mblock);
|
||||
heap.complete_block_write(mblock);
|
||||
heap.complete_lsn_write(obj->lsn);
|
||||
}
|
||||
|
||||
void _test_redirect_intent(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint64_t inode, uint64_t stripe, uint64_t version, uint64_t location,
|
||||
bool stable, uint32_t offset, uint32_t len, uint8_t *data, uint32_t expected_mblock = 0)
|
||||
{
|
||||
heap.use_data(INODE_WITH_POOL(1, inode), location); // blocks are allocated before write and outside the heap_t
|
||||
uint32_t mblock = 999999;
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, inode), .stripe = stripe };
|
||||
uint8_t ext_bitmap[dsk.clean_entry_bitmap_size];
|
||||
memset(ext_bitmap, 0x8e, dsk.clean_entry_bitmap_size);
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
int res = heap.add_redirect_intent(oid, &obj, version, offset, len, location, ext_bitmap, data, &mblock);
|
||||
assert(res == 0);
|
||||
assert(mblock == expected_mblock || expected_mblock == UINT32_MAX);
|
||||
heap.start_block_write(mblock);
|
||||
heap.complete_block_write(mblock);
|
||||
}
|
||||
|
||||
int _test_do_small_write(blockstore_heap_t & heap, blockstore_disk_t & dsk, uint64_t inode, uint64_t stripe, uint64_t version,
|
||||
uint32_t offset, uint32_t len, uint64_t location, bool stable, uint8_t *data, bool is_intent = false, uint32_t *mblock = NULL, heap_entry_t **obj = NULL)
|
||||
{
|
||||
@@ -975,8 +1011,10 @@ void test_modify_bitmap()
|
||||
printf("OK test_modify_bitmap\n");
|
||||
}
|
||||
|
||||
void test_recheck(bool async, bool csum, bool intent)
|
||||
void test_recheck(bool async, bool csum)
|
||||
{
|
||||
printf("test_recheck %s %s\n", async ? "async" : "sync", csum ? "csum" : "no_csum");
|
||||
|
||||
blockstore_disk_t dsk;
|
||||
_test_init(dsk, csum);
|
||||
std::vector<uint8_t> buffer_area(dsk.journal_device_size);
|
||||
@@ -989,15 +1027,52 @@ void test_recheck(bool async, bool csum, bool intent)
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data());
|
||||
heap.finish_recheck();
|
||||
|
||||
// object 1
|
||||
_test_big_write(heap, dsk, 1, 0, 1, 0x20000, true, 0, 8192, buffer_area.data());
|
||||
_test_small_write(heap, dsk, 1, 0, 2, 8*1024, 8*1024, 16*1024, true, buffer_area.data(), intent);
|
||||
// object 1 - two intent writes, both valid
|
||||
_test_big_write(heap, dsk, 1, 0, 1, 0, true, 0, 8192, buffer_area.data());
|
||||
_test_small_write(heap, dsk, 1, 0, 2, 4*1024, 8*1024, 0, true, buffer_area.data(), true);
|
||||
_test_small_write(heap, dsk, 1, 0, 3, 8*1024, 8*1024, 0, true, buffer_area.data(), true);
|
||||
|
||||
// object 2
|
||||
_test_big_write(heap, dsk, 2, 0, 1, 0x40000, true, 0, 8192, buffer_area.data());
|
||||
if (intent)
|
||||
_test_small_write(heap, dsk, 2, 0, 2, 20*1024, 4*1024, 36*1024, true, buffer_area.data(), intent);
|
||||
_test_small_write(heap, dsk, 2, 0, intent ? 3 : 2, 8*1024, 12*1024, 24*1024, true, buffer_area.data(), intent);
|
||||
// object 2 - two intent writes, second invalid
|
||||
_test_big_write(heap, dsk, 2, 0, 1, 0x20000, true, 0, 8192, buffer_area.data());
|
||||
_test_small_write(heap, dsk, 2, 0, 2, 4*1024, 8*1024, 0, true, buffer_area.data(), true);
|
||||
_test_small_write(heap, dsk, 2, 0, 3, 8*1024, 8*1024, 0, true, buffer_area.data(), true);
|
||||
|
||||
// object 3 - 2 valid small writes
|
||||
_test_big_write(heap, dsk, 3, 0, 1, 0x40000, true, 0, 8192, buffer_area.data());
|
||||
memset(buffer_area.data()+12*1024, 0xab, 8*1024);
|
||||
_test_small_write(heap, dsk, 3, 0, 2, 4*1024, 8*1024, 12*1024, true, buffer_area.data());
|
||||
memset(buffer_area.data()+20*1024, 0xab, 8*1024);
|
||||
_test_small_write(heap, dsk, 3, 0, 3, 8*1024, 8*1024, 20*1024, true, buffer_area.data());
|
||||
|
||||
// object 4 - first valid and second invalid small write
|
||||
_test_big_write(heap, dsk, 4, 0, 1, 0x60000, true, 0, 8192, buffer_area.data());
|
||||
memset(buffer_area.data()+28*1024, 0xab, 8*1024);
|
||||
_test_small_write(heap, dsk, 4, 0, 2, 4*1024, 8*1024, 28*1024, true, buffer_area.data());
|
||||
memset(buffer_area.data()+36*1024, 0xab, 8*1024);
|
||||
memset(buffer_area.data()+36*1024+4096+40, 0xcc, 40);
|
||||
_test_small_write(heap, dsk, 4, 0, 3, 8*1024, 8*1024, 36*1024, true, buffer_area.data());
|
||||
|
||||
// object 5 - first invalid and second valid small write
|
||||
_test_big_write(heap, dsk, 5, 0, 1, 0x80000, true, 0, 8192, buffer_area.data());
|
||||
memset(buffer_area.data()+44*1024, 0xab, 8*1024);
|
||||
memset(buffer_area.data()+44*1024+4096+40, 0xcc, 40);
|
||||
_test_small_write(heap, dsk, 5, 0, 2, 4*1024, 8*1024, 44*1024, true, buffer_area.data());
|
||||
memset(buffer_area.data()+52*1024, 0xab, 8*1024);
|
||||
_test_small_write(heap, dsk, 5, 0, 3, 8*1024, 8*1024, 52*1024, true, buffer_area.data());
|
||||
|
||||
// object 6 - single big_intent write, valid
|
||||
_test_redirect_intent(heap, dsk, 6, 0, 1, 0xA0000, true, 16384, 8192, buffer_area.data());
|
||||
|
||||
// object 7 - single big_intent write, invalid
|
||||
_test_redirect_intent(heap, dsk, 7, 0, 1, 0xC0000, true, 16384, 8192, buffer_area.data());
|
||||
|
||||
// object 8 - big_write + big_intent write, valid
|
||||
_test_big_write(heap, dsk, 8, 0, 1, 0xE0000, true, 0, 8192, buffer_area.data());
|
||||
_test_big_intent(heap, dsk, 8, 0, 2, true, 16384, 8192, buffer_area.data());
|
||||
|
||||
// object 9 - big_write + big_intent write, invalid
|
||||
_test_big_write(heap, dsk, 9, 0, 1, 0x100000, true, 0, 8192, buffer_area.data());
|
||||
_test_big_intent(heap, dsk, 9, 0, 2, true, 16384, 8192, buffer_area.data());
|
||||
|
||||
// persist
|
||||
assert(heap.get_meta_block_used_space(0) > 0);
|
||||
@@ -1007,8 +1082,10 @@ void test_recheck(bool async, bool csum, bool intent)
|
||||
|
||||
// reload heap
|
||||
{
|
||||
memset(buffer_area.data()+16*1024, 0xab, 20*1024); // valid data
|
||||
memset(buffer_area.data()+20*1024+64, 0xcc, 4); // invalid data in the second block of the first write
|
||||
memset(buffer_area.data()+16*1024, 0xab, 8*1024); // valid data for object 1
|
||||
memset(buffer_area.data()+24*1024, 0xab, 12*1024); // valid data for object 1 write 1
|
||||
memset(buffer_area.data()+36*1024, 0xab, 4*1024); // valid data for object 1 write 2
|
||||
memset(buffer_area.data()+36*1024+64, 0xcc, 4); // invalid data for object 1 write 2
|
||||
|
||||
blockstore_heap_t heap(&dsk, async ? NULL : buffer_area.data(), 10);
|
||||
uint64_t entries_loaded;
|
||||
@@ -1021,24 +1098,37 @@ void test_recheck(bool async, bool csum, bool intent)
|
||||
calls++;
|
||||
if (len)
|
||||
{
|
||||
if (!intent)
|
||||
assert(len == 8*1024);
|
||||
if (is_data)
|
||||
{
|
||||
assert(!is_data);
|
||||
assert(offset == 16384 && len == 8192 || offset == 24*1024 && len == 12*1024);
|
||||
memcpy(buf, buffer_area.data()+offset, len);
|
||||
// intent writes
|
||||
if (offset == 8*1024) // valid
|
||||
memcpy(buf, buffer_area.data(), len);
|
||||
else if (offset == 0x20000+8*1024) // invalid
|
||||
memset(buf, 0xcc, len);
|
||||
else if (offset == 0xA0000+16*1024) // valid
|
||||
memcpy(buf, buffer_area.data(), len);
|
||||
else if (offset == 0xC0000+16*1024) // invalid
|
||||
memset(buf, 0xcc, len);
|
||||
else if (offset == 0xE0000+16*1024) // valid
|
||||
memcpy(buf, buffer_area.data(), len);
|
||||
else if (offset == 0x100000+16*1024) // invalid
|
||||
memset(buf, 0xcc, len);
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(is_data);
|
||||
assert(offset == 0x20000+8192 && len == 8192 || 0x40000+8192 && len == 12*1024);
|
||||
memcpy(buf, buffer_area.data() + (offset == 0x20000+8192 ? 16*1024 : 24*1024), len);
|
||||
assert(offset == 12*1024 || offset == 20*1024 || offset == 28*1024 || offset == 36*1024 ||
|
||||
offset == 44*1024 || offset == 52*1024);
|
||||
memcpy(buf, buffer_area.data()+offset, len);
|
||||
}
|
||||
assert(cb);
|
||||
cb();
|
||||
}
|
||||
}, 1);
|
||||
assert(done);
|
||||
assert(calls == (async || intent ? 3 : 1));
|
||||
assert(calls == (async ? 13 : 7));
|
||||
|
||||
heap.finish_recheck();
|
||||
|
||||
@@ -1046,30 +1136,88 @@ void test_recheck(bool async, bool csum, bool intent)
|
||||
assert(mod.size() == 1);
|
||||
assert(mod[0] == 0);
|
||||
|
||||
// read object 1 - big_write should be there but small_write should be rechecked and removed
|
||||
// check objects
|
||||
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 1);
|
||||
assert(obj->lsn == 1);
|
||||
assert(obj->entry_type == BS_HEAP_BIG_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 1);
|
||||
assert(obj->big_location(&heap) == 0x20000);
|
||||
assert(count_writes(heap, obj) == 3);
|
||||
assert(obj->lsn == 3);
|
||||
assert(obj->entry_type == BS_HEAP_INTENT_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 3);
|
||||
|
||||
// read object 2 - both writes should be present
|
||||
oid = { .inode = INODE_WITH_POOL(1, 2), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == (intent ? 3 : 2));
|
||||
assert(obj->lsn == (intent ? 5 : 4));
|
||||
assert(count_writes(heap, obj) == 2);
|
||||
assert(obj->lsn == 5);
|
||||
assert(obj->entry_type == BS_HEAP_INTENT_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 2);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 3), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 3);
|
||||
assert(obj->lsn == 9);
|
||||
assert(obj->entry_type == BS_HEAP_SMALL_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == (intent ? 3 : 2));
|
||||
assert(obj->small().offset == 8192);
|
||||
assert(obj->small().len == 12*1024);
|
||||
assert(obj->small().location == 24*1024);
|
||||
assert(obj->version == 3);
|
||||
assert(obj->small().offset == 8*1024);
|
||||
assert(obj->small().len == 8*1024);
|
||||
assert(obj->small().location == 20*1024);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 4), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 2);
|
||||
assert(obj->lsn == 11);
|
||||
assert(obj->entry_type == BS_HEAP_SMALL_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 2);
|
||||
assert(obj->small().offset == 4*1024);
|
||||
assert(obj->small().len == 8*1024);
|
||||
assert(obj->small().location == 28*1024);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 5), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 1);
|
||||
assert(obj->lsn == 13);
|
||||
assert(obj->entry_type == BS_HEAP_BIG_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 1);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 6), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 1);
|
||||
assert(obj->lsn == 16);
|
||||
assert(obj->entry_type == BS_HEAP_BIG_INTENT|BS_HEAP_STABLE);
|
||||
assert(obj->version == 1);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 7), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(!obj);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 8), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 1);
|
||||
assert(obj->lsn == 19);
|
||||
assert(obj->entry_type == BS_HEAP_BIG_INTENT|BS_HEAP_STABLE);
|
||||
assert(obj->version == 2);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 9), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 1);
|
||||
assert(obj->lsn == 21); // lsn 20 is inserted for compaction
|
||||
assert(obj->entry_type == BS_HEAP_BIG_WRITE|BS_HEAP_STABLE);
|
||||
assert(obj->version == 1);
|
||||
|
||||
// check space
|
||||
|
||||
assert(check_used_space(heap, dsk, 0));
|
||||
}
|
||||
|
||||
printf("OK test_recheck %s %s %s\n", async ? "async" : "sync", csum ? "csum" : "no_csum", intent ? "intent" : "buffered");
|
||||
printf("...OK\n");
|
||||
}
|
||||
|
||||
void test_corruption()
|
||||
@@ -2147,6 +2295,326 @@ void test_explicit_complete()
|
||||
printf("OK test_explicit_complete\n");
|
||||
}
|
||||
|
||||
void test_skip_double_claim()
|
||||
{
|
||||
blockstore_disk_t dsk;
|
||||
_test_init(dsk, false);
|
||||
dsk.skip_double_claim = true;
|
||||
std::vector<uint8_t> tmp(dsk.meta_block_size);
|
||||
std::vector<uint8_t> out(dsk.meta_block_size*3);
|
||||
std::vector<uint8_t> buffer_area(dsk.journal_device_size);
|
||||
heap_entry_t *wr1 = NULL, *wr2 = NULL, *wr3 = NULL, *wr4 = NULL;
|
||||
uint32_t total_size = 0;
|
||||
|
||||
{
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data());
|
||||
|
||||
wr1 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr1->size = heap.get_big_entry_size();
|
||||
wr1->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr1->lsn = 1;
|
||||
wr1->inode = INODE_WITH_POOL(1, 1);
|
||||
wr1->stripe = 0;
|
||||
wr1->version = 1;
|
||||
wr1->set_big_location(&heap, 0x40000); // <-- overwritten
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
total_size += wr1->size;
|
||||
|
||||
wr2 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr2->size = heap.get_big_entry_size();
|
||||
wr2->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr2->lsn = 2;
|
||||
wr2->inode = INODE_WITH_POOL(1, 1);
|
||||
wr2->stripe = 0;
|
||||
wr2->version = 2;
|
||||
wr2->set_big_location(&heap, 0); // <-- double claimed
|
||||
wr2->crc32c = wr2->calc_crc32c();
|
||||
total_size += wr2->size;
|
||||
|
||||
wr3 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr3->size = heap.get_big_entry_size();
|
||||
wr3->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr3->lsn = 3;
|
||||
wr3->inode = INODE_WITH_POOL(1, 1);
|
||||
wr3->stripe = 0x20000;
|
||||
wr3->version = 1;
|
||||
wr3->set_big_location(&heap, 0); // <-- double claimed
|
||||
wr3->crc32c = wr3->calc_crc32c();
|
||||
total_size += wr3->size;
|
||||
|
||||
wr4 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr4->size = heap.get_big_entry_size();
|
||||
wr4->entry_type = BS_HEAP_BIG_WRITE; // <-- unstable
|
||||
wr4->lsn = 4;
|
||||
wr4->inode = INODE_WITH_POOL(1, 1);
|
||||
wr4->stripe = 0x20000;
|
||||
wr4->version = 2;
|
||||
wr4->set_big_location(&heap, 0x20000);
|
||||
wr4->crc32c = wr4->calc_crc32c();
|
||||
total_size += wr4->size;
|
||||
|
||||
*(uint16_t*)(tmp.data() + total_size) = dsk.meta_block_size - total_size;
|
||||
*(uint16_t*)(tmp.data() + total_size + 2) = BS_HEAP_FREE_SPACE;
|
||||
|
||||
uint64_t entries_loaded;
|
||||
heap.load_blocks(0, dsk.meta_block_size, tmp.data(), false, entries_loaded);
|
||||
heap.finish_load();
|
||||
bool done = heap.recheck_small_writes([&](bool, uint64_t, uint64_t, uint8_t*, std::function<void()> cb) {}, 1);
|
||||
assert(done);
|
||||
heap.finish_recheck();
|
||||
auto mod = heap.get_recheck_modified_blocks();
|
||||
assert(mod.size() == 1);
|
||||
assert(mod[0] == 0);
|
||||
|
||||
// [1 2] [3 4] - should erase first
|
||||
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
assert(!obj);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0x20000 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
|
||||
assert(heap.is_data_used(0));
|
||||
assert(heap.is_data_used(0x20000));
|
||||
assert(!heap.is_data_used(0x40000));
|
||||
|
||||
assert(check_used_space(heap, dsk, 0));
|
||||
|
||||
heap.get_meta_block(0, out.data());
|
||||
}
|
||||
|
||||
{
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data());
|
||||
|
||||
wr1->lsn = 1;
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
wr2->lsn = 3;
|
||||
wr2->crc32c = wr2->calc_crc32c();
|
||||
wr3->lsn = 2;
|
||||
wr3->crc32c = wr3->calc_crc32c();
|
||||
wr4->lsn = 4;
|
||||
wr4->crc32c = wr4->calc_crc32c();
|
||||
|
||||
uint64_t entries_loaded;
|
||||
heap.load_blocks(0, dsk.meta_block_size, tmp.data(), false, entries_loaded);
|
||||
heap.finish_load();
|
||||
bool done = heap.recheck_small_writes([&](bool, uint64_t, uint64_t, uint8_t*, std::function<void()> cb) {}, 1);
|
||||
assert(done);
|
||||
heap.finish_recheck();
|
||||
auto mod = heap.get_recheck_modified_blocks();
|
||||
assert(mod.size() == 1);
|
||||
assert(mod[0] == 0);
|
||||
|
||||
// [1 [2 3] 4] - intersect - should erase both
|
||||
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
assert(!obj);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0x20000 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(!obj);
|
||||
|
||||
assert(!heap.is_data_used(0));
|
||||
assert(!heap.is_data_used(0x20000));
|
||||
assert(!heap.is_data_used(0x40000));
|
||||
|
||||
assert(check_used_space(heap, dsk, 0));
|
||||
|
||||
heap.get_meta_block(0, out.data()+dsk.meta_block_size);
|
||||
}
|
||||
|
||||
{
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data());
|
||||
|
||||
// [3 4] [1 2] - should erase second
|
||||
|
||||
wr1->lsn = 3;
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
wr2->lsn = 4;
|
||||
wr2->crc32c = wr2->calc_crc32c();
|
||||
wr3->lsn = 1;
|
||||
wr3->crc32c = wr3->calc_crc32c();
|
||||
wr4->lsn = 2;
|
||||
wr4->crc32c = wr4->calc_crc32c();
|
||||
|
||||
uint64_t entries_loaded;
|
||||
heap.load_blocks(0, dsk.meta_block_size, tmp.data(), false, entries_loaded);
|
||||
heap.finish_load();
|
||||
bool done = heap.recheck_small_writes([&](bool, uint64_t, uint64_t, uint8_t*, std::function<void()> cb) {}, 1);
|
||||
assert(done);
|
||||
heap.finish_recheck();
|
||||
auto mod = heap.get_recheck_modified_blocks();
|
||||
assert(mod.size() == 1);
|
||||
assert(mod[0] == 0);
|
||||
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0x20000 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(!obj);
|
||||
|
||||
assert(heap.is_data_used(0));
|
||||
assert(!heap.is_data_used(0x20000));
|
||||
assert(!heap.is_data_used(0x40000));
|
||||
|
||||
assert(check_used_space(heap, dsk, 0));
|
||||
|
||||
heap.get_meta_block(0, out.data()+dsk.meta_block_size*2);
|
||||
}
|
||||
|
||||
// Validate persisted variants
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data());
|
||||
uint64_t entries_loaded;
|
||||
heap.load_blocks(0, dsk.meta_block_size, out.data() + dsk.meta_block_size*i, false, entries_loaded);
|
||||
heap.finish_load();
|
||||
bool done = heap.recheck_small_writes([&](bool, uint64_t, uint64_t, uint8_t*, std::function<void()> cb) {}, 1);
|
||||
assert(done);
|
||||
heap.finish_recheck();
|
||||
auto mod = heap.get_recheck_modified_blocks();
|
||||
assert(mod.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void test_postpone_load()
|
||||
{
|
||||
blockstore_disk_t dsk;
|
||||
// FIXME dsk.readonly = true;
|
||||
_test_init(dsk, false);
|
||||
std::vector<uint8_t> tmp(dsk.meta_block_size*10);
|
||||
std::vector<uint8_t> buffer_area(dsk.journal_device_size);
|
||||
|
||||
{
|
||||
blockstore_heap_t heap(&dsk, buffer_area.data(), 10);
|
||||
|
||||
size_t total_size = 0;
|
||||
auto wr1 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr1->size = heap.get_big_entry_size();
|
||||
wr1->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr1->lsn = 1;
|
||||
wr1->inode = INODE_WITH_POOL(1, 1);
|
||||
wr1->stripe = 0;
|
||||
wr1->version = 1;
|
||||
wr1->set_big_location(&heap, 0x20000);
|
||||
memset(wr1->get_ext_bitmap(&heap), 0xff, dsk.clean_entry_bitmap_size);
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
total_size += wr1->size;
|
||||
|
||||
assert(total_size+heap.get_big_entry_size() <= dsk.meta_block_size);
|
||||
wr1 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr1->size = heap.get_big_entry_size();
|
||||
wr1->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr1->lsn = 20; // 20 but compacted - newest entry
|
||||
wr1->inode = INODE_WITH_POOL(1, 1);
|
||||
wr1->stripe = 0;
|
||||
wr1->version = 1;
|
||||
wr1->set_big_location(&heap, 0x20000);
|
||||
memset(wr1->get_ext_bitmap(&heap), 0xff, dsk.clean_entry_bitmap_size);
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
total_size += wr1->size;
|
||||
|
||||
uint32_t small_size = heap.get_small_entry_size(0, 4096);
|
||||
auto add_small = [&](uint64_t lsn)
|
||||
{
|
||||
assert(total_size+small_size <= dsk.meta_block_size);
|
||||
auto wr2 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr2->size = small_size;
|
||||
wr2->entry_type = BS_HEAP_SMALL_WRITE|BS_HEAP_STABLE;
|
||||
wr2->lsn = lsn;
|
||||
wr2->inode = INODE_WITH_POOL(1, 1);
|
||||
wr2->stripe = 0;
|
||||
wr2->version = lsn;
|
||||
wr2->small().offset = (lsn % 32)*4096;
|
||||
wr2->small().len = 4096;
|
||||
wr2->small().location = lsn*4096;
|
||||
memset(wr2->get_ext_bitmap(&heap), 0xff, dsk.clean_entry_bitmap_size);
|
||||
*((uint32_t*)wr2->get_checksum(&heap)) = crc32c(0, buffer_area.data()+wr2->small().location, 4096);
|
||||
wr2->crc32c = wr2->calc_crc32c();
|
||||
total_size += small_size;
|
||||
};
|
||||
for (int i = 0; i < 10; i++)
|
||||
add_small(2 + 2*i); // 2..20
|
||||
for (int i = 0; i < 10; i++)
|
||||
add_small(30 - i); // 21..30
|
||||
for (int i = 0; i < 9; i++)
|
||||
add_small(3 + 2*i); // 3..19
|
||||
|
||||
assert(total_size+heap.get_big_entry_size() <= dsk.meta_block_size);
|
||||
wr1 = (heap_entry_t*)(tmp.data() + total_size);
|
||||
wr1->size = heap.get_big_entry_size();
|
||||
wr1->entry_type = BS_HEAP_BIG_WRITE|BS_HEAP_STABLE;
|
||||
wr1->lsn = 15; // 15 but also compacted
|
||||
wr1->inode = INODE_WITH_POOL(1, 1);
|
||||
wr1->stripe = 0;
|
||||
wr1->version = 1;
|
||||
wr1->set_big_location(&heap, 0x20000);
|
||||
memset(wr1->get_ext_bitmap(&heap), 0xff, dsk.clean_entry_bitmap_size);
|
||||
wr1->crc32c = wr1->calc_crc32c();
|
||||
total_size += wr1->size;
|
||||
|
||||
*(uint16_t*)(tmp.data() + total_size) = dsk.meta_block_size - total_size;
|
||||
*(uint16_t*)(tmp.data() + total_size + 2) = BS_HEAP_FREE_SPACE;
|
||||
|
||||
uint64_t entries_loaded;
|
||||
heap.load_blocks(0, dsk.meta_block_size, tmp.data(), false, entries_loaded);
|
||||
|
||||
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
heap_entry_t *obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 22);
|
||||
|
||||
heap.finish_load();
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 32);
|
||||
uint64_t clsn = 30;
|
||||
bool stable = true;
|
||||
for (auto wr = obj; wr; wr = heap.prev(wr))
|
||||
{
|
||||
assert(wr->lsn == clsn);
|
||||
if (clsn == 20 || clsn == 15)
|
||||
{
|
||||
assert(wr->entry_type == (stable ? BS_HEAP_BIG_WRITE|BS_HEAP_STABLE : BS_HEAP_SMALL_WRITE|BS_HEAP_STABLE));
|
||||
if (stable)
|
||||
stable = false;
|
||||
else
|
||||
{
|
||||
clsn--;
|
||||
stable = true;
|
||||
}
|
||||
}
|
||||
else if (clsn == 1)
|
||||
{
|
||||
assert(wr->entry_type == BS_HEAP_BIG_WRITE|BS_HEAP_STABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(wr->entry_type == BS_HEAP_SMALL_WRITE|BS_HEAP_STABLE);
|
||||
clsn--;
|
||||
}
|
||||
}
|
||||
|
||||
bool done = heap.recheck_small_writes([&](bool, uint64_t, uint64_t, uint8_t*, std::function<void()> cb) {}, 1);
|
||||
assert(done);
|
||||
heap.finish_recheck();
|
||||
auto mod = heap.get_recheck_modified_blocks();
|
||||
assert(mod.size() == 1);
|
||||
|
||||
oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
|
||||
obj = heap.read_entry(oid);
|
||||
assert(obj);
|
||||
assert(count_writes(heap, obj) == 11);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Add a test for big_intent, incl. explicit_complete with big_intent over big_write over deletion over big_write :)
|
||||
|
||||
int main(int narg, char *args[])
|
||||
@@ -2164,14 +2632,10 @@ int main(int narg, char *args[])
|
||||
test_compact(false, false);
|
||||
test_iterate_compaction();
|
||||
test_modify_bitmap();
|
||||
test_recheck(false, true, false);
|
||||
test_recheck(false, false, false);
|
||||
test_recheck(true, true, false);
|
||||
test_recheck(true, false, false);
|
||||
test_recheck(false, true, true);
|
||||
test_recheck(false, false, true);
|
||||
test_recheck(true, true, true);
|
||||
test_recheck(true, false, true);
|
||||
test_recheck(false, true);
|
||||
test_recheck(false, false);
|
||||
test_recheck(true, true);
|
||||
test_recheck(true, false);
|
||||
test_corruption();
|
||||
test_full_overwrite(true);
|
||||
test_full_overwrite(false);
|
||||
@@ -2187,5 +2651,7 @@ int main(int narg, char *args[])
|
||||
test_recalc_stats();
|
||||
test_redirect_intent_csums();
|
||||
test_explicit_complete();
|
||||
test_skip_double_claim();
|
||||
test_postpone_load();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
+1
-1
@@ -410,7 +410,7 @@ uint32_t crc32c_pad(uint32_t prev_crc, const void *buf, size_t len, size_t left_
|
||||
r = crc32c(r, zero_page, 4096);
|
||||
right_pad -= 4096;
|
||||
}
|
||||
if (left_pad > 0)
|
||||
if (right_pad > 0)
|
||||
r = crc32c(r, zero_page, right_pad);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ timerfd_manager_t::timerfd_manager_t(std::function<void(int, bool, std::function
|
||||
{
|
||||
this->set_fd_handler = set_fd_handler;
|
||||
wait_state = 0;
|
||||
// FIXME: Use timerfd_interval based directly on io_uring
|
||||
if (set_fd_handler)
|
||||
{
|
||||
timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
|
||||
Reference in New Issue
Block a user