|
@@ -8,6 +8,8 @@
|
|
|
|
|
|
#include <linux/workqueue.h>
|
|
|
#include <linux/bootmem.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <linux/sysctl.h>
|
|
|
#include <linux/cpuset.h>
|
|
|
#include <linux/device.h>
|
|
|
#include <linux/export.h>
|
|
@@ -29,12 +31,20 @@
|
|
|
#define PTF_VERTICAL (1UL)
|
|
|
#define PTF_CHECK (2UL)
|
|
|
|
|
|
+enum {
|
|
|
+ TOPOLOGY_MODE_HW,
|
|
|
+ TOPOLOGY_MODE_SINGLE,
|
|
|
+ TOPOLOGY_MODE_PACKAGE,
|
|
|
+ TOPOLOGY_MODE_UNINITIALIZED
|
|
|
+};
|
|
|
+
|
|
|
struct mask_info {
|
|
|
struct mask_info *next;
|
|
|
unsigned char id;
|
|
|
cpumask_t mask;
|
|
|
};
|
|
|
|
|
|
+static int topology_mode = TOPOLOGY_MODE_UNINITIALIZED;
|
|
|
static void set_topology_timer(void);
|
|
|
static void topology_work_fn(struct work_struct *work);
|
|
|
static struct sysinfo_15_1_x *tl_info;
|
|
@@ -59,11 +69,26 @@ static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
|
|
|
cpumask_t mask;
|
|
|
|
|
|
cpumask_copy(&mask, cpumask_of(cpu));
|
|
|
- if (!MACHINE_HAS_TOPOLOGY)
|
|
|
- return mask;
|
|
|
- for (; info; info = info->next) {
|
|
|
- if (cpumask_test_cpu(cpu, &info->mask))
|
|
|
- return info->mask;
|
|
|
+ switch (topology_mode) {
|
|
|
+ case TOPOLOGY_MODE_HW:
|
|
|
+ while (info) {
|
|
|
+ if (cpumask_test_cpu(cpu, &info->mask)) {
|
|
|
+ mask = info->mask;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ info = info->next;
|
|
|
+ }
|
|
|
+ if (cpumask_empty(&mask))
|
|
|
+ cpumask_copy(&mask, cpumask_of(cpu));
|
|
|
+ break;
|
|
|
+ case TOPOLOGY_MODE_PACKAGE:
|
|
|
+ cpumask_copy(&mask, cpu_present_mask);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* fallthrough */
|
|
|
+ case TOPOLOGY_MODE_SINGLE:
|
|
|
+ cpumask_copy(&mask, cpumask_of(cpu));
|
|
|
+ break;
|
|
|
}
|
|
|
return mask;
|
|
|
}
|
|
@@ -74,7 +99,7 @@ static cpumask_t cpu_thread_map(unsigned int cpu)
|
|
|
int i;
|
|
|
|
|
|
cpumask_copy(&mask, cpumask_of(cpu));
|
|
|
- if (!MACHINE_HAS_TOPOLOGY)
|
|
|
+ if (topology_mode != TOPOLOGY_MODE_HW)
|
|
|
return mask;
|
|
|
cpu -= cpu % (smp_cpu_mtid + 1);
|
|
|
for (i = 0; i <= smp_cpu_mtid; i++)
|
|
@@ -184,10 +209,8 @@ static void topology_update_polarization_simple(void)
|
|
|
{
|
|
|
int cpu;
|
|
|
|
|
|
- mutex_lock(&smp_cpu_state_mutex);
|
|
|
for_each_possible_cpu(cpu)
|
|
|
smp_cpu_set_polarization(cpu, POLARIZATION_HRZ);
|
|
|
- mutex_unlock(&smp_cpu_state_mutex);
|
|
|
}
|
|
|
|
|
|
static int ptf(unsigned long fc)
|
|
@@ -223,7 +246,7 @@ int topology_set_cpu_management(int fc)
|
|
|
static void update_cpu_masks(void)
|
|
|
{
|
|
|
struct cpu_topology_s390 *topo;
|
|
|
- int cpu;
|
|
|
+ int cpu, id;
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
topo = &cpu_topology[cpu];
|
|
@@ -231,12 +254,13 @@ static void update_cpu_masks(void)
|
|
|
topo->core_mask = cpu_group_map(&socket_info, cpu);
|
|
|
topo->book_mask = cpu_group_map(&book_info, cpu);
|
|
|
topo->drawer_mask = cpu_group_map(&drawer_info, cpu);
|
|
|
- if (!MACHINE_HAS_TOPOLOGY) {
|
|
|
+ if (topology_mode != TOPOLOGY_MODE_HW) {
|
|
|
+ id = topology_mode == TOPOLOGY_MODE_PACKAGE ? 0 : cpu;
|
|
|
topo->thread_id = cpu;
|
|
|
topo->core_id = cpu;
|
|
|
- topo->socket_id = cpu;
|
|
|
- topo->book_id = cpu;
|
|
|
- topo->drawer_id = cpu;
|
|
|
+ topo->socket_id = id;
|
|
|
+ topo->book_id = id;
|
|
|
+ topo->drawer_id = id;
|
|
|
if (cpu_present(cpu))
|
|
|
cpumask_set_cpu(cpu, &cpus_with_topology);
|
|
|
}
|
|
@@ -254,6 +278,7 @@ static int __arch_update_cpu_topology(void)
|
|
|
struct sysinfo_15_1_x *info = tl_info;
|
|
|
int rc = 0;
|
|
|
|
|
|
+ mutex_lock(&smp_cpu_state_mutex);
|
|
|
cpumask_clear(&cpus_with_topology);
|
|
|
if (MACHINE_HAS_TOPOLOGY) {
|
|
|
rc = 1;
|
|
@@ -263,6 +288,7 @@ static int __arch_update_cpu_topology(void)
|
|
|
update_cpu_masks();
|
|
|
if (!MACHINE_HAS_TOPOLOGY)
|
|
|
topology_update_polarization_simple();
|
|
|
+ mutex_unlock(&smp_cpu_state_mutex);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
@@ -289,6 +315,11 @@ void topology_schedule_update(void)
|
|
|
schedule_work(&topology_work);
|
|
|
}
|
|
|
|
|
|
+static void topology_flush_work(void)
|
|
|
+{
|
|
|
+ flush_work(&topology_work);
|
|
|
+}
|
|
|
+
|
|
|
static void topology_timer_fn(unsigned long ignored)
|
|
|
{
|
|
|
if (ptf(PTF_CHECK))
|
|
@@ -459,6 +490,12 @@ void __init topology_init_early(void)
|
|
|
struct sysinfo_15_1_x *info;
|
|
|
|
|
|
set_sched_topology(s390_topology);
|
|
|
+ if (topology_mode == TOPOLOGY_MODE_UNINITIALIZED) {
|
|
|
+ if (MACHINE_HAS_TOPOLOGY)
|
|
|
+ topology_mode = TOPOLOGY_MODE_HW;
|
|
|
+ else
|
|
|
+ topology_mode = TOPOLOGY_MODE_SINGLE;
|
|
|
+ }
|
|
|
if (!MACHINE_HAS_TOPOLOGY)
|
|
|
goto out;
|
|
|
tl_info = memblock_virt_alloc(PAGE_SIZE, PAGE_SIZE);
|
|
@@ -474,12 +511,97 @@ out:
|
|
|
__arch_update_cpu_topology();
|
|
|
}
|
|
|
|
|
|
+static inline int topology_get_mode(int enabled)
|
|
|
+{
|
|
|
+ if (!enabled)
|
|
|
+ return TOPOLOGY_MODE_SINGLE;
|
|
|
+ return MACHINE_HAS_TOPOLOGY ? TOPOLOGY_MODE_HW : TOPOLOGY_MODE_PACKAGE;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int topology_is_enabled(void)
|
|
|
+{
|
|
|
+ return topology_mode != TOPOLOGY_MODE_SINGLE;
|
|
|
+}
|
|
|
+
|
|
|
+static int __init topology_setup(char *str)
|
|
|
+{
|
|
|
+ bool enabled;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = kstrtobool(str, &enabled);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ topology_mode = topology_get_mode(enabled);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+early_param("topology", topology_setup);
|
|
|
+
|
|
|
+static int topology_ctl_handler(struct ctl_table *ctl, int write,
|
|
|
+ void __user *buffer, size_t *lenp, loff_t *ppos)
|
|
|
+{
|
|
|
+ unsigned int len;
|
|
|
+ int new_mode;
|
|
|
+ char buf[2];
|
|
|
+
|
|
|
+ if (!*lenp || *ppos) {
|
|
|
+ *lenp = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (!write) {
|
|
|
+ strncpy(buf, topology_is_enabled() ? "1\n" : "0\n",
|
|
|
+ ARRAY_SIZE(buf));
|
|
|
+ len = strnlen(buf, ARRAY_SIZE(buf));
|
|
|
+ if (len > *lenp)
|
|
|
+ len = *lenp;
|
|
|
+ if (copy_to_user(buffer, buf, len))
|
|
|
+ return -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ len = *lenp;
|
|
|
+ if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
|
|
|
+ return -EFAULT;
|
|
|
+ if (buf[0] != '0' && buf[0] != '1')
|
|
|
+ return -EINVAL;
|
|
|
+ mutex_lock(&smp_cpu_state_mutex);
|
|
|
+ new_mode = topology_get_mode(buf[0] == '1');
|
|
|
+ if (topology_mode != new_mode) {
|
|
|
+ topology_mode = new_mode;
|
|
|
+ topology_schedule_update();
|
|
|
+ }
|
|
|
+ mutex_unlock(&smp_cpu_state_mutex);
|
|
|
+ topology_flush_work();
|
|
|
+out:
|
|
|
+ *lenp = len;
|
|
|
+ *ppos += len;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct ctl_table topology_ctl_table[] = {
|
|
|
+ {
|
|
|
+ .procname = "topology",
|
|
|
+ .mode = 0644,
|
|
|
+ .proc_handler = topology_ctl_handler,
|
|
|
+ },
|
|
|
+ { },
|
|
|
+};
|
|
|
+
|
|
|
+static struct ctl_table topology_dir_table[] = {
|
|
|
+ {
|
|
|
+ .procname = "s390",
|
|
|
+ .maxlen = 0,
|
|
|
+ .mode = 0555,
|
|
|
+ .child = topology_ctl_table,
|
|
|
+ },
|
|
|
+ { },
|
|
|
+};
|
|
|
+
|
|
|
static int __init topology_init(void)
|
|
|
{
|
|
|
if (MACHINE_HAS_TOPOLOGY)
|
|
|
set_topology_timer();
|
|
|
else
|
|
|
topology_update_polarization_simple();
|
|
|
+ register_sysctl_table(topology_dir_table);
|
|
|
return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching);
|
|
|
}
|
|
|
device_initcall(topology_init);
|