diff options
author | Andy Whitcroft <apw@shadowen.org> | 2005-06-23 03:07:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-23 12:45:04 -0400 |
commit | d41dee369bff3b9dcb6328d4d822926c28cc2594 (patch) | |
tree | a0405f3b7af3ebca21838a7d427bd75a067bf850 /mm/page_alloc.c | |
parent | af705362ab6018071310c5fcd436a6b457517d5f (diff) |
[PATCH] sparsemem memory model
Sparsemem abstracts the use of discontiguous mem_maps[]. This kind of
mem_map[] is needed by discontiguous memory machines (like in the old
CONFIG_DISCONTIGMEM case) as well as memory hotplug systems. Sparsemem
replaces DISCONTIGMEM when enabled, and it is hoped that it can eventually
become a complete replacement.
A significant advantage over DISCONTIGMEM is that it's completely separated
from CONFIG_NUMA. When producing this patch, it became apparent in that NUMA
and DISCONTIG are often confused.
Another advantage is that sparse doesn't require each NUMA node's ranges to be
contiguous. It can handle overlapping ranges between nodes with no problems,
where DISCONTIGMEM currently throws away that memory.
Sparsemem uses an array to provide different pfn_to_page() translations for
each SECTION_SIZE area of physical memory. This is what allows the mem_map[]
to be chopped up.
In order to do quick pfn_to_page() operations, the section number of the page
is encoded in page->flags. Part of the sparsemem infrastructure enables
sharing of these bits more dynamically (at compile-time) between the
page_zone() and sparsemem operations. However, on 32-bit architectures, the
number of bits is quite limited, and may require growing the size of the
page->flags type in certain conditions. Several things might force this to
occur: a decrease in the SECTION_SIZE (if you want to hotplug smaller areas of
memory), an increase in the physical address space, or an increase in the
number of used page->flags.
One thing to note is that, once sparsemem is present, the NUMA node
information no longer needs to be stored in the page->flags. It might provide
speed increases on certain platforms and will be stored there if there is
room. But, if out of room, an alternate (theoretically slower) mechanism is
used.
This patch introduces CONFIG_FLATMEM. It is used in almost all cases where
there used to be an #ifndef DISCONTIG, because SPARSEMEM and DISCONTIGMEM
often have to compile out the same areas of code.
Signed-off-by: Andy Whitcroft <apw@shadowen.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Martin Bligh <mbligh@aracnet.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: Bob Picco <bob.picco@hp.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 39 |
1 files changed, 30 insertions, 9 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 20e239599db0..5c1b8982a6da 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -68,7 +68,7 @@ EXPORT_SYMBOL(nr_swap_pages); | |||
68 | * Used by page_zone() to look up the address of the struct zone whose | 68 | * Used by page_zone() to look up the address of the struct zone whose |
69 | * id is encoded in the upper bits of page->flags | 69 | * id is encoded in the upper bits of page->flags |
70 | */ | 70 | */ |
71 | struct zone *zone_table[1 << (ZONES_SHIFT + NODES_SHIFT)]; | 71 | struct zone *zone_table[1 << ZONETABLE_SHIFT]; |
72 | EXPORT_SYMBOL(zone_table); | 72 | EXPORT_SYMBOL(zone_table); |
73 | 73 | ||
74 | static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; | 74 | static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; |
@@ -1649,11 +1649,15 @@ static void __init calculate_zone_totalpages(struct pglist_data *pgdat, | |||
1649 | void __init memmap_init_zone(unsigned long size, int nid, unsigned long zone, | 1649 | void __init memmap_init_zone(unsigned long size, int nid, unsigned long zone, |
1650 | unsigned long start_pfn) | 1650 | unsigned long start_pfn) |
1651 | { | 1651 | { |
1652 | struct page *start = pfn_to_page(start_pfn); | ||
1653 | struct page *page; | 1652 | struct page *page; |
1653 | int end_pfn = start_pfn + size; | ||
1654 | int pfn; | ||
1654 | 1655 | ||
1655 | for (page = start; page < (start + size); page++) { | 1656 | for (pfn = start_pfn; pfn < end_pfn; pfn++, page++) { |
1656 | set_page_links(page, zone, nid); | 1657 | if (!early_pfn_valid(pfn)) |
1658 | continue; | ||
1659 | page = pfn_to_page(pfn); | ||
1660 | set_page_links(page, zone, nid, pfn); | ||
1657 | set_page_count(page, 0); | 1661 | set_page_count(page, 0); |
1658 | reset_page_mapcount(page); | 1662 | reset_page_mapcount(page); |
1659 | SetPageReserved(page); | 1663 | SetPageReserved(page); |
@@ -1677,6 +1681,20 @@ void zone_init_free_lists(struct pglist_data *pgdat, struct zone *zone, | |||
1677 | } | 1681 | } |
1678 | } | 1682 | } |
1679 | 1683 | ||
1684 | #define ZONETABLE_INDEX(x, zone_nr) ((x << ZONES_SHIFT) | zone_nr) | ||
1685 | void zonetable_add(struct zone *zone, int nid, int zid, unsigned long pfn, | ||
1686 | unsigned long size) | ||
1687 | { | ||
1688 | unsigned long snum = pfn_to_section_nr(pfn); | ||
1689 | unsigned long end = pfn_to_section_nr(pfn + size); | ||
1690 | |||
1691 | if (FLAGS_HAS_NODE) | ||
1692 | zone_table[ZONETABLE_INDEX(nid, zid)] = zone; | ||
1693 | else | ||
1694 | for (; snum <= end; snum++) | ||
1695 | zone_table[ZONETABLE_INDEX(snum, zid)] = zone; | ||
1696 | } | ||
1697 | |||
1680 | #ifndef __HAVE_ARCH_MEMMAP_INIT | 1698 | #ifndef __HAVE_ARCH_MEMMAP_INIT |
1681 | #define memmap_init(size, nid, zone, start_pfn) \ | 1699 | #define memmap_init(size, nid, zone, start_pfn) \ |
1682 | memmap_init_zone((size), (nid), (zone), (start_pfn)) | 1700 | memmap_init_zone((size), (nid), (zone), (start_pfn)) |
@@ -1861,7 +1879,6 @@ static void __init free_area_init_core(struct pglist_data *pgdat, | |||
1861 | unsigned long size, realsize; | 1879 | unsigned long size, realsize; |
1862 | unsigned long batch; | 1880 | unsigned long batch; |
1863 | 1881 | ||
1864 | zone_table[NODEZONE(nid, j)] = zone; | ||
1865 | realsize = size = zones_size[j]; | 1882 | realsize = size = zones_size[j]; |
1866 | if (zholes_size) | 1883 | if (zholes_size) |
1867 | realsize -= zholes_size[j]; | 1884 | realsize -= zholes_size[j]; |
@@ -1927,6 +1944,8 @@ static void __init free_area_init_core(struct pglist_data *pgdat, | |||
1927 | 1944 | ||
1928 | memmap_init(size, nid, j, zone_start_pfn); | 1945 | memmap_init(size, nid, j, zone_start_pfn); |
1929 | 1946 | ||
1947 | zonetable_add(zone, nid, j, zone_start_pfn, size); | ||
1948 | |||
1930 | zone_start_pfn += size; | 1949 | zone_start_pfn += size; |
1931 | 1950 | ||
1932 | zone_init_free_lists(pgdat, zone, zone->spanned_pages); | 1951 | zone_init_free_lists(pgdat, zone, zone->spanned_pages); |
@@ -1935,28 +1954,30 @@ static void __init free_area_init_core(struct pglist_data *pgdat, | |||
1935 | 1954 | ||
1936 | static void __init alloc_node_mem_map(struct pglist_data *pgdat) | 1955 | static void __init alloc_node_mem_map(struct pglist_data *pgdat) |
1937 | { | 1956 | { |
1938 | unsigned long size; | ||
1939 | struct page *map; | ||
1940 | |||
1941 | /* Skip empty nodes */ | 1957 | /* Skip empty nodes */ |
1942 | if (!pgdat->node_spanned_pages) | 1958 | if (!pgdat->node_spanned_pages) |
1943 | return; | 1959 | return; |
1944 | 1960 | ||
1961 | #ifdef CONFIG_FLAT_NODE_MEM_MAP | ||
1945 | /* ia64 gets its own node_mem_map, before this, without bootmem */ | 1962 | /* ia64 gets its own node_mem_map, before this, without bootmem */ |
1946 | if (!pgdat->node_mem_map) { | 1963 | if (!pgdat->node_mem_map) { |
1964 | unsigned long size; | ||
1965 | struct page *map; | ||
1966 | |||
1947 | size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); | 1967 | size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); |
1948 | map = alloc_remap(pgdat->node_id, size); | 1968 | map = alloc_remap(pgdat->node_id, size); |
1949 | if (!map) | 1969 | if (!map) |
1950 | map = alloc_bootmem_node(pgdat, size); | 1970 | map = alloc_bootmem_node(pgdat, size); |
1951 | pgdat->node_mem_map = map; | 1971 | pgdat->node_mem_map = map; |
1952 | } | 1972 | } |
1953 | #ifndef CONFIG_DISCONTIGMEM | 1973 | #ifdef CONFIG_FLATMEM |
1954 | /* | 1974 | /* |
1955 | * With no DISCONTIG, the global mem_map is just set as node 0's | 1975 | * With no DISCONTIG, the global mem_map is just set as node 0's |
1956 | */ | 1976 | */ |
1957 | if (pgdat == NODE_DATA(0)) | 1977 | if (pgdat == NODE_DATA(0)) |
1958 | mem_map = NODE_DATA(0)->node_mem_map; | 1978 | mem_map = NODE_DATA(0)->node_mem_map; |
1959 | #endif | 1979 | #endif |
1980 | #endif /* CONFIG_FLAT_NODE_MEM_MAP */ | ||
1960 | } | 1981 | } |
1961 | 1982 | ||
1962 | void __init free_area_init_node(int nid, struct pglist_data *pgdat, | 1983 | void __init free_area_init_node(int nid, struct pglist_data *pgdat, |