๏ปฟ# GUP (Get User Pages) โ Linux 7.0
1. ๊ฐ์
Get User Pages(GUP)๋ ์ปค๋์ด ์ฌ์ฉ์ ๊ณต๊ฐ์ ๋ฌผ๋ฆฌ ํ์ด์ง๋ฅผ ์ง์ ์ฐธ์กฐํ๊ฑฐ๋ ๊ณ ์ (pin)ํ๋ ๋ฉ์ปค๋์ฆ์ด๋ค. ์ฌ์ฉ์ ํ๋ก์ธ์ค์ ๊ฐ์ ์ฃผ์์ ๋งคํ๋ ๋ฌผ๋ฆฌ ํ์ด์ง๋ฅผ ์ปค๋์ด ์ฐพ์์ ์ฐธ์กฐ ์นด์ดํธ๋ฅผ ์ฌ๋ ค, ํด๋น ํ์ด์ง๊ฐ ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋์ง ์๋๋ก ๋ณด์ฅํ๋ค. ๋ํ์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ก DMA ๋ฒํผ ํ ๋น, RDMA, virtio, ๋๋ฒ๊ทธ ๋๊ตฌ, ์ฝ์ด ๋คํ ๋ฑ์ด ์๋ค.
GUP์ ํฌ๊ฒ ๋ ๊ฐ์ง ๊ฒฝ๋ก๋ก ๋์ํ๋ค: slow path๋ mmap_lock์ ํ๋ํ ํ VMA ๊ฒ์ฆ, ํ์ด์ง ํ
์ด๋ธ ์ํ, ํ์ด์ง ํดํธ ์ฒ๋ฆฌ๋ฅผ ์ํํ๊ณ , fast path๋ IRQ๋ฅผ ๋นํ์ฑํํ์ฌ ํ์ด์ง ํ
์ด๋ธ์ ์ง์ ์ํํ์ฌ ์ ๊ธ ์์ด ๋น ๋ฅด๊ฒ ํ์ด์ง๋ฅผ ํํ๋ค. fast path์์ ์คํจํ๋ฉด slow path๋ก ํด๋ฐฑํ๋ค.
์์ค ํ์ผ: mm/gup.c, include/linux/mm_types.h, include/linux/mm.h
2. ๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# GUP ๊ด๋ จ ์นด์ดํฐ ํ์ธ
grep -E "nr_foll_pin_(acquired|released)" /proc/vmstat
# ํ์ฌ ์์คํ
์์ pin_user_pages ์ฌ์ฉ ํํฉ (kprobe๊ฐ ํ์ํ ๊ฒฝ์ฐ)
sudo perf probe -l | grep -E "gup|pin_user_pages|unpin_user_page"
# ๋ฉ๋ชจ๋ฆฌ ๋งต์์ GUP์ด ์ฌ์ฉํ ์ ์๋ ์์ญ ํ์ธ
sed -n '1,20p' /proc/self/maps
# ํ์ด์ง ํ
์ด๋ธ ๊ตฌ์กฐ ํ์ธ
od -An -tx8 -N 40 /proc/self/pagemap 2>/dev/null || echo "pagemap ์ ๊ทผ ๊ถํ ํ์"
# GUP ๊ด๋ จ ์ปค๋ ๋ก๊ทธ ํ์ธ
dmesg | grep -i "pin\|gup" | tail -10
# ํ์ด์ง ์ฐธ์กฐ ์นด์ดํธ ํ์ธ (ํด๋น ํ์ด์ง๊ฐ ํ๋์ด ์๋์ง)
od -An -tu8 -N 40 /proc/kpagecount 2>/dev/null || echo "kpagecount ์ ๊ทผ ๊ถํ ํ์"
# GUP ๊ด๋ จ ์ฌ๋ณผ ํ์ธ
grep -E "get_user_pages|pin_user_pages|unpin_user_page" /proc/kallsyms | head -20
# GUP fast path ๊ด๋ จ ์ปค๋ ์ค์ ํ์ธ
zcat /proc/config.gz 2>/dev/null | grep -E "HAVE_GUP_FAST|CONFIG_MMU|CONFIG_DEBUG_VM" || grep -E "HAVE_GUP_FAST|CONFIG_MMU|CONFIG_DEBUG_VM" /boot/config-$(uname -r)
3. ํต์ฌ ์๋ฃ๊ตฌ์กฐ
3.1 FOLL_* ํ๋๊ทธ (`include/linux/mm_types.h:1809-1852`)
GUP ํจ์๋ค์ ๋์์ ์ ์ดํ๋ ํ๋๊ทธ ๋ชจ์์ด๋ค. ๊ฐ ํ๋๊ทธ๋ ํ์ด์ง ํ์ ์ฉ๋์ ์กฐ๊ฑด์ ์ ์ํ๋ค.
// include/linux/mm_types.h:1809-1852
enum {
/* pte๊ฐ writable์ธ์ง ํ์ธ */
FOLL_WRITE = 1 << 0,
/* page์์ get_page ์ํ */
FOLL_GET = 1 << 1,
/* ๊ตฌ๋ฉ์ด 0์ด ๋ ๊ฒฝ์ฐ ์ค๋ฅ ๋ฐํ */
FOLL_DUMP = 1 << 2,
/* ๊ถํ ์์ด get_user_pages ์ฝ๊ธฐ/์ฐ๊ธฐ */
FOLL_FORCE = 1 << 3,
/* ๋์คํฌ ์ ์ก์ด ํ์ํ๋ฉด IO๋ฅผ ์์ํ๊ณ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ฐํ */
FOLL_NOWAIT = 1 << 4,
/* ํ์ด์ง๋ฅผ fault-in ํ์ง ์์ */
FOLL_NOFAULT = 1 << 5,
/* hwpoison ์ฌ๋ถ๋ฅผ ํ์ธ */
FOLL_HWPOISON = 1 << 6,
/* ํ์ผ ๋งคํ์ ํ์ง ์์ */
FOLL_ANON = 1 << 7,
/* FOLL_LONGTERM์ ํ์ด์ง๊ฐ ๋ฌด๊ธฐํ์ ๊ฐ๊น์ด ๊ธฐ๊ฐ ๋์ ์กํ ์์ ์ ์์์ ๋ปํจ */
FOLL_LONGTERM = 1 << 8,
/* ๋ฐํ ์ ์ huge pmd๋ฅผ ๋ถํ */
FOLL_SPLIT_PMD = 1 << 9,
/* PCI P2PDMA ํ์ด์ง ๋ฐํ ํ์ฉ */
FOLL_PCI_P2PDMA = 1 << 10,
/* ์ผ๋ฐ signal์์ ์ธํฐ๋ฝํธ ํ์ฉ */
FOLL_INTERRUPTIBLE = 1 << 11,
/* ํญ์ NUMA ํํธ fault๋ฅผ ๋ฐ์ */
FOLL_HONOR_NUMA_FAULT = 1 << 12,
};
3.2 GUP Pin ์นด์ดํ ๋ฉ์ปค๋์ฆ
FOLL_GET๊ณผ FOLL_PIN์ ์๋ก ๋ค๋ฅธ ์ฐธ์กฐ ์นด์ดํ
๋ฐฉ์์ ์ฌ์ฉํ๋ค. FOLL_GET์ page->_refcount๋ง ์ฆ๊ฐ์ํค์ง๋ง, FOLL_PIN์ ๋ํ ํด๋ฆฌ์ค์ ๊ฒฝ์ฐ ๋ณ๋์ _pincount ํ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ ํํ ํ ์๋ฅผ ์ถ์ ํ๋ค.
// mm/gup.c:140-174 โ try_grab_folio()
int __must_check try_grab_folio(struct folio *folio, int refs,
unsigned int flags)
{
if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
return -ENOMEM;
if (unlikely(!(flags & FOLL_PCI_P2PDMA) && folio_is_pci_p2pdma(folio)))
return -EREMOTEIO;
if (flags & FOLL_GET)
folio_ref_add(folio, refs);
else if (flags & FOLL_PIN) {
/* zero page์๋ pin์ ๊ฑธ์ง ์๋๋ค. ์์ง์ด์ง๋ ์๊ณ , ๋ง์ ๊ณณ์์ ์ฌ์ฉ๋๋ค. */
if (is_zero_folio(folio))
return 0;
/* ํ์ด์ง๊ฐ ์ ๋ง pin๋๋๋ก normal page refcount๋ ์ต์ ํ ๋ฒ์ ์ฆ๊ฐ์ํจ๋ค. */
if (folio_has_pincount(folio)) {
folio_ref_add(folio, refs);
atomic_add(refs, &folio->_pincount);
} else {
folio_ref_add(folio, refs * GUP_PIN_COUNTING_BIAS);
}
node_stat_mod_folio(folio, NR_FOLL_PIN_ACQUIRED, refs);
}
return 0;
}
3.3 pages_or_folios ๊ตฌ์กฐ์ฒด (`mm/gup.c:2200-2215`)
ํ์ด์ง ๋ฐฐ์ด๊ณผ ํด๋ฆฌ์ค ๋ฐฐ์ด์ ๋ชจ๋ ์ง์ํ๋ ๋ํผ ๊ตฌ์กฐ์ฒด์ด๋ค. FOLL_LONGTERM ๋ง์ด๊ทธ๋ ์ด์ ์ฒ๋ฆฌ์ ์ฌ์ฉ๋๋ค.
// mm/gup.c:2200-2215
/*
* pages ๋๋ folio ๋ฐฐ์ด ์ค ํ๋๋ฅผ ๋ด๋๋ค.
* struct page์ struct folio์ ๋ฐฐ์น๊ฐ ์์ผ๋ก ์์ ํ ๋ฌ๋ผ์ง ์ ์์ผ๋ฏ๋ก,
* ๋จ์ํ folio ๋ฐฐ์ด์ page ๋ฐฐ์ด์ฒ๋ผ ํด์ํ์ง ์๋๋ค.
* ๋ํ page_folio() ํธ์ถ์ ๊ณผ๋ํ๊ฒ ๋ฐ๋ณตํ์ง ์๊ฒ ํด ์ค๋ค.
*/
struct pages_or_folios {
union {
struct page **pages;
struct folio **folios;
void **entries;
};
bool has_folios; // folios ์ฌ์ฉ ์ฌ๋ถ
long nr_entries; // ์ํธ๋ฆฌ ์
};
3.4 FOLL ํ๋๊ทธ ๋ถ๋ฅ ํ
| ๊ตฌ๋ถ | ํ๋๊ทธ | ์ค๋ช |
|---|---|---|
| ์ ๊ทผ ์ ์ด | FOLL_WRITE | ์ฐ๊ธฐ ์ ๊ทผ ํ์ฉ |
| FOLL_FORCE | COW ๋งคํ์์๋ ์ฐ๊ธฐ ํ์ฉ | |
| FOLL_ANON | ์ต๋ช ํ์ด์ง๋ง ๋์ | |
| ํ ๋น ๋ฐฉ์ | FOLL_GET | put_page()๋ก ํด์ |
| FOLL_PIN | unpin_user_page()๋ก ํด์ | |
| FOLL_LONGTERM | ์ฅ์๊ฐ ํ (๋ง์ด๊ทธ๋ ์ด์ ๋๋ฐ) | |
| ๋์ ์ ์ด | FOLL_NOWAIT | ํดํธ ์ ์ฆ์ ๋ฐํ |
| FOLL_NOFAULT | ํดํธ ๋ฏธ๋ฐ์ | |
| FOLL_INTERRUPTIBLE | ์ ํธ๋ก ์ค๋จ ๊ฐ๋ฅ | |
| ํน์ ์ฉ๋ | FOLL_DUMP | ์ฝ์ด ๋คํ |
| FOLL_HWPOISON | ํ๋์จ์ด ์ค๋ฅ ํ์ด์ง | |
| FOLL_PCI_P2PDMA | PCI P2P DMA | |
| FOLL_SPLIT_PMD | THP ๋ถํ | |
| FOLL_HONOR_NUMA_FAULT | NUMA ํดํธ ์ฒ๋ฆฌ |
4. ํต์ฌ ํจ์
4.1 get_user_pages() (`mm/gup.c:2644-2655`)
ํ์ฌ ํ๋ก์ธ์ค์ ์ฌ์ฉ์ ํ์ด์ง๋ฅผ ํํ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ API์ด๋ค. ๋ด๋ถ์ ์ผ๋ก __get_user_pages_locked()๋ฅผ ํธ์ถํ๋ค.
// mm/gup.c:2644-2655
long get_user_pages(unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages)
{
int locked = 1;
if (!is_valid_gup_args(pages, NULL, &gup_flags, FOLL_TOUCH))
return -EINVAL;
return __get_user_pages_locked(current->mm, start, nr_pages, pages,
&locked, gup_flags);
}
EXPORT_SYMBOL(get_user_pages);
4.2 pin_user_pages() (`mm/gup.c:3376-3386`)
DMA ๋ฒํผ ํ ๋น ๋ฑ์ ์ํด ํ์ด์ง๋ฅผ ์ฅ์๊ฐ ๊ณ ์ ํ ๋ ์ฌ์ฉํ๋ค. FOLL_PIN ํ๋๊ทธ๊ฐ ์ค์ ๋์ด ๋ณ๋์ ํ ์นด์ดํ ์ ์ฌ์ฉํ๋ค.
// mm/gup.c:3376-3386
long pin_user_pages(unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages)
{
int locked = 1;
if (!is_valid_gup_args(pages, NULL, &gup_flags, FOLL_PIN))
return 0;
return __gup_longterm_locked(current->mm, start, nr_pages,
pages, &locked, gup_flags);
}
EXPORT_SYMBOL(pin_user_pages);
4.3 get_user_pages_fast() (`mm/gup.c:3276-3289`)
mmap_lock ์์ด ๋น ๋ฅด๊ฒ ํ์ด์ง๋ฅผ ํํ๋ ํจ์์ด๋ค. fast path์์ ์คํจํ๋ฉด slow path๋ก ํด๋ฐฑํ๋ค.
// mm/gup.c:3276-3289
int get_user_pages_fast(unsigned long start, int nr_pages,
unsigned int gup_flags, struct page **pages)
{
/* ํธ์ถ์๊ฐ FOLL_GET์ ๋ช
์ํ์ ์๋ ์๊ณ ์๋ ์๋ ์๋ค. ๋ ๋ค ๊ฐ๋ฅํ๋ค.
* ํ์ง๋ง mm/gup.c ๋ด๋ถ์ ๋น ๋ฅธ ๊ฒฝ๋ก๋ ๋ฐ๋์ FOLL_GET์ ์ค์ ํด์ผ ํ๋ค.
* gup fast๋ ํญ์ "page refcount +1" ์ฑ๊ฒฉ์ ์์ฒญ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
*/
if (!is_valid_gup_args(pages, NULL, &gup_flags, FOLL_GET))
return -EINVAL;
return gup_fast_fallback(start, nr_pages, gup_flags, pages);
}
EXPORT_SYMBOL_GPL(get_user_pages_fast);
4.4 faultin_page() (`mm/gup.c:1087-1160`)
ํ์ด์ง ํดํธ๋ฅผ ์๋์ผ๋ก ๋ฐ์์์ผ ํ์ด์ง๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ๋ค. VMA ๊ถํ ๊ฒ์ฆ ํ handle_mm_fault()๋ฅผ ํธ์ถํ๋ค.
// mm/gup.c:1087-1160
static int faultin_page(struct vm_area_struct *vma,
unsigned long address, unsigned int flags, bool unshare,
int *locked)
{
unsigned int fault_flags = 0;
vm_fault_t ret;
if (flags & FOLL_NOFAULT)
return -EFAULT;
if (flags & FOLL_WRITE)
fault_flags |= FAULT_FLAG_WRITE;
if (flags & FOLL_REMOTE)
fault_flags |= FAULT_FLAG_REMOTE;
if (flags & FOLL_UNLOCKABLE) {
fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
/* FAULT_FLAG_INTERRUPTIBLE์ ์ ํ ์ฌํญ์ด๋ค. GUP ํธ์ถ์๊ฐ FOLL_INTERRUPTIBLE์ ์ค์ผ ์ผ ๋ค. */
if (flags & FOLL_INTERRUPTIBLE)
fault_flags |= FAULT_FLAG_INTERRUPTIBLE;
}
if (flags & FOLL_NOWAIT)
fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT;
if (flags & FOLL_TRIED) {
/* FAULT_FLAG_ALLOW_RETRY์ FAULT_FLAG_TRIED๋ ํจ๊ป ์กด์ฌํ ์ ์๋ค. */
fault_flags |= FAULT_FLAG_TRIED;
}
if (unshare) {
fault_flags |= FAULT_FLAG_UNSHARE;
/* FAULT_FLAG_WRITE์ FAULT_FLAG_UNSHARE๋ ํจ๊ป ์ธ ์ ์๋ค. */
VM_WARN_ON_ONCE(fault_flags & FAULT_FLAG_WRITE);
}
ret = handle_mm_fault(vma, address, fault_flags, NULL);
if (ret & VM_FAULT_COMPLETED) {
/* FAULT_FLAG_RETRY_NOWAIT๋ฅผ ์ผ๋ค๋ฉด page fault handler๊ฐ mmap lock์ ๋์ง ์๋๋ค. */
WARN_ON_ONCE(fault_flags & FAULT_FLAG_RETRY_NOWAIT);
*locked = 0;
/* VM_FAULT_RETRY์ ๊ฐ์ ์๋ฏธ๋ฅผ ๋๊ณ ์ถ์ง๋ง -EBUSY๋ ์ค์ ์ํฉ์ ์ ์ค๋ช
ํ์ง ๋ชปํ๋ค. */
return -EAGAIN;
}
if (ret & VM_FAULT_ERROR) {
int err = vm_fault_to_errno(ret, flags);
if (err)
return err;
BUG();
}
if (ret & VM_FAULT_RETRY) {
if (!(fault_flags & FAULT_FLAG_RETRY_NOWAIT))
*locked = 0;
return -EBUSY;
}
return 0;
}
4.5 follow_page_mask() (`mm/gup.c:1007-1028`)
ํ์ด์ง ํ ์ด๋ธ์ ์ํํ์ฌ ์ฌ์ฉ์ ๊ฐ์ ์ฃผ์์ ํด๋นํ๋ ํ์ด์ง๋ฅผ ์ฐพ๋๋ค.
// mm/gup.c:1007-1028
static struct page *follow_page_mask(struct vm_area_struct *vma,
unsigned long address, unsigned int flags,
unsigned long *page_mask)
{
pgd_t *pgd;
struct mm_struct *mm = vma->vm_mm;
struct page *page;
vma_pgtable_walk_begin(vma);
*page_mask = 0;
pgd = pgd_offset(mm, address);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
page = no_page_table(vma, flags, address);
else
page = follow_p4d_mask(vma, address, pgd, flags, page_mask);
vma_pgtable_walk_end(vma);
return page;
}
5. ํธ์ถ ํ๋ฆ
5.1 Slow Path (get_user_pages)
get_user_pages()
โโ __get_user_pages_locked()
โโ mmap_read_lock() // mmap_lock ํ๋
โโ __get_user_pages()
โโ gup_vma_lookup() // VMA ๊ฒ์
โโ check_vma_flags() // ๊ถํ ๊ฒ์ฆ
โโ follow_page_mask() // ํ์ด์ง ํ
์ด๋ธ ์ํ
โ โโ pgd_offset()
โ โโ follow_p4d_mask()
โ โโ follow_pud_mask()
โ โโ follow_pmd_mask()
โ โ โโ follow_page_pte() // PTE ๋ ๋ฒจ
โ โ โโ follow_huge_pmd() // THP/HugeTLB
โ โโ try_grab_folio() // ์ฐธ์กฐ ์นด์ดํธ ์ฆ๊ฐ
โโ faultin_page() // ํดํธ ๋ฐ์ ์
โ โโ handle_mm_fault()
โโ mmap_read_unlock() // mmap_lock ํด์
5.2 Fast Path (get_user_pages_fast)
get_user_pages_fast()
โโ gup_fast_fallback()
โโ gup_fast()
โ โโ raw_seqcount_try_begin() // write_protect_seq ํ์ธ
โ โโ local_irq_save() // IRQ ๋นํ์ฑํ
โ โโ gup_fast_pgd_range() // ํ์ด์ง ํ
์ด๋ธ ์ํ
โ โ โโ gup_fast_pte_range() // ์ง์ PTE ์ํ
โ โ โโ ptep_get_lockless()
โ โ โโ try_grab_folio_fast()
โ โ โโ gup_fast_folio_allowed()
โ โโ local_irq_restore()
โโ __gup_longterm_locked() // ๋จ์ ํ์ด์ง๋ฅผ slow path๋ก ์ฒ๋ฆฌ
6. ์กฐ๊ฑด๋ณ ๋น๊ต
6.1 get_user_pages vs pin_user_pages
| ํญ๋ชฉ | get_user_pages | pin_user_pages |
|---|---|---|
| ํ๋๊ทธ | FOLL_GET | FOLL_PIN |
| ํด์ API | put_page() | unpin_user_page() |
| ์นด์ดํ ๋ฐฉ์ | refcount๋ง ์ฆ๊ฐ | refcount + _pincount |
| ์ฅ์๊ฐ ์ฌ์ฉ | ๋ถ๊ฐ (FOLL_LONGTERM ๋ถ๊ฐ) | ๊ฐ๋ฅ |
| ๋ง์ด๊ทธ๋ ์ด์ | ๋ฏธ์ง์ | longterm ์ ์๋ ๋ง์ด๊ทธ๋ ์ด์ |
| ์ฃผ์ ์ฌ์ฉ ์ฌ๋ก | ์์ ์ ๊ทผ, ๋๋ฒ๊ทธ | DMA, RDMA, virtio |
6.2 Slow Path vs Fast Path
| ํญ๋ชฉ | Slow Path | Fast Path |
|---|---|---|
| mmap_lock | ํ์ | ๋ถํ์ |
| IRQ ์ํ | ํ์ฑํ | ๋นํ์ฑํ |
| VMA ๊ฒ์ฆ | O | X |
| ํ์ด์ง ํดํธ ์ฒ๋ฆฌ | O | X (ํดํธ ์ ์คํจ) |
| THP ์ฒ๋ฆฌ | PMD ๋ถํ ๊ฐ๋ฅ | PMD leaf ์ง์ ํ |
| ๋์ | ๋ชจ๋ ์๋๋ฆฌ์ค | ํซ ํจ์ค, IRQ ์ปจํ ์คํธ |
| ์ฑ๋ฅ | ๋๋ฆผ | ๋น ๋ฆ |
6.3 FOLL_WRITE ์ฒ๋ฆฌ ๊ฒฝ๋ก
| ์กฐ๊ฑด | ๋์ |
|---|---|
| VMA์ VM_WRITE ์์ | -EFAULT ๋ฐํ (FOLL_FORCE ์ COW ์๋) |
| PTE ์ฝ๊ธฐ ์ ์ฉ | can_follow_write_pte()๋ก COW ๊ฐ๋ฅ ์ฌ๋ถ ํ์ธ |
| ๊ณต์ ๋งคํ | FOLL_FORCE๋ก๋ ์ฐ๊ธฐ ๋ถ๊ฐ |
| PageAnonExclusive() | COW ํด์ ๊ฐ๋ฅ ์ ์ฐ๊ธฐ ํ์ฉ |
6.4 longterm ํ ๋ง์ด๊ทธ๋ ์ด์ ํ๋ฆ
| ๋จ๊ณ | ๋์ |
|---|---|
| 1. FOLL_LONGTERM ํ | __gup_longterm_locked() ์ง์ |
| 2. collect_longterm_unpinnable_folios() | ์ฅ์๊ฐ ํ ๋ถ๊ฐ ํ์ด์ง ์์ง |
| 3. folio_isolate_lru() | LRU์์ ๊ฒฉ๋ฆฌ |
| 4. migrate_longterm_unpinnable_folios() | ๋ง์ด๊ทธ๋ ์ด์ ์ํ |
| 5. -EAGAIN ๋ฐํ | ํธ์ถ์๊ฐ ๋ค์ ํ ์๋ |
| 6. ๋ชจ๋ ํ์ด์ง ํ ์ฑ๊ณต | ์ฑ๊ณต ๋ฐํ |
7. ์ฃผ์์ฌํญ ๋ฐ ์ด์ ๊ณ ๋ ค์ฌํญ
7.1 GUP Fast Path ์ ์ฝ ์กฐ๊ฑด
write_protect_seq๋ก fork()์ COW์ ๊ฒฝํฉ ๋ฐฉ์ง7.2 pin ์นด์ดํ ์ ํ์ฑ
_pincount ํ๋๋ก ์ ํํ ํ ์ ์ถ์ refcount์ GUP_PIN_COUNTING_BIAS(256)๋ฅผ ๊ณฑํ์ฌ ํ ํ์folio_add_pin(): ๊ธฐ์กด ํ์ ์ถ๊ฐ ํ ๊ฐ๋ฅ (memfd_pin_folios์์ ํ์ฉ)7.3 ์ฑ๋ฅ ํ๋
get_user_pages_fast_only(): fast path๋ง ์ฌ์ฉ, slow path ํด๋ฐฑ ์์FOLL_NOWAIT: ํดํธ ๋๊ธฐ ์์ด ์ฆ์ ๋ฐํFOLL_INTERRUPTIBLE: ์ ํธ๋ก ์ค๋จ ๊ฐ๋ฅ (๊ธด GUP ๋ฐฉ์ง)FOLL_NOFAULT: ํดํธ ๋ฏธ๋ฐ์ (๊ธฐ์กด ๋งคํ ํ์ธ์ฉ)