* Re: [PATCH v2] powerpc/pseries: explicitly reschedule during drmem_lmb list traversal
From: Tyrel Datwyler @ 2020-08-12 16:22 UTC (permalink / raw)
To: Nathan Lynch, linuxppc-dev; +Cc: cheloha, ldufour
In-Reply-To: <20200812012005.1919255-1-nathanl@linux.ibm.com>
On 8/11/20 6:20 PM, Nathan Lynch wrote:
> The drmem lmb list can have hundreds of thousands of entries, and
> unfortunately lookups take the form of linear searches. As long as
> this is the case, traversals have the potential to monopolize the CPU
> and provoke lockup reports, workqueue stalls, and the like unless
> they explicitly yield.
>
> Rather than placing cond_resched() calls within various
> for_each_drmem_lmb() loop blocks in the code, put it in the iteration
> expression of the loop macro itself so users can't omit it.
>
> Call cond_resched() on every 20th element. Each iteration of the loop
> in DLPAR code paths can involve around ten RTAS calls which can each
> take up to 250us, so this ensures the check is performed at worst
> every few milliseconds.
>
> Fixes: 6c6ea53725b3 ("powerpc/mm: Separate ibm, dynamic-memory data from DT format")
> Signed-off-by: Nathan Lynch <nathanl@linux.ibm.com>
> ---
> arch/powerpc/include/asm/drmem.h | 18 +++++++++++++++++-
> 1 file changed, 17 insertions(+), 1 deletion(-)
>
> Changes since v1:
> * Add bounds assertions in drmem_lmb_next().
> * Call cond_resched() in the iterator on only every 20th element
> instead of on every iteration, to reduce overhead in tight loops.
>
> diff --git a/arch/powerpc/include/asm/drmem.h b/arch/powerpc/include/asm/drmem.h
> index 17ccc6474ab6..583277e30dd2 100644
> --- a/arch/powerpc/include/asm/drmem.h
> +++ b/arch/powerpc/include/asm/drmem.h
> @@ -8,6 +8,9 @@
> #ifndef _ASM_POWERPC_LMB_H
> #define _ASM_POWERPC_LMB_H
>
> +#include <linux/bug.h>
> +#include <linux/sched.h>
> +
> struct drmem_lmb {
> u64 base_addr;
> u32 drc_index;
> @@ -26,8 +29,21 @@ struct drmem_lmb_info {
>
> extern struct drmem_lmb_info *drmem_info;
>
> +static inline struct drmem_lmb *drmem_lmb_next(struct drmem_lmb *lmb)
> +{
> + const unsigned int resched_interval = 20;
> +
> + BUG_ON(lmb < drmem_info->lmbs);
> + BUG_ON(lmb >= drmem_info->lmbs + drmem_info->n_lmbs);
I think BUG_ON is a pretty big no-no these days unless there is no other option.
-Tyrel
> +
> + if ((lmb - drmem_info->lmbs) % resched_interval == 0)
> + cond_resched();
> +
> + return ++lmb;
> +}
> +
> #define for_each_drmem_lmb_in_range(lmb, start, end) \
> - for ((lmb) = (start); (lmb) < (end); (lmb)++)
> + for ((lmb) = (start); (lmb) < (end); lmb = drmem_lmb_next(lmb))
>
> #define for_each_drmem_lmb(lmb) \
> for_each_drmem_lmb_in_range((lmb), \
>
^ permalink raw reply
* Re: [PATCH v3 8/8] mm/vmalloc: Hugepage vmalloc mappings
From: Jonathan Cameron @ 2020-08-12 16:18 UTC (permalink / raw)
To: Nicholas Piggin
Cc: linux-arch, linuxppc-dev, Catalin Marinas, x86, linux-kernel,
linuxarm, linux-mm, Zefan Li, Borislav Petkov, H. Peter Anvin,
Thomas Gleixner, Will Deacon, Ingo Molnar, linux-arm-kernel
In-Reply-To: <20200812132524.000067a6@Huawei.com>
On Wed, 12 Aug 2020 13:25:24 +0100
Jonathan Cameron <Jonathan.Cameron@Huawei.com> wrote:
> On Mon, 10 Aug 2020 12:27:32 +1000
> Nicholas Piggin <npiggin@gmail.com> wrote:
>
> > On platforms that define HAVE_ARCH_HUGE_VMAP and support PMD vmaps,
> > vmalloc will attempt to allocate PMD-sized pages first, before falling
> > back to small pages.
> >
> > Allocations which use something other than PAGE_KERNEL protections are
> > not permitted to use huge pages yet, not all callers expect this (e.g.,
> > module allocations vs strict module rwx).
> >
> > This reduces TLB misses by nearly 30x on a `git diff` workload on a
> > 2-node POWER9 (59,800 -> 2,100) and reduces CPU cycles by 0.54%.
> >
> > This can result in more internal fragmentation and memory overhead for a
> > given allocation, an option nohugevmap is added to disable at boot.
> >
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Hi Nicholas,
>
> Busy afternoon, but a possible point of interest in line in the meantime.
>
I did manage to get back to this.
The issue I think is that ARM64 defines THREAD_ALIGN with CONFIG_VMAP_STACK
to be 2* THREAD SIZE. There is comment in arch/arm64/include/asm/memory.h
that this is to allow cheap checking of overflow.
A quick grep suggests ARM64 is the only architecture to do this...
Jonathan
>
> ...
>
> > @@ -2701,22 +2760,45 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
> > pgprot_t prot, unsigned long vm_flags, int node,
> > const void *caller)
> > {
> > - struct vm_struct *area;
> > + struct vm_struct *area = NULL;
> > void *addr;
> > unsigned long real_size = size;
> > + unsigned long real_align = align;
> > + unsigned int shift = PAGE_SHIFT;
> >
> > size = PAGE_ALIGN(size);
> > if (!size || (size >> PAGE_SHIFT) > totalram_pages())
> > goto fail;
> >
> > - area = __get_vm_area_node(real_size, align, VM_ALLOC | VM_UNINITIALIZED |
> > + if (vmap_allow_huge && (pgprot_val(prot) == pgprot_val(PAGE_KERNEL))) {
> > + unsigned long size_per_node;
> > +
> > + /*
> > + * Try huge pages. Only try for PAGE_KERNEL allocations,
> > + * others like modules don't yet expect huge pages in
> > + * their allocations due to apply_to_page_range not
> > + * supporting them.
> > + */
> > +
> > + size_per_node = size;
> > + if (node == NUMA_NO_NODE)
> > + size_per_node /= num_online_nodes();
> > + if (size_per_node >= PMD_SIZE)
> > + shift = PMD_SHIFT;
> > + }
> > +
> > +again:
> > + align = max(real_align, 1UL << shift);
> > + size = ALIGN(real_size, align);
>
> So my suspicion is that the issue on arm64 is related to this.
> In the relevant call path, align is 32K whilst the size is 16K
>
> Previously I don't think we force size to be a multiple of align.
>
> I think this results in nr_pages being double what it was before.
>
>
> > +
> > + area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED |
> > vm_flags, start, end, node, gfp_mask, caller);
> > if (!area)
> > goto fail;
> >
> > - addr = __vmalloc_area_node(area, gfp_mask, prot, node);
> > + addr = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
> > if (!addr)
> > - return NULL;
> > + goto fail;
> >
> > /*
> > * In this function, newly allocated vm_struct has VM_UNINITIALIZED
> > @@ -2730,8 +2812,16 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
> > return addr;
> >
> > fail:
> > - warn_alloc(gfp_mask, NULL,
> > + if (shift > PAGE_SHIFT) {
> > + shift = PAGE_SHIFT;
> > + goto again;
> > + }
> > +
> > + if (!area) {
> > + /* Warn for area allocation, page allocations already warn */
> > + warn_alloc(gfp_mask, NULL,
> > "vmalloc: allocation failure: %lu bytes", real_size);
> > + }
> > return NULL;
> > }
> >
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2] powerpc/pseries: explicitly reschedule during drmem_lmb list traversal
From: Christophe Leroy @ 2020-08-12 14:26 UTC (permalink / raw)
To: Nathan Lynch, linuxppc-dev; +Cc: tyreld, cheloha, ldufour
In-Reply-To: <878sekyo2l.fsf@linux.ibm.com>
Le 12/08/2020 à 15:46, Nathan Lynch a écrit :
> Hi Christophe,
>
> Christophe Leroy <christophe.leroy@csgroup.eu> writes:
>>> +static inline struct drmem_lmb *drmem_lmb_next(struct drmem_lmb *lmb)
>>> +{
>>> + const unsigned int resched_interval = 20;
>>> +
>>> + BUG_ON(lmb < drmem_info->lmbs);
>>> + BUG_ON(lmb >= drmem_info->lmbs + drmem_info->n_lmbs);
>>
>> BUG_ON() shall be avoided unless absolutely necessary.
>> Wouldn't WARN_ON() together with an early return be enough ?
>
> Not sure what a sensible early return behavior would be. If the iterator
> doesn't advance the pointer the behavior will be a hang.
I was thinking about returning lmb++ without checking the cond_resched
stuff.
>
> BUG_ON a bounds-check failure is appropriate here; many users of this
> API will corrupt memory otherwise.
It looks really strange to me to do bounds-checks in an iterator like
this. Should be checked before entering the loop.
>
>>> +
>>> + if ((lmb - drmem_info->lmbs) % resched_interval == 0)
>>> + cond_resched();
>>
>> Do you need something that precise ? Can't you use 16 or 32 and use a
>> logical AND instead of a MODULO ?
>
> Eh if you're executing in this code you've already lost with respect to
> performance considerations at this level, see the discussion on v1. I'll
> use 16 since I'm going to reroll the patch though.
>
>> And what garanties that lmb is always an element of a table based at
>> drmem_info->lmbs ?
>
> Well, that's its only intended use right now. There should not be any
> other arrays of drmem_lmb objects, and I hope we don't gain any.
>
>
>> What about:
>>
>> static inline struct drmem_lmb *drmem_lmb_next(struct drmem_lmb *lmb,
>> struct drmem_lmb *start)
>> {
>> const unsigned int resched_interval = 16;
>>
>> if ((++lmb - start) & resched_interval == 0)
> ^^^
> Did you mean '%' here? The bitwise AND doesn't do what I want.
I meant '& (resched_interval - 1)' indeed. But yes we can leave the %,
GCC will change a % 16 to an & 15.
>
> Otherwise, making drmem_lmb_next() more general by adding a 'start'
> argument could ease refactoring to come, so I'll do that.
Yes, the main idea is to avoid the access to a global variable in such
an helper.
>
Christophe
^ permalink raw reply
* Re: [PATCH 14/16] debug_vm_pgtable/hugetlb: Disable hugetlb test on ppc64
From: Aneesh Kumar K.V @ 2020-08-12 13:52 UTC (permalink / raw)
To: Anshuman Khandual, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <83766a01-6ffb-798c-c5b9-46b2d672f618@arm.com>
On 8/12/20 7:04 PM, Anshuman Khandual wrote:
>
>
> On 08/12/2020 06:46 PM, Aneesh Kumar K.V wrote:
>> On 8/12/20 6:33 PM, Anshuman Khandual wrote:
>>>
>>>
>>> On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
>>>> The seems to be missing quite a lot of details w.r.t allocating
>>>> the correct pgtable_t page (huge_pte_alloc()), holding the right
>>>> lock (huge_pte_lock()) etc. The vma used is also not a hugetlb VMA.
>>>>
>>>> ppc64 do have runtime checks within CONFIG_DEBUG_VM for most of these.
>>>> Hence disable the test on ppc64.
>>>
>>> This test is free from any platform specific #ifdefs which should
>>> never be broken. If hugetlb_advanced_tests() does not work or is
>>> not detailed enough for ppc64, then it would be great if you could
>>> suggest some improvements so that it works for all enabled platforms.
>>>
>>>
>>
>> As mentioned the test is broken. For hugetlb, the pgtable_t pages should be allocated by huge_pte_alloc(). We need to hold huget_pte_lock() before updating huge tlb pte. That takes hugepage size, which is mostly derived out of vma. Hence vma need to be a hugetlb vma. Some of the functions also depend on hstate. Also we should use set_huge_pte_at() when setting up hugetlb pte entries. I was tempted to remove that test completely marking it broken. But avoided that by marking it broken on only PPC64.
>
> The test is not broken, hugetlb helpers on multiple platforms dont complain about
> this at all. The tests here emulate 'enough' MM objects required for the helpers
> on enabled platforms, to perform the primary task i.e page table transformation it
> is expected to do. The test does not claim to emulate a perfect MM environment for
> a given subsystem's (like HugeTLB) arch helpers. Now in this case, the MM objects
> being emulated for the HugeTLB advanced tests does not seem to be sufficient for
> ppc64 but it can be improved. But that does not mean it is broken in it's current
> form for other platforms.
>
There is nothing ppc64 specific here. It is just that we have
CONFIG_DEBUG_VM based checks for different possibly wrong usages of
these functions. This was done because we have different page sizes, two
different translations to support and we want to avoid any wrong usage.
IMHO expecting hugetlb page table helpers to work with a non hugetlb VMA
and without holding hugeTLB pte lock is a clear violation of hugetlb
interface.
-aneesh
^ permalink raw reply
* Re: [PATCH v2] powerpc/pseries: explicitly reschedule during drmem_lmb list traversal
From: Nathan Lynch @ 2020-08-12 13:46 UTC (permalink / raw)
To: Christophe Leroy, linuxppc-dev; +Cc: tyreld, cheloha, ldufour
In-Reply-To: <c39976ce-a95c-a9cb-d119-d272b8de2f28@csgroup.eu>
Hi Christophe,
Christophe Leroy <christophe.leroy@csgroup.eu> writes:
>> +static inline struct drmem_lmb *drmem_lmb_next(struct drmem_lmb *lmb)
>> +{
>> + const unsigned int resched_interval = 20;
>> +
>> + BUG_ON(lmb < drmem_info->lmbs);
>> + BUG_ON(lmb >= drmem_info->lmbs + drmem_info->n_lmbs);
>
> BUG_ON() shall be avoided unless absolutely necessary.
> Wouldn't WARN_ON() together with an early return be enough ?
Not sure what a sensible early return behavior would be. If the iterator
doesn't advance the pointer the behavior will be a hang.
BUG_ON a bounds-check failure is appropriate here; many users of this
API will corrupt memory otherwise.
>> +
>> + if ((lmb - drmem_info->lmbs) % resched_interval == 0)
>> + cond_resched();
>
> Do you need something that precise ? Can't you use 16 or 32 and use a
> logical AND instead of a MODULO ?
Eh if you're executing in this code you've already lost with respect to
performance considerations at this level, see the discussion on v1. I'll
use 16 since I'm going to reroll the patch though.
> And what garanties that lmb is always an element of a table based at
> drmem_info->lmbs ?
Well, that's its only intended use right now. There should not be any
other arrays of drmem_lmb objects, and I hope we don't gain any.
> What about:
>
> static inline struct drmem_lmb *drmem_lmb_next(struct drmem_lmb *lmb,
> struct drmem_lmb *start)
> {
> const unsigned int resched_interval = 16;
>
> if ((++lmb - start) & resched_interval == 0)
^^^
Did you mean '%' here? The bitwise AND doesn't do what I want.
Otherwise, making drmem_lmb_next() more general by adding a 'start'
argument could ease refactoring to come, so I'll do that.
^ permalink raw reply
* Re: [PATCH v2] powerpc/pseries/hotplug-cpu: wait indefinitely for vCPU death
From: Greg Kurz @ 2020-08-12 13:36 UTC (permalink / raw)
To: Michael Roth
Cc: Nathan Lynch, linuxppc-dev, Cedric Le Goater,
Thiago Jung Bauermann
In-Reply-To: <20200811161544.10513-1-mdroth@linux.vnet.ibm.com>
On Tue, 11 Aug 2020 11:15:44 -0500
Michael Roth <mdroth@linux.vnet.ibm.com> wrote:
> For a power9 KVM guest with XIVE enabled, running a test loop
> where we hotplug 384 vcpus and then unplug them, the following traces
> can be seen (generally within a few loops) either from the unplugged
> vcpu:
>
> [ 1767.353447] cpu 65 (hwid 65) Ready to die...
> [ 1767.952096] Querying DEAD? cpu 66 (66) shows 2
> [ 1767.952311] list_del corruption. next->prev should be c00a000002470208, but was c00a000002470048
> [ 1767.952322] ------------[ cut here ]------------
> [ 1767.952323] kernel BUG at lib/list_debug.c:56!
> [ 1767.952325] Oops: Exception in kernel mode, sig: 5 [#1]
> [ 1767.952326] LE SMP NR_CPUS=2048 NUMA pSeries
> [ 1767.952328] Modules linked in: fuse nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nf_tables_set nft_chain_nat_ipv6 nf_nat_ipv6 nft_chain_route_ipv6 nft_chain_nat_ipv4 nf_nat_ipv4 nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nft_chain_route_ipv4 ip6_tables nft_compat ip_set nf_tables nfnetlink uio_pdrv_genirq ip_tables xfs libcrc32c sd_mod sg xts vmx_crypto virtio_net net_failover failover virtio_scsi dm_multipath dm_mirror dm_region_hash dm_log dm_mod be2iscsi bnx2i cnic uio cxgb4i cxgb4 libcxgbi libcxgb qla4xxx iscsi_boot_sysfs iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi
> [ 1767.952352] CPU: 66 PID: 0 Comm: swapper/66 Kdump: loaded Not tainted 4.18.0-221.el8.ppc64le #1
> [ 1767.952354] NIP: c0000000007ab50c LR: c0000000007ab508 CTR: 00000000000003ac
> [ 1767.952355] REGS: c0000009e5a17840 TRAP: 0700 Not tainted (4.18.0-221.el8.ppc64le)
> [ 1767.952355] MSR: 800000000282b033 <SF,VEC,VSX,EE,FP,ME,IR,DR,RI,LE> CR: 28000842 XER: 20040000
> [ 1767.952360] CFAR: c0000000001ffe64 IRQMASK: 1
> [ 1767.952360] GPR00: c0000000007ab508 c0000009e5a17ac0 c000000001ac0700 0000000000000054
> [ 1767.952360] GPR04: c0000009f056cf90 c0000009f05f5628 c0000009ed40d654 c000000001c90700
> [ 1767.952360] GPR08: 0000000000000007 c0000009f0573980 00000009ef2b0000 7562202c38303230
> [ 1767.952360] GPR12: 0000000000000000 c0000007fff6ce80 c00a000002470208 0000000000000000
> [ 1767.952360] GPR16: 0000000000000001 c0000009f05fbb00 0000000000000800 c0000009ff3d4980
> [ 1767.952360] GPR20: c0000009f05fbb10 5deadbeef0000100 5deadbeef0000200 0000000000187961
> [ 1767.952360] GPR24: c0000009e5a17b78 0000000000000000 0000000000000001 ffffffffffffffff
> [ 1767.952360] GPR28: c00a000002470200 c0000009f05fbb10 c0000009f05fbb10 0000000000000000
> [ 1767.952375] NIP [c0000000007ab50c] __list_del_entry_valid+0xac/0x100
> [ 1767.952376] LR [c0000000007ab508] __list_del_entry_valid+0xa8/0x100
> [ 1767.952377] Call Trace:
> [ 1767.952378] [c0000009e5a17ac0] [c0000000007ab508] __list_del_entry_valid+0xa8/0x100 (unreliable)
> [ 1767.952381] [c0000009e5a17b20] [c000000000476e18] free_pcppages_bulk+0x1f8/0x940
> [ 1767.952383] [c0000009e5a17c20] [c00000000047a9a0] free_unref_page+0xd0/0x100
> [ 1767.952386] [c0000009e5a17c50] [c0000000000bc2a8] xive_spapr_cleanup_queue+0x148/0x1b0
> [ 1767.952388] [c0000009e5a17cf0] [c0000000000b96dc] xive_teardown_cpu+0x1bc/0x240
> [ 1767.952391] [c0000009e5a17d30] [c00000000010bf28] pseries_mach_cpu_die+0x78/0x2f0
> [ 1767.952393] [c0000009e5a17de0] [c00000000005c8d8] cpu_die+0x48/0x70
> [ 1767.952394] [c0000009e5a17e00] [c000000000021cf0] arch_cpu_idle_dead+0x20/0x40
> [ 1767.952397] [c0000009e5a17e20] [c0000000001b4294] do_idle+0x2f4/0x4c0
> [ 1767.952399] [c0000009e5a17ea0] [c0000000001b46a8] cpu_startup_entry+0x38/0x40
> [ 1767.952400] [c0000009e5a17ed0] [c00000000005c43c] start_secondary+0x7bc/0x8f0
> [ 1767.952403] [c0000009e5a17f90] [c00000000000ac70] start_secondary_prolog+0x10/0x14
>
> or on the worker thread handling the unplug:
>
> [ 1538.253044] pseries-hotplug-cpu: Attempting to remove CPU <NULL>, drc index: 1000013a
> [ 1538.360259] Querying DEAD? cpu 314 (314) shows 2
> [ 1538.360736] BUG: Bad page state in process kworker/u768:3 pfn:95de1
> [ 1538.360746] cpu 314 (hwid 314) Ready to die...
> [ 1538.360784] page:c00a000002577840 refcount:0 mapcount:-128 mapping:0000000000000000 index:0x0
> [ 1538.361881] flags: 0x5ffffc00000000()
> [ 1538.361908] raw: 005ffffc00000000 5deadbeef0000100 5deadbeef0000200 0000000000000000
> [ 1538.361955] raw: 0000000000000000 0000000000000000 00000000ffffff7f 0000000000000000
> [ 1538.362002] page dumped because: nonzero mapcount
> [ 1538.362033] Modules linked in: kvm xt_CHECKSUM ipt_MASQUERADE xt_conntrack ipt_REJECT nft_counter nf_nat_tftp nft_objref nf_conntrack_tftp tun bridge stp llc nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nft_ct nf_tables_set nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 ip6_tables nft_compat ip_set nf_tables nfnetlink sunrpc xts vmx_crypto ip_tables xfs libcrc32c sd_mod sg virtio_net net_failover virtio_scsi failover dm_mirror dm_region_hash dm_log dm_mod
> [ 1538.362613] CPU: 0 PID: 548 Comm: kworker/u768:3 Kdump: loaded Not tainted 4.18.0-224.el8.bz1856588.ppc64le #1
> [ 1538.362687] Workqueue: pseries hotplug workque pseries_hp_work_fn
> [ 1538.362725] Call Trace:
> [ 1538.362743] [c0000009d4adf590] [c000000000e0e0fc] dump_stack+0xb0/0xf4 (unreliable)
> [ 1538.362789] [c0000009d4adf5d0] [c000000000475dfc] bad_page+0x12c/0x1b0
> [ 1538.362827] [c0000009d4adf660] [c0000000004784bc] free_pcppages_bulk+0x5bc/0x940
> [ 1538.362871] [c0000009d4adf760] [c000000000478c38] page_alloc_cpu_dead+0x118/0x120
> [ 1538.362918] [c0000009d4adf7b0] [c00000000015b898] cpuhp_invoke_callback.constprop.5+0xb8/0x760
> [ 1538.362969] [c0000009d4adf820] [c00000000015eee8] _cpu_down+0x188/0x340
> [ 1538.363007] [c0000009d4adf890] [c00000000015d75c] cpu_down+0x5c/0xa0
> [ 1538.363045] [c0000009d4adf8c0] [c00000000092c544] cpu_subsys_offline+0x24/0x40
> [ 1538.363091] [c0000009d4adf8e0] [c0000000009212f0] device_offline+0xf0/0x130
> [ 1538.363129] [c0000009d4adf920] [c00000000010aee4] dlpar_offline_cpu+0x1c4/0x2a0
> [ 1538.363174] [c0000009d4adf9e0] [c00000000010b2f8] dlpar_cpu_remove+0xb8/0x190
> [ 1538.363219] [c0000009d4adfa60] [c00000000010b4fc] dlpar_cpu_remove_by_index+0x12c/0x150
> [ 1538.363264] [c0000009d4adfaf0] [c00000000010ca24] dlpar_cpu+0x94/0x800
> [ 1538.363302] [c0000009d4adfc00] [c000000000102cc8] pseries_hp_work_fn+0x128/0x1e0
> [ 1538.363347] [c0000009d4adfc70] [c00000000018aa84] process_one_work+0x304/0x5d0
> [ 1538.363394] [c0000009d4adfd10] [c00000000018b5cc] worker_thread+0xcc/0x7a0
> [ 1538.363433] [c0000009d4adfdc0] [c00000000019567c] kthread+0x1ac/0x1c0
> [ 1538.363469] [c0000009d4adfe30] [c00000000000b7dc] ret_from_kernel_thread+0x5c/0x80
>
> The latter trace is due to the following sequence:
>
> page_alloc_cpu_dead
> drain_pages
> drain_pages_zone
> free_pcppages_bulk
>
> where drain_pages() in this case is called under the assumption that
> the unplugged cpu is no longer executing. To ensure that is the case,
> and early call is made to __cpu_die()->pseries_cpu_die(), which runs
> a loop that waits for the cpu to reach a halted state by polling its
> status via query-cpu-stopped-state RTAS calls. It only polls for
> 25 iterations before giving up, however, and in the trace above this
> results in the following being printed only .1 seconds after the
> hotplug worker thread begins processing the unplug request:
>
> [ 1538.253044] pseries-hotplug-cpu: Attempting to remove CPU <NULL>, drc index: 1000013a
> [ 1538.360259] Querying DEAD? cpu 314 (314) shows 2
>
> At that point the worker thread assumes the unplugged CPU is in some
> unknown/dead state and procedes with the cleanup, causing the race with
> the XIVE cleanup code executed by the unplugged CPU.
>
> Fix this by waiting indefinitely, but also making an effort to avoid
> spurious lockup messages by allowing for rescheduling after polling
> the CPU status and printing a warning if we wait for longer than 120s.
>
> Fixes: eac1e731b59ee ("powerpc/xive: guest exploitation of the XIVE interrupt controller")
> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1856588
> Suggested-by: Michael Ellerman <mpe@ellerman.id.au>
> Cc: Thiago Jung Bauermann <bauerman@linux.ibm.com>
> Cc: Michael Ellerman <mpe@ellerman.id.au>
> Cc: Cedric Le Goater <clg@kaod.org>
> Cc: Greg Kurz <groug@kaod.org>
> Cc: Nathan Lynch <nathanl@linux.ibm.com>
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
> changes from v1:
> - renamed from "powerpc/pseries/hotplug-cpu: increase wait time for vCPU death"
> - wait indefinitely, but issue cond_resched() when RTAS reports that CPU
> hasn't stopped (Michael)
> - print a warning after 120s of waiting (Michael)
> - use pr_warn() instead of default printk() level
> ---
> arch/powerpc/platforms/pseries/hotplug-cpu.c | 18 ++++++++++++------
> 1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> index c6e0d8abf75e..7a974ed6b240 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
> @@ -107,22 +107,28 @@ static int pseries_cpu_disable(void)
> */
> static void pseries_cpu_die(unsigned int cpu)
> {
> - int tries;
> int cpu_status = 1;
> unsigned int pcpu = get_hard_smp_processor_id(cpu);
> + unsigned long timeout = jiffies + msecs_to_jiffies(120000);
>
> - for (tries = 0; tries < 25; tries++) {
> + while (true) {
> cpu_status = smp_query_cpu_stopped(pcpu);
> if (cpu_status == QCSS_STOPPED ||
> cpu_status == QCSS_HARDWARE_ERROR)
> break;
> - cpu_relax();
>
> + if (time_after(jiffies, timeout)) {
> + pr_warn("CPU %i (hwid %i) didn't die after 120 seconds\n",
> + cpu, pcpu);
> + timeout = jiffies + msecs_to_jiffies(120000);
> + }
> +
> + cond_resched();
> }
>
> - if (cpu_status != 0) {
> - printk("Querying DEAD? cpu %i (%i) shows %i\n",
> - cpu, pcpu, cpu_status);
> + if (cpu_status == QCSS_HARDWARE_ERROR) {
> + pr_warn("CPU %i (hwid %i) reported error while dying\n",
> + cpu, pcpu);
> }
>
> /* Isolation and deallocation are definitely done by
Reviewed-by: Greg Kurz <groug@kaod.org>
With my reproducer I used to hit the issues this patch is supposed to
fix after less than 30 minutes (KVM guest runnning upstream kernel on
a Witherspoon system running a recent RHEL8.3 rc). With this patch
applied I could run the reproducer for a couple of hours without seeing
anything wrong.
Thanks !
Tested-by: Greg Kurz <groug@kaod.org>
^ permalink raw reply
* Re: [PATCH 14/16] debug_vm_pgtable/hugetlb: Disable hugetlb test on ppc64
From: Anshuman Khandual @ 2020-08-12 13:34 UTC (permalink / raw)
To: Aneesh Kumar K.V, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <efc8f8c8-c16d-0826-9401-a45005ecaf62@linux.ibm.com>
On 08/12/2020 06:46 PM, Aneesh Kumar K.V wrote:
> On 8/12/20 6:33 PM, Anshuman Khandual wrote:
>>
>>
>> On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
>>> The seems to be missing quite a lot of details w.r.t allocating
>>> the correct pgtable_t page (huge_pte_alloc()), holding the right
>>> lock (huge_pte_lock()) etc. The vma used is also not a hugetlb VMA.
>>>
>>> ppc64 do have runtime checks within CONFIG_DEBUG_VM for most of these.
>>> Hence disable the test on ppc64.
>>
>> This test is free from any platform specific #ifdefs which should
>> never be broken. If hugetlb_advanced_tests() does not work or is
>> not detailed enough for ppc64, then it would be great if you could
>> suggest some improvements so that it works for all enabled platforms.
>>
>>
>
> As mentioned the test is broken. For hugetlb, the pgtable_t pages should be allocated by huge_pte_alloc(). We need to hold huget_pte_lock() before updating huge tlb pte. That takes hugepage size, which is mostly derived out of vma. Hence vma need to be a hugetlb vma. Some of the functions also depend on hstate. Also we should use set_huge_pte_at() when setting up hugetlb pte entries. I was tempted to remove that test completely marking it broken. But avoided that by marking it broken on only PPC64.
The test is not broken, hugetlb helpers on multiple platforms dont complain about
this at all. The tests here emulate 'enough' MM objects required for the helpers
on enabled platforms, to perform the primary task i.e page table transformation it
is expected to do. The test does not claim to emulate a perfect MM environment for
a given subsystem's (like HugeTLB) arch helpers. Now in this case, the MM objects
being emulated for the HugeTLB advanced tests does not seem to be sufficient for
ppc64 but it can be improved. But that does not mean it is broken in it's current
form for other platforms.
>
>
>
>>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
>>> ---
>>> mm/debug_vm_pgtable.c | 6 +++++-
>>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>>> index 529892b9be2f..3e112d0ba1b2 100644
>>> --- a/mm/debug_vm_pgtable.c
>>> +++ b/mm/debug_vm_pgtable.c
>>> @@ -800,6 +800,7 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
>>> #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
>>> }
>>> +#ifndef CONFIG_PPC_BOOK3S_64
>>> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>>> struct vm_area_struct *vma,
>>> pte_t *ptep, unsigned long pfn,
>>> @@ -842,6 +843,7 @@ static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>>> pte = huge_ptep_get(ptep);
>>> WARN_ON(!(huge_pte_write(pte) && huge_pte_dirty(pte)));
>>> }
>>> +#endif
>>> #else /* !CONFIG_HUGETLB_PAGE */
>>> static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
>>> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>>> @@ -1053,7 +1055,9 @@ static int __init debug_vm_pgtable(void)
>>> pud_populate_tests(mm, pudp, saved_pmdp);
>>> spin_unlock(ptl);
>>> - //hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>>> +#ifndef CONFIG_PPC_BOOK3S_64
>>> + hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>>> +#endif
>>> spin_lock(&mm->page_table_lock);
>>> p4d_clear_tests(mm, p4dp);
>>>
>
>
^ permalink raw reply
* Re: [PATCH 14/16] debug_vm_pgtable/hugetlb: Disable hugetlb test on ppc64
From: Aneesh Kumar K.V @ 2020-08-12 13:16 UTC (permalink / raw)
To: Anshuman Khandual, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <d067c278-6391-72f1-75c2-ccbd52909233@arm.com>
On 8/12/20 6:33 PM, Anshuman Khandual wrote:
>
>
> On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
>> The seems to be missing quite a lot of details w.r.t allocating
>> the correct pgtable_t page (huge_pte_alloc()), holding the right
>> lock (huge_pte_lock()) etc. The vma used is also not a hugetlb VMA.
>>
>> ppc64 do have runtime checks within CONFIG_DEBUG_VM for most of these.
>> Hence disable the test on ppc64.
>
> This test is free from any platform specific #ifdefs which should
> never be broken. If hugetlb_advanced_tests() does not work or is
> not detailed enough for ppc64, then it would be great if you could
> suggest some improvements so that it works for all enabled platforms.
>
>
As mentioned the test is broken. For hugetlb, the pgtable_t pages should
be allocated by huge_pte_alloc(). We need to hold huget_pte_lock()
before updating huge tlb pte. That takes hugepage size, which is mostly
derived out of vma. Hence vma need to be a hugetlb vma. Some of the
functions also depend on hstate. Also we should use set_huge_pte_at()
when setting up hugetlb pte entries. I was tempted to remove that test
completely marking it broken. But avoided that by marking it broken on
only PPC64.
>> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
>> ---
>> mm/debug_vm_pgtable.c | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
>> index 529892b9be2f..3e112d0ba1b2 100644
>> --- a/mm/debug_vm_pgtable.c
>> +++ b/mm/debug_vm_pgtable.c
>> @@ -800,6 +800,7 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
>> #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
>> }
>>
>> +#ifndef CONFIG_PPC_BOOK3S_64
>> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>> struct vm_area_struct *vma,
>> pte_t *ptep, unsigned long pfn,
>> @@ -842,6 +843,7 @@ static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>> pte = huge_ptep_get(ptep);
>> WARN_ON(!(huge_pte_write(pte) && huge_pte_dirty(pte)));
>> }
>> +#endif
>> #else /* !CONFIG_HUGETLB_PAGE */
>> static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
>> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
>> @@ -1053,7 +1055,9 @@ static int __init debug_vm_pgtable(void)
>> pud_populate_tests(mm, pudp, saved_pmdp);
>> spin_unlock(ptl);
>>
>> - //hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>> +#ifndef CONFIG_PPC_BOOK3S_64
>> + hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
>> +#endif
>>
>> spin_lock(&mm->page_table_lock);
>> p4d_clear_tests(mm, p4dp);
>>
^ permalink raw reply
* [Virtual ppce500] virtio_gpu virtio0: swiotlb buffer is full
From: Christian Zigotzky @ 2020-08-12 13:09 UTC (permalink / raw)
To: daniel.vetter
Cc: Darren Stevens, mad skateman, Michel Dänzer,
Maling list - DRI developers, kvm-ppc@vger.kernel.org,
R.T.Dickinson, linuxppc-dev
In-Reply-To: <9805f81d-651d-d1a3-fd05-fb224a8c2031@xenosoft.de>
Hello Daniel,
The VirtIO-GPU doesn't work anymore with the latest Git kernel in a
virtual e5500 PPC64 QEMU machine [1,2] after the commit "drm/virtio:
Call the right shmem helpers". [3]
The kernel 5.8 works with the VirtIO-GPU in this virtual machine.
I bisected today [4].
Result: drm/virtio: Call the right shmem helpers (
d323bb44e4d23802eb25d13de1f93f2335bd60d0) [3] is the first bad commit.
I was able to revert the first bad commit. [5] After that I compiled a
new kernel again. Then I was able to boot Linux with this kernel in a
virtual e5500 PPC64 QEMU machine with the VirtIO-GPU.
I created a patch. [6] With this patch I can use the VirtIO-GPU again.
Could you please check the first bad commit?
Thanks,
Christian
[1] QEMU command: qemu-system-ppc64 -M ppce500 -cpu e5500 -enable-kvm -m
1024 -kernel uImage -drive
format=raw,file=fienix-soar_3.0-2020608-net.img,index=0,if=virtio -nic
user,model=e1000 -append "rw root=/dev/vda2" -device virtio-vga -device
virtio-mouse-pci -device virtio-keyboard-pci -device pci-ohci,id=newusb
-device usb-audio,bus=newusb.0 -smp 4
[2] Error messages:
virtio_gpu virtio0: swiotlb buffer is full (sz: 4096 bytes), total 0
(slots), used 0 (slots)
BUG: Kernel NULL pointer dereference on read at 0x00000010
Faulting instruction address: 0xc0000000000c7324
Oops: Kernel access of bad area, sig: 11 [#1]
BE PAGE_SIZE=4K PREEMPT SMP NR_CPUS=4 QEMU e500
Modules linked in:
CPU: 2 PID: 1678 Comm: kworker/2:2 Not tainted
5.9-a3_A-EON_X5000-11735-g06a81c1c7db9-dirty #1
Workqueue: events .virtio_gpu_dequeue_ctrl_func
NIP: c0000000000c7324 LR: c0000000000c72e4 CTR: c000000000462930
REGS: c00000003dba75e0 TRAP: 0300 Not tainted
(5.9-a3_A-EON_X5000-11735-g06a81c1c7db9-dirty)
MSR: 0000000090029000 <CE,EE,ME> CR: 24002288 XER: 00000000
DEAR: 0000000000000010 ESR: 0000000000000000 IRQMASK: 0
GPR00: c0000000000c6188 c00000003dba7870 c0000000017f2300 c00000003d893010
GPR04: 0000000000000000 0000000000000001 0000000000000000 0000000000000000
GPR08: 0000000000000000 0000000000000000 0000000000000000 7f7f7f7f7f7f7f7f
GPR12: 0000000024002284 c00000003fff9200 c00000000008c3a0 c0000000061566c0
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR24: 0000000000000001 0000000000110000 0000000000000000 0000000000000000
GPR28: c00000003d893010 0000000000000000 0000000000000000 c00000003d893010
NIP [c0000000000c7324] .dma_direct_unmap_sg+0x4c/0xd8
LR [c0000000000c72e4] .dma_direct_unmap_sg+0xc/0xd8
Call Trace:
[c00000003dba7870] [c00000003dba7950] 0xc00000003dba7950 (unreliable)
[c00000003dba7920] [c0000000000c6188] .dma_unmap_sg_attrs+0x5c/0x98
[c00000003dba79d0] [c0000000005cd438] .drm_gem_shmem_free_object+0x98/0xcc
[c00000003dba7a50] [c0000000006af5b4] .virtio_gpu_cleanup_object+0xc8/0xd4
[c00000003dba7ad0] [c0000000006ad3bc] .virtio_gpu_cmd_unref_cb+0x1c/0x30
[c00000003dba7b40] [c0000000006adab8]
.virtio_gpu_dequeue_ctrl_func+0x208/0x28c
[c00000003dba7c10] [c000000000086b70] .process_one_work+0x1a4/0x258
[c00000003dba7cb0] [c0000000000870f4] .worker_thread+0x214/0x284
[c00000003dba7d70] [c00000000008c4f0] .kthread+0x150/0x158
[c00000003dba7e20] [c00000000000082c] .ret_from_kernel_thread+0x58/0x60
Instruction dump:
f821ff51 7cb82b78 7cdb3378 4e000000 7cfa3b78 3bc00000 7f9ec000 41fc0014
382100b0 81810008 7d808120 48bc1ba8 <e93d0010> ebfc0248 833d0018 7fff4850
---[ end trace f28d194d9f0955a8 ]---
virtio_gpu virtio0: swiotlb buffer is full (sz: 4096 bytes), total 0
(slots), used 0 (slots)
virtio_gpu virtio0: swiotlb buffer is full (sz: 16384 bytes), total 0
(slots), used 0 (slots)
---
[3]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d323bb44e4d23802eb25d13de1f93f2335bd60d0
[4] https://forum.hyperion-entertainment.com/viewtopic.php?p=51377#p51377
[5] git revert d323bb44e4d23802eb25d13de1f93f2335bd60d0 //Output:
[master 966950f724e4] Revert "drm/virtio: Call the right shmem helpers"
1 file changed, 1 insertion(+), 1 deletion(-)
[6]
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c
b/drivers/gpu/drm/virtio/virtgpu_object.c
index 6ccbd01cd888..346cef5ce251 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -150,7 +150,7 @@ static int virtio_gpu_object_shmem_init(struct
virtio_gpu_device *vgdev,
if (ret < 0)
return -EINVAL;
- shmem->pages = drm_gem_shmem_get_pages_sgt(&bo->base.base);
+ shmem->pages = drm_gem_shmem_get_sg_table(&bo->base.base);
if (!shmem->pages) {
drm_gem_shmem_unpin(&bo->base.base);
return -EINVAL;
---
^ permalink raw reply related
* Re: [PATCH 15/16] debug_vm_pgtable/savedwrite: Use savedwrite test with protnone ptes
From: Anshuman Khandual @ 2020-08-12 13:06 UTC (permalink / raw)
To: Aneesh Kumar K.V, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <20200812063358.369514-15-aneesh.kumar@linux.ibm.com>
On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
> Saved write support was added to track the write bit of a pte after marking the
> pte protnone. This was done so that AUTONUMA can convert a write pte to protnone
> and still track the old write bit. When converting it back we set the pte write
> bit correctly thereby avoiding a write fault again.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
> ---
> mm/debug_vm_pgtable.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 3e112d0ba1b2..eea62d5e503b 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -1006,8 +1006,8 @@ static int __init debug_vm_pgtable(void)
> pud_leaf_tests(pud_aligned, prot);
>
> #ifdef CONFIG_NUMA_BALANCING
> - pte_savedwrite_tests(pte_aligned, prot);
> - pmd_savedwrite_tests(pmd_aligned, prot);
> + pte_savedwrite_tests(pte_aligned, protnone);
> + pmd_savedwrite_tests(pmd_aligned, protnone);
> #endif
> pte_special_tests(pte_aligned, prot);
> pte_protnone_tests(pte_aligned, protnone);
>
This can be folded back with [PATCH 5/16] which now checks for
CONFIG_NUMA_BALANCING in pxx_savedwrite_tests().
^ permalink raw reply
* Re: [PATCH 14/16] debug_vm_pgtable/hugetlb: Disable hugetlb test on ppc64
From: Anshuman Khandual @ 2020-08-12 13:03 UTC (permalink / raw)
To: Aneesh Kumar K.V, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <20200812063358.369514-14-aneesh.kumar@linux.ibm.com>
On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
> The seems to be missing quite a lot of details w.r.t allocating
> the correct pgtable_t page (huge_pte_alloc()), holding the right
> lock (huge_pte_lock()) etc. The vma used is also not a hugetlb VMA.
>
> ppc64 do have runtime checks within CONFIG_DEBUG_VM for most of these.
> Hence disable the test on ppc64.
This test is free from any platform specific #ifdefs which should
never be broken. If hugetlb_advanced_tests() does not work or is
not detailed enough for ppc64, then it would be great if you could
suggest some improvements so that it works for all enabled platforms.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
> ---
> mm/debug_vm_pgtable.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 529892b9be2f..3e112d0ba1b2 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -800,6 +800,7 @@ static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot)
> #endif /* CONFIG_ARCH_WANT_GENERAL_HUGETLB */
> }
>
> +#ifndef CONFIG_PPC_BOOK3S_64
> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
> struct vm_area_struct *vma,
> pte_t *ptep, unsigned long pfn,
> @@ -842,6 +843,7 @@ static void __init hugetlb_advanced_tests(struct mm_struct *mm,
> pte = huge_ptep_get(ptep);
> WARN_ON(!(huge_pte_write(pte) && huge_pte_dirty(pte)));
> }
> +#endif
> #else /* !CONFIG_HUGETLB_PAGE */
> static void __init hugetlb_basic_tests(unsigned long pfn, pgprot_t prot) { }
> static void __init hugetlb_advanced_tests(struct mm_struct *mm,
> @@ -1053,7 +1055,9 @@ static int __init debug_vm_pgtable(void)
> pud_populate_tests(mm, pudp, saved_pmdp);
> spin_unlock(ptl);
>
> - //hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
> +#ifndef CONFIG_PPC_BOOK3S_64
> + hugetlb_advanced_tests(mm, vma, ptep, pte_aligned, vaddr, prot);
> +#endif
>
> spin_lock(&mm->page_table_lock);
> p4d_clear_tests(mm, p4dp);
>
^ permalink raw reply
* Re: [PATCH 09/16] debug_vm_pgtable/set_pud: Don't use set_pud_at to update an existing pud entry
From: Anshuman Khandual @ 2020-08-12 12:46 UTC (permalink / raw)
To: Aneesh Kumar K.V, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <20200812063358.369514-9-aneesh.kumar@linux.ibm.com>
On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
> set_pud_at() should not be used to set a pte entry at locations that
> already holds a valid pte entry. Architectures like ppc64 don't do TLB
> invalidate in set_pud_at() and hence expect it to be used to set locations
> that are not a valid PTE.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
> ---
> mm/debug_vm_pgtable.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index 60bf876081b8..644d28861ce9 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -278,9 +278,6 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
> WARN_ON(pud_write(pud));
>
> #ifndef __PAGETABLE_PMD_FOLDED
> -
> - pud = pud_mkhuge(pfn_pud(pfn, prot));
> - set_pud_at(mm, vaddr, pudp, pud);
> pudp_huge_get_and_clear(mm, vaddr, pudp);
> pud = READ_ONCE(*pudp);
> WARN_ON(!pud_none(pud));
> @@ -302,6 +299,11 @@ static void __init pud_advanced_tests(struct mm_struct *mm,
> pud = READ_ONCE(*pudp);
> WARN_ON(!(pud_write(pud) && pud_dirty(pud)));
>
> + pudp_huge_get_and_clear_full(vma, vaddr, pudp, 1);
> + pud = READ_ONCE(*pudp);
> + WARN_ON(!pud_none(pud));
> +
> + pud = pud_mkhuge(pfn_pud(pfn, prot));
> pud = pud_mkyoung(pud);
> set_pud_at(mm, vaddr, pudp, pud);
> pudp_test_and_clear_young(vma, vaddr, pudp);
Very similar to [PATCH 3/16] wrt set_pte_at(), please fold it.
^ permalink raw reply
* Re: [PATCH 08/16] debug_vm_pgtable/set_pmd: Don't use set_pmd_at to update an existing pmd entry
From: Anshuman Khandual @ 2020-08-12 12:46 UTC (permalink / raw)
To: Aneesh Kumar K.V, linux-mm, akpm; +Cc: linuxppc-dev
In-Reply-To: <20200812063358.369514-8-aneesh.kumar@linux.ibm.com>
On 08/12/2020 12:03 PM, Aneesh Kumar K.V wrote:
> set_pmd_at() should not be used to set a pte entry at locations that
> already holds a valid pte entry. Architectures like ppc64 don't do TLB
> invalidate in set_pmd_at() and hence expect it to be used to set locations
> that are not a valid PTE.
>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
> ---
> mm/debug_vm_pgtable.c | 5 +----
> 1 file changed, 1 insertion(+), 4 deletions(-)
>
> diff --git a/mm/debug_vm_pgtable.c b/mm/debug_vm_pgtable.c
> index cd609a212dd4..60bf876081b8 100644
> --- a/mm/debug_vm_pgtable.c
> +++ b/mm/debug_vm_pgtable.c
> @@ -164,8 +164,6 @@ static void __init pmd_advanced_tests(struct mm_struct *mm,
> pmd = READ_ONCE(*pmdp);
> WARN_ON(pmd_write(pmd));
>
> - pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
> - set_pmd_at(mm, vaddr, pmdp, pmd);
> pmdp_huge_get_and_clear(mm, vaddr, pmdp);
> pmd = READ_ONCE(*pmdp);
> WARN_ON(!pmd_none(pmd));
> @@ -180,12 +178,11 @@ static void __init pmd_advanced_tests(struct mm_struct *mm,
> pmd = READ_ONCE(*pmdp);
> WARN_ON(!(pmd_write(pmd) && pmd_dirty(pmd)));
>
> - pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
> - set_pmd_at(mm, vaddr, pmdp, pmd);
> pmdp_huge_get_and_clear_full(vma, vaddr, pmdp, 1);
> pmd = READ_ONCE(*pmdp);
> WARN_ON(!pmd_none(pmd));
>
> + pmd = pmd_mkhuge(pfn_pmd(pfn, prot));
> pmd = pmd_mkyoung(pmd);
> set_pmd_at(mm, vaddr, pmdp, pmd);
> pmdp_test_and_clear_young(vma, vaddr, pmdp);
>
Very similar to [PATCH 3/16] wrt set_pte_at(), please fold it.
^ permalink raw reply
* Re: [PATCH v2] powerpc/uaccess: Use flexible addressing with __put_user()/__get_user()
From: Christophe Leroy @ 2020-08-12 12:32 UTC (permalink / raw)
To: Michael Ellerman, Benjamin Herrenschmidt, Paul Mackerras, npiggin,
segher
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <c827fd9b-984d-ca86-67e9-512ca10d118f@csgroup.eu>
Le 08/07/2020 à 06:49, Christophe Leroy a écrit :
>
>
> Le 07/07/2020 à 21:02, Christophe Leroy a écrit :
>>
>>
>> Le 07/07/2020 à 14:44, Christophe Leroy a écrit :
>>>
>>>
>>> Le 30/06/2020 à 03:19, Michael Ellerman a écrit :
>>>> Michael Ellerman <mpe@ellerman.id.au> writes:
>>>>> Christophe Leroy <christophe.leroy@csgroup.eu> writes:
>>>>>> Hi Michael,
>>>>>>
>>>>>> I see this patch is marked as "defered" in patchwork, but I can't see
>>>>>> any related discussion. Is it normal ?
>>>>>
>>>>> Because it uses the "m<>" constraint which didn't work on GCC 4.6.
>>>>>
>>>>> https://github.com/linuxppc/issues/issues/297
>>>>>
>>>>> So we should be able to pick it up for v5.9 hopefully.
>>>>
>>>> It seems to break the build with the kernel.org 4.9.4 compiler and
>>>> corenet64_smp_defconfig:
>>>
>>> Most likely a GCC bug ?
>>>
>>> It seems the problem vanishes with patch
>>> https://patchwork.ozlabs.org/project/linuxppc-dev/patch/173de3b659fa3a5f126a0eb170522cccd909950f.1594125164.git.christophe.leroy@csgroup.eu/
>>>
>>
>> Same kind of issue in signal_64.c now.
>>
>> The following patch fixes it:
>> https://patchwork.ozlabs.org/project/linuxppc-dev/patch/810bd8840ef990a200f58c9dea9abe767ca02a3a.1594146723.git.christophe.leroy@csgroup.eu/
>>
>>
>
> This time I confirm, with the two above mentioned patches, it builds OK
> with 4.9, see
> http://kisskb.ellerman.id.au/kisskb/head/810bd8840ef990a200f58c9dea9abe767ca02a3a/
>
>
I see you've merged those patches that make the issue disappear, yet not
this patch yet. I guess you are still a bit chilly about it, so I split
it in two parts for you to safely take patch 1 as soon as possible while
handling the "m<>" constraint subject more carefully via
https://github.com/linuxppc/issues/issues/297 in a later stage.
Anyway, it seems that GCC doesn't make much use of the "m<>" and the
pre-update form. Most of the benefit of flexible addressing seems to be
achieved with patch 1 ie without the "m<>" constraint and update form.
Christophe
^ permalink raw reply
* Re: [PATCH v3 8/8] mm/vmalloc: Hugepage vmalloc mappings
From: Jonathan Cameron @ 2020-08-12 12:25 UTC (permalink / raw)
To: Nicholas Piggin
Cc: linux-arch, Zefan Li, H. Peter Anvin, Will Deacon, x86,
linux-kernel, linux-mm, Ingo Molnar, Borislav Petkov,
Catalin Marinas, Thomas Gleixner, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20200810022732.1150009-9-npiggin@gmail.com>
On Mon, 10 Aug 2020 12:27:32 +1000
Nicholas Piggin <npiggin@gmail.com> wrote:
> On platforms that define HAVE_ARCH_HUGE_VMAP and support PMD vmaps,
> vmalloc will attempt to allocate PMD-sized pages first, before falling
> back to small pages.
>
> Allocations which use something other than PAGE_KERNEL protections are
> not permitted to use huge pages yet, not all callers expect this (e.g.,
> module allocations vs strict module rwx).
>
> This reduces TLB misses by nearly 30x on a `git diff` workload on a
> 2-node POWER9 (59,800 -> 2,100) and reduces CPU cycles by 0.54%.
>
> This can result in more internal fragmentation and memory overhead for a
> given allocation, an option nohugevmap is added to disable at boot.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Hi Nicholas,
Busy afternoon, but a possible point of interest in line in the meantime.
...
> @@ -2701,22 +2760,45 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
> pgprot_t prot, unsigned long vm_flags, int node,
> const void *caller)
> {
> - struct vm_struct *area;
> + struct vm_struct *area = NULL;
> void *addr;
> unsigned long real_size = size;
> + unsigned long real_align = align;
> + unsigned int shift = PAGE_SHIFT;
>
> size = PAGE_ALIGN(size);
> if (!size || (size >> PAGE_SHIFT) > totalram_pages())
> goto fail;
>
> - area = __get_vm_area_node(real_size, align, VM_ALLOC | VM_UNINITIALIZED |
> + if (vmap_allow_huge && (pgprot_val(prot) == pgprot_val(PAGE_KERNEL))) {
> + unsigned long size_per_node;
> +
> + /*
> + * Try huge pages. Only try for PAGE_KERNEL allocations,
> + * others like modules don't yet expect huge pages in
> + * their allocations due to apply_to_page_range not
> + * supporting them.
> + */
> +
> + size_per_node = size;
> + if (node == NUMA_NO_NODE)
> + size_per_node /= num_online_nodes();
> + if (size_per_node >= PMD_SIZE)
> + shift = PMD_SHIFT;
> + }
> +
> +again:
> + align = max(real_align, 1UL << shift);
> + size = ALIGN(real_size, align);
So my suspicion is that the issue on arm64 is related to this.
In the relevant call path, align is 32K whilst the size is 16K
Previously I don't think we force size to be a multiple of align.
I think this results in nr_pages being double what it was before.
> +
> + area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNINITIALIZED |
> vm_flags, start, end, node, gfp_mask, caller);
> if (!area)
> goto fail;
>
> - addr = __vmalloc_area_node(area, gfp_mask, prot, node);
> + addr = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
> if (!addr)
> - return NULL;
> + goto fail;
>
> /*
> * In this function, newly allocated vm_struct has VM_UNINITIALIZED
> @@ -2730,8 +2812,16 @@ void *__vmalloc_node_range(unsigned long size, unsigned long align,
> return addr;
>
> fail:
> - warn_alloc(gfp_mask, NULL,
> + if (shift > PAGE_SHIFT) {
> + shift = PAGE_SHIFT;
> + goto again;
> + }
> +
> + if (!area) {
> + /* Warn for area allocation, page allocations already warn */
> + warn_alloc(gfp_mask, NULL,
> "vmalloc: allocation failure: %lu bytes", real_size);
> + }
> return NULL;
> }
>
^ permalink raw reply
* [PATCH v3 1/2] powerpc/uaccess: Use flexible addressing with __put_user()/__get_user()
From: Christophe Leroy @ 2020-08-12 12:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
At the time being, __put_user()/__get_user() and friends only use
D-form addressing, with 0 offset. Ex:
lwz reg1, 0(reg2)
Give the compiler the opportunity to use other adressing modes
whenever possible, to get more optimised code.
Hereunder is a small exemple:
struct test {
u32 item1;
u16 item2;
u8 item3;
u64 item4;
};
int set_test_user(struct test __user *from, struct test __user *to)
{
int err;
u32 item1;
u16 item2;
u8 item3;
u64 item4;
err = __get_user(item1, &from->item1);
err |= __get_user(item2, &from->item2);
err |= __get_user(item3, &from->item3);
err |= __get_user(item4, &from->item4);
err |= __put_user(item1, &to->item1);
err |= __put_user(item2, &to->item2);
err |= __put_user(item3, &to->item3);
err |= __put_user(item4, &to->item4);
return err;
}
Before the patch:
00000df0 <set_test_user>:
df0: 94 21 ff f0 stwu r1,-16(r1)
df4: 39 40 00 00 li r10,0
df8: 93 c1 00 08 stw r30,8(r1)
dfc: 93 e1 00 0c stw r31,12(r1)
e00: 7d 49 53 78 mr r9,r10
e04: 80 a3 00 00 lwz r5,0(r3)
e08: 38 e3 00 04 addi r7,r3,4
e0c: 7d 46 53 78 mr r6,r10
e10: a0 e7 00 00 lhz r7,0(r7)
e14: 7d 29 33 78 or r9,r9,r6
e18: 39 03 00 06 addi r8,r3,6
e1c: 7d 46 53 78 mr r6,r10
e20: 89 08 00 00 lbz r8,0(r8)
e24: 7d 29 33 78 or r9,r9,r6
e28: 38 63 00 08 addi r3,r3,8
e2c: 7d 46 53 78 mr r6,r10
e30: 83 c3 00 00 lwz r30,0(r3)
e34: 83 e3 00 04 lwz r31,4(r3)
e38: 7d 29 33 78 or r9,r9,r6
e3c: 7d 43 53 78 mr r3,r10
e40: 90 a4 00 00 stw r5,0(r4)
e44: 7d 29 1b 78 or r9,r9,r3
e48: 38 c4 00 04 addi r6,r4,4
e4c: 7d 43 53 78 mr r3,r10
e50: b0 e6 00 00 sth r7,0(r6)
e54: 7d 29 1b 78 or r9,r9,r3
e58: 38 e4 00 06 addi r7,r4,6
e5c: 7d 43 53 78 mr r3,r10
e60: 99 07 00 00 stb r8,0(r7)
e64: 7d 23 1b 78 or r3,r9,r3
e68: 38 84 00 08 addi r4,r4,8
e6c: 93 c4 00 00 stw r30,0(r4)
e70: 93 e4 00 04 stw r31,4(r4)
e74: 7c 63 53 78 or r3,r3,r10
e78: 83 c1 00 08 lwz r30,8(r1)
e7c: 83 e1 00 0c lwz r31,12(r1)
e80: 38 21 00 10 addi r1,r1,16
e84: 4e 80 00 20 blr
After the patch:
00000dbc <set_test_user>:
dbc: 39 40 00 00 li r10,0
dc0: 7d 49 53 78 mr r9,r10
dc4: 80 03 00 00 lwz r0,0(r3)
dc8: 7d 48 53 78 mr r8,r10
dcc: a1 63 00 04 lhz r11,4(r3)
dd0: 7d 29 43 78 or r9,r9,r8
dd4: 7d 48 53 78 mr r8,r10
dd8: 88 a3 00 06 lbz r5,6(r3)
ddc: 7d 29 43 78 or r9,r9,r8
de0: 7d 48 53 78 mr r8,r10
de4: 80 c3 00 08 lwz r6,8(r3)
de8: 80 e3 00 0c lwz r7,12(r3)
dec: 7d 29 43 78 or r9,r9,r8
df0: 7d 43 53 78 mr r3,r10
df4: 90 04 00 00 stw r0,0(r4)
df8: 7d 29 1b 78 or r9,r9,r3
dfc: 7d 43 53 78 mr r3,r10
e00: b1 64 00 04 sth r11,4(r4)
e04: 7d 29 1b 78 or r9,r9,r3
e08: 7d 43 53 78 mr r3,r10
e0c: 98 a4 00 06 stb r5,6(r4)
e10: 7d 23 1b 78 or r3,r9,r3
e14: 90 c4 00 08 stw r6,8(r4)
e18: 90 e4 00 0c stw r7,12(r4)
e1c: 7c 63 53 78 or r3,r3,r10
e20: 4e 80 00 20 blr
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Reviewed-by: Segher Boessenkool <segher@kernel.crashing.org>
---
v3:
- Removed the pre-update form and the <> modifier, because it seems
to be problematic with some older versions of GCC (namely 4.9) and
doesn't seems to generate much code of that form anyway. So better
to get it merged with the pre-update form than not be merged at all.
The pre-update form is added in a following patch that may then get
merged later once we have cleared the issue with it.
v2:
- Added <> modifier in __put_user_asm() and __get_user_asm()
- Removed %U2 in __put_user_asm2() and __get_user_asm2()
- Reworded the commit log
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/include/asm/uaccess.h | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 64c04ab09112..c546fb36043d 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -159,7 +159,7 @@ extern long __put_user_bad(void);
*/
#define __put_user_asm(x, addr, err, op) \
__asm__ __volatile__( \
- "1: " op " %1,0(%2) # put_user\n" \
+ "1: " op "%X2 %1,%2 # put_user\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: li %0,%3\n" \
@@ -167,7 +167,7 @@ extern long __put_user_bad(void);
".previous\n" \
EX_TABLE(1b, 3b) \
: "=r" (err) \
- : "r" (x), "b" (addr), "i" (-EFAULT), "0" (err))
+ : "r" (x), "m" (*addr), "i" (-EFAULT), "0" (err))
#ifdef __powerpc64__
#define __put_user_asm2(x, ptr, retval) \
@@ -175,8 +175,8 @@ extern long __put_user_bad(void);
#else /* __powerpc64__ */
#define __put_user_asm2(x, addr, err) \
__asm__ __volatile__( \
- "1: stw %1,0(%2)\n" \
- "2: stw %1+1,4(%2)\n" \
+ "1: stw%X2 %1,%2\n" \
+ "2: stw%X2 %L1,%L2\n" \
"3:\n" \
".section .fixup,\"ax\"\n" \
"4: li %0,%3\n" \
@@ -185,7 +185,7 @@ extern long __put_user_bad(void);
EX_TABLE(1b, 4b) \
EX_TABLE(2b, 4b) \
: "=r" (err) \
- : "r" (x), "b" (addr), "i" (-EFAULT), "0" (err))
+ : "r" (x), "m" (*addr), "i" (-EFAULT), "0" (err))
#endif /* __powerpc64__ */
#define __put_user_size_allowed(x, ptr, size, retval) \
@@ -317,7 +317,7 @@ extern long __get_user_bad(void);
#define __get_user_asm(x, addr, err, op) \
__asm__ __volatile__( \
- "1: "op" %1,0(%2) # get_user\n" \
+ "1: "op"%X2 %1, %2 # get_user\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: li %0,%3\n" \
@@ -326,7 +326,7 @@ extern long __get_user_bad(void);
".previous\n" \
EX_TABLE(1b, 3b) \
: "=r" (err), "=r" (x) \
- : "b" (addr), "i" (-EFAULT), "0" (err))
+ : "m" (*addr), "i" (-EFAULT), "0" (err))
#ifdef __powerpc64__
#define __get_user_asm2(x, addr, err) \
@@ -334,8 +334,8 @@ extern long __get_user_bad(void);
#else /* __powerpc64__ */
#define __get_user_asm2(x, addr, err) \
__asm__ __volatile__( \
- "1: lwz %1,0(%2)\n" \
- "2: lwz %1+1,4(%2)\n" \
+ "1: lwz%X2 %1, %2\n" \
+ "2: lwz%X2 %L1, %L2\n" \
"3:\n" \
".section .fixup,\"ax\"\n" \
"4: li %0,%3\n" \
@@ -346,7 +346,7 @@ extern long __get_user_bad(void);
EX_TABLE(1b, 4b) \
EX_TABLE(2b, 4b) \
: "=r" (err), "=&r" (x) \
- : "b" (addr), "i" (-EFAULT), "0" (err))
+ : "m" (*addr), "i" (-EFAULT), "0" (err))
#endif /* __powerpc64__ */
#define __get_user_size_allowed(x, ptr, size, retval) \
@@ -356,10 +356,10 @@ do { \
if (size > sizeof(x)) \
(x) = __get_user_bad(); \
switch (size) { \
- case 1: __get_user_asm(x, ptr, retval, "lbz"); break; \
- case 2: __get_user_asm(x, ptr, retval, "lhz"); break; \
- case 4: __get_user_asm(x, ptr, retval, "lwz"); break; \
- case 8: __get_user_asm2(x, ptr, retval); break; \
+ case 1: __get_user_asm(x, (u8 __user *)ptr, retval, "lbz"); break; \
+ case 2: __get_user_asm(x, (u16 __user *)ptr, retval, "lhz"); break; \
+ case 4: __get_user_asm(x, (u32 __user *)ptr, retval, "lwz"); break; \
+ case 8: __get_user_asm2(x, (u64 __user *)ptr, retval); break; \
default: (x) = __get_user_bad(); \
} \
} while (0)
--
2.25.0
^ permalink raw reply related
* [PATCH v3 2/2] powerpc/uaccess: Add pre-update addressing to __get_user_asm() and __put_user_asm()
From: Christophe Leroy @ 2020-08-12 12:25 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <c27bc4e598daf3bbb225de7a1f5c52121cf1e279.1597235091.git.christophe.leroy@csgroup.eu>
Enable pre-update addressing mode in __get_user_asm() and __put_user_asm()
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
v3: new, splited out from patch 1.
---
arch/powerpc/include/asm/uaccess.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index c546fb36043d..eadb25f4cc9f 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -159,7 +159,7 @@ extern long __put_user_bad(void);
*/
#define __put_user_asm(x, addr, err, op) \
__asm__ __volatile__( \
- "1: " op "%X2 %1,%2 # put_user\n" \
+ "1: " op "%U2%X2 %1,%2 # put_user\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: li %0,%3\n" \
@@ -167,7 +167,7 @@ extern long __put_user_bad(void);
".previous\n" \
EX_TABLE(1b, 3b) \
: "=r" (err) \
- : "r" (x), "m" (*addr), "i" (-EFAULT), "0" (err))
+ : "r" (x), "m<>" (*addr), "i" (-EFAULT), "0" (err))
#ifdef __powerpc64__
#define __put_user_asm2(x, ptr, retval) \
@@ -317,7 +317,7 @@ extern long __get_user_bad(void);
#define __get_user_asm(x, addr, err, op) \
__asm__ __volatile__( \
- "1: "op"%X2 %1, %2 # get_user\n" \
+ "1: "op"%U2%X2 %1, %2 # get_user\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: li %0,%3\n" \
@@ -326,7 +326,7 @@ extern long __get_user_bad(void);
".previous\n" \
EX_TABLE(1b, 3b) \
: "=r" (err), "=r" (x) \
- : "m" (*addr), "i" (-EFAULT), "0" (err))
+ : "m<>" (*addr), "i" (-EFAULT), "0" (err))
#ifdef __powerpc64__
#define __get_user_asm2(x, addr, err) \
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 18/19] powerpc/signal32: Add and use unsafe_put_sigset_t()
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
put_sigset_t() calls copy_to_user() for copying two words.
Because INLINE_COPY_TO_USER is not defined on powerpc,
copy_to_user() doesn't get optimised and falls back to
copy_tofrom_user() with the relevant glue. This is terribly
inefficient for copying two words.
By switching to unsafe_put_user(), we end up with something as
simple as:
3cc: 81 3d 00 00 lwz r9,0(r29)
3d0: 91 26 00 b4 stw r9,180(r6)
3d4: 81 3d 00 04 lwz r9,4(r29)
3d8: 91 26 00 b8 stw r9,184(r6)
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index d03ba3d8eb68..6cbff0293ff4 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -87,6 +87,8 @@ static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
return put_compat_sigset(uset, set, sizeof(*uset));
}
+#define unsafe_put_sigset_t unsafe_put_compat_sigset
+
static inline int get_sigset_t(sigset_t *set,
const compat_sigset_t __user *uset)
{
@@ -143,6 +145,13 @@ static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
return copy_to_user(uset, set, sizeof(*uset));
}
+#define unsafe_put_sigset_t(uset, set, label) do { \
+ sigset_t __user *__us = uset ; \
+ const sigset_t *__s = set; \
+ \
+ unsafe_copy_to_user(__us, __s, sizeof(*__us), label); \
+} while (0)
+
static inline int get_sigset_t(sigset_t *set, const sigset_t __user *uset)
{
return copy_from_user(set, uset, sizeof(*uset));
@@ -820,10 +829,11 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
{
unsafe_put_user(0, &rt_sf->uc.uc_link, failed);
}
+
+ unsafe_put_sigset_t(&rt_sf->uc.uc_sigmask, oldset, failed);
+
user_write_access_end();
- if (put_sigset_t(&rt_sf->uc.uc_sigmask, oldset))
- goto badframe;
if (copy_siginfo_to_user(&rt_sf->info, &ksig->info))
goto badframe;
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 19/19] powerpc/signal32: Switch swap_context() to user_access_begin() logic
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
As this was the last user of put_sigset_t(), remove it as well.
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 6cbff0293ff4..399d823782cf 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -82,11 +82,6 @@
* Functions for flipping sigsets (thanks to brain dead generic
* implementation that makes things simple for little endian only)
*/
-static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
-{
- return put_compat_sigset(uset, set, sizeof(*uset));
-}
-
#define unsafe_put_sigset_t unsafe_put_compat_sigset
static inline int get_sigset_t(sigset_t *set,
@@ -140,11 +135,6 @@ static inline int restore_general_regs(struct pt_regs *regs,
#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
-static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
-{
- return copy_to_user(uset, set, sizeof(*uset));
-}
-
#define unsafe_put_sigset_t(uset, set, label) do { \
sigset_t __user *__us = uset ; \
const sigset_t *__s = set; \
@@ -1012,11 +1002,13 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
*/
mctx = (struct mcontext __user *)
((unsigned long) &old_ctx->uc_mcontext & ~0xfUL);
- if (!access_ok(old_ctx, ctx_size)
- || save_user_regs(regs, mctx, NULL, 0, ctx_has_vsx_region)
- || put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked)
- || __put_user(to_user_ptr(mctx), &old_ctx->uc_regs))
+ if (save_user_regs(regs, mctx, NULL, 0, ctx_has_vsx_region))
+ return -EFAULT;
+ if (!user_write_access_begin(old_ctx, ctx_size))
return -EFAULT;
+ unsafe_put_sigset_t(&old_ctx->uc_sigmask, ¤t->blocked, failed);
+ unsafe_put_user(to_user_ptr(mctx), &old_ctx->uc_regs, failed);
+ user_write_access_end();
}
if (new_ctx == NULL)
return 0;
@@ -1040,6 +1032,10 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
set_thread_flag(TIF_RESTOREALL);
return 0;
+
+failed:
+ user_write_access_end();
+ return -EFAULT;
}
#ifdef CONFIG_PPC64
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 17/19] signal: Add unsafe_put_compat_sigset()
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
Implement 'unsafe' version of put_compat_sigset()
For the bigendian, use unsafe_put_user() directly
to avoid intermediate copy through the stack.
For the littleendian, use a straight unsafe_copy_to_user().
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Dmitry V. Levin <ldv@altlinux.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
---
include/linux/compat.h | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/include/linux/compat.h b/include/linux/compat.h
index c4255d8a4a8a..bbe5f9658ed1 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -442,6 +442,38 @@ put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set,
#endif
}
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define unsafe_put_compat_sigset(compat, set, label) do { \
+ compat_sigset_t __user *__c = compat; \
+ const sigset_t *__s = set; \
+ \
+ switch (_NSIG_WORDS) { \
+ case 4: \
+ unsafe_put_user(__s->sig[3] >> 32, __c->sig[7], label); \
+ unsafe_put_user(__s->sig[3], __c->sig[6], label); \
+ fallthrough; \
+ case 3: \
+ unsafe_put_user(__s->sig[2] >> 32, __c->sig[5], label); \
+ unsafe_put_user(__s->sig[2], __c->sig[4], label); \
+ fallthrough; \
+ case 2: \
+ unsafe_put_user(__s->sig[1] >> 32, __c->sig[3], label); \
+ unsafe_put_user(__s->sig[1], __c->sig[2], label); \
+ fallthrough; \
+ case 1: \
+ unsafe_put_user(__s->sig[0] >> 32, __c->sig[1], label); \
+ unsafe_put_user(__s->sig[0], __c->sig[0], label); \
+ } \
+} while (0)
+#else
+#define unsafe_put_compat_sigset(compat, set, label) do { \
+ compat_sigset_t __user *__c = compat; \
+ const sigset_t *__s = set; \
+ \
+ unsafe_copy_to_user(__c, __s, sizeof(*__c), label); \
+} while (0)
+#endif
+
extern int compat_ptrace_request(struct task_struct *child,
compat_long_t request,
compat_ulong_t addr, compat_ulong_t data);
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 16/19] powerpc/signal32: Switch handle_rt_signal32() to user_access_begin() logic
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 47 +++++++++++++++++++++------------
1 file changed, 30 insertions(+), 17 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 4ea83578ba9d..d03ba3d8eb68 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -58,8 +58,6 @@
#define mcontext mcontext32
#define ucontext ucontext32
-#define __save_altstack __compat_save_altstack
-
/*
* Userspace code may pass a ucontext which doesn't include VSX added
* at the end. We need to check for this case.
@@ -797,16 +795,36 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
/* Put a Real Time Context onto stack */
rt_sf = get_sigframe(ksig, tsk, sizeof(*rt_sf), 1);
- if (!access_ok(rt_sf, sizeof(*rt_sf)))
+ if (!user_write_access_begin(rt_sf, sizeof(*rt_sf)))
goto badframe;
/* Put the siginfo & fill in most of the ucontext */
- if (copy_siginfo_to_user(&rt_sf->info, &ksig->info)
- || __put_user(0, &rt_sf->uc.uc_flags)
- || __save_altstack(&rt_sf->uc.uc_stack, regs->gpr[1])
- || __put_user(to_user_ptr(&rt_sf->uc.uc_mcontext),
- &rt_sf->uc.uc_regs)
- || put_sigset_t(&rt_sf->uc.uc_sigmask, oldset))
+ unsafe_put_user(0, &rt_sf->uc.uc_flags, failed);
+#ifdef CONFIG_PPC64
+ unsafe_compat_save_altstack(&rt_sf->uc.uc_stack, regs->gpr[1], failed);
+#else
+ unsafe_save_altstack(&rt_sf->uc.uc_stack, regs->gpr[1], failed);
+#endif
+ unsafe_put_user(to_user_ptr(&rt_sf->uc.uc_mcontext), &rt_sf->uc.uc_regs, failed);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ tm_frame = &rt_sf->uc_transact.uc_mcontext;
+ if (MSR_TM_ACTIVE(msr)) {
+ unsafe_put_user((unsigned long)&rt_sf->uc_transact,
+ &rt_sf->uc.uc_link, failed);
+ unsafe_put_user((unsigned long)tm_frame,
+ &rt_sf->uc_transact.uc_regs, failed);
+ }
+ else
+#endif
+ {
+ unsafe_put_user(0, &rt_sf->uc.uc_link, failed);
+ }
+ user_write_access_end();
+
+ if (put_sigset_t(&rt_sf->uc.uc_sigmask, oldset))
+ goto badframe;
+ if (copy_siginfo_to_user(&rt_sf->info, &ksig->info))
goto badframe;
/* Save user registers on the stack */
@@ -820,21 +838,13 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
- tm_frame = &rt_sf->uc_transact.uc_mcontext;
if (MSR_TM_ACTIVE(msr)) {
- if (__put_user((unsigned long)&rt_sf->uc_transact,
- &rt_sf->uc.uc_link) ||
- __put_user((unsigned long)tm_frame,
- &rt_sf->uc_transact.uc_regs))
- goto badframe;
if (save_tm_user_regs(regs, frame, tm_frame, sigret, msr))
goto badframe;
}
else
#endif
{
- if (__put_user(0, &rt_sf->uc.uc_link))
- goto badframe;
if (save_user_regs(regs, frame, tm_frame, sigret, 1))
goto badframe;
}
@@ -861,6 +871,9 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
regs->msr |= (MSR_KERNEL & MSR_LE);
return 0;
+failed:
+ user_write_access_end();
+
badframe:
signal_fault(tsk, regs, "handle_rt_signal32", rt_sf);
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 11/19] powerpc/signal32: Simplify logging in handle_rt_signal32()
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
If something is bad in the frame, there is no point in
knowing which part of the frame exactly is wrong as it
got allocated as a single block.
Always print the root address of the frame in case on
failed user access, just like handle_signal32().
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index d1087dd87174..495bee1b713d 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -754,7 +754,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
struct rt_sigframe __user *rt_sf;
struct mcontext __user *frame;
struct mcontext __user *tm_frame = NULL;
- void __user *addr;
unsigned long newsp = 0;
int sigret;
unsigned long tramp;
@@ -769,7 +768,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
/* Put a Real Time Context onto stack */
rt_sf = get_sigframe(ksig, tsk, sizeof(*rt_sf), 1);
- addr = rt_sf;
if (!access_ok(rt_sf, sizeof(*rt_sf)))
goto badframe;
@@ -784,7 +782,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Save user registers on the stack */
frame = &rt_sf->uc.uc_mcontext;
- addr = frame;
if (vdso32_rt_sigtramp && tsk->mm->context.vdso_base) {
sigret = 0;
tramp = tsk->mm->context.vdso_base + vdso32_rt_sigtramp;
@@ -820,7 +817,6 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
/* create a stack frame for the caller of the handler */
newsp = ((unsigned long)rt_sf) - (__SIGNAL_FRAMESIZE + 16);
- addr = (void __user *)regs->gpr[1];
if (put_user(regs->gpr[1], (u32 __user *)newsp))
goto badframe;
@@ -837,7 +833,7 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t *oldset,
return 0;
badframe:
- signal_fault(tsk, regs, "handle_rt_signal32", addr);
+ signal_fault(tsk, regs, "handle_rt_signal32", rt_sf);
return 1;
}
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 15/19] powerpc/signal32: Switch handle_signal32() to user_access_begin() logic
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 0d076c2a9f6c..4ea83578ba9d 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -1241,23 +1241,23 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
/* Set up Signal Frame */
frame = get_sigframe(ksig, tsk, sizeof(*frame), 1);
- if (!access_ok(frame, sizeof(*frame)))
+ if (!user_write_access_begin(frame, sizeof(*frame)))
goto badframe;
sc = (struct sigcontext __user *) &frame->sctx;
#if _NSIG != 64
#error "Please adjust handle_signal()"
#endif
- if (__put_user(to_user_ptr(ksig->ka.sa.sa_handler), &sc->handler)
- || __put_user(oldset->sig[0], &sc->oldmask)
+ unsafe_put_user(to_user_ptr(ksig->ka.sa.sa_handler), &sc->handler, failed);
+ unsafe_put_user(oldset->sig[0], &sc->oldmask, failed);
#ifdef CONFIG_PPC64
- || __put_user((oldset->sig[0] >> 32), &sc->_unused[3])
+ unsafe_put_user((oldset->sig[0] >> 32), &sc->_unused[3], failed);
#else
- || __put_user(oldset->sig[1], &sc->_unused[3])
+ unsafe_put_user(oldset->sig[1], &sc->_unused[3], failed);
#endif
- || __put_user(to_user_ptr(&frame->mctx), &sc->regs)
- || __put_user(ksig->sig, &sc->signal))
- goto badframe;
+ unsafe_put_user(to_user_ptr(&frame->mctx), &sc->regs, failed);
+ unsafe_put_user(ksig->sig, &sc->signal, failed);
+ user_write_access_end();
if (vdso32_sigtramp && tsk->mm->context.vdso_base) {
sigret = 0;
@@ -1300,6 +1300,9 @@ int handle_signal32(struct ksignal *ksig, sigset_t *oldset,
regs->msr &= ~MSR_LE;
return 0;
+failed:
+ user_write_access_end();
+
badframe:
signal_fault(tsk, regs, "handle_signal32", frame);
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 14/19] powerpc/signal32: Switch save_user_regs() and save_tm_user_regs() to user_access_begin() logic
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal_32.c | 168 ++++++++++++++++----------------
1 file changed, 84 insertions(+), 84 deletions(-)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 2c3d5d4400ec..0d076c2a9f6c 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -98,7 +98,7 @@ static inline int get_sigset_t(sigset_t *set,
#define to_user_ptr(p) ptr_to_compat(p)
#define from_user_ptr(p) compat_ptr(p)
-static inline int save_general_regs(struct pt_regs *regs,
+static __always_inline int save_general_regs(struct pt_regs *regs,
struct mcontext __user *frame)
{
elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
@@ -113,10 +113,12 @@ static inline int save_general_regs(struct pt_regs *regs,
else
val = gregs[i];
- if (__put_user(val, &frame->mc_gregs[i]))
- return -EFAULT;
+ unsafe_put_user(val, &frame->mc_gregs[i], failed);
}
return 0;
+
+failed:
+ return 1;
}
static inline int restore_general_regs(struct pt_regs *regs,
@@ -151,11 +153,15 @@ static inline int get_sigset_t(sigset_t *set, const sigset_t __user *uset)
#define to_user_ptr(p) ((unsigned long)(p))
#define from_user_ptr(p) ((void __user *)(p))
-static inline int save_general_regs(struct pt_regs *regs,
+static __always_inline int save_general_regs(struct pt_regs *regs,
struct mcontext __user *frame)
{
WARN_ON(!FULL_REGS(regs));
- return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
+ unsafe_copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE, failed);
+ return 0;
+
+failed:
+ return 1;
}
static inline int restore_general_regs(struct pt_regs *regs,
@@ -258,16 +264,18 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
flush_spe_to_thread(current);
#endif
+ if (!user_write_access_begin(frame, sizeof(*frame)))
+ return 1;
+
/* save general registers */
if (save_general_regs(regs, frame))
- return 1;
+ goto failed;
#ifdef CONFIG_ALTIVEC
/* save altivec registers */
if (current->thread.used_vr) {
- if (__copy_to_user(&frame->mc_vregs, ¤t->thread.vr_state,
- ELF_NVRREG * sizeof(vector128)))
- return 1;
+ unsafe_copy_to_user(&frame->mc_vregs, ¤t->thread.vr_state,
+ ELF_NVRREG * sizeof(vector128), failed);
/* set MSR_VEC in the saved MSR value to indicate that
frame->mc_vregs contains valid data */
msr |= MSR_VEC;
@@ -280,11 +288,9 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
* most significant bits of that same vector. --BenH
* Note that the current VRSAVE value is in the SPR at this point.
*/
- if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32]))
- return 1;
+ unsafe_put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32], failed);
#endif /* CONFIG_ALTIVEC */
- if (copy_fpr_to_user(&frame->mc_fregs, current))
- return 1;
+ unsafe_copy_fpr_to_user(&frame->mc_fregs, current, failed);
/*
* Clear the MSR VSX bit to indicate there is no valid state attached
@@ -299,17 +305,15 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
* contains valid data
*/
if (current->thread.used_vsr && ctx_has_vsx_region) {
- if (copy_vsx_to_user(&frame->mc_vsregs, current))
- return 1;
+ unsafe_copy_vsx_to_user(&frame->mc_vsregs, current, failed);
msr |= MSR_VSX;
}
#endif /* CONFIG_VSX */
#ifdef CONFIG_SPE
/* save spe registers */
if (current->thread.used_spe) {
- if (__copy_to_user(&frame->mc_vregs, current->thread.evr,
- ELF_NEVRREG * sizeof(u32)))
- return 1;
+ unsafe_copy_to_user(&frame->mc_vregs, current->thread.evr,
+ ELF_NEVRREG * sizeof(u32)), failed);
/* set MSR_SPE in the saved MSR value to indicate that
frame->mc_vregs contains valid data */
msr |= MSR_SPE;
@@ -317,19 +321,18 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
/* else assert((regs->msr & MSR_SPE) == 0) */
/* We always copy to/from spefscr */
- if (__put_user(current->thread.spefscr, (u32 __user *)&frame->mc_vregs + ELF_NEVRREG))
- return 1;
+ unsafe_put_user(current->thread.spefscr, (u32 __user *)&frame->mc_vregs + ELF_NEVRREG, failed);
#endif /* CONFIG_SPE */
- if (__put_user(msr, &frame->mc_gregs[PT_MSR]))
- return 1;
+ unsafe_put_user(msr, &frame->mc_gregs[PT_MSR], failed);
if (sigret) {
/* Set up the sigreturn trampoline: li 0,sigret; sc */
- if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0])
- || __put_user(PPC_INST_SC, &frame->tramp[1]))
- return 1;
+ unsafe_put_user(PPC_INST_ADDI + sigret, &frame->tramp[0], failed);
+ unsafe_put_user(PPC_INST_SC, &frame->tramp[1], failed);
}
+ user_write_access_end();
+
if (sigret)
flush_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[2]);
@@ -341,6 +344,10 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
return 1;
return 0;
+
+failed:
+ user_write_access_end();
+ return 1;
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
@@ -369,15 +376,17 @@ static int save_tm_user_regs(struct pt_regs *regs,
flush_spe_to_thread(current);
#endif
- if (save_general_regs(¤t->thread.ckpt_regs, frame))
+ if (!user_write_access_begin(frame, sizeof(*frame)))
return 1;
+ if (save_general_regs(¤t->thread.ckpt_regs, frame))
+ goto failed;
+
#ifdef CONFIG_ALTIVEC
/* save altivec registers */
if (current->thread.used_vr) {
- if (__copy_to_user(&frame->mc_vregs, ¤t->thread.ckvr_state,
- ELF_NVRREG * sizeof(vector128)))
- return 1;
+ unsafe_copy_to_user(&frame->mc_vregs, ¤t->thread.ckvr_state,
+ ELF_NVRREG * sizeof(vector128), failed);
/* set MSR_VEC in the saved MSR value to indicate that
* frame->mc_vregs contains valid data
@@ -390,13 +399,11 @@ static int save_tm_user_regs(struct pt_regs *regs,
* significant bits of a vector, we "cheat" and stuff VRSAVE in the
* most significant bits of that same vector. --BenH
*/
- if (__put_user(current->thread.ckvrsave,
- (u32 __user *)&frame->mc_vregs[32]))
- return 1;
+ unsafe_put_user(current->thread.ckvrsave,
+ (u32 __user *)&frame->mc_vregs[32], failed);
#endif /* CONFIG_ALTIVEC */
- if (copy_ckfpr_to_user(&frame->mc_fregs, current))
- return 1;
+ unsafe_copy_ckfpr_to_user(&frame->mc_fregs, current, failed);
#ifdef CONFIG_VSX
/*
* Copy VSR 0-31 upper half from thread_struct to local
@@ -405,8 +412,7 @@ static int save_tm_user_regs(struct pt_regs *regs,
* contains valid data
*/
if (current->thread.used_vsr) {
- if (copy_ckvsx_to_user(&frame->mc_vsregs, current))
- return 1;
+ unsafe_copy_ckvsx_to_user(&frame->mc_vsregs, current, failed);
msr |= MSR_VSX;
}
#endif /* CONFIG_VSX */
@@ -415,91 +421,85 @@ static int save_tm_user_regs(struct pt_regs *regs,
* simply the same as in save_user_regs().
*/
if (current->thread.used_spe) {
- if (__copy_to_user(&frame->mc_vregs, current->thread.evr,
- ELF_NEVRREG * sizeof(u32)))
- return 1;
+ unsafe_copy_to_user(&frame->mc_vregs, current->thread.evr,
+ ELF_NEVRREG * sizeof(u32), failed);
/* set MSR_SPE in the saved MSR value to indicate that
* frame->mc_vregs contains valid data */
msr |= MSR_SPE;
}
/* We always copy to/from spefscr */
- if (__put_user(current->thread.spefscr, (u32 __user *)&frame->mc_vregs + ELF_NEVRREG))
- return 1;
+ unsafe_put_user(current->thread.spefscr,
+ (u32 __user *)&frame->mc_vregs + ELF_NEVRREG, failed);
#endif /* CONFIG_SPE */
- if (__put_user(msr, &frame->mc_gregs[PT_MSR]))
- return 1;
+ unsafe_put_user(msr, &frame->mc_gregs[PT_MSR], failed);
+
if (sigret) {
/* Set up the sigreturn trampoline: li 0,sigret; sc */
- if (__put_user(PPC_INST_ADDI + sigret, &frame->tramp[0])
- || __put_user(PPC_INST_SC, &frame->tramp[1]))
- return 1;
+ unsafe_put_user(PPC_INST_ADDI + sigret, &frame->tramp[0], failed);
+ unsafe_put_user(PPC_INST_SC, &frame->tramp[1], failed);
}
+ user_write_access_end();
+
if (sigret)
flush_icache_range((unsigned long) &frame->tramp[0],
(unsigned long) &frame->tramp[2]);
- if (save_general_regs(regs, tm_frame))
+ if (!user_write_access_begin(tm_frame, sizeof(*tm_frame)))
return 1;
+ if (save_general_regs(regs, tm_frame))
+ goto failed;
+
/* Stash the top half of the 64bit MSR into the 32bit MSR word
* of the transactional mcontext. This way we have a backward-compatible
* MSR in the 'normal' (checkpointed) mcontext and additionally one can
* also look at what type of transaction (T or S) was active at the
* time of the signal.
*/
- if (__put_user((msr >> 32), &tm_frame->mc_gregs[PT_MSR]))
- return 1;
+ unsafe_put_user((msr >> 32), &tm_frame->mc_gregs[PT_MSR], failed);
#ifdef CONFIG_ALTIVEC
if (current->thread.used_vr) {
- if (msr & MSR_VEC) {
- if (__copy_to_user(&tm_frame->mc_vregs,
- ¤t->thread.vr_state,
- ELF_NVRREG * sizeof(vector128)))
- return 1;
- } else {
- if (__copy_to_user(&tm_frame->mc_vregs,
- ¤t->thread.ckvr_state,
- ELF_NVRREG * sizeof(vector128)))
- return 1;
- }
+ if (msr & MSR_VEC)
+ unsafe_copy_to_user(&tm_frame->mc_vregs,
+ ¤t->thread.vr_state,
+ ELF_NVRREG * sizeof(vector128), failed);
+ else
+ unsafe_copy_to_user(&tm_frame->mc_vregs,
+ ¤t->thread.ckvr_state,
+ ELF_NVRREG * sizeof(vector128), failed);
}
- if (msr & MSR_VEC) {
- if (__put_user(current->thread.vrsave,
- (u32 __user *)&tm_frame->mc_vregs[32]))
- return 1;
- } else {
- if (__put_user(current->thread.ckvrsave,
- (u32 __user *)&tm_frame->mc_vregs[32]))
- return 1;
- }
+ if (msr & MSR_VEC)
+ unsafe_put_user(current->thread.vrsave,
+ (u32 __user *)&tm_frame->mc_vregs[32], failed);
+ else
+ unsafe_put_user(current->thread.ckvrsave,
+ (u32 __user *)&tm_frame->mc_vregs[32], failed);
#endif /* CONFIG_ALTIVEC */
- if (msr & MSR_FP) {
- if (copy_fpr_to_user(&tm_frame->mc_fregs, current))
- return 1;
- } else {
- if (copy_ckfpr_to_user(&tm_frame->mc_fregs, current))
- return 1;
- }
+ if (msr & MSR_FP)
+ unsafe_copy_fpr_to_user(&tm_frame->mc_fregs, current, failed);
+ else
+ unsafe_copy_ckfpr_to_user(&tm_frame->mc_fregs, current, failed);
#ifdef CONFIG_VSX
if (current->thread.used_vsr) {
- if (msr & MSR_VSX) {
- if (copy_vsx_to_user(&tm_frame->mc_vsregs,
- current))
- return 1;
- } else {
- if (copy_ckvsx_to_user(&tm_frame->mc_vsregs, current))
- return 1;
- }
+ if (msr & MSR_VSX)
+ unsafe_copy_vsx_to_user(&tm_frame->mc_vsregs, current, failed);
+ else
+ unsafe_copy_ckvsx_to_user(&tm_frame->mc_vsregs, current, failed);
}
#endif /* CONFIG_VSX */
+ user_write_access_end();
return 0;
+
+failed:
+ user_write_access_end();
+ return 1;
}
#endif
--
2.25.0
^ permalink raw reply related
* [RFC PATCH v1 13/19] powerpc/signal32: Create 'unsafe' versions of copy_[ck][fpr/vsx]_to_user()
From: Christophe Leroy @ 2020-08-12 12:06 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman, ldv,
viro
Cc: linuxppc-dev, linux-kernel
In-Reply-To: <cover.1597233555.git.christophe.leroy@csgroup.eu>
For the non VSX version, that's trivial. Just use unsafe_copy_to_user()
instead of __copy_to_user().
For the VSX version, remove the intermediate step through a buffer and
use unsafe_put_user() directly. This generates a far smaller code which
is acceptable to inline, see below:
Standard VSX version:
0000000000000000 <.copy_fpr_to_user>:
0: 7c 08 02 a6 mflr r0
4: fb e1 ff f8 std r31,-8(r1)
8: 39 00 00 20 li r8,32
c: 39 24 0b 80 addi r9,r4,2944
10: 7d 09 03 a6 mtctr r8
14: f8 01 00 10 std r0,16(r1)
18: f8 21 fe 71 stdu r1,-400(r1)
1c: 39 41 00 68 addi r10,r1,104
20: e9 09 00 00 ld r8,0(r9)
24: 39 4a 00 08 addi r10,r10,8
28: 39 29 00 10 addi r9,r9,16
2c: f9 0a 00 00 std r8,0(r10)
30: 42 00 ff f0 bdnz 20 <.copy_fpr_to_user+0x20>
34: e9 24 0d 80 ld r9,3456(r4)
38: 3d 42 00 00 addis r10,r2,0
3a: R_PPC64_TOC16_HA .toc
3c: eb ea 00 00 ld r31,0(r10)
3e: R_PPC64_TOC16_LO_DS .toc
40: f9 21 01 70 std r9,368(r1)
44: e9 3f 00 00 ld r9,0(r31)
48: 81 29 00 20 lwz r9,32(r9)
4c: 2f 89 00 00 cmpwi cr7,r9,0
50: 40 9c 00 18 bge cr7,68 <.copy_fpr_to_user+0x68>
54: 4c 00 01 2c isync
58: 3d 20 40 00 lis r9,16384
5c: 79 29 07 c6 rldicr r9,r9,32,31
60: 7d 3d 03 a6 mtspr 29,r9
64: 4c 00 01 2c isync
68: 38 a0 01 08 li r5,264
6c: 38 81 00 70 addi r4,r1,112
70: 48 00 00 01 bl 70 <.copy_fpr_to_user+0x70>
70: R_PPC64_REL24 .__copy_tofrom_user
74: 60 00 00 00 nop
78: e9 3f 00 00 ld r9,0(r31)
7c: 81 29 00 20 lwz r9,32(r9)
80: 2f 89 00 00 cmpwi cr7,r9,0
84: 40 9c 00 18 bge cr7,9c <.copy_fpr_to_user+0x9c>
88: 4c 00 01 2c isync
8c: 39 20 ff ff li r9,-1
90: 79 29 00 44 rldicr r9,r9,0,1
94: 7d 3d 03 a6 mtspr 29,r9
98: 4c 00 01 2c isync
9c: 38 21 01 90 addi r1,r1,400
a0: e8 01 00 10 ld r0,16(r1)
a4: eb e1 ff f8 ld r31,-8(r1)
a8: 7c 08 03 a6 mtlr r0
ac: 4e 80 00 20 blr
'unsafe' simulated VSX version (The ... are only nops) using
unsafe_copy_fpr_to_user() macro:
unsigned long copy_fpr_to_user(void __user *to,
struct task_struct *task)
{
unsafe_copy_fpr_to_user(to, task, failed);
return 0;
failed:
return 1;
}
0000000000000000 <.copy_fpr_to_user>:
0: 39 00 00 20 li r8,32
4: 39 44 0b 80 addi r10,r4,2944
8: 7d 09 03 a6 mtctr r8
c: 7c 69 1b 78 mr r9,r3
...
20: e9 0a 00 00 ld r8,0(r10)
24: f9 09 00 00 std r8,0(r9)
28: 39 4a 00 10 addi r10,r10,16
2c: 39 29 00 08 addi r9,r9,8
30: 42 00 ff f0 bdnz 20 <.copy_fpr_to_user+0x20>
34: e9 24 0d 80 ld r9,3456(r4)
38: f9 23 01 00 std r9,256(r3)
3c: 38 60 00 00 li r3,0
40: 4e 80 00 20 blr
...
50: 38 60 00 01 li r3,1
54: 4e 80 00 20 blr
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
arch/powerpc/kernel/signal.h | 47 ++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h
index f610cfafa478..abe570f06c12 100644
--- a/arch/powerpc/kernel/signal.h
+++ b/arch/powerpc/kernel/signal.h
@@ -32,7 +32,49 @@ unsigned long copy_fpr_to_user(void __user *to, struct task_struct *task);
unsigned long copy_ckfpr_to_user(void __user *to, struct task_struct *task);
unsigned long copy_fpr_from_user(struct task_struct *task, void __user *from);
unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);
+
+#define unsafe_copy_fpr_to_user(to, task, label) do { \
+ u64 __user *buf = (u64 __user *)to; \
+ int i; \
+ \
+ for (i = 0; i < ELF_NFPREG - 1 ; i++) \
+ unsafe_put_user(task->thread.TS_FPR(i), &buf[i], label);\
+ unsafe_put_user(task->thread.fp_state.fpscr, &buf[i], label); \
+} while (0)
+
+#define unsafe_copy_vsx_to_user(to, task, label) do { \
+ u64 __user *buf = (u64 __user *)to; \
+ int i; \
+ \
+ for (i = 0; i < ELF_NVSRHALFREG ; i++) \
+ unsafe_put_user(task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET], \
+ &buf[i], label);\
+} while (0)
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+#define unsafe_copy_ckfpr_to_user(to, task, label) do { \
+ u64 __user *buf = (u64 __user *)to; \
+ int i; \
+ \
+ for (i = 0; i < ELF_NFPREG - 1 ; i++) \
+ unsafe_put_user(task->thread.TS_CKFPR(i), &buf[i], label);\
+ unsafe_put_user(task->thread.ckfp_state.fpscr, &buf[i], label); \
+} while (0)
+
+#define unsafe_copy_ckvsx_to_user(to, task, label) do { \
+ u64 __user *buf = (u64 __user *)to; \
+ int i; \
+ \
+ for (i = 0; i < ELF_NVSRHALFREG ; i++) \
+ unsafe_put_user(task->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET], \
+ &buf[i], label);\
+} while (0)
+#endif
#elif defined(CONFIG_PPC_FPU_REGS)
+
+#define unsafe_copy_fpr_to_user(to, task, label) \
+ unsafe_copy_to_user(to, task->thread.fp_state.fpr, ELF_NFPREG * sizeof(double), label)
+
static inline unsigned long
copy_fpr_to_user(void __user *to, struct task_struct *task)
{
@@ -48,6 +90,9 @@ copy_fpr_from_user(struct task_struct *task, void __user *from)
}
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+#define unsafe_copy_ckfpr_to_user(to, task, label) \
+ unsafe_copy_to_user(to, task->thread.ckfp_state.fpr, ELF_NFPREG * sizeof(double), label)
+
inline unsigned long copy_ckfpr_to_user(void __user *to, struct task_struct *task)
{
return __copy_to_user(to, task->thread.ckfp_state.fpr,
@@ -62,6 +107,8 @@ copy_ckfpr_from_user(struct task_struct *task, void __user *from)
}
#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
#else
+#define unsafe_copy_fpr_to_user(to, task, label) do { } while (0)
+
static inline unsigned long
copy_fpr_to_user(void __user *to, struct task_struct *task)
{
--
2.25.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox