Skip to content

Commit

Permalink
ARM: Fix broken highmem support
Browse files Browse the repository at this point in the history
Currently, highmem is selectable, and you can request an increased
vmalloc area.  However, none of this has any effect on the memory
layout since a patch in the highmem series was accidentally dropped.
Moreover, even if you did want highmem, all memory would still be
registered as lowmem, possibly resulting in overflow of the available
virtual mapping space.

The highmem boundary is determined by the highest allowed beginning
of the vmalloc area, which depends on its configurable minimum size
(see commit 60296c7 for details on
this).

We should create mappings and initialize bootmem only for low memory,
while the zone allocator must still be told about highmem.

Currently, memory nodes which are completely located in high memory
are not supported.  This is not a huge limitation since systems
relying on highmem support are unlikely to have discontiguous memory
with large holes.

[ A similar patch was meant to be merged before commit 5f0fbf9
  and be available  in Linux v2.6.30, however some git rebase screw-up
  of mine dropped the first commit of the series, and that goofage
  escaped testing somehow as well. -- Nico ]

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Reviewed-by: Nicolas Pitre <nico@marvell.com>
  • Loading branch information
Russell King authored and Russell King committed Aug 15, 2009
1 parent 3b3119f commit dde5828
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 47 deletions.
3 changes: 2 additions & 1 deletion arch/arm/include/asm/setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ static struct tagtable __tagtable_##fn __tag = { tag, fn }
struct membank {
unsigned long start;
unsigned long size;
int node;
unsigned short node;
unsigned short highmem;
};

struct meminfo {
Expand Down
118 changes: 73 additions & 45 deletions arch/arm/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,32 @@ void show_mem(void)
printk("%d pages swap cached\n", cached);
}

static void __init find_node_limits(int node, struct meminfo *mi,
unsigned long *min, unsigned long *max_low, unsigned long *max_high)
{
int i;

*min = -1UL;
*max_low = *max_high = 0;

for_each_nodebank(i, mi, node) {
struct membank *bank = &mi->bank[i];
unsigned long start, end;

start = bank_pfn_start(bank);
end = bank_pfn_end(bank);

if (*min > start)
*min = start;
if (*max_high < end)
*max_high = end;
if (bank->highmem)
continue;
if (*max_low < end)
*max_low = end;
}
}

/*
* FIXME: We really want to avoid allocating the bootmap bitmap
* over the top of the initrd. Hopefully, this is located towards
Expand Down Expand Up @@ -210,40 +236,24 @@ static inline void map_memory_bank(struct membank *bank)
#endif
}

static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)
static void __init bootmem_init_node(int node, struct meminfo *mi,
unsigned long start_pfn, unsigned long end_pfn)
{
unsigned long start_pfn, end_pfn, boot_pfn;
unsigned long boot_pfn;
unsigned int boot_pages;
pg_data_t *pgdat;
int i;

start_pfn = -1UL;
end_pfn = 0;

/*
* Calculate the pfn range, and map the memory banks for this node.
* Map the memory banks for this node.
*/
for_each_nodebank(i, mi, node) {
struct membank *bank = &mi->bank[i];
unsigned long start, end;

start = bank_pfn_start(bank);
end = bank_pfn_end(bank);

if (start_pfn > start)
start_pfn = start;
if (end_pfn < end)
end_pfn = end;

map_memory_bank(bank);
if (!bank->highmem)
map_memory_bank(bank);
}

/*
* If there is no memory in this node, ignore it.
*/
if (end_pfn == 0)
return end_pfn;

/*
* Allocate the bootmem bitmap page.
*/
Expand All @@ -260,7 +270,8 @@ static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)

for_each_nodebank(i, mi, node) {
struct membank *bank = &mi->bank[i];
free_bootmem_node(pgdat, bank_phys_start(bank), bank_phys_size(bank));
if (!bank->highmem)
free_bootmem_node(pgdat, bank_phys_start(bank), bank_phys_size(bank));
memory_present(node, bank_pfn_start(bank), bank_pfn_end(bank));
}

Expand All @@ -269,8 +280,6 @@ static unsigned long __init bootmem_init_node(int node, struct meminfo *mi)
*/
reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT,
boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT);

return end_pfn;
}

static void __init bootmem_reserve_initrd(int node)
Expand All @@ -297,59 +306,83 @@ static void __init bootmem_reserve_initrd(int node)
static void __init bootmem_free_node(int node, struct meminfo *mi)
{
unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
unsigned long start_pfn, end_pfn;
pg_data_t *pgdat = NODE_DATA(node);
unsigned long min, max_low, max_high;
int i;

start_pfn = pgdat->bdata->node_min_pfn;
end_pfn = pgdat->bdata->node_low_pfn;
find_node_limits(node, mi, &min, &max_low, &max_high);

/*
* initialise the zones within this node.
*/
memset(zone_size, 0, sizeof(zone_size));
memset(zhole_size, 0, sizeof(zhole_size));

/*
* The size of this node has already been determined. If we need
* to do anything fancy with the allocation of this memory to the
* zones, now is the time to do it.
*/
zone_size[0] = end_pfn - start_pfn;
zone_size[0] = max_low - min;
#ifdef CONFIG_HIGHMEM
zone_size[ZONE_HIGHMEM] = max_high - max_low;
#endif

/*
* For each bank in this node, calculate the size of the holes.
* holes = node_size - sum(bank_sizes_in_node)
*/
zhole_size[0] = zone_size[0];
for_each_nodebank(i, mi, node)
zhole_size[0] -= bank_pfn_size(&mi->bank[i]);
memcpy(zhole_size, zone_size, sizeof(zhole_size));
for_each_nodebank(i, mi, node) {
int idx = 0;
#ifdef CONFIG_HIGHMEM
if (mi->bank[i].highmem)
idx = ZONE_HIGHMEM;
#endif
zhole_size[idx] -= bank_pfn_size(&mi->bank[i]);
}

/*
* Adjust the sizes according to any special requirements for
* this machine type.
*/
arch_adjust_zones(node, zone_size, zhole_size);

free_area_init_node(node, zone_size, start_pfn, zhole_size);
free_area_init_node(node, zone_size, min, zhole_size);
}

void __init bootmem_init(void)
{
struct meminfo *mi = &meminfo;
unsigned long memend_pfn = 0;
unsigned long min, max_low, max_high;
int node, initrd_node;

/*
* Locate which node contains the ramdisk image, if any.
*/
initrd_node = check_initrd(mi);

max_low = max_high = 0;

/*
* Run through each node initialising the bootmem allocator.
*/
for_each_node(node) {
unsigned long end_pfn = bootmem_init_node(node, mi);
unsigned long node_low, node_high;

find_node_limits(node, mi, &min, &node_low, &node_high);

if (node_low > max_low)
max_low = node_low;
if (node_high > max_high)
max_high = node_high;

/*
* If there is no memory in this node, ignore it.
* (We can't have nodes which have no lowmem)
*/
if (node_low == 0)
continue;

bootmem_init_node(node, mi, min, node_low);

/*
* Reserve any special node zero regions.
Expand All @@ -362,12 +395,6 @@ void __init bootmem_init(void)
*/
if (node == initrd_node)
bootmem_reserve_initrd(node);

/*
* Remember the highest memory PFN.
*/
if (end_pfn > memend_pfn)
memend_pfn = end_pfn;
}

/*
Expand All @@ -383,7 +410,7 @@ void __init bootmem_init(void)
for_each_node(node)
bootmem_free_node(node, mi);

high_memory = __va((memend_pfn << PAGE_SHIFT) - 1) + 1;
high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1;

/*
* This doesn't seem to be used by the Linux memory manager any
Expand All @@ -393,7 +420,8 @@ void __init bootmem_init(void)
* Note: max_low_pfn and max_pfn reflect the number of _pages_ in
* the system, not the maximum PFN.
*/
max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;
max_low_pfn = max_low - PHYS_PFN_OFFSET;
max_pfn = max_high - PHYS_PFN_OFFSET;
}

static inline int free_area(unsigned long pfn, unsigned long end, char *s)
Expand Down
9 changes: 8 additions & 1 deletion arch/arm/mm/mmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -687,13 +687,19 @@ __early_param("vmalloc=", early_vmalloc);

static void __init sanity_check_meminfo(void)
{
int i, j;
int i, j, highmem = 0;

for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
struct membank *bank = &meminfo.bank[j];
*bank = meminfo.bank[i];

#ifdef CONFIG_HIGHMEM
if (__va(bank->start) > VMALLOC_MIN ||
__va(bank->start) < (void *)PAGE_OFFSET)
highmem = 1;

bank->highmem = highmem;

/*
* Split those memory banks which are partially overlapping
* the vmalloc area greatly simplifying things later.
Expand All @@ -714,6 +720,7 @@ static void __init sanity_check_meminfo(void)
i++;
bank[1].size -= VMALLOC_MIN - __va(bank->start);
bank[1].start = __pa(VMALLOC_MIN - 1) + 1;
bank[1].highmem = highmem = 1;
j++;
}
bank->size = VMALLOC_MIN - __va(bank->start);
Expand Down

0 comments on commit dde5828

Please sign in to comment.