From: Andi Kleen <ak@suse.de>
To: linux-kernel@vger.kernel.org, jbeulich@novell.com, mingo@elte.hu,
tglx@linutronix.de
Subject: [PATCH] [12/31] CPA: CLFLUSH support in change_page_attr()
Date: Mon, 14 Jan 2008 23:16:44 +0100 (CET) [thread overview]
Message-ID: <20080114221644.816F314F83@wotan.suse.de> (raw)
In-Reply-To: <200801141116.534682000@suse.de>
Queue individual data pages for flushing with CLFLUSH in change_page_attr(),
instead of doing global WBINVDs. WBINVD is a very painful operation
for the CPU (can take msecs) and quite slow too. Worse it is not interruptible
and can cause long latencies on hypervisors on older Intel VT systems.
CLFLUSH on the other hand only flushes the cache lines that actually need to be
flushed and since it works in smaller chunks is more preemeptible.
To do this c_p_a needs to save the address to be flush for global_tlb_flush()
later. This is done using a separate data structure, not struct page,
because page->lru is often used or not there for memory holes.
Also the flushes are done in FIFO order now, not LIFO.
Signed-off-by: Andi Kleen <ak@suse.de>
---
arch/x86/mm/pageattr_32.c | 78 ++++++++++++++++++++++++++++++++++------------
arch/x86/mm/pageattr_64.c | 77 ++++++++++++++++++++++++++++++++++-----------
2 files changed, 118 insertions(+), 37 deletions(-)
Index: linux/arch/x86/mm/pageattr_64.c
===================================================================
--- linux.orig/arch/x86/mm/pageattr_64.c
+++ linux/arch/x86/mm/pageattr_64.c
@@ -13,6 +13,11 @@
#include <asm/tlbflush.h>
#include <asm/io.h>
+struct flush {
+ struct list_head l;
+ unsigned long addr;
+};
+
pte_t *lookup_address(unsigned long address, int *level)
{
pgd_t *pgd = pgd_offset_k(address);
@@ -63,6 +68,11 @@ static struct page *split_large_page(uns
return base;
}
+struct flush_arg {
+ int full_flush;
+ struct list_head l;
+};
+
void clflush_cache_range(void *adr, int size)
{
int i;
@@ -72,27 +82,27 @@ void clflush_cache_range(void *adr, int
static void flush_kernel_map(void *arg)
{
- struct list_head *l = (struct list_head *)arg;
- struct page *pg;
+ struct flush_arg *a = (struct flush_arg *)arg;
+ struct flush *f;
+
+ if (!cpu_has_clflush)
+ a->full_flush = 1;
/* When clflush is available always use it because it is
much cheaper than WBINVD. */
- /* clflush is still broken. Disable for now. */
- if (1 || !cpu_has_clflush)
+ if (a->full_flush)
asm volatile("wbinvd" ::: "memory");
- else list_for_each_entry(pg, l, lru) {
- void *adr = page_address(pg);
- clflush_cache_range(adr, PAGE_SIZE);
+ list_for_each_entry(f, &a->l, l) {
+ if (!a->full_flush)
+ clflush_cache_range((void *)f->addr, PAGE_SIZE);
}
__flush_tlb_all();
}
-static inline void flush_map(struct list_head *l)
-{
- on_each_cpu(flush_kernel_map, l, 1, 1);
-}
-
-static LIST_HEAD(deferred_pages); /* protected by init_mm.mmap_sem */
+/* both protected by init_mm.mmap_sem */
+static int full_flush;
+static LIST_HEAD(deferred_pages);
+static LIST_HEAD(flush_pages);
static inline void save_page(struct page *fpage)
{
@@ -124,6 +134,25 @@ static void revert_page(unsigned long ad
set_pte((pte_t *)pmd, large_pte);
}
+/*
+ * Mark the address for flushing later in global_tlb_flush().
+ *
+ * Other parts of the kernel are already in a feeding frenzy over the various
+ * struct page fields. Instead of trying to compete allocate a separate
+ * data structure to keep track of the flush. This has the added bonus that
+ * it will work for MMIO holes without mem_map too.
+ */
+static void set_tlb_flush(unsigned long address)
+{
+ struct flush *f = kmalloc(sizeof(struct flush), GFP_KERNEL);
+ if (!f) {
+ full_flush = 1;
+ return;
+ }
+ f->addr = address;
+ list_add_tail(&f->l, &flush_pages);
+}
+
static int
__change_page_attr(unsigned long address, unsigned long pfn, pgprot_t prot,
pgprot_t ref_prot)
@@ -136,8 +165,11 @@ __change_page_attr(unsigned long address
kpte = lookup_address(address, &level);
if (!kpte) return 0;
kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK);
- BUG_ON(PageLRU(kpte_page));
BUG_ON(PageCompound(kpte_page));
+ BUG_ON(PageLRU(kpte_page));
+
+ set_tlb_flush(address);
+
if (pgprot_val(prot) != pgprot_val(ref_prot)) {
if (!pte_huge(*kpte)) {
set_pte(kpte, pfn_pte(pfn, prot));
@@ -231,7 +263,9 @@ int change_page_attr(struct page *page,
void global_flush_tlb(void)
{
struct page *pg, *next;
- struct list_head l;
+ struct flush *f, *fnext;
+ struct flush_arg arg;
+ struct list_head free_pages;
/*
* Write-protect the semaphore, to exclude two contexts
@@ -239,12 +273,19 @@ void global_flush_tlb(void)
* exclude new additions to the deferred_pages list:
*/
down_write(&init_mm.mmap_sem);
- list_replace_init(&deferred_pages, &l);
+ arg.full_flush = full_flush;
+ full_flush = 0;
+ list_replace_init(&flush_pages, &arg.l);
+ list_replace_init(&deferred_pages, &free_pages);
up_write(&init_mm.mmap_sem);
- flush_map(&l);
+ on_each_cpu(flush_kernel_map, &arg, 1, 1);
+
+ list_for_each_entry_safe(f, fnext, &arg.l, l) {
+ kfree(f);
+ }
- list_for_each_entry_safe(pg, next, &l, lru) {
+ list_for_each_entry_safe(pg, next, &free_pages, lru) {
list_del(&pg->lru);
clear_bit(PG_arch_1, &pg->flags);
if (page_private(pg) != 0)
Index: linux/arch/x86/mm/pageattr_32.c
===================================================================
--- linux.orig/arch/x86/mm/pageattr_32.c
+++ linux/arch/x86/mm/pageattr_32.c
@@ -15,8 +15,15 @@
#include <asm/sections.h>
/* Protected by init_mm.mmap_sem */
+/* Variables protected by cpa_lock */
+static int full_flush;
static struct list_head df_list = LIST_HEAD_INIT(df_list);
+static LIST_HEAD(flush_pages);
+struct flush {
+ struct list_head l;
+ unsigned long addr;
+};
pte_t *lookup_address(unsigned long address, int *level)
{
pgd_t *pgd = pgd_offset_k(address);
@@ -67,25 +74,31 @@ static struct page *split_large_page(uns
return base;
}
-static void cache_flush_page(struct page *p)
+struct flush_arg {
+ int full_flush;
+ struct list_head l;
+};
+
+void clflush_cache_range(void *adr, int size)
{
- void *adr = page_address(p);
int i;
- for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)
+ for (i = 0; i < size; i += boot_cpu_data.x86_clflush_size)
clflush(adr+i);
}
static void flush_kernel_map(void *arg)
{
- struct list_head *lh = (struct list_head *)arg;
- struct page *p;
+ struct flush_arg *a = (struct flush_arg *)arg;
+ struct flush *f;
- /* High level code is not ready for clflush yet */
- if (0 && cpu_has_clflush) {
- list_for_each_entry (p, lh, lru)
- cache_flush_page(p);
- } else if (boot_cpu_data.x86_model >= 4)
+ if (!cpu_has_clflush)
+ a->full_flush = 1;
+ if (a->full_flush && boot_cpu_data.x86_model >= 4)
wbinvd();
+ list_for_each_entry(f, &a->l, l) {
+ if (!a->full_flush)
+ clflush_cache_range((void *)f->addr, PAGE_SIZE);
+ }
/* Flush all to work around Errata in early athlons regarding
* large page flushing.
@@ -141,6 +154,25 @@ static inline void save_page(struct page
list_add(&kpte_page->lru, &df_list);
}
+/*
+ * Mark the address for flushing later in global_tlb_flush().
+ *
+ * Other parts of the kernel are already in a feeding frenzy over the various
+ * struct page fields. Instead of trying to compete allocate a separate
+ * data structure to keep track of the flush. This has the added bonus that
+ * it will work for MMIO holes without mem_map too.
+ */
+static void set_tlb_flush(unsigned long address)
+{
+ struct flush *f = kmalloc(sizeof(struct flush), GFP_KERNEL);
+ if (!f) {
+ full_flush = 1;
+ return;
+ }
+ f->addr = address;
+ list_add_tail(&f->l, &flush_pages);
+}
+
static int
__change_page_attr(struct page *page, pgprot_t prot)
{
@@ -159,6 +191,8 @@ __change_page_attr(struct page *page, pg
BUG_ON(PageLRU(kpte_page));
BUG_ON(PageCompound(kpte_page));
+ set_tlb_flush(address);
+
if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) {
if (!pte_huge(*kpte)) {
set_pte_atomic(kpte, mk_pte(page, prot));
@@ -199,11 +233,6 @@ __change_page_attr(struct page *page, pg
return 0;
}
-static inline void flush_map(struct list_head *l)
-{
- on_each_cpu(flush_kernel_map, l, 1, 1);
-}
-
/*
* Change the page attributes of an page in the linear mapping.
*
@@ -234,16 +263,27 @@ int change_page_attr(struct page *page,
void global_flush_tlb(void)
{
- struct list_head l;
+ struct flush_arg arg;
struct page *pg, *next;
+ struct flush *f, *fnext;
+ struct list_head free_pages;
BUG_ON(irqs_disabled());
down_write(&init_mm.mmap_sem);
- list_replace_init(&df_list, &l);
+ arg.full_flush = full_flush;
+ full_flush = 0;
+ list_replace_init(&flush_pages, &arg.l);
+ list_replace_init(&df_list, &free_pages);
up_write(&init_mm.mmap_sem);
- flush_map(&l);
- list_for_each_entry_safe(pg, next, &l, lru) {
+
+ on_each_cpu(flush_kernel_map, &arg, 1, 1);
+
+ list_for_each_entry_safe(f, fnext, &arg.l, l) {
+ kfree(f);
+ }
+
+ list_for_each_entry_safe(pg, next, &free_pages, lru) {
list_del(&pg->lru);
clear_bit(PG_arch_1, &pg->flags);
if (PageReserved(pg) || !cpu_has_pse || page_private(pg) != 0)
next prev parent reply other threads:[~2008-01-14 22:21 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-01-14 22:16 [PATCH] [0/31] Great change_page_attr patch series v2 Andi Kleen
2008-01-14 22:16 ` [PATCH] [1/31] Shrink __PAGE_KERNEL/__PAGE_KERNEL_EXEC on non PAE kernels Andi Kleen
2008-01-14 22:16 ` [PATCH] [2/31] CPA: Do a simple self test at boot Andi Kleen
2008-01-15 8:47 ` Harvey Harrison
2008-01-15 9:59 ` Andi Kleen
2008-01-15 10:07 ` Harvey Harrison
2008-01-14 22:16 ` [PATCH] [3/31] Add pte accessors for the global bit Andi Kleen
2008-01-14 22:16 ` [PATCH] [4/31] Add pte_pgprot on i386 Andi Kleen
2008-01-15 13:00 ` Johannes Weiner
2008-01-14 22:16 ` [PATCH] [5/31] Don't drop NX bit in pte modifier functions for 32bit Andi Kleen
2008-01-14 22:16 ` [PATCH] [6/31] CPA: Undo white space changes Andi Kleen
2008-01-14 22:16 ` [PATCH] [7/31] Extract page table dumping code from i386 fault handler into dump_pagetable() Andi Kleen
2008-01-15 8:56 ` Harvey Harrison
2008-01-15 10:00 ` Andi Kleen
2008-01-15 10:05 ` Harvey Harrison
2008-01-14 22:16 ` [PATCH] [8/31] CPA: Return the page table level in lookup_address() Andi Kleen
2008-01-14 22:16 ` [PATCH] [9/31] CPA: Add simple self test at boot Andi Kleen
2008-01-15 10:37 ` Harvey Harrison
2008-01-14 22:16 ` [PATCH] [10/31] CPA: Change kernel_map_pages to not use c_p_a() Andi Kleen
2008-01-14 22:16 ` [PATCH] [11/31] CPA: Change 32bit back to init_mm semaphore locking Andi Kleen
2008-01-14 22:16 ` Andi Kleen [this message]
2008-01-15 8:40 ` [PATCH] [12/31] CPA: CLFLUSH support in change_page_attr() Jan Beulich
2008-01-15 9:57 ` Andi Kleen
2008-01-14 22:16 ` [PATCH] [13/31] CPA: Use macros to modify the PG_arch_1 page flags in change_page_attr Andi Kleen
2008-01-15 9:29 ` Harvey Harrison
2008-01-15 10:06 ` Andi Kleen
2008-01-15 10:15 ` Harvey Harrison
2008-01-15 10:25 ` Andi Kleen
2008-01-14 22:16 ` [PATCH] [14/31] CPA: Use page granuality TLB flushing " Andi Kleen
2008-01-14 22:16 ` [PATCH] [15/31] CPA: Don't flush the caches when the CPU supports self-snoop Andi Kleen
2008-01-14 22:16 ` [PATCH] [16/31] CPA: Use wbinvd() macro instead of inline assembly in 64bit c_p_a() Andi Kleen
2008-01-14 22:16 ` [PATCH] [17/31] CPA: Reorder TLB / cache flushes to follow Intel recommendation Andi Kleen
2008-01-14 22:16 ` [PATCH] [18/31] CPA: Make change_page_attr() more robust against use of PAT bits Andi Kleen
2008-01-14 22:16 ` [PATCH] [19/31] CPA: Limit cache flushing to pages that really change caching Andi Kleen
2008-01-15 8:46 ` Jan Beulich
2008-01-14 22:16 ` [PATCH] [20/31] CPA: Fix inaccurate comments in 64bit change_page_attr() Andi Kleen
2008-01-14 22:16 ` [PATCH] [21/31] CPA: Dump pagetable when inconsistency is detected Andi Kleen
2008-01-14 22:16 ` [PATCH] [22/31] CPA: Only queue actually unused page table pages for freeing Andi Kleen
2008-01-14 22:16 ` [PATCH] [23/31] CPA: Remove unnecessary masking of address Andi Kleen
2008-01-14 22:16 ` [PATCH] [24/31] CPA: Only unmap kernel init pages in text mapping when CONFIG_DEBUG_RODATA is set Andi Kleen
2008-01-14 22:16 ` [PATCH] [25/31] CPA: Always do full TLB flush when splitting large pages Andi Kleen
2008-01-14 22:16 ` [PATCH] [26/31] CPA: Fix reference counting when changing already changed pages Andi Kleen
2008-01-15 9:05 ` Jan Beulich
2008-01-15 10:04 ` Andi Kleen
2008-01-15 12:00 ` Jan Beulich
2008-01-14 22:17 ` [PATCH] [27/31] CPA: Change comments of external interfaces to kerneldoc format Andi Kleen
2008-01-14 22:50 ` Randy Dunlap
2008-01-15 0:49 ` Andi Kleen
2008-01-14 22:17 ` [PATCH] [28/31] CPA: Make kernel_text test match boot mapping initialization Andi Kleen
2008-01-14 22:17 ` [PATCH] [29/31] CPA: Add a BUG_ON checking for someone setting the kernel text NX Andi Kleen
2008-01-14 22:17 ` [PATCH] [30/31] Remove set_kernel_exec Andi Kleen
2008-01-14 22:17 ` [PATCH] [31/31] Clean up pte_exec Andi Kleen
2008-01-15 9:11 ` [PATCH] [0/31] Great change_page_attr patch series v2 Jan Beulich
2008-01-15 10:06 ` Andi Kleen
2008-01-15 11:55 ` Jan Beulich
2008-01-15 12:43 ` Andi Kleen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080114221644.816F314F83@wotan.suse.de \
--to=ak@suse.de \
--cc=jbeulich@novell.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®