Skip to content

Commit

Permalink
[SPARC64]: Fix memory leak when cpu hotplugging.
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 9, 2007
1 parent 6c70b6f commit b434e71
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 65 deletions.
7 changes: 2 additions & 5 deletions arch/sparc64/kernel/hvtramp.S
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,8 @@ hv_cpu_startup:
call hard_smp_processor_id
nop

mov %o0, %o1
mov 0, %o0
mov 0, %o2
call sun4v_init_mondo_queues
mov 1, %o3
call sun4v_register_mondo_queues
nop

call init_cur_cpu_trap
mov %g6, %o0
Expand Down
74 changes: 24 additions & 50 deletions arch/sparc64/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand All @@ -943,20 +943,10 @@ static void __cpuinit sun4v_register_mondo_queues(int this_cpu)
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 order = get_order(size);
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);
}

void *p = __alloc_bootmem_low(size, size, 0);
if (!p) {
prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
prom_halt();
Expand All @@ -965,19 +955,10 @@ static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask
*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 order = get_order(size);
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);
}
void *p = __alloc_bootmem_low(size, size, 0);

if (!p) {
prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
Expand All @@ -987,18 +968,14 @@ static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask,
*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
void *page;

BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64));

if (use_bootmem)
page = alloc_bootmem_low_pages(PAGE_SIZE);
else
page = (void *) get_zeroed_page(GFP_ATOMIC);

page = alloc_bootmem_low_pages(PAGE_SIZE);
if (!page) {
prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n");
prom_halt();
Expand All @@ -1009,30 +986,27 @@ static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_
#endif
}

/* Allocate and register the mondo and error queues for this cpu. */
void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load)
/* Allocate mondo and error queues for all possible cpus. */
static void __init sun4v_init_mondo_queues(void)
{
struct trap_per_cpu *tb = &trap_block[cpu];
int cpu;

if (alloc) {
alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask, use_bootmem);
alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask, use_bootmem);
alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask, use_bootmem);
alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask, use_bootmem);
alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask, use_bootmem);
alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, tb->nonresum_qmask, use_bootmem);
for_each_possible_cpu(cpu) {
struct trap_per_cpu *tb = &trap_block[cpu];

init_cpu_send_mondo_info(tb, use_bootmem);
}
alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask);
alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask);
alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask);
alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask);
alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask);
alloc_one_kbuf(&tb->nonresum_kernel_buf_pa,
tb->nonresum_qmask);

if (load) {
if (cpu != 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);
init_cpu_send_mondo_info(tb);
}

/* Load up the boot cpu's entries. */
sun4v_register_mondo_queues(hard_smp_processor_id());
}

static struct irqaction timer_irq_action = {
Expand All @@ -1047,7 +1021,7 @@ void __init init_IRQ(void)
memset(&ivector_table[0], 0, sizeof(ivector_table));

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
* registers, a spurious one could be left around from the
Expand Down
5 changes: 0 additions & 5 deletions arch/sparc64/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,6 @@ static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg)
}
#endif

extern void sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load);

extern unsigned long sparc64_cpu_startup;

/* The OBP cpu startup callback truncates the 3rd arg cookie to
Expand All @@ -359,9 +357,6 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu)
cpu_new_thread = task_thread_info(p);

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 (ldom_domaining_enabled)
ldom_startcpu_cpuid(cpu,
Expand Down
7 changes: 2 additions & 5 deletions arch/sparc64/kernel/trampoline.S
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,8 @@ after_lock_tlb:
call hard_smp_processor_id
nop

mov %o0, %o1
mov 0, %o0
mov 0, %o2
call sun4v_init_mondo_queues
mov 1, %o3
call sun4v_register_mondo_queues
nop

1: call init_cur_cpu_trap
ldx [%l0], %o0
Expand Down

0 comments on commit b434e71

Please sign in to comment.