* [Patch v4 08/12] memory-hotplug: remove memmap of sparse-vmemmap
From: Wen Congyang @ 2012-11-27 10:00 UTC (permalink / raw)
To: x86, linux-mm, linux-kernel, linuxppc-dev, linux-acpi, linux-s390,
linux-sh, linux-ia64, cmetcalf, sparclinux
Cc: Len Brown, Wen Congyang, Jianguo Wu, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, Christoph Lameter,
Andrew Morton, Jiang Liu
In-Reply-To: <1354010422-19648-1-git-send-email-wency@cn.fujitsu.com>
From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
All pages of virtual mapping in removed memory cannot be freed, since some pages
used as PGD/PUD includes not only removed memory but also other memory. So the
patch checks whether page can be freed or not.
How to check whether page can be freed or not?
1. When removing memory, the page structs of the revmoved memory are filled
with 0FD.
2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
In this case, the page used as PT/PMD can be freed.
Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
ppc, s390, and sparc.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <liuj97@gmail.com>
CC: Len Brown <len.brown@intel.com>
CC: Christoph Lameter <cl@linux.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Signed-off-by: Jianguo Wu <wujianguo@huawei.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
arch/ia64/mm/discontig.c | 8 ++++
arch/powerpc/mm/init_64.c | 8 ++++
arch/s390/mm/vmem.c | 8 ++++
arch/sparc/mm/init_64.c | 8 ++++
arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mm.h | 2 +
mm/memory_hotplug.c | 17 +------
mm/sparse.c | 19 ++++----
8 files changed, 165 insertions(+), 24 deletions(-)
diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
index 33943db..0d23b69 100644
--- a/arch/ia64/mm/discontig.c
+++ b/arch/ia64/mm/discontig.c
@@ -823,6 +823,14 @@ int __meminit vmemmap_populate(struct page *start_page,
return vmemmap_populate_basepages(start_page, size, node);
}
+void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
+{
+}
+
+void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
+{
+}
+
void register_page_bootmem_memmap(unsigned long section_nr,
struct page *start_page, unsigned long size)
{
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index 6466440..df7d155 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -298,6 +298,14 @@ int __meminit vmemmap_populate(struct page *start_page,
return 0;
}
+void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
+{
+}
+
+void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
+{
+}
+
void register_page_bootmem_memmap(unsigned long section_nr,
struct page *start_page, unsigned long size)
{
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index 4f4803a..ab69c34 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -236,6 +236,14 @@ out:
return ret;
}
+void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
+{
+}
+
+void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
+{
+}
+
void register_page_bootmem_memmap(unsigned long section_nr,
struct page *start_page, unsigned long size)
{
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 75a984b..546855d 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2232,6 +2232,14 @@ void __meminit vmemmap_populate_print_last(void)
}
}
+void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
+{
+}
+
+void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
+{
+}
+
void register_page_bootmem_memmap(unsigned long section_nr,
struct page *start_page, unsigned long size)
{
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 795dae3..e85626d 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -998,6 +998,125 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
return 0;
}
+#define PAGE_INUSE 0xFD
+
+unsigned long find_and_clear_pte_page(unsigned long addr, unsigned long end,
+ struct page **pp, int *page_size)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte = NULL;
+ void *page_addr;
+ unsigned long next;
+
+ *pp = NULL;
+
+ pgd = pgd_offset_k(addr);
+ if (pgd_none(*pgd))
+ return pgd_addr_end(addr, end);
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud))
+ return pud_addr_end(addr, end);
+
+ if (!cpu_has_pse) {
+ next = (addr + PAGE_SIZE) & PAGE_MASK;
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ return next;
+
+ pte = pte_offset_kernel(pmd, addr);
+ if (pte_none(*pte))
+ return next;
+
+ *page_size = PAGE_SIZE;
+ *pp = pte_page(*pte);
+ } else {
+ next = pmd_addr_end(addr, end);
+
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ return next;
+
+ *page_size = PMD_SIZE;
+ *pp = pmd_page(*pmd);
+ }
+
+ /*
+ * Removed page structs are filled with 0xFD.
+ */
+ memset((void *)addr, PAGE_INUSE, next - addr);
+
+ page_addr = page_address(*pp);
+
+ /*
+ * Check the page is filled with 0xFD or not.
+ * memchr_inv() returns the address. In this case, we cannot
+ * clear PTE/PUD entry, since the page is used by other.
+ * So we cannot also free the page.
+ *
+ * memchr_inv() returns NULL. In this case, we can clear
+ * PTE/PUD entry, since the page is not used by other.
+ * So we can also free the page.
+ */
+ if (memchr_inv(page_addr, PAGE_INUSE, *page_size)) {
+ *pp = NULL;
+ return next;
+ }
+
+ if (!cpu_has_pse)
+ pte_clear(&init_mm, addr, pte);
+ else
+ pmd_clear(pmd);
+
+ return next;
+}
+
+void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
+{
+ unsigned long addr = (unsigned long)memmap;
+ unsigned long end = (unsigned long)(memmap + nr_pages);
+ unsigned long next;
+ struct page *page;
+ int page_size;
+
+ for (; addr < end; addr = next) {
+ page = NULL;
+ page_size = 0;
+ next = find_and_clear_pte_page(addr, end, &page, &page_size);
+ if (!page)
+ continue;
+
+ free_pages((unsigned long)page_address(page),
+ get_order(page_size));
+ __flush_tlb_one(addr);
+ }
+}
+
+void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
+{
+ unsigned long addr = (unsigned long)memmap;
+ unsigned long end = (unsigned long)(memmap + nr_pages);
+ unsigned long next;
+ struct page *page;
+ int page_size;
+ unsigned long magic;
+
+ for (; addr < end; addr = next) {
+ page = NULL;
+ page_size = 0;
+ next = find_and_clear_pte_page(addr, end, &page, &page_size);
+ if (!page)
+ continue;
+
+ magic = (unsigned long) page->lru.next;
+ if (magic == SECTION_INFO)
+ put_page_bootmem(page);
+ flush_tlb_kernel_range(addr, end);
+ }
+}
+
void register_page_bootmem_memmap(unsigned long section_nr,
struct page *start_page, unsigned long size)
{
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5657670..94d5ccd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1642,6 +1642,8 @@ int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
void vmemmap_populate_print_last(void);
void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
unsigned long size);
+void vmemmap_kfree(struct page *memmpa, unsigned long nr_pages);
+void vmemmap_free_bootmem(struct page *memmpa, unsigned long nr_pages);
enum mf_flags {
MF_COUNT_INCREASED = 1 << 0,
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index ccc11b6..7797e91 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -301,19 +301,6 @@ static int __meminit __add_section(int nid, struct zone *zone,
return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
}
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-static int __remove_section(struct zone *zone, struct mem_section *ms)
-{
- int ret = -EINVAL;
-
- if (!valid_section(ms))
- return ret;
-
- ret = unregister_memory_section(ms);
-
- return ret;
-}
-#else
static int __remove_section(struct zone *zone, struct mem_section *ms)
{
unsigned long flags;
@@ -330,9 +317,9 @@ static int __remove_section(struct zone *zone, struct mem_section *ms)
pgdat_resize_lock(pgdat, &flags);
sparse_remove_one_section(zone, ms);
pgdat_resize_unlock(pgdat, &flags);
- return 0;
+
+ return ret;
}
-#endif
/*
* Reasonably generic function for adding memory. It is
diff --git a/mm/sparse.c b/mm/sparse.c
index fac95f2..c723bc2 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -613,12 +613,13 @@ static inline struct page *kmalloc_section_memmap(unsigned long pnum, int nid,
/* This will make the necessary allocations eventually. */
return sparse_mem_map_populate(pnum, nid);
}
-static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
+static void __kfree_section_memmap(struct page *page, unsigned long nr_pages)
{
- return; /* XXX: Not implemented yet */
+ vmemmap_kfree(page, nr_pages);
}
-static void free_map_bootmem(struct page *page, unsigned long nr_pages)
+static void free_map_bootmem(struct page *page)
{
+ vmemmap_free_bootmem(page, PAGES_PER_SECTION);
}
#else
static struct page *__kmalloc_section_memmap(unsigned long nr_pages)
@@ -658,10 +659,14 @@ static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
get_order(sizeof(struct page) * nr_pages));
}
-static void free_map_bootmem(struct page *page, unsigned long nr_pages)
+static void free_map_bootmem(struct page *page)
{
unsigned long maps_section_nr, removing_section_nr, i;
unsigned long magic;
+ unsigned long nr_pages;
+
+ nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
+ >> PAGE_SHIFT;
for (i = 0; i < nr_pages; i++, page++) {
magic = (unsigned long) page->lru.next;
@@ -688,7 +693,6 @@ static void free_map_bootmem(struct page *page, unsigned long nr_pages)
static void free_section_usemap(struct page *memmap, unsigned long *usemap)
{
struct page *usemap_page;
- unsigned long nr_pages;
if (!usemap)
return;
@@ -713,10 +717,7 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
struct page *memmap_page;
memmap_page = virt_to_page(memmap);
- nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
- >> PAGE_SHIFT;
-
- free_map_bootmem(memmap_page, nr_pages);
+ free_map_bootmem(memmap_page);
}
}
--
1.8.0
^ permalink raw reply related
* [Patch v4 02/12] memory-hotplug: check whether all memory blocks are offlined or not when removing memory
From: Wen Congyang @ 2012-11-27 10:00 UTC (permalink / raw)
To: x86, linux-mm, linux-kernel, linuxppc-dev, linux-acpi, linux-s390,
linux-sh, linux-ia64, cmetcalf, sparclinux
Cc: Len Brown, Wen Congyang, Jianguo Wu, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, Christoph Lameter,
Andrew Morton, Jiang Liu
In-Reply-To: <1354010422-19648-1-git-send-email-wency@cn.fujitsu.com>
From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
We remove the memory like this:
1. lock memory hotplug
2. offline a memory block
3. unlock memory hotplug
4. repeat 1-3 to offline all memory blocks
5. lock memory hotplug
6. remove memory(TODO)
7. unlock memory hotplug
All memory blocks must be offlined before removing memory. But we don't hold
the lock in the whole operation. So we should check whether all memory blocks
are offlined before step6. Otherwise, kernel maybe panicked.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <liuj97@gmail.com>
CC: Len Brown <len.brown@intel.com>
CC: Christoph Lameter <cl@linux.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
---
drivers/base/memory.c | 6 ++++++
include/linux/memory_hotplug.h | 1 +
mm/memory_hotplug.c | 47 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 86c8821..badb025 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -675,6 +675,12 @@ int offline_memory_block(struct memory_block *mem)
return ret;
}
+/* return true if the memory block is offlined, otherwise, return false */
+bool is_memblock_offlined(struct memory_block *mem)
+{
+ return mem->state == MEM_OFFLINE;
+}
+
/*
* Initialize the sysfs support for memory devices...
*/
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 95573ec..38675e9 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -236,6 +236,7 @@ extern int add_memory(int nid, u64 start, u64 size);
extern int arch_add_memory(int nid, u64 start, u64 size);
extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages);
extern int offline_memory_block(struct memory_block *mem);
+extern bool is_memblock_offlined(struct memory_block *mem);
extern int remove_memory(u64 start, u64 size);
extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
int nr_pages);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index b825dbc..b6d1101 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1054,6 +1054,53 @@ repeat:
goto repeat;
}
+ lock_memory_hotplug();
+
+ /*
+ * we have offlined all memory blocks like this:
+ * 1. lock memory hotplug
+ * 2. offline a memory block
+ * 3. unlock memory hotplug
+ *
+ * repeat step1-3 to offline the memory block. All memory blocks
+ * must be offlined before removing memory. But we don't hold the
+ * lock in the whole operation. So we should check whether all
+ * memory blocks are offlined.
+ */
+
+ for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+ section_nr = pfn_to_section_nr(pfn);
+ if (!present_section_nr(section_nr))
+ continue;
+
+ section = __nr_to_section(section_nr);
+ /* same memblock? */
+ if (mem)
+ if ((section_nr >= mem->start_section_nr) &&
+ (section_nr <= mem->end_section_nr))
+ continue;
+
+ mem = find_memory_block_hinted(section, mem);
+ if (!mem)
+ continue;
+
+ ret = is_memblock_offlined(mem);
+ if (!ret) {
+ pr_warn("removing memory fails, because memory "
+ "[%#010llx-%#010llx] is onlined\n",
+ PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)),
+ PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1)) - 1);
+
+ kobject_put(&mem->dev.kobj);
+ unlock_memory_hotplug();
+ return ret;
+ }
+ }
+
+ if (mem)
+ kobject_put(&mem->dev.kobj);
+ unlock_memory_hotplug();
+
return 0;
}
#else
--
1.8.0
^ permalink raw reply related
* [Patch v4 07/12] memory-hotplug: implement register_page_bootmem_info_section of sparse-vmemmap
From: Wen Congyang @ 2012-11-27 10:00 UTC (permalink / raw)
To: x86, linux-mm, linux-kernel, linuxppc-dev, linux-acpi, linux-s390,
linux-sh, linux-ia64, cmetcalf, sparclinux
Cc: Len Brown, Wen Congyang, Jianguo Wu, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, Christoph Lameter,
Andrew Morton, Jiang Liu
In-Reply-To: <1354010422-19648-1-git-send-email-wency@cn.fujitsu.com>
From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
For removing memmap region of sparse-vmemmap which is allocated bootmem,
memmap region of sparse-vmemmap needs to be registered by get_page_bootmem().
So the patch searches pages of virtual mapping and registers the pages by
get_page_bootmem().
Note: register_page_bootmem_memmap() is not implemented for ia64, ppc, s390,
and sparc.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <liuj97@gmail.com>
CC: Len Brown <len.brown@intel.com>
CC: Christoph Lameter <cl@linux.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
---
arch/ia64/mm/discontig.c | 6 +++++
arch/powerpc/mm/init_64.c | 6 +++++
arch/s390/mm/vmem.c | 6 +++++
arch/sparc/mm/init_64.c | 6 +++++
arch/x86/mm/init_64.c | 52 ++++++++++++++++++++++++++++++++++++++++++
include/linux/memory_hotplug.h | 11 ++-------
include/linux/mm.h | 3 ++-
mm/memory_hotplug.c | 33 +++++++++++++++++++++++----
8 files changed, 109 insertions(+), 14 deletions(-)
diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
index c641333..33943db 100644
--- a/arch/ia64/mm/discontig.c
+++ b/arch/ia64/mm/discontig.c
@@ -822,4 +822,10 @@ int __meminit vmemmap_populate(struct page *start_page,
{
return vmemmap_populate_basepages(start_page, size, node);
}
+
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+ /* TODO */
+}
#endif
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index 95a4529..6466440 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -297,5 +297,11 @@ int __meminit vmemmap_populate(struct page *start_page,
return 0;
}
+
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+ /* TODO */
+}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index 387c7c6..4f4803a 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -236,6 +236,12 @@ out:
return ret;
}
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+ /* TODO */
+}
+
/*
* Add memory segment to the segment list if it doesn't overlap with
* an already present segment.
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 9e28a11..75a984b 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2231,6 +2231,12 @@ void __meminit vmemmap_populate_print_last(void)
node_start = 0;
}
}
+
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+ /* TODO */
+}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
static void prot_init_common(unsigned long page_none,
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 5675335..795dae3 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -998,6 +998,58 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
return 0;
}
+void register_page_bootmem_memmap(unsigned long section_nr,
+ struct page *start_page, unsigned long size)
+{
+ unsigned long addr = (unsigned long)start_page;
+ unsigned long end = (unsigned long)(start_page + size);
+ unsigned long next;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+
+ for (; addr < end; addr = next) {
+ pte_t *pte = NULL;
+
+ pgd = pgd_offset_k(addr);
+ if (pgd_none(*pgd)) {
+ next = (addr + PAGE_SIZE) & PAGE_MASK;
+ continue;
+ }
+ get_page_bootmem(section_nr, pgd_page(*pgd), MIX_SECTION_INFO);
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud)) {
+ next = (addr + PAGE_SIZE) & PAGE_MASK;
+ continue;
+ }
+ get_page_bootmem(section_nr, pud_page(*pud), MIX_SECTION_INFO);
+
+ if (!cpu_has_pse) {
+ next = (addr + PAGE_SIZE) & PAGE_MASK;
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ continue;
+ get_page_bootmem(section_nr, pmd_page(*pmd),
+ MIX_SECTION_INFO);
+
+ pte = pte_offset_kernel(pmd, addr);
+ if (pte_none(*pte))
+ continue;
+ get_page_bootmem(section_nr, pte_page(*pte),
+ SECTION_INFO);
+ } else {
+ next = pmd_addr_end(addr, end);
+
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ continue;
+ get_page_bootmem(section_nr, pmd_page(*pmd),
+ SECTION_INFO);
+ }
+ }
+}
+
void __meminit vmemmap_populate_print_last(void)
{
if (p_start) {
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 191b2d9..d4c4402 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -163,17 +163,10 @@ static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat)
#endif /* CONFIG_NUMA */
#endif /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-static inline void register_page_bootmem_info_node(struct pglist_data *pgdat)
-{
-}
-static inline void put_page_bootmem(struct page *page)
-{
-}
-#else
extern void register_page_bootmem_info_node(struct pglist_data *pgdat);
extern void put_page_bootmem(struct page *page);
-#endif
+extern void get_page_bootmem(unsigned long ingo, struct page *page,
+ unsigned long type);
/*
* Lock for memory hotplug guarantees 1) all callbacks for memory hotplug
diff --git a/include/linux/mm.h b/include/linux/mm.h
index bcaab4e..5657670 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1640,7 +1640,8 @@ int vmemmap_populate_basepages(struct page *start_page,
unsigned long pages, int node);
int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
void vmemmap_populate_print_last(void);
-
+void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
+ unsigned long size);
enum mf_flags {
MF_COUNT_INCREASED = 1 << 0,
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 171610d..ccc11b6 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -91,9 +91,8 @@ static void release_memory_resource(struct resource *res)
}
#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
-#ifndef CONFIG_SPARSEMEM_VMEMMAP
-static void get_page_bootmem(unsigned long info, struct page *page,
- unsigned long type)
+void get_page_bootmem(unsigned long info, struct page *page,
+ unsigned long type)
{
page->lru.next = (struct list_head *) type;
SetPagePrivate(page);
@@ -120,6 +119,7 @@ void __ref put_page_bootmem(struct page *page)
}
+#ifndef CONFIG_SPARSEMEM_VMEMMAP
static void register_page_bootmem_info_section(unsigned long start_pfn)
{
unsigned long *usemap, mapsize, section_nr, i;
@@ -153,6 +153,32 @@ static void register_page_bootmem_info_section(unsigned long start_pfn)
get_page_bootmem(section_nr, page, MIX_SECTION_INFO);
}
+#else
+static void register_page_bootmem_info_section(unsigned long start_pfn)
+{
+ unsigned long *usemap, mapsize, section_nr, i;
+ struct mem_section *ms;
+ struct page *page, *memmap;
+
+ if (!pfn_valid(start_pfn))
+ return;
+
+ section_nr = pfn_to_section_nr(start_pfn);
+ ms = __nr_to_section(section_nr);
+
+ memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr);
+
+ register_page_bootmem_memmap(section_nr, memmap, PAGES_PER_SECTION);
+
+ usemap = __nr_to_section(section_nr)->pageblock_flags;
+ page = virt_to_page(usemap);
+
+ mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT;
+
+ for (i = 0; i < mapsize; i++, page++)
+ get_page_bootmem(section_nr, page, MIX_SECTION_INFO);
+}
+#endif
void register_page_bootmem_info_node(struct pglist_data *pgdat)
{
@@ -195,7 +221,6 @@ void register_page_bootmem_info_node(struct pglist_data *pgdat)
register_page_bootmem_info_section(pfn);
}
}
-#endif /* !CONFIG_SPARSEMEM_VMEMMAP */
static void grow_zone_span(struct zone *zone, unsigned long start_pfn,
unsigned long end_pfn)
--
1.8.0
^ permalink raw reply related
* [Patch v4 01/12] memory-hotplug: try to offline the memory twice to avoid dependence
From: Wen Congyang @ 2012-11-27 10:00 UTC (permalink / raw)
To: x86, linux-mm, linux-kernel, linuxppc-dev, linux-acpi, linux-s390,
linux-sh, linux-ia64, cmetcalf, sparclinux
Cc: Len Brown, Wen Congyang, Jianguo Wu, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, Christoph Lameter,
Andrew Morton, Jiang Liu
In-Reply-To: <1354010422-19648-1-git-send-email-wency@cn.fujitsu.com>
memory can't be offlined when CONFIG_MEMCG is selected.
For example: there is a memory device on node 1. The address range
is [1G, 1.5G). You will find 4 new directories memory8, memory9, memory10,
and memory11 under the directory /sys/devices/system/memory/.
If CONFIG_MEMCG is selected, we will allocate memory to store page cgroup
when we online pages. When we online memory8, the memory stored page cgroup
is not provided by this memory device. But when we online memory9, the memory
stored page cgroup may be provided by memory8. So we can't offline memory8
now. We should offline the memory in the reversed order.
When the memory device is hotremoved, we will auto offline memory provided
by this memory device. But we don't know which memory is onlined first, so
offlining memory may fail. In such case, iterate twice to offline the memory.
1st iterate: offline every non primary memory block.
2nd iterate: offline primary (i.e. first added) memory block.
This idea is suggested by KOSAKI Motohiro.
CC: David Rientjes <rientjes@google.com>
CC: Jiang Liu <liuj97@gmail.com>
CC: Len Brown <len.brown@intel.com>
CC: Christoph Lameter <cl@linux.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
CC: Andrew Morton <akpm@linux-foundation.org>
CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
CC: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
mm/memory_hotplug.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index e4eeaca..b825dbc 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1012,10 +1012,13 @@ int remove_memory(u64 start, u64 size)
unsigned long start_pfn, end_pfn;
unsigned long pfn, section_nr;
int ret;
+ int return_on_error = 0;
+ int retry = 0;
start_pfn = PFN_DOWN(start);
end_pfn = start_pfn + PFN_DOWN(size);
+repeat:
for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
section_nr = pfn_to_section_nr(pfn);
if (!present_section_nr(section_nr))
@@ -1034,14 +1037,23 @@ int remove_memory(u64 start, u64 size)
ret = offline_memory_block(mem);
if (ret) {
- kobject_put(&mem->dev.kobj);
- return ret;
+ if (return_on_error) {
+ kobject_put(&mem->dev.kobj);
+ return ret;
+ } else {
+ retry = 1;
+ }
}
}
if (mem)
kobject_put(&mem->dev.kobj);
+ if (retry) {
+ return_on_error = 1;
+ goto repeat;
+ }
+
return 0;
}
#else
--
1.8.0
^ permalink raw reply related
* Re: [PATCH v3 08/12] memory-hotplug: remove memmap of sparse-vmemmap
From: Jianguo Wu @ 2012-11-27 7:14 UTC (permalink / raw)
To: Wen Congyang
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, linux-mm, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50B4625F.1050307@cn.fujitsu.com>
On 2012/11/27 14:49, Wen Congyang wrote:
> At 11/27/2012 01:47 PM, Jianguo Wu Wrote:
>> On 2012/11/1 17:44, Wen Congyang wrote:
>>
>>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>
>>> All pages of virtual mapping in removed memory cannot be freed, since some pages
>>> used as PGD/PUD includes not only removed memory but also other memory. So the
>>> patch checks whether page can be freed or not.
>>>
>>> How to check whether page can be freed or not?
>>> 1. When removing memory, the page structs of the revmoved memory are filled
>>> with 0FD.
>>> 2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
>>> In this case, the page used as PT/PMD can be freed.
>>>
>>> Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
>>> into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
>>>
>>> Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
>>> ppc, s390, and sparc.
>>>
>>> CC: David Rientjes <rientjes@google.com>
>>> CC: Jiang Liu <liuj97@gmail.com>
>>> CC: Len Brown <len.brown@intel.com>
>>> CC: Christoph Lameter <cl@linux.com>
>>> Cc: Minchan Kim <minchan.kim@gmail.com>
>>> CC: Andrew Morton <akpm@linux-foundation.org>
>>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>>> CC: Wen Congyang <wency@cn.fujitsu.com>
>>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>> ---
>>> arch/ia64/mm/discontig.c | 8 ++++
>>> arch/powerpc/mm/init_64.c | 8 ++++
>>> arch/s390/mm/vmem.c | 8 ++++
>>> arch/sparc/mm/init_64.c | 8 ++++
>>> arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
>>> include/linux/mm.h | 2 +
>>> mm/memory_hotplug.c | 17 +------
>>> mm/sparse.c | 5 +-
>>> 8 files changed, 158 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
>>> index 33943db..0d23b69 100644
>>> --- a/arch/ia64/mm/discontig.c
>>> +++ b/arch/ia64/mm/discontig.c
>>> @@ -823,6 +823,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>>> return vmemmap_populate_basepages(start_page, size, node);
>>> }
>>>
>>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> void register_page_bootmem_memmap(unsigned long section_nr,
>>> struct page *start_page, unsigned long size)
>>> {
>>> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
>>> index 6466440..df7d155 100644
>>> --- a/arch/powerpc/mm/init_64.c
>>> +++ b/arch/powerpc/mm/init_64.c
>>> @@ -298,6 +298,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>>> return 0;
>>> }
>>>
>>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> void register_page_bootmem_memmap(unsigned long section_nr,
>>> struct page *start_page, unsigned long size)
>>> {
>>> diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
>>> index 4f4803a..ab69c34 100644
>>> --- a/arch/s390/mm/vmem.c
>>> +++ b/arch/s390/mm/vmem.c
>>> @@ -236,6 +236,14 @@ out:
>>> return ret;
>>> }
>>>
>>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> void register_page_bootmem_memmap(unsigned long section_nr,
>>> struct page *start_page, unsigned long size)
>>> {
>>> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
>>> index 75a984b..546855d 100644
>>> --- a/arch/sparc/mm/init_64.c
>>> +++ b/arch/sparc/mm/init_64.c
>>> @@ -2232,6 +2232,14 @@ void __meminit vmemmap_populate_print_last(void)
>>> }
>>> }
>>>
>>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> +}
>>> +
>>> void register_page_bootmem_memmap(unsigned long section_nr,
>>> struct page *start_page, unsigned long size)
>>> {
>>> diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
>>> index 795dae3..e85626d 100644
>>> --- a/arch/x86/mm/init_64.c
>>> +++ b/arch/x86/mm/init_64.c
>>> @@ -998,6 +998,125 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
>>> return 0;
>>> }
>>>
>>> +#define PAGE_INUSE 0xFD
>>> +
>>> +unsigned long find_and_clear_pte_page(unsigned long addr, unsigned long end,
>>> + struct page **pp, int *page_size)
>>> +{
>>> + pgd_t *pgd;
>>> + pud_t *pud;
>>> + pmd_t *pmd;
>>> + pte_t *pte = NULL;
>>> + void *page_addr;
>>> + unsigned long next;
>>> +
>>> + *pp = NULL;
>>> +
>>> + pgd = pgd_offset_k(addr);
>>> + if (pgd_none(*pgd))
>>> + return pgd_addr_end(addr, end);
>>> +
>>> + pud = pud_offset(pgd, addr);
>>> + if (pud_none(*pud))
>>> + return pud_addr_end(addr, end);
>>> +
>>> + if (!cpu_has_pse) {
>>> + next = (addr + PAGE_SIZE) & PAGE_MASK;
>>> + pmd = pmd_offset(pud, addr);
>>> + if (pmd_none(*pmd))
>>> + return next;
>>> +
>>> + pte = pte_offset_kernel(pmd, addr);
>>> + if (pte_none(*pte))
>>> + return next;
>>> +
>>> + *page_size = PAGE_SIZE;
>>> + *pp = pte_page(*pte);
>>> + } else {
>>> + next = pmd_addr_end(addr, end);
>>> +
>>> + pmd = pmd_offset(pud, addr);
>>> + if (pmd_none(*pmd))
>>> + return next;
>>> +
>>> + *page_size = PMD_SIZE;
>>> + *pp = pmd_page(*pmd);
>>> + }
>>> +
>>> + /*
>>> + * Removed page structs are filled with 0xFD.
>>> + */
>>> + memset((void *)addr, PAGE_INUSE, next - addr);
>>> +
>>> + page_addr = page_address(*pp);
>>> +
>>> + /*
>>> + * Check the page is filled with 0xFD or not.
>>> + * memchr_inv() returns the address. In this case, we cannot
>>> + * clear PTE/PUD entry, since the page is used by other.
>>> + * So we cannot also free the page.
>>> + *
>>> + * memchr_inv() returns NULL. In this case, we can clear
>>> + * PTE/PUD entry, since the page is not used by other.
>>> + * So we can also free the page.
>>> + */
>>> + if (memchr_inv(page_addr, PAGE_INUSE, *page_size)) {
>>> + *pp = NULL;
>>> + return next;
>>> + }
>>> +
>>> + if (!cpu_has_pse)
>>> + pte_clear(&init_mm, addr, pte);
>>> + else
>>> + pmd_clear(pmd);
>>> +
>>> + return next;
>>> +}
>>> +
>>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> + unsigned long addr = (unsigned long)memmap;
>>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>>> + unsigned long next;
>>> + struct page *page;
>>> + int page_size;
>>> +
>>> + for (; addr < end; addr = next) {
>>> + page = NULL;
>>> + page_size = 0;
>>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>>> + if (!page)
>>> + continue;
>>> +
>>> + free_pages((unsigned long)page_address(page),
>>> + get_order(page_size));
>>> + __flush_tlb_one(addr);
>>> + }
>>> +}
>>> +
>>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>>> +{
>>> + unsigned long addr = (unsigned long)memmap;
>>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>>> + unsigned long next;
>>> + struct page *page;
>>> + int page_size;
>>> + unsigned long magic;
>>> +
>>> + for (; addr < end; addr = next) {
>>> + page = NULL;
>>> + page_size = 0;
>>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>>> + if (!page)
>>> + continue;
>>> +
>>> + magic = (unsigned long) page->lru.next;
>>> + if (magic == SECTION_INFO)
>>> + put_page_bootmem(page);
>>> + flush_tlb_kernel_range(addr, end);
>>> + }
>>> +}
>>> +
>>> void register_page_bootmem_memmap(unsigned long section_nr,
>>> struct page *start_page, unsigned long size)
>>> {
>>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>>> index 8e5a56f..42b8723 100644
>>> --- a/include/linux/mm.h
>>> +++ b/include/linux/mm.h
>>> @@ -1642,6 +1642,8 @@ int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
>>> void vmemmap_populate_print_last(void);
>>> void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
>>> unsigned long size);
>>> +void vmemmap_kfree(struct page *memmpa, unsigned long nr_pages);
>>> +void vmemmap_free_bootmem(struct page *memmpa, unsigned long nr_pages);
>>>
>>> enum mf_flags {
>>> MF_COUNT_INCREASED = 1 << 0,
>>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>>> index db9806c..03153cf 100644
>>> --- a/mm/memory_hotplug.c
>>> +++ b/mm/memory_hotplug.c
>>> @@ -312,19 +312,6 @@ static int __meminit __add_section(int nid, struct zone *zone,
>>> return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
>>> }
>>>
>>> -#ifdef CONFIG_SPARSEMEM_VMEMMAP
>>> -static int __remove_section(struct zone *zone, struct mem_section *ms)
>>> -{
>>> - int ret = -EINVAL;
>>> -
>>> - if (!valid_section(ms))
>>> - return ret;
>>> -
>>> - ret = unregister_memory_section(ms);
>>> -
>>> - return ret;
>>> -}
>>> -#else
>>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>>> {
>>> unsigned long flags;
>>> @@ -341,9 +328,9 @@ static int __remove_section(struct zone *zone, struct mem_section *ms)
>>> pgdat_resize_lock(pgdat, &flags);
>>> sparse_remove_one_section(zone, ms);
>>> pgdat_resize_unlock(pgdat, &flags);
>>> - return 0;
>>> +
>>> + return ret;
>>> }
>>> -#endif
>>>
>>> /*
>>> * Reasonably generic function for adding memory. It is
>>> diff --git a/mm/sparse.c b/mm/sparse.c
>>> index fac95f2..ab9d755 100644
>>> --- a/mm/sparse.c
>>> +++ b/mm/sparse.c
>>> @@ -613,12 +613,13 @@ static inline struct page *kmalloc_section_memmap(unsigned long pnum, int nid,
>>> /* This will make the necessary allocations eventually. */
>>> return sparse_mem_map_populate(pnum, nid);
>>> }
>>> -static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
>>> +static void __kfree_section_memmap(struct page *page, unsigned long nr_pages)
>>> {
>>> - return; /* XXX: Not implemented yet */
>>> + vmemmap_kfree(page, nr_pages);
>>> }
>>> static void free_map_bootmem(struct page *page, unsigned long nr_pages)
>>> {
>>> + vmemmap_free_bootmem(page, nr_pages);
>>> }
>>
>> Hi Congyang,
>> For vmemmap, nr_pages should be PAGES_PER_SECTION for free_map_bootmem(),
>> which is passed by free_section_usemap(), right?
>> But now, nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page)) >> PAGE_SHIFT.
>>
>> Signed-off-by: Jianguo Wu <wujianguo@huawei.com>
>> ---
>> mm/sparse.c | 4 ++++
>> 1 files changed, 4 insertions(+), 0 deletions(-)
>>
>> diff --git a/mm/sparse.c b/mm/sparse.c
>> index fac95f2..31e5282 100644
>> --- a/mm/sparse.c
>> +++ b/mm/sparse.c
>> @@ -713,8 +713,12 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
>> struct page *memmap_page;
>> memmap_page = virt_to_page(memmap);
>>
>> +#ifdef CONFIG_SPARSEMEM_VMEMMAP
>> + nr_pages = PAGES_PER_SECTION;
>> +#else
>> nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
>> >> PAGE_SHIFT;
>> +#endif
>
> Hmm, to avoid using ifdef, I think we can pass PAGE_PER_SECTION to free_map_bootmem(),
> and calculate how many pages is used to store struct page.
>
yes, it is ugly to use ifdef. Or we can remove parameter--nr_pages of free_map_bootmem(),
and calculate nr_pages in free_map_bootmem() directly.
> Thanks
> Wen Congyang
>
>>
>> free_map_bootmem(memmap_page, nr_pages);
>> }
>
>
> .
>
^ permalink raw reply
* Re: [PATCH v3 08/12] memory-hotplug: remove memmap of sparse-vmemmap
From: Wen Congyang @ 2012-11-27 6:49 UTC (permalink / raw)
To: Jianguo Wu
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, linux-mm, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50B45400.20800@huawei.com>
At 11/27/2012 01:47 PM, Jianguo Wu Wrote:
> On 2012/11/1 17:44, Wen Congyang wrote:
>
>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>
>> All pages of virtual mapping in removed memory cannot be freed, since some pages
>> used as PGD/PUD includes not only removed memory but also other memory. So the
>> patch checks whether page can be freed or not.
>>
>> How to check whether page can be freed or not?
>> 1. When removing memory, the page structs of the revmoved memory are filled
>> with 0FD.
>> 2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
>> In this case, the page used as PT/PMD can be freed.
>>
>> Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
>> into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
>>
>> Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
>> ppc, s390, and sparc.
>>
>> CC: David Rientjes <rientjes@google.com>
>> CC: Jiang Liu <liuj97@gmail.com>
>> CC: Len Brown <len.brown@intel.com>
>> CC: Christoph Lameter <cl@linux.com>
>> Cc: Minchan Kim <minchan.kim@gmail.com>
>> CC: Andrew Morton <akpm@linux-foundation.org>
>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>> CC: Wen Congyang <wency@cn.fujitsu.com>
>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>> ---
>> arch/ia64/mm/discontig.c | 8 ++++
>> arch/powerpc/mm/init_64.c | 8 ++++
>> arch/s390/mm/vmem.c | 8 ++++
>> arch/sparc/mm/init_64.c | 8 ++++
>> arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mm.h | 2 +
>> mm/memory_hotplug.c | 17 +------
>> mm/sparse.c | 5 +-
>> 8 files changed, 158 insertions(+), 17 deletions(-)
>>
>> diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
>> index 33943db..0d23b69 100644
>> --- a/arch/ia64/mm/discontig.c
>> +++ b/arch/ia64/mm/discontig.c
>> @@ -823,6 +823,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>> return vmemmap_populate_basepages(start_page, size, node);
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
>> index 6466440..df7d155 100644
>> --- a/arch/powerpc/mm/init_64.c
>> +++ b/arch/powerpc/mm/init_64.c
>> @@ -298,6 +298,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>> return 0;
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
>> index 4f4803a..ab69c34 100644
>> --- a/arch/s390/mm/vmem.c
>> +++ b/arch/s390/mm/vmem.c
>> @@ -236,6 +236,14 @@ out:
>> return ret;
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
>> index 75a984b..546855d 100644
>> --- a/arch/sparc/mm/init_64.c
>> +++ b/arch/sparc/mm/init_64.c
>> @@ -2232,6 +2232,14 @@ void __meminit vmemmap_populate_print_last(void)
>> }
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
>> index 795dae3..e85626d 100644
>> --- a/arch/x86/mm/init_64.c
>> +++ b/arch/x86/mm/init_64.c
>> @@ -998,6 +998,125 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
>> return 0;
>> }
>>
>> +#define PAGE_INUSE 0xFD
>> +
>> +unsigned long find_and_clear_pte_page(unsigned long addr, unsigned long end,
>> + struct page **pp, int *page_size)
>> +{
>> + pgd_t *pgd;
>> + pud_t *pud;
>> + pmd_t *pmd;
>> + pte_t *pte = NULL;
>> + void *page_addr;
>> + unsigned long next;
>> +
>> + *pp = NULL;
>> +
>> + pgd = pgd_offset_k(addr);
>> + if (pgd_none(*pgd))
>> + return pgd_addr_end(addr, end);
>> +
>> + pud = pud_offset(pgd, addr);
>> + if (pud_none(*pud))
>> + return pud_addr_end(addr, end);
>> +
>> + if (!cpu_has_pse) {
>> + next = (addr + PAGE_SIZE) & PAGE_MASK;
>> + pmd = pmd_offset(pud, addr);
>> + if (pmd_none(*pmd))
>> + return next;
>> +
>> + pte = pte_offset_kernel(pmd, addr);
>> + if (pte_none(*pte))
>> + return next;
>> +
>> + *page_size = PAGE_SIZE;
>> + *pp = pte_page(*pte);
>> + } else {
>> + next = pmd_addr_end(addr, end);
>> +
>> + pmd = pmd_offset(pud, addr);
>> + if (pmd_none(*pmd))
>> + return next;
>> +
>> + *page_size = PMD_SIZE;
>> + *pp = pmd_page(*pmd);
>> + }
>> +
>> + /*
>> + * Removed page structs are filled with 0xFD.
>> + */
>> + memset((void *)addr, PAGE_INUSE, next - addr);
>> +
>> + page_addr = page_address(*pp);
>> +
>> + /*
>> + * Check the page is filled with 0xFD or not.
>> + * memchr_inv() returns the address. In this case, we cannot
>> + * clear PTE/PUD entry, since the page is used by other.
>> + * So we cannot also free the page.
>> + *
>> + * memchr_inv() returns NULL. In this case, we can clear
>> + * PTE/PUD entry, since the page is not used by other.
>> + * So we can also free the page.
>> + */
>> + if (memchr_inv(page_addr, PAGE_INUSE, *page_size)) {
>> + *pp = NULL;
>> + return next;
>> + }
>> +
>> + if (!cpu_has_pse)
>> + pte_clear(&init_mm, addr, pte);
>> + else
>> + pmd_clear(pmd);
>> +
>> + return next;
>> +}
>> +
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> + unsigned long addr = (unsigned long)memmap;
>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>> + unsigned long next;
>> + struct page *page;
>> + int page_size;
>> +
>> + for (; addr < end; addr = next) {
>> + page = NULL;
>> + page_size = 0;
>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>> + if (!page)
>> + continue;
>> +
>> + free_pages((unsigned long)page_address(page),
>> + get_order(page_size));
>> + __flush_tlb_one(addr);
>> + }
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> + unsigned long addr = (unsigned long)memmap;
>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>> + unsigned long next;
>> + struct page *page;
>> + int page_size;
>> + unsigned long magic;
>> +
>> + for (; addr < end; addr = next) {
>> + page = NULL;
>> + page_size = 0;
>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>> + if (!page)
>> + continue;
>> +
>> + magic = (unsigned long) page->lru.next;
>> + if (magic == SECTION_INFO)
>> + put_page_bootmem(page);
>> + flush_tlb_kernel_range(addr, end);
>> + }
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 8e5a56f..42b8723 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -1642,6 +1642,8 @@ int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
>> void vmemmap_populate_print_last(void);
>> void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
>> unsigned long size);
>> +void vmemmap_kfree(struct page *memmpa, unsigned long nr_pages);
>> +void vmemmap_free_bootmem(struct page *memmpa, unsigned long nr_pages);
>>
>> enum mf_flags {
>> MF_COUNT_INCREASED = 1 << 0,
>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>> index db9806c..03153cf 100644
>> --- a/mm/memory_hotplug.c
>> +++ b/mm/memory_hotplug.c
>> @@ -312,19 +312,6 @@ static int __meminit __add_section(int nid, struct zone *zone,
>> return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
>> }
>>
>> -#ifdef CONFIG_SPARSEMEM_VMEMMAP
>> -static int __remove_section(struct zone *zone, struct mem_section *ms)
>> -{
>> - int ret = -EINVAL;
>> -
>> - if (!valid_section(ms))
>> - return ret;
>> -
>> - ret = unregister_memory_section(ms);
>> -
>> - return ret;
>> -}
>> -#else
>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>> {
>> unsigned long flags;
>> @@ -341,9 +328,9 @@ static int __remove_section(struct zone *zone, struct mem_section *ms)
>> pgdat_resize_lock(pgdat, &flags);
>> sparse_remove_one_section(zone, ms);
>> pgdat_resize_unlock(pgdat, &flags);
>> - return 0;
>> +
>> + return ret;
>> }
>> -#endif
>>
>> /*
>> * Reasonably generic function for adding memory. It is
>> diff --git a/mm/sparse.c b/mm/sparse.c
>> index fac95f2..ab9d755 100644
>> --- a/mm/sparse.c
>> +++ b/mm/sparse.c
>> @@ -613,12 +613,13 @@ static inline struct page *kmalloc_section_memmap(unsigned long pnum, int nid,
>> /* This will make the necessary allocations eventually. */
>> return sparse_mem_map_populate(pnum, nid);
>> }
>> -static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
>> +static void __kfree_section_memmap(struct page *page, unsigned long nr_pages)
>> {
>> - return; /* XXX: Not implemented yet */
>> + vmemmap_kfree(page, nr_pages);
>> }
>> static void free_map_bootmem(struct page *page, unsigned long nr_pages)
>> {
>> + vmemmap_free_bootmem(page, nr_pages);
>> }
>
> Hi Congyang,
> For vmemmap, nr_pages should be PAGES_PER_SECTION for free_map_bootmem(),
> which is passed by free_section_usemap(), right?
> But now, nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page)) >> PAGE_SHIFT.
>
> Signed-off-by: Jianguo Wu <wujianguo@huawei.com>
> ---
> mm/sparse.c | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/mm/sparse.c b/mm/sparse.c
> index fac95f2..31e5282 100644
> --- a/mm/sparse.c
> +++ b/mm/sparse.c
> @@ -713,8 +713,12 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
> struct page *memmap_page;
> memmap_page = virt_to_page(memmap);
>
> +#ifdef CONFIG_SPARSEMEM_VMEMMAP
> + nr_pages = PAGES_PER_SECTION;
> +#else
> nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
> >> PAGE_SHIFT;
> +#endif
Hmm, to avoid using ifdef, I think we can pass PAGE_PER_SECTION to free_map_bootmem(),
and calculate how many pages is used to store struct page.
Thanks
Wen Congyang
>
> free_map_bootmem(memmap_page, nr_pages);
> }
^ permalink raw reply
* Re: [PATCH v3 08/12] memory-hotplug: remove memmap of sparse-vmemmap
From: Wen Congyang @ 2012-11-27 6:39 UTC (permalink / raw)
To: Jianguo Wu
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, linux-mm, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50B45400.20800@huawei.com>
At 11/27/2012 01:47 PM, Jianguo Wu Wrote:
> On 2012/11/1 17:44, Wen Congyang wrote:
>
>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>
>> All pages of virtual mapping in removed memory cannot be freed, since some pages
>> used as PGD/PUD includes not only removed memory but also other memory. So the
>> patch checks whether page can be freed or not.
>>
>> How to check whether page can be freed or not?
>> 1. When removing memory, the page structs of the revmoved memory are filled
>> with 0FD.
>> 2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
>> In this case, the page used as PT/PMD can be freed.
>>
>> Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
>> into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
>>
>> Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
>> ppc, s390, and sparc.
>>
>> CC: David Rientjes <rientjes@google.com>
>> CC: Jiang Liu <liuj97@gmail.com>
>> CC: Len Brown <len.brown@intel.com>
>> CC: Christoph Lameter <cl@linux.com>
>> Cc: Minchan Kim <minchan.kim@gmail.com>
>> CC: Andrew Morton <akpm@linux-foundation.org>
>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>> CC: Wen Congyang <wency@cn.fujitsu.com>
>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>> ---
>> arch/ia64/mm/discontig.c | 8 ++++
>> arch/powerpc/mm/init_64.c | 8 ++++
>> arch/s390/mm/vmem.c | 8 ++++
>> arch/sparc/mm/init_64.c | 8 ++++
>> arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mm.h | 2 +
>> mm/memory_hotplug.c | 17 +------
>> mm/sparse.c | 5 +-
>> 8 files changed, 158 insertions(+), 17 deletions(-)
>>
>> diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
>> index 33943db..0d23b69 100644
>> --- a/arch/ia64/mm/discontig.c
>> +++ b/arch/ia64/mm/discontig.c
>> @@ -823,6 +823,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>> return vmemmap_populate_basepages(start_page, size, node);
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
>> index 6466440..df7d155 100644
>> --- a/arch/powerpc/mm/init_64.c
>> +++ b/arch/powerpc/mm/init_64.c
>> @@ -298,6 +298,14 @@ int __meminit vmemmap_populate(struct page *start_page,
>> return 0;
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
>> index 4f4803a..ab69c34 100644
>> --- a/arch/s390/mm/vmem.c
>> +++ b/arch/s390/mm/vmem.c
>> @@ -236,6 +236,14 @@ out:
>> return ret;
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
>> index 75a984b..546855d 100644
>> --- a/arch/sparc/mm/init_64.c
>> +++ b/arch/sparc/mm/init_64.c
>> @@ -2232,6 +2232,14 @@ void __meminit vmemmap_populate_print_last(void)
>> }
>> }
>>
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
>> index 795dae3..e85626d 100644
>> --- a/arch/x86/mm/init_64.c
>> +++ b/arch/x86/mm/init_64.c
>> @@ -998,6 +998,125 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
>> return 0;
>> }
>>
>> +#define PAGE_INUSE 0xFD
>> +
>> +unsigned long find_and_clear_pte_page(unsigned long addr, unsigned long end,
>> + struct page **pp, int *page_size)
>> +{
>> + pgd_t *pgd;
>> + pud_t *pud;
>> + pmd_t *pmd;
>> + pte_t *pte = NULL;
>> + void *page_addr;
>> + unsigned long next;
>> +
>> + *pp = NULL;
>> +
>> + pgd = pgd_offset_k(addr);
>> + if (pgd_none(*pgd))
>> + return pgd_addr_end(addr, end);
>> +
>> + pud = pud_offset(pgd, addr);
>> + if (pud_none(*pud))
>> + return pud_addr_end(addr, end);
>> +
>> + if (!cpu_has_pse) {
>> + next = (addr + PAGE_SIZE) & PAGE_MASK;
>> + pmd = pmd_offset(pud, addr);
>> + if (pmd_none(*pmd))
>> + return next;
>> +
>> + pte = pte_offset_kernel(pmd, addr);
>> + if (pte_none(*pte))
>> + return next;
>> +
>> + *page_size = PAGE_SIZE;
>> + *pp = pte_page(*pte);
>> + } else {
>> + next = pmd_addr_end(addr, end);
>> +
>> + pmd = pmd_offset(pud, addr);
>> + if (pmd_none(*pmd))
>> + return next;
>> +
>> + *page_size = PMD_SIZE;
>> + *pp = pmd_page(*pmd);
>> + }
>> +
>> + /*
>> + * Removed page structs are filled with 0xFD.
>> + */
>> + memset((void *)addr, PAGE_INUSE, next - addr);
>> +
>> + page_addr = page_address(*pp);
>> +
>> + /*
>> + * Check the page is filled with 0xFD or not.
>> + * memchr_inv() returns the address. In this case, we cannot
>> + * clear PTE/PUD entry, since the page is used by other.
>> + * So we cannot also free the page.
>> + *
>> + * memchr_inv() returns NULL. In this case, we can clear
>> + * PTE/PUD entry, since the page is not used by other.
>> + * So we can also free the page.
>> + */
>> + if (memchr_inv(page_addr, PAGE_INUSE, *page_size)) {
>> + *pp = NULL;
>> + return next;
>> + }
>> +
>> + if (!cpu_has_pse)
>> + pte_clear(&init_mm, addr, pte);
>> + else
>> + pmd_clear(pmd);
>> +
>> + return next;
>> +}
>> +
>> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
>> +{
>> + unsigned long addr = (unsigned long)memmap;
>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>> + unsigned long next;
>> + struct page *page;
>> + int page_size;
>> +
>> + for (; addr < end; addr = next) {
>> + page = NULL;
>> + page_size = 0;
>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>> + if (!page)
>> + continue;
>> +
>> + free_pages((unsigned long)page_address(page),
>> + get_order(page_size));
>> + __flush_tlb_one(addr);
>> + }
>> +}
>> +
>> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
>> +{
>> + unsigned long addr = (unsigned long)memmap;
>> + unsigned long end = (unsigned long)(memmap + nr_pages);
>> + unsigned long next;
>> + struct page *page;
>> + int page_size;
>> + unsigned long magic;
>> +
>> + for (; addr < end; addr = next) {
>> + page = NULL;
>> + page_size = 0;
>> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
>> + if (!page)
>> + continue;
>> +
>> + magic = (unsigned long) page->lru.next;
>> + if (magic == SECTION_INFO)
>> + put_page_bootmem(page);
>> + flush_tlb_kernel_range(addr, end);
>> + }
>> +}
>> +
>> void register_page_bootmem_memmap(unsigned long section_nr,
>> struct page *start_page, unsigned long size)
>> {
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 8e5a56f..42b8723 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -1642,6 +1642,8 @@ int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
>> void vmemmap_populate_print_last(void);
>> void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
>> unsigned long size);
>> +void vmemmap_kfree(struct page *memmpa, unsigned long nr_pages);
>> +void vmemmap_free_bootmem(struct page *memmpa, unsigned long nr_pages);
>>
>> enum mf_flags {
>> MF_COUNT_INCREASED = 1 << 0,
>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>> index db9806c..03153cf 100644
>> --- a/mm/memory_hotplug.c
>> +++ b/mm/memory_hotplug.c
>> @@ -312,19 +312,6 @@ static int __meminit __add_section(int nid, struct zone *zone,
>> return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
>> }
>>
>> -#ifdef CONFIG_SPARSEMEM_VMEMMAP
>> -static int __remove_section(struct zone *zone, struct mem_section *ms)
>> -{
>> - int ret = -EINVAL;
>> -
>> - if (!valid_section(ms))
>> - return ret;
>> -
>> - ret = unregister_memory_section(ms);
>> -
>> - return ret;
>> -}
>> -#else
>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>> {
>> unsigned long flags;
>> @@ -341,9 +328,9 @@ static int __remove_section(struct zone *zone, struct mem_section *ms)
>> pgdat_resize_lock(pgdat, &flags);
>> sparse_remove_one_section(zone, ms);
>> pgdat_resize_unlock(pgdat, &flags);
>> - return 0;
>> +
>> + return ret;
>> }
>> -#endif
>>
>> /*
>> * Reasonably generic function for adding memory. It is
>> diff --git a/mm/sparse.c b/mm/sparse.c
>> index fac95f2..ab9d755 100644
>> --- a/mm/sparse.c
>> +++ b/mm/sparse.c
>> @@ -613,12 +613,13 @@ static inline struct page *kmalloc_section_memmap(unsigned long pnum, int nid,
>> /* This will make the necessary allocations eventually. */
>> return sparse_mem_map_populate(pnum, nid);
>> }
>> -static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
>> +static void __kfree_section_memmap(struct page *page, unsigned long nr_pages)
>> {
>> - return; /* XXX: Not implemented yet */
>> + vmemmap_kfree(page, nr_pages);
>> }
>> static void free_map_bootmem(struct page *page, unsigned long nr_pages)
>> {
>> + vmemmap_free_bootmem(page, nr_pages);
>> }
>
> Hi Congyang,
> For vmemmap, nr_pages should be PAGES_PER_SECTION for free_map_bootmem(),
> which is passed by free_section_usemap(), right?
Yes, you are right. I will merge it into my patchset.
I will sent the new patchset later today.
Thanks
Wen Congyang
> But now, nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page)) >> PAGE_SHIFT.
>
> Signed-off-by: Jianguo Wu <wujianguo@huawei.com>
> ---
> mm/sparse.c | 4 ++++
> 1 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/mm/sparse.c b/mm/sparse.c
> index fac95f2..31e5282 100644
> --- a/mm/sparse.c
> +++ b/mm/sparse.c
> @@ -713,8 +713,12 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
> struct page *memmap_page;
> memmap_page = virt_to_page(memmap);
>
> +#ifdef CONFIG_SPARSEMEM_VMEMMAP
> + nr_pages = PAGES_PER_SECTION;
> +#else
> nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
> >> PAGE_SHIFT;
> +#endif
>
> free_map_bootmem(memmap_page, nr_pages);
> }
^ permalink raw reply
* Re: [PATCH] cpuidle: Measure idle state durations with monotonic clock
From: Len Brown @ 2012-11-27 6:15 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Kevin Hilman, Deepthi Dharwar, Trinabh Gupta, Lists Linaro-dev,
linux-pm, Daniel Lezcano, linux-kernel, linux-acpi,
Srivatsa S. Bhat, Julius Werner, Andrew Morton, linuxppc-dev,
Sameer Nanda
In-Reply-To: <3364211.o5ZCq4Y6Je@vostro.rjw.lan>
>> drivers/idle/intel_idle.c | 14 +-----
Acked-by: Len Brown <len.brown@intel.com>
thanks!
-Len
^ permalink raw reply
* Re: [PATCH] cpuidle: Measure idle state durations with monotonic clock
From: Len Brown @ 2012-11-27 6:14 UTC (permalink / raw)
To: Preeti Murthy
Cc: Kevin Hilman, Deepthi Dharwar, Trinabh Gupta, Lists Linaro-dev,
Peter Zijlstra, linux-pm, Daniel Lezcano, linux-kernel,
Rafael J. Wysocki, linux-acpi, Srivatsa S. Bhat, Julius Werner,
Andrew Morton, linuxppc-dev, Sameer Nanda
In-Reply-To: <CAM4v1pPhaP=pVnFMXUU_nmrOxhY5yPNtbBdQsT5MH37MndK6LQ@mail.gmail.com>
On 11/15/2012 04:04 AM, Preeti Murthy wrote:
> Hi all,
>
> The code looks correct and inviting to me as it has led to good cleanups.
> I dont think passing 0 as the argument to the function
> sched_clock_idle_wakeup_event()
> should lead to problems,as it does not do anything useful with the
> passed arguments.
>
> My only curiosity is what was the purpose of passing idle residency time to
> sched_clock_idle_wakeup_event() when this data could always be retrieved from
> dev->last_residency for each cpu,which gets almost immediately updated.
sched_clock_idle_wakeup_event() is part of the scheduler.
The scheduler doesn't know what a cpuidle_device is, and
probably should not grow such a dependency.
cheers,
-Len Brown, Intel Open Source Technology Center
> But this does not seem to come in way of this patch for now.Anyway I
> have added Peter to
> the list so that he can opine about this issue if possible and needed.
>
> Reviewed-by: Preeti U Murthy <preeti@linux.vnet.ibm.com>
>
>
> Regards
> Preeti U Murthy
>
^ permalink raw reply
* Re: [PATCH v3 08/12] memory-hotplug: remove memmap of sparse-vmemmap
From: Jianguo Wu @ 2012-11-27 5:47 UTC (permalink / raw)
To: Wen Congyang
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, linux-mm, Yasuaki Ishimatsu, paulus,
Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <1351763083-7905-9-git-send-email-wency@cn.fujitsu.com>
On 2012/11/1 17:44, Wen Congyang wrote:
> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>
> All pages of virtual mapping in removed memory cannot be freed, since some pages
> used as PGD/PUD includes not only removed memory but also other memory. So the
> patch checks whether page can be freed or not.
>
> How to check whether page can be freed or not?
> 1. When removing memory, the page structs of the revmoved memory are filled
> with 0FD.
> 2. All page structs are filled with 0xFD on PT/PMD, PT/PMD can be cleared.
> In this case, the page used as PT/PMD can be freed.
>
> Applying patch, __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is integrated
> into one. So __remove_section() of CONFIG_SPARSEMEM_VMEMMAP is deleted.
>
> Note: vmemmap_kfree() and vmemmap_free_bootmem() are not implemented for ia64,
> ppc, s390, and sparc.
>
> CC: David Rientjes <rientjes@google.com>
> CC: Jiang Liu <liuj97@gmail.com>
> CC: Len Brown <len.brown@intel.com>
> CC: Christoph Lameter <cl@linux.com>
> Cc: Minchan Kim <minchan.kim@gmail.com>
> CC: Andrew Morton <akpm@linux-foundation.org>
> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
> CC: Wen Congyang <wency@cn.fujitsu.com>
> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
> ---
> arch/ia64/mm/discontig.c | 8 ++++
> arch/powerpc/mm/init_64.c | 8 ++++
> arch/s390/mm/vmem.c | 8 ++++
> arch/sparc/mm/init_64.c | 8 ++++
> arch/x86/mm/init_64.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/mm.h | 2 +
> mm/memory_hotplug.c | 17 +------
> mm/sparse.c | 5 +-
> 8 files changed, 158 insertions(+), 17 deletions(-)
>
> diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c
> index 33943db..0d23b69 100644
> --- a/arch/ia64/mm/discontig.c
> +++ b/arch/ia64/mm/discontig.c
> @@ -823,6 +823,14 @@ int __meminit vmemmap_populate(struct page *start_page,
> return vmemmap_populate_basepages(start_page, size, node);
> }
>
> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index 6466440..df7d155 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -298,6 +298,14 @@ int __meminit vmemmap_populate(struct page *start_page,
> return 0;
> }
>
> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
> diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
> index 4f4803a..ab69c34 100644
> --- a/arch/s390/mm/vmem.c
> +++ b/arch/s390/mm/vmem.c
> @@ -236,6 +236,14 @@ out:
> return ret;
> }
>
> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
> index 75a984b..546855d 100644
> --- a/arch/sparc/mm/init_64.c
> +++ b/arch/sparc/mm/init_64.c
> @@ -2232,6 +2232,14 @@ void __meminit vmemmap_populate_print_last(void)
> }
> }
>
> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
> +{
> +}
> +
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
> diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
> index 795dae3..e85626d 100644
> --- a/arch/x86/mm/init_64.c
> +++ b/arch/x86/mm/init_64.c
> @@ -998,6 +998,125 @@ vmemmap_populate(struct page *start_page, unsigned long size, int node)
> return 0;
> }
>
> +#define PAGE_INUSE 0xFD
> +
> +unsigned long find_and_clear_pte_page(unsigned long addr, unsigned long end,
> + struct page **pp, int *page_size)
> +{
> + pgd_t *pgd;
> + pud_t *pud;
> + pmd_t *pmd;
> + pte_t *pte = NULL;
> + void *page_addr;
> + unsigned long next;
> +
> + *pp = NULL;
> +
> + pgd = pgd_offset_k(addr);
> + if (pgd_none(*pgd))
> + return pgd_addr_end(addr, end);
> +
> + pud = pud_offset(pgd, addr);
> + if (pud_none(*pud))
> + return pud_addr_end(addr, end);
> +
> + if (!cpu_has_pse) {
> + next = (addr + PAGE_SIZE) & PAGE_MASK;
> + pmd = pmd_offset(pud, addr);
> + if (pmd_none(*pmd))
> + return next;
> +
> + pte = pte_offset_kernel(pmd, addr);
> + if (pte_none(*pte))
> + return next;
> +
> + *page_size = PAGE_SIZE;
> + *pp = pte_page(*pte);
> + } else {
> + next = pmd_addr_end(addr, end);
> +
> + pmd = pmd_offset(pud, addr);
> + if (pmd_none(*pmd))
> + return next;
> +
> + *page_size = PMD_SIZE;
> + *pp = pmd_page(*pmd);
> + }
> +
> + /*
> + * Removed page structs are filled with 0xFD.
> + */
> + memset((void *)addr, PAGE_INUSE, next - addr);
> +
> + page_addr = page_address(*pp);
> +
> + /*
> + * Check the page is filled with 0xFD or not.
> + * memchr_inv() returns the address. In this case, we cannot
> + * clear PTE/PUD entry, since the page is used by other.
> + * So we cannot also free the page.
> + *
> + * memchr_inv() returns NULL. In this case, we can clear
> + * PTE/PUD entry, since the page is not used by other.
> + * So we can also free the page.
> + */
> + if (memchr_inv(page_addr, PAGE_INUSE, *page_size)) {
> + *pp = NULL;
> + return next;
> + }
> +
> + if (!cpu_has_pse)
> + pte_clear(&init_mm, addr, pte);
> + else
> + pmd_clear(pmd);
> +
> + return next;
> +}
> +
> +void vmemmap_kfree(struct page *memmap, unsigned long nr_pages)
> +{
> + unsigned long addr = (unsigned long)memmap;
> + unsigned long end = (unsigned long)(memmap + nr_pages);
> + unsigned long next;
> + struct page *page;
> + int page_size;
> +
> + for (; addr < end; addr = next) {
> + page = NULL;
> + page_size = 0;
> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
> + if (!page)
> + continue;
> +
> + free_pages((unsigned long)page_address(page),
> + get_order(page_size));
> + __flush_tlb_one(addr);
> + }
> +}
> +
> +void vmemmap_free_bootmem(struct page *memmap, unsigned long nr_pages)
> +{
> + unsigned long addr = (unsigned long)memmap;
> + unsigned long end = (unsigned long)(memmap + nr_pages);
> + unsigned long next;
> + struct page *page;
> + int page_size;
> + unsigned long magic;
> +
> + for (; addr < end; addr = next) {
> + page = NULL;
> + page_size = 0;
> + next = find_and_clear_pte_page(addr, end, &page, &page_size);
> + if (!page)
> + continue;
> +
> + magic = (unsigned long) page->lru.next;
> + if (magic == SECTION_INFO)
> + put_page_bootmem(page);
> + flush_tlb_kernel_range(addr, end);
> + }
> +}
> +
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 8e5a56f..42b8723 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -1642,6 +1642,8 @@ int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
> void vmemmap_populate_print_last(void);
> void register_page_bootmem_memmap(unsigned long section_nr, struct page *map,
> unsigned long size);
> +void vmemmap_kfree(struct page *memmpa, unsigned long nr_pages);
> +void vmemmap_free_bootmem(struct page *memmpa, unsigned long nr_pages);
>
> enum mf_flags {
> MF_COUNT_INCREASED = 1 << 0,
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index db9806c..03153cf 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -312,19 +312,6 @@ static int __meminit __add_section(int nid, struct zone *zone,
> return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
> }
>
> -#ifdef CONFIG_SPARSEMEM_VMEMMAP
> -static int __remove_section(struct zone *zone, struct mem_section *ms)
> -{
> - int ret = -EINVAL;
> -
> - if (!valid_section(ms))
> - return ret;
> -
> - ret = unregister_memory_section(ms);
> -
> - return ret;
> -}
> -#else
> static int __remove_section(struct zone *zone, struct mem_section *ms)
> {
> unsigned long flags;
> @@ -341,9 +328,9 @@ static int __remove_section(struct zone *zone, struct mem_section *ms)
> pgdat_resize_lock(pgdat, &flags);
> sparse_remove_one_section(zone, ms);
> pgdat_resize_unlock(pgdat, &flags);
> - return 0;
> +
> + return ret;
> }
> -#endif
>
> /*
> * Reasonably generic function for adding memory. It is
> diff --git a/mm/sparse.c b/mm/sparse.c
> index fac95f2..ab9d755 100644
> --- a/mm/sparse.c
> +++ b/mm/sparse.c
> @@ -613,12 +613,13 @@ static inline struct page *kmalloc_section_memmap(unsigned long pnum, int nid,
> /* This will make the necessary allocations eventually. */
> return sparse_mem_map_populate(pnum, nid);
> }
> -static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
> +static void __kfree_section_memmap(struct page *page, unsigned long nr_pages)
> {
> - return; /* XXX: Not implemented yet */
> + vmemmap_kfree(page, nr_pages);
> }
> static void free_map_bootmem(struct page *page, unsigned long nr_pages)
> {
> + vmemmap_free_bootmem(page, nr_pages);
> }
Hi Congyang,
For vmemmap, nr_pages should be PAGES_PER_SECTION for free_map_bootmem(),
which is passed by free_section_usemap(), right?
But now, nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page)) >> PAGE_SHIFT.
Signed-off-by: Jianguo Wu <wujianguo@huawei.com>
---
mm/sparse.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/mm/sparse.c b/mm/sparse.c
index fac95f2..31e5282 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -713,8 +713,12 @@ static void free_section_usemap(struct page *memmap, unsigned long *usemap)
struct page *memmap_page;
memmap_page = virt_to_page(memmap);
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ nr_pages = PAGES_PER_SECTION;
+#else
nr_pages = PAGE_ALIGN(PAGES_PER_SECTION * sizeof(struct page))
>> PAGE_SHIFT;
+#endif
free_map_bootmem(memmap_page, nr_pages);
}
--
1.7.6.1
> #else
> static struct page *__kmalloc_section_memmap(unsigned long nr_pages)
^ permalink raw reply related
* Re: [PATCH 1/3] powerpc: Relocate prom_init.c on 64bit
From: Benjamin Herrenschmidt @ 2012-11-27 5:22 UTC (permalink / raw)
To: Anton Blanchard; +Cc: linuxppc-dev, paulus, amodra
In-Reply-To: <20121127143903.4b5cab17@kryten>
On Tue, 2012-11-27 at 14:39 +1100, Anton Blanchard wrote:
> The ppc64 kernel can get loaded at any address which means
> our very early init code in prom_init.c must be relocatable. We do
> this with a pretty nasty RELOC() macro that we wrap accesses of
> variables with. It is very fragile and sometimes we forget to add a
> RELOC() to an uncommon path or sometimes a compiler change breaks it.
>
> 32bit has a much more elegant solution where we build prom_init.c
> with -mrelocatable and then process the relocations manually.
> Unfortunately we can't do the equivalent on 64bit and we would
> have to build the entire kernel relocatable (-pie), resulting in a
> large increase in kernel footprint (megabytes of relocation data).
> The relocation data will be marked __initdata but it still creates
> more pressure on our already tight memory layout at boot.
>
> Alan Modra pointed out that the 64bit ABI is relocatable even
> if we don't build with -pie, we just need to relocate the TOC.
> This patch implements that idea and relocates the TOC entries of
> prom_init.c. An added bonus is there are very few relocations to
> process which helps keep boot times on simulators down.
>
> gcc does not put 64bit integer constants into the TOC but to be
> safe we may want a build time script which passes through the
> prom_init.c TOC entries to make sure everything looks reasonable.
My only potential objection was that it might have been cleaner to
actually build prom_init.c as a separate binary alltogether and piggy
back it...
Ben.
> Signed-off-by: Anton Blanchard <anton@samba.org>
> ---
>
> To keep the patch small and reviewable, I separated the removal
> of the RELOC macro into a follow up patch.
>
> For simplicity I do the relocation in C but if self brain surgery
> keeps people up at night we can move it into assembly.
>
> Index: b/arch/powerpc/kernel/prom_init.c
> ===================================================================
> --- a/arch/powerpc/kernel/prom_init.c
> +++ b/arch/powerpc/kernel/prom_init.c
> @@ -66,8 +66,8 @@
> * is running at whatever address it has been loaded at.
> * On ppc32 we compile with -mrelocatable, which means that references
> * to extern and static variables get relocated automatically.
> - * On ppc64 we have to relocate the references explicitly with
> - * RELOC. (Note that strings count as static variables.)
> + * ppc64 objects are always relocatable, we just need to relocate the
> + * TOC.
> *
> * Because OF may have mapped I/O devices into the area starting at
> * KERNELBASE, particularly on CHRP machines, we can't safely call
> @@ -79,13 +79,12 @@
> * On ppc64, 64 bit values are truncated to 32 bits (and
> * fortunately don't get interpreted as two arguments).
> */
> +#define RELOC(x) (x)
> +#define ADDR(x) (u32)(unsigned long)(x)
> +
> #ifdef CONFIG_PPC64
> -#define RELOC(x) (*PTRRELOC(&(x)))
> -#define ADDR(x) (u32) add_reloc_offset((unsigned long)(x))
> #define OF_WORKAROUNDS 0
> #else
> -#define RELOC(x) (x)
> -#define ADDR(x) (u32) (x)
> #define OF_WORKAROUNDS of_workarounds
> int of_workarounds;
> #endif
> @@ -334,9 +333,6 @@ static void __init prom_printf(const cha
> struct prom_t *_prom = &RELOC(prom);
>
> va_start(args, format);
> -#ifdef CONFIG_PPC64
> - format = PTRRELOC(format);
> -#endif
> for (p = format; *p != 0; p = q) {
> for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
> ;
> @@ -437,9 +433,6 @@ static unsigned int __init prom_claim(un
>
> static void __init __attribute__((noreturn)) prom_panic(const char *reason)
> {
> -#ifdef CONFIG_PPC64
> - reason = PTRRELOC(reason);
> -#endif
> prom_print(reason);
> /* Do not call exit because it clears the screen on pmac
> * it also causes some sort of double-fault on early pmacs */
> @@ -929,7 +922,7 @@ static void __init prom_send_capabilitie
> * (we assume this is the same for all cores) and use it to
> * divide NR_CPUS.
> */
> - cores = (u32 *)PTRRELOC(&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]);
> + cores = (u32 *)&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET];
> if (*cores != NR_CPUS) {
> prom_printf("WARNING ! "
> "ibm_architecture_vec structure inconsistent: %lu!\n",
> @@ -2850,6 +2843,53 @@ static void __init prom_check_initrd(uns
> #endif /* CONFIG_BLK_DEV_INITRD */
> }
>
> +#ifdef CONFIG_PPC64
> +#ifdef CONFIG_RELOCATABLE
> +static void reloc_toc(void)
> +{
> +}
> +
> +static void unreloc_toc(void)
> +{
> +}
> +#else
> +static void __reloc_toc(void *tocstart, unsigned long offset,
> + unsigned long nr_entries)
> +{
> + unsigned long i;
> + unsigned long *toc_entry = (unsigned long *)tocstart;
> +
> + for (i = 0; i < nr_entries; i++) {
> + *toc_entry = *toc_entry + offset;
> + toc_entry++;
> + }
> +}
> +
> +static void reloc_toc(void)
> +{
> + unsigned long offset = reloc_offset();
> + unsigned long nr_entries =
> + (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
> +
> + /* Need to add offset to get at __prom_init_toc_start */
> + __reloc_toc(__prom_init_toc_start + offset, offset, nr_entries);
> +
> + mb();
> +}
> +
> +static void unreloc_toc(void)
> +{
> + unsigned long offset = reloc_offset();
> + unsigned long nr_entries =
> + (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
> +
> + mb();
> +
> + /* __prom_init_toc_start has been relocated, no need to add offset */
> + __reloc_toc(__prom_init_toc_start, -offset, nr_entries);
> +}
> +#endif
> +#endif
>
> /*
> * We enter here early on, when the Open Firmware prom is still
> @@ -2867,6 +2907,8 @@ unsigned long __init prom_init(unsigned
> #ifdef CONFIG_PPC32
> unsigned long offset = reloc_offset();
> reloc_got2(offset);
> +#else
> + reloc_toc();
> #endif
>
> _prom = &RELOC(prom);
> @@ -3061,6 +3103,8 @@ unsigned long __init prom_init(unsigned
>
> #ifdef CONFIG_PPC32
> reloc_got2(-offset);
> +#else
> + unreloc_toc();
> #endif
>
> #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
> Index: b/arch/powerpc/kernel/prom_init_check.sh
> ===================================================================
> --- a/arch/powerpc/kernel/prom_init_check.sh
> +++ b/arch/powerpc/kernel/prom_init_check.sh
> @@ -22,7 +22,7 @@ __secondary_hold_acknowledge __secondary
> strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
> reloc_got2 kernstart_addr memstart_addr linux_banner _stext
> opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry
> -boot_command_line"
> +boot_command_line __prom_init_toc_start __prom_init_toc_end"
>
> NM="$1"
> OBJ="$2"
> Index: b/arch/powerpc/kernel/vmlinux.lds.S
> ===================================================================
> --- a/arch/powerpc/kernel/vmlinux.lds.S
> +++ b/arch/powerpc/kernel/vmlinux.lds.S
> @@ -218,6 +218,11 @@ SECTIONS
>
> .got : AT(ADDR(.got) - LOAD_OFFSET) {
> __toc_start = .;
> +#ifndef CONFIG_RELOCATABLE
> + __prom_init_toc_start = .;
> + arch/powerpc/kernel/prom_init.o*(.toc .got)
> + __prom_init_toc_end = .;
> +#endif
> *(.got)
> *(.toc)
> }
> Index: b/arch/powerpc/Makefile
> ===================================================================
> --- a/arch/powerpc/Makefile
> +++ b/arch/powerpc/Makefile
> @@ -136,6 +136,7 @@ head-$(CONFIG_FSL_BOOKE) := arch/powerpc
> head-$(CONFIG_PPC64) += arch/powerpc/kernel/entry_64.o
> head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o
> head-$(CONFIG_ALTIVEC) += arch/powerpc/kernel/vector.o
> +head-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += arch/powerpc/kernel/prom_init.o
>
> core-y += arch/powerpc/kernel/ \
> arch/powerpc/mm/ \
> Index: b/arch/powerpc/kernel/Makefile
> ===================================================================
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -91,7 +91,6 @@ obj-$(CONFIG_RELOCATABLE_PPC32) += reloc
> obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
> obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
> obj-$(CONFIG_KGDB) += kgdb.o
> -obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
> obj-$(CONFIG_MODULES) += ppc_ksyms.o
> obj-$(CONFIG_BOOTX_TEXT) += btext.o
> obj-$(CONFIG_SMP) += smp.o
> @@ -142,6 +141,7 @@ GCOV_PROFILE_kprobes.o := n
> extra-$(CONFIG_PPC_FPU) += fpu.o
> extra-$(CONFIG_ALTIVEC) += vector.o
> extra-$(CONFIG_PPC64) += entry_64.o
> +extra-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
>
> extra-y += systbl_chk.i
> $(obj)/systbl.o: systbl_chk
> Index: b/arch/powerpc/include/asm/sections.h
> ===================================================================
> --- a/arch/powerpc/include/asm/sections.h
> +++ b/arch/powerpc/include/asm/sections.h
> @@ -10,6 +10,9 @@
>
> extern char __end_interrupts[];
>
> +extern char __prom_init_toc_start[];
> +extern char __prom_init_toc_end[];
> +
> static inline int in_kernel_text(unsigned long addr)
> {
> if (addr >= (unsigned long)_stext && addr < (unsigned long)__init_end)
^ permalink raw reply
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: Alex Williamson @ 2012-11-27 5:07 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <50B44866.5000905@ozlabs.ru>
On Tue, 2012-11-27 at 15:58 +1100, Alexey Kardashevskiy wrote:
> On 27/11/12 15:29, Alex Williamson wrote:
> > On Tue, 2012-11-27 at 15:06 +1100, Alexey Kardashevskiy wrote:
> >> On 27/11/12 05:20, Alex Williamson wrote:
> >>> On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
> >>>> VFIO implements platform independent stuff such as
> >>>> a PCI driver, BAR access (via read/write on a file descriptor
> >>>> or direct mapping when possible) and IRQ signaling.
> >>>>
> >>>> The platform dependent part includes IOMMU initialization
> >>>> and handling. This patch implements an IOMMU driver for VFIO
> >>>> which does mapping/unmapping pages for the guest IO and
> >>>> provides information about DMA window (required by a POWERPC
> >>>> guest).
> >>>>
> >>>> The counterpart in QEMU is required to support this functionality.
> >>>>
> >>>> Cc: David Gibson <david@gibson.dropbear.id.au>
> >>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> >>>> ---
> >>>> drivers/vfio/Kconfig | 6 +
> >>>> drivers/vfio/Makefile | 1 +
> >>>> drivers/vfio/vfio_iommu_spapr_tce.c | 247 +++++++++++++++++++++++++++++++++++
> >>>> include/linux/vfio.h | 20 +++
> >>>> 4 files changed, 274 insertions(+)
> >>>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
> >>>>
> >>>> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
> >>>> index 7cd5dec..b464687 100644
> >>>> --- a/drivers/vfio/Kconfig
> >>>> +++ b/drivers/vfio/Kconfig
> >>>> @@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
> >>>> depends on VFIO
> >>>> default n
> >>>>
> >>>> +config VFIO_IOMMU_SPAPR_TCE
> >>>> + tristate
> >>>> + depends on VFIO && SPAPR_TCE_IOMMU
> >>>> + default n
> >>>> +
> >>>> menuconfig VFIO
> >>>> tristate "VFIO Non-Privileged userspace driver framework"
> >>>> depends on IOMMU_API
> >>>> select VFIO_IOMMU_TYPE1 if X86
> >>>> + select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
> >>>> help
> >>>> VFIO provides a framework for secure userspace device drivers.
> >>>> See Documentation/vfio.txt for more details.
> >>>> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
> >>>> index 2398d4a..72bfabc 100644
> >>>> --- a/drivers/vfio/Makefile
> >>>> +++ b/drivers/vfio/Makefile
> >>>> @@ -1,3 +1,4 @@
> >>>> obj-$(CONFIG_VFIO) += vfio.o
> >>>> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
> >>>> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
> >>>> obj-$(CONFIG_VFIO_PCI) += pci/
> >>>> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
> >>>> new file mode 100644
> >>>> index 0000000..46a6298
> >>>> --- /dev/null
> >>>> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
> >>>> @@ -0,0 +1,247 @@
> >>>> +/*
> >>>> + * VFIO: IOMMU DMA mapping support for TCE on POWER
> >>>> + *
> >>>> + * Copyright (C) 2012 IBM Corp. All rights reserved.
> >>>> + * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or modify
> >>>> + * it under the terms of the GNU General Public License version 2 as
> >>>> + * published by the Free Software Foundation.
> >>>> + *
> >>>> + * Derived from original vfio_iommu_type1.c:
> >>>> + * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
> >>>> + * Author: Alex Williamson <alex.williamson@redhat.com>
> >>>> + */
> >>>> +
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/pci.h>
> >>>> +#include <linux/slab.h>
> >>>> +#include <linux/uaccess.h>
> >>>> +#include <linux/err.h>
> >>>> +#include <linux/vfio.h>
> >>>> +#include <asm/iommu.h>
> >>>> +
> >>>> +#define DRIVER_VERSION "0.1"
> >>>> +#define DRIVER_AUTHOR "aik@ozlabs.ru"
> >>>> +#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
> >>>> +
> >>>> +static void tce_iommu_detach_group(void *iommu_data,
> >>>> + struct iommu_group *iommu_group);
> >>>> +
> >>>> +/*
> >>>> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
> >>>> + */
> >>>> +
> >>>> +/*
> >>>> + * The container descriptor supports only a single group per container.
> >>>> + * Required by the API as the container is not supplied with the IOMMU group
> >>>> + * at the moment of initialization.
> >>>> + */
> >>>> +struct tce_container {
> >>>> + struct mutex lock;
> >>>> + struct iommu_table *tbl;
> >>>> +};
> >>>> +
> >>>> +static void *tce_iommu_open(unsigned long arg)
> >>>> +{
> >>>> + struct tce_container *container;
> >>>> +
> >>>> + if (arg != VFIO_SPAPR_TCE_IOMMU) {
> >>>> + printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
> >>>> + return ERR_PTR(-EINVAL);
> >>>> + }
> >>>> +
> >>>> + container = kzalloc(sizeof(*container), GFP_KERNEL);
> >>>> + if (!container)
> >>>> + return ERR_PTR(-ENOMEM);
> >>>> +
> >>>> + mutex_init(&container->lock);
> >>>> +
> >>>> + return container;
> >>>> +}
> >>>> +
> >>>> +static void tce_iommu_release(void *iommu_data)
> >>>> +{
> >>>> + struct tce_container *container = iommu_data;
> >>>> +
> >>>> + WARN_ON(container->tbl && !container->tbl->it_group);
> >>>
> >>> I think your patch ordering is backwards here. it_group isn't added
> >>> until 2/2. I'd really like to see the arch/powerpc code approved and
> >>> merged by the powerpc maintainer before we add the code that makes use
> >>> of it into vfio. Otherwise we just get lots of churn if interfaces
> >>> change or they disapprove of it altogether.
> >>
> >>
> >> Makes sense, thanks.
> >>
> >>
> >>>> + if (container->tbl && container->tbl->it_group)
> >>>> + tce_iommu_detach_group(iommu_data, container->tbl->it_group);
> >>>> +
> >>>> + mutex_destroy(&container->lock);
> >>>> +
> >>>> + kfree(container);
> >>>> +}
> >>>> +
> >>>> +static long tce_iommu_ioctl(void *iommu_data,
> >>>> + unsigned int cmd, unsigned long arg)
> >>>> +{
> >>>> + struct tce_container *container = iommu_data;
> >>>> + unsigned long minsz;
> >>>> +
> >>>> + switch (cmd) {
> >>>> + case VFIO_CHECK_EXTENSION: {
> >>>> + return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
> >>>> + }
> >>>> + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
> >>>> + struct vfio_iommu_spapr_tce_info info;
> >>>> + struct iommu_table *tbl = container->tbl;
> >>>> +
> >>>> + if (WARN_ON(!tbl))
> >>>> + return -ENXIO;
> >>>> +
> >>>> + minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
> >>>> + dma64_window_size);
> >>>> +
> >>>> + if (copy_from_user(&info, (void __user *)arg, minsz))
> >>>> + return -EFAULT;
> >>>> +
> >>>> + if (info.argsz < minsz)
> >>>> + return -EINVAL;
> >>>> +
> >>>> + info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
> >>>> + info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
> >>>> + info.dma64_window_start = 0;
> >>>> + info.dma64_window_size = 0;
> >>>> + info.flags = 0;
> >>>> +
> >>>> + if (copy_to_user((void __user *)arg, &info, minsz))
> >>>> + return -EFAULT;
> >>>> +
> >>>> + return 0;
> >>>> + }
> >>>> + case VFIO_IOMMU_MAP_DMA: {
> >>>> + vfio_iommu_spapr_tce_dma_map param;
> >>>> + struct iommu_table *tbl = container->tbl;
> >>>> + enum dma_data_direction direction = DMA_NONE;
> >>>> +
> >>>> + if (WARN_ON(!tbl))
> >>>> + return -ENXIO;
> >>>> +
> >>>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
> >>>> +
> >>>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
> >>>> + return -EFAULT;
> >>>> +
> >>>> + if (param.argsz < minsz)
> >>>> + return -EINVAL;
> >>>> +
> >>>> + if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
> >>>> + (param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
> >>>> + direction = DMA_BIDIRECTIONAL;
> >>>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
> >>>> + direction = DMA_TO_DEVICE;
> >>>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
> >>>> + direction = DMA_FROM_DEVICE;
> >>>> + }
> >>>> +
> >>>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
> >>>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >>>
> >>> On x86 we force iova, vaddr, and size to all be aligned to the smallest
> >>> page granularity of the iommu and return -EINVAL if it doesn't fit.
> >>> What does it imply to the user if they're always aligned to work here?
> >>> Won't this interface happily map overlapping entries with no indication
> >>> to the user that the previous mapping is no longer valid?
> >>> Maybe another reason why a combined unmap/map makes me nervous, we have
> >>> to assume the user knows what they're doing.
> >>
> >>
> >> I got used to guests which do know what they are doing so I am pretty calm :)
> >> but ok, I'll move alignment to the QEMU, it makes sense.
> >>
> >>
> >>>> +
> >>>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >>>> + param.vaddr & IOMMU_PAGE_MASK, direction,
> >>>> + param.size >> IOMMU_PAGE_SHIFT);
> >>>> + }
> >>>> + case VFIO_IOMMU_UNMAP_DMA: {
> >>>> + vfio_iommu_spapr_tce_dma_unmap param;
> >>>> + struct iommu_table *tbl = container->tbl;
> >>>> +
> >>>> + if (WARN_ON(!tbl))
> >>>> + return -ENXIO;
> >>>> +
> >>>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
> >>>> +
> >>>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
> >>>> + return -EFAULT;
> >>>> +
> >>>> + if (param.argsz < minsz)
> >>>> + return -EINVAL;
> >>>> +
> >>>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
> >>>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >>>> +
> >>>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >>>> + 0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
> >>>> + }
> >>>> + default:
> >>>> + printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
> >>>
> >>> pr_warn
> >>>
> >>>> + }
> >>>> +
> >>>> + return -ENOTTY;
> >>>> +}
> >>>> +
> >>>> +static int tce_iommu_attach_group(void *iommu_data,
> >>>> + struct iommu_group *iommu_group)
> >>>> +{
> >>>> + struct tce_container *container = iommu_data;
> >>>> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
> >>>> +
> >>>> + BUG_ON(!tbl);
> >>>> + mutex_lock(&container->lock);
> >>>> + pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
> >>>> + iommu_group_id(iommu_group), iommu_group);
> >>>> + if (container->tbl) {
> >>>> + printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
> >>>
> >>> pr_warn
> >>>
> >>>> + iommu_group_id(container->tbl->it_group),
> >>>> + iommu_group_id(iommu_group));
> >>>> + mutex_unlock(&container->lock);
> >>>> + return -EBUSY;
> >>>> + }
> >>>> +
> >>>> + container->tbl = tbl;
> >>>
> >>> Would it be too much paranoia to clear all the tce here as you do below
> >>> on detach?
> >>
> >> Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e.
> >> unmaps) the whole DMA window at the boot time.
> >
> > But that's just one user of this interface, we can't assume they'll all
> > be so agreeable. If any tces were enabled here, a malicious user would
> > have a window to host memory, right? Thanks,
>
>
> But I still release pages on detach(), how can this code be not called on
> the guest exit (normal or crashed)?
What's the initial state? You leave it clean, but who came before you?
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: David Gibson @ 2012-11-27 5:06 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: linuxppc-dev, Alex Williamson, Paul Mackerras, linux-kernel, kvm
In-Reply-To: <50B44866.5000905@ozlabs.ru>
On Tue, Nov 27, 2012 at 03:58:14PM +1100, Alexey Kardashevskiy wrote:
> On 27/11/12 15:29, Alex Williamson wrote:
> >On Tue, 2012-11-27 at 15:06 +1100, Alexey Kardashevskiy wrote:
> >>On 27/11/12 05:20, Alex Williamson wrote:
> >>>On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
> >>>>VFIO implements platform independent stuff such as
> >>>>a PCI driver, BAR access (via read/write on a file descriptor
> >>>>or direct mapping when possible) and IRQ signaling.
> >>>>
> >>>>The platform dependent part includes IOMMU initialization
> >>>>and handling. This patch implements an IOMMU driver for VFIO
> >>>>which does mapping/unmapping pages for the guest IO and
> >>>>provides information about DMA window (required by a POWERPC
> >>>>guest).
> >>>>
> >>>>The counterpart in QEMU is required to support this functionality.
> >>>>
> >>>>Cc: David Gibson <david@gibson.dropbear.id.au>
> >>>>Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> >>>>---
> >>>> drivers/vfio/Kconfig | 6 +
> >>>> drivers/vfio/Makefile | 1 +
> >>>> drivers/vfio/vfio_iommu_spapr_tce.c | 247 +++++++++++++++++++++++++++++++++++
> >>>> include/linux/vfio.h | 20 +++
> >>>> 4 files changed, 274 insertions(+)
> >>>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
> >>>>
> >>>>diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
> >>>>index 7cd5dec..b464687 100644
> >>>>--- a/drivers/vfio/Kconfig
> >>>>+++ b/drivers/vfio/Kconfig
> >>>>@@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
> >>>> depends on VFIO
> >>>> default n
> >>>>
> >>>>+config VFIO_IOMMU_SPAPR_TCE
> >>>>+ tristate
> >>>>+ depends on VFIO && SPAPR_TCE_IOMMU
> >>>>+ default n
> >>>>+
> >>>> menuconfig VFIO
> >>>> tristate "VFIO Non-Privileged userspace driver framework"
> >>>> depends on IOMMU_API
> >>>> select VFIO_IOMMU_TYPE1 if X86
> >>>>+ select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
> >>>> help
> >>>> VFIO provides a framework for secure userspace device drivers.
> >>>> See Documentation/vfio.txt for more details.
> >>>>diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
> >>>>index 2398d4a..72bfabc 100644
> >>>>--- a/drivers/vfio/Makefile
> >>>>+++ b/drivers/vfio/Makefile
> >>>>@@ -1,3 +1,4 @@
> >>>> obj-$(CONFIG_VFIO) += vfio.o
> >>>> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
> >>>>+obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
> >>>> obj-$(CONFIG_VFIO_PCI) += pci/
> >>>>diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
> >>>>new file mode 100644
> >>>>index 0000000..46a6298
> >>>>--- /dev/null
> >>>>+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
> >>>>@@ -0,0 +1,247 @@
> >>>>+/*
> >>>>+ * VFIO: IOMMU DMA mapping support for TCE on POWER
> >>>>+ *
> >>>>+ * Copyright (C) 2012 IBM Corp. All rights reserved.
> >>>>+ * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
> >>>>+ *
> >>>>+ * This program is free software; you can redistribute it and/or modify
> >>>>+ * it under the terms of the GNU General Public License version 2 as
> >>>>+ * published by the Free Software Foundation.
> >>>>+ *
> >>>>+ * Derived from original vfio_iommu_type1.c:
> >>>>+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
> >>>>+ * Author: Alex Williamson <alex.williamson@redhat.com>
> >>>>+ */
> >>>>+
> >>>>+#include <linux/module.h>
> >>>>+#include <linux/pci.h>
> >>>>+#include <linux/slab.h>
> >>>>+#include <linux/uaccess.h>
> >>>>+#include <linux/err.h>
> >>>>+#include <linux/vfio.h>
> >>>>+#include <asm/iommu.h>
> >>>>+
> >>>>+#define DRIVER_VERSION "0.1"
> >>>>+#define DRIVER_AUTHOR "aik@ozlabs.ru"
> >>>>+#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
> >>>>+
> >>>>+static void tce_iommu_detach_group(void *iommu_data,
> >>>>+ struct iommu_group *iommu_group);
> >>>>+
> >>>>+/*
> >>>>+ * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
> >>>>+ */
> >>>>+
> >>>>+/*
> >>>>+ * The container descriptor supports only a single group per container.
> >>>>+ * Required by the API as the container is not supplied with the IOMMU group
> >>>>+ * at the moment of initialization.
> >>>>+ */
> >>>>+struct tce_container {
> >>>>+ struct mutex lock;
> >>>>+ struct iommu_table *tbl;
> >>>>+};
> >>>>+
> >>>>+static void *tce_iommu_open(unsigned long arg)
> >>>>+{
> >>>>+ struct tce_container *container;
> >>>>+
> >>>>+ if (arg != VFIO_SPAPR_TCE_IOMMU) {
> >>>>+ printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
> >>>>+ return ERR_PTR(-EINVAL);
> >>>>+ }
> >>>>+
> >>>>+ container = kzalloc(sizeof(*container), GFP_KERNEL);
> >>>>+ if (!container)
> >>>>+ return ERR_PTR(-ENOMEM);
> >>>>+
> >>>>+ mutex_init(&container->lock);
> >>>>+
> >>>>+ return container;
> >>>>+}
> >>>>+
> >>>>+static void tce_iommu_release(void *iommu_data)
> >>>>+{
> >>>>+ struct tce_container *container = iommu_data;
> >>>>+
> >>>>+ WARN_ON(container->tbl && !container->tbl->it_group);
> >>>
> >>>I think your patch ordering is backwards here. it_group isn't added
> >>>until 2/2. I'd really like to see the arch/powerpc code approved and
> >>>merged by the powerpc maintainer before we add the code that makes use
> >>>of it into vfio. Otherwise we just get lots of churn if interfaces
> >>>change or they disapprove of it altogether.
> >>
> >>
> >>Makes sense, thanks.
> >>
> >>
> >>>>+ if (container->tbl && container->tbl->it_group)
> >>>>+ tce_iommu_detach_group(iommu_data, container->tbl->it_group);
> >>>>+
> >>>>+ mutex_destroy(&container->lock);
> >>>>+
> >>>>+ kfree(container);
> >>>>+}
> >>>>+
> >>>>+static long tce_iommu_ioctl(void *iommu_data,
> >>>>+ unsigned int cmd, unsigned long arg)
> >>>>+{
> >>>>+ struct tce_container *container = iommu_data;
> >>>>+ unsigned long minsz;
> >>>>+
> >>>>+ switch (cmd) {
> >>>>+ case VFIO_CHECK_EXTENSION: {
> >>>>+ return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
> >>>>+ }
> >>>>+ case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
> >>>>+ struct vfio_iommu_spapr_tce_info info;
> >>>>+ struct iommu_table *tbl = container->tbl;
> >>>>+
> >>>>+ if (WARN_ON(!tbl))
> >>>>+ return -ENXIO;
> >>>>+
> >>>>+ minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
> >>>>+ dma64_window_size);
> >>>>+
> >>>>+ if (copy_from_user(&info, (void __user *)arg, minsz))
> >>>>+ return -EFAULT;
> >>>>+
> >>>>+ if (info.argsz < minsz)
> >>>>+ return -EINVAL;
> >>>>+
> >>>>+ info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
> >>>>+ info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
> >>>>+ info.dma64_window_start = 0;
> >>>>+ info.dma64_window_size = 0;
> >>>>+ info.flags = 0;
> >>>>+
> >>>>+ if (copy_to_user((void __user *)arg, &info, minsz))
> >>>>+ return -EFAULT;
> >>>>+
> >>>>+ return 0;
> >>>>+ }
> >>>>+ case VFIO_IOMMU_MAP_DMA: {
> >>>>+ vfio_iommu_spapr_tce_dma_map param;
> >>>>+ struct iommu_table *tbl = container->tbl;
> >>>>+ enum dma_data_direction direction = DMA_NONE;
> >>>>+
> >>>>+ if (WARN_ON(!tbl))
> >>>>+ return -ENXIO;
> >>>>+
> >>>>+ minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
> >>>>+
> >>>>+ if (copy_from_user(¶m, (void __user *)arg, minsz))
> >>>>+ return -EFAULT;
> >>>>+
> >>>>+ if (param.argsz < minsz)
> >>>>+ return -EINVAL;
> >>>>+
> >>>>+ if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
> >>>>+ (param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
> >>>>+ direction = DMA_BIDIRECTIONAL;
> >>>>+ } else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
> >>>>+ direction = DMA_TO_DEVICE;
> >>>>+ } else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
> >>>>+ direction = DMA_FROM_DEVICE;
> >>>>+ }
> >>>>+
> >>>>+ param.size += param.iova & ~IOMMU_PAGE_MASK;
> >>>>+ param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >>>
> >>>On x86 we force iova, vaddr, and size to all be aligned to the smallest
> >>>page granularity of the iommu and return -EINVAL if it doesn't fit.
> >>>What does it imply to the user if they're always aligned to work here?
> >>>Won't this interface happily map overlapping entries with no indication
> >>>to the user that the previous mapping is no longer valid?
> >>>Maybe another reason why a combined unmap/map makes me nervous, we have
> >>>to assume the user knows what they're doing.
> >>
> >>
> >>I got used to guests which do know what they are doing so I am pretty calm :)
> >>but ok, I'll move alignment to the QEMU, it makes sense.
> >>
> >>
> >>>>+
> >>>>+ return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >>>>+ param.vaddr & IOMMU_PAGE_MASK, direction,
> >>>>+ param.size >> IOMMU_PAGE_SHIFT);
> >>>>+ }
> >>>>+ case VFIO_IOMMU_UNMAP_DMA: {
> >>>>+ vfio_iommu_spapr_tce_dma_unmap param;
> >>>>+ struct iommu_table *tbl = container->tbl;
> >>>>+
> >>>>+ if (WARN_ON(!tbl))
> >>>>+ return -ENXIO;
> >>>>+
> >>>>+ minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
> >>>>+
> >>>>+ if (copy_from_user(¶m, (void __user *)arg, minsz))
> >>>>+ return -EFAULT;
> >>>>+
> >>>>+ if (param.argsz < minsz)
> >>>>+ return -EINVAL;
> >>>>+
> >>>>+ param.size += param.iova & ~IOMMU_PAGE_MASK;
> >>>>+ param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >>>>+
> >>>>+ return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >>>>+ 0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
> >>>>+ }
> >>>>+ default:
> >>>>+ printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
> >>>
> >>>pr_warn
> >>>
> >>>>+ }
> >>>>+
> >>>>+ return -ENOTTY;
> >>>>+}
> >>>>+
> >>>>+static int tce_iommu_attach_group(void *iommu_data,
> >>>>+ struct iommu_group *iommu_group)
> >>>>+{
> >>>>+ struct tce_container *container = iommu_data;
> >>>>+ struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
> >>>>+
> >>>>+ BUG_ON(!tbl);
> >>>>+ mutex_lock(&container->lock);
> >>>>+ pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
> >>>>+ iommu_group_id(iommu_group), iommu_group);
> >>>>+ if (container->tbl) {
> >>>>+ printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
> >>>
> >>>pr_warn
> >>>
> >>>>+ iommu_group_id(container->tbl->it_group),
> >>>>+ iommu_group_id(iommu_group));
> >>>>+ mutex_unlock(&container->lock);
> >>>>+ return -EBUSY;
> >>>>+ }
> >>>>+
> >>>>+ container->tbl = tbl;
> >>>
> >>>Would it be too much paranoia to clear all the tce here as you do below
> >>>on detach?
> >>
> >>Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e.
> >>unmaps) the whole DMA window at the boot time.
> >
> >But that's just one user of this interface, we can't assume they'll all
> >be so agreeable. If any tces were enabled here, a malicious user would
> >have a window to host memory, right? Thanks,
>
>
> But I still release pages on detach(), how can this code be not
> called on the guest exit (normal or crashed)?
I think the concern is about robustness if some bug elsewhere in the
kernel left some TCE entries in place before the table was handed over
to VFIO.
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
^ permalink raw reply
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: Alexey Kardashevskiy @ 2012-11-27 4:58 UTC (permalink / raw)
To: Alex Williamson
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1353990588.1809.148.camel@bling.home>
On 27/11/12 15:29, Alex Williamson wrote:
> On Tue, 2012-11-27 at 15:06 +1100, Alexey Kardashevskiy wrote:
>> On 27/11/12 05:20, Alex Williamson wrote:
>>> On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
>>>> VFIO implements platform independent stuff such as
>>>> a PCI driver, BAR access (via read/write on a file descriptor
>>>> or direct mapping when possible) and IRQ signaling.
>>>>
>>>> The platform dependent part includes IOMMU initialization
>>>> and handling. This patch implements an IOMMU driver for VFIO
>>>> which does mapping/unmapping pages for the guest IO and
>>>> provides information about DMA window (required by a POWERPC
>>>> guest).
>>>>
>>>> The counterpart in QEMU is required to support this functionality.
>>>>
>>>> Cc: David Gibson <david@gibson.dropbear.id.au>
>>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>>> ---
>>>> drivers/vfio/Kconfig | 6 +
>>>> drivers/vfio/Makefile | 1 +
>>>> drivers/vfio/vfio_iommu_spapr_tce.c | 247 +++++++++++++++++++++++++++++++++++
>>>> include/linux/vfio.h | 20 +++
>>>> 4 files changed, 274 insertions(+)
>>>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>>>>
>>>> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
>>>> index 7cd5dec..b464687 100644
>>>> --- a/drivers/vfio/Kconfig
>>>> +++ b/drivers/vfio/Kconfig
>>>> @@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
>>>> depends on VFIO
>>>> default n
>>>>
>>>> +config VFIO_IOMMU_SPAPR_TCE
>>>> + tristate
>>>> + depends on VFIO && SPAPR_TCE_IOMMU
>>>> + default n
>>>> +
>>>> menuconfig VFIO
>>>> tristate "VFIO Non-Privileged userspace driver framework"
>>>> depends on IOMMU_API
>>>> select VFIO_IOMMU_TYPE1 if X86
>>>> + select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
>>>> help
>>>> VFIO provides a framework for secure userspace device drivers.
>>>> See Documentation/vfio.txt for more details.
>>>> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
>>>> index 2398d4a..72bfabc 100644
>>>> --- a/drivers/vfio/Makefile
>>>> +++ b/drivers/vfio/Makefile
>>>> @@ -1,3 +1,4 @@
>>>> obj-$(CONFIG_VFIO) += vfio.o
>>>> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
>>>> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
>>>> obj-$(CONFIG_VFIO_PCI) += pci/
>>>> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
>>>> new file mode 100644
>>>> index 0000000..46a6298
>>>> --- /dev/null
>>>> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
>>>> @@ -0,0 +1,247 @@
>>>> +/*
>>>> + * VFIO: IOMMU DMA mapping support for TCE on POWER
>>>> + *
>>>> + * Copyright (C) 2012 IBM Corp. All rights reserved.
>>>> + * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + *
>>>> + * Derived from original vfio_iommu_type1.c:
>>>> + * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
>>>> + * Author: Alex Williamson <alex.williamson@redhat.com>
>>>> + */
>>>> +
>>>> +#include <linux/module.h>
>>>> +#include <linux/pci.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/uaccess.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/vfio.h>
>>>> +#include <asm/iommu.h>
>>>> +
>>>> +#define DRIVER_VERSION "0.1"
>>>> +#define DRIVER_AUTHOR "aik@ozlabs.ru"
>>>> +#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
>>>> +
>>>> +static void tce_iommu_detach_group(void *iommu_data,
>>>> + struct iommu_group *iommu_group);
>>>> +
>>>> +/*
>>>> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
>>>> + */
>>>> +
>>>> +/*
>>>> + * The container descriptor supports only a single group per container.
>>>> + * Required by the API as the container is not supplied with the IOMMU group
>>>> + * at the moment of initialization.
>>>> + */
>>>> +struct tce_container {
>>>> + struct mutex lock;
>>>> + struct iommu_table *tbl;
>>>> +};
>>>> +
>>>> +static void *tce_iommu_open(unsigned long arg)
>>>> +{
>>>> + struct tce_container *container;
>>>> +
>>>> + if (arg != VFIO_SPAPR_TCE_IOMMU) {
>>>> + printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
>>>> + return ERR_PTR(-EINVAL);
>>>> + }
>>>> +
>>>> + container = kzalloc(sizeof(*container), GFP_KERNEL);
>>>> + if (!container)
>>>> + return ERR_PTR(-ENOMEM);
>>>> +
>>>> + mutex_init(&container->lock);
>>>> +
>>>> + return container;
>>>> +}
>>>> +
>>>> +static void tce_iommu_release(void *iommu_data)
>>>> +{
>>>> + struct tce_container *container = iommu_data;
>>>> +
>>>> + WARN_ON(container->tbl && !container->tbl->it_group);
>>>
>>> I think your patch ordering is backwards here. it_group isn't added
>>> until 2/2. I'd really like to see the arch/powerpc code approved and
>>> merged by the powerpc maintainer before we add the code that makes use
>>> of it into vfio. Otherwise we just get lots of churn if interfaces
>>> change or they disapprove of it altogether.
>>
>>
>> Makes sense, thanks.
>>
>>
>>>> + if (container->tbl && container->tbl->it_group)
>>>> + tce_iommu_detach_group(iommu_data, container->tbl->it_group);
>>>> +
>>>> + mutex_destroy(&container->lock);
>>>> +
>>>> + kfree(container);
>>>> +}
>>>> +
>>>> +static long tce_iommu_ioctl(void *iommu_data,
>>>> + unsigned int cmd, unsigned long arg)
>>>> +{
>>>> + struct tce_container *container = iommu_data;
>>>> + unsigned long minsz;
>>>> +
>>>> + switch (cmd) {
>>>> + case VFIO_CHECK_EXTENSION: {
>>>> + return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
>>>> + }
>>>> + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
>>>> + struct vfio_iommu_spapr_tce_info info;
>>>> + struct iommu_table *tbl = container->tbl;
>>>> +
>>>> + if (WARN_ON(!tbl))
>>>> + return -ENXIO;
>>>> +
>>>> + minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
>>>> + dma64_window_size);
>>>> +
>>>> + if (copy_from_user(&info, (void __user *)arg, minsz))
>>>> + return -EFAULT;
>>>> +
>>>> + if (info.argsz < minsz)
>>>> + return -EINVAL;
>>>> +
>>>> + info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
>>>> + info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
>>>> + info.dma64_window_start = 0;
>>>> + info.dma64_window_size = 0;
>>>> + info.flags = 0;
>>>> +
>>>> + if (copy_to_user((void __user *)arg, &info, minsz))
>>>> + return -EFAULT;
>>>> +
>>>> + return 0;
>>>> + }
>>>> + case VFIO_IOMMU_MAP_DMA: {
>>>> + vfio_iommu_spapr_tce_dma_map param;
>>>> + struct iommu_table *tbl = container->tbl;
>>>> + enum dma_data_direction direction = DMA_NONE;
>>>> +
>>>> + if (WARN_ON(!tbl))
>>>> + return -ENXIO;
>>>> +
>>>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
>>>> +
>>>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
>>>> + return -EFAULT;
>>>> +
>>>> + if (param.argsz < minsz)
>>>> + return -EINVAL;
>>>> +
>>>> + if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
>>>> + (param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
>>>> + direction = DMA_BIDIRECTIONAL;
>>>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
>>>> + direction = DMA_TO_DEVICE;
>>>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
>>>> + direction = DMA_FROM_DEVICE;
>>>> + }
>>>> +
>>>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
>>>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>>>
>>> On x86 we force iova, vaddr, and size to all be aligned to the smallest
>>> page granularity of the iommu and return -EINVAL if it doesn't fit.
>>> What does it imply to the user if they're always aligned to work here?
>>> Won't this interface happily map overlapping entries with no indication
>>> to the user that the previous mapping is no longer valid?
>>> Maybe another reason why a combined unmap/map makes me nervous, we have
>>> to assume the user knows what they're doing.
>>
>>
>> I got used to guests which do know what they are doing so I am pretty calm :)
>> but ok, I'll move alignment to the QEMU, it makes sense.
>>
>>
>>>> +
>>>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>>>> + param.vaddr & IOMMU_PAGE_MASK, direction,
>>>> + param.size >> IOMMU_PAGE_SHIFT);
>>>> + }
>>>> + case VFIO_IOMMU_UNMAP_DMA: {
>>>> + vfio_iommu_spapr_tce_dma_unmap param;
>>>> + struct iommu_table *tbl = container->tbl;
>>>> +
>>>> + if (WARN_ON(!tbl))
>>>> + return -ENXIO;
>>>> +
>>>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
>>>> +
>>>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
>>>> + return -EFAULT;
>>>> +
>>>> + if (param.argsz < minsz)
>>>> + return -EINVAL;
>>>> +
>>>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
>>>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>>>> +
>>>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>>>> + 0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
>>>> + }
>>>> + default:
>>>> + printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
>>>
>>> pr_warn
>>>
>>>> + }
>>>> +
>>>> + return -ENOTTY;
>>>> +}
>>>> +
>>>> +static int tce_iommu_attach_group(void *iommu_data,
>>>> + struct iommu_group *iommu_group)
>>>> +{
>>>> + struct tce_container *container = iommu_data;
>>>> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
>>>> +
>>>> + BUG_ON(!tbl);
>>>> + mutex_lock(&container->lock);
>>>> + pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
>>>> + iommu_group_id(iommu_group), iommu_group);
>>>> + if (container->tbl) {
>>>> + printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
>>>
>>> pr_warn
>>>
>>>> + iommu_group_id(container->tbl->it_group),
>>>> + iommu_group_id(iommu_group));
>>>> + mutex_unlock(&container->lock);
>>>> + return -EBUSY;
>>>> + }
>>>> +
>>>> + container->tbl = tbl;
>>>
>>> Would it be too much paranoia to clear all the tce here as you do below
>>> on detach?
>>
>> Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e.
>> unmaps) the whole DMA window at the boot time.
>
> But that's just one user of this interface, we can't assume they'll all
> be so agreeable. If any tces were enabled here, a malicious user would
> have a window to host memory, right? Thanks,
But I still release pages on detach(), how can this code be not called on
the guest exit (normal or crashed)?
>
> Alex
>
--
Alexey
^ permalink raw reply
* Re: [PATCH 2/2] vfio powerpc: enabled on powernv platform
From: Alex Williamson @ 2012-11-27 4:41 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1353661396-14374-3-git-send-email-aik@ozlabs.ru>
On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
> This patch initializes IOMMU groups based on the IOMMU
> configuration discovered during the PCI scan on POWERNV
> (POWER non virtualized) platform. The IOMMU groups are
> to be used later by VFIO driver (PCI pass through).
>
> It also implements an API for mapping/unmapping pages for
> guest PCI drivers and providing DMA window properties.
> This API is going to be used later by QEMU-VFIO to handle
> h_put_tce hypercalls from the KVM guest.
>
> Although this driver has been tested only on the POWERNV
> platform, it should work on any platform which supports
> TCE tables.
>
> To enable VFIO on POWER, enable SPAPR_TCE_IOMMU config
> option and configure VFIO as required.
>
> Cc: David Gibson <david@gibson.dropbear.id.au>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> arch/powerpc/include/asm/iommu.h | 6 ++
> arch/powerpc/kernel/iommu.c | 141 ++++++++++++++++++++++++++++++++++
> arch/powerpc/platforms/powernv/pci.c | 135 ++++++++++++++++++++++++++++++++
> drivers/iommu/Kconfig | 8 ++
> 4 files changed, 290 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
> index cbfe678..5ba66cb 100644
> --- a/arch/powerpc/include/asm/iommu.h
> +++ b/arch/powerpc/include/asm/iommu.h
> @@ -76,6 +76,9 @@ struct iommu_table {
> struct iommu_pool large_pool;
> struct iommu_pool pools[IOMMU_NR_POOLS];
> unsigned long *it_map; /* A simple allocation bitmap for now */
> +#ifdef CONFIG_IOMMU_API
> + struct iommu_group *it_group;
> +#endif
> };
>
> struct scatterlist;
> @@ -147,5 +150,8 @@ static inline void iommu_restore(void)
> }
> #endif
>
> +extern long iommu_put_tces(struct iommu_table *tbl, unsigned long entry, uint64_t tce,
> + enum dma_data_direction direction, unsigned long pages);
> +
> #endif /* __KERNEL__ */
> #endif /* _ASM_IOMMU_H */
> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
> index ff5a6ce..c8dad1f 100644
> --- a/arch/powerpc/kernel/iommu.c
> +++ b/arch/powerpc/kernel/iommu.c
> @@ -44,6 +44,7 @@
> #include <asm/kdump.h>
> #include <asm/fadump.h>
> #include <asm/vio.h>
> +#include <asm/tce.h>
>
> #define DBG(...)
>
> @@ -856,3 +857,143 @@ void iommu_free_coherent(struct iommu_table *tbl, size_t size,
> free_pages((unsigned long)vaddr, get_order(size));
> }
> }
> +
> +#ifdef CONFIG_IOMMU_API
> +/*
> + * SPAPR TCE API
> + */
> +static struct page *free_tce(struct iommu_table *tbl, unsigned long entry)
> +{
> + struct page *page;
> + unsigned long oldtce;
> +
> + oldtce = ppc_md.tce_get(tbl, entry);
> +
> + if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
> + return NULL;
> +
> + page = pfn_to_page(oldtce >> PAGE_SHIFT);
> +
> + WARN_ON(!page);
> + if (page && (oldtce & TCE_PCI_WRITE))
> + SetPageDirty(page);
> + ppc_md.tce_free(tbl, entry, 1);
> +
> + return page;
> +}
> +
> +static int put_tce(struct iommu_table *tbl, unsigned long entry,
> + uint64_t tce, enum dma_data_direction direction)
> +{
> + int ret;
> + struct page *page = NULL;
> + unsigned long kva, offset;
> +
> + /* Map new TCE */
> + offset = (tce & IOMMU_PAGE_MASK) - (tce & PAGE_MASK);
> + ret = get_user_pages_fast(tce & PAGE_MASK, 1,
> + direction != DMA_TO_DEVICE, &page);
We're locking memory here on behalf of the user, but I don't see where
rlimit gets checked to verify the user has privileges to lock the pages.
I know you're locking a much smaller set of memory than x86 does, but
are we just foregoing that added security?
> + if (ret < 1) {
> + printk(KERN_ERR "tce_vfio: get_user_pages_fast failed tce=%llx ioba=%lx ret=%d\n",
> + tce, entry << IOMMU_PAGE_SHIFT, ret);
> + if (!ret)
> + ret = -EFAULT;
> + return ret;
> + }
> +
> + kva = (unsigned long) page_address(page);
> + kva += offset;
> +
> + /* tce_build receives a virtual address */
> + entry += tbl->it_offset; /* Offset into real TCE table */
> + ret = ppc_md.tce_build(tbl, entry, 1, kva, direction, NULL);
> +
> + /* tce_build() only returns non-zero for transient errors */
> + if (unlikely(ret)) {
> + printk(KERN_ERR "tce_vfio: tce_put failed on tce=%llx ioba=%lx kva=%lx ret=%d\n",
> + tce, entry << IOMMU_PAGE_SHIFT, kva, ret);
> + put_page(page);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static void tce_flush(struct iommu_table *tbl)
> +{
> + /* Flush/invalidate TLB caches if necessary */
> + if (ppc_md.tce_flush)
> + ppc_md.tce_flush(tbl);
> +
> + /* Make sure updates are seen by hardware */
> + mb();
> +}
> +
> +long iommu_put_tces(struct iommu_table *tbl, unsigned long entry, uint64_t tce,
> + enum dma_data_direction direction, unsigned long pages)
> +{
> + int i, ret = 0, pages_to_put = 0;
> + struct page *page;
> + struct iommu_pool *pool = get_pool(tbl, entry);
> + struct page **oldpages;
> + const int oldpagesnum = PAGE_SIZE/sizeof(*oldpages);
> +
> + BUILD_BUG_ON(PAGE_SIZE < IOMMU_PAGE_SIZE);
> +
> + /* Handle a single page request without allocation
> + of pages-to-release array */
nit, this comment style doesn't seem to match anything existing in this
file. I'd also be tempted to use pr_err/warn in this file, but I'll
leave that for the maintainers. Thanks,
Alex
> + if (pages == 1) {
> + spin_lock(&(pool->lock));
> + page = free_tce(tbl, entry);
> +
> + if (direction != DMA_NONE)
> + ret = put_tce(tbl, entry, tce, direction);
> +
> + tce_flush(tbl);
> +
> + if (page)
> + put_page(page);
> +
> + spin_unlock(&(pool->lock));
> + return ret;
> + }
> +
> + /* Releasing multiple pages */
> + /* Allocate an array for pages to be released after TCE table
> + is updated */
> + oldpages = kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!oldpages)
> + return -ENOMEM;
> +
> + spin_lock(&(pool->lock));
> +
> + for (i = 0; (i < pages) && !ret; ++i, ++entry, tce += IOMMU_PAGE_SIZE) {
> + page = free_tce(tbl, entry);
> + if (page) {
> + oldpages[pages_to_put] = page;
> + ++pages_to_put;
> + }
> +
> + if (direction != DMA_NONE)
> + ret = put_tce(tbl, entry, tce, direction);
> +
> + /* Release old pages if we reached the end of oldpages[] or
> + it is the last page or we are about to exit the loop */
> + if ((pages_to_put == oldpagesnum) || (i == pages - 1) || ret) {
> + tce_flush(tbl);
> +
> + /* Release pages after removing them from TCE table */
> + while (pages_to_put) {
> + --pages_to_put;
> + put_page(oldpages[pages_to_put]);
> + }
> + }
> + }
> +
> + spin_unlock(&(pool->lock));
> + kfree(oldpages);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iommu_put_tces);
> +#endif /* CONFIG_IOMMU_API */
> diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
> index 05205cf..660dcc6 100644
> --- a/arch/powerpc/platforms/powernv/pci.c
> +++ b/arch/powerpc/platforms/powernv/pci.c
> @@ -20,6 +20,7 @@
> #include <linux/irq.h>
> #include <linux/io.h>
> #include <linux/msi.h>
> +#include <linux/iommu.h>
>
> #include <asm/sections.h>
> #include <asm/io.h>
> @@ -613,3 +614,137 @@ void __init pnv_pci_init(void)
> ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
> #endif
> }
> +
> +#ifdef CONFIG_IOMMU_API
> +/*
> + * IOMMU groups support required by VFIO
> + */
> +static int add_device(struct device *dev)
> +{
> + struct iommu_table *tbl;
> + int ret = 0;
> +
> + if (WARN_ON(dev->iommu_group)) {
> + printk(KERN_WARNING "tce_vfio: device %s is already in iommu group %d, skipping\n",
> + dev_name(dev),
> + iommu_group_id(dev->iommu_group));
> + return -EBUSY;
> + }
> +
> + tbl = get_iommu_table_base(dev);
> + if (!tbl) {
> + pr_debug("tce_vfio: skipping device %s with no tbl\n",
> + dev_name(dev));
> + return 0;
> + }
> +
> + pr_debug("tce_vfio: adding %s to iommu group %d\n",
> + dev_name(dev), iommu_group_id(tbl->it_group));
> +
> + ret = iommu_group_add_device(tbl->it_group, dev);
> + if (ret < 0)
> + printk(KERN_ERR "tce_vfio: %s has not been added, ret=%d\n",
> + dev_name(dev), ret);
> +
> + return ret;
> +}
> +
> +static void del_device(struct device *dev)
> +{
> + iommu_group_remove_device(dev);
> +}
> +
> +static int iommu_bus_notifier(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct device *dev = data;
> +
> + switch (action) {
> + case BUS_NOTIFY_ADD_DEVICE:
> + return add_device(dev);
> + case BUS_NOTIFY_DEL_DEVICE:
> + del_device(dev);
> + return 0;
> + default:
> + return 0;
> + }
> +}
> +
> +static struct notifier_block tce_iommu_bus_nb = {
> + .notifier_call = iommu_bus_notifier,
> +};
> +
> +static void group_release(void *iommu_data)
> +{
> + struct iommu_table *tbl = iommu_data;
> + tbl->it_group = NULL;
> +}
> +
> +static int __init tce_iommu_init(void)
> +{
> + struct pci_dev *pdev = NULL;
> + struct iommu_table *tbl;
> + struct iommu_group *grp;
> +
> + bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb);
> +
> + /* Allocate and initialize IOMMU groups */
> + for_each_pci_dev(pdev) {
> + tbl = get_iommu_table_base(&pdev->dev);
> + if (!tbl)
> + continue;
> +
> + /* Skip already initialized */
> + if (tbl->it_group)
> + continue;
> +
> + grp = iommu_group_alloc();
> + if (IS_ERR(grp)) {
> + printk(KERN_INFO "tce_vfio: cannot create "
> + "new IOMMU group, ret=%ld\n",
> + PTR_ERR(grp));
> + return PTR_ERR(grp);
> + }
> + tbl->it_group = grp;
> + iommu_group_set_iommudata(grp, tbl, group_release);
> + }
> +
> + /* Add PCI devices to VFIO groups */
> + for_each_pci_dev(pdev)
> + add_device(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static void __exit tce_iommu_cleanup(void)
> +{
> + struct pci_dev *pdev = NULL;
> + struct iommu_table *tbl;
> + struct iommu_group *grp = NULL;
> +
> + bus_unregister_notifier(&pci_bus_type, &tce_iommu_bus_nb);
> +
> + /* Delete PCI devices from VFIO groups */
> + for_each_pci_dev(pdev)
> + del_device(&pdev->dev);
> +
> + /* Release VFIO groups */
> + for_each_pci_dev(pdev) {
> + tbl = get_iommu_table_base(&pdev->dev);
> + if (!tbl)
> + continue;
> + grp = tbl->it_group;
> +
> + /* Skip (already) uninitialized */
> + if (!grp)
> + continue;
> +
> + /* Do actual release, group_release() is expected to work */
> + iommu_group_put(grp);
> + BUG_ON(tbl->it_group);
> + }
> +}
> +
> +module_init(tce_iommu_init);
> +module_exit(tce_iommu_cleanup);
> +#endif /* CONFIG_IOMMU_API */
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index 9f69b56..29d11dc 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -187,4 +187,12 @@ config EXYNOS_IOMMU_DEBUG
>
> Say N unless you need kernel log message for IOMMU debugging
>
> +config SPAPR_TCE_IOMMU
> + bool "sPAPR TCE IOMMU Support"
> + depends on PPC_POWERNV
> + select IOMMU_API
> + help
> + Enables bits of IOMMU API required by VFIO. The iommu_ops is
> + still not implemented.
> +
> endif # IOMMU_SUPPORT
^ permalink raw reply
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: Alex Williamson @ 2012-11-27 4:29 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <50B43C36.60707@ozlabs.ru>
On Tue, 2012-11-27 at 15:06 +1100, Alexey Kardashevskiy wrote:
> On 27/11/12 05:20, Alex Williamson wrote:
> > On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
> >> VFIO implements platform independent stuff such as
> >> a PCI driver, BAR access (via read/write on a file descriptor
> >> or direct mapping when possible) and IRQ signaling.
> >>
> >> The platform dependent part includes IOMMU initialization
> >> and handling. This patch implements an IOMMU driver for VFIO
> >> which does mapping/unmapping pages for the guest IO and
> >> provides information about DMA window (required by a POWERPC
> >> guest).
> >>
> >> The counterpart in QEMU is required to support this functionality.
> >>
> >> Cc: David Gibson <david@gibson.dropbear.id.au>
> >> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> >> ---
> >> drivers/vfio/Kconfig | 6 +
> >> drivers/vfio/Makefile | 1 +
> >> drivers/vfio/vfio_iommu_spapr_tce.c | 247 +++++++++++++++++++++++++++++++++++
> >> include/linux/vfio.h | 20 +++
> >> 4 files changed, 274 insertions(+)
> >> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
> >>
> >> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
> >> index 7cd5dec..b464687 100644
> >> --- a/drivers/vfio/Kconfig
> >> +++ b/drivers/vfio/Kconfig
> >> @@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
> >> depends on VFIO
> >> default n
> >>
> >> +config VFIO_IOMMU_SPAPR_TCE
> >> + tristate
> >> + depends on VFIO && SPAPR_TCE_IOMMU
> >> + default n
> >> +
> >> menuconfig VFIO
> >> tristate "VFIO Non-Privileged userspace driver framework"
> >> depends on IOMMU_API
> >> select VFIO_IOMMU_TYPE1 if X86
> >> + select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
> >> help
> >> VFIO provides a framework for secure userspace device drivers.
> >> See Documentation/vfio.txt for more details.
> >> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
> >> index 2398d4a..72bfabc 100644
> >> --- a/drivers/vfio/Makefile
> >> +++ b/drivers/vfio/Makefile
> >> @@ -1,3 +1,4 @@
> >> obj-$(CONFIG_VFIO) += vfio.o
> >> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
> >> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
> >> obj-$(CONFIG_VFIO_PCI) += pci/
> >> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
> >> new file mode 100644
> >> index 0000000..46a6298
> >> --- /dev/null
> >> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
> >> @@ -0,0 +1,247 @@
> >> +/*
> >> + * VFIO: IOMMU DMA mapping support for TCE on POWER
> >> + *
> >> + * Copyright (C) 2012 IBM Corp. All rights reserved.
> >> + * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * Derived from original vfio_iommu_type1.c:
> >> + * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
> >> + * Author: Alex Williamson <alex.williamson@redhat.com>
> >> + */
> >> +
> >> +#include <linux/module.h>
> >> +#include <linux/pci.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/uaccess.h>
> >> +#include <linux/err.h>
> >> +#include <linux/vfio.h>
> >> +#include <asm/iommu.h>
> >> +
> >> +#define DRIVER_VERSION "0.1"
> >> +#define DRIVER_AUTHOR "aik@ozlabs.ru"
> >> +#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
> >> +
> >> +static void tce_iommu_detach_group(void *iommu_data,
> >> + struct iommu_group *iommu_group);
> >> +
> >> +/*
> >> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
> >> + */
> >> +
> >> +/*
> >> + * The container descriptor supports only a single group per container.
> >> + * Required by the API as the container is not supplied with the IOMMU group
> >> + * at the moment of initialization.
> >> + */
> >> +struct tce_container {
> >> + struct mutex lock;
> >> + struct iommu_table *tbl;
> >> +};
> >> +
> >> +static void *tce_iommu_open(unsigned long arg)
> >> +{
> >> + struct tce_container *container;
> >> +
> >> + if (arg != VFIO_SPAPR_TCE_IOMMU) {
> >> + printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
> >> + return ERR_PTR(-EINVAL);
> >> + }
> >> +
> >> + container = kzalloc(sizeof(*container), GFP_KERNEL);
> >> + if (!container)
> >> + return ERR_PTR(-ENOMEM);
> >> +
> >> + mutex_init(&container->lock);
> >> +
> >> + return container;
> >> +}
> >> +
> >> +static void tce_iommu_release(void *iommu_data)
> >> +{
> >> + struct tce_container *container = iommu_data;
> >> +
> >> + WARN_ON(container->tbl && !container->tbl->it_group);
> >
> > I think your patch ordering is backwards here. it_group isn't added
> > until 2/2. I'd really like to see the arch/powerpc code approved and
> > merged by the powerpc maintainer before we add the code that makes use
> > of it into vfio. Otherwise we just get lots of churn if interfaces
> > change or they disapprove of it altogether.
>
>
> Makes sense, thanks.
>
>
> >> + if (container->tbl && container->tbl->it_group)
> >> + tce_iommu_detach_group(iommu_data, container->tbl->it_group);
> >> +
> >> + mutex_destroy(&container->lock);
> >> +
> >> + kfree(container);
> >> +}
> >> +
> >> +static long tce_iommu_ioctl(void *iommu_data,
> >> + unsigned int cmd, unsigned long arg)
> >> +{
> >> + struct tce_container *container = iommu_data;
> >> + unsigned long minsz;
> >> +
> >> + switch (cmd) {
> >> + case VFIO_CHECK_EXTENSION: {
> >> + return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
> >> + }
> >> + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
> >> + struct vfio_iommu_spapr_tce_info info;
> >> + struct iommu_table *tbl = container->tbl;
> >> +
> >> + if (WARN_ON(!tbl))
> >> + return -ENXIO;
> >> +
> >> + minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
> >> + dma64_window_size);
> >> +
> >> + if (copy_from_user(&info, (void __user *)arg, minsz))
> >> + return -EFAULT;
> >> +
> >> + if (info.argsz < minsz)
> >> + return -EINVAL;
> >> +
> >> + info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
> >> + info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
> >> + info.dma64_window_start = 0;
> >> + info.dma64_window_size = 0;
> >> + info.flags = 0;
> >> +
> >> + if (copy_to_user((void __user *)arg, &info, minsz))
> >> + return -EFAULT;
> >> +
> >> + return 0;
> >> + }
> >> + case VFIO_IOMMU_MAP_DMA: {
> >> + vfio_iommu_spapr_tce_dma_map param;
> >> + struct iommu_table *tbl = container->tbl;
> >> + enum dma_data_direction direction = DMA_NONE;
> >> +
> >> + if (WARN_ON(!tbl))
> >> + return -ENXIO;
> >> +
> >> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
> >> +
> >> + if (copy_from_user(¶m, (void __user *)arg, minsz))
> >> + return -EFAULT;
> >> +
> >> + if (param.argsz < minsz)
> >> + return -EINVAL;
> >> +
> >> + if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
> >> + (param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
> >> + direction = DMA_BIDIRECTIONAL;
> >> + } else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
> >> + direction = DMA_TO_DEVICE;
> >> + } else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
> >> + direction = DMA_FROM_DEVICE;
> >> + }
> >> +
> >> + param.size += param.iova & ~IOMMU_PAGE_MASK;
> >> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >
> > On x86 we force iova, vaddr, and size to all be aligned to the smallest
> > page granularity of the iommu and return -EINVAL if it doesn't fit.
> > What does it imply to the user if they're always aligned to work here?
> > Won't this interface happily map overlapping entries with no indication
> > to the user that the previous mapping is no longer valid?
> > Maybe another reason why a combined unmap/map makes me nervous, we have
> > to assume the user knows what they're doing.
>
>
> I got used to guests which do know what they are doing so I am pretty calm :)
> but ok, I'll move alignment to the QEMU, it makes sense.
>
>
> >> +
> >> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >> + param.vaddr & IOMMU_PAGE_MASK, direction,
> >> + param.size >> IOMMU_PAGE_SHIFT);
> >> + }
> >> + case VFIO_IOMMU_UNMAP_DMA: {
> >> + vfio_iommu_spapr_tce_dma_unmap param;
> >> + struct iommu_table *tbl = container->tbl;
> >> +
> >> + if (WARN_ON(!tbl))
> >> + return -ENXIO;
> >> +
> >> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
> >> +
> >> + if (copy_from_user(¶m, (void __user *)arg, minsz))
> >> + return -EFAULT;
> >> +
> >> + if (param.argsz < minsz)
> >> + return -EINVAL;
> >> +
> >> + param.size += param.iova & ~IOMMU_PAGE_MASK;
> >> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
> >> +
> >> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
> >> + 0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
> >> + }
> >> + default:
> >> + printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
> >
> > pr_warn
> >
> >> + }
> >> +
> >> + return -ENOTTY;
> >> +}
> >> +
> >> +static int tce_iommu_attach_group(void *iommu_data,
> >> + struct iommu_group *iommu_group)
> >> +{
> >> + struct tce_container *container = iommu_data;
> >> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
> >> +
> >> + BUG_ON(!tbl);
> >> + mutex_lock(&container->lock);
> >> + pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
> >> + iommu_group_id(iommu_group), iommu_group);
> >> + if (container->tbl) {
> >> + printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
> >
> > pr_warn
> >
> >> + iommu_group_id(container->tbl->it_group),
> >> + iommu_group_id(iommu_group));
> >> + mutex_unlock(&container->lock);
> >> + return -EBUSY;
> >> + }
> >> +
> >> + container->tbl = tbl;
> >
> > Would it be too much paranoia to clear all the tce here as you do below
> > on detach?
>
> Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e.
> unmaps) the whole DMA window at the boot time.
But that's just one user of this interface, we can't assume they'll all
be so agreeable. If any tces were enabled here, a malicious user would
have a window to host memory, right? Thanks,
Alex
^ permalink raw reply
* Re: [PATCH] vfio powerpc: enabled and supported on powernv platform
From: Alex Williamson @ 2012-11-27 4:23 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Paul Mackerras,
Sethi Varun-B16395, linuxppc-dev@lists.ozlabs.org, David Gibson
In-Reply-To: <50B4334E.2010802@ozlabs.ru>
On Tue, 2012-11-27 at 14:28 +1100, Alexey Kardashevskiy wrote:
> On 27/11/12 05:04, Alex Williamson wrote:
> > On Mon, 2012-11-26 at 08:18 -0700, Alex Williamson wrote:
> >> On Fri, 2012-11-23 at 13:02 +1100, Alexey Kardashevskiy wrote:
> >>> On 22/11/12 22:56, Sethi Varun-B16395 wrote:
> >>>>
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: linux-kernel-owner@vger.kernel.org [mailto:linux-kernel-
> >>>>> owner@vger.kernel.org] On Behalf Of Alex Williamson
> >>>>> Sent: Tuesday, November 20, 2012 11:50 PM
> >>>>> To: Alexey Kardashevskiy
> >>>>> Cc: Benjamin Herrenschmidt; Paul Mackerras; linuxppc-
> >>>>> dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; kvm@vger.kernel.org;
> >>>>> David Gibson
> >>>>> Subject: Re: [PATCH] vfio powerpc: enabled and supported on powernv
> >>>>> platform
> >>>>>
> >>>>> On Tue, 2012-11-20 at 11:48 +1100, Alexey Kardashevskiy wrote:
> >>>>>> VFIO implements platform independent stuff such as a PCI driver, BAR
> >>>>>> access (via read/write on a file descriptor or direct mapping when
> >>>>>> possible) and IRQ signaling.
> >>>>>> The platform dependent part includes IOMMU initialization and
> >>>>>> handling.
> >>>>>>
> >>>>>> This patch initializes IOMMU groups based on the IOMMU configuration
> >>>>>> discovered during the PCI scan, only POWERNV platform is supported at
> >>>>>> the moment.
> >>>>>>
> >>>>>> Also the patch implements an VFIO-IOMMU driver which manages DMA
> >>>>>> mapping/unmapping requests coming from the client (now QEMU). It also
> >>>>>> returns a DMA window information to let the guest initialize the
> >>>>>> device tree for a guest OS properly. Although this driver has been
> >>>>>> tested only on POWERNV, it should work on any platform supporting TCE
> >>>>>> tables.
> >>>>>>
> >>>>>> To enable VFIO on POWER, enable SPAPR_TCE_IOMMU config option.
> >>>>>>
> >>>>>> Cc: David Gibson <david@gibson.dropbear.id.au>
> >>>>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> >>>>>> ---
> >>>>>> arch/powerpc/include/asm/iommu.h | 6 +
> >>>>>> arch/powerpc/kernel/iommu.c | 140 +++++++++++++++++++
> >>>>>> arch/powerpc/platforms/powernv/pci.c | 135 +++++++++++++++++++
> >>>>>> drivers/iommu/Kconfig | 8 ++
> >>>>>> drivers/vfio/Kconfig | 6 +
> >>>>>> drivers/vfio/Makefile | 1 +
> >>>>>> drivers/vfio/vfio_iommu_spapr_tce.c | 247
> >>>>> ++++++++++++++++++++++++++++++++++
> >>>>>> include/linux/vfio.h | 20 +++
> >>>>>> 8 files changed, 563 insertions(+)
> >>>>>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
> >>>>>>
> >>>>>> diff --git a/arch/powerpc/include/asm/iommu.h
> >>>>>> b/arch/powerpc/include/asm/iommu.h
> >>>>>> index cbfe678..5ba66cb 100644
> >>>>>> --- a/arch/powerpc/include/asm/iommu.h
> >>>>>> +++ b/arch/powerpc/include/asm/iommu.h
> >>>>>> @@ -64,30 +64,33 @@ struct iommu_pool { }
> >>>>>> ____cacheline_aligned_in_smp;
> >>>>>>
> >>>>>> struct iommu_table {
> >>>>>> unsigned long it_busno; /* Bus number this table belongs to */
> >>>>>> unsigned long it_size; /* Size of iommu table in entries */
> >>>>>> unsigned long it_offset; /* Offset into global table */
> >>>>>> unsigned long it_base; /* mapped address of tce table */
> >>>>>> unsigned long it_index; /* which iommu table this is */
> >>>>>> unsigned long it_type; /* type: PCI or Virtual Bus */
> >>>>>> unsigned long it_blocksize; /* Entries in each block (cacheline)
> >>>>> */
> >>>>>> unsigned long poolsize;
> >>>>>> unsigned long nr_pools;
> >>>>>> struct iommu_pool large_pool;
> >>>>>> struct iommu_pool pools[IOMMU_NR_POOLS];
> >>>>>> unsigned long *it_map; /* A simple allocation bitmap for now
> >>>>> */
> >>>>>> +#ifdef CONFIG_IOMMU_API
> >>>>>> + struct iommu_group *it_group;
> >>>>>> +#endif
> >>>>>> };
> >>>>>>
> >>>>>> struct scatterlist;
> >>>>>>
> >>>>>> static inline void set_iommu_table_base(struct device *dev, void
> >>>>>> *base) {
> >>>>>> dev->archdata.dma_data.iommu_table_base = base; }
> >>>>>>
> >>>>>> static inline void *get_iommu_table_base(struct device *dev) {
> >>>>>> return dev->archdata.dma_data.iommu_table_base;
> >>>>>> }
> >>>>>>
> >>>>>> /* Frees table for an individual device node */ @@ -135,17 +138,20 @@
> >>>>>> static inline void pci_iommu_init(void) { } extern void
> >>>>>> alloc_dart_table(void); #if defined(CONFIG_PPC64) &&
> >>>>>> defined(CONFIG_PM) static inline void iommu_save(void) {
> >>>>>> if (ppc_md.iommu_save)
> >>>>>> ppc_md.iommu_save();
> >>>>>> }
> >>>>>>
> >>>>>> static inline void iommu_restore(void) {
> >>>>>> if (ppc_md.iommu_restore)
> >>>>>> ppc_md.iommu_restore();
> >>>>>> }
> >>>>>> #endif
> >>>>>>
> >>>>>> +extern long iommu_put_tces(struct iommu_table *tbl, unsigned long
> >>>>> entry, uint64_t tce,
> >>>>>> + enum dma_data_direction direction, unsigned long pages);
> >>>>>> +
> >>>>>> #endif /* __KERNEL__ */
> >>>>>> #endif /* _ASM_IOMMU_H */
> >>>>>> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
> >>>>>> index ff5a6ce..94f614b 100644
> >>>>>> --- a/arch/powerpc/kernel/iommu.c
> >>>>>> +++ b/arch/powerpc/kernel/iommu.c
> >>>>>> @@ -32,30 +32,31 @@
> >>>>>> #include <linux/dma-mapping.h>
> >>>>>> #include <linux/bitmap.h>
> >>>>>> #include <linux/iommu-helper.h>
> >>>>>> #include <linux/crash_dump.h>
> >>>>>> #include <linux/hash.h>
> >>>>>> #include <linux/fault-inject.h>
> >>>>>> #include <linux/pci.h>
> >>>>>> #include <asm/io.h>
> >>>>>> #include <asm/prom.h>
> >>>>>> #include <asm/iommu.h>
> >>>>>> #include <asm/pci-bridge.h>
> >>>>>> #include <asm/machdep.h>
> >>>>>> #include <asm/kdump.h>
> >>>>>> #include <asm/fadump.h>
> >>>>>> #include <asm/vio.h>
> >>>>>> +#include <asm/tce.h>
> >>>>>>
> >>>>>> #define DBG(...)
> >>>>>>
> >>>>>> static int novmerge;
> >>>>>>
> >>>>>> static void __iommu_free(struct iommu_table *, dma_addr_t, unsigned
> >>>>>> int);
> >>>>>>
> >>>>>> static int __init setup_iommu(char *str) {
> >>>>>> if (!strcmp(str, "novmerge"))
> >>>>>> novmerge = 1;
> >>>>>> else if (!strcmp(str, "vmerge"))
> >>>>>> novmerge = 0;
> >>>>>> return 1;
> >>>>>> }
> >>>>>> @@ -844,15 +845,154 @@ void *iommu_alloc_coherent(struct device *dev,
> >>>>>> struct iommu_table *tbl, }
> >>>>>>
> >>>>>> void iommu_free_coherent(struct iommu_table *tbl, size_t size,
> >>>>>> void *vaddr, dma_addr_t dma_handle) {
> >>>>>> if (tbl) {
> >>>>>> unsigned int nio_pages;
> >>>>>>
> >>>>>> size = PAGE_ALIGN(size);
> >>>>>> nio_pages = size >> IOMMU_PAGE_SHIFT;
> >>>>>> iommu_free(tbl, dma_handle, nio_pages);
> >>>>>> size = PAGE_ALIGN(size);
> >>>>>> free_pages((unsigned long)vaddr, get_order(size));
> >>>>>> }
> >>>>>> }
> >>>>>> +
> >>>>>> +#ifdef CONFIG_IOMMU_API
> >>>>>> +/*
> >>>>>> + * SPAPR TCE API
> >>>>>> + */
> >>>>>> +static struct page *free_tce(struct iommu_table *tbl, unsigned long
> >>>>>> +entry) {
> >>>>>> + struct page *page = NULL;
> >>>>>
> >>>>> NULL initialization doesn't appear to be necessary
> >>>>>
> >>>>>> + unsigned long oldtce;
> >>>>>> +
> >>>>>> + oldtce = ppc_md.tce_get(tbl, entry);
> >>>>>> +
> >>>>>> + if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
> >>>>>> + return NULL;
> >>>>>> +
> >>>>>> + page = pfn_to_page(oldtce >> PAGE_SHIFT);
> >>>>>> +
> >>>>>> + WARN_ON(!page);
> >>>>>> + if (page && (oldtce & TCE_PCI_WRITE))
> >>>>>> + SetPageDirty(page);
> >>>>>> + ppc_md.tce_free(tbl, entry, 1);
> >>>>>> +
> >>>>>> + return page;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int put_tce(struct iommu_table *tbl, unsigned long entry,
> >>>>>> + uint64_t tce, enum dma_data_direction direction) {
> >>>>>> + int ret;
> >>>>>> + struct page *page = NULL;
> >>>>>> + unsigned long kva, offset;
> >>>>>> +
> >>>>>> + /* Map new TCE */
> >>>>>> + offset = (tce & IOMMU_PAGE_MASK) - (tce & PAGE_MASK);
> >>>>>> + ret = get_user_pages_fast(tce & PAGE_MASK, 1,
> >>>>>> + direction != DMA_TO_DEVICE, &page);
> >>>>>> + if (ret < 1) {
> >>>>>> + printk(KERN_ERR "tce_vfio: get_user_pages_fast failed
> >>>>> tce=%llx ioba=%lx ret=%d\n",
> >>>>>> + tce, entry << IOMMU_PAGE_SHIFT, ret);
> >>>>>> + if (!ret)
> >>>>>> + ret = -EFAULT;
> >>>>>
> >>>>> Missing return ret? Otherwise we've got some bogus uses of page below
> >>>>> and we're setting ret for no reason here.
> >>>>>
> >>>>>> + }
> >>>>>> +
> >>>>>> + kva = (unsigned long) page_address(page);
> >>>>>> + kva += offset;
> >>>>>> +
> >>>>>> + /* tce_build receives a virtual address */
> >>>>>> + entry += tbl->it_offset; /* Offset into real TCE table */
> >>>>>> + ret = ppc_md.tce_build(tbl, entry, 1, kva, direction, NULL);
> >>>>>> +
> >>>>>> + /* tce_build() only returns non-zero for transient errors */
> >>>>>> + if (unlikely(ret)) {
> >>>>>> + printk(KERN_ERR "tce_vfio: tce_put failed on tce=%llx
> >>>>> ioba=%lx kva=%lx ret=%d\n",
> >>>>>> + tce, entry << IOMMU_PAGE_SHIFT, kva, ret);
> >>>>>> + put_page(page);
> >>>>>> + return -EIO;
> >>>>>> + }
> >>>>>> +
> >>>>>> + return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static void tce_flush(struct iommu_table *tbl) {
> >>>>>> + /* Flush/invalidate TLB caches if necessary */
> >>>>>> + if (ppc_md.tce_flush)
> >>>>>> + ppc_md.tce_flush(tbl);
> >>>>>> +
> >>>>>> + /* Make sure updates are seen by hardware */
> >>>>>> + mb();
> >>>>>> +}
> >>>>>> +
> >>>>>> +long iommu_put_tces(struct iommu_table *tbl, unsigned long entry,
> >>>>> uint64_t tce,
> >>>>>> + enum dma_data_direction direction, unsigned long pages) {
> >>>>>> + int i, ret = 0, pages_to_put = 0;
> >>>>>> + struct page *page;
> >>>>>> + struct iommu_pool *pool = get_pool(tbl, entry);
> >>>>>> + struct page **oldpages;
> >>>>>> + const int oldpagesnum = PAGE_SIZE/sizeof(*oldpages);
> >>>>>> +
> >>>>>> + BUILD_BUG_ON(PAGE_SIZE < IOMMU_PAGE_SIZE);
> >>>>>> +
> >>>>>> + /* Handle a single page request without allocation
> >>>>>> + of pages-to-release array */
> >>>>>> + if (pages == 1) {
> >>>>>> + spin_lock(&(pool->lock));
> >>>>>> + page = free_tce(tbl, entry);
> >>>>>> +
> >>>>>> + if (direction != DMA_NONE)
> >>>>>> + ret = put_tce(tbl, entry, tce, direction);
> >>>>>> +
> >>>>>> + tce_flush(tbl);
> >>>>>> +
> >>>>>> + if (page)
> >>>>>> + put_page(page);
> >>>>>> +
> >>>>>> + spin_unlock(&(pool->lock));
> >>>>>> + return ret;
> >>>>>> + }
> >>>>>> +
> >>>>>> + /* Releasing multiple pages */
> >>>>>> + /* Allocate an array for pages to be released after TCE table
> >>>>>> + is updated */
> >>>>>> + oldpages = kmalloc(PAGE_SIZE, GFP_KERNEL);
> >>>>>> + if (!oldpages)
> >>>>>> + return -ENOMEM;
> >>>>>> +
> >>>>>> + spin_lock(&(pool->lock));
> >>>>>> +
> >>>>>> + for (i = 0; (i < pages) && !ret; ++i, ++entry, tce +=
> >>>>> IOMMU_PAGE_SIZE) {
> >>>>>> + page = free_tce(tbl, entry);
> >>>>>> + if (page) {
> >>>>>> + oldpages[pages_to_put] = page;
> >>>>>> + ++pages_to_put;
> >>>>>> + }
> >>>>>> +
> >>>>>> + if (direction != DMA_NONE)
> >>>>>> + ret = put_tce(tbl, entry, tce, direction);
> >>>>>> +
> >>>>>> + /* Release old pages if we reached the end of oldpages[] or
> >>>>>> + it is the last page or we are about to exit the loop */
> >>>>>> + if ((pages_to_put == oldpagesnum) || (i == pages - 1) || ret)
> >>>>> {
> >>>>>> + tce_flush(tbl);
> >>>>>
> >>>>> Avoiding tce_flush() is the reason for all this extra overhead, right?
> >>>>> I wonder if it'd be cleaner separating map vs unmap, where the map case
> >>>>> can avoid the oldpages array... but that means inserting new mappings on
> >>>>> top of old ones wouldn't put the pages.
> >>>
> >>>
> >>> Yes, we do not want to loose pages if the guest forgot to unmap them.
> >>
> >> Hmm, does that mean we're not actively clearing tce entries or somehow
> >> disabling the iommu window when the iommu is released through vfio?
> >
> > Ok, I see tces are put on shutdown via tce_iommu_detach_group, so you're
> > more concerned about the guest simply mapping over top of it's own
> > mappings. Is that common? Is it common enough for every multi-page
> > mapping to assume it will happen? I know this is a performance
> > sensitive path for you and it seems like a map-only w/ fallback to
> > unmap, remap would be better in the general case.
>
>
> I do not get it. Where exactly does the performance suffer?
> iommu_put_tces() with non zero "tce" (i.e. "map") has to check if the entry
> is not used, at least to return EBUSY when it is, and this check is
> performed. If it is zero, there is no overhead at all. And it is going to
> be the 99.(9)% case as the guest (un)maps one page per call.
I was mostly concerned about the kmalloc in your mapping path. If you
had a map-only path it could scan the whole range to verify it's clear
and return EBUSY w/o allocating a buffer. A second pass could do the
actual mappings. Maybe it's not much of a win if you expect 99% of the
mappings to be single pages but since you effectively have a pv iommu
interface I wouldn't be surprised if they get batched in the guest.
> Generally speaking we want to move "put tce" completely to the kernel for
> the (much) better performance and vfio won't be dealing with it all.
Right, but let's not use that as an excuse to be lazy and not ask if we
can do better here.
> We already agreed that SPAPR TCE driver uses x86 (aka type1) API but I do
> not see why the powerpc implementation should look x86 alike as it still
> operates with powerpc machine dependent callbacks so the reader has to have
> some powerpc knowledge.
I'm only using x86 as an example because it's the only one we have. I
don't think anything we're talking about here is x86-ish or powerpc-ish.
There's a kmalloc in a performance path and I'm asking if we can get rid
of it. I'm also nervous that we're silently doing fixups on user
parameters to adjust mapping sizes and clear overlaps without any
warning to the user. Thanks,
Alex
^ permalink raw reply
* Re: [PATCH 1/2] vfio powerpc: implemented IOMMU driver for VFIO
From: Alexey Kardashevskiy @ 2012-11-27 4:06 UTC (permalink / raw)
To: Alex Williamson
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1353954038.1809.114.camel@bling.home>
On 27/11/12 05:20, Alex Williamson wrote:
> On Fri, 2012-11-23 at 20:03 +1100, Alexey Kardashevskiy wrote:
>> VFIO implements platform independent stuff such as
>> a PCI driver, BAR access (via read/write on a file descriptor
>> or direct mapping when possible) and IRQ signaling.
>>
>> The platform dependent part includes IOMMU initialization
>> and handling. This patch implements an IOMMU driver for VFIO
>> which does mapping/unmapping pages for the guest IO and
>> provides information about DMA window (required by a POWERPC
>> guest).
>>
>> The counterpart in QEMU is required to support this functionality.
>>
>> Cc: David Gibson <david@gibson.dropbear.id.au>
>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> ---
>> drivers/vfio/Kconfig | 6 +
>> drivers/vfio/Makefile | 1 +
>> drivers/vfio/vfio_iommu_spapr_tce.c | 247 +++++++++++++++++++++++++++++++++++
>> include/linux/vfio.h | 20 +++
>> 4 files changed, 274 insertions(+)
>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>>
>> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
>> index 7cd5dec..b464687 100644
>> --- a/drivers/vfio/Kconfig
>> +++ b/drivers/vfio/Kconfig
>> @@ -3,10 +3,16 @@ config VFIO_IOMMU_TYPE1
>> depends on VFIO
>> default n
>>
>> +config VFIO_IOMMU_SPAPR_TCE
>> + tristate
>> + depends on VFIO && SPAPR_TCE_IOMMU
>> + default n
>> +
>> menuconfig VFIO
>> tristate "VFIO Non-Privileged userspace driver framework"
>> depends on IOMMU_API
>> select VFIO_IOMMU_TYPE1 if X86
>> + select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
>> help
>> VFIO provides a framework for secure userspace device drivers.
>> See Documentation/vfio.txt for more details.
>> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
>> index 2398d4a..72bfabc 100644
>> --- a/drivers/vfio/Makefile
>> +++ b/drivers/vfio/Makefile
>> @@ -1,3 +1,4 @@
>> obj-$(CONFIG_VFIO) += vfio.o
>> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
>> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
>> obj-$(CONFIG_VFIO_PCI) += pci/
>> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
>> new file mode 100644
>> index 0000000..46a6298
>> --- /dev/null
>> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
>> @@ -0,0 +1,247 @@
>> +/*
>> + * VFIO: IOMMU DMA mapping support for TCE on POWER
>> + *
>> + * Copyright (C) 2012 IBM Corp. All rights reserved.
>> + * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * Derived from original vfio_iommu_type1.c:
>> + * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
>> + * Author: Alex Williamson <alex.williamson@redhat.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/err.h>
>> +#include <linux/vfio.h>
>> +#include <asm/iommu.h>
>> +
>> +#define DRIVER_VERSION "0.1"
>> +#define DRIVER_AUTHOR "aik@ozlabs.ru"
>> +#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
>> +
>> +static void tce_iommu_detach_group(void *iommu_data,
>> + struct iommu_group *iommu_group);
>> +
>> +/*
>> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
>> + */
>> +
>> +/*
>> + * The container descriptor supports only a single group per container.
>> + * Required by the API as the container is not supplied with the IOMMU group
>> + * at the moment of initialization.
>> + */
>> +struct tce_container {
>> + struct mutex lock;
>> + struct iommu_table *tbl;
>> +};
>> +
>> +static void *tce_iommu_open(unsigned long arg)
>> +{
>> + struct tce_container *container;
>> +
>> + if (arg != VFIO_SPAPR_TCE_IOMMU) {
>> + printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + container = kzalloc(sizeof(*container), GFP_KERNEL);
>> + if (!container)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + mutex_init(&container->lock);
>> +
>> + return container;
>> +}
>> +
>> +static void tce_iommu_release(void *iommu_data)
>> +{
>> + struct tce_container *container = iommu_data;
>> +
>> + WARN_ON(container->tbl && !container->tbl->it_group);
>
> I think your patch ordering is backwards here. it_group isn't added
> until 2/2. I'd really like to see the arch/powerpc code approved and
> merged by the powerpc maintainer before we add the code that makes use
> of it into vfio. Otherwise we just get lots of churn if interfaces
> change or they disapprove of it altogether.
Makes sense, thanks.
>> + if (container->tbl && container->tbl->it_group)
>> + tce_iommu_detach_group(iommu_data, container->tbl->it_group);
>> +
>> + mutex_destroy(&container->lock);
>> +
>> + kfree(container);
>> +}
>> +
>> +static long tce_iommu_ioctl(void *iommu_data,
>> + unsigned int cmd, unsigned long arg)
>> +{
>> + struct tce_container *container = iommu_data;
>> + unsigned long minsz;
>> +
>> + switch (cmd) {
>> + case VFIO_CHECK_EXTENSION: {
>> + return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
>> + }
>> + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
>> + struct vfio_iommu_spapr_tce_info info;
>> + struct iommu_table *tbl = container->tbl;
>> +
>> + if (WARN_ON(!tbl))
>> + return -ENXIO;
>> +
>> + minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
>> + dma64_window_size);
>> +
>> + if (copy_from_user(&info, (void __user *)arg, minsz))
>> + return -EFAULT;
>> +
>> + if (info.argsz < minsz)
>> + return -EINVAL;
>> +
>> + info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
>> + info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
>> + info.dma64_window_start = 0;
>> + info.dma64_window_size = 0;
>> + info.flags = 0;
>> +
>> + if (copy_to_user((void __user *)arg, &info, minsz))
>> + return -EFAULT;
>> +
>> + return 0;
>> + }
>> + case VFIO_IOMMU_MAP_DMA: {
>> + vfio_iommu_spapr_tce_dma_map param;
>> + struct iommu_table *tbl = container->tbl;
>> + enum dma_data_direction direction = DMA_NONE;
>> +
>> + if (WARN_ON(!tbl))
>> + return -ENXIO;
>> +
>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
>> +
>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
>> + return -EFAULT;
>> +
>> + if (param.argsz < minsz)
>> + return -EINVAL;
>> +
>> + if ((param.flags & VFIO_DMA_MAP_FLAG_READ) &&
>> + (param.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
>> + direction = DMA_BIDIRECTIONAL;
>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
>> + direction = DMA_TO_DEVICE;
>> + } else if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) {
>> + direction = DMA_FROM_DEVICE;
>> + }
>> +
>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>
> On x86 we force iova, vaddr, and size to all be aligned to the smallest
> page granularity of the iommu and return -EINVAL if it doesn't fit.
> What does it imply to the user if they're always aligned to work here?
> Won't this interface happily map overlapping entries with no indication
> to the user that the previous mapping is no longer valid?
> Maybe another reason why a combined unmap/map makes me nervous, we have
> to assume the user knows what they're doing.
I got used to guests which do know what they are doing so I am pretty calm :)
but ok, I'll move alignment to the QEMU, it makes sense.
>> +
>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>> + param.vaddr & IOMMU_PAGE_MASK, direction,
>> + param.size >> IOMMU_PAGE_SHIFT);
>> + }
>> + case VFIO_IOMMU_UNMAP_DMA: {
>> + vfio_iommu_spapr_tce_dma_unmap param;
>> + struct iommu_table *tbl = container->tbl;
>> +
>> + if (WARN_ON(!tbl))
>> + return -ENXIO;
>> +
>> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
>> +
>> + if (copy_from_user(¶m, (void __user *)arg, minsz))
>> + return -EFAULT;
>> +
>> + if (param.argsz < minsz)
>> + return -EINVAL;
>> +
>> + param.size += param.iova & ~IOMMU_PAGE_MASK;
>> + param.size = _ALIGN_UP(param.size, IOMMU_PAGE_SIZE);
>> +
>> + return iommu_put_tces(tbl, param.iova >> IOMMU_PAGE_SHIFT,
>> + 0, DMA_NONE, param.size >> IOMMU_PAGE_SHIFT);
>> + }
>> + default:
>> + printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
>
> pr_warn
>
>> + }
>> +
>> + return -ENOTTY;
>> +}
>> +
>> +static int tce_iommu_attach_group(void *iommu_data,
>> + struct iommu_group *iommu_group)
>> +{
>> + struct tce_container *container = iommu_data;
>> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
>> +
>> + BUG_ON(!tbl);
>> + mutex_lock(&container->lock);
>> + pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
>> + iommu_group_id(iommu_group), iommu_group);
>> + if (container->tbl) {
>> + printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
>
> pr_warn
>
>> + iommu_group_id(container->tbl->it_group),
>> + iommu_group_id(iommu_group));
>> + mutex_unlock(&container->lock);
>> + return -EBUSY;
>> + }
>> +
>> + container->tbl = tbl;
>
> Would it be too much paranoia to clear all the tce here as you do below
> on detach?
Guess so. I do unmap on detach() and the guest calls put_tce(0) (i.e.
unmaps) the whole DMA window at the boot time.
> ie. is there any risk that there's leftover programming?
> x86 allocates a new domain on open of the iommu, so we always start out
> clean.
>> + mutex_unlock(&container->lock);
>> +
>> + return 0;
>> +}
>> +
>> +static void tce_iommu_detach_group(void *iommu_data,
>> + struct iommu_group *iommu_group)
>> +{
>> + struct tce_container *container = iommu_data;
>> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
>> +
>> + BUG_ON(!tbl);
>> + mutex_lock(&container->lock);
>> + if (tbl != container->tbl) {
>> + printk(KERN_WARNING "tce_vfio: detaching group #%u, expected group is #%u\n",
>
> pr_warn
>
>> + iommu_group_id(iommu_group),
>> + iommu_group_id(tbl->it_group));
>> + } else {
>> +
>> + pr_debug("tce_vfio: detaching group #%u from iommu %p\n",
>> + iommu_group_id(iommu_group), iommu_group);
>> +
>> + iommu_put_tces(tbl, tbl->it_offset, 0, DMA_NONE, tbl->it_size);
>
> So this cleans out any mappings when vfio is closed, good.
>
>> + container->tbl = NULL;
>> + }
>> + mutex_unlock(&container->lock);
>> +}
>> +
>> +const struct vfio_iommu_driver_ops tce_iommu_driver_ops = {
>> + .name = "iommu-vfio-powerpc",
>> + .owner = THIS_MODULE,
>> + .open = tce_iommu_open,
>> + .release = tce_iommu_release,
>> + .ioctl = tce_iommu_ioctl,
>> + .attach_group = tce_iommu_attach_group,
>> + .detach_group = tce_iommu_detach_group,
>> +};
>> +
>> +static int __init tce_iommu_init(void)
>> +{
>> + return vfio_register_iommu_driver(&tce_iommu_driver_ops);
>> +}
>> +
>> +static void __exit tce_iommu_cleanup(void)
>> +{
>> + vfio_unregister_iommu_driver(&tce_iommu_driver_ops);
>> +}
>> +
>> +module_init(tce_iommu_init);
>> +module_exit(tce_iommu_cleanup);
>> +
>> +MODULE_VERSION(DRIVER_VERSION);
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR(DRIVER_AUTHOR);
>> +MODULE_DESCRIPTION(DRIVER_DESC);
>> +
>> diff --git a/include/linux/vfio.h b/include/linux/vfio.h
>> index 0a4f180..3ecd65c 100644
>> --- a/include/linux/vfio.h
>> +++ b/include/linux/vfio.h
>> @@ -99,6 +99,7 @@ extern void vfio_unregister_iommu_driver(
>> /* Extensions */
>>
>> #define VFIO_TYPE1_IOMMU 1
>> +#define VFIO_SPAPR_TCE_IOMMU 2
>>
>> /*
>> * The IOCTL interface is designed for extensibility by embedding the
>> @@ -442,4 +443,23 @@ struct vfio_iommu_type1_dma_unmap {
>>
>> #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
>>
>> +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */
>> +
>> +struct vfio_iommu_spapr_tce_info {
>> + __u32 argsz;
>> + __u32 flags;
>> + __u32 dma32_window_start;
>> + __u32 dma32_window_size;
>> + __u64 dma64_window_start;
>> + __u64 dma64_window_size;
>> +};
>
> Is there anything we can document about this?
I'll put some.
> It should probably list that size is in bytes. Is there any need to communicate the IOMMU page
> size here?
It is always 4k. I'll put it to comments.
>> +
>> +#define VFIO_IOMMU_SPAPR_TCE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
>> +
>> +/* Reuse type1 map/unmap structs as they are the same at the moment */
>> +typedef struct vfio_iommu_type1_dma_map vfio_iommu_spapr_tce_dma_map;
>> +typedef struct vfio_iommu_type1_dma_unmap vfio_iommu_spapr_tce_dma_unmap;
>> +
>> +/* ***************************************************************** */
>> +
>> #endif /* VFIO_H */
>
> Thanks,
>
> Alex
>
>
>
--
Alexey
^ permalink raw reply
* [PATCH 3/3] powerpc: Build kernel with -mcmodel=medium
From: Anton Blanchard @ 2012-11-27 3:41 UTC (permalink / raw)
To: benh, paulus, amodra; +Cc: linuxppc-dev
In-Reply-To: <20121127143903.4b5cab17@kryten>
Finally remove the two level TOC and build with -mcmodel=medium.
Unfortunately we can't build modules with -mcmodel=medium due to
the tricks the kernel module loader plays with percpu data:
# -mcmodel=medium breaks modules because it uses 32bit offsets from
# the TOC pointer to create pointers where possible. Pointers into the
# percpu data area are created by this method.
#
# The kernel module loader relocates the percpu data section from the
# original location (starting with 0xd...) to somewhere in the base
# kernel percpu data space (starting with 0xc...). We need a full
# 64bit relocation for this to work, hence -mcmodel=large.
On older kernels we fall back to the two level TOC (-mminimal-toc)
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: b/arch/powerpc/Makefile
===================================================================
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -67,7 +67,24 @@ LDFLAGS_vmlinux-y := -Bstatic
LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie
LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y)
-CFLAGS-$(CONFIG_PPC64) := -mminimal-toc -mtraceback=no -mcall-aixdesc
+ifeq ($(CONFIG_PPC64),y)
+ifeq ($(call cc-option-yn,-mcmodel=medium),y)
+ # -mcmodel=medium breaks modules because it uses 32bit offsets from
+ # the TOC pointer to create pointers where possible. Pointers into the
+ # percpu data area are created by this method.
+ #
+ # The kernel module loader relocates the percpu data section from the
+ # original location (starting with 0xd...) to somewhere in the base
+ # kernel percpu data space (starting with 0xc...). We need a full
+ # 64bit relocation for this to work, hence -mcmodel=large.
+ KBUILD_CFLAGS_MODULE += -mcmodel=large
+else
+ export NO_MINIMAL_TOC := -mno-minimal-toc
+endif
+endif
+
+CFLAGS-$(CONFIG_PPC64) := -mtraceback=no -mcall-aixdesc
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc)
CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple
CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4)
Index: b/arch/powerpc/kernel/Makefile
===================================================================
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -7,7 +7,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UT
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
ifeq ($(CONFIG_PPC64),y)
-CFLAGS_prom_init.o += -mno-minimal-toc
+CFLAGS_prom_init.o += $(NO_MINIMAL_TOC)
endif
ifeq ($(CONFIG_PPC32),y)
CFLAGS_prom_init.o += -fPIC
Index: b/arch/powerpc/lib/Makefile
===================================================================
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -4,7 +4,7 @@
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
CFLAGS_REMOVE_code-patching.o = -pg
CFLAGS_REMOVE_feature-fixups.o = -pg
Index: b/arch/powerpc/mm/Makefile
===================================================================
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -4,7 +4,7 @@
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
obj-y := fault.o mem.o pgtable.o gup.o \
init_$(CONFIG_WORD_SIZE).o \
Index: b/arch/powerpc/oprofile/Makefile
===================================================================
--- a/arch/powerpc/oprofile/Makefile
+++ b/arch/powerpc/oprofile/Makefile
@@ -1,6 +1,6 @@
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
obj-$(CONFIG_OPROFILE) += oprofile.o
Index: b/arch/powerpc/platforms/pseries/Makefile
===================================================================
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -1,4 +1,4 @@
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG
obj-y := lpar.o hvCall.o nvram.o reconfig.o \
Index: b/arch/powerpc/sysdev/Makefile
===================================================================
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -1,6 +1,6 @@
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
Index: b/arch/powerpc/xmon/Makefile
===================================================================
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -4,7 +4,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -
GCOV_PROFILE := n
-ccflags-$(CONFIG_PPC64) := -mno-minimal-toc
+ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
obj-y += xmon.o nonstdio.o
Index: b/arch/powerpc/platforms/wsp/Makefile
===================================================================
--- a/arch/powerpc/platforms/wsp/Makefile
+++ b/arch/powerpc/platforms/wsp/Makefile
@@ -1,4 +1,4 @@
-ccflags-y += -mno-minimal-toc
+ccflags-y += $(NO_MINIMAL_TOC)
obj-y += setup.o ics.o wsp.o
obj-$(CONFIG_PPC_PSR2) += psr2.o
Index: b/arch/powerpc/kernel/head_64.S
===================================================================
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -169,6 +169,7 @@ _GLOBAL(generic_secondary_thread_init)
/* get a valid TOC pointer, wherever we're mapped at */
bl .relative_toc
+ tovirt(r2,r2)
#ifdef CONFIG_PPC_BOOK3E
/* Book3E initialization */
@@ -195,6 +196,7 @@ _GLOBAL(generic_secondary_smp_init)
/* get a valid TOC pointer, wherever we're mapped at */
bl .relative_toc
+ tovirt(r2,r2)
#ifdef CONFIG_PPC_BOOK3E
/* Book3E initialization */
@@ -531,6 +533,7 @@ _GLOBAL(pmac_secondary_start)
/* get TOC pointer (real address) */
bl .relative_toc
+ tovirt(r2,r2)
/* Copy some CPU settings from CPU 0 */
bl .__restore_cpu_ppc970
@@ -665,6 +668,13 @@ _GLOBAL(enable_64b_mode)
* This puts the TOC pointer into r2, offset by 0x8000 (as expected
* by the toolchain). It computes the correct value for wherever we
* are running at the moment, using position-independent code.
+ *
+ * Note: The compiler constructs pointers using offsets from the
+ * TOC in -mcmodel=medium mode. After we relocate to 0 but before
+ * the MMU is on we need our TOC to be a virtual address otherwise
+ * these pointers will be real addresses which may get stored and
+ * accessed later with the MMU on. We use tovirt() at the call
+ * sites to handle this.
*/
_GLOBAL(relative_toc)
mflr r0
@@ -681,8 +691,9 @@ p_toc: .llong __toc_start + 0x8000 - 0b
* This is where the main kernel code starts.
*/
_INIT_STATIC(start_here_multiplatform)
- /* set up the TOC (real address) */
- bl .relative_toc
+ /* set up the TOC */
+ bl .relative_toc
+ tovirt(r2,r2)
/* Clear out the BSS. It may have been done in prom_init,
* already but that's irrelevant since prom_init will soon
Index: b/arch/powerpc/kernel/module_64.c
===================================================================
--- a/arch/powerpc/kernel/module_64.c
+++ b/arch/powerpc/kernel/module_64.c
@@ -386,6 +386,14 @@ int apply_relocate_add(Elf64_Shdr *sechd
| (value & 0xffff);
break;
+ case R_PPC64_TOC16_LO:
+ /* Subtract TOC pointer */
+ value -= my_r2(sechdrs, me);
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
case R_PPC64_TOC16_DS:
/* Subtract TOC pointer */
value -= my_r2(sechdrs, me);
@@ -399,6 +407,28 @@ int apply_relocate_add(Elf64_Shdr *sechd
| (value & 0xfffc);
break;
+ case R_PPC64_TOC16_LO_DS:
+ /* Subtract TOC pointer */
+ value -= my_r2(sechdrs, me);
+ if ((value & 3) != 0) {
+ printk("%s: bad TOC16_LO_DS relocation (%lu)\n",
+ me->name, value);
+ return -ENOEXEC;
+ }
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xfffc)
+ | (value & 0xfffc);
+ break;
+
+ case R_PPC64_TOC16_HA:
+ /* Subtract TOC pointer */
+ value -= my_r2(sechdrs, me);
+ value = ((value + 0x8000) >> 16);
+ *((uint16_t *) location)
+ = (*((uint16_t *) location) & ~0xffff)
+ | (value & 0xffff);
+ break;
+
case R_PPC_REL24:
/* FIXME: Handle weak symbols here --RR */
if (sym->st_shndx == SHN_UNDEF) {
^ permalink raw reply
* [PATCH 2/3] powerpc: Remove RELOC() macro
From: Anton Blanchard @ 2012-11-27 3:40 UTC (permalink / raw)
To: benh, paulus, amodra; +Cc: linuxppc-dev
In-Reply-To: <20121127143903.4b5cab17@kryten>
Now we relocate prom_init.c on 64bit we can finally remove the
nasty RELOC() macro.
Finally a patch that I can claim has a net positive effect on
the kernel. It doesn't happen very often.
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: b/arch/powerpc/kernel/prom_init.c
===================================================================
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -79,7 +79,6 @@
* On ppc64, 64 bit values are truncated to 32 bits (and
* fortunately don't get interpreted as two arguments).
*/
-#define RELOC(x) (x)
#define ADDR(x) (u32)(unsigned long)(x)
#ifdef CONFIG_PPC64
@@ -94,7 +93,7 @@ int of_workarounds;
#define PROM_BUG() do { \
prom_printf("kernel BUG at %s line 0x%x!\n", \
- RELOC(__FILE__), __LINE__); \
+ __FILE__, __LINE__); \
__asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \
} while (0)
@@ -232,7 +231,7 @@ static int __init call_prom(const char *
for (i = 0; i < nret; i++)
args.args[nargs+i] = 0;
- if (enter_prom(&args, RELOC(prom_entry)) < 0)
+ if (enter_prom(&args, prom_entry) < 0)
return PROM_ERROR;
return (nret > 0) ? args.args[nargs] : 0;
@@ -257,7 +256,7 @@ static int __init call_prom_ret(const ch
for (i = 0; i < nret; i++)
args.args[nargs+i] = 0;
- if (enter_prom(&args, RELOC(prom_entry)) < 0)
+ if (enter_prom(&args, prom_entry) < 0)
return PROM_ERROR;
if (rets != NULL)
@@ -271,20 +270,19 @@ static int __init call_prom_ret(const ch
static void __init prom_print(const char *msg)
{
const char *p, *q;
- struct prom_t *_prom = &RELOC(prom);
- if (_prom->stdout == 0)
+ if (prom.stdout == 0)
return;
for (p = msg; *p != 0; p = q) {
for (q = p; *q != 0 && *q != '\n'; ++q)
;
if (q > p)
- call_prom("write", 3, 1, _prom->stdout, p, q - p);
+ call_prom("write", 3, 1, prom.stdout, p, q - p);
if (*q == 0)
break;
++q;
- call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2);
+ call_prom("write", 3, 1, prom.stdout, ADDR("\r\n"), 2);
}
}
@@ -293,7 +291,6 @@ static void __init prom_print_hex(unsign
{
int i, nibbles = sizeof(val)*2;
char buf[sizeof(val)*2+1];
- struct prom_t *_prom = &RELOC(prom);
for (i = nibbles-1; i >= 0; i--) {
buf[i] = (val & 0xf) + '0';
@@ -302,7 +299,7 @@ static void __init prom_print_hex(unsign
val >>= 4;
}
buf[nibbles] = '\0';
- call_prom("write", 3, 1, _prom->stdout, buf, nibbles);
+ call_prom("write", 3, 1, prom.stdout, buf, nibbles);
}
/* max number of decimal digits in an unsigned long */
@@ -311,7 +308,6 @@ static void __init prom_print_dec(unsign
{
int i, size;
char buf[UL_DIGITS+1];
- struct prom_t *_prom = &RELOC(prom);
for (i = UL_DIGITS-1; i >= 0; i--) {
buf[i] = (val % 10) + '0';
@@ -321,7 +317,7 @@ static void __init prom_print_dec(unsign
}
/* shift stuff down */
size = UL_DIGITS - i;
- call_prom("write", 3, 1, _prom->stdout, buf+i, size);
+ call_prom("write", 3, 1, prom.stdout, buf+i, size);
}
static void __init prom_printf(const char *format, ...)
@@ -330,19 +326,18 @@ static void __init prom_printf(const cha
va_list args;
unsigned long v;
long vs;
- struct prom_t *_prom = &RELOC(prom);
va_start(args, format);
for (p = format; *p != 0; p = q) {
for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
;
if (q > p)
- call_prom("write", 3, 1, _prom->stdout, p, q - p);
+ call_prom("write", 3, 1, prom.stdout, p, q - p);
if (*q == 0)
break;
if (*q == '\n') {
++q;
- call_prom("write", 3, 1, _prom->stdout,
+ call_prom("write", 3, 1, prom.stdout,
ADDR("\r\n"), 2);
continue;
}
@@ -364,7 +359,7 @@ static void __init prom_printf(const cha
++q;
vs = va_arg(args, int);
if (vs < 0) {
- prom_print(RELOC("-"));
+ prom_print("-");
vs = -vs;
}
prom_print_dec(vs);
@@ -385,7 +380,7 @@ static void __init prom_printf(const cha
++q;
vs = va_arg(args, long);
if (vs < 0) {
- prom_print(RELOC("-"));
+ prom_print("-");
vs = -vs;
}
prom_print_dec(vs);
@@ -399,7 +394,6 @@ static void __init prom_printf(const cha
static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
unsigned long align)
{
- struct prom_t *_prom = &RELOC(prom);
if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) {
/*
@@ -410,21 +404,21 @@ static unsigned int __init prom_claim(un
prom_arg_t result;
ret = call_prom_ret("call-method", 5, 2, &result,
- ADDR("claim"), _prom->memory,
+ ADDR("claim"), prom.memory,
align, size, virt);
if (ret != 0 || result == -1)
return -1;
ret = call_prom_ret("call-method", 5, 2, &result,
- ADDR("claim"), _prom->mmumap,
+ ADDR("claim"), prom.mmumap,
align, size, virt);
if (ret != 0) {
call_prom("call-method", 4, 1, ADDR("release"),
- _prom->memory, size, virt);
+ prom.memory, size, virt);
return -1;
}
/* the 0x12 is M (coherence) + PP == read/write */
call_prom("call-method", 6, 1,
- ADDR("map"), _prom->mmumap, 0x12, size, virt, virt);
+ ADDR("map"), prom.mmumap, 0x12, size, virt, virt);
return virt;
}
return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size,
@@ -436,7 +430,7 @@ static void __init __attribute__((noretu
prom_print(reason);
/* Do not call exit because it clears the screen on pmac
* it also causes some sort of double-fault on early pmacs */
- if (RELOC(of_platform) == PLATFORM_POWERMAC)
+ if (of_platform == PLATFORM_POWERMAC)
asm("trap\n");
/* ToDo: should put up an SRC here on pSeries */
@@ -518,13 +512,13 @@ static int __init prom_setprop(phandle n
add_string(&p, tohex((u32)(unsigned long) value));
add_string(&p, tohex(valuelen));
add_string(&p, tohex(ADDR(pname)));
- add_string(&p, tohex(strlen(RELOC(pname))));
+ add_string(&p, tohex(strlen(pname)));
add_string(&p, "property");
*p = 0;
return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);
}
-/* We can't use the standard versions because of RELOC headaches. */
+/* We can't use the standard versions because of relocation headaches. */
#define isxdigit(c) (('0' <= (c) && (c) <= '9') \
|| ('a' <= (c) && (c) <= 'f') \
|| ('A' <= (c) && (c) <= 'F'))
@@ -591,43 +585,42 @@ static unsigned long prom_memparse(const
*/
static void __init early_cmdline_parse(void)
{
- struct prom_t *_prom = &RELOC(prom);
const char *opt;
char *p;
int l = 0;
- RELOC(prom_cmd_line[0]) = 0;
- p = RELOC(prom_cmd_line);
- if ((long)_prom->chosen > 0)
- l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
+ prom_cmd_line[0] = 0;
+ p = prom_cmd_line;
+ if ((long)prom.chosen > 0)
+ l = prom_getprop(prom.chosen, "bootargs", p, COMMAND_LINE_SIZE-1);
#ifdef CONFIG_CMDLINE
if (l <= 0 || p[0] == '\0') /* dbl check */
- strlcpy(RELOC(prom_cmd_line),
- RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line));
+ strlcpy(prom_cmd_line,
+ CONFIG_CMDLINE, sizeof(prom_cmd_line));
#endif /* CONFIG_CMDLINE */
- prom_printf("command line: %s\n", RELOC(prom_cmd_line));
+ prom_printf("command line: %s\n", prom_cmd_line);
#ifdef CONFIG_PPC64
- opt = strstr(RELOC(prom_cmd_line), RELOC("iommu="));
+ opt = strstr(prom_cmd_line, "iommu=");
if (opt) {
prom_printf("iommu opt is: %s\n", opt);
opt += 6;
while (*opt && *opt == ' ')
opt++;
- if (!strncmp(opt, RELOC("off"), 3))
- RELOC(prom_iommu_off) = 1;
- else if (!strncmp(opt, RELOC("force"), 5))
- RELOC(prom_iommu_force_on) = 1;
+ if (!strncmp(opt, "off", 3))
+ prom_iommu_off = 1;
+ else if (!strncmp(opt, "force", 5))
+ prom_iommu_force_on = 1;
}
#endif
- opt = strstr(RELOC(prom_cmd_line), RELOC("mem="));
+ opt = strstr(prom_cmd_line, "mem=");
if (opt) {
opt += 4;
- RELOC(prom_memory_limit) = prom_memparse(opt, (const char **)&opt);
+ prom_memory_limit = prom_memparse(opt, (const char **)&opt);
#ifdef CONFIG_PPC64
/* Align to 16 MB == size of ppc64 large page */
- RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000);
+ prom_memory_limit = ALIGN(prom_memory_limit, 0x1000000);
#endif
}
}
@@ -880,7 +873,7 @@ static int __init prom_count_smt_threads
type[0] = 0;
prom_getprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, RELOC("cpu")))
+ if (strcmp(type, "cpu"))
continue;
/*
* There is an entry for each smt thread, each entry being
@@ -998,21 +991,21 @@ static void __init prom_send_capabilitie
*/
static unsigned long __init alloc_up(unsigned long size, unsigned long align)
{
- unsigned long base = RELOC(alloc_bottom);
+ unsigned long base = alloc_bottom;
unsigned long addr = 0;
if (align)
base = _ALIGN_UP(base, align);
prom_debug("alloc_up(%x, %x)\n", size, align);
- if (RELOC(ram_top) == 0)
+ if (ram_top == 0)
prom_panic("alloc_up() called with mem not initialized\n");
if (align)
- base = _ALIGN_UP(RELOC(alloc_bottom), align);
+ base = _ALIGN_UP(alloc_bottom, align);
else
- base = RELOC(alloc_bottom);
+ base = alloc_bottom;
- for(; (base + size) <= RELOC(alloc_top);
+ for(; (base + size) <= alloc_top;
base = _ALIGN_UP(base + 0x100000, align)) {
prom_debug(" trying: 0x%x\n\r", base);
addr = (unsigned long)prom_claim(base, size, 0);
@@ -1024,14 +1017,14 @@ static unsigned long __init alloc_up(uns
}
if (addr == 0)
return 0;
- RELOC(alloc_bottom) = addr + size;
+ alloc_bottom = addr + size;
prom_debug(" -> %x\n", addr);
- prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom));
- prom_debug(" alloc_top : %x\n", RELOC(alloc_top));
- prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
- prom_debug(" rmo_top : %x\n", RELOC(rmo_top));
- prom_debug(" ram_top : %x\n", RELOC(ram_top));
+ prom_debug(" alloc_bottom : %x\n", alloc_bottom);
+ prom_debug(" alloc_top : %x\n", alloc_top);
+ prom_debug(" alloc_top_hi : %x\n", alloc_top_high);
+ prom_debug(" rmo_top : %x\n", rmo_top);
+ prom_debug(" ram_top : %x\n", ram_top);
return addr;
}
@@ -1047,32 +1040,32 @@ static unsigned long __init alloc_down(u
unsigned long base, addr = 0;
prom_debug("alloc_down(%x, %x, %s)\n", size, align,
- highmem ? RELOC("(high)") : RELOC("(low)"));
- if (RELOC(ram_top) == 0)
+ highmem ? "(high)" : "(low)");
+ if (ram_top == 0)
prom_panic("alloc_down() called with mem not initialized\n");
if (highmem) {
/* Carve out storage for the TCE table. */
- addr = _ALIGN_DOWN(RELOC(alloc_top_high) - size, align);
- if (addr <= RELOC(alloc_bottom))
+ addr = _ALIGN_DOWN(alloc_top_high - size, align);
+ if (addr <= alloc_bottom)
return 0;
/* Will we bump into the RMO ? If yes, check out that we
* didn't overlap existing allocations there, if we did,
* we are dead, we must be the first in town !
*/
- if (addr < RELOC(rmo_top)) {
+ if (addr < rmo_top) {
/* Good, we are first */
- if (RELOC(alloc_top) == RELOC(rmo_top))
- RELOC(alloc_top) = RELOC(rmo_top) = addr;
+ if (alloc_top == rmo_top)
+ alloc_top = rmo_top = addr;
else
return 0;
}
- RELOC(alloc_top_high) = addr;
+ alloc_top_high = addr;
goto bail;
}
- base = _ALIGN_DOWN(RELOC(alloc_top) - size, align);
- for (; base > RELOC(alloc_bottom);
+ base = _ALIGN_DOWN(alloc_top - size, align);
+ for (; base > alloc_bottom;
base = _ALIGN_DOWN(base - 0x100000, align)) {
prom_debug(" trying: 0x%x\n\r", base);
addr = (unsigned long)prom_claim(base, size, 0);
@@ -1082,15 +1075,15 @@ static unsigned long __init alloc_down(u
}
if (addr == 0)
return 0;
- RELOC(alloc_top) = addr;
+ alloc_top = addr;
bail:
prom_debug(" -> %x\n", addr);
- prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom));
- prom_debug(" alloc_top : %x\n", RELOC(alloc_top));
- prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
- prom_debug(" rmo_top : %x\n", RELOC(rmo_top));
- prom_debug(" ram_top : %x\n", RELOC(ram_top));
+ prom_debug(" alloc_bottom : %x\n", alloc_bottom);
+ prom_debug(" alloc_top : %x\n", alloc_top);
+ prom_debug(" alloc_top_hi : %x\n", alloc_top_high);
+ prom_debug(" rmo_top : %x\n", rmo_top);
+ prom_debug(" ram_top : %x\n", ram_top);
return addr;
}
@@ -1130,7 +1123,7 @@ static unsigned long __init prom_next_ce
static void __init reserve_mem(u64 base, u64 size)
{
u64 top = base + size;
- unsigned long cnt = RELOC(mem_reserve_cnt);
+ unsigned long cnt = mem_reserve_cnt;
if (size == 0)
return;
@@ -1145,9 +1138,9 @@ static void __init reserve_mem(u64 base,
if (cnt >= (MEM_RESERVE_MAP_SIZE - 1))
prom_panic("Memory reserve map exhausted !\n");
- RELOC(mem_reserve_map)[cnt].base = base;
- RELOC(mem_reserve_map)[cnt].size = size;
- RELOC(mem_reserve_cnt) = cnt + 1;
+ mem_reserve_map[cnt].base = base;
+ mem_reserve_map[cnt].size = size;
+ mem_reserve_cnt = cnt + 1;
}
/*
@@ -1160,7 +1153,6 @@ static void __init prom_init_mem(void)
char *path, type[64];
unsigned int plen;
cell_t *p, *endp;
- struct prom_t *_prom = &RELOC(prom);
u32 rac, rsc;
/*
@@ -1169,14 +1161,14 @@ static void __init prom_init_mem(void)
* 2) top of memory
*/
rac = 2;
- prom_getprop(_prom->root, "#address-cells", &rac, sizeof(rac));
+ prom_getprop(prom.root, "#address-cells", &rac, sizeof(rac));
rsc = 1;
- prom_getprop(_prom->root, "#size-cells", &rsc, sizeof(rsc));
+ prom_getprop(prom.root, "#size-cells", &rsc, sizeof(rsc));
prom_debug("root_addr_cells: %x\n", (unsigned long) rac);
prom_debug("root_size_cells: %x\n", (unsigned long) rsc);
prom_debug("scanning memory:\n");
- path = RELOC(prom_scratch);
+ path = prom_scratch;
for (node = 0; prom_next_node(&node); ) {
type[0] = 0;
@@ -1189,15 +1181,15 @@ static void __init prom_init_mem(void)
*/
prom_getprop(node, "name", type, sizeof(type));
}
- if (strcmp(type, RELOC("memory")))
+ if (strcmp(type, "memory"))
continue;
- plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf));
+ plen = prom_getprop(node, "reg", regbuf, sizeof(regbuf));
if (plen > sizeof(regbuf)) {
prom_printf("memory node too large for buffer !\n");
plen = sizeof(regbuf);
}
- p = RELOC(regbuf);
+ p = regbuf;
endp = p + (plen / sizeof(cell_t));
#ifdef DEBUG_PROM
@@ -1215,14 +1207,14 @@ static void __init prom_init_mem(void)
if (size == 0)
continue;
prom_debug(" %x %x\n", base, size);
- if (base == 0 && (RELOC(of_platform) & PLATFORM_LPAR))
- RELOC(rmo_top) = size;
- if ((base + size) > RELOC(ram_top))
- RELOC(ram_top) = base + size;
+ if (base == 0 && (of_platform & PLATFORM_LPAR))
+ rmo_top = size;
+ if ((base + size) > ram_top)
+ ram_top = base + size;
}
}
- RELOC(alloc_bottom) = PAGE_ALIGN((unsigned long)&RELOC(_end) + 0x4000);
+ alloc_bottom = PAGE_ALIGN((unsigned long)&_end + 0x4000);
/*
* If prom_memory_limit is set we reduce the upper limits *except* for
@@ -1230,20 +1222,20 @@ static void __init prom_init_mem(void)
* TCE's up there.
*/
- RELOC(alloc_top_high) = RELOC(ram_top);
+ alloc_top_high = ram_top;
- if (RELOC(prom_memory_limit)) {
- if (RELOC(prom_memory_limit) <= RELOC(alloc_bottom)) {
+ if (prom_memory_limit) {
+ if (prom_memory_limit <= alloc_bottom) {
prom_printf("Ignoring mem=%x <= alloc_bottom.\n",
- RELOC(prom_memory_limit));
- RELOC(prom_memory_limit) = 0;
- } else if (RELOC(prom_memory_limit) >= RELOC(ram_top)) {
+ prom_memory_limit);
+ prom_memory_limit = 0;
+ } else if (prom_memory_limit >= ram_top) {
prom_printf("Ignoring mem=%x >= ram_top.\n",
- RELOC(prom_memory_limit));
- RELOC(prom_memory_limit) = 0;
+ prom_memory_limit);
+ prom_memory_limit = 0;
} else {
- RELOC(ram_top) = RELOC(prom_memory_limit);
- RELOC(rmo_top) = min(RELOC(rmo_top), RELOC(prom_memory_limit));
+ ram_top = prom_memory_limit;
+ rmo_top = min(rmo_top, prom_memory_limit);
}
}
@@ -1255,36 +1247,35 @@ static void __init prom_init_mem(void)
* Since 768MB is plenty of room, and we need to cap to something
* reasonable on 32-bit, cap at 768MB on all machines.
*/
- if (!RELOC(rmo_top))
- RELOC(rmo_top) = RELOC(ram_top);
- RELOC(rmo_top) = min(0x30000000ul, RELOC(rmo_top));
- RELOC(alloc_top) = RELOC(rmo_top);
- RELOC(alloc_top_high) = RELOC(ram_top);
+ if (!rmo_top)
+ rmo_top = ram_top;
+ rmo_top = min(0x30000000ul, rmo_top);
+ alloc_top = rmo_top;
+ alloc_top_high = ram_top;
/*
* Check if we have an initrd after the kernel but still inside
* the RMO. If we do move our bottom point to after it.
*/
- if (RELOC(prom_initrd_start) &&
- RELOC(prom_initrd_start) < RELOC(rmo_top) &&
- RELOC(prom_initrd_end) > RELOC(alloc_bottom))
- RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end));
+ if (prom_initrd_start &&
+ prom_initrd_start < rmo_top &&
+ prom_initrd_end > alloc_bottom)
+ alloc_bottom = PAGE_ALIGN(prom_initrd_end);
prom_printf("memory layout at init:\n");
- prom_printf(" memory_limit : %x (16 MB aligned)\n", RELOC(prom_memory_limit));
- prom_printf(" alloc_bottom : %x\n", RELOC(alloc_bottom));
- prom_printf(" alloc_top : %x\n", RELOC(alloc_top));
- prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
- prom_printf(" rmo_top : %x\n", RELOC(rmo_top));
- prom_printf(" ram_top : %x\n", RELOC(ram_top));
+ prom_printf(" memory_limit : %x (16 MB aligned)\n", prom_memory_limit);
+ prom_printf(" alloc_bottom : %x\n", alloc_bottom);
+ prom_printf(" alloc_top : %x\n", alloc_top);
+ prom_printf(" alloc_top_hi : %x\n", alloc_top_high);
+ prom_printf(" rmo_top : %x\n", rmo_top);
+ prom_printf(" ram_top : %x\n", ram_top);
}
static void __init prom_close_stdin(void)
{
- struct prom_t *_prom = &RELOC(prom);
ihandle val;
- if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0)
+ if (prom_getprop(prom.chosen, "stdin", &val, sizeof(val)) > 0)
call_prom("close", 1, 0, val);
}
@@ -1325,19 +1316,19 @@ static void __init prom_query_opal(void)
}
prom_printf("Querying for OPAL presence... ");
- rc = opal_query_takeover(&RELOC(prom_opal_size),
- &RELOC(prom_opal_align));
+ rc = opal_query_takeover(&prom_opal_size,
+ &prom_opal_align);
prom_debug("(rc = %ld) ", rc);
if (rc != 0) {
prom_printf("not there.\n");
return;
}
- RELOC(of_platform) = PLATFORM_OPAL;
+ of_platform = PLATFORM_OPAL;
prom_printf(" there !\n");
- prom_debug(" opal_size = 0x%lx\n", RELOC(prom_opal_size));
- prom_debug(" opal_align = 0x%lx\n", RELOC(prom_opal_align));
- if (RELOC(prom_opal_align) < 0x10000)
- RELOC(prom_opal_align) = 0x10000;
+ prom_debug(" opal_size = 0x%lx\n", prom_opal_size);
+ prom_debug(" opal_align = 0x%lx\n", prom_opal_align);
+ if (prom_opal_align < 0x10000)
+ prom_opal_align = 0x10000;
}
static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...)
@@ -1358,8 +1349,8 @@ static int prom_rtas_call(int token, int
for (i = 0; i < nret; ++i)
rtas_args.rets[i] = 0;
- opal_enter_rtas(&rtas_args, RELOC(prom_rtas_data),
- RELOC(prom_rtas_entry));
+ opal_enter_rtas(&rtas_args, prom_rtas_data,
+ prom_rtas_entry);
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
@@ -1374,9 +1365,8 @@ static void __init prom_opal_hold_cpus(v
phandle node;
char type[64];
u32 servers[8];
- struct prom_t *_prom = &RELOC(prom);
- void *entry = (unsigned long *)&RELOC(opal_secondary_entry);
- struct opal_secondary_data *data = &RELOC(opal_secondary_data);
+ void *entry = (unsigned long *)&opal_secondary_entry;
+ struct opal_secondary_data *data = &opal_secondary_data;
prom_debug("prom_opal_hold_cpus: start...\n");
prom_debug(" - entry = 0x%x\n", entry);
@@ -1389,12 +1379,12 @@ static void __init prom_opal_hold_cpus(v
for (node = 0; prom_next_node(&node); ) {
type[0] = 0;
prom_getprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, RELOC("cpu")) != 0)
+ if (strcmp(type, "cpu") != 0)
continue;
/* Skip non-configured cpus. */
if (prom_getprop(node, "status", type, sizeof(type)) > 0)
- if (strcmp(type, RELOC("okay")) != 0)
+ if (strcmp(type, "okay") != 0)
continue;
cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers,
@@ -1405,7 +1395,7 @@ static void __init prom_opal_hold_cpus(v
for (i = 0; i < cnt; i++) {
cpu = servers[i];
prom_debug("CPU %d ... ", cpu);
- if (cpu == _prom->cpu) {
+ if (cpu == prom.cpu) {
prom_debug("booted !\n");
continue;
}
@@ -1416,7 +1406,7 @@ static void __init prom_opal_hold_cpus(v
* spinloop.
*/
data->ack = -1;
- rc = prom_rtas_call(RELOC(prom_rtas_start_cpu), 3, 1,
+ rc = prom_rtas_call(prom_rtas_start_cpu, 3, 1,
NULL, cpu, entry, data);
prom_debug("rtas rc=%d ...", rc);
@@ -1436,21 +1426,21 @@ static void __init prom_opal_hold_cpus(v
static void __init prom_opal_takeover(void)
{
- struct opal_secondary_data *data = &RELOC(opal_secondary_data);
+ struct opal_secondary_data *data = &opal_secondary_data;
struct opal_takeover_args *args = &data->args;
- u64 align = RELOC(prom_opal_align);
+ u64 align = prom_opal_align;
u64 top_addr, opal_addr;
- args->k_image = (u64)RELOC(_stext);
+ args->k_image = (u64)_stext;
args->k_size = _end - _stext;
args->k_entry = 0;
args->k_entry2 = 0x60;
top_addr = _ALIGN_UP(args->k_size, align);
- if (RELOC(prom_initrd_start) != 0) {
- args->rd_image = RELOC(prom_initrd_start);
- args->rd_size = RELOC(prom_initrd_end) - args->rd_image;
+ if (prom_initrd_start != 0) {
+ args->rd_image = prom_initrd_start;
+ args->rd_size = prom_initrd_end - args->rd_image;
args->rd_loc = top_addr;
top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align);
}
@@ -1462,13 +1452,13 @@ static void __init prom_opal_takeover(vo
* has plenty of memory, and we ask for the HAL for now to
* be just below the 1G point, or above the initrd
*/
- opal_addr = _ALIGN_DOWN(0x40000000 - RELOC(prom_opal_size), align);
+ opal_addr = _ALIGN_DOWN(0x40000000 - prom_opal_size, align);
if (opal_addr < top_addr)
opal_addr = top_addr;
args->hal_addr = opal_addr;
/* Copy the command line to the kernel image */
- strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line),
+ strlcpy(boot_command_line, prom_cmd_line,
COMMAND_LINE_SIZE);
prom_debug(" k_image = 0x%lx\n", args->k_image);
@@ -1550,8 +1540,8 @@ static void __init prom_instantiate_opal
&entry, sizeof(entry));
#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
- RELOC(prom_opal_base) = base;
- RELOC(prom_opal_entry) = entry;
+ prom_opal_base = base;
+ prom_opal_entry = entry;
#endif
prom_debug("prom_instantiate_opal: end...\n");
}
@@ -1609,9 +1599,9 @@ static void __init prom_instantiate_rtas
#ifdef CONFIG_PPC_POWERNV
/* PowerVN takeover hack */
- RELOC(prom_rtas_data) = base;
- RELOC(prom_rtas_entry) = entry;
- prom_getprop(rtas_node, "start-cpu", &RELOC(prom_rtas_start_cpu), 4);
+ prom_rtas_data = base;
+ prom_rtas_entry = entry;
+ prom_getprop(rtas_node, "start-cpu", &prom_rtas_start_cpu, 4);
#endif
prom_debug("rtas base = 0x%x\n", base);
prom_debug("rtas entry = 0x%x\n", entry);
@@ -1686,20 +1676,20 @@ static void __init prom_initialize_tce_t
phandle node;
ihandle phb_node;
char compatible[64], type[64], model[64];
- char *path = RELOC(prom_scratch);
+ char *path = prom_scratch;
u64 base, align;
u32 minalign, minsize;
u64 tce_entry, *tce_entryp;
u64 local_alloc_top, local_alloc_bottom;
u64 i;
- if (RELOC(prom_iommu_off))
+ if (prom_iommu_off)
return;
prom_debug("starting prom_initialize_tce_table\n");
/* Cache current top of allocs so we reserve a single block */
- local_alloc_top = RELOC(alloc_top_high);
+ local_alloc_top = alloc_top_high;
local_alloc_bottom = local_alloc_top;
/* Search all nodes looking for PHBs. */
@@ -1712,19 +1702,19 @@ static void __init prom_initialize_tce_t
prom_getprop(node, "device_type", type, sizeof(type));
prom_getprop(node, "model", model, sizeof(model));
- if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL))
+ if ((type[0] == 0) || (strstr(type, "pci") == NULL))
continue;
/* Keep the old logic intact to avoid regression. */
if (compatible[0] != 0) {
- if ((strstr(compatible, RELOC("python")) == NULL) &&
- (strstr(compatible, RELOC("Speedwagon")) == NULL) &&
- (strstr(compatible, RELOC("Winnipeg")) == NULL))
+ if ((strstr(compatible, "python") == NULL) &&
+ (strstr(compatible, "Speedwagon") == NULL) &&
+ (strstr(compatible, "Winnipeg") == NULL))
continue;
} else if (model[0] != 0) {
- if ((strstr(model, RELOC("ython")) == NULL) &&
- (strstr(model, RELOC("peedwagon")) == NULL) &&
- (strstr(model, RELOC("innipeg")) == NULL))
+ if ((strstr(model, "ython") == NULL) &&
+ (strstr(model, "peedwagon") == NULL) &&
+ (strstr(model, "innipeg") == NULL))
continue;
}
@@ -1803,8 +1793,8 @@ static void __init prom_initialize_tce_t
/* These are only really needed if there is a memory limit in
* effect, but we don't know so export them always. */
- RELOC(prom_tce_alloc_start) = local_alloc_bottom;
- RELOC(prom_tce_alloc_end) = local_alloc_top;
+ prom_tce_alloc_start = local_alloc_bottom;
+ prom_tce_alloc_end = local_alloc_top;
/* Flag the first invalid entry */
prom_debug("ending prom_initialize_tce_table\n");
@@ -1841,7 +1831,6 @@ static void __init prom_hold_cpus(void)
unsigned int reg;
phandle node;
char type[64];
- struct prom_t *_prom = &RELOC(prom);
unsigned long *spinloop
= (void *) LOW_ADDR(__secondary_hold_spinloop);
unsigned long *acknowledge
@@ -1867,12 +1856,12 @@ static void __init prom_hold_cpus(void)
for (node = 0; prom_next_node(&node); ) {
type[0] = 0;
prom_getprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, RELOC("cpu")) != 0)
+ if (strcmp(type, "cpu") != 0)
continue;
/* Skip non-configured cpus. */
if (prom_getprop(node, "status", type, sizeof(type)) > 0)
- if (strcmp(type, RELOC("okay")) != 0)
+ if (strcmp(type, "okay") != 0)
continue;
reg = -1;
@@ -1886,7 +1875,7 @@ static void __init prom_hold_cpus(void)
*/
*acknowledge = (unsigned long)-1;
- if (reg != _prom->cpu) {
+ if (reg != prom.cpu) {
/* Primary Thread of non-boot cpu or any thread */
prom_printf("starting cpu hw idx %lu... ", reg);
call_prom("start-cpu", 3, 0, node,
@@ -1913,22 +1902,20 @@ static void __init prom_hold_cpus(void)
static void __init prom_init_client_services(unsigned long pp)
{
- struct prom_t *_prom = &RELOC(prom);
-
/* Get a handle to the prom entry point before anything else */
- RELOC(prom_entry) = pp;
+ prom_entry = pp;
/* get a handle for the stdout device */
- _prom->chosen = call_prom("finddevice", 1, 1, ADDR("/chosen"));
- if (!PHANDLE_VALID(_prom->chosen))
+ prom.chosen = call_prom("finddevice", 1, 1, ADDR("/chosen"));
+ if (!PHANDLE_VALID(prom.chosen))
prom_panic("cannot find chosen"); /* msg won't be printed :( */
/* get device tree root */
- _prom->root = call_prom("finddevice", 1, 1, ADDR("/"));
- if (!PHANDLE_VALID(_prom->root))
+ prom.root = call_prom("finddevice", 1, 1, ADDR("/"));
+ if (!PHANDLE_VALID(prom.root))
prom_panic("cannot find device tree root"); /* msg won't be printed :( */
- _prom->mmumap = 0;
+ prom.mmumap = 0;
}
#ifdef CONFIG_PPC32
@@ -1939,7 +1926,6 @@ static void __init prom_init_client_serv
*/
static void __init prom_find_mmu(void)
{
- struct prom_t *_prom = &RELOC(prom);
phandle oprom;
char version[64];
@@ -1957,10 +1943,10 @@ static void __init prom_find_mmu(void)
call_prom("interpret", 1, 1, "dev /memory 0 to allow-reclaim");
} else
return;
- _prom->memory = call_prom("open", 1, 1, ADDR("/memory"));
- prom_getprop(_prom->chosen, "mmu", &_prom->mmumap,
- sizeof(_prom->mmumap));
- if (!IHANDLE_VALID(_prom->memory) || !IHANDLE_VALID(_prom->mmumap))
+ prom.memory = call_prom("open", 1, 1, ADDR("/memory"));
+ prom_getprop(prom.chosen, "mmu", &prom.mmumap,
+ sizeof(prom.mmumap));
+ if (!IHANDLE_VALID(prom.memory) || !IHANDLE_VALID(prom.mmumap))
of_workarounds &= ~OF_WA_CLAIM; /* hmmm */
}
#else
@@ -1969,36 +1955,34 @@ static void __init prom_find_mmu(void)
static void __init prom_init_stdout(void)
{
- struct prom_t *_prom = &RELOC(prom);
- char *path = RELOC(of_stdout_device);
+ char *path = of_stdout_device;
char type[16];
u32 val;
- if (prom_getprop(_prom->chosen, "stdout", &val, sizeof(val)) <= 0)
+ if (prom_getprop(prom.chosen, "stdout", &val, sizeof(val)) <= 0)
prom_panic("cannot find stdout");
- _prom->stdout = val;
+ prom.stdout = val;
/* Get the full OF pathname of the stdout device */
memset(path, 0, 256);
- call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255);
- val = call_prom("instance-to-package", 1, 1, _prom->stdout);
- prom_setprop(_prom->chosen, "/chosen", "linux,stdout-package",
+ call_prom("instance-to-path", 3, 1, prom.stdout, path, 255);
+ val = call_prom("instance-to-package", 1, 1, prom.stdout);
+ prom_setprop(prom.chosen, "/chosen", "linux,stdout-package",
&val, sizeof(val));
- prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device));
- prom_setprop(_prom->chosen, "/chosen", "linux,stdout-path",
+ prom_printf("OF stdout device is: %s\n", of_stdout_device);
+ prom_setprop(prom.chosen, "/chosen", "linux,stdout-path",
path, strlen(path) + 1);
/* If it's a display, note it */
memset(type, 0, sizeof(type));
prom_getprop(val, "device_type", type, sizeof(type));
- if (strcmp(type, RELOC("display")) == 0)
+ if (strcmp(type, "display") == 0)
prom_setprop(val, path, "linux,boot-display", NULL, 0);
}
static int __init prom_find_machine_type(void)
{
- struct prom_t *_prom = &RELOC(prom);
char compat[256];
int len, i = 0;
#ifdef CONFIG_PPC64
@@ -2007,7 +1991,7 @@ static int __init prom_find_machine_type
#endif
/* Look for a PowerMac or a Cell */
- len = prom_getprop(_prom->root, "compatible",
+ len = prom_getprop(prom.root, "compatible",
compat, sizeof(compat)-1);
if (len > 0) {
compat[len] = 0;
@@ -2016,16 +2000,16 @@ static int __init prom_find_machine_type
int sl = strlen(p);
if (sl == 0)
break;
- if (strstr(p, RELOC("Power Macintosh")) ||
- strstr(p, RELOC("MacRISC")))
+ if (strstr(p, "Power Macintosh") ||
+ strstr(p, "MacRISC"))
return PLATFORM_POWERMAC;
#ifdef CONFIG_PPC64
/* We must make sure we don't detect the IBM Cell
* blades as pSeries due to some firmware issues,
* so we do it here.
*/
- if (strstr(p, RELOC("IBM,CBEA")) ||
- strstr(p, RELOC("IBM,CPBW-1.0")))
+ if (strstr(p, "IBM,CBEA") ||
+ strstr(p, "IBM,CPBW-1.0"))
return PLATFORM_GENERIC;
#endif /* CONFIG_PPC64 */
i += sl + 1;
@@ -2042,11 +2026,11 @@ static int __init prom_find_machine_type
* non-IBM designs !
* - it has /rtas
*/
- len = prom_getprop(_prom->root, "device_type",
+ len = prom_getprop(prom.root, "device_type",
compat, sizeof(compat)-1);
if (len <= 0)
return PLATFORM_GENERIC;
- if (strcmp(compat, RELOC("chrp")))
+ if (strcmp(compat, "chrp"))
return PLATFORM_GENERIC;
/* Default to pSeries. We need to know if we are running LPAR */
@@ -2108,11 +2092,11 @@ static void __init prom_check_displays(v
for (node = 0; prom_next_node(&node); ) {
memset(type, 0, sizeof(type));
prom_getprop(node, "device_type", type, sizeof(type));
- if (strcmp(type, RELOC("display")) != 0)
+ if (strcmp(type, "display") != 0)
continue;
/* It seems OF doesn't null-terminate the path :-( */
- path = RELOC(prom_scratch);
+ path = prom_scratch;
memset(path, 0, PROM_SCRATCH_SIZE);
/*
@@ -2136,15 +2120,15 @@ static void __init prom_check_displays(v
/* Setup a usable color table when the appropriate
* method is available. Should update this to set-colors */
- clut = RELOC(default_colors);
+ clut = default_colors;
for (i = 0; i < 16; i++, clut += 3)
if (prom_set_color(ih, i, clut[0], clut[1],
clut[2]) != 0)
break;
#ifdef CONFIG_LOGO_LINUX_CLUT224
- clut = PTRRELOC(RELOC(logo_linux_clut224.clut));
- for (i = 0; i < RELOC(logo_linux_clut224.clutsize); i++, clut += 3)
+ clut = PTRRELOC(logo_linux_clut224.clut);
+ for (i = 0; i < logo_linux_clut224.clutsize; i++, clut += 3)
if (prom_set_color(ih, i + 32, clut[0], clut[1],
clut[2]) != 0)
break;
@@ -2164,8 +2148,8 @@ static void __init *make_room(unsigned l
unsigned long room, chunk;
prom_debug("Chunk exhausted, claiming more at %x...\n",
- RELOC(alloc_bottom));
- room = RELOC(alloc_top) - RELOC(alloc_bottom);
+ alloc_bottom);
+ room = alloc_top - alloc_bottom;
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
if (room < PAGE_SIZE)
@@ -2191,9 +2175,9 @@ static unsigned long __init dt_find_stri
{
char *s, *os;
- s = os = (char *)RELOC(dt_string_start);
+ s = os = (char *)dt_string_start;
s += 4;
- while (s < (char *)RELOC(dt_string_end)) {
+ while (s < (char *)dt_string_end) {
if (strcmp(s, str) == 0)
return s - os;
s += strlen(s) + 1;
@@ -2215,10 +2199,10 @@ static void __init scan_dt_build_strings
unsigned long soff;
phandle child;
- sstart = (char *)RELOC(dt_string_start);
+ sstart = (char *)dt_string_start;
/* get and store all property names */
- prev_name = RELOC("");
+ prev_name = "";
for (;;) {
/* 64 is max len of name including nul. */
namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1);
@@ -2229,9 +2213,9 @@ static void __init scan_dt_build_strings
}
/* skip "name" */
- if (strcmp(namep, RELOC("name")) == 0) {
+ if (strcmp(namep, "name") == 0) {
*mem_start = (unsigned long)namep;
- prev_name = RELOC("name");
+ prev_name = "name";
continue;
}
/* get/create string entry */
@@ -2242,7 +2226,7 @@ static void __init scan_dt_build_strings
} else {
/* Trim off some if we can */
*mem_start = (unsigned long)namep + strlen(namep) + 1;
- RELOC(dt_string_end) = *mem_start;
+ dt_string_end = *mem_start;
}
prev_name = namep;
}
@@ -2297,35 +2281,35 @@ static void __init scan_dt_build_struct(
}
/* get it again for debugging */
- path = RELOC(prom_scratch);
+ path = prom_scratch;
memset(path, 0, PROM_SCRATCH_SIZE);
call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1);
/* get and store all properties */
- prev_name = RELOC("");
- sstart = (char *)RELOC(dt_string_start);
+ prev_name = "";
+ sstart = (char *)dt_string_start;
for (;;) {
if (call_prom("nextprop", 3, 1, node, prev_name,
- RELOC(pname)) != 1)
+ pname) != 1)
break;
/* skip "name" */
- if (strcmp(RELOC(pname), RELOC("name")) == 0) {
- prev_name = RELOC("name");
+ if (strcmp(pname, "name") == 0) {
+ prev_name = "name";
continue;
}
/* find string offset */
- soff = dt_find_string(RELOC(pname));
+ soff = dt_find_string(pname);
if (soff == 0) {
prom_printf("WARNING: Can't find string index for"
- " <%s>, node %s\n", RELOC(pname), path);
+ " <%s>, node %s\n", pname, path);
break;
}
prev_name = sstart + soff;
/* get length */
- l = call_prom("getproplen", 2, 1, node, RELOC(pname));
+ l = call_prom("getproplen", 2, 1, node, pname);
/* sanity checks */
if (l == PROM_ERROR)
@@ -2338,10 +2322,10 @@ static void __init scan_dt_build_struct(
/* push property content */
valp = make_room(mem_start, mem_end, l, 4);
- call_prom("getprop", 4, 1, node, RELOC(pname), valp, l);
+ call_prom("getprop", 4, 1, node, pname, valp, l);
*mem_start = _ALIGN(*mem_start, 4);
- if (!strcmp(RELOC(pname), RELOC("phandle")))
+ if (!strcmp(pname, "phandle"))
has_phandle = 1;
}
@@ -2349,7 +2333,7 @@ static void __init scan_dt_build_struct(
* existed (can happen with OPAL)
*/
if (!has_phandle) {
- soff = dt_find_string(RELOC("linux,phandle"));
+ soff = dt_find_string("linux,phandle");
if (soff == 0)
prom_printf("WARNING: Can't find string index for"
" <linux-phandle> node %s\n", path);
@@ -2377,7 +2361,6 @@ static void __init flatten_device_tree(v
phandle root;
unsigned long mem_start, mem_end, room;
struct boot_param_header *hdr;
- struct prom_t *_prom = &RELOC(prom);
char *namep;
u64 *rsvmap;
@@ -2385,10 +2368,10 @@ static void __init flatten_device_tree(v
* Check how much room we have between alloc top & bottom (+/- a
* few pages), crop to 1MB, as this is our "chunk" size
*/
- room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000;
+ room = alloc_top - alloc_bottom - 0x4000;
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
- prom_debug("starting device tree allocs at %x\n", RELOC(alloc_bottom));
+ prom_debug("starting device tree allocs at %x\n", alloc_bottom);
/* Now try to claim that */
mem_start = (unsigned long)alloc_up(room, PAGE_SIZE);
@@ -2405,66 +2388,66 @@ static void __init flatten_device_tree(v
mem_start = _ALIGN(mem_start, 4);
hdr = make_room(&mem_start, &mem_end,
sizeof(struct boot_param_header), 4);
- RELOC(dt_header_start) = (unsigned long)hdr;
+ dt_header_start = (unsigned long)hdr;
rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8);
/* Start of strings */
mem_start = PAGE_ALIGN(mem_start);
- RELOC(dt_string_start) = mem_start;
+ dt_string_start = mem_start;
mem_start += 4; /* hole */
/* Add "linux,phandle" in there, we'll need it */
namep = make_room(&mem_start, &mem_end, 16, 1);
- strcpy(namep, RELOC("linux,phandle"));
+ strcpy(namep, "linux,phandle");
mem_start = (unsigned long)namep + strlen(namep) + 1;
/* Build string array */
prom_printf("Building dt strings...\n");
scan_dt_build_strings(root, &mem_start, &mem_end);
- RELOC(dt_string_end) = mem_start;
+ dt_string_end = mem_start;
/* Build structure */
mem_start = PAGE_ALIGN(mem_start);
- RELOC(dt_struct_start) = mem_start;
+ dt_struct_start = mem_start;
prom_printf("Building dt structure...\n");
scan_dt_build_struct(root, &mem_start, &mem_end);
dt_push_token(OF_DT_END, &mem_start, &mem_end);
- RELOC(dt_struct_end) = PAGE_ALIGN(mem_start);
+ dt_struct_end = PAGE_ALIGN(mem_start);
/* Finish header */
- hdr->boot_cpuid_phys = _prom->cpu;
+ hdr->boot_cpuid_phys = prom.cpu;
hdr->magic = OF_DT_HEADER;
- hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start);
- hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start);
- hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start);
- hdr->dt_strings_size = RELOC(dt_string_end) - RELOC(dt_string_start);
- hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start);
+ hdr->totalsize = dt_struct_end - dt_header_start;
+ hdr->off_dt_struct = dt_struct_start - dt_header_start;
+ hdr->off_dt_strings = dt_string_start - dt_header_start;
+ hdr->dt_strings_size = dt_string_end - dt_string_start;
+ hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - dt_header_start;
hdr->version = OF_DT_VERSION;
/* Version 16 is not backward compatible */
hdr->last_comp_version = 0x10;
/* Copy the reserve map in */
- memcpy(rsvmap, RELOC(mem_reserve_map), sizeof(mem_reserve_map));
+ memcpy(rsvmap, mem_reserve_map, sizeof(mem_reserve_map));
#ifdef DEBUG_PROM
{
int i;
prom_printf("reserved memory map:\n");
- for (i = 0; i < RELOC(mem_reserve_cnt); i++)
+ for (i = 0; i < mem_reserve_cnt; i++)
prom_printf(" %x - %x\n",
- RELOC(mem_reserve_map)[i].base,
- RELOC(mem_reserve_map)[i].size);
+ mem_reserve_map[i].base,
+ mem_reserve_map[i].size);
}
#endif
/* Bump mem_reserve_cnt to cause further reservations to fail
* since it's too late.
*/
- RELOC(mem_reserve_cnt) = MEM_RESERVE_MAP_SIZE;
+ mem_reserve_cnt = MEM_RESERVE_MAP_SIZE;
prom_printf("Device tree strings 0x%x -> 0x%x\n",
- RELOC(dt_string_start), RELOC(dt_string_end));
+ dt_string_start, dt_string_end);
prom_printf("Device tree struct 0x%x -> 0x%x\n",
- RELOC(dt_struct_start), RELOC(dt_struct_end));
+ dt_struct_start, dt_struct_end);
}
@@ -2519,7 +2502,6 @@ static void __init fixup_device_tree_map
phandle mc;
u32 mc_reg[4];
char *name = "/hostbridge@f8000000";
- struct prom_t *_prom = &RELOC(prom);
u32 ac, sc;
mc = call_prom("finddevice", 1, 1, ADDR(name));
@@ -2529,8 +2511,8 @@ static void __init fixup_device_tree_map
if (prom_getproplen(mc, "reg") != 8)
return;
- prom_getprop(_prom->root, "#address-cells", &ac, sizeof(ac));
- prom_getprop(_prom->root, "#size-cells", &sc, sizeof(sc));
+ prom_getprop(prom.root, "#address-cells", &ac, sizeof(ac));
+ prom_getprop(prom.root, "#size-cells", &sc, sizeof(sc));
if ((ac != 2) || (sc != 2))
return;
@@ -2799,46 +2781,43 @@ static void __init fixup_device_tree(voi
static void __init prom_find_boot_cpu(void)
{
- struct prom_t *_prom = &RELOC(prom);
u32 getprop_rval;
ihandle prom_cpu;
phandle cpu_pkg;
- _prom->cpu = 0;
- if (prom_getprop(_prom->chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0)
+ prom.cpu = 0;
+ if (prom_getprop(prom.chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0)
return;
cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu);
prom_getprop(cpu_pkg, "reg", &getprop_rval, sizeof(getprop_rval));
- _prom->cpu = getprop_rval;
+ prom.cpu = getprop_rval;
- prom_debug("Booting CPU hw index = %lu\n", _prom->cpu);
+ prom_debug("Booting CPU hw index = %lu\n", prom.cpu);
}
static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
{
#ifdef CONFIG_BLK_DEV_INITRD
- struct prom_t *_prom = &RELOC(prom);
-
if (r3 && r4 && r4 != 0xdeadbeef) {
unsigned long val;
- RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3;
- RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4;
+ prom_initrd_start = is_kernel_addr(r3) ? __pa(r3) : r3;
+ prom_initrd_end = prom_initrd_start + r4;
- val = RELOC(prom_initrd_start);
- prom_setprop(_prom->chosen, "/chosen", "linux,initrd-start",
+ val = prom_initrd_start;
+ prom_setprop(prom.chosen, "/chosen", "linux,initrd-start",
&val, sizeof(val));
- val = RELOC(prom_initrd_end);
- prom_setprop(_prom->chosen, "/chosen", "linux,initrd-end",
+ val = prom_initrd_end;
+ prom_setprop(prom.chosen, "/chosen", "linux,initrd-end",
&val, sizeof(val));
- reserve_mem(RELOC(prom_initrd_start),
- RELOC(prom_initrd_end) - RELOC(prom_initrd_start));
+ reserve_mem(prom_initrd_start,
+ prom_initrd_end - prom_initrd_start);
- prom_debug("initrd_start=0x%x\n", RELOC(prom_initrd_start));
- prom_debug("initrd_end=0x%x\n", RELOC(prom_initrd_end));
+ prom_debug("initrd_start=0x%x\n", prom_initrd_start);
+ prom_debug("initrd_end=0x%x\n", prom_initrd_end);
}
#endif /* CONFIG_BLK_DEV_INITRD */
}
@@ -2901,7 +2880,6 @@ unsigned long __init prom_init(unsigned
unsigned long r6, unsigned long r7,
unsigned long kbase)
{
- struct prom_t *_prom;
unsigned long hdr;
#ifdef CONFIG_PPC32
@@ -2911,12 +2889,10 @@ unsigned long __init prom_init(unsigned
reloc_toc();
#endif
- _prom = &RELOC(prom);
-
/*
* First zero the BSS
*/
- memset(&RELOC(__bss_start), 0, __bss_stop - __bss_start);
+ memset(&__bss_start, 0, __bss_stop - __bss_start);
/*
* Init interface to Open Firmware, get some node references,
@@ -2935,14 +2911,14 @@ unsigned long __init prom_init(unsigned
*/
prom_init_stdout();
- prom_printf("Preparing to boot %s", RELOC(linux_banner));
+ prom_printf("Preparing to boot %s", linux_banner);
/*
* Get default machine type. At this point, we do not differentiate
* between pSeries SMP and pSeries LPAR
*/
- RELOC(of_platform) = prom_find_machine_type();
- prom_printf("Detected machine type: %x\n", RELOC(of_platform));
+ of_platform = prom_find_machine_type();
+ prom_printf("Detected machine type: %x\n", of_platform);
#ifndef CONFIG_NONSTATIC_KERNEL
/* Bail if this is a kdump kernel. */
@@ -2959,15 +2935,15 @@ unsigned long __init prom_init(unsigned
/*
* On pSeries, inform the firmware about our capabilities
*/
- if (RELOC(of_platform) == PLATFORM_PSERIES ||
- RELOC(of_platform) == PLATFORM_PSERIES_LPAR)
+ if (of_platform == PLATFORM_PSERIES ||
+ of_platform == PLATFORM_PSERIES_LPAR)
prom_send_capabilities();
#endif
/*
* Copy the CPU hold code
*/
- if (RELOC(of_platform) != PLATFORM_POWERMAC)
+ if (of_platform != PLATFORM_POWERMAC)
copy_and_flush(0, kbase, 0x100, 0);
/*
@@ -2996,7 +2972,7 @@ unsigned long __init prom_init(unsigned
* that uses the allocator, we need to make sure we get the top of memory
* available for us here...
*/
- if (RELOC(of_platform) == PLATFORM_PSERIES)
+ if (of_platform == PLATFORM_PSERIES)
prom_initialize_tce_table();
#endif
@@ -3004,19 +2980,19 @@ unsigned long __init prom_init(unsigned
* On non-powermacs, try to instantiate RTAS. PowerMacs don't
* have a usable RTAS implementation.
*/
- if (RELOC(of_platform) != PLATFORM_POWERMAC &&
- RELOC(of_platform) != PLATFORM_OPAL)
+ if (of_platform != PLATFORM_POWERMAC &&
+ of_platform != PLATFORM_OPAL)
prom_instantiate_rtas();
#ifdef CONFIG_PPC_POWERNV
/* Detect HAL and try instanciating it & doing takeover */
- if (RELOC(of_platform) == PLATFORM_PSERIES_LPAR) {
+ if (of_platform == PLATFORM_PSERIES_LPAR) {
prom_query_opal();
- if (RELOC(of_platform) == PLATFORM_OPAL) {
+ if (of_platform == PLATFORM_OPAL) {
prom_opal_hold_cpus();
prom_opal_takeover();
}
- } else if (RELOC(of_platform) == PLATFORM_OPAL)
+ } else if (of_platform == PLATFORM_OPAL)
prom_instantiate_opal();
#endif
@@ -3030,32 +3006,32 @@ unsigned long __init prom_init(unsigned
*
* PowerMacs use a different mechanism to spin CPUs
*/
- if (RELOC(of_platform) != PLATFORM_POWERMAC &&
- RELOC(of_platform) != PLATFORM_OPAL)
+ if (of_platform != PLATFORM_POWERMAC &&
+ of_platform != PLATFORM_OPAL)
prom_hold_cpus();
/*
* Fill in some infos for use by the kernel later on
*/
- if (RELOC(prom_memory_limit))
- prom_setprop(_prom->chosen, "/chosen", "linux,memory-limit",
- &RELOC(prom_memory_limit),
+ if (prom_memory_limit)
+ prom_setprop(prom.chosen, "/chosen", "linux,memory-limit",
+ &prom_memory_limit,
sizeof(prom_memory_limit));
#ifdef CONFIG_PPC64
- if (RELOC(prom_iommu_off))
- prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
+ if (prom_iommu_off)
+ prom_setprop(prom.chosen, "/chosen", "linux,iommu-off",
NULL, 0);
- if (RELOC(prom_iommu_force_on))
- prom_setprop(_prom->chosen, "/chosen", "linux,iommu-force-on",
+ if (prom_iommu_force_on)
+ prom_setprop(prom.chosen, "/chosen", "linux,iommu-force-on",
NULL, 0);
- if (RELOC(prom_tce_alloc_start)) {
- prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-start",
- &RELOC(prom_tce_alloc_start),
+ if (prom_tce_alloc_start) {
+ prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-start",
+ &prom_tce_alloc_start,
sizeof(prom_tce_alloc_start));
- prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-end",
- &RELOC(prom_tce_alloc_end),
+ prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-end",
+ &prom_tce_alloc_end,
sizeof(prom_tce_alloc_end));
}
#endif
@@ -3077,8 +3053,8 @@ unsigned long __init prom_init(unsigned
* closed stdin already (in particular the powerbook 101). It
* appears that the OPAL version of OFW doesn't like it either.
*/
- if (RELOC(of_platform) != PLATFORM_POWERMAC &&
- RELOC(of_platform) != PLATFORM_OPAL)
+ if (of_platform != PLATFORM_POWERMAC &&
+ of_platform != PLATFORM_OPAL)
prom_close_stdin();
/*
@@ -3093,10 +3069,10 @@ unsigned long __init prom_init(unsigned
* tree and NULL as r5, thus triggering the new entry point which
* is common to us and kexec
*/
- hdr = RELOC(dt_header_start);
+ hdr = dt_header_start;
/* Don't print anything after quiesce under OPAL, it crashes OFW */
- if (RELOC(of_platform) != PLATFORM_OPAL) {
+ if (of_platform != PLATFORM_OPAL) {
prom_printf("returning from prom_init\n");
prom_debug("->dt_header_start=0x%x\n", hdr);
}
@@ -3110,7 +3086,7 @@ unsigned long __init prom_init(unsigned
#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
/* OPAL early debug gets the OPAL base & entry in r8 and r9 */
__start(hdr, kbase, 0, 0, 0,
- RELOC(prom_opal_base), RELOC(prom_opal_entry));
+ prom_opal_base, prom_opal_entry);
#else
__start(hdr, kbase, 0, 0, 0, 0, 0);
#endif
^ permalink raw reply
* [PATCH 1/3] powerpc: Relocate prom_init.c on 64bit
From: Anton Blanchard @ 2012-11-27 3:39 UTC (permalink / raw)
To: benh, paulus, amodra; +Cc: linuxppc-dev
The ppc64 kernel can get loaded at any address which means
our very early init code in prom_init.c must be relocatable. We do
this with a pretty nasty RELOC() macro that we wrap accesses of
variables with. It is very fragile and sometimes we forget to add a
RELOC() to an uncommon path or sometimes a compiler change breaks it.
32bit has a much more elegant solution where we build prom_init.c
with -mrelocatable and then process the relocations manually.
Unfortunately we can't do the equivalent on 64bit and we would
have to build the entire kernel relocatable (-pie), resulting in a
large increase in kernel footprint (megabytes of relocation data).
The relocation data will be marked __initdata but it still creates
more pressure on our already tight memory layout at boot.
Alan Modra pointed out that the 64bit ABI is relocatable even
if we don't build with -pie, we just need to relocate the TOC.
This patch implements that idea and relocates the TOC entries of
prom_init.c. An added bonus is there are very few relocations to
process which helps keep boot times on simulators down.
gcc does not put 64bit integer constants into the TOC but to be
safe we may want a build time script which passes through the
prom_init.c TOC entries to make sure everything looks reasonable.
Signed-off-by: Anton Blanchard <anton@samba.org>
---
To keep the patch small and reviewable, I separated the removal
of the RELOC macro into a follow up patch.
For simplicity I do the relocation in C but if self brain surgery
keeps people up at night we can move it into assembly.
Index: b/arch/powerpc/kernel/prom_init.c
===================================================================
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -66,8 +66,8 @@
* is running at whatever address it has been loaded at.
* On ppc32 we compile with -mrelocatable, which means that references
* to extern and static variables get relocated automatically.
- * On ppc64 we have to relocate the references explicitly with
- * RELOC. (Note that strings count as static variables.)
+ * ppc64 objects are always relocatable, we just need to relocate the
+ * TOC.
*
* Because OF may have mapped I/O devices into the area starting at
* KERNELBASE, particularly on CHRP machines, we can't safely call
@@ -79,13 +79,12 @@
* On ppc64, 64 bit values are truncated to 32 bits (and
* fortunately don't get interpreted as two arguments).
*/
+#define RELOC(x) (x)
+#define ADDR(x) (u32)(unsigned long)(x)
+
#ifdef CONFIG_PPC64
-#define RELOC(x) (*PTRRELOC(&(x)))
-#define ADDR(x) (u32) add_reloc_offset((unsigned long)(x))
#define OF_WORKAROUNDS 0
#else
-#define RELOC(x) (x)
-#define ADDR(x) (u32) (x)
#define OF_WORKAROUNDS of_workarounds
int of_workarounds;
#endif
@@ -334,9 +333,6 @@ static void __init prom_printf(const cha
struct prom_t *_prom = &RELOC(prom);
va_start(args, format);
-#ifdef CONFIG_PPC64
- format = PTRRELOC(format);
-#endif
for (p = format; *p != 0; p = q) {
for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
;
@@ -437,9 +433,6 @@ static unsigned int __init prom_claim(un
static void __init __attribute__((noreturn)) prom_panic(const char *reason)
{
-#ifdef CONFIG_PPC64
- reason = PTRRELOC(reason);
-#endif
prom_print(reason);
/* Do not call exit because it clears the screen on pmac
* it also causes some sort of double-fault on early pmacs */
@@ -929,7 +922,7 @@ static void __init prom_send_capabilitie
* (we assume this is the same for all cores) and use it to
* divide NR_CPUS.
*/
- cores = (u32 *)PTRRELOC(&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]);
+ cores = (u32 *)&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET];
if (*cores != NR_CPUS) {
prom_printf("WARNING ! "
"ibm_architecture_vec structure inconsistent: %lu!\n",
@@ -2850,6 +2843,53 @@ static void __init prom_check_initrd(uns
#endif /* CONFIG_BLK_DEV_INITRD */
}
+#ifdef CONFIG_PPC64
+#ifdef CONFIG_RELOCATABLE
+static void reloc_toc(void)
+{
+}
+
+static void unreloc_toc(void)
+{
+}
+#else
+static void __reloc_toc(void *tocstart, unsigned long offset,
+ unsigned long nr_entries)
+{
+ unsigned long i;
+ unsigned long *toc_entry = (unsigned long *)tocstart;
+
+ for (i = 0; i < nr_entries; i++) {
+ *toc_entry = *toc_entry + offset;
+ toc_entry++;
+ }
+}
+
+static void reloc_toc(void)
+{
+ unsigned long offset = reloc_offset();
+ unsigned long nr_entries =
+ (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
+
+ /* Need to add offset to get at __prom_init_toc_start */
+ __reloc_toc(__prom_init_toc_start + offset, offset, nr_entries);
+
+ mb();
+}
+
+static void unreloc_toc(void)
+{
+ unsigned long offset = reloc_offset();
+ unsigned long nr_entries =
+ (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long);
+
+ mb();
+
+ /* __prom_init_toc_start has been relocated, no need to add offset */
+ __reloc_toc(__prom_init_toc_start, -offset, nr_entries);
+}
+#endif
+#endif
/*
* We enter here early on, when the Open Firmware prom is still
@@ -2867,6 +2907,8 @@ unsigned long __init prom_init(unsigned
#ifdef CONFIG_PPC32
unsigned long offset = reloc_offset();
reloc_got2(offset);
+#else
+ reloc_toc();
#endif
_prom = &RELOC(prom);
@@ -3061,6 +3103,8 @@ unsigned long __init prom_init(unsigned
#ifdef CONFIG_PPC32
reloc_got2(-offset);
+#else
+ unreloc_toc();
#endif
#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
Index: b/arch/powerpc/kernel/prom_init_check.sh
===================================================================
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -22,7 +22,7 @@ __secondary_hold_acknowledge __secondary
strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
reloc_got2 kernstart_addr memstart_addr linux_banner _stext
opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry
-boot_command_line"
+boot_command_line __prom_init_toc_start __prom_init_toc_end"
NM="$1"
OBJ="$2"
Index: b/arch/powerpc/kernel/vmlinux.lds.S
===================================================================
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -218,6 +218,11 @@ SECTIONS
.got : AT(ADDR(.got) - LOAD_OFFSET) {
__toc_start = .;
+#ifndef CONFIG_RELOCATABLE
+ __prom_init_toc_start = .;
+ arch/powerpc/kernel/prom_init.o*(.toc .got)
+ __prom_init_toc_end = .;
+#endif
*(.got)
*(.toc)
}
Index: b/arch/powerpc/Makefile
===================================================================
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -136,6 +136,7 @@ head-$(CONFIG_FSL_BOOKE) := arch/powerpc
head-$(CONFIG_PPC64) += arch/powerpc/kernel/entry_64.o
head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o
head-$(CONFIG_ALTIVEC) += arch/powerpc/kernel/vector.o
+head-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += arch/powerpc/kernel/prom_init.o
core-y += arch/powerpc/kernel/ \
arch/powerpc/mm/ \
Index: b/arch/powerpc/kernel/Makefile
===================================================================
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -91,7 +91,6 @@ obj-$(CONFIG_RELOCATABLE_PPC32) += reloc
obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
obj-$(CONFIG_KGDB) += kgdb.o
-obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
obj-$(CONFIG_MODULES) += ppc_ksyms.o
obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_SMP) += smp.o
@@ -142,6 +141,7 @@ GCOV_PROFILE_kprobes.o := n
extra-$(CONFIG_PPC_FPU) += fpu.o
extra-$(CONFIG_ALTIVEC) += vector.o
extra-$(CONFIG_PPC64) += entry_64.o
+extra-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o
extra-y += systbl_chk.i
$(obj)/systbl.o: systbl_chk
Index: b/arch/powerpc/include/asm/sections.h
===================================================================
--- a/arch/powerpc/include/asm/sections.h
+++ b/arch/powerpc/include/asm/sections.h
@@ -10,6 +10,9 @@
extern char __end_interrupts[];
+extern char __prom_init_toc_start[];
+extern char __prom_init_toc_end[];
+
static inline int in_kernel_text(unsigned long addr)
{
if (addr >= (unsigned long)_stext && addr < (unsigned long)__init_end)
^ permalink raw reply
* Re: [PATCH] vfio powerpc: enabled and supported on powernv platform
From: Alexey Kardashevskiy @ 2012-11-27 3:28 UTC (permalink / raw)
To: Alex Williamson
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Paul Mackerras,
Sethi Varun-B16395, linuxppc-dev@lists.ozlabs.org, David Gibson
In-Reply-To: <1353953070.1809.103.camel@bling.home>
On 27/11/12 05:04, Alex Williamson wrote:
> On Mon, 2012-11-26 at 08:18 -0700, Alex Williamson wrote:
>> On Fri, 2012-11-23 at 13:02 +1100, Alexey Kardashevskiy wrote:
>>> On 22/11/12 22:56, Sethi Varun-B16395 wrote:
>>>>
>>>>
>>>>> -----Original Message-----
>>>>> From: linux-kernel-owner@vger.kernel.org [mailto:linux-kernel-
>>>>> owner@vger.kernel.org] On Behalf Of Alex Williamson
>>>>> Sent: Tuesday, November 20, 2012 11:50 PM
>>>>> To: Alexey Kardashevskiy
>>>>> Cc: Benjamin Herrenschmidt; Paul Mackerras; linuxppc-
>>>>> dev@lists.ozlabs.org; linux-kernel@vger.kernel.org; kvm@vger.kernel.org;
>>>>> David Gibson
>>>>> Subject: Re: [PATCH] vfio powerpc: enabled and supported on powernv
>>>>> platform
>>>>>
>>>>> On Tue, 2012-11-20 at 11:48 +1100, Alexey Kardashevskiy wrote:
>>>>>> VFIO implements platform independent stuff such as a PCI driver, BAR
>>>>>> access (via read/write on a file descriptor or direct mapping when
>>>>>> possible) and IRQ signaling.
>>>>>> The platform dependent part includes IOMMU initialization and
>>>>>> handling.
>>>>>>
>>>>>> This patch initializes IOMMU groups based on the IOMMU configuration
>>>>>> discovered during the PCI scan, only POWERNV platform is supported at
>>>>>> the moment.
>>>>>>
>>>>>> Also the patch implements an VFIO-IOMMU driver which manages DMA
>>>>>> mapping/unmapping requests coming from the client (now QEMU). It also
>>>>>> returns a DMA window information to let the guest initialize the
>>>>>> device tree for a guest OS properly. Although this driver has been
>>>>>> tested only on POWERNV, it should work on any platform supporting TCE
>>>>>> tables.
>>>>>>
>>>>>> To enable VFIO on POWER, enable SPAPR_TCE_IOMMU config option.
>>>>>>
>>>>>> Cc: David Gibson <david@gibson.dropbear.id.au>
>>>>>> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>>>>>> ---
>>>>>> arch/powerpc/include/asm/iommu.h | 6 +
>>>>>> arch/powerpc/kernel/iommu.c | 140 +++++++++++++++++++
>>>>>> arch/powerpc/platforms/powernv/pci.c | 135 +++++++++++++++++++
>>>>>> drivers/iommu/Kconfig | 8 ++
>>>>>> drivers/vfio/Kconfig | 6 +
>>>>>> drivers/vfio/Makefile | 1 +
>>>>>> drivers/vfio/vfio_iommu_spapr_tce.c | 247
>>>>> ++++++++++++++++++++++++++++++++++
>>>>>> include/linux/vfio.h | 20 +++
>>>>>> 8 files changed, 563 insertions(+)
>>>>>> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>>>>>>
>>>>>> diff --git a/arch/powerpc/include/asm/iommu.h
>>>>>> b/arch/powerpc/include/asm/iommu.h
>>>>>> index cbfe678..5ba66cb 100644
>>>>>> --- a/arch/powerpc/include/asm/iommu.h
>>>>>> +++ b/arch/powerpc/include/asm/iommu.h
>>>>>> @@ -64,30 +64,33 @@ struct iommu_pool { }
>>>>>> ____cacheline_aligned_in_smp;
>>>>>>
>>>>>> struct iommu_table {
>>>>>> unsigned long it_busno; /* Bus number this table belongs to */
>>>>>> unsigned long it_size; /* Size of iommu table in entries */
>>>>>> unsigned long it_offset; /* Offset into global table */
>>>>>> unsigned long it_base; /* mapped address of tce table */
>>>>>> unsigned long it_index; /* which iommu table this is */
>>>>>> unsigned long it_type; /* type: PCI or Virtual Bus */
>>>>>> unsigned long it_blocksize; /* Entries in each block (cacheline)
>>>>> */
>>>>>> unsigned long poolsize;
>>>>>> unsigned long nr_pools;
>>>>>> struct iommu_pool large_pool;
>>>>>> struct iommu_pool pools[IOMMU_NR_POOLS];
>>>>>> unsigned long *it_map; /* A simple allocation bitmap for now
>>>>> */
>>>>>> +#ifdef CONFIG_IOMMU_API
>>>>>> + struct iommu_group *it_group;
>>>>>> +#endif
>>>>>> };
>>>>>>
>>>>>> struct scatterlist;
>>>>>>
>>>>>> static inline void set_iommu_table_base(struct device *dev, void
>>>>>> *base) {
>>>>>> dev->archdata.dma_data.iommu_table_base = base; }
>>>>>>
>>>>>> static inline void *get_iommu_table_base(struct device *dev) {
>>>>>> return dev->archdata.dma_data.iommu_table_base;
>>>>>> }
>>>>>>
>>>>>> /* Frees table for an individual device node */ @@ -135,17 +138,20 @@
>>>>>> static inline void pci_iommu_init(void) { } extern void
>>>>>> alloc_dart_table(void); #if defined(CONFIG_PPC64) &&
>>>>>> defined(CONFIG_PM) static inline void iommu_save(void) {
>>>>>> if (ppc_md.iommu_save)
>>>>>> ppc_md.iommu_save();
>>>>>> }
>>>>>>
>>>>>> static inline void iommu_restore(void) {
>>>>>> if (ppc_md.iommu_restore)
>>>>>> ppc_md.iommu_restore();
>>>>>> }
>>>>>> #endif
>>>>>>
>>>>>> +extern long iommu_put_tces(struct iommu_table *tbl, unsigned long
>>>>> entry, uint64_t tce,
>>>>>> + enum dma_data_direction direction, unsigned long pages);
>>>>>> +
>>>>>> #endif /* __KERNEL__ */
>>>>>> #endif /* _ASM_IOMMU_H */
>>>>>> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
>>>>>> index ff5a6ce..94f614b 100644
>>>>>> --- a/arch/powerpc/kernel/iommu.c
>>>>>> +++ b/arch/powerpc/kernel/iommu.c
>>>>>> @@ -32,30 +32,31 @@
>>>>>> #include <linux/dma-mapping.h>
>>>>>> #include <linux/bitmap.h>
>>>>>> #include <linux/iommu-helper.h>
>>>>>> #include <linux/crash_dump.h>
>>>>>> #include <linux/hash.h>
>>>>>> #include <linux/fault-inject.h>
>>>>>> #include <linux/pci.h>
>>>>>> #include <asm/io.h>
>>>>>> #include <asm/prom.h>
>>>>>> #include <asm/iommu.h>
>>>>>> #include <asm/pci-bridge.h>
>>>>>> #include <asm/machdep.h>
>>>>>> #include <asm/kdump.h>
>>>>>> #include <asm/fadump.h>
>>>>>> #include <asm/vio.h>
>>>>>> +#include <asm/tce.h>
>>>>>>
>>>>>> #define DBG(...)
>>>>>>
>>>>>> static int novmerge;
>>>>>>
>>>>>> static void __iommu_free(struct iommu_table *, dma_addr_t, unsigned
>>>>>> int);
>>>>>>
>>>>>> static int __init setup_iommu(char *str) {
>>>>>> if (!strcmp(str, "novmerge"))
>>>>>> novmerge = 1;
>>>>>> else if (!strcmp(str, "vmerge"))
>>>>>> novmerge = 0;
>>>>>> return 1;
>>>>>> }
>>>>>> @@ -844,15 +845,154 @@ void *iommu_alloc_coherent(struct device *dev,
>>>>>> struct iommu_table *tbl, }
>>>>>>
>>>>>> void iommu_free_coherent(struct iommu_table *tbl, size_t size,
>>>>>> void *vaddr, dma_addr_t dma_handle) {
>>>>>> if (tbl) {
>>>>>> unsigned int nio_pages;
>>>>>>
>>>>>> size = PAGE_ALIGN(size);
>>>>>> nio_pages = size >> IOMMU_PAGE_SHIFT;
>>>>>> iommu_free(tbl, dma_handle, nio_pages);
>>>>>> size = PAGE_ALIGN(size);
>>>>>> free_pages((unsigned long)vaddr, get_order(size));
>>>>>> }
>>>>>> }
>>>>>> +
>>>>>> +#ifdef CONFIG_IOMMU_API
>>>>>> +/*
>>>>>> + * SPAPR TCE API
>>>>>> + */
>>>>>> +static struct page *free_tce(struct iommu_table *tbl, unsigned long
>>>>>> +entry) {
>>>>>> + struct page *page = NULL;
>>>>>
>>>>> NULL initialization doesn't appear to be necessary
>>>>>
>>>>>> + unsigned long oldtce;
>>>>>> +
>>>>>> + oldtce = ppc_md.tce_get(tbl, entry);
>>>>>> +
>>>>>> + if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
>>>>>> + return NULL;
>>>>>> +
>>>>>> + page = pfn_to_page(oldtce >> PAGE_SHIFT);
>>>>>> +
>>>>>> + WARN_ON(!page);
>>>>>> + if (page && (oldtce & TCE_PCI_WRITE))
>>>>>> + SetPageDirty(page);
>>>>>> + ppc_md.tce_free(tbl, entry, 1);
>>>>>> +
>>>>>> + return page;
>>>>>> +}
>>>>>> +
>>>>>> +static int put_tce(struct iommu_table *tbl, unsigned long entry,
>>>>>> + uint64_t tce, enum dma_data_direction direction) {
>>>>>> + int ret;
>>>>>> + struct page *page = NULL;
>>>>>> + unsigned long kva, offset;
>>>>>> +
>>>>>> + /* Map new TCE */
>>>>>> + offset = (tce & IOMMU_PAGE_MASK) - (tce & PAGE_MASK);
>>>>>> + ret = get_user_pages_fast(tce & PAGE_MASK, 1,
>>>>>> + direction != DMA_TO_DEVICE, &page);
>>>>>> + if (ret < 1) {
>>>>>> + printk(KERN_ERR "tce_vfio: get_user_pages_fast failed
>>>>> tce=%llx ioba=%lx ret=%d\n",
>>>>>> + tce, entry << IOMMU_PAGE_SHIFT, ret);
>>>>>> + if (!ret)
>>>>>> + ret = -EFAULT;
>>>>>
>>>>> Missing return ret? Otherwise we've got some bogus uses of page below
>>>>> and we're setting ret for no reason here.
>>>>>
>>>>>> + }
>>>>>> +
>>>>>> + kva = (unsigned long) page_address(page);
>>>>>> + kva += offset;
>>>>>> +
>>>>>> + /* tce_build receives a virtual address */
>>>>>> + entry += tbl->it_offset; /* Offset into real TCE table */
>>>>>> + ret = ppc_md.tce_build(tbl, entry, 1, kva, direction, NULL);
>>>>>> +
>>>>>> + /* tce_build() only returns non-zero for transient errors */
>>>>>> + if (unlikely(ret)) {
>>>>>> + printk(KERN_ERR "tce_vfio: tce_put failed on tce=%llx
>>>>> ioba=%lx kva=%lx ret=%d\n",
>>>>>> + tce, entry << IOMMU_PAGE_SHIFT, kva, ret);
>>>>>> + put_page(page);
>>>>>> + return -EIO;
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void tce_flush(struct iommu_table *tbl) {
>>>>>> + /* Flush/invalidate TLB caches if necessary */
>>>>>> + if (ppc_md.tce_flush)
>>>>>> + ppc_md.tce_flush(tbl);
>>>>>> +
>>>>>> + /* Make sure updates are seen by hardware */
>>>>>> + mb();
>>>>>> +}
>>>>>> +
>>>>>> +long iommu_put_tces(struct iommu_table *tbl, unsigned long entry,
>>>>> uint64_t tce,
>>>>>> + enum dma_data_direction direction, unsigned long pages) {
>>>>>> + int i, ret = 0, pages_to_put = 0;
>>>>>> + struct page *page;
>>>>>> + struct iommu_pool *pool = get_pool(tbl, entry);
>>>>>> + struct page **oldpages;
>>>>>> + const int oldpagesnum = PAGE_SIZE/sizeof(*oldpages);
>>>>>> +
>>>>>> + BUILD_BUG_ON(PAGE_SIZE < IOMMU_PAGE_SIZE);
>>>>>> +
>>>>>> + /* Handle a single page request without allocation
>>>>>> + of pages-to-release array */
>>>>>> + if (pages == 1) {
>>>>>> + spin_lock(&(pool->lock));
>>>>>> + page = free_tce(tbl, entry);
>>>>>> +
>>>>>> + if (direction != DMA_NONE)
>>>>>> + ret = put_tce(tbl, entry, tce, direction);
>>>>>> +
>>>>>> + tce_flush(tbl);
>>>>>> +
>>>>>> + if (page)
>>>>>> + put_page(page);
>>>>>> +
>>>>>> + spin_unlock(&(pool->lock));
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + /* Releasing multiple pages */
>>>>>> + /* Allocate an array for pages to be released after TCE table
>>>>>> + is updated */
>>>>>> + oldpages = kmalloc(PAGE_SIZE, GFP_KERNEL);
>>>>>> + if (!oldpages)
>>>>>> + return -ENOMEM;
>>>>>> +
>>>>>> + spin_lock(&(pool->lock));
>>>>>> +
>>>>>> + for (i = 0; (i < pages) && !ret; ++i, ++entry, tce +=
>>>>> IOMMU_PAGE_SIZE) {
>>>>>> + page = free_tce(tbl, entry);
>>>>>> + if (page) {
>>>>>> + oldpages[pages_to_put] = page;
>>>>>> + ++pages_to_put;
>>>>>> + }
>>>>>> +
>>>>>> + if (direction != DMA_NONE)
>>>>>> + ret = put_tce(tbl, entry, tce, direction);
>>>>>> +
>>>>>> + /* Release old pages if we reached the end of oldpages[] or
>>>>>> + it is the last page or we are about to exit the loop */
>>>>>> + if ((pages_to_put == oldpagesnum) || (i == pages - 1) || ret)
>>>>> {
>>>>>> + tce_flush(tbl);
>>>>>
>>>>> Avoiding tce_flush() is the reason for all this extra overhead, right?
>>>>> I wonder if it'd be cleaner separating map vs unmap, where the map case
>>>>> can avoid the oldpages array... but that means inserting new mappings on
>>>>> top of old ones wouldn't put the pages.
>>>
>>>
>>> Yes, we do not want to loose pages if the guest forgot to unmap them.
>>
>> Hmm, does that mean we're not actively clearing tce entries or somehow
>> disabling the iommu window when the iommu is released through vfio?
>
> Ok, I see tces are put on shutdown via tce_iommu_detach_group, so you're
> more concerned about the guest simply mapping over top of it's own
> mappings. Is that common? Is it common enough for every multi-page
> mapping to assume it will happen? I know this is a performance
> sensitive path for you and it seems like a map-only w/ fallback to
> unmap, remap would be better in the general case.
I do not get it. Where exactly does the performance suffer?
iommu_put_tces() with non zero "tce" (i.e. "map") has to check if the entry
is not used, at least to return EBUSY when it is, and this check is
performed. If it is zero, there is no overhead at all. And it is going to
be the 99.(9)% case as the guest (un)maps one page per call.
Generally speaking we want to move "put tce" completely to the kernel for
the (much) better performance and vfio won't be dealing with it all.
We already agreed that SPAPR TCE driver uses x86 (aka type1) API but I do
not see why the powerpc implementation should look x86 alike as it still
operates with powerpc machine dependent callbacks so the reader has to have
some powerpc knowledge.
> On x86 we do exactly that, but we do the unmap, remap from userspace
> when we get an EBUSY. Thanks,
--
Alexey
^ permalink raw reply
* [PATCH 16/16] powerpc: Documentation for transactional memory on powerpc
From: Michael Neuling @ 2012-11-27 2:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>
Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
Documentation/powerpc/transactional_memory.txt | 119 ++++++++++++++++++++++++
1 file changed, 119 insertions(+)
create mode 100644 Documentation/powerpc/transactional_memory.txt
diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt
new file mode 100644
index 0000000..881df6b
--- /dev/null
+++ b/Documentation/powerpc/transactional_memory.txt
@@ -0,0 +1,119 @@
+Transactional Memory support
+============================
+
+POWER kernel support for this feature is currently limited to supporting
+its use by user programs. It is not currently used by the kernel itself.
+
+This file aims to sum up how it is supported by Linux and what behaviour you
+can expect from your user programs.
+
+
+Basic overview
+==============
+
+Hardware Transactional Memory is supported on POWER8 processors, and is a
+feature that enables a different form of atomic memory access. Several new
+instructions are presented to delimit transactions; transactions are
+guaranteed to either complete atomically or roll back and undo any partial
+changes.
+
+A simple transaction looks like this:
+
+begin_move_money:
+ tbegin
+ beq abort_handler
+
+ ld r4, SAVINGS_ACCT(r3)
+ ld r5, CURRENT_ACCT(r3)
+ subi r5, r5, 1
+ addi r4, r4, 1
+ std r4, SAVINGS_ACCT(r3)
+ std r5, CURRENT_ACCT(r3)
+
+ tend
+
+ b continue
+
+abort_handler:
+ ... test for odd failures ...
+
+ /* Retry the transaction if it failed because it conflicted with
+ * someone else: */
+ b begin_move_money
+
+
+The 'tbegin' instruction denotes the start point, and 'tend' the end point.
+Between these points the processor is in 'Transactional' state; any memory
+references will complete in one go if there are no conflicts with other
+transactional or non-transactional accesses within the system. In this
+example, the transaction completes as though it were normal straight-line code
+IF no other processor has touched SAVINGS_ACCT(r3) or CURRENT_ACCT(r3); an
+atomic move of money from the current account to the savings account has been
+performed. Even though the normal ld/std instructions are used (note no
+lwarx/stwcx), either *both* SAVINGS_ACCT(r3) and CURRENT_ACCT(r3) will be
+updated, or neither will be updated.
+
+If, in the meantime, there is a conflict with the locations accessed by the
+transaction, the transaction will be aborted by the CPU. Register and memory
+state will roll back to that at the 'tbegin', and control will continue from
+'tbegin+4'. The branch to abort_handler will be taken this second time; the
+abort handler can check the cause of the failure, and retry.
+
+Checkpointed registers include all GPRs, FPRs, VRs/VSRs, LR, CCR/CR, CTR, FPCSR
+and a few other status/flag regs; see the ISA for details.
+
+Causes of transaction aborts
+============================
+
+- Conflicts with cache lines used by other processors
+- Signals
+- Context switches
+- See the ISA for full documentation of everything that will abort transactions.
+
+
+Syscalls
+========
+
+Performing syscalls from within transaction is not recommended, and can lead
+to unpredictable results.
+
+Syscalls do not by design abort transactions, but beware: The kernel code will
+not be running in transactional state. The effect of syscalls will always
+remain visible, but depending on the call they may abort your transaction as a
+side-effect, read soon-to-be-aborted transactional data that should not remain
+invisible, etc. If you constantly retry a transaction that constantly aborts
+itself by calling a syscall, you'll have a livelock & make no progress.
+
+Simple syscalls (e.g. sigprocmask()) "could" be OK. Even things like write()
+from, say, printf() should be OK as long as the kernel does not access any
+memory that was accessed transactionally.
+
+Consider any syscalls that happen to work as debug-only -- not recommended for
+production use. Best to queue them up till after the transaction is over.
+
+
+Failure cause codes used by kernel
+==================================
+
+These are defined in <asm/reg.h>, and distinguish different reasons why the
+kernel aborted a transaction:
+
+ TM_CAUSE_RESCHED Thread was rescheduled.
+ TM_CAUSE_FAC_UNAV FP/VEC/VSX unavailable trap.
+ TM_CAUSE_SYSCALL Currently unused; future syscalls that must abort
+ transactions for consistency will use this.
+ TM_CAUSE_SIGNAL Signal delivered.
+ TM_CAUSE_MISC Currently unused.
+
+These can be checked by the user program's abort handler as TEXASR[0:7].
+
+
+GDB
+===
+
+GDB and ptrace are not currently TM-aware. If one stops during a transaction,
+it looks like the transaction has just started (the checkpointed state is
+presented). The transaction cannot then be continued and will take the failure
+handler route. Furthermore, the transactional 2nd register state will be
+inaccessible. GDB can currently be used on programs using TM, but not sensibly
+in parts within transactions.
--
1.7.9.5
^ permalink raw reply related
* [PATCH 15/16] powerpc: Add transactional memory to pseries and ppc64 defconfigs
From: Michael Neuling @ 2012-11-27 2:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>
Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
arch/powerpc/configs/ppc64_defconfig | 1 +
arch/powerpc/configs/pseries_defconfig | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index 6d03530..26000f6 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -46,6 +46,7 @@ CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_HZ_100=y
CONFIG_BINFMT_MISC=m
+CONFIG_TRANSACTIONAL_MEM=y
CONFIG_HOTPLUG_CPU=y
CONFIG_KEXEC=y
CONFIG_IRQ_ALL_CPUS=y
diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig
index 5b8e1e5..b5f94b7 100644
--- a/arch/powerpc/configs/pseries_defconfig
+++ b/arch/powerpc/configs/pseries_defconfig
@@ -40,6 +40,7 @@ CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_HZ_100=y
CONFIG_BINFMT_MISC=m
+CONFIG_TRANSACTIONAL_MEM=y
CONFIG_HOTPLUG_CPU=y
CONFIG_KEXEC=y
CONFIG_IRQ_ALL_CPUS=y
--
1.7.9.5
^ permalink raw reply related
* [PATCH 14/16] powerpc: Add config option for transactional memory
From: Michael Neuling @ 2012-11-27 2:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Michael Neuling, linuxppc-dev, Matt Evans
In-Reply-To: <1353984488-1283-1-git-send-email-mikey@neuling.org>
Kconfig option for transactional memory on powerpc.
Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Michael Neuling <mikey@neuling.org>
---
arch/powerpc/Kconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index a902a5c..ece67ca 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -308,6 +308,14 @@ config MATH_EMULATION
unit, which will allow programs that use floating-point
instructions to run.
+config TRANSACTIONAL_MEM
+ bool "Transactional Memory support"
+ depends on PPC64
+ depends on SMP
+ default n
+ ---help---
+ Support user-mode Transactional Memory.
+
config 8XX_MINIMAL_FPEMU
bool "Minimal math emulation for 8xx"
depends on 8xx && !MATH_EMULATION
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox