diff options
author | Paul Mundt <lethal@linux-sh.org> | 2011-01-12 00:37:42 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-01-12 00:37:42 -0500 |
commit | 83eb95b852902f952ba594447a796ad8146b9462 (patch) | |
tree | 33c199aeeae58b69ad8d6d2a33c2d96ba2b98ddf /arch/powerpc/mm/numa.c | |
parent | efb3e34b6176d30c4fe8635fa8e1beb6280cc2cd (diff) | |
parent | 9bbe7b984096ac45586da2adf26c14069ecb79b2 (diff) |
Merge branch 'sh/sdio' into sh-latest
Diffstat (limited to 'arch/powerpc/mm/numa.c')
-rw-r--r-- | arch/powerpc/mm/numa.c | 311 |
1 files changed, 298 insertions, 13 deletions
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 74505b245374..bf5cb91f07de 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c | |||
@@ -20,10 +20,15 @@ | |||
20 | #include <linux/memblock.h> | 20 | #include <linux/memblock.h> |
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/pfn.h> | 22 | #include <linux/pfn.h> |
23 | #include <linux/cpuset.h> | ||
24 | #include <linux/node.h> | ||
23 | #include <asm/sparsemem.h> | 25 | #include <asm/sparsemem.h> |
24 | #include <asm/prom.h> | 26 | #include <asm/prom.h> |
25 | #include <asm/system.h> | 27 | #include <asm/system.h> |
26 | #include <asm/smp.h> | 28 | #include <asm/smp.h> |
29 | #include <asm/firmware.h> | ||
30 | #include <asm/paca.h> | ||
31 | #include <asm/hvcall.h> | ||
27 | 32 | ||
28 | static int numa_enabled = 1; | 33 | static int numa_enabled = 1; |
29 | 34 | ||
@@ -163,7 +168,7 @@ static void __init get_node_active_region(unsigned long start_pfn, | |||
163 | work_with_active_regions(nid, get_active_region_work_fn, node_ar); | 168 | work_with_active_regions(nid, get_active_region_work_fn, node_ar); |
164 | } | 169 | } |
165 | 170 | ||
166 | static void __cpuinit map_cpu_to_node(int cpu, int node) | 171 | static void map_cpu_to_node(int cpu, int node) |
167 | { | 172 | { |
168 | numa_cpu_lookup_table[cpu] = node; | 173 | numa_cpu_lookup_table[cpu] = node; |
169 | 174 | ||
@@ -173,7 +178,7 @@ static void __cpuinit map_cpu_to_node(int cpu, int node) | |||
173 | cpumask_set_cpu(cpu, node_to_cpumask_map[node]); | 178 | cpumask_set_cpu(cpu, node_to_cpumask_map[node]); |
174 | } | 179 | } |
175 | 180 | ||
176 | #ifdef CONFIG_HOTPLUG_CPU | 181 | #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PPC_SPLPAR) |
177 | static void unmap_cpu_from_node(unsigned long cpu) | 182 | static void unmap_cpu_from_node(unsigned long cpu) |
178 | { | 183 | { |
179 | int node = numa_cpu_lookup_table[cpu]; | 184 | int node = numa_cpu_lookup_table[cpu]; |
@@ -187,7 +192,7 @@ static void unmap_cpu_from_node(unsigned long cpu) | |||
187 | cpu, node); | 192 | cpu, node); |
188 | } | 193 | } |
189 | } | 194 | } |
190 | #endif /* CONFIG_HOTPLUG_CPU */ | 195 | #endif /* CONFIG_HOTPLUG_CPU || CONFIG_PPC_SPLPAR */ |
191 | 196 | ||
192 | /* must hold reference to node during call */ | 197 | /* must hold reference to node during call */ |
193 | static const int *of_get_associativity(struct device_node *dev) | 198 | static const int *of_get_associativity(struct device_node *dev) |
@@ -246,32 +251,41 @@ static void initialize_distance_lookup_table(int nid, | |||
246 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa | 251 | /* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa |
247 | * info is found. | 252 | * info is found. |
248 | */ | 253 | */ |
249 | static int of_node_to_nid_single(struct device_node *device) | 254 | static int associativity_to_nid(const unsigned int *associativity) |
250 | { | 255 | { |
251 | int nid = -1; | 256 | int nid = -1; |
252 | const unsigned int *tmp; | ||
253 | 257 | ||
254 | if (min_common_depth == -1) | 258 | if (min_common_depth == -1) |
255 | goto out; | 259 | goto out; |
256 | 260 | ||
257 | tmp = of_get_associativity(device); | 261 | if (associativity[0] >= min_common_depth) |
258 | if (!tmp) | 262 | nid = associativity[min_common_depth]; |
259 | goto out; | ||
260 | |||
261 | if (tmp[0] >= min_common_depth) | ||
262 | nid = tmp[min_common_depth]; | ||
263 | 263 | ||
264 | /* POWER4 LPAR uses 0xffff as invalid node */ | 264 | /* POWER4 LPAR uses 0xffff as invalid node */ |
265 | if (nid == 0xffff || nid >= MAX_NUMNODES) | 265 | if (nid == 0xffff || nid >= MAX_NUMNODES) |
266 | nid = -1; | 266 | nid = -1; |
267 | 267 | ||
268 | if (nid > 0 && tmp[0] >= distance_ref_points_depth) | 268 | if (nid > 0 && associativity[0] >= distance_ref_points_depth) |
269 | initialize_distance_lookup_table(nid, tmp); | 269 | initialize_distance_lookup_table(nid, associativity); |
270 | 270 | ||
271 | out: | 271 | out: |
272 | return nid; | 272 | return nid; |
273 | } | 273 | } |
274 | 274 | ||
275 | /* Returns the nid associated with the given device tree node, | ||
276 | * or -1 if not found. | ||
277 | */ | ||
278 | static int of_node_to_nid_single(struct device_node *device) | ||
279 | { | ||
280 | int nid = -1; | ||
281 | const unsigned int *tmp; | ||
282 | |||
283 | tmp = of_get_associativity(device); | ||
284 | if (tmp) | ||
285 | nid = associativity_to_nid(tmp); | ||
286 | return nid; | ||
287 | } | ||
288 | |||
275 | /* Walk the device tree upwards, looking for an associativity id */ | 289 | /* Walk the device tree upwards, looking for an associativity id */ |
276 | int of_node_to_nid(struct device_node *device) | 290 | int of_node_to_nid(struct device_node *device) |
277 | { | 291 | { |
@@ -1247,4 +1261,275 @@ int hot_add_scn_to_nid(unsigned long scn_addr) | |||
1247 | return nid; | 1261 | return nid; |
1248 | } | 1262 | } |
1249 | 1263 | ||
1264 | static u64 hot_add_drconf_memory_max(void) | ||
1265 | { | ||
1266 | struct device_node *memory = NULL; | ||
1267 | unsigned int drconf_cell_cnt = 0; | ||
1268 | u64 lmb_size = 0; | ||
1269 | const u32 *dm = 0; | ||
1270 | |||
1271 | memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); | ||
1272 | if (memory) { | ||
1273 | drconf_cell_cnt = of_get_drconf_memory(memory, &dm); | ||
1274 | lmb_size = of_get_lmb_size(memory); | ||
1275 | of_node_put(memory); | ||
1276 | } | ||
1277 | return lmb_size * drconf_cell_cnt; | ||
1278 | } | ||
1279 | |||
1280 | /* | ||
1281 | * memory_hotplug_max - return max address of memory that may be added | ||
1282 | * | ||
1283 | * This is currently only used on systems that support drconfig memory | ||
1284 | * hotplug. | ||
1285 | */ | ||
1286 | u64 memory_hotplug_max(void) | ||
1287 | { | ||
1288 | return max(hot_add_drconf_memory_max(), memblock_end_of_DRAM()); | ||
1289 | } | ||
1250 | #endif /* CONFIG_MEMORY_HOTPLUG */ | 1290 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
1291 | |||
1292 | /* Vrtual Processor Home Node (VPHN) support */ | ||
1293 | #ifdef CONFIG_PPC_SPLPAR | ||
1294 | #define VPHN_NR_CHANGE_CTRS (8) | ||
1295 | static u8 vphn_cpu_change_counts[NR_CPUS][VPHN_NR_CHANGE_CTRS]; | ||
1296 | static cpumask_t cpu_associativity_changes_mask; | ||
1297 | static int vphn_enabled; | ||
1298 | static void set_topology_timer(void); | ||
1299 | |||
1300 | /* | ||
1301 | * Store the current values of the associativity change counters in the | ||
1302 | * hypervisor. | ||
1303 | */ | ||
1304 | static void setup_cpu_associativity_change_counters(void) | ||
1305 | { | ||
1306 | int cpu = 0; | ||
1307 | |||
1308 | for_each_possible_cpu(cpu) { | ||
1309 | int i = 0; | ||
1310 | u8 *counts = vphn_cpu_change_counts[cpu]; | ||
1311 | volatile u8 *hypervisor_counts = lppaca[cpu].vphn_assoc_counts; | ||
1312 | |||
1313 | for (i = 0; i < VPHN_NR_CHANGE_CTRS; i++) { | ||
1314 | counts[i] = hypervisor_counts[i]; | ||
1315 | } | ||
1316 | } | ||
1317 | } | ||
1318 | |||
1319 | /* | ||
1320 | * The hypervisor maintains a set of 8 associativity change counters in | ||
1321 | * the VPA of each cpu that correspond to the associativity levels in the | ||
1322 | * ibm,associativity-reference-points property. When an associativity | ||
1323 | * level changes, the corresponding counter is incremented. | ||
1324 | * | ||
1325 | * Set a bit in cpu_associativity_changes_mask for each cpu whose home | ||
1326 | * node associativity levels have changed. | ||
1327 | * | ||
1328 | * Returns the number of cpus with unhandled associativity changes. | ||
1329 | */ | ||
1330 | static int update_cpu_associativity_changes_mask(void) | ||
1331 | { | ||
1332 | int cpu = 0, nr_cpus = 0; | ||
1333 | cpumask_t *changes = &cpu_associativity_changes_mask; | ||
1334 | |||
1335 | cpumask_clear(changes); | ||
1336 | |||
1337 | for_each_possible_cpu(cpu) { | ||
1338 | int i, changed = 0; | ||
1339 | u8 *counts = vphn_cpu_change_counts[cpu]; | ||
1340 | volatile u8 *hypervisor_counts = lppaca[cpu].vphn_assoc_counts; | ||
1341 | |||
1342 | for (i = 0; i < VPHN_NR_CHANGE_CTRS; i++) { | ||
1343 | if (hypervisor_counts[i] > counts[i]) { | ||
1344 | counts[i] = hypervisor_counts[i]; | ||
1345 | changed = 1; | ||
1346 | } | ||
1347 | } | ||
1348 | if (changed) { | ||
1349 | cpumask_set_cpu(cpu, changes); | ||
1350 | nr_cpus++; | ||
1351 | } | ||
1352 | } | ||
1353 | |||
1354 | return nr_cpus; | ||
1355 | } | ||
1356 | |||
1357 | /* 6 64-bit registers unpacked into 12 32-bit associativity values */ | ||
1358 | #define VPHN_ASSOC_BUFSIZE (6*sizeof(u64)/sizeof(u32)) | ||
1359 | |||
1360 | /* | ||
1361 | * Convert the associativity domain numbers returned from the hypervisor | ||
1362 | * to the sequence they would appear in the ibm,associativity property. | ||
1363 | */ | ||
1364 | static int vphn_unpack_associativity(const long *packed, unsigned int *unpacked) | ||
1365 | { | ||
1366 | int i = 0; | ||
1367 | int nr_assoc_doms = 0; | ||
1368 | const u16 *field = (const u16*) packed; | ||
1369 | |||
1370 | #define VPHN_FIELD_UNUSED (0xffff) | ||
1371 | #define VPHN_FIELD_MSB (0x8000) | ||
1372 | #define VPHN_FIELD_MASK (~VPHN_FIELD_MSB) | ||
1373 | |||
1374 | for (i = 0; i < VPHN_ASSOC_BUFSIZE; i++) { | ||
1375 | if (*field == VPHN_FIELD_UNUSED) { | ||
1376 | /* All significant fields processed, and remaining | ||
1377 | * fields contain the reserved value of all 1's. | ||
1378 | * Just store them. | ||
1379 | */ | ||
1380 | unpacked[i] = *((u32*)field); | ||
1381 | field += 2; | ||
1382 | } | ||
1383 | else if (*field & VPHN_FIELD_MSB) { | ||
1384 | /* Data is in the lower 15 bits of this field */ | ||
1385 | unpacked[i] = *field & VPHN_FIELD_MASK; | ||
1386 | field++; | ||
1387 | nr_assoc_doms++; | ||
1388 | } | ||
1389 | else { | ||
1390 | /* Data is in the lower 15 bits of this field | ||
1391 | * concatenated with the next 16 bit field | ||
1392 | */ | ||
1393 | unpacked[i] = *((u32*)field); | ||
1394 | field += 2; | ||
1395 | nr_assoc_doms++; | ||
1396 | } | ||
1397 | } | ||
1398 | |||
1399 | return nr_assoc_doms; | ||
1400 | } | ||
1401 | |||
1402 | /* | ||
1403 | * Retrieve the new associativity information for a virtual processor's | ||
1404 | * home node. | ||
1405 | */ | ||
1406 | static long hcall_vphn(unsigned long cpu, unsigned int *associativity) | ||
1407 | { | ||
1408 | long rc = 0; | ||
1409 | long retbuf[PLPAR_HCALL9_BUFSIZE] = {0}; | ||
1410 | u64 flags = 1; | ||
1411 | int hwcpu = get_hard_smp_processor_id(cpu); | ||
1412 | |||
1413 | rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, hwcpu); | ||
1414 | vphn_unpack_associativity(retbuf, associativity); | ||
1415 | |||
1416 | return rc; | ||
1417 | } | ||
1418 | |||
1419 | static long vphn_get_associativity(unsigned long cpu, | ||
1420 | unsigned int *associativity) | ||
1421 | { | ||
1422 | long rc = 0; | ||
1423 | |||
1424 | rc = hcall_vphn(cpu, associativity); | ||
1425 | |||
1426 | switch (rc) { | ||
1427 | case H_FUNCTION: | ||
1428 | printk(KERN_INFO | ||
1429 | "VPHN is not supported. Disabling polling...\n"); | ||
1430 | stop_topology_update(); | ||
1431 | break; | ||
1432 | case H_HARDWARE: | ||
1433 | printk(KERN_ERR | ||
1434 | "hcall_vphn() experienced a hardware fault " | ||
1435 | "preventing VPHN. Disabling polling...\n"); | ||
1436 | stop_topology_update(); | ||
1437 | } | ||
1438 | |||
1439 | return rc; | ||
1440 | } | ||
1441 | |||
1442 | /* | ||
1443 | * Update the node maps and sysfs entries for each cpu whose home node | ||
1444 | * has changed. | ||
1445 | */ | ||
1446 | int arch_update_cpu_topology(void) | ||
1447 | { | ||
1448 | int cpu = 0, nid = 0, old_nid = 0; | ||
1449 | unsigned int associativity[VPHN_ASSOC_BUFSIZE] = {0}; | ||
1450 | struct sys_device *sysdev = NULL; | ||
1451 | |||
1452 | for_each_cpu_mask(cpu, cpu_associativity_changes_mask) { | ||
1453 | vphn_get_associativity(cpu, associativity); | ||
1454 | nid = associativity_to_nid(associativity); | ||
1455 | |||
1456 | if (nid < 0 || !node_online(nid)) | ||
1457 | nid = first_online_node; | ||
1458 | |||
1459 | old_nid = numa_cpu_lookup_table[cpu]; | ||
1460 | |||
1461 | /* Disable hotplug while we update the cpu | ||
1462 | * masks and sysfs. | ||
1463 | */ | ||
1464 | get_online_cpus(); | ||
1465 | unregister_cpu_under_node(cpu, old_nid); | ||
1466 | unmap_cpu_from_node(cpu); | ||
1467 | map_cpu_to_node(cpu, nid); | ||
1468 | register_cpu_under_node(cpu, nid); | ||
1469 | put_online_cpus(); | ||
1470 | |||
1471 | sysdev = get_cpu_sysdev(cpu); | ||
1472 | if (sysdev) | ||
1473 | kobject_uevent(&sysdev->kobj, KOBJ_CHANGE); | ||
1474 | } | ||
1475 | |||
1476 | return 1; | ||
1477 | } | ||
1478 | |||
1479 | static void topology_work_fn(struct work_struct *work) | ||
1480 | { | ||
1481 | rebuild_sched_domains(); | ||
1482 | } | ||
1483 | static DECLARE_WORK(topology_work, topology_work_fn); | ||
1484 | |||
1485 | void topology_schedule_update(void) | ||
1486 | { | ||
1487 | schedule_work(&topology_work); | ||
1488 | } | ||
1489 | |||
1490 | static void topology_timer_fn(unsigned long ignored) | ||
1491 | { | ||
1492 | if (!vphn_enabled) | ||
1493 | return; | ||
1494 | if (update_cpu_associativity_changes_mask() > 0) | ||
1495 | topology_schedule_update(); | ||
1496 | set_topology_timer(); | ||
1497 | } | ||
1498 | static struct timer_list topology_timer = | ||
1499 | TIMER_INITIALIZER(topology_timer_fn, 0, 0); | ||
1500 | |||
1501 | static void set_topology_timer(void) | ||
1502 | { | ||
1503 | topology_timer.data = 0; | ||
1504 | topology_timer.expires = jiffies + 60 * HZ; | ||
1505 | add_timer(&topology_timer); | ||
1506 | } | ||
1507 | |||
1508 | /* | ||
1509 | * Start polling for VPHN associativity changes. | ||
1510 | */ | ||
1511 | int start_topology_update(void) | ||
1512 | { | ||
1513 | int rc = 0; | ||
1514 | |||
1515 | if (firmware_has_feature(FW_FEATURE_VPHN)) { | ||
1516 | vphn_enabled = 1; | ||
1517 | setup_cpu_associativity_change_counters(); | ||
1518 | init_timer_deferrable(&topology_timer); | ||
1519 | set_topology_timer(); | ||
1520 | rc = 1; | ||
1521 | } | ||
1522 | |||
1523 | return rc; | ||
1524 | } | ||
1525 | __initcall(start_topology_update); | ||
1526 | |||
1527 | /* | ||
1528 | * Disable polling for VPHN associativity changes. | ||
1529 | */ | ||
1530 | int stop_topology_update(void) | ||
1531 | { | ||
1532 | vphn_enabled = 0; | ||
1533 | return del_timer_sync(&topology_timer); | ||
1534 | } | ||
1535 | #endif /* CONFIG_PPC_SPLPAR */ | ||