- 2024년 LSFMM+BPF Summit에서 Linux 파일 시스템에 Rust를 적용하는 방안이 논의됐고, 2023년 12월 RFC 이후 나온 두 번째 RFC 패치가 토론의 중심이 됨
- Rust-for-Linux 쪽은 파일 시스템 API의 요구사항을 Rust 타입 시스템에 담아 컴파일 시점 오류 검출, 자원 정리 자동화, 메모리 관련 취약점 감소를 노림
iget_locked()사례는 C 호출자가 직접 처리하던 null 확인, inode 상태 구분, 실패 처리 등을 Rust의get_or_create_inode()가 타입과 자동 정리로 강제하려는 방향을 보여줌- Dave Chinner, Christian Brauner, James Bottomley, Ted Ts'o 등은 C API와 Rust API의 이름 불일치, API 동기화, 객체 생명주기 차이, 50개 이상의 파일 시스템을 고려한 유지보수 부담을 우려함
- 갈등의 핵심은 Rust 추상화의 장점 자체보다, C 코드가 계속 바뀔 때 Rust 바인딩과 추상화를 맞춰 가는 고통을 누가 부담할지에 있음
LSFMM+BPF에서 열린 파일 시스템 Rust 세션
- 2024년 Linux Storage, Filesystem, Memory Management, and BPF Summit에서 Wedson Almeida Filho와 Kent Overstreet가 Linux 파일 시스템에 Rust를 사용하는 방안을 다룸
- Almeida는 2023년 12월 파일 시스템용 Rust 추상화 RFC 패치 세트를 올렸고, 이 접근을 둘러싸고 의견 차이가 있었음
- 세션이 열린 5월 중순 같은 날, Almeida는 두 번째 RFC 패치 버전을 올려 다른 Rust 관련 주제와 함께 논의하려 함
Rust-for-Linux의 파일 시스템 추상화 목표
- 제안된 파일 시스템 추상화는 Rust-for-Linux project가 추구하는 방향을 반영함
- 핵심은 파일 시스템 API의 요구사항을 Rust 타입 시스템으로 더 많이 표현해 컴파일 시점에 실수를 잡는 것임
- C 코드에서 쉽게 제공하기 어려운 작업도 자동화하려 함
- 예: 자원 정리
- 목표는 파일 시스템 개발 경험을 더 생산적으로 만들고, 컴파일러가 찾을 수 있는 문제를 디버깅하는 시간을 줄이며, 메모리 관련 취약점을 낮추는 것임
- Overstreet는 bcachefs에서 2주짜리 버그 추적을 너무 많이 겪었고, Rust가 C보다 더 많은 것을 제공한다고 봄
- Rust는 정의되지 않은 동작을 제거함
- 코드 내부에서 무슨 일이 일어나는지 볼 수 있는 기능을 제공함
- Rust 코드의 올바름을 증명할 수 있게 되면 기능 개발을 방해하는 버그가 훨씬 줄어들 것으로 봄
iget_locked()를 둘러싼 타입 시스템 사례
- Almeida는 슬라이드에서 현재 커널의
iget_locked()가 복잡한 요구사항을 가진다고 예를 들었음 - C 호출자는 여러 조건을 직접 처리해야 함
- 반환값이 null인지 확인해야 함
- 반환된
struct inode가 새 inode인지 기존 inode인지 확인해야 함 - 새 inode라면 사용 전에 초기화해야 함
- 초기화가 실패하면
iget_failed()를 호출해야 함
- Al Viro는 Almeida 슬라이드의
iget_locked()호출자 요구사항 일부에 동의하지 않았고, 세부 동작을 두고 논쟁이 이어짐 - Overstreet는 이런 규칙을 Rust 타입과 추상화에 캡슐화하면 컴파일러가 올바른 처리를 강제할 수 있다고 봄
- Almeida가 제시한 Rust 쪽 대응 함수는
get_or_create_inode()였음- C와 마찬가지로 실패 여부는 확인해야 함
- 성공 시 호출자는 일반 참조 카운트 inode를 받거나 새 inode를 받음
- 일반 inode는 객체가 더 이상 참조되지 않을 때 참조 카운트가 자동 감소함
- 새 inode는 초기화되지 않으면
iget_failed()에 해당하는 처리를 자동 호출함 - 새 inode가 한 번 초기화되면 일반 inode가 되고, 이후 참조 카운트 자동 감소가 적용됨
- 이 동작들은 타입 시스템으로 강제됨
- Viro는 이 제약들이 실제 소스 코드 어디에 정의될지 의문을 제기함
- Almeida는 Viro와 다른 파일 시스템 개발자들로부터 제약을 파악한 뒤, 이를 강제할 타입과 추상화를 만들려는 것이라고 답함
C API와 Rust API 사이의 단절
- Dave Chinner는 C API와 Rust API의 이름이 다르면 기존 개발자가 C 코드를 보고 Rust의 대응 호출을 알기 어렵다고 봄
- 같은 이름을 쓰지 않으면 기존 개발 커뮤니티에 완전히 낯선 API가 될 수 있다는 우려도 나옴
- C 코드가 바뀔 때 Rust 코드도 따라가야 하므로, 그 작업을 누가 맡을지가 문제로 남음
- Almeida는 논의가 필요한 사안이라고 인정함
- 이름 변경에 반대하지는 않음
- 다만
iget_locked()가 좋은 이름이라고 보지는 않으며, 더 나은 이름을 만들 기회로 볼 수도 있다고 함
- Viro는
iget_locked()가 superblock 객체의 멤버 함수가 아니라 라이브러리 함수이므로 예시 선택이 좋지 않다고 봄 - Almeida는
get_or_create_inode()도 라이브러리 함수로 바꿀 수 있으며, 예시는 제약을 타입에 인코딩하는 방법을 보여주려는 것이었다고 답함
범용 추상화와 단순 파일 시스템 중심 접근 사이의 선택
- Christian Brauner는 Rust 추상화가 모든 커널 파일 시스템을 위한 범용 추상화인지, Rust로 작성된 더 단순한 파일 시스템에 필요한 기능 중심인지 먼저 정해야 한다고 봄
- 장기적으로
get_or_create_inode()같은 함수가iget_locked()보다 훨씬 많은 제약을 담으면 문제가 생길 수 있음 - C 코드는 특히 초기에는 Rust 코드보다 더 빠르게 진화할 수 있어, 두 API를 계속 동기화해야 함
- Overstreet는 Rust 추상화를 추가하면서 리팩터링과 정리를 함께 할 것인지가 핵심이며, 그것이 필요하다고 강하게 봄
- James Bottomley는 객체 생명주기가 Rust API에는 인코딩되지만 C에는 그에 해당하는 표현이 없다고 봄
- 한쪽에서 객체 생명주기가 바뀌면 다른 쪽에 버그가 생길 수 있음
- Chinner는 inode 객체의 생명주기가 때로 파일 시스템별로 다르다고 말함
- 단일한 생명주기 이해를 API에 넣으면 일부 파일 시스템에서 그 함수들이 동작하지 않을 수 있음
- Almeida는 예시가 현재
iget_locked()를 호출하고 이득을 볼 수 있는 파일 시스템에만 쓰일 것이라고 답함- Rust 개발자들은 파일 시스템이 현재 하는 방식을 바꾸도록 강제하려는 것이 아님
“고통을 누가 부담할 것인가”
- Ted Ts'o는 Rust라는 “종교”로 모두를 전환시키려는 시도가 있는 것처럼 보이지만, Linux에는 50개 이상의 파일 시스템이 있고 즉시 전환되지는 않을 것이라고 말함
- C 코드는 계속 개선될 것이고, 그 변화가 Rust 바인딩을 깨면 해당 바인딩에 의존하는 파일 시스템도 깨질 수 있음
- Ts'o는 당분간 Rust 바인딩이 2급 시민이며, 깨진 Rust 바인딩은 전체 파일 시스템 커뮤니티가 아니라 Rust-for-Linux 개발자들의 문제라고 봄
- 그는 Rust 바인딩 개발과 C 코드 진화를 병행하면서, 대량의 의미를 타입 시스템에 담는 접근이 좋은지 나쁜지 1~2년 안에 드러날 것이라고 봄
- Ts'o에게 큰 변화는 결국 고통 배분 문제임
- C API를 바꾸는 개발자가 영향을 받은 C 코드는 고치더라도, Rust를 모르기 때문에 Rust 바인딩은 고치지 않겠다고 말할 수 있음
- Almeida는 C API를 고정하려는 것이 아니라, 파일 시스템 개발자들이 API의 의미를 설명해 주면 그것을 Rust에 인코딩하려는 것이라고 답함
- Bottomley는 의미가 바인딩에 더 많이 인코딩될수록 동기화 관점에서 더 취약해질 수 있다고 봄
- Almeida는 API가 바뀌면 API 사용자를 업데이트해야 하는 것은 다른 사용자와 마찬가지라고 답함
메서드, 함수, 타입에 무엇을 담을지
- Viro는
iget_locked()대체안이 메서드에 의존하는 점을 다시 문제 삼음- 메서드를 쓰면 인자가 명시적으로 지정되지 않는다고 봄
- Overstreet는 메서드에 대한 불만이 상속에 지나치게 의존하는 C++ 같은 언어에서 비롯된다고 봄
- Rust는 그렇게 하지 않으며, Rust의 메서드는 대체로 문법적 요소라고 말함
- Jan Kara는 inode 자체에 따르는 동작과
iget_locked()함수에 내재한 동작을 구분함- inode에는 참조 카운트와 그 처리 같은 동작이 따름
iget_locked()함수에는 별도의 동작이 내재함
- Overstreet와 Almeida는 두 부분이 모두 타입에 인코딩되지만 분리되어 있으며, inode 타입을 사용하는 다른 함수는 서로 다른 속성의 반환값을 가질 수 있다고 답함
- Viro는 VFS에서 inode가 현재 방식으로 동작하는 이유를 설명했고, 작게 시작해서 진행 방향을 보자는 데에는 동의함
- Overstreet는 이번 예시가 복잡해 출발점으로 좋지 않았을 수도 있다고 했고, Viro는 “아니, 그렇지 않다”고 답하며 세션이 마무리됨