|
@@ -478,6 +478,72 @@ void __init smp_setup_processor_id(void)
|
|
|
printk(KERN_INFO "Booting Linux on physical CPU 0x%x\n", mpidr);
|
|
|
}
|
|
|
|
|
|
+struct mpidr_hash mpidr_hash;
|
|
|
+#ifdef CONFIG_SMP
|
|
|
+/**
|
|
|
+ * smp_build_mpidr_hash - Pre-compute shifts required at each affinity
|
|
|
+ * level in order to build a linear index from an
|
|
|
+ * MPIDR value. Resulting algorithm is a collision
|
|
|
+ * free hash carried out through shifting and ORing
|
|
|
+ */
|
|
|
+static void __init smp_build_mpidr_hash(void)
|
|
|
+{
|
|
|
+ u32 i, affinity;
|
|
|
+ u32 fs[3], bits[3], ls, mask = 0;
|
|
|
+ /*
|
|
|
+ * Pre-scan the list of MPIDRS and filter out bits that do
|
|
|
+ * not contribute to affinity levels, ie they never toggle.
|
|
|
+ */
|
|
|
+ for_each_possible_cpu(i)
|
|
|
+ mask |= (cpu_logical_map(i) ^ cpu_logical_map(0));
|
|
|
+ pr_debug("mask of set bits 0x%x\n", mask);
|
|
|
+ /*
|
|
|
+ * Find and stash the last and first bit set at all affinity levels to
|
|
|
+ * check how many bits are required to represent them.
|
|
|
+ */
|
|
|
+ for (i = 0; i < 3; i++) {
|
|
|
+ affinity = MPIDR_AFFINITY_LEVEL(mask, i);
|
|
|
+ /*
|
|
|
+ * Find the MSB bit and LSB bits position
|
|
|
+ * to determine how many bits are required
|
|
|
+ * to express the affinity level.
|
|
|
+ */
|
|
|
+ ls = fls(affinity);
|
|
|
+ fs[i] = affinity ? ffs(affinity) - 1 : 0;
|
|
|
+ bits[i] = ls - fs[i];
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * An index can be created from the MPIDR by isolating the
|
|
|
+ * significant bits at each affinity level and by shifting
|
|
|
+ * them in order to compress the 24 bits values space to a
|
|
|
+ * compressed set of values. This is equivalent to hashing
|
|
|
+ * the MPIDR through shifting and ORing. It is a collision free
|
|
|
+ * hash though not minimal since some levels might contain a number
|
|
|
+ * of CPUs that is not an exact power of 2 and their bit
|
|
|
+ * representation might contain holes, eg MPIDR[7:0] = {0x2, 0x80}.
|
|
|
+ */
|
|
|
+ mpidr_hash.shift_aff[0] = fs[0];
|
|
|
+ mpidr_hash.shift_aff[1] = MPIDR_LEVEL_BITS + fs[1] - bits[0];
|
|
|
+ mpidr_hash.shift_aff[2] = 2*MPIDR_LEVEL_BITS + fs[2] -
|
|
|
+ (bits[1] + bits[0]);
|
|
|
+ mpidr_hash.mask = mask;
|
|
|
+ mpidr_hash.bits = bits[2] + bits[1] + bits[0];
|
|
|
+ pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] mask[0x%x] bits[%u]\n",
|
|
|
+ mpidr_hash.shift_aff[0],
|
|
|
+ mpidr_hash.shift_aff[1],
|
|
|
+ mpidr_hash.shift_aff[2],
|
|
|
+ mpidr_hash.mask,
|
|
|
+ mpidr_hash.bits);
|
|
|
+ /*
|
|
|
+ * 4x is an arbitrary value used to warn on a hash table much bigger
|
|
|
+ * than expected on most systems.
|
|
|
+ */
|
|
|
+ if (mpidr_hash_size() > 4 * num_possible_cpus())
|
|
|
+ pr_warn("Large number of MPIDR hash buckets detected\n");
|
|
|
+ sync_cache_w(&mpidr_hash);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
static void __init setup_processor(void)
|
|
|
{
|
|
|
struct proc_info_list *list;
|
|
@@ -825,6 +891,7 @@ void __init setup_arch(char **cmdline_p)
|
|
|
smp_set_ops(mdesc->smp);
|
|
|
}
|
|
|
smp_init_cpus();
|
|
|
+ smp_build_mpidr_hash();
|
|
|
}
|
|
|
#endif
|
|
|
|