OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [ia64/] [mm/] [hugetlbpage.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * IA-64 Huge TLB Page Support for Kernel.
3
 *
4
 * Copyright (C) 2002, Rohit Seth <rohit.seth@intel.com>
5
 */
6
 
7
#include <linux/config.h>
8
#include <linux/init.h>
9
#include <linux/fs.h>
10
#include <linux/mm.h>
11
#include <linux/hugetlb.h>
12
#include <linux/pagemap.h>
13
#include <linux/smp_lock.h>
14
#include <linux/slab.h>
15
#include <linux/sysctl.h>
16
#include <asm/mman.h>
17
#include <asm/pgalloc.h>
18
#include <asm/tlb.h>
19
 
20
 
21
#define TASK_HPAGE_BASE (REGION_HPAGE << REGION_SHIFT)
22
 
23
static long    htlbpagemem;
24
int     htlbpage_max;
25
static long    htlbzone_pages;
26
 
27
struct vm_operations_struct hugetlb_vm_ops;
28
static LIST_HEAD(htlbpage_freelist);
29
static spinlock_t htlbpage_lock = SPIN_LOCK_UNLOCKED;
30
 
31
static struct page *alloc_hugetlb_page(void)
32
{
33
        int i;
34
        struct page *page;
35
 
36
        spin_lock(&htlbpage_lock);
37
        if (list_empty(&htlbpage_freelist)) {
38
                spin_unlock(&htlbpage_lock);
39
                return NULL;
40
        }
41
 
42
        page = list_entry(htlbpage_freelist.next, struct page, list);
43
        list_del(&page->list);
44
        htlbpagemem--;
45
        spin_unlock(&htlbpage_lock);
46
        set_page_count(page, 1);
47
        for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i)
48
                clear_highpage(&page[i]);
49
        return page;
50
}
51
 
52
static pte_t *
53
huge_pte_alloc (struct mm_struct *mm, unsigned long addr)
54
{
55
        unsigned long taddr = htlbpage_to_page(addr);
56
        pgd_t *pgd;
57
        pmd_t *pmd;
58
        pte_t *pte = NULL;
59
 
60
        pgd = pgd_offset(mm, taddr);
61
        pmd = pmd_alloc(mm, pgd, taddr);
62
        if (pmd)
63
                pte = pte_alloc(mm, pmd, taddr);
64
        return pte;
65
}
66
 
67
static pte_t *
68
huge_pte_offset (struct mm_struct *mm, unsigned long addr)
69
{
70
        unsigned long taddr = htlbpage_to_page(addr);
71
        pgd_t *pgd;
72
        pmd_t *pmd;
73
        pte_t *pte = NULL;
74
 
75
        pgd = pgd_offset(mm, taddr);
76
        pmd = pmd_offset(pgd, taddr);
77
        pte = pte_offset(pmd, taddr);
78
        return pte;
79
}
80
 
81
#define mk_pte_huge(entry) { pte_val(entry) |= _PAGE_P; }
82
 
83
static void
84
set_huge_pte (struct mm_struct *mm, struct vm_area_struct *vma,
85
              struct page *page, pte_t * page_table, int write_access)
86
{
87
        pte_t entry;
88
 
89
        mm->rss += (HPAGE_SIZE / PAGE_SIZE);
90
        if (write_access) {
91
                entry =
92
                    pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
93
        } else
94
                entry = pte_wrprotect(mk_pte(page, vma->vm_page_prot));
95
        entry = pte_mkyoung(entry);
96
        mk_pte_huge(entry);
97
        set_pte(page_table, entry);
98
        return;
99
}
100
/*
101
 * This function checks for proper alignment of input addr and len parameters.
102
 */
103
int is_aligned_hugepage_range(unsigned long addr, unsigned long len)
104
{
105
        if (len & ~HPAGE_MASK)
106
                return -EINVAL;
107
        if (addr & ~HPAGE_MASK)
108
                return -EINVAL;
109
        if (REGION_NUMBER(addr) != REGION_HPAGE)
110
                return -EINVAL;
111
 
112
        return 0;
113
}
114
/* This function checks if the address and address+len falls out of HugeTLB region.  It
115
 * return -EINVAL if any part of address range falls in HugeTLB region.
116
 */
117
int  is_invalid_hugepage_range(unsigned long addr, unsigned long len)
118
{
119
        if (REGION_NUMBER(addr) == REGION_HPAGE)
120
                return -EINVAL;
121
        if (REGION_NUMBER(addr+len) == REGION_HPAGE)
122
                return -EINVAL;
123
        return 0;
124
}
125
 
126
/*
127
 * Same as generic free_pgtables(), except constant PGDIR_* and pgd_offset
128
 * are hugetlb region specific.
129
 */
130
void hugetlb_free_pgtables(struct mm_struct * mm, struct vm_area_struct *prev,
131
        unsigned long start, unsigned long end)
132
{
133
        unsigned long first = start & HUGETLB_PGDIR_MASK;
134
        unsigned long last = end + HUGETLB_PGDIR_SIZE - 1;
135
        unsigned long start_index, end_index;
136
 
137
        if (!prev) {
138
                prev = mm->mmap;
139
                if (!prev)
140
                        goto no_mmaps;
141
                if (prev->vm_end > start) {
142
                        if (last > prev->vm_start)
143
                                last = prev->vm_start;
144
                        goto no_mmaps;
145
                }
146
        }
147
        for (;;) {
148
                struct vm_area_struct *next = prev->vm_next;
149
 
150
                if (next) {
151
                        if (next->vm_start < start) {
152
                                prev = next;
153
                                continue;
154
                        }
155
                        if (last > next->vm_start)
156
                                last = next->vm_start;
157
                }
158
                if (prev->vm_end > first)
159
                        first = prev->vm_end + HUGETLB_PGDIR_SIZE - 1;
160
                break;
161
        }
162
no_mmaps:
163
        if (last < first)
164
                return;
165
        /*
166
         * If the PGD bits are not consecutive in the virtual address, the
167
         * old method of shifting the VA >> by PGDIR_SHIFT doesn't work.
168
         */
169
        start_index = pgd_index(htlbpage_to_page(first));
170
        end_index = pgd_index(htlbpage_to_page(last));
171
        if (end_index > start_index) {
172
                clear_page_tables(mm, start_index, end_index - start_index);
173
                flush_tlb_pgtables(mm, first & HUGETLB_PGDIR_MASK,
174
                                   last & HUGETLB_PGDIR_MASK);
175
        }
176
}
177
 
178
int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
179
                        struct vm_area_struct *vma)
180
{
181
        pte_t *src_pte, *dst_pte, entry;
182
        struct page *ptepage;
183
        unsigned long addr = vma->vm_start;
184
        unsigned long end = vma->vm_end;
185
 
186
        while (addr < end) {
187
                dst_pte = huge_pte_alloc(dst, addr);
188
                if (!dst_pte)
189
                        goto nomem;
190
                src_pte = huge_pte_offset(src, addr);
191
                entry = *src_pte;
192
                ptepage = pte_page(entry);
193
                get_page(ptepage);
194
                set_pte(dst_pte, entry);
195
                dst->rss += (HPAGE_SIZE / PAGE_SIZE);
196
                addr += HPAGE_SIZE;
197
        }
198
        return 0;
199
nomem:
200
        return -ENOMEM;
201
}
202
 
203
int
204
follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
205
                    struct page **pages, struct vm_area_struct **vmas,
206
                    unsigned long *st, int *length, int i)
207
{
208
        pte_t *ptep, pte;
209
        unsigned long start = *st;
210
        unsigned long pstart;
211
        int len = *length;
212
        struct page *page;
213
 
214
        do {
215
                pstart = start;
216
                ptep = huge_pte_offset(mm, start);
217
                pte = *ptep;
218
 
219
back1:
220
                page = pte_page(pte);
221
                if (pages) {
222
                        page += ((start & ~HPAGE_MASK) >> PAGE_SHIFT);
223
                        pages[i] = page;
224
                }
225
                if (vmas)
226
                        vmas[i] = vma;
227
                i++;
228
                len--;
229
                start += PAGE_SIZE;
230
                if (((start & HPAGE_MASK) == pstart) && len &&
231
                                (start < vma->vm_end))
232
                        goto back1;
233
        } while (len && start < vma->vm_end);
234
        *length = len;
235
        *st = start;
236
        return i;
237
}
238
 
239
void free_huge_page(struct page *page)
240
{
241
        BUG_ON(page_count(page));
242
        BUG_ON(page->mapping);
243
 
244
        INIT_LIST_HEAD(&page->list);
245
 
246
        spin_lock(&htlbpage_lock);
247
        list_add(&page->list, &htlbpage_freelist);
248
        htlbpagemem++;
249
        spin_unlock(&htlbpage_lock);
250
}
251
 
252
void huge_page_release(struct page *page)
253
{
254
        if (!put_page_testzero(page))
255
                return;
256
 
257
        free_huge_page(page);
258
}
259
 
260
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
261
{
262
        struct mm_struct *mm = vma->vm_mm;
263
        unsigned long address;
264
        pte_t *pte;
265
        struct page *page;
266
 
267
        BUG_ON(start & (HPAGE_SIZE - 1));
268
        BUG_ON(end & (HPAGE_SIZE - 1));
269
 
270
        for (address = start; address < end; address += HPAGE_SIZE) {
271
                pte = huge_pte_offset(mm, address);
272
                if (pte_none(*pte))
273
                        continue;
274
                page = pte_page(*pte);
275
                huge_page_release(page);
276
                pte_clear(pte);
277
        }
278
        mm->rss -= (end - start) >> PAGE_SHIFT;
279
        flush_tlb_range(mm, start, end);
280
}
281
 
282
void zap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long length)
283
{
284
        struct mm_struct *mm = vma->vm_mm;
285
        spin_lock(&mm->page_table_lock);
286
        unmap_hugepage_range(vma, start, start + length);
287
        spin_unlock(&mm->page_table_lock);
288
}
289
 
290
int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma)
291
{
292
        struct mm_struct *mm = current->mm;
293
        struct inode *inode = mapping->host;
294
        unsigned long addr;
295
        int ret = 0;
296
 
297
        BUG_ON(vma->vm_start & ~HPAGE_MASK);
298
        BUG_ON(vma->vm_end & ~HPAGE_MASK);
299
 
300
        spin_lock(&mm->page_table_lock);
301
        for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) {
302
                unsigned long idx;
303
                pte_t *pte = huge_pte_alloc(mm, addr);
304
                struct page *page;
305
 
306
                if (!pte) {
307
                        ret = -ENOMEM;
308
                        goto out;
309
                }
310
                if (!pte_none(*pte))
311
                        continue;
312
 
313
                idx = ((addr - vma->vm_start) >> HPAGE_SHIFT)
314
                        + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT));
315
                page = find_get_page(mapping, idx);
316
                if (!page) {
317
                        /* charge the fs quota first */
318
                        if (hugetlb_get_quota(mapping)) {
319
                                ret = -ENOMEM;
320
                                goto out;
321
                        }
322
                        page = alloc_hugetlb_page();
323
                        if (!page) {
324
                                hugetlb_put_quota(mapping);
325
                                ret = -ENOMEM;
326
                                goto out;
327
                        }
328
                        add_to_page_cache(page, mapping, idx);
329
                        unlock_page(page);
330
                }
331
                set_huge_pte(mm, vma, page, pte, vma->vm_flags & VM_WRITE);
332
        }
333
out:
334
        spin_unlock(&mm->page_table_lock);
335
        return ret;
336
}
337
 
338
unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
339
                unsigned long pgoff, unsigned long flags)
340
{
341
        struct vm_area_struct *vmm;
342
 
343
        if (len > RGN_MAP_LIMIT)
344
                return -ENOMEM;
345
        if (len & ~HPAGE_MASK)
346
                return -EINVAL;
347
        /* This code assumes that REGION_HPAGE != 0. */
348
        if ((REGION_NUMBER(addr) != REGION_HPAGE) || (addr & (HPAGE_SIZE - 1)))
349
                addr = TASK_HPAGE_BASE;
350
        else
351
                addr = COLOR_HALIGN(addr);
352
        for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
353
                /* At this point:  (!vmm || addr < vmm->vm_end). */
354
                if (REGION_OFFSET(addr) + len > RGN_MAP_LIMIT)
355
                        return -ENOMEM;
356
                if (!vmm || (addr + len) <= vmm->vm_start)
357
                        return addr;
358
                addr = COLOR_HALIGN(vmm->vm_end);
359
        }
360
}
361
void update_and_free_page(struct page *page)
362
{
363
        int j;
364
        struct page *map;
365
 
366
        map = page;
367
        htlbzone_pages--;
368
        for (j = 0; j < (HPAGE_SIZE / PAGE_SIZE); j++) {
369
                map->flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced |
370
                                1 << PG_dirty | 1 << PG_active | 1 << PG_reserved);
371
                set_page_count(map, 0);
372
                map++;
373
        }
374
        set_page_count(page, 1);
375
        __free_pages(page, HUGETLB_PAGE_ORDER);
376
}
377
 
378
int try_to_free_low(int count)
379
{
380
        struct list_head *p;
381
        struct page *page, *map;
382
 
383
        map = NULL;
384
        spin_lock(&htlbpage_lock);
385
        list_for_each(p, &htlbpage_freelist) {
386
                if (map) {
387
                        list_del(&map->list);
388
                        update_and_free_page(map);
389
                        htlbpagemem--;
390
                        map = NULL;
391
                        if (++count == 0)
392
                                break;
393
                }
394
                page = list_entry(p, struct page, list);
395
                if ((page_zone(page))->name[0] != 'H') //Look for non-Highmem zones.
396
                        map = page;
397
        }
398
        if (map) {
399
                list_del(&map->list);
400
                update_and_free_page(map);
401
                htlbpagemem--;
402
                count++;
403
        }
404
        spin_unlock(&htlbpage_lock);
405
        return count;
406
}
407
 
408
int set_hugetlb_mem_size(int count)
409
{
410
        int j, lcount;
411
        struct page *page, *map;
412
 
413
        if (count < 0)
414
                lcount = count;
415
        else
416
                lcount = count - htlbzone_pages;
417
 
418
        if (lcount == 0)
419
                return (int)htlbzone_pages;
420
        if (lcount > 0) {        /* Increase the mem size. */
421
                while (lcount--) {
422
                        page = alloc_pages(__GFP_HIGHMEM, HUGETLB_PAGE_ORDER);
423
                        if (page == NULL)
424
                                break;
425
                        map = page;
426
                        for (j = 0; j < (HPAGE_SIZE / PAGE_SIZE); j++) {
427
                                SetPageReserved(map);
428
                                map++;
429
                        }
430
                        spin_lock(&htlbpage_lock);
431
                        list_add(&page->list, &htlbpage_freelist);
432
                        htlbpagemem++;
433
                        htlbzone_pages++;
434
                        spin_unlock(&htlbpage_lock);
435
                }
436
                return (int) htlbzone_pages;
437
        }
438
        /* Shrink the memory size. */
439
        lcount = try_to_free_low(lcount);
440
        while (lcount++ < 0) {
441
                page = alloc_hugetlb_page();
442
                if (page == NULL)
443
                        break;
444
                spin_lock(&htlbpage_lock);
445
                update_and_free_page(page);
446
                spin_unlock(&htlbpage_lock);
447
        }
448
        return (int) htlbzone_pages;
449
}
450
 
451
int hugetlb_sysctl_handler(ctl_table *table, int write, struct file *file, void *buffer, size_t *length)
452
{
453
        proc_dointvec(table, write, file, buffer, length);
454
        htlbpage_max = set_hugetlb_mem_size(htlbpage_max);
455
        return 0;
456
}
457
 
458
static int __init hugetlb_setup(char *s)
459
{
460
        if (sscanf(s, "%d", &htlbpage_max) <= 0)
461
                htlbpage_max = 0;
462
        return 1;
463
}
464
__setup("hugepages=", hugetlb_setup);
465
 
466
static int __init hugetlb_init(void)
467
{
468
        int i, j;
469
        struct page *page;
470
 
471
        for (i = 0; i < htlbpage_max; ++i) {
472
                page = alloc_pages(__GFP_HIGHMEM, HUGETLB_PAGE_ORDER);
473
                if (!page)
474
                        break;
475
                for (j = 0; j < HPAGE_SIZE/PAGE_SIZE; ++j)
476
                        SetPageReserved(&page[j]);
477
                spin_lock(&htlbpage_lock);
478
                list_add(&page->list, &htlbpage_freelist);
479
                spin_unlock(&htlbpage_lock);
480
        }
481
        htlbpage_max = htlbpagemem = htlbzone_pages = i;
482
        printk("Total HugeTLB memory allocated, %ld\n", htlbpagemem);
483
        return 0;
484
}
485
module_init(hugetlb_init);
486
 
487
int hugetlb_report_meminfo(char *buf)
488
{
489
        return sprintf(buf,
490
                        "HugePages_Total: %5lu\n"
491
                        "HugePages_Free:  %5lu\n"
492
                        "Hugepagesize:    %5lu kB\n",
493
                        htlbzone_pages,
494
                        htlbpagemem,
495
                        HPAGE_SIZE/1024);
496
}
497
 
498
int is_hugepage_mem_enough(size_t size)
499
{
500
        if (size > (htlbpagemem << HPAGE_SHIFT))
501
                return 0;
502
        return 1;
503
}
504
 
505
static struct page *hugetlb_nopage(struct vm_area_struct * area, unsigned long address, int unused)
506
{
507
        BUG();
508
        return NULL;
509
}
510
 
511
struct vm_operations_struct hugetlb_vm_ops = {
512
        .nopage =       hugetlb_nopage,
513
};

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.