@@ -662,15 +662,16 @@ void *k_mem_map_phys_guard(uintptr_t phys, size_t size, uint32_t flags, bool is_
662662 arch_mem_map (dst , phys , size , flags );
663663 }
664664
665- if (!uninit ) {
665+ out :
666+ k_spin_unlock (& z_mm_lock , key );
667+
668+ if (dst != NULL && !uninit ) {
666669 /* If we later implement mappings to a copy-on-write
667670 * zero page, won't need this step
668671 */
669672 memset (dst , 0 , size );
670673 }
671674
672- out :
673- k_spin_unlock (& z_mm_lock , key );
674675 return dst ;
675676}
676677
@@ -1236,14 +1237,18 @@ static inline void do_backing_store_page_out(uintptr_t location)
12361237#endif /* CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM */
12371238}
12381239
1239- /* Current implementation relies on interrupt locking to any prevent page table
1240- * access, which falls over if other CPUs are active. Addressing this is not
1241- * as simple as using spinlocks as regular memory reads/writes constitute
1242- * "access" in this sense.
1243- *
1244- * Current needs for demand paging are on uniprocessor systems.
1240+ #if defined(CONFIG_SMP ) && defined(CONFIG_DEMAND_PAGING_ALLOW_IRQ )
1241+ /*
1242+ * SMP support is very simple. Some resources such as the scratch page could
1243+ * be made per CPU, backing store driver execution be confined to the faulting
1244+ * CPU, statistics be made to cope with access concurrency, etc. But in the
1245+ * end we're dealing with memory transfer to/from some external storage which
1246+ * is inherently slow and whose access is most likely serialized anyway.
1247+ * So let's simply enforce global demand paging serialization across all CPUs
1248+ * with a mutex as there is no real gain from added parallelism here.
12451249 */
1246- BUILD_ASSERT (!IS_ENABLED (CONFIG_SMP ));
1250+ static K_MUTEX_DEFINE (z_mm_paging_lock );
1251+ #endif
12471252
12481253static void virt_region_foreach (void * addr , size_t size ,
12491254 void (* func )(void * ))
@@ -1333,16 +1338,21 @@ static int do_mem_evict(void *addr)
13331338 bool dirty ;
13341339 struct k_mem_page_frame * pf ;
13351340 uintptr_t location ;
1336- int key , ret ;
1341+ k_spinlock_key_t key ;
13371342 uintptr_t flags , phys ;
1343+ int ret ;
13381344
13391345#if CONFIG_DEMAND_PAGING_ALLOW_IRQ
13401346 __ASSERT (!k_is_in_isr (),
13411347 "%s is unavailable in ISRs with CONFIG_DEMAND_PAGING_ALLOW_IRQ" ,
13421348 __func__ );
1349+ #ifdef CONFIG_SMP
1350+ k_mutex_lock (& z_mm_paging_lock , K_FOREVER );
1351+ #else
13431352 k_sched_lock ();
1353+ #endif
13441354#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
1345- key = irq_lock ( );
1355+ key = k_spin_lock ( & z_mm_lock );
13461356 flags = arch_page_info_get (addr , & phys , false);
13471357 __ASSERT ((flags & ARCH_DATA_PAGE_NOT_MAPPED ) == 0 ,
13481358 "address %p isn't mapped" , addr );
@@ -1362,19 +1372,23 @@ static int do_mem_evict(void *addr)
13621372
13631373 __ASSERT (ret == 0 , "failed to prepare page frame" );
13641374#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1365- irq_unlock ( key );
1375+ k_spin_unlock ( & z_mm_lock , key );
13661376#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
13671377 if (dirty ) {
13681378 do_backing_store_page_out (location );
13691379 }
13701380#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1371- key = irq_lock ( );
1381+ key = k_spin_lock ( & z_mm_lock );
13721382#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
13731383 page_frame_free_locked (pf );
13741384out :
1375- irq_unlock ( key );
1385+ k_spin_unlock ( & z_mm_lock , key );
13761386#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1387+ #ifdef CONFIG_SMP
1388+ k_mutex_unlock (& z_mm_paging_lock );
1389+ #else
13771390 k_sched_unlock ();
1391+ #endif
13781392#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
13791393 return ret ;
13801394}
@@ -1400,11 +1414,12 @@ int k_mem_page_out(void *addr, size_t size)
14001414
14011415int k_mem_page_frame_evict (uintptr_t phys )
14021416{
1403- int key , ret ;
1417+ k_spinlock_key_t key ;
14041418 struct k_mem_page_frame * pf ;
14051419 bool dirty ;
14061420 uintptr_t flags ;
14071421 uintptr_t location ;
1422+ int ret ;
14081423
14091424 __ASSERT (page_frames_initialized , "%s called on 0x%lx too early" ,
14101425 __func__ , phys );
@@ -1417,9 +1432,13 @@ int k_mem_page_frame_evict(uintptr_t phys)
14171432 __ASSERT (!k_is_in_isr (),
14181433 "%s is unavailable in ISRs with CONFIG_DEMAND_PAGING_ALLOW_IRQ" ,
14191434 __func__ );
1435+ #ifdef CONFIG_SMP
1436+ k_mutex_lock (& z_mm_paging_lock , K_FOREVER );
1437+ #else
14201438 k_sched_lock ();
1439+ #endif
14211440#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
1422- key = irq_lock ( );
1441+ key = k_spin_lock ( & z_mm_lock );
14231442 pf = k_mem_phys_to_page_frame (phys );
14241443 if (!k_mem_page_frame_is_mapped (pf )) {
14251444 /* Nothing to do, free page */
@@ -1436,19 +1455,23 @@ int k_mem_page_frame_evict(uintptr_t phys)
14361455 }
14371456
14381457#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1439- irq_unlock ( key );
1458+ k_spin_unlock ( & z_mm_lock , key );
14401459#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
14411460 if (dirty ) {
14421461 do_backing_store_page_out (location );
14431462 }
14441463#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1445- key = irq_lock ( );
1464+ k_spin_unlock ( & z_mm_lock , key );
14461465#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
14471466 page_frame_free_locked (pf );
14481467out :
1449- irq_unlock ( key );
1468+ k_spin_unlock ( & z_mm_lock , key );
14501469#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1470+ #ifdef CONFIG_SMP
1471+ k_mutex_unlock (& z_mm_paging_lock );
1472+ #else
14511473 k_sched_unlock ();
1474+ #endif
14521475#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
14531476 return ret ;
14541477}
@@ -1549,12 +1572,13 @@ static inline struct k_mem_page_frame *do_eviction_select(bool *dirty)
15491572static bool do_page_fault (void * addr , bool pin )
15501573{
15511574 struct k_mem_page_frame * pf ;
1552- int key , ret ;
1575+ k_spinlock_key_t key ;
15531576 uintptr_t page_in_location , page_out_location ;
15541577 enum arch_page_location status ;
15551578 bool result ;
15561579 bool dirty = false;
1557- struct k_thread * faulting_thread = _current_cpu -> current ;
1580+ struct k_thread * faulting_thread ;
1581+ int ret ;
15581582
15591583 __ASSERT (page_frames_initialized , "page fault at %p happened too early" ,
15601584 addr );
@@ -1568,13 +1592,11 @@ static bool do_page_fault(void *addr, bool pin)
15681592 */
15691593
15701594#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1571- /* We lock the scheduler so that other threads are never scheduled
1572- * during the page-in/out operation.
1573- *
1574- * We do however re-enable interrupts during the page-in/page-out
1575- * operation if and only if interrupts were enabled when the exception
1576- * was taken; in this configuration page faults in an ISR are a bug;
1577- * all their code/data must be pinned.
1595+ /*
1596+ * We do re-enable interrupts during the page-in/page-out operation
1597+ * if and only if interrupts were enabled when the exception was
1598+ * taken; in this configuration page faults in an ISR are a bug; all
1599+ * their code/data must be pinned.
15781600 *
15791601 * If interrupts were disabled when the exception was taken, the
15801602 * arch code is responsible for keeping them that way when entering
@@ -1584,20 +1606,35 @@ static bool do_page_fault(void *addr, bool pin)
15841606 * entire operation. This is far worse for system interrupt latency
15851607 * but requires less pinned pages and ISRs may also take page faults.
15861608 *
1587- * Support for allowing k_mem_paging_backing_store_page_out() and
1609+ * On UP we lock the scheduler so that other threads are never
1610+ * scheduled during the page-in/out operation. Support for
1611+ * allowing k_mem_paging_backing_store_page_out() and
15881612 * k_mem_paging_backing_store_page_in() to also sleep and allow
15891613 * other threads to run (such as in the case where the transfer is
1590- * async DMA) is not implemented. Even if limited to thread context,
1591- * arbitrary memory access triggering exceptions that put a thread to
1592- * sleep on a contended page fault operation will break scheduling
1593- * assumptions of cooperative threads or threads that implement
1594- * crticial sections with spinlocks or disabling IRQs.
1614+ * async DMA) is not supported on UP. Even if limited to thread
1615+ * context, arbitrary memory access triggering exceptions that put
1616+ * a thread to sleep on a contended page fault operation will break
1617+ * scheduling assumptions of cooperative threads or threads that
1618+ * implement critical sections with spinlocks or disabling IRQs.
1619+ *
1620+ * On SMP, though, exclusivity cannot be assumed solely from being
1621+ * a cooperative thread. Another thread with any prio may be running
1622+ * on another CPU so exclusion must already be enforced by other
1623+ * means. Therefore trying to prevent scheduling on SMP is pointless,
1624+ * and k_sched_lock() is equivalent to a no-op on SMP anyway.
1625+ * As a result, sleeping/rescheduling in the SMP case is fine.
15951626 */
1596- k_sched_lock ();
15971627 __ASSERT (!k_is_in_isr (), "ISR page faults are forbidden" );
1628+ #ifdef CONFIG_SMP
1629+ k_mutex_lock (& z_mm_paging_lock , K_FOREVER );
1630+ #else
1631+ k_sched_lock ();
1632+ #endif
15981633#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
15991634
1600- key = irq_lock ();
1635+ key = k_spin_lock (& z_mm_lock );
1636+ faulting_thread = _current_cpu -> current ;
1637+
16011638 status = arch_page_location_get (addr , & page_in_location );
16021639 if (status == ARCH_PAGE_LOCATION_BAD ) {
16031640 /* Return false to treat as a fatal error */
@@ -1628,7 +1665,7 @@ static bool do_page_fault(void *addr, bool pin)
16281665 __ASSERT (status == ARCH_PAGE_LOCATION_PAGED_OUT ,
16291666 "unexpected status value %d" , status );
16301667
1631- paging_stats_faults_inc (faulting_thread , key );
1668+ paging_stats_faults_inc (faulting_thread , key . key );
16321669
16331670 pf = free_page_frame_list_get ();
16341671 if (pf == NULL ) {
@@ -1645,7 +1682,7 @@ static bool do_page_fault(void *addr, bool pin)
16451682 __ASSERT (ret == 0 , "failed to prepare page frame" );
16461683
16471684#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1648- irq_unlock ( key );
1685+ k_spin_unlock ( & z_mm_lock , key );
16491686 /* Interrupts are now unlocked if they were not locked when we entered
16501687 * this function, and we may service ISRs. The scheduler is still
16511688 * locked.
@@ -1657,7 +1694,7 @@ static bool do_page_fault(void *addr, bool pin)
16571694 do_backing_store_page_in (page_in_location );
16581695
16591696#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1660- key = irq_lock ( );
1697+ key = k_spin_lock ( & z_mm_lock );
16611698 k_mem_page_frame_clear (pf , K_MEM_PAGE_FRAME_BUSY );
16621699#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
16631700 k_mem_page_frame_clear (pf , K_MEM_PAGE_FRAME_MAPPED );
@@ -1672,9 +1709,13 @@ static bool do_page_fault(void *addr, bool pin)
16721709 k_mem_paging_eviction_add (pf );
16731710 }
16741711out :
1675- irq_unlock ( key );
1712+ k_spin_unlock ( & z_mm_lock , key );
16761713#ifdef CONFIG_DEMAND_PAGING_ALLOW_IRQ
1714+ #ifdef CONFIG_SMP
1715+ k_mutex_unlock (& z_mm_paging_lock );
1716+ #else
16771717 k_sched_unlock ();
1718+ #endif
16781719#endif /* CONFIG_DEMAND_PAGING_ALLOW_IRQ */
16791720
16801721 return result ;
@@ -1722,10 +1763,10 @@ bool k_mem_page_fault(void *addr)
17221763static void do_mem_unpin (void * addr )
17231764{
17241765 struct k_mem_page_frame * pf ;
1725- unsigned int key ;
1766+ k_spinlock_key_t key ;
17261767 uintptr_t flags , phys ;
17271768
1728- key = irq_lock ( );
1769+ key = k_spin_lock ( & z_mm_lock );
17291770 flags = arch_page_info_get (addr , & phys , false);
17301771 __ASSERT ((flags & ARCH_DATA_PAGE_NOT_MAPPED ) == 0 ,
17311772 "invalid data page at %p" , addr );
@@ -1736,7 +1777,7 @@ static void do_mem_unpin(void *addr)
17361777 k_mem_paging_eviction_add (pf );
17371778 }
17381779 }
1739- irq_unlock ( key );
1780+ k_spin_unlock ( & z_mm_lock , key );
17401781}
17411782
17421783void k_mem_unpin (void * addr , size_t size )
0 commit comments