๏ปฟ# mremap ๐Ÿ”„

๊ด€๋ จ ์†Œ์Šค: mm/mremap.c, mm/internal.h, include/uapi/linux/mman.h

๊ฐœ์š” (Overview)

mremap()๋Š” ๊ธฐ์กด ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ(VMA)์˜ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ฐ€์ƒ ์ฃผ์†Œ๋กœ ์ด๋™ํ•˜๋Š” ์‹œ์Šคํ…œ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ ํ˜ธ์ถœ์€ ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘์˜ ํ™•์žฅ, ์ถ•์†Œ, ์ด๋™์„ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์„œ๋ธŒ์‹œ์Šคํ…œ์—์„œ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ : mremap()๋Š” ์•„ํŒŒํŠธ๋ฅผ ๋ฆฌ๋ชจ๋ธ๋งํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฐฉ์˜ ํฌ๊ธฐ๋ฅผ ํ‚ค์šฐ๊ฑฐ๋‚˜(ํ™•์žฅ), ์ค„์ด๊ฑฐ๋‚˜(์ถ•์†Œ), ์•„์˜ˆ ๋‹ค๋ฅธ ์ธต์œผ๋กœ ์˜ฎ๊ธฐ๋Š”(์ด๋™) ๊ฒƒ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ปค๋„์€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์กฐ์ •ํ•˜์—ฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณ„์†ํ•ด์„œ ๋™์ผํ•œ ๊ฐ€์ƒ ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

mremap()๋Š” ์„ธ ๊ฐ€์ง€ ๊ธฐ๋ณธ ์—ฐ์‚ฐ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค: in-place ํ™•์žฅ(in-place expansion), in-place ์ถ•์†Œ(in-place shrinking), ๊ทธ๋ฆฌ๊ณ  VMA ์ด๋™(VMA moving). ๊ฐ ์—ฐ์‚ฐ์€ ์„œ๋กœ ๋‹ค๋ฅธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ปค๋„์€ ์ตœ์ ์˜ ๋ฐฉ๋ฒ•์„ ์ž๋™์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ:

mm/mremap.c          # ๋ฉ”์ธ ๊ตฌํ˜„ (1998์ค„)
mm/internal.h        # pagetable_move_control ๊ตฌ์กฐ์ฒด ์ •์˜
include/uapi/linux/mman.h  # MREMAP ํ”Œ๋ž˜๊ทธ ์ •์˜

mremap ํ˜ธ์ถœ ํ๋ฆ„

mremap ํ˜ธ์ถœ ํ๋ฆ„

mremap ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

mremap ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น

# 1. mremap ์‹œ์Šคํ…œ ํ˜ธ์ถœ ํ˜ธ์ถœ ํšŸ์ˆ˜ ํ™•์ธ
cat /proc/kstat | grep mremap

# 2. ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์˜ VMA ๋งต ํ™•์ธ
cat /proc/self/maps | head -20

# 3. mremap ๊ด€๋ จ ์ปค๋„ ํ†ต๊ณ„ ํ™•์ธ
cat /proc/vmstat | grep -i mremap

# 4. mremap ์‹คํŒจ ํšŸ์ˆ˜ ํ™•์ธ (dmesg์—์„œ)
dmesg | grep -i "mremap" | tail -10

# 5. ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ VMA ๋ณ€๊ฒฝ ์ถ”์ 
strace -e mremap <ํ”„๋กœ์„ธ์Šค๋ช…>

# 6. mremap ํ”Œ๋ž˜๊ทธ ํ™•์ธ
grep -r "MREMAP_" /usr/include/linux/mman.h

# 7. ์ปค๋„ mremap ๊ด€๋ จ ๋ชจ๋“ˆ ํ™•์ธ
lsmod | grep -i mremap

# 8. mremap ๊ด€๋ จ ์ปค๋„ ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ
sysctl -a | grep -i mremap

# 9. mremap ํ˜ธ์ถœ ์‹œ VMA ๋ณ€๊ฒฝ ์ถ”์ 
perf trace -e mremap <ํ”„๋กœ์„ธ์Šค๋ช…>

# 10. mremap ๊ด€๋ จ ์ปค๋„ ๋กœ๊ทธ ํ™•์ธ
journalctl -k | grep -i mremap

# 11. VMA ๋งต ์ˆ˜ ํ™•์ธ (mremap ์‹คํŒจ ์›์ธ ์ง„๋‹จ)
cat /proc/self/status | grep -i vmsize

# 12. ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ํ†ต๊ณ„ ํ™•์ธ
cat /proc/vmstat | grep -i mmap

# 13. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ํ†ต๊ณ„ ํ™•์ธ
cat /proc/vmstat | grep -i pte

# 14. TLB ํ”Œ๋Ÿฌ์‹œ ํ†ต๊ณ„ ํ™•์ธ
cat /proc/vmstat | grep -i tlb

ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ

1. vma_remap_struct

mremap() ์—ฐ์‚ฐ ์ „์ฒด๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฉ”์ธ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:50-73 */
struct vma_remap_struct {
/* ์‚ฌ์šฉ์ž ์ œ๊ณต ์ƒํƒœ */
unsigned long addr;      /* ๋ฆฌ๋งคํ•‘ํ•  ์›๋ณธ ๊ฐ€์ƒ ์ฃผ์†Œ */
unsigned long old_len;   /* ๋ฆฌ๋งคํ•‘ํ•  ๋ฒ”์œ„์˜ ๊ธฐ์กด ๊ธธ์ด */
unsigned long new_len;   /* ์›ํ•˜๋Š” ์ƒˆ ๊ธธ์ด */
const unsigned long flags; /* MREMAP_* ํ”Œ๋ž˜๊ทธ */
unsigned long new_addr;  /* ์„ ํƒ์  ์ƒˆ ์ฃผ์†Œ */

/* uffd ์ƒํƒœ */
struct vm_userfaultfd_ctx *uf;
struct list_head *uf_unmap_early;
struct list_head *uf_unmap;

/* VMA ์ƒํƒœ - do_mremap()์—์„œ ๊ฒฐ์ • */
struct vm_area_struct *vma;

/* ๋‚ด๋ถ€ ์ƒํƒœ - do_mremap()์—์„œ ๊ฒฐ์ • */
unsigned long delta;     /* old_len๊ณผ new_len์˜ ์ ˆ๋Œ€ ์ฐจ์ด */
bool populate_expand;    /* mlock()๋œ ํ™•์žฅ ์˜์—ญ, populate ํ•„์š” */
enum mremap_type remap_type; /* expand, shrink ๋“ฑ */
bool mmap_locked;        /* ํ˜„์žฌ mm์ด ์“ฐ๊ธฐ ์ž ๊ธˆ ์ƒํƒœ์ธ์ง€ */
unsigned long charged;   /* VM_ACCOUNT์ธ ๊ฒฝ์šฐ, ์ฒญ๊ตฌํ•  ํŽ˜์ด์ง€ ์ˆ˜ */
bool vmi_needs_invalidate; /* VMA ๋ฐ˜๋ณต์ž๊ฐ€ ๋ฌดํšจํ™”๋˜์—ˆ๋Š”์ง€ */
};

2. pagetable_move_control

ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ด๋™ ์—ฐ์‚ฐ์„ ์ œ์–ดํ•˜๋Š” ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.

/* mm/internal.h:46-56 */
struct pagetable_move_control {
struct vm_area_struct *old; /* ์†Œ์Šค VMA */
struct vm_area_struct *new; /* ๋Œ€์ƒ VMA */
unsigned long old_addr;     /* ์ด๋™์ด ์‹œ์ž‘๋˜๋Š” ์ฃผ์†Œ */
unsigned long old_end;      /* ๊ธฐ์กด ๋ฒ”์œ„๊ฐ€ ๋๋‚˜๋Š” ์ฃผ์†Œ (ํฌํ•จ๋˜์ง€ ์•Š์Œ) */
unsigned long new_addr;     /* ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์ด๋™ํ•  ์ฃผ์†Œ */
unsigned long len_in;       /* ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ๋ฆฌ๋งคํ•‘ํ•  ๋ฐ”์ดํŠธ ์ˆ˜ */

bool need_rmap_locks;       /* rmap ์ž ๊ธˆ์ด ํ•„์š”ํ•œ์ง€ */
bool for_stack;             /* ์ดˆ๊ธฐ ์ž„์‹œ ์Šคํƒ์„ ์ด๋™ ์ค‘์ธ์ง€ */
};

/* PAGETABLE_MOVE ๋งคํฌ๋กœ๋กœ ์ดˆ๊ธฐํ™” */
/* mm/internal.h:58-66 */
#define PAGETABLE_MOVE(name, old_, new_, old_addr_, new_addr_, len_)    \
struct pagetable_move_control name = {                             \
.old = old_,                                                   \
.new = new_,                                                   \
.old_addr = old_addr_,                                         \
.old_end = (old_addr_) + (len_),                               \
.new_addr = new_addr_,                                         \
.len_in = len_,                                                \
}

3. mremap_type ์—ด๊ฑฐํ˜•

๋ฆฌ๋งคํ•‘ ์—ฐ์‚ฐ ์œ ํ˜•์„ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.

/* mm/mremap.c:36-41 */
enum mremap_type {
MREMAP_INVALID,    /* ์ดˆ๊ธฐ ์ƒํƒœ */
MREMAP_NO_RESIZE,  /* old_len == new_len, ์ด๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์Œ */
MREMAP_SHRINK,     /* old_len > new_len */
MREMAP_EXPAND,     /* old_len < new_len */
};

4. MREMAP ํ”Œ๋ž˜๊ทธ

/* include/uapi/linux/mman.h:9-11 */
#define MREMAP_MAYMOVE    1  /* ์ด๋™ ํ—ˆ์šฉ */
#define MREMAP_FIXED      2  /* ๊ณ ์ • ์ฃผ์†Œ๋กœ ์ด๋™ */
#define MREMAP_DONTUNMAP  4  /* ์›๋ณธ ๋งคํ•‘ ์œ ์ง€ (๋ณต์‚ฌ ํ›„ ์ด๋™) */

MREMAP_MAYMOVE: mremap๊ฐ€ ์ƒˆ ์ฃผ์†Œ์—์„œ ๋งคํ•‘์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. in-place ํ™•์žฅ/์ถ•์†Œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•  ๋•Œ ์ด๋™์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

MREMAP_FIXED: ์ง€์ •๋œ ์ฃผ์†Œ(new_addr)๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๋งคํ•‘์ด ์žˆ์œผ๋ฉด ๋จผ์ € ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. MREMAP_MAYMOVE๊ฐ€ ํ•จ๊ป˜ ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

MREMAP_DONTUNMAP: ์›๋ณธ ๋งคํ•‘์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ƒˆ ์œ„์น˜์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ํ•จ์ˆ˜

1. SYSCALL_DEFINE5(mremap)

mremap() ์‹œ์Šคํ…œ ํ˜ธ์ถœ์˜ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1965-1998 */
SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
unsigned long, new_len, unsigned long, flags,
unsigned long, new_addr)
{
struct vm_userfaultfd_ctx uf = NULL_VM_UFFD_CTX;
LIST_HEAD(uf_unmap_early);
LIST_HEAD(uf_unmap);
struct vma_remap_struct vrm = {
.addr = untagged_addr(addr),  /* ARM64 ํƒœ๊ทธ ์ œ๊ฑฐ */
.old_len = old_len,
.new_len = new_len,
.flags = flags,
.new_addr = new_addr,
.uf = &uf,
.uf_unmap_early = &uf_unmap_early,
.uf_unmap = &uf_unmap,
.remap_type = MREMAP_INVALID,
};
return do_mremap(&vrm);
}

์—ญํ• : ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์—์„œ ์ „๋‹ฌ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  vma_remap_struct๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ ํ›„ do_mremap()๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ARM64 ํƒœ๊ทธ ์ฃผ์†Œ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด untagged_addr()์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2. do_mremap

mremap์˜ ๋ฉ”์ธ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1915-1956 */
static unsigned long do_mremap(struct vma_remap_struct *vrm)
{
struct mm_struct *mm = current->mm;
unsigned long res;
bool failed;

/* ๊ธธ์ด๋ฅผ ํŽ˜์ด์ง€ ํฌ๊ธฐ์— ์ •๋ ฌ */
vrm->old_len = PAGE_ALIGN(vrm->old_len);
vrm->new_len = PAGE_ALIGN(vrm->new_len);

/* ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฒ€์ฆ */
res = check_mremap_params(vrm);
if (res)
return res;

/* mmap ์“ฐ๊ธฐ ์ž ๊ธˆ ํš๋“ */
if (mmap_write_lock_killable(mm))
return -EINTR;
vrm->mmap_locked = true;

/* ์ด๋™๋งŒ ํ•˜๋Š” ๊ฒฝ์šฐ */
if (vrm_move_only(vrm)) {
res = remap_move(vrm);
} else {
/* VMA ์กฐํšŒ ๋ฐ ๊ฒ€์ฆ */
vrm->vma = vma_lookup(current->mm, vrm->addr);
res = check_prep_vma(vrm);
if (res)
goto out;

/* mremap ์‹คํ–‰ */
res = vrm_implies_new_addr(vrm) ? mremap_to(vrm) : mremap_at(vrm);
}

out:
failed = IS_ERR_VALUE(res);
if (vrm->mmap_locked)
mmap_write_unlock(mm);

/* mlock๋œ ํ™•์žฅ ์˜์—ญ populate */
if (!failed && vrm->populate_expand)
mm_populate(vrm->new_addr + vrm->old_len, vrm->delta);

/* userfaultfd ์•Œ๋ฆผ */
notify_uffd(vrm, failed);
return res;
}

์—ญํ• : ํŽ˜์ด์ง€ ์ •๋ ฌ๋œ ๊ธธ์ด๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฒ€์ฆํ•œ ํ›„, mmap ์ž ๊ธˆ์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๋‹ค์Œ ์ด๋™ ์ „์šฉ ๊ฒฝ๋กœ ๋˜๋Š” ์ผ๋ฐ˜ ๊ฒฝ๋กœ๋ฅผ ์„ ํƒํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

3. check_mremap_params

mremap ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1756-1824 */
static unsigned long check_mremap_params(struct vma_remap_struct *vrm)
{
unsigned long addr = vrm->addr;
unsigned long flags = vrm->flags;

/* ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ํ”Œ๋ž˜๊ทธ ๊ฐ’ ๋ฐฉ์ง€ */
if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP))
return -EINVAL;

/* ์‹œ์ž‘ ์ฃผ์†Œ๋Š” ํŽ˜์ด์ง€ ์ •๋ ฌ์–ด์•ผ ํ•จ */
if (offset_in_page(addr))
return -EINVAL;

/* new_len์ด 0์ด๋ฉด ์•ˆ๋จ */
if (!vrm->new_len)
return -EINVAL;

/* new_len์ด TASK_SIZE๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์•ˆ๋จ */
if (vrm->new_len > TASK_SIZE)
return -EINVAL;

/* ๊ณ ์ • ์ฃผ์†Œ ์ง€์ • ์‹œ ์ถ”๊ฐ€ ๊ฒ€์ฆ */
if (!vrm_implies_new_addr(vrm))
return 0;

/* new_addr์ด TASK_SIZE - new_len์„ ์ดˆ๊ณผํ•˜๋ฉด ์•ˆ๋จ */
if (vrm->new_addr > TASK_SIZE - vrm->new_len)
return -EINVAL;

/* new_addr์€ ํŽ˜์ด์ง€ ์ •๋ ฌ์–ด์•ผ ํ•จ */
if (offset_in_page(vrm->new_addr))
return -EINVAL;

/* ๊ณ ์ • ์ฃผ์†Œ๋Š” MREMAP_MAYMOVE ํ•„์š” */
if (!(flags & MREMAP_MAYMOVE))
return -EINVAL;

/* MREMAP_DONTUNMAP์€ ํฌ๊ธฐ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ */
if (flags & MREMAP_DONTUNMAP && vrm->old_len != vrm->new_len)
return -EINVAL;

/* ๋Œ€์ƒ VMA์™€ ์†Œ์Šค VMA๊ฐ€ ๊ฒน์น˜๋ฉด ์•ˆ๋จ */
if (vrm_overlaps(vrm))
return -EINVAL;

/* VMA ๋งต ์ˆ˜ ์ œํ•œ ํ™•์ธ */
if ((current->mm->map_count + 2) >= sysctl_max_map_count - 3)
return -ENOMEM;

return 0;
}

์—ญํ• : ํ”Œ๋ž˜๊ทธ ์œ ํšจ์„ฑ, ์ฃผ์†Œ ์ •๋ ฌ, ํฌ๊ธฐ ์ œํ•œ, VMA ๊ฒน์นจ ๋“ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

4. check_prep_vma

VMA ์ด๋™ ์ „ ์ค€๋น„ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1655-1750 */
static int check_prep_vma(struct vma_remap_struct *vrm)
{
struct vm_area_struct *vma = vrm->vma;
struct mm_struct *mm = current->mm;
unsigned long addr = vrm->addr;
unsigned long old_len, new_len, pgoff;

if (!vma)
return -EFAULT;

/* mseal()๋œ VMA๋Š” mremap ๋ถˆ๊ฐ€ */
if (vma_is_sealed(vma))
return -EPERM;

/* HugeTLB ํŽ˜์ด์ง€๋Š” ์ •๋ ฌ ํ•„์š” */
if (is_vm_hugetlb_page(vma) && !align_hugetlb(vrm))
return -EINVAL;

vrm_set_delta(vrm);
vrm->remap_type = vrm_remap_type(vrm);

/* ์ƒˆ ์ฃผ์†Œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ */
if (!vrm_implies_new_addr(vrm))
vrm->new_addr = addr;

if (!vrm_will_map_new(vrm))
return 0;

old_len = vrm->old_len;
new_len = vrm->new_len;

/* ๋น„๊ณต์œ  ๋งคํ•‘์˜ ๊ฒฝ์šฐ old_len=0 ํ—ˆ์šฉ ์•ˆ๋จ */
if (!old_len && !(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) {
pr_warn_once("%s (%d): attempted to duplicate a private mapping with mremap. This is not supported.\n",
current->comm, current->pid);
return -EINVAL;
}

/* MREMAP_DONTUNMAP์€ VM_DONTEXPAND/VM_PFNMAP ๋ถˆ๊ฐ€ */
if ((vrm->flags & MREMAP_DONTUNMAP) &&
(vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)))
return -EINVAL;

/* ์ถ•์†Œ ์‹œ old_len ์กฐ์ • */
if (vrm->remap_type == MREMAP_SHRINK)
old_len = new_len;

/* old_len์ด VMA ๋ฒ”์œ„๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์•ˆ๋จ */
if (old_len > vma->vm_end - addr)
return -EFAULT;

if (new_len == old_len)
return 0;

/* mlock๋œ VMA ํ™•์žฅ ์‹œ populate ํ•„์š” */
if (vma->vm_flags & VM_LOCKED)
vrm->populate_expand = true;

/* ํŽ˜์ด์ง€ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๊ฒ€์‚ฌ */
pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
pgoff += vma->vm_pgoff;
if (pgoff + (new_len >> PAGE_SHIFT) < pgoff)
return -EINVAL;

/* VM_DONTEXPAND/VM_PFNMAP๋Š” ํ™•์žฅ ๋ถˆ๊ฐ€ */
if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP))
return -EFAULT;

if (!mlock_future_ok(mm, vma->vm_flags & VM_LOCKED, vrm->delta))
return -EAGAIN;

if (!may_expand_vm(mm, vma->vm_flags, vrm->delta >> PAGE_SHIFT))
return -ENOMEM;

return 0;
}

์—ญํ• : VMA ์ƒํƒœ, mseal, HugeTLB ์ •๋ ฌ, ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

5. move_vma

VMA๋ฅผ ์ƒˆ ์œ„์น˜๋กœ ์ด๋™ํ•˜๋Š” ํ•ต์‹ฌ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1270-1321 */
static unsigned long move_vma(struct vma_remap_struct *vrm)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *new_vma;
unsigned long hiwater_vm;
int err;

/* ์ด๋™ ์ „ ์ค€๋น„ */
err = prep_move_vma(vrm);
if (err)
return err;

/* ๊ณ„์ • ์ฒ˜๋ฆฌ */
if (!vrm_calc_charge(vrm))
return -ENOMEM;

/* VMA ์“ฐ๊ธฐ ์ž ๊ธˆ ํš๋“ */
vma_start_write(vrm->vma);

/* ๋ณต์‚ฌ ๋‹จ๊ณ„ ์ˆ˜ํ–‰ */
err = copy_vma_and_data(vrm, &new_vma);
if (err && !new_vma)
return err;

/* ํ†ต๊ณ„ ๊ณ„์ • */
hiwater_vm = mm->hiwater_vm;
vrm_stat_account(vrm, vrm->new_len);

/* ์›๋ณธ VMA ์ฒ˜๋ฆฌ */
if (unlikely(!err && (vrm->flags & MREMAP_DONTUNMAP)))
dontunmap_complete(vrm, new_vma);
else
unmap_source_vma(vrm);

mm->hiwater_vm = hiwater_vm;
return err ? (unsigned long)err : vrm->new_addr;
}

์—ญํ• : VMA๋ฅผ ์ƒˆ ์œ„์น˜๋กœ ๋ณต์‚ฌํ•˜๊ณ , ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์ด๋™ํ•œ ํ›„, ์›๋ณธ VMA๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. MREMAP_DONTUNMAP ํ”Œ๋ž˜๊ทธ๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ ์›๋ณธ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

6. move_page_tables

ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์‹ค์ œ ์ด๋™ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:795-879 */
unsigned long move_page_tables(struct pagetable_move_control *pmc)
{
unsigned long extent;
struct mmu_notifier_range range;
pmd_t *old_pmd, *new_pmd;
pud_t *old_pud, *new_pud;
struct mm_struct *mm = pmc->old->vm_mm;

if (!pmc->len_in)
return 0;

/* HugeTLB ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ ๋ณ„๋„ ์ฒ˜๋ฆฌ */
if (is_vm_hugetlb_page(pmc->old))
return move_hugetlb_page_tables(pmc->old, pmc->new, pmc->old_addr,
pmc->new_addr, pmc->len_in);

/* PMD ๊ฒฝ๊ณ„์— ์ •๋ ฌํ•˜์—ฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ */
try_realign_addr(pmc, PMD_MASK);

/* ์บ์‹œ ํ”Œ๋Ÿฌ์‹œ ๋ฐ MMU ์•Œ๋ฆผ ์‹œ์ž‘ */
flush_cache_range(pmc->old, pmc->old_addr, pmc->old_end);
mmu_notifier_range_init(&range, MMU_NOTIFY_UNMAP, 0, mm,
pmc->old_addr, pmc->old_end);
mmu_notifier_invalidate_range_start(&range);

/* ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ˆœํšŒ ๋ฐ ์ด๋™ */
for (; !pmc_done(pmc); pmc_next(pmc, extent)) {
cond_resched();

/* PUD ๋ ˆ๋ฒจ ์ด๋™ ์‹œ๋„ */
extent = get_extent(NORMAL_PUD, pmc);
old_pud = get_old_pud(mm, pmc->old_addr);
if (!old_pud)
continue;
new_pud = alloc_new_pud(mm, pmc->new_addr);
if (!new_pud)
break;

/* Huge PUD ์ด๋™ */
if (pud_trans_huge(*old_pud)) {
if (extent == HPAGE_PUD_SIZE) {
move_pgt_entry(pmc, HPAGE_PUD, old_pud, new_pud);
continue;
}
} else if (IS_ENABLED(CONFIG_HAVE_MOVE_PUD) && extent == PUD_SIZE) {
if (move_pgt_entry(pmc, NORMAL_PUD, old_pud, new_pud))
continue;
}

/* PMD ๋ ˆ๋ฒจ ์ด๋™ ์‹œ๋„ */
extent = get_extent(NORMAL_PMD, pmc);
old_pmd = get_old_pmd(mm, pmc->old_addr);
if (!old_pmd)
continue;
new_pmd = alloc_new_pmd(mm, pmc->new_addr);
if (!new_pmd)
break;

/* Huge PMD ์ด๋™ */
if (pmd_is_huge(*old_pmd)) {
if (extent == HPAGE_PMD_SIZE &&
move_pgt_entry(pmc, HPAGE_PMD, old_pmd, new_pmd))
continue;
split_huge_pmd(pmc->old, old_pmd, pmc->old_addr);
} else if (IS_ENABLED(CONFIG_HAVE_MOVE_PMD) && extent == PMD_SIZE) {
if (move_pgt_entry(pmc, NORMAL_PMD, old_pmd, new_pmd))
continue;
}

/* PTE ๋ ˆ๋ฒจ ์ด๋™ */
if (pmd_none(*old_pmd))
continue;
if (pte_alloc(pmc->new->vm_mm, new_pmd))
break;
if (move_ptes(pmc, extent, old_pmd, new_pmd) < 0)
goto again;
}

mmu_notifier_invalidate_range_end(&range);
return pmc_progress(pmc);
}

์—ญํ• : ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๊ณ„์ธต(PUDโ†’PMDโ†’PTE)์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ์—”ํŠธ๋ฆฌ๋ฅผ ์ƒˆ ์œ„์น˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. Huge ํŽ˜์ด์ง€์™€ ์ผ๋ฐ˜ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, rmap ์ž ๊ธˆ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

7. move_ptes

๊ฐœ๋ณ„ PTE๋ฅผ ์ด๋™ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:197-319 */
static int move_ptes(struct pagetable_move_control *pmc,
unsigned long extent, pmd_t *old_pmd, pmd_t *new_pmd)
{
struct vm_area_struct *vma = pmc->old;
bool need_clear_uffd_wp = vma_has_uffd_without_event_remap(vma);
struct mm_struct *mm = vma->vm_mm;
pte_t *old_ptep, *new_ptep;
pte_t old_pte, pte;
pmd_t dummy_pmdval;
spinlock_t *old_ptl, *new_ptl;
bool force_flush = false;
unsigned long old_addr = pmc->old_addr;
unsigned long new_addr = pmc->new_addr;
unsigned long old_end = old_addr + extent;
unsigned long len = old_end - old_addr;
int max_nr_ptes;
int nr_ptes;
int err = 0;

/* rmap ์ž ๊ธˆ ํ•„์š”์‹œ ํš๋“ */
if (pmc->need_rmap_locks)
take_rmap_locks(vma);

/* ์†Œ์Šค PTE ์ž ๊ธˆ */
old_ptep = pte_offset_map_lock(mm, old_pmd, old_addr, &old_ptl);
if (!old_ptep) {
err = -EAGAIN;
goto out;
}

/* ๋Œ€์ƒ PTE ๋งคํ•‘ */
new_ptep = pte_offset_map_rw_nolock(mm, new_pmd, new_addr, &dummy_pmdval,
&new_ptl);
if (!new_ptep) {
pte_unmap_unlock(old_ptep, old_ptl);
err = -EAGAIN;
goto out;
}

/* ์ค‘์ฒฉ ์ž ๊ธˆ ์ฒ˜๋ฆฌ */
if (new_ptl != old_ptl)
spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING);
flush_tlb_batched_pending(vma->vm_mm);
lazy_mmu_mode_enable();

/* PTE ์ˆœํšŒ ๋ฐ ์ด๋™ */
for (; old_addr < old_end; old_ptep += nr_ptes, old_addr += nr_ptes * PAGE_SIZE,
new_ptep += nr_ptes, new_addr += nr_ptes * PAGE_SIZE) {
VM_WARN_ON_ONCE(!pte_none(*new_ptep));

nr_ptes = 1;
max_nr_ptes = (old_end - old_addr) >> PAGE_SHIFT;
old_pte = ptep_get(old_ptep);
if (pte_none(old_pte))
continue;

/* ์œ ํšจํ•œ PTE์ธ ๊ฒฝ์šฐ TLB ํ”Œ๋Ÿฌ์‹œ ํ•„์š” */
if (pte_present(old_pte)) {
nr_ptes = mremap_folio_pte_batch(vma, old_addr, old_ptep,
old_pte, max_nr_ptes);
force_flush = true;
}

/* PTE ์ด๋™ */
pte = get_and_clear_ptes(mm, old_addr, old_ptep, nr_ptes);
pte = move_pte(pte, old_addr, new_addr);
pte = move_soft_dirty_pte(pte);

/* uffd-wp ์ฒ˜๋ฆฌ */
if (need_clear_uffd_wp && pte_is_uffd_wp_marker(pte))
pte_clear(mm, new_addr, new_ptep);
else {
if (need_clear_uffd_wp) {
if (pte_present(pte))
pte = pte_clear_uffd_wp(pte);
else
pte = pte_swp_clear_uffd_wp(pte);
}
set_ptes(mm, new_addr, new_ptep, pte, nr_ptes);
}
}

lazy_mmu_mode_disable();
if (force_flush)
flush_tlb_range(vma, old_end - len, old_end);
if (new_ptl != old_ptl)
spin_unlock(new_ptl);
pte_unmap(new_ptep - 1);
pte_unmap_unlock(old_ptep - 1, old_ptl);
out:
if (pmc->need_rmap_locks)
drop_rmap_locks(vma);
return err;
}

์—ญํ• : ๊ฐœ๋ณ„ PTE๋ฅผ ์†Œ์Šค์—์„œ ๋Œ€์ƒ์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. folio ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ, soft-dirty ๋น„ํŠธ ์ฒ˜๋ฆฌ, userfaultfd-wp ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

8. mremap_folio_pte_batch

large folio์˜ ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ PTE๋ฅผ ํ•œ ๋ฒˆ์— ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:178-195 */
static int mremap_folio_pte_batch(struct vm_area_struct *vma, unsigned long addr,
pte_t *ptep, pte_t pte, int max_nr)
{
struct folio *folio;

if (max_nr == 1)
return 1;

/* large folio๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฐฐ์น˜ ๋ถˆ๊ฐ€ */
if (pte_batch_hint(ptep, pte) == 1)
return 1;

folio = vm_normal_folio(vma, addr, pte);
if (!folio || !folio_test_large(folio))
return 1;

return folio_pte_batch_flags(folio, NULL, ptep, &pte, max_nr, FPB_RESPECT_WRITE);
}

์—ญํ• : large folio์˜ ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ PTE๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

9. move_soft_dirty_pte

soft-dirty ๋น„ํŠธ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:159-176 */
static pte_t move_soft_dirty_pte(pte_t pte)
{
if (pte_none(pte))
return pte;

/* soft dirty ๋น„ํŠธ ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์—์„œ PTE ์ด๋™ ๊ฐ์ง€ */
if (pgtable_supports_soft_dirty()) {
if (pte_present(pte))
pte = pte_mksoft_dirty(pte);
else
pte = pte_swp_mksoft_dirty(pte);
}

return pte;
}

์—ญํ• : PTE๊ฐ€ ์ด๋™๋˜์—ˆ์Œ์„ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์—์„œ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก soft-dirty ๋น„ํŠธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

10. mremap_to

์ง€์ •๋œ ์ฃผ์†Œ๋กœ VMA๋ฅผ ๋ฆฌ๋งคํ•‘ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1367-1417 */
static unsigned long mremap_to(struct vma_remap_struct *vrm)
{
struct mm_struct *mm = current->mm;
unsigned long err;

/* MREMAP_FIXED์ธ ๊ฒฝ์šฐ ๋Œ€์ƒ ์˜์—ญ ๋จผ์ € ์ œ๊ฑฐ */
if (vrm->flags & MREMAP_FIXED) {
err = do_munmap(mm, vrm->new_addr, vrm->new_len,
vrm->uf_unmap_early);
vrm->vma = NULL;
vrm->vmi_needs_invalidate = true;
if (err)
return err;

/* VMA ์žฌ์กฐํšŒ */
vrm->vma = vma_lookup(mm, vrm->addr);
if (!vrm->vma)
return -EFAULT;
}

/* ์ถ•์†Œ ์ฒ˜๋ฆฌ */
if (vrm->remap_type == MREMAP_SHRINK) {
err = shrink_vma(vrm, false);
if (err)
return err;

vrm->old_len = vrm->new_len;
}

/* MREMAP_DONTUNMAP์€ ํ™•์žฅ ๊ฒ€์ฆ */
if (vrm->flags & MREMAP_DONTUNMAP) {
vm_flags_t vm_flags = vrm->vma->vm_flags;
unsigned long pages = vrm->old_len >> PAGE_SHIFT;

if (!may_expand_vm(mm, vm_flags, pages))
return -ENOMEM;
}

/* ์ƒˆ ์ฃผ์†Œ ๊ฒฐ์ • */
err = vrm_set_new_addr(vrm);
if (err)
return err;

return move_vma(vrm);
}

์—ญํ• : MREMAP_FIXED ํ”Œ๋ž˜๊ทธ๋กœ ์ง€์ •๋œ ์ฃผ์†Œ๋กœ VMA๋ฅผ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

11. mremap_at

์ฃผ์†Œ ์ง€์ • ์—†์ด VMA ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1554-1584 */
static unsigned long mremap_at(struct vma_remap_struct *vrm)
{
unsigned long res;

switch (vrm->remap_type) {
case MREMAP_INVALID:
break;
case MREMAP_NO_RESIZE:
/* ํฌ๊ธฐ ๋ณ€๊ฒฝ ์—†์Œ - no-op */
return vrm->addr;
case MREMAP_SHRINK:
/* ์ถ•์†Œ - ํ•ญ์ƒ in-place ๊ฐ€๋Šฅ */
res = shrink_vma(vrm, true);
if (res)
return res;

return vrm->addr;
case MREMAP_EXPAND:
return expand_vma(vrm);
}

WARN_ON_ONCE(1);
return -EINVAL;
}

์—ญํ• : MREMAP_NO_RESIZE, MREMAP_SHRINK, MREMAP_EXPAND ์œ ํ˜•์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

12. expand_vma

VMA๋ฅผ ํ™•์žฅํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1516-1548 */
static unsigned long expand_vma(struct vma_remap_struct *vrm)
{
unsigned long err;

/* in-place ํ™•์žฅ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ */
if (vrm_can_expand_in_place(vrm)) {
err = expand_vma_in_place(vrm);
if (err)
return err;

return vrm->addr;
}

/* in-place ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด ์ด๋™ ํ•„์š” */
if (!(vrm->flags & MREMAP_MAYMOVE))
return -ENOMEM;

/* ์ƒˆ ์ฃผ์†Œ ๊ฒฐ์ • */
err = vrm_set_new_addr(vrm);
if (err)
return err;

return move_vma(vrm);
}

์—ญํ• : in-place ํ™•์žฅ์„ ์‹œ๋„ํ•˜๊ณ , ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด MREMAP_MAYMOVE ํ”Œ๋ž˜๊ทธ๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ ์ด๋™์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

13. expand_vma_in_place

VMA๋ฅผ in-place๋กœ ํ™•์žฅํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1456-1484 */
static unsigned long expand_vma_in_place(struct vma_remap_struct *vrm)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma = vrm->vma;
VMA_ITERATOR(vmi, mm, vma->vm_end);

/* ๊ณ„์ • ์ฒ˜๋ฆฌ */
if (!vrm_calc_charge(vrm))
return -ENOMEM;

/* VMA ํ™•์žฅ ๋ฐ ๋ณ‘ํ•ฉ */
vma = vma_merge_extend(&vmi, vma, vrm->delta);
if (!vma) {
vrm_uncharge(vrm);
return -ENOMEM;
}
vrm->vma = vma;

/* ํ†ต๊ณ„ ๊ณ„์ • */
vrm_stat_account(vrm, vrm->delta);

return 0;
}

์—ญํ• : VMA๋ฅผ in-place๋กœ ํ™•์žฅํ•˜๊ณ , ์ธ์ ‘ VMA์™€ ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

14. shrink_vma

VMA๋ฅผ ์ถ•์†Œํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1330-1361 */
static unsigned long shrink_vma(struct vma_remap_struct *vrm,
bool drop_lock)
{
struct mm_struct *mm = current->mm;
unsigned long unmap_start = vrm->addr + vrm->new_len;
unsigned long unmap_bytes = vrm->delta;
unsigned long res;
VMA_ITERATOR(vmi, mm, unmap_start);

VM_BUG_ON(vrm->remap_type != MREMAP_SHRINK);

/* ์ถ•์†Œ ์˜์—ญ ์ œ๊ฑฐ */
res = do_vmi_munmap(&vmi, mm, unmap_start, unmap_bytes,
vrm->uf_unmap, drop_lock);
vrm->vma = NULL;
if (res)
return res;

/* ์ž ๊ธˆ ํ•ด์ œ ์‹œ VMA ์žฌ์กฐํšŒ */
if (drop_lock) {
vrm->mmap_locked = false;
} else {
vrm->vma = vma_lookup(mm, vrm->addr);
if (!vrm->vma)
return -EFAULT;
}

return 0;
}

์—ญํ• : VMA์˜ ๋๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•˜์—ฌ ํฌ๊ธฐ๋ฅผ ์ถ•์†Œํ•ฉ๋‹ˆ๋‹ค.

15. remap_move

์—ฌ๋Ÿฌ VMA๋ฅผ ํ•œ ๋ฒˆ์— ์ด๋™ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* mm/mremap.c:1826-1913 */
static unsigned long remap_move(struct vma_remap_struct *vrm)
{
struct vm_area_struct *vma;
unsigned long start = vrm->addr;
unsigned long end = vrm->addr + vrm->old_len;
unsigned long new_addr = vrm->new_addr;
unsigned long target_addr = new_addr;
unsigned long res = -EFAULT;
unsigned long last_end;
bool seen_vma = false;

VMA_ITERATOR(vmi, current->mm, start);

/* ๋ฒ”์œ„ ๋‚ด ๋ชจ๋“  VMA ์ˆœํšŒ */
for_each_vma_range(vmi, vma, end) {
unsigned long addr = max(vma->vm_start, start);
unsigned long len = min(end, vma->vm_end) - addr;
unsigned long offset, res_vma;
bool multi_allowed;

/* ์‹œ์ž‘ ๋ถ€๋ถ„์˜ ๊ฐ„๊ฒฉ ํ—ˆ์šฉ ์•ˆ๋จ */
if (!seen_vma && start < vma->vm_start)
return -EFAULT;

/* ๊ฐ„๊ฒฉ ์œ ์ง€ํ•˜์—ฌ ์ด๋™ */
offset = seen_vma ? vma->vm_start - last_end : 0;
last_end = vma->vm_end;

vrm->vma = vma;
vrm->addr = addr;
vrm->new_addr = target_addr + offset;
vrm->old_len = vrm->new_len = len;

multi_allowed = vma_multi_allowed(vma);
if (!multi_allowed) {
if (seen_vma)
return -EFAULT;
if (vma->vm_end < end)
return -EFAULT;
}

res_vma = check_prep_vma(vrm);
if (!res_vma)
res_vma = mremap_to(vrm);
if (IS_ERR_VALUE(res_vma))
return res_vma;

if (!seen_vma) {
VM_WARN_ON_ONCE(multi_allowed && res_vma != new_addr);
res = res_vma;
}

if (vrm->vmi_needs_invalidate) {
vma_iter_invalidate(&vmi);
vrm->vmi_needs_invalidate = false;
}
seen_vma = true;
target_addr = res_vma + vrm->new_len;
}

return res;
}

์—ญํ• : ์—ฌ๋Ÿฌ VMA๋ฅผ ํ•œ ๋ฒˆ์— ์ด๋™ํ•  ๋•Œ ๊ฐ„๊ฒฉ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


ํ˜ธ์ถœ ํ๋ฆ„

SYSCALL_DEFINE5(mremap)  โ† ์‚ฌ์šฉ์ž ์‹œ์Šคํ…œ ํ˜ธ์ถœ ์ง„์ž…์ 
โ†“
do_mremap()  โ† ๋ฉ”์ธ ๋กœ์ง ์ฒ˜๋ฆฌ
โ”œโ”€ check_mremap_params()  # ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฒ€์ฆ (ํ”Œ๋ž˜๊ทธ, ์ฃผ์†Œ, ํฌ๊ธฐ)
โ”œโ”€ mmap_write_lock_killable()  # mmap ์“ฐ๊ธฐ ์ž ๊ธˆ ํš๋“
โ”œโ”€ vrm_move_only()?
โ”‚   โ”œโ”€ YES โ†’ remap_move()  # ๋‹ค์ค‘ VMA ์ด๋™ (๊ฐ„๊ฒฉ ์œ ์ง€)
โ”‚   โ”‚         โ””โ”€ for_each_vma_range()
โ”‚   โ”‚             โ”œโ”€ check_prep_vma()  # VMA ์ค€๋น„ ๊ฒ€์ฆ
โ”‚   โ”‚             โ””โ”€ mremap_to()  # ์ง€์ • ์ฃผ์†Œ๋กœ ์ด๋™
โ”‚   โ”‚                 โ”œโ”€ do_munmap()  # ๋Œ€์ƒ ์˜์—ญ ์ œ๊ฑฐ
โ”‚   โ”‚                 โ”œโ”€ vrm_set_new_addr()  # ์ƒˆ ์ฃผ์†Œ ๊ฒฐ์ •
โ”‚   โ”‚                 โ””โ”€ move_vma()
โ”‚   โ”‚                     โ”œโ”€ prep_move_vma()  # VMA ๋ถ„ํ•  ์ค€๋น„
โ”‚   โ”‚                     โ”œโ”€ vrm_calc_charge()  # ๊ณ„์ • ์ฒ˜๋ฆฌ
โ”‚   โ”‚                     โ”œโ”€ copy_vma_and_data()
โ”‚   โ”‚                     โ”‚   โ”œโ”€ copy_vma()  # VMA ๋ณต์‚ฌ
โ”‚   โ”‚                     โ”‚   โ””โ”€ move_page_tables()  # ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ด๋™
โ”‚   โ”‚                     โ”‚       โ”œโ”€ try_realign_addr()  # PMD ๊ฒฝ๊ณ„ ์ •๋ ฌ
โ”‚   โ”‚                     โ”‚       โ”œโ”€ move_pgt_entry()  # PUD/PMD ๋ ˆ๋ฒจ ์ด๋™
โ”‚   โ”‚                     โ”‚       โ””โ”€ move_ptes()  # PTE ๋ ˆ๋ฒจ ์ด๋™
โ”‚   โ”‚                     โ”‚           โ””โ”€ mremap_folio_pte_batch()  # large folio ๋ฐฐ์น˜
โ”‚   โ”‚                     โ”œโ”€ vrm_stat_account()  # ํ†ต๊ณ„ ๊ณ„์ •
โ”‚   โ”‚                     โ””โ”€ unmap_source_vma()  # ์›๋ณธ ์ œ๊ฑฐ
โ”‚   โ””โ”€ NO โ†’ vma_lookup()  # VMA ์กฐํšŒ
โ”‚           โ”œโ”€ check_prep_vma()  # VMA ์ค€๋น„ ๊ฒ€์ฆ
โ”‚           โ””โ”€ mremap_to() ๋˜๋Š” mremap_at()
โ”‚               โ”œโ”€ mremap_to()  # ๊ณ ์ • ์ฃผ์†Œ๋กœ ์ด๋™
โ”‚               โ”‚   โ”œโ”€ do_munmap()  # ๋Œ€์ƒ ์˜์—ญ ์ œ๊ฑฐ
โ”‚               โ”‚   โ”œโ”€ shrink_vma()  # ์ถ•์†Œ ์ฒ˜๋ฆฌ
โ”‚               โ”‚   โ””โ”€ move_vma()
โ”‚               โ””โ”€ mremap_at()  # ์ฃผ์†Œ ์ง€์ • ์—†์ด ์ด๋™
โ”‚                   โ”œโ”€ MREMAP_NO_RESIZE โ†’ ์ฆ‰์‹œ ๋ฐ˜ํ™˜
โ”‚                   โ”œโ”€ MREMAP_SHRINK โ†’ shrink_vma()
โ”‚                   โ””โ”€ MREMAP_EXPAND โ†’ expand_vma()
โ”‚                       โ”œโ”€ vrm_can_expand_in_place()?
โ”‚                       โ”‚   โ”œโ”€ YES โ†’ expand_vma_in_place()
โ”‚                       โ”‚   โ”‚         โ””โ”€ vma_merge_extend()
โ”‚                       โ”‚   โ””โ”€ NO โ†’ move_vma()
โ””โ”€ mmap_write_unlock()  # mmap ์ž ๊ธˆ ํ•ด์ œ

ํ๋ฆ„ ์„ค๋ช…

1. ์ง„์ž…์ : SYSCALL_DEFINE5(mremap)์ด ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„ vma_remap_struct๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

2. ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฒ€์ฆ: check_mremap_params()๊ฐ€ ํ”Œ๋ž˜๊ทธ, ์ฃผ์†Œ ์ •๋ ฌ, ํฌ๊ธฐ ์ œํ•œ ๋“ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

3. ์ž ๊ธˆ ํš๋“: mmap_write_lock_killable()๋กœ mmap ์“ฐ๊ธฐ ์ž ๊ธˆ์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.

4. ๊ฒฝ๋กœ ์„ ํƒ:

- vrm_move_only()๊ฐ€ true์ด๋ฉด remap_move()๋กœ ์—ฌ๋Ÿฌ VMA๋ฅผ ํ•œ ๋ฒˆ์— ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

- ์•„๋‹ˆ๋ฉด ๋‹จ์ผ VMA ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

5. VMA ์ฒ˜๋ฆฌ:

- mremap_to(): MREMAP_FIXED ํ”Œ๋ž˜๊ทธ๋กœ ์ง€์ •๋œ ์ฃผ์†Œ๋กœ ์ด๋™

- mremap_at(): ์ฃผ์†Œ ์ง€์ • ์—†์ด ํฌ๊ธฐ ๋ณ€๊ฒฝ

6. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ด๋™: move_page_tables()๊ฐ€ PUDโ†’PMDโ†’PTE ์ˆœ์„œ๋กœ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

7. ์„ฑ๋Šฅ ์ตœ์ ํ™”:

- try_realign_addr(): PMD ๊ฒฝ๊ณ„์— ์ •๋ ฌํ•˜์—ฌ ์ฒ˜๋ฆฌ ํšจ์œจ ํ–ฅ์ƒ

- mremap_folio_pte_batch(): large folio์˜ ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ PTE๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ

8. ๋งˆ๋ฌด๋ฆฌ: mmap ์ž ๊ธˆ ํ•ด์ œ, userfaultfd ์•Œ๋ฆผ, mlock๋œ ์˜์—ญ populate ์ฒ˜๋ฆฌ


์กฐ๊ฑด๋ณ„ ๋น„๊ต

1. MREMAP ํ”Œ๋ž˜๊ทธ๋ณ„ ๋™์ž‘

ํ”Œ๋ž˜๊ทธ๊ฐ’๋™์ž‘์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
MREMAP_MAYMOVE1์ด๋™ ํ—ˆ์šฉ๊ธฐ๋ณธ mremap ์‚ฌ์šฉ ์‹œ
MREMAP_FIXED2๊ณ ์ • ์ฃผ์†Œ๋กœ ์ด๋™ํŠน์ • ์ฃผ์†Œ์— ๋งคํ•‘ ๊ฐ•์ œ
MREMAP_DONTUNMAP4์›๋ณธ ์œ ์ง€ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ

2. ๋ฆฌ๋งคํ•‘ ํƒ€์ž…๋ณ„ ์ฒ˜๋ฆฌ

ํƒ€์ž…์กฐ๊ฑด์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•๊ฒฐ๊ณผ
MREMAP_NO_RESIZEold_len == new_len์ฆ‰์‹œ ๋ฐ˜ํ™˜no-op
MREMAP_SHRINKold_len > new_lenin-place ์ถ•์†Œ์ƒˆ ์ฃผ์†Œ ๋ฐ˜ํ™˜
MREMAP_EXPANDold_len < new_lenin-place ํ™•์žฅ ๋˜๋Š” ์ด๋™์ƒˆ ์ฃผ์†Œ ๋ฐ˜ํ™˜

3. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ด๋™ ์šฐ์„ ์ˆœ์œ„

๋ ˆ๋ฒจ์กฐ๊ฑด์ด๋™ ๋ฐฉ๋ฒ•์„ฑ๋Šฅ
HPAGE_PUDHuge PUD + ์ •๋ ฌmove_huge_pud()๊ฐ€์žฅ ๋น ๋ฆ„
NORMAL_PUD์ผ๋ฐ˜ PUD + ์ •๋ ฌmove_normal_pud()๋น ๋ฆ„
HPAGE_PMDHuge PMD + ์ •๋ ฌmove_huge_pmd()๋น ๋ฆ„
NORMAL_PMD์ผ๋ฐ˜ PMD + ์ •๋ ฌmove_normal_pmd()๋ณดํ†ต
PTE์ผ๋ฐ˜ ํŽ˜์ด์ง€move_ptes()๊ฐ€์žฅ ๋А๋ฆผ

4. ํŠน์ˆ˜ ์ฒ˜๋ฆฌ ์กฐ๊ฑด

์กฐ๊ฑด์ฒ˜๋ฆฌ์„ค๋ช…
HugeTLB ํŽ˜์ด์ง€move_hugetlb_page_tables()๋ณ„๋„์˜ huge page ํ…Œ์ด๋ธ” ์ด๋™
uffd-wp ๋“ฑ๋กpte_clear_uffd_wp()userfaultfd-wp ๋งˆ์ปค ์ œ๊ฑฐ
VM_ACCOUNTvrm_calc_charge()๋ฉ”๋ชจ๋ฆฌ ๊ณ„์ • ์ฒญ๊ตฌ
VM_LOCKEDpopulate_expandํ™•์žฅ ์˜์—ญ ์ž๋™ populate
KSM ํŽ˜์ด์ง€ksm_madvise(MADV_UNMERGEABLE)KSM ๋ณ‘ํ•ฉ ํ•ด์ œ
mseal()๋œ VMAcheck_prep_vma()์—์„œ ๊ฑฐ๋ถ€๋ด‰์ธ๋œ VMA ๋ฆฌ๋งคํ•‘ ๋ถˆ๊ฐ€
large foliomremap_folio_pte_batch()์—ฌ๋Ÿฌ PTE ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

5. ํ™•์žฅ/์ถ•์†Œ ๋™์ž‘ ๋น„๊ต

์—ฐ์‚ฐ์กฐ๊ฑดin-place ๊ฐ€๋Šฅ์ด๋™ ํ•„์š”
ํ™•์žฅ (EXPAND)VMA ๋๋ถ€๋ถ„ + ๋นˆ ๊ณต๊ฐ„๊ฐ€๋Šฅ๋ถˆ๊ฐ€๋Šฅ ์‹œ ์ด๋™
์ถ•์†Œ (SHRINK)VMA ๋๋ถ€๋ถ„ ์ œ๊ฑฐํ•ญ์ƒ ๊ฐ€๋Šฅ๋ถˆํ•„์š”
์ด๋™ (MOVE)์ƒˆ ์ฃผ์†Œ ์ง€์ •๋ถˆ๊ฐ€๋Šฅํ•ญ์ƒ ์ด๋™

6. rmap ์ž ๊ธˆ ์ฒ˜๋ฆฌ ๋น„๊ต

๋ ˆ๋ฒจrmap ์ž ๊ธˆ์„ค๋ช…
NORMAL_PMDํ•ญ์ƒ ํ•„์š”์ผ๋ฐ˜ PMD ๋ ˆ๋ฒจ ์ด๋™
NORMAL_PUDํ•ญ์ƒ ํ•„์š”์ผ๋ฐ˜ PUD ๋ ˆ๋ฒจ ์ด๋™
HPAGE_PMD์„ ํƒ์ Huge PMD ์ด๋™ ์‹œ
HPAGE_PUD์„ ํƒ์ Huge PUD ์ด๋™ ์‹œ
PTE์„ ํƒ์ PTE ๋ ˆ๋ฒจ ์ด๋™ ์‹œ

๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • VMA / mmap
  • Compaction
  • Migration
  • mprotect
  • Page Table Walk