๏ปฟ# 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๋กœ ํด๋ฐฑํ•œ๋‹ค.

GUP ํ˜ธ์ถœ ํ๋ฆ„
GUP ์ž๋ฃŒ๊ตฌ์กฐ ๋ฐ Pin ์นด์šดํŒ…
์†Œ์Šค ํŒŒ์ผ: 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_FORCECOW ๋งคํ•‘์—์„œ๋„ ์“ฐ๊ธฐ ํ—ˆ์šฉ
FOLL_ANON์ต๋ช… ํŽ˜์ด์ง€๋งŒ ๋Œ€์ƒ
ํ• ๋‹น ๋ฐฉ์‹FOLL_GETput_page()๋กœ ํ•ด์ œ
FOLL_PINunpin_user_page()๋กœ ํ•ด์ œ
FOLL_LONGTERM์žฅ์‹œ๊ฐ„ ํ•€ (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋™๋ฐ˜)
๋™์ž‘ ์ œ์–ดFOLL_NOWAITํดํŠธ ์‹œ ์ฆ‰์‹œ ๋ฐ˜ํ™˜
FOLL_NOFAULTํดํŠธ ๋ฏธ๋ฐœ์ƒ
FOLL_INTERRUPTIBLE์‹ ํ˜ธ๋กœ ์ค‘๋‹จ ๊ฐ€๋Šฅ
ํŠน์ˆ˜ ์šฉ๋„FOLL_DUMP์ฝ”์–ด ๋คํ”„
FOLL_HWPOISONํ•˜๋“œ์›จ์–ด ์˜ค๋ฅ˜ ํŽ˜์ด์ง€
FOLL_PCI_P2PDMAPCI P2P DMA
FOLL_SPLIT_PMDTHP ๋ถ„ํ• 
FOLL_HONOR_NUMA_FAULTNUMA ํดํŠธ ์ฒ˜๋ฆฌ

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_pagespin_user_pages
ํ”Œ๋ž˜๊ทธFOLL_GETFOLL_PIN
ํ•ด์ œ APIput_page()unpin_user_page()
์นด์šดํŒ… ๋ฐฉ์‹refcount๋งŒ ์ฆ๊ฐ€refcount + _pincount
์žฅ์‹œ๊ฐ„ ์‚ฌ์šฉ๋ถˆ๊ฐ€ (FOLL_LONGTERM ๋ถˆ๊ฐ€)๊ฐ€๋Šฅ
๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋ฏธ์ง€์›longterm ์‹œ ์ž๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€์ž„์‹œ ์ ‘๊ทผ, ๋””๋ฒ„๊ทธDMA, RDMA, virtio

6.2 Slow Path vs Fast Path

ํ•ญ๋ชฉSlow PathFast Path
mmap_lockํ•„์š”๋ถˆํ•„์š”
IRQ ์ƒํƒœํ™œ์„ฑํ™”๋น„ํ™œ์„ฑํ™”
VMA ๊ฒ€์ฆOX
ํŽ˜์ด์ง€ ํดํŠธ ์ฒ˜๋ฆฌOX (ํดํŠธ ์‹œ ์‹คํŒจ)
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 ์ œ์•ฝ ์กฐ๊ฑด

  • IRQ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ์ƒํƒœ์—์„œ ๋™์ž‘ํ•˜๋ฏ€๋กœ ๊ธด ์ž‘์—… ๊ธˆ์ง€
  • write_protect_seq๋กœ fork()์˜ COW์™€ ๊ฒฝํ•ฉ ๋ฐฉ์ง€
  • secretmem, shmem๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๋งคํ•‘์€ longterm+write ์‹œ ๊ฑฐ๋ถ€
  • PCI P2P DMA ํŽ˜์ด์ง€๋Š” ๋ณ„๋„ ํ”Œ๋ž˜๊ทธ(FOLL_PCI_P2PDMA) ํ•„์š”
  • 7.2 pin ์นด์šดํŒ… ์ •ํ™•์„ฑ

  • ๋Œ€ํ˜• ํด๋ฆฌ์˜ค(large folio): _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: ํดํŠธ ๋ฏธ๋ฐœ์ƒ (๊ธฐ์กด ๋งคํ•‘ ํ™•์ธ์šฉ)

  • 8. ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • VMA / mmap
  • Huge Pages / THP
  • Reverse Mapping
  • mlock / munlock
  • Migration
  • Secret Memory
  • memfd