|
@@ -2931,6 +2931,41 @@ static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
|
|
|
realtotalpages);
|
|
|
}
|
|
|
|
|
|
+#ifndef CONFIG_SPARSEMEM
|
|
|
+/*
|
|
|
+ * Calculate the size of the zone->blockflags rounded to an unsigned long
|
|
|
+ * Start by making sure zonesize is a multiple of MAX_ORDER-1 by rounding up
|
|
|
+ * Then figure 1 NR_PAGEBLOCK_BITS worth of bits per MAX_ORDER-1, finally
|
|
|
+ * round what is now in bits to nearest long in bits, then return it in
|
|
|
+ * bytes.
|
|
|
+ */
|
|
|
+static unsigned long __init usemap_size(unsigned long zonesize)
|
|
|
+{
|
|
|
+ unsigned long usemapsize;
|
|
|
+
|
|
|
+ usemapsize = roundup(zonesize, MAX_ORDER_NR_PAGES);
|
|
|
+ usemapsize = usemapsize >> (MAX_ORDER-1);
|
|
|
+ usemapsize *= NR_PAGEBLOCK_BITS;
|
|
|
+ usemapsize = roundup(usemapsize, 8 * sizeof(unsigned long));
|
|
|
+
|
|
|
+ return usemapsize / 8;
|
|
|
+}
|
|
|
+
|
|
|
+static void __init setup_usemap(struct pglist_data *pgdat,
|
|
|
+ struct zone *zone, unsigned long zonesize)
|
|
|
+{
|
|
|
+ unsigned long usemapsize = usemap_size(zonesize);
|
|
|
+ zone->pageblock_flags = NULL;
|
|
|
+ if (usemapsize) {
|
|
|
+ zone->pageblock_flags = alloc_bootmem_node(pgdat, usemapsize);
|
|
|
+ memset(zone->pageblock_flags, 0, usemapsize);
|
|
|
+ }
|
|
|
+}
|
|
|
+#else
|
|
|
+static void inline setup_usemap(struct pglist_data *pgdat,
|
|
|
+ struct zone *zone, unsigned long zonesize) {}
|
|
|
+#endif /* CONFIG_SPARSEMEM */
|
|
|
+
|
|
|
/*
|
|
|
* Set up the zone data structures:
|
|
|
* - mark all pages reserved
|
|
@@ -3011,6 +3046,7 @@ static void __meminit free_area_init_core(struct pglist_data *pgdat,
|
|
|
if (!size)
|
|
|
continue;
|
|
|
|
|
|
+ setup_usemap(pgdat, zone, size);
|
|
|
ret = init_currently_empty_zone(zone, zone_start_pfn,
|
|
|
size, MEMMAP_EARLY);
|
|
|
BUG_ON(ret);
|
|
@@ -3991,4 +4027,79 @@ EXPORT_SYMBOL(pfn_to_page);
|
|
|
EXPORT_SYMBOL(page_to_pfn);
|
|
|
#endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */
|
|
|
|
|
|
+/* Return a pointer to the bitmap storing bits affecting a block of pages */
|
|
|
+static inline unsigned long *get_pageblock_bitmap(struct zone *zone,
|
|
|
+ unsigned long pfn)
|
|
|
+{
|
|
|
+#ifdef CONFIG_SPARSEMEM
|
|
|
+ return __pfn_to_section(pfn)->pageblock_flags;
|
|
|
+#else
|
|
|
+ return zone->pageblock_flags;
|
|
|
+#endif /* CONFIG_SPARSEMEM */
|
|
|
+}
|
|
|
+
|
|
|
+static inline int pfn_to_bitidx(struct zone *zone, unsigned long pfn)
|
|
|
+{
|
|
|
+#ifdef CONFIG_SPARSEMEM
|
|
|
+ pfn &= (PAGES_PER_SECTION-1);
|
|
|
+ return (pfn >> (MAX_ORDER-1)) * NR_PAGEBLOCK_BITS;
|
|
|
+#else
|
|
|
+ pfn = pfn - zone->zone_start_pfn;
|
|
|
+ return (pfn >> (MAX_ORDER-1)) * NR_PAGEBLOCK_BITS;
|
|
|
+#endif /* CONFIG_SPARSEMEM */
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * get_pageblock_flags_group - Return the requested group of flags for the MAX_ORDER_NR_PAGES block of pages
|
|
|
+ * @page: The page within the block of interest
|
|
|
+ * @start_bitidx: The first bit of interest to retrieve
|
|
|
+ * @end_bitidx: The last bit of interest
|
|
|
+ * returns pageblock_bits flags
|
|
|
+ */
|
|
|
+unsigned long get_pageblock_flags_group(struct page *page,
|
|
|
+ int start_bitidx, int end_bitidx)
|
|
|
+{
|
|
|
+ struct zone *zone;
|
|
|
+ unsigned long *bitmap;
|
|
|
+ unsigned long pfn, bitidx;
|
|
|
+ unsigned long flags = 0;
|
|
|
+ unsigned long value = 1;
|
|
|
+
|
|
|
+ zone = page_zone(page);
|
|
|
+ pfn = page_to_pfn(page);
|
|
|
+ bitmap = get_pageblock_bitmap(zone, pfn);
|
|
|
+ bitidx = pfn_to_bitidx(zone, pfn);
|
|
|
+
|
|
|
+ for (; start_bitidx <= end_bitidx; start_bitidx++, value <<= 1)
|
|
|
+ if (test_bit(bitidx + start_bitidx, bitmap))
|
|
|
+ flags |= value;
|
|
|
|
|
|
+ return flags;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * set_pageblock_flags_group - Set the requested group of flags for a MAX_ORDER_NR_PAGES block of pages
|
|
|
+ * @page: The page within the block of interest
|
|
|
+ * @start_bitidx: The first bit of interest
|
|
|
+ * @end_bitidx: The last bit of interest
|
|
|
+ * @flags: The flags to set
|
|
|
+ */
|
|
|
+void set_pageblock_flags_group(struct page *page, unsigned long flags,
|
|
|
+ int start_bitidx, int end_bitidx)
|
|
|
+{
|
|
|
+ struct zone *zone;
|
|
|
+ unsigned long *bitmap;
|
|
|
+ unsigned long pfn, bitidx;
|
|
|
+ unsigned long value = 1;
|
|
|
+
|
|
|
+ zone = page_zone(page);
|
|
|
+ pfn = page_to_pfn(page);
|
|
|
+ bitmap = get_pageblock_bitmap(zone, pfn);
|
|
|
+ bitidx = pfn_to_bitidx(zone, pfn);
|
|
|
+
|
|
|
+ for (; start_bitidx <= end_bitidx; start_bitidx++, value <<= 1)
|
|
|
+ if (flags & value)
|
|
|
+ __set_bit(bitidx + start_bitidx, bitmap);
|
|
|
+ else
|
|
|
+ __clear_bit(bitidx + start_bitidx, bitmap);
|
|
|
+}
|