LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] powerpc: Fix build errors STRICT_MM_TYPECHECKS
From: Aneesh Kumar K.V @ 2013-05-06 20:51 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev, Aneesh Kumar K.V

From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/pte-hash64-64k.h | 2 +-
 arch/powerpc/kernel/pci-common.c          | 5 ++---
 arch/powerpc/mm/init_64.c                 | 3 ++-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/pte-hash64-64k.h b/arch/powerpc/include/asm/pte-hash64-64k.h
index 7ad8ef1..98e27be 100644
--- a/arch/powerpc/include/asm/pte-hash64-64k.h
+++ b/arch/powerpc/include/asm/pte-hash64-64k.h
@@ -58,7 +58,7 @@
  * generic accessors and iterators here
  */
 #define __real_pte(e,p) 	((real_pte_t) { \
-			(e), ((e) & _PAGE_COMBO) ? \
+			(e), (pte_val(e) & _PAGE_COMBO) ? \
 				(pte_val(*((p) + PTRS_PER_PTE))) : 0 })
 #define __rpte_to_hidx(r,index)	((pte_val((r).pte) & _PAGE_COMBO) ? \
         (((r).hidx >> ((index)<<2)) & 0xf) : ((pte_val((r).pte) >> 12) & 0xf))
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index f325dc9..fd4b917 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -359,7 +359,6 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp,
 				      enum pci_mmap_state mmap_state,
 				      int write_combine)
 {
-	unsigned long prot = pgprot_val(protection);
 
 	/* Write combine is always 0 on non-memory space mappings. On
 	 * memory space, if the user didn't pass 1, we check for a
@@ -376,9 +375,9 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp,
 
 	/* XXX would be nice to have a way to ask for write-through */
 	if (write_combine)
-		return pgprot_noncached_wc(prot);
+		return pgprot_noncached_wc(protection);
 	else
-		return pgprot_noncached(prot);
+		return pgprot_noncached(protection);
 }
 
 /*
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index c9ec657..d0cd9e4 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -218,7 +218,8 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
 					     unsigned long phys)
 {
 	int  mapped = htab_bolt_mapping(start, start + page_size, phys,
-					PAGE_KERNEL, mmu_vmemmap_psize,
+					pgprot_val(PAGE_KERNEL),
+					mmu_vmemmap_psize,
 					mmu_kernel_ssize);
 	BUG_ON(mapped < 0);
 }
-- 
1.8.1.2

^ permalink raw reply related

* Re: [PATCH v2 1/4] powerpc/cputable: reserve bits in HWCAP2 for new features
From: Ryan Arnold @ 2013-05-06 19:07 UTC (permalink / raw)
  To: Nishanth Aravamudan
  Cc: Michael Neuling, Michael R Meissner, Steve Munroe, Peter Bergner,
	linuxppc-dev
In-Reply-To: <20130504004756.GA3532@linux.vnet.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 939 bytes --]

Hi Nish,

Nishanth Aravamudan <nacc@linux.vnet.ibm.com> wrote on 05/03/2013 07:47:56
PM:
> +/* in AT_HWCAP2 */
> +#define PPC_FEATURE2_ARCH_2_07      0x80000000
> +#define PPC_FEATURE2_HTM      0x40000000
> +#define PPC_FEATURE2_DSCR      0x20000000
> +#define PPC_FEATURE2_EBB      0x10000000
> +#define PPC_FEATURE2_ISEL      0x08000000
> +#define PPC_FEATURE2_TAR      0x04000000
> +
>  #endif /* _UAPI__ASM_POWERPC_CPUTABLE_H */

Following the existing naming convention in cputable.h, the features should
probably be amended to the following:

#define PPC_FEATURE2_ARCH_2_07      0x80000000
#define PPC_FEATURE2_HAS_HTM        0x40000000
#define PPC_FEATURE2_HAS_DSC        0x20000000
#define PPC_FEATURE2_HAS_EBB        0x10000000
#define PPC_FEATURE2_HAS_ISEL       0x08000000
#define PPC_FEATURE2_HAS_TAR        0x04000000

Notice that I changed DSCR to DSC.  The 'R' wasn't descriptive.

Ryan S. Arnold
IBM Linux Technology Center

[-- Attachment #2: Type: text/html, Size: 1690 bytes --]

^ permalink raw reply

* Re: [PATCH] powerpc/pci: Support per-aperture memory offset
From: Bjorn Helgaas @ 2013-05-06 17:55 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Thomas Petazzoni, linux-pci@vger.kernel.org, Andrew Murray,
	Scott Wood, Sethi Varun-B16395, linuxppc-dev
In-Reply-To: <1367823016.15842.22.camel@pasglop>

On Sun, May 5, 2013 at 11:50 PM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> The PCI core supports an offset per aperture nowadays but our arch
> code still has a single offset per host bridge representing the
> difference betwen CPU memory addresses and PCI MMIO addresses.
>
> This is a problem as new machines and hypervisor versions are
> coming out where the 64-bit windows will have a different offset
> (basically mapped 1:1) from the 32-bit windows.
>
> This fixes it by using separate offsets. In the long run, we probably
> want to get rid of that intermediary struct pci_controller and have
> those directly stored into the pci_host_bridge as they are parsed
> but this will be a more invasive change.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This doesn't touch the PCI core, but it looks good to me, for whatever
that's worth.

> ---
>
> Now, this is a big one but I'd like to still merge it for 3.10 because
> we're having new machine coming up (and new versions of pHyp on existing
> machines) that are going to expose MMIO windows with different offsets
> (basically our 64-bit windows are 1:1 and our 32-bit windows remapped).
>
> I'm not expecting any major issue with the patch, I've tested it on some
> of our machines here and will test it more during the next couple of days
> the notable thing is the removal of a "workaround" / fallback on 32-bit
> that I suspect only ever mattered for machines long unsupported (PReP ?)
> as I don't think we have anything that doesn't populate the bridge
> resources and expects to work nowadays (other stuff would break anyway).
>
> This is also why I'm NAK'ing the patch making pci_process_bridge_OF_ranges()
> generic since I need to change it for powerpc and this isn't the right
> long term approach (we should "merge" pci_controller & pci_host_bridge
> instead and directly populate the pci_host_bridge apertures).
>
> If I see no major issue with the patch during the next few days, I'll send
> it to Linus with my next pull request, probably at -rc1.
>
> Kumar, Scott, Sethi, please test on FSL HW. I'll take care of macs and 4xx
> in addition to the various IBM ppc64 platforms.
>
> Ben.
>
>  arch/powerpc/include/asm/pci-bridge.h       |    6 +-
>  arch/powerpc/kernel/pci-common.c            |   97 ++++++++-------------------
>  arch/powerpc/kernel/pci_32.c                |    2 +-
>  arch/powerpc/kernel/pci_64.c                |    2 +-
>  arch/powerpc/platforms/embedded6xx/mpc10x.h |   11 ---
>  arch/powerpc/platforms/powermac/pci.c       |    2 +-
>  arch/powerpc/platforms/powernv/pci-ioda.c   |   10 +--
>  arch/powerpc/platforms/wsp/wsp_pci.c        |    2 +-
>  arch/powerpc/sysdev/fsl_pci.c               |   11 +--
>  arch/powerpc/sysdev/ppc4xx_pci.c            |   15 +++--
>  10 files changed, 54 insertions(+), 104 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
> index 0694f73..8b11b5b 100644
> --- a/arch/powerpc/include/asm/pci-bridge.h
> +++ b/arch/powerpc/include/asm/pci-bridge.h
> @@ -39,11 +39,6 @@ struct pci_controller {
>         resource_size_t io_base_phys;
>         resource_size_t pci_io_size;
>
> -       /* Some machines (PReP) have a non 1:1 mapping of
> -        * the PCI memory space in the CPU bus space
> -        */
> -       resource_size_t pci_mem_offset;
> -
>         /* Some machines have a special region to forward the ISA
>          * "memory" cycles such as VGA memory regions. Left to 0
>          * if unsupported
> @@ -86,6 +81,7 @@ struct pci_controller {
>          */
>         struct resource io_resource;
>         struct resource mem_resources[3];
> +       resource_size_t mem_offset[3];
>         int global_number;              /* PCI domain number */
>
>         resource_size_t dma_window_base_cur;
> diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
> index cf00588..f5c5c90 100644
> --- a/arch/powerpc/kernel/pci-common.c
> +++ b/arch/powerpc/kernel/pci-common.c
> @@ -786,22 +786,8 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose,
>                                 hose->isa_mem_size = size;
>                         }
>
> -                       /* We get the PCI/Mem offset from the first range or
> -                        * the, current one if the offset came from an ISA
> -                        * hole. If they don't match, bugger.
> -                        */
> -                       if (memno == 0 ||
> -                           (isa_hole >= 0 && pci_addr != 0 &&
> -                            hose->pci_mem_offset == isa_mb))
> -                               hose->pci_mem_offset = cpu_addr - pci_addr;
> -                       else if (pci_addr != 0 &&
> -                                hose->pci_mem_offset != cpu_addr - pci_addr) {
> -                               printk(KERN_INFO
> -                                      " \\--> Skipped (offset mismatch) !\n");
> -                               continue;
> -                       }
> -
>                         /* Build resource */
> +                       hose->mem_offset[memno] = cpu_addr - pci_addr;
>                         res = &hose->mem_resources[memno++];
>                         res->flags = IORESOURCE_MEM;
>                         if (pci_space & 0x40000000)
> @@ -817,20 +803,6 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose,
>                         res->child = NULL;
>                 }
>         }
> -
> -       /* If there's an ISA hole and the pci_mem_offset is -not- matching
> -        * the ISA hole offset, then we need to remove the ISA hole from
> -        * the resource list for that brige
> -        */
> -       if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) {
> -               unsigned int next = isa_hole + 1;
> -               printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb);
> -               if (next < memno)
> -                       memmove(&hose->mem_resources[isa_hole],
> -                               &hose->mem_resources[next],
> -                               sizeof(struct resource) * (memno - next));
> -               hose->mem_resources[--memno].flags = 0;
> -       }
>  }
>
>  /* Decide whether to display the domain number in /proc */
> @@ -916,6 +888,7 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
>         struct pci_controller *hose = pci_bus_to_host(bus);
>         struct pci_dev *dev = bus->self;
>         resource_size_t offset;
> +       struct pci_bus_region region;
>         u16 command;
>         int i;
>
> @@ -925,10 +898,10 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
>
>         /* Job is a bit different between memory and IO */
>         if (res->flags & IORESOURCE_MEM) {
> -               /* If the BAR is non-0 (res != pci_mem_offset) then it's probably been
> -                * initialized by somebody
> -                */
> -               if (res->start != hose->pci_mem_offset)
> +               pcibios_resource_to_bus(dev, &region, res);
> +
> +               /* If the BAR is non-0 then it's probably been initialized */
> +               if (region.start != 0)
>                         return 0;
>
>                 /* The BAR is 0, let's check if memory decoding is enabled on
> @@ -940,11 +913,11 @@ static int pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
>
>                 /* Memory decoding is enabled and the BAR is 0. If any of the bridge
>                  * resources covers that starting address (0 then it's good enough for
> -                * us for memory
> +                * us for memory space)
>                  */
>                 for (i = 0; i < 3; i++) {
>                         if ((hose->mem_resources[i].flags & IORESOURCE_MEM) &&
> -                           hose->mem_resources[i].start == hose->pci_mem_offset)
> +                           hose->mem_resources[i].start == hose->mem_offset[i])
>                                 return 0;
>                 }
>
> @@ -1381,10 +1354,9 @@ static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus)
>
>   no_io:
>         /* Check for memory */
> -       offset = hose->pci_mem_offset;
> -       pr_debug("hose mem offset: %016llx\n", (unsigned long long)offset);
>         for (i = 0; i < 3; i++) {
>                 pres = &hose->mem_resources[i];
> +               offset = hose->mem_offset[i];
>                 if (!(pres->flags & IORESOURCE_MEM))
>                         continue;
>                 pr_debug("hose mem res: %pR\n", pres);
> @@ -1524,6 +1496,7 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
>                                         struct list_head *resources)
>  {
>         struct resource *res;
> +       resource_size_t offset;
>         int i;
>
>         /* Hookup PHB IO resource */
> @@ -1533,51 +1506,37 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose,
>                 printk(KERN_WARNING "PCI: I/O resource not set for host"
>                        " bridge %s (domain %d)\n",
>                        hose->dn->full_name, hose->global_number);
> -#ifdef CONFIG_PPC32
> -               /* Workaround for lack of IO resource only on 32-bit */
> -               res->start = (unsigned long)hose->io_base_virt - isa_io_base;
> -               res->end = res->start + IO_SPACE_LIMIT;
> -               res->flags = IORESOURCE_IO;
> -#endif /* CONFIG_PPC32 */
> -       }
> -       if (res->flags) {
> -               pr_debug("PCI: PHB IO resource    = %016llx-%016llx [%lx]\n",
> +       } else {
> +               offset = pcibios_io_space_offset(hose);
> +
> +               pr_debug("PCI: PHB IO resource    = %08llx-%08llx [%lx] off 0x%08llx\n",
>                          (unsigned long long)res->start,
>                          (unsigned long long)res->end,
> -                        (unsigned long)res->flags);
> -               pci_add_resource_offset(resources, res, pcibios_io_space_offset(hose));
> -
> -               pr_debug("PCI: PHB IO  offset     = %08lx\n",
> -                        (unsigned long)hose->io_base_virt - _IO_BASE);
> +                        (unsigned long)res->flags,
> +                        (unsigned long long)offset);

If you use %pR, these will match any similar messages from the PCI core.

> +               pci_add_resource_offset(resources, res, offset);
>         }
>
>         /* Hookup PHB Memory resources */
>         for (i = 0; i < 3; ++i) {
>                 res = &hose->mem_resources[i];
>                 if (!res->flags) {
> -                       if (i > 0)
> -                               continue;
>                         printk(KERN_ERR "PCI: Memory resource 0 not set for "
>                                "host bridge %s (domain %d)\n",
>                                hose->dn->full_name, hose->global_number);

I don't understand what's going on here, but the message no longer
matches the code (we refer to "Memory resource 0," but it might be 0,
1, or 2).

> -#ifdef CONFIG_PPC32
> -                       /* Workaround for lack of MEM resource only on 32-bit */
> -                       res->start = hose->pci_mem_offset;
> -                       res->end = (resource_size_t)-1LL;
> -                       res->flags = IORESOURCE_MEM;
> -#endif /* CONFIG_PPC32 */
> -               }
> -               if (res->flags) {
> -                       pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n", i,
> -                                (unsigned long long)res->start,
> -                                (unsigned long long)res->end,
> -                                (unsigned long)res->flags);
> -                       pci_add_resource_offset(resources, res, hose->pci_mem_offset);
> +                       continue;
>                 }
> -       }
> +               offset = hose->mem_offset[i];
>
> -       pr_debug("PCI: PHB MEM offset     = %016llx\n",
> -                (unsigned long long)hose->pci_mem_offset);
> +
> +               pr_debug("PCI: PHB MEM resource %d = %08llx-%08llx [%lx] off 0x%08llx\n", i,
> +                        (unsigned long long)res->start,
> +                        (unsigned long long)res->end,
> +                        (unsigned long)res->flags,
> +                        (unsigned long long)offset);
> +
> +               pci_add_resource_offset(resources, res, offset);
> +       }
>  }
>
>  /*
> diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
> index e37c215..432459c 100644
> --- a/arch/powerpc/kernel/pci_32.c
> +++ b/arch/powerpc/kernel/pci_32.c
> @@ -295,7 +295,7 @@ long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
>         case IOBASE_BRIDGE_NUMBER:
>                 return (long)hose->first_busno;
>         case IOBASE_MEMORY:
> -               return (long)hose->pci_mem_offset;
> +               return (long)hose->mem_offset[0];
>         case IOBASE_IO:
>                 return (long)hose->io_base_phys;
>         case IOBASE_ISA_IO:
> diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
> index 51a133a..873050d 100644
> --- a/arch/powerpc/kernel/pci_64.c
> +++ b/arch/powerpc/kernel/pci_64.c
> @@ -246,7 +246,7 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus,
>         case IOBASE_BRIDGE_NUMBER:
>                 return (long)hose->first_busno;
>         case IOBASE_MEMORY:
> -               return (long)hose->pci_mem_offset;
> +               return (long)hose->mem_offset[0];
>         case IOBASE_IO:
>                 return (long)hose->io_base_phys;
>         case IOBASE_ISA_IO:
> diff --git a/arch/powerpc/platforms/embedded6xx/mpc10x.h b/arch/powerpc/platforms/embedded6xx/mpc10x.h
> index b30a6a3..b290b63 100644
> --- a/arch/powerpc/platforms/embedded6xx/mpc10x.h
> +++ b/arch/powerpc/platforms/embedded6xx/mpc10x.h
> @@ -81,17 +81,6 @@
>  #define        MPC10X_MAPB_PCI_MEM_OFFSET      (MPC10X_MAPB_ISA_MEM_BASE -     \
>                                          MPC10X_MAPB_PCI_MEM_START)
>
> -/* Set hose members to values appropriate for the mem map used */
> -#define        MPC10X_SETUP_HOSE(hose, map) {                                  \
> -       (hose)->pci_mem_offset = MPC10X_MAP##map##_PCI_MEM_OFFSET;      \
> -       (hose)->io_space.start = MPC10X_MAP##map##_PCI_IO_START;        \
> -       (hose)->io_space.end = MPC10X_MAP##map##_PCI_IO_END;            \
> -       (hose)->mem_space.start = MPC10X_MAP##map##_PCI_MEM_START;      \
> -       (hose)->mem_space.end = MPC10X_MAP##map##_PCI_MEM_END;          \
> -       (hose)->io_base_virt = (void *)MPC10X_MAP##map##_ISA_IO_BASE;   \
> -}

I guess this was previously unused; I don't see any corresponding
changes to a user of MPC10X_SETUP_HOSE().

> -
> -
>  /* Miscellaneous Configuration register offsets */
>  #define        MPC10X_CFG_PIR_REG              0x09
>  #define        MPC10X_CFG_PIR_HOST_BRIDGE      0x00
> diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
> index 2b8af75..cf7009b 100644
> --- a/arch/powerpc/platforms/powermac/pci.c
> +++ b/arch/powerpc/platforms/powermac/pci.c
> @@ -824,6 +824,7 @@ static void __init parse_region_decode(struct pci_controller *hose,
>                         hose->mem_resources[cur].name = hose->dn->full_name;
>                         hose->mem_resources[cur].start = base;
>                         hose->mem_resources[cur].end = end;
> +                       hose->mem_offset[cur] = 0;
>                         DBG("  %d: 0x%08lx-0x%08lx\n", cur, base, end);
>                 } else {
>                         DBG("   :           -0x%08lx\n", end);
> @@ -866,7 +867,6 @@ static void __init setup_u3_ht(struct pci_controller* hose)
>         hose->io_resource.start = 0;
>         hose->io_resource.end = 0x003fffff;
>         hose->io_resource.flags = IORESOURCE_IO;
> -       hose->pci_mem_offset = 0;
>         hose->first_busno = 0;
>         hose->last_busno = 0xef;
>
> diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
> index 97b08fc..1da578b 100644
> --- a/arch/powerpc/platforms/powernv/pci-ioda.c
> +++ b/arch/powerpc/platforms/powernv/pci-ioda.c
> @@ -915,11 +915,14 @@ static void pnv_ioda_setup_pe_seg(struct pci_controller *hose,
>                                 index++;
>                         }
>                 } else if (res->flags & IORESOURCE_MEM) {
> +                       /* WARNING: Assumes M32 is mem region 0 in PHB. We need to
> +                        * harden that algorithm when we start supporting M64
> +                        */
>                         region.start = res->start -
> -                                      hose->pci_mem_offset -
> +                                      hose->mem_offset[0] -
>                                        phb->ioda.m32_pci_base;
>                         region.end   = res->end -
> -                                      hose->pci_mem_offset -
> +                                      hose->mem_offset[0] -
>                                        phb->ioda.m32_pci_base;
>                         index = region.start / phb->ioda.m32_segsize;
>
> @@ -1115,8 +1118,7 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, int ioda_type)
>         phb->ioda.m32_size += 0x10000;
>
>         phb->ioda.m32_segsize = phb->ioda.m32_size / phb->ioda.total_pe;
> -       phb->ioda.m32_pci_base = hose->mem_resources[0].start -
> -               hose->pci_mem_offset;
> +       phb->ioda.m32_pci_base = hose->mem_resources[0].start - hose->mem_offset[0];
>         phb->ioda.io_size = hose->pci_io_size;
>         phb->ioda.io_segsize = phb->ioda.io_size / phb->ioda.total_pe;
>         phb->ioda.io_pci_base = 0; /* XXX calculate this ? */
> diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c
> index 8e22f56..62cb527 100644
> --- a/arch/powerpc/platforms/wsp/wsp_pci.c
> +++ b/arch/powerpc/platforms/wsp/wsp_pci.c
> @@ -502,7 +502,7 @@ static void __init wsp_pcie_configure_hw(struct pci_controller *hose)
>                  (~(hose->mem_resources[0].end -
>                     hose->mem_resources[0].start)) & 0x3ffffff0000ul);
>         out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR,
> -                (hose->mem_resources[0].start - hose->pci_mem_offset) | 1);
> +                (hose->mem_resources[0].start - hose->mem_offset[0]) | 1);
>
>         /* Clear all TVT entries
>          *
> diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
> index cffe7ed..028ac1f 100644
> --- a/arch/powerpc/sysdev/fsl_pci.c
> +++ b/arch/powerpc/sysdev/fsl_pci.c
> @@ -178,7 +178,7 @@ static void setup_pci_atmu(struct pci_controller *hose)
>         struct ccsr_pci __iomem *pci = hose->private_data;
>         int i, j, n, mem_log, win_idx = 3, start_idx = 1, end_idx = 4;
>         u64 mem, sz, paddr_hi = 0;
> -       u64 paddr_lo = ULLONG_MAX;
> +       u64 offset = 0, paddr_lo = ULLONG_MAX;
>         u32 pcicsrbar = 0, pcicsrbar_sz;
>         u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL |
>                         PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
> @@ -208,8 +208,9 @@ static void setup_pci_atmu(struct pci_controller *hose)
>                 paddr_lo = min(paddr_lo, (u64)hose->mem_resources[i].start);
>                 paddr_hi = max(paddr_hi, (u64)hose->mem_resources[i].end);
>
> -               n = setup_one_atmu(pci, j, &hose->mem_resources[i],
> -                                  hose->pci_mem_offset);
> +               /* We assume all memory resources have the same offset */
> +               offset = hose->mem_offset[i];

This code doesn't *look* like you're assuming all resources have the
same offset, although maybe elsewhere you set every
hose->mem_offset[i] to the same value.

> +               n = setup_one_atmu(pci, j, &hose->mem_resources[i], offset);
>
>                 if (n < 0 || j >= 5) {
>                         pr_err("Ran out of outbound PCI ATMUs for resource %d!\n", i);
> @@ -239,8 +240,8 @@ static void setup_pci_atmu(struct pci_controller *hose)
>         }
>
>         /* convert to pci address space */
> -       paddr_hi -= hose->pci_mem_offset;
> -       paddr_lo -= hose->pci_mem_offset;
> +       paddr_hi -= offset;
> +       paddr_lo -= offset;
>
>         if (paddr_hi == paddr_lo) {
>                 pr_err("%s: No outbound window space\n", name);
> diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
> index 56e8b3c..64603a1 100644
> --- a/arch/powerpc/sysdev/ppc4xx_pci.c
> +++ b/arch/powerpc/sysdev/ppc4xx_pci.c
> @@ -257,6 +257,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
>         /* Setup outbound memory windows */
>         for (i = j = 0; i < 3; i++) {
>                 struct resource *res = &hose->mem_resources[i];
> +               resource_size_t offset = hose->mem_offset[i];
>
>                 /* we only care about memory windows */
>                 if (!(res->flags & IORESOURCE_MEM))
> @@ -270,7 +271,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
>                 /* Configure the resource */
>                 if (ppc4xx_setup_one_pci_PMM(hose, reg,
>                                              res->start,
> -                                            res->start - hose->pci_mem_offset,
> +                                            res->start - offset,
>                                              resource_size(res),
>                                              res->flags,
>                                              j) == 0) {
> @@ -279,7 +280,7 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
>                         /* If the resource PCI address is 0 then we have our
>                          * ISA memory hole
>                          */
> -                       if (res->start == hose->pci_mem_offset)
> +                       if (res->start == offset)
>                                 found_isa_hole = 1;
>                 }
>         }
> @@ -457,6 +458,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
>         /* Setup outbound memory windows */
>         for (i = j = 0; i < 3; i++) {
>                 struct resource *res = &hose->mem_resources[i];
> +               resource_size_t offset = hose->mem_offset[i];
>
>                 /* we only care about memory windows */
>                 if (!(res->flags & IORESOURCE_MEM))
> @@ -470,7 +472,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
>                 /* Configure the resource */
>                 if (ppc4xx_setup_one_pcix_POM(hose, reg,
>                                               res->start,
> -                                             res->start - hose->pci_mem_offset,
> +                                             res->start - offset,
>                                               resource_size(res),
>                                               res->flags,
>                                               j) == 0) {
> @@ -479,7 +481,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
>                         /* If the resource PCI address is 0 then we have our
>                          * ISA memory hole
>                          */
> -                       if (res->start == hose->pci_mem_offset)
> +                       if (res->start == offset)
>                                 found_isa_hole = 1;
>                 }
>         }
> @@ -1792,6 +1794,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
>         /* Setup outbound memory windows */
>         for (i = j = 0; i < 3; i++) {
>                 struct resource *res = &hose->mem_resources[i];
> +               resource_size_t offset = hose->mem_offset[i];
>
>                 /* we only care about memory windows */
>                 if (!(res->flags & IORESOURCE_MEM))
> @@ -1805,7 +1808,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
>                 /* Configure the resource */
>                 if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
>                                                res->start,
> -                                              res->start - hose->pci_mem_offset,
> +                                              res->start - offset,
>                                                resource_size(res),
>                                                res->flags,
>                                                j) == 0) {
> @@ -1814,7 +1817,7 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
>                         /* If the resource PCI address is 0 then we have our
>                          * ISA memory hole
>                          */
> -                       if (res->start == hose->pci_mem_offset)
> +                       if (res->start == offset)
>                                 found_isa_hole = 1;
>                 }
>         }
>
>

^ permalink raw reply

* Re: [v1][KVM][PATCH 1/1] kvm:ppc: enable doorbell exception with E500MC
From: Alexander Graf @ 2013-05-06 14:58 UTC (permalink / raw)
  To: Tiejun Chen; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <1367808836-28635-1-git-send-email-tiejun.chen@windriver.com>

On 05/06/2013 04:53 AM, Tiejun Chen wrote:
> Actually E500MC also support doorbell exception, and CONFIG_PPC_E500MC
> can cover BOOK3E/BOOK3E_64 as well.
>
> Signed-off-by: Tiejun Chen<tiejun.chen@windriver.com>
> ---
>   arch/powerpc/kvm/booke.c |    2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> index 1020119..dc1f590 100644
> --- a/arch/powerpc/kvm/booke.c
> +++ b/arch/powerpc/kvm/booke.c
> @@ -795,7 +795,7 @@ static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu,
>   		kvmppc_fill_pt_regs(&regs);
>   		timer_interrupt(&regs);
>   		break;
> -#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64)
> +#if defined(CONFIG_PPC_E500MC)

I suppose you mean CONFIG_KVM_E500MC here? Why didn't this work for you 
before? The ifdef above should cover the same range of CPUs.


Alex

^ permalink raw reply

* Re: [PATCH] powerpc, perf: Fix processing conditions for invalid BHRB entries
From: Anshuman Khandual @ 2013-05-06 12:25 UTC (permalink / raw)
  To: Michael Neuling; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <24261.1367838705@ale.ozlabs.ibm.com>

On 05/06/2013 04:41 PM, Michael Neuling wrote:
> Anshuman Khandual <khandual@linux.vnet.ibm.com> wrote:
> 
>> Fixing some conditions during BHRB entry processing.
> 
> I think we can simplify this a lot more... something like the below.
> 

I feel that the conditional handling of the invalid BHRB entries should be
present which would help us during the debug and also could be used for more
granular branch classification or error handling later on.

> Also, this marks the "to" address as all 1s, which is better poison
> value since it's possible to branch to/from 0x0.
>

Agreed.


> diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
> index c627843..d410d65 100644
> --- a/arch/powerpc/perf/core-book3s.c
> +++ b/arch/powerpc/perf/core-book3s.c
> @@ -1463,65 +1463,45 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
>  {
>  	u64 val;
>  	u64 addr;
> -	int r_index, u_index, target, pred;
> +	int r_index, u_index, pred;
> 
>  	r_index = 0;
>  	u_index = 0;
>  	while (r_index < ppmu->bhrb_nr) {
>  		/* Assembly read function */
> -		val = read_bhrb(r_index);
> +		val = read_bhrb(r_index++);
> 
>  		/* Terminal marker: End of valid BHRB entries */
> -		if (val == 0) {
> +		if (!val) {
>  			break;
>  		} else {
>  			/* BHRB field break up */
>  			addr = val & BHRB_EA;
>  			pred = val & BHRB_PREDICTION;
> -			target = val & BHRB_TARGET;
> 
> -			/* Probable Missed entry: Not applicable for POWER8 */
> -			if ((addr == 0) && (target == 0) && (pred == 1)) {
> -				r_index++;
> +			if (!addr)
> +				/* invalid entry */
>  				continue;
> -			}
> -
> -			/* Real Missed entry: Power8 based missed entry */
> -			if ((addr == 0) && (target == 1) && (pred == 1)) {
> -				r_index++;
> -				continue;
> -			}
> 
> -			/* Reserved condition: Not a valid entry  */
> -			if ((addr == 0) && (target == 1) && (pred == 0)) {
> -				r_index++;
> -				continue;
> -			}
> -
> -			/* Is a target address */
>  			if (val & BHRB_TARGET) {
>  				/* First address cannot be a target address */
> -				if (r_index == 0) {
> -					r_index++;
> +				if (r_index == 0)
>  					continue;
> -				}
> 
>  				/* Update target address for the previous entry */
>  				cpuhw->bhrb_entries[u_index - 1].to = addr;
>  				cpuhw->bhrb_entries[u_index - 1].mispred = pred;
>  				cpuhw->bhrb_entries[u_index - 1].predicted = ~pred;
> -
> -				/* Dont increment u_index */
> -				r_index++;
>  			} else {
>  				/* Update address, flags for current entry */
>  				cpuhw->bhrb_entries[u_index].from = addr;
> +				cpuhw->bhrb_entries[u_index].to =
> +					0xffffffffffffffff;
>  				cpuhw->bhrb_entries[u_index].mispred = pred;
>  				cpuhw->bhrb_entries[u_index].predicted = ~pred;
> 
>  				/* Successfully popullated one entry */
>  				u_index++;
> -				r_index++;
>  			}
>  		}
>  	}
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
> 

^ permalink raw reply

* Re: [PATCH] arch/powerpc: advertise ISA2.07, HTM, DSCR, EBB and ISEL bits in HWCAP2
From: Ryan Arnold @ 2013-05-06 14:38 UTC (permalink / raw)
  To: Nishanth Aravamudan
  Cc: Michael R Meissner, Steve Munroe, Michael Neuling, Peter Bergner,
	linuxppc-dev
In-Reply-To: <20130503234019.GE8561@linux.vnet.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 2443 bytes --]


Nishanth Aravamudan <nacc@linux.vnet.ibm.com> wrote on 05/03/2013 06:40:19
PM:

> Nishanth Aravamudan <nacc@linux.vnet.ibm.com>
> 05/03/2013 06:40 PM
>
> To
>
> Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> cc
>
> Michael Neuling <michael.neuling@au1.ibm.com>, Michael R Meissner/
> Cambridge/IBM@IBMUS, Steve Munroe/Rochester/IBM@IBMUS, Peter
> Bergner/Rochester/IBM@IBMUS, Ryan Arnold/Rochester/IBM@IBMUS,
> linuxppc-dev@lists.ozlabs.org
>
> Subject
>
> Re: [PATCH] arch/powerpc: advertise ISA2.07, HTM, DSCR, EBB and ISEL
> bits in HWCAP2
>
> On 04.05.2013 [09:23:51 +1000], Benjamin Herrenschmidt wrote:
> > On Fri, 2013-05-03 at 16:19 -0700, Nishanth Aravamudan wrote:
> > > +/* in AT_HWCAP2 */
> > > +#define PPC_FEATURE2_ARCH_2_07         0x80000000
> > > +#define PPC_FEATURE2_HTM               0x40000000
> > > +#define PPC_FEATURE2_DSCR              0x20000000
> > > +#define PPC_FEATURE2_EBB               0x10000000
> > > +#define PPC_FEATURE2_ISEL              0x08000000
> >
> > Should we "adjust" (ie filter out) some of these based
> > on CONFIG_ options (such as transactional memory enabled,
> > EBB supported by the hypervisor, etc...) ?
>
> Err, yeah, that seems reasonable :) However, it seems like glibc uses
> these values rather directly so it knows what bits to check for each
> feature. Therefore, it seems like it would be better to do the
> ifdeffery/checking in the user in cputable.c, but that seems like it
> could get quite complicated.
>
> Would it be ok (I guess I'm asking Ryan & co. here) to have an #ifdef in
> the definition that may or may not mean the bit is set in the aux
> vector, but the bit, if set, would always be the same bit?

My understanding was that these bits being 'on' is an indication of what
features the hardware supports (or what the kernel emulates) and a not an
indication of whether that facility is currently enabled or not.  If the
hardware supports a particular feature but it is not enabled I'd expect
that user-space usage of that feature would cause the kernel to trap on a
facility availability exception (which is how Altivec/VMX is implemented,
being defaulted to turned off).

Otherwise there's no way I could know whether an ISA [optional] feature is
actually available on a particular machine.

And yes, the bits can't change.  My usage of hwcap.h has to coincide with
the kernel's asm/cputable.h
Ryan

[-- Attachment #2: Type: text/html, Size: 3457 bytes --]

^ permalink raw reply

* Re: [PATCHv5 0/2] Speed Cap fixes for ppc64
From: Jerome Glisse @ 2013-05-06 14:33 UTC (permalink / raw)
  To: Alex Deucher
  Cc: dri-devel, Brian King, Jerome Glisse,
	Thadeu Lima de Souza Cascardo, Bjorn Helgaas, Alex Deucher,
	linuxppc-dev
In-Reply-To: <CADnq5_M9n0q4CucEhNqrJBjHsTh19ASsYr=nsUAJA_-mRr8qvQ@mail.gmail.com>

n Mon, May 6, 2013 at 10:32 AM, Alex Deucher <alexdeucher@gmail.com> wrote:
> On Fri, May 3, 2013 at 7:01 PM, Benjamin Herrenschmidt
> <benh@kernel.crashing.org> wrote:
>> On Fri, 2013-05-03 at 19:43 -0300, Kleber Sacilotto de Souza wrote:
>>
>>> This patch series does:
>>>   1. max_bus_speed is used to set the device to gen2 speeds
>>>   2. on power there's no longer a conflict between the pseries call and other
>>> architectures, because the overwrite is done via a ppc_md hook
>>>   3. radeon is using bus->max_bus_speed instead of drm_pcie_get_speed_cap_mask
>>> for gen2 capability detection
>>>
>>> The first patch consists of some architecture changes, such as adding a hook on
>>> powerpc for pci_root_bridge_prepare, so that pseries will initialize it to a
>>> function, while all other architectures get a NULL pointer. So that whenever
>>> pci_create_root_bus is called, we'll get max_bus_speed properly setup from
>>> OpenFirmware.
>>>
>>> The second patch consists of simple radeon changes not to call
>>> drm_get_pcie_speed_cap_mask anymore. I assume that on x86 machines,
>>> the max_bus_speed property will be properly set already.
>>
>> So I'm ok with the approach now and I might even put the powerpc patch
>> in for 3.10 since arguably we are fixing a nasty bug (uninitialized
>> max_bus_speed).
>>
>> David, what's your feeling about the radeon change ? It would be nice if
>> that could go in soon for various distro targets :-) On the other hand
>> I'm not going to be pushy if you are not comfortable with it.
>
> FWIW, the radeon change looks fine to me.
>
> Alex

As said previously, looks fine to me too.

Cheers,
Jerome

^ permalink raw reply

* Re: [PATCHv5 0/2] Speed Cap fixes for ppc64
From: Alex Deucher @ 2013-05-06 14:32 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dri-devel, Kleber Sacilotto de Souza, Bjorn Helgaas,
	Jerome Glisse, Thadeu Lima de Souza Cascardo, Brian King,
	Alex Deucher, linuxppc-dev
In-Reply-To: <1367622108.4389.129.camel@pasglop>

On Fri, May 3, 2013 at 7:01 PM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Fri, 2013-05-03 at 19:43 -0300, Kleber Sacilotto de Souza wrote:
>
>> This patch series does:
>>   1. max_bus_speed is used to set the device to gen2 speeds
>>   2. on power there's no longer a conflict between the pseries call and other
>> architectures, because the overwrite is done via a ppc_md hook
>>   3. radeon is using bus->max_bus_speed instead of drm_pcie_get_speed_cap_mask
>> for gen2 capability detection
>>
>> The first patch consists of some architecture changes, such as adding a hook on
>> powerpc for pci_root_bridge_prepare, so that pseries will initialize it to a
>> function, while all other architectures get a NULL pointer. So that whenever
>> pci_create_root_bus is called, we'll get max_bus_speed properly setup from
>> OpenFirmware.
>>
>> The second patch consists of simple radeon changes not to call
>> drm_get_pcie_speed_cap_mask anymore. I assume that on x86 machines,
>> the max_bus_speed property will be properly set already.
>
> So I'm ok with the approach now and I might even put the powerpc patch
> in for 3.10 since arguably we are fixing a nasty bug (uninitialized
> max_bus_speed).
>
> David, what's your feeling about the radeon change ? It would be nice if
> that could go in soon for various distro targets :-) On the other hand
> I'm not going to be pushy if you are not comfortable with it.

FWIW, the radeon change looks fine to me.

Alex

^ permalink raw reply

* [PATCH 2/3] powerpc/powernv: Disable IO space for PCI buses
From: Gavin Shan @ 2013-05-06 13:44 UTC (permalink / raw)
  To: linuxppc-dev, linux-pci; +Cc: bhelgaas, yinghai, Gavin Shan
In-Reply-To: <1367847858-6506-1-git-send-email-shangw@linux.vnet.ibm.com>

The patch intends to set the special flag (PCI_BUS_FLAGS_NO_IO) for
root buses so PCI core will skip assignment for IO stuff. Besides,
we also clear the IO resources on all PCI devices for PHB3.

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/machdep.h        |    3 ++
 arch/powerpc/kernel/pci-common.c          |    7 +++++
 arch/powerpc/platforms/powernv/pci-ioda.c |   38 +++++++++++++++++++++++++++++
 3 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 3f3f691..ebc2ffd 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -220,6 +220,9 @@ struct machdep_calls {
 	/* Called during PCI resource reassignment */
 	resource_size_t (*pcibios_window_alignment)(struct pci_bus *, unsigned long type);
 
+	/* Called when adding PCI bus */
+	void (*pcibios_add_bus)(struct pci_bus *);
+
 	/* Called to shutdown machine specific hardware not already controlled
 	 * by other drivers.
 	 */
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index f325dc9..7b8a6f1 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -120,6 +120,13 @@ resource_size_t pcibios_window_alignment(struct pci_bus *bus,
 	return 1;
 }
 
+/* The function will be called while adding PCI bus to system */
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	if (ppc_md.pcibios_add_bus)
+		ppc_md.pcibios_add_bus(bus);
+}
+
 static resource_size_t pcibios_io_size(const struct pci_controller *hose)
 {
 #ifdef CONFIG_PPC64
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 8c6c9cf..0c3fa29 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -1015,6 +1015,40 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,
 	return phb->ioda.io_segsize;
 }
 
+/*
+ * The function will be called while adding PCI bus to the
+ * system. In turn, we should set flag to indicate that the
+ * root bus doesn't have IO resources.
+ */
+static void pnv_pci_add_bus(struct pci_bus *bus)
+{
+	struct pci_controller *hose = pci_bus_to_host(bus);
+	struct pnv_phb *phb = hose->private_data;
+
+	/*
+	 * We only need set the flag for root bus since the
+	 * bus flags are copied over from parent to children
+	 */
+	if (pci_is_root_bus(bus) &&
+	    phb->model == PNV_PHB_MODEL_PHB3)
+		bus->bus_flags |= PCI_BUS_FLAGS_NO_IO;
+}
+
+static void pnv_pci_fixup_resources(struct pci_dev *dev)
+{
+	int i;
+	struct resource *res;
+
+	if (!(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_IO))
+		return;
+
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+		res = dev->resource + i;
+		if (res->flags & IORESOURCE_IO)
+			res->flags = 0;
+	}
+}
+
 /* Prevent enabling devices for which we couldn't properly
  * assign a PE
  */
@@ -1189,6 +1223,10 @@ void __init pnv_pci_init_ioda_phb(struct device_node *np, int ioda_type)
 	ppc_md.pcibios_fixup = pnv_pci_ioda_fixup;
 	ppc_md.pcibios_enable_device_hook = pnv_pci_enable_device_hook;
 	ppc_md.pcibios_window_alignment = pnv_pci_window_alignment;
+	if (ioda_type == PNV_PHB_IODA2) {
+		ppc_md.pcibios_add_bus = pnv_pci_add_bus;
+		ppc_md.pcibios_fixup_resources = pnv_pci_fixup_resources;
+	}
 	pci_add_flags(PCI_REASSIGN_ALL_RSRC);
 
 	/* Reset IODA tables to a clean state */
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 3/3] powerpc/powernv: Don't configure IO window on PHB3
From: Gavin Shan @ 2013-05-06 13:44 UTC (permalink / raw)
  To: linuxppc-dev, linux-pci; +Cc: bhelgaas, yinghai, Gavin Shan
In-Reply-To: <1367847858-6506-1-git-send-email-shangw@linux.vnet.ibm.com>

We needn't configure IO windows for the corresponding PEs on PHB3
since that doesn't support IO.

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/pci-ioda.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 0c3fa29..b4f3edb 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -894,7 +894,9 @@ static void pnv_ioda_setup_pe_seg(struct pci_controller *hose,
 		    res->start > res->end)
 			continue;
 
-		if (res->flags & IORESOURCE_IO) {
+		/* We needn't setup IO windows for PHB3 */
+		if (!(pe->pbus->bus_flags & PCI_BUS_FLAGS_NO_IO) &&
+		    res->flags & IORESOURCE_IO) {
 			region.start = res->start - phb->ioda.io_pci_base;
 			region.end   = res->end - phb->ioda.io_pci_base;
 			index = region.start / phb->ioda.io_segsize;
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH 1/3] PCI: PCI_BUS_FLAGS_NO_IO flag for PCI bus
From: Gavin Shan @ 2013-05-06 13:44 UTC (permalink / raw)
  To: linuxppc-dev, linux-pci; +Cc: bhelgaas, yinghai, Gavin Shan

There has some PCI hosts (e.g. PHB3) that don't support IO. So the
patch intends to introduce the flag to indicate the special case.
In turn, we won't do IO resource assignment and enable that.

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
 drivers/pci/pci.c       |    3 +++
 drivers/pci/setup-bus.c |   25 ++++++++++++++++---------
 include/linux/pci.h     |    1 +
 3 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a899d8b..2548bad 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1218,6 +1218,9 @@ int pci_enable_device_mem(struct pci_dev *dev)
  */
 int pci_enable_device(struct pci_dev *dev)
 {
+	if (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_IO)
+		return pci_enable_device_flags(dev, IORESOURCE_MEM);
+
 	return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
 }
 
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 16abaaa..c8293e90 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -133,6 +133,10 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
 
 		r = &dev->resource[i];
 
+		if ((dev->bus->bus_flags & PCI_BUS_FLAGS_NO_IO) &&
+		    (r->flags & IORESOURCE_IO))
+			continue;
+
 		if (r->flags & IORESOURCE_PCI_FIXED)
 			continue;
 
@@ -1083,17 +1087,20 @@ static void __ref __pci_bus_size_bridges(struct pci_bus *bus,
 			additional_io_size  = pci_hotplug_io_size;
 			additional_mem_size = pci_hotplug_mem_size;
 		}
+
+		/* Follow thru */
+	default:
+		if (!(bus->bus_flags & PCI_BUS_FLAGS_NO_IO))
+			pbus_size_io(bus,
+				     realloc_head ? 0 : additional_io_size,
+				     additional_io_size, realloc_head);
 		/*
-		 * Follow thru
+		 * If the bridge supports prefetchable range, size it
+		 * separately. If it doesn't, or its prefetchable window
+		 * has already been allocated by arch code, try
+		 * non-prefetchable range for both types of PCI memory
+		 * resources.
 		 */
-	default:
-		pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
-			     additional_io_size, realloc_head);
-		/* If the bridge supports prefetchable range, size it
-		   separately. If it doesn't, or its prefetchable window
-		   has already been allocated by arch code, try
-		   non-prefetchable range for both types of PCI memory
-		   resources. */
 		mask = IORESOURCE_MEM;
 		prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
 		if (pbus_size_mem(bus, prefmask, prefmask,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 3a24e4f..53b595a 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -181,6 +181,7 @@ typedef unsigned short __bitwise pci_bus_flags_t;
 enum pci_bus_flags {
 	PCI_BUS_FLAGS_NO_MSI   = (__force pci_bus_flags_t) 1,
 	PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2,
+	PCI_BUS_FLAGS_NO_IO    = (__force pci_bus_flags_t) 4
 };
 
 /* Based on the PCI Hotplug Spec, but some values are made up by us */
-- 
1.7.5.4

^ permalink raw reply related

* Re: [PATCH] powerpc, perf: Clear out branch entries to avoid any previous stale values
From: Michael Ellerman @ 2013-05-06 12:53 UTC (permalink / raw)
  To: Anshuman Khandual; +Cc: linuxppc-dev, mikey, linux-kernel
In-Reply-To: <1367827480-5375-1-git-send-email-khandual@linux.vnet.ibm.com>

On Mon, 2013-05-06 at 13:34 +0530, Anshuman Khandual wrote:
> The 'to' field inside branch entries might contain stale values from previous
> PMU interrupt instances which had indirect branches. So clear all the values
> before reading a fresh set of BHRB entries after a PMU interrupt.
> 
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> ---
>  arch/powerpc/perf/core-book3s.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
> index c627843..09db68d 100644
> --- a/arch/powerpc/perf/core-book3s.c
> +++ b/arch/powerpc/perf/core-book3s.c
> @@ -1590,6 +1590,8 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
>  		if (event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
>  			struct cpu_hw_events *cpuhw;
>  			cpuhw = &__get_cpu_var(cpu_hw_events);
> +			memset(cpuhw->bhrb_entries, 0,
> +				sizeof(struct perf_branch_entry) * BHRB_MAX_ENTRIES);
>  			power_pmu_bhrb_read(cpuhw);
>  			data.br_stack = &cpuhw->bhrb_stack;
>  		}

Wouldn't it be just as effective, and less overhead, to set .to = 0; in
the else branch in power_pmu_bhrb_read() ?

eg:

diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index c627843..30af11a4 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1516,6 +1516,7 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
                        } else {
                                /* Update address, flags for current entry */
                                cpuhw->bhrb_entries[u_index].from = addr;
+                               cpuhw->bhrb_entries[u_index].to = 0;
                                cpuhw->bhrb_entries[u_index].mispred = pred;
                                cpuhw->bhrb_entries[u_index].predicted = ~pred;
 

cheers

^ permalink raw reply related

* [PATCH] powerpc: Fix single step emulation of 32bit overflowed branches
From: Michael Neuling @ 2013-05-06 11:32 UTC (permalink / raw)
  To: benh; +Cc: Linux PPC dev, Paul Mackerras

Check truncate_if_32bit() on final write to nip.

Signed-off-by: Michael Neuling <mikey@neuling.org>

diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index e15c521..99c7fc1 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -580,7 +580,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
 		if (instr & 1)
 			regs->link = regs->nip;
 		if (branch_taken(instr, regs))
-			regs->nip = imm;
+			regs->nip = truncate_if_32bit(regs->msr, imm);
 		return 1;
 #ifdef CONFIG_PPC64
 	case 17:	/* sc */

^ permalink raw reply related

* Re: [PATCH] powerpc, perf: Fix processing conditions for invalid BHRB entries
From: Michael Neuling @ 2013-05-06 11:11 UTC (permalink / raw)
  To: Anshuman Khandual; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1367831206-16331-1-git-send-email-khandual@linux.vnet.ibm.com>

Anshuman Khandual <khandual@linux.vnet.ibm.com> wrote:

> Fixing some conditions during BHRB entry processing.

I think we can simplify this a lot more... something like the below.

Also, this marks the "to" address as all 1s, which is better poison
value since it's possible to branch to/from 0x0.

diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index c627843..d410d65 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1463,65 +1463,45 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
 {
 	u64 val;
 	u64 addr;
-	int r_index, u_index, target, pred;
+	int r_index, u_index, pred;
 
 	r_index = 0;
 	u_index = 0;
 	while (r_index < ppmu->bhrb_nr) {
 		/* Assembly read function */
-		val = read_bhrb(r_index);
+		val = read_bhrb(r_index++);
 
 		/* Terminal marker: End of valid BHRB entries */
-		if (val == 0) {
+		if (!val) {
 			break;
 		} else {
 			/* BHRB field break up */
 			addr = val & BHRB_EA;
 			pred = val & BHRB_PREDICTION;
-			target = val & BHRB_TARGET;
 
-			/* Probable Missed entry: Not applicable for POWER8 */
-			if ((addr == 0) && (target == 0) && (pred == 1)) {
-				r_index++;
+			if (!addr)
+				/* invalid entry */
 				continue;
-			}
-
-			/* Real Missed entry: Power8 based missed entry */
-			if ((addr == 0) && (target == 1) && (pred == 1)) {
-				r_index++;
-				continue;
-			}
 
-			/* Reserved condition: Not a valid entry  */
-			if ((addr == 0) && (target == 1) && (pred == 0)) {
-				r_index++;
-				continue;
-			}
-
-			/* Is a target address */
 			if (val & BHRB_TARGET) {
 				/* First address cannot be a target address */
-				if (r_index == 0) {
-					r_index++;
+				if (r_index == 0)
 					continue;
-				}
 
 				/* Update target address for the previous entry */
 				cpuhw->bhrb_entries[u_index - 1].to = addr;
 				cpuhw->bhrb_entries[u_index - 1].mispred = pred;
 				cpuhw->bhrb_entries[u_index - 1].predicted = ~pred;
-
-				/* Dont increment u_index */
-				r_index++;
 			} else {
 				/* Update address, flags for current entry */
 				cpuhw->bhrb_entries[u_index].from = addr;
+				cpuhw->bhrb_entries[u_index].to =
+					0xffffffffffffffff;
 				cpuhw->bhrb_entries[u_index].mispred = pred;
 				cpuhw->bhrb_entries[u_index].predicted = ~pred;
 
 				/* Successfully popullated one entry */
 				u_index++;
-				r_index++;
 			}
 		}
 	}

^ permalink raw reply related

* Re: [PATCH] irqdomain: Allow quiet failure mode
From: Grant Likely @ 2013-05-06 11:07 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1367822494.15842.12.camel@pasglop>

On Mon, May 6, 2013 at 7:41 AM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> Some interrupt controllers refuse to map interrupts marked as
> "protected" by firwmare. Since we try to map everyting in the
> device-tree on some platforms, we end up with a lot of nasty
> WARN's in the boot log for what is a normal situation on those
> machines.
>
> This defines a specific return code (-EPERM) from the host map()
> callback which cause irqdomain to fail silently.
>
> MPIC is updated to return this when hitting a protected source
> printing only a single line message for diagnostic purposes.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
>
> Grant, I'm happy to merge that myself provided I have your ack,
> it fixes an annoying problem on Cell introduced by previous changes
> to irqdomain (not recent but users started to notice).

Yes, please merge it since powerpc needs it.

Acked-by: Grant Likely <grant.likely@linaro.org>

g.

^ permalink raw reply

* [PATCH] powerpc, perf: Clear out branch entries to avoid any previous stale values
From: Anshuman Khandual @ 2013-05-06  6:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: mikey

The 'to' field inside branch entries might contain stale values from previous
PMU interrupt instances which had indirect branches. So clear all the values
before reading a fresh set of BHRB entries after a PMU interrupt.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 arch/powerpc/perf/core-book3s.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index c627843..09db68d 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1590,6 +1590,8 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
 		if (event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
 			struct cpu_hw_events *cpuhw;
 			cpuhw = &__get_cpu_var(cpu_hw_events);
+			memset(cpuhw->bhrb_entries, 0,
+				sizeof(struct perf_branch_entry) * BHRB_MAX_ENTRIES);
 			power_pmu_bhrb_read(cpuhw);
 			data.br_stack = &cpuhw->bhrb_stack;
 		}
-- 
1.7.11.7

^ permalink raw reply related

* [PATCH] powerpc, perf: Clear out branch entries to avoid any previous stale values
From: Anshuman Khandual @ 2013-05-06  8:04 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: mikey

The 'to' field inside branch entries might contain stale values from previous
PMU interrupt instances which had indirect branches. So clear all the values
before reading a fresh set of BHRB entries after a PMU interrupt.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 arch/powerpc/perf/core-book3s.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index c627843..09db68d 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1590,6 +1590,8 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
 		if (event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
 			struct cpu_hw_events *cpuhw;
 			cpuhw = &__get_cpu_var(cpu_hw_events);
+			memset(cpuhw->bhrb_entries, 0,
+				sizeof(struct perf_branch_entry) * BHRB_MAX_ENTRIES);
 			power_pmu_bhrb_read(cpuhw);
 			data.br_stack = &cpuhw->bhrb_stack;
 		}
-- 
1.7.11.7

^ permalink raw reply related

* [PATCH] powerpc, perf: Fix processing conditions for invalid BHRB entries
From: Anshuman Khandual @ 2013-05-06  9:06 UTC (permalink / raw)
  To: linuxppc-dev, linux-kernel; +Cc: mikey, khandual

Fixing some conditions during BHRB entry processing.

Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
---
 arch/powerpc/perf/core-book3s.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 09db68d..1de2756 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1481,25 +1481,25 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw)
 			target = val & BHRB_TARGET;
 
 			/* Probable Missed entry: Not applicable for POWER8 */
-			if ((addr == 0) && (target == 0) && (pred == 1)) {
+			if ((addr == 0) && (!target) && pred) {
 				r_index++;
 				continue;
 			}
 
 			/* Real Missed entry: Power8 based missed entry */
-			if ((addr == 0) && (target == 1) && (pred == 1)) {
+			if ((addr == 0) && target && pred) {
 				r_index++;
 				continue;
 			}
 
 			/* Reserved condition: Not a valid entry  */
-			if ((addr == 0) && (target == 1) && (pred == 0)) {
+			if ((addr == 0) && target && (!pred)) {
 				r_index++;
 				continue;
 			}
 
 			/* Is a target address */
-			if (val & BHRB_TARGET) {
+			if (target) {
 				/* First address cannot be a target address */
 				if (r_index == 0) {
 					r_index++;
-- 
1.7.11.7

^ permalink raw reply related

* Re: [PATCH 8/8] powerpc/topology: Fix spurr attribute permission
From: Benjamin Herrenschmidt @ 2013-05-06  8:02 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <1367823055.15842.23.camel@pasglop>

On Mon, 2013-05-06 at 16:50 +1000, Benjamin Herrenschmidt wrote:
> We are registering the attribute with permission 0600 but it
> doesn't have a store callback, which causes WARN_ON's during
> boot. Fix the permission.

Ignore the 8/8 in the subject, that's just me not cleaning
things up properly :-)

Cheers,
Ben.

> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/powerpc/kernel/sysfs.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
> index 3ce1f86..e68a845 100644
> --- a/arch/powerpc/kernel/sysfs.c
> +++ b/arch/powerpc/kernel/sysfs.c
> @@ -180,7 +180,7 @@ SYSFS_PMCSETUP(dscr, SPRN_DSCR);
>  SYSFS_PMCSETUP(pir, SPRN_PIR);
>  
>  static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra);
> -static DEVICE_ATTR(spurr, 0600, show_spurr, NULL);
> +static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
>  static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr);
>  static DEVICE_ATTR(purr, 0600, show_purr, store_purr);
>  static DEVICE_ATTR(pir, 0400, show_pir, NULL);
> 
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply

* [PATCH 6/6] KVM: PPC: Add hugepage support for IOMMU in-kernel handling
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

This adds special support for huge pages (16MB).  The reference
counting cannot be easily done for such pages in real mode (when
MMU is off) so we added a list of huge pages.  It is populated in
virtual mode and get_page is called just once per a huge page.
Real mode handlers check if the requested page is huge and in the list,
then no reference counting is done, otherwise an exit to virtual mode
happens.  The list is released at KVM exit.  At the moment the fastest
card available for tests uses up to 9 huge pages so walking through this
list is not very expensive.  However this can change and we may want
to optimize this.

Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 arch/powerpc/include/asm/kvm_host.h |    2 +
 arch/powerpc/include/asm/kvm_ppc.h  |   24 +++++++++++
 arch/powerpc/kvm/book3s_64_vio.c    |   79 ++++++++++++++++++++++++++++++++++-
 arch/powerpc/kvm/book3s_64_vio_hv.c |   47 ++++++++++++++++++++-
 4 files changed, 149 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 2b70cbc..b6a047e 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -180,6 +180,8 @@ struct kvmppc_spapr_tce_table {
 	u32 window_size;
 	bool virtmode_only;
 	struct iommu_group *grp;    /* used for IOMMU groups */
+	struct list_head hugepages; /* used for IOMMU groups */
+	spinlock_t hugepages_lock;  /* used for IOMMU groups */
 	struct page *pages[0];
 };
 
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index bdfa140..3c95464 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -154,6 +154,30 @@ extern long kvmppc_virtmode_h_put_tce_indirect(struct kvm_vcpu *vcpu,
 extern long kvmppc_virtmode_h_stuff_tce(struct kvm_vcpu *vcpu,
 		unsigned long liobn, unsigned long ioba,
 		unsigned long tce_value, unsigned long npages);
+
+/*
+ * The KVM guest can be backed with 16MB pages (qemu switch
+ * -mem-path /var/lib/hugetlbfs/global/pagesize-16MB/).
+ * In this case, we cannot do page counting from the real mode
+ * as the compound pages are used - they are linked in a list
+ * with pointers as virtual addresses which are inaccessible
+ * in real mode.
+ *
+ * The code below keeps a 16MB pages list and uses page struct
+ * in real mode if it is already locked in RAM and inserted into
+ * the list or switches to the virtual mode where it can be
+ * handled in a usual manner.
+ */
+struct iommu_kvmppc_hugepage {
+	struct list_head list;
+	pte_t pte;		/* Huge page PTE */
+	unsigned long pa;	/* Base phys address used as a real TCE */
+	struct page *page;	/* page struct of the very first subpage */
+	unsigned long size;	/* Huge page size (always 16MB at the moment) */
+};
+extern struct iommu_kvmppc_hugepage *kvmppc_iommu_hugepage_find(
+		struct kvmppc_spapr_tce_table *tt, pte_t pte);
+
 extern long kvm_vm_ioctl_allocate_rma(struct kvm *kvm,
 				struct kvm_allocate_rma *rma);
 extern struct kvmppc_linear_info *kvm_alloc_rma(void);
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index 98cf949..274458d 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -54,6 +54,59 @@ static bool kvmppc_tce_virt_only = false;
 module_param_named(virt_only, kvmppc_tce_virt_only, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(virt_only, "Disable realmode handling of IOMMU map/unmap");
 
+#ifdef CONFIG_IOMMU_API
+/*
+ * Adds a new huge page descriptor to the list.
+ */
+static struct iommu_kvmppc_hugepage *kvmppc_iommu_hugepage_add(
+		struct kvmppc_spapr_tce_table *tt,
+		pte_t pte, unsigned long va, unsigned long pg_size)
+{
+	int ret;
+	struct iommu_kvmppc_hugepage *hp;
+	struct page *p;
+
+	va = va & ~(pg_size - 1);
+	ret = get_user_pages_fast(va, 1, true/*write*/, &p);
+	if ((ret != 1) || !p)
+		return NULL;
+
+	hp = kzalloc(sizeof(*hp), GFP_KERNEL);
+	if (!hp)
+		return NULL;
+
+	hp->page = p;
+	hp->pte = pte;
+	hp->pa = __pa((unsigned long) page_address(hp->page));
+	hp->size = pg_size;
+
+	spin_lock(&tt->hugepages_lock);
+	list_add(&hp->list, &tt->hugepages);
+	spin_unlock(&tt->hugepages_lock);
+
+	return hp;
+}
+
+static void kvmppc_iommu_hugepages_init(struct kvmppc_spapr_tce_table *tt)
+{
+	INIT_LIST_HEAD(&tt->hugepages);
+	spin_lock_init(&tt->hugepages_lock);
+}
+
+static void kvmppc_iommu_hugepages_cleanup(struct kvmppc_spapr_tce_table *tt)
+{
+	struct iommu_kvmppc_hugepage *hp, *tmp;
+
+	spin_lock(&tt->hugepages_lock);
+	list_for_each_entry_safe(hp, tmp, &tt->hugepages, list) {
+		list_del(&hp->list);
+		put_page(hp->page);
+		kfree(hp);
+	}
+	spin_unlock(&tt->hugepages_lock);
+}
+#endif /* CONFIG_IOMMU_API */
+
 /*
  * TCE tables handlers.
  */
@@ -73,6 +126,7 @@ static void release_spapr_tce_table(struct kvmppc_spapr_tce_table *stt)
 #ifdef CONFIG_IOMMU_API
 	if (stt->grp) {
 		iommu_group_put(stt->grp);
+		kvmppc_iommu_hugepages_cleanup(stt);
 	} else
 #endif
 		for (i = 0; i < kvmppc_stt_npages(stt->window_size); i++)
@@ -211,6 +265,7 @@ long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
 	kvm_get_kvm(kvm);
 
 	mutex_lock(&kvm->lock);
+	kvmppc_iommu_hugepages_init(tt);
 	list_add(&tt->list, &kvm->arch.spapr_tce_tables);
 
 	mutex_unlock(&kvm->lock);
@@ -259,6 +314,8 @@ static int put_tce_virt_mode(struct kvmppc_spapr_tce_table *tt,
 {
 	int ret;
 	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
+	struct iommu_kvmppc_hugepage *hp;
+	enum dma_data_direction direction = iommu_tce_direction(tce);
 
 	ret = iommu_tce_put_param_check(tbl, ioba, tce);
 	if (ret)
@@ -268,7 +325,27 @@ static int put_tce_virt_mode(struct kvmppc_spapr_tce_table *tt,
 	if (pg_size == PAGE_SIZE)
 		return iommu_put_tce_user_mode(tbl, entry, tce);
 
-	return -EAGAIN;
+	/*
+	 * Hugepages case - manage the hugepage list.
+	 * kvmppc_iommu_hugepage_find() may find a huge page if called
+	 * from h_put_tce_indirect call.
+	 */
+	hp = kvmppc_iommu_hugepage_find(tt, pte);
+	if (!hp) {
+		/* This is the first time usage of this huge page */
+		hp = kvmppc_iommu_hugepage_add(tt, pte, tce, pg_size);
+		if (!hp)
+			return -EFAULT;
+	}
+
+	tce = (unsigned long) __va(hp->pa) + (tce & (pg_size - 1));
+
+	ret = iommu_tce_build(tbl, entry, tce, direction);
+	if (ret < 0)
+		pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%d\n",
+				__func__, ioba, tce, ret);
+
+	return ret;
 }
 
 static pte_t va_to_linux_pte(struct kvm_vcpu *vcpu,
diff --git a/arch/powerpc/kvm/book3s_64_vio_hv.c b/arch/powerpc/kvm/book3s_64_vio_hv.c
index c5e5905..a91ff7b 100644
--- a/arch/powerpc/kvm/book3s_64_vio_hv.c
+++ b/arch/powerpc/kvm/book3s_64_vio_hv.c
@@ -43,6 +43,29 @@
 #define TCES_PER_PAGE	(PAGE_SIZE / sizeof(u64))
 #define ERROR_ADDR      (~(unsigned long)0x0)
 
+#ifdef CONFIG_IOMMU_API
+/*
+ * Huge pages trick helper.
+ */
+struct iommu_kvmppc_hugepage *kvmppc_iommu_hugepage_find(
+		struct kvmppc_spapr_tce_table *tt, pte_t pte)
+{
+	struct iommu_kvmppc_hugepage *hp, *ret = NULL;
+
+	spin_lock(&tt->hugepages_lock);
+	list_for_each_entry(hp, &tt->hugepages, list) {
+		if (hp->pte == pte) {
+			ret = hp;
+			break;
+		}
+	}
+	spin_unlock(&tt->hugepages_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(kvmppc_iommu_hugepage_find);
+#endif /* CONFIG_IOMMU_API */
+
 /*
  * Finds a TCE table descriptor by LIOBN.
  */
@@ -191,6 +214,15 @@ static int clear_tce_real_mode(struct iommu_table *tbl,
 		if (oldtce & TCE_PCI_WRITE)
 			SetPageDirty(page);
 
+		/*
+		 * As get_page is called only once on a HUGE page,
+		 * and it is done in virtual mode,
+		 * we do not release it here, instead we postpone it
+		 * till the KVM exit.
+		 */
+		if (PageCompound(page))
+			continue;
+
 		ret = realmode_put_page(page);
 		if (ret)
 			break;
@@ -210,14 +242,25 @@ static int put_tce_real_mode(struct kvmppc_spapr_tce_table *tt,
 	int ret;
 	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
 	struct page *page = NULL;
+	struct iommu_kvmppc_hugepage *hp = NULL;
 	enum dma_data_direction direction = iommu_tce_direction(tce);
 
 	ret = iommu_tce_put_param_check(tbl, ioba, tce);
 	if (ret)
 		return ret;
 
-	if (pg_size != PAGE_SIZE)
-		return -EAGAIN;
+	/* This is a huge page. we continue only if it is already in the list */
+	if (pg_size != PAGE_SIZE) {
+		hp = kvmppc_iommu_hugepage_find(tt, pte);
+
+		/* Go to virtual mode to add a hugepage to the list if not found */
+		if (!hp)
+			return -EAGAIN;
+
+		/* tce_build receives a kernel virtual addresses */
+		return iommu_tce_build(tbl, entry, (unsigned long) __va(tce),
+				direction);
+	}
 
 	/* Small page case, find page struct to increment a counter */
 	page = realmode_pfn_to_page(tce >> PAGE_SHIFT);
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 5/6] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

This allows the host kernel to handle H_PUT_TCE, H_PUT_TCE_INDIRECT
and H_STUFF_TCE requests without passing them to QEMU, which should
save time on switching to QEMU and back.

Both real and virtual modes are supported - whenever the kernel
fails to handle TCE request, it passes it to the virtual mode.
If it the virtual mode handlers fail, then the request is passed
to the user mode, for example, to QEMU.

This adds a new KVM_CAP_SPAPR_TCE_IOMMU ioctl to asssociate
a virtual PCI bus ID (LIOBN) with an IOMMU group, which enables
in-kernel handling of IOMMU map/unmap.

This adds a special case for huge pages (16MB).  The reference
counting cannot be easily done for such pages in real mode (when
MMU is off) so we added a list of huge pages.  It is populated in
virtual mode and get_page is called just once per a huge page.
Real mode handlers check if the requested page is huge and in the list,
then no reference counting is done, otherwise an exit to virtual mode
happens.  The list is released at KVM exit.  At the moment the fastest
card available for tests uses up to 9 huge pages so walking through this
list is not very expensive.  However this can change and we may want
to optimize this.

This also adds the virt_only parameter to the KVM module
for debug and performance check purposes.

Tests show that this patch increases transmission speed from 220MB/s
to 750..1020MB/s on 10Gb network (Chelsea CXGB3 10Gb ethernet card).

Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 Documentation/virtual/kvm/api.txt   |   28 ++++
 arch/powerpc/include/asm/kvm_host.h |    2 +
 arch/powerpc/include/asm/kvm_ppc.h  |    2 +
 arch/powerpc/include/uapi/asm/kvm.h |    7 +
 arch/powerpc/kvm/book3s_64_vio.c    |  242 ++++++++++++++++++++++++++++++++++-
 arch/powerpc/kvm/book3s_64_vio_hv.c |  192 +++++++++++++++++++++++++++
 arch/powerpc/kvm/powerpc.c          |   12 ++
 include/uapi/linux/kvm.h            |    2 +
 8 files changed, 485 insertions(+), 2 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index f621cd6..2039767 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2127,6 +2127,34 @@ written, then `n_invalid' invalid entries, invalidating any previously
 valid entries found.
 
 
+4.79 KVM_CREATE_SPAPR_TCE_IOMMU
+
+Capability: KVM_CAP_SPAPR_TCE_IOMMU
+Architectures: powerpc
+Type: vm ioctl
+Parameters: struct kvm_create_spapr_tce_iommu (in)
+Returns: 0 on success, -1 on error
+
+This creates a link between IOMMU group and a hardware TCE (translation
+control entry) table. This link lets the host kernel know what IOMMU
+group (i.e. TCE table) to use for the LIOBN number passed with
+H_PUT_TCE, H_PUT_TCE_INDIRECT, H_STUFF_TCE hypercalls.
+
+/* for KVM_CAP_SPAPR_TCE_IOMMU */
+struct kvm_create_spapr_tce_iommu {
+	__u64 liobn;
+	__u32 iommu_id;
+	__u32 flags;
+};
+
+No flag is supported at the moment.
+
+When the guest issues TCE call on a liobn for which a TCE table has been
+registered, the kernel will handle it in real mode, updating the hardware
+TCE table. TCE table calls for other liobns will cause a vm exit and must
+be handled by userspace.
+
+
 5. The kvm_run structure
 ------------------------
 
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 36ceb0d..2b70cbc 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -178,6 +178,8 @@ struct kvmppc_spapr_tce_table {
 	struct kvm *kvm;
 	u64 liobn;
 	u32 window_size;
+	bool virtmode_only;
+	struct iommu_group *grp;    /* used for IOMMU groups */
 	struct page *pages[0];
 };
 
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index d501246..bdfa140 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -139,6 +139,8 @@ extern void kvmppc_xics_free(struct kvm *kvm);
 
 extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 				struct kvm_create_spapr_tce *args);
+extern long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
+				struct kvm_create_spapr_tce_iommu *args);
 extern struct kvmppc_spapr_tce_table *kvmppc_find_tce_table(
 		struct kvm_vcpu *vcpu, unsigned long liobn);
 extern long kvmppc_emulated_h_put_tce(struct kvmppc_spapr_tce_table *stt,
diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h
index 681b314..b67d44b 100644
--- a/arch/powerpc/include/uapi/asm/kvm.h
+++ b/arch/powerpc/include/uapi/asm/kvm.h
@@ -291,6 +291,13 @@ struct kvm_create_spapr_tce {
 	__u32 window_size;
 };
 
+/* for KVM_CAP_SPAPR_TCE_IOMMU */
+struct kvm_create_spapr_tce_iommu {
+	__u64 liobn;
+	__u32 iommu_id;
+	__u32 flags;
+};
+
 /* for KVM_ALLOCATE_RMA */
 struct kvm_allocate_rma {
 	__u64 rma_size;
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index 643ac1e..98cf949 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -27,6 +27,9 @@
 #include <linux/hugetlb.h>
 #include <linux/list.h>
 #include <linux/anon_inodes.h>
+#include <linux/pci.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
 
 #include <asm/tlbflush.h>
 #include <asm/kvm_ppc.h>
@@ -38,10 +41,19 @@
 #include <asm/kvm_host.h>
 #include <asm/udbg.h>
 #include <asm/iommu.h>
+#include <asm/tce.h>
+
+#define DRIVER_VERSION	"0.1"
+#define DRIVER_AUTHOR	"Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>"
+#define DRIVER_DESC	"POWERPC KVM driver"
 
 #define TCES_PER_PAGE	(PAGE_SIZE / sizeof(u64))
 #define ERROR_ADDR      (~(unsigned long)0x0)
 
+static bool kvmppc_tce_virt_only = false;
+module_param_named(virt_only, kvmppc_tce_virt_only, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(virt_only, "Disable realmode handling of IOMMU map/unmap");
+
 /*
  * TCE tables handlers.
  */
@@ -58,8 +70,13 @@ static void release_spapr_tce_table(struct kvmppc_spapr_tce_table *stt)
 
 	mutex_lock(&kvm->lock);
 	list_del(&stt->list);
-	for (i = 0; i < kvmppc_stt_npages(stt->window_size); i++)
-		__free_page(stt->pages[i]);
+#ifdef CONFIG_IOMMU_API
+	if (stt->grp) {
+		iommu_group_put(stt->grp);
+	} else
+#endif
+		for (i = 0; i < kvmppc_stt_npages(stt->window_size); i++)
+			__free_page(stt->pages[i]);
 	kfree(stt);
 	mutex_unlock(&kvm->lock);
 
@@ -155,9 +172,127 @@ fail:
 	return ret;
 }
 
+#ifdef CONFIG_IOMMU_API
+static const struct file_operations kvm_spapr_tce_iommu_fops = {
+	.release	= kvm_spapr_tce_release,
+};
+
+long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
+		struct kvm_create_spapr_tce_iommu *args)
+{
+	struct kvmppc_spapr_tce_table *tt = NULL;
+	struct iommu_group *grp;
+	struct iommu_table *tbl;
+
+	/* Find an IOMMU table for the given ID */
+	grp = iommu_group_get_by_id(args->iommu_id);
+	if (!grp)
+		return -ENXIO;
+
+	tbl = iommu_group_get_iommudata(grp);
+	if (!tbl)
+		return -ENXIO;
+
+	/* Check this LIOBN hasn't been previously allocated */
+	list_for_each_entry(tt, &kvm->arch.spapr_tce_tables, list) {
+		if (tt->liobn == args->liobn)
+			return -EBUSY;
+	}
+
+	tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+	if (!tt)
+		return -ENOMEM;
+
+	tt->liobn = args->liobn;
+	tt->kvm = kvm;
+	tt->virtmode_only = kvmppc_tce_virt_only;
+	tt->grp = grp;
+
+	kvm_get_kvm(kvm);
+
+	mutex_lock(&kvm->lock);
+	list_add(&tt->list, &kvm->arch.spapr_tce_tables);
+
+	mutex_unlock(&kvm->lock);
+
+	pr_debug("LIOBN=%llX hooked to IOMMU %d, flags=%u\n",
+			args->liobn, args->iommu_id, args->flags);
+
+	return anon_inode_getfd("kvm-spapr-tce-iommu",
+			&kvm_spapr_tce_iommu_fops, tt, O_RDWR);
+}
+#else
+long kvm_vm_ioctl_create_spapr_tce_iommu(struct kvm *kvm,
+		struct kvm_create_spapr_tce_iommu *args)
+{
+	return -ENOSYS;
+}
+#endif /* CONFIG_IOMMU_API */
+
+#ifdef CONFIG_IOMMU_API
 /*
  * Virtual mode handling of IOMMU map/unmap.
  */
+static int clear_tce_virt_mode(struct iommu_table *tbl,
+		unsigned long ioba, unsigned long tce_value,
+		unsigned long npages)
+{
+	int ret;
+	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
+
+	ret = iommu_tce_clear_param_check(tbl, ioba, tce_value, npages);
+	if (ret)
+		return ret;
+
+	ret = iommu_clear_tces_and_put_pages(tbl, entry, npages);
+	if (ret < 0)
+		pr_err("iommu_tce: %s failed ioba=%lx, tce_value=%lx ret=%d\n",
+				__func__, ioba, tce_value, ret);
+
+	return ret;
+}
+
+static int put_tce_virt_mode(struct kvmppc_spapr_tce_table *tt,
+		struct iommu_table *tbl,
+		unsigned long ioba, unsigned long tce,
+		pte_t pte, unsigned long pg_size)
+{
+	int ret;
+	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
+
+	ret = iommu_tce_put_param_check(tbl, ioba, tce);
+	if (ret)
+		return ret;
+
+	/* System page size case, easy to handle */
+	if (pg_size == PAGE_SIZE)
+		return iommu_put_tce_user_mode(tbl, entry, tce);
+
+	return -EAGAIN;
+}
+
+static pte_t va_to_linux_pte(struct kvm_vcpu *vcpu,
+		unsigned long hva, bool writing, unsigned long *pg_sizep)
+{
+#ifdef CONFIG_KVM_BOOK3S_64_HV
+	/* Find out the page pte and size if requested */
+	pte_t pte;
+	unsigned long pg_size = 0;
+
+	pte = lookup_linux_pte(vcpu->arch.pgdir, hva,
+			writing, &pg_size);
+	if (!pte_present(pte))
+		return 0;
+
+	*pg_sizep = pg_size;
+
+	return pte;
+#else
+	return 0;
+#endif
+}
+#endif /* CONFIG_IOMMU_API */
+
 /* Converts guest physical address into host virtual */
 static unsigned long get_virt_address(struct kvm_vcpu *vcpu,
 		unsigned long gpa)
@@ -188,6 +323,43 @@ long kvmppc_virtmode_h_put_tce(struct kvm_vcpu *vcpu,
 	if (!tt)
 		return H_TOO_HARD;
 
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		if (tce & (TCE_PCI_READ | TCE_PCI_WRITE)) {
+			unsigned long hpa, pg_size = 0;
+			pte_t pte;
+
+			hpa = get_virt_address(vcpu, tce);
+			if (hpa == ERROR_ADDR)
+				return -EFAULT;
+
+			pte = va_to_linux_pte(vcpu, hpa, tce & TCE_PCI_WRITE,
+					&pg_size);
+			if (!pte)
+				return -EFAULT;
+
+			ret = put_tce_virt_mode(tt, tbl, ioba, hpa,
+					pte, pg_size);
+		} else {
+			ret = clear_tce_virt_mode(tbl, ioba, 0, 1);
+		}
+		iommu_flush_tce(tbl);
+
+		WARN_ON(ret == -EAGAIN);
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	return kvmppc_emulated_h_put_tce(tt, ioba, tce);
 }
@@ -213,6 +385,52 @@ long kvmppc_virtmode_h_put_tce_indirect(struct kvm_vcpu *vcpu,
 	if (tces == ERROR_ADDR)
 		return H_TOO_HARD;
 
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret = 0;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		for (i = 0; i < npages; ++i) {
+			unsigned long hpa, pg_size = 0;
+			pte_t pte = 0;
+			unsigned long tce;
+			unsigned long ptce = tces + i * sizeof(unsigned long);
+
+			if (get_user(tce, (unsigned long __user *)ptce))
+				break;
+
+			hpa = get_virt_address(vcpu, tce);
+			if (hpa == ERROR_ADDR)
+				return -EFAULT;
+
+			pte = va_to_linux_pte(vcpu, hpa,
+					tce & TCE_PCI_WRITE, &pg_size);
+			if (!pte)
+				return -EFAULT;
+
+			ret = put_tce_virt_mode(tt, tbl,
+					ioba + (i << IOMMU_PAGE_SHIFT),
+					hpa, pte, pg_size);
+			if (ret)
+				break;
+		}
+		if (ret)
+			clear_tce_virt_mode(tbl, ioba, 0, i);
+
+		iommu_flush_tce(tbl);
+
+		WARN_ON(ret == -EAGAIN);
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
 		return H_PARAMETER;
@@ -253,6 +471,26 @@ long kvmppc_virtmode_h_stuff_tce(struct kvm_vcpu *vcpu,
 	if (!tt)
 		return H_TOO_HARD;
 
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		ret = clear_tce_virt_mode(tbl, ioba,
+				tce_value, npages);
+
+		WARN_ON(ret == -EAGAIN);
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
 		return H_PARAMETER;
diff --git a/arch/powerpc/kvm/book3s_64_vio_hv.c b/arch/powerpc/kvm/book3s_64_vio_hv.c
index 55fdf7a..c5e5905 100644
--- a/arch/powerpc/kvm/book3s_64_vio_hv.c
+++ b/arch/powerpc/kvm/book3s_64_vio_hv.c
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/hugetlb.h>
 #include <linux/list.h>
+#include <linux/iommu.h>
 
 #include <asm/tlbflush.h>
 #include <asm/kvm_ppc.h>
@@ -161,6 +162,85 @@ static unsigned long get_real_address(struct kvm_vcpu *vcpu,
 	return hwaddr;
 }
 
+#ifdef CONFIG_IOMMU_API
+static int clear_tce_real_mode(struct iommu_table *tbl,
+		unsigned long ioba,
+		unsigned long tce_value, unsigned long npages)
+{
+	int ret;
+	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
+
+	ret = iommu_tce_clear_param_check(tbl, ioba, tce_value, npages);
+	if (ret)
+		return ret;
+
+	for ( ; npages; --npages, ++entry) {
+		struct page *page;
+		unsigned long oldtce;
+
+		oldtce = iommu_clear_tce(tbl, entry);
+		if (!oldtce)
+			continue;
+
+		page = realmode_pfn_to_page(oldtce >> PAGE_SHIFT);
+		if (!page) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (oldtce & TCE_PCI_WRITE)
+			SetPageDirty(page);
+
+		ret = realmode_put_page(page);
+		if (ret)
+			break;
+	}
+	/* if (ret < 0)
+		pr_err("iommu_tce: %s failed ioba=%lx, tce_value=%lx ret=%d\n",
+				__func__, ioba, tce_value, ret); */
+
+	return ret;
+}
+
+static int put_tce_real_mode(struct kvmppc_spapr_tce_table *tt,
+		struct iommu_table *tbl,
+		unsigned long ioba, unsigned long tce,
+		pte_t pte, unsigned long pg_size)
+{
+	int ret;
+	unsigned long entry = ioba >> IOMMU_PAGE_SHIFT;
+	struct page *page = NULL;
+	enum dma_data_direction direction = iommu_tce_direction(tce);
+
+	ret = iommu_tce_put_param_check(tbl, ioba, tce);
+	if (ret)
+		return ret;
+
+	if (pg_size != PAGE_SIZE)
+		return -EAGAIN;
+
+	/* Small page case, find page struct to increment a counter */
+	page = realmode_pfn_to_page(tce >> PAGE_SHIFT);
+	if (!page)
+		return -EAGAIN;
+
+	ret = realmode_get_page(page);
+	if (ret)
+		return ret;
+
+	/* tce_build accepts virtual addresses */
+	ret = iommu_tce_build(tbl, entry, (unsigned long) __va(tce), direction);
+	if (ret)
+		realmode_put_page(page);
+
+	/* if (ret < 0)
+		pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%d\n",
+				__func__, ioba, tce, ret); */
+
+	return ret;
+}
+#endif /* CONFIG_IOMMU_API */
+
 long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
 		      unsigned long ioba, unsigned long tce)
 {
@@ -171,6 +251,44 @@ long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
 	if (!tt)
 		return H_TOO_HARD;
 
+	if (tt->virtmode_only)
+		return H_TOO_HARD;
+
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		if (tce & (TCE_PCI_READ | TCE_PCI_WRITE)) {
+			unsigned long hpa, pg_size = 0;
+			pte_t pte = 0;
+
+			hpa = get_real_address(vcpu, tce, tce & TCE_PCI_WRITE,
+					&pte, &pg_size);
+			if (hpa == ERROR_ADDR)
+				return H_TOO_HARD;
+
+			ret = put_tce_real_mode(tt, tbl, ioba,
+					hpa, pte, pg_size);
+		} else {
+			ret = clear_tce_real_mode(tbl, ioba, 0, 1);
+		}
+		iommu_flush_tce(tbl);
+
+		if (ret == -EAGAIN)
+			return H_TOO_HARD;
+
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	return kvmppc_emulated_h_put_tce(tt, ioba, tce);
 }
@@ -192,10 +310,58 @@ long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu,
 	if (!tt)
 		return H_TOO_HARD;
 
+	if (tt->virtmode_only)
+		return H_TOO_HARD;
+
 	tces = get_real_address(vcpu, tce_list, false, NULL, NULL);
 	if (tces == ERROR_ADDR)
 		return H_TOO_HARD;
 
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret = 0;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		for (i = 0; i < npages; ++i) {
+			unsigned long hpa, pg_size = 0;
+			pte_t pte = 0;
+			unsigned long tce;
+			unsigned long ptce = tces + i * sizeof(unsigned long);
+
+			if (get_user(tce, (unsigned long __user *)ptce))
+				break;
+
+			hpa = get_real_address(vcpu, tce,
+					tce & TCE_PCI_WRITE,
+					&pte, &pg_size);
+			if (hpa == ERROR_ADDR)
+				ret = -EAGAIN;
+			else
+				ret = put_tce_real_mode(tt, tbl,
+						ioba + (i << IOMMU_PAGE_SHIFT),
+						hpa, pte, pg_size);
+			if (ret)
+				break;
+		}
+		if (ret)
+			clear_tce_real_mode(tbl, ioba, 0, i);
+
+		iommu_flush_tce(tbl);
+
+		if (ret == -EAGAIN)
+			return H_TOO_HARD;
+
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
 		return H_PARAMETER;
@@ -236,6 +402,32 @@ long kvmppc_h_stuff_tce(struct kvm_vcpu *vcpu,
 	if (!tt)
 		return H_TOO_HARD;
 
+	if (tt->virtmode_only)
+		return H_TOO_HARD;
+
+#ifdef CONFIG_IOMMU_API
+	if (tt->grp) {
+		long ret;
+		struct iommu_table *tbl = iommu_group_get_iommudata(tt->grp);
+
+		/* Return error if the group is being destroyed */
+		if (!tbl)
+			return H_RESCINDED;
+
+		ret = clear_tce_real_mode(tbl, ioba,
+				tce_value, npages);
+		iommu_flush_tce(tbl);
+
+		if (ret == -EAGAIN)
+			return H_TOO_HARD;
+
+		if (ret < 0)
+			return H_PARAMETER;
+
+		return H_SUCCESS;
+	}
+#endif
+
 	/* Emulated IO */
 	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
 		return H_PARAMETER;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index b7ad589..269b0f6 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -385,6 +385,7 @@ int kvm_dev_ioctl_check_extension(long ext)
 		break;
 #endif
 	case KVM_CAP_SPAPR_MULTITCE:
+	case KVM_CAP_SPAPR_TCE_IOMMU:
 		r = 1;
 		break;
 	default:
@@ -935,6 +936,17 @@ long kvm_arch_vm_ioctl(struct file *filp,
 		r = kvm_vm_ioctl_create_spapr_tce(kvm, &create_tce);
 		goto out;
 	}
+	case KVM_CREATE_SPAPR_TCE_IOMMU: {
+		struct kvm_create_spapr_tce_iommu create_tce_iommu;
+		struct kvm *kvm = filp->private_data;
+
+		r = -EFAULT;
+		if (copy_from_user(&create_tce_iommu, argp,
+				sizeof(create_tce_iommu)))
+			goto out;
+		r = kvm_vm_ioctl_create_spapr_tce_iommu(kvm, &create_tce_iommu);
+		goto out;
+	}
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 #ifdef CONFIG_KVM_BOOK3S_64_HV
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6c04da1..161e1d3 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -641,6 +641,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_RTAS (0x100000 + 87)
 #define KVM_CAP_SPAPR_XICS (0x100000 + 88)
 #define KVM_CAP_SPAPR_MULTITCE (0x110000 + 89)
+#define KVM_CAP_SPAPR_TCE_IOMMU (0x110000 + 90)
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -885,6 +886,7 @@ struct kvm_s390_ucas_mapping {
 #define KVM_PPC_GET_HTAB_FD	  _IOW(KVMIO,  0xaa, struct kvm_get_htab_fd)
 /* Available with KVM_CAP_PPC_RTAS */
 #define KVM_PPC_RTAS_DEFINE_TOKEN _IOW(KVMIO,  0xdc, struct kvm_rtas_token_args)
+#define KVM_CREATE_SPAPR_TCE_IOMMU _IOW(KVMIO,  0xaf, struct kvm_create_spapr_tce_iommu)
 
 /*
  * ioctls for vcpu fds
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 4/6] iommu: Add a function to find an iommu group by id
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

As IOMMU groups are exposed to the user space by their numbers,
the user space can use them in various kernel APIs so the kernel
might need an API to find a group by its ID.

As an example, QEMU VFIO on PPC64 platform needs it to associate
a logical bus number (LIOBN) with a specific IOMMU group in order
to support in-kernel handling of DMA map/unmap requests.

This adds the iommu_group_get_by_id(id) function which performs
this search.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 drivers/iommu/iommu.c |   29 +++++++++++++++++++++++++++++
 include/linux/iommu.h |    1 +
 2 files changed, 30 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index ddbdaca..5514dfa 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -204,6 +204,35 @@ again:
 }
 EXPORT_SYMBOL_GPL(iommu_group_alloc);
 
+struct iommu_group *iommu_group_get_by_id(int id)
+{
+	struct kobject *group_kobj;
+	struct iommu_group *group;
+	const char *name;
+
+	if (!iommu_group_kset)
+		return NULL;
+
+	name = kasprintf(GFP_KERNEL, "%d", id);
+	if (!name)
+		return NULL;
+
+	group_kobj = kset_find_obj(iommu_group_kset, name);
+	kfree(name);
+
+	if (!group_kobj)
+		return NULL;
+
+	group = container_of(group_kobj, struct iommu_group, kobj);
+	BUG_ON(group->id != id);
+
+	kobject_get(group->devices_kobj);
+	kobject_put(&group->kobj);
+
+	return group;
+}
+EXPORT_SYMBOL_GPL(iommu_group_get_by_id);
+
 /**
  * iommu_group_get_iommudata - retrieve iommu_data registered for a group
  * @group: the group
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f3b99e1..00e5d7d 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -113,6 +113,7 @@ struct iommu_ops {
 extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops);
 extern bool iommu_present(struct bus_type *bus);
 extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus);
+extern struct iommu_group *iommu_group_get_by_id(int id);
 extern void iommu_domain_free(struct iommu_domain *domain);
 extern int iommu_attach_device(struct iommu_domain *domain,
 			       struct device *dev);
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 3/6] powerpc: Prepare to support kernel handling of IOMMU map/unmap
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

The current VFIO-on-POWER implementation supports only user mode
driven mapping, i.e. QEMU is sending requests to map/unmap pages.
However this approach is really slow, so we want to move that to KVM.
Since H_PUT_TCE can be extremely performance sensitive (especially with
network adapters where each packet needs to be mapped/unmapped) we chose
to implement that as a "fast" hypercall directly in "real
mode" (processor still in the guest context but MMU off).

To be able to do that, we need to provide some facilities to
access the struct page count within that real mode environment as things
like the sparsemem vmemmap mappings aren't accessible.

This adds an API to increment/decrement page counter as
get_user_pages API used for user mode mapping does not work
in the real mode.

CONFIG_SPARSEMEM_VMEMMAP and CONFIG_FLATMEM are supported.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Paul Mackerras <paulus@samba.org>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 arch/powerpc/include/asm/pgtable-ppc64.h |    4 ++
 arch/powerpc/mm/init_64.c                |   77 +++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index 0182c20..4c56ede 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -377,6 +377,10 @@ static inline pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea,
 }
 #endif /* !CONFIG_HUGETLB_PAGE */
 
+struct page *realmode_pfn_to_page(unsigned long pfn);
+int realmode_get_page(struct page *page);
+int realmode_put_page(struct page *page);
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_POWERPC_PGTABLE_PPC64_H_ */
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index 95a4529..838b8ae 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -297,5 +297,80 @@ int __meminit vmemmap_populate(struct page *start_page,
 
 	return 0;
 }
-#endif /* CONFIG_SPARSEMEM_VMEMMAP */
 
+/*
+ * We do not have access to the sparsemem vmemmap, so we fallback to
+ * walking the list of sparsemem blocks which we already maintain for
+ * the sake of crashdump. In the long run, we might want to maintain
+ * a tree if performance of that linear walk becomes a problem.
+ *
+ * Any of realmode_XXXX functions can fail due to:
+ * 1) As real sparsemem blocks do not lay in RAM continously (they
+ * are in virtual address space which is not available in the real mode),
+ * the requested page struct can be split between blocks so get_page/put_page
+ * may fail.
+ * 2) When huge pages are used, the get_page/put_page API will fail
+ * in real mode as the linked addresses in the page struct are virtual
+ * too.
+ * When 1) or 2) takes place, the API returns an error code to cause
+ * an exit to kernel virtual mode where the operation will be completed.
+ */
+struct page *realmode_pfn_to_page(unsigned long pfn)
+{
+	struct vmemmap_backing *vmem_back;
+	struct page *page;
+	unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
+	unsigned long pg_va = (unsigned long) pfn_to_page(pfn);
+
+	for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) {
+		if (pg_va < vmem_back->virt_addr)
+			continue;
+
+		/* Check that page struct is not split between real pages */
+		if ((pg_va + sizeof(struct page)) >
+				(vmem_back->virt_addr + page_size))
+			return NULL;
+
+		page = (struct page *) (vmem_back->phys + pg_va -
+				vmem_back->virt_addr);
+		return page;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
+
+#elif defined(CONFIG_FLATMEM)
+
+struct page *realmode_pfn_to_page(unsigned long pfn)
+{
+	struct page *page = pfn_to_page(pfn);
+	return page;
+}
+EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
+
+#endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */
+
+#if defined(CONFIG_SPARSEMEM_VMEMMAP) || defined(CONFIG_FLATMEM)
+int realmode_get_page(struct page *page)
+{
+	if (PageTail(page))
+		return -EAGAIN;
+
+	get_page(page);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(realmode_get_page);
+
+int realmode_put_page(struct page *page)
+{
+	if (PageCompound(page))
+		return -EAGAIN;
+
+	put_page(page);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(realmode_put_page);
+#endif
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 2/6] KVM: PPC: Add support for multiple-TCE hcalls
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

This adds real mode handlers for the H_PUT_TCE_INDIRECT and
H_STUFF_TCE hypercalls for QEMU emulated devices such as virtio
devices or emulated PCI.  These calls allow adding multiple entries
(up to 512) into the TCE table in one call which saves time on
transition to/from real mode.

This adds a guest physical to host real address converter
and calls the existing H_PUT_TCE handler. The converting function
is going to be fully utilized by upcoming VFIO supporting patches.

This also implements the KVM_CAP_PPC_MULTITCE capability,
so in order to support the functionality of this patch, QEMU
needs to query for this capability and set the "hcall-multi-tce"
hypertas property only if the capability is present, otherwise
there will be serious performance degradation.

Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 Documentation/virtual/kvm/api.txt       |   15 ++
 arch/powerpc/include/asm/kvm_ppc.h      |   15 +-
 arch/powerpc/kvm/book3s_64_vio.c        |  114 +++++++++++++++
 arch/powerpc/kvm/book3s_64_vio_hv.c     |  231 +++++++++++++++++++++++++++----
 arch/powerpc/kvm/book3s_hv.c            |   23 +++
 arch/powerpc/kvm/book3s_hv_rmhandlers.S |    6 +
 arch/powerpc/kvm/book3s_pr_papr.c       |   37 ++++-
 arch/powerpc/kvm/powerpc.c              |    3 +
 include/uapi/linux/kvm.h                |    1 +
 9 files changed, 413 insertions(+), 32 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index a4df553..f621cd6 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -2463,3 +2463,18 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
    where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
  - The tsize field of mas1 shall be set to 4K on TLB0, even though the
    hardware ignores this value for TLB0.
+
+
+6.4 KVM_CAP_PPC_MULTITCE
+
+Architectures: ppc
+Parameters: none
+Returns: 0 on success; -1 on error
+
+This capability enables the guest to put/remove multiple TCE entries
+per hypercall which significanly accelerates DMA operations for PPC KVM
+guests.
+
+When this capability is enabled, H_PUT_TCE_INDIRECT and H_STUFF_TCE are
+expected to occur rather than H_PUT_TCE which supports only one TCE entry
+per call.
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 99da298..d501246 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -139,8 +139,19 @@ extern void kvmppc_xics_free(struct kvm *kvm);
 
 extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 				struct kvm_create_spapr_tce *args);
-extern long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
-			     unsigned long ioba, unsigned long tce);
+extern struct kvmppc_spapr_tce_table *kvmppc_find_tce_table(
+		struct kvm_vcpu *vcpu, unsigned long liobn);
+extern long kvmppc_emulated_h_put_tce(struct kvmppc_spapr_tce_table *stt,
+		unsigned long ioba, unsigned long tce);
+extern long kvmppc_virtmode_h_put_tce(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce);
+extern long kvmppc_virtmode_h_put_tce_indirect(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_list, unsigned long npages);
+extern long kvmppc_virtmode_h_stuff_tce(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_value, unsigned long npages);
 extern long kvm_vm_ioctl_allocate_rma(struct kvm *kvm,
 				struct kvm_allocate_rma *rma);
 extern struct kvmppc_linear_info *kvm_alloc_rma(void);
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index 72ffc89..643ac1e 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -14,6 +14,7 @@
  *
  * Copyright 2010 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
  * Copyright 2011 David Gibson, IBM Corporation <dwg@au1.ibm.com>
+ * Copyright 2013 Alexey Kardashevskiy, IBM Corporation <aik@au1.ibm.com>
  */
 
 #include <linux/types.h>
@@ -36,9 +37,14 @@
 #include <asm/ppc-opcode.h>
 #include <asm/kvm_host.h>
 #include <asm/udbg.h>
+#include <asm/iommu.h>
 
 #define TCES_PER_PAGE	(PAGE_SIZE / sizeof(u64))
+#define ERROR_ADDR      (~(unsigned long)0x0)
 
+/*
+ * TCE tables handlers.
+ */
 static long kvmppc_stt_npages(unsigned long window_size)
 {
 	return ALIGN((window_size >> SPAPR_TCE_SHIFT)
@@ -148,3 +154,111 @@ fail:
 	}
 	return ret;
 }
+
+/*
+ * Virtual mode handling of IOMMU map/unmap.
+ */
+/* Converts guest physical address into host virtual */
+static unsigned long get_virt_address(struct kvm_vcpu *vcpu,
+		unsigned long gpa)
+{
+	unsigned long hva, gfn = gpa >> PAGE_SHIFT;
+	struct kvm_memory_slot *memslot;
+
+	memslot = search_memslots(kvm_memslots(vcpu->kvm), gfn);
+	if (!memslot)
+		return ERROR_ADDR;
+
+	/*
+	 * Convert gfn to hva preserving flags and an offset
+	 * within a system page
+	 */
+	hva = __gfn_to_hva_memslot(memslot, gfn) + (gpa & ~PAGE_MASK);
+	return hva;
+}
+
+long kvmppc_virtmode_h_put_tce(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce)
+{
+	struct kvmppc_spapr_tce_table *tt;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to userspace */
+	if (!tt)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	return kvmppc_emulated_h_put_tce(tt, ioba, tce);
+}
+
+long kvmppc_virtmode_h_put_tce_indirect(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_list, unsigned long npages)
+{
+	struct kvmppc_spapr_tce_table *tt;
+	long i;
+	unsigned long tces;
+
+	/* The whole table addressed by tce_list resides in 4K page */
+	if (npages > 512)
+		return H_PARAMETER;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to userspace */
+	if (!tt)
+		return H_TOO_HARD;
+
+	tces = get_virt_address(vcpu, tce_list);
+	if (tces == ERROR_ADDR)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
+		return H_PARAMETER;
+
+	for (i = 0; i < npages; ++i) {
+		unsigned long tce;
+		unsigned long ptce = tces + i * sizeof(unsigned long);
+
+		if (get_user(tce, (unsigned long __user *)ptce))
+			break;
+
+		if (kvmppc_emulated_h_put_tce(tt,
+				ioba + (i << IOMMU_PAGE_SHIFT),	tce))
+			break;
+	}
+	if (i == npages)
+		return H_SUCCESS;
+
+	/* Failed, do cleanup */
+	do {
+		--i;
+		kvmppc_emulated_h_put_tce(tt, ioba + (i << IOMMU_PAGE_SHIFT),
+				0);
+	} while (i);
+
+	return H_PARAMETER;
+}
+
+long kvmppc_virtmode_h_stuff_tce(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_value, unsigned long npages)
+{
+	struct kvmppc_spapr_tce_table *tt;
+	long i;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to userspace */
+	if (!tt)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
+		return H_PARAMETER;
+
+	for (i = 0; i < npages; ++i, ioba += IOMMU_PAGE_SIZE)
+		kvmppc_emulated_h_put_tce(tt, ioba, tce_value);
+
+	return H_SUCCESS;
+}
diff --git a/arch/powerpc/kvm/book3s_64_vio_hv.c b/arch/powerpc/kvm/book3s_64_vio_hv.c
index 30c2f3b..55fdf7a 100644
--- a/arch/powerpc/kvm/book3s_64_vio_hv.c
+++ b/arch/powerpc/kvm/book3s_64_vio_hv.c
@@ -14,6 +14,7 @@
  *
  * Copyright 2010 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
  * Copyright 2011 David Gibson, IBM Corporation <dwg@au1.ibm.com>
+ * Copyright 2013 Alexey Kardashevskiy, IBM Corporation <aik@au1.ibm.com>
  */
 
 #include <linux/types.h>
@@ -35,42 +36,214 @@
 #include <asm/ppc-opcode.h>
 #include <asm/kvm_host.h>
 #include <asm/udbg.h>
+#include <asm/iommu.h>
+#include <asm/tce.h>
 
 #define TCES_PER_PAGE	(PAGE_SIZE / sizeof(u64))
+#define ERROR_ADDR      (~(unsigned long)0x0)
 
-/* WARNING: This will be called in real-mode on HV KVM and virtual
- *          mode on PR KVM
+/*
+ * Finds a TCE table descriptor by LIOBN.
  */
+struct kvmppc_spapr_tce_table *kvmppc_find_tce_table(struct kvm_vcpu *vcpu,
+		unsigned long liobn)
+{
+	struct kvmppc_spapr_tce_table *tt;
+
+	list_for_each_entry(tt, &vcpu->kvm->arch.spapr_tce_tables, list) {
+		if (tt->liobn == liobn)
+			return tt;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(kvmppc_find_tce_table);
+
+/*
+ * kvmppc_emulated_h_put_tce() handles TCE requests for devices emulated
+ * by QEMU. It puts guest TCE values into the table and expects
+ * the QEMU to convert them later in the QEMU device implementation.
+ * Works in both real and virtual modes.
+ */
+long kvmppc_emulated_h_put_tce(struct kvmppc_spapr_tce_table *tt,
+		unsigned long ioba, unsigned long tce)
+{
+	unsigned long idx = ioba >> SPAPR_TCE_SHIFT;
+	struct page *page;
+	u64 *tbl;
+
+	/* udbg_printf("H_PUT_TCE: liobn 0x%lx => tt=%p  window_size=0x%x\n", */
+	/*	    liobn, tt, tt->window_size); */
+	if (ioba >= tt->window_size) {
+		/* pr_err("%s failed on ioba=%lx\n", __func__, ioba); */
+		return H_PARAMETER;
+	}
+	/*
+	 * Note on the use of page_address() in real mode,
+	 *
+	 * It is safe to use page_address() in real mode on ppc64 because
+	 * page_address() is always defined as lowmem_page_address()
+	 * which returns __va(PFN_PHYS(page_to_pfn(page))) which is arithmetial
+	 * operation and does not access page struct.
+	 *
+	 * Theoretically page_address() could be defined different
+	 * but either WANT_PAGE_VIRTUAL or HASHED_PAGE_VIRTUAL
+	 * should be enabled.
+	 * WANT_PAGE_VIRTUAL is never enabled on ppc32/ppc64,
+	 * HASHED_PAGE_VIRTUAL could be enabled for ppc32 only and only
+	 * if CONFIG_HIGHMEM is defined. As CONFIG_SPARSEMEM_VMEMMAP
+	 * is not expected to be enabled on ppc32, page_address()
+	 * is safe for ppc32 as well.
+	 */
+#if defined(HASHED_PAGE_VIRTUAL) || defined(WANT_PAGE_VIRTUAL)
+#error TODO: fix to avoid page_address() here
+#endif
+	page = tt->pages[idx / TCES_PER_PAGE];
+	tbl = (u64 *)page_address(page);
+
+	/*
+	 * Validate TCE address.
+	 * At the moment only flags are validated
+	 * as other check will significantly slow down
+	 * or can make it even impossible to handle TCE requests
+	 * in real mode.
+	 */
+	if (tce & ~(IOMMU_PAGE_MASK | TCE_PCI_WRITE | TCE_PCI_READ))
+		return H_PARAMETER;
+
+	/* udbg_printf("tce @ %p\n", &tbl[idx % TCES_PER_PAGE]); */
+	tbl[idx % TCES_PER_PAGE] = tce;
+
+	return H_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(kvmppc_emulated_h_put_tce);
+
+#ifdef CONFIG_KVM_BOOK3S_64_HV
+/*
+ * Converts guest physical address into host real address.
+ * Also returns pte and page size if the page is present in page table.
+ */
+static unsigned long get_real_address(struct kvm_vcpu *vcpu,
+		unsigned long gpa, bool writing,
+		pte_t *ptep, unsigned long *pg_sizep)
+{
+	struct kvm_memory_slot *memslot;
+	pte_t pte;
+	unsigned long hva, pg_size = 0, hwaddr, offset;
+	unsigned long gfn = gpa >> PAGE_SHIFT;
+
+	/* Find a KVM memslot */
+	memslot = search_memslots(kvm_memslots(vcpu->kvm), gfn);
+	if (!memslot)
+		return ERROR_ADDR;
+
+	/* Convert guest physical address to host virtual */
+	hva = __gfn_to_hva_memslot(memslot, gfn);
+
+	/* Find a PTE and determine the size */
+	pte = lookup_linux_pte(vcpu->arch.pgdir, hva,
+			writing, &pg_size);
+	if (!pte_present(pte))
+		return ERROR_ADDR;
+
+	/* Calculate host phys address keeping flags and offset in the page */
+	offset = gpa & (pg_size - 1);
+
+	/* pte_pfn(pte) should return an address aligned to pg_size */
+	hwaddr = (pte_pfn(pte) << PAGE_SHIFT) + offset;
+
+	/* Copy outer values if required */
+	if (pg_sizep)
+		*pg_sizep = pg_size;
+	if (ptep)
+		*ptep = pte;
+
+	return hwaddr;
+}
+
 long kvmppc_h_put_tce(struct kvm_vcpu *vcpu, unsigned long liobn,
 		      unsigned long ioba, unsigned long tce)
 {
-	struct kvm *kvm = vcpu->kvm;
-	struct kvmppc_spapr_tce_table *stt;
-
-	/* udbg_printf("H_PUT_TCE(): liobn=0x%lx ioba=0x%lx, tce=0x%lx\n", */
-	/* 	    liobn, ioba, tce); */
-
-	list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) {
-		if (stt->liobn == liobn) {
-			unsigned long idx = ioba >> SPAPR_TCE_SHIFT;
-			struct page *page;
-			u64 *tbl;
-
-			/* udbg_printf("H_PUT_TCE: liobn 0x%lx => stt=%p  window_size=0x%x\n", */
-			/* 	    liobn, stt, stt->window_size); */
-			if (ioba >= stt->window_size)
-				return H_PARAMETER;
-
-			page = stt->pages[idx / TCES_PER_PAGE];
-			tbl = (u64 *)page_address(page);
-
-			/* FIXME: Need to validate the TCE itself */
-			/* udbg_printf("tce @ %p\n", &tbl[idx % TCES_PER_PAGE]); */
-			tbl[idx % TCES_PER_PAGE] = tce;
-			return H_SUCCESS;
-		}
+	struct kvmppc_spapr_tce_table *tt;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to virtual space */
+	if (!tt)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	return kvmppc_emulated_h_put_tce(tt, ioba, tce);
+}
+
+long kvmppc_h_put_tce_indirect(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_list,	unsigned long npages)
+{
+	struct kvmppc_spapr_tce_table *tt;
+	long i;
+	unsigned long tces;
+
+	/* The whole table addressed by tce_list resides in 4K page */
+	if (npages > 512)
+		return H_PARAMETER;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to virtual space */
+	if (!tt)
+		return H_TOO_HARD;
+
+	tces = get_real_address(vcpu, tce_list, false, NULL, NULL);
+	if (tces == ERROR_ADDR)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
+		return H_PARAMETER;
+
+	for (i = 0; i < npages; ++i) {
+		unsigned long tce;
+		unsigned long ptce = tces + i * sizeof(unsigned long);
+
+		if (get_user(tce, (unsigned long __user *)ptce))
+			break;
+
+		if (kvmppc_emulated_h_put_tce(tt,
+				ioba + (i << IOMMU_PAGE_SHIFT), tce))
+			break;
 	}
+	if (i == npages)
+		return H_SUCCESS;
+
+	/* Failed, do cleanup */
+	do {
+		--i;
+		kvmppc_emulated_h_put_tce(tt, ioba + (i << IOMMU_PAGE_SHIFT),
+				0);
+	} while (i);
+
+	return H_PARAMETER;
+}
 
-	/* Didn't find the liobn, punt it to userspace */
-	return H_TOO_HARD;
+long kvmppc_h_stuff_tce(struct kvm_vcpu *vcpu,
+		unsigned long liobn, unsigned long ioba,
+		unsigned long tce_value, unsigned long npages)
+{
+	struct kvmppc_spapr_tce_table *tt;
+	long i;
+
+	tt = kvmppc_find_tce_table(vcpu, liobn);
+	/* Didn't find the liobn, put it to virtual space */
+	if (!tt)
+		return H_TOO_HARD;
+
+	/* Emulated IO */
+	if ((ioba + (npages << IOMMU_PAGE_SHIFT)) > tt->window_size)
+		return H_PARAMETER;
+
+	for (i = 0; i < npages; ++i, ioba += IOMMU_PAGE_SIZE)
+		kvmppc_emulated_h_put_tce(tt, ioba, tce_value);
+
+	return H_SUCCESS;
 }
+
+#endif /* CONFIG_KVM_BOOK3S_64_HV */
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index e5afdcb..6eb6f44 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -565,6 +565,29 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
 			ret = kvmppc_xics_hcall(vcpu, req);
 			break;
 		} /* fallthrough */
+	case H_PUT_TCE:
+		ret = kvmppc_virtmode_h_put_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
+						kvmppc_get_gpr(vcpu, 5),
+						kvmppc_get_gpr(vcpu, 6));
+		if (ret == H_TOO_HARD)
+			return RESUME_HOST;
+		break;
+	case H_PUT_TCE_INDIRECT:
+		ret = kvmppc_virtmode_h_put_tce_indirect(vcpu, kvmppc_get_gpr(vcpu, 4),
+						kvmppc_get_gpr(vcpu, 5),
+						kvmppc_get_gpr(vcpu, 6),
+						kvmppc_get_gpr(vcpu, 7));
+		if (ret == H_TOO_HARD)
+			return RESUME_HOST;
+		break;
+	case H_STUFF_TCE:
+		ret = kvmppc_virtmode_h_stuff_tce(vcpu, kvmppc_get_gpr(vcpu, 4),
+						kvmppc_get_gpr(vcpu, 5),
+						kvmppc_get_gpr(vcpu, 6),
+						kvmppc_get_gpr(vcpu, 7));
+		if (ret == H_TOO_HARD)
+			return RESUME_HOST;
+		break;
 	default:
 		return RESUME_HOST;
 	}
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index d3e26be..4d73406 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -1472,6 +1472,12 @@ hcall_real_table:
 	.long	0		/* 0x11c */
 	.long	0		/* 0x120 */
 	.long	.kvmppc_h_bulk_remove - hcall_real_table
+	.long	0		/* 0x128 */
+	.long	0		/* 0x12c */
+	.long	0		/* 0x130 */
+	.long	0		/* 0x134 */
+	.long	.kvmppc_h_stuff_tce - hcall_real_table
+	.long	.kvmppc_h_put_tce_indirect - hcall_real_table
 hcall_real_table_end:
 
 ignore_hdec:
diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c
index 8352cac..603e4f2 100644
--- a/arch/powerpc/kvm/book3s_pr_papr.c
+++ b/arch/powerpc/kvm/book3s_pr_papr.c
@@ -220,7 +220,38 @@ static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu)
 	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
 	long rc;
 
-	rc = kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
+	rc = kvmppc_virtmode_h_put_tce(vcpu, liobn, ioba, tce);
+	if (rc == H_TOO_HARD)
+		return EMULATE_FAIL;
+	kvmppc_set_gpr(vcpu, 3, rc);
+	return EMULATE_DONE;
+}
+
+static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
+{
+	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
+	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
+	unsigned long tce = kvmppc_get_gpr(vcpu, 6);
+	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
+	long rc;
+
+	rc = kvmppc_virtmode_h_put_tce_indirect(vcpu, liobn, ioba,
+			tce, npages);
+	if (rc == H_TOO_HARD)
+		return EMULATE_FAIL;
+	kvmppc_set_gpr(vcpu, 3, rc);
+	return EMULATE_DONE;
+}
+
+static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
+{
+	unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
+	unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
+	unsigned long tce_value = kvmppc_get_gpr(vcpu, 6);
+	unsigned long npages = kvmppc_get_gpr(vcpu, 7);
+	long rc;
+
+	rc = kvmppc_virtmode_h_stuff_tce(vcpu, liobn, ioba, tce_value, npages);
 	if (rc == H_TOO_HARD)
 		return EMULATE_FAIL;
 	kvmppc_set_gpr(vcpu, 3, rc);
@@ -249,6 +280,10 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
 		return kvmppc_h_pr_bulk_remove(vcpu);
 	case H_PUT_TCE:
 		return kvmppc_h_pr_put_tce(vcpu);
+	case H_PUT_TCE_INDIRECT:
+		return kvmppc_h_pr_put_tce_indirect(vcpu);
+	case H_STUFF_TCE:
+		return kvmppc_h_pr_stuff_tce(vcpu);
 	case H_CEDE:
 		vcpu->arch.shared->msr |= MSR_EE;
 		kvm_vcpu_block(vcpu);
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index f9c159e..b7ad589 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -384,6 +384,9 @@ int kvm_dev_ioctl_check_extension(long ext)
 		r = 1;
 		break;
 #endif
+	case KVM_CAP_SPAPR_MULTITCE:
+		r = 1;
+		break;
 	default:
 		r = 0;
 		break;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6f49c87..6c04da1 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -640,6 +640,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_HTAB_FD 84
 #define KVM_CAP_PPC_RTAS (0x100000 + 87)
 #define KVM_CAP_SPAPR_XICS (0x100000 + 88)
+#define KVM_CAP_SPAPR_MULTITCE (0x110000 + 89)
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 1/6] KVM: PPC: Make lookup_linux_pte public
From: Alexey Kardashevskiy @ 2013-05-06  7:25 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc,
	Alex Williamson, Paul Mackerras, Joerg Roedel, David Gibson
In-Reply-To: <1367825157-27231-1-git-send-email-aik@ozlabs.ru>

The lookup_linux_pte() function returns a linux PTE which is needed in
the process of converting KVM guest physical address into host real
address in real mode.

This conversion will be used by upcoming support of H_PUT_TCE_INDIRECT,
as the TCE list address comes from the guest and is a guest physical
address.  This makes lookup_linux_pte() public so that code can call
it.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Cc: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 arch/powerpc/include/asm/kvm_ppc.h  |    3 +++
 arch/powerpc/kvm/book3s_hv_rm_mmu.c |    5 +++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 41426c9..99da298 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -379,4 +379,7 @@ static inline ulong kvmppc_get_ea_indexed(struct kvm_vcpu *vcpu, int ra, int rb)
 	return ea;
 }
 
+pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
+		int writing, unsigned long *pte_sizep);
+
 #endif /* __POWERPC_KVM_PPC_H__ */
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 6dcbb49..18fc382 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -134,8 +134,8 @@ static void remove_revmap_chain(struct kvm *kvm, long pte_index,
 	unlock_rmap(rmap);
 }
 
-static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
-			      int writing, unsigned long *pte_sizep)
+pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
+		       int writing, unsigned long *pte_sizep)
 {
 	pte_t *ptep;
 	unsigned long ps = *pte_sizep;
@@ -154,6 +154,7 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
 		return __pte(0);
 	return kvmppc_read_update_linux_pte(ptep, writing);
 }
+EXPORT_SYMBOL_GPL(lookup_linux_pte);
 
 static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)
 {
-- 
1.7.10.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox