aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm/numa.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2011-01-12 00:37:42 -0500
committerPaul Mundt <lethal@linux-sh.org>2011-01-12 00:37:42 -0500
commit83eb95b852902f952ba594447a796ad8146b9462 (patch)
tree33c199aeeae58b69ad8d6d2a33c2d96ba2b98ddf /arch/powerpc/mm/numa.c
parentefb3e34b6176d30c4fe8635fa8e1beb6280cc2cd (diff)
parent9bbe7b984096ac45586da2adf26c14069ecb79b2 (diff)
Merge branch 'sh/sdio' into sh-latest
Diffstat (limited to 'arch/powerpc/mm/numa.c')
-rw-r--r--arch/powerpc/mm/numa.c311
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
28static int numa_enabled = 1; 33static 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
166static void __cpuinit map_cpu_to_node(int cpu, int node) 171static 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)
177static void unmap_cpu_from_node(unsigned long cpu) 182static 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 */
193static const int *of_get_associativity(struct device_node *dev) 198static 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 */
249static int of_node_to_nid_single(struct device_node *device) 254static 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
271out: 271out:
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 */
278static 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 */
276int of_node_to_nid(struct device_node *device) 290int 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
1264static 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 */
1286u64 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)
1295static u8 vphn_cpu_change_counts[NR_CPUS][VPHN_NR_CHANGE_CTRS];
1296static cpumask_t cpu_associativity_changes_mask;
1297static int vphn_enabled;
1298static void set_topology_timer(void);
1299
1300/*
1301 * Store the current values of the associativity change counters in the
1302 * hypervisor.
1303 */
1304static 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 */
1330static 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 */
1364static 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 */
1406static 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
1419static 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 */
1446int 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
1479static void topology_work_fn(struct work_struct *work)
1480{
1481 rebuild_sched_domains();
1482}
1483static DECLARE_WORK(topology_work, topology_work_fn);
1484
1485void topology_schedule_update(void)
1486{
1487 schedule_work(&topology_work);
1488}
1489
1490static 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}
1498static struct timer_list topology_timer =
1499 TIMER_INITIALIZER(topology_timer_fn, 0, 0);
1500
1501static 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 */
1511int 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 */
1530int stop_topology_update(void)
1531{
1532 vphn_enabled = 0;
1533 return del_timer_sync(&topology_timer);
1534}
1535#endif /* CONFIG_PPC_SPLPAR */