topology.c 9.6 KB
Newer Older
1
/*
2
 *    Copyright IBM Corp. 2007,2011
3 4 5
 *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
 */

6 7 8
#define KMSG_COMPONENT "cpu"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

9
#include <linux/workqueue.h>
10
#include <linux/bootmem.h>
11 12 13
#include <linux/cpuset.h>
#include <linux/device.h>
#include <linux/kernel.h>
14
#include <linux/sched.h>
15 16
#include <linux/init.h>
#include <linux/delay.h>
17 18
#include <linux/cpu.h>
#include <linux/smp.h>
19
#include <linux/mm.h>
20

H
Heiko Carstens 已提交
21 22 23
#define PTF_HORIZONTAL	(0UL)
#define PTF_VERTICAL	(1UL)
#define PTF_CHECK	(2UL)
24

25 26
struct mask_info {
	struct mask_info *next;
27
	unsigned char id;
28 29 30
	cpumask_t mask;
};

H
Heiko Carstens 已提交
31
static int topology_enabled = 1;
32
static void topology_work_fn(struct work_struct *work);
33
static struct sysinfo_15_1_x *tl_info;
34 35 36
static struct timer_list topology_timer;
static void set_topology_timer(void);
static DECLARE_WORK(topology_work, topology_work_fn);
H
Heiko Carstens 已提交
37 38
/* topology_lock protects the core linked list */
static DEFINE_SPINLOCK(topology_lock);
39

40
static struct mask_info core_info;
41
cpumask_t cpu_core_map[NR_CPUS];
42
unsigned char cpu_core_id[NR_CPUS];
43

44 45 46
static struct mask_info book_info;
cpumask_t cpu_book_map[NR_CPUS];
unsigned char cpu_book_id[NR_CPUS];
47 48 49

/* smp_cpu_state_mutex must be held when accessing this array */
int cpu_polarization[NR_CPUS];
50 51

static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
52 53 54
{
	cpumask_t mask;

55
	cpumask_clear(&mask);
56 57 58 59
	if (!topology_enabled || !MACHINE_HAS_TOPOLOGY) {
		cpumask_copy(&mask, cpumask_of(cpu));
		return mask;
	}
60
	while (info) {
61
		if (cpumask_test_cpu(cpu, &info->mask)) {
62
			mask = info->mask;
63 64
			break;
		}
65
		info = info->next;
66
	}
67 68
	if (cpumask_empty(&mask))
		cpumask_copy(&mask, cpumask_of(cpu));
69 70 71
	return mask;
}

72 73 74
static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
					  struct mask_info *book,
					  struct mask_info *core,
75
					  int one_core_per_cpu)
76 77 78
{
	unsigned int cpu;

79 80 81
	for (cpu = find_first_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS);
	     cpu < TOPOLOGY_CPU_BITS;
	     cpu = find_next_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS, cpu + 1))
82 83 84
	{
		unsigned int rcpu, lcpu;

85
		rcpu = TOPOLOGY_CPU_BITS - 1 - cpu + tl_cpu->origin;
86
		for_each_present_cpu(lcpu) {
87 88
			if (cpu_logical_map(lcpu) != rcpu)
				continue;
89
			cpumask_set_cpu(lcpu, &book->mask);
90
			cpu_book_id[lcpu] = book->id;
91
			cpumask_set_cpu(lcpu, &core->mask);
92
			if (one_core_per_cpu) {
93 94 95 96 97
				cpu_core_id[lcpu] = rcpu;
				core = core->next;
			} else {
				cpu_core_id[lcpu] = core->id;
			}
98
			cpu_set_polarization(lcpu, tl_cpu->pp);
99 100
		}
	}
101
	return core;
102 103
}

104
static void clear_masks(void)
105
{
106
	struct mask_info *info;
107

108 109
	info = &core_info;
	while (info) {
110
		cpumask_clear(&info->mask);
111 112 113 114
		info = info->next;
	}
	info = &book_info;
	while (info) {
115
		cpumask_clear(&info->mask);
116
		info = info->next;
117 118 119
	}
}

120
static union topology_entry *next_tle(union topology_entry *tle)
121
{
122 123 124
	if (!tle->nl)
		return (union topology_entry *)((struct topology_cpu *)tle + 1);
	return (union topology_entry *)((struct topology_container *)tle + 1);
125 126
}

127
static void __tl_to_cores_generic(struct sysinfo_15_1_x *info)
128
{
129
	struct mask_info *core = &core_info;
130
	struct mask_info *book = &book_info;
131
	union topology_entry *tle, *end;
132

H
Heiko Carstens 已提交
133
	tle = info->tle;
134
	end = (union topology_entry *)((unsigned long)info + info->length);
135 136 137
	while (tle < end) {
		switch (tle->nl) {
		case 2:
138 139
			book = book->next;
			book->id = tle->container.id;
140 141 142
			break;
		case 1:
			core = core->next;
143
			core->id = tle->container.id;
144 145
			break;
		case 0:
146
			add_cpus_to_mask(&tle->cpu, book, core, 0);
147 148
			break;
		default:
149
			clear_masks();
150
			return;
151 152 153
		}
		tle = next_tle(tle);
	}
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

static void __tl_to_cores_z10(struct sysinfo_15_1_x *info)
{
	struct mask_info *core = &core_info;
	struct mask_info *book = &book_info;
	union topology_entry *tle, *end;

	tle = info->tle;
	end = (union topology_entry *)((unsigned long)info + info->length);
	while (tle < end) {
		switch (tle->nl) {
		case 1:
			book = book->next;
			book->id = tle->container.id;
			break;
		case 0:
			core = add_cpus_to_mask(&tle->cpu, book, core, 1);
			break;
		default:
			clear_masks();
			return;
		}
		tle = next_tle(tle);
	}
}

static void tl_to_cores(struct sysinfo_15_1_x *info)
{
	struct cpuid cpu_id;

	get_cpu_id(&cpu_id);
	spin_lock_irq(&topology_lock);
	clear_masks();
	switch (cpu_id.machine) {
	case 0x2097:
	case 0x2098:
		__tl_to_cores_z10(info);
		break;
	default:
		__tl_to_cores_generic(info);
	}
H
Heiko Carstens 已提交
196
	spin_unlock_irq(&topology_lock);
197 198
}

H
Heiko Carstens 已提交
199 200 201 202 203
static void topology_update_polarization_simple(void)
{
	int cpu;

	mutex_lock(&smp_cpu_state_mutex);
204
	for_each_possible_cpu(cpu)
205
		cpu_set_polarization(cpu, POLARIZATION_HRZ);
H
Heiko Carstens 已提交
206 207 208 209
	mutex_unlock(&smp_cpu_state_mutex);
}

static int ptf(unsigned long fc)
210 211 212 213 214 215 216 217
{
	int rc;

	asm volatile(
		"	.insn	rre,0xb9a20000,%1,%1\n"
		"	ipm	%0\n"
		"	srl	%0,28\n"
		: "=d" (rc)
H
Heiko Carstens 已提交
218 219 220 221 222 223
		: "d" (fc)  : "cc");
	return rc;
}

int topology_set_cpu_management(int fc)
{
224
	int cpu, rc;
H
Heiko Carstens 已提交
225

226
	if (!MACHINE_HAS_TOPOLOGY)
H
Heiko Carstens 已提交
227 228 229 230 231 232 233
		return -EOPNOTSUPP;
	if (fc)
		rc = ptf(PTF_VERTICAL);
	else
		rc = ptf(PTF_HORIZONTAL);
	if (rc)
		return -EBUSY;
234
	for_each_possible_cpu(cpu)
235
		cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
236 237 238
	return rc;
}

239 240
static void update_cpu_core_map(void)
{
241
	unsigned long flags;
242 243
	int cpu;

244 245 246 247 248 249 250 251
	spin_lock_irqsave(&topology_lock, flags);
	for_each_possible_cpu(cpu) {
		cpu_core_map[cpu] = cpu_group_map(&core_info, cpu);
		cpu_book_map[cpu] = cpu_group_map(&book_info, cpu);
	}
	spin_unlock_irqrestore(&topology_lock, flags);
}

252
void store_topology(struct sysinfo_15_1_x *info)
253 254 255 256 257 258 259
{
	int rc;

	rc = stsi(info, 15, 1, 3);
	if (rc != -ENOSYS)
		return;
	stsi(info, 15, 1, 2);
260 261
}

262
int arch_update_cpu_topology(void)
263
{
264
	struct sysinfo_15_1_x *info = tl_info;
265 266 267
	struct sys_device *sysdev;
	int cpu;

268
	if (!MACHINE_HAS_TOPOLOGY) {
269
		update_cpu_core_map();
H
Heiko Carstens 已提交
270
		topology_update_polarization_simple();
271
		return 0;
H
Heiko Carstens 已提交
272
	}
273
	store_topology(info);
274
	tl_to_cores(info);
275
	update_cpu_core_map();
276 277 278 279
	for_each_online_cpu(cpu) {
		sysdev = get_cpu_sysdev(cpu);
		kobject_uevent(&sysdev->kobj, KOBJ_CHANGE);
	}
280
	return 1;
281 282
}

283 284
static void topology_work_fn(struct work_struct *work)
{
285
	rebuild_sched_domains();
286 287
}

H
Heiko Carstens 已提交
288 289 290 291 292
void topology_schedule_update(void)
{
	schedule_work(&topology_work);
}

293 294
static void topology_timer_fn(unsigned long ignored)
{
H
Heiko Carstens 已提交
295 296
	if (ptf(PTF_CHECK))
		topology_schedule_update();
297 298 299 300 301 302 303 304 305 306 307
	set_topology_timer();
}

static void set_topology_timer(void)
{
	topology_timer.function = topology_timer_fn;
	topology_timer.data = 0;
	topology_timer.expires = jiffies + 60 * HZ;
	add_timer(&topology_timer);
}

308
static int __init early_parse_topology(char *p)
309
{
H
Heiko Carstens 已提交
310
	if (strncmp(p, "off", 3))
311
		return 0;
H
Heiko Carstens 已提交
312
	topology_enabled = 0;
313
	return 0;
314
}
315
early_param("topology", early_parse_topology);
316

317 318
static void __init alloc_masks(struct sysinfo_15_1_x *info,
			       struct mask_info *mask, int offset)
319 320 321
{
	int i, nr_masks;

322
	nr_masks = info->mag[TOPOLOGY_NR_MAG - offset];
323
	for (i = 0; i < info->mnest - offset; i++)
324
		nr_masks *= info->mag[TOPOLOGY_NR_MAG - offset - 1 - i];
325 326 327 328 329 330 331
	nr_masks = max(nr_masks, 1);
	for (i = 0; i < nr_masks; i++) {
		mask->next = alloc_bootmem(sizeof(struct mask_info));
		mask = mask->next;
	}
}

332 333
void __init s390_init_cpu_topology(void)
{
334
	struct sysinfo_15_1_x *info;
335 336
	int i;

337
	if (!MACHINE_HAS_TOPOLOGY)
338 339 340
		return;
	tl_info = alloc_bootmem_pages(PAGE_SIZE);
	info = tl_info;
341
	store_topology(info);
342
	pr_info("The CPU configuration topology of the machine is:");
343
	for (i = 0; i < TOPOLOGY_NR_MAG; i++)
344 345
		printk(KERN_CONT " %d", info->mag[i]);
	printk(KERN_CONT " / %d\n", info->mnest);
346 347
	alloc_masks(info, &core_info, 1);
	alloc_masks(info, &book_info, 2);
348
}
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

static int cpu_management;

static ssize_t dispatching_show(struct sysdev_class *class,
				struct sysdev_class_attribute *attr,
				char *buf)
{
	ssize_t count;

	mutex_lock(&smp_cpu_state_mutex);
	count = sprintf(buf, "%d\n", cpu_management);
	mutex_unlock(&smp_cpu_state_mutex);
	return count;
}

static ssize_t dispatching_store(struct sysdev_class *dev,
				 struct sysdev_class_attribute *attr,
				 const char *buf,
				 size_t count)
{
	int val, rc;
	char delim;

	if (sscanf(buf, "%d %c", &val, &delim) != 1)
		return -EINVAL;
	if (val != 0 && val != 1)
		return -EINVAL;
	rc = 0;
	get_online_cpus();
	mutex_lock(&smp_cpu_state_mutex);
	if (cpu_management == val)
		goto out;
	rc = topology_set_cpu_management(val);
	if (!rc)
		cpu_management = val;
out:
	mutex_unlock(&smp_cpu_state_mutex);
	put_online_cpus();
	return rc ? rc : count;
}
static SYSDEV_CLASS_ATTR(dispatching, 0644, dispatching_show,
			 dispatching_store);

static ssize_t cpu_polarization_show(struct sys_device *dev,
				     struct sysdev_attribute *attr, char *buf)
{
	int cpu = dev->id;
	ssize_t count;

	mutex_lock(&smp_cpu_state_mutex);
	switch (cpu_read_polarization(cpu)) {
	case POLARIZATION_HRZ:
		count = sprintf(buf, "horizontal\n");
		break;
	case POLARIZATION_VL:
		count = sprintf(buf, "vertical:low\n");
		break;
	case POLARIZATION_VM:
		count = sprintf(buf, "vertical:medium\n");
		break;
	case POLARIZATION_VH:
		count = sprintf(buf, "vertical:high\n");
		break;
	default:
		count = sprintf(buf, "unknown\n");
		break;
	}
	mutex_unlock(&smp_cpu_state_mutex);
	return count;
}
static SYSDEV_ATTR(polarization, 0444, cpu_polarization_show, NULL);

static struct attribute *topology_cpu_attrs[] = {
	&attr_polarization.attr,
	NULL,
};

static struct attribute_group topology_cpu_attr_group = {
	.attrs = topology_cpu_attrs,
};

int topology_cpu_init(struct cpu *cpu)
{
	return sysfs_create_group(&cpu->sysdev.kobj, &topology_cpu_attr_group);
}

static int __init topology_init(void)
{
	if (!MACHINE_HAS_TOPOLOGY) {
		topology_update_polarization_simple();
		goto out;
	}
	init_timer_deferrable(&topology_timer);
	set_topology_timer();
out:
	update_cpu_core_map();
	return sysdev_class_create_file(&cpu_sysdev_class, &attr_dispatching);
}
device_initcall(topology_init);