본문으로 건너뛰기

uring_cmd recovery batched — future work

perf/iouring-recovery-batched-read(커밋 87638cb)에서 uring_cmd batched는 제거하고 io_uring(block)+POSIX만 남겼다. uring_cmd recovery 활성화는 아래 선행 버그 수정이 필요하다.

호칭 정정: uring_cmd(passthrough)는 #3274로 dev에 이미 머지됨(7021790). 따라서 이 버그는 "#3274 영역"이 아니라 현재 dev 코드의 passthrough read 경로(read_uring / _read_uring_cmd_buffers) 문제다.

증상 (실 NVMe, 2026-06-23 · 524,272 entries · /dev/nvme6n1 prepare ↔ /dev/ng6n1 measure)

  • posix/io_uring(block): 정상 indexed=524272 (io_uring 11.6s, serial 대비 6.08×).
  • io_uring_cmd(char): indexed=0. 로그:
    _load_meta_payload read failed at offset 4096: [Errno 22] io_uring I/O error
    _load_meta_payload read failed at offset 134221824: [Errno 22] io_uring I/O error
    no valid on-device metadata checkpoint found

원인 (확정 단서 + 유력 가설)

  • meta header(offset 0, 4096B=1페이지) read는 성공(magic 읽힘). meta payload(offset 4096+, ~75MiB를 max_data_transfer_size=2MiB chunk로 read)에서 EINVAL. → device_path 검증(P1)이 아니라 payload read 자체 실패.
  • 유력 원인: _read_uring_cmd_buffers의 버퍼는 비정렬 bytearray인데, uring_cmd는 use_odirect=falseread_uring이 non-bounce로 비정렬 ptr을 그대로 NVMe DMA addr로 전달. passthrough는 multi-page 전송 시 PRP 페이지 정렬을 요구 → 2MiB(512페이지) 비정렬 시작이 PRP 위반 → EINVAL. header(단일 페이지=PRP 1개)는 통과해서 그동안 안 드러남.
  • 정적 코드 분석상 커맨드 구성(nvme_uring_cmd_prep slba/nlb/opcode/addr/nsid)·NvmeUringCmd 레이아웃·lba_shift 출처는 모두 정상.

확정 검증 (다른 PC, 실 NVMe)

  • chunk를 4096(단일 페이지)으로 축소 시 EINVAL 사라지면 = multi-page 문제.
  • 또는 버퍼를 페이지 정렬 시 사라지면 = 정렬 문제. (둘 다 뿌리는 "정렬 bounce 없음")

수정 방향 (Rust, 정렬 bounce)

  • Rust read_uring/batched_read의 정렬 판정에 use_uring_cmd 포함 → 비정렬 버퍼를 페이지 정렬 AlignedBuf bounce로 read + copy-back(기존 bounce 경로 재사용). 정렬 bounce면 2MiB multi-page PRP도 정상 → chunk 축소 불필요.
    let needs_align = self.use_odirect || self.use_uring_cmd;
    let ptr_aligned = if needs_align { (ptr as usize).is_multiple_of(align) } else { true };
    let use_bounce = !ptr_aligned || cap < total_len;
  • 영향 범위: load_many_into/_read_buffers모든 uring_cmd read의 multi-page payload가 동일 버그. recovery만의 문제 아님 → passthrough read 전반 수정 + 별도 이슈/Rust PR(CODEOWNER @DongDongJu 영역)로 격상.

기존 fix 브랜치 검토 — priv/sy/fix/raw-block-uring-cmd-aligned-buffers (커밋 a340ab7)

결론: 이 브랜치만으로는 recovery 버그 해결 안 됨 (부분 fix).

  • 브랜치 내용: _allocate_aligned_buffer() 추가 후, _read_uring_cmd_buffers/_write_uring_cmd_bufferspadding 경로(len(dst) < total_len)에서 만드는 임시 버퍼만 block_align 정렬로 교체.
    # _read_uring_cmd_buffers (core.py:1227~)
    if len(dst) < total_len: # padding 필요할 때만
    target = self._allocate_aligned_buffer(total_len) # 정렬 ✅ (fix가 바꾼 곳)
    copy_back = True
    else:
    target = dst[:total_len] # 버퍼 충분하면 호출자 버퍼 그대로 (정렬 안 함 ❌)
    copy_back = False
  • recovery는 이 분기를 안 탐: _load_meta_payload(core.py:1539)가 buf = bytearray(total_len)정확히 total_len 크기 버퍼를 할당 → len(dst) < total_len이 거짓 → else 분기 → 정렬 안 된 bytearray를 그대로 DMA 대상으로 전달 → 비정렬 multi-page PRP 위반 → EINVAL 그대로.
  • 즉 a340ab7은 "호출자 버퍼 < total_len(=padding 필요)" 케이스만 커버. recovery처럼 정확한 크기의 비정렬 버퍼를 직접 넘기는 경로는 미커버.
  • → 보강 옵션: (a) Python else 분기도 "ptr 비정렬이면 aligned target bounce + copy_back"으로 확장, 또는 (b) 위 "수정 방향"대로 Rust read_uring에서 비정렬 ptr 무조건 bounce. 호출자별 중복을 피하려면 (b)가 깔끔(모든 호출자 일괄 해결). a340ab7은 (b)와 별개로 padding 경로 정렬을 보장하므로 병행 가치는 있으나 recovery 재활성화의 선행조건은 (b).

재활성화 (Rust 수정·실 NVMe 검증 후)

  1. core dispatch _read_slot_headers에서 not self.use_uring_cmd 제거(1줄) → uring_cmd도 _read_slot_headers_batched로.
  2. 테스트 test_validate_loaded_entries_uring_cmd_uses_sequential_uring_cmd_uses_batched_read.
  3. 벤치 --io-engine io_uring_cmd + --cmd-device-path(measure char) + payload device_path omit 재도입(block prepare/char measure path 불일치 우회). README io_uring_cmd 블록 복원.
  4. 실 NVMe char device에서 indexed=N 확인.

위 1~3의 제거 시점 구현은 git 이력(이 커밋 직전)과 plan shimmying-squishing-bumblebee.md F4에 보존됨.