Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/cris/mm/tlb.c | |
3 | * | |
4 | * Copyright (C) 2000, 2001 Axis Communications AB | |
5 | * | |
6 | * Authors: Bjorn Wesen (bjornw@axis.com) | |
7 | * | |
8 | */ | |
9 | ||
10 | #include <linux/init.h> | |
8447157a | 11 | #include <linux/kernel.h> |
589ee628 IM |
12 | #include <linux/mm_types.h> |
13 | ||
1da177e4 LT |
14 | #include <asm/tlb.h> |
15 | ||
16 | #define D(x) | |
17 | ||
18 | /* The TLB can host up to 64 different mm contexts at the same time. | |
19 | * The running context is R_MMU_CONTEXT, and each TLB entry contains a | |
20 | * page_id that has to match to give a hit. In page_id_map, we keep track | |
49b4ff33 | 21 | * of which mm we have assigned to which page_id, so that we know when |
1da177e4 LT |
22 | * to invalidate TLB entries. |
23 | * | |
24 | * The last page_id is never running - it is used as an invalid page_id | |
25 | * so we can make TLB entries that will never match. | |
26 | * | |
27 | * Notice that we need to make the flushes atomic, otherwise an interrupt | |
28 | * handler that uses vmalloced memory might cause a TLB load in the middle | |
29 | * of a flush causing. | |
30 | */ | |
31 | ||
32 | struct mm_struct *page_id_map[NUM_PAGEID]; | |
33 | static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ | |
34 | ||
1da177e4 LT |
35 | /* the following functions are similar to those used in the PPC port */ |
36 | ||
37 | static inline void | |
38 | alloc_context(struct mm_struct *mm) | |
39 | { | |
40 | struct mm_struct *old_mm; | |
41 | ||
42 | D(printk("tlb: alloc context %d (%p)\n", map_replace_ptr, mm)); | |
43 | ||
44 | /* did we replace an mm ? */ | |
45 | ||
46 | old_mm = page_id_map[map_replace_ptr]; | |
47 | ||
48 | if(old_mm) { | |
49 | /* throw out any TLB entries belonging to the mm we replace | |
50 | * in the map | |
51 | */ | |
52 | flush_tlb_mm(old_mm); | |
53 | ||
4f18cfbf | 54 | old_mm->context.page_id = NO_CONTEXT; |
1da177e4 LT |
55 | } |
56 | ||
57 | /* insert it into the page_id_map */ | |
58 | ||
4f18cfbf | 59 | mm->context.page_id = map_replace_ptr; |
1da177e4 LT |
60 | page_id_map[map_replace_ptr] = mm; |
61 | ||
62 | map_replace_ptr++; | |
63 | ||
64 | if(map_replace_ptr == INVALID_PAGEID) | |
65 | map_replace_ptr = 0; /* wrap around */ | |
66 | } | |
67 | ||
68 | /* | |
69 | * if needed, get a new MMU context for the mm. otherwise nothing is done. | |
70 | */ | |
71 | ||
72 | void | |
73 | get_mmu_context(struct mm_struct *mm) | |
74 | { | |
4f18cfbf | 75 | if(mm->context.page_id == NO_CONTEXT) |
1da177e4 LT |
76 | alloc_context(mm); |
77 | } | |
78 | ||
79 | /* called by __exit_mm to destroy the used MMU context if any before | |
80 | * destroying the mm itself. this is only called when the last user of the mm | |
81 | * drops it. | |
82 | * | |
83 | * the only thing we really need to do here is mark the used PID slot | |
84 | * as empty. | |
85 | */ | |
86 | ||
87 | void | |
88 | destroy_context(struct mm_struct *mm) | |
89 | { | |
4f18cfbf MS |
90 | if(mm->context.page_id != NO_CONTEXT) { |
91 | D(printk("destroy_context %d (%p)\n", mm->context.page_id, mm)); | |
1da177e4 | 92 | flush_tlb_mm(mm); /* TODO this might be redundant ? */ |
4f18cfbf | 93 | page_id_map[mm->context.page_id] = NULL; |
1da177e4 LT |
94 | } |
95 | } | |
96 | ||
97 | /* called once during VM initialization, from init.c */ | |
98 | ||
99 | void __init | |
100 | tlb_init(void) | |
101 | { | |
102 | int i; | |
103 | ||
104 | /* clear the page_id map */ | |
105 | ||
8447157a | 106 | for (i = 1; i < ARRAY_SIZE(page_id_map); i++) |
1da177e4 LT |
107 | page_id_map[i] = NULL; |
108 | ||
109 | /* invalidate the entire TLB */ | |
110 | ||
111 | flush_tlb_all(); | |
112 | ||
113 | /* the init_mm has context 0 from the boot */ | |
114 | ||
115 | page_id_map[0] = &init_mm; | |
116 | } |