Compare commits

...
Author SHA1 Message Date
Vitaliy Filippov ee0bae573f Remove n_subops=0 (TODO also needs OSD unit tests) 2026-05-21 13:31:32 +03:00
Vitaliy Filippov 27be4ee2fa Followup to mark_partial_write fix - also free subops in other branch to prevent assert on ENOSPC 2026-05-20 22:04:28 +03:00
Vitaliy Filippov 00517e2bac Fix header checksum after new store write-meta 2026-05-19 12:30:21 +03:00
Vitaliy Filippov e0a2615cbc Fix test build 2026-05-19 02:27:03 +03:00
Vitaliy Filippov 63fe3c323a Release 3.0.12
Important fixes (except the new store):

- Fixed a possible use-after-free in the OSD during error handling of initial
  commit/rollback of objects in EC pools.
- Fixed a possible free of an invalid pointer in the OSD during read errors
  from snapshot/clone chains in EC pools.
- Fixed possibly incorrect handling of commit/rollback operations in EC pools
  during pool PG count changes.
- Fixed the inverted fsync enable parameter in the ublk driver (fsync was not
  enabled on pools without immediate_commit).
- Added the raw-ls command for debugging purposes to find object versions in
  the cluster using listing operations.

New store fixes:

- Improved startup speed by using LSN-based sorting only for objects with a
  large number of intermediate versions.
- Added skip_double_claim option as a temporary workaround to fix the rare OSD
  startup error with the "double claimed block" message, observed by several
  users. This option does not affect data integrity.
- Fixed incorrect rechecking of small writes during startup, which in theory
  could lead to duplicate small write object entries on the OSD.
- Fixed fsync operation for disks with a writeback cache (without capacitors):
  - Fixed incorrect semantics of consecutive fsyncs (next fsync was not blocked
    by the previous one).
  - Added fsync when copying small writes from the buffer to the data device
    (somehow forgotten during initial development).
  - Added fsync after the initial garbage collection during OSD startup.
  - Fixed incorrect cast of LSN from uint64 to uint32, breaking fsync when
    reaching LSN 2^32.
- Added missing verification of the metadata header checksum during startup.
- Fixed incorrect updating of object checksums in perfect_csum_update=true mode.
- Fixed a possible OSD crash with "assertion failed" when processing a malformed
  EC STABILIZE operation.
- Fixed the accounting of active compactor coroutines.
- Removed broken and untested new->old store conversion support.

Minor issues fixed:

- Incorrect accounting of OSD local operation statistics in replicated pools.
- Missing non-zero exitcodes on vitastor-disk resize command errors.
- Missing reset of the list of inconsistent objects during PG restarts.
- Theoretically possible hangs of various OSD operations when working with
  completely corrupted objects (without a single available copy), and possibly
  in some other very rare situations.
- Incorrect fsyncs when deleting objects from pools without immediate_commit
  (on disks with a writeback cache), which previously could leave garbage when
  deleting misplaced objects.
- Possible crash/memory corruption of the NFS server during a targeted attack
  on NFS-RDMA.
- Possibly incorrect handling of ENOSPC/EIO write errors in replicated pools,
  leading to inability to retry the write later.
- Possible crash instead of a clean error exit when starting an OSD with the
  old storage engine on a disk with corrupted journal data.
- Shallow copying of PG configuration in the monitor, however, not related to
  actual bugs.
- Incorrect checking of allocated blocks in the QEMU driver in an unused code
  branch (without the BDRV_WANT_ZERO flag).
- Possible memory leak on read errors of corrupted objects.
- Possible incorrect PG states when corrupted objects are detected.
- Possible failure to mark all "bad" copies of an object during scrubs without
  checksums and with a large number of replicas (> 4).
- Incorrect checksum calculation in the old storage engine when
  bitmap_granularity < 4096 (a practically unused configuration).
- Theoretically possible OSD crash in rare cases during a scrub and simultaneous
  object recovery.
- Theoretically possible OSD crash when handling PING operation errors.
- Slightly suboptimal logic for reusing the RDMA send buffer.
- Possible memory leak when canceling an already running scrub via no_scrub.
- Possible memory corruption when a client (e.g., QEMU code) passes invalid
  buffers and the writeback cache is enabled.
- Potentially incorrect search for corrupted parts of EC objects (inability
  to find a "good" combination) during a scrub with checksums disabled.
- Possible additional memory usage on the OSD side when handling failed reads
  from snapshots (not a leak however - the memory was freed upon client
  disconnection).
- Potential sudden write slowdown at certain pg epoch values due to incorrect
  epoch update logic in etcd.
2026-05-18 02:24:22 +03:00
Vitaliy Filippov a88465df05 Fix build under older gcc 2026-05-18 01:17:56 +03:00
Vitaliy Filippov ad24be717a Wrap delete rdma_conn into destroy_rdma_conn 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 648e3b12f0 Replace public/protected ifdef with a friend class 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a675993c74 Remove unused osd_test.cpp and mock/ringloop.h, move epmgr out of osd_t 2026-05-18 01:17:56 +03:00
Vitaliy Filippov c9dfd0f67d Fix possible buffer overflow in NFS-RDMA 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 84919a10a9 Extract msgr_iothread to separate .{cpp,h} 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 51ae4d6e24 Remove extra assert in heap after reworking recheck 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 572b20fedc Fix mark_partial_write for replicas (TODO: try to write an OSD unit test for it) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4e2724b28f Remove duplicate subop submit/fail code 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 768b1675f8 Do not use ISA-L in test_crc32c 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 38fa722725 Remove the rest of tcmalloc from build 2026-05-18 01:17:56 +03:00
Vitaliy Filippov e56d83fb7f Remove heap->v1 conversion code (untested, unused and probably buggy) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov ff95a85875 Add bound check to je_crc32 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 98203568a8 Handle ENOENT from add_commit 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 89df98ee08 Deep copy pg_history and prev_pgs before mutation 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 0007a831b6 Add return to handle_flush_op when stopping the PG 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 40517c335f Fix absolute bmp_end 2026-05-18 01:17:56 +03:00
Vitaliy Filippov c9f7308b6a Wrap this submit_ into n_subops > 0 too 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 85c7e3bde0 Fix inverted writeback in ublk 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4fb55b3535 Rework recheck_small_writes to free multiple bad entries more correctly and not use garbage as "already freed" flag 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 912aca11a3 Improve test_recheck to check all possible combinations 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 7b454bd16c Do not try to free snapshot_bitmaps 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a0c8be46a4 Speedup blockstore_heap loading by postponing only objects with a lot of versions 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 53b4329fac Add missing active_flushers-- 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a7f41c4a12 Add assert to reconstruct_stripes_ec 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 5d78057ac3 Fix cur_op->buf memory leak on EIO/EDOM reads(?) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 8efc5a353f Fix pg_state_bits overwriting PG_HAS_CORRUPTED 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 603b26b896 Add a FIXME about delete buffering 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a3b0fe0deb Fix op stats not tracked for OP_WRITE_STABLE 2026-05-18 01:17:56 +03:00
Vitaliy Filippov f504e356d5 Fix schedule_next_recheck_at 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4ed17b7070 Sync metadata after writing out recheck_modified_blocks 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 1fd2819724 Fix marking only first OSD from non-majority group as corrupted during scrub
Note: Could be tested with 5 replicas - 3 good and 2 corrupted if we had OSD unit tests
2026-05-18 01:17:56 +03:00
Vitaliy Filippov dcdabbc1ec Fix vitastor-disk resize error return codes missed 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 625d5b7b9e Fix punch_holes not updating entry crc (bug when perfect_csum_update==true) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 9e507fd333 Fix typo in crc32_pad
Didn't affect anything though because it was only skipping < 4096 byte remainders
2026-05-18 01:17:56 +03:00
Vitaliy Filippov c2b5118127 Add data device fsyncs during compaction (forgotten during initial heap store dev) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 4b926e2223 Add a test for missing data device fsyncs 2026-05-18 01:17:56 +03:00
Vitaliy Filippov a5d9a6996a Do not try to increment ref_count if recheck_state is null 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 0ee03e7172 Add reply == request opcode check (also just in case) 2026-05-18 01:17:56 +03:00
Vitaliy Filippov 88b7d9afcd Expect to receive bitmap only for successfull response 2026-05-18 01:17:56 +03:00
Vitaliy Filippov f271c8450c Block sync by all previous syncs 2026-05-11 16:33:49 +03:00
Vitaliy Filippov f78d7d4efc Do not capture client pointer in ping lambda 2026-05-11 16:30:01 +03:00
Vitaliy Filippov fdaf7c88ff Fix RDMA send buffer wraparound - could only lead to slower sending but not to corruption because the bug was canceled by send_out_pos >= send_done_pos check 2026-05-11 16:28:03 +03:00
Vitaliy Filippov 2fb6eb0c30 Free scrub_cur_list.buf when turning on no_scrub (potential memory leak) 2026-05-11 16:15:48 +03:00
Vitaliy Filippov 36d2b56208 Deep copy merged_history, also just in case 2026-05-11 16:14:03 +03:00
Vitaliy Filippov 14b22f2ba9 Retry even fatal errors for OP_SYNC (no real-world issue, just in case) 2026-05-11 16:08:14 +03:00
Vitaliy Filippov fe8b1fe0cc Add assert in cluster_client writeback to prevent overflow when incorrect operation data is passed 2026-05-11 16:06:19 +03:00
Vitaliy Filippov 1ec963e468 Fix ec_find_good for misplaced objects with extra corrupted chunks 2026-05-11 15:57:21 +03:00
Vitaliy Filippov 5100f822d8 Wrap all submit_primary_xxx_subops waits with if (n_subops) > 0 2026-05-11 15:17:50 +03:00
Vitaliy Filippov 7432494e88 Remove _LARGEFILE64_SOURCE warnings 2026-05-11 14:59:20 +03:00
Vitaliy Filippov d0c0f3ea39 Do not ignore cur_primary change on PG state validation errors 2026-05-11 14:59:20 +03:00
Vitaliy Filippov f61190f31d Fix send_list/outbox desync on failed SEC_READ_BMP (possible memory leak) 2026-05-11 14:59:11 +03:00
Vitaliy Filippov 3dc0ab5c33 Clear inconsistent_objects on repeer 2026-05-11 14:59:08 +03:00
Vitaliy Filippov de96efed2f Fix 2^(64-PG_EPOCH_BITS) - 1 check 2026-05-11 14:59:08 +03:00
Vitaliy Filippov 87a5230798 Add forgotten header_csum check to the new store 2026-05-11 14:59:08 +03:00
Vitaliy Filippov 0c5e6d4346 Do not use modified_block(ui32) to store lsn(ui64) 2026-05-11 14:59:08 +03:00
Vitaliy Filippov b278087410 Fix assert in blockstore_heap 2026-05-11 13:01:03 +03:00
Vitaliy Filippov a8e821b13b Mark OSDs as dirty during deletes more correctly 2026-05-11 12:56:23 +03:00
Vitaliy Filippov caa70317fa Add a workaround for "double claim" bug with the new store 2026-05-10 17:00:36 +03:00
Vitaliy Filippov b8eaaabfe4 Add raw-ls command for troubleshooting 2026-05-10 17:00:36 +03:00
Vitaliy Filippov e4d80c415e Always check pool resharding state also for stabilize/rollback 2026-05-10 13:22:02 +03:00
Vitaliy Filippov 553191c3ff Add asserts to the new store too O:-) 2026-05-10 11:39:54 +03:00
Vitaliy Filippov ab385252b5 Add offset asserts before each write 2026-05-10 10:56:32 +03:00
93 changed files with 1971 additions and 1268 deletions
+1 -1
View File
@@ -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
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.11
VITASTOR_VERSION ?= v3.0.12
all: build push
+1 -1
View File
@@ -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)"
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -1,4 +1,4 @@
vitastor (3.0.11-1) unstable; urgency=medium
vitastor (3.0.12-1) unstable; urgency=medium
* Bugfixes
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.11
VITASTOR_VERSION ?= v3.0.12
all: build push
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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: \
+2 -2
View File
@@ -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: \
+1 -2
View File
@@ -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
+1 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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": [
+1 -1
View File
@@ -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__)
+2 -2
View File
@@ -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++
+2 -2
View File
@@ -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++
+2 -2
View File
@@ -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++
+2 -2
View File
@@ -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
View File
@@ -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})
+1
View File
@@ -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;
+2
View File
@@ -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;
+112 -18
View File
@@ -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;
+13
View File
@@ -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
View File
@@ -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())
{
+24 -6
View File
@@ -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);
+8 -3
View File
@@ -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)
{
+24 -16
View File
@@ -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(),
+7
View File
@@ -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())
+4 -2
View File
@@ -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;
}
+4
View File
@@ -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;
+1 -1
View File
@@ -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"
+2
View File
@@ -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
);
+1
View File
@@ -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
);
+5 -6
View File
@@ -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
+2 -4
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -9
View File
@@ -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;
};
+9 -3
View File
@@ -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);
}
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+129
View File
@@ -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();
}
}
+38
View File
@@ -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);
};
+1
View File
@@ -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;
+17 -2
View File
@@ -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;
}
+3 -2
View File
@@ -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);
+7 -1
View File
@@ -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)
{
+7 -26
View File
@@ -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
}
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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())
{
+1 -1
View File
@@ -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}
+1
View File
@@ -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)
+10
View File
@@ -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)
+1
View File
@@ -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);
+212
View File
@@ -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;
};
}
-1
View File
@@ -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}
)
+3
View File
@@ -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);
-3
View File
@@ -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();
+2 -1
View File
@@ -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;
+19 -136
View File
@@ -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)
+5 -5
View File
@@ -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);
+2 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+4 -1
View File
@@ -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;
}
+3
View File
@@ -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?
+2
View File
@@ -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
View File
@@ -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 });
+1 -14
View File
@@ -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;
+58 -64
View File
@@ -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++;
}
+16 -10
View File
@@ -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)
{
+39 -16
View File
@@ -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
View File
@@ -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;
}
+53
View File
@@ -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);
}
+3 -1
View File
@@ -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",
+8 -11
View File
@@ -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;
+2 -6
View File
@@ -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
+4
View File
@@ -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)
{
}
-31
View File
@@ -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()
{
}
};
-389
View File
@@ -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);
}
+9 -6
View File
@@ -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)
+2 -1
View File
@@ -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);
+131 -4
View File
@@ -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);
+12 -3
View File
@@ -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);
+2 -1
View File
@@ -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
View File
@@ -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;
}
+3
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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);