Nuevas características de la gestión de memoria de Linux - Interpretación de los folios de memoria

1. ¿Qué es el folio [ˈfoʊlioʊ]?

1.1 Definición de folio

Agregue folios de memoria, un nuevo tipo para representar páginas de orden 0 o la página principal de una página compuesta.

Folio puede considerarse como una capa de embalaje para una página, del tipo que no tiene gastos generales. Un folio puede ser una sola página o una página compuesta.

(La imagen se refiere a la optimización extrema en torno a HugeTLB)

La figura anterior es un diagrama esquemático de la estructura de la página: 64 bytes administran banderas, lru, mapeo, índice, privado, {ref_, map_}count, memcg_data y otra información. Cuando la página es una página compuesta, las banderas anteriores y otra información están en la página principal, y la página final reutiliza y administra la información compuesta {head, mapcount, order, nr, dtor} y otra información.

struct folio {
        /* private: don't document the anon union */
        union {
                struct {
        /* public: */
                        unsigned long flags;
                        struct list_head lru;
                        struct address_space *mapping;
                        pgoff_t index;
                        void *private;
                        atomic_t _mapcount;
                        atomic_t _refcount;
#ifdef CONFIG_MEMCG
                        unsigned long memcg_data;
#endif
        /* private: the union with struct page is transitional */
                };
                struct page page;
        };
};

En la definición de estructura de folio, banderas, lru y otra información son completamente consistentes con la página, por lo que se pueden unir con la página. Esto le permite utilizar folio->flags directamente en lugar de folio->page->flags.

#define page_folio(p)           (_Generic((p),                          \
        const struct page *:    (const struct folio *)_compound_head(p), \
        struct page *:          (struct folio *)_compound_head(p)))
#define nth_page(page,n) ((page) + (n))
#define folio_page(folio, n)    nth_page(&(folio)->page, n)

Page_folio puede resultar un poco confuso a primera vista, pero en realidad equivale a:

switch (typeof(p)) {
  case const struct page *:
    return (const struct folio *)_compound_head(p);
  case struct page *:
    return (struct folio *)_compound_head(p)));
}

Es así de simple.

_Generic es una característica de selección genérica C11 STANDARD - 6.5.1.1 ( https://www.open-std.org/JTC1/sc22/wg14/www/docs/n1570.pdf ), la sintaxis es la siguiente:

Generic selection
Syntax
 generic-selection:
  _Generic ( assignment-expression , generic-assoc-list )
 generic-assoc-list:
  generic-association
  generic-assoc-list , generic-association
 generic-association:
  type-name : assignment-expression
  default : assignment-expression

La conversión entre página y folio también es muy sencilla. Independientemente de la página principal o final, cuando se convierte a folio, el significado es equivalente a obtener el folio correspondiente a la página principal; cuando el folio se convierte a página, se usa folio->página para obtener la página principal y folio_page(folio, n) se puede utilizar para obtener la página final.

La pregunta es, originalmente la página puede representar una página base o una página compuesta, ¿por qué necesitamos introducir el folio?

1.2 ¿Qué puede hacer el folio?

El tipo de folio permite que una función declare que solo espera una página principal. Casi incidentalmente, esto nos permite eliminar varias llamadas a VM_BUG_ON(PageTail(page)) y composite_head().

La razón es que la página tiene demasiados significados, puede ser página base, página principal compuesta o página final compuesta.

Como se mencionó anteriormente, la metainformación de la página se almacena en la página principal (la página base puede considerarse como la página principal), como página->mapeo, página->índice, etc. Pero en la ruta mm, el parámetro de página pasado siempre debe juzgarse si es una página principal o una página final. Dado que no hay caché de contexto, es posible que haya demasiadas llamadas a composite_head duplicadas en la ruta mm.

Aquí tomamos como ejemplo la llamada a la función mem_cgroup_move_account. Una llamada a mem_cgroup_move_account puede ejecutar Compound_head hasta 7 veces.

static inline struct page *compound_head(struct page *page)
{
        unsigned long head = READ_ONCE(page->compound_head);
        if (unlikely(head & 1))
                return (struct page *) (head - 1);
        return page;
}

Tomemos page_mapping (página) como ejemplo para un análisis detallado. Después de ingresar a la función, primero ejecute Compound_head (página) para obtener el mapeo de páginas y otra información. También hay una rama PageSwapCache (página). Al ejecutar esta función de rama, se pasa la página y es necesario ejecutar Compound_head (página) una vez dentro de la función para obtener la información del indicador de la página.

struct address_space *page_mapping(struct page *page)
{
        struct address_space *mapping;
        page = compound_head(page);
        /* This happens if someone calls flush_dcache_page on slab page */
        if (unlikely(PageSlab(page)))
                return NULL;
        if (unlikely(PageSwapCache(page))) {
                swp_entry_t entry;
                entry.val = page_private(page);
                return swap_address_space(entry);
        }
        mapping = page->mapping;
        if ((unsigned long)mapping & PAGE_MAPPING_ANON)
                return NULL;
        return (void *)((unsigned long)mapping & ~PAGE_MAPPING_FLAGS);
}
EXPORT_SYMBOL(page_mapping);

Después de cambiar a folio, page_mapping(page) corresponde a folio_mapping(folio), y folio implica que el folio en sí es la página principal, por lo que se omiten las dos llamadas a composite_head(page). mem_cgroup_move_account es solo la punta del iceberg, la ruta mm está llena de llamadas a composite_head. El efecto acumulativo es que no solo se reduce la sobrecarga de ejecución, sino que también se le puede recordar al desarrollador que la página actual debe ser la página principal, lo que reduce el número de ramas de juicio.

1.3 Valor directo del folio

1) Reducir demasiadas llamadas redundantes a composite_head.

2) Solicite a los desarrolladores que reconozcan que esta es la página principal cuando vean la publicación.

3) Corrija posibles errores causados ​​por la página final.

Here's an example where our current confusion between "any page"
and "head page" at least produces confusing behaviour, if not an
outright bug, isolate_migratepages_block():
        page = pfn_to_page(low_pfn);
        if (PageCompound(page) && !cc->alloc_contig) {
                const unsigned int order = compound_order(page);
                if (likely(order < MAX_ORDER))
                        low_pfn += (1UL << order) - 1;
                goto isolate_fail;
        }
compound_order() does not expect a tail page; it returns 0 unless it's
a head page.  I think what we actually want to do here is:
        if (!cc->alloc_contig) {
            struct page *head = compound_head(page);
            if (PageHead(head)) {
                const unsigned int order = compound_order(head);
                low_pfn |= (1UL << order) - 1;
                goto isolate_fail;
            }
        }
Not earth-shattering; not even necessarily a bug.  But it's an example
of the way the code reads is different from how the code is executed,
and that's potentially dangerous.  Having a different type for tail
and not-tail pages prevents the muddy thinking that can lead to
tail pages being passed to compound_order().

1.4 folio-5.16 ha sido fusionado

Esto convierte solo partes del MM central y el caché de la página.

willy/pagecache.git tiene un total de  209  confirmaciones. En esta ventana de combinación 5.16, el autor Matthew Wilcox (Oracle) <[email protected]> fusionó por primera vez la parte básica del folio, concretamente la etiqueta de combinación folio-5.16, que contiene 90  confirmaciones  , 74  archivos modificados con  2914  adiciones y  1703  eliminaciones. Además de la definición de folio y otra infraestructura, este cambio se centra principalmente en las partes memcg, mapa de archivos y reescritura. folio-5.16 Parece digno de mención el proceso de sustitución gradual de página por folio. Hay demasiadas rutas de mm. Si tiene un trastorno obsesivo-compulsivo y las reemplaza todas a la vez, debe usar el método de arriba hacia abajo, cambiar de asignación de páginas a folio y luego cambiar completamente. Esto no es realista, es necesario modificar casi toda la carpeta mm. Folio-5.16 adopta el método ascendente. Al comienzo de una determinada función en la ruta mm, la página se reemplaza por folio. Todas las implementaciones internas utilizan folio, formando un "cierre". Luego modifique su función de llamada y llame a la función con folio como parámetro. Hasta que se hayan modificado todas las funciones de la persona que llama, este "cierre" se habrá ampliado a otro nivel. Algunas funciones tienen muchas personas que llaman y no se pueden modificar a la vez. Folio-5.16 proporciona una capa de contenedor. Aquí tomamos page_mapping/folio_mapping como ejemplo.

Primero, el cierre contiene infraestructura como folio_test_slab(folio), folio_test_swapcache(folio) y luego se expande hacia arriba hasta folio_mapping. Hay muchas personas que llaman a page_mapping. mem_cgroup_move_account puede llamar a folio_mapping sin problemas, pero page_evictable todavía usa page_mapping. Entonces el cierre deja de expandirse aquí.

struct address_space *folio_mapping(struct folio *folio)
{
        struct address_space *mapping;
        /* This happens if someone calls flush_dcache_page on slab page */
        if (unlikely(folio_test_slab(folio)))
                return NULL;
        if (unlikely(folio_test_swapcache(folio)))
                return swap_address_space(folio_swap_entry(folio));
        mapping = folio->mapping;
        if ((unsigned long)mapping & PAGE_MAPPING_ANON)
                return NULL;
        return (void *)((unsigned long)mapping & ~PAGE_MAPPING_FLAGS);
}
struct address_space *page_mapping(struct page *page)
{
        return folio_mapping(page_folio(page));
}
mem_cgroup_move_account(page, ...) {
  folio = page_folio(page);
  mapping = folio_mapping(folio);
}
page_evictable(page, ...) {
  ret = !mapping_unevictable(page_mapping(page)) && !PageMlocked(page);
}

2. ¿ Eso es todo para folio?

Muchos amigos tienen el mismo sentimiento que yo al ver esto: ¿Eso es todo? ¿Es solo un problema de cabeza compuesta? Tuve que estudiar LWN: una discusión sobre folios ( https://lwn.net/Articles/869942/ ) , LPC 2021 - File Systems MC ( https://www.youtube.com/watch?v=U6HYrd85hQ8&t=1475s )  Discusión del jefe sobre el folio. Luego descubrí que el tema de Matthew Wilcox no era "El folio", sino "E/S eficiente con buffer". Las cosas no son simples.

Esta vez, folio-5.16 incorpora todos los códigos relacionados con fs. El jefe del grupo mencionó que "los jefes de la comunidad Linux-mm no están de acuerdo en reemplazar todas las páginas con folio. En cuanto a las páginas anónimas y losas, todavía no se pueden reemplazar". A corto plazo." Así que continué navegando por la lista de correo de Linux-mm.

2.1 Discusión comunitaria sobre el folio

2.1.1 Denominación

El primero es Linus, Linus dijo que no odia este conjunto de parches, porque este conjunto de parches realmente resuelve el problema de composite_head; pero tampoco le gusta este conjunto de parches, porque el folio suena poco intuitivo. Después de varias discusiones sobre el nombre, por supuesto el nombre final fue folio.

2.1.2 Opiniones de desarrolladores de FS

Actualmente, todas las páginas en el caché de páginas son páginas de 4K, y las páginas grandes en el caché de páginas también son de solo lectura, como la función de código de páginas grandes ( https://openanolis.cn/sig/Cloud-Kernel/doc /475049355931222178 ) . ¿Por qué no se han implementado páginas grandes transparentes en el caché de páginas? Puede consultar este LWN ( https://lwn.net/Articles/686690/ ) . Una de las razones es que para implementar la lectura y escritura de archivos THP, el procesamiento del caché de páginas por parte de fs basado en buffer_head es demasiado complicado.

  • buffer_head
    buffer_head representa la posición de desplazamiento del dispositivo de bloque de la asignación de memoria física. Generalmente, un buffer_head también tiene un tamaño de 4K, por lo que buffer_head corresponde a una página. Algunos sistemas de archivos pueden utilizar un tamaño de bloque más pequeño, como 1 KB o 512 bytes. Una página de este tipo puede tener hasta 4 u 8 estructuras buffer_head para describir la ubicación del disco físico correspondiente a su memoria.
    De esta manera, cuando se trata de lectura y escritura de varias páginas, cada página necesita obtener la relación entre la página y el desplazamiento del disco a través de get_block, lo cual es ineficiente y complicado.
  • iomap
    iomap se tomó originalmente de XFS, se basa en extensiones y, naturalmente, admite varias páginas. Es decir, cuando se trata de lectura y escritura de varias páginas, la relación entre todas las páginas y los desplazamientos del disco se puede obtener con una sola traducción.
    A través de iomap, el sistema de archivos está aislado del caché de páginas. Por ejemplo, ambos usan bytes para expresar el tamaño, en lugar de cuántas páginas hay. Por lo tanto, Matthew Wilcox sugiere que cualquier sistema de archivos que utilice caché de página directamente debería considerar cambiar a iomap o netfs_lib.
    Puede haber más formas de aislar fs y la caché de página que folio, pero la recopilación de dispersión, por ejemplo, no es aceptable porque la abstracción es demasiado compleja.

Esta es la razón por la que folio se implementó por primera vez en XFS/AFS, porque estos dos sistemas de archivos se basan en iomap. Es por eso que los desarrolladores de FS esperan firmemente que las publicaciones se fusionen, pueden usar fácilmente páginas más grandes en el caché de páginas, este enfoque puede hacer que la E/S del sistema de archivos sea más eficiente. buffer_head tiene algunas características de las que aún carece el iomap actual. La integración de folio puede hacer avanzar iomap, permitiendo cambiar los sistemas de archivos basados ​​en bloques para usar iomap.

2.1.3 Opiniones de desarrolladores de MM

La mayor objeción provino de Johannes Weiner. Reconoció el problema de Compound_head, pero consideró que no valía la pena introducir un cambio tan grande para solucionar el problema. También creía que la optimización de folio en fs no era necesaria para páginas anónimas.

A diferencia del lado del sistema de archivos, esto parece una gran rotación por muy poco valor tangible. Y nos deja con un resultado final que a nadie parece entusiasmar mucho. Pero la abstracción de la publicación es de nivel demasiado bajo para usar SÓLO para el caché de archivos y NO para anon. Está demasiado cerca de la capa de la página y duplicaría demasiado para poder mantenerla una al lado de la otra.

Al final, con el firme apoyo de folio de Kirill A. Shutemov, Michal Hocko y otros grandes, Johannes Weiner también se comprometió.

2.1.4 Llegar a un consenso

Al final de la discusión comunitaria, las objeciones contra el folio ya no existían en el código del folio-5.15, pero se perdió la ventana de fusión de 5.15, por lo que esta vez el folio-5.16 se fusionó intacto.

2.2 El profundo valor del folio

Creo que el problema con el folio es que todo el mundo quiere leer sus esperanzas y sueños en él y se decepciona cuando ve que su problema relacionado no se soluciona mágicamente con el folio.
Folio comenzó como una forma de aliviar el dolor de trabajar con páginas compuestas. Proporciona una vista unificada de páginas base y páginas compuestas. Eso es todo.
Se requiere trabajo preliminar para una adopción más amplia de páginas compuestas en la caché de páginas. Pero también será útil para THP anon y hugetlb.
Según la tasa de adopción y el código resultante, la nueva abstracción tiene buenos efectos posteriores. Puede que sea adecuado para más de lo que estaba previsto inicialmente. Genial.
Pero si no resuelve tu problema... bueno, lo siento...
El conjunto de parches supone un buen paso adelante y reduce el desorden que creé en el camino hacia el enorme-tmpfs.
Me alegraría ver el conjunto de parches en sentido ascendente.
--Kirill A. Shutemov

Todo el mundo conoce la "confusión relacionada con la página de estructura", pero nadie la ha resuelto. Todos han estado soportando en silencio este problema a largo plazo y el código está lleno del siguiente código.

if (compound_head(page)) // do A;
else                     // do B;

Folio no es perfecto. Quizás porque las expectativas de todos son demasiado altas, algunas personas expresaron su decepción con la implementación final de folio. Pero la mayoría ve el folio como un paso importante en la dirección correcta . Después de todo, hay más trabajo por hacer en el futuro.

3. Trabajos de seguimiento de folios y otros

3.1 plan de desarrollo de folio

Para 5.17, pretendemos convertir varios sistemas de archivos (XFS y AFS están listos; otros sistemas de archivos pueden hacerlo) y también convertir más MM y caché de páginas a folios. Para 5.18, las publicaciones de varias páginas deberían estar listas.

3.2 Folio también puede mejorar el rendimiento

La ganancia del 80% es real, pero parece ser un punto de referencia artificial (inicio de Postgres, que no es una carga de trabajo importante). Las cargas de trabajo reales (por ejemplo, construir el kernel, ejecutar postgres en estado estable, etc.) parecen beneficiarse entre 0 y 10%.

folio-5.16 reduce una gran cantidad de llamadas a composite_head, lo que debería mejorar el rendimiento en micro puntos de referencia con sistema alto. No probado. Una vez que se admitan las publicaciones de varias páginas de folio-5.18, en teoría la eficiencia de E/S se puede mejorar, por lo que esperaremos y veremos.

3.3 ¿Cómo debo utilizar folio?

Lo que más deberían hacer los desarrolladores de FS es convertir aquellos sistemas de archivos que todavía usan buffer head para usar iomap para E/S, al menos para aquellos sistemas de archivos basados ​​en bloques. Otros desarrolladores pueden aceptar fácilmente folio. Las nuevas funciones desarrolladas en base a 5.16+ pueden usar folio. Simplemente familiarícese con la API. La esencia de la API, como la asignación de memoria y el reciclaje, no ha cambiado.

Haga clic para probar el producto en la nube de forma gratuita ahora y comenzar su viaje práctico en la nube.

Enlace original

Este artículo es contenido original de Alibaba Cloud y no puede reproducirse sin permiso.

Se dio a conocer oficialmente la versión web de Windows 12 deepin-IDE compilada por estudiantes de secundaria. Se conoce como QQ "verdaderamente desarrollado de forma independiente" y ha logrado "actualizaciones simultáneas de tres terminales", y la arquitectura NT subyacente se basa en Electron QQ para Linux lanzó oficialmente 3.2.0 "Padre de Hongmeng" Wang Chenglu: El sistema de versión para PC Hongmeng se lanzará el próximo año para desafiar a ChatGPT. Se lanzan estos 8 productos nacionales de IA de gran modelo, GitUI v0.24.0. El fondo de pantalla predeterminado de Ubuntu 23.10, un Git Se revela terminal escrito en Rust . Los "Tauren" en el laberinto. JetBrains anuncia la hoja de ruta de WebStorm 2023.3 en China. Human Java Ecosystem, Solon v2.5.3 lanzado
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/yunqi/blog/10108651
Recomendado
Clasificación