* Re: [PATCH 03/10] mm: Convert p[te|md]_numa users to p[te|md]_protnone_numa
From: Aneesh Kumar K.V @ 2014-11-21 5:28 UTC (permalink / raw)
To: Mel Gorman, Linux Kernel
Cc: Rik van Riel, Linus Torvalds, Hugh Dickins, Linux-MM, Ingo Molnar,
Paul Mackerras, Mel Gorman, Sasha Levin, Dave Jones, LinuxPPC-dev,
Kirill Shutemov
In-Reply-To: <1416478790-27522-4-git-send-email-mgorman@suse.de>
Mel Gorman <mgorman@suse.de> writes:
> Convert existing users of pte_numa and friends to the new helper. Note
> that the kernel is broken after this patch is applied until the other
> page table modifiers are also altered. This patch layout is to make
> review easier.
>
.....
> diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c
> index c90e602..b5d58d3 100644
> --- a/arch/powerpc/mm/pgtable.c
> +++ b/arch/powerpc/mm/pgtable.c
> @@ -173,7 +173,13 @@ void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
> pte_t pte)
> {
> #ifdef CONFIG_DEBUG_VM
> - WARN_ON(pte_val(*ptep) & _PAGE_PRESENT);
> + /*
> + * When handling numa faults, we already have the pte marked
> + * _PAGE_PRESENT, but we can be sure that it is not in hpte.
> + * Hence we can use set_pte_at for them.
> + */
> + WARN_ON((pte_val(*ptep) & (_PAGE_PRESENT | _PAGE_USER)) ==
> + (_PAGE_PRESENT | _PAGE_USER));
> #endif
This can be VM_WARN_ON with #ifdef removed.
> /* Note: mm->context.id might not yet have been assigned as
> * this context might not have been activated yet when this
> diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
-aneesh
^ permalink raw reply
* [PATCH v2 1/2] cpufreq: qoriq: Make the driver usable on all QorIQ platforms
From: Tang Yuantian @ 2014-11-21 7:28 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: r64188, linuxppc-dev, Tang Yuantian, linux-kernel, linux-pm
Freescale introduced new ARM core-based SoCs which support dynamic
frequency switch feature. DFS on new SoCs are compatible with current
PowerPC CoreNet platforms. In order to support those new platforms,
this driver needs to be updated. The main changes include:
1. Changed the names of functions in driver.
2. Added two new functions get_cpu_physical_id() and get_bus_freq().
3. Used a new way to get the CPU mask which share clock wire.
Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
---
v2:
- split the name change into a separete patch
- use policy->driver_data instead of per_cpu variable
drivers/cpufreq/Kconfig.arm | 8 ++
drivers/cpufreq/Kconfig.powerpc | 11 ++-
drivers/cpufreq/ppc-corenet-cpufreq.c | 160 +++++++++++++++++++++-------------
3 files changed, 112 insertions(+), 67 deletions(-)
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 83a75dc..1925ae94 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -247,3 +247,11 @@ config ARM_TEGRA_CPUFREQ
default y
help
This adds the CPUFreq driver support for TEGRA SOCs.
+
+config QORIQ_CPUFREQ
+ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
+ depends on OF && COMMON_CLK
+ select CLK_PPC_CORENET
+ help
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index 72564b7..3a34248 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -23,14 +23,13 @@ config CPU_FREQ_MAPLE
This adds support for frequency switching on Maple 970FX
Evaluation Board and compatible boards (IBM JS2x blades).
-config PPC_CORENET_CPUFREQ
- tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
- depends on PPC_E500MC && OF && COMMON_CLK
+config QORIQ_CPUFREQ
+ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
+ depends on OF && COMMON_CLK
select CLK_PPC_CORENET
help
- This adds the CPUFreq driver support for Freescale e500mc,
- e5500 and e6500 series SoCs which are capable of changing
- the CPU's frequency dynamically.
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c
index bee5df7..949d992 100644
--- a/drivers/cpufreq/ppc-corenet-cpufreq.c
+++ b/drivers/cpufreq/ppc-corenet-cpufreq.c
@@ -1,7 +1,7 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
- * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs.
+ * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,10 +20,9 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/smp.h>
-#include <sysdev/fsl_soc.h>
/**
- * struct cpu_data - per CPU data struct
+ * struct cpu_data
* @parent: the parent node of cpu clock
* @table: frequency table
*/
@@ -67,17 +66,78 @@ static const struct soc_data sdata[] = {
static u32 min_cpufreq;
static const u32 *fmask;
-static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
+#if defined(CONFIG_ARM)
+static int get_cpu_physical_id(int cpu)
+{
+ return topology_core_id(cpu);
+}
+#else
+static int get_cpu_physical_id(int cpu)
+{
+ return get_hard_smp_processor_id(cpu);
+}
+#endif
-/* cpumask in a cluster */
-static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
+static u32 get_bus_freq(void)
+{
+ struct device_node *soc;
+ u32 sysfreq;
+
+ soc = of_find_node_by_type(NULL, "soc");
+ if (!soc)
+ return 0;
+
+ if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
+ sysfreq = 0;
+
+ of_node_put(soc);
+
+ return sysfreq;
+}
-#ifndef CONFIG_SMP
-static inline const struct cpumask *cpu_core_mask(int cpu)
+static struct device_node *cpu_to_clk_node(int cpu)
{
- return cpumask_of(0);
+ struct device_node *np, *clk_np;
+
+ if (!cpu_present(cpu))
+ return NULL;
+
+ np = of_get_cpu_node(cpu, NULL);
+ if (!np)
+ return NULL;
+
+ clk_np = of_parse_phandle(np, "clocks", 0);
+ if (!clk_np)
+ return NULL;
+
+ of_node_put(np);
+
+ return clk_np;
+}
+
+/* traverse cpu nodes to get cpu mask of sharing clock wire */
+static void set_affected_cpus(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *clk_np;
+ struct cpumask *dstp = policy->cpus;
+ int i;
+
+ np = cpu_to_clk_node(policy->cpu);
+ if (!np)
+ return;
+
+ for_each_present_cpu(i) {
+ clk_np = cpu_to_clk_node(i);
+ if (!clk_np)
+ continue;
+
+ if (clk_np == np)
+ cpumask_set_cpu(i, dstp);
+
+ of_node_put(clk_np);
+ }
+ of_node_put(np);
}
-#endif
/* reduce the duplicated frequencies in frequency table */
static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
@@ -105,6 +165,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
int i, j, ind;
unsigned int freq, max_freq;
struct cpufreq_frequency_table table;
+
for (i = 0; i < count - 1; i++) {
max_freq = freq_table[i].frequency;
ind = i;
@@ -129,7 +190,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
}
}
-static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
+static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct device_node *np;
int i, count, ret;
@@ -145,10 +206,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- pr_err("%s: no memory\n", __func__);
+ if (!data)
goto err_np;
- }
policy->clk = of_clk_get(np, 0);
if (IS_ERR(policy->clk)) {
@@ -170,7 +229,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
}
if (fmask)
- mask = fmask[get_hard_smp_processor_id(cpu)];
+ mask = fmask[get_cpu_physical_id(cpu)];
else
mask = 0x0;
@@ -201,13 +260,12 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
data->table = table;
/* update ->cpus if we have cluster, no harm if not */
- cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
- for_each_cpu(i, per_cpu(cpu_mask, cpu))
- per_cpu(cpu_data, i) = data;
+ set_affected_cpus(policy);
+ policy->driver_data = data;
/* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC;
- do_div(u64temp, fsl_get_sys_freq());
+ do_div(u64temp, get_bus_freq());
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np);
@@ -219,7 +277,7 @@ err_nomem1:
err_node:
of_node_put(data->parent);
err_nomem2:
- per_cpu(cpu_data, cpu) = NULL;
+ policy->driver_data = NULL;
kfree(data);
err_np:
of_node_put(np);
@@ -227,43 +285,41 @@ err_np:
return -ENODEV;
}
-static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
- struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
+ struct cpu_data *data = policy->driver_data;
unsigned int cpu;
of_node_put(data->parent);
kfree(data->table);
kfree(data);
-
- for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
- per_cpu(cpu_data, cpu) = NULL;
+ policy->driver_data = NULL;
return 0;
}
-static int corenet_cpufreq_target(struct cpufreq_policy *policy,
+static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
unsigned int index)
{
struct clk *parent;
- struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
+ struct cpu_data *data = policy->driver_data;
parent = of_clk_get(data->parent, data->table[index].driver_data);
return clk_set_parent(policy->clk, parent);
}
-static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
- .name = "ppc_cpufreq",
+static struct cpufreq_driver qoriq_cpufreq_driver = {
+ .name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS,
- .init = corenet_cpufreq_cpu_init,
- .exit = __exit_p(corenet_cpufreq_cpu_exit),
+ .init = qoriq_cpufreq_cpu_init,
+ .exit = __exit_p(qoriq_cpufreq_cpu_exit),
.verify = cpufreq_generic_frequency_table_verify,
- .target_index = corenet_cpufreq_target,
+ .target_index = qoriq_cpufreq_target,
.get = cpufreq_generic_get,
.attr = cpufreq_generic_attr,
};
-static const struct of_device_id node_matches[] __initdata = {
+static const struct of_device_id node_matches[] __initconst = {
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
@@ -273,61 +329,43 @@ static const struct of_device_id node_matches[] __initdata = {
{}
};
-static int __init ppc_corenet_cpufreq_init(void)
+static int __init qoriq_cpufreq_init(void)
{
int ret;
struct device_node *np;
const struct of_device_id *match;
const struct soc_data *data;
- unsigned int cpu;
np = of_find_matching_node(NULL, node_matches);
if (!np)
return -ENODEV;
- for_each_possible_cpu(cpu) {
- if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
- goto err_mask;
- cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
- }
-
match = of_match_node(node_matches, np);
data = match->data;
if (data) {
if (data->flag)
fmask = data->freq_mask;
- min_cpufreq = fsl_get_sys_freq();
+ min_cpufreq = get_bus_freq();
} else {
- min_cpufreq = fsl_get_sys_freq() / 2;
+ min_cpufreq = get_bus_freq() / 2;
}
of_node_put(np);
- ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
+ ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret)
- pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
+ pr_info("Freescale QorIQ CPU frequency scaling driver\n");
return ret;
-
-err_mask:
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- return -ENOMEM;
}
-module_init(ppc_corenet_cpufreq_init);
+module_init(qoriq_cpufreq_init);
-static void __exit ppc_corenet_cpufreq_exit(void)
+static void __exit qoriq_cpufreq_exit(void)
{
- unsigned int cpu;
-
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
+ cpufreq_unregister_driver(&qoriq_cpufreq_driver);
}
-module_exit(ppc_corenet_cpufreq_exit);
+module_exit(qoriq_cpufreq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
-MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");
+MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
--
2.1.0.27.g96db324
^ permalink raw reply related
* [PATCH 2/2] cpufreq: qoriq: rename the driver
From: Tang Yuantian @ 2014-11-21 7:28 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: r64188, linuxppc-dev, Tang Yuantian, linux-kernel, linux-pm
In-Reply-To: <1416554882-16566-1-git-send-email-Yuantian.Tang@freescale.com>
This driver works on all QorIQ platforms which include
ARM-based cores and PPC-based cores.
Rename it in order to represent better.
Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
---
drivers/cpufreq/ppc-corenet-cpufreq.c | 371 ----------------------------------
drivers/cpufreq/qoriq-cpufreq.c | 371 ++++++++++++++++++++++++++++++++++
2 files changed, 371 insertions(+), 371 deletions(-)
delete mode 100644 drivers/cpufreq/ppc-corenet-cpufreq.c
create mode 100644 drivers/cpufreq/qoriq-cpufreq.c
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c
deleted file mode 100644
index 949d992..0000000
--- a/drivers/cpufreq/ppc-corenet-cpufreq.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright 2013 Freescale Semiconductor, Inc.
- *
- * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/clk.h>
-#include <linux/cpufreq.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/smp.h>
-
-/**
- * struct cpu_data
- * @parent: the parent node of cpu clock
- * @table: frequency table
- */
-struct cpu_data {
- struct device_node *parent;
- struct cpufreq_frequency_table *table;
-};
-
-/**
- * struct soc_data - SoC specific data
- * @freq_mask: mask the disallowed frequencies
- * @flag: unique flags
- */
-struct soc_data {
- u32 freq_mask[4];
- u32 flag;
-};
-
-#define FREQ_MASK 1
-/* see hardware specification for the allowed frqeuencies */
-static const struct soc_data sdata[] = {
- { /* used by p2041 and p3041 */
- .freq_mask = {0x8, 0x8, 0x2, 0x2},
- .flag = FREQ_MASK,
- },
- { /* used by p5020 */
- .freq_mask = {0x8, 0x2},
- .flag = FREQ_MASK,
- },
- { /* used by p4080, p5040 */
- .freq_mask = {0},
- .flag = 0,
- },
-};
-
-/*
- * the minimum allowed core frequency, in Hz
- * for chassis v1.0, >= platform frequency
- * for chassis v2.0, >= platform frequency / 2
- */
-static u32 min_cpufreq;
-static const u32 *fmask;
-
-#if defined(CONFIG_ARM)
-static int get_cpu_physical_id(int cpu)
-{
- return topology_core_id(cpu);
-}
-#else
-static int get_cpu_physical_id(int cpu)
-{
- return get_hard_smp_processor_id(cpu);
-}
-#endif
-
-static u32 get_bus_freq(void)
-{
- struct device_node *soc;
- u32 sysfreq;
-
- soc = of_find_node_by_type(NULL, "soc");
- if (!soc)
- return 0;
-
- if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
- sysfreq = 0;
-
- of_node_put(soc);
-
- return sysfreq;
-}
-
-static struct device_node *cpu_to_clk_node(int cpu)
-{
- struct device_node *np, *clk_np;
-
- if (!cpu_present(cpu))
- return NULL;
-
- np = of_get_cpu_node(cpu, NULL);
- if (!np)
- return NULL;
-
- clk_np = of_parse_phandle(np, "clocks", 0);
- if (!clk_np)
- return NULL;
-
- of_node_put(np);
-
- return clk_np;
-}
-
-/* traverse cpu nodes to get cpu mask of sharing clock wire */
-static void set_affected_cpus(struct cpufreq_policy *policy)
-{
- struct device_node *np, *clk_np;
- struct cpumask *dstp = policy->cpus;
- int i;
-
- np = cpu_to_clk_node(policy->cpu);
- if (!np)
- return;
-
- for_each_present_cpu(i) {
- clk_np = cpu_to_clk_node(i);
- if (!clk_np)
- continue;
-
- if (clk_np == np)
- cpumask_set_cpu(i, dstp);
-
- of_node_put(clk_np);
- }
- of_node_put(np);
-}
-
-/* reduce the duplicated frequencies in frequency table */
-static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
- int count)
-{
- int i, j;
-
- for (i = 1; i < count; i++) {
- for (j = 0; j < i; j++) {
- if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID ||
- freq_table[j].frequency !=
- freq_table[i].frequency)
- continue;
-
- freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
- break;
- }
- }
-}
-
-/* sort the frequencies in frequency table in descenting order */
-static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
- int count)
-{
- int i, j, ind;
- unsigned int freq, max_freq;
- struct cpufreq_frequency_table table;
-
- for (i = 0; i < count - 1; i++) {
- max_freq = freq_table[i].frequency;
- ind = i;
- for (j = i + 1; j < count; j++) {
- freq = freq_table[j].frequency;
- if (freq == CPUFREQ_ENTRY_INVALID ||
- freq <= max_freq)
- continue;
- ind = j;
- max_freq = freq;
- }
-
- if (ind != i) {
- /* exchange the frequencies */
- table.driver_data = freq_table[i].driver_data;
- table.frequency = freq_table[i].frequency;
- freq_table[i].driver_data = freq_table[ind].driver_data;
- freq_table[i].frequency = freq_table[ind].frequency;
- freq_table[ind].driver_data = table.driver_data;
- freq_table[ind].frequency = table.frequency;
- }
- }
-}
-
-static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
-{
- struct device_node *np;
- int i, count, ret;
- u32 freq, mask;
- struct clk *clk;
- struct cpufreq_frequency_table *table;
- struct cpu_data *data;
- unsigned int cpu = policy->cpu;
- u64 u64temp;
-
- np = of_get_cpu_node(cpu, NULL);
- if (!np)
- return -ENODEV;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- goto err_np;
-
- policy->clk = of_clk_get(np, 0);
- if (IS_ERR(policy->clk)) {
- pr_err("%s: no clock information\n", __func__);
- goto err_nomem2;
- }
-
- data->parent = of_parse_phandle(np, "clocks", 0);
- if (!data->parent) {
- pr_err("%s: could not get clock information\n", __func__);
- goto err_nomem2;
- }
-
- count = of_property_count_strings(data->parent, "clock-names");
- table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
- if (!table) {
- pr_err("%s: no memory\n", __func__);
- goto err_node;
- }
-
- if (fmask)
- mask = fmask[get_cpu_physical_id(cpu)];
- else
- mask = 0x0;
-
- for (i = 0; i < count; i++) {
- clk = of_clk_get(data->parent, i);
- freq = clk_get_rate(clk);
- /*
- * the clock is valid if its frequency is not masked
- * and large than minimum allowed frequency.
- */
- if (freq < min_cpufreq || (mask & (1 << i)))
- table[i].frequency = CPUFREQ_ENTRY_INVALID;
- else
- table[i].frequency = freq / 1000;
- table[i].driver_data = i;
- }
- freq_table_redup(table, count);
- freq_table_sort(table, count);
- table[i].frequency = CPUFREQ_TABLE_END;
-
- /* set the min and max frequency properly */
- ret = cpufreq_table_validate_and_show(policy, table);
- if (ret) {
- pr_err("invalid frequency table: %d\n", ret);
- goto err_nomem1;
- }
-
- data->table = table;
-
- /* update ->cpus if we have cluster, no harm if not */
- set_affected_cpus(policy);
- policy->driver_data = data;
-
- /* Minimum transition latency is 12 platform clocks */
- u64temp = 12ULL * NSEC_PER_SEC;
- do_div(u64temp, get_bus_freq());
- policy->cpuinfo.transition_latency = u64temp + 1;
-
- of_node_put(np);
-
- return 0;
-
-err_nomem1:
- kfree(table);
-err_node:
- of_node_put(data->parent);
-err_nomem2:
- policy->driver_data = NULL;
- kfree(data);
-err_np:
- of_node_put(np);
-
- return -ENODEV;
-}
-
-static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
-{
- struct cpu_data *data = policy->driver_data;
- unsigned int cpu;
-
- of_node_put(data->parent);
- kfree(data->table);
- kfree(data);
- policy->driver_data = NULL;
-
- return 0;
-}
-
-static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
- unsigned int index)
-{
- struct clk *parent;
- struct cpu_data *data = policy->driver_data;
-
- parent = of_clk_get(data->parent, data->table[index].driver_data);
- return clk_set_parent(policy->clk, parent);
-}
-
-static struct cpufreq_driver qoriq_cpufreq_driver = {
- .name = "qoriq_cpufreq",
- .flags = CPUFREQ_CONST_LOOPS,
- .init = qoriq_cpufreq_cpu_init,
- .exit = __exit_p(qoriq_cpufreq_cpu_exit),
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = qoriq_cpufreq_target,
- .get = cpufreq_generic_get,
- .attr = cpufreq_generic_attr,
-};
-
-static const struct of_device_id node_matches[] __initconst = {
- { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
- { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
- { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
- { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
- { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
- { .compatible = "fsl,qoriq-clockgen-2.0", },
- {}
-};
-
-static int __init qoriq_cpufreq_init(void)
-{
- int ret;
- struct device_node *np;
- const struct of_device_id *match;
- const struct soc_data *data;
-
- np = of_find_matching_node(NULL, node_matches);
- if (!np)
- return -ENODEV;
-
- match = of_match_node(node_matches, np);
- data = match->data;
- if (data) {
- if (data->flag)
- fmask = data->freq_mask;
- min_cpufreq = get_bus_freq();
- } else {
- min_cpufreq = get_bus_freq() / 2;
- }
-
- of_node_put(np);
-
- ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
- if (!ret)
- pr_info("Freescale QorIQ CPU frequency scaling driver\n");
-
- return ret;
-}
-module_init(qoriq_cpufreq_init);
-
-static void __exit qoriq_cpufreq_exit(void)
-{
- cpufreq_unregister_driver(&qoriq_cpufreq_driver);
-}
-module_exit(qoriq_cpufreq_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
-MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
new file mode 100644
index 0000000..949d992
--- /dev/null
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+
+/**
+ * struct cpu_data
+ * @parent: the parent node of cpu clock
+ * @table: frequency table
+ */
+struct cpu_data {
+ struct device_node *parent;
+ struct cpufreq_frequency_table *table;
+};
+
+/**
+ * struct soc_data - SoC specific data
+ * @freq_mask: mask the disallowed frequencies
+ * @flag: unique flags
+ */
+struct soc_data {
+ u32 freq_mask[4];
+ u32 flag;
+};
+
+#define FREQ_MASK 1
+/* see hardware specification for the allowed frqeuencies */
+static const struct soc_data sdata[] = {
+ { /* used by p2041 and p3041 */
+ .freq_mask = {0x8, 0x8, 0x2, 0x2},
+ .flag = FREQ_MASK,
+ },
+ { /* used by p5020 */
+ .freq_mask = {0x8, 0x2},
+ .flag = FREQ_MASK,
+ },
+ { /* used by p4080, p5040 */
+ .freq_mask = {0},
+ .flag = 0,
+ },
+};
+
+/*
+ * the minimum allowed core frequency, in Hz
+ * for chassis v1.0, >= platform frequency
+ * for chassis v2.0, >= platform frequency / 2
+ */
+static u32 min_cpufreq;
+static const u32 *fmask;
+
+#if defined(CONFIG_ARM)
+static int get_cpu_physical_id(int cpu)
+{
+ return topology_core_id(cpu);
+}
+#else
+static int get_cpu_physical_id(int cpu)
+{
+ return get_hard_smp_processor_id(cpu);
+}
+#endif
+
+static u32 get_bus_freq(void)
+{
+ struct device_node *soc;
+ u32 sysfreq;
+
+ soc = of_find_node_by_type(NULL, "soc");
+ if (!soc)
+ return 0;
+
+ if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
+ sysfreq = 0;
+
+ of_node_put(soc);
+
+ return sysfreq;
+}
+
+static struct device_node *cpu_to_clk_node(int cpu)
+{
+ struct device_node *np, *clk_np;
+
+ if (!cpu_present(cpu))
+ return NULL;
+
+ np = of_get_cpu_node(cpu, NULL);
+ if (!np)
+ return NULL;
+
+ clk_np = of_parse_phandle(np, "clocks", 0);
+ if (!clk_np)
+ return NULL;
+
+ of_node_put(np);
+
+ return clk_np;
+}
+
+/* traverse cpu nodes to get cpu mask of sharing clock wire */
+static void set_affected_cpus(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *clk_np;
+ struct cpumask *dstp = policy->cpus;
+ int i;
+
+ np = cpu_to_clk_node(policy->cpu);
+ if (!np)
+ return;
+
+ for_each_present_cpu(i) {
+ clk_np = cpu_to_clk_node(i);
+ if (!clk_np)
+ continue;
+
+ if (clk_np == np)
+ cpumask_set_cpu(i, dstp);
+
+ of_node_put(clk_np);
+ }
+ of_node_put(np);
+}
+
+/* reduce the duplicated frequencies in frequency table */
+static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
+ int count)
+{
+ int i, j;
+
+ for (i = 1; i < count; i++) {
+ for (j = 0; j < i; j++) {
+ if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID ||
+ freq_table[j].frequency !=
+ freq_table[i].frequency)
+ continue;
+
+ freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ break;
+ }
+ }
+}
+
+/* sort the frequencies in frequency table in descenting order */
+static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
+ int count)
+{
+ int i, j, ind;
+ unsigned int freq, max_freq;
+ struct cpufreq_frequency_table table;
+
+ for (i = 0; i < count - 1; i++) {
+ max_freq = freq_table[i].frequency;
+ ind = i;
+ for (j = i + 1; j < count; j++) {
+ freq = freq_table[j].frequency;
+ if (freq == CPUFREQ_ENTRY_INVALID ||
+ freq <= max_freq)
+ continue;
+ ind = j;
+ max_freq = freq;
+ }
+
+ if (ind != i) {
+ /* exchange the frequencies */
+ table.driver_data = freq_table[i].driver_data;
+ table.frequency = freq_table[i].frequency;
+ freq_table[i].driver_data = freq_table[ind].driver_data;
+ freq_table[i].frequency = freq_table[ind].frequency;
+ freq_table[ind].driver_data = table.driver_data;
+ freq_table[ind].frequency = table.frequency;
+ }
+ }
+}
+
+static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ struct device_node *np;
+ int i, count, ret;
+ u32 freq, mask;
+ struct clk *clk;
+ struct cpufreq_frequency_table *table;
+ struct cpu_data *data;
+ unsigned int cpu = policy->cpu;
+ u64 u64temp;
+
+ np = of_get_cpu_node(cpu, NULL);
+ if (!np)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto err_np;
+
+ policy->clk = of_clk_get(np, 0);
+ if (IS_ERR(policy->clk)) {
+ pr_err("%s: no clock information\n", __func__);
+ goto err_nomem2;
+ }
+
+ data->parent = of_parse_phandle(np, "clocks", 0);
+ if (!data->parent) {
+ pr_err("%s: could not get clock information\n", __func__);
+ goto err_nomem2;
+ }
+
+ count = of_property_count_strings(data->parent, "clock-names");
+ table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
+ if (!table) {
+ pr_err("%s: no memory\n", __func__);
+ goto err_node;
+ }
+
+ if (fmask)
+ mask = fmask[get_cpu_physical_id(cpu)];
+ else
+ mask = 0x0;
+
+ for (i = 0; i < count; i++) {
+ clk = of_clk_get(data->parent, i);
+ freq = clk_get_rate(clk);
+ /*
+ * the clock is valid if its frequency is not masked
+ * and large than minimum allowed frequency.
+ */
+ if (freq < min_cpufreq || (mask & (1 << i)))
+ table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ else
+ table[i].frequency = freq / 1000;
+ table[i].driver_data = i;
+ }
+ freq_table_redup(table, count);
+ freq_table_sort(table, count);
+ table[i].frequency = CPUFREQ_TABLE_END;
+
+ /* set the min and max frequency properly */
+ ret = cpufreq_table_validate_and_show(policy, table);
+ if (ret) {
+ pr_err("invalid frequency table: %d\n", ret);
+ goto err_nomem1;
+ }
+
+ data->table = table;
+
+ /* update ->cpus if we have cluster, no harm if not */
+ set_affected_cpus(policy);
+ policy->driver_data = data;
+
+ /* Minimum transition latency is 12 platform clocks */
+ u64temp = 12ULL * NSEC_PER_SEC;
+ do_div(u64temp, get_bus_freq());
+ policy->cpuinfo.transition_latency = u64temp + 1;
+
+ of_node_put(np);
+
+ return 0;
+
+err_nomem1:
+ kfree(table);
+err_node:
+ of_node_put(data->parent);
+err_nomem2:
+ policy->driver_data = NULL;
+ kfree(data);
+err_np:
+ of_node_put(np);
+
+ return -ENODEV;
+}
+
+static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ struct cpu_data *data = policy->driver_data;
+ unsigned int cpu;
+
+ of_node_put(data->parent);
+ kfree(data->table);
+ kfree(data);
+ policy->driver_data = NULL;
+
+ return 0;
+}
+
+static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int index)
+{
+ struct clk *parent;
+ struct cpu_data *data = policy->driver_data;
+
+ parent = of_clk_get(data->parent, data->table[index].driver_data);
+ return clk_set_parent(policy->clk, parent);
+}
+
+static struct cpufreq_driver qoriq_cpufreq_driver = {
+ .name = "qoriq_cpufreq",
+ .flags = CPUFREQ_CONST_LOOPS,
+ .init = qoriq_cpufreq_cpu_init,
+ .exit = __exit_p(qoriq_cpufreq_cpu_exit),
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = qoriq_cpufreq_target,
+ .get = cpufreq_generic_get,
+ .attr = cpufreq_generic_attr,
+};
+
+static const struct of_device_id node_matches[] __initconst = {
+ { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
+ { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
+ { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
+ { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], },
+ { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], },
+ { .compatible = "fsl,qoriq-clockgen-2.0", },
+ {}
+};
+
+static int __init qoriq_cpufreq_init(void)
+{
+ int ret;
+ struct device_node *np;
+ const struct of_device_id *match;
+ const struct soc_data *data;
+
+ np = of_find_matching_node(NULL, node_matches);
+ if (!np)
+ return -ENODEV;
+
+ match = of_match_node(node_matches, np);
+ data = match->data;
+ if (data) {
+ if (data->flag)
+ fmask = data->freq_mask;
+ min_cpufreq = get_bus_freq();
+ } else {
+ min_cpufreq = get_bus_freq() / 2;
+ }
+
+ of_node_put(np);
+
+ ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
+ if (!ret)
+ pr_info("Freescale QorIQ CPU frequency scaling driver\n");
+
+ return ret;
+}
+module_init(qoriq_cpufreq_init);
+
+static void __exit qoriq_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&qoriq_cpufreq_driver);
+}
+module_exit(qoriq_cpufreq_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
+MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
--
2.1.0.27.g96db324
^ permalink raw reply related
* Re: [PATCH v2 5/6] pseries: Implement memory hotplug add in the kernel
From: Cyril Bur @ 2014-11-21 7:49 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev
In-Reply-To: <546A6EA7.3030806@linux.vnet.ibm.com>
On Mon, 2014-11-17 at 15:54 -0600, Nathan Fontenot wrote:
> Move handling of memory hotplug add on pseries completely into the kernel.
>
> The current memory hotplug add path involves the drmgr command doing part
> of this work in userspace and requesting the kernel to do additional pieces.
> This patch allows us to handle the act completely in the kernel via rtas
> hotplug events. This allows us to perform the operation faster and provide
> a common memory hotplug add path for PowerVM and PowerKVM systems.
>
> The patch does introduce a static rtas_hp_event variable that is set to
> true when updating the device tree during memory hotplug initiated from
> a rtas hotplug event. This is needed because we do not need to do the
> work in the of notifier, this work is already performed in handling the
> hotplug request. At a later time we can remove this when we deprecate the
> previous method of memory hotplug.
>
> Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/platforms/pseries/hotplug-memory.c | 244 +++++++++++++++++++++++
> 1 file changed, 243 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index 69d178b..b57d42b 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -16,6 +16,7 @@
> #include <linux/memblock.h>
> #include <linux/memory.h>
> #include <linux/memory_hotplug.h>
> +#include <linux/slab.h>
>
> #include <asm/firmware.h>
> #include <asm/machdep.h>
> @@ -23,6 +24,8 @@
> #include <asm/sparsemem.h>
> #include "pseries.h"
>
> +static bool rtas_hp_event;
> +
> unsigned long pseries_memory_block_size(void)
> {
> struct device_node *np;
> @@ -66,6 +69,52 @@ unsigned long pseries_memory_block_size(void)
> return memblock_size;
> }
>
> +static void dlpar_free_drconf_property(struct property *prop)
> +{
> + kfree(prop->name);
> + kfree(prop->value);
> + kfree(prop);
> +}
> +
> +static struct property *dlpar_clone_drconf_property(struct device_node *dn)
> +{
> + struct property *prop, *new_prop;
> +
> + prop = of_find_property(dn, "ibm,dynamic-memory", NULL);
> + if (!prop)
> + return NULL;
> +
> + new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
> + if (!new_prop)
> + return NULL;
> +
> + new_prop->name = kstrdup(prop->name, GFP_KERNEL);
> + new_prop->value = kmalloc(prop->length, GFP_KERNEL);
> + if (!new_prop->name || !new_prop->value) {
> + dlpar_free_drconf_property(new_prop);
> + return NULL;
> + }
> +
> + memcpy(new_prop->value, prop->value, prop->length);
> + new_prop->length = prop->length;
> +
> + return new_prop;
> +}
> +
> +static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb)
> +{
> + unsigned long section_nr;
> + struct mem_section *mem_sect;
> + struct memory_block *mem_block;
> + u64 phys_addr = be64_to_cpu(lmb->base_addr);
> +
> + section_nr = pfn_to_section_nr(PFN_DOWN(phys_addr));
> + mem_sect = __nr_to_section(section_nr);
> +
> + mem_block = find_memory_block(mem_sect);
> + return mem_block;
> +}
> +
> #ifdef CONFIG_MEMORY_HOTREMOVE
> static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
> {
> @@ -136,19 +185,209 @@ static inline int pseries_remove_mem_node(struct device_node *np)
> }
> #endif /* CONFIG_MEMORY_HOTREMOVE */
>
> +static int dlpar_add_lmb(struct of_drconf_cell *lmb)
> +{
> + struct memory_block *mem_block;
> + u64 phys_addr;
> + uint32_t drc_index;
I started commenting this and it turns out you've used uint32_t almost
everywhere for values you've pulled from the device tree or the elog.
These should be u32.
> + unsigned long pages_per_block;
> + unsigned long block_sz;
> + int nid, sections_per_block;
> + int rc;
> +
> + if (be32_to_cpu(lmb->flags) & DRCONF_MEM_ASSIGNED)
> + return -EINVAL;
> +
> + phys_addr = be64_to_cpu(lmb->base_addr);
> + drc_index = be32_to_cpu(lmb->drc_index);
> + block_sz = memory_block_size_bytes();
> + sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
> + pages_per_block = PAGES_PER_SECTION * sections_per_block;
> +
Perhaps I'm being a bit slow here but it isn't exactly clear what you're
getting with all those variables and could you explain what that
statement below is checking for?
> + if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
> + return -EINVAL;
> +
> + rc = dlpar_acquire_drc(drc_index);
> + if (rc)
> + return rc;
> +
> + /* Find the node id for this address */
> + nid = memory_add_physaddr_to_nid(phys_addr);
> +
> + /* Add the memory */
> + rc = add_memory(nid, phys_addr, block_sz);
> + if (rc) {
> + dlpar_release_drc(drc_index);
> + return rc;
> + }
> +
> + /* Register this block of memory */
> + rc = memblock_add(phys_addr, block_sz);
> + if (rc) {
> + remove_memory(nid, phys_addr, block_sz);
> + dlpar_release_drc(drc_index);
> + return rc;
> + }
> +
> + mem_block = lmb_to_memblock(lmb);
> + if (!mem_block) {
> + remove_memory(nid, phys_addr, block_sz);
> + dlpar_release_drc(drc_index);
> + return -EINVAL;
> + }
> +
> + rc = device_online(&mem_block->dev);
> + put_device(&mem_block->dev);
> + if (rc) {
> + remove_memory(nid, phys_addr, block_sz);
> + dlpar_release_drc(drc_index);
> + return rc;
> + }
> +
> + lmb->flags |= cpu_to_be32(DRCONF_MEM_ASSIGNED);
> + return 0;
> +}
> +
> +static int dlpar_memory_add_by_count(struct pseries_hp_errorlog *hp_elog,
> + struct property *prop)
> +{
> + struct of_drconf_cell *lmbs;
> + uint32_t num_lmbs;
> + __be32 *p;
> + int i, lmbs_to_add;
> + int lmbs_available = 0;
> + int lmbs_added = 0;
> + int rc;
> +
> + lmbs_to_add = be32_to_cpu(hp_elog->_drc_u.drc_count);
Didn't you already do the endian conversion back in
handle_dlpar_errorlog?
> + pr_info("Attempting to hot-add %d LMB(s)\n", lmbs_to_add);
> +
> + if (lmbs_to_add == 0)
> + return -EINVAL;
> +
> + p = prop->value;
> + num_lmbs = be32_to_cpu(*p++);
> + lmbs = (struct of_drconf_cell *)p;
> +
> + /* Validate that there are enough LMBs to satisfy the request */
> + for (i = 0; i < num_lmbs; i++) {
> + if (!(be32_to_cpu(lmbs[i].flags) & DRCONF_MEM_ASSIGNED))
> + lmbs_available++;
> + }
> +
> + if (lmbs_available < lmbs_to_add)
> + return -EINVAL;
> +
I'm wondering why the next 3 lines when the if could be incorporated
into the for condition
for (i = 0; i < num_lmbs && lmbs_to_add != lmbs_added; i++) {
(or lmbs_to_add < lmbs_added)...
> + for (i = 0; i < num_lmbs; i++) {
> + if (lmbs_to_add == lmbs_added)
> + break;
> +
> + rc = dlpar_add_lmb(&lmbs[i]);
> + if (rc)
> + continue;
> +
> + lmbs_added++;
> + pr_info("Memory at %llx (drc index %x) has been hot-added\n",
> + be64_to_cpu(lmbs[i].base_addr),
> + be32_to_cpu(lmbs[i].drc_index));
This message feels a tad premature, you're going to roll back if the
requested amount couldn't be added which is good but then there's going
to be a confusing mix of 'hot-added' followed by 'hot-removed'. Could
this message be moved to when you clear the reserved fields?
> +
> + /* Mark this lmb so we can remove it later if all of the
> + * requested LMBs cannot be added.
> + */
> + lmbs[i].reserved = 1;
Writing 1 like that into a __be variable will upset sparse.
As a more general comment that it might be worth not passing around __be
structures and convert them all into CPU endian in one place so that all
these functions can do away with the endian conversion calls.
> + }
> +
> + if (lmbs_added != lmbs_to_add) {
> + /* TODO: remove added lmbs */
> + rc = -EINVAL;
> + }
> +
> + /* Clear the reserved fields */
> + for (i = 0; i < num_lmbs; i++)
> + lmbs[i].reserved = 0;
> +
> + return rc;
> +}
> +
> +static int dlpar_memory_add_by_index(struct pseries_hp_errorlog *hp_elog,
> + struct property *prop)
> +{
> + struct of_drconf_cell *lmbs;
> + uint32_t num_lmbs, drc_index;
> + __be32 *p;
> + int i, lmb_found;
> + int rc;
> +
> + drc_index = be32_to_cpu(hp_elog->_drc_u.drc_index);
Didn't you already do the endian conversion back in
handle_dlpar_errorlog?
> + pr_info("Attempting to hot-add LMB, drc index %x\n", drc_index);
> +
> + p = prop->value;
> + num_lmbs = be32_to_cpu(*p++);
> + lmbs = (struct of_drconf_cell *)p;
> +
> + lmb_found = 0;
> + for (i = 0; i < num_lmbs; i++) {
> + if (lmbs[i].drc_index == hp_elog->_drc_u.drc_index) {
Probably wanted to use your local variable drc_index here also
lmbs[i].drc_index is __be and I don't think you've converted it but the
other side of the condition has been...
> + lmb_found = 1;
> + rc = dlpar_add_lmb(&lmbs[i]);
> + break;
> + }
> + }
> +
> + if (!lmb_found)
> + rc = -EINVAL;
> +
> + if (rc)
> + pr_info("Failed to hot-add memory, drc index %x\n", drc_index);
> + else
> + pr_info("Memory at %llx (drc index %x) has been hot-added\n",
> + be64_to_cpu(lmbs[i].base_addr), drc_index);
> +
> + return rc;
> +}
> +
> int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
> {
> - int rc = 0;
> + struct device_node *dn;
> + struct property *prop;
> + int rc;
>
> lock_device_hotplug();
>
> + dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
> + if (!dn)
> + return -EINVAL;
> +
> + prop = dlpar_clone_drconf_property(dn);
> + if (!prop) {
> + of_node_put(dn);
> + return -EINVAL;
> + }
> +
> switch (hp_elog->action) {
> + case PSERIES_HP_ELOG_ACTION_ADD:
> + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
> + rc = dlpar_memory_add_by_count(hp_elog, prop);
> + else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
> + rc = dlpar_memory_add_by_index(hp_elog, prop);
> + else
> + rc = -EINVAL;
> + break;
> default:
> pr_err("Invalid action (%d) specified\n", hp_elog->action);
> rc = -EINVAL;
> break;
> }
>
> + if (rc)
> + dlpar_free_drconf_property(prop);
> + else {
> + rtas_hp_event = true;
> + of_update_property(dn, prop);
> + rtas_hp_event = false;
> + }
> +
> + of_node_put(dn);
> unlock_device_hotplug();
> return rc;
> }
> @@ -193,6 +432,9 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
> __be32 *p;
> int i, rc = -EINVAL;
>
> + if (rtas_hp_event)
> + return 0;
> +
> memblock_size = pseries_memory_block_size();
> if (!memblock_size)
> return -EINVAL;
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH v2 6/6] pseries: Implement memory hotplug remove in the kernel
From: Cyril Bur @ 2014-11-21 7:49 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <546A6EFD.2020001@linux.vnet.ibm.com>
On Mon, 2014-11-17 at 15:56 -0600, Nathan Fontenot wrote:
> Move handling of memory hotplug remove on pseries completely into the kernel.
>
> The current memory hotplug remove path involves the drmgr command doing part
> of this work in userspace and requesting the kernel to do additional pieces.
> This patch allows us to handle the act completely in the kernel via rtas
> hotplug events. This allows us to perform the operation faster and provide
> a common memory hotplug remove path for PowerVM and PowerKVM systems.
>
> Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/platforms/pseries/hotplug-memory.c | 206 ++++++++++++++++++++++-
> 1 file changed, 201 insertions(+), 5 deletions(-)
>
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index b57d42b..c8189e8 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -173,6 +173,179 @@ static int pseries_remove_mem_node(struct device_node *np)
> pseries_remove_memblock(base, lmb_size);
> return 0;
> }
> +
> +static int lmb_is_removable(struct of_drconf_cell *lmb)
> +{
> + int i, scns_per_block;
> + int rc = 1;
> + unsigned long pfn, block_sz;
> + u64 phys_addr;
> +
> + if (!(be32_to_cpu(lmb->flags) & DRCONF_MEM_ASSIGNED))
> + return -1;
This makes me kind of nervous. You're using the return value of
lmb_is_removable as a boolean but it returns three possible values -1,0
and 1. Functionally it looks correct to me so its not a massive issue
> +
> + phys_addr = be64_to_cpu(lmb->base_addr);
> + block_sz = memory_block_size_bytes();
> + scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
> +
> + for (i = 0; i < scns_per_block; i++) {
> + pfn = PFN_DOWN(phys_addr);
> + if (!pfn_present(pfn))
> + continue;
> +
> + rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
> + phys_addr += MIN_MEMORY_BLOCK_SIZE;
> + }
> +
> + return rc;
> +}
> +
> +static int dlpar_add_lmb(struct of_drconf_cell *);
> +
> +static int dlpar_remove_lmb(struct of_drconf_cell *lmb)
> +{
> + struct memory_block *mem_block;
> + unsigned long block_sz;
> + u64 phys_addr;
> + uint32_t drc_index;
> + int nid, rc;
> +
> + if (!lmb_is_removable(lmb))
> + return -EINVAL;
> +
> + phys_addr = be64_to_cpu(lmb->base_addr);
> + drc_index = be32_to_cpu(lmb->drc_index);
> +
> + mem_block = lmb_to_memblock(lmb);
> + if (!mem_block)
> + return -EINVAL;
> +
> + rc = device_offline(&mem_block->dev);
> + put_device(&mem_block->dev);
> + if (rc)
> + return rc;
> +
> + block_sz = pseries_memory_block_size();
> + nid = memory_add_physaddr_to_nid(phys_addr);
> +
> + remove_memory(nid, phys_addr, block_sz);
> +
> + /* Update memory regions for memory remove */
> + memblock_remove(phys_addr, block_sz);
> +
> + dlpar_release_drc(drc_index);
> +
> + lmb->flags &= cpu_to_be32(~DRCONF_MEM_ASSIGNED);
> + pr_info("Memory at %llx (drc index %x) has been hot-removed\n",
> + be64_to_cpu(lmb->base_addr), drc_index);
dlpar_add_lmb doesn't print anything but dlpar_remove_lmb does? Related
to my comment about printing a 'hot-add' messages prematurely, perhaps
move this to the callers
> +
> + return 0;
> +}
> +
> +static int dlpar_memory_remove_by_count(struct pseries_hp_errorlog *hp_elog,
> + struct property *prop)
> +{
> + struct of_drconf_cell *lmbs;
> + int lmbs_to_remove, lmbs_removed = 0;
> + int lmbs_available = 0;
> + uint32_t num_lmbs;
> + __be32 *p;
> + int i, rc;
> +
> + lmbs_to_remove = be32_to_cpu(hp_elog->_drc_u.drc_count);
Didn't you already do the endian conversion back in
handle_dlpar_errorlog?
> + pr_info("Attempting to hot-remove %d LMB(s)\n", lmbs_to_remove);
> +
> + if (lmbs_to_remove == 0)
> + return -EINVAL;
> +
> + p = prop->value;
> + num_lmbs = be32_to_cpu(*p++);
> + lmbs = (struct of_drconf_cell *)p;
> +
> + /* Validate that there are enough LMBs to satisfy the request */
> + for (i = 0; i < num_lmbs; i++) {
> + if (be32_to_cpu(lmbs[i].flags) & DRCONF_MEM_ASSIGNED)
> + lmbs_available++;
> + }
> +
> + if (lmbs_available < lmbs_to_remove)
> + return -EINVAL;
> +
> + for (i = 0; i < num_lmbs; i++) {
> + if (lmbs_to_remove == lmbs_removed)
> + break;
> +
> + rc = dlpar_remove_lmb(&lmbs[i]);
> + if (rc)
> + continue;
> +
> + lmbs_removed++;
> +
> + /* Mark this lmb so we can add it later if all of the
> + * requested LMBs cannot be removed.
> + */
> + lmbs[i].reserved = 1;
> + }
> +
> + if (lmbs_removed != lmbs_to_remove) {
> + pr_err("Memory hot-remove failed, adding LMB's back\n");
> +
> + for (i = 0; i < num_lmbs; i++) {
> + if (!lmbs[i].reserved)
> + continue;
> +
> + rc = dlpar_add_lmb(&lmbs[i]);
> + if (rc)
If this happens you this will leave an LMB removed but an error code
from this function will cause dlpar_memory to discard the dn prop that
it passed in. If you couldn't roll back completely the caller should
still update the dn property with the ones that it has left
removed/failed to re-add. Perhaps this function needs a mechanism to
report success/failure (as it currently does) and a mechanism to say
whether or not the dn property is dirty or not.
> + pr_err("Failed to add LMB back, drc index %x\n",
> + be32_to_cpu(lmbs[i].drc_index));
> +
On a clean (everything rolled back successfully) failure, I don't think
you _need_ to bother although this is nice cleanup, you didn't do it
dlpar_memory_add_by_count - I'm in favour of being clean especially
since on an 'unclean' failure you should clear that flag - if you're
going to still update the device tree.
> + lmbs[i].reserved = 0;
> + }
> + rc = -EINVAL;
> + } else {
> + /* remove any reserved markings */
> + for (i = 0; i < num_lmbs; i++)
> + lmbs[i].reserved = 0;
> + }
Hmmmm since the if statements are checking for failures. It might be
more clear to return -EINVAL (rather than setting rc) and the code in
the else block can be moved out of it and come to think of it, return 0.
Otherwise it looks odd to me with the 'normal' success case code being
in an else statement.
> +
> + return rc;
> +}
> +
> +static int dlpar_memory_remove_by_index(struct pseries_hp_errorlog *hp_elog,
> + struct property *prop)
> +{
> + struct of_drconf_cell *lmbs;
> + uint32_t num_lmbs, drc_index;
> + int lmb_found;
> + __be32 *p;
> + int i, rc;
> +
> + drc_index = be32_to_cpu(hp_elog->_drc_u.drc_index);
Didn't you already do the endian conversion back in
handle_dlpar_errorlog?
> + pr_info("Attempting to hot-remove LMB, drc index %x\n", drc_index);
> +
> + p = prop->value;
> + num_lmbs = be32_to_cpu(*p++);
> + lmbs = (struct of_drconf_cell *)p;
> +
> + lmb_found = 0;
> + for (i = 0; i < num_lmbs; i++) {
> + if (lmbs[i].drc_index == hp_elog->_drc_u.drc_index) {
> + lmb_found = 1;
> + rc = dlpar_remove_lmb(&lmbs[i]);
> + break;
> + }
> + }
> +
> + if (!lmb_found)
> + rc = -EINVAL;
> +
> + if (rc)
> + pr_info("Failed to hot-remove memory, drc index %x\n",
> + drc_index);
> +
> + return rc;
> +}
> +
> #else
> static inline int pseries_remove_memblock(unsigned long base,
> unsigned int memblock_size)
> @@ -183,6 +356,11 @@ static inline int pseries_remove_mem_node(struct device_node *np)
> {
> return 0;
> }
> +static inline int dlpar_memory_remove(struct pseries_hp_errorlog *hp_elog)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> #endif /* CONFIG_MEMORY_HOTREMOVE */
>
> static int dlpar_add_lmb(struct of_drconf_cell *lmb)
> @@ -298,14 +476,24 @@ static int dlpar_memory_add_by_count(struct pseries_hp_errorlog *hp_elog,
> }
>
> if (lmbs_added != lmbs_to_add) {
> - /* TODO: remove added lmbs */
> + pr_err("Memory hot-add failed, removing any added LMBs\n");
> +
> + for (i = 0; i < num_lmbs; i++) {
> + if (!lmbs[i].reserved)
> + continue;
> +
> + rc = dlpar_remove_lmb(&lmbs[i]);
> + if (rc)
There is a very much related comment in dlpar_memory_remove_by_count
about this condition being true.
> + pr_err("Failed to remove LMB, drc index %x\n",
> + be32_to_cpu(lmbs[i].drc_index));
You don't clear the reserved flag here?
> + }
> rc = -EINVAL;
Same observation as in dlpar_memory_remove_by_count
> + } else {
> + /* Clear the reserved fields */
> + for (i = 0; i < num_lmbs; i++)
> + lmbs[i].reserved = 0;
> }
>
> - /* Clear the reserved fields */
> - for (i = 0; i < num_lmbs; i++)
> - lmbs[i].reserved = 0;
> -
> return rc;
> }
>
> @@ -373,6 +561,14 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
> else
> rc = -EINVAL;
> break;
> + case PSERIES_HP_ELOG_ACTION_REMOVE:
> + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT)
> + rc = dlpar_memory_remove_by_count(hp_elog, prop);
> + else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX)
> + rc = dlpar_memory_remove_by_index(hp_elog, prop);
> + else
> + rc = -EINVAL;
> + break;
> default:
> pr_err("Invalid action (%d) specified\n", hp_elog->action);
> rc = -EINVAL;
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH v2 3/6] pseries: Create new device hotplug entry point
From: Cyril Bur @ 2014-11-21 7:49 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <546A6DEE.8080708@linux.vnet.ibm.com>
On Mon, 2014-11-17 at 15:51 -0600, Nathan Fontenot wrote:
> Create a new entry point for device hotplug on pseries that will
> work for both PowerVM and PowerKVM systems.
>
> The current process to hotplug (or dlpar) devices (generally the same
> process for memory, cpu, and pci devices) on PowerVM systems is initiated
> from the HMC, which communicates the request to the partitions through
> the RSCT framework. The RSCT framework then invokes the drmgr command.
> The drmgr command performs the hotplug operation by doing some pieces,
> such as most of the rtas calls and device tree parsing, in userspace
> and make requests to the kernel to online/offline the device, update the
> device tree and add/remove the device.
>
> For PowerKVM the approach for device hotplug is to follow what is currently
> being done for pci hotplug. A hotplug request is initiated from the host,
> QEMU then generates an EPOW interrupt to the guest which causes the guest
> to make the rtas,check-exception call. In QEMU, the rtas,check-exception call
> returns a rtas hotplug event to the guest.
Please forgive my ignorance of the exact details of how this all works.
I've been trying to wrap my head around how it works in a little more
detail than your high level overview. Correct me where I go wrong.
So the EPOW interrupt comes in and QEMU receives the
rtas,check-exception call and returns the rtas hotplug event. As you
state below, the connection of the arrival of the rtas hotplug event to
the hotplug code will be made in a subsequent patch.
Here is my understanding of what happens when a hotplug event gets
processed.
An LMB is selected
from /ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory in the
device tree which is populated at boot time (although it's possible that
it can updated once the guest is running?) either because it matches a
specific drc-index or simply because it is in the list and we are to
hotplug 'count' LMBs. So there's a maximum amount of memory that can be
hotplugged (without device tree updates...)?
Once selected the kernel informs the hypervisor that it is going to use
that LMB with two RTAS calls, 'get-sensor-state' and 'set-indicator'
which first checks the actual state of that LMB with the hypervisor and
then marks it as 'in use' (in dlpar_acquire_drc).
After that, find the (NUMA?) node id of the memory and inform the
generic kernel about this new memory.
For some reason memblock needs to be informed separately (does it need
to be informed exactly?), which as you pointed out in the previous
version of this patchset you're not sure why it isn't done in
add_memory, and you still do it because it's what currently happens? I'd
very much like to know why this sequence of events but I suspect the
explanation might get quite involved.
Finally mark the LMB as assigned in its device tree node. This is
bookkeeping right?
This process is repeated for each LMB that should be added.
In the event a failure a best effort rollback is done, as you mentioned
in the previous version, it may not always be possible but at least it's
attempted.
Once all this succeeds update the device tree.
Basically the exact reverse of this process happens for unplug.
I do have questions about this process: What is the most likely part to
fail? I have no idea how feasible it would be but perhaps trying to do
the likely failure on all the LBMs might help unwinding and perhaps
provide a guarantee that it can be completely rolled back. I'm really
not sure but by the looks of things are going to be pretty reversible up
until device_online.
I can't help but notice the duplication with memory_probe_store
(although that doesn't do any rollback). Probably unavoidable and its
really not much code.
Thanks in advance for the clarifications,
Cyril
>
> Please note that the current pci hotplug path for PowerKVM involves the
> kernel receiving the rtas hotplug event, passing it to rtas_errd in
> userspace, and having rtas_errd invoke drmgr. The drmgr command then
> handles the request as described above for PowerVM systems. This is to
> be updated to perform pci completely in the kernel in a later patch set.
>
> There is no need for this circuitous route, we should handle the entire
> hotplug of devices in the kernel. What I am planning is to enable this
> by moving the code to handle device hotplug from drmgr into the kernel to
> provide a single path for both PowerVM and PowerKVM systems. This patch
> provides the common entry point. For PowerKVM a future update to the kernel
> rtas code will recognize rtas hotplug events returned from
> rtas,check-exception calls and use the common entry point to handle device
> hotplug entirely in the kernel.
>
> For PowerVM systems, this patch creates the /sys/kernel/dlpar file that rtas
> hotplug events can be written to by drmgr and passed to the common entry point.
> There is no chance of updating how we receive hotplug requests on PowerVM
> systems.
>
> Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/platforms/pseries/dlpar.c | 72 ++++++++++++++++++++++-
> arch/powerpc/platforms/pseries/hotplug-memory.c | 19 ++++++
> arch/powerpc/platforms/pseries/pseries.h | 10 +++
> 3 files changed, 99 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
> index c22bb1b..ec825d3 100644
> --- a/arch/powerpc/platforms/pseries/dlpar.c
> +++ b/arch/powerpc/platforms/pseries/dlpar.c
> @@ -10,6 +10,8 @@
> * 2 as published by the Free Software Foundation.
> */
>
> +#define pr_fmt(fmt) "dlpar: " fmt
> +
> #include <linux/kernel.h>
> #include <linux/notifier.h>
> #include <linux/spinlock.h>
> @@ -535,13 +537,79 @@ static ssize_t dlpar_cpu_release(const char *buf, size_t count)
> return count;
> }
>
> +#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
> +
> +static int handle_dlpar_errorlog(struct rtas_error_log *error_log)
> +{
> + struct pseries_errorlog *pseries_log;
> + struct pseries_hp_errorlog *hp_elog;
> + int rc;
> +
> + pseries_log = get_pseries_errorlog(error_log,
> + PSERIES_ELOG_SECT_ID_HOTPLUG);
> + if (!pseries_log || (pseries_log->length == 0))
> + return -EINVAL;
> +
> + hp_elog = (struct pseries_hp_errorlog *)pseries_log->data;
Maybe rather than doing this here it might be worth having a function
that returns you a pseries_hp_errorlog from a pseries_errorlog but also
with everything in CPU endian which will make 5/6 and 6/6 look nicer.
> +
> + /* Go ahead and convert the hotplug type to the correct endianness
> + * to avoid converting it everywhere we use it.
> + */
> + switch (hp_elog->id_type) {
> + case PSERIES_HP_ELOG_ID_DRC_COUNT:
> + hp_elog->_drc_u.drc_count =
> + be32_to_cpu(hp_elog->_drc_u.drc_count);
> + case PSERIES_HP_ELOG_ID_DRC_INDEX:
> + hp_elog->_drc_u.drc_index =
> + be32_to_cpu(hp_elog->_drc_u.drc_index);
> + }
You're converting __be32 to CPU endian but storing them back into a
__be32? This will upset sparse.
Should this not also have a default case?
> +
> + switch (hp_elog->resource) {
> + case PSERIES_HP_ELOG_RESOURCE_MEM:
> + rc = dlpar_memory(hp_elog);
> + break;
> + default:
> + pr_warn_ratelimited("Invalid resource (%d) specified\n",
> + hp_elog->resource);
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +static ssize_t dlpar_store(struct file *filp, struct kobject *kobj,
> + struct bin_attribute *bin_attr, char *buf,
> + loff_t pos, size_t count)
> +{
> + struct rtas_error_log *error_log;
> + int rc;
> +
> + error_log = kmalloc(count, GFP_KERNEL);
> + if (!error_log)
> + return -ENOMEM;
> +
> + memcpy(error_log, buf, count);
> +
> + rc = handle_dlpar_errorlog(error_log);
> + kfree(error_log);
> + return rc ? rc : count;
> +}
> +
> +static BIN_ATTR(dlpar, S_IWUSR, NULL, dlpar_store, 0);
> +
> static int __init pseries_dlpar_init(void)
> {
> + int rc;
> +
> +#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
> ppc_md.cpu_probe = dlpar_cpu_probe;
> ppc_md.cpu_release = dlpar_cpu_release;
> +#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
>
> - return 0;
> + rc = sysfs_create_bin_file(kernel_kobj, &bin_attr_dlpar);
> +
> + return rc;
> }
> machine_device_initcall(pseries, pseries_dlpar_init);
>
> -#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
> index 3cb256c..69d178b 100644
> --- a/arch/powerpc/platforms/pseries/hotplug-memory.c
> +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
> @@ -9,6 +9,8 @@
> * 2 of the License, or (at your option) any later version.
> */
>
> +#define pr_fmt(fmt) "pseries-hotplug-mem: " fmt
> +
> #include <linux/of.h>
> #include <linux/of_address.h>
> #include <linux/memblock.h>
> @@ -134,6 +136,23 @@ static inline int pseries_remove_mem_node(struct device_node *np)
> }
> #endif /* CONFIG_MEMORY_HOTREMOVE */
>
> +int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
> +{
> + int rc = 0;
> +
> + lock_device_hotplug();
> +
> + switch (hp_elog->action) {
> + default:
> + pr_err("Invalid action (%d) specified\n", hp_elog->action);
> + rc = -EINVAL;
> + break;
> + }
> +
> + unlock_device_hotplug();
> + return rc;
> +}
> +
> static int pseries_add_mem_node(struct device_node *np)
> {
> const char *type;
> diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
> index 239bee5..40e0339 100644
> --- a/arch/powerpc/platforms/pseries/pseries.h
> +++ b/arch/powerpc/platforms/pseries/pseries.h
> @@ -11,6 +11,7 @@
> #define _PSERIES_PSERIES_H
>
> #include <linux/interrupt.h>
> +#include <asm/rtas.h>
>
> struct device_node;
>
> @@ -63,6 +64,15 @@ extern int dlpar_detach_node(struct device_node *);
> int dlpar_acquire_drc(u32 drc_index);
> int dlpar_release_drc(u32 drc_index);
>
> +#ifdef CONFIG_MEMORY_HOTPLUG
> +int dlpar_memory(struct pseries_hp_errorlog *hp_elog);
> +#else
> +static inline int dlpar_memory(struct pseries_hp_errorlog *hp_elog)
> +{
> + return -EOPNOTSUPP;
> +}
> +#endif
> +
> /* PCI root bridge prepare function override for pseries */
> struct pci_host_bridge;
> int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: [PATCH v2 1/2] cpufreq: qoriq: Make the driver usable on all QorIQ platforms
From: Viresh Kumar @ 2014-11-21 7:54 UTC (permalink / raw)
To: Tang Yuantian
Cc: r64188, linuxppc-dev@ozlabs.org, Rafael J. Wysocki,
Linux Kernel Mailing List, linux-pm@vger.kernel.org
In-Reply-To: <1416554882-16566-1-git-send-email-Yuantian.Tang@freescale.com>
On 21 November 2014 12:58, Tang Yuantian <Yuantian.Tang@freescale.com> wrote:
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index 83a75dc..1925ae94 100644
> --- a/drivers/cpufreq/Kconfig.arm
> +++ b/drivers/cpufreq/Kconfig.arm
> @@ -247,3 +247,11 @@ config ARM_TEGRA_CPUFREQ
> default y
> help
> This adds the CPUFreq driver support for TEGRA SOCs.
> +
> +config QORIQ_CPUFREQ
> + tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
> + depends on OF && COMMON_CLK
> + select CLK_PPC_CORENET
> + help
> + This adds the CPUFreq driver support for Freescale QorIQ SoCs
> + which are capable of changing the CPU's frequency dynamically.
> diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
> index 72564b7..3a34248 100644
> --- a/drivers/cpufreq/Kconfig.powerpc
> +++ b/drivers/cpufreq/Kconfig.powerpc
> @@ -23,14 +23,13 @@ config CPU_FREQ_MAPLE
> This adds support for frequency switching on Maple 970FX
> Evaluation Board and compatible boards (IBM JS2x blades).
>
> -config PPC_CORENET_CPUFREQ
> - tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
> - depends on PPC_E500MC && OF && COMMON_CLK
> +config QORIQ_CPUFREQ
> + tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
> + depends on OF && COMMON_CLK
> select CLK_PPC_CORENET
> help
> - This adds the CPUFreq driver support for Freescale e500mc,
> - e5500 and e6500 series SoCs which are capable of changing
> - the CPU's frequency dynamically.
> + This adds the CPUFreq driver support for Freescale QorIQ SoCs
> + which are capable of changing the CPU's frequency dynamically.
>
> config CPU_FREQ_PMAC
> bool "Support for Apple PowerBooks"
Now that following patch is pushed into linux-next:
f41f481 cpufreq: Kconfig: Remove architecture specific menu entries
you can move above config entries directly to drivers/cpufreq/Kconfig
instead of replicating them.
^ permalink raw reply
* Re: [PATCH 2/2] cpufreq: qoriq: rename the driver
From: Viresh Kumar @ 2014-11-21 7:56 UTC (permalink / raw)
To: Tang Yuantian
Cc: r64188, linuxppc-dev@ozlabs.org, Rafael J. Wysocki,
Linux Kernel Mailing List, linux-pm@vger.kernel.org
In-Reply-To: <1416554882-16566-2-git-send-email-Yuantian.Tang@freescale.com>
On 21 November 2014 12:58, Tang Yuantian <Yuantian.Tang@freescale.com> wrote:
> This driver works on all QorIQ platforms which include
> ARM-based cores and PPC-based cores.
> Rename it in order to represent better.
>
> Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
> ---
> drivers/cpufreq/ppc-corenet-cpufreq.c | 371 ----------------------------------
> drivers/cpufreq/qoriq-cpufreq.c | 371 ++++++++++++++++++++++++++++++++++
> 2 files changed, 371 insertions(+), 371 deletions(-)
> delete mode 100644 drivers/cpufreq/ppc-corenet-cpufreq.c
> create mode 100644 drivers/cpufreq/qoriq-cpufreq.c
This is not reviewable at all.. How do I verify that you haven't changed
something in the files as well ?
To make life easy always create patches with:
git format-patch -C -M
Above options handle renames pretty well.
^ permalink raw reply
* RE: [PATCH 2/2] cpufreq: qoriq: rename the driver
From: Yuantian Tang @ 2014-11-21 9:16 UTC (permalink / raw)
To: Viresh Kumar
Cc: linuxppc-dev@ozlabs.org, Rafael J. Wysocki,
Linux Kernel Mailing List, Jason.Jin@freescale.com,
linux-pm@vger.kernel.org
In-Reply-To: <CAKohpo=xLOxXrCJNO0LmrVxMDNsFy2Kxg6bNzHm=vQCJnBvSaw@mail.gmail.com>
SGVsbG8gVmlyZXNoLA0KDQpXaGVuIEkgdXNlIC1DIC1NIG9wdGlvbnMsIGNoZWNrcGF0Y2ggcmVw
b3J0cyBlcnJvcjoNCg0KW3Rhbmd5dEB0aXRhbiBsaW51eC1wbV0kIC4vc2NyaXB0cy9jaGVja3Bh
dGNoLnBsIDAwMDItY3B1ZnJlcS1xb3JpcS1yZW5hbWUtdGhlLWRyaXZlci5wYXRjaCANCldBUk5J
Tkc6IGFkZGVkLCBtb3ZlZCBvciBkZWxldGVkIGZpbGUocyksIGRvZXMgTUFJTlRBSU5FUlMgbmVl
ZCB1cGRhdGluZz8NCiMxNTogDQogZHJpdmVycy9jcHVmcmVxL3twcGMtY29yZW5ldC1jcHVmcmVx
LmMgPT4gcW9yaXEtY3B1ZnJlcS5jfSB8IDANCg0KRVJST1I6IERvZXMgbm90IGFwcGVhciB0byBi
ZSBhIHVuaWZpZWQtZGlmZiBmb3JtYXQgcGF0Y2gNCg0KdG90YWw6IDEgZXJyb3JzLCAxIHdhcm5p
bmdzLCAwIGxpbmVzIGNoZWNrZWQNCg0KMDAwMi1jcHVmcmVxLXFvcmlxLXJlbmFtZS10aGUtZHJp
dmVyLnBhdGNoIGhhcyBzdHlsZSBwcm9ibGVtcywgcGxlYXNlIHJldmlldy4NCg0KSSBqdXN0IHJl
bmFtZSBpdCBhbmQgZG8gbm90IGNoYW5nZSBhbnl0aW5nLiBJc24ndCB0aGF0IGFsbG93ZWQ/DQoN
ClRoYW5rcywNCll1YW50aWFuDQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJv
bTogVmlyZXNoIEt1bWFyIFttYWlsdG86dmlyZXNoLmt1bWFyQGxpbmFyby5vcmddDQo+IFNlbnQ6
IEZyaWRheSwgTm92ZW1iZXIgMjEsIDIwMTQgMzo1NiBQTQ0KPiBUbzogVGFuZyBZdWFudGlhbi1C
Mjk5ODMNCj4gQ2M6IFJhZmFlbCBKLiBXeXNvY2tpOyBMaW51eCBLZXJuZWwgTWFpbGluZyBMaXN0
OyBsaW51eC1wbUB2Z2VyLmtlcm5lbC5vcmc7DQo+IGxpbnV4cHBjLWRldkBvemxhYnMub3JnOyBK
aW4gWmhlbmd4aW9uZy1SNjQxODgNCj4gU3ViamVjdDogUmU6IFtQQVRDSCAyLzJdIGNwdWZyZXE6
IHFvcmlxOiByZW5hbWUgdGhlIGRyaXZlcg0KPiANCj4gT24gMjEgTm92ZW1iZXIgMjAxNCAxMjo1
OCwgVGFuZyBZdWFudGlhbiA8WXVhbnRpYW4uVGFuZ0BmcmVlc2NhbGUuY29tPg0KPiB3cm90ZToN
Cj4gPiBUaGlzIGRyaXZlciB3b3JrcyBvbiBhbGwgUW9ySVEgcGxhdGZvcm1zIHdoaWNoIGluY2x1
ZGUgQVJNLWJhc2VkIGNvcmVzDQo+ID4gYW5kIFBQQy1iYXNlZCBjb3Jlcy4NCj4gPiBSZW5hbWUg
aXQgaW4gb3JkZXIgdG8gcmVwcmVzZW50IGJldHRlci4NCj4gPg0KPiA+IFNpZ25lZC1vZmYtYnk6
IFRhbmcgWXVhbnRpYW4gPFl1YW50aWFuLlRhbmdAZnJlZXNjYWxlLmNvbT4NCj4gPiAtLS0NCj4g
PiAgZHJpdmVycy9jcHVmcmVxL3BwYy1jb3JlbmV0LWNwdWZyZXEuYyB8IDM3MSAtLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQo+ID4gIGRyaXZlcnMvY3B1ZnJlcS9xb3JpcS1jcHVm
cmVxLmMgICAgICAgfCAzNzENCj4gKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKw0K
PiA+ICAyIGZpbGVzIGNoYW5nZWQsIDM3MSBpbnNlcnRpb25zKCspLCAzNzEgZGVsZXRpb25zKC0p
ICBkZWxldGUgbW9kZQ0KPiA+IDEwMDY0NCBkcml2ZXJzL2NwdWZyZXEvcHBjLWNvcmVuZXQtY3B1
ZnJlcS5jDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL2NwdWZyZXEvcW9yaXEtY3B1
ZnJlcS5jDQo+IA0KPiBUaGlzIGlzIG5vdCByZXZpZXdhYmxlIGF0IGFsbC4uIEhvdyBkbyBJIHZl
cmlmeSB0aGF0IHlvdSBoYXZlbid0IGNoYW5nZWQgc29tZXRoaW5nDQo+IGluIHRoZSBmaWxlcyBh
cyB3ZWxsID8NCj4gDQo+IFRvIG1ha2UgbGlmZSBlYXN5IGFsd2F5cyBjcmVhdGUgcGF0Y2hlcyB3
aXRoOg0KPiANCj4gZ2l0IGZvcm1hdC1wYXRjaCAtQyAtTQ0KPiANCj4gQWJvdmUgb3B0aW9ucyBo
YW5kbGUgcmVuYW1lcyBwcmV0dHkgd2VsbC4NCg==
^ permalink raw reply
* Re: [PATCH 2/2] cpufreq: qoriq: rename the driver
From: Viresh Kumar @ 2014-11-21 9:17 UTC (permalink / raw)
To: Yuantian Tang
Cc: linuxppc-dev@ozlabs.org, Rafael J. Wysocki,
Linux Kernel Mailing List, Jason.Jin@freescale.com,
linux-pm@vger.kernel.org
In-Reply-To: <dedc11e3150d44ee89bb1e96b5447687@DM2PR03MB574.namprd03.prod.outlook.com>
On 21 November 2014 14:46, Yuantian Tang <Yuantian.Tang@freescale.com> wrote:
> When I use -C -M options, checkpatch reports error:
>
> [tangyt@titan linux-pm]$ ./scripts/checkpatch.pl 0002-cpufreq-qoriq-rename-the-driver.patch
> WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
> #15:
> drivers/cpufreq/{ppc-corenet-cpufreq.c => qoriq-cpufreq.c} | 0
>
> ERROR: Does not appear to be a unified-diff format patch
>
> total: 1 errors, 1 warnings, 0 lines checked
>
> 0002-cpufreq-qoriq-rename-the-driver.patch has style problems, please review.
>
> I just rename it and do not change anyting. Isn't that allowed?
Ignore these warnings.
^ permalink raw reply
* [PATCH v3 1/2] cpufreq: qoriq: Make the driver usable on all QorIQ platforms
From: Tang Yuantian @ 2014-11-21 9:18 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: r64188, linuxppc-dev, Tang Yuantian, linux-kernel, linux-pm
Freescale introduced new ARM core-based SoCs which support dynamic
frequency switch feature. DFS on new SoCs are compatible with current
PowerPC CoreNet platforms. In order to support those new platforms,
this driver needs to be updated. The main changes include:
1. Changed the names of functions in driver.
2. Added two new functions get_cpu_physical_id() and get_bus_freq().
3. Used a new way to get the CPU mask which share clock wire.
Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
---
v3:
- put the menu entries into Kconfig
v2:
- split the name change into a separete patch
- use policy->driver_data instead of per_cpu variable
drivers/cpufreq/Kconfig | 8 ++
drivers/cpufreq/Kconfig.powerpc | 9 --
drivers/cpufreq/ppc-corenet-cpufreq.c | 160 +++++++++++++++++++++-------------
3 files changed, 107 insertions(+), 70 deletions(-)
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 29b2ef5..fe0aab8 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -292,5 +292,13 @@ config SH_CPU_FREQ
If unsure, say N.
endif
+config QORIQ_CPUFREQ
+ tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
+ depends on OF && COMMON_CLK && (PPC || ARM)
+ select CLK_PPC_CORENET
+ help
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
+
endif
endmenu
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index 72564b7..3a0595b 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -23,15 +23,6 @@ config CPU_FREQ_MAPLE
This adds support for frequency switching on Maple 970FX
Evaluation Board and compatible boards (IBM JS2x blades).
-config PPC_CORENET_CPUFREQ
- tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
- depends on PPC_E500MC && OF && COMMON_CLK
- select CLK_PPC_CORENET
- help
- This adds the CPUFreq driver support for Freescale e500mc,
- e5500 and e6500 series SoCs which are capable of changing
- the CPU's frequency dynamically.
-
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
depends on ADB_PMU && PPC32
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c
index bee5df7..949d992 100644
--- a/drivers/cpufreq/ppc-corenet-cpufreq.c
+++ b/drivers/cpufreq/ppc-corenet-cpufreq.c
@@ -1,7 +1,7 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
- * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs.
+ * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -20,10 +20,9 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/smp.h>
-#include <sysdev/fsl_soc.h>
/**
- * struct cpu_data - per CPU data struct
+ * struct cpu_data
* @parent: the parent node of cpu clock
* @table: frequency table
*/
@@ -67,17 +66,78 @@ static const struct soc_data sdata[] = {
static u32 min_cpufreq;
static const u32 *fmask;
-static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
+#if defined(CONFIG_ARM)
+static int get_cpu_physical_id(int cpu)
+{
+ return topology_core_id(cpu);
+}
+#else
+static int get_cpu_physical_id(int cpu)
+{
+ return get_hard_smp_processor_id(cpu);
+}
+#endif
-/* cpumask in a cluster */
-static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
+static u32 get_bus_freq(void)
+{
+ struct device_node *soc;
+ u32 sysfreq;
+
+ soc = of_find_node_by_type(NULL, "soc");
+ if (!soc)
+ return 0;
+
+ if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
+ sysfreq = 0;
+
+ of_node_put(soc);
+
+ return sysfreq;
+}
-#ifndef CONFIG_SMP
-static inline const struct cpumask *cpu_core_mask(int cpu)
+static struct device_node *cpu_to_clk_node(int cpu)
{
- return cpumask_of(0);
+ struct device_node *np, *clk_np;
+
+ if (!cpu_present(cpu))
+ return NULL;
+
+ np = of_get_cpu_node(cpu, NULL);
+ if (!np)
+ return NULL;
+
+ clk_np = of_parse_phandle(np, "clocks", 0);
+ if (!clk_np)
+ return NULL;
+
+ of_node_put(np);
+
+ return clk_np;
+}
+
+/* traverse cpu nodes to get cpu mask of sharing clock wire */
+static void set_affected_cpus(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *clk_np;
+ struct cpumask *dstp = policy->cpus;
+ int i;
+
+ np = cpu_to_clk_node(policy->cpu);
+ if (!np)
+ return;
+
+ for_each_present_cpu(i) {
+ clk_np = cpu_to_clk_node(i);
+ if (!clk_np)
+ continue;
+
+ if (clk_np == np)
+ cpumask_set_cpu(i, dstp);
+
+ of_node_put(clk_np);
+ }
+ of_node_put(np);
}
-#endif
/* reduce the duplicated frequencies in frequency table */
static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
@@ -105,6 +165,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
int i, j, ind;
unsigned int freq, max_freq;
struct cpufreq_frequency_table table;
+
for (i = 0; i < count - 1; i++) {
max_freq = freq_table[i].frequency;
ind = i;
@@ -129,7 +190,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
}
}
-static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
+static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct device_node *np;
int i, count, ret;
@@ -145,10 +206,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- pr_err("%s: no memory\n", __func__);
+ if (!data)
goto err_np;
- }
policy->clk = of_clk_get(np, 0);
if (IS_ERR(policy->clk)) {
@@ -170,7 +229,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
}
if (fmask)
- mask = fmask[get_hard_smp_processor_id(cpu)];
+ mask = fmask[get_cpu_physical_id(cpu)];
else
mask = 0x0;
@@ -201,13 +260,12 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
data->table = table;
/* update ->cpus if we have cluster, no harm if not */
- cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
- for_each_cpu(i, per_cpu(cpu_mask, cpu))
- per_cpu(cpu_data, i) = data;
+ set_affected_cpus(policy);
+ policy->driver_data = data;
/* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC;
- do_div(u64temp, fsl_get_sys_freq());
+ do_div(u64temp, get_bus_freq());
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np);
@@ -219,7 +277,7 @@ err_nomem1:
err_node:
of_node_put(data->parent);
err_nomem2:
- per_cpu(cpu_data, cpu) = NULL;
+ policy->driver_data = NULL;
kfree(data);
err_np:
of_node_put(np);
@@ -227,43 +285,41 @@ err_np:
return -ENODEV;
}
-static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
- struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
+ struct cpu_data *data = policy->driver_data;
unsigned int cpu;
of_node_put(data->parent);
kfree(data->table);
kfree(data);
-
- for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
- per_cpu(cpu_data, cpu) = NULL;
+ policy->driver_data = NULL;
return 0;
}
-static int corenet_cpufreq_target(struct cpufreq_policy *policy,
+static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
unsigned int index)
{
struct clk *parent;
- struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
+ struct cpu_data *data = policy->driver_data;
parent = of_clk_get(data->parent, data->table[index].driver_data);
return clk_set_parent(policy->clk, parent);
}
-static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
- .name = "ppc_cpufreq",
+static struct cpufreq_driver qoriq_cpufreq_driver = {
+ .name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS,
- .init = corenet_cpufreq_cpu_init,
- .exit = __exit_p(corenet_cpufreq_cpu_exit),
+ .init = qoriq_cpufreq_cpu_init,
+ .exit = __exit_p(qoriq_cpufreq_cpu_exit),
.verify = cpufreq_generic_frequency_table_verify,
- .target_index = corenet_cpufreq_target,
+ .target_index = qoriq_cpufreq_target,
.get = cpufreq_generic_get,
.attr = cpufreq_generic_attr,
};
-static const struct of_device_id node_matches[] __initdata = {
+static const struct of_device_id node_matches[] __initconst = {
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
@@ -273,61 +329,43 @@ static const struct of_device_id node_matches[] __initdata = {
{}
};
-static int __init ppc_corenet_cpufreq_init(void)
+static int __init qoriq_cpufreq_init(void)
{
int ret;
struct device_node *np;
const struct of_device_id *match;
const struct soc_data *data;
- unsigned int cpu;
np = of_find_matching_node(NULL, node_matches);
if (!np)
return -ENODEV;
- for_each_possible_cpu(cpu) {
- if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
- goto err_mask;
- cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
- }
-
match = of_match_node(node_matches, np);
data = match->data;
if (data) {
if (data->flag)
fmask = data->freq_mask;
- min_cpufreq = fsl_get_sys_freq();
+ min_cpufreq = get_bus_freq();
} else {
- min_cpufreq = fsl_get_sys_freq() / 2;
+ min_cpufreq = get_bus_freq() / 2;
}
of_node_put(np);
- ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
+ ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret)
- pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
+ pr_info("Freescale QorIQ CPU frequency scaling driver\n");
return ret;
-
-err_mask:
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- return -ENOMEM;
}
-module_init(ppc_corenet_cpufreq_init);
+module_init(qoriq_cpufreq_init);
-static void __exit ppc_corenet_cpufreq_exit(void)
+static void __exit qoriq_cpufreq_exit(void)
{
- unsigned int cpu;
-
- for_each_possible_cpu(cpu)
- free_cpumask_var(per_cpu(cpu_mask, cpu));
-
- cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
+ cpufreq_unregister_driver(&qoriq_cpufreq_driver);
}
-module_exit(ppc_corenet_cpufreq_exit);
+module_exit(qoriq_cpufreq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
-MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");
+MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
--
2.1.0.27.g96db324
^ permalink raw reply related
* [PATCH v2 2/2] cpufreq: qoriq: rename the driver
From: Tang Yuantian @ 2014-11-21 9:18 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: r64188, linuxppc-dev, Tang Yuantian, linux-kernel, linux-pm
In-Reply-To: <1416561518-24835-1-git-send-email-Yuantian.Tang@freescale.com>
This driver works on all QorIQ platforms which include
ARM-based cores and PPC-based cores.
Rename it in order to represent better.
Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
---
v2:
- use -C -M options when format-patch
drivers/cpufreq/{ppc-corenet-cpufreq.c => qoriq-cpufreq.c} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename drivers/cpufreq/{ppc-corenet-cpufreq.c => qoriq-cpufreq.c} (100%)
diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
similarity index 100%
rename from drivers/cpufreq/ppc-corenet-cpufreq.c
rename to drivers/cpufreq/qoriq-cpufreq.c
--
2.1.0.27.g96db324
^ permalink raw reply
* Re: [PATCH v3 1/2] cpufreq: qoriq: Make the driver usable on all QorIQ platforms
From: Viresh Kumar @ 2014-11-21 9:22 UTC (permalink / raw)
To: Tang Yuantian
Cc: r64188, linuxppc-dev@ozlabs.org, Rafael J. Wysocki,
Linux Kernel Mailing List, linux-pm@vger.kernel.org
In-Reply-To: <1416561518-24835-1-git-send-email-Yuantian.Tang@freescale.com>
On 21 November 2014 14:48, Tang Yuantian <Yuantian.Tang@freescale.com> wrote:
> Freescale introduced new ARM core-based SoCs which support dynamic
> frequency switch feature. DFS on new SoCs are compatible with current
> PowerPC CoreNet platforms. In order to support those new platforms,
> this driver needs to be updated. The main changes include:
>
> 1. Changed the names of functions in driver.
> 2. Added two new functions get_cpu_physical_id() and get_bus_freq().
> 3. Used a new way to get the CPU mask which share clock wire.
>
> Signed-off-by: Tang Yuantian <Yuantian.Tang@freescale.com>
> ---
> v3:
> - put the menu entries into Kconfig
> v2:
> - split the name change into a separete patch
> - use policy->driver_data instead of per_cpu variable
>
> drivers/cpufreq/Kconfig | 8 ++
> drivers/cpufreq/Kconfig.powerpc | 9 --
> drivers/cpufreq/ppc-corenet-cpufreq.c | 160 +++++++++++++++++++++-------------
> 3 files changed, 107 insertions(+), 70 deletions(-)
For both patches:
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
^ permalink raw reply
* Re: [PATCH 0/10] Replace _PAGE_NUMA with PAGE_NONE protections v2
From: Mel Gorman @ 2014-11-21 9:31 UTC (permalink / raw)
To: Sasha Levin
Cc: Rik van Riel, Linus Torvalds, Hugh Dickins, Linux Kernel,
Linux-MM, Ingo Molnar, Paul Mackerras, Aneesh Kumar, Dave Jones,
LinuxPPC-dev, Kirill Shutemov
In-Reply-To: <546E6221.5000409@oracle.com>
On Thu, Nov 20, 2014 at 04:50:25PM -0500, Sasha Levin wrote:
> On 11/20/2014 05:19 AM, Mel Gorman wrote:
> > V1 failed while running under kvm-tools very quickly and a second report
> > indicated that it happens on bare metal as well. This version survived
> > an overnight run of trinity running under kvm-tools here but verification
> > from Sasha would be appreciated.
>
> Hi Mel,
>
> I tried giving it a spin, but it won't apply at all on the latest -mm
> tree:
>
> $ git am -3 numa/*
> Applying: mm: numa: Do not dereference pmd outside of the lock during NUMA hinting fault
> Applying: mm: Add p[te|md] protnone helpers for use by NUMA balancing
> Applying: mm: Convert p[te|md]_numa users to p[te|md]_protnone_numa
> fatal: sha1 information is lacking or useless (mm/huge_memory.c).
> Repository lacks necessary blobs to fall back on 3-way merge.
> Cannot fall back to three-way merge.
>
> Did I miss a prerequisite?
>
No. V2 was still against 3.18-rc4 as that was what I had vanilla kernel
test data for. V3 will be against latest mmotm.
--
Mel Gorman
SUSE Labs
^ permalink raw reply
* Re: [PATCH 02/10] mm: Add p[te|md] protnone helpers for use by NUMA balancing
From: Mel Gorman @ 2014-11-21 9:35 UTC (permalink / raw)
To: Linus Torvalds
Cc: Rik van Riel, Hugh Dickins, Linux Kernel, Linux-MM, Ingo Molnar,
Paul Mackerras, Aneesh Kumar, Sasha Levin, Dave Jones,
LinuxPPC-dev, Kirill Shutemov
In-Reply-To: <CA+55aFwV80r66w4RmtY-MAUGkwmfBJe+C5KFD3ZnNgYb_KbBpQ@mail.gmail.com>
On Thu, Nov 20, 2014 at 11:54:06AM -0800, Linus Torvalds wrote:
> On Thu, Nov 20, 2014 at 2:19 AM, Mel Gorman <mgorman@suse.de> wrote:
> > This is a preparatory patch that introduces protnone helpers for automatic
> > NUMA balancing.
>
> Oh, I hadn't noticed that you had renamed these things. It was
> probably already true in your V1 version.
>
> I do *not* think that "pte_protnone_numa()" makes sense as a name. It
> only confuses people to think that there is still/again something
> NUMA-special about the PTE. The whole point of the protnone changes
> was to make it really very very clear that from a hardware standpoint,
> this is *exactly* about protnone, and nothing else.
>
> The fact that we then use protnone PTE's for numa faults is a VM
> internal issue, it should *not* show up in the architecture page table
> helpers.
>
> I'm not NAK'ing this name, but I really think it's a very important
> part of the whole patch series - to stop the stupid confusion about
> NUMA entries. As far as the page tables are concerned, this has
> absolutely _zero_ to do with NUMA.
>
> We made that mistake once. We're fixing it. Let the naming *show* that
> it's fixed, and this is "pte_protnone()".
>
> The places that use this for NUMA handling might have a comment or
> something. But they'll be in the VM where this matters, not in the
> architecture page table description files. The comment would be
> something like "if the vma is accessible, but the PTE is marked
> protnone, this is a autonuma entry".
>
I feared that people would eventually make the mistake of thinking that
pte_protnone() would return true for PROT_NONE VMAs that do *not* have
the page table bit set. I'll use the old name as you suggest and expand
the comment. It'll be in v3.
--
Mel Gorman
SUSE Labs
^ permalink raw reply
* Re: [RFC PATCH 07/16] PCI: Separate pci_host_bridge creation out of pci_create_root_bus()
From: Liviu Dudau @ 2014-11-21 9:53 UTC (permalink / raw)
To: Yijing Wang
Cc: Liviu Dudau, Tony Luck, Russell King, linux-pci@vger.kernel.org,
x86@kernel.org, linux-kernel@vger.kernel.org, huxinwei@huawei.com,
Thierry Reding, suravee.suthikulpanit@amd.com, Bjorn Helgaas,
linux-ia64@vger.kernel.org, Thomas Gleixner, Wuyun,
linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <546EA90D.3040304@huawei.com>
On Fri, Nov 21, 2014 at 02:53:01AM +0000, Yijing Wang wrote:
> >>> pci_create_host_bridge() can get pci_host_bridge ops while pci_create=
_root_bus() gets
> >>> the bus ops. For find out the MSI controller, the domain number and a=
ny other HB
> >>> specific stuff, you use the HB ops. For config R/W acceses you use bu=
s ops.
> >>>
> >>
> >> I want to unexport pci_create_root_bus() if we have pci_create_host_br=
idge().
> >
> > That's fine, but the function signature doesn't change, right? And I th=
ink we should keep
> > pci_scan_root_bus() with the current signature as well (except change s=
ysdata into a
> > struct pci_host_bridge *). We create the host bridge in one step, stuff=
it with all the
> > data that we need for scanning the root and associated busses, and then=
call pci_scan_root_bus().
> > pci_host_bridge has the ops from pci_create_host_bridge() and the root =
bus from pci_scan_root_bus().
>=20
> Liviu, could you have some draft patches about replace sysdata with pci_h=
ost_bridge, so we could
> discuss the details base it. :)
Sure, I'll try to send something soon.
Best regards,
Liviu
>=20
> >
> > Hope this makes sense.
> >
> >>
> >>
> >>>>>> -struct pci_bus *pci_create_root_bus(struct device *parent, u32 db=
,
> >>>>>> - struct pci_ops *ops, void *sysdata, struct list_he=
ad *resources)
> >>>>>> +struct pci_bus *__pci_create_root_bus(struct pci_host_bridge *bri=
dge)
> >>>>>> {
> >>>>>> int error;
> >>>>>> - struct pci_host_bridge *bridge;
> >>>>>> struct pci_bus *b, *b2;
> >>>>>> - struct pci_host_bridge_window *window, *n;
> >>>>>> + struct pci_host_bridge_window *window;
> >>>>>> struct resource *res;
> >>>>>> resource_size_t offset;
> >>>>>> char bus_addr[64];
> >>>>>> char *fmt;
> >>>>>> - u8 bus =3D PCI_BUSNUM(db);
> >>>>>> + struct device *parent =3D bridge->dev.parent;
> >>>>>>
> >>>>>> b =3D pci_alloc_bus(NULL);
> >>>>>> if (!b)
> >>>>>> return NULL;
> >>>>>>
> >>>>>> - b->sysdata =3D sysdata;
> >>>>>> - b->ops =3D ops;
> >>>>>> - b->number =3D b->busn_res.start =3D bus;
> >>>>>> + b->sysdata =3D bridge->sysdata;
> >>>>>
> >>>>> I think bridge should be the b->sysdata here.
> >>>>
> >>>> ? what's the meaning?
> >>>
> >>> Currently, bus->sysdata holds a pointer to the arch/driver host bridg=
e structure, as passed
> >>> in pci_create_root_bus(). If you agree with my idea of wrapping the h=
ost bridge driver
> >>> structure around the pci_host_bridge, then we will always have a way =
of retrieving that
> >>> information; but for backwards compatibility we could set bus->sysdat=
a to be the bridge.
> >>> Then existing macros that convert sysdata to pci_controller can be re=
used after being
> >>> updated.
> >>
> >> I think wrapping the host bridge driver structure around the pci_host_=
bridge could help
> >> us to find the private pci_controller, but in this case, pci_host_brid=
ge is not a pointer
> >> so we put all pci_host_bridge init stuff in host drivers ?
> >
> > Of course we put pci_host_bridge init *calls* in the host drivers becau=
se we need to do host
> > driver specific initialisation there anyway.
> >
> >> And if we still use arch spec
> >> macros convert ssydata to pci_controller, so what's the greatest advan=
tage ?
> >
> > Agree, the macro is not useful unless we have a common pci_controller s=
tructure. Sorry about
> > the noise.
> >
> >>
> >> If we move domain and msi_chip out of sysdata, what's other things in =
sysdata still need us
> >> to convert in PCI core ?
> >
> > I've never said we need to keep sysdata. From my discussion with Bjorn =
a year ago the idea was
> > to put the sysdata members into pci_host_bridge structure and remove sy=
sdata member from pci_bus.
> > pci_scan_root_bus() can still get a parameter called sysdata if we want=
, but it will be a
> > struct pci_host_bridge * type.
>=20
> Fine.
>=20
> >
> > Best regards,
> > Liviu
> >
> >>
> >>
> >>
> >>>
> >>> Best regards,
> >>> Liviu
> >>>
> >>>>
> >>>>>
> >>>>>> + b->ops =3D bridge->ops;
> >>>>>
> >>>>> See comment above why I don't think this is necessary.
> >>>>>
> >>>>>> + b->number =3D b->busn_res.start =3D bridge->busnum;
> >>>>>> pci_bus_assign_domain_nr(b, parent);
> >>>>>> - b2 =3D pci_find_bus(pci_domain_nr(b), bus);
> >>>>>> + bridge->domain =3D pci_domain_nr(b);
> >>>>>
> >>>>> Do you really want to overwrite the bridge's domain with the one fr=
om a bus that
> >>>>> could possibly be rejected a couple of lines further down?
> >>>>>
> >>>>> As an asside: if we are doing the split of pci_host_bridge from roo=
t bus creation
> >>>>> it is worth in my opinion to move the domain setup in pci_create_ho=
st_bridge()
> >>>>> and stop fiddling with it here.
> >>>>
> >>>>
> >>>> Hi Liviu, these lines just temporary, I will remove it after all hos=
t drivers
> >>>> save its domain in pci_host_bridge.
> >>>>
> >>>>
> >>>>>
> >>>>> Otherwise it looks to me like you are heading in the right directio=
n.
> >>>>
> >>>> Thanks!
> >>>> Yijing.
> >>>>
> >>>>>
> >>>>> Best regards,
> >>>>> Liviu
> >>>>>
> >>>>>> + b2 =3D pci_find_bus(pci_domain_nr(b), bridge->busnum);
> >>>>>> if (b2) {
> >>>>>> /* If we already got to this bus through a differe=
nt bridge, ignore it */
> >>>>>> dev_dbg(&b2->dev, "bus already known\n");
> >>>>>> goto err_out;
> >>>>>> }
> >>>>>>
> >>>>>> - bridge =3D pci_alloc_host_bridge(b);
> >>>>>> - if (!bridge)
> >>>>>> - goto err_out;
> >>>>>> -
> >>>>>> - bridge->dev.parent =3D parent;
> >>>>>> - bridge->dev.release =3D pci_release_host_bridge_dev;
> >>>>>> - dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b=
), bus);
> >>>>>> - error =3D pcibios_root_bridge_prepare(bridge);
> >>>>>> - if (error) {
> >>>>>> - kfree(bridge);
> >>>>>> - goto err_out;
> >>>>>> - }
> >>>>>> -
> >>>>>> - error =3D device_register(&bridge->dev);
> >>>>>> - if (error) {
> >>>>>> - put_device(&bridge->dev);
> >>>>>> - goto err_out;
> >>>>>> - }
> >>>>>> b->bridge =3D get_device(&bridge->dev);
> >>>>>> device_enable_async_suspend(b->bridge);
> >>>>>> pci_set_bus_of_node(b);
> >>>>>> @@ -1950,7 +1899,7 @@ struct pci_bus *pci_create_root_bus(struct d=
evice *parent, u32 db,
> >>>>>>
> >>>>>> b->dev.class =3D &pcibus_class;
> >>>>>> b->dev.parent =3D b->bridge;
> >>>>>> - dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
> >>>>>> + dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), b->nu=
mber);
> >>>>>> error =3D device_register(&b->dev);
> >>>>>> if (error)
> >>>>>> goto class_dev_reg_err;
> >>>>>> @@ -1966,12 +1915,11 @@ struct pci_bus *pci_create_root_bus(struct=
device *parent, u32 db,
> >>>>>> printk(KERN_INFO "PCI host bridge to bus %s\n", de=
v_name(&b->dev));
> >>>>>>
> >>>>>> /* Add initial resources to the bus */
> >>>>>> - list_for_each_entry_safe(window, n, resources, list) {
> >>>>>> - list_move_tail(&window->list, &bridge->windows);
> >>>>>> + list_for_each_entry(window, &bridge->windows, list) {
> >>>>>> res =3D window->res;
> >>>>>> offset =3D window->offset;
> >>>>>> if (res->flags & IORESOURCE_BUS)
> >>>>>> - pci_bus_insert_busn_res(b, bus, res->end);
> >>>>>> + pci_bus_insert_busn_res(b, b->number, res-=
>end);
> >>>>>> else
> >>>>>> pci_bus_add_resource(b, res, 0);
> >>>>>> if (offset) {
> >>>>>> @@ -2001,6 +1949,25 @@ err_out:
> >>>>>> return NULL;
> >>>>>> }
> >>>>>>
> >>>>>> +struct pci_bus *pci_create_root_bus(struct device *parent, u32 bu=
s,
> >>>>>> + struct pci_ops *ops, void *sysdata, struct list_he=
ad *resources)
> >>>>>> +{
> >>>>>> + struct pci_host_bridge *host;
> >>>>>> +
> >>>>>> + host =3D pci_create_host_bridge(parent, bus, ops,
> >>>>>> + sysdata ,resources);
> >>>>>> + if (!host)
> >>>>>> + return NULL;
> >>>>>> +
> >>>>>> + host->bus =3D __pci_create_root_bus(host);
> >>>>>> + if (!host->bus) {
> >>>>>> + pci_free_host_bridge(host);
> >>>>>> + return NULL;
> >>>>>> + }
> >>>>>> +
> >>>>>> + return host->bus;
> >>>>>> +}
> >>>>>> +
> >>>>>> int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_m=
ax)
> >>>>>> {
> >>>>>> struct resource *res =3D &b->busn_res;
> >>>>>> @@ -2069,40 +2036,37 @@ struct pci_bus *pci_scan_root_bus(struct d=
evice *parent, u32 db,
> >>>>>> {
> >>>>>> struct pci_host_bridge_window *window;
> >>>>>> bool found =3D false;
> >>>>>> - struct pci_bus *b;
> >>>>>> - LIST_HEAD(default_res);
> >>>>>> + struct pci_host_bridge *host;
> >>>>>> int max;
> >>>>>>
> >>>>>> - if (!resources) {
> >>>>>> - pci_add_resource(&default_res, &ioport_resource);
> >>>>>> - pci_add_resource(&default_res, &iomem_resource);
> >>>>>> - pci_add_resource(&default_res, &busn_resource);
> >>>>>> - } else {
> >>>>>> - list_for_each_entry(window, resources, list)
> >>>>>> - if (window->res->flags & IORESOURCE_BUS) {
> >>>>>> - found =3D true;
> >>>>>> - break;
> >>>>>> - }
> >>>>>> - }
> >>>>>> + host =3D pci_create_host_bridge(parent, db, ops, sysdata, =
resources);
> >>>>>> + if (!host)
> >>>>>> + return NULL;
> >>>>>>
> >>>>>> - b =3D pci_create_root_bus(parent, db, ops, sysdata,
> >>>>>> - resources ? resources : &default_res);
> >>>>>> - if (!b)
> >>>>>> + list_for_each_entry(window, &host->windows, list)
> >>>>>> + if (window->res->flags & IORESOURCE_BUS) {
> >>>>>> + found =3D true;
> >>>>>> + break;
> >>>>>> + }
> >>>>>> +
> >>>>>> + host->bus =3D __pci_create_root_bus(host);
> >>>>>> + if (!host->bus) {
> >>>>>> + pci_free_host_bridge(host);
> >>>>>> return NULL;
> >>>>>> + }
> >>>>>>
> >>>>>> if (!found) {
> >>>>>> - dev_info(&b->dev,
> >>>>>> + dev_info(&host->bus->dev,
> >>>>>> "No busn resource found for root bus, will use [b=
us %02x-ff]\n",
> >>>>>> PCI_BUSNUM(db));
> >>>>>> - pci_bus_insert_busn_res(b, PCI_BUSNUM(db), 255);
> >>>>>> + pci_bus_insert_busn_res(host->bus, PCI_BUSNUM(db),=
255);
> >>>>>> }
> >>>>>>
> >>>>>> - max =3D pci_scan_child_bus(b);
> >>>>>> -
> >>>>>> + max =3D pci_scan_child_bus(host->bus);
> >>>>>> if (!found)
> >>>>>> - pci_bus_update_busn_res_end(b, max);
> >>>>>> + pci_bus_update_busn_res_end(host->bus, max);
> >>>>>>
> >>>>>> - return b;
> >>>>>> + return host->bus;
> >>>>>> }
> >>>>>> EXPORT_SYMBOL(pci_scan_root_bus);
> >>>>>>
> >>>>>> diff --git a/include/linux/pci.h b/include/linux/pci.h
> >>>>>> index 8b11b38..daa7f40 100644
> >>>>>> --- a/include/linux/pci.h
> >>>>>> +++ b/include/linux/pci.h
> >>>>>> @@ -402,7 +402,12 @@ struct pci_host_bridge_window {
> >>>>>> struct pci_host_bridge {
> >>>>>> struct device dev;
> >>>>>> struct pci_bus *bus; /* root bus */
> >>>>>> + struct list_head list;
> >>>>>> struct list_head windows; /* pci_host_bridge_windows=
*/
> >>>>>> + int busnum;
> >>>>>> + int domain;
> >>>>>> + void *sysdata;
> >>>>>> + struct pci_ops *ops;
> >>>>>> void (*release_fn)(struct pci_host_bridge *);
> >>>>>> void *release_data;
> >>>>>> };
> >>>>>> @@ -413,7 +418,9 @@ void pci_set_host_bridge_release(struct pci_ho=
st_bridge *bridge,
> >>>>>> void *release_data);
> >>>>>>
> >>>>>> int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge);
> >>>>>> -
> >>>>>> +struct pci_host_bridge *pci_create_host_bridge(
> >>>>>> + struct device *parent, u32 db, struct pci_ops *ops=
,
> >>>>>> + void *sys, struct list_head *resources);
> >>>>>> /*
> >>>>>> * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those tha=
t correspond
> >>>>>> * to P2P or CardBus bridge windows) go in a table. Additional o=
nes (for
> >>>>>> @@ -770,6 +777,8 @@ void pci_bus_add_devices(const struct pci_bus =
*bus);
> >>>>>> struct pci_bus *pci_create_root_bus(struct device *parent, u32 bu=
s,
> >>>>>> struct pci_ops *ops, void *sys=
data,
> >>>>>> struct list_head *resources);
> >>>>>> +struct pci_bus *__pci_create_root_bus(struct pci_host_bridge *hos=
t);
> >>>>>> +void pci_free_host_bridge(struct pci_host_bridge *host);
> >>>>>> int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busma=
x);
> >>>>>> int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax);
> >>>>>> void pci_bus_release_busn_res(struct pci_bus *b);
> >>>>>> --
> >>>>>> 1.7.1
> >>>>>>
> >>>>>> --
> >>>>>> To unsubscribe from this list: send the line "unsubscribe linux-pc=
i" in
> >>>>>> the body of a message to majordomo@vger.kernel.org
> >>>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>> --
> >>>> Thanks!
> >>>> Yijing
> >>>>
> >>>> --
> >>>> To unsubscribe from this list: send the line "unsubscribe linux-pci"=
in
> >>>> the body of a message to majordomo@vger.kernel.org
> >>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >>>>
> >>>
> >>
> >>
> >> --
> >> Thanks!
> >> Yijing
> >>
> >>
> >
>=20
>=20
> --
> Thanks!
> Yijing
>=20
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>=20
--=20
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
| I would like to |
| fix the world, |
| but they're not |
| giving me the |
\ source code! /
---------------
=C2=AF\_(=E3=83=84)_/=C2=AF
^ permalink raw reply
* Re: [PATCH V4 7/8] powerpc, ptrace: Enable support for miscellaneous debug registers
From: Anshuman Khandual @ 2014-11-21 10:26 UTC (permalink / raw)
To: Denis Kirjanov
Cc: mikey, james.hogan, avagin, Paul.Clothier, davem, peterz, palves,
linux-kernel, oleg, dhowells, linuxppc-dev, davej, tglx, sukadev,
akpm, sam.bobroff
In-Reply-To: <CAHj3AVna2H1VZvP0=Xjw9pqS+gRKR7_KH9FNhX3vUBFh7OU2wA@mail.gmail.com>
On 11/14/2014 03:30 PM, Denis Kirjanov wrote:
> On 11/13/14, Anshuman Khandual <khandual@linux.vnet.ibm.com> wrote:
>> > On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
>>> >> This patch enables get and set of miscellaneous debug registers through
>>> >> ptrace PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing new
>>> >> powerpc specific register set REGSET_MISC support corresponding to the
>>> >> new ELF core note NT_PPC_MISC added previously in this regard.
>> >
>> > Right now this one does not compile for "ppc64e_defconfig" and
>> > "pmac32_defconfig" config options. The patch below will fix it
>> > and would be part of next revision.
> It would be great to have a test tool for that under
> tools/testing/selftests/ptrace/
>
Hey Denis,
I have already added one test case under selftests/powerpc/tm/ which
will test all the newly added ELF core note based ptrace requests on
powerpc. This test case is added as part of the next patch 8/8 in
the series. Please let me know if the test case does not cover something
you are looking for.
^ permalink raw reply
* [PATCH 1/1] tty-hvsi_lib: Deletion of an unnecessary check before the function call "tty_kref_put"
From: SF Markus Elfring @ 2014-11-21 11:45 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, linuxppc-dev
Cc: Julia Lawall, kernel-janitors, LKML
In-Reply-To: <5317A59D.4@users.sourceforge.net>
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Fri, 21 Nov 2014 12:40:32 +0100
The tty_kref_put() function tests whether its argument is NULL and then
returns immediately. Thus the test around the call is not needed.
This issue was detected by using the Coccinelle software.
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
drivers/tty/hvc/hvsi_lib.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
index 7ae6c29..a270f04 100644
--- a/drivers/tty/hvc/hvsi_lib.c
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -405,8 +405,7 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp)
hvsi_send_close(pv);
}
- if (pv->tty)
- tty_kref_put(pv->tty);
+ tty_kref_put(pv->tty);
pv->tty = NULL;
}
--
2.1.3
^ permalink raw reply related
* Re: [PATCH V4 6/8] powerpc, ptrace: Enable support for transactional memory register sets
From: Anshuman Khandual @ 2014-11-21 13:11 UTC (permalink / raw)
To: Sukadev Bhattiprolu
Cc: mikey, james.hogan, avagin, Paul.Clothier, davem, peterz, palves,
linux-kernel, oleg, dhowells, linuxppc-dev, davej, akpm, tglx,
sam.bobroff
In-Reply-To: <20141118211848.GA3075@us.ibm.com>
On 11/19/2014 02:48 AM, Sukadev Bhattiprolu wrote:
> Anshuman Khandual [khandual@linux.vnet.ibm.com] wrote:
> | This patch enables get and set of transactional memory related register
> | sets through PTRACE_GETREGSET-PTRACE_SETREGSET interface by implementing
> | four new powerpc specific register sets i.e REGSET_TM_SPR, REGSET_TM_CGPR,
> | REGSET_TM_CFPR, REGSET_CVMX support corresponding to these following new
> | ELF core note types added previously in this regard.
> |
> | (1) NT_PPC_TM_SPR
> | (2) NT_PPC_TM_CGPR
> | (3) NT_PPC_TM_CFPR
> | (4) NT_PPC_TM_CVMX
> |
> | Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> | ---
> | arch/powerpc/include/uapi/asm/elf.h | 2 +
> | arch/powerpc/kernel/ptrace.c | 666 +++++++++++++++++++++++++++++++++++-
> | 2 files changed, 653 insertions(+), 15 deletions(-)
> |
> | diff --git a/arch/powerpc/include/uapi/asm/elf.h b/arch/powerpc/include/uapi/asm/elf.h
> | index 59dad11..fdc8e2f 100644
> | --- a/arch/powerpc/include/uapi/asm/elf.h
> | +++ b/arch/powerpc/include/uapi/asm/elf.h
> | @@ -91,6 +91,8 @@
> |
> | #define ELF_NGREG 48 /* includes nip, msr, lr, etc. */
> | #define ELF_NFPREG 33 /* includes fpscr */
> | +#define ELF_NVMX 34 /* includes all vector registers */
> | +#define ELF_NTMSPRREG 7 /* includes TM sprs, org_msr, dscr, tar, ppr */
> |
> | typedef unsigned long elf_greg_t64;
> | typedef elf_greg_t64 elf_gregset_t64[ELF_NGREG];
> | diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
> | index 2bbbd10..b279947 100644
> | --- a/arch/powerpc/kernel/ptrace.c
> | +++ b/arch/powerpc/kernel/ptrace.c
> | @@ -63,6 +63,11 @@ struct pt_regs_offset {
> | {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
> | #define REG_OFFSET_END {.name = NULL, .offset = 0}
> |
> | +/* Some common structure offsets */
> | +#define TSO(f) (offsetof(struct thread_struct, f))
> | +#define TVSO(f) (offsetof(struct thread_vr_state, f))
> | +#define TFSO(f) (offsetof(struct thread_fp_state, f))
> | +
> | static const struct pt_regs_offset regoffset_table[] = {
> | GPR_OFFSET_NAME(0),
> | GPR_OFFSET_NAME(1),
> | @@ -792,6 +797,534 @@ static int evr_set(struct task_struct *target, const struct user_regset *regset,
> | }
> | #endif /* CONFIG_SPE */
> |
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | +/*
> | + * tm_spr_active
> | + *
> | + * This function checks number of available regisers in
> | + * the transactional memory SPR category.
> | + */
> | +static int tm_spr_active(struct task_struct *target,
> | + const struct user_regset *regset)
> | +{
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return 0;
> | +
> | + return regset->n;
> | +}
> | +
> | +/*
> | + * tm_spr_get
> | + *
> | + * This function gets transactional memory related SPR registers
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct {
> | + * u64 tm_tfhar;
> | + * u64 tm_texasr;
> | + * u64 tm_tfiar;
> | + * unsigned long tm_orig_msr;
> | + * unsigned long tm_tar;
> | + * unsigned long tm_ppr;
> | + * unsigned long tm_dscr;
> | + * };
> | + */
> | +static int tm_spr_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + /* Build tests */
> | + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
> | + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
> | + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
> | + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long) +
> Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
> TSO(ckpt_regs) ?
Yeah we can.
> | + sizeof(struct pt_regs) != TSO(tm_tar));
> | + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
> | + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + /* Flush the states */
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* TFHAR register */
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfhar, 0, sizeof(u64));
>
> The last two parameters, (start_pos, end_pos) are easy to understand
> here, but...
Okay.
>
> | +
> | + /* TEXASR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_texasr, sizeof(u64),
> | + 2 * sizeof(u64));
>
> ... gets harder to understand here and subsequent fields below.
>
> Given that you already do the BUILD_BUG_ON() tests above, how about
> using TSO(tm_texasr) and TSO(tfiar) here for start_pos and end_pos ?
Hmm, I understand that as it looks kind of ugly, but writing to/from
the user level buffer is done looking at the user interface buffer
structure layout mentioned below.
* struct {
* u64 tm_tfhar;
* u64 tm_texasr;
* u64 tm_tfiar;
* unsigned long tm_orig_msr;
* unsigned long tm_tar;
* unsigned long tm_ppr;
* unsigned long tm_dscr;
* };
Looking at this structure will help some one understand the copy in/out
process and it's order better.
>
> Also, how about just returning if the copyout fails ? If the first
> copyout fails, we will still check 'if(!ret)' several times below.
Hmm, thats true. But the code flow is very similar to that of gpr_get/
gpr_set functions though it has a BUILD_BUG_ON check in between. The
rational is to stop copyout/in when we hit the first error and not to
proceed any further. We can return from the first error itself.
>
> | +
> | + /* TFIAR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfiar,
> | + 2 * sizeof(u64), 3 * sizeof(u64));
> | +
> | + /* TM checkpointed original MSR */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_orig_msr, 3 * sizeof(u64),
> | + 3 * sizeof(u64) + sizeof(unsigned long));
> | +
> | + /* TM checkpointed TAR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tar, 3 * sizeof(u64) +
> | + sizeof(unsigned long) ,
> | + 3 * sizeof(u64) + 2 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed PPR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_ppr, 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long),
> | + 3 * sizeof(u64) + 3 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed DSCR register */
> | + if (!ret)
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_dscr, 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long),
> | + 3 * sizeof(u64) + 4 * sizeof(unsigned long));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_spr_set
> | + *
> | + * This function sets transactional memory related SPR registers
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct {
> | + * u64 tm_tfhar;
> | + * u64 tm_texasr;
> | + * u64 tm_tfiar;
> | + * unsigned long tm_orig_msr;
> | + * unsigned long tm_tar;
> | + * unsigned long tm_ppr;
> | + * unsigned long tm_dscr;
> | + * };
> | + */
> | +static int tm_spr_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + /* Build tests */
> | + BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr));
> | + BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar));
> | + BUILD_BUG_ON(TSO(tm_orig_msr) + sizeof(unsigned long)
>
> Can we replace TSO(tm_orig_msr) + sizeof(unsigned long) with
> TSO(ckpt_regs) ?
Yeah we can.
>
> | + + sizeof(struct pt_regs) != TSO(tm_tar));
> | + BUILD_BUG_ON(TSO(tm_tar) + sizeof(unsigned long) != TSO(tm_ppr));
> | + BUILD_BUG_ON(TSO(tm_ppr) + sizeof(unsigned long) != TSO(tm_dscr));
> | + BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(tm_orig_msr));
>
> How about moving this last line up after the check for TSO(tm_tfiar) ?
Done.
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + /* Flush the states */
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* TFHAR register */
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfhar, 0, sizeof(u64));
> | +
> | + /* TEXASR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_texasr, sizeof(u64),
> | + 2 * sizeof(u64));
>
> Return if copyin() fails ?
Yeah both the cases are similar.
>
> | +
> | + /* TFIAR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tfiar,
> | + 2 * sizeof(u64), 3 * sizeof(u64));
> | +
> | +
> | + /* TM checkpointed orig MSR */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_orig_msr, 3 * sizeof(u64),
> | + 3 * sizeof(u64) + sizeof(unsigned long));
> | +
> | +
> | + /* TM checkpointed TAR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_tar, 3 * sizeof(u64) +
> | + sizeof(unsigned long), 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed PPR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_ppr, 3 * sizeof(u64) +
> | + 2 * sizeof(unsigned long), 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long));
> | +
> | + /* TM checkpointed DSCR register */
> | + if (!ret)
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.tm_dscr, 3 * sizeof(u64) +
> | + 3 * sizeof(unsigned long), 3 * sizeof(u64) +
> | + 4 * sizeof(unsigned long));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cgpr_active
> | + *
> | + * This function checks the number of available regisers in
> | + * transaction checkpointed GPR category.
> | + */
> | +static int tm_cgpr_active(struct task_struct *target,
> | + const struct user_regset *regset)
> | +{
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return 0;
> | +
> | + return regset->n;
> | +}
> | +
> | +/*
> | + * tm_cgpr_get
> | + *
> | + * This function gets transaction checkpointed GPR registers
> | + *
> | + * When the transaction is active, 'ckpt_regs' holds all the checkpointed
> | + * GPR register values for the current transaction to fall back on if it
> | + * aborts in between. This function gets those checkpointed GPR registers.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct data {
> | + * struct pt_regs ckpt_regs;
> | + * };
> | + */
> | +static int tm_cgpr_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.ckpt_regs, 0,
> | + sizeof(struct pt_regs));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cgpr_set
> | + *
> | + * This function sets in transaction checkpointed GPR registers
> | + *
> | + * When the transaction is active, 'ckpt_regs' holds the checkpointed
> | + * GPR register values for the current transaction to fall back on if it
> | + * aborts in between. This function sets those checkpointed GPR registers.
> | + *
> | + * Userspace intaerface buffer:
> | + *
> | + * struct data {
> | + * struct pt_regs ckpt_regs;
> | + * };
> | + */
> | +static int tm_cgpr_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.ckpt_regs, 0,
> | + sizeof(struct pt_regs));
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cfpr_active
> | + *
> | + * This function checks number of available regisers in
> | + * transaction checkpointed FPR category.
> | + */
> | +static int tm_cfpr_active(struct task_struct *target,
> | + const struct user_regset *regset)
> | +{
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return 0;
> | +
> | + return regset->n;
> | +}
> | +
> | +/*
> | + * tm_cfpr_get
> | + *
> | + * This function gets in transaction checkpointed FPR registers
> | + *
> | + * When the transaction is active 'fp_state' holds the checkpointed
> | + * values for the current transaction to fall back on if it aborts
> | + * in between. This function gets those checkpointed FPR registers.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct data {
> | + * u64 fpr[32];
> | + * u64 fpscr;
> | + *};
> | + */
> | +static int tm_cfpr_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + u64 buf[33];
> | + int i;
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* copy to local buffer then write that out */
> | + for (i = 0; i < 32 ; i++)
> | + buf[i] = target->thread.TS_FPR(i);
> | + buf[32] = target->thread.fp_state.fpscr;
> | + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
> | +}
> | +
> | +/*
> | + * tm_cfpr_set
> | + *
> | + * This function sets in transaction checkpointed FPR registers
> | + *
> | + * When the transaction is active 'fp_state' holds the checkpointed
> | + * FPR register values for the current transaction to fall back on
> | + * if it aborts in between. This function sets these checkpointed
> | + * FPR registers.
> | + *
> | + * Userspace interface buffer layout:
> | + *
> | + * struct data {
> | + * u64 fpr[32];
> | + * u64 fpscr;
> | + *};
> | + */
> | +static int tm_cfpr_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + u64 buf[33];
> | + int i;
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + /* copy to local buffer then write that out */
> | + i = user_regset_copyin(&pos, &count, &kbuf, &ubuf, buf, 0, -1);
> | + if (i)
> | + return i;
> | + for (i = 0; i < 32 ; i++)
> | + target->thread.TS_FPR(i) = buf[i];
> | + target->thread.fp_state.fpscr = buf[32];
> | + return 0;
> | +}
> | +
> | +/*
> | + * tm_cvmx_active
> | + *
> | + * This function checks the number of available regisers in
> | + * checkpointed VMX category.
> | + */
> | +static int tm_cvmx_active(struct task_struct *target,
> | + const struct user_regset *regset)
> | +{
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return 0;
> | +
> | + return regset->n;
> | +}
> | +
> | +/*
> | + * tm_cvmx_get
> | + *
> | + * This function gets in transaction checkpointed VMX registers
> | + *
> | + * When the transaction is active 'vr_state' and 'vr_save' hold
> | + * the checkpointed values for the current transaction to fall
> | + * back on if it aborts in between.
> | + *
> | + * User interface buffer:
> | + *
> | + * struct data {
> | + * vector128 vr[32];
> | + * vector128 vscr;
> | + * vector128 vrsave;
> | + *};
> | + */
> | +static int tm_cvmx_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + /* Flush the state */
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.vr_state, 0,
> | + 33 * sizeof(vector128));
> | + if (!ret) {
> | + /*
> | + * Copy out only the low-order word of vrsave.
> | + */
> | + union {
> | + elf_vrreg_t reg;
> | + u32 word;
> | + } vrsave;
> | + memset(&vrsave, 0, sizeof(vrsave));
> | + vrsave.word = target->thread.vrsave;
> | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave,
> | + 33 * sizeof(vector128), -1);
> | + }
> | +
> | + return ret;
> | +}
> | +
> | +/*
> | + * tm_cvmx_set
> | + *
> | + * This function sets in transaction checkpointed VMX registers
> | + *
> | + * When the transaction is active 'vr_state' and 'vr_save' hold
> | + * the checkpointed values for the current transaction to fall
> | + * back on if it aborts in between.
> | + *
> | + * Userspace interface buffer:
> | + *
> | + * struct data {
> | + * vector128 vr[32];
> | + * vector128 vscr;
> | + * vector128 vrsave;
> | + *};
> | + */
> | +static int tm_cvmx_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + int ret;
> | +
> | + BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32]));
> | +
> | + if (!cpu_has_feature(CPU_FTR_TM))
> | + return -ENODEV;
> | +
> | + if (!MSR_TM_ACTIVE(target->thread.regs->msr))
> | + return -ENODATA;
> | +
> | + flush_fp_to_thread(target);
> | + flush_altivec_to_thread(target);
> | + flush_tmregs_to_thread(target);
> | +
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
> | + &target->thread.vr_state, 0,
> | + 33 * sizeof(vector128));
> | + if (!ret && count > 0) {
> | + /*
> | + * We use only the first word of vrsave.
>
> For consistency with the _get() function above, s/first/low-order/ ?
Done.
> | + */
> | + union {
> | + elf_vrreg_t reg;
> | + u32 word;
> | + } vrsave;
> | + memset(&vrsave, 0, sizeof(vrsave));
> | + vrsave.word = target->thread.vrsave;
> | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave,
> | + 33 * sizeof(vector128), -1);
> | + if (!ret)
> | + target->thread.vrsave = vrsave.word;
> | + }
> | +
> | + return ret;
> | +}
> | +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
> |
> | /*
> | * These are our native regset flavors.
> | @@ -808,6 +1341,12 @@ enum powerpc_regset {
> | #ifdef CONFIG_SPE
> | REGSET_SPE,
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + REGSET_TM_SPR, /* TM specific SPR registers */
> | + REGSET_TM_CGPR, /* TM checkpointed GPR registers */
> | + REGSET_TM_CFPR, /* TM checkpointed FPR registers */
> | + REGSET_TM_CVMX, /* TM checkpointed VMX registers */
> | +#endif
> | };
> |
> | static const struct user_regset native_regsets[] = {
> | @@ -842,6 +1381,28 @@ static const struct user_regset native_regsets[] = {
> | .active = evr_active, .get = evr_get, .set = evr_set
> | },
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + [REGSET_TM_SPR] = {
> | + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
> | + .size = sizeof(u64), .align = sizeof(u64),
> | + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
> | + },
> | + [REGSET_TM_CGPR] = {
> | + .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
> | + .size = sizeof(long), .align = sizeof(long),
> | + .active = tm_cgpr_active, .get = tm_cgpr_get, .set = tm_cgpr_set
> | + },
> | + [REGSET_TM_CFPR] = {
> | + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
> | + .size = sizeof(double), .align = sizeof(double),
> | + .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
> | + },
> | + [REGSET_TM_CVMX] = {
> | + .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
> | + .size = sizeof(vector128), .align = sizeof(vector128),
> | + .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
> | + },
> | +#endif
> | };
> |
> | static const struct user_regset_view user_ppc_native_view = {
> | @@ -852,24 +1413,35 @@ static const struct user_regset_view user_ppc_native_view = {
> | #ifdef CONFIG_PPC64
> | #include <linux/compat.h>
> |
> | -static int gpr32_get(struct task_struct *target,
> | +static int common_gpr32_get(struct task_struct *target,
> | const struct user_regset *regset,
> | unsigned int pos, unsigned int count,
> | - void *kbuf, void __user *ubuf)
> | + void *kbuf, void __user *ubuf, bool in_tm)
> | {
> | - const unsigned long *regs = &target->thread.regs->gpr[0];
> | + const unsigned long *regs;
> | compat_ulong_t *k = kbuf;
> | compat_ulong_t __user *u = ubuf;
> | compat_ulong_t reg;
> | int i;
> |
> | - if (target->thread.regs == NULL)
> | - return -EIO;
> | + if (in_tm) {
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + regs = &target->thread.ckpt_regs.gpr[0];
> | +#endif
>
> regs uninitialized if in_tm is true and CONFIG_PPC_TRANSACTIONAL_MEM
> is false ? It appears that it cannot/should not happen, how about BUGON() ?
> or at least regs = NULL to silence compiler warnings ?
Yeah it cannot happen, seems like compiler is able to figure that out as
I dont get any warning because of that. BUG_ON sounds like a good option
after the if-else block to verify whether the regs variable got any valid
address value to it or not. Also we can start with regs = NULL at the
starting of the function as well.
>
>
> | + } else {
> | + regs = &target->thread.regs->gpr[0];
> |
> | - if (!FULL_REGS(target->thread.regs)) {
> | - /* We have a partial register set. Fill 14-31 with bogus values */
> | - for (i = 14; i < 32; i++)
> | - target->thread.regs->gpr[i] = NV_REG_POISON;
> | + if (target->thread.regs == NULL)
> | + return -EIO;
> | +
> | + if (!FULL_REGS(target->thread.regs)) {
> | + /*
> | + * We have a partial register set.
> | + * Fill 14-31 with bogus values.
> | + */
> | + for (i = 14; i < 32; i++)
> | + target->thread.regs->gpr[i] = NV_REG_POISON;
> | + }
> | }
> |
> | pos /= sizeof(reg);
> | @@ -909,20 +1481,28 @@ static int gpr32_get(struct task_struct *target,
> | PT_REGS_COUNT * sizeof(reg), -1);
> | }
> |
> | -static int gpr32_set(struct task_struct *target,
> | +static int common_gpr32_set(struct task_struct *target,
> | const struct user_regset *regset,
> | unsigned int pos, unsigned int count,
> | - const void *kbuf, const void __user *ubuf)
> | + const void *kbuf, const void __user *ubuf, bool in_tm)
> | {
> | - unsigned long *regs = &target->thread.regs->gpr[0];
> | + unsigned long *regs;
> | const compat_ulong_t *k = kbuf;
> | const compat_ulong_t __user *u = ubuf;
> | compat_ulong_t reg;
> |
> | - if (target->thread.regs == NULL)
> | - return -EIO;
> | + if (in_tm) {
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + regs = &target->thread.ckpt_regs.gpr[0];
> | +#endif
>
> ditto
Done.
>
> | + } else {
> | + regs = &target->thread.regs->gpr[0];
> |
> | - CHECK_FULL_REGS(target->thread.regs);
> | + if (target->thread.regs == NULL)
> | + return -EIO;
> | +
> | + CHECK_FULL_REGS(target->thread.regs);
> | + }
> |
> | pos /= sizeof(reg);
> | count /= sizeof(reg);
> | @@ -982,6 +1562,39 @@ static int gpr32_set(struct task_struct *target,
> | (PT_TRAP + 1) * sizeof(reg), -1);
> | }
> |
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | +static int tm_cgpr32_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 1);
> | +}
> | +
> | +static int tm_cgpr32_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
> | +
> | +static int gpr32_get(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + void *kbuf, void __user *ubuf)
> | +{
> | + return common_gpr32_get(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | +
> | +static int gpr32_set(struct task_struct *target,
> | + const struct user_regset *regset,
> | + unsigned int pos, unsigned int count,
> | + const void *kbuf, const void __user *ubuf)
> | +{
> | + return common_gpr32_set(target, regset, pos, count, kbuf, ubuf, 0);
> | +}
> | /*
> | * These are the regset flavors matching the CONFIG_PPC32 native set.
> | */
> | @@ -1010,6 +1623,29 @@ static const struct user_regset compat_regsets[] = {
> | .active = evr_active, .get = evr_get, .set = evr_set
> | },
> | #endif
> | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> | + [REGSET_TM_SPR] = {
> | + .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
> | + .size = sizeof(u64), .align = sizeof(u64),
> | + .active = tm_spr_active, .get = tm_spr_get, .set = tm_spr_set
> | + },
> | + [REGSET_TM_CGPR] = {
> | + .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
> | + .size = sizeof(long), .align = sizeof(long),
> | + .active = tm_cgpr_active,
> | + .get = tm_cgpr32_get, .set = tm_cgpr32_set
> | + },
> | + [REGSET_TM_CFPR] = {
> | + .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
> | + .size = sizeof(double), .align = sizeof(double),
> | + .active = tm_cfpr_active, .get = tm_cfpr_get, .set = tm_cfpr_set
> | + },
> | + [REGSET_TM_CVMX] = {
> | + .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
> | + .size = sizeof(vector128), .align = sizeof(vector128),
> | + .active = tm_cvmx_active, .get = tm_cvmx_get, .set = tm_cvmx_set
> | + },
> | +#endif
> | };
> |
> | static const struct user_regset_view user_ppc_compat_view = {
> | --
> | 1.9.3
>
^ permalink raw reply
* Re: [PATCH V4 2/8] powerpc, process: Add functions flush_tm_state, flush_tmregs_to_thread
From: Anshuman Khandual @ 2014-11-21 13:13 UTC (permalink / raw)
To: linux-kernel, linuxppc-dev
Cc: mikey, james.hogan, avagin, Paul.Clothier, peterz, palves, oleg,
dhowells, tglx, davej, akpm, sukadev, davem, sam.bobroff
In-Reply-To: <1415683597-22819-3-git-send-email-khandual@linux.vnet.ibm.com>
On 11/11/2014 10:56 AM, Anshuman Khandual wrote:
> This patch creates a new function called flush_tm_state to flush
> the existing transactional memory state from the thread. It also
> creates a function called flush_tmregs_to_thread which will then
> be used on subsequent patches in this series.
>
> Signed-off-by: Anshuman Khandual <khandual@linux.vnet.ibm.com>
> ---
> arch/powerpc/include/asm/switch_to.h | 8 ++++++
> arch/powerpc/kernel/process.c | 49 +++++++++++++++++++++++++++++-------
> 2 files changed, 48 insertions(+), 9 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
> index 58abeda..23752a9 100644
> --- a/arch/powerpc/include/asm/switch_to.h
> +++ b/arch/powerpc/include/asm/switch_to.h
> @@ -82,6 +82,14 @@ static inline void flush_spe_to_thread(struct task_struct *t)
> }
> #endif
>
> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> +extern void flush_tmregs_to_thread(struct task_struct *);
> +#else
> +static inline void flush_tmregs_to_thread(struct task_struct *t)
> +{
> +}
> +#endif
> +
> static inline void clear_task_ebb(struct task_struct *t)
> {
> #ifdef CONFIG_PPC_BOOK3S_64
> diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
> index aa1df89..c36477b 100644
> --- a/arch/powerpc/kernel/process.c
> +++ b/arch/powerpc/kernel/process.c
> @@ -745,6 +745,45 @@ void restore_tm_state(struct pt_regs *regs)
> #define __switch_to_tm(prev)
> #endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
>
> +/*
> + * flush_tm_state
> + *
> + * Flush TM state out so we can copy it. __switch_to_tm() does this
> + * flush but it removes the checkpointed state from the current CPU and
> + * transitions the CPU out of TM mode. Hence we need to call
> + * tm_recheckpoint_new_task() (on the same task) to restore the
> + * checkpointed state back and the TM mode.
> + */
> +static void flush_tm_state(struct task_struct *src)
> +{
> + __switch_to_tm(src);
> + tm_recheckpoint_new_task(src);
> +}
> +
> +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> +void flush_tmregs_to_thread(struct task_struct *tsk)
> +{
> + /*
> + * If task is not current, it should have been flushed
> + * already to it's thread_struct during __switch_to().
> + */
> + if (tsk != current)
> + return;
As of now, ptrace does not support process self tracing. So here in this
function we really dont have to do anything if current and tsk does not
match each other. In case they match, we will just post one warning (as
ptrace generic code should have prevented this from happening) and move
on. Will make this function very simple like the following and remove
the next patch as well which was created because of this patch.
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
void flush_tmregs_to_thread(struct task_struct *tsk)
{
/*
* Self tracing is not yet supported through ptrace
* interface. Warn here if attempted.
*/
WARN_ON(tsk == current);
return;
}
#endif
^ permalink raw reply
* [PATCH 01/10] mm: numa: Do not dereference pmd outside of the lock during NUMA hinting fault
From: Mel Gorman @ 2014-11-21 13:57 UTC (permalink / raw)
To: Linux Kernel, Linux-MM, LinuxPPC-dev
Cc: Rik van Riel, Hugh Dickins, Ingo Molnar, Paul Mackerras,
Aneesh Kumar, Sasha Levin, Dave Jones, Linus Torvalds,
Kirill Shutemov, Mel Gorman
In-Reply-To: <1416578268-19597-1-git-send-email-mgorman@suse.de>
A transhuge NUMA hinting fault may find the page is migrating and should
wait until migration completes. The check is race-prone because the pmd
is deferenced outside of the page lock and while the race is tiny, it'll
be larger if the PMD is cleared while marking PMDs for hinting fault.
This patch closes the race.
Signed-off-by: Mel Gorman <mgorman@suse.de>
---
include/linux/migrate.h | 4 ----
mm/huge_memory.c | 3 ++-
mm/migrate.c | 6 ------
3 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index 01aad3e..a3edcdf 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -77,7 +77,6 @@ static inline int migrate_huge_page_move_mapping(struct address_space *mapping,
#ifdef CONFIG_NUMA_BALANCING
extern bool pmd_trans_migrating(pmd_t pmd);
-extern void wait_migrate_huge_page(struct anon_vma *anon_vma, pmd_t *pmd);
extern int migrate_misplaced_page(struct page *page,
struct vm_area_struct *vma, int node);
extern bool migrate_ratelimited(int node);
@@ -86,9 +85,6 @@ static inline bool pmd_trans_migrating(pmd_t pmd)
{
return false;
}
-static inline void wait_migrate_huge_page(struct anon_vma *anon_vma, pmd_t *pmd)
-{
-}
static inline int migrate_misplaced_page(struct page *page,
struct vm_area_struct *vma, int node)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 817a875..a2cd021 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1283,8 +1283,9 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
* check_same as the page may no longer be mapped.
*/
if (unlikely(pmd_trans_migrating(*pmdp))) {
+ page = pmd_page(*pmdp);
spin_unlock(ptl);
- wait_migrate_huge_page(vma->anon_vma, pmdp);
+ wait_on_page_locked(page);
goto out;
}
diff --git a/mm/migrate.c b/mm/migrate.c
index 41945cb..11d86b4 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1698,12 +1698,6 @@ bool pmd_trans_migrating(pmd_t pmd)
return PageLocked(page);
}
-void wait_migrate_huge_page(struct anon_vma *anon_vma, pmd_t *pmd)
-{
- struct page *page = pmd_page(*pmd);
- wait_on_page_locked(page);
-}
-
/*
* Attempt to migrate a misplaced page to the specified destination
* node. Caller is expected to have an elevated reference count on
--
2.1.2
^ permalink raw reply related
* [PATCH 0/10] Replace _PAGE_NUMA with PAGE_NONE protections v3
From: Mel Gorman @ 2014-11-21 13:57 UTC (permalink / raw)
To: Linux Kernel, Linux-MM, LinuxPPC-dev
Cc: Rik van Riel, Hugh Dickins, Ingo Molnar, Paul Mackerras,
Aneesh Kumar, Sasha Levin, Dave Jones, Linus Torvalds,
Kirill Shutemov, Mel Gorman
The main change here is to rebase on mmotm-20141119 as the series had
significant conflicts that were non-obvious to resolve. The main blockers
for merging are independent testing from Sasha (trinity), independent
testing from Aneesh (ppc64 support) and acks from Ben and Paul on the
powerpc patches.
Changelog since V2
o Rename *_protnone_numa to _protnone and extend docs (linus)
o Rebase to mmotm-20141119 for pre-merge testing (mel)
o Conver WARN_ON to VM_WARN_ON (aneesh)
Changelog since V1
o ppc64 paranoia checks and clarifications (aneesh)
o Fix trinity regression (hopefully)
o Reduce unnecessary TLB flushes (mel)
Automatic NUMA balancing depends on being able to protect PTEs to trap a
fault and gather reference locality information. Very broadly speaking it
would mark PTEs as not present and use another bit to distinguish between
NUMA hinting faults and other types of faults. It was universally loved
by everybody and caused no problems whatsoever. That last sentence might
be a lie.
This series is very heavily based on patches from Linus and Aneesh to
replace the existing PTE/PMD NUMA helper functions with normal change
protections. I did alter and add parts of it but I consider them relatively
minor contributions. At their suggestion, acked-bys are in there but I've
no problem converting them to Signed-off-by if requested.
AFAIK, this has received no testing on ppc64 and I'm depending on Aneesh for
that. I tested trinity under kvm-tool and passed and ran a few other basic
tests. At the time of writing, only the short-lived tests have completed
but testing of V2 indicated that long-term testing had no surprises. In
most cases I'm leaving out detail as it's not that interesting.
specjbb single JVM: There was negligible performance difference in the
benchmark itself for short runs. However, system activity is
higher and interrupts are much higher over time -- possibly TLB
flushes. Migrations are also higher. Overall, this is more overhead
but considering the problems faced with the old approach I think
we just have to suck it up and find another way of reducing the
overhead.
specjbb multi JVM: Negligible performance difference to the actual benchmark
but like the single JVM case, the system overhead is noticeably
higher. Again, interrupts are a major factor.
autonumabench: This was all over the place and about all that can be
reasonably concluded is that it's different but not necessarily
better or worse.
autonumabench
3.18.0-rc5 3.18.0-rc5
mmotm-20141119 protnone-v3r3
User NUMA01 32380.24 ( 0.00%) 21642.92 ( 33.16%)
User NUMA01_THEADLOCAL 22481.02 ( 0.00%) 22283.22 ( 0.88%)
User NUMA02 3137.00 ( 0.00%) 3116.54 ( 0.65%)
User NUMA02_SMT 1614.03 ( 0.00%) 1543.53 ( 4.37%)
System NUMA01 322.97 ( 0.00%) 1465.89 (-353.88%)
System NUMA01_THEADLOCAL 91.87 ( 0.00%) 49.32 ( 46.32%)
System NUMA02 37.83 ( 0.00%) 14.61 ( 61.38%)
System NUMA02_SMT 7.36 ( 0.00%) 7.45 ( -1.22%)
Elapsed NUMA01 716.63 ( 0.00%) 599.29 ( 16.37%)
Elapsed NUMA01_THEADLOCAL 553.98 ( 0.00%) 539.94 ( 2.53%)
Elapsed NUMA02 83.85 ( 0.00%) 83.04 ( 0.97%)
Elapsed NUMA02_SMT 86.57 ( 0.00%) 79.15 ( 8.57%)
CPU NUMA01 4563.00 ( 0.00%) 3855.00 ( 15.52%)
CPU NUMA01_THEADLOCAL 4074.00 ( 0.00%) 4136.00 ( -1.52%)
CPU NUMA02 3785.00 ( 0.00%) 3770.00 ( 0.40%)
CPU NUMA02_SMT 1872.00 ( 0.00%) 1959.00 ( -4.65%)
System CPU usage of NUMA01 is worse but it's an adverse workload on this
machine so I'm reluctant to conclude that it's a problem that matters. On
the other workloads that are sensible on this machine, system CPU usage
is great. Overall time to complete the benchmark is comparable
3.18.0-rc5 3.18.0-rc5
mmotm-20141119protnone-v3r3
User 59612.50 48586.44
System 460.22 1537.45
Elapsed 1442.20 1304.29
NUMA alloc hit 5075182 5743353
NUMA alloc miss 0 0
NUMA interleave hit 0 0
NUMA alloc local 5075174 5743339
NUMA base PTE updates 637061448 443106883
NUMA huge PMD updates 1243434 864747
NUMA page range updates 1273699656 885857347
NUMA hint faults 1658116 1214277
NUMA hint local faults 959487 754113
NUMA hint local percent 57 62
NUMA pages migrated 5467056 61676398
The NUMA pages migrated look terrible but when I looked at a graph of the
activity over time I see that the massive spike in migration activity was
during NUMA01. This correlates with high system CPU usage and could be simply
down to bad luck but any modifications that affect that workload would be
related to scan rates and migrations, not the protection mechanism. For
all other workloads, migration activity was comparable.
Overall, headline performance figures are comparable but the overhead
is higher, mostly in interrupts. To some extent, higher overhead from
this approach was anticipated but not to this degree. It's going to be
necessary to reduce this again with a separate series in the future. It's
still worth going ahead with this series though as it's likely to avoid
constant headaches with Xen and is probably easier to maintain.
arch/powerpc/include/asm/pgtable.h | 53 ++----------
arch/powerpc/include/asm/pte-common.h | 5 --
arch/powerpc/include/asm/pte-hash64.h | 6 --
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 2 +-
arch/powerpc/mm/copro_fault.c | 8 +-
arch/powerpc/mm/fault.c | 25 ++----
arch/powerpc/mm/pgtable.c | 11 ++-
arch/powerpc/mm/pgtable_64.c | 3 +-
arch/x86/include/asm/pgtable.h | 46 +++++-----
arch/x86/include/asm/pgtable_64.h | 5 --
arch/x86/include/asm/pgtable_types.h | 41 +--------
arch/x86/mm/gup.c | 4 +-
include/asm-generic/pgtable.h | 153 ++--------------------------------
include/linux/migrate.h | 4 -
include/linux/swapops.h | 2 +-
include/uapi/linux/mempolicy.h | 2 +-
mm/gup.c | 10 +--
mm/huge_memory.c | 50 ++++++-----
mm/memory.c | 18 ++--
mm/mempolicy.c | 2 +-
mm/migrate.c | 8 +-
mm/mprotect.c | 48 +++++------
mm/pgtable-generic.c | 2 -
23 files changed, 134 insertions(+), 374 deletions(-)
--
2.1.2
^ permalink raw reply
* [PATCH 02/10] mm: Add p[te|md] protnone helpers for use by NUMA balancing
From: Mel Gorman @ 2014-11-21 13:57 UTC (permalink / raw)
To: Linux Kernel, Linux-MM, LinuxPPC-dev
Cc: Rik van Riel, Hugh Dickins, Ingo Molnar, Paul Mackerras,
Aneesh Kumar, Sasha Levin, Dave Jones, Linus Torvalds,
Kirill Shutemov, Mel Gorman
In-Reply-To: <1416578268-19597-1-git-send-email-mgorman@suse.de>
This is a preparatory patch that introduces protnone helpers for automatic
NUMA balancing.
Signed-off-by: Mel Gorman <mgorman@suse.de>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/pgtable.h | 15 +++++++++++++++
arch/x86/include/asm/pgtable.h | 16 ++++++++++++++++
include/asm-generic/pgtable.h | 20 ++++++++++++++++++++
3 files changed, 51 insertions(+)
diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h
index a8805fe..490bd6d 100644
--- a/arch/powerpc/include/asm/pgtable.h
+++ b/arch/powerpc/include/asm/pgtable.h
@@ -39,6 +39,21 @@ static inline int pte_none(pte_t pte) { return (pte_val(pte) & ~_PTE_NONE_MASK)
static inline pgprot_t pte_pgprot(pte_t pte) { return __pgprot(pte_val(pte) & PAGE_PROT_BITS); }
#ifdef CONFIG_NUMA_BALANCING
+/*
+ * These work without NUMA balancing but the kernel does not care. See the
+ * comment in include/asm-generic/pgtable.h
+ */
+static inline int pte_protnone(pte_t pte)
+{
+ return (pte_val(pte) &
+ (_PAGE_PRESENT | _PAGE_USER)) == _PAGE_PRESENT;
+}
+
+static inline int pmd_protnone(pmd_t pmd)
+{
+ return pte_protnone(pmd_pte(pmd));
+}
+
static inline int pte_present(pte_t pte)
{
return pte_val(pte) & _PAGE_NUMA_MASK;
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 081d6f4..2e25780 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -502,6 +502,22 @@ static inline int pmd_present(pmd_t pmd)
_PAGE_NUMA);
}
+#ifdef CONFIG_NUMA_BALANCING
+/*
+ * These work without NUMA balancing but the kernel does not care. See the
+ * comment in include/asm-generic/pgtable.h
+ */
+static inline int pte_protnone(pte_t pte)
+{
+ return pte_flags(pte) & _PAGE_PROTNONE;
+}
+
+static inline int pmd_protnone(pmd_t pmd)
+{
+ return pmd_flags(pmd) & _PAGE_PROTNONE;
+}
+#endif /* CONFIG_NUMA_BALANCING */
+
static inline int pmd_none(pmd_t pmd)
{
/* Only check low word on 32-bit platforms, since it might be
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 177d597..d497d08 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -688,6 +688,26 @@ static inline int pmd_trans_unstable(pmd_t *pmd)
#endif
}
+#ifndef CONFIG_NUMA_BALANCING
+/*
+ * Technically a PTE can be PROTNONE even when not doing NUMA balancing but
+ * the only case the kernel cares is for NUMA balancing and is only ever set
+ * when the VMA is accessible. For PROT_NONE VMAs, the PTEs are not marked
+ * _PAGE_PROTNONE so by by default, implement the helper as "always no". It
+ * is the responsibility of the caller to distinguish between PROT_NONE
+ * protections and NUMA hinting fault protections.
+ */
+static inline int pte_protnone(pte_t pte)
+{
+ return 0;
+}
+
+static inline int pmd_protnone(pmd_t pmd)
+{
+ return 0;
+}
+#endif /* CONFIG_NUMA_BALANCING */
+
#ifdef CONFIG_NUMA_BALANCING
/*
* _PAGE_NUMA distinguishes between an unmapped page table entry, an entry that
--
2.1.2
^ permalink raw reply related
* [PATCH 03/10] mm: Convert p[te|md]_numa users to p[te|md]_protnone_numa
From: Mel Gorman @ 2014-11-21 13:57 UTC (permalink / raw)
To: Linux Kernel, Linux-MM, LinuxPPC-dev
Cc: Rik van Riel, Hugh Dickins, Ingo Molnar, Paul Mackerras,
Aneesh Kumar, Sasha Levin, Dave Jones, Linus Torvalds,
Kirill Shutemov, Mel Gorman
In-Reply-To: <1416578268-19597-1-git-send-email-mgorman@suse.de>
Convert existing users of pte_numa and friends to the new helper. Note
that the kernel is broken after this patch is applied until the other
page table modifiers are also altered. This patch layout is to make
review easier.
Signed-off-by: Mel Gorman <mgorman@suse.de>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Aneesh Kumar <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/kvm/book3s_hv_rm_mmu.c | 2 +-
arch/powerpc/mm/fault.c | 5 -----
arch/powerpc/mm/pgtable.c | 11 ++++++++---
arch/powerpc/mm/pgtable_64.c | 3 ++-
arch/x86/mm/gup.c | 4 ++--
include/uapi/linux/mempolicy.h | 2 +-
mm/gup.c | 10 +++++-----
mm/huge_memory.c | 16 +++++++--------
mm/memory.c | 4 ++--
mm/mprotect.c | 39 ++++++++++---------------------------
mm/pgtable-generic.c | 2 +-
11 files changed, 40 insertions(+), 58 deletions(-)
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 084ad54..3e6ad3f 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -235,7 +235,7 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
pte_size = psize;
pte = lookup_linux_pte_and_update(pgdir, hva, writing,
&pte_size);
- if (pte_present(pte) && !pte_numa(pte)) {
+ if (pte_present(pte) && !pte_protnone(pte)) {
if (writing && !pte_write(pte))
/* make the actual HPTE be read-only */
ptel = hpte_make_readonly(ptel);
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index eb79907..b434153 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -398,8 +398,6 @@ good_area:
* processors use the same I/D cache coherency mechanism
* as embedded.
*/
- if (error_code & DSISR_PROTFAULT)
- goto bad_area;
#endif /* CONFIG_PPC_STD_MMU */
/*
@@ -423,9 +421,6 @@ good_area:
flags |= FAULT_FLAG_WRITE;
/* a read */
} else {
- /* protection fault */
- if (error_code & 0x08000000)
- goto bad_area;
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto bad_area;
}
diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c
index c90e602..83dfcb5 100644
--- a/arch/powerpc/mm/pgtable.c
+++ b/arch/powerpc/mm/pgtable.c
@@ -172,9 +172,14 @@ static pte_t set_access_flags_filter(pte_t pte, struct vm_area_struct *vma,
void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
pte_t pte)
{
-#ifdef CONFIG_DEBUG_VM
- WARN_ON(pte_val(*ptep) & _PAGE_PRESENT);
-#endif
+ /*
+ * When handling numa faults, we already have the pte marked
+ * _PAGE_PRESENT, but we can be sure that it is not in hpte.
+ * Hence we can use set_pte_at for them.
+ */
+ VM_WARN_ON((pte_val(*ptep) & (_PAGE_PRESENT | _PAGE_USER)) ==
+ (_PAGE_PRESENT | _PAGE_USER));
+
/* Note: mm->context.id might not yet have been assigned as
* this context might not have been activated yet when this
* is called.
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index 87ff0c1..435ebf7 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -718,7 +718,8 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t pmd)
{
#ifdef CONFIG_DEBUG_VM
- WARN_ON(pmd_val(*pmdp) & _PAGE_PRESENT);
+ WARN_ON((pmd_val(*pmdp) & (_PAGE_PRESENT | _PAGE_USER)) ==
+ (_PAGE_PRESENT | _PAGE_USER));
assert_spin_locked(&mm->page_table_lock);
WARN_ON(!pmd_trans_huge(pmd));
#endif
diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c
index 207d9aef..f32e12c 100644
--- a/arch/x86/mm/gup.c
+++ b/arch/x86/mm/gup.c
@@ -84,7 +84,7 @@ static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
struct page *page;
/* Similar to the PMD case, NUMA hinting must take slow path */
- if (pte_numa(pte)) {
+ if (pte_protnone(pte)) {
pte_unmap(ptep);
return 0;
}
@@ -178,7 +178,7 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
* slowpath for accounting purposes and so that they
* can be serialised against THP migration.
*/
- if (pmd_numa(pmd))
+ if (pmd_protnone(pmd))
return 0;
if (!gup_huge_pmd(pmd, addr, next, write, pages, nr))
return 0;
diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h
index 0d11c3d..9cd8b21 100644
--- a/include/uapi/linux/mempolicy.h
+++ b/include/uapi/linux/mempolicy.h
@@ -67,7 +67,7 @@ enum mpol_rebind_step {
#define MPOL_F_LOCAL (1 << 1) /* preferred local allocation */
#define MPOL_F_REBINDING (1 << 2) /* identify policies in rebinding */
#define MPOL_F_MOF (1 << 3) /* this policy wants migrate on fault */
-#define MPOL_F_MORON (1 << 4) /* Migrate On pte_numa Reference On Node */
+#define MPOL_F_MORON (1 << 4) /* Migrate On protnone Reference On Node */
#endif /* _UAPI_LINUX_MEMPOLICY_H */
diff --git a/mm/gup.c b/mm/gup.c
index 0ca1df9..e5dab89 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -64,7 +64,7 @@ retry:
migration_entry_wait(mm, pmd, address);
goto retry;
}
- if ((flags & FOLL_NUMA) && pte_numa(pte))
+ if ((flags & FOLL_NUMA) && pte_protnone(pte))
goto no_page;
if ((flags & FOLL_WRITE) && !pte_write(pte)) {
pte_unmap_unlock(ptep, ptl);
@@ -193,7 +193,7 @@ struct page *follow_page_mask(struct vm_area_struct *vma,
}
return page;
}
- if ((flags & FOLL_NUMA) && pmd_numa(*pmd))
+ if ((flags & FOLL_NUMA) && pmd_protnone(*pmd))
return no_page_table(vma, flags);
if (pmd_trans_huge(*pmd)) {
if (flags & FOLL_SPLIT) {
@@ -740,10 +740,10 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end,
/*
* Similar to the PMD case below, NUMA hinting must take slow
- * path
+ * path using the pte_protnone check.
*/
if (!pte_present(pte) || pte_special(pte) ||
- pte_numa(pte) || (write && !pte_write(pte)))
+ pte_protnone(pte) || (write && !pte_write(pte)))
goto pte_unmap;
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
@@ -938,7 +938,7 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
* slowpath for accounting purposes and so that they
* can be serialised against THP migration.
*/
- if (pmd_numa(pmd))
+ if (pmd_protnone(pmd))
return 0;
if (!gup_huge_pmd(pmd, pmdp, addr, next, write,
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index a2cd021..f81fddf 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1222,7 +1222,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
return ERR_PTR(-EFAULT);
/* Full NUMA hinting faults to serialise migration in fault paths */
- if ((flags & FOLL_NUMA) && pmd_numa(*pmd))
+ if ((flags & FOLL_NUMA) && pmd_protnone(*pmd))
goto out;
page = pmd_page(*pmd);
@@ -1353,7 +1353,7 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
/*
* Migrate the THP to the requested node, returns with page unlocked
- * and pmd_numa cleared.
+ * and access rights restored.
*/
spin_unlock(ptl);
migrated = migrate_misplaced_transhuge_page(mm, vma,
@@ -1368,7 +1368,7 @@ clear_pmdnuma:
BUG_ON(!PageLocked(page));
pmd = pmd_mknonnuma(pmd);
set_pmd_at(mm, haddr, pmdp, pmd);
- VM_BUG_ON(pmd_numa(*pmdp));
+ VM_BUG_ON(pmd_protnone(*pmdp));
update_mmu_cache_pmd(vma, addr, pmdp);
unlock_page(page);
out_unlock:
@@ -1514,7 +1514,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
ret = 1;
if (!prot_numa) {
entry = pmdp_get_and_clear_notify(mm, addr, pmd);
- if (pmd_numa(entry))
+ if (pmd_protnone(entry))
entry = pmd_mknonnuma(entry);
entry = pmd_modify(entry, newprot);
ret = HPAGE_PMD_NR;
@@ -1530,7 +1530,7 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
* local vs remote hits on the zero page.
*/
if (!is_huge_zero_page(page) &&
- !pmd_numa(*pmd)) {
+ !pmd_protnone(*pmd)) {
pmdp_set_numa(mm, addr, pmd);
ret = HPAGE_PMD_NR;
}
@@ -1798,9 +1798,9 @@ static int __split_huge_page_map(struct page *page,
pte_t *pte, entry;
BUG_ON(PageCompound(page+i));
/*
- * Note that pmd_numa is not transferred deliberately
- * to avoid any possibility that pte_numa leaks to
- * a PROT_NONE VMA by accident.
+ * Note that NUMA hinting access restrictions are not
+ * transferred to avoid any possibility of altering
+ * permissions across VMAs.
*/
entry = mk_pte(page + i, vma->vm_page_prot);
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
diff --git a/mm/memory.c b/mm/memory.c
index ae923f5..eaa46f1 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3214,7 +3214,7 @@ static int handle_pte_fault(struct mm_struct *mm,
pte, pmd, flags, entry);
}
- if (pte_numa(entry))
+ if (pte_protnone(entry))
return do_numa_page(mm, vma, address, entry, pte, pmd);
ptl = pte_lockptr(mm, pmd);
@@ -3292,7 +3292,7 @@ static int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
if (pmd_trans_splitting(orig_pmd))
return 0;
- if (pmd_numa(orig_pmd))
+ if (pmd_protnone(orig_pmd))
return do_huge_pmd_numa_page(mm, vma, address,
orig_pmd, pmd);
diff --git a/mm/mprotect.c b/mm/mprotect.c
index ace9345..e93ddac 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -75,36 +75,17 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
oldpte = *pte;
if (pte_present(oldpte)) {
pte_t ptent;
- bool updated = false;
-
- if (!prot_numa) {
- ptent = ptep_modify_prot_start(mm, addr, pte);
- if (pte_numa(ptent))
- ptent = pte_mknonnuma(ptent);
- ptent = pte_modify(ptent, newprot);
- /*
- * Avoid taking write faults for pages we
- * know to be dirty.
- */
- if (dirty_accountable && pte_dirty(ptent) &&
- (pte_soft_dirty(ptent) ||
- !(vma->vm_flags & VM_SOFTDIRTY)))
- ptent = pte_mkwrite(ptent);
- ptep_modify_prot_commit(mm, addr, pte, ptent);
- updated = true;
- } else {
- struct page *page;
-
- page = vm_normal_page(vma, addr, oldpte);
- if (page && !PageKsm(page)) {
- if (!pte_numa(oldpte)) {
- ptep_set_numa(mm, addr, pte);
- updated = true;
- }
- }
+ ptent = ptep_modify_prot_start(mm, addr, pte);
+ ptent = pte_modify(ptent, newprot);
+
+ /* Avoid taking write faults for known dirty pages */
+ if (dirty_accountable && pte_dirty(ptent) &&
+ (pte_soft_dirty(ptent) ||
+ !(vma->vm_flags & VM_SOFTDIRTY))) {
+ ptent = pte_mkwrite(ptent);
}
- if (updated)
- pages++;
+ ptep_modify_prot_commit(mm, addr, pte, ptent);
+ pages++;
} else if (IS_ENABLED(CONFIG_MIGRATION) && !pte_file(oldpte)) {
swp_entry_t entry = pte_to_swp_entry(oldpte);
diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
index dfb79e0..4b8ad76 100644
--- a/mm/pgtable-generic.c
+++ b/mm/pgtable-generic.c
@@ -193,7 +193,7 @@ void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
pmd_t *pmdp)
{
pmd_t entry = *pmdp;
- if (pmd_numa(entry))
+ if (pmd_protnone(entry))
entry = pmd_mknonnuma(entry);
set_pmd_at(vma->vm_mm, address, pmdp, pmd_mknotpresent(entry));
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
--
2.1.2
^ permalink raw reply related
* [PATCH 04/10] ppc64: Add paranoid warnings for unexpected DSISR_PROTFAULT
From: Mel Gorman @ 2014-11-21 13:57 UTC (permalink / raw)
To: Linux Kernel, Linux-MM, LinuxPPC-dev
Cc: Rik van Riel, Hugh Dickins, Ingo Molnar, Paul Mackerras,
Aneesh Kumar, Sasha Levin, Dave Jones, Linus Torvalds,
Kirill Shutemov, Mel Gorman
In-Reply-To: <1416578268-19597-1-git-send-email-mgorman@suse.de>
ppc64 should not be depending on DSISR_PROTFAULT and it's unexpected
if they are triggered. This patch adds warnings just in case they
are being accidentally depended upon.
Signed-off-by: Mel Gorman <mgorman@suse.de>
Acked-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
arch/powerpc/mm/copro_fault.c | 8 ++++++--
arch/powerpc/mm/fault.c | 20 +++++++++-----------
2 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c
index 5a236f0..0450d68 100644
--- a/arch/powerpc/mm/copro_fault.c
+++ b/arch/powerpc/mm/copro_fault.c
@@ -64,10 +64,14 @@ int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
if (!(vma->vm_flags & VM_WRITE))
goto out_unlock;
} else {
- if (dsisr & DSISR_PROTFAULT)
- goto out_unlock;
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto out_unlock;
+ /*
+ * protfault should only happen due to us
+ * mapping a region readonly temporarily. PROT_NONE
+ * is also covered by the VMA check above.
+ */
+ WARN_ON_ONCE(dsisr & DSISR_PROTFAULT);
}
ret = 0;
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index b434153..1bcd378 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -389,17 +389,6 @@ good_area:
#endif /* CONFIG_8xx */
if (is_exec) {
-#ifdef CONFIG_PPC_STD_MMU
- /* Protection fault on exec go straight to failure on
- * Hash based MMUs as they either don't support per-page
- * execute permission, or if they do, it's handled already
- * at the hash level. This test would probably have to
- * be removed if we change the way this works to make hash
- * processors use the same I/D cache coherency mechanism
- * as embedded.
- */
-#endif /* CONFIG_PPC_STD_MMU */
-
/*
* Allow execution from readable areas if the MMU does not
* provide separate controls over reading and executing.
@@ -414,6 +403,14 @@ good_area:
(cpu_has_feature(CPU_FTR_NOEXECUTE) ||
!(vma->vm_flags & (VM_READ | VM_WRITE))))
goto bad_area;
+#ifdef CONFIG_PPC_STD_MMU
+ /*
+ * protfault should only happen due to us
+ * mapping a region readonly temporarily. PROT_NONE
+ * is also covered by the VMA check above.
+ */
+ WARN_ON_ONCE(error_code & DSISR_PROTFAULT);
+#endif /* CONFIG_PPC_STD_MMU */
/* a write */
} else if (is_write) {
if (!(vma->vm_flags & VM_WRITE))
@@ -423,6 +420,7 @@ good_area:
} else {
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto bad_area;
+ WARN_ON_ONCE(error_code & DSISR_PROTFAULT);
}
/*
--
2.1.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox