* [PATCH v2] mmc: sdhci-of-arasan: Add quirk for unstable clocks
From: Adrian Hunter @ 2018-06-19 9:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180615081830.zrgx6arwn6ga27gu@laureti-dev>
On 15/06/18 11:18, Helmut Grohne wrote:
> Some controllers immediately report SDHCI_CLOCK_INT_STABLE after
> enabling the clock even when the clock is not stable. When used in
> conjunction with older/slower cards, this can result in:
>
> mmc0: error -84 whilst initialising SD card
>
> When the stable reporting is broken, we simply wait for the maximum
> stabilization period.
>
> Signed-off-by: Helmut Grohne <h.grohne@intenta.de>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 2 ++
> drivers/mmc/host/sdhci-of-arasan.c | 16 ++++++++++++++++
> 2 files changed, 18 insertions(+)
>
> Changes since v1 (RFC):
> * Use an arasan-specific quirk in the ->set_clock() callback as requested by
> Adrian Hunter.
>
> diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> index 60481bfc3d31..c0e0f04a8504 100644
> --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
> @@ -39,6 +39,8 @@ Optional Properties:
> - xlnx,fails-without-test-cd: when present, the controller doesn't work when
> the CD line is not connected properly, and the line is not connected
> properly. Test mode can be used to force the controller to function.
> + - xlnx,int-clock-stable-broken: when present, the controller always reports
> + that the internal clock is stable even when it is not.
>
> Example:
> sdhci at e0100000 {
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index c33a5f7393bd..f7fe26c75150 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -102,6 +102,9 @@ struct sdhci_arasan_data {
>
> /* Controller does not have CD wired and will not function normally without */
> #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
> +/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the
> + * internal clock even when the clock isn't stable */
> +#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1)
> };
>
> static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
> @@ -207,6 +210,16 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
>
> sdhci_set_clock(host, clock);
>
> + if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE)
> + /*
> + * Some controllers immediately report SDHCI_CLOCK_INT_STABLE
> + * after enabling the clock even though the clock is not
> + * stable. Trying to use a clock without waiting here results
> + * in EILSEQ while detecting some older/slower cards. The
> + * chosen delay is the maximum delay from sdhci_set_clock.
> + */
> + msleep(20);
> +
> if (ctrl_phy) {
> phy_power_on(sdhci_arasan->phy);
> sdhci_arasan->is_phy_on = true;
> @@ -759,6 +772,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
> if (of_property_read_bool(np, "xlnx,fails-without-test-cd"))
> sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST;
>
> + if (of_property_read_bool(np, "xlnx,int-clock-stable-broken"))
> + sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE;
> +
> pltfm_host->clk = clk_xin;
>
> if (of_device_is_compatible(pdev->dev.of_node,
>
^ permalink raw reply
* [PATCH] arm64/mm: Introduce a variable to hold base address of linear region
From: Jin, Yanjiang @ 2018-06-19 9:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619085541.GB13984@arm.com>
> -----Original Message-----
> From: Will Deacon [mailto:will.deacon at arm.com]
> Sent: 2018?6?19? 16:56
> To: Jin, Yanjiang <yanjiang.jin@hxt-semitech.com>
> Cc: James Morse <james.morse@arm.com>; Bhupesh Sharma
> <bhsharma@redhat.com>; Mark Rutland <mark.rutland@arm.com>; Ard
> Biesheuvel <ard.biesheuvel@linaro.org>; Catalin Marinas
> <catalin.marinas@arm.com>; Kexec Mailing List <kexec@lists.infradead.org>;
> AKASHI Takahiro <takahiro.akashi@linaro.org>; Bhupesh SHARMA
> <bhupesh.linux@gmail.com>; linux-arm-kernel <linux-arm-
> kernel at lists.infradead.org>
> Subject: Re: [PATCH] arm64/mm: Introduce a variable to hold base address of
> linear region
>
> On Tue, Jun 19, 2018 at 03:02:15AM +0000, Jin, Yanjiang wrote:
> > > You seem to be using this for user-space phys_to_virt() based on
> > > values found in /proc/iomem. This should give you what you want, and
> > > isolate your user-space from the kernel's unexpected naming of variables.
> >
> > I don't know could I simplify this problem?
> > Let's ignore what memstart_addr represents here, we just want to
> > implement
> > phys_to_virt() in an userspace applications(kexec-tools or others).
> >
> > ARM64 Kernel has a below definition:
> >
> > #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) |
> PAGE_OFFSET)
> >
> > So userspace app must know PHYS_OFFSET(equal to memstart_addr now).
> > Seems this is very simple, but memstart_addr has gone through several
> > operations in arm64_memblock_init() depends on different Kernel
> > configurations, so userspace app needs to know many additional definitions as
> following:
> >
> > memblock_start_of_DRAM(), (ifdef CONFIG_SPARSEMEM_VMEMMAP),
> > ARM64_MEMSTART_SHIFT, SECTION_SIZE_BITS, PAGE_OFFSET,
> > memblock_end_of_DRAM(), IS_ENABLED(CONFIG_RANDOMIZE_BASE),
> > memstart_offset_seed.
> >
> > It is hard to know all above in kexec-tools now. Originally I planned
> > to read memstart_addr's value from "/dev/mem", but someone thought not
> > all Kernels enable "/dev/mem", we'd better find a more generic
> > approach. So we want to get some suggestions from ARM kernel community.
> > Can we export this variable in Kernel side through sysconf() or other
> > similar methods? Or someone can provide an effect way to get
> > memstart_addr's value?
>
> I thought the suggestion from James was to expose this via an ELF NOTE in kcore
> and vmcore (or in the header directly if that's possible, but I'm not sure about it)?
Hi Will,
Thanks for your reply firstly. But same as DEVMEM, kcore is not a must-have, so we can't depend on it.
On the other hand, phys_to_virt() is called during generating vmcore in Kexec-tools, vmcore also can't help this issue.
Unfortunately, not all platforms support analyzing Kernel config in userspace application, so Kexec-tools can't know some key kernel options. If not so, we can simulate the whole arm64_memblock_init() progress in kexec-tools.
Thanks,
Yanjiang
>
> Will
This email is intended only for the named addressee. It may contain information that is confidential/private, legally privileged, or copyright-protected, and you should handle it accordingly. If you are not the intended recipient, you do not have legal rights to retain, copy, or distribute this email or its contents, and should promptly delete the email and all electronic copies in your system; do not retain copies in any media. If you have received this email in error, please notify the sender promptly. Thank you.
^ permalink raw reply
* [PATCH] arm64/mm: Introduce a variable to hold base address of linear region
From: Will Deacon @ 2018-06-19 9:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6860c284f1c74faa95edd6cf866ec80d@HXTBJIDCEMVIW02.hxtcorp.net>
On Tue, Jun 19, 2018 at 09:34:56AM +0000, Jin, Yanjiang wrote:
> > On Tue, Jun 19, 2018 at 03:02:15AM +0000, Jin, Yanjiang wrote:
> > > > You seem to be using this for user-space phys_to_virt() based on
> > > > values found in /proc/iomem. This should give you what you want, and
> > > > isolate your user-space from the kernel's unexpected naming of variables.
> > >
> > > I don't know could I simplify this problem?
> > > Let's ignore what memstart_addr represents here, we just want to
> > > implement
> > > phys_to_virt() in an userspace applications(kexec-tools or others).
> > >
> > > ARM64 Kernel has a below definition:
> > >
> > > #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) |
> > PAGE_OFFSET)
> > >
> > > So userspace app must know PHYS_OFFSET(equal to memstart_addr now).
> > > Seems this is very simple, but memstart_addr has gone through several
> > > operations in arm64_memblock_init() depends on different Kernel
> > > configurations, so userspace app needs to know many additional definitions as
> > following:
> > >
> > > memblock_start_of_DRAM(), (ifdef CONFIG_SPARSEMEM_VMEMMAP),
> > > ARM64_MEMSTART_SHIFT, SECTION_SIZE_BITS, PAGE_OFFSET,
> > > memblock_end_of_DRAM(), IS_ENABLED(CONFIG_RANDOMIZE_BASE),
> > > memstart_offset_seed.
> > >
> > > It is hard to know all above in kexec-tools now. Originally I planned
> > > to read memstart_addr's value from "/dev/mem", but someone thought not
> > > all Kernels enable "/dev/mem", we'd better find a more generic
> > > approach. So we want to get some suggestions from ARM kernel community.
> > > Can we export this variable in Kernel side through sysconf() or other
> > > similar methods? Or someone can provide an effect way to get
> > > memstart_addr's value?
> >
> > I thought the suggestion from James was to expose this via an ELF NOTE in kcore
> > and vmcore (or in the header directly if that's possible, but I'm not sure about it)?
>
> Thanks for your reply firstly. But same as DEVMEM, kcore is not a
> must-have, so we can't depend on it.
Neither is KEXEC. We can select PROC_KCORE from KEXEC if it helps.
> On the other hand, phys_to_virt() is called during generating vmcore in
> Kexec-tools, vmcore also can't help this issue.
I don't understand this part. If you have the vmcore in your hand, why can't
you grok the pv offset from the note and use that in phys_to_virt()?
> Unfortunately, not all platforms support analyzing Kernel config in
> userspace application, so Kexec-tools can't know some key kernel options.
> If not so, we can simulate the whole arm64_memblock_init() progress in
> kexec-tools.
I don't understand what the kernel config has to do with kexec tools.
Will
^ permalink raw reply
* [PATCH 0/2] KVM: Fix !KVM_COMPAT ioctl behaviour
From: Marc Zyngier @ 2018-06-19 9:42 UTC (permalink / raw)
To: linux-arm-kernel
Mark Rutland recently pointed out that a couple of issues with the way
we deal with compat tasks:
1) arm64 unexpectedly selects KVM_COMPAT, while offering no way for a
32bit userspace to change any register
2) Even deselecting KVM_COMPAT, we end-up with compat_ioctl being NULL,
and the VFS behaviour is thus to call the non-compat ioctl
(1) is completely unintentional and should be fixed. (2) is quite odd,
and probably just as unintentional. This series fixes it by installing
a compat_ioctl callback whose only job is to return -EINVAL.
Tested on arm64 with a 32bit kvmtool.
Marc Zyngier (2):
KVM: Enforce error in ioctl for compat tasks when !KVM_COMPAT
KVM: arm64: Prevent KVM_COMPAT from being selected
virt/kvm/Kconfig | 2 +-
virt/kvm/kvm_main.c | 19 +++++++++----------
2 files changed, 10 insertions(+), 11 deletions(-)
--
2.17.1
^ permalink raw reply
* [PATCH 1/2] KVM: Enforce error in ioctl for compat tasks when !KVM_COMPAT
From: Marc Zyngier @ 2018-06-19 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619094251.8586-1-marc.zyngier@arm.com>
The current behaviour of the compat ioctls is a bit odd.
We provide a compat_ioctl method when KVM_COMPAT is set, and NULL
otherwise. But NULL means that the normal, non-compat ioctl should
be used directly for compat tasks, and there is no way to actually
prevent a compat task from issueing KVM ioctls.
This patch changes this behaviour, by always registering a compat_ioctl
method, even if KVM_COMPAT is not selected. In that case, the callback
will always return -EINVAL.
Reported-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/kvm_main.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index ada21f47f22b..8b47507faab5 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -116,6 +116,11 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
#ifdef CONFIG_KVM_COMPAT
static long kvm_vcpu_compat_ioctl(struct file *file, unsigned int ioctl,
unsigned long arg);
+#define KVM_COMPAT(c) .compat_ioctl = (c)
+#else
+static long kvm_no_compat_ioctl(struct file *file, unsigned int ioctl,
+ unsigned long arg) { return -EINVAL; }
+#define KVM_COMPAT(c) .compat_ioctl = kvm_no_compat_ioctl
#endif
static int hardware_enable_all(void);
static void hardware_disable_all(void);
@@ -2396,11 +2401,9 @@ static int kvm_vcpu_release(struct inode *inode, struct file *filp)
static struct file_operations kvm_vcpu_fops = {
.release = kvm_vcpu_release,
.unlocked_ioctl = kvm_vcpu_ioctl,
-#ifdef CONFIG_KVM_COMPAT
- .compat_ioctl = kvm_vcpu_compat_ioctl,
-#endif
.mmap = kvm_vcpu_mmap,
.llseek = noop_llseek,
+ KVM_COMPAT(kvm_vcpu_compat_ioctl),
};
/*
@@ -2824,10 +2827,8 @@ static int kvm_device_release(struct inode *inode, struct file *filp)
static const struct file_operations kvm_device_fops = {
.unlocked_ioctl = kvm_device_ioctl,
-#ifdef CONFIG_KVM_COMPAT
- .compat_ioctl = kvm_device_ioctl,
-#endif
.release = kvm_device_release,
+ KVM_COMPAT(kvm_device_ioctl),
};
struct kvm_device *kvm_device_from_filp(struct file *filp)
@@ -3165,10 +3166,8 @@ static long kvm_vm_compat_ioctl(struct file *filp,
static struct file_operations kvm_vm_fops = {
.release = kvm_vm_release,
.unlocked_ioctl = kvm_vm_ioctl,
-#ifdef CONFIG_KVM_COMPAT
- .compat_ioctl = kvm_vm_compat_ioctl,
-#endif
.llseek = noop_llseek,
+ KVM_COMPAT(kvm_vm_compat_ioctl),
};
static int kvm_dev_ioctl_create_vm(unsigned long type)
@@ -3259,8 +3258,8 @@ static long kvm_dev_ioctl(struct file *filp,
static struct file_operations kvm_chardev_ops = {
.unlocked_ioctl = kvm_dev_ioctl,
- .compat_ioctl = kvm_dev_ioctl,
.llseek = noop_llseek,
+ KVM_COMPAT(kvm_dev_ioctl),
};
static struct miscdevice kvm_dev = {
--
2.17.1
^ permalink raw reply related
* [PATCH 2/2] KVM: arm64: Prevent KVM_COMPAT from being selected
From: Marc Zyngier @ 2018-06-19 9:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619094251.8586-1-marc.zyngier@arm.com>
There is very little point in trying to support the 32bit KVM/arm API
on arm64, and this was never an anticipated use case.
Let's make it clear by not selecting KVM_COMPAT.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 72143cfaf6ec..ea434ddc8499 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -47,7 +47,7 @@ config KVM_GENERIC_DIRTYLOG_READ_PROTECT
config KVM_COMPAT
def_bool y
- depends on KVM && COMPAT && !S390
+ depends on KVM && COMPAT && !(S390 || ARM64)
config HAVE_KVM_IRQ_BYPASS
bool
--
2.17.1
^ permalink raw reply related
* [PATCH] arm64/mm: Introduce a variable to hold base address of linear region
From: Jin, Yanjiang @ 2018-06-19 9:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619094045.GD13984@arm.com>
> -----Original Message-----
> From: Will Deacon [mailto:will.deacon at arm.com]
> Sent: 2018?6?19? 17:41
> To: Jin, Yanjiang <yanjiang.jin@hxt-semitech.com>
> Cc: James Morse <james.morse@arm.com>; Bhupesh Sharma
> <bhsharma@redhat.com>; Mark Rutland <mark.rutland@arm.com>; Ard
> Biesheuvel <ard.biesheuvel@linaro.org>; Catalin Marinas
> <catalin.marinas@arm.com>; Kexec Mailing List <kexec@lists.infradead.org>;
> AKASHI Takahiro <takahiro.akashi@linaro.org>; Bhupesh SHARMA
> <bhupesh.linux@gmail.com>; linux-arm-kernel <linux-arm-
> kernel at lists.infradead.org>
> Subject: Re: [PATCH] arm64/mm: Introduce a variable to hold base address of
> linear region
>
> On Tue, Jun 19, 2018 at 09:34:56AM +0000, Jin, Yanjiang wrote:
> > > On Tue, Jun 19, 2018 at 03:02:15AM +0000, Jin, Yanjiang wrote:
> > > > > You seem to be using this for user-space phys_to_virt() based on
> > > > > values found in /proc/iomem. This should give you what you want,
> > > > > and isolate your user-space from the kernel's unexpected naming of
> variables.
> > > >
> > > > I don't know could I simplify this problem?
> > > > Let's ignore what memstart_addr represents here, we just want to
> > > > implement
> > > > phys_to_virt() in an userspace applications(kexec-tools or others).
> > > >
> > > > ARM64 Kernel has a below definition:
> > > >
> > > > #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) |
> > > PAGE_OFFSET)
> > > >
> > > > So userspace app must know PHYS_OFFSET(equal to memstart_addr now).
> > > > Seems this is very simple, but memstart_addr has gone through
> > > > several operations in arm64_memblock_init() depends on different
> > > > Kernel configurations, so userspace app needs to know many
> > > > additional definitions as
> > > following:
> > > >
> > > > memblock_start_of_DRAM(), (ifdef CONFIG_SPARSEMEM_VMEMMAP),
> > > > ARM64_MEMSTART_SHIFT, SECTION_SIZE_BITS, PAGE_OFFSET,
> > > > memblock_end_of_DRAM(), IS_ENABLED(CONFIG_RANDOMIZE_BASE),
> > > > memstart_offset_seed.
> > > >
> > > > It is hard to know all above in kexec-tools now. Originally I
> > > > planned to read memstart_addr's value from "/dev/mem", but someone
> > > > thought not all Kernels enable "/dev/mem", we'd better find a more
> > > > generic approach. So we want to get some suggestions from ARM kernel
> community.
> > > > Can we export this variable in Kernel side through sysconf() or
> > > > other similar methods? Or someone can provide an effect way to get
> > > > memstart_addr's value?
> > >
> > > I thought the suggestion from James was to expose this via an ELF
> > > NOTE in kcore and vmcore (or in the header directly if that's possible, but I'm
> not sure about it)?
> >
> > Thanks for your reply firstly. But same as DEVMEM, kcore is not a
> > must-have, so we can't depend on it.
>
> Neither is KEXEC. We can select PROC_KCORE from KEXEC if it helps.
>
> > On the other hand, phys_to_virt() is called during generating vmcore
> > in Kexec-tools, vmcore also can't help this issue.
>
> I don't understand this part. If you have the vmcore in your hand, why can't you
> grok the pv offset from the note and use that in phys_to_virt()?
It is a chicken-and-egg issue.
phys_to virt() is for crashdump setup. To generate vmcore, we must call phys_to_virt(). At this point, no vmcore exists.
Yanjiang
>
> > Unfortunately, not all platforms support analyzing Kernel config in
> > userspace application, so Kexec-tools can't know some key kernel options.
> > If not so, we can simulate the whole arm64_memblock_init() progress
> > in kexec-tools.
>
> I don't understand what the kernel config has to do with kexec tools.
I mean that if we can know kernel .config in all circumstances, we can calculate memstart_addr as below in Kexec-tools:
memstart_addr = round_down(memblock_start_of_DRAM(),
ARM64_MEMSTART_ALIGN);
#if defined(CONFIG_SPARSEMEM_VMEMMAP) && ARM64_MEMSTART_SHIFT < SECTION_SIZE_BITS
#define ARM64_MEMSTART_ALIGN (1UL << SECTION_SIZE_BITS)
......
#endif
#define ARM64_MEMSTART_SHIFT PMD_SHIFT
#if CONFIG_PGTABLE_LEVELS > 2
#define PMD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(2)
...........
#endif
Yanjiang
>
> Will
This email is intended only for the named addressee. It may contain information that is confidential/private, legally privileged, or copyright-protected, and you should handle it accordingly. If you are not the intended recipient, you do not have legal rights to retain, copy, or distribute this email or its contents, and should promptly delete the email and all electronic copies in your system; do not retain copies in any media. If you have received this email in error, please notify the sender promptly. Thank you.
^ permalink raw reply
* [PATCH 1/2] KVM: Enforce error in ioctl for compat tasks when !KVM_COMPAT
From: Mark Rutland @ 2018-06-19 10:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619094251.8586-2-marc.zyngier@arm.com>
On Tue, Jun 19, 2018 at 10:42:50AM +0100, Marc Zyngier wrote:
> The current behaviour of the compat ioctls is a bit odd.
> We provide a compat_ioctl method when KVM_COMPAT is set, and NULL
> otherwise. But NULL means that the normal, non-compat ioctl should
> be used directly for compat tasks, and there is no way to actually
> prevent a compat task from issueing KVM ioctls.
>
> This patch changes this behaviour, by always registering a compat_ioctl
> method, even if KVM_COMPAT is not selected. In that case, the callback
> will always return -EINVAL.
>
> Reported-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
I virt/kvm/Kconfig we have:
config KVM_COMPAT
def_bool y
depends on KVM && COMPAT && !S390
... and in arch/s390 we have COMPAT support, so does this potentially break
anything there?
Thanks,
Mark.
> ---
> virt/kvm/kvm_main.c | 19 +++++++++----------
> 1 file changed, 9 insertions(+), 10 deletions(-)
>
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index ada21f47f22b..8b47507faab5 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -116,6 +116,11 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
> #ifdef CONFIG_KVM_COMPAT
> static long kvm_vcpu_compat_ioctl(struct file *file, unsigned int ioctl,
> unsigned long arg);
> +#define KVM_COMPAT(c) .compat_ioctl = (c)
> +#else
> +static long kvm_no_compat_ioctl(struct file *file, unsigned int ioctl,
> + unsigned long arg) { return -EINVAL; }
> +#define KVM_COMPAT(c) .compat_ioctl = kvm_no_compat_ioctl
> #endif
> static int hardware_enable_all(void);
> static void hardware_disable_all(void);
> @@ -2396,11 +2401,9 @@ static int kvm_vcpu_release(struct inode *inode, struct file *filp)
> static struct file_operations kvm_vcpu_fops = {
> .release = kvm_vcpu_release,
> .unlocked_ioctl = kvm_vcpu_ioctl,
> -#ifdef CONFIG_KVM_COMPAT
> - .compat_ioctl = kvm_vcpu_compat_ioctl,
> -#endif
> .mmap = kvm_vcpu_mmap,
> .llseek = noop_llseek,
> + KVM_COMPAT(kvm_vcpu_compat_ioctl),
> };
>
> /*
> @@ -2824,10 +2827,8 @@ static int kvm_device_release(struct inode *inode, struct file *filp)
>
> static const struct file_operations kvm_device_fops = {
> .unlocked_ioctl = kvm_device_ioctl,
> -#ifdef CONFIG_KVM_COMPAT
> - .compat_ioctl = kvm_device_ioctl,
> -#endif
> .release = kvm_device_release,
> + KVM_COMPAT(kvm_device_ioctl),
> };
>
> struct kvm_device *kvm_device_from_filp(struct file *filp)
> @@ -3165,10 +3166,8 @@ static long kvm_vm_compat_ioctl(struct file *filp,
> static struct file_operations kvm_vm_fops = {
> .release = kvm_vm_release,
> .unlocked_ioctl = kvm_vm_ioctl,
> -#ifdef CONFIG_KVM_COMPAT
> - .compat_ioctl = kvm_vm_compat_ioctl,
> -#endif
> .llseek = noop_llseek,
> + KVM_COMPAT(kvm_vm_compat_ioctl),
> };
>
> static int kvm_dev_ioctl_create_vm(unsigned long type)
> @@ -3259,8 +3258,8 @@ static long kvm_dev_ioctl(struct file *filp,
>
> static struct file_operations kvm_chardev_ops = {
> .unlocked_ioctl = kvm_dev_ioctl,
> - .compat_ioctl = kvm_dev_ioctl,
> .llseek = noop_llseek,
> + KVM_COMPAT(kvm_dev_ioctl),
> };
>
> static struct miscdevice kvm_dev = {
> --
> 2.17.1
>
^ permalink raw reply
* [PATCH] iommu/io-pgtable-arm: Fix pgtable allocation in selftest
From: Will Deacon @ 2018-06-19 10:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180618112754.24942-1-jean-philippe.brucker@arm.com>
On Mon, Jun 18, 2018 at 12:27:54PM +0100, Jean-Philippe Brucker wrote:
> Commit 4b123757eeaa ("iommu/io-pgtable-arm: Make allocations
> NUMA-aware") added a NUMA hint to page table allocation, but the pgtable
> selftest doesn't provide an SMMU device parameter. Since dev_to_node
> doesn't accept a NULL argument, add a special case for selftest.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
> ---
> drivers/iommu/io-pgtable-arm.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
Thanks, I'll pick this up.
Will
^ permalink raw reply
* [PATCH 1/2] KVM: Enforce error in ioctl for compat tasks when !KVM_COMPAT
From: Marc Zyngier @ 2018-06-19 10:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619100134.4loab4jtnfdzrpxu@lakrids.cambridge.arm.com>
On 19/06/18 11:01, Mark Rutland wrote:
> On Tue, Jun 19, 2018 at 10:42:50AM +0100, Marc Zyngier wrote:
>> The current behaviour of the compat ioctls is a bit odd.
>> We provide a compat_ioctl method when KVM_COMPAT is set, and NULL
>> otherwise. But NULL means that the normal, non-compat ioctl should
>> be used directly for compat tasks, and there is no way to actually
>> prevent a compat task from issueing KVM ioctls.
>>
>> This patch changes this behaviour, by always registering a compat_ioctl
>> method, even if KVM_COMPAT is not selected. In that case, the callback
>> will always return -EINVAL.
>>
>> Reported-by: Mark Rutland <mark.rutland@arm.com>
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>
> I virt/kvm/Kconfig we have:
>
> config KVM_COMPAT
> def_bool y
> depends on KVM && COMPAT && !S390
>
> ... and in arch/s390 we have COMPAT support, so does this potentially break
> anything there?
It doesn't seem to (COMPAT seems to support the 31 bit stuff, which I
don't think ever had KVM support), but my s390-foo is quite basic.
Christian, could you help here?
Thanks,
M.
>
> Thanks,
> Mark.
>
>> ---
>> virt/kvm/kvm_main.c | 19 +++++++++----------
>> 1 file changed, 9 insertions(+), 10 deletions(-)
>>
>> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
>> index ada21f47f22b..8b47507faab5 100644
>> --- a/virt/kvm/kvm_main.c
>> +++ b/virt/kvm/kvm_main.c
>> @@ -116,6 +116,11 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
>> #ifdef CONFIG_KVM_COMPAT
>> static long kvm_vcpu_compat_ioctl(struct file *file, unsigned int ioctl,
>> unsigned long arg);
>> +#define KVM_COMPAT(c) .compat_ioctl = (c)
>> +#else
>> +static long kvm_no_compat_ioctl(struct file *file, unsigned int ioctl,
>> + unsigned long arg) { return -EINVAL; }
>> +#define KVM_COMPAT(c) .compat_ioctl = kvm_no_compat_ioctl
>> #endif
>> static int hardware_enable_all(void);
>> static void hardware_disable_all(void);
>> @@ -2396,11 +2401,9 @@ static int kvm_vcpu_release(struct inode *inode, struct file *filp)
>> static struct file_operations kvm_vcpu_fops = {
>> .release = kvm_vcpu_release,
>> .unlocked_ioctl = kvm_vcpu_ioctl,
>> -#ifdef CONFIG_KVM_COMPAT
>> - .compat_ioctl = kvm_vcpu_compat_ioctl,
>> -#endif
>> .mmap = kvm_vcpu_mmap,
>> .llseek = noop_llseek,
>> + KVM_COMPAT(kvm_vcpu_compat_ioctl),
>> };
>>
>> /*
>> @@ -2824,10 +2827,8 @@ static int kvm_device_release(struct inode *inode, struct file *filp)
>>
>> static const struct file_operations kvm_device_fops = {
>> .unlocked_ioctl = kvm_device_ioctl,
>> -#ifdef CONFIG_KVM_COMPAT
>> - .compat_ioctl = kvm_device_ioctl,
>> -#endif
>> .release = kvm_device_release,
>> + KVM_COMPAT(kvm_device_ioctl),
>> };
>>
>> struct kvm_device *kvm_device_from_filp(struct file *filp)
>> @@ -3165,10 +3166,8 @@ static long kvm_vm_compat_ioctl(struct file *filp,
>> static struct file_operations kvm_vm_fops = {
>> .release = kvm_vm_release,
>> .unlocked_ioctl = kvm_vm_ioctl,
>> -#ifdef CONFIG_KVM_COMPAT
>> - .compat_ioctl = kvm_vm_compat_ioctl,
>> -#endif
>> .llseek = noop_llseek,
>> + KVM_COMPAT(kvm_vm_compat_ioctl),
>> };
>>
>> static int kvm_dev_ioctl_create_vm(unsigned long type)
>> @@ -3259,8 +3258,8 @@ static long kvm_dev_ioctl(struct file *filp,
>>
>> static struct file_operations kvm_chardev_ops = {
>> .unlocked_ioctl = kvm_dev_ioctl,
>> - .compat_ioctl = kvm_dev_ioctl,
>> .llseek = noop_llseek,
>> + KVM_COMPAT(kvm_dev_ioctl),
>> };
>>
>> static struct miscdevice kvm_dev = {
>> --
>> 2.17.1
>>
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH 2/2] KVM: arm64: Prevent KVM_COMPAT from being selected
From: Mark Rutland @ 2018-06-19 10:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180619094251.8586-3-marc.zyngier@arm.com>
On Tue, Jun 19, 2018 at 10:42:51AM +0100, Marc Zyngier wrote:
> There is very little point in trying to support the 32bit KVM/arm API
> on arm64, and this was never an anticipated use case.
>
> Let's make it clear by not selecting KVM_COMPAT.
Do any other architectures have KVM && COMPAT, and expose an
incompatible ABI to COMPAT tasks?
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Given the state of arm/arm64, this makes sense to me. FWIW:
Acked-by: Mark Rutland <mark.rutland@arm.com>
> ---
> virt/kvm/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
> index 72143cfaf6ec..ea434ddc8499 100644
> --- a/virt/kvm/Kconfig
> +++ b/virt/kvm/Kconfig
> @@ -47,7 +47,7 @@ config KVM_GENERIC_DIRTYLOG_READ_PROTECT
>
> config KVM_COMPAT
> def_bool y
> - depends on KVM && COMPAT && !S390
> + depends on KVM && COMPAT && !(S390 || ARM64)
>
> config HAVE_KVM_IRQ_BYPASS
> bool
> --
> 2.17.1
>
^ permalink raw reply
* [PATCH] dt-bindings: Fix unbalanced quotation marks
From: Jon Hunter @ 2018-06-19 10:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180617143127.11421-1-j.neuschaefer@gmx.net>
On 17/06/18 15:31, Jonathan Neusch?fer wrote:
> Multiple binding documents have various forms of unbalanced quotation
> marks. Fix them.
>
> Signed-off-by: Jonathan Neusch?fer <j.neuschaefer@gmx.net>
> ---
>
> Should I split this patch so that different parts can go through different trees?
> ---
> .../devicetree/bindings/arm/samsung/samsung-boards.txt | 2 +-
> .../devicetree/bindings/gpio/nintendo,hollywood-gpio.txt | 2 +-
> Documentation/devicetree/bindings/input/touchscreen/hideep.txt | 2 +-
> .../bindings/interrupt-controller/nvidia,tegra20-ictlr.txt | 2 +-
> .../devicetree/bindings/interrupt-controller/st,stm32-exti.txt | 2 +-
> Documentation/devicetree/bindings/mips/brcm/soc.txt | 2 +-
> Documentation/devicetree/bindings/net/fsl-fman.txt | 2 +-
> Documentation/devicetree/bindings/power/power_domain.txt | 2 +-
> Documentation/devicetree/bindings/regulator/tps65090.txt | 2 +-
> Documentation/devicetree/bindings/reset/st,sti-softreset.txt | 2 +-
> Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt | 2 +-
> Documentation/devicetree/bindings/sound/qcom,apq8096.txt | 2 +-
> 12 files changed, 12 insertions(+), 12 deletions(-)
...
> diff --git a/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.txt b/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.txt
> index 1099fe0788fa..f246ccbf8838 100644
> --- a/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.txt
> +++ b/Documentation/devicetree/bindings/interrupt-controller/nvidia,tegra20-ictlr.txt
> @@ -15,7 +15,7 @@ Required properties:
> include "nvidia,tegra30-ictlr".
> - reg : Specifies base physical address and size of the registers.
> Each controller must be described separately (Tegra20 has 4 of them,
> - whereas Tegra30 and later have 5"
> + whereas Tegra30 and later have 5).
> - interrupt-controller : Identifies the node as an interrupt controller.
> - #interrupt-cells : Specifies the number of cells needed to encode an
> interrupt source. The value must be 3.
For the above Tegra portion ...
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Cheers
Jon
--
nvpublic
^ permalink raw reply
* [PATCH v3 0/7] arm64: perf: Support for chained counters
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
This series adds support for counting PMU events using 64bit counters
for arm64 PMU.
The Arm v8 PMUv3 supports combining two adjacent 32bit counters
(low even and hig odd counters) to count a given "event" in 64bit mode.
This series adds the support for 64bit events in the core arm_pmu driver
infrastructure and adds the support for armv8 64bit kernel PMU to use
chained counters to count in 64bit mode. For CPU cycles, we use the cycle
counter in 64bit mode, only when requested. If the cycle counter is not
available, we fall back to chaining the counters.
Tested on Juno, Fast models. Applies on 4.18-rc1
Changes since v2:
- Clean up select counter routine. (Mark Rutland)
- Stop PMU while processing overflows (Mark Rutland)
- Drop special allocation algorithm for chain indices
- Since we access the counters when the PMU is stopped,
get rid of the unncessary barriers.
- Ensure a counter is allocated when checking for chained event
Change since v1:
- Remove unnecessary isb()s in chain counter reads/writes
- Fix event programming order for counters
- Tighten chain counter event read sequence
- Set chain event to count in all ELs
- Cleanup helpers to be consistent
- Fix build break on xcale PMU (reported by kbuild-robot)
- Remove the explicit counter width field from pmu backends and default
to 32bit.
- Rename flag ARMPMU_EVT_LONG => ARMPMU_EVT_64BIT and
the format string "chain" => "bits64". (Unfortunately we can't use "64bit"
and I am open for suggestion on a better name)
- Rename armpmu_get_event_max_period() => armpmu_event_max_period()
- For 64bit CPU cycles events, allow chaining if cycle counter is
not available.
Suzuki K Poulose (7):
arm_pmu: Clean up maximum period handling
arm_pmu: Change API to support 64bit counter values
arm_pmu: Add support for 64bit event counters
arm_pmu: Tidy up clear_event_idx call backs
arm64: perf: Clean up armv8pmu_select_counter
arm64: perf: Disable PMU while processing counter overflows
arm64: perf: Add support for chaining event counters
arch/arm/kernel/perf_event_v6.c | 6 +-
arch/arm/kernel/perf_event_v7.c | 7 +-
arch/arm/kernel/perf_event_xscale.c | 10 +-
arch/arm64/kernel/perf_event.c | 249 +++++++++++++++++++++++++++++-------
drivers/perf/arm_pmu.c | 51 +++++---
include/linux/perf/arm_pmu.h | 11 +-
6 files changed, 255 insertions(+), 79 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH v3 1/7] arm_pmu: Clean up maximum period handling
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
Each PMU defines their max_period of the counter as the maximum
value that can be counted. Since all the PMU backends support
32bit counters by default, let us remove the redundant field.
No functional changes.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
No changes since v2
---
arch/arm/kernel/perf_event_v6.c | 2 --
arch/arm/kernel/perf_event_v7.c | 1 -
arch/arm/kernel/perf_event_xscale.c | 2 --
arch/arm64/kernel/perf_event.c | 1 -
drivers/perf/arm_pmu.c | 16 ++++++++++++----
include/linux/perf/arm_pmu.h | 1 -
6 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index be42c4f..f64a6bf 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -495,7 +495,6 @@ static void armv6pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = armv6pmu_stop;
cpu_pmu->map_event = armv6_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
}
static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu)
@@ -546,7 +545,6 @@ static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = armv6pmu_stop;
cpu_pmu->map_event = armv6mpcore_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
return 0;
}
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 57f01e0..ecca4cd 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1170,7 +1170,6 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv7pmu_start;
cpu_pmu->stop = armv7pmu_stop;
cpu_pmu->reset = armv7pmu_reset;
- cpu_pmu->max_period = (1LLU << 32) - 1;
};
static void armv7_read_num_pmnc_events(void *info)
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c
index 88d1a76..c4f0294 100644
--- a/arch/arm/kernel/perf_event_xscale.c
+++ b/arch/arm/kernel/perf_event_xscale.c
@@ -374,7 +374,6 @@ static int xscale1pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = xscale1pmu_stop;
cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
return 0;
}
@@ -743,7 +742,6 @@ static int xscale2pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = xscale2pmu_stop;
cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 5;
- cpu_pmu->max_period = (1LLU << 32) - 1;
return 0;
}
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 33147aa..678ecff 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -960,7 +960,6 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv8pmu_start,
cpu_pmu->stop = armv8pmu_stop,
cpu_pmu->reset = armv8pmu_reset,
- cpu_pmu->max_period = (1LLU << 32) - 1,
cpu_pmu->set_event_filter = armv8pmu_set_event_filter;
return 0;
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index a6347d4..6ddc00d 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -28,6 +28,11 @@
static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
static DEFINE_PER_CPU(int, cpu_irq);
+static inline u64 arm_pmu_max_period(void)
+{
+ return (1ULL << 32) - 1;
+}
+
static int
armpmu_map_cache_event(const unsigned (*cache_map)
[PERF_COUNT_HW_CACHE_MAX]
@@ -114,8 +119,10 @@ int armpmu_event_set_period(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw;
s64 left = local64_read(&hwc->period_left);
s64 period = hwc->sample_period;
+ u64 max_period;
int ret = 0;
+ max_period = arm_pmu_max_period();
if (unlikely(left <= -period)) {
left = period;
local64_set(&hwc->period_left, left);
@@ -136,8 +143,8 @@ int armpmu_event_set_period(struct perf_event *event)
* effect we are reducing max_period to account for
* interrupt latency (and we are being very conservative).
*/
- if (left > (armpmu->max_period >> 1))
- left = armpmu->max_period >> 1;
+ if (left > (max_period >> 1))
+ left = (max_period >> 1);
local64_set(&hwc->prev_count, (u64)-left);
@@ -153,6 +160,7 @@ u64 armpmu_event_update(struct perf_event *event)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
u64 delta, prev_raw_count, new_raw_count;
+ u64 max_period = arm_pmu_max_period();
again:
prev_raw_count = local64_read(&hwc->prev_count);
@@ -162,7 +170,7 @@ u64 armpmu_event_update(struct perf_event *event)
new_raw_count) != prev_raw_count)
goto again;
- delta = (new_raw_count - prev_raw_count) & armpmu->max_period;
+ delta = (new_raw_count - prev_raw_count) & max_period;
local64_add(delta, &event->count);
local64_sub(delta, &hwc->period_left);
@@ -402,7 +410,7 @@ __hw_perf_event_init(struct perf_event *event)
* is far less likely to overtake the previous one unless
* you have some serious IRQ latency issues.
*/
- hwc->sample_period = armpmu->max_period >> 1;
+ hwc->sample_period = arm_pmu_max_period() >> 1;
hwc->last_period = hwc->sample_period;
local64_set(&hwc->period_left, hwc->sample_period);
}
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index ad54444..12c30a2 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -94,7 +94,6 @@ struct arm_pmu {
void (*reset)(void *);
int (*map_event)(struct perf_event *event);
int num_events;
- u64 max_period;
bool secure_access; /* 32-bit ARM only */
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);
--
2.7.4
^ permalink raw reply related
* [PATCH v3 2/7] arm_pmu: Change API to support 64bit counter values
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
Convert the {read/write}_counter APIs to handle 64bit values
to enable supporting chained event counters.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
- No changes since v2
---
arch/arm/kernel/perf_event_v6.c | 4 ++--
arch/arm/kernel/perf_event_v7.c | 4 ++--
arch/arm/kernel/perf_event_xscale.c | 8 ++++----
arch/arm64/kernel/perf_event.c | 9 ++++-----
include/linux/perf/arm_pmu.h | 4 ++--
5 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index f64a6bf..0729f98 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -233,7 +233,7 @@ armv6_pmcr_counter_has_overflowed(unsigned long pmcr,
return ret;
}
-static inline u32 armv6pmu_read_counter(struct perf_event *event)
+static inline u64 armv6pmu_read_counter(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -251,7 +251,7 @@ static inline u32 armv6pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv6pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv6pmu_write_counter(struct perf_event *event, u64 value)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index ecca4cd..fd7ce01 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -743,7 +743,7 @@ static inline void armv7_pmnc_select_counter(int idx)
isb();
}
-static inline u32 armv7pmu_read_counter(struct perf_event *event)
+static inline u64 armv7pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -763,7 +763,7 @@ static inline u32 armv7pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv7pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv7pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c
index c4f0294..942230f 100644
--- a/arch/arm/kernel/perf_event_xscale.c
+++ b/arch/arm/kernel/perf_event_xscale.c
@@ -316,7 +316,7 @@ static void xscale1pmu_stop(struct arm_pmu *cpu_pmu)
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
-static inline u32 xscale1pmu_read_counter(struct perf_event *event)
+static inline u64 xscale1pmu_read_counter(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -337,7 +337,7 @@ static inline u32 xscale1pmu_read_counter(struct perf_event *event)
return val;
}
-static inline void xscale1pmu_write_counter(struct perf_event *event, u32 val)
+static inline void xscale1pmu_write_counter(struct perf_event *event, u64 val)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -678,7 +678,7 @@ static void xscale2pmu_stop(struct arm_pmu *cpu_pmu)
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
-static inline u32 xscale2pmu_read_counter(struct perf_event *event)
+static inline u64 xscale2pmu_read_counter(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -705,7 +705,7 @@ static inline u32 xscale2pmu_read_counter(struct perf_event *event)
return val;
}
-static inline void xscale2pmu_write_counter(struct perf_event *event, u32 val)
+static inline void xscale2pmu_write_counter(struct perf_event *event, u64 val)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 678ecff..66a2ffd 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -512,7 +512,7 @@ static inline int armv8pmu_select_counter(int idx)
return idx;
}
-static inline u32 armv8pmu_read_counter(struct perf_event *event)
+static inline u64 armv8pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -530,7 +530,7 @@ static inline u32 armv8pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv8pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -545,9 +545,8 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u32 value)
* count using the lower 32bits and we want an interrupt when
* it overflows.
*/
- u64 value64 = 0xffffffff00000000ULL | value;
-
- write_sysreg(value64, pmccntr_el0);
+ value |= 0xffffffff00000000ULL;
+ write_sysreg(value, pmccntr_el0);
} else if (armv8pmu_select_counter(idx) == idx)
write_sysreg(value, pmxevcntr_el0);
}
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 12c30a2..f7126a2 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -87,8 +87,8 @@ struct arm_pmu {
struct perf_event *event);
int (*set_event_filter)(struct hw_perf_event *evt,
struct perf_event_attr *attr);
- u32 (*read_counter)(struct perf_event *event);
- void (*write_counter)(struct perf_event *event, u32 val);
+ u64 (*read_counter)(struct perf_event *event);
+ void (*write_counter)(struct perf_event *event, u64 val);
void (*start)(struct arm_pmu *);
void (*stop)(struct arm_pmu *);
void (*reset)(void *);
--
2.7.4
^ permalink raw reply related
* [PATCH v3 3/7] arm_pmu: Add support for 64bit event counters
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
Each PMU has a set of 32bit event counters. But in some
special cases, the events could be counted using counters
which are effectively 64bit wide.
e.g, Arm V8 PMUv3 has a 64 bit cycle counter which can count
only the CPU cycles. Also, the PMU can chain the event counters
to effectively count as a 64bit counter.
Add support for tracking the events that uses 64bit counters.
This only affects the periods set for each counter in the core
driver.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v2:
- None
---
drivers/perf/arm_pmu.c | 15 +++++++++------
include/linux/perf/arm_pmu.h | 6 ++++++
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 6ddc00d..e3766a8 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -28,9 +28,11 @@
static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
static DEFINE_PER_CPU(int, cpu_irq);
-static inline u64 arm_pmu_max_period(void)
+static inline u64 arm_pmu_event_max_period(struct perf_event *event)
{
- return (1ULL << 32) - 1;
+ return (event->hw.flags & ARMPMU_EVT_64BIT) ?
+ GENMASK_ULL(63, 0) :
+ GENMASK_ULL(31, 0);
}
static int
@@ -122,7 +124,7 @@ int armpmu_event_set_period(struct perf_event *event)
u64 max_period;
int ret = 0;
- max_period = arm_pmu_max_period();
+ max_period = arm_pmu_event_max_period(event);
if (unlikely(left <= -period)) {
left = period;
local64_set(&hwc->period_left, left);
@@ -148,7 +150,7 @@ int armpmu_event_set_period(struct perf_event *event)
local64_set(&hwc->prev_count, (u64)-left);
- armpmu->write_counter(event, (u64)(-left) & 0xffffffff);
+ armpmu->write_counter(event, (u64)(-left) & max_period);
perf_event_update_userpage(event);
@@ -160,7 +162,7 @@ u64 armpmu_event_update(struct perf_event *event)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
u64 delta, prev_raw_count, new_raw_count;
- u64 max_period = arm_pmu_max_period();
+ u64 max_period = arm_pmu_event_max_period(event);
again:
prev_raw_count = local64_read(&hwc->prev_count);
@@ -368,6 +370,7 @@ __hw_perf_event_init(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw;
int mapping;
+ hwc->flags = 0;
mapping = armpmu->map_event(event);
if (mapping < 0) {
@@ -410,7 +413,7 @@ __hw_perf_event_init(struct perf_event *event)
* is far less likely to overtake the previous one unless
* you have some serious IRQ latency issues.
*/
- hwc->sample_period = arm_pmu_max_period() >> 1;
+ hwc->sample_period = arm_pmu_event_max_period(event) >> 1;
hwc->last_period = hwc->sample_period;
local64_set(&hwc->period_left, hwc->sample_period);
}
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index f7126a2..10f92e1 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -25,6 +25,12 @@
*/
#define ARMPMU_MAX_HWEVENTS 32
+/*
+ * ARM PMU hw_event flags
+ */
+/* Event uses a 64bit counter */
+#define ARMPMU_EVT_64BIT 1
+
#define HW_OP_UNSUPPORTED 0xFFFF
#define C(_x) PERF_COUNT_HW_CACHE_##_x
#define CACHE_OP_UNSUPPORTED 0xFFFF
--
2.7.4
^ permalink raw reply related
* [PATCH v3 4/7] arm_pmu: Tidy up clear_event_idx call backs
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
The armpmu uses get_event_idx callback to allocate an event
counter for a given event, which marks the selected counter
as "used". Now, when we delete the counter, the arm_pmu goes
ahead and clears the "used" bit and then invokes the "clear_event_idx"
call back, which kind of splits the job between the core code
and the backend. Tidy this up by relying on the clear_event_idx
to do the book keeping, if available. Otherwise, let the core
driver do the default "clear" bit operation. This will be useful
for adding the chained event support, where we leave the event
idx maintenance to the backend.
Also, when an event is removed from the PMU, reset the hw.idx
to indicate that a counter is not allocated for this event,
to help the backends do better checks. This will be also used
for the chain counter support.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v2:
- Reset the event counter after an event is removed.
---
arch/arm/kernel/perf_event_v7.c | 2 ++
drivers/perf/arm_pmu.c | 17 +++++++++++++----
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index fd7ce01..765d265 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1637,6 +1637,7 @@ static void krait_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
bool venum_event = EVENT_VENUM(hwc->config_base);
bool krait_event = EVENT_CPU(hwc->config_base);
+ clear_bit(hwc->idx, cpuc->used_mask);
if (venum_event || krait_event) {
bit = krait_event_to_bit(event, region, group);
clear_bit(bit, cpuc->used_mask);
@@ -1966,6 +1967,7 @@ static void scorpion_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
bool venum_event = EVENT_VENUM(hwc->config_base);
bool scorpion_event = EVENT_CPU(hwc->config_base);
+ clear_bit(hwc->idx, cpuc->used_mask);
if (venum_event || scorpion_event) {
bit = scorpion_event_to_bit(event, region, group);
clear_bit(bit, cpuc->used_mask);
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index e3766a8..6e10e8c 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -227,6 +227,16 @@ static void armpmu_start(struct perf_event *event, int flags)
armpmu->enable(event);
}
+static void armpmu_clear_event_idx(struct arm_pmu *armpmu,
+ struct pmu_hw_events *hw_events,
+ struct perf_event *event)
+{
+ if (armpmu->clear_event_idx)
+ armpmu->clear_event_idx(hw_events, event);
+ else
+ clear_bit(event->hw.idx, hw_events->used_mask);
+}
+
static void
armpmu_del(struct perf_event *event, int flags)
{
@@ -237,11 +247,10 @@ armpmu_del(struct perf_event *event, int flags)
armpmu_stop(event, PERF_EF_UPDATE);
hw_events->events[idx] = NULL;
- clear_bit(idx, hw_events->used_mask);
- if (armpmu->clear_event_idx)
- armpmu->clear_event_idx(hw_events, event);
-
+ armpmu_clear_event_idx(armpmu, hw_events, event);
perf_event_update_userpage(event);
+ /* Clear the allocated counter */
+ hwc->idx = -1;
}
static int
--
2.7.4
^ permalink raw reply related
* [PATCH v3 5/7] arm64: perf: Clean up armv8pmu_select_counter
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
armv8pmu_select_counter always returns the passed idx. So
let us make that void and get rid of the pointless checks.
Suggested-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/perf_event.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 66a2ffd..9ce3729 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -503,13 +503,17 @@ static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx)
return pmnc & BIT(ARMV8_IDX_TO_COUNTER(idx));
}
-static inline int armv8pmu_select_counter(int idx)
+static inline void armv8pmu_select_counter(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
write_sysreg(counter, pmselr_el0);
isb();
+}
- return idx;
+static inline u32 armv8pmu_read_evcntr(int idx)
+{
+ armv8pmu_select_counter(idx);
+ return read_sysreg(pmxevcntr_el0);
}
static inline u64 armv8pmu_read_counter(struct perf_event *event)
@@ -524,12 +528,18 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
smp_processor_id(), idx);
else if (idx == ARMV8_IDX_CYCLE_COUNTER)
value = read_sysreg(pmccntr_el0);
- else if (armv8pmu_select_counter(idx) == idx)
- value = read_sysreg(pmxevcntr_el0);
+ else
+ value = armv8pmu_read_evcntr(idx);
return value;
}
+static inline void armv8pmu_write_evcntr(int idx, u32 value)
+{
+ armv8pmu_select_counter(idx);
+ write_sysreg(value, pmxevcntr_el0);
+}
+
static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
@@ -547,16 +557,15 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
*/
value |= 0xffffffff00000000ULL;
write_sysreg(value, pmccntr_el0);
- } else if (armv8pmu_select_counter(idx) == idx)
- write_sysreg(value, pmxevcntr_el0);
+ } else
+ armv8pmu_write_evcntr(idx, value);
}
static inline void armv8pmu_write_evtype(int idx, u32 val)
{
- if (armv8pmu_select_counter(idx) == idx) {
- val &= ARMV8_PMU_EVTYPE_MASK;
- write_sysreg(val, pmxevtyper_el0);
- }
+ armv8pmu_select_counter(idx);
+ val &= ARMV8_PMU_EVTYPE_MASK;
+ write_sysreg(val, pmxevtyper_el0);
}
static inline int armv8pmu_enable_counter(int idx)
--
2.7.4
^ permalink raw reply related
* [PATCH v3 6/7] arm64: perf: Disable PMU while processing counter overflows
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
The arm64 PMU updates the event counters and reprograms the
counters in the overflow IRQ handler without disabling the
PMU. This could potentially cause skews in for group counters,
where the overflowed counters may potentially loose some event
counts, while they are reprogrammed. To prevent this, disable
the PMU while we process the counter overflows and enable it
right back when we are done.
This patch also moves the PMU stop/start routines to avoid a
forward declaration.
Suggested-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/perf_event.c | 50 +++++++++++++++++++++++-------------------
1 file changed, 28 insertions(+), 22 deletions(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 9ce3729..eebc635 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -678,6 +678,28 @@ static void armv8pmu_disable_event(struct perf_event *event)
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
+static void armv8pmu_start(struct arm_pmu *cpu_pmu)
+{
+ unsigned long flags;
+ struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+ /* Enable all counters */
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
+{
+ unsigned long flags;
+ struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+ /* Disable all counters */
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
{
u32 pmovsr;
@@ -702,6 +724,11 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
*/
regs = get_irq_regs();
+ /*
+ * Stop the PMU while processing the counter overflows
+ * to prevent skews in group events.
+ */
+ armv8pmu_stop(cpu_pmu);
for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc;
@@ -726,6 +753,7 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
if (perf_event_overflow(event, &data, regs))
cpu_pmu->disable(event);
}
+ armv8pmu_start(cpu_pmu);
/*
* Handle the pending perf events.
@@ -739,28 +767,6 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
return IRQ_HANDLED;
}
-static void armv8pmu_start(struct arm_pmu *cpu_pmu)
-{
- unsigned long flags;
- struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
- raw_spin_lock_irqsave(&events->pmu_lock, flags);
- /* Enable all counters */
- armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E);
- raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
-}
-
-static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
-{
- unsigned long flags;
- struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
-
- raw_spin_lock_irqsave(&events->pmu_lock, flags);
- /* Disable all counters */
- armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMU_PMCR_E);
- raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
-}
-
static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
struct perf_event *event)
{
--
2.7.4
^ permalink raw reply related
* [PATCH v3 7/7] arm64: perf: Add support for chaining event counters
From: Suzuki K Poulose @ 2018-06-19 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403342-17899-1-git-send-email-suzuki.poulose@arm.com>
Add support for 64bit event by using chained event counters
and 64bit cycle counters.
PMUv3 allows chaining a pair of adjacent 32-bit counters, effectively
forming a 64-bit counter. The low/even counter is programmed to count
the event of interest, and the high/odd counter is programmed to count
the CHAIN event, taken when the low/even counter overflows.
For CPU cycles, when 64bit mode is requested, the cycle counter
is used in 64bit mode. If the cycle counter is not available,
falls back to chaining.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes sinec v2:
- Drop special allocation algorithm for chain indices
- Since we access the counters when the PMU is stopped,
get rid of the unncessary barriers.
- Ensure a counter is allocated when checking for chained event
---
arch/arm64/kernel/perf_event.c | 184 ++++++++++++++++++++++++++++++++++++-----
drivers/perf/arm_pmu.c | 13 ++-
2 files changed, 169 insertions(+), 28 deletions(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index eebc635..a03def7 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -446,9 +446,16 @@ static struct attribute_group armv8_pmuv3_events_attr_group = {
};
PMU_FORMAT_ATTR(event, "config:0-15");
+PMU_FORMAT_ATTR(bits64, "config1:0");
+
+static inline bool armv8pmu_event_is_64bit(struct perf_event *event)
+{
+ return event->attr.config1 & 0x1;
+}
static struct attribute *armv8_pmuv3_format_attrs[] = {
&format_attr_event.attr,
+ &format_attr_bits64.attr,
NULL,
};
@@ -466,6 +473,20 @@ static struct attribute_group armv8_pmuv3_format_attr_group = {
(ARMV8_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)
/*
+ * Use chained counter for a 64bit event, if we could not allocate
+ * the 64bit cycle counter. This must be called after a counter
+ * was allocated.
+ */
+static inline bool armv8pmu_event_is_chained(struct perf_event *event)
+{
+ int idx = event->hw.idx;
+
+ return !WARN_ON(idx < 0) &&
+ armv8pmu_event_is_64bit(event) &&
+ (event->hw.idx != ARMV8_IDX_CYCLE_COUNTER);
+}
+
+/*
* ARMv8 low level PMU access
*/
@@ -516,12 +537,28 @@ static inline u32 armv8pmu_read_evcntr(int idx)
return read_sysreg(pmxevcntr_el0);
}
+static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
+{
+ int idx = event->hw.idx;
+ u64 val = 0;
+
+ val = armv8pmu_read_evcntr(idx);
+ /*
+ * We always read the counter with the PMU turned off.
+ * So we don't need special care for reading chained
+ * counters.
+ */
+ if (armv8pmu_event_is_chained(event))
+ val = (val << 32) | armv8pmu_read_evcntr(idx - 1);
+ return val;
+}
+
static inline u64 armv8pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
- u32 value = 0;
+ u64 value = 0;
if (!armv8pmu_counter_valid(cpu_pmu, idx))
pr_err("CPU%u reading wrong counter %d\n",
@@ -529,7 +566,7 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
else if (idx == ARMV8_IDX_CYCLE_COUNTER)
value = read_sysreg(pmccntr_el0);
else
- value = armv8pmu_read_evcntr(idx);
+ value = armv8pmu_read_hw_counter(event);
return value;
}
@@ -540,6 +577,19 @@ static inline void armv8pmu_write_evcntr(int idx, u32 value)
write_sysreg(value, pmxevcntr_el0);
}
+static inline void armv8pmu_write_hw_counter(struct perf_event *event,
+ u64 value)
+{
+ int idx = event->hw.idx;
+
+ if (armv8pmu_event_is_chained(event)) {
+ armv8pmu_write_evcntr(idx, upper_32_bits(value));
+ armv8pmu_write_evcntr(idx - 1, lower_32_bits(value));
+ } else {
+ armv8pmu_write_evcntr(idx, value);
+ }
+}
+
static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
@@ -551,14 +601,15 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
smp_processor_id(), idx);
else if (idx == ARMV8_IDX_CYCLE_COUNTER) {
/*
- * Set the upper 32bits as this is a 64bit counter but we only
+ * Set the upper 32bits as this is a 64bit counter, if we only
* count using the lower 32bits and we want an interrupt when
* it overflows.
*/
- value |= 0xffffffff00000000ULL;
+ if (!armv8pmu_event_is_64bit(event))
+ value |= 0xffffffff00000000ULL;
write_sysreg(value, pmccntr_el0);
} else
- armv8pmu_write_evcntr(idx, value);
+ armv8pmu_write_hw_counter(event, value);
}
static inline void armv8pmu_write_evtype(int idx, u32 val)
@@ -568,6 +619,27 @@ static inline void armv8pmu_write_evtype(int idx, u32 val)
write_sysreg(val, pmxevtyper_el0);
}
+static inline void armv8pmu_write_event_type(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ /*
+ * For chained events, the low counter is programmed to count
+ * the event of interest and the high counter is programmed
+ * with CHAIN event code with filters set to count at all ELs.
+ */
+ if (armv8pmu_event_is_chained(event)) {
+ u32 chain_evt = ARMV8_PMUV3_PERFCTR_CHAIN |
+ ARMV8_PMU_INCLUDE_EL2;
+
+ armv8pmu_write_evtype(idx - 1, hwc->config_base);
+ armv8pmu_write_evtype(idx, chain_evt);
+ } else {
+ armv8pmu_write_evtype(idx, hwc->config_base);
+ }
+}
+
static inline int armv8pmu_enable_counter(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -575,6 +647,16 @@ static inline int armv8pmu_enable_counter(int idx)
return idx;
}
+static inline void armv8pmu_enable_event_counter(struct perf_event *event)
+{
+ int idx = event->hw.idx;
+
+ armv8pmu_enable_counter(idx);
+ if (armv8pmu_event_is_chained(event))
+ armv8pmu_enable_counter(idx - 1);
+ isb();
+}
+
static inline int armv8pmu_disable_counter(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -582,6 +664,16 @@ static inline int armv8pmu_disable_counter(int idx)
return idx;
}
+static inline void armv8pmu_disable_event_counter(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (armv8pmu_event_is_chained(event))
+ armv8pmu_disable_counter(idx - 1);
+ armv8pmu_disable_counter(idx);
+}
+
static inline int armv8pmu_enable_intens(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -589,6 +681,12 @@ static inline int armv8pmu_enable_intens(int idx)
return idx;
}
+static inline int armv8pmu_enable_event_irq(struct perf_event *event)
+{
+ /* For chained events, enable the interrupt for only the high counter */
+ return armv8pmu_enable_intens(event->hw.idx);
+}
+
static inline int armv8pmu_disable_intens(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -601,6 +699,11 @@ static inline int armv8pmu_disable_intens(int idx)
return idx;
}
+static inline int armv8pmu_disable_event_irq(struct perf_event *event)
+{
+ return armv8pmu_disable_intens(event->hw.idx);
+}
+
static inline u32 armv8pmu_getreset_flags(void)
{
u32 value;
@@ -618,10 +721,8 @@ static inline u32 armv8pmu_getreset_flags(void)
static void armv8pmu_enable_event(struct perf_event *event)
{
unsigned long flags;
- struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
- int idx = hwc->idx;
/*
* Enable counter and interrupt, and set the counter to count
@@ -632,22 +733,22 @@ static void armv8pmu_enable_event(struct perf_event *event)
/*
* Disable counter
*/
- armv8pmu_disable_counter(idx);
+ armv8pmu_disable_event_counter(event);
/*
* Set event (if destined for PMNx counters).
*/
- armv8pmu_write_evtype(idx, hwc->config_base);
+ armv8pmu_write_event_type(event);
/*
* Enable interrupt for this counter
*/
- armv8pmu_enable_intens(idx);
+ armv8pmu_enable_event_irq(event);
/*
* Enable counter
*/
- armv8pmu_enable_counter(idx);
+ armv8pmu_enable_event_counter(event);
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
@@ -655,10 +756,8 @@ static void armv8pmu_enable_event(struct perf_event *event)
static void armv8pmu_disable_event(struct perf_event *event)
{
unsigned long flags;
- struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
- int idx = hwc->idx;
/*
* Disable counter and interrupt
@@ -668,12 +767,12 @@ static void armv8pmu_disable_event(struct perf_event *event)
/*
* Disable counter
*/
- armv8pmu_disable_counter(idx);
+ armv8pmu_disable_event_counter(event);
/*
* Disable interrupt for this counter
*/
- armv8pmu_disable_intens(idx);
+ armv8pmu_disable_event_irq(event);
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
@@ -767,6 +866,37 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
return IRQ_HANDLED;
}
+static int armv8pmu_get_single_idx(struct pmu_hw_events *cpuc,
+ struct arm_pmu *cpu_pmu)
+{
+ int idx;
+
+ for (idx = ARMV8_IDX_COUNTER0; idx < cpu_pmu->num_events; idx++)
+ if (!test_and_set_bit(idx, cpuc->used_mask))
+ return idx;
+ return -EAGAIN;
+}
+
+static int armv8pmu_get_chain_idx(struct pmu_hw_events *cpuc,
+ struct arm_pmu *cpu_pmu)
+{
+ int idx;
+
+ /*
+ * Chaining requires two consecutive event counters, where
+ * the lower idx must be even.
+ */
+ for (idx = ARMV8_IDX_COUNTER0 + 1; idx < cpu_pmu->num_events; idx += 2)
+ if (!test_and_set_bit(idx, cpuc->used_mask)) {
+ /* Check if the preceding even counter is available */
+ if (!test_and_set_bit(idx - 1, cpuc->used_mask))
+ return idx;
+ /* Release the Odd counter */
+ clear_bit(idx, cpuc->used_mask);
+ }
+ return -EAGAIN;
+}
+
static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
struct perf_event *event)
{
@@ -784,13 +914,21 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
/*
* Otherwise use events counters
*/
- for (idx = ARMV8_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) {
- if (!test_and_set_bit(idx, cpuc->used_mask))
- return idx;
- }
+ idx = armv8pmu_event_is_64bit(event) ?
+ armv8pmu_get_chain_idx(cpuc, cpu_pmu) :
+ armv8pmu_get_single_idx(cpuc, cpu_pmu);
- /* The counters are all in use. */
- return -EAGAIN;
+ return idx;
+}
+
+static void armv8pmu_clear_event_idx(struct pmu_hw_events *cpuc,
+ struct perf_event *event)
+{
+ int idx = event->hw.idx;
+
+ clear_bit(idx, cpuc->used_mask);
+ if (armv8pmu_event_is_chained(event))
+ clear_bit(idx - 1, cpuc->used_mask);
}
/*
@@ -865,6 +1003,9 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
&armv8_pmuv3_perf_cache_map,
ARMV8_PMU_EVTYPE_EVENT);
+ if (armv8pmu_event_is_64bit(event))
+ event->hw.flags |= ARMPMU_EVT_64BIT;
+
/* Onl expose micro/arch events supported by this PMU */
if ((hw_event_id > 0) && (hw_event_id < ARMV8_PMUV3_MAX_COMMON_EVENTS)
&& test_bit(hw_event_id, armpmu->pmceid_bitmap)) {
@@ -971,6 +1112,7 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->read_counter = armv8pmu_read_counter,
cpu_pmu->write_counter = armv8pmu_write_counter,
cpu_pmu->get_event_idx = armv8pmu_get_event_idx,
+ cpu_pmu->clear_event_idx = armv8pmu_clear_event_idx,
cpu_pmu->start = armv8pmu_start,
cpu_pmu->stop = armv8pmu_stop,
cpu_pmu->reset = armv8pmu_reset,
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 6e10e8c..a4675e4 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -674,14 +674,13 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
int idx;
for (idx = 0; idx < armpmu->num_events; idx++) {
- /*
- * If the counter is not used skip it, there is no
- * need of stopping/restarting it.
- */
- if (!test_bit(idx, hw_events->used_mask))
- continue;
-
event = hw_events->events[idx];
+ /*
+ * If there is no event at this idx (e.g, an idx used
+ * by a chained event in Arm v8 PMUv3), skip it.
+ */
+ if (!event)
+ continue;
switch (cmd) {
case CPU_PM_ENTER:
--
2.7.4
^ permalink raw reply related
* [PATCH] arm64/mm: Introduce a variable to hold base address of linear region
From: James Morse @ 2018-06-19 10:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a720d91be20844cd9ed84728268300d9@HXTBJIDCEMVIW02.hxtcorp.net>
Hi Yanjiang, Will,
On 19/06/18 10:57, Jin, Yanjiang wrote:
>> -----Original Message-----
>> From: Will Deacon [mailto:will.deacon at arm.com]
>> Sent: 2018?6?19? 17:41
>> To: Jin, Yanjiang <yanjiang.jin@hxt-semitech.com>
>> Cc: James Morse <james.morse@arm.com>; Bhupesh Sharma
>> <bhsharma@redhat.com>; Mark Rutland <mark.rutland@arm.com>; Ard
>> Biesheuvel <ard.biesheuvel@linaro.org>; Catalin Marinas
>> <catalin.marinas@arm.com>; Kexec Mailing List <kexec@lists.infradead.org>;
>> AKASHI Takahiro <takahiro.akashi@linaro.org>; Bhupesh SHARMA
>> <bhupesh.linux@gmail.com>; linux-arm-kernel <linux-arm-
>> kernel at lists.infradead.org>
>> Subject: Re: [PATCH] arm64/mm: Introduce a variable to hold base address of
>> linear region
>>
>> On Tue, Jun 19, 2018 at 09:34:56AM +0000, Jin, Yanjiang wrote:
>>>> On Tue, Jun 19, 2018 at 03:02:15AM +0000, Jin, Yanjiang wrote:
>>>>>> You seem to be using this for user-space phys_to_virt() based on
>>>>>> values found in /proc/iomem. This should give you what you want,
>>>>>> and isolate your user-space from the kernel's unexpected naming of
>> variables.
>>>>>
>>>>> I don't know could I simplify this problem?
>>>>> Let's ignore what memstart_addr represents here, we just want to
>>>>> implement
>>>>> phys_to_virt() in an userspace applications(kexec-tools or others).
>>>>>
>>>>> ARM64 Kernel has a below definition:
>>>>>
>>>>> #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET) |
>>>> PAGE_OFFSET)
>>>>>
>>>>> So userspace app must know PHYS_OFFSET(equal to memstart_addr now).
>>>>> Seems this is very simple, but memstart_addr has gone through
>>>>> several operations in arm64_memblock_init() depends on different
>>>>> Kernel configurations, so userspace app needs to know many
>>>>> additional definitions as
>>>> following:
>>>>>
>>>>> memblock_start_of_DRAM(), (ifdef CONFIG_SPARSEMEM_VMEMMAP),
>>>>> ARM64_MEMSTART_SHIFT, SECTION_SIZE_BITS, PAGE_OFFSET,
>>>>> memblock_end_of_DRAM(), IS_ENABLED(CONFIG_RANDOMIZE_BASE),
>>>>> memstart_offset_seed.
>>>>>
>>>>> It is hard to know all above in kexec-tools now. Originally I
>>>>> planned to read memstart_addr's value from "/dev/mem", but someone
>>>>> thought not all Kernels enable "/dev/mem", we'd better find a more
>>>>> generic approach. So we want to get some suggestions from ARM kernel
>> community.
>>>>> Can we export this variable in Kernel side through sysconf() or
>>>>> other similar methods? Or someone can provide an effect way to get
>>>>> memstart_addr's value?
>>>>
>>>> I thought the suggestion from James was to expose this via an ELF
>>>> NOTE in kcore and vmcore (or in the header directly if that's possible, but I'm
>> not sure about it)?
>>>
>>> Thanks for your reply firstly. But same as DEVMEM, kcore is not a
>>> must-have, so we can't depend on it.
>>
>> Neither is KEXEC. We can select PROC_KCORE from KEXEC if it helps.
>>
>>> On the other hand, phys_to_virt() is called during generating vmcore
>>> in Kexec-tools, vmcore also can't help this issue.
>>
>> I don't understand this part. If you have the vmcore in your hand, why can't you
>> grok the pv offset from the note and use that in phys_to_virt()?
>
> It is a chicken-and-egg issue.
> phys_to virt() is for crashdump setup. To generate vmcore, we must call
> phys_to_virt(). At this point, no vmcore exists.
Its needed for the parts of the ELF header that kexec-tools generates at kdump
load time?
So adding this pv_offset to the key=value data crash_save_vmcoreinfo_init()
saves isn't available early enough?
If we select PROC_KCORE for KEXEC so you know you will have /proc/kcore if the
system supports kdump. We should probably provide the same information in the
PT_NOTE section of the /proc/kcore file.
(I thought the kdump kernel exported that crash_save_vmcoreinfo_init() data as
an elf-note itself, but digging deeper I see the kernel exposes the physical
address in /sys/kernel/vmcoreinfo. Presumably its passed back via the kdump
elfcorehdr.)
>>> Unfortunately, not all platforms support analyzing Kernel config in
>>> userspace application, so Kexec-tools can't know some key kernel options.
>>> If not so, we can simulate the whole arm64_memblock_init() progress
>>> in kexec-tools.
>>
>> I don't understand what the kernel config has to do with kexec tools.
>
> I mean that if we can know kernel .config in all circumstances, we can calculate memstart_addr as below in Kexec-tools:
>
>
> memstart_addr = round_down(memblock_start_of_DRAM(),
> ARM64_MEMSTART_ALIGN);
This wouldn't work for KASLR. Having the kernel provide you with the offset
means you are insulated from the details of phys_to_virt() and what affects
these values. It should be possible to do this in the same way for all
architectures.
Thanks,
James
^ permalink raw reply
* [PATCH v5 0/3] Support Common Not Private translations
From: Vladimir Murzin @ 2018-06-19 10:18 UTC (permalink / raw)
To: linux-arm-kernel
Common Not Private (CNP) translations is a feature of ARMv8.2
extension which allows translation table entries to be shared between
different PEs in the same inner shareable domain, so the hardware can
use this fact to optimise the caching of such entries in the TLB.
This patch set is an attempt to bring CNP support into Linux. It was
tested on a v8.2 Fast Model with exploring traces and checking that
TTBRx_ELy and VTTBR_EL2 have CnP bit set where appropriate.
Changelog:
v4 -> v5
- cleanups and comment/commit improvements (per James)
- VTTBR_CNP_BIT instead of a hard-coded value (per Catalin)
- rebased on 4.18-rc1
v3 -> v4
- rebased on 4.17-rc4 for real
v2 -> v3
- do not enable CNP if we are crush kernel (per James)
- default to "no"
- rebased on 4.17-rc4
v1 -> v2
- handle cpuilde case (per James)
- use lm_allias with swapper_pg_dir (per James)
- rule out ARM64_SW_TTBR0_PAN case (per Catalin)
- s/BUG_ON/WARN_ON/ (per Catalin)
- comment and commit message updates (per Catalin)
- TTBR_CNP_BIT moved to asm/pgtable-hwdef.h (per Catalin)
- has_useable_cnp() simplified (per Julien)
RFC -> v1
- dropped RFC tag
- rebased on 4.14-rc4
Thanks!
Vladimir Murzin (3):
arm64: mm: Support Common Not Private translations
arm64: KVM: Enable Common Not Private translations
arm64: Introduce command line parameter to disable CNP
Documentation/admin-guide/kernel-parameters.txt | 4 +++
arch/arm/include/asm/kvm_arm.h | 1 +
arch/arm/include/asm/kvm_mmu.h | 5 +++
arch/arm64/Kconfig | 13 ++++++++
arch/arm64/include/asm/cpucaps.h | 3 +-
arch/arm64/include/asm/cpufeature.h | 6 ++++
arch/arm64/include/asm/kvm_arm.h | 1 +
arch/arm64/include/asm/kvm_mmu.h | 5 +++
arch/arm64/include/asm/mmu_context.h | 12 +++++++
arch/arm64/include/asm/pgtable-hwdef.h | 2 ++
arch/arm64/kernel/cpufeature.c | 44 +++++++++++++++++++++++++
arch/arm64/kernel/suspend.c | 4 +++
arch/arm64/kvm/hyp-init.S | 3 ++
arch/arm64/mm/context.c | 3 ++
arch/arm64/mm/proc.S | 6 ++++
virt/kvm/arm/arm.c | 4 +--
16 files changed, 113 insertions(+), 3 deletions(-)
--
2.0.0
^ permalink raw reply
* [PATCH v5 1/3] arm64: mm: Support Common Not Private translations
From: Vladimir Murzin @ 2018-06-19 10:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403502-2843-1-git-send-email-vladimir.murzin@arm.com>
Common Not Private (CNP) is a feature of ARMv8.2 extension which
allows translation table entries to be shared between different PEs in
the same inner shareable domain, so the hardware can use this fact to
optimise the caching of such entries in the TLB.
CNP occupies one bit in TTBRx_ELy and VTTBR_EL2, which advertises to
the hardware that the translation table entries pointed to by this
TTBR are the same as every PE in the same inner shareable domain for
which the equivalent TTBR also has CNP bit set. In case CNP bit is set
but TTBR does not point at the same translation table entries for a
given ASID and VMID, then the system is mis-configured, so the results
of translations are UNPREDICTABLE.
For kernel we postpone setting CNP till all cpus are up and rely on
cpufeature framework to 1) patch the code which is sensitive to CNP
and 2) update TTBR1_EL1 with CNP bit set. TTBR1_EL1 can be
reprogrammed as result of hibernation or cpuidle (via __enable_mmu).
For these two cases we restore CNP bit via __cpu_suspend_exit().
There are a few cases we need to care of changes in TTBR0_EL1:
- a switch to idmap
- software emulated PAN
we rule out latter via Kconfig options and for the former we make
sure that CNP is set for non-zero ASIDs only.
Reviewed-by: James Morse <james.morse@arm.com>
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
arch/arm64/Kconfig | 13 +++++++++++++
arch/arm64/include/asm/cpucaps.h | 3 ++-
arch/arm64/include/asm/cpufeature.h | 6 ++++++
arch/arm64/include/asm/mmu_context.h | 12 ++++++++++++
arch/arm64/include/asm/pgtable-hwdef.h | 2 ++
arch/arm64/kernel/cpufeature.c | 35 ++++++++++++++++++++++++++++++++++
arch/arm64/kernel/suspend.c | 4 ++++
arch/arm64/mm/context.c | 3 +++
arch/arm64/mm/proc.S | 6 ++++++
9 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 42c090c..70a4ad3 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1124,6 +1124,19 @@ config ARM64_RAS_EXTN
and access the new registers if the system supports the extension.
Platform RAS features may additionally depend on firmware support.
+config ARM64_CNP
+ bool "Enable support for Common Not Private (CNP) translations"
+ depends on ARM64_PAN || !ARM64_SW_TTBR0_PAN
+ help
+ Common Not Private (CNP) allows translation table entries to
+ be shared between different PEs in the same inner shareable
+ domain, so the hardware can use this fact to optimise the
+ caching of such entries in the TLB.
+
+ Selecting this option allows the CNP feature to be detected
+ at runtime, and does not affect PEs that do not implement
+ this feature.
+
endmenu
config ARM64_SVE
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 8a699c7..7219654 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -49,7 +49,8 @@
#define ARM64_HAS_CACHE_DIC 28
#define ARM64_HW_DBM 29
#define ARM64_SSBD 30
+#define ARM64_HAS_CNP 31
-#define ARM64_NCAPS 31
+#define ARM64_NCAPS 32
#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 1717ba1..b5e2440 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -508,6 +508,12 @@ static inline bool system_supports_sve(void)
cpus_have_const_cap(ARM64_SVE);
}
+static inline bool system_supports_cnp(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_CNP) &&
+ cpus_have_const_cap(ARM64_HAS_CNP);
+}
+
#define ARM64_SSBD_UNKNOWN -1
#define ARM64_SSBD_FORCE_DISABLE 0
#define ARM64_SSBD_KERNEL 1
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 39ec0b8..c506fb7 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -149,6 +149,18 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)
phys_addr_t pgd_phys = virt_to_phys(pgdp);
+ if (system_supports_cnp() && !WARN_ON(pgdp != lm_alias(swapper_pg_dir))) {
+ /*
+ * cpu_replace_ttbr1() is used when there's a boot CPU
+ * up (i.e. cpufeature framework is not up yet) and
+ * latter only when we enable CNP via cpufeature's
+ * enable() callback.
+ * Also we rely on the cpu_hwcap bit being set before
+ * calling the enable() function.
+ */
+ pgd_phys |= TTBR_CNP_BIT;
+ }
+
replace_phys = (void *)__pa_symbol(idmap_cpu_replace_ttbr1);
cpu_install_idmap();
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index fd208ea..1d7d8da 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -211,6 +211,8 @@
#define PHYS_MASK_SHIFT (CONFIG_ARM64_PA_BITS)
#define PHYS_MASK ((UL(1) << PHYS_MASK_SHIFT) - 1)
+#define TTBR_CNP_BIT (UL(1) << 0)
+
/*
* TCR flags.
*/
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index d2856b1..e0129f2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -20,6 +20,7 @@
#include <linux/bsearch.h>
#include <linux/cpumask.h>
+#include <linux/crash_dump.h>
#include <linux/sort.h>
#include <linux/stop_machine.h>
#include <linux/types.h>
@@ -117,6 +118,7 @@ EXPORT_SYMBOL(cpu_hwcap_keys);
static bool __maybe_unused
cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused);
+static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);
/*
* NOTE: Any changes to the visibility of features should be kept in
@@ -858,6 +860,21 @@ static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,
return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT);
}
+static bool __maybe_unused
+has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
+{
+#ifdef CONFIG_CRASH_DUMP
+ /*
+ * Kdump isn't guaranteed to power-off all secondary CPUs, CNP
+ * may share TLB entries with a CPU stuck in the crashed
+ * kernel.
+ */
+ if (elfcorehdr_size)
+ return false;
+#endif
+ return has_cpuid_feature(entry, scope);
+}
+
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */
@@ -1202,6 +1219,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.cpu_enable = cpu_enable_hw_dbm,
},
#endif
+#ifdef CONFIG_ARM64_CNP
+ {
+ .desc = "Common not Private translations",
+ .capability = ARM64_HAS_CNP,
+ .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .matches = has_useable_cnp,
+ .sys_reg = SYS_ID_AA64MMFR2_EL1,
+ .sign = FTR_UNSIGNED,
+ .field_pos = ID_AA64MMFR2_CNP_SHIFT,
+ .min_field_value = 1,
+ .cpu_enable = cpu_enable_cnp,
+ },
+#endif
{},
};
@@ -1638,6 +1668,11 @@ cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO));
}
+static void __maybe_unused cpu_enable_cnp(struct arm64_cpu_capabilities const *cap)
+{
+ cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
+}
+
/*
* We emulate only the following system register space.
* Op0 = 0x3, CRn = 0x0, Op1 = 0x0, CRm = [0, 4 - 7]
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 70c2833..9405d1b 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -48,6 +48,10 @@ void notrace __cpu_suspend_exit(void)
*/
cpu_uninstall_idmap();
+ /* Restore CnP bit in TTBR1_EL1 */
+ if (system_supports_cnp())
+ cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
+
/*
* PSTATE was not saved over suspend/resume, re-enable any detected
* features that might not have been set correctly.
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index c127f94..a65af49 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -196,6 +196,9 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
unsigned long flags;
u64 asid, old_active_asid;
+ if (system_supports_cnp())
+ cpu_set_reserved_ttbr0();
+
asid = atomic64_read(&mm->context.id);
/*
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 5f9a73a..d46d0ca 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -160,6 +160,12 @@ ENTRY(cpu_do_switch_mm)
mrs x2, ttbr1_el1
mmid x1, x1 // get mm->context.id
phys_to_ttbr x3, x0
+
+alternative_if ARM64_HAS_CNP
+ cbz x1, 1f // skip CNP for reserved ASID
+ orr x3, x3, #TTBR_CNP_BIT
+1:
+alternative_else_nop_endif
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
bfi x3, x1, #48, #16 // set the ASID field in TTBR0
#endif
--
2.0.0
^ permalink raw reply related
* [PATCH v5 2/3] arm64: KVM: Enable Common Not Private translations
From: Vladimir Murzin @ 2018-06-19 10:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403502-2843-1-git-send-email-vladimir.murzin@arm.com>
We rely on cpufeature framework to detect and enable CNP so for KVM we
need to patch hyp to set CNP bit just before TTBR0_EL2 gets written.
For the guest we encode CNP bit while building vttbr, so we don't need
to bother with that in a world switch.
Reviewed-by: James Morse <james.morse@arm.com>
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
arch/arm/include/asm/kvm_arm.h | 1 +
arch/arm/include/asm/kvm_mmu.h | 5 +++++
arch/arm64/include/asm/kvm_arm.h | 1 +
arch/arm64/include/asm/kvm_mmu.h | 5 +++++
arch/arm64/kvm/hyp-init.S | 3 +++
virt/kvm/arm/arm.c | 4 ++--
6 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index 3ab8b37..2d43dca 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -161,6 +161,7 @@
#else
#define VTTBR_X (5 - KVM_T0SZ)
#endif
+#define VTTBR_CNP_BIT _AC(1, UL)
#define VTTBR_BADDR_MASK (((_AC(1, ULL) << (40 - VTTBR_X)) - 1) << VTTBR_X)
#define VTTBR_VMID_SHIFT _AC(48, ULL)
#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 8553d68..98d1753 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -363,6 +363,11 @@ static inline int hyp_map_aux_data(void)
#define kvm_phys_to_vttbr(addr) (addr)
+static inline bool kvm_cpu_has_cnp(void)
+{
+ return false;
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 6dd285e..0b5a849 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -174,6 +174,7 @@
#define VTCR_EL2_FLAGS (VTCR_EL2_COMMON_BITS | VTCR_EL2_TGRAN_FLAGS)
#define VTTBR_X (VTTBR_X_TGRAN_MAGIC - VTCR_EL2_T0SZ_IPA)
+#define VTTBR_CNP_BIT (UL(1))
#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_X)
#define VTTBR_VMID_SHIFT (UL(48))
#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index fb9a712..b66b13f 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -498,5 +498,10 @@ static inline int hyp_map_aux_data(void)
#define kvm_phys_to_vttbr(addr) phys_to_ttbr(addr)
+static inline bool kvm_cpu_has_cnp(void)
+{
+ return system_supports_cnp();
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 6fd91b3..94998d5 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -64,6 +64,9 @@ __do_hyp_init:
b.lo __kvm_handle_stub_hvc
phys_to_ttbr x4, x0
+alternative_if ARM64_HAS_CNP
+ orr x4, x4, #TTBR_CNP_BIT
+alternative_else_nop_endif
msr ttbr0_el2, x4
mrs x4, tcr_el1
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 04e554c..221b127 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -490,7 +490,7 @@ static bool need_new_vmid_gen(struct kvm *kvm)
static void update_vttbr(struct kvm *kvm)
{
phys_addr_t pgd_phys;
- u64 vmid;
+ u64 vmid, cnp = kvm_cpu_has_cnp() ? VTTBR_CNP_BIT : 0;
bool new_gen;
read_lock(&kvm_vmid_lock);
@@ -540,7 +540,7 @@ static void update_vttbr(struct kvm *kvm)
pgd_phys = virt_to_phys(kvm->arch.pgd);
BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK);
vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK(kvm_vmid_bits);
- kvm->arch.vttbr = kvm_phys_to_vttbr(pgd_phys) | vmid;
+ kvm->arch.vttbr = kvm_phys_to_vttbr(pgd_phys) | vmid | cnp;
write_unlock(&kvm_vmid_lock);
}
--
2.0.0
^ permalink raw reply related
* [PATCH v5 3/3] arm64: Introduce command line parameter to disable CNP
From: Vladimir Murzin @ 2018-06-19 10:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529403502-2843-1-git-send-email-vladimir.murzin@arm.com>
There are cases when activating of Common Not Private (CNP) feature
might not be desirable; this patch allows to forcefully disable CNP
even it is supported by hardware.
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
Documentation/admin-guide/kernel-parameters.txt | 4 ++++
arch/arm64/kernel/cpufeature.c | 11 ++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index efc7aa7..4abc485 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2648,6 +2648,10 @@
noclflush [BUGS=X86] Don't use the CLFLUSH instruction
+ nocnp [ARM64]
+ Disable CNP (Common not Private translations)
+ even if it is supported by processor.
+
nodelayacct [KNL] Disable per-task delay accounting
nodsp [SH] Disable hardware DSP at boot time.
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index e0129f2..270fb3b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -860,6 +860,15 @@ static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,
return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT);
}
+static bool nocnp;
+
+static int __init early_nocnp(char *p)
+{
+ nocnp = true;
+ return 0;
+}
+early_param("nocnp", early_nocnp);
+
static bool __maybe_unused
has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
{
@@ -872,7 +881,7 @@ has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
if (elfcorehdr_size)
return false;
#endif
- return has_cpuid_feature(entry, scope);
+ return has_cpuid_feature(entry, scope) && !nocnp;
}
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
--
2.0.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