마지막 제출을 앞두고 며칠간 열심히 달려서 얼추 마무리가 되었다.

알고리즘 개발 후 테스트를 완료하고 보고서 작성, 포스터 제작까지 끝났다.

이제 과제 소개 영상만 만들어서 제출하면 된다.

 

장장 7개월간 리눅스에 거의 파묻혀 살았는데, 예상했던 것보다 훨씬 힘들었다.

알고리즘 개발보다 리눅스 공부하고 환경 세팅하는 데 시간이 많이 소요되었다.

그래도 정말 많은 걸 얻어간 과제였다고 생각한다.

이젠 리눅스 환경은 거의 윈도우 수준으로 익숙해진 느낌이다.

정말 뛰어난 팀원들과 함께하면서 공부도 많이 했고, 도움도 많이 받았다.

이 정도면 잘 마쳤다고 생각하고, 10월 말에 있을 시연만 끝내면 졸업 과제도 끝이다!

계속 자료 조사하고 소스 코드 컴파일하다 보니 순식간에 시간이 지나갔다.

사실 Device Mapper 연동하고 나서는 계속 코드 분석하고 조금씩 수정해 나가는 과정이었다.

Device Mapper 관련된 코드가 있는 Linux/drivers/md 폴더 내의 파일을 이용한다.

주로 건드리는 파일은 구조체들이 선언되어 있는 dm-zoned.h, zone을 매핑하는 함수들이 있는 dm-zoned-target.c, 그리고 메타데이터 관련 함수들이 있는 dm-zoned-metadata.c이다.

 

어떻게 해야 Linux Container별로 입출력을 분리할 수 있을까? 하고 생각해 본 결과, Cgroup은 컨테이너별로 생성된다는 사실을 이용해 각 컨테이너들의 Cgroup이 저장된 주소를 일종의 Key로 사용할 수 있다는 결론을 내렸다. 물론 이 결론이 나기까지 거의 한 달 동안 코드와 구조체를 뒤져본 것 같다. 똑같은 페이지를 수백 번 왔다갔다해서 외울 지경이다.

아무튼, 원래 있던 구조체에 원소를 추가하면 그 구조체를 사용하는 커널의 다른 요소까지 건드릴 것 같아서 최대한 기존에 있는(전달받는) 파라미터 내의 구조체 속에서 정보를 찾아오려 했다. 아무래도 입출력에 관련된 함수들이다 보니 bio 구조체는 기본으로 전달받는 경우가 많았다. bio 구조체(blk_types.h 안에 있다) 내의 bi_blkg(struct blkcg_gq) 안의 blkcg(struct blkcg) 안의 css(struct cgroup_subsys_state)안에 있는 id와 cgroup 주소를 사용하기로 했고, 이 값을 저장하기 위해 dm-zoned.h 안의 zone 구조체에 id와 cgroup 포인터 변수를 추가해 받아왔다. 자꾸 Null값이 들어가는지 터지는 경우가 있어 예외 처리를 해 주었다.

 

현재는 받아온 cgroup 정보(id / address)를 어떻게 key값으로 만들어서 분리 할당해야 하는지를 고민중이다. Cgroup별로 zone을 할당하거나, Zone을 확인하여 이미 사용하는 Cgroup이 있으면 새로 zone을 할당하는 선택지가 있었다. 아마 후자로 진행할 듯 싶다.

 

공부한 건 엄청 많은데... 한 줄 고치고 컴파일하고, 에러 원인 찾으려고 계속 printk 이용해서 커널 메시지 찍어보고 구글링하는 과정의 연속이어서 그런지 아직까진 가시적인 결과가 안 나온다. 이제 시간이 얼마 남지 않았으니 노력한 만큼 결과를 내기 위해 열심히 머리 굴려 봐야겠다.

 

 

'ZNS Isolation with Linux Container' 카테고리의 다른 글

마무리  (0) 2022.10.09
ZNS SSD 논문 번역  (0) 2022.07.16
Uftrace 사용법  (0) 2022.07.10
ZNS SSD에 관한 공부와 Zonefs 파일시스템 마운트 방법  (0) 2022.07.10
Linux Kernel Code Compile  (0) 2022.06.24

해당 논문의 번역은 개인적인 학습을 위해 진행된 것이므로 영문 원본과 비교하며 읽는 것을 추천함.

1. https://www.usenix.org/system/files/osdi21-han.pdf

  NVMe ZNS는 고정 사이즈의 Zone으로 나뉜 논리 주소 공간 인터페이스이다. 각 Zone은 순차적 쓰기를 필요로 하며 재사용을 위해서는 명시적으로 리셋되어야 한다. 첫째로, 서로 다른 I/O Stream간의 성능 독립이 이루어진다. 이는 각 I/O Stream에 독립된 Zone을 할당함으로써 이루어지며, multi-tenant(단일 소프트웨어 인스턴스로 서로 다른 여러 사용자 그룹에 서비스를 제공할 수 있는 소프트웨어 아키텍처) 시스템에 사용하기 좋다. 둘째로, 만약 zone 크기가 flash erase block size의 배수가 된다면 ZNS SSD는 zone 단위의 논리-물리적 주소 매핑이 가능하다.(Zone과 Flash Block간의 매핑) Coarse-grained(덩어리 작업) 매핑은 SSD의 작은 내부 DRAM을 필요로 한다. 일반적인 SSD와 비교해서 DRAM은 0.1%의 저장 공간만을 필요로 한다. Fine-grained(쪼개진 작업)의 경우 ZNS SSD 작업에 사용되는  DRAM의 크기는 현저하게 줄어든다. 보통 zone에 매핑된 flash block들은 zone reset시 완전히 비활성화되기 때문에 SSD 내부의 Garbage collection은 필요하지 않다. 

I/O Stack for ZNS

  보통 새로운 저장소의 인터페이스는 소프트웨어 스택의 개조를 필요로 한다. ZNS의 경우 가장 메이저한 두 가지의 I/O stack component를 수정해야 한다. File system과 I/O scheduler이다. 첫째로, EXT4같은 In-place updating file system들은 LFS(Log-structured file system)과 같은 Logging file system을 추가하여 random update를 제거하도록 수정되어야 한다. LFS에 Logging을 추가하여 순차적으로 쓰여지게 하면 각 segment는 하나 또는 그 이상의 zone에 매핑될 수 있다.둘째로, I/O scheduler는 zone을 위해 in-order write request delivery를 수행해야 한다. 예를 들어 각각의 zone에는 in-order queue가 사용될 수 있고, 스케쥴러는 각자 다른 존들에 제공할 서비스의 순서만을 결정하면 된다.

Increased Host Overhead

  LFS에 logging scheme(로그 기록 스키마)을 추가하면 dirty block들은 Garbage collection(=segment compaction)에 의해 수거되어야 하며, 모든 유효한 데이터는 다른 segment로 이동하여 해당 segment는 깨끗해진다. 이러한 작업은 특히 시스템 이용량이 많을 때 수행되는 복사 명령을 증가시킨다. 비록 Log-on-log 상황의 중복 GC(Garbage collection)이 회피되더라도, Host-side의 GC는 ZNS SSD 상의 GC를 적게 사용하는 것에 대한 교환(대가)으로 수행되어야 한다. Host-Side의 GC는 I/O request handling, host-to-device data transfer, pace alloctaion for read data등의 역할을 구행하기 때문에 Device-side의 GC에 비해 overhead가 크다. 추가로 segment-compaction은 Data relocation을 위해 파일 시스템 메타데이터를 조정할 필요가 있다. 더구나 Segment compaction을 위한 데이터 복사 명령은 일괄적으로 수행되기 때문에 대기하고 있는 write 요청의 평균 대기 시간이 중요해진다. F2FS의 실험에 따르면 - F2FS는 가장 많이 사용되는 log-structured 파일 시스템이다-파일 시스템의 사용량이 90%일 때 segment compaction에 의한 성능 저하는 약 20%였다. 따라서 현재의 ZNS는 host의 복잡성이 증가하는 것을 고려하지 않고 SSD 측의 이익에 집중한다고 볼 수 있다. SSD의 설계를 단순화하기 위해서는 복잡한 것들을 host 측으로 넘겨야 한다. (그럼에도 불구하고 Host는 ZNS를 통해 성능 격리와 예측을 통한 이익을 얻을 수 있다.)

  추가로, ZNS 저장소는 더 많은 수의 flash chip을 내장하여 SSD의 bandwidth를 늘림에 따라 diminishing return을 야기한다. ZNS 기기의 zone 크기는 SSD 내부의 flash chip의 병렬성을 이용할 수 있을 정도로 크게 결정된다. 따라서 bandwidth가 높은 ZNS SSD는 더 큰 zone size를 제공할 것이고, 파일 시스템은 이에 따라 더 큰 segment size를 사용한다. 그러면 host는 커진 segment size에 따라 일반적으로 같은 비율로 증가한 compaction overhead 때문에 고통받는다. IO 성능을 높이고 diminishing return을 극복하려면 host와 device 간의 상호 디자인이 필요하고, 이는 단순히 SSD의 GC를 host로 옮기는 대신 오리지널 ZNS의 이점을 손상시키지 않으면서 가장 적절한 장소에서 segment compaction이 일어나도록 한다. 

LFS-aware ZNS

  우리는 LFS의 segment compaction overhead를 극복하기 위해 device level의 지원이 필요하다. 두 가지 접근법이 고려될 수 있다: compaction acceleration과 compaction avoidance가 그것이다. 우리는 새로운 LFS-aware ZNS interface인 ZNS+를 정의하고, 이에 대한 구현으로 Internal Zone Compaction(IZC)와 sparse-sequential overwrite를 지원하는 zone_compaction과 TL_open이라는 두 명령어를 구현한다. Segment compaction은 네 가지의 서브태스크를 필요로 한다: victim segment selection, destination block allocation, valid data copy, and metedata update이다. 다른 것들이 host file system에 의해서 수행되지만 data copy의 경우 SSD가 맡는 편이 낫다. 왜냐하면 device-side data copy가 host-side copy보다 빠르기 때문이다. compaction accleration을 위해 ZNS+는 host가 zone_compaction 명령을 통해 SSD에게 data copy 명령을 내릴 수 있도록 한다.

  Segment compaction을 회피하기 위해 LFS는 스레드 로깅이라는 대체 회수 체계를 활용할 수 있다. 스레드 로깅은 새 데이터를 덮어씀으로써 기존 dirty segment에서 유효하지 않은 공간을 회수한다. 클리닝 작업을 필요로 하지 않지만 segment에 대한 random overwrite를 생성한다. 일반 SSD를 사용한 F2FS 실험에서 스레드 로깅은 세그먼트 압축보다 쓰기 트래픽이 작고 성능이 더 높은 것으로 나타났다. 그러나 스레드 로깅은 랜덤 쓰기 때문에 순차적 쓰기 전용 ZNS 인터페이스와 호환되지 않는다. 따라서현재 F2FS는 ZNS에 대해 스레드 로깅을 사용하지 않도록 설정하고 있다. 이 문서에서는 segment recycling이라는 용어를 사용하여 세그먼트 압축과 스레드 로깅을 모두 다룰 것이다.

  ZNS+의 순차적 overwrite 인터페이스는 ZNS의 조밀한 순차 append write 제약의 완화된 버전이다. TL_open을 통해 열린 Zone의 경우 스레드 로깅에 대해 sparse sequential overwrite가 허용된다. ZNS+ SSD는 동일한 세그먼트에서 접근되지 않은 유효한 블록(internal plugging)사이의 요청을 막음으로써 sparse sequential write requests을 dense sequential requests으로 변환하고 병합된 요청을 새로 할당된 플래시 블록으로 리디렉션한다. IZC와 유사하게 internal plugging은 Host-side 작업을 포함하지 않고 세그먼트의 유효한 데이터를 내부적으로 복사한다. internal plugging에 대한 쓰기 패턴의 유일한 요구 사항은 연속 쓰기의 블록 주소가 오름차순으로 되어 있어야 한다는 것이다. internal plugging은 쓰기 요청 간에 처리되므로 batch-style segment compaction에 비해 쓰기 요청의 평균 응답 시간이 향상된다. ZNS+는 원래 ZNS+에 비해 상당한 확장 기능이 있지만 ZNS+ SSD는 작은 매핑 테이블, 중복 GC 없음, 과도하게 공급된 공간 없음, 성능 격리/예측 가능성과 같은 원래 ZNS SSD와 동일한 장점을 제공할 수 있다.

ZNS+-aware File System

  파일 시스템 역시 ZNS+의 새로운 기능을 활용하도록 조정되어야 한다. 첫째로, SSD 내부 데이터 복사 작업에서는 논리 블럭의 시작/도착 주소(LBA)에 따라 서로 다른 복사 경로를 사용한다. 예를 들어, 두 LBA가 동일한 플래시 칩에 매핑되어 있으면 플래시 메모리의 copyback 작업을 활용할 수 있으므로 off-chip 데이터 전송 없이 플래시 칩 내에서 데이터를 이동하여 데이터 이동 지연 시간을 줄일 수 있다. copyback 작업은 현재 표준 NAND 인터페이스에 있으며 SSD 내부 Garbage collection에서 유용하다는 것이 많은 연구에서 입증되었다. copyback 작업을 완전히 활용하기 위해, 우리는 세그먼트 압축에 대해 copyback-aware block allocation을 제안한다. 이는 데이터의 시작 주소와 목적지 주소가 같은 플래시 칩 내에 매핑되도록 목적지 주소를 할당한다. 이 기술은 다른 SSD의 빠른 복사 경로를 대상으로도 확장될 수 있다.

  두 번째로, ZNS+는 세그먼트 압축 가속과 스레드 로깅을 모두 지원하므로 호스트 파일 시스템은 하나의 세그먼트 재활용 정책을 선택해야 한다. 스레드 로깅은 세그먼트 압축 오버헤드를 피할 수 있지만 제3.3.2조에 설명된 바와 같이 몇 가지 단점이 있다. 따라서 우리는 스레드 로깅의 장단점을 모두 고려하여 ZNS+에 대한 hybrid 세그먼트 재활용 기술을 제안한다. ZNS+는 회수 비용과 이점을 기반으로 스레드 로깅 또는 세그먼트 압축을 선택한다.

  우리는 FEMU의 SSD 에뮬레이터와 OpenSSD 장치에 ZNS+ SSD를 구현했다. 실험에서 수정된 F2FS 및 ZNS+ SSD로 구성된 스토리지 시스템의 파일 시스템 성능은 기존 F2FS 및 ZNS SSD 기반 스토리지 시스템의 1.33–2.91배였다. 소스 코드는 https://github.com/eslab-skku/ZNSplus에 공개되어 있다. 

SSD Architecture

  현대의 SSD는 여러 개의 플래시 칩으로 구성되며 병렬 처리를 위해 다중 채널 및 다중 경로 구조를 채택한다.여러 개의 병렬 플래시 컨트롤러(채널)가 있으며, 각 컨트롤러는 교차 배치방식으로 여러 개의 플래시 칩에 액세스할 수 있다. 각 플래시 칩에는 여러 개의 flash erase block이 있으며 각 블록은 여러 개의 플래시 페이지로 구성된다. 플래시 블록은 지워지기 전까지 덮어씌워질 수 없다. 따라서 SSD는 플래시 변환 계층(FTL)이라는 특수한 펌웨어를 사용하여 호스트가 사용하는 논리적 주소를 플래시 메모리 칩 내의 위치를 나타내는 물리적 주소로 변환하는 논리적 매핑을 관리한다. 일반적으로 플래시 메모리 제조업체는 내부 셀 간 간섭으로 인해 플래시 블록 내부의 페이지를 페이지 번호 순서대로 프로그래밍할 것을 권장한다. 최근 플래시 제품의 플래시 페이지 크기는 일반인 논리적 블록 크기(예: 4KB)보다 크기 때문에 물리적 플래시 페이지에 여러 개의 논리적으로 연속된 블록의 집합이 기록된다. 이 연구에서 우리는 플래시 페이지에 매핑된 논리적 연속 블록들을 Chunk라 지칭한다.

  일반적으로 플래시 메모리 칩은 read, program, erase, copyback 명령을 지원한다. copyback 명령은 off-chip data transfer 없이 플래시 칩 내의 플래시 페이지 간에 데이터를 복사하는 데 사용된다. 청크는 copyback 작업의 기본 단위입니다. 칩 내부 데이터 전송은 대상 페이지의 ECC(오류 정정 코드)를 확인할 수 없기 때문에 bit error propagation 문제가 존재한다. 이 문제에 대처하기 위해 copyback 작업을 수행하는 동시에 플래시 컨트롤러에서 오류 검사를 수행할 수 있다. 오류가 감지되면 복사된 페이지가 비활성화되고 수정된 데이터가 플래시 컨트롤러에 의해 프로그래밍된다. 또 다른 솔루션은 플래시 칩의 copyback 오류 특성에 따라 결정되는 threshold copyback counts를 사용하여 제한된 수의 연속 copyback만 허용하는 것이다.

Zone Mapping in ZNS SSD

  현 NVMe 표준 인터페이스는 ZNS command와 zone type을 정의한다. 읽고 쓰기 명령과 함께 Zone open, close, finish, reset 등 다양한 zone 관리 명령어들이 존재한다. 논의 중인 중요한 명령인 simply copy는 host가 SSD에게 하나 또는 그 이상의 논리적 블록 범위로부터 단순한 연속적인 논리 블록 범위로 데이터를 내부 복사할 수 잇도록 하는 것이다. 이는 분명히 zone_compaction 명령어와 유사하지만, 현재 사용 가능한 copy 명령어와 관련된 논점은 없으며, 간단한 복사는 3절에서 설명하겠지만 ZNS+에는 적합하지 않다.

  ZNS 규격에는 Zone 매핑 제약 조건이 없다. 저장 장치 내의 Zone의 물리적 위치와 zone 내부의 chunk들은 host에게 투명하다. 장치 공급업체는 내부 설계 문제를 결정하는 서로 다른 매핑 정책을 선택할 수 있다. 우리는 필요한 매핑 정보의 크기를 최소화하고 플래시 칩 수준의 병렬화를 극대화할 수 있는 일반적이고 효율적인 SSD 내부 영역 매핑 정책을 소개한다. Zone의 크기에 따라 하나의 Zone은 하나 또는 그 이상의 물리적 플래시 블록에 매핑될 수 있으며, 이를 Zone에 매핑된 FBG(Flash Block Group)라고 한다. Zone크기는 부분적으로 유효한(일부만 쓰여진) 플래시 블록이 생성되지 않도록 플래시 블록 크기에 맞춰야 합니다. 플래시 동작 병렬 처리를 최대화하기 위해 병렬로 액세스할 수 있는 플래시 칩 세트의 플래시 블록은 Zone의 FBG를 구성하고 zone의 chunk들은 병렬 플래시 칩에 interleave하게 배치되어야 한다. Chunk Interleaving을 위한 병렬 플래시 칩의 수를 Zone interleaving degree Dzone이라고 하며, 병렬 플래시 칩 상의 논리적으로 연속적인 청크의 집합을 스트라이프라고 한다. Coarse-grained zone-to-FBG 매핑의 경우, FBG는 병렬 플래시 칩에서 동일한 block offset의 플래시 블록을 가지며 스트라이프의 청크는 다른 플래시 블록에서 동일한 page 오프셋에 위치한다.

 

- Dzone에 관한 수학적 내용은 번역하지 않았음 -

 

2.3 F2FS Segment Management

  이 연구에서 우리는 F2FS를 active하게 유지되는 LFS인 ZNS-aware file system으로 인식한다.

 F2FS는 그림 2와 같이 6가지 유형의 세그먼트(즉, 각 node와 data에 대한 hot, warm, 그리고 cold segment)를 사용하고 multihead logging policy을 사용한다. 각 유형에 대해서는 한 번에 단 하나의 node만 열릴 수 있다. 핫 데이터와 콜드 데이터를 서로 다른 segment로 분리하면 세그먼트 압축 비용을 줄일 수 있다. 노드 블록은 데이터 블록의 inode 또는 인덱스를 포함하는 반면 데이터 블록은 디렉터리 또는 사용자 파일 데이터를 포함한다. hot 그리고 warm 세그먼트의 콜드 블럭은 세그먼트 압축 중에 콜드 세그먼트로 이동합니다. F2FS는 append logging과 thread logging을 모두 지원한다. Append logging에서 블록은 clean 세그먼트에 기록되므로 엄격한 순차 쓰기가 발생한다. 반면, thread logging은 클리닝 작업 없이 기존 더티 세그먼트의 쓸모없는 공간에 블록을 쓴다.F2FS는 adaptive logging 정책을 사용한다. 사용 가능한 세그먼트의 수가 충분하면 먼저 append logging이 먼저 수행된다. 그러나 사용 가능한 세그먼트가 부족해지면 세그먼트 압축을 호출하는 대신 사용 가능한 세그먼트를 더 이상 사용하지 않도록 thread logging이 활성화된다. 그러나 현재 ZNS용 F2FS 패치에서는 thread logging이 사용되지 않으므로 세그먼트 압축은 F2FS for ZNS에서 자주 트리거된다. 세그먼트 압축과 관련하여 F2FS는 foreground 작업과 background 작업을 모두 지원한다. Foreground 압축은 들어오는 쓰기 요청을 처리할 수 있는 사용 가능한 세그먼트가 충분하지 않을 때 호출된다. 따라서 압축 중에 쓰기 요청이 지연된다. Background 압축은 파일 시스템이 유휴 상태이고 사용 가능한 세그먼트 수가 임계값 미만인 경우에만 트리거된다. 따라서 Background 압축으로 인한 IO 지연은 미미하다. 임계값은 압축이 자주 발생하지 않으므로 SSD의 수명을 해치지 않을 정도로 작게 구성되기에, 특히 파일 시스템 활용률이 높고 쓰기 폭주가 발생할 경우 Background 압축만으로 무효화된 공간을 제때 회수할 수 없다.
따라서 전체적인 IO 성능을 향상시키기 위해 Foreground 압축을 최적화하는 것이 중요하다. 본 논문에서는 Foreground 압축 성능에 중점을 둔다.

ZNS+ Interface and File System support

Motivation

Normal Segment Compaction

일반적인 LFS 세그먼트 압축의 전체 프로세스는 네 가지 작업으로 구성된다.
1. 타겟 세그먼트 선택(victim segment), 2. 대상 블록 할당, 3. 유효한 데이터 복사, 4.메타데이터 업데이트(그림 3(a)). 타겟 세그먼트 선택은 압축이 가장 낮은 세그먼트를 대상으로 이루어진다. 블록 할당은 대상 세그먼트로부터 연속적인 여유 공간을 할당한다. 데이터 복사 작업은 호스트 시작 읽기 및 쓰기 요청을 통해 타겟 세그먼트의 모든 유효한 데이터를 목적지 세그먼트로 이동하며, 이 경우 호스트와 스토리지 간에 상당한 데이터 전송 트래픽이 생성된다. 데이터 복사 작업에는 읽기 및 쓰기 단계가 있다.

Read Phase

  호스트는 Page cache에 캐시되지 않은 경우 victim segment의 유효한 블록에 대한 읽기 요청을 SSD로 보낸다. 읽기 요청을 전송하기 전에 해당 메모리 페이지를 페이지 캐시에 할당해야 하며, 페이지 프레임 회수를 위해 저장소에 쓰기 요청을 호출할 수 있다. 타겟 블록은 일반적으로 논리 주소 공간에 흩어져 있기 때문에 여러 개의 읽기 요청이 하나씩 전송된다. SSD는 여러 flash read 명령을 통해 읽기 요청에 대한 target 데이터를 읽는다. 이 작업은 서로 다른 플래시 칩에서 중복될 수 있다. 읽기 요청의 크기가 플래시 칩 병렬화에 비해 작으면 플래시 칩의 유휴 간격이 많을 것이다. 플래시 칩에서 읽은 데이터는 NVMe와 같은 스토리지 인터페이스를 통해 호스트로 전송된다. SSD 에뮬레이터를 사용한 실험에서 우리는 요청 제출/완료 처리와 장치 간 데이터 전송이 전체 읽기 지연 시간 중 각각 약 7%와 44%를 차지하는 것을 관찰했다. (상세 실험 환경은 4절에서)

Write Phase

  이 단계는 읽기 단계에서 실행된 모든 읽기 요청이 완료된 후에 시작할 수 있다. Append Logging Scheme에서 LFS는 목적지 segement에서 쓰기 작업을 위한 새로운 블럭들을 순차적으로 할당하기 때문에 파일 시스템은 요청 처리 overhead를 줄이기 위해 하나의 커다란 쓰기 요청을 만들 것이다. 따라서 파일 시스템은 모든 대상 블록이 페이지 캐시로 전송될 때까지 기다린다. 그 결과, 그림 3(a)과 같이 SSD의 유휴 간격이 커진다.

Metadata Update

  F2FS는 갑작스러운 충돌이 발생할 때 데이터를 최신 checkpoint 상태로 롤백하여 파일 시스템의 일관성을 유지할 수 있다. 파일 시스템은 데이터 위치의 변경을 반영하기 위해 수정된 여러 메타데이터 블록과 노드 블록을 저장소에 쓴 다음 체크포인트 블록(④)을 쓴다. 세그먼트 압축 전에 victim 세그먼트의 유효한 블록이 차지했던 스토리지 공간을 회수하려면 메타데이터를 영구적으로 수정해야 한다. 그렇지 않으면 회수된 공간에서 새 데이터를 덮어쓸 때 데이터 손실이 발생할 수 있다.

IZC-based Segment Compaction

그림 3(b)은 IZC 체계에 따른 압축 프로세스를 보여준다. 일반 세그먼트 압축의 데이터 복사 작업은 copy offloading로 대체되며, 이 작업은 블록 복사 정보(즉, 출발 및 목적지 LBA)를 전송하기 위해 zone_compaction 명령을 전송한다. 타겟 데이터가 Host page cache에 로드되지 않기 때문에 페이지 캐시 할당은 필요하지 않다. SSD 내부 컨트롤러는 플래시 칩 활용률을 극대화하면서 여러 읽기 및 쓰기 작업을 효율적으로 스케줄링할 수 있다. 따라서 세그먼트 압축 지연 시간을 크게 줄일 수 있다. 추가로, 스토리지 내 block copy는 copyback operation을 사용할 수 있다.

 

LFS-aware ZNS+ Interface

  표 1은 원래 ZNS와 제안된 ZNS+ 인터페이스를 비교한다. ZNS+는 zone_compaction, TL_open 및 identify_mapping의 세 가지 새 명령을 지원한다. zone_compact는 IZC 작업을 요청하는 데 사용된다. 비교를 위해, 현재 ZNS의 간단한 복사 명령은 단일 consecutive destination LBA 범위를 제공한다. 우리의 새로운 ZNS+ 인터페이스에서 목적지 주소 범위는 비연속적일 수 있으므로 zone_compaction 명령은 연속적인 블록 범위가 아닌 목적지 LBA를 지정하도록 설계되었다. TL_open은 threaded logging을 위한 zone을 open하는 데 사용됩니다. TL_open 영역은 reset 없이 덮어쓸 수 있으며 overwrite 요청은 sparse sequential일 수 있다. 호스트는 identify_mapping 명령을 사용하여 각 청크에 매핑된 플래시 칩을 결정하는 주소 비트 필드를 알 수 있다.

 

Internal Zone Compaction

ZNS+ 스토리지 시스템의 세그먼트 압축 프로세스는 다음과 같다.

Cached Page handling

  첫 번째 단계는 victim 세그먼트의 각 유효한 블록에 대해 해당 페이지가 호스트 DRAM에 캐시되고 있는지 확인하는 것이다. 캐시된 페이지가 더럽다면 목적지 세그먼트에 기록되어야 하며 IZC 작업에서 제외되어야 한다. 캐시된 페이지가 깨끗한 경우 쓰기 요청을 통해 작성되거나 zone_compact를 통해 내부적으로 복사될 수 있다. 만약 쓰기 요청을 통해 호스트에서 전송되는 경우 해당 플래시 읽기 작업을 건너뛸 수 있다. 이는 이미 캐시되어 있기 때문이며 페이지 할당이 필요하지 않다. 대신 데이터 전송 및 쓰기 요청 처리 오버헤드가 생긴다. 플래시 읽기 비용과 데이터 전송 비용을 비교함으로써 캐시된 블록을 재배치할 적절한 방식을 선택할 수 있다. 일반적으로 TLC 또는 QLC 기술을 기반으로 하는 고밀도 플래시 메모리는 호스트-스토리지 데이터 전송 비용보다 액세스 대기 시간이 상대적으로 높다. 그러나 최근 ZNAND[11]는 읽기 지연 시간이 매우 짧기 때문에 ZNAND SSD의 캐시된 블록에 대해 in-storage copy를 사용하는 것이 좋다.

Copy Offloading

  둘째, 데이터 복사 작업을 ZNS+ SSD로 offload하기 위해 zone_compaction(소스 LBA, 대상 LBA) 명령이 생성된다. i번째 source LBA의 데이터는 ZNS+ SSD에 의해 i번째 destination LBA로 복사된다. F2FS에서 스레드 로깅이 활성화된 경우, 세그먼트 압축은 hole-plugging와 마찬가지로 TL_open 세그먼트를 대상으로 선택할 수 있다. 이 경우 목적지 LBA들은 비연속적일 수 있다.

Processing IZC

  마지막으로 ZNS+ SSD는 zone_compaction 명령을 처리한다. 소스 및 목적지 LBA의 매핑된 플래시 칩을 확인하여 copyback 명령으로 복사할 수 있는 copybackable한 청크를 식별한다. 청크의 모든 블록을 복사해야 하는 경우에만 이 청크는 copybackable하다. SSD 펌웨어는 non-copybackable 청크에 대해 플래시 읽기 및 쓰기 작업을 실행한다.

Async Interface and Request Scheduling

  zone_compaction 명령의 처리는 비동기적이다. 호스트에서 실행한 압축 명령은 명령 대기열로 대기열에 저장되고 호스트는 명령 완료를 기다리지 않는다. 따라서 사전 실행된 zone 압축이 완료되기 전에 호스트가 다음 IO 요청을 즉시 실행할 수 있다. 비동기 처리 기능은 다음 요청의 대기 시간을 제거하여 성능을 향상시킬 수 있다. LFS의 checkpoint scheme 덕분에 비동기 명령어 처리는 파일 시스템의 일관성을 해치지 않는다.

  비동기 인터페이스에서는 Zone 압축을 처리하는 동안 보류 중인 일반 요청이 여러 개 있다. ZNS+ SSD는 Zone 압축의 긴 대기 시간으로 인한 convoy effect를 피하기 위해 일반 요청을 다시 정렬할 수 있다. 정상 요청의 taget zone이 사전에 도착한 zone 압축 요청과 무관할 경우 zone 압축 완료 전에 처리할 수 있다. 진행 중인 zone 압축의 destination zone에 대한 읽기 요청의 경우에도 zone의 WP가 읽기 요청의 target 블록 주소를 통과했다면 읽기 요청을 처리할 수 있다.

 

3.2.2. Sparse Sequential Overwrite

Internal Plugging

  스레드 로깅을 지원하기 위해 ZNS+는 sparse sequential overwrite를 지원한다. 스레드 로깅의 쓰기 패턴은 ZNS와 호환되지 않지만 한 가지 다행스러운 점이 있다; 스레드 로깅은 블록의 낮은 주소를 먼저 소비하기 때문에 블록 주소의 증가 순서로 더티 세그먼트의 빈 공간에 액세스한다. 따라서 액세스 패턴은 sparse sequential하다(즉, Zone의 WP는 감소하지 않을 것이다). 스레드 로깅은 세그먼트를 덮어쓰지만 SSD 펌웨어가 요청 사이의 skipped block을 읽고 호스트가 보낸 데이터 블록에 병합하면 taget zone에 조밀한 순차 쓰기 요청을 할 수 있다. 이 기술을 내부 플러그라고 한다. 연결 작업은 SSD 내부 작업이기 때문에 지연 시간이 호스트 수준 복사 지연 시간보다 짧다. 또한 SSD는 병렬 플래시 칩 간에 plugging 명령들을 효율적으로 스케줄링하여 지연 시간을 숨길 수 있다.

  그림 4는 내부 플러그의 예를 보여준다. zone-to-FBG Mapping에서 보이듯 세그먼트 1은 이제 파일 시스템의 Zone 1에 매핑되고 FBG 6은 Zone 1에 할당된다. FBG는 각각 다른 플래시 칩에 있는 4개의 플래시 블록으로 구성됩니다. 호스트 파일 시스템은 세그먼트 1을 thread logging에 할당하고 쓰기 요청을 invalid block에 보내 valid한 block들을 건너뛰면서 회수한다. 예를 들어 청크 0에 있는 A와 B의 블록은 건너뛴 블록이다.

Opening Zone for Threaded Logging

  Interner plugging의 경우, SSD는 스레드 로깅의 target 세그먼트에서 건너뛴 블록을 인식해야 한다. ZNS IO 스택의 순차적 요청 전달로 인해 SSD는 들어오는 쓰기 요청의 시작 LBA와 해당 영역의 현재 WP를 비교하여 건너뛴 블록을 식별할 수 있다. 그러나 쓰기 요청이 도착한 후에만 이전에 건너뛴 블록을 인식할 수 있다. 따라서 plugging operation으로 인해 쓰기 요청 처리가 지연된다. 이 문제를 해결하기 위해, 우리는 TL_open(오픈 존, 유효한 비트맵)이라는 특수 명령을 추가했는데, 이 명령은 스레드 로깅을 위해 target zone의 유효한 bitmap을 제공한다. 할당된 세그먼트가 TL_open을 통해 알려지면 스레드 로깅은 전송되어진 bitmap에 표시된 invalid한 블록만 회수한다. 따라서 SSD는 스레드 로깅으로 건너뛸 블록을 미리 식별하고 다음 쓰기 요청이 도착하기 전에 plugging을 시행할 수 있다.

 

- 이 이후 영역부터는 일반적인 ZNS SSD가 아니라 해당 연구자들이 만들어낸 ZNS+ SSD에 관련된 내용인 것 같아서 추후 참고하면 될 것 같음 -

우리가 이 과제를 수행하면서 가장 많이 사용하는 명령어는 Uftrace일 것이다.

Uftrace는 리눅스에서 기본으로 지원하는 함수 추적 툴인 Ftrace를 한 유저분께서 변형한 것이다.

https://github.com/namhyung/uftrace

 

GitHub - namhyung/uftrace: Function graph tracer for C/C++/Rust

Function graph tracer for C/C++/Rust. Contribute to namhyung/uftrace development by creating an account on GitHub.

github.com

 

 

함수 추적

$ sudo uftrace record [options] [command]

우리가 사용하는 옵션들 외 일부

-a: show arguments and return value of known functions

-A: show function arguments

-f: show FIELDs in the replay or graph output

-F: Only trace those FUNCs

-k: Trace kernel functions also(if supported)

-K: Trace kernel functions within DEPTH

-N: Don't trace those FUNCs

-R: Show function return values

-t: Hide small functions run less than the TIME

-v: Print debug messages

 

추적 결과 출력

$ ufrace replay [options]

원한다면 tui를 이용하는 등 다양한 방법으로 볼 수 있다. 나는 주로 파이프라이닝을 통해 결과를 텍스트 파일로 저장하여 분석한다.

 

 

Cscope와 Ctags에 대한 사용법을 배우고 1차 목표를 설정하였다. 제법 찾아본 건 많은데 실질적으로 진행한 건 얼마 되지 않는 것 같다.

 

Cscope 사용법

1. Cscope를 실행할 디렉토리에서 vi를 실행한다. 이 때 cscope는 해당 디렉토리 아래의 모든 디렉토리 안의 파일을 탐색한다.

2. cscope.out 파일을 생성한다.

: cscope -Rb

3. 이후 vi가 cscope.out 파일을 인식하도록 한다.

: cs f [option] [name]

이 때 사용되는 옵션들은 아래 공식 Documentation에서 확인할 수 있다.

http://cscope.sourceforge.net/cscope_man_page.html

우리는 주로 함수 이름을 통해 정의를 찾아내야 하므로 g 옵션(전역 함수 정의를 찾는다)을 이용했다.

 

처음에는 그냥 fio를 이용해서 입출력을 시행했는데, 생각해 보니 현재 서버에서 사용하는 저장장치는 일반 SSD이다. 여기에 입출력을 시행해 봤자 어차피 Zone에 관련된 함수들은 사용되지 않을 것 같아서 다시 목표를 설정하기로 했다. 처음에는 zns ssd를 마운트하면 자동으로 입출력이 그쪽으로 될 것이라고 생각했다. 여기서 마운트의 개념을 다시 한 번 알고 가자.

 

Mount

사용자가 유닉스 계열 머신에서 파일에 접근하고자 할 때 파일을 포함하고자 있는 파일 시스템을 저장장치에 연결(마운트)해주어야 한다.

 

쉽게 말해서 디스크에 파일시스템을 붙여주는 작업이다. 같이 작업하는 팀원께서 깔끔하게 에러 처리까지 알려주신 덕에 수월하게 진행할 수 있었다.

 

1. mkzonefs를 설치한다.

https://github.com/damien-lemoal/zonefs-tools

git 주소 및 사용 방법이 나와 있다.

설치 과정이 꽤 복잡하다. 사용되는 라이브러리들 중 util-linux라는 것이 있는데 wget이나 git clone을 이용해 직접 파일을 받아 컴파일 후 make하는 과정을 거쳐줘야 한다.

 

2. ZBD를 포맷한다. 

$ mkzonefs /dev/<disk name>

3. zonefs 파일시스템을 마운트한다.

$ sudo modprobe zonefs
$ sudo mount -t zonefs /dev/<disk name> /mnt

이 때, 리눅스 커널 버전에 따라 기본으로 zonefs를 지원하지 않을 수 있다. 또, 버전이 충분히 높더라도 해당 옵션 설정이 되어있지 않을 수 있다! 꼭 커널 코드 디렉토리에서 sudo make menuconfig를 실행, File system 옵션 내부 zoned filesystem support를 활성화 후 변경사항이 있다면 재컴파일하자.

 

 

그런데 사실 마운트 여부는 딱히 fio와 관계가 없었다. Fio 명령 위치는 옵션을 통해 조절할 수 있기 때문이다.

 

https://zonedstorage.io/docs/benchmarking/fio/zns-fio

 

fio Examples for NVMe ZNS Devices | Zoned Storage

To avoid additional overhead, the mq-deadline scheduler may be disabled for the

zonedstorage.io

거의 매일같이 방문하는 Zoned Storage에 관련된 홈페이지이다. 여기서 친절하게 ZBD에 입출력을 시행하거나 성능을 측정하는 방법을 알려주고 있다. 해당 게시글은 우리가 사용하고 있는 NVMe ZNS Device들에 순차 쓰기 명령을 전달하는 법이 나와 있다.

 fio 명령 수행 과정을 uftrace를 이용하여 커널 단위로 추적하고 사용되는 함수들을 분석한 결과 blkdev_zone_mgmt_ioctl, blkdev_zone_mgmt 이 함수 두 개가 좀 수상해 보였다. 물론 이 외에도 다양한 함수들과 구조체들을 찾아보았지만 아직 코드 분석 단계까지는 들어가지 않았기에 구조에 대해 조금 더 공부하고 추가할 예정이다.

'ZNS Isolation with Linux Container' 카테고리의 다른 글

ZNS SSD 논문 번역  (0) 2022.07.16
Uftrace 사용법  (0) 2022.07.10
Linux Kernel Code Compile  (0) 2022.06.24
VNC Server 설정하기  (0) 2022.06.23
Linux Shell Commands  (0) 2022.06.23

Cgroup 코드를 수정 후 적용하기 위해서는 소스파일을 컴파일 후 make해서 리눅스 커널을 바뀐 코드로 교체해줘야 한다.

모든 작업은 root 계정에서 진행하였다.

 

우선 linux source들을 모아놓은 사이트에 가서 wget으로 코드를 다운받는다.

$wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.gz

이후 해당 파일을 /usr/src로 이동 후 압축 해제해 준다. 

$mv linux-5.10.tar.gz /usr/src
$cd /usr/src
$tar xvf linux-5.10.tar.gz

압축 해제한 폴더로 이동하여 configuration 파일을 만들어야 하는데, 이는 현재 사용하는 커널의 것을 그대로 복사해서 만들었다.

$sudo cp /boot/config-{Current Kernel Version} ./.config

이제 컴파일을 해 줄 건데, 필요한 패키지를 설치한다. 내가 설치한 패키지 목록은 아래와 같다.

- build-essential: 컴파일에 필요한 필수 패키지들. GNU debugger, g++/GNU compiler등을 포함.

- libncurses5: Terminal Handling을 위한 Library

- libncurses5-dev: ncurses의 developer library

- bison: Context-free grammer를 LR / GLR로 변환해주는 패키지

- flex: Lexical pattern을 text로 인식하게 해 주는 패키지

- libssl-dev: Internet상의 통신을 보호하기 위한 패키지

- libelf-dev: High Level에서 ELF file을 읽고 쓸 수 있도록 해 주는 패키지

- bin86: 32bit 코드를 만들 수 있게 해 줌

- kernel-package: Linux kernel을 빌드하기 위해 필요한 utility들

- dwarves: pahole에 의존하는 더미 패키지

*pahole: shows and manipulates data structure layout

 

이제 컴파일을 진행해야 한다. 커널 컴파일은 시간이 오래 걸리기 때문에 컴퓨터의 CPU 코어 개수를 확인하여 전부 활용할 수 있도록 한다.

$nproc

얻어낸 CPU 코어 개수를 아래 명령어의 {coreNumber}부분에 넣어준다.

$sudo make-kpkg -j{coreNumber} --initrd --revision=1.0 kernel_image

컴파일에 시간이 굉장히 오래 걸리는데다 에러도 엄청 많이 난다. 한 3~4번 다시 한 것 같은데, 내가 찾은 에러 해결법을 몇 가지 적어보겠다.

make[2]: *** [debian/rules:6: build] Errore 2
dpkg-buildpackage: Errore: debian/rules build subprocess returned exit status 2
make[1]: *** [scripts/Makefile.package:77: deb-pkg] Errore 2
make: *** [Makefile:1464: deb-pkg] Errore 2

이 에러는 .config 파일의 CONFIG_SYSTEM_TRUSTED_KEYS 부분의 값을 ""로 넣어줘야 해결된다고 한다.

해결할 수 있는 명령어는 다음과 같다.

$scripts/config --disable SYSTEM_TRUSTED_KEYS

이 과정을 수행하니

BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
make: *** [Makefile:1106: vmlinux] Error 1

이런 에러가 나왔다. 위에서 말한 패키지인 dwarves를 설치해서 해결하였다.

$sudo apt install dwarves

컴파일이 완료되면 Linux 폴더의 상위 폴더(/usr/src)에 linux-image-{version}_amd64.deb라는 커널 이미지 파일이 생성된다. 이제 커널 이미지를 등록해주기만 하면 끝이다.

$cd ../
$sudo dpkg -i {KernelImageFile}

이후 시스템 리부트를 해 주면 커널이 성공적으로 업데이트되었음을 알 수 있다.

$sudo systemctl reboot

참고로 커널 버전 확인은

$uname -r

로 할 수 있다. 나는 기존의 커널 버전과 다른 코드를 가져와서 사용했기에 버전이 바뀐 것으로 커널이 잘 적용되었음을 확인하였다.

 

Reference:

https://ndb796.tistory.com/534

https://junshim.github.io/linux%20kernel%20study/Linux_Kernel_Compile/

https://stackoverflow.com/questions/61657707/btf-tmp-vmlinux-btf-pahole-pahole-is-not-available

http://emal.iptime.org/noriwiki/index.php/Linux_%EB%B9%8C%EB%93%9C

https://askubuntu.com/questions/1327749/error-in-compiling-installing-realtime-kernel-on-ubuntu-20-04-2-lts

https://psychoria.tistory.com/785

https://packages.debian.org/

'ZNS Isolation with Linux Container' 카테고리의 다른 글

Uftrace 사용법  (0) 2022.07.10
ZNS SSD에 관한 공부와 Zonefs 파일시스템 마운트 방법  (0) 2022.07.10
VNC Server 설정하기  (0) 2022.06.23
Linux Shell Commands  (0) 2022.06.23
The Block I/O Layer  (0) 2022.05.24

+ Recent posts