LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH AUTOSEL 5.1 89/95] powerpc: enable a 30-bit ZONE_DMA for 32-bit pmac
From: Sasha Levin @ 2019-06-27  0:30 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Sasha Levin, Aaro Koskinen, linuxppc-dev, Christoph Hellwig,
	Larry Finger
In-Reply-To: <20190627003021.19867-1-sashal@kernel.org>

From: Christoph Hellwig <hch@lst.de>

[ Upstream commit 9739ab7eda459f0669ec9807e0d9be5020bab88c ]

With the strict dma mask checking introduced with the switch to
the generic DMA direct code common wifi chips on 32-bit powerbooks
stopped working.  Add a 30-bit ZONE_DMA to the 32-bit pmac builds
to allow them to reliably allocate dma coherent memory.

Fixes: 65a21b71f948 ("powerpc/dma: remove dma_nommu_dma_supported")
Reported-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Tested-by: Larry Finger <Larry.Finger@lwfinger.net>
Acked-by: Larry Finger <Larry.Finger@lwfinger.net>
Tested-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/powerpc/include/asm/page.h         | 7 +++++++
 arch/powerpc/mm/mem.c                   | 3 ++-
 arch/powerpc/platforms/powermac/Kconfig | 1 +
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index ed870468ef6f..d408711d09fb 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -330,6 +330,13 @@ struct vm_area_struct;
 #endif /* __ASSEMBLY__ */
 #include <asm/slice.h>
 
+/*
+ * Allow 30-bit DMA for very limited Broadcom wifi chips on many powerbooks.
+ */
+#ifdef CONFIG_PPC32
+#define ARCH_ZONE_DMA_BITS 30
+#else
 #define ARCH_ZONE_DMA_BITS 31
+#endif
 
 #endif /* _ASM_POWERPC_PAGE_H */
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index f6787f90e158..b98ce400a889 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -255,7 +255,8 @@ void __init paging_init(void)
 	       (long int)((top_of_ram - total_ram) >> 20));
 
 #ifdef CONFIG_ZONE_DMA
-	max_zone_pfns[ZONE_DMA]	= min(max_low_pfn, 0x7fffffffUL >> PAGE_SHIFT);
+	max_zone_pfns[ZONE_DMA]	= min(max_low_pfn,
+			((1UL << ARCH_ZONE_DMA_BITS) - 1) >> PAGE_SHIFT);
 #endif
 	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
 #ifdef CONFIG_HIGHMEM
diff --git a/arch/powerpc/platforms/powermac/Kconfig b/arch/powerpc/platforms/powermac/Kconfig
index f834a19ed772..c02d8c503b29 100644
--- a/arch/powerpc/platforms/powermac/Kconfig
+++ b/arch/powerpc/platforms/powermac/Kconfig
@@ -7,6 +7,7 @@ config PPC_PMAC
 	select PPC_INDIRECT_PCI if PPC32
 	select PPC_MPC106 if PPC32
 	select PPC_NATIVE
+	select ZONE_DMA if PPC32
 	default y
 
 config PPC_PMAC64
-- 
2.20.1


^ permalink raw reply related

* [PATCH AUTOSEL 5.1 45/95] ibmvnic: Fix unchecked return codes of memory allocations
From: Sasha Levin @ 2019-06-27  0:29 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Sasha Levin, netdev, Thomas Falcon, linuxppc-dev,
	David S . Miller
In-Reply-To: <20190627003021.19867-1-sashal@kernel.org>

From: Thomas Falcon <tlfalcon@linux.ibm.com>

[ Upstream commit 7c940b1a5291e5069d561f5b8f0e51db6b7a259a ]

The return values for these memory allocations are unchecked,
which may cause an oops if the driver does not handle them after
a failure. Fix by checking the function's return code.

Signed-off-by: Thomas Falcon <tlfalcon@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/net/ethernet/ibm/ibmvnic.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 664e52fa7919..0e4029c54241 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -438,9 +438,10 @@ static int reset_rx_pools(struct ibmvnic_adapter *adapter)
 		if (rx_pool->buff_size != be64_to_cpu(size_array[i])) {
 			free_long_term_buff(adapter, &rx_pool->long_term_buff);
 			rx_pool->buff_size = be64_to_cpu(size_array[i]);
-			alloc_long_term_buff(adapter, &rx_pool->long_term_buff,
-					     rx_pool->size *
-					     rx_pool->buff_size);
+			rc = alloc_long_term_buff(adapter,
+						  &rx_pool->long_term_buff,
+						  rx_pool->size *
+						  rx_pool->buff_size);
 		} else {
 			rc = reset_long_term_buff(adapter,
 						  &rx_pool->long_term_buff);
@@ -706,9 +707,9 @@ static int init_tx_pools(struct net_device *netdev)
 			return rc;
 		}
 
-		init_one_tx_pool(netdev, &adapter->tso_pool[i],
-				 IBMVNIC_TSO_BUFS,
-				 IBMVNIC_TSO_BUF_SZ);
+		rc = init_one_tx_pool(netdev, &adapter->tso_pool[i],
+				      IBMVNIC_TSO_BUFS,
+				      IBMVNIC_TSO_BUF_SZ);
 		if (rc) {
 			release_tx_pools(adapter);
 			return rc;
-- 
2.20.1


^ permalink raw reply related

* [PATCH AUTOSEL 5.1 44/95] ibmvnic: Refresh device multicast list after reset
From: Sasha Levin @ 2019-06-27  0:29 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Sasha Levin, netdev, Thomas Falcon, linuxppc-dev,
	David S . Miller
In-Reply-To: <20190627003021.19867-1-sashal@kernel.org>

From: Thomas Falcon <tlfalcon@linux.ibm.com>

[ Upstream commit be32a24372cf162e825332da1a7ccef058d4f20b ]

It was observed that multicast packets were no longer received after
a device reset.  The fix is to resend the current multicast list to
the backing device after recovery.

Signed-off-by: Thomas Falcon <tlfalcon@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/net/ethernet/ibm/ibmvnic.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 71bf895409a1..664e52fa7919 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -1851,6 +1851,9 @@ static int do_reset(struct ibmvnic_adapter *adapter,
 		return 0;
 	}
 
+	/* refresh device's multicast list */
+	ibmvnic_set_multi(netdev);
+
 	/* kick napi */
 	for (i = 0; i < adapter->req_rx_queues; i++)
 		napi_schedule(&adapter->napi[i]);
-- 
2.20.1


^ permalink raw reply related

* [PATCH AUTOSEL 5.1 43/95] ibmvnic: Do not close unopened driver during reset
From: Sasha Levin @ 2019-06-27  0:29 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Sasha Levin, netdev, Thomas Falcon, linuxppc-dev,
	David S . Miller
In-Reply-To: <20190627003021.19867-1-sashal@kernel.org>

From: Thomas Falcon <tlfalcon@linux.ibm.com>

[ Upstream commit 1f94608b0ce141be5286dde31270590bdf35b86a ]

Check driver state before halting it during a reset. If the driver is
not running, do nothing. Otherwise, a request to deactivate a down link
can cause an error and the reset will fail.

Signed-off-by: Thomas Falcon <tlfalcon@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/net/ethernet/ibm/ibmvnic.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 3dfb2d131eb7..71bf895409a1 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -1751,7 +1751,8 @@ static int do_reset(struct ibmvnic_adapter *adapter,
 
 	ibmvnic_cleanup(netdev);
 
-	if (adapter->reset_reason != VNIC_RESET_MOBILITY &&
+	if (reset_state == VNIC_OPEN &&
+	    adapter->reset_reason != VNIC_RESET_MOBILITY &&
 	    adapter->reset_reason != VNIC_RESET_FAILOVER) {
 		rc = __ibmvnic_close(netdev);
 		if (rc)
-- 
2.20.1


^ permalink raw reply related

* Re: Bisected regression in v5.1 on PowerBook G3 (Wallstreet)
From: Finn Thain @ 2019-06-27  0:25 UTC (permalink / raw)
  To: Christophe Leroy; +Cc: linuxppc-dev, Stan
In-Reply-To: <03df9f3c-003a-b4ae-6f76-ba8a20742b25@c-s.fr>

On Wed, 26 Jun 2019, Christophe Leroy wrote:

> Hi Finn,
> 
> On 06/26/2019 02:06 AM, Finn Thain wrote:
> > Hi Christophe,
> > 
> > I received a report of a regression between v5.0 and v5.1 which causes 
> > the current release to crash during boot with a machine check 
> > exception. Please see console log below.
> > 
> > Stan (whom I've Cc'd) tells me that this happens on every attempt to 
> > boot. I asked him to try 'git bisect'. The results are given below. 
> > Can you see anything in commit 93c4a162b014 that might explain this?
> 
> Might be a false positive. That commit has a problem, but that problem 
> is fixed by 4622a2d43101 ("powerpc/6xx: fix setup and use of 
> SPRN_SPRG_PGDIR for hash32")
> 
> I would bet your problem is related to commit f7354ccac844 ("powerpc/32: 
> Remove CURRENT_THREAD_INFO and rename TI_CPU"). That problem is fixed by 
> commit 397d2300b08c ("powerpc/32s: fix flush_hash_pages() on SMP") 
> upstream, and in linux 5.1.4 by commit fda49aec2515 on 
> stable/linux-5.1.y
> 

I see. I've just discovered that this issue has already been covered on 
this list. I should have done a bit more research.

> Can you test ?
> 

Stan did some more tests and confirmed that the problem has been fixed in 
397d2300b08c and stable/linux-5.1.y.

Thanks.

-- 

> Thanks
> Christophe
> 

^ permalink raw reply

* Re: [PATCH 3/4] powerpc/powernv: remove unused NPU DMA code
From: Alexey Kardashevskiy @ 2019-06-27  0:21 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-kernel, Oliver O'Halloran, Frederic Barrat,
	Paul Mackerras, linuxppc-dev
In-Reply-To: <20190626074935.GA25452@lst.de>



On 26/06/2019 17:49, Christoph Hellwig wrote:
> On Wed, Jun 26, 2019 at 10:44:38AM +1000, Alexey Kardashevskiy wrote:
>>
>>
>> On 26/06/2019 00:52, Christoph Hellwig wrote:
>>> None of these routines were ever used anywhere in the kernel tree
>>> since they were added to the kernel.
>>
>>
>> So none of my comments has been addressed. Nice.
> 
> Which comment?  Last time I asked you complaint "it is still used in
> exactly the same way as before" which you later clarified that you
> have a hidden out of tree user somewhere, and you only objected to

It is not hidden, anyone can download and inspect that GPL driver.

> the word "dead".  That has been fixed and there were no further
> comments.

You still have it in the cover letter so at very least 3/4 is not a part
of this patchset then.

And I still want to see a formal statement about out-of-tree drivers
support/tolerance. If you manage to remove this code, I'll have to post
a revert (again and again) but I would rather know the exact list of
what we do and what we do not do about such drivers and if the list 1)
exists 2) is reasonable then I could try to come up with a better
solution or point others to the policy and push them to do the right
thing. Right now it is just you pretending that the nVidia driver does
not exist, this is not helping. Thanks,


-- 
Alexey

^ permalink raw reply

* Re: [PATCH] powerpc/64s/radix: Define arch_ioremap_p4d_supported()
From: Stephen Rothwell @ 2019-06-27  0:06 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: Aneesh Kumar K.V, linux-kernel, Nicholas Piggin, linux-mm,
	linux-next, Paul Mackerras, Andrew Morton, linuxppc-dev
In-Reply-To: <1561555260-17335-1-git-send-email-anshuman.khandual@arm.com>

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

Hi Anshuman,

On Wed, 26 Jun 2019 18:51:00 +0530 Anshuman Khandual <anshuman.khandual@arm.com> wrote:
>
> Recent core ioremap changes require HAVE_ARCH_HUGE_VMAP subscribing archs
> provide arch_ioremap_p4d_supported() failing which will result in a build
> failure like the following.
> 
> ld: lib/ioremap.o: in function `.ioremap_huge_init':
> ioremap.c:(.init.text+0x3c): undefined reference to
> `.arch_ioremap_p4d_supported'
> 
> This defines a stub implementation for arch_ioremap_p4d_supported() keeping
> it disabled for now to fix the build problem.
> 
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
> Cc: Nicholas Piggin <npiggin@gmail.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Stephen Rothwell <sfr@canb.auug.org.au>
> Cc: linuxppc-dev@lists.ozlabs.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-next@vger.kernel.org
> 
> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
> ---
> This has been just build tested and fixes the problem reported earlier.
> 
>  arch/powerpc/mm/book3s64/radix_pgtable.c | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c
> index 8904aa1..c81da88 100644
> --- a/arch/powerpc/mm/book3s64/radix_pgtable.c
> +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
> @@ -1124,6 +1124,11 @@ void radix__ptep_modify_prot_commit(struct vm_area_struct *vma,
>  	set_pte_at(mm, addr, ptep, pte);
>  }
>  
> +int __init arch_ioremap_p4d_supported(void)
> +{
> +	return 0;
> +}
> +
>  int __init arch_ioremap_pud_supported(void)
>  {
>  	/* HPT does not cope with large pages in the vmalloc area */
> -- 
> 2.7.4
> 

I will add that as a merge resolution patch for the akpm-current tree
merge today.

-- 
Cheers,
Stephen Rothwell

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH] powerpc/rtas: Fix hang in race against concurrent cpu offline
From: Nathan Lynch @ 2019-06-26 23:51 UTC (permalink / raw)
  To: Juliet Kim; +Cc: linuxppc-dev, mmc, mwb
In-Reply-To: <c99fb125-ba6d-66aa-d963-83e854bc0eb7@linux.vnet.ibm.com>

Hi Juliet,

Juliet Kim <julietk@linux.vnet.ibm.com> writes:
> On 6/25/19 12:29 PM, Nathan Lynch wrote:
>> Juliet Kim <julietk@linux.vnet.ibm.com> writes:
>>>
>>> However, that fix failed to notify Hypervisor that the LPM attempted
>>> had been abandoned which results in a system hang.
>
>> It is surprising to me that leaving a migration unterminated would cause
>> Linux to hang. Can you explain more about how that happens?
>>
> PHYP will block further requests(next partition migration, dlpar etc) while
> it's in suspending state. That would have a follow-on effect on the HMC and
> potentially this and other partitions.

I can believe that operations on _this LPAR_ would be blocked by the
platform and/or management console while the migration remains
unterminated, but the OS should not be able to perpetrate a denial of
service on other partitions or the management console simply by botching
the LPM protocol. If it can, that's not Linux's bug to fix.


>>> Fix this by sending a signal PHYP to cancel the migration, so that PHYP
>>> can stop waiting, and clean up the migration.
>>
>> This is well-spotted and rtas_ibm_suspend_me() needs to signal
>> cancellation in several error paths. But I don't agree that this is one
>> of them: this race is going to be a temporary condition in any
>> production setting, and retrying would allow the migration to
>> succeed.
>
> If LPM and CPU offine requests conflict with one another, it might be better
> to let them fail and let the customer decide which he prefers.

Hmm I don't think so. When (if ever) this happens in production it would
be the result of an unlucky race with a power management daemon or
similar, not a conscious decision of the administrator in the moment.


> IBM i cancels migration if the other OS components/operations veto
> migration. It’s consistent with other OS behavior for LPM.

But this situation isn't really like that. If we were to have a real
veto mechanism, it would only make sense to have it run as early as
possible, before the platform has done a bunch of work. This benign,
recoverable race is occurring right before we complete the migration,
which at this point has been copying state to the destination for
minutes or hours. It doesn't make sense to error out like this.

As I mentioned earlier though, it does make sense to signal a
cancellation for these less-recoverable error conditions in
rtas_ibm_suspend_me():

- rtas_online_cpus_mask() failure
- alloc_cpumask_var() failure
- the atomic_read(&data.error) != 0 case after returning from the IPI


^ permalink raw reply

* Re: [PATCH] powerpc/pseries: Fix maximum memory value
From: Nathan Lynch @ 2019-06-26 22:36 UTC (permalink / raw)
  To: Aravinda Prasad; +Cc: naveen.n.rao, linuxppc-dev
In-Reply-To: <156154180258.26182.5331913391297025368.stgit@aravinda>

Aravinda Prasad <aravinda@linux.vnet.ibm.com> writes:
> Calculating the maximum memory based on the number of lmbs
> and lmb size does not account for the RMA region. Hence
> use drmem_lmb_memory_max(), which already accounts for the
> RMA region, to fetch the maximum memory value.
>
> Fixes: 772b039fd9a7: ("powerpc/pseries: Export maximum memory value")
> Signed-off-by: Aravinda Prasad <aravinda@linux.vnet.ibm.com>
> ---
>  arch/powerpc/platforms/pseries/lparcfg.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
> index e33e8bc..f425842 100644
> --- a/arch/powerpc/platforms/pseries/lparcfg.c
> +++ b/arch/powerpc/platforms/pseries/lparcfg.c
> @@ -435,7 +435,7 @@ static void maxmem_data(struct seq_file *m)
>  {
>  	unsigned long maxmem = 0;
>  
> -	maxmem += drmem_info->n_lmbs * drmem_info->lmb_size;
> +	maxmem += drmem_lmb_memory_max();

Would memory_hotplug_max() be better here? There's no guarantee an LPAR
will have the device tree node/properties that populate drmem.

^ permalink raw reply

* Re: [PATCH] powerpc/rtas: Fix hang in race against concurrent cpu offline
From: Juliet Kim @ 2019-06-26 21:49 UTC (permalink / raw)
  To: Nathan Lynch; +Cc: linuxppc-dev, mmc, mwb
In-Reply-To: <87a7e5tvyb.fsf@linux.ibm.com>

On 6/25/19 12:29 PM, Nathan Lynch wrote:
> Juliet Kim <julietk@linux.vnet.ibm.com> writes:
>> The commit
>> (“powerpc/rtas: Fix a potential race between CPU-Offline & Migration)
>> attempted to fix a hang in Live Partition Mobility(LPM) by abandoning
>> the LPM attempt if a race between LPM and concurrent CPU offline was
>> detected.
>>
>> However, that fix failed to notify Hypervisor that the LPM attempted
>> had been abandoned which results in a system hang.
> It is surprising to me that leaving a migration unterminated would cause
> Linux to hang. Can you explain more about how that happens?
>
PHYP will block further requests(next partition migration, dlpar etc) while
it's in suspending state. That would have a follow-on effect on the HMC and
potentially this and other partitions.
>> Fix this by sending a signal PHYP to cancel the migration, so that PHYP
>> can stop waiting, and clean up the migration.
> This is well-spotted and rtas_ibm_suspend_me() needs to signal
> cancellation in several error paths. But I don't agree that this is one
> of them: this race is going to be a temporary condition in any
> production setting, and retrying would allow the migration to succeed.
If LPM and CPU offine requests conflict with one another, it might be better
to let them fail and let the customer decide which he prefers. IBM i cancels
migration if the other OS components/operations veto migration. It’s consistent
with other OS behavior for LPM. I think all the IBM products should have a
consistent customer experience. Even if the race can be temporary, it still
could happen and can cause livelock.

^ permalink raw reply

* Re: [PATCH] powerpc/rtas: retry when cpu offline races with suspend/migration
From: Juliet Kim @ 2019-06-26 21:40 UTC (permalink / raw)
  To: Nathan Lynch; +Cc: ego, mmc, linuxppc-dev
In-Reply-To: <877e99ts5f.fsf@linux.ibm.com>


On 6/25/19 1:51 PM, Nathan Lynch wrote:
> Juliet Kim <julietk@linux.vnet.ibm.com> writes:
>
>> There's some concern this could retry forever, resulting in live lock.
> First of all the system will make progress in other areas even if there
> are repeated retries; we're not indefinitely holding locks or anything
> like that.

For instance, system admin runs a script that picks and offlines CPUs in a
loop to keep a certain rate of onlined CPUs for energy saving. If LPM keeps
putting CPUs back online, that would never finish, and would keepgenerating
new offline requests

> Second, Linux checks the H_VASI_STATE result on every retry. If the
> platform wants to terminate the migration (say, if it imposes a
> timeout), Linux will abandon it when H_VASI_STATE fails to return
> H_VASI_SUSPENDING. And it seems incorrect to bail out before that
> happens, absent hard errors on the Linux side such as allocation
> failures.
I confirmed with the PHYP and HMC folks that they wouldn't time out the LPM
request including H_VASI_STATE, so if the LPM retries were unlucky enough to
encounter repeated CPU offline attempts (maybe some customer code retrying
that), then the retries could continue indefinitely, or until some manual
intervention.  And in the mean time, the LPM delay here would cause PHYP to
block other operations.


^ permalink raw reply

* [PATCH] recordmcount: Fix spurious mcount entries on powerpc
From: Naveen N. Rao @ 2019-06-26 18:38 UTC (permalink / raw)
  To: Michael Ellerman, Steven Rostedt, Christophe Leroy
  Cc: linuxppc-dev, linux-kernel

The recent change enabling HAVE_C_RECORDMCOUNT on powerpc started
showing the following issue:

  # modprobe kprobe_example
   ftrace-powerpc: Not expected bl: opcode is 3c4c0001
   WARNING: CPU: 0 PID: 227 at kernel/trace/ftrace.c:2001 ftrace_bug+0x90/0x318
   Modules linked in:
   CPU: 0 PID: 227 Comm: modprobe Not tainted 5.2.0-rc6-00678-g1c329100b942 #2
   NIP:  c000000000264318 LR: c00000000025d694 CTR: c000000000f5cd30
   REGS: c000000001f2b7b0 TRAP: 0700   Not tainted  (5.2.0-rc6-00678-g1c329100b942)
   MSR:  900000010282b033 <SF,HV,VEC,VSX,EE,FP,ME,IR,DR,RI,LE,TM[E]>  CR: 28228222  XER: 00000000
   CFAR: c0000000002642fc IRQMASK: 0
   <snip>
   NIP [c000000000264318] ftrace_bug+0x90/0x318
   LR [c00000000025d694] ftrace_process_locs+0x4f4/0x5e0
   Call Trace:
   [c000000001f2ba40] [0000000000000004] 0x4 (unreliable)
   [c000000001f2bad0] [c00000000025d694] ftrace_process_locs+0x4f4/0x5e0
   [c000000001f2bb90] [c00000000020ff10] load_module+0x25b0/0x30c0
   [c000000001f2bd00] [c000000000210cb0] sys_finit_module+0xc0/0x130
   [c000000001f2be20] [c00000000000bda4] system_call+0x5c/0x70
   Instruction dump:
   419e0018 2f83ffff 419e00bc 2f83ffea 409e00cc 4800001c 0fe00000 3c62ff96
   39000001 39400000 386386d0 480000c4 <0fe00000> 3ce20003 39000001 3c62ff96
   ---[ end trace 4c438d5cebf78381 ]---
   ftrace failed to modify
   [<c0080000012a0008>] 0xc0080000012a0008
    actual:   01:00:4c:3c
   Initializing ftrace call sites
   ftrace record flags: 2000000
    (0)
    expected tramp: c00000000006af4c

Looking at the relocation records in __mcount_loc showed a few spurious
entries:
  RELOCATION RECORDS FOR [__mcount_loc]:
  OFFSET           TYPE              VALUE
  0000000000000000 R_PPC64_ADDR64    .text.unlikely+0x0000000000000008
  0000000000000008 R_PPC64_ADDR64    .text.unlikely+0x0000000000000014
  0000000000000010 R_PPC64_ADDR64    .text.unlikely+0x0000000000000060
  0000000000000018 R_PPC64_ADDR64    .text.unlikely+0x00000000000000b4
  0000000000000020 R_PPC64_ADDR64    .init.text+0x0000000000000008
  0000000000000028 R_PPC64_ADDR64    .init.text+0x0000000000000014

The first entry in each section is incorrect. Looking at the relocation
records, the spurious entries correspond to the R_PPC64_ENTRY records:
  RELOCATION RECORDS FOR [.text.unlikely]:
  OFFSET           TYPE              VALUE
  0000000000000000 R_PPC64_REL64     .TOC.-0x0000000000000008
  0000000000000008 R_PPC64_ENTRY     *ABS*
  0000000000000014 R_PPC64_REL24     _mcount
  <snip>

The problem is that we are not validating the return value from
get_mcountsym() in sift_rel_mcount(). With this entry, mcountsym is 0,
but Elf_r_sym(relp) also ends up being 0. Fix this by ensuring mcountsym
is valid before processing the entry.

Fixes: c7d64b560ce80 ("powerpc/ftrace: Enable C Version of recordmcount")
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
 scripts/recordmcount.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h
index 13c5e6c8829c..47fca2c69a73 100644
--- a/scripts/recordmcount.h
+++ b/scripts/recordmcount.h
@@ -325,7 +325,8 @@ static uint_t *sift_rel_mcount(uint_t *mlocp,
 		if (!mcountsym)
 			mcountsym = get_mcountsym(sym0, relp, str0);
 
-		if (mcountsym == Elf_r_sym(relp) && !is_fake_mcount(relp)) {
+		if (mcountsym && mcountsym == Elf_r_sym(relp) &&
+				!is_fake_mcount(relp)) {
 			uint_t const addend =
 				_w(_w(relp->r_offset) - recval + mcount_adjust);
 			mrelp->r_offset = _w(offbase
-- 
2.22.0


^ permalink raw reply related

* Re: [PATCHv2 2/2] PCI: layerscape: EP and RC drivers are compiled separately
From: Bjorn Helgaas @ 2019-06-26 17:51 UTC (permalink / raw)
  To: Xiaowei Bao
  Cc: mark.rutland, roy.zang, lorenzo.pieralisi, arnd, devicetree,
	gregkh, kstewart, linuxppc-dev, linux-pci, linux-kernel, kishon,
	minghuan.Lian, robh+dt, linux-arm-kernel, pombredanne, leoyang.li,
	shawnguo, shawn.lin, mingkai.hu
In-Reply-To: <20190626111139.32878-2-xiaowei.bao@nxp.com>

If you post another revision for any reason, please change the subject
so it's worded as a command and mentions the new config options, e.g.,

  PCI: layerscape: Add CONFIG_PCI_LAYERSCAPE_EP to build EP/RC separately

On Wed, Jun 26, 2019 at 07:11:39PM +0800, Xiaowei Bao wrote:
> Compile the EP and RC drivers separately with different configuration
> options, this looks clearer.
> 
> Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com>
> ---
> v2:
>  - No change.
> 
>  drivers/pci/controller/dwc/Kconfig  |   20 ++++++++++++++++++--
>  drivers/pci/controller/dwc/Makefile |    3 ++-
>  2 files changed, 20 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
> index a6ce1ee..a41ccf5 100644
> --- a/drivers/pci/controller/dwc/Kconfig
> +++ b/drivers/pci/controller/dwc/Kconfig
> @@ -131,13 +131,29 @@ config PCI_KEYSTONE_EP
>  	  DesignWare core functions to implement the driver.
>  
>  config PCI_LAYERSCAPE
> -	bool "Freescale Layerscape PCIe controller"
> +	bool "Freescale Layerscape PCIe controller - Host mode"
>  	depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST)
>  	depends on PCI_MSI_IRQ_DOMAIN
>  	select MFD_SYSCON
>  	select PCIE_DW_HOST
>  	help
> -	  Say Y here if you want PCIe controller support on Layerscape SoCs.
> +	  Say Y here if you want to enable PCIe controller support on Layerscape
> +	  SoCs to work in Host mode.
> +	  This controller can work either as EP or RC. The RCW[HOST_AGT_PEX]
> +	  determines which PCIe controller works in EP mode and which PCIe
> +	  controller works in RC mode.
> +
> +config PCI_LAYERSCAPE_EP
> +	bool "Freescale Layerscape PCIe controller - Endpoint mode"
> +	depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST)
> +	depends on PCI_ENDPOINT
> +	select PCIE_DW_EP
> +	help
> +	  Say Y here if you want to enable PCIe controller support on Layerscape
> +	  SoCs to work in Endpoint mode.
> +	  This controller can work either as EP or RC. The RCW[HOST_AGT_PEX]
> +	  determines which PCIe controller works in EP mode and which PCIe
> +	  controller works in RC mode.
>  
>  config PCI_HISI
>  	depends on OF && (ARM64 || COMPILE_TEST)
> diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
> index b085dfd..824fde7 100644
> --- a/drivers/pci/controller/dwc/Makefile
> +++ b/drivers/pci/controller/dwc/Makefile
> @@ -8,7 +8,8 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
>  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
>  obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
>  obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
> -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
> +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> +obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o
>  obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
>  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
>  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
> -- 
> 1.7.1
> 

^ permalink raw reply

* Re: [PATCH v3 3/3] powerpc/papr_scm: Force a scm-unbind if initial scm-bind fails
From: Aneesh Kumar K.V @ 2019-06-26 16:58 UTC (permalink / raw)
  To: Vaibhav Jain, linuxppc-dev
  Cc: Laurent Dufour, Vaibhav Jain, David Gibson, Oliver O'Halloran
In-Reply-To: <20190626140404.27448-4-vaibhav@linux.ibm.com>

Vaibhav Jain <vaibhav@linux.ibm.com> writes:

> In some cases initial bind of scm memory for an lpar can fail if
> previously it wasn't released using a scm-unbind hcall. This situation
> can arise due to panic of the previous kernel or forced lpar
> fadump. In such cases the H_SCM_BIND_MEM return a H_OVERLAP error.
>
> To mitigate such cases the patch updates papr_scm_probe() to force a
> call to drc_pmem_unbind() in case the initial bind of scm memory fails
> with EBUSY error. In case scm-bind operation again fails after the
> forced scm-unbind then we follow the existing error path. We also
> update drc_pmem_bind() to handle the H_OVERLAP error returned by phyp
> and indicate it as a EBUSY error back to the caller.
>
> Suggested-by: "Oliver O'Halloran" <oohall@gmail.com>
> Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
> Reviewed-by: Oliver O'Halloran <oohall@gmail.com>
> ---
> Change-log:
> v3:
> * Minor update to a code comment. [Oliver]
>
> v2:
> * Moved the retry code from drc_pmem_bind() to papr_scm_probe()
>   [Oliver]
> * Changed the type of variable 'rc' in drc_pmem_bind() to
>   int64_t. [Oliver]
> ---
>  arch/powerpc/platforms/pseries/papr_scm.c | 15 ++++++++++++++-
>  1 file changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
> index c01a03fd3ee7..7c5e10c063a0 100644
> --- a/arch/powerpc/platforms/pseries/papr_scm.c
> +++ b/arch/powerpc/platforms/pseries/papr_scm.c
> @@ -43,8 +43,9 @@ struct papr_scm_priv {
>  static int drc_pmem_bind(struct papr_scm_priv *p)
>  {
>  	unsigned long ret[PLPAR_HCALL_BUFSIZE];
> -	uint64_t rc, token;
>  	uint64_t saved = 0;
> +	uint64_t token;
> +	int64_t rc;
>  
>  	/*
>  	 * When the hypervisor cannot map all the requested memory in a single
> @@ -64,6 +65,10 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
>  	} while (rc == H_BUSY);
>  
>  	if (rc) {
> +		/* H_OVERLAP needs a separate error path */
> +		if (rc == H_OVERLAP)
> +			return -EBUSY;
> +
>  		dev_err(&p->pdev->dev, "bind err: %lld\n", rc);
>  		return -ENXIO;
>  	}
> @@ -331,6 +336,14 @@ static int papr_scm_probe(struct platform_device *pdev)
>  
>  	/* request the hypervisor to bind this region to somewhere in memory */
>  	rc = drc_pmem_bind(p);
> +
> +	/* If phyp says drc memory still bound then force unbound and retry */
> +	if (rc == -EBUSY) {
> +		dev_warn(&pdev->dev, "Retrying bind after unbinding\n");
> +		drc_pmem_unbind(p);


This should only be caused by kexec right? And considering kernel nor
hypervisor won't change device binding details, can you check switching
this to H_SCM_QUERY_BLOCK_MEM_BINDING?  Will that result in faster boot? 



> +		rc = drc_pmem_bind(p);
> +	}
> +
>  	if (rc)
>  		goto err;
>  

I am also not sure about the module reference count here. Should we
increment the module reference count after a bind so that we can track
failures in ubind and fail the module unload?

-aneesh


^ permalink raw reply

* Re: [PATCH v3 1/3] powerpc/pseries: Update SCM hcall op-codes in hvcall.h
From: Aneesh Kumar K.V @ 2019-06-26 16:53 UTC (permalink / raw)
  To: Vaibhav Jain, linuxppc-dev
  Cc: Laurent Dufour, Vaibhav Jain, David Gibson, Oliver O'Halloran
In-Reply-To: <20190626140404.27448-2-vaibhav@linux.ibm.com>

Vaibhav Jain <vaibhav@linux.ibm.com> writes:

> Update the hvcalls.h to include op-codes for new hcalls introduce to
> manage SCM memory. Also update existing hcall definitions to reflect
> current papr specification for SCM.
>
> Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>


Why split this as a separate patch? You should fold this to the next one
where we actually use the constant.

> ---
> Change-log:
>
> v3:
> * Added updated opcode for H_SCM_HEALTH [Oliver]
>
> v2:
> * None new patch in this series.
> ---
>  arch/powerpc/include/asm/hvcall.h | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
> index 463c63a9fcf1..11112023e327 100644
> --- a/arch/powerpc/include/asm/hvcall.h
> +++ b/arch/powerpc/include/asm/hvcall.h
> @@ -302,9 +302,14 @@
>  #define H_SCM_UNBIND_MEM        0x3F0
>  #define H_SCM_QUERY_BLOCK_MEM_BINDING 0x3F4
>  #define H_SCM_QUERY_LOGICAL_MEM_BINDING 0x3F8
> -#define H_SCM_MEM_QUERY	        0x3FC
> -#define H_SCM_BLOCK_CLEAR       0x400
> -#define MAX_HCALL_OPCODE	H_SCM_BLOCK_CLEAR
> +#define H_SCM_UNBIND_ALL        0x3FC
> +#define H_SCM_HEALTH            0x400
> +#define H_SCM_PERFORMANCE_STATS 0x418
> +#define MAX_HCALL_OPCODE	H_SCM_PERFORMANCE_STATS
> +
> +/* Scope args for H_SCM_UNBIND_ALL */
> +#define H_UNBIND_SCOPE_ALL (0x1)
> +#define H_UNBIND_SCOPE_DRC (0x2)
>  
>  /* H_VIOCTL functions */
>  #define H_GET_VIOA_DUMP_SIZE	0x01
> -- 
> 2.21.0


^ permalink raw reply

* Re: [PATCH RFC] generic ELF support for kexec
From: Christophe Leroy @ 2019-06-26 16:09 UTC (permalink / raw)
  To: Sven Schnelle, kexec; +Cc: linux-s390, deller, linuxppc-dev
In-Reply-To: <20190625185433.GA10934@t470p.stackframe.org>

Hi Sven,

On 06/25/2019 06:54 PM, Sven Schnelle wrote:
> Hi List,
> 
> i recently started working on kexec for PA-RISC. While doing so, i figured
> that powerpc already has support for reading ELF images inside of the Kernel.
> My first attempt was to steal the source code and modify it for PA-RISC, but
> it turned out that i didn't had to change much. Only ARM specific stuff like
> fdt blob fetching had to be removed.
> 
> So instead of duplicating the code, i thought about moving the ELF stuff to
> the core kexec code, and exposing several function to use that code from the
> arch specific code.
> 
> I'm attaching the patch to this Mail. What do you think about that change?
> s390 also uses ELF files, and (maybe?) could also switch to this implementation.
> But i don't know anything about S/390 and don't have one in my basement. So
> i'll leave s390 to the IBM folks.
> 
> I haven't really tested PowerPC yet. Can anyone give me a helping hand what
> would be a good target to test this code in QEMU? Or even better, test this
> code on real Hardware?

Where did you start from ? Your patch doesn't apply on latest 
powerpc/merge branch 
(https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git):

[root@localhost linux-powerpc]# git am -3 
/root/Downloads/RFC-generic-ELF-support-for-kexec.patch
Applying: generic ELF support for kexec
Using index info to reconstruct a base tree...
M	arch/powerpc/kernel/kexec_elf_64.c
M	kernel/Makefile
Falling back to patching base and 3-way merge...
Auto-merging kernel/Makefile
Auto-merging arch/powerpc/kernel/kexec_elf_64.c
CONFLICT (content): Merge conflict in arch/powerpc/kernel/kexec_elf_64.c
error: Failed to merge in the changes.
Patch failed at 0001 generic ELF support for kexec

Neither does it apply on 5.2-rc6

Looks like it cleanly applies on 5.1

Could you generate your patch using 'git format-patch -M -C ' ? It would 
be a lot easier to see the real changes:

  arch/Kconfig                                       |   3 +
  arch/powerpc/Kconfig                               |   1 +
  arch/powerpc/kernel/kexec_elf_64.c                 | 547 
+--------------------
  include/linux/kexec.h                              |  35 ++
  kernel/Makefile                                    |   1 +
  .../kexec_elf_64.c => kernel/kexec_file_elf.c      | 192 ++------
  6 files changed, 96 insertions(+), 683 deletions(-)
  copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_file_elf.c (77%)

Thanks
Christophe

> 
> If that change is acceptable i would finish the patch and submit it. I think
> best would be to push this change through Helge's parisc tree, so we don't
> have any dependencies to sort out.
> 
> Regards,
> Sven
> 
> [PATCH] kexec: add generic support for elf kernel images
> 
> Signed-off-by: Sven Schnelle <svens@stackframe.org>
> ---
>   arch/Kconfig                       |   3 +
>   arch/powerpc/Kconfig               |   1 +
>   arch/powerpc/kernel/kexec_elf_64.c | 547 +--------------------------
>   include/linux/kexec.h              |  35 ++
>   kernel/Makefile                    |   1 +
>   kernel/kexec_file_elf.c            | 574 +++++++++++++++++++++++++++++
>   6 files changed, 619 insertions(+), 542 deletions(-)
>   create mode 100644 kernel/kexec_file_elf.c
> 
> diff --git a/arch/Kconfig b/arch/Kconfig
> index c47b328eada0..de7520100136 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
>   	select CRASH_CORE
>   	bool
>   
> +config KEXEC_FILE_ELF
> +	bool
> +
>   config HAVE_IMA_KEXEC
>   	bool
>   
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 8c1c636308c8..48241260b6ae 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -502,6 +502,7 @@ config KEXEC_FILE
>   	select KEXEC_CORE
>   	select HAVE_IMA_KEXEC
>   	select BUILD_BIN2C
> +	select KEXEC_FILE_ELF
>   	depends on PPC64
>   	depends on CRYPTO=y
>   	depends on CRYPTO_SHA256=y
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..0059e36913e9 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -21,8 +21,6 @@
>    * GNU General Public License for more details.
>    */
>   
> -#define pr_fmt(fmt)	"kexec_elf: " fmt
> -
>   #include <linux/elf.h>
>   #include <linux/kexec.h>
>   #include <linux/libfdt.h>
> @@ -31,540 +29,6 @@
>   #include <linux/slab.h>
>   #include <linux/types.h>
>   
> -#define PURGATORY_STACK_SIZE	(16 * 1024)
> -
> -#define elf_addr_to_cpu	elf64_to_cpu
> -
> -#ifndef Elf_Rel
> -#define Elf_Rel		Elf64_Rel
> -#endif /* Elf_Rel */
> -
> -struct elf_info {
> -	/*
> -	 * Where the ELF binary contents are kept.
> -	 * Memory managed by the user of the struct.
> -	 */
> -	const char *buffer;
> -
> -	const struct elfhdr *ehdr;
> -	const struct elf_phdr *proghdrs;
> -	struct elf_shdr *sechdrs;
> -};
> -
> -static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> -{
> -       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> -}
> -
> -static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le64_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be64_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le16_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be16_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le32_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be32_to_cpu(value);
> -
> -	return value;
> -}
> -
> -/**
> - * elf_is_ehdr_sane - check that it is safe to use the ELF header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> -{
> -	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> -		pr_debug("Bad program header size.\n");
> -		return false;
> -	} else if (ehdr->e_shnum > 0 &&
> -		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> -		pr_debug("Bad section header size.\n");
> -		return false;
> -	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> -		   ehdr->e_version != EV_CURRENT) {
> -		pr_debug("Unknown ELF version.\n");
> -		return false;
> -	}
> -
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		size_t phdr_size;
> -
> -		/*
> -		 * e_phnum is at most 65535 so calculating the size of the
> -		 * program header cannot overflow.
> -		 */
> -		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -		/* Sanity check the program header table location. */
> -		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> -			pr_debug("Program headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> -			pr_debug("Program headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		size_t shdr_size;
> -
> -		/*
> -		 * e_shnum is at most 65536 so calculating
> -		 * the size of the section header cannot overflow.
> -		 */
> -		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> -
> -		/* Sanity check the section header table location. */
> -		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> -			pr_debug("Section headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> -			pr_debug("Section headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> -{
> -	struct elfhdr *buf_ehdr;
> -
> -	if (len < sizeof(*buf_ehdr)) {
> -		pr_debug("Buffer is too small to hold ELF header.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	memset(ehdr, 0, sizeof(*ehdr));
> -	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> -	if (!elf_is_elf_file(ehdr)) {
> -		pr_debug("No ELF header magic.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> -		pr_debug("Not a supported ELF class.\n");
> -		return -ENOEXEC;
> -	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> -		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> -		pr_debug("Not a supported ELF data format.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	buf_ehdr = (struct elfhdr *) buf;
> -	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> -		pr_debug("Bad ELF header size.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> -	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> -	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> -	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> -	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> -	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> -	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> -	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> -	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> -	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> -	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> -	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> -
> -	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_is_phdr_sane - check that it is safe to use the program header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> -{
> -
> -	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> -		pr_debug("ELF segment location wraps around.\n");
> -		return false;
> -	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> -		pr_debug("ELF segment not in file.\n");
> -		return false;
> -	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> -		pr_debug("ELF segment address wraps around.\n");
> -		return false;
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	/* Override the const in proghdrs, we are the ones doing the loading. */
> -	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> -	const char *pbuf;
> -	struct elf_phdr *buf_phdr;
> -
> -	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> -	buf_phdr = (struct elf_phdr *) pbuf;
> -
> -	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> -	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> -	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> -	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> -	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> -	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> -	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> -
> -	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_phdrs - read the program headers from the buffer
> - *
> - * This function assumes that the program header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_phdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t phdr_size, i;
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -
> -	/*
> -	 * e_phnum is at most 65535 so calculating the size of the
> -	 * program header cannot overflow.
> -	 */
> -	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> -	if (!elf_info->proghdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_phdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			elf_info->proghdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_is_shdr_sane - check that it is safe to use the section header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> -{
> -	bool size_ok;
> -
> -	/* SHT_NULL headers have undefined values, so we can't check them. */
> -	if (shdr->sh_type == SHT_NULL)
> -		return true;
> -
> -	/* Now verify sh_entsize */
> -	switch (shdr->sh_type) {
> -	case SHT_SYMTAB:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> -		break;
> -	case SHT_RELA:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> -		break;
> -	case SHT_DYNAMIC:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> -		break;
> -	case SHT_REL:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> -		break;
> -	case SHT_NOTE:
> -	case SHT_PROGBITS:
> -	case SHT_HASH:
> -	case SHT_NOBITS:
> -	default:
> -		/*
> -		 * This is a section whose entsize requirements
> -		 * I don't care about.  If I don't know about
> -		 * the section I can't care about it's entsize
> -		 * requirements.
> -		 */
> -		size_ok = true;
> -		break;
> -	}
> -
> -	if (!size_ok) {
> -		pr_debug("ELF section with wrong entry size.\n");
> -		return false;
> -	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> -		pr_debug("ELF section address wraps around.\n");
> -		return false;
> -	}
> -
> -	if (shdr->sh_type != SHT_NOBITS) {
> -		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> -			pr_debug("ELF section location wraps around.\n");
> -			return false;
> -		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> -			pr_debug("ELF section not in file.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -	const char *sbuf;
> -	struct elf_shdr *buf_shdr;
> -
> -	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> -	buf_shdr = (struct elf_shdr *) sbuf;
> -
> -	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> -	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> -	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> -	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> -	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> -	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> -	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> -	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> -	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> -
> -	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_shdrs - read the section headers from the buffer
> - *
> - * This function assumes that the section header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_shdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t shdr_size, i;
> -
> -	/*
> -	 * e_shnum is at most 65536 so calculating
> -	 * the size of the section header cannot overflow.
> -	 */
> -	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> -
> -	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> -	if (!elf_info->sechdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_shdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->sechdrs);
> -			elf_info->sechdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> - * @buf:	Buffer to read ELF file from.
> - * @len:	Size of @buf.
> - * @ehdr:	Pointer to existing struct which will be populated.
> - * @elf_info:	Pointer to existing struct which will be populated.
> - *
> - * This function allows reading ELF files with different byte order than
> - * the kernel, byte-swapping the fields as needed.
> - *
> - * Return:
> - * On success returns 0, and the caller should call elf_free_info(elf_info) to
> - * free the memory allocated for the section and program headers.
> - */
> -int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info)
> -{
> -	int ret;
> -
> -	ret = elf_read_ehdr(buf, len, ehdr);
> -	if (ret)
> -		return ret;
> -
> -	elf_info->buffer = buf;
> -	elf_info->ehdr = ehdr;
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		ret = elf_read_phdrs(buf, len, elf_info);
> -		if (ret)
> -			return ret;
> -	}
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		ret = elf_read_shdrs(buf, len, elf_info);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_free_info - free memory allocated by elf_read_from_buffer
> - */
> -void elf_free_info(struct elf_info *elf_info)
> -{
> -	kfree(elf_info->proghdrs);
> -	kfree(elf_info->sechdrs);
> -	memset(elf_info, 0, sizeof(*elf_info));
> -}
> -/**
> - * build_elf_exec_info - read ELF executable and check that we can use it
> - */
> -static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
> -			       struct elf_info *elf_info)
> -{
> -	int i;
> -	int ret;
> -
> -	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> -	if (ret)
> -		return ret;
> -
> -	/* Big endian vmlinux has type ET_DYN. */
> -	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> -		pr_err("Not an ELF executable.\n");
> -		goto error;
> -	} else if (!elf_info->proghdrs) {
> -		pr_err("No ELF program header.\n");
> -		goto error;
> -	}
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		/*
> -		 * Kexec does not support loading interpreters.
> -		 * In addition this check keeps us from attempting
> -		 * to kexec ordinay executables.
> -		 */
> -		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> -			pr_err("Requires an ELF interpreter.\n");
> -			goto error;
> -		}
> -	}
> -
> -	return 0;
> -error:
> -	elf_free_info(elf_info);
> -	return -ENOEXEC;
> -}
> -
> -static int elf64_probe(const char *buf, unsigned long len)
> -{
> -	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> -	int ret;
> -
> -	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
> -	if (ret)
> -		return ret;
> -
> -	elf_free_info(&elf_info);
> -
> -	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_exec_load - load ELF executable image
> - * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> - *			section will be loaded in memory.
> - *
> - * Return:
> - * 0 on success, negative value on failure.
> - */
> -static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info,
> -			 unsigned long *lowest_load_addr)
> -{
> -	unsigned long base = 0, lowest_addr = UINT_MAX;
> -	int ret;
> -	size_t i;
> -	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
> -				  .top_down = false };
> -
> -	/* Read in the PT_LOAD segments. */
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		unsigned long load_addr;
> -		size_t size;
> -		const struct elf_phdr *phdr;
> -
> -		phdr = &elf_info->proghdrs[i];
> -		if (phdr->p_type != PT_LOAD)
> -			continue;
> -
> -		size = phdr->p_filesz;
> -		if (size > phdr->p_memsz)
> -			size = phdr->p_memsz;
> -
> -		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
> -		kbuf.bufsz = size;
> -		kbuf.memsz = phdr->p_memsz;
> -		kbuf.buf_align = phdr->p_align;
> -		kbuf.buf_min = phdr->p_paddr + base;
> -		ret = kexec_add_buffer(&kbuf);
> -		if (ret)
> -			goto out;
> -		load_addr = kbuf.mem;
> -
> -		if (load_addr < lowest_addr)
> -			lowest_addr = load_addr;
> -	}
> -
> -	/* Update entry point to reflect new load address. */
> -	ehdr->e_entry += base;
> -
> -	*lowest_load_addr = lowest_addr;
> -	ret = 0;
> - out:
> -	return ret;
> -}
> -
>   static void *elf64_load(struct kimage *image, char *kernel_buf,
>   			unsigned long kernel_len, char *initrd,
>   			unsigned long initrd_len, char *cmdline,
> @@ -577,17 +41,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   	void *fdt;
>   	const void *slave_code;
>   	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> +	struct kexec_elf_info elf_info;
>   	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
>   				  .buf_max = ppc64_rma_size };
>   	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
>   				  .buf_max = ppc64_rma_size, .top_down = true };
>   
> -	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> +	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
>   	if (ret)
>   		goto out;
>   
> -	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> +	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
>   	if (ret)
>   		goto out;
>   
> @@ -652,13 +116,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   		pr_err("Error setting up the purgatory.\n");
>   
>   out:
> -	elf_free_info(&elf_info);
> -
> +	kexec_free_elf_info(&elf_info);
>   	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
>   	return ret ? ERR_PTR(ret) : fdt;
>   }
>   
>   const struct kexec_file_ops kexec_elf64_ops = {
> -	.probe = elf64_probe,
> +	.probe = kexec_elf_probe,
>   	.load = elf64_load,
>   };
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..49b23b425f84 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
>   				       void **addr, unsigned long *sz);
>   #endif /* CONFIG_KEXEC_FILE */
>   
> +#ifdef CONFIG_KEXEC_FILE_ELF
> +
> +struct kexec_elf_info {
> +	/*
> +	 * Where the ELF binary contents are kept.
> +	 * Memory managed by the user of the struct.
> +	 */
> +	const char *buffer;
> +
> +	const struct elfhdr *ehdr;
> +	const struct elf_phdr *proghdrs;
> +	struct elf_shdr *sechdrs;
> +};
> +
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> +			  char *kernel_buf, unsigned long kernel_len,
> +			  unsigned long *kernel_load_addr);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +#endif
>   struct kimage {
>   	kimage_entry_t head;
>   	kimage_entry_t *entry;
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 33824f0385b3..fdba91785977 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
>   obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
>   obj-$(CONFIG_KEXEC) += kexec.o
>   obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
> +obj-$(CONFIG_KEXEC_FILE_ELF) += kexec_file_elf.o
>   obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
>   obj-$(CONFIG_COMPAT) += compat.o
>   obj-$(CONFIG_CGROUPS) += cgroup/
> diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
> new file mode 100644
> index 000000000000..bb966c93492c
> --- /dev/null
> +++ b/kernel/kexec_file_elf.c
> @@ -0,0 +1,574 @@
> +/*
> + * Load ELF vmlinux file for the kexec_file_load syscall.
> + *
> + * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
> + * Copyright (C) 2004  IBM Corp.
> + * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
> + * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
> + * Copyright (C) 2016  IBM Corporation
> + *
> + * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
> + * Heavily modified for the kernel by
> + * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation (version 2 of the License).
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt)	"kexec_elf: " fmt
> +
> +#include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/libfdt.h>
> +#include <linux/module.h>
> +#include <linux/of_fdt.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#define elf_addr_to_cpu	elf64_to_cpu
> +
> +#ifndef Elf_Rel
> +#define Elf_Rel		Elf64_Rel
> +#endif /* Elf_Rel */
> +
> +static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> +{
> +       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> +}
> +
> +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le64_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be64_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le16_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be16_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le32_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be32_to_cpu(value);
> +
> +	return value;
> +}
> +
> +/**
> + * elf_is_ehdr_sane - check that it is safe to use the ELF header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> +{
> +	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> +		pr_debug("Bad program header size.\n");
> +		return false;
> +	} else if (ehdr->e_shnum > 0 &&
> +		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> +		pr_debug("Bad section header size.\n");
> +		return false;
> +	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> +		   ehdr->e_version != EV_CURRENT) {
> +		pr_debug("Unknown ELF version.\n");
> +		return false;
> +	}
> +
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		size_t phdr_size;
> +
> +		/*
> +		 * e_phnum is at most 65535 so calculating the size of the
> +		 * program header cannot overflow.
> +		 */
> +		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +		/* Sanity check the program header table location. */
> +		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> +			pr_debug("Program headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> +			pr_debug("Program headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		size_t shdr_size;
> +
> +		/*
> +		 * e_shnum is at most 65536 so calculating
> +		 * the size of the section header cannot overflow.
> +		 */
> +		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> +
> +		/* Sanity check the section header table location. */
> +		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> +			pr_debug("Section headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> +			pr_debug("Section headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> +{
> +	struct elfhdr *buf_ehdr;
> +
> +	if (len < sizeof(*buf_ehdr)) {
> +		pr_debug("Buffer is too small to hold ELF header.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	memset(ehdr, 0, sizeof(*ehdr));
> +	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> +	if (!elf_is_elf_file(ehdr)) {
> +		pr_debug("No ELF header magic.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> +		pr_debug("Not a supported ELF class.\n");
> +		return -ENOEXEC;
> +	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> +		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> +		pr_debug("Not a supported ELF data format.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	buf_ehdr = (struct elfhdr *) buf;
> +	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> +		pr_debug("Bad ELF header size.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> +	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> +	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> +	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> +	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> +	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> +	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> +	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> +	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> +	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> +	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> +	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> +
> +	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_is_phdr_sane - check that it is safe to use the program header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> +{
> +
> +	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> +		pr_debug("ELF segment location wraps around.\n");
> +		return false;
> +	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> +		pr_debug("ELF segment not in file.\n");
> +		return false;
> +	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> +		pr_debug("ELF segment address wraps around.\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	/* Override the const in proghdrs, we are the ones doing the loading. */
> +	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> +	const char *pbuf;
> +	struct elf_phdr *buf_phdr;
> +
> +	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> +	buf_phdr = (struct elf_phdr *) pbuf;
> +
> +	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> +	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> +	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> +	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> +	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> +	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> +	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> +
> +	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_phdrs - read the program headers from the buffer
> + *
> + * This function assumes that the program header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_phdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t phdr_size, i;
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +
> +	/*
> +	 * e_phnum is at most 65535 so calculating the size of the
> +	 * program header cannot overflow.
> +	 */
> +	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> +	if (!elf_info->proghdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_phdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			elf_info->proghdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_is_shdr_sane - check that it is safe to use the section header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> +{
> +	bool size_ok;
> +
> +	/* SHT_NULL headers have undefined values, so we can't check them. */
> +	if (shdr->sh_type == SHT_NULL)
> +		return true;
> +
> +	/* Now verify sh_entsize */
> +	switch (shdr->sh_type) {
> +	case SHT_SYMTAB:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> +		break;
> +	case SHT_RELA:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> +		break;
> +	case SHT_DYNAMIC:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> +		break;
> +	case SHT_REL:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> +		break;
> +	case SHT_NOTE:
> +	case SHT_PROGBITS:
> +	case SHT_HASH:
> +	case SHT_NOBITS:
> +	default:
> +		/*
> +		 * This is a section whose entsize requirements
> +		 * I don't care about.  If I don't know about
> +		 * the section I can't care about it's entsize
> +		 * requirements.
> +		 */
> +		size_ok = true;
> +		break;
> +	}
> +
> +	if (!size_ok) {
> +		pr_debug("ELF section with wrong entry size.\n");
> +		return false;
> +	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> +		pr_debug("ELF section address wraps around.\n");
> +		return false;
> +	}
> +
> +	if (shdr->sh_type != SHT_NOBITS) {
> +		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> +			pr_debug("ELF section location wraps around.\n");
> +			return false;
> +		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> +			pr_debug("ELF section not in file.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +	const char *sbuf;
> +	struct elf_shdr *buf_shdr;
> +
> +	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> +	buf_shdr = (struct elf_shdr *) sbuf;
> +
> +	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> +	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> +	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> +	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> +	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> +	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> +	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> +	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> +	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> +
> +	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_shdrs - read the section headers from the buffer
> + *
> + * This function assumes that the section header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_shdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t shdr_size, i;
> +
> +	/*
> +	 * e_shnum is at most 65536 so calculating
> +	 * the size of the section header cannot overflow.
> +	 */
> +	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> +
> +	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> +	if (!elf_info->sechdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_shdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->sechdrs);
> +			elf_info->sechdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> + * @buf:	Buffer to read ELF file from.
> + * @len:	Size of @buf.
> + * @ehdr:	Pointer to existing struct which will be populated.
> + * @elf_info:	Pointer to existing struct which will be populated.
> + *
> + * This function allows reading ELF files with different byte order than
> + * the kernel, byte-swapping the fields as needed.
> + *
> + * Return:
> + * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
> + * free the memory allocated for the section and program headers.
> + */
> +int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info)
> +{
> +	int ret;
> +
> +	ret = elf_read_ehdr(buf, len, ehdr);
> +	if (ret)
> +		return ret;
> +
> +	elf_info->buffer = buf;
> +	elf_info->ehdr = ehdr;
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		ret = elf_read_phdrs(buf, len, elf_info);
> +		if (ret)
> +			return ret;
> +	}
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		ret = elf_read_shdrs(buf, len, elf_info);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> + */
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> +{
> +	kfree(elf_info->proghdrs);
> +	kfree(elf_info->sechdrs);
> +	memset(elf_info, 0, sizeof(*elf_info));
> +}
> +EXPORT_SYMBOL(kexec_free_elf_info);
> +
> +/**
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> + */
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	int i;
> +	int ret;
> +
> +	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> +	if (ret)
> +		return ret;
> +
> +	/* Big endian vmlinux has type ET_DYN. */
> +	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> +		pr_err("Not an ELF executable.\n");
> +		goto error;
> +	} else if (!elf_info->proghdrs) {
> +		pr_err("No ELF program header.\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		/*
> +		 * Kexec does not support loading interpreters.
> +		 * In addition this check keeps us from attempting
> +		 * to kexec ordinay executables.
> +		 */
> +		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> +			pr_err("Requires an ELF interpreter.\n");
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +error:
> +	kexec_free_elf_info(elf_info);
> +	return -ENOEXEC;
> +}
> +EXPORT_SYMBOL(kexec_build_elf_info);
> +
> +/**
> + * elf_exec_load - load ELF executable image
> + * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> + *			section will be loaded in memory.
> + *
> + * Return:
> + * 0 on success, negative value on failure.
> + */
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +		   struct kexec_elf_info *elf_info,
> +		   struct kexec_buf *kbuf,
> +		   unsigned long *lowest_load_addr)
> +{
> +	unsigned long lowest_addr = UINT_MAX;
> +	int ret;
> +	size_t i;
> +	/* Read in the PT_LOAD segments. */
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		unsigned long load_addr;
> +		size_t size;
> +		const struct elf_phdr *phdr;
> +
> +		phdr = &elf_info->proghdrs[i];
> +		if (phdr->p_type != PT_LOAD)
> +			continue;
> +
> +		size = phdr->p_filesz;
> +		if (size > phdr->p_memsz)
> +			size = phdr->p_memsz;
> +
> +		kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
> +		kbuf->bufsz = size;
> +		kbuf->memsz = phdr->p_memsz;
> +		kbuf->buf_align = phdr->p_align;
> +		kbuf->buf_min = phdr->p_paddr;
> +		kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
> +		ret = kexec_add_buffer(kbuf);
> +		if (ret)
> +			goto out;
> +		load_addr = kbuf->mem;
> +
> +		if (load_addr < lowest_addr)
> +			lowest_addr = load_addr;
> +	}
> +
> +	image->start = ehdr->e_entry;
> +	*lowest_load_addr = lowest_addr;
> +	ret = 0;
> + out:
> +	return ret;
> +}
> +EXPORT_SYMBOL(kexec_elf_load);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> +			char *kernel_buf, unsigned long kernel_len,
> +			unsigned long *kernel_load_addr)
> +{
> +	int ret;
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +
> +	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> +	if (ret)
> +		goto out;
> +
> +	ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
> +out:
> +	kexec_free_elf_info(&elf_info);
> +	return ret;
> +
> +}
> +EXPORT_SYMBOL(kexec_elf_kernel_load);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +	int ret;
> +
> +	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> +	if (ret)
> +		return ret;
> +
> +	kexec_free_elf_info(&elf_info);
> +
> +	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> +}
> +EXPORT_SYMBOL(kexec_elf_probe);
> 



^ permalink raw reply

* Re: DMA coherency in drivers/tty/serial/mpsc.c
From: Mark Greer @ 2019-06-26 16:09 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Greg Kroah-Hartman, linux-kernel, Mark Greer, Paul Gortmaker,
	linux-serial, Dale Farnsworth, linuxppc-dev
In-Reply-To: <20190626064837.GA24531@lst.de>

On Wed, Jun 26, 2019 at 08:48:37AM +0200, Christoph Hellwig wrote:
> On Tue, Jun 25, 2019 at 09:37:22AM -0700, Mark Greer wrote:
> > Yeah, the mpsc driver had lots of ugly cache related hacks because of
> > cache coherency bugs in the early version of the MV64x60 bridge chips
> > that it was embedded in.  That chip is pretty much dead now and I've
> > removed core support for it from the powerpc tree.  Removing the mpsc
> > driver is on my todo list but I've been busy and lazy.  So, to sum it
> > up, don't spend any more time worrying about it as it should be removed.
> > 
> > I'll post a patch to do that tonight and I'm sorry for any time you've
> > spent looking at it so far.
> 
> No problem.  And if future such broken chips show up we now have
> support for per-device DMA coherency settings and could actually
> handle it in a reaѕonably clean way.

Ah, good to know - thanks.

BTW, I just submitted a patch to remove the driver.

Mark
--

^ permalink raw reply

* Re: [PATCH v3 2/3] powerpc/papr_scm: Update drc_pmem_unbind() to use H_SCM_UNBIND_ALL
From: Aneesh Kumar K.V @ 2019-06-26 15:41 UTC (permalink / raw)
  To: Vaibhav Jain, linuxppc-dev
  Cc: Laurent Dufour, Oliver O'Halloran, David Gibson
In-Reply-To: <20190626140404.27448-3-vaibhav@linux.ibm.com>

On 6/26/19 7:34 PM, Vaibhav Jain wrote:
> The new hcall named H_SCM_UNBIND_ALL has been introduce that can
> unbind all or specific scm memory assigned to an lpar. This is
> more efficient than using H_SCM_UNBIND_MEM as currently we don't
> support partial unbind of scm memory.
> 
> Hence this patch proposes following changes to drc_pmem_unbind():
> 
>      * Update drc_pmem_unbind() to replace hcall H_SCM_UNBIND_MEM to
>        H_SCM_UNBIND_ALL.
> 
>      * Update drc_pmem_unbind() to handles cases when PHYP asks the guest
>        kernel to wait for specific amount of time before retrying the
>        hcall via the 'LONG_BUSY' return value.
> 
>      * Ensure appropriate error code is returned back from the function
>        in case of an error.
> 
> Reviewed-by: Oliver O'Halloran <oohall@gmail.com>
> Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
> ---
> Change-log:
> 
> v3:
> * Fixed a build warning reported by kbuild-robot.
> * Updated patch description to put emphasis on 'scm memory' instead of
>    'scm drc memory blocks' as for phyp there is a stark difference
>    between how drc are managed for scm memory v/s regular memory. [Oliver]
> 
> v2:
> * Added a dev_dbg when unbind operation succeeds [Oliver]
> * Changed type of variable 'rc' to int64_t [Oliver]
> * Removed the code that was logging a warning in case bind operation
>    takes >1-seconds [Oliver]
> * Spinned off changes to hvcall.h as a separate patch. [Oliver]
> ---
>   arch/powerpc/platforms/pseries/papr_scm.c | 29 +++++++++++++++++------
>   1 file changed, 22 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
> index 96c53b23e58f..c01a03fd3ee7 100644
> --- a/arch/powerpc/platforms/pseries/papr_scm.c
> +++ b/arch/powerpc/platforms/pseries/papr_scm.c
> @@ -11,6 +11,7 @@
>   #include <linux/sched.h>
>   #include <linux/libnvdimm.h>
>   #include <linux/platform_device.h>
> +#include <linux/delay.h>
>   
>   #include <asm/plpar_wrappers.h>
>   
> @@ -77,22 +78,36 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
>   static int drc_pmem_unbind(struct papr_scm_priv *p)
>   {
>   	unsigned long ret[PLPAR_HCALL_BUFSIZE];
> -	uint64_t rc, token;
> +	uint64_t token = 0;
> +	int64_t rc;
>   
> -	token = 0;
> +	dev_dbg(&p->pdev->dev, "unbind drc %x\n", p->drc_index);
>   
> -	/* NB: unbind has the same retry requirements mentioned above */
> +	/* NB: unbind has the same retry requirements as drc_pmem_bind() */
>   	do {
> -		rc = plpar_hcall(H_SCM_UNBIND_MEM, ret, p->drc_index,
> -				p->bound_addr, p->blocks, token);
> +
> +		/* Unbind of all SCM resources associated with drcIndex */
> +		rc = plpar_hcall(H_SCM_UNBIND_ALL, ret, H_UNBIND_SCOPE_DRC,
> +				 p->drc_index, token);
>   		token = ret[0];
> -		cond_resched();
> +
> +		/* Check if we are stalled for some time */
> +		if (H_IS_LONG_BUSY(rc)) {
> +			msleep(get_longbusy_msecs(rc));
> +			rc = H_BUSY;
> +		} else if (rc == H_BUSY) {
> +			cond_resched();
> +		}
> +
>   	} while (rc == H_BUSY);
>   
>   	if (rc)
>   		dev_err(&p->pdev->dev, "unbind error: %lld\n", rc);
> +	else
> +		dev_dbg(&p->pdev->dev, "unbind drc %x complete\n",
> +			p->drc_index);
>   
Can we add p->drc_index as part of these messages? Also s/%x/0x%x ?


> -	return !!rc;
> +	return rc == H_SUCCESS ? 0 : -ENXIO;
>   }
>   
The error -ENXIO is confusing. Can we keep the HCALL error as return for 
this? We don't check error from this. If we can't take any action based 
on the return. Then may be make it  void?


>   static int papr_scm_meta_get(struct papr_scm_priv *p,
> 


-aneesh


^ permalink raw reply

* [PATCH v3 3/3] powerpc/papr_scm: Force a scm-unbind if initial scm-bind fails
From: Vaibhav Jain @ 2019-06-26 14:04 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Aneesh Kumar K . V, Oliver O'Halloran, Vaibhav Jain,
	Laurent Dufour, David Gibson
In-Reply-To: <20190626140404.27448-1-vaibhav@linux.ibm.com>

In some cases initial bind of scm memory for an lpar can fail if
previously it wasn't released using a scm-unbind hcall. This situation
can arise due to panic of the previous kernel or forced lpar
fadump. In such cases the H_SCM_BIND_MEM return a H_OVERLAP error.

To mitigate such cases the patch updates papr_scm_probe() to force a
call to drc_pmem_unbind() in case the initial bind of scm memory fails
with EBUSY error. In case scm-bind operation again fails after the
forced scm-unbind then we follow the existing error path. We also
update drc_pmem_bind() to handle the H_OVERLAP error returned by phyp
and indicate it as a EBUSY error back to the caller.

Suggested-by: "Oliver O'Halloran" <oohall@gmail.com>
Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
Reviewed-by: Oliver O'Halloran <oohall@gmail.com>
---
Change-log:
v3:
* Minor update to a code comment. [Oliver]

v2:
* Moved the retry code from drc_pmem_bind() to papr_scm_probe()
  [Oliver]
* Changed the type of variable 'rc' in drc_pmem_bind() to
  int64_t. [Oliver]
---
 arch/powerpc/platforms/pseries/papr_scm.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index c01a03fd3ee7..7c5e10c063a0 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -43,8 +43,9 @@ struct papr_scm_priv {
 static int drc_pmem_bind(struct papr_scm_priv *p)
 {
 	unsigned long ret[PLPAR_HCALL_BUFSIZE];
-	uint64_t rc, token;
 	uint64_t saved = 0;
+	uint64_t token;
+	int64_t rc;
 
 	/*
 	 * When the hypervisor cannot map all the requested memory in a single
@@ -64,6 +65,10 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
 	} while (rc == H_BUSY);
 
 	if (rc) {
+		/* H_OVERLAP needs a separate error path */
+		if (rc == H_OVERLAP)
+			return -EBUSY;
+
 		dev_err(&p->pdev->dev, "bind err: %lld\n", rc);
 		return -ENXIO;
 	}
@@ -331,6 +336,14 @@ static int papr_scm_probe(struct platform_device *pdev)
 
 	/* request the hypervisor to bind this region to somewhere in memory */
 	rc = drc_pmem_bind(p);
+
+	/* If phyp says drc memory still bound then force unbound and retry */
+	if (rc == -EBUSY) {
+		dev_warn(&pdev->dev, "Retrying bind after unbinding\n");
+		drc_pmem_unbind(p);
+		rc = drc_pmem_bind(p);
+	}
+
 	if (rc)
 		goto err;
 
-- 
2.21.0


^ permalink raw reply related

* [PATCH v3 1/3] powerpc/pseries: Update SCM hcall op-codes in hvcall.h
From: Vaibhav Jain @ 2019-06-26 14:04 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Aneesh Kumar K . V, Oliver O'Halloran, Vaibhav Jain,
	Laurent Dufour, David Gibson
In-Reply-To: <20190626140404.27448-1-vaibhav@linux.ibm.com>

Update the hvcalls.h to include op-codes for new hcalls introduce to
manage SCM memory. Also update existing hcall definitions to reflect
current papr specification for SCM.

Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
Change-log:

v3:
* Added updated opcode for H_SCM_HEALTH [Oliver]

v2:
* None new patch in this series.
---
 arch/powerpc/include/asm/hvcall.h | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 463c63a9fcf1..11112023e327 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -302,9 +302,14 @@
 #define H_SCM_UNBIND_MEM        0x3F0
 #define H_SCM_QUERY_BLOCK_MEM_BINDING 0x3F4
 #define H_SCM_QUERY_LOGICAL_MEM_BINDING 0x3F8
-#define H_SCM_MEM_QUERY	        0x3FC
-#define H_SCM_BLOCK_CLEAR       0x400
-#define MAX_HCALL_OPCODE	H_SCM_BLOCK_CLEAR
+#define H_SCM_UNBIND_ALL        0x3FC
+#define H_SCM_HEALTH            0x400
+#define H_SCM_PERFORMANCE_STATS 0x418
+#define MAX_HCALL_OPCODE	H_SCM_PERFORMANCE_STATS
+
+/* Scope args for H_SCM_UNBIND_ALL */
+#define H_UNBIND_SCOPE_ALL (0x1)
+#define H_UNBIND_SCOPE_DRC (0x2)
 
 /* H_VIOCTL functions */
 #define H_GET_VIOA_DUMP_SIZE	0x01
-- 
2.21.0


^ permalink raw reply related

* [PATCH v3 2/3] powerpc/papr_scm: Update drc_pmem_unbind() to use H_SCM_UNBIND_ALL
From: Vaibhav Jain @ 2019-06-26 14:04 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Aneesh Kumar K . V, Oliver O'Halloran, Vaibhav Jain,
	Laurent Dufour, David Gibson
In-Reply-To: <20190626140404.27448-1-vaibhav@linux.ibm.com>

The new hcall named H_SCM_UNBIND_ALL has been introduce that can
unbind all or specific scm memory assigned to an lpar. This is
more efficient than using H_SCM_UNBIND_MEM as currently we don't
support partial unbind of scm memory.

Hence this patch proposes following changes to drc_pmem_unbind():

    * Update drc_pmem_unbind() to replace hcall H_SCM_UNBIND_MEM to
      H_SCM_UNBIND_ALL.

    * Update drc_pmem_unbind() to handles cases when PHYP asks the guest
      kernel to wait for specific amount of time before retrying the
      hcall via the 'LONG_BUSY' return value.

    * Ensure appropriate error code is returned back from the function
      in case of an error.

Reviewed-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com>
---
Change-log:

v3:
* Fixed a build warning reported by kbuild-robot.
* Updated patch description to put emphasis on 'scm memory' instead of
  'scm drc memory blocks' as for phyp there is a stark difference
  between how drc are managed for scm memory v/s regular memory. [Oliver]

v2:
* Added a dev_dbg when unbind operation succeeds [Oliver]
* Changed type of variable 'rc' to int64_t [Oliver]
* Removed the code that was logging a warning in case bind operation
  takes >1-seconds [Oliver]
* Spinned off changes to hvcall.h as a separate patch. [Oliver]
---
 arch/powerpc/platforms/pseries/papr_scm.c | 29 +++++++++++++++++------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/papr_scm.c b/arch/powerpc/platforms/pseries/papr_scm.c
index 96c53b23e58f..c01a03fd3ee7 100644
--- a/arch/powerpc/platforms/pseries/papr_scm.c
+++ b/arch/powerpc/platforms/pseries/papr_scm.c
@@ -11,6 +11,7 @@
 #include <linux/sched.h>
 #include <linux/libnvdimm.h>
 #include <linux/platform_device.h>
+#include <linux/delay.h>
 
 #include <asm/plpar_wrappers.h>
 
@@ -77,22 +78,36 @@ static int drc_pmem_bind(struct papr_scm_priv *p)
 static int drc_pmem_unbind(struct papr_scm_priv *p)
 {
 	unsigned long ret[PLPAR_HCALL_BUFSIZE];
-	uint64_t rc, token;
+	uint64_t token = 0;
+	int64_t rc;
 
-	token = 0;
+	dev_dbg(&p->pdev->dev, "unbind drc %x\n", p->drc_index);
 
-	/* NB: unbind has the same retry requirements mentioned above */
+	/* NB: unbind has the same retry requirements as drc_pmem_bind() */
 	do {
-		rc = plpar_hcall(H_SCM_UNBIND_MEM, ret, p->drc_index,
-				p->bound_addr, p->blocks, token);
+
+		/* Unbind of all SCM resources associated with drcIndex */
+		rc = plpar_hcall(H_SCM_UNBIND_ALL, ret, H_UNBIND_SCOPE_DRC,
+				 p->drc_index, token);
 		token = ret[0];
-		cond_resched();
+
+		/* Check if we are stalled for some time */
+		if (H_IS_LONG_BUSY(rc)) {
+			msleep(get_longbusy_msecs(rc));
+			rc = H_BUSY;
+		} else if (rc == H_BUSY) {
+			cond_resched();
+		}
+
 	} while (rc == H_BUSY);
 
 	if (rc)
 		dev_err(&p->pdev->dev, "unbind error: %lld\n", rc);
+	else
+		dev_dbg(&p->pdev->dev, "unbind drc %x complete\n",
+			p->drc_index);
 
-	return !!rc;
+	return rc == H_SUCCESS ? 0 : -ENXIO;
 }
 
 static int papr_scm_meta_get(struct papr_scm_priv *p,
-- 
2.21.0


^ permalink raw reply related

* [PATCH v3 0/3] powerpc/papr_scm: Workaround for failure of drc bind after kexec
From: Vaibhav Jain @ 2019-06-26 14:04 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: Aneesh Kumar K . V, Oliver O'Halloran, Vaibhav Jain,
	Laurent Dufour, David Gibson

Presently an error is returned in response to hcall H_SCM_BIND_MEM when a
new kernel boots on lpar via kexec. This prevents papr_scm from registering
drc memory regions with nvdimm. The error reported is of the form below:

"papr_scm ibm,persistent-memory:ibm,pmemory@44100002: bind err: -68"

On investigation it was revealed that phyp returns this error as previous
kernel did not completely release bindings for drc scm-memory blocks and
hence phyp rejected request for re-binding these block to lpar with error
H_OVERLAP. Also support for a new H_SCM_UNBIND_ALL is recently added which
is better suited for releasing all the bound scm-memory block from an lpar.

So leveraging new hcall H_SCM_UNBIND_ALL, we can workaround H_OVERLAP issue
during kexec by forcing an unbind of all drm scm-memory blocks and issuing
H_SCM_BIND_MEM to re-bind the drc scm-memory blocks to lpar. This sequence
will also be needed when a new kernel boot on lpar after previous kernel
panicked and it never got an opportunity to call H_SCM_UNBIND_MEM/ALL.

Hence this patch-set implements following changes to papr_scm module:

* Update hvcall.h to include opcodes for new hcall H_SCM_UNBIND_ALL.

* Update it to use H_SCM_UNBIND_ALL instead of H_SCM_UNBIND_MEM

* In case hcall H_SCM_BIND_MEM fails with error H_OVERLAP, force
  H_SCM_UNBIND_ALL and retry the bind operation again.

With the patch-set applied re-bind of drc scm-memory to lpar succeeds after
a kexec to new kernel as illustrated below:

# Old kernel
$ sudo ndctl list -R
[
  {
    "dev":"region0",
    <snip>
    ....
  }
]
# kexec to new kernel
$ sudo kexec --initrd=... vmlinux
...
...
I'm in purgatory
...
papr_scm ibm,persistent-memory:ibm,pmemory@44100002: Un-binding and retrying
...
# New kernel
$ sudo ndctl list -R
[
  {
    "dev":"region0",
    <snip>
    ....
  }
]

---
Change-log:
v3:
* Fixed a build warning reported by kbuild test robot.
* Updated the hcall opcode from latest papr-scm specification.
* Fixed a minor code comment & patch description as pointed out by Oliver.

v2:
* Addressed review comments from Oliver on v1 patchset.

Vaibhav Jain (3):
  powerpc/pseries: Update SCM hcall op-codes in hvcall.h
  powerpc/papr_scm: Update drc_pmem_unbind() to use H_SCM_UNBIND_ALL
  powerpc/papr_scm: Force a scm-unbind if initial scm-bind fails

 arch/powerpc/include/asm/hvcall.h         | 11 ++++--
 arch/powerpc/platforms/pseries/papr_scm.c | 44 ++++++++++++++++++-----
 2 files changed, 44 insertions(+), 11 deletions(-)

-- 
2.21.0


^ permalink raw reply

* Re: [PATCH 1/2] x86, numa: always initialize all possible nodes
From: Michal Hocko @ 2019-06-26 13:54 UTC (permalink / raw)
  To: Barret Rhoden
  Cc: Tony Luck, linux-ia64, Dave Hansen, Peter Zijlstra, x86, LKML,
	Pingfan Liu, linux-mm, Ingo Molnar, linuxppc-dev
In-Reply-To: <20190502130031.GC29835@dhcp22.suse.cz>

On Thu 02-05-19 09:00:31, Michal Hocko wrote:
> On Wed 01-05-19 15:12:32, Barret Rhoden wrote:
> [...]
> > A more elegant solution may be to avoid registering with sysfs during early
> > boot, or something else entirely.  But I figured I'd ask for help at this
> > point.  =)
> 
> Thanks for the report and an excellent analysis! This is really helpful.
> I will think about this some more but I am traveling this week. It seems
> really awkward to register a sysfs file for an empty range. That looks
> like a bug to me.

I am sorry, but I didn't get to this for a long time and I am still
busy. The patch has been dropped from the mm tree (thus linux-next). I
hope I can revisit this or somebody else will take over and finish this
work. This is much more trickier than I anticipated unfortunately.

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* [PATCH] powerpc/64s/radix: Define arch_ioremap_p4d_supported()
From: Anshuman Khandual @ 2019-06-26 13:21 UTC (permalink / raw)
  To: linux-mm
  Cc: Stephen Rothwell, Anshuman Khandual, linux-kernel,
	Nicholas Piggin, linux-next, Paul Mackerras, Aneesh Kumar K.V,
	Andrew Morton, linuxppc-dev

Recent core ioremap changes require HAVE_ARCH_HUGE_VMAP subscribing archs
provide arch_ioremap_p4d_supported() failing which will result in a build
failure like the following.

ld: lib/ioremap.o: in function `.ioremap_huge_init':
ioremap.c:(.init.text+0x3c): undefined reference to
`.arch_ioremap_p4d_supported'

This defines a stub implementation for arch_ioremap_p4d_supported() keeping
it disabled for now to fix the build problem.

Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-next@vger.kernel.org

Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
This has been just build tested and fixes the problem reported earlier.

 arch/powerpc/mm/book3s64/radix_pgtable.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c
index 8904aa1..c81da88 100644
--- a/arch/powerpc/mm/book3s64/radix_pgtable.c
+++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
@@ -1124,6 +1124,11 @@ void radix__ptep_modify_prot_commit(struct vm_area_struct *vma,
 	set_pte_at(mm, addr, ptep, pte);
 }
 
+int __init arch_ioremap_p4d_supported(void)
+{
+	return 0;
+}
+
 int __init arch_ioremap_pud_supported(void)
 {
 	/* HPT does not cope with large pages in the vmalloc area */
-- 
2.7.4


^ permalink raw reply related

* [PATCHv2 2/2] PCI: layerscape: EP and RC drivers are compiled separately
From: Xiaowei Bao @ 2019-06-26 11:11 UTC (permalink / raw)
  To: bhelgaas, robh+dt, mark.rutland, shawnguo, leoyang.li, kishon,
	lorenzo.pieralisi, arnd, gregkh, minghuan.Lian, mingkai.hu,
	roy.zang, kstewart, pombredanne, shawn.lin, linux-pci, devicetree,
	linux-kernel, linux-arm-kernel, linuxppc-dev
  Cc: Xiaowei Bao
In-Reply-To: <20190626111139.32878-1-xiaowei.bao@nxp.com>

Compile the EP and RC drivers separately with different configuration
options, this looks clearer.

Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com>
---
v2:
 - No change.

 drivers/pci/controller/dwc/Kconfig  |   20 ++++++++++++++++++--
 drivers/pci/controller/dwc/Makefile |    3 ++-
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index a6ce1ee..a41ccf5 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -131,13 +131,29 @@ config PCI_KEYSTONE_EP
 	  DesignWare core functions to implement the driver.
 
 config PCI_LAYERSCAPE
-	bool "Freescale Layerscape PCIe controller"
+	bool "Freescale Layerscape PCIe controller - Host mode"
 	depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST)
 	depends on PCI_MSI_IRQ_DOMAIN
 	select MFD_SYSCON
 	select PCIE_DW_HOST
 	help
-	  Say Y here if you want PCIe controller support on Layerscape SoCs.
+	  Say Y here if you want to enable PCIe controller support on Layerscape
+	  SoCs to work in Host mode.
+	  This controller can work either as EP or RC. The RCW[HOST_AGT_PEX]
+	  determines which PCIe controller works in EP mode and which PCIe
+	  controller works in RC mode.
+
+config PCI_LAYERSCAPE_EP
+	bool "Freescale Layerscape PCIe controller - Endpoint mode"
+	depends on OF && (ARM || ARCH_LAYERSCAPE || COMPILE_TEST)
+	depends on PCI_ENDPOINT
+	select PCIE_DW_EP
+	help
+	  Say Y here if you want to enable PCIe controller support on Layerscape
+	  SoCs to work in Endpoint mode.
+	  This controller can work either as EP or RC. The RCW[HOST_AGT_PEX]
+	  determines which PCIe controller works in EP mode and which PCIe
+	  controller works in RC mode.
 
 config PCI_HISI
 	depends on OF && (ARM64 || COMPILE_TEST)
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index b085dfd..824fde7 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -8,7 +8,8 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
 obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
-obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o
 obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
-- 
1.7.1


^ 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