diff options
author | Arjan van de Ven <arjan@infradead.org> | 2008-01-30 07:34:06 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:34:06 -0500 |
commit | 75cbade8ea3127a84e3da7c2c15808e54f0df7e8 (patch) | |
tree | b91fd5e8f9f5f8da0da55a7783c70fc3149cbf2d /arch/x86/mm/pageattr.c | |
parent | e81d5dc41b67349c06e80658227c9156738f0df1 (diff) |
x86: a new API for drivers/etc to control cache and other page attributes
Right now, if drivers or other code want to change, say, a cache attribute of a
page, the only API they have is change_page_attr(). c-p-a is a really bad API
for this, because it forces the caller to know *ALL* the attributes he wants
for the page, not just the 1 thing he wants to change. So code that wants to
set a page uncachable, needs to be aware of the NX status as well etc etc etc.
This patch introduces a set of new APIs for this, set_pages_<attr> and
set_memory_<attr>, that offer a logical change to the user, and leave all
attributes not implied by the requested logical change alone.
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 4208571334db..f3c9510b8d92 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -211,6 +211,8 @@ repeat: | |||
211 | * mem_map entry (pfn_valid() is false). | 211 | * mem_map entry (pfn_valid() is false). |
212 | * | 212 | * |
213 | * See change_page_attr() documentation for more details. | 213 | * See change_page_attr() documentation for more details. |
214 | * | ||
215 | * Modules and drivers should use the set_memory_* APIs instead. | ||
214 | */ | 216 | */ |
215 | 217 | ||
216 | int change_page_attr_addr(unsigned long address, int numpages, pgprot_t prot) | 218 | int change_page_attr_addr(unsigned long address, int numpages, pgprot_t prot) |
@@ -273,6 +275,8 @@ int change_page_attr_addr(unsigned long address, int numpages, pgprot_t prot) | |||
273 | * (e.g. in user space) * This function only deals with the kernel linear map. | 275 | * (e.g. in user space) * This function only deals with the kernel linear map. |
274 | * | 276 | * |
275 | * For MMIO areas without mem_map use change_page_attr_addr() instead. | 277 | * For MMIO areas without mem_map use change_page_attr_addr() instead. |
278 | * | ||
279 | * Modules and drivers should use the set_pages_* APIs instead. | ||
276 | */ | 280 | */ |
277 | int change_page_attr(struct page *page, int numpages, pgprot_t prot) | 281 | int change_page_attr(struct page *page, int numpages, pgprot_t prot) |
278 | { | 282 | { |
@@ -282,6 +286,199 @@ int change_page_attr(struct page *page, int numpages, pgprot_t prot) | |||
282 | } | 286 | } |
283 | EXPORT_SYMBOL(change_page_attr); | 287 | EXPORT_SYMBOL(change_page_attr); |
284 | 288 | ||
289 | /** | ||
290 | * change_page_attr_set - Change page table attributes in the linear mapping. | ||
291 | * @addr: Virtual address in linear mapping. | ||
292 | * @numpages: Number of pages to change | ||
293 | * @prot: Protection/caching type bits to set (PAGE_*) | ||
294 | * | ||
295 | * Returns 0 on success, otherwise a negated errno. | ||
296 | * | ||
297 | * This should be used when a page is mapped with a different caching policy | ||
298 | * than write-back somewhere - some CPUs do not like it when mappings with | ||
299 | * different caching policies exist. This changes the page attributes of the | ||
300 | * in kernel linear mapping too. | ||
301 | * | ||
302 | * Caller must call global_flush_tlb() later to make the changes active. | ||
303 | * | ||
304 | * The caller needs to ensure that there are no conflicting mappings elsewhere | ||
305 | * (e.g. in user space) * This function only deals with the kernel linear map. | ||
306 | * | ||
307 | * This function is different from change_page_attr() in that only selected bits | ||
308 | * are impacted, all other bits remain as is. | ||
309 | */ | ||
310 | int change_page_attr_set(unsigned long addr, int numpages, pgprot_t prot) | ||
311 | { | ||
312 | pgprot_t current_prot; | ||
313 | int level; | ||
314 | pte_t *pte; | ||
315 | |||
316 | pte = lookup_address(addr, &level); | ||
317 | if (pte) | ||
318 | current_prot = pte_pgprot(*pte); | ||
319 | else | ||
320 | pgprot_val(current_prot) = 0; | ||
321 | |||
322 | pgprot_val(prot) = pgprot_val(current_prot) | pgprot_val(prot); | ||
323 | |||
324 | return change_page_attr_addr(addr, numpages, prot); | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * change_page_attr_clear - Change page table attributes in the linear mapping. | ||
329 | * @addr: Virtual address in linear mapping. | ||
330 | * @numpages: Number of pages to change | ||
331 | * @prot: Protection/caching type bits to clear (PAGE_*) | ||
332 | * | ||
333 | * Returns 0 on success, otherwise a negated errno. | ||
334 | * | ||
335 | * This should be used when a page is mapped with a different caching policy | ||
336 | * than write-back somewhere - some CPUs do not like it when mappings with | ||
337 | * different caching policies exist. This changes the page attributes of the | ||
338 | * in kernel linear mapping too. | ||
339 | * | ||
340 | * Caller must call global_flush_tlb() later to make the changes active. | ||
341 | * | ||
342 | * The caller needs to ensure that there are no conflicting mappings elsewhere | ||
343 | * (e.g. in user space) * This function only deals with the kernel linear map. | ||
344 | * | ||
345 | * This function is different from change_page_attr() in that only selected bits | ||
346 | * are impacted, all other bits remain as is. | ||
347 | */ | ||
348 | int change_page_attr_clear(unsigned long addr, int numpages, pgprot_t prot) | ||
349 | { | ||
350 | pgprot_t current_prot; | ||
351 | int level; | ||
352 | pte_t *pte; | ||
353 | |||
354 | pte = lookup_address(addr, &level); | ||
355 | if (pte) | ||
356 | current_prot = pte_pgprot(*pte); | ||
357 | else | ||
358 | pgprot_val(current_prot) = 0; | ||
359 | |||
360 | pgprot_val(prot) = pgprot_val(current_prot) & ~pgprot_val(prot); | ||
361 | |||
362 | return change_page_attr_addr(addr, numpages, prot); | ||
363 | } | ||
364 | |||
365 | |||
366 | |||
367 | int set_memory_uc(unsigned long addr, int numpages) | ||
368 | { | ||
369 | pgprot_t uncached; | ||
370 | |||
371 | pgprot_val(uncached) = _PAGE_PCD | _PAGE_PWT; | ||
372 | return change_page_attr_set(addr, numpages, uncached); | ||
373 | } | ||
374 | EXPORT_SYMBOL(set_memory_uc); | ||
375 | |||
376 | int set_memory_wb(unsigned long addr, int numpages) | ||
377 | { | ||
378 | pgprot_t uncached; | ||
379 | |||
380 | pgprot_val(uncached) = _PAGE_PCD | _PAGE_PWT; | ||
381 | return change_page_attr_clear(addr, numpages, uncached); | ||
382 | } | ||
383 | EXPORT_SYMBOL(set_memory_wb); | ||
384 | |||
385 | int set_memory_x(unsigned long addr, int numpages) | ||
386 | { | ||
387 | pgprot_t nx; | ||
388 | |||
389 | pgprot_val(nx) = _PAGE_NX; | ||
390 | return change_page_attr_clear(addr, numpages, nx); | ||
391 | } | ||
392 | EXPORT_SYMBOL(set_memory_x); | ||
393 | |||
394 | int set_memory_nx(unsigned long addr, int numpages) | ||
395 | { | ||
396 | pgprot_t nx; | ||
397 | |||
398 | pgprot_val(nx) = _PAGE_NX; | ||
399 | return change_page_attr_set(addr, numpages, nx); | ||
400 | } | ||
401 | EXPORT_SYMBOL(set_memory_nx); | ||
402 | |||
403 | int set_memory_ro(unsigned long addr, int numpages) | ||
404 | { | ||
405 | pgprot_t rw; | ||
406 | |||
407 | pgprot_val(rw) = _PAGE_RW; | ||
408 | return change_page_attr_clear(addr, numpages, rw); | ||
409 | } | ||
410 | EXPORT_SYMBOL(set_memory_ro); | ||
411 | |||
412 | int set_memory_rw(unsigned long addr, int numpages) | ||
413 | { | ||
414 | pgprot_t rw; | ||
415 | |||
416 | pgprot_val(rw) = _PAGE_RW; | ||
417 | return change_page_attr_set(addr, numpages, rw); | ||
418 | } | ||
419 | EXPORT_SYMBOL(set_memory_rw); | ||
420 | |||
421 | int set_pages_uc(struct page *page, int numpages) | ||
422 | { | ||
423 | unsigned long addr = (unsigned long)page_address(page); | ||
424 | pgprot_t uncached; | ||
425 | |||
426 | pgprot_val(uncached) = _PAGE_PCD | _PAGE_PWT; | ||
427 | return change_page_attr_set(addr, numpages, uncached); | ||
428 | } | ||
429 | EXPORT_SYMBOL(set_pages_uc); | ||
430 | |||
431 | int set_pages_wb(struct page *page, int numpages) | ||
432 | { | ||
433 | unsigned long addr = (unsigned long)page_address(page); | ||
434 | pgprot_t uncached; | ||
435 | |||
436 | pgprot_val(uncached) = _PAGE_PCD | _PAGE_PWT; | ||
437 | return change_page_attr_clear(addr, numpages, uncached); | ||
438 | } | ||
439 | EXPORT_SYMBOL(set_pages_wb); | ||
440 | |||
441 | int set_pages_x(struct page *page, int numpages) | ||
442 | { | ||
443 | unsigned long addr = (unsigned long)page_address(page); | ||
444 | pgprot_t nx; | ||
445 | |||
446 | pgprot_val(nx) = _PAGE_NX; | ||
447 | return change_page_attr_clear(addr, numpages, nx); | ||
448 | } | ||
449 | EXPORT_SYMBOL(set_pages_x); | ||
450 | |||
451 | int set_pages_nx(struct page *page, int numpages) | ||
452 | { | ||
453 | unsigned long addr = (unsigned long)page_address(page); | ||
454 | pgprot_t nx; | ||
455 | |||
456 | pgprot_val(nx) = _PAGE_NX; | ||
457 | return change_page_attr_set(addr, numpages, nx); | ||
458 | } | ||
459 | EXPORT_SYMBOL(set_pages_nx); | ||
460 | |||
461 | int set_pages_ro(struct page *page, int numpages) | ||
462 | { | ||
463 | unsigned long addr = (unsigned long)page_address(page); | ||
464 | pgprot_t rw; | ||
465 | |||
466 | pgprot_val(rw) = _PAGE_RW; | ||
467 | return change_page_attr_clear(addr, numpages, rw); | ||
468 | } | ||
469 | EXPORT_SYMBOL(set_pages_ro); | ||
470 | |||
471 | int set_pages_rw(struct page *page, int numpages) | ||
472 | { | ||
473 | unsigned long addr = (unsigned long)page_address(page); | ||
474 | pgprot_t rw; | ||
475 | |||
476 | pgprot_val(rw) = _PAGE_RW; | ||
477 | return change_page_attr_set(addr, numpages, rw); | ||
478 | } | ||
479 | EXPORT_SYMBOL(set_pages_rw); | ||
480 | |||
481 | |||
285 | void clflush_cache_range(void *addr, int size) | 482 | void clflush_cache_range(void *addr, int size) |
286 | { | 483 | { |
287 | int i; | 484 | int i; |