.. include:: ../disclaimer-zh_CN.rst :Original: Documentation/mm/page_migration.rst :翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> :æ ¡è¯‘: ======== 页é¢è¿ç§» ======== 页é¢è¿ç§»å…许在进程è¿è¡Œæ—¶åœ¨NUMA系统的节点之间移动页é¢çš„物ç†ä½ç½®ã€‚è¿™æ„味ç€è¿›ç¨‹æ‰€çœ‹åˆ°çš„虚拟地 å€å¹¶æ²¡æœ‰æ”¹å˜ã€‚然而,系统会é‡æ–°å®‰æŽ’这些页é¢çš„物ç†ä½ç½®ã€‚ 也å¯ä»¥å‚è§ :ref: `<异构内å˜ç®¡ç† (HMM)>` 以了解将页é¢è¿ç§»åˆ°è®¾å¤‡ç§æœ‰å†…å˜æˆ–从设备ç§æœ‰å†…å˜ä¸è¿ç§»ã€‚ 页é¢è¿ç§»çš„主è¦ç›®çš„是通过将页é¢ç§»åˆ°è®¿é—®è¯¥å†…å˜çš„进程所è¿è¡Œçš„处ç†å™¨é™„è¿‘æ¥å‡å°‘内å˜è®¿é—®çš„延迟。 页é¢è¿ç§»å…许进程通过MF_MOVEå’ŒMF_MOVE_ALL选项手动é‡æ–°å®šä½å…¶é¡µé¢æ‰€åœ¨çš„节点,åŒæ—¶é€šè¿‡ mbind()设置一个新的内å˜ç–略。一个进程的页é¢ä¹Ÿå¯ä»¥é€šè¿‡sys_migrate_pages()å‡½æ•°è°ƒç”¨ä»Žå¦ ä¸€ä¸ªè¿›ç¨‹é‡æ–°å®šä½ã€‚migrate_pages()函数调用接收两组节点,并将一个进程ä½äºŽæ—§èŠ‚点上的页é¢ç§» åŠ¨åˆ°ç›®æ ‡èŠ‚ç‚¹ä¸Šã€‚é¡µé¢è¿ç§»åŠŸèƒ½ç”±Andi Kleençš„numactl包æä¾›(需è¦0.9.3以上的版本,其仓库 地å€https://github.com/numactl/numactl.git)。numactlæ供了libnuma,它为页é¢è¿ç§» æ供了与其他NUMA功能类似的接å£ã€‚执行 cat ``/proc/<pid>/numa_maps`` å…许轻æ¾æŸ¥çœ‹è¿› 程的页é¢ä½ç½®ã€‚å‚è§proc(5)手册ä¸çš„numa_maps文档。 如果调度程åºå°†ä¸€ä¸ªè¿›ç¨‹é‡æ–°å®‰ç½®åˆ°ä¸€ä¸ªé¥è¿œçš„节点上的处ç†å™¨ï¼Œæ‰‹åŠ¨è¿ç§»æ˜¯å¾ˆæœ‰ç”¨çš„。批é‡è°ƒåº¦ç¨‹åº 或管ç†å‘˜å¯ä»¥æ£€æµ‹åˆ°è¿™ç§æƒ…况,并将进程的页é¢ç§»åˆ°æ–°å¤„ç†å™¨é™„è¿‘ã€‚å†…æ ¸æœ¬èº«åªæ供手动的页è¿ç§»æ”¯æŒã€‚ 自动的页é¢è¿ç§»å¯ä»¥é€šè¿‡ç”¨æˆ·ç©ºé—´çš„进程移动页é¢æ¥å®žçŽ°ã€‚一个特殊的函数调用 "move_pages" å…许 在一个进程ä¸ç§»åŠ¨å•ä¸ªé¡µé¢ã€‚例如,NUMA分æžå™¨å¯ä»¥èŽ·å¾—一个显示频ç¹çš„节点外访问的日志,并å¯ä»¥ä½¿ 用这个结果将页é¢ç§»åŠ¨åˆ°æ›´æœ‰åˆ©çš„ä½ç½®ã€‚ 较大型的设备通常使用cpusets将系统分割æˆè‹¥å¹²ä¸ªèŠ‚点。Paul Jackson为cpusetsé…备了当任务被 转移到å¦ä¸€ä¸ªcpuset时移动页é¢çš„能力(è§:ref:`CPUSETS <cpusets>`)。Cpusetså…许进程定 ä½çš„自动化。如果一个任务被移到一个新的cpuset上,那么它的所有页é¢ä¹Ÿä¼šéšä¹‹ç§»åŠ¨ï¼Œè¿™æ ·è¿›ç¨‹çš„ 性能就ä¸ä¼šæ€¥å‰§ä¸‹é™ã€‚如果cpusetå…许的内å˜èŠ‚点å‘生å˜åŒ–,cpusetä¸çš„进程页也会被移动。 页é¢è¿ç§»å…许为所有è¿ç§»æŠ€æœ¯ä¿ç•™ä¸€ç»„节点ä¸é¡µé¢çš„相对ä½ç½®ï¼Œè¿™å°†ä¿ç•™ç”Ÿæˆçš„特定内å˜åˆ†é…模å¼å³ä½¿ 进程已被è¿ç§»ã€‚为了ä¿ç•™å†…å˜å»¶è¿Ÿï¼Œè¿™ä¸€ç‚¹æ˜¯å¿…è¦çš„。è¿ç§»åŽçš„进程将以类似的性能è¿è¡Œã€‚ 页é¢è¿ç§»åˆ†å‡ 个æ¥éª¤è¿›è¡Œã€‚é¦–å…ˆä¸ºé‚£äº›è¯•å›¾ä»Žå†…æ ¸ä¸ä½¿ç”¨migrate_pages()的进程åšä¸€ä¸ªé«˜å±‚次的 æ述(对于用户空间的使用,å¯ä»¥å‚考上é¢æ到的Andi Kleençš„numactl包),然åŽå¯¹ä½Žæ°´å¹³çš„细 节工作åšä¸€ä¸ªä½Žæ°´å¹³æ述。 åœ¨å†…æ ¸ä¸ä½¿ç”¨ migrate_pages() ============================ 1. 从LRUä¸ç§»é™¤é¡µé¢ã€‚ è¦è¿ç§»çš„页é¢åˆ—表是通过扫æ页é¢å¹¶æŠŠå®ƒä»¬ç§»åˆ°åˆ—表ä¸æ¥ç”Ÿæˆçš„。这是通过调用 isolate_lru_page() æ¥å®Œæˆçš„。调用isolate_lru_page()å¢žåŠ äº†å¯¹è¯¥é¡µçš„å¼•ç”¨ï¼Œè¿™æ ·åœ¨é¡µé¢è¿ç§»å‘生时它就ä¸ä¼š 消失。它还å¯ä»¥é˜²æ¢äº¤æ¢å™¨æˆ–其他扫æ器é‡åˆ°è¯¥é¡µã€‚ 2. 我们需è¦æœ‰ä¸€ä¸ªnew_folio_t类型的函数,å¯ä»¥ä¼ 递给migrate_pages()。这个函数应该计算 出如何在给定的旧页é¢ä¸åˆ†é…æ£ç¡®çš„新页é¢ã€‚ 3. migrate_pages()函数被调用,它试图进行è¿ç§»ã€‚它将调用该函数为æ¯ä¸ªè¢«è€ƒè™‘è¿ç§»çš„页é¢åˆ† é…新的页é¢ã€‚ migrate_pages()如何工作 ======================= migrate_pages()对它的页é¢åˆ—表进行了多次处ç†ã€‚如果当时对一个页é¢çš„所有引用都å¯ä»¥è¢«ç§»é™¤ï¼Œ 那么这个页é¢å°±ä¼šè¢«ç§»åŠ¨ã€‚该页已ç»é€šè¿‡isolate_lru_page()从LRUä¸ç§»é™¤ï¼Œå¹¶ä¸”refcount被 å¢žåŠ ï¼Œä»¥ä¾¿åœ¨é¡µé¢è¿ç§»å‘生时ä¸é‡Šæ”¾è¯¥é¡µã€‚ æ¥éª¤: 1. é”定è¦è¿ç§»çš„页é¢ã€‚ 2. ç¡®ä¿å›žå†™å·²ç»å®Œæˆã€‚ 3. é”定我们è¦è¿ç§»åˆ°çš„新页é¢ã€‚é”定它是为了在è¿ç§»è¿‡ç¨‹ä¸ç«‹å³é˜»æ¢å¯¹è¿™ä¸ªï¼ˆå°šæœªæ›´æ–°çš„)页é¢çš„ 访问。 4. 所有对该页的页表引用都被转æ¢ä¸ºè¿ç§»æ¡ç›®ã€‚这就å‡å°‘了一个页é¢çš„mapcount。如果产生的 mapcountä¸æ˜¯é›¶ï¼Œé‚£ä¹ˆæˆ‘们就ä¸è¿ç§»è¯¥é¡µã€‚所有试图访问该页的用户空间进程现在将ç‰å¾…页 é¢é”或者ç‰å¾…è¿ç§»é¡µè¡¨é¡¹è¢«ç§»é™¤ã€‚ 5. i_pagesçš„é”被æŒæœ‰ã€‚è¿™å°†å¯¼è‡´æ‰€æœ‰è¯•å›¾é€šè¿‡æ˜ å°„è®¿é—®è¯¥é¡µçš„è¿›ç¨‹åœ¨è‡ªæ—‹é”上阻塞。 6. 检查该页的Refcount,如果还有引用,我们就退出。å¦åˆ™ï¼Œæˆ‘们知é“我们是唯一引用这个页 é¢çš„人。 7. æ£€æŸ¥åŸºæ•°æ ‘ï¼Œå¦‚æžœå®ƒä¸åŒ…å«æŒ‡å‘这个页é¢çš„æŒ‡é’ˆï¼Œé‚£ä¹ˆæˆ‘ä»¬å°±é€€å‡ºï¼Œå› ä¸ºå…¶ä»–äººä¿®æ”¹äº†åŸºæ•°æ ‘ã€‚ 8. 新的页é¢è¦ç”¨æ—§çš„页é¢çš„一些设置进行预处ç†ï¼Œè¿™æ ·è®¿é—®æ–°çš„页é¢å°±ä¼šå‘现一个具有æ£ç¡®è®¾ç½® 的页é¢ã€‚ 9. åŸºæ•°æ ‘è¢«æ”¹å˜ä»¥æŒ‡å‘新的页é¢ã€‚ 10. æ—§é¡µçš„å¼•ç”¨è®¡æ•°è¢«åˆ é™¤ï¼Œå› ä¸ºåœ°å€ç©ºé—´çš„引用已ç»æ¶ˆå¤±ã€‚å¯¹æ–°é¡µçš„å¼•ç”¨è¢«å»ºç«‹ï¼Œå› ä¸ºæ–°é¡µè¢« 地å€ç©ºé—´å¼•ç”¨ã€‚ 11. i_pagesé”è¢«æ”¾å¼ƒã€‚è¿™æ ·ä¸€æ¥ï¼Œåœ¨æ˜ å°„ä¸çš„查找åˆå˜å¾—å¯èƒ½äº†ã€‚进程将从在é”上自旋到在 被é”的新页上ç¡çœ 。 12. 页é¢å†…容被å¤åˆ¶åˆ°æ–°çš„页é¢ä¸Šã€‚ 13. 剩余的页é¢æ ‡å¿—被å¤åˆ¶åˆ°æ–°çš„页é¢ä¸Šã€‚ 14. 旧的页é¢æ ‡å¿—被清除,以表明该页é¢ä¸å†æ供任何信æ¯ã€‚ 15. 新页é¢ä¸Šçš„回写队列被触å‘了。 16. 如果è¿ç§»æ¡ç›®è¢«æ’入到页表ä¸ï¼Œé‚£ä¹ˆå°±ç”¨çœŸæ£çš„ptes替æ¢å®ƒä»¬ã€‚è¿™æ ·åšå°†ä½¿é‚£äº›å°šæœªç‰å¾…页 é”的用户空间进程能够访问。 17. 页é¢é”从新旧页é¢ä¸Šè¢«æ’¤é”€ã€‚ç‰å¾…页é”的进程将é‡åšä»–们的缺页异常,并将到达新的页é¢ã€‚ 18. 新的页é¢è¢«ç§»åˆ°LRUä¸ï¼Œå¯ä»¥è¢«äº¤æ¢å™¨ç‰å†æ¬¡æ‰«æ。 éžLRU页é¢è¿ç§» ============= 尽管è¿ç§»æœ€åˆçš„目的是为了å‡å°‘NUMA的内å˜è®¿é—®å»¶è¿Ÿï¼Œä½†åŽ‹ç¼©ä¹Ÿä½¿ç”¨è¿ç§»æ¥åˆ›å»ºé«˜é˜¶é¡µé¢ã€‚ ç›®å‰å®žçŽ°çš„问题是,它被设计为åªè¿ç§»*LRU*页。然而,有一些潜在的éžLRU页é¢å¯ä»¥åœ¨é©±åŠ¨ä¸ 被è¿ç§»ï¼Œä¾‹å¦‚,zsmalloc,virtio-balloon页é¢ã€‚ 对于virtio-balloon页é¢ï¼Œè¿ç§»ä»£ç 路径的æŸäº›éƒ¨åˆ†å·²ç»è¢«é’©ä½ï¼Œå¹¶æ·»åŠ 了virtio-balloon 的特定函数æ¥æ‹¦æˆªè¿ç§»é€»è¾‘。这对一个驱动æ¥è¯´å¤ªç‰¹æ®Šäº†ï¼Œæ‰€ä»¥å…¶ä»–想让自己的页é¢å¯ç§»åŠ¨çš„驱 动就必须在è¿ç§»è·¯å¾„ä¸æ·»åŠ 自己的特定钩å。 为了克æœè¿™ä¸ªé—®é¢˜ï¼ŒVM支æŒéžLRU页é¢è¿ç§»ï¼Œå®ƒä¸ºéžLRUå¯ç§»åŠ¨é¡µé¢æ供了通用函数,而在è¿ç§» 路径ä¸æ²¡æœ‰ç‰¹å®šçš„驱动程åºé’©å。 如果一个驱动程åºæƒ³è®©å®ƒçš„页é¢å¯ç§»åŠ¨ï¼Œå®ƒåº”该定义三个函数,这些函数是 struct address_space_operations的函数指针。 1. ``bool (*isolate_page) (struct page *page, isolate_mode_t mode);`` VM对驱动的isolate_page()函数的期望是,如果驱动æˆåŠŸéš”离了该页,则返回*true*。 返回trueåŽï¼ŒVMä¼šå°†è¯¥é¡µæ ‡è®°ä¸ºPG_isolatedï¼Œè¿™æ ·å¤šä¸ªCPU的并å‘隔离就会跳过该 页进行隔离。如果驱动程åºä¸èƒ½éš”离该页,它应该返回*false*。 一旦页é¢è¢«æˆåŠŸéš”离,VM就会使用page.lruå—æ®µï¼Œå› æ¤é©±åŠ¨ç¨‹åºä¸åº”期望ä¿ç•™è¿™äº›å—段的值。 2. ``int (*migratepage) (struct address_space *mapping,`` | ``struct page *newpage, struct page *oldpage, enum migrate_mode);`` 隔离åŽï¼Œè™šæ‹Ÿæœºç”¨éš”离的页é¢è°ƒç”¨é©±åŠ¨çš„migratepage()。migratepage()的功能是将旧页 的内容移动到新页,并设置struct page newpageçš„å—段。请记ä½ï¼Œå¦‚æžœä½ æˆåŠŸè¿ç§»äº†æ—§é¡µ 并返回MIGRATEPAGE_SUCCESSï¼Œä½ åº”è¯¥é€šè¿‡page_lock下的__ClearPageMovable()å‘虚 拟机表明旧页ä¸å†å¯ç§»åŠ¨ã€‚如果驱动暂时ä¸èƒ½è¿ç§»è¯¥é¡µï¼Œé©±åŠ¨å¯ä»¥è¿”回-EAGAIN。在-EAGAIN 时,VM会在çŸæ—¶é—´å†…é‡è¯•é¡µé¢è¿ç§»ï¼Œå› 为VMå°†-EAGAINç†è§£ä¸º "临时è¿ç§»å¤±è´¥"。在返回除 -EAGAIN以外的任何错误时,VM将放弃页é¢è¿ç§»è€Œä¸é‡è¯•ã€‚ 在migratepage()函数ä¸ï¼Œé©±åŠ¨ç¨‹åºä¸åº”该接触page.lruå—段。 3. ``void (*putback_page)(struct page *);`` 如果在隔离页上è¿ç§»å¤±è´¥ï¼ŒVMåº”è¯¥å°†éš”ç¦»é¡µè¿”å›žç»™é©±åŠ¨ï¼Œå› æ¤VM用隔离页调用驱动的 putback_page()。在这个函数ä¸ï¼Œé©±åŠ¨åº”该把隔离页放回自己的数æ®ç»“æž„ä¸ã€‚ éžLRUå¯ç§»åŠ¨é¡µæ ‡å¿— 有两个页é¢æ ‡å¿—用于支æŒéžLRUå¯ç§»åŠ¨é¡µé¢ã€‚ * PG_movable 驱动应该使用下é¢çš„函数æ¥ä½¿é¡µé¢åœ¨page_lock下å¯ç§»åŠ¨ã€‚:: void __SetPageMovable(struct page *page, struct address_space *mapping) 它需è¦address_spaceçš„å‚æ•°æ¥æ³¨å†Œå°†è¢«VM调用的migration family函数。确切地说, PG_movableä¸æ˜¯struct page的一个真æ£çš„æ ‡å¿—ã€‚ç›¸å,VMå¤ç”¨äº†page->mapping的低 ä½æ¥è¡¨ç¤ºå®ƒ:: #define PAGE_MAPPING_MOVABLE 0x2 page->mapping = page->mapping | PAGE_MAPPING_MOVABLE; 所以驱动ä¸åº”该直接访问page->mapping。相å,驱动应该使用page_mapping()ï¼Œå®ƒå¯ ä»¥åœ¨é¡µé¢é”下å±è”½æŽ‰page->mapping的低2ä½ï¼Œä»Žè€ŒèŽ·å¾—æ£ç¡®çš„struct address_space。 对于éžLRUå¯ç§»åŠ¨é¡µé¢çš„测试,VM支æŒ__PageMovable()函数。然而,它并ä¸èƒ½ä¿è¯è¯†åˆ« éžLRUå¯ç§»åŠ¨é¡µé¢ï¼Œå› 为page->mappingå—段与struct pageä¸çš„其他å˜é‡æ˜¯ç»Ÿä¸€çš„。如 果驱动程åºåœ¨è¢«è™šæ‹Ÿæœºéš”离åŽé‡Šæ”¾äº†é¡µé¢ï¼Œå°½ç®¡page->mapping设置了PAGE_MAPPING_MOVABLE, 但它并没有一个稳定的值(看看__ClearPageMovable)。但是__PageMovable()在页 é¢è¢«éš”离åŽï¼Œæ— 论页é¢æ˜¯LRU还是éžLRUå¯ç§»åŠ¨çš„ï¼Œè°ƒç”¨å®ƒå¼€é”€éƒ½å¾ˆä½Žï¼Œå› ä¸ºLRU页é¢åœ¨ page->mappingä¸ä¸å¯èƒ½æœ‰PAGE_MAPPING_MOVABLE设置。在用pfn扫æä¸çš„lock_page() 进行更大开销的检查æ¥é€‰æ‹©å—害者之å‰ï¼Œå®ƒä¹Ÿå¾ˆé€‚åˆåªæ˜¯çž¥ä¸€çœ¼æ¥æµ‹è¯•éžLRUå¯ç§»åŠ¨çš„页é¢ã€‚ 为了ä¿è¯éžLRUçš„å¯ç§»åŠ¨é¡µé¢ï¼ŒVMæ供了PageMovable()函数。与__PageMovable()ä¸ åŒï¼ŒPageMovable()在lock_page()下验è¯page->mappingå’Œ mapping->a_ops->isolate_page。lock_page()å¯ä»¥é˜²æ¢çªç„¶ç ´åpage->mapping。 使用__SetPageMovable()的驱动应该在释放页é¢ä¹‹å‰é€šè¿‡page_lock()下的 __ClearMovablePage()æ¸…é™¤è¯¥æ ‡å¿—ã€‚ * PG_isolated 为了防æ¢å‡ 个CPUåŒæ—¶è¿›è¡Œéš”离,VM在lock_page()下将隔离的页é¢æ ‡è®°ä¸ºPG_isolated。 å› æ¤ï¼Œå¦‚果一个CPUé‡åˆ°PG_isolatedéžLRUå¯ç§»åŠ¨é¡µé¢ï¼Œå®ƒå¯ä»¥è·³è¿‡å®ƒã€‚驱动程åºä¸éœ€è¦ æ“ä½œè¿™ä¸ªæ ‡å¿—ï¼Œå› ä¸ºVM会自动设置/清除它。请记ä½ï¼Œå¦‚果驱动程åºçœ‹åˆ°PG_isolated页, è¿™æ„味ç€è¯¥é¡µå·²ç»è¢«VM隔离,所以它ä¸åº”该碰page.lruå—段。PG_isolatedæ ‡å¿—ä¸Ž PG_reclaimæ ‡å¿—æ˜¯åŒä¹‰çš„,所以驱动程åºä¸åº”该为自己的目的使用PG_isolated。 监测è¿ç§» ======== 以下事件(计数器)å¯ç”¨äºŽç›‘控页é¢è¿ç§»ã€‚ 1. PGMIGRATE_SUCCESS: æ£å¸¸çš„页é¢è¿ç§»æˆåŠŸã€‚æ¯ä¸ªè®¡æ•°å™¨æ„味ç€ä¸€ä¸ªé¡µé¢è¢«è¿ç§»äº†ã€‚如果该 页是一个éžTHPå’Œéžhugetlbé¡µï¼Œé‚£ä¹ˆè¿™ä¸ªè®¡æ•°å™¨ä¼šå¢žåŠ 1。如果该页é¢æ˜¯ä¸€ä¸ªTHP或hugetlb 页é¢ï¼Œé‚£ä¹ˆè¿™ä¸ªè®¡æ•°å™¨ä¼šéšç€THP或hugetlbå页é¢çš„æ•°é‡è€Œå¢žåŠ 。例如,è¿ç§»ä¸€ä¸ªæœ‰4KBå¤§å° çš„åŸºç¡€é¡µï¼ˆå页)的2MB THPï¼Œå°†å¯¼è‡´è¿™ä¸ªè®¡æ•°å™¨å¢žåŠ 512。 2. PGMIGRATE_FAIL: æ£å¸¸çš„页é¢è¿ç§»å¤±è´¥ã€‚与上é¢PGMIGRATE_SUCCESS的计数规则相åŒï¼šå¦‚ 果是THP或hugetlb,这个计数将被å页的数é‡å¢žåŠ 。 3. THP_MIGRATION_SUCCESS: 一个THP被è¿ç§»è€Œæ²¡æœ‰è¢«åˆ†å‰²ã€‚ 4. THP_MIGRATION_FAIL: 一个THPä¸èƒ½è¢«è¿ç§»ï¼Œä¹Ÿä¸èƒ½è¢«åˆ†å‰²ã€‚ 5. THP_MIGRATION_SPLIT: 一个THP被è¿ç§»äº†ï¼Œä½†ä¸æ˜¯è¿™æ ·çš„:首先,这个THP必须被分割。 在拆分之åŽï¼Œå¯¹å®ƒçš„å页é¢è¿›è¡Œäº†è¿ç§»é‡è¯•ã€‚ THP_MIGRATION_* 事件也会更新相应的PGMIGRATE_SUCCESS或PGMIGRATE_FAIL事件。 例如,一个THPè¿ç§»å¤±è´¥å°†å¯¼è‡´THP_MIGRATION_FAILå’ŒPGMIGRATE_FAILå¢žåŠ ã€‚ Christoph Lameter,2006å¹´5月8日。 Minchan Kim,2016å¹´3月28日。