Commit b434e719 authored by David S. Miller's avatar David S. Miller

[SPARC64]: Fix memory leak when cpu hotplugging.

Every time a cpu is added via hotplug, we allocate the per-cpu MONDO
queues but we never free them up.  Freeing isn't easy since the first
cpu gets this memory from bootmem.

Therefore, the simplest thing to do to fix this bug is to allocate the
queues for all possible cpus at boot time.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6c70b6fc
...@@ -115,11 +115,8 @@ hv_cpu_startup: ...@@ -115,11 +115,8 @@ hv_cpu_startup:
call hard_smp_processor_id call hard_smp_processor_id
nop nop
mov %o0, %o1 call sun4v_register_mondo_queues
mov 0, %o0 nop
mov 0, %o2
call sun4v_init_mondo_queues
mov 1, %o3
call init_cur_cpu_trap call init_cur_cpu_trap
mov %g6, %o0 mov %g6, %o0
......
...@@ -929,7 +929,7 @@ static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type ...@@ -929,7 +929,7 @@ static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type
} }
} }
static void __cpuinit sun4v_register_mondo_queues(int this_cpu) void __cpuinit sun4v_register_mondo_queues(int this_cpu)
{ {
struct trap_per_cpu *tb = &trap_block[this_cpu]; struct trap_per_cpu *tb = &trap_block[this_cpu];
...@@ -943,20 +943,10 @@ static void __cpuinit sun4v_register_mondo_queues(int this_cpu) ...@@ -943,20 +943,10 @@ static void __cpuinit sun4v_register_mondo_queues(int this_cpu)
tb->nonresum_qmask); tb->nonresum_qmask);
} }
static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem) static void __init alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask)
{ {
unsigned long size = PAGE_ALIGN(qmask + 1); unsigned long size = PAGE_ALIGN(qmask + 1);
unsigned long order = get_order(size); void *p = __alloc_bootmem_low(size, size, 0);
void *p = NULL;
if (use_bootmem) {
p = __alloc_bootmem_low(size, size, 0);
} else {
struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
if (page)
p = page_address(page);
}
if (!p) { if (!p) {
prom_printf("SUN4V: Error, cannot allocate mondo queue.\n"); prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
prom_halt(); prom_halt();
...@@ -965,19 +955,10 @@ static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask ...@@ -965,19 +955,10 @@ static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask
*pa_ptr = __pa(p); *pa_ptr = __pa(p);
} }
static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask, int use_bootmem) static void __init alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask)
{ {
unsigned long size = PAGE_ALIGN(qmask + 1); unsigned long size = PAGE_ALIGN(qmask + 1);
unsigned long order = get_order(size); void *p = __alloc_bootmem_low(size, size, 0);
void *p = NULL;
if (use_bootmem) {
p = __alloc_bootmem_low(size, size, 0);
} else {
struct page *page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, order);
if (page)
p = page_address(page);
}
if (!p) { if (!p) {
prom_printf("SUN4V: Error, cannot allocate kbuf page.\n"); prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
...@@ -987,18 +968,14 @@ static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask, ...@@ -987,18 +968,14 @@ static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask,
*pa_ptr = __pa(p); *pa_ptr = __pa(p);
} }
static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_bootmem) static void __init init_cpu_send_mondo_info(struct trap_per_cpu *tb)
{ {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
void *page; void *page;
BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64)); BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64));
if (use_bootmem)
page = alloc_bootmem_low_pages(PAGE_SIZE); page = alloc_bootmem_low_pages(PAGE_SIZE);
else
page = (void *) get_zeroed_page(GFP_ATOMIC);
if (!page) { if (!page) {
prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n"); prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n");
prom_halt(); prom_halt();
...@@ -1009,30 +986,27 @@ static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_ ...@@ -1009,30 +986,27 @@ static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_
#endif #endif
} }
/* Allocate and register the mondo and error queues for this cpu. */ /* Allocate mondo and error queues for all possible cpus. */
void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load) static void __init sun4v_init_mondo_queues(void)
{ {
int cpu;
for_each_possible_cpu(cpu) {
struct trap_per_cpu *tb = &trap_block[cpu]; struct trap_per_cpu *tb = &trap_block[cpu];
if (alloc) { alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask);
alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask, use_bootmem); alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask);
alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask, use_bootmem); alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask);
alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask, use_bootmem); alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask);
alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask, use_bootmem); alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask);
alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask, use_bootmem); alloc_one_kbuf(&tb->nonresum_kernel_buf_pa,
alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, tb->nonresum_qmask, use_bootmem); tb->nonresum_qmask);
init_cpu_send_mondo_info(tb, use_bootmem); init_cpu_send_mondo_info(tb);
} }
if (load) { /* Load up the boot cpu's entries. */
if (cpu != hard_smp_processor_id()) { sun4v_register_mondo_queues(hard_smp_processor_id());
prom_printf("SUN4V: init mondo on cpu %d not %d\n",
cpu, hard_smp_processor_id());
prom_halt();
}
sun4v_register_mondo_queues(cpu);
}
} }
static struct irqaction timer_irq_action = { static struct irqaction timer_irq_action = {
...@@ -1047,7 +1021,7 @@ void __init init_IRQ(void) ...@@ -1047,7 +1021,7 @@ void __init init_IRQ(void)
memset(&ivector_table[0], 0, sizeof(ivector_table)); memset(&ivector_table[0], 0, sizeof(ivector_table));
if (tlb_type == hypervisor) if (tlb_type == hypervisor)
sun4v_init_mondo_queues(1, hard_smp_processor_id(), 1, 1); sun4v_init_mondo_queues();
/* We need to clear any IRQ's pending in the soft interrupt /* We need to clear any IRQ's pending in the soft interrupt
* registers, a spurious one could be left around from the * registers, a spurious one could be left around from the
......
...@@ -334,8 +334,6 @@ static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg) ...@@ -334,8 +334,6 @@ static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg)
} }
#endif #endif
extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load);
extern unsigned long sparc64_cpu_startup; extern unsigned long sparc64_cpu_startup;
/* The OBP cpu startup callback truncates the 3rd arg cookie to /* The OBP cpu startup callback truncates the 3rd arg cookie to
...@@ -359,9 +357,6 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu) ...@@ -359,9 +357,6 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu)
cpu_new_thread = task_thread_info(p); cpu_new_thread = task_thread_info(p);
if (tlb_type == hypervisor) { if (tlb_type == hypervisor) {
/* Alloc the mondo queues, cpu will load them. */
sun4v_init_mondo_queues(0, cpu, 1, 0);
#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU) #if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
if (ldom_domaining_enabled) if (ldom_domaining_enabled)
ldom_startcpu_cpuid(cpu, ldom_startcpu_cpuid(cpu,
......
...@@ -366,11 +366,8 @@ after_lock_tlb: ...@@ -366,11 +366,8 @@ after_lock_tlb:
call hard_smp_processor_id call hard_smp_processor_id
nop nop
mov %o0, %o1 call sun4v_register_mondo_queues
mov 0, %o0 nop
mov 0, %o2
call sun4v_init_mondo_queues
mov 1, %o3
1: call init_cur_cpu_trap 1: call init_cur_cpu_trap
ldx [%l0], %o0 ldx [%l0], %o0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment