@@ -41,10 +41,39 @@ static frames_array_t early_frames;
4141static list_head_t free_frames [MAX_PAGE_ORDER + 1 ];
4242static 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))
4554static size_t frames_count [MAX_PAGE_ORDER + 1 ];
4655
56+ /* first lock to serialize normal access to pmm (i.e. not array frame refill) */
4757static 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+ static void try_create_4k_frames (void );
67+
68+ static void pmm_lock () {
69+ spin_lock (& lock );
70+ spin_lock (& priority_lock );
71+ }
72+
73+ static void pmm_unlock () {
74+ spin_unlock (& priority_lock );
75+ spin_unlock (& lock );
76+ }
4877
4978void display_frames_count (void ) {
5079 printk ("Avail memory frames: (total size: %lu MB)\n" , total_phys_memory / MB (1 ));
@@ -109,7 +138,49 @@ static frames_array_t *new_frames_array(void) {
109138 if (!boot_flags .virt )
110139 array = (frames_array_t * ) mfn_to_virt_kern (frame -> mfn );
111140 else {
141+ /* switch to special refilling mode to avoid deadlock with paging */
142+ spin_unlock (& priority_lock );
143+
144+ /* only paging is allowed to use pmm with only the priority_lock */
112145 array = vmap_kern_4k (mfn_to_virt_map (frame -> mfn ), frame -> mfn , L1_PROT );
146+
147+ /* switch back to normal mode */
148+ spin_lock (& priority_lock );
149+
150+ if (!array )
151+ goto error ;
152+ }
153+
154+ dprintk ("%s: allocated new frames array: %p\n" , __func__ , array );
155+
156+ init_frames_array (array );
157+
158+ return array ;
159+ error :
160+ panic ("PMM: Unable to allocate new page for frame array" );
161+ UNREACHABLE ();
162+ }
163+
164+ static frames_array_t * new_frames_array_nolock (void ) {
165+ frames_array_t * array ;
166+ frame_t * frame ;
167+
168+ frame = reserve_frame (get_first_frame (free_frames , PAGE_ORDER_4K ));
169+ if (!frame )
170+ goto error ;
171+
172+ if (!boot_flags .virt )
173+ array = (frames_array_t * ) mfn_to_virt_kern (frame -> mfn );
174+ else {
175+ /* switch to special refilling mode to avoid deadlock with paging */
176+ spin_unlock (& priority_lock );
177+
178+ /* only paging is allowed to use pmm with only the priority_lock */
179+ array = vmap_pmm_refill_nolock (mfn_to_virt_map (frame -> mfn ), frame -> mfn , L1_PROT );
180+
181+ /* switch back to normal mode */
182+ spin_lock (& priority_lock );
183+
113184 if (!array )
114185 goto error ;
115186 }
@@ -426,31 +497,31 @@ static frame_t *_find_mfn_frame(list_head_t *list, mfn_t mfn, unsigned int order
426497frame_t * find_free_mfn_frame (mfn_t mfn , unsigned int order ) {
427498 frame_t * frame ;
428499
429- spin_lock ( & lock );
500+ pmm_lock ( );
430501 frame = _find_mfn_frame (free_frames , mfn , order );
431- spin_unlock ( & lock );
502+ pmm_unlock ( );
432503
433504 return frame ;
434505}
435506
436507frame_t * find_busy_mfn_frame (mfn_t mfn , unsigned int order ) {
437508 frame_t * frame ;
438509
439- spin_lock ( & lock );
510+ pmm_lock ( );
440511 frame = _find_mfn_frame (busy_frames , mfn , order );
441- spin_unlock ( & lock );
512+ pmm_unlock ( );
442513
443514 return frame ;
444515}
445516
446517frame_t * find_mfn_frame (mfn_t mfn , unsigned int order ) {
447518 frame_t * frame ;
448519
449- spin_lock ( & lock );
520+ pmm_lock ( );
450521 frame = _find_mfn_frame (busy_frames , mfn , order );
451522 if (!frame )
452523 frame = _find_mfn_frame (free_frames , mfn , order );
453- spin_unlock ( & lock );
524+ pmm_unlock ( );
454525
455526 return frame ;
456527}
@@ -471,31 +542,31 @@ static frame_t *_find_paddr_frame(list_head_t *list, paddr_t paddr) {
471542frame_t * find_free_paddr_frame (paddr_t paddr ) {
472543 frame_t * frame ;
473544
474- spin_lock ( & lock );
545+ pmm_lock ( );
475546 frame = _find_paddr_frame (free_frames , paddr );
476- spin_unlock ( & lock );
547+ pmm_unlock ( );
477548
478549 return frame ;
479550}
480551
481552frame_t * find_busy_paddr_frame (paddr_t paddr ) {
482553 frame_t * frame ;
483554
484- spin_lock ( & lock );
555+ pmm_lock ( );
485556 frame = _find_paddr_frame (busy_frames , paddr );
486- spin_unlock ( & lock );
557+ pmm_unlock ( );
487558
488559 return frame ;
489560}
490561
491562frame_t * find_paddr_frame (paddr_t paddr ) {
492563 frame_t * frame ;
493564
494- spin_lock ( & lock );
565+ pmm_lock ( );
495566 frame = _find_paddr_frame (busy_frames , paddr );
496567 if (!frame )
497568 frame = _find_paddr_frame (free_frames , paddr );
498- spin_unlock ( & lock );
569+ pmm_unlock ( );
499570
500571 return frame ;
501572}
@@ -599,20 +670,20 @@ static void try_create_4k_frames(void) {
599670 * This function does not split larger frames.
600671 */
601672frame_t * get_free_frames_cond (free_frames_cond_t cb ) {
602- spin_lock ( & lock );
673+ pmm_lock ( );
603674 try_create_4k_frames ();
604675 for_each_order (order ) {
605676 frame_t * frame ;
606677
607678 list_for_each_entry (frame , & free_frames [order ], list ) {
608679 if (cb (frame )) {
609680 reserve_frame (frame );
610- spin_unlock ( & lock );
681+ pmm_unlock ( );
611682 return frame ;
612683 }
613684 }
614685 }
615- spin_unlock ( & lock );
686+ pmm_unlock ( );
616687
617688 return NULL ;
618689}
@@ -623,22 +694,22 @@ frame_t *get_free_frames(unsigned int order) {
623694 if (order > MAX_PAGE_ORDER )
624695 return NULL ;
625696
626- spin_lock ( & lock );
697+ pmm_lock ( );
627698 if (order == PAGE_ORDER_4K )
628699 try_create_4k_frames ();
629700
630701 while (list_is_empty (& free_frames [order ])) {
631702 BUG_ON (order == PAGE_ORDER_4K );
632703 frame = find_larger_frame (free_frames , order );
633704 if (!frame ) {
634- spin_unlock ( & lock );
705+ pmm_unlock ( );
635706 return NULL ;
636707 }
637708 split_frame (frame );
638709 }
639710
640711 frame = reserve_frame (get_first_frame (free_frames , order ));
641- spin_unlock ( & lock );
712+ pmm_unlock ( );
642713
643714 return frame ;
644715}
@@ -648,7 +719,7 @@ void put_free_frames(mfn_t mfn, unsigned int order) {
648719
649720 ASSERT (order <= MAX_PAGE_ORDER );
650721
651- spin_lock ( & lock );
722+ pmm_lock ( );
652723 frame = _find_mfn_frame (busy_frames , mfn , order );
653724 if (!frame ) {
654725 warning ("PMM: unable to find frame: %lx, order: %u among busy frames" , mfn ,
@@ -660,7 +731,7 @@ void put_free_frames(mfn_t mfn, unsigned int order) {
660731 merge_frames (frame );
661732
662733unlock :
663- spin_unlock ( & lock );
734+ pmm_unlock ( );
664735}
665736
666737void map_frames_array (void ) {
@@ -674,3 +745,33 @@ void map_frames_array(void) {
674745 BUG_ON (!vmap_kern_4k (va , mfn , L1_PROT ));
675746 }
676747}
748+
749+ /* functions for paging to avoid deadlocks */
750+
751+ frame_t * get_free_frame_norefill (void ) {
752+ frame_t * frame ;
753+
754+ spin_lock (& priority_lock );
755+ frame = reserve_frame (get_first_frame (free_frames , PAGE_ORDER_4K ));
756+ spin_unlock (& priority_lock );
757+
758+ /* we ran out of reserved frames. increase MIN_NUM_4K_FRAMES */
759+ BUG_ON (!frame );
760+
761+ return frame ;
762+ }
763+
764+ void refill_from_paging (void ) {
765+ spin_lock (& priority_lock );
766+
767+ /* ensure enough space to refill 4K frames without frame array allocation */
768+ if (total_free_frames < MIN_NOREFILL_FREE_FRAMES_THRESHOLD )
769+ new_frames_array_nolock ();
770+ /* if this fails, increase MIN_NUM_4K_FRAMES to allow for multiple array refills */
771+ BUG_ON (total_free_frames < MIN_NOREFILL_FREE_FRAMES_THRESHOLD );
772+
773+ /* refill the 4K frames */
774+ try_create_4k_frames ();
775+
776+ spin_unlock (& priority_lock );
777+ }
0 commit comments