User virtual maps (mmap2)

현재 유저 주소 공간의 vm(가상 메모리)에 file 또는 anon 매핑을 요청한다.

 

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

 

인수

  • addr
    • 매핑을 원하는 시작 가상 주소 값으로 커널이 이를 hint로 이용하여 적절한 주소를 찾으려고 한다. null이 입력되는 경우 커널이 빈 공간을 찾는다.
  • length
    • 매핑할 길이(bytes)
  • prot
    • 메모리 보호 속성
      • PROT_EXEC: 페이지는 실행 가능하다.
      • PROT_READ: 페이지는 읽기 가능하다.
      • PROT_WRITE: 페이지는 쓰기 가능하다.
      • PROT_NONE: 페이지는 접근할 할 수 없다.
  • flags
    • 플래그
  • fd
    • 파일 디스크립터
  • offset
    • 파일 offset
      • file 매핑하는 경우 skip 할 페이지 수를 사용한다.

 

요청 플래그

  • MAP_FIXED
    • 지정된 시작 가상 주소로 매핑한다. 지정된 주소를 사용할 수 없는 경우 mmap()은 실패한다.
    • 가상 시작 주소 addr는 페이지 사이즈의 배수여야 한다.
    • 요청 영역이 기존 매핑과 중복되는 경우 중복되는 영역의 기존 매핑은 제거된다.
  • MAP_ANONYMOUS
    • 어떠한 파일하고도 연결되지 않고 0으로 초기화된 영역.
    • fd는 무시되지만 어떤 경우에는 -1이 요구된다.
    • offset는 0으로 한다.
    • MAP_SHARED와 같이 사용될 수도 있다. (커널 v2.4)
  • MAP_FILE
    • 파일 매핑
  •  MAP_SHARED
    • 다른 프로세스와 영역에 대해 공유 매핑.
  • MAP_PRIVATE
    • private COW(Copy On Write) 매핑을 만든다. 이 영역은 다른 프로세스와 공유되지 않는다.
  •  MAP_DENYWRITE
    • 쓰기 금지된 매핑 영역
  •  MAP_EXECUTABLE
    • 실행 가능한 매핑 영역
  •  MAP_GROWSDOWN
    • 하향으로 자라는 스택에 사용된다.
  • MAP_HUGETLB (커널 v2.6.32)
    • huge page들을 사용하여 할당하게 한다.
  • MAP_HUGE_2MB, MAP_HUGE_1GB (커널 v3.8)
    • MAP_HUGETLB와 같이 사용되며 huge page를 선택할 수 있다.
    • “/sys/kernel/mm/hugepages” 디렉토리에 사용할 수 있는 huge page 종류를 볼 수 있다.
  •  MAP_LOCKED
    • mlock()과 동일하게 요청 영역은 물리메모리가 미리 할당되어 매핑된다.
    • mlock()과 다르게 물리메모리를 미리 할당하고 매핑할 때 실패하더라도 곧바로 -ENOMEM 에러를 발생시키지 않는다.
  •  MAP_NONBLOCK
    • 오직 MAP_POPULATE와 같이 사용될 때에만 의미가 있다.
  •  MAP_NORESERVE
    • 이 영역은 swap 공간을 준비하지 않는다.
  •  MAP_POPULATE
    • 매핑에 대한 페이지 테이블을 활성화(prefault)한다.
    • private 매핑에서만 사용된다. (커널 v2.6.23)
  • MAP_STACK (커널 v2.6.27)
    • 프로세스 또는 스레드 스택을 할당한다.
  • MAP_UNINITIALIZED (커널 v2.6.33)
    • anonymous 페이지들을 클리어하지 않는다.

 

sys_mmap2()

arch/arm/kernel/entry-common.S

/* 
 * Note: off_4k (r5) is always units of 4K.  If we can't do the requested
 * offset, we return EINVAL.
 */
sys_mmap2: 
#if PAGE_SHIFT > 12
                tst     r5, #PGOFF_MASK
                moveq   r5, r5, lsr #PAGE_SHIFT - 12
                streq   r5, [sp, #4]
                beq     sys_mmap_pgoff
                mov     r0, #-EINVAL
                ret     lr
#else
                str     r5, [sp, #4]
                b       sys_mmap_pgoff
#endif
ENDPROC(sys_mmap2)

유저 anon 및 file 캐시 매핑을 요청한다.

 

sys_mmap_pgoff()

mm/mmap.c

SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
                unsigned long, prot, unsigned long, flags,
                unsigned long, fd, unsigned long, pgoff)
{
        struct file *file = NULL;
        unsigned long retval = -EBADF;
                                      
        if (!(flags & MAP_ANONYMOUS)) {
                audit_mmap_fd(fd, flags);
                file = fget(fd);      
                if (!file)
                        goto out;
                if (is_file_hugepages(file))
                        len = ALIGN(len, huge_page_size(hstate_file(file)));
                retval = -EINVAL;
                if (unlikely(flags & MAP_HUGETLB && !is_file_hugepages(file)))
                        goto out_fput;
        } else if (flags & MAP_HUGETLB) {
                struct user_struct *user = NULL;
                struct hstate *hs;
                 
                hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & SHM_HUGE_MASK);
                if (!hs)              
                        return -EINVAL;

                len = ALIGN(len, huge_page_size(hs));
                /*
                 * VM_NORESERVE is used because the reservations will be
                 * taken when vm_ops->mmap() is called
                 * A dummy user value is used because we are not locking
                 * memory so no accounting is necessary
                 */
                file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,
                                VM_NORESERVE,
                                &user, HUGETLB_ANONHUGE_INODE,
                                (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);
                if (IS_ERR(file))
                        return PTR_ERR(file);
        }

        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);

        retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);
out_fput:
        if (file)
                fput(file);
out:
        return retval;
}

file 디스크립터에서 pgoff 페이지 부터 요청 길이만큼 요청한 가상 주소에  prot 속성으로 매핑한다.

  • 코드 라인 8~17에서 file 매핑인 경우 audit 설정이 필요 시 설정하고 huge 페이지를 사용하는 파일인 경우 길이를 huge 페이지에 맞춰 정렬한다.
    • 코드 라인 9에서 현재 태스크의 audit_context가 설정된 경우 audit_context에 요청 fd와 flags를 설정하고 타입으로 AUDIT_MMAP을 대입한다.
    • 코드 라인 10~12에서 file 디스크립터를 통해 file을 알아오는데 실패하는 경우 out 레이블을 통해 -EBADF 에러를 반환한다.
    • 코드 라인 13~14에서 hugetlbfs 또는 huge 페이지 매핑을 이용하는 공유 파일인 경우 길이를 huge 페이지 단위로 정렬한다.
    • 코드 라인 15~17에서 MAP_HUGETLB 플래그 요청을 하였지만 file이 huge 페이지 요청 타입이 아닌 경우 -EINVAL 에러를 반환한다.
  • 코드 라인 18~39에서 MAP_HUGETLB 요청이 있는 경우 길이를 huge 페이지 단위로 정렬하여 HUGETLB_ANON_FILE 형태의 파일을 준비한다.
    • 코드 라인 22~24에서 플래그에 기록된 huge 페이지 정보가 발견되지 않으면 -EINVAL 에러를 반환한다.
      • 플래그의 bit26~bit31에 사이즈를 로그 단위로 변환하여 저장하였다.
        • x86 예) 21 -> 2M huge page, 30 -> 1G huge page
    • 코드 라인 26에서 길이를 huge 페이지 단위로 정렬한다.
    • 코드 라인 33~38에서 HUGETLB_ANON_FILE 형태의 파일을 준비한다. 실패하는 경우 에러를 반환한다.
  • 코드 라인 41~43에서 플래그에서 MAP_EXECUTABLE 및 MAP_DENYWRITE를 제외하고 매핑을 한다.

 

is_file_hugepages()

include/linux/hugetlb.h

static inline int is_file_hugepages(struct file *file)
{               
        if (file->f_op == &hugetlbfs_file_operations)
                return 1;
        if (is_file_shm_hugepages(file)) 
                return 1;
         
        return 0;       
}

file이 hugetlbfs에서 사용하는 파일이거나 huge 페이지를 사용하는 공유 메모리 파일인 경우 1을 반환한다. 그 외에는 0을 반환한다.

 

Audit 관련 함수

audit_mmap_fd()

include/linux/audit.h

static inline void audit_mmap_fd(int fd, int flags)
{
        if (unlikely(!audit_dummy_context()))
                __audit_mmap_fd(fd, flags);
}

작은 확률로 현재 태스크의 audit_context가 설정된 경우 audit_context에 요청 fd와 flags를 설정하고 타입으로 AUDIT_MMAP을 대입한다.

 

audit_dummy_context()

include/linux/audit.h

static inline int audit_dummy_context(void)
{
        void *p = current->audit_context;
        return !p || *(int *)p;
}

현재 태스크의 audit_context가 설정된 경우 0을 반환하고, 설정되지 않은 경우 0이 아닌 값을 반환한다.

 

__audit_mmap_fd()

kernel/auditsc.c

void __audit_mmap_fd(int fd, int flags) 
{
        struct audit_context *context = current->audit_context;
        context->mmap.fd = fd;
        context->mmap.flags = flags;
        context->type = AUDIT_MMAP;
}

현재 태스크의 audit_context에 요청 fd와 flags를 설정하고 타입으로 AUDIT_MMAP을 대입한다.

 

vm_mmap_pgoff()

mm/util.c

unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,
        unsigned long len, unsigned long prot,
        unsigned long flag, unsigned long pgoff)
{
        unsigned long ret;
        struct mm_struct *mm = current->mm;
        unsigned long populate;

        ret = security_mmap_file(file, prot, flag);
        if (!ret) {
                down_write(&mm->mmap_sem);
                ret = do_mmap_pgoff(file, addr, len, prot, flag, pgoff,
                                    &populate);
                up_write(&mm->mmap_sem);
                if (populate)
                        mm_populate(ret, populate);
        }
        return ret;
}

file을 pgoff 페이지 부터 요청 길이만큼 가상 주소 addr에  prot 속성으로 매핑한다.

  • 코드 라인 9에서 LSM 및 LIM을 통해 파일 매핑의 허가 여부를 알아온다. 0=성공
    • LSM(Linux Security Module)과 LIM(Linux Integrity Module)의 hook api를 호출하여 한다.
  • 코드 라인 10~13에서 mmap_sem write 세마포어 락을 사용하여 vm 매핑을 요청한다.
  • 코드 라인 14~15에서 매핑 영역을 활성화(물리 RAM을 매핑)한다.

 

do_mmap_pgoff()

mm/mmap.c

/* 
 * The caller must hold down_write(&current->mm->mmap_sem).
 */

unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                        unsigned long len, unsigned long prot,
                        unsigned long flags, unsigned long pgoff,
                        unsigned long *populate)
{
        struct mm_struct *mm = current->mm;
        vm_flags_t vm_flags;
                         
        *populate = 0;

        /*
         * Does the application expect PROT_READ to imply PROT_EXEC?
         *
         * (the exception is when the underlying filesystem is noexec
         *  mounted, in which case we dont add PROT_EXEC.)
         */
        if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
                if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC)))
                        prot |= PROT_EXEC; 

        if (!len)
                return -EINVAL; 

        if (!(flags & MAP_FIXED))
                addr = round_hint_to_min(addr);

        /* Careful about overflows.. */
        len = PAGE_ALIGN(len);
        if (!len)
                return -ENOMEM;

        /* offset overflow? */
        if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
                return -EOVERFLOW;

        /* Too many mappings? */
        if (mm->map_count > sysctl_max_map_count)
                return -ENOMEM;

        /* Obtain the address to map to. we verify (or select) it and ensure
         * that it represents a valid section of the address space.
         */
        addr = get_unmapped_area(file, addr, len, pgoff, flags);
        if (addr & ~PAGE_MASK)
                return addr;

file을 pgoff 페이지 부터 요청 길이만큼 가상 주소 addr에  prot 속성으로 매핑한다. 실제 메모리가 매핑된 경우 출력 인수 populate에 길이를 대입한다.

  • 코드 라인 21~23에서 read 속성이 요청되는 경우 현재 태스크의 personality에 READ_IMPLIES_EXEC가 설정된 경우 exec 속성을 추가한다. 단 sysfs 및 proc 마운트 위의 file과 같이 마운트 플래그가  MNT_NOEXEC 설정이 있는경우는 제외한다.
  • 코드 라인 25~26에서 길이가 0인 경우 -EINVAL 에러를 반환한다.
  • 코드 라인 28~29에서 고정 매핑을 요청한 경우가 아니면 페이지 정렬한 주소를 사용하되 mmap_min_addr 보다 작은 경우 mmap_min_addr 주소로 변경한다.
  • 코드 라인 32~34에서 길이를 페이지 단위로 정렬한다. 단 0인 경우 -ENOMEM 에러를 반환한다.
  • 코드 라인 37~38에서 pgoff 페이지+ len 페이지가 시스템 주소 범위를 초과하는 경우 -EOVERFLOW 에러를 반환한다.
  • 코드 라인 41~42에서 현재 태스크의 메모리 디스크립터에 매핑된 수가 최대치 허용을 초과한 경우 -ENOMEM 에러를 반환한다.
    • 기본 매핑 최대치: 65530 (“/proc/sys/vm/max_map_count”에서 설정)
  • 코드 라인 47~49에서 매핑되지 않은 영역을 찾아 시작 가상 주소를 알아온다. 만일 에러인 경우 에러 코드를 반환한다.
.
        /* Do simple checking here so the lower-level routines won't have
         * to. we assume access permissions have been handled by the open
         * of the memory object, so we don't do any here.
         */
        vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
                        mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;

        if (flags & MAP_LOCKED)
                if (!can_do_mlock())
                        return -EPERM;

        if (mlock_future_check(mm, vm_flags, len))
                return -EAGAIN;

        if (file) {
                struct inode *inode = file_inode(file);

                switch (flags & MAP_TYPE) {
                case MAP_SHARED:
                        if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
                                return -EACCES;

                        /*
                         * Make sure we don't allow writing to an append-only
                         * file..
                         */
                        if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))
                                return -EACCES;

                        /*
                         * Make sure there are no mandatory locks on the file.
                         */
                        if (locks_verify_locked(file))
                                return -EAGAIN;

                        vm_flags |= VM_SHARED | VM_MAYSHARE;
                        if (!(file->f_mode & FMODE_WRITE))
                                vm_flags &= ~(VM_MAYWRITE | VM_SHARED);

                        /* fall through */
                case MAP_PRIVATE:
                        if (!(file->f_mode & FMODE_READ))
                                return -EACCES;
                        if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
                                if (vm_flags & VM_EXEC)
                                        return -EPERM;
                                vm_flags &= ~VM_MAYEXEC;
                        }
                        if (!file->f_op->mmap)
                                return -ENODEV;
                        if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
                                return -EINVAL;
                        break;

                default:
                        return -EINVAL;
                }
        } else {
                switch (flags & MAP_TYPE) {
                case MAP_SHARED:
                        if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
                                return -EINVAL;
                        /*
                         * Ignore pgoff.
                         */
                        pgoff = 0;
                        vm_flags |= VM_SHARED | VM_MAYSHARE;
                        break;
                case MAP_PRIVATE:
                        /*
                         * Set pgoff according to addr for anon_vma.
                         */
                        pgoff = addr >> PAGE_SHIFT;
                        break;
                default:
                        return -EINVAL;
                }
        }

        /*
         * Set 'VM_NORESERVE' if we should not account for the
         * memory use of this mapping.
         */
        if (flags & MAP_NORESERVE) {
                /* We honor MAP_NORESERVE if allowed to overcommit */
                if (sysctl_overcommit_memory != OVERCOMMIT_NEVER)
                        vm_flags |= VM_NORESERVE;

                /* hugetlb applies strict overcommit unless MAP_NORESERVE */
                if (file && is_file_hugepages(file))
                        vm_flags |= VM_NORESERVE;
        }

        addr = mmap_region(file, addr, len, vm_flags, pgoff);
        if (!IS_ERR_VALUE(addr) &&
            ((vm_flags & VM_LOCKED) ||
             (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
                *populate = len;
        return addr;
}
  • 코드 라인 6~7에서 vm_flags에 vm 플래그로 변환한 prot 플래그, vm 플래그로 변환한 flags, 메모리 디스크립터의 기본 플래그에 mayread, maywrite, mayexec를 추가한다.
  • 코드 라인 9~11에서 MAP_LOCKED 플래그 요청이 있는 경우 mlock 최대치 제한 등으로 인해 수행할 수 없으면 -EPERM 에러를 반환한다.
  • 코드 라인 13~14에서 VM_LOCKED 요청이 있고 기존 mlock 페이지에 요청 길이를 추가하여 mlock 페이지 최대치 제한에 걸리는 경우 -EAGAIN 에러를 반환한다.
  • 코드 라인 16~59에서 file 매핑인 경우 inode 값을 가져오고 다음 두 가지 요청을 수행한다.
    • 코드 라인 19~41에서 공유 파일 매핑(MAP_SHARED) 플래그 요청인 경우
      • 코드 라인 20~21에서 write 속성이 없는 파일에 write 요청을 한 경우 -EACCESS 에러를 반환한다.
      • 코드 라인 27~28에서 append only 파일인 경우 write 요청을 한 경우  -EACCESS 에러를 반환한다.
      • 코드 라인 33~34에서 mandatory 락이 걸려있는 파일인 경우 -EAGAIN 에러를 반환한다.
      • 코드 라인 36에서 vm_flags에 share 및 mayshare 속성을 추가한다.
      • 코드 라인 37~39에서 write 속성이 없는 파일인 경우 maywrite와 shared를 제거한다. 아래 private 요청을 계속 진행한다.
    • 코드 라인 42~55에서 private  파일 매핑(MAP_PRIVATE) 플래그 요청인 경우
      • 코드 라인 43~44에서 read 속성이 없는 파일인 경우 -EACCESS 에러를 반환한다.
      • 코드 라인 45~49에서 file이 마운트 곳의 마운트 플래그에 실행 금지가 설정된 경우 mayexec를 제거한다. 단 VM_EXEC 요청이 있는 경우에는 -EPERM 에러를 반환한다.
        • “/proc 및 /sys”가 마운트 되어 있는 곳은 실행 파일이 있을 수 없다.
      • 코드 라인 51~52에서 매핑이 없는 파일은 -ENODEV 에러를 반환한다.
      • 코드 라인 53~54에서 growsdown 및 growsup 요청된 경우 -EINVAL 에러를 반환한다.
  • 코드 라인 60~80에서 anon 매핑인 경우 다음 두 가지 요청을 수행한다.
    • 코드 라인 61~70에서shared anon 매핑인 경우 pgoff=0, shared 및 maysahre 플래그를 추가한다. 단 growsdown 및 growsup 요청된 경우 -EINVAL 에러를 반환한다.
    • 코드 라인 71~76에서 private anon 매핑인 경우 pgoff에 가상 주소 페이지 번호를 대입한다.
  • 코드 라인 86~94에서 no reserve 요청인 경우 over commit 모드가 OVERCOMMIT_NEVER가 아닌 경우 또는 또한 huge page 파일인 경우  vm_noreserve 플래그를 추가한다.
  • 코드 라인 96에서 file을 pgoff 페이지 부터 가상 주소 addr에 길이 len 만큼 vm_flags 속성을 사용하여 vma를 구성하고 등록한다.
  • 코드 라인 97~100에서 실제 메모리가 매핑된 경우 출력 인수 populate에 길이를 대입한다.

 

round_hint_to_min()

mm/mmap.c

static inline unsigned long round_hint_to_min(unsigned long hint)
{
        hint &= PAGE_MASK;
        if (((void *)hint != NULL) &&
            (hint < mmap_min_addr))
                return PAGE_ALIGN(mmap_min_addr);
        return hint;
}

가상 주소 hint를 페이지 단위로 절삭하고 반환한다. 단 그 주소가 mmap_min_addr 보다 낮은 경우 페이지 단위로 정렬한 mmap_min_addr 주소를 반환한다.

 

calc_vm_prot_bits()

include/linux/mman.h

/*
 * Combine the mmap "prot" argument into "vm_flags" used internally.
 */
static inline unsigned long
calc_vm_prot_bits(unsigned long prot)
{
        return _calc_vm_trans(prot, PROT_READ,  VM_READ ) |
               _calc_vm_trans(prot, PROT_WRITE, VM_WRITE) |
               _calc_vm_trans(prot, PROT_EXEC,  VM_EXEC) |
               arch_calc_vm_prot_bits(prot);
}

prot 값에 PROT_READ, PROT_WRITE, PROT_EXEC 비트가 있는 경우 각각 VM_READ, VM_WRITE, VM_EXEC 플래그 속성으로 변환하여 반환한다.

  • 특정 아키텍처에 맞게 속성을 추가할 수 있다. (arm은 추가 없음)

 

_calc_vm_trans()

include/linux/mman.h

/*
 * Optimisation macro.  It is equivalent to:
 *      (x & bit1) ? bit2 : 0
 * but this version is faster. 
 * ("bit1" and "bit2" must be single bits)
 */
#define _calc_vm_trans(x, bit1, bit2) \
  ((bit1) <= (bit2) ? ((x) & (bit1)) * ((bit2) / (bit1)) \
   : ((x) & (bit1)) / ((bit1) / (bit2)))

x 플래그에서 bi1 속성이 있는 경우 bit 속성으로 변환하여 반환한다. 없는 경우 0을 반환한다.

 

calc_vm_flag_bits()

include/linux/mman.h

/*
 * Combine the mmap "flags" argument into "vm_flags" used internally.
 */ 
static inline unsigned long
calc_vm_flag_bits(unsigned long flags)
{
        return _calc_vm_trans(flags, MAP_GROWSDOWN,  VM_GROWSDOWN ) |
               _calc_vm_trans(flags, MAP_DENYWRITE,  VM_DENYWRITE ) |
               _calc_vm_trans(flags, MAP_LOCKED,     VM_LOCKED    );
}

flags 값에 MAP_GROWSDOWN, MAP_DENYWRITE, MAP_LOCKED 비트가 있는 경우 각각 VM_GROWSDOWN, VM_DENYWRITE, VM_LOCKED 플래그 속성으로 변환하여 반환한다.

 

unsigned long mmap_region(struct file *file, unsigned long addr,
                unsigned long len, vm_flags_t vm_flags, unsigned long pgoff)
{
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev;
        int error;
        struct rb_node **rb_link, *rb_parent;
        unsigned long charged = 0;

        /* Check against address space limit. */
        if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
                unsigned long nr_pages;

                /*
                 * MAP_FIXED may remove pages of mappings that intersects with
                 * requested mapping. Account for the pages it would unmap.
                 */
                if (!(vm_flags & MAP_FIXED))
                        return -ENOMEM;

                nr_pages = count_vma_pages_range(mm, addr, addr + len);

                if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))
                        return -ENOMEM;
        }

        /* Clear old maps */
        error = -ENOMEM;
munmap_back:
        if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) {
                if (do_munmap(mm, addr, len))
                        return -ENOMEM;
                goto munmap_back;
        }

        /*
         * Private writable mapping: check memory availability
         */
        if (accountable_mapping(file, vm_flags)) {
                charged = len >> PAGE_SHIFT;
                if (security_vm_enough_memory_mm(mm, charged))
                        return -ENOMEM;
                vm_flags |= VM_ACCOUNT;
        }

        /*
         * Can we just expand an old mapping?
         */
        vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL);
        if (vma)
                goto out;

file을 pgoff 페이지 부터 요청 길이만큼 가상 주소 addr에  prot 속성으로 vma를 구성하고 등록한다.

  • 코드 라인 11 ~19에서 len 페이지 만큼의 공간을 확장할 수 없는 경우 fix 매핑이 아닌 경우 -ENOMEM 에러를 반환한다.
  • 코드 라인 21~24에서 fix 매핑인 경우 기존 영역과 겹치지 않는 페이지 만큼이 확장 가능하지 않으면 -ENOMEM 에러를 반환환다.
    • fix 매핑인 경우 겹치는 영역만큼은 뺴고 매핑할 계획이다.
  • 코드 라인 29~34에서  munmap_back 레이블이다. 요청 영역과 겹치는 기존 영역을 언매핑하고 다시 이 레이블로 이동한다. 만일 언매핑이 실패한 경우 -ENOMEM 에러를 반환한다.
  • 코드 라인 39~44에서 vm 메모리 계량이 필요한 매핑인 경우 LSM을 통해 charged 페이지 만큼 vm 메모리 할당으로 commit하고 허용된 경우 VM_ACCOUNT 플래그를 추가한다.
    • accountable 매핑 확인(private writable 매핑)
      • huge 파일 매핑이 아니고 noreserve 및 shared가 없고 write 요청은 있는 경우이다.
    • LSM 모듈에서 SELinux 모듈을 사용 여부에 따라 selinux_vm_enough_memory() 함수를 먼저 호출하여 admin 권한만큼의 추가 영역을 확보한 후 __vm_enough_memory() 함수를 호출하여 commit 량에 대해 commit 옵션에 따라 vm 허용치 제한을 통해  할당 여부를 반환한다.
    • LSM 모듈에서 기본 Posix Capability 모듈만을 사용하는 경우 cap_vm_enough_memory() 함수를 먼저 호출하여 admin권한만큼의 추가 영역을 확보한 후 __vm_enough_memory() 함수를 호출하여 commit 량에 대해 commit 옵션에 따라 vm 허용치 제한을 통해  할당 여부를 반환한다.
  •  코드 라인 49~51에서 기존 영역과 병합할 수 있는 경우이면 병합한다. 병합에 성공한 경우 out 레이블로 이동한다.
.
        /*
         * Determine the object being mapped and call the appropriate
         * specific mapper. the address has already been validated, but
         * not unmapped, but the maps are removed from the list.
         */
        vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
        if (!vma) {
                error = -ENOMEM;
                goto unacct_error;
        }

        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
        vma->vm_flags = vm_flags;
        vma->vm_page_prot = vm_get_page_prot(vm_flags);
        vma->vm_pgoff = pgoff;
        INIT_LIST_HEAD(&vma->anon_vma_chain);

        if (file) {
                if (vm_flags & VM_DENYWRITE) {
                        error = deny_write_access(file);
                        if (error)
                                goto free_vma;
                }
                if (vm_flags & VM_SHARED) {
                        error = mapping_map_writable(file->f_mapping);
                        if (error)
                                goto allow_write_and_free_vma;
                }

                /* ->mmap() can change vma->vm_file, but must guarantee that
                 * vma_link() below can deny write-access if VM_DENYWRITE is set
                 * and map writably if VM_SHARED is set. This usually means the
                 * new file must not have been exposed to user-space, yet.
                 */
                vma->vm_file = get_file(file);
                error = file->f_op->mmap(file, vma);
                if (error)
                        goto unmap_and_free_vma;

                /* Can addr have changed??
                 *
                 * Answer: Yes, several device drivers can do it in their
                 *         f_op->mmap method. -DaveM
                 * Bug: If addr is changed, prev, rb_link, rb_parent should
                 *      be updated for vma_link()
                 */
                WARN_ON_ONCE(addr != vma->vm_start);

                addr = vma->vm_start;
                vm_flags = vma->vm_flags;
        } else if (vm_flags & VM_SHARED) {
                error = shmem_zero_setup(vma);
                if (error)
                        goto free_vma;
        }

        vma_link(mm, vma, prev, rb_link, rb_parent);
        /* Once vma denies write, undo our temporary denial count */
        if (file) {
                if (vm_flags & VM_SHARED)
                        mapping_unmap_writable(file->f_mapping);
                if (vm_flags & VM_DENYWRITE)
                        allow_write_access(file);
        }
        file = vma->vm_file;
  • 코드 라인 7~19에서 vma(vm_area_struct 구조체)를 할당하고 구성한다. 할당이 실패하는 경우 unacct_error로 이동한다.
  • 코드 라인 13~19에서 vma를 구성한다.
  • 코드 라인 21~53에서 file 매핑인 경우
    • 코드 라인 22~26에서 denywrite 요청이 있는 경우 file을 denywrite 상태로 만든다. 실패하는 경우 free_vma 레이블로 이동한다.
    • 코드 라인 27~31에서  shared 요청이 있는 경우 매핑 영역에 대해 다음 writable 파일 매핑 요청이 거절되게 설정한다. 실패하는 경우 allow_write_and_free_vma 레이블로 이동한다.
    • 코드 라인 38에서 file의 사용 카운터 f_count를 증가시키고 file을 vma_vm_file에 대입한다.
    • 코드 라인 39~41에서 vma 정보로 file 매핑을 수행한다. 실패하는 경우 unmap_and_free_vma 레이블로 이동한다.
  • 코드 라인 54~58에서 shared anon 매핑을 준비한다. 실패하는 경우 free_vma 레이블로 이동한다.
    • “/dev/zero” 파일을 vma->vm_file에 지정하고 vma->vm_ops에 전역 shmem_vm_ops를 대입한다.
  • 코드 라인 60에서 vma 정보를 추가한다.
  • 코드 라인 62~67에서 shared file 매핑인 경우 매핑 영역에 대해 writable 매핑이 가능하게 바꾼다. 그리고 denywrite 요청을 가진 file 매핑인 경우 file에 대해 writable 매핑이 가능하도록 설정한다.

 

out:
        perf_event_mmap(vma);

        vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
        if (vm_flags & VM_LOCKED) {
                if (!((vm_flags & VM_SPECIAL) || is_vm_hugetlb_page(vma) ||
                                        vma == get_gate_vma(current->mm)))
                        mm->locked_vm += (len >> PAGE_SHIFT);
                else
                        vma->vm_flags &= ~VM_LOCKED;
        }

        if (file)
                uprobe_mmap(vma);

        /*
         * New (or expanded) vma always get soft dirty status.
         * Otherwise user-space soft-dirty page tracker won't
         * be able to distinguish situation when vma area unmapped,
         * then new mapped in-place (which must be aimed as
         * a completely new data area).
         */
        vma->vm_flags |= VM_SOFTDIRTY;

        vma_set_page_prot(vma);

        return addr;

unmap_and_free_vma:
        vma->vm_file = NULL;
        fput(file);

        /* Undo any partial mapping done by a device driver. */
        unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
        charged = 0;
        if (vm_flags & VM_SHARED)
                mapping_unmap_writable(file->f_mapping);
allow_write_and_free_vma:
        if (vm_flags & VM_DENYWRITE)
                allow_write_access(file);
free_vma:
        kmem_cache_free(vm_area_cachep, vma);
unacct_error:
        if (charged)
                vm_unacct_memory(charged);
        return error;
}
  • 코드 라인 2에서 mmap에 대한 performance events 정보를 출력한다.
  • 코드 라인 4에서 메모리 디스크립터에 대해 가상 메모리에 대한 다음의 vm stat 카운터를 len 페이지만큼 추가한다.
    • mm->total_vm
    • file 매핑인 경우 mm->shared_vm
    • file 매핑이면서 읽기 또는 쓰기 속성이 있는 경우 mm->exec_vm
    • stack 매핑인 경우 mm->stack_mm
  • 코드 라인 5~11에서 mlock(VM_LOCKED) 요청인 경우 VM_SPECIAL, hugetlb 페이지 또는 gate vma 요청인 경우 VM_LOCKED 플래그를 삭제한다. 그렇지 않은 경우 mm->locked_vm에 len 페이지를 추가한다.
  • 코드 라인 13~14에서 file 매핑인 경우 uprobe가 설정되어 있고 uprobe filter 체인이 걸려있는 경우 는 요청 주소에 break point 명령 코드로 업데이트한다.
  • 코드 라인 23~27에서 softdirty 플래그를 추가하고 vma에 기록하고 정상적으로 가상 주소를 반환한다.
  • 코드 라인 29~37에서 unmap_and_free_vma 레이블은 매핑을 해제한다.
  • 코드 라인 38~40에서 allow_write_and_free_vma 레이블은 denywrite 요청이 있는 경우 inode의 i_writecount를 증가 시킨다.
  • 코드 라인 41~42에서 free_vma 레이블은 vma object를 할당 해제 한다.
  • 코드 라인 43~46에서 unacct_error 레이블은 vm 계량을 했었던(Private writable 매핑) 경우 commit 양을 되돌리기 위해 charged 만큼 다시 감소시킨다.

 

count_vma_pages_range()

mm/mmap.c

static unsigned long count_vma_pages_range(struct mm_struct *mm,
                unsigned long addr, unsigned long end)
{
        unsigned long nr_pages = 0;
        struct vm_area_struct *vma;

        /* Find first overlaping mapping */
        vma = find_vma_intersection(mm, addr, end);
        if (!vma)
                return 0;

        nr_pages = (min(end, vma->vm_end) -
                max(addr, vma->vm_start)) >> PAGE_SHIFT;

        /* Iterate over the rest of the overlaps */
        for (vma = vma->vm_next; vma; vma = vma->vm_next) {
                unsigned long overlap_len;

                if (vma->vm_start > end)
                        break;

                overlap_len = min(end, vma->vm_end) - vma->vm_start;
                nr_pages += overlap_len >> PAGE_SHIFT;
        }

        return nr_pages;
}

요청 영역과 기존 vma 영역과 겹치는 페이지 수를 반환한다. 겹치는 영역이 없으면 0을 반환한다.

  • 코드 라인 8~10에서 기존 vma 영역과 요청 영역이 겹치는 경우 관련 vma를 알아온다. 겹치는 영역이 없는 경우 0을 반환한다.
  • 코드 라인 12~13에서 현재 vma에서 겹치는 페이지 수를 산출한다.
  • 코드 라인 16~26에서 다음 vma 영역들과도 비교하여 겹치는 페이지 수를 산출한 후 반환한다.

 

accountable_mapping()

mm/mmap.c

/*
 * We account for memory if it's a private writeable mapping,
 * not hugepages and VM_NORESERVE wasn't set.
 */
static inline int accountable_mapping(struct file *file, vm_flags_t vm_flags)
{
        /*
         * hugetlb has its own accounting separate from the core VM
         * VM_HUGETLB may not be set yet so we cannot check for that flag.
         */
        if (file && is_file_hugepages(file))
                return 0;

        return (vm_flags & (VM_NORESERVE | VM_SHARED | VM_WRITE)) == VM_WRITE;
}

vm 메모리 계량이 필요한 매핑인지 확인한다.

  • accountable 매핑 확인(private writable 매핑)
    • huge 파일 매핑이 아니고 noreserve 및 shared가 없고 write 요청은 있는 경우이다.

 

writable 파일 매핑(1)

inode->i_writecount 값 상태

  • 0(writable)
    • write 권한이 허용되지 않은 상태로 새로운 writable 권한 요청이 가능한 상태
  • 1(write)
    • write 권한이 허용된 상태로 새로운 write 권한 요청은 금지된 상태
  • 음수(denywrite)
    • denywrite 요청에 의해 모든 write 권한 요청이 금지된 상태

 

get_write_access()

include/linux/fs.h

/*
 * get_write_access() gets write permission for a file.
 * put_write_access() releases this write permission.
 * This is used for regular files.
 * We cannot support write (and maybe mmap read-write shared) accesses and
 * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode
 * can have the following values:
 * 0: no writers, no VM_DENYWRITE mappings
 * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist
 * > 0: (i_writecount) users are writing to the file.
 *
 * Normally we operate on that counter with atomic_{inc,dec} and it's safe
 * except for the cases where we don't hold i_writecount yet. Then we need to
 * use {get,deny}_write_access() - these functions check the sign and refuse
 * to do the change if sign is wrong.
 */
static inline int get_write_access(struct inode *inode)
{
        return atomic_inc_unless_negative(&inode->i_writecount) ? 0 : -ETXTBSY;
}

inode에 대해 write 권한을 요청한다. 성공하면 0을 반환하고, 실패하는 경우 -ETXTBSY 에러를 반환한다.

  • inode->i_writecount를 음수(-)가 아닌한 증가시킨다. 성공한 경우 0을 반환하고 , 이미 음수(-)여서 증가가 불가능한 경우 -ETXTBSY 에러를 반환한다.

 

put_write_access()

include/linux/fs.h

static inline void put_write_access(struct inode * inode)
{
        atomic_dec(&inode->i_writecount);
}

inode에 대해 write 권한을 제거한다.

  • inode->i_writecount를 감소시킨다.

 

deny_write_access()

include/linux/fs.h

static inline int deny_write_access(struct file *file)
{
        struct inode *inode = file_inode(file);
        return atomic_dec_unless_positive(&inode->i_writecount) ? 0 : -ETXTBSY;
}

요청 file을 denywrite 상태로 만들어 writable 매핑을 만들 수 없게 금지한다. 성공한 경우 0을 반환하고 그렇지 않은 경우  -ETXTBSY 에러를 반환한다.

  • 요청 file의 inode->i_writecount를 양수(+)가 아닌한 감소시킨다. 성공한 경우 0을 반환하고  이미 양수(+)여서 감소가 불가능한 경우 -ETXTBSY 에러를 반환한다.

 

allow_write_access()

include/linux/fs.h

static inline void allow_write_access(struct file *file)
{
        if (file)       
                atomic_inc(&file_inode(file)->i_writecount);
}

요청 파일에 대해 denywrite를 제거하여 새로운 writable 매핑을 허용할 수 있는 상태로 변경한다.

  • 요청 file의 inode->i_writecount를 증가시킨다.

 

writable 파일 매핑(2)

mapping->i_mapwritable 값 상태 (VM_SHARED 카운터)

  • 0(writable)
    • 공유 write 매핑되어 있지 않은 상태로 새로운 writable 매핑 요청이 가능한 상태
  • 양수(write)
    • 공유 write 매핑되어 있는 상태로 새로운 write 매핑 요청은 금지된 상태
  • 음수(denywrite)
    • 공유 denywrite 요청에 의해 모든 write 매핑이 금지된 상태이다.

 

 

mapping_map_writable()

include/linux/fs.h

static inline int mapping_map_writable(struct address_space *mapping)
{
        return atomic_inc_unless_negative(&mapping->i_mmap_writable) ?
                0 : -EPERM;
}

매핑 영역을 writable 공유 매핑 상태로 요청한다. 성공하면 0을 반환하고, 실패하는 경우 -EPERM을 반환한다.

  • mapping->i_mmapwritable을 음수(-)가 아닌한 증가시킨다. 성공한 경우 0을 반환하고 , 이미 음수(-)여서 증가가 불가능한 경우 -EPERM 에러를 반환한다.

 

mapping_unmap_writable()

include/linux/fs.h

static inline void mapping_unmap_writable(struct address_space *mapping)
{
        atomic_dec(&mapping->i_mmap_writable);
}

writable 공유 매핑 영역을 언맵 요청하여 writable 상태로 변경한다. 다시 새로운 writable 매핑 요청을 받을 수 있는 상태이다.

  • mapping->i_mmap_writable을 감소시킨다.

 

mapping_deny_writable()

include/linux/fs.h

static inline int mapping_deny_writable(struct address_space *mapping)
{
        return atomic_dec_unless_positive(&mapping->i_mmap_writable) ?
                0 : -EBUSY;
}

요청 매핑 영역에 대해 denywritable 상태로 변경하여 writable 매핑을 만들 수 없게 금지한다. 성공하면 0을 반환하고, 실패하는 경우 -EBUSY를 반환한다.

  • mapping->i_writecount를 양수(+)가 아닌한 감소시킨다. 성공한 경우 0을 반환하고 , 이미 양수(+)여서 감소가 불가능한 경우 -EPERM 에러를 반환한다.

 

mapping_allow_writable()

include/linux/fs.h

static inline void mapping_allow_writable(struct address_space *mapping)
{               
        atomic_inc(&mapping->i_mmap_writable);
}

요청 매핑 영역에 대해 denywrite를 제거하여 새로운 writable 매핑을 받을 수 있는 상태로 변경한다.

 

vm_stat_account()

mm/mmap.c

void vm_stat_account(struct mm_struct *mm, unsigned long flags,
                                                struct file *file, long pages)
{
        const unsigned long stack_flags
                = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);

        mm->total_vm += pages;

        if (file) {
                mm->shared_vm += pages;
                if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
                        mm->exec_vm += pages;
        } else if (flags & stack_flags)
                mm->stack_vm += pages;
}

매핑 용도에 맞게 각각의 vm stat에 대해 pages 만큼 추가한다.

  • 코드 라인 4~5에서 스택영역의 할당 요청인 경우
  • 코드 라인 7에서 mm->total_vm 카운터에 페이지 수를 추가한다.
  • 코드 라인 9~12에서 file 매핑인 경우 mm->shared_vm 카운터에 페이지 수를 추가한다. 만일 실행 및 쓰기 요청이 있는 경우에는 mm->exec_vm 카운터에도 페이지 수를 추가한다.
  • 코드 라인 13~14에서 stack 매핑인 경우 mm->stack_vm 카운터에 페이지 수를 추가한다.

 

vma_set_page_prot()

mm/mmap.c

/* Update vma->vm_page_prot to reflect vma->vm_flags. */
void vma_set_page_prot(struct vm_area_struct *vma)
{
        unsigned long vm_flags = vma->vm_flags;

        vma->vm_page_prot = vm_pgprot_modify(vma->vm_page_prot, vm_flags);
        if (vma_wants_writenotify(vma)) {
                vm_flags &= ~VM_SHARED;
                vma->vm_page_prot = vm_pgprot_modify(vma->vm_page_prot,
                                                     vm_flags);
        }
}

vma->vm_flags에 맞는 메모리 속성 값으로 vma->vm_page_prot 속성 값을 업데이트한다.

  • 코드 라인 6에서 요청 vma 영역의 vm_page_prot 속성에 대해 아키텍처에 커스트마이즈된 캐시 변환이 필요한 경우 변환된 vm_flags 값을 저장한다. 매칭되지 않으면 그냥 vm_flags 속성을 저장한다.
  • 코드 라인 7~11에서 vma가 write notify를 사용하고자 하는 경우 shared 플래그를 제거한다.
    • vma 영역이 shared 매핑이고 페이지들이 read only 설정이된 경우 write 이벤트를 트래킹하고자 할 때 true를 반환한다.

 

vm_pgprot_modify()

mm/mmap.c

static pgprot_t vm_pgprot_modify(pgprot_t oldprot, unsigned long vm_flags)
{
        return pgprot_modify(oldprot, vm_get_page_prot(vm_flags));
}

oldprot에 해당하는 아키텍처별로 미리 정의된 프로토콜 변환이 있으면 변환하여 속성을 반환한다.

  • arm에서는 요청하는 oldprot 속성이 noncache, writecombine, device가 있는 경우 각각 noncache, buffer, noncache 형태로 변환한다. 매칭되지 않는 속성은 newprot 속성을 변환없이 그대로 반환한다.

 

 

pgprot_modify()

include/asm-generic/pgtable.h

static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
{
        if (pgprot_val(oldprot) == pgprot_val(pgprot_noncached(oldprot)))
                newprot = pgprot_noncached(newprot);
        if (pgprot_val(oldprot) == pgprot_val(pgprot_writecombine(oldprot)))
                newprot = pgprot_writecombine(newprot);
        if (pgprot_val(oldprot) == pgprot_val(pgprot_device(oldprot)))
                newprot = pgprot_device(newprot);
        return newprot;
}

아키텍처에서 매핑 시 old 마스크사용하는 odl 캐시 속성을 new 캐시 속성에 맞게 변환한다.

  • arm: no cache 또는 buffer

 

arch/arm/include/asm/pgtable.h

#define __pgprot_modify(prot,mask,bits)         \
        __pgprot((pgprot_val(prot) & ~(mask)) | (bits))
        
#define pgprot_noncached(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
          
#define pgprot_writecombine(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)

#define pgprot_stronglyordered(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)

#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
#define pgprot_dmacoherent(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN)
#else     
#define pgprot_dmacoherent(prot) \
        __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN)
#endif

arm 아키텍처에서 매핑 시 사용하는 캐시 속성을 선택한다. (uncached 또는 buffrable)

  • noncache -> no cache 속성 사용
  • writecombine -> buffer 속성 사용
  • stronglyordered -> no cache 속성 사용
  • dmacoherennt -> CONFIG_ARM_DMA_MEM_BUFFERABLE 커널 옵션에 따라 buffer 또는 no cache 사용
    • armv6 또는 armv7에서 dma가 buffer 사용 가능하다.

 

vma_wants_writenotify()

mm/mmap.c

/*
 * Some shared mappigns will want the pages marked read-only
 * to track write events. If so, we'll downgrade vm_page_prot
 * to the private version (using protection_map[] without the
 * VM_SHARED bit).
 */
int vma_wants_writenotify(struct vm_area_struct *vma)
{
        vm_flags_t vm_flags = vma->vm_flags;

        /* If it was private or non-writable, the write bit is already clear */
        if ((vm_flags & (VM_WRITE|VM_SHARED)) != ((VM_WRITE|VM_SHARED)))
                return 0;

        /* The backer wishes to know when pages are first written to? */
        if (vma->vm_ops && vma->vm_ops->page_mkwrite)
                return 1;

        /* The open routine did something to the protections that pgprot_modify
         * won't preserve? */
        if (pgprot_val(vma->vm_page_prot) !=
            pgprot_val(vm_pgprot_modify(vma->vm_page_prot, vm_flags)))
                return 0;

        /* Do we need to track softdirty? */
        if (IS_ENABLED(CONFIG_MEM_SOFT_DIRTY) && !(vm_flags & VM_SOFTDIRTY))
                return 1;

        /* Specialty mapping? */
        if (vm_flags & VM_PFNMAP)
                return 0;

        /* Can the mapping track the dirty pages? */
        return vma->vm_file && vma->vm_file->f_mapping &&
                mapping_cap_account_dirty(vma->vm_file->f_mapping);
}

vma 영역이 shared 매핑이고 페이지들이 read only 설정이된 경우 write 이벤트를 트래킹하고자 할 때 true를 반환한다.

  • 코드 라인 12~13에서 write 및 shared 요청이 없는 vma의 경우 false(0)를 반환한다.
  • 코드 라인 16~17에서 vm_ops->mkwrite 콜백 함수가 지정된 경우 true(1)를 반환한다.
  • 코드 라인 21~23에서 vm_page_prot 속성에 변화를 줄 필요가 없는 경우 false(0)를 반환한다.
  • 코드 라인 26~27에서 CONFIG_MEM_SOFT_DIRTY 커널 옵션을 사용하면서 soft dirty 기능을 요청하지 않은 경우  true(1)를 반환한다.
  • 코드 라인 30~31에서 pfnmap 매핑을 요청한 경우 false(0)을 반환한다.
  • 코드 라인 34~35에서 file 매핑이면서 bdi 에 dirty capable 설정된 경우  true(1)를 반환한다. 그렇지 않으면 flase(0)을 반환한다.

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.