I/O가 더는 진행 중이 아니라는 사실과 I/O가 성공했다는 사실은 다릅니다. device가 error로 완료해도 writeback bit를 풀어 waiter를 깨워야 합니다. 따라서 file writeback의 helper들은 제출과 대기를 나누고, 마지막에 별도의 error 상태를 확인합니다.
filemap_flush()와 file_write_and_wait_range()를 이름만 비슷한 동기화 함수로 묶으면 이 구분이 사라집니다. upstream v6.18 구현에서 각각 어떤 계약을 제공하는지 살펴보겠습니다.
flush는 integrity writeback과 다릅니다
filemap_flush()는 mapping 전체에 WB_SYNC_NONE을 사용해 writeback을 시작하는 경로입니다. 이미 다른 I/O가 진행 중인 대상 등을 건너뛸 수 있으며 모든 dirty folio를 반드시 제출하고 기다린다는 계약이 아닙니다. 이름의 flush만 보고 crash 시 보존을 요구하는 API로 해석해서는 안 됩니다.
반대로 filemap_fdatawrite() 계열은 WB_SYNC_ALL로 data-integrity 제출을 요구합니다. 그렇지만 이름에 wait가 없는 이 helper가 새로 제출한 I/O의 완료까지 모두 기다려 준다고 단정할 수 없습니다. callback은 integrity 목적에 맞게 대상을 처리하고, 별도 wait helper가 완료와 error를 맡습니다. v6.18 mm/filemap.c
아래는 구현코드가 아닌 호출관계입니다.
filemap_flush(mapping)
-> __filemap_fdatawrite(mapping, WB_SYNC_NONE)
-> __filemap_fdatawrite_range(mapping, 0, LLONG_MAX, WB_SYNC_NONE)
filemap_write_and_wait_range(mapping, start, end)
-> __filemap_fdatawrite_range(mapping, start, end, WB_SYNC_ALL)
-> __filemap_fdatawait_range()
-> filemap_check_errors()
file_write_and_wait_range(file, start, end)
-> integrity data writeback and wait
-> file_check_and_advance_wb_err()
byte 범위는 inclusive end를 사용합니다
__filemap_fdatawrite_range()는 writeback_control을 만들고 nr_to_write = LONG_MAX, 지정한 byte 범위와 sync mode를 전달합니다. 이 interface의 end는 마지막 byte를 포함합니다. [start, start + length) 형태의 사용자 범위를 변환할 때 길이가 0인 경우와 overflow를 별도로 고려해야 합니다.
filemap_fdatawrite_wbc()는 mapping이 writeback 가능한지와 DIRTY mark가 있는지를 검사하고, 필요하면 do_writepages()로 내려갑니다. writeback 가능 여부는 BDI의 능력과 관계된 조건이지 cache page 수를 세는 검사와 같은 뜻이 아닙니다. cache가 있어도 dirty가 없다면 새 제출이 필요하지 않을 수 있습니다.
mapping_needs_writeback()처럼 cache 존재를 빠르게 보는 helper도 있습니다. 이것은 이미 진행 중인 I/O를 기다릴 필요가 있을지 포함한 판단입니다. dirty가 없다는 이유만으로 wait를 생략하면 앞서 제출된 I/O를 놓칠 수 있습니다.
현재 mapping_needs_writeback()의 검사는 정확히 mapping->nrpages입니다. 이것을 mapping이 writeback할 능력이 있는지 확인하는 mapping_can_writeback()과 섞어서는 안 됩니다. 후자는 host inode의 BDI가 BDI_CAP_WRITEBACK을 갖는지 확인합니다. 두 전체 helper는 다음과 같습니다. v6.18 filemap.c · v6.18 backing-dev.h
mapping_needs_writeback_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
static bool mapping_needs_writeback(struct address_space *mapping)
{
return mapping->nrpages;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Complete scope from Linux v6.18 include/linux/backing-dev.h.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/backing-dev.h
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* include/linux/backing-dev.h
*
* low-level device information and state which is propagated up through
* to high-level code.
*/
static inline bool mapping_can_writeback(struct address_space *mapping)
{
return inode_to_bdi(mapping->host)->capabilities & BDI_CAP_WRITEBACK;
}
제출 쪽의 전체 함수도 구분해서 볼 수 있습니다. filemap_flush()와 filemap_fdatawrite()는 같은 내부 helper에 서로 다른 sync mode를 넘깁니다. __filemap_fdatawrite()는 전체 범위를, __filemap_fdatawrite_range()는 caller의 범위로 만든 control을 전달합니다. 모든 경우에 새 I/O의 완료 wait까지 여기서 추가하는 것은 아닙니다.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_flush(struct address_space *mapping)
{
return __filemap_fdatawrite(mapping, WB_SYNC_NONE);
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_fdatawrite(struct address_space *mapping)
{
return __filemap_fdatawrite(mapping, WB_SYNC_ALL);
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
static inline int __filemap_fdatawrite(struct address_space *mapping,
int sync_mode)
{
return __filemap_fdatawrite_range(mapping, 0, LLONG_MAX, sync_mode);
}
__filemap_fdatawrite_range_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start,
loff_t end, int sync_mode)
{
struct writeback_control wbc = {
.sync_mode = sync_mode,
.nr_to_write = LONG_MAX,
.range_start = start,
.range_end = end,
};
return filemap_fdatawrite_wbc(mapping, &wbc);
}
filemap_fdatawrite_wbc()는 writeback 능력과 DIRTY mark가 없으면 0으로 일찍 반환합니다. 그 뒤 cgroup accounting용 연결을 준비하고 do_writepages()의 결과를 받은 후 연결을 해제합니다. page 수 검사로 early return한다고 설명하는 것은 이 함수의 조건과 다릅니다.
filemap_fdatawrite_wbc_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_fdatawrite_wbc(struct address_space *mapping,
struct writeback_control *wbc)
{
int ret;
if (!mapping_can_writeback(mapping) ||
!mapping_tagged(mapping, PAGECACHE_TAG_DIRTY))
return 0;
wbc_attach_fdatawrite_inode(wbc, mapping->host);
ret = do_writepages(mapping, wbc);
wbc_detach_inode(wbc);
return ret;
}
WRITEBACK mark를 찾아서 기다립니다
__filemap_fdatawait_range()는 byte 범위를 page index로 바꾼 뒤 PAGECACHE_TAG_WRITEBACK으로 folio batch를 찾습니다. 각 folio의 folio_wait_writeback()을 호출하고 batch 참조를 놓은 다음 필요하면 reschedule합니다.
모든 cached folio를 잠그는 함수는 아닙니다. waiting 대상은 진행 중인 writeback이며, 대기가 끝났다고 caller가 folio lock을 획득하지도 않습니다. batch를 release한다는 것도 각 folio를 반드시 물리적으로 해제한다는 뜻이 아니라 조회 참조를 반환하는 것입니다.
오래된 pagevec loop에는 각 page의 PG_error를 지우는 코드가 보일 수 있습니다. v6.18의 이 wait loop는 folio batch와 writeback 대기를 사용하며 같은 per-page error 정리 코드를 전제로 하지 않습니다. error의 최종 보고는 mapping 또는 file의 별도 경로를 확인해야 합니다.
아래는 v6.18의 내부 wait와 두 wrapper 전체입니다. filemap_fdatawait()는 전체 범위를 지정하며, filemap_fdatawait_range()가 대기 뒤 mapping error를 확인하고 반환합니다. 내부 helper는 오류를 직접 반환하는 함수가 아닙니다.
__filemap_fdatawait_range_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
static void __filemap_fdatawait_range(struct address_space *mapping,
loff_t start_byte, loff_t end_byte)
{
pgoff_t index = start_byte >> PAGE_SHIFT;
pgoff_t end = end_byte >> PAGE_SHIFT;
struct folio_batch fbatch;
unsigned nr_folios;
folio_batch_init(&fbatch);
while (index <= end) {
unsigned i;
nr_folios = filemap_get_folios_tag(mapping, &index, end,
PAGECACHE_TAG_WRITEBACK, &fbatch);
if (!nr_folios)
break;
for (i = 0; i < nr_folios; i++) {
struct folio *folio = fbatch.folios[i];
folio_wait_writeback(folio);
}
folio_batch_release(&fbatch);
cond_resched();
}
}
// SPDX-License-Identifier: GPL-2.0
/*
* Complete scope from Linux v6.18 include/linux/pagemap.h.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/pagemap.h
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* Copyright 1995 Linus Torvalds
*/
static inline int filemap_fdatawait(struct address_space *mapping)
{
return filemap_fdatawait_range(mapping, 0, LLONG_MAX);
}
filemap_fdatawait_range_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_fdatawait_range(struct address_space *mapping, loff_t start_byte,
loff_t end_byte)
{
__filemap_fdatawait_range(mapping, start_byte, end_byte);
return filemap_check_errors(mapping);
}
비교를 위해 v6.0의 내부 wait 전체도 제시합니다. 이 구현에는 end_byte < start_byte guard와 ClearPageError()가 있습니다. pagevec 참조의 release, reschedule, WRITEBACK tag 조회라는 역할은 이어지지만 current code와 token까지 동일한 함수가 아닙니다. 대기 종료를 곧바로 성공적인 disk write로 해석하지 않는 원칙은 두 구현 모두에 적용됩니다.
__filemap_fdatawait_range_v6_0.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.0 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
static void __filemap_fdatawait_range(struct address_space *mapping,
loff_t start_byte, loff_t end_byte)
{
pgoff_t index = start_byte >> PAGE_SHIFT;
pgoff_t end = end_byte >> PAGE_SHIFT;
struct pagevec pvec;
int nr_pages;
if (end_byte < start_byte)
return;
pagevec_init(&pvec);
while (index <= end) {
unsigned i;
nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index,
end, PAGECACHE_TAG_WRITEBACK);
if (!nr_pages)
break;
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i];
wait_on_page_writeback(page);
ClearPageError(page);
}
pagevec_release(&pvec);
cond_resched();
}
}
일부 error가 나도 이미 제출한 I/O는 남습니다
filemap_write_and_wait_range()는 제출에서 -ENOSPC 같은 error가 나도 일부 I/O는 시작되었을 수 있다고 보고 대기를 수행합니다. 곧바로 반환하면 caller가 버려도 된다고 판단한 data를 device가 아직 사용하고 있을 수 있습니다.
-EIO는 이 경로에서 별도로 취급하여 후속 대기를 피합니다. 심각한 제출 실패를 나타낼 수 있다는 구현상의 선택입니다. 이를 모든 함수에 적용되는 일반적인 EIO 규칙으로 확장해서는 안 됩니다.
마지막에는 mapping error를 확인하며, 직접 제출 과정의 error가 이미 있으면 그 값을 우선 보존합니다. 직접 호출의 성공과 비동기 완료에서 기록된 error를 합치는 단계가 따로 있는 것입니다.
다음은 v6.18 filemap_write_and_wait()와 range 함수 전체입니다. 현재 range 함수는 lend < lstart이면 먼저 0을 반환하며, 이 early return에서는 mapping error를 읽고 지우지도 않습니다. 유효한 inclusive 범위에 대한 후속 처리와 invalid range의 동작을 같게 설명해서는 안 됩니다.
filemap_write_and_wait_v6_18.c
// SPDX-License-Identifier: GPL-2.0
/*
* Complete scope from Linux v6.18 include/linux/pagemap.h.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/pagemap.h
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* Copyright 1995 Linus Torvalds
*/
static inline int filemap_write_and_wait(struct address_space *mapping)
{
return filemap_write_and_wait_range(mapping, 0, LLONG_MAX);
}
filemap_write_and_wait_range_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend)
{
int err = 0, err2;
if (lend < lstart)
return 0;
if (mapping_needs_writeback(mapping)) {
err = __filemap_fdatawrite_range(mapping, lstart, lend,
WB_SYNC_ALL);
/*
* Even if the above returned error, the pages may be
* written partially (e.g. -ENOSPC), so we wait for it.
* But the -EIO is special case, it may indicate the worst
* thing (e.g. bug) happened, so we avoid waiting for it.
*/
if (err != -EIO)
__filemap_fdatawait_range(mapping, lstart, lend);
}
err2 = filemap_check_errors(mapping);
if (!err)
err = err2;
return err;
}
mapping 단위의 filemap_check_errors()는 AS_ENOSPC와 AS_EIO를 test-and-clear합니다. 둘 다 관찰되면 EIO가 우선합니다. 여러 caller가 이 공유 flag를 소비할 수 있으므로 이것은 file마다 독립된 error 이력을 보장하는 경로가 아닙니다.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_check_errors(struct address_space *mapping)
{
int ret = 0;
/* Check for outstanding write errors */
if (test_bit(AS_ENOSPC, &mapping->flags) &&
test_and_clear_bit(AS_ENOSPC, &mapping->flags))
ret = -ENOSPC;
if (test_bit(AS_EIO, &mapping->flags) &&
test_and_clear_bit(AS_EIO, &mapping->flags))
ret = -EIO;
return ret;
}
file별 cursor는 error의 관찰 위치를 기억합니다
여러 열린 file이 같은 inode와 mapping을 공유할 수 있습니다. 한 caller가 mapping error를 읽고 지웠다는 이유로 다른 writer가 관련 error를 영원히 놓쳐서는 안 됩니다. file별 interface는 file->f_wb_err cursor와 mapping의 error sequence를 사용하여 이전 관찰 이후의 writeback error를 확인하고 관찰 위치를 진전시킵니다.
이것은 failed I/O 각각을 무한히 보존한 event log는 아닙니다. error가 어떤 범위의 마지막 write와 정확히 일대일 대응한다는 보장도 아닙니다. background writeback의 오류가 나중에 fsync()에서 보고될 수 있으므로 application은 sync 반환값을 반드시 다루어야 합니다.
아래는 v6.18의 file별 제출·대기와 error cursor 처리 전체입니다. file_write_and_wait_range()도 invalid range를 먼저 반환하며, 정상 범위에서는 새로 제출할 cache page가 없더라도 error cursor를 확인할 수 있습니다. 제출의 errno가 먼저 있으면 그 값을 유지하되 cursor 관찰 자체는 수행합니다.
file_write_and_wait_range_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int file_write_and_wait_range(struct file *file, loff_t lstart, loff_t lend)
{
int err = 0, err2;
struct address_space *mapping = file->f_mapping;
if (lend < lstart)
return 0;
if (mapping_needs_writeback(mapping)) {
err = __filemap_fdatawrite_range(mapping, lstart, lend,
WB_SYNC_ALL);
/* See comment of filemap_write_and_wait() */
if (err != -EIO)
__filemap_fdatawait_range(mapping, lstart, lend);
}
err2 = file_check_and_advance_wb_err(file);
if (!err)
err = err2;
return err;
}
file_check_and_advance_wb_err_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int file_check_and_advance_wb_err(struct file *file)
{
int err = 0;
errseq_t old = READ_ONCE(file->f_wb_err);
struct address_space *mapping = file->f_mapping;
/* Locklessly handle the common case where nothing has changed */
if (errseq_check(&mapping->wb_err, old)) {
/* Something changed, must use slow path */
spin_lock(&file->f_lock);
old = file->f_wb_err;
err = errseq_check_and_advance(&mapping->wb_err,
&file->f_wb_err);
trace_file_check_and_advance_wb_err(file, old);
spin_unlock(&file->f_lock);
}
/*
* We're mostly using this function as a drop in replacement for
* filemap_check_errors. Clear AS_EIO/AS_ENOSPC to emulate the effect
* that the legacy code would have had on these flags.
*/
clear_bit(AS_EIO, &mapping->flags);
clear_bit(AS_ENOSPC, &mapping->flags);
return err;
}
빠른 경로는 READ_ONCE(file->f_wb_err)와 mapping의 sequence를 비교합니다. 바뀐 오류가 있으면 file->f_lock 아래에서 현재 cursor를 다시 읽고 진전시킵니다. 같은 open file description을 공유하는 dup()된 fd는 같은 struct file과 cursor를 공유하므로 fd 숫자마다 독립적이라고 생각하면 안 됩니다. 마지막에는 legacy flag도 지워 예전 mapping helper의 효과와 맞춥니다. dup(2)
시스템·filesystem 차원의 flusher처럼 error를 직접 소비해 보고할 책임이 없는 caller를 위해서는 keep-errors helper도 있습니다. 기다리는 동작이 같아 보여도 error를 지우는지, 보존하는지, file cursor를 진전시키는지에 따라 선택이 달라집니다.
filemap_fdatawait_keep_errors()와 range variant는 내부 wait 뒤 filemap_check_and_keep_errors()를 사용합니다. 아래 세 전체 함수는 공유 flag의 error를 검사하지만 지우지 않습니다. superblock sync가 이 helper를 쓰는 이유와 inode 참조를 잡고 기다리는 순서는 superblock sync의 두 wait와 연결됩니다.
filemap_fdatawait_keep_errors_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_fdatawait_keep_errors(struct address_space *mapping)
{
__filemap_fdatawait_range(mapping, 0, LLONG_MAX);
return filemap_check_and_keep_errors(mapping);
}
filemap_fdatawait_range_keep_errors_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
int filemap_fdatawait_range_keep_errors(struct address_space *mapping,
loff_t start_byte, loff_t end_byte)
{
__filemap_fdatawait_range(mapping, start_byte, end_byte);
return filemap_check_and_keep_errors(mapping);
}
filemap_check_and_keep_errors_v6_18.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Complete scope from Linux v6.18 mm/filemap.c.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/filemap.c
* Kernel excerpt; not a standalone program.
* The book exact source tag is not asserted.
*/
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
static int filemap_check_and_keep_errors(struct address_space *mapping)
{
/* Check for outstanding write errors */
if (test_bit(AS_EIO, &mapping->flags))
return -EIO;
if (test_bit(AS_ENOSPC, &mapping->flags))
return -ENOSPC;
return 0;
}
data helper가 성공했다고 filesystem metadata와 device flush까지 모두 끝났다는 뜻은 아닙니다. 최종 fsync callback은 필요한 순서를 조합합니다. 제출, 대기, error 관찰, durability의 범위를 나누어 읽어야 각 helper의 성공값을 정확히 해석할 수 있습니다.
같은 이유로 sync_file_range()를 fsync의 대체품으로 삼지 않습니다. 이 API는 metadata와 volatile device write cache flush를 보장하지 않습니다. 범위의 data writeback을 제어하는 목적과 crash 후 data 복구를 보장하는 목적은 다릅니다. sync_file_range(2)
포함한 kernel 함수들은 표시한 tag의 전체 scope이며 주변 kernel 선언을 전제로 합니다. source의 GPL-2.0 license를 함께 확인할 수 있습니다. 이 글에서는 kernel compile, I/O 실패 주입이나 device 실측을 수행하지 않았습니다.
참고 자료
- Lorenzo Stoakes, The Linux Memory Manager, Early Access 2025, Chapter 10, File Writeback.
- Linux v6.18 mm/filemap.c: filemap 제출·대기·error helper 전체 구현.
- Linux v6.18 fs/libfs.c: generic fsync에서의 data helper 사용과 후속 처리.
- fsync(2): 비동기 writeback error의 사용자 보고.