'Linux/Memory'에 해당되는 글 2건

  1. 2007/10/08 사용자 메모리 공간
  2. 2007/10/08 비연속 메모리 영역 관리
커널 메모리에 대한 관리와 사용자 프로세스에 대한 메모리 관리는 극명한 차이를 보인다.
커널 메모리의 경우에는 다음과 같은 특징으로 즉각적인 메모리 처리를 해준다.

- 커널은 운영체에의 가장 높은 우선순위 요소이다. 즉, 즉각적인 메모리 할당이 필요하다.
- 커널은 스스롤 믿는다. 커널 내부의 코드에 대해서 메모리 관리 부분에서 오류 처리를 하지 않음

반면에 사용자 모드 프로세스들을 위한 메모리 할당은 전혀 다른 상황이다.

- 동적 메모리 할당이 급하지 않다고 생각함. 메모리 할당을 최대한 미룬다
- 사용자 프로그램을 믿지 않기 때문에, 사용자 모드에서의 주소 접근에 대한 에러 처리를 항상 준비함

프로세스에 대한 주소 공간

사용자 삽입 이미지


각 프로세스에 대해서 메모리는 vm_area_struct라는 구조체 형태로 해서 할당된 메모리 블럭들을 관리한다. 각 메모리 블럭이 가리키는 가상 주소는 PAGE_SIZE(4096)의 배수 위치에서 시작해야 하며, 크기도 PAGE_SIZE의 배수이어야 한다.

새로운 메모리 공간 즉, vm_area_struct가 확보되는 경우들로는 다음과 같은 경우들이 있다.

- 콘솔에서 명령을 입력해서 새로운 프로세스를 만드는 경우
- 현재 수행중인 프로그램이 exec를 통해서 새로운 프로그램을 적재할 경우
- 파일에 대한 메모리 매핑을 수행할 경우
- 현재 할당되어 있는 스택양을 넘어서는 스택 사용이 있을 경우(재귀호출, 지역변수 선언...)
- 프로세스간 공유 메모리를 만들었을 경우
- malloc() 과 같은 함수를 사용해서 동적 영역(힙)을 증가시켰을 경우

위와 관련된 유저 모드에서의 메모리 관련 함수들은 다음과 같다.

brk(), execve(), _exit(), fork(), mmap(), mmap2(), mremap(), remap_file_pages(), munmap(), shmat(), shmdt()

위의 함수들 중에서 실질적인 메모리 할당과 연관된 함수들로는 brk()와 mmap() 함수 등이다. 하지만, 이 함수들도 물리적 메모리를 확보하지는 않으며, 단지 vm_area_struct 구조체를 새로 구성하는 일만 수행한다.

vm_area_struct는 나중에 실제 메모리를 할당하는 부분인 Page Fault Handler 부분에서 사용된다. 이것을 이용해 페이지 폴트 핸들러는 다음 두가지 상황을 파악하게 된다.

- 프로그래밍 오류에 의해서 발생한 경우
- 페이지가 확보되지 않아서 발생한 경우 (이 경우 정상적인 메모리 접근이므로 해당 메모리를 확보해준다)

메모리 디스크립터(mm_struct)

프로세스의 메모리에 대한 모든 정보를 관리하는 구조체로서 task_struct 구조체내에서 mm 필드가 보관하고 있다. mm_struct 구조체의 내용은 다음과 같다.

310 struct mm_struct {
311         struct vm_area_struct * mmap;           /* vm_area_struct 구조체 리스트의 첫 항목 */
312         struct rb_root mm_rb;                        /* red-black tree의 루트 노드 */
313         struct vm_area_struct * mmap_cache; /* 마지막으로 호출된 find_vma() 함수가 리턴한 값*/
314         unsigned long (*get_unmapped_area) (struct file *filp,
315                                 unsigned long addr, unsigned long len,
316                                 unsigned long pgoff, unsigned long flags);
317         void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
318         unsigned long mmap_base;                /* base of mmap area */
319         unsigned long task_size;                /* size of task vm space */
320         unsigned long cached_hole_size;         /* if non-zero, the largest hole below free_area_cache */
321         unsigned long free_area_cache;          /* first hole of size cached_hole_size or larger */
322         pgd_t * pgd;
323         atomic_t mm_users;                      /* How many users with user space? */
324         atomic_t mm_count;                      /* How many references to "struct mm_struct" (users count as 1) */
325         int map_count;                          /* number of VMAs */
326         struct rw_semaphore mmap_sem;
327         spinlock_t page_table_lock;             /* Protects page tables and some counters */
328
329         struct list_head mmlist;                /* List of maybe swapped mm's.  These are globally strung
330                                                  * together off init_mm.mmlist, and are protected
331                                                  * by mmlist_lock
332                                                  */
333
334         /* Special counters, in some configurations protected by the
335          * page_table_lock, in other configurations by being atomic.
336          */
337         mm_counter_t _file_rss;
338         mm_counter_t _anon_rss;
339
340         unsigned long hiwater_rss;      /* High-watermark of RSS usage */
341         unsigned long hiwater_vm;       /* High-water virtual memory usage */
342
343         unsigned long total_vm, locked_vm, shared_vm, exec_vm;
344         unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
345         unsigned long start_code, end_code, start_data, end_data;
346         unsigned long start_brk, brk, start_stack;
347         unsigned long arg_start, arg_end, env_start, env_end;
348
349         unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
350
351         cpumask_t cpu_vm_mask;
352
353         /* Architecture-specific MM context */
354         mm_context_t context;
355
356         /* Swap token stuff */
357         /*
358          * Last value of global fault stamp as seen by this process.
359          * In other words, this value gives an indication of how long
360          * it has been since this task got the token.
361          * Look at mm/thrash.c
362          */
363         unsigned int faultstamp;
364         unsigned int token_priority;
365         unsigned int last_interval;
366
367         unsigned char dumpable:2;
368
369         /* coredumping support */
370         int core_waiters;
371         struct completion *core_startup_done, core_done;
372
373         /* aio bits */
374         rwlock_t                ioctx_list_lock;
375         struct kioctx           *ioctx_list;
376 };

시스템에 존재하는 모든 mm_struct 들은 이중 링크드 리스트로 연결되어 있으며, 이에 대한 정보는 mm_struct 안의 mmlist 필드에 의해서 관리된다. 전체 리스트를 살펴보고자 한다면 init_mm 변수의 mmlist 필드를 사용하면 된다.

17 struct mm_struct init_mm = INIT_MM(init_mm);

47 #define INIT_MM(name) \
48 {                                                               \
49         .mm_rb          = RB_ROOT,                              \
50         .pgd            = swapper_pg_dir,                       \
51         .mm_users       = ATOMIC_INIT(2),                       \
52         .mm_count       = ATOMIC_INIT(1),                       \
53         .mmap_sem       = __RWSEM_INITIALIZER(name.mmap_sem),   \
54         .page_table_lock =  __SPIN_LOCK_UNLOCKED(name.page_table_lock), \
55         .mmlist         = LIST_HEAD_INIT(name.mmlist),          \
56         .cpu_vm_mask    = CPU_MASK_ALL,                         \
57 }

init_mm 변수는 0번 프로세스에 대한 메모리 정보를 관리하는 것으로서 처음 부팅시에 사용된다.

mm_struct 구조체를 확보하기 위해서는 mm_alloc() 함수가 사용된다. 이것은 실제로는 kmem_cache_alloc()를 호출하는 다음과 같은 코드로 구성되어 있다.

355 struct mm_struct * mm_alloc(void)
356 {
357         struct mm_struct * mm;
358
359         mm = allocate_mm();
360         if (mm) {
361                 memset(mm, 0, sizeof(*mm));
362                 mm = mm_init(mm);
363         }
364         return mm;
365 }

323 #define allocate_mm()   (kmem_cache_alloc(mm_cachep, GFP_KERNEL))

mm_struct 해제는 다음 함수를 사용한다.

383 void mmput(struct mm_struct *mm)
384 {
385         might_sleep();
386
387         if (atomic_dec_and_test(&mm->mm_users)) {
388                 exit_aio(mm);
389                 exit_mmap(mm);
390                 if (!list_empty(&mm->mmlist)) {
391                         spin_lock(&mmlist_lock);
392                         list_del(&mm->mmlist);
393                         spin_unlock(&mmlist_lock);
394                 }
395                 put_swap_token(mm);
396                 mmdrop(mm);
397         }
398 }

1381 static inline void mmdrop(struct mm_struct * mm)
1382 {
1383         if (atomic_dec_and_test(&mm->mm_count))
1384                 __mmdrop(mm);
1385 }
1386

372 void fastcall __mmdrop(struct mm_struct *mm)
373 {
374         BUG_ON(mm == &init_mm);
375         mm_free_pgd(mm);
376         destroy_context(mm);
377         free_mm(mm);
378 }

324 #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm)))

311 static inline void mm_free_pgd(struct mm_struct * mm)
312 {
313         pgd_free(mm->pgd);
314 }

mm_users 필드의 값을 하나 감소시키며, 이 값이 0인 경우에만 실제로 해당 구조체를 제거한다. 물론, 여기에서도 mm_count 필드를 다시 감소시키며, 이게 0이 되어야 실질적인 해제가 이루어진다.

커널 쓰레드에 대한 메모리 디스크립터

커널 쓰레드는 커널 모드에서 수행되기 때문에 TASK_SIZE 아래쪽 메모리를 접근할 일은 없다. 따라서, vm_area_struct 형태의 정보도 관리할 필요가 없다. 하지만, 불필요한 TLB와 캐쉬 플러쉬를 방지하기 위해서 커널 쓰레드인 경우에는 이전에 수행하던 유저모드 프로세스의 메모리 정보를 그대로 유지하게 된다.

유저 모드 프로세스에서는 mm과 active_mm이 모두 자신이 관리하는 mm_struct를 가리키고 있다. 하지만, 커널 쓰레드의 mm 필드는 NULL 이며, active_mm 필드가 이전 프로세스의 active_mm 값을 가지고 있게 된다. 이렇게 되면, 결국 이전에 마지막으로 수행된 유저 프로세스의 mm값을 가리키게 된다.
크리에이티브 커먼즈 라이선스
Creative Commons License
이올린에 북마크하기(0) 이올린에 추천하기(0)

'Linux > Memory' 카테고리의 다른 글

사용자 메모리 공간  (0) 2007/10/08
비연속 메모리 영역 관리  (0) 2007/10/08
Posted by Daniel Kwon
커널에서 사용하는 메모리는 크게 연속적인 메모리 공간과 비연속적인 메모리 할당으로 나누어진다. 연속메모리는 kmalloc()이나 __get_free_pages() 계열의 함수를 통해서 획득할 수 있으며, virt_to_page()나 __pa() 매크로를 통해 쉽게 물리적인 메모리에 대한 정보를 얻을 수 있는 형태이다. 물리적 메모리의 주소를 얻을 수 있는 __pa() 매크로를 보면 다음과 같이 구성되어 있다.

125 #define __pa(x)                 ((unsigned long)(x)-PAGE_OFFSET)

여기서 PAGE_OFFSET은 x86의 경우 3G로서 커널의 시작 주소를 나타낸다. 즉, 커널의 시작 주소 부분만이 더해져서 가상 주소가 만들어진 것이 연속적인 메모리의 특징이다.

반면, 비연속적인 메모리는 메모리 요청에 대해서 여기저기 산재해 있는 page들을 묶어서 하나의 가상 주소로 보여주는 형태이기 때문에 가상 주소로부터 물리적인 주소를 바로 알아낼 수 없다.(물론, page table 항목을 조사하면 알수는 있다.)

비연속적인 메모리를 사용하면 외부 단편화에 의해서 사용할 수 없었던 메모리를 사용할 수 있게 된다는 장점이 있다. 즉, 물리적으로 흩어져 있어서 __get_free_pages() 계열의 함수로는 얻을 수 없었던 메모리이지만, 비연속적인 메모리 할당 방식을 사용하면 얻을 수 있다는 것이다. 또한가지 장점은 HighMemory를 접근하기 위한 방법으로도 사용할 수 있다는 점이다.

단점으로는, DMA 연산등에 사용할 수 없다는 점과 page table에 대한 잦은 변화에 따른 속도 저하가 발생한다는 점이다. DMA연산에서는 CPU를 거치지 않기 때문에 물리적인 주소를 제공해야 하는데, page들이 여기저기 흩어져 있기 때문에 주소 변환 작업을 하지 않는 DMA에서는 처리가 불가능하다. 또한, 메모리를 할당하고 해제할 때마다 여기 저기 흩어져 있는 페이지들을 모아서 사용하기 때문에 페이지 테이블에 대한 업데이트가 자주 발생하며, TLB의 캐쉬 정보를 사용하는 효율성이 떨어질 수 도 있다.

 
사용자 삽입 이미지

PAGE_OFFSET :  커널 가상 메모리의 시작 주소

high_memory : 가상 메모리중 연속적 메모리 공간으로 사용할 수 있는 부분으로 PAGE_OFFSET + 896MB임

VMALLOC_START : vmalloc() 함수에서 사용하는 가상 메모리 주소 공간의 시작 주소로서 high_memory + VMALLOC_OFFSET 위치임

82 #define VMALLOC_OFFSET  (8*1024*1024)
83 #define VMALLOC_START   (((unsigned long) high_memory + vmalloc_earlyreserve + \
84                         2*VMALLOC_OFFSET-1) & ~(VMALLOC_OFFSET-1))
85 #ifdef CONFIG_HIGHMEM
86 # define VMALLOC_END    (PKMAP_BASE-2*PAGE_SIZE)
87 #else
88 # define VMALLOC_END    (FIXADDR_START-2*PAGE_SIZE)
89 #endif

PKMAP_BASE : High Memory를 매핑해서 사용하기 위한 용도의 공간

vm_struct : 비연속 메모리 공간에 대한 Descriptor

25 struct vm_struct {
26         /* keep next,addr,size together to speedup lookups */
27         struct vm_struct        *ne
xt
;
28         void                    *addr;
29         unsigned long           size;
30         unsigned long           flags;
31         struct page             **pages;
32         unsigned int            nr_pages;
33         unsigned long           phys_addr;
34 };

addr : 할당된 가상 주소에 대한 정보를 가지고 있음
size : 할당된 메모리의 크기
pages : 실제로 할당된 페이지들에 대한 디스크립터인 page 구조체들의 배열(page들은 물리적으로 연속적이지 않음)
nr_pages : 할당된 페이지의 갯수
phys_addr : 하드웨어 장치에 대한 I/O 공유 메모리를 매핑하기 위한 경우가 아니면 0

비연속 메모리 할당 : vmalloc()

34 static inline void * vmalloc (unsigned long size)
35 {
36         return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
37 }

235 void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
236 {
237         void * addr;
238         struct vm_struct *area;
239
240         size = PAGE_ALIGN(size);
241         if (!size || (size >> PAGE_SHIFT) > num_physpages) {
242 BUG();
243                 return NULL;
244         }
245         area = get_vm_area(size, VM_ALLOC);
246         if (!area)
247                 return NULL;
248         addr = area->addr;
249         if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) {
250                 vfree(addr);
251                 return NULL;
252         }
253         return addr;
254 }
255

vmalloc() 함수 코드 부분은 불필요한 코드 부분을 생략하기 위해서 2.4.20 버전의 코드를 가지고 설명하겠다.
비연속 메모리 공간을 확보하는 것은 크게 두 단계로 이루어진다.

첫번째는 비연속 메모리로 사용할 수 있는 가상 메모리 공간이 존재하는지 확인(get_vm_area 함수)
다음으로는 할당된 공간에 대해서 실제 물리 메모리 할당 및 페이지 테이블 업데이트

171 struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
172 {
173         unsigned long addr;
174         struct vm_struct **p, *tmp, *area;
175
176 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
177         if (!area)
178                 return NULL;
179
180         size += PAGE_SIZE;
181         if (!size) {
182                 kfree (area);
183                 return NULL;
184         }
185
186         addr = VMALLOC_START;
187         write_lock(&vmlist_lock);
188         for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
189                 if ((size + addr) < addr)
190                         goto out;
191                 if (size + addr <= (unsigned long) tmp->addr)
192                         break;
193                 addr = tmp->size + (unsigned long) tmp->addr;
194                 if (addr > VMALLOC_END-size)
195                         goto out;
196         }
197         area->flags = flags;
198         area->addr = (void *)addr;
199         area->size = size;
200         area->next = *p;
201         *p = area;
202         write_unlock(&vmlist_lock);
203         return area;
204
205 out:
206         write_unlock(&vmlist_lock);
207         kfree(area);
208         return NULL;
209 }

get_vm_area() 함수에서는 VMALLOC_START 부터 시작해서 VMALLOC_END 까지의 사이 중에서 사용 가능한 가상 메모리가 남아 있는지 확인한다. 현재 할당된 메모리들은 vmlist 로부터 단일 링크드 리스트 형태로 연결되어 있으므로 그것을 따라 가면서 확인한다.

vmalloc()에서 사용하는 구조체인 vm_struct 를 만들어야 하는데, 이때 kmalloc() 함수를 이용한다(176번째 줄). 이때, 두번째 인자로 GFP_KERNEL을 사용하는데, 이 때문에 vmalloc() 함수는 메모리 할당 중 블럭킹이 되면 안되는 인터럽트 컨텍스트등에서는 사용하면 안된다.

140 inline int vmalloc_area_pages (unsigned long address, unsigned long size,
141                                int gfp_mask, pgprot_t prot)
142 {
143         pgd_t * dir;
144         unsigned long end = address + size;
145         int ret;
146
147         dir = pgd_offset_k(address);
148         spin_lock(&init_mm.page_table_lock);
149         do {
150                 pmd_t *pmd;
151                
152                 pmd = pmd_alloc(&init_mm, dir, address);
153                 ret = -ENOMEM;
154                 if (!pmd)
155                         break;
156
157                 ret = -ENOMEM;
158                 if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot))
159                         break;
160
161                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
162                 dir++;
163
164                 ret = 0;
165         } while (address && (address < end));
166         spin_unlock(&init_mm.page_table_lock);
167         flush_cache_all();
168         return ret;
169 }

get_vm_area()에서 리턴된 정보는 단지 사용 가능한 메모리 영역에 대한 부분이고, 실제로 이를 기반으로 페이지를 확보해야 한다. get_vm_area()에서 정상적으로 메모리 주소를 반환했다 하더라도 물리적인 메모리가 존재하지 않는다면 실제 메모리 확보는 이루어지지 않게 된다. vmalloc_area_pages()는 실제 물리적인 메모리를 확보하는 함수이다.

먼저 커널과 연관된 Page Global Directory 테이블중 address에 대응하는 위치를 얻어온다. 다음에는 해당 address에 대응한 pmd를 얻어오며, 없을 경우 새로 확보한다 (2.6의 경우에는 pmd에 앞서 pud에 대한 부분을 같은 방식으로 확보한다).

120 static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot)
121 {
122 unsigned long end;
123
124         address &= ~PGDIR_MASK;
125         end = address + size;
126         if (end > PGDIR_SIZE)
127                 end = PGDIR_SIZE;
128         do {
129                 pte_t * pte = pte_alloc(&init_mm, pmd, address);
130                 if (!pte)
131                         return -ENOMEM;
132                 if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
133                         return -ENOMEM;
134                 address = (address + PMD_SIZE) & PMD_MASK;
135                 pmd++;
136         } while (address < end);
137         return 0;
138 }

95 static inline int alloc_area_pte (pte_t * pte, unsigned long address,
96                         unsigned long size, int gfp_mask, pgprot_t prot)
97 {
98         unsigned long end;
99
100         address &= ~PMD_MASK;
101         end = address + size;
102         if (end > PMD_SIZE)
103                 end = PMD_SIZE;
104         do {
105                 struct page * page;
106                 spin_unlock(&init_mm.page_table_lock);
107                 page = alloc_page(gfp_mask);
108                 spin_lock(&init_mm.page_table_lock);
109                 if (!pte_none(*pte))
110                         printk(KERN_ERR "alloc_area_pte: page already exists\n");
111                 if (!page)
112                         return -ENOMEM;
113                 set_pte(pte, mk_pte(page, prot));
114                 address += PAGE_SIZE;
115                 pte++;
116         } while (address < end);
117         return 0;
118 }


다음으로 alloc_area_pmd()에서는 Page TAble Entry에 대한 정보를 검색하며 없으면 새로 할당한다 (pte_alloc 함수). 마지막으로 alloc_area_pte()에서는 실제 사용할 메모리에 대한 부분을 확보한다.  이를 위해서 107번째 줄과 같이 alloc_page() 함수를 호출해서 한개의 페이지를 확보하고, 이에 대한 정보를 pte 부분에 업데이트 시킨다(113번째 줄).

이것을 전체 메모리에 대해서 반복하기 때문에, 실제로 확보되는 물리적인 메모리는 페이지 단위로 떨어져 있을 수 있다.

비연속 메모리 공간의 반납 : vfree()

vfree() 함수는 vmalloc()이나 vmalloc32(), vunmap() 함수등에 의해서 확보된 메모리를 반납할 때 사용된다.

211 void vfree(void * addr)
212 {
213         struct vm_struct **p, *tmp;
214
215         if (!addr)
216                 return;
217         if ((PAGE_SIZE-1) & (unsigned long) addr) {
218                 printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
219                 return;
220         }
221         write_lock(&vmlist_lock);
222         for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
223                 if (tmp->addr == addr) {
224                         *p = tmp->next;
225                         vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
226                         write_unlock(&vmlist_lock);
227                         kfree(tmp);
228                         return;
229                 }
230         }
231         write_unlock(&vmlist_lock);
232         printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr);
233 }

vmalloc()에 비해서 훨씬 간단한 구조를 가지고 있다. 주어진 addr에 대해서 기본적인 검사를 수행한 후 vmlist에서 시작되는 전체 비연속 메모리 공간에 대한 구조체들을 조사해서 addr로 시작하는 것을 찾는다.

발견된 vm_struct에 대해서 vmfree_area_pages()를 호출해서 할당되었던 물리 메모리를 해제하고 나서 vm_struct 자체에 대한 공간(kmalloc()으로 할당되었음)도 반납한다.

80 void vmfree_area_pages(unsigned long address, unsigned long size)
81 {
82         pgd_t * dir;
83         unsigned long end = address + size;
84
85         dir = pgd_offset_k(address);
86         flush_cache_all();
87         do {
88                 free_area_pmd(dir, address, end - address);
89                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
90                 dir++;
91         } while (address && (address < end));
92         flush_tlb_all();
93 }

56 static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
57 {
58         pmd_t * pmd;
59         unsigned long end;
60
61         if (pgd_none(*dir))
62                 return;
63         if (pgd_bad(*dir)) {
64                 pgd_ERROR(*dir);
65                 pgd_clear(dir);
66                 return;
67         }
68         pmd = pmd_offset(dir, address);
69         address &= ~PGDIR_MASK;
70         end = address + size;
71         if (end > PGDIR_SIZE)
72                 end = PGDIR_SIZE;
73         do {
74                 free_area_pte(pmd, address, end - address);
75                 address = (address + PMD_SIZE) & PMD_MASK;
76                 pmd++;
77         } while (address < end);
78 }

22 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
23 {
24         pte_t * pte;
25 unsigned long end;
26
27         if (pmd_none(*pmd))
28                 return;
29         if (pmd_bad(*pmd)) {
30                 pmd_ERROR(*pmd);
31                 pmd_clear(pmd);
32                 return;
33         }
34         pte = pte_offset(pmd, address);
35         address &= ~PMD_MASK;
36         end = address + size;
37         if (end > PMD_SIZE)
38                 end = PMD_SIZE;
39         do {
40                 pte_t page;
41                 page = ptep_get_and_clear(pte);
42                 address +=