Skip to content

Commit a6f23ee

Browse files
committed
pagetables,pmm: fix deadlock
1. paging gets exclusive pmm access during array refill. 2. pmm can call back into paging while holding the paging lock. 3. paging performs pmm refill if needed to ensure reserved frames. Signed-off-by: Sandro Rüegge <[email protected]>
1 parent ba970a2 commit a6f23ee

File tree

4 files changed

+152
-23
lines changed

4 files changed

+152
-23
lines changed

arch/x86/pagetables.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ static mfn_t get_cr3_mfn(cr3_t *cr3_entry) {
200200
void *cr3_mapped = NULL;
201201

202202
if (mfn_invalid(cr3_entry->mfn)) {
203-
frame_t *frame = get_free_frame();
203+
frame_t *frame = get_free_frame_norefill();
204204
BUG_ON(!frame);
205205
frame->flags.pagetable = 1;
206206

@@ -243,7 +243,7 @@ static mfn_t get_pgentry_mfn(mfn_t tab_mfn, pt_index_t index, unsigned long flag
243243

244244
mfn = mfn_from_pgentry(*entry);
245245
if (mfn_invalid(mfn)) {
246-
frame_t *frame = get_free_frame();
246+
frame_t *frame = get_free_frame_norefill();
247247
BUG_ON(!frame);
248248
frame->flags.pagetable = 1;
249249

@@ -308,6 +308,7 @@ static void *_vmap(cr3_t *cr3_ptr, void *va, mfn_t mfn, unsigned int order,
308308
invlpg(va);
309309

310310
done:
311+
refill_from_paging();
311312
return va;
312313
}
313314

@@ -420,6 +421,18 @@ void *vmap_4k(cr3_t *cr3_ptr, void *va, mfn_t mfn, unsigned long l1_flags,
420421
return va;
421422
}
422423

424+
void *vmap_pmm_refill_nolock(void *va, mfn_t mfn, unsigned long l1_flags) {
425+
unsigned long _va = _ul(va) & PAGE_ORDER_TO_MASK(PAGE_ORDER_4K);
426+
427+
dprintk("%s: va: 0x%p mfn: 0x%lx\n", __func__, va, mfn);
428+
429+
BUG_ON(!vmap_lock);
430+
va = _vmap_4k(&cr3, _ptr(_va), mfn, l1_flags, false);
431+
BUG_ON(!vmap_lock);
432+
433+
return va;
434+
}
435+
423436
static inline void init_tmp_mapping(void) {
424437
pte_t *tab = get_l1_table(_tmp_mapping);
425438
_tmp_mapping_entry = (pgentry_t *) l1_table_entry(tab, _tmp_mapping);

include/arch/x86/pagetable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ static inline void *vmap_kern_4k(void *va, mfn_t mfn, unsigned long l1_flags) {
360360
return vmap_4k(&cr3, va, mfn, l1_flags, false);
361361
}
362362

363+
void *vmap_pmm_refill_nolock(void *va, mfn_t mfn, unsigned long l1_flags);
364+
363365
static inline void *vmap_user(void *va, mfn_t mfn, unsigned int order,
364366
#if defined(__x86_64__)
365367
unsigned long l4_flags,

include/mm/pmm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ extern void init_pmm(void);
7373

7474
extern frame_t *get_free_frames_cond(free_frames_cond_t cb);
7575
extern frame_t *get_free_frames(unsigned int order);
76+
extern frame_t *get_free_frame_norefill(void);
7677
extern void put_free_frames(mfn_t mfn, unsigned int order);
7778
extern void reclaim_frame(mfn_t mfn, unsigned int order);
7879

@@ -84,6 +85,7 @@ extern frame_t *find_busy_paddr_frame(paddr_t paddr);
8485
extern frame_t *find_paddr_frame(paddr_t paddr);
8586

8687
extern void map_frames_array(void);
88+
extern void refill_from_paging(void);
8789

8890
/* Static definitions */
8991

mm/pmm.c

Lines changed: 133 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,42 @@ static frames_array_t early_frames;
4141
static list_head_t free_frames[MAX_PAGE_ORDER + 1];
4242
static list_head_t busy_frames[MAX_PAGE_ORDER + 1];
4343

44-
#define MIN_NUM_4K_FRAMES 2
44+
/*
45+
* Worst case: pmm wants to refill while paging just started on another thread
46+
* 1 for pmm refill attempt (new_frames_array)
47+
* 3 for paging currently ongoing
48+
* 4 for refill_from_paging (1 array frame, 3 for mapping it)
49+
*/
50+
#define MIN_NUM_4K_FRAMES (1 + 3 + (1 + 3))
51+
/* enough array frames to refill 4K frames in the worst case without needing refill */
52+
#define MIN_NOREFILL_FREE_FRAMES_THRESHOLD \
53+
(MIN_FREE_FRAMES_THRESHOLD + MIN_NUM_4K_FRAMES + (MAX_PAGE_ORDER - PAGE_ORDER_4K))
4554
static size_t frames_count[MAX_PAGE_ORDER + 1];
4655

56+
/* first lock to serialize normal access to pmm (i.e. not array frame refill) */
4757
static spinlock_t lock = SPINLOCK_INIT;
58+
/**
59+
* second lock to give paging priority access to pmm during array frame refill
60+
*
61+
* Ensure that get_free_frame_norefill and refill_from_paging are only called while
62+
* holding the paging lock.
63+
*/
64+
static spinlock_t priority_lock = SPINLOCK_INIT;
65+
66+
/* avoid recursive refilling after paging */
67+
static bool refill_from_paging_ongoing = false;
68+
69+
static void try_create_4k_frames(void);
70+
71+
static void pmm_lock() {
72+
spin_lock(&lock);
73+
spin_lock(&priority_lock);
74+
}
75+
76+
static void pmm_unlock() {
77+
spin_unlock(&priority_lock);
78+
spin_unlock(&lock);
79+
}
4880

4981
void display_frames_count(void) {
5082
printk("Avail memory frames: (total size: %lu MB)\n", total_phys_memory / MB(1));
@@ -109,7 +141,49 @@ static frames_array_t *new_frames_array(void) {
109141
if (!boot_flags.virt)
110142
array = (frames_array_t *) mfn_to_virt_kern(frame->mfn);
111143
else {
144+
/* switch to special refilling mode to avoid deadlock with paging */
145+
spin_unlock(&priority_lock);
146+
147+
/* only paging is allowed to use pmm with only the priority_lock */
112148
array = vmap_kern_4k(mfn_to_virt_map(frame->mfn), frame->mfn, L1_PROT);
149+
150+
/* switch back to normal mode */
151+
spin_lock(&priority_lock);
152+
153+
if (!array)
154+
goto error;
155+
}
156+
157+
dprintk("%s: allocated new frames array: %p\n", __func__, array);
158+
159+
init_frames_array(array);
160+
161+
return array;
162+
error:
163+
panic("PMM: Unable to allocate new page for frame array");
164+
UNREACHABLE();
165+
}
166+
167+
static frames_array_t *new_frames_array_nolock(void) {
168+
frames_array_t *array;
169+
frame_t *frame;
170+
171+
frame = reserve_frame(get_first_frame(free_frames, PAGE_ORDER_4K));
172+
if (!frame)
173+
goto error;
174+
175+
if (!boot_flags.virt)
176+
array = (frames_array_t *) mfn_to_virt_kern(frame->mfn);
177+
else {
178+
/* switch to special refilling mode to avoid deadlock with paging */
179+
spin_unlock(&priority_lock);
180+
181+
/* only paging is allowed to use pmm with only the priority_lock */
182+
array = vmap_pmm_refill_nolock(mfn_to_virt_map(frame->mfn), frame->mfn, L1_PROT);
183+
184+
/* switch back to normal mode */
185+
spin_lock(&priority_lock);
186+
113187
if (!array)
114188
goto error;
115189
}
@@ -426,31 +500,31 @@ static frame_t *_find_mfn_frame(list_head_t *list, mfn_t mfn, unsigned int order
426500
frame_t *find_free_mfn_frame(mfn_t mfn, unsigned int order) {
427501
frame_t *frame;
428502

429-
spin_lock(&lock);
503+
pmm_lock();
430504
frame = _find_mfn_frame(free_frames, mfn, order);
431-
spin_unlock(&lock);
505+
pmm_unlock();
432506

433507
return frame;
434508
}
435509

436510
frame_t *find_busy_mfn_frame(mfn_t mfn, unsigned int order) {
437511
frame_t *frame;
438512

439-
spin_lock(&lock);
513+
pmm_lock();
440514
frame = _find_mfn_frame(busy_frames, mfn, order);
441-
spin_unlock(&lock);
515+
pmm_unlock();
442516

443517
return frame;
444518
}
445519

446520
frame_t *find_mfn_frame(mfn_t mfn, unsigned int order) {
447521
frame_t *frame;
448522

449-
spin_lock(&lock);
523+
pmm_lock();
450524
frame = _find_mfn_frame(busy_frames, mfn, order);
451525
if (!frame)
452526
frame = _find_mfn_frame(free_frames, mfn, order);
453-
spin_unlock(&lock);
527+
pmm_unlock();
454528

455529
return frame;
456530
}
@@ -471,31 +545,31 @@ static frame_t *_find_paddr_frame(list_head_t *list, paddr_t paddr) {
471545
frame_t *find_free_paddr_frame(paddr_t paddr) {
472546
frame_t *frame;
473547

474-
spin_lock(&lock);
548+
pmm_lock();
475549
frame = _find_paddr_frame(free_frames, paddr);
476-
spin_unlock(&lock);
550+
pmm_unlock();
477551

478552
return frame;
479553
}
480554

481555
frame_t *find_busy_paddr_frame(paddr_t paddr) {
482556
frame_t *frame;
483557

484-
spin_lock(&lock);
558+
pmm_lock();
485559
frame = _find_paddr_frame(busy_frames, paddr);
486-
spin_unlock(&lock);
560+
pmm_unlock();
487561

488562
return frame;
489563
}
490564

491565
frame_t *find_paddr_frame(paddr_t paddr) {
492566
frame_t *frame;
493567

494-
spin_lock(&lock);
568+
pmm_lock();
495569
frame = _find_paddr_frame(busy_frames, paddr);
496570
if (!frame)
497571
frame = _find_paddr_frame(free_frames, paddr);
498-
spin_unlock(&lock);
572+
pmm_unlock();
499573

500574
return frame;
501575
}
@@ -599,20 +673,20 @@ static void try_create_4k_frames(void) {
599673
* This function does not split larger frames.
600674
*/
601675
frame_t *get_free_frames_cond(free_frames_cond_t cb) {
602-
spin_lock(&lock);
676+
pmm_lock();
603677
try_create_4k_frames();
604678
for_each_order (order) {
605679
frame_t *frame;
606680

607681
list_for_each_entry (frame, &free_frames[order], list) {
608682
if (cb(frame)) {
609683
reserve_frame(frame);
610-
spin_unlock(&lock);
684+
pmm_unlock();
611685
return frame;
612686
}
613687
}
614688
}
615-
spin_unlock(&lock);
689+
pmm_unlock();
616690

617691
return NULL;
618692
}
@@ -623,22 +697,22 @@ frame_t *get_free_frames(unsigned int order) {
623697
if (order > MAX_PAGE_ORDER)
624698
return NULL;
625699

626-
spin_lock(&lock);
700+
pmm_lock();
627701
if (order == PAGE_ORDER_4K)
628702
try_create_4k_frames();
629703

630704
while (list_is_empty(&free_frames[order])) {
631705
BUG_ON(order == PAGE_ORDER_4K);
632706
frame = find_larger_frame(free_frames, order);
633707
if (!frame) {
634-
spin_unlock(&lock);
708+
pmm_unlock();
635709
return NULL;
636710
}
637711
split_frame(frame);
638712
}
639713

640714
frame = reserve_frame(get_first_frame(free_frames, order));
641-
spin_unlock(&lock);
715+
pmm_unlock();
642716

643717
return frame;
644718
}
@@ -648,7 +722,7 @@ void put_free_frames(mfn_t mfn, unsigned int order) {
648722

649723
ASSERT(order <= MAX_PAGE_ORDER);
650724

651-
spin_lock(&lock);
725+
pmm_lock();
652726
frame = _find_mfn_frame(busy_frames, mfn, order);
653727
if (!frame) {
654728
warning("PMM: unable to find frame: %lx, order: %u among busy frames", mfn,
@@ -660,7 +734,7 @@ void put_free_frames(mfn_t mfn, unsigned int order) {
660734
merge_frames(frame);
661735

662736
unlock:
663-
spin_unlock(&lock);
737+
pmm_unlock();
664738
}
665739

666740
void map_frames_array(void) {
@@ -674,3 +748,41 @@ void map_frames_array(void) {
674748
BUG_ON(!vmap_kern_4k(va, mfn, L1_PROT));
675749
}
676750
}
751+
752+
/* functions for paging to avoid deadlocks */
753+
754+
frame_t *get_free_frame_norefill(void) {
755+
frame_t *frame;
756+
757+
spin_lock(&priority_lock);
758+
frame = reserve_frame(get_first_frame(free_frames, PAGE_ORDER_4K));
759+
spin_unlock(&priority_lock);
760+
761+
/* we ran out of reserved frames. increase MIN_NUM_4K_FRAMES */
762+
BUG_ON(!frame);
763+
764+
return frame;
765+
}
766+
767+
void refill_from_paging(void) {
768+
spin_lock(&priority_lock);
769+
770+
/* avoid refill_from_paging being called from refill_from_paging */
771+
if (!refill_from_paging_ongoing) {
772+
refill_from_paging_ongoing = true;
773+
774+
/* ensure enough space to refill 4K frames without frame array allocation */
775+
if (total_free_frames < MIN_NOREFILL_FREE_FRAMES_THRESHOLD)
776+
new_frames_array_nolock();
777+
/* if this fails, increase MIN_NUM_4K_FRAMES to allow for multiple array refills
778+
*/
779+
BUG_ON(total_free_frames < MIN_NOREFILL_FREE_FRAMES_THRESHOLD);
780+
781+
/* refill the 4K frames */
782+
try_create_4k_frames();
783+
784+
refill_from_paging_ongoing = false;
785+
}
786+
787+
spin_unlock(&priority_lock);
788+
}

0 commit comments

Comments
 (0)