* [PATCH v3 4/9] powerpc/pseries: Introduce rwlock to gatekeep DTLB usage
From: Naveen N. Rao @ 2019-07-03 17:03 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
Since we would be introducing a new user of the DTL buffer in a
subsequent patch, we need a way to gatekeep use of the DTL buffer.
The current debugfs interface for DTL allows registering and opening
cpu-specific DTL buffers. Cpu specific files are exposed under
debugfs 'powerpc/dtl/' node, and changing 'dtl_event_mask' in the same
directory enables controlling the event mask used when registering DTL
buffer for a particular cpu.
Subsequently, we will be introducing a user of the DTL buffers that
registers access to the DTL buffers across all cpus with the same event
mask. To ensure these two users do not step on each other, we introduce
a rwlock to gatekeep DTL buffer access. This fits the requirement of the
current debugfs interface wanting to allow multiple independent
cpu-specific users (read lock), and the subsequent user wanting
exclusive access (write lock).
Suggested-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/lppaca.h | 2 ++
arch/powerpc/platforms/pseries/dtl.c | 11 ++++++++++-
arch/powerpc/platforms/pseries/lpar.c | 4 ++++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h
index 060697768287..837a43a1b578 100644
--- a/arch/powerpc/include/asm/lppaca.h
+++ b/arch/powerpc/include/asm/lppaca.h
@@ -19,6 +19,7 @@
*/
#include <linux/cache.h>
#include <linux/threads.h>
+#include <linux/spinlock_types.h>
#include <asm/types.h>
#include <asm/mmu.h>
#include <asm/firmware.h>
@@ -153,6 +154,7 @@ struct dtl_entry {
#define DTL_LOG_ALL (DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT)
extern struct kmem_cache *dtl_cache;
+extern rwlock_t dtl_access_lock;
/*
* When CONFIG_VIRT_CPU_ACCOUNTING_NATIVE = y, the cpu accounting code controls
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index a88176afba32..2b87480f2837 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -180,11 +180,16 @@ static int dtl_enable(struct dtl *dtl)
if (dtl->buf)
return -EBUSY;
+ /* ensure there are no other conflicting dtl users */
+ if (!read_trylock(&dtl_access_lock))
+ return -EBUSY;
+
n_entries = dtl_buf_entries;
buf = kmem_cache_alloc_node(dtl_cache, GFP_KERNEL, cpu_to_node(dtl->cpu));
if (!buf) {
printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
__func__, dtl->cpu);
+ read_unlock(&dtl_access_lock);
return -ENOMEM;
}
@@ -201,8 +206,11 @@ static int dtl_enable(struct dtl *dtl)
}
spin_unlock(&dtl->lock);
- if (rc)
+ if (rc) {
+ read_unlock(&dtl_access_lock);
kmem_cache_free(dtl_cache, buf);
+ }
+
return rc;
}
@@ -214,6 +222,7 @@ static void dtl_disable(struct dtl *dtl)
dtl->buf = NULL;
dtl->buf_entries = 0;
spin_unlock(&dtl->lock);
+ read_unlock(&dtl_access_lock);
}
/* file interface */
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 8c5377fe9985..1e105a3a9f83 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -99,6 +99,10 @@ void register_dtl_buffer(int cpu)
}
}
+#ifdef CONFIG_PPC_SPLPAR
+DEFINE_RWLOCK(dtl_access_lock);
+#endif /* CONFIG_PPC_SPLPAR */
+
void vpa_init(int cpu)
{
int hwcpu = get_hard_smp_processor_id(cpu);
--
2.22.0
^ permalink raw reply related
* [PATCH v3 5/9] powerpc/pseries: Generalize hcall_vphn()
From: Naveen N. Rao @ 2019-07-03 17:03 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
H_HOME_NODE_ASSOCIATIVITY hcall can take two different flags and return
different associativity information in each case. Generalize the
existing hcall_vphn() function to take flags as an argument and to
return the result. Update the only existing user to pass the proper
arguments.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
arch/powerpc/mm/book3s64/vphn.h | 8 ++++++++
arch/powerpc/mm/numa.c | 27 +++++++++++++--------------
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/arch/powerpc/mm/book3s64/vphn.h b/arch/powerpc/mm/book3s64/vphn.h
index f0b93c2dd578..f7ff1e0c3801 100644
--- a/arch/powerpc/mm/book3s64/vphn.h
+++ b/arch/powerpc/mm/book3s64/vphn.h
@@ -11,6 +11,14 @@
*/
#define VPHN_ASSOC_BUFSIZE (VPHN_REGISTER_COUNT*sizeof(u64)/sizeof(u16) + 1)
+/*
+ * The H_HOME_NODE_ASSOCIATIVITY hcall takes two values for flags:
+ * 1 for retrieving associativity information for a guest cpu
+ * 2 for retrieving associativity information for a host/hypervisor cpu
+ */
+#define VPHN_FLAG_VCPU 1
+#define VPHN_FLAG_PCPU 2
+
extern int vphn_unpack_associativity(const long *packed, __be32 *unpacked);
#endif
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 917904d2fe97..42ea79514420 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1083,6 +1083,17 @@ static void reset_topology_timer(void);
static int topology_timer_secs = 1;
static int topology_inited;
+static long hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity)
+{
+ long rc;
+ long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
+
+ rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, cpu);
+ vphn_unpack_associativity(retbuf, associativity);
+
+ return rc;
+}
+
/*
* Change polling interval for associativity changes.
*/
@@ -1161,25 +1172,13 @@ static int update_cpu_associativity_changes_mask(void)
* Retrieve the new associativity information for a virtual processor's
* home node.
*/
-static long hcall_vphn(unsigned long cpu, __be32 *associativity)
-{
- long rc;
- long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
- u64 flags = 1;
- int hwcpu = get_hard_smp_processor_id(cpu);
-
- rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, hwcpu);
- vphn_unpack_associativity(retbuf, associativity);
-
- return rc;
-}
-
static long vphn_get_associativity(unsigned long cpu,
__be32 *associativity)
{
long rc;
- rc = hcall_vphn(cpu, associativity);
+ rc = hcall_vphn(get_hard_smp_processor_id(cpu),
+ VPHN_FLAG_VCPU, associativity);
switch (rc) {
case H_FUNCTION:
--
2.22.0
^ permalink raw reply related
* [PATCH v3 6/9] powerpc/pseries: Move mm/book3s64/vphn.c under platforms/pseries/
From: Naveen N. Rao @ 2019-07-03 17:03 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
hcall_vphn() is specific to pseries and will be used in a subsequent
patch. So, move it to a more appropriate place under
arch/powerpc/platforms/pseries. Also merge vphn.h into lppaca.h
and update vphn selftest to use the new files.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/lppaca.h | 24 +++++++++++++++++++
arch/powerpc/mm/book3s64/Makefile | 1 -
arch/powerpc/mm/book3s64/vphn.h | 24 -------------------
arch/powerpc/mm/numa.c | 14 -----------
arch/powerpc/platforms/pseries/Makefile | 1 +
.../{mm/book3s64 => platforms/pseries}/vphn.c | 20 ++++++++++++++--
tools/testing/selftests/powerpc/vphn/Makefile | 2 +-
.../selftests/powerpc/vphn/asm/lppaca.h | 1 +
tools/testing/selftests/powerpc/vphn/vphn.c | 2 +-
tools/testing/selftests/powerpc/vphn/vphn.h | 1 -
10 files changed, 46 insertions(+), 44 deletions(-)
delete mode 100644 arch/powerpc/mm/book3s64/vphn.h
rename arch/powerpc/{mm/book3s64 => platforms/pseries}/vphn.c (80%)
create mode 120000 tools/testing/selftests/powerpc/vphn/asm/lppaca.h
delete mode 120000 tools/testing/selftests/powerpc/vphn/vphn.h
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h
index 837a43a1b578..f60257c9766d 100644
--- a/arch/powerpc/include/asm/lppaca.h
+++ b/arch/powerpc/include/asm/lppaca.h
@@ -5,6 +5,29 @@
*/
#ifndef _ASM_POWERPC_LPPACA_H
#define _ASM_POWERPC_LPPACA_H
+
+/*
+ * The below VPHN macros are outside the __KERNEL__ check since these are
+ * used for compiling the vphn selftest in userspace
+ */
+
+/* The H_HOME_NODE_ASSOCIATIVITY h_call returns 6 64-bit registers. */
+#define VPHN_REGISTER_COUNT 6
+
+/*
+ * 6 64-bit registers unpacked into up to 24 be32 associativity values. To
+ * form the complete property we have to add the length in the first cell.
+ */
+#define VPHN_ASSOC_BUFSIZE (VPHN_REGISTER_COUNT*sizeof(u64)/sizeof(u16) + 1)
+
+/*
+ * The H_HOME_NODE_ASSOCIATIVITY hcall takes two values for flags:
+ * 1 for retrieving associativity information for a guest cpu
+ * 2 for retrieving associativity information for a host/hypervisor cpu
+ */
+#define VPHN_FLAG_VCPU 1
+#define VPHN_FLAG_PCPU 2
+
#ifdef __KERNEL__
/*
@@ -166,6 +189,7 @@ extern void (*dtl_consumer)(struct dtl_entry *entry, u64 index);
extern void register_dtl_buffer(int cpu);
extern void alloc_dtl_buffers(void);
+extern long hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity);
#endif /* CONFIG_PPC_BOOK3S */
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/mm/book3s64/Makefile b/arch/powerpc/mm/book3s64/Makefile
index 974b4fc19f4f..fd393b8be14f 100644
--- a/arch/powerpc/mm/book3s64/Makefile
+++ b/arch/powerpc/mm/book3s64/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_PPC_NATIVE) += hash_native.o
obj-$(CONFIG_PPC_RADIX_MMU) += radix_pgtable.o radix_tlb.o
obj-$(CONFIG_PPC_4K_PAGES) += hash_4k.o
obj-$(CONFIG_PPC_64K_PAGES) += hash_64k.o
-obj-$(CONFIG_PPC_SPLPAR) += vphn.o
obj-$(CONFIG_HUGETLB_PAGE) += hash_hugetlbpage.o
ifdef CONFIG_HUGETLB_PAGE
obj-$(CONFIG_PPC_RADIX_MMU) += radix_hugetlbpage.o
diff --git a/arch/powerpc/mm/book3s64/vphn.h b/arch/powerpc/mm/book3s64/vphn.h
deleted file mode 100644
index f7ff1e0c3801..000000000000
--- a/arch/powerpc/mm/book3s64/vphn.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ARCH_POWERPC_MM_VPHN_H_
-#define _ARCH_POWERPC_MM_VPHN_H_
-
-/* The H_HOME_NODE_ASSOCIATIVITY h_call returns 6 64-bit registers. */
-#define VPHN_REGISTER_COUNT 6
-
-/*
- * 6 64-bit registers unpacked into up to 24 be32 associativity values. To
- * form the complete property we have to add the length in the first cell.
- */
-#define VPHN_ASSOC_BUFSIZE (VPHN_REGISTER_COUNT*sizeof(u64)/sizeof(u16) + 1)
-
-/*
- * The H_HOME_NODE_ASSOCIATIVITY hcall takes two values for flags:
- * 1 for retrieving associativity information for a guest cpu
- * 2 for retrieving associativity information for a host/hypervisor cpu
- */
-#define VPHN_FLAG_VCPU 1
-#define VPHN_FLAG_PCPU 2
-
-extern int vphn_unpack_associativity(const long *packed, __be32 *unpacked);
-
-#endif
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 42ea79514420..77f673717362 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1063,9 +1063,6 @@ u64 memory_hotplug_max(void)
/* Virtual Processor Home Node (VPHN) support */
#ifdef CONFIG_PPC_SPLPAR
-
-#include "book3s64/vphn.h"
-
struct topology_update_data {
struct topology_update_data *next;
unsigned int cpu;
@@ -1083,17 +1080,6 @@ static void reset_topology_timer(void);
static int topology_timer_secs = 1;
static int topology_inited;
-static long hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity)
-{
- long rc;
- long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
-
- rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, cpu);
- vphn_unpack_associativity(retbuf, associativity);
-
- return rc;
-}
-
/*
* Change polling interval for associativity changes.
*/
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index a43ec843c8e2..ab3d59aeacca 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_LPARCFG) += lparcfg.o
obj-$(CONFIG_IBMVIO) += vio.o
obj-$(CONFIG_IBMEBUS) += ibmebus.o
obj-$(CONFIG_PAPR_SCM) += papr_scm.o
+obj-$(CONFIG_PPC_SPLPAR) += vphn.o
ifdef CONFIG_PPC_PSERIES
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/mm/book3s64/vphn.c b/arch/powerpc/platforms/pseries/vphn.c
similarity index 80%
rename from arch/powerpc/mm/book3s64/vphn.c
rename to arch/powerpc/platforms/pseries/vphn.c
index 0ee7734afb50..3f07bf6c670e 100644
--- a/arch/powerpc/mm/book3s64/vphn.c
+++ b/arch/powerpc/platforms/pseries/vphn.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <asm/byteorder.h>
-#include "vphn.h"
+#include <asm/lppaca.h>
/*
* The associativity domain numbers are returned from the hypervisor as a
@@ -22,7 +22,7 @@
*
* Convert to the sequence they would appear in the ibm,associativity property.
*/
-int vphn_unpack_associativity(const long *packed, __be32 *unpacked)
+static int vphn_unpack_associativity(const long *packed, __be32 *unpacked)
{
__be64 be_packed[VPHN_REGISTER_COUNT];
int i, nr_assoc_doms = 0;
@@ -71,3 +71,19 @@ int vphn_unpack_associativity(const long *packed, __be32 *unpacked)
return nr_assoc_doms;
}
+
+/* NOTE: This file is included by a selftest and built in userspace. */
+#ifdef __KERNEL__
+#include <asm/hvcall.h>
+
+long hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity)
+{
+ long rc;
+ long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
+
+ rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, cpu);
+ vphn_unpack_associativity(retbuf, associativity);
+
+ return rc;
+}
+#endif
diff --git a/tools/testing/selftests/powerpc/vphn/Makefile b/tools/testing/selftests/powerpc/vphn/Makefile
index 18b885da01bd..cf65cbf33085 100644
--- a/tools/testing/selftests/powerpc/vphn/Makefile
+++ b/tools/testing/selftests/powerpc/vphn/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
TEST_GEN_PROGS := test-vphn
-CFLAGS += -m64
+CFLAGS += -m64 -I$(CURDIR)
top_srcdir = ../../../../..
include ../../lib.mk
diff --git a/tools/testing/selftests/powerpc/vphn/asm/lppaca.h b/tools/testing/selftests/powerpc/vphn/asm/lppaca.h
new file mode 120000
index 000000000000..942b1d00999c
--- /dev/null
+++ b/tools/testing/selftests/powerpc/vphn/asm/lppaca.h
@@ -0,0 +1 @@
+../../../../../../arch/powerpc/include/asm/lppaca.h
\ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/vphn/vphn.c b/tools/testing/selftests/powerpc/vphn/vphn.c
index 1d1f5f2be3b2..5b5fbddccabd 120000
--- a/tools/testing/selftests/powerpc/vphn/vphn.c
+++ b/tools/testing/selftests/powerpc/vphn/vphn.c
@@ -1 +1 @@
-../../../../../arch/powerpc/mm/book3s64/vphn.c
\ No newline at end of file
+../../../../../arch/powerpc/platforms/pseries/vphn.c
\ No newline at end of file
diff --git a/tools/testing/selftests/powerpc/vphn/vphn.h b/tools/testing/selftests/powerpc/vphn/vphn.h
deleted file mode 120000
index 45fe160f8288..000000000000
--- a/tools/testing/selftests/powerpc/vphn/vphn.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../arch/powerpc/mm/book3s64/vphn.h
\ No newline at end of file
--
2.22.0
^ permalink raw reply related
* [PATCH v3 7/9] powerpc/pseries: Provide vcpu dispatch statistics
From: Naveen N. Rao @ 2019-07-03 17:04 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
For Shared Processor LPARs, the POWER Hypervisor maintains a relatively
static mapping of the LPAR processors (vcpus) to physical processor
chips (representing the "home" node) and tries to always dispatch vcpus
on their associated physical processor chip. However, under certain
scenarios, vcpus may be dispatched on a different processor chip (away
from its home node). The actual physical processor number on which a
certain vcpu is dispatched is available to the guest in the
'processor_id' field of each DTL entry.
The guest can discover the home node of each vcpu through the
H_HOME_NODE_ASSOCIATIVITY(flags=1) hcall. The guest can also discover
the associativity of physical processors, as represented in the DTL
entry, through the H_HOME_NODE_ASSOCIATIVITY(flags=2) hcall.
These can then be compared to determine if the vcpu was dispatched on
its home node or not. If the vcpu was not dispatched on the home node,
it is possible to determine if the vcpu was dispatched in a different
chip, socket or drawer.
Introduce a procfs file /proc/powerpc/vcpudispatch_stats that can be
used to obtain these statistics. Writing '1' to this file enables
collecting the statistics, while writing '0' disables the statistics.
The statistics themselves are available by reading the procfs file. By
default, the DTLB log for each vcpu is processed 50 times a second so as
not to miss any entries. This processing frequency can be changed
through /proc/powerpc/vcpudispatch_stats_freq.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/topology.h | 6 +
arch/powerpc/mm/numa.c | 16 +
arch/powerpc/platforms/pseries/lpar.c | 525 +++++++++++++++++++++++++-
3 files changed, 545 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index f85e2b01c3df..2f7e1ea5089e 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -35,6 +35,7 @@ static inline int pcibus_to_node(struct pci_bus *bus)
cpu_all_mask : \
cpumask_of_node(pcibus_to_node(bus)))
+extern int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc);
extern int __node_distance(int, int);
#define node_distance(a, b) __node_distance(a, b)
@@ -84,6 +85,11 @@ static inline int numa_update_cpu_topology(bool cpus_locked)
static inline void update_numa_cpu_lookup_table(unsigned int cpu, int node) {}
+static inline int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc)
+{
+ return 0;
+}
+
#endif /* CONFIG_NUMA */
#if defined(CONFIG_NUMA) && defined(CONFIG_PPC_SPLPAR)
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 77f673717362..946dda2470a8 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -163,6 +163,22 @@ static void unmap_cpu_from_node(unsigned long cpu)
}
#endif /* CONFIG_HOTPLUG_CPU || CONFIG_PPC_SPLPAR */
+int cpu_distance(__be32 *cpu1_assoc, __be32 *cpu2_assoc)
+{
+ int dist = 0;
+
+ int i, index;
+
+ for (i = 0; i < distance_ref_points_depth; i++) {
+ index = be32_to_cpu(distance_ref_points[i]);
+ if (cpu1_assoc[index] == cpu2_assoc[index])
+ break;
+ dist++;
+ }
+
+ return dist;
+}
+
/* must hold reference to node during call */
static const __be32 *of_get_associativity(struct device_node *dev)
{
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 1e105a3a9f83..951a3d755190 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -17,6 +17,10 @@
#include <linux/jump_label.h>
#include <linux/delay.h>
#include <linux/stop_machine.h>
+#include <linux/spinlock.h>
+#include <linux/cpuhotplug.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/page.h>
@@ -52,6 +56,12 @@ EXPORT_SYMBOL(plpar_hcall);
EXPORT_SYMBOL(plpar_hcall9);
EXPORT_SYMBOL(plpar_hcall_norets);
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+static u8 dtl_mask = DTL_LOG_PREEMPT;
+#else
+static u8 dtl_mask;
+#endif
+
void alloc_dtl_buffers(void)
{
int cpu;
@@ -60,11 +70,15 @@ void alloc_dtl_buffers(void)
for_each_possible_cpu(cpu) {
pp = paca_ptrs[cpu];
+ if (pp->dispatch_log)
+ continue;
dtl = kmem_cache_alloc(dtl_cache, GFP_KERNEL);
if (!dtl) {
pr_warn("Failed to allocate dispatch trace log for cpu %d\n",
cpu);
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
pr_warn("Stolen time statistics will be unreliable\n");
+#endif
break;
}
@@ -84,7 +98,7 @@ void register_dtl_buffer(int cpu)
pp = paca_ptrs[cpu];
dtl = pp->dispatch_log;
- if (dtl) {
+ if (dtl && dtl_mask) {
pp->dtl_ridx = 0;
pp->dtl_curr = dtl;
lppaca_of(cpu).dtl_idx = 0;
@@ -95,12 +109,519 @@ void register_dtl_buffer(int cpu)
if (ret)
pr_err("WARNING: DTL registration of cpu %d (hw %d) "
"failed with %ld\n", cpu, hwcpu, ret);
- lppaca_of(cpu).dtl_enable_mask = DTL_LOG_PREEMPT;
+ lppaca_of(cpu).dtl_enable_mask = dtl_mask;
}
}
#ifdef CONFIG_PPC_SPLPAR
+struct dtl_worker {
+ struct delayed_work work;
+ int cpu;
+};
+
+struct vcpu_dispatch_data {
+ int last_disp_cpu;
+
+ int total_disp;
+
+ int same_cpu_disp;
+ int same_chip_disp;
+ int diff_chip_disp;
+ int far_chip_disp;
+
+ int numa_home_disp;
+ int numa_remote_disp;
+ int numa_far_disp;
+};
+
+/*
+ * This represents the number of cpus in the hypervisor. Since there is no
+ * architected way to discover the number of processors in the host, we
+ * provision for dealing with NR_CPUS. This is currently 2048 by default, and
+ * is sufficient for our purposes. This will need to be tweaked if
+ * CONFIG_NR_CPUS is changed.
+ */
+#define NR_CPUS_H NR_CPUS
+
DEFINE_RWLOCK(dtl_access_lock);
+static DEFINE_PER_CPU(struct vcpu_dispatch_data, vcpu_disp_data);
+static DEFINE_PER_CPU(u64, dtl_entry_ridx);
+static DEFINE_PER_CPU(struct dtl_worker, dtl_workers);
+static enum cpuhp_state dtl_worker_state;
+static DEFINE_MUTEX(dtl_enable_mutex);
+static int vcpudispatch_stats_on __read_mostly;
+static int vcpudispatch_stats_freq = 50;
+static __be32 *vcpu_associativity, *pcpu_associativity;
+
+
+static void free_dtl_buffers(void)
+{
+#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ int cpu;
+ struct paca_struct *pp;
+
+ for_each_possible_cpu(cpu) {
+ pp = paca_ptrs[cpu];
+ if (!pp->dispatch_log)
+ continue;
+ kmem_cache_free(dtl_cache, pp->dispatch_log);
+ pp->dtl_ridx = 0;
+ pp->dispatch_log = 0;
+ pp->dispatch_log_end = 0;
+ pp->dtl_curr = 0;
+ }
+#endif
+}
+
+static int init_cpu_associativity(void)
+{
+ vcpu_associativity = kcalloc(num_possible_cpus() / threads_per_core,
+ VPHN_ASSOC_BUFSIZE * sizeof(__be32), GFP_KERNEL);
+ pcpu_associativity = kcalloc(NR_CPUS_H / threads_per_core,
+ VPHN_ASSOC_BUFSIZE * sizeof(__be32), GFP_KERNEL);
+
+ if (!vcpu_associativity || !pcpu_associativity) {
+ pr_err("error allocating memory for associativity information\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void destroy_cpu_associativity(void)
+{
+ kfree(vcpu_associativity);
+ kfree(pcpu_associativity);
+ vcpu_associativity = pcpu_associativity = 0;
+}
+
+static __be32 *__get_cpu_associativity(int cpu, __be32 *cpu_assoc, int flag)
+{
+ __be32 *assoc;
+ int rc = 0;
+
+ assoc = &cpu_assoc[(int)(cpu / threads_per_core) * VPHN_ASSOC_BUFSIZE];
+ if (!assoc[0]) {
+ rc = hcall_vphn(cpu, flag, &assoc[0]);
+ if (rc)
+ return NULL;
+ }
+
+ return assoc;
+}
+
+static __be32 *get_pcpu_associativity(int cpu)
+{
+ return __get_cpu_associativity(cpu, pcpu_associativity, VPHN_FLAG_PCPU);
+}
+
+static __be32 *get_vcpu_associativity(int cpu)
+{
+ return __get_cpu_associativity(cpu, vcpu_associativity, VPHN_FLAG_VCPU);
+}
+
+static int cpu_relative_dispatch_distance(int last_disp_cpu, int cur_disp_cpu)
+{
+ __be32 *last_disp_cpu_assoc, *cur_disp_cpu_assoc;
+
+ if (last_disp_cpu >= NR_CPUS_H || cur_disp_cpu >= NR_CPUS_H)
+ return -EINVAL;
+
+ last_disp_cpu_assoc = get_pcpu_associativity(last_disp_cpu);
+ cur_disp_cpu_assoc = get_pcpu_associativity(cur_disp_cpu);
+
+ if (!last_disp_cpu_assoc || !cur_disp_cpu_assoc)
+ return -EIO;
+
+ return cpu_distance(last_disp_cpu_assoc, cur_disp_cpu_assoc);
+}
+
+static int cpu_home_node_dispatch_distance(int disp_cpu)
+{
+ __be32 *disp_cpu_assoc, *vcpu_assoc;
+ int vcpu_id = smp_processor_id();
+
+ if (disp_cpu >= NR_CPUS_H) {
+ pr_debug_ratelimited("vcpu dispatch cpu %d > %d\n",
+ disp_cpu, NR_CPUS_H);
+ return -EINVAL;
+ }
+
+ disp_cpu_assoc = get_pcpu_associativity(disp_cpu);
+ vcpu_assoc = get_vcpu_associativity(vcpu_id);
+
+ if (!disp_cpu_assoc || !vcpu_assoc)
+ return -EIO;
+
+ return cpu_distance(disp_cpu_assoc, vcpu_assoc);
+}
+
+static void update_vcpu_disp_stat(int disp_cpu)
+{
+ struct vcpu_dispatch_data *disp;
+ int distance;
+
+ disp = this_cpu_ptr(&vcpu_disp_data);
+ if (disp->last_disp_cpu == -1) {
+ disp->last_disp_cpu = disp_cpu;
+ return;
+ }
+
+ disp->total_disp++;
+
+ if (disp->last_disp_cpu == disp_cpu ||
+ (cpu_first_thread_sibling(disp->last_disp_cpu) ==
+ cpu_first_thread_sibling(disp_cpu)))
+ disp->same_cpu_disp++;
+ else {
+ distance = cpu_relative_dispatch_distance(disp->last_disp_cpu,
+ disp_cpu);
+ if (distance < 0)
+ pr_debug_ratelimited("vcpudispatch_stats: cpu %d: error determining associativity\n",
+ smp_processor_id());
+ else {
+ switch (distance) {
+ case 0:
+ disp->same_chip_disp++;
+ break;
+ case 1:
+ disp->diff_chip_disp++;
+ break;
+ case 2:
+ disp->far_chip_disp++;
+ break;
+ default:
+ pr_debug_ratelimited("vcpudispatch_stats: cpu %d (%d -> %d): unexpected relative dispatch distance %d\n",
+ smp_processor_id(),
+ disp->last_disp_cpu,
+ disp_cpu,
+ distance);
+ }
+ }
+ }
+
+ distance = cpu_home_node_dispatch_distance(disp_cpu);
+ if (distance < 0)
+ pr_debug_ratelimited("vcpudispatch_stats: cpu %d: error determining associativity\n",
+ smp_processor_id());
+ else {
+ switch (distance) {
+ case 0:
+ disp->numa_home_disp++;
+ break;
+ case 1:
+ disp->numa_remote_disp++;
+ break;
+ case 2:
+ disp->numa_far_disp++;
+ break;
+ default:
+ pr_debug_ratelimited("vcpudispatch_stats: cpu %d on %d: unexpected numa dispatch distance %d\n",
+ smp_processor_id(),
+ disp_cpu,
+ distance);
+ }
+ }
+
+ disp->last_disp_cpu = disp_cpu;
+}
+
+static void process_dtl_buffer(struct work_struct *work)
+{
+ struct dtl_entry dtle;
+ u64 i = __this_cpu_read(dtl_entry_ridx);
+ struct dtl_entry *dtl = local_paca->dispatch_log + (i % N_DISPATCH_LOG);
+ struct dtl_entry *dtl_end = local_paca->dispatch_log_end;
+ struct lppaca *vpa = local_paca->lppaca_ptr;
+ struct dtl_worker *d = container_of(work, struct dtl_worker, work.work);
+
+ if (!local_paca->dispatch_log)
+ return;
+
+ /* if we have been migrated away, we cancel ourself */
+ if (d->cpu != smp_processor_id()) {
+ pr_debug("vcpudispatch_stats: cpu %d worker migrated -- canceling worker\n",
+ smp_processor_id());
+ return;
+ }
+
+ if (i == be64_to_cpu(vpa->dtl_idx))
+ goto out;
+
+ while (i < be64_to_cpu(vpa->dtl_idx)) {
+ dtle = *dtl;
+ barrier();
+ if (i + N_DISPATCH_LOG < be64_to_cpu(vpa->dtl_idx)) {
+ /* buffer has overflowed */
+ pr_debug_ratelimited("vcpudispatch_stats: cpu %d lost %lld DTL samples\n",
+ d->cpu,
+ be64_to_cpu(vpa->dtl_idx) - N_DISPATCH_LOG - i);
+ i = be64_to_cpu(vpa->dtl_idx) - N_DISPATCH_LOG;
+ dtl = local_paca->dispatch_log + (i % N_DISPATCH_LOG);
+ continue;
+ }
+ update_vcpu_disp_stat(be16_to_cpu(dtle.processor_id));
+ ++i;
+ ++dtl;
+ if (dtl == dtl_end)
+ dtl = local_paca->dispatch_log;
+ }
+
+ __this_cpu_write(dtl_entry_ridx, i);
+
+out:
+ schedule_delayed_work_on(d->cpu, to_delayed_work(work),
+ HZ / vcpudispatch_stats_freq);
+}
+
+static int dtl_worker_online(unsigned int cpu)
+{
+ struct dtl_worker *d = &per_cpu(dtl_workers, cpu);
+
+ memset(d, 0, sizeof(*d));
+ INIT_DELAYED_WORK(&d->work, process_dtl_buffer);
+ d->cpu = cpu;
+
+#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ per_cpu(dtl_entry_ridx, cpu) = 0;
+ register_dtl_buffer(cpu);
+#else
+ per_cpu(dtl_entry_ridx, cpu) = be64_to_cpu(lppaca_of(cpu).dtl_idx);
+#endif
+
+ schedule_delayed_work_on(cpu, &d->work, HZ / vcpudispatch_stats_freq);
+ return 0;
+}
+
+static int dtl_worker_offline(unsigned int cpu)
+{
+ struct dtl_worker *d = &per_cpu(dtl_workers, cpu);
+
+ cancel_delayed_work_sync(&d->work);
+
+#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ unregister_dtl(get_hard_smp_processor_id(cpu));
+#endif
+
+ return 0;
+}
+
+static void set_global_dtl_mask(u8 mask)
+{
+ int cpu;
+
+ dtl_mask = mask;
+ for_each_present_cpu(cpu)
+ lppaca_of(cpu).dtl_enable_mask = dtl_mask;
+}
+
+static void reset_global_dtl_mask(void)
+{
+ int cpu;
+
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
+ dtl_mask = DTL_LOG_PREEMPT;
+#else
+ dtl_mask = 0;
+#endif
+ for_each_present_cpu(cpu)
+ lppaca_of(cpu).dtl_enable_mask = dtl_mask;
+}
+
+static int dtl_worker_enable(void)
+{
+ int rc = 0, state;
+
+ if (!write_trylock(&dtl_access_lock)) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ set_global_dtl_mask(DTL_LOG_ALL);
+
+ /* Setup dtl buffers and register those */
+ alloc_dtl_buffers();
+
+ state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powerpc/dtl:online",
+ dtl_worker_online, dtl_worker_offline);
+ if (state < 0) {
+ pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
+ free_dtl_buffers();
+ reset_global_dtl_mask();
+ write_unlock(&dtl_access_lock);
+ rc = -EINVAL;
+ goto out;
+ }
+ dtl_worker_state = state;
+
+out:
+ return rc;
+}
+
+static void dtl_worker_disable(void)
+{
+ cpuhp_remove_state(dtl_worker_state);
+ free_dtl_buffers();
+ reset_global_dtl_mask();
+ write_unlock(&dtl_access_lock);
+}
+
+static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
+ size_t count, loff_t *ppos)
+{
+ struct vcpu_dispatch_data *disp;
+ int rc, cmd, cpu;
+ char buf[16];
+
+ if (count > 15)
+ return -EINVAL;
+
+ if (copy_from_user(buf, p, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+ rc = kstrtoint(buf, 0, &cmd);
+ if (rc || cmd < 0 || cmd > 1) {
+ pr_err("vcpudispatch_stats: please use 0 to disable or 1 to enable dispatch statistics\n");
+ return rc ? rc : -EINVAL;
+ }
+
+ mutex_lock(&dtl_enable_mutex);
+
+ if ((cmd == 0 && !vcpudispatch_stats_on) ||
+ (cmd == 1 && vcpudispatch_stats_on))
+ goto out;
+
+ if (cmd) {
+ rc = init_cpu_associativity();
+ if (rc)
+ goto out;
+
+ for_each_possible_cpu(cpu) {
+ disp = per_cpu_ptr(&vcpu_disp_data, cpu);
+ memset(disp, 0, sizeof(*disp));
+ disp->last_disp_cpu = -1;
+ }
+
+ rc = dtl_worker_enable();
+ if (rc) {
+ destroy_cpu_associativity();
+ goto out;
+ }
+ } else {
+ dtl_worker_disable();
+ destroy_cpu_associativity();
+ }
+
+ vcpudispatch_stats_on = cmd;
+
+out:
+ mutex_unlock(&dtl_enable_mutex);
+ if (rc)
+ return rc;
+ return count;
+}
+
+static int vcpudispatch_stats_display(struct seq_file *p, void *v)
+{
+ int cpu;
+ struct vcpu_dispatch_data *disp;
+
+ if (!vcpudispatch_stats_on) {
+ seq_puts(p, "off\n");
+ return 0;
+ }
+
+ for_each_online_cpu(cpu) {
+ disp = per_cpu_ptr(&vcpu_disp_data, cpu);
+ seq_printf(p, "cpu%d", cpu);
+ seq_put_decimal_ull(p, " ", disp->total_disp);
+ seq_put_decimal_ull(p, " ", disp->same_cpu_disp);
+ seq_put_decimal_ull(p, " ", disp->same_chip_disp);
+ seq_put_decimal_ull(p, " ", disp->diff_chip_disp);
+ seq_put_decimal_ull(p, " ", disp->far_chip_disp);
+ seq_put_decimal_ull(p, " ", disp->numa_home_disp);
+ seq_put_decimal_ull(p, " ", disp->numa_remote_disp);
+ seq_put_decimal_ull(p, " ", disp->numa_far_disp);
+ seq_puts(p, "\n");
+ }
+
+ return 0;
+}
+
+static int vcpudispatch_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, vcpudispatch_stats_display, NULL);
+}
+
+static const struct file_operations vcpudispatch_stats_proc_ops = {
+ .open = vcpudispatch_stats_open,
+ .read = seq_read,
+ .write = vcpudispatch_stats_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t vcpudispatch_stats_freq_write(struct file *file,
+ const char __user *p, size_t count, loff_t *ppos)
+{
+ int rc, freq;
+ char buf[16];
+
+ if (count > 15)
+ return -EINVAL;
+
+ if (copy_from_user(buf, p, count))
+ return -EFAULT;
+
+ buf[count] = 0;
+ rc = kstrtoint(buf, 0, &freq);
+ if (rc || freq < 1 || freq > HZ) {
+ pr_err("vcpudispatch_stats_freq: please specify a frequency between 1 and %d\n",
+ HZ);
+ return rc ? rc : -EINVAL;
+ }
+
+ vcpudispatch_stats_freq = freq;
+
+ return count;
+}
+
+static int vcpudispatch_stats_freq_display(struct seq_file *p, void *v)
+{
+ seq_printf(p, "%d\n", vcpudispatch_stats_freq);
+ return 0;
+}
+
+static int vcpudispatch_stats_freq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, vcpudispatch_stats_freq_display, NULL);
+}
+
+static const struct file_operations vcpudispatch_stats_freq_proc_ops = {
+ .open = vcpudispatch_stats_freq_open,
+ .read = seq_read,
+ .write = vcpudispatch_stats_freq_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init vcpudispatch_stats_procfs_init(void)
+{
+ if (!lppaca_shared_proc(get_lppaca()))
+ return 0;
+
+ if (!proc_create("powerpc/vcpudispatch_stats", 0600, NULL,
+ &vcpudispatch_stats_proc_ops))
+ pr_err("vcpudispatch_stats: error creating procfs file\n");
+ else if (!proc_create("powerpc/vcpudispatch_stats_freq", 0600, NULL,
+ &vcpudispatch_stats_freq_proc_ops))
+ pr_err("vcpudispatch_stats_freq: error creating procfs file\n");
+
+ return 0;
+}
+
+machine_device_initcall(pseries, vcpudispatch_stats_procfs_init);
#endif /* CONFIG_PPC_SPLPAR */
void vpa_init(int cpu)
--
2.22.0
^ permalink raw reply related
* [PATCH v3 8/9] powerpc/pseries: Protect against hogging the cpu while setting up the stats
From: Naveen N. Rao @ 2019-07-03 17:04 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
When enabling or disabling the vcpu dispatch statistics, we do a lot of
work including allocating/deallocating memory across all possible cpus
for the DTL buffer. In order to guard against hogging the cpu for too
long, track the time we're taking and yield the processor if necessary.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/lppaca.h | 2 +-
arch/powerpc/platforms/pseries/lpar.c | 29 ++++++++++++++++++--------
arch/powerpc/platforms/pseries/setup.c | 2 +-
3 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h
index f60257c9766d..3b4b305796ae 100644
--- a/arch/powerpc/include/asm/lppaca.h
+++ b/arch/powerpc/include/asm/lppaca.h
@@ -188,7 +188,7 @@ extern rwlock_t dtl_access_lock;
extern void (*dtl_consumer)(struct dtl_entry *entry, u64 index);
extern void register_dtl_buffer(int cpu);
-extern void alloc_dtl_buffers(void);
+extern void alloc_dtl_buffers(unsigned long *time_limit);
extern long hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity);
#endif /* CONFIG_PPC_BOOK3S */
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 951a3d755190..4bee9b4fc876 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -62,7 +62,7 @@ static u8 dtl_mask = DTL_LOG_PREEMPT;
static u8 dtl_mask;
#endif
-void alloc_dtl_buffers(void)
+void alloc_dtl_buffers(unsigned long *time_limit)
{
int cpu;
struct paca_struct *pp;
@@ -86,6 +86,11 @@ void alloc_dtl_buffers(void)
pp->dispatch_log = dtl;
pp->dispatch_log_end = dtl + N_DISPATCH_LOG;
pp->dtl_curr = dtl;
+
+ if (time_limit && time_after(jiffies, *time_limit)) {
+ cond_resched();
+ *time_limit = jiffies + HZ;
+ }
}
}
@@ -154,7 +159,7 @@ static int vcpudispatch_stats_freq = 50;
static __be32 *vcpu_associativity, *pcpu_associativity;
-static void free_dtl_buffers(void)
+static void free_dtl_buffers(unsigned long *time_limit)
{
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
int cpu;
@@ -169,6 +174,11 @@ static void free_dtl_buffers(void)
pp->dispatch_log = 0;
pp->dispatch_log_end = 0;
pp->dtl_curr = 0;
+
+ if (time_limit && time_after(jiffies, *time_limit)) {
+ cond_resched();
+ *time_limit = jiffies + HZ;
+ }
}
#endif
}
@@ -428,7 +438,7 @@ static void reset_global_dtl_mask(void)
lppaca_of(cpu).dtl_enable_mask = dtl_mask;
}
-static int dtl_worker_enable(void)
+static int dtl_worker_enable(unsigned long *time_limit)
{
int rc = 0, state;
@@ -440,13 +450,13 @@ static int dtl_worker_enable(void)
set_global_dtl_mask(DTL_LOG_ALL);
/* Setup dtl buffers and register those */
- alloc_dtl_buffers();
+ alloc_dtl_buffers(time_limit);
state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powerpc/dtl:online",
dtl_worker_online, dtl_worker_offline);
if (state < 0) {
pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
- free_dtl_buffers();
+ free_dtl_buffers(time_limit);
reset_global_dtl_mask();
write_unlock(&dtl_access_lock);
rc = -EINVAL;
@@ -458,10 +468,10 @@ static int dtl_worker_enable(void)
return rc;
}
-static void dtl_worker_disable(void)
+static void dtl_worker_disable(unsigned long *time_limit)
{
cpuhp_remove_state(dtl_worker_state);
- free_dtl_buffers();
+ free_dtl_buffers(time_limit);
reset_global_dtl_mask();
write_unlock(&dtl_access_lock);
}
@@ -469,6 +479,7 @@ static void dtl_worker_disable(void)
static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
size_t count, loff_t *ppos)
{
+ unsigned long time_limit = jiffies + HZ;
struct vcpu_dispatch_data *disp;
int rc, cmd, cpu;
char buf[16];
@@ -503,13 +514,13 @@ static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
disp->last_disp_cpu = -1;
}
- rc = dtl_worker_enable();
+ rc = dtl_worker_enable(&time_limit);
if (rc) {
destroy_cpu_associativity();
goto out;
}
} else {
- dtl_worker_disable();
+ dtl_worker_disable(&time_limit);
destroy_cpu_associativity();
}
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index e4d75b958593..76fd6de90581 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -279,7 +279,7 @@ static int alloc_dispatch_logs(void)
if (!dtl_cache)
return 0;
- alloc_dtl_buffers();
+ alloc_dtl_buffers(0);
/* Register the DTL for the current (boot) cpu */
register_dtl_buffer(smp_processor_id());
--
2.22.0
^ permalink raw reply related
* [PATCH v3 9/9] powerpc/pseries: Add documentation for vcpudispatch_stats
From: Naveen N. Rao @ 2019-07-03 17:04 UTC (permalink / raw)
To: Michael Ellerman
Cc: Nathan Lynch, Aneesh Kumar K.V, Mingming Cao, linuxppc-dev
In-Reply-To: <cover.1562171646.git.naveen.n.rao@linux.vnet.ibm.com>
Add a document describing the fields provided by
/proc/powerpc/vcpudispatch_stats.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
---
Documentation/powerpc/vcpudispatch_stats.txt | 68 ++++++++++++++++++++
1 file changed, 68 insertions(+)
create mode 100644 Documentation/powerpc/vcpudispatch_stats.txt
diff --git a/Documentation/powerpc/vcpudispatch_stats.txt b/Documentation/powerpc/vcpudispatch_stats.txt
new file mode 100644
index 000000000000..e21476bfd78c
--- /dev/null
+++ b/Documentation/powerpc/vcpudispatch_stats.txt
@@ -0,0 +1,68 @@
+VCPU Dispatch Statistics:
+=========================
+
+For Shared Processor LPARs, the POWER Hypervisor maintains a relatively
+static mapping of the LPAR processors (vcpus) to physical processor
+chips (representing the "home" node) and tries to always dispatch vcpus
+on their associated physical processor chip. However, under certain
+scenarios, vcpus may be dispatched on a different processor chip (away
+from its home node).
+
+/proc/powerpc/vcpudispatch_stats can be used to obtain statistics
+related to the vcpu dispatch behavior. Writing '1' to this file enables
+collecting the statistics, while writing '0' disables the statistics.
+By default, the DTLB log for each vcpu is processed 50 times a second so
+as not to miss any entries. This processing frequency can be changed
+through /proc/powerpc/vcpudispatch_stats_freq.
+
+The statistics themselves are available by reading the procfs file
+/proc/powerpc/vcpudispatch_stats. Each line in the output corresponds to
+a vcpu as represented by the first field, followed by 8 numbers.
+
+The first number corresponds to:
+1. total vcpu dispatches since the beginning of statistics collection
+
+The next 4 numbers represent vcpu dispatch dispersions:
+2. number of times this vcpu was dispatched on the same processor as last
+ time
+3. number of times this vcpu was dispatched on a different processor core
+ as last time, but within the same chip
+4. number of times this vcpu was dispatched on a different chip
+5. number of times this vcpu was dispatches on a different socket/drawer
+(next numa boundary)
+
+The final 3 numbers represent statistics in relation to the home node of
+the vcpu:
+6. number of times this vcpu was dispatched in its home node (chip)
+7. number of times this vcpu was dispatched in a different node
+8. number of times this vcpu was dispatched in a node further away (numa
+distance)
+
+An example output:
+ $ sudo cat /proc/powerpc/vcpudispatch_stats
+ cpu0 6839 4126 2683 30 0 6821 18 0
+ cpu1 2515 1274 1229 12 0 2509 6 0
+ cpu2 2317 1198 1109 10 0 2312 5 0
+ cpu3 2259 1165 1088 6 0 2256 3 0
+ cpu4 2205 1143 1056 6 0 2202 3 0
+ cpu5 2165 1121 1038 6 0 2162 3 0
+ cpu6 2183 1127 1050 6 0 2180 3 0
+ cpu7 2193 1133 1052 8 0 2187 6 0
+ cpu8 2165 1115 1032 18 0 2156 9 0
+ cpu9 2301 1252 1033 16 0 2293 8 0
+ cpu10 2197 1138 1041 18 0 2187 10 0
+ cpu11 2273 1185 1062 26 0 2260 13 0
+ cpu12 2186 1125 1043 18 0 2177 9 0
+ cpu13 2161 1115 1030 16 0 2153 8 0
+ cpu14 2206 1153 1033 20 0 2196 10 0
+ cpu15 2163 1115 1032 16 0 2155 8 0
+
+In the output above, for vcpu0, there have been 6839 dispatches since
+statistics were enabled. 4126 of those dispatches were on the same
+physical cpu as the last time. 2683 were on a different core, but within
+the same chip, while 30 dispatches were on a different chip compared to
+its last dispatch.
+
+Also, out of the total of 6839 dispatches, we see that there have been
+6821 dispatches on the vcpu's home node, while 18 dispatches were
+outside its home node, on a neighbouring chip.
--
2.22.0
^ permalink raw reply related
* Re: [v2 09/12] powerpc/mce: Enable MCE notifiers in external modules
From: Reza Arbab @ 2019-07-03 17:20 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Santosh Sivaraj, Aneesh Kumar K.V, Mahesh Salgaonkar,
Chandan Rajendra, linuxppc-dev
In-Reply-To: <1562047959.5y756f60wn.astroid@bobo.none>
On Tue, Jul 02, 2019 at 04:17:11PM +1000, Nicholas Piggin wrote:
>Santosh Sivaraj's on July 2, 2019 3:19 pm:
>> --- a/arch/powerpc/kernel/exceptions-64s.S
>> +++ b/arch/powerpc/kernel/exceptions-64s.S
>> @@ -458,6 +458,12 @@ EXC_COMMON_BEGIN(machine_check_handle_early)
>> bl machine_check_early
>> std r3,RESULT(r1) /* Save result */
>>
>> + /* Notifiers may be in a module, so enable virtual addressing. */
>> + mfmsr r11
>> + ori r11,r11,MSR_IR
>> + ori r11,r11,MSR_DR
>> + mtmsr r11
>
>Can't do this, we could take a machine check somewhere the MMU is
>not sane (in fact the guest early mce handling that was added recently
>should not be enabling virtual mode either, which needs to be fixed).
Rats. So in machine_check_handle_early() there are two options; either
1. The mc is unhandled/unrecoverable. Stay in real mode, proceed to
unrecover_mce(), the fatal path of no return (panic, reboot, etc).
2. The mc is handled/recovered. Return from MCE where any further action
can be done by processing the machine check event workqueue. Am I
understanding you correctly that this is the absolute earliest we can
get back to virtual mode?
Since the notifier chain is actually part of the decision between (1)
and (2), it's a hard limitation then that callbacks be in real address
space. Is there any way to structure things so that's not the case?
Luckily this patch isn't really necessary for memcpy_mcsafe(), but we
have a couple of other potential users of the notifier from external
modules (so their callbacks would require virtual mode).
--
Reza Arbab
^ permalink raw reply
* [PATCH v2 3/7] soc/fsl/qbman: Cleanup QMan queues if device was already initialized
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
If the QMan device was previously initialized make sure all the
frame queues are out of service once all the portals are probed.
This handles the case where the kernel is restarted without the
SoC being reset (kexec for example)
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/qman.c | 4 ++--
drivers/soc/fsl/qbman/qman_ccsr.c | 13 ++++++++++++-
drivers/soc/fsl/qbman/qman_portal.c | 18 +++++++++++++++++-
drivers/soc/fsl/qbman/qman_priv.h | 7 +++++++
4 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index 636f83f..f10f77d 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -2581,7 +2581,7 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
#define qm_dqrr_drain_nomatch(p) \
_qm_dqrr_consume_and_match(p, 0, 0, false)
-static int qman_shutdown_fq(u32 fqid)
+int qman_shutdown_fq(u32 fqid)
{
struct qman_portal *p;
struct device *dev;
@@ -2754,7 +2754,7 @@ static int qman_shutdown_fq(u32 fqid)
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
QM_MCR_VERB_ALTER_OOS);
- if (mcr->result) {
+ if (mcr->result != QM_MCR_RESULT_OK) {
dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
fqid, mcr->result);
ret = -EIO;
diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c
index 39f6fc1..fcf77e0 100644
--- a/drivers/soc/fsl/qbman/qman_ccsr.c
+++ b/drivers/soc/fsl/qbman/qman_ccsr.c
@@ -483,7 +483,7 @@ RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
#endif
-static unsigned int qm_get_fqid_maxcnt(void)
+unsigned int qm_get_fqid_maxcnt(void)
{
return fqd_sz / 64;
}
@@ -728,6 +728,17 @@ int qman_is_probed(void)
}
EXPORT_SYMBOL_GPL(qman_is_probed);
+int qman_requires_cleanup(void)
+{
+ return __qman_requires_cleanup;
+}
+
+void qman_done_cleanup(void)
+{
+ __qman_requires_cleanup = 0;
+}
+
+
static int fsl_qman_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c
index 75717bc..153727c 100644
--- a/drivers/soc/fsl/qbman/qman_portal.c
+++ b/drivers/soc/fsl/qbman/qman_portal.c
@@ -233,7 +233,7 @@ static int qman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct qm_portal_config *pcfg;
struct resource *addr_phys[2];
- int irq, cpu, err;
+ int irq, cpu, err, i;
u32 val;
err = qman_is_probed();
@@ -328,6 +328,22 @@ static int qman_portal_probe(struct platform_device *pdev)
if (!cpu_online(cpu))
qman_offline_cpu(cpu);
+ if (__qman_portals_probed == 1 && qman_requires_cleanup()) {
+ /*
+ * QMan wasn't reset prior to boot (Kexec for example)
+ * Empty all the frame queues so they are in reset state
+ */
+ for (i = 0; i < qm_get_fqid_maxcnt(); i++) {
+ err = qman_shutdown_fq(i);
+ if (err) {
+ dev_err(dev, "Failed to shutdown frame queue %d\n",
+ i);
+ goto err_portal_init;
+ }
+ }
+ qman_done_cleanup();
+ }
+
return 0;
err_portal_init:
diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h
index 0451571..a8a35fe 100644
--- a/drivers/soc/fsl/qbman/qman_priv.h
+++ b/drivers/soc/fsl/qbman/qman_priv.h
@@ -272,3 +272,10 @@ extern struct qman_portal *affine_portals[NR_CPUS];
extern struct qman_portal *qman_dma_portal;
const struct qm_portal_config *qman_get_qm_portal_config(
struct qman_portal *portal);
+
+unsigned int qm_get_fqid_maxcnt(void);
+
+int qman_shutdown_fq(u32 fqid);
+
+int qman_requires_cleanup(void);
+void qman_done_cleanup(void);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 0/7] soc/fsl/qbman: Enable Kexec for DPAA1 devices
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
Most DPAA1 devices do not support a soft reset which is an issue if
Kexec starts a new kernel. This patch series allows Kexec to function
by detecting that the QBMan device was previously initialized.
The patches fix some issues with device cleanup as well as ensuring
that the location of the QBMan private memories has not changed
after the execution of the Kexec.
Changes since v1:
- Removed a bug fix and sent it separately to ease backporting
Roy Pledge (7):
soc/fsl/qbman: Rework QBMan private memory setup
soc/fsl/qbman: Cleanup buffer pools if BMan was initialized prior to
bootup
soc/fsl/qbman: Cleanup QMan queues if device was already initialized
soc/fsl/qbman: Fix drain_mr_fqni()
soc/fsl/qbman: Disable interrupts during portal recovery
soc/fsl/qbman: Fixup qman_shutdown_fq()
soc/fsl/qbman: Update device tree with reserved memory
drivers/soc/fsl/qbman/bman.c | 17 ++++----
drivers/soc/fsl/qbman/bman_ccsr.c | 36 +++++++++++++++-
drivers/soc/fsl/qbman/bman_portal.c | 18 +++++++-
drivers/soc/fsl/qbman/bman_priv.h | 5 +++
drivers/soc/fsl/qbman/dpaa_sys.c | 63 ++++++++++++++++------------
drivers/soc/fsl/qbman/qman.c | 83 +++++++++++++++++++++++++++++--------
drivers/soc/fsl/qbman/qman_ccsr.c | 59 +++++++++++++++++++++++---
drivers/soc/fsl/qbman/qman_portal.c | 18 +++++++-
drivers/soc/fsl/qbman/qman_priv.h | 8 ++++
9 files changed, 246 insertions(+), 61 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH v2 1/7] soc/fsl/qbman: Rework QBMan private memory setup
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
Rework QBMan private memory setup so that the areas are not
zeroed if the device was previously initialized
If the QMan private memory was already initialized skip the PFDR
initialization.
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/bman_ccsr.c | 26 ++++++++++++++++++++--
drivers/soc/fsl/qbman/dpaa_sys.c | 7 +++---
drivers/soc/fsl/qbman/qman_ccsr.c | 45 ++++++++++++++++++++++++++++++++++-----
3 files changed, 67 insertions(+), 11 deletions(-)
diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c
index 7c3cc96..dc6d7e5 100644
--- a/drivers/soc/fsl/qbman/bman_ccsr.c
+++ b/drivers/soc/fsl/qbman/bman_ccsr.c
@@ -97,17 +97,40 @@ static void bm_get_version(u16 *id, u8 *major, u8 *minor)
/* signal transactions for FBPRs with higher priority */
#define FBPR_AR_RPRIO_HI BIT(30)
-static void bm_set_memory(u64 ba, u32 size)
+/* Track if probe has occurred and if cleanup is required */
+static int __bman_probed;
+static int __bman_requires_cleanup;
+
+
+static int bm_set_memory(u64 ba, u32 size)
{
+ u32 bar, bare;
u32 exp = ilog2(size);
/* choke if size isn't within range */
DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 &&
is_power_of_2(size));
/* choke if '[e]ba' has lower-alignment than 'size' */
DPAA_ASSERT(!(ba & (size - 1)));
+
+ /* Check to see if BMan has already been initialized */
+ bar = bm_ccsr_in(REG_FBPR_BAR);
+ if (bar) {
+ /* Maker sure ba == what was programmed) */
+ bare = bm_ccsr_in(REG_FBPR_BARE);
+ if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
+ pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
+ ba, bare, bar);
+ return -ENOMEM;
+ }
+ pr_info("BMan BAR already configured\n");
+ __bman_requires_cleanup = 1;
+ return 1;
+ }
+
bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba));
bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba));
bm_ccsr_out(REG_FBPR_AR, exp - 1);
+ return 0;
}
/*
@@ -120,7 +143,6 @@ static void bm_set_memory(u64 ba, u32 size)
*/
static dma_addr_t fbpr_a;
static size_t fbpr_sz;
-static int __bman_probed;
static int bman_fbpr(struct reserved_mem *rmem)
{
diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c
index e6d48dc..3e0a7f3 100644
--- a/drivers/soc/fsl/qbman/dpaa_sys.c
+++ b/drivers/soc/fsl/qbman/dpaa_sys.c
@@ -40,6 +40,7 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
int ret;
struct device_node *mem_node;
u64 size64;
+ struct reserved_mem *rmem;
ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx);
if (ret) {
@@ -62,10 +63,8 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
return -ENODEV;
}
- if (!dma_alloc_coherent(dev, *size, addr, 0)) {
- dev_err(dev, "DMA Alloc memory failed\n");
- return -ENODEV;
- }
+ rmem = of_reserved_mem_lookup(mem_node);
+ *addr = rmem->base;
/*
* Disassociate the reserved memory area from the device
diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c
index a6bb430..39f6fc1 100644
--- a/drivers/soc/fsl/qbman/qman_ccsr.c
+++ b/drivers/soc/fsl/qbman/qman_ccsr.c
@@ -274,6 +274,7 @@ static u32 __iomem *qm_ccsr_start;
/* A SDQCR mask comprising all the available/visible pool channels */
static u32 qm_pools_sdqcr;
static int __qman_probed;
+static int __qman_requires_cleanup;
static inline u32 qm_ccsr_in(u32 offset)
{
@@ -340,19 +341,46 @@ static void qm_get_version(u16 *id, u8 *major, u8 *minor)
}
#define PFDR_AR_EN BIT(31)
-static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
+static int qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
{
+ void *ptr;
u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
u32 exp = ilog2(size);
+ u32 bar, bare;
/* choke if size isn't within range */
DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
is_power_of_2(size));
/* choke if 'ba' has lower-alignment than 'size' */
DPAA_ASSERT(!(ba & (size - 1)));
+
+ /* Check to see if QMan has already been initialized */
+ bar = qm_ccsr_in(offset + REG_offset_BAR);
+ if (bar) {
+ /* Maker sure ba == what was programmed) */
+ bare = qm_ccsr_in(offset);
+ if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
+ pr_err("Attempted to reinitialize QMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
+ ba, bare, bar);
+ return -ENOMEM;
+ }
+ __qman_requires_cleanup = 1;
+ /* Return 1 to indicate memory was previously programmed */
+ return 1;
+ }
+ /* Need to temporarily map the area to make sure it is zeroed */
+ ptr = memremap(ba, size, MEMREMAP_WB);
+ if (!ptr) {
+ pr_crit("memremap() of QMan private memory failed\n");
+ return -ENOMEM;
+ }
+ memset(ptr, 0, size);
+ memunmap(ptr);
+
qm_ccsr_out(offset, upper_32_bits(ba));
qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
+ return 0;
}
static void qm_set_pfdr_threshold(u32 th, u8 k)
@@ -571,12 +599,19 @@ static int qman_init_ccsr(struct device *dev)
int i, err;
/* FQD memory */
- qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
+ err = qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
+ if (err < 0)
+ return err;
/* PFDR memory */
- qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
- err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
- if (err)
+ err = qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
+ if (err < 0)
return err;
+ /* Only initialize PFDRs if the QMan was not initialized before */
+ if (err == 0) {
+ err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
+ if (err)
+ return err;
+ }
/* thresholds */
qm_set_pfdr_threshold(512, 64);
qm_set_sfdr_threshold(128);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/7] soc/fsl/qbman: Cleanup buffer pools if BMan was initialized prior to bootup
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
Clean the BMan buffer pools if the device had been initialized
previously. This will ensure a consistent state if the kernel
was soft restarted (kexec for example)
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/bman.c | 17 +++++++++--------
drivers/soc/fsl/qbman/bman_ccsr.c | 10 ++++++++++
drivers/soc/fsl/qbman/bman_portal.c | 18 +++++++++++++++++-
drivers/soc/fsl/qbman/bman_priv.h | 5 +++++
4 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c
index f84ab59..f4fb527 100644
--- a/drivers/soc/fsl/qbman/bman.c
+++ b/drivers/soc/fsl/qbman/bman.c
@@ -635,30 +635,31 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
return 0;
}
-static int bm_shutdown_pool(u32 bpid)
+int bm_shutdown_pool(u32 bpid)
{
+ int err = 0;
struct bm_mc_command *bm_cmd;
union bm_mc_result *bm_res;
+
+ struct bman_portal *p = get_affine_portal();
while (1) {
- struct bman_portal *p = get_affine_portal();
/* Acquire buffers until empty */
bm_cmd = bm_mc_start(&p->p);
bm_cmd->bpid = bpid;
bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1);
if (!bm_mc_result_timeout(&p->p, &bm_res)) {
- put_affine_portal();
pr_crit("BMan Acquire Command timedout\n");
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
+ goto done;
}
if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
- put_affine_portal();
/* Pool is empty */
- return 0;
+ goto done;
}
- put_affine_portal();
}
-
+done:
+ put_affine_portal();
return 0;
}
diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c
index dc6d7e5..cb24a08 100644
--- a/drivers/soc/fsl/qbman/bman_ccsr.c
+++ b/drivers/soc/fsl/qbman/bman_ccsr.c
@@ -195,6 +195,16 @@ int bman_is_probed(void)
}
EXPORT_SYMBOL_GPL(bman_is_probed);
+int bman_requires_cleanup(void)
+{
+ return __bman_requires_cleanup;
+}
+
+void bman_done_cleanup(void)
+{
+ __bman_requires_cleanup = 0;
+}
+
static int fsl_bman_probe(struct platform_device *pdev)
{
int ret, err_irq;
diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c
index c78cc69..cc06d95 100644
--- a/drivers/soc/fsl/qbman/bman_portal.c
+++ b/drivers/soc/fsl/qbman/bman_portal.c
@@ -100,7 +100,7 @@ static int bman_portal_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct bm_portal_config *pcfg;
struct resource *addr_phys[2];
- int irq, cpu, err;
+ int irq, cpu, err, i;
err = bman_is_probed();
if (!err)
@@ -181,6 +181,22 @@ static int bman_portal_probe(struct platform_device *pdev)
if (!cpu_online(cpu))
bman_offline_cpu(cpu);
+ if (__bman_portals_probed == 1 && bman_requires_cleanup()) {
+ /*
+ * BMan wasn't reset prior to boot (Kexec for example)
+ * Empty all the buffer pools so they are in reset state
+ */
+ for (i = 0; i < BM_POOL_MAX; i++) {
+ err = bm_shutdown_pool(i);
+ if (err) {
+ dev_err(dev, "Failed to shutdown bpool %d\n",
+ i);
+ goto err_portal_init;
+ }
+ }
+ bman_done_cleanup();
+ }
+
return 0;
err_portal_init:
diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h
index 751ce90..aa3981e 100644
--- a/drivers/soc/fsl/qbman/bman_priv.h
+++ b/drivers/soc/fsl/qbman/bman_priv.h
@@ -76,3 +76,8 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits);
const struct bm_portal_config *
bman_get_bm_portal_config(const struct bman_portal *portal);
+
+int bman_requires_cleanup(void);
+void bman_done_cleanup(void);
+
+int bm_shutdown_pool(u32 bpid);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 4/7] soc/fsl/qbman: Fix drain_mr_fqni()
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
The drain_mr_fqni() function may be called fron uninterruptable
context so convert the msleep() to an mdelay(). Also ensure that
the valid bit is updated while polling.
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/qman.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index f10f77d..2989504 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -1164,6 +1164,7 @@ static int drain_mr_fqrni(struct qm_portal *p)
{
const union qm_mr_entry *msg;
loop:
+ qm_mr_pvb_update(p);
msg = qm_mr_current(p);
if (!msg) {
/*
@@ -1180,7 +1181,8 @@ static int drain_mr_fqrni(struct qm_portal *p)
* entries well before the ring has been fully consumed, so
* we're being *really* paranoid here.
*/
- msleep(1);
+ mdelay(1);
+ qm_mr_pvb_update(p);
msg = qm_mr_current(p);
if (!msg)
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH v2 5/7] soc/fsl/qbman: Disable interrupts during portal recovery
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
Disable the QBMan interrupts during recovery.
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/qman.c | 22 +++++++++++++++++++---
drivers/soc/fsl/qbman/qman_ccsr.c | 1 +
drivers/soc/fsl/qbman/qman_priv.h | 1 +
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index 2989504..4a99ce5 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -1070,6 +1070,20 @@ int qman_wq_alloc(void)
return 0;
}
+
+void qman_enable_irqs(void)
+{
+ int i;
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (affine_portals[i]) {
+ qm_out(&affine_portals[i]->p, QM_REG_ISR, 0xffffffff);
+ qm_out(&affine_portals[i]->p, QM_REG_IIR, 0);
+ }
+
+ }
+}
+
/*
* This is what everything can wait on, even if it migrates to a different cpu
* to the one whose affine portal it is waiting on.
@@ -1269,8 +1283,8 @@ static int qman_create_portal(struct qman_portal *portal,
qm_out(p, QM_REG_ISDR, isdr);
portal->irq_sources = 0;
qm_out(p, QM_REG_IER, 0);
- qm_out(p, QM_REG_ISR, 0xffffffff);
snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
+ qm_out(p, QM_REG_IIR, 1);
if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
dev_err(c->dev, "request_irq() failed\n");
goto fail_irq;
@@ -1290,7 +1304,7 @@ static int qman_create_portal(struct qman_portal *portal,
isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI);
qm_out(p, QM_REG_ISDR, isdr);
if (qm_dqrr_current(p)) {
- dev_err(c->dev, "DQRR unclean\n");
+ dev_dbg(c->dev, "DQRR unclean\n");
qm_dqrr_cdc_consume_n(p, 0xffff);
}
if (qm_mr_current(p) && drain_mr_fqrni(p)) {
@@ -1303,8 +1317,10 @@ static int qman_create_portal(struct qman_portal *portal,
}
/* Success */
portal->config = c;
+ qm_out(p, QM_REG_ISR, 0xffffffff);
qm_out(p, QM_REG_ISDR, 0);
- qm_out(p, QM_REG_IIR, 0);
+ if (!qman_requires_cleanup())
+ qm_out(p, QM_REG_IIR, 0);
/* Write a sane SDQCR */
qm_dqrr_sdqcr_set(p, portal->sdqcr);
return 0;
diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c
index fcf77e0..8d17643 100644
--- a/drivers/soc/fsl/qbman/qman_ccsr.c
+++ b/drivers/soc/fsl/qbman/qman_ccsr.c
@@ -735,6 +735,7 @@ int qman_requires_cleanup(void)
void qman_done_cleanup(void)
{
+ qman_enable_irqs();
__qman_requires_cleanup = 0;
}
diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h
index a8a35fe..fd1cf54 100644
--- a/drivers/soc/fsl/qbman/qman_priv.h
+++ b/drivers/soc/fsl/qbman/qman_priv.h
@@ -279,3 +279,4 @@ int qman_shutdown_fq(u32 fqid);
int qman_requires_cleanup(void);
void qman_done_cleanup(void);
+void qman_enable_irqs(void);
--
2.7.4
^ permalink raw reply related
* [PATCH v2 6/7] soc/fsl/qbman: Fixup qman_shutdown_fq()
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
When shutting down a FQ on a dedicated channel only the
SW portal associated with that channel can dequeue from it.
Make sure the correct portal is use.
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/qman.c | 53 +++++++++++++++++++++++++++++++++++---------
1 file changed, 42 insertions(+), 11 deletions(-)
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
index 4a99ce5..bf68d86 100644
--- a/drivers/soc/fsl/qbman/qman.c
+++ b/drivers/soc/fsl/qbman/qman.c
@@ -1018,6 +1018,20 @@ static inline void put_affine_portal(void)
put_cpu_var(qman_affine_portal);
}
+
+static inline struct qman_portal *get_portal_for_channel(u16 channel)
+{
+ int i;
+
+ for (i = 0; i < num_possible_cpus(); i++) {
+ if (affine_portals[i] &&
+ affine_portals[i]->config->channel == channel)
+ return affine_portals[i];
+ }
+
+ return NULL;
+}
+
static struct workqueue_struct *qm_portal_wq;
int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
@@ -2601,7 +2615,7 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
int qman_shutdown_fq(u32 fqid)
{
- struct qman_portal *p;
+ struct qman_portal *p, *channel_portal;
struct device *dev;
union qm_mc_command *mcc;
union qm_mc_result *mcr;
@@ -2641,17 +2655,28 @@ int qman_shutdown_fq(u32 fqid)
channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
wq = qm_fqd_get_wq(&mcr->queryfq.fqd);
+ if (channel < qm_channel_pool1) {
+ channel_portal = get_portal_for_channel(channel);
+ if (channel_portal == NULL) {
+ dev_err(dev, "Can't find portal for dedicated channel 0x%x\n",
+ channel);
+ ret = -EIO;
+ goto out;
+ }
+ } else
+ channel_portal = p;
+
switch (state) {
case QM_MCR_NP_STATE_TEN_SCHED:
case QM_MCR_NP_STATE_TRU_SCHED:
case QM_MCR_NP_STATE_ACTIVE:
case QM_MCR_NP_STATE_PARKED:
orl_empty = 0;
- mcc = qm_mc_start(&p->p);
+ mcc = qm_mc_start(&channel_portal->p);
qm_fqid_set(&mcc->fq, fqid);
- qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
- if (!qm_mc_result_timeout(&p->p, &mcr)) {
- dev_err(dev, "QUERYFQ_NP timeout\n");
+ qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE);
+ if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) {
+ dev_err(dev, "ALTER_RETIRE timeout\n");
ret = -ETIMEDOUT;
goto out;
}
@@ -2659,6 +2684,9 @@ int qman_shutdown_fq(u32 fqid)
QM_MCR_VERB_ALTER_RETIRE);
res = mcr->result; /* Make a copy as we reuse MCR below */
+ if (res == QM_MCR_RESULT_OK)
+ drain_mr_fqrni(&channel_portal->p);
+
if (res == QM_MCR_RESULT_PENDING) {
/*
* Need to wait for the FQRN in the message ring, which
@@ -2688,21 +2716,25 @@ int qman_shutdown_fq(u32 fqid)
}
/* Set the sdqcr to drain this channel */
if (channel < qm_channel_pool1)
- qm_dqrr_sdqcr_set(&p->p,
+ qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_DEDICATED);
else
- qm_dqrr_sdqcr_set(&p->p,
+ qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_POOL_CONV
(channel));
do {
/* Keep draining DQRR while checking the MR*/
- qm_dqrr_drain_nomatch(&p->p);
+ qm_dqrr_drain_nomatch(&channel_portal->p);
/* Process message ring too */
- found_fqrn = qm_mr_drain(&p->p, FQRN);
+ found_fqrn = qm_mr_drain(&channel_portal->p,
+ FQRN);
cpu_relax();
} while (!found_fqrn);
+ /* Restore SDQCR */
+ qm_dqrr_sdqcr_set(&channel_portal->p,
+ channel_portal->sdqcr);
}
if (res != QM_MCR_RESULT_OK &&
@@ -2733,9 +2765,8 @@ int qman_shutdown_fq(u32 fqid)
* Wait for a dequeue and process the dequeues,
* making sure to empty the ring completely
*/
- } while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
+ } while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
}
- qm_dqrr_sdqcr_set(&p->p, 0);
while (!orl_empty) {
/* Wait for the ORL to have been completely drained */
--
2.7.4
^ permalink raw reply related
* [PATCH v2 7/7] soc/fsl/qbman: Update device tree with reserved memory
From: Roy Pledge @ 2019-07-03 20:59 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Leo Li
Cc: Roy Pledge
In-Reply-To: <1562187548-32261-1-git-send-email-roy.pledge@nxp.com>
When using the reserved memory node in the device tree there are
two options - dynamic or static. If a dynamic allocation was
selected (where the kernel selects the address for the allocation)
convert it to a static allocation by inserting the reg property.
This will ensure the same memory is reused after a kexec()
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
---
drivers/soc/fsl/qbman/dpaa_sys.c | 60 ++++++++++++++++++++++++----------------
1 file changed, 36 insertions(+), 24 deletions(-)
diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c
index 3e0a7f3..9dd8bb5 100644
--- a/drivers/soc/fsl/qbman/dpaa_sys.c
+++ b/drivers/soc/fsl/qbman/dpaa_sys.c
@@ -37,41 +37,53 @@
int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
size_t *size)
{
- int ret;
struct device_node *mem_node;
- u64 size64;
struct reserved_mem *rmem;
+ struct property *prop;
+ int len, err;
+ __be32 *res_array;
- ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx);
- if (ret) {
- dev_err(dev,
- "of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n",
- idx, ret);
- return -ENODEV;
- }
- mem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (mem_node) {
- ret = of_property_read_u64(mem_node, "size", &size64);
- if (ret) {
- dev_err(dev, "of_address_to_resource fails 0x%x\n",
- ret);
- return -ENODEV;
- }
- *size = size64;
- } else {
+ mem_node = of_parse_phandle(dev->of_node, "memory-region", idx);
+ if (!mem_node) {
dev_err(dev, "No memory-region found for index %d\n", idx);
return -ENODEV;
}
rmem = of_reserved_mem_lookup(mem_node);
+ if (!rmem) {
+ dev_err(dev, "of_reserved_mem_lookup() returned NULL\n");
+ return -ENODEV;
+ }
*addr = rmem->base;
+ *size = rmem->size;
/*
- * Disassociate the reserved memory area from the device
- * because a device can only have one DMA memory area. This
- * should be fine since the memory is allocated and initialized
- * and only ever accessed by the QBMan device from now on
+ * Check if the reg property exists - if not insert the node
+ * so upon kexec() the same memory region address will be preserved.
+ * This is needed because QBMan HW does not allow the base address/
+ * size to be modified once set.
*/
- of_reserved_mem_device_release(dev);
+ prop = of_find_property(mem_node, "reg", &len);
+ if (!prop) {
+ prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+ prop->value = res_array = devm_kzalloc(dev, sizeof(__be32) * 4,
+ GFP_KERNEL);
+ if (!prop->value)
+ return -ENOMEM;
+ res_array[0] = cpu_to_be32(upper_32_bits(*addr));
+ res_array[1] = cpu_to_be32(lower_32_bits(*addr));
+ res_array[2] = cpu_to_be32(upper_32_bits(*size));
+ res_array[3] = cpu_to_be32(lower_32_bits(*size));
+ prop->length = sizeof(__be32) * 4;
+ prop->name = devm_kstrdup(dev, "reg", GFP_KERNEL);
+ if (!prop->name)
+ return -ENOMEM;
+ err = of_add_property(mem_node, prop);
+ if (err)
+ return err;
+ }
+
return 0;
}
--
2.7.4
^ permalink raw reply related
* Re: Kernel Bug/oops during boot (PowerVM LPAR w/vTPM)
From: Michal Suchánek @ 2019-07-03 21:15 UTC (permalink / raw)
To: Sachin Sant; +Cc: nayna, linuxppc-dev, gcwilson
In-Reply-To: <20190703131716.7e16d840@naga>
On Wed, 3 Jul 2019 13:17:16 +0200
Michal Suchánek <msuchanek@suse.de> wrote:
> On Wed, 3 Jul 2019 15:54:59 +0530
> Sachin Sant <sachinp@linux.vnet.ibm.com> wrote:
>
> > Booting mainline kernel on PowerVM LPAR with vTPM enabled results
> > into a kernel crash.
> >
> > [ 0.365989] BUG: Kernel NULL pointer dereference at 0x00000012
...
> > [ 0.366085] NIP [c00000000073dd80] tpm1_pcr_extend+0x130/0x230
> > [ 0.366090] LR [c00000000073dcd0] tpm1_pcr_extend+0x80/0x230
...
>
> You need to revert (or fix up) commit 0b6cf6b97b7e ("tpm: pass an array
> of tpm_extend_digest structures to tpm_pcr_extend()". At least
> reverting it fixes the issue for me.
FTR this is the revert on lkml https://lkml.org/lkml/2019/7/1/423
>
> Thanks
>
> Michal
>
^ permalink raw reply
* Re: [PATCH v3 01/16] powerpc/fadump: move internal fadump code to a new file
From: Hari Bathini @ 2019-07-03 17:36 UTC (permalink / raw)
To: Oliver O'Halloran, linuxppc-dev
Cc: Ananth N Mavinakayanahalli, Mahesh J Salgaonkar, Vasant Hegde,
Nicholas Piggin, Stewart Smith, Daniel Axtens
In-Reply-To: <5f4b28e4df2049506a9fa03eead6cfbad9d7cdf3.camel@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 613 bytes --]
On 03/07/19 9:00 AM, Oliver O'Halloran wrote:
> On Wed, 2019-06-26 at 02:15 +0530, Hari Bathini wrote:
>> Refactoring fadump code means internal fadump code is referenced from
>> different places. For ease, move internal code to a new file.
> Can you elaborate a bit? I don't really get what the difference between
> fadump and fadump-internal code is supposed to be. Why can't all this
> just live in fadump.c?
I am trying to keep the code referenced by both platforms (platform/pseries &
platform/powernv) in an internal file while using include/asm/fadump.h for
the interface with other kernel components..
[-- Attachment #2: Type: text/html, Size: 1168 bytes --]
^ permalink raw reply
* Re: [PATCH v3 03/16] pseries/fadump: move out platform specific support from generic code
From: Hari Bathini @ 2019-07-03 17:48 UTC (permalink / raw)
To: Oliver O'Halloran, linuxppc-dev
Cc: Ananth N Mavinakayanahalli, Mahesh J Salgaonkar, Vasant Hegde,
Nicholas Piggin, Stewart Smith, Daniel Axtens
In-Reply-To: <8a779e216bb088b33b022cd026bdb647e05aa338.camel@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2400 bytes --]
On 03/07/19 9:34 AM, Oliver O'Halloran wrote:
> On Wed, 2019-06-26 at 02:16 +0530, Hari Bathini wrote:
>> Introduce callbacks for platform specific operations like register,
>> unregister, invalidate & such, and move pseries specific code into
>> platform code.
> Please don't move around large blocks of code *and* change the code in
> a single patch. It makes reviewing the changes extremely tedious since
> the changes are mixed in with hundreds of lines of nothing.
Right, Oliver.
I am already working on splitting up few other patches for ease of reviewing.
Thought of keeping this patch this way though as it doesn't add any new logic
but moves the code around. Will ensure I split this one up too for the sake
of sanity.
>> Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
>> ---
>> arch/powerpc/include/asm/fadump.h | 75 ----
>> arch/powerpc/kernel/fadump-common.h | 38 ++
>> arch/powerpc/kernel/fadump.c | 500 ++-----------------------
>> arch/powerpc/platforms/pseries/Makefile | 1
>> arch/powerpc/platforms/pseries/rtas-fadump.c | 529 ++++++++++++++++++++++++++
>> arch/powerpc/platforms/pseries/rtas-fadump.h | 96 +++++
>> 6 files changed, 700 insertions(+), 539 deletions(-)
>> create mode 100644 arch/powerpc/platforms/pseries/rtas-fadump.c
>> create mode 100644 arch/powerpc/platforms/pseries/rtas-fadump.h
>>
>> +static struct fadump_ops pseries_fadump_ops = {
>> + .init_fadump_mem_struct = pseries_init_fadump_mem_struct,
>> + .register_fadump = pseries_register_fadump,
> I realise you are just translating the existing interface, but why is
> init_fadump_mem_struct() done as a seperate step and not as a part of
> the registration function? The struct doesn't seem to be necessary
> until the actual registration happens.
Yeah. But registration is a user choice and can happen multiple times within
a single boot (for example, due to hotplug operations) but the structure
contents remain the same. So, it is initialized once early on...
>
>> + .unregister_fadump = pseries_unregister_fadump,
>> + .invalidate_fadump = pseries_invalidate_fadump,
>> + .process_fadump = pseries_process_fadump,
>> + .fadump_region_show = pseries_fadump_region_show,
>> + .crash_fadump = pseries_crash_fadump,
> Rename this to fadump_trigger or something, it's not clear what it
> does.
Sure.
Thanks for the review!
- Hari
[-- Attachment #2: Type: text/html, Size: 3962 bytes --]
^ permalink raw reply
* Re: [PATCH V2 2/2] ASoC: fsl_esai: recover the channel swap after xrun
From: Cezary Rojewski @ 2019-07-03 20:56 UTC (permalink / raw)
To: shengjiu.wang
Cc: alsa-devel, timur, Xiubo.Lee, linuxppc-dev, linux-kernel,
nicoleotsuka, broonie, festevam
In-Reply-To: <c29639336b6b32fa781bdddad30287f8b76d5e0b.1562136119.git.shengjiu.wang@nxp.com>
On 2019-07-03 08:42, shengjiu.wang@nxp.com wrote:
> +static void fsl_esai_reset(unsigned long arg)
> +{
> + struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
> + u32 saisr, tfcr, rfcr;
> +
> + /* save the registers */
> + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
> + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
> +
> + /* stop the tx & rx */
> + fsl_esai_trigger_stop(esai_priv, 1);
> + fsl_esai_trigger_stop(esai_priv, 0);
> +
> + /* reset the esai, and restore the registers */
> + fsl_esai_init(esai_priv);
<comment below applies>
> +
> + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
> + ESAI_xCR_xPR_MASK,
> + ESAI_xCR_xPR);
> + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
> + ESAI_xCR_xPR_MASK,
> + ESAI_xCR_xPR);
> +
> + /* restore registers by regcache_sync */
> + fsl_esai_register_restore(esai_priv);
> +
Both _init and _restore may fail given their declaration in 1/2 "ASoC:
fsl_esai: Wrap some operations to be functions" yet here you simply
ignore the return values.
If failure of said functions is permissive, it might be a good place for
a comment.
Czarek
^ permalink raw reply
* [PATCH] KVM: PPC: Book3S HV: XIVE: Fix typo in comment
From: Greg Kurz @ 2019-07-03 22:03 UTC (permalink / raw)
To: Michael Ellerman; +Cc: trivial, linuxppc-dev, linux-kernel
Cc: trivial@kernel.org
Signed-off-by: Greg Kurz <groug@kaod.org>
---
arch/powerpc/kvm/book3s_xive.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/kvm/book3s_xive.c b/arch/powerpc/kvm/book3s_xive.c
index 1fcb3d6f1db8..e3169e8bf1fc 100644
--- a/arch/powerpc/kvm/book3s_xive.c
+++ b/arch/powerpc/kvm/book3s_xive.c
@@ -501,7 +501,7 @@ static void xive_finish_unmask(struct kvmppc_xive *xive,
kvmppc_xive_select_irq(state, &hw_num, &xd);
/*
- * See command in xive_lock_and_mask() concerning masking
+ * See comment in xive_lock_and_mask() concerning masking
* via firmware.
*/
if (xd->flags & OPAL_XIVE_IRQ_MASK_VIA_FW) {
^ permalink raw reply related
* Re: [PATCH v2] powerpc/boot: pass CONFIG options in a simpler and more robust way
From: Michael Ellerman @ 2019-07-04 0:26 UTC (permalink / raw)
To: Masahiro Yamada, linuxppc-dev
Cc: Rob Herring, linux-kernel, Nicholas Piggin, Mark Greer,
Masahiro Yamada, Oliver O'Halloran, Joel Stanley,
Paul Mackerras
In-Reply-To: <1557756964-13087-1-git-send-email-yamada.masahiro@socionext.com>
Masahiro Yamada <yamada.masahiro@socionext.com> writes:
> Commit 5e9dcb6188a4 ("powerpc/boot: Expose Kconfig symbols to wrapper")
> was wrong, but commit e41b93a6be57 ("powerpc/boot: Fix build failures
> with -j 1") was also wrong.
>
> The correct dependency is:
>
> $(obj)/serial.o: $(obj)/autoconf.h
>
> However, I do not see the reason why we need to copy autoconf.h to
> arch/power/boot/. Nor do I see consistency in the way of passing
> CONFIG options.
>
> decompress.c references CONFIG_KERNEL_GZIP and CONFIG_KERNEL_XZ, which
> are passed via the command line.
>
> serial.c includes autoconf.h to reference a couple of CONFIG options,
> but this is fragile because we often forget to include "autoconf.h"
> from source files.
>
> In fact, it is already broken.
>
> ppc_asm.h references CONFIG_PPC_8xx, but utils.S is not given any way
> to access CONFIG options. So, CONFIG_PPC_8xx is never defined here.
>
> Pass $(LINUXINCLUDE) to make sure CONFIG options are accessible from
> all .c and .S files in arch/powerpc/boot/.
This breaks our skiroot_defconfig, I don't know why yet:
In file included from /kisskb/src/arch/powerpc/boot/../../../lib/decompress_unxz.c:236:0,
from /kisskb/src/arch/powerpc/boot/decompress.c:42:
/kisskb/src/arch/powerpc/boot/../../../lib/xz/xz_dec_bcj.c: In function 'bcj_powerpc':
/kisskb/src/arch/powerpc/boot/../../../lib/xz/xz_dec_bcj.c:166:11: warning: implicit declaration of function 'get_unaligned_be32' [-Wimplicit-function-declaration]
instr = get_unaligned_be32(buf + i);
http://kisskb.ellerman.id.au/kisskb/buildresult/13862914/
cheers
^ permalink raw reply
* Re: [v2 09/12] powerpc/mce: Enable MCE notifiers in external modules
From: Nicholas Piggin @ 2019-07-04 2:36 UTC (permalink / raw)
To: Reza Arbab
Cc: Santosh Sivaraj, Aneesh Kumar K.V, Mahesh Salgaonkar,
Chandan Rajendra, linuxppc-dev
In-Reply-To: <20190703172008.aiyofnhqgbzi6ckw@arbab-vm>
Reza Arbab's on July 4, 2019 3:20 am:
> On Tue, Jul 02, 2019 at 04:17:11PM +1000, Nicholas Piggin wrote:
>>Santosh Sivaraj's on July 2, 2019 3:19 pm:
>>> --- a/arch/powerpc/kernel/exceptions-64s.S
>>> +++ b/arch/powerpc/kernel/exceptions-64s.S
>>> @@ -458,6 +458,12 @@ EXC_COMMON_BEGIN(machine_check_handle_early)
>>> bl machine_check_early
>>> std r3,RESULT(r1) /* Save result */
>>>
>>> + /* Notifiers may be in a module, so enable virtual addressing. */
>>> + mfmsr r11
>>> + ori r11,r11,MSR_IR
>>> + ori r11,r11,MSR_DR
>>> + mtmsr r11
>>
>>Can't do this, we could take a machine check somewhere the MMU is
>>not sane (in fact the guest early mce handling that was added recently
>>should not be enabling virtual mode either, which needs to be fixed).
>
> Rats. So in machine_check_handle_early() there are two options; either
>
> 1. The mc is unhandled/unrecoverable. Stay in real mode, proceed to
> unrecover_mce(), the fatal path of no return (panic, reboot, etc).
>
> 2. The mc is handled/recovered. Return from MCE where any further action
> can be done by processing the machine check event workqueue. Am I
> understanding you correctly that this is the absolute earliest we can
> get back to virtual mode?
Yes.
> Since the notifier chain is actually part of the decision between (1)
> and (2), it's a hard limitation then that callbacks be in real address
> space. Is there any way to structure things so that's not the case?
If we tested for KVM guest first, and went through and marked (maybe
in a paca flag) everywhere else that put the MMU into a bad / non-host
state, and had the notifiers use the machine check stack, then it
would be possible to enable MMU here.
Hmm, testing for IR|DR after testing for KVM guest might actually be
enough without requiring changes outside the machine check handler...
Actually no that may not quite work because the handler could take a
SLB miss and it might have been triggered inside the SLB miss handler.
All in all I'm pretty against turning on MMU in the MCE handler
anywhere.
> Luckily this patch isn't really necessary for memcpy_mcsafe(), but we
> have a couple of other potential users of the notifier from external
> modules (so their callbacks would require virtual mode).
What users are there? Do they do any significant amount of logic that
can not be moved to vmlinux?
Thanks,
Nick
^ permalink raw reply
* [PATCH] tpm: fixes uninitialized allocated banks for IBM vtpm driver
From: Nayna Jain @ 2019-07-04 3:32 UTC (permalink / raw)
To: linux-integrity, linuxppc-dev
Cc: Sachin Sant, George Wilson, Nayna Jain, linux-kernel,
Jarkko Sakkinen, Jason Gunthorpe, Mimi Zohar, Peter Huewe,
Michal Suchanek
The nr_allocated_banks and allocated banks are initialized as part of
tpm_chip_register. Currently, this is done as part of auto startup
function. However, some drivers, like the ibm vtpm driver, do not run
auto startup during initialization. This results in uninitialized memory
issue and causes a kernel panic during boot.
This patch moves the pcr allocation outside the auto startup function
into tpm_chip_register. This ensures that allocated banks are initialized
in any case.
Fixes: 879b589210a9 ("tpm: retrieve digest size of unknown algorithms with
PCR read")
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
drivers/char/tpm/tpm-chip.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/char/tpm/tpm.h | 1 +
drivers/char/tpm/tpm1-cmd.c | 12 ------------
drivers/char/tpm/tpm2-cmd.c | 6 +-----
4 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 8804c9e916fd..958508bb8379 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -550,6 +550,39 @@ static int tpm_add_hwrng(struct tpm_chip *chip)
return hwrng_register(&chip->hwrng);
}
+/*
+ * tpm_pcr_allocation() - initializes the chip allocated banks for PCRs
+ */
+static int tpm_pcr_allocation(struct tpm_chip *chip)
+{
+ int rc = 0;
+
+ if (chip->flags & TPM_CHIP_FLAG_TPM2) {
+ rc = tpm2_get_pcr_allocation(chip);
+ if (rc)
+ goto out;
+ }
+
+ /* Initialize TPM 1.2 */
+ chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks),
+ GFP_KERNEL);
+ if (!chip->allocated_banks) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ chip->allocated_banks[0].alg_id = TPM_ALG_SHA1;
+ chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1];
+ chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1;
+ chip->nr_allocated_banks = 1;
+
+ return 0;
+out:
+ if (rc < 0)
+ rc = -ENODEV;
+ return rc;
+}
+
/*
* tpm_chip_register() - create a character device for the TPM chip
* @chip: TPM chip to use.
@@ -573,6 +606,10 @@ int tpm_chip_register(struct tpm_chip *chip)
if (rc)
return rc;
+ rc = tpm_pcr_allocation(chip);
+ if (rc)
+ return rc;
+
tpm_sysfs_add_device(chip);
rc = tpm_bios_log_setup(chip);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 2cce072f25b5..eabe6b755fa6 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -454,6 +454,7 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
u32 *value, const char *desc);
+ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
int tpm2_auto_startup(struct tpm_chip *chip);
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index 85dcf2654d11..ec5f3693c096 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -696,18 +696,6 @@ int tpm1_auto_startup(struct tpm_chip *chip)
goto out;
}
- chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks),
- GFP_KERNEL);
- if (!chip->allocated_banks) {
- rc = -ENOMEM;
- goto out;
- }
-
- chip->allocated_banks[0].alg_id = TPM_ALG_SHA1;
- chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1];
- chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1;
- chip->nr_allocated_banks = 1;
-
return rc;
out:
if (rc > 0)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index e74c5b7b64bf..b4384d0e3741 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -841,7 +841,7 @@ struct tpm2_pcr_selection {
u8 pcr_select[3];
} __packed;
-static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
+ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
{
struct tpm2_pcr_selection pcr_selection;
struct tpm_buf buf;
@@ -1041,10 +1041,6 @@ int tpm2_auto_startup(struct tpm_chip *chip)
goto out;
}
- rc = tpm2_get_pcr_allocation(chip);
- if (rc)
- goto out;
-
rc = tpm2_get_cc_attrs_tbl(chip);
out:
--
2.20.1
^ permalink raw reply related
* Re: Kernel Bug/oops during boot (PowerVM LPAR w/vTPM)
From: Nayna @ 2019-07-04 3:36 UTC (permalink / raw)
To: Michal Suchánek, Sachin Sant
Cc: nayna, linuxppc-dev, Mimi Zohar, gcwilson
In-Reply-To: <20190703231559.29d54c5c@kitsune.suse.cz>
Hi Sachin and Michal,
On 07/03/2019 05:15 PM, Michal Suchánek wrote:
> On Wed, 3 Jul 2019 13:17:16 +0200
> Michal Suchánek <msuchanek@suse.de> wrote:
>
>> On Wed, 3 Jul 2019 15:54:59 +0530
>> Sachin Sant <sachinp@linux.vnet.ibm.com> wrote:
>>
>>> Booting mainline kernel on PowerVM LPAR with vTPM enabled results
>>> into a kernel crash.
>>>
>>> [ 0.365989] BUG: Kernel NULL pointer dereference at 0x00000012
> ...
>>> [ 0.366085] NIP [c00000000073dd80] tpm1_pcr_extend+0x130/0x230
>>> [ 0.366090] LR [c00000000073dcd0] tpm1_pcr_extend+0x80/0x230
> ...
>> You need to revert (or fix up) commit 0b6cf6b97b7e ("tpm: pass an array
>> of tpm_extend_digest structures to tpm_pcr_extend()". At least
>> reverting it fixes the issue for me.
> FTR this is the revert on lkml https://lkml.org/lkml/2019/7/1/423
>
>
I have just now posted the fix for this to the mailing list and copied
both of you. I would appreciate your testing and confirming that the
patch "tpm: fixes uninitialized allocated banks for IBM vtpm driver"
addresses this bug.
Thanks & Regards,
- Nayna
^ permalink raw reply
* pci memory resources not being assigned to bridge
From: Chris Packham @ 2019-07-04 5:11 UTC (permalink / raw)
To: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org
Hi,
I'm in the process of updating our products from a 4.4 based kernel to
5.1 (and probably 5.2 since that release is imminent).
On one product which uses a Freescale/NXP P2041 CPU, IDT pcie bridge and
Marvell switch chip[1].
Annoyingly the hardware has a reset line that holds the switch chip in
reset but the bridge chip has a separate reset line that is not held
high by default. So when the system starts up the initial scan sees the
bridge and assigns resource to its end points. Later once the switch
chip reset is released (either via a gpio-hog or manually) and the bus
rescanned the switch devices are detected but the host side of the pci
bridge isn't assigned any memory space so pci accesses trigger a master
abort which on that SoC leads to a machine check exception.
[root@linuxbox ~]# uname -a
Linux linuxbox 5.2.0-rc7-at1+ #8 SMP Thu Jul 4 04:26:18 UTC 2019 ppc
GNU/Linu[root@linuxbox ~]# lspci -t
-+-[2000:00]---00.0-[01-06]----00.0-[02-06]--+-02.0-[03]--
| +-03.0-[04]--
| +-04.0-[05]--
| \-05.0-[06]--
\-[0000:00]-
[root@linuxbox ~]# lspci -v -s 2000:01:00
2000:01:00.0 Class 0604: Device 111d:803e (rev 0e)
Flags: bus master, fast devsel, latency 0
Bus: primary=01, secondary=02, subordinate=06, sec-latency=0
I/O behind bridge: None
Memory behind bridge: None
Prefetchable memory behind bridge: None
Capabilities: [40] Express Upstream Port, MSI 00
Capabilities: [c0] Power Management version 3
Capabilities: [100] Advanced Error Reporting
Capabilities: [200] Virtual Channel
Kernel driver in use: pcieport
[root@linuxbox ~]# echo 472 >/sys/class/gpio/export
[root@linuxbox ~]# echo out >/sys/class/gpio/gpio472/direction
[root@linuxbox ~]# echo 0 >/sys/class/gpio/gpio472/value
[root@linuxbox ~]# echo 1 >/sys/bus/pci/rescan
pci 2000:03:00.0: [11ab:e097] type 00 class 0x058000
pci 2000:03:00.0: reg 0x10: [mem 0xd0000000-0xd00fffff 64bit pref]
pci 2000:03:00.0: reg 0x18: [mem 0x00000000-0x03ffffff 64bit]
pci 2000:03:00.0: Adding to iommu group 15
pci_bus 2000:03: busn_res: [bus 03] end is updated to 03
pci 2000:04:00.0: [11ab:e097] type 00 class 0x058000
pci 2000:04:00.0: reg 0x10: [mem 0xd0000000-0xd00fffff 64bit pref]
pci 2000:04:00.0: reg 0x18: [mem 0x00000000-0x03ffffff 64bit]
pci 2000:04:00.0: Adding to iommu group 15
pci_bus 2000:04: busn_res: [bus 04] end is updated to 04
pci 2000:05:00.0: [11ab:e097] type 00 class 0x058000
pci 2000:05:00.0: reg 0x10: [mem 0xd0000000-0xd00fffff 64bit pref]
pci 2000:05:00.0: reg 0x18: [mem 0x00000000-0x03ffffff 64bit]
pci 2000:05:00.0: Adding to iommu group 15
pci_bus 2000:05: busn_res: [bus 05] end is updated to 05
pci 2000:06:00.0: [11ab:e097] type 00 class 0x058000
pci 2000:06:00.0: reg 0x10: [mem 0xd0000000-0xd00fffff 64bit pref]
pci 2000:06:00.0: reg 0x18: [mem 0x00000000-0x03ffffff 64bit]
pci 2000:06:00.0: Adding to iommu group 15
pci_bus 2000:06: busn_res: [bus 06] end is updated to 06
pci_bus 2000:02: busn_res: [bus 02-06] end is updated to 06
pci_bus 2000:01: busn_res: [bus 01-06] end is updated to 06
pcieport 2000:00:00.0: BAR 9: no space for [mem size 0x00400000 64bit pref]
pcieport 2000:00:00.0: BAR 9: failed to assign [mem size 0x00400000
64bit pref]
pcieport 2000:01:00.0: BAR 8: assigned [mem 0xc40000000-0xc4fffffff]
pcieport 2000:01:00.0: BAR 9: assigned [mem 0xc50000000-0xc503fffff
64bit pref]
pci 2000:02:02.0: BAR 8: assigned [mem 0xc40000000-0xc43ffffff]
pci 2000:02:03.0: BAR 8: assigned [mem 0xc44000000-0xc47ffffff]
pci 2000:02:04.0: BAR 8: assigned [mem 0xc48000000-0xc4bffffff]
pci 2000:02:05.0: BAR 8: assigned [mem 0xc4c000000-0xc4fffffff]
pci 2000:02:02.0: BAR 9: assigned [mem 0xc50000000-0xc500fffff 64bit pref]
pci 2000:02:03.0: BAR 9: assigned [mem 0xc50100000-0xc501fffff 64bit pref]
pci 2000:02:04.0: BAR 9: assigned [mem 0xc50200000-0xc502fffff 64bit pref]
pci 2000:02:05.0: BAR 9: assigned [mem 0xc50300000-0xc503fffff 64bit pref]
pci 2000:03:00.0: BAR 2: assigned [mem 0xc40000000-0xc43ffffff 64bit]
pci 2000:03:00.0: BAR 0: assigned [mem 0xc50000000-0xc500fffff 64bit pref]
pci 2000:02:02.0: PCI bridge to [bus 03]
pci 2000:02:02.0: bridge window [mem 0xc40000000-0xc43ffffff]
pci 2000:02:02.0: bridge window [mem 0xc50000000-0xc500fffff 64bit pref]
pci 2000:04:00.0: BAR 2: assigned [mem 0xc44000000-0xc47ffffff 64bit]
pci 2000:04:00.0: BAR 0: assigned [mem 0xc50100000-0xc501fffff 64bit pref]
pci 2000:02:03.0: PCI bridge to [bus 04]
pci 2000:02:03.0: bridge window [mem 0xc44000000-0xc47ffffff]
pci 2000:02:03.0: bridge window [mem 0xc50100000-0xc501fffff 64bit pref]
pci 2000:05:00.0: BAR 2: assigned [mem 0xc48000000-0xc4bffffff 64bit]
pci 2000:05:00.0: BAR 0: assigned [mem 0xc50200000-0xc502fffff 64bit pref]
pci 2000:02:04.0: PCI bridge to [bus 05]
pci 2000:02:04.0: bridge window [mem 0xc48000000-0xc4bffffff]
pci 2000:02:04.0: bridge window [mem 0xc50200000-0xc502fffff 64bit pref]
pci 2000:06:00.0: BAR 2: assigned [mem 0xc4c000000-0xc4fffffff 64bit]
pci 2000:06:00.0: BAR 0: assigned [mem 0xc50300000-0xc503fffff 64bit pref]
pci 2000:02:05.0: PCI bridge to [bus 06]
pci 2000:02:05.0: bridge window [mem 0xc4c000000-0xc4fffffff]
pci 2000:02:05.0: bridge window [mem 0xc50300000-0xc503fffff 64bit pref]
[root@linuxbox ~]# lspci -t
-+-[2000:00]---00.0-[01-06]----00.0-[02-06]--+-02.0-[03]----00.0
| +-03.0-[04]----00.0
| +-04.0-[05]----00.0
| \-05.0-[06]----00.0
\-[0000:00]-
[root@linuxbox ~]# lspci -v -s 2000:01:00
2000:01:00.0 Class 0604: Device 111d:803e (rev 0e)
Flags: bus master, fast devsel, latency 0
Bus: primary=01, secondary=02, subordinate=06, sec-latency=0
I/O behind bridge: None
Memory behind bridge: None
Prefetchable memory behind bridge: None
Capabilities: [40] Express Upstream Port, MSI 00
Capabilities: [c0] Power Management version 3
Capabilities: [100] Advanced Error Reporting
Capabilities: [200] Virtual Channel
Kernel driver in use: pcieport
[root@linuxbox ~]# modprobe pptest
pptest: loading out-of-tree module taints kernel.
pptest: module license 'unspecified' taints kernel.
Disabling lock debugging due to kernel taint
pptest 2000:03:00.0: enabling device (0000 -> 0002)
pptest 2000:03:00.0: resource0 [mem 0xc50000000-0xc500fffff 64bit pref]
mapped to c1980000
pptest 2000:03:00.0: resource2 [mem 0xc40000000-0xc43ffffff 64bit]
mapped to c1b00000
Oops: Machine check, sig: 7 [#1]
BE PAGE_SIZE=4K SMP NR_CPUS=8 CoreNet Generic
Modules linked in: pptest(PO+)
CPU: 0 PID: 417 Comm: modprobe Tainted: P M O 5.2.0-rc7-at1+ #8
NIP: b96fd10c LR: b96fd100 CTR: 80419df0
REGS: af19bf08 TRAP: 0204 Tainted: P M O (5.2.0-rc7-at1+)
MSR: 00029002 <CE,EE,ME> CR: 28228422 XER: 20000000
DEAR: c1b0004c ESR: 00000000
GPR00: b96fd100 ad107c68 ada1dd00 00000055 ae7303c4 ae739ba8 2dd8e000
00021002
GPR08: 00000007 c1b0004c 2dd8e000 000001f8 000001f8 10030838 0000001d
00000003
GPR16: ad107eb0 b96fd68e b96fd61c 0000fff1 ad107e00 00000398 b96bd000
00000017
GPR24: b96fd488 00000002 b96fd308 b96fd308 b96fd280 adb2cd10 a9e55800
a9e55878
NIP [b96fd10c] 0xb96fd10c
LR [b96fd100] 0xb96fd100
Call Trace:
[ad107c68] [b96fd100] 0xb96fd100 (unreliable)
[ad107c88] [803d0344] pci_device_probe+0xc4/0x150
[ad107ca8] [8043898c] really_probe+0x1ec/0x340
[ad107cd8] [8043909c] device_driver_attach+0x7c/0x90
[ad107cf8] [8043916c] __driver_attach+0xbc/0x120
[ad107d18] [804364b8] bus_for_each_dev+0x68/0xc0
[ad107d48] [80437ad8] bus_add_driver+0x178/0x250
[ad107d68] [80439888] driver_register+0x88/0x160
[ad107d78] [80002c00] do_one_initcall+0x40/0x210
[ad107dd8] [800c1218] do_init_module+0x60/0x1e8
[ad107df8] [800c0198] load_module+0x1c68/0x2230
[ad107ea8] [800c0940] sys_finit_module+0xa0/0xd0
[ad107f38] [800111d8] ret_from_syscall+0x0/0x38
--- interrupt: c00 at 0xff1ca78
LR = 0x1000edf4
Instruction dump:
7f84e378 38a00000 480000f5 80fd0008 7fe3fb78 7f84e378 38a00002 38de01e0
480000dd 813d0008 3929004c 7c0004ac <7ca04c2c> 0c050000 4c00012c 3c80b970
---[ end trace baaeae7553646d27 ]---
Machine check in kernel mode.
Caused by (from MCSR=a000): Load Error Report
Guarded Load Error Report
Bus error
If I remove the bridge, release the switch reset then rescan the pci bus
the memory space is assigned and things appear to work.
[root@linuxbox ~]# echo 1
>/sys/bus/pci/devices/2000:01:00.0/removepci_bus 2000:03: busn_res:
[bus 03] is released
pci 2000:02:02.0: Removing from iommu group 15
pci_bus 2000:04: busn_res: [bus 04] is released
pci 2000:02:03.0: Removing from iommu group 15
pci_bus 2000:05: busn_res: [bus 05] is released
pci 2000:02:04.0: Removing from iommu group 15
pci_bus 2000:06: busn_res: [bus 06] is released
pci 2000:02:05.0: Removing from iommu group 15
pci_bus 2000:02: busn_res: [bus 02-06] is released
pci 2000:01:00.0: Removing from iommu group 15
[root@linuxbox ~]# echo 472 >/sys/class/gpio/export
[root@linuxbox ~]# echo out >/sys/class/gpio/gpio472/direction
[root@linuxbox ~]# echo 0 >/sys/class/gpio/gpio472/value
[root@linuxbox ~]# echo 1 >/sys/bus/pci/rescan
snip
[root@linuxbox ~]# lspci -t
-+-[2000:00]---00.0-[01-06]----00.0-[02-06]--+-02.0-[03]----00.0
| +-03.0-[04]----00.0
| +-04.0-[05]----00.0
| \-05.0-[06]----00.0
\-[0000:00]-
[root@linuxbox ~]# lspci -v -s 2000:01:00
2000:01:00.0 Class 0604: Device 111d:803e (rev 0e)
Flags: bus master, fast devsel, latency 0
Bus: primary=01, secondary=02, subordinate=06, sec-latency=0
I/O behind bridge: None
Memory behind bridge: e0000000-efffffff [size=256M]
Prefetchable memory behind bridge:
00000000f0000000-00000000f03fffff [size=4M]
Capabilities: [40] Express Upstream Port, MSI 00
Capabilities: [c0] Power Management version 3
Capabilities: [100] Advanced Error Reporting
Capabilities: [200] Virtual Channel
Kernel driver in use: pcieport
[root@linuxbox ~]# modprobe pptest
pptest: loading out-of-tree module taints kernel.
pptest: module license 'unspecified' taints kernel.
Disabling lock debugging due to kernel taint
pptest 2000:03:00.0: enabling device (0000 -> 0002)
pptest 2000:03:00.0: resource0 [mem 0xc50000000-0xc500fffff 64bit pref]
mapped to c1980000
pptest 2000:03:00.0: resource2 [mem 0xc40000000-0xc43ffffff 64bit]
mapped to c1b00000
pptest 2000:03:00.0: 0x4c = e0973
pptest 2000:03:00.0: 0x50 = 11ab
pptest 2000:04:00.0: enabling device (0000 -> 0002)
pptest 2000:04:00.0: resource0 [mem 0xc50100000-0xc501fffff 64bit pref]
mapped to c5b80000
pptest 2000:04:00.0: resource2 [mem 0xc44000000-0xc47ffffff 64bit]
mapped to c5d00000
pptest 2000:04:00.0: 0x4c = e0973
pptest 2000:04:00.0: 0x50 = 11ab
pptest 2000:05:00.0: enabling device (0000 -> 0002)
pptest 2000:05:00.0: resource0 [mem 0xc50200000-0xc502fffff 64bit pref]
mapped to c9d80000
pptest 2000:05:00.0: resource2 [mem 0xc48000000-0xc4bffffff 64bit]
mapped to c9f00000
pptest 2000:05:00.0: 0x4c = e0973
pptest 2000:05:00.0: 0x50 = 11ab
pptest 2000:06:00.0: enabling device (0000 -> 0002)
pptest 2000:06:00.0: resource0 [mem 0xc50300000-0xc503fffff 64bit pref]
mapped to cdf80000
pptest 2000:06:00.0: resource2 [mem 0xc4c000000-0xc4fffffff 64bit]
mapped to ce100000
pptest 2000:06:00.0: 0x4c = e0973
pptest 2000:06:00.0: 0x50 = 11ab
Previously (under 4.4.x) we were able to get the memory space assigned
by triggering a rescan (i.e. the first case above) but I've always seen
the code that did that as a bit of a hack.
Why would the rescan in the second case assign memory that wasn't
assigned by the first case? Is there any way I can avoid the removal
dance, ideally by assigning reset gpios in the device tree?
Thanks,
Chris
--
[1] - Similar hardware to
https://lore.kernel.org/linux-pci/403548ec3a7543b08ca32e47a1465e70@svr-chch-ex1.atlnz.lc/
but a different CPU and architecture.
^ permalink raw reply
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