diff options
-rw-r--r-- | arch/ia64/kernel/uncached.c | 86 |
1 files changed, 57 insertions, 29 deletions
diff --git a/arch/ia64/kernel/uncached.c b/arch/ia64/kernel/uncached.c index 5f03b9e524dd..4c73a6763669 100644 --- a/arch/ia64/kernel/uncached.c +++ b/arch/ia64/kernel/uncached.c | |||
@@ -32,32 +32,38 @@ | |||
32 | 32 | ||
33 | extern void __init efi_memmap_walk_uc(efi_freemem_callback_t, void *); | 33 | extern void __init efi_memmap_walk_uc(efi_freemem_callback_t, void *); |
34 | 34 | ||
35 | #define MAX_UNCACHED_GRANULES 5 | 35 | struct uncached_pool { |
36 | static int allocated_granules; | 36 | struct gen_pool *pool; |
37 | struct mutex add_chunk_mutex; /* serialize adding a converted chunk */ | ||
38 | int nchunks_added; /* #of converted chunks added to pool */ | ||
39 | atomic_t status; /* smp called function's return status*/ | ||
40 | }; | ||
41 | |||
42 | #define MAX_CONVERTED_CHUNKS_PER_NODE 2 | ||
37 | 43 | ||
38 | struct gen_pool *uncached_pool[MAX_NUMNODES]; | 44 | struct uncached_pool uncached_pools[MAX_NUMNODES]; |
39 | 45 | ||
40 | 46 | ||
41 | static void uncached_ipi_visibility(void *data) | 47 | static void uncached_ipi_visibility(void *data) |
42 | { | 48 | { |
43 | int status; | 49 | int status; |
50 | struct uncached_pool *uc_pool = (struct uncached_pool *)data; | ||
44 | 51 | ||
45 | status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); | 52 | status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); |
46 | if ((status != PAL_VISIBILITY_OK) && | 53 | if ((status != PAL_VISIBILITY_OK) && |
47 | (status != PAL_VISIBILITY_OK_REMOTE_NEEDED)) | 54 | (status != PAL_VISIBILITY_OK_REMOTE_NEEDED)) |
48 | printk(KERN_DEBUG "pal_prefetch_visibility() returns %i on " | 55 | atomic_inc(&uc_pool->status); |
49 | "CPU %i\n", status, raw_smp_processor_id()); | ||
50 | } | 56 | } |
51 | 57 | ||
52 | 58 | ||
53 | static void uncached_ipi_mc_drain(void *data) | 59 | static void uncached_ipi_mc_drain(void *data) |
54 | { | 60 | { |
55 | int status; | 61 | int status; |
62 | struct uncached_pool *uc_pool = (struct uncached_pool *)data; | ||
56 | 63 | ||
57 | status = ia64_pal_mc_drain(); | 64 | status = ia64_pal_mc_drain(); |
58 | if (status) | 65 | if (status != PAL_STATUS_SUCCESS) |
59 | printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on " | 66 | atomic_inc(&uc_pool->status); |
60 | "CPU %i\n", status, raw_smp_processor_id()); | ||
61 | } | 67 | } |
62 | 68 | ||
63 | 69 | ||
@@ -70,21 +76,34 @@ static void uncached_ipi_mc_drain(void *data) | |||
70 | * This is accomplished by first allocating a granule of cached memory pages | 76 | * This is accomplished by first allocating a granule of cached memory pages |
71 | * and then converting them to uncached memory pages. | 77 | * and then converting them to uncached memory pages. |
72 | */ | 78 | */ |
73 | static int uncached_add_chunk(struct gen_pool *pool, int nid) | 79 | static int uncached_add_chunk(struct uncached_pool *uc_pool, int nid) |
74 | { | 80 | { |
75 | struct page *page; | 81 | struct page *page; |
76 | int status, i; | 82 | int status, i, nchunks_added = uc_pool->nchunks_added; |
77 | unsigned long c_addr, uc_addr; | 83 | unsigned long c_addr, uc_addr; |
78 | 84 | ||
79 | if (allocated_granules >= MAX_UNCACHED_GRANULES) | 85 | if (mutex_lock_interruptible(&uc_pool->add_chunk_mutex) != 0) |
86 | return -1; /* interrupted by a signal */ | ||
87 | |||
88 | if (uc_pool->nchunks_added > nchunks_added) { | ||
89 | /* someone added a new chunk while we were waiting */ | ||
90 | mutex_unlock(&uc_pool->add_chunk_mutex); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | if (uc_pool->nchunks_added >= MAX_CONVERTED_CHUNKS_PER_NODE) { | ||
95 | mutex_unlock(&uc_pool->add_chunk_mutex); | ||
80 | return -1; | 96 | return -1; |
97 | } | ||
81 | 98 | ||
82 | /* attempt to allocate a granule's worth of cached memory pages */ | 99 | /* attempt to allocate a granule's worth of cached memory pages */ |
83 | 100 | ||
84 | page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO, | 101 | page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO, |
85 | IA64_GRANULE_SHIFT-PAGE_SHIFT); | 102 | IA64_GRANULE_SHIFT-PAGE_SHIFT); |
86 | if (!page) | 103 | if (!page) { |
104 | mutex_unlock(&uc_pool->add_chunk_mutex); | ||
87 | return -1; | 105 | return -1; |
106 | } | ||
88 | 107 | ||
89 | /* convert the memory pages from cached to uncached */ | 108 | /* convert the memory pages from cached to uncached */ |
90 | 109 | ||
@@ -102,11 +121,14 @@ static int uncached_add_chunk(struct gen_pool *pool, int nid) | |||
102 | flush_tlb_kernel_range(uc_addr, uc_adddr + IA64_GRANULE_SIZE); | 121 | flush_tlb_kernel_range(uc_addr, uc_adddr + IA64_GRANULE_SIZE); |
103 | 122 | ||
104 | status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); | 123 | status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL); |
105 | if (!status) { | 124 | if (status == PAL_VISIBILITY_OK_REMOTE_NEEDED) { |
106 | status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1); | 125 | atomic_set(&uc_pool->status, 0); |
107 | if (status) | 126 | status = smp_call_function(uncached_ipi_visibility, uc_pool, |
127 | 0, 1); | ||
128 | if (status || atomic_read(&uc_pool->status)) | ||
108 | goto failed; | 129 | goto failed; |
109 | } | 130 | } else if (status != PAL_VISIBILITY_OK) |
131 | goto failed; | ||
110 | 132 | ||
111 | preempt_disable(); | 133 | preempt_disable(); |
112 | 134 | ||
@@ -120,20 +142,24 @@ static int uncached_add_chunk(struct gen_pool *pool, int nid) | |||
120 | 142 | ||
121 | preempt_enable(); | 143 | preempt_enable(); |
122 | 144 | ||
123 | ia64_pal_mc_drain(); | 145 | status = ia64_pal_mc_drain(); |
124 | status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1); | 146 | if (status != PAL_STATUS_SUCCESS) |
125 | if (status) | 147 | goto failed; |
148 | atomic_set(&uc_pool->status, 0); | ||
149 | status = smp_call_function(uncached_ipi_mc_drain, uc_pool, 0, 1); | ||
150 | if (status || atomic_read(&uc_pool->status)) | ||
126 | goto failed; | 151 | goto failed; |
127 | 152 | ||
128 | /* | 153 | /* |
129 | * The chunk of memory pages has been converted to uncached so now we | 154 | * The chunk of memory pages has been converted to uncached so now we |
130 | * can add it to the pool. | 155 | * can add it to the pool. |
131 | */ | 156 | */ |
132 | status = gen_pool_add(pool, uc_addr, IA64_GRANULE_SIZE, nid); | 157 | status = gen_pool_add(uc_pool->pool, uc_addr, IA64_GRANULE_SIZE, nid); |
133 | if (status) | 158 | if (status) |
134 | goto failed; | 159 | goto failed; |
135 | 160 | ||
136 | allocated_granules++; | 161 | uc_pool->nchunks_added++; |
162 | mutex_unlock(&uc_pool->add_chunk_mutex); | ||
137 | return 0; | 163 | return 0; |
138 | 164 | ||
139 | /* failed to convert or add the chunk so give it back to the kernel */ | 165 | /* failed to convert or add the chunk so give it back to the kernel */ |
@@ -142,6 +168,7 @@ failed: | |||
142 | ClearPageUncached(&page[i]); | 168 | ClearPageUncached(&page[i]); |
143 | 169 | ||
144 | free_pages(c_addr, IA64_GRANULE_SHIFT-PAGE_SHIFT); | 170 | free_pages(c_addr, IA64_GRANULE_SHIFT-PAGE_SHIFT); |
171 | mutex_unlock(&uc_pool->add_chunk_mutex); | ||
145 | return -1; | 172 | return -1; |
146 | } | 173 | } |
147 | 174 | ||
@@ -158,7 +185,7 @@ failed: | |||
158 | unsigned long uncached_alloc_page(int starting_nid) | 185 | unsigned long uncached_alloc_page(int starting_nid) |
159 | { | 186 | { |
160 | unsigned long uc_addr; | 187 | unsigned long uc_addr; |
161 | struct gen_pool *pool; | 188 | struct uncached_pool *uc_pool; |
162 | int nid; | 189 | int nid; |
163 | 190 | ||
164 | if (unlikely(starting_nid >= MAX_NUMNODES)) | 191 | if (unlikely(starting_nid >= MAX_NUMNODES)) |
@@ -171,14 +198,14 @@ unsigned long uncached_alloc_page(int starting_nid) | |||
171 | do { | 198 | do { |
172 | if (!node_online(nid)) | 199 | if (!node_online(nid)) |
173 | continue; | 200 | continue; |
174 | pool = uncached_pool[nid]; | 201 | uc_pool = &uncached_pools[nid]; |
175 | if (pool == NULL) | 202 | if (uc_pool->pool == NULL) |
176 | continue; | 203 | continue; |
177 | do { | 204 | do { |
178 | uc_addr = gen_pool_alloc(pool, PAGE_SIZE); | 205 | uc_addr = gen_pool_alloc(uc_pool->pool, PAGE_SIZE); |
179 | if (uc_addr != 0) | 206 | if (uc_addr != 0) |
180 | return uc_addr; | 207 | return uc_addr; |
181 | } while (uncached_add_chunk(pool, nid) == 0); | 208 | } while (uncached_add_chunk(uc_pool, nid) == 0); |
182 | 209 | ||
183 | } while ((nid = (nid + 1) % MAX_NUMNODES) != starting_nid); | 210 | } while ((nid = (nid + 1) % MAX_NUMNODES) != starting_nid); |
184 | 211 | ||
@@ -197,7 +224,7 @@ EXPORT_SYMBOL(uncached_alloc_page); | |||
197 | void uncached_free_page(unsigned long uc_addr) | 224 | void uncached_free_page(unsigned long uc_addr) |
198 | { | 225 | { |
199 | int nid = paddr_to_nid(uc_addr - __IA64_UNCACHED_OFFSET); | 226 | int nid = paddr_to_nid(uc_addr - __IA64_UNCACHED_OFFSET); |
200 | struct gen_pool *pool = uncached_pool[nid]; | 227 | struct gen_pool *pool = uncached_pools[nid].pool; |
201 | 228 | ||
202 | if (unlikely(pool == NULL)) | 229 | if (unlikely(pool == NULL)) |
203 | return; | 230 | return; |
@@ -224,7 +251,7 @@ static int __init uncached_build_memmap(unsigned long uc_start, | |||
224 | unsigned long uc_end, void *arg) | 251 | unsigned long uc_end, void *arg) |
225 | { | 252 | { |
226 | int nid = paddr_to_nid(uc_start - __IA64_UNCACHED_OFFSET); | 253 | int nid = paddr_to_nid(uc_start - __IA64_UNCACHED_OFFSET); |
227 | struct gen_pool *pool = uncached_pool[nid]; | 254 | struct gen_pool *pool = uncached_pools[nid].pool; |
228 | size_t size = uc_end - uc_start; | 255 | size_t size = uc_end - uc_start; |
229 | 256 | ||
230 | touch_softlockup_watchdog(); | 257 | touch_softlockup_watchdog(); |
@@ -242,7 +269,8 @@ static int __init uncached_init(void) | |||
242 | int nid; | 269 | int nid; |
243 | 270 | ||
244 | for_each_online_node(nid) { | 271 | for_each_online_node(nid) { |
245 | uncached_pool[nid] = gen_pool_create(PAGE_SHIFT, nid); | 272 | uncached_pools[nid].pool = gen_pool_create(PAGE_SHIFT, nid); |
273 | mutex_init(&uncached_pools[nid].add_chunk_mutex); | ||
246 | } | 274 | } |
247 | 275 | ||
248 | efi_memmap_walk_uc(uncached_build_memmap, NULL); | 276 | efi_memmap_walk_uc(uncached_build_memmap, NULL); |