tmpfs / shmem
๊ด๋ จ ์์ค:mm/shmem.c,include/linux/shmem_fs.h
๊ด๋ จ ๋ฌธ์: ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ฐ์ ยท Folio / Page Cache ยท Swap / zswap ยท Memory Cgroup
๊ฐ์ (Overview)
tmpfs๋ ๋ฆฌ๋ ์ค ์ปค๋์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ ํ์ผ ์์คํ ์ผ๋ก, ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋๋ฉฐ ์์คํ ์ข ๋ฃ ์ ์ฌ๋ผ์ง๋๋ค. shmem(shared memory)๋ tmpfs์ ๋ด๋ถ ๊ตฌํ์ฒด๋ก, SysV IPC ๊ณต์ ๋ฉ๋ชจ๋ฆฌ, POSIX ๊ณต์ ๋ฉ๋ชจ๋ฆฌ, mmap๋ ์ต๋ช ํ์ผ ๋ฑ์ ๊ตฌํํฉ๋๋ค. tmpfs๋ swap์ ์ฌ์ฉํ ์ ์์ด ์ธ๋ถ ๋ฉ๋ชจ๋ฆฌ๋ก ์ซ๊ฒจ๋ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋ณด๋ค ํฐ ๊ฐ์ ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค.
์ปค๋ ๋ด๋ถ์์ shmem์ ํ์ผ ์บ์(folio/page cache)์ swap ์บ์๋ฅผ ์ฐ๊ฒฐํ๋ ์ค๊ฐ ๋ค๋ฆฌ ์ญํ ์ ํฉ๋๋ค. ํ์ด์ง๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์์ผ๋ฉด file cache๋ก, swap์ผ๋ก ์ซ๊ฒจ๋๋ฉด swap entry๋ก ๊ด๋ฆฌ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด anonymous shared memory์ file-backed memory๋ฅผ ๋์ผํ ์ธํฐํ์ด์ค๋ก ์ฒ๋ฆฌํฉ๋๋ค.
์ด์ ๊ด์ ์์๋ tmpfs๋ฅผ "๋์คํฌ ํ์ผ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค์ ๋ก๋ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ ๋ฐ๋ผ swap๊น์ง ์๋ณตํ๋ ํ์ผ ๊ฐ์ฒด"๋ก ์ดํดํ๋ฉด ํธํฉ๋๋ค. memfd_create(), /dev/shm, ๋ธ๋ผ์ฐ์ /DB์ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ์ธ๊ทธ๋จผํธ, ์ปจํ
์ด๋ ๋ด๋ถ์ tmpfs ๋ง์ดํธ๊ฐ ๋ชจ๋ ๊ฐ์ shmem ์ฝ๋ ๊ฒฝ๋ก ์์์ ๋์ํ๋ฏ๋ก, page cache, reclaim, memcg, THP ์ค์ ์ด ํจ๊ป ์ฝํ๋๋ค.
/* ์ฃผ์ ์์ค ํ์ผ */
mm/shmem.c // shmem ํต์ฌ ๊ตฌํ
include/linux/shmem_fs.h // ํต์ฌ ๊ตฌ์กฐ์ฒด ์ ์
mm/shmem_quota.c // quota ์ง์
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# tmpfs ๋ง์ดํธ ํ์ธ
mount | grep tmpfs
# tmpfs ์ฌ์ฉ๋ ํ์ธ
df -h /dev/shm
df -h /tmp
# shmem ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํ์ธ
cat /proc/meminfo | grep -i shmem
# ์์คํ
์ ์ฒด shmem ํ์ด์ง ์
cat /proc/vmstat | grep nr_shmem
# hugepage ์ฌ์ฉ ์ค์ธ shmem ํ์ด์ง
cat /proc/vmstat | grep nr_shmem_thps
# ํน์ ํ๋ก์ธ์ค์ shmem ์ฌ์ฉ๋
pmap -x <PID> | grep shmem
# tmpfs ํ์ผ ์์คํ
์ ๋ณด
stat -f /dev/shm
# slabinfo์์ shmem ๊ด๋ จ slab ํ์ธ
cat /proc/slabinfo | grep shmem_inode_cache
# NUMA ๋
ธ๋๋ณ shmem ํ ๋น ์ ์ฑ
ํ์ธ
cat /proc/<PID>/numa_maps | grep shmem
# shmem THP ์ ์ฑ
ํ์ธ
cat /sys/kernel/mm/transparent_hugepage/shmem_enabled
# tmpfs ๋ง์ดํธ ์ต์
ํ์ธ
findmnt -no TARGET,OPTIONS /dev/shm
# ํ๋ก์ธ์ค๋ณ shared/tmpfs ๋งคํ ํฌ๊ธฐ ํ์ธ
grep -E 'Shared_Clean|Shared_Dirty|Anonymous|ShmemPmdMapped' /proc/<PID>/smaps
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct shmem_inode_info
shmem inode์ ํต์ฌ ํ์ฅ ๊ตฌ์กฐ์ฒด. ์ผ๋ฐ inode๋ณด๋ค ํฐ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ํ์ํฉ๋๋ค.
/* include/linux/shmem_fs.h:36-59 */
struct shmem_inode_info {
spinlock_t lock;
unsigned int seals; // shmem seal ๋นํธ
unsigned long flags;
unsigned long alloced; // ํ์ผ์ ํ ๋น๋ ๋ฐ์ดํฐ ํ์ด์ง ์
unsigned long swapped; // swap์ผ๋ก ๋๊ธด ํ์ด์ง ์์ ์๊ณ
union {
struct offset_ctx dir_offsets; // ์์ ์ ์ธ ๋๋ ํฐ๋ฆฌ ์คํ์
struct {
struct list_head shrinklist; // ์ถ์ ๊ฐ๋ฅํ huge folio inode ๋ชฉ๋ก
struct list_head swaplist; // swap ์ inode ์ฒด์ธ
};
};
struct timespec64 i_crtime; // ํ์ผ ์์ฑ ์๊ฐ
struct shared_policy policy; // NUMA ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ ์ฑ
struct simple_xattrs xattrs; // xattr ๋ชฉ๋ก
pgoff_t fallocend; // ๊ฐ์ฅ ๋์ fallocate end index
unsigned int fsflags; // FS_IOC_[SG]ETFLAGS์ฉ
atomic_t stop_eviction; // inode ์์
์ค hold
#ifdef CONFIG_TMPFS_QUOTA
struct dquot __rcu *i_dquot[MAXQUOTAS];
#endif
struct inode vfs_inode;
};
ํต์ฌ ํ๋ ์ค๋ช :
alloced: file cache์ ์กด์ฌํ๋ ํ์ด์ง ์ (swap์ ์๋ ๊ฒ ํฌํจ)swapped: swap์ผ๋ก ์ซ๊ฒจ๋ ํ์ด์ง ์. alloced == nrpages + swapped ๊ด๊ณshrinklist: ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ huge page๋ฅผ ๋ถํ (fsplit)ํ๊ธฐ ์ํ inode ๋ชฉ๋กswaplist: shmem_unuse()๊ฐ swap off ์ inode๋ฅผ ์ฐพ๊ธฐ ์ํ ์ฒด์ธstruct shmem_sb_info
superblock ์์ค์ shmem ๋ฉํ๋ฐ์ดํฐ ๊ด๋ฆฌ ๊ตฌ์กฐ์ฒด.
/* include/linux/shmem_fs.h:73-92 */
struct shmem_sb_info {
unsigned long max_blocks; // ๋ธ๋ก ์ ์ ํ
struct percpu_counter used_blocks; // ํ ๋น๋ ๋ธ๋ก ์
unsigned long max_inodes; // inode ์ ์ ํ
unsigned long free_ispace; // ๋จ์ inode ๊ณต๊ฐ
raw_spinlock_t stat_lock; // sb_info ๋ณ๊ฒฝ ์ง๋ ฌํ
umode_t mode; // ๋ฃจํธ ๋๋ ํ ๋ฆฌ ๋ง์ดํธ ๋ชจ๋
unsigned char huge; // hugepage ์ฌ์ฉ ์ฌ๋ถ
kuid_t uid; // ๋ฃจํธ ๋๋ ํ ๋ฆฌ UID
kgid_t gid; // ๋ฃจํธ ๋๋ ํ ๋ฆฌ GID
bool full_inums; // ino uint/ino_t ์ ํ
bool noswap; // swap ์ฌ์ฉ ๋ถ๊ฐ ํ๋๊ทธ
ino_t next_ino; // ๋ค์ ์ฌ์ฉํ inode ๋ฒํธ
ino_t __percpu *ino_batch; // per-cpu inode ๋ฒํธ ๋ฐฐ์น
struct mempolicy *mpol; // ๊ธฐ๋ณธ NUMA ์ ์ฑ
spinlock_t shrinklist_lock; // shrinklist ๋ณดํธ
struct list_head shrinklist; // ์ถ์ ๊ฐ๋ฅํ inode ๋ชฉ๋ก
unsigned long shrinklist_len; // shrinklist ๊ธธ์ด
struct shmem_quota_limits qlimits; // quota ์ ํ
};
ํต์ฌ ํ๋๊ทธ:
SHMEM_F_NORESERVE (BIT(0)): ์ ์ฒด ๊ฐ์ฒด ์ฌ์ ํ ๋น ๋นํ์ฑํSHMEM_F_LOCKED (BIT(1)): swap ์ฌ์ฉ ๋ถ๊ฐ (SysV SHM_LOCK์ฉ)SHMEM_F_MAPPING_FROZEN (BIT(2)): grow/shrink/hole punch ๋ถ๊ฐ (folio pinning์ฉ)struct shmem_falloc
fallocate ์์ ์ค hole punch ๋๊ธฐ ์ํ๋ฅผ ๋ด๋ ๊ตฌ์กฐ์ฒด.
/* mm/shmem.c:105-111 */
struct shmem_falloc {
wait_queue_head_t *waitq; // hole punch ๋๊ธฐ ํ
pgoff_t start; // ํ์ฌ fallocate ์ค์ธ ๋ฒ์ ์์
pgoff_t next; // ๋ค์ fallocateํ ํ์ด์ง ์คํ์
pgoff_t nr_falloced; // ์๋ก fallocate๋ ํ์ด์ง ์
pgoff_t nr_unswapped; // writeout์ด swap์ ๊ฑฐ๋ถํ ํ์
};
ํต์ฌ ํจ์
shmem_get_folio_gfp()
ํ์ด์ง ์บ์์์ folio๋ฅผ ์ฐพ๊ฑฐ๋, swap์์ ์ฝ์ด์ค๊ฑฐ๋, ์๋กญ๊ฒ ํ ๋นํฉ๋๋ค.
/* mm/shmem.c:2467-2644 */
static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
loff_t write_end, struct folio **foliop, enum sgp_type sgp,
gfp_t gfp, struct vm_fault *vmf, vm_fault_t *fault_type)
/* mm/shmem.c:2491-2557 */
folio = filemap_get_entry(inode->i_mapping, index);
if (folio && vma && userfaultfd_minor(vma)) {
if (!xa_is_value(folio))
folio_put(folio);
*fault_type = handle_userfault(vmf, VM_UFFD_MINOR);
return 0;
}
if (xa_is_value(folio)) {
error = shmem_swapin_folio(inode, index, &folio,
sgp, gfp, vma, fault_type);
if (error == -EEXIST)
goto repeat;
*foliop = folio;
return error;
}
if (folio) {
folio_lock(folio);
/* folio๊ฐ truncate๋์๊ฑฐ๋ swap out๋์๋์ง ํ์ธ */
if (unlikely(folio->mapping != inode->i_mapping)) {
folio_unlock(folio);
folio_put(folio);
goto repeat;
}
if (sgp == SGP_WRITE)
folio_mark_accessed(folio);
if (folio_test_uptodate(folio))
goto out;
/* fallocate๋ folio */
if (sgp != SGP_READ)
goto clear;
folio_unlock(folio);
folio_put(folio);
}
/*
* SGP_READ: hole์ด๋ฉด NULL folio์ 0์ ๋ฐํํ๊ณ ํธ์ถ์๊ฐ zero ์ฒ๋ฆฌ.
* SGP_NOALLOC: hole์ด๋ฉด NULL folio์ -ENOENT๋ฅผ ๋ฐํํ๊ณ ํธ์ถ์๊ฐ ์คํจ ์ฒ๋ฆฌ.
*/
*foliop = NULL;
if (sgp == SGP_READ)
return 0;
if (sgp == SGP_NOALLOC)
return -ENOENT;
/* ๋น ๋ฅธ cache/swap ์กฐํ์์ ์ฐพ์ง ๋ชปํ์ผ๋ฏ๋ก ์ folio๋ฅผ ํ ๋นํ๋ค. */
if (vma && userfaultfd_missing(vma)) {
*fault_type = handle_userfault(vmf, VM_UFFD_MISSING);
return 0;
}
/* anonymous shmem๊ณผ tmpfs์ ํ์ฉ๋ hugepage order๋ฅผ ์ฐพ๋๋ค. */
orders = shmem_allowable_huge_orders(inode, vma, index, write_end, false);
if (orders > 0) {
gfp_t huge_gfp;
huge_gfp = vma_thp_gfp_mask(vma);
huge_gfp = limit_gfp_mask(huge_gfp, gfp);
folio = shmem_alloc_and_add_folio(vmf, huge_gfp,
inode, index, fault_mm, orders);
if (!IS_ERR(folio)) {
if (folio_test_pmd_mappable(folio))
count_vm_event(THP_FILE_ALLOC);
count_mthp_stat(folio_order(folio), MTHP_STAT_SHMEM_ALLOC);
goto alloced;
}
if (PTR_ERR(folio) == -EEXIST)
goto repeat;
}
๋ถ๊ธฐ ๋ก์ง:
1. filemap_get_entry()๋ก ํ์ด์ง ์บ์ ํ์
2. xa_is_value(folio) โ swap entry โ shmem_swapin_folio() ํธ์ถ
3. folio ์กด์ฌ + locked โ uptodate ํ์ธ ํ ๋ฐํ
4. ์์ โ SGP_READ/SGP_NOALLOC์ด๋ฉด ๋ฐํ, ๊ทธ ์ธ ํ ๋น
5. hugepage ํ์ฉ ์ shmem_alloc_and_add_folio() (large folio)
6. ์ผ๋ฐ ํ ๋น ์ shmem_alloc_and_add_folio() (order 0)
shmem_swapin_folio()
swap entry๋ฅผ ์ค์ folio๋ก ์ฝ์ด์ต๋๋ค.
/* mm/shmem.c:2293-2456 */
static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
struct folio **foliop, enum sgp_type sgp,
gfp_t gfp, struct vm_area_struct *vma,
vm_fault_t *fault_type)
/* mm/shmem.c:2335-2359 */
folio = swap_cache_get_folio(swap);
if (!folio) {
if (data_race(si->flags & SWP_SYNCHRONOUS_IO)) {
/* swap cache์ readahead๋ฅผ ๊ฑด๋๋ด direct swapin */
folio = shmem_swap_alloc_folio(inode, vma, index,
index_entry, order, gfp);
if (IS_ERR(folio)) {
error = PTR_ERR(folio);
folio = NULL;
goto failed;
}
} else {
/* cached swapin์ order 0 folio๋ง ์ง์ */
folio = shmem_swapin_cluster(swap, gfp, info, index);
if (!folio) {
error = -ENOMEM;
goto failed;
}
}
if (fault_type) {
*fault_type |= VM_FAULT_MAJOR;
count_vm_event(PGMAJFAULT);
count_memcg_event_mm(fault_mm, PGMAJFAULT);
}
} else {
swap_update_readahead(folio, NULL, 0);
}
/* mm/shmem.c:2425-2437 */
error = shmem_add_to_page_cache(folio, mapping, index,
swp_to_radix_entry(swap), gfp);
if (error)
goto failed;
shmem_recalc_inode(inode, 0, -nr_pages);
if (sgp == SGP_WRITE)
folio_mark_accessed(folio);
folio_put_swap(folio, NULL);
swap_cache_del_folio(folio);
folio_mark_dirty(folio);
ํ๋ฆ:
1. radix_to_swp_entry()๋ก swap entry ๋ณํ
2. shmem_confirm_swap()์ผ๋ก swap entry ์ ํจ์ฑ ํ์ธ
3. swap_cache_get_folio()๋ก swap cache์์ ๊ธฐ์กด folio ํ์
4. ์์ผ๋ฉด SWP_SYNCHRONOUS_IO ํ๋๊ทธ์ ๋ฐ๋ผ:
- ์ง๋ ฌ IO: shmem_swap_alloc_folio() (๋๊ธฐ ์์ด ์ง์ ํ ๋น)
- ๋น์ง๋ ฌ: shmem_swapin_cluster() (readahead ํฌํจ)
5. swap entry ๋ถํ ํ์ ์ shmem_split_large_entry() ํธ์ถ
6. shmem_add_to_page_cache()๋ก ํ์ด์ง ์บ์์ ์ถ๊ฐ
7. folio_put_swap() + swap_cache_del_folio()๋ก swap cache์์ ์ ๊ฑฐ
shmem_alloc_and_add_folio()
์ folio๋ฅผ ํ ๋นํ๊ณ ํ์ด์ง ์บ์์ ์ถ๊ฐํฉ๋๋ค.
/* mm/shmem.c:1936-2038 */
static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
gfp_t gfp, struct inode *inode, pgoff_t index,
struct mm_struct *fault_mm, unsigned long orders)
/* mm/shmem.c:1978-2031 */
__folio_set_locked(folio);
__folio_set_swapbacked(folio);
gfp &= GFP_RECLAIM_MASK;
error = mem_cgroup_charge(folio, fault_mm, gfp);
if (error) {
if (xa_find(&mapping->i_pages, &index,
index + pages - 1, XA_PRESENT)) {
error = -EEXIST;
} else if (pages > 1) {
if (pages == HPAGE_PMD_NR) {
count_vm_event(THP_FILE_FALLBACK);
count_vm_event(THP_FILE_FALLBACK_CHARGE);
}
count_mthp_stat(folio_order(folio), MTHP_STAT_SHMEM_FALLBACK);
count_mthp_stat(folio_order(folio), MTHP_STAT_SHMEM_FALLBACK_CHARGE);
}
goto unlock;
}
error = shmem_add_to_page_cache(folio, mapping, index, NULL, gfp);
if (error)
goto unlock;
error = shmem_inode_acct_blocks(inode, pages);
if (error) {
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
long freed;
/*
* ํ์ผ์์คํ
์์ i_size ๋ฐ large folio๋ฅผ ์กฐ๊ธ ์ชผ๊ฐ์
* ๊ณต๊ฐ์ ๋์ฐพ์ ๋ณธ๋ค.
*/
shmem_unused_huge_shrink(sbinfo, NULL, pages);
/*
* freed page๋ฅผ ๋ฐ์ํ๋ ค๊ณ shmem_recalc_inode()์ ๋น์ทํ ์ ๋ฆฌ๋ฅผ ํ๋ค.
* ๋ค๋ง ํ์ฌ folio๋ ์์ง cache์ ์์ผ๋ฏ๋ก ์์ ํ ๊ท ํ์ด ๋ง์ง๋ ์๋๋ค.
*/
spin_lock(&info->lock);
freed = pages + info->alloced - info->swapped -
READ_ONCE(mapping->nrpages);
if (freed > 0)
info->alloced -= freed;
spin_unlock(&info->lock);
if (freed > 0)
shmem_inode_unacct_blocks(inode, freed);
error = shmem_inode_acct_blocks(inode, pages);
if (error) {
filemap_remove_folio(folio);
goto unlock;
}
}
shmem_recalc_inode(inode, pages, 0);
folio_add_lru(folio);
ํ๋ฆ:
1. shmem_suitable_orders()๋ก ์ฌ์ฉ ๊ฐ๋ฅํ huge page order ํ์ธ
2. shmem_alloc_folio()๋ก NUMA ์ ์ฑ
์ ์ฉํ์ฌ ํ ๋น
3. mem_cgroup_charge()๋ก memcg ์ถฉ์
4. shmem_add_to_page_cache()๋ก XArray์ ์ถ๊ฐ
5. shmem_inode_acct_blocks()๋ก ๋ธ๋ก ํ ๋น๋ ํ์ธ
6. shmem_recalc_inode()๋ก inode ํต๊ณ ๊ฐฑ์
shmem_writeout()
folio๋ฅผ swap์ผ๋ก ๋ด๋ณด๋ ๋๋ค.
/* mm/shmem.c:1590-1737 */
int shmem_writeout(struct folio *folio, struct swap_iocb **plug,
struct list_head *folio_list)
/* mm/shmem.c:1601-1642 */
if ((info->flags & SHMEM_F_LOCKED) || sbinfo->noswap)
goto redirty;
if (!total_swap_pages)
goto redirty;
if (folio_test_large(folio)) {
index = shmem_fallocend(inode,
DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE));
if ((index > folio->index && index < folio_next_index(folio)) ||
!IS_ENABLED(CONFIG_THP_SWAP))
split = true;
}
if (split) {
int order;
try_split:
order = folio_order(folio);
/* subpage๊ฐ ์ฌ์ ํ dirty ์ํ๊ฐ ๋๋๋ก ๋ณด์ฅ */
folio_test_set_dirty(folio);
if (split_folio_to_list(folio, folio_list))
goto redirty;
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
if (order >= HPAGE_PMD_ORDER) {
count_memcg_folio_events(folio, THP_SWPOUT_FALLBACK, 1);
count_vm_event(THP_SWPOUT_FALLBACK);
}
#endif
count_mthp_stat(order, MTHP_STAT_SWPOUT_FALLBACK);
folio_clear_dirty(folio);
}
/* mm/shmem.c:1679-1703 */
if (!folio_alloc_swap(folio)) {
bool first_swapped = shmem_recalc_inode(inode, 0, nr_pages);
/*
* ์์ง swaplist์ ์์ผ๋ฉด ์ง๊ธ ์ถ๊ฐํด์ shmem_unuse()๊ฐ
* swap๋ inode๋ฅผ ๋ค์ ์ฐพ์ ์ ์๊ฒ ํ๋ค.
*/
if (first_swapped) {
spin_lock(&shmem_swaplist_lock);
if (list_empty(&info->swaplist))
list_add(&info->swaplist, &shmem_swaplist);
spin_unlock(&shmem_swaplist_lock);
}
folio_dup_swap(folio, NULL);
shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));
BUG_ON(folio_mapped(folio));
error = swap_writeout(folio, plug);
if (error != AOP_WRITEPAGE_ACTIVATE)
return error;
}
ํ๋ฆ:
1. SHMEM_F_LOCKED ๋๋ noswap์ด๋ฉด ์ฆ์ redirty
2. large folio์ธ ๊ฒฝ์ฐ split_folio_to_list()๋ก ๋ถํ
3. folio_alloc_swap()์ผ๋ก swap ํ ๋น
4. shmem_delete_from_page_cache()๋ก ์บ์์์ ์ ๊ฑฐ
5. swap_writeout()์ผ๋ก ์ค์ swap ๋์คํฌ์ ๊ธฐ๋ก
6. ์คํจ ์ ์บ์์ ๋ค์ ์ถ๊ฐ
shmem_falloc_wait()
hole punch ์งํ ์ค ๋ฐ์ํ ํ์ด์ง ํดํธ๋ฅผ ๋๊ธฐ์ํต๋๋ค.
/* mm/shmem.c:2708-2747 */
static vm_fault_t shmem_falloc_wait(struct vm_fault *vmf, struct inode *inode)
/* mm/shmem.c:2714-2741 */
spin_lock(&inode->i_lock);
shmem_falloc = inode->i_private;
if (shmem_falloc &&
shmem_falloc->waitq &&
vmf->pgoff >= shmem_falloc->start &&
vmf->pgoff < shmem_falloc->next) {
wait_queue_head_t *shmem_falloc_waitq;
DEFINE_WAIT_FUNC(shmem_fault_wait, synchronous_wake_function);
ret = VM_FAULT_NOPAGE;
fpin = maybe_unlock_mmap_for_io(vmf, NULL);
shmem_falloc_waitq = shmem_falloc->waitq;
prepare_to_wait(shmem_falloc_waitq, &shmem_fault_wait,
TASK_UNINTERRUPTIBLE);
spin_unlock(&inode->i_lock);
schedule();
/*
* shmem_falloc_waitq๋ hole-punch task์ stack์ ๊ฐ๋ฆฌํจ๋ค.
* ์ฌ๊ธฐ ๋๋ฌํ ๋์ฏค ๋ฌดํจ๊ฐ ๋ ์ ์์ง๋ง, ๊ทธ ๊ฒฝ์ฐ finish_wait()๋
* ์ญ์ฐธ์กฐํ์ง ์๋๋ค. ๋ค๋ง wake_up_all()๊ณผ์ ๊ฒฝํฉ ๋๋ฌธ์ i_lock์ ํ์ํ๋ค.
*/
spin_lock(&inode->i_lock);
finish_wait(shmem_falloc_waitq, &shmem_fault_wait);
}
spin_unlock(&inode->i_lock);
๋์:
1. inode->i_private๊ฐ shmem_falloc์ด๋ฉด
2. waitq์์ TASK_UNINTERRUPTIBLE๋ก ๋๊ธฐ
3. hole punch ์๋ฃ ํ wake_up์ผ๋ก ๊นจ์ด๋จ
shmem_fault()
VMA fault ํธ๋ค๋ฌ๋ก, hole punch ๋๊ธฐ์ shmem_get_folio_gfp() ํธ์ถ์ ์ฐ๊ฒฐํฉ๋๋ค.
/* mm/shmem.c:2749-2777 */
static vm_fault_t shmem_fault(struct vm_fault *vmf)
/* mm/shmem.c:2761-2776 */
if (unlikely(inode->i_private)) {
ret = shmem_falloc_wait(vmf, inode);
if (ret)
return ret;
}
err = shmem_get_folio_gfp(inode, vmf->pgoff, 0, &folio, SGP_CACHE,
gfp, vmf, &ret);
if (err)
return vmf_error(err);
if (folio) {
vmf->page = folio_file_page(folio, vmf->pgoff);
ret |= VM_FAULT_LOCKED;
}
return ret;
์ญํ :
1. inode->i_private๊ฐ ์ด์ ์์ผ๋ฉด punch/fallocate ๊ฒฝํฉ์ ๋จผ์ ํด์
2. SGP_CACHE๋ก ์บ์ ์กฐํ, swapin, ์ folio ํ ๋น์ ํตํฉ ์ฒ๋ฆฌ
3. ์ต์ข
์ ์ผ๋ก vmf->page์ fault ๋์ subpage๋ฅผ ์ฐ๊ฒฐํ๊ณ VM_FAULT_LOCKED๋ฅผ ๋ฐํ
ํธ์ถ ํ๋ฆ
shmem_fault (VMA ํดํธ ์ง์
์ )
โโโ shmem_falloc_wait (hole punch ๋๊ธฐ)
โโโ shmem_get_folio_gfp
โโโ filemap_get_entry โ ํ์ด์ง ์บ์ ํ์
โโโ shmem_swapin_folio โ swap์์ ์ฝ๊ธฐ
โ โโโ swap_cache_get_folio โ swap cache ํํธ
โ โโโ shmem_swap_alloc_folio โ ์ folio ํ ๋น
โ โโโ shmem_split_large_entry โ large swap entry ๋ถํ
โ โโโ shmem_add_to_page_cache โ ์บ์์ ์ถ๊ฐ
โโโ shmem_alloc_and_add_folio โ ์ folio ํ ๋น
โ โโโ shmem_alloc_folio โ ๋ฉ๋ชจ๋ฆฌ ํ ๋น
โ โโโ mem_cgroup_charge โ memcg ์ถฉ์
โ โโโ shmem_add_to_page_cache โ ์บ์์ ์ถ๊ฐ
โโโ shmem_recalc_inode โ ํต๊ณ ๊ฐฑ์
shmem_writeout (swap ๋ด๋ณด๋ด๊ธฐ)
โโโ split_folio_to_list โ large folio ๋ถํ
โโโ folio_alloc_swap โ swap ํ ๋น
โโโ shmem_delete_from_page_cache โ ์บ์ ์ ๊ฑฐ
โโโ swap_writeout โ swap ๋์คํฌ ๊ธฐ๋ก
shmem_undo_range (truncate/punch hole)
โโโ find_lock_entries โ ๋์ folio ๊ฒ์
โโโ truncate_inode_folio โ folio ์ ๊ฑฐ
โโโ shmem_free_swap โ swap entry ํด์
shmem_unuse (swap off)
โโโ shmem_unuse_inode โ inode ๋ด swap ํ์
โ โโโ shmem_find_swap_entries โ swap entry ์ฐพ๊ธฐ
โ โโโ shmem_unuse_swap_entries โ swap โ ์บ์ ๋ณต์
โโโ shmem_unuse_inode ๋ฐ๋ณต
shmem_fault โ shmem_get_folio_gfp โ shmem_writeout โ swap_writeout
์กฐ๊ฑด๋ณ ๋น๊ต
SGP (shmem_get_folio) ํ์ ๋ณ ๋์
/* include/linux/shmem_fs.h:166-171 */
enum sgp_type {
SGP_READ, // i_size๋ฅผ ๋์ง ์๊ณ ํ์ด์ง๋ฅผ ํ ๋นํ์ง ์์
SGP_NOALLOC, // ๋น์ทํ์ง๋ง hole์์๋ ์คํจํ๊ฑฐ๋ fallocated ํ์ด์ง๋ฅผ ์ฌ์ฉ
SGP_CACHE, // i_size๋ฅผ ๋์ง ์์ผ๋ฉฐ ํ์ํ๋ฉด ํ์ด์ง๋ฅผ ํ ๋นํจ
SGP_WRITE, // i_size๋ฅผ ๋์ ์ ์๊ณ !Uptodate ํ์ด์ง๋ ํ ๋นํจ
SGP_FALLOC, // SGP_WRITE์ ๋น์ทํ์ง๋ง ๊ธฐ์กด ํ์ด์ง๋ฅผ Uptodate๋ก ๋ง๋ฆ
};
| SGP ํ์ | i_size ์ด๊ณผ | ํ์ด์ง ํ ๋น | ์ฌ์ฉ ์์ |
|---|---|---|---|
| SGP_READ | ๋ถ๊ฐ | ๋ถ๊ฐ | ์ฝ๊ธฐ๋ง, hole์ NULL ๋ฐํ |
| SGP_NOALLOC | ๋ถ๊ฐ | ๋ถ๊ฐ | hole์ด๋ฉด -ENOENT ๋ฐํ |
| SGP_CACHE | ๋ถ๊ฐ | ๊ฐ๋ฅ | ์ผ๋ฐ ์ฝ๊ธฐ/์ฐ๊ธฐ (ํ์ด์ง ํดํธ) |
| SGP_WRITE | ๊ฐ๋ฅ | ๊ฐ๋ฅ | ์ฐ๊ธฐ ์, new folio์ referenced ์ค์ |
| SGP_FALLOC | ๊ฐ๋ฅ | ๊ฐ๋ฅ | fallocate, existing folio ์ด๊ธฐํ |
Huge Page ์ง์ ๋ชจ๋
| ๋ชจ๋ | ๋์ | ๋ง์ดํธ ์ต์ |
|---|---|---|
| SHMEM_HUGE_NEVER (0) | huge page ์ฌ์ฉ ์ํจ | `huge=never` |
| SHMEM_HUGE_ALWAYS (1) | ํญ์ huge page ์๋ | `huge=always` |
| SHMEM_HUGE_WITHIN_SIZE (2) | i_size ๋ด์ ์์ ๋๋ง | `huge=within_size` |
| SHMEM_HUGE_ADVISE (3) | madvise(MADV_HUGEPAGE) ์์๋ง | `huge=advise` |
| SHMEM_HUGE_DENY (-1) | ์ ์ฒด ๋นํ์ฑํ (๊ธด๊ธ์ฉ) | sysfs์์ ์ค์ |
| SHMEM_HUGE_FORCE (-2) | ์ ์ฒด ๊ฐ์ ํ์ฑํ (ํ ์คํธ์ฉ) | sysfs์์ ์ค์ |
fallocate ๋ชจ๋๋ณ ๋์
| ๋ชจ๋ | ๋์ | ๋ฐํ๊ฐ |
|---|---|---|
| ๊ธฐ๋ณธ (0) | ํ ๋น + zeroing | 0 |
| FALLOC_FL_KEEP_SIZE | i_size ๋ณ๊ฒฝ ์์ด ํ ๋น | 0 |
| FALLOC_FL_PUNCH_HOLE | hole punch + truncate | 0 |
| SHMEM_F_MAPPING_FROZEN | ๋ชจ๋ ๋ณ๊ฒฝ ๋ถ๊ฐ | -EPERM |
| F_SEAL_GROW | ํ์ฅ ๋ถ๊ฐ | -EPERM |
| F_SEAL_SHRINK | ์ถ์ ๋ถ๊ฐ | -EPERM |
| F_SEAL_WRITE | ์ฐ๊ธฐ ๋ถ๊ฐ | -EPERM |
Swap In ๊ฒฝ๋ก ์ ํ
| ์กฐ๊ฑด | ๊ฒฝ๋ก | ์ค๋ช |
|---|---|---|
| SWP_SYNCHRONOUS_IO | shmem_swap_alloc_folio | ๋๊ธฐ ์์ด ์ง์ ํ ๋น |
| cached swap | swap_cache_get_folio | ๊ธฐ์กด swap cache ํํธ |
| !SWP_SYNCHRONOUS_IO | shmem_swapin_cluster | readahead ํฌํจ |
| uffd armed | order 0 fallback | per-page fault ๋ณด์ฅ |
| zswap ๋นํ์ฑํ | order 0 fallback | zswap ๊ณ ๋ ค ์์ |