๏ปฟ# Readahead (์ ํ ์ฝ๊ธฐ)
๊ด๋ จ ์์ค:mm/readahead.c,include/linux/pagemap.h,include/linux/fs.h,include/linux/backing-dev-defs.h
๊ฐ์ (Overview)
Readahead(์ ํ ์ฝ๊ธฐ)๋ ์์ฉ ํ๋ก๊ทธ๋จ์ด ๋ช
์์ ์ผ๋ก ์์ฒญํ๊ธฐ ์ ์ ํ์ผ์ ๋ด์ฉ์ page cache์ ๋ฏธ๋ฆฌ ๋ก๋ํ๋ ๋ฉ์ปค๋์ฆ์ด๋ค. readahead.c๋ address_space ์์ค์์ ๋์ํ๋ฉฐ, ๋๊ธฐ ์ฝ๊ธฐ์ ๋น๋๊ธฐ readahead๋ฅผ ๊ฒฐํฉํ์ฌ ๋์คํฌ I/O์ ์ ํ๋ฆฌ์ผ์ด์
์ฒ๋ฆฌ๋ฅผ ์ค๋ฒ๋ฉ์ํจ๋ค. ํต์ฌ ์์ด๋์ด๋ "readahead pipelining"์ผ๋ก, ์์ฉ ํ๋ก๊ทธ๋จ์ด ํ์ฌ readahead ์๋์ฐ์ ๋ง์ง๋ง async_size ํ์ด์ง์ ๋๋ฌํ๋ฉด ๋ค์ readahead๋ฅผ ๋น๋๊ธฐ๋ก ์์ํ์ฌ ๋์คํฌ ๋๊ธฐ ์๊ฐ์ ์จ๊ธฐ๋ ๊ฒ์ด๋ค. ๋์๊ด์์ ๋ค์ ์๋์ด ์ฝ์ ๊ฐ๋ฅ์ฑ์ด ๋์ ์ฑ
์ ๋ฏธ๋ฆฌ ์นดํธ์ ๊บผ๋ด ๋๋ ๊ฒ๊ณผ ๋น์ทํ๋ค.
Linux 7.0์์๋ large folio ์ง์์ด ๊ฐํ๋์ด page_cache_ra_order() ๊ฒฝ๋ก์์ order > 0์ธ large folio๋ฅผ ํ ๋นํ ์ ์์ผ๋ฉฐ, ra->order ํ๋๋ฅผ ํตํด preferred folio order๊ฐ ๊ด๋ฆฌ๋๋ค. readahead๋ ์ธ ๊ฐ์ง ํธ๋ฆฌ๊ฑฐ๋ก ๋ฐ๋๋๋ค: (1) cache miss ์ ๋๊ธฐ readahead (page_cache_sync_ra), (2) PG_readahead ํ๋๊ทธ๊ฐ ์ค์ ๋ folio ์ ๊ทผ ์ ๋น๋๊ธฐ readahead (page_cache_async_ra), (3) readahead() ์์คํ
ํธ์ถ ๋ฐ posix_fadvise(POSIX_FADV_WILLNEED).
ํต์ฌ ์์ค ํ์ผ:
mm/readahead.c โ readahead ์๊ณ ๋ฆฌ์ฆ ์ ์ฒด ๊ตฌํ (841์ค)include/linux/pagemap.h โ struct readahead_control, DEFINE_READAHEAD, readahead_folio() ๋ฑ accessorinclude/linux/fs.h โ struct file_ra_state (readahead ์๋์ฐ ์ํ ์ถ์ )include/linux/backing-dev-defs.h โ struct backing_dev_info (ra_pages, io_pages)include/trace/events/readahead.h โ tracepoint 4์ข
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# readahead ๊ด๋ จ ์ปค๋ ์ฌ๋ณผ ํ์ธ
cat /proc/kallsyms | grep -E 'page_cache_sync_ra|page_cache_async_ra|page_cache_ra_unbounded|page_cache_ra_order|readahead_expand'
# readahead tracepoint ํ์ฑํ
echo 1 > /sys/kernel/debug/tracing/events/readahead/enable
cat /sys/kernel/debug/tracing/trace_pipe | head -20
# readahead tracepoint๋ฅผ ํญ๋ชฉ๋ณ๋ก ํ์ธ
echo 1 > /sys/kernel/debug/tracing/events/readahead/page_cache_sync_ra/enable
echo 1 > /sys/kernel/debug/tracing/events/readahead/page_cache_async_ra/enable
echo 1 > /sys/kernel/debug/tracing/events/readahead/page_cache_ra_order/enable
echo 1 > /sys/kernel/debug/tracing/events/readahead/page_cache_ra_unbounded/enable
cat /sys/kernel/debug/tracing/trace_pipe
# ํน์ BDI์ readahead ์๋์ฐ ํฌ๊ธฐ ํ์ธ (ra_pages)
cat /sys/block/sda/queue/read_ahead_kb
# ํ์ผ ์์คํ
๋ณ readahead ์ค์ ํ์ธ
cat /sys/block/<dev>/queue/read_ahead_kb
# readahead ๊ด๋ จ procfs ํต๊ณ
cat /proc/diskstats | grep -E 'read_ahead|ra_'
# readahead ์์คํ
ํธ์ถ ์ถ์
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_readahead/enable
cat /sys/kernel/debug/tracing/trace_pipe
# ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์คํจ ์ readahead ์ค๋จ ํ์ธ
dmesg | grep -i 'readahead\|oom'
# ํ์ด์ง ์บ์ ์ํ ํ์ธ
cat /proc/meminfo | grep -E 'Cached|Buffers|Active'
# ํ์ผ๋ณ readahead ๋์ ๊ด์ฐฐ (strace)
strace -e trace=readahead,read,pread64 -p <PID>
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
file_ra_state โ ํ์ผ๋ณ readahead ์๋์ฐ ์ํ
// include/linux/fs.h:1212-1220
struct file_ra_state {
pgoff_t start; // ๊ฐ์ฅ ์ต๊ทผ readahead ์์ ํ์ด์ง ์ธ๋ฑ์ค
unsigned int size; // ๊ฐ์ฅ ์ต๊ทผ readahead์์ ์ฝ์ ์ด ํ์ด์ง ์
unsigned int async_size; // ๋น๋๊ธฐ ์์ญ ํฌ๊ธฐ (์ด ์๋งํผ ๋จ์์ ๋ ๋ค์ readahead ํธ๋ฆฌ๊ฑฐ)
unsigned int ra_pages; // ์ต๋ readahead ์๋์ฐ ํฌ๊ธฐ (bdi->ra_pages์์ ๋ณต์ฌ)
unsigned short order; // large folio order (0์ด๋ฉด ์ผ๋ฐ 4KB ํ์ด์ง)
unsigned short mmap_miss; // mmap ์ ๊ทผ ์ page cache miss ํ์
loff_t prev_pos; // ๊ฐ์ฅ ์ต๊ทผ ์ฝ๊ธฐ ์์ฒญ์ ๋ง์ง๋ง ๋ฐ์ดํธ ์์น
};
async_size: readahead ์๋์ฐ ๋ด์์ "๋ฏธ๋ฆฌ ์ฝ์" ์์ญ์ ํฌ๊ธฐ. ์ฒซ ๋ฒ์งธ async ํ์ด์ง์ PG_readahead ํ๋๊ทธ๊ฐ ์ค์ ๋จra_pages: backing_dev_info.ra_pages์์ ๋ณต์ฌ. ์ผ๋ฐ์ ์ผ๋ก read_ahead_kb / 4 (์: 128KB โ 32ํ์ด์ง)order: Linux 7.0์ large folio ์ง์์ผ๋ก page_cache_ra_order()์์ ์ฌ์ฉreadahead_control โ readahead ์์ฒญ ์ปจํ ์คํธ
// include/linux/pagemap.h:1347-1358
struct readahead_control {
struct file *file; // ๋์ ํ์ผ (๋คํธ์ํฌ FS์์ ์ธ์ฆ์ฉ, ๋ด๋ถ ํธ์ถ ์ NULL)
struct address_space *mapping; // readahead ๋์ address_space
struct file_ra_state *ra; // ํ์ผ๋ณ readahead ์ํ (์ ํ์ )
/* private: readahead_* accessor ์ฌ์ฉ */
pgoff_t _index; // ํ์ฌ readahead ์์ ํ์ด์ง ์ธ๋ฑ์ค
unsigned int _nr_pages; // ๋จ์ readahead ํ์ด์ง ์
unsigned int _batch_count; // ํ์ฌ ๋ฐฐ์น์์ ์ฒ๋ฆฌ๋ ํ์ด์ง ์
bool dropbehind; // dropbehind ํ๋๊ทธ (ํ์ผ ๋์์ ์ฝ์ ๋ ๊ณผ๊ฑฐ ํ์ด์ง ํด์ )
bool _workingset; // workingset ํ๋๊ทธ ๊ฐ์ง ์ฌ๋ถ
unsigned long _pflags; // PSI ๋ฉ๋ชจ๋ฆฌ ์คํจ ํ๋๊ทธ
};
backing_dev_info โ BDI readahead ์ค์
// include/linux/backing-dev-defs.h:168-175
struct backing_dev_info {
u64 id;
/* ... */
unsigned long __data_racy ra_pages; /* ์ต๋ readahead ํฌ๊ธฐ (ํ์ด์ง ๋จ์) */
unsigned long io_pages; /* ํ์ฉ ์ต๋ IO ํฌ๊ธฐ */
/* ... */
};
ํต์ฌ ํจ์
page_cache_sync_ra() โ ๋๊ธฐ readahead ์ง์ ์
// mm/readahead.c:557-631
void page_cache_sync_ra(struct readahead_control *ractl,
unsigned long req_count)
{
pgoff_t index = readahead_index(ractl);
bool do_forced_ra = ractl->file && (ractl->file->f_mode & FMODE_RANDOM);
struct file_ra_state *ra = ractl->ra;
unsigned long max_pages, contig_count;
pgoff_t prev_index, miss;
// readahead ๋นํ์ฑํ ๋๋ ๋ธ๋ก cgroup ํผ์ก ์ ๊ฐ์ ์ฝ๊ธฐ๋ก ํด๋ฐฑ
if (!ra->ra_pages || blk_cgroup_congested()) {
if (!ractl->file) return;
req_count = 1; do_forced_ra = true;
}
// FMODE_RANDOM ํ์ผ (์: ๋ฐ์ดํฐ๋ฒ ์ด์ค)์ force_page_cache_ra๋ก ์ฒ๋ฆฌ
if (do_forced_ra) {
force_page_cache_ra(ractl, req_count);
return;
}
max_pages = ractl_max_pages(ractl, req_count);
prev_index = (unsigned long long)ra->prev_pos >> PAGE_SHIFT;
// ์บ์ ๋ฏธ์ค ๋๋ ์์ฐจ ์ฝ๊ธฐ ๊ฐ์ง: (index - prev_index) <= 1
if (!index || req_count > max_pages || index - prev_index <= 1UL) {
ra->start = index;
ra->size = get_init_ra_size(req_count, max_pages);
ra->async_size = ra->size > req_count ?
ra->size - req_count : ra->size >> 1;
goto readit;
}
// page cache์์ ์ด์ ์บ์ ์ด๋ ฅ ๊ฒ์
rcu_read_lock();
miss = page_cache_prev_miss(ractl->mapping, index - 1, max_pages);
rcu_read_unlock();
contig_count = index - miss - 1;
// ๋
๋ฆฝ์ ์์ ๋๋ค ์ฝ๊ธฐ: readahead ์ํ ์ค์ผ ๋ฐฉ์ง
if (contig_count <= req_count) {
do_page_cache_ra(ractl, req_count, 0);
return;
}
// ํ์ผ ์ฒ์๋ถํฐ ์บ์๋ ๊ฒฝ์ฐ: ๊ฐํ ์์ฐจ ์คํธ๋ฆผ ์ง์
if (miss == ULONG_MAX) contig_count *= 2;
ra->start = index;
ra->size = min(contig_count + req_count, max_pages);
ra->async_size = 1;
readit:
ra->order = 0;
ractl->_index = ra->start;
page_cache_ra_order(ractl, ra);
}
์ญํ : cache miss ์ readahead ์๋์ฐ๋ฅผ ๊ณ์ฐํ๊ณ large folio ๊ฒฝ๋ก๋ฅผ ํตํด I/O๋ฅผ ์์ํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
ra->ra_pages == 0 ๋๋ blk_cgroup_congested() โ req_count = 1๋ก ๊ฐ์ ์ฝ๊ธฐFMODE_RANDOM โ force_page_cache_ra()๋ก 2MB ์ฒญํฌ ๋จ์ ์ฝ๊ธฐindex - prev_index <= 1 โ ์์ฐจ ์ฝ๊ธฐ โ get_init_ra_size()๋ก ์ด๊ธฐ ์๋์ฐcontig_count <= req_count โ ๋๋ค ์ฝ๊ธฐ โ readahead ์ํ ๋ณ๊ฒฝ ์์ด ์ฝ๊ธฐmiss == ULONG_MAX โ ํ์ผ ์ฒ์๋ถํฐ ์บ์ โ contig_count *= 2page_cache_async_ra() โ ๋น๋๊ธฐ readahead ํธ๋ฆฌ๊ฑฐ
// mm/readahead.c:633-702
void page_cache_async_ra(struct readahead_control *ractl,
struct folio *folio, unsigned long req_count)
{
unsigned long max_pages;
struct file_ra_state *ra = ractl->ra;
pgoff_t index = readahead_index(ractl);
pgoff_t expected, start, end, aligned_end, align;
if (!ra->ra_pages) return;
// PG_readahead์ PG_reclaim ๋นํธ ๊ณต์ โ writeback ์ค์ด๋ฉด ๊ฑด๋๋
if (folio_test_writeback(folio)) return;
folio_clear_readahead(folio);
if (blk_cgroup_congested()) return;
max_pages = ractl_max_pages(ractl, req_count);
// ์์ฐจ ์ ๊ทผ ์์ ์์น ํ์ธ
expected = round_down(ra->start + ra->size - ra->async_size,
folio_nr_pages(folio));
if (index == expected) {
// ์์ฐจ ํํธ: ์๋์ฐ๋ฅผ ์์ผ๋ก ๋ฐ๊ณ ํฌ๊ธฐ ์ฆ๊ฐ
ra->start += ra->size;
ra->size = max(ra->size, get_next_ra_size(ra, max_pages));
goto readit;
}
// ๋น์์ฐจ ํํธ (interleaved reads ๋ฑ): page cache์์ async_size ์ถ์
rcu_read_lock();
start = page_cache_next_miss(ractl->mapping, index + 1, max_pages);
rcu_read_unlock();
if (!start || start - index > max_pages) return;
ra->start = start;
ra->size = start - index;
ra->size += req_count;
ra->size = get_next_ra_size(ra, max_pages);
readit:
ra->order += 2;
align = 1UL << min(ra->order, ffs(max_pages) - 1);
end = ra->start + ra->size;
aligned_end = round_down(end, align);
if (aligned_end > ra->start)
ra->size -= end - aligned_end;
ra->async_size = ra->size;
ractl->_index = ra->start;
page_cache_ra_order(ractl, ra);
}
์ญํ : PG_readahead ํ๋๊ทธ๊ฐ ์ค์ ๋ folio์ ์ ๊ทผํ์ ๋ ๋ค์ readahead ์๋์ฐ๋ฅผ ๊ณ์ฐํ๊ณ ์์ํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
folio_test_writeback(folio) โ PG_reclaim ๋นํธ์ ๋์ผ โ writeback ์ค์ด๋ฉด ์คํตindex == expected (์์ฐจ ํํธ) โ ์๋์ฐ ํฌ๊ธฐ ์ฆ๊ฐ (get_next_ra_size)index != expected (๋น์์ฐจ ํํธ) โ page cache์์ ์ด์ ๋น์ฐ์ ๊ตฌ๊ฐ ๊ฒ์ ํ ์๋์ฐ ์ฌ์ค์ page_cache_ra_unbounded() โ ๊ธฐ๋ณธ readahead ๋ฃจํ
// mm/readahead.c:211-307
void page_cache_ra_unbounded(struct readahead_control *ractl,
unsigned long nr_to_read, unsigned long lookahead_size)
{
struct address_space *mapping = ractl->mapping;
unsigned long index = readahead_index(ractl);
gfp_t gfp_mask = readahead_gfp_mask(mapping);
unsigned long mark = ULONG_MAX, i = 0;
unsigned int min_nrpages = mapping_min_folio_nrpages(mapping);
unsigned int nofs = memalloc_nofs_save();
index = mapping_align_index(mapping, index);
// lookahead ํฌ๊ธฐ ๊ฒฐ์ : lookahead ์์ญ ์์ ์ธ๋ฑ์ค ๊ณ์ฐ
if (lookahead_size <= nr_to_read) {
unsigned long ra_folio_index;
ra_folio_index = round_up(readahead_index(ractl) +
nr_to_read - lookahead_size,
min_nrpages);
mark = ra_folio_index - index;
}
// ํ์ด์ง ์บ์์ folio ์ฌ์ ํ ๋น ๋ฃจํ
while (i < nr_to_read) {
struct folio *folio = xa_load(&mapping->i_pages, index + i);
if (folio && !xa_is_value(folio)) {
// ์ด๋ฏธ ์กด์ฌํ๋ folio โ ํ์ฌ ๋ฐฐ์น ํ๋ฌ์ ํ ๊ฑด๋๋
read_pages(ractl);
ractl->_index += min_nrpages;
i = ractl->_index + ractl->_nr_pages - index;
continue;
}
folio = ractl_alloc_folio(ractl, gfp_mask,
mapping_min_folio_order(mapping));
if (!folio) break;
ret = filemap_add_folio(mapping, folio, index + i, gfp_mask);
if (ret < 0) {
folio_put(folio);
if (ret == -ENOMEM) break;
read_pages(ractl);
/* ... continue */
}
if (i == mark) folio_set_readahead(folio); // PG_readahead ํ๋๊ทธ ์ค์
ractl->_nr_pages += min_nrpages;
i += min_nrpages;
}
read_pages(ractl);
memalloc_nofs_restore(nofs);
}
์ญํ : ํ์ผ ์์คํ ์ readahead ๊ฒฝ๋ก์์ ์ง์ ํธ์ถํ๋ ๊ธฐ๋ณธ readahead ๊ตฌํ. ํ์ด์ง๋ฅผ ๋จผ์ ํ ๋นํ ํ I/O๋ฅผ ์ ์ถํ์ฌ ์ฝ๊ธฐ-์ฐ๊ธฐ ๊ฐ์ ๊ต์ฐจ๋ฅผ ๋ฐฉ์งํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
xa_load๋ก ๊ธฐ์กด folio ์กด์ฌ ํ์ธ โ ์์ผ๋ฉด ํ์ฌ ๋ฐฐ์น ํ๋ฌ์ ํ ์คํตfilemap_add_folio ์คํจ โ -ENOMEM์ด๋ฉด ์ค๋จ, ๊ธฐํ ์๋ฌ์ด๋ฉด ํ๋ฌ์ ํ ๊ณ์i == mark โ lookahead ์์ญ ์์ โ folio_set_readahead()๋ก PG_readahead ์ค์ page_cache_ra_order() โ large folio readahead
// mm/readahead.c:467-540
void page_cache_ra_order(struct readahead_control *ractl,
struct file_ra_state *ra)
{
struct address_space *mapping = ractl->mapping;
pgoff_t start = readahead_index(ractl);
unsigned int min_order = mapping_min_folio_order(mapping);
pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT;
pgoff_t mark = index + ra->size - ra->async_size;
unsigned int new_order = ra->order;
if (!mapping_large_folio_support(mapping)) {
ra->order = 0;
goto fallback;
}
new_order = min(mapping_max_folio_order(mapping), new_order);
new_order = min_t(unsigned int, new_order, ilog2(ra->size));
new_order = max(new_order, min_order);
while (index <= limit) {
unsigned int order = new_order;
// ์ธ๋ฑ์ค ์ ๋ ฌ์ ๋ฐ๋ผ order ์กฐ์
if (index & ((1UL << order) - 1))
order = __ffs(index);
// EOF๋ฅผ ์ด๊ณผํ์ง ์๋๋ก order ์ถ์
while (order > min_order && index + (1UL << order) - 1 > limit)
order--;
err = ra_alloc_folio(ractl, index, mark, order, gfp);
if (err) break;
index += 1UL << order;
}
read_pages(ractl);
fallback:
if (ra->size > index - start)
do_page_cache_ra(ractl, ra->size - (index - start),
ra->async_size);
}
์ญํ : large folio ์ง์ ์ higher-order folio๋ก readahead. ra->order๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ด์ง ์ธ๋ฑ์ค ์ ๋ ฌ๊ณผ EOF ์ ํ์ ๋ง์ถฐ order๋ฅผ ๋์ ์กฐ์ ํ๋ค.
get_next_ra_size() โ ๋ค์ readahead ์๋์ฐ ํฌ๊ธฐ ๊ณ์ฐ
// mm/readahead.c:394-404
static unsigned long get_next_ra_size(struct file_ra_state *ra,
unsigned long max)
{
unsigned long cur = ra->size;
if (cur < max / 16)
return 4 * cur; // ์์ ๋: 4๋ฐฐ ์ฆ๊ฐ (๊ณต๊ฒฉ์ ๋จํ์
)
if (cur <= max / 2)
return 2 * cur; // ์ค๊ฐ: 2๋ฐฐ ์ฆ๊ฐ
return max; // ์ถฉ๋ถํ ํฌ๋ฉด: ์ต๋๊ฐ ์ ์ง
}
์ญํ : ์์ฐจ readahead์์ ์๋์ฐ ํฌ๊ธฐ๋ฅผ ์ ์ง์ ์ผ๋ก ์ฆ๊ฐ์ํค๋ ramp-up ํจ์.
readahead_expand() โ readahead ์ฐฝ ํ์ฅ
// mm/readahead.c:766-841
void readahead_expand(struct readahead_control *ractl,
loff_t new_start, size_t new_len)
{
struct address_space *mapping = ractl->mapping;
struct file_ra_state *ra = ractl->ra;
pgoff_t new_index, new_nr_pages;
gfp_t gfp_mask = readahead_gfp_mask(mapping);
unsigned long min_nrpages = mapping_min_folio_nrpages(mapping);
unsigned int min_order = mapping_min_folio_order(mapping);
new_index = new_start / PAGE_SIZE;
/* readahead ์ฝ๋๋ ํธ์ถ ์ ์ ractl->_index๋ฅผ min_nrpages๋ก ์ ๋ ฌํด์ผ ํ๋ค. */
VM_BUG_ON(!IS_ALIGNED(ractl->_index, min_nrpages));
/* ์์ชฝ ๊ฒฝ๊ณ๋ฅผ ์๋๋ก ํ์ฅํ๋ค. */
while (ractl->_index > new_index) {
unsigned long index = ractl->_index - 1;
struct folio *folio = xa_load(&mapping->i_pages, index);
if (folio && !xa_is_value(folio))
return; /* folio๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค */
folio = ractl_alloc_folio(ractl, gfp_mask, min_order);
if (!folio)
return;
index = mapping_align_index(mapping, index);
if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
folio_put(folio);
return;
}
if (unlikely(folio_test_workingset(folio)) && !ractl->_workingset) {
ractl->_workingset = true;
psi_memstall_enter(&ractl->_pflags);
}
ractl->_nr_pages += min_nrpages;
ractl->_index = folio->index;
}
new_len += new_start - readahead_pos(ractl);
new_nr_pages = DIV_ROUND_UP(new_len, PAGE_SIZE);
/* ๋ค์ชฝ ๊ฒฝ๊ณ๋ฅผ ์๋ก ํ์ฅํ๋ค. */
while (ractl->_nr_pages < new_nr_pages) {
unsigned long index = ractl->_index + ractl->_nr_pages;
struct folio *folio = xa_load(&mapping->i_pages, index);
if (folio && !xa_is_value(folio))
return; /* folio๊ฐ ์ด๋ฏธ ์กด์ฌํ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค */
folio = ractl_alloc_folio(ractl, gfp_mask, min_order);
if (!folio)
return;
index = mapping_align_index(mapping, index);
if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
folio_put(folio);
return;
}
if (unlikely(folio_test_workingset(folio)) && !ractl->_workingset) {
ractl->_workingset = true;
psi_memstall_enter(&ractl->_pflags);
}
ractl->_nr_pages += min_nrpages;
if (ra) {
ra->size += min_nrpages;
ra->async_size += min_nrpages;
}
}
}
EXPORT_SYMBOL(readahead_expand);
์ญํ : filesystem ->readahead() ๊ฒฝ๋ก์์ ํ์ฌ ์์ฒญ ์ฐฝ์ ์๋ค๋ก ๋ํ์ผ ํ ๋ ์ฐ๋ ๋ณด์กฐ ํจ์๋ค. ์ค๊ฐ์ ์ด๋ฏธ ์กด์ฌํ๋ folio๋ฅผ ๋ง๋๋ฉด ๋ ์ด์ ํ์ฅํ์ง ์๊ณ ๋ฉ์ถ๋ฉฐ, THP๊ฐ ๋ผ์ด๋ค๋ฉด ์์ฒญํ ๊ธธ์ด๋ณด๋ค ๋ ํฌ๊ฒ ๋์ด๋ ์ ์๋ค.
ํธ์ถ ํ๋ฆ
read() / mmap() / fadvise()
โ
โโ [cache miss] โ page_cache_sync_readahead()
โ โโ page_cache_sync_ra()
โ โโ FMODE_RANDOM? โ force_page_cache_ra()
โ โ โโ do_page_cache_ra() โ page_cache_ra_unbounded()
โ โโ ์์ฐจ ์ฝ๊ธฐ ๊ฐ์ง โ get_init_ra_size() โ page_cache_ra_order()
โ โโ ๋๋ค ์ฝ๊ธฐ โ do_page_cache_ra()
โ
โโ [PG_readahead ์ ๊ทผ] โ page_cache_async_readahead()
โ โโ page_cache_async_ra()
โ โโ ์์ฐจ ํํธ โ get_next_ra_size() โ page_cache_ra_order()
โ โโ ๋น์์ฐจ ํํธ โ page_cache_next_miss() โ get_next_ra_size()
โ
โโ readahead() ์์คํ
ํธ์ถ โ ksys_readahead()
โโ vfs_fadvise(POSIX_FADV_WILLNEED)
โโ page_cache_sync_readahead()
page_cache_ra_order()
โโ mapping_large_folio_support() ํ์ธ
โโ ra_alloc_folio() ๋ฃจํ (large folio ํ ๋น)
โ โโ ractl_alloc_folio() โ filemap_alloc_folio()
โ โโ filemap_add_folio()
โโ fallback: do_page_cache_ra() (์ผ๋ฐ readahead ๊ฒฝ๋ก)
page_cache_ra_unbounded()
โโ memalloc_nofs_save() (VM ์ฌํ ๋น ๋ฐฉ์ง)
โโ folio ์ฌ์ ํ ๋น ๋ฃจํ
โ โโ xa_load() โ ๊ธฐ์กด folio ํ์ธ
โ โโ ractl_alloc_folio() โ filemap_alloc_folio()
โ โโ filemap_add_folio() โ page cache์ ์ถ๊ฐ
โโ read_pages()
โโ aops->readahead() โ ํ์ผ์์คํ
readahead ํธ๋ค๋ฌ
โ โโ readahead_folio() ๋ฐ๋ณต
โโ aops->read_folio() โ ๊ฐ๋ณ folio ์ฝ๊ธฐ (ํด๋ฐฑ)
filesystem ->readahead() ๊ตฌํ
โโ ํ์ ์ readahead_expand()๋ก ์์ฒญ ์ฐฝ์ ์๋ค๋ก ํ์ฅ
์กฐ๊ฑด๋ณ ๋น๊ต
readahead ํธ๋ฆฌ๊ฑฐ ๋น๊ต
| ์กฐ๊ฑด | ๋๊ธฐ readahead | ๋น๋๊ธฐ readahead | ๊ฐ์ ์ฝ๊ธฐ |
|---|---|---|---|
| **์ง์ ์ ** | `page_cache_sync_ra()` | `page_cache_async_ra()` | `force_page_cache_ra()` |
| **ํธ๋ฆฌ๊ฑฐ** | cache miss | PG_readahead ํ๋๊ทธ ์ ๊ทผ | FMODE_RANDOM ๋๋ ra_pages=0 |
| **์๋์ฐ ๊ณ์ฐ** | `get_init_ra_size()` | `get_next_ra_size()` | ์์ฒญ ํฌ๊ธฐ ๊ทธ๋๋ก |
| **async_size** | `size - req_count` | `size` (์ ์ฒด) | 0 |
| **large folio** | ์ง์ | ์ง์ | ๋นํ์ฑ (์ผ๋ฐ 4KB) |
| **์ํ ๋ณ๊ฒฝ** | ra->start/size ์ค์ | ์๋์ฐ ์ ์ง | ์์ |
readahead ์๋์ฐ ramp-up ๋น๊ต
| ํ์ฌ ํฌ๊ธฐ | ๋ฐฐ์จ | ์ค๋ช |
|---|---|---|
| `cur < max/16` | ร4 | ์ด๊ธฐ ๊ณต๊ฒฉ์ ๋จํ์ |
| `cur โค max/2` | ร2 | ์ ์ง์ ์ฆ๊ฐ |
| `cur > max/2` | max | ์ต๋ ์๋์ฐ ์ ์ง |
folio ์ฒ๋ฆฌ ๊ฒฝ๋ก ๋น๊ต
| ์๋๋ฆฌ์ค | ์ฒ๋ฆฌ ๋ฐฉ์ |
|---|---|
| ๊ธฐ์กด folio ์กด์ฌ (`xa_load` ์ฑ๊ณต) | ํ์ฌ ๋ฐฐ์น ํ๋ฌ์ ํ ์คํต |
| ์ folio ํ ๋น ์ฑ๊ณต | filemap_add_folio๋ก page cache ์ถ๊ฐ |
| ํ ๋น ์คํจ (`-ENOMEM`) | readahead ์ค๋จ |
| filemap_add_folio ์คํจ (๊ธฐํ) | ๋ฐฐ์น ํ๋ฌ์ ํ ๊ณ์ |
| PG_readahead ๋งํฌ ๋๋ฌ | `folio_set_readahead()` ์ค์ |
readahead pipelining ์๋ฆฌ
|==================#===========================|
^start ^PG_readahead ^end
|<----- async_size -------->|
|<------------- size ----------------------->|
1. ์์ฉ ํ๋ก๊ทธ๋จ์ด start ~ # ์ฌ์ด๋ฅผ ์ฝ์ (๋๊ธฐ)
2. # ์ ๋๋ฌํ๋ฉด ๋ค์ readahead ์์ (๋น๋๊ธฐ)
3. ์์ฉ ํ๋ก๊ทธ๋จ์ด ๋๋จธ์ง async ์์ญ์ ์ฝ๋ ๋์
๋์คํฌ์์ ๋ค์ ์๋์ฐ ๋ก๋ฉ
4. ๋ค์ ์๋์ฐ ๋ก๋ฉ ์๋ฃ ์๊น์ง ์์ฉ ํ๋ก๊ทธ๋จ์ด
ํ์ฌ ์๋์ฐ๋ฅผ ์๋นํ๋ฉด I/O ๋๊ธฐ ์์
async_size๊ฐ ํด์๋ก ํ์ดํ๋ผ์ด๋ ํจ๊ณผ๊ฐ ํฌ์ง๋ง ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐra_pages๊ฐ max ์ญํ ์ ํ์ฌ ์๋์ฐ ์ํ ์ ํ