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
Vitaliy Filippov 041185c673 Release 3.0.11
A single hotfix:

- Fix missing error handling in EC snapshot/cloned image reads potentially
  leading to corrupted reads during OSD/PG restarts
2026-05-09 18:49:36 +03:00
Vitaliy Filippov b03ac80a57 Fix missing error handling in chained read sec_read_bmp handling 2026-05-07 15:01:49 +03:00
Vitaliy Filippov 2ba56074f9 Run copy_part_bitmaps only after all subops succeed (slightly more correct) 2026-05-07 15:01:40 +03:00
Vitaliy Filippov 4acfe149cb Release 3.0.10
Important bug fixes (new store):
- Fix OSDs possibly refusing to start with "write metadata failed at offset xxx: Invalid argument"
  (fix buffer alignment during initial garbage collection)
- Rollback change from 3.0.4 - on-disk garbage entries are not skipped on start again. This change
  doesn't have any impact normally, but OSDs originally running 3.0.0-3.0.2 and then upgraded
  to 3.0.9 may hit a bug where 3.0.9 refuses to start due to entries marked as garbage too
  early and flushed to disk in 3.0.0-3.0.2.

Other changes:
- Auto-select the only RDMA device/port if there is only one
- Rollback one 3.0.9 change - there was no actual use-after-free :)
  (the problem was only relevant to an unstable development version)
- Fix `vitastor-nfs --trace` option
- Fix an unintended 1 second sleep in vitastor-cli rm-data
- Fix inode statistics not being cleared for a deleted pool
- Fix print to stdout in client
2026-04-27 13:53:48 +03:00
Vitaliy Filippov 008ed5b269 Rollback 25ecca7625 - there was no actual use-after-free :) 2026-04-22 01:35:38 +03:00
Vitaliy Filippov 4fffe0f032 Fix vitastor-nfs trace option 2026-04-20 21:51:47 +03:00
Vitaliy Filippov a76d5ccc0d Wakeup callers in rm-data 2026-04-17 13:53:32 +03:00
Vitaliy Filippov 8ed1e180e0 Clear inode_stats in mon 2026-04-14 02:44:46 +03:00
Vitaliy Filippov 8832fc3b14 Fix print to stdout in client 2026-04-14 02:43:50 +03:00
Vitaliy Filippov 0134934c99 Do not skip garbage entries on start (rollback change from 3.0.4) 2026-04-11 12:05:02 +03:00
Vitaliy Filippov 2e36f292bd Fix buffer alignment during metadata clearing on init 2026-04-10 21:56:12 +03:00
Vitaliy Filippov bcc6419760 Auto-select the only RDMA device/port if there is only one 2026-04-08 15:46:10 +03:00
Vitaliy Filippov dd5941b9a4 Release 3.0.9
- Fix broken garbage collection in the new store leading to very high memory usage
- Make the new store do full garbage collection on OSD startup by default (can be turned off with a new gc_on_start parameter)
- Make startup logging with the new store more informative, allow to log amount of garbage with log_level > 1
- Fix clients reading from snapshots incorrectly on retries (for example when OSDs were restarted during read)
- Fix a possible "send-after-free" in OSD which could probably also affect snapshot reads
- Fix OSD crashing when handling a read_bitmap operation from a deleted object with the new store
2026-04-04 18:38:40 +03:00
Vitaliy Filippov 4005b88865 Do not use node_type 2026-04-04 17:37:47 +03:00
Vitaliy Filippov 280b5cd675 Add gc_on_start docs 2026-04-04 17:19:32 +03:00
Vitaliy Filippov e5c505eaf4 Write 1 entry to blocks to collect garbage 2026-04-03 18:08:12 +00:00
Vitaliy Filippov c1d244d4f0 Fix init metadata entry loading logging 2026-04-03 18:08:12 +00:00
Vitaliy Filippov 9b264a212f Do full metadata GC on start (new store) 2026-04-03 18:08:12 +00:00
Vitaliy Filippov ff7f5cb4f4 Report live & garbage entry count / memory with log_level > 1 2026-04-02 23:45:13 +00:00
Vitaliy Filippov 25ecca7625 Fix use-after-free in OSD ops (ooops) 2026-04-02 13:48:17 +00:00
Vitaliy Filippov 99c4244004 Try to write into blocks with more garbage first 2026-04-02 13:48:17 +00:00
Vitaliy Filippov 9949b9fb4e Fix incorrect reads from snapshots on retries 2026-03-31 02:06:53 +03:00
Vitaliy Filippov e6881ad1d5 Fix read_bitmap from deleted objects with the new store 2026-03-31 01:59:53 +03:00
106 changed files with 2277 additions and 1337 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
set(VITASTOR_VERSION "3.0.8")
set(VITASTOR_VERSION "3.0.12")
include(CTest)
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.8
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.8
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.8
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.8"
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.8-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.8
VITASTOR_VERSION ?= v3.0.12
all: build push
+1 -1
View File
@@ -4,7 +4,7 @@
#
# Desired Vitastor version
VITASTOR_VERSION=v3.0.8
VITASTOR_VERSION=v3.0.12
# Additional arguments for all containers
# For example, you may want to specify a custom logging driver here
+7
View File
@@ -70,6 +70,7 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -753,3 +754,9 @@ This option sets the maximum number of object is a chunk. Moving 100k objects us
- Default: 100
This option sets the interval between handling two PG count change chunks.
## gc_on_start
- Type: boolean
Forcibly clean all garbage entries in the new store on every OSD restart.
+7
View File
@@ -71,6 +71,7 @@
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -793,3 +794,9 @@ pg_minsize OSD во время переключений, что может по
- Значение по умолчанию: 100
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
## gc_on_start
- Тип: булево (да/нет)
Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
+4
View File
@@ -938,3 +938,7 @@
This option sets the interval between handling two PG count change chunks.
info_ru: |
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
- name: gc_on_start
type: bool
info: Forcibly clean all garbage entries in the new store on every OSD restart.
info_ru: Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.
+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.8`
`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.8 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.8`
`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.8 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
## Базовая инструкция
+1
View File
@@ -262,3 +262,4 @@ Options:
| `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |
+1
View File
@@ -274,3 +274,4 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+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.8",
"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
View File
@@ -37,6 +37,7 @@ function derive_osd_stats(st, prev, prev_diff)
const n = c.count - BigInt(pr && pr.count||0);
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
}
diff.inode_stats = {};
for (const pool_id in st.inode_stats||{})
{
diff.inode_stats[pool_id] = {};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor",
"version": "3.0.8",
"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.8'
VITASTOR_VERSION = '3.0.12'
LOG = logging.getLogger(__name__)
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.8
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.8.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.8
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.8.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.8
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.8.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.8
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.8.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.8")
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})
+5
View File
@@ -228,4 +228,9 @@ public:
virtual uint64_t get_journal_size() = 0;
virtual uint32_t get_bitmap_granularity() = 0;
virtual uint64_t get_live_entries() = 0;
virtual uint64_t get_live_memory() = 0;
virtual uint64_t get_garbage_entries() = 0;
virtual uint64_t get_garbage_memory() = 0;
};
+3
View File
@@ -94,6 +94,9 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
csum_block_size = parse_size(config["csum_block_size"]);
discard_on_start = config.find("discard_on_start") != config.end() &&
(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;
+4
View File
@@ -57,6 +57,10 @@ struct blockstore_disk_t
bool inmemory_journal = true;
// Data discard granularity and minimum size (for the sake of performance)
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;
File diff suppressed because it is too large Load Diff
+41 -9
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);
@@ -117,10 +117,13 @@ struct heap_object_mvcc_t
struct heap_block_info_t
{
uint32_t used_space = 0;
struct __attribute__((__packed__))
{
uint32_t used_space = 0;
uint32_t garbage_space = 0;
};
uint64_t mod_lsn = 0, mod_lsn_to = 0; // only 1 block write of LSN sequence is allowed at a moment
bool is_writing: 1;
bool has_garbage: 1;
bool is_writing = false;
std::vector<heap_list_item_t*> entries;
};
@@ -155,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,
@@ -184,6 +197,11 @@ class blockstore_heap_t
uint64_t buffer_area_used_space = 0;
uint64_t data_used_space = 0;
uint64_t live_entries = 0;
uint64_t live_memory = 0;
uint64_t garbage_entries = 0;
uint64_t garbage_memory = 0;
uint64_t next_lsn = 0;
uint32_t last_allocated_block = UINT32_MAX;
heap_mvcc_map_t object_mvcc;
@@ -200,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;
@@ -211,14 +231,22 @@ 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);
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
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);
@@ -345,6 +373,10 @@ public:
uint32_t get_compact_queue_size();
uint32_t get_to_compact_count();
uint64_t get_compacted_count();
uint64_t get_live_entries();
uint64_t get_live_memory();
uint64_t get_garbage_entries();
uint64_t get_garbage_memory();
uint64_t entry_pos(uint32_t block_num, uint32_t offset);
heap_entry_t *entry_from_pos(uint64_t entry_pos, bool allow_unallocated = false);
+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)
{
+5
View File
@@ -229,4 +229,9 @@ public:
uint64_t get_free_block_count();
inline uint32_t get_bitmap_granularity() { return dsk.bitmap_granularity; }
inline uint64_t get_journal_size() { return dsk.journal_len; }
inline uint64_t get_live_entries() { return heap->get_live_entries(); }
inline uint64_t get_live_memory() { return heap->get_live_memory(); }
inline uint64_t get_garbage_entries() { return heap->get_garbage_entries(); }
inline uint64_t get_garbage_memory() { return heap->get_garbage_memory(); }
};
+35 -18
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)
@@ -240,23 +248,7 @@ resume_4:
}
// metadata read finished
bs->heap->finish_load();
printf("Metadata entries loaded: %ju, used blocks: %ju / %ju\n", entries_loaded, bs->heap->get_data_used_space() / bs->dsk.data_block_size, bs->dsk.block_count);
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;
}
}
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
// asynchronous recheck
resume_6:
wait_state = 6;
@@ -293,6 +285,11 @@ resume_7:
if (bs->readonly)
{
recheck_mod.clear();
printf("Actual metadata entries: %ju\n", bs->heap->get_live_entries());
}
else
{
printf("Actual metadata entries: %ju, clearing garbage in %zu metadata blocks\n", bs->heap->get_live_entries(), recheck_mod.size());
}
for (i = 0; i < recheck_mod.size(); i++)
{
@@ -306,7 +303,7 @@ resume_8:
uint32_t block_num = recheck_mod[i];
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data)
@@ -332,5 +329,25 @@ 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(),
format_size(bs->dsk.block_count * bs->dsk.data_block_size).c_str());
return 0;
}
+4
View File
@@ -462,6 +462,10 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
{
if (target_version >= wr->version)
{
if (wr->type() == BS_HEAP_DELETE)
{
return false;
}
found = true;
if (result_version)
{
+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
);
+25
View File
@@ -855,4 +855,29 @@ std::string blockstore_impl_t::get_op_diag(blockstore_op_t *op)
return std::string(buf);
}
uint64_t blockstore_impl_t::get_live_entries()
{
return used_blocks;
}
uint64_t blockstore_impl_t::get_live_memory()
{
uint64_t used = 0;
for (auto & kv: clean_db_shards)
{
used += kv.second.size() * sizeof(blockstore_clean_db_t::value_type);
}
return used;
}
uint64_t blockstore_impl_t::get_garbage_entries()
{
return dirty_db.size();
}
uint64_t blockstore_impl_t::get_garbage_memory()
{
return (sizeof(obj_ver_id) + sizeof(dirty_entry) + 32) * dirty_db.size();
}
} // namespace v1
+4
View File
@@ -332,6 +332,10 @@ public:
inline uint64_t get_free_block_count() { return dsk.block_count - used_blocks; }
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
inline uint64_t get_journal_size() { return dsk.journal_len; }
uint64_t get_live_entries();
uint64_t get_live_memory();
uint64_t get_garbage_entries();
uint64_t get_garbage_memory();
};
} // namespace v1
+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)
+9 -9
View File
@@ -590,7 +590,7 @@ void cluster_client_t::on_change_pool_config_hook()
{
if (log_level > 2 && pg_counts[pool_item.first])
{
printf("Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
fprintf(stderr, "Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
pg_counts[pool_item.first], pool_item.second.real_pg_count);
}
// At this point, all pool operations should have been suspended
@@ -1119,6 +1119,13 @@ resume_2:
// Finished successfully
// Even if the PG count has changed in meanwhile we treat it as success
// because if some operations were invalid for the new PG count we'd get errors
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
// Copy part bitmaps only after finishing all part reads
for (auto & part: op->parts)
if ((part.flags & (PART_SENT|PART_DONE|PART_VALID)) == (PART_SENT|PART_DONE|PART_VALID))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
// Check parent inode
@@ -1164,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)
@@ -1630,13 +1637,6 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
}
if (op->inflight_count == 0 && !op->retry_after)
{
// Copy part bitmaps only after finishing all part reads
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
{
for (auto & part: op->parts)
if (part.flags == (PART_SENT|PART_VALID|PART_DONE))
copy_part_bitmap(op, &part);
}
if (op->opcode == OSD_OP_SYNC)
continue_sync(op);
else
+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;
+31 -2
View File
@@ -187,6 +187,8 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
ibv_device **raw_dev_list = NULL;
ibv_device **dev_list = NULL;
ibv_device *single_list[2] = {};
int up_ports = 0;
int single_port_num = 0;
raw_dev_list = dev_list = ibv_get_device_list(NULL);
if (!dev_list || !*dev_list)
@@ -221,6 +223,7 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
dev_list = single_list;
}
retry:
for (int i = 0; dev_list[i]; ++i)
{
auto dev = dev_list[i];
@@ -258,6 +261,9 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index);
continue;
}
up_ports++;
single_port_num = port_num;
single_list[0] = dev;
uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu);
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
if (sel_gid_index < 0)
@@ -298,6 +304,14 @@ cleanup_dev:
ibv_close_device(context);
}
if (!ret.size() && up_ports == 1 && dev_list != single_list)
{
// Auto-select the only available device/port if there is only one
dev_list = single_list;
sel_port_num = single_port_num;
goto retry;
}
cleanup:
if (raw_dev_list)
ibv_free_device_list(raw_dev_list);
@@ -598,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);
@@ -608,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,
@@ -778,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.8
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;
};
}
+2
View File
@@ -286,6 +286,8 @@ struct rm_inode_t
.data = data,
};
}
// Wakeup callers (otherwise they wake up only on next loop())
parent->ringloop->wakeup();
}
in_continue = 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)
+2 -1
View File
@@ -121,6 +121,7 @@ static const char* help_text =
" --logfile <FILE> log to the specified file\n"
" --enforce 1 enforce permissions at the server side (default is disabled)\n"
" --foreground 1 stay in foreground, do not daemonize\n"
" --trace trace all NFS requests\n"
"\n"
"NFS proxy is stateless if you use immediate_commit=all in your cluster and if\n"
"you do not use client_enable_writeback=true, so you can freely use multiple\n"
@@ -158,7 +159,7 @@ json11::Json::object nfs_proxy_t::parse_args(int narg, const char *args[])
{
const char *opt = args[i]+2;
cfg[str_replace(opt, "-", "_")] = !strcmp(opt, "json") || !strcmp(opt, "block") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") || !strcmp(opt, "trace") ||
!strcmp(opt, "include-empty") || !strcmp(opt, "no-rm") || i == narg-1 ? "1" : args[++i];
}
else
+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)
+8 -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);
});
@@ -529,6 +525,11 @@ void osd_t::exec_op(osd_op_t *cur_op)
void osd_t::print_stats()
{
if (bs && log_level > 1)
{
printf("[OSD %ju] Live entries: %ju (%ju bytes), garbage entries: %ju (%ju bytes)\n", osd_num,
bs->get_live_entries(), bs->get_live_memory(), bs->get_garbage_entries(), bs->get_garbage_memory());
}
for (int i = OSD_OP_MIN; i <= OSD_OP_MAX; i++)
{
if (msgr.stats.op_stat_count[i] != prev_stats.op_stat_count[i] && i != OSD_OP_PING)
+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 });
+9 -15
View File
@@ -82,6 +82,7 @@ int osd_t::read_bitmaps(osd_op_t *cur_op, pg_t *pg, int base_state)
goto resume_0;
else if (op_data->st == base_state+1)
goto resume_1;
assert(op_data->st < base_state);
if (!pg || pg->state == PG_ACTIVE && pg->scheme == POOL_SCHEME_REPLICATED)
{
// Happy path for clean replicated PGs (all bitmaps are available locally)
@@ -112,6 +113,12 @@ resume_0:
return 1;
}
resume_1:
if (op_data->errors > 0)
{
// Failure
finish_op(cur_op, op_data->errcode);
return -1;
}
if (pg->scheme != POOL_SCHEME_REPLICATED)
{
for (int chain_num = 0; chain_num < op_data->chain_size; chain_num++)
@@ -183,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;
@@ -312,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;
@@ -607,7 +601,7 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
{
if (cur > prev)
{
// Send buffer in parts to avoid copying
// Send buffer in parts to avoid copying
if (!prev_set)
{
while ((cur-prev) > zero_buffer_size/bs_bitmap_granularity)
+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);

Some files were not shown because too many files have changed in this diff Show More