Skip to content

Commit bdc8cb9

Browse files
hansendcLinus Torvalds
authored andcommitted
[PATCH] memory hotplug locking: zone span seqlock
See the "fixup bad_range()" patch for more information, but this actually creates a the lock to protect things making assumptions about a zone's size staying constant at runtime. Signed-off-by: Dave Hansen <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 208d54e commit bdc8cb9

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

include/linux/memory_hotplug.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,54 @@ void pgdat_resize_lock(struct pglist_data *pgdat, unsigned long *flags)
1616
static inline
1717
void pgdat_resize_unlock(struct pglist_data *pgdat, unsigned long *flags)
1818
{
19-
spin_lock_irqrestore(&pgdat->node_size_lock, *flags);
19+
spin_unlock_irqrestore(&pgdat->node_size_lock, *flags);
2020
}
2121
static inline
2222
void pgdat_resize_init(struct pglist_data *pgdat)
2323
{
2424
spin_lock_init(&pgdat->node_size_lock);
2525
}
26+
/*
27+
* Zone resizing functions
28+
*/
29+
static inline unsigned zone_span_seqbegin(struct zone *zone)
30+
{
31+
return read_seqbegin(&zone->span_seqlock);
32+
}
33+
static inline int zone_span_seqretry(struct zone *zone, unsigned iv)
34+
{
35+
return read_seqretry(&zone->span_seqlock, iv);
36+
}
37+
static inline void zone_span_writelock(struct zone *zone)
38+
{
39+
write_seqlock(&zone->span_seqlock);
40+
}
41+
static inline void zone_span_writeunlock(struct zone *zone)
42+
{
43+
write_sequnlock(&zone->span_seqlock);
44+
}
45+
static inline void zone_seqlock_init(struct zone *zone)
46+
{
47+
seqlock_init(&zone->span_seqlock);
48+
}
2649
#else /* ! CONFIG_MEMORY_HOTPLUG */
2750
/*
2851
* Stub functions for when hotplug is off
2952
*/
3053
static inline void pgdat_resize_lock(struct pglist_data *p, unsigned long *f) {}
3154
static inline void pgdat_resize_unlock(struct pglist_data *p, unsigned long *f) {}
3255
static inline void pgdat_resize_init(struct pglist_data *pgdat) {}
33-
#endif
56+
57+
static inline unsigned zone_span_seqbegin(struct zone *zone)
58+
{
59+
return 0;
60+
}
61+
static inline int zone_span_seqretry(struct zone *zone, unsigned iv)
62+
{
63+
return 0;
64+
}
65+
static inline void zone_span_writelock(struct zone *zone) {}
66+
static inline void zone_span_writeunlock(struct zone *zone) {}
67+
static inline void zone_seqlock_init(struct zone *zone) {}
68+
#endif /* ! CONFIG_MEMORY_HOTPLUG */
3469
#endif /* __LINUX_MEMORY_HOTPLUG_H */

include/linux/mmzone.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/threads.h>
1313
#include <linux/numa.h>
1414
#include <linux/init.h>
15+
#include <linux/seqlock.h>
1516
#include <asm/atomic.h>
1617

1718
/* Free memory management - zoned buddy allocator. */
@@ -137,6 +138,10 @@ struct zone {
137138
* free areas of different sizes
138139
*/
139140
spinlock_t lock;
141+
#ifdef CONFIG_MEMORY_HOTPLUG
142+
/* see spanned/present_pages for more description */
143+
seqlock_t span_seqlock;
144+
#endif
140145
struct free_area free_area[MAX_ORDER];
141146

142147

@@ -220,6 +225,16 @@ struct zone {
220225
/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
221226
unsigned long zone_start_pfn;
222227

228+
/*
229+
* zone_start_pfn, spanned_pages and present_pages are all
230+
* protected by span_seqlock. It is a seqlock because it has
231+
* to be read outside of zone->lock, and it is done in the main
232+
* allocator path. But, it is written quite infrequently.
233+
*
234+
* The lock is declared along with zone->lock because it is
235+
* frequently read in proximity to zone->lock. It's good to
236+
* give them a chance of being in the same cacheline.
237+
*/
223238
unsigned long spanned_pages; /* total size, including holes */
224239
unsigned long present_pages; /* amount of memory (excluding holes) */
225240

mm/page_alloc.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/sysctl.h>
3434
#include <linux/cpu.h>
3535
#include <linux/cpuset.h>
36+
#include <linux/memory_hotplug.h>
3637
#include <linux/nodemask.h>
3738
#include <linux/vmalloc.h>
3839

@@ -80,12 +81,19 @@ unsigned long __initdata nr_all_pages;
8081

8182
static int page_outside_zone_boundaries(struct zone *zone, struct page *page)
8283
{
83-
if (page_to_pfn(page) >= zone->zone_start_pfn + zone->spanned_pages)
84-
return 1;
85-
if (page_to_pfn(page) < zone->zone_start_pfn)
86-
return 1;
84+
int ret = 0;
85+
unsigned seq;
86+
unsigned long pfn = page_to_pfn(page);
8787

88-
return 0;
88+
do {
89+
seq = zone_span_seqbegin(zone);
90+
if (pfn >= zone->zone_start_pfn + zone->spanned_pages)
91+
ret = 1;
92+
else if (pfn < zone->zone_start_pfn)
93+
ret = 1;
94+
} while (zone_span_seqretry(zone, seq));
95+
96+
return ret;
8997
}
9098

9199
static int page_is_consistent(struct zone *zone, struct page *page)
@@ -1980,6 +1988,7 @@ static void __init free_area_init_core(struct pglist_data *pgdat,
19801988
zone->name = zone_names[j];
19811989
spin_lock_init(&zone->lock);
19821990
spin_lock_init(&zone->lru_lock);
1991+
zone_seqlock_init(zone);
19831992
zone->zone_pgdat = pgdat;
19841993
zone->free_pages = 0;
19851994

0 commit comments

Comments
 (0)