summaryrefslogtreecommitdiffstats
path: root/highmem-rt-Implement-pfn-and-prot-kmaps.patch
blob: 9c45e7dac419593609728802cf03651006aec05a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
From b153b62c603ed8c5ca030dc8188b76aeee4f162d Mon Sep 17 00:00:00 2001
From: Peter Zijlstra <a.p.zijlstra@chello.nl>
Date: Thu, 25 Feb 2010 12:43:52 +0100
Subject: [PATCH] highmem, -rt: Implement pfn and prot kmaps

commit e946864ec813ebab2f42752c99b080e1e012aaf6 in tip.

iomap_32 uses kmap_atomic_prot_pfn() for its maps, but on -rt we have
to use kmap() for such mappings, so teach kmap about pfn and prot
thingies.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
---
 arch/x86/include/asm/highmem.h |    9 ++-
 arch/x86/mm/highmem_32.c       |   27 +++++---
 arch/x86/mm/iomap_32.c         |   32 +--------
 mm/highmem.c                   |  152 +++++++++++++++++++++++++++------------
 4 files changed, 130 insertions(+), 90 deletions(-)

diff --git a/arch/x86/include/asm/highmem.h b/arch/x86/include/asm/highmem.h
index 91bc9f6..d20a885 100644
--- a/arch/x86/include/asm/highmem.h
+++ b/arch/x86/include/asm/highmem.h
@@ -55,14 +55,17 @@ extern unsigned long highstart_pfn, highend_pfn;
 #define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) << PAGE_SHIFT))
 
 extern void *kmap_high(struct page *page);
+extern void *kmap_pfn_prot(unsigned long pfn, pgprot_t prot);
 extern void kunmap_high(struct page *page);
 
 void *kmap(struct page *page);
+void *kmap_page_prot(struct page *page, pgprot_t prot);
 extern void kunmap_virt(void *ptr);
 extern struct page *kmap_to_page(void *ptr);
 void kunmap(struct page *page);
 
 void *__kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot);
+void *__kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot);
 void *__kmap_atomic(struct page *page, enum km_type type);
 void *__kmap_atomic_direct(struct page *page, enum km_type type);
 void __kunmap_atomic(void *kvaddr, enum km_type type);
@@ -82,15 +85,17 @@ extern void add_highpages_with_active_regions(int nid, unsigned long start_pfn,
  * on PREEMPT_RT kmap_atomic() is a wrapper that uses kmap():
  */
 #ifdef CONFIG_PREEMPT_RT
-# define kmap_atomic_prot(page, type, prot)	({ pagefault_disable(); kmap(page); })
+# define kmap_atomic_prot(page, type, prot)	({ pagefault_disable(); kmap_pfn_prot(page_to_pfn(page), prot); })
+# define kmap_atomic_prot_pfn(pfn, type, prot)	({ pagefault_disable(); kmap_pfn_prot(pfn, prot); })
 # define kmap_atomic(page, type)	({ pagefault_disable(); kmap(page); })
 # define kmap_atomic_pfn(pfn, type)	kmap(pfn_to_page(pfn))
-# define kunmap_atomic(kvaddr, type)	do { pagefault_enable(); kunmap_virt(kvaddr); } while(0)
+# define kunmap_atomic(kvaddr, type)	do { kunmap_virt(kvaddr); pagefault_enable(); } while(0)
 # define kmap_atomic_to_page(kvaddr)	kmap_to_page(kvaddr)
 # define kmap_atomic_direct(page, type)	__kmap_atomic_direct(page, type)
 # define kunmap_atomic_direct(kvaddr, type)	__kunmap_atomic(kvaddr, type)
 #else
 # define kmap_atomic_prot(page, type, prot)	__kmap_atomic_prot(page, type, prot)
+# define kmap_atomic_prot_pfn(pfn, type, prot)  __kmap_atomic_prot_pfn(pfn, type, prot)
 # define kmap_atomic(page, type)	__kmap_atomic(page, type)
 # define kmap_atomic_pfn(pfn, type)	__kmap_atomic_pfn(pfn, type)
 # define kunmap_atomic(kvaddr, type)	__kunmap_atomic(kvaddr, type)
diff --git a/arch/x86/mm/highmem_32.c b/arch/x86/mm/highmem_32.c
index dcb1899..7f2ac8a 100644
--- a/arch/x86/mm/highmem_32.c
+++ b/arch/x86/mm/highmem_32.c
@@ -19,16 +19,6 @@ void kunmap(struct page *page)
 	kunmap_high(page);
 }
 
-void kunmap_virt(void *ptr)
-{
-	struct page *page;
-
-	if ((unsigned long)ptr < PKMAP_ADDR(0))
-		return;
-	page = pte_page(pkmap_page_table[PKMAP_NR((unsigned long)ptr)]);
-	kunmap(page);
-}
-
 struct page *kmap_to_page(void *ptr)
 {
 	struct page *page;
@@ -75,6 +65,23 @@ void *__kmap_atomic_direct(struct page *page, enum km_type type)
 	return __kmap_atomic_prot(page, type, kmap_prot);
 }
 
+void *__kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot)
+{
+	enum fixed_addresses idx;
+	unsigned long vaddr;
+
+	preempt_disable();
+	pagefault_disable();
+
+	debug_kmap_atomic(type);
+	idx = type + KM_TYPE_NR * smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	set_pte(kmap_pte - idx, pfn_pte(pfn, prot));
+	arch_flush_lazy_mmu_mode();
+
+	return (void *)vaddr;
+}
+
 void *__kmap_atomic(struct page *page, enum km_type type)
 {
 	return kmap_atomic_prot(page, type, kmap_prot);
diff --git a/arch/x86/mm/iomap_32.c b/arch/x86/mm/iomap_32.c
index 715d822..38a1a68 100644
--- a/arch/x86/mm/iomap_32.c
+++ b/arch/x86/mm/iomap_32.c
@@ -55,23 +55,6 @@ iomap_free(resource_size_t base, unsigned long size)
 }
 EXPORT_SYMBOL_GPL(iomap_free);
 
-void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot)
-{
-	enum fixed_addresses idx;
-	unsigned long vaddr;
-
-	preempt_disable();
-	pagefault_disable();
-
-	debug_kmap_atomic(type);
-	idx = type + KM_TYPE_NR * smp_processor_id();
-	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
-	set_pte(kmap_pte - idx, pfn_pte(pfn, prot));
-	arch_flush_lazy_mmu_mode();
-
-	return (void *)vaddr;
-}
-
 /*
  * Map 'pfn' using fixed map 'type' and protections 'prot'
  */
@@ -94,19 +77,6 @@ EXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn);
 void
 iounmap_atomic(void *kvaddr, enum km_type type)
 {
-	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
-	enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id();
-
-	/*
-	 * Force other mappings to Oops if they'll try to access this pte
-	 * without first remap it.  Keeping stale mappings around is a bad idea
-	 * also, in case the page changes cacheability attributes or becomes
-	 * a protected page in a hypervisor.
-	 */
-	if (vaddr == __fix_to_virt(FIX_KMAP_BEGIN+idx))
-		kpte_clear_flush(kmap_pte-idx, vaddr);
-
-	pagefault_enable();
-	preempt_enable();
+	kunmap_atomic(kvaddr, type);
 }
 EXPORT_SYMBOL_GPL(iounmap_atomic);
diff --git a/mm/highmem.c b/mm/highmem.c
index 2cc0034..9164c02 100644
--- a/mm/highmem.c
+++ b/mm/highmem.c
@@ -66,7 +66,13 @@ unsigned int nr_free_highpages (void)
  *  1 means its free for use - either mapped or not.
  *  n means that there are (n-1) current users of it.
  */
-static atomic_t pkmap_count[LAST_PKMAP];
+
+struct pkmap_state {
+	atomic_t count;
+	int	 pfn;
+};
+
+static struct pkmap_state pkmap[LAST_PKMAP];
 static atomic_t pkmap_hand;
 static atomic_t pkmap_free;
 static atomic_t pkmap_users;
@@ -105,25 +111,26 @@ static DECLARE_WAIT_QUEUE_HEAD(pkmap_wait);
  */
 static int pkmap_try_free(int pos)
 {
-	if (atomic_cmpxchg(&pkmap_count[pos], 1, 0) != 1)
+	if (atomic_cmpxchg(&pkmap[pos].count, 1, 0) != 1)
 		return -1;
 	atomic_dec(&pkmap_free);
 	/*
 	 * TODO: add a young bit to make it CLOCK
 	 */
 	if (!pte_none(pkmap_page_table[pos])) {
-		struct page *page = pte_page(pkmap_page_table[pos]);
 		unsigned long addr = PKMAP_ADDR(pos);
 		pte_t *ptep = &pkmap_page_table[pos];
 
-		VM_BUG_ON(addr != (unsigned long)page_address(page));
+		if (!pkmap[pos].pfn) {
+			struct page *page = pte_page(pkmap_page_table[pos]);
+			VM_BUG_ON(addr != (unsigned long)page_address(page));
+			if (!__set_page_address(page, NULL, pos))
+				BUG();
+			flush_kernel_dcache_page(page);
+		}
 
-		if (!__set_page_address(page, NULL, pos))
-			BUG();
-		flush_kernel_dcache_page(page);
 		pte_clear(&init_mm, addr, ptep);
 
-
 		return 1;
 	}
 
@@ -187,7 +194,7 @@ got_one:
 				continue;
 
 			if (!flush) {
-				atomic_t *counter = &pkmap_count[pos2];
+				atomic_t *counter = &pkmap[pos2].count;
 				VM_BUG_ON(atomic_read(counter) != 0);
 				atomic_set(counter, 2);
 				pkmap_put(counter);
@@ -197,7 +204,7 @@ got_one:
 		flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
 
 		for (i = 0; i < nr; i++) {
-			atomic_t *counter = &pkmap_count[entries[i]];
+			atomic_t *counter = &pkmap[entries[i]].count;
 			VM_BUG_ON(atomic_read(counter) != 0);
 			atomic_set(counter, 2);
 			pkmap_put(counter);
@@ -207,32 +214,51 @@ got_one:
 	return pos;
 }
 
-static unsigned long pkmap_insert(struct page *page)
+static unsigned long pkmap_insert(unsigned long pfn, pgprot_t prot)
 {
 	int pos = pkmap_get_free();
 	unsigned long vaddr = PKMAP_ADDR(pos);
 	pte_t *ptep = &pkmap_page_table[pos];
-	pte_t entry = mk_pte(page, kmap_prot);
-	atomic_t *counter = &pkmap_count[pos];
+	pte_t entry = pfn_pte(pfn, prot);
+	atomic_t *counter = &pkmap[pos].count;
 
 	VM_BUG_ON(atomic_read(counter) != 0);
-
 	set_pte_at(&init_mm, vaddr, ptep, entry);
-	if (unlikely(!__set_page_address(page, (void *)vaddr, pos))) {
+
+	pkmap[pos].pfn =
+		!(pgprot_val(prot) == pgprot_val(kmap_prot) && pfn_valid(pfn));
+
+	if (!pkmap[pos].pfn) {
+		struct page *page = pfn_to_page(pfn);
+
+		if (unlikely(!__set_page_address(page, (void *)vaddr, pos))) {
+			/*
+			 * concurrent pkmap_inserts for this page -
+			 * the other won the race, release this entry.
+			 *
+			 * we can still clear the pte without a tlb flush since
+			 * it couldn't have been used yet.
+			 */
+			pte_clear(&init_mm, vaddr, ptep);
+			VM_BUG_ON(atomic_read(counter) != 0);
+			atomic_set(counter, 2);
+			pkmap_put(counter);
+			return 0;
+		}
+	} else {
+#ifdef ARCH_NEEDS_KMAP_HIGH_GET
 		/*
-		 * concurrent pkmap_inserts for this page -
-		 * the other won the race, release this entry.
+		 * non-default prot and pure pfn memory doesn't
+		 * get map deduplication, nor a working page_address
 		 *
-		 * we can still clear the pte without a tlb flush since
-		 * it couldn't have been used yet.
+		 * this also makes it incompatible with
+		 * ARCH_NEEDS_KMAP_HIGH_GET
 		 */
-		pte_clear(&init_mm, vaddr, ptep);
-		VM_BUG_ON(atomic_read(counter) != 0);
-		atomic_set(counter, 2);
-		pkmap_put(counter);
-		vaddr = 0;
-	} else
-		atomic_set(counter, 2);
+		BUG();
+#endif
+	}
+
+	atomic_set(counter, 2);
 
 	return vaddr;
 }
@@ -313,20 +339,17 @@ static void kunmap_account(void)
 	wake_up(&pkmap_wait);
 }
 
-void *kmap_high(struct page *page)
+void *kmap_get(struct page *page)
 {
 	unsigned long vaddr;
-
-
-	kmap_account();
 again:
 	vaddr = (unsigned long)page_address(page);
 	if (vaddr) {
-		atomic_t *counter = &pkmap_count[PKMAP_NR(vaddr)];
+		atomic_t *counter = &pkmap[PKMAP_NR(vaddr)].count;
 		if (atomic_inc_not_zero(counter)) {
 			/*
-			 * atomic_inc_not_zero implies a (memory) barrier on success
-			 * so page address will be reloaded.
+			 * atomic_inc_not_zero implies a (memory) barrier on
+			 * success so page address will be reloaded.
 			 */
 			unsigned long vaddr2 = (unsigned long)page_address(page);
 			if (likely(vaddr == vaddr2))
@@ -341,19 +364,49 @@ again:
 			 * reused.
 			 */
 			pkmap_put(counter);
-			goto again;
 		}
+		goto again;
 	}
+	return (void *)vaddr;
+}
 
-	vaddr = pkmap_insert(page);
-	if (!vaddr)
-		goto again;
+void *kmap_high(struct page *page)
+{
+	unsigned long vaddr;
+
+	kmap_account();
+
+again:
+	vaddr = (unsigned long)kmap_get(page);
+	if (!vaddr) {
+		vaddr = pkmap_insert(page_to_pfn(page), kmap_prot);
+		if (!vaddr)
+			goto again;
+	}
 
 	return (void *)vaddr;
 }
 
 EXPORT_SYMBOL(kmap_high);
 
+void *kmap_pfn_prot(unsigned long pfn, pgprot_t prot)
+{
+	unsigned long vaddr;
+
+	if (pgprot_val(prot) == pgprot_val(kmap_prot) &&
+			pfn_valid(pfn) && PageHighMem(pfn_to_page(pfn)))
+		return kmap_high(pfn_to_page(pfn));
+
+	kmap_account();
+
+	vaddr = pkmap_insert(pfn, prot);
+	BUG_ON(!vaddr);
+
+	return (void *)vaddr;
+}
+
+EXPORT_SYMBOL(kmap_pfn_prot);
+
 #ifdef ARCH_NEEDS_KMAP_HIGH_GET
 /**
  * kmap_high_get - pin a highmem page into memory
@@ -370,21 +423,26 @@ void *kmap_high_get(struct page *page)
 	unsigned long vaddr, flags;
 
 	lock_kmap_any(flags);
-	vaddr = (unsigned long)page_address(page);
-	if (vaddr) {
-		BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 1);
-		pkmap_count[PKMAP_NR(vaddr)]++;
-	}
+	vaddr = (unsigned long)kmap_get(page);
 	unlock_kmap_any(flags);
-	return (void*) vaddr;
+	return (void *)vaddr;
 }
 #endif
 
- void kunmap_high(struct page *page)
+void kunmap_virt(void *ptr)
+{
+	unsigned long vaddr = (unsigned long)ptr;
+	if (vaddr < PKMAP_ADDR(0) || vaddr >= PKMAP_ADDR(LAST_PKMAP))
+		return;
+	pkmap_put(&pkmap[PKMAP_NR(vaddr)].count);
+	kunmap_account();
+}
+
+void kunmap_high(struct page *page)
 {
 	unsigned long vaddr = (unsigned long)page_address(page);
 	BUG_ON(!vaddr);
-	pkmap_put(&pkmap_count[PKMAP_NR(vaddr)]);
+	pkmap_put(&pkmap[PKMAP_NR(vaddr)].count);
 	kunmap_account();
 }
 
@@ -539,8 +597,8 @@ void __init page_address_init(void)
 #ifdef CONFIG_HIGHMEM
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(pkmap_count); i++)
-		atomic_set(&pkmap_count[i], 1);
+	for (i = 0; i < ARRAY_SIZE(pkmap); i++)
+		atomic_set(&pkmap[i].count, 1);
 	atomic_set(&pkmap_hand, 0);
 	atomic_set(&pkmap_free, LAST_PKMAP);
 	atomic_set(&pkmap_users, 0);
-- 
1.7.0.4