9a0b5817ad
Implement SMP alternatives, i.e. switching at runtime between different code versions for UP and SMP. The code can patch both SMP->UP and UP->SMP. The UP->SMP case is useful for CPU hotplug. With CONFIG_CPU_HOTPLUG enabled the code switches to UP at boot time and when the number of CPUs goes down to 1, and switches to SMP when the number of CPUs goes up to 2. Without CONFIG_CPU_HOTPLUG or on non-SMP-capable systems the code is patched once at boot time (if needed) and the tables are released afterwards. The changes in detail: * The current alternatives bits are moved to a separate file, the SMP alternatives code is added there. * The patch adds some new elf sections to the kernel: .smp_altinstructions like .altinstructions, also contains a list of alt_instr structs. .smp_altinstr_replacement like .altinstr_replacement, but also has some space to save original instruction before replaving it. .smp_locks list of pointers to lock prefixes which can be nop'ed out on UP. The first two are used to replace more complex instruction sequences such as spinlocks and semaphores. It would be possible to deal with the lock prefixes with that as well, but by handling them as special case the table sizes become much smaller. * The sections are page-aligned and padded up to page size, so they can be free if they are not needed. * Splitted the code to release init pages to a separate function and use it to release the elf sections if they are unused. Signed-off-by: Gerd Hoffmann <kraxel@suse.de> Signed-off-by: Chuck Ebbert <76306.1226@compuserve.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
179 lines
5.4 KiB
C
179 lines
5.4 KiB
C
#include <linux/smp.h>
|
|
#include <linux/timex.h>
|
|
#include <linux/string.h>
|
|
#include <asm/semaphore.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/cpufreq.h>
|
|
|
|
/*
|
|
* Get CPU information for use by the procfs.
|
|
*/
|
|
static int show_cpuinfo(struct seq_file *m, void *v)
|
|
{
|
|
/*
|
|
* These flag bits must match the definitions in <asm/cpufeature.h>.
|
|
* NULL means this bit is undefined or reserved; either way it doesn't
|
|
* have meaning as far as Linux is concerned. Note that it's important
|
|
* to realize there is a difference between this table and CPUID -- if
|
|
* applications want to get the raw CPUID data, they should access
|
|
* /dev/cpu/<cpu_nr>/cpuid instead.
|
|
*/
|
|
static char *x86_cap_flags[] = {
|
|
/* Intel-defined */
|
|
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
|
|
"cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
|
|
"pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx",
|
|
"fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe",
|
|
|
|
/* AMD-defined */
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL,
|
|
NULL, "fxsr_opt", "rdtscp", NULL, NULL, "lm", "3dnowext", "3dnow",
|
|
|
|
/* Transmeta-defined */
|
|
"recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
/* Other (Linux-defined) */
|
|
"cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr",
|
|
NULL, NULL, NULL, NULL,
|
|
"constant_tsc", "up", NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
/* Intel-defined (#2) */
|
|
"pni", NULL, NULL, "monitor", "ds_cpl", "vmx", NULL, "est",
|
|
"tm2", NULL, "cid", NULL, NULL, "cx16", "xtpr", NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
/* VIA/Cyrix/Centaur-defined */
|
|
NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en",
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
|
|
/* AMD-defined (#2) */
|
|
"lahf_lm", "cmp_legacy", "svm", NULL, "cr8legacy", NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
};
|
|
static char *x86_power_flags[] = {
|
|
"ts", /* temperature sensor */
|
|
"fid", /* frequency id control */
|
|
"vid", /* voltage id control */
|
|
"ttp", /* thermal trip */
|
|
"tm",
|
|
"stc",
|
|
NULL,
|
|
/* nothing */ /* constant_tsc - moved to flags */
|
|
};
|
|
struct cpuinfo_x86 *c = v;
|
|
int i, n = c - cpu_data;
|
|
int fpu_exception;
|
|
|
|
#ifdef CONFIG_SMP
|
|
if (!cpu_online(n))
|
|
return 0;
|
|
#endif
|
|
seq_printf(m, "processor\t: %d\n"
|
|
"vendor_id\t: %s\n"
|
|
"cpu family\t: %d\n"
|
|
"model\t\t: %d\n"
|
|
"model name\t: %s\n",
|
|
n,
|
|
c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown",
|
|
c->x86,
|
|
c->x86_model,
|
|
c->x86_model_id[0] ? c->x86_model_id : "unknown");
|
|
|
|
if (c->x86_mask || c->cpuid_level >= 0)
|
|
seq_printf(m, "stepping\t: %d\n", c->x86_mask);
|
|
else
|
|
seq_printf(m, "stepping\t: unknown\n");
|
|
|
|
if ( cpu_has(c, X86_FEATURE_TSC) ) {
|
|
unsigned int freq = cpufreq_quick_get(n);
|
|
if (!freq)
|
|
freq = cpu_khz;
|
|
seq_printf(m, "cpu MHz\t\t: %u.%03u\n",
|
|
freq / 1000, (freq % 1000));
|
|
}
|
|
|
|
/* Cache size */
|
|
if (c->x86_cache_size >= 0)
|
|
seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size);
|
|
#ifdef CONFIG_X86_HT
|
|
if (c->x86_max_cores * smp_num_siblings > 1) {
|
|
seq_printf(m, "physical id\t: %d\n", phys_proc_id[n]);
|
|
seq_printf(m, "siblings\t: %d\n", cpus_weight(cpu_core_map[n]));
|
|
seq_printf(m, "core id\t\t: %d\n", cpu_core_id[n]);
|
|
seq_printf(m, "cpu cores\t: %d\n", c->booted_cores);
|
|
}
|
|
#endif
|
|
|
|
/* We use exception 16 if we have hardware math and we've either seen it or the CPU claims it is internal */
|
|
fpu_exception = c->hard_math && (ignore_fpu_irq || cpu_has_fpu);
|
|
seq_printf(m, "fdiv_bug\t: %s\n"
|
|
"hlt_bug\t\t: %s\n"
|
|
"f00f_bug\t: %s\n"
|
|
"coma_bug\t: %s\n"
|
|
"fpu\t\t: %s\n"
|
|
"fpu_exception\t: %s\n"
|
|
"cpuid level\t: %d\n"
|
|
"wp\t\t: %s\n"
|
|
"flags\t\t:",
|
|
c->fdiv_bug ? "yes" : "no",
|
|
c->hlt_works_ok ? "no" : "yes",
|
|
c->f00f_bug ? "yes" : "no",
|
|
c->coma_bug ? "yes" : "no",
|
|
c->hard_math ? "yes" : "no",
|
|
fpu_exception ? "yes" : "no",
|
|
c->cpuid_level,
|
|
c->wp_works_ok ? "yes" : "no");
|
|
|
|
for ( i = 0 ; i < 32*NCAPINTS ; i++ )
|
|
if ( test_bit(i, c->x86_capability) &&
|
|
x86_cap_flags[i] != NULL )
|
|
seq_printf(m, " %s", x86_cap_flags[i]);
|
|
|
|
for (i = 0; i < 32; i++)
|
|
if (c->x86_power & (1 << i)) {
|
|
if (i < ARRAY_SIZE(x86_power_flags) &&
|
|
x86_power_flags[i])
|
|
seq_printf(m, "%s%s",
|
|
x86_power_flags[i][0]?" ":"",
|
|
x86_power_flags[i]);
|
|
else
|
|
seq_printf(m, " [%d]", i);
|
|
}
|
|
|
|
seq_printf(m, "\nbogomips\t: %lu.%02lu\n\n",
|
|
c->loops_per_jiffy/(500000/HZ),
|
|
(c->loops_per_jiffy/(5000/HZ)) % 100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *c_start(struct seq_file *m, loff_t *pos)
|
|
{
|
|
return *pos < NR_CPUS ? cpu_data + *pos : NULL;
|
|
}
|
|
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
|
{
|
|
++*pos;
|
|
return c_start(m, pos);
|
|
}
|
|
static void c_stop(struct seq_file *m, void *v)
|
|
{
|
|
}
|
|
struct seq_operations cpuinfo_op = {
|
|
.start = c_start,
|
|
.next = c_next,
|
|
.stop = c_stop,
|
|
.show = show_cpuinfo,
|
|
};
|